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
package/dist/cli/index.js CHANGED
@@ -3232,7 +3232,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
3232
3232
 
3233
3233
  // src/cli/version.ts
3234
3234
  function getCliVersion() {
3235
- return true ? "1.9.5" : pkg.version;
3235
+ return true ? "1.9.6" : pkg.version;
3236
3236
  }
3237
3237
  var init_version = __esm({
3238
3238
  "src/cli/version.ts"() {
@@ -9475,9 +9475,12 @@ END;
9475
9475
  // src/store/sqlite-db.ts
9476
9476
  var sqlite_db_exports = {};
9477
9477
  __export(sqlite_db_exports, {
9478
+ acquireDatabase: () => acquireDatabase,
9478
9479
  closeAllDatabases: () => closeAllDatabases,
9479
9480
  closeDatabase: () => closeDatabase,
9481
+ evictIdleDatabases: () => evictIdleDatabases,
9480
9482
  getDatabase: () => getDatabase,
9483
+ getDatabaseStats: () => getDatabaseStats,
9481
9484
  isSqliteAvailable: () => isSqliteAvailable,
9482
9485
  loadBetterSqlite3: () => loadBetterSqlite3
9483
9486
  });
@@ -9515,11 +9518,21 @@ function applySchemaMigrations(db2) {
9515
9518
  apply();
9516
9519
  }
9517
9520
  }
9521
+ function normalizeDataDir(dataDir) {
9522
+ return path3.resolve(dataDir);
9523
+ }
9524
+ function touchDatabase(normalized, db2) {
9525
+ _dbCache.delete(normalized);
9526
+ _dbCache.set(normalized, db2);
9527
+ }
9518
9528
  function getDatabase(dataDir) {
9519
- const normalized = path3.resolve(dataDir);
9529
+ const normalized = normalizeDataDir(dataDir);
9520
9530
  assertNotHomeDataDir(normalized);
9521
9531
  const existing = _dbCache.get(normalized);
9522
- if (existing) return existing;
9532
+ if (existing) {
9533
+ touchDatabase(normalized, existing);
9534
+ return existing;
9535
+ }
9523
9536
  loadBetterSqlite3();
9524
9537
  fs.mkdirSync(dataDir, { recursive: true });
9525
9538
  const dbPath = path3.join(dataDir, "memorix.db");
@@ -9549,6 +9562,8 @@ function getDatabase(dataDir) {
9549
9562
  db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
9550
9563
  db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
9551
9564
  db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
9565
+ db2.exec(CREATE_TASK_CONTINUITY_EVENTS_TABLE);
9566
+ db2.exec(CREATE_AGENT_EFFECTS_TABLE);
9552
9567
  try {
9553
9568
  db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
9554
9569
  } catch {
@@ -9587,7 +9602,7 @@ function getDatabase(dataDir) {
9587
9602
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('storage_generation', '0')`).run();
9588
9603
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('next_id', '1')`).run();
9589
9604
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('mini_skills_generation', '0')`).run();
9590
- _dbCache.set(normalized, db2);
9605
+ touchDatabase(normalized, db2);
9591
9606
  return db2;
9592
9607
  } catch (error40) {
9593
9608
  try {
@@ -9597,8 +9612,46 @@ function getDatabase(dataDir) {
9597
9612
  throw error40;
9598
9613
  }
9599
9614
  }
9615
+ function acquireDatabase(dataDir) {
9616
+ const normalized = normalizeDataDir(dataDir);
9617
+ const db2 = getDatabase(normalized);
9618
+ _dbLeases.set(normalized, (_dbLeases.get(normalized) ?? 0) + 1);
9619
+ let released = false;
9620
+ return {
9621
+ dataDir: normalized,
9622
+ db: db2,
9623
+ release: () => {
9624
+ if (released) return;
9625
+ released = true;
9626
+ const count3 = (_dbLeases.get(normalized) ?? 0) - 1;
9627
+ if (count3 > 0) _dbLeases.set(normalized, count3);
9628
+ else _dbLeases.delete(normalized);
9629
+ }
9630
+ };
9631
+ }
9632
+ function getDatabaseStats() {
9633
+ let leased = 0;
9634
+ for (const count3 of _dbLeases.values()) leased += count3;
9635
+ return { cached: _dbCache.size, leased, leasedDataDirs: _dbLeases.size };
9636
+ }
9637
+ function evictIdleDatabases(maxCached) {
9638
+ const target = Math.max(0, Math.floor(maxCached));
9639
+ let evicted = 0;
9640
+ for (const [normalized, db2] of [..._dbCache]) {
9641
+ if (_dbCache.size <= target) break;
9642
+ if ((_dbLeases.get(normalized) ?? 0) > 0) continue;
9643
+ try {
9644
+ db2.close();
9645
+ } catch {
9646
+ }
9647
+ _dbCache.delete(normalized);
9648
+ evicted++;
9649
+ }
9650
+ return evicted;
9651
+ }
9600
9652
  function closeDatabase(dataDir) {
9601
- const normalized = path3.resolve(dataDir);
9653
+ const normalized = normalizeDataDir(dataDir);
9654
+ if ((_dbLeases.get(normalized) ?? 0) > 0) return;
9602
9655
  const db2 = _dbCache.get(normalized);
9603
9656
  if (db2) {
9604
9657
  try {
@@ -9616,6 +9669,7 @@ function closeAllDatabases() {
9616
9669
  }
9617
9670
  _dbCache.delete(key);
9618
9671
  }
9672
+ _dbLeases.clear();
9619
9673
  }
9620
9674
  function isSqliteAvailable() {
9621
9675
  try {
@@ -9625,7 +9679,7 @@ function isSqliteAvailable() {
9625
9679
  return false;
9626
9680
  }
9627
9681
  }
9628
- 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;
9682
+ 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;
9629
9683
  var init_sqlite_db = __esm({
9630
9684
  "src/store/sqlite-db.ts"() {
9631
9685
  "use strict";
@@ -10342,6 +10396,43 @@ CREATE TABLE IF NOT EXISTS memory_feedback_events (
10342
10396
  delta REAL NOT NULL DEFAULT 0,
10343
10397
  observed_at TEXT NOT NULL
10344
10398
  );
10399
+ `;
10400
+ CREATE_TASK_CONTINUITY_EVENTS_TABLE = `
10401
+ CREATE TABLE IF NOT EXISTS task_continuity_events (
10402
+ id TEXT PRIMARY KEY,
10403
+ project_id TEXT NOT NULL,
10404
+ task_id TEXT NOT NULL,
10405
+ task TEXT NOT NULL,
10406
+ kind TEXT NOT NULL,
10407
+ content TEXT NOT NULL,
10408
+ item_status TEXT,
10409
+ source_ref TEXT,
10410
+ evidence_json TEXT NOT NULL DEFAULT '[]',
10411
+ actor TEXT,
10412
+ created_at TEXT NOT NULL,
10413
+ event_key TEXT,
10414
+ item_key TEXT,
10415
+ supersedes_id TEXT
10416
+ );
10417
+ `;
10418
+ CREATE_AGENT_EFFECTS_TABLE = `
10419
+ CREATE TABLE IF NOT EXISTS agent_effects (
10420
+ id TEXT PRIMARY KEY,
10421
+ pipeline_id TEXT NOT NULL,
10422
+ task_id TEXT NOT NULL,
10423
+ attempt INTEGER NOT NULL,
10424
+ effect_key TEXT NOT NULL,
10425
+ tool TEXT NOT NULL,
10426
+ risk_tier TEXT NOT NULL,
10427
+ replay_policy TEXT NOT NULL,
10428
+ status TEXT NOT NULL,
10429
+ call_id TEXT,
10430
+ idempotency_key TEXT NOT NULL,
10431
+ input_fingerprint TEXT,
10432
+ detail TEXT,
10433
+ started_at INTEGER NOT NULL,
10434
+ finished_at INTEGER
10435
+ );
10345
10436
  `;
10346
10437
  CREATE_MCP_BINDINGS_TABLE = `
10347
10438
  CREATE TABLE IF NOT EXISTS mcp_bindings (
@@ -10420,7 +10511,18 @@ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(projec
10420
10511
  CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
10421
10512
  CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
10422
10513
  CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
10514
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_project_task ON task_continuity_events(project_id, task_id, created_at ASC);
10515
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_project_recent ON task_continuity_events(project_id, created_at DESC);
10516
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_item ON task_continuity_events(project_id, task_id, kind, item_key, created_at DESC);
10517
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
10518
+ ON task_continuity_events(project_id, task_id, event_key)
10519
+ WHERE event_key IS NOT NULL;
10520
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_effect_identity
10521
+ ON agent_effects(pipeline_id, task_id, attempt, effect_key);
10522
+ CREATE INDEX IF NOT EXISTS idx_agent_effect_pipeline ON agent_effects(pipeline_id, started_at ASC);
10423
10523
  CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
10524
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC);
10525
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at);
10424
10526
  CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
10425
10527
  ON maintenance_jobs(project_id, kind, dedupe_key)
10426
10528
  WHERE status IN ('pending', 'running', 'retry');
@@ -10591,6 +10693,8 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
10591
10693
  db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
10592
10694
  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)");
10593
10695
  db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
10696
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC)");
10697
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at)");
10594
10698
  }
10595
10699
  },
10596
10700
  {
@@ -10601,9 +10705,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
10601
10705
  addColumnIfMissing(db2, "code_symbols", "source", "source TEXT");
10602
10706
  addColumnIfMissing(db2, "code_edges", "source", "source TEXT");
10603
10707
  }
10708
+ },
10709
+ {
10710
+ id: "1.9-continuity-v2",
10711
+ apply: (db2) => {
10712
+ addColumnIfMissing(db2, "task_continuity_events", "event_key", "event_key TEXT");
10713
+ addColumnIfMissing(db2, "task_continuity_events", "item_key", "item_key TEXT");
10714
+ addColumnIfMissing(db2, "task_continuity_events", "supersedes_id", "supersedes_id TEXT");
10715
+ 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)");
10716
+ db2.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
10717
+ ON task_continuity_events(project_id, task_id, event_key)
10718
+ WHERE event_key IS NOT NULL`);
10719
+ }
10604
10720
  }
10605
10721
  ];
10606
10722
  _dbCache = /* @__PURE__ */ new Map();
10723
+ _dbLeases = /* @__PURE__ */ new Map();
10607
10724
  }
10608
10725
  });
10609
10726
 
@@ -21094,18 +21211,23 @@ __export(yaml_loader_exports, {
21094
21211
  });
21095
21212
  import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
21096
21213
  import { homedir } from "os";
21214
+ function cacheKey(homeDir, projectRoot) {
21215
+ return `${homeDir}\0${projectRoot ?? ""}`;
21216
+ }
21097
21217
  function initProjectRoot(root) {
21098
21218
  globalProjectRoot = root;
21099
- configCache.delete(root);
21219
+ configCache.delete(cacheKey(homedir(), root));
21100
21220
  }
21101
21221
  function clearProjectRoot() {
21102
21222
  globalProjectRoot = null;
21103
21223
  }
21104
- function loadYamlConfig(projectRoot) {
21224
+ function loadYamlConfig(projectRoot, options = {}) {
21225
+ const homeDir = options.homeDir ?? homedir();
21105
21226
  const resolvedRoot = projectRoot === null ? null : projectRoot ?? globalProjectRoot ?? null;
21106
- const cached2 = configCache.get(resolvedRoot ?? null);
21227
+ const key = cacheKey(homeDir, resolvedRoot);
21228
+ const cached2 = configCache.get(key);
21107
21229
  if (cached2) return cached2;
21108
- const userYaml = getGlobalYamlPath(homedir());
21230
+ const userYaml = getGlobalYamlPath(homeDir);
21109
21231
  const projectYaml = resolvedRoot ? getProjectYamlPath(resolvedRoot) : null;
21110
21232
  let userConfig = {};
21111
21233
  let projectConfig = {};
@@ -21137,12 +21259,12 @@ function loadYamlConfig(projectRoot) {
21137
21259
  server: { ...userConfig.server, ...projectConfig.server },
21138
21260
  team: { ...userConfig.team, ...projectConfig.team }
21139
21261
  };
21140
- configCache.set(resolvedRoot ?? null, merged);
21262
+ configCache.set(key, merged);
21141
21263
  return merged;
21142
21264
  }
21143
- function resetYamlConfigCache(projectRoot) {
21265
+ function resetYamlConfigCache(projectRoot, homeDir = homedir()) {
21144
21266
  if (projectRoot !== void 0) {
21145
- configCache.delete(projectRoot ?? null);
21267
+ configCache.delete(cacheKey(homeDir, projectRoot ?? null));
21146
21268
  } else {
21147
21269
  configCache.clear();
21148
21270
  }
@@ -21353,9 +21475,9 @@ var init_detector = __esm({
21353
21475
  // src/config/legacy-loader.ts
21354
21476
  import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
21355
21477
  import { homedir as homedir2 } from "os";
21356
- function loadFileConfig() {
21478
+ function loadFileConfig(homeDir = homedir2()) {
21357
21479
  if (cachedConfig !== null) return cachedConfig;
21358
- const configPath = getLegacyConfigJsonPath(homedir2());
21480
+ const configPath = getLegacyConfigJsonPath(homeDir);
21359
21481
  try {
21360
21482
  if (existsSync6(configPath)) {
21361
21483
  cachedConfig = JSON.parse(readFileSync5(configPath, "utf-8"));
@@ -21385,15 +21507,15 @@ import { homedir as homedir3 } from "os";
21385
21507
  function loadTomlConfig(options = {}) {
21386
21508
  const homeDir = options.homeDir ?? homedir3();
21387
21509
  const projectRoot = options.projectRoot ?? null;
21388
- const cacheKey2 = `${homeDir}\0${projectRoot ?? ""}`;
21389
- const cached2 = configCache2.get(cacheKey2);
21510
+ const cacheKey4 = `${homeDir}\0${projectRoot ?? ""}`;
21511
+ const cached2 = configCache2.get(cacheKey4);
21390
21512
  if (cached2) return cached2;
21391
21513
  const userToml = getGlobalConfigTomlPath(homeDir);
21392
21514
  const projectToml = projectRoot ? getProjectConfigTomlPath(projectRoot) : null;
21393
21515
  const userConfig = existsSync7(userToml) ? parseTomlConfig(readFileSync6(userToml, "utf8"), userToml) : {};
21394
21516
  const projectConfig = projectToml && existsSync7(projectToml) ? parseTomlConfig(readFileSync6(projectToml, "utf8"), projectToml) : {};
21395
21517
  const merged = mergeTomlConfig(userConfig, projectConfig);
21396
- configCache2.set(cacheKey2, merged);
21518
+ configCache2.set(cacheKey4, merged);
21397
21519
  return merged;
21398
21520
  }
21399
21521
  function mergeTomlConfig(base, override) {
@@ -21989,8 +22111,8 @@ function getResolvedConfig(options = {}) {
21989
22111
  const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
21990
22112
  loadDotenv(projectRoot === null ? void 0 : projectRoot ?? void 0, { userHomeDir: homeDir });
21991
22113
  const toml = loadTomlConfig({ projectRoot: projectRoot ?? null, homeDir });
21992
- const yaml = loadYamlConfig(projectRoot ?? null);
21993
- const legacy = loadFileConfig();
22114
+ const yaml = loadYamlConfig(projectRoot ?? null, { homeDir });
22115
+ const legacy = loadFileConfig(homeDir);
21994
22116
  const embeddingBaseUrl = first(process.env.MEMORIX_EMBEDDING_BASE_URL, toml.embedding?.base_url, yaml.embedding?.baseUrl, legacy.embeddingApi?.baseUrl);
21995
22117
  const openRouterEmbeddingApiKey = isOpenRouterUrl(embeddingBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
21996
22118
  const memoryLlmProvider = first(
@@ -22012,15 +22134,17 @@ function getResolvedConfig(options = {}) {
22012
22134
  legacy.llm?.baseUrl
22013
22135
  );
22014
22136
  const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
22137
+ const isAtlasCloud = memoryLlmProvider === "atlascloud";
22015
22138
  const memoryLlmApiKey = first(
22016
22139
  process.env.MEMORIX_LLM_API_KEY,
22017
22140
  process.env.MEMORIX_API_KEY,
22018
22141
  toml.memory?.llm?.api_key,
22019
22142
  yaml.llm?.apiKey,
22020
22143
  legacy.llm?.apiKey,
22021
- process.env.OPENAI_API_KEY,
22022
- process.env.ANTHROPIC_API_KEY,
22023
- openRouterMemoryLlmApiKey
22144
+ // Never borrow another provider's credentials for the Atlas Cloud preset.
22145
+ isAtlasCloud ? process.env.ATLASCLOUD_API_KEY : process.env.OPENAI_API_KEY,
22146
+ isAtlasCloud ? void 0 : process.env.ANTHROPIC_API_KEY,
22147
+ isAtlasCloud ? void 0 : openRouterMemoryLlmApiKey
22024
22148
  );
22025
22149
  const resolved = {
22026
22150
  agent: {
@@ -22199,7 +22323,8 @@ function getEnvSourceNames() {
22199
22323
  "MEMORIX_CODEGRAPH_EXTERNAL_CONTEXT",
22200
22324
  "MEMORIX_CODEGRAPH_EXTERNAL_COMMAND",
22201
22325
  "MEMORIX_CODEGRAPH_EXTERNAL_TIMEOUT_MS",
22202
- "OPENROUTER_API_KEY"
22326
+ "OPENROUTER_API_KEY",
22327
+ "ATLASCLOUD_API_KEY"
22203
22328
  ].filter((name) => process.env[name]);
22204
22329
  }
22205
22330
  function isOpenRouterUrl(value) {
@@ -22418,12 +22543,45 @@ function textHash(text3) {
22418
22543
  async function loadDiskCache() {
22419
22544
  try {
22420
22545
  const raw = await readFile(CACHE_FILE, "utf-8");
22546
+ if (Buffer.byteLength(raw, "utf8") > MAX_CACHE_FILE_BYTES) {
22547
+ console.error("[memorix] FastEmbed cache exceeds the disk load limit; starting with an empty cache");
22548
+ return;
22549
+ }
22421
22550
  const entries = JSON.parse(raw);
22422
- for (const [k6, v6] of entries) cache.set(k6, v6);
22423
- console.error(`[memorix] Loaded ${entries.length} cached embeddings from disk`);
22551
+ if (!Array.isArray(entries)) return;
22552
+ for (const entry of entries) {
22553
+ if (!Array.isArray(entry) || typeof entry[0] !== "string" || !isVector(entry[1])) continue;
22554
+ cacheSet(entry[0], entry[1], false);
22555
+ }
22556
+ console.error(`[memorix] Loaded ${cache.size} cached embeddings from disk`);
22424
22557
  } catch {
22425
22558
  }
22426
22559
  }
22560
+ function isVector(value) {
22561
+ return Array.isArray(value) && value.every((entry) => typeof entry === "number");
22562
+ }
22563
+ function maxCachePayloadBytes() {
22564
+ const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
22565
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES;
22566
+ }
22567
+ function cacheSet(hash4, value, markDirty = true) {
22568
+ const incomingBytes = value.length * 8;
22569
+ if (incomingBytes > maxCachePayloadBytes()) return;
22570
+ const existing = cache.get(hash4);
22571
+ if (existing) cachePayloadBytes -= existing.length * 8;
22572
+ else {
22573
+ while (cache.size >= MAX_CACHE_SIZE || cachePayloadBytes + incomingBytes > maxCachePayloadBytes()) {
22574
+ const firstKey = cache.keys().next().value;
22575
+ if (firstKey === void 0) break;
22576
+ const old = cache.get(firstKey);
22577
+ cache.delete(firstKey);
22578
+ if (old) cachePayloadBytes -= old.length * 8;
22579
+ }
22580
+ }
22581
+ cache.set(hash4, value);
22582
+ cachePayloadBytes += incomingBytes;
22583
+ if (markDirty) diskCacheDirty = true;
22584
+ }
22427
22585
  async function saveDiskCache() {
22428
22586
  if (!diskCacheDirty) return;
22429
22587
  try {
@@ -22434,7 +22592,7 @@ async function saveDiskCache() {
22434
22592
  } catch {
22435
22593
  }
22436
22594
  }
22437
- var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, diskCacheDirty, FastEmbedProvider;
22595
+ var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, DEFAULT_CACHE_PAYLOAD_BYTES, MAX_CACHE_FILE_BYTES, cachePayloadBytes, diskCacheDirty, FastEmbedProvider;
22438
22596
  var init_fastembed_provider = __esm({
22439
22597
  "src/embedding/fastembed-provider.ts"() {
22440
22598
  "use strict";
@@ -22444,6 +22602,9 @@ var init_fastembed_provider = __esm({
22444
22602
  CACHE_FILE = join3(CACHE_DIR, ".embedding-cache.json");
22445
22603
  cache = /* @__PURE__ */ new Map();
22446
22604
  MAX_CACHE_SIZE = 5e3;
22605
+ DEFAULT_CACHE_PAYLOAD_BYTES = 64 * 1024 * 1024;
22606
+ MAX_CACHE_FILE_BYTES = 128 * 1024 * 1024;
22607
+ cachePayloadBytes = 0;
22447
22608
  diskCacheDirty = false;
22448
22609
  FastEmbedProvider = class _FastEmbedProvider {
22449
22610
  name = "fastembed-bge-small";
@@ -22485,7 +22646,7 @@ var init_fastembed_provider = __esm({
22485
22646
  if (result.length !== this.dimensions) {
22486
22647
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
22487
22648
  }
22488
- this.cacheSet(hash4, result);
22649
+ cacheSet(hash4, result);
22489
22650
  return result;
22490
22651
  }
22491
22652
  async embedBatch(texts) {
@@ -22510,7 +22671,7 @@ var init_fastembed_provider = __esm({
22510
22671
  const originalIdx = uncachedIndices[batchIdx];
22511
22672
  const plain = Array.from(vec2);
22512
22673
  results[originalIdx] = plain;
22513
- this.cacheSet(textHash(uncachedTexts[batchIdx]), plain);
22674
+ cacheSet(textHash(uncachedTexts[batchIdx]), plain);
22514
22675
  batchIdx++;
22515
22676
  }
22516
22677
  }
@@ -22518,14 +22679,6 @@ var init_fastembed_provider = __esm({
22518
22679
  }
22519
22680
  return results;
22520
22681
  }
22521
- cacheSet(hash4, value) {
22522
- if (cache.size >= MAX_CACHE_SIZE) {
22523
- const firstKey = cache.keys().next().value;
22524
- if (firstKey !== void 0) cache.delete(firstKey);
22525
- }
22526
- cache.set(hash4, value);
22527
- diskCacheDirty = true;
22528
- }
22529
22682
  };
22530
22683
  }
22531
22684
  });
@@ -22535,7 +22688,15 @@ var transformers_provider_exports = {};
22535
22688
  __export(transformers_provider_exports, {
22536
22689
  TransformersProvider: () => TransformersProvider
22537
22690
  });
22538
- var cache2, MAX_CACHE_SIZE2, TransformersProvider;
22691
+ import { createHash as createHash2 } from "crypto";
22692
+ function cacheKey2(text3) {
22693
+ return createHash2("sha256").update(text3).digest("hex");
22694
+ }
22695
+ function maxCachePayloadBytes2() {
22696
+ const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
22697
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES2;
22698
+ }
22699
+ var cache2, MAX_CACHE_SIZE2, DEFAULT_CACHE_PAYLOAD_BYTES2, cachePayloadBytes2, TransformersProvider;
22539
22700
  var init_transformers_provider = __esm({
22540
22701
  "src/embedding/transformers-provider.ts"() {
22541
22702
  "use strict";
@@ -22543,6 +22704,8 @@ var init_transformers_provider = __esm({
22543
22704
  init_provider();
22544
22705
  cache2 = /* @__PURE__ */ new Map();
22545
22706
  MAX_CACHE_SIZE2 = 5e3;
22707
+ DEFAULT_CACHE_PAYLOAD_BYTES2 = 64 * 1024 * 1024;
22708
+ cachePayloadBytes2 = 0;
22546
22709
  TransformersProvider = class _TransformersProvider {
22547
22710
  name = "transformers-minilm";
22548
22711
  dimensions = 384;
@@ -22577,7 +22740,7 @@ var init_transformers_provider = __esm({
22577
22740
  return Promise.all(inputs.map((input) => this.embedInput(input, options)));
22578
22741
  }
22579
22742
  async embed(text3) {
22580
- const cached2 = cache2.get(text3);
22743
+ const cached2 = cache2.get(cacheKey2(text3));
22581
22744
  if (cached2) return cached2;
22582
22745
  const output = await this.extractor(text3, {
22583
22746
  pooling: "mean",
@@ -22587,7 +22750,7 @@ var init_transformers_provider = __esm({
22587
22750
  if (result.length !== this.dimensions) {
22588
22751
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
22589
22752
  }
22590
- this.cacheSet(text3, result);
22753
+ this.cacheSet(cacheKey2(text3), result);
22591
22754
  return result;
22592
22755
  }
22593
22756
  async embedBatch(texts) {
@@ -22595,7 +22758,7 @@ var init_transformers_provider = __esm({
22595
22758
  const uncachedIndices = [];
22596
22759
  const uncachedTexts = [];
22597
22760
  for (let i4 = 0; i4 < texts.length; i4++) {
22598
- const cached2 = cache2.get(texts[i4]);
22761
+ const cached2 = cache2.get(cacheKey2(texts[i4]));
22599
22762
  if (cached2) {
22600
22763
  results[i4] = cached2;
22601
22764
  } else {
@@ -22613,17 +22776,27 @@ var init_transformers_provider = __esm({
22613
22776
  const vec2 = Array.from(allVecs[i4]);
22614
22777
  const originalIdx = uncachedIndices[i4];
22615
22778
  results[originalIdx] = vec2;
22616
- this.cacheSet(uncachedTexts[i4], vec2);
22779
+ this.cacheSet(cacheKey2(uncachedTexts[i4]), vec2);
22617
22780
  }
22618
22781
  }
22619
22782
  return results;
22620
22783
  }
22621
22784
  cacheSet(key, value) {
22622
- if (cache2.size >= MAX_CACHE_SIZE2) {
22623
- const firstKey = cache2.keys().next().value;
22624
- if (firstKey !== void 0) cache2.delete(firstKey);
22785
+ const incomingBytes = value.length * 8;
22786
+ if (incomingBytes > maxCachePayloadBytes2()) return;
22787
+ const existing = cache2.get(key);
22788
+ if (existing) cachePayloadBytes2 -= existing.length * 8;
22789
+ else {
22790
+ while (cache2.size >= MAX_CACHE_SIZE2 || cachePayloadBytes2 + incomingBytes > maxCachePayloadBytes2()) {
22791
+ const firstKey = cache2.keys().next().value;
22792
+ if (firstKey === void 0) break;
22793
+ const old = cache2.get(firstKey);
22794
+ cache2.delete(firstKey);
22795
+ if (old) cachePayloadBytes2 -= old.length * 8;
22796
+ }
22625
22797
  }
22626
22798
  cache2.set(key, value);
22799
+ cachePayloadBytes2 += incomingBytes;
22627
22800
  }
22628
22801
  };
22629
22802
  }
@@ -22645,7 +22818,7 @@ __export(api_provider_exports, {
22645
22818
  saveDiskCacheNow: () => saveDiskCacheNow,
22646
22819
  seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
22647
22820
  });
22648
- import { createHash as createHash2 } from "crypto";
22821
+ import { createHash as createHash3 } from "crypto";
22649
22822
  import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
22650
22823
  import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
22651
22824
  import { join as join4 } from "path";
@@ -22672,7 +22845,7 @@ function cacheStatFile() {
22672
22845
  function payloadBytes(vector) {
22673
22846
  return vector.length * BYTES_PER_DIM;
22674
22847
  }
22675
- function maxCachePayloadBytes() {
22848
+ function maxCachePayloadBytes3() {
22676
22849
  const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
22677
22850
  if (raw) {
22678
22851
  const parsed = Number.parseInt(raw, 10);
@@ -22682,7 +22855,7 @@ function maxCachePayloadBytes() {
22682
22855
  }
22683
22856
  function maxEntriesForDimensions(dimensions) {
22684
22857
  const dim = Math.max(1, Math.floor(dimensions));
22685
- const byBytes = Math.floor(maxCachePayloadBytes() / (dim * BYTES_PER_DIM));
22858
+ const byBytes = Math.floor(maxCachePayloadBytes3() / (dim * BYTES_PER_DIM));
22686
22859
  return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
22687
22860
  }
22688
22861
  function rememberDimensions(vector) {
@@ -22695,13 +22868,15 @@ function evictOldest() {
22695
22868
  if (firstKey === void 0) return;
22696
22869
  const old = cache3.get(firstKey);
22697
22870
  cache3.delete(firstKey);
22698
- if (old) cachePayloadBytes -= payloadBytes(old);
22871
+ if (old) cachePayloadBytes3 -= payloadBytes(old);
22699
22872
  }
22700
22873
  function evictToFit(incomingBytes, incomingDims) {
22874
+ if (incomingBytes > maxCachePayloadBytes3()) return false;
22701
22875
  const limit2 = maxEntriesForDimensions(incomingDims);
22702
- while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes + incomingBytes > maxCachePayloadBytes())) {
22876
+ while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes3 + incomingBytes > maxCachePayloadBytes3())) {
22703
22877
  evictOldest();
22704
22878
  }
22879
+ return true;
22705
22880
  }
22706
22881
  function isNumberArray(value) {
22707
22882
  return Array.isArray(value) && value.every((entry) => typeof entry === "number");
@@ -22728,12 +22903,12 @@ function cacheInsert(hash4, value, dirty) {
22728
22903
  const incoming = payloadBytes(value);
22729
22904
  const existing = cache3.get(hash4);
22730
22905
  if (existing) {
22731
- cachePayloadBytes -= payloadBytes(existing);
22906
+ cachePayloadBytes3 -= payloadBytes(existing);
22732
22907
  cache3.delete(hash4);
22733
22908
  }
22734
- evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
22909
+ if (!evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS)) return;
22735
22910
  cache3.set(hash4, value);
22736
- cachePayloadBytes += incoming;
22911
+ cachePayloadBytes3 += incoming;
22737
22912
  if (dirty) diskCacheDirty2 = true;
22738
22913
  }
22739
22914
  async function readCacheStat() {
@@ -22769,7 +22944,7 @@ function cacheNamespace(config2) {
22769
22944
  ].join("|");
22770
22945
  }
22771
22946
  function textHash2(text3, namespace) {
22772
- return createHash2("sha256").update(`${namespace}\0${text3}`).digest("hex").slice(0, 16);
22947
+ return createHash3("sha256").update(`${namespace}\0${text3}`).digest("hex").slice(0, 16);
22773
22948
  }
22774
22949
  function inputIdentity(input, options = {}) {
22775
22950
  return JSON.stringify({
@@ -23111,7 +23286,7 @@ function getApiEmbeddingDiskCacheLoadCountForTests() {
23111
23286
  }
23112
23287
  function resetApiEmbeddingCacheForTests() {
23113
23288
  cache3.clear();
23114
- cachePayloadBytes = 0;
23289
+ cachePayloadBytes3 = 0;
23115
23290
  cacheDimensions = null;
23116
23291
  diskCacheEntryCount = 0;
23117
23292
  diskCacheLoaded = false;
@@ -23122,14 +23297,14 @@ function resetApiEmbeddingCacheForTests() {
23122
23297
  }
23123
23298
  function seedApiEmbeddingCacheForTests(entries) {
23124
23299
  cache3.clear();
23125
- cachePayloadBytes = 0;
23300
+ cachePayloadBytes3 = 0;
23126
23301
  cacheDimensions = null;
23127
23302
  for (const [key, value] of entries) cacheInsert(key, value, true);
23128
23303
  diskCacheLoaded = true;
23129
23304
  diskCacheDirty2 = true;
23130
23305
  }
23131
23306
  function getApiEmbeddingCachePayloadBytesForTests() {
23132
- return cachePayloadBytes;
23307
+ return cachePayloadBytes3;
23133
23308
  }
23134
23309
  function getApiEmbeddingJsonlLineCountCallsForTests() {
23135
23310
  return jsonlLineCountCalls;
@@ -23303,7 +23478,7 @@ function scheduleDiskSave() {
23303
23478
  diskSaveTimer = null;
23304
23479
  }, DISK_SAVE_DEBOUNCE_MS);
23305
23480
  }
23306
- function cacheSet(hash4, value) {
23481
+ function cacheSet2(hash4, value) {
23307
23482
  cacheInsert(hash4, value, true);
23308
23483
  }
23309
23484
  function resolveEnvEmbeddingApiKey() {
@@ -23423,7 +23598,7 @@ async function waitForRetry(waitMs, signal) {
23423
23598
  abortSignal.addEventListener("abort", onAbort, { once: true });
23424
23599
  });
23425
23600
  }
23426
- 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;
23601
+ 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;
23427
23602
  var init_api_provider = __esm({
23428
23603
  "src/embedding/api-provider.ts"() {
23429
23604
  "use strict";
@@ -23435,7 +23610,7 @@ var init_api_provider = __esm({
23435
23610
  BYTES_PER_DIM = 8;
23436
23611
  DEFAULT_ASSUMED_DIMENSIONS = 4096;
23437
23612
  MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
23438
- cachePayloadBytes = 0;
23613
+ cachePayloadBytes3 = 0;
23439
23614
  cacheDimensions = null;
23440
23615
  diskCacheEntryCount = 0;
23441
23616
  diskCacheDirty2 = false;
@@ -23620,7 +23795,7 @@ var init_api_provider = __esm({
23620
23795
  } else {
23621
23796
  embedding = await apiCall();
23622
23797
  }
23623
- cacheSet(hash4, embedding);
23798
+ cacheSet2(hash4, embedding);
23624
23799
  scheduleDiskSave();
23625
23800
  return embedding;
23626
23801
  }
@@ -23668,7 +23843,7 @@ var init_api_provider = __esm({
23668
23843
  for (const item of response.data) {
23669
23844
  const originalIdx = chunkIndices[item.index];
23670
23845
  results[originalIdx] = item.embedding;
23671
- cacheSet(textHash2(normalizedTexts[originalIdx], this.cacheKeyNamespace), item.embedding);
23846
+ cacheSet2(textHash2(normalizedTexts[originalIdx], this.cacheKeyNamespace), item.embedding);
23672
23847
  }
23673
23848
  } catch (error40) {
23674
23849
  const providerLimit = parseBatchLimit(error40);
@@ -23746,7 +23921,7 @@ var init_api_provider = __esm({
23746
23921
  throw new Error(`Expected ${this.dimensions}d, got ${embedding.length}d; dimension mismatch`);
23747
23922
  }
23748
23923
  this.trackUsage(response);
23749
- cacheSet(hash4, embedding);
23924
+ cacheSet2(hash4, embedding);
23750
23925
  scheduleDiskSave();
23751
23926
  return embedding;
23752
23927
  }
@@ -25129,6 +25304,7 @@ var init_provider2 = __esm({
25129
25304
  openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4.1-nano" },
25130
25305
  anthropic: { baseUrl: "https://api.anthropic.com/v1", model: "claude-3-5-haiku-latest" },
25131
25306
  openrouter: { baseUrl: "https://openrouter.ai/api/v1", model: "openai/gpt-4.1-nano" },
25307
+ atlascloud: { baseUrl: "https://api.atlascloud.ai/v1", model: "deepseek-ai/deepseek-v3.2" },
25132
25308
  custom: { baseUrl: "http://localhost:11434/v1", model: "llama3" }
25133
25309
  };
25134
25310
  currentConfig = null;
@@ -25892,7 +26068,7 @@ var init_obs_store = __esm({
25892
26068
  });
25893
26069
 
25894
26070
  // src/search/semantic-index.ts
25895
- import { createHash as createHash3 } from "crypto";
26071
+ import { createHash as createHash4 } from "crypto";
25896
26072
  import { createRequire as createRequire3 } from "module";
25897
26073
  import path10 from "path";
25898
26074
  function createSemanticIndexProfile(provider2) {
@@ -25906,7 +26082,7 @@ function stateKey(dataDir, profile) {
25906
26082
  return `${path10.resolve(dataDir)}::${profile.key}`;
25907
26083
  }
25908
26084
  function tableName(profile) {
25909
- const digest2 = createHash3("sha256").update(profile.key).digest("hex").slice(0, 20);
26085
+ const digest2 = createHash4("sha256").update(profile.key).digest("hex").slice(0, 20);
25910
26086
  return `${TABLE_PREFIX}${digest2}`;
25911
26087
  }
25912
26088
  function indexRoot(dataDir) {
@@ -26205,6 +26381,7 @@ var init_secret_filter = __esm({
26205
26381
  // src/runtime/maintenance-jobs.ts
26206
26382
  var maintenance_jobs_exports = {};
26207
26383
  __export(maintenance_jobs_exports, {
26384
+ DEFAULT_FAILED_HISTORY_RETENTION_MS: () => DEFAULT_FAILED_HISTORY_RETENTION_MS,
26208
26385
  MAINTENANCE_JOB_KINDS: () => MAINTENANCE_JOB_KINDS,
26209
26386
  MaintenanceJobStore: () => MaintenanceJobStore,
26210
26387
  MaintenanceJobWorker: () => MaintenanceJobWorker,
@@ -26254,7 +26431,7 @@ function normalizedKinds(kinds) {
26254
26431
  function getMaintenanceRetryDelayMs(attempts) {
26255
26432
  return Math.min(6e4, 1e3 * 2 ** Math.max(0, attempts - 1));
26256
26433
  }
26257
- var MAINTENANCE_JOB_KINDS, DEFAULT_MAX_ATTEMPTS, DEFAULT_LEASE_MS, DEFAULT_POLL_INTERVAL_MS, MAX_ERROR_LENGTH, DEFAULT_COMPLETED_HISTORY_RETENTION_MS, MaintenanceJobStore, MaintenanceJobWorker;
26434
+ 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;
26258
26435
  var init_maintenance_jobs = __esm({
26259
26436
  "src/runtime/maintenance-jobs.ts"() {
26260
26437
  "use strict";
@@ -26281,6 +26458,7 @@ var init_maintenance_jobs = __esm({
26281
26458
  DEFAULT_POLL_INTERVAL_MS = 2e3;
26282
26459
  MAX_ERROR_LENGTH = 1e3;
26283
26460
  DEFAULT_COMPLETED_HISTORY_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
26461
+ DEFAULT_FAILED_HISTORY_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
26284
26462
  MaintenanceJobStore = class {
26285
26463
  db;
26286
26464
  constructor(dataDir) {
@@ -26407,18 +26585,21 @@ var init_maintenance_jobs = __esm({
26407
26585
  }
26408
26586
  /**
26409
26587
  * Completed jobs are useful recent operator history, not permanent data.
26410
- * Failed jobs stay available for diagnosis until an operator explicitly
26411
- * resolves them or clears the database.
26588
+ * Failed diagnostics are retained longer, but are still bounded so a
26589
+ * permanently failing job kind cannot grow the database forever.
26412
26590
  */
26413
26591
  pruneCompletedHistory(options = {}) {
26414
26592
  const now3 = options.now ?? Date.now();
26415
26593
  const maxAgeMs = Number.isFinite(options.maxAgeMs) ? Math.max(0, Math.floor(options.maxAgeMs)) : DEFAULT_COMPLETED_HISTORY_RETENTION_MS;
26594
+ const failedMaxAgeMs = Number.isFinite(options.failedMaxAgeMs) ? Math.max(0, Math.floor(options.failedMaxAgeMs)) : DEFAULT_FAILED_HISTORY_RETENTION_MS;
26416
26595
  const result = this.db.prepare(`
26417
26596
  DELETE FROM maintenance_jobs
26418
- WHERE status = 'completed'
26597
+ WHERE (status = 'completed'
26419
26598
  AND completed_at IS NOT NULL
26420
- AND completed_at < ?
26421
- `).run(now3 - maxAgeMs);
26599
+ AND completed_at < ?)
26600
+ OR (status = 'failed'
26601
+ AND updated_at < ?)
26602
+ `).run(now3 - maxAgeMs, now3 - failedMaxAgeMs);
26422
26603
  return Number(result.changes ?? 0);
26423
26604
  }
26424
26605
  claimNext(options) {
@@ -26777,7 +26958,7 @@ __export(evidence_store_exports, {
26777
26958
  EvidenceCardStore: () => EvidenceCardStore,
26778
26959
  buildEvidenceCardId: () => buildEvidenceCardId
26779
26960
  });
26780
- import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
26961
+ import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
26781
26962
  function safeJson(value, fallback) {
26782
26963
  if (typeof value !== "string" || value.length === 0) return fallback;
26783
26964
  try {
@@ -26811,7 +26992,7 @@ function rowToCard(row) {
26811
26992
  };
26812
26993
  }
26813
26994
  function observationHash(observation) {
26814
- return createHash4("sha256").update(JSON.stringify({
26995
+ return createHash5("sha256").update(JSON.stringify({
26815
26996
  projectId: observation.projectId,
26816
26997
  id: observation.id,
26817
26998
  title: observation.title,
@@ -27408,13 +27589,13 @@ __export(workspace_exports, {
27408
27589
  loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
27409
27590
  resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
27410
27591
  });
27411
- import { createHash as createHash5 } from "crypto";
27592
+ import { createHash as createHash6 } from "crypto";
27412
27593
  import { execFile } from "child_process";
27413
27594
  import { promises as fs7 } from "fs";
27414
27595
  import path12 from "path";
27415
27596
  import { promisify } from "util";
27416
27597
  function hash(value) {
27417
- return createHash5("sha256").update(value).digest("hex");
27598
+ return createHash6("sha256").update(value).digest("hex");
27418
27599
  }
27419
27600
  function workspaceId(projectId, mode) {
27420
27601
  return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
@@ -28567,14 +28748,14 @@ __export(asset_store_exports, {
28567
28748
  readMediaAsset: () => readMediaAsset,
28568
28749
  removeMediaAsset: () => removeMediaAsset
28569
28750
  });
28570
- import { createHash as createHash6, randomUUID as randomUUID6 } from "crypto";
28751
+ import { createHash as createHash7, randomUUID as randomUUID6 } from "crypto";
28571
28752
  import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
28572
28753
  import path13 from "path";
28573
28754
  function mediaRoot(dataDir) {
28574
28755
  return path13.resolve(dataDir, "media");
28575
28756
  }
28576
28757
  function projectStorageKey(projectId) {
28577
- return createHash6("sha256").update(projectId).digest("hex").slice(0, 16);
28758
+ return createHash7("sha256").update(projectId).digest("hex").slice(0, 16);
28578
28759
  }
28579
28760
  function validateUnderRoot(root, candidate2) {
28580
28761
  const resolved = path13.resolve(candidate2);
@@ -28634,7 +28815,7 @@ async function readHeader(filePath, bytes = 32) {
28634
28815
  }
28635
28816
  async function sha256File(filePath) {
28636
28817
  const bytes = await readFile3(filePath);
28637
- return createHash6("sha256").update(bytes).digest("hex");
28818
+ return createHash7("sha256").update(bytes).digest("hex");
28638
28819
  }
28639
28820
  function destinationFor(root, projectId, sha2562, extension3) {
28640
28821
  const relative5 = path13.join("assets", projectStorageKey(projectId), sha2562.slice(0, 2), `${sha2562}${extension3}`);
@@ -28740,7 +28921,7 @@ async function importMediaBuffer(input) {
28740
28921
  if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
28741
28922
  if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
28742
28923
  const detected = detectMedia(input.bytes.subarray(0, 32));
28743
- const sha2562 = createHash6("sha256").update(input.bytes).digest("hex");
28924
+ const sha2562 = createHash7("sha256").update(input.bytes).digest("hex");
28744
28925
  return importKnownMedia({
28745
28926
  dataDir: input.dataDir,
28746
28927
  projectId: input.projectId,
@@ -28758,7 +28939,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
28758
28939
  if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
28759
28940
  const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
28760
28941
  if (bytes.length !== asset.byteSize) throw new Error(`Media asset size no longer matches metadata: ${asset.id}`);
28761
- const sha2562 = createHash6("sha256").update(bytes).digest("hex");
28942
+ const sha2562 = createHash7("sha256").update(bytes).digest("hex");
28762
28943
  if (sha2562 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
28763
28944
  return bytes;
28764
28945
  }
@@ -81628,7 +81809,7 @@ var require_gaxios = __commonJS({
81628
81809
  var retry_js_1 = require_retry3();
81629
81810
  var stream_1 = __require("stream");
81630
81811
  var interceptor_js_1 = require_interceptor();
81631
- var randomUUID21 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
81812
+ var randomUUID23 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
81632
81813
  var HTTP_STATUS_NO_CONTENT = 204;
81633
81814
  var Gaxios = class {
81634
81815
  agentCache = /* @__PURE__ */ new Map();
@@ -81901,7 +82082,7 @@ var require_gaxios = __commonJS({
81901
82082
  */
81902
82083
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
81903
82084
  if (opts.multipart?.length) {
81904
- const boundary = await randomUUID21();
82085
+ const boundary = await randomUUID23();
81905
82086
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
81906
82087
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
81907
82088
  } else if (shouldDirectlyPassData) {
@@ -87573,16 +87754,16 @@ var require_jwtaccess = __commonJS({
87573
87754
  * @returns A string that returns the cached key.
87574
87755
  */
87575
87756
  getCachedKey(url2, scopes) {
87576
- let cacheKey2 = url2;
87757
+ let cacheKey4 = url2;
87577
87758
  if (scopes && Array.isArray(scopes) && scopes.length) {
87578
- cacheKey2 = url2 ? `${url2}_${scopes.join("_")}` : `${scopes.join("_")}`;
87759
+ cacheKey4 = url2 ? `${url2}_${scopes.join("_")}` : `${scopes.join("_")}`;
87579
87760
  } else if (typeof scopes === "string") {
87580
- cacheKey2 = url2 ? `${url2}_${scopes}` : scopes;
87761
+ cacheKey4 = url2 ? `${url2}_${scopes}` : scopes;
87581
87762
  }
87582
- if (!cacheKey2) {
87763
+ if (!cacheKey4) {
87583
87764
  throw Error("Scopes or url must be provided");
87584
87765
  }
87585
- return cacheKey2;
87766
+ return cacheKey4;
87586
87767
  }
87587
87768
  /**
87588
87769
  * Get a non-expired access token, after refreshing if necessary.
@@ -94128,7 +94309,7 @@ var require_websocket = __commonJS({
94128
94309
  var http3 = __require("http");
94129
94310
  var net = __require("net");
94130
94311
  var tls = __require("tls");
94131
- var { randomBytes: randomBytes3, createHash: createHash27 } = __require("crypto");
94312
+ var { randomBytes: randomBytes3, createHash: createHash29 } = __require("crypto");
94132
94313
  var { Duplex, Readable: Readable3 } = __require("stream");
94133
94314
  var { URL: URL2 } = __require("url");
94134
94315
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -94796,7 +94977,7 @@ var require_websocket = __commonJS({
94796
94977
  abortHandshake(websocket, socket, "Invalid Upgrade header");
94797
94978
  return;
94798
94979
  }
94799
- const digest2 = createHash27("sha1").update(key + GUID).digest("base64");
94980
+ const digest2 = createHash29("sha1").update(key + GUID).digest("base64");
94800
94981
  if (res.headers["sec-websocket-accept"] !== digest2) {
94801
94982
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
94802
94983
  return;
@@ -95168,7 +95349,7 @@ var require_websocket_server = __commonJS({
95168
95349
  var EventEmitter2 = __require("events");
95169
95350
  var http3 = __require("http");
95170
95351
  var { Duplex } = __require("stream");
95171
- var { createHash: createHash27 } = __require("crypto");
95352
+ var { createHash: createHash29 } = __require("crypto");
95172
95353
  var extension3 = require_extension();
95173
95354
  var PerMessageDeflate2 = require_permessage_deflate();
95174
95355
  var subprotocol2 = require_subprotocol();
@@ -95475,7 +95656,7 @@ var require_websocket_server = __commonJS({
95475
95656
  );
95476
95657
  }
95477
95658
  if (this._state > RUNNING) return abortHandshake(socket, 503);
95478
- const digest2 = createHash27("sha1").update(key + GUID).digest("base64");
95659
+ const digest2 = createHash29("sha1").update(key + GUID).digest("base64");
95479
95660
  const headers = [
95480
95661
  "HTTP/1.1 101 Switching Protocols",
95481
95662
  "Upgrade: websocket",
@@ -175397,9 +175578,9 @@ var init_union_priority_sort = __esm({
175397
175578
  // node_modules/typebox/build/value/clean/from_union.mjs
175398
175579
  function FromUnion9(context, type, value) {
175399
175580
  for (const schema of UnionPrioritySort(type.anyOf)) {
175400
- const clean = FromType19(context, schema, Clone2(value));
175401
- if (Check2(context, schema, clean))
175402
- return clean;
175581
+ const clean2 = FromType19(context, schema, Clone2(value));
175582
+ if (Check2(context, schema, clean2))
175583
+ return clean2;
175403
175584
  }
175404
175585
  return value;
175405
175586
  }
@@ -180041,9 +180222,9 @@ var init_long_term = __esm({
180041
180222
  });
180042
180223
 
180043
180224
  // src/codegraph/ids.ts
180044
- import { createHash as createHash7 } from "crypto";
180225
+ import { createHash as createHash8 } from "crypto";
180045
180226
  function digest(input) {
180046
- return createHash7("sha256").update(input).digest("hex").slice(0, 16);
180227
+ return createHash8("sha256").update(input).digest("hex").slice(0, 16);
180047
180228
  }
180048
180229
  function normalizeCodePath(path50) {
180049
180230
  return path50.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
@@ -181091,7 +181272,7 @@ var init_store = __esm({
181091
181272
 
181092
181273
  // src/codegraph/code-state.ts
181093
181274
  import { execFile as execFile2 } from "child_process";
181094
- import { createHash as createHash8 } from "crypto";
181275
+ import { createHash as createHash9 } from "crypto";
181095
181276
  import { promisify as promisify3 } from "util";
181096
181277
  async function runGit(rootPath, args) {
181097
181278
  try {
@@ -181120,7 +181301,7 @@ function changedPathCount(status) {
181120
181301
  return count3;
181121
181302
  }
181122
181303
  function fingerprint(baseRevision, status) {
181123
- return createHash8("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
181304
+ return createHash9("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
181124
181305
  }
181125
181306
  async function collectCodeStateSnapshot(input) {
181126
181307
  const [revision, status] = await Promise.all([
@@ -181214,12 +181395,12 @@ var init_exclude3 = __esm({
181214
181395
  });
181215
181396
 
181216
181397
  // src/codegraph/semantic-provider.ts
181217
- import { createHash as createHash9 } from "crypto";
181398
+ import { createHash as createHash10 } from "crypto";
181218
181399
  import "fs";
181219
181400
  import { resolve as resolve3 } from "path";
181220
181401
  import ts from "typescript";
181221
181402
  function hashText(value) {
181222
- return createHash9("sha256").update(value).digest("hex");
181403
+ return createHash10("sha256").update(value).digest("hex");
181223
181404
  }
181224
181405
  function canonicalPath(value) {
181225
181406
  const normalized = resolve3(value).replace(/\\/g, "/");
@@ -181564,11 +181745,11 @@ __export(lite_provider_exports, {
181564
181745
  indexProjectLite: () => indexProjectLite,
181565
181746
  refreshProjectLite: () => refreshProjectLite
181566
181747
  });
181567
- import { createHash as createHash10 } from "crypto";
181748
+ import { createHash as createHash11 } from "crypto";
181568
181749
  import { existsSync as existsSync14, readdirSync as readdirSync2, readFileSync as readFileSync9, statSync as statSync4 } from "fs";
181569
181750
  import { join as join5, relative as relative2 } from "path";
181570
181751
  function hashText2(text3) {
181571
- return createHash10("sha256").update(text3).digest("hex");
181752
+ return createHash11("sha256").update(text3).digest("hex");
181572
181753
  }
181573
181754
  function extension2(path50) {
181574
181755
  const index = path50.lastIndexOf(".");
@@ -182201,7 +182382,7 @@ var claim_store_exports = {};
182201
182382
  __export(claim_store_exports, {
182202
182383
  ClaimStore: () => ClaimStore
182203
182384
  });
182204
- import { createHash as createHash11, randomUUID as randomUUID12 } from "crypto";
182385
+ import { createHash as createHash12, randomUUID as randomUUID12 } from "crypto";
182205
182386
  function optionalText3(value) {
182206
182387
  return typeof value === "string" && value ? value : void 0;
182207
182388
  }
@@ -182254,7 +182435,7 @@ function rowToEvent3(row) {
182254
182435
  };
182255
182436
  }
182256
182437
  function evidenceKey(input) {
182257
- return createHash11("sha256").update(JSON.stringify([
182438
+ return createHash12("sha256").update(JSON.stringify([
182258
182439
  input.evidenceKind,
182259
182440
  input.evidenceId,
182260
182441
  input.relation,
@@ -182452,7 +182633,7 @@ __export(claims_exports, {
182452
182633
  supersedeClaim: () => supersedeClaim,
182453
182634
  writeClaim: () => writeClaim
182454
182635
  });
182455
- import { createHash as createHash12, randomUUID as randomUUID13 } from "crypto";
182636
+ import { createHash as createHash13, randomUUID as randomUUID13 } from "crypto";
182456
182637
  function compactText2(value, field) {
182457
182638
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
182458
182639
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
@@ -182464,7 +182645,7 @@ function normalizedTerm(value) {
182464
182645
  return compactText2(value, "identity").toLocaleLowerCase("en-US");
182465
182646
  }
182466
182647
  function hashIdentity(parts) {
182467
- return createHash12("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
182648
+ return createHash13("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
182468
182649
  }
182469
182650
  function buildClaimIdentity(input) {
182470
182651
  const subject = normalizedTerm(input.subject);
@@ -182705,7 +182886,7 @@ function supersedeClaim(store2, input) {
182705
182886
  });
182706
182887
  }
182707
182888
  function hashObservation(observation) {
182708
- return createHash12("sha256").update(JSON.stringify([
182889
+ return createHash13("sha256").update(JSON.stringify([
182709
182890
  observation.title,
182710
182891
  observation.narrative,
182711
182892
  observation.facts,
@@ -182949,11 +183130,11 @@ var init_frontmatter = __esm({
182949
183130
  });
182950
183131
 
182951
183132
  // src/knowledge/markdown.ts
182952
- import { createHash as createHash13 } from "crypto";
183133
+ import { createHash as createHash14 } from "crypto";
182953
183134
  import { promises as fs11 } from "fs";
182954
183135
  import path20 from "path";
182955
183136
  function hashContent(content) {
182956
- return createHash13("sha256").update(content).digest("hex");
183137
+ return createHash14("sha256").update(content).digest("hex");
182957
183138
  }
182958
183139
  function stringField(data, key) {
182959
183140
  const value = data[key];
@@ -183079,11 +183260,11 @@ __export(wiki_exports, {
183079
183260
  lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
183080
183261
  readKnowledgePage: () => readKnowledgePage
183081
183262
  });
183082
- import { createHash as createHash14 } from "crypto";
183263
+ import { createHash as createHash15 } from "crypto";
183083
183264
  import { promises as fs12 } from "fs";
183084
183265
  import path21 from "path";
183085
183266
  function hash2(value) {
183086
- return createHash14("sha256").update(value).digest("hex");
183267
+ return createHash15("sha256").update(value).digest("hex");
183087
183268
  }
183088
183269
  function now() {
183089
183270
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -184302,11 +184483,11 @@ __export(workflows_exports, {
184302
184483
  syncCanonicalWorkflows: () => syncCanonicalWorkflows,
184303
184484
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
184304
184485
  });
184305
- import { createHash as createHash15 } from "crypto";
184486
+ import { createHash as createHash16 } from "crypto";
184306
184487
  import { promises as fs13 } from "fs";
184307
184488
  import path22 from "path";
184308
184489
  function hash3(value) {
184309
- return createHash15("sha256").update(value).digest("hex");
184490
+ return createHash16("sha256").update(value).digest("hex");
184310
184491
  }
184311
184492
  function now2() {
184312
184493
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -189337,6 +189518,8 @@ var team_store_exports = {};
189337
189518
  __export(team_store_exports, {
189338
189519
  AGENT_TYPE_ROLE_MAP: () => AGENT_TYPE_ROLE_MAP,
189339
189520
  DEFAULT_ROLES: () => DEFAULT_ROLES,
189521
+ DEFAULT_TEAM_QUERY_LIMIT: () => DEFAULT_TEAM_QUERY_LIMIT,
189522
+ MAX_TEAM_QUERY_LIMIT: () => MAX_TEAM_QUERY_LIMIT,
189340
189523
  TeamStore: () => TeamStore,
189341
189524
  getTeamStore: () => getTeamStore,
189342
189525
  initTeamStore: () => initTeamStore,
@@ -189346,6 +189529,10 @@ __export(team_store_exports, {
189346
189529
  import { randomUUID as randomUUID16 } from "crypto";
189347
189530
  import path25 from "path";
189348
189531
  import fs15 from "fs";
189532
+ function clampTeamQueryLimit(limit2) {
189533
+ if (!Number.isFinite(limit2) || limit2 == null) return DEFAULT_TEAM_QUERY_LIMIT;
189534
+ return Math.min(MAX_TEAM_QUERY_LIMIT, Math.max(1, Math.floor(limit2)));
189535
+ }
189349
189536
  function getTeamStore() {
189350
189537
  if (!_teamStore) {
189351
189538
  throw new Error("[memorix] TeamStore not initialized \u2014 call initTeamStore() first");
@@ -189369,12 +189556,14 @@ function resetTeamStore() {
189369
189556
  _teamStore = null;
189370
189557
  _teamStoreDataDir = null;
189371
189558
  }
189372
- var DEFAULT_ROLES, AGENT_TYPE_ROLE_MAP, TeamStore, _teamStore, _teamStoreDataDir;
189559
+ var DEFAULT_TEAM_QUERY_LIMIT, MAX_TEAM_QUERY_LIMIT, DEFAULT_ROLES, AGENT_TYPE_ROLE_MAP, TeamStore, _teamStore, _teamStoreDataDir;
189373
189560
  var init_team_store = __esm({
189374
189561
  "src/team/team-store.ts"() {
189375
189562
  "use strict";
189376
189563
  init_esm_shims();
189377
189564
  init_sqlite_db();
189565
+ DEFAULT_TEAM_QUERY_LIMIT = 100;
189566
+ MAX_TEAM_QUERY_LIMIT = 500;
189378
189567
  DEFAULT_ROLES = [
189379
189568
  { roleId: "planner", label: "Planner", description: "Breaks down requirements, creates tasks, defines dependencies", preferredAgentTypes: ["claude-code", "cursor", "codex"], maxConcurrent: 2 },
189380
189569
  { roleId: "researcher", label: "Researcher", description: "Investigates codebase, gathers context, answers questions", preferredAgentTypes: ["claude-code", "cursor", "gemini-cli"], maxConcurrent: 2 },
@@ -189684,32 +189873,37 @@ var init_team_store = __esm({
189684
189873
  return this.stmtAgentFindByInstance.get(projectId, agentType, instanceId);
189685
189874
  }
189686
189875
  listAgents(projectId, filter) {
189687
- const all = this.stmtAgentListByProject.all(projectId);
189876
+ const limit2 = clampTeamQueryLimit(filter?.limit);
189688
189877
  if (filter?.status) {
189689
- return all.filter((a4) => a4.status === filter.status);
189878
+ return this.db.prepare(
189879
+ `SELECT * FROM team_agents WHERE project_id = ? AND status = ? ORDER BY joined_at DESC LIMIT ?`
189880
+ ).all(projectId, filter.status, limit2);
189690
189881
  }
189691
- return all;
189882
+ return this.db.prepare(
189883
+ `SELECT * FROM team_agents WHERE project_id = ? ORDER BY joined_at DESC LIMIT ?`
189884
+ ).all(projectId, limit2);
189692
189885
  }
189693
189886
  /** List agents across all projects (for global scope) */
189694
- listAllAgents() {
189887
+ listAllAgents(limit2) {
189695
189888
  if (!this.db) return [];
189696
- return this.db.prepare("SELECT * FROM team_agents ORDER BY last_heartbeat DESC").all();
189889
+ return this.db.prepare("SELECT * FROM team_agents ORDER BY last_heartbeat DESC LIMIT ?").all(clampTeamQueryLimit(limit2));
189697
189890
  }
189698
189891
  /** List locks across all projects (for global scope) */
189699
- listAllLocks() {
189892
+ listAllLocks(limit2) {
189700
189893
  if (!this.db) return [];
189701
189894
  this.db.prepare("DELETE FROM team_locks WHERE expires_at <= ?").run(Date.now());
189702
- return this.db.prepare("SELECT * FROM team_locks WHERE expires_at > ? ORDER BY locked_at DESC").all(Date.now());
189895
+ return this.db.prepare("SELECT * FROM team_locks WHERE expires_at > ? ORDER BY locked_at DESC LIMIT ?").all(Date.now(), clampTeamQueryLimit(limit2));
189703
189896
  }
189704
189897
  /** List tasks across all projects (for global scope) */
189705
189898
  listAllTasks(filter) {
189706
189899
  if (!this.db) return [];
189900
+ const limit2 = clampTeamQueryLimit(filter?.limit);
189707
189901
  if (filter?.available) {
189708
189902
  return this.db.prepare(
189709
- `SELECT t.* FROM team_tasks t WHERE t.status = 'pending' AND t.assignee_agent_id IS NULL ORDER BY t.created_at DESC`
189710
- ).all();
189903
+ `SELECT t.* FROM team_tasks t WHERE t.status = 'pending' AND t.assignee_agent_id IS NULL ORDER BY t.created_at DESC LIMIT ?`
189904
+ ).all(limit2);
189711
189905
  }
189712
- return this.db.prepare("SELECT * FROM team_tasks ORDER BY created_at DESC").all();
189906
+ return this.db.prepare("SELECT * FROM team_tasks ORDER BY created_at DESC LIMIT ?").all(limit2);
189713
189907
  }
189714
189908
  heartbeat(agentId) {
189715
189909
  const info2 = this.stmtAgentUpdateHeartbeat.run(Date.now(), agentId);
@@ -189744,7 +189938,10 @@ var init_team_store = __esm({
189744
189938
  return staleIds;
189745
189939
  }
189746
189940
  getActiveCount(projectId) {
189747
- return this.listAgents(projectId, { status: "active" }).length;
189941
+ const row = this.db.prepare(
189942
+ `SELECT COUNT(*) AS count FROM team_agents WHERE project_id = ? AND status = 'active'`
189943
+ ).get(projectId);
189944
+ return Number(row?.count ?? 0);
189748
189945
  }
189749
189946
  // ═══════════════════════════════════════════════════════════════════
189750
189947
  // Message Operations
@@ -189779,8 +189976,14 @@ var init_team_store = __esm({
189779
189976
  this.stmtMsgInsert.run(row);
189780
189977
  return row;
189781
189978
  }
189782
- getInbox(projectId, agentId) {
189783
- return this.stmtMsgInbox.all(projectId, agentId);
189979
+ getInbox(projectId, agentId, limit2) {
189980
+ return this.db.prepare(`
189981
+ SELECT * FROM team_messages
189982
+ WHERE project_id = ?
189983
+ AND (recipient_agent_id = ? OR recipient_agent_id IS NULL)
189984
+ ORDER BY created_at ASC
189985
+ LIMIT ?
189986
+ `).all(projectId, agentId, clampTeamQueryLimit(limit2));
189784
189987
  }
189785
189988
  getUnreadCount(projectId, agentId) {
189786
189989
  const row = this.stmtMsgUnreadCount.get(projectId, agentId);
@@ -189937,17 +190140,22 @@ var init_team_store = __esm({
189937
190140
  return info2.changes;
189938
190141
  }
189939
190142
  listTasks(projectId, filter) {
189940
- if (filter?.available) {
189941
- return this.stmtTaskAvailable.all(projectId);
189942
- }
189943
- const all = this.stmtTaskListByProject.all(projectId);
190143
+ const clauses = ["project_id = ?"];
190144
+ const values = [projectId];
190145
+ if (filter?.available) clauses.push("status = 'pending'", "assignee_agent_id IS NULL");
189944
190146
  if (filter?.status) {
189945
- return all.filter((t4) => t4.status === filter.status);
190147
+ clauses.push("status = ?");
190148
+ values.push(filter.status);
189946
190149
  }
189947
190150
  if (filter?.assignee) {
189948
- return all.filter((t4) => t4.assignee_agent_id === filter.assignee);
190151
+ clauses.push("assignee_agent_id = ?");
190152
+ values.push(filter.assignee);
189949
190153
  }
189950
- return all;
190154
+ values.push(clampTeamQueryLimit(filter?.limit));
190155
+ const orderBy = filter?.available ? "created_at ASC, rowid ASC" : "created_at DESC, rowid DESC";
190156
+ return this.db.prepare(
190157
+ `SELECT * FROM team_tasks WHERE ${clauses.join(" AND ")} ORDER BY ${orderBy} LIMIT ?`
190158
+ ).all(...values);
189951
190159
  }
189952
190160
  /**
189953
190161
  * List available tasks for a specific agent, sorted by role affinity.
@@ -189959,7 +190167,7 @@ var init_team_store = __esm({
189959
190167
  const agent = this.stmtAgentFindById.get(agentId);
189960
190168
  if (!agent) return [];
189961
190169
  const agentRole = agent.role;
189962
- const available = this.stmtTaskAvailable.all(projectId);
190170
+ const available = this.listTasks(projectId, { available: true, limit: MAX_TEAM_QUERY_LIMIT });
189963
190171
  const eligible = available.filter((t4) => !t4.required_role || t4.required_role === agentRole);
189964
190172
  const score = (t4) => {
189965
190173
  if (t4.preferred_role === agentRole) return 0;
@@ -190013,12 +190221,16 @@ var init_team_store = __esm({
190013
190221
  const row = this.stmtLockGet.get(file2, projectId);
190014
190222
  return row ?? null;
190015
190223
  }
190016
- listLocks(projectId, agentId) {
190224
+ listLocks(projectId, agentId, limit2) {
190017
190225
  this.stmtLockDeleteExpired.run(projectId, Date.now());
190018
190226
  if (agentId) {
190019
- return this.stmtLockListByAgent.all(projectId, agentId);
190227
+ return this.db.prepare(
190228
+ `SELECT * FROM team_locks WHERE project_id = ? AND locked_by = ? ORDER BY locked_at DESC LIMIT ?`
190229
+ ).all(projectId, agentId, clampTeamQueryLimit(limit2));
190020
190230
  }
190021
- return this.stmtLockListByProject.all(projectId);
190231
+ return this.db.prepare(
190232
+ `SELECT * FROM team_locks WHERE project_id = ? ORDER BY locked_at DESC LIMIT ?`
190233
+ ).all(projectId, clampTeamQueryLimit(limit2));
190022
190234
  }
190023
190235
  releaseAllLocks(agentId) {
190024
190236
  const info2 = this.stmtLockDeleteByAgent.run(agentId);
@@ -192869,6 +193081,8 @@ function buildInitTomlConfig(options) {
192869
193081
  lines.push('model = "claude-3-haiku-20240307"');
192870
193082
  } else if (options.llmProvider === "openrouter") {
192871
193083
  lines.push('# model = "openai/gpt-4o-mini"');
193084
+ } else if (options.llmProvider === "atlascloud") {
193085
+ lines.push('model = "deepseek-ai/deepseek-v3.2"');
192872
193086
  }
192873
193087
  if (options.llmProvider === "custom") {
192874
193088
  lines.push("# OpenAI-compatible endpoint, for example DashScope, DeepSeek, Ollama, or an internal gateway.");
@@ -193001,6 +193215,7 @@ var init_init = __esm({
193001
193215
  { value: "openai", label: "OpenAI", hint: "gpt-4o-mini" },
193002
193216
  { value: "anthropic", label: "Anthropic", hint: "claude-3-haiku" },
193003
193217
  { value: "openrouter", label: "OpenRouter", hint: "multi-provider" },
193218
+ { value: "atlascloud", label: "Atlas Cloud", hint: "DeepSeek V3.2" },
193004
193219
  { value: "custom", label: "Custom", hint: "OpenAI-compatible endpoint" }
193005
193220
  ]
193006
193221
  });
@@ -193054,7 +193269,7 @@ var init_init = __esm({
193054
193269
  ];
193055
193270
  if (llmProvider !== "none") {
193056
193271
  envLines.push("# LLM API key");
193057
- if (llmProvider === "openai" || llmProvider === "custom") {
193272
+ if (llmProvider === "openai" || llmProvider === "custom" || llmProvider === "atlascloud") {
193058
193273
  envLines.push("MEMORIX_LLM_API_KEY=sk-your-key-here");
193059
193274
  } else if (llmProvider === "anthropic") {
193060
193275
  envLines.push("MEMORIX_LLM_API_KEY=sk-ant-your-key-here");
@@ -194595,6 +194810,7 @@ var init_official_skills = __esm({
194595
194810
  "| Need the sequence around one memory | `memorix_timeline` | `memorix memory timeline --id <id>` |",
194596
194811
  "| Check source and freshness for a memory | `memorix_evidence` | `memorix evidence list|get|events` |",
194597
194812
  "| Record whether a memory helped, conflicted, or was corrected | `memorix_feedback` | `memorix feedback record|show|audit` |",
194813
+ "| Track task requirements, decisions, verification, and outcome | `memorix_continuity` | `memorix continuity start|record|show|close` |",
194598
194814
  "| Inspect or import controlled media | `memorix_media` | `memorix media import|attach|list|show|status` |",
194599
194815
  '| Learned reusable project knowledge | `memorix_store` | `memorix memory store --type <type> --entity <name> --title "<title>" "<text>"` |',
194600
194816
  "| Task or bug is complete/outdated | `memorix_resolve` | `memorix memory resolve --ids <ids>` |",
@@ -194618,6 +194834,7 @@ var init_official_skills = __esm({
194618
194834
  "3. Use stale or unbound memory only as a warning or search lead.",
194619
194835
  "4. Store durable decisions, fixes, and gotchas after the project state changes.",
194620
194836
  "5. Resolve completed or obsolete memories so future briefs stay small.",
194837
+ "6. For multi-step work, start a continuity ledger and pass its taskId back to memorix_project_context.",
194621
194838
  "",
194622
194839
  "## Store Rules",
194623
194840
  "",
@@ -198185,6 +198402,7 @@ function freshnessForMemory(status) {
198185
198402
  return "unknown";
198186
198403
  }
198187
198404
  function receiptOmissionKind(raw) {
198405
+ if (raw.includes("continuity")) return "continuity";
198188
198406
  if (raw.includes("continuation")) return "continuation";
198189
198407
  if (raw.includes("task")) return "task";
198190
198408
  if (raw.includes("fact")) return "current-fact";
@@ -198295,6 +198513,77 @@ function renderTaskWorksetPrompt(input) {
198295
198513
  trust: "source-backed"
198296
198514
  });
198297
198515
  appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
198516
+ if (input.continuity) {
198517
+ const continuity = input.continuity;
198518
+ appendLine(lines, "", maxTokens, omitted, "continuity-heading");
198519
+ appendLine(lines, "Task continuity", maxTokens, omitted, "continuity-heading", selected, {
198520
+ kind: "continuity",
198521
+ id: "continuity:" + continuity.taskId,
198522
+ reason: "explicit task continuity ledger",
198523
+ trust: "source-backed"
198524
+ });
198525
+ appendLine(
198526
+ lines,
198527
+ "- Status: " + continuity.status + " | " + short(continuity.task, 30),
198528
+ maxTokens,
198529
+ omitted,
198530
+ "continuity-task"
198531
+ );
198532
+ appendLine(
198533
+ lines,
198534
+ "- Outcome: " + continuity.outcome.state,
198535
+ maxTokens,
198536
+ omitted,
198537
+ "continuity-outcome",
198538
+ selected,
198539
+ {
198540
+ kind: "continuity",
198541
+ id: "continuity:" + continuity.taskId,
198542
+ reason: "deterministic task outcome projection",
198543
+ trust: "derived"
198544
+ }
198545
+ );
198546
+ for (const requirement of continuity.requirements.slice(0, 3)) {
198547
+ appendLine(lines, "- Requirement: " + short(requirement, 24), maxTokens, omitted, "continuity-requirement", selected, {
198548
+ kind: "continuity",
198549
+ id: "continuity:" + continuity.taskId,
198550
+ reason: "explicit task requirement",
198551
+ trust: "source-backed"
198552
+ });
198553
+ }
198554
+ for (const decision of continuity.decisions.slice(0, 2)) {
198555
+ appendLine(lines, "- Decision: " + short(decision, 24), maxTokens, omitted, "continuity-decision", selected, {
198556
+ kind: "continuity",
198557
+ id: "continuity:" + continuity.taskId,
198558
+ reason: "explicit task decision",
198559
+ trust: "source-backed"
198560
+ });
198561
+ }
198562
+ for (const verification of continuity.verification.slice(-3)) {
198563
+ appendLine(
198564
+ lines,
198565
+ "- Verify [" + verification.status + "]: " + short(verification.content, 22),
198566
+ maxTokens,
198567
+ omitted,
198568
+ "continuity-verification",
198569
+ selected,
198570
+ {
198571
+ kind: "continuity",
198572
+ id: "continuity:" + continuity.taskId,
198573
+ reason: "task verification state",
198574
+ trust: "source-backed"
198575
+ }
198576
+ );
198577
+ }
198578
+ for (const risk of continuity.risks.slice(0, 2)) {
198579
+ appendLine(lines, "- Risk: " + short(risk, 22), maxTokens, omitted, "continuity-risk", selected, {
198580
+ kind: "continuity",
198581
+ id: "continuity:" + continuity.taskId,
198582
+ reason: "open task risk",
198583
+ trust: "source-backed"
198584
+ });
198585
+ }
198586
+ }
198298
198587
  if (input.alwaysOn) {
198299
198588
  const hasAlwaysOn = input.alwaysOn.profile.length > 0 || Boolean(input.alwaysOn.state) || input.alwaysOn.durable.length > 0;
198300
198589
  if (hasAlwaysOn) {
@@ -198818,6 +199107,7 @@ async function buildTaskWorkset(input) {
198818
199107
  version: "1.3",
198819
199108
  task,
198820
199109
  lens: input.lens,
199110
+ ...input.continuity ? { continuity: input.continuity } : {},
198821
199111
  ...input.alwaysOn && (input.alwaysOn.profile.length > 0 || input.alwaysOn.state || input.alwaysOn.durable.length > 0) ? { alwaysOn: input.alwaysOn } : {},
198822
199112
  currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
198823
199113
  ...continuation ? { continuation } : {},
@@ -201226,7 +201516,7 @@ var compaction_checkpoint_store_exports = {};
201226
201516
  __export(compaction_checkpoint_store_exports, {
201227
201517
  CompactionCheckpointStore: () => CompactionCheckpointStore
201228
201518
  });
201229
- import { createHash as createHash16, randomUUID as randomUUID17 } from "crypto";
201519
+ import { createHash as createHash17, randomUUID as randomUUID17 } from "crypto";
201230
201520
  function optionalText7(value) {
201231
201521
  return typeof value === "string" && value ? value : void 0;
201232
201522
  }
@@ -201268,7 +201558,7 @@ function sourceKeyFor(input, summary) {
201268
201558
  input.tokensBefore ?? "",
201269
201559
  input.firstKeptEntryId ?? ""
201270
201560
  ].join("\0");
201271
- return `derived:${createHash16("sha256").update(material).digest("hex").slice(0, 32)}`;
201561
+ return `derived:${createHash17("sha256").update(material).digest("hex").slice(0, 32)}`;
201272
201562
  }
201273
201563
  function rowToCheckpoint(row) {
201274
201564
  return {
@@ -201526,6 +201816,305 @@ var init_compaction_checkpoint_store = __esm({
201526
201816
  }
201527
201817
  });
201528
201818
 
201819
+ // src/knowledge/task-continuity.ts
201820
+ var task_continuity_exports = {};
201821
+ __export(task_continuity_exports, {
201822
+ TASK_CONTINUITY_EVENT_KINDS: () => TASK_CONTINUITY_EVENT_KINDS,
201823
+ TaskContinuityStore: () => TaskContinuityStore,
201824
+ evaluateTaskContinuity: () => evaluateTaskContinuity
201825
+ });
201826
+ import { randomUUID as randomUUID18 } from "crypto";
201827
+ function clean(value, maxLength = MAX_TEXT_LENGTH2) {
201828
+ return sanitizeCredentials(value).replace(/\s+/g, " ").trim().slice(0, maxLength);
201829
+ }
201830
+ function cleanList(values, maxLength = MAX_TEXT_LENGTH2) {
201831
+ return [...new Set((values ?? []).map((value) => clean(value, maxLength)).filter(Boolean))].slice(0, MAX_LIST_ITEMS2);
201832
+ }
201833
+ function parseRefs(value) {
201834
+ if (typeof value !== "string") return [];
201835
+ try {
201836
+ const parsed = JSON.parse(value);
201837
+ return Array.isArray(parsed) ? cleanList(parsed.filter((item) => typeof item === "string"), 500).slice(0, MAX_EVIDENCE_REFS) : [];
201838
+ } catch {
201839
+ return [];
201840
+ }
201841
+ }
201842
+ function evaluateTaskContinuity(ledger) {
201843
+ const verification = {
201844
+ passed: ledger.verification.filter((item) => item.status === "passed").length,
201845
+ failed: ledger.verification.filter((item) => item.status === "failed").length,
201846
+ pending: ledger.verification.filter((item) => item.status === "pending").length,
201847
+ skipped: ledger.verification.filter((item) => item.status === "skipped").length
201848
+ };
201849
+ const reasons = [];
201850
+ if (verification.failed > 0) reasons.push("verification-failed");
201851
+ if (verification.pending > 0) reasons.push("verification-pending");
201852
+ if (ledger.risks.length > 0) reasons.push("open-risks");
201853
+ if (ledger.status === "blocked") reasons.push("task-blocked");
201854
+ if (ledger.status === "abandoned") reasons.push("task-abandoned");
201855
+ if (ledger.status === "blocked" || ledger.status === "abandoned" || verification.failed > 0) {
201856
+ return {
201857
+ state: "at-risk",
201858
+ score: verification.passed > verification.failed ? 0.55 : 0.2,
201859
+ verification,
201860
+ reasons
201861
+ };
201862
+ }
201863
+ if (ledger.status === "completed") {
201864
+ if (verification.passed > 0 && verification.pending === 0 && verification.skipped === 0) {
201865
+ return {
201866
+ state: ledger.risks.length > 0 ? "validated-with-risks" : "validated",
201867
+ score: ledger.risks.length > 0 ? 0.8 : 1,
201868
+ verification,
201869
+ reasons: reasons.length > 0 ? reasons : ["verification-passed"]
201870
+ };
201871
+ }
201872
+ return {
201873
+ state: "unverified",
201874
+ score: 0.4,
201875
+ verification,
201876
+ reasons: reasons.length > 0 ? reasons : ["no-passing-verification"]
201877
+ };
201878
+ }
201879
+ return {
201880
+ state: "in-progress",
201881
+ score: verification.passed > 0 && verification.pending === 0 ? 0.75 : 0.5,
201882
+ verification,
201883
+ reasons: reasons.length > 0 ? reasons : ["task-open"]
201884
+ };
201885
+ }
201886
+ function rowToEvent4(row) {
201887
+ const status = row.item_status ? String(row.item_status) : void 0;
201888
+ return {
201889
+ id: String(row.id),
201890
+ projectId: String(row.project_id),
201891
+ taskId: String(row.task_id),
201892
+ task: clean(String(row.task), MAX_TASK_LENGTH),
201893
+ kind: row.kind,
201894
+ content: clean(String(row.content)),
201895
+ ...status ? { status } : {},
201896
+ ...row.source_ref ? { sourceRef: clean(String(row.source_ref), 500) } : {},
201897
+ evidenceRefs: parseRefs(row.evidence_json),
201898
+ ...row.actor ? { actor: clean(String(row.actor), 200) } : {},
201899
+ at: String(row.created_at),
201900
+ ...row.event_key ? { eventKey: clean(String(row.event_key), 200) } : {},
201901
+ ...row.item_key ? { itemKey: clean(String(row.item_key), 120) } : {},
201902
+ ...row.supersedes_id ? { supersedesId: clean(String(row.supersedes_id), 120) } : {}
201903
+ };
201904
+ }
201905
+ function aggregate(events) {
201906
+ const first2 = events.find((event) => event.kind === "task") ?? events[0];
201907
+ const latestOutcome = [...events].reverse().find((event) => event.kind === "outcome");
201908
+ const verificationByKey = /* @__PURE__ */ new Map();
201909
+ for (const event of events.filter((event2) => event2.kind === "verification")) {
201910
+ verificationByKey.set(event.itemKey ?? `event:${event.id}`, event);
201911
+ }
201912
+ const ledger = {
201913
+ taskId: first2.taskId,
201914
+ projectId: first2.projectId,
201915
+ task: first2.task,
201916
+ status: latestOutcome?.status ?? "open",
201917
+ requirements: events.filter((event) => event.kind === "requirement").map((event) => event.content),
201918
+ decisions: events.filter((event) => event.kind === "decision").map((event) => event.content),
201919
+ verification: [...verificationByKey.values()].map((event) => ({
201920
+ id: event.itemKey ?? event.id,
201921
+ content: event.content,
201922
+ status: event.status ?? "pending",
201923
+ ...event.sourceRef ? { sourceRef: event.sourceRef } : {},
201924
+ evidenceRefs: event.evidenceRefs
201925
+ })),
201926
+ risks: events.filter((event) => event.kind === "risk").map((event) => event.content),
201927
+ outcomes: events.filter((event) => event.kind === "outcome").map((event) => event.content),
201928
+ updatedAt: events[events.length - 1].at,
201929
+ events
201930
+ };
201931
+ return { ...ledger, outcome: evaluateTaskContinuity(ledger) };
201932
+ }
201933
+ var TASK_CONTINUITY_EVENT_KINDS, MAX_TEXT_LENGTH2, MAX_TASK_LENGTH, MAX_LIST_ITEMS2, MAX_EVIDENCE_REFS, MAX_EVENTS_PER_TASK, TaskContinuityStore;
201934
+ var init_task_continuity = __esm({
201935
+ "src/knowledge/task-continuity.ts"() {
201936
+ "use strict";
201937
+ init_esm_shims();
201938
+ init_secret_filter();
201939
+ init_sqlite_db();
201940
+ TASK_CONTINUITY_EVENT_KINDS = [
201941
+ "task",
201942
+ "requirement",
201943
+ "decision",
201944
+ "verification",
201945
+ "risk",
201946
+ "outcome"
201947
+ ];
201948
+ MAX_TEXT_LENGTH2 = 2e3;
201949
+ MAX_TASK_LENGTH = 1e3;
201950
+ MAX_LIST_ITEMS2 = 8;
201951
+ MAX_EVIDENCE_REFS = 8;
201952
+ MAX_EVENTS_PER_TASK = 200;
201953
+ TaskContinuityStore = class {
201954
+ db = null;
201955
+ async init(dataDir) {
201956
+ this.db = getDatabase(dataDir);
201957
+ }
201958
+ requireDb() {
201959
+ if (!this.db) throw new Error("TaskContinuityStore is not initialized.");
201960
+ return this.db;
201961
+ }
201962
+ insert(input) {
201963
+ const db2 = this.requireDb();
201964
+ const projectId = clean(input.projectId, 500);
201965
+ const taskId = clean(input.taskId, 120);
201966
+ const eventKey = input.eventKey ? clean(input.eventKey, 200) : void 0;
201967
+ if (eventKey) {
201968
+ const existing = db2.prepare(
201969
+ "SELECT * FROM task_continuity_events WHERE project_id = ? AND task_id = ? AND event_key = ?"
201970
+ ).get(projectId, taskId, eventKey);
201971
+ if (existing) return rowToEvent4(existing);
201972
+ }
201973
+ const existingCount = db2.prepare(
201974
+ "SELECT COUNT(*) AS count FROM task_continuity_events WHERE project_id = ? AND task_id = ?"
201975
+ ).get(projectId, taskId);
201976
+ if (Number(existingCount?.count ?? 0) >= MAX_EVENTS_PER_TASK) {
201977
+ throw new Error("Task continuity ledger reached its 200-event limit.");
201978
+ }
201979
+ const event = {
201980
+ id: randomUUID18(),
201981
+ projectId,
201982
+ taskId,
201983
+ task: clean(input.task, MAX_TASK_LENGTH),
201984
+ kind: input.kind,
201985
+ content: clean(input.content),
201986
+ ...input.status ? { status: input.status } : {},
201987
+ ...input.sourceRef ? { sourceRef: clean(input.sourceRef, 500) } : {},
201988
+ evidenceRefs: cleanList(input.evidenceRefs, 500).slice(0, MAX_EVIDENCE_REFS),
201989
+ ...input.actor ? { actor: clean(input.actor, 200) } : {},
201990
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
201991
+ ...eventKey ? { eventKey } : {},
201992
+ ...input.itemKey ? { itemKey: clean(input.itemKey, 120) } : {},
201993
+ ...input.supersedesId ? { supersedesId: clean(input.supersedesId, 120) } : {}
201994
+ };
201995
+ db2.prepare(`
201996
+ INSERT INTO task_continuity_events (
201997
+ id, project_id, task_id, task, kind, content, item_status,
201998
+ source_ref, evidence_json, actor, created_at, event_key, item_key, supersedes_id
201999
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
202000
+ `).run(
202001
+ event.id,
202002
+ event.projectId,
202003
+ event.taskId,
202004
+ event.task,
202005
+ event.kind,
202006
+ event.content,
202007
+ event.status ?? null,
202008
+ event.sourceRef ?? null,
202009
+ JSON.stringify(event.evidenceRefs),
202010
+ event.actor ?? null,
202011
+ event.at,
202012
+ event.eventKey ?? null,
202013
+ event.itemKey ?? null,
202014
+ event.supersedesId ?? null
202015
+ );
202016
+ return event;
202017
+ }
202018
+ start(input) {
202019
+ const task = clean(input.task, MAX_TASK_LENGTH);
202020
+ if (!task) throw new Error("A non-empty task is required.");
202021
+ const idempotencyKey = input.idempotencyKey ? clean(input.idempotencyKey, 200) : void 0;
202022
+ const taskId = clean(input.taskId ?? idempotencyKey ?? "", 120) || randomUUID18();
202023
+ const requirements = cleanList(input.requirements);
202024
+ const transaction = this.requireDb().transaction(() => {
202025
+ this.insert({
202026
+ projectId: input.projectId,
202027
+ taskId,
202028
+ task,
202029
+ kind: "task",
202030
+ content: task,
202031
+ status: "open",
202032
+ actor: input.actor,
202033
+ ...idempotencyKey ? { eventKey: `start:${idempotencyKey}` } : {}
202034
+ });
202035
+ for (const requirement of requirements) {
202036
+ this.insert({
202037
+ projectId: input.projectId,
202038
+ taskId,
202039
+ task,
202040
+ kind: "requirement",
202041
+ content: requirement,
202042
+ actor: input.actor
202043
+ });
202044
+ }
202045
+ });
202046
+ transaction.immediate();
202047
+ return { taskId, ledger: this.get(input.projectId, taskId) };
202048
+ }
202049
+ record(input) {
202050
+ const projectId = clean(input.projectId, 500);
202051
+ const taskId = clean(input.taskId, 120);
202052
+ const existing = this.get(projectId, taskId);
202053
+ if (!existing) throw new Error("Task continuity ledger not found: " + taskId);
202054
+ const transaction = this.requireDb().transaction(() => this.insert({
202055
+ projectId,
202056
+ taskId,
202057
+ task: existing.task,
202058
+ kind: input.kind,
202059
+ content: input.content,
202060
+ ...input.kind === "verification" && input.verificationStatus ? { status: input.verificationStatus } : {},
202061
+ sourceRef: input.sourceRef,
202062
+ evidenceRefs: input.evidenceRefs,
202063
+ actor: input.actor,
202064
+ at: input.at,
202065
+ ...input.idempotencyKey ? { eventKey: input.idempotencyKey } : {},
202066
+ ...input.verificationId ? { itemKey: input.verificationId } : {}
202067
+ }));
202068
+ const event = transaction.immediate();
202069
+ return { event, ledger: this.get(projectId, taskId) };
202070
+ }
202071
+ close(input) {
202072
+ const projectId = clean(input.projectId, 500);
202073
+ const taskId = clean(input.taskId, 120);
202074
+ const existing = this.get(projectId, taskId);
202075
+ if (!existing) throw new Error("Task continuity ledger not found: " + taskId);
202076
+ const transaction = this.requireDb().transaction(() => this.insert({
202077
+ projectId,
202078
+ taskId,
202079
+ task: existing.task,
202080
+ kind: "outcome",
202081
+ content: input.content,
202082
+ status: input.status,
202083
+ sourceRef: input.sourceRef,
202084
+ evidenceRefs: input.evidenceRefs,
202085
+ actor: input.actor,
202086
+ at: input.at,
202087
+ ...input.idempotencyKey ? { eventKey: input.idempotencyKey } : {}
202088
+ }));
202089
+ const event = transaction.immediate();
202090
+ return { event, ledger: this.get(projectId, taskId) };
202091
+ }
202092
+ get(projectId, taskId) {
202093
+ const normalizedProjectId = clean(projectId, 500);
202094
+ const normalizedTaskId = clean(taskId, 120);
202095
+ const rows = this.requireDb().prepare(`
202096
+ SELECT * FROM task_continuity_events
202097
+ WHERE project_id = ? AND task_id = ?
202098
+ ORDER BY created_at ASC, rowid ASC
202099
+ `).all(normalizedProjectId, normalizedTaskId);
202100
+ if (rows.length === 0) return void 0;
202101
+ return aggregate(rows.map(rowToEvent4));
202102
+ }
202103
+ list(projectId, limit2 = 20) {
202104
+ const rows = this.requireDb().prepare(`
202105
+ SELECT task_id
202106
+ FROM task_continuity_events
202107
+ WHERE project_id = ?
202108
+ GROUP BY task_id
202109
+ ORDER BY MAX(created_at) DESC
202110
+ LIMIT ?
202111
+ `).all(projectId, Math.min(100, Math.max(1, Math.floor(limit2))));
202112
+ return rows.map((row) => this.get(projectId, String(row.task_id))).filter((ledger) => Boolean(ledger));
202113
+ }
202114
+ };
202115
+ }
202116
+ });
202117
+
201529
202118
  // src/codegraph/auto-context.ts
201530
202119
  var auto_context_exports = {};
201531
202120
  __export(auto_context_exports, {
@@ -201782,6 +202371,16 @@ async function buildAutoProjectContext(input) {
201782
202371
  };
201783
202372
  }
201784
202373
  }
202374
+ let continuity = input.continuity;
202375
+ if (!continuity && input.taskId) {
202376
+ try {
202377
+ const { TaskContinuityStore: TaskContinuityStore2 } = await Promise.resolve().then(() => (init_task_continuity(), task_continuity_exports));
202378
+ const continuityStore = new TaskContinuityStore2();
202379
+ await continuityStore.init(input.dataDir);
202380
+ continuity = continuityStore.get(input.project.id, input.taskId);
202381
+ } catch {
202382
+ }
202383
+ }
201785
202384
  const alwaysOn = await buildAlwaysOnBlock({
201786
202385
  dataDir: input.dataDir,
201787
202386
  projectId: input.project.id,
@@ -201800,6 +202399,7 @@ async function buildAutoProjectContext(input) {
201800
202399
  providerQuality: providerQuality2,
201801
202400
  currentFacts: worksetFactLines(currentFacts),
201802
202401
  ...continuation ? { continuation } : {},
202402
+ ...continuity ? { continuity } : {},
201803
202403
  codeState: codeStateLine(overview),
201804
202404
  ...codeEvolution ? { codeEvolution } : {},
201805
202405
  reliableMemory: sourceSets.reliableSources.slice(0, lens.sourceLimit).map((source) => ({
@@ -201847,6 +202447,7 @@ async function buildAutoProjectContext(input) {
201847
202447
  refresh,
201848
202448
  providerQuality: providerQuality2,
201849
202449
  ...continuationRequested && workset.continuation ? { continuation: workset.continuation } : {},
202450
+ ...workset.continuity ? { continuity: workset.continuity } : {},
201850
202451
  workset
201851
202452
  };
201852
202453
  }
@@ -202148,7 +202749,14 @@ function buildBoundedContextReceipt(input) {
202148
202749
  externalState: provider2?.external.state ?? "not-detected",
202149
202750
  ...input.workset.provenance.snapshotId ? { snapshotId: input.workset.provenance.snapshotId } : {}
202150
202751
  },
202151
- ...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {}
202752
+ ...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {},
202753
+ ...input.workset.continuity ? {
202754
+ continuity: {
202755
+ taskId: input.workset.continuity.taskId,
202756
+ status: input.workset.continuity.status,
202757
+ outcome: input.workset.continuity.outcome
202758
+ }
202759
+ } : {}
202152
202760
  };
202153
202761
  }
202154
202762
  var init_context_receipt = __esm({
@@ -202159,10 +202767,10 @@ var init_context_receipt = __esm({
202159
202767
  });
202160
202768
 
202161
202769
  // src/cli/fallback-budget.ts
202162
- import { createHash as createHash17 } from "crypto";
202770
+ import { createHash as createHash18 } from "crypto";
202163
202771
  function fallbackRequestKey(projectId, task) {
202164
202772
  const normalized = (task ?? "").trim().toLowerCase().replace(/\s+/g, " ");
202165
- return createHash17("sha256").update(`${projectId}
202773
+ return createHash18("sha256").update(`${projectId}
202166
202774
  ${normalized}`).digest("hex").slice(0, 32);
202167
202775
  }
202168
202776
  var DEFAULT_FALLBACK_TTL_MS, CliFallbackBudget;
@@ -202247,6 +202855,7 @@ var init_context2 = __esm({
202247
202855
  },
202248
202856
  args: {
202249
202857
  task: { type: "string", description: "Current task for context shaping" },
202858
+ taskId: { type: "string", description: "Optional task continuity ledger id to include" },
202250
202859
  input: {
202251
202860
  type: "positional",
202252
202861
  description: "Current task for context shaping (ergonomic positional form)",
@@ -202278,6 +202887,7 @@ var init_context2 = __esm({
202278
202887
  dataDir,
202279
202888
  observations: filterReadableObservations(getAllObservations(), reader2),
202280
202889
  task,
202890
+ taskId: args.taskId?.trim() || void 0,
202281
202891
  agent: coerceAgentTarget(args.agent),
202282
202892
  refresh: coerceRefreshMode(args.refresh),
202283
202893
  reader: reader2,
@@ -203780,6 +204390,158 @@ var init_feedback2 = __esm({
203780
204390
  }
203781
204391
  });
203782
204392
 
204393
+ // src/cli/commands/continuity.ts
204394
+ var continuity_exports = {};
204395
+ __export(continuity_exports, {
204396
+ default: () => continuity_default
204397
+ });
204398
+ var recordKinds, continuity_default;
204399
+ var init_continuity = __esm({
204400
+ "src/cli/commands/continuity.ts"() {
204401
+ "use strict";
204402
+ init_esm_shims();
204403
+ init_dist2();
204404
+ init_task_continuity();
204405
+ init_operator_shared();
204406
+ recordKinds = [
204407
+ "requirement",
204408
+ "decision",
204409
+ "verification",
204410
+ "risk"
204411
+ ];
204412
+ continuity_default = defineCommand({
204413
+ meta: {
204414
+ name: "continuity",
204415
+ description: "Track task requirements, decisions, verification, risks, and outcomes"
204416
+ },
204417
+ args: {
204418
+ taskId: { type: "string", description: "Continuity task id" },
204419
+ task: { type: "string", description: "Task description for start" },
204420
+ requirements: { type: "string", description: "Comma-separated requirements for start" },
204421
+ kind: { type: "string", description: "requirement, decision, verification, or risk" },
204422
+ content: { type: "string", description: "Entry or outcome text" },
204423
+ verificationStatus: { type: "string", description: "pending, passed, failed, or skipped" },
204424
+ verificationId: { type: "string", description: "Stable verification obligation id for status transitions" },
204425
+ idempotencyKey: { type: "string", description: "Stable retry key for idempotent writes" },
204426
+ status: { type: "string", description: "completed, blocked, or abandoned for close" },
204427
+ source: { type: "string", description: "Source or evidence reference" },
204428
+ evidence: { type: "string", description: "Comma-separated evidence references" },
204429
+ actor: { type: "string", description: "Actor id or label" },
204430
+ limit: { type: "string", description: "Maximum ledgers for list" },
204431
+ json: { type: "boolean", description: "Emit machine-readable JSON output" }
204432
+ },
204433
+ run: async ({ args }) => {
204434
+ const action = args._?.[0] || "";
204435
+ const asJson = !!args.json;
204436
+ try {
204437
+ const { project, dataDir, identity } = await getCliProjectContext({ searchIndex: false });
204438
+ const store2 = new TaskContinuityStore();
204439
+ await store2.init(dataDir);
204440
+ const actor = String(args.actor || identity?.agentId || "").trim() || void 0;
204441
+ if (action === "start") {
204442
+ if (!args.task) {
204443
+ emitError('task is required for "memorix continuity start"', asJson);
204444
+ return;
204445
+ }
204446
+ const result = store2.start({
204447
+ projectId: project.id,
204448
+ task: String(args.task),
204449
+ ...args.taskId ? { taskId: String(args.taskId) } : {},
204450
+ requirements: parseCsvList(args.requirements),
204451
+ ...actor ? { actor } : {},
204452
+ ...args.idempotencyKey ? { idempotencyKey: String(args.idempotencyKey) } : {}
204453
+ });
204454
+ emitResult({ project, ...result }, "Continuity started: " + result.taskId, asJson);
204455
+ return;
204456
+ }
204457
+ if (action === "list") {
204458
+ const ledgers = store2.list(project.id, Number(args.limit || 20));
204459
+ emitResult(
204460
+ { project, ledgers },
204461
+ ledgers.length ? ledgers.map((item) => item.status + ": " + item.taskId + " " + item.task).join("\n") : "No continuity ledgers found.",
204462
+ asJson
204463
+ );
204464
+ return;
204465
+ }
204466
+ const taskId = String(args.taskId || "").trim();
204467
+ if (!taskId) {
204468
+ emitError("taskId is required for this continuity action.", asJson);
204469
+ return;
204470
+ }
204471
+ if (action === "show") {
204472
+ const ledger = store2.get(project.id, taskId);
204473
+ if (!ledger) {
204474
+ emitError("Task continuity ledger not found.", asJson);
204475
+ return;
204476
+ }
204477
+ emitResult({ project, ledger }, JSON.stringify(ledger, null, 2), asJson);
204478
+ return;
204479
+ }
204480
+ if (action === "record") {
204481
+ const kind = String(args.kind || "").trim();
204482
+ const content = String(args.content || "").trim();
204483
+ if (!recordKinds.includes(kind) || !content) {
204484
+ emitError("record requires --kind (requirement, decision, verification, risk) and --content.", asJson);
204485
+ return;
204486
+ }
204487
+ const verificationStatus = args.verificationStatus ? String(args.verificationStatus).trim() : void 0;
204488
+ if (verificationStatus && !["pending", "passed", "failed", "skipped"].includes(verificationStatus)) {
204489
+ emitError("verificationStatus must be pending, passed, failed, or skipped.", asJson);
204490
+ return;
204491
+ }
204492
+ const result = store2.record({
204493
+ projectId: project.id,
204494
+ taskId,
204495
+ kind,
204496
+ content,
204497
+ ...verificationStatus ? { verificationStatus } : {},
204498
+ ...args.source ? { sourceRef: String(args.source) } : {},
204499
+ evidenceRefs: parseCsvList(args.evidence),
204500
+ ...actor ? { actor } : {},
204501
+ ...args.verificationId ? { verificationId: String(args.verificationId) } : {},
204502
+ ...args.idempotencyKey ? { idempotencyKey: String(args.idempotencyKey) } : {}
204503
+ });
204504
+ emitResult({ project, ...result }, "Continuity recorded: " + kind, asJson);
204505
+ return;
204506
+ }
204507
+ if (action === "close") {
204508
+ const status = String(args.status || "").trim();
204509
+ const content = String(args.content || "").trim();
204510
+ if (!["completed", "blocked", "abandoned"].includes(status) || !content) {
204511
+ emitError("close requires --status (completed, blocked, abandoned) and --content.", asJson);
204512
+ return;
204513
+ }
204514
+ const result = store2.close({
204515
+ projectId: project.id,
204516
+ taskId,
204517
+ status,
204518
+ content,
204519
+ ...args.source ? { sourceRef: String(args.source) } : {},
204520
+ evidenceRefs: parseCsvList(args.evidence),
204521
+ ...actor ? { actor } : {},
204522
+ ...args.idempotencyKey ? { idempotencyKey: String(args.idempotencyKey) } : {}
204523
+ });
204524
+ emitResult({ project, ...result }, "Continuity closed: " + status, asJson);
204525
+ return;
204526
+ }
204527
+ console.log("Memorix Task Continuity");
204528
+ console.log("");
204529
+ console.log("Usage:");
204530
+ console.log(' memorix continuity start --task "..." [--requirements "a,b"]');
204531
+ console.log(' memorix continuity record --taskId <id> --kind decision --content "..."');
204532
+ console.log(' memorix continuity record --taskId <id> --kind verification --verificationStatus passed --content "..."');
204533
+ console.log(" Use --verificationId <id> to transition an existing verification obligation.");
204534
+ console.log(" memorix continuity show --taskId <id>");
204535
+ console.log(" memorix continuity list [--limit 20]");
204536
+ console.log(' memorix continuity close --taskId <id> --status completed --content "..."');
204537
+ } catch (error40) {
204538
+ emitError(error40 instanceof Error ? error40.message : String(error40), asJson);
204539
+ }
204540
+ }
204541
+ });
204542
+ }
204543
+ });
204544
+
203783
204545
  // src/cli/commands/reasoning.ts
203784
204546
  var reasoning_exports = {};
203785
204547
  __export(reasoning_exports, {
@@ -206980,7 +207742,7 @@ __export(planner_exports, {
206980
207742
  materializeTaskGraph: () => materializeTaskGraph,
206981
207743
  seedAutonomousPipeline: () => seedAutonomousPipeline
206982
207744
  });
206983
- import { randomUUID as randomUUID18 } from "crypto";
207745
+ import { randomUUID as randomUUID19 } from "crypto";
206984
207746
  function isPlannerTask(metadata) {
206985
207747
  if (!metadata) return null;
206986
207748
  try {
@@ -207038,7 +207800,7 @@ function seedAutonomousPipeline(teamStore, projectId, config2, opts) {
207038
207800
  const maxIterations = config2.maxIterations ?? 3;
207039
207801
  const taskBudget = config2.taskBudget ?? 15;
207040
207802
  const structuredPlan = opts?.structuredPlan ?? true;
207041
- const pipelineId = randomUUID18();
207803
+ const pipelineId = randomUUID19();
207042
207804
  const meta = {
207043
207805
  plannerType: "plan",
207044
207806
  pipelineId,
@@ -207957,11 +208719,11 @@ __export(receipt_service_exports, {
207957
208719
  buildHandoffReceipt: () => buildHandoffReceipt,
207958
208720
  formatHandoffReceipt: () => formatHandoffReceipt
207959
208721
  });
207960
- import { createHash as createHash18 } from "crypto";
208722
+ import { createHash as createHash19 } from "crypto";
207961
208723
  import { existsSync as existsSync23, readFileSync as readFileSync15 } from "fs";
207962
208724
  import path37 from "path";
207963
208725
  function sha256(value) {
207964
- return `sha256:${createHash18("sha256").update(value).digest("hex")}`;
208726
+ return `sha256:${createHash19("sha256").update(value).digest("hex")}`;
207965
208727
  }
207966
208728
  function hashId(projectId, id) {
207967
208729
  return sha256(`${projectId}:obs:${id}`);
@@ -208194,6 +208956,7 @@ var init_tool_profile = __esm({
208194
208956
  // keep the default micro profile at its nine-tool token budget.
208195
208957
  memorix_evidence: ["lite", "team", "full"],
208196
208958
  memorix_feedback: ["lite", "team", "full"],
208959
+ memorix_continuity: ["lite", "team", "full"],
208197
208960
  // ── lite: extended solo memory surface ────────────────────────────
208198
208961
  memorix_graph_context: ["lite", "team", "full"],
208199
208962
  memorix_timeline: ["lite", "team", "full"],
@@ -208244,7 +209007,7 @@ var init_tool_profile = __esm({
208244
209007
  function getMcpServerInfo() {
208245
209008
  return {
208246
209009
  name: "memorix",
208247
- version: true ? "1.9.5" : "1.0.1"
209010
+ version: true ? "1.9.6" : "1.0.1"
208248
209011
  };
208249
209012
  }
208250
209013
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -215850,8 +216613,8 @@ var require_core = __commonJS({
215850
216613
  return this;
215851
216614
  }
215852
216615
  case "object": {
215853
- const cacheKey2 = schemaKeyRef;
215854
- this._cache.delete(cacheKey2);
216616
+ const cacheKey4 = schemaKeyRef;
216617
+ this._cache.delete(cacheKey4);
215855
216618
  let id = schemaKeyRef[this.opts.schemaId];
215856
216619
  if (id) {
215857
216620
  id = (0, resolve_1.normalizeId)(id);
@@ -221039,9 +221802,9 @@ var init_graph_scope = __esm({
221039
221802
  });
221040
221803
 
221041
221804
  // src/rules/utils.ts
221042
- import { createHash as createHash19 } from "crypto";
221805
+ import { createHash as createHash20 } from "crypto";
221043
221806
  function hashContent2(content) {
221044
- return createHash19("sha256").update(content.trim()).digest("hex").substring(0, 16);
221807
+ return createHash20("sha256").update(content.trim()).digest("hex").substring(0, 16);
221045
221808
  }
221046
221809
  function generateRuleId(source, filePath) {
221047
221810
  const sanitized = filePath.replace(/[\/\\]/g, "-").replace(/^\./, "");
@@ -222630,8 +223393,8 @@ function legacyBehavior(homeDir) {
222630
223393
  function getBehaviorConfig(options = {}) {
222631
223394
  const homeDir = options.homeDir ?? homedir34();
222632
223395
  const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
222633
- const cacheKey2 = `${homeDir}\0${projectRoot ?? ""}`;
222634
- const cached2 = cache4.get(cacheKey2);
223396
+ const cacheKey4 = `${homeDir}\0${projectRoot ?? ""}`;
223397
+ const cached2 = cache4.get(cacheKey4);
222635
223398
  if (cached2) return cached2;
222636
223399
  const legacy = legacyBehavior(homeDir);
222637
223400
  let resolved = {};
@@ -222645,7 +223408,7 @@ function getBehaviorConfig(options = {}) {
222645
223408
  autoCleanup: typeof resolved.autoCleanup === "boolean" ? resolved.autoCleanup : typeof legacy.autoCleanup === "boolean" ? legacy.autoCleanup : DEFAULTS.autoCleanup,
222646
223409
  formationMode: isFormationMode(resolved.formation) ? resolved.formation : isFormationMode(legacy.formationMode) ? legacy.formationMode : DEFAULTS.formationMode
222647
223410
  };
222648
- cache4.set(cacheKey2, config2);
223411
+ cache4.set(cacheKey4, config2);
222649
223412
  return config2;
222650
223413
  }
222651
223414
  function resetBehaviorConfigCache() {
@@ -264676,7 +265439,7 @@ __export(server_exports2, {
264676
265439
  createMemorixServer: () => createMemorixServer,
264677
265440
  shouldAwaitProjectRuntime: () => shouldAwaitProjectRuntime
264678
265441
  });
264679
- import { createHash as createHash20 } from "crypto";
265442
+ import { createHash as createHash21 } from "crypto";
264680
265443
  function formatFormationStageDurations(stageDurationsMs) {
264681
265444
  const orderedStages = ["extract", "resolve", "evaluate"];
264682
265445
  const parts = orderedStages.filter((stage) => stageDurationsMs[stage] !== void 0).map((stage) => `${stage}=${stageDurationsMs[stage]}ms`);
@@ -264758,7 +265521,7 @@ function coerceObjectArray(val) {
264758
265521
  return [];
264759
265522
  }
264760
265523
  function createDeterministicInstanceId(projectId, agentType, agentName) {
264761
- const digest2 = createHash20("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
265524
+ const digest2 = createHash21("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
264762
265525
  return `auto-${digest2}`;
264763
265526
  }
264764
265527
  function shouldAwaitProjectRuntime(toolName) {
@@ -264965,6 +265728,22 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
264965
265728
  }
264966
265729
  await projectRuntimeInitPromise;
264967
265730
  };
265731
+ const activateProjectRuntime = async () => {
265732
+ if (!projectResolved) return;
265733
+ await initObservationStore(projectDir2);
265734
+ await initMiniSkillStore(projectDir2);
265735
+ await initSessionStore(projectDir2);
265736
+ await initObservations(projectDir2, { skipCorpusLoad: true });
265737
+ try {
265738
+ const { initProjectRoot: initProjectRoot2 } = await Promise.resolve().then(() => (init_yaml_loader(), yaml_loader_exports));
265739
+ const { resetDotenv: resetDotenv2, loadDotenv: loadDotenv2 } = await Promise.resolve().then(() => (init_dotenv_loader(), dotenv_loader_exports));
265740
+ initProjectRoot2(project.rootPath);
265741
+ resetDotenv2();
265742
+ loadDotenv2(project.rootPath);
265743
+ } catch {
265744
+ }
265745
+ initLLM({ projectRoot: project.rootPath });
265746
+ };
264968
265747
  let maintenanceWorker = null;
264969
265748
  const startProjectMaintenanceWorker = async (autoCleanup = false) => {
264970
265749
  maintenanceWorker?.stop();
@@ -265880,6 +266659,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
265880
266659
  description: "Build a compact Memory Autopilot brief for the current coding task. Schedules Code Memory refresh when needed, includes Start here files, reliable code-bound memories, stale/suspect cautions, and verification hints. Use this at the start of a new coding turn or after switching tasks.",
265881
266660
  inputSchema: {
265882
266661
  task: external_exports2.string().optional().describe("Current coding task or question"),
266662
+ taskId: external_exports2.string().max(120).optional().describe("Optional task continuity ledger id to include in the brief"),
265883
266663
  refresh: external_exports2.enum(["auto", "always", "never"]).optional().default("auto").describe(
265884
266664
  "Code Memory refresh policy. auto refreshes only when missing or stale."
265885
266665
  ),
@@ -265904,7 +266684,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
265904
266684
  limit: external_exports2.number().optional().describe("Reserved for future source limits; current prompt stays compact by default.")
265905
266685
  }
265906
266686
  },
265907
- async ({ task, refresh, format, agent }) => {
266687
+ async ({ task, taskId, refresh, format, agent }) => {
265908
266688
  const unresolved = requireResolvedProject("build project context for the current project");
265909
266689
  if (unresolved) return unresolved;
265910
266690
  const [
@@ -265934,6 +266714,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
265934
266714
  dataDir: projectDir2,
265935
266715
  observations: observations2,
265936
266716
  task,
266717
+ taskId,
265937
266718
  agent,
265938
266719
  refresh: refresh ?? "auto",
265939
266720
  reader: getObservationReader(),
@@ -268359,6 +269140,94 @@ ${json4}
268359
269140
  }
268360
269141
  }
268361
269142
  );
269143
+ server.registerTool(
269144
+ "memorix_continuity",
269145
+ {
269146
+ title: "Task Continuity Ledger",
269147
+ description: "Maintain a bounded, append-only continuity ledger for a coding task. Use start for requirements, record for decisions/verification/risks, show or list to inspect, and close for the final outcome. Pass the returned taskId to memorix_project_context so the current brief includes this ledger.",
269148
+ inputSchema: {
269149
+ action: external_exports2.enum(["start", "record", "show", "list", "close"]).describe("Continuity operation"),
269150
+ taskId: external_exports2.string().max(120).optional().describe("Continuity task id"),
269151
+ task: external_exports2.string().max(1e3).optional().describe("Task description for start"),
269152
+ requirements: external_exports2.array(external_exports2.string().max(2e3)).max(8).optional().describe("Requirements for start"),
269153
+ kind: external_exports2.enum(["requirement", "decision", "verification", "risk"]).optional().describe("Entry kind for record"),
269154
+ content: external_exports2.string().max(2e3).optional().describe("Requirement, decision, verification, risk, or outcome text"),
269155
+ verificationStatus: external_exports2.enum(["pending", "passed", "failed", "skipped"]).optional(),
269156
+ verificationId: external_exports2.string().max(120).optional().describe("Stable verification obligation id; reuse it to move pending to passed/failed"),
269157
+ idempotencyKey: external_exports2.string().max(200).optional().describe("Stable retry key; repeating the same operation returns the original event"),
269158
+ status: external_exports2.enum(["completed", "blocked", "abandoned"]).optional().describe("Outcome status for close"),
269159
+ sourceRef: external_exports2.string().max(500).optional().describe("Evidence or source reference"),
269160
+ evidenceRefs: external_exports2.array(external_exports2.string().max(500)).max(8).optional(),
269161
+ actor: external_exports2.string().max(200).optional(),
269162
+ limit: external_exports2.number().int().positive().max(100).optional().default(20)
269163
+ }
269164
+ },
269165
+ async ({ action, taskId, task, requirements, kind, content, verificationStatus, verificationId, idempotencyKey, status, sourceRef, evidenceRefs: evidenceRefs2, actor, limit: limit2 }) => {
269166
+ const unresolved = requireResolvedProject("manage task continuity for the current project");
269167
+ if (unresolved) return unresolved;
269168
+ const { TaskContinuityStore: TaskContinuityStore2 } = await Promise.resolve().then(() => (init_task_continuity(), task_continuity_exports));
269169
+ const continuity = new TaskContinuityStore2();
269170
+ await continuity.init(projectDir2);
269171
+ const effectiveActor = actor ?? currentAgentId;
269172
+ try {
269173
+ if (action === "start") {
269174
+ if (!task?.trim()) return { content: [{ type: "text", text: "task is required for continuity start." }], isError: true };
269175
+ const result2 = continuity.start({
269176
+ projectId: project.id,
269177
+ task,
269178
+ ...taskId ? { taskId } : {},
269179
+ requirements,
269180
+ ...effectiveActor ? { actor: effectiveActor } : {},
269181
+ ...idempotencyKey ? { idempotencyKey } : {}
269182
+ });
269183
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result2 }, null, 2) }] };
269184
+ }
269185
+ if (action === "list") {
269186
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ledgers: continuity.list(project.id, limit2) }, null, 2) }] };
269187
+ }
269188
+ if (!taskId?.trim()) return { content: [{ type: "text", text: "taskId is required for this continuity action." }], isError: true };
269189
+ if (action === "show") {
269190
+ const ledger = continuity.get(project.id, taskId);
269191
+ if (!ledger) return { content: [{ type: "text", text: "Task continuity ledger not found." }], isError: true };
269192
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ledger }, null, 2) }] };
269193
+ }
269194
+ if (action === "record") {
269195
+ if (!kind || !content?.trim()) {
269196
+ return { content: [{ type: "text", text: "record requires kind and content." }], isError: true };
269197
+ }
269198
+ const result2 = continuity.record({
269199
+ projectId: project.id,
269200
+ taskId,
269201
+ kind,
269202
+ content,
269203
+ ...verificationStatus ? { verificationStatus } : {},
269204
+ ...sourceRef ? { sourceRef } : {},
269205
+ ...evidenceRefs2 ? { evidenceRefs: evidenceRefs2 } : {},
269206
+ ...effectiveActor ? { actor: effectiveActor } : {},
269207
+ ...verificationId ? { verificationId } : {},
269208
+ ...idempotencyKey ? { idempotencyKey } : {}
269209
+ });
269210
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result2 }, null, 2) }] };
269211
+ }
269212
+ if (!status || !content?.trim()) {
269213
+ return { content: [{ type: "text", text: "close requires status and content." }], isError: true };
269214
+ }
269215
+ const result = continuity.close({
269216
+ projectId: project.id,
269217
+ taskId,
269218
+ status,
269219
+ content,
269220
+ ...sourceRef ? { sourceRef } : {},
269221
+ ...evidenceRefs2 ? { evidenceRefs: evidenceRefs2 } : {},
269222
+ ...effectiveActor ? { actor: effectiveActor } : {},
269223
+ ...idempotencyKey ? { idempotencyKey } : {}
269224
+ });
269225
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result }, null, 2) }] };
269226
+ } catch (error40) {
269227
+ return { content: [{ type: "text", text: error40 instanceof Error ? error40.message : String(error40) }], isError: true };
269228
+ }
269229
+ }
269230
+ );
268362
269231
  if (teamFeaturesEnabled) {
268363
269232
  const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
268364
269233
  initTeamStoreForProject = initTeamStore2;
@@ -269386,7 +270255,9 @@ fi
269386
270255
  graphManager,
269387
270256
  projectId: project.id,
269388
270257
  deferredInit,
270258
+ activateProjectRuntime,
269389
270259
  switchProject,
270260
+ getProjectDataDir: () => projectDir2,
269390
270261
  isExplicitlyBound: () => projectBinding.isExplicit(),
269391
270262
  getRequestContext,
269392
270263
  handleTransportClose
@@ -269462,13 +270333,21 @@ var init_server4 = __esm({
269462
270333
  // src/server/modern-mcp-bridge.ts
269463
270334
  var modern_mcp_bridge_exports = {};
269464
270335
  __export(modern_mcp_bridge_exports, {
269465
- createModernMcpBridge: () => createModernMcpBridge
270336
+ ModernMcpRuntimePool: () => ModernMcpRuntimePool,
270337
+ createModernMcpBridge: () => createModernMcpBridge,
270338
+ createModernMcpRuntime: () => createModernMcpRuntime,
270339
+ withBusinessRuntimeQueue: () => withBusinessRuntimeQueue
269466
270340
  });
269467
270341
  import {
269468
270342
  fromJsonSchema,
269469
270343
  McpServer as ModernMcpServer
269470
270344
  } from "@modelcontextprotocol/server";
269471
- async function createModernMcpBridge(options) {
270345
+ function withBusinessRuntimeQueue(work) {
270346
+ const result = businessRuntimeQueue.then(work);
270347
+ businessRuntimeQueue = result.catch(() => void 0);
270348
+ return result;
270349
+ }
270350
+ async function createModernMcpRuntime(options) {
269472
270351
  const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server4(), server_exports2));
269473
270352
  const legacy = await createMemorixServer2(
269474
270353
  options.projectRoot,
@@ -269489,41 +270368,62 @@ async function createModernMcpBridge(options) {
269489
270368
  await legacy.server.connect(serverTransport);
269490
270369
  await legacyClient.connect(clientTransport);
269491
270370
  const listed = await legacyClient.listTools();
269492
- const modern = new ModernMcpServer(getMcpServerInfo());
269493
- for (const tool of listed.tools) {
269494
- modern.registerTool(
269495
- tool.name,
269496
- {
269497
- ...tool.title ? { title: tool.title } : {},
269498
- ...tool.description ? { description: tool.description } : {},
269499
- inputSchema: fromJsonSchema(tool.inputSchema)
269500
- },
269501
- async (args) => await legacyClient.callTool({
269502
- name: tool.name,
269503
- arguments: args
269504
- })
269505
- );
269506
- }
269507
- const bridge = modern;
269508
- bridge.memorix = {
270371
+ const callTool = (name, args) => withBusinessRuntimeQueue(async () => {
270372
+ await legacy.activateProjectRuntime();
270373
+ return await legacyClient.callTool({ name, arguments: args });
270374
+ });
270375
+ const lifecycle = {
269509
270376
  projectId: legacy.projectId,
269510
270377
  deferredInit: legacy.deferredInit,
270378
+ activateProjectRuntime: legacy.activateProjectRuntime,
269511
270379
  switchProject: legacy.switchProject,
270380
+ getProjectDataDir: legacy.getProjectDataDir,
269512
270381
  isExplicitlyBound: legacy.isExplicitlyBound,
269513
270382
  handleTransportClose: legacy.handleTransportClose
269514
270383
  };
269515
270384
  let closed = false;
269516
- const modernClose = modern.close.bind(modern);
269517
- modern.close = async () => {
270385
+ return {
270386
+ projectId: legacy.projectId,
270387
+ getProjectDataDir: legacy.getProjectDataDir,
270388
+ createBridge: () => {
270389
+ const requestServer = new ModernMcpServer(getMcpServerInfo());
270390
+ for (const tool of listed.tools) {
270391
+ requestServer.registerTool(
270392
+ tool.name,
270393
+ {
270394
+ ...tool.title ? { title: tool.title } : {},
270395
+ ...tool.description ? { description: tool.description } : {},
270396
+ inputSchema: fromJsonSchema(tool.inputSchema)
270397
+ },
270398
+ async (args) => await callTool(tool.name, args)
270399
+ );
270400
+ }
270401
+ requestServer.memorix = lifecycle;
270402
+ return requestServer;
270403
+ },
270404
+ close: async () => {
270405
+ if (closed) return;
270406
+ closed = true;
270407
+ await legacyClient.close().catch(() => void 0);
270408
+ await legacy.server.close().catch(() => void 0);
270409
+ legacy.handleTransportClose();
270410
+ }
270411
+ };
270412
+ }
270413
+ async function createModernMcpBridge(options) {
270414
+ const runtime = await createModernMcpRuntime(options);
270415
+ const bridge = runtime.createBridge();
270416
+ const close = bridge.close.bind(bridge);
270417
+ let closed = false;
270418
+ bridge.close = async () => {
269518
270419
  if (closed) return;
269519
270420
  closed = true;
269520
- await modernClose();
269521
- await legacyClient.close().catch(() => void 0);
269522
- await legacy.server.close().catch(() => void 0);
269523
- legacy.handleTransportClose();
270421
+ await close();
270422
+ await runtime.close();
269524
270423
  };
269525
270424
  return bridge;
269526
270425
  }
270426
+ var businessRuntimeQueue, ModernMcpRuntimePool;
269527
270427
  var init_modern_mcp_bridge = __esm({
269528
270428
  "src/server/modern-mcp-bridge.ts"() {
269529
270429
  "use strict";
@@ -269531,6 +270431,74 @@ var init_modern_mcp_bridge = __esm({
269531
270431
  init_client4();
269532
270432
  init_inMemory();
269533
270433
  init_mcp_discovery();
270434
+ businessRuntimeQueue = Promise.resolve();
270435
+ ModernMcpRuntimePool = class {
270436
+ constructor(maxEntries = 32) {
270437
+ this.maxEntries = maxEntries;
270438
+ }
270439
+ maxEntries;
270440
+ entries = /* @__PURE__ */ new Map();
270441
+ pending = /* @__PURE__ */ new Map();
270442
+ async createBridge(key, create6) {
270443
+ let entry = this.entries.get(key);
270444
+ if (!entry) {
270445
+ let pending = this.pending.get(key);
270446
+ if (!pending) {
270447
+ pending = create6().then((runtime) => {
270448
+ const created = { runtime, refs: 0, lastUsedAt: Date.now() };
270449
+ this.entries.set(key, created);
270450
+ return created;
270451
+ });
270452
+ this.pending.set(key, pending);
270453
+ }
270454
+ try {
270455
+ entry = await pending;
270456
+ } finally {
270457
+ this.pending.delete(key);
270458
+ }
270459
+ }
270460
+ entry.refs++;
270461
+ entry.lastUsedAt = Date.now();
270462
+ const bridge = entry.runtime.createBridge();
270463
+ const close = bridge.close.bind(bridge);
270464
+ let released = false;
270465
+ bridge.close = async () => {
270466
+ try {
270467
+ await close();
270468
+ } finally {
270469
+ if (!released) {
270470
+ released = true;
270471
+ entry.refs = Math.max(0, entry.refs - 1);
270472
+ entry.lastUsedAt = Date.now();
270473
+ }
270474
+ }
270475
+ };
270476
+ return bridge;
270477
+ }
270478
+ async evictIdle() {
270479
+ let closed = 0;
270480
+ const idle = [...this.entries.entries()].filter(([, entry]) => entry.refs === 0).sort(([, left], [, right]) => left.lastUsedAt - right.lastUsedAt);
270481
+ for (const [key, entry] of idle) {
270482
+ if (this.entries.size <= this.maxEntries || entry.refs > 0) continue;
270483
+ this.entries.delete(key);
270484
+ await entry.runtime.close();
270485
+ closed++;
270486
+ }
270487
+ return closed;
270488
+ }
270489
+ async close() {
270490
+ const entries = [...this.entries.values()];
270491
+ this.entries.clear();
270492
+ await Promise.all(entries.map((entry) => entry.runtime.close().catch(() => void 0)));
270493
+ }
270494
+ stats() {
270495
+ return {
270496
+ entries: this.entries.size,
270497
+ activeRefs: [...this.entries.values()].reduce((sum, entry) => sum + entry.refs, 0),
270498
+ pending: this.pending.size
270499
+ };
270500
+ }
270501
+ };
269534
270502
  }
269535
270503
  });
269536
270504
 
@@ -270159,7 +271127,7 @@ function writeFromReadableStream(stream2, writable) {
270159
271127
  else if (writable.destroyed) return;
270160
271128
  return writeFromReadableStreamDefaultReader(stream2.getReader(), writable);
270161
271129
  }
270162
- var RequestError, reValidRequestUrl, reDotSegment, reValidHost, buildUrl, toRequestError, GlobalRequest, Request$1, newHeadersFromIncoming, wrapBodyStream, byteExactEncodings, isByteExactEncoding, bodyBufferedBeforeDisconnectKey, bodyBufferedLengthBeforeDisconnectKey, toBufferChunk, isRecoverableDisconnectedIncoming, recordBodyBufferedBeforeDisconnect, readBodyBufferedBeforeDisconnect, enqueueBufferedBody, newRequestFromIncoming, getRequestCache, requestCache, incomingKey, urlKey, methodKey, headersKey, abortControllerKey, getAbortController, abortRequest, bodyBufferKey, bodyReadPromiseKey, bodyConsumedDirectlyKey, bodyLockReaderKey, abortReasonKey, newBodyUnusableError, rejectBodyUnusable, textDecoder2, consumeBodyDirectOnce, toArrayBuffer, contentType, methodTokenRegExp, normalizeIncomingMethod, validateDirectReadMethod, readBodyWithFastPath, readRawBodyIfAvailable, normalizeAbortError, readBodyDirect, requestPrototype, newRequest, defaultContentType, responseCache, getResponseCache, cacheKey, GlobalResponse, Response$1, validRedirectUrl, parseRedirectUrl, validRedirectStatuses, buildOutgoingHttpHeaders, outgoingEnded, incomingDraining, DRAIN_TIMEOUT_MS, MAX_DRAIN_BYTES, drainIncoming, makeCloseHandler, isImmediateCacheableResponse, handleRequestError, handleFetchError, handleResponseError, flushHeaders, responseViaCache, isPromise, responseViaResponseObject, getRequestListener, CloseEvent, ErrorEvent, generateConnectionSymbol, CONNECTION_SYMBOL_KEY, WAIT_FOR_WEBSOCKET_SYMBOL, upgradeWebSocket;
271130
+ var RequestError, reValidRequestUrl, reDotSegment, reValidHost, buildUrl, toRequestError, GlobalRequest, Request$1, newHeadersFromIncoming, wrapBodyStream, byteExactEncodings, isByteExactEncoding, bodyBufferedBeforeDisconnectKey, bodyBufferedLengthBeforeDisconnectKey, toBufferChunk, isRecoverableDisconnectedIncoming, recordBodyBufferedBeforeDisconnect, readBodyBufferedBeforeDisconnect, enqueueBufferedBody, newRequestFromIncoming, getRequestCache, requestCache, incomingKey, urlKey, methodKey, headersKey, abortControllerKey, getAbortController, abortRequest, bodyBufferKey, bodyReadPromiseKey, bodyConsumedDirectlyKey, bodyLockReaderKey, abortReasonKey, newBodyUnusableError, rejectBodyUnusable, textDecoder2, consumeBodyDirectOnce, toArrayBuffer, contentType, methodTokenRegExp, normalizeIncomingMethod, validateDirectReadMethod, readBodyWithFastPath, readRawBodyIfAvailable, normalizeAbortError, readBodyDirect, requestPrototype, newRequest, defaultContentType, responseCache, getResponseCache, cacheKey3, GlobalResponse, Response$1, validRedirectUrl, parseRedirectUrl, validRedirectStatuses, buildOutgoingHttpHeaders, outgoingEnded, incomingDraining, DRAIN_TIMEOUT_MS, MAX_DRAIN_BYTES, drainIncoming, makeCloseHandler, isImmediateCacheableResponse, handleRequestError, handleFetchError, handleResponseError, flushHeaders, responseViaCache, isPromise, responseViaResponseObject, getRequestListener, CloseEvent, ErrorEvent, generateConnectionSymbol, CONNECTION_SYMBOL_KEY, WAIT_FOR_WEBSOCKET_SYMBOL, upgradeWebSocket;
270163
271131
  var init_dist8 = __esm({
270164
271132
  "node_modules/@hono/node-server/dist/index.mjs"() {
270165
271133
  "use strict";
@@ -270630,15 +271598,15 @@ var init_dist8 = __esm({
270630
271598
  defaultContentType = "text/plain; charset=UTF-8";
270631
271599
  responseCache = /* @__PURE__ */ Symbol("responseCache");
270632
271600
  getResponseCache = /* @__PURE__ */ Symbol("getResponseCache");
270633
- cacheKey = /* @__PURE__ */ Symbol("cache");
271601
+ cacheKey3 = /* @__PURE__ */ Symbol("cache");
270634
271602
  GlobalResponse = global.Response;
270635
271603
  Response$1 = class Response$12 {
270636
271604
  #body;
270637
271605
  #init;
270638
271606
  [getResponseCache]() {
270639
- const cache5 = this[cacheKey];
271607
+ const cache5 = this[cacheKey3];
270640
271608
  const liveHeaders = cache5 && cache5[2] instanceof Headers ? cache5[2] : void 0;
270641
- delete this[cacheKey];
271609
+ delete this[cacheKey3];
270642
271610
  return this[responseCache] ||= new GlobalResponse(this.#body, liveHeaders ? {
270643
271611
  status: this.#init?.status,
270644
271612
  statusText: this.#init?.statusText,
@@ -270658,14 +271626,14 @@ var init_dist8 = __esm({
270658
271626
  this.#init = init2 instanceof Response$12 ? init2.#init : init2;
270659
271627
  headers = new Headers(init2.headers);
270660
271628
  } else this.#init = init2;
270661
- if (body == null || typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey] = [
271629
+ if (body == null || typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey3] = [
270662
271630
  init2?.status || 200,
270663
271631
  body ?? null,
270664
271632
  headers || init2?.headers
270665
271633
  ];
270666
271634
  }
270667
271635
  get headers() {
270668
- const cache5 = this[cacheKey];
271636
+ const cache5 = this[cacheKey3];
270669
271637
  if (cache5) {
270670
271638
  if (!(cache5[2] instanceof Headers)) cache5[2] = new Headers(cache5[2] || (cache5[1] === null ? void 0 : { "content-type": defaultContentType }));
270671
271639
  return cache5[2];
@@ -270673,7 +271641,7 @@ var init_dist8 = __esm({
270673
271641
  return this[getResponseCache]().headers;
270674
271642
  }
270675
271643
  get status() {
270676
- return this[cacheKey]?.[0] ?? this[getResponseCache]().status;
271644
+ return this[cacheKey3]?.[0] ?? this[getResponseCache]().status;
270677
271645
  }
270678
271646
  get ok() {
270679
271647
  const status = this.status;
@@ -270826,8 +271794,8 @@ var init_dist8 = __esm({
270826
271794
  });
270827
271795
  };
270828
271796
  isImmediateCacheableResponse = (res) => {
270829
- if (!(cacheKey in res)) return false;
270830
- const body = res[cacheKey][1];
271797
+ if (!(cacheKey3 in res)) return false;
271798
+ const body = res[cacheKey3][1];
270831
271799
  return body === null || typeof body === "string" || body instanceof Uint8Array;
270832
271800
  };
270833
271801
  handleRequestError = () => new Response(null, { status: 400 });
@@ -270846,7 +271814,7 @@ var init_dist8 = __esm({
270846
271814
  if ("flushHeaders" in outgoing && outgoing.writable) outgoing.flushHeaders();
270847
271815
  };
270848
271816
  responseViaCache = async (res, outgoing) => {
270849
- let [status, body, header] = res[cacheKey];
271817
+ let [status, body, header] = res[cacheKey3];
270850
271818
  if (!header) {
270851
271819
  if (body === null) {
270852
271820
  outgoing.writeHead(status);
@@ -270914,7 +271882,7 @@ var init_dist8 = __esm({
270914
271882
  res = errRes;
270915
271883
  }
270916
271884
  else res = await res.catch(handleFetchError);
270917
- if (cacheKey in res) return responseViaCache(res, outgoing);
271885
+ if (cacheKey3 in res) return responseViaCache(res, outgoing);
270918
271886
  const resHeaderRecord = buildOutgoingHttpHeaders(res.headers, res.body === null ? void 0 : defaultContentType);
270919
271887
  if (res.body) {
270920
271888
  const reader2 = res.body.getReader();
@@ -272151,7 +273119,7 @@ __export(mcp_binding_store_exports, {
272151
273119
  DEFAULT_MCP_BINDING_TTL_MS: () => DEFAULT_MCP_BINDING_TTL_MS,
272152
273120
  McpBindingStore: () => McpBindingStore
272153
273121
  });
272154
- import { randomUUID as randomUUID19 } from "crypto";
273122
+ import { randomUUID as randomUUID20 } from "crypto";
272155
273123
  function rowToBinding(row) {
272156
273124
  return {
272157
273125
  handleId: String(row.handle_id),
@@ -272183,7 +273151,7 @@ var init_mcp_binding_store = __esm({
272183
273151
  const now3 = /* @__PURE__ */ new Date();
272184
273152
  const ttlMs = input.ttlMs ?? DEFAULT_MCP_BINDING_TTL_MS;
272185
273153
  const binding = {
272186
- handleId: `mxh_${randomUUID19().replace(/-/g, "")}`,
273154
+ handleId: `mxh_${randomUUID20().replace(/-/g, "")}`,
272187
273155
  projectId: input.projectId,
272188
273156
  projectRoot: input.projectRoot,
272189
273157
  dataDir: input.dataDir,
@@ -272310,10 +273278,14 @@ var init_control_plane_maintenance = __esm({
272310
273278
  var serve_http_exports = {};
272311
273279
  __export(serve_http_exports, {
272312
273280
  DEFAULT_HTTP_BODY_LIMIT_BYTES: () => DEFAULT_HTTP_BODY_LIMIT_BYTES,
273281
+ DEFAULT_MAX_HTTP_SESSIONS: () => DEFAULT_MAX_HTTP_SESSIONS,
272313
273282
  DEFAULT_SESSION_TIMEOUT_MS: () => DEFAULT_SESSION_TIMEOUT_MS,
272314
273283
  EXPIRED_SESSION_TTL_MS: () => EXPIRED_SESSION_TTL_MS,
272315
273284
  HttpPayloadTooLargeError: () => HttpPayloadTooLargeError,
272316
273285
  MAX_HTTP_BODY_LIMIT_BYTES: () => MAX_HTTP_BODY_LIMIT_BYTES,
273286
+ MAX_HTTP_SESSIONS_LIMIT: () => MAX_HTTP_SESSIONS_LIMIT,
273287
+ MAX_PROJECT_CACHE_ENTRIES: () => MAX_PROJECT_CACHE_ENTRIES,
273288
+ MAX_STORE_CACHE_ENTRIES: () => MAX_STORE_CACHE_ENTRIES,
272317
273289
  MCP_SDK_COMPAT_PROTOCOL_VERSION: () => MCP_SDK_COMPAT_PROTOCOL_VERSION,
272318
273290
  MIN_HTTP_BODY_LIMIT_BYTES: () => MIN_HTTP_BODY_LIMIT_BYTES,
272319
273291
  _testing: () => _testing2,
@@ -272322,11 +273294,13 @@ __export(serve_http_exports, {
272322
273294
  isServerDiscoverRequest: () => isServerDiscoverRequest,
272323
273295
  parseHttpBodyLimit: () => parseHttpBodyLimit,
272324
273296
  parseJsonBody: () => parseJsonBody,
273297
+ parseMaxHttpSessions: () => parseMaxHttpSessions,
272325
273298
  parseSessionTimeoutMs: () => parseSessionTimeoutMs,
272326
273299
  resolveServeHttpDefaults: () => resolveServeHttpDefaults,
272327
273300
  validateHttpBindSecurity: () => validateHttpBindSecurity
272328
273301
  });
272329
273302
  import { getHeapStatistics } from "v8";
273303
+ import { monitorEventLoopDelay } from "perf_hooks";
272330
273304
  import { timingSafeEqual as timingSafeEqual2 } from "crypto";
272331
273305
  function isServerDiscoverRequest(body) {
272332
273306
  return Boolean(body && typeof body === "object" && !Array.isArray(body) && body.method === "server/discover");
@@ -272347,6 +273321,13 @@ function parseHttpBodyLimit(raw) {
272347
273321
  }
272348
273322
  return Math.min(parsed, MAX_HTTP_BODY_LIMIT_BYTES);
272349
273323
  }
273324
+ function parseMaxHttpSessions(raw) {
273325
+ const value = raw?.trim();
273326
+ if (!value) return DEFAULT_MAX_HTTP_SESSIONS;
273327
+ const parsed = Number(value);
273328
+ if (!Number.isSafeInteger(parsed) || parsed < 1) return DEFAULT_MAX_HTTP_SESSIONS;
273329
+ return Math.min(parsed, MAX_HTTP_SESSIONS_LIMIT);
273330
+ }
272350
273331
  function isLoopbackHost(host) {
272351
273332
  const normalized = host.trim().toLowerCase();
272352
273333
  return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1" || normalized === "[::1]";
@@ -272419,7 +273400,7 @@ function resolveServeHttpDefaults(argsPort, configured) {
272419
273400
  const transportNotice = configured.transport === "stdio" ? '[memorix] config server.transport = "stdio"; serve-http continues on HTTP (the explicit command wins).' : void 0;
272420
273401
  return { port, transportNotice };
272421
273402
  }
272422
- var DEFAULT_SESSION_TIMEOUT_MS, EXPIRED_SESSION_TTL_MS, DEFAULT_HTTP_BODY_LIMIT_BYTES, MAX_HTTP_BODY_LIMIT_BYTES, MIN_HTTP_BODY_LIMIT_BYTES, MCP_SDK_COMPAT_PROTOCOL_VERSION, HttpPayloadTooLargeError, serve_http_default, _testing2;
273403
+ var DEFAULT_SESSION_TIMEOUT_MS, EXPIRED_SESSION_TTL_MS, DEFAULT_HTTP_BODY_LIMIT_BYTES, MAX_HTTP_BODY_LIMIT_BYTES, MIN_HTTP_BODY_LIMIT_BYTES, MAX_PROJECT_CACHE_ENTRIES, MAX_STORE_CACHE_ENTRIES, DEFAULT_MAX_HTTP_SESSIONS, MAX_HTTP_SESSIONS_LIMIT, MCP_SDK_COMPAT_PROTOCOL_VERSION, HttpPayloadTooLargeError, serve_http_default, _testing2;
272423
273404
  var init_serve_http = __esm({
272424
273405
  "src/cli/commands/serve-http.ts"() {
272425
273406
  "use strict";
@@ -272438,6 +273419,10 @@ var init_serve_http = __esm({
272438
273419
  DEFAULT_HTTP_BODY_LIMIT_BYTES = 10 * 1024 * 1024;
272439
273420
  MAX_HTTP_BODY_LIMIT_BYTES = 64 * 1024 * 1024;
272440
273421
  MIN_HTTP_BODY_LIMIT_BYTES = 64 * 1024;
273422
+ MAX_PROJECT_CACHE_ENTRIES = 256;
273423
+ MAX_STORE_CACHE_ENTRIES = 32;
273424
+ DEFAULT_MAX_HTTP_SESSIONS = 64;
273425
+ MAX_HTTP_SESSIONS_LIMIT = 512;
272441
273426
  MCP_SDK_COMPAT_PROTOCOL_VERSION = "2025-11-25";
272442
273427
  HttpPayloadTooLargeError = class extends Error {
272443
273428
  statusCode = 413;
@@ -272475,7 +273460,7 @@ var init_serve_http = __esm({
272475
273460
  },
272476
273461
  run: async ({ args }) => {
272477
273462
  const { createServer: createServer3 } = await import("http");
272478
- const { randomUUID: randomUUID21 } = await import("crypto");
273463
+ const { randomUUID: randomUUID23 } = await import("crypto");
272479
273464
  const { StreamableHTTPServerTransport: StreamableHTTPServerTransport2 } = await Promise.resolve().then(() => (init_streamableHttp(), streamableHttp_exports));
272480
273465
  const { isInitializeRequest: isInitializeRequest2 } = await Promise.resolve().then(() => (init_types8(), types_exports3));
272481
273466
  const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server4(), server_exports2));
@@ -272485,7 +273470,8 @@ var init_serve_http = __esm({
272485
273470
  const { homedir: homedir38 } = await import("os");
272486
273471
  const { McpBindingStore: McpBindingStore2 } = await Promise.resolve().then(() => (init_mcp_binding_store(), mcp_binding_store_exports));
272487
273472
  const { MemoryFeedbackStore: MemoryFeedbackStore2 } = await Promise.resolve().then(() => (init_feedback(), feedback_exports));
272488
- const { createModernMcpBridge: createModernMcpBridge2 } = await Promise.resolve().then(() => (init_modern_mcp_bridge(), modern_mcp_bridge_exports));
273473
+ const { createModernMcpRuntime: createModernMcpRuntime2, ModernMcpRuntimePool: ModernMcpRuntimePool2, withBusinessRuntimeQueue: withBusinessRuntimeQueue2 } = await Promise.resolve().then(() => (init_modern_mcp_bridge(), modern_mcp_bridge_exports));
273474
+ const { acquireDatabase: acquireDatabase2, evictIdleDatabases: evictIdleDatabases2, getDatabaseStats: getDatabaseStats2 } = await Promise.resolve().then(() => (init_sqlite_db(), sqlite_db_exports));
272489
273475
  const { createMcpHandler } = await import("@modelcontextprotocol/server");
272490
273476
  const { toNodeHandler } = await import("@modelcontextprotocol/node");
272491
273477
  let configuredDefaults = {};
@@ -272537,42 +273523,108 @@ var init_serve_http = __esm({
272537
273523
  console.error(`[memorix] Project root: ${projectRoot}`);
272538
273524
  const sessionTimeoutMs = parseSessionTimeoutMs(process.env.MEMORIX_SESSION_TIMEOUT_MS);
272539
273525
  const httpBodyLimitBytes = parseHttpBodyLimit(process.env.MEMORIX_HTTP_MAX_BODY_BYTES);
273526
+ const maxHttpSessions = parseMaxHttpSessions(process.env.MEMORIX_MAX_HTTP_SESSIONS);
273527
+ const eventLoopDelay = monitorEventLoopDelay({ resolution: 20 });
273528
+ eventLoopDelay.enable();
272540
273529
  console.error(
272541
273530
  sessionTimeoutMs === 0 ? "[memorix] HTTP session idle timeout: disabled" : `[memorix] HTTP session idle timeout: ${Math.round(sessionTimeoutMs / 6e4)}min (${sessionTimeoutMs}ms)`
272542
273531
  );
273532
+ console.error(`[memorix] HTTP session limit: ${maxHttpSessions}`);
272543
273533
  const teamStoreCache = /* @__PURE__ */ new Map();
273534
+ const teamStoreLeases = /* @__PURE__ */ new Map();
272544
273535
  const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
273536
+ function cacheSet3(cache5, key, value, limit2, onEvict) {
273537
+ cache5.delete(key);
273538
+ cache5.set(key, value);
273539
+ while (cache5.size > limit2) {
273540
+ const oldest = cache5.keys().next().value;
273541
+ if (oldest === void 0) break;
273542
+ const oldValue = cache5.get(oldest);
273543
+ cache5.delete(oldest);
273544
+ if (oldValue !== void 0) onEvict?.(oldest, oldValue);
273545
+ }
273546
+ }
272545
273547
  async function getTeamStore2(dataDir) {
272546
273548
  const existing = teamStoreCache.get(dataDir);
272547
- if (existing) return existing;
272548
- const store2 = await initTeamStore2(dataDir);
272549
- teamStoreCache.set(dataDir, store2);
272550
- return store2;
273549
+ if (existing) {
273550
+ cacheSet3(teamStoreCache, dataDir, existing, MAX_STORE_CACHE_ENTRIES);
273551
+ return existing;
273552
+ }
273553
+ const lease = acquireDatabase2(dataDir);
273554
+ try {
273555
+ const store2 = await initTeamStore2(dataDir);
273556
+ teamStoreLeases.set(dataDir, lease);
273557
+ cacheSet3(teamStoreCache, dataDir, store2, MAX_STORE_CACHE_ENTRIES, (evictedDataDir) => {
273558
+ teamStoreLeases.get(evictedDataDir)?.release();
273559
+ teamStoreLeases.delete(evictedDataDir);
273560
+ evictIdleDatabases2(MAX_STORE_CACHE_ENTRIES);
273561
+ });
273562
+ return store2;
273563
+ } catch (error40) {
273564
+ lease.release();
273565
+ throw error40;
273566
+ }
272551
273567
  }
273568
+ const controlPlaneBaseDir = persistMod.getBaseDataDir();
273569
+ const controlPlaneBaseLease = acquireDatabase2(controlPlaneBaseDir);
272552
273570
  const statelessBindingStore = new McpBindingStore2();
272553
- await statelessBindingStore.init(persistMod.getBaseDataDir());
273571
+ await statelessBindingStore.init(controlPlaneBaseDir);
273572
+ const modernRuntimePool = new ModernMcpRuntimePool2(MAX_STORE_CACHE_ENTRIES);
273573
+ async function resolveModernDataDir(root, persisted) {
273574
+ if (persisted?.dataDir) return persisted.dataDir;
273575
+ const detected = detectorMod.detectProject(root);
273576
+ return detected ? persistMod.getProjectDataDir(detected.id) : controlPlaneBaseDir;
273577
+ }
272554
273578
  const modernMcpHandler = createMcpHandler(async ({ requestInfo }) => {
272555
273579
  const handleId = requestInfo?.headers.get("mcp-project-handle") ?? void 0;
272556
273580
  const persisted = handleId ? statelessBindingStore.touch(handleId) : void 0;
272557
273581
  const requestedRoot = requestInfo?.headers.get("mcp-project-root") ?? void 0;
272558
273582
  const root = persisted?.projectRoot ?? requestedRoot ?? projectRoot;
272559
- return createModernMcpBridge2({
272560
- projectRoot: root,
272561
- allowUntrackedFallback: false,
272562
- deferProjectInitUntilBound: true,
272563
- deferProjectRuntimeInit: true,
272564
- dashboardMode: "control-plane",
272565
- dashboardPort: port,
272566
- toolProfile
272567
- });
273583
+ const dataDir = await resolveModernDataDir(root, persisted);
273584
+ const runtimeKey = JSON.stringify([dataDir, root]);
273585
+ return modernRuntimePool.createBridge(runtimeKey, () => withBusinessRuntimeQueue2(async () => {
273586
+ const lease = acquireDatabase2(dataDir);
273587
+ try {
273588
+ const runtime = await createModernMcpRuntime2({
273589
+ projectRoot: root,
273590
+ allowUntrackedFallback: false,
273591
+ deferProjectInitUntilBound: true,
273592
+ deferProjectRuntimeInit: true,
273593
+ dashboardMode: "control-plane",
273594
+ dashboardPort: port,
273595
+ toolProfile
273596
+ });
273597
+ const close = runtime.close.bind(runtime);
273598
+ return {
273599
+ ...runtime,
273600
+ close: async () => {
273601
+ try {
273602
+ await close();
273603
+ } finally {
273604
+ lease.release();
273605
+ }
273606
+ }
273607
+ };
273608
+ } catch (error40) {
273609
+ lease.release();
273610
+ throw error40;
273611
+ }
273612
+ }));
272568
273613
  }, {
272569
273614
  // Legacy requests are routed to the existing sessionful implementation
272570
273615
  // below; this handler is strict modern-only by construction.
272571
273616
  legacy: "reject",
272572
- responseMode: "auto"
273617
+ responseMode: "auto",
273618
+ onerror: (error40) => {
273619
+ const message = error40 instanceof Error ? error40.message : String(error40);
273620
+ const stack = error40 instanceof Error ? error40.stack : void 0;
273621
+ console.error(`[memorix] modern MCP request failed: ${message}${stack ? `
273622
+ ${stack}` : ""}`);
273623
+ }
272573
273624
  });
272574
273625
  const modernNodeHandler = toNodeHandler(modernMcpHandler);
272575
273626
  const sessions = /* @__PURE__ */ new Map();
273627
+ let pendingSessionInitializations = 0;
272576
273628
  const sessionLastActivity = /* @__PURE__ */ new Map();
272577
273629
  const expiredSessions = /* @__PURE__ */ new Map();
272578
273630
  let suppressedProbeCount = 0;
@@ -272747,7 +273799,11 @@ var init_serve_http = __esm({
272747
273799
  if (sessionId && sessions.has(sessionId)) {
272748
273800
  sessionLastActivity.set(sessionId, Date.now());
272749
273801
  patchMcpResponse(res);
272750
- await sessions.get(sessionId).transport.handleRequest(req, res, body);
273802
+ const session = sessions.get(sessionId);
273803
+ await withBusinessRuntimeQueue2(async () => {
273804
+ await session.activateProjectRuntime();
273805
+ await session.transport.handleRequest(req, res, body);
273806
+ });
272751
273807
  return;
272752
273808
  }
272753
273809
  if (sessionId) {
@@ -272755,11 +273811,32 @@ var init_serve_http = __esm({
272755
273811
  return;
272756
273812
  }
272757
273813
  if (!sessionId && isInitializeRequest2(body)) {
273814
+ if (sessions.size + pendingSessionInitializations >= maxHttpSessions) {
273815
+ res.writeHead(429, {
273816
+ "Content-Type": "application/json",
273817
+ "Retry-After": "5"
273818
+ });
273819
+ res.end(JSON.stringify({
273820
+ jsonrpc: "2.0",
273821
+ error: { code: -32029, message: `HTTP session limit reached (${maxHttpSessions}). Retry after an existing session closes.` },
273822
+ id: body.id ?? null
273823
+ }));
273824
+ return;
273825
+ }
273826
+ pendingSessionInitializations++;
273827
+ let pendingReservationReleased = false;
273828
+ const releasePendingReservation = () => {
273829
+ if (pendingReservationReleased) return;
273830
+ pendingReservationReleased = true;
273831
+ pendingSessionInitializations = Math.max(0, pendingSessionInitializations - 1);
273832
+ };
272758
273833
  let createdState = null;
273834
+ const sessionLeases = [acquireDatabase2(defaultDataDir)];
272759
273835
  const transport = new StreamableHTTPServerTransport2({
272760
- sessionIdGenerator: () => randomUUID21(),
273836
+ sessionIdGenerator: () => randomUUID23(),
272761
273837
  enableJsonResponse: true,
272762
273838
  onsessioninitialized: (sid) => {
273839
+ releasePendingReservation();
272763
273840
  if (createdState) sessions.set(sid, createdState);
272764
273841
  sessionLastActivity.set(sid, Date.now());
272765
273842
  const timer = setTimeout(() => {
@@ -272770,6 +273847,7 @@ var init_serve_http = __esm({
272770
273847
  }
272771
273848
  });
272772
273849
  transport.onclose = () => {
273850
+ releasePendingReservation();
272773
273851
  const sid = transport.sessionId;
272774
273852
  if (sid) {
272775
273853
  const pending = pendingLogTimers.get(sid);
@@ -272785,26 +273863,49 @@ var init_serve_http = __esm({
272785
273863
  sessionLastActivity.delete(sid);
272786
273864
  }
272787
273865
  handleTransportClose();
273866
+ for (const lease of sessionLeases) lease.release();
273867
+ evictIdleDatabases2(MAX_STORE_CACHE_ENTRIES);
272788
273868
  };
272789
273869
  const binding = createProjectBindingController2(projectRoot);
272790
- const { server, switchProject, handleTransportClose } = await createMemorixServer2(
272791
- projectRoot,
272792
- void 0,
272793
- void 0,
272794
- {
272795
- allowUntrackedFallback: false,
272796
- deferProjectInitUntilBound: true,
272797
- // Same as stdio: initialize/tools/list must not hydrate 40k+
272798
- // observations on the HTTP thread. /health and the LaunchAgent
272799
- // watchdog die if createMemorixServer blocks here.
272800
- deferProjectRuntimeInit: true,
272801
- dashboardMode: "control-plane",
272802
- dashboardPort: port,
272803
- toolProfile,
272804
- projectBinding: binding
273870
+ let server;
273871
+ let activateProjectRuntime;
273872
+ let handleTransportClose;
273873
+ let getProjectDataDirForSession;
273874
+ let switchProject;
273875
+ try {
273876
+ ({ server, activateProjectRuntime, switchProject, handleTransportClose, getProjectDataDir: getProjectDataDirForSession } = await createMemorixServer2(
273877
+ projectRoot,
273878
+ void 0,
273879
+ void 0,
273880
+ {
273881
+ allowUntrackedFallback: false,
273882
+ deferProjectInitUntilBound: true,
273883
+ // Same as stdio: initialize/tools/list must not hydrate 40k+
273884
+ // observations on the HTTP thread. /health and the LaunchAgent
273885
+ // watchdog die if createMemorixServer blocks here.
273886
+ deferProjectRuntimeInit: true,
273887
+ dashboardMode: "control-plane",
273888
+ dashboardPort: port,
273889
+ toolProfile,
273890
+ projectBinding: binding
273891
+ }
273892
+ ));
273893
+ } catch (error40) {
273894
+ releasePendingReservation();
273895
+ for (const lease of sessionLeases) lease.release();
273896
+ throw error40;
273897
+ }
273898
+ const switchProjectWithLease = async (newCwd, source) => {
273899
+ const before = getProjectDataDirForSession();
273900
+ const changed = await switchProject(newCwd, source);
273901
+ const after = getProjectDataDirForSession();
273902
+ if (changed && after !== before) {
273903
+ sessionLeases[0].release();
273904
+ sessionLeases[0] = acquireDatabase2(after);
272805
273905
  }
272806
- );
272807
- createdState = { transport, server, switchProject, binding };
273906
+ return changed;
273907
+ };
273908
+ createdState = { transport, server, activateProjectRuntime, switchProject: switchProjectWithLease, binding, leases: sessionLeases };
272808
273909
  await server.connect(transport);
272809
273910
  const tryRootsSwitch = async () => {
272810
273911
  try {
@@ -272843,7 +273944,14 @@ var init_serve_http = __esm({
272843
273944
  } catch {
272844
273945
  }
272845
273946
  patchMcpResponse(res);
272846
- await transport.handleRequest(req, res, body);
273947
+ try {
273948
+ await withBusinessRuntimeQueue2(async () => {
273949
+ await activateProjectRuntime();
273950
+ await transport.handleRequest(req, res, body);
273951
+ });
273952
+ } finally {
273953
+ if (!transport.sessionId) releasePendingReservation();
273954
+ }
272847
273955
  return;
272848
273956
  }
272849
273957
  res.writeHead(400, { "Content-Type": "application/json" });
@@ -272877,7 +273985,7 @@ var init_serve_http = __esm({
272877
273985
  sendUnknownSession(res, sessionId);
272878
273986
  return;
272879
273987
  }
272880
- await sessions.get(sessionId).transport.handleRequest(req, res);
273988
+ await withBusinessRuntimeQueue2(() => sessions.get(sessionId).transport.handleRequest(req, res));
272881
273989
  }
272882
273990
  const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
272883
273991
  const { getProjectDataDir: getProjectDataDir2, getBaseDataDir: getBaseDataDir2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
@@ -272898,8 +274006,9 @@ var init_serve_http = __esm({
272898
274006
  });
272899
274007
  controlPlaneMaintenanceWorker.start();
272900
274008
  const projectDataDirCache = /* @__PURE__ */ new Map();
272901
- projectDataDirCache.set(defaultProject.id, defaultDataDir);
274009
+ cacheSet3(projectDataDirCache, defaultProject.id, defaultDataDir, MAX_PROJECT_CACHE_ENTRIES);
272902
274010
  const dashboardObservationStores = /* @__PURE__ */ new Map();
274011
+ const dashboardStoreLeases = /* @__PURE__ */ new Map();
272903
274012
  async function resolveRequestProject(url2) {
272904
274013
  const requestedId = url2.searchParams.get("project");
272905
274014
  if (!requestedId || requestedId === defaultProject.id) {
@@ -272908,18 +274017,36 @@ var init_serve_http = __esm({
272908
274017
  let dataDir = projectDataDirCache.get(requestedId);
272909
274018
  if (!dataDir) {
272910
274019
  dataDir = await getProjectDataDir2(requestedId);
272911
- projectDataDirCache.set(requestedId, dataDir);
274020
+ cacheSet3(projectDataDirCache, requestedId, dataDir, MAX_PROJECT_CACHE_ENTRIES);
272912
274021
  }
272913
274022
  const name = requestedId.split("/").pop() || requestedId;
272914
274023
  return { projectId: requestedId, projectName: name, dataDir };
272915
274024
  }
272916
274025
  async function getDashboardObservationStore(dataDir) {
272917
274026
  const cached2 = dashboardObservationStores.get(dataDir);
272918
- if (cached2) return cached2;
274027
+ if (cached2) {
274028
+ cacheSet3(dashboardObservationStores, dataDir, cached2, MAX_STORE_CACHE_ENTRIES);
274029
+ return cached2;
274030
+ }
272919
274031
  const { createObservationStore: createObservationStore2 } = await Promise.resolve().then(() => (init_obs_store(), obs_store_exports));
272920
- const store2 = await createObservationStore2(dataDir);
272921
- dashboardObservationStores.set(dataDir, store2);
272922
- return store2;
274032
+ const lease = acquireDatabase2(dataDir);
274033
+ try {
274034
+ const store2 = await createObservationStore2(dataDir);
274035
+ dashboardStoreLeases.set(dataDir, lease);
274036
+ cacheSet3(dashboardObservationStores, dataDir, store2, MAX_STORE_CACHE_ENTRIES, (evictedDataDir, evictedStore) => {
274037
+ try {
274038
+ evictedStore.close();
274039
+ } catch {
274040
+ }
274041
+ dashboardStoreLeases.get(evictedDataDir)?.release();
274042
+ dashboardStoreLeases.delete(evictedDataDir);
274043
+ evictIdleDatabases2(MAX_STORE_CACHE_ENTRIES);
274044
+ });
274045
+ return store2;
274046
+ } catch (error40) {
274047
+ lease.release();
274048
+ throw error40;
274049
+ }
272923
274050
  }
272924
274051
  async function loadDashboardObservations(dataDir) {
272925
274052
  const store2 = await getDashboardObservationStore(dataDir);
@@ -273749,6 +274876,18 @@ var init_serve_http = __esm({
273749
274876
  arrayBuffers: memory.arrayBuffers,
273750
274877
  heapLimit: heap.heap_size_limit
273751
274878
  },
274879
+ sqlite: getDatabaseStats2(),
274880
+ sessions: {
274881
+ active: sessions.size,
274882
+ pending: pendingSessionInitializations,
274883
+ limit: maxHttpSessions
274884
+ },
274885
+ modernRuntime: modernRuntimePool.stats(),
274886
+ eventLoop: {
274887
+ meanMs: Number.isFinite(eventLoopDelay.mean) ? eventLoopDelay.mean / 1e6 : null,
274888
+ p95Ms: eventLoopDelay.percentile(95) / 1e6,
274889
+ maxMs: eventLoopDelay.max / 1e6
274890
+ },
273752
274891
  embedding: getEmbeddingRuntimeHealth2()
273753
274892
  }));
273754
274893
  return;
@@ -273865,6 +275004,13 @@ var init_serve_http = __esm({
273865
275004
  }
273866
275005
  }
273867
275006
  dashboardObservationStores.clear();
275007
+ for (const lease of dashboardStoreLeases.values()) lease.release();
275008
+ dashboardStoreLeases.clear();
275009
+ for (const lease of teamStoreLeases.values()) lease.release();
275010
+ teamStoreLeases.clear();
275011
+ controlPlaneBaseLease.release();
275012
+ await modernRuntimePool.close();
275013
+ eventLoopDelay.disable();
273868
275014
  const { closeAllDatabases: closeAllDatabases2 } = await Promise.resolve().then(() => (init_sqlite_db(), sqlite_db_exports));
273869
275015
  closeAllDatabases2();
273870
275016
  throw error40;
@@ -273875,6 +275021,8 @@ var init_serve_http = __esm({
273875
275021
  const now3 = Date.now();
273876
275022
  forgetExpiredSessions(now3);
273877
275023
  statelessBindingStore.cleanupExpired(now3);
275024
+ evictIdleDatabases2(MAX_STORE_CACHE_ENTRIES);
275025
+ void modernRuntimePool.evictIdle();
273878
275026
  if (SESSION_TIMEOUT_MS === 0) return;
273879
275027
  for (const [sid, state] of sessions) {
273880
275028
  const lastActive = sessionLastActivity.get(sid) ?? 0;
@@ -273918,6 +275066,10 @@ var init_serve_http = __esm({
273918
275066
  }
273919
275067
  }
273920
275068
  dashboardObservationStores.clear();
275069
+ for (const lease of dashboardStoreLeases.values()) lease.release();
275070
+ dashboardStoreLeases.clear();
275071
+ for (const lease of teamStoreLeases.values()) lease.release();
275072
+ teamStoreLeases.clear();
273921
275073
  for (const [sid, state] of sessions) {
273922
275074
  try {
273923
275075
  await state.transport.close();
@@ -273926,8 +275078,10 @@ var init_serve_http = __esm({
273926
275078
  console.error(`[memorix] Error closing session ${sid}:`, err);
273927
275079
  }
273928
275080
  }
275081
+ await modernRuntimePool.close();
273929
275082
  clearInterval(gcInterval);
273930
275083
  clearInterval(heartbeatInterval);
275084
+ eventLoopDelay.disable();
273931
275085
  await new Promise((resolve9) => {
273932
275086
  if (!httpServer.listening) {
273933
275087
  resolve9();
@@ -273936,6 +275090,7 @@ var init_serve_http = __esm({
273936
275090
  httpServer.close(() => resolve9());
273937
275091
  });
273938
275092
  const { closeAllDatabases: closeAllDatabases2 } = await Promise.resolve().then(() => (init_sqlite_db(), sqlite_db_exports));
275093
+ controlPlaneBaseLease.release();
273939
275094
  closeAllDatabases2();
273940
275095
  process.exit(0);
273941
275096
  };
@@ -273979,6 +275134,7 @@ var init_serve_http = __esm({
273979
275134
  MAX_HTTP_BODY_LIMIT_BYTES,
273980
275135
  MIN_HTTP_BODY_LIMIT_BYTES,
273981
275136
  parseSessionTimeoutMs,
275137
+ parseMaxHttpSessions,
273982
275138
  parseHttpBodyLimit,
273983
275139
  isLoopbackHost,
273984
275140
  validateHttpBindSecurity,
@@ -274328,7 +275484,7 @@ var init_merge = __esm({
274328
275484
  });
274329
275485
 
274330
275486
  // src/sync/journal.ts
274331
- import { createHash as createHash21 } from "crypto";
275487
+ import { createHash as createHash22 } from "crypto";
274332
275488
  function nowIso() {
274333
275489
  return (/* @__PURE__ */ new Date()).toISOString();
274334
275490
  }
@@ -274340,7 +275496,7 @@ function deriveSyncKey(obs, originDevice) {
274340
275496
  function contentHash(obs) {
274341
275497
  const { id: _id, writeGeneration: _wg, ...rest } = obs;
274342
275498
  const stable = stableJson(rest);
274343
- return createHash21("sha256").update(stable).digest("hex");
275499
+ return createHash22("sha256").update(stable).digest("hex");
274344
275500
  }
274345
275501
  function stableJson(value) {
274346
275502
  if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
@@ -274463,9 +275619,9 @@ var init_policy2 = __esm({
274463
275619
  });
274464
275620
 
274465
275621
  // src/sync/namespace.ts
274466
- import { createHash as createHash22 } from "crypto";
275622
+ import { createHash as createHash23 } from "crypto";
274467
275623
  function syncNamespace(projectId) {
274468
- return `project-${createHash22("sha256").update(projectId).digest("hex").slice(0, 32)}`;
275624
+ return `project-${createHash23("sha256").update(projectId).digest("hex").slice(0, 32)}`;
274469
275625
  }
274470
275626
  function userSyncNamespace(env3 = process.env) {
274471
275627
  const override = env3.MEMORIX_SYNC_USER_NAMESPACE?.trim();
@@ -274491,7 +275647,7 @@ var init_namespace = __esm({
274491
275647
  });
274492
275648
 
274493
275649
  // src/sync/engine.ts
274494
- import { createHash as createHash23 } from "crypto";
275650
+ import { createHash as createHash24 } from "crypto";
274495
275651
  async function runSync(store2, remote, opts) {
274496
275652
  const report = {
274497
275653
  remote: remote.kind,
@@ -274695,7 +275851,7 @@ async function recordConcurrentConflict(store2, report, local, incoming, localRo
274695
275851
  const incomingWins = compareVersion(incoming.version, { revision: local.revision, writer: local.writer }) > 0;
274696
275852
  const loser = incomingWins ? { revision: local.revision, writer: local.writer } : incoming.version;
274697
275853
  const loserRow = incomingWins ? localRow : incoming.row;
274698
- const id = createHash23("sha256").update(`${store2.projectId()}|${incoming.syncKey}|${local.revision}|${local.writer}|${incoming.version.revision}|${incoming.version.writer}`).digest("hex");
275854
+ const id = createHash24("sha256").update(`${store2.projectId()}|${incoming.syncKey}|${local.revision}|${local.writer}|${incoming.version.revision}|${incoming.version.writer}`).digest("hex");
274699
275855
  await store2.recordConflict({
274700
275856
  id,
274701
275857
  projectId: store2.projectId(),
@@ -274734,7 +275890,7 @@ var init_engine6 = __esm({
274734
275890
 
274735
275891
  // src/sync/store-port.ts
274736
275892
  import os7 from "os";
274737
- import { randomUUID as randomUUID20 } from "crypto";
275893
+ import { randomUUID as randomUUID21 } from "crypto";
274738
275894
  function createSqliteSyncStore(dataDir, store2, projectId, options = {}) {
274739
275895
  const db2 = getDatabase(dataDir);
274740
275896
  const scope = options.scope ?? "project";
@@ -274784,7 +275940,7 @@ function createSqliteSyncStore(dataDir, store2, projectId, options = {}) {
274784
275940
  function ensureDevice() {
274785
275941
  const row = getMeta.get(DEVICE_KEY);
274786
275942
  if (row?.value) return row.value;
274787
- const id = `dev_${randomUUID20().replaceAll("-", "")}`;
275943
+ const id = `dev_${randomUUID21().replaceAll("-", "")}`;
274788
275944
  setMeta.run(DEVICE_KEY, id);
274789
275945
  setMeta.run(FINGERPRINT_KEY, deviceFingerprint);
274790
275946
  return id;
@@ -274814,7 +275970,7 @@ function createSqliteSyncStore(dataDir, store2, projectId, options = {}) {
274814
275970
  assertCanPublish,
274815
275971
  rotateDevice: () => {
274816
275972
  const previous = ensureDevice();
274817
- const id = `dev_${randomUUID20().replaceAll("-", "")}`;
275973
+ const id = `dev_${randomUUID21().replaceAll("-", "")}`;
274818
275974
  const pending = pendingForDevice.all(previous);
274819
275975
  const rotate = db2.transaction(() => {
274820
275976
  setMeta.run(DEVICE_KEY, id);
@@ -277591,10 +278747,10 @@ __export(handler_exports, {
277591
278747
  resetCooldowns: () => resetCooldowns,
277592
278748
  runHook: () => runHook
277593
278749
  });
277594
- import { createHash as createHash24 } from "crypto";
278750
+ import { createHash as createHash25 } from "crypto";
277595
278751
  function deriveHookActorId(input) {
277596
278752
  const material = `${input.agent ?? "unknown"}\0${input.sessionId ?? "unknown"}`;
277597
- return `hook:${createHash24("sha256").update(material).digest("hex").slice(0, 24)}`;
278753
+ return `hook:${createHash25("sha256").update(material).digest("hex").slice(0, 24)}`;
277598
278754
  }
277599
278755
  function classifyTool(input) {
277600
278756
  if (input.event === "post_edit") return "file_modify";
@@ -279556,10 +280712,10 @@ var init_ingest = __esm({
279556
280712
  });
279557
280713
 
279558
280714
  // src/media/embedding.ts
279559
- import { createHash as createHash25 } from "crypto";
280715
+ import { createHash as createHash26 } from "crypto";
279560
280716
  function profileKey(provider2, asset) {
279561
280717
  const identity = `${provider2.name}\0${provider2.dimensions}\0${asset.kind}`;
279562
- return `media:${createHash25("sha256").update(identity).digest("hex").slice(0, 20)}`;
280718
+ return `media:${createHash26("sha256").update(identity).digest("hex").slice(0, 20)}`;
279563
280719
  }
279564
280720
  function supportsAsset(provider2, asset) {
279565
280721
  return !!provider2.embedInput && (provider2.supportedModalities?.includes(asset.kind) ?? false);
@@ -284751,13 +285907,21 @@ var init_capability_router = __esm({
284751
285907
  // src/orchestrate/pipeline-trace.ts
284752
285908
  function initTraceTable(db2) {
284753
285909
  db2.exec(CREATE_TABLE_SQL);
285910
+ for (const column of ["attempt INTEGER", "risk_tier TEXT", "effect_key TEXT", "memory_refs TEXT"]) {
285911
+ try {
285912
+ db2.exec(`ALTER TABLE pipeline_traces ADD COLUMN ${column}`);
285913
+ } catch {
285914
+ }
285915
+ }
284754
285916
  db2.exec(CREATE_INDEX_SQL);
284755
285917
  }
284756
285918
  function writeTrace(db2, event) {
284757
285919
  if (!_insertStmt) {
284758
285920
  _insertStmt = db2.prepare(`
284759
- INSERT INTO pipeline_traces (pipeline_id, timestamp, type, task_id, agent, detail, duration_ms)
284760
- VALUES (?, ?, ?, ?, ?, ?, ?)
285921
+ INSERT INTO pipeline_traces (
285922
+ pipeline_id, timestamp, type, task_id, agent, detail, duration_ms,
285923
+ attempt, risk_tier, effect_key, memory_refs
285924
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
284761
285925
  `);
284762
285926
  }
284763
285927
  _insertStmt.run(
@@ -284766,8 +285930,12 @@ function writeTrace(db2, event) {
284766
285930
  event.type,
284767
285931
  event.taskId ?? null,
284768
285932
  event.agent ?? null,
284769
- event.detail,
284770
- event.durationMs ?? null
285933
+ sanitizeCredentials(event.detail).slice(0, 2e3),
285934
+ event.durationMs ?? null,
285935
+ event.attempt ?? null,
285936
+ event.riskTier ?? null,
285937
+ event.effectKey ?? null,
285938
+ event.memoryRefs ? JSON.stringify(event.memoryRefs.slice(0, 16)) : null
284771
285939
  );
284772
285940
  }
284773
285941
  function pruneOldTraces(db2, keepPipelines = 20) {
@@ -284796,6 +285964,7 @@ var init_pipeline_trace = __esm({
284796
285964
  "src/orchestrate/pipeline-trace.ts"() {
284797
285965
  "use strict";
284798
285966
  init_esm_shims();
285967
+ init_secret_filter();
284799
285968
  CREATE_TABLE_SQL = `
284800
285969
  CREATE TABLE IF NOT EXISTS pipeline_traces (
284801
285970
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -284805,7 +285974,11 @@ var init_pipeline_trace = __esm({
284805
285974
  task_id TEXT,
284806
285975
  agent TEXT,
284807
285976
  detail TEXT NOT NULL,
284808
- duration_ms INTEGER
285977
+ duration_ms INTEGER,
285978
+ attempt INTEGER,
285979
+ risk_tier TEXT,
285980
+ effect_key TEXT,
285981
+ memory_refs TEXT
284809
285982
  )
284810
285983
  `;
284811
285984
  CREATE_INDEX_SQL = `
@@ -285568,8 +286741,140 @@ var init_permission = __esm({
285568
286741
  }
285569
286742
  });
285570
286743
 
286744
+ // src/orchestrate/effect-ledger.ts
286745
+ import { createHash as createHash27, randomUUID as randomUUID22 } from "crypto";
286746
+ function fingerprintEffectInput(input) {
286747
+ if (input === void 0) return void 0;
286748
+ try {
286749
+ const safe = sanitizeCredentials(JSON.stringify(input));
286750
+ return createHash27("sha256").update(safe).digest("hex").slice(0, 24);
286751
+ } catch {
286752
+ return void 0;
286753
+ }
286754
+ }
286755
+ function rowToEffect(row) {
286756
+ return {
286757
+ id: String(row.id),
286758
+ pipelineId: String(row.pipeline_id),
286759
+ taskId: String(row.task_id),
286760
+ attempt: Number(row.attempt),
286761
+ effectKey: String(row.effect_key),
286762
+ tool: String(row.tool),
286763
+ riskTier: row.risk_tier,
286764
+ replayPolicy: row.replay_policy,
286765
+ status: row.status,
286766
+ ...row.call_id ? { callId: String(row.call_id) } : {},
286767
+ idempotencyKey: String(row.idempotency_key),
286768
+ ...row.input_fingerprint ? { inputFingerprint: String(row.input_fingerprint) } : {},
286769
+ ...row.detail ? { detail: String(row.detail) } : {},
286770
+ startedAt: Number(row.started_at),
286771
+ ...row.finished_at ? { finishedAt: Number(row.finished_at) } : {}
286772
+ };
286773
+ }
286774
+ var AgentEffectLedger;
286775
+ var init_effect_ledger = __esm({
286776
+ "src/orchestrate/effect-ledger.ts"() {
286777
+ "use strict";
286778
+ init_esm_shims();
286779
+ init_secret_filter();
286780
+ init_permission();
286781
+ AgentEffectLedger = class {
286782
+ constructor(db2) {
286783
+ this.db = db2;
286784
+ }
286785
+ db;
286786
+ observe(input) {
286787
+ const existing = this.db.prepare(`
286788
+ SELECT * FROM agent_effects
286789
+ WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND effect_key = ?
286790
+ `).get(input.pipelineId, input.taskId, input.attempt, input.effectKey);
286791
+ if (existing) return rowToEffect(existing);
286792
+ const riskTier = classifyTool2(input.tool);
286793
+ const replayPolicy = riskTier === "safe" ? "safe" : "requires-idempotency-key";
286794
+ const idempotencyKey = `${input.pipelineId}:${input.taskId}:${input.attempt}:${input.effectKey}`;
286795
+ const now3 = Date.now();
286796
+ const record2 = {
286797
+ id: randomUUID22(),
286798
+ pipelineId: input.pipelineId,
286799
+ taskId: input.taskId,
286800
+ attempt: input.attempt,
286801
+ effectKey: input.effectKey,
286802
+ tool: input.tool.slice(0, 200),
286803
+ riskTier,
286804
+ replayPolicy,
286805
+ status: "observed",
286806
+ ...input.callId ? { callId: input.callId.slice(0, 200) } : {},
286807
+ idempotencyKey,
286808
+ ...fingerprintEffectInput(input.input) ? { inputFingerprint: fingerprintEffectInput(input.input) } : {},
286809
+ startedAt: now3
286810
+ };
286811
+ this.db.prepare(`
286812
+ INSERT OR IGNORE INTO agent_effects (
286813
+ id, pipeline_id, task_id, attempt, effect_key, tool, risk_tier,
286814
+ replay_policy, status, call_id, idempotency_key, input_fingerprint,
286815
+ detail, started_at, finished_at
286816
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
286817
+ `).run(
286818
+ record2.id,
286819
+ record2.pipelineId,
286820
+ record2.taskId,
286821
+ record2.attempt,
286822
+ record2.effectKey,
286823
+ record2.tool,
286824
+ record2.riskTier,
286825
+ record2.replayPolicy,
286826
+ record2.status,
286827
+ record2.callId ?? null,
286828
+ record2.idempotencyKey,
286829
+ record2.inputFingerprint ?? null,
286830
+ null,
286831
+ record2.startedAt,
286832
+ null
286833
+ );
286834
+ return rowToEffect(this.db.prepare(`
286835
+ SELECT * FROM agent_effects
286836
+ WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND effect_key = ?
286837
+ `).get(input.pipelineId, input.taskId, input.attempt, input.effectKey));
286838
+ }
286839
+ settle(input) {
286840
+ const detail = input.detail ? sanitizeCredentials(input.detail).slice(0, 500) : void 0;
286841
+ this.db.prepare(`
286842
+ UPDATE agent_effects
286843
+ SET status = ?, detail = ?, finished_at = ?
286844
+ WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND effect_key = ?
286845
+ `).run(
286846
+ input.status,
286847
+ detail ?? null,
286848
+ Date.now(),
286849
+ input.pipelineId,
286850
+ input.taskId,
286851
+ input.attempt,
286852
+ input.effectKey
286853
+ );
286854
+ const row = this.db.prepare(`
286855
+ SELECT * FROM agent_effects
286856
+ WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND effect_key = ?
286857
+ `).get(input.pipelineId, input.taskId, input.attempt, input.effectKey);
286858
+ return row ? rowToEffect(row) : void 0;
286859
+ }
286860
+ markAttemptUnknown(pipelineId, taskId, attempt) {
286861
+ const result = this.db.prepare(`
286862
+ UPDATE agent_effects
286863
+ SET status = 'unknown', detail = COALESCE(detail, 'No matching tool result observed'), finished_at = ?
286864
+ WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND status = 'observed'
286865
+ `).run(Date.now(), pipelineId, taskId, attempt);
286866
+ return Number(result.changes ?? 0);
286867
+ }
286868
+ list(pipelineId, taskId) {
286869
+ const rows = taskId ? this.db.prepare("SELECT * FROM agent_effects WHERE pipeline_id = ? AND task_id = ? ORDER BY started_at ASC, rowid ASC").all(pipelineId, taskId) : this.db.prepare("SELECT * FROM agent_effects WHERE pipeline_id = ? ORDER BY started_at ASC, rowid ASC").all(pipelineId);
286870
+ return rows.map(rowToEffect);
286871
+ }
286872
+ };
286873
+ }
286874
+ });
286875
+
285571
286876
  // src/orchestrate/memorix-bridge.ts
285572
- import { createHash as createHash26 } from "crypto";
286877
+ import { createHash as createHash28 } from "crypto";
285573
286878
  async function getStoreObservation() {
285574
286879
  if (!_storeObservation) {
285575
286880
  const mod = await Promise.resolve().then(() => (init_observations(), observations_exports));
@@ -285585,7 +286890,7 @@ async function getSearchObservations() {
285585
286890
  return _searchObservations;
285586
286891
  }
285587
286892
  function automaticActorId(...parts) {
285588
- return `orchestrator:${createHash26("sha256").update(parts.join("\0")).digest("hex").slice(0, 24)}`;
286893
+ return `orchestrator:${createHash28("sha256").update(parts.join("\0")).digest("hex").slice(0, 24)}`;
285589
286894
  }
285590
286895
  function isEligibleAutomaticLesson(entry) {
285591
286896
  return entry.admissionState !== "candidate" && entry.admissionState !== "ephemeral";
@@ -285732,7 +287037,7 @@ function sanitizeErrorPattern(text3) {
285732
287037
  }
285733
287038
  function hashErrorPattern(errorOutput) {
285734
287039
  const sanitized = sanitizeErrorPattern(errorOutput).slice(0, 500);
285735
- return createHash26("sha256").update(sanitized).digest("hex").slice(0, 12);
287040
+ return createHash28("sha256").update(sanitized).digest("hex").slice(0, 12);
285736
287041
  }
285737
287042
  function sleep6(ms2) {
285738
287043
  return new Promise((resolve9) => setTimeout(() => resolve9([]), ms2));
@@ -285843,10 +287148,12 @@ async function runCoordinationLoop(config2) {
285843
287148
  const dispatchedAdapterNames = /* @__PURE__ */ new Set();
285844
287149
  const routingDecisions = [];
285845
287150
  let traceDb = null;
287151
+ let effectLedger = null;
285846
287152
  try {
285847
287153
  traceDb = teamStore.getDb();
285848
287154
  resetTraceCache();
285849
287155
  initTraceTable(traceDb);
287156
+ if (pipelineId) effectLedger = new AgentEffectLedger(traceDb);
285850
287157
  } catch {
285851
287158
  }
285852
287159
  if (useWorktrees) {
@@ -286147,6 +287454,32 @@ ${formatCostSummary(costSummary)}`);
286147
287454
  for await (const msg of agentProcess.messages) {
286148
287455
  if (msg.type === "tool_use") {
286149
287456
  dispatch.toolCount++;
287457
+ const effectKey = msg.callId ?? `seq:${dispatch.toolCount}`;
287458
+ const effect = effectLedger?.observe({
287459
+ pipelineId,
287460
+ taskId: dispatch.taskId,
287461
+ attempt: dispatch.attempt,
287462
+ effectKey,
287463
+ tool: msg.tool ?? "unknown",
287464
+ ...msg.callId ? { callId: msg.callId } : {},
287465
+ ...msg.input ? { input: msg.input } : {}
287466
+ });
287467
+ if (effect && traceDb && pipelineId) {
287468
+ try {
287469
+ writeTrace(traceDb, {
287470
+ pipelineId,
287471
+ timestamp: Date.now(),
287472
+ type: "effect",
287473
+ taskId: dispatch.taskId,
287474
+ agent: dispatch.adapterName,
287475
+ detail: `observed tool=${effect.tool} risk=${effect.riskTier} replay=${effect.replayPolicy} effect=${effect.effectKey}`,
287476
+ attempt: dispatch.attempt,
287477
+ riskTier: effect.riskTier,
287478
+ effectKey: effect.effectKey
287479
+ });
287480
+ } catch {
287481
+ }
287482
+ }
286150
287483
  if (msg.tool) tracker.record(msg.tool);
286151
287484
  emit("agent:tool_use", `[${adapter.name}] tool #${dispatch.toolCount}: ${msg.tool ?? "unknown"}`, {
286152
287485
  taskId: task.task_id,
@@ -286166,6 +287499,16 @@ ${formatCostSummary(costSummary)}`);
286166
287499
  }
286167
287500
  }
286168
287501
  }
287502
+ if (msg.type === "tool_result" && msg.callId && effectLedger && pipelineId) {
287503
+ effectLedger.settle({
287504
+ pipelineId,
287505
+ taskId: dispatch.taskId,
287506
+ attempt: dispatch.attempt,
287507
+ effectKey: msg.callId,
287508
+ status: "succeeded",
287509
+ detail: "tool result observed"
287510
+ });
287511
+ }
286169
287512
  if (msg.type === "text" && msg.content) {
286170
287513
  dispatch.accumulatedText += msg.content;
286171
287514
  }
@@ -286248,6 +287591,9 @@ ${formatCostSummary(costSummary)}`);
286248
287591
  if (result.sessionId) {
286249
287592
  taskSessionIds.set(dispatch.taskId, result.sessionId);
286250
287593
  }
287594
+ if (effectLedger && pipelineId) {
287595
+ effectLedger.markAttemptUnknown(pipelineId, dispatch.taskId, dispatch.attempt);
287596
+ }
286251
287597
  const taskState = teamStore.getTask(dispatch.taskId);
286252
287598
  const taskDesc = taskState?.description ?? dispatch.taskId;
286253
287599
  if (!result.killed && result.exitCode === 0) {
@@ -286311,6 +287657,25 @@ ${formatCostSummary(costSummary)}`);
286311
287657
  for await (const msg of fixProcess.messages) {
286312
287658
  if (msg.type === "tool_use") {
286313
287659
  fixDispatch.toolCount++;
287660
+ effectLedger?.observe({
287661
+ pipelineId,
287662
+ taskId: fixDispatch.taskId,
287663
+ attempt: fixDispatch.attempt,
287664
+ effectKey: msg.callId ?? `seq:${fixDispatch.toolCount}`,
287665
+ tool: msg.tool ?? "unknown",
287666
+ ...msg.callId ? { callId: msg.callId } : {},
287667
+ ...msg.input ? { input: msg.input } : {}
287668
+ });
287669
+ }
287670
+ if (msg.type === "tool_result" && msg.callId && effectLedger && pipelineId) {
287671
+ effectLedger.settle({
287672
+ pipelineId,
287673
+ taskId: fixDispatch.taskId,
287674
+ attempt: fixDispatch.attempt,
287675
+ effectKey: msg.callId,
287676
+ status: "succeeded",
287677
+ detail: "tool result observed"
287678
+ });
286314
287679
  }
286315
287680
  if (msg.type === "text" && msg.content) {
286316
287681
  fixDispatch.accumulatedText += msg.content;
@@ -286740,6 +288105,7 @@ var init_coordinator = __esm({
286740
288105
  init_cost_tracker();
286741
288106
  init_evidence2();
286742
288107
  init_permission();
288108
+ init_effect_ledger();
286743
288109
  init_memorix_bridge();
286744
288110
  }
286745
288111
  });
@@ -288149,6 +289515,7 @@ function ConfigureView({ onBack }) {
288149
289515
  { key: "openai", label: "OpenAI", hint: "gpt-4o-mini" },
288150
289516
  { key: "anthropic", label: "Anthropic", hint: "claude-3-haiku" },
288151
289517
  { key: "openrouter", label: "OpenRouter", hint: "multi-provider" },
289518
+ { key: "atlascloud", label: "Atlas Cloud", hint: "DeepSeek V3.2" },
288152
289519
  { key: "custom", label: "Custom endpoint", hint: "OpenAI-compatible" },
288153
289520
  { key: "disable", label: "Disable LLM", hint: "free heuristic mode", color: COLORS.warning },
288154
289521
  { key: "back", label: "Back", hint: "", color: COLORS.muted }
@@ -288441,6 +289808,7 @@ function ConfigureView({ onBack }) {
288441
289808
  function getDefaultModel(provider2) {
288442
289809
  if (provider2 === "anthropic") return "claude-3-haiku-20240307";
288443
289810
  if (provider2 === "openrouter") return "openai/gpt-4o-mini";
289811
+ if (provider2 === "atlascloud") return "deepseek-ai/deepseek-v3.2";
288444
289812
  return "gpt-4o-mini";
288445
289813
  }
288446
289814
  var init_ConfigureView = __esm({
@@ -291646,10 +293014,22 @@ var GUIDES = {
291646
293014
  context: {
291647
293015
  summary: "Build the bounded task Workset used to resume or begin real work.",
291648
293016
  usage: [
291649
- 'memorix context "continue the release fix" [--refresh auto|always|never]',
293017
+ 'memorix context "continue the release fix" [--task-id <continuity-id>] [--refresh auto|always|never]',
291650
293018
  'memorix resume "continue the release fix" [--refresh auto|always|never]'
291651
293019
  ]
291652
293020
  },
293021
+ continuity: {
293022
+ summary: "Track task requirements, decisions, verification, risks, and outcomes.",
293023
+ usage: [
293024
+ 'memorix continuity start --task "Harden the release path" [--requirements "a,b"]',
293025
+ 'memorix continuity record --task-id <id> --kind decision --content "..."',
293026
+ 'memorix continuity record --task-id <id> --kind verification --verification-status passed --content "..."',
293027
+ "memorix continuity show --task-id <id>",
293028
+ 'memorix continuity close --task-id <id> --status completed --content "..."',
293029
+ "memorix continuity list"
293030
+ ],
293031
+ notes: ["Pass the returned task id to memorix context --task-id so the bounded brief includes the ledger."]
293032
+ },
291653
293033
  explain: {
291654
293034
  summary: "Show why a task context contains its current facts and memory evidence.",
291655
293035
  usage: ["memorix explain [--refresh auto|always|never]"]
@@ -292058,6 +293438,7 @@ var main2 = defineCommand({
292058
293438
  knowledge: () => Promise.resolve().then(() => (init_knowledge(), knowledge_exports)).then((m6) => m6.default),
292059
293439
  evidence: () => Promise.resolve().then(() => (init_evidence(), evidence_exports)).then((m6) => m6.default),
292060
293440
  feedback: () => Promise.resolve().then(() => (init_feedback2(), feedback_exports2)).then((m6) => m6.default),
293441
+ continuity: () => Promise.resolve().then(() => (init_continuity(), continuity_exports)).then((m6) => m6.default),
292061
293442
  reasoning: () => Promise.resolve().then(() => (init_reasoning(), reasoning_exports)).then((m6) => m6.default),
292062
293443
  retention: () => Promise.resolve().then(() => (init_retention2(), retention_exports2)).then((m6) => m6.default),
292063
293444
  formation: () => Promise.resolve().then(() => (init_formation2(), formation_exports2)).then((m6) => m6.default),
@@ -292158,6 +293539,7 @@ var main2 = defineCommand({
292158
293539
  "poll",
292159
293540
  "evidence",
292160
293541
  "feedback",
293542
+ "continuity",
292161
293543
  "receipt",
292162
293544
  "serve",
292163
293545
  "serve-http",
@@ -292222,6 +293604,7 @@ var main2 = defineCommand({
292222
293604
  console.error(" knowledge Review source-backed knowledge pages and project workflows");
292223
293605
  console.error(" evidence Inspect source-backed evidence cards");
292224
293606
  console.error(" feedback Record or audit memory feedback");
293607
+ console.error(" continuity Record task requirements, decisions, verification, and outcome");
292225
293608
  console.error(" reasoning Store/search decision rationale");
292226
293609
  console.error(" retention Inspect stale/archive status");
292227
293610
  console.error(" formation Inspect Memory Formation metrics");