memorix 1.9.4 → 1.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/CHANGELOG.md +75 -1
  2. package/README.md +12 -6
  3. package/README.zh-CN.md +12 -6
  4. package/dist/cli/index.js +2533 -585
  5. package/dist/index.d.ts +15 -8
  6. package/dist/index.js +1201 -284
  7. package/dist/maintenance-runner.js +387 -216
  8. package/dist/media-audio-runner.js +300 -129
  9. package/dist/media-video-runner.js +300 -129
  10. package/dist/memcode-runtime/CHANGELOG.md +75 -1
  11. package/dist/sdk.d.ts +1 -0
  12. package/dist/sdk.js +1882 -998
  13. package/dist/types.d.ts +1 -1
  14. package/dist/vector-backfill-runner.js +300 -129
  15. package/docs/AGENT_OPERATOR_PLAYBOOK.md +47 -3
  16. package/docs/API_REFERENCE.md +39 -1
  17. package/docs/CONFIGURATION.md +58 -1
  18. package/docs/DEVELOPMENT.md +1 -1
  19. package/docs/DOCKER.md +23 -3
  20. package/docs/INTEGRATIONS.md +1 -1
  21. package/docs/KNOWN_ISSUES_AND_ROADMAP.md +61 -1
  22. package/docs/MULTI-DEVICE-SYNC-RESEARCH.md +4 -0
  23. package/docs/PERFORMANCE.md +46 -2
  24. package/docs/README.md +2 -1
  25. package/docs/SETUP.md +56 -1
  26. package/package.json +3 -2
  27. package/plugins/antigravity/memorix/plugin.json +1 -1
  28. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  29. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  30. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  31. package/plugins/copilot/memorix/plugin.json +1 -1
  32. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  33. package/plugins/hermes/memorix/plugin.yaml +1 -1
  34. package/plugins/omp/memorix/package.json +1 -1
  35. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  36. package/plugins/pi/memorix/package.json +1 -1
  37. package/server.json +2 -2
  38. package/src/cli/capability-map.ts +1 -0
  39. package/src/cli/command-guide.ts +15 -3
  40. package/src/cli/commands/agent-integrations.ts +57 -0
  41. package/src/cli/commands/background.ts +11 -2
  42. package/src/cli/commands/context.ts +2 -0
  43. package/src/cli/commands/continuity.ts +155 -0
  44. package/src/cli/commands/init.ts +5 -2
  45. package/src/cli/commands/serve-http.ts +435 -84
  46. package/src/cli/commands/setup.ts +77 -10
  47. package/src/cli/commands/sync-store.ts +47 -12
  48. package/src/cli/commands/sync.ts +26 -0
  49. package/src/cli/index.ts +3 -1
  50. package/src/cli/tui/ConfigureView.tsx +2 -0
  51. package/src/codegraph/auto-context.ts +17 -0
  52. package/src/config/legacy-loader.ts +2 -2
  53. package/src/config/resolved-config.ts +8 -5
  54. package/src/config/yaml-loader.ts +20 -10
  55. package/src/dashboard/server.ts +10 -12
  56. package/src/embedding/api-provider.ts +4 -2
  57. package/src/embedding/fastembed-provider.ts +47 -14
  58. package/src/embedding/transformers-provider.ts +29 -7
  59. package/src/evaluation/memory-quality-gate.ts +90 -0
  60. package/src/hooks/official-skills.ts +2 -0
  61. package/src/integrations/registry.ts +1 -1
  62. package/src/knowledge/context-assembly.ts +1 -0
  63. package/src/knowledge/context-receipt.ts +15 -0
  64. package/src/knowledge/task-continuity.ts +413 -0
  65. package/src/knowledge/workset.ts +77 -0
  66. package/src/llm/provider.ts +2 -1
  67. package/src/memory/graph.ts +10 -8
  68. package/src/memory/session.ts +1 -1
  69. package/src/orchestrate/coordinator.ts +60 -0
  70. package/src/orchestrate/effect-ledger.ts +160 -0
  71. package/src/orchestrate/pipeline-trace.ts +40 -6
  72. package/src/runtime/maintenance-jobs.ts +12 -6
  73. package/src/sdk.ts +24 -0
  74. package/src/server/mcp-binding-store.ts +14 -3
  75. package/src/server/modern-mcp-bridge.ts +153 -27
  76. package/src/server/tool-profile.ts +3 -2
  77. package/src/server.ts +131 -7
  78. package/src/store/file-lock.ts +28 -3
  79. package/src/store/graph-store.ts +178 -46
  80. package/src/store/persistence.ts +14 -9
  81. package/src/store/sqlite-db.ts +150 -7
  82. package/src/store/sqlite-store.ts +32 -27
  83. package/src/sync/engine.ts +9 -7
  84. package/src/sync/namespace.ts +23 -0
  85. package/src/sync/policy.ts +19 -4
  86. package/src/sync/store-port.ts +43 -13
  87. package/src/sync/types.ts +2 -0
  88. package/src/team/team-store.ts +62 -27
  89. package/src/types.ts +1 -1
  90. package/src/workspace/mcp-adapters/grok.ts +32 -0
package/dist/index.js CHANGED
@@ -444,11 +444,21 @@ function applySchemaMigrations(db2) {
444
444
  apply();
445
445
  }
446
446
  }
447
+ function normalizeDataDir(dataDir) {
448
+ return path3.resolve(dataDir);
449
+ }
450
+ function touchDatabase(normalized, db2) {
451
+ _dbCache.delete(normalized);
452
+ _dbCache.set(normalized, db2);
453
+ }
447
454
  function getDatabase(dataDir) {
448
- const normalized = path3.resolve(dataDir);
455
+ const normalized = normalizeDataDir(dataDir);
449
456
  assertNotHomeDataDir(normalized);
450
457
  const existing = _dbCache.get(normalized);
451
- if (existing) return existing;
458
+ if (existing) {
459
+ touchDatabase(normalized, existing);
460
+ return existing;
461
+ }
452
462
  loadBetterSqlite3();
453
463
  fs.mkdirSync(dataDir, { recursive: true });
454
464
  const dbPath = path3.join(dataDir, "memorix.db");
@@ -478,6 +488,8 @@ function getDatabase(dataDir) {
478
488
  db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
479
489
  db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
480
490
  db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
491
+ db2.exec(CREATE_TASK_CONTINUITY_EVENTS_TABLE);
492
+ db2.exec(CREATE_AGENT_EFFECTS_TABLE);
481
493
  try {
482
494
  db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
483
495
  } catch {
@@ -516,7 +528,7 @@ function getDatabase(dataDir) {
516
528
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('storage_generation', '0')`).run();
517
529
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('next_id', '1')`).run();
518
530
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('mini_skills_generation', '0')`).run();
519
- _dbCache.set(normalized, db2);
531
+ touchDatabase(normalized, db2);
520
532
  return db2;
521
533
  } catch (error) {
522
534
  try {
@@ -534,8 +546,9 @@ function closeAllDatabases() {
534
546
  }
535
547
  _dbCache.delete(key);
536
548
  }
549
+ _dbLeases.clear();
537
550
  }
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;
551
+ 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
552
  var init_sqlite_db = __esm({
540
553
  "src/store/sqlite-db.ts"() {
541
554
  "use strict";
@@ -707,18 +720,21 @@ CREATE TABLE IF NOT EXISTS chat_transcript (
707
720
  `;
708
721
  CREATE_GRAPH_ENTITIES_TABLE = `
709
722
  CREATE TABLE IF NOT EXISTS graph_entities (
710
- name TEXT PRIMARY KEY,
723
+ projectId TEXT NOT NULL,
724
+ name TEXT NOT NULL,
711
725
  entityType TEXT NOT NULL DEFAULT '',
712
- observations TEXT NOT NULL DEFAULT '[]'
726
+ observations TEXT NOT NULL DEFAULT '[]',
727
+ PRIMARY KEY (projectId, name)
713
728
  );
714
729
  `;
715
730
  CREATE_GRAPH_RELATIONS_TABLE = `
716
731
  CREATE TABLE IF NOT EXISTS graph_relations (
717
732
  id INTEGER PRIMARY KEY AUTOINCREMENT,
733
+ projectId TEXT NOT NULL,
718
734
  from_entity TEXT NOT NULL,
719
735
  to_entity TEXT NOT NULL,
720
736
  relationType TEXT NOT NULL DEFAULT '',
721
- UNIQUE(from_entity, to_entity, relationType)
737
+ UNIQUE(projectId, from_entity, to_entity, relationType)
722
738
  );
723
739
  `;
724
740
  CREATE_CODE_FILES_TABLE = `
@@ -1249,6 +1265,43 @@ CREATE TABLE IF NOT EXISTS memory_feedback_events (
1249
1265
  delta REAL NOT NULL DEFAULT 0,
1250
1266
  observed_at TEXT NOT NULL
1251
1267
  );
1268
+ `;
1269
+ CREATE_TASK_CONTINUITY_EVENTS_TABLE = `
1270
+ CREATE TABLE IF NOT EXISTS task_continuity_events (
1271
+ id TEXT PRIMARY KEY,
1272
+ project_id TEXT NOT NULL,
1273
+ task_id TEXT NOT NULL,
1274
+ task TEXT NOT NULL,
1275
+ kind TEXT NOT NULL,
1276
+ content TEXT NOT NULL,
1277
+ item_status TEXT,
1278
+ source_ref TEXT,
1279
+ evidence_json TEXT NOT NULL DEFAULT '[]',
1280
+ actor TEXT,
1281
+ created_at TEXT NOT NULL,
1282
+ event_key TEXT,
1283
+ item_key TEXT,
1284
+ supersedes_id TEXT
1285
+ );
1286
+ `;
1287
+ CREATE_AGENT_EFFECTS_TABLE = `
1288
+ CREATE TABLE IF NOT EXISTS agent_effects (
1289
+ id TEXT PRIMARY KEY,
1290
+ pipeline_id TEXT NOT NULL,
1291
+ task_id TEXT NOT NULL,
1292
+ attempt INTEGER NOT NULL,
1293
+ effect_key TEXT NOT NULL,
1294
+ tool TEXT NOT NULL,
1295
+ risk_tier TEXT NOT NULL,
1296
+ replay_policy TEXT NOT NULL,
1297
+ status TEXT NOT NULL,
1298
+ call_id TEXT,
1299
+ idempotency_key TEXT NOT NULL,
1300
+ input_fingerprint TEXT,
1301
+ detail TEXT,
1302
+ started_at INTEGER NOT NULL,
1303
+ finished_at INTEGER
1304
+ );
1252
1305
  `;
1253
1306
  CREATE_MCP_BINDINGS_TABLE = `
1254
1307
  CREATE TABLE IF NOT EXISTS mcp_bindings (
@@ -1327,7 +1380,18 @@ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(projec
1327
1380
  CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
1328
1381
  CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
1329
1382
  CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
1383
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_project_task ON task_continuity_events(project_id, task_id, created_at ASC);
1384
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_project_recent ON task_continuity_events(project_id, created_at DESC);
1385
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_item ON task_continuity_events(project_id, task_id, kind, item_key, created_at DESC);
1386
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
1387
+ ON task_continuity_events(project_id, task_id, event_key)
1388
+ WHERE event_key IS NOT NULL;
1389
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_effect_identity
1390
+ ON agent_effects(pipeline_id, task_id, attempt, effect_key);
1391
+ CREATE INDEX IF NOT EXISTS idx_agent_effect_pipeline ON agent_effects(pipeline_id, started_at ASC);
1330
1392
  CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
1393
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC);
1394
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at);
1331
1395
  CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1332
1396
  ON maintenance_jobs(project_id, kind, dedupe_key)
1333
1397
  WHERE status IN ('pending', 'running', 'retry');
@@ -1498,6 +1562,8 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1498
1562
  db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
1499
1563
  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)");
1500
1564
  db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
1565
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC)");
1566
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at)");
1501
1567
  }
1502
1568
  },
1503
1569
  {
@@ -1508,9 +1574,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1508
1574
  addColumnIfMissing(db2, "code_symbols", "source", "source TEXT");
1509
1575
  addColumnIfMissing(db2, "code_edges", "source", "source TEXT");
1510
1576
  }
1577
+ },
1578
+ {
1579
+ id: "1.9-continuity-v2",
1580
+ apply: (db2) => {
1581
+ addColumnIfMissing(db2, "task_continuity_events", "event_key", "event_key TEXT");
1582
+ addColumnIfMissing(db2, "task_continuity_events", "item_key", "item_key TEXT");
1583
+ addColumnIfMissing(db2, "task_continuity_events", "supersedes_id", "supersedes_id TEXT");
1584
+ 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)");
1585
+ db2.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
1586
+ ON task_continuity_events(project_id, task_id, event_key)
1587
+ WHERE event_key IS NOT NULL`);
1588
+ }
1511
1589
  }
1512
1590
  ];
1513
1591
  _dbCache = /* @__PURE__ */ new Map();
1592
+ _dbLeases = /* @__PURE__ */ new Map();
1514
1593
  }
1515
1594
  });
1516
1595
 
@@ -1518,6 +1597,21 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1518
1597
  import { promises as fs2 } from "fs";
1519
1598
  import { randomUUID } from "crypto";
1520
1599
  import path4 from "path";
1600
+ function canReclaimStaleLock(raw) {
1601
+ try {
1602
+ const parsed = JSON.parse(raw);
1603
+ const pid = Number(parsed.pid);
1604
+ if (!Number.isSafeInteger(pid) || pid < 1) return true;
1605
+ try {
1606
+ process.kill(pid, 0);
1607
+ return false;
1608
+ } catch (error) {
1609
+ return error.code !== "EPERM";
1610
+ }
1611
+ } catch {
1612
+ return true;
1613
+ }
1614
+ }
1521
1615
  async function acquireLock(lockPath) {
1522
1616
  for (let i2 = 0; i2 < MAX_RETRIES; i2++) {
1523
1617
  try {
@@ -1531,9 +1625,12 @@ async function acquireLock(lockPath) {
1531
1625
  try {
1532
1626
  const stat5 = await fs2.stat(lockPath);
1533
1627
  if (Date.now() - stat5.mtimeMs > LOCK_STALE_MS) {
1534
- await fs2.unlink(lockPath).catch(() => {
1535
- });
1536
- continue;
1628
+ const raw2 = await fs2.readFile(lockPath, "utf8").catch(() => "");
1629
+ if (canReclaimStaleLock(raw2)) {
1630
+ await fs2.unlink(lockPath).catch(() => {
1631
+ });
1632
+ continue;
1633
+ }
1537
1634
  }
1538
1635
  } catch {
1539
1636
  continue;
@@ -1544,6 +1641,10 @@ async function acquireLock(lockPath) {
1544
1641
  }
1545
1642
  }
1546
1643
  }
1644
+ const raw = await fs2.readFile(lockPath, "utf8").catch(() => "");
1645
+ if (!canReclaimStaleLock(raw)) {
1646
+ throw new Error(`Failed to acquire lock: ${lockPath} (owner is still running)`);
1647
+ }
1547
1648
  await fs2.unlink(lockPath).catch(() => {
1548
1649
  });
1549
1650
  try {
@@ -1782,6 +1883,11 @@ async function listProjectDirs(baseDir) {
1782
1883
  }
1783
1884
  }
1784
1885
  async function migrateSubdirsToFlat(baseDir) {
1886
+ const base = baseDir ?? resolveDefaultDataDir();
1887
+ await fs4.mkdir(base, { recursive: true });
1888
+ return withFileLock(base, () => migrateSubdirsToFlatUnlocked(base));
1889
+ }
1890
+ async function migrateSubdirsToFlatUnlocked(baseDir) {
1785
1891
  const base = baseDir ?? resolveDefaultDataDir();
1786
1892
  await fs4.mkdir(base, { recursive: true });
1787
1893
  let entries;
@@ -1838,7 +1944,7 @@ async function migrateSubdirsToFlat(baseDir) {
1838
1944
  for (const { obs } of subdirData) {
1839
1945
  for (const o of obs) {
1840
1946
  const isDuplicate = allObs.some(
1841
- (existing) => existing.title === o.title && existing.createdAt === o.createdAt
1947
+ (existing) => existing.title === o.title && existing.createdAt === o.createdAt && existing.projectId === o.projectId
1842
1948
  );
1843
1949
  if (!isDuplicate) {
1844
1950
  allObs.push(o);
@@ -1871,18 +1977,14 @@ async function migrateSubdirsToFlat(baseDir) {
1871
1977
  mergedRelations.push(rel);
1872
1978
  }
1873
1979
  }
1874
- await fs4.writeFile(path6.join(base, "observations.json"), JSON.stringify(allObs, null, 2), "utf-8");
1875
- await fs4.writeFile(
1876
- path6.join(base, "counter.json"),
1877
- JSON.stringify({ nextId: allObs.length + 1 }),
1878
- "utf-8"
1879
- );
1980
+ await atomicWriteFile(path6.join(base, "observations.json"), JSON.stringify(allObs, null, 2));
1981
+ await atomicWriteFile(path6.join(base, "counter.json"), JSON.stringify({ nextId: allObs.length + 1 }));
1880
1982
  const graphLines = [
1881
1983
  ...[...entityMap.values()].map((e2) => JSON.stringify({ type: "entity", name: e2.name, entityType: e2.entityType, observations: e2.observations })),
1882
1984
  ...mergedRelations.map((r2) => JSON.stringify({ type: "relation", from: r2.from, to: r2.to, relationType: r2.relationType }))
1883
1985
  ];
1884
1986
  if (graphLines.length > 0) {
1885
- await fs4.writeFile(path6.join(base, "graph.jsonl"), graphLines.join("\n"), "utf-8");
1987
+ await atomicWriteFile(path6.join(base, "graph.jsonl"), graphLines.join("\n"));
1886
1988
  }
1887
1989
  let allSessions = [];
1888
1990
  try {
@@ -1900,7 +2002,7 @@ async function migrateSubdirsToFlat(baseDir) {
1900
2002
  }
1901
2003
  }
1902
2004
  if (allSessions.length > 0) {
1903
- await fs4.writeFile(path6.join(base, "sessions.json"), JSON.stringify(allSessions, null, 2), "utf-8");
2005
+ await atomicWriteFile(path6.join(base, "sessions.json"), JSON.stringify(allSessions, null, 2));
1904
2006
  }
1905
2007
  const backupDir = path6.join(base, ".migrated-subdirs");
1906
2008
  await fs4.mkdir(backupDir, { recursive: true });
@@ -1936,6 +2038,7 @@ var init_persistence = __esm({
1936
2038
  "src/store/persistence.ts"() {
1937
2039
  "use strict";
1938
2040
  init_esm_shims();
2041
+ init_file_lock();
1939
2042
  init_persistence_json();
1940
2043
  }
1941
2044
  });
@@ -1943,6 +2046,7 @@ var init_persistence = __esm({
1943
2046
  // src/store/graph-store.ts
1944
2047
  var graph_store_exports = {};
1945
2048
  __export(graph_store_exports, {
2049
+ DEFAULT_GRAPH_PROJECT_ID: () => DEFAULT_GRAPH_PROJECT_ID,
1946
2050
  GraphSqliteStore: () => GraphSqliteStore,
1947
2051
  getGraphStore: () => getGraphStore,
1948
2052
  initGraphStore: () => initGraphStore,
@@ -1972,37 +2076,106 @@ function entityToRow(entity) {
1972
2076
  observations: JSON.stringify(entity.observations ?? [])
1973
2077
  };
1974
2078
  }
1975
- async function initGraphStore(dataDir) {
1976
- if (_graphStore && _graphDataDir === dataDir) return _graphStore;
1977
- _graphStore = new GraphSqliteStore();
1978
- await _graphStore.init(dataDir);
1979
- _graphDataDir = dataDir;
1980
- return _graphStore;
2079
+ function ensureScopedGraphSchema(db2) {
2080
+ const entityColumns = db2.prepare("PRAGMA table_info(graph_entities)").all();
2081
+ if (!entityColumns.some((column) => column.name === "projectId")) {
2082
+ const migrate = db2.transaction(() => {
2083
+ db2.exec("ALTER TABLE graph_entities RENAME TO graph_entities_legacy_unscoped");
2084
+ db2.exec(CREATE_SCOPED_GRAPH_ENTITIES);
2085
+ db2.exec(`INSERT INTO graph_entities (projectId, name, entityType, observations)
2086
+ SELECT '${LEGACY_GRAPH_PROJECT_ID}', name, entityType, observations
2087
+ FROM graph_entities_legacy_unscoped`);
2088
+ db2.exec("DROP TABLE graph_entities_legacy_unscoped");
2089
+ });
2090
+ migrate();
2091
+ } else {
2092
+ db2.exec(CREATE_SCOPED_GRAPH_ENTITIES);
2093
+ }
2094
+ const relationColumns = db2.prepare("PRAGMA table_info(graph_relations)").all();
2095
+ if (!relationColumns.some((column) => column.name === "projectId")) {
2096
+ const migrate = db2.transaction(() => {
2097
+ db2.exec("ALTER TABLE graph_relations RENAME TO graph_relations_legacy_unscoped");
2098
+ db2.exec(CREATE_SCOPED_GRAPH_RELATIONS);
2099
+ db2.exec(`INSERT INTO graph_relations (projectId, from_entity, to_entity, relationType)
2100
+ SELECT '${LEGACY_GRAPH_PROJECT_ID}', from_entity, to_entity, relationType
2101
+ FROM graph_relations_legacy_unscoped`);
2102
+ db2.exec("DROP TABLE graph_relations_legacy_unscoped");
2103
+ });
2104
+ migrate();
2105
+ } else {
2106
+ db2.exec(CREATE_SCOPED_GRAPH_RELATIONS);
2107
+ }
1981
2108
  }
1982
- function getGraphStore() {
1983
- if (!_graphStore) throw new Error("[memorix] GraphStore not initialized \u2014 call initGraphStore() first");
1984
- return _graphStore;
2109
+ function graphStoreKey(dataDir, projectId) {
2110
+ return `${path7.resolve(dataDir)}\0${projectId}`;
2111
+ }
2112
+ async function initGraphStore(dataDir, projectId = DEFAULT_GRAPH_PROJECT_ID) {
2113
+ const key = graphStoreKey(dataDir, projectId);
2114
+ const existing = _graphStores.get(key);
2115
+ if (existing) {
2116
+ _graphStores.delete(key);
2117
+ _graphStores.set(key, existing);
2118
+ _activeGraphKey = key;
2119
+ return existing;
2120
+ }
2121
+ const store = new GraphSqliteStore();
2122
+ await store.init(dataDir, projectId);
2123
+ _graphStores.set(key, store);
2124
+ while (_graphStores.size > MAX_GRAPH_STORE_CACHE) {
2125
+ const oldest = _graphStores.keys().next().value;
2126
+ if (!oldest) break;
2127
+ _graphStores.delete(oldest);
2128
+ }
2129
+ _activeGraphKey = key;
2130
+ return store;
2131
+ }
2132
+ function getGraphStore(dataDir, projectId) {
2133
+ const key = dataDir && projectId ? graphStoreKey(dataDir, projectId) : _activeGraphKey;
2134
+ const store = key ? _graphStores.get(key) : void 0;
2135
+ if (!store) throw new Error("[memorix] GraphStore not initialized \u2014 call initGraphStore() first");
2136
+ return store;
1985
2137
  }
1986
2138
  function resetGraphStore() {
1987
- if (_graphStore) {
2139
+ for (const store of _graphStores.values()) {
1988
2140
  try {
1989
- _graphStore.close();
2141
+ store.close();
1990
2142
  } catch {
1991
2143
  }
1992
2144
  }
1993
- _graphStore = null;
1994
- _graphDataDir = null;
2145
+ _graphStores.clear();
2146
+ _activeGraphKey = null;
1995
2147
  }
1996
- var GraphSqliteStore, _graphStore, _graphDataDir;
2148
+ var DEFAULT_GRAPH_PROJECT_ID, LEGACY_GRAPH_PROJECT_ID, MAX_GRAPH_STORE_CACHE, CREATE_SCOPED_GRAPH_ENTITIES, CREATE_SCOPED_GRAPH_RELATIONS, GraphSqliteStore, _graphStores, _activeGraphKey;
1997
2149
  var init_graph_store = __esm({
1998
2150
  "src/store/graph-store.ts"() {
1999
2151
  "use strict";
2000
2152
  init_esm_shims();
2001
2153
  init_sqlite_db();
2002
2154
  init_persistence();
2155
+ DEFAULT_GRAPH_PROJECT_ID = "__default__";
2156
+ LEGACY_GRAPH_PROJECT_ID = "__legacy__";
2157
+ MAX_GRAPH_STORE_CACHE = 32;
2158
+ CREATE_SCOPED_GRAPH_ENTITIES = `
2159
+ CREATE TABLE IF NOT EXISTS graph_entities (
2160
+ projectId TEXT NOT NULL,
2161
+ name TEXT NOT NULL,
2162
+ entityType TEXT NOT NULL DEFAULT '',
2163
+ observations TEXT NOT NULL DEFAULT '[]',
2164
+ PRIMARY KEY (projectId, name)
2165
+ );`;
2166
+ CREATE_SCOPED_GRAPH_RELATIONS = `
2167
+ CREATE TABLE IF NOT EXISTS graph_relations (
2168
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
2169
+ projectId TEXT NOT NULL,
2170
+ from_entity TEXT NOT NULL,
2171
+ to_entity TEXT NOT NULL,
2172
+ relationType TEXT NOT NULL DEFAULT '',
2173
+ UNIQUE(projectId, from_entity, to_entity, relationType)
2174
+ );`;
2003
2175
  GraphSqliteStore = class {
2004
2176
  db = null;
2005
2177
  dataDir = "";
2178
+ projectId = DEFAULT_GRAPH_PROJECT_ID;
2006
2179
  stmtInsertEntity = null;
2007
2180
  stmtUpdateEntityObs = null;
2008
2181
  stmtDeleteEntity = null;
@@ -2012,45 +2185,89 @@ var init_graph_store = __esm({
2012
2185
  stmtDeleteRelation = null;
2013
2186
  stmtDeleteRelationsByEntity = null;
2014
2187
  stmtSelectAllRelations = null;
2015
- async init(dataDir) {
2188
+ async init(dataDir, projectId = DEFAULT_GRAPH_PROJECT_ID) {
2016
2189
  this.dataDir = dataDir;
2190
+ this.projectId = projectId;
2017
2191
  this.db = getDatabase(dataDir);
2192
+ ensureScopedGraphSchema(this.db);
2018
2193
  this.stmtInsertEntity = this.db.prepare(
2019
- `INSERT OR REPLACE INTO graph_entities (name, entityType, observations) VALUES (@name, @entityType, @observations)`
2194
+ `INSERT OR REPLACE INTO graph_entities (projectId, name, entityType, observations) VALUES (@projectId, @name, @entityType, @observations)`
2020
2195
  );
2021
2196
  this.stmtUpdateEntityObs = this.db.prepare(
2022
- `UPDATE graph_entities SET observations = @observations WHERE name = @name`
2197
+ `UPDATE graph_entities SET observations = @observations WHERE projectId = @projectId AND name = @name`
2023
2198
  );
2024
- this.stmtDeleteEntity = this.db.prepare(`DELETE FROM graph_entities WHERE name = ?`);
2025
- this.stmtSelectAllEntities = this.db.prepare(`SELECT * FROM graph_entities`);
2026
- this.stmtSelectEntityByName = this.db.prepare(`SELECT * FROM graph_entities WHERE name = ?`);
2199
+ this.stmtDeleteEntity = this.db.prepare(`DELETE FROM graph_entities WHERE projectId = ? AND name = ?`);
2200
+ this.stmtSelectAllEntities = this.db.prepare(`SELECT * FROM graph_entities WHERE projectId = ? ORDER BY name`);
2201
+ this.stmtSelectEntityByName = this.db.prepare(`SELECT * FROM graph_entities WHERE projectId = ? AND name = ?`);
2027
2202
  this.stmtInsertRelation = this.db.prepare(
2028
- `INSERT OR IGNORE INTO graph_relations (from_entity, to_entity, relationType) VALUES (@from, @to, @relationType)`
2203
+ `INSERT OR IGNORE INTO graph_relations (projectId, from_entity, to_entity, relationType) VALUES (@projectId, @from, @to, @relationType)`
2029
2204
  );
2030
2205
  this.stmtDeleteRelation = this.db.prepare(
2031
- `DELETE FROM graph_relations WHERE from_entity = ? AND to_entity = ? AND relationType = ?`
2206
+ `DELETE FROM graph_relations WHERE projectId = ? AND from_entity = ? AND to_entity = ? AND relationType = ?`
2032
2207
  );
2033
2208
  this.stmtDeleteRelationsByEntity = this.db.prepare(
2034
- `DELETE FROM graph_relations WHERE from_entity = ? OR to_entity = ?`
2209
+ `DELETE FROM graph_relations WHERE projectId = ? AND (from_entity = ? OR to_entity = ?)`
2035
2210
  );
2036
- this.stmtSelectAllRelations = this.db.prepare(`SELECT * FROM graph_relations`);
2211
+ this.stmtSelectAllRelations = this.db.prepare(`SELECT * FROM graph_relations WHERE projectId = ? ORDER BY id`);
2037
2212
  await this.migrateFromJsonlIfNeeded();
2038
2213
  }
2039
2214
  async migrateFromJsonlIfNeeded() {
2040
- const count2 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM graph_entities`).get();
2215
+ const count2 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM graph_entities WHERE projectId = ?`).get(this.projectId);
2216
+ const totalCount = this.db.prepare(`SELECT COUNT(*) AS cnt FROM graph_entities`).get();
2041
2217
  if (count2.cnt > 0) return;
2218
+ if (totalCount.cnt > 0) return;
2042
2219
  const jsonlPath = path7.join(this.dataDir, "graph.jsonl");
2043
2220
  if (!fs5.existsSync(jsonlPath)) return;
2044
2221
  try {
2045
2222
  const data = await loadGraphJsonl(this.dataDir);
2046
2223
  if (data.entities.length === 0 && data.relations.length === 0) return;
2047
2224
  console.error(`[memorix] Migrating graph from JSONL to SQLite (${data.entities.length} entities, ${data.relations.length} relations)...`);
2225
+ const observations2 = this.db.prepare("SELECT id, projectId FROM observations").all();
2226
+ const projectByObservationId = new Map(observations2.map((row) => [row.id, row.projectId]));
2227
+ const projectsByEntity = /* @__PURE__ */ new Map();
2228
+ for (const entity of data.entities) {
2229
+ const projects = /* @__PURE__ */ new Set();
2230
+ for (const content of entity.observations ?? []) {
2231
+ const match2 = /^\[#(\d+)\]/.exec(content);
2232
+ const projectId = match2 ? projectByObservationId.get(Number(match2[1])) : void 0;
2233
+ if (projectId) projects.add(projectId);
2234
+ }
2235
+ projectsByEntity.set(entity.name, projects);
2236
+ }
2237
+ let changed = true;
2238
+ while (changed) {
2239
+ changed = false;
2240
+ for (const relation of data.relations) {
2241
+ const from = projectsByEntity.get(relation.from) ?? /* @__PURE__ */ new Set();
2242
+ const to = projectsByEntity.get(relation.to) ?? /* @__PURE__ */ new Set();
2243
+ if (from.size > 0 && to.size === 0) {
2244
+ projectsByEntity.set(relation.to, new Set(from));
2245
+ changed = true;
2246
+ } else if (to.size > 0 && from.size === 0) {
2247
+ projectsByEntity.set(relation.from, new Set(to));
2248
+ changed = true;
2249
+ }
2250
+ }
2251
+ }
2252
+ const entityRows = data.entities.flatMap((entity) => {
2253
+ const projects = projectsByEntity.get(entity.name) ?? /* @__PURE__ */ new Set();
2254
+ const owners = projects.size > 0 ? [...projects] : [this.projectId];
2255
+ return owners.map((projectId) => ({ ...entityToRow(entity), projectId }));
2256
+ });
2257
+ const projectSet = new Set(entityRows.map((row) => String(row.projectId)));
2258
+ const relationRows = data.relations.flatMap((relation) => {
2259
+ const from = projectsByEntity.get(relation.from) ?? /* @__PURE__ */ new Set();
2260
+ const to = projectsByEntity.get(relation.to) ?? /* @__PURE__ */ new Set();
2261
+ const shared = [...from].filter((projectId) => to.has(projectId));
2262
+ const owners = shared.length > 0 ? shared : from.size === 0 && to.size === 0 ? [this.projectId] : [];
2263
+ return owners.filter((projectId) => projectSet.has(projectId)).map((projectId) => ({ projectId, from: relation.from, to: relation.to, relationType: relation.relationType }));
2264
+ });
2048
2265
  const insertAll = this.db.transaction(() => {
2049
- for (const entity of data.entities) {
2050
- this.stmtInsertEntity.run(entityToRow(entity));
2266
+ for (const entity of entityRows) {
2267
+ this.stmtInsertEntity.run(entity);
2051
2268
  }
2052
- for (const rel of data.relations) {
2053
- this.stmtInsertRelation.run({ from: rel.from, to: rel.to, relationType: rel.relationType });
2269
+ for (const relation of relationRows) {
2270
+ this.stmtInsertRelation.run(relation);
2054
2271
  }
2055
2272
  });
2056
2273
  insertAll();
@@ -2060,10 +2277,10 @@ var init_graph_store = __esm({
2060
2277
  }
2061
2278
  }
2062
2279
  loadEntities() {
2063
- return this.stmtSelectAllEntities.all().map(rowToEntity);
2280
+ return this.stmtSelectAllEntities.all(this.projectId).map(rowToEntity);
2064
2281
  }
2065
2282
  loadRelations() {
2066
- return this.stmtSelectAllRelations.all().map((row) => ({
2283
+ return this.stmtSelectAllRelations.all(this.projectId).map((row) => ({
2067
2284
  from: row.from_entity,
2068
2285
  to: row.to_entity,
2069
2286
  relationType: row.relationType
@@ -2072,7 +2289,7 @@ var init_graph_store = __esm({
2072
2289
  insertEntities(entities) {
2073
2290
  const insertAll = this.db.transaction(() => {
2074
2291
  for (const entity of entities) {
2075
- this.stmtInsertEntity.run(entityToRow(entity));
2292
+ this.stmtInsertEntity.run({ ...entityToRow(entity), projectId: this.projectId });
2076
2293
  }
2077
2294
  });
2078
2295
  insertAll();
@@ -2080,7 +2297,7 @@ var init_graph_store = __esm({
2080
2297
  insertRelations(relations) {
2081
2298
  const insertAll = this.db.transaction(() => {
2082
2299
  for (const rel of relations) {
2083
- this.stmtInsertRelation.run({ from: rel.from, to: rel.to, relationType: rel.relationType });
2300
+ this.stmtInsertRelation.run({ projectId: this.projectId, from: rel.from, to: rel.to, relationType: rel.relationType });
2084
2301
  }
2085
2302
  });
2086
2303
  insertAll();
@@ -2088,8 +2305,8 @@ var init_graph_store = __esm({
2088
2305
  deleteEntities(names) {
2089
2306
  const deleteAll = this.db.transaction(() => {
2090
2307
  for (const name of names) {
2091
- this.stmtDeleteRelationsByEntity.run(name, name);
2092
- this.stmtDeleteEntity.run(name);
2308
+ this.stmtDeleteRelationsByEntity.run(this.projectId, name, name);
2309
+ this.stmtDeleteEntity.run(this.projectId, name);
2093
2310
  }
2094
2311
  });
2095
2312
  deleteAll();
@@ -2097,7 +2314,7 @@ var init_graph_store = __esm({
2097
2314
  deleteRelations(relations) {
2098
2315
  const deleteAll = this.db.transaction(() => {
2099
2316
  for (const rel of relations) {
2100
- this.stmtDeleteRelation.run(rel.from, rel.to, rel.relationType);
2317
+ this.stmtDeleteRelation.run(this.projectId, rel.from, rel.to, rel.relationType);
2101
2318
  }
2102
2319
  });
2103
2320
  deleteAll();
@@ -2105,13 +2322,13 @@ var init_graph_store = __esm({
2105
2322
  addObservations(updates) {
2106
2323
  const updateAll = this.db.transaction(() => {
2107
2324
  for (const u of updates) {
2108
- const row = this.stmtSelectEntityByName.get(u.entityName);
2325
+ const row = this.stmtSelectEntityByName.get(this.projectId, u.entityName);
2109
2326
  if (!row) continue;
2110
2327
  const existing = safeJsonParse(row.observations, []);
2111
2328
  const newObs = u.contents.filter((c) => !existing.includes(c));
2112
2329
  if (newObs.length > 0) {
2113
2330
  existing.push(...newObs);
2114
- this.stmtUpdateEntityObs.run({ name: u.entityName, observations: JSON.stringify(existing) });
2331
+ this.stmtUpdateEntityObs.run({ projectId: this.projectId, name: u.entityName, observations: JSON.stringify(existing) });
2115
2332
  }
2116
2333
  }
2117
2334
  });
@@ -2120,11 +2337,11 @@ var init_graph_store = __esm({
2120
2337
  deleteObservations(deletions) {
2121
2338
  const deleteAll = this.db.transaction(() => {
2122
2339
  for (const d of deletions) {
2123
- const row = this.stmtSelectEntityByName.get(d.entityName);
2340
+ const row = this.stmtSelectEntityByName.get(this.projectId, d.entityName);
2124
2341
  if (!row) continue;
2125
2342
  const existing = safeJsonParse(row.observations, []);
2126
2343
  const filtered = existing.filter((o) => !d.observations.includes(o));
2127
- this.stmtUpdateEntityObs.run({ name: d.entityName, observations: JSON.stringify(filtered) });
2344
+ this.stmtUpdateEntityObs.run({ projectId: this.projectId, name: d.entityName, observations: JSON.stringify(filtered) });
2128
2345
  }
2129
2346
  });
2130
2347
  deleteAll();
@@ -2135,13 +2352,13 @@ var init_graph_store = __esm({
2135
2352
  */
2136
2353
  replaceAll(entities, relations) {
2137
2354
  const replaceAll = this.db.transaction(() => {
2138
- this.db.prepare("DELETE FROM graph_relations").run();
2139
- this.db.prepare("DELETE FROM graph_entities").run();
2355
+ this.db.prepare("DELETE FROM graph_relations WHERE projectId = ?").run(this.projectId);
2356
+ this.db.prepare("DELETE FROM graph_entities WHERE projectId = ?").run(this.projectId);
2140
2357
  for (const entity of entities) {
2141
- this.stmtInsertEntity.run(entityToRow(entity));
2358
+ this.stmtInsertEntity.run({ ...entityToRow(entity), projectId: this.projectId });
2142
2359
  }
2143
2360
  for (const rel of relations) {
2144
- this.stmtInsertRelation.run({ from: rel.from, to: rel.to, relationType: rel.relationType });
2361
+ this.stmtInsertRelation.run({ projectId: this.projectId, from: rel.from, to: rel.to, relationType: rel.relationType });
2145
2362
  }
2146
2363
  });
2147
2364
  replaceAll();
@@ -2149,8 +2366,8 @@ var init_graph_store = __esm({
2149
2366
  close() {
2150
2367
  }
2151
2368
  };
2152
- _graphStore = null;
2153
- _graphDataDir = null;
2369
+ _graphStores = /* @__PURE__ */ new Map();
2370
+ _activeGraphKey = null;
2154
2371
  }
2155
2372
  });
2156
2373
 
@@ -2165,11 +2382,14 @@ var init_graph = __esm({
2165
2382
  entities = [];
2166
2383
  relations = [];
2167
2384
  projectDir;
2385
+ projectId;
2386
+ store = null;
2168
2387
  initialized = false;
2169
2388
  /** Index: lowercase entity name → Entity for O(1) lookups */
2170
2389
  entityIndex = /* @__PURE__ */ new Map();
2171
- constructor(projectDir2) {
2390
+ constructor(projectDir2, projectId = "__default__") {
2172
2391
  this.projectDir = projectDir2;
2392
+ this.projectId = projectId;
2173
2393
  }
2174
2394
  /** Rebuild the entity name index */
2175
2395
  rebuildIndex() {
@@ -2181,10 +2401,9 @@ var init_graph = __esm({
2181
2401
  /** Load graph from SQLite on first access */
2182
2402
  async init() {
2183
2403
  if (this.initialized) return;
2184
- await initGraphStore(this.projectDir);
2185
- const store = getGraphStore();
2186
- this.entities = store.loadEntities();
2187
- this.relations = store.loadRelations();
2404
+ this.store = await initGraphStore(this.projectDir, this.projectId);
2405
+ this.entities = this.store.loadEntities();
2406
+ this.relations = this.store.loadRelations();
2188
2407
  this.rebuildIndex();
2189
2408
  this.initialized = true;
2190
2409
  }
@@ -2198,8 +2417,8 @@ var init_graph = __esm({
2198
2417
  }
2199
2418
  /** Persist current state to SQLite */
2200
2419
  async save() {
2201
- const store = getGraphStore();
2202
- store.replaceAll(this.entities, this.relations);
2420
+ if (!this.store) throw new Error("[memorix] KnowledgeGraphManager not initialized");
2421
+ this.store.replaceAll(this.entities, this.relations);
2203
2422
  }
2204
2423
  /** Create new entities (skip duplicates by name) */
2205
2424
  async createEntities(entities) {
@@ -3085,18 +3304,23 @@ __export(yaml_loader_exports, {
3085
3304
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
3086
3305
  import { homedir } from "os";
3087
3306
  import { parse as parseYamlDocument } from "yaml";
3307
+ function cacheKey(homeDir, projectRoot) {
3308
+ return `${homeDir}\0${projectRoot ?? ""}`;
3309
+ }
3088
3310
  function initProjectRoot(root) {
3089
3311
  globalProjectRoot = root;
3090
- configCache.delete(root);
3312
+ configCache.delete(cacheKey(homedir(), root));
3091
3313
  }
3092
3314
  function clearProjectRoot() {
3093
3315
  globalProjectRoot = null;
3094
3316
  }
3095
- function loadYamlConfig(projectRoot) {
3317
+ function loadYamlConfig(projectRoot, options = {}) {
3318
+ const homeDir = options.homeDir ?? homedir();
3096
3319
  const resolvedRoot = projectRoot === null ? null : projectRoot ?? globalProjectRoot ?? null;
3097
- const cached = configCache.get(resolvedRoot ?? null);
3320
+ const key = cacheKey(homeDir, resolvedRoot);
3321
+ const cached = configCache.get(key);
3098
3322
  if (cached) return cached;
3099
- const userYaml = getGlobalYamlPath(homedir());
3323
+ const userYaml = getGlobalYamlPath(homeDir);
3100
3324
  const projectYaml = resolvedRoot ? getProjectYamlPath(resolvedRoot) : null;
3101
3325
  let userConfig = {};
3102
3326
  let projectConfig = {};
@@ -3128,12 +3352,12 @@ function loadYamlConfig(projectRoot) {
3128
3352
  server: { ...userConfig.server, ...projectConfig.server },
3129
3353
  team: { ...userConfig.team, ...projectConfig.team }
3130
3354
  };
3131
- configCache.set(resolvedRoot ?? null, merged);
3355
+ configCache.set(key, merged);
3132
3356
  return merged;
3133
3357
  }
3134
- function resetYamlConfigCache(projectRoot) {
3358
+ function resetYamlConfigCache(projectRoot, homeDir = homedir()) {
3135
3359
  if (projectRoot !== void 0) {
3136
- configCache.delete(projectRoot ?? null);
3360
+ configCache.delete(cacheKey(homeDir, projectRoot ?? null));
3137
3361
  } else {
3138
3362
  configCache.clear();
3139
3363
  }
@@ -3343,9 +3567,9 @@ var init_detector = __esm({
3343
3567
  // src/config/legacy-loader.ts
3344
3568
  import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
3345
3569
  import { homedir as homedir2 } from "os";
3346
- function loadFileConfig() {
3570
+ function loadFileConfig(homeDir = homedir2()) {
3347
3571
  if (cachedConfig !== null) return cachedConfig;
3348
- const configPath = getLegacyConfigJsonPath(homedir2());
3572
+ const configPath = getLegacyConfigJsonPath(homeDir);
3349
3573
  try {
3350
3574
  if (existsSync4(configPath)) {
3351
3575
  cachedConfig = JSON.parse(readFileSync4(configPath, "utf-8"));
@@ -3375,15 +3599,15 @@ import { homedir as homedir3 } from "os";
3375
3599
  function loadTomlConfig(options = {}) {
3376
3600
  const homeDir = options.homeDir ?? homedir3();
3377
3601
  const projectRoot = options.projectRoot ?? null;
3378
- const cacheKey = `${homeDir}\0${projectRoot ?? ""}`;
3379
- const cached = configCache2.get(cacheKey);
3602
+ const cacheKey3 = `${homeDir}\0${projectRoot ?? ""}`;
3603
+ const cached = configCache2.get(cacheKey3);
3380
3604
  if (cached) return cached;
3381
3605
  const userToml = getGlobalConfigTomlPath(homeDir);
3382
3606
  const projectToml = projectRoot ? getProjectConfigTomlPath(projectRoot) : null;
3383
3607
  const userConfig = existsSync5(userToml) ? parseTomlConfig(readFileSync5(userToml, "utf8"), userToml) : {};
3384
3608
  const projectConfig = projectToml && existsSync5(projectToml) ? parseTomlConfig(readFileSync5(projectToml, "utf8"), projectToml) : {};
3385
3609
  const merged = mergeTomlConfig(userConfig, projectConfig);
3386
- configCache2.set(cacheKey, merged);
3610
+ configCache2.set(cacheKey3, merged);
3387
3611
  return merged;
3388
3612
  }
3389
3613
  function mergeTomlConfig(base, override) {
@@ -3583,8 +3807,8 @@ function getResolvedConfig(options = {}) {
3583
3807
  const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
3584
3808
  loadDotenv(projectRoot === null ? void 0 : projectRoot ?? void 0, { userHomeDir: homeDir });
3585
3809
  const toml = loadTomlConfig({ projectRoot: projectRoot ?? null, homeDir });
3586
- const yaml = loadYamlConfig(projectRoot ?? null);
3587
- const legacy = loadFileConfig();
3810
+ const yaml = loadYamlConfig(projectRoot ?? null, { homeDir });
3811
+ const legacy = loadFileConfig(homeDir);
3588
3812
  const embeddingBaseUrl = first(process.env.MEMORIX_EMBEDDING_BASE_URL, toml.embedding?.base_url, yaml.embedding?.baseUrl, legacy.embeddingApi?.baseUrl);
3589
3813
  const openRouterEmbeddingApiKey = isOpenRouterUrl(embeddingBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
3590
3814
  const memoryLlmProvider = first(
@@ -3606,15 +3830,17 @@ function getResolvedConfig(options = {}) {
3606
3830
  legacy.llm?.baseUrl
3607
3831
  );
3608
3832
  const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
3833
+ const isAtlasCloud = memoryLlmProvider === "atlascloud";
3609
3834
  const memoryLlmApiKey = first(
3610
3835
  process.env.MEMORIX_LLM_API_KEY,
3611
3836
  process.env.MEMORIX_API_KEY,
3612
3837
  toml.memory?.llm?.api_key,
3613
3838
  yaml.llm?.apiKey,
3614
3839
  legacy.llm?.apiKey,
3615
- process.env.OPENAI_API_KEY,
3616
- process.env.ANTHROPIC_API_KEY,
3617
- openRouterMemoryLlmApiKey
3840
+ // Never borrow another provider's credentials for the Atlas Cloud preset.
3841
+ isAtlasCloud ? process.env.ATLASCLOUD_API_KEY : process.env.OPENAI_API_KEY,
3842
+ isAtlasCloud ? void 0 : process.env.ANTHROPIC_API_KEY,
3843
+ isAtlasCloud ? void 0 : openRouterMemoryLlmApiKey
3618
3844
  );
3619
3845
  const resolved = {
3620
3846
  agent: {
@@ -3793,7 +4019,8 @@ function getEnvSourceNames() {
3793
4019
  "MEMORIX_CODEGRAPH_EXTERNAL_CONTEXT",
3794
4020
  "MEMORIX_CODEGRAPH_EXTERNAL_COMMAND",
3795
4021
  "MEMORIX_CODEGRAPH_EXTERNAL_TIMEOUT_MS",
3796
- "OPENROUTER_API_KEY"
4022
+ "OPENROUTER_API_KEY",
4023
+ "ATLASCLOUD_API_KEY"
3797
4024
  ].filter((name) => process.env[name]);
3798
4025
  }
3799
4026
  function isOpenRouterUrl(value) {
@@ -4012,12 +4239,45 @@ function textHash(text3) {
4012
4239
  async function loadDiskCache() {
4013
4240
  try {
4014
4241
  const raw = await readFile(CACHE_FILE, "utf-8");
4242
+ if (Buffer.byteLength(raw, "utf8") > MAX_CACHE_FILE_BYTES) {
4243
+ console.error("[memorix] FastEmbed cache exceeds the disk load limit; starting with an empty cache");
4244
+ return;
4245
+ }
4015
4246
  const entries = JSON.parse(raw);
4016
- for (const [k, v] of entries) cache.set(k, v);
4017
- console.error(`[memorix] Loaded ${entries.length} cached embeddings from disk`);
4247
+ if (!Array.isArray(entries)) return;
4248
+ for (const entry of entries) {
4249
+ if (!Array.isArray(entry) || typeof entry[0] !== "string" || !isVector(entry[1])) continue;
4250
+ cacheSet(entry[0], entry[1], false);
4251
+ }
4252
+ console.error(`[memorix] Loaded ${cache.size} cached embeddings from disk`);
4018
4253
  } catch {
4019
4254
  }
4020
4255
  }
4256
+ function isVector(value) {
4257
+ return Array.isArray(value) && value.every((entry) => typeof entry === "number");
4258
+ }
4259
+ function maxCachePayloadBytes() {
4260
+ const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
4261
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES;
4262
+ }
4263
+ function cacheSet(hash4, value, markDirty = true) {
4264
+ const incomingBytes = value.length * 8;
4265
+ if (incomingBytes > maxCachePayloadBytes()) return;
4266
+ const existing = cache.get(hash4);
4267
+ if (existing) cachePayloadBytes -= existing.length * 8;
4268
+ else {
4269
+ while (cache.size >= MAX_CACHE_SIZE || cachePayloadBytes + incomingBytes > maxCachePayloadBytes()) {
4270
+ const firstKey = cache.keys().next().value;
4271
+ if (firstKey === void 0) break;
4272
+ const old = cache.get(firstKey);
4273
+ cache.delete(firstKey);
4274
+ if (old) cachePayloadBytes -= old.length * 8;
4275
+ }
4276
+ }
4277
+ cache.set(hash4, value);
4278
+ cachePayloadBytes += incomingBytes;
4279
+ if (markDirty) diskCacheDirty = true;
4280
+ }
4021
4281
  async function saveDiskCache() {
4022
4282
  if (!diskCacheDirty) return;
4023
4283
  try {
@@ -4028,7 +4288,7 @@ async function saveDiskCache() {
4028
4288
  } catch {
4029
4289
  }
4030
4290
  }
4031
- var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, diskCacheDirty, FastEmbedProvider;
4291
+ var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, DEFAULT_CACHE_PAYLOAD_BYTES, MAX_CACHE_FILE_BYTES, cachePayloadBytes, diskCacheDirty, FastEmbedProvider;
4032
4292
  var init_fastembed_provider = __esm({
4033
4293
  "src/embedding/fastembed-provider.ts"() {
4034
4294
  "use strict";
@@ -4038,6 +4298,9 @@ var init_fastembed_provider = __esm({
4038
4298
  CACHE_FILE = join2(CACHE_DIR, ".embedding-cache.json");
4039
4299
  cache = /* @__PURE__ */ new Map();
4040
4300
  MAX_CACHE_SIZE = 5e3;
4301
+ DEFAULT_CACHE_PAYLOAD_BYTES = 64 * 1024 * 1024;
4302
+ MAX_CACHE_FILE_BYTES = 128 * 1024 * 1024;
4303
+ cachePayloadBytes = 0;
4041
4304
  diskCacheDirty = false;
4042
4305
  FastEmbedProvider = class _FastEmbedProvider {
4043
4306
  name = "fastembed-bge-small";
@@ -4079,7 +4342,7 @@ var init_fastembed_provider = __esm({
4079
4342
  if (result.length !== this.dimensions) {
4080
4343
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
4081
4344
  }
4082
- this.cacheSet(hash4, result);
4345
+ cacheSet(hash4, result);
4083
4346
  return result;
4084
4347
  }
4085
4348
  async embedBatch(texts) {
@@ -4104,7 +4367,7 @@ var init_fastembed_provider = __esm({
4104
4367
  const originalIdx = uncachedIndices[batchIdx];
4105
4368
  const plain = Array.from(vec);
4106
4369
  results[originalIdx] = plain;
4107
- this.cacheSet(textHash(uncachedTexts[batchIdx]), plain);
4370
+ cacheSet(textHash(uncachedTexts[batchIdx]), plain);
4108
4371
  batchIdx++;
4109
4372
  }
4110
4373
  }
@@ -4112,14 +4375,6 @@ var init_fastembed_provider = __esm({
4112
4375
  }
4113
4376
  return results;
4114
4377
  }
4115
- cacheSet(hash4, value) {
4116
- if (cache.size >= MAX_CACHE_SIZE) {
4117
- const firstKey = cache.keys().next().value;
4118
- if (firstKey !== void 0) cache.delete(firstKey);
4119
- }
4120
- cache.set(hash4, value);
4121
- diskCacheDirty = true;
4122
- }
4123
4378
  };
4124
4379
  }
4125
4380
  });
@@ -4129,7 +4384,15 @@ var transformers_provider_exports = {};
4129
4384
  __export(transformers_provider_exports, {
4130
4385
  TransformersProvider: () => TransformersProvider
4131
4386
  });
4132
- var cache2, MAX_CACHE_SIZE2, TransformersProvider;
4387
+ import { createHash as createHash2 } from "crypto";
4388
+ function cacheKey2(text3) {
4389
+ return createHash2("sha256").update(text3).digest("hex");
4390
+ }
4391
+ function maxCachePayloadBytes2() {
4392
+ const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
4393
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES2;
4394
+ }
4395
+ var cache2, MAX_CACHE_SIZE2, DEFAULT_CACHE_PAYLOAD_BYTES2, cachePayloadBytes2, TransformersProvider;
4133
4396
  var init_transformers_provider = __esm({
4134
4397
  "src/embedding/transformers-provider.ts"() {
4135
4398
  "use strict";
@@ -4137,6 +4400,8 @@ var init_transformers_provider = __esm({
4137
4400
  init_provider();
4138
4401
  cache2 = /* @__PURE__ */ new Map();
4139
4402
  MAX_CACHE_SIZE2 = 5e3;
4403
+ DEFAULT_CACHE_PAYLOAD_BYTES2 = 64 * 1024 * 1024;
4404
+ cachePayloadBytes2 = 0;
4140
4405
  TransformersProvider = class _TransformersProvider {
4141
4406
  name = "transformers-minilm";
4142
4407
  dimensions = 384;
@@ -4171,7 +4436,7 @@ var init_transformers_provider = __esm({
4171
4436
  return Promise.all(inputs.map((input) => this.embedInput(input, options)));
4172
4437
  }
4173
4438
  async embed(text3) {
4174
- const cached = cache2.get(text3);
4439
+ const cached = cache2.get(cacheKey2(text3));
4175
4440
  if (cached) return cached;
4176
4441
  const output = await this.extractor(text3, {
4177
4442
  pooling: "mean",
@@ -4181,7 +4446,7 @@ var init_transformers_provider = __esm({
4181
4446
  if (result.length !== this.dimensions) {
4182
4447
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
4183
4448
  }
4184
- this.cacheSet(text3, result);
4449
+ this.cacheSet(cacheKey2(text3), result);
4185
4450
  return result;
4186
4451
  }
4187
4452
  async embedBatch(texts) {
@@ -4189,7 +4454,7 @@ var init_transformers_provider = __esm({
4189
4454
  const uncachedIndices = [];
4190
4455
  const uncachedTexts = [];
4191
4456
  for (let i2 = 0; i2 < texts.length; i2++) {
4192
- const cached = cache2.get(texts[i2]);
4457
+ const cached = cache2.get(cacheKey2(texts[i2]));
4193
4458
  if (cached) {
4194
4459
  results[i2] = cached;
4195
4460
  } else {
@@ -4207,17 +4472,27 @@ var init_transformers_provider = __esm({
4207
4472
  const vec = Array.from(allVecs[i2]);
4208
4473
  const originalIdx = uncachedIndices[i2];
4209
4474
  results[originalIdx] = vec;
4210
- this.cacheSet(uncachedTexts[i2], vec);
4475
+ this.cacheSet(cacheKey2(uncachedTexts[i2]), vec);
4211
4476
  }
4212
4477
  }
4213
4478
  return results;
4214
4479
  }
4215
4480
  cacheSet(key, value) {
4216
- if (cache2.size >= MAX_CACHE_SIZE2) {
4217
- const firstKey = cache2.keys().next().value;
4218
- if (firstKey !== void 0) cache2.delete(firstKey);
4481
+ const incomingBytes = value.length * 8;
4482
+ if (incomingBytes > maxCachePayloadBytes2()) return;
4483
+ const existing = cache2.get(key);
4484
+ if (existing) cachePayloadBytes2 -= existing.length * 8;
4485
+ else {
4486
+ while (cache2.size >= MAX_CACHE_SIZE2 || cachePayloadBytes2 + incomingBytes > maxCachePayloadBytes2()) {
4487
+ const firstKey = cache2.keys().next().value;
4488
+ if (firstKey === void 0) break;
4489
+ const old = cache2.get(firstKey);
4490
+ cache2.delete(firstKey);
4491
+ if (old) cachePayloadBytes2 -= old.length * 8;
4492
+ }
4219
4493
  }
4220
4494
  cache2.set(key, value);
4495
+ cachePayloadBytes2 += incomingBytes;
4221
4496
  }
4222
4497
  };
4223
4498
  }
@@ -4239,7 +4514,7 @@ __export(api_provider_exports, {
4239
4514
  saveDiskCacheNow: () => saveDiskCacheNow,
4240
4515
  seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
4241
4516
  });
4242
- import { createHash as createHash2 } from "crypto";
4517
+ import { createHash as createHash3 } from "crypto";
4243
4518
  import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
4244
4519
  import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
4245
4520
  import { join as join3 } from "path";
@@ -4266,7 +4541,7 @@ function cacheStatFile() {
4266
4541
  function payloadBytes(vector) {
4267
4542
  return vector.length * BYTES_PER_DIM;
4268
4543
  }
4269
- function maxCachePayloadBytes() {
4544
+ function maxCachePayloadBytes3() {
4270
4545
  const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
4271
4546
  if (raw) {
4272
4547
  const parsed = Number.parseInt(raw, 10);
@@ -4276,7 +4551,7 @@ function maxCachePayloadBytes() {
4276
4551
  }
4277
4552
  function maxEntriesForDimensions(dimensions) {
4278
4553
  const dim = Math.max(1, Math.floor(dimensions));
4279
- const byBytes = Math.floor(maxCachePayloadBytes() / (dim * BYTES_PER_DIM));
4554
+ const byBytes = Math.floor(maxCachePayloadBytes3() / (dim * BYTES_PER_DIM));
4280
4555
  return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
4281
4556
  }
4282
4557
  function rememberDimensions(vector) {
@@ -4289,13 +4564,15 @@ function evictOldest() {
4289
4564
  if (firstKey === void 0) return;
4290
4565
  const old = cache3.get(firstKey);
4291
4566
  cache3.delete(firstKey);
4292
- if (old) cachePayloadBytes -= payloadBytes(old);
4567
+ if (old) cachePayloadBytes3 -= payloadBytes(old);
4293
4568
  }
4294
4569
  function evictToFit(incomingBytes, incomingDims) {
4570
+ if (incomingBytes > maxCachePayloadBytes3()) return false;
4295
4571
  const limit2 = maxEntriesForDimensions(incomingDims);
4296
- while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes + incomingBytes > maxCachePayloadBytes())) {
4572
+ while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes3 + incomingBytes > maxCachePayloadBytes3())) {
4297
4573
  evictOldest();
4298
4574
  }
4575
+ return true;
4299
4576
  }
4300
4577
  function isNumberArray(value) {
4301
4578
  return Array.isArray(value) && value.every((entry) => typeof entry === "number");
@@ -4322,12 +4599,12 @@ function cacheInsert(hash4, value, dirty) {
4322
4599
  const incoming = payloadBytes(value);
4323
4600
  const existing = cache3.get(hash4);
4324
4601
  if (existing) {
4325
- cachePayloadBytes -= payloadBytes(existing);
4602
+ cachePayloadBytes3 -= payloadBytes(existing);
4326
4603
  cache3.delete(hash4);
4327
4604
  }
4328
- evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
4605
+ if (!evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS)) return;
4329
4606
  cache3.set(hash4, value);
4330
- cachePayloadBytes += incoming;
4607
+ cachePayloadBytes3 += incoming;
4331
4608
  if (dirty) diskCacheDirty2 = true;
4332
4609
  }
4333
4610
  async function readCacheStat() {
@@ -4363,7 +4640,7 @@ function cacheNamespace(config) {
4363
4640
  ].join("|");
4364
4641
  }
4365
4642
  function textHash2(text3, namespace) {
4366
- return createHash2("sha256").update(`${namespace}\0${text3}`).digest("hex").slice(0, 16);
4643
+ return createHash3("sha256").update(`${namespace}\0${text3}`).digest("hex").slice(0, 16);
4367
4644
  }
4368
4645
  function inputIdentity(input, options = {}) {
4369
4646
  return JSON.stringify({
@@ -4705,7 +4982,7 @@ function getApiEmbeddingDiskCacheLoadCountForTests() {
4705
4982
  }
4706
4983
  function resetApiEmbeddingCacheForTests() {
4707
4984
  cache3.clear();
4708
- cachePayloadBytes = 0;
4985
+ cachePayloadBytes3 = 0;
4709
4986
  cacheDimensions = null;
4710
4987
  diskCacheEntryCount = 0;
4711
4988
  diskCacheLoaded = false;
@@ -4716,14 +4993,14 @@ function resetApiEmbeddingCacheForTests() {
4716
4993
  }
4717
4994
  function seedApiEmbeddingCacheForTests(entries) {
4718
4995
  cache3.clear();
4719
- cachePayloadBytes = 0;
4996
+ cachePayloadBytes3 = 0;
4720
4997
  cacheDimensions = null;
4721
4998
  for (const [key, value] of entries) cacheInsert(key, value, true);
4722
4999
  diskCacheLoaded = true;
4723
5000
  diskCacheDirty2 = true;
4724
5001
  }
4725
5002
  function getApiEmbeddingCachePayloadBytesForTests() {
4726
- return cachePayloadBytes;
5003
+ return cachePayloadBytes3;
4727
5004
  }
4728
5005
  function getApiEmbeddingJsonlLineCountCallsForTests() {
4729
5006
  return jsonlLineCountCalls;
@@ -4897,7 +5174,7 @@ function scheduleDiskSave() {
4897
5174
  diskSaveTimer = null;
4898
5175
  }, DISK_SAVE_DEBOUNCE_MS);
4899
5176
  }
4900
- function cacheSet(hash4, value) {
5177
+ function cacheSet2(hash4, value) {
4901
5178
  cacheInsert(hash4, value, true);
4902
5179
  }
4903
5180
  function resolveEnvEmbeddingApiKey() {
@@ -5017,7 +5294,7 @@ async function waitForRetry(waitMs, signal) {
5017
5294
  abortSignal.addEventListener("abort", onAbort, { once: true });
5018
5295
  });
5019
5296
  }
5020
- 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;
5297
+ 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;
5021
5298
  var init_api_provider = __esm({
5022
5299
  "src/embedding/api-provider.ts"() {
5023
5300
  "use strict";
@@ -5029,7 +5306,7 @@ var init_api_provider = __esm({
5029
5306
  BYTES_PER_DIM = 8;
5030
5307
  DEFAULT_ASSUMED_DIMENSIONS = 4096;
5031
5308
  MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
5032
- cachePayloadBytes = 0;
5309
+ cachePayloadBytes3 = 0;
5033
5310
  cacheDimensions = null;
5034
5311
  diskCacheEntryCount = 0;
5035
5312
  diskCacheDirty2 = false;
@@ -5214,7 +5491,7 @@ var init_api_provider = __esm({
5214
5491
  } else {
5215
5492
  embedding = await apiCall();
5216
5493
  }
5217
- cacheSet(hash4, embedding);
5494
+ cacheSet2(hash4, embedding);
5218
5495
  scheduleDiskSave();
5219
5496
  return embedding;
5220
5497
  }
@@ -5262,7 +5539,7 @@ var init_api_provider = __esm({
5262
5539
  for (const item of response.data) {
5263
5540
  const originalIdx = chunkIndices[item.index];
5264
5541
  results[originalIdx] = item.embedding;
5265
- cacheSet(textHash2(normalizedTexts[originalIdx], this.cacheKeyNamespace), item.embedding);
5542
+ cacheSet2(textHash2(normalizedTexts[originalIdx], this.cacheKeyNamespace), item.embedding);
5266
5543
  }
5267
5544
  } catch (error) {
5268
5545
  const providerLimit = parseBatchLimit(error);
@@ -5340,7 +5617,7 @@ var init_api_provider = __esm({
5340
5617
  throw new Error(`Expected ${this.dimensions}d, got ${embedding.length}d; dimension mismatch`);
5341
5618
  }
5342
5619
  this.trackUsage(response);
5343
- cacheSet(hash4, embedding);
5620
+ cacheSet2(hash4, embedding);
5344
5621
  scheduleDiskSave();
5345
5622
  return embedding;
5346
5623
  }
@@ -6723,6 +7000,7 @@ var init_provider2 = __esm({
6723
7000
  openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4.1-nano" },
6724
7001
  anthropic: { baseUrl: "https://api.anthropic.com/v1", model: "claude-3-5-haiku-latest" },
6725
7002
  openrouter: { baseUrl: "https://openrouter.ai/api/v1", model: "openai/gpt-4.1-nano" },
7003
+ atlascloud: { baseUrl: "https://api.atlascloud.ai/v1", model: "deepseek-ai/deepseek-v3.2" },
6726
7004
  custom: { baseUrl: "http://localhost:11434/v1", model: "llama3" }
6727
7005
  };
6728
7006
  currentConfig = null;
@@ -6986,6 +7264,7 @@ var init_sqlite_store = __esm({
6986
7264
  init_sqlite_db();
6987
7265
  init_sqlite_reliability();
6988
7266
  init_sqlite_fts();
7267
+ init_file_lock();
6989
7268
  SqliteBackend = class {
6990
7269
  db = null;
6991
7270
  dataDir = "";
@@ -7149,38 +7428,43 @@ var init_sqlite_store = __esm({
7149
7428
  }
7150
7429
  // ── Migration ────────────────────────────────────────────────────
7151
7430
  async migrateFromJsonIfNeeded() {
7152
- const count2 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM observations`).get();
7153
- if (count2.cnt > 0) return;
7154
7431
  const jsonPath = path10.join(this.dataDir, "observations.json");
7155
7432
  if (!fs7.existsSync(jsonPath)) return;
7156
- try {
7433
+ await withFileLock(this.dataDir, async () => {
7434
+ const count2 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM observations`).get();
7435
+ if (count2.cnt > 0) return;
7157
7436
  const raw = fs7.readFileSync(jsonPath, "utf-8");
7158
- const observations2 = JSON.parse(raw);
7437
+ const parsed = JSON.parse(raw);
7438
+ if (!Array.isArray(parsed)) {
7439
+ throw new Error("[memorix] observations.json migration refused: expected a JSON array");
7440
+ }
7441
+ const observations2 = parsed;
7159
7442
  if (observations2.length === 0) return;
7443
+ if (observations2.some((obs) => !obs || !Number.isSafeInteger(obs.id) || obs.id < 1 || typeof obs.projectId !== "string")) {
7444
+ throw new Error("[memorix] observations.json migration refused: invalid observation record");
7445
+ }
7160
7446
  console.error(`[memorix] Migrating ${observations2.length} observations from JSON to SQLite...`);
7161
7447
  const insertMany = this.db.transaction((obsList) => {
7162
7448
  for (const obs of obsList) {
7163
7449
  this.stmtInsert.run(obsToRow(obs));
7164
7450
  }
7165
- });
7166
- insertMany(observations2);
7167
- const counterPath = path10.join(this.dataDir, "counter.json");
7168
- if (fs7.existsSync(counterPath)) {
7169
- try {
7451
+ const counterPath = path10.join(this.dataDir, "counter.json");
7452
+ let counterNextId;
7453
+ if (fs7.existsSync(counterPath)) {
7170
7454
  const counterData = JSON.parse(fs7.readFileSync(counterPath, "utf-8"));
7171
- const nextId2 = counterData.nextId ?? counterData.next_id ?? Math.max(...observations2.map((o) => o.id)) + 1;
7172
- this.rawSaveIdCounter(nextId2);
7173
- } catch {
7174
- this.rawSaveIdCounter(Math.max(...observations2.map((o) => o.id)) + 1);
7455
+ const candidate = counterData.nextId ?? counterData.next_id;
7456
+ if (candidate !== void 0 && (!Number.isSafeInteger(candidate) || Number(candidate) < 1)) {
7457
+ throw new Error("[memorix] counter.json migration refused: nextId must be a positive integer");
7458
+ }
7459
+ counterNextId = candidate === void 0 ? void 0 : Number(candidate);
7175
7460
  }
7176
- } else {
7177
- this.rawSaveIdCounter(Math.max(...observations2.map((o) => o.id)) + 1);
7178
- }
7179
- this.bumpGeneration();
7461
+ const maxId = Math.max(...obsList.map((obs) => obs.id));
7462
+ this.rawSaveIdCounter(Math.max(counterNextId ?? 1, maxId + 1));
7463
+ this.bumpGeneration();
7464
+ });
7465
+ insertMany(observations2);
7180
7466
  console.error(`[memorix] Migration complete. ${observations2.length} observations now in SQLite.`);
7181
- } catch (err) {
7182
- console.error(`[memorix] JSON\u2192SQLite migration failed (non-fatal, data preserved in JSON): ${err}`);
7183
- }
7467
+ });
7184
7468
  }
7185
7469
  // ── Public read ──────────────────────────────────────────────────
7186
7470
  async loadAll() {
@@ -7480,7 +7764,7 @@ var init_obs_store = __esm({
7480
7764
  });
7481
7765
 
7482
7766
  // src/search/semantic-index.ts
7483
- import { createHash as createHash3 } from "crypto";
7767
+ import { createHash as createHash4 } from "crypto";
7484
7768
  import { createRequire as createRequire3 } from "module";
7485
7769
  import path11 from "path";
7486
7770
  function createSemanticIndexProfile(provider2) {
@@ -7494,7 +7778,7 @@ function stateKey(dataDir, profile) {
7494
7778
  return `${path11.resolve(dataDir)}::${profile.key}`;
7495
7779
  }
7496
7780
  function tableName(profile) {
7497
- const digest2 = createHash3("sha256").update(profile.key).digest("hex").slice(0, 20);
7781
+ const digest2 = createHash4("sha256").update(profile.key).digest("hex").slice(0, 20);
7498
7782
  return `${TABLE_PREFIX}${digest2}`;
7499
7783
  }
7500
7784
  function indexRoot(dataDir) {
@@ -9537,6 +9821,7 @@ var init_secret_filter = __esm({
9537
9821
  // src/runtime/maintenance-jobs.ts
9538
9822
  var maintenance_jobs_exports = {};
9539
9823
  __export(maintenance_jobs_exports, {
9824
+ DEFAULT_FAILED_HISTORY_RETENTION_MS: () => DEFAULT_FAILED_HISTORY_RETENTION_MS,
9540
9825
  MAINTENANCE_JOB_KINDS: () => MAINTENANCE_JOB_KINDS,
9541
9826
  MaintenanceJobStore: () => MaintenanceJobStore,
9542
9827
  MaintenanceJobWorker: () => MaintenanceJobWorker,
@@ -9586,7 +9871,7 @@ function normalizedKinds(kinds) {
9586
9871
  function getMaintenanceRetryDelayMs(attempts) {
9587
9872
  return Math.min(6e4, 1e3 * 2 ** Math.max(0, attempts - 1));
9588
9873
  }
9589
- var MAINTENANCE_JOB_KINDS, DEFAULT_MAX_ATTEMPTS, DEFAULT_LEASE_MS, DEFAULT_POLL_INTERVAL_MS, MAX_ERROR_LENGTH, DEFAULT_COMPLETED_HISTORY_RETENTION_MS, MaintenanceJobStore, MaintenanceJobWorker;
9874
+ 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;
9590
9875
  var init_maintenance_jobs = __esm({
9591
9876
  "src/runtime/maintenance-jobs.ts"() {
9592
9877
  "use strict";
@@ -9613,6 +9898,7 @@ var init_maintenance_jobs = __esm({
9613
9898
  DEFAULT_POLL_INTERVAL_MS = 2e3;
9614
9899
  MAX_ERROR_LENGTH = 1e3;
9615
9900
  DEFAULT_COMPLETED_HISTORY_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
9901
+ DEFAULT_FAILED_HISTORY_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
9616
9902
  MaintenanceJobStore = class {
9617
9903
  db;
9618
9904
  constructor(dataDir) {
@@ -9739,18 +10025,21 @@ var init_maintenance_jobs = __esm({
9739
10025
  }
9740
10026
  /**
9741
10027
  * Completed jobs are useful recent operator history, not permanent data.
9742
- * Failed jobs stay available for diagnosis until an operator explicitly
9743
- * resolves them or clears the database.
10028
+ * Failed diagnostics are retained longer, but are still bounded so a
10029
+ * permanently failing job kind cannot grow the database forever.
9744
10030
  */
9745
10031
  pruneCompletedHistory(options = {}) {
9746
10032
  const now3 = options.now ?? Date.now();
9747
10033
  const maxAgeMs = Number.isFinite(options.maxAgeMs) ? Math.max(0, Math.floor(options.maxAgeMs)) : DEFAULT_COMPLETED_HISTORY_RETENTION_MS;
10034
+ const failedMaxAgeMs = Number.isFinite(options.failedMaxAgeMs) ? Math.max(0, Math.floor(options.failedMaxAgeMs)) : DEFAULT_FAILED_HISTORY_RETENTION_MS;
9748
10035
  const result = this.db.prepare(`
9749
10036
  DELETE FROM maintenance_jobs
9750
- WHERE status = 'completed'
10037
+ WHERE (status = 'completed'
9751
10038
  AND completed_at IS NOT NULL
9752
- AND completed_at < ?
9753
- `).run(now3 - maxAgeMs);
10039
+ AND completed_at < ?)
10040
+ OR (status = 'failed'
10041
+ AND updated_at < ?)
10042
+ `).run(now3 - maxAgeMs, now3 - failedMaxAgeMs);
9754
10043
  return Number(result.changes ?? 0);
9755
10044
  }
9756
10045
  claimNext(options) {
@@ -10104,7 +10393,7 @@ var init_lifecycle = __esm({
10104
10393
  });
10105
10394
 
10106
10395
  // src/store/evidence-store.ts
10107
- import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
10396
+ import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
10108
10397
  function safeJson(value, fallback) {
10109
10398
  if (typeof value !== "string" || value.length === 0) return fallback;
10110
10399
  try {
@@ -10138,7 +10427,7 @@ function rowToCard(row) {
10138
10427
  };
10139
10428
  }
10140
10429
  function observationHash(observation) {
10141
- return createHash4("sha256").update(JSON.stringify({
10430
+ return createHash5("sha256").update(JSON.stringify({
10142
10431
  projectId: observation.projectId,
10143
10432
  id: observation.id,
10144
10433
  title: observation.title,
@@ -10732,13 +11021,13 @@ __export(workspace_exports, {
10732
11021
  loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
10733
11022
  resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
10734
11023
  });
10735
- import { createHash as createHash5 } from "crypto";
11024
+ import { createHash as createHash6 } from "crypto";
10736
11025
  import { execFile } from "child_process";
10737
11026
  import { promises as fs9 } from "fs";
10738
11027
  import path14 from "path";
10739
11028
  import { promisify } from "util";
10740
11029
  function hash(value) {
10741
- return createHash5("sha256").update(value).digest("hex");
11030
+ return createHash6("sha256").update(value).digest("hex");
10742
11031
  }
10743
11032
  function workspaceId(projectId, mode) {
10744
11033
  return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
@@ -11891,14 +12180,14 @@ __export(asset_store_exports, {
11891
12180
  readMediaAsset: () => readMediaAsset,
11892
12181
  removeMediaAsset: () => removeMediaAsset
11893
12182
  });
11894
- import { createHash as createHash6, randomUUID as randomUUID6 } from "crypto";
12183
+ import { createHash as createHash7, randomUUID as randomUUID6 } from "crypto";
11895
12184
  import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
11896
12185
  import path15 from "path";
11897
12186
  function mediaRoot(dataDir) {
11898
12187
  return path15.resolve(dataDir, "media");
11899
12188
  }
11900
12189
  function projectStorageKey(projectId) {
11901
- return createHash6("sha256").update(projectId).digest("hex").slice(0, 16);
12190
+ return createHash7("sha256").update(projectId).digest("hex").slice(0, 16);
11902
12191
  }
11903
12192
  function validateUnderRoot(root, candidate) {
11904
12193
  const resolved = path15.resolve(candidate);
@@ -11958,7 +12247,7 @@ async function readHeader(filePath, bytes = 32) {
11958
12247
  }
11959
12248
  async function sha256File(filePath) {
11960
12249
  const bytes = await readFile3(filePath);
11961
- return createHash6("sha256").update(bytes).digest("hex");
12250
+ return createHash7("sha256").update(bytes).digest("hex");
11962
12251
  }
11963
12252
  function destinationFor(root, projectId, sha256, extension3) {
11964
12253
  const relative5 = path15.join("assets", projectStorageKey(projectId), sha256.slice(0, 2), `${sha256}${extension3}`);
@@ -12064,7 +12353,7 @@ async function importMediaBuffer(input) {
12064
12353
  if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
12065
12354
  if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
12066
12355
  const detected = detectMedia(input.bytes.subarray(0, 32));
12067
- const sha256 = createHash6("sha256").update(input.bytes).digest("hex");
12356
+ const sha256 = createHash7("sha256").update(input.bytes).digest("hex");
12068
12357
  return importKnownMedia({
12069
12358
  dataDir: input.dataDir,
12070
12359
  projectId: input.projectId,
@@ -12082,7 +12371,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
12082
12371
  if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
12083
12372
  const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
12084
12373
  if (bytes.length !== asset.byteSize) throw new Error(`Media asset size no longer matches metadata: ${asset.id}`);
12085
- const sha256 = createHash6("sha256").update(bytes).digest("hex");
12374
+ const sha256 = createHash7("sha256").update(bytes).digest("hex");
12086
12375
  if (sha256 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
12087
12376
  return bytes;
12088
12377
  }
@@ -64952,7 +65241,7 @@ var require_gaxios = __commonJS({
64952
65241
  var retry_js_1 = require_retry3();
64953
65242
  var stream_1 = __require("stream");
64954
65243
  var interceptor_js_1 = require_interceptor();
64955
- var randomUUID19 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
65244
+ var randomUUID20 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
64956
65245
  var HTTP_STATUS_NO_CONTENT = 204;
64957
65246
  var Gaxios = class {
64958
65247
  agentCache = /* @__PURE__ */ new Map();
@@ -65225,7 +65514,7 @@ var require_gaxios = __commonJS({
65225
65514
  */
65226
65515
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
65227
65516
  if (opts.multipart?.length) {
65228
- const boundary = await randomUUID19();
65517
+ const boundary = await randomUUID20();
65229
65518
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
65230
65519
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
65231
65520
  } else if (shouldDirectlyPassData) {
@@ -70897,16 +71186,16 @@ var require_jwtaccess = __commonJS({
70897
71186
  * @returns A string that returns the cached key.
70898
71187
  */
70899
71188
  getCachedKey(url, scopes) {
70900
- let cacheKey = url;
71189
+ let cacheKey3 = url;
70901
71190
  if (scopes && Array.isArray(scopes) && scopes.length) {
70902
- cacheKey = url ? `${url}_${scopes.join("_")}` : `${scopes.join("_")}`;
71191
+ cacheKey3 = url ? `${url}_${scopes.join("_")}` : `${scopes.join("_")}`;
70903
71192
  } else if (typeof scopes === "string") {
70904
- cacheKey = url ? `${url}_${scopes}` : scopes;
71193
+ cacheKey3 = url ? `${url}_${scopes}` : scopes;
70905
71194
  }
70906
- if (!cacheKey) {
71195
+ if (!cacheKey3) {
70907
71196
  throw Error("Scopes or url must be provided");
70908
71197
  }
70909
- return cacheKey;
71198
+ return cacheKey3;
70910
71199
  }
70911
71200
  /**
70912
71201
  * Get a non-expired access token, after refreshing if necessary.
@@ -77452,7 +77741,7 @@ var require_websocket = __commonJS({
77452
77741
  var http3 = __require("http");
77453
77742
  var net = __require("net");
77454
77743
  var tls = __require("tls");
77455
- var { randomBytes: randomBytes2, createHash: createHash19 } = __require("crypto");
77744
+ var { randomBytes: randomBytes2, createHash: createHash20 } = __require("crypto");
77456
77745
  var { Duplex, Readable: Readable2 } = __require("stream");
77457
77746
  var { URL: URL2 } = __require("url");
77458
77747
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -78120,7 +78409,7 @@ var require_websocket = __commonJS({
78120
78409
  abortHandshake(websocket, socket, "Invalid Upgrade header");
78121
78410
  return;
78122
78411
  }
78123
- const digest2 = createHash19("sha1").update(key + GUID).digest("base64");
78412
+ const digest2 = createHash20("sha1").update(key + GUID).digest("base64");
78124
78413
  if (res.headers["sec-websocket-accept"] !== digest2) {
78125
78414
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
78126
78415
  return;
@@ -78492,7 +78781,7 @@ var require_websocket_server = __commonJS({
78492
78781
  var EventEmitter2 = __require("events");
78493
78782
  var http3 = __require("http");
78494
78783
  var { Duplex } = __require("stream");
78495
- var { createHash: createHash19 } = __require("crypto");
78784
+ var { createHash: createHash20 } = __require("crypto");
78496
78785
  var extension3 = require_extension();
78497
78786
  var PerMessageDeflate2 = require_permessage_deflate();
78498
78787
  var subprotocol2 = require_subprotocol();
@@ -78799,7 +79088,7 @@ var require_websocket_server = __commonJS({
78799
79088
  );
78800
79089
  }
78801
79090
  if (this._state > RUNNING) return abortHandshake(socket, 503);
78802
- const digest2 = createHash19("sha1").update(key + GUID).digest("base64");
79091
+ const digest2 = createHash20("sha1").update(key + GUID).digest("base64");
78803
79092
  const headers = [
78804
79093
  "HTTP/1.1 101 Switching Protocols",
78805
79094
  "Upgrade: websocket",
@@ -142858,9 +143147,9 @@ var init_union_priority_sort = __esm({
142858
143147
  // node_modules/typebox/build/value/clean/from_union.mjs
142859
143148
  function FromUnion9(context, type, value) {
142860
143149
  for (const schema of UnionPrioritySort(type.anyOf)) {
142861
- const clean = FromType19(context, schema, Clone2(value));
142862
- if (Check2(context, schema, clean))
142863
- return clean;
143150
+ const clean2 = FromType19(context, schema, Clone2(value));
143151
+ if (Check2(context, schema, clean2))
143152
+ return clean2;
142864
143153
  }
142865
143154
  return value;
142866
143155
  }
@@ -147498,9 +147787,9 @@ var init_long_term = __esm({
147498
147787
  });
147499
147788
 
147500
147789
  // src/codegraph/ids.ts
147501
- import { createHash as createHash7 } from "crypto";
147790
+ import { createHash as createHash8 } from "crypto";
147502
147791
  function digest(input) {
147503
- return createHash7("sha256").update(input).digest("hex").slice(0, 16);
147792
+ return createHash8("sha256").update(input).digest("hex").slice(0, 16);
147504
147793
  }
147505
147794
  function normalizeCodePath(path38) {
147506
147795
  return path38.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
@@ -148548,7 +148837,7 @@ var init_store = __esm({
148548
148837
 
148549
148838
  // src/codegraph/code-state.ts
148550
148839
  import { execFile as execFile2 } from "child_process";
148551
- import { createHash as createHash8 } from "crypto";
148840
+ import { createHash as createHash9 } from "crypto";
148552
148841
  import { promisify as promisify3 } from "util";
148553
148842
  async function runGit(rootPath, args) {
148554
148843
  try {
@@ -148577,7 +148866,7 @@ function changedPathCount(status) {
148577
148866
  return count2;
148578
148867
  }
148579
148868
  function fingerprint(baseRevision, status) {
148580
- return createHash8("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
148869
+ return createHash9("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
148581
148870
  }
148582
148871
  async function collectCodeStateSnapshot(input) {
148583
148872
  const [revision, status] = await Promise.all([
@@ -148671,12 +148960,12 @@ var init_exclude3 = __esm({
148671
148960
  });
148672
148961
 
148673
148962
  // src/codegraph/semantic-provider.ts
148674
- import { createHash as createHash9 } from "crypto";
148963
+ import { createHash as createHash10 } from "crypto";
148675
148964
  import "fs";
148676
148965
  import { resolve } from "path";
148677
148966
  import ts from "typescript";
148678
148967
  function hashText(value) {
148679
- return createHash9("sha256").update(value).digest("hex");
148968
+ return createHash10("sha256").update(value).digest("hex");
148680
148969
  }
148681
148970
  function canonicalPath(value) {
148682
148971
  const normalized = resolve(value).replace(/\\/g, "/");
@@ -149021,11 +149310,11 @@ __export(lite_provider_exports, {
149021
149310
  indexProjectLite: () => indexProjectLite,
149022
149311
  refreshProjectLite: () => refreshProjectLite
149023
149312
  });
149024
- import { createHash as createHash10 } from "crypto";
149313
+ import { createHash as createHash11 } from "crypto";
149025
149314
  import { existsSync as existsSync12, readdirSync as readdirSync2, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
149026
149315
  import { join as join4, relative as relative2 } from "path";
149027
149316
  function hashText2(text3) {
149028
- return createHash10("sha256").update(text3).digest("hex");
149317
+ return createHash11("sha256").update(text3).digest("hex");
149029
149318
  }
149030
149319
  function extension2(path38) {
149031
149320
  const index = path38.lastIndexOf(".");
@@ -149658,7 +149947,7 @@ var claim_store_exports = {};
149658
149947
  __export(claim_store_exports, {
149659
149948
  ClaimStore: () => ClaimStore
149660
149949
  });
149661
- import { createHash as createHash11, randomUUID as randomUUID12 } from "crypto";
149950
+ import { createHash as createHash12, randomUUID as randomUUID12 } from "crypto";
149662
149951
  function optionalText3(value) {
149663
149952
  return typeof value === "string" && value ? value : void 0;
149664
149953
  }
@@ -149711,7 +150000,7 @@ function rowToEvent3(row) {
149711
150000
  };
149712
150001
  }
149713
150002
  function evidenceKey(input) {
149714
- return createHash11("sha256").update(JSON.stringify([
150003
+ return createHash12("sha256").update(JSON.stringify([
149715
150004
  input.evidenceKind,
149716
150005
  input.evidenceId,
149717
150006
  input.relation,
@@ -149909,7 +150198,7 @@ __export(claims_exports, {
149909
150198
  supersedeClaim: () => supersedeClaim,
149910
150199
  writeClaim: () => writeClaim
149911
150200
  });
149912
- import { createHash as createHash12, randomUUID as randomUUID13 } from "crypto";
150201
+ import { createHash as createHash13, randomUUID as randomUUID13 } from "crypto";
149913
150202
  function compactText2(value, field) {
149914
150203
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
149915
150204
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
@@ -149921,7 +150210,7 @@ function normalizedTerm(value) {
149921
150210
  return compactText2(value, "identity").toLocaleLowerCase("en-US");
149922
150211
  }
149923
150212
  function hashIdentity(parts) {
149924
- return createHash12("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
150213
+ return createHash13("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
149925
150214
  }
149926
150215
  function buildClaimIdentity(input) {
149927
150216
  const subject = normalizedTerm(input.subject);
@@ -150162,7 +150451,7 @@ function supersedeClaim(store, input) {
150162
150451
  });
150163
150452
  }
150164
150453
  function hashObservation(observation) {
150165
- return createHash12("sha256").update(JSON.stringify([
150454
+ return createHash13("sha256").update(JSON.stringify([
150166
150455
  observation.title,
150167
150456
  observation.narrative,
150168
150457
  observation.facts,
@@ -150406,11 +150695,11 @@ var init_frontmatter = __esm({
150406
150695
  });
150407
150696
 
150408
150697
  // src/knowledge/markdown.ts
150409
- import { createHash as createHash13 } from "crypto";
150698
+ import { createHash as createHash14 } from "crypto";
150410
150699
  import { promises as fs13 } from "fs";
150411
150700
  import path22 from "path";
150412
150701
  function hashContent(content) {
150413
- return createHash13("sha256").update(content).digest("hex");
150702
+ return createHash14("sha256").update(content).digest("hex");
150414
150703
  }
150415
150704
  function stringField(data, key) {
150416
150705
  const value = data[key];
@@ -150536,11 +150825,11 @@ __export(wiki_exports, {
150536
150825
  lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
150537
150826
  readKnowledgePage: () => readKnowledgePage
150538
150827
  });
150539
- import { createHash as createHash14 } from "crypto";
150828
+ import { createHash as createHash15 } from "crypto";
150540
150829
  import { promises as fs14 } from "fs";
150541
150830
  import path23 from "path";
150542
150831
  function hash2(value) {
150543
- return createHash14("sha256").update(value).digest("hex");
150832
+ return createHash15("sha256").update(value).digest("hex");
150544
150833
  }
150545
150834
  function now() {
150546
150835
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -151759,11 +152048,11 @@ __export(workflows_exports, {
151759
152048
  syncCanonicalWorkflows: () => syncCanonicalWorkflows,
151760
152049
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
151761
152050
  });
151762
- import { createHash as createHash15 } from "crypto";
152051
+ import { createHash as createHash16 } from "crypto";
151763
152052
  import { promises as fs15 } from "fs";
151764
152053
  import path24 from "path";
151765
152054
  function hash3(value) {
151766
- return createHash15("sha256").update(value).digest("hex");
152055
+ return createHash16("sha256").update(value).digest("hex");
151767
152056
  }
151768
152057
  function now2() {
151769
152058
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -156124,9 +156413,9 @@ var init_graph_context = __esm({
156124
156413
  });
156125
156414
 
156126
156415
  // src/rules/utils.ts
156127
- import { createHash as createHash16 } from "crypto";
156416
+ import { createHash as createHash17 } from "crypto";
156128
156417
  function hashContent2(content) {
156129
- return createHash16("sha256").update(content.trim()).digest("hex").substring(0, 16);
156418
+ return createHash17("sha256").update(content.trim()).digest("hex").substring(0, 16);
156130
156419
  }
156131
156420
  function generateRuleId(source, filePath) {
156132
156421
  const sanitized = filePath.replace(/[\/\\]/g, "-").replace(/^\./, "");
@@ -158569,6 +158858,7 @@ var init_tool_profile = __esm({
158569
158858
  // keep the default micro profile at its nine-tool token budget.
158570
158859
  memorix_evidence: ["lite", "team", "full"],
158571
158860
  memorix_feedback: ["lite", "team", "full"],
158861
+ memorix_continuity: ["lite", "team", "full"],
158572
158862
  // ── lite: extended solo memory surface ────────────────────────────
158573
158863
  memorix_graph_context: ["lite", "team", "full"],
158574
158864
  memorix_timeline: ["lite", "team", "full"],
@@ -159802,7 +160092,7 @@ var init_request_context = __esm({
159802
160092
  function getMcpServerInfo() {
159803
160093
  return {
159804
160094
  name: "memorix",
159805
- version: true ? "1.9.4" : "1.0.1"
160095
+ version: true ? "1.9.6" : "1.0.1"
159806
160096
  };
159807
160097
  }
159808
160098
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -159891,8 +160181,8 @@ function legacyBehavior(homeDir) {
159891
160181
  function getBehaviorConfig(options = {}) {
159892
160182
  const homeDir = options.homeDir ?? homedir22();
159893
160183
  const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
159894
- const cacheKey = `${homeDir}\0${projectRoot ?? ""}`;
159895
- const cached = cache4.get(cacheKey);
160184
+ const cacheKey3 = `${homeDir}\0${projectRoot ?? ""}`;
160185
+ const cached = cache4.get(cacheKey3);
159896
160186
  if (cached) return cached;
159897
160187
  const legacy = legacyBehavior(homeDir);
159898
160188
  let resolved = {};
@@ -159906,7 +160196,7 @@ function getBehaviorConfig(options = {}) {
159906
160196
  autoCleanup: typeof resolved.autoCleanup === "boolean" ? resolved.autoCleanup : typeof legacy.autoCleanup === "boolean" ? legacy.autoCleanup : DEFAULTS.autoCleanup,
159907
160197
  formationMode: isFormationMode(resolved.formation) ? resolved.formation : isFormationMode(legacy.formationMode) ? legacy.formationMode : DEFAULTS.formationMode
159908
160198
  };
159909
- cache4.set(cacheKey, config);
160199
+ cache4.set(cacheKey3, config);
159910
160200
  return config;
159911
160201
  }
159912
160202
  function resetBehaviorConfigCache() {
@@ -160197,7 +160487,7 @@ async function getSessionContext(projectDir2, projectId, limit2 = 3, reader2) {
160197
160487
  let graphNeighbors = [];
160198
160488
  if (!reader2 && activeEntities.length > 0) {
160199
160489
  try {
160200
- const graphMgr = new KnowledgeGraphManager(projectDir2);
160490
+ const graphMgr = new KnowledgeGraphManager(projectDir2, projectId);
160201
160491
  await graphMgr.init();
160202
160492
  const { relations } = await graphMgr.readGraph();
160203
160493
  const activeSet = new Set(activeEntities.map((n) => n.toLowerCase()));
@@ -160511,6 +160801,7 @@ function freshnessForMemory(status) {
160511
160801
  return "unknown";
160512
160802
  }
160513
160803
  function receiptOmissionKind(raw) {
160804
+ if (raw.includes("continuity")) return "continuity";
160514
160805
  if (raw.includes("continuation")) return "continuation";
160515
160806
  if (raw.includes("task")) return "task";
160516
160807
  if (raw.includes("fact")) return "current-fact";
@@ -160621,6 +160912,77 @@ function renderTaskWorksetPrompt(input) {
160621
160912
  trust: "source-backed"
160622
160913
  });
160623
160914
  appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
160915
+ if (input.continuity) {
160916
+ const continuity = input.continuity;
160917
+ appendLine(lines, "", maxTokens, omitted, "continuity-heading");
160918
+ appendLine(lines, "Task continuity", maxTokens, omitted, "continuity-heading", selected, {
160919
+ kind: "continuity",
160920
+ id: "continuity:" + continuity.taskId,
160921
+ reason: "explicit task continuity ledger",
160922
+ trust: "source-backed"
160923
+ });
160924
+ appendLine(
160925
+ lines,
160926
+ "- Status: " + continuity.status + " | " + short(continuity.task, 30),
160927
+ maxTokens,
160928
+ omitted,
160929
+ "continuity-task"
160930
+ );
160931
+ appendLine(
160932
+ lines,
160933
+ "- Outcome: " + continuity.outcome.state,
160934
+ maxTokens,
160935
+ omitted,
160936
+ "continuity-outcome",
160937
+ selected,
160938
+ {
160939
+ kind: "continuity",
160940
+ id: "continuity:" + continuity.taskId,
160941
+ reason: "deterministic task outcome projection",
160942
+ trust: "derived"
160943
+ }
160944
+ );
160945
+ for (const requirement of continuity.requirements.slice(0, 3)) {
160946
+ appendLine(lines, "- Requirement: " + short(requirement, 24), maxTokens, omitted, "continuity-requirement", selected, {
160947
+ kind: "continuity",
160948
+ id: "continuity:" + continuity.taskId,
160949
+ reason: "explicit task requirement",
160950
+ trust: "source-backed"
160951
+ });
160952
+ }
160953
+ for (const decision of continuity.decisions.slice(0, 2)) {
160954
+ appendLine(lines, "- Decision: " + short(decision, 24), maxTokens, omitted, "continuity-decision", selected, {
160955
+ kind: "continuity",
160956
+ id: "continuity:" + continuity.taskId,
160957
+ reason: "explicit task decision",
160958
+ trust: "source-backed"
160959
+ });
160960
+ }
160961
+ for (const verification of continuity.verification.slice(-3)) {
160962
+ appendLine(
160963
+ lines,
160964
+ "- Verify [" + verification.status + "]: " + short(verification.content, 22),
160965
+ maxTokens,
160966
+ omitted,
160967
+ "continuity-verification",
160968
+ selected,
160969
+ {
160970
+ kind: "continuity",
160971
+ id: "continuity:" + continuity.taskId,
160972
+ reason: "task verification state",
160973
+ trust: "source-backed"
160974
+ }
160975
+ );
160976
+ }
160977
+ for (const risk of continuity.risks.slice(0, 2)) {
160978
+ appendLine(lines, "- Risk: " + short(risk, 22), maxTokens, omitted, "continuity-risk", selected, {
160979
+ kind: "continuity",
160980
+ id: "continuity:" + continuity.taskId,
160981
+ reason: "open task risk",
160982
+ trust: "source-backed"
160983
+ });
160984
+ }
160985
+ }
160624
160986
  if (input.alwaysOn) {
160625
160987
  const hasAlwaysOn = input.alwaysOn.profile.length > 0 || Boolean(input.alwaysOn.state) || input.alwaysOn.durable.length > 0;
160626
160988
  if (hasAlwaysOn) {
@@ -161144,6 +161506,7 @@ async function buildTaskWorkset(input) {
161144
161506
  version: "1.3",
161145
161507
  task,
161146
161508
  lens: input.lens,
161509
+ ...input.continuity ? { continuity: input.continuity } : {},
161147
161510
  ...input.alwaysOn && (input.alwaysOn.profile.length > 0 || input.alwaysOn.state || input.alwaysOn.durable.length > 0) ? { alwaysOn: input.alwaysOn } : {},
161148
161511
  currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
161149
161512
  ...continuation ? { continuation } : {},
@@ -162528,7 +162891,7 @@ var compaction_checkpoint_store_exports = {};
162528
162891
  __export(compaction_checkpoint_store_exports, {
162529
162892
  CompactionCheckpointStore: () => CompactionCheckpointStore
162530
162893
  });
162531
- import { createHash as createHash17, randomUUID as randomUUID16 } from "crypto";
162894
+ import { createHash as createHash18, randomUUID as randomUUID16 } from "crypto";
162532
162895
  function optionalText7(value) {
162533
162896
  return typeof value === "string" && value ? value : void 0;
162534
162897
  }
@@ -162570,7 +162933,7 @@ function sourceKeyFor(input, summary) {
162570
162933
  input.tokensBefore ?? "",
162571
162934
  input.firstKeptEntryId ?? ""
162572
162935
  ].join("\0");
162573
- return `derived:${createHash17("sha256").update(material).digest("hex").slice(0, 32)}`;
162936
+ return `derived:${createHash18("sha256").update(material).digest("hex").slice(0, 32)}`;
162574
162937
  }
162575
162938
  function rowToCheckpoint(row) {
162576
162939
  return {
@@ -162828,6 +163191,305 @@ var init_compaction_checkpoint_store = __esm({
162828
163191
  }
162829
163192
  });
162830
163193
 
163194
+ // src/knowledge/task-continuity.ts
163195
+ var task_continuity_exports = {};
163196
+ __export(task_continuity_exports, {
163197
+ TASK_CONTINUITY_EVENT_KINDS: () => TASK_CONTINUITY_EVENT_KINDS,
163198
+ TaskContinuityStore: () => TaskContinuityStore,
163199
+ evaluateTaskContinuity: () => evaluateTaskContinuity
163200
+ });
163201
+ import { randomUUID as randomUUID17 } from "crypto";
163202
+ function clean(value, maxLength = MAX_TEXT_LENGTH2) {
163203
+ return sanitizeCredentials(value).replace(/\s+/g, " ").trim().slice(0, maxLength);
163204
+ }
163205
+ function cleanList(values, maxLength = MAX_TEXT_LENGTH2) {
163206
+ return [...new Set((values ?? []).map((value) => clean(value, maxLength)).filter(Boolean))].slice(0, MAX_LIST_ITEMS2);
163207
+ }
163208
+ function parseRefs(value) {
163209
+ if (typeof value !== "string") return [];
163210
+ try {
163211
+ const parsed = JSON.parse(value);
163212
+ return Array.isArray(parsed) ? cleanList(parsed.filter((item) => typeof item === "string"), 500).slice(0, MAX_EVIDENCE_REFS) : [];
163213
+ } catch {
163214
+ return [];
163215
+ }
163216
+ }
163217
+ function evaluateTaskContinuity(ledger) {
163218
+ const verification = {
163219
+ passed: ledger.verification.filter((item) => item.status === "passed").length,
163220
+ failed: ledger.verification.filter((item) => item.status === "failed").length,
163221
+ pending: ledger.verification.filter((item) => item.status === "pending").length,
163222
+ skipped: ledger.verification.filter((item) => item.status === "skipped").length
163223
+ };
163224
+ const reasons = [];
163225
+ if (verification.failed > 0) reasons.push("verification-failed");
163226
+ if (verification.pending > 0) reasons.push("verification-pending");
163227
+ if (ledger.risks.length > 0) reasons.push("open-risks");
163228
+ if (ledger.status === "blocked") reasons.push("task-blocked");
163229
+ if (ledger.status === "abandoned") reasons.push("task-abandoned");
163230
+ if (ledger.status === "blocked" || ledger.status === "abandoned" || verification.failed > 0) {
163231
+ return {
163232
+ state: "at-risk",
163233
+ score: verification.passed > verification.failed ? 0.55 : 0.2,
163234
+ verification,
163235
+ reasons
163236
+ };
163237
+ }
163238
+ if (ledger.status === "completed") {
163239
+ if (verification.passed > 0 && verification.pending === 0 && verification.skipped === 0) {
163240
+ return {
163241
+ state: ledger.risks.length > 0 ? "validated-with-risks" : "validated",
163242
+ score: ledger.risks.length > 0 ? 0.8 : 1,
163243
+ verification,
163244
+ reasons: reasons.length > 0 ? reasons : ["verification-passed"]
163245
+ };
163246
+ }
163247
+ return {
163248
+ state: "unverified",
163249
+ score: 0.4,
163250
+ verification,
163251
+ reasons: reasons.length > 0 ? reasons : ["no-passing-verification"]
163252
+ };
163253
+ }
163254
+ return {
163255
+ state: "in-progress",
163256
+ score: verification.passed > 0 && verification.pending === 0 ? 0.75 : 0.5,
163257
+ verification,
163258
+ reasons: reasons.length > 0 ? reasons : ["task-open"]
163259
+ };
163260
+ }
163261
+ function rowToEvent4(row) {
163262
+ const status = row.item_status ? String(row.item_status) : void 0;
163263
+ return {
163264
+ id: String(row.id),
163265
+ projectId: String(row.project_id),
163266
+ taskId: String(row.task_id),
163267
+ task: clean(String(row.task), MAX_TASK_LENGTH),
163268
+ kind: row.kind,
163269
+ content: clean(String(row.content)),
163270
+ ...status ? { status } : {},
163271
+ ...row.source_ref ? { sourceRef: clean(String(row.source_ref), 500) } : {},
163272
+ evidenceRefs: parseRefs(row.evidence_json),
163273
+ ...row.actor ? { actor: clean(String(row.actor), 200) } : {},
163274
+ at: String(row.created_at),
163275
+ ...row.event_key ? { eventKey: clean(String(row.event_key), 200) } : {},
163276
+ ...row.item_key ? { itemKey: clean(String(row.item_key), 120) } : {},
163277
+ ...row.supersedes_id ? { supersedesId: clean(String(row.supersedes_id), 120) } : {}
163278
+ };
163279
+ }
163280
+ function aggregate(events) {
163281
+ const first2 = events.find((event) => event.kind === "task") ?? events[0];
163282
+ const latestOutcome = [...events].reverse().find((event) => event.kind === "outcome");
163283
+ const verificationByKey = /* @__PURE__ */ new Map();
163284
+ for (const event of events.filter((event2) => event2.kind === "verification")) {
163285
+ verificationByKey.set(event.itemKey ?? `event:${event.id}`, event);
163286
+ }
163287
+ const ledger = {
163288
+ taskId: first2.taskId,
163289
+ projectId: first2.projectId,
163290
+ task: first2.task,
163291
+ status: latestOutcome?.status ?? "open",
163292
+ requirements: events.filter((event) => event.kind === "requirement").map((event) => event.content),
163293
+ decisions: events.filter((event) => event.kind === "decision").map((event) => event.content),
163294
+ verification: [...verificationByKey.values()].map((event) => ({
163295
+ id: event.itemKey ?? event.id,
163296
+ content: event.content,
163297
+ status: event.status ?? "pending",
163298
+ ...event.sourceRef ? { sourceRef: event.sourceRef } : {},
163299
+ evidenceRefs: event.evidenceRefs
163300
+ })),
163301
+ risks: events.filter((event) => event.kind === "risk").map((event) => event.content),
163302
+ outcomes: events.filter((event) => event.kind === "outcome").map((event) => event.content),
163303
+ updatedAt: events[events.length - 1].at,
163304
+ events
163305
+ };
163306
+ return { ...ledger, outcome: evaluateTaskContinuity(ledger) };
163307
+ }
163308
+ var TASK_CONTINUITY_EVENT_KINDS, MAX_TEXT_LENGTH2, MAX_TASK_LENGTH, MAX_LIST_ITEMS2, MAX_EVIDENCE_REFS, MAX_EVENTS_PER_TASK, TaskContinuityStore;
163309
+ var init_task_continuity = __esm({
163310
+ "src/knowledge/task-continuity.ts"() {
163311
+ "use strict";
163312
+ init_esm_shims();
163313
+ init_secret_filter();
163314
+ init_sqlite_db();
163315
+ TASK_CONTINUITY_EVENT_KINDS = [
163316
+ "task",
163317
+ "requirement",
163318
+ "decision",
163319
+ "verification",
163320
+ "risk",
163321
+ "outcome"
163322
+ ];
163323
+ MAX_TEXT_LENGTH2 = 2e3;
163324
+ MAX_TASK_LENGTH = 1e3;
163325
+ MAX_LIST_ITEMS2 = 8;
163326
+ MAX_EVIDENCE_REFS = 8;
163327
+ MAX_EVENTS_PER_TASK = 200;
163328
+ TaskContinuityStore = class {
163329
+ db = null;
163330
+ async init(dataDir) {
163331
+ this.db = getDatabase(dataDir);
163332
+ }
163333
+ requireDb() {
163334
+ if (!this.db) throw new Error("TaskContinuityStore is not initialized.");
163335
+ return this.db;
163336
+ }
163337
+ insert(input) {
163338
+ const db2 = this.requireDb();
163339
+ const projectId = clean(input.projectId, 500);
163340
+ const taskId = clean(input.taskId, 120);
163341
+ const eventKey = input.eventKey ? clean(input.eventKey, 200) : void 0;
163342
+ if (eventKey) {
163343
+ const existing = db2.prepare(
163344
+ "SELECT * FROM task_continuity_events WHERE project_id = ? AND task_id = ? AND event_key = ?"
163345
+ ).get(projectId, taskId, eventKey);
163346
+ if (existing) return rowToEvent4(existing);
163347
+ }
163348
+ const existingCount = db2.prepare(
163349
+ "SELECT COUNT(*) AS count FROM task_continuity_events WHERE project_id = ? AND task_id = ?"
163350
+ ).get(projectId, taskId);
163351
+ if (Number(existingCount?.count ?? 0) >= MAX_EVENTS_PER_TASK) {
163352
+ throw new Error("Task continuity ledger reached its 200-event limit.");
163353
+ }
163354
+ const event = {
163355
+ id: randomUUID17(),
163356
+ projectId,
163357
+ taskId,
163358
+ task: clean(input.task, MAX_TASK_LENGTH),
163359
+ kind: input.kind,
163360
+ content: clean(input.content),
163361
+ ...input.status ? { status: input.status } : {},
163362
+ ...input.sourceRef ? { sourceRef: clean(input.sourceRef, 500) } : {},
163363
+ evidenceRefs: cleanList(input.evidenceRefs, 500).slice(0, MAX_EVIDENCE_REFS),
163364
+ ...input.actor ? { actor: clean(input.actor, 200) } : {},
163365
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
163366
+ ...eventKey ? { eventKey } : {},
163367
+ ...input.itemKey ? { itemKey: clean(input.itemKey, 120) } : {},
163368
+ ...input.supersedesId ? { supersedesId: clean(input.supersedesId, 120) } : {}
163369
+ };
163370
+ db2.prepare(`
163371
+ INSERT INTO task_continuity_events (
163372
+ id, project_id, task_id, task, kind, content, item_status,
163373
+ source_ref, evidence_json, actor, created_at, event_key, item_key, supersedes_id
163374
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
163375
+ `).run(
163376
+ event.id,
163377
+ event.projectId,
163378
+ event.taskId,
163379
+ event.task,
163380
+ event.kind,
163381
+ event.content,
163382
+ event.status ?? null,
163383
+ event.sourceRef ?? null,
163384
+ JSON.stringify(event.evidenceRefs),
163385
+ event.actor ?? null,
163386
+ event.at,
163387
+ event.eventKey ?? null,
163388
+ event.itemKey ?? null,
163389
+ event.supersedesId ?? null
163390
+ );
163391
+ return event;
163392
+ }
163393
+ start(input) {
163394
+ const task = clean(input.task, MAX_TASK_LENGTH);
163395
+ if (!task) throw new Error("A non-empty task is required.");
163396
+ const idempotencyKey = input.idempotencyKey ? clean(input.idempotencyKey, 200) : void 0;
163397
+ const taskId = clean(input.taskId ?? idempotencyKey ?? "", 120) || randomUUID17();
163398
+ const requirements = cleanList(input.requirements);
163399
+ const transaction = this.requireDb().transaction(() => {
163400
+ this.insert({
163401
+ projectId: input.projectId,
163402
+ taskId,
163403
+ task,
163404
+ kind: "task",
163405
+ content: task,
163406
+ status: "open",
163407
+ actor: input.actor,
163408
+ ...idempotencyKey ? { eventKey: `start:${idempotencyKey}` } : {}
163409
+ });
163410
+ for (const requirement of requirements) {
163411
+ this.insert({
163412
+ projectId: input.projectId,
163413
+ taskId,
163414
+ task,
163415
+ kind: "requirement",
163416
+ content: requirement,
163417
+ actor: input.actor
163418
+ });
163419
+ }
163420
+ });
163421
+ transaction.immediate();
163422
+ return { taskId, ledger: this.get(input.projectId, taskId) };
163423
+ }
163424
+ record(input) {
163425
+ const projectId = clean(input.projectId, 500);
163426
+ const taskId = clean(input.taskId, 120);
163427
+ const existing = this.get(projectId, taskId);
163428
+ if (!existing) throw new Error("Task continuity ledger not found: " + taskId);
163429
+ const transaction = this.requireDb().transaction(() => this.insert({
163430
+ projectId,
163431
+ taskId,
163432
+ task: existing.task,
163433
+ kind: input.kind,
163434
+ content: input.content,
163435
+ ...input.kind === "verification" && input.verificationStatus ? { status: input.verificationStatus } : {},
163436
+ sourceRef: input.sourceRef,
163437
+ evidenceRefs: input.evidenceRefs,
163438
+ actor: input.actor,
163439
+ at: input.at,
163440
+ ...input.idempotencyKey ? { eventKey: input.idempotencyKey } : {},
163441
+ ...input.verificationId ? { itemKey: input.verificationId } : {}
163442
+ }));
163443
+ const event = transaction.immediate();
163444
+ return { event, ledger: this.get(projectId, taskId) };
163445
+ }
163446
+ close(input) {
163447
+ const projectId = clean(input.projectId, 500);
163448
+ const taskId = clean(input.taskId, 120);
163449
+ const existing = this.get(projectId, taskId);
163450
+ if (!existing) throw new Error("Task continuity ledger not found: " + taskId);
163451
+ const transaction = this.requireDb().transaction(() => this.insert({
163452
+ projectId,
163453
+ taskId,
163454
+ task: existing.task,
163455
+ kind: "outcome",
163456
+ content: input.content,
163457
+ status: input.status,
163458
+ sourceRef: input.sourceRef,
163459
+ evidenceRefs: input.evidenceRefs,
163460
+ actor: input.actor,
163461
+ at: input.at,
163462
+ ...input.idempotencyKey ? { eventKey: input.idempotencyKey } : {}
163463
+ }));
163464
+ const event = transaction.immediate();
163465
+ return { event, ledger: this.get(projectId, taskId) };
163466
+ }
163467
+ get(projectId, taskId) {
163468
+ const normalizedProjectId = clean(projectId, 500);
163469
+ const normalizedTaskId = clean(taskId, 120);
163470
+ const rows = this.requireDb().prepare(`
163471
+ SELECT * FROM task_continuity_events
163472
+ WHERE project_id = ? AND task_id = ?
163473
+ ORDER BY created_at ASC, rowid ASC
163474
+ `).all(normalizedProjectId, normalizedTaskId);
163475
+ if (rows.length === 0) return void 0;
163476
+ return aggregate(rows.map(rowToEvent4));
163477
+ }
163478
+ list(projectId, limit2 = 20) {
163479
+ const rows = this.requireDb().prepare(`
163480
+ SELECT task_id
163481
+ FROM task_continuity_events
163482
+ WHERE project_id = ?
163483
+ GROUP BY task_id
163484
+ ORDER BY MAX(created_at) DESC
163485
+ LIMIT ?
163486
+ `).all(projectId, Math.min(100, Math.max(1, Math.floor(limit2))));
163487
+ return rows.map((row) => this.get(projectId, String(row.task_id))).filter((ledger) => Boolean(ledger));
163488
+ }
163489
+ };
163490
+ }
163491
+ });
163492
+
162831
163493
  // src/codegraph/auto-context.ts
162832
163494
  var auto_context_exports = {};
162833
163495
  __export(auto_context_exports, {
@@ -163084,6 +163746,16 @@ async function buildAutoProjectContext(input) {
163084
163746
  };
163085
163747
  }
163086
163748
  }
163749
+ let continuity = input.continuity;
163750
+ if (!continuity && input.taskId) {
163751
+ try {
163752
+ const { TaskContinuityStore: TaskContinuityStore2 } = await Promise.resolve().then(() => (init_task_continuity(), task_continuity_exports));
163753
+ const continuityStore = new TaskContinuityStore2();
163754
+ await continuityStore.init(input.dataDir);
163755
+ continuity = continuityStore.get(input.project.id, input.taskId);
163756
+ } catch {
163757
+ }
163758
+ }
163087
163759
  const alwaysOn = await buildAlwaysOnBlock({
163088
163760
  dataDir: input.dataDir,
163089
163761
  projectId: input.project.id,
@@ -163102,6 +163774,7 @@ async function buildAutoProjectContext(input) {
163102
163774
  providerQuality: providerQuality2,
163103
163775
  currentFacts: worksetFactLines(currentFacts),
163104
163776
  ...continuation ? { continuation } : {},
163777
+ ...continuity ? { continuity } : {},
163105
163778
  codeState: codeStateLine(overview),
163106
163779
  ...codeEvolution ? { codeEvolution } : {},
163107
163780
  reliableMemory: sourceSets.reliableSources.slice(0, lens.sourceLimit).map((source) => ({
@@ -163149,6 +163822,7 @@ async function buildAutoProjectContext(input) {
163149
163822
  refresh,
163150
163823
  providerQuality: providerQuality2,
163151
163824
  ...continuationRequested && workset.continuation ? { continuation: workset.continuation } : {},
163825
+ ...workset.continuity ? { continuity: workset.continuity } : {},
163152
163826
  workset
163153
163827
  };
163154
163828
  }
@@ -163450,7 +164124,14 @@ function buildBoundedContextReceipt(input) {
163450
164124
  externalState: provider2?.external.state ?? "not-detected",
163451
164125
  ...input.workset.provenance.snapshotId ? { snapshotId: input.workset.provenance.snapshotId } : {}
163452
164126
  },
163453
- ...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {}
164127
+ ...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {},
164128
+ ...input.workset.continuity ? {
164129
+ continuity: {
164130
+ taskId: input.workset.continuity.taskId,
164131
+ status: input.workset.continuity.status,
164132
+ outcome: input.workset.continuity.outcome
164133
+ }
164134
+ } : {}
163454
164135
  };
163455
164136
  }
163456
164137
  var init_context_receipt = __esm({
@@ -164082,15 +164763,21 @@ var team_store_exports = {};
164082
164763
  __export(team_store_exports, {
164083
164764
  AGENT_TYPE_ROLE_MAP: () => AGENT_TYPE_ROLE_MAP,
164084
164765
  DEFAULT_ROLES: () => DEFAULT_ROLES,
164766
+ DEFAULT_TEAM_QUERY_LIMIT: () => DEFAULT_TEAM_QUERY_LIMIT,
164767
+ MAX_TEAM_QUERY_LIMIT: () => MAX_TEAM_QUERY_LIMIT,
164085
164768
  TeamStore: () => TeamStore,
164086
164769
  getTeamStore: () => getTeamStore,
164087
164770
  initTeamStore: () => initTeamStore,
164088
164771
  isTeamStoreInitialized: () => isTeamStoreInitialized,
164089
164772
  resetTeamStore: () => resetTeamStore
164090
164773
  });
164091
- import { randomUUID as randomUUID17 } from "crypto";
164774
+ import { randomUUID as randomUUID18 } from "crypto";
164092
164775
  import path31 from "path";
164093
164776
  import fs18 from "fs";
164777
+ function clampTeamQueryLimit(limit2) {
164778
+ if (!Number.isFinite(limit2) || limit2 == null) return DEFAULT_TEAM_QUERY_LIMIT;
164779
+ return Math.min(MAX_TEAM_QUERY_LIMIT, Math.max(1, Math.floor(limit2)));
164780
+ }
164094
164781
  function getTeamStore() {
164095
164782
  if (!_teamStore) {
164096
164783
  throw new Error("[memorix] TeamStore not initialized \u2014 call initTeamStore() first");
@@ -164114,12 +164801,14 @@ function resetTeamStore() {
164114
164801
  _teamStore = null;
164115
164802
  _teamStoreDataDir = null;
164116
164803
  }
164117
- var DEFAULT_ROLES, AGENT_TYPE_ROLE_MAP, TeamStore, _teamStore, _teamStoreDataDir;
164804
+ var DEFAULT_TEAM_QUERY_LIMIT, MAX_TEAM_QUERY_LIMIT, DEFAULT_ROLES, AGENT_TYPE_ROLE_MAP, TeamStore, _teamStore, _teamStoreDataDir;
164118
164805
  var init_team_store = __esm({
164119
164806
  "src/team/team-store.ts"() {
164120
164807
  "use strict";
164121
164808
  init_esm_shims();
164122
164809
  init_sqlite_db();
164810
+ DEFAULT_TEAM_QUERY_LIMIT = 100;
164811
+ MAX_TEAM_QUERY_LIMIT = 500;
164123
164812
  DEFAULT_ROLES = [
164124
164813
  { roleId: "planner", label: "Planner", description: "Breaks down requirements, creates tasks, defines dependencies", preferredAgentTypes: ["claude-code", "cursor", "codex"], maxConcurrent: 2 },
164125
164814
  { roleId: "researcher", label: "Researcher", description: "Investigates codebase, gathers context, answers questions", preferredAgentTypes: ["claude-code", "cursor", "gemini-cli"], maxConcurrent: 2 },
@@ -164377,7 +165066,7 @@ var init_team_store = __esm({
164377
165066
  * reactivate it (preserving agent_id). Otherwise create a new one.
164378
165067
  */
164379
165068
  registerAgent(input) {
164380
- const instanceId = input.instanceId || randomUUID17();
165069
+ const instanceId = input.instanceId || randomUUID18();
164381
165070
  const now3 = Date.now();
164382
165071
  const existing = this.stmtAgentFindByInstance.get(
164383
165072
  input.projectId,
@@ -164403,7 +165092,7 @@ var init_team_store = __esm({
164403
165092
  this.eventBus?.emit("agent:joined", { agentId: row2.agent_id, projectId: input.projectId, agentType: input.agentType });
164404
165093
  return row2;
164405
165094
  }
164406
- const agentId = randomUUID17();
165095
+ const agentId = randomUUID18();
164407
165096
  this.stmtAgentUpsert.run({
164408
165097
  agent_id: agentId,
164409
165098
  project_id: input.projectId,
@@ -164429,32 +165118,37 @@ var init_team_store = __esm({
164429
165118
  return this.stmtAgentFindByInstance.get(projectId, agentType, instanceId);
164430
165119
  }
164431
165120
  listAgents(projectId, filter) {
164432
- const all = this.stmtAgentListByProject.all(projectId);
165121
+ const limit2 = clampTeamQueryLimit(filter?.limit);
164433
165122
  if (filter?.status) {
164434
- return all.filter((a) => a.status === filter.status);
165123
+ return this.db.prepare(
165124
+ `SELECT * FROM team_agents WHERE project_id = ? AND status = ? ORDER BY joined_at DESC LIMIT ?`
165125
+ ).all(projectId, filter.status, limit2);
164435
165126
  }
164436
- return all;
165127
+ return this.db.prepare(
165128
+ `SELECT * FROM team_agents WHERE project_id = ? ORDER BY joined_at DESC LIMIT ?`
165129
+ ).all(projectId, limit2);
164437
165130
  }
164438
165131
  /** List agents across all projects (for global scope) */
164439
- listAllAgents() {
165132
+ listAllAgents(limit2) {
164440
165133
  if (!this.db) return [];
164441
- return this.db.prepare("SELECT * FROM team_agents ORDER BY last_heartbeat DESC").all();
165134
+ return this.db.prepare("SELECT * FROM team_agents ORDER BY last_heartbeat DESC LIMIT ?").all(clampTeamQueryLimit(limit2));
164442
165135
  }
164443
165136
  /** List locks across all projects (for global scope) */
164444
- listAllLocks() {
165137
+ listAllLocks(limit2) {
164445
165138
  if (!this.db) return [];
164446
165139
  this.db.prepare("DELETE FROM team_locks WHERE expires_at <= ?").run(Date.now());
164447
- return this.db.prepare("SELECT * FROM team_locks WHERE expires_at > ? ORDER BY locked_at DESC").all(Date.now());
165140
+ return this.db.prepare("SELECT * FROM team_locks WHERE expires_at > ? ORDER BY locked_at DESC LIMIT ?").all(Date.now(), clampTeamQueryLimit(limit2));
164448
165141
  }
164449
165142
  /** List tasks across all projects (for global scope) */
164450
165143
  listAllTasks(filter) {
164451
165144
  if (!this.db) return [];
165145
+ const limit2 = clampTeamQueryLimit(filter?.limit);
164452
165146
  if (filter?.available) {
164453
165147
  return this.db.prepare(
164454
- `SELECT t.* FROM team_tasks t WHERE t.status = 'pending' AND t.assignee_agent_id IS NULL ORDER BY t.created_at DESC`
164455
- ).all();
165148
+ `SELECT t.* FROM team_tasks t WHERE t.status = 'pending' AND t.assignee_agent_id IS NULL ORDER BY t.created_at DESC LIMIT ?`
165149
+ ).all(limit2);
164456
165150
  }
164457
- return this.db.prepare("SELECT * FROM team_tasks ORDER BY created_at DESC").all();
165151
+ return this.db.prepare("SELECT * FROM team_tasks ORDER BY created_at DESC LIMIT ?").all(limit2);
164458
165152
  }
164459
165153
  heartbeat(agentId) {
164460
165154
  const info = this.stmtAgentUpdateHeartbeat.run(Date.now(), agentId);
@@ -164489,7 +165183,10 @@ var init_team_store = __esm({
164489
165183
  return staleIds;
164490
165184
  }
164491
165185
  getActiveCount(projectId) {
164492
- return this.listAgents(projectId, { status: "active" }).length;
165186
+ const row = this.db.prepare(
165187
+ `SELECT COUNT(*) AS count FROM team_agents WHERE project_id = ? AND status = 'active'`
165188
+ ).get(projectId);
165189
+ return Number(row?.count ?? 0);
164493
165190
  }
164494
165191
  // ═══════════════════════════════════════════════════════════════════
164495
165192
  // Message Operations
@@ -164505,7 +165202,7 @@ var init_team_store = __esm({
164505
165202
  return { error: `Recipient agent '${input.recipientAgentId}' not found \u2014 cannot send to unknown agent` };
164506
165203
  }
164507
165204
  }
164508
- const id = randomUUID17();
165205
+ const id = randomUUID18();
164509
165206
  const now3 = Date.now();
164510
165207
  const row = {
164511
165208
  id,
@@ -164524,8 +165221,14 @@ var init_team_store = __esm({
164524
165221
  this.stmtMsgInsert.run(row);
164525
165222
  return row;
164526
165223
  }
164527
- getInbox(projectId, agentId) {
164528
- return this.stmtMsgInbox.all(projectId, agentId);
165224
+ getInbox(projectId, agentId, limit2) {
165225
+ return this.db.prepare(`
165226
+ SELECT * FROM team_messages
165227
+ WHERE project_id = ?
165228
+ AND (recipient_agent_id = ? OR recipient_agent_id IS NULL)
165229
+ ORDER BY created_at ASC
165230
+ LIMIT ?
165231
+ `).all(projectId, agentId, clampTeamQueryLimit(limit2));
164529
165232
  }
164530
165233
  getUnreadCount(projectId, agentId) {
164531
165234
  const row = this.stmtMsgUnreadCount.get(projectId, agentId);
@@ -164555,7 +165258,7 @@ var init_team_store = __esm({
164555
165258
  // Task Operations (with atomic claim semantics)
164556
165259
  // ═══════════════════════════════════════════════════════════════════
164557
165260
  createTask(input) {
164558
- const taskId = randomUUID17();
165261
+ const taskId = randomUUID18();
164559
165262
  const now3 = Date.now();
164560
165263
  const row = {
164561
165264
  task_id: taskId,
@@ -164682,17 +165385,22 @@ var init_team_store = __esm({
164682
165385
  return info.changes;
164683
165386
  }
164684
165387
  listTasks(projectId, filter) {
164685
- if (filter?.available) {
164686
- return this.stmtTaskAvailable.all(projectId);
164687
- }
164688
- const all = this.stmtTaskListByProject.all(projectId);
165388
+ const clauses = ["project_id = ?"];
165389
+ const values = [projectId];
165390
+ if (filter?.available) clauses.push("status = 'pending'", "assignee_agent_id IS NULL");
164689
165391
  if (filter?.status) {
164690
- return all.filter((t2) => t2.status === filter.status);
165392
+ clauses.push("status = ?");
165393
+ values.push(filter.status);
164691
165394
  }
164692
165395
  if (filter?.assignee) {
164693
- return all.filter((t2) => t2.assignee_agent_id === filter.assignee);
165396
+ clauses.push("assignee_agent_id = ?");
165397
+ values.push(filter.assignee);
164694
165398
  }
164695
- return all;
165399
+ values.push(clampTeamQueryLimit(filter?.limit));
165400
+ const orderBy = filter?.available ? "created_at ASC, rowid ASC" : "created_at DESC, rowid DESC";
165401
+ return this.db.prepare(
165402
+ `SELECT * FROM team_tasks WHERE ${clauses.join(" AND ")} ORDER BY ${orderBy} LIMIT ?`
165403
+ ).all(...values);
164696
165404
  }
164697
165405
  /**
164698
165406
  * List available tasks for a specific agent, sorted by role affinity.
@@ -164704,7 +165412,7 @@ var init_team_store = __esm({
164704
165412
  const agent = this.stmtAgentFindById.get(agentId);
164705
165413
  if (!agent) return [];
164706
165414
  const agentRole = agent.role;
164707
- const available = this.stmtTaskAvailable.all(projectId);
165415
+ const available = this.listTasks(projectId, { available: true, limit: MAX_TEAM_QUERY_LIMIT });
164708
165416
  const eligible = available.filter((t2) => !t2.required_role || t2.required_role === agentRole);
164709
165417
  const score = (t2) => {
164710
165418
  if (t2.preferred_role === agentRole) return 0;
@@ -164758,12 +165466,16 @@ var init_team_store = __esm({
164758
165466
  const row = this.stmtLockGet.get(file, projectId);
164759
165467
  return row ?? null;
164760
165468
  }
164761
- listLocks(projectId, agentId) {
165469
+ listLocks(projectId, agentId, limit2) {
164762
165470
  this.stmtLockDeleteExpired.run(projectId, Date.now());
164763
165471
  if (agentId) {
164764
- return this.stmtLockListByAgent.all(projectId, agentId);
165472
+ return this.db.prepare(
165473
+ `SELECT * FROM team_locks WHERE project_id = ? AND locked_by = ? ORDER BY locked_at DESC LIMIT ?`
165474
+ ).all(projectId, agentId, clampTeamQueryLimit(limit2));
164765
165475
  }
164766
- return this.stmtLockListByProject.all(projectId);
165476
+ return this.db.prepare(
165477
+ `SELECT * FROM team_locks WHERE project_id = ? ORDER BY locked_at DESC LIMIT ?`
165478
+ ).all(projectId, clampTeamQueryLimit(limit2));
164767
165479
  }
164768
165480
  releaseAllLocks(agentId) {
164769
165481
  const info = this.stmtLockDeleteByAgent.run(agentId);
@@ -164827,7 +165539,7 @@ var init_team_store = __esm({
164827
165539
  for (const [agentId, msgs] of Object.entries(snap.messages.inboxes)) {
164828
165540
  for (const msg of msgs) {
164829
165541
  this.stmtMsgInsert.run({
164830
- id: msg.id ?? randomUUID17(),
165542
+ id: msg.id ?? randomUUID18(),
164831
165543
  project_id: "migrated",
164832
165544
  sender_agent_id: msg.from ?? "unknown",
164833
165545
  recipient_agent_id: msg.to === "__broadcast__" ? null : msg.to ?? agentId,
@@ -166539,8 +167251,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
166539
167251
  break;
166540
167252
  }
166541
167253
  case "/graph": {
166542
- await initGraphStore(effectiveDataDir);
166543
- const gStore = getGraphStore();
167254
+ const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
166544
167255
  const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
166545
167256
  const graphObs = filterDashboardObservations(
166546
167257
  await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
@@ -166584,8 +167295,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
166584
167295
  break;
166585
167296
  }
166586
167297
  case "/stats": {
166587
- await initGraphStore(effectiveDataDir);
166588
- const graph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
167298
+ const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
167299
+ const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
166589
167300
  const observations2 = filterDashboardObservations(
166590
167301
  await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
166591
167302
  effectiveProjectId
@@ -166778,15 +167489,15 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
166778
167489
  case "/knowledge-graph": {
166779
167490
  const { generateKnowledgeGraph: generateKnowledgeGraph2 } = await Promise.resolve().then(() => (init_knowledge_graph(), knowledge_graph_exports));
166780
167491
  const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
166781
- const { initGraphStore: initGraphStore2, getGraphStore: getGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
167492
+ const { initGraphStore: initGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
166782
167493
  await initMiniSkillStore2(effectiveDataDir);
166783
- await initGraphStore2(effectiveDataDir);
167494
+ const gStore = await initGraphStore2(effectiveDataDir, effectiveProjectId);
166784
167495
  const allObs = filterDashboardObservations(
166785
167496
  await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
166786
167497
  effectiveProjectId
166787
167498
  );
166788
167499
  const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
166789
- const fullGraph = { entities: getGraphStore2().loadEntities(), relations: getGraphStore2().loadRelations() };
167500
+ const fullGraph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
166790
167501
  const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
166791
167502
  const graph = generateKnowledgeGraph2({
166792
167503
  projectId: effectiveProjectId,
@@ -166983,8 +167694,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
166983
167694
  } else {
166984
167695
  await obsStore.remove(obsId);
166985
167696
  try {
166986
- await initGraphStore(effectiveDataDir);
166987
- const gStore = getGraphStore();
167697
+ const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
166988
167698
  const prefix = `[#${obsId}] `;
166989
167699
  const deletions = [];
166990
167700
  for (const entity of gStore.loadEntities()) {
@@ -166999,8 +167709,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
166999
167709
  break;
167000
167710
  }
167001
167711
  if (apiPath === "/export") {
167002
- await initGraphStore(effectiveDataDir);
167003
- const fullGraph = { entities: getGraphStore().loadEntities(), relations: getGraphStore().loadRelations() };
167712
+ const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
167713
+ const fullGraph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
167004
167714
  const observations2 = filterDashboardObservations(
167005
167715
  await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
167006
167716
  effectiveProjectId
@@ -167787,7 +168497,7 @@ __export(planner_exports, {
167787
168497
  materializeTaskGraph: () => materializeTaskGraph,
167788
168498
  seedAutonomousPipeline: () => seedAutonomousPipeline
167789
168499
  });
167790
- import { randomUUID as randomUUID18 } from "crypto";
168500
+ import { randomUUID as randomUUID19 } from "crypto";
167791
168501
  function isPlannerTask(metadata) {
167792
168502
  if (!metadata) return null;
167793
168503
  try {
@@ -167845,7 +168555,7 @@ function seedAutonomousPipeline(teamStore, projectId, config, opts) {
167845
168555
  const maxIterations = config.maxIterations ?? 3;
167846
168556
  const taskBudget = config.taskBudget ?? 15;
167847
168557
  const structuredPlan = opts?.structuredPlan ?? true;
167848
- const pipelineId = randomUUID18();
168558
+ const pipelineId = randomUUID19();
167849
168559
  const meta = {
167850
168560
  plannerType: "plan",
167851
168561
  pipelineId,
@@ -168526,6 +169236,7 @@ var init_official_skills = __esm({
168526
169236
  "| Need the sequence around one memory | `memorix_timeline` | `memorix memory timeline --id <id>` |",
168527
169237
  "| Check source and freshness for a memory | `memorix_evidence` | `memorix evidence list|get|events` |",
168528
169238
  "| Record whether a memory helped, conflicted, or was corrected | `memorix_feedback` | `memorix feedback record|show|audit` |",
169239
+ "| Track task requirements, decisions, verification, and outcome | `memorix_continuity` | `memorix continuity start|record|show|close` |",
168529
169240
  "| Inspect or import controlled media | `memorix_media` | `memorix media import|attach|list|show|status` |",
168530
169241
  '| Learned reusable project knowledge | `memorix_store` | `memorix memory store --type <type> --entity <name> --title "<title>" "<text>"` |',
168531
169242
  "| Task or bug is complete/outdated | `memorix_resolve` | `memorix memory resolve --ids <ids>` |",
@@ -168549,6 +169260,7 @@ var init_official_skills = __esm({
168549
169260
  "3. Use stale or unbound memory only as a warning or search lead.",
168550
169261
  "4. Store durable decisions, fixes, and gotchas after the project state changes.",
168551
169262
  "5. Resolve completed or obsolete memories so future briefs stay small.",
169263
+ "6. For multi-step work, start a continuity ledger and pass its taskId back to memorix_project_context.",
168552
169264
  "",
168553
169265
  "## Store Rules",
168554
169266
  "",
@@ -170270,7 +170982,7 @@ __export(server_exports2, {
170270
170982
  createMemorixServer: () => createMemorixServer,
170271
170983
  shouldAwaitProjectRuntime: () => shouldAwaitProjectRuntime
170272
170984
  });
170273
- import { createHash as createHash18 } from "crypto";
170985
+ import { createHash as createHash19 } from "crypto";
170274
170986
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
170275
170987
  import { z as z627 } from "zod";
170276
170988
  function formatFormationStageDurations(stageDurationsMs) {
@@ -170354,7 +171066,7 @@ function coerceObjectArray(val) {
170354
171066
  return [];
170355
171067
  }
170356
171068
  function createDeterministicInstanceId(projectId, agentType, agentName) {
170357
- const digest2 = createHash18("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
171069
+ const digest2 = createHash19("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
170358
171070
  return `auto-${digest2}`;
170359
171071
  }
170360
171072
  function shouldAwaitProjectRuntime(toolName) {
@@ -170445,7 +171157,7 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
170445
171157
  const store = getObservationStore();
170446
171158
  console.error(`[memorix] ObservationStore backend: ${store.getBackendName()}, generation: ${store.getGeneration()}`);
170447
171159
  }
170448
- let graphManager = new KnowledgeGraphManager(projectDir2);
171160
+ let graphManager = new KnowledgeGraphManager(projectDir2, project.id);
170449
171161
  if (!deferProjectRuntimeInit) {
170450
171162
  await graphManager.init();
170451
171163
  await initObservations(projectDir2);
@@ -170457,7 +171169,7 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
170457
171169
  await initObservationStore(projectDir2);
170458
171170
  await initMiniSkillStore(projectDir2);
170459
171171
  await initSessionStore(projectDir2);
170460
- graphManager = new KnowledgeGraphManager(projectDir2);
171172
+ graphManager = new KnowledgeGraphManager(projectDir2, project.id);
170461
171173
  await graphManager.init();
170462
171174
  await initObservations(projectDir2);
170463
171175
  const indexed = await prepareSearchIndex();
@@ -170561,6 +171273,22 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
170561
171273
  }
170562
171274
  await projectRuntimeInitPromise;
170563
171275
  };
171276
+ const activateProjectRuntime = async () => {
171277
+ if (!projectResolved) return;
171278
+ await initObservationStore(projectDir2);
171279
+ await initMiniSkillStore(projectDir2);
171280
+ await initSessionStore(projectDir2);
171281
+ await initObservations(projectDir2, { skipCorpusLoad: true });
171282
+ try {
171283
+ const { initProjectRoot: initProjectRoot2 } = await Promise.resolve().then(() => (init_yaml_loader(), yaml_loader_exports));
171284
+ const { resetDotenv: resetDotenv2, loadDotenv: loadDotenv2 } = await Promise.resolve().then(() => (init_dotenv_loader(), dotenv_loader_exports));
171285
+ initProjectRoot2(project.rootPath);
171286
+ resetDotenv2();
171287
+ loadDotenv2(project.rootPath);
171288
+ } catch {
171289
+ }
171290
+ initLLM({ projectRoot: project.rootPath });
171291
+ };
170564
171292
  let maintenanceWorker = null;
170565
171293
  const startProjectMaintenanceWorker = async (autoCleanup = false) => {
170566
171294
  maintenanceWorker?.stop();
@@ -171476,6 +172204,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
171476
172204
  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.",
171477
172205
  inputSchema: {
171478
172206
  task: z627.string().optional().describe("Current coding task or question"),
172207
+ taskId: z627.string().max(120).optional().describe("Optional task continuity ledger id to include in the brief"),
171479
172208
  refresh: z627.enum(["auto", "always", "never"]).optional().default("auto").describe(
171480
172209
  "Code Memory refresh policy. auto refreshes only when missing or stale."
171481
172210
  ),
@@ -171500,7 +172229,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
171500
172229
  limit: z627.number().optional().describe("Reserved for future source limits; current prompt stays compact by default.")
171501
172230
  }
171502
172231
  },
171503
- async ({ task, refresh, format, agent }) => {
172232
+ async ({ task, taskId, refresh, format, agent }) => {
171504
172233
  const unresolved = requireResolvedProject("build project context for the current project");
171505
172234
  if (unresolved) return unresolved;
171506
172235
  const [
@@ -171530,6 +172259,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
171530
172259
  dataDir: projectDir2,
171531
172260
  observations: observations2,
171532
172261
  task,
172262
+ taskId,
171533
172263
  agent,
171534
172264
  refresh: refresh ?? "auto",
171535
172265
  reader: getObservationReader(),
@@ -173955,6 +174685,94 @@ ${json2}
173955
174685
  }
173956
174686
  }
173957
174687
  );
174688
+ server.registerTool(
174689
+ "memorix_continuity",
174690
+ {
174691
+ title: "Task Continuity Ledger",
174692
+ 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.",
174693
+ inputSchema: {
174694
+ action: z627.enum(["start", "record", "show", "list", "close"]).describe("Continuity operation"),
174695
+ taskId: z627.string().max(120).optional().describe("Continuity task id"),
174696
+ task: z627.string().max(1e3).optional().describe("Task description for start"),
174697
+ requirements: z627.array(z627.string().max(2e3)).max(8).optional().describe("Requirements for start"),
174698
+ kind: z627.enum(["requirement", "decision", "verification", "risk"]).optional().describe("Entry kind for record"),
174699
+ content: z627.string().max(2e3).optional().describe("Requirement, decision, verification, risk, or outcome text"),
174700
+ verificationStatus: z627.enum(["pending", "passed", "failed", "skipped"]).optional(),
174701
+ verificationId: z627.string().max(120).optional().describe("Stable verification obligation id; reuse it to move pending to passed/failed"),
174702
+ idempotencyKey: z627.string().max(200).optional().describe("Stable retry key; repeating the same operation returns the original event"),
174703
+ status: z627.enum(["completed", "blocked", "abandoned"]).optional().describe("Outcome status for close"),
174704
+ sourceRef: z627.string().max(500).optional().describe("Evidence or source reference"),
174705
+ evidenceRefs: z627.array(z627.string().max(500)).max(8).optional(),
174706
+ actor: z627.string().max(200).optional(),
174707
+ limit: z627.number().int().positive().max(100).optional().default(20)
174708
+ }
174709
+ },
174710
+ async ({ action, taskId, task, requirements, kind, content, verificationStatus, verificationId, idempotencyKey, status, sourceRef, evidenceRefs: evidenceRefs2, actor, limit: limit2 }) => {
174711
+ const unresolved = requireResolvedProject("manage task continuity for the current project");
174712
+ if (unresolved) return unresolved;
174713
+ const { TaskContinuityStore: TaskContinuityStore2 } = await Promise.resolve().then(() => (init_task_continuity(), task_continuity_exports));
174714
+ const continuity = new TaskContinuityStore2();
174715
+ await continuity.init(projectDir2);
174716
+ const effectiveActor = actor ?? currentAgentId;
174717
+ try {
174718
+ if (action === "start") {
174719
+ if (!task?.trim()) return { content: [{ type: "text", text: "task is required for continuity start." }], isError: true };
174720
+ const result2 = continuity.start({
174721
+ projectId: project.id,
174722
+ task,
174723
+ ...taskId ? { taskId } : {},
174724
+ requirements,
174725
+ ...effectiveActor ? { actor: effectiveActor } : {},
174726
+ ...idempotencyKey ? { idempotencyKey } : {}
174727
+ });
174728
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result2 }, null, 2) }] };
174729
+ }
174730
+ if (action === "list") {
174731
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ledgers: continuity.list(project.id, limit2) }, null, 2) }] };
174732
+ }
174733
+ if (!taskId?.trim()) return { content: [{ type: "text", text: "taskId is required for this continuity action." }], isError: true };
174734
+ if (action === "show") {
174735
+ const ledger = continuity.get(project.id, taskId);
174736
+ if (!ledger) return { content: [{ type: "text", text: "Task continuity ledger not found." }], isError: true };
174737
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ledger }, null, 2) }] };
174738
+ }
174739
+ if (action === "record") {
174740
+ if (!kind || !content?.trim()) {
174741
+ return { content: [{ type: "text", text: "record requires kind and content." }], isError: true };
174742
+ }
174743
+ const result2 = continuity.record({
174744
+ projectId: project.id,
174745
+ taskId,
174746
+ kind,
174747
+ content,
174748
+ ...verificationStatus ? { verificationStatus } : {},
174749
+ ...sourceRef ? { sourceRef } : {},
174750
+ ...evidenceRefs2 ? { evidenceRefs: evidenceRefs2 } : {},
174751
+ ...effectiveActor ? { actor: effectiveActor } : {},
174752
+ ...verificationId ? { verificationId } : {},
174753
+ ...idempotencyKey ? { idempotencyKey } : {}
174754
+ });
174755
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result2 }, null, 2) }] };
174756
+ }
174757
+ if (!status || !content?.trim()) {
174758
+ return { content: [{ type: "text", text: "close requires status and content." }], isError: true };
174759
+ }
174760
+ const result = continuity.close({
174761
+ projectId: project.id,
174762
+ taskId,
174763
+ status,
174764
+ content,
174765
+ ...sourceRef ? { sourceRef } : {},
174766
+ ...evidenceRefs2 ? { evidenceRefs: evidenceRefs2 } : {},
174767
+ ...effectiveActor ? { actor: effectiveActor } : {},
174768
+ ...idempotencyKey ? { idempotencyKey } : {}
174769
+ });
174770
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result }, null, 2) }] };
174771
+ } catch (error) {
174772
+ return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true };
174773
+ }
174774
+ }
174775
+ );
173958
174776
  if (teamFeaturesEnabled) {
173959
174777
  const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
173960
174778
  initTeamStoreForProject = initTeamStore2;
@@ -174982,7 +175800,9 @@ fi
174982
175800
  graphManager,
174983
175801
  projectId: project.id,
174984
175802
  deferredInit,
175803
+ activateProjectRuntime,
174985
175804
  switchProject,
175805
+ getProjectDataDir: () => projectDir2,
174986
175806
  isExplicitlyBound: () => projectBinding.isExplicit(),
174987
175807
  getRequestContext,
174988
175808
  handleTransportClose
@@ -175333,7 +176153,10 @@ var init_mcp_root_path = __esm({
175333
176153
  // src/server/modern-mcp-bridge.ts
175334
176154
  var modern_mcp_bridge_exports = {};
175335
176155
  __export(modern_mcp_bridge_exports, {
175336
- createModernMcpBridge: () => createModernMcpBridge
176156
+ ModernMcpRuntimePool: () => ModernMcpRuntimePool,
176157
+ createModernMcpBridge: () => createModernMcpBridge,
176158
+ createModernMcpRuntime: () => createModernMcpRuntime,
176159
+ withBusinessRuntimeQueue: () => withBusinessRuntimeQueue
175337
176160
  });
175338
176161
  import {
175339
176162
  fromJsonSchema,
@@ -175341,7 +176164,12 @@ import {
175341
176164
  } from "@modelcontextprotocol/server";
175342
176165
  import { Client as LegacyClient } from "@modelcontextprotocol/sdk/client/index.js";
175343
176166
  import { InMemoryTransport as LegacyInMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
175344
- async function createModernMcpBridge(options) {
176167
+ function withBusinessRuntimeQueue(work) {
176168
+ const result = businessRuntimeQueue.then(work);
176169
+ businessRuntimeQueue = result.catch(() => void 0);
176170
+ return result;
176171
+ }
176172
+ async function createModernMcpRuntime(options) {
175345
176173
  const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server2(), server_exports2));
175346
176174
  const legacy = await createMemorixServer2(
175347
176175
  options.projectRoot,
@@ -175362,46 +176190,135 @@ async function createModernMcpBridge(options) {
175362
176190
  await legacy.server.connect(serverTransport);
175363
176191
  await legacyClient.connect(clientTransport);
175364
176192
  const listed = await legacyClient.listTools();
175365
- const modern = new ModernMcpServer(getMcpServerInfo());
175366
- for (const tool of listed.tools) {
175367
- modern.registerTool(
175368
- tool.name,
175369
- {
175370
- ...tool.title ? { title: tool.title } : {},
175371
- ...tool.description ? { description: tool.description } : {},
175372
- inputSchema: fromJsonSchema(tool.inputSchema)
175373
- },
175374
- async (args) => await legacyClient.callTool({
175375
- name: tool.name,
175376
- arguments: args
175377
- })
175378
- );
175379
- }
175380
- const bridge = modern;
175381
- bridge.memorix = {
176193
+ const callTool = (name, args) => withBusinessRuntimeQueue(async () => {
176194
+ await legacy.activateProjectRuntime();
176195
+ return await legacyClient.callTool({ name, arguments: args });
176196
+ });
176197
+ const lifecycle = {
175382
176198
  projectId: legacy.projectId,
175383
176199
  deferredInit: legacy.deferredInit,
176200
+ activateProjectRuntime: legacy.activateProjectRuntime,
175384
176201
  switchProject: legacy.switchProject,
176202
+ getProjectDataDir: legacy.getProjectDataDir,
175385
176203
  isExplicitlyBound: legacy.isExplicitlyBound,
175386
176204
  handleTransportClose: legacy.handleTransportClose
175387
176205
  };
175388
176206
  let closed = false;
175389
- const modernClose = modern.close.bind(modern);
175390
- modern.close = async () => {
176207
+ return {
176208
+ projectId: legacy.projectId,
176209
+ getProjectDataDir: legacy.getProjectDataDir,
176210
+ createBridge: () => {
176211
+ const requestServer = new ModernMcpServer(getMcpServerInfo());
176212
+ for (const tool of listed.tools) {
176213
+ requestServer.registerTool(
176214
+ tool.name,
176215
+ {
176216
+ ...tool.title ? { title: tool.title } : {},
176217
+ ...tool.description ? { description: tool.description } : {},
176218
+ inputSchema: fromJsonSchema(tool.inputSchema)
176219
+ },
176220
+ async (args) => await callTool(tool.name, args)
176221
+ );
176222
+ }
176223
+ requestServer.memorix = lifecycle;
176224
+ return requestServer;
176225
+ },
176226
+ close: async () => {
176227
+ if (closed) return;
176228
+ closed = true;
176229
+ await legacyClient.close().catch(() => void 0);
176230
+ await legacy.server.close().catch(() => void 0);
176231
+ legacy.handleTransportClose();
176232
+ }
176233
+ };
176234
+ }
176235
+ async function createModernMcpBridge(options) {
176236
+ const runtime = await createModernMcpRuntime(options);
176237
+ const bridge = runtime.createBridge();
176238
+ const close = bridge.close.bind(bridge);
176239
+ let closed = false;
176240
+ bridge.close = async () => {
175391
176241
  if (closed) return;
175392
176242
  closed = true;
175393
- await modernClose();
175394
- await legacyClient.close().catch(() => void 0);
175395
- await legacy.server.close().catch(() => void 0);
175396
- legacy.handleTransportClose();
176243
+ await close();
176244
+ await runtime.close();
175397
176245
  };
175398
176246
  return bridge;
175399
176247
  }
176248
+ var businessRuntimeQueue, ModernMcpRuntimePool;
175400
176249
  var init_modern_mcp_bridge = __esm({
175401
176250
  "src/server/modern-mcp-bridge.ts"() {
175402
176251
  "use strict";
175403
176252
  init_esm_shims();
175404
176253
  init_mcp_discovery();
176254
+ businessRuntimeQueue = Promise.resolve();
176255
+ ModernMcpRuntimePool = class {
176256
+ constructor(maxEntries = 32) {
176257
+ this.maxEntries = maxEntries;
176258
+ }
176259
+ maxEntries;
176260
+ entries = /* @__PURE__ */ new Map();
176261
+ pending = /* @__PURE__ */ new Map();
176262
+ async createBridge(key, create2) {
176263
+ let entry = this.entries.get(key);
176264
+ if (!entry) {
176265
+ let pending = this.pending.get(key);
176266
+ if (!pending) {
176267
+ pending = create2().then((runtime) => {
176268
+ const created = { runtime, refs: 0, lastUsedAt: Date.now() };
176269
+ this.entries.set(key, created);
176270
+ return created;
176271
+ });
176272
+ this.pending.set(key, pending);
176273
+ }
176274
+ try {
176275
+ entry = await pending;
176276
+ } finally {
176277
+ this.pending.delete(key);
176278
+ }
176279
+ }
176280
+ entry.refs++;
176281
+ entry.lastUsedAt = Date.now();
176282
+ const bridge = entry.runtime.createBridge();
176283
+ const close = bridge.close.bind(bridge);
176284
+ let released = false;
176285
+ bridge.close = async () => {
176286
+ try {
176287
+ await close();
176288
+ } finally {
176289
+ if (!released) {
176290
+ released = true;
176291
+ entry.refs = Math.max(0, entry.refs - 1);
176292
+ entry.lastUsedAt = Date.now();
176293
+ }
176294
+ }
176295
+ };
176296
+ return bridge;
176297
+ }
176298
+ async evictIdle() {
176299
+ let closed = 0;
176300
+ const idle = [...this.entries.entries()].filter(([, entry]) => entry.refs === 0).sort(([, left], [, right]) => left.lastUsedAt - right.lastUsedAt);
176301
+ for (const [key, entry] of idle) {
176302
+ if (this.entries.size <= this.maxEntries || entry.refs > 0) continue;
176303
+ this.entries.delete(key);
176304
+ await entry.runtime.close();
176305
+ closed++;
176306
+ }
176307
+ return closed;
176308
+ }
176309
+ async close() {
176310
+ const entries = [...this.entries.values()];
176311
+ this.entries.clear();
176312
+ await Promise.all(entries.map((entry) => entry.runtime.close().catch(() => void 0)));
176313
+ }
176314
+ stats() {
176315
+ return {
176316
+ entries: this.entries.size,
176317
+ activeRefs: [...this.entries.values()].reduce((sum, entry) => sum + entry.refs, 0),
176318
+ pending: this.pending.size
176319
+ };
176320
+ }
176321
+ };
175405
176322
  }
175406
176323
  });
175407
176324
 
@@ -175513,7 +176430,7 @@ var init_serve_shared = __esm({
175513
176430
 
175514
176431
  // src/cli/version.ts
175515
176432
  function getCliVersion() {
175516
- return true ? "1.9.4" : pkg.version;
176433
+ return true ? "1.9.6" : pkg.version;
175517
176434
  }
175518
176435
  var init_version3 = __esm({
175519
176436
  "src/cli/version.ts"() {