memorix 1.2.9 → 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 (68) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +30 -4
  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 +1945 -579
  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 +24 -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 +5 -3
  23. package/docs/API_REFERENCE.md +20 -1
  24. package/docs/ARCHITECTURE.md +34 -1
  25. package/docs/DEVELOPMENT.md +10 -3
  26. package/docs/INTEGRATIONS.md +1 -1
  27. package/docs/MODULES.md +27 -1
  28. package/docs/README.md +3 -3
  29. package/docs/SETUP.md +6 -4
  30. package/docs/dev-log/progress.txt +22 -0
  31. package/docs/hooks-architecture.md +1 -1
  32. package/llms-full.txt +5 -1
  33. package/llms.txt +2 -0
  34. package/package.json +7 -2
  35. package/plugins/antigravity/memorix/rules/memorix.md +2 -0
  36. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  37. package/plugins/claude/memorix/skills/memorix-memory/SKILL.md +3 -0
  38. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  39. package/plugins/codex/memorix/skills/memorix-memory/SKILL.md +3 -0
  40. package/plugins/copilot/memorix/plugin.json +1 -1
  41. package/plugins/copilot/memorix/skills/memorix-memory/SKILL.md +3 -0
  42. package/plugins/cursor/memorix/skills/memorix-memory/SKILL.md +3 -0
  43. package/plugins/gemini/memorix/GEMINI.md +2 -0
  44. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  45. package/plugins/omp/memorix/package.json +1 -1
  46. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  47. package/plugins/pi/memorix/package.json +1 -1
  48. package/plugins/pi/memorix/skills/memorix-memory/SKILL.md +3 -0
  49. package/server.json +38 -0
  50. package/src/cli/command-guide.ts +9 -1
  51. package/src/cli/commands/agent-integrations.ts +12 -6
  52. package/src/cli/commands/codegraph.ts +1 -0
  53. package/src/cli/commands/memory.ts +217 -1
  54. package/src/cli/commands/setup.ts +200 -23
  55. package/src/codegraph/auto-context.ts +11 -0
  56. package/src/codegraph/context-pack.ts +3 -0
  57. package/src/embedding/api-provider.ts +23 -9
  58. package/src/embedding/provider.ts +21 -10
  59. package/src/hooks/installers/index.ts +2 -0
  60. package/src/knowledge/context-assembly.ts +2 -1
  61. package/src/knowledge/workset.ts +77 -4
  62. package/src/memory/long-term-store.ts +242 -0
  63. package/src/memory/long-term-types.ts +136 -0
  64. package/src/memory/long-term.ts +626 -0
  65. package/src/memory/owner.ts +75 -0
  66. package/src/server.ts +112 -30
  67. package/src/store/sqlite-db.ts +74 -0
  68. package/src/types.ts +20 -0
package/dist/sdk.js CHANGED
@@ -200,15 +200,15 @@ function loadSqlite() {
200
200
  throw new Error("[memorix] SQLite is unavailable (better-sqlite3, node:sqlite, and bun:sqlite failed)");
201
201
  }
202
202
  }
203
- function createDatabase(path26, options) {
203
+ function createDatabase(path27, options) {
204
204
  const Sqlite = loadSqlite();
205
205
  try {
206
- return addCompatibility(instantiateDatabase(Sqlite, path26, options));
206
+ return addCompatibility(instantiateDatabase(Sqlite, path27, options));
207
207
  } catch (error) {
208
208
  if (driver !== "better-sqlite3" || !isNativeBindingFailure(error)) throw error;
209
209
  Database = loadNodeSqlite();
210
210
  driver = "node:sqlite";
211
- return addCompatibility(instantiateDatabase(Database, path26, options));
211
+ return addCompatibility(instantiateDatabase(Database, path27, options));
212
212
  }
213
213
  }
214
214
  var Database, driver, requireFromHere;
@@ -335,7 +335,7 @@ function closeAllDatabases() {
335
335
  _dbCache.delete(key);
336
336
  }
337
337
  }
338
- 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;
338
+ 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;
339
339
  var init_sqlite_db = __esm({
340
340
  "src/store/sqlite-db.ts"() {
341
341
  "use strict";
@@ -761,6 +761,57 @@ CREATE TABLE IF NOT EXISTS knowledge_claim_events (
761
761
  " FOREIGN KEY (workflowId) REFERENCES knowledge_workflows(id) ON DELETE CASCADE",
762
762
  ");"
763
763
  ].join("\n");
764
+ CREATE_LONG_TERM_MEMORIES_TABLE = `
765
+ CREATE TABLE IF NOT EXISTS long_term_memories (
766
+ id TEXT PRIMARY KEY,
767
+ originProjectId TEXT NOT NULL,
768
+ ownerId TEXT NOT NULL,
769
+ scope TEXT NOT NULL,
770
+ kind TEXT NOT NULL,
771
+ state TEXT NOT NULL,
772
+ portability TEXT NOT NULL DEFAULT 'project-bound',
773
+ title TEXT NOT NULL,
774
+ content TEXT NOT NULL,
775
+ factsJson TEXT NOT NULL DEFAULT '[]',
776
+ tagsJson TEXT NOT NULL DEFAULT '[]',
777
+ applicability TEXT,
778
+ origin TEXT NOT NULL,
779
+ createdAt TEXT NOT NULL,
780
+ updatedAt TEXT NOT NULL,
781
+ qualifiedAt TEXT,
782
+ approvedAt TEXT,
783
+ archivedAt TEXT,
784
+ supersededBy TEXT,
785
+ lastValidatedAt TEXT,
786
+ accessCount INTEGER NOT NULL DEFAULT 0,
787
+ lastAccessedAt TEXT
788
+ );
789
+ `;
790
+ CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE = `
791
+ CREATE TABLE IF NOT EXISTS long_term_memory_evidence (
792
+ id TEXT PRIMARY KEY,
793
+ memoryId TEXT NOT NULL,
794
+ kind TEXT NOT NULL,
795
+ referenceId TEXT NOT NULL,
796
+ relation TEXT NOT NULL,
797
+ locator TEXT,
798
+ capturedHash TEXT,
799
+ createdAt TEXT NOT NULL,
800
+ FOREIGN KEY (memoryId) REFERENCES long_term_memories(id) ON DELETE CASCADE
801
+ );
802
+ `;
803
+ CREATE_LONG_TERM_MEMORY_EVENTS_TABLE = `
804
+ CREATE TABLE IF NOT EXISTS long_term_memory_events (
805
+ id TEXT PRIMARY KEY,
806
+ memoryId TEXT NOT NULL,
807
+ kind TEXT NOT NULL,
808
+ fromState TEXT,
809
+ toState TEXT,
810
+ detail TEXT,
811
+ createdAt TEXT NOT NULL,
812
+ FOREIGN KEY (memoryId) REFERENCES long_term_memories(id) ON DELETE CASCADE
813
+ );
814
+ `;
764
815
  CREATE_MAINTENANCE_JOBS_TABLE = `
765
816
  CREATE TABLE IF NOT EXISTS maintenance_jobs (
766
817
  id TEXT PRIMARY KEY,
@@ -856,6 +907,11 @@ CREATE INDEX IF NOT EXISTS idx_knowledge_page_claims_claim ON knowledge_page_cla
856
907
  CREATE INDEX IF NOT EXISTS idx_knowledge_proposals_workspace_status ON knowledge_proposals(workspaceId, status, createdAt DESC);
857
908
  CREATE INDEX IF NOT EXISTS idx_knowledge_workflows_workspace_status ON knowledge_workflows(workspaceId, status, updatedAt DESC);
858
909
  CREATE INDEX IF NOT EXISTS idx_knowledge_workflow_runs_project_workflow ON knowledge_workflow_runs(projectId, workflowId, startedAt DESC);
910
+ CREATE INDEX IF NOT EXISTS idx_long_term_memories_owner_state ON long_term_memories(ownerId, state, updatedAt DESC);
911
+ CREATE INDEX IF NOT EXISTS idx_long_term_memories_project_state ON long_term_memories(originProjectId, state, updatedAt DESC);
912
+ CREATE INDEX IF NOT EXISTS idx_long_term_memories_scope_portability ON long_term_memories(scope, portability, state, updatedAt DESC);
913
+ CREATE INDEX IF NOT EXISTS idx_long_term_memory_evidence_memory ON long_term_memory_evidence(memoryId, createdAt);
914
+ CREATE INDEX IF NOT EXISTS idx_long_term_memory_events_memory ON long_term_memory_events(memoryId, createdAt);
859
915
  CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_ready ON maintenance_jobs(status, run_after);
860
916
  CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_project ON maintenance_jobs(project_id, status, run_after);
861
917
  CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_targets(updated_at);
@@ -944,6 +1000,19 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
944
1000
  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)");
945
1001
  db2.exec("CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key)");
946
1002
  }
1003
+ },
1004
+ {
1005
+ id: "1.3-long-term-memory",
1006
+ apply: (db2) => {
1007
+ db2.exec(CREATE_LONG_TERM_MEMORIES_TABLE);
1008
+ db2.exec(CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE);
1009
+ db2.exec(CREATE_LONG_TERM_MEMORY_EVENTS_TABLE);
1010
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_owner_state ON long_term_memories(ownerId, state, updatedAt DESC)");
1011
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_project_state ON long_term_memories(originProjectId, state, updatedAt DESC)");
1012
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_scope_portability ON long_term_memories(scope, portability, state, updatedAt DESC)");
1013
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memory_evidence_memory ON long_term_memory_evidence(memoryId, createdAt)");
1014
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memory_events_memory ON long_term_memory_events(memoryId, createdAt)");
1015
+ }
947
1016
  }
948
1017
  ];
949
1018
  _dbCache = /* @__PURE__ */ new Map();
@@ -2732,9 +2801,9 @@ function parseTomlConfig(content, filePath) {
2732
2801
  }
2733
2802
  return root;
2734
2803
  }
2735
- function ensureTable(root, path26) {
2804
+ function ensureTable(root, path27) {
2736
2805
  let cursor = root;
2737
- for (const part of path26) {
2806
+ for (const part of path27) {
2738
2807
  const value = cursor[part];
2739
2808
  if (!value || typeof value !== "object" || Array.isArray(value)) {
2740
2809
  cursor[part] = {};
@@ -3610,9 +3679,10 @@ function parseBatchLimit(error) {
3610
3679
  }
3611
3680
  return null;
3612
3681
  }
3613
- async function fetchWithRetry(url, apiKey, body, attempt = 0) {
3682
+ async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0) {
3614
3683
  const controller = new AbortController();
3615
- const timeout = setTimeout(() => controller.abort(), 1e4);
3684
+ const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) ? Math.max(100, Math.min(Math.floor(options.timeoutMs), 6e4)) : 1e4;
3685
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
3616
3686
  let response;
3617
3687
  try {
3618
3688
  response = await fetch(url, {
@@ -3627,7 +3697,7 @@ async function fetchWithRetry(url, apiKey, body, attempt = 0) {
3627
3697
  } catch (err) {
3628
3698
  clearTimeout(timeout);
3629
3699
  if (err instanceof Error && err.name === "AbortError") {
3630
- throw new Error(`Embedding API timeout after 10s: ${url}`);
3700
+ throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
3631
3701
  }
3632
3702
  throw err;
3633
3703
  }
@@ -3635,13 +3705,13 @@ async function fetchWithRetry(url, apiKey, body, attempt = 0) {
3635
3705
  if (response.ok) {
3636
3706
  return response.json();
3637
3707
  }
3638
- if ((response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
3708
+ if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
3639
3709
  const delay = BASE_DELAY_MS * Math.pow(2, attempt);
3640
3710
  const retryAfter = response.headers.get("retry-after");
3641
3711
  const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : delay;
3642
3712
  console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
3643
3713
  await new Promise((resolve2) => setTimeout(resolve2, waitMs));
3644
- return fetchWithRetry(url, apiKey, body, attempt + 1);
3714
+ return fetchWithRetry(url, apiKey, body, options, attempt + 1);
3645
3715
  }
3646
3716
  const errorText2 = await response.text().catch(() => "unknown error");
3647
3717
  throw new Error(`Embedding API error (${response.status}): ${errorText2}`);
@@ -3697,7 +3767,10 @@ var init_api_provider = __esm({
3697
3767
  }
3698
3768
  } else {
3699
3769
  if (!allowNetworkProbe) return null;
3700
- probeDimensions = await _APIEmbeddingProvider.probeAPI(config);
3770
+ probeDimensions = await _APIEmbeddingProvider.probeAPI(config, {
3771
+ timeoutMs: options.requestTimeoutMs,
3772
+ retry: options.retry
3773
+ });
3701
3774
  console.error(`[memorix] API embedding: ${config.model} @ ${config.baseUrl} (${probeDimensions}d)`);
3702
3775
  saveCachedDims(config, probeDimensions).catch(() => {
3703
3776
  });
@@ -3739,7 +3812,7 @@ var init_api_provider = __esm({
3739
3812
  baseUrl = baseUrl.replace(/\/+$/, "");
3740
3813
  return { apiKey, baseUrl, model, requestedDimensions };
3741
3814
  }
3742
- static async probeAPI(config) {
3815
+ static async probeAPI(config, options) {
3743
3816
  const body = {
3744
3817
  model: config.model,
3745
3818
  input: "dimension probe"
@@ -3750,14 +3823,15 @@ var init_api_provider = __esm({
3750
3823
  const response = await fetchWithRetry(
3751
3824
  `${config.baseUrl}/embeddings`,
3752
3825
  config.apiKey,
3753
- body
3826
+ body,
3827
+ options
3754
3828
  );
3755
3829
  if (response.data.length === 0 || !response.data[0].embedding) {
3756
3830
  throw new Error("API probe returned no embeddings; check model name and API key");
3757
3831
  }
3758
3832
  return response.data[0].embedding.length;
3759
3833
  }
3760
- async embed(text) {
3834
+ async embed(text, options) {
3761
3835
  const normalized = normalizeText(text);
3762
3836
  const hash4 = textHash2(normalized, this.cacheKeyNamespace);
3763
3837
  if (diskCacheLoaded) {
@@ -3775,7 +3849,8 @@ var init_api_provider = __esm({
3775
3849
  const response = await fetchWithRetry(
3776
3850
  `${this.config.baseUrl}/embeddings`,
3777
3851
  this.config.apiKey,
3778
- body
3852
+ body,
3853
+ options
3779
3854
  );
3780
3855
  const embedding2 = response.data[0].embedding;
3781
3856
  if (embedding2.length !== this.dimensions) {
@@ -3809,7 +3884,7 @@ var init_api_provider = __esm({
3809
3884
  scheduleDiskSave();
3810
3885
  return embedding;
3811
3886
  }
3812
- async embedBatch(texts) {
3887
+ async embedBatch(texts, options) {
3813
3888
  await ensureDiskCacheLoaded();
3814
3889
  const normalizedTexts = texts.map(normalizeText);
3815
3890
  const results = new Array(texts.length);
@@ -3843,7 +3918,8 @@ var init_api_provider = __esm({
3843
3918
  const response = await fetchWithRetry(
3844
3919
  `${this.config.baseUrl}/embeddings`,
3845
3920
  this.config.apiKey,
3846
- body
3921
+ body,
3922
+ options
3847
3923
  );
3848
3924
  this.trackUsage(response);
3849
3925
  for (const item of response.data) {
@@ -4020,9 +4096,9 @@ function wrapProvider(candidate) {
4020
4096
  return {
4021
4097
  name: candidate.name,
4022
4098
  dimensions: candidate.dimensions,
4023
- async embed(text) {
4099
+ async embed(text, options) {
4024
4100
  try {
4025
- return await candidate.embed(text);
4101
+ return await candidate.embed(text, options);
4026
4102
  } catch (error) {
4027
4103
  if (kind === "api" && getEmbeddingMode2() === "auto" && isTemporaryEmbeddingFailure(error)) {
4028
4104
  warnOnce("[memorix] API embedding temporarily unavailable \u2014 switching to local fallback provider");
@@ -4030,7 +4106,7 @@ function wrapProvider(candidate) {
4030
4106
  if (fallback) {
4031
4107
  provider = wrapProvider(fallback);
4032
4108
  warnOnce(`[memorix] Embedding fallback activated: ${provider.name} (${provider.dimensions}d)`);
4033
- return provider.embed(text);
4109
+ return provider.embed(text, options);
4034
4110
  }
4035
4111
  }
4036
4112
  if (isTemporaryEmbeddingFailure(error)) {
@@ -4039,9 +4115,9 @@ function wrapProvider(candidate) {
4039
4115
  throw error;
4040
4116
  }
4041
4117
  },
4042
- async embedBatch(texts) {
4118
+ async embedBatch(texts, options) {
4043
4119
  try {
4044
- return await candidate.embedBatch(texts);
4120
+ return await candidate.embedBatch(texts, options);
4045
4121
  } catch (error) {
4046
4122
  if (kind === "api" && getEmbeddingMode2() === "auto" && isTemporaryEmbeddingFailure(error)) {
4047
4123
  warnOnce("[memorix] API embedding temporarily unavailable \u2014 switching to local fallback provider");
@@ -4049,7 +4125,7 @@ function wrapProvider(candidate) {
4049
4125
  if (fallback) {
4050
4126
  provider = wrapProvider(fallback);
4051
4127
  warnOnce(`[memorix] Embedding fallback activated: ${provider.name} (${provider.dimensions}d)`);
4052
- return provider.embedBatch(texts);
4128
+ return provider.embedBatch(texts, options);
4053
4129
  }
4054
4130
  }
4055
4131
  if (isTemporaryEmbeddingFailure(error)) {
@@ -4105,14 +4181,14 @@ async function getEmbeddingProvider(options = {}) {
4105
4181
  return provider;
4106
4182
  }
4107
4183
  if (mode === "api") {
4108
- const initialized = await createAPIProvider();
4184
+ const initialized = await createAPIProvider(options);
4109
4185
  if (!initialized) return null;
4110
4186
  provider = wrapProvider(initialized);
4111
4187
  warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
4112
4188
  return provider;
4113
4189
  }
4114
4190
  if (hasAPIEmbeddingConfig()) {
4115
- const initialized = await createAPIProvider();
4191
+ const initialized = await createAPIProvider(options);
4116
4192
  if (initialized) {
4117
4193
  provider = wrapProvider(initialized);
4118
4194
  warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
@@ -8968,12 +9044,12 @@ import { createHash as createHash4 } from "crypto";
8968
9044
  function digest(input) {
8969
9045
  return createHash4("sha256").update(input).digest("hex").slice(0, 16);
8970
9046
  }
8971
- function normalizeCodePath(path26) {
8972
- return path26.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
9047
+ function normalizeCodePath(path27) {
9048
+ return path27.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
8973
9049
  }
8974
- function makeCodeFileId(projectId, path26) {
9050
+ function makeCodeFileId(projectId, path27) {
8975
9051
  return `file:${digest(`${projectId}
8976
- ${normalizeCodePath(path26)}`)}`;
9052
+ ${normalizeCodePath(path27)}`)}`;
8977
9053
  }
8978
9054
  function makeCodeSymbolId(input) {
8979
9055
  return `symbol:${digest([
@@ -9447,8 +9523,8 @@ var init_store = __esm({
9447
9523
  });
9448
9524
  tx();
9449
9525
  }
9450
- getFile(projectId, path26) {
9451
- const row = this.db.prepare(`SELECT * FROM code_files WHERE projectId = ? AND path = ?`).get(projectId, normalizeCodePath(path26));
9526
+ getFile(projectId, path27) {
9527
+ const row = this.db.prepare(`SELECT * FROM code_files WHERE projectId = ? AND path = ?`).get(projectId, normalizeCodePath(path27));
9452
9528
  return row ? rowToFile(row) : null;
9453
9529
  }
9454
9530
  listFiles(projectId) {
@@ -9683,30 +9759,30 @@ function normalizeCodeGraphExcludePatterns(exclude) {
9683
9759
  ...(exclude ?? []).map((pattern) => pattern.trim()).filter(Boolean)
9684
9760
  ])];
9685
9761
  }
9686
- function isCodeGraphExcludedPath(path26, exclude) {
9687
- const normalized = normalizeCodePath2(path26);
9762
+ function isCodeGraphExcludedPath(path27, exclude) {
9763
+ const normalized = normalizeCodePath2(path27);
9688
9764
  return normalizeCodeGraphExcludePatterns(exclude).some((pattern) => matchesPattern(normalized, normalizeCodePath2(pattern)));
9689
9765
  }
9690
- function normalizeCodePath2(path26) {
9691
- return path26.replace(/\\/g, "/").replace(/^\.\/+/, "");
9766
+ function normalizeCodePath2(path27) {
9767
+ return path27.replace(/\\/g, "/").replace(/^\.\/+/, "");
9692
9768
  }
9693
- function matchesPattern(path26, pattern) {
9769
+ function matchesPattern(path27, pattern) {
9694
9770
  if (pattern.endsWith("/**")) {
9695
9771
  const base = pattern.slice(0, -3);
9696
9772
  if (base.startsWith("**/")) {
9697
9773
  const suffix = base.slice(3);
9698
- return path26 === suffix || path26.endsWith(`/${suffix}`) || path26.includes(`/${suffix}/`);
9774
+ return path27 === suffix || path27.endsWith(`/${suffix}`) || path27.includes(`/${suffix}/`);
9699
9775
  }
9700
9776
  if (!base.includes("/")) {
9701
- return path26 === base || path26.startsWith(`${base}/`) || path26.includes(`/${base}/`);
9777
+ return path27 === base || path27.startsWith(`${base}/`) || path27.includes(`/${base}/`);
9702
9778
  }
9703
- return path26 === base || path26.startsWith(`${base}/`);
9779
+ return path27 === base || path27.startsWith(`${base}/`);
9704
9780
  }
9705
9781
  if (pattern.startsWith("**/")) {
9706
9782
  const suffix = pattern.slice(3);
9707
- return path26 === suffix || path26.endsWith(`/${suffix}`);
9783
+ return path27 === suffix || path27.endsWith(`/${suffix}`);
9708
9784
  }
9709
- return path26 === pattern || path26.startsWith(`${pattern}/`);
9785
+ return path27 === pattern || path27.startsWith(`${pattern}/`);
9710
9786
  }
9711
9787
  var DEFAULT_CODEGRAPH_EXCLUDES;
9712
9788
  var init_exclude = __esm({
@@ -9751,12 +9827,12 @@ import { join as join4, relative } from "path";
9751
9827
  function hashText(text) {
9752
9828
  return createHash6("sha256").update(text).digest("hex");
9753
9829
  }
9754
- function extension(path26) {
9755
- const index = path26.lastIndexOf(".");
9756
- return index === -1 ? "" : path26.slice(index);
9830
+ function extension(path27) {
9831
+ const index = path27.lastIndexOf(".");
9832
+ return index === -1 ? "" : path27.slice(index);
9757
9833
  }
9758
- function languageForPath(path26) {
9759
- const ext = extension(path26);
9834
+ function languageForPath(path27) {
9835
+ const ext = extension(path27);
9760
9836
  return LANGUAGE_BY_EXTENSION.get(ext) ?? "unknown";
9761
9837
  }
9762
9838
  function resolveMaxFileBytes(value) {
@@ -10237,7 +10313,7 @@ function resolveObservationCodeRefs(obs, lookup) {
10237
10313
  }
10238
10314
  function createStoreLookup(store, projectId) {
10239
10315
  return {
10240
- findFile: (path26) => store.getFile(projectId, path26) ?? void 0,
10316
+ findFile: (path27) => store.getFile(projectId, path27) ?? void 0,
10241
10317
  findSymbols: (names, hintedFileIds) => store.findSymbolsByNames(projectId, names, hintedFileIds)
10242
10318
  };
10243
10319
  }
@@ -10250,7 +10326,7 @@ function createSnapshotLookup(files, symbols) {
10250
10326
  symbolsByName.set(symbol.name, group);
10251
10327
  }
10252
10328
  return {
10253
- findFile: (path26) => filesByPath.get(normalizeCodePath(path26)),
10329
+ findFile: (path27) => filesByPath.get(normalizeCodePath(path27)),
10254
10330
  findSymbols: (names, hintedFileIds) => {
10255
10331
  const hinted = new Set(hintedFileIds);
10256
10332
  const found = [];
@@ -11867,8 +11943,8 @@ __export(task_lens_exports, {
11867
11943
  scoreLensSource: () => scoreLensSource,
11868
11944
  shouldShowLensSource: () => shouldShowLensSource
11869
11945
  });
11870
- function normalizePath2(path26) {
11871
- return path26.replace(/\\/g, "/");
11946
+ function normalizePath2(path27) {
11947
+ return path27.replace(/\\/g, "/");
11872
11948
  }
11873
11949
  function tokenize2(text) {
11874
11950
  return (text.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
@@ -11930,8 +12006,8 @@ function isContinuationTask(task) {
11930
12006
  (keyword) => containsTaskKeyword(normalized, keyword)
11931
12007
  );
11932
12008
  }
11933
- function pathKindScore(path26, lens) {
11934
- const normalized = normalizePath2(path26).toLowerCase();
12009
+ function pathKindScore(path27, lens) {
12010
+ const normalized = normalizePath2(path27).toLowerCase();
11935
12011
  const name = normalized.split("/").pop() ?? normalized;
11936
12012
  const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
11937
12013
  const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
@@ -11979,8 +12055,8 @@ function sourceTaskMatchScore(source, task) {
11979
12055
  if (matches === 1) return 24;
11980
12056
  return 0;
11981
12057
  }
11982
- function fallbackPathRank(path26) {
11983
- const normalized = normalizePath2(path26);
12058
+ function fallbackPathRank(path27) {
12059
+ const normalized = normalizePath2(path27);
11984
12060
  if (normalized.startsWith("src/")) return 0;
11985
12061
  if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
11986
12062
  if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
@@ -11989,11 +12065,11 @@ function fallbackPathRank(path26) {
11989
12065
  }
11990
12066
  function rankLensPaths(paths, lens, task) {
11991
12067
  const tokens = tokenize2(task ?? "");
11992
- return [...new Set(paths.map(normalizePath2))].map((path26, index) => ({
11993
- path: path26,
12068
+ return [...new Set(paths.map(normalizePath2))].map((path27, index) => ({
12069
+ path: path27,
11994
12070
  index,
11995
- score: pathKindScore(path26, lens) + tokenScore(path26, tokens),
11996
- fallback: fallbackPathRank(path26)
12071
+ score: pathKindScore(path27, lens) + tokenScore(path27, tokens),
12072
+ fallback: fallbackPathRank(path27)
11997
12073
  })).sort((a, b) => b.score - a.score || a.fallback - b.fallback || a.index - b.index || a.path.localeCompare(b.path)).map((item) => item.path);
11998
12074
  }
11999
12075
  function lensPathCandidates(lens) {
@@ -16245,6 +16321,827 @@ var init_session = __esm({
16245
16321
  }
16246
16322
  });
16247
16323
 
16324
+ // src/memory/owner.ts
16325
+ var owner_exports = {};
16326
+ __export(owner_exports, {
16327
+ resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
16328
+ });
16329
+ import { randomUUID as randomUUID8 } from "crypto";
16330
+ import { promises as fs15 } from "fs";
16331
+ import path19 from "path";
16332
+ function ownerPath(dataDir) {
16333
+ const resolved = path19.resolve(dataDir);
16334
+ const home = path19.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path19.dirname(resolved) : resolved;
16335
+ return path19.join(home, "memorix-user.json");
16336
+ }
16337
+ function normalizedOwnerId(value) {
16338
+ const trimmed = value?.trim();
16339
+ return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
16340
+ }
16341
+ async function readOwnerFile(filePath) {
16342
+ try {
16343
+ const value = JSON.parse(await fs15.readFile(filePath, "utf8"));
16344
+ const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
16345
+ return id ? { id, source: "local-file" } : void 0;
16346
+ } catch {
16347
+ return void 0;
16348
+ }
16349
+ }
16350
+ async function resolveLocalMemoryOwner(dataDir, options = {}) {
16351
+ const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
16352
+ if (configured) return { id: configured, source: "environment" };
16353
+ const filePath = ownerPath(dataDir);
16354
+ const existing = await readOwnerFile(filePath);
16355
+ if (existing || !options.create) return existing;
16356
+ await fs15.mkdir(path19.dirname(filePath), { recursive: true });
16357
+ const created = {
16358
+ version: 1,
16359
+ id: "local-" + randomUUID8(),
16360
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
16361
+ };
16362
+ try {
16363
+ await fs15.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
16364
+ return { id: created.id, source: "local-file" };
16365
+ } catch (error) {
16366
+ if (error?.code !== "EEXIST") throw error;
16367
+ const concurrent = await readOwnerFile(filePath);
16368
+ if (concurrent) return concurrent;
16369
+ throw new Error("Memorix could not establish a valid local memory owner identity.");
16370
+ }
16371
+ }
16372
+ var ID_PATTERN;
16373
+ var init_owner = __esm({
16374
+ "src/memory/owner.ts"() {
16375
+ "use strict";
16376
+ init_esm_shims();
16377
+ ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
16378
+ }
16379
+ });
16380
+
16381
+ // src/memory/long-term-store.ts
16382
+ import { randomUUID as randomUUID9 } from "crypto";
16383
+ function optionalText5(value) {
16384
+ return typeof value === "string" && value.trim() ? value : void 0;
16385
+ }
16386
+ function arrayValue2(value) {
16387
+ if (typeof value !== "string" || !value) return [];
16388
+ try {
16389
+ const parsed = JSON.parse(value);
16390
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
16391
+ } catch {
16392
+ return [];
16393
+ }
16394
+ }
16395
+ function rowToMemory(row) {
16396
+ return {
16397
+ id: row.id,
16398
+ originProjectId: row.originProjectId,
16399
+ ownerId: row.ownerId,
16400
+ scope: row.scope,
16401
+ kind: row.kind,
16402
+ state: row.state,
16403
+ portability: row.portability,
16404
+ title: row.title,
16405
+ content: row.content,
16406
+ facts: arrayValue2(row.factsJson),
16407
+ tags: arrayValue2(row.tagsJson),
16408
+ ...optionalText5(row.applicability) ? { applicability: row.applicability } : {},
16409
+ origin: row.origin,
16410
+ createdAt: row.createdAt,
16411
+ updatedAt: row.updatedAt,
16412
+ ...optionalText5(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
16413
+ ...optionalText5(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
16414
+ ...optionalText5(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
16415
+ ...optionalText5(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
16416
+ ...optionalText5(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
16417
+ accessCount: Number(row.accessCount ?? 0),
16418
+ ...optionalText5(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
16419
+ };
16420
+ }
16421
+ function rowToEvidence2(row) {
16422
+ return {
16423
+ id: row.id,
16424
+ memoryId: row.memoryId,
16425
+ kind: row.kind,
16426
+ referenceId: row.referenceId,
16427
+ relation: row.relation,
16428
+ ...optionalText5(row.locator) ? { locator: row.locator } : {},
16429
+ ...optionalText5(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
16430
+ createdAt: row.createdAt
16431
+ };
16432
+ }
16433
+ function rowToEvent2(row) {
16434
+ return {
16435
+ id: row.id,
16436
+ memoryId: row.memoryId,
16437
+ kind: row.kind,
16438
+ ...optionalText5(row.fromState) ? { fromState: row.fromState } : {},
16439
+ ...optionalText5(row.toState) ? { toState: row.toState } : {},
16440
+ ...optionalText5(row.detail) ? { detail: row.detail } : {},
16441
+ createdAt: row.createdAt
16442
+ };
16443
+ }
16444
+ var LongTermMemoryStore;
16445
+ var init_long_term_store = __esm({
16446
+ "src/memory/long-term-store.ts"() {
16447
+ "use strict";
16448
+ init_esm_shims();
16449
+ init_sqlite_db();
16450
+ LongTermMemoryStore = class {
16451
+ db = null;
16452
+ async init(dataDir) {
16453
+ this.db = getDatabase(dataDir);
16454
+ }
16455
+ transaction(fn) {
16456
+ return this.db.transaction(fn)();
16457
+ }
16458
+ get(id) {
16459
+ const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
16460
+ return row ? rowToMemory(row) : void 0;
16461
+ }
16462
+ list(options = {}) {
16463
+ const limit = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
16464
+ const clauses = [];
16465
+ const params = [];
16466
+ if (options.ownerId) {
16467
+ clauses.push("ownerId = ?");
16468
+ params.push(options.ownerId);
16469
+ }
16470
+ if (options.originProjectId) {
16471
+ clauses.push("originProjectId = ?");
16472
+ params.push(options.originProjectId);
16473
+ }
16474
+ const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
16475
+ const rows = this.db.prepare(
16476
+ "SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
16477
+ ).all(...params, limit);
16478
+ return rows.map(rowToMemory);
16479
+ }
16480
+ insert(memory) {
16481
+ this.db.prepare(`
16482
+ INSERT INTO long_term_memories (
16483
+ id, originProjectId, ownerId, scope, kind, state, portability,
16484
+ title, content, factsJson, tagsJson, applicability, origin,
16485
+ createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
16486
+ supersededBy, lastValidatedAt, accessCount, lastAccessedAt
16487
+ ) VALUES (
16488
+ @id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
16489
+ @title, @content, @factsJson, @tagsJson, @applicability, @origin,
16490
+ @createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
16491
+ @supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
16492
+ )
16493
+ `).run({
16494
+ ...memory,
16495
+ factsJson: JSON.stringify(memory.facts),
16496
+ tagsJson: JSON.stringify(memory.tags),
16497
+ applicability: memory.applicability ?? null,
16498
+ qualifiedAt: memory.qualifiedAt ?? null,
16499
+ approvedAt: memory.approvedAt ?? null,
16500
+ archivedAt: memory.archivedAt ?? null,
16501
+ supersededBy: memory.supersededBy ?? null,
16502
+ lastValidatedAt: memory.lastValidatedAt ?? null,
16503
+ lastAccessedAt: memory.lastAccessedAt ?? null
16504
+ });
16505
+ }
16506
+ update(memory) {
16507
+ this.db.prepare(`
16508
+ UPDATE long_term_memories SET
16509
+ scope = @scope,
16510
+ kind = @kind,
16511
+ state = @state,
16512
+ portability = @portability,
16513
+ title = @title,
16514
+ content = @content,
16515
+ factsJson = @factsJson,
16516
+ tagsJson = @tagsJson,
16517
+ applicability = @applicability,
16518
+ updatedAt = @updatedAt,
16519
+ qualifiedAt = @qualifiedAt,
16520
+ approvedAt = @approvedAt,
16521
+ archivedAt = @archivedAt,
16522
+ supersededBy = @supersededBy,
16523
+ lastValidatedAt = @lastValidatedAt,
16524
+ accessCount = @accessCount,
16525
+ lastAccessedAt = @lastAccessedAt
16526
+ WHERE id = @id
16527
+ `).run({
16528
+ ...memory,
16529
+ factsJson: JSON.stringify(memory.facts),
16530
+ tagsJson: JSON.stringify(memory.tags),
16531
+ applicability: memory.applicability ?? null,
16532
+ qualifiedAt: memory.qualifiedAt ?? null,
16533
+ approvedAt: memory.approvedAt ?? null,
16534
+ archivedAt: memory.archivedAt ?? null,
16535
+ supersededBy: memory.supersededBy ?? null,
16536
+ lastValidatedAt: memory.lastValidatedAt ?? null,
16537
+ lastAccessedAt: memory.lastAccessedAt ?? null
16538
+ });
16539
+ }
16540
+ insertEvidence(input) {
16541
+ const evidence = {
16542
+ id: randomUUID9(),
16543
+ ...input,
16544
+ createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
16545
+ };
16546
+ this.db.prepare(`
16547
+ INSERT INTO long_term_memory_evidence (
16548
+ id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
16549
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
16550
+ `).run(
16551
+ evidence.id,
16552
+ evidence.memoryId,
16553
+ evidence.kind,
16554
+ evidence.referenceId,
16555
+ evidence.relation,
16556
+ evidence.locator ?? null,
16557
+ evidence.capturedHash ?? null,
16558
+ evidence.createdAt
16559
+ );
16560
+ return evidence;
16561
+ }
16562
+ listEvidence(memoryId) {
16563
+ return this.db.prepare(`
16564
+ SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
16565
+ `).all(memoryId).map(rowToEvidence2);
16566
+ }
16567
+ recordEvent(input) {
16568
+ const event = {
16569
+ id: randomUUID9(),
16570
+ ...input,
16571
+ createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
16572
+ };
16573
+ this.db.prepare(`
16574
+ INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
16575
+ VALUES (?, ?, ?, ?, ?, ?, ?)
16576
+ `).run(
16577
+ event.id,
16578
+ event.memoryId,
16579
+ event.kind,
16580
+ event.fromState ?? null,
16581
+ event.toState ?? null,
16582
+ event.detail ?? null,
16583
+ event.createdAt
16584
+ );
16585
+ return event;
16586
+ }
16587
+ listEvents(memoryId) {
16588
+ return this.db.prepare(`
16589
+ SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
16590
+ `).all(memoryId).map(rowToEvent2);
16591
+ }
16592
+ markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
16593
+ const unique2 = [...new Set(ids.filter(Boolean))];
16594
+ if (unique2.length === 0) return;
16595
+ const statement = this.db.prepare(`
16596
+ UPDATE long_term_memories
16597
+ SET accessCount = accessCount + 1, lastAccessedAt = ?
16598
+ WHERE id = ?
16599
+ `);
16600
+ this.db.transaction(() => {
16601
+ for (const id of unique2) statement.run(at, id);
16602
+ })();
16603
+ }
16604
+ };
16605
+ }
16606
+ });
16607
+
16608
+ // src/memory/long-term-types.ts
16609
+ var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
16610
+ var init_long_term_types = __esm({
16611
+ "src/memory/long-term-types.ts"() {
16612
+ "use strict";
16613
+ init_esm_shims();
16614
+ LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
16615
+ LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
16616
+ LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
16617
+ LONG_TERM_MEMORY_EVIDENCE_KINDS = [
16618
+ "observation",
16619
+ "claim",
16620
+ "workflow",
16621
+ "session",
16622
+ "checkpoint",
16623
+ "git",
16624
+ "code",
16625
+ "test",
16626
+ "run",
16627
+ "document",
16628
+ "manual",
16629
+ "user"
16630
+ ];
16631
+ LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
16632
+ "supports",
16633
+ "verifies",
16634
+ "derives",
16635
+ "user-confirmed"
16636
+ ];
16637
+ }
16638
+ });
16639
+
16640
+ // src/memory/long-term.ts
16641
+ var long_term_exports = {};
16642
+ __export(long_term_exports, {
16643
+ approveLongTermMemory: () => approveLongTermMemory,
16644
+ archiveLongTermMemory: () => archiveLongTermMemory,
16645
+ canReadLongTermMemory: () => canReadLongTermMemory,
16646
+ createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
16647
+ createManualLongTermMemory: () => createManualLongTermMemory,
16648
+ getLongTermMemoryDetail: () => getLongTermMemoryDetail,
16649
+ isEligibleLongTermMemory: () => isEligibleLongTermMemory,
16650
+ listLongTermMemories: () => listLongTermMemories,
16651
+ promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
16652
+ qualifyLongTermMemory: () => qualifyLongTermMemory,
16653
+ renderLongTermMemorySummary: () => renderLongTermMemorySummary,
16654
+ selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
16655
+ supersedeLongTermMemory: () => supersedeLongTermMemory
16656
+ });
16657
+ import { randomUUID as randomUUID10 } from "crypto";
16658
+ function isOneOf(value, values) {
16659
+ return typeof value === "string" && values.includes(value);
16660
+ }
16661
+ function compactText2(value, field, max = MAX_TEXT_LENGTH) {
16662
+ if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
16663
+ const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
16664
+ if (!safe) throw new Error("Long-term memory " + field + " is required.");
16665
+ if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
16666
+ return safe;
16667
+ }
16668
+ function compactList(values, field) {
16669
+ if (!values) return [];
16670
+ if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
16671
+ return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText2(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
16672
+ }
16673
+ function normalizeEvidence(input) {
16674
+ if (!Array.isArray(input) || input.length === 0) {
16675
+ throw new Error("A long-term memory requires at least one source evidence reference.");
16676
+ }
16677
+ return input.map((evidence) => {
16678
+ if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
16679
+ throw new Error("Long-term memory evidence kind is invalid.");
16680
+ }
16681
+ if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
16682
+ throw new Error("Long-term memory evidence relation is invalid.");
16683
+ }
16684
+ return {
16685
+ kind: evidence.kind,
16686
+ referenceId: compactText2(evidence.referenceId, "evidence reference", 1e3),
16687
+ relation: evidence.relation,
16688
+ ...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator", 1e3) } : {},
16689
+ ...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash", 1e3) } : {}
16690
+ };
16691
+ });
16692
+ }
16693
+ function assertScope(scope) {
16694
+ if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
16695
+ }
16696
+ function assertKind(kind) {
16697
+ if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
16698
+ }
16699
+ function assertPortability(value) {
16700
+ if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
16701
+ throw new Error("Long-term memory portability is invalid.");
16702
+ }
16703
+ }
16704
+ function assertPortableEvidence(scope, portability, evidence) {
16705
+ if (portability !== "portable") return;
16706
+ if (scope !== "user") {
16707
+ throw new Error("Only user-scoped long-term memories may be portable across projects.");
16708
+ }
16709
+ if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
16710
+ throw new Error("Portable user memory may only use manual or user-confirmed evidence; project code, Git, observation, test, session, claim, and workflow evidence must remain project-bound.");
16711
+ }
16712
+ }
16713
+ function requireTeamReader(scope, reader) {
16714
+ if (scope === "team" && reader?.isTeamMember !== true) {
16715
+ throw new Error("Team long-term memory requires an explicit active team identity for this project.");
16716
+ }
16717
+ }
16718
+ function matchesScope(memory, reader) {
16719
+ if (memory.scope === "project") return memory.originProjectId === reader.projectId;
16720
+ if (memory.scope === "team") {
16721
+ return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
16722
+ }
16723
+ if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
16724
+ return memory.portability === "portable" || memory.originProjectId === reader.projectId;
16725
+ }
16726
+ function canReadLongTermMemory(memory, reader) {
16727
+ return matchesScope(memory, reader);
16728
+ }
16729
+ function isEligibleLongTermMemory(memory) {
16730
+ return memory.state === "qualified" || memory.state === "approved";
16731
+ }
16732
+ function taskTerms(task) {
16733
+ const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
16734
+ return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
16735
+ }
16736
+ function kindFitScore(kind, task) {
16737
+ const normalized = task.toLocaleLowerCase("en-US");
16738
+ const hints = {
16739
+ episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
16740
+ semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
16741
+ procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
16742
+ };
16743
+ return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
16744
+ }
16745
+ function validationFreshnessScore(memory) {
16746
+ const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
16747
+ if (!validatedAt) return 0;
16748
+ const time = Date.parse(validatedAt);
16749
+ if (!Number.isFinite(time)) return 0;
16750
+ const ageDays = Math.max(0, Date.now() - time) / (24 * 60 * 60 * 1e3);
16751
+ if (ageDays <= 30) return 0.35;
16752
+ if (ageDays <= 90) return 0.15;
16753
+ return 0;
16754
+ }
16755
+ function scoreForTask(memory, task) {
16756
+ const terms = taskTerms(task);
16757
+ if (terms.length === 0) return void 0;
16758
+ const title = memory.title.toLocaleLowerCase("en-US");
16759
+ const content = memory.content.toLocaleLowerCase("en-US");
16760
+ const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
16761
+ const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
16762
+ const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
16763
+ let score = memory.state === "approved" ? 0.5 : 0;
16764
+ const matched = [];
16765
+ for (const term of terms) {
16766
+ let termScore = 0;
16767
+ if (title.includes(term)) termScore += 5;
16768
+ if (tags.includes(term)) termScore += 4;
16769
+ if (applicability.includes(term)) termScore += 3;
16770
+ if (facts.includes(term)) termScore += 2;
16771
+ if (content.includes(term)) termScore += 1;
16772
+ if (termScore > 0) {
16773
+ score += termScore;
16774
+ matched.push(term);
16775
+ }
16776
+ }
16777
+ if (matched.length === 0) return void 0;
16778
+ score += kindFitScore(memory.kind, task);
16779
+ score += validationFreshnessScore(memory);
16780
+ const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
16781
+ const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
16782
+ return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
16783
+ }
16784
+ function semanticText(memory) {
16785
+ return sanitizeCredentials([
16786
+ memory.title,
16787
+ memory.applicability,
16788
+ memory.tags.join(" "),
16789
+ memory.facts.join(" "),
16790
+ memory.content
16791
+ ].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
16792
+ }
16793
+ function cosineSimilarity(left, right) {
16794
+ if (left.length === 0 || left.length !== right.length) return 0;
16795
+ let dot = 0;
16796
+ let leftMagnitude = 0;
16797
+ let rightMagnitude = 0;
16798
+ for (let index = 0; index < left.length; index += 1) {
16799
+ const a = left[index];
16800
+ const b = right[index];
16801
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return 0;
16802
+ dot += a * b;
16803
+ leftMagnitude += a * a;
16804
+ rightMagnitude += b * b;
16805
+ }
16806
+ if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
16807
+ return dot / Math.sqrt(leftMagnitude * rightMagnitude);
16808
+ }
16809
+ function semanticSelection(memory, similarity, task) {
16810
+ if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
16811
+ const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
16812
+ const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
16813
+ return {
16814
+ score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
16815
+ reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
16816
+ };
16817
+ }
16818
+ async function resolveSemanticProvider(explicit) {
16819
+ if (explicit !== void 0) return explicit;
16820
+ try {
16821
+ const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
16822
+ return await withTimeout(
16823
+ getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
16824
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS,
16825
+ "Long-term semantic provider initialization"
16826
+ );
16827
+ } catch {
16828
+ return null;
16829
+ }
16830
+ }
16831
+ function createStore(dataDir) {
16832
+ const store = new LongTermMemoryStore();
16833
+ return store.init(dataDir).then(() => store);
16834
+ }
16835
+ async function createManualLongTermMemory(input) {
16836
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
16837
+ if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
16838
+ assertScope(input.scope);
16839
+ const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID10(), relation: "supports" }];
16840
+ return createLongTermMemoryWithDataDir({
16841
+ dataDir: input.dataDir,
16842
+ originProjectId: input.projectId,
16843
+ ownerId: owner.id,
16844
+ scope: input.scope,
16845
+ kind: input.kind,
16846
+ portability: input.portability,
16847
+ title: input.title,
16848
+ content: input.content,
16849
+ facts: input.facts,
16850
+ tags: input.tags,
16851
+ applicability: input.applicability,
16852
+ origin: "manual",
16853
+ evidence,
16854
+ reader: input.reader
16855
+ });
16856
+ }
16857
+ async function promoteObservationToLongTermMemory(input) {
16858
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
16859
+ if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
16860
+ return createLongTermMemoryWithDataDir({
16861
+ dataDir: input.dataDir,
16862
+ originProjectId: input.observation.projectId,
16863
+ ownerId: owner.id,
16864
+ scope: input.scope,
16865
+ kind: input.kind,
16866
+ portability: "project-bound",
16867
+ title: input.observation.title,
16868
+ content: input.observation.narrative,
16869
+ facts: input.observation.facts,
16870
+ tags: input.tags ?? input.observation.concepts,
16871
+ applicability: input.applicability,
16872
+ origin: input.observation.source === "git" ? "git" : "agent",
16873
+ evidence: [{
16874
+ kind: "observation",
16875
+ referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
16876
+ relation: "derives"
16877
+ }],
16878
+ reader: input.reader
16879
+ });
16880
+ }
16881
+ async function createLongTermMemoryCandidate(input) {
16882
+ return createLongTermMemoryWithDataDir(input);
16883
+ }
16884
+ async function createLongTermMemoryWithDataDir(input) {
16885
+ assertScope(input.scope);
16886
+ assertKind(input.kind);
16887
+ const portability = input.portability ?? "project-bound";
16888
+ assertPortability(portability);
16889
+ requireTeamReader(input.scope, input.reader);
16890
+ const evidenceInputs = normalizeEvidence(input.evidence);
16891
+ assertPortableEvidence(input.scope, portability, evidenceInputs);
16892
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
16893
+ const memory = {
16894
+ id: randomUUID10(),
16895
+ originProjectId: compactText2(input.originProjectId, "origin project id", 1e3),
16896
+ ownerId: compactText2(input.ownerId, "owner id", 160),
16897
+ scope: input.scope,
16898
+ kind: input.kind,
16899
+ state: "candidate",
16900
+ portability,
16901
+ title: compactText2(input.title, "title", 500),
16902
+ content: compactText2(input.content, "content"),
16903
+ facts: compactList(input.facts, "facts"),
16904
+ tags: compactList(input.tags, "tags"),
16905
+ ...input.applicability ? { applicability: compactText2(input.applicability, "applicability", 1e3) } : {},
16906
+ origin: input.origin ?? "manual",
16907
+ createdAt: now3,
16908
+ updatedAt: now3,
16909
+ accessCount: 0
16910
+ };
16911
+ const store = await createStore(input.dataDir);
16912
+ const evidence = store.transaction(() => {
16913
+ store.insert(memory);
16914
+ const written = evidenceInputs.map((item) => store.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
16915
+ store.recordEvent({
16916
+ memoryId: memory.id,
16917
+ kind: "created",
16918
+ toState: "candidate",
16919
+ detail: "Created as a source-backed long-term memory candidate.",
16920
+ createdAt: now3
16921
+ });
16922
+ return written;
16923
+ });
16924
+ return { memory, evidence };
16925
+ }
16926
+ function transitionDetail(value) {
16927
+ return compactText2(value, "transition reason", 1e3);
16928
+ }
16929
+ async function transitionMemory(input) {
16930
+ const store = await createStore(input.dataDir);
16931
+ return store.transaction(() => {
16932
+ const current = store.get(input.id);
16933
+ if (!current) throw new Error("Long-term memory was not found.");
16934
+ if (current.state !== input.expected) {
16935
+ throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
16936
+ }
16937
+ const evidence = store.listEvidence(current.id);
16938
+ if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
16939
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
16940
+ const next = {
16941
+ ...current,
16942
+ state: input.next,
16943
+ updatedAt: now3,
16944
+ lastValidatedAt: now3,
16945
+ ...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
16946
+ };
16947
+ store.update(next);
16948
+ store.recordEvent({
16949
+ memoryId: next.id,
16950
+ kind: input.event,
16951
+ fromState: current.state,
16952
+ toState: next.state,
16953
+ detail: transitionDetail(input.reason),
16954
+ createdAt: now3
16955
+ });
16956
+ return next;
16957
+ });
16958
+ }
16959
+ async function qualifyLongTermMemory(input) {
16960
+ return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
16961
+ }
16962
+ async function approveLongTermMemory(input) {
16963
+ return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
16964
+ }
16965
+ async function archiveLongTermMemory(input) {
16966
+ const store = await createStore(input.dataDir);
16967
+ return store.transaction(() => {
16968
+ const current = store.get(input.id);
16969
+ if (!current) throw new Error("Long-term memory was not found.");
16970
+ if (current.state === "archived" || current.state === "superseded") {
16971
+ throw new Error("Long-term memory is already inactive.");
16972
+ }
16973
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
16974
+ const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
16975
+ store.update(next);
16976
+ store.recordEvent({
16977
+ memoryId: next.id,
16978
+ kind: "archived",
16979
+ fromState: current.state,
16980
+ toState: "archived",
16981
+ detail: transitionDetail(input.reason),
16982
+ createdAt: now3
16983
+ });
16984
+ return next;
16985
+ });
16986
+ }
16987
+ async function supersedeLongTermMemory(input) {
16988
+ const store = await createStore(input.dataDir);
16989
+ return store.transaction(() => {
16990
+ const current = store.get(input.id);
16991
+ if (!current) throw new Error("Long-term memory was not found.");
16992
+ if (current.state === "archived" || current.state === "superseded") {
16993
+ throw new Error("Long-term memory is already inactive.");
16994
+ }
16995
+ if (current.id === input.supersededBy) {
16996
+ throw new Error("Long-term memory cannot supersede itself.");
16997
+ }
16998
+ const replacement = store.get(input.supersededBy);
16999
+ if (!replacement || !isEligibleLongTermMemory(replacement)) {
17000
+ throw new Error("Replacement long-term memory must be qualified or approved.");
17001
+ }
17002
+ if (replacement.scope !== current.scope) {
17003
+ throw new Error("Replacement long-term memory must use the same scope.");
17004
+ }
17005
+ if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
17006
+ throw new Error("User long-term memory may only be superseded by the same local owner.");
17007
+ }
17008
+ if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
17009
+ throw new Error("Project and team long-term memory may only be superseded within the same project.");
17010
+ }
17011
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
17012
+ const next = {
17013
+ ...current,
17014
+ state: "superseded",
17015
+ supersededBy: replacement.id,
17016
+ updatedAt: now3
17017
+ };
17018
+ store.update(next);
17019
+ store.recordEvent({
17020
+ memoryId: next.id,
17021
+ kind: "superseded",
17022
+ fromState: current.state,
17023
+ toState: "superseded",
17024
+ detail: transitionDetail(input.reason),
17025
+ createdAt: now3
17026
+ });
17027
+ return next;
17028
+ });
17029
+ }
17030
+ async function listLongTermMemories(input) {
17031
+ const store = await createStore(input.dataDir);
17032
+ return store.list({ limit: input.limit ?? 100 }).filter((memory) => canReadLongTermMemory(memory, input.reader)).filter((memory) => input.includeInactive || memory.state !== "archived" && memory.state !== "superseded").map((memory) => ({ memory, evidence: store.listEvidence(memory.id) }));
17033
+ }
17034
+ async function getLongTermMemoryDetail(input) {
17035
+ const store = await createStore(input.dataDir);
17036
+ const memory = store.get(input.id);
17037
+ if (!memory || !canReadLongTermMemory(memory, input.reader)) {
17038
+ throw new Error("Long-term memory was not found in the active scope.");
17039
+ }
17040
+ store.markAccess([memory.id]);
17041
+ return { memory, evidence: store.listEvidence(memory.id), events: store.listEvents(memory.id) };
17042
+ }
17043
+ async function selectLongTermMemoriesForTask(input) {
17044
+ const task = input.task?.trim();
17045
+ if (!task) return [];
17046
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
17047
+ const reader = {
17048
+ projectId: input.projectId,
17049
+ ...owner ? { ownerId: owner.id } : {},
17050
+ ...input.agentId ? { agentId: input.agentId } : {},
17051
+ ...input.isTeamMember ? { isTeamMember: true } : {}
17052
+ };
17053
+ const store = await createStore(input.dataDir);
17054
+ const eligible = store.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
17055
+ let selected = eligible.map((memory) => {
17056
+ const ranked = scoreForTask(memory, task);
17057
+ return ranked ? {
17058
+ memory,
17059
+ evidence: store.listEvidence(memory.id),
17060
+ score: ranked.score,
17061
+ reason: ranked.reason
17062
+ } : void 0;
17063
+ }).filter((item) => Boolean(item)).sort((left, right) => right.score - left.score || right.memory.updatedAt.localeCompare(left.memory.updatedAt) || left.memory.id.localeCompare(right.memory.id));
17064
+ if (selected.length === 0 && eligible.length > 0) {
17065
+ const provider2 = await resolveSemanticProvider(input.embeddingProvider);
17066
+ const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
17067
+ if (provider2 && candidates.length > 0) {
17068
+ try {
17069
+ const vectors = await withTimeout(
17070
+ provider2.embedBatch([task, ...candidates.map(semanticText)], {
17071
+ timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
17072
+ retry: false
17073
+ }),
17074
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS,
17075
+ "Long-term semantic retrieval"
17076
+ );
17077
+ const query = vectors[0];
17078
+ if (Array.isArray(query)) {
17079
+ selected = candidates.map((memory, index) => {
17080
+ const vector = vectors[index + 1];
17081
+ const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
17082
+ return ranked ? {
17083
+ memory,
17084
+ evidence: store.listEvidence(memory.id),
17085
+ score: ranked.score,
17086
+ reason: ranked.reason
17087
+ } : void 0;
17088
+ }).filter((item) => Boolean(item)).sort((left, right) => right.score - left.score || right.memory.updatedAt.localeCompare(left.memory.updatedAt) || left.memory.id.localeCompare(right.memory.id));
17089
+ }
17090
+ } catch {
17091
+ }
17092
+ }
17093
+ }
17094
+ selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
17095
+ store.markAccess(selected.map((item) => item.memory.id));
17096
+ return selected;
17097
+ }
17098
+ function renderLongTermMemorySummary(memory, maxTokens = 22) {
17099
+ const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
17100
+ const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
17101
+ return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
17102
+ }
17103
+ var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
17104
+ var init_long_term = __esm({
17105
+ "src/memory/long-term.ts"() {
17106
+ "use strict";
17107
+ init_esm_shims();
17108
+ init_token_budget();
17109
+ init_timeout();
17110
+ init_secret_filter();
17111
+ init_owner();
17112
+ init_long_term_store();
17113
+ init_long_term_types();
17114
+ MAX_TEXT_LENGTH = 12e3;
17115
+ MAX_LIST_ITEMS = 40;
17116
+ MAX_SEMANTIC_CANDIDATES = 48;
17117
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
17118
+ MIN_SEMANTIC_SIMILARITY = 0.58;
17119
+ PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
17120
+ STOP_TERMS = /* @__PURE__ */ new Set([
17121
+ "the",
17122
+ "and",
17123
+ "for",
17124
+ "with",
17125
+ "from",
17126
+ "that",
17127
+ "this",
17128
+ "into",
17129
+ "when",
17130
+ "what",
17131
+ "\u5982\u4F55",
17132
+ "\u4EC0\u4E48",
17133
+ "\u8FD9\u4E2A",
17134
+ "\u90A3\u4E2A",
17135
+ "\u6211\u4EEC",
17136
+ "\u9879\u76EE",
17137
+ "\u8FDB\u884C",
17138
+ "\u9700\u8981",
17139
+ "\u7EE7\u7EED",
17140
+ "\u5904\u7406"
17141
+ ]);
17142
+ }
17143
+ });
17144
+
16248
17145
  // src/knowledge/workset.ts
16249
17146
  function unique(values) {
16250
17147
  return [...new Set(values.filter(Boolean))];
@@ -16348,6 +17245,7 @@ function receiptOmissionKind(raw) {
16348
17245
  if (raw.includes("state")) return "code-state";
16349
17246
  if (raw.includes("semantic")) return "semantic-code";
16350
17247
  if (raw.includes("start")) return "start-here";
17248
+ if (raw.includes("durable-memory")) return "durable-memory";
16351
17249
  if (raw.includes("memory")) return "memory";
16352
17250
  if (raw.includes("claim")) return "claim";
16353
17251
  if (raw.includes("knowledge-page")) return "knowledge-page";
@@ -16606,6 +17504,26 @@ function renderTaskWorksetPrompt(input) {
16606
17504
  });
16607
17505
  }
16608
17506
  }
17507
+ if (input.durableMemory.length > 0) {
17508
+ appendLine(lines, "", maxTokens, omitted, "durable-memory-heading");
17509
+ appendLine(lines, "Durable memory", maxTokens, omitted, "durable-memory-heading");
17510
+ for (const memory of input.durableMemory.slice(0, 3)) {
17511
+ appendLine(
17512
+ lines,
17513
+ "- " + memory.kind + " (" + memory.scope + ", " + memory.state + "; ref durable:" + memory.id + "): " + memory.summary,
17514
+ maxTokens,
17515
+ omitted,
17516
+ "durable-memory",
17517
+ selected,
17518
+ {
17519
+ kind: "durable-memory",
17520
+ id: "durable:" + memory.id,
17521
+ reason: memory.reason,
17522
+ trust: "source-backed"
17523
+ }
17524
+ );
17525
+ }
17526
+ }
16609
17527
  if (input.workflows.length > 0) {
16610
17528
  appendLine(lines, "", maxTokens, omitted, "workflow-heading");
16611
17529
  appendLine(lines, "Project workflow", maxTokens, omitted, "workflow-heading");
@@ -16675,6 +17593,32 @@ async function buildTaskWorkset(input) {
16675
17593
  evidenceRefs: evidenceByClaim.get(claim.id).map((item) => item.id),
16676
17594
  reason: selection.reasons[claim.id] ?? "source-qualified task match"
16677
17595
  }));
17596
+ let durableMemory = [];
17597
+ let durableEvidence = [];
17598
+ if (task) {
17599
+ try {
17600
+ const selected = await selectLongTermMemoriesForTask({
17601
+ dataDir: input.dataDir,
17602
+ projectId: input.projectId,
17603
+ task,
17604
+ ...input.reader?.agentId ? { agentId: input.reader.agentId } : {},
17605
+ ...input.reader?.isTeamMember ? { isTeamMember: true } : {},
17606
+ limit: 3
17607
+ });
17608
+ durableMemory = selected.map((item) => ({
17609
+ id: item.memory.id,
17610
+ kind: item.memory.kind,
17611
+ scope: item.memory.scope,
17612
+ state: item.memory.state,
17613
+ title: item.memory.title,
17614
+ summary: renderLongTermMemorySummary(item.memory, 20),
17615
+ evidenceRefs: item.evidence.map((evidence) => evidence.id),
17616
+ reason: item.reason
17617
+ }));
17618
+ durableEvidence = selected.flatMap((item) => item.evidence);
17619
+ } catch {
17620
+ }
17621
+ }
16678
17622
  let workspace;
16679
17623
  let pages = [];
16680
17624
  let workflows = [];
@@ -16714,7 +17658,10 @@ async function buildTaskWorkset(input) {
16714
17658
  const evidenceIds = unique(selection.claims.flatMap((claim) => evidenceIdsForClaim(
16715
17659
  claim,
16716
17660
  evidenceByClaim.get(claim.id) ?? []
16717
- )));
17661
+ )).concat(
17662
+ durableMemory.map((memory) => "durable:" + memory.id),
17663
+ durableEvidence.map((evidence) => "durable-evidence:" + evidence.id)
17664
+ ));
16718
17665
  const verification = unique([
16719
17666
  ...workflows.flatMap((workflow) => workflow.verificationGates),
16720
17667
  ...input.verificationHints
@@ -16740,7 +17687,7 @@ async function buildTaskWorkset(input) {
16740
17687
  } : {}
16741
17688
  } : void 0;
16742
17689
  const base = {
16743
- version: "1.2",
17690
+ version: "1.3",
16744
17691
  task,
16745
17692
  lens: input.lens,
16746
17693
  currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
@@ -16753,6 +17700,7 @@ async function buildTaskWorkset(input) {
16753
17700
  hiddenCautionMemoryCount: input.hiddenCautionMemoryCount ?? 0,
16754
17701
  claims,
16755
17702
  pages,
17703
+ durableMemory,
16756
17704
  workflows,
16757
17705
  cautions: normalizedCautions,
16758
17706
  verification,
@@ -16769,7 +17717,7 @@ async function buildTaskWorkset(input) {
16769
17717
  budget: { maxTokens }
16770
17718
  });
16771
17719
  const receipt = {
16772
- version: "1.2.4",
17720
+ version: "1.3",
16773
17721
  target: input.deliveryTarget ?? "project-context",
16774
17722
  elapsedMs: Math.max(0, Date.now() - startedAt),
16775
17723
  budget: {
@@ -16804,6 +17752,7 @@ var init_workset = __esm({
16804
17752
  init_workspace();
16805
17753
  init_workflow_store();
16806
17754
  init_workflows();
17755
+ init_long_term();
16807
17756
  CONTINUATION_DETAIL_TOKEN_BUDGET = 20;
16808
17757
  }
16809
17758
  });
@@ -16816,7 +17765,7 @@ __export(current_facts_exports, {
16816
17765
  });
16817
17766
  import { execFileSync } from "child_process";
16818
17767
  import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
16819
- import path19 from "path";
17768
+ import path20 from "path";
16820
17769
  function readTextIfExists(filePath) {
16821
17770
  try {
16822
17771
  if (!existsSync12(filePath)) return null;
@@ -16826,7 +17775,7 @@ function readTextIfExists(filePath) {
16826
17775
  }
16827
17776
  }
16828
17777
  function readPackageVersion(rootPath) {
16829
- const text = readTextIfExists(path19.join(rootPath, "package.json"));
17778
+ const text = readTextIfExists(path20.join(rootPath, "package.json"));
16830
17779
  if (!text) return void 0;
16831
17780
  try {
16832
17781
  const parsed = JSON.parse(text);
@@ -16836,7 +17785,7 @@ function readPackageVersion(rootPath) {
16836
17785
  }
16837
17786
  }
16838
17787
  function readLatestChangelog(rootPath) {
16839
- const text = readTextIfExists(path19.join(rootPath, "CHANGELOG.md"));
17788
+ const text = readTextIfExists(path20.join(rootPath, "CHANGELOG.md"));
16840
17789
  if (!text) return void 0;
16841
17790
  const match = text.match(/^##\s+\[?v?([^\]\s]+)\]?\s*(?:-\s*(\d{4}-\d{2}-\d{2}))?/m);
16842
17791
  if (!match) return void 0;
@@ -16906,7 +17855,7 @@ function detectStaleProgressNotes(input) {
16906
17855
  ];
16907
17856
  const notes = [];
16908
17857
  for (const relPath of candidates) {
16909
- const content = readTextIfExists(path19.join(input.rootPath, relPath));
17858
+ const content = readTextIfExists(path20.join(input.rootPath, relPath));
16910
17859
  if (!content) continue;
16911
17860
  const parsed = parseProgressNote(content);
16912
17861
  const olderThanChangelog = compareDateStrings(parsed.lastUpdated, input.latestChangelog?.date);
@@ -17157,9 +18106,9 @@ async function inspectExternalCodeGraph(input) {
17157
18106
  return { health, quality: providerQuality({ mode, health }) };
17158
18107
  }
17159
18108
  function safeRelativePath(projectRoot, value, exclude) {
17160
- const path26 = safeText(value, 1024);
17161
- if (!path26 || isAbsolute(path26)) return void 0;
17162
- const absolute = resolve(projectRoot, path26);
18109
+ const path27 = safeText(value, 1024);
18110
+ if (!path27 || isAbsolute(path27)) return void 0;
18111
+ const absolute = resolve(projectRoot, path27);
17163
18112
  const fromRoot = relative2(projectRoot, absolute);
17164
18113
  if (!fromRoot || fromRoot === ".." || fromRoot.startsWith("..\\") || fromRoot.startsWith("../") || isAbsolute(fromRoot)) return void 0;
17165
18114
  const normalized = normalizeCodePath(fromRoot);
@@ -17434,15 +18383,15 @@ function countLanguages(files) {
17434
18383
  }
17435
18384
  return [...counts.entries()].map(([language, files2]) => ({ language, files: files2 })).sort((a, b) => a.language.localeCompare(b.language));
17436
18385
  }
17437
- function suggestedReadRank(path26) {
17438
- const normalized = path26.replace(/\\/g, "/");
18386
+ function suggestedReadRank(path27) {
18387
+ const normalized = path27.replace(/\\/g, "/");
17439
18388
  if (normalized.startsWith("src/")) return 0;
17440
18389
  if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 0;
17441
18390
  if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
17442
18391
  return 3;
17443
18392
  }
17444
18393
  function compactSuggestedReads(paths, limit = 8, exclude) {
17445
- return uniq(paths).filter((path26) => !isCodeGraphExcludedPath(path26, exclude)).sort((a, b) => suggestedReadRank(a) - suggestedReadRank(b)).slice(0, limit);
18394
+ return uniq(paths).filter((path27) => !isCodeGraphExcludedPath(path27, exclude)).sort((a, b) => suggestedReadRank(a) - suggestedReadRank(b)).slice(0, limit);
17446
18395
  }
17447
18396
  function collectGraph(store, projectId, observations2, exclude) {
17448
18397
  const files = store.listFiles(projectId);
@@ -17482,14 +18431,14 @@ function collectGraph(store, projectId, observations2, exclude) {
17482
18431
  }
17483
18432
  if (files.length === 0) {
17484
18433
  for (const observation of observations2) {
17485
- for (const path26 of observation.filesModified ?? []) {
17486
- if (!path26 || isCodeGraphExcludedPath(path26, exclude)) continue;
17487
- suggestedReads.push(path26);
18434
+ for (const path27 of observation.filesModified ?? []) {
18435
+ if (!path27 || isCodeGraphExcludedPath(path27, exclude)) continue;
18436
+ suggestedReads.push(path27);
17488
18437
  sources.push({
17489
18438
  observationId: observation.id,
17490
18439
  title: observation.title,
17491
18440
  type: observation.type,
17492
- path: path26,
18441
+ path: path27,
17493
18442
  status: "unbound",
17494
18443
  reason: "Recorded file hint; Code Memory refresh is pending."
17495
18444
  });
@@ -17558,8 +18507,8 @@ var compaction_checkpoint_store_exports = {};
17558
18507
  __export(compaction_checkpoint_store_exports, {
17559
18508
  CompactionCheckpointStore: () => CompactionCheckpointStore
17560
18509
  });
17561
- import { createHash as createHash13, randomUUID as randomUUID8 } from "crypto";
17562
- function optionalText5(value) {
18510
+ import { createHash as createHash13, randomUUID as randomUUID11 } from "crypto";
18511
+ function optionalText6(value) {
17563
18512
  return typeof value === "string" && value ? value : void 0;
17564
18513
  }
17565
18514
  function optionalNumber2(value) {
@@ -17613,15 +18562,15 @@ function rowToCheckpoint(row) {
17613
18562
  reason: row.reason,
17614
18563
  sourceEvent: row.source_event,
17615
18564
  sourceKey: row.source_key,
17616
- ...optionalText5(row.summary) ? { summary: row.summary } : {},
18565
+ ...optionalText6(row.summary) ? { summary: row.summary } : {},
17617
18566
  ...optionalNumber2(row.tokens_before) !== void 0 ? { tokensBefore: Number(row.tokens_before) } : {},
17618
- ...optionalText5(row.first_kept_entry_id) ? { firstKeptEntryId: row.first_kept_entry_id } : {},
18567
+ ...optionalText6(row.first_kept_entry_id) ? { firstKeptEntryId: row.first_kept_entry_id } : {},
17619
18568
  ...parseDetails(row.details_json) ? { details: parseDetails(row.details_json) } : {},
17620
18569
  transcriptAvailable: Boolean(row.transcript_available),
17621
18570
  status: row.status,
17622
18571
  preCapturedAt: row.pre_captured_at,
17623
- ...optionalText5(row.completed_at) ? { completedAt: row.completed_at } : {},
17624
- ...optionalText5(row.delivered_at) ? { deliveredAt: row.delivered_at } : {},
18572
+ ...optionalText6(row.completed_at) ? { completedAt: row.completed_at } : {},
18573
+ ...optionalText6(row.delivered_at) ? { deliveredAt: row.delivered_at } : {},
17625
18574
  deliveryCount: Number(row.delivery_count ?? 0),
17626
18575
  createdAt: row.created_at,
17627
18576
  updatedAt: row.updated_at
@@ -17659,7 +18608,7 @@ var init_compaction_checkpoint_store = __esm({
17659
18608
  if (pending) return rowToCheckpoint(pending);
17660
18609
  const now3 = input.capturedAt ?? (/* @__PURE__ */ new Date()).toISOString();
17661
18610
  const checkpoint = {
17662
- id: randomUUID8(),
18611
+ id: randomUUID11(),
17663
18612
  projectId: input.projectId,
17664
18613
  sessionId: input.sessionId,
17665
18614
  agent: input.agent,
@@ -17667,7 +18616,7 @@ var init_compaction_checkpoint_store = __esm({
17667
18616
  captureKind: "preflight",
17668
18617
  reason: safeReason(input.reason),
17669
18618
  sourceEvent: input.sourceEvent,
17670
- sourceKey: `pre:${randomUUID8()}`,
18619
+ sourceKey: `pre:${randomUUID11()}`,
17671
18620
  transcriptAvailable: Boolean(input.transcriptAvailable),
17672
18621
  status: "active",
17673
18622
  preCapturedAt: now3,
@@ -17733,7 +18682,7 @@ var init_compaction_checkpoint_store = __esm({
17733
18682
  return this.get(checkpoint2.id);
17734
18683
  }
17735
18684
  const checkpoint = {
17736
- id: randomUUID8(),
18685
+ id: randomUUID11(),
17737
18686
  projectId: input.projectId,
17738
18687
  sessionId: input.sessionId,
17739
18688
  agent: input.agent,
@@ -17868,7 +18817,7 @@ __export(auto_context_exports, {
17868
18817
  formatLegacyAutoProjectContextPrompt: () => formatLegacyAutoProjectContextPrompt
17869
18818
  });
17870
18819
  import { existsSync as existsSync14, statSync as statSync3 } from "fs";
17871
- import path20 from "path";
18820
+ import path21 from "path";
17872
18821
  function activeProjectObservations(observations2, projectId) {
17873
18822
  return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
17874
18823
  }
@@ -18103,6 +19052,7 @@ async function buildAutoProjectContext(input) {
18103
19052
  stale: overview.freshness.stale
18104
19053
  },
18105
19054
  runtimeCautions,
19055
+ ...input.reader ? { reader: input.reader } : {},
18106
19056
  ...input.deliveryTarget ? { deliveryTarget: input.deliveryTarget } : {}
18107
19057
  });
18108
19058
  return {
@@ -18147,7 +19097,7 @@ function dedupeSourcesByObservation(sources) {
18147
19097
  function existingLensCandidates(rootPath, lens) {
18148
19098
  const out = [];
18149
19099
  for (const candidate of lensPathCandidates(lens)) {
18150
- const absolute = path20.join(rootPath, candidate);
19100
+ const absolute = path21.join(rootPath, candidate);
18151
19101
  try {
18152
19102
  if (!existsSync14(absolute)) continue;
18153
19103
  const stat = statSync3(absolute);
@@ -18257,7 +19207,7 @@ function formatAutoProjectContextSummary(context) {
18257
19207
  "Start here"
18258
19208
  ].filter(Boolean);
18259
19209
  if (startHere.length > 0) {
18260
- startHere.forEach((path26, index) => lines.push(`${index + 1}. ${path26}`));
19210
+ startHere.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
18261
19211
  } else {
18262
19212
  lines.push("- no code-bound reads yet; inspect the task-relevant files directly");
18263
19213
  }
@@ -18266,6 +19216,14 @@ function formatAutoProjectContextSummary(context) {
18266
19216
  "Reliable memory",
18267
19217
  reliableSources.length > 0 ? `- ${reliableSources.length} current code-bound memory link(s)` : "- none yet"
18268
19218
  );
19219
+ if (context.workset.durableMemory.length > 0) {
19220
+ lines.push("", "Durable memory");
19221
+ for (const memory of context.workset.durableMemory.slice(0, 3)) {
19222
+ lines.push(
19223
+ "- " + memory.kind + " (" + memory.scope + ", " + memory.state + "): " + compactContinuationText(memory.summary, 28)
19224
+ );
19225
+ }
19226
+ }
18269
19227
  const continuation = context.workset.continuation;
18270
19228
  if (continuation?.previousSession || continuation?.memories.length || continuation?.compactCheckpoint) {
18271
19229
  lines.push("", "Resume from prior work");
@@ -18315,7 +19273,7 @@ function formatLegacyAutoProjectContextPrompt(context) {
18315
19273
  if (startHere.length === 0) {
18316
19274
  lines.push("- no code-bound reads yet; inspect the task-relevant code directly");
18317
19275
  } else {
18318
- startHere.forEach((path26, index) => lines.push(`${index + 1}. ${path26}`));
19276
+ startHere.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
18319
19277
  }
18320
19278
  const { reliableSources, cautionSources, hiddenReliableCount, hiddenCautionCount } = lensSourceSets(context);
18321
19279
  lines.push("", "Reliable memory");
@@ -18566,6 +19524,7 @@ async function attachTaskWorkset(input) {
18566
19524
  stale: input.pack.warnings.filter((warning) => warning.status === "stale").length
18567
19525
  },
18568
19526
  runtimeCautions: input.runtimeCautions,
19527
+ ...input.reader ? { reader: input.reader } : {},
18569
19528
  deliveryTarget: "context-pack"
18570
19529
  });
18571
19530
  return { ...input.pack, workset };
@@ -18576,7 +19535,7 @@ function buildContextPackPrompt(pack) {
18576
19535
  const unboundMemories = pack.memories.filter((memory) => memory.status === "unbound");
18577
19536
  const lines = ["## Task", pack.task, "", "## Reliable Memories"];
18578
19537
  const visibleCodeFacts = pack.codeFacts.filter((fact) => !isCodeGraphExcludedPath(fact.path)).slice(0, 5);
18579
- const visibleSuggestedReads = pack.suggestedReads.filter((path26) => !isCodeGraphExcludedPath(path26)).slice(0, 5);
19538
+ const visibleSuggestedReads = pack.suggestedReads.filter((path27) => !isCodeGraphExcludedPath(path27)).slice(0, 5);
18580
19539
  if (reliableMemories.length === 0) lines.push("- none");
18581
19540
  for (const memory of reliableMemories) {
18582
19541
  lines.push(`- #${memory.id} ${memory.status}: [${memory.type}] ${memory.title} (${memory.reason})`);
@@ -18600,7 +19559,7 @@ function buildContextPackPrompt(pack) {
18600
19559
  }
18601
19560
  lines.push("", "## Suggested Next Reads");
18602
19561
  if (visibleSuggestedReads.length === 0) lines.push("- none");
18603
- visibleSuggestedReads.forEach((path26, index) => lines.push(`${index + 1}. ${path26}`));
19562
+ visibleSuggestedReads.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
18604
19563
  lines.push("", "## Suggested Verification");
18605
19564
  if (pack.suggestedVerification.length === 0) lines.push("- none");
18606
19565
  for (const command of pack.suggestedVerification) lines.push(`- ${command}`);
@@ -18992,9 +19951,9 @@ __export(team_store_exports, {
18992
19951
  isTeamStoreInitialized: () => isTeamStoreInitialized,
18993
19952
  resetTeamStore: () => resetTeamStore
18994
19953
  });
18995
- import { randomUUID as randomUUID9 } from "crypto";
18996
- import path21 from "path";
18997
- import fs15 from "fs";
19954
+ import { randomUUID as randomUUID12 } from "crypto";
19955
+ import path22 from "path";
19956
+ import fs16 from "fs";
18998
19957
  function getTeamStore() {
18999
19958
  if (!_teamStore) {
19000
19959
  throw new Error("[memorix] TeamStore not initialized \u2014 call initTeamStore() first");
@@ -19281,7 +20240,7 @@ var init_team_store = __esm({
19281
20240
  * reactivate it (preserving agent_id). Otherwise create a new one.
19282
20241
  */
19283
20242
  registerAgent(input) {
19284
- const instanceId = input.instanceId || randomUUID9();
20243
+ const instanceId = input.instanceId || randomUUID12();
19285
20244
  const now3 = Date.now();
19286
20245
  const existing = this.stmtAgentFindByInstance.get(
19287
20246
  input.projectId,
@@ -19307,7 +20266,7 @@ var init_team_store = __esm({
19307
20266
  this.eventBus?.emit("agent:joined", { agentId: row2.agent_id, projectId: input.projectId, agentType: input.agentType });
19308
20267
  return row2;
19309
20268
  }
19310
- const agentId = randomUUID9();
20269
+ const agentId = randomUUID12();
19311
20270
  this.stmtAgentUpsert.run({
19312
20271
  agent_id: agentId,
19313
20272
  project_id: input.projectId,
@@ -19409,7 +20368,7 @@ var init_team_store = __esm({
19409
20368
  return { error: `Recipient agent '${input.recipientAgentId}' not found \u2014 cannot send to unknown agent` };
19410
20369
  }
19411
20370
  }
19412
- const id = randomUUID9();
20371
+ const id = randomUUID12();
19413
20372
  const now3 = Date.now();
19414
20373
  const row = {
19415
20374
  id,
@@ -19459,7 +20418,7 @@ var init_team_store = __esm({
19459
20418
  // Task Operations (with atomic claim semantics)
19460
20419
  // ═══════════════════════════════════════════════════════════════════
19461
20420
  createTask(input) {
19462
- const taskId = randomUUID9();
20421
+ const taskId = randomUUID12();
19463
20422
  const now3 = Date.now();
19464
20423
  const row = {
19465
20424
  task_id: taskId,
@@ -19684,19 +20643,19 @@ var init_team_store = __esm({
19684
20643
  const agentCount = this.db.prepare(`SELECT COUNT(*) AS cnt FROM team_agents`).get();
19685
20644
  if (agentCount.cnt > 0) return;
19686
20645
  const candidates = [
19687
- path21.join(this.dataDir, "team-state.json"),
19688
- path21.join(this.dataDir, "..", "team-state.json")
20646
+ path22.join(this.dataDir, "team-state.json"),
20647
+ path22.join(this.dataDir, "..", "team-state.json")
19689
20648
  ];
19690
20649
  let jsonPath = null;
19691
20650
  for (const p of candidates) {
19692
- if (fs15.existsSync(p)) {
20651
+ if (fs16.existsSync(p)) {
19693
20652
  jsonPath = p;
19694
20653
  break;
19695
20654
  }
19696
20655
  }
19697
20656
  if (!jsonPath) return;
19698
20657
  try {
19699
- const raw = fs15.readFileSync(jsonPath, "utf-8");
20658
+ const raw = fs16.readFileSync(jsonPath, "utf-8");
19700
20659
  const snap = JSON.parse(raw);
19701
20660
  if (snap.version !== 1) return;
19702
20661
  console.error(`[memorix] Migrating team-state.json to SQLite...`);
@@ -19731,7 +20690,7 @@ var init_team_store = __esm({
19731
20690
  for (const [agentId, msgs] of Object.entries(snap.messages.inboxes)) {
19732
20691
  for (const msg of msgs) {
19733
20692
  this.stmtMsgInsert.run({
19734
- id: msg.id ?? randomUUID9(),
20693
+ id: msg.id ?? randomUUID12(),
19735
20694
  project_id: "migrated",
19736
20695
  sender_agent_id: msg.from ?? "unknown",
19737
20696
  recipient_agent_id: msg.to === "__broadcast__" ? null : msg.to ?? agentId,
@@ -19790,7 +20749,7 @@ var init_team_store = __esm({
19790
20749
  `[memorix] Team migration complete: ${agentsMigrated} agents, ${messagesMigrated} messages, ${tasksMigrated} tasks, ${locksMigrated} locks.`
19791
20750
  );
19792
20751
  try {
19793
- fs15.renameSync(jsonPath, jsonPath + ".migrated");
20752
+ fs16.renameSync(jsonPath, jsonPath + ".migrated");
19794
20753
  } catch {
19795
20754
  }
19796
20755
  } catch (err) {
@@ -19825,7 +20784,7 @@ var init_team_store = __esm({
19825
20784
  * Uses projectId derived from the dataDir path.
19826
20785
  */
19827
20786
  seedDefaultRoles(dataDir) {
19828
- const projectId = path21.basename(path21.resolve(dataDir, ".."));
20787
+ const projectId = path22.basename(path22.resolve(dataDir, ".."));
19829
20788
  const count2 = this.stmtRoleCountByProject.get(projectId).cnt;
19830
20789
  if (count2 > 0) return;
19831
20790
  const now3 = Date.now();
@@ -20839,8 +21798,8 @@ __export(server_exports, {
20839
21798
  startDashboard: () => startDashboard
20840
21799
  });
20841
21800
  import { createServer } from "http";
20842
- import { promises as fs16 } from "fs";
20843
- import path22 from "path";
21801
+ import { promises as fs17 } from "fs";
21802
+ import path23 from "path";
20844
21803
  import { exec } from "child_process";
20845
21804
  function sendJson(res, data, status = 200) {
20846
21805
  res.writeHead(status, {
@@ -21318,7 +22277,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
21318
22277
  const aliasModule = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
21319
22278
  canonicalId = await aliasModule.getCanonicalId(effectiveProjectId);
21320
22279
  const { promises: fsP } = await import("fs");
21321
- const registryPath = path22.join(baseDir, ".project-aliases.json");
22280
+ const registryPath = path23.join(baseDir, ".project-aliases.json");
21322
22281
  const raw = await fsP.readFile(registryPath, "utf-8");
21323
22282
  const registry = JSON.parse(raw);
21324
22283
  aliasGroups = registry.groups || [];
@@ -21433,14 +22392,14 @@ async function serveStatic(req, res, staticDir) {
21433
22392
  if (urlPath === "/" || !urlPath.includes(".")) {
21434
22393
  urlPath = "/index.html";
21435
22394
  }
21436
- const filePath = path22.join(staticDir, urlPath);
22395
+ const filePath = path23.join(staticDir, urlPath);
21437
22396
  if (!filePath.startsWith(staticDir)) {
21438
22397
  sendError(res, "Forbidden", 403);
21439
22398
  return;
21440
22399
  }
21441
22400
  try {
21442
- const data = await fs16.readFile(filePath);
21443
- const ext = path22.extname(filePath);
22401
+ const data = await fs17.readFile(filePath);
22402
+ const ext = path23.extname(filePath);
21444
22403
  res.writeHead(200, {
21445
22404
  "Content-Type": MIME_TYPES[ext] || "application/octet-stream",
21446
22405
  "Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
@@ -21450,7 +22409,7 @@ async function serveStatic(req, res, staticDir) {
21450
22409
  res.end(data);
21451
22410
  } catch {
21452
22411
  try {
21453
- const indexData = await fs16.readFile(path22.join(staticDir, "index.html"));
22412
+ const indexData = await fs17.readFile(path23.join(staticDir, "index.html"));
21454
22413
  res.writeHead(200, {
21455
22414
  "Content-Type": "text/html; charset=utf-8",
21456
22415
  "Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
@@ -22163,7 +23122,7 @@ function collectCodeMemory(opts) {
22163
23122
  if (status.indexedAt) lines.push(`Last scan: ${status.indexedAt}`);
22164
23123
  if (suggestedReads.length > 0) {
22165
23124
  lines.push("Suggested reads:");
22166
- suggestedReads.forEach((path26, index) => lines.push(`${index + 1}. ${path26}`));
23125
+ suggestedReads.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
22167
23126
  }
22168
23127
  return lines.join("\n");
22169
23128
  } catch {
@@ -22188,7 +23147,7 @@ __export(planner_exports, {
22188
23147
  materializeTaskGraph: () => materializeTaskGraph,
22189
23148
  seedAutonomousPipeline: () => seedAutonomousPipeline
22190
23149
  });
22191
- import { randomUUID as randomUUID10 } from "crypto";
23150
+ import { randomUUID as randomUUID13 } from "crypto";
22192
23151
  function isPlannerTask(metadata) {
22193
23152
  if (!metadata) return null;
22194
23153
  try {
@@ -22246,7 +23205,7 @@ function seedAutonomousPipeline(teamStore, projectId, config, opts) {
22246
23205
  const maxIterations = config.maxIterations ?? 3;
22247
23206
  const taskBudget = config.taskBudget ?? 15;
22248
23207
  const structuredPlan = opts?.structuredPlan ?? true;
22249
- const pipelineId = randomUUID10();
23208
+ const pipelineId = randomUUID13();
22250
23209
  const meta = {
22251
23210
  plannerType: "plan",
22252
23211
  pipelineId,
@@ -22936,15 +23895,15 @@ __export(audit_exports, {
22936
23895
  removeFile: () => removeFile,
22937
23896
  saveAudit: () => saveAudit
22938
23897
  });
22939
- import * as fs17 from "fs/promises";
22940
- import * as path23 from "path";
23898
+ import * as fs18 from "fs/promises";
23899
+ import * as path24 from "path";
22941
23900
  import { homedir as homedir24 } from "os";
22942
23901
  function getAuditFilePath() {
22943
- return process.env.MEMORIX_AUDIT_FILE || path23.join(homedir24(), ".memorix", "audit.json");
23902
+ return process.env.MEMORIX_AUDIT_FILE || path24.join(homedir24(), ".memorix", "audit.json");
22944
23903
  }
22945
23904
  async function loadAudit() {
22946
23905
  try {
22947
- const content = await fs17.readFile(getAuditFilePath(), "utf-8");
23906
+ const content = await fs18.readFile(getAuditFilePath(), "utf-8");
22948
23907
  return JSON.parse(content);
22949
23908
  } catch {
22950
23909
  return {
@@ -22955,9 +23914,9 @@ async function loadAudit() {
22955
23914
  }
22956
23915
  async function saveAudit(data) {
22957
23916
  const auditFile = getAuditFilePath();
22958
- const dir = path23.dirname(auditFile);
22959
- await fs17.mkdir(dir, { recursive: true });
22960
- await fs17.writeFile(auditFile, JSON.stringify(data, null, 2), "utf-8");
23917
+ const dir = path24.dirname(auditFile);
23918
+ await fs18.mkdir(dir, { recursive: true });
23919
+ await fs18.writeFile(auditFile, JSON.stringify(data, null, 2), "utf-8");
22961
23920
  }
22962
23921
  function getProjectId(projectRoot) {
22963
23922
  try {
@@ -23039,8 +23998,8 @@ __export(installers_exports, {
23039
23998
  resolveOpenCodeHookCommand: () => resolveOpenCodeHookCommand,
23040
23999
  uninstallHooks: () => uninstallHooks
23041
24000
  });
23042
- import * as fs18 from "fs/promises";
23043
- import * as path24 from "path";
24001
+ import * as fs19 from "fs/promises";
24002
+ import * as path25 from "path";
23044
24003
  import * as os3 from "os";
23045
24004
  import { execSync as execSync3 } from "child_process";
23046
24005
  function resolveHookCommand() {
@@ -23056,7 +24015,7 @@ function resolveWindowsMemorixShim() {
23056
24015
  stdio: ["ignore", "pipe", "ignore"],
23057
24016
  windowsHide: true
23058
24017
  }));
23059
- return output.split(/\r?\n/).map((candidate) => candidate.trim()).find((candidate) => path24.isAbsolute(candidate)) ?? null;
24018
+ return output.split(/\r?\n/).map((candidate) => candidate.trim()).find((candidate) => path25.isAbsolute(candidate)) ?? null;
23060
24019
  } catch {
23061
24020
  return null;
23062
24021
  }
@@ -23251,9 +24210,9 @@ async function installOfficialSkillsForAgent(agent, projectRoot, global = false)
23251
24210
  const root = global ? os3.homedir() : projectRoot;
23252
24211
  const skillPaths = [];
23253
24212
  for (const skillEntry of OFFICIAL_MEMORIX_SKILLS) {
23254
- const skillPath = path24.join(root, relativeDir, skillEntry.name, "SKILL.md");
23255
- await fs18.mkdir(path24.dirname(skillPath), { recursive: true });
23256
- await fs18.writeFile(skillPath, skillEntry.content, "utf-8");
24213
+ const skillPath = path25.join(root, relativeDir, skillEntry.name, "SKILL.md");
24214
+ await fs19.mkdir(path25.dirname(skillPath), { recursive: true });
24215
+ await fs19.writeFile(skillPath, skillEntry.content, "utf-8");
23257
24216
  skillPaths.push(skillPath);
23258
24217
  try {
23259
24218
  const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
@@ -23466,124 +24425,124 @@ async function installOpenCodeSkills(projectRoot, global = false) {
23466
24425
  function getProjectConfigPath(agent, projectRoot) {
23467
24426
  switch (agent) {
23468
24427
  case "claude":
23469
- return path24.join(projectRoot, ".claude", "settings.local.json");
24428
+ return path25.join(projectRoot, ".claude", "settings.local.json");
23470
24429
  case "copilot":
23471
- return path24.join(projectRoot, ".github", "hooks", "memorix.json");
24430
+ return path25.join(projectRoot, ".github", "hooks", "memorix.json");
23472
24431
  case "windsurf":
23473
- return path24.join(projectRoot, ".windsurf", "hooks.json");
24432
+ return path25.join(projectRoot, ".windsurf", "hooks.json");
23474
24433
  case "cursor":
23475
- return path24.join(projectRoot, ".cursor", "hooks.json");
24434
+ return path25.join(projectRoot, ".cursor", "hooks.json");
23476
24435
  case "kiro":
23477
- return path24.join(projectRoot, ".kiro", "hooks", "memorix-agent-stop.kiro.hook");
24436
+ return path25.join(projectRoot, ".kiro", "hooks", "memorix-agent-stop.kiro.hook");
23478
24437
  case "codex":
23479
- return path24.join(projectRoot, "AGENTS.md");
24438
+ return path25.join(projectRoot, "AGENTS.md");
23480
24439
  case "trae":
23481
- return path24.join(projectRoot, ".trae", "rules", "project_rules.md");
24440
+ return path25.join(projectRoot, ".trae", "rules", "project_rules.md");
23482
24441
  case "opencode":
23483
- return path24.join(projectRoot, ".opencode", "plugins", "memorix.js");
24442
+ return path25.join(projectRoot, ".opencode", "plugins", "memorix.js");
23484
24443
  case "pi":
23485
- return path24.join(projectRoot, ".pi", "packages", "memorix", "extensions", "memorix.js");
24444
+ return path25.join(projectRoot, ".pi", "packages", "memorix", "extensions", "memorix.js");
23486
24445
  case "openclaw":
23487
- return path24.join(os3.homedir(), ".openclaw", "extensions", "memorix", "hooks", "memorix", "HOOK.md");
24446
+ return path25.join(os3.homedir(), ".openclaw", "extensions", "memorix", "hooks", "memorix", "HOOK.md");
23488
24447
  case "hermes":
23489
- return path24.join(os3.homedir(), ".hermes", "plugins", "memorix", "plugin.yaml");
24448
+ return path25.join(os3.homedir(), ".hermes", "plugins", "memorix", "plugin.yaml");
23490
24449
  case "omp":
23491
- return path24.join(projectRoot, ".omp", "packages", "memorix", "extensions", "memorix.js");
24450
+ return path25.join(projectRoot, ".omp", "packages", "memorix", "extensions", "memorix.js");
23492
24451
  case "antigravity":
23493
- return path24.join(projectRoot, ".agents", "hooks.json");
24452
+ return path25.join(projectRoot, ".agents", "hooks.json");
23494
24453
  case "gemini-cli":
23495
- return path24.join(projectRoot, ".gemini", "settings.json");
24454
+ return path25.join(projectRoot, ".gemini", "settings.json");
23496
24455
  default:
23497
- return path24.join(projectRoot, ".memorix", "hooks.json");
24456
+ return path25.join(projectRoot, ".memorix", "hooks.json");
23498
24457
  }
23499
24458
  }
23500
24459
  function getGlobalConfigPath(agent) {
23501
24460
  const home = os3.homedir();
23502
24461
  switch (agent) {
23503
24462
  case "claude":
23504
- return path24.join(home, ".claude", "settings.json");
24463
+ return path25.join(home, ".claude", "settings.json");
23505
24464
  case "copilot":
23506
24465
  return "";
23507
24466
  case "windsurf":
23508
- return path24.join(home, ".codeium", "windsurf", "hooks.json");
24467
+ return path25.join(home, ".codeium", "windsurf", "hooks.json");
23509
24468
  case "cursor":
23510
- return path24.join(home, ".cursor", "hooks.json");
24469
+ return path25.join(home, ".cursor", "hooks.json");
23511
24470
  case "antigravity":
23512
- return path24.join(home, ".gemini", "config", "hooks.json");
24471
+ return path25.join(home, ".gemini", "config", "hooks.json");
23513
24472
  case "gemini-cli":
23514
- return path24.join(home, ".gemini", "settings.json");
24473
+ return path25.join(home, ".gemini", "settings.json");
23515
24474
  case "opencode":
23516
- return path24.join(home, ".config", "opencode", "plugins", "memorix.js");
24475
+ return path25.join(home, ".config", "opencode", "plugins", "memorix.js");
23517
24476
  case "pi":
23518
- return path24.join(home, ".pi", "agent", "packages", "memorix", "extensions", "memorix.js");
24477
+ return path25.join(home, ".pi", "agent", "packages", "memorix", "extensions", "memorix.js");
23519
24478
  case "openclaw":
23520
- return path24.join(home, ".openclaw", "extensions", "memorix", "hooks", "memorix", "HOOK.md");
24479
+ return path25.join(home, ".openclaw", "extensions", "memorix", "hooks", "memorix", "HOOK.md");
23521
24480
  case "hermes":
23522
- return path24.join(home, ".hermes", "plugins", "memorix", "plugin.yaml");
24481
+ return path25.join(home, ".hermes", "plugins", "memorix", "plugin.yaml");
23523
24482
  case "omp":
23524
- return path24.join(home, ".omp", "agent", "packages", "memorix", "extensions", "memorix.js");
24483
+ return path25.join(home, ".omp", "agent", "packages", "memorix", "extensions", "memorix.js");
23525
24484
  case "trae":
23526
- return path24.join(home, ".trae", "rules", "project_rules.md");
24485
+ return path25.join(home, ".trae", "rules", "project_rules.md");
23527
24486
  default:
23528
- return path24.join(home, ".memorix", "hooks.json");
24487
+ return path25.join(home, ".memorix", "hooks.json");
23529
24488
  }
23530
24489
  }
23531
24490
  function getAgentRulesPath(agent, root, global = false) {
23532
24491
  if (global) {
23533
24492
  switch (agent) {
23534
24493
  case "windsurf":
23535
- return path24.join(root, ".codeium", "windsurf", "rules", "memorix.md");
24494
+ return path25.join(root, ".codeium", "windsurf", "rules", "memorix.md");
23536
24495
  case "cursor":
23537
- return path24.join(root, ".cursor", "rules", "memorix.mdc");
24496
+ return path25.join(root, ".cursor", "rules", "memorix.mdc");
23538
24497
  case "claude":
23539
- return path24.join(root, ".claude", "CLAUDE.md");
24498
+ return path25.join(root, ".claude", "CLAUDE.md");
23540
24499
  case "codex":
23541
- return path24.join(root, ".codex", "AGENTS.md");
24500
+ return path25.join(root, ".codex", "AGENTS.md");
23542
24501
  case "kiro":
23543
- return path24.join(root, ".kiro", "steering", "memorix.md");
24502
+ return path25.join(root, ".kiro", "steering", "memorix.md");
23544
24503
  case "opencode":
23545
- return path24.join(root, ".config", "opencode", "AGENTS.md");
24504
+ return path25.join(root, ".config", "opencode", "AGENTS.md");
23546
24505
  case "antigravity":
23547
24506
  case "gemini-cli":
23548
- return path24.join(root, ".gemini", "GEMINI.md");
24507
+ return path25.join(root, ".gemini", "GEMINI.md");
23549
24508
  case "trae":
23550
- return path24.join(root, ".trae", "rules", "project_rules.md");
24509
+ return path25.join(root, ".trae", "rules", "project_rules.md");
23551
24510
  default:
23552
- return path24.join(root, ".agent", "rules", "memorix.md");
24511
+ return path25.join(root, ".agent", "rules", "memorix.md");
23553
24512
  }
23554
24513
  }
23555
24514
  switch (agent) {
23556
24515
  case "windsurf":
23557
- return path24.join(root, ".windsurf", "rules", "memorix.md");
24516
+ return path25.join(root, ".windsurf", "rules", "memorix.md");
23558
24517
  case "cursor":
23559
- return path24.join(root, ".cursor", "rules", "memorix.mdc");
24518
+ return path25.join(root, ".cursor", "rules", "memorix.mdc");
23560
24519
  case "claude":
23561
- return path24.join(root, "CLAUDE.md");
24520
+ return path25.join(root, "CLAUDE.md");
23562
24521
  case "copilot":
23563
- return path24.join(root, ".github", "copilot-instructions.md");
24522
+ return path25.join(root, ".github", "copilot-instructions.md");
23564
24523
  case "codex":
23565
- return path24.join(root, "AGENTS.md");
24524
+ return path25.join(root, "AGENTS.md");
23566
24525
  case "kiro":
23567
- return path24.join(root, ".kiro", "steering", "memorix.md");
24526
+ return path25.join(root, ".kiro", "steering", "memorix.md");
23568
24527
  case "opencode":
23569
- return path24.join(root, "AGENTS.md");
24528
+ return path25.join(root, "AGENTS.md");
23570
24529
  case "antigravity":
23571
24530
  case "gemini-cli":
23572
- return path24.join(root, "GEMINI.md");
24531
+ return path25.join(root, "GEMINI.md");
23573
24532
  case "trae":
23574
- return path24.join(root, ".trae", "rules", "project_rules.md");
24533
+ return path25.join(root, ".trae", "rules", "project_rules.md");
23575
24534
  default:
23576
- return path24.join(root, ".agent", "rules", "memorix.md");
24535
+ return path25.join(root, ".agent", "rules", "memorix.md");
23577
24536
  }
23578
24537
  }
23579
24538
  async function detectCopilotExtension(home) {
23580
24539
  const extDirs = [
23581
- path24.join(home, ".vscode", "extensions"),
23582
- path24.join(home, ".vscode-insiders", "extensions")
24540
+ path25.join(home, ".vscode", "extensions"),
24541
+ path25.join(home, ".vscode-insiders", "extensions")
23583
24542
  ];
23584
24543
  for (const extDir of extDirs) {
23585
24544
  try {
23586
- const entries = await fs18.readdir(extDir);
24545
+ const entries = await fs19.readdir(extDir);
23587
24546
  if (entries.some((e) => e.startsWith("github.copilot-"))) {
23588
24547
  return true;
23589
24548
  }
@@ -23595,21 +24554,21 @@ async function detectCopilotExtension(home) {
23595
24554
  async function detectInstalledAgents() {
23596
24555
  const agents = [];
23597
24556
  const home = os3.homedir();
23598
- const claudeDir = path24.join(home, ".claude");
24557
+ const claudeDir = path25.join(home, ".claude");
23599
24558
  try {
23600
- await fs18.access(claudeDir);
24559
+ await fs19.access(claudeDir);
23601
24560
  agents.push("claude");
23602
24561
  } catch {
23603
24562
  }
23604
- const windsurfDir = path24.join(home, ".codeium", "windsurf");
24563
+ const windsurfDir = path25.join(home, ".codeium", "windsurf");
23605
24564
  try {
23606
- await fs18.access(windsurfDir);
24565
+ await fs19.access(windsurfDir);
23607
24566
  agents.push("windsurf");
23608
24567
  } catch {
23609
24568
  }
23610
- const cursorDir = path24.join(home, ".cursor");
24569
+ const cursorDir = path25.join(home, ".cursor");
23611
24570
  try {
23612
- await fs18.access(cursorDir);
24571
+ await fs19.access(cursorDir);
23613
24572
  agents.push("cursor");
23614
24573
  } catch {
23615
24574
  }
@@ -23617,21 +24576,21 @@ async function detectInstalledAgents() {
23617
24576
  if (copilotDetected) {
23618
24577
  agents.push("copilot");
23619
24578
  }
23620
- const kiroConfig = path24.join(home, ".kiro");
24579
+ const kiroConfig = path25.join(home, ".kiro");
23621
24580
  try {
23622
- await fs18.access(kiroConfig);
24581
+ await fs19.access(kiroConfig);
23623
24582
  agents.push("kiro");
23624
24583
  } catch {
23625
24584
  }
23626
- const codexDir = path24.join(home, ".codex");
24585
+ const codexDir = path25.join(home, ".codex");
23627
24586
  try {
23628
- await fs18.access(codexDir);
24587
+ await fs19.access(codexDir);
23629
24588
  agents.push("codex");
23630
24589
  } catch {
23631
24590
  }
23632
- const antigravityDir = path24.join(home, ".gemini", "config");
24591
+ const antigravityDir = path25.join(home, ".gemini", "config");
23633
24592
  try {
23634
- await fs18.access(antigravityDir);
24593
+ await fs19.access(antigravityDir);
23635
24594
  agents.push("antigravity");
23636
24595
  } catch {
23637
24596
  }
@@ -23642,15 +24601,15 @@ async function detectInstalledAgents() {
23642
24601
  agents.push("gemini-cli");
23643
24602
  } catch {
23644
24603
  }
23645
- const opencodeDir = path24.join(home, ".config", "opencode");
24604
+ const opencodeDir = path25.join(home, ".config", "opencode");
23646
24605
  try {
23647
- await fs18.access(opencodeDir);
24606
+ await fs19.access(opencodeDir);
23648
24607
  agents.push("opencode");
23649
24608
  } catch {
23650
24609
  }
23651
- const traeDir = path24.join(home, ".trae");
24610
+ const traeDir = path25.join(home, ".trae");
23652
24611
  try {
23653
- await fs18.access(traeDir);
24612
+ await fs19.access(traeDir);
23654
24613
  agents.push("trae");
23655
24614
  } catch {
23656
24615
  }
@@ -23684,7 +24643,7 @@ async function installHooks(agent, projectRoot, global = false) {
23684
24643
  try {
23685
24644
  const { access: access2, unlink: unlink2 } = await import("fs/promises");
23686
24645
  await access2(entry.path);
23687
- const basename3 = path24.basename(entry.path);
24646
+ const basename3 = path25.basename(entry.path);
23688
24647
  if (basename3 === "AGENTS.md" || basename3 === "GEMINI.md" || basename3 === "CONTEXT.md") {
23689
24648
  continue;
23690
24649
  }
@@ -23739,8 +24698,8 @@ async function installHooks(agent, projectRoot, global = false) {
23739
24698
  case "opencode": {
23740
24699
  const pluginContent = generateOpenCodePlugin();
23741
24700
  const pluginPath = global ? getGlobalConfigPath(agent) : getProjectConfigPath(agent, projectRoot);
23742
- await fs18.mkdir(path24.dirname(pluginPath), { recursive: true });
23743
- await fs18.writeFile(pluginPath, pluginContent, "utf-8");
24701
+ await fs19.mkdir(path25.dirname(pluginPath), { recursive: true });
24702
+ await fs19.writeFile(pluginPath, pluginContent, "utf-8");
23744
24703
  try {
23745
24704
  const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
23746
24705
  await recordFile2(projectRoot, "hook", pluginPath, agent);
@@ -23769,14 +24728,14 @@ async function installHooks(agent, projectRoot, global = false) {
23769
24728
  default:
23770
24729
  generated = generateClaudeConfig();
23771
24730
  }
23772
- await fs18.mkdir(path24.dirname(configPath), { recursive: true });
24731
+ await fs19.mkdir(path25.dirname(configPath), { recursive: true });
23773
24732
  if (agent === "kiro") {
23774
24733
  const hookFiles = generateKiroHookFiles();
23775
- const hooksDir = path24.join(path24.dirname(configPath));
23776
- await fs18.mkdir(hooksDir, { recursive: true });
24734
+ const hooksDir = path25.join(path25.dirname(configPath));
24735
+ await fs19.mkdir(hooksDir, { recursive: true });
23777
24736
  for (const hf of hookFiles) {
23778
- const hookPath = path24.join(hooksDir, hf.filename);
23779
- await fs18.writeFile(hookPath, hf.content, "utf-8");
24737
+ const hookPath = path25.join(hooksDir, hf.filename);
24738
+ await fs19.writeFile(hookPath, hf.content, "utf-8");
23780
24739
  try {
23781
24740
  const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
23782
24741
  await recordFile2(projectRoot, "hook", hookPath, agent);
@@ -23786,7 +24745,7 @@ async function installHooks(agent, projectRoot, global = false) {
23786
24745
  } else {
23787
24746
  let existing = {};
23788
24747
  try {
23789
- const content = await fs18.readFile(configPath, "utf-8");
24748
+ const content = await fs19.readFile(configPath, "utf-8");
23790
24749
  existing = JSON.parse(content);
23791
24750
  } catch {
23792
24751
  }
@@ -23821,7 +24780,7 @@ async function installHooks(agent, projectRoot, global = false) {
23821
24780
  const h = merged.hooks;
23822
24781
  if (h) delete h.preToolUse;
23823
24782
  }
23824
- await fs18.writeFile(configPath, JSON.stringify(merged, null, 2), "utf-8");
24783
+ await fs19.writeFile(configPath, JSON.stringify(merged, null, 2), "utf-8");
23825
24784
  try {
23826
24785
  const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
23827
24786
  await recordFile2(projectRoot, "hook", configPath, agent);
@@ -23869,18 +24828,18 @@ async function installAgentRules(agent, projectRoot, global = false) {
23869
24828
  const rulesRoot = global ? os3.homedir() : projectRoot;
23870
24829
  const rulesPath = getAgentRulesPath(agent, rulesRoot, global);
23871
24830
  try {
23872
- await fs18.mkdir(path24.dirname(rulesPath), { recursive: true });
24831
+ await fs19.mkdir(path25.dirname(rulesPath), { recursive: true });
23873
24832
  if (agent === "claude" || agent === "codex" || agent === "opencode" || agent === "antigravity" || agent === "gemini-cli") {
23874
24833
  try {
23875
- const existing = await fs18.readFile(rulesPath, "utf-8");
23876
- await fs18.writeFile(rulesPath, mergeMemorixRulesContent(existing, rulesContent), "utf-8");
24834
+ const existing = await fs19.readFile(rulesPath, "utf-8");
24835
+ await fs19.writeFile(rulesPath, mergeMemorixRulesContent(existing, rulesContent), "utf-8");
23877
24836
  try {
23878
24837
  const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
23879
24838
  await recordFile2(projectRoot, "rule", rulesPath, agent);
23880
24839
  } catch {
23881
24840
  }
23882
24841
  } catch {
23883
- await fs18.writeFile(rulesPath, rulesContent, "utf-8");
24842
+ await fs19.writeFile(rulesPath, rulesContent, "utf-8");
23884
24843
  try {
23885
24844
  const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
23886
24845
  await recordFile2(projectRoot, "rule", rulesPath, agent);
@@ -23889,9 +24848,9 @@ async function installAgentRules(agent, projectRoot, global = false) {
23889
24848
  }
23890
24849
  } else {
23891
24850
  try {
23892
- await fs18.access(rulesPath);
24851
+ await fs19.access(rulesPath);
23893
24852
  } catch {
23894
- await fs18.writeFile(rulesPath, rulesContent, "utf-8");
24853
+ await fs19.writeFile(rulesPath, rulesContent, "utf-8");
23895
24854
  try {
23896
24855
  const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
23897
24856
  await recordFile2(projectRoot, "rule", rulesPath, agent);
@@ -23981,6 +24940,8 @@ alwaysApply: true
23981
24940
  "- Include `filesModified` when relevant",
23982
24941
  "- Use `topicKey` for topics that evolve over time (prevents duplicates)",
23983
24942
  '- For "why" decisions, use `memorix_store_reasoning`',
24943
+ '- For a stable fact, reusable procedure, or completed episode that merits deliberate long-term review, include `longTerm` in `memorix_store` with the appropriate kind and normally `scope: "project"`. It creates a candidate only: do not use it for routine updates, do not make project-derived evidence portable user memory, and do not assume it enters context until an operator qualifies and approves it through `memorix memory long-term`.',
24944
+ "- A `user` + `portable` durable memory delivered in a task brief is intentionally available across projects. When it matches the task, use it as reusable background even if its origin differs; do not treat it as a current-project fact. Expand it only when needed with `memorix_detail` using its `durable:<id>` reference and a specific purpose.",
23984
24945
  "",
23985
24946
  "**Don't store:** greetings, simple file reads, trivial commands (ls, pwd, git status).",
23986
24947
  "",
@@ -24017,16 +24978,16 @@ async function uninstallHooks(agent, projectRoot, global = false) {
24017
24978
  let success = false;
24018
24979
  try {
24019
24980
  if (agent === "kiro" || agent === "opencode") {
24020
- await fs18.unlink(configPath);
24981
+ await fs19.unlink(configPath);
24021
24982
  success = true;
24022
24983
  } else {
24023
- const content = await fs18.readFile(configPath, "utf-8");
24984
+ const content = await fs19.readFile(configPath, "utf-8");
24024
24985
  const config = JSON.parse(content);
24025
24986
  delete config.hooks;
24026
24987
  if (Object.keys(config).length === 0) {
24027
- await fs18.unlink(configPath);
24988
+ await fs19.unlink(configPath);
24028
24989
  } else {
24029
- await fs18.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
24990
+ await fs19.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
24030
24991
  }
24031
24992
  success = true;
24032
24993
  }
@@ -24039,10 +25000,10 @@ async function uninstallHooks(agent, projectRoot, global = false) {
24039
25000
  const agentFiles = prevFiles.filter((e) => e.agent === agent);
24040
25001
  for (const entry of agentFiles) {
24041
25002
  try {
24042
- const basename3 = path24.basename(entry.path);
25003
+ const basename3 = path25.basename(entry.path);
24043
25004
  if (basename3 === "AGENTS.md" || basename3 === "GEMINI.md" || basename3 === "CONTEXT.md") {
24044
25005
  try {
24045
- const content = await fs18.readFile(entry.path, "utf-8");
25006
+ const content = await fs19.readFile(entry.path, "utf-8");
24046
25007
  const memorixStart = content.indexOf("# Memorix");
24047
25008
  if (memorixStart >= 0) {
24048
25009
  const afterMemorix = content.substring(memorixStart);
@@ -24054,9 +25015,9 @@ async function uninstallHooks(agent, projectRoot, global = false) {
24054
25015
  }
24055
25016
  const cleaned = (before + "\n" + after).trim();
24056
25017
  if (cleaned.length === 0) {
24057
- await fs18.unlink(entry.path);
25018
+ await fs19.unlink(entry.path);
24058
25019
  } else {
24059
- await fs18.writeFile(entry.path, cleaned + "\n", "utf-8");
25020
+ await fs19.writeFile(entry.path, cleaned + "\n", "utf-8");
24060
25021
  }
24061
25022
  }
24062
25023
  } catch {
@@ -24065,7 +25026,7 @@ async function uninstallHooks(agent, projectRoot, global = false) {
24065
25026
  auditCleaned = true;
24066
25027
  continue;
24067
25028
  }
24068
- await fs18.unlink(entry.path);
25029
+ await fs19.unlink(entry.path);
24069
25030
  await removeFile2(projectRoot, entry.path);
24070
25031
  auditCleaned = true;
24071
25032
  } catch {
@@ -24077,14 +25038,14 @@ async function uninstallHooks(agent, projectRoot, global = false) {
24077
25038
  if (success) {
24078
25039
  try {
24079
25040
  const { rm } = await import("fs/promises");
24080
- const dir = path24.dirname(configPath);
25041
+ const dir = path25.dirname(configPath);
24081
25042
  let current = dir;
24082
25043
  for (let i = 0; i < 3; i++) {
24083
25044
  try {
24084
- const entries = await fs18.readdir(current);
25045
+ const entries = await fs19.readdir(current);
24085
25046
  if (entries.length === 0) {
24086
25047
  await rm(current, { recursive: true });
24087
- current = path24.dirname(current);
25048
+ current = path25.dirname(current);
24088
25049
  } else {
24089
25050
  break;
24090
25051
  }
@@ -24108,12 +25069,12 @@ async function getHookStatus(projectRoot) {
24108
25069
  let usedPath = projectPath;
24109
25070
  const verifiedByDefault = agent !== "opencode" && agent !== "pi";
24110
25071
  try {
24111
- await fs18.access(projectPath);
25072
+ await fs19.access(projectPath);
24112
25073
  installed = true;
24113
25074
  } catch {
24114
25075
  if (globalPath) {
24115
25076
  try {
24116
- await fs18.access(globalPath);
25077
+ await fs19.access(globalPath);
24117
25078
  installed = true;
24118
25079
  usedPath = globalPath;
24119
25080
  } catch {
@@ -24122,7 +25083,7 @@ async function getHookStatus(projectRoot) {
24122
25083
  }
24123
25084
  if (installed && agent === "opencode") {
24124
25085
  try {
24125
- const content = await fs18.readFile(usedPath, "utf-8");
25086
+ const content = await fs19.readFile(usedPath, "utf-8");
24126
25087
  const match = content.match(/@generated-version\s+(\d+)/);
24127
25088
  const installedVersion = match ? parseInt(match[1], 10) : 0;
24128
25089
  outdated = installedVersion < OPENCODE_PLUGIN_VERSION;
@@ -24153,11 +25114,11 @@ var init_installers = __esm({
24153
25114
  init_official_skills();
24154
25115
  OPENCODE_PLUGIN_VERSION = 7;
24155
25116
  AGENT_SKILL_DIRS = {
24156
- cursor: { project: path24.join(".cursor", "skills"), global: path24.join(".cursor", "skills") },
24157
- windsurf: { project: path24.join(".windsurf", "skills"), global: path24.join(".windsurf", "skills") },
24158
- kiro: { project: path24.join(".kiro", "skills"), global: path24.join(".kiro", "skills") },
24159
- opencode: { project: path24.join(".opencode", "skills"), global: path24.join(".config", "opencode", "skills") },
24160
- trae: { project: path24.join(".trae", "skills"), global: path24.join(".trae", "skills") }
25117
+ cursor: { project: path25.join(".cursor", "skills"), global: path25.join(".cursor", "skills") },
25118
+ windsurf: { project: path25.join(".windsurf", "skills"), global: path25.join(".windsurf", "skills") },
25119
+ kiro: { project: path25.join(".kiro", "skills"), global: path25.join(".kiro", "skills") },
25120
+ opencode: { project: path25.join(".opencode", "skills"), global: path25.join(".config", "opencode", "skills") },
25121
+ trae: { project: path25.join(".trae", "skills"), global: path25.join(".trae", "skills") }
24161
25122
  };
24162
25123
  PACKAGE_OWNED_HOOK_AGENTS = /* @__PURE__ */ new Set(["openclaw", "hermes", "omp"]);
24163
25124
  }
@@ -24171,9 +25132,9 @@ __export(hooks_path_exports, {
24171
25132
  resolveHooksDir: () => resolveHooksDir
24172
25133
  });
24173
25134
  import { existsSync as existsSync17, readFileSync as readFileSync13, statSync as statSync4, mkdirSync as mkdirSync4 } from "fs";
24174
- import path25 from "path";
25135
+ import path26 from "path";
24175
25136
  function resolveGitDir(projectRoot) {
24176
- const dotGit = path25.join(projectRoot, ".git");
25137
+ const dotGit = path26.join(projectRoot, ".git");
24177
25138
  if (!existsSync17(dotGit)) return null;
24178
25139
  const stat = statSync4(dotGit);
24179
25140
  if (stat.isDirectory()) {
@@ -24185,7 +25146,7 @@ function resolveGitDir(projectRoot) {
24185
25146
  const match = content.match(/^gitdir:\s*(.+)$/);
24186
25147
  if (match) {
24187
25148
  const gitdir = match[1].trim();
24188
- const resolved = path25.isAbsolute(gitdir) ? gitdir : path25.resolve(projectRoot, gitdir);
25149
+ const resolved = path26.isAbsolute(gitdir) ? gitdir : path26.resolve(projectRoot, gitdir);
24189
25150
  if (existsSync17(resolved)) {
24190
25151
  return resolved;
24191
25152
  }
@@ -24198,10 +25159,10 @@ function resolveGitDir(projectRoot) {
24198
25159
  function resolveHooksDir(projectRoot) {
24199
25160
  const gitDir = resolveGitDir(projectRoot);
24200
25161
  if (!gitDir) return null;
24201
- const hooksDir = path25.join(gitDir, "hooks");
25162
+ const hooksDir = path26.join(gitDir, "hooks");
24202
25163
  return {
24203
25164
  hooksDir,
24204
- hookPath: path25.join(hooksDir, "post-commit")
25165
+ hookPath: path26.join(hooksDir, "post-commit")
24205
25166
  };
24206
25167
  }
24207
25168
  function ensureHooksDir(projectRoot) {
@@ -27353,10 +28314,10 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
27353
28314
  pathsToCheck.push(join19(homedir21(), ".gemini", "antigravity", "mcp_config.json"));
27354
28315
  }
27355
28316
  const merged = /* @__PURE__ */ new Map();
27356
- for (const path26 of pathsToCheck) {
27357
- if (existsSync10(path26)) {
28317
+ for (const path27 of pathsToCheck) {
28318
+ if (existsSync10(path27)) {
27358
28319
  try {
27359
- const content = readFileSync7(path26, "utf-8");
28320
+ const content = readFileSync7(path27, "utf-8");
27360
28321
  const servers = adapter.parse(content);
27361
28322
  for (const s of servers) {
27362
28323
  if (!merged.has(s.name)) merged.set(s.name, s);
@@ -28147,7 +29108,7 @@ The path should point to a directory containing a .git folder.`
28147
29108
  };
28148
29109
  const server = existingServer ?? new McpServer({
28149
29110
  name: "memorix",
28150
- version: true ? "1.2.9" : "1.0.1"
29111
+ version: true ? "1.3.0" : "1.0.1"
28151
29112
  });
28152
29113
  const originalRegisterTool = server.registerTool.bind(server);
28153
29114
  server.registerTool = ((name, ...args) => {
@@ -28227,7 +29188,7 @@ The path should point to a directory containing a .git folder.`
28227
29188
  "memorix_store",
28228
29189
  {
28229
29190
  title: "Store Memory",
28230
- description: "Store a new observation/memory. Automatically indexed for search. Use type to classify: gotcha ([GOTCHA] critical pitfall), decision ([DECISION] architecture choice), problem-solution ([FIX] bug fix), how-it-works ([INFO] explanation), what-changed ([CHANGE] change), discovery ([DISCOVERY] insight), why-it-exists ([WHY] rationale), trade-off ([TRADEOFF] compromise), session-request ([SESSION] original goal). Project visibility is the default. Personal or team visibility requires an explicitly joined coordination identity. For a read-only task, do not store unless the user explicitly asks to save a record.",
29191
+ description: "Store a new observation/memory. Automatically indexed for search. Use type to classify: gotcha ([GOTCHA] critical pitfall), decision ([DECISION] architecture choice), problem-solution ([FIX] bug fix), how-it-works ([INFO] explanation), what-changed ([CHANGE] change), discovery ([DISCOVERY] insight), why-it-exists ([WHY] rationale), trade-off ([TRADEOFF] compromise), session-request ([SESSION] original goal). Project visibility is the default. Personal or team visibility requires an explicitly joined coordination identity. Set longTerm only when the caller explicitly wants an additional source-backed long-term candidate; it is never injected until an operator records a review through the CLI. For a read-only task, do not store unless the user explicitly asks to save a record.",
28231
29192
  inputSchema: {
28232
29193
  entityName: z2.string().describe('The entity this observation belongs to (e.g., "auth-module", "port-config")'),
28233
29194
  type: z2.enum(OBSERVATION_TYPES).describe("Observation type for classification"),
@@ -28249,30 +29210,43 @@ The path should point to a directory containing a .git folder.`
28249
29210
  visibility: z2.enum(["personal", "project", "team"]).optional().describe(
28250
29211
  "Retrieval scope. Project is the normal shared default; personal/team require memorix_session_start with joinTeam=true."
28251
29212
  ),
29213
+ longTerm: z2.object({
29214
+ kind: z2.enum(["episodic", "semantic", "procedural"]).describe("Cognitive kind for an additional long-term candidate."),
29215
+ scope: z2.enum(["project", "user", "team"]).optional().default("project").describe("Long-term scope. User capture is private source evidence and needs a bound agent identity."),
29216
+ tags: z2.array(z2.string()).optional().describe("Small set of retrieval tags for the candidate."),
29217
+ applicability: z2.string().optional().describe("When this durable fact or procedure applies.")
29218
+ }).optional().describe("Optional explicit request to create a source-backed long-term candidate alongside this observation. Candidates are not automatically injected."),
28252
29219
  overrideReadOnly: z2.boolean().optional().describe(
28253
29220
  "Use only when the user explicitly asks to save memory during a read-only or no-modification task."
28254
29221
  )
28255
29222
  }
28256
29223
  },
28257
- async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility, overrideReadOnly }) => {
29224
+ async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility, longTerm, overrideReadOnly }) => {
28258
29225
  const unresolved = requireResolvedProject("store memory in the current project");
28259
29226
  if (unresolved) return unresolved;
28260
29227
  const readOnlyBoundary = blockReadOnlyAutopilotWrite(overrideReadOnly);
28261
29228
  if (readOnlyBoundary) return readOnlyBoundary;
28262
- const requestedVisibility = visibility ?? "project";
29229
+ const requestedVisibility = longTerm?.scope === "user" ? "personal" : longTerm?.scope === "team" ? "team" : visibility;
29230
+ const effectiveVisibility = requestedVisibility ?? "project";
28263
29231
  const reader = getObservationReader();
28264
- if (requestedVisibility !== "project" && !currentAgentId) {
29232
+ if (effectiveVisibility !== "project" && !currentAgentId) {
28265
29233
  return {
28266
29234
  content: [{ type: "text", text: "Personal or team memory requires memorix_session_start with joinTeam=true so Memorix can bind an owner." }],
28267
29235
  isError: true
28268
29236
  };
28269
29237
  }
28270
- if (requestedVisibility === "team" && !reader.isTeamMember) {
29238
+ if (effectiveVisibility === "team" && !reader.isTeamMember) {
28271
29239
  return {
28272
29240
  content: [{ type: "text", text: "Team memory requires an active coordination membership in the current project." }],
28273
29241
  isError: true
28274
29242
  };
28275
29243
  }
29244
+ if (longTerm?.scope === "team" && !reader.isTeamMember) {
29245
+ return {
29246
+ content: [{ type: "text", text: "Team long-term memory requires an active coordination membership in the current project." }],
29247
+ isError: true
29248
+ };
29249
+ }
28276
29250
  try {
28277
29251
  return await withFreshIndex(async () => {
28278
29252
  let entityName = rawEntityName;
@@ -28294,7 +29268,7 @@ The path should point to a directory containing a .git folder.`
28294
29268
  const useFormation = formationMode === "active";
28295
29269
  let formationResult = null;
28296
29270
  let formationNote = "";
28297
- if (useFormation && !topicKey && !progress) {
29271
+ if (useFormation && !topicKey && !progress && !longTerm) {
28298
29272
  let currentFormationStage = "setup";
28299
29273
  const completedFormationStages = {};
28300
29274
  const formationStartTime = Date.now();
@@ -28445,7 +29419,7 @@ ${modeIcon} Formation[active]: ${formationResult.evaluation.category} (${formati
28445
29419
  }
28446
29420
  let compactAction = "";
28447
29421
  let compactMerged = false;
28448
- if (!useFormation && !topicKey && !progress) {
29422
+ if (!useFormation && !topicKey && !progress && !longTerm) {
28449
29423
  try {
28450
29424
  const searchResult = await compactSearch({
28451
29425
  query: `${title} ${narrative.substring(0, 200)}`,
@@ -28602,9 +29576,33 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
28602
29576
  sourceDetail: "explicit",
28603
29577
  valueCategory: formationResult?.evaluation.category,
28604
29578
  createdByAgentId: currentAgentId,
28605
- visibility,
29579
+ visibility: requestedVisibility,
28606
29580
  visibilityReader: reader
28607
29581
  });
29582
+ let longTermNote = "";
29583
+ if (longTerm) {
29584
+ try {
29585
+ const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
29586
+ const promoted = await promoteObservationToLongTermMemory2({
29587
+ dataDir: projectDir2,
29588
+ observation: obs,
29589
+ scope: longTerm.scope,
29590
+ kind: longTerm.kind,
29591
+ tags: longTerm.tags,
29592
+ applicability: longTerm.applicability,
29593
+ reader: {
29594
+ projectId: project.id,
29595
+ ...reader.agentId ? { agentId: reader.agentId } : {},
29596
+ ...reader.isTeamMember ? { isTeamMember: true } : {}
29597
+ }
29598
+ });
29599
+ longTermNote = `
29600
+ Long-term candidate: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}). Review it with the CLI before it is delivered automatically.`;
29601
+ } catch (longTermError) {
29602
+ longTermNote = `
29603
+ [WARN] Observation stored, but long-term candidate was not created: ${longTermError instanceof Error ? longTermError.message : "unknown error"}`;
29604
+ }
29605
+ }
28608
29606
  await graphManager.addObservations([
28609
29607
  { entityName, contents: [`[#${obs.id}] ${title}`] }
28610
29608
  ]);
@@ -28621,7 +29619,7 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
28621
29619
  const enrichment = enrichmentParts.length > 0 ? `
28622
29620
  Auto-enriched: ${enrichmentParts.join(", ")}` : "";
28623
29621
  const action = upserted ? "[UPDATED] Updated" : "[OK] Stored";
28624
- if (!useFormation && !topicKey && !progress) {
29622
+ if (!useFormation && !topicKey && !progress && !longTerm) {
28625
29623
  const shadowFormation = async () => {
28626
29624
  let oldCompactDecision = null;
28627
29625
  try {
@@ -28711,7 +29709,7 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
28711
29709
  {
28712
29710
  type: "text",
28713
29711
  text: `${action} observation #${obs.id} "${title}" (~${obs.tokens} tokens)
28714
- Entity: ${entityName} | Type: ${type} | Project: ${project.id}${obs.topicKey ? ` | Topic: ${obs.topicKey}` : ""}${compactAction}${compressionNote}${enrichment}${formationNote}${attributionWarning}`
29712
+ Entity: ${entityName} | Type: ${type} | Project: ${project.id}${obs.topicKey ? ` | Topic: ${obs.topicKey}` : ""}${compactAction}${compressionNote}${enrichment}${formationNote}${attributionWarning}${longTermNote}`
28715
29713
  }
28716
29714
  ]
28717
29715
  };
@@ -29084,7 +30082,8 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
29084
30082
  } : {},
29085
30083
  ...external.outline ? { semanticCode: external.outline } : {},
29086
30084
  providerQuality: external.quality,
29087
- ...external.caution ? { runtimeCautions: [{ kind: "external-codegraph-fallback", message: external.caution }] } : {}
30085
+ ...external.caution ? { runtimeCautions: [{ kind: "external-codegraph-fallback", message: external.caution }] } : {},
30086
+ reader: getObservationReader()
29088
30087
  });
29089
30088
  const text = buildContextPackPrompt2(pack);
29090
30089
  return {
@@ -29758,7 +30757,7 @@ ${actions.join("\n")}`
29758
30757
  "memorix_detail",
29759
30758
  {
29760
30759
  title: "Memory Details",
29761
- description: 'Fetch full observation or mini-skill details \u2014 includes source kind (explicit memory / hook trace / git evidence), value category, and cross-references (~500-1000 tokens each). Do not re-fetch content already covered by a complete memorix_project_context brief unless a specific fact is still missing or the user asks for deeper history; provide purpose when intentionally expanding. Always use memorix_search first to find relevant IDs, then fetch only what you need. Accepts typed refs from search results (e.g. "obs:42", "skill:3") via the typedRefs field, or legacy numeric ids / object refs for backward compatibility.',
30760
+ description: 'Fetch full observation, mini-skill, or curated durable-memory details \u2014 includes source kind (explicit memory / hook trace / git evidence), value category, and cross-references (~500-1000 tokens each). Do not re-fetch content already covered by a complete memorix_project_context brief unless a specific fact is still missing or the user asks for deeper history; provide purpose when intentionally expanding. Always use memorix_search first to find relevant IDs, then fetch only what you need. Accepts typed refs from search results (e.g. "obs:42", "skill:3") and durable refs from a project brief (e.g. "durable:<uuid>") via the typedRefs field, or legacy numeric ids / object refs for backward compatibility.',
29762
30761
  inputSchema: {
29763
30762
  ids: z2.array(z2.number()).optional().describe("Observation IDs to fetch (legacy, from memorix_search results)"),
29764
30763
  refs: z2.array(
@@ -29767,7 +30766,7 @@ ${actions.join("\n")}`
29767
30766
  projectId: z2.string().optional().describe("Project ID for global-search disambiguation")
29768
30767
  })
29769
30768
  ).optional().describe("Explicit observation refs. Prefer this for global search results."),
29770
- typedRefs: z2.array(z2.string()).optional().describe('Typed memory refs from search results, e.g. "obs:42", "skill:3", "obs:42@org/proj"'),
30769
+ typedRefs: z2.array(z2.string()).optional().describe('Typed memory refs from search results or a project brief, e.g. "obs:42", "skill:3", "durable:<uuid>", "obs:42@org/proj"'),
29771
30770
  purpose: z2.string().optional().describe(
29772
30771
  "Why this must expand beyond the latest Autopilot brief. Name the missing fact or the user's explicit request."
29773
30772
  ),
@@ -29780,14 +30779,19 @@ ${actions.join("\n")}`
29780
30779
  const safeIds = coerceNumberArray(ids);
29781
30780
  const safeRefs = coerceObservationRefs(refs);
29782
30781
  const safeTypedRefs = coerceStringArray(typedRefs);
29783
- const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id) || safeTypedRefs.some((ref) => ref.includes("@") && !ref.endsWith(`@${project.id}`));
30782
+ const durableIds = safeTypedRefs.flatMap((ref) => {
30783
+ const match = /^durable:([0-9a-f-]{36})$/i.exec(ref.trim());
30784
+ return match ? [match[1]] : [];
30785
+ });
30786
+ const observationTypedRefs = safeTypedRefs.filter((ref) => !/^durable:/i.test(ref.trim()));
30787
+ const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id) || observationTypedRefs.some((ref) => ref.includes("@") && !ref.endsWith(`@${project.id}`));
29784
30788
  if (!hasCrossProjectRef) {
29785
30789
  const boundary = requireExplicitAutopilotExpansion("memorix_detail", purpose);
29786
30790
  if (boundary) return boundary;
29787
30791
  const requestedObservationIds = [
29788
30792
  ...safeIds,
29789
30793
  ...safeRefs.map((ref) => ref.id),
29790
- ...safeTypedRefs.flatMap((ref) => {
30794
+ ...observationTypedRefs.flatMap((ref) => {
29791
30795
  const match = /^obs:(\d+)(?:@.+)?$/i.exec(ref.trim());
29792
30796
  return match ? [Number(match[1])] : [];
29793
30797
  })
@@ -29795,15 +30799,36 @@ ${actions.join("\n")}`
29795
30799
  const duplicate = blockCoveredAutopilotEvidence("memorix_detail", requestedObservationIds, force);
29796
30800
  if (duplicate) return duplicate;
29797
30801
  }
29798
- let result;
30802
+ const formatted = [];
29799
30803
  try {
29800
- const reader = getObservationReader(hasCrossProjectRef ? "global" : "project");
29801
- if (safeTypedRefs.length > 0) {
29802
- result = await compactDetail(safeTypedRefs, { reader });
29803
- } else if (safeRefs.length > 0) {
29804
- result = await compactDetail(safeRefs, { reader });
29805
- } else {
29806
- result = await compactDetail(safeIds.map((id) => ({ id, projectId: project.id })), { reader });
30804
+ if (durableIds.length > 0) {
30805
+ const [{ getLongTermMemoryDetail: getLongTermMemoryDetail2 }, { resolveLocalMemoryOwner: resolveLocalMemoryOwner2 }] = await Promise.all([
30806
+ Promise.resolve().then(() => (init_long_term(), long_term_exports)),
30807
+ Promise.resolve().then(() => (init_owner(), owner_exports))
30808
+ ]);
30809
+ const owner = await resolveLocalMemoryOwner2(projectDir2, { create: false });
30810
+ const observationReader = getObservationReader();
30811
+ const durableReader = {
30812
+ projectId: project.id,
30813
+ ...owner ? { ownerId: owner.id } : {},
30814
+ ...observationReader.agentId ? { agentId: observationReader.agentId } : {},
30815
+ ...observationReader.isTeamMember ? { isTeamMember: true } : {}
30816
+ };
30817
+ const details = await Promise.all(durableIds.slice(0, 5).map(async (id) => {
30818
+ try {
30819
+ return await getLongTermMemoryDetail2({ dataDir: projectDir2, id, reader: durableReader });
30820
+ } catch (error) {
30821
+ return { error: error instanceof Error ? error.message : String(error), id };
30822
+ }
30823
+ }));
30824
+ formatted.push(JSON.stringify({ durableMemory: details }, null, 2));
30825
+ }
30826
+ if (observationTypedRefs.length > 0 || safeRefs.length > 0 || safeIds.length > 0) {
30827
+ const reader = getObservationReader(hasCrossProjectRef ? "global" : "project");
30828
+ const result = observationTypedRefs.length > 0 ? await compactDetail(observationTypedRefs, { reader }) : safeRefs.length > 0 ? await compactDetail(safeRefs, { reader }) : await compactDetail(safeIds.map((id) => ({ id, projectId: project.id })), { reader });
30829
+ formatted.push(
30830
+ result.documents.length > 0 ? result.formatted : observationTypedRefs.length > 0 ? `No memories found for refs: ${observationTypedRefs.join(", ")}` : safeRefs.length > 0 ? `No memories found for refs: ${safeRefs.map((ref) => `${ref.projectId ?? "current"}#${ref.id}`).join(", ")}` : `No memories found for IDs: ${safeIds.join(", ")}`
30831
+ );
29807
30832
  }
29808
30833
  } catch (err) {
29809
30834
  return { content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }], isError: true };
@@ -29812,7 +30837,7 @@ ${actions.join("\n")}`
29812
30837
  content: [
29813
30838
  {
29814
30839
  type: "text",
29815
- text: result.documents.length > 0 ? result.formatted : safeTypedRefs.length > 0 ? `No memories found for refs: ${safeTypedRefs.join(", ")}` : safeRefs.length > 0 ? `No memories found for refs: ${safeRefs.map((ref) => `${ref.projectId ?? "current"}#${ref.id}`).join(", ")}` : `No memories found for IDs: ${safeIds.join(", ")}`
30840
+ text: formatted.length > 0 ? formatted.join("\n\n") : safeTypedRefs.length > 0 ? `No memories found for refs: ${safeTypedRefs.join(", ")}` : safeRefs.length > 0 ? `No memories found for refs: ${safeRefs.map((ref) => `${ref.projectId ?? "current"}#${ref.id}`).join(", ")}` : `No memories found for IDs: ${safeIds.join(", ")}`
29816
30841
  }
29817
30842
  ]
29818
30843
  };
@@ -30532,9 +31557,9 @@ ${skill2.content}` }]
30532
31557
  lines.push(`- **Description**: ${sk.description}`);
30533
31558
  lines.push(`- **Observations**: ${sk.content.split("\n").length} lines of knowledge`);
30534
31559
  if (write && target) {
30535
- const path26 = engine.writeSkill(sk, target);
30536
- if (path26) {
30537
- lines.push(`- [OK] **Written**: \`${path26}\``);
31560
+ const path27 = engine.writeSkill(sk, target);
31561
+ if (path27) {
31562
+ lines.push(`- [OK] **Written**: \`${path27}\``);
30538
31563
  } else {
30539
31564
  lines.push(`- [ERROR] Failed to write`);
30540
31565
  }
@@ -32153,11 +33178,11 @@ async function createMemoryClient(options) {
32153
33178
  `[memorix-sdk] No Git repository found at "${projectRoot}". Memorix requires a Git-tracked project for identity resolution.`
32154
33179
  );
32155
33180
  }
32156
- const path26 = await import("path");
33181
+ const path27 = await import("path");
32157
33182
  const os4 = await import("os");
32158
- const dataDir = path26.join(os4.homedir(), ".memorix", "data", project.id.replace(/\//g, path26.sep));
32159
- const fs19 = await import("fs");
32160
- fs19.mkdirSync(dataDir, { recursive: true });
33183
+ const dataDir = path27.join(os4.homedir(), ".memorix", "data", project.id.replace(/\//g, path27.sep));
33184
+ const fs20 = await import("fs");
33185
+ fs20.mkdirSync(dataDir, { recursive: true });
32161
33186
  const client = new MemoryClient(project.id, projectRoot, dataDir);
32162
33187
  await client._init(silent);
32163
33188
  return client;