memorix 1.2.10 → 1.3.0

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 (63) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +28 -2
  3. package/README.zh-CN.md +28 -2
  4. package/assets/star-history-dark.svg +30 -0
  5. package/assets/star-history-light.svg +30 -0
  6. package/assets/star-history.json +599 -0
  7. package/dist/cli/index.js +1783 -557
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/index.js +1292 -267
  10. package/dist/index.js.map +1 -1
  11. package/dist/maintenance-runner.js +97 -21
  12. package/dist/maintenance-runner.js.map +1 -1
  13. package/dist/memcode-runtime/CHANGELOG.md +15 -0
  14. package/dist/sdk.d.ts +1 -1
  15. package/dist/sdk.js +1294 -269
  16. package/dist/sdk.js.map +1 -1
  17. package/dist/types.d.ts +99 -1
  18. package/dist/types.js.map +1 -1
  19. package/dist/vector-backfill-runner.js +97 -21
  20. package/dist/vector-backfill-runner.js.map +1 -1
  21. package/docs/1.3-MEMORY-ARCHITECTURE.md +236 -0
  22. package/docs/AGENT_OPERATOR_PLAYBOOK.md +3 -1
  23. package/docs/API_REFERENCE.md +20 -1
  24. package/docs/ARCHITECTURE.md +34 -1
  25. package/docs/DEVELOPMENT.md +3 -2
  26. package/docs/MODULES.md +27 -1
  27. package/docs/README.md +3 -3
  28. package/docs/dev-log/progress.txt +22 -0
  29. package/llms-full.txt +5 -1
  30. package/llms.txt +2 -0
  31. package/package.json +2 -1
  32. package/plugins/antigravity/memorix/rules/memorix.md +2 -0
  33. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  34. package/plugins/claude/memorix/skills/memorix-memory/SKILL.md +3 -0
  35. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  36. package/plugins/codex/memorix/skills/memorix-memory/SKILL.md +3 -0
  37. package/plugins/copilot/memorix/plugin.json +1 -1
  38. package/plugins/copilot/memorix/skills/memorix-memory/SKILL.md +3 -0
  39. package/plugins/cursor/memorix/skills/memorix-memory/SKILL.md +3 -0
  40. package/plugins/gemini/memorix/GEMINI.md +2 -0
  41. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  42. package/plugins/omp/memorix/package.json +1 -1
  43. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  44. package/plugins/pi/memorix/package.json +1 -1
  45. package/plugins/pi/memorix/skills/memorix-memory/SKILL.md +3 -0
  46. package/server.json +2 -2
  47. package/src/cli/command-guide.ts +9 -1
  48. package/src/cli/commands/codegraph.ts +1 -0
  49. package/src/cli/commands/memory.ts +217 -1
  50. package/src/codegraph/auto-context.ts +11 -0
  51. package/src/codegraph/context-pack.ts +3 -0
  52. package/src/embedding/api-provider.ts +23 -9
  53. package/src/embedding/provider.ts +21 -10
  54. package/src/hooks/installers/index.ts +2 -0
  55. package/src/knowledge/context-assembly.ts +2 -1
  56. package/src/knowledge/workset.ts +77 -4
  57. package/src/memory/long-term-store.ts +242 -0
  58. package/src/memory/long-term-types.ts +136 -0
  59. package/src/memory/long-term.ts +626 -0
  60. package/src/memory/owner.ts +75 -0
  61. package/src/server.ts +112 -30
  62. package/src/store/sqlite-db.ts +74 -0
  63. package/src/types.ts +20 -0
@@ -279,7 +279,7 @@ function closeAllDatabases() {
279
279
  _dbCache.delete(key);
280
280
  }
281
281
  }
282
- 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_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_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
282
+ 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_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_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
283
283
  var init_sqlite_db = __esm({
284
284
  "src/store/sqlite-db.ts"() {
285
285
  "use strict";
@@ -705,6 +705,57 @@ CREATE TABLE IF NOT EXISTS knowledge_claim_events (
705
705
  " FOREIGN KEY (workflowId) REFERENCES knowledge_workflows(id) ON DELETE CASCADE",
706
706
  ");"
707
707
  ].join("\n");
708
+ CREATE_LONG_TERM_MEMORIES_TABLE = `
709
+ CREATE TABLE IF NOT EXISTS long_term_memories (
710
+ id TEXT PRIMARY KEY,
711
+ originProjectId TEXT NOT NULL,
712
+ ownerId TEXT NOT NULL,
713
+ scope TEXT NOT NULL,
714
+ kind TEXT NOT NULL,
715
+ state TEXT NOT NULL,
716
+ portability TEXT NOT NULL DEFAULT 'project-bound',
717
+ title TEXT NOT NULL,
718
+ content TEXT NOT NULL,
719
+ factsJson TEXT NOT NULL DEFAULT '[]',
720
+ tagsJson TEXT NOT NULL DEFAULT '[]',
721
+ applicability TEXT,
722
+ origin TEXT NOT NULL,
723
+ createdAt TEXT NOT NULL,
724
+ updatedAt TEXT NOT NULL,
725
+ qualifiedAt TEXT,
726
+ approvedAt TEXT,
727
+ archivedAt TEXT,
728
+ supersededBy TEXT,
729
+ lastValidatedAt TEXT,
730
+ accessCount INTEGER NOT NULL DEFAULT 0,
731
+ lastAccessedAt TEXT
732
+ );
733
+ `;
734
+ CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE = `
735
+ CREATE TABLE IF NOT EXISTS long_term_memory_evidence (
736
+ id TEXT PRIMARY KEY,
737
+ memoryId TEXT NOT NULL,
738
+ kind TEXT NOT NULL,
739
+ referenceId TEXT NOT NULL,
740
+ relation TEXT NOT NULL,
741
+ locator TEXT,
742
+ capturedHash TEXT,
743
+ createdAt TEXT NOT NULL,
744
+ FOREIGN KEY (memoryId) REFERENCES long_term_memories(id) ON DELETE CASCADE
745
+ );
746
+ `;
747
+ CREATE_LONG_TERM_MEMORY_EVENTS_TABLE = `
748
+ CREATE TABLE IF NOT EXISTS long_term_memory_events (
749
+ id TEXT PRIMARY KEY,
750
+ memoryId TEXT NOT NULL,
751
+ kind TEXT NOT NULL,
752
+ fromState TEXT,
753
+ toState TEXT,
754
+ detail TEXT,
755
+ createdAt TEXT NOT NULL,
756
+ FOREIGN KEY (memoryId) REFERENCES long_term_memories(id) ON DELETE CASCADE
757
+ );
758
+ `;
708
759
  CREATE_MAINTENANCE_JOBS_TABLE = `
709
760
  CREATE TABLE IF NOT EXISTS maintenance_jobs (
710
761
  id TEXT PRIMARY KEY,
@@ -800,6 +851,11 @@ CREATE INDEX IF NOT EXISTS idx_knowledge_page_claims_claim ON knowledge_page_cla
800
851
  CREATE INDEX IF NOT EXISTS idx_knowledge_proposals_workspace_status ON knowledge_proposals(workspaceId, status, createdAt DESC);
801
852
  CREATE INDEX IF NOT EXISTS idx_knowledge_workflows_workspace_status ON knowledge_workflows(workspaceId, status, updatedAt DESC);
802
853
  CREATE INDEX IF NOT EXISTS idx_knowledge_workflow_runs_project_workflow ON knowledge_workflow_runs(projectId, workflowId, startedAt DESC);
854
+ CREATE INDEX IF NOT EXISTS idx_long_term_memories_owner_state ON long_term_memories(ownerId, state, updatedAt DESC);
855
+ CREATE INDEX IF NOT EXISTS idx_long_term_memories_project_state ON long_term_memories(originProjectId, state, updatedAt DESC);
856
+ CREATE INDEX IF NOT EXISTS idx_long_term_memories_scope_portability ON long_term_memories(scope, portability, state, updatedAt DESC);
857
+ CREATE INDEX IF NOT EXISTS idx_long_term_memory_evidence_memory ON long_term_memory_evidence(memoryId, createdAt);
858
+ CREATE INDEX IF NOT EXISTS idx_long_term_memory_events_memory ON long_term_memory_events(memoryId, createdAt);
803
859
  CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_ready ON maintenance_jobs(status, run_after);
804
860
  CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_project ON maintenance_jobs(project_id, status, run_after);
805
861
  CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_targets(updated_at);
@@ -888,6 +944,19 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
888
944
  db2.exec("CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_session_pending ON compaction_checkpoints(project_id, session_id, agent, phase, status, pre_captured_at DESC)");
889
945
  db2.exec("CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key)");
890
946
  }
947
+ },
948
+ {
949
+ id: "1.3-long-term-memory",
950
+ apply: (db2) => {
951
+ db2.exec(CREATE_LONG_TERM_MEMORIES_TABLE);
952
+ db2.exec(CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE);
953
+ db2.exec(CREATE_LONG_TERM_MEMORY_EVENTS_TABLE);
954
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_owner_state ON long_term_memories(ownerId, state, updatedAt DESC)");
955
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_project_state ON long_term_memories(originProjectId, state, updatedAt DESC)");
956
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_scope_portability ON long_term_memories(scope, portability, state, updatedAt DESC)");
957
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memory_evidence_memory ON long_term_memory_evidence(memoryId, createdAt)");
958
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memory_events_memory ON long_term_memory_events(memoryId, createdAt)");
959
+ }
891
960
  }
892
961
  ];
893
962
  _dbCache = /* @__PURE__ */ new Map();
@@ -3435,9 +3504,10 @@ function parseBatchLimit(error) {
3435
3504
  }
3436
3505
  return null;
3437
3506
  }
3438
- async function fetchWithRetry(url, apiKey, body, attempt = 0) {
3507
+ async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0) {
3439
3508
  const controller = new AbortController();
3440
- const timeout = setTimeout(() => controller.abort(), 1e4);
3509
+ const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) ? Math.max(100, Math.min(Math.floor(options.timeoutMs), 6e4)) : 1e4;
3510
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
3441
3511
  let response;
3442
3512
  try {
3443
3513
  response = await fetch(url, {
@@ -3452,7 +3522,7 @@ async function fetchWithRetry(url, apiKey, body, attempt = 0) {
3452
3522
  } catch (err) {
3453
3523
  clearTimeout(timeout);
3454
3524
  if (err instanceof Error && err.name === "AbortError") {
3455
- throw new Error(`Embedding API timeout after 10s: ${url}`);
3525
+ throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
3456
3526
  }
3457
3527
  throw err;
3458
3528
  }
@@ -3460,13 +3530,13 @@ async function fetchWithRetry(url, apiKey, body, attempt = 0) {
3460
3530
  if (response.ok) {
3461
3531
  return response.json();
3462
3532
  }
3463
- if ((response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
3533
+ if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
3464
3534
  const delay = BASE_DELAY_MS * Math.pow(2, attempt);
3465
3535
  const retryAfter = response.headers.get("retry-after");
3466
3536
  const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : delay;
3467
3537
  console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
3468
3538
  await new Promise((resolve) => setTimeout(resolve, waitMs));
3469
- return fetchWithRetry(url, apiKey, body, attempt + 1);
3539
+ return fetchWithRetry(url, apiKey, body, options, attempt + 1);
3470
3540
  }
3471
3541
  const errorText2 = await response.text().catch(() => "unknown error");
3472
3542
  throw new Error(`Embedding API error (${response.status}): ${errorText2}`);
@@ -3522,7 +3592,10 @@ var init_api_provider = __esm({
3522
3592
  }
3523
3593
  } else {
3524
3594
  if (!allowNetworkProbe) return null;
3525
- probeDimensions = await _APIEmbeddingProvider.probeAPI(config);
3595
+ probeDimensions = await _APIEmbeddingProvider.probeAPI(config, {
3596
+ timeoutMs: options.requestTimeoutMs,
3597
+ retry: options.retry
3598
+ });
3526
3599
  console.error(`[memorix] API embedding: ${config.model} @ ${config.baseUrl} (${probeDimensions}d)`);
3527
3600
  saveCachedDims(config, probeDimensions).catch(() => {
3528
3601
  });
@@ -3564,7 +3637,7 @@ var init_api_provider = __esm({
3564
3637
  baseUrl = baseUrl.replace(/\/+$/, "");
3565
3638
  return { apiKey, baseUrl, model, requestedDimensions };
3566
3639
  }
3567
- static async probeAPI(config) {
3640
+ static async probeAPI(config, options) {
3568
3641
  const body = {
3569
3642
  model: config.model,
3570
3643
  input: "dimension probe"
@@ -3575,14 +3648,15 @@ var init_api_provider = __esm({
3575
3648
  const response = await fetchWithRetry(
3576
3649
  `${config.baseUrl}/embeddings`,
3577
3650
  config.apiKey,
3578
- body
3651
+ body,
3652
+ options
3579
3653
  );
3580
3654
  if (response.data.length === 0 || !response.data[0].embedding) {
3581
3655
  throw new Error("API probe returned no embeddings; check model name and API key");
3582
3656
  }
3583
3657
  return response.data[0].embedding.length;
3584
3658
  }
3585
- async embed(text) {
3659
+ async embed(text, options) {
3586
3660
  const normalized = normalizeText(text);
3587
3661
  const hash4 = textHash2(normalized, this.cacheKeyNamespace);
3588
3662
  if (diskCacheLoaded) {
@@ -3600,7 +3674,8 @@ var init_api_provider = __esm({
3600
3674
  const response = await fetchWithRetry(
3601
3675
  `${this.config.baseUrl}/embeddings`,
3602
3676
  this.config.apiKey,
3603
- body
3677
+ body,
3678
+ options
3604
3679
  );
3605
3680
  const embedding2 = response.data[0].embedding;
3606
3681
  if (embedding2.length !== this.dimensions) {
@@ -3634,7 +3709,7 @@ var init_api_provider = __esm({
3634
3709
  scheduleDiskSave();
3635
3710
  return embedding;
3636
3711
  }
3637
- async embedBatch(texts) {
3712
+ async embedBatch(texts, options) {
3638
3713
  await ensureDiskCacheLoaded();
3639
3714
  const normalizedTexts = texts.map(normalizeText);
3640
3715
  const results = new Array(texts.length);
@@ -3668,7 +3743,8 @@ var init_api_provider = __esm({
3668
3743
  const response = await fetchWithRetry(
3669
3744
  `${this.config.baseUrl}/embeddings`,
3670
3745
  this.config.apiKey,
3671
- body
3746
+ body,
3747
+ options
3672
3748
  );
3673
3749
  this.trackUsage(response);
3674
3750
  for (const item of response.data) {
@@ -3845,9 +3921,9 @@ function wrapProvider(candidate) {
3845
3921
  return {
3846
3922
  name: candidate.name,
3847
3923
  dimensions: candidate.dimensions,
3848
- async embed(text) {
3924
+ async embed(text, options) {
3849
3925
  try {
3850
- return await candidate.embed(text);
3926
+ return await candidate.embed(text, options);
3851
3927
  } catch (error) {
3852
3928
  if (kind === "api" && getEmbeddingMode2() === "auto" && isTemporaryEmbeddingFailure(error)) {
3853
3929
  warnOnce("[memorix] API embedding temporarily unavailable \u2014 switching to local fallback provider");
@@ -3855,7 +3931,7 @@ function wrapProvider(candidate) {
3855
3931
  if (fallback) {
3856
3932
  provider = wrapProvider(fallback);
3857
3933
  warnOnce(`[memorix] Embedding fallback activated: ${provider.name} (${provider.dimensions}d)`);
3858
- return provider.embed(text);
3934
+ return provider.embed(text, options);
3859
3935
  }
3860
3936
  }
3861
3937
  if (isTemporaryEmbeddingFailure(error)) {
@@ -3864,9 +3940,9 @@ function wrapProvider(candidate) {
3864
3940
  throw error;
3865
3941
  }
3866
3942
  },
3867
- async embedBatch(texts) {
3943
+ async embedBatch(texts, options) {
3868
3944
  try {
3869
- return await candidate.embedBatch(texts);
3945
+ return await candidate.embedBatch(texts, options);
3870
3946
  } catch (error) {
3871
3947
  if (kind === "api" && getEmbeddingMode2() === "auto" && isTemporaryEmbeddingFailure(error)) {
3872
3948
  warnOnce("[memorix] API embedding temporarily unavailable \u2014 switching to local fallback provider");
@@ -3874,7 +3950,7 @@ function wrapProvider(candidate) {
3874
3950
  if (fallback) {
3875
3951
  provider = wrapProvider(fallback);
3876
3952
  warnOnce(`[memorix] Embedding fallback activated: ${provider.name} (${provider.dimensions}d)`);
3877
- return provider.embedBatch(texts);
3953
+ return provider.embedBatch(texts, options);
3878
3954
  }
3879
3955
  }
3880
3956
  if (isTemporaryEmbeddingFailure(error)) {
@@ -3930,14 +4006,14 @@ async function getEmbeddingProvider(options = {}) {
3930
4006
  return provider;
3931
4007
  }
3932
4008
  if (mode === "api") {
3933
- const initialized = await createAPIProvider();
4009
+ const initialized = await createAPIProvider(options);
3934
4010
  if (!initialized) return null;
3935
4011
  provider = wrapProvider(initialized);
3936
4012
  warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
3937
4013
  return provider;
3938
4014
  }
3939
4015
  if (hasAPIEmbeddingConfig()) {
3940
- const initialized = await createAPIProvider();
4016
+ const initialized = await createAPIProvider(options);
3941
4017
  if (initialized) {
3942
4018
  provider = wrapProvider(initialized);
3943
4019
  warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);