memorix 1.9.5 → 1.9.7

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 (64) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +6 -1
  3. package/README.zh-CN.md +6 -1
  4. package/dist/cli/index.js +1743 -348
  5. package/dist/index.d.ts +12 -7
  6. package/dist/index.js +1048 -266
  7. package/dist/maintenance-runner.js +339 -187
  8. package/dist/media-audio-runner.js +339 -187
  9. package/dist/media-video-runner.js +339 -187
  10. package/dist/memcode-runtime/CHANGELOG.md +53 -0
  11. package/dist/sdk.js +967 -241
  12. package/dist/vector-backfill-runner.js +339 -187
  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/orama-store.ts +16 -1
  62. package/src/store/sqlite-db.ts +144 -4
  63. package/src/store/sqlite-fts.ts +38 -16
  64. 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.7" : pkg.version;
3236
3236
  }
3237
3237
  var init_version = __esm({
3238
3238
  "src/cli/version.ts"() {
@@ -9349,6 +9349,74 @@ var init_launch_root = __esm({
9349
9349
  }
9350
9350
  });
9351
9351
 
9352
+ // src/store/sqlite-reliability.ts
9353
+ function isTransientSqliteError(error40) {
9354
+ const code = error40?.code;
9355
+ if (typeof code === "string") {
9356
+ return /^SQLITE_BUSY(?:_|$)/i.test(code);
9357
+ }
9358
+ return error40 instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error40.message);
9359
+ }
9360
+ function isSqliteUnavailableError(error40) {
9361
+ return error40 instanceof SqliteUnavailableError || error40 instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error40.message);
9362
+ }
9363
+ function degradedReadError(storeName) {
9364
+ return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
9365
+ }
9366
+ function degradedWriteError(storeName) {
9367
+ return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
9368
+ }
9369
+ function formatSqliteFailure(error40) {
9370
+ return error40 instanceof Error && error40.message ? error40.message : String(error40);
9371
+ }
9372
+ function delay(ms2) {
9373
+ return new Promise((resolve9) => setTimeout(resolve9, ms2));
9374
+ }
9375
+ async function retrySqliteBusy(operation, label) {
9376
+ for (let attempt = 0; ; attempt++) {
9377
+ try {
9378
+ return await operation();
9379
+ } catch (error40) {
9380
+ if (!isTransientSqliteError(error40) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error40;
9381
+ const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
9382
+ console.error(
9383
+ `[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
9384
+ );
9385
+ await delay(waitMs);
9386
+ }
9387
+ }
9388
+ }
9389
+ async function initializeSqliteStore(factory, dataDir, label) {
9390
+ try {
9391
+ loadBetterSqlite3();
9392
+ } catch (error40) {
9393
+ throw new SqliteUnavailableError(error40);
9394
+ }
9395
+ return retrySqliteBusy(async () => {
9396
+ const store2 = factory();
9397
+ await store2.init(dataDir);
9398
+ return store2;
9399
+ }, label);
9400
+ }
9401
+ var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
9402
+ var init_sqlite_reliability = __esm({
9403
+ "src/store/sqlite-reliability.ts"() {
9404
+ "use strict";
9405
+ init_esm_shims();
9406
+ init_sqlite_db();
9407
+ SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
9408
+ SqliteUnavailableError = class extends Error {
9409
+ code = "MEMORIX_SQLITE_UNAVAILABLE";
9410
+ constructor(cause) {
9411
+ super(
9412
+ cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
9413
+ );
9414
+ this.name = "SqliteUnavailableError";
9415
+ }
9416
+ };
9417
+ }
9418
+ });
9419
+
9352
9420
  // src/store/sqlite-fts.ts
9353
9421
  function normalizeFtsToken(token) {
9354
9422
  return token.replace(/"/g, '""');
@@ -9362,22 +9430,27 @@ function buildFtsQuery(query) {
9362
9430
  function initializeObservationLexicalIndex(db2) {
9363
9431
  const known = ftsAvailability.get(db2);
9364
9432
  if (known !== void 0) return known;
9365
- try {
9366
- db2.exec(CREATE_OBSERVATION_FTS);
9367
- const observationCount = Number(db2.prepare("SELECT COUNT(*) AS count FROM observations").get()?.count ?? 0);
9368
- const indexedCount = Number(db2.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
9369
- if (observationCount !== indexedCount) {
9370
- db2.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
9433
+ let lastError;
9434
+ for (let attempt = 1; attempt <= FTS_INIT_MAX_ATTEMPTS; attempt++) {
9435
+ try {
9436
+ db2.exec(CREATE_OBSERVATION_FTS);
9437
+ const observationCount = Number(db2.prepare("SELECT COUNT(*) AS count FROM observations").get()?.count ?? 0);
9438
+ const indexedCount = Number(db2.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
9439
+ if (observationCount !== indexedCount) {
9440
+ db2.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
9441
+ }
9442
+ ftsAvailability.set(db2, true);
9443
+ return true;
9444
+ } catch (error40) {
9445
+ lastError = error40;
9446
+ if (!isTransientSqliteError(error40) || attempt === FTS_INIT_MAX_ATTEMPTS) break;
9371
9447
  }
9372
- ftsAvailability.set(db2, true);
9373
- return true;
9374
- } catch (error40) {
9375
- ftsAvailability.set(db2, false);
9376
- console.warn(
9377
- `[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${error40 instanceof Error ? error40.message : String(error40)}`
9378
- );
9379
- return false;
9380
9448
  }
9449
+ ftsAvailability.set(db2, false);
9450
+ console.warn(
9451
+ `[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${lastError instanceof Error ? lastError.message : String(lastError)}`
9452
+ );
9453
+ return false;
9381
9454
  }
9382
9455
  function isObservationLexicalIndexEnabled(db2) {
9383
9456
  return ftsAvailability.get(db2) === true;
@@ -9431,11 +9504,12 @@ function searchObservationLexically(db2, options) {
9431
9504
  score: Math.max(0, Number(row.lexical_score ?? 0))
9432
9505
  }));
9433
9506
  }
9434
- var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS;
9507
+ var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS, FTS_INIT_MAX_ATTEMPTS;
9435
9508
  var init_sqlite_fts = __esm({
9436
9509
  "src/store/sqlite-fts.ts"() {
9437
9510
  "use strict";
9438
9511
  init_esm_shims();
9512
+ init_sqlite_reliability();
9439
9513
  ftsAvailability = /* @__PURE__ */ new WeakMap();
9440
9514
  FTS_TABLE = "observations_fts";
9441
9515
  CREATE_OBSERVATION_FTS = `
@@ -9469,15 +9543,19 @@ CREATE TRIGGER IF NOT EXISTS observations_fts_au AFTER UPDATE ON observations BE
9469
9543
  VALUES (new.id, new.entityName, new.title, new.narrative, new.facts, new.filesModified, new.concepts, new.attachments);
9470
9544
  END;
9471
9545
  `;
9546
+ FTS_INIT_MAX_ATTEMPTS = 3;
9472
9547
  }
9473
9548
  });
9474
9549
 
9475
9550
  // src/store/sqlite-db.ts
9476
9551
  var sqlite_db_exports = {};
9477
9552
  __export(sqlite_db_exports, {
9553
+ acquireDatabase: () => acquireDatabase,
9478
9554
  closeAllDatabases: () => closeAllDatabases,
9479
9555
  closeDatabase: () => closeDatabase,
9556
+ evictIdleDatabases: () => evictIdleDatabases,
9480
9557
  getDatabase: () => getDatabase,
9558
+ getDatabaseStats: () => getDatabaseStats,
9481
9559
  isSqliteAvailable: () => isSqliteAvailable,
9482
9560
  loadBetterSqlite3: () => loadBetterSqlite3
9483
9561
  });
@@ -9515,11 +9593,21 @@ function applySchemaMigrations(db2) {
9515
9593
  apply();
9516
9594
  }
9517
9595
  }
9596
+ function normalizeDataDir(dataDir) {
9597
+ return path3.resolve(dataDir);
9598
+ }
9599
+ function touchDatabase(normalized, db2) {
9600
+ _dbCache.delete(normalized);
9601
+ _dbCache.set(normalized, db2);
9602
+ }
9518
9603
  function getDatabase(dataDir) {
9519
- const normalized = path3.resolve(dataDir);
9604
+ const normalized = normalizeDataDir(dataDir);
9520
9605
  assertNotHomeDataDir(normalized);
9521
9606
  const existing = _dbCache.get(normalized);
9522
- if (existing) return existing;
9607
+ if (existing) {
9608
+ touchDatabase(normalized, existing);
9609
+ return existing;
9610
+ }
9523
9611
  loadBetterSqlite3();
9524
9612
  fs.mkdirSync(dataDir, { recursive: true });
9525
9613
  const dbPath = path3.join(dataDir, "memorix.db");
@@ -9549,6 +9637,8 @@ function getDatabase(dataDir) {
9549
9637
  db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
9550
9638
  db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
9551
9639
  db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
9640
+ db2.exec(CREATE_TASK_CONTINUITY_EVENTS_TABLE);
9641
+ db2.exec(CREATE_AGENT_EFFECTS_TABLE);
9552
9642
  try {
9553
9643
  db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
9554
9644
  } catch {
@@ -9587,7 +9677,7 @@ function getDatabase(dataDir) {
9587
9677
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('storage_generation', '0')`).run();
9588
9678
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('next_id', '1')`).run();
9589
9679
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('mini_skills_generation', '0')`).run();
9590
- _dbCache.set(normalized, db2);
9680
+ touchDatabase(normalized, db2);
9591
9681
  return db2;
9592
9682
  } catch (error40) {
9593
9683
  try {
@@ -9597,8 +9687,46 @@ function getDatabase(dataDir) {
9597
9687
  throw error40;
9598
9688
  }
9599
9689
  }
9690
+ function acquireDatabase(dataDir) {
9691
+ const normalized = normalizeDataDir(dataDir);
9692
+ const db2 = getDatabase(normalized);
9693
+ _dbLeases.set(normalized, (_dbLeases.get(normalized) ?? 0) + 1);
9694
+ let released = false;
9695
+ return {
9696
+ dataDir: normalized,
9697
+ db: db2,
9698
+ release: () => {
9699
+ if (released) return;
9700
+ released = true;
9701
+ const count3 = (_dbLeases.get(normalized) ?? 0) - 1;
9702
+ if (count3 > 0) _dbLeases.set(normalized, count3);
9703
+ else _dbLeases.delete(normalized);
9704
+ }
9705
+ };
9706
+ }
9707
+ function getDatabaseStats() {
9708
+ let leased = 0;
9709
+ for (const count3 of _dbLeases.values()) leased += count3;
9710
+ return { cached: _dbCache.size, leased, leasedDataDirs: _dbLeases.size };
9711
+ }
9712
+ function evictIdleDatabases(maxCached) {
9713
+ const target = Math.max(0, Math.floor(maxCached));
9714
+ let evicted = 0;
9715
+ for (const [normalized, db2] of [..._dbCache]) {
9716
+ if (_dbCache.size <= target) break;
9717
+ if ((_dbLeases.get(normalized) ?? 0) > 0) continue;
9718
+ try {
9719
+ db2.close();
9720
+ } catch {
9721
+ }
9722
+ _dbCache.delete(normalized);
9723
+ evicted++;
9724
+ }
9725
+ return evicted;
9726
+ }
9600
9727
  function closeDatabase(dataDir) {
9601
- const normalized = path3.resolve(dataDir);
9728
+ const normalized = normalizeDataDir(dataDir);
9729
+ if ((_dbLeases.get(normalized) ?? 0) > 0) return;
9602
9730
  const db2 = _dbCache.get(normalized);
9603
9731
  if (db2) {
9604
9732
  try {
@@ -9616,6 +9744,7 @@ function closeAllDatabases() {
9616
9744
  }
9617
9745
  _dbCache.delete(key);
9618
9746
  }
9747
+ _dbLeases.clear();
9619
9748
  }
9620
9749
  function isSqliteAvailable() {
9621
9750
  try {
@@ -9625,7 +9754,7 @@ function isSqliteAvailable() {
9625
9754
  return false;
9626
9755
  }
9627
9756
  }
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;
9757
+ 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
9758
  var init_sqlite_db = __esm({
9630
9759
  "src/store/sqlite-db.ts"() {
9631
9760
  "use strict";
@@ -10342,6 +10471,43 @@ CREATE TABLE IF NOT EXISTS memory_feedback_events (
10342
10471
  delta REAL NOT NULL DEFAULT 0,
10343
10472
  observed_at TEXT NOT NULL
10344
10473
  );
10474
+ `;
10475
+ CREATE_TASK_CONTINUITY_EVENTS_TABLE = `
10476
+ CREATE TABLE IF NOT EXISTS task_continuity_events (
10477
+ id TEXT PRIMARY KEY,
10478
+ project_id TEXT NOT NULL,
10479
+ task_id TEXT NOT NULL,
10480
+ task TEXT NOT NULL,
10481
+ kind TEXT NOT NULL,
10482
+ content TEXT NOT NULL,
10483
+ item_status TEXT,
10484
+ source_ref TEXT,
10485
+ evidence_json TEXT NOT NULL DEFAULT '[]',
10486
+ actor TEXT,
10487
+ created_at TEXT NOT NULL,
10488
+ event_key TEXT,
10489
+ item_key TEXT,
10490
+ supersedes_id TEXT
10491
+ );
10492
+ `;
10493
+ CREATE_AGENT_EFFECTS_TABLE = `
10494
+ CREATE TABLE IF NOT EXISTS agent_effects (
10495
+ id TEXT PRIMARY KEY,
10496
+ pipeline_id TEXT NOT NULL,
10497
+ task_id TEXT NOT NULL,
10498
+ attempt INTEGER NOT NULL,
10499
+ effect_key TEXT NOT NULL,
10500
+ tool TEXT NOT NULL,
10501
+ risk_tier TEXT NOT NULL,
10502
+ replay_policy TEXT NOT NULL,
10503
+ status TEXT NOT NULL,
10504
+ call_id TEXT,
10505
+ idempotency_key TEXT NOT NULL,
10506
+ input_fingerprint TEXT,
10507
+ detail TEXT,
10508
+ started_at INTEGER NOT NULL,
10509
+ finished_at INTEGER
10510
+ );
10345
10511
  `;
10346
10512
  CREATE_MCP_BINDINGS_TABLE = `
10347
10513
  CREATE TABLE IF NOT EXISTS mcp_bindings (
@@ -10420,7 +10586,18 @@ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(projec
10420
10586
  CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
10421
10587
  CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
10422
10588
  CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
10589
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_project_task ON task_continuity_events(project_id, task_id, created_at ASC);
10590
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_project_recent ON task_continuity_events(project_id, created_at DESC);
10591
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_item ON task_continuity_events(project_id, task_id, kind, item_key, created_at DESC);
10592
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
10593
+ ON task_continuity_events(project_id, task_id, event_key)
10594
+ WHERE event_key IS NOT NULL;
10595
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_effect_identity
10596
+ ON agent_effects(pipeline_id, task_id, attempt, effect_key);
10597
+ CREATE INDEX IF NOT EXISTS idx_agent_effect_pipeline ON agent_effects(pipeline_id, started_at ASC);
10423
10598
  CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
10599
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC);
10600
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at);
10424
10601
  CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
10425
10602
  ON maintenance_jobs(project_id, kind, dedupe_key)
10426
10603
  WHERE status IN ('pending', 'running', 'retry');
@@ -10591,6 +10768,8 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
10591
10768
  db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
10592
10769
  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
10770
  db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
10771
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC)");
10772
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at)");
10594
10773
  }
10595
10774
  },
10596
10775
  {
@@ -10601,77 +10780,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
10601
10780
  addColumnIfMissing(db2, "code_symbols", "source", "source TEXT");
10602
10781
  addColumnIfMissing(db2, "code_edges", "source", "source TEXT");
10603
10782
  }
10783
+ },
10784
+ {
10785
+ id: "1.9-continuity-v2",
10786
+ apply: (db2) => {
10787
+ addColumnIfMissing(db2, "task_continuity_events", "event_key", "event_key TEXT");
10788
+ addColumnIfMissing(db2, "task_continuity_events", "item_key", "item_key TEXT");
10789
+ addColumnIfMissing(db2, "task_continuity_events", "supersedes_id", "supersedes_id TEXT");
10790
+ 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)");
10791
+ db2.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
10792
+ ON task_continuity_events(project_id, task_id, event_key)
10793
+ WHERE event_key IS NOT NULL`);
10794
+ }
10604
10795
  }
10605
10796
  ];
10606
10797
  _dbCache = /* @__PURE__ */ new Map();
10607
- }
10608
- });
10609
-
10610
- // src/store/sqlite-reliability.ts
10611
- function isTransientSqliteError(error40) {
10612
- const code = error40?.code;
10613
- if (typeof code === "string") {
10614
- return /^SQLITE_BUSY(?:_|$)/i.test(code);
10615
- }
10616
- return error40 instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error40.message);
10617
- }
10618
- function isSqliteUnavailableError(error40) {
10619
- return error40 instanceof SqliteUnavailableError || error40 instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error40.message);
10620
- }
10621
- function degradedReadError(storeName) {
10622
- return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
10623
- }
10624
- function degradedWriteError(storeName) {
10625
- return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
10626
- }
10627
- function formatSqliteFailure(error40) {
10628
- return error40 instanceof Error && error40.message ? error40.message : String(error40);
10629
- }
10630
- function delay(ms2) {
10631
- return new Promise((resolve9) => setTimeout(resolve9, ms2));
10632
- }
10633
- async function retrySqliteBusy(operation, label) {
10634
- for (let attempt = 0; ; attempt++) {
10635
- try {
10636
- return await operation();
10637
- } catch (error40) {
10638
- if (!isTransientSqliteError(error40) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error40;
10639
- const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
10640
- console.error(
10641
- `[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
10642
- );
10643
- await delay(waitMs);
10644
- }
10645
- }
10646
- }
10647
- async function initializeSqliteStore(factory, dataDir, label) {
10648
- try {
10649
- loadBetterSqlite3();
10650
- } catch (error40) {
10651
- throw new SqliteUnavailableError(error40);
10652
- }
10653
- return retrySqliteBusy(async () => {
10654
- const store2 = factory();
10655
- await store2.init(dataDir);
10656
- return store2;
10657
- }, label);
10658
- }
10659
- var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
10660
- var init_sqlite_reliability = __esm({
10661
- "src/store/sqlite-reliability.ts"() {
10662
- "use strict";
10663
- init_esm_shims();
10664
- init_sqlite_db();
10665
- SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
10666
- SqliteUnavailableError = class extends Error {
10667
- code = "MEMORIX_SQLITE_UNAVAILABLE";
10668
- constructor(cause) {
10669
- super(
10670
- cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
10671
- );
10672
- this.name = "SqliteUnavailableError";
10673
- }
10674
- };
10798
+ _dbLeases = /* @__PURE__ */ new Map();
10675
10799
  }
10676
10800
  });
10677
10801
 
@@ -21094,18 +21218,23 @@ __export(yaml_loader_exports, {
21094
21218
  });
21095
21219
  import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
21096
21220
  import { homedir } from "os";
21221
+ function cacheKey(homeDir, projectRoot) {
21222
+ return `${homeDir}\0${projectRoot ?? ""}`;
21223
+ }
21097
21224
  function initProjectRoot(root) {
21098
21225
  globalProjectRoot = root;
21099
- configCache.delete(root);
21226
+ configCache.delete(cacheKey(homedir(), root));
21100
21227
  }
21101
21228
  function clearProjectRoot() {
21102
21229
  globalProjectRoot = null;
21103
21230
  }
21104
- function loadYamlConfig(projectRoot) {
21231
+ function loadYamlConfig(projectRoot, options = {}) {
21232
+ const homeDir = options.homeDir ?? homedir();
21105
21233
  const resolvedRoot = projectRoot === null ? null : projectRoot ?? globalProjectRoot ?? null;
21106
- const cached2 = configCache.get(resolvedRoot ?? null);
21234
+ const key = cacheKey(homeDir, resolvedRoot);
21235
+ const cached2 = configCache.get(key);
21107
21236
  if (cached2) return cached2;
21108
- const userYaml = getGlobalYamlPath(homedir());
21237
+ const userYaml = getGlobalYamlPath(homeDir);
21109
21238
  const projectYaml = resolvedRoot ? getProjectYamlPath(resolvedRoot) : null;
21110
21239
  let userConfig = {};
21111
21240
  let projectConfig = {};
@@ -21137,12 +21266,12 @@ function loadYamlConfig(projectRoot) {
21137
21266
  server: { ...userConfig.server, ...projectConfig.server },
21138
21267
  team: { ...userConfig.team, ...projectConfig.team }
21139
21268
  };
21140
- configCache.set(resolvedRoot ?? null, merged);
21269
+ configCache.set(key, merged);
21141
21270
  return merged;
21142
21271
  }
21143
- function resetYamlConfigCache(projectRoot) {
21272
+ function resetYamlConfigCache(projectRoot, homeDir = homedir()) {
21144
21273
  if (projectRoot !== void 0) {
21145
- configCache.delete(projectRoot ?? null);
21274
+ configCache.delete(cacheKey(homeDir, projectRoot ?? null));
21146
21275
  } else {
21147
21276
  configCache.clear();
21148
21277
  }
@@ -21353,9 +21482,9 @@ var init_detector = __esm({
21353
21482
  // src/config/legacy-loader.ts
21354
21483
  import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
21355
21484
  import { homedir as homedir2 } from "os";
21356
- function loadFileConfig() {
21485
+ function loadFileConfig(homeDir = homedir2()) {
21357
21486
  if (cachedConfig !== null) return cachedConfig;
21358
- const configPath = getLegacyConfigJsonPath(homedir2());
21487
+ const configPath = getLegacyConfigJsonPath(homeDir);
21359
21488
  try {
21360
21489
  if (existsSync6(configPath)) {
21361
21490
  cachedConfig = JSON.parse(readFileSync5(configPath, "utf-8"));
@@ -21385,15 +21514,15 @@ import { homedir as homedir3 } from "os";
21385
21514
  function loadTomlConfig(options = {}) {
21386
21515
  const homeDir = options.homeDir ?? homedir3();
21387
21516
  const projectRoot = options.projectRoot ?? null;
21388
- const cacheKey2 = `${homeDir}\0${projectRoot ?? ""}`;
21389
- const cached2 = configCache2.get(cacheKey2);
21517
+ const cacheKey4 = `${homeDir}\0${projectRoot ?? ""}`;
21518
+ const cached2 = configCache2.get(cacheKey4);
21390
21519
  if (cached2) return cached2;
21391
21520
  const userToml = getGlobalConfigTomlPath(homeDir);
21392
21521
  const projectToml = projectRoot ? getProjectConfigTomlPath(projectRoot) : null;
21393
21522
  const userConfig = existsSync7(userToml) ? parseTomlConfig(readFileSync6(userToml, "utf8"), userToml) : {};
21394
21523
  const projectConfig = projectToml && existsSync7(projectToml) ? parseTomlConfig(readFileSync6(projectToml, "utf8"), projectToml) : {};
21395
21524
  const merged = mergeTomlConfig(userConfig, projectConfig);
21396
- configCache2.set(cacheKey2, merged);
21525
+ configCache2.set(cacheKey4, merged);
21397
21526
  return merged;
21398
21527
  }
21399
21528
  function mergeTomlConfig(base, override) {
@@ -21989,8 +22118,8 @@ function getResolvedConfig(options = {}) {
21989
22118
  const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
21990
22119
  loadDotenv(projectRoot === null ? void 0 : projectRoot ?? void 0, { userHomeDir: homeDir });
21991
22120
  const toml = loadTomlConfig({ projectRoot: projectRoot ?? null, homeDir });
21992
- const yaml = loadYamlConfig(projectRoot ?? null);
21993
- const legacy = loadFileConfig();
22121
+ const yaml = loadYamlConfig(projectRoot ?? null, { homeDir });
22122
+ const legacy = loadFileConfig(homeDir);
21994
22123
  const embeddingBaseUrl = first(process.env.MEMORIX_EMBEDDING_BASE_URL, toml.embedding?.base_url, yaml.embedding?.baseUrl, legacy.embeddingApi?.baseUrl);
21995
22124
  const openRouterEmbeddingApiKey = isOpenRouterUrl(embeddingBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
21996
22125
  const memoryLlmProvider = first(
@@ -22012,15 +22141,17 @@ function getResolvedConfig(options = {}) {
22012
22141
  legacy.llm?.baseUrl
22013
22142
  );
22014
22143
  const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
22144
+ const isAtlasCloud = memoryLlmProvider === "atlascloud";
22015
22145
  const memoryLlmApiKey = first(
22016
22146
  process.env.MEMORIX_LLM_API_KEY,
22017
22147
  process.env.MEMORIX_API_KEY,
22018
22148
  toml.memory?.llm?.api_key,
22019
22149
  yaml.llm?.apiKey,
22020
22150
  legacy.llm?.apiKey,
22021
- process.env.OPENAI_API_KEY,
22022
- process.env.ANTHROPIC_API_KEY,
22023
- openRouterMemoryLlmApiKey
22151
+ // Never borrow another provider's credentials for the Atlas Cloud preset.
22152
+ isAtlasCloud ? process.env.ATLASCLOUD_API_KEY : process.env.OPENAI_API_KEY,
22153
+ isAtlasCloud ? void 0 : process.env.ANTHROPIC_API_KEY,
22154
+ isAtlasCloud ? void 0 : openRouterMemoryLlmApiKey
22024
22155
  );
22025
22156
  const resolved = {
22026
22157
  agent: {
@@ -22199,7 +22330,8 @@ function getEnvSourceNames() {
22199
22330
  "MEMORIX_CODEGRAPH_EXTERNAL_CONTEXT",
22200
22331
  "MEMORIX_CODEGRAPH_EXTERNAL_COMMAND",
22201
22332
  "MEMORIX_CODEGRAPH_EXTERNAL_TIMEOUT_MS",
22202
- "OPENROUTER_API_KEY"
22333
+ "OPENROUTER_API_KEY",
22334
+ "ATLASCLOUD_API_KEY"
22203
22335
  ].filter((name) => process.env[name]);
22204
22336
  }
22205
22337
  function isOpenRouterUrl(value) {
@@ -22418,12 +22550,45 @@ function textHash(text3) {
22418
22550
  async function loadDiskCache() {
22419
22551
  try {
22420
22552
  const raw = await readFile(CACHE_FILE, "utf-8");
22553
+ if (Buffer.byteLength(raw, "utf8") > MAX_CACHE_FILE_BYTES) {
22554
+ console.error("[memorix] FastEmbed cache exceeds the disk load limit; starting with an empty cache");
22555
+ return;
22556
+ }
22421
22557
  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`);
22558
+ if (!Array.isArray(entries)) return;
22559
+ for (const entry of entries) {
22560
+ if (!Array.isArray(entry) || typeof entry[0] !== "string" || !isVector(entry[1])) continue;
22561
+ cacheSet(entry[0], entry[1], false);
22562
+ }
22563
+ console.error(`[memorix] Loaded ${cache.size} cached embeddings from disk`);
22424
22564
  } catch {
22425
22565
  }
22426
22566
  }
22567
+ function isVector(value) {
22568
+ return Array.isArray(value) && value.every((entry) => typeof entry === "number");
22569
+ }
22570
+ function maxCachePayloadBytes() {
22571
+ const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
22572
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES;
22573
+ }
22574
+ function cacheSet(hash4, value, markDirty = true) {
22575
+ const incomingBytes = value.length * 8;
22576
+ if (incomingBytes > maxCachePayloadBytes()) return;
22577
+ const existing = cache.get(hash4);
22578
+ if (existing) cachePayloadBytes -= existing.length * 8;
22579
+ else {
22580
+ while (cache.size >= MAX_CACHE_SIZE || cachePayloadBytes + incomingBytes > maxCachePayloadBytes()) {
22581
+ const firstKey = cache.keys().next().value;
22582
+ if (firstKey === void 0) break;
22583
+ const old = cache.get(firstKey);
22584
+ cache.delete(firstKey);
22585
+ if (old) cachePayloadBytes -= old.length * 8;
22586
+ }
22587
+ }
22588
+ cache.set(hash4, value);
22589
+ cachePayloadBytes += incomingBytes;
22590
+ if (markDirty) diskCacheDirty = true;
22591
+ }
22427
22592
  async function saveDiskCache() {
22428
22593
  if (!diskCacheDirty) return;
22429
22594
  try {
@@ -22434,7 +22599,7 @@ async function saveDiskCache() {
22434
22599
  } catch {
22435
22600
  }
22436
22601
  }
22437
- var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, diskCacheDirty, FastEmbedProvider;
22602
+ var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, DEFAULT_CACHE_PAYLOAD_BYTES, MAX_CACHE_FILE_BYTES, cachePayloadBytes, diskCacheDirty, FastEmbedProvider;
22438
22603
  var init_fastembed_provider = __esm({
22439
22604
  "src/embedding/fastembed-provider.ts"() {
22440
22605
  "use strict";
@@ -22444,6 +22609,9 @@ var init_fastembed_provider = __esm({
22444
22609
  CACHE_FILE = join3(CACHE_DIR, ".embedding-cache.json");
22445
22610
  cache = /* @__PURE__ */ new Map();
22446
22611
  MAX_CACHE_SIZE = 5e3;
22612
+ DEFAULT_CACHE_PAYLOAD_BYTES = 64 * 1024 * 1024;
22613
+ MAX_CACHE_FILE_BYTES = 128 * 1024 * 1024;
22614
+ cachePayloadBytes = 0;
22447
22615
  diskCacheDirty = false;
22448
22616
  FastEmbedProvider = class _FastEmbedProvider {
22449
22617
  name = "fastembed-bge-small";
@@ -22485,7 +22653,7 @@ var init_fastembed_provider = __esm({
22485
22653
  if (result.length !== this.dimensions) {
22486
22654
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
22487
22655
  }
22488
- this.cacheSet(hash4, result);
22656
+ cacheSet(hash4, result);
22489
22657
  return result;
22490
22658
  }
22491
22659
  async embedBatch(texts) {
@@ -22510,7 +22678,7 @@ var init_fastembed_provider = __esm({
22510
22678
  const originalIdx = uncachedIndices[batchIdx];
22511
22679
  const plain = Array.from(vec2);
22512
22680
  results[originalIdx] = plain;
22513
- this.cacheSet(textHash(uncachedTexts[batchIdx]), plain);
22681
+ cacheSet(textHash(uncachedTexts[batchIdx]), plain);
22514
22682
  batchIdx++;
22515
22683
  }
22516
22684
  }
@@ -22518,14 +22686,6 @@ var init_fastembed_provider = __esm({
22518
22686
  }
22519
22687
  return results;
22520
22688
  }
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
22689
  };
22530
22690
  }
22531
22691
  });
@@ -22535,7 +22695,15 @@ var transformers_provider_exports = {};
22535
22695
  __export(transformers_provider_exports, {
22536
22696
  TransformersProvider: () => TransformersProvider
22537
22697
  });
22538
- var cache2, MAX_CACHE_SIZE2, TransformersProvider;
22698
+ import { createHash as createHash2 } from "crypto";
22699
+ function cacheKey2(text3) {
22700
+ return createHash2("sha256").update(text3).digest("hex");
22701
+ }
22702
+ function maxCachePayloadBytes2() {
22703
+ const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
22704
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES2;
22705
+ }
22706
+ var cache2, MAX_CACHE_SIZE2, DEFAULT_CACHE_PAYLOAD_BYTES2, cachePayloadBytes2, TransformersProvider;
22539
22707
  var init_transformers_provider = __esm({
22540
22708
  "src/embedding/transformers-provider.ts"() {
22541
22709
  "use strict";
@@ -22543,6 +22711,8 @@ var init_transformers_provider = __esm({
22543
22711
  init_provider();
22544
22712
  cache2 = /* @__PURE__ */ new Map();
22545
22713
  MAX_CACHE_SIZE2 = 5e3;
22714
+ DEFAULT_CACHE_PAYLOAD_BYTES2 = 64 * 1024 * 1024;
22715
+ cachePayloadBytes2 = 0;
22546
22716
  TransformersProvider = class _TransformersProvider {
22547
22717
  name = "transformers-minilm";
22548
22718
  dimensions = 384;
@@ -22577,7 +22747,7 @@ var init_transformers_provider = __esm({
22577
22747
  return Promise.all(inputs.map((input) => this.embedInput(input, options)));
22578
22748
  }
22579
22749
  async embed(text3) {
22580
- const cached2 = cache2.get(text3);
22750
+ const cached2 = cache2.get(cacheKey2(text3));
22581
22751
  if (cached2) return cached2;
22582
22752
  const output = await this.extractor(text3, {
22583
22753
  pooling: "mean",
@@ -22587,7 +22757,7 @@ var init_transformers_provider = __esm({
22587
22757
  if (result.length !== this.dimensions) {
22588
22758
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
22589
22759
  }
22590
- this.cacheSet(text3, result);
22760
+ this.cacheSet(cacheKey2(text3), result);
22591
22761
  return result;
22592
22762
  }
22593
22763
  async embedBatch(texts) {
@@ -22595,7 +22765,7 @@ var init_transformers_provider = __esm({
22595
22765
  const uncachedIndices = [];
22596
22766
  const uncachedTexts = [];
22597
22767
  for (let i4 = 0; i4 < texts.length; i4++) {
22598
- const cached2 = cache2.get(texts[i4]);
22768
+ const cached2 = cache2.get(cacheKey2(texts[i4]));
22599
22769
  if (cached2) {
22600
22770
  results[i4] = cached2;
22601
22771
  } else {
@@ -22613,17 +22783,27 @@ var init_transformers_provider = __esm({
22613
22783
  const vec2 = Array.from(allVecs[i4]);
22614
22784
  const originalIdx = uncachedIndices[i4];
22615
22785
  results[originalIdx] = vec2;
22616
- this.cacheSet(uncachedTexts[i4], vec2);
22786
+ this.cacheSet(cacheKey2(uncachedTexts[i4]), vec2);
22617
22787
  }
22618
22788
  }
22619
22789
  return results;
22620
22790
  }
22621
22791
  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);
22792
+ const incomingBytes = value.length * 8;
22793
+ if (incomingBytes > maxCachePayloadBytes2()) return;
22794
+ const existing = cache2.get(key);
22795
+ if (existing) cachePayloadBytes2 -= existing.length * 8;
22796
+ else {
22797
+ while (cache2.size >= MAX_CACHE_SIZE2 || cachePayloadBytes2 + incomingBytes > maxCachePayloadBytes2()) {
22798
+ const firstKey = cache2.keys().next().value;
22799
+ if (firstKey === void 0) break;
22800
+ const old = cache2.get(firstKey);
22801
+ cache2.delete(firstKey);
22802
+ if (old) cachePayloadBytes2 -= old.length * 8;
22803
+ }
22625
22804
  }
22626
22805
  cache2.set(key, value);
22806
+ cachePayloadBytes2 += incomingBytes;
22627
22807
  }
22628
22808
  };
22629
22809
  }
@@ -22645,7 +22825,7 @@ __export(api_provider_exports, {
22645
22825
  saveDiskCacheNow: () => saveDiskCacheNow,
22646
22826
  seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
22647
22827
  });
22648
- import { createHash as createHash2 } from "crypto";
22828
+ import { createHash as createHash3 } from "crypto";
22649
22829
  import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
22650
22830
  import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
22651
22831
  import { join as join4 } from "path";
@@ -22672,7 +22852,7 @@ function cacheStatFile() {
22672
22852
  function payloadBytes(vector) {
22673
22853
  return vector.length * BYTES_PER_DIM;
22674
22854
  }
22675
- function maxCachePayloadBytes() {
22855
+ function maxCachePayloadBytes3() {
22676
22856
  const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
22677
22857
  if (raw) {
22678
22858
  const parsed = Number.parseInt(raw, 10);
@@ -22682,7 +22862,7 @@ function maxCachePayloadBytes() {
22682
22862
  }
22683
22863
  function maxEntriesForDimensions(dimensions) {
22684
22864
  const dim = Math.max(1, Math.floor(dimensions));
22685
- const byBytes = Math.floor(maxCachePayloadBytes() / (dim * BYTES_PER_DIM));
22865
+ const byBytes = Math.floor(maxCachePayloadBytes3() / (dim * BYTES_PER_DIM));
22686
22866
  return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
22687
22867
  }
22688
22868
  function rememberDimensions(vector) {
@@ -22695,13 +22875,15 @@ function evictOldest() {
22695
22875
  if (firstKey === void 0) return;
22696
22876
  const old = cache3.get(firstKey);
22697
22877
  cache3.delete(firstKey);
22698
- if (old) cachePayloadBytes -= payloadBytes(old);
22878
+ if (old) cachePayloadBytes3 -= payloadBytes(old);
22699
22879
  }
22700
22880
  function evictToFit(incomingBytes, incomingDims) {
22881
+ if (incomingBytes > maxCachePayloadBytes3()) return false;
22701
22882
  const limit2 = maxEntriesForDimensions(incomingDims);
22702
- while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes + incomingBytes > maxCachePayloadBytes())) {
22883
+ while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes3 + incomingBytes > maxCachePayloadBytes3())) {
22703
22884
  evictOldest();
22704
22885
  }
22886
+ return true;
22705
22887
  }
22706
22888
  function isNumberArray(value) {
22707
22889
  return Array.isArray(value) && value.every((entry) => typeof entry === "number");
@@ -22728,12 +22910,12 @@ function cacheInsert(hash4, value, dirty) {
22728
22910
  const incoming = payloadBytes(value);
22729
22911
  const existing = cache3.get(hash4);
22730
22912
  if (existing) {
22731
- cachePayloadBytes -= payloadBytes(existing);
22913
+ cachePayloadBytes3 -= payloadBytes(existing);
22732
22914
  cache3.delete(hash4);
22733
22915
  }
22734
- evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
22916
+ if (!evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS)) return;
22735
22917
  cache3.set(hash4, value);
22736
- cachePayloadBytes += incoming;
22918
+ cachePayloadBytes3 += incoming;
22737
22919
  if (dirty) diskCacheDirty2 = true;
22738
22920
  }
22739
22921
  async function readCacheStat() {
@@ -22769,7 +22951,7 @@ function cacheNamespace(config2) {
22769
22951
  ].join("|");
22770
22952
  }
22771
22953
  function textHash2(text3, namespace) {
22772
- return createHash2("sha256").update(`${namespace}\0${text3}`).digest("hex").slice(0, 16);
22954
+ return createHash3("sha256").update(`${namespace}\0${text3}`).digest("hex").slice(0, 16);
22773
22955
  }
22774
22956
  function inputIdentity(input, options = {}) {
22775
22957
  return JSON.stringify({
@@ -23111,7 +23293,7 @@ function getApiEmbeddingDiskCacheLoadCountForTests() {
23111
23293
  }
23112
23294
  function resetApiEmbeddingCacheForTests() {
23113
23295
  cache3.clear();
23114
- cachePayloadBytes = 0;
23296
+ cachePayloadBytes3 = 0;
23115
23297
  cacheDimensions = null;
23116
23298
  diskCacheEntryCount = 0;
23117
23299
  diskCacheLoaded = false;
@@ -23122,14 +23304,14 @@ function resetApiEmbeddingCacheForTests() {
23122
23304
  }
23123
23305
  function seedApiEmbeddingCacheForTests(entries) {
23124
23306
  cache3.clear();
23125
- cachePayloadBytes = 0;
23307
+ cachePayloadBytes3 = 0;
23126
23308
  cacheDimensions = null;
23127
23309
  for (const [key, value] of entries) cacheInsert(key, value, true);
23128
23310
  diskCacheLoaded = true;
23129
23311
  diskCacheDirty2 = true;
23130
23312
  }
23131
23313
  function getApiEmbeddingCachePayloadBytesForTests() {
23132
- return cachePayloadBytes;
23314
+ return cachePayloadBytes3;
23133
23315
  }
23134
23316
  function getApiEmbeddingJsonlLineCountCallsForTests() {
23135
23317
  return jsonlLineCountCalls;
@@ -23303,7 +23485,7 @@ function scheduleDiskSave() {
23303
23485
  diskSaveTimer = null;
23304
23486
  }, DISK_SAVE_DEBOUNCE_MS);
23305
23487
  }
23306
- function cacheSet(hash4, value) {
23488
+ function cacheSet2(hash4, value) {
23307
23489
  cacheInsert(hash4, value, true);
23308
23490
  }
23309
23491
  function resolveEnvEmbeddingApiKey() {
@@ -23423,7 +23605,7 @@ async function waitForRetry(waitMs, signal) {
23423
23605
  abortSignal.addEventListener("abort", onAbort, { once: true });
23424
23606
  });
23425
23607
  }
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;
23608
+ 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
23609
  var init_api_provider = __esm({
23428
23610
  "src/embedding/api-provider.ts"() {
23429
23611
  "use strict";
@@ -23435,7 +23617,7 @@ var init_api_provider = __esm({
23435
23617
  BYTES_PER_DIM = 8;
23436
23618
  DEFAULT_ASSUMED_DIMENSIONS = 4096;
23437
23619
  MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
23438
- cachePayloadBytes = 0;
23620
+ cachePayloadBytes3 = 0;
23439
23621
  cacheDimensions = null;
23440
23622
  diskCacheEntryCount = 0;
23441
23623
  diskCacheDirty2 = false;
@@ -23620,7 +23802,7 @@ var init_api_provider = __esm({
23620
23802
  } else {
23621
23803
  embedding = await apiCall();
23622
23804
  }
23623
- cacheSet(hash4, embedding);
23805
+ cacheSet2(hash4, embedding);
23624
23806
  scheduleDiskSave();
23625
23807
  return embedding;
23626
23808
  }
@@ -23668,7 +23850,7 @@ var init_api_provider = __esm({
23668
23850
  for (const item of response.data) {
23669
23851
  const originalIdx = chunkIndices[item.index];
23670
23852
  results[originalIdx] = item.embedding;
23671
- cacheSet(textHash2(normalizedTexts[originalIdx], this.cacheKeyNamespace), item.embedding);
23853
+ cacheSet2(textHash2(normalizedTexts[originalIdx], this.cacheKeyNamespace), item.embedding);
23672
23854
  }
23673
23855
  } catch (error40) {
23674
23856
  const providerLimit = parseBatchLimit(error40);
@@ -23746,7 +23928,7 @@ var init_api_provider = __esm({
23746
23928
  throw new Error(`Expected ${this.dimensions}d, got ${embedding.length}d; dimension mismatch`);
23747
23929
  }
23748
23930
  this.trackUsage(response);
23749
- cacheSet(hash4, embedding);
23931
+ cacheSet2(hash4, embedding);
23750
23932
  scheduleDiskSave();
23751
23933
  return embedding;
23752
23934
  }
@@ -25129,6 +25311,7 @@ var init_provider2 = __esm({
25129
25311
  openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4.1-nano" },
25130
25312
  anthropic: { baseUrl: "https://api.anthropic.com/v1", model: "claude-3-5-haiku-latest" },
25131
25313
  openrouter: { baseUrl: "https://openrouter.ai/api/v1", model: "openai/gpt-4.1-nano" },
25314
+ atlascloud: { baseUrl: "https://api.atlascloud.ai/v1", model: "deepseek-ai/deepseek-v3.2" },
25132
25315
  custom: { baseUrl: "http://localhost:11434/v1", model: "llama3" }
25133
25316
  };
25134
25317
  currentConfig = null;
@@ -25892,7 +26075,7 @@ var init_obs_store = __esm({
25892
26075
  });
25893
26076
 
25894
26077
  // src/search/semantic-index.ts
25895
- import { createHash as createHash3 } from "crypto";
26078
+ import { createHash as createHash4 } from "crypto";
25896
26079
  import { createRequire as createRequire3 } from "module";
25897
26080
  import path10 from "path";
25898
26081
  function createSemanticIndexProfile(provider2) {
@@ -25906,7 +26089,7 @@ function stateKey(dataDir, profile) {
25906
26089
  return `${path10.resolve(dataDir)}::${profile.key}`;
25907
26090
  }
25908
26091
  function tableName(profile) {
25909
- const digest2 = createHash3("sha256").update(profile.key).digest("hex").slice(0, 20);
26092
+ const digest2 = createHash4("sha256").update(profile.key).digest("hex").slice(0, 20);
25910
26093
  return `${TABLE_PREFIX}${digest2}`;
25911
26094
  }
25912
26095
  function indexRoot(dataDir) {
@@ -26205,6 +26388,7 @@ var init_secret_filter = __esm({
26205
26388
  // src/runtime/maintenance-jobs.ts
26206
26389
  var maintenance_jobs_exports = {};
26207
26390
  __export(maintenance_jobs_exports, {
26391
+ DEFAULT_FAILED_HISTORY_RETENTION_MS: () => DEFAULT_FAILED_HISTORY_RETENTION_MS,
26208
26392
  MAINTENANCE_JOB_KINDS: () => MAINTENANCE_JOB_KINDS,
26209
26393
  MaintenanceJobStore: () => MaintenanceJobStore,
26210
26394
  MaintenanceJobWorker: () => MaintenanceJobWorker,
@@ -26254,7 +26438,7 @@ function normalizedKinds(kinds) {
26254
26438
  function getMaintenanceRetryDelayMs(attempts) {
26255
26439
  return Math.min(6e4, 1e3 * 2 ** Math.max(0, attempts - 1));
26256
26440
  }
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;
26441
+ 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
26442
  var init_maintenance_jobs = __esm({
26259
26443
  "src/runtime/maintenance-jobs.ts"() {
26260
26444
  "use strict";
@@ -26281,6 +26465,7 @@ var init_maintenance_jobs = __esm({
26281
26465
  DEFAULT_POLL_INTERVAL_MS = 2e3;
26282
26466
  MAX_ERROR_LENGTH = 1e3;
26283
26467
  DEFAULT_COMPLETED_HISTORY_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
26468
+ DEFAULT_FAILED_HISTORY_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
26284
26469
  MaintenanceJobStore = class {
26285
26470
  db;
26286
26471
  constructor(dataDir) {
@@ -26407,18 +26592,21 @@ var init_maintenance_jobs = __esm({
26407
26592
  }
26408
26593
  /**
26409
26594
  * 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.
26595
+ * Failed diagnostics are retained longer, but are still bounded so a
26596
+ * permanently failing job kind cannot grow the database forever.
26412
26597
  */
26413
26598
  pruneCompletedHistory(options = {}) {
26414
26599
  const now3 = options.now ?? Date.now();
26415
26600
  const maxAgeMs = Number.isFinite(options.maxAgeMs) ? Math.max(0, Math.floor(options.maxAgeMs)) : DEFAULT_COMPLETED_HISTORY_RETENTION_MS;
26601
+ const failedMaxAgeMs = Number.isFinite(options.failedMaxAgeMs) ? Math.max(0, Math.floor(options.failedMaxAgeMs)) : DEFAULT_FAILED_HISTORY_RETENTION_MS;
26416
26602
  const result = this.db.prepare(`
26417
26603
  DELETE FROM maintenance_jobs
26418
- WHERE status = 'completed'
26604
+ WHERE (status = 'completed'
26419
26605
  AND completed_at IS NOT NULL
26420
- AND completed_at < ?
26421
- `).run(now3 - maxAgeMs);
26606
+ AND completed_at < ?)
26607
+ OR (status = 'failed'
26608
+ AND updated_at < ?)
26609
+ `).run(now3 - maxAgeMs, now3 - failedMaxAgeMs);
26422
26610
  return Number(result.changes ?? 0);
26423
26611
  }
26424
26612
  claimNext(options) {
@@ -26777,7 +26965,7 @@ __export(evidence_store_exports, {
26777
26965
  EvidenceCardStore: () => EvidenceCardStore,
26778
26966
  buildEvidenceCardId: () => buildEvidenceCardId
26779
26967
  });
26780
- import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
26968
+ import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
26781
26969
  function safeJson(value, fallback) {
26782
26970
  if (typeof value !== "string" || value.length === 0) return fallback;
26783
26971
  try {
@@ -26811,7 +26999,7 @@ function rowToCard(row) {
26811
26999
  };
26812
27000
  }
26813
27001
  function observationHash(observation) {
26814
- return createHash4("sha256").update(JSON.stringify({
27002
+ return createHash5("sha256").update(JSON.stringify({
26815
27003
  projectId: observation.projectId,
26816
27004
  id: observation.id,
26817
27005
  title: observation.title,
@@ -27408,13 +27596,13 @@ __export(workspace_exports, {
27408
27596
  loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
27409
27597
  resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
27410
27598
  });
27411
- import { createHash as createHash5 } from "crypto";
27599
+ import { createHash as createHash6 } from "crypto";
27412
27600
  import { execFile } from "child_process";
27413
27601
  import { promises as fs7 } from "fs";
27414
27602
  import path12 from "path";
27415
27603
  import { promisify } from "util";
27416
27604
  function hash(value) {
27417
- return createHash5("sha256").update(value).digest("hex");
27605
+ return createHash6("sha256").update(value).digest("hex");
27418
27606
  }
27419
27607
  function workspaceId(projectId, mode) {
27420
27608
  return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
@@ -28567,14 +28755,14 @@ __export(asset_store_exports, {
28567
28755
  readMediaAsset: () => readMediaAsset,
28568
28756
  removeMediaAsset: () => removeMediaAsset
28569
28757
  });
28570
- import { createHash as createHash6, randomUUID as randomUUID6 } from "crypto";
28758
+ import { createHash as createHash7, randomUUID as randomUUID6 } from "crypto";
28571
28759
  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
28760
  import path13 from "path";
28573
28761
  function mediaRoot(dataDir) {
28574
28762
  return path13.resolve(dataDir, "media");
28575
28763
  }
28576
28764
  function projectStorageKey(projectId) {
28577
- return createHash6("sha256").update(projectId).digest("hex").slice(0, 16);
28765
+ return createHash7("sha256").update(projectId).digest("hex").slice(0, 16);
28578
28766
  }
28579
28767
  function validateUnderRoot(root, candidate2) {
28580
28768
  const resolved = path13.resolve(candidate2);
@@ -28634,7 +28822,7 @@ async function readHeader(filePath, bytes = 32) {
28634
28822
  }
28635
28823
  async function sha256File(filePath) {
28636
28824
  const bytes = await readFile3(filePath);
28637
- return createHash6("sha256").update(bytes).digest("hex");
28825
+ return createHash7("sha256").update(bytes).digest("hex");
28638
28826
  }
28639
28827
  function destinationFor(root, projectId, sha2562, extension3) {
28640
28828
  const relative5 = path13.join("assets", projectStorageKey(projectId), sha2562.slice(0, 2), `${sha2562}${extension3}`);
@@ -28740,7 +28928,7 @@ async function importMediaBuffer(input) {
28740
28928
  if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
28741
28929
  if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
28742
28930
  const detected = detectMedia(input.bytes.subarray(0, 32));
28743
- const sha2562 = createHash6("sha256").update(input.bytes).digest("hex");
28931
+ const sha2562 = createHash7("sha256").update(input.bytes).digest("hex");
28744
28932
  return importKnownMedia({
28745
28933
  dataDir: input.dataDir,
28746
28934
  projectId: input.projectId,
@@ -28758,7 +28946,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
28758
28946
  if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
28759
28947
  const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
28760
28948
  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");
28949
+ const sha2562 = createHash7("sha256").update(bytes).digest("hex");
28762
28950
  if (sha2562 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
28763
28951
  return bytes;
28764
28952
  }
@@ -81628,7 +81816,7 @@ var require_gaxios = __commonJS({
81628
81816
  var retry_js_1 = require_retry3();
81629
81817
  var stream_1 = __require("stream");
81630
81818
  var interceptor_js_1 = require_interceptor();
81631
- var randomUUID21 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
81819
+ var randomUUID23 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
81632
81820
  var HTTP_STATUS_NO_CONTENT = 204;
81633
81821
  var Gaxios = class {
81634
81822
  agentCache = /* @__PURE__ */ new Map();
@@ -81901,7 +82089,7 @@ var require_gaxios = __commonJS({
81901
82089
  */
81902
82090
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
81903
82091
  if (opts.multipart?.length) {
81904
- const boundary = await randomUUID21();
82092
+ const boundary = await randomUUID23();
81905
82093
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
81906
82094
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
81907
82095
  } else if (shouldDirectlyPassData) {
@@ -87573,16 +87761,16 @@ var require_jwtaccess = __commonJS({
87573
87761
  * @returns A string that returns the cached key.
87574
87762
  */
87575
87763
  getCachedKey(url2, scopes) {
87576
- let cacheKey2 = url2;
87764
+ let cacheKey4 = url2;
87577
87765
  if (scopes && Array.isArray(scopes) && scopes.length) {
87578
- cacheKey2 = url2 ? `${url2}_${scopes.join("_")}` : `${scopes.join("_")}`;
87766
+ cacheKey4 = url2 ? `${url2}_${scopes.join("_")}` : `${scopes.join("_")}`;
87579
87767
  } else if (typeof scopes === "string") {
87580
- cacheKey2 = url2 ? `${url2}_${scopes}` : scopes;
87768
+ cacheKey4 = url2 ? `${url2}_${scopes}` : scopes;
87581
87769
  }
87582
- if (!cacheKey2) {
87770
+ if (!cacheKey4) {
87583
87771
  throw Error("Scopes or url must be provided");
87584
87772
  }
87585
- return cacheKey2;
87773
+ return cacheKey4;
87586
87774
  }
87587
87775
  /**
87588
87776
  * Get a non-expired access token, after refreshing if necessary.
@@ -94128,7 +94316,7 @@ var require_websocket = __commonJS({
94128
94316
  var http3 = __require("http");
94129
94317
  var net = __require("net");
94130
94318
  var tls = __require("tls");
94131
- var { randomBytes: randomBytes3, createHash: createHash27 } = __require("crypto");
94319
+ var { randomBytes: randomBytes3, createHash: createHash29 } = __require("crypto");
94132
94320
  var { Duplex, Readable: Readable3 } = __require("stream");
94133
94321
  var { URL: URL2 } = __require("url");
94134
94322
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -94796,7 +94984,7 @@ var require_websocket = __commonJS({
94796
94984
  abortHandshake(websocket, socket, "Invalid Upgrade header");
94797
94985
  return;
94798
94986
  }
94799
- const digest2 = createHash27("sha1").update(key + GUID).digest("base64");
94987
+ const digest2 = createHash29("sha1").update(key + GUID).digest("base64");
94800
94988
  if (res.headers["sec-websocket-accept"] !== digest2) {
94801
94989
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
94802
94990
  return;
@@ -95168,7 +95356,7 @@ var require_websocket_server = __commonJS({
95168
95356
  var EventEmitter2 = __require("events");
95169
95357
  var http3 = __require("http");
95170
95358
  var { Duplex } = __require("stream");
95171
- var { createHash: createHash27 } = __require("crypto");
95359
+ var { createHash: createHash29 } = __require("crypto");
95172
95360
  var extension3 = require_extension();
95173
95361
  var PerMessageDeflate2 = require_permessage_deflate();
95174
95362
  var subprotocol2 = require_subprotocol();
@@ -95475,7 +95663,7 @@ var require_websocket_server = __commonJS({
95475
95663
  );
95476
95664
  }
95477
95665
  if (this._state > RUNNING) return abortHandshake(socket, 503);
95478
- const digest2 = createHash27("sha1").update(key + GUID).digest("base64");
95666
+ const digest2 = createHash29("sha1").update(key + GUID).digest("base64");
95479
95667
  const headers = [
95480
95668
  "HTTP/1.1 101 Switching Protocols",
95481
95669
  "Upgrade: websocket",
@@ -175397,9 +175585,9 @@ var init_union_priority_sort = __esm({
175397
175585
  // node_modules/typebox/build/value/clean/from_union.mjs
175398
175586
  function FromUnion9(context, type, value) {
175399
175587
  for (const schema of UnionPrioritySort(type.anyOf)) {
175400
- const clean = FromType19(context, schema, Clone2(value));
175401
- if (Check2(context, schema, clean))
175402
- return clean;
175588
+ const clean2 = FromType19(context, schema, Clone2(value));
175589
+ if (Check2(context, schema, clean2))
175590
+ return clean2;
175403
175591
  }
175404
175592
  return value;
175405
175593
  }
@@ -180041,9 +180229,9 @@ var init_long_term = __esm({
180041
180229
  });
180042
180230
 
180043
180231
  // src/codegraph/ids.ts
180044
- import { createHash as createHash7 } from "crypto";
180232
+ import { createHash as createHash8 } from "crypto";
180045
180233
  function digest(input) {
180046
- return createHash7("sha256").update(input).digest("hex").slice(0, 16);
180234
+ return createHash8("sha256").update(input).digest("hex").slice(0, 16);
180047
180235
  }
180048
180236
  function normalizeCodePath(path50) {
180049
180237
  return path50.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
@@ -181091,7 +181279,7 @@ var init_store = __esm({
181091
181279
 
181092
181280
  // src/codegraph/code-state.ts
181093
181281
  import { execFile as execFile2 } from "child_process";
181094
- import { createHash as createHash8 } from "crypto";
181282
+ import { createHash as createHash9 } from "crypto";
181095
181283
  import { promisify as promisify3 } from "util";
181096
181284
  async function runGit(rootPath, args) {
181097
181285
  try {
@@ -181120,7 +181308,7 @@ function changedPathCount(status) {
181120
181308
  return count3;
181121
181309
  }
181122
181310
  function fingerprint(baseRevision, status) {
181123
- return createHash8("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
181311
+ return createHash9("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
181124
181312
  }
181125
181313
  async function collectCodeStateSnapshot(input) {
181126
181314
  const [revision, status] = await Promise.all([
@@ -181214,12 +181402,12 @@ var init_exclude3 = __esm({
181214
181402
  });
181215
181403
 
181216
181404
  // src/codegraph/semantic-provider.ts
181217
- import { createHash as createHash9 } from "crypto";
181405
+ import { createHash as createHash10 } from "crypto";
181218
181406
  import "fs";
181219
181407
  import { resolve as resolve3 } from "path";
181220
181408
  import ts from "typescript";
181221
181409
  function hashText(value) {
181222
- return createHash9("sha256").update(value).digest("hex");
181410
+ return createHash10("sha256").update(value).digest("hex");
181223
181411
  }
181224
181412
  function canonicalPath(value) {
181225
181413
  const normalized = resolve3(value).replace(/\\/g, "/");
@@ -181564,11 +181752,11 @@ __export(lite_provider_exports, {
181564
181752
  indexProjectLite: () => indexProjectLite,
181565
181753
  refreshProjectLite: () => refreshProjectLite
181566
181754
  });
181567
- import { createHash as createHash10 } from "crypto";
181755
+ import { createHash as createHash11 } from "crypto";
181568
181756
  import { existsSync as existsSync14, readdirSync as readdirSync2, readFileSync as readFileSync9, statSync as statSync4 } from "fs";
181569
181757
  import { join as join5, relative as relative2 } from "path";
181570
181758
  function hashText2(text3) {
181571
- return createHash10("sha256").update(text3).digest("hex");
181759
+ return createHash11("sha256").update(text3).digest("hex");
181572
181760
  }
181573
181761
  function extension2(path50) {
181574
181762
  const index = path50.lastIndexOf(".");
@@ -182201,7 +182389,7 @@ var claim_store_exports = {};
182201
182389
  __export(claim_store_exports, {
182202
182390
  ClaimStore: () => ClaimStore
182203
182391
  });
182204
- import { createHash as createHash11, randomUUID as randomUUID12 } from "crypto";
182392
+ import { createHash as createHash12, randomUUID as randomUUID12 } from "crypto";
182205
182393
  function optionalText3(value) {
182206
182394
  return typeof value === "string" && value ? value : void 0;
182207
182395
  }
@@ -182254,7 +182442,7 @@ function rowToEvent3(row) {
182254
182442
  };
182255
182443
  }
182256
182444
  function evidenceKey(input) {
182257
- return createHash11("sha256").update(JSON.stringify([
182445
+ return createHash12("sha256").update(JSON.stringify([
182258
182446
  input.evidenceKind,
182259
182447
  input.evidenceId,
182260
182448
  input.relation,
@@ -182452,7 +182640,7 @@ __export(claims_exports, {
182452
182640
  supersedeClaim: () => supersedeClaim,
182453
182641
  writeClaim: () => writeClaim
182454
182642
  });
182455
- import { createHash as createHash12, randomUUID as randomUUID13 } from "crypto";
182643
+ import { createHash as createHash13, randomUUID as randomUUID13 } from "crypto";
182456
182644
  function compactText2(value, field) {
182457
182645
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
182458
182646
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
@@ -182464,7 +182652,7 @@ function normalizedTerm(value) {
182464
182652
  return compactText2(value, "identity").toLocaleLowerCase("en-US");
182465
182653
  }
182466
182654
  function hashIdentity(parts) {
182467
- return createHash12("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
182655
+ return createHash13("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
182468
182656
  }
182469
182657
  function buildClaimIdentity(input) {
182470
182658
  const subject = normalizedTerm(input.subject);
@@ -182705,7 +182893,7 @@ function supersedeClaim(store2, input) {
182705
182893
  });
182706
182894
  }
182707
182895
  function hashObservation(observation) {
182708
- return createHash12("sha256").update(JSON.stringify([
182896
+ return createHash13("sha256").update(JSON.stringify([
182709
182897
  observation.title,
182710
182898
  observation.narrative,
182711
182899
  observation.facts,
@@ -182949,11 +183137,11 @@ var init_frontmatter = __esm({
182949
183137
  });
182950
183138
 
182951
183139
  // src/knowledge/markdown.ts
182952
- import { createHash as createHash13 } from "crypto";
183140
+ import { createHash as createHash14 } from "crypto";
182953
183141
  import { promises as fs11 } from "fs";
182954
183142
  import path20 from "path";
182955
183143
  function hashContent(content) {
182956
- return createHash13("sha256").update(content).digest("hex");
183144
+ return createHash14("sha256").update(content).digest("hex");
182957
183145
  }
182958
183146
  function stringField(data, key) {
182959
183147
  const value = data[key];
@@ -183079,11 +183267,11 @@ __export(wiki_exports, {
183079
183267
  lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
183080
183268
  readKnowledgePage: () => readKnowledgePage
183081
183269
  });
183082
- import { createHash as createHash14 } from "crypto";
183270
+ import { createHash as createHash15 } from "crypto";
183083
183271
  import { promises as fs12 } from "fs";
183084
183272
  import path21 from "path";
183085
183273
  function hash2(value) {
183086
- return createHash14("sha256").update(value).digest("hex");
183274
+ return createHash15("sha256").update(value).digest("hex");
183087
183275
  }
183088
183276
  function now() {
183089
183277
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -184302,11 +184490,11 @@ __export(workflows_exports, {
184302
184490
  syncCanonicalWorkflows: () => syncCanonicalWorkflows,
184303
184491
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
184304
184492
  });
184305
- import { createHash as createHash15 } from "crypto";
184493
+ import { createHash as createHash16 } from "crypto";
184306
184494
  import { promises as fs13 } from "fs";
184307
184495
  import path22 from "path";
184308
184496
  function hash3(value) {
184309
- return createHash15("sha256").update(value).digest("hex");
184497
+ return createHash16("sha256").update(value).digest("hex");
184310
184498
  }
184311
184499
  function now2() {
184312
184500
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -188043,6 +188231,7 @@ async function searchObservations(options) {
188043
188231
  };
188044
188232
  let queryVector = null;
188045
188233
  let embeddingProviderForSearch = null;
188234
+ let embeddingUnavailable = false;
188046
188235
  if (quality !== "fast" && embeddingEnabled && hasQuery && tier !== "fast") {
188047
188236
  try {
188048
188237
  const provider2 = await getEmbeddingProvider();
@@ -188050,6 +188239,7 @@ async function searchObservations(options) {
188050
188239
  embeddingProviderForSearch = provider2;
188051
188240
  const activeVectorDimensions = getVectorDimensions();
188052
188241
  if (activeVectorDimensions !== null && provider2.dimensions !== activeVectorDimensions) {
188242
+ embeddingUnavailable = true;
188053
188243
  rememberSearchMode(
188054
188244
  modeKey,
188055
188245
  `fulltext (embedding dimension mismatch: provider ${provider2.dimensions}d vs index ${activeVectorDimensions}d)`
@@ -188089,8 +188279,11 @@ async function searchObservations(options) {
188089
188279
  hybridWeights: isCJKHeavy ? { text: 0.2, vector: 0.8 } : { text: 0.6, vector: 0.4 }
188090
188280
  };
188091
188281
  }
188282
+ } else {
188283
+ embeddingUnavailable = true;
188092
188284
  }
188093
188285
  } catch (error40) {
188286
+ embeddingUnavailable = true;
188094
188287
  rememberSearchMode(modeKey, "fulltext (embedding unavailable)");
188095
188288
  console.error("[memorix] Embedding failed or timed out, falling back to fulltext search");
188096
188289
  }
@@ -188098,7 +188291,7 @@ async function searchObservations(options) {
188098
188291
  mark("preSearch");
188099
188292
  let results;
188100
188293
  const persistentCorpusEligible = await shouldUsePersistentSearch(projectIds, options.projectId);
188101
- const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled);
188294
+ const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled || embeddingUnavailable);
188102
188295
  let persistent = persistentCorpusEligible && queryVector && embeddingProviderForSearch ? await searchPersistentSemantically(options, projectIds, requestLimit, queryVector, embeddingProviderForSearch) : canUsePersistentLexical ? await searchPersistentLexically(options, projectIds, requestLimit) : null;
188103
188296
  const durableStore = getOptionalObservationStore();
188104
188297
  if (!persistent && queryVector && durableStore?.hasLexicalIndex?.() && count2(database) === 0) {
@@ -189337,6 +189530,8 @@ var team_store_exports = {};
189337
189530
  __export(team_store_exports, {
189338
189531
  AGENT_TYPE_ROLE_MAP: () => AGENT_TYPE_ROLE_MAP,
189339
189532
  DEFAULT_ROLES: () => DEFAULT_ROLES,
189533
+ DEFAULT_TEAM_QUERY_LIMIT: () => DEFAULT_TEAM_QUERY_LIMIT,
189534
+ MAX_TEAM_QUERY_LIMIT: () => MAX_TEAM_QUERY_LIMIT,
189340
189535
  TeamStore: () => TeamStore,
189341
189536
  getTeamStore: () => getTeamStore,
189342
189537
  initTeamStore: () => initTeamStore,
@@ -189346,6 +189541,10 @@ __export(team_store_exports, {
189346
189541
  import { randomUUID as randomUUID16 } from "crypto";
189347
189542
  import path25 from "path";
189348
189543
  import fs15 from "fs";
189544
+ function clampTeamQueryLimit(limit2) {
189545
+ if (!Number.isFinite(limit2) || limit2 == null) return DEFAULT_TEAM_QUERY_LIMIT;
189546
+ return Math.min(MAX_TEAM_QUERY_LIMIT, Math.max(1, Math.floor(limit2)));
189547
+ }
189349
189548
  function getTeamStore() {
189350
189549
  if (!_teamStore) {
189351
189550
  throw new Error("[memorix] TeamStore not initialized \u2014 call initTeamStore() first");
@@ -189369,12 +189568,14 @@ function resetTeamStore() {
189369
189568
  _teamStore = null;
189370
189569
  _teamStoreDataDir = null;
189371
189570
  }
189372
- var DEFAULT_ROLES, AGENT_TYPE_ROLE_MAP, TeamStore, _teamStore, _teamStoreDataDir;
189571
+ var DEFAULT_TEAM_QUERY_LIMIT, MAX_TEAM_QUERY_LIMIT, DEFAULT_ROLES, AGENT_TYPE_ROLE_MAP, TeamStore, _teamStore, _teamStoreDataDir;
189373
189572
  var init_team_store = __esm({
189374
189573
  "src/team/team-store.ts"() {
189375
189574
  "use strict";
189376
189575
  init_esm_shims();
189377
189576
  init_sqlite_db();
189577
+ DEFAULT_TEAM_QUERY_LIMIT = 100;
189578
+ MAX_TEAM_QUERY_LIMIT = 500;
189378
189579
  DEFAULT_ROLES = [
189379
189580
  { roleId: "planner", label: "Planner", description: "Breaks down requirements, creates tasks, defines dependencies", preferredAgentTypes: ["claude-code", "cursor", "codex"], maxConcurrent: 2 },
189380
189581
  { roleId: "researcher", label: "Researcher", description: "Investigates codebase, gathers context, answers questions", preferredAgentTypes: ["claude-code", "cursor", "gemini-cli"], maxConcurrent: 2 },
@@ -189684,32 +189885,37 @@ var init_team_store = __esm({
189684
189885
  return this.stmtAgentFindByInstance.get(projectId, agentType, instanceId);
189685
189886
  }
189686
189887
  listAgents(projectId, filter) {
189687
- const all = this.stmtAgentListByProject.all(projectId);
189888
+ const limit2 = clampTeamQueryLimit(filter?.limit);
189688
189889
  if (filter?.status) {
189689
- return all.filter((a4) => a4.status === filter.status);
189890
+ return this.db.prepare(
189891
+ `SELECT * FROM team_agents WHERE project_id = ? AND status = ? ORDER BY joined_at DESC LIMIT ?`
189892
+ ).all(projectId, filter.status, limit2);
189690
189893
  }
189691
- return all;
189894
+ return this.db.prepare(
189895
+ `SELECT * FROM team_agents WHERE project_id = ? ORDER BY joined_at DESC LIMIT ?`
189896
+ ).all(projectId, limit2);
189692
189897
  }
189693
189898
  /** List agents across all projects (for global scope) */
189694
- listAllAgents() {
189899
+ listAllAgents(limit2) {
189695
189900
  if (!this.db) return [];
189696
- return this.db.prepare("SELECT * FROM team_agents ORDER BY last_heartbeat DESC").all();
189901
+ return this.db.prepare("SELECT * FROM team_agents ORDER BY last_heartbeat DESC LIMIT ?").all(clampTeamQueryLimit(limit2));
189697
189902
  }
189698
189903
  /** List locks across all projects (for global scope) */
189699
- listAllLocks() {
189904
+ listAllLocks(limit2) {
189700
189905
  if (!this.db) return [];
189701
189906
  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());
189907
+ return this.db.prepare("SELECT * FROM team_locks WHERE expires_at > ? ORDER BY locked_at DESC LIMIT ?").all(Date.now(), clampTeamQueryLimit(limit2));
189703
189908
  }
189704
189909
  /** List tasks across all projects (for global scope) */
189705
189910
  listAllTasks(filter) {
189706
189911
  if (!this.db) return [];
189912
+ const limit2 = clampTeamQueryLimit(filter?.limit);
189707
189913
  if (filter?.available) {
189708
189914
  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();
189915
+ `SELECT t.* FROM team_tasks t WHERE t.status = 'pending' AND t.assignee_agent_id IS NULL ORDER BY t.created_at DESC LIMIT ?`
189916
+ ).all(limit2);
189711
189917
  }
189712
- return this.db.prepare("SELECT * FROM team_tasks ORDER BY created_at DESC").all();
189918
+ return this.db.prepare("SELECT * FROM team_tasks ORDER BY created_at DESC LIMIT ?").all(limit2);
189713
189919
  }
189714
189920
  heartbeat(agentId) {
189715
189921
  const info2 = this.stmtAgentUpdateHeartbeat.run(Date.now(), agentId);
@@ -189744,7 +189950,10 @@ var init_team_store = __esm({
189744
189950
  return staleIds;
189745
189951
  }
189746
189952
  getActiveCount(projectId) {
189747
- return this.listAgents(projectId, { status: "active" }).length;
189953
+ const row = this.db.prepare(
189954
+ `SELECT COUNT(*) AS count FROM team_agents WHERE project_id = ? AND status = 'active'`
189955
+ ).get(projectId);
189956
+ return Number(row?.count ?? 0);
189748
189957
  }
189749
189958
  // ═══════════════════════════════════════════════════════════════════
189750
189959
  // Message Operations
@@ -189779,8 +189988,14 @@ var init_team_store = __esm({
189779
189988
  this.stmtMsgInsert.run(row);
189780
189989
  return row;
189781
189990
  }
189782
- getInbox(projectId, agentId) {
189783
- return this.stmtMsgInbox.all(projectId, agentId);
189991
+ getInbox(projectId, agentId, limit2) {
189992
+ return this.db.prepare(`
189993
+ SELECT * FROM team_messages
189994
+ WHERE project_id = ?
189995
+ AND (recipient_agent_id = ? OR recipient_agent_id IS NULL)
189996
+ ORDER BY created_at ASC
189997
+ LIMIT ?
189998
+ `).all(projectId, agentId, clampTeamQueryLimit(limit2));
189784
189999
  }
189785
190000
  getUnreadCount(projectId, agentId) {
189786
190001
  const row = this.stmtMsgUnreadCount.get(projectId, agentId);
@@ -189937,17 +190152,22 @@ var init_team_store = __esm({
189937
190152
  return info2.changes;
189938
190153
  }
189939
190154
  listTasks(projectId, filter) {
189940
- if (filter?.available) {
189941
- return this.stmtTaskAvailable.all(projectId);
189942
- }
189943
- const all = this.stmtTaskListByProject.all(projectId);
190155
+ const clauses = ["project_id = ?"];
190156
+ const values = [projectId];
190157
+ if (filter?.available) clauses.push("status = 'pending'", "assignee_agent_id IS NULL");
189944
190158
  if (filter?.status) {
189945
- return all.filter((t4) => t4.status === filter.status);
190159
+ clauses.push("status = ?");
190160
+ values.push(filter.status);
189946
190161
  }
189947
190162
  if (filter?.assignee) {
189948
- return all.filter((t4) => t4.assignee_agent_id === filter.assignee);
190163
+ clauses.push("assignee_agent_id = ?");
190164
+ values.push(filter.assignee);
189949
190165
  }
189950
- return all;
190166
+ values.push(clampTeamQueryLimit(filter?.limit));
190167
+ const orderBy = filter?.available ? "created_at ASC, rowid ASC" : "created_at DESC, rowid DESC";
190168
+ return this.db.prepare(
190169
+ `SELECT * FROM team_tasks WHERE ${clauses.join(" AND ")} ORDER BY ${orderBy} LIMIT ?`
190170
+ ).all(...values);
189951
190171
  }
189952
190172
  /**
189953
190173
  * List available tasks for a specific agent, sorted by role affinity.
@@ -189959,7 +190179,7 @@ var init_team_store = __esm({
189959
190179
  const agent = this.stmtAgentFindById.get(agentId);
189960
190180
  if (!agent) return [];
189961
190181
  const agentRole = agent.role;
189962
- const available = this.stmtTaskAvailable.all(projectId);
190182
+ const available = this.listTasks(projectId, { available: true, limit: MAX_TEAM_QUERY_LIMIT });
189963
190183
  const eligible = available.filter((t4) => !t4.required_role || t4.required_role === agentRole);
189964
190184
  const score = (t4) => {
189965
190185
  if (t4.preferred_role === agentRole) return 0;
@@ -190013,12 +190233,16 @@ var init_team_store = __esm({
190013
190233
  const row = this.stmtLockGet.get(file2, projectId);
190014
190234
  return row ?? null;
190015
190235
  }
190016
- listLocks(projectId, agentId) {
190236
+ listLocks(projectId, agentId, limit2) {
190017
190237
  this.stmtLockDeleteExpired.run(projectId, Date.now());
190018
190238
  if (agentId) {
190019
- return this.stmtLockListByAgent.all(projectId, agentId);
190239
+ return this.db.prepare(
190240
+ `SELECT * FROM team_locks WHERE project_id = ? AND locked_by = ? ORDER BY locked_at DESC LIMIT ?`
190241
+ ).all(projectId, agentId, clampTeamQueryLimit(limit2));
190020
190242
  }
190021
- return this.stmtLockListByProject.all(projectId);
190243
+ return this.db.prepare(
190244
+ `SELECT * FROM team_locks WHERE project_id = ? ORDER BY locked_at DESC LIMIT ?`
190245
+ ).all(projectId, clampTeamQueryLimit(limit2));
190022
190246
  }
190023
190247
  releaseAllLocks(agentId) {
190024
190248
  const info2 = this.stmtLockDeleteByAgent.run(agentId);
@@ -192869,6 +193093,8 @@ function buildInitTomlConfig(options) {
192869
193093
  lines.push('model = "claude-3-haiku-20240307"');
192870
193094
  } else if (options.llmProvider === "openrouter") {
192871
193095
  lines.push('# model = "openai/gpt-4o-mini"');
193096
+ } else if (options.llmProvider === "atlascloud") {
193097
+ lines.push('model = "deepseek-ai/deepseek-v3.2"');
192872
193098
  }
192873
193099
  if (options.llmProvider === "custom") {
192874
193100
  lines.push("# OpenAI-compatible endpoint, for example DashScope, DeepSeek, Ollama, or an internal gateway.");
@@ -193001,6 +193227,7 @@ var init_init = __esm({
193001
193227
  { value: "openai", label: "OpenAI", hint: "gpt-4o-mini" },
193002
193228
  { value: "anthropic", label: "Anthropic", hint: "claude-3-haiku" },
193003
193229
  { value: "openrouter", label: "OpenRouter", hint: "multi-provider" },
193230
+ { value: "atlascloud", label: "Atlas Cloud", hint: "DeepSeek V3.2" },
193004
193231
  { value: "custom", label: "Custom", hint: "OpenAI-compatible endpoint" }
193005
193232
  ]
193006
193233
  });
@@ -193054,7 +193281,7 @@ var init_init = __esm({
193054
193281
  ];
193055
193282
  if (llmProvider !== "none") {
193056
193283
  envLines.push("# LLM API key");
193057
- if (llmProvider === "openai" || llmProvider === "custom") {
193284
+ if (llmProvider === "openai" || llmProvider === "custom" || llmProvider === "atlascloud") {
193058
193285
  envLines.push("MEMORIX_LLM_API_KEY=sk-your-key-here");
193059
193286
  } else if (llmProvider === "anthropic") {
193060
193287
  envLines.push("MEMORIX_LLM_API_KEY=sk-ant-your-key-here");
@@ -194595,6 +194822,7 @@ var init_official_skills = __esm({
194595
194822
  "| Need the sequence around one memory | `memorix_timeline` | `memorix memory timeline --id <id>` |",
194596
194823
  "| Check source and freshness for a memory | `memorix_evidence` | `memorix evidence list|get|events` |",
194597
194824
  "| Record whether a memory helped, conflicted, or was corrected | `memorix_feedback` | `memorix feedback record|show|audit` |",
194825
+ "| Track task requirements, decisions, verification, and outcome | `memorix_continuity` | `memorix continuity start|record|show|close` |",
194598
194826
  "| Inspect or import controlled media | `memorix_media` | `memorix media import|attach|list|show|status` |",
194599
194827
  '| Learned reusable project knowledge | `memorix_store` | `memorix memory store --type <type> --entity <name> --title "<title>" "<text>"` |',
194600
194828
  "| Task or bug is complete/outdated | `memorix_resolve` | `memorix memory resolve --ids <ids>` |",
@@ -194618,6 +194846,7 @@ var init_official_skills = __esm({
194618
194846
  "3. Use stale or unbound memory only as a warning or search lead.",
194619
194847
  "4. Store durable decisions, fixes, and gotchas after the project state changes.",
194620
194848
  "5. Resolve completed or obsolete memories so future briefs stay small.",
194849
+ "6. For multi-step work, start a continuity ledger and pass its taskId back to memorix_project_context.",
194621
194850
  "",
194622
194851
  "## Store Rules",
194623
194852
  "",
@@ -198185,6 +198414,7 @@ function freshnessForMemory(status) {
198185
198414
  return "unknown";
198186
198415
  }
198187
198416
  function receiptOmissionKind(raw) {
198417
+ if (raw.includes("continuity")) return "continuity";
198188
198418
  if (raw.includes("continuation")) return "continuation";
198189
198419
  if (raw.includes("task")) return "task";
198190
198420
  if (raw.includes("fact")) return "current-fact";
@@ -198295,6 +198525,77 @@ function renderTaskWorksetPrompt(input) {
198295
198525
  trust: "source-backed"
198296
198526
  });
198297
198527
  appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
198528
+ if (input.continuity) {
198529
+ const continuity = input.continuity;
198530
+ appendLine(lines, "", maxTokens, omitted, "continuity-heading");
198531
+ appendLine(lines, "Task continuity", maxTokens, omitted, "continuity-heading", selected, {
198532
+ kind: "continuity",
198533
+ id: "continuity:" + continuity.taskId,
198534
+ reason: "explicit task continuity ledger",
198535
+ trust: "source-backed"
198536
+ });
198537
+ appendLine(
198538
+ lines,
198539
+ "- Status: " + continuity.status + " | " + short(continuity.task, 30),
198540
+ maxTokens,
198541
+ omitted,
198542
+ "continuity-task"
198543
+ );
198544
+ appendLine(
198545
+ lines,
198546
+ "- Outcome: " + continuity.outcome.state,
198547
+ maxTokens,
198548
+ omitted,
198549
+ "continuity-outcome",
198550
+ selected,
198551
+ {
198552
+ kind: "continuity",
198553
+ id: "continuity:" + continuity.taskId,
198554
+ reason: "deterministic task outcome projection",
198555
+ trust: "derived"
198556
+ }
198557
+ );
198558
+ for (const requirement of continuity.requirements.slice(0, 3)) {
198559
+ appendLine(lines, "- Requirement: " + short(requirement, 24), maxTokens, omitted, "continuity-requirement", selected, {
198560
+ kind: "continuity",
198561
+ id: "continuity:" + continuity.taskId,
198562
+ reason: "explicit task requirement",
198563
+ trust: "source-backed"
198564
+ });
198565
+ }
198566
+ for (const decision of continuity.decisions.slice(0, 2)) {
198567
+ appendLine(lines, "- Decision: " + short(decision, 24), maxTokens, omitted, "continuity-decision", selected, {
198568
+ kind: "continuity",
198569
+ id: "continuity:" + continuity.taskId,
198570
+ reason: "explicit task decision",
198571
+ trust: "source-backed"
198572
+ });
198573
+ }
198574
+ for (const verification of continuity.verification.slice(-3)) {
198575
+ appendLine(
198576
+ lines,
198577
+ "- Verify [" + verification.status + "]: " + short(verification.content, 22),
198578
+ maxTokens,
198579
+ omitted,
198580
+ "continuity-verification",
198581
+ selected,
198582
+ {
198583
+ kind: "continuity",
198584
+ id: "continuity:" + continuity.taskId,
198585
+ reason: "task verification state",
198586
+ trust: "source-backed"
198587
+ }
198588
+ );
198589
+ }
198590
+ for (const risk of continuity.risks.slice(0, 2)) {
198591
+ appendLine(lines, "- Risk: " + short(risk, 22), maxTokens, omitted, "continuity-risk", selected, {
198592
+ kind: "continuity",
198593
+ id: "continuity:" + continuity.taskId,
198594
+ reason: "open task risk",
198595
+ trust: "source-backed"
198596
+ });
198597
+ }
198598
+ }
198298
198599
  if (input.alwaysOn) {
198299
198600
  const hasAlwaysOn = input.alwaysOn.profile.length > 0 || Boolean(input.alwaysOn.state) || input.alwaysOn.durable.length > 0;
198300
198601
  if (hasAlwaysOn) {
@@ -198818,6 +199119,7 @@ async function buildTaskWorkset(input) {
198818
199119
  version: "1.3",
198819
199120
  task,
198820
199121
  lens: input.lens,
199122
+ ...input.continuity ? { continuity: input.continuity } : {},
198821
199123
  ...input.alwaysOn && (input.alwaysOn.profile.length > 0 || input.alwaysOn.state || input.alwaysOn.durable.length > 0) ? { alwaysOn: input.alwaysOn } : {},
198822
199124
  currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
198823
199125
  ...continuation ? { continuation } : {},
@@ -201226,7 +201528,7 @@ var compaction_checkpoint_store_exports = {};
201226
201528
  __export(compaction_checkpoint_store_exports, {
201227
201529
  CompactionCheckpointStore: () => CompactionCheckpointStore
201228
201530
  });
201229
- import { createHash as createHash16, randomUUID as randomUUID17 } from "crypto";
201531
+ import { createHash as createHash17, randomUUID as randomUUID17 } from "crypto";
201230
201532
  function optionalText7(value) {
201231
201533
  return typeof value === "string" && value ? value : void 0;
201232
201534
  }
@@ -201268,7 +201570,7 @@ function sourceKeyFor(input, summary) {
201268
201570
  input.tokensBefore ?? "",
201269
201571
  input.firstKeptEntryId ?? ""
201270
201572
  ].join("\0");
201271
- return `derived:${createHash16("sha256").update(material).digest("hex").slice(0, 32)}`;
201573
+ return `derived:${createHash17("sha256").update(material).digest("hex").slice(0, 32)}`;
201272
201574
  }
201273
201575
  function rowToCheckpoint(row) {
201274
201576
  return {
@@ -201526,6 +201828,305 @@ var init_compaction_checkpoint_store = __esm({
201526
201828
  }
201527
201829
  });
201528
201830
 
201831
+ // src/knowledge/task-continuity.ts
201832
+ var task_continuity_exports = {};
201833
+ __export(task_continuity_exports, {
201834
+ TASK_CONTINUITY_EVENT_KINDS: () => TASK_CONTINUITY_EVENT_KINDS,
201835
+ TaskContinuityStore: () => TaskContinuityStore,
201836
+ evaluateTaskContinuity: () => evaluateTaskContinuity
201837
+ });
201838
+ import { randomUUID as randomUUID18 } from "crypto";
201839
+ function clean(value, maxLength = MAX_TEXT_LENGTH2) {
201840
+ return sanitizeCredentials(value).replace(/\s+/g, " ").trim().slice(0, maxLength);
201841
+ }
201842
+ function cleanList(values, maxLength = MAX_TEXT_LENGTH2) {
201843
+ return [...new Set((values ?? []).map((value) => clean(value, maxLength)).filter(Boolean))].slice(0, MAX_LIST_ITEMS2);
201844
+ }
201845
+ function parseRefs(value) {
201846
+ if (typeof value !== "string") return [];
201847
+ try {
201848
+ const parsed = JSON.parse(value);
201849
+ return Array.isArray(parsed) ? cleanList(parsed.filter((item) => typeof item === "string"), 500).slice(0, MAX_EVIDENCE_REFS) : [];
201850
+ } catch {
201851
+ return [];
201852
+ }
201853
+ }
201854
+ function evaluateTaskContinuity(ledger) {
201855
+ const verification = {
201856
+ passed: ledger.verification.filter((item) => item.status === "passed").length,
201857
+ failed: ledger.verification.filter((item) => item.status === "failed").length,
201858
+ pending: ledger.verification.filter((item) => item.status === "pending").length,
201859
+ skipped: ledger.verification.filter((item) => item.status === "skipped").length
201860
+ };
201861
+ const reasons = [];
201862
+ if (verification.failed > 0) reasons.push("verification-failed");
201863
+ if (verification.pending > 0) reasons.push("verification-pending");
201864
+ if (ledger.risks.length > 0) reasons.push("open-risks");
201865
+ if (ledger.status === "blocked") reasons.push("task-blocked");
201866
+ if (ledger.status === "abandoned") reasons.push("task-abandoned");
201867
+ if (ledger.status === "blocked" || ledger.status === "abandoned" || verification.failed > 0) {
201868
+ return {
201869
+ state: "at-risk",
201870
+ score: verification.passed > verification.failed ? 0.55 : 0.2,
201871
+ verification,
201872
+ reasons
201873
+ };
201874
+ }
201875
+ if (ledger.status === "completed") {
201876
+ if (verification.passed > 0 && verification.pending === 0 && verification.skipped === 0) {
201877
+ return {
201878
+ state: ledger.risks.length > 0 ? "validated-with-risks" : "validated",
201879
+ score: ledger.risks.length > 0 ? 0.8 : 1,
201880
+ verification,
201881
+ reasons: reasons.length > 0 ? reasons : ["verification-passed"]
201882
+ };
201883
+ }
201884
+ return {
201885
+ state: "unverified",
201886
+ score: 0.4,
201887
+ verification,
201888
+ reasons: reasons.length > 0 ? reasons : ["no-passing-verification"]
201889
+ };
201890
+ }
201891
+ return {
201892
+ state: "in-progress",
201893
+ score: verification.passed > 0 && verification.pending === 0 ? 0.75 : 0.5,
201894
+ verification,
201895
+ reasons: reasons.length > 0 ? reasons : ["task-open"]
201896
+ };
201897
+ }
201898
+ function rowToEvent4(row) {
201899
+ const status = row.item_status ? String(row.item_status) : void 0;
201900
+ return {
201901
+ id: String(row.id),
201902
+ projectId: String(row.project_id),
201903
+ taskId: String(row.task_id),
201904
+ task: clean(String(row.task), MAX_TASK_LENGTH),
201905
+ kind: row.kind,
201906
+ content: clean(String(row.content)),
201907
+ ...status ? { status } : {},
201908
+ ...row.source_ref ? { sourceRef: clean(String(row.source_ref), 500) } : {},
201909
+ evidenceRefs: parseRefs(row.evidence_json),
201910
+ ...row.actor ? { actor: clean(String(row.actor), 200) } : {},
201911
+ at: String(row.created_at),
201912
+ ...row.event_key ? { eventKey: clean(String(row.event_key), 200) } : {},
201913
+ ...row.item_key ? { itemKey: clean(String(row.item_key), 120) } : {},
201914
+ ...row.supersedes_id ? { supersedesId: clean(String(row.supersedes_id), 120) } : {}
201915
+ };
201916
+ }
201917
+ function aggregate(events) {
201918
+ const first2 = events.find((event) => event.kind === "task") ?? events[0];
201919
+ const latestOutcome = [...events].reverse().find((event) => event.kind === "outcome");
201920
+ const verificationByKey = /* @__PURE__ */ new Map();
201921
+ for (const event of events.filter((event2) => event2.kind === "verification")) {
201922
+ verificationByKey.set(event.itemKey ?? `event:${event.id}`, event);
201923
+ }
201924
+ const ledger = {
201925
+ taskId: first2.taskId,
201926
+ projectId: first2.projectId,
201927
+ task: first2.task,
201928
+ status: latestOutcome?.status ?? "open",
201929
+ requirements: events.filter((event) => event.kind === "requirement").map((event) => event.content),
201930
+ decisions: events.filter((event) => event.kind === "decision").map((event) => event.content),
201931
+ verification: [...verificationByKey.values()].map((event) => ({
201932
+ id: event.itemKey ?? event.id,
201933
+ content: event.content,
201934
+ status: event.status ?? "pending",
201935
+ ...event.sourceRef ? { sourceRef: event.sourceRef } : {},
201936
+ evidenceRefs: event.evidenceRefs
201937
+ })),
201938
+ risks: events.filter((event) => event.kind === "risk").map((event) => event.content),
201939
+ outcomes: events.filter((event) => event.kind === "outcome").map((event) => event.content),
201940
+ updatedAt: events[events.length - 1].at,
201941
+ events
201942
+ };
201943
+ return { ...ledger, outcome: evaluateTaskContinuity(ledger) };
201944
+ }
201945
+ var TASK_CONTINUITY_EVENT_KINDS, MAX_TEXT_LENGTH2, MAX_TASK_LENGTH, MAX_LIST_ITEMS2, MAX_EVIDENCE_REFS, MAX_EVENTS_PER_TASK, TaskContinuityStore;
201946
+ var init_task_continuity = __esm({
201947
+ "src/knowledge/task-continuity.ts"() {
201948
+ "use strict";
201949
+ init_esm_shims();
201950
+ init_secret_filter();
201951
+ init_sqlite_db();
201952
+ TASK_CONTINUITY_EVENT_KINDS = [
201953
+ "task",
201954
+ "requirement",
201955
+ "decision",
201956
+ "verification",
201957
+ "risk",
201958
+ "outcome"
201959
+ ];
201960
+ MAX_TEXT_LENGTH2 = 2e3;
201961
+ MAX_TASK_LENGTH = 1e3;
201962
+ MAX_LIST_ITEMS2 = 8;
201963
+ MAX_EVIDENCE_REFS = 8;
201964
+ MAX_EVENTS_PER_TASK = 200;
201965
+ TaskContinuityStore = class {
201966
+ db = null;
201967
+ async init(dataDir) {
201968
+ this.db = getDatabase(dataDir);
201969
+ }
201970
+ requireDb() {
201971
+ if (!this.db) throw new Error("TaskContinuityStore is not initialized.");
201972
+ return this.db;
201973
+ }
201974
+ insert(input) {
201975
+ const db2 = this.requireDb();
201976
+ const projectId = clean(input.projectId, 500);
201977
+ const taskId = clean(input.taskId, 120);
201978
+ const eventKey = input.eventKey ? clean(input.eventKey, 200) : void 0;
201979
+ if (eventKey) {
201980
+ const existing = db2.prepare(
201981
+ "SELECT * FROM task_continuity_events WHERE project_id = ? AND task_id = ? AND event_key = ?"
201982
+ ).get(projectId, taskId, eventKey);
201983
+ if (existing) return rowToEvent4(existing);
201984
+ }
201985
+ const existingCount = db2.prepare(
201986
+ "SELECT COUNT(*) AS count FROM task_continuity_events WHERE project_id = ? AND task_id = ?"
201987
+ ).get(projectId, taskId);
201988
+ if (Number(existingCount?.count ?? 0) >= MAX_EVENTS_PER_TASK) {
201989
+ throw new Error("Task continuity ledger reached its 200-event limit.");
201990
+ }
201991
+ const event = {
201992
+ id: randomUUID18(),
201993
+ projectId,
201994
+ taskId,
201995
+ task: clean(input.task, MAX_TASK_LENGTH),
201996
+ kind: input.kind,
201997
+ content: clean(input.content),
201998
+ ...input.status ? { status: input.status } : {},
201999
+ ...input.sourceRef ? { sourceRef: clean(input.sourceRef, 500) } : {},
202000
+ evidenceRefs: cleanList(input.evidenceRefs, 500).slice(0, MAX_EVIDENCE_REFS),
202001
+ ...input.actor ? { actor: clean(input.actor, 200) } : {},
202002
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
202003
+ ...eventKey ? { eventKey } : {},
202004
+ ...input.itemKey ? { itemKey: clean(input.itemKey, 120) } : {},
202005
+ ...input.supersedesId ? { supersedesId: clean(input.supersedesId, 120) } : {}
202006
+ };
202007
+ db2.prepare(`
202008
+ INSERT INTO task_continuity_events (
202009
+ id, project_id, task_id, task, kind, content, item_status,
202010
+ source_ref, evidence_json, actor, created_at, event_key, item_key, supersedes_id
202011
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
202012
+ `).run(
202013
+ event.id,
202014
+ event.projectId,
202015
+ event.taskId,
202016
+ event.task,
202017
+ event.kind,
202018
+ event.content,
202019
+ event.status ?? null,
202020
+ event.sourceRef ?? null,
202021
+ JSON.stringify(event.evidenceRefs),
202022
+ event.actor ?? null,
202023
+ event.at,
202024
+ event.eventKey ?? null,
202025
+ event.itemKey ?? null,
202026
+ event.supersedesId ?? null
202027
+ );
202028
+ return event;
202029
+ }
202030
+ start(input) {
202031
+ const task = clean(input.task, MAX_TASK_LENGTH);
202032
+ if (!task) throw new Error("A non-empty task is required.");
202033
+ const idempotencyKey = input.idempotencyKey ? clean(input.idempotencyKey, 200) : void 0;
202034
+ const taskId = clean(input.taskId ?? idempotencyKey ?? "", 120) || randomUUID18();
202035
+ const requirements = cleanList(input.requirements);
202036
+ const transaction = this.requireDb().transaction(() => {
202037
+ this.insert({
202038
+ projectId: input.projectId,
202039
+ taskId,
202040
+ task,
202041
+ kind: "task",
202042
+ content: task,
202043
+ status: "open",
202044
+ actor: input.actor,
202045
+ ...idempotencyKey ? { eventKey: `start:${idempotencyKey}` } : {}
202046
+ });
202047
+ for (const requirement of requirements) {
202048
+ this.insert({
202049
+ projectId: input.projectId,
202050
+ taskId,
202051
+ task,
202052
+ kind: "requirement",
202053
+ content: requirement,
202054
+ actor: input.actor
202055
+ });
202056
+ }
202057
+ });
202058
+ transaction.immediate();
202059
+ return { taskId, ledger: this.get(input.projectId, taskId) };
202060
+ }
202061
+ record(input) {
202062
+ const projectId = clean(input.projectId, 500);
202063
+ const taskId = clean(input.taskId, 120);
202064
+ const existing = this.get(projectId, taskId);
202065
+ if (!existing) throw new Error("Task continuity ledger not found: " + taskId);
202066
+ const transaction = this.requireDb().transaction(() => this.insert({
202067
+ projectId,
202068
+ taskId,
202069
+ task: existing.task,
202070
+ kind: input.kind,
202071
+ content: input.content,
202072
+ ...input.kind === "verification" && input.verificationStatus ? { status: input.verificationStatus } : {},
202073
+ sourceRef: input.sourceRef,
202074
+ evidenceRefs: input.evidenceRefs,
202075
+ actor: input.actor,
202076
+ at: input.at,
202077
+ ...input.idempotencyKey ? { eventKey: input.idempotencyKey } : {},
202078
+ ...input.verificationId ? { itemKey: input.verificationId } : {}
202079
+ }));
202080
+ const event = transaction.immediate();
202081
+ return { event, ledger: this.get(projectId, taskId) };
202082
+ }
202083
+ close(input) {
202084
+ const projectId = clean(input.projectId, 500);
202085
+ const taskId = clean(input.taskId, 120);
202086
+ const existing = this.get(projectId, taskId);
202087
+ if (!existing) throw new Error("Task continuity ledger not found: " + taskId);
202088
+ const transaction = this.requireDb().transaction(() => this.insert({
202089
+ projectId,
202090
+ taskId,
202091
+ task: existing.task,
202092
+ kind: "outcome",
202093
+ content: input.content,
202094
+ status: input.status,
202095
+ sourceRef: input.sourceRef,
202096
+ evidenceRefs: input.evidenceRefs,
202097
+ actor: input.actor,
202098
+ at: input.at,
202099
+ ...input.idempotencyKey ? { eventKey: input.idempotencyKey } : {}
202100
+ }));
202101
+ const event = transaction.immediate();
202102
+ return { event, ledger: this.get(projectId, taskId) };
202103
+ }
202104
+ get(projectId, taskId) {
202105
+ const normalizedProjectId = clean(projectId, 500);
202106
+ const normalizedTaskId = clean(taskId, 120);
202107
+ const rows = this.requireDb().prepare(`
202108
+ SELECT * FROM task_continuity_events
202109
+ WHERE project_id = ? AND task_id = ?
202110
+ ORDER BY created_at ASC, rowid ASC
202111
+ `).all(normalizedProjectId, normalizedTaskId);
202112
+ if (rows.length === 0) return void 0;
202113
+ return aggregate(rows.map(rowToEvent4));
202114
+ }
202115
+ list(projectId, limit2 = 20) {
202116
+ const rows = this.requireDb().prepare(`
202117
+ SELECT task_id
202118
+ FROM task_continuity_events
202119
+ WHERE project_id = ?
202120
+ GROUP BY task_id
202121
+ ORDER BY MAX(created_at) DESC
202122
+ LIMIT ?
202123
+ `).all(projectId, Math.min(100, Math.max(1, Math.floor(limit2))));
202124
+ return rows.map((row) => this.get(projectId, String(row.task_id))).filter((ledger) => Boolean(ledger));
202125
+ }
202126
+ };
202127
+ }
202128
+ });
202129
+
201529
202130
  // src/codegraph/auto-context.ts
201530
202131
  var auto_context_exports = {};
201531
202132
  __export(auto_context_exports, {
@@ -201782,6 +202383,16 @@ async function buildAutoProjectContext(input) {
201782
202383
  };
201783
202384
  }
201784
202385
  }
202386
+ let continuity = input.continuity;
202387
+ if (!continuity && input.taskId) {
202388
+ try {
202389
+ const { TaskContinuityStore: TaskContinuityStore2 } = await Promise.resolve().then(() => (init_task_continuity(), task_continuity_exports));
202390
+ const continuityStore = new TaskContinuityStore2();
202391
+ await continuityStore.init(input.dataDir);
202392
+ continuity = continuityStore.get(input.project.id, input.taskId);
202393
+ } catch {
202394
+ }
202395
+ }
201785
202396
  const alwaysOn = await buildAlwaysOnBlock({
201786
202397
  dataDir: input.dataDir,
201787
202398
  projectId: input.project.id,
@@ -201800,6 +202411,7 @@ async function buildAutoProjectContext(input) {
201800
202411
  providerQuality: providerQuality2,
201801
202412
  currentFacts: worksetFactLines(currentFacts),
201802
202413
  ...continuation ? { continuation } : {},
202414
+ ...continuity ? { continuity } : {},
201803
202415
  codeState: codeStateLine(overview),
201804
202416
  ...codeEvolution ? { codeEvolution } : {},
201805
202417
  reliableMemory: sourceSets.reliableSources.slice(0, lens.sourceLimit).map((source) => ({
@@ -201847,6 +202459,7 @@ async function buildAutoProjectContext(input) {
201847
202459
  refresh,
201848
202460
  providerQuality: providerQuality2,
201849
202461
  ...continuationRequested && workset.continuation ? { continuation: workset.continuation } : {},
202462
+ ...workset.continuity ? { continuity: workset.continuity } : {},
201850
202463
  workset
201851
202464
  };
201852
202465
  }
@@ -202148,7 +202761,14 @@ function buildBoundedContextReceipt(input) {
202148
202761
  externalState: provider2?.external.state ?? "not-detected",
202149
202762
  ...input.workset.provenance.snapshotId ? { snapshotId: input.workset.provenance.snapshotId } : {}
202150
202763
  },
202151
- ...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {}
202764
+ ...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {},
202765
+ ...input.workset.continuity ? {
202766
+ continuity: {
202767
+ taskId: input.workset.continuity.taskId,
202768
+ status: input.workset.continuity.status,
202769
+ outcome: input.workset.continuity.outcome
202770
+ }
202771
+ } : {}
202152
202772
  };
202153
202773
  }
202154
202774
  var init_context_receipt = __esm({
@@ -202159,10 +202779,10 @@ var init_context_receipt = __esm({
202159
202779
  });
202160
202780
 
202161
202781
  // src/cli/fallback-budget.ts
202162
- import { createHash as createHash17 } from "crypto";
202782
+ import { createHash as createHash18 } from "crypto";
202163
202783
  function fallbackRequestKey(projectId, task) {
202164
202784
  const normalized = (task ?? "").trim().toLowerCase().replace(/\s+/g, " ");
202165
- return createHash17("sha256").update(`${projectId}
202785
+ return createHash18("sha256").update(`${projectId}
202166
202786
  ${normalized}`).digest("hex").slice(0, 32);
202167
202787
  }
202168
202788
  var DEFAULT_FALLBACK_TTL_MS, CliFallbackBudget;
@@ -202247,6 +202867,7 @@ var init_context2 = __esm({
202247
202867
  },
202248
202868
  args: {
202249
202869
  task: { type: "string", description: "Current task for context shaping" },
202870
+ taskId: { type: "string", description: "Optional task continuity ledger id to include" },
202250
202871
  input: {
202251
202872
  type: "positional",
202252
202873
  description: "Current task for context shaping (ergonomic positional form)",
@@ -202278,6 +202899,7 @@ var init_context2 = __esm({
202278
202899
  dataDir,
202279
202900
  observations: filterReadableObservations(getAllObservations(), reader2),
202280
202901
  task,
202902
+ taskId: args.taskId?.trim() || void 0,
202281
202903
  agent: coerceAgentTarget(args.agent),
202282
202904
  refresh: coerceRefreshMode(args.refresh),
202283
202905
  reader: reader2,
@@ -203780,6 +204402,158 @@ var init_feedback2 = __esm({
203780
204402
  }
203781
204403
  });
203782
204404
 
204405
+ // src/cli/commands/continuity.ts
204406
+ var continuity_exports = {};
204407
+ __export(continuity_exports, {
204408
+ default: () => continuity_default
204409
+ });
204410
+ var recordKinds, continuity_default;
204411
+ var init_continuity = __esm({
204412
+ "src/cli/commands/continuity.ts"() {
204413
+ "use strict";
204414
+ init_esm_shims();
204415
+ init_dist2();
204416
+ init_task_continuity();
204417
+ init_operator_shared();
204418
+ recordKinds = [
204419
+ "requirement",
204420
+ "decision",
204421
+ "verification",
204422
+ "risk"
204423
+ ];
204424
+ continuity_default = defineCommand({
204425
+ meta: {
204426
+ name: "continuity",
204427
+ description: "Track task requirements, decisions, verification, risks, and outcomes"
204428
+ },
204429
+ args: {
204430
+ taskId: { type: "string", description: "Continuity task id" },
204431
+ task: { type: "string", description: "Task description for start" },
204432
+ requirements: { type: "string", description: "Comma-separated requirements for start" },
204433
+ kind: { type: "string", description: "requirement, decision, verification, or risk" },
204434
+ content: { type: "string", description: "Entry or outcome text" },
204435
+ verificationStatus: { type: "string", description: "pending, passed, failed, or skipped" },
204436
+ verificationId: { type: "string", description: "Stable verification obligation id for status transitions" },
204437
+ idempotencyKey: { type: "string", description: "Stable retry key for idempotent writes" },
204438
+ status: { type: "string", description: "completed, blocked, or abandoned for close" },
204439
+ source: { type: "string", description: "Source or evidence reference" },
204440
+ evidence: { type: "string", description: "Comma-separated evidence references" },
204441
+ actor: { type: "string", description: "Actor id or label" },
204442
+ limit: { type: "string", description: "Maximum ledgers for list" },
204443
+ json: { type: "boolean", description: "Emit machine-readable JSON output" }
204444
+ },
204445
+ run: async ({ args }) => {
204446
+ const action = args._?.[0] || "";
204447
+ const asJson = !!args.json;
204448
+ try {
204449
+ const { project, dataDir, identity } = await getCliProjectContext({ searchIndex: false });
204450
+ const store2 = new TaskContinuityStore();
204451
+ await store2.init(dataDir);
204452
+ const actor = String(args.actor || identity?.agentId || "").trim() || void 0;
204453
+ if (action === "start") {
204454
+ if (!args.task) {
204455
+ emitError('task is required for "memorix continuity start"', asJson);
204456
+ return;
204457
+ }
204458
+ const result = store2.start({
204459
+ projectId: project.id,
204460
+ task: String(args.task),
204461
+ ...args.taskId ? { taskId: String(args.taskId) } : {},
204462
+ requirements: parseCsvList(args.requirements),
204463
+ ...actor ? { actor } : {},
204464
+ ...args.idempotencyKey ? { idempotencyKey: String(args.idempotencyKey) } : {}
204465
+ });
204466
+ emitResult({ project, ...result }, "Continuity started: " + result.taskId, asJson);
204467
+ return;
204468
+ }
204469
+ if (action === "list") {
204470
+ const ledgers = store2.list(project.id, Number(args.limit || 20));
204471
+ emitResult(
204472
+ { project, ledgers },
204473
+ ledgers.length ? ledgers.map((item) => item.status + ": " + item.taskId + " " + item.task).join("\n") : "No continuity ledgers found.",
204474
+ asJson
204475
+ );
204476
+ return;
204477
+ }
204478
+ const taskId = String(args.taskId || "").trim();
204479
+ if (!taskId) {
204480
+ emitError("taskId is required for this continuity action.", asJson);
204481
+ return;
204482
+ }
204483
+ if (action === "show") {
204484
+ const ledger = store2.get(project.id, taskId);
204485
+ if (!ledger) {
204486
+ emitError("Task continuity ledger not found.", asJson);
204487
+ return;
204488
+ }
204489
+ emitResult({ project, ledger }, JSON.stringify(ledger, null, 2), asJson);
204490
+ return;
204491
+ }
204492
+ if (action === "record") {
204493
+ const kind = String(args.kind || "").trim();
204494
+ const content = String(args.content || "").trim();
204495
+ if (!recordKinds.includes(kind) || !content) {
204496
+ emitError("record requires --kind (requirement, decision, verification, risk) and --content.", asJson);
204497
+ return;
204498
+ }
204499
+ const verificationStatus = args.verificationStatus ? String(args.verificationStatus).trim() : void 0;
204500
+ if (verificationStatus && !["pending", "passed", "failed", "skipped"].includes(verificationStatus)) {
204501
+ emitError("verificationStatus must be pending, passed, failed, or skipped.", asJson);
204502
+ return;
204503
+ }
204504
+ const result = store2.record({
204505
+ projectId: project.id,
204506
+ taskId,
204507
+ kind,
204508
+ content,
204509
+ ...verificationStatus ? { verificationStatus } : {},
204510
+ ...args.source ? { sourceRef: String(args.source) } : {},
204511
+ evidenceRefs: parseCsvList(args.evidence),
204512
+ ...actor ? { actor } : {},
204513
+ ...args.verificationId ? { verificationId: String(args.verificationId) } : {},
204514
+ ...args.idempotencyKey ? { idempotencyKey: String(args.idempotencyKey) } : {}
204515
+ });
204516
+ emitResult({ project, ...result }, "Continuity recorded: " + kind, asJson);
204517
+ return;
204518
+ }
204519
+ if (action === "close") {
204520
+ const status = String(args.status || "").trim();
204521
+ const content = String(args.content || "").trim();
204522
+ if (!["completed", "blocked", "abandoned"].includes(status) || !content) {
204523
+ emitError("close requires --status (completed, blocked, abandoned) and --content.", asJson);
204524
+ return;
204525
+ }
204526
+ const result = store2.close({
204527
+ projectId: project.id,
204528
+ taskId,
204529
+ status,
204530
+ content,
204531
+ ...args.source ? { sourceRef: String(args.source) } : {},
204532
+ evidenceRefs: parseCsvList(args.evidence),
204533
+ ...actor ? { actor } : {},
204534
+ ...args.idempotencyKey ? { idempotencyKey: String(args.idempotencyKey) } : {}
204535
+ });
204536
+ emitResult({ project, ...result }, "Continuity closed: " + status, asJson);
204537
+ return;
204538
+ }
204539
+ console.log("Memorix Task Continuity");
204540
+ console.log("");
204541
+ console.log("Usage:");
204542
+ console.log(' memorix continuity start --task "..." [--requirements "a,b"]');
204543
+ console.log(' memorix continuity record --taskId <id> --kind decision --content "..."');
204544
+ console.log(' memorix continuity record --taskId <id> --kind verification --verificationStatus passed --content "..."');
204545
+ console.log(" Use --verificationId <id> to transition an existing verification obligation.");
204546
+ console.log(" memorix continuity show --taskId <id>");
204547
+ console.log(" memorix continuity list [--limit 20]");
204548
+ console.log(' memorix continuity close --taskId <id> --status completed --content "..."');
204549
+ } catch (error40) {
204550
+ emitError(error40 instanceof Error ? error40.message : String(error40), asJson);
204551
+ }
204552
+ }
204553
+ });
204554
+ }
204555
+ });
204556
+
203783
204557
  // src/cli/commands/reasoning.ts
203784
204558
  var reasoning_exports = {};
203785
204559
  __export(reasoning_exports, {
@@ -206980,7 +207754,7 @@ __export(planner_exports, {
206980
207754
  materializeTaskGraph: () => materializeTaskGraph,
206981
207755
  seedAutonomousPipeline: () => seedAutonomousPipeline
206982
207756
  });
206983
- import { randomUUID as randomUUID18 } from "crypto";
207757
+ import { randomUUID as randomUUID19 } from "crypto";
206984
207758
  function isPlannerTask(metadata) {
206985
207759
  if (!metadata) return null;
206986
207760
  try {
@@ -207038,7 +207812,7 @@ function seedAutonomousPipeline(teamStore, projectId, config2, opts) {
207038
207812
  const maxIterations = config2.maxIterations ?? 3;
207039
207813
  const taskBudget = config2.taskBudget ?? 15;
207040
207814
  const structuredPlan = opts?.structuredPlan ?? true;
207041
- const pipelineId = randomUUID18();
207815
+ const pipelineId = randomUUID19();
207042
207816
  const meta = {
207043
207817
  plannerType: "plan",
207044
207818
  pipelineId,
@@ -207957,11 +208731,11 @@ __export(receipt_service_exports, {
207957
208731
  buildHandoffReceipt: () => buildHandoffReceipt,
207958
208732
  formatHandoffReceipt: () => formatHandoffReceipt
207959
208733
  });
207960
- import { createHash as createHash18 } from "crypto";
208734
+ import { createHash as createHash19 } from "crypto";
207961
208735
  import { existsSync as existsSync23, readFileSync as readFileSync15 } from "fs";
207962
208736
  import path37 from "path";
207963
208737
  function sha256(value) {
207964
- return `sha256:${createHash18("sha256").update(value).digest("hex")}`;
208738
+ return `sha256:${createHash19("sha256").update(value).digest("hex")}`;
207965
208739
  }
207966
208740
  function hashId(projectId, id) {
207967
208741
  return sha256(`${projectId}:obs:${id}`);
@@ -208194,6 +208968,7 @@ var init_tool_profile = __esm({
208194
208968
  // keep the default micro profile at its nine-tool token budget.
208195
208969
  memorix_evidence: ["lite", "team", "full"],
208196
208970
  memorix_feedback: ["lite", "team", "full"],
208971
+ memorix_continuity: ["lite", "team", "full"],
208197
208972
  // ── lite: extended solo memory surface ────────────────────────────
208198
208973
  memorix_graph_context: ["lite", "team", "full"],
208199
208974
  memorix_timeline: ["lite", "team", "full"],
@@ -208244,7 +209019,7 @@ var init_tool_profile = __esm({
208244
209019
  function getMcpServerInfo() {
208245
209020
  return {
208246
209021
  name: "memorix",
208247
- version: true ? "1.9.5" : "1.0.1"
209022
+ version: true ? "1.9.7" : "1.0.1"
208248
209023
  };
208249
209024
  }
208250
209025
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -215850,8 +216625,8 @@ var require_core = __commonJS({
215850
216625
  return this;
215851
216626
  }
215852
216627
  case "object": {
215853
- const cacheKey2 = schemaKeyRef;
215854
- this._cache.delete(cacheKey2);
216628
+ const cacheKey4 = schemaKeyRef;
216629
+ this._cache.delete(cacheKey4);
215855
216630
  let id = schemaKeyRef[this.opts.schemaId];
215856
216631
  if (id) {
215857
216632
  id = (0, resolve_1.normalizeId)(id);
@@ -221039,9 +221814,9 @@ var init_graph_scope = __esm({
221039
221814
  });
221040
221815
 
221041
221816
  // src/rules/utils.ts
221042
- import { createHash as createHash19 } from "crypto";
221817
+ import { createHash as createHash20 } from "crypto";
221043
221818
  function hashContent2(content) {
221044
- return createHash19("sha256").update(content.trim()).digest("hex").substring(0, 16);
221819
+ return createHash20("sha256").update(content.trim()).digest("hex").substring(0, 16);
221045
221820
  }
221046
221821
  function generateRuleId(source, filePath) {
221047
221822
  const sanitized = filePath.replace(/[\/\\]/g, "-").replace(/^\./, "");
@@ -222630,8 +223405,8 @@ function legacyBehavior(homeDir) {
222630
223405
  function getBehaviorConfig(options = {}) {
222631
223406
  const homeDir = options.homeDir ?? homedir34();
222632
223407
  const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
222633
- const cacheKey2 = `${homeDir}\0${projectRoot ?? ""}`;
222634
- const cached2 = cache4.get(cacheKey2);
223408
+ const cacheKey4 = `${homeDir}\0${projectRoot ?? ""}`;
223409
+ const cached2 = cache4.get(cacheKey4);
222635
223410
  if (cached2) return cached2;
222636
223411
  const legacy = legacyBehavior(homeDir);
222637
223412
  let resolved = {};
@@ -222645,7 +223420,7 @@ function getBehaviorConfig(options = {}) {
222645
223420
  autoCleanup: typeof resolved.autoCleanup === "boolean" ? resolved.autoCleanup : typeof legacy.autoCleanup === "boolean" ? legacy.autoCleanup : DEFAULTS.autoCleanup,
222646
223421
  formationMode: isFormationMode(resolved.formation) ? resolved.formation : isFormationMode(legacy.formationMode) ? legacy.formationMode : DEFAULTS.formationMode
222647
223422
  };
222648
- cache4.set(cacheKey2, config2);
223423
+ cache4.set(cacheKey4, config2);
222649
223424
  return config2;
222650
223425
  }
222651
223426
  function resetBehaviorConfigCache() {
@@ -264676,7 +265451,7 @@ __export(server_exports2, {
264676
265451
  createMemorixServer: () => createMemorixServer,
264677
265452
  shouldAwaitProjectRuntime: () => shouldAwaitProjectRuntime
264678
265453
  });
264679
- import { createHash as createHash20 } from "crypto";
265454
+ import { createHash as createHash21 } from "crypto";
264680
265455
  function formatFormationStageDurations(stageDurationsMs) {
264681
265456
  const orderedStages = ["extract", "resolve", "evaluate"];
264682
265457
  const parts = orderedStages.filter((stage) => stageDurationsMs[stage] !== void 0).map((stage) => `${stage}=${stageDurationsMs[stage]}ms`);
@@ -264758,7 +265533,7 @@ function coerceObjectArray(val) {
264758
265533
  return [];
264759
265534
  }
264760
265535
  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);
265536
+ const digest2 = createHash21("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
264762
265537
  return `auto-${digest2}`;
264763
265538
  }
264764
265539
  function shouldAwaitProjectRuntime(toolName) {
@@ -264965,6 +265740,22 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
264965
265740
  }
264966
265741
  await projectRuntimeInitPromise;
264967
265742
  };
265743
+ const activateProjectRuntime = async () => {
265744
+ if (!projectResolved) return;
265745
+ await initObservationStore(projectDir2);
265746
+ await initMiniSkillStore(projectDir2);
265747
+ await initSessionStore(projectDir2);
265748
+ await initObservations(projectDir2, { skipCorpusLoad: true });
265749
+ try {
265750
+ const { initProjectRoot: initProjectRoot2 } = await Promise.resolve().then(() => (init_yaml_loader(), yaml_loader_exports));
265751
+ const { resetDotenv: resetDotenv2, loadDotenv: loadDotenv2 } = await Promise.resolve().then(() => (init_dotenv_loader(), dotenv_loader_exports));
265752
+ initProjectRoot2(project.rootPath);
265753
+ resetDotenv2();
265754
+ loadDotenv2(project.rootPath);
265755
+ } catch {
265756
+ }
265757
+ initLLM({ projectRoot: project.rootPath });
265758
+ };
264968
265759
  let maintenanceWorker = null;
264969
265760
  const startProjectMaintenanceWorker = async (autoCleanup = false) => {
264970
265761
  maintenanceWorker?.stop();
@@ -265880,6 +266671,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
265880
266671
  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
266672
  inputSchema: {
265882
266673
  task: external_exports2.string().optional().describe("Current coding task or question"),
266674
+ taskId: external_exports2.string().max(120).optional().describe("Optional task continuity ledger id to include in the brief"),
265883
266675
  refresh: external_exports2.enum(["auto", "always", "never"]).optional().default("auto").describe(
265884
266676
  "Code Memory refresh policy. auto refreshes only when missing or stale."
265885
266677
  ),
@@ -265904,7 +266696,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
265904
266696
  limit: external_exports2.number().optional().describe("Reserved for future source limits; current prompt stays compact by default.")
265905
266697
  }
265906
266698
  },
265907
- async ({ task, refresh, format, agent }) => {
266699
+ async ({ task, taskId, refresh, format, agent }) => {
265908
266700
  const unresolved = requireResolvedProject("build project context for the current project");
265909
266701
  if (unresolved) return unresolved;
265910
266702
  const [
@@ -265934,6 +266726,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
265934
266726
  dataDir: projectDir2,
265935
266727
  observations: observations2,
265936
266728
  task,
266729
+ taskId,
265937
266730
  agent,
265938
266731
  refresh: refresh ?? "auto",
265939
266732
  reader: getObservationReader(),
@@ -268359,6 +269152,94 @@ ${json4}
268359
269152
  }
268360
269153
  }
268361
269154
  );
269155
+ server.registerTool(
269156
+ "memorix_continuity",
269157
+ {
269158
+ title: "Task Continuity Ledger",
269159
+ 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.",
269160
+ inputSchema: {
269161
+ action: external_exports2.enum(["start", "record", "show", "list", "close"]).describe("Continuity operation"),
269162
+ taskId: external_exports2.string().max(120).optional().describe("Continuity task id"),
269163
+ task: external_exports2.string().max(1e3).optional().describe("Task description for start"),
269164
+ requirements: external_exports2.array(external_exports2.string().max(2e3)).max(8).optional().describe("Requirements for start"),
269165
+ kind: external_exports2.enum(["requirement", "decision", "verification", "risk"]).optional().describe("Entry kind for record"),
269166
+ content: external_exports2.string().max(2e3).optional().describe("Requirement, decision, verification, risk, or outcome text"),
269167
+ verificationStatus: external_exports2.enum(["pending", "passed", "failed", "skipped"]).optional(),
269168
+ verificationId: external_exports2.string().max(120).optional().describe("Stable verification obligation id; reuse it to move pending to passed/failed"),
269169
+ idempotencyKey: external_exports2.string().max(200).optional().describe("Stable retry key; repeating the same operation returns the original event"),
269170
+ status: external_exports2.enum(["completed", "blocked", "abandoned"]).optional().describe("Outcome status for close"),
269171
+ sourceRef: external_exports2.string().max(500).optional().describe("Evidence or source reference"),
269172
+ evidenceRefs: external_exports2.array(external_exports2.string().max(500)).max(8).optional(),
269173
+ actor: external_exports2.string().max(200).optional(),
269174
+ limit: external_exports2.number().int().positive().max(100).optional().default(20)
269175
+ }
269176
+ },
269177
+ async ({ action, taskId, task, requirements, kind, content, verificationStatus, verificationId, idempotencyKey, status, sourceRef, evidenceRefs: evidenceRefs2, actor, limit: limit2 }) => {
269178
+ const unresolved = requireResolvedProject("manage task continuity for the current project");
269179
+ if (unresolved) return unresolved;
269180
+ const { TaskContinuityStore: TaskContinuityStore2 } = await Promise.resolve().then(() => (init_task_continuity(), task_continuity_exports));
269181
+ const continuity = new TaskContinuityStore2();
269182
+ await continuity.init(projectDir2);
269183
+ const effectiveActor = actor ?? currentAgentId;
269184
+ try {
269185
+ if (action === "start") {
269186
+ if (!task?.trim()) return { content: [{ type: "text", text: "task is required for continuity start." }], isError: true };
269187
+ const result2 = continuity.start({
269188
+ projectId: project.id,
269189
+ task,
269190
+ ...taskId ? { taskId } : {},
269191
+ requirements,
269192
+ ...effectiveActor ? { actor: effectiveActor } : {},
269193
+ ...idempotencyKey ? { idempotencyKey } : {}
269194
+ });
269195
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result2 }, null, 2) }] };
269196
+ }
269197
+ if (action === "list") {
269198
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ledgers: continuity.list(project.id, limit2) }, null, 2) }] };
269199
+ }
269200
+ if (!taskId?.trim()) return { content: [{ type: "text", text: "taskId is required for this continuity action." }], isError: true };
269201
+ if (action === "show") {
269202
+ const ledger = continuity.get(project.id, taskId);
269203
+ if (!ledger) return { content: [{ type: "text", text: "Task continuity ledger not found." }], isError: true };
269204
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ledger }, null, 2) }] };
269205
+ }
269206
+ if (action === "record") {
269207
+ if (!kind || !content?.trim()) {
269208
+ return { content: [{ type: "text", text: "record requires kind and content." }], isError: true };
269209
+ }
269210
+ const result2 = continuity.record({
269211
+ projectId: project.id,
269212
+ taskId,
269213
+ kind,
269214
+ content,
269215
+ ...verificationStatus ? { verificationStatus } : {},
269216
+ ...sourceRef ? { sourceRef } : {},
269217
+ ...evidenceRefs2 ? { evidenceRefs: evidenceRefs2 } : {},
269218
+ ...effectiveActor ? { actor: effectiveActor } : {},
269219
+ ...verificationId ? { verificationId } : {},
269220
+ ...idempotencyKey ? { idempotencyKey } : {}
269221
+ });
269222
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result2 }, null, 2) }] };
269223
+ }
269224
+ if (!status || !content?.trim()) {
269225
+ return { content: [{ type: "text", text: "close requires status and content." }], isError: true };
269226
+ }
269227
+ const result = continuity.close({
269228
+ projectId: project.id,
269229
+ taskId,
269230
+ status,
269231
+ content,
269232
+ ...sourceRef ? { sourceRef } : {},
269233
+ ...evidenceRefs2 ? { evidenceRefs: evidenceRefs2 } : {},
269234
+ ...effectiveActor ? { actor: effectiveActor } : {},
269235
+ ...idempotencyKey ? { idempotencyKey } : {}
269236
+ });
269237
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result }, null, 2) }] };
269238
+ } catch (error40) {
269239
+ return { content: [{ type: "text", text: error40 instanceof Error ? error40.message : String(error40) }], isError: true };
269240
+ }
269241
+ }
269242
+ );
268362
269243
  if (teamFeaturesEnabled) {
268363
269244
  const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
268364
269245
  initTeamStoreForProject = initTeamStore2;
@@ -269386,7 +270267,9 @@ fi
269386
270267
  graphManager,
269387
270268
  projectId: project.id,
269388
270269
  deferredInit,
270270
+ activateProjectRuntime,
269389
270271
  switchProject,
270272
+ getProjectDataDir: () => projectDir2,
269390
270273
  isExplicitlyBound: () => projectBinding.isExplicit(),
269391
270274
  getRequestContext,
269392
270275
  handleTransportClose
@@ -269462,13 +270345,21 @@ var init_server4 = __esm({
269462
270345
  // src/server/modern-mcp-bridge.ts
269463
270346
  var modern_mcp_bridge_exports = {};
269464
270347
  __export(modern_mcp_bridge_exports, {
269465
- createModernMcpBridge: () => createModernMcpBridge
270348
+ ModernMcpRuntimePool: () => ModernMcpRuntimePool,
270349
+ createModernMcpBridge: () => createModernMcpBridge,
270350
+ createModernMcpRuntime: () => createModernMcpRuntime,
270351
+ withBusinessRuntimeQueue: () => withBusinessRuntimeQueue
269466
270352
  });
269467
270353
  import {
269468
270354
  fromJsonSchema,
269469
270355
  McpServer as ModernMcpServer
269470
270356
  } from "@modelcontextprotocol/server";
269471
- async function createModernMcpBridge(options) {
270357
+ function withBusinessRuntimeQueue(work) {
270358
+ const result = businessRuntimeQueue.then(work);
270359
+ businessRuntimeQueue = result.catch(() => void 0);
270360
+ return result;
270361
+ }
270362
+ async function createModernMcpRuntime(options) {
269472
270363
  const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server4(), server_exports2));
269473
270364
  const legacy = await createMemorixServer2(
269474
270365
  options.projectRoot,
@@ -269489,41 +270380,62 @@ async function createModernMcpBridge(options) {
269489
270380
  await legacy.server.connect(serverTransport);
269490
270381
  await legacyClient.connect(clientTransport);
269491
270382
  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 = {
270383
+ const callTool = (name, args) => withBusinessRuntimeQueue(async () => {
270384
+ await legacy.activateProjectRuntime();
270385
+ return await legacyClient.callTool({ name, arguments: args });
270386
+ });
270387
+ const lifecycle = {
269509
270388
  projectId: legacy.projectId,
269510
270389
  deferredInit: legacy.deferredInit,
270390
+ activateProjectRuntime: legacy.activateProjectRuntime,
269511
270391
  switchProject: legacy.switchProject,
270392
+ getProjectDataDir: legacy.getProjectDataDir,
269512
270393
  isExplicitlyBound: legacy.isExplicitlyBound,
269513
270394
  handleTransportClose: legacy.handleTransportClose
269514
270395
  };
269515
270396
  let closed = false;
269516
- const modernClose = modern.close.bind(modern);
269517
- modern.close = async () => {
270397
+ return {
270398
+ projectId: legacy.projectId,
270399
+ getProjectDataDir: legacy.getProjectDataDir,
270400
+ createBridge: () => {
270401
+ const requestServer = new ModernMcpServer(getMcpServerInfo());
270402
+ for (const tool of listed.tools) {
270403
+ requestServer.registerTool(
270404
+ tool.name,
270405
+ {
270406
+ ...tool.title ? { title: tool.title } : {},
270407
+ ...tool.description ? { description: tool.description } : {},
270408
+ inputSchema: fromJsonSchema(tool.inputSchema)
270409
+ },
270410
+ async (args) => await callTool(tool.name, args)
270411
+ );
270412
+ }
270413
+ requestServer.memorix = lifecycle;
270414
+ return requestServer;
270415
+ },
270416
+ close: async () => {
270417
+ if (closed) return;
270418
+ closed = true;
270419
+ await legacyClient.close().catch(() => void 0);
270420
+ await legacy.server.close().catch(() => void 0);
270421
+ legacy.handleTransportClose();
270422
+ }
270423
+ };
270424
+ }
270425
+ async function createModernMcpBridge(options) {
270426
+ const runtime = await createModernMcpRuntime(options);
270427
+ const bridge = runtime.createBridge();
270428
+ const close = bridge.close.bind(bridge);
270429
+ let closed = false;
270430
+ bridge.close = async () => {
269518
270431
  if (closed) return;
269519
270432
  closed = true;
269520
- await modernClose();
269521
- await legacyClient.close().catch(() => void 0);
269522
- await legacy.server.close().catch(() => void 0);
269523
- legacy.handleTransportClose();
270433
+ await close();
270434
+ await runtime.close();
269524
270435
  };
269525
270436
  return bridge;
269526
270437
  }
270438
+ var businessRuntimeQueue, ModernMcpRuntimePool;
269527
270439
  var init_modern_mcp_bridge = __esm({
269528
270440
  "src/server/modern-mcp-bridge.ts"() {
269529
270441
  "use strict";
@@ -269531,6 +270443,74 @@ var init_modern_mcp_bridge = __esm({
269531
270443
  init_client4();
269532
270444
  init_inMemory();
269533
270445
  init_mcp_discovery();
270446
+ businessRuntimeQueue = Promise.resolve();
270447
+ ModernMcpRuntimePool = class {
270448
+ constructor(maxEntries = 32) {
270449
+ this.maxEntries = maxEntries;
270450
+ }
270451
+ maxEntries;
270452
+ entries = /* @__PURE__ */ new Map();
270453
+ pending = /* @__PURE__ */ new Map();
270454
+ async createBridge(key, create6) {
270455
+ let entry = this.entries.get(key);
270456
+ if (!entry) {
270457
+ let pending = this.pending.get(key);
270458
+ if (!pending) {
270459
+ pending = create6().then((runtime) => {
270460
+ const created = { runtime, refs: 0, lastUsedAt: Date.now() };
270461
+ this.entries.set(key, created);
270462
+ return created;
270463
+ });
270464
+ this.pending.set(key, pending);
270465
+ }
270466
+ try {
270467
+ entry = await pending;
270468
+ } finally {
270469
+ this.pending.delete(key);
270470
+ }
270471
+ }
270472
+ entry.refs++;
270473
+ entry.lastUsedAt = Date.now();
270474
+ const bridge = entry.runtime.createBridge();
270475
+ const close = bridge.close.bind(bridge);
270476
+ let released = false;
270477
+ bridge.close = async () => {
270478
+ try {
270479
+ await close();
270480
+ } finally {
270481
+ if (!released) {
270482
+ released = true;
270483
+ entry.refs = Math.max(0, entry.refs - 1);
270484
+ entry.lastUsedAt = Date.now();
270485
+ }
270486
+ }
270487
+ };
270488
+ return bridge;
270489
+ }
270490
+ async evictIdle() {
270491
+ let closed = 0;
270492
+ const idle = [...this.entries.entries()].filter(([, entry]) => entry.refs === 0).sort(([, left], [, right]) => left.lastUsedAt - right.lastUsedAt);
270493
+ for (const [key, entry] of idle) {
270494
+ if (this.entries.size <= this.maxEntries || entry.refs > 0) continue;
270495
+ this.entries.delete(key);
270496
+ await entry.runtime.close();
270497
+ closed++;
270498
+ }
270499
+ return closed;
270500
+ }
270501
+ async close() {
270502
+ const entries = [...this.entries.values()];
270503
+ this.entries.clear();
270504
+ await Promise.all(entries.map((entry) => entry.runtime.close().catch(() => void 0)));
270505
+ }
270506
+ stats() {
270507
+ return {
270508
+ entries: this.entries.size,
270509
+ activeRefs: [...this.entries.values()].reduce((sum, entry) => sum + entry.refs, 0),
270510
+ pending: this.pending.size
270511
+ };
270512
+ }
270513
+ };
269534
270514
  }
269535
270515
  });
269536
270516
 
@@ -270159,7 +271139,7 @@ function writeFromReadableStream(stream2, writable) {
270159
271139
  else if (writable.destroyed) return;
270160
271140
  return writeFromReadableStreamDefaultReader(stream2.getReader(), writable);
270161
271141
  }
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;
271142
+ 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
271143
  var init_dist8 = __esm({
270164
271144
  "node_modules/@hono/node-server/dist/index.mjs"() {
270165
271145
  "use strict";
@@ -270630,15 +271610,15 @@ var init_dist8 = __esm({
270630
271610
  defaultContentType = "text/plain; charset=UTF-8";
270631
271611
  responseCache = /* @__PURE__ */ Symbol("responseCache");
270632
271612
  getResponseCache = /* @__PURE__ */ Symbol("getResponseCache");
270633
- cacheKey = /* @__PURE__ */ Symbol("cache");
271613
+ cacheKey3 = /* @__PURE__ */ Symbol("cache");
270634
271614
  GlobalResponse = global.Response;
270635
271615
  Response$1 = class Response$12 {
270636
271616
  #body;
270637
271617
  #init;
270638
271618
  [getResponseCache]() {
270639
- const cache5 = this[cacheKey];
271619
+ const cache5 = this[cacheKey3];
270640
271620
  const liveHeaders = cache5 && cache5[2] instanceof Headers ? cache5[2] : void 0;
270641
- delete this[cacheKey];
271621
+ delete this[cacheKey3];
270642
271622
  return this[responseCache] ||= new GlobalResponse(this.#body, liveHeaders ? {
270643
271623
  status: this.#init?.status,
270644
271624
  statusText: this.#init?.statusText,
@@ -270658,14 +271638,14 @@ var init_dist8 = __esm({
270658
271638
  this.#init = init2 instanceof Response$12 ? init2.#init : init2;
270659
271639
  headers = new Headers(init2.headers);
270660
271640
  } else this.#init = init2;
270661
- if (body == null || typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey] = [
271641
+ if (body == null || typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey3] = [
270662
271642
  init2?.status || 200,
270663
271643
  body ?? null,
270664
271644
  headers || init2?.headers
270665
271645
  ];
270666
271646
  }
270667
271647
  get headers() {
270668
- const cache5 = this[cacheKey];
271648
+ const cache5 = this[cacheKey3];
270669
271649
  if (cache5) {
270670
271650
  if (!(cache5[2] instanceof Headers)) cache5[2] = new Headers(cache5[2] || (cache5[1] === null ? void 0 : { "content-type": defaultContentType }));
270671
271651
  return cache5[2];
@@ -270673,7 +271653,7 @@ var init_dist8 = __esm({
270673
271653
  return this[getResponseCache]().headers;
270674
271654
  }
270675
271655
  get status() {
270676
- return this[cacheKey]?.[0] ?? this[getResponseCache]().status;
271656
+ return this[cacheKey3]?.[0] ?? this[getResponseCache]().status;
270677
271657
  }
270678
271658
  get ok() {
270679
271659
  const status = this.status;
@@ -270826,8 +271806,8 @@ var init_dist8 = __esm({
270826
271806
  });
270827
271807
  };
270828
271808
  isImmediateCacheableResponse = (res) => {
270829
- if (!(cacheKey in res)) return false;
270830
- const body = res[cacheKey][1];
271809
+ if (!(cacheKey3 in res)) return false;
271810
+ const body = res[cacheKey3][1];
270831
271811
  return body === null || typeof body === "string" || body instanceof Uint8Array;
270832
271812
  };
270833
271813
  handleRequestError = () => new Response(null, { status: 400 });
@@ -270846,7 +271826,7 @@ var init_dist8 = __esm({
270846
271826
  if ("flushHeaders" in outgoing && outgoing.writable) outgoing.flushHeaders();
270847
271827
  };
270848
271828
  responseViaCache = async (res, outgoing) => {
270849
- let [status, body, header] = res[cacheKey];
271829
+ let [status, body, header] = res[cacheKey3];
270850
271830
  if (!header) {
270851
271831
  if (body === null) {
270852
271832
  outgoing.writeHead(status);
@@ -270914,7 +271894,7 @@ var init_dist8 = __esm({
270914
271894
  res = errRes;
270915
271895
  }
270916
271896
  else res = await res.catch(handleFetchError);
270917
- if (cacheKey in res) return responseViaCache(res, outgoing);
271897
+ if (cacheKey3 in res) return responseViaCache(res, outgoing);
270918
271898
  const resHeaderRecord = buildOutgoingHttpHeaders(res.headers, res.body === null ? void 0 : defaultContentType);
270919
271899
  if (res.body) {
270920
271900
  const reader2 = res.body.getReader();
@@ -272151,7 +273131,7 @@ __export(mcp_binding_store_exports, {
272151
273131
  DEFAULT_MCP_BINDING_TTL_MS: () => DEFAULT_MCP_BINDING_TTL_MS,
272152
273132
  McpBindingStore: () => McpBindingStore
272153
273133
  });
272154
- import { randomUUID as randomUUID19 } from "crypto";
273134
+ import { randomUUID as randomUUID20 } from "crypto";
272155
273135
  function rowToBinding(row) {
272156
273136
  return {
272157
273137
  handleId: String(row.handle_id),
@@ -272183,7 +273163,7 @@ var init_mcp_binding_store = __esm({
272183
273163
  const now3 = /* @__PURE__ */ new Date();
272184
273164
  const ttlMs = input.ttlMs ?? DEFAULT_MCP_BINDING_TTL_MS;
272185
273165
  const binding = {
272186
- handleId: `mxh_${randomUUID19().replace(/-/g, "")}`,
273166
+ handleId: `mxh_${randomUUID20().replace(/-/g, "")}`,
272187
273167
  projectId: input.projectId,
272188
273168
  projectRoot: input.projectRoot,
272189
273169
  dataDir: input.dataDir,
@@ -272310,10 +273290,14 @@ var init_control_plane_maintenance = __esm({
272310
273290
  var serve_http_exports = {};
272311
273291
  __export(serve_http_exports, {
272312
273292
  DEFAULT_HTTP_BODY_LIMIT_BYTES: () => DEFAULT_HTTP_BODY_LIMIT_BYTES,
273293
+ DEFAULT_MAX_HTTP_SESSIONS: () => DEFAULT_MAX_HTTP_SESSIONS,
272313
273294
  DEFAULT_SESSION_TIMEOUT_MS: () => DEFAULT_SESSION_TIMEOUT_MS,
272314
273295
  EXPIRED_SESSION_TTL_MS: () => EXPIRED_SESSION_TTL_MS,
272315
273296
  HttpPayloadTooLargeError: () => HttpPayloadTooLargeError,
272316
273297
  MAX_HTTP_BODY_LIMIT_BYTES: () => MAX_HTTP_BODY_LIMIT_BYTES,
273298
+ MAX_HTTP_SESSIONS_LIMIT: () => MAX_HTTP_SESSIONS_LIMIT,
273299
+ MAX_PROJECT_CACHE_ENTRIES: () => MAX_PROJECT_CACHE_ENTRIES,
273300
+ MAX_STORE_CACHE_ENTRIES: () => MAX_STORE_CACHE_ENTRIES,
272317
273301
  MCP_SDK_COMPAT_PROTOCOL_VERSION: () => MCP_SDK_COMPAT_PROTOCOL_VERSION,
272318
273302
  MIN_HTTP_BODY_LIMIT_BYTES: () => MIN_HTTP_BODY_LIMIT_BYTES,
272319
273303
  _testing: () => _testing2,
@@ -272322,11 +273306,13 @@ __export(serve_http_exports, {
272322
273306
  isServerDiscoverRequest: () => isServerDiscoverRequest,
272323
273307
  parseHttpBodyLimit: () => parseHttpBodyLimit,
272324
273308
  parseJsonBody: () => parseJsonBody,
273309
+ parseMaxHttpSessions: () => parseMaxHttpSessions,
272325
273310
  parseSessionTimeoutMs: () => parseSessionTimeoutMs,
272326
273311
  resolveServeHttpDefaults: () => resolveServeHttpDefaults,
272327
273312
  validateHttpBindSecurity: () => validateHttpBindSecurity
272328
273313
  });
272329
273314
  import { getHeapStatistics } from "v8";
273315
+ import { monitorEventLoopDelay } from "perf_hooks";
272330
273316
  import { timingSafeEqual as timingSafeEqual2 } from "crypto";
272331
273317
  function isServerDiscoverRequest(body) {
272332
273318
  return Boolean(body && typeof body === "object" && !Array.isArray(body) && body.method === "server/discover");
@@ -272347,6 +273333,13 @@ function parseHttpBodyLimit(raw) {
272347
273333
  }
272348
273334
  return Math.min(parsed, MAX_HTTP_BODY_LIMIT_BYTES);
272349
273335
  }
273336
+ function parseMaxHttpSessions(raw) {
273337
+ const value = raw?.trim();
273338
+ if (!value) return DEFAULT_MAX_HTTP_SESSIONS;
273339
+ const parsed = Number(value);
273340
+ if (!Number.isSafeInteger(parsed) || parsed < 1) return DEFAULT_MAX_HTTP_SESSIONS;
273341
+ return Math.min(parsed, MAX_HTTP_SESSIONS_LIMIT);
273342
+ }
272350
273343
  function isLoopbackHost(host) {
272351
273344
  const normalized = host.trim().toLowerCase();
272352
273345
  return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1" || normalized === "[::1]";
@@ -272419,7 +273412,7 @@ function resolveServeHttpDefaults(argsPort, configured) {
272419
273412
  const transportNotice = configured.transport === "stdio" ? '[memorix] config server.transport = "stdio"; serve-http continues on HTTP (the explicit command wins).' : void 0;
272420
273413
  return { port, transportNotice };
272421
273414
  }
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;
273415
+ 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
273416
  var init_serve_http = __esm({
272424
273417
  "src/cli/commands/serve-http.ts"() {
272425
273418
  "use strict";
@@ -272438,6 +273431,10 @@ var init_serve_http = __esm({
272438
273431
  DEFAULT_HTTP_BODY_LIMIT_BYTES = 10 * 1024 * 1024;
272439
273432
  MAX_HTTP_BODY_LIMIT_BYTES = 64 * 1024 * 1024;
272440
273433
  MIN_HTTP_BODY_LIMIT_BYTES = 64 * 1024;
273434
+ MAX_PROJECT_CACHE_ENTRIES = 256;
273435
+ MAX_STORE_CACHE_ENTRIES = 32;
273436
+ DEFAULT_MAX_HTTP_SESSIONS = 64;
273437
+ MAX_HTTP_SESSIONS_LIMIT = 512;
272441
273438
  MCP_SDK_COMPAT_PROTOCOL_VERSION = "2025-11-25";
272442
273439
  HttpPayloadTooLargeError = class extends Error {
272443
273440
  statusCode = 413;
@@ -272475,7 +273472,7 @@ var init_serve_http = __esm({
272475
273472
  },
272476
273473
  run: async ({ args }) => {
272477
273474
  const { createServer: createServer3 } = await import("http");
272478
- const { randomUUID: randomUUID21 } = await import("crypto");
273475
+ const { randomUUID: randomUUID23 } = await import("crypto");
272479
273476
  const { StreamableHTTPServerTransport: StreamableHTTPServerTransport2 } = await Promise.resolve().then(() => (init_streamableHttp(), streamableHttp_exports));
272480
273477
  const { isInitializeRequest: isInitializeRequest2 } = await Promise.resolve().then(() => (init_types8(), types_exports3));
272481
273478
  const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server4(), server_exports2));
@@ -272485,7 +273482,8 @@ var init_serve_http = __esm({
272485
273482
  const { homedir: homedir38 } = await import("os");
272486
273483
  const { McpBindingStore: McpBindingStore2 } = await Promise.resolve().then(() => (init_mcp_binding_store(), mcp_binding_store_exports));
272487
273484
  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));
273485
+ const { createModernMcpRuntime: createModernMcpRuntime2, ModernMcpRuntimePool: ModernMcpRuntimePool2, withBusinessRuntimeQueue: withBusinessRuntimeQueue2 } = await Promise.resolve().then(() => (init_modern_mcp_bridge(), modern_mcp_bridge_exports));
273486
+ const { acquireDatabase: acquireDatabase2, evictIdleDatabases: evictIdleDatabases2, getDatabaseStats: getDatabaseStats2 } = await Promise.resolve().then(() => (init_sqlite_db(), sqlite_db_exports));
272489
273487
  const { createMcpHandler } = await import("@modelcontextprotocol/server");
272490
273488
  const { toNodeHandler } = await import("@modelcontextprotocol/node");
272491
273489
  let configuredDefaults = {};
@@ -272537,42 +273535,108 @@ var init_serve_http = __esm({
272537
273535
  console.error(`[memorix] Project root: ${projectRoot}`);
272538
273536
  const sessionTimeoutMs = parseSessionTimeoutMs(process.env.MEMORIX_SESSION_TIMEOUT_MS);
272539
273537
  const httpBodyLimitBytes = parseHttpBodyLimit(process.env.MEMORIX_HTTP_MAX_BODY_BYTES);
273538
+ const maxHttpSessions = parseMaxHttpSessions(process.env.MEMORIX_MAX_HTTP_SESSIONS);
273539
+ const eventLoopDelay = monitorEventLoopDelay({ resolution: 20 });
273540
+ eventLoopDelay.enable();
272540
273541
  console.error(
272541
273542
  sessionTimeoutMs === 0 ? "[memorix] HTTP session idle timeout: disabled" : `[memorix] HTTP session idle timeout: ${Math.round(sessionTimeoutMs / 6e4)}min (${sessionTimeoutMs}ms)`
272542
273543
  );
273544
+ console.error(`[memorix] HTTP session limit: ${maxHttpSessions}`);
272543
273545
  const teamStoreCache = /* @__PURE__ */ new Map();
273546
+ const teamStoreLeases = /* @__PURE__ */ new Map();
272544
273547
  const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
273548
+ function cacheSet3(cache5, key, value, limit2, onEvict) {
273549
+ cache5.delete(key);
273550
+ cache5.set(key, value);
273551
+ while (cache5.size > limit2) {
273552
+ const oldest = cache5.keys().next().value;
273553
+ if (oldest === void 0) break;
273554
+ const oldValue = cache5.get(oldest);
273555
+ cache5.delete(oldest);
273556
+ if (oldValue !== void 0) onEvict?.(oldest, oldValue);
273557
+ }
273558
+ }
272545
273559
  async function getTeamStore2(dataDir) {
272546
273560
  const existing = teamStoreCache.get(dataDir);
272547
- if (existing) return existing;
272548
- const store2 = await initTeamStore2(dataDir);
272549
- teamStoreCache.set(dataDir, store2);
272550
- return store2;
273561
+ if (existing) {
273562
+ cacheSet3(teamStoreCache, dataDir, existing, MAX_STORE_CACHE_ENTRIES);
273563
+ return existing;
273564
+ }
273565
+ const lease = acquireDatabase2(dataDir);
273566
+ try {
273567
+ const store2 = await initTeamStore2(dataDir);
273568
+ teamStoreLeases.set(dataDir, lease);
273569
+ cacheSet3(teamStoreCache, dataDir, store2, MAX_STORE_CACHE_ENTRIES, (evictedDataDir) => {
273570
+ teamStoreLeases.get(evictedDataDir)?.release();
273571
+ teamStoreLeases.delete(evictedDataDir);
273572
+ evictIdleDatabases2(MAX_STORE_CACHE_ENTRIES);
273573
+ });
273574
+ return store2;
273575
+ } catch (error40) {
273576
+ lease.release();
273577
+ throw error40;
273578
+ }
272551
273579
  }
273580
+ const controlPlaneBaseDir = persistMod.getBaseDataDir();
273581
+ const controlPlaneBaseLease = acquireDatabase2(controlPlaneBaseDir);
272552
273582
  const statelessBindingStore = new McpBindingStore2();
272553
- await statelessBindingStore.init(persistMod.getBaseDataDir());
273583
+ await statelessBindingStore.init(controlPlaneBaseDir);
273584
+ const modernRuntimePool = new ModernMcpRuntimePool2(MAX_STORE_CACHE_ENTRIES);
273585
+ async function resolveModernDataDir(root, persisted) {
273586
+ if (persisted?.dataDir) return persisted.dataDir;
273587
+ const detected = detectorMod.detectProject(root);
273588
+ return detected ? persistMod.getProjectDataDir(detected.id) : controlPlaneBaseDir;
273589
+ }
272554
273590
  const modernMcpHandler = createMcpHandler(async ({ requestInfo }) => {
272555
273591
  const handleId = requestInfo?.headers.get("mcp-project-handle") ?? void 0;
272556
273592
  const persisted = handleId ? statelessBindingStore.touch(handleId) : void 0;
272557
273593
  const requestedRoot = requestInfo?.headers.get("mcp-project-root") ?? void 0;
272558
273594
  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
- });
273595
+ const dataDir = await resolveModernDataDir(root, persisted);
273596
+ const runtimeKey = JSON.stringify([dataDir, root]);
273597
+ return modernRuntimePool.createBridge(runtimeKey, () => withBusinessRuntimeQueue2(async () => {
273598
+ const lease = acquireDatabase2(dataDir);
273599
+ try {
273600
+ const runtime = await createModernMcpRuntime2({
273601
+ projectRoot: root,
273602
+ allowUntrackedFallback: false,
273603
+ deferProjectInitUntilBound: true,
273604
+ deferProjectRuntimeInit: true,
273605
+ dashboardMode: "control-plane",
273606
+ dashboardPort: port,
273607
+ toolProfile
273608
+ });
273609
+ const close = runtime.close.bind(runtime);
273610
+ return {
273611
+ ...runtime,
273612
+ close: async () => {
273613
+ try {
273614
+ await close();
273615
+ } finally {
273616
+ lease.release();
273617
+ }
273618
+ }
273619
+ };
273620
+ } catch (error40) {
273621
+ lease.release();
273622
+ throw error40;
273623
+ }
273624
+ }));
272568
273625
  }, {
272569
273626
  // Legacy requests are routed to the existing sessionful implementation
272570
273627
  // below; this handler is strict modern-only by construction.
272571
273628
  legacy: "reject",
272572
- responseMode: "auto"
273629
+ responseMode: "auto",
273630
+ onerror: (error40) => {
273631
+ const message = error40 instanceof Error ? error40.message : String(error40);
273632
+ const stack = error40 instanceof Error ? error40.stack : void 0;
273633
+ console.error(`[memorix] modern MCP request failed: ${message}${stack ? `
273634
+ ${stack}` : ""}`);
273635
+ }
272573
273636
  });
272574
273637
  const modernNodeHandler = toNodeHandler(modernMcpHandler);
272575
273638
  const sessions = /* @__PURE__ */ new Map();
273639
+ let pendingSessionInitializations = 0;
272576
273640
  const sessionLastActivity = /* @__PURE__ */ new Map();
272577
273641
  const expiredSessions = /* @__PURE__ */ new Map();
272578
273642
  let suppressedProbeCount = 0;
@@ -272747,7 +273811,11 @@ var init_serve_http = __esm({
272747
273811
  if (sessionId && sessions.has(sessionId)) {
272748
273812
  sessionLastActivity.set(sessionId, Date.now());
272749
273813
  patchMcpResponse(res);
272750
- await sessions.get(sessionId).transport.handleRequest(req, res, body);
273814
+ const session = sessions.get(sessionId);
273815
+ await withBusinessRuntimeQueue2(async () => {
273816
+ await session.activateProjectRuntime();
273817
+ await session.transport.handleRequest(req, res, body);
273818
+ });
272751
273819
  return;
272752
273820
  }
272753
273821
  if (sessionId) {
@@ -272755,11 +273823,32 @@ var init_serve_http = __esm({
272755
273823
  return;
272756
273824
  }
272757
273825
  if (!sessionId && isInitializeRequest2(body)) {
273826
+ if (sessions.size + pendingSessionInitializations >= maxHttpSessions) {
273827
+ res.writeHead(429, {
273828
+ "Content-Type": "application/json",
273829
+ "Retry-After": "5"
273830
+ });
273831
+ res.end(JSON.stringify({
273832
+ jsonrpc: "2.0",
273833
+ error: { code: -32029, message: `HTTP session limit reached (${maxHttpSessions}). Retry after an existing session closes.` },
273834
+ id: body.id ?? null
273835
+ }));
273836
+ return;
273837
+ }
273838
+ pendingSessionInitializations++;
273839
+ let pendingReservationReleased = false;
273840
+ const releasePendingReservation = () => {
273841
+ if (pendingReservationReleased) return;
273842
+ pendingReservationReleased = true;
273843
+ pendingSessionInitializations = Math.max(0, pendingSessionInitializations - 1);
273844
+ };
272758
273845
  let createdState = null;
273846
+ const sessionLeases = [acquireDatabase2(defaultDataDir)];
272759
273847
  const transport = new StreamableHTTPServerTransport2({
272760
- sessionIdGenerator: () => randomUUID21(),
273848
+ sessionIdGenerator: () => randomUUID23(),
272761
273849
  enableJsonResponse: true,
272762
273850
  onsessioninitialized: (sid) => {
273851
+ releasePendingReservation();
272763
273852
  if (createdState) sessions.set(sid, createdState);
272764
273853
  sessionLastActivity.set(sid, Date.now());
272765
273854
  const timer = setTimeout(() => {
@@ -272770,6 +273859,7 @@ var init_serve_http = __esm({
272770
273859
  }
272771
273860
  });
272772
273861
  transport.onclose = () => {
273862
+ releasePendingReservation();
272773
273863
  const sid = transport.sessionId;
272774
273864
  if (sid) {
272775
273865
  const pending = pendingLogTimers.get(sid);
@@ -272785,26 +273875,49 @@ var init_serve_http = __esm({
272785
273875
  sessionLastActivity.delete(sid);
272786
273876
  }
272787
273877
  handleTransportClose();
273878
+ for (const lease of sessionLeases) lease.release();
273879
+ evictIdleDatabases2(MAX_STORE_CACHE_ENTRIES);
272788
273880
  };
272789
273881
  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
273882
+ let server;
273883
+ let activateProjectRuntime;
273884
+ let handleTransportClose;
273885
+ let getProjectDataDirForSession;
273886
+ let switchProject;
273887
+ try {
273888
+ ({ server, activateProjectRuntime, switchProject, handleTransportClose, getProjectDataDir: getProjectDataDirForSession } = await createMemorixServer2(
273889
+ projectRoot,
273890
+ void 0,
273891
+ void 0,
273892
+ {
273893
+ allowUntrackedFallback: false,
273894
+ deferProjectInitUntilBound: true,
273895
+ // Same as stdio: initialize/tools/list must not hydrate 40k+
273896
+ // observations on the HTTP thread. /health and the LaunchAgent
273897
+ // watchdog die if createMemorixServer blocks here.
273898
+ deferProjectRuntimeInit: true,
273899
+ dashboardMode: "control-plane",
273900
+ dashboardPort: port,
273901
+ toolProfile,
273902
+ projectBinding: binding
273903
+ }
273904
+ ));
273905
+ } catch (error40) {
273906
+ releasePendingReservation();
273907
+ for (const lease of sessionLeases) lease.release();
273908
+ throw error40;
273909
+ }
273910
+ const switchProjectWithLease = async (newCwd, source) => {
273911
+ const before = getProjectDataDirForSession();
273912
+ const changed = await switchProject(newCwd, source);
273913
+ const after = getProjectDataDirForSession();
273914
+ if (changed && after !== before) {
273915
+ sessionLeases[0].release();
273916
+ sessionLeases[0] = acquireDatabase2(after);
272805
273917
  }
272806
- );
272807
- createdState = { transport, server, switchProject, binding };
273918
+ return changed;
273919
+ };
273920
+ createdState = { transport, server, activateProjectRuntime, switchProject: switchProjectWithLease, binding, leases: sessionLeases };
272808
273921
  await server.connect(transport);
272809
273922
  const tryRootsSwitch = async () => {
272810
273923
  try {
@@ -272843,7 +273956,14 @@ var init_serve_http = __esm({
272843
273956
  } catch {
272844
273957
  }
272845
273958
  patchMcpResponse(res);
272846
- await transport.handleRequest(req, res, body);
273959
+ try {
273960
+ await withBusinessRuntimeQueue2(async () => {
273961
+ await activateProjectRuntime();
273962
+ await transport.handleRequest(req, res, body);
273963
+ });
273964
+ } finally {
273965
+ if (!transport.sessionId) releasePendingReservation();
273966
+ }
272847
273967
  return;
272848
273968
  }
272849
273969
  res.writeHead(400, { "Content-Type": "application/json" });
@@ -272877,7 +273997,7 @@ var init_serve_http = __esm({
272877
273997
  sendUnknownSession(res, sessionId);
272878
273998
  return;
272879
273999
  }
272880
- await sessions.get(sessionId).transport.handleRequest(req, res);
274000
+ await withBusinessRuntimeQueue2(() => sessions.get(sessionId).transport.handleRequest(req, res));
272881
274001
  }
272882
274002
  const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
272883
274003
  const { getProjectDataDir: getProjectDataDir2, getBaseDataDir: getBaseDataDir2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
@@ -272898,8 +274018,9 @@ var init_serve_http = __esm({
272898
274018
  });
272899
274019
  controlPlaneMaintenanceWorker.start();
272900
274020
  const projectDataDirCache = /* @__PURE__ */ new Map();
272901
- projectDataDirCache.set(defaultProject.id, defaultDataDir);
274021
+ cacheSet3(projectDataDirCache, defaultProject.id, defaultDataDir, MAX_PROJECT_CACHE_ENTRIES);
272902
274022
  const dashboardObservationStores = /* @__PURE__ */ new Map();
274023
+ const dashboardStoreLeases = /* @__PURE__ */ new Map();
272903
274024
  async function resolveRequestProject(url2) {
272904
274025
  const requestedId = url2.searchParams.get("project");
272905
274026
  if (!requestedId || requestedId === defaultProject.id) {
@@ -272908,18 +274029,36 @@ var init_serve_http = __esm({
272908
274029
  let dataDir = projectDataDirCache.get(requestedId);
272909
274030
  if (!dataDir) {
272910
274031
  dataDir = await getProjectDataDir2(requestedId);
272911
- projectDataDirCache.set(requestedId, dataDir);
274032
+ cacheSet3(projectDataDirCache, requestedId, dataDir, MAX_PROJECT_CACHE_ENTRIES);
272912
274033
  }
272913
274034
  const name = requestedId.split("/").pop() || requestedId;
272914
274035
  return { projectId: requestedId, projectName: name, dataDir };
272915
274036
  }
272916
274037
  async function getDashboardObservationStore(dataDir) {
272917
274038
  const cached2 = dashboardObservationStores.get(dataDir);
272918
- if (cached2) return cached2;
274039
+ if (cached2) {
274040
+ cacheSet3(dashboardObservationStores, dataDir, cached2, MAX_STORE_CACHE_ENTRIES);
274041
+ return cached2;
274042
+ }
272919
274043
  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;
274044
+ const lease = acquireDatabase2(dataDir);
274045
+ try {
274046
+ const store2 = await createObservationStore2(dataDir);
274047
+ dashboardStoreLeases.set(dataDir, lease);
274048
+ cacheSet3(dashboardObservationStores, dataDir, store2, MAX_STORE_CACHE_ENTRIES, (evictedDataDir, evictedStore) => {
274049
+ try {
274050
+ evictedStore.close();
274051
+ } catch {
274052
+ }
274053
+ dashboardStoreLeases.get(evictedDataDir)?.release();
274054
+ dashboardStoreLeases.delete(evictedDataDir);
274055
+ evictIdleDatabases2(MAX_STORE_CACHE_ENTRIES);
274056
+ });
274057
+ return store2;
274058
+ } catch (error40) {
274059
+ lease.release();
274060
+ throw error40;
274061
+ }
272923
274062
  }
272924
274063
  async function loadDashboardObservations(dataDir) {
272925
274064
  const store2 = await getDashboardObservationStore(dataDir);
@@ -273749,6 +274888,18 @@ var init_serve_http = __esm({
273749
274888
  arrayBuffers: memory.arrayBuffers,
273750
274889
  heapLimit: heap.heap_size_limit
273751
274890
  },
274891
+ sqlite: getDatabaseStats2(),
274892
+ sessions: {
274893
+ active: sessions.size,
274894
+ pending: pendingSessionInitializations,
274895
+ limit: maxHttpSessions
274896
+ },
274897
+ modernRuntime: modernRuntimePool.stats(),
274898
+ eventLoop: {
274899
+ meanMs: Number.isFinite(eventLoopDelay.mean) ? eventLoopDelay.mean / 1e6 : null,
274900
+ p95Ms: eventLoopDelay.percentile(95) / 1e6,
274901
+ maxMs: eventLoopDelay.max / 1e6
274902
+ },
273752
274903
  embedding: getEmbeddingRuntimeHealth2()
273753
274904
  }));
273754
274905
  return;
@@ -273865,6 +275016,13 @@ var init_serve_http = __esm({
273865
275016
  }
273866
275017
  }
273867
275018
  dashboardObservationStores.clear();
275019
+ for (const lease of dashboardStoreLeases.values()) lease.release();
275020
+ dashboardStoreLeases.clear();
275021
+ for (const lease of teamStoreLeases.values()) lease.release();
275022
+ teamStoreLeases.clear();
275023
+ controlPlaneBaseLease.release();
275024
+ await modernRuntimePool.close();
275025
+ eventLoopDelay.disable();
273868
275026
  const { closeAllDatabases: closeAllDatabases2 } = await Promise.resolve().then(() => (init_sqlite_db(), sqlite_db_exports));
273869
275027
  closeAllDatabases2();
273870
275028
  throw error40;
@@ -273875,6 +275033,8 @@ var init_serve_http = __esm({
273875
275033
  const now3 = Date.now();
273876
275034
  forgetExpiredSessions(now3);
273877
275035
  statelessBindingStore.cleanupExpired(now3);
275036
+ evictIdleDatabases2(MAX_STORE_CACHE_ENTRIES);
275037
+ void modernRuntimePool.evictIdle();
273878
275038
  if (SESSION_TIMEOUT_MS === 0) return;
273879
275039
  for (const [sid, state] of sessions) {
273880
275040
  const lastActive = sessionLastActivity.get(sid) ?? 0;
@@ -273918,6 +275078,10 @@ var init_serve_http = __esm({
273918
275078
  }
273919
275079
  }
273920
275080
  dashboardObservationStores.clear();
275081
+ for (const lease of dashboardStoreLeases.values()) lease.release();
275082
+ dashboardStoreLeases.clear();
275083
+ for (const lease of teamStoreLeases.values()) lease.release();
275084
+ teamStoreLeases.clear();
273921
275085
  for (const [sid, state] of sessions) {
273922
275086
  try {
273923
275087
  await state.transport.close();
@@ -273926,8 +275090,10 @@ var init_serve_http = __esm({
273926
275090
  console.error(`[memorix] Error closing session ${sid}:`, err);
273927
275091
  }
273928
275092
  }
275093
+ await modernRuntimePool.close();
273929
275094
  clearInterval(gcInterval);
273930
275095
  clearInterval(heartbeatInterval);
275096
+ eventLoopDelay.disable();
273931
275097
  await new Promise((resolve9) => {
273932
275098
  if (!httpServer.listening) {
273933
275099
  resolve9();
@@ -273936,6 +275102,7 @@ var init_serve_http = __esm({
273936
275102
  httpServer.close(() => resolve9());
273937
275103
  });
273938
275104
  const { closeAllDatabases: closeAllDatabases2 } = await Promise.resolve().then(() => (init_sqlite_db(), sqlite_db_exports));
275105
+ controlPlaneBaseLease.release();
273939
275106
  closeAllDatabases2();
273940
275107
  process.exit(0);
273941
275108
  };
@@ -273979,6 +275146,7 @@ var init_serve_http = __esm({
273979
275146
  MAX_HTTP_BODY_LIMIT_BYTES,
273980
275147
  MIN_HTTP_BODY_LIMIT_BYTES,
273981
275148
  parseSessionTimeoutMs,
275149
+ parseMaxHttpSessions,
273982
275150
  parseHttpBodyLimit,
273983
275151
  isLoopbackHost,
273984
275152
  validateHttpBindSecurity,
@@ -274328,7 +275496,7 @@ var init_merge = __esm({
274328
275496
  });
274329
275497
 
274330
275498
  // src/sync/journal.ts
274331
- import { createHash as createHash21 } from "crypto";
275499
+ import { createHash as createHash22 } from "crypto";
274332
275500
  function nowIso() {
274333
275501
  return (/* @__PURE__ */ new Date()).toISOString();
274334
275502
  }
@@ -274340,7 +275508,7 @@ function deriveSyncKey(obs, originDevice) {
274340
275508
  function contentHash(obs) {
274341
275509
  const { id: _id, writeGeneration: _wg, ...rest } = obs;
274342
275510
  const stable = stableJson(rest);
274343
- return createHash21("sha256").update(stable).digest("hex");
275511
+ return createHash22("sha256").update(stable).digest("hex");
274344
275512
  }
274345
275513
  function stableJson(value) {
274346
275514
  if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
@@ -274463,9 +275631,9 @@ var init_policy2 = __esm({
274463
275631
  });
274464
275632
 
274465
275633
  // src/sync/namespace.ts
274466
- import { createHash as createHash22 } from "crypto";
275634
+ import { createHash as createHash23 } from "crypto";
274467
275635
  function syncNamespace(projectId) {
274468
- return `project-${createHash22("sha256").update(projectId).digest("hex").slice(0, 32)}`;
275636
+ return `project-${createHash23("sha256").update(projectId).digest("hex").slice(0, 32)}`;
274469
275637
  }
274470
275638
  function userSyncNamespace(env3 = process.env) {
274471
275639
  const override = env3.MEMORIX_SYNC_USER_NAMESPACE?.trim();
@@ -274491,7 +275659,7 @@ var init_namespace = __esm({
274491
275659
  });
274492
275660
 
274493
275661
  // src/sync/engine.ts
274494
- import { createHash as createHash23 } from "crypto";
275662
+ import { createHash as createHash24 } from "crypto";
274495
275663
  async function runSync(store2, remote, opts) {
274496
275664
  const report = {
274497
275665
  remote: remote.kind,
@@ -274695,7 +275863,7 @@ async function recordConcurrentConflict(store2, report, local, incoming, localRo
274695
275863
  const incomingWins = compareVersion(incoming.version, { revision: local.revision, writer: local.writer }) > 0;
274696
275864
  const loser = incomingWins ? { revision: local.revision, writer: local.writer } : incoming.version;
274697
275865
  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");
275866
+ const id = createHash24("sha256").update(`${store2.projectId()}|${incoming.syncKey}|${local.revision}|${local.writer}|${incoming.version.revision}|${incoming.version.writer}`).digest("hex");
274699
275867
  await store2.recordConflict({
274700
275868
  id,
274701
275869
  projectId: store2.projectId(),
@@ -274734,7 +275902,7 @@ var init_engine6 = __esm({
274734
275902
 
274735
275903
  // src/sync/store-port.ts
274736
275904
  import os7 from "os";
274737
- import { randomUUID as randomUUID20 } from "crypto";
275905
+ import { randomUUID as randomUUID21 } from "crypto";
274738
275906
  function createSqliteSyncStore(dataDir, store2, projectId, options = {}) {
274739
275907
  const db2 = getDatabase(dataDir);
274740
275908
  const scope = options.scope ?? "project";
@@ -274784,7 +275952,7 @@ function createSqliteSyncStore(dataDir, store2, projectId, options = {}) {
274784
275952
  function ensureDevice() {
274785
275953
  const row = getMeta.get(DEVICE_KEY);
274786
275954
  if (row?.value) return row.value;
274787
- const id = `dev_${randomUUID20().replaceAll("-", "")}`;
275955
+ const id = `dev_${randomUUID21().replaceAll("-", "")}`;
274788
275956
  setMeta.run(DEVICE_KEY, id);
274789
275957
  setMeta.run(FINGERPRINT_KEY, deviceFingerprint);
274790
275958
  return id;
@@ -274814,7 +275982,7 @@ function createSqliteSyncStore(dataDir, store2, projectId, options = {}) {
274814
275982
  assertCanPublish,
274815
275983
  rotateDevice: () => {
274816
275984
  const previous = ensureDevice();
274817
- const id = `dev_${randomUUID20().replaceAll("-", "")}`;
275985
+ const id = `dev_${randomUUID21().replaceAll("-", "")}`;
274818
275986
  const pending = pendingForDevice.all(previous);
274819
275987
  const rotate = db2.transaction(() => {
274820
275988
  setMeta.run(DEVICE_KEY, id);
@@ -277591,10 +278759,10 @@ __export(handler_exports, {
277591
278759
  resetCooldowns: () => resetCooldowns,
277592
278760
  runHook: () => runHook
277593
278761
  });
277594
- import { createHash as createHash24 } from "crypto";
278762
+ import { createHash as createHash25 } from "crypto";
277595
278763
  function deriveHookActorId(input) {
277596
278764
  const material = `${input.agent ?? "unknown"}\0${input.sessionId ?? "unknown"}`;
277597
- return `hook:${createHash24("sha256").update(material).digest("hex").slice(0, 24)}`;
278765
+ return `hook:${createHash25("sha256").update(material).digest("hex").slice(0, 24)}`;
277598
278766
  }
277599
278767
  function classifyTool(input) {
277600
278768
  if (input.event === "post_edit") return "file_modify";
@@ -279556,10 +280724,10 @@ var init_ingest = __esm({
279556
280724
  });
279557
280725
 
279558
280726
  // src/media/embedding.ts
279559
- import { createHash as createHash25 } from "crypto";
280727
+ import { createHash as createHash26 } from "crypto";
279560
280728
  function profileKey(provider2, asset) {
279561
280729
  const identity = `${provider2.name}\0${provider2.dimensions}\0${asset.kind}`;
279562
- return `media:${createHash25("sha256").update(identity).digest("hex").slice(0, 20)}`;
280730
+ return `media:${createHash26("sha256").update(identity).digest("hex").slice(0, 20)}`;
279563
280731
  }
279564
280732
  function supportsAsset(provider2, asset) {
279565
280733
  return !!provider2.embedInput && (provider2.supportedModalities?.includes(asset.kind) ?? false);
@@ -284751,13 +285919,21 @@ var init_capability_router = __esm({
284751
285919
  // src/orchestrate/pipeline-trace.ts
284752
285920
  function initTraceTable(db2) {
284753
285921
  db2.exec(CREATE_TABLE_SQL);
285922
+ for (const column of ["attempt INTEGER", "risk_tier TEXT", "effect_key TEXT", "memory_refs TEXT"]) {
285923
+ try {
285924
+ db2.exec(`ALTER TABLE pipeline_traces ADD COLUMN ${column}`);
285925
+ } catch {
285926
+ }
285927
+ }
284754
285928
  db2.exec(CREATE_INDEX_SQL);
284755
285929
  }
284756
285930
  function writeTrace(db2, event) {
284757
285931
  if (!_insertStmt) {
284758
285932
  _insertStmt = db2.prepare(`
284759
- INSERT INTO pipeline_traces (pipeline_id, timestamp, type, task_id, agent, detail, duration_ms)
284760
- VALUES (?, ?, ?, ?, ?, ?, ?)
285933
+ INSERT INTO pipeline_traces (
285934
+ pipeline_id, timestamp, type, task_id, agent, detail, duration_ms,
285935
+ attempt, risk_tier, effect_key, memory_refs
285936
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
284761
285937
  `);
284762
285938
  }
284763
285939
  _insertStmt.run(
@@ -284766,8 +285942,12 @@ function writeTrace(db2, event) {
284766
285942
  event.type,
284767
285943
  event.taskId ?? null,
284768
285944
  event.agent ?? null,
284769
- event.detail,
284770
- event.durationMs ?? null
285945
+ sanitizeCredentials(event.detail).slice(0, 2e3),
285946
+ event.durationMs ?? null,
285947
+ event.attempt ?? null,
285948
+ event.riskTier ?? null,
285949
+ event.effectKey ?? null,
285950
+ event.memoryRefs ? JSON.stringify(event.memoryRefs.slice(0, 16)) : null
284771
285951
  );
284772
285952
  }
284773
285953
  function pruneOldTraces(db2, keepPipelines = 20) {
@@ -284796,6 +285976,7 @@ var init_pipeline_trace = __esm({
284796
285976
  "src/orchestrate/pipeline-trace.ts"() {
284797
285977
  "use strict";
284798
285978
  init_esm_shims();
285979
+ init_secret_filter();
284799
285980
  CREATE_TABLE_SQL = `
284800
285981
  CREATE TABLE IF NOT EXISTS pipeline_traces (
284801
285982
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -284805,7 +285986,11 @@ var init_pipeline_trace = __esm({
284805
285986
  task_id TEXT,
284806
285987
  agent TEXT,
284807
285988
  detail TEXT NOT NULL,
284808
- duration_ms INTEGER
285989
+ duration_ms INTEGER,
285990
+ attempt INTEGER,
285991
+ risk_tier TEXT,
285992
+ effect_key TEXT,
285993
+ memory_refs TEXT
284809
285994
  )
284810
285995
  `;
284811
285996
  CREATE_INDEX_SQL = `
@@ -285568,8 +286753,140 @@ var init_permission = __esm({
285568
286753
  }
285569
286754
  });
285570
286755
 
286756
+ // src/orchestrate/effect-ledger.ts
286757
+ import { createHash as createHash27, randomUUID as randomUUID22 } from "crypto";
286758
+ function fingerprintEffectInput(input) {
286759
+ if (input === void 0) return void 0;
286760
+ try {
286761
+ const safe = sanitizeCredentials(JSON.stringify(input));
286762
+ return createHash27("sha256").update(safe).digest("hex").slice(0, 24);
286763
+ } catch {
286764
+ return void 0;
286765
+ }
286766
+ }
286767
+ function rowToEffect(row) {
286768
+ return {
286769
+ id: String(row.id),
286770
+ pipelineId: String(row.pipeline_id),
286771
+ taskId: String(row.task_id),
286772
+ attempt: Number(row.attempt),
286773
+ effectKey: String(row.effect_key),
286774
+ tool: String(row.tool),
286775
+ riskTier: row.risk_tier,
286776
+ replayPolicy: row.replay_policy,
286777
+ status: row.status,
286778
+ ...row.call_id ? { callId: String(row.call_id) } : {},
286779
+ idempotencyKey: String(row.idempotency_key),
286780
+ ...row.input_fingerprint ? { inputFingerprint: String(row.input_fingerprint) } : {},
286781
+ ...row.detail ? { detail: String(row.detail) } : {},
286782
+ startedAt: Number(row.started_at),
286783
+ ...row.finished_at ? { finishedAt: Number(row.finished_at) } : {}
286784
+ };
286785
+ }
286786
+ var AgentEffectLedger;
286787
+ var init_effect_ledger = __esm({
286788
+ "src/orchestrate/effect-ledger.ts"() {
286789
+ "use strict";
286790
+ init_esm_shims();
286791
+ init_secret_filter();
286792
+ init_permission();
286793
+ AgentEffectLedger = class {
286794
+ constructor(db2) {
286795
+ this.db = db2;
286796
+ }
286797
+ db;
286798
+ observe(input) {
286799
+ const existing = this.db.prepare(`
286800
+ SELECT * FROM agent_effects
286801
+ WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND effect_key = ?
286802
+ `).get(input.pipelineId, input.taskId, input.attempt, input.effectKey);
286803
+ if (existing) return rowToEffect(existing);
286804
+ const riskTier = classifyTool2(input.tool);
286805
+ const replayPolicy = riskTier === "safe" ? "safe" : "requires-idempotency-key";
286806
+ const idempotencyKey = `${input.pipelineId}:${input.taskId}:${input.attempt}:${input.effectKey}`;
286807
+ const now3 = Date.now();
286808
+ const record2 = {
286809
+ id: randomUUID22(),
286810
+ pipelineId: input.pipelineId,
286811
+ taskId: input.taskId,
286812
+ attempt: input.attempt,
286813
+ effectKey: input.effectKey,
286814
+ tool: input.tool.slice(0, 200),
286815
+ riskTier,
286816
+ replayPolicy,
286817
+ status: "observed",
286818
+ ...input.callId ? { callId: input.callId.slice(0, 200) } : {},
286819
+ idempotencyKey,
286820
+ ...fingerprintEffectInput(input.input) ? { inputFingerprint: fingerprintEffectInput(input.input) } : {},
286821
+ startedAt: now3
286822
+ };
286823
+ this.db.prepare(`
286824
+ INSERT OR IGNORE INTO agent_effects (
286825
+ id, pipeline_id, task_id, attempt, effect_key, tool, risk_tier,
286826
+ replay_policy, status, call_id, idempotency_key, input_fingerprint,
286827
+ detail, started_at, finished_at
286828
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
286829
+ `).run(
286830
+ record2.id,
286831
+ record2.pipelineId,
286832
+ record2.taskId,
286833
+ record2.attempt,
286834
+ record2.effectKey,
286835
+ record2.tool,
286836
+ record2.riskTier,
286837
+ record2.replayPolicy,
286838
+ record2.status,
286839
+ record2.callId ?? null,
286840
+ record2.idempotencyKey,
286841
+ record2.inputFingerprint ?? null,
286842
+ null,
286843
+ record2.startedAt,
286844
+ null
286845
+ );
286846
+ return rowToEffect(this.db.prepare(`
286847
+ SELECT * FROM agent_effects
286848
+ WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND effect_key = ?
286849
+ `).get(input.pipelineId, input.taskId, input.attempt, input.effectKey));
286850
+ }
286851
+ settle(input) {
286852
+ const detail = input.detail ? sanitizeCredentials(input.detail).slice(0, 500) : void 0;
286853
+ this.db.prepare(`
286854
+ UPDATE agent_effects
286855
+ SET status = ?, detail = ?, finished_at = ?
286856
+ WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND effect_key = ?
286857
+ `).run(
286858
+ input.status,
286859
+ detail ?? null,
286860
+ Date.now(),
286861
+ input.pipelineId,
286862
+ input.taskId,
286863
+ input.attempt,
286864
+ input.effectKey
286865
+ );
286866
+ const row = this.db.prepare(`
286867
+ SELECT * FROM agent_effects
286868
+ WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND effect_key = ?
286869
+ `).get(input.pipelineId, input.taskId, input.attempt, input.effectKey);
286870
+ return row ? rowToEffect(row) : void 0;
286871
+ }
286872
+ markAttemptUnknown(pipelineId, taskId, attempt) {
286873
+ const result = this.db.prepare(`
286874
+ UPDATE agent_effects
286875
+ SET status = 'unknown', detail = COALESCE(detail, 'No matching tool result observed'), finished_at = ?
286876
+ WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND status = 'observed'
286877
+ `).run(Date.now(), pipelineId, taskId, attempt);
286878
+ return Number(result.changes ?? 0);
286879
+ }
286880
+ list(pipelineId, taskId) {
286881
+ 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);
286882
+ return rows.map(rowToEffect);
286883
+ }
286884
+ };
286885
+ }
286886
+ });
286887
+
285571
286888
  // src/orchestrate/memorix-bridge.ts
285572
- import { createHash as createHash26 } from "crypto";
286889
+ import { createHash as createHash28 } from "crypto";
285573
286890
  async function getStoreObservation() {
285574
286891
  if (!_storeObservation) {
285575
286892
  const mod = await Promise.resolve().then(() => (init_observations(), observations_exports));
@@ -285585,7 +286902,7 @@ async function getSearchObservations() {
285585
286902
  return _searchObservations;
285586
286903
  }
285587
286904
  function automaticActorId(...parts) {
285588
- return `orchestrator:${createHash26("sha256").update(parts.join("\0")).digest("hex").slice(0, 24)}`;
286905
+ return `orchestrator:${createHash28("sha256").update(parts.join("\0")).digest("hex").slice(0, 24)}`;
285589
286906
  }
285590
286907
  function isEligibleAutomaticLesson(entry) {
285591
286908
  return entry.admissionState !== "candidate" && entry.admissionState !== "ephemeral";
@@ -285732,7 +287049,7 @@ function sanitizeErrorPattern(text3) {
285732
287049
  }
285733
287050
  function hashErrorPattern(errorOutput) {
285734
287051
  const sanitized = sanitizeErrorPattern(errorOutput).slice(0, 500);
285735
- return createHash26("sha256").update(sanitized).digest("hex").slice(0, 12);
287052
+ return createHash28("sha256").update(sanitized).digest("hex").slice(0, 12);
285736
287053
  }
285737
287054
  function sleep6(ms2) {
285738
287055
  return new Promise((resolve9) => setTimeout(() => resolve9([]), ms2));
@@ -285843,10 +287160,12 @@ async function runCoordinationLoop(config2) {
285843
287160
  const dispatchedAdapterNames = /* @__PURE__ */ new Set();
285844
287161
  const routingDecisions = [];
285845
287162
  let traceDb = null;
287163
+ let effectLedger = null;
285846
287164
  try {
285847
287165
  traceDb = teamStore.getDb();
285848
287166
  resetTraceCache();
285849
287167
  initTraceTable(traceDb);
287168
+ if (pipelineId) effectLedger = new AgentEffectLedger(traceDb);
285850
287169
  } catch {
285851
287170
  }
285852
287171
  if (useWorktrees) {
@@ -286147,6 +287466,32 @@ ${formatCostSummary(costSummary)}`);
286147
287466
  for await (const msg of agentProcess.messages) {
286148
287467
  if (msg.type === "tool_use") {
286149
287468
  dispatch.toolCount++;
287469
+ const effectKey = msg.callId ?? `seq:${dispatch.toolCount}`;
287470
+ const effect = effectLedger?.observe({
287471
+ pipelineId,
287472
+ taskId: dispatch.taskId,
287473
+ attempt: dispatch.attempt,
287474
+ effectKey,
287475
+ tool: msg.tool ?? "unknown",
287476
+ ...msg.callId ? { callId: msg.callId } : {},
287477
+ ...msg.input ? { input: msg.input } : {}
287478
+ });
287479
+ if (effect && traceDb && pipelineId) {
287480
+ try {
287481
+ writeTrace(traceDb, {
287482
+ pipelineId,
287483
+ timestamp: Date.now(),
287484
+ type: "effect",
287485
+ taskId: dispatch.taskId,
287486
+ agent: dispatch.adapterName,
287487
+ detail: `observed tool=${effect.tool} risk=${effect.riskTier} replay=${effect.replayPolicy} effect=${effect.effectKey}`,
287488
+ attempt: dispatch.attempt,
287489
+ riskTier: effect.riskTier,
287490
+ effectKey: effect.effectKey
287491
+ });
287492
+ } catch {
287493
+ }
287494
+ }
286150
287495
  if (msg.tool) tracker.record(msg.tool);
286151
287496
  emit("agent:tool_use", `[${adapter.name}] tool #${dispatch.toolCount}: ${msg.tool ?? "unknown"}`, {
286152
287497
  taskId: task.task_id,
@@ -286166,6 +287511,16 @@ ${formatCostSummary(costSummary)}`);
286166
287511
  }
286167
287512
  }
286168
287513
  }
287514
+ if (msg.type === "tool_result" && msg.callId && effectLedger && pipelineId) {
287515
+ effectLedger.settle({
287516
+ pipelineId,
287517
+ taskId: dispatch.taskId,
287518
+ attempt: dispatch.attempt,
287519
+ effectKey: msg.callId,
287520
+ status: "succeeded",
287521
+ detail: "tool result observed"
287522
+ });
287523
+ }
286169
287524
  if (msg.type === "text" && msg.content) {
286170
287525
  dispatch.accumulatedText += msg.content;
286171
287526
  }
@@ -286248,6 +287603,9 @@ ${formatCostSummary(costSummary)}`);
286248
287603
  if (result.sessionId) {
286249
287604
  taskSessionIds.set(dispatch.taskId, result.sessionId);
286250
287605
  }
287606
+ if (effectLedger && pipelineId) {
287607
+ effectLedger.markAttemptUnknown(pipelineId, dispatch.taskId, dispatch.attempt);
287608
+ }
286251
287609
  const taskState = teamStore.getTask(dispatch.taskId);
286252
287610
  const taskDesc = taskState?.description ?? dispatch.taskId;
286253
287611
  if (!result.killed && result.exitCode === 0) {
@@ -286311,6 +287669,25 @@ ${formatCostSummary(costSummary)}`);
286311
287669
  for await (const msg of fixProcess.messages) {
286312
287670
  if (msg.type === "tool_use") {
286313
287671
  fixDispatch.toolCount++;
287672
+ effectLedger?.observe({
287673
+ pipelineId,
287674
+ taskId: fixDispatch.taskId,
287675
+ attempt: fixDispatch.attempt,
287676
+ effectKey: msg.callId ?? `seq:${fixDispatch.toolCount}`,
287677
+ tool: msg.tool ?? "unknown",
287678
+ ...msg.callId ? { callId: msg.callId } : {},
287679
+ ...msg.input ? { input: msg.input } : {}
287680
+ });
287681
+ }
287682
+ if (msg.type === "tool_result" && msg.callId && effectLedger && pipelineId) {
287683
+ effectLedger.settle({
287684
+ pipelineId,
287685
+ taskId: fixDispatch.taskId,
287686
+ attempt: fixDispatch.attempt,
287687
+ effectKey: msg.callId,
287688
+ status: "succeeded",
287689
+ detail: "tool result observed"
287690
+ });
286314
287691
  }
286315
287692
  if (msg.type === "text" && msg.content) {
286316
287693
  fixDispatch.accumulatedText += msg.content;
@@ -286740,6 +288117,7 @@ var init_coordinator = __esm({
286740
288117
  init_cost_tracker();
286741
288118
  init_evidence2();
286742
288119
  init_permission();
288120
+ init_effect_ledger();
286743
288121
  init_memorix_bridge();
286744
288122
  }
286745
288123
  });
@@ -288149,6 +289527,7 @@ function ConfigureView({ onBack }) {
288149
289527
  { key: "openai", label: "OpenAI", hint: "gpt-4o-mini" },
288150
289528
  { key: "anthropic", label: "Anthropic", hint: "claude-3-haiku" },
288151
289529
  { key: "openrouter", label: "OpenRouter", hint: "multi-provider" },
289530
+ { key: "atlascloud", label: "Atlas Cloud", hint: "DeepSeek V3.2" },
288152
289531
  { key: "custom", label: "Custom endpoint", hint: "OpenAI-compatible" },
288153
289532
  { key: "disable", label: "Disable LLM", hint: "free heuristic mode", color: COLORS.warning },
288154
289533
  { key: "back", label: "Back", hint: "", color: COLORS.muted }
@@ -288441,6 +289820,7 @@ function ConfigureView({ onBack }) {
288441
289820
  function getDefaultModel(provider2) {
288442
289821
  if (provider2 === "anthropic") return "claude-3-haiku-20240307";
288443
289822
  if (provider2 === "openrouter") return "openai/gpt-4o-mini";
289823
+ if (provider2 === "atlascloud") return "deepseek-ai/deepseek-v3.2";
288444
289824
  return "gpt-4o-mini";
288445
289825
  }
288446
289826
  var init_ConfigureView = __esm({
@@ -291646,10 +293026,22 @@ var GUIDES = {
291646
293026
  context: {
291647
293027
  summary: "Build the bounded task Workset used to resume or begin real work.",
291648
293028
  usage: [
291649
- 'memorix context "continue the release fix" [--refresh auto|always|never]',
293029
+ 'memorix context "continue the release fix" [--task-id <continuity-id>] [--refresh auto|always|never]',
291650
293030
  'memorix resume "continue the release fix" [--refresh auto|always|never]'
291651
293031
  ]
291652
293032
  },
293033
+ continuity: {
293034
+ summary: "Track task requirements, decisions, verification, risks, and outcomes.",
293035
+ usage: [
293036
+ 'memorix continuity start --task "Harden the release path" [--requirements "a,b"]',
293037
+ 'memorix continuity record --task-id <id> --kind decision --content "..."',
293038
+ 'memorix continuity record --task-id <id> --kind verification --verification-status passed --content "..."',
293039
+ "memorix continuity show --task-id <id>",
293040
+ 'memorix continuity close --task-id <id> --status completed --content "..."',
293041
+ "memorix continuity list"
293042
+ ],
293043
+ notes: ["Pass the returned task id to memorix context --task-id so the bounded brief includes the ledger."]
293044
+ },
291653
293045
  explain: {
291654
293046
  summary: "Show why a task context contains its current facts and memory evidence.",
291655
293047
  usage: ["memorix explain [--refresh auto|always|never]"]
@@ -292058,6 +293450,7 @@ var main2 = defineCommand({
292058
293450
  knowledge: () => Promise.resolve().then(() => (init_knowledge(), knowledge_exports)).then((m6) => m6.default),
292059
293451
  evidence: () => Promise.resolve().then(() => (init_evidence(), evidence_exports)).then((m6) => m6.default),
292060
293452
  feedback: () => Promise.resolve().then(() => (init_feedback2(), feedback_exports2)).then((m6) => m6.default),
293453
+ continuity: () => Promise.resolve().then(() => (init_continuity(), continuity_exports)).then((m6) => m6.default),
292061
293454
  reasoning: () => Promise.resolve().then(() => (init_reasoning(), reasoning_exports)).then((m6) => m6.default),
292062
293455
  retention: () => Promise.resolve().then(() => (init_retention2(), retention_exports2)).then((m6) => m6.default),
292063
293456
  formation: () => Promise.resolve().then(() => (init_formation2(), formation_exports2)).then((m6) => m6.default),
@@ -292158,6 +293551,7 @@ var main2 = defineCommand({
292158
293551
  "poll",
292159
293552
  "evidence",
292160
293553
  "feedback",
293554
+ "continuity",
292161
293555
  "receipt",
292162
293556
  "serve",
292163
293557
  "serve-http",
@@ -292222,6 +293616,7 @@ var main2 = defineCommand({
292222
293616
  console.error(" knowledge Review source-backed knowledge pages and project workflows");
292223
293617
  console.error(" evidence Inspect source-backed evidence cards");
292224
293618
  console.error(" feedback Record or audit memory feedback");
293619
+ console.error(" continuity Record task requirements, decisions, verification, and outcome");
292225
293620
  console.error(" reasoning Store/search decision rationale");
292226
293621
  console.error(" retention Inspect stale/archive status");
292227
293622
  console.error(" formation Inspect Memory Formation metrics");