memorix 1.9.5 → 1.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +6 -1
  3. package/README.zh-CN.md +6 -1
  4. package/dist/cli/index.js +1743 -348
  5. package/dist/index.d.ts +12 -7
  6. package/dist/index.js +1048 -266
  7. package/dist/maintenance-runner.js +339 -187
  8. package/dist/media-audio-runner.js +339 -187
  9. package/dist/media-video-runner.js +339 -187
  10. package/dist/memcode-runtime/CHANGELOG.md +53 -0
  11. package/dist/sdk.js +967 -241
  12. package/dist/vector-backfill-runner.js +339 -187
  13. package/docs/API_REFERENCE.md +36 -0
  14. package/docs/CONFIGURATION.md +43 -0
  15. package/docs/DEVELOPMENT.md +1 -1
  16. package/docs/KNOWN_ISSUES_AND_ROADMAP.md +49 -14
  17. package/docs/PERFORMANCE.md +46 -2
  18. package/docs/README.md +2 -1
  19. package/docs/SETUP.md +1 -1
  20. package/package.json +2 -1
  21. package/plugins/antigravity/memorix/plugin.json +1 -1
  22. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  23. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  24. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  25. package/plugins/copilot/memorix/plugin.json +1 -1
  26. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  27. package/plugins/hermes/memorix/plugin.yaml +1 -1
  28. package/plugins/omp/memorix/package.json +1 -1
  29. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  30. package/plugins/pi/memorix/package.json +1 -1
  31. package/server.json +2 -2
  32. package/src/cli/capability-map.ts +1 -0
  33. package/src/cli/command-guide.ts +13 -1
  34. package/src/cli/commands/context.ts +2 -0
  35. package/src/cli/commands/continuity.ts +155 -0
  36. package/src/cli/commands/init.ts +5 -2
  37. package/src/cli/commands/serve-http.ts +242 -42
  38. package/src/cli/index.ts +3 -1
  39. package/src/cli/tui/ConfigureView.tsx +2 -0
  40. package/src/codegraph/auto-context.ts +17 -0
  41. package/src/config/legacy-loader.ts +2 -2
  42. package/src/config/resolved-config.ts +8 -5
  43. package/src/config/yaml-loader.ts +20 -10
  44. package/src/embedding/api-provider.ts +4 -2
  45. package/src/embedding/fastembed-provider.ts +47 -14
  46. package/src/embedding/transformers-provider.ts +29 -7
  47. package/src/evaluation/memory-quality-gate.ts +90 -0
  48. package/src/hooks/official-skills.ts +2 -0
  49. package/src/knowledge/context-assembly.ts +1 -0
  50. package/src/knowledge/context-receipt.ts +15 -0
  51. package/src/knowledge/task-continuity.ts +413 -0
  52. package/src/knowledge/workset.ts +77 -0
  53. package/src/llm/provider.ts +2 -1
  54. package/src/orchestrate/coordinator.ts +60 -0
  55. package/src/orchestrate/effect-ledger.ts +160 -0
  56. package/src/orchestrate/pipeline-trace.ts +40 -6
  57. package/src/runtime/maintenance-jobs.ts +12 -6
  58. package/src/server/modern-mcp-bridge.ts +153 -27
  59. package/src/server/tool-profile.ts +3 -2
  60. package/src/server.ts +129 -5
  61. package/src/store/orama-store.ts +16 -1
  62. package/src/store/sqlite-db.ts +144 -4
  63. package/src/store/sqlite-fts.ts +38 -16
  64. package/src/team/team-store.ts +62 -27
package/dist/sdk.js CHANGED
@@ -294,6 +294,74 @@ var init_launch_root = __esm({
294
294
  }
295
295
  });
296
296
 
297
+ // src/store/sqlite-reliability.ts
298
+ function isTransientSqliteError(error) {
299
+ const code = error?.code;
300
+ if (typeof code === "string") {
301
+ return /^SQLITE_BUSY(?:_|$)/i.test(code);
302
+ }
303
+ return error instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error.message);
304
+ }
305
+ function isSqliteUnavailableError(error) {
306
+ return error instanceof SqliteUnavailableError || error instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error.message);
307
+ }
308
+ function degradedReadError(storeName) {
309
+ return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
310
+ }
311
+ function degradedWriteError(storeName) {
312
+ return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
313
+ }
314
+ function formatSqliteFailure(error) {
315
+ return error instanceof Error && error.message ? error.message : String(error);
316
+ }
317
+ function delay(ms) {
318
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
319
+ }
320
+ async function retrySqliteBusy(operation, label) {
321
+ for (let attempt = 0; ; attempt++) {
322
+ try {
323
+ return await operation();
324
+ } catch (error) {
325
+ if (!isTransientSqliteError(error) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error;
326
+ const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
327
+ console.error(
328
+ `[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
329
+ );
330
+ await delay(waitMs);
331
+ }
332
+ }
333
+ }
334
+ async function initializeSqliteStore(factory, dataDir, label) {
335
+ try {
336
+ loadBetterSqlite3();
337
+ } catch (error) {
338
+ throw new SqliteUnavailableError(error);
339
+ }
340
+ return retrySqliteBusy(async () => {
341
+ const store = factory();
342
+ await store.init(dataDir);
343
+ return store;
344
+ }, label);
345
+ }
346
+ var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
347
+ var init_sqlite_reliability = __esm({
348
+ "src/store/sqlite-reliability.ts"() {
349
+ "use strict";
350
+ init_esm_shims();
351
+ init_sqlite_db();
352
+ SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
353
+ SqliteUnavailableError = class extends Error {
354
+ code = "MEMORIX_SQLITE_UNAVAILABLE";
355
+ constructor(cause) {
356
+ super(
357
+ cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
358
+ );
359
+ this.name = "SqliteUnavailableError";
360
+ }
361
+ };
362
+ }
363
+ });
364
+
297
365
  // src/store/sqlite-fts.ts
298
366
  function normalizeFtsToken(token) {
299
367
  return token.replace(/"/g, '""');
@@ -307,22 +375,27 @@ function buildFtsQuery(query) {
307
375
  function initializeObservationLexicalIndex(db2) {
308
376
  const known = ftsAvailability.get(db2);
309
377
  if (known !== void 0) return known;
310
- try {
311
- db2.exec(CREATE_OBSERVATION_FTS);
312
- const observationCount = Number(db2.prepare("SELECT COUNT(*) AS count FROM observations").get()?.count ?? 0);
313
- const indexedCount = Number(db2.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
314
- if (observationCount !== indexedCount) {
315
- db2.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
378
+ let lastError;
379
+ for (let attempt = 1; attempt <= FTS_INIT_MAX_ATTEMPTS; attempt++) {
380
+ try {
381
+ db2.exec(CREATE_OBSERVATION_FTS);
382
+ const observationCount = Number(db2.prepare("SELECT COUNT(*) AS count FROM observations").get()?.count ?? 0);
383
+ const indexedCount = Number(db2.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
384
+ if (observationCount !== indexedCount) {
385
+ db2.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
386
+ }
387
+ ftsAvailability.set(db2, true);
388
+ return true;
389
+ } catch (error) {
390
+ lastError = error;
391
+ if (!isTransientSqliteError(error) || attempt === FTS_INIT_MAX_ATTEMPTS) break;
316
392
  }
317
- ftsAvailability.set(db2, true);
318
- return true;
319
- } catch (error) {
320
- ftsAvailability.set(db2, false);
321
- console.warn(
322
- `[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${error instanceof Error ? error.message : String(error)}`
323
- );
324
- return false;
325
393
  }
394
+ ftsAvailability.set(db2, false);
395
+ console.warn(
396
+ `[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${lastError instanceof Error ? lastError.message : String(lastError)}`
397
+ );
398
+ return false;
326
399
  }
327
400
  function isObservationLexicalIndexEnabled(db2) {
328
401
  return ftsAvailability.get(db2) === true;
@@ -376,11 +449,12 @@ function searchObservationLexically(db2, options) {
376
449
  score: Math.max(0, Number(row.lexical_score ?? 0))
377
450
  }));
378
451
  }
379
- var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS;
452
+ var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS, FTS_INIT_MAX_ATTEMPTS;
380
453
  var init_sqlite_fts = __esm({
381
454
  "src/store/sqlite-fts.ts"() {
382
455
  "use strict";
383
456
  init_esm_shims();
457
+ init_sqlite_reliability();
384
458
  ftsAvailability = /* @__PURE__ */ new WeakMap();
385
459
  FTS_TABLE = "observations_fts";
386
460
  CREATE_OBSERVATION_FTS = `
@@ -414,15 +488,19 @@ CREATE TRIGGER IF NOT EXISTS observations_fts_au AFTER UPDATE ON observations BE
414
488
  VALUES (new.id, new.entityName, new.title, new.narrative, new.facts, new.filesModified, new.concepts, new.attachments);
415
489
  END;
416
490
  `;
491
+ FTS_INIT_MAX_ATTEMPTS = 3;
417
492
  }
418
493
  });
419
494
 
420
495
  // src/store/sqlite-db.ts
421
496
  var sqlite_db_exports = {};
422
497
  __export(sqlite_db_exports, {
498
+ acquireDatabase: () => acquireDatabase,
423
499
  closeAllDatabases: () => closeAllDatabases,
424
500
  closeDatabase: () => closeDatabase,
501
+ evictIdleDatabases: () => evictIdleDatabases,
425
502
  getDatabase: () => getDatabase,
503
+ getDatabaseStats: () => getDatabaseStats,
426
504
  isSqliteAvailable: () => isSqliteAvailable,
427
505
  loadBetterSqlite3: () => loadBetterSqlite3
428
506
  });
@@ -460,11 +538,21 @@ function applySchemaMigrations(db2) {
460
538
  apply();
461
539
  }
462
540
  }
541
+ function normalizeDataDir(dataDir) {
542
+ return path3.resolve(dataDir);
543
+ }
544
+ function touchDatabase(normalized, db2) {
545
+ _dbCache.delete(normalized);
546
+ _dbCache.set(normalized, db2);
547
+ }
463
548
  function getDatabase(dataDir) {
464
- const normalized = path3.resolve(dataDir);
549
+ const normalized = normalizeDataDir(dataDir);
465
550
  assertNotHomeDataDir(normalized);
466
551
  const existing = _dbCache.get(normalized);
467
- if (existing) return existing;
552
+ if (existing) {
553
+ touchDatabase(normalized, existing);
554
+ return existing;
555
+ }
468
556
  loadBetterSqlite3();
469
557
  fs.mkdirSync(dataDir, { recursive: true });
470
558
  const dbPath = path3.join(dataDir, "memorix.db");
@@ -494,6 +582,8 @@ function getDatabase(dataDir) {
494
582
  db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
495
583
  db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
496
584
  db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
585
+ db2.exec(CREATE_TASK_CONTINUITY_EVENTS_TABLE);
586
+ db2.exec(CREATE_AGENT_EFFECTS_TABLE);
497
587
  try {
498
588
  db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
499
589
  } catch {
@@ -532,7 +622,7 @@ function getDatabase(dataDir) {
532
622
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('storage_generation', '0')`).run();
533
623
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('next_id', '1')`).run();
534
624
  db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('mini_skills_generation', '0')`).run();
535
- _dbCache.set(normalized, db2);
625
+ touchDatabase(normalized, db2);
536
626
  return db2;
537
627
  } catch (error) {
538
628
  try {
@@ -542,8 +632,46 @@ function getDatabase(dataDir) {
542
632
  throw error;
543
633
  }
544
634
  }
635
+ function acquireDatabase(dataDir) {
636
+ const normalized = normalizeDataDir(dataDir);
637
+ const db2 = getDatabase(normalized);
638
+ _dbLeases.set(normalized, (_dbLeases.get(normalized) ?? 0) + 1);
639
+ let released = false;
640
+ return {
641
+ dataDir: normalized,
642
+ db: db2,
643
+ release: () => {
644
+ if (released) return;
645
+ released = true;
646
+ const count2 = (_dbLeases.get(normalized) ?? 0) - 1;
647
+ if (count2 > 0) _dbLeases.set(normalized, count2);
648
+ else _dbLeases.delete(normalized);
649
+ }
650
+ };
651
+ }
652
+ function getDatabaseStats() {
653
+ let leased = 0;
654
+ for (const count2 of _dbLeases.values()) leased += count2;
655
+ return { cached: _dbCache.size, leased, leasedDataDirs: _dbLeases.size };
656
+ }
657
+ function evictIdleDatabases(maxCached) {
658
+ const target = Math.max(0, Math.floor(maxCached));
659
+ let evicted = 0;
660
+ for (const [normalized, db2] of [..._dbCache]) {
661
+ if (_dbCache.size <= target) break;
662
+ if ((_dbLeases.get(normalized) ?? 0) > 0) continue;
663
+ try {
664
+ db2.close();
665
+ } catch {
666
+ }
667
+ _dbCache.delete(normalized);
668
+ evicted++;
669
+ }
670
+ return evicted;
671
+ }
545
672
  function closeDatabase(dataDir) {
546
- const normalized = path3.resolve(dataDir);
673
+ const normalized = normalizeDataDir(dataDir);
674
+ if ((_dbLeases.get(normalized) ?? 0) > 0) return;
547
675
  const db2 = _dbCache.get(normalized);
548
676
  if (db2) {
549
677
  try {
@@ -561,6 +689,7 @@ function closeAllDatabases() {
561
689
  }
562
690
  _dbCache.delete(key);
563
691
  }
692
+ _dbLeases.clear();
564
693
  }
565
694
  function isSqliteAvailable() {
566
695
  try {
@@ -570,7 +699,7 @@ function isSqliteAvailable() {
570
699
  return false;
571
700
  }
572
701
  }
573
- 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;
702
+ 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;
574
703
  var init_sqlite_db = __esm({
575
704
  "src/store/sqlite-db.ts"() {
576
705
  "use strict";
@@ -1287,6 +1416,43 @@ CREATE TABLE IF NOT EXISTS memory_feedback_events (
1287
1416
  delta REAL NOT NULL DEFAULT 0,
1288
1417
  observed_at TEXT NOT NULL
1289
1418
  );
1419
+ `;
1420
+ CREATE_TASK_CONTINUITY_EVENTS_TABLE = `
1421
+ CREATE TABLE IF NOT EXISTS task_continuity_events (
1422
+ id TEXT PRIMARY KEY,
1423
+ project_id TEXT NOT NULL,
1424
+ task_id TEXT NOT NULL,
1425
+ task TEXT NOT NULL,
1426
+ kind TEXT NOT NULL,
1427
+ content TEXT NOT NULL,
1428
+ item_status TEXT,
1429
+ source_ref TEXT,
1430
+ evidence_json TEXT NOT NULL DEFAULT '[]',
1431
+ actor TEXT,
1432
+ created_at TEXT NOT NULL,
1433
+ event_key TEXT,
1434
+ item_key TEXT,
1435
+ supersedes_id TEXT
1436
+ );
1437
+ `;
1438
+ CREATE_AGENT_EFFECTS_TABLE = `
1439
+ CREATE TABLE IF NOT EXISTS agent_effects (
1440
+ id TEXT PRIMARY KEY,
1441
+ pipeline_id TEXT NOT NULL,
1442
+ task_id TEXT NOT NULL,
1443
+ attempt INTEGER NOT NULL,
1444
+ effect_key TEXT NOT NULL,
1445
+ tool TEXT NOT NULL,
1446
+ risk_tier TEXT NOT NULL,
1447
+ replay_policy TEXT NOT NULL,
1448
+ status TEXT NOT NULL,
1449
+ call_id TEXT,
1450
+ idempotency_key TEXT NOT NULL,
1451
+ input_fingerprint TEXT,
1452
+ detail TEXT,
1453
+ started_at INTEGER NOT NULL,
1454
+ finished_at INTEGER
1455
+ );
1290
1456
  `;
1291
1457
  CREATE_MCP_BINDINGS_TABLE = `
1292
1458
  CREATE TABLE IF NOT EXISTS mcp_bindings (
@@ -1365,7 +1531,18 @@ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(projec
1365
1531
  CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
1366
1532
  CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
1367
1533
  CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
1534
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_project_task ON task_continuity_events(project_id, task_id, created_at ASC);
1535
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_project_recent ON task_continuity_events(project_id, created_at DESC);
1536
+ CREATE INDEX IF NOT EXISTS idx_task_continuity_item ON task_continuity_events(project_id, task_id, kind, item_key, created_at DESC);
1537
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
1538
+ ON task_continuity_events(project_id, task_id, event_key)
1539
+ WHERE event_key IS NOT NULL;
1540
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_effect_identity
1541
+ ON agent_effects(pipeline_id, task_id, attempt, effect_key);
1542
+ CREATE INDEX IF NOT EXISTS idx_agent_effect_pipeline ON agent_effects(pipeline_id, started_at ASC);
1368
1543
  CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
1544
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC);
1545
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at);
1369
1546
  CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1370
1547
  ON maintenance_jobs(project_id, kind, dedupe_key)
1371
1548
  WHERE status IN ('pending', 'running', 'retry');
@@ -1536,6 +1713,8 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1536
1713
  db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
1537
1714
  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)");
1538
1715
  db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
1716
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC)");
1717
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at)");
1539
1718
  }
1540
1719
  },
1541
1720
  {
@@ -1546,9 +1725,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1546
1725
  addColumnIfMissing(db2, "code_symbols", "source", "source TEXT");
1547
1726
  addColumnIfMissing(db2, "code_edges", "source", "source TEXT");
1548
1727
  }
1728
+ },
1729
+ {
1730
+ id: "1.9-continuity-v2",
1731
+ apply: (db2) => {
1732
+ addColumnIfMissing(db2, "task_continuity_events", "event_key", "event_key TEXT");
1733
+ addColumnIfMissing(db2, "task_continuity_events", "item_key", "item_key TEXT");
1734
+ addColumnIfMissing(db2, "task_continuity_events", "supersedes_id", "supersedes_id TEXT");
1735
+ 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)");
1736
+ db2.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
1737
+ ON task_continuity_events(project_id, task_id, event_key)
1738
+ WHERE event_key IS NOT NULL`);
1739
+ }
1549
1740
  }
1550
1741
  ];
1551
1742
  _dbCache = /* @__PURE__ */ new Map();
1743
+ _dbLeases = /* @__PURE__ */ new Map();
1552
1744
  }
1553
1745
  });
1554
1746
 
@@ -2521,74 +2713,6 @@ var init_types = __esm({
2521
2713
  }
2522
2714
  });
2523
2715
 
2524
- // src/store/sqlite-reliability.ts
2525
- function isTransientSqliteError(error) {
2526
- const code = error?.code;
2527
- if (typeof code === "string") {
2528
- return /^SQLITE_BUSY(?:_|$)/i.test(code);
2529
- }
2530
- return error instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error.message);
2531
- }
2532
- function isSqliteUnavailableError(error) {
2533
- return error instanceof SqliteUnavailableError || error instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error.message);
2534
- }
2535
- function degradedReadError(storeName) {
2536
- return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
2537
- }
2538
- function degradedWriteError(storeName) {
2539
- return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
2540
- }
2541
- function formatSqliteFailure(error) {
2542
- return error instanceof Error && error.message ? error.message : String(error);
2543
- }
2544
- function delay(ms) {
2545
- return new Promise((resolve5) => setTimeout(resolve5, ms));
2546
- }
2547
- async function retrySqliteBusy(operation, label) {
2548
- for (let attempt = 0; ; attempt++) {
2549
- try {
2550
- return await operation();
2551
- } catch (error) {
2552
- if (!isTransientSqliteError(error) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error;
2553
- const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
2554
- console.error(
2555
- `[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
2556
- );
2557
- await delay(waitMs);
2558
- }
2559
- }
2560
- }
2561
- async function initializeSqliteStore(factory, dataDir, label) {
2562
- try {
2563
- loadBetterSqlite3();
2564
- } catch (error) {
2565
- throw new SqliteUnavailableError(error);
2566
- }
2567
- return retrySqliteBusy(async () => {
2568
- const store = factory();
2569
- await store.init(dataDir);
2570
- return store;
2571
- }, label);
2572
- }
2573
- var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
2574
- var init_sqlite_reliability = __esm({
2575
- "src/store/sqlite-reliability.ts"() {
2576
- "use strict";
2577
- init_esm_shims();
2578
- init_sqlite_db();
2579
- SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
2580
- SqliteUnavailableError = class extends Error {
2581
- code = "MEMORIX_SQLITE_UNAVAILABLE";
2582
- constructor(cause) {
2583
- super(
2584
- cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
2585
- );
2586
- this.name = "SqliteUnavailableError";
2587
- }
2588
- };
2589
- }
2590
- });
2591
-
2592
2716
  // src/store/mini-skill-store.ts
2593
2717
  var mini_skill_store_exports = {};
2594
2718
  __export(mini_skill_store_exports, {
@@ -3216,18 +3340,23 @@ __export(yaml_loader_exports, {
3216
3340
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
3217
3341
  import { homedir } from "os";
3218
3342
  import { parse as parseYamlDocument } from "yaml";
3343
+ function cacheKey(homeDir, projectRoot) {
3344
+ return `${homeDir}\0${projectRoot ?? ""}`;
3345
+ }
3219
3346
  function initProjectRoot(root) {
3220
3347
  globalProjectRoot = root;
3221
- configCache.delete(root);
3348
+ configCache.delete(cacheKey(homedir(), root));
3222
3349
  }
3223
3350
  function clearProjectRoot() {
3224
3351
  globalProjectRoot = null;
3225
3352
  }
3226
- function loadYamlConfig(projectRoot) {
3353
+ function loadYamlConfig(projectRoot, options = {}) {
3354
+ const homeDir = options.homeDir ?? homedir();
3227
3355
  const resolvedRoot = projectRoot === null ? null : projectRoot ?? globalProjectRoot ?? null;
3228
- const cached = configCache.get(resolvedRoot ?? null);
3356
+ const key = cacheKey(homeDir, resolvedRoot);
3357
+ const cached = configCache.get(key);
3229
3358
  if (cached) return cached;
3230
- const userYaml = getGlobalYamlPath(homedir());
3359
+ const userYaml = getGlobalYamlPath(homeDir);
3231
3360
  const projectYaml = resolvedRoot ? getProjectYamlPath(resolvedRoot) : null;
3232
3361
  let userConfig = {};
3233
3362
  let projectConfig = {};
@@ -3259,12 +3388,12 @@ function loadYamlConfig(projectRoot) {
3259
3388
  server: { ...userConfig.server, ...projectConfig.server },
3260
3389
  team: { ...userConfig.team, ...projectConfig.team }
3261
3390
  };
3262
- configCache.set(resolvedRoot ?? null, merged);
3391
+ configCache.set(key, merged);
3263
3392
  return merged;
3264
3393
  }
3265
- function resetYamlConfigCache(projectRoot) {
3394
+ function resetYamlConfigCache(projectRoot, homeDir = homedir()) {
3266
3395
  if (projectRoot !== void 0) {
3267
- configCache.delete(projectRoot ?? null);
3396
+ configCache.delete(cacheKey(homeDir, projectRoot ?? null));
3268
3397
  } else {
3269
3398
  configCache.clear();
3270
3399
  }
@@ -3474,9 +3603,9 @@ var init_detector = __esm({
3474
3603
  // src/config/legacy-loader.ts
3475
3604
  import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
3476
3605
  import { homedir as homedir2 } from "os";
3477
- function loadFileConfig() {
3606
+ function loadFileConfig(homeDir = homedir2()) {
3478
3607
  if (cachedConfig !== null) return cachedConfig;
3479
- const configPath = getLegacyConfigJsonPath(homedir2());
3608
+ const configPath = getLegacyConfigJsonPath(homeDir);
3480
3609
  try {
3481
3610
  if (existsSync4(configPath)) {
3482
3611
  cachedConfig = JSON.parse(readFileSync4(configPath, "utf-8"));
@@ -3506,15 +3635,15 @@ import { homedir as homedir3 } from "os";
3506
3635
  function loadTomlConfig(options = {}) {
3507
3636
  const homeDir = options.homeDir ?? homedir3();
3508
3637
  const projectRoot = options.projectRoot ?? null;
3509
- const cacheKey = `${homeDir}\0${projectRoot ?? ""}`;
3510
- const cached = configCache2.get(cacheKey);
3638
+ const cacheKey3 = `${homeDir}\0${projectRoot ?? ""}`;
3639
+ const cached = configCache2.get(cacheKey3);
3511
3640
  if (cached) return cached;
3512
3641
  const userToml = getGlobalConfigTomlPath(homeDir);
3513
3642
  const projectToml = projectRoot ? getProjectConfigTomlPath(projectRoot) : null;
3514
3643
  const userConfig = existsSync5(userToml) ? parseTomlConfig(readFileSync5(userToml, "utf8"), userToml) : {};
3515
3644
  const projectConfig = projectToml && existsSync5(projectToml) ? parseTomlConfig(readFileSync5(projectToml, "utf8"), projectToml) : {};
3516
3645
  const merged = mergeTomlConfig(userConfig, projectConfig);
3517
- configCache2.set(cacheKey, merged);
3646
+ configCache2.set(cacheKey3, merged);
3518
3647
  return merged;
3519
3648
  }
3520
3649
  function mergeTomlConfig(base, override) {
@@ -3714,8 +3843,8 @@ function getResolvedConfig(options = {}) {
3714
3843
  const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
3715
3844
  loadDotenv(projectRoot === null ? void 0 : projectRoot ?? void 0, { userHomeDir: homeDir });
3716
3845
  const toml = loadTomlConfig({ projectRoot: projectRoot ?? null, homeDir });
3717
- const yaml = loadYamlConfig(projectRoot ?? null);
3718
- const legacy = loadFileConfig();
3846
+ const yaml = loadYamlConfig(projectRoot ?? null, { homeDir });
3847
+ const legacy = loadFileConfig(homeDir);
3719
3848
  const embeddingBaseUrl = first(process.env.MEMORIX_EMBEDDING_BASE_URL, toml.embedding?.base_url, yaml.embedding?.baseUrl, legacy.embeddingApi?.baseUrl);
3720
3849
  const openRouterEmbeddingApiKey = isOpenRouterUrl(embeddingBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
3721
3850
  const memoryLlmProvider = first(
@@ -3737,15 +3866,17 @@ function getResolvedConfig(options = {}) {
3737
3866
  legacy.llm?.baseUrl
3738
3867
  );
3739
3868
  const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
3869
+ const isAtlasCloud = memoryLlmProvider === "atlascloud";
3740
3870
  const memoryLlmApiKey = first(
3741
3871
  process.env.MEMORIX_LLM_API_KEY,
3742
3872
  process.env.MEMORIX_API_KEY,
3743
3873
  toml.memory?.llm?.api_key,
3744
3874
  yaml.llm?.apiKey,
3745
3875
  legacy.llm?.apiKey,
3746
- process.env.OPENAI_API_KEY,
3747
- process.env.ANTHROPIC_API_KEY,
3748
- openRouterMemoryLlmApiKey
3876
+ // Never borrow another provider's credentials for the Atlas Cloud preset.
3877
+ isAtlasCloud ? process.env.ATLASCLOUD_API_KEY : process.env.OPENAI_API_KEY,
3878
+ isAtlasCloud ? void 0 : process.env.ANTHROPIC_API_KEY,
3879
+ isAtlasCloud ? void 0 : openRouterMemoryLlmApiKey
3749
3880
  );
3750
3881
  const resolved = {
3751
3882
  agent: {
@@ -3924,7 +4055,8 @@ function getEnvSourceNames() {
3924
4055
  "MEMORIX_CODEGRAPH_EXTERNAL_CONTEXT",
3925
4056
  "MEMORIX_CODEGRAPH_EXTERNAL_COMMAND",
3926
4057
  "MEMORIX_CODEGRAPH_EXTERNAL_TIMEOUT_MS",
3927
- "OPENROUTER_API_KEY"
4058
+ "OPENROUTER_API_KEY",
4059
+ "ATLASCLOUD_API_KEY"
3928
4060
  ].filter((name) => process.env[name]);
3929
4061
  }
3930
4062
  function isOpenRouterUrl(value) {
@@ -4143,12 +4275,45 @@ function textHash(text3) {
4143
4275
  async function loadDiskCache() {
4144
4276
  try {
4145
4277
  const raw = await readFile(CACHE_FILE, "utf-8");
4278
+ if (Buffer.byteLength(raw, "utf8") > MAX_CACHE_FILE_BYTES) {
4279
+ console.error("[memorix] FastEmbed cache exceeds the disk load limit; starting with an empty cache");
4280
+ return;
4281
+ }
4146
4282
  const entries = JSON.parse(raw);
4147
- for (const [k, v] of entries) cache.set(k, v);
4148
- console.error(`[memorix] Loaded ${entries.length} cached embeddings from disk`);
4283
+ if (!Array.isArray(entries)) return;
4284
+ for (const entry of entries) {
4285
+ if (!Array.isArray(entry) || typeof entry[0] !== "string" || !isVector(entry[1])) continue;
4286
+ cacheSet(entry[0], entry[1], false);
4287
+ }
4288
+ console.error(`[memorix] Loaded ${cache.size} cached embeddings from disk`);
4149
4289
  } catch {
4150
4290
  }
4151
4291
  }
4292
+ function isVector(value) {
4293
+ return Array.isArray(value) && value.every((entry) => typeof entry === "number");
4294
+ }
4295
+ function maxCachePayloadBytes() {
4296
+ const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
4297
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES;
4298
+ }
4299
+ function cacheSet(hash4, value, markDirty = true) {
4300
+ const incomingBytes = value.length * 8;
4301
+ if (incomingBytes > maxCachePayloadBytes()) return;
4302
+ const existing = cache.get(hash4);
4303
+ if (existing) cachePayloadBytes -= existing.length * 8;
4304
+ else {
4305
+ while (cache.size >= MAX_CACHE_SIZE || cachePayloadBytes + incomingBytes > maxCachePayloadBytes()) {
4306
+ const firstKey = cache.keys().next().value;
4307
+ if (firstKey === void 0) break;
4308
+ const old = cache.get(firstKey);
4309
+ cache.delete(firstKey);
4310
+ if (old) cachePayloadBytes -= old.length * 8;
4311
+ }
4312
+ }
4313
+ cache.set(hash4, value);
4314
+ cachePayloadBytes += incomingBytes;
4315
+ if (markDirty) diskCacheDirty = true;
4316
+ }
4152
4317
  async function saveDiskCache() {
4153
4318
  if (!diskCacheDirty) return;
4154
4319
  try {
@@ -4159,7 +4324,7 @@ async function saveDiskCache() {
4159
4324
  } catch {
4160
4325
  }
4161
4326
  }
4162
- var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, diskCacheDirty, FastEmbedProvider;
4327
+ var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, DEFAULT_CACHE_PAYLOAD_BYTES, MAX_CACHE_FILE_BYTES, cachePayloadBytes, diskCacheDirty, FastEmbedProvider;
4163
4328
  var init_fastembed_provider = __esm({
4164
4329
  "src/embedding/fastembed-provider.ts"() {
4165
4330
  "use strict";
@@ -4169,6 +4334,9 @@ var init_fastembed_provider = __esm({
4169
4334
  CACHE_FILE = join2(CACHE_DIR, ".embedding-cache.json");
4170
4335
  cache = /* @__PURE__ */ new Map();
4171
4336
  MAX_CACHE_SIZE = 5e3;
4337
+ DEFAULT_CACHE_PAYLOAD_BYTES = 64 * 1024 * 1024;
4338
+ MAX_CACHE_FILE_BYTES = 128 * 1024 * 1024;
4339
+ cachePayloadBytes = 0;
4172
4340
  diskCacheDirty = false;
4173
4341
  FastEmbedProvider = class _FastEmbedProvider {
4174
4342
  name = "fastembed-bge-small";
@@ -4210,7 +4378,7 @@ var init_fastembed_provider = __esm({
4210
4378
  if (result.length !== this.dimensions) {
4211
4379
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
4212
4380
  }
4213
- this.cacheSet(hash4, result);
4381
+ cacheSet(hash4, result);
4214
4382
  return result;
4215
4383
  }
4216
4384
  async embedBatch(texts) {
@@ -4235,7 +4403,7 @@ var init_fastembed_provider = __esm({
4235
4403
  const originalIdx = uncachedIndices[batchIdx];
4236
4404
  const plain = Array.from(vec);
4237
4405
  results[originalIdx] = plain;
4238
- this.cacheSet(textHash(uncachedTexts[batchIdx]), plain);
4406
+ cacheSet(textHash(uncachedTexts[batchIdx]), plain);
4239
4407
  batchIdx++;
4240
4408
  }
4241
4409
  }
@@ -4243,14 +4411,6 @@ var init_fastembed_provider = __esm({
4243
4411
  }
4244
4412
  return results;
4245
4413
  }
4246
- cacheSet(hash4, value) {
4247
- if (cache.size >= MAX_CACHE_SIZE) {
4248
- const firstKey = cache.keys().next().value;
4249
- if (firstKey !== void 0) cache.delete(firstKey);
4250
- }
4251
- cache.set(hash4, value);
4252
- diskCacheDirty = true;
4253
- }
4254
4414
  };
4255
4415
  }
4256
4416
  });
@@ -4260,7 +4420,15 @@ var transformers_provider_exports = {};
4260
4420
  __export(transformers_provider_exports, {
4261
4421
  TransformersProvider: () => TransformersProvider
4262
4422
  });
4263
- var cache2, MAX_CACHE_SIZE2, TransformersProvider;
4423
+ import { createHash as createHash2 } from "crypto";
4424
+ function cacheKey2(text3) {
4425
+ return createHash2("sha256").update(text3).digest("hex");
4426
+ }
4427
+ function maxCachePayloadBytes2() {
4428
+ const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
4429
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES2;
4430
+ }
4431
+ var cache2, MAX_CACHE_SIZE2, DEFAULT_CACHE_PAYLOAD_BYTES2, cachePayloadBytes2, TransformersProvider;
4264
4432
  var init_transformers_provider = __esm({
4265
4433
  "src/embedding/transformers-provider.ts"() {
4266
4434
  "use strict";
@@ -4268,6 +4436,8 @@ var init_transformers_provider = __esm({
4268
4436
  init_provider();
4269
4437
  cache2 = /* @__PURE__ */ new Map();
4270
4438
  MAX_CACHE_SIZE2 = 5e3;
4439
+ DEFAULT_CACHE_PAYLOAD_BYTES2 = 64 * 1024 * 1024;
4440
+ cachePayloadBytes2 = 0;
4271
4441
  TransformersProvider = class _TransformersProvider {
4272
4442
  name = "transformers-minilm";
4273
4443
  dimensions = 384;
@@ -4302,7 +4472,7 @@ var init_transformers_provider = __esm({
4302
4472
  return Promise.all(inputs.map((input) => this.embedInput(input, options)));
4303
4473
  }
4304
4474
  async embed(text3) {
4305
- const cached = cache2.get(text3);
4475
+ const cached = cache2.get(cacheKey2(text3));
4306
4476
  if (cached) return cached;
4307
4477
  const output = await this.extractor(text3, {
4308
4478
  pooling: "mean",
@@ -4312,7 +4482,7 @@ var init_transformers_provider = __esm({
4312
4482
  if (result.length !== this.dimensions) {
4313
4483
  throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
4314
4484
  }
4315
- this.cacheSet(text3, result);
4485
+ this.cacheSet(cacheKey2(text3), result);
4316
4486
  return result;
4317
4487
  }
4318
4488
  async embedBatch(texts) {
@@ -4320,7 +4490,7 @@ var init_transformers_provider = __esm({
4320
4490
  const uncachedIndices = [];
4321
4491
  const uncachedTexts = [];
4322
4492
  for (let i2 = 0; i2 < texts.length; i2++) {
4323
- const cached = cache2.get(texts[i2]);
4493
+ const cached = cache2.get(cacheKey2(texts[i2]));
4324
4494
  if (cached) {
4325
4495
  results[i2] = cached;
4326
4496
  } else {
@@ -4338,17 +4508,27 @@ var init_transformers_provider = __esm({
4338
4508
  const vec = Array.from(allVecs[i2]);
4339
4509
  const originalIdx = uncachedIndices[i2];
4340
4510
  results[originalIdx] = vec;
4341
- this.cacheSet(uncachedTexts[i2], vec);
4511
+ this.cacheSet(cacheKey2(uncachedTexts[i2]), vec);
4342
4512
  }
4343
4513
  }
4344
4514
  return results;
4345
4515
  }
4346
4516
  cacheSet(key, value) {
4347
- if (cache2.size >= MAX_CACHE_SIZE2) {
4348
- const firstKey = cache2.keys().next().value;
4349
- if (firstKey !== void 0) cache2.delete(firstKey);
4517
+ const incomingBytes = value.length * 8;
4518
+ if (incomingBytes > maxCachePayloadBytes2()) return;
4519
+ const existing = cache2.get(key);
4520
+ if (existing) cachePayloadBytes2 -= existing.length * 8;
4521
+ else {
4522
+ while (cache2.size >= MAX_CACHE_SIZE2 || cachePayloadBytes2 + incomingBytes > maxCachePayloadBytes2()) {
4523
+ const firstKey = cache2.keys().next().value;
4524
+ if (firstKey === void 0) break;
4525
+ const old = cache2.get(firstKey);
4526
+ cache2.delete(firstKey);
4527
+ if (old) cachePayloadBytes2 -= old.length * 8;
4528
+ }
4350
4529
  }
4351
4530
  cache2.set(key, value);
4531
+ cachePayloadBytes2 += incomingBytes;
4352
4532
  }
4353
4533
  };
4354
4534
  }
@@ -4370,7 +4550,7 @@ __export(api_provider_exports, {
4370
4550
  saveDiskCacheNow: () => saveDiskCacheNow,
4371
4551
  seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
4372
4552
  });
4373
- import { createHash as createHash2 } from "crypto";
4553
+ import { createHash as createHash3 } from "crypto";
4374
4554
  import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
4375
4555
  import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
4376
4556
  import { join as join3 } from "path";
@@ -4397,7 +4577,7 @@ function cacheStatFile() {
4397
4577
  function payloadBytes(vector) {
4398
4578
  return vector.length * BYTES_PER_DIM;
4399
4579
  }
4400
- function maxCachePayloadBytes() {
4580
+ function maxCachePayloadBytes3() {
4401
4581
  const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
4402
4582
  if (raw) {
4403
4583
  const parsed = Number.parseInt(raw, 10);
@@ -4407,7 +4587,7 @@ function maxCachePayloadBytes() {
4407
4587
  }
4408
4588
  function maxEntriesForDimensions(dimensions) {
4409
4589
  const dim = Math.max(1, Math.floor(dimensions));
4410
- const byBytes = Math.floor(maxCachePayloadBytes() / (dim * BYTES_PER_DIM));
4590
+ const byBytes = Math.floor(maxCachePayloadBytes3() / (dim * BYTES_PER_DIM));
4411
4591
  return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
4412
4592
  }
4413
4593
  function rememberDimensions(vector) {
@@ -4420,13 +4600,15 @@ function evictOldest() {
4420
4600
  if (firstKey === void 0) return;
4421
4601
  const old = cache3.get(firstKey);
4422
4602
  cache3.delete(firstKey);
4423
- if (old) cachePayloadBytes -= payloadBytes(old);
4603
+ if (old) cachePayloadBytes3 -= payloadBytes(old);
4424
4604
  }
4425
4605
  function evictToFit(incomingBytes, incomingDims) {
4606
+ if (incomingBytes > maxCachePayloadBytes3()) return false;
4426
4607
  const limit2 = maxEntriesForDimensions(incomingDims);
4427
- while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes + incomingBytes > maxCachePayloadBytes())) {
4608
+ while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes3 + incomingBytes > maxCachePayloadBytes3())) {
4428
4609
  evictOldest();
4429
4610
  }
4611
+ return true;
4430
4612
  }
4431
4613
  function isNumberArray(value) {
4432
4614
  return Array.isArray(value) && value.every((entry) => typeof entry === "number");
@@ -4453,12 +4635,12 @@ function cacheInsert(hash4, value, dirty) {
4453
4635
  const incoming = payloadBytes(value);
4454
4636
  const existing = cache3.get(hash4);
4455
4637
  if (existing) {
4456
- cachePayloadBytes -= payloadBytes(existing);
4638
+ cachePayloadBytes3 -= payloadBytes(existing);
4457
4639
  cache3.delete(hash4);
4458
4640
  }
4459
- evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
4641
+ if (!evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS)) return;
4460
4642
  cache3.set(hash4, value);
4461
- cachePayloadBytes += incoming;
4643
+ cachePayloadBytes3 += incoming;
4462
4644
  if (dirty) diskCacheDirty2 = true;
4463
4645
  }
4464
4646
  async function readCacheStat() {
@@ -4494,7 +4676,7 @@ function cacheNamespace(config) {
4494
4676
  ].join("|");
4495
4677
  }
4496
4678
  function textHash2(text3, namespace) {
4497
- return createHash2("sha256").update(`${namespace}\0${text3}`).digest("hex").slice(0, 16);
4679
+ return createHash3("sha256").update(`${namespace}\0${text3}`).digest("hex").slice(0, 16);
4498
4680
  }
4499
4681
  function inputIdentity(input, options = {}) {
4500
4682
  return JSON.stringify({
@@ -4836,7 +5018,7 @@ function getApiEmbeddingDiskCacheLoadCountForTests() {
4836
5018
  }
4837
5019
  function resetApiEmbeddingCacheForTests() {
4838
5020
  cache3.clear();
4839
- cachePayloadBytes = 0;
5021
+ cachePayloadBytes3 = 0;
4840
5022
  cacheDimensions = null;
4841
5023
  diskCacheEntryCount = 0;
4842
5024
  diskCacheLoaded = false;
@@ -4847,14 +5029,14 @@ function resetApiEmbeddingCacheForTests() {
4847
5029
  }
4848
5030
  function seedApiEmbeddingCacheForTests(entries) {
4849
5031
  cache3.clear();
4850
- cachePayloadBytes = 0;
5032
+ cachePayloadBytes3 = 0;
4851
5033
  cacheDimensions = null;
4852
5034
  for (const [key, value] of entries) cacheInsert(key, value, true);
4853
5035
  diskCacheLoaded = true;
4854
5036
  diskCacheDirty2 = true;
4855
5037
  }
4856
5038
  function getApiEmbeddingCachePayloadBytesForTests() {
4857
- return cachePayloadBytes;
5039
+ return cachePayloadBytes3;
4858
5040
  }
4859
5041
  function getApiEmbeddingJsonlLineCountCallsForTests() {
4860
5042
  return jsonlLineCountCalls;
@@ -5028,7 +5210,7 @@ function scheduleDiskSave() {
5028
5210
  diskSaveTimer = null;
5029
5211
  }, DISK_SAVE_DEBOUNCE_MS);
5030
5212
  }
5031
- function cacheSet(hash4, value) {
5213
+ function cacheSet2(hash4, value) {
5032
5214
  cacheInsert(hash4, value, true);
5033
5215
  }
5034
5216
  function resolveEnvEmbeddingApiKey() {
@@ -5148,7 +5330,7 @@ async function waitForRetry(waitMs, signal) {
5148
5330
  abortSignal.addEventListener("abort", onAbort, { once: true });
5149
5331
  });
5150
5332
  }
5151
- 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;
5333
+ 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;
5152
5334
  var init_api_provider = __esm({
5153
5335
  "src/embedding/api-provider.ts"() {
5154
5336
  "use strict";
@@ -5160,7 +5342,7 @@ var init_api_provider = __esm({
5160
5342
  BYTES_PER_DIM = 8;
5161
5343
  DEFAULT_ASSUMED_DIMENSIONS = 4096;
5162
5344
  MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
5163
- cachePayloadBytes = 0;
5345
+ cachePayloadBytes3 = 0;
5164
5346
  cacheDimensions = null;
5165
5347
  diskCacheEntryCount = 0;
5166
5348
  diskCacheDirty2 = false;
@@ -5345,7 +5527,7 @@ var init_api_provider = __esm({
5345
5527
  } else {
5346
5528
  embedding = await apiCall();
5347
5529
  }
5348
- cacheSet(hash4, embedding);
5530
+ cacheSet2(hash4, embedding);
5349
5531
  scheduleDiskSave();
5350
5532
  return embedding;
5351
5533
  }
@@ -5393,7 +5575,7 @@ var init_api_provider = __esm({
5393
5575
  for (const item of response.data) {
5394
5576
  const originalIdx = chunkIndices[item.index];
5395
5577
  results[originalIdx] = item.embedding;
5396
- cacheSet(textHash2(normalizedTexts[originalIdx], this.cacheKeyNamespace), item.embedding);
5578
+ cacheSet2(textHash2(normalizedTexts[originalIdx], this.cacheKeyNamespace), item.embedding);
5397
5579
  }
5398
5580
  } catch (error) {
5399
5581
  const providerLimit = parseBatchLimit(error);
@@ -5471,7 +5653,7 @@ var init_api_provider = __esm({
5471
5653
  throw new Error(`Expected ${this.dimensions}d, got ${embedding.length}d; dimension mismatch`);
5472
5654
  }
5473
5655
  this.trackUsage(response);
5474
- cacheSet(hash4, embedding);
5656
+ cacheSet2(hash4, embedding);
5475
5657
  scheduleDiskSave();
5476
5658
  return embedding;
5477
5659
  }
@@ -6854,6 +7036,7 @@ var init_provider2 = __esm({
6854
7036
  openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4.1-nano" },
6855
7037
  anthropic: { baseUrl: "https://api.anthropic.com/v1", model: "claude-3-5-haiku-latest" },
6856
7038
  openrouter: { baseUrl: "https://openrouter.ai/api/v1", model: "openai/gpt-4.1-nano" },
7039
+ atlascloud: { baseUrl: "https://api.atlascloud.ai/v1", model: "deepseek-ai/deepseek-v3.2" },
6857
7040
  custom: { baseUrl: "http://localhost:11434/v1", model: "llama3" }
6858
7041
  };
6859
7042
  currentConfig = null;
@@ -7630,7 +7813,7 @@ __export(semantic_index_exports, {
7630
7813
  upsertSemanticVector: () => upsertSemanticVector,
7631
7814
  upsertSemanticVectors: () => upsertSemanticVectors
7632
7815
  });
7633
- import { createHash as createHash3 } from "crypto";
7816
+ import { createHash as createHash4 } from "crypto";
7634
7817
  import { createRequire as createRequire3 } from "module";
7635
7818
  import path11 from "path";
7636
7819
  function createSemanticIndexProfile(provider2) {
@@ -7644,7 +7827,7 @@ function stateKey(dataDir, profile) {
7644
7827
  return `${path11.resolve(dataDir)}::${profile.key}`;
7645
7828
  }
7646
7829
  function tableName(profile) {
7647
- const digest2 = createHash3("sha256").update(profile.key).digest("hex").slice(0, 20);
7830
+ const digest2 = createHash4("sha256").update(profile.key).digest("hex").slice(0, 20);
7648
7831
  return `${TABLE_PREFIX}${digest2}`;
7649
7832
  }
7650
7833
  function indexRoot(dataDir) {
@@ -9031,6 +9214,7 @@ async function searchObservations(options) {
9031
9214
  };
9032
9215
  let queryVector = null;
9033
9216
  let embeddingProviderForSearch = null;
9217
+ let embeddingUnavailable = false;
9034
9218
  if (quality !== "fast" && embeddingEnabled && hasQuery && tier !== "fast") {
9035
9219
  try {
9036
9220
  const provider2 = await getEmbeddingProvider();
@@ -9038,6 +9222,7 @@ async function searchObservations(options) {
9038
9222
  embeddingProviderForSearch = provider2;
9039
9223
  const activeVectorDimensions = getVectorDimensions();
9040
9224
  if (activeVectorDimensions !== null && provider2.dimensions !== activeVectorDimensions) {
9225
+ embeddingUnavailable = true;
9041
9226
  rememberSearchMode(
9042
9227
  modeKey,
9043
9228
  `fulltext (embedding dimension mismatch: provider ${provider2.dimensions}d vs index ${activeVectorDimensions}d)`
@@ -9077,8 +9262,11 @@ async function searchObservations(options) {
9077
9262
  hybridWeights: isCJKHeavy ? { text: 0.2, vector: 0.8 } : { text: 0.6, vector: 0.4 }
9078
9263
  };
9079
9264
  }
9265
+ } else {
9266
+ embeddingUnavailable = true;
9080
9267
  }
9081
9268
  } catch (error) {
9269
+ embeddingUnavailable = true;
9082
9270
  rememberSearchMode(modeKey, "fulltext (embedding unavailable)");
9083
9271
  console.error("[memorix] Embedding failed or timed out, falling back to fulltext search");
9084
9272
  }
@@ -9086,7 +9274,7 @@ async function searchObservations(options) {
9086
9274
  mark("preSearch");
9087
9275
  let results;
9088
9276
  const persistentCorpusEligible = await shouldUsePersistentSearch(projectIds, options.projectId);
9089
- const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled);
9277
+ const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled || embeddingUnavailable);
9090
9278
  let persistent = persistentCorpusEligible && queryVector && embeddingProviderForSearch ? await searchPersistentSemantically(options, projectIds, requestLimit, queryVector, embeddingProviderForSearch) : canUsePersistentLexical ? await searchPersistentLexically(options, projectIds, requestLimit) : null;
9091
9279
  const durableStore = getOptionalObservationStore();
9092
9280
  if (!persistent && queryVector && durableStore?.hasLexicalIndex?.() && count(database) === 0) {
@@ -9732,6 +9920,7 @@ var init_secret_filter = __esm({
9732
9920
  // src/runtime/maintenance-jobs.ts
9733
9921
  var maintenance_jobs_exports = {};
9734
9922
  __export(maintenance_jobs_exports, {
9923
+ DEFAULT_FAILED_HISTORY_RETENTION_MS: () => DEFAULT_FAILED_HISTORY_RETENTION_MS,
9735
9924
  MAINTENANCE_JOB_KINDS: () => MAINTENANCE_JOB_KINDS,
9736
9925
  MaintenanceJobStore: () => MaintenanceJobStore,
9737
9926
  MaintenanceJobWorker: () => MaintenanceJobWorker,
@@ -9781,7 +9970,7 @@ function normalizedKinds(kinds) {
9781
9970
  function getMaintenanceRetryDelayMs(attempts) {
9782
9971
  return Math.min(6e4, 1e3 * 2 ** Math.max(0, attempts - 1));
9783
9972
  }
9784
- var MAINTENANCE_JOB_KINDS, DEFAULT_MAX_ATTEMPTS, DEFAULT_LEASE_MS, DEFAULT_POLL_INTERVAL_MS, MAX_ERROR_LENGTH, DEFAULT_COMPLETED_HISTORY_RETENTION_MS, MaintenanceJobStore, MaintenanceJobWorker;
9973
+ 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;
9785
9974
  var init_maintenance_jobs = __esm({
9786
9975
  "src/runtime/maintenance-jobs.ts"() {
9787
9976
  "use strict";
@@ -9808,6 +9997,7 @@ var init_maintenance_jobs = __esm({
9808
9997
  DEFAULT_POLL_INTERVAL_MS = 2e3;
9809
9998
  MAX_ERROR_LENGTH = 1e3;
9810
9999
  DEFAULT_COMPLETED_HISTORY_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
10000
+ DEFAULT_FAILED_HISTORY_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
9811
10001
  MaintenanceJobStore = class {
9812
10002
  db;
9813
10003
  constructor(dataDir) {
@@ -9934,18 +10124,21 @@ var init_maintenance_jobs = __esm({
9934
10124
  }
9935
10125
  /**
9936
10126
  * Completed jobs are useful recent operator history, not permanent data.
9937
- * Failed jobs stay available for diagnosis until an operator explicitly
9938
- * resolves them or clears the database.
10127
+ * Failed diagnostics are retained longer, but are still bounded so a
10128
+ * permanently failing job kind cannot grow the database forever.
9939
10129
  */
9940
10130
  pruneCompletedHistory(options = {}) {
9941
10131
  const now3 = options.now ?? Date.now();
9942
10132
  const maxAgeMs = Number.isFinite(options.maxAgeMs) ? Math.max(0, Math.floor(options.maxAgeMs)) : DEFAULT_COMPLETED_HISTORY_RETENTION_MS;
10133
+ const failedMaxAgeMs = Number.isFinite(options.failedMaxAgeMs) ? Math.max(0, Math.floor(options.failedMaxAgeMs)) : DEFAULT_FAILED_HISTORY_RETENTION_MS;
9943
10134
  const result = this.db.prepare(`
9944
10135
  DELETE FROM maintenance_jobs
9945
- WHERE status = 'completed'
10136
+ WHERE (status = 'completed'
9946
10137
  AND completed_at IS NOT NULL
9947
- AND completed_at < ?
9948
- `).run(now3 - maxAgeMs);
10138
+ AND completed_at < ?)
10139
+ OR (status = 'failed'
10140
+ AND updated_at < ?)
10141
+ `).run(now3 - maxAgeMs, now3 - failedMaxAgeMs);
9949
10142
  return Number(result.changes ?? 0);
9950
10143
  }
9951
10144
  claimNext(options) {
@@ -10299,7 +10492,7 @@ var init_lifecycle = __esm({
10299
10492
  });
10300
10493
 
10301
10494
  // src/store/evidence-store.ts
10302
- import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
10495
+ import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
10303
10496
  function safeJson(value, fallback) {
10304
10497
  if (typeof value !== "string" || value.length === 0) return fallback;
10305
10498
  try {
@@ -10333,7 +10526,7 @@ function rowToCard(row) {
10333
10526
  };
10334
10527
  }
10335
10528
  function observationHash(observation) {
10336
- return createHash4("sha256").update(JSON.stringify({
10529
+ return createHash5("sha256").update(JSON.stringify({
10337
10530
  projectId: observation.projectId,
10338
10531
  id: observation.id,
10339
10532
  title: observation.title,
@@ -10927,13 +11120,13 @@ __export(workspace_exports, {
10927
11120
  loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
10928
11121
  resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
10929
11122
  });
10930
- import { createHash as createHash5 } from "crypto";
11123
+ import { createHash as createHash6 } from "crypto";
10931
11124
  import { execFile } from "child_process";
10932
11125
  import { promises as fs9 } from "fs";
10933
11126
  import path14 from "path";
10934
11127
  import { promisify } from "util";
10935
11128
  function hash(value) {
10936
- return createHash5("sha256").update(value).digest("hex");
11129
+ return createHash6("sha256").update(value).digest("hex");
10937
11130
  }
10938
11131
  function workspaceId(projectId, mode) {
10939
11132
  return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
@@ -12086,14 +12279,14 @@ __export(asset_store_exports, {
12086
12279
  readMediaAsset: () => readMediaAsset,
12087
12280
  removeMediaAsset: () => removeMediaAsset
12088
12281
  });
12089
- import { createHash as createHash6, randomUUID as randomUUID6 } from "crypto";
12282
+ import { createHash as createHash7, randomUUID as randomUUID6 } from "crypto";
12090
12283
  import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
12091
12284
  import path15 from "path";
12092
12285
  function mediaRoot(dataDir) {
12093
12286
  return path15.resolve(dataDir, "media");
12094
12287
  }
12095
12288
  function projectStorageKey(projectId) {
12096
- return createHash6("sha256").update(projectId).digest("hex").slice(0, 16);
12289
+ return createHash7("sha256").update(projectId).digest("hex").slice(0, 16);
12097
12290
  }
12098
12291
  function validateUnderRoot(root, candidate) {
12099
12292
  const resolved = path15.resolve(candidate);
@@ -12153,7 +12346,7 @@ async function readHeader(filePath, bytes = 32) {
12153
12346
  }
12154
12347
  async function sha256File(filePath) {
12155
12348
  const bytes = await readFile3(filePath);
12156
- return createHash6("sha256").update(bytes).digest("hex");
12349
+ return createHash7("sha256").update(bytes).digest("hex");
12157
12350
  }
12158
12351
  function destinationFor(root, projectId, sha256, extension3) {
12159
12352
  const relative5 = path15.join("assets", projectStorageKey(projectId), sha256.slice(0, 2), `${sha256}${extension3}`);
@@ -12259,7 +12452,7 @@ async function importMediaBuffer(input) {
12259
12452
  if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
12260
12453
  if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
12261
12454
  const detected = detectMedia(input.bytes.subarray(0, 32));
12262
- const sha256 = createHash6("sha256").update(input.bytes).digest("hex");
12455
+ const sha256 = createHash7("sha256").update(input.bytes).digest("hex");
12263
12456
  return importKnownMedia({
12264
12457
  dataDir: input.dataDir,
12265
12458
  projectId: input.projectId,
@@ -12277,7 +12470,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
12277
12470
  if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
12278
12471
  const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
12279
12472
  if (bytes.length !== asset.byteSize) throw new Error(`Media asset size no longer matches metadata: ${asset.id}`);
12280
- const sha256 = createHash6("sha256").update(bytes).digest("hex");
12473
+ const sha256 = createHash7("sha256").update(bytes).digest("hex");
12281
12474
  if (sha256 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
12282
12475
  return bytes;
12283
12476
  }
@@ -65147,7 +65340,7 @@ var require_gaxios = __commonJS({
65147
65340
  var retry_js_1 = require_retry3();
65148
65341
  var stream_1 = __require("stream");
65149
65342
  var interceptor_js_1 = require_interceptor();
65150
- var randomUUID19 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
65343
+ var randomUUID20 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
65151
65344
  var HTTP_STATUS_NO_CONTENT = 204;
65152
65345
  var Gaxios = class {
65153
65346
  agentCache = /* @__PURE__ */ new Map();
@@ -65420,7 +65613,7 @@ var require_gaxios = __commonJS({
65420
65613
  */
65421
65614
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
65422
65615
  if (opts.multipart?.length) {
65423
- const boundary = await randomUUID19();
65616
+ const boundary = await randomUUID20();
65424
65617
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
65425
65618
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
65426
65619
  } else if (shouldDirectlyPassData) {
@@ -71092,16 +71285,16 @@ var require_jwtaccess = __commonJS({
71092
71285
  * @returns A string that returns the cached key.
71093
71286
  */
71094
71287
  getCachedKey(url, scopes) {
71095
- let cacheKey = url;
71288
+ let cacheKey3 = url;
71096
71289
  if (scopes && Array.isArray(scopes) && scopes.length) {
71097
- cacheKey = url ? `${url}_${scopes.join("_")}` : `${scopes.join("_")}`;
71290
+ cacheKey3 = url ? `${url}_${scopes.join("_")}` : `${scopes.join("_")}`;
71098
71291
  } else if (typeof scopes === "string") {
71099
- cacheKey = url ? `${url}_${scopes}` : scopes;
71292
+ cacheKey3 = url ? `${url}_${scopes}` : scopes;
71100
71293
  }
71101
- if (!cacheKey) {
71294
+ if (!cacheKey3) {
71102
71295
  throw Error("Scopes or url must be provided");
71103
71296
  }
71104
- return cacheKey;
71297
+ return cacheKey3;
71105
71298
  }
71106
71299
  /**
71107
71300
  * Get a non-expired access token, after refreshing if necessary.
@@ -77647,7 +77840,7 @@ var require_websocket = __commonJS({
77647
77840
  var http3 = __require("http");
77648
77841
  var net = __require("net");
77649
77842
  var tls = __require("tls");
77650
- var { randomBytes: randomBytes2, createHash: createHash19 } = __require("crypto");
77843
+ var { randomBytes: randomBytes2, createHash: createHash20 } = __require("crypto");
77651
77844
  var { Duplex, Readable: Readable2 } = __require("stream");
77652
77845
  var { URL: URL2 } = __require("url");
77653
77846
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -78315,7 +78508,7 @@ var require_websocket = __commonJS({
78315
78508
  abortHandshake(websocket, socket, "Invalid Upgrade header");
78316
78509
  return;
78317
78510
  }
78318
- const digest2 = createHash19("sha1").update(key + GUID).digest("base64");
78511
+ const digest2 = createHash20("sha1").update(key + GUID).digest("base64");
78319
78512
  if (res.headers["sec-websocket-accept"] !== digest2) {
78320
78513
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
78321
78514
  return;
@@ -78687,7 +78880,7 @@ var require_websocket_server = __commonJS({
78687
78880
  var EventEmitter2 = __require("events");
78688
78881
  var http3 = __require("http");
78689
78882
  var { Duplex } = __require("stream");
78690
- var { createHash: createHash19 } = __require("crypto");
78883
+ var { createHash: createHash20 } = __require("crypto");
78691
78884
  var extension3 = require_extension();
78692
78885
  var PerMessageDeflate2 = require_permessage_deflate();
78693
78886
  var subprotocol2 = require_subprotocol();
@@ -78994,7 +79187,7 @@ var require_websocket_server = __commonJS({
78994
79187
  );
78995
79188
  }
78996
79189
  if (this._state > RUNNING) return abortHandshake(socket, 503);
78997
- const digest2 = createHash19("sha1").update(key + GUID).digest("base64");
79190
+ const digest2 = createHash20("sha1").update(key + GUID).digest("base64");
78998
79191
  const headers = [
78999
79192
  "HTTP/1.1 101 Switching Protocols",
79000
79193
  "Upgrade: websocket",
@@ -143053,9 +143246,9 @@ var init_union_priority_sort = __esm({
143053
143246
  // node_modules/typebox/build/value/clean/from_union.mjs
143054
143247
  function FromUnion9(context, type, value) {
143055
143248
  for (const schema of UnionPrioritySort(type.anyOf)) {
143056
- const clean = FromType19(context, schema, Clone2(value));
143057
- if (Check2(context, schema, clean))
143058
- return clean;
143249
+ const clean2 = FromType19(context, schema, Clone2(value));
143250
+ if (Check2(context, schema, clean2))
143251
+ return clean2;
143059
143252
  }
143060
143253
  return value;
143061
143254
  }
@@ -147693,9 +147886,9 @@ var init_long_term = __esm({
147693
147886
  });
147694
147887
 
147695
147888
  // src/codegraph/ids.ts
147696
- import { createHash as createHash7 } from "crypto";
147889
+ import { createHash as createHash8 } from "crypto";
147697
147890
  function digest(input) {
147698
- return createHash7("sha256").update(input).digest("hex").slice(0, 16);
147891
+ return createHash8("sha256").update(input).digest("hex").slice(0, 16);
147699
147892
  }
147700
147893
  function normalizeCodePath(path37) {
147701
147894
  return path37.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
@@ -148743,7 +148936,7 @@ var init_store = __esm({
148743
148936
 
148744
148937
  // src/codegraph/code-state.ts
148745
148938
  import { execFile as execFile2 } from "child_process";
148746
- import { createHash as createHash8 } from "crypto";
148939
+ import { createHash as createHash9 } from "crypto";
148747
148940
  import { promisify as promisify3 } from "util";
148748
148941
  async function runGit(rootPath, args) {
148749
148942
  try {
@@ -148772,7 +148965,7 @@ function changedPathCount(status) {
148772
148965
  return count2;
148773
148966
  }
148774
148967
  function fingerprint(baseRevision, status) {
148775
- return createHash8("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
148968
+ return createHash9("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
148776
148969
  }
148777
148970
  async function collectCodeStateSnapshot(input) {
148778
148971
  const [revision, status] = await Promise.all([
@@ -148866,12 +149059,12 @@ var init_exclude3 = __esm({
148866
149059
  });
148867
149060
 
148868
149061
  // src/codegraph/semantic-provider.ts
148869
- import { createHash as createHash9 } from "crypto";
149062
+ import { createHash as createHash10 } from "crypto";
148870
149063
  import "fs";
148871
149064
  import { resolve } from "path";
148872
149065
  import ts from "typescript";
148873
149066
  function hashText(value) {
148874
- return createHash9("sha256").update(value).digest("hex");
149067
+ return createHash10("sha256").update(value).digest("hex");
148875
149068
  }
148876
149069
  function canonicalPath(value) {
148877
149070
  const normalized = resolve(value).replace(/\\/g, "/");
@@ -149216,11 +149409,11 @@ __export(lite_provider_exports, {
149216
149409
  indexProjectLite: () => indexProjectLite,
149217
149410
  refreshProjectLite: () => refreshProjectLite
149218
149411
  });
149219
- import { createHash as createHash10 } from "crypto";
149412
+ import { createHash as createHash11 } from "crypto";
149220
149413
  import { existsSync as existsSync12, readdirSync as readdirSync2, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
149221
149414
  import { join as join4, relative as relative2 } from "path";
149222
149415
  function hashText2(text3) {
149223
- return createHash10("sha256").update(text3).digest("hex");
149416
+ return createHash11("sha256").update(text3).digest("hex");
149224
149417
  }
149225
149418
  function extension2(path37) {
149226
149419
  const index = path37.lastIndexOf(".");
@@ -149853,7 +150046,7 @@ var claim_store_exports = {};
149853
150046
  __export(claim_store_exports, {
149854
150047
  ClaimStore: () => ClaimStore
149855
150048
  });
149856
- import { createHash as createHash11, randomUUID as randomUUID12 } from "crypto";
150049
+ import { createHash as createHash12, randomUUID as randomUUID12 } from "crypto";
149857
150050
  function optionalText3(value) {
149858
150051
  return typeof value === "string" && value ? value : void 0;
149859
150052
  }
@@ -149906,7 +150099,7 @@ function rowToEvent3(row) {
149906
150099
  };
149907
150100
  }
149908
150101
  function evidenceKey(input) {
149909
- return createHash11("sha256").update(JSON.stringify([
150102
+ return createHash12("sha256").update(JSON.stringify([
149910
150103
  input.evidenceKind,
149911
150104
  input.evidenceId,
149912
150105
  input.relation,
@@ -150104,7 +150297,7 @@ __export(claims_exports, {
150104
150297
  supersedeClaim: () => supersedeClaim,
150105
150298
  writeClaim: () => writeClaim
150106
150299
  });
150107
- import { createHash as createHash12, randomUUID as randomUUID13 } from "crypto";
150300
+ import { createHash as createHash13, randomUUID as randomUUID13 } from "crypto";
150108
150301
  function compactText2(value, field) {
150109
150302
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
150110
150303
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
@@ -150116,7 +150309,7 @@ function normalizedTerm(value) {
150116
150309
  return compactText2(value, "identity").toLocaleLowerCase("en-US");
150117
150310
  }
150118
150311
  function hashIdentity(parts) {
150119
- return createHash12("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
150312
+ return createHash13("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
150120
150313
  }
150121
150314
  function buildClaimIdentity(input) {
150122
150315
  const subject = normalizedTerm(input.subject);
@@ -150357,7 +150550,7 @@ function supersedeClaim(store, input) {
150357
150550
  });
150358
150551
  }
150359
150552
  function hashObservation(observation) {
150360
- return createHash12("sha256").update(JSON.stringify([
150553
+ return createHash13("sha256").update(JSON.stringify([
150361
150554
  observation.title,
150362
150555
  observation.narrative,
150363
150556
  observation.facts,
@@ -150601,11 +150794,11 @@ var init_frontmatter = __esm({
150601
150794
  });
150602
150795
 
150603
150796
  // src/knowledge/markdown.ts
150604
- import { createHash as createHash13 } from "crypto";
150797
+ import { createHash as createHash14 } from "crypto";
150605
150798
  import { promises as fs13 } from "fs";
150606
150799
  import path22 from "path";
150607
150800
  function hashContent(content) {
150608
- return createHash13("sha256").update(content).digest("hex");
150801
+ return createHash14("sha256").update(content).digest("hex");
150609
150802
  }
150610
150803
  function stringField(data, key) {
150611
150804
  const value = data[key];
@@ -150731,11 +150924,11 @@ __export(wiki_exports, {
150731
150924
  lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
150732
150925
  readKnowledgePage: () => readKnowledgePage
150733
150926
  });
150734
- import { createHash as createHash14 } from "crypto";
150927
+ import { createHash as createHash15 } from "crypto";
150735
150928
  import { promises as fs14 } from "fs";
150736
150929
  import path23 from "path";
150737
150930
  function hash2(value) {
150738
- return createHash14("sha256").update(value).digest("hex");
150931
+ return createHash15("sha256").update(value).digest("hex");
150739
150932
  }
150740
150933
  function now() {
150741
150934
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -151954,11 +152147,11 @@ __export(workflows_exports, {
151954
152147
  syncCanonicalWorkflows: () => syncCanonicalWorkflows,
151955
152148
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
151956
152149
  });
151957
- import { createHash as createHash15 } from "crypto";
152150
+ import { createHash as createHash16 } from "crypto";
151958
152151
  import { promises as fs15 } from "fs";
151959
152152
  import path24 from "path";
151960
152153
  function hash3(value) {
151961
- return createHash15("sha256").update(value).digest("hex");
152154
+ return createHash16("sha256").update(value).digest("hex");
151962
152155
  }
151963
152156
  function now2() {
151964
152157
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -155993,8 +156186,8 @@ function legacyBehavior(homeDir) {
155993
156186
  function getBehaviorConfig(options = {}) {
155994
156187
  const homeDir = options.homeDir ?? homedir22();
155995
156188
  const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
155996
- const cacheKey = `${homeDir}\0${projectRoot ?? ""}`;
155997
- const cached = cache4.get(cacheKey);
156189
+ const cacheKey3 = `${homeDir}\0${projectRoot ?? ""}`;
156190
+ const cached = cache4.get(cacheKey3);
155998
156191
  if (cached) return cached;
155999
156192
  const legacy = legacyBehavior(homeDir);
156000
156193
  let resolved = {};
@@ -156008,7 +156201,7 @@ function getBehaviorConfig(options = {}) {
156008
156201
  autoCleanup: typeof resolved.autoCleanup === "boolean" ? resolved.autoCleanup : typeof legacy.autoCleanup === "boolean" ? legacy.autoCleanup : DEFAULTS.autoCleanup,
156009
156202
  formationMode: isFormationMode(resolved.formation) ? resolved.formation : isFormationMode(legacy.formationMode) ? legacy.formationMode : DEFAULTS.formationMode
156010
156203
  };
156011
- cache4.set(cacheKey, config);
156204
+ cache4.set(cacheKey3, config);
156012
156205
  return config;
156013
156206
  }
156014
156207
  function resetBehaviorConfigCache() {
@@ -156613,6 +156806,7 @@ function freshnessForMemory(status) {
156613
156806
  return "unknown";
156614
156807
  }
156615
156808
  function receiptOmissionKind(raw) {
156809
+ if (raw.includes("continuity")) return "continuity";
156616
156810
  if (raw.includes("continuation")) return "continuation";
156617
156811
  if (raw.includes("task")) return "task";
156618
156812
  if (raw.includes("fact")) return "current-fact";
@@ -156723,6 +156917,77 @@ function renderTaskWorksetPrompt(input) {
156723
156917
  trust: "source-backed"
156724
156918
  });
156725
156919
  appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
156920
+ if (input.continuity) {
156921
+ const continuity = input.continuity;
156922
+ appendLine(lines, "", maxTokens, omitted, "continuity-heading");
156923
+ appendLine(lines, "Task continuity", maxTokens, omitted, "continuity-heading", selected, {
156924
+ kind: "continuity",
156925
+ id: "continuity:" + continuity.taskId,
156926
+ reason: "explicit task continuity ledger",
156927
+ trust: "source-backed"
156928
+ });
156929
+ appendLine(
156930
+ lines,
156931
+ "- Status: " + continuity.status + " | " + short(continuity.task, 30),
156932
+ maxTokens,
156933
+ omitted,
156934
+ "continuity-task"
156935
+ );
156936
+ appendLine(
156937
+ lines,
156938
+ "- Outcome: " + continuity.outcome.state,
156939
+ maxTokens,
156940
+ omitted,
156941
+ "continuity-outcome",
156942
+ selected,
156943
+ {
156944
+ kind: "continuity",
156945
+ id: "continuity:" + continuity.taskId,
156946
+ reason: "deterministic task outcome projection",
156947
+ trust: "derived"
156948
+ }
156949
+ );
156950
+ for (const requirement of continuity.requirements.slice(0, 3)) {
156951
+ appendLine(lines, "- Requirement: " + short(requirement, 24), maxTokens, omitted, "continuity-requirement", selected, {
156952
+ kind: "continuity",
156953
+ id: "continuity:" + continuity.taskId,
156954
+ reason: "explicit task requirement",
156955
+ trust: "source-backed"
156956
+ });
156957
+ }
156958
+ for (const decision of continuity.decisions.slice(0, 2)) {
156959
+ appendLine(lines, "- Decision: " + short(decision, 24), maxTokens, omitted, "continuity-decision", selected, {
156960
+ kind: "continuity",
156961
+ id: "continuity:" + continuity.taskId,
156962
+ reason: "explicit task decision",
156963
+ trust: "source-backed"
156964
+ });
156965
+ }
156966
+ for (const verification of continuity.verification.slice(-3)) {
156967
+ appendLine(
156968
+ lines,
156969
+ "- Verify [" + verification.status + "]: " + short(verification.content, 22),
156970
+ maxTokens,
156971
+ omitted,
156972
+ "continuity-verification",
156973
+ selected,
156974
+ {
156975
+ kind: "continuity",
156976
+ id: "continuity:" + continuity.taskId,
156977
+ reason: "task verification state",
156978
+ trust: "source-backed"
156979
+ }
156980
+ );
156981
+ }
156982
+ for (const risk of continuity.risks.slice(0, 2)) {
156983
+ appendLine(lines, "- Risk: " + short(risk, 22), maxTokens, omitted, "continuity-risk", selected, {
156984
+ kind: "continuity",
156985
+ id: "continuity:" + continuity.taskId,
156986
+ reason: "open task risk",
156987
+ trust: "source-backed"
156988
+ });
156989
+ }
156990
+ }
156726
156991
  if (input.alwaysOn) {
156727
156992
  const hasAlwaysOn = input.alwaysOn.profile.length > 0 || Boolean(input.alwaysOn.state) || input.alwaysOn.durable.length > 0;
156728
156993
  if (hasAlwaysOn) {
@@ -157246,6 +157511,7 @@ async function buildTaskWorkset(input) {
157246
157511
  version: "1.3",
157247
157512
  task,
157248
157513
  lens: input.lens,
157514
+ ...input.continuity ? { continuity: input.continuity } : {},
157249
157515
  ...input.alwaysOn && (input.alwaysOn.profile.length > 0 || input.alwaysOn.state || input.alwaysOn.durable.length > 0) ? { alwaysOn: input.alwaysOn } : {},
157250
157516
  currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
157251
157517
  ...continuation ? { continuation } : {},
@@ -158630,7 +158896,7 @@ var compaction_checkpoint_store_exports = {};
158630
158896
  __export(compaction_checkpoint_store_exports, {
158631
158897
  CompactionCheckpointStore: () => CompactionCheckpointStore
158632
158898
  });
158633
- import { createHash as createHash17, randomUUID as randomUUID16 } from "crypto";
158899
+ import { createHash as createHash18, randomUUID as randomUUID16 } from "crypto";
158634
158900
  function optionalText7(value) {
158635
158901
  return typeof value === "string" && value ? value : void 0;
158636
158902
  }
@@ -158672,7 +158938,7 @@ function sourceKeyFor(input, summary) {
158672
158938
  input.tokensBefore ?? "",
158673
158939
  input.firstKeptEntryId ?? ""
158674
158940
  ].join("\0");
158675
- return `derived:${createHash17("sha256").update(material).digest("hex").slice(0, 32)}`;
158941
+ return `derived:${createHash18("sha256").update(material).digest("hex").slice(0, 32)}`;
158676
158942
  }
158677
158943
  function rowToCheckpoint(row) {
158678
158944
  return {
@@ -158930,6 +159196,305 @@ var init_compaction_checkpoint_store = __esm({
158930
159196
  }
158931
159197
  });
158932
159198
 
159199
+ // src/knowledge/task-continuity.ts
159200
+ var task_continuity_exports = {};
159201
+ __export(task_continuity_exports, {
159202
+ TASK_CONTINUITY_EVENT_KINDS: () => TASK_CONTINUITY_EVENT_KINDS,
159203
+ TaskContinuityStore: () => TaskContinuityStore,
159204
+ evaluateTaskContinuity: () => evaluateTaskContinuity
159205
+ });
159206
+ import { randomUUID as randomUUID17 } from "crypto";
159207
+ function clean(value, maxLength = MAX_TEXT_LENGTH2) {
159208
+ return sanitizeCredentials(value).replace(/\s+/g, " ").trim().slice(0, maxLength);
159209
+ }
159210
+ function cleanList(values, maxLength = MAX_TEXT_LENGTH2) {
159211
+ return [...new Set((values ?? []).map((value) => clean(value, maxLength)).filter(Boolean))].slice(0, MAX_LIST_ITEMS2);
159212
+ }
159213
+ function parseRefs(value) {
159214
+ if (typeof value !== "string") return [];
159215
+ try {
159216
+ const parsed = JSON.parse(value);
159217
+ return Array.isArray(parsed) ? cleanList(parsed.filter((item) => typeof item === "string"), 500).slice(0, MAX_EVIDENCE_REFS) : [];
159218
+ } catch {
159219
+ return [];
159220
+ }
159221
+ }
159222
+ function evaluateTaskContinuity(ledger) {
159223
+ const verification = {
159224
+ passed: ledger.verification.filter((item) => item.status === "passed").length,
159225
+ failed: ledger.verification.filter((item) => item.status === "failed").length,
159226
+ pending: ledger.verification.filter((item) => item.status === "pending").length,
159227
+ skipped: ledger.verification.filter((item) => item.status === "skipped").length
159228
+ };
159229
+ const reasons = [];
159230
+ if (verification.failed > 0) reasons.push("verification-failed");
159231
+ if (verification.pending > 0) reasons.push("verification-pending");
159232
+ if (ledger.risks.length > 0) reasons.push("open-risks");
159233
+ if (ledger.status === "blocked") reasons.push("task-blocked");
159234
+ if (ledger.status === "abandoned") reasons.push("task-abandoned");
159235
+ if (ledger.status === "blocked" || ledger.status === "abandoned" || verification.failed > 0) {
159236
+ return {
159237
+ state: "at-risk",
159238
+ score: verification.passed > verification.failed ? 0.55 : 0.2,
159239
+ verification,
159240
+ reasons
159241
+ };
159242
+ }
159243
+ if (ledger.status === "completed") {
159244
+ if (verification.passed > 0 && verification.pending === 0 && verification.skipped === 0) {
159245
+ return {
159246
+ state: ledger.risks.length > 0 ? "validated-with-risks" : "validated",
159247
+ score: ledger.risks.length > 0 ? 0.8 : 1,
159248
+ verification,
159249
+ reasons: reasons.length > 0 ? reasons : ["verification-passed"]
159250
+ };
159251
+ }
159252
+ return {
159253
+ state: "unverified",
159254
+ score: 0.4,
159255
+ verification,
159256
+ reasons: reasons.length > 0 ? reasons : ["no-passing-verification"]
159257
+ };
159258
+ }
159259
+ return {
159260
+ state: "in-progress",
159261
+ score: verification.passed > 0 && verification.pending === 0 ? 0.75 : 0.5,
159262
+ verification,
159263
+ reasons: reasons.length > 0 ? reasons : ["task-open"]
159264
+ };
159265
+ }
159266
+ function rowToEvent4(row) {
159267
+ const status = row.item_status ? String(row.item_status) : void 0;
159268
+ return {
159269
+ id: String(row.id),
159270
+ projectId: String(row.project_id),
159271
+ taskId: String(row.task_id),
159272
+ task: clean(String(row.task), MAX_TASK_LENGTH),
159273
+ kind: row.kind,
159274
+ content: clean(String(row.content)),
159275
+ ...status ? { status } : {},
159276
+ ...row.source_ref ? { sourceRef: clean(String(row.source_ref), 500) } : {},
159277
+ evidenceRefs: parseRefs(row.evidence_json),
159278
+ ...row.actor ? { actor: clean(String(row.actor), 200) } : {},
159279
+ at: String(row.created_at),
159280
+ ...row.event_key ? { eventKey: clean(String(row.event_key), 200) } : {},
159281
+ ...row.item_key ? { itemKey: clean(String(row.item_key), 120) } : {},
159282
+ ...row.supersedes_id ? { supersedesId: clean(String(row.supersedes_id), 120) } : {}
159283
+ };
159284
+ }
159285
+ function aggregate(events) {
159286
+ const first2 = events.find((event) => event.kind === "task") ?? events[0];
159287
+ const latestOutcome = [...events].reverse().find((event) => event.kind === "outcome");
159288
+ const verificationByKey = /* @__PURE__ */ new Map();
159289
+ for (const event of events.filter((event2) => event2.kind === "verification")) {
159290
+ verificationByKey.set(event.itemKey ?? `event:${event.id}`, event);
159291
+ }
159292
+ const ledger = {
159293
+ taskId: first2.taskId,
159294
+ projectId: first2.projectId,
159295
+ task: first2.task,
159296
+ status: latestOutcome?.status ?? "open",
159297
+ requirements: events.filter((event) => event.kind === "requirement").map((event) => event.content),
159298
+ decisions: events.filter((event) => event.kind === "decision").map((event) => event.content),
159299
+ verification: [...verificationByKey.values()].map((event) => ({
159300
+ id: event.itemKey ?? event.id,
159301
+ content: event.content,
159302
+ status: event.status ?? "pending",
159303
+ ...event.sourceRef ? { sourceRef: event.sourceRef } : {},
159304
+ evidenceRefs: event.evidenceRefs
159305
+ })),
159306
+ risks: events.filter((event) => event.kind === "risk").map((event) => event.content),
159307
+ outcomes: events.filter((event) => event.kind === "outcome").map((event) => event.content),
159308
+ updatedAt: events[events.length - 1].at,
159309
+ events
159310
+ };
159311
+ return { ...ledger, outcome: evaluateTaskContinuity(ledger) };
159312
+ }
159313
+ var TASK_CONTINUITY_EVENT_KINDS, MAX_TEXT_LENGTH2, MAX_TASK_LENGTH, MAX_LIST_ITEMS2, MAX_EVIDENCE_REFS, MAX_EVENTS_PER_TASK, TaskContinuityStore;
159314
+ var init_task_continuity = __esm({
159315
+ "src/knowledge/task-continuity.ts"() {
159316
+ "use strict";
159317
+ init_esm_shims();
159318
+ init_secret_filter();
159319
+ init_sqlite_db();
159320
+ TASK_CONTINUITY_EVENT_KINDS = [
159321
+ "task",
159322
+ "requirement",
159323
+ "decision",
159324
+ "verification",
159325
+ "risk",
159326
+ "outcome"
159327
+ ];
159328
+ MAX_TEXT_LENGTH2 = 2e3;
159329
+ MAX_TASK_LENGTH = 1e3;
159330
+ MAX_LIST_ITEMS2 = 8;
159331
+ MAX_EVIDENCE_REFS = 8;
159332
+ MAX_EVENTS_PER_TASK = 200;
159333
+ TaskContinuityStore = class {
159334
+ db = null;
159335
+ async init(dataDir) {
159336
+ this.db = getDatabase(dataDir);
159337
+ }
159338
+ requireDb() {
159339
+ if (!this.db) throw new Error("TaskContinuityStore is not initialized.");
159340
+ return this.db;
159341
+ }
159342
+ insert(input) {
159343
+ const db2 = this.requireDb();
159344
+ const projectId = clean(input.projectId, 500);
159345
+ const taskId = clean(input.taskId, 120);
159346
+ const eventKey = input.eventKey ? clean(input.eventKey, 200) : void 0;
159347
+ if (eventKey) {
159348
+ const existing = db2.prepare(
159349
+ "SELECT * FROM task_continuity_events WHERE project_id = ? AND task_id = ? AND event_key = ?"
159350
+ ).get(projectId, taskId, eventKey);
159351
+ if (existing) return rowToEvent4(existing);
159352
+ }
159353
+ const existingCount = db2.prepare(
159354
+ "SELECT COUNT(*) AS count FROM task_continuity_events WHERE project_id = ? AND task_id = ?"
159355
+ ).get(projectId, taskId);
159356
+ if (Number(existingCount?.count ?? 0) >= MAX_EVENTS_PER_TASK) {
159357
+ throw new Error("Task continuity ledger reached its 200-event limit.");
159358
+ }
159359
+ const event = {
159360
+ id: randomUUID17(),
159361
+ projectId,
159362
+ taskId,
159363
+ task: clean(input.task, MAX_TASK_LENGTH),
159364
+ kind: input.kind,
159365
+ content: clean(input.content),
159366
+ ...input.status ? { status: input.status } : {},
159367
+ ...input.sourceRef ? { sourceRef: clean(input.sourceRef, 500) } : {},
159368
+ evidenceRefs: cleanList(input.evidenceRefs, 500).slice(0, MAX_EVIDENCE_REFS),
159369
+ ...input.actor ? { actor: clean(input.actor, 200) } : {},
159370
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
159371
+ ...eventKey ? { eventKey } : {},
159372
+ ...input.itemKey ? { itemKey: clean(input.itemKey, 120) } : {},
159373
+ ...input.supersedesId ? { supersedesId: clean(input.supersedesId, 120) } : {}
159374
+ };
159375
+ db2.prepare(`
159376
+ INSERT INTO task_continuity_events (
159377
+ id, project_id, task_id, task, kind, content, item_status,
159378
+ source_ref, evidence_json, actor, created_at, event_key, item_key, supersedes_id
159379
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
159380
+ `).run(
159381
+ event.id,
159382
+ event.projectId,
159383
+ event.taskId,
159384
+ event.task,
159385
+ event.kind,
159386
+ event.content,
159387
+ event.status ?? null,
159388
+ event.sourceRef ?? null,
159389
+ JSON.stringify(event.evidenceRefs),
159390
+ event.actor ?? null,
159391
+ event.at,
159392
+ event.eventKey ?? null,
159393
+ event.itemKey ?? null,
159394
+ event.supersedesId ?? null
159395
+ );
159396
+ return event;
159397
+ }
159398
+ start(input) {
159399
+ const task = clean(input.task, MAX_TASK_LENGTH);
159400
+ if (!task) throw new Error("A non-empty task is required.");
159401
+ const idempotencyKey = input.idempotencyKey ? clean(input.idempotencyKey, 200) : void 0;
159402
+ const taskId = clean(input.taskId ?? idempotencyKey ?? "", 120) || randomUUID17();
159403
+ const requirements = cleanList(input.requirements);
159404
+ const transaction = this.requireDb().transaction(() => {
159405
+ this.insert({
159406
+ projectId: input.projectId,
159407
+ taskId,
159408
+ task,
159409
+ kind: "task",
159410
+ content: task,
159411
+ status: "open",
159412
+ actor: input.actor,
159413
+ ...idempotencyKey ? { eventKey: `start:${idempotencyKey}` } : {}
159414
+ });
159415
+ for (const requirement of requirements) {
159416
+ this.insert({
159417
+ projectId: input.projectId,
159418
+ taskId,
159419
+ task,
159420
+ kind: "requirement",
159421
+ content: requirement,
159422
+ actor: input.actor
159423
+ });
159424
+ }
159425
+ });
159426
+ transaction.immediate();
159427
+ return { taskId, ledger: this.get(input.projectId, taskId) };
159428
+ }
159429
+ record(input) {
159430
+ const projectId = clean(input.projectId, 500);
159431
+ const taskId = clean(input.taskId, 120);
159432
+ const existing = this.get(projectId, taskId);
159433
+ if (!existing) throw new Error("Task continuity ledger not found: " + taskId);
159434
+ const transaction = this.requireDb().transaction(() => this.insert({
159435
+ projectId,
159436
+ taskId,
159437
+ task: existing.task,
159438
+ kind: input.kind,
159439
+ content: input.content,
159440
+ ...input.kind === "verification" && input.verificationStatus ? { status: input.verificationStatus } : {},
159441
+ sourceRef: input.sourceRef,
159442
+ evidenceRefs: input.evidenceRefs,
159443
+ actor: input.actor,
159444
+ at: input.at,
159445
+ ...input.idempotencyKey ? { eventKey: input.idempotencyKey } : {},
159446
+ ...input.verificationId ? { itemKey: input.verificationId } : {}
159447
+ }));
159448
+ const event = transaction.immediate();
159449
+ return { event, ledger: this.get(projectId, taskId) };
159450
+ }
159451
+ close(input) {
159452
+ const projectId = clean(input.projectId, 500);
159453
+ const taskId = clean(input.taskId, 120);
159454
+ const existing = this.get(projectId, taskId);
159455
+ if (!existing) throw new Error("Task continuity ledger not found: " + taskId);
159456
+ const transaction = this.requireDb().transaction(() => this.insert({
159457
+ projectId,
159458
+ taskId,
159459
+ task: existing.task,
159460
+ kind: "outcome",
159461
+ content: input.content,
159462
+ status: input.status,
159463
+ sourceRef: input.sourceRef,
159464
+ evidenceRefs: input.evidenceRefs,
159465
+ actor: input.actor,
159466
+ at: input.at,
159467
+ ...input.idempotencyKey ? { eventKey: input.idempotencyKey } : {}
159468
+ }));
159469
+ const event = transaction.immediate();
159470
+ return { event, ledger: this.get(projectId, taskId) };
159471
+ }
159472
+ get(projectId, taskId) {
159473
+ const normalizedProjectId = clean(projectId, 500);
159474
+ const normalizedTaskId = clean(taskId, 120);
159475
+ const rows = this.requireDb().prepare(`
159476
+ SELECT * FROM task_continuity_events
159477
+ WHERE project_id = ? AND task_id = ?
159478
+ ORDER BY created_at ASC, rowid ASC
159479
+ `).all(normalizedProjectId, normalizedTaskId);
159480
+ if (rows.length === 0) return void 0;
159481
+ return aggregate(rows.map(rowToEvent4));
159482
+ }
159483
+ list(projectId, limit2 = 20) {
159484
+ const rows = this.requireDb().prepare(`
159485
+ SELECT task_id
159486
+ FROM task_continuity_events
159487
+ WHERE project_id = ?
159488
+ GROUP BY task_id
159489
+ ORDER BY MAX(created_at) DESC
159490
+ LIMIT ?
159491
+ `).all(projectId, Math.min(100, Math.max(1, Math.floor(limit2))));
159492
+ return rows.map((row) => this.get(projectId, String(row.task_id))).filter((ledger) => Boolean(ledger));
159493
+ }
159494
+ };
159495
+ }
159496
+ });
159497
+
158933
159498
  // src/codegraph/auto-context.ts
158934
159499
  var auto_context_exports = {};
158935
159500
  __export(auto_context_exports, {
@@ -159186,6 +159751,16 @@ async function buildAutoProjectContext(input) {
159186
159751
  };
159187
159752
  }
159188
159753
  }
159754
+ let continuity = input.continuity;
159755
+ if (!continuity && input.taskId) {
159756
+ try {
159757
+ const { TaskContinuityStore: TaskContinuityStore2 } = await Promise.resolve().then(() => (init_task_continuity(), task_continuity_exports));
159758
+ const continuityStore = new TaskContinuityStore2();
159759
+ await continuityStore.init(input.dataDir);
159760
+ continuity = continuityStore.get(input.project.id, input.taskId);
159761
+ } catch {
159762
+ }
159763
+ }
159189
159764
  const alwaysOn = await buildAlwaysOnBlock({
159190
159765
  dataDir: input.dataDir,
159191
159766
  projectId: input.project.id,
@@ -159204,6 +159779,7 @@ async function buildAutoProjectContext(input) {
159204
159779
  providerQuality: providerQuality2,
159205
159780
  currentFacts: worksetFactLines(currentFacts),
159206
159781
  ...continuation ? { continuation } : {},
159782
+ ...continuity ? { continuity } : {},
159207
159783
  codeState: codeStateLine(overview),
159208
159784
  ...codeEvolution ? { codeEvolution } : {},
159209
159785
  reliableMemory: sourceSets.reliableSources.slice(0, lens.sourceLimit).map((source) => ({
@@ -159251,6 +159827,7 @@ async function buildAutoProjectContext(input) {
159251
159827
  refresh,
159252
159828
  providerQuality: providerQuality2,
159253
159829
  ...continuationRequested && workset.continuation ? { continuation: workset.continuation } : {},
159830
+ ...workset.continuity ? { continuity: workset.continuity } : {},
159254
159831
  workset
159255
159832
  };
159256
159833
  }
@@ -159552,7 +160129,14 @@ function buildBoundedContextReceipt(input) {
159552
160129
  externalState: provider2?.external.state ?? "not-detected",
159553
160130
  ...input.workset.provenance.snapshotId ? { snapshotId: input.workset.provenance.snapshotId } : {}
159554
160131
  },
159555
- ...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {}
160132
+ ...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {},
160133
+ ...input.workset.continuity ? {
160134
+ continuity: {
160135
+ taskId: input.workset.continuity.taskId,
160136
+ status: input.workset.continuity.status,
160137
+ outcome: input.workset.continuity.outcome
160138
+ }
160139
+ } : {}
159556
160140
  };
159557
160141
  }
159558
160142
  var init_context_receipt = __esm({
@@ -160184,15 +160768,21 @@ var team_store_exports = {};
160184
160768
  __export(team_store_exports, {
160185
160769
  AGENT_TYPE_ROLE_MAP: () => AGENT_TYPE_ROLE_MAP,
160186
160770
  DEFAULT_ROLES: () => DEFAULT_ROLES,
160771
+ DEFAULT_TEAM_QUERY_LIMIT: () => DEFAULT_TEAM_QUERY_LIMIT,
160772
+ MAX_TEAM_QUERY_LIMIT: () => MAX_TEAM_QUERY_LIMIT,
160187
160773
  TeamStore: () => TeamStore,
160188
160774
  getTeamStore: () => getTeamStore,
160189
160775
  initTeamStore: () => initTeamStore,
160190
160776
  isTeamStoreInitialized: () => isTeamStoreInitialized,
160191
160777
  resetTeamStore: () => resetTeamStore
160192
160778
  });
160193
- import { randomUUID as randomUUID17 } from "crypto";
160779
+ import { randomUUID as randomUUID18 } from "crypto";
160194
160780
  import path31 from "path";
160195
160781
  import fs18 from "fs";
160782
+ function clampTeamQueryLimit(limit2) {
160783
+ if (!Number.isFinite(limit2) || limit2 == null) return DEFAULT_TEAM_QUERY_LIMIT;
160784
+ return Math.min(MAX_TEAM_QUERY_LIMIT, Math.max(1, Math.floor(limit2)));
160785
+ }
160196
160786
  function getTeamStore() {
160197
160787
  if (!_teamStore) {
160198
160788
  throw new Error("[memorix] TeamStore not initialized \u2014 call initTeamStore() first");
@@ -160216,12 +160806,14 @@ function resetTeamStore() {
160216
160806
  _teamStore = null;
160217
160807
  _teamStoreDataDir = null;
160218
160808
  }
160219
- var DEFAULT_ROLES, AGENT_TYPE_ROLE_MAP, TeamStore, _teamStore, _teamStoreDataDir;
160809
+ var DEFAULT_TEAM_QUERY_LIMIT, MAX_TEAM_QUERY_LIMIT, DEFAULT_ROLES, AGENT_TYPE_ROLE_MAP, TeamStore, _teamStore, _teamStoreDataDir;
160220
160810
  var init_team_store = __esm({
160221
160811
  "src/team/team-store.ts"() {
160222
160812
  "use strict";
160223
160813
  init_esm_shims();
160224
160814
  init_sqlite_db();
160815
+ DEFAULT_TEAM_QUERY_LIMIT = 100;
160816
+ MAX_TEAM_QUERY_LIMIT = 500;
160225
160817
  DEFAULT_ROLES = [
160226
160818
  { roleId: "planner", label: "Planner", description: "Breaks down requirements, creates tasks, defines dependencies", preferredAgentTypes: ["claude-code", "cursor", "codex"], maxConcurrent: 2 },
160227
160819
  { roleId: "researcher", label: "Researcher", description: "Investigates codebase, gathers context, answers questions", preferredAgentTypes: ["claude-code", "cursor", "gemini-cli"], maxConcurrent: 2 },
@@ -160479,7 +161071,7 @@ var init_team_store = __esm({
160479
161071
  * reactivate it (preserving agent_id). Otherwise create a new one.
160480
161072
  */
160481
161073
  registerAgent(input) {
160482
- const instanceId = input.instanceId || randomUUID17();
161074
+ const instanceId = input.instanceId || randomUUID18();
160483
161075
  const now3 = Date.now();
160484
161076
  const existing = this.stmtAgentFindByInstance.get(
160485
161077
  input.projectId,
@@ -160505,7 +161097,7 @@ var init_team_store = __esm({
160505
161097
  this.eventBus?.emit("agent:joined", { agentId: row2.agent_id, projectId: input.projectId, agentType: input.agentType });
160506
161098
  return row2;
160507
161099
  }
160508
- const agentId = randomUUID17();
161100
+ const agentId = randomUUID18();
160509
161101
  this.stmtAgentUpsert.run({
160510
161102
  agent_id: agentId,
160511
161103
  project_id: input.projectId,
@@ -160531,32 +161123,37 @@ var init_team_store = __esm({
160531
161123
  return this.stmtAgentFindByInstance.get(projectId, agentType, instanceId);
160532
161124
  }
160533
161125
  listAgents(projectId, filter) {
160534
- const all = this.stmtAgentListByProject.all(projectId);
161126
+ const limit2 = clampTeamQueryLimit(filter?.limit);
160535
161127
  if (filter?.status) {
160536
- return all.filter((a) => a.status === filter.status);
161128
+ return this.db.prepare(
161129
+ `SELECT * FROM team_agents WHERE project_id = ? AND status = ? ORDER BY joined_at DESC LIMIT ?`
161130
+ ).all(projectId, filter.status, limit2);
160537
161131
  }
160538
- return all;
161132
+ return this.db.prepare(
161133
+ `SELECT * FROM team_agents WHERE project_id = ? ORDER BY joined_at DESC LIMIT ?`
161134
+ ).all(projectId, limit2);
160539
161135
  }
160540
161136
  /** List agents across all projects (for global scope) */
160541
- listAllAgents() {
161137
+ listAllAgents(limit2) {
160542
161138
  if (!this.db) return [];
160543
- return this.db.prepare("SELECT * FROM team_agents ORDER BY last_heartbeat DESC").all();
161139
+ return this.db.prepare("SELECT * FROM team_agents ORDER BY last_heartbeat DESC LIMIT ?").all(clampTeamQueryLimit(limit2));
160544
161140
  }
160545
161141
  /** List locks across all projects (for global scope) */
160546
- listAllLocks() {
161142
+ listAllLocks(limit2) {
160547
161143
  if (!this.db) return [];
160548
161144
  this.db.prepare("DELETE FROM team_locks WHERE expires_at <= ?").run(Date.now());
160549
- return this.db.prepare("SELECT * FROM team_locks WHERE expires_at > ? ORDER BY locked_at DESC").all(Date.now());
161145
+ return this.db.prepare("SELECT * FROM team_locks WHERE expires_at > ? ORDER BY locked_at DESC LIMIT ?").all(Date.now(), clampTeamQueryLimit(limit2));
160550
161146
  }
160551
161147
  /** List tasks across all projects (for global scope) */
160552
161148
  listAllTasks(filter) {
160553
161149
  if (!this.db) return [];
161150
+ const limit2 = clampTeamQueryLimit(filter?.limit);
160554
161151
  if (filter?.available) {
160555
161152
  return this.db.prepare(
160556
- `SELECT t.* FROM team_tasks t WHERE t.status = 'pending' AND t.assignee_agent_id IS NULL ORDER BY t.created_at DESC`
160557
- ).all();
161153
+ `SELECT t.* FROM team_tasks t WHERE t.status = 'pending' AND t.assignee_agent_id IS NULL ORDER BY t.created_at DESC LIMIT ?`
161154
+ ).all(limit2);
160558
161155
  }
160559
- return this.db.prepare("SELECT * FROM team_tasks ORDER BY created_at DESC").all();
161156
+ return this.db.prepare("SELECT * FROM team_tasks ORDER BY created_at DESC LIMIT ?").all(limit2);
160560
161157
  }
160561
161158
  heartbeat(agentId) {
160562
161159
  const info = this.stmtAgentUpdateHeartbeat.run(Date.now(), agentId);
@@ -160591,7 +161188,10 @@ var init_team_store = __esm({
160591
161188
  return staleIds;
160592
161189
  }
160593
161190
  getActiveCount(projectId) {
160594
- return this.listAgents(projectId, { status: "active" }).length;
161191
+ const row = this.db.prepare(
161192
+ `SELECT COUNT(*) AS count FROM team_agents WHERE project_id = ? AND status = 'active'`
161193
+ ).get(projectId);
161194
+ return Number(row?.count ?? 0);
160595
161195
  }
160596
161196
  // ═══════════════════════════════════════════════════════════════════
160597
161197
  // Message Operations
@@ -160607,7 +161207,7 @@ var init_team_store = __esm({
160607
161207
  return { error: `Recipient agent '${input.recipientAgentId}' not found \u2014 cannot send to unknown agent` };
160608
161208
  }
160609
161209
  }
160610
- const id = randomUUID17();
161210
+ const id = randomUUID18();
160611
161211
  const now3 = Date.now();
160612
161212
  const row = {
160613
161213
  id,
@@ -160626,8 +161226,14 @@ var init_team_store = __esm({
160626
161226
  this.stmtMsgInsert.run(row);
160627
161227
  return row;
160628
161228
  }
160629
- getInbox(projectId, agentId) {
160630
- return this.stmtMsgInbox.all(projectId, agentId);
161229
+ getInbox(projectId, agentId, limit2) {
161230
+ return this.db.prepare(`
161231
+ SELECT * FROM team_messages
161232
+ WHERE project_id = ?
161233
+ AND (recipient_agent_id = ? OR recipient_agent_id IS NULL)
161234
+ ORDER BY created_at ASC
161235
+ LIMIT ?
161236
+ `).all(projectId, agentId, clampTeamQueryLimit(limit2));
160631
161237
  }
160632
161238
  getUnreadCount(projectId, agentId) {
160633
161239
  const row = this.stmtMsgUnreadCount.get(projectId, agentId);
@@ -160657,7 +161263,7 @@ var init_team_store = __esm({
160657
161263
  // Task Operations (with atomic claim semantics)
160658
161264
  // ═══════════════════════════════════════════════════════════════════
160659
161265
  createTask(input) {
160660
- const taskId = randomUUID17();
161266
+ const taskId = randomUUID18();
160661
161267
  const now3 = Date.now();
160662
161268
  const row = {
160663
161269
  task_id: taskId,
@@ -160784,17 +161390,22 @@ var init_team_store = __esm({
160784
161390
  return info.changes;
160785
161391
  }
160786
161392
  listTasks(projectId, filter) {
160787
- if (filter?.available) {
160788
- return this.stmtTaskAvailable.all(projectId);
160789
- }
160790
- const all = this.stmtTaskListByProject.all(projectId);
161393
+ const clauses = ["project_id = ?"];
161394
+ const values = [projectId];
161395
+ if (filter?.available) clauses.push("status = 'pending'", "assignee_agent_id IS NULL");
160791
161396
  if (filter?.status) {
160792
- return all.filter((t2) => t2.status === filter.status);
161397
+ clauses.push("status = ?");
161398
+ values.push(filter.status);
160793
161399
  }
160794
161400
  if (filter?.assignee) {
160795
- return all.filter((t2) => t2.assignee_agent_id === filter.assignee);
161401
+ clauses.push("assignee_agent_id = ?");
161402
+ values.push(filter.assignee);
160796
161403
  }
160797
- return all;
161404
+ values.push(clampTeamQueryLimit(filter?.limit));
161405
+ const orderBy = filter?.available ? "created_at ASC, rowid ASC" : "created_at DESC, rowid DESC";
161406
+ return this.db.prepare(
161407
+ `SELECT * FROM team_tasks WHERE ${clauses.join(" AND ")} ORDER BY ${orderBy} LIMIT ?`
161408
+ ).all(...values);
160798
161409
  }
160799
161410
  /**
160800
161411
  * List available tasks for a specific agent, sorted by role affinity.
@@ -160806,7 +161417,7 @@ var init_team_store = __esm({
160806
161417
  const agent = this.stmtAgentFindById.get(agentId);
160807
161418
  if (!agent) return [];
160808
161419
  const agentRole = agent.role;
160809
- const available = this.stmtTaskAvailable.all(projectId);
161420
+ const available = this.listTasks(projectId, { available: true, limit: MAX_TEAM_QUERY_LIMIT });
160810
161421
  const eligible = available.filter((t2) => !t2.required_role || t2.required_role === agentRole);
160811
161422
  const score = (t2) => {
160812
161423
  if (t2.preferred_role === agentRole) return 0;
@@ -160860,12 +161471,16 @@ var init_team_store = __esm({
160860
161471
  const row = this.stmtLockGet.get(file, projectId);
160861
161472
  return row ?? null;
160862
161473
  }
160863
- listLocks(projectId, agentId) {
161474
+ listLocks(projectId, agentId, limit2) {
160864
161475
  this.stmtLockDeleteExpired.run(projectId, Date.now());
160865
161476
  if (agentId) {
160866
- return this.stmtLockListByAgent.all(projectId, agentId);
161477
+ return this.db.prepare(
161478
+ `SELECT * FROM team_locks WHERE project_id = ? AND locked_by = ? ORDER BY locked_at DESC LIMIT ?`
161479
+ ).all(projectId, agentId, clampTeamQueryLimit(limit2));
160867
161480
  }
160868
- return this.stmtLockListByProject.all(projectId);
161481
+ return this.db.prepare(
161482
+ `SELECT * FROM team_locks WHERE project_id = ? ORDER BY locked_at DESC LIMIT ?`
161483
+ ).all(projectId, clampTeamQueryLimit(limit2));
160869
161484
  }
160870
161485
  releaseAllLocks(agentId) {
160871
161486
  const info = this.stmtLockDeleteByAgent.run(agentId);
@@ -160929,7 +161544,7 @@ var init_team_store = __esm({
160929
161544
  for (const [agentId, msgs] of Object.entries(snap.messages.inboxes)) {
160930
161545
  for (const msg of msgs) {
160931
161546
  this.stmtMsgInsert.run({
160932
- id: msg.id ?? randomUUID17(),
161547
+ id: msg.id ?? randomUUID18(),
160933
161548
  project_id: "migrated",
160934
161549
  sender_agent_id: msg.from ?? "unknown",
160935
161550
  recipient_agent_id: msg.to === "__broadcast__" ? null : msg.to ?? agentId,
@@ -163887,7 +164502,7 @@ __export(planner_exports, {
163887
164502
  materializeTaskGraph: () => materializeTaskGraph,
163888
164503
  seedAutonomousPipeline: () => seedAutonomousPipeline
163889
164504
  });
163890
- import { randomUUID as randomUUID18 } from "crypto";
164505
+ import { randomUUID as randomUUID19 } from "crypto";
163891
164506
  function isPlannerTask(metadata) {
163892
164507
  if (!metadata) return null;
163893
164508
  try {
@@ -163945,7 +164560,7 @@ function seedAutonomousPipeline(teamStore, projectId, config, opts) {
163945
164560
  const maxIterations = config.maxIterations ?? 3;
163946
164561
  const taskBudget = config.taskBudget ?? 15;
163947
164562
  const structuredPlan = opts?.structuredPlan ?? true;
163948
- const pipelineId = randomUUID18();
164563
+ const pipelineId = randomUUID19();
163949
164564
  const meta = {
163950
164565
  plannerType: "plan",
163951
164566
  pipelineId,
@@ -164626,6 +165241,7 @@ var init_official_skills = __esm({
164626
165241
  "| Need the sequence around one memory | `memorix_timeline` | `memorix memory timeline --id <id>` |",
164627
165242
  "| Check source and freshness for a memory | `memorix_evidence` | `memorix evidence list|get|events` |",
164628
165243
  "| Record whether a memory helped, conflicted, or was corrected | `memorix_feedback` | `memorix feedback record|show|audit` |",
165244
+ "| Track task requirements, decisions, verification, and outcome | `memorix_continuity` | `memorix continuity start|record|show|close` |",
164629
165245
  "| Inspect or import controlled media | `memorix_media` | `memorix media import|attach|list|show|status` |",
164630
165246
  '| Learned reusable project knowledge | `memorix_store` | `memorix memory store --type <type> --entity <name> --title "<title>" "<text>"` |',
164631
165247
  "| Task or bug is complete/outdated | `memorix_resolve` | `memorix memory resolve --ids <ids>` |",
@@ -164649,6 +165265,7 @@ var init_official_skills = __esm({
164649
165265
  "3. Use stale or unbound memory only as a warning or search lead.",
164650
165266
  "4. Store durable decisions, fixes, and gotchas after the project state changes.",
164651
165267
  "5. Resolve completed or obsolete memories so future briefs stay small.",
165268
+ "6. For multi-step work, start a continuity ledger and pass its taskId back to memorix_project_context.",
164652
165269
  "",
164653
165270
  "## Store Rules",
164654
165271
  "",
@@ -166376,7 +166993,7 @@ init_freshness();
166376
166993
  init_obs_store();
166377
166994
  init_mini_skill_store();
166378
166995
  init_session_store();
166379
- import { createHash as createHash18 } from "crypto";
166996
+ import { createHash as createHash19 } from "crypto";
166380
166997
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
166381
166998
  import { z as z627 } from "zod";
166382
166999
 
@@ -167768,9 +168385,9 @@ init_frontmatter();
167768
168385
 
167769
168386
  // src/rules/utils.ts
167770
168387
  init_esm_shims();
167771
- import { createHash as createHash16 } from "crypto";
168388
+ import { createHash as createHash17 } from "crypto";
167772
168389
  function hashContent2(content) {
167773
- return createHash16("sha256").update(content.trim()).digest("hex").substring(0, 16);
168390
+ return createHash17("sha256").update(content.trim()).digest("hex").substring(0, 16);
167774
168391
  }
167775
168392
  function generateRuleId(source, filePath) {
167776
168393
  const sanitized = filePath.replace(/[\/\\]/g, "-").replace(/^\./, "");
@@ -169976,6 +170593,7 @@ var TOOL_PROFILES = Object.freeze({
169976
170593
  // keep the default micro profile at its nine-tool token budget.
169977
170594
  memorix_evidence: ["lite", "team", "full"],
169978
170595
  memorix_feedback: ["lite", "team", "full"],
170596
+ memorix_continuity: ["lite", "team", "full"],
169979
170597
  // ── lite: extended solo memory surface ────────────────────────────
169980
170598
  memorix_graph_context: ["lite", "team", "full"],
169981
170599
  memorix_timeline: ["lite", "team", "full"],
@@ -170198,7 +170816,7 @@ var MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
170198
170816
  function getMcpServerInfo() {
170199
170817
  return {
170200
170818
  name: "memorix",
170201
- version: true ? "1.9.5" : "1.0.1"
170819
+ version: true ? "1.9.7" : "1.0.1"
170202
170820
  };
170203
170821
  }
170204
170822
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -170333,7 +170951,7 @@ function coerceObjectArray(val) {
170333
170951
  return [];
170334
170952
  }
170335
170953
  function createDeterministicInstanceId(projectId, agentType, agentName) {
170336
- const digest2 = createHash18("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
170954
+ const digest2 = createHash19("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
170337
170955
  return `auto-${digest2}`;
170338
170956
  }
170339
170957
  var BOOTSTRAP_SAFE_TOOL_NAMES = /* @__PURE__ */ new Set([
@@ -170549,6 +171167,22 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
170549
171167
  }
170550
171168
  await projectRuntimeInitPromise;
170551
171169
  };
171170
+ const activateProjectRuntime = async () => {
171171
+ if (!projectResolved) return;
171172
+ await initObservationStore(projectDir2);
171173
+ await initMiniSkillStore(projectDir2);
171174
+ await initSessionStore(projectDir2);
171175
+ await initObservations(projectDir2, { skipCorpusLoad: true });
171176
+ try {
171177
+ const { initProjectRoot: initProjectRoot2 } = await Promise.resolve().then(() => (init_yaml_loader(), yaml_loader_exports));
171178
+ const { resetDotenv: resetDotenv2, loadDotenv: loadDotenv2 } = await Promise.resolve().then(() => (init_dotenv_loader(), dotenv_loader_exports));
171179
+ initProjectRoot2(project.rootPath);
171180
+ resetDotenv2();
171181
+ loadDotenv2(project.rootPath);
171182
+ } catch {
171183
+ }
171184
+ initLLM({ projectRoot: project.rootPath });
171185
+ };
170552
171186
  let maintenanceWorker = null;
170553
171187
  const startProjectMaintenanceWorker = async (autoCleanup = false) => {
170554
171188
  maintenanceWorker?.stop();
@@ -171464,6 +172098,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
171464
172098
  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.",
171465
172099
  inputSchema: {
171466
172100
  task: z627.string().optional().describe("Current coding task or question"),
172101
+ taskId: z627.string().max(120).optional().describe("Optional task continuity ledger id to include in the brief"),
171467
172102
  refresh: z627.enum(["auto", "always", "never"]).optional().default("auto").describe(
171468
172103
  "Code Memory refresh policy. auto refreshes only when missing or stale."
171469
172104
  ),
@@ -171488,7 +172123,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
171488
172123
  limit: z627.number().optional().describe("Reserved for future source limits; current prompt stays compact by default.")
171489
172124
  }
171490
172125
  },
171491
- async ({ task, refresh, format, agent }) => {
172126
+ async ({ task, taskId, refresh, format, agent }) => {
171492
172127
  const unresolved = requireResolvedProject("build project context for the current project");
171493
172128
  if (unresolved) return unresolved;
171494
172129
  const [
@@ -171518,6 +172153,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
171518
172153
  dataDir: projectDir2,
171519
172154
  observations: observations2,
171520
172155
  task,
172156
+ taskId,
171521
172157
  agent,
171522
172158
  refresh: refresh ?? "auto",
171523
172159
  reader: getObservationReader(),
@@ -173943,6 +174579,94 @@ ${json2}
173943
174579
  }
173944
174580
  }
173945
174581
  );
174582
+ server.registerTool(
174583
+ "memorix_continuity",
174584
+ {
174585
+ title: "Task Continuity Ledger",
174586
+ 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.",
174587
+ inputSchema: {
174588
+ action: z627.enum(["start", "record", "show", "list", "close"]).describe("Continuity operation"),
174589
+ taskId: z627.string().max(120).optional().describe("Continuity task id"),
174590
+ task: z627.string().max(1e3).optional().describe("Task description for start"),
174591
+ requirements: z627.array(z627.string().max(2e3)).max(8).optional().describe("Requirements for start"),
174592
+ kind: z627.enum(["requirement", "decision", "verification", "risk"]).optional().describe("Entry kind for record"),
174593
+ content: z627.string().max(2e3).optional().describe("Requirement, decision, verification, risk, or outcome text"),
174594
+ verificationStatus: z627.enum(["pending", "passed", "failed", "skipped"]).optional(),
174595
+ verificationId: z627.string().max(120).optional().describe("Stable verification obligation id; reuse it to move pending to passed/failed"),
174596
+ idempotencyKey: z627.string().max(200).optional().describe("Stable retry key; repeating the same operation returns the original event"),
174597
+ status: z627.enum(["completed", "blocked", "abandoned"]).optional().describe("Outcome status for close"),
174598
+ sourceRef: z627.string().max(500).optional().describe("Evidence or source reference"),
174599
+ evidenceRefs: z627.array(z627.string().max(500)).max(8).optional(),
174600
+ actor: z627.string().max(200).optional(),
174601
+ limit: z627.number().int().positive().max(100).optional().default(20)
174602
+ }
174603
+ },
174604
+ async ({ action, taskId, task, requirements, kind, content, verificationStatus, verificationId, idempotencyKey, status, sourceRef, evidenceRefs: evidenceRefs2, actor, limit: limit2 }) => {
174605
+ const unresolved = requireResolvedProject("manage task continuity for the current project");
174606
+ if (unresolved) return unresolved;
174607
+ const { TaskContinuityStore: TaskContinuityStore2 } = await Promise.resolve().then(() => (init_task_continuity(), task_continuity_exports));
174608
+ const continuity = new TaskContinuityStore2();
174609
+ await continuity.init(projectDir2);
174610
+ const effectiveActor = actor ?? currentAgentId;
174611
+ try {
174612
+ if (action === "start") {
174613
+ if (!task?.trim()) return { content: [{ type: "text", text: "task is required for continuity start." }], isError: true };
174614
+ const result2 = continuity.start({
174615
+ projectId: project.id,
174616
+ task,
174617
+ ...taskId ? { taskId } : {},
174618
+ requirements,
174619
+ ...effectiveActor ? { actor: effectiveActor } : {},
174620
+ ...idempotencyKey ? { idempotencyKey } : {}
174621
+ });
174622
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result2 }, null, 2) }] };
174623
+ }
174624
+ if (action === "list") {
174625
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ledgers: continuity.list(project.id, limit2) }, null, 2) }] };
174626
+ }
174627
+ if (!taskId?.trim()) return { content: [{ type: "text", text: "taskId is required for this continuity action." }], isError: true };
174628
+ if (action === "show") {
174629
+ const ledger = continuity.get(project.id, taskId);
174630
+ if (!ledger) return { content: [{ type: "text", text: "Task continuity ledger not found." }], isError: true };
174631
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ledger }, null, 2) }] };
174632
+ }
174633
+ if (action === "record") {
174634
+ if (!kind || !content?.trim()) {
174635
+ return { content: [{ type: "text", text: "record requires kind and content." }], isError: true };
174636
+ }
174637
+ const result2 = continuity.record({
174638
+ projectId: project.id,
174639
+ taskId,
174640
+ kind,
174641
+ content,
174642
+ ...verificationStatus ? { verificationStatus } : {},
174643
+ ...sourceRef ? { sourceRef } : {},
174644
+ ...evidenceRefs2 ? { evidenceRefs: evidenceRefs2 } : {},
174645
+ ...effectiveActor ? { actor: effectiveActor } : {},
174646
+ ...verificationId ? { verificationId } : {},
174647
+ ...idempotencyKey ? { idempotencyKey } : {}
174648
+ });
174649
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result2 }, null, 2) }] };
174650
+ }
174651
+ if (!status || !content?.trim()) {
174652
+ return { content: [{ type: "text", text: "close requires status and content." }], isError: true };
174653
+ }
174654
+ const result = continuity.close({
174655
+ projectId: project.id,
174656
+ taskId,
174657
+ status,
174658
+ content,
174659
+ ...sourceRef ? { sourceRef } : {},
174660
+ ...evidenceRefs2 ? { evidenceRefs: evidenceRefs2 } : {},
174661
+ ...effectiveActor ? { actor: effectiveActor } : {},
174662
+ ...idempotencyKey ? { idempotencyKey } : {}
174663
+ });
174664
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result }, null, 2) }] };
174665
+ } catch (error) {
174666
+ return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true };
174667
+ }
174668
+ }
174669
+ );
173946
174670
  if (teamFeaturesEnabled) {
173947
174671
  const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
173948
174672
  initTeamStoreForProject = initTeamStore2;
@@ -174970,7 +175694,9 @@ fi
174970
175694
  graphManager,
174971
175695
  projectId: project.id,
174972
175696
  deferredInit,
175697
+ activateProjectRuntime,
174973
175698
  switchProject,
175699
+ getProjectDataDir: () => projectDir2,
174974
175700
  isExplicitlyBound: () => projectBinding.isExplicit(),
174975
175701
  getRequestContext,
174976
175702
  handleTransportClose