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/index.js CHANGED
@@ -286,6 +286,74 @@ var init_launch_root = __esm({
286
286
  }
287
287
  });
288
288
 
289
+ // src/store/sqlite-reliability.ts
290
+ function isTransientSqliteError(error) {
291
+ const code = error?.code;
292
+ if (typeof code === "string") {
293
+ return /^SQLITE_BUSY(?:_|$)/i.test(code);
294
+ }
295
+ return error instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error.message);
296
+ }
297
+ function isSqliteUnavailableError(error) {
298
+ return error instanceof SqliteUnavailableError || error instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error.message);
299
+ }
300
+ function degradedReadError(storeName) {
301
+ return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
302
+ }
303
+ function degradedWriteError(storeName) {
304
+ return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
305
+ }
306
+ function formatSqliteFailure(error) {
307
+ return error instanceof Error && error.message ? error.message : String(error);
308
+ }
309
+ function delay(ms) {
310
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
311
+ }
312
+ async function retrySqliteBusy(operation, label) {
313
+ for (let attempt = 0; ; attempt++) {
314
+ try {
315
+ return await operation();
316
+ } catch (error) {
317
+ if (!isTransientSqliteError(error) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error;
318
+ const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
319
+ console.error(
320
+ `[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
321
+ );
322
+ await delay(waitMs);
323
+ }
324
+ }
325
+ }
326
+ async function initializeSqliteStore(factory, dataDir, label) {
327
+ try {
328
+ loadBetterSqlite3();
329
+ } catch (error) {
330
+ throw new SqliteUnavailableError(error);
331
+ }
332
+ return retrySqliteBusy(async () => {
333
+ const store = factory();
334
+ await store.init(dataDir);
335
+ return store;
336
+ }, label);
337
+ }
338
+ var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
339
+ var init_sqlite_reliability = __esm({
340
+ "src/store/sqlite-reliability.ts"() {
341
+ "use strict";
342
+ init_esm_shims();
343
+ init_sqlite_db();
344
+ SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
345
+ SqliteUnavailableError = class extends Error {
346
+ code = "MEMORIX_SQLITE_UNAVAILABLE";
347
+ constructor(cause) {
348
+ super(
349
+ cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
350
+ );
351
+ this.name = "SqliteUnavailableError";
352
+ }
353
+ };
354
+ }
355
+ });
356
+
289
357
  // src/store/sqlite-fts.ts
290
358
  function normalizeFtsToken(token) {
291
359
  return token.replace(/"/g, '""');
@@ -299,22 +367,27 @@ function buildFtsQuery(query) {
299
367
  function initializeObservationLexicalIndex(db2) {
300
368
  const known = ftsAvailability.get(db2);
301
369
  if (known !== void 0) return known;
302
- try {
303
- db2.exec(CREATE_OBSERVATION_FTS);
304
- const observationCount = Number(db2.prepare("SELECT COUNT(*) AS count FROM observations").get()?.count ?? 0);
305
- const indexedCount = Number(db2.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
306
- if (observationCount !== indexedCount) {
307
- db2.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
370
+ let lastError;
371
+ for (let attempt = 1; attempt <= FTS_INIT_MAX_ATTEMPTS; attempt++) {
372
+ try {
373
+ db2.exec(CREATE_OBSERVATION_FTS);
374
+ const observationCount = Number(db2.prepare("SELECT COUNT(*) AS count FROM observations").get()?.count ?? 0);
375
+ const indexedCount = Number(db2.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
376
+ if (observationCount !== indexedCount) {
377
+ db2.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
378
+ }
379
+ ftsAvailability.set(db2, true);
380
+ return true;
381
+ } catch (error) {
382
+ lastError = error;
383
+ if (!isTransientSqliteError(error) || attempt === FTS_INIT_MAX_ATTEMPTS) break;
308
384
  }
309
- ftsAvailability.set(db2, true);
310
- return true;
311
- } catch (error) {
312
- ftsAvailability.set(db2, false);
313
- console.warn(
314
- `[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${error instanceof Error ? error.message : String(error)}`
315
- );
316
- return false;
317
385
  }
386
+ ftsAvailability.set(db2, false);
387
+ console.warn(
388
+ `[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${lastError instanceof Error ? lastError.message : String(lastError)}`
389
+ );
390
+ return false;
318
391
  }
319
392
  function isObservationLexicalIndexEnabled(db2) {
320
393
  return ftsAvailability.get(db2) === true;
@@ -368,11 +441,12 @@ function searchObservationLexically(db2, options) {
368
441
  score: Math.max(0, Number(row.lexical_score ?? 0))
369
442
  }));
370
443
  }
371
- var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS;
444
+ var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS, FTS_INIT_MAX_ATTEMPTS;
372
445
  var init_sqlite_fts = __esm({
373
446
  "src/store/sqlite-fts.ts"() {
374
447
  "use strict";
375
448
  init_esm_shims();
449
+ init_sqlite_reliability();
376
450
  ftsAvailability = /* @__PURE__ */ new WeakMap();
377
451
  FTS_TABLE = "observations_fts";
378
452
  CREATE_OBSERVATION_FTS = `
@@ -406,6 +480,7 @@ CREATE TRIGGER IF NOT EXISTS observations_fts_au AFTER UPDATE ON observations BE
406
480
  VALUES (new.id, new.entityName, new.title, new.narrative, new.facts, new.filesModified, new.concepts, new.attachments);
407
481
  END;
408
482
  `;
483
+ FTS_INIT_MAX_ATTEMPTS = 3;
409
484
  }
410
485
  });
411
486
 
@@ -444,11 +519,21 @@ function applySchemaMigrations(db2) {
444
519
  apply();
445
520
  }
446
521
  }
522
+ function normalizeDataDir(dataDir) {
523
+ return path3.resolve(dataDir);
524
+ }
525
+ function touchDatabase(normalized, db2) {
526
+ _dbCache.delete(normalized);
527
+ _dbCache.set(normalized, db2);
528
+ }
447
529
  function getDatabase(dataDir) {
448
- const normalized = path3.resolve(dataDir);
530
+ const normalized = normalizeDataDir(dataDir);
449
531
  assertNotHomeDataDir(normalized);
450
532
  const existing = _dbCache.get(normalized);
451
- if (existing) return existing;
533
+ if (existing) {
534
+ touchDatabase(normalized, existing);
535
+ return existing;
536
+ }
452
537
  loadBetterSqlite3();
453
538
  fs.mkdirSync(dataDir, { recursive: true });
454
539
  const dbPath = path3.join(dataDir, "memorix.db");
@@ -478,6 +563,8 @@ function getDatabase(dataDir) {
478
563
  db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
479
564
  db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
480
565
  db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
566
+ db2.exec(CREATE_TASK_CONTINUITY_EVENTS_TABLE);
567
+ db2.exec(CREATE_AGENT_EFFECTS_TABLE);
481
568
  try {
482
569
  db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
483
570
  } catch {
@@ -516,7 +603,7 @@ function getDatabase(dataDir) {
516
603
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('storage_generation', '0')`).run();
517
604
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('next_id', '1')`).run();
518
605
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('mini_skills_generation', '0')`).run();
519
- _dbCache.set(normalized, db2);
606
+ touchDatabase(normalized, db2);
520
607
  return db2;
521
608
  } catch (error) {
522
609
  try {
@@ -534,8 +621,9 @@ function closeAllDatabases() {
534
621
  }
535
622
  _dbCache.delete(key);
536
623
  }
624
+ _dbLeases.clear();
537
625
  }
538
- 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;
626
+ 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;
539
627
  var init_sqlite_db = __esm({
540
628
  "src/store/sqlite-db.ts"() {
541
629
  "use strict";
@@ -1252,6 +1340,43 @@ CREATE TABLE IF NOT EXISTS memory_feedback_events (
1252
1340
  delta REAL NOT NULL DEFAULT 0,
1253
1341
  observed_at TEXT NOT NULL
1254
1342
  );
1343
+ `;
1344
+ CREATE_TASK_CONTINUITY_EVENTS_TABLE = `
1345
+ CREATE TABLE IF NOT EXISTS task_continuity_events (
1346
+ id TEXT PRIMARY KEY,
1347
+ project_id TEXT NOT NULL,
1348
+ task_id TEXT NOT NULL,
1349
+ task TEXT NOT NULL,
1350
+ kind TEXT NOT NULL,
1351
+ content TEXT NOT NULL,
1352
+ item_status TEXT,
1353
+ source_ref TEXT,
1354
+ evidence_json TEXT NOT NULL DEFAULT '[]',
1355
+ actor TEXT,
1356
+ created_at TEXT NOT NULL,
1357
+ event_key TEXT,
1358
+ item_key TEXT,
1359
+ supersedes_id TEXT
1360
+ );
1361
+ `;
1362
+ CREATE_AGENT_EFFECTS_TABLE = `
1363
+ CREATE TABLE IF NOT EXISTS agent_effects (
1364
+ id TEXT PRIMARY KEY,
1365
+ pipeline_id TEXT NOT NULL,
1366
+ task_id TEXT NOT NULL,
1367
+ attempt INTEGER NOT NULL,
1368
+ effect_key TEXT NOT NULL,
1369
+ tool TEXT NOT NULL,
1370
+ risk_tier TEXT NOT NULL,
1371
+ replay_policy TEXT NOT NULL,
1372
+ status TEXT NOT NULL,
1373
+ call_id TEXT,
1374
+ idempotency_key TEXT NOT NULL,
1375
+ input_fingerprint TEXT,
1376
+ detail TEXT,
1377
+ started_at INTEGER NOT NULL,
1378
+ finished_at INTEGER
1379
+ );
1255
1380
  `;
1256
1381
  CREATE_MCP_BINDINGS_TABLE = `
1257
1382
  CREATE TABLE IF NOT EXISTS mcp_bindings (
@@ -1330,7 +1455,18 @@ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(projec
1330
1455
  CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
1331
1456
  CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
1332
1457
  CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
1458
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_project_task ON task_continuity_events(project_id, task_id, created_at ASC);
1459
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_project_recent ON task_continuity_events(project_id, created_at DESC);
1460
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_item ON task_continuity_events(project_id, task_id, kind, item_key, created_at DESC);
1461
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
1462
+ ON task_continuity_events(project_id, task_id, event_key)
1463
+ WHERE event_key IS NOT NULL;
1464
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_effect_identity
1465
+ ON agent_effects(pipeline_id, task_id, attempt, effect_key);
1466
+ CREATE INDEX IF NOT EXISTS idx_agent_effect_pipeline ON agent_effects(pipeline_id, started_at ASC);
1333
1467
  CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
1468
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC);
1469
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at);
1334
1470
  CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1335
1471
  ON maintenance_jobs(project_id, kind, dedupe_key)
1336
1472
  WHERE status IN ('pending', 'running', 'retry');
@@ -1501,6 +1637,8 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1501
1637
  db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
1502
1638
  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)");
1503
1639
  db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
1640
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC)");
1641
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at)");
1504
1642
  }
1505
1643
  },
1506
1644
  {
@@ -1511,9 +1649,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1511
1649
  addColumnIfMissing(db2, "code_symbols", "source", "source TEXT");
1512
1650
  addColumnIfMissing(db2, "code_edges", "source", "source TEXT");
1513
1651
  }
1652
+ },
1653
+ {
1654
+ id: "1.9-continuity-v2",
1655
+ apply: (db2) => {
1656
+ addColumnIfMissing(db2, "task_continuity_events", "event_key", "event_key TEXT");
1657
+ addColumnIfMissing(db2, "task_continuity_events", "item_key", "item_key TEXT");
1658
+ addColumnIfMissing(db2, "task_continuity_events", "supersedes_id", "supersedes_id TEXT");
1659
+ 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)");
1660
+ db2.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
1661
+ ON task_continuity_events(project_id, task_id, event_key)
1662
+ WHERE event_key IS NOT NULL`);
1663
+ }
1514
1664
  }
1515
1665
  ];
1516
1666
  _dbCache = /* @__PURE__ */ new Map();
1667
+ _dbLeases = /* @__PURE__ */ new Map();
1517
1668
  }
1518
1669
  });
1519
1670
 
@@ -2481,74 +2632,6 @@ var init_types = __esm({
2481
2632
  }
2482
2633
  });
2483
2634
 
2484
- // src/store/sqlite-reliability.ts
2485
- function isTransientSqliteError(error) {
2486
- const code = error?.code;
2487
- if (typeof code === "string") {
2488
- return /^SQLITE_BUSY(?:_|$)/i.test(code);
2489
- }
2490
- return error instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error.message);
2491
- }
2492
- function isSqliteUnavailableError(error) {
2493
- return error instanceof SqliteUnavailableError || error instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error.message);
2494
- }
2495
- function degradedReadError(storeName) {
2496
- return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
2497
- }
2498
- function degradedWriteError(storeName) {
2499
- return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
2500
- }
2501
- function formatSqliteFailure(error) {
2502
- return error instanceof Error && error.message ? error.message : String(error);
2503
- }
2504
- function delay(ms) {
2505
- return new Promise((resolve5) => setTimeout(resolve5, ms));
2506
- }
2507
- async function retrySqliteBusy(operation, label) {
2508
- for (let attempt = 0; ; attempt++) {
2509
- try {
2510
- return await operation();
2511
- } catch (error) {
2512
- if (!isTransientSqliteError(error) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error;
2513
- const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
2514
- console.error(
2515
- `[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
2516
- );
2517
- await delay(waitMs);
2518
- }
2519
- }
2520
- }
2521
- async function initializeSqliteStore(factory, dataDir, label) {
2522
- try {
2523
- loadBetterSqlite3();
2524
- } catch (error) {
2525
- throw new SqliteUnavailableError(error);
2526
- }
2527
- return retrySqliteBusy(async () => {
2528
- const store = factory();
2529
- await store.init(dataDir);
2530
- return store;
2531
- }, label);
2532
- }
2533
- var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
2534
- var init_sqlite_reliability = __esm({
2535
- "src/store/sqlite-reliability.ts"() {
2536
- "use strict";
2537
- init_esm_shims();
2538
- init_sqlite_db();
2539
- SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
2540
- SqliteUnavailableError = class extends Error {
2541
- code = "MEMORIX_SQLITE_UNAVAILABLE";
2542
- constructor(cause) {
2543
- super(
2544
- cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
2545
- );
2546
- this.name = "SqliteUnavailableError";
2547
- }
2548
- };
2549
- }
2550
- });
2551
-
2552
2635
  // src/store/mini-skill-store.ts
2553
2636
  var mini_skill_store_exports = {};
2554
2637
  __export(mini_skill_store_exports, {
@@ -3228,18 +3311,23 @@ __export(yaml_loader_exports, {
3228
3311
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
3229
3312
  import { homedir } from "os";
3230
3313
  import { parse as parseYamlDocument } from "yaml";
3314
+ function cacheKey(homeDir, projectRoot) {
3315
+ return `${homeDir}\0${projectRoot ?? ""}`;
3316
+ }
3231
3317
  function initProjectRoot(root) {
3232
3318
  globalProjectRoot = root;
3233
- configCache.delete(root);
3319
+ configCache.delete(cacheKey(homedir(), root));
3234
3320
  }
3235
3321
  function clearProjectRoot() {
3236
3322
  globalProjectRoot = null;
3237
3323
  }
3238
- function loadYamlConfig(projectRoot) {
3324
+ function loadYamlConfig(projectRoot, options = {}) {
3325
+ const homeDir = options.homeDir ?? homedir();
3239
3326
  const resolvedRoot = projectRoot === null ? null : projectRoot ?? globalProjectRoot ?? null;
3240
- const cached = configCache.get(resolvedRoot ?? null);
3327
+ const key = cacheKey(homeDir, resolvedRoot);
3328
+ const cached = configCache.get(key);
3241
3329
  if (cached) return cached;
3242
- const userYaml = getGlobalYamlPath(homedir());
3330
+ const userYaml = getGlobalYamlPath(homeDir);
3243
3331
  const projectYaml = resolvedRoot ? getProjectYamlPath(resolvedRoot) : null;
3244
3332
  let userConfig = {};
3245
3333
  let projectConfig = {};
@@ -3271,12 +3359,12 @@ function loadYamlConfig(projectRoot) {
3271
3359
  server: { ...userConfig.server, ...projectConfig.server },
3272
3360
  team: { ...userConfig.team, ...projectConfig.team }
3273
3361
  };
3274
- configCache.set(resolvedRoot ?? null, merged);
3362
+ configCache.set(key, merged);
3275
3363
  return merged;
3276
3364
  }
3277
- function resetYamlConfigCache(projectRoot) {
3365
+ function resetYamlConfigCache(projectRoot, homeDir = homedir()) {
3278
3366
  if (projectRoot !== void 0) {
3279
- configCache.delete(projectRoot ?? null);
3367
+ configCache.delete(cacheKey(homeDir, projectRoot ?? null));
3280
3368
  } else {
3281
3369
  configCache.clear();
3282
3370
  }
@@ -3486,9 +3574,9 @@ var init_detector = __esm({
3486
3574
  // src/config/legacy-loader.ts
3487
3575
  import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
3488
3576
  import { homedir as homedir2 } from "os";
3489
- function loadFileConfig() {
3577
+ function loadFileConfig(homeDir = homedir2()) {
3490
3578
  if (cachedConfig !== null) return cachedConfig;
3491
- const configPath = getLegacyConfigJsonPath(homedir2());
3579
+ const configPath = getLegacyConfigJsonPath(homeDir);
3492
3580
  try {
3493
3581
  if (existsSync4(configPath)) {
3494
3582
  cachedConfig = JSON.parse(readFileSync4(configPath, "utf-8"));
@@ -3518,15 +3606,15 @@ import { homedir as homedir3 } from "os";
3518
3606
  function loadTomlConfig(options = {}) {
3519
3607
  const homeDir = options.homeDir ?? homedir3();
3520
3608
  const projectRoot = options.projectRoot ?? null;
3521
- const cacheKey = `${homeDir}\0${projectRoot ?? ""}`;
3522
- const cached = configCache2.get(cacheKey);
3609
+ const cacheKey3 = `${homeDir}\0${projectRoot ?? ""}`;
3610
+ const cached = configCache2.get(cacheKey3);
3523
3611
  if (cached) return cached;
3524
3612
  const userToml = getGlobalConfigTomlPath(homeDir);
3525
3613
  const projectToml = projectRoot ? getProjectConfigTomlPath(projectRoot) : null;
3526
3614
  const userConfig = existsSync5(userToml) ? parseTomlConfig(readFileSync5(userToml, "utf8"), userToml) : {};
3527
3615
  const projectConfig = projectToml && existsSync5(projectToml) ? parseTomlConfig(readFileSync5(projectToml, "utf8"), projectToml) : {};
3528
3616
  const merged = mergeTomlConfig(userConfig, projectConfig);
3529
- configCache2.set(cacheKey, merged);
3617
+ configCache2.set(cacheKey3, merged);
3530
3618
  return merged;
3531
3619
  }
3532
3620
  function mergeTomlConfig(base, override) {
@@ -3726,8 +3814,8 @@ function getResolvedConfig(options = {}) {
3726
3814
  const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
3727
3815
  loadDotenv(projectRoot === null ? void 0 : projectRoot ?? void 0, { userHomeDir: homeDir });
3728
3816
  const toml = loadTomlConfig({ projectRoot: projectRoot ?? null, homeDir });
3729
- const yaml = loadYamlConfig(projectRoot ?? null);
3730
- const legacy = loadFileConfig();
3817
+ const yaml = loadYamlConfig(projectRoot ?? null, { homeDir });
3818
+ const legacy = loadFileConfig(homeDir);
3731
3819
  const embeddingBaseUrl = first(process.env.MEMORIX_EMBEDDING_BASE_URL, toml.embedding?.base_url, yaml.embedding?.baseUrl, legacy.embeddingApi?.baseUrl);
3732
3820
  const openRouterEmbeddingApiKey = isOpenRouterUrl(embeddingBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
3733
3821
  const memoryLlmProvider = first(
@@ -3749,15 +3837,17 @@ function getResolvedConfig(options = {}) {
3749
3837
  legacy.llm?.baseUrl
3750
3838
  );
3751
3839
  const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
3840
+ const isAtlasCloud = memoryLlmProvider === "atlascloud";
3752
3841
  const memoryLlmApiKey = first(
3753
3842
  process.env.MEMORIX_LLM_API_KEY,
3754
3843
  process.env.MEMORIX_API_KEY,
3755
3844
  toml.memory?.llm?.api_key,
3756
3845
  yaml.llm?.apiKey,
3757
3846
  legacy.llm?.apiKey,
3758
- process.env.OPENAI_API_KEY,
3759
- process.env.ANTHROPIC_API_KEY,
3760
- openRouterMemoryLlmApiKey
3847
+ // Never borrow another provider's credentials for the Atlas Cloud preset.
3848
+ isAtlasCloud ? process.env.ATLASCLOUD_API_KEY : process.env.OPENAI_API_KEY,
3849
+ isAtlasCloud ? void 0 : process.env.ANTHROPIC_API_KEY,
3850
+ isAtlasCloud ? void 0 : openRouterMemoryLlmApiKey
3761
3851
  );
3762
3852
  const resolved = {
3763
3853
  agent: {
@@ -3936,7 +4026,8 @@ function getEnvSourceNames() {
3936
4026
  "MEMORIX_CODEGRAPH_EXTERNAL_CONTEXT",
3937
4027
  "MEMORIX_CODEGRAPH_EXTERNAL_COMMAND",
3938
4028
  "MEMORIX_CODEGRAPH_EXTERNAL_TIMEOUT_MS",
3939
- "OPENROUTER_API_KEY"
4029
+ "OPENROUTER_API_KEY",
4030
+ "ATLASCLOUD_API_KEY"
3940
4031
  ].filter((name) => process.env[name]);
3941
4032
  }
3942
4033
  function isOpenRouterUrl(value) {
@@ -4155,12 +4246,45 @@ function textHash(text3) {
4155
4246
  async function loadDiskCache() {
4156
4247
  try {
4157
4248
  const raw = await readFile(CACHE_FILE, "utf-8");
4249
+ if (Buffer.byteLength(raw, "utf8") > MAX_CACHE_FILE_BYTES) {
4250
+ console.error("[memorix] FastEmbed cache exceeds the disk load limit; starting with an empty cache");
4251
+ return;
4252
+ }
4158
4253
  const entries = JSON.parse(raw);
4159
- for (const [k, v] of entries) cache.set(k, v);
4160
- console.error(`[memorix] Loaded ${entries.length} cached embeddings from disk`);
4254
+ if (!Array.isArray(entries)) return;
4255
+ for (const entry of entries) {
4256
+ if (!Array.isArray(entry) || typeof entry[0] !== "string" || !isVector(entry[1])) continue;
4257
+ cacheSet(entry[0], entry[1], false);
4258
+ }
4259
+ console.error(`[memorix] Loaded ${cache.size} cached embeddings from disk`);
4161
4260
  } catch {
4162
4261
  }
4163
4262
  }
4263
+ function isVector(value) {
4264
+ return Array.isArray(value) && value.every((entry) => typeof entry === "number");
4265
+ }
4266
+ function maxCachePayloadBytes() {
4267
+ const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
4268
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES;
4269
+ }
4270
+ function cacheSet(hash4, value, markDirty = true) {
4271
+ const incomingBytes = value.length * 8;
4272
+ if (incomingBytes > maxCachePayloadBytes()) return;
4273
+ const existing = cache.get(hash4);
4274
+ if (existing) cachePayloadBytes -= existing.length * 8;
4275
+ else {
4276
+ while (cache.size >= MAX_CACHE_SIZE || cachePayloadBytes + incomingBytes > maxCachePayloadBytes()) {
4277
+ const firstKey = cache.keys().next().value;
4278
+ if (firstKey === void 0) break;
4279
+ const old = cache.get(firstKey);
4280
+ cache.delete(firstKey);
4281
+ if (old) cachePayloadBytes -= old.length * 8;
4282
+ }
4283
+ }
4284
+ cache.set(hash4, value);
4285
+ cachePayloadBytes += incomingBytes;
4286
+ if (markDirty) diskCacheDirty = true;
4287
+ }
4164
4288
  async function saveDiskCache() {
4165
4289
  if (!diskCacheDirty) return;
4166
4290
  try {
@@ -4171,7 +4295,7 @@ async function saveDiskCache() {
4171
4295
  } catch {
4172
4296
  }
4173
4297
  }
4174
- var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, diskCacheDirty, FastEmbedProvider;
4298
+ var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, DEFAULT_CACHE_PAYLOAD_BYTES, MAX_CACHE_FILE_BYTES, cachePayloadBytes, diskCacheDirty, FastEmbedProvider;
4175
4299
  var init_fastembed_provider = __esm({
4176
4300
  "src/embedding/fastembed-provider.ts"() {
4177
4301
  "use strict";
@@ -4181,6 +4305,9 @@ var init_fastembed_provider = __esm({
4181
4305
  CACHE_FILE = join2(CACHE_DIR, ".embedding-cache.json");
4182
4306
  cache = /* @__PURE__ */ new Map();
4183
4307
  MAX_CACHE_SIZE = 5e3;
4308
+ DEFAULT_CACHE_PAYLOAD_BYTES = 64 * 1024 * 1024;
4309
+ MAX_CACHE_FILE_BYTES = 128 * 1024 * 1024;
4310
+ cachePayloadBytes = 0;
4184
4311
  diskCacheDirty = false;
4185
4312
  FastEmbedProvider = class _FastEmbedProvider {
4186
4313
  name = "fastembed-bge-small";
@@ -4222,7 +4349,7 @@ var init_fastembed_provider = __esm({
4222
4349
  if (result.length !== this.dimensions) {
4223
4350
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
4224
4351
  }
4225
- this.cacheSet(hash4, result);
4352
+ cacheSet(hash4, result);
4226
4353
  return result;
4227
4354
  }
4228
4355
  async embedBatch(texts) {
@@ -4247,7 +4374,7 @@ var init_fastembed_provider = __esm({
4247
4374
  const originalIdx = uncachedIndices[batchIdx];
4248
4375
  const plain = Array.from(vec);
4249
4376
  results[originalIdx] = plain;
4250
- this.cacheSet(textHash(uncachedTexts[batchIdx]), plain);
4377
+ cacheSet(textHash(uncachedTexts[batchIdx]), plain);
4251
4378
  batchIdx++;
4252
4379
  }
4253
4380
  }
@@ -4255,14 +4382,6 @@ var init_fastembed_provider = __esm({
4255
4382
  }
4256
4383
  return results;
4257
4384
  }
4258
- cacheSet(hash4, value) {
4259
- if (cache.size >= MAX_CACHE_SIZE) {
4260
- const firstKey = cache.keys().next().value;
4261
- if (firstKey !== void 0) cache.delete(firstKey);
4262
- }
4263
- cache.set(hash4, value);
4264
- diskCacheDirty = true;
4265
- }
4266
4385
  };
4267
4386
  }
4268
4387
  });
@@ -4272,7 +4391,15 @@ var transformers_provider_exports = {};
4272
4391
  __export(transformers_provider_exports, {
4273
4392
  TransformersProvider: () => TransformersProvider
4274
4393
  });
4275
- var cache2, MAX_CACHE_SIZE2, TransformersProvider;
4394
+ import { createHash as createHash2 } from "crypto";
4395
+ function cacheKey2(text3) {
4396
+ return createHash2("sha256").update(text3).digest("hex");
4397
+ }
4398
+ function maxCachePayloadBytes2() {
4399
+ const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
4400
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES2;
4401
+ }
4402
+ var cache2, MAX_CACHE_SIZE2, DEFAULT_CACHE_PAYLOAD_BYTES2, cachePayloadBytes2, TransformersProvider;
4276
4403
  var init_transformers_provider = __esm({
4277
4404
  "src/embedding/transformers-provider.ts"() {
4278
4405
  "use strict";
@@ -4280,6 +4407,8 @@ var init_transformers_provider = __esm({
4280
4407
  init_provider();
4281
4408
  cache2 = /* @__PURE__ */ new Map();
4282
4409
  MAX_CACHE_SIZE2 = 5e3;
4410
+ DEFAULT_CACHE_PAYLOAD_BYTES2 = 64 * 1024 * 1024;
4411
+ cachePayloadBytes2 = 0;
4283
4412
  TransformersProvider = class _TransformersProvider {
4284
4413
  name = "transformers-minilm";
4285
4414
  dimensions = 384;
@@ -4314,7 +4443,7 @@ var init_transformers_provider = __esm({
4314
4443
  return Promise.all(inputs.map((input) => this.embedInput(input, options)));
4315
4444
  }
4316
4445
  async embed(text3) {
4317
- const cached = cache2.get(text3);
4446
+ const cached = cache2.get(cacheKey2(text3));
4318
4447
  if (cached) return cached;
4319
4448
  const output = await this.extractor(text3, {
4320
4449
  pooling: "mean",
@@ -4324,7 +4453,7 @@ var init_transformers_provider = __esm({
4324
4453
  if (result.length !== this.dimensions) {
4325
4454
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
4326
4455
  }
4327
- this.cacheSet(text3, result);
4456
+ this.cacheSet(cacheKey2(text3), result);
4328
4457
  return result;
4329
4458
  }
4330
4459
  async embedBatch(texts) {
@@ -4332,7 +4461,7 @@ var init_transformers_provider = __esm({
4332
4461
  const uncachedIndices = [];
4333
4462
  const uncachedTexts = [];
4334
4463
  for (let i2 = 0; i2 < texts.length; i2++) {
4335
- const cached = cache2.get(texts[i2]);
4464
+ const cached = cache2.get(cacheKey2(texts[i2]));
4336
4465
  if (cached) {
4337
4466
  results[i2] = cached;
4338
4467
  } else {
@@ -4350,17 +4479,27 @@ var init_transformers_provider = __esm({
4350
4479
  const vec = Array.from(allVecs[i2]);
4351
4480
  const originalIdx = uncachedIndices[i2];
4352
4481
  results[originalIdx] = vec;
4353
- this.cacheSet(uncachedTexts[i2], vec);
4482
+ this.cacheSet(cacheKey2(uncachedTexts[i2]), vec);
4354
4483
  }
4355
4484
  }
4356
4485
  return results;
4357
4486
  }
4358
4487
  cacheSet(key, value) {
4359
- if (cache2.size >= MAX_CACHE_SIZE2) {
4360
- const firstKey = cache2.keys().next().value;
4361
- if (firstKey !== void 0) cache2.delete(firstKey);
4488
+ const incomingBytes = value.length * 8;
4489
+ if (incomingBytes > maxCachePayloadBytes2()) return;
4490
+ const existing = cache2.get(key);
4491
+ if (existing) cachePayloadBytes2 -= existing.length * 8;
4492
+ else {
4493
+ while (cache2.size >= MAX_CACHE_SIZE2 || cachePayloadBytes2 + incomingBytes > maxCachePayloadBytes2()) {
4494
+ const firstKey = cache2.keys().next().value;
4495
+ if (firstKey === void 0) break;
4496
+ const old = cache2.get(firstKey);
4497
+ cache2.delete(firstKey);
4498
+ if (old) cachePayloadBytes2 -= old.length * 8;
4499
+ }
4362
4500
  }
4363
4501
  cache2.set(key, value);
4502
+ cachePayloadBytes2 += incomingBytes;
4364
4503
  }
4365
4504
  };
4366
4505
  }
@@ -4382,7 +4521,7 @@ __export(api_provider_exports, {
4382
4521
  saveDiskCacheNow: () => saveDiskCacheNow,
4383
4522
  seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
4384
4523
  });
4385
- import { createHash as createHash2 } from "crypto";
4524
+ import { createHash as createHash3 } from "crypto";
4386
4525
  import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
4387
4526
  import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
4388
4527
  import { join as join3 } from "path";
@@ -4409,7 +4548,7 @@ function cacheStatFile() {
4409
4548
  function payloadBytes(vector) {
4410
4549
  return vector.length * BYTES_PER_DIM;
4411
4550
  }
4412
- function maxCachePayloadBytes() {
4551
+ function maxCachePayloadBytes3() {
4413
4552
  const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
4414
4553
  if (raw) {
4415
4554
  const parsed = Number.parseInt(raw, 10);
@@ -4419,7 +4558,7 @@ function maxCachePayloadBytes() {
4419
4558
  }
4420
4559
  function maxEntriesForDimensions(dimensions) {
4421
4560
  const dim = Math.max(1, Math.floor(dimensions));
4422
- const byBytes = Math.floor(maxCachePayloadBytes() / (dim * BYTES_PER_DIM));
4561
+ const byBytes = Math.floor(maxCachePayloadBytes3() / (dim * BYTES_PER_DIM));
4423
4562
  return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
4424
4563
  }
4425
4564
  function rememberDimensions(vector) {
@@ -4432,13 +4571,15 @@ function evictOldest() {
4432
4571
  if (firstKey === void 0) return;
4433
4572
  const old = cache3.get(firstKey);
4434
4573
  cache3.delete(firstKey);
4435
- if (old) cachePayloadBytes -= payloadBytes(old);
4574
+ if (old) cachePayloadBytes3 -= payloadBytes(old);
4436
4575
  }
4437
4576
  function evictToFit(incomingBytes, incomingDims) {
4577
+ if (incomingBytes > maxCachePayloadBytes3()) return false;
4438
4578
  const limit2 = maxEntriesForDimensions(incomingDims);
4439
- while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes + incomingBytes > maxCachePayloadBytes())) {
4579
+ while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes3 + incomingBytes > maxCachePayloadBytes3())) {
4440
4580
  evictOldest();
4441
4581
  }
4582
+ return true;
4442
4583
  }
4443
4584
  function isNumberArray(value) {
4444
4585
  return Array.isArray(value) && value.every((entry) => typeof entry === "number");
@@ -4465,12 +4606,12 @@ function cacheInsert(hash4, value, dirty) {
4465
4606
  const incoming = payloadBytes(value);
4466
4607
  const existing = cache3.get(hash4);
4467
4608
  if (existing) {
4468
- cachePayloadBytes -= payloadBytes(existing);
4609
+ cachePayloadBytes3 -= payloadBytes(existing);
4469
4610
  cache3.delete(hash4);
4470
4611
  }
4471
- evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
4612
+ if (!evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS)) return;
4472
4613
  cache3.set(hash4, value);
4473
- cachePayloadBytes += incoming;
4614
+ cachePayloadBytes3 += incoming;
4474
4615
  if (dirty) diskCacheDirty2 = true;
4475
4616
  }
4476
4617
  async function readCacheStat() {
@@ -4506,7 +4647,7 @@ function cacheNamespace(config) {
4506
4647
  ].join("|");
4507
4648
  }
4508
4649
  function textHash2(text3, namespace) {
4509
- return createHash2("sha256").update(`${namespace}\0${text3}`).digest("hex").slice(0, 16);
4650
+ return createHash3("sha256").update(`${namespace}\0${text3}`).digest("hex").slice(0, 16);
4510
4651
  }
4511
4652
  function inputIdentity(input, options = {}) {
4512
4653
  return JSON.stringify({
@@ -4848,7 +4989,7 @@ function getApiEmbeddingDiskCacheLoadCountForTests() {
4848
4989
  }
4849
4990
  function resetApiEmbeddingCacheForTests() {
4850
4991
  cache3.clear();
4851
- cachePayloadBytes = 0;
4992
+ cachePayloadBytes3 = 0;
4852
4993
  cacheDimensions = null;
4853
4994
  diskCacheEntryCount = 0;
4854
4995
  diskCacheLoaded = false;
@@ -4859,14 +5000,14 @@ function resetApiEmbeddingCacheForTests() {
4859
5000
  }
4860
5001
  function seedApiEmbeddingCacheForTests(entries) {
4861
5002
  cache3.clear();
4862
- cachePayloadBytes = 0;
5003
+ cachePayloadBytes3 = 0;
4863
5004
  cacheDimensions = null;
4864
5005
  for (const [key, value] of entries) cacheInsert(key, value, true);
4865
5006
  diskCacheLoaded = true;
4866
5007
  diskCacheDirty2 = true;
4867
5008
  }
4868
5009
  function getApiEmbeddingCachePayloadBytesForTests() {
4869
- return cachePayloadBytes;
5010
+ return cachePayloadBytes3;
4870
5011
  }
4871
5012
  function getApiEmbeddingJsonlLineCountCallsForTests() {
4872
5013
  return jsonlLineCountCalls;
@@ -5040,7 +5181,7 @@ function scheduleDiskSave() {
5040
5181
  diskSaveTimer = null;
5041
5182
  }, DISK_SAVE_DEBOUNCE_MS);
5042
5183
  }
5043
- function cacheSet(hash4, value) {
5184
+ function cacheSet2(hash4, value) {
5044
5185
  cacheInsert(hash4, value, true);
5045
5186
  }
5046
5187
  function resolveEnvEmbeddingApiKey() {
@@ -5160,7 +5301,7 @@ async function waitForRetry(waitMs, signal) {
5160
5301
  abortSignal.addEventListener("abort", onAbort, { once: true });
5161
5302
  });
5162
5303
  }
5163
- 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;
5304
+ 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;
5164
5305
  var init_api_provider = __esm({
5165
5306
  "src/embedding/api-provider.ts"() {
5166
5307
  "use strict";
@@ -5172,7 +5313,7 @@ var init_api_provider = __esm({
5172
5313
  BYTES_PER_DIM = 8;
5173
5314
  DEFAULT_ASSUMED_DIMENSIONS = 4096;
5174
5315
  MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
5175
- cachePayloadBytes = 0;
5316
+ cachePayloadBytes3 = 0;
5176
5317
  cacheDimensions = null;
5177
5318
  diskCacheEntryCount = 0;
5178
5319
  diskCacheDirty2 = false;
@@ -5357,7 +5498,7 @@ var init_api_provider = __esm({
5357
5498
  } else {
5358
5499
  embedding = await apiCall();
5359
5500
  }
5360
- cacheSet(hash4, embedding);
5501
+ cacheSet2(hash4, embedding);
5361
5502
  scheduleDiskSave();
5362
5503
  return embedding;
5363
5504
  }
@@ -5405,7 +5546,7 @@ var init_api_provider = __esm({
5405
5546
  for (const item of response.data) {
5406
5547
  const originalIdx = chunkIndices[item.index];
5407
5548
  results[originalIdx] = item.embedding;
5408
- cacheSet(textHash2(normalizedTexts[originalIdx], this.cacheKeyNamespace), item.embedding);
5549
+ cacheSet2(textHash2(normalizedTexts[originalIdx], this.cacheKeyNamespace), item.embedding);
5409
5550
  }
5410
5551
  } catch (error) {
5411
5552
  const providerLimit = parseBatchLimit(error);
@@ -5483,7 +5624,7 @@ var init_api_provider = __esm({
5483
5624
  throw new Error(`Expected ${this.dimensions}d, got ${embedding.length}d; dimension mismatch`);
5484
5625
  }
5485
5626
  this.trackUsage(response);
5486
- cacheSet(hash4, embedding);
5627
+ cacheSet2(hash4, embedding);
5487
5628
  scheduleDiskSave();
5488
5629
  return embedding;
5489
5630
  }
@@ -6866,6 +7007,7 @@ var init_provider2 = __esm({
6866
7007
  openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4.1-nano" },
6867
7008
  anthropic: { baseUrl: "https://api.anthropic.com/v1", model: "claude-3-5-haiku-latest" },
6868
7009
  openrouter: { baseUrl: "https://openrouter.ai/api/v1", model: "openai/gpt-4.1-nano" },
7010
+ atlascloud: { baseUrl: "https://api.atlascloud.ai/v1", model: "deepseek-ai/deepseek-v3.2" },
6869
7011
  custom: { baseUrl: "http://localhost:11434/v1", model: "llama3" }
6870
7012
  };
6871
7013
  currentConfig = null;
@@ -7629,7 +7771,7 @@ var init_obs_store = __esm({
7629
7771
  });
7630
7772
 
7631
7773
  // src/search/semantic-index.ts
7632
- import { createHash as createHash3 } from "crypto";
7774
+ import { createHash as createHash4 } from "crypto";
7633
7775
  import { createRequire as createRequire3 } from "module";
7634
7776
  import path11 from "path";
7635
7777
  function createSemanticIndexProfile(provider2) {
@@ -7643,7 +7785,7 @@ function stateKey(dataDir, profile) {
7643
7785
  return `${path11.resolve(dataDir)}::${profile.key}`;
7644
7786
  }
7645
7787
  function tableName(profile) {
7646
- const digest2 = createHash3("sha256").update(profile.key).digest("hex").slice(0, 20);
7788
+ const digest2 = createHash4("sha256").update(profile.key).digest("hex").slice(0, 20);
7647
7789
  return `${TABLE_PREFIX}${digest2}`;
7648
7790
  }
7649
7791
  function indexRoot(dataDir) {
@@ -8985,6 +9127,7 @@ async function searchObservations(options) {
8985
9127
  };
8986
9128
  let queryVector = null;
8987
9129
  let embeddingProviderForSearch = null;
9130
+ let embeddingUnavailable = false;
8988
9131
  if (quality !== "fast" && embeddingEnabled && hasQuery && tier !== "fast") {
8989
9132
  try {
8990
9133
  const provider2 = await getEmbeddingProvider();
@@ -8992,6 +9135,7 @@ async function searchObservations(options) {
8992
9135
  embeddingProviderForSearch = provider2;
8993
9136
  const activeVectorDimensions = getVectorDimensions();
8994
9137
  if (activeVectorDimensions !== null && provider2.dimensions !== activeVectorDimensions) {
9138
+ embeddingUnavailable = true;
8995
9139
  rememberSearchMode(
8996
9140
  modeKey,
8997
9141
  `fulltext (embedding dimension mismatch: provider ${provider2.dimensions}d vs index ${activeVectorDimensions}d)`
@@ -9031,8 +9175,11 @@ async function searchObservations(options) {
9031
9175
  hybridWeights: isCJKHeavy ? { text: 0.2, vector: 0.8 } : { text: 0.6, vector: 0.4 }
9032
9176
  };
9033
9177
  }
9178
+ } else {
9179
+ embeddingUnavailable = true;
9034
9180
  }
9035
9181
  } catch (error) {
9182
+ embeddingUnavailable = true;
9036
9183
  rememberSearchMode(modeKey, "fulltext (embedding unavailable)");
9037
9184
  console.error("[memorix] Embedding failed or timed out, falling back to fulltext search");
9038
9185
  }
@@ -9040,7 +9187,7 @@ async function searchObservations(options) {
9040
9187
  mark("preSearch");
9041
9188
  let results;
9042
9189
  const persistentCorpusEligible = await shouldUsePersistentSearch(projectIds, options.projectId);
9043
- const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled);
9190
+ const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled || embeddingUnavailable);
9044
9191
  let persistent = persistentCorpusEligible && queryVector && embeddingProviderForSearch ? await searchPersistentSemantically(options, projectIds, requestLimit, queryVector, embeddingProviderForSearch) : canUsePersistentLexical ? await searchPersistentLexically(options, projectIds, requestLimit) : null;
9045
9192
  const durableStore = getOptionalObservationStore();
9046
9193
  if (!persistent && queryVector && durableStore?.hasLexicalIndex?.() && count(database) === 0) {
@@ -9686,6 +9833,7 @@ var init_secret_filter = __esm({
9686
9833
  // src/runtime/maintenance-jobs.ts
9687
9834
  var maintenance_jobs_exports = {};
9688
9835
  __export(maintenance_jobs_exports, {
9836
+ DEFAULT_FAILED_HISTORY_RETENTION_MS: () => DEFAULT_FAILED_HISTORY_RETENTION_MS,
9689
9837
  MAINTENANCE_JOB_KINDS: () => MAINTENANCE_JOB_KINDS,
9690
9838
  MaintenanceJobStore: () => MaintenanceJobStore,
9691
9839
  MaintenanceJobWorker: () => MaintenanceJobWorker,
@@ -9735,7 +9883,7 @@ function normalizedKinds(kinds) {
9735
9883
  function getMaintenanceRetryDelayMs(attempts) {
9736
9884
  return Math.min(6e4, 1e3 * 2 ** Math.max(0, attempts - 1));
9737
9885
  }
9738
- var MAINTENANCE_JOB_KINDS, DEFAULT_MAX_ATTEMPTS, DEFAULT_LEASE_MS, DEFAULT_POLL_INTERVAL_MS, MAX_ERROR_LENGTH, DEFAULT_COMPLETED_HISTORY_RETENTION_MS, MaintenanceJobStore, MaintenanceJobWorker;
9886
+ 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;
9739
9887
  var init_maintenance_jobs = __esm({
9740
9888
  "src/runtime/maintenance-jobs.ts"() {
9741
9889
  "use strict";
@@ -9762,6 +9910,7 @@ var init_maintenance_jobs = __esm({
9762
9910
  DEFAULT_POLL_INTERVAL_MS = 2e3;
9763
9911
  MAX_ERROR_LENGTH = 1e3;
9764
9912
  DEFAULT_COMPLETED_HISTORY_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
9913
+ DEFAULT_FAILED_HISTORY_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
9765
9914
  MaintenanceJobStore = class {
9766
9915
  db;
9767
9916
  constructor(dataDir) {
@@ -9888,18 +10037,21 @@ var init_maintenance_jobs = __esm({
9888
10037
  }
9889
10038
  /**
9890
10039
  * Completed jobs are useful recent operator history, not permanent data.
9891
- * Failed jobs stay available for diagnosis until an operator explicitly
9892
- * resolves them or clears the database.
10040
+ * Failed diagnostics are retained longer, but are still bounded so a
10041
+ * permanently failing job kind cannot grow the database forever.
9893
10042
  */
9894
10043
  pruneCompletedHistory(options = {}) {
9895
10044
  const now3 = options.now ?? Date.now();
9896
10045
  const maxAgeMs = Number.isFinite(options.maxAgeMs) ? Math.max(0, Math.floor(options.maxAgeMs)) : DEFAULT_COMPLETED_HISTORY_RETENTION_MS;
10046
+ const failedMaxAgeMs = Number.isFinite(options.failedMaxAgeMs) ? Math.max(0, Math.floor(options.failedMaxAgeMs)) : DEFAULT_FAILED_HISTORY_RETENTION_MS;
9897
10047
  const result = this.db.prepare(`
9898
10048
  DELETE FROM maintenance_jobs
9899
- WHERE status = 'completed'
10049
+ WHERE (status = 'completed'
9900
10050
  AND completed_at IS NOT NULL
9901
- AND completed_at < ?
9902
- `).run(now3 - maxAgeMs);
10051
+ AND completed_at < ?)
10052
+ OR (status = 'failed'
10053
+ AND updated_at < ?)
10054
+ `).run(now3 - maxAgeMs, now3 - failedMaxAgeMs);
9903
10055
  return Number(result.changes ?? 0);
9904
10056
  }
9905
10057
  claimNext(options) {
@@ -10253,7 +10405,7 @@ var init_lifecycle = __esm({
10253
10405
  });
10254
10406
 
10255
10407
  // src/store/evidence-store.ts
10256
- import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
10408
+ import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
10257
10409
  function safeJson(value, fallback) {
10258
10410
  if (typeof value !== "string" || value.length === 0) return fallback;
10259
10411
  try {
@@ -10287,7 +10439,7 @@ function rowToCard(row) {
10287
10439
  };
10288
10440
  }
10289
10441
  function observationHash(observation) {
10290
- return createHash4("sha256").update(JSON.stringify({
10442
+ return createHash5("sha256").update(JSON.stringify({
10291
10443
  projectId: observation.projectId,
10292
10444
  id: observation.id,
10293
10445
  title: observation.title,
@@ -10881,13 +11033,13 @@ __export(workspace_exports, {
10881
11033
  loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
10882
11034
  resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
10883
11035
  });
10884
- import { createHash as createHash5 } from "crypto";
11036
+ import { createHash as createHash6 } from "crypto";
10885
11037
  import { execFile } from "child_process";
10886
11038
  import { promises as fs9 } from "fs";
10887
11039
  import path14 from "path";
10888
11040
  import { promisify } from "util";
10889
11041
  function hash(value) {
10890
- return createHash5("sha256").update(value).digest("hex");
11042
+ return createHash6("sha256").update(value).digest("hex");
10891
11043
  }
10892
11044
  function workspaceId(projectId, mode) {
10893
11045
  return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
@@ -12040,14 +12192,14 @@ __export(asset_store_exports, {
12040
12192
  readMediaAsset: () => readMediaAsset,
12041
12193
  removeMediaAsset: () => removeMediaAsset
12042
12194
  });
12043
- import { createHash as createHash6, randomUUID as randomUUID6 } from "crypto";
12195
+ import { createHash as createHash7, randomUUID as randomUUID6 } from "crypto";
12044
12196
  import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
12045
12197
  import path15 from "path";
12046
12198
  function mediaRoot(dataDir) {
12047
12199
  return path15.resolve(dataDir, "media");
12048
12200
  }
12049
12201
  function projectStorageKey(projectId) {
12050
- return createHash6("sha256").update(projectId).digest("hex").slice(0, 16);
12202
+ return createHash7("sha256").update(projectId).digest("hex").slice(0, 16);
12051
12203
  }
12052
12204
  function validateUnderRoot(root, candidate) {
12053
12205
  const resolved = path15.resolve(candidate);
@@ -12107,7 +12259,7 @@ async function readHeader(filePath, bytes = 32) {
12107
12259
  }
12108
12260
  async function sha256File(filePath) {
12109
12261
  const bytes = await readFile3(filePath);
12110
- return createHash6("sha256").update(bytes).digest("hex");
12262
+ return createHash7("sha256").update(bytes).digest("hex");
12111
12263
  }
12112
12264
  function destinationFor(root, projectId, sha256, extension3) {
12113
12265
  const relative5 = path15.join("assets", projectStorageKey(projectId), sha256.slice(0, 2), `${sha256}${extension3}`);
@@ -12213,7 +12365,7 @@ async function importMediaBuffer(input) {
12213
12365
  if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
12214
12366
  if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
12215
12367
  const detected = detectMedia(input.bytes.subarray(0, 32));
12216
- const sha256 = createHash6("sha256").update(input.bytes).digest("hex");
12368
+ const sha256 = createHash7("sha256").update(input.bytes).digest("hex");
12217
12369
  return importKnownMedia({
12218
12370
  dataDir: input.dataDir,
12219
12371
  projectId: input.projectId,
@@ -12231,7 +12383,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
12231
12383
  if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
12232
12384
  const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
12233
12385
  if (bytes.length !== asset.byteSize) throw new Error(`Media asset size no longer matches metadata: ${asset.id}`);
12234
- const sha256 = createHash6("sha256").update(bytes).digest("hex");
12386
+ const sha256 = createHash7("sha256").update(bytes).digest("hex");
12235
12387
  if (sha256 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
12236
12388
  return bytes;
12237
12389
  }
@@ -65101,7 +65253,7 @@ var require_gaxios = __commonJS({
65101
65253
  var retry_js_1 = require_retry3();
65102
65254
  var stream_1 = __require("stream");
65103
65255
  var interceptor_js_1 = require_interceptor();
65104
- var randomUUID19 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
65256
+ var randomUUID20 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
65105
65257
  var HTTP_STATUS_NO_CONTENT = 204;
65106
65258
  var Gaxios = class {
65107
65259
  agentCache = /* @__PURE__ */ new Map();
@@ -65374,7 +65526,7 @@ var require_gaxios = __commonJS({
65374
65526
  */
65375
65527
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
65376
65528
  if (opts.multipart?.length) {
65377
- const boundary = await randomUUID19();
65529
+ const boundary = await randomUUID20();
65378
65530
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
65379
65531
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
65380
65532
  } else if (shouldDirectlyPassData) {
@@ -71046,16 +71198,16 @@ var require_jwtaccess = __commonJS({
71046
71198
  * @returns A string that returns the cached key.
71047
71199
  */
71048
71200
  getCachedKey(url, scopes) {
71049
- let cacheKey = url;
71201
+ let cacheKey3 = url;
71050
71202
  if (scopes && Array.isArray(scopes) && scopes.length) {
71051
- cacheKey = url ? `${url}_${scopes.join("_")}` : `${scopes.join("_")}`;
71203
+ cacheKey3 = url ? `${url}_${scopes.join("_")}` : `${scopes.join("_")}`;
71052
71204
  } else if (typeof scopes === "string") {
71053
- cacheKey = url ? `${url}_${scopes}` : scopes;
71205
+ cacheKey3 = url ? `${url}_${scopes}` : scopes;
71054
71206
  }
71055
- if (!cacheKey) {
71207
+ if (!cacheKey3) {
71056
71208
  throw Error("Scopes or url must be provided");
71057
71209
  }
71058
- return cacheKey;
71210
+ return cacheKey3;
71059
71211
  }
71060
71212
  /**
71061
71213
  * Get a non-expired access token, after refreshing if necessary.
@@ -77601,7 +77753,7 @@ var require_websocket = __commonJS({
77601
77753
  var http3 = __require("http");
77602
77754
  var net = __require("net");
77603
77755
  var tls = __require("tls");
77604
- var { randomBytes: randomBytes2, createHash: createHash19 } = __require("crypto");
77756
+ var { randomBytes: randomBytes2, createHash: createHash20 } = __require("crypto");
77605
77757
  var { Duplex, Readable: Readable2 } = __require("stream");
77606
77758
  var { URL: URL2 } = __require("url");
77607
77759
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -78269,7 +78421,7 @@ var require_websocket = __commonJS({
78269
78421
  abortHandshake(websocket, socket, "Invalid Upgrade header");
78270
78422
  return;
78271
78423
  }
78272
- const digest2 = createHash19("sha1").update(key + GUID).digest("base64");
78424
+ const digest2 = createHash20("sha1").update(key + GUID).digest("base64");
78273
78425
  if (res.headers["sec-websocket-accept"] !== digest2) {
78274
78426
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
78275
78427
  return;
@@ -78641,7 +78793,7 @@ var require_websocket_server = __commonJS({
78641
78793
  var EventEmitter2 = __require("events");
78642
78794
  var http3 = __require("http");
78643
78795
  var { Duplex } = __require("stream");
78644
- var { createHash: createHash19 } = __require("crypto");
78796
+ var { createHash: createHash20 } = __require("crypto");
78645
78797
  var extension3 = require_extension();
78646
78798
  var PerMessageDeflate2 = require_permessage_deflate();
78647
78799
  var subprotocol2 = require_subprotocol();
@@ -78948,7 +79100,7 @@ var require_websocket_server = __commonJS({
78948
79100
  );
78949
79101
  }
78950
79102
  if (this._state > RUNNING) return abortHandshake(socket, 503);
78951
- const digest2 = createHash19("sha1").update(key + GUID).digest("base64");
79103
+ const digest2 = createHash20("sha1").update(key + GUID).digest("base64");
78952
79104
  const headers = [
78953
79105
  "HTTP/1.1 101 Switching Protocols",
78954
79106
  "Upgrade: websocket",
@@ -143007,9 +143159,9 @@ var init_union_priority_sort = __esm({
143007
143159
  // node_modules/typebox/build/value/clean/from_union.mjs
143008
143160
  function FromUnion9(context, type, value) {
143009
143161
  for (const schema of UnionPrioritySort(type.anyOf)) {
143010
- const clean = FromType19(context, schema, Clone2(value));
143011
- if (Check2(context, schema, clean))
143012
- return clean;
143162
+ const clean2 = FromType19(context, schema, Clone2(value));
143163
+ if (Check2(context, schema, clean2))
143164
+ return clean2;
143013
143165
  }
143014
143166
  return value;
143015
143167
  }
@@ -147647,9 +147799,9 @@ var init_long_term = __esm({
147647
147799
  });
147648
147800
 
147649
147801
  // src/codegraph/ids.ts
147650
- import { createHash as createHash7 } from "crypto";
147802
+ import { createHash as createHash8 } from "crypto";
147651
147803
  function digest(input) {
147652
- return createHash7("sha256").update(input).digest("hex").slice(0, 16);
147804
+ return createHash8("sha256").update(input).digest("hex").slice(0, 16);
147653
147805
  }
147654
147806
  function normalizeCodePath(path38) {
147655
147807
  return path38.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
@@ -148697,7 +148849,7 @@ var init_store = __esm({
148697
148849
 
148698
148850
  // src/codegraph/code-state.ts
148699
148851
  import { execFile as execFile2 } from "child_process";
148700
- import { createHash as createHash8 } from "crypto";
148852
+ import { createHash as createHash9 } from "crypto";
148701
148853
  import { promisify as promisify3 } from "util";
148702
148854
  async function runGit(rootPath, args) {
148703
148855
  try {
@@ -148726,7 +148878,7 @@ function changedPathCount(status) {
148726
148878
  return count2;
148727
148879
  }
148728
148880
  function fingerprint(baseRevision, status) {
148729
- return createHash8("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
148881
+ return createHash9("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
148730
148882
  }
148731
148883
  async function collectCodeStateSnapshot(input) {
148732
148884
  const [revision, status] = await Promise.all([
@@ -148820,12 +148972,12 @@ var init_exclude3 = __esm({
148820
148972
  });
148821
148973
 
148822
148974
  // src/codegraph/semantic-provider.ts
148823
- import { createHash as createHash9 } from "crypto";
148975
+ import { createHash as createHash10 } from "crypto";
148824
148976
  import "fs";
148825
148977
  import { resolve } from "path";
148826
148978
  import ts from "typescript";
148827
148979
  function hashText(value) {
148828
- return createHash9("sha256").update(value).digest("hex");
148980
+ return createHash10("sha256").update(value).digest("hex");
148829
148981
  }
148830
148982
  function canonicalPath(value) {
148831
148983
  const normalized = resolve(value).replace(/\\/g, "/");
@@ -149170,11 +149322,11 @@ __export(lite_provider_exports, {
149170
149322
  indexProjectLite: () => indexProjectLite,
149171
149323
  refreshProjectLite: () => refreshProjectLite
149172
149324
  });
149173
- import { createHash as createHash10 } from "crypto";
149325
+ import { createHash as createHash11 } from "crypto";
149174
149326
  import { existsSync as existsSync12, readdirSync as readdirSync2, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
149175
149327
  import { join as join4, relative as relative2 } from "path";
149176
149328
  function hashText2(text3) {
149177
- return createHash10("sha256").update(text3).digest("hex");
149329
+ return createHash11("sha256").update(text3).digest("hex");
149178
149330
  }
149179
149331
  function extension2(path38) {
149180
149332
  const index = path38.lastIndexOf(".");
@@ -149807,7 +149959,7 @@ var claim_store_exports = {};
149807
149959
  __export(claim_store_exports, {
149808
149960
  ClaimStore: () => ClaimStore
149809
149961
  });
149810
- import { createHash as createHash11, randomUUID as randomUUID12 } from "crypto";
149962
+ import { createHash as createHash12, randomUUID as randomUUID12 } from "crypto";
149811
149963
  function optionalText3(value) {
149812
149964
  return typeof value === "string" && value ? value : void 0;
149813
149965
  }
@@ -149860,7 +150012,7 @@ function rowToEvent3(row) {
149860
150012
  };
149861
150013
  }
149862
150014
  function evidenceKey(input) {
149863
- return createHash11("sha256").update(JSON.stringify([
150015
+ return createHash12("sha256").update(JSON.stringify([
149864
150016
  input.evidenceKind,
149865
150017
  input.evidenceId,
149866
150018
  input.relation,
@@ -150058,7 +150210,7 @@ __export(claims_exports, {
150058
150210
  supersedeClaim: () => supersedeClaim,
150059
150211
  writeClaim: () => writeClaim
150060
150212
  });
150061
- import { createHash as createHash12, randomUUID as randomUUID13 } from "crypto";
150213
+ import { createHash as createHash13, randomUUID as randomUUID13 } from "crypto";
150062
150214
  function compactText2(value, field) {
150063
150215
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
150064
150216
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
@@ -150070,7 +150222,7 @@ function normalizedTerm(value) {
150070
150222
  return compactText2(value, "identity").toLocaleLowerCase("en-US");
150071
150223
  }
150072
150224
  function hashIdentity(parts) {
150073
- return createHash12("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
150225
+ return createHash13("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
150074
150226
  }
150075
150227
  function buildClaimIdentity(input) {
150076
150228
  const subject = normalizedTerm(input.subject);
@@ -150311,7 +150463,7 @@ function supersedeClaim(store, input) {
150311
150463
  });
150312
150464
  }
150313
150465
  function hashObservation(observation) {
150314
- return createHash12("sha256").update(JSON.stringify([
150466
+ return createHash13("sha256").update(JSON.stringify([
150315
150467
  observation.title,
150316
150468
  observation.narrative,
150317
150469
  observation.facts,
@@ -150555,11 +150707,11 @@ var init_frontmatter = __esm({
150555
150707
  });
150556
150708
 
150557
150709
  // src/knowledge/markdown.ts
150558
- import { createHash as createHash13 } from "crypto";
150710
+ import { createHash as createHash14 } from "crypto";
150559
150711
  import { promises as fs13 } from "fs";
150560
150712
  import path22 from "path";
150561
150713
  function hashContent(content) {
150562
- return createHash13("sha256").update(content).digest("hex");
150714
+ return createHash14("sha256").update(content).digest("hex");
150563
150715
  }
150564
150716
  function stringField(data, key) {
150565
150717
  const value = data[key];
@@ -150685,11 +150837,11 @@ __export(wiki_exports, {
150685
150837
  lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
150686
150838
  readKnowledgePage: () => readKnowledgePage
150687
150839
  });
150688
- import { createHash as createHash14 } from "crypto";
150840
+ import { createHash as createHash15 } from "crypto";
150689
150841
  import { promises as fs14 } from "fs";
150690
150842
  import path23 from "path";
150691
150843
  function hash2(value) {
150692
- return createHash14("sha256").update(value).digest("hex");
150844
+ return createHash15("sha256").update(value).digest("hex");
150693
150845
  }
150694
150846
  function now() {
150695
150847
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -151908,11 +152060,11 @@ __export(workflows_exports, {
151908
152060
  syncCanonicalWorkflows: () => syncCanonicalWorkflows,
151909
152061
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
151910
152062
  });
151911
- import { createHash as createHash15 } from "crypto";
152063
+ import { createHash as createHash16 } from "crypto";
151912
152064
  import { promises as fs15 } from "fs";
151913
152065
  import path24 from "path";
151914
152066
  function hash3(value) {
151915
- return createHash15("sha256").update(value).digest("hex");
152067
+ return createHash16("sha256").update(value).digest("hex");
151916
152068
  }
151917
152069
  function now2() {
151918
152070
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -156273,9 +156425,9 @@ var init_graph_context = __esm({
156273
156425
  });
156274
156426
 
156275
156427
  // src/rules/utils.ts
156276
- import { createHash as createHash16 } from "crypto";
156428
+ import { createHash as createHash17 } from "crypto";
156277
156429
  function hashContent2(content) {
156278
- return createHash16("sha256").update(content.trim()).digest("hex").substring(0, 16);
156430
+ return createHash17("sha256").update(content.trim()).digest("hex").substring(0, 16);
156279
156431
  }
156280
156432
  function generateRuleId(source, filePath) {
156281
156433
  const sanitized = filePath.replace(/[\/\\]/g, "-").replace(/^\./, "");
@@ -158718,6 +158870,7 @@ var init_tool_profile = __esm({
158718
158870
  // keep the default micro profile at its nine-tool token budget.
158719
158871
  memorix_evidence: ["lite", "team", "full"],
158720
158872
  memorix_feedback: ["lite", "team", "full"],
158873
+ memorix_continuity: ["lite", "team", "full"],
158721
158874
  // ── lite: extended solo memory surface ────────────────────────────
158722
158875
  memorix_graph_context: ["lite", "team", "full"],
158723
158876
  memorix_timeline: ["lite", "team", "full"],
@@ -159951,7 +160104,7 @@ var init_request_context = __esm({
159951
160104
  function getMcpServerInfo() {
159952
160105
  return {
159953
160106
  name: "memorix",
159954
- version: true ? "1.9.5" : "1.0.1"
160107
+ version: true ? "1.9.7" : "1.0.1"
159955
160108
  };
159956
160109
  }
159957
160110
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -160040,8 +160193,8 @@ function legacyBehavior(homeDir) {
160040
160193
  function getBehaviorConfig(options = {}) {
160041
160194
  const homeDir = options.homeDir ?? homedir22();
160042
160195
  const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
160043
- const cacheKey = `${homeDir}\0${projectRoot ?? ""}`;
160044
- const cached = cache4.get(cacheKey);
160196
+ const cacheKey3 = `${homeDir}\0${projectRoot ?? ""}`;
160197
+ const cached = cache4.get(cacheKey3);
160045
160198
  if (cached) return cached;
160046
160199
  const legacy = legacyBehavior(homeDir);
160047
160200
  let resolved = {};
@@ -160055,7 +160208,7 @@ function getBehaviorConfig(options = {}) {
160055
160208
  autoCleanup: typeof resolved.autoCleanup === "boolean" ? resolved.autoCleanup : typeof legacy.autoCleanup === "boolean" ? legacy.autoCleanup : DEFAULTS.autoCleanup,
160056
160209
  formationMode: isFormationMode(resolved.formation) ? resolved.formation : isFormationMode(legacy.formationMode) ? legacy.formationMode : DEFAULTS.formationMode
160057
160210
  };
160058
- cache4.set(cacheKey, config);
160211
+ cache4.set(cacheKey3, config);
160059
160212
  return config;
160060
160213
  }
160061
160214
  function resetBehaviorConfigCache() {
@@ -160660,6 +160813,7 @@ function freshnessForMemory(status) {
160660
160813
  return "unknown";
160661
160814
  }
160662
160815
  function receiptOmissionKind(raw) {
160816
+ if (raw.includes("continuity")) return "continuity";
160663
160817
  if (raw.includes("continuation")) return "continuation";
160664
160818
  if (raw.includes("task")) return "task";
160665
160819
  if (raw.includes("fact")) return "current-fact";
@@ -160770,6 +160924,77 @@ function renderTaskWorksetPrompt(input) {
160770
160924
  trust: "source-backed"
160771
160925
  });
160772
160926
  appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
160927
+ if (input.continuity) {
160928
+ const continuity = input.continuity;
160929
+ appendLine(lines, "", maxTokens, omitted, "continuity-heading");
160930
+ appendLine(lines, "Task continuity", maxTokens, omitted, "continuity-heading", selected, {
160931
+ kind: "continuity",
160932
+ id: "continuity:" + continuity.taskId,
160933
+ reason: "explicit task continuity ledger",
160934
+ trust: "source-backed"
160935
+ });
160936
+ appendLine(
160937
+ lines,
160938
+ "- Status: " + continuity.status + " | " + short(continuity.task, 30),
160939
+ maxTokens,
160940
+ omitted,
160941
+ "continuity-task"
160942
+ );
160943
+ appendLine(
160944
+ lines,
160945
+ "- Outcome: " + continuity.outcome.state,
160946
+ maxTokens,
160947
+ omitted,
160948
+ "continuity-outcome",
160949
+ selected,
160950
+ {
160951
+ kind: "continuity",
160952
+ id: "continuity:" + continuity.taskId,
160953
+ reason: "deterministic task outcome projection",
160954
+ trust: "derived"
160955
+ }
160956
+ );
160957
+ for (const requirement of continuity.requirements.slice(0, 3)) {
160958
+ appendLine(lines, "- Requirement: " + short(requirement, 24), maxTokens, omitted, "continuity-requirement", selected, {
160959
+ kind: "continuity",
160960
+ id: "continuity:" + continuity.taskId,
160961
+ reason: "explicit task requirement",
160962
+ trust: "source-backed"
160963
+ });
160964
+ }
160965
+ for (const decision of continuity.decisions.slice(0, 2)) {
160966
+ appendLine(lines, "- Decision: " + short(decision, 24), maxTokens, omitted, "continuity-decision", selected, {
160967
+ kind: "continuity",
160968
+ id: "continuity:" + continuity.taskId,
160969
+ reason: "explicit task decision",
160970
+ trust: "source-backed"
160971
+ });
160972
+ }
160973
+ for (const verification of continuity.verification.slice(-3)) {
160974
+ appendLine(
160975
+ lines,
160976
+ "- Verify [" + verification.status + "]: " + short(verification.content, 22),
160977
+ maxTokens,
160978
+ omitted,
160979
+ "continuity-verification",
160980
+ selected,
160981
+ {
160982
+ kind: "continuity",
160983
+ id: "continuity:" + continuity.taskId,
160984
+ reason: "task verification state",
160985
+ trust: "source-backed"
160986
+ }
160987
+ );
160988
+ }
160989
+ for (const risk of continuity.risks.slice(0, 2)) {
160990
+ appendLine(lines, "- Risk: " + short(risk, 22), maxTokens, omitted, "continuity-risk", selected, {
160991
+ kind: "continuity",
160992
+ id: "continuity:" + continuity.taskId,
160993
+ reason: "open task risk",
160994
+ trust: "source-backed"
160995
+ });
160996
+ }
160997
+ }
160773
160998
  if (input.alwaysOn) {
160774
160999
  const hasAlwaysOn = input.alwaysOn.profile.length > 0 || Boolean(input.alwaysOn.state) || input.alwaysOn.durable.length > 0;
160775
161000
  if (hasAlwaysOn) {
@@ -161293,6 +161518,7 @@ async function buildTaskWorkset(input) {
161293
161518
  version: "1.3",
161294
161519
  task,
161295
161520
  lens: input.lens,
161521
+ ...input.continuity ? { continuity: input.continuity } : {},
161296
161522
  ...input.alwaysOn && (input.alwaysOn.profile.length > 0 || input.alwaysOn.state || input.alwaysOn.durable.length > 0) ? { alwaysOn: input.alwaysOn } : {},
161297
161523
  currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
161298
161524
  ...continuation ? { continuation } : {},
@@ -162677,7 +162903,7 @@ var compaction_checkpoint_store_exports = {};
162677
162903
  __export(compaction_checkpoint_store_exports, {
162678
162904
  CompactionCheckpointStore: () => CompactionCheckpointStore
162679
162905
  });
162680
- import { createHash as createHash17, randomUUID as randomUUID16 } from "crypto";
162906
+ import { createHash as createHash18, randomUUID as randomUUID16 } from "crypto";
162681
162907
  function optionalText7(value) {
162682
162908
  return typeof value === "string" && value ? value : void 0;
162683
162909
  }
@@ -162719,7 +162945,7 @@ function sourceKeyFor(input, summary) {
162719
162945
  input.tokensBefore ?? "",
162720
162946
  input.firstKeptEntryId ?? ""
162721
162947
  ].join("\0");
162722
- return `derived:${createHash17("sha256").update(material).digest("hex").slice(0, 32)}`;
162948
+ return `derived:${createHash18("sha256").update(material).digest("hex").slice(0, 32)}`;
162723
162949
  }
162724
162950
  function rowToCheckpoint(row) {
162725
162951
  return {
@@ -162977,6 +163203,305 @@ var init_compaction_checkpoint_store = __esm({
162977
163203
  }
162978
163204
  });
162979
163205
 
163206
+ // src/knowledge/task-continuity.ts
163207
+ var task_continuity_exports = {};
163208
+ __export(task_continuity_exports, {
163209
+ TASK_CONTINUITY_EVENT_KINDS: () => TASK_CONTINUITY_EVENT_KINDS,
163210
+ TaskContinuityStore: () => TaskContinuityStore,
163211
+ evaluateTaskContinuity: () => evaluateTaskContinuity
163212
+ });
163213
+ import { randomUUID as randomUUID17 } from "crypto";
163214
+ function clean(value, maxLength = MAX_TEXT_LENGTH2) {
163215
+ return sanitizeCredentials(value).replace(/\s+/g, " ").trim().slice(0, maxLength);
163216
+ }
163217
+ function cleanList(values, maxLength = MAX_TEXT_LENGTH2) {
163218
+ return [...new Set((values ?? []).map((value) => clean(value, maxLength)).filter(Boolean))].slice(0, MAX_LIST_ITEMS2);
163219
+ }
163220
+ function parseRefs(value) {
163221
+ if (typeof value !== "string") return [];
163222
+ try {
163223
+ const parsed = JSON.parse(value);
163224
+ return Array.isArray(parsed) ? cleanList(parsed.filter((item) => typeof item === "string"), 500).slice(0, MAX_EVIDENCE_REFS) : [];
163225
+ } catch {
163226
+ return [];
163227
+ }
163228
+ }
163229
+ function evaluateTaskContinuity(ledger) {
163230
+ const verification = {
163231
+ passed: ledger.verification.filter((item) => item.status === "passed").length,
163232
+ failed: ledger.verification.filter((item) => item.status === "failed").length,
163233
+ pending: ledger.verification.filter((item) => item.status === "pending").length,
163234
+ skipped: ledger.verification.filter((item) => item.status === "skipped").length
163235
+ };
163236
+ const reasons = [];
163237
+ if (verification.failed > 0) reasons.push("verification-failed");
163238
+ if (verification.pending > 0) reasons.push("verification-pending");
163239
+ if (ledger.risks.length > 0) reasons.push("open-risks");
163240
+ if (ledger.status === "blocked") reasons.push("task-blocked");
163241
+ if (ledger.status === "abandoned") reasons.push("task-abandoned");
163242
+ if (ledger.status === "blocked" || ledger.status === "abandoned" || verification.failed > 0) {
163243
+ return {
163244
+ state: "at-risk",
163245
+ score: verification.passed > verification.failed ? 0.55 : 0.2,
163246
+ verification,
163247
+ reasons
163248
+ };
163249
+ }
163250
+ if (ledger.status === "completed") {
163251
+ if (verification.passed > 0 && verification.pending === 0 && verification.skipped === 0) {
163252
+ return {
163253
+ state: ledger.risks.length > 0 ? "validated-with-risks" : "validated",
163254
+ score: ledger.risks.length > 0 ? 0.8 : 1,
163255
+ verification,
163256
+ reasons: reasons.length > 0 ? reasons : ["verification-passed"]
163257
+ };
163258
+ }
163259
+ return {
163260
+ state: "unverified",
163261
+ score: 0.4,
163262
+ verification,
163263
+ reasons: reasons.length > 0 ? reasons : ["no-passing-verification"]
163264
+ };
163265
+ }
163266
+ return {
163267
+ state: "in-progress",
163268
+ score: verification.passed > 0 && verification.pending === 0 ? 0.75 : 0.5,
163269
+ verification,
163270
+ reasons: reasons.length > 0 ? reasons : ["task-open"]
163271
+ };
163272
+ }
163273
+ function rowToEvent4(row) {
163274
+ const status = row.item_status ? String(row.item_status) : void 0;
163275
+ return {
163276
+ id: String(row.id),
163277
+ projectId: String(row.project_id),
163278
+ taskId: String(row.task_id),
163279
+ task: clean(String(row.task), MAX_TASK_LENGTH),
163280
+ kind: row.kind,
163281
+ content: clean(String(row.content)),
163282
+ ...status ? { status } : {},
163283
+ ...row.source_ref ? { sourceRef: clean(String(row.source_ref), 500) } : {},
163284
+ evidenceRefs: parseRefs(row.evidence_json),
163285
+ ...row.actor ? { actor: clean(String(row.actor), 200) } : {},
163286
+ at: String(row.created_at),
163287
+ ...row.event_key ? { eventKey: clean(String(row.event_key), 200) } : {},
163288
+ ...row.item_key ? { itemKey: clean(String(row.item_key), 120) } : {},
163289
+ ...row.supersedes_id ? { supersedesId: clean(String(row.supersedes_id), 120) } : {}
163290
+ };
163291
+ }
163292
+ function aggregate(events) {
163293
+ const first2 = events.find((event) => event.kind === "task") ?? events[0];
163294
+ const latestOutcome = [...events].reverse().find((event) => event.kind === "outcome");
163295
+ const verificationByKey = /* @__PURE__ */ new Map();
163296
+ for (const event of events.filter((event2) => event2.kind === "verification")) {
163297
+ verificationByKey.set(event.itemKey ?? `event:${event.id}`, event);
163298
+ }
163299
+ const ledger = {
163300
+ taskId: first2.taskId,
163301
+ projectId: first2.projectId,
163302
+ task: first2.task,
163303
+ status: latestOutcome?.status ?? "open",
163304
+ requirements: events.filter((event) => event.kind === "requirement").map((event) => event.content),
163305
+ decisions: events.filter((event) => event.kind === "decision").map((event) => event.content),
163306
+ verification: [...verificationByKey.values()].map((event) => ({
163307
+ id: event.itemKey ?? event.id,
163308
+ content: event.content,
163309
+ status: event.status ?? "pending",
163310
+ ...event.sourceRef ? { sourceRef: event.sourceRef } : {},
163311
+ evidenceRefs: event.evidenceRefs
163312
+ })),
163313
+ risks: events.filter((event) => event.kind === "risk").map((event) => event.content),
163314
+ outcomes: events.filter((event) => event.kind === "outcome").map((event) => event.content),
163315
+ updatedAt: events[events.length - 1].at,
163316
+ events
163317
+ };
163318
+ return { ...ledger, outcome: evaluateTaskContinuity(ledger) };
163319
+ }
163320
+ var TASK_CONTINUITY_EVENT_KINDS, MAX_TEXT_LENGTH2, MAX_TASK_LENGTH, MAX_LIST_ITEMS2, MAX_EVIDENCE_REFS, MAX_EVENTS_PER_TASK, TaskContinuityStore;
163321
+ var init_task_continuity = __esm({
163322
+ "src/knowledge/task-continuity.ts"() {
163323
+ "use strict";
163324
+ init_esm_shims();
163325
+ init_secret_filter();
163326
+ init_sqlite_db();
163327
+ TASK_CONTINUITY_EVENT_KINDS = [
163328
+ "task",
163329
+ "requirement",
163330
+ "decision",
163331
+ "verification",
163332
+ "risk",
163333
+ "outcome"
163334
+ ];
163335
+ MAX_TEXT_LENGTH2 = 2e3;
163336
+ MAX_TASK_LENGTH = 1e3;
163337
+ MAX_LIST_ITEMS2 = 8;
163338
+ MAX_EVIDENCE_REFS = 8;
163339
+ MAX_EVENTS_PER_TASK = 200;
163340
+ TaskContinuityStore = class {
163341
+ db = null;
163342
+ async init(dataDir) {
163343
+ this.db = getDatabase(dataDir);
163344
+ }
163345
+ requireDb() {
163346
+ if (!this.db) throw new Error("TaskContinuityStore is not initialized.");
163347
+ return this.db;
163348
+ }
163349
+ insert(input) {
163350
+ const db2 = this.requireDb();
163351
+ const projectId = clean(input.projectId, 500);
163352
+ const taskId = clean(input.taskId, 120);
163353
+ const eventKey = input.eventKey ? clean(input.eventKey, 200) : void 0;
163354
+ if (eventKey) {
163355
+ const existing = db2.prepare(
163356
+ "SELECT * FROM task_continuity_events WHERE project_id = ? AND task_id = ? AND event_key = ?"
163357
+ ).get(projectId, taskId, eventKey);
163358
+ if (existing) return rowToEvent4(existing);
163359
+ }
163360
+ const existingCount = db2.prepare(
163361
+ "SELECT COUNT(*) AS count FROM task_continuity_events WHERE project_id = ? AND task_id = ?"
163362
+ ).get(projectId, taskId);
163363
+ if (Number(existingCount?.count ?? 0) >= MAX_EVENTS_PER_TASK) {
163364
+ throw new Error("Task continuity ledger reached its 200-event limit.");
163365
+ }
163366
+ const event = {
163367
+ id: randomUUID17(),
163368
+ projectId,
163369
+ taskId,
163370
+ task: clean(input.task, MAX_TASK_LENGTH),
163371
+ kind: input.kind,
163372
+ content: clean(input.content),
163373
+ ...input.status ? { status: input.status } : {},
163374
+ ...input.sourceRef ? { sourceRef: clean(input.sourceRef, 500) } : {},
163375
+ evidenceRefs: cleanList(input.evidenceRefs, 500).slice(0, MAX_EVIDENCE_REFS),
163376
+ ...input.actor ? { actor: clean(input.actor, 200) } : {},
163377
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
163378
+ ...eventKey ? { eventKey } : {},
163379
+ ...input.itemKey ? { itemKey: clean(input.itemKey, 120) } : {},
163380
+ ...input.supersedesId ? { supersedesId: clean(input.supersedesId, 120) } : {}
163381
+ };
163382
+ db2.prepare(`
163383
+ INSERT INTO task_continuity_events (
163384
+ id, project_id, task_id, task, kind, content, item_status,
163385
+ source_ref, evidence_json, actor, created_at, event_key, item_key, supersedes_id
163386
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
163387
+ `).run(
163388
+ event.id,
163389
+ event.projectId,
163390
+ event.taskId,
163391
+ event.task,
163392
+ event.kind,
163393
+ event.content,
163394
+ event.status ?? null,
163395
+ event.sourceRef ?? null,
163396
+ JSON.stringify(event.evidenceRefs),
163397
+ event.actor ?? null,
163398
+ event.at,
163399
+ event.eventKey ?? null,
163400
+ event.itemKey ?? null,
163401
+ event.supersedesId ?? null
163402
+ );
163403
+ return event;
163404
+ }
163405
+ start(input) {
163406
+ const task = clean(input.task, MAX_TASK_LENGTH);
163407
+ if (!task) throw new Error("A non-empty task is required.");
163408
+ const idempotencyKey = input.idempotencyKey ? clean(input.idempotencyKey, 200) : void 0;
163409
+ const taskId = clean(input.taskId ?? idempotencyKey ?? "", 120) || randomUUID17();
163410
+ const requirements = cleanList(input.requirements);
163411
+ const transaction = this.requireDb().transaction(() => {
163412
+ this.insert({
163413
+ projectId: input.projectId,
163414
+ taskId,
163415
+ task,
163416
+ kind: "task",
163417
+ content: task,
163418
+ status: "open",
163419
+ actor: input.actor,
163420
+ ...idempotencyKey ? { eventKey: `start:${idempotencyKey}` } : {}
163421
+ });
163422
+ for (const requirement of requirements) {
163423
+ this.insert({
163424
+ projectId: input.projectId,
163425
+ taskId,
163426
+ task,
163427
+ kind: "requirement",
163428
+ content: requirement,
163429
+ actor: input.actor
163430
+ });
163431
+ }
163432
+ });
163433
+ transaction.immediate();
163434
+ return { taskId, ledger: this.get(input.projectId, taskId) };
163435
+ }
163436
+ record(input) {
163437
+ const projectId = clean(input.projectId, 500);
163438
+ const taskId = clean(input.taskId, 120);
163439
+ const existing = this.get(projectId, taskId);
163440
+ if (!existing) throw new Error("Task continuity ledger not found: " + taskId);
163441
+ const transaction = this.requireDb().transaction(() => this.insert({
163442
+ projectId,
163443
+ taskId,
163444
+ task: existing.task,
163445
+ kind: input.kind,
163446
+ content: input.content,
163447
+ ...input.kind === "verification" && input.verificationStatus ? { status: input.verificationStatus } : {},
163448
+ sourceRef: input.sourceRef,
163449
+ evidenceRefs: input.evidenceRefs,
163450
+ actor: input.actor,
163451
+ at: input.at,
163452
+ ...input.idempotencyKey ? { eventKey: input.idempotencyKey } : {},
163453
+ ...input.verificationId ? { itemKey: input.verificationId } : {}
163454
+ }));
163455
+ const event = transaction.immediate();
163456
+ return { event, ledger: this.get(projectId, taskId) };
163457
+ }
163458
+ close(input) {
163459
+ const projectId = clean(input.projectId, 500);
163460
+ const taskId = clean(input.taskId, 120);
163461
+ const existing = this.get(projectId, taskId);
163462
+ if (!existing) throw new Error("Task continuity ledger not found: " + taskId);
163463
+ const transaction = this.requireDb().transaction(() => this.insert({
163464
+ projectId,
163465
+ taskId,
163466
+ task: existing.task,
163467
+ kind: "outcome",
163468
+ content: input.content,
163469
+ status: input.status,
163470
+ sourceRef: input.sourceRef,
163471
+ evidenceRefs: input.evidenceRefs,
163472
+ actor: input.actor,
163473
+ at: input.at,
163474
+ ...input.idempotencyKey ? { eventKey: input.idempotencyKey } : {}
163475
+ }));
163476
+ const event = transaction.immediate();
163477
+ return { event, ledger: this.get(projectId, taskId) };
163478
+ }
163479
+ get(projectId, taskId) {
163480
+ const normalizedProjectId = clean(projectId, 500);
163481
+ const normalizedTaskId = clean(taskId, 120);
163482
+ const rows = this.requireDb().prepare(`
163483
+ SELECT * FROM task_continuity_events
163484
+ WHERE project_id = ? AND task_id = ?
163485
+ ORDER BY created_at ASC, rowid ASC
163486
+ `).all(normalizedProjectId, normalizedTaskId);
163487
+ if (rows.length === 0) return void 0;
163488
+ return aggregate(rows.map(rowToEvent4));
163489
+ }
163490
+ list(projectId, limit2 = 20) {
163491
+ const rows = this.requireDb().prepare(`
163492
+ SELECT task_id
163493
+ FROM task_continuity_events
163494
+ WHERE project_id = ?
163495
+ GROUP BY task_id
163496
+ ORDER BY MAX(created_at) DESC
163497
+ LIMIT ?
163498
+ `).all(projectId, Math.min(100, Math.max(1, Math.floor(limit2))));
163499
+ return rows.map((row) => this.get(projectId, String(row.task_id))).filter((ledger) => Boolean(ledger));
163500
+ }
163501
+ };
163502
+ }
163503
+ });
163504
+
162980
163505
  // src/codegraph/auto-context.ts
162981
163506
  var auto_context_exports = {};
162982
163507
  __export(auto_context_exports, {
@@ -163233,6 +163758,16 @@ async function buildAutoProjectContext(input) {
163233
163758
  };
163234
163759
  }
163235
163760
  }
163761
+ let continuity = input.continuity;
163762
+ if (!continuity && input.taskId) {
163763
+ try {
163764
+ const { TaskContinuityStore: TaskContinuityStore2 } = await Promise.resolve().then(() => (init_task_continuity(), task_continuity_exports));
163765
+ const continuityStore = new TaskContinuityStore2();
163766
+ await continuityStore.init(input.dataDir);
163767
+ continuity = continuityStore.get(input.project.id, input.taskId);
163768
+ } catch {
163769
+ }
163770
+ }
163236
163771
  const alwaysOn = await buildAlwaysOnBlock({
163237
163772
  dataDir: input.dataDir,
163238
163773
  projectId: input.project.id,
@@ -163251,6 +163786,7 @@ async function buildAutoProjectContext(input) {
163251
163786
  providerQuality: providerQuality2,
163252
163787
  currentFacts: worksetFactLines(currentFacts),
163253
163788
  ...continuation ? { continuation } : {},
163789
+ ...continuity ? { continuity } : {},
163254
163790
  codeState: codeStateLine(overview),
163255
163791
  ...codeEvolution ? { codeEvolution } : {},
163256
163792
  reliableMemory: sourceSets.reliableSources.slice(0, lens.sourceLimit).map((source) => ({
@@ -163298,6 +163834,7 @@ async function buildAutoProjectContext(input) {
163298
163834
  refresh,
163299
163835
  providerQuality: providerQuality2,
163300
163836
  ...continuationRequested && workset.continuation ? { continuation: workset.continuation } : {},
163837
+ ...workset.continuity ? { continuity: workset.continuity } : {},
163301
163838
  workset
163302
163839
  };
163303
163840
  }
@@ -163599,7 +164136,14 @@ function buildBoundedContextReceipt(input) {
163599
164136
  externalState: provider2?.external.state ?? "not-detected",
163600
164137
  ...input.workset.provenance.snapshotId ? { snapshotId: input.workset.provenance.snapshotId } : {}
163601
164138
  },
163602
- ...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {}
164139
+ ...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {},
164140
+ ...input.workset.continuity ? {
164141
+ continuity: {
164142
+ taskId: input.workset.continuity.taskId,
164143
+ status: input.workset.continuity.status,
164144
+ outcome: input.workset.continuity.outcome
164145
+ }
164146
+ } : {}
163603
164147
  };
163604
164148
  }
163605
164149
  var init_context_receipt = __esm({
@@ -164231,15 +164775,21 @@ var team_store_exports = {};
164231
164775
  __export(team_store_exports, {
164232
164776
  AGENT_TYPE_ROLE_MAP: () => AGENT_TYPE_ROLE_MAP,
164233
164777
  DEFAULT_ROLES: () => DEFAULT_ROLES,
164778
+ DEFAULT_TEAM_QUERY_LIMIT: () => DEFAULT_TEAM_QUERY_LIMIT,
164779
+ MAX_TEAM_QUERY_LIMIT: () => MAX_TEAM_QUERY_LIMIT,
164234
164780
  TeamStore: () => TeamStore,
164235
164781
  getTeamStore: () => getTeamStore,
164236
164782
  initTeamStore: () => initTeamStore,
164237
164783
  isTeamStoreInitialized: () => isTeamStoreInitialized,
164238
164784
  resetTeamStore: () => resetTeamStore
164239
164785
  });
164240
- import { randomUUID as randomUUID17 } from "crypto";
164786
+ import { randomUUID as randomUUID18 } from "crypto";
164241
164787
  import path31 from "path";
164242
164788
  import fs18 from "fs";
164789
+ function clampTeamQueryLimit(limit2) {
164790
+ if (!Number.isFinite(limit2) || limit2 == null) return DEFAULT_TEAM_QUERY_LIMIT;
164791
+ return Math.min(MAX_TEAM_QUERY_LIMIT, Math.max(1, Math.floor(limit2)));
164792
+ }
164243
164793
  function getTeamStore() {
164244
164794
  if (!_teamStore) {
164245
164795
  throw new Error("[memorix] TeamStore not initialized \u2014 call initTeamStore() first");
@@ -164263,12 +164813,14 @@ function resetTeamStore() {
164263
164813
  _teamStore = null;
164264
164814
  _teamStoreDataDir = null;
164265
164815
  }
164266
- var DEFAULT_ROLES, AGENT_TYPE_ROLE_MAP, TeamStore, _teamStore, _teamStoreDataDir;
164816
+ var DEFAULT_TEAM_QUERY_LIMIT, MAX_TEAM_QUERY_LIMIT, DEFAULT_ROLES, AGENT_TYPE_ROLE_MAP, TeamStore, _teamStore, _teamStoreDataDir;
164267
164817
  var init_team_store = __esm({
164268
164818
  "src/team/team-store.ts"() {
164269
164819
  "use strict";
164270
164820
  init_esm_shims();
164271
164821
  init_sqlite_db();
164822
+ DEFAULT_TEAM_QUERY_LIMIT = 100;
164823
+ MAX_TEAM_QUERY_LIMIT = 500;
164272
164824
  DEFAULT_ROLES = [
164273
164825
  { roleId: "planner", label: "Planner", description: "Breaks down requirements, creates tasks, defines dependencies", preferredAgentTypes: ["claude-code", "cursor", "codex"], maxConcurrent: 2 },
164274
164826
  { roleId: "researcher", label: "Researcher", description: "Investigates codebase, gathers context, answers questions", preferredAgentTypes: ["claude-code", "cursor", "gemini-cli"], maxConcurrent: 2 },
@@ -164526,7 +165078,7 @@ var init_team_store = __esm({
164526
165078
  * reactivate it (preserving agent_id). Otherwise create a new one.
164527
165079
  */
164528
165080
  registerAgent(input) {
164529
- const instanceId = input.instanceId || randomUUID17();
165081
+ const instanceId = input.instanceId || randomUUID18();
164530
165082
  const now3 = Date.now();
164531
165083
  const existing = this.stmtAgentFindByInstance.get(
164532
165084
  input.projectId,
@@ -164552,7 +165104,7 @@ var init_team_store = __esm({
164552
165104
  this.eventBus?.emit("agent:joined", { agentId: row2.agent_id, projectId: input.projectId, agentType: input.agentType });
164553
165105
  return row2;
164554
165106
  }
164555
- const agentId = randomUUID17();
165107
+ const agentId = randomUUID18();
164556
165108
  this.stmtAgentUpsert.run({
164557
165109
  agent_id: agentId,
164558
165110
  project_id: input.projectId,
@@ -164578,32 +165130,37 @@ var init_team_store = __esm({
164578
165130
  return this.stmtAgentFindByInstance.get(projectId, agentType, instanceId);
164579
165131
  }
164580
165132
  listAgents(projectId, filter) {
164581
- const all = this.stmtAgentListByProject.all(projectId);
165133
+ const limit2 = clampTeamQueryLimit(filter?.limit);
164582
165134
  if (filter?.status) {
164583
- return all.filter((a) => a.status === filter.status);
165135
+ return this.db.prepare(
165136
+ `SELECT * FROM team_agents WHERE project_id = ? AND status = ? ORDER BY joined_at DESC LIMIT ?`
165137
+ ).all(projectId, filter.status, limit2);
164584
165138
  }
164585
- return all;
165139
+ return this.db.prepare(
165140
+ `SELECT * FROM team_agents WHERE project_id = ? ORDER BY joined_at DESC LIMIT ?`
165141
+ ).all(projectId, limit2);
164586
165142
  }
164587
165143
  /** List agents across all projects (for global scope) */
164588
- listAllAgents() {
165144
+ listAllAgents(limit2) {
164589
165145
  if (!this.db) return [];
164590
- return this.db.prepare("SELECT * FROM team_agents ORDER BY last_heartbeat DESC").all();
165146
+ return this.db.prepare("SELECT * FROM team_agents ORDER BY last_heartbeat DESC LIMIT ?").all(clampTeamQueryLimit(limit2));
164591
165147
  }
164592
165148
  /** List locks across all projects (for global scope) */
164593
- listAllLocks() {
165149
+ listAllLocks(limit2) {
164594
165150
  if (!this.db) return [];
164595
165151
  this.db.prepare("DELETE FROM team_locks WHERE expires_at <= ?").run(Date.now());
164596
- return this.db.prepare("SELECT * FROM team_locks WHERE expires_at > ? ORDER BY locked_at DESC").all(Date.now());
165152
+ return this.db.prepare("SELECT * FROM team_locks WHERE expires_at > ? ORDER BY locked_at DESC LIMIT ?").all(Date.now(), clampTeamQueryLimit(limit2));
164597
165153
  }
164598
165154
  /** List tasks across all projects (for global scope) */
164599
165155
  listAllTasks(filter) {
164600
165156
  if (!this.db) return [];
165157
+ const limit2 = clampTeamQueryLimit(filter?.limit);
164601
165158
  if (filter?.available) {
164602
165159
  return this.db.prepare(
164603
- `SELECT t.* FROM team_tasks t WHERE t.status = 'pending' AND t.assignee_agent_id IS NULL ORDER BY t.created_at DESC`
164604
- ).all();
165160
+ `SELECT t.* FROM team_tasks t WHERE t.status = 'pending' AND t.assignee_agent_id IS NULL ORDER BY t.created_at DESC LIMIT ?`
165161
+ ).all(limit2);
164605
165162
  }
164606
- return this.db.prepare("SELECT * FROM team_tasks ORDER BY created_at DESC").all();
165163
+ return this.db.prepare("SELECT * FROM team_tasks ORDER BY created_at DESC LIMIT ?").all(limit2);
164607
165164
  }
164608
165165
  heartbeat(agentId) {
164609
165166
  const info = this.stmtAgentUpdateHeartbeat.run(Date.now(), agentId);
@@ -164638,7 +165195,10 @@ var init_team_store = __esm({
164638
165195
  return staleIds;
164639
165196
  }
164640
165197
  getActiveCount(projectId) {
164641
- return this.listAgents(projectId, { status: "active" }).length;
165198
+ const row = this.db.prepare(
165199
+ `SELECT COUNT(*) AS count FROM team_agents WHERE project_id = ? AND status = 'active'`
165200
+ ).get(projectId);
165201
+ return Number(row?.count ?? 0);
164642
165202
  }
164643
165203
  // ═══════════════════════════════════════════════════════════════════
164644
165204
  // Message Operations
@@ -164654,7 +165214,7 @@ var init_team_store = __esm({
164654
165214
  return { error: `Recipient agent '${input.recipientAgentId}' not found \u2014 cannot send to unknown agent` };
164655
165215
  }
164656
165216
  }
164657
- const id = randomUUID17();
165217
+ const id = randomUUID18();
164658
165218
  const now3 = Date.now();
164659
165219
  const row = {
164660
165220
  id,
@@ -164673,8 +165233,14 @@ var init_team_store = __esm({
164673
165233
  this.stmtMsgInsert.run(row);
164674
165234
  return row;
164675
165235
  }
164676
- getInbox(projectId, agentId) {
164677
- return this.stmtMsgInbox.all(projectId, agentId);
165236
+ getInbox(projectId, agentId, limit2) {
165237
+ return this.db.prepare(`
165238
+ SELECT * FROM team_messages
165239
+ WHERE project_id = ?
165240
+ AND (recipient_agent_id = ? OR recipient_agent_id IS NULL)
165241
+ ORDER BY created_at ASC
165242
+ LIMIT ?
165243
+ `).all(projectId, agentId, clampTeamQueryLimit(limit2));
164678
165244
  }
164679
165245
  getUnreadCount(projectId, agentId) {
164680
165246
  const row = this.stmtMsgUnreadCount.get(projectId, agentId);
@@ -164704,7 +165270,7 @@ var init_team_store = __esm({
164704
165270
  // Task Operations (with atomic claim semantics)
164705
165271
  // ═══════════════════════════════════════════════════════════════════
164706
165272
  createTask(input) {
164707
- const taskId = randomUUID17();
165273
+ const taskId = randomUUID18();
164708
165274
  const now3 = Date.now();
164709
165275
  const row = {
164710
165276
  task_id: taskId,
@@ -164831,17 +165397,22 @@ var init_team_store = __esm({
164831
165397
  return info.changes;
164832
165398
  }
164833
165399
  listTasks(projectId, filter) {
164834
- if (filter?.available) {
164835
- return this.stmtTaskAvailable.all(projectId);
164836
- }
164837
- const all = this.stmtTaskListByProject.all(projectId);
165400
+ const clauses = ["project_id = ?"];
165401
+ const values = [projectId];
165402
+ if (filter?.available) clauses.push("status = 'pending'", "assignee_agent_id IS NULL");
164838
165403
  if (filter?.status) {
164839
- return all.filter((t2) => t2.status === filter.status);
165404
+ clauses.push("status = ?");
165405
+ values.push(filter.status);
164840
165406
  }
164841
165407
  if (filter?.assignee) {
164842
- return all.filter((t2) => t2.assignee_agent_id === filter.assignee);
165408
+ clauses.push("assignee_agent_id = ?");
165409
+ values.push(filter.assignee);
164843
165410
  }
164844
- return all;
165411
+ values.push(clampTeamQueryLimit(filter?.limit));
165412
+ const orderBy = filter?.available ? "created_at ASC, rowid ASC" : "created_at DESC, rowid DESC";
165413
+ return this.db.prepare(
165414
+ `SELECT * FROM team_tasks WHERE ${clauses.join(" AND ")} ORDER BY ${orderBy} LIMIT ?`
165415
+ ).all(...values);
164845
165416
  }
164846
165417
  /**
164847
165418
  * List available tasks for a specific agent, sorted by role affinity.
@@ -164853,7 +165424,7 @@ var init_team_store = __esm({
164853
165424
  const agent = this.stmtAgentFindById.get(agentId);
164854
165425
  if (!agent) return [];
164855
165426
  const agentRole = agent.role;
164856
- const available = this.stmtTaskAvailable.all(projectId);
165427
+ const available = this.listTasks(projectId, { available: true, limit: MAX_TEAM_QUERY_LIMIT });
164857
165428
  const eligible = available.filter((t2) => !t2.required_role || t2.required_role === agentRole);
164858
165429
  const score = (t2) => {
164859
165430
  if (t2.preferred_role === agentRole) return 0;
@@ -164907,12 +165478,16 @@ var init_team_store = __esm({
164907
165478
  const row = this.stmtLockGet.get(file, projectId);
164908
165479
  return row ?? null;
164909
165480
  }
164910
- listLocks(projectId, agentId) {
165481
+ listLocks(projectId, agentId, limit2) {
164911
165482
  this.stmtLockDeleteExpired.run(projectId, Date.now());
164912
165483
  if (agentId) {
164913
- return this.stmtLockListByAgent.all(projectId, agentId);
165484
+ return this.db.prepare(
165485
+ `SELECT * FROM team_locks WHERE project_id = ? AND locked_by = ? ORDER BY locked_at DESC LIMIT ?`
165486
+ ).all(projectId, agentId, clampTeamQueryLimit(limit2));
164914
165487
  }
164915
- return this.stmtLockListByProject.all(projectId);
165488
+ return this.db.prepare(
165489
+ `SELECT * FROM team_locks WHERE project_id = ? ORDER BY locked_at DESC LIMIT ?`
165490
+ ).all(projectId, clampTeamQueryLimit(limit2));
164916
165491
  }
164917
165492
  releaseAllLocks(agentId) {
164918
165493
  const info = this.stmtLockDeleteByAgent.run(agentId);
@@ -164976,7 +165551,7 @@ var init_team_store = __esm({
164976
165551
  for (const [agentId, msgs] of Object.entries(snap.messages.inboxes)) {
164977
165552
  for (const msg of msgs) {
164978
165553
  this.stmtMsgInsert.run({
164979
- id: msg.id ?? randomUUID17(),
165554
+ id: msg.id ?? randomUUID18(),
164980
165555
  project_id: "migrated",
164981
165556
  sender_agent_id: msg.from ?? "unknown",
164982
165557
  recipient_agent_id: msg.to === "__broadcast__" ? null : msg.to ?? agentId,
@@ -167934,7 +168509,7 @@ __export(planner_exports, {
167934
168509
  materializeTaskGraph: () => materializeTaskGraph,
167935
168510
  seedAutonomousPipeline: () => seedAutonomousPipeline
167936
168511
  });
167937
- import { randomUUID as randomUUID18 } from "crypto";
168512
+ import { randomUUID as randomUUID19 } from "crypto";
167938
168513
  function isPlannerTask(metadata) {
167939
168514
  if (!metadata) return null;
167940
168515
  try {
@@ -167992,7 +168567,7 @@ function seedAutonomousPipeline(teamStore, projectId, config, opts) {
167992
168567
  const maxIterations = config.maxIterations ?? 3;
167993
168568
  const taskBudget = config.taskBudget ?? 15;
167994
168569
  const structuredPlan = opts?.structuredPlan ?? true;
167995
- const pipelineId = randomUUID18();
168570
+ const pipelineId = randomUUID19();
167996
168571
  const meta = {
167997
168572
  plannerType: "plan",
167998
168573
  pipelineId,
@@ -168673,6 +169248,7 @@ var init_official_skills = __esm({
168673
169248
  "| Need the sequence around one memory | `memorix_timeline` | `memorix memory timeline --id <id>` |",
168674
169249
  "| Check source and freshness for a memory | `memorix_evidence` | `memorix evidence list|get|events` |",
168675
169250
  "| Record whether a memory helped, conflicted, or was corrected | `memorix_feedback` | `memorix feedback record|show|audit` |",
169251
+ "| Track task requirements, decisions, verification, and outcome | `memorix_continuity` | `memorix continuity start|record|show|close` |",
168676
169252
  "| Inspect or import controlled media | `memorix_media` | `memorix media import|attach|list|show|status` |",
168677
169253
  '| Learned reusable project knowledge | `memorix_store` | `memorix memory store --type <type> --entity <name> --title "<title>" "<text>"` |',
168678
169254
  "| Task or bug is complete/outdated | `memorix_resolve` | `memorix memory resolve --ids <ids>` |",
@@ -168696,6 +169272,7 @@ var init_official_skills = __esm({
168696
169272
  "3. Use stale or unbound memory only as a warning or search lead.",
168697
169273
  "4. Store durable decisions, fixes, and gotchas after the project state changes.",
168698
169274
  "5. Resolve completed or obsolete memories so future briefs stay small.",
169275
+ "6. For multi-step work, start a continuity ledger and pass its taskId back to memorix_project_context.",
168699
169276
  "",
168700
169277
  "## Store Rules",
168701
169278
  "",
@@ -170417,7 +170994,7 @@ __export(server_exports2, {
170417
170994
  createMemorixServer: () => createMemorixServer,
170418
170995
  shouldAwaitProjectRuntime: () => shouldAwaitProjectRuntime
170419
170996
  });
170420
- import { createHash as createHash18 } from "crypto";
170997
+ import { createHash as createHash19 } from "crypto";
170421
170998
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
170422
170999
  import { z as z627 } from "zod";
170423
171000
  function formatFormationStageDurations(stageDurationsMs) {
@@ -170501,7 +171078,7 @@ function coerceObjectArray(val) {
170501
171078
  return [];
170502
171079
  }
170503
171080
  function createDeterministicInstanceId(projectId, agentType, agentName) {
170504
- const digest2 = createHash18("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
171081
+ const digest2 = createHash19("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
170505
171082
  return `auto-${digest2}`;
170506
171083
  }
170507
171084
  function shouldAwaitProjectRuntime(toolName) {
@@ -170708,6 +171285,22 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
170708
171285
  }
170709
171286
  await projectRuntimeInitPromise;
170710
171287
  };
171288
+ const activateProjectRuntime = async () => {
171289
+ if (!projectResolved) return;
171290
+ await initObservationStore(projectDir2);
171291
+ await initMiniSkillStore(projectDir2);
171292
+ await initSessionStore(projectDir2);
171293
+ await initObservations(projectDir2, { skipCorpusLoad: true });
171294
+ try {
171295
+ const { initProjectRoot: initProjectRoot2 } = await Promise.resolve().then(() => (init_yaml_loader(), yaml_loader_exports));
171296
+ const { resetDotenv: resetDotenv2, loadDotenv: loadDotenv2 } = await Promise.resolve().then(() => (init_dotenv_loader(), dotenv_loader_exports));
171297
+ initProjectRoot2(project.rootPath);
171298
+ resetDotenv2();
171299
+ loadDotenv2(project.rootPath);
171300
+ } catch {
171301
+ }
171302
+ initLLM({ projectRoot: project.rootPath });
171303
+ };
170711
171304
  let maintenanceWorker = null;
170712
171305
  const startProjectMaintenanceWorker = async (autoCleanup = false) => {
170713
171306
  maintenanceWorker?.stop();
@@ -171623,6 +172216,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
171623
172216
  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.",
171624
172217
  inputSchema: {
171625
172218
  task: z627.string().optional().describe("Current coding task or question"),
172219
+ taskId: z627.string().max(120).optional().describe("Optional task continuity ledger id to include in the brief"),
171626
172220
  refresh: z627.enum(["auto", "always", "never"]).optional().default("auto").describe(
171627
172221
  "Code Memory refresh policy. auto refreshes only when missing or stale."
171628
172222
  ),
@@ -171647,7 +172241,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
171647
172241
  limit: z627.number().optional().describe("Reserved for future source limits; current prompt stays compact by default.")
171648
172242
  }
171649
172243
  },
171650
- async ({ task, refresh, format, agent }) => {
172244
+ async ({ task, taskId, refresh, format, agent }) => {
171651
172245
  const unresolved = requireResolvedProject("build project context for the current project");
171652
172246
  if (unresolved) return unresolved;
171653
172247
  const [
@@ -171677,6 +172271,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
171677
172271
  dataDir: projectDir2,
171678
172272
  observations: observations2,
171679
172273
  task,
172274
+ taskId,
171680
172275
  agent,
171681
172276
  refresh: refresh ?? "auto",
171682
172277
  reader: getObservationReader(),
@@ -174102,6 +174697,94 @@ ${json2}
174102
174697
  }
174103
174698
  }
174104
174699
  );
174700
+ server.registerTool(
174701
+ "memorix_continuity",
174702
+ {
174703
+ title: "Task Continuity Ledger",
174704
+ 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.",
174705
+ inputSchema: {
174706
+ action: z627.enum(["start", "record", "show", "list", "close"]).describe("Continuity operation"),
174707
+ taskId: z627.string().max(120).optional().describe("Continuity task id"),
174708
+ task: z627.string().max(1e3).optional().describe("Task description for start"),
174709
+ requirements: z627.array(z627.string().max(2e3)).max(8).optional().describe("Requirements for start"),
174710
+ kind: z627.enum(["requirement", "decision", "verification", "risk"]).optional().describe("Entry kind for record"),
174711
+ content: z627.string().max(2e3).optional().describe("Requirement, decision, verification, risk, or outcome text"),
174712
+ verificationStatus: z627.enum(["pending", "passed", "failed", "skipped"]).optional(),
174713
+ verificationId: z627.string().max(120).optional().describe("Stable verification obligation id; reuse it to move pending to passed/failed"),
174714
+ idempotencyKey: z627.string().max(200).optional().describe("Stable retry key; repeating the same operation returns the original event"),
174715
+ status: z627.enum(["completed", "blocked", "abandoned"]).optional().describe("Outcome status for close"),
174716
+ sourceRef: z627.string().max(500).optional().describe("Evidence or source reference"),
174717
+ evidenceRefs: z627.array(z627.string().max(500)).max(8).optional(),
174718
+ actor: z627.string().max(200).optional(),
174719
+ limit: z627.number().int().positive().max(100).optional().default(20)
174720
+ }
174721
+ },
174722
+ async ({ action, taskId, task, requirements, kind, content, verificationStatus, verificationId, idempotencyKey, status, sourceRef, evidenceRefs: evidenceRefs2, actor, limit: limit2 }) => {
174723
+ const unresolved = requireResolvedProject("manage task continuity for the current project");
174724
+ if (unresolved) return unresolved;
174725
+ const { TaskContinuityStore: TaskContinuityStore2 } = await Promise.resolve().then(() => (init_task_continuity(), task_continuity_exports));
174726
+ const continuity = new TaskContinuityStore2();
174727
+ await continuity.init(projectDir2);
174728
+ const effectiveActor = actor ?? currentAgentId;
174729
+ try {
174730
+ if (action === "start") {
174731
+ if (!task?.trim()) return { content: [{ type: "text", text: "task is required for continuity start." }], isError: true };
174732
+ const result2 = continuity.start({
174733
+ projectId: project.id,
174734
+ task,
174735
+ ...taskId ? { taskId } : {},
174736
+ requirements,
174737
+ ...effectiveActor ? { actor: effectiveActor } : {},
174738
+ ...idempotencyKey ? { idempotencyKey } : {}
174739
+ });
174740
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result2 }, null, 2) }] };
174741
+ }
174742
+ if (action === "list") {
174743
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ledgers: continuity.list(project.id, limit2) }, null, 2) }] };
174744
+ }
174745
+ if (!taskId?.trim()) return { content: [{ type: "text", text: "taskId is required for this continuity action." }], isError: true };
174746
+ if (action === "show") {
174747
+ const ledger = continuity.get(project.id, taskId);
174748
+ if (!ledger) return { content: [{ type: "text", text: "Task continuity ledger not found." }], isError: true };
174749
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ledger }, null, 2) }] };
174750
+ }
174751
+ if (action === "record") {
174752
+ if (!kind || !content?.trim()) {
174753
+ return { content: [{ type: "text", text: "record requires kind and content." }], isError: true };
174754
+ }
174755
+ const result2 = continuity.record({
174756
+ projectId: project.id,
174757
+ taskId,
174758
+ kind,
174759
+ content,
174760
+ ...verificationStatus ? { verificationStatus } : {},
174761
+ ...sourceRef ? { sourceRef } : {},
174762
+ ...evidenceRefs2 ? { evidenceRefs: evidenceRefs2 } : {},
174763
+ ...effectiveActor ? { actor: effectiveActor } : {},
174764
+ ...verificationId ? { verificationId } : {},
174765
+ ...idempotencyKey ? { idempotencyKey } : {}
174766
+ });
174767
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result2 }, null, 2) }] };
174768
+ }
174769
+ if (!status || !content?.trim()) {
174770
+ return { content: [{ type: "text", text: "close requires status and content." }], isError: true };
174771
+ }
174772
+ const result = continuity.close({
174773
+ projectId: project.id,
174774
+ taskId,
174775
+ status,
174776
+ content,
174777
+ ...sourceRef ? { sourceRef } : {},
174778
+ ...evidenceRefs2 ? { evidenceRefs: evidenceRefs2 } : {},
174779
+ ...effectiveActor ? { actor: effectiveActor } : {},
174780
+ ...idempotencyKey ? { idempotencyKey } : {}
174781
+ });
174782
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result }, null, 2) }] };
174783
+ } catch (error) {
174784
+ return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true };
174785
+ }
174786
+ }
174787
+ );
174105
174788
  if (teamFeaturesEnabled) {
174106
174789
  const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
174107
174790
  initTeamStoreForProject = initTeamStore2;
@@ -175129,7 +175812,9 @@ fi
175129
175812
  graphManager,
175130
175813
  projectId: project.id,
175131
175814
  deferredInit,
175815
+ activateProjectRuntime,
175132
175816
  switchProject,
175817
+ getProjectDataDir: () => projectDir2,
175133
175818
  isExplicitlyBound: () => projectBinding.isExplicit(),
175134
175819
  getRequestContext,
175135
175820
  handleTransportClose
@@ -175480,7 +176165,10 @@ var init_mcp_root_path = __esm({
175480
176165
  // src/server/modern-mcp-bridge.ts
175481
176166
  var modern_mcp_bridge_exports = {};
175482
176167
  __export(modern_mcp_bridge_exports, {
175483
- createModernMcpBridge: () => createModernMcpBridge
176168
+ ModernMcpRuntimePool: () => ModernMcpRuntimePool,
176169
+ createModernMcpBridge: () => createModernMcpBridge,
176170
+ createModernMcpRuntime: () => createModernMcpRuntime,
176171
+ withBusinessRuntimeQueue: () => withBusinessRuntimeQueue
175484
176172
  });
175485
176173
  import {
175486
176174
  fromJsonSchema,
@@ -175488,7 +176176,12 @@ import {
175488
176176
  } from "@modelcontextprotocol/server";
175489
176177
  import { Client as LegacyClient } from "@modelcontextprotocol/sdk/client/index.js";
175490
176178
  import { InMemoryTransport as LegacyInMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
175491
- async function createModernMcpBridge(options) {
176179
+ function withBusinessRuntimeQueue(work) {
176180
+ const result = businessRuntimeQueue.then(work);
176181
+ businessRuntimeQueue = result.catch(() => void 0);
176182
+ return result;
176183
+ }
176184
+ async function createModernMcpRuntime(options) {
175492
176185
  const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server2(), server_exports2));
175493
176186
  const legacy = await createMemorixServer2(
175494
176187
  options.projectRoot,
@@ -175509,46 +176202,135 @@ async function createModernMcpBridge(options) {
175509
176202
  await legacy.server.connect(serverTransport);
175510
176203
  await legacyClient.connect(clientTransport);
175511
176204
  const listed = await legacyClient.listTools();
175512
- const modern = new ModernMcpServer(getMcpServerInfo());
175513
- for (const tool of listed.tools) {
175514
- modern.registerTool(
175515
- tool.name,
175516
- {
175517
- ...tool.title ? { title: tool.title } : {},
175518
- ...tool.description ? { description: tool.description } : {},
175519
- inputSchema: fromJsonSchema(tool.inputSchema)
175520
- },
175521
- async (args) => await legacyClient.callTool({
175522
- name: tool.name,
175523
- arguments: args
175524
- })
175525
- );
175526
- }
175527
- const bridge = modern;
175528
- bridge.memorix = {
176205
+ const callTool = (name, args) => withBusinessRuntimeQueue(async () => {
176206
+ await legacy.activateProjectRuntime();
176207
+ return await legacyClient.callTool({ name, arguments: args });
176208
+ });
176209
+ const lifecycle = {
175529
176210
  projectId: legacy.projectId,
175530
176211
  deferredInit: legacy.deferredInit,
176212
+ activateProjectRuntime: legacy.activateProjectRuntime,
175531
176213
  switchProject: legacy.switchProject,
176214
+ getProjectDataDir: legacy.getProjectDataDir,
175532
176215
  isExplicitlyBound: legacy.isExplicitlyBound,
175533
176216
  handleTransportClose: legacy.handleTransportClose
175534
176217
  };
175535
176218
  let closed = false;
175536
- const modernClose = modern.close.bind(modern);
175537
- modern.close = async () => {
176219
+ return {
176220
+ projectId: legacy.projectId,
176221
+ getProjectDataDir: legacy.getProjectDataDir,
176222
+ createBridge: () => {
176223
+ const requestServer = new ModernMcpServer(getMcpServerInfo());
176224
+ for (const tool of listed.tools) {
176225
+ requestServer.registerTool(
176226
+ tool.name,
176227
+ {
176228
+ ...tool.title ? { title: tool.title } : {},
176229
+ ...tool.description ? { description: tool.description } : {},
176230
+ inputSchema: fromJsonSchema(tool.inputSchema)
176231
+ },
176232
+ async (args) => await callTool(tool.name, args)
176233
+ );
176234
+ }
176235
+ requestServer.memorix = lifecycle;
176236
+ return requestServer;
176237
+ },
176238
+ close: async () => {
176239
+ if (closed) return;
176240
+ closed = true;
176241
+ await legacyClient.close().catch(() => void 0);
176242
+ await legacy.server.close().catch(() => void 0);
176243
+ legacy.handleTransportClose();
176244
+ }
176245
+ };
176246
+ }
176247
+ async function createModernMcpBridge(options) {
176248
+ const runtime = await createModernMcpRuntime(options);
176249
+ const bridge = runtime.createBridge();
176250
+ const close = bridge.close.bind(bridge);
176251
+ let closed = false;
176252
+ bridge.close = async () => {
175538
176253
  if (closed) return;
175539
176254
  closed = true;
175540
- await modernClose();
175541
- await legacyClient.close().catch(() => void 0);
175542
- await legacy.server.close().catch(() => void 0);
175543
- legacy.handleTransportClose();
176255
+ await close();
176256
+ await runtime.close();
175544
176257
  };
175545
176258
  return bridge;
175546
176259
  }
176260
+ var businessRuntimeQueue, ModernMcpRuntimePool;
175547
176261
  var init_modern_mcp_bridge = __esm({
175548
176262
  "src/server/modern-mcp-bridge.ts"() {
175549
176263
  "use strict";
175550
176264
  init_esm_shims();
175551
176265
  init_mcp_discovery();
176266
+ businessRuntimeQueue = Promise.resolve();
176267
+ ModernMcpRuntimePool = class {
176268
+ constructor(maxEntries = 32) {
176269
+ this.maxEntries = maxEntries;
176270
+ }
176271
+ maxEntries;
176272
+ entries = /* @__PURE__ */ new Map();
176273
+ pending = /* @__PURE__ */ new Map();
176274
+ async createBridge(key, create2) {
176275
+ let entry = this.entries.get(key);
176276
+ if (!entry) {
176277
+ let pending = this.pending.get(key);
176278
+ if (!pending) {
176279
+ pending = create2().then((runtime) => {
176280
+ const created = { runtime, refs: 0, lastUsedAt: Date.now() };
176281
+ this.entries.set(key, created);
176282
+ return created;
176283
+ });
176284
+ this.pending.set(key, pending);
176285
+ }
176286
+ try {
176287
+ entry = await pending;
176288
+ } finally {
176289
+ this.pending.delete(key);
176290
+ }
176291
+ }
176292
+ entry.refs++;
176293
+ entry.lastUsedAt = Date.now();
176294
+ const bridge = entry.runtime.createBridge();
176295
+ const close = bridge.close.bind(bridge);
176296
+ let released = false;
176297
+ bridge.close = async () => {
176298
+ try {
176299
+ await close();
176300
+ } finally {
176301
+ if (!released) {
176302
+ released = true;
176303
+ entry.refs = Math.max(0, entry.refs - 1);
176304
+ entry.lastUsedAt = Date.now();
176305
+ }
176306
+ }
176307
+ };
176308
+ return bridge;
176309
+ }
176310
+ async evictIdle() {
176311
+ let closed = 0;
176312
+ const idle = [...this.entries.entries()].filter(([, entry]) => entry.refs === 0).sort(([, left], [, right]) => left.lastUsedAt - right.lastUsedAt);
176313
+ for (const [key, entry] of idle) {
176314
+ if (this.entries.size <= this.maxEntries || entry.refs > 0) continue;
176315
+ this.entries.delete(key);
176316
+ await entry.runtime.close();
176317
+ closed++;
176318
+ }
176319
+ return closed;
176320
+ }
176321
+ async close() {
176322
+ const entries = [...this.entries.values()];
176323
+ this.entries.clear();
176324
+ await Promise.all(entries.map((entry) => entry.runtime.close().catch(() => void 0)));
176325
+ }
176326
+ stats() {
176327
+ return {
176328
+ entries: this.entries.size,
176329
+ activeRefs: [...this.entries.values()].reduce((sum, entry) => sum + entry.refs, 0),
176330
+ pending: this.pending.size
176331
+ };
176332
+ }
176333
+ };
175552
176334
  }
175553
176335
  });
175554
176336
 
@@ -175660,7 +176442,7 @@ var init_serve_shared = __esm({
175660
176442
 
175661
176443
  // src/cli/version.ts
175662
176444
  function getCliVersion() {
175663
- return true ? "1.9.5" : pkg.version;
176445
+ return true ? "1.9.7" : pkg.version;
175664
176446
  }
175665
176447
  var init_version3 = __esm({
175666
176448
  "src/cli/version.ts"() {