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/index.js CHANGED
@@ -149,15 +149,15 @@ function loadSqlite() {
149
149
  throw new Error("[memorix] SQLite is unavailable (better-sqlite3, node:sqlite, and bun:sqlite failed)");
150
150
  }
151
151
  }
152
- function createDatabase(path26, options) {
152
+ function createDatabase(path27, options) {
153
153
  const Sqlite = loadSqlite();
154
154
  try {
155
- return addCompatibility(instantiateDatabase(Sqlite, path26, options));
155
+ return addCompatibility(instantiateDatabase(Sqlite, path27, options));
156
156
  } catch (error) {
157
157
  if (driver !== "better-sqlite3" || !isNativeBindingFailure(error)) throw error;
158
158
  Database = loadNodeSqlite();
159
159
  driver = "node:sqlite";
160
- return addCompatibility(instantiateDatabase(Database, path26, options));
160
+ return addCompatibility(instantiateDatabase(Database, path27, options));
161
161
  }
162
162
  }
163
163
  var Database, driver, requireFromHere;
@@ -284,7 +284,7 @@ function closeAllDatabases() {
284
284
  _dbCache.delete(key);
285
285
  }
286
286
  }
287
- 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;
287
+ 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;
288
288
  var init_sqlite_db = __esm({
289
289
  "src/store/sqlite-db.ts"() {
290
290
  "use strict";
@@ -710,6 +710,57 @@ CREATE TABLE IF NOT EXISTS knowledge_claim_events (
710
710
  " FOREIGN KEY (workflowId) REFERENCES knowledge_workflows(id) ON DELETE CASCADE",
711
711
  ");"
712
712
  ].join("\n");
713
+ CREATE_LONG_TERM_MEMORIES_TABLE = `
714
+ CREATE TABLE IF NOT EXISTS long_term_memories (
715
+ id TEXT PRIMARY KEY,
716
+ originProjectId TEXT NOT NULL,
717
+ ownerId TEXT NOT NULL,
718
+ scope TEXT NOT NULL,
719
+ kind TEXT NOT NULL,
720
+ state TEXT NOT NULL,
721
+ portability TEXT NOT NULL DEFAULT 'project-bound',
722
+ title TEXT NOT NULL,
723
+ content TEXT NOT NULL,
724
+ factsJson TEXT NOT NULL DEFAULT '[]',
725
+ tagsJson TEXT NOT NULL DEFAULT '[]',
726
+ applicability TEXT,
727
+ origin TEXT NOT NULL,
728
+ createdAt TEXT NOT NULL,
729
+ updatedAt TEXT NOT NULL,
730
+ qualifiedAt TEXT,
731
+ approvedAt TEXT,
732
+ archivedAt TEXT,
733
+ supersededBy TEXT,
734
+ lastValidatedAt TEXT,
735
+ accessCount INTEGER NOT NULL DEFAULT 0,
736
+ lastAccessedAt TEXT
737
+ );
738
+ `;
739
+ CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE = `
740
+ CREATE TABLE IF NOT EXISTS long_term_memory_evidence (
741
+ id TEXT PRIMARY KEY,
742
+ memoryId TEXT NOT NULL,
743
+ kind TEXT NOT NULL,
744
+ referenceId TEXT NOT NULL,
745
+ relation TEXT NOT NULL,
746
+ locator TEXT,
747
+ capturedHash TEXT,
748
+ createdAt TEXT NOT NULL,
749
+ FOREIGN KEY (memoryId) REFERENCES long_term_memories(id) ON DELETE CASCADE
750
+ );
751
+ `;
752
+ CREATE_LONG_TERM_MEMORY_EVENTS_TABLE = `
753
+ CREATE TABLE IF NOT EXISTS long_term_memory_events (
754
+ id TEXT PRIMARY KEY,
755
+ memoryId TEXT NOT NULL,
756
+ kind TEXT NOT NULL,
757
+ fromState TEXT,
758
+ toState TEXT,
759
+ detail TEXT,
760
+ createdAt TEXT NOT NULL,
761
+ FOREIGN KEY (memoryId) REFERENCES long_term_memories(id) ON DELETE CASCADE
762
+ );
763
+ `;
713
764
  CREATE_MAINTENANCE_JOBS_TABLE = `
714
765
  CREATE TABLE IF NOT EXISTS maintenance_jobs (
715
766
  id TEXT PRIMARY KEY,
@@ -805,6 +856,11 @@ CREATE INDEX IF NOT EXISTS idx_knowledge_page_claims_claim ON knowledge_page_cla
805
856
  CREATE INDEX IF NOT EXISTS idx_knowledge_proposals_workspace_status ON knowledge_proposals(workspaceId, status, createdAt DESC);
806
857
  CREATE INDEX IF NOT EXISTS idx_knowledge_workflows_workspace_status ON knowledge_workflows(workspaceId, status, updatedAt DESC);
807
858
  CREATE INDEX IF NOT EXISTS idx_knowledge_workflow_runs_project_workflow ON knowledge_workflow_runs(projectId, workflowId, startedAt DESC);
859
+ CREATE INDEX IF NOT EXISTS idx_long_term_memories_owner_state ON long_term_memories(ownerId, state, updatedAt DESC);
860
+ CREATE INDEX IF NOT EXISTS idx_long_term_memories_project_state ON long_term_memories(originProjectId, state, updatedAt DESC);
861
+ CREATE INDEX IF NOT EXISTS idx_long_term_memories_scope_portability ON long_term_memories(scope, portability, state, updatedAt DESC);
862
+ CREATE INDEX IF NOT EXISTS idx_long_term_memory_evidence_memory ON long_term_memory_evidence(memoryId, createdAt);
863
+ CREATE INDEX IF NOT EXISTS idx_long_term_memory_events_memory ON long_term_memory_events(memoryId, createdAt);
808
864
  CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_ready ON maintenance_jobs(status, run_after);
809
865
  CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_project ON maintenance_jobs(project_id, status, run_after);
810
866
  CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_targets(updated_at);
@@ -893,6 +949,19 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
893
949
  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)");
894
950
  db2.exec("CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key)");
895
951
  }
952
+ },
953
+ {
954
+ id: "1.3-long-term-memory",
955
+ apply: (db2) => {
956
+ db2.exec(CREATE_LONG_TERM_MEMORIES_TABLE);
957
+ db2.exec(CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE);
958
+ db2.exec(CREATE_LONG_TERM_MEMORY_EVENTS_TABLE);
959
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_owner_state ON long_term_memories(ownerId, state, updatedAt DESC)");
960
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_project_state ON long_term_memories(originProjectId, state, updatedAt DESC)");
961
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_scope_portability ON long_term_memories(scope, portability, state, updatedAt DESC)");
962
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memory_evidence_memory ON long_term_memory_evidence(memoryId, createdAt)");
963
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memory_events_memory ON long_term_memory_events(memoryId, createdAt)");
964
+ }
896
965
  }
897
966
  ];
898
967
  _dbCache = /* @__PURE__ */ new Map();
@@ -2728,9 +2797,9 @@ function parseTomlConfig(content, filePath) {
2728
2797
  }
2729
2798
  return root;
2730
2799
  }
2731
- function ensureTable(root, path26) {
2800
+ function ensureTable(root, path27) {
2732
2801
  let cursor = root;
2733
- for (const part of path26) {
2802
+ for (const part of path27) {
2734
2803
  const value = cursor[part];
2735
2804
  if (!value || typeof value !== "object" || Array.isArray(value)) {
2736
2805
  cursor[part] = {};
@@ -3606,9 +3675,10 @@ function parseBatchLimit(error) {
3606
3675
  }
3607
3676
  return null;
3608
3677
  }
3609
- async function fetchWithRetry(url, apiKey, body, attempt = 0) {
3678
+ async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0) {
3610
3679
  const controller = new AbortController();
3611
- const timeout = setTimeout(() => controller.abort(), 1e4);
3680
+ const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) ? Math.max(100, Math.min(Math.floor(options.timeoutMs), 6e4)) : 1e4;
3681
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
3612
3682
  let response;
3613
3683
  try {
3614
3684
  response = await fetch(url, {
@@ -3623,7 +3693,7 @@ async function fetchWithRetry(url, apiKey, body, attempt = 0) {
3623
3693
  } catch (err) {
3624
3694
  clearTimeout(timeout);
3625
3695
  if (err instanceof Error && err.name === "AbortError") {
3626
- throw new Error(`Embedding API timeout after 10s: ${url}`);
3696
+ throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
3627
3697
  }
3628
3698
  throw err;
3629
3699
  }
@@ -3631,13 +3701,13 @@ async function fetchWithRetry(url, apiKey, body, attempt = 0) {
3631
3701
  if (response.ok) {
3632
3702
  return response.json();
3633
3703
  }
3634
- if ((response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
3704
+ if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
3635
3705
  const delay = BASE_DELAY_MS * Math.pow(2, attempt);
3636
3706
  const retryAfter = response.headers.get("retry-after");
3637
3707
  const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : delay;
3638
3708
  console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
3639
3709
  await new Promise((resolve2) => setTimeout(resolve2, waitMs));
3640
- return fetchWithRetry(url, apiKey, body, attempt + 1);
3710
+ return fetchWithRetry(url, apiKey, body, options, attempt + 1);
3641
3711
  }
3642
3712
  const errorText2 = await response.text().catch(() => "unknown error");
3643
3713
  throw new Error(`Embedding API error (${response.status}): ${errorText2}`);
@@ -3693,7 +3763,10 @@ var init_api_provider = __esm({
3693
3763
  }
3694
3764
  } else {
3695
3765
  if (!allowNetworkProbe) return null;
3696
- probeDimensions = await _APIEmbeddingProvider.probeAPI(config);
3766
+ probeDimensions = await _APIEmbeddingProvider.probeAPI(config, {
3767
+ timeoutMs: options.requestTimeoutMs,
3768
+ retry: options.retry
3769
+ });
3697
3770
  console.error(`[memorix] API embedding: ${config.model} @ ${config.baseUrl} (${probeDimensions}d)`);
3698
3771
  saveCachedDims(config, probeDimensions).catch(() => {
3699
3772
  });
@@ -3735,7 +3808,7 @@ var init_api_provider = __esm({
3735
3808
  baseUrl = baseUrl.replace(/\/+$/, "");
3736
3809
  return { apiKey, baseUrl, model, requestedDimensions };
3737
3810
  }
3738
- static async probeAPI(config) {
3811
+ static async probeAPI(config, options) {
3739
3812
  const body = {
3740
3813
  model: config.model,
3741
3814
  input: "dimension probe"
@@ -3746,14 +3819,15 @@ var init_api_provider = __esm({
3746
3819
  const response = await fetchWithRetry(
3747
3820
  `${config.baseUrl}/embeddings`,
3748
3821
  config.apiKey,
3749
- body
3822
+ body,
3823
+ options
3750
3824
  );
3751
3825
  if (response.data.length === 0 || !response.data[0].embedding) {
3752
3826
  throw new Error("API probe returned no embeddings; check model name and API key");
3753
3827
  }
3754
3828
  return response.data[0].embedding.length;
3755
3829
  }
3756
- async embed(text) {
3830
+ async embed(text, options) {
3757
3831
  const normalized = normalizeText(text);
3758
3832
  const hash4 = textHash2(normalized, this.cacheKeyNamespace);
3759
3833
  if (diskCacheLoaded) {
@@ -3771,7 +3845,8 @@ var init_api_provider = __esm({
3771
3845
  const response = await fetchWithRetry(
3772
3846
  `${this.config.baseUrl}/embeddings`,
3773
3847
  this.config.apiKey,
3774
- body
3848
+ body,
3849
+ options
3775
3850
  );
3776
3851
  const embedding2 = response.data[0].embedding;
3777
3852
  if (embedding2.length !== this.dimensions) {
@@ -3805,7 +3880,7 @@ var init_api_provider = __esm({
3805
3880
  scheduleDiskSave();
3806
3881
  return embedding;
3807
3882
  }
3808
- async embedBatch(texts) {
3883
+ async embedBatch(texts, options) {
3809
3884
  await ensureDiskCacheLoaded();
3810
3885
  const normalizedTexts = texts.map(normalizeText);
3811
3886
  const results = new Array(texts.length);
@@ -3839,7 +3914,8 @@ var init_api_provider = __esm({
3839
3914
  const response = await fetchWithRetry(
3840
3915
  `${this.config.baseUrl}/embeddings`,
3841
3916
  this.config.apiKey,
3842
- body
3917
+ body,
3918
+ options
3843
3919
  );
3844
3920
  this.trackUsage(response);
3845
3921
  for (const item of response.data) {
@@ -4016,9 +4092,9 @@ function wrapProvider(candidate) {
4016
4092
  return {
4017
4093
  name: candidate.name,
4018
4094
  dimensions: candidate.dimensions,
4019
- async embed(text) {
4095
+ async embed(text, options) {
4020
4096
  try {
4021
- return await candidate.embed(text);
4097
+ return await candidate.embed(text, options);
4022
4098
  } catch (error) {
4023
4099
  if (kind === "api" && getEmbeddingMode2() === "auto" && isTemporaryEmbeddingFailure(error)) {
4024
4100
  warnOnce("[memorix] API embedding temporarily unavailable \u2014 switching to local fallback provider");
@@ -4026,7 +4102,7 @@ function wrapProvider(candidate) {
4026
4102
  if (fallback) {
4027
4103
  provider = wrapProvider(fallback);
4028
4104
  warnOnce(`[memorix] Embedding fallback activated: ${provider.name} (${provider.dimensions}d)`);
4029
- return provider.embed(text);
4105
+ return provider.embed(text, options);
4030
4106
  }
4031
4107
  }
4032
4108
  if (isTemporaryEmbeddingFailure(error)) {
@@ -4035,9 +4111,9 @@ function wrapProvider(candidate) {
4035
4111
  throw error;
4036
4112
  }
4037
4113
  },
4038
- async embedBatch(texts) {
4114
+ async embedBatch(texts, options) {
4039
4115
  try {
4040
- return await candidate.embedBatch(texts);
4116
+ return await candidate.embedBatch(texts, options);
4041
4117
  } catch (error) {
4042
4118
  if (kind === "api" && getEmbeddingMode2() === "auto" && isTemporaryEmbeddingFailure(error)) {
4043
4119
  warnOnce("[memorix] API embedding temporarily unavailable \u2014 switching to local fallback provider");
@@ -4045,7 +4121,7 @@ function wrapProvider(candidate) {
4045
4121
  if (fallback) {
4046
4122
  provider = wrapProvider(fallback);
4047
4123
  warnOnce(`[memorix] Embedding fallback activated: ${provider.name} (${provider.dimensions}d)`);
4048
- return provider.embedBatch(texts);
4124
+ return provider.embedBatch(texts, options);
4049
4125
  }
4050
4126
  }
4051
4127
  if (isTemporaryEmbeddingFailure(error)) {
@@ -4101,14 +4177,14 @@ async function getEmbeddingProvider(options = {}) {
4101
4177
  return provider;
4102
4178
  }
4103
4179
  if (mode === "api") {
4104
- const initialized = await createAPIProvider();
4180
+ const initialized = await createAPIProvider(options);
4105
4181
  if (!initialized) return null;
4106
4182
  provider = wrapProvider(initialized);
4107
4183
  warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
4108
4184
  return provider;
4109
4185
  }
4110
4186
  if (hasAPIEmbeddingConfig()) {
4111
- const initialized = await createAPIProvider();
4187
+ const initialized = await createAPIProvider(options);
4112
4188
  if (initialized) {
4113
4189
  provider = wrapProvider(initialized);
4114
4190
  warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
@@ -8964,12 +9040,12 @@ import { createHash as createHash4 } from "crypto";
8964
9040
  function digest(input) {
8965
9041
  return createHash4("sha256").update(input).digest("hex").slice(0, 16);
8966
9042
  }
8967
- function normalizeCodePath(path26) {
8968
- return path26.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
9043
+ function normalizeCodePath(path27) {
9044
+ return path27.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
8969
9045
  }
8970
- function makeCodeFileId(projectId, path26) {
9046
+ function makeCodeFileId(projectId, path27) {
8971
9047
  return `file:${digest(`${projectId}
8972
- ${normalizeCodePath(path26)}`)}`;
9048
+ ${normalizeCodePath(path27)}`)}`;
8973
9049
  }
8974
9050
  function makeCodeSymbolId(input) {
8975
9051
  return `symbol:${digest([
@@ -9443,8 +9519,8 @@ var init_store = __esm({
9443
9519
  });
9444
9520
  tx();
9445
9521
  }
9446
- getFile(projectId, path26) {
9447
- const row = this.db.prepare(`SELECT * FROM code_files WHERE projectId = ? AND path = ?`).get(projectId, normalizeCodePath(path26));
9522
+ getFile(projectId, path27) {
9523
+ const row = this.db.prepare(`SELECT * FROM code_files WHERE projectId = ? AND path = ?`).get(projectId, normalizeCodePath(path27));
9448
9524
  return row ? rowToFile(row) : null;
9449
9525
  }
9450
9526
  listFiles(projectId) {
@@ -9679,30 +9755,30 @@ function normalizeCodeGraphExcludePatterns(exclude) {
9679
9755
  ...(exclude ?? []).map((pattern) => pattern.trim()).filter(Boolean)
9680
9756
  ])];
9681
9757
  }
9682
- function isCodeGraphExcludedPath(path26, exclude) {
9683
- const normalized = normalizeCodePath2(path26);
9758
+ function isCodeGraphExcludedPath(path27, exclude) {
9759
+ const normalized = normalizeCodePath2(path27);
9684
9760
  return normalizeCodeGraphExcludePatterns(exclude).some((pattern) => matchesPattern(normalized, normalizeCodePath2(pattern)));
9685
9761
  }
9686
- function normalizeCodePath2(path26) {
9687
- return path26.replace(/\\/g, "/").replace(/^\.\/+/, "");
9762
+ function normalizeCodePath2(path27) {
9763
+ return path27.replace(/\\/g, "/").replace(/^\.\/+/, "");
9688
9764
  }
9689
- function matchesPattern(path26, pattern) {
9765
+ function matchesPattern(path27, pattern) {
9690
9766
  if (pattern.endsWith("/**")) {
9691
9767
  const base = pattern.slice(0, -3);
9692
9768
  if (base.startsWith("**/")) {
9693
9769
  const suffix = base.slice(3);
9694
- return path26 === suffix || path26.endsWith(`/${suffix}`) || path26.includes(`/${suffix}/`);
9770
+ return path27 === suffix || path27.endsWith(`/${suffix}`) || path27.includes(`/${suffix}/`);
9695
9771
  }
9696
9772
  if (!base.includes("/")) {
9697
- return path26 === base || path26.startsWith(`${base}/`) || path26.includes(`/${base}/`);
9773
+ return path27 === base || path27.startsWith(`${base}/`) || path27.includes(`/${base}/`);
9698
9774
  }
9699
- return path26 === base || path26.startsWith(`${base}/`);
9775
+ return path27 === base || path27.startsWith(`${base}/`);
9700
9776
  }
9701
9777
  if (pattern.startsWith("**/")) {
9702
9778
  const suffix = pattern.slice(3);
9703
- return path26 === suffix || path26.endsWith(`/${suffix}`);
9779
+ return path27 === suffix || path27.endsWith(`/${suffix}`);
9704
9780
  }
9705
- return path26 === pattern || path26.startsWith(`${pattern}/`);
9781
+ return path27 === pattern || path27.startsWith(`${pattern}/`);
9706
9782
  }
9707
9783
  var DEFAULT_CODEGRAPH_EXCLUDES;
9708
9784
  var init_exclude = __esm({
@@ -9747,12 +9823,12 @@ import { join as join4, relative } from "path";
9747
9823
  function hashText(text) {
9748
9824
  return createHash6("sha256").update(text).digest("hex");
9749
9825
  }
9750
- function extension(path26) {
9751
- const index = path26.lastIndexOf(".");
9752
- return index === -1 ? "" : path26.slice(index);
9826
+ function extension(path27) {
9827
+ const index = path27.lastIndexOf(".");
9828
+ return index === -1 ? "" : path27.slice(index);
9753
9829
  }
9754
- function languageForPath(path26) {
9755
- const ext = extension(path26);
9830
+ function languageForPath(path27) {
9831
+ const ext = extension(path27);
9756
9832
  return LANGUAGE_BY_EXTENSION.get(ext) ?? "unknown";
9757
9833
  }
9758
9834
  function resolveMaxFileBytes(value) {
@@ -10233,7 +10309,7 @@ function resolveObservationCodeRefs(obs, lookup) {
10233
10309
  }
10234
10310
  function createStoreLookup(store, projectId) {
10235
10311
  return {
10236
- findFile: (path26) => store.getFile(projectId, path26) ?? void 0,
10312
+ findFile: (path27) => store.getFile(projectId, path27) ?? void 0,
10237
10313
  findSymbols: (names, hintedFileIds) => store.findSymbolsByNames(projectId, names, hintedFileIds)
10238
10314
  };
10239
10315
  }
@@ -10246,7 +10322,7 @@ function createSnapshotLookup(files, symbols) {
10246
10322
  symbolsByName.set(symbol.name, group);
10247
10323
  }
10248
10324
  return {
10249
- findFile: (path26) => filesByPath.get(normalizeCodePath(path26)),
10325
+ findFile: (path27) => filesByPath.get(normalizeCodePath(path27)),
10250
10326
  findSymbols: (names, hintedFileIds) => {
10251
10327
  const hinted = new Set(hintedFileIds);
10252
10328
  const found = [];
@@ -11863,8 +11939,8 @@ __export(task_lens_exports, {
11863
11939
  scoreLensSource: () => scoreLensSource,
11864
11940
  shouldShowLensSource: () => shouldShowLensSource
11865
11941
  });
11866
- function normalizePath2(path26) {
11867
- return path26.replace(/\\/g, "/");
11942
+ function normalizePath2(path27) {
11943
+ return path27.replace(/\\/g, "/");
11868
11944
  }
11869
11945
  function tokenize2(text) {
11870
11946
  return (text.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
@@ -11926,8 +12002,8 @@ function isContinuationTask(task) {
11926
12002
  (keyword) => containsTaskKeyword(normalized, keyword)
11927
12003
  );
11928
12004
  }
11929
- function pathKindScore(path26, lens) {
11930
- const normalized = normalizePath2(path26).toLowerCase();
12005
+ function pathKindScore(path27, lens) {
12006
+ const normalized = normalizePath2(path27).toLowerCase();
11931
12007
  const name = normalized.split("/").pop() ?? normalized;
11932
12008
  const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
11933
12009
  const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
@@ -11975,8 +12051,8 @@ function sourceTaskMatchScore(source, task) {
11975
12051
  if (matches === 1) return 24;
11976
12052
  return 0;
11977
12053
  }
11978
- function fallbackPathRank(path26) {
11979
- const normalized = normalizePath2(path26);
12054
+ function fallbackPathRank(path27) {
12055
+ const normalized = normalizePath2(path27);
11980
12056
  if (normalized.startsWith("src/")) return 0;
11981
12057
  if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
11982
12058
  if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
@@ -11985,11 +12061,11 @@ function fallbackPathRank(path26) {
11985
12061
  }
11986
12062
  function rankLensPaths(paths, lens, task) {
11987
12063
  const tokens = tokenize2(task ?? "");
11988
- return [...new Set(paths.map(normalizePath2))].map((path26, index) => ({
11989
- path: path26,
12064
+ return [...new Set(paths.map(normalizePath2))].map((path27, index) => ({
12065
+ path: path27,
11990
12066
  index,
11991
- score: pathKindScore(path26, lens) + tokenScore(path26, tokens),
11992
- fallback: fallbackPathRank(path26)
12067
+ score: pathKindScore(path27, lens) + tokenScore(path27, tokens),
12068
+ fallback: fallbackPathRank(path27)
11993
12069
  })).sort((a, b) => b.score - a.score || a.fallback - b.fallback || a.index - b.index || a.path.localeCompare(b.path)).map((item) => item.path);
11994
12070
  }
11995
12071
  function lensPathCandidates(lens) {
@@ -16241,6 +16317,827 @@ var init_session = __esm({
16241
16317
  }
16242
16318
  });
16243
16319
 
16320
+ // src/memory/owner.ts
16321
+ var owner_exports = {};
16322
+ __export(owner_exports, {
16323
+ resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
16324
+ });
16325
+ import { randomUUID as randomUUID8 } from "crypto";
16326
+ import { promises as fs15 } from "fs";
16327
+ import path19 from "path";
16328
+ function ownerPath(dataDir) {
16329
+ const resolved = path19.resolve(dataDir);
16330
+ const home = path19.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path19.dirname(resolved) : resolved;
16331
+ return path19.join(home, "memorix-user.json");
16332
+ }
16333
+ function normalizedOwnerId(value) {
16334
+ const trimmed = value?.trim();
16335
+ return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
16336
+ }
16337
+ async function readOwnerFile(filePath) {
16338
+ try {
16339
+ const value = JSON.parse(await fs15.readFile(filePath, "utf8"));
16340
+ const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
16341
+ return id ? { id, source: "local-file" } : void 0;
16342
+ } catch {
16343
+ return void 0;
16344
+ }
16345
+ }
16346
+ async function resolveLocalMemoryOwner(dataDir, options = {}) {
16347
+ const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
16348
+ if (configured) return { id: configured, source: "environment" };
16349
+ const filePath = ownerPath(dataDir);
16350
+ const existing = await readOwnerFile(filePath);
16351
+ if (existing || !options.create) return existing;
16352
+ await fs15.mkdir(path19.dirname(filePath), { recursive: true });
16353
+ const created = {
16354
+ version: 1,
16355
+ id: "local-" + randomUUID8(),
16356
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
16357
+ };
16358
+ try {
16359
+ await fs15.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
16360
+ return { id: created.id, source: "local-file" };
16361
+ } catch (error) {
16362
+ if (error?.code !== "EEXIST") throw error;
16363
+ const concurrent = await readOwnerFile(filePath);
16364
+ if (concurrent) return concurrent;
16365
+ throw new Error("Memorix could not establish a valid local memory owner identity.");
16366
+ }
16367
+ }
16368
+ var ID_PATTERN;
16369
+ var init_owner = __esm({
16370
+ "src/memory/owner.ts"() {
16371
+ "use strict";
16372
+ init_esm_shims();
16373
+ ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
16374
+ }
16375
+ });
16376
+
16377
+ // src/memory/long-term-store.ts
16378
+ import { randomUUID as randomUUID9 } from "crypto";
16379
+ function optionalText5(value) {
16380
+ return typeof value === "string" && value.trim() ? value : void 0;
16381
+ }
16382
+ function arrayValue2(value) {
16383
+ if (typeof value !== "string" || !value) return [];
16384
+ try {
16385
+ const parsed = JSON.parse(value);
16386
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
16387
+ } catch {
16388
+ return [];
16389
+ }
16390
+ }
16391
+ function rowToMemory(row) {
16392
+ return {
16393
+ id: row.id,
16394
+ originProjectId: row.originProjectId,
16395
+ ownerId: row.ownerId,
16396
+ scope: row.scope,
16397
+ kind: row.kind,
16398
+ state: row.state,
16399
+ portability: row.portability,
16400
+ title: row.title,
16401
+ content: row.content,
16402
+ facts: arrayValue2(row.factsJson),
16403
+ tags: arrayValue2(row.tagsJson),
16404
+ ...optionalText5(row.applicability) ? { applicability: row.applicability } : {},
16405
+ origin: row.origin,
16406
+ createdAt: row.createdAt,
16407
+ updatedAt: row.updatedAt,
16408
+ ...optionalText5(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
16409
+ ...optionalText5(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
16410
+ ...optionalText5(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
16411
+ ...optionalText5(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
16412
+ ...optionalText5(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
16413
+ accessCount: Number(row.accessCount ?? 0),
16414
+ ...optionalText5(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
16415
+ };
16416
+ }
16417
+ function rowToEvidence2(row) {
16418
+ return {
16419
+ id: row.id,
16420
+ memoryId: row.memoryId,
16421
+ kind: row.kind,
16422
+ referenceId: row.referenceId,
16423
+ relation: row.relation,
16424
+ ...optionalText5(row.locator) ? { locator: row.locator } : {},
16425
+ ...optionalText5(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
16426
+ createdAt: row.createdAt
16427
+ };
16428
+ }
16429
+ function rowToEvent2(row) {
16430
+ return {
16431
+ id: row.id,
16432
+ memoryId: row.memoryId,
16433
+ kind: row.kind,
16434
+ ...optionalText5(row.fromState) ? { fromState: row.fromState } : {},
16435
+ ...optionalText5(row.toState) ? { toState: row.toState } : {},
16436
+ ...optionalText5(row.detail) ? { detail: row.detail } : {},
16437
+ createdAt: row.createdAt
16438
+ };
16439
+ }
16440
+ var LongTermMemoryStore;
16441
+ var init_long_term_store = __esm({
16442
+ "src/memory/long-term-store.ts"() {
16443
+ "use strict";
16444
+ init_esm_shims();
16445
+ init_sqlite_db();
16446
+ LongTermMemoryStore = class {
16447
+ db = null;
16448
+ async init(dataDir) {
16449
+ this.db = getDatabase(dataDir);
16450
+ }
16451
+ transaction(fn) {
16452
+ return this.db.transaction(fn)();
16453
+ }
16454
+ get(id) {
16455
+ const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
16456
+ return row ? rowToMemory(row) : void 0;
16457
+ }
16458
+ list(options = {}) {
16459
+ const limit = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
16460
+ const clauses = [];
16461
+ const params = [];
16462
+ if (options.ownerId) {
16463
+ clauses.push("ownerId = ?");
16464
+ params.push(options.ownerId);
16465
+ }
16466
+ if (options.originProjectId) {
16467
+ clauses.push("originProjectId = ?");
16468
+ params.push(options.originProjectId);
16469
+ }
16470
+ const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
16471
+ const rows = this.db.prepare(
16472
+ "SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
16473
+ ).all(...params, limit);
16474
+ return rows.map(rowToMemory);
16475
+ }
16476
+ insert(memory) {
16477
+ this.db.prepare(`
16478
+ INSERT INTO long_term_memories (
16479
+ id, originProjectId, ownerId, scope, kind, state, portability,
16480
+ title, content, factsJson, tagsJson, applicability, origin,
16481
+ createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
16482
+ supersededBy, lastValidatedAt, accessCount, lastAccessedAt
16483
+ ) VALUES (
16484
+ @id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
16485
+ @title, @content, @factsJson, @tagsJson, @applicability, @origin,
16486
+ @createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
16487
+ @supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
16488
+ )
16489
+ `).run({
16490
+ ...memory,
16491
+ factsJson: JSON.stringify(memory.facts),
16492
+ tagsJson: JSON.stringify(memory.tags),
16493
+ applicability: memory.applicability ?? null,
16494
+ qualifiedAt: memory.qualifiedAt ?? null,
16495
+ approvedAt: memory.approvedAt ?? null,
16496
+ archivedAt: memory.archivedAt ?? null,
16497
+ supersededBy: memory.supersededBy ?? null,
16498
+ lastValidatedAt: memory.lastValidatedAt ?? null,
16499
+ lastAccessedAt: memory.lastAccessedAt ?? null
16500
+ });
16501
+ }
16502
+ update(memory) {
16503
+ this.db.prepare(`
16504
+ UPDATE long_term_memories SET
16505
+ scope = @scope,
16506
+ kind = @kind,
16507
+ state = @state,
16508
+ portability = @portability,
16509
+ title = @title,
16510
+ content = @content,
16511
+ factsJson = @factsJson,
16512
+ tagsJson = @tagsJson,
16513
+ applicability = @applicability,
16514
+ updatedAt = @updatedAt,
16515
+ qualifiedAt = @qualifiedAt,
16516
+ approvedAt = @approvedAt,
16517
+ archivedAt = @archivedAt,
16518
+ supersededBy = @supersededBy,
16519
+ lastValidatedAt = @lastValidatedAt,
16520
+ accessCount = @accessCount,
16521
+ lastAccessedAt = @lastAccessedAt
16522
+ WHERE id = @id
16523
+ `).run({
16524
+ ...memory,
16525
+ factsJson: JSON.stringify(memory.facts),
16526
+ tagsJson: JSON.stringify(memory.tags),
16527
+ applicability: memory.applicability ?? null,
16528
+ qualifiedAt: memory.qualifiedAt ?? null,
16529
+ approvedAt: memory.approvedAt ?? null,
16530
+ archivedAt: memory.archivedAt ?? null,
16531
+ supersededBy: memory.supersededBy ?? null,
16532
+ lastValidatedAt: memory.lastValidatedAt ?? null,
16533
+ lastAccessedAt: memory.lastAccessedAt ?? null
16534
+ });
16535
+ }
16536
+ insertEvidence(input) {
16537
+ const evidence = {
16538
+ id: randomUUID9(),
16539
+ ...input,
16540
+ createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
16541
+ };
16542
+ this.db.prepare(`
16543
+ INSERT INTO long_term_memory_evidence (
16544
+ id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
16545
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
16546
+ `).run(
16547
+ evidence.id,
16548
+ evidence.memoryId,
16549
+ evidence.kind,
16550
+ evidence.referenceId,
16551
+ evidence.relation,
16552
+ evidence.locator ?? null,
16553
+ evidence.capturedHash ?? null,
16554
+ evidence.createdAt
16555
+ );
16556
+ return evidence;
16557
+ }
16558
+ listEvidence(memoryId) {
16559
+ return this.db.prepare(`
16560
+ SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
16561
+ `).all(memoryId).map(rowToEvidence2);
16562
+ }
16563
+ recordEvent(input) {
16564
+ const event = {
16565
+ id: randomUUID9(),
16566
+ ...input,
16567
+ createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
16568
+ };
16569
+ this.db.prepare(`
16570
+ INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
16571
+ VALUES (?, ?, ?, ?, ?, ?, ?)
16572
+ `).run(
16573
+ event.id,
16574
+ event.memoryId,
16575
+ event.kind,
16576
+ event.fromState ?? null,
16577
+ event.toState ?? null,
16578
+ event.detail ?? null,
16579
+ event.createdAt
16580
+ );
16581
+ return event;
16582
+ }
16583
+ listEvents(memoryId) {
16584
+ return this.db.prepare(`
16585
+ SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
16586
+ `).all(memoryId).map(rowToEvent2);
16587
+ }
16588
+ markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
16589
+ const unique2 = [...new Set(ids.filter(Boolean))];
16590
+ if (unique2.length === 0) return;
16591
+ const statement = this.db.prepare(`
16592
+ UPDATE long_term_memories
16593
+ SET accessCount = accessCount + 1, lastAccessedAt = ?
16594
+ WHERE id = ?
16595
+ `);
16596
+ this.db.transaction(() => {
16597
+ for (const id of unique2) statement.run(at, id);
16598
+ })();
16599
+ }
16600
+ };
16601
+ }
16602
+ });
16603
+
16604
+ // src/memory/long-term-types.ts
16605
+ var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
16606
+ var init_long_term_types = __esm({
16607
+ "src/memory/long-term-types.ts"() {
16608
+ "use strict";
16609
+ init_esm_shims();
16610
+ LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
16611
+ LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
16612
+ LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
16613
+ LONG_TERM_MEMORY_EVIDENCE_KINDS = [
16614
+ "observation",
16615
+ "claim",
16616
+ "workflow",
16617
+ "session",
16618
+ "checkpoint",
16619
+ "git",
16620
+ "code",
16621
+ "test",
16622
+ "run",
16623
+ "document",
16624
+ "manual",
16625
+ "user"
16626
+ ];
16627
+ LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
16628
+ "supports",
16629
+ "verifies",
16630
+ "derives",
16631
+ "user-confirmed"
16632
+ ];
16633
+ }
16634
+ });
16635
+
16636
+ // src/memory/long-term.ts
16637
+ var long_term_exports = {};
16638
+ __export(long_term_exports, {
16639
+ approveLongTermMemory: () => approveLongTermMemory,
16640
+ archiveLongTermMemory: () => archiveLongTermMemory,
16641
+ canReadLongTermMemory: () => canReadLongTermMemory,
16642
+ createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
16643
+ createManualLongTermMemory: () => createManualLongTermMemory,
16644
+ getLongTermMemoryDetail: () => getLongTermMemoryDetail,
16645
+ isEligibleLongTermMemory: () => isEligibleLongTermMemory,
16646
+ listLongTermMemories: () => listLongTermMemories,
16647
+ promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
16648
+ qualifyLongTermMemory: () => qualifyLongTermMemory,
16649
+ renderLongTermMemorySummary: () => renderLongTermMemorySummary,
16650
+ selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
16651
+ supersedeLongTermMemory: () => supersedeLongTermMemory
16652
+ });
16653
+ import { randomUUID as randomUUID10 } from "crypto";
16654
+ function isOneOf(value, values) {
16655
+ return typeof value === "string" && values.includes(value);
16656
+ }
16657
+ function compactText2(value, field, max = MAX_TEXT_LENGTH) {
16658
+ if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
16659
+ const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
16660
+ if (!safe) throw new Error("Long-term memory " + field + " is required.");
16661
+ if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
16662
+ return safe;
16663
+ }
16664
+ function compactList(values, field) {
16665
+ if (!values) return [];
16666
+ if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
16667
+ return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText2(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
16668
+ }
16669
+ function normalizeEvidence(input) {
16670
+ if (!Array.isArray(input) || input.length === 0) {
16671
+ throw new Error("A long-term memory requires at least one source evidence reference.");
16672
+ }
16673
+ return input.map((evidence) => {
16674
+ if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
16675
+ throw new Error("Long-term memory evidence kind is invalid.");
16676
+ }
16677
+ if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
16678
+ throw new Error("Long-term memory evidence relation is invalid.");
16679
+ }
16680
+ return {
16681
+ kind: evidence.kind,
16682
+ referenceId: compactText2(evidence.referenceId, "evidence reference", 1e3),
16683
+ relation: evidence.relation,
16684
+ ...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator", 1e3) } : {},
16685
+ ...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash", 1e3) } : {}
16686
+ };
16687
+ });
16688
+ }
16689
+ function assertScope(scope) {
16690
+ if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
16691
+ }
16692
+ function assertKind(kind) {
16693
+ if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
16694
+ }
16695
+ function assertPortability(value) {
16696
+ if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
16697
+ throw new Error("Long-term memory portability is invalid.");
16698
+ }
16699
+ }
16700
+ function assertPortableEvidence(scope, portability, evidence) {
16701
+ if (portability !== "portable") return;
16702
+ if (scope !== "user") {
16703
+ throw new Error("Only user-scoped long-term memories may be portable across projects.");
16704
+ }
16705
+ if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
16706
+ 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.");
16707
+ }
16708
+ }
16709
+ function requireTeamReader(scope, reader) {
16710
+ if (scope === "team" && reader?.isTeamMember !== true) {
16711
+ throw new Error("Team long-term memory requires an explicit active team identity for this project.");
16712
+ }
16713
+ }
16714
+ function matchesScope(memory, reader) {
16715
+ if (memory.scope === "project") return memory.originProjectId === reader.projectId;
16716
+ if (memory.scope === "team") {
16717
+ return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
16718
+ }
16719
+ if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
16720
+ return memory.portability === "portable" || memory.originProjectId === reader.projectId;
16721
+ }
16722
+ function canReadLongTermMemory(memory, reader) {
16723
+ return matchesScope(memory, reader);
16724
+ }
16725
+ function isEligibleLongTermMemory(memory) {
16726
+ return memory.state === "qualified" || memory.state === "approved";
16727
+ }
16728
+ function taskTerms(task) {
16729
+ const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
16730
+ return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
16731
+ }
16732
+ function kindFitScore(kind, task) {
16733
+ const normalized = task.toLocaleLowerCase("en-US");
16734
+ const hints = {
16735
+ episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
16736
+ semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
16737
+ procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
16738
+ };
16739
+ return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
16740
+ }
16741
+ function validationFreshnessScore(memory) {
16742
+ const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
16743
+ if (!validatedAt) return 0;
16744
+ const time = Date.parse(validatedAt);
16745
+ if (!Number.isFinite(time)) return 0;
16746
+ const ageDays = Math.max(0, Date.now() - time) / (24 * 60 * 60 * 1e3);
16747
+ if (ageDays <= 30) return 0.35;
16748
+ if (ageDays <= 90) return 0.15;
16749
+ return 0;
16750
+ }
16751
+ function scoreForTask(memory, task) {
16752
+ const terms = taskTerms(task);
16753
+ if (terms.length === 0) return void 0;
16754
+ const title = memory.title.toLocaleLowerCase("en-US");
16755
+ const content = memory.content.toLocaleLowerCase("en-US");
16756
+ const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
16757
+ const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
16758
+ const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
16759
+ let score = memory.state === "approved" ? 0.5 : 0;
16760
+ const matched = [];
16761
+ for (const term of terms) {
16762
+ let termScore = 0;
16763
+ if (title.includes(term)) termScore += 5;
16764
+ if (tags.includes(term)) termScore += 4;
16765
+ if (applicability.includes(term)) termScore += 3;
16766
+ if (facts.includes(term)) termScore += 2;
16767
+ if (content.includes(term)) termScore += 1;
16768
+ if (termScore > 0) {
16769
+ score += termScore;
16770
+ matched.push(term);
16771
+ }
16772
+ }
16773
+ if (matched.length === 0) return void 0;
16774
+ score += kindFitScore(memory.kind, task);
16775
+ score += validationFreshnessScore(memory);
16776
+ const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
16777
+ const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
16778
+ return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
16779
+ }
16780
+ function semanticText(memory) {
16781
+ return sanitizeCredentials([
16782
+ memory.title,
16783
+ memory.applicability,
16784
+ memory.tags.join(" "),
16785
+ memory.facts.join(" "),
16786
+ memory.content
16787
+ ].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
16788
+ }
16789
+ function cosineSimilarity(left, right) {
16790
+ if (left.length === 0 || left.length !== right.length) return 0;
16791
+ let dot = 0;
16792
+ let leftMagnitude = 0;
16793
+ let rightMagnitude = 0;
16794
+ for (let index = 0; index < left.length; index += 1) {
16795
+ const a = left[index];
16796
+ const b = right[index];
16797
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return 0;
16798
+ dot += a * b;
16799
+ leftMagnitude += a * a;
16800
+ rightMagnitude += b * b;
16801
+ }
16802
+ if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
16803
+ return dot / Math.sqrt(leftMagnitude * rightMagnitude);
16804
+ }
16805
+ function semanticSelection(memory, similarity, task) {
16806
+ if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
16807
+ const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
16808
+ const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
16809
+ return {
16810
+ score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
16811
+ reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
16812
+ };
16813
+ }
16814
+ async function resolveSemanticProvider(explicit) {
16815
+ if (explicit !== void 0) return explicit;
16816
+ try {
16817
+ const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
16818
+ return await withTimeout(
16819
+ getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
16820
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS,
16821
+ "Long-term semantic provider initialization"
16822
+ );
16823
+ } catch {
16824
+ return null;
16825
+ }
16826
+ }
16827
+ function createStore(dataDir) {
16828
+ const store = new LongTermMemoryStore();
16829
+ return store.init(dataDir).then(() => store);
16830
+ }
16831
+ async function createManualLongTermMemory(input) {
16832
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
16833
+ if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
16834
+ assertScope(input.scope);
16835
+ const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID10(), relation: "supports" }];
16836
+ return createLongTermMemoryWithDataDir({
16837
+ dataDir: input.dataDir,
16838
+ originProjectId: input.projectId,
16839
+ ownerId: owner.id,
16840
+ scope: input.scope,
16841
+ kind: input.kind,
16842
+ portability: input.portability,
16843
+ title: input.title,
16844
+ content: input.content,
16845
+ facts: input.facts,
16846
+ tags: input.tags,
16847
+ applicability: input.applicability,
16848
+ origin: "manual",
16849
+ evidence,
16850
+ reader: input.reader
16851
+ });
16852
+ }
16853
+ async function promoteObservationToLongTermMemory(input) {
16854
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
16855
+ if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
16856
+ return createLongTermMemoryWithDataDir({
16857
+ dataDir: input.dataDir,
16858
+ originProjectId: input.observation.projectId,
16859
+ ownerId: owner.id,
16860
+ scope: input.scope,
16861
+ kind: input.kind,
16862
+ portability: "project-bound",
16863
+ title: input.observation.title,
16864
+ content: input.observation.narrative,
16865
+ facts: input.observation.facts,
16866
+ tags: input.tags ?? input.observation.concepts,
16867
+ applicability: input.applicability,
16868
+ origin: input.observation.source === "git" ? "git" : "agent",
16869
+ evidence: [{
16870
+ kind: "observation",
16871
+ referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
16872
+ relation: "derives"
16873
+ }],
16874
+ reader: input.reader
16875
+ });
16876
+ }
16877
+ async function createLongTermMemoryCandidate(input) {
16878
+ return createLongTermMemoryWithDataDir(input);
16879
+ }
16880
+ async function createLongTermMemoryWithDataDir(input) {
16881
+ assertScope(input.scope);
16882
+ assertKind(input.kind);
16883
+ const portability = input.portability ?? "project-bound";
16884
+ assertPortability(portability);
16885
+ requireTeamReader(input.scope, input.reader);
16886
+ const evidenceInputs = normalizeEvidence(input.evidence);
16887
+ assertPortableEvidence(input.scope, portability, evidenceInputs);
16888
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
16889
+ const memory = {
16890
+ id: randomUUID10(),
16891
+ originProjectId: compactText2(input.originProjectId, "origin project id", 1e3),
16892
+ ownerId: compactText2(input.ownerId, "owner id", 160),
16893
+ scope: input.scope,
16894
+ kind: input.kind,
16895
+ state: "candidate",
16896
+ portability,
16897
+ title: compactText2(input.title, "title", 500),
16898
+ content: compactText2(input.content, "content"),
16899
+ facts: compactList(input.facts, "facts"),
16900
+ tags: compactList(input.tags, "tags"),
16901
+ ...input.applicability ? { applicability: compactText2(input.applicability, "applicability", 1e3) } : {},
16902
+ origin: input.origin ?? "manual",
16903
+ createdAt: now3,
16904
+ updatedAt: now3,
16905
+ accessCount: 0
16906
+ };
16907
+ const store = await createStore(input.dataDir);
16908
+ const evidence = store.transaction(() => {
16909
+ store.insert(memory);
16910
+ const written = evidenceInputs.map((item) => store.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
16911
+ store.recordEvent({
16912
+ memoryId: memory.id,
16913
+ kind: "created",
16914
+ toState: "candidate",
16915
+ detail: "Created as a source-backed long-term memory candidate.",
16916
+ createdAt: now3
16917
+ });
16918
+ return written;
16919
+ });
16920
+ return { memory, evidence };
16921
+ }
16922
+ function transitionDetail(value) {
16923
+ return compactText2(value, "transition reason", 1e3);
16924
+ }
16925
+ async function transitionMemory(input) {
16926
+ const store = await createStore(input.dataDir);
16927
+ return store.transaction(() => {
16928
+ const current = store.get(input.id);
16929
+ if (!current) throw new Error("Long-term memory was not found.");
16930
+ if (current.state !== input.expected) {
16931
+ throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
16932
+ }
16933
+ const evidence = store.listEvidence(current.id);
16934
+ if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
16935
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
16936
+ const next = {
16937
+ ...current,
16938
+ state: input.next,
16939
+ updatedAt: now3,
16940
+ lastValidatedAt: now3,
16941
+ ...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
16942
+ };
16943
+ store.update(next);
16944
+ store.recordEvent({
16945
+ memoryId: next.id,
16946
+ kind: input.event,
16947
+ fromState: current.state,
16948
+ toState: next.state,
16949
+ detail: transitionDetail(input.reason),
16950
+ createdAt: now3
16951
+ });
16952
+ return next;
16953
+ });
16954
+ }
16955
+ async function qualifyLongTermMemory(input) {
16956
+ return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
16957
+ }
16958
+ async function approveLongTermMemory(input) {
16959
+ return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
16960
+ }
16961
+ async function archiveLongTermMemory(input) {
16962
+ const store = await createStore(input.dataDir);
16963
+ return store.transaction(() => {
16964
+ const current = store.get(input.id);
16965
+ if (!current) throw new Error("Long-term memory was not found.");
16966
+ if (current.state === "archived" || current.state === "superseded") {
16967
+ throw new Error("Long-term memory is already inactive.");
16968
+ }
16969
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
16970
+ const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
16971
+ store.update(next);
16972
+ store.recordEvent({
16973
+ memoryId: next.id,
16974
+ kind: "archived",
16975
+ fromState: current.state,
16976
+ toState: "archived",
16977
+ detail: transitionDetail(input.reason),
16978
+ createdAt: now3
16979
+ });
16980
+ return next;
16981
+ });
16982
+ }
16983
+ async function supersedeLongTermMemory(input) {
16984
+ const store = await createStore(input.dataDir);
16985
+ return store.transaction(() => {
16986
+ const current = store.get(input.id);
16987
+ if (!current) throw new Error("Long-term memory was not found.");
16988
+ if (current.state === "archived" || current.state === "superseded") {
16989
+ throw new Error("Long-term memory is already inactive.");
16990
+ }
16991
+ if (current.id === input.supersededBy) {
16992
+ throw new Error("Long-term memory cannot supersede itself.");
16993
+ }
16994
+ const replacement = store.get(input.supersededBy);
16995
+ if (!replacement || !isEligibleLongTermMemory(replacement)) {
16996
+ throw new Error("Replacement long-term memory must be qualified or approved.");
16997
+ }
16998
+ if (replacement.scope !== current.scope) {
16999
+ throw new Error("Replacement long-term memory must use the same scope.");
17000
+ }
17001
+ if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
17002
+ throw new Error("User long-term memory may only be superseded by the same local owner.");
17003
+ }
17004
+ if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
17005
+ throw new Error("Project and team long-term memory may only be superseded within the same project.");
17006
+ }
17007
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
17008
+ const next = {
17009
+ ...current,
17010
+ state: "superseded",
17011
+ supersededBy: replacement.id,
17012
+ updatedAt: now3
17013
+ };
17014
+ store.update(next);
17015
+ store.recordEvent({
17016
+ memoryId: next.id,
17017
+ kind: "superseded",
17018
+ fromState: current.state,
17019
+ toState: "superseded",
17020
+ detail: transitionDetail(input.reason),
17021
+ createdAt: now3
17022
+ });
17023
+ return next;
17024
+ });
17025
+ }
17026
+ async function listLongTermMemories(input) {
17027
+ const store = await createStore(input.dataDir);
17028
+ 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) }));
17029
+ }
17030
+ async function getLongTermMemoryDetail(input) {
17031
+ const store = await createStore(input.dataDir);
17032
+ const memory = store.get(input.id);
17033
+ if (!memory || !canReadLongTermMemory(memory, input.reader)) {
17034
+ throw new Error("Long-term memory was not found in the active scope.");
17035
+ }
17036
+ store.markAccess([memory.id]);
17037
+ return { memory, evidence: store.listEvidence(memory.id), events: store.listEvents(memory.id) };
17038
+ }
17039
+ async function selectLongTermMemoriesForTask(input) {
17040
+ const task = input.task?.trim();
17041
+ if (!task) return [];
17042
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
17043
+ const reader = {
17044
+ projectId: input.projectId,
17045
+ ...owner ? { ownerId: owner.id } : {},
17046
+ ...input.agentId ? { agentId: input.agentId } : {},
17047
+ ...input.isTeamMember ? { isTeamMember: true } : {}
17048
+ };
17049
+ const store = await createStore(input.dataDir);
17050
+ const eligible = store.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
17051
+ let selected = eligible.map((memory) => {
17052
+ const ranked = scoreForTask(memory, task);
17053
+ return ranked ? {
17054
+ memory,
17055
+ evidence: store.listEvidence(memory.id),
17056
+ score: ranked.score,
17057
+ reason: ranked.reason
17058
+ } : void 0;
17059
+ }).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));
17060
+ if (selected.length === 0 && eligible.length > 0) {
17061
+ const provider2 = await resolveSemanticProvider(input.embeddingProvider);
17062
+ const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
17063
+ if (provider2 && candidates.length > 0) {
17064
+ try {
17065
+ const vectors = await withTimeout(
17066
+ provider2.embedBatch([task, ...candidates.map(semanticText)], {
17067
+ timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
17068
+ retry: false
17069
+ }),
17070
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS,
17071
+ "Long-term semantic retrieval"
17072
+ );
17073
+ const query = vectors[0];
17074
+ if (Array.isArray(query)) {
17075
+ selected = candidates.map((memory, index) => {
17076
+ const vector = vectors[index + 1];
17077
+ const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
17078
+ return ranked ? {
17079
+ memory,
17080
+ evidence: store.listEvidence(memory.id),
17081
+ score: ranked.score,
17082
+ reason: ranked.reason
17083
+ } : void 0;
17084
+ }).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));
17085
+ }
17086
+ } catch {
17087
+ }
17088
+ }
17089
+ }
17090
+ selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
17091
+ store.markAccess(selected.map((item) => item.memory.id));
17092
+ return selected;
17093
+ }
17094
+ function renderLongTermMemorySummary(memory, maxTokens = 22) {
17095
+ const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
17096
+ const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
17097
+ return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
17098
+ }
17099
+ var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
17100
+ var init_long_term = __esm({
17101
+ "src/memory/long-term.ts"() {
17102
+ "use strict";
17103
+ init_esm_shims();
17104
+ init_token_budget();
17105
+ init_timeout();
17106
+ init_secret_filter();
17107
+ init_owner();
17108
+ init_long_term_store();
17109
+ init_long_term_types();
17110
+ MAX_TEXT_LENGTH = 12e3;
17111
+ MAX_LIST_ITEMS = 40;
17112
+ MAX_SEMANTIC_CANDIDATES = 48;
17113
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
17114
+ MIN_SEMANTIC_SIMILARITY = 0.58;
17115
+ PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
17116
+ STOP_TERMS = /* @__PURE__ */ new Set([
17117
+ "the",
17118
+ "and",
17119
+ "for",
17120
+ "with",
17121
+ "from",
17122
+ "that",
17123
+ "this",
17124
+ "into",
17125
+ "when",
17126
+ "what",
17127
+ "\u5982\u4F55",
17128
+ "\u4EC0\u4E48",
17129
+ "\u8FD9\u4E2A",
17130
+ "\u90A3\u4E2A",
17131
+ "\u6211\u4EEC",
17132
+ "\u9879\u76EE",
17133
+ "\u8FDB\u884C",
17134
+ "\u9700\u8981",
17135
+ "\u7EE7\u7EED",
17136
+ "\u5904\u7406"
17137
+ ]);
17138
+ }
17139
+ });
17140
+
16244
17141
  // src/knowledge/workset.ts
16245
17142
  function unique(values) {
16246
17143
  return [...new Set(values.filter(Boolean))];
@@ -16344,6 +17241,7 @@ function receiptOmissionKind(raw) {
16344
17241
  if (raw.includes("state")) return "code-state";
16345
17242
  if (raw.includes("semantic")) return "semantic-code";
16346
17243
  if (raw.includes("start")) return "start-here";
17244
+ if (raw.includes("durable-memory")) return "durable-memory";
16347
17245
  if (raw.includes("memory")) return "memory";
16348
17246
  if (raw.includes("claim")) return "claim";
16349
17247
  if (raw.includes("knowledge-page")) return "knowledge-page";
@@ -16602,6 +17500,26 @@ function renderTaskWorksetPrompt(input) {
16602
17500
  });
16603
17501
  }
16604
17502
  }
17503
+ if (input.durableMemory.length > 0) {
17504
+ appendLine(lines, "", maxTokens, omitted, "durable-memory-heading");
17505
+ appendLine(lines, "Durable memory", maxTokens, omitted, "durable-memory-heading");
17506
+ for (const memory of input.durableMemory.slice(0, 3)) {
17507
+ appendLine(
17508
+ lines,
17509
+ "- " + memory.kind + " (" + memory.scope + ", " + memory.state + "; ref durable:" + memory.id + "): " + memory.summary,
17510
+ maxTokens,
17511
+ omitted,
17512
+ "durable-memory",
17513
+ selected,
17514
+ {
17515
+ kind: "durable-memory",
17516
+ id: "durable:" + memory.id,
17517
+ reason: memory.reason,
17518
+ trust: "source-backed"
17519
+ }
17520
+ );
17521
+ }
17522
+ }
16605
17523
  if (input.workflows.length > 0) {
16606
17524
  appendLine(lines, "", maxTokens, omitted, "workflow-heading");
16607
17525
  appendLine(lines, "Project workflow", maxTokens, omitted, "workflow-heading");
@@ -16671,6 +17589,32 @@ async function buildTaskWorkset(input) {
16671
17589
  evidenceRefs: evidenceByClaim.get(claim.id).map((item) => item.id),
16672
17590
  reason: selection.reasons[claim.id] ?? "source-qualified task match"
16673
17591
  }));
17592
+ let durableMemory = [];
17593
+ let durableEvidence = [];
17594
+ if (task) {
17595
+ try {
17596
+ const selected = await selectLongTermMemoriesForTask({
17597
+ dataDir: input.dataDir,
17598
+ projectId: input.projectId,
17599
+ task,
17600
+ ...input.reader?.agentId ? { agentId: input.reader.agentId } : {},
17601
+ ...input.reader?.isTeamMember ? { isTeamMember: true } : {},
17602
+ limit: 3
17603
+ });
17604
+ durableMemory = selected.map((item) => ({
17605
+ id: item.memory.id,
17606
+ kind: item.memory.kind,
17607
+ scope: item.memory.scope,
17608
+ state: item.memory.state,
17609
+ title: item.memory.title,
17610
+ summary: renderLongTermMemorySummary(item.memory, 20),
17611
+ evidenceRefs: item.evidence.map((evidence) => evidence.id),
17612
+ reason: item.reason
17613
+ }));
17614
+ durableEvidence = selected.flatMap((item) => item.evidence);
17615
+ } catch {
17616
+ }
17617
+ }
16674
17618
  let workspace;
16675
17619
  let pages = [];
16676
17620
  let workflows = [];
@@ -16710,7 +17654,10 @@ async function buildTaskWorkset(input) {
16710
17654
  const evidenceIds = unique(selection.claims.flatMap((claim) => evidenceIdsForClaim(
16711
17655
  claim,
16712
17656
  evidenceByClaim.get(claim.id) ?? []
16713
- )));
17657
+ )).concat(
17658
+ durableMemory.map((memory) => "durable:" + memory.id),
17659
+ durableEvidence.map((evidence) => "durable-evidence:" + evidence.id)
17660
+ ));
16714
17661
  const verification = unique([
16715
17662
  ...workflows.flatMap((workflow) => workflow.verificationGates),
16716
17663
  ...input.verificationHints
@@ -16736,7 +17683,7 @@ async function buildTaskWorkset(input) {
16736
17683
  } : {}
16737
17684
  } : void 0;
16738
17685
  const base = {
16739
- version: "1.2",
17686
+ version: "1.3",
16740
17687
  task,
16741
17688
  lens: input.lens,
16742
17689
  currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
@@ -16749,6 +17696,7 @@ async function buildTaskWorkset(input) {
16749
17696
  hiddenCautionMemoryCount: input.hiddenCautionMemoryCount ?? 0,
16750
17697
  claims,
16751
17698
  pages,
17699
+ durableMemory,
16752
17700
  workflows,
16753
17701
  cautions: normalizedCautions,
16754
17702
  verification,
@@ -16765,7 +17713,7 @@ async function buildTaskWorkset(input) {
16765
17713
  budget: { maxTokens }
16766
17714
  });
16767
17715
  const receipt = {
16768
- version: "1.2.4",
17716
+ version: "1.3",
16769
17717
  target: input.deliveryTarget ?? "project-context",
16770
17718
  elapsedMs: Math.max(0, Date.now() - startedAt),
16771
17719
  budget: {
@@ -16800,6 +17748,7 @@ var init_workset = __esm({
16800
17748
  init_workspace();
16801
17749
  init_workflow_store();
16802
17750
  init_workflows();
17751
+ init_long_term();
16803
17752
  CONTINUATION_DETAIL_TOKEN_BUDGET = 20;
16804
17753
  }
16805
17754
  });
@@ -16812,7 +17761,7 @@ __export(current_facts_exports, {
16812
17761
  });
16813
17762
  import { execFileSync } from "child_process";
16814
17763
  import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
16815
- import path19 from "path";
17764
+ import path20 from "path";
16816
17765
  function readTextIfExists(filePath) {
16817
17766
  try {
16818
17767
  if (!existsSync12(filePath)) return null;
@@ -16822,7 +17771,7 @@ function readTextIfExists(filePath) {
16822
17771
  }
16823
17772
  }
16824
17773
  function readPackageVersion(rootPath) {
16825
- const text = readTextIfExists(path19.join(rootPath, "package.json"));
17774
+ const text = readTextIfExists(path20.join(rootPath, "package.json"));
16826
17775
  if (!text) return void 0;
16827
17776
  try {
16828
17777
  const parsed = JSON.parse(text);
@@ -16832,7 +17781,7 @@ function readPackageVersion(rootPath) {
16832
17781
  }
16833
17782
  }
16834
17783
  function readLatestChangelog(rootPath) {
16835
- const text = readTextIfExists(path19.join(rootPath, "CHANGELOG.md"));
17784
+ const text = readTextIfExists(path20.join(rootPath, "CHANGELOG.md"));
16836
17785
  if (!text) return void 0;
16837
17786
  const match = text.match(/^##\s+\[?v?([^\]\s]+)\]?\s*(?:-\s*(\d{4}-\d{2}-\d{2}))?/m);
16838
17787
  if (!match) return void 0;
@@ -16902,7 +17851,7 @@ function detectStaleProgressNotes(input) {
16902
17851
  ];
16903
17852
  const notes = [];
16904
17853
  for (const relPath of candidates) {
16905
- const content = readTextIfExists(path19.join(input.rootPath, relPath));
17854
+ const content = readTextIfExists(path20.join(input.rootPath, relPath));
16906
17855
  if (!content) continue;
16907
17856
  const parsed = parseProgressNote(content);
16908
17857
  const olderThanChangelog = compareDateStrings(parsed.lastUpdated, input.latestChangelog?.date);
@@ -17153,9 +18102,9 @@ async function inspectExternalCodeGraph(input) {
17153
18102
  return { health, quality: providerQuality({ mode, health }) };
17154
18103
  }
17155
18104
  function safeRelativePath(projectRoot, value, exclude) {
17156
- const path26 = safeText(value, 1024);
17157
- if (!path26 || isAbsolute(path26)) return void 0;
17158
- const absolute = resolve(projectRoot, path26);
18105
+ const path27 = safeText(value, 1024);
18106
+ if (!path27 || isAbsolute(path27)) return void 0;
18107
+ const absolute = resolve(projectRoot, path27);
17159
18108
  const fromRoot = relative2(projectRoot, absolute);
17160
18109
  if (!fromRoot || fromRoot === ".." || fromRoot.startsWith("..\\") || fromRoot.startsWith("../") || isAbsolute(fromRoot)) return void 0;
17161
18110
  const normalized = normalizeCodePath(fromRoot);
@@ -17430,15 +18379,15 @@ function countLanguages(files) {
17430
18379
  }
17431
18380
  return [...counts.entries()].map(([language, files2]) => ({ language, files: files2 })).sort((a, b) => a.language.localeCompare(b.language));
17432
18381
  }
17433
- function suggestedReadRank(path26) {
17434
- const normalized = path26.replace(/\\/g, "/");
18382
+ function suggestedReadRank(path27) {
18383
+ const normalized = path27.replace(/\\/g, "/");
17435
18384
  if (normalized.startsWith("src/")) return 0;
17436
18385
  if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 0;
17437
18386
  if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
17438
18387
  return 3;
17439
18388
  }
17440
18389
  function compactSuggestedReads(paths, limit = 8, exclude) {
17441
- return uniq(paths).filter((path26) => !isCodeGraphExcludedPath(path26, exclude)).sort((a, b) => suggestedReadRank(a) - suggestedReadRank(b)).slice(0, limit);
18390
+ return uniq(paths).filter((path27) => !isCodeGraphExcludedPath(path27, exclude)).sort((a, b) => suggestedReadRank(a) - suggestedReadRank(b)).slice(0, limit);
17442
18391
  }
17443
18392
  function collectGraph(store, projectId, observations2, exclude) {
17444
18393
  const files = store.listFiles(projectId);
@@ -17478,14 +18427,14 @@ function collectGraph(store, projectId, observations2, exclude) {
17478
18427
  }
17479
18428
  if (files.length === 0) {
17480
18429
  for (const observation of observations2) {
17481
- for (const path26 of observation.filesModified ?? []) {
17482
- if (!path26 || isCodeGraphExcludedPath(path26, exclude)) continue;
17483
- suggestedReads.push(path26);
18430
+ for (const path27 of observation.filesModified ?? []) {
18431
+ if (!path27 || isCodeGraphExcludedPath(path27, exclude)) continue;
18432
+ suggestedReads.push(path27);
17484
18433
  sources.push({
17485
18434
  observationId: observation.id,
17486
18435
  title: observation.title,
17487
18436
  type: observation.type,
17488
- path: path26,
18437
+ path: path27,
17489
18438
  status: "unbound",
17490
18439
  reason: "Recorded file hint; Code Memory refresh is pending."
17491
18440
  });
@@ -17554,8 +18503,8 @@ var compaction_checkpoint_store_exports = {};
17554
18503
  __export(compaction_checkpoint_store_exports, {
17555
18504
  CompactionCheckpointStore: () => CompactionCheckpointStore
17556
18505
  });
17557
- import { createHash as createHash13, randomUUID as randomUUID8 } from "crypto";
17558
- function optionalText5(value) {
18506
+ import { createHash as createHash13, randomUUID as randomUUID11 } from "crypto";
18507
+ function optionalText6(value) {
17559
18508
  return typeof value === "string" && value ? value : void 0;
17560
18509
  }
17561
18510
  function optionalNumber2(value) {
@@ -17609,15 +18558,15 @@ function rowToCheckpoint(row) {
17609
18558
  reason: row.reason,
17610
18559
  sourceEvent: row.source_event,
17611
18560
  sourceKey: row.source_key,
17612
- ...optionalText5(row.summary) ? { summary: row.summary } : {},
18561
+ ...optionalText6(row.summary) ? { summary: row.summary } : {},
17613
18562
  ...optionalNumber2(row.tokens_before) !== void 0 ? { tokensBefore: Number(row.tokens_before) } : {},
17614
- ...optionalText5(row.first_kept_entry_id) ? { firstKeptEntryId: row.first_kept_entry_id } : {},
18563
+ ...optionalText6(row.first_kept_entry_id) ? { firstKeptEntryId: row.first_kept_entry_id } : {},
17615
18564
  ...parseDetails(row.details_json) ? { details: parseDetails(row.details_json) } : {},
17616
18565
  transcriptAvailable: Boolean(row.transcript_available),
17617
18566
  status: row.status,
17618
18567
  preCapturedAt: row.pre_captured_at,
17619
- ...optionalText5(row.completed_at) ? { completedAt: row.completed_at } : {},
17620
- ...optionalText5(row.delivered_at) ? { deliveredAt: row.delivered_at } : {},
18568
+ ...optionalText6(row.completed_at) ? { completedAt: row.completed_at } : {},
18569
+ ...optionalText6(row.delivered_at) ? { deliveredAt: row.delivered_at } : {},
17621
18570
  deliveryCount: Number(row.delivery_count ?? 0),
17622
18571
  createdAt: row.created_at,
17623
18572
  updatedAt: row.updated_at
@@ -17655,7 +18604,7 @@ var init_compaction_checkpoint_store = __esm({
17655
18604
  if (pending) return rowToCheckpoint(pending);
17656
18605
  const now3 = input.capturedAt ?? (/* @__PURE__ */ new Date()).toISOString();
17657
18606
  const checkpoint = {
17658
- id: randomUUID8(),
18607
+ id: randomUUID11(),
17659
18608
  projectId: input.projectId,
17660
18609
  sessionId: input.sessionId,
17661
18610
  agent: input.agent,
@@ -17663,7 +18612,7 @@ var init_compaction_checkpoint_store = __esm({
17663
18612
  captureKind: "preflight",
17664
18613
  reason: safeReason(input.reason),
17665
18614
  sourceEvent: input.sourceEvent,
17666
- sourceKey: `pre:${randomUUID8()}`,
18615
+ sourceKey: `pre:${randomUUID11()}`,
17667
18616
  transcriptAvailable: Boolean(input.transcriptAvailable),
17668
18617
  status: "active",
17669
18618
  preCapturedAt: now3,
@@ -17729,7 +18678,7 @@ var init_compaction_checkpoint_store = __esm({
17729
18678
  return this.get(checkpoint2.id);
17730
18679
  }
17731
18680
  const checkpoint = {
17732
- id: randomUUID8(),
18681
+ id: randomUUID11(),
17733
18682
  projectId: input.projectId,
17734
18683
  sessionId: input.sessionId,
17735
18684
  agent: input.agent,
@@ -17864,7 +18813,7 @@ __export(auto_context_exports, {
17864
18813
  formatLegacyAutoProjectContextPrompt: () => formatLegacyAutoProjectContextPrompt
17865
18814
  });
17866
18815
  import { existsSync as existsSync14, statSync as statSync3 } from "fs";
17867
- import path20 from "path";
18816
+ import path21 from "path";
17868
18817
  function activeProjectObservations(observations2, projectId) {
17869
18818
  return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
17870
18819
  }
@@ -18099,6 +19048,7 @@ async function buildAutoProjectContext(input) {
18099
19048
  stale: overview.freshness.stale
18100
19049
  },
18101
19050
  runtimeCautions,
19051
+ ...input.reader ? { reader: input.reader } : {},
18102
19052
  ...input.deliveryTarget ? { deliveryTarget: input.deliveryTarget } : {}
18103
19053
  });
18104
19054
  return {
@@ -18143,7 +19093,7 @@ function dedupeSourcesByObservation(sources) {
18143
19093
  function existingLensCandidates(rootPath, lens) {
18144
19094
  const out = [];
18145
19095
  for (const candidate of lensPathCandidates(lens)) {
18146
- const absolute = path20.join(rootPath, candidate);
19096
+ const absolute = path21.join(rootPath, candidate);
18147
19097
  try {
18148
19098
  if (!existsSync14(absolute)) continue;
18149
19099
  const stat = statSync3(absolute);
@@ -18253,7 +19203,7 @@ function formatAutoProjectContextSummary(context) {
18253
19203
  "Start here"
18254
19204
  ].filter(Boolean);
18255
19205
  if (startHere.length > 0) {
18256
- startHere.forEach((path26, index) => lines.push(`${index + 1}. ${path26}`));
19206
+ startHere.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
18257
19207
  } else {
18258
19208
  lines.push("- no code-bound reads yet; inspect the task-relevant files directly");
18259
19209
  }
@@ -18262,6 +19212,14 @@ function formatAutoProjectContextSummary(context) {
18262
19212
  "Reliable memory",
18263
19213
  reliableSources.length > 0 ? `- ${reliableSources.length} current code-bound memory link(s)` : "- none yet"
18264
19214
  );
19215
+ if (context.workset.durableMemory.length > 0) {
19216
+ lines.push("", "Durable memory");
19217
+ for (const memory of context.workset.durableMemory.slice(0, 3)) {
19218
+ lines.push(
19219
+ "- " + memory.kind + " (" + memory.scope + ", " + memory.state + "): " + compactContinuationText(memory.summary, 28)
19220
+ );
19221
+ }
19222
+ }
18265
19223
  const continuation = context.workset.continuation;
18266
19224
  if (continuation?.previousSession || continuation?.memories.length || continuation?.compactCheckpoint) {
18267
19225
  lines.push("", "Resume from prior work");
@@ -18311,7 +19269,7 @@ function formatLegacyAutoProjectContextPrompt(context) {
18311
19269
  if (startHere.length === 0) {
18312
19270
  lines.push("- no code-bound reads yet; inspect the task-relevant code directly");
18313
19271
  } else {
18314
- startHere.forEach((path26, index) => lines.push(`${index + 1}. ${path26}`));
19272
+ startHere.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
18315
19273
  }
18316
19274
  const { reliableSources, cautionSources, hiddenReliableCount, hiddenCautionCount } = lensSourceSets(context);
18317
19275
  lines.push("", "Reliable memory");
@@ -18562,6 +19520,7 @@ async function attachTaskWorkset(input) {
18562
19520
  stale: input.pack.warnings.filter((warning) => warning.status === "stale").length
18563
19521
  },
18564
19522
  runtimeCautions: input.runtimeCautions,
19523
+ ...input.reader ? { reader: input.reader } : {},
18565
19524
  deliveryTarget: "context-pack"
18566
19525
  });
18567
19526
  return { ...input.pack, workset };
@@ -18572,7 +19531,7 @@ function buildContextPackPrompt(pack) {
18572
19531
  const unboundMemories = pack.memories.filter((memory) => memory.status === "unbound");
18573
19532
  const lines = ["## Task", pack.task, "", "## Reliable Memories"];
18574
19533
  const visibleCodeFacts = pack.codeFacts.filter((fact) => !isCodeGraphExcludedPath(fact.path)).slice(0, 5);
18575
- const visibleSuggestedReads = pack.suggestedReads.filter((path26) => !isCodeGraphExcludedPath(path26)).slice(0, 5);
19534
+ const visibleSuggestedReads = pack.suggestedReads.filter((path27) => !isCodeGraphExcludedPath(path27)).slice(0, 5);
18576
19535
  if (reliableMemories.length === 0) lines.push("- none");
18577
19536
  for (const memory of reliableMemories) {
18578
19537
  lines.push(`- #${memory.id} ${memory.status}: [${memory.type}] ${memory.title} (${memory.reason})`);
@@ -18596,7 +19555,7 @@ function buildContextPackPrompt(pack) {
18596
19555
  }
18597
19556
  lines.push("", "## Suggested Next Reads");
18598
19557
  if (visibleSuggestedReads.length === 0) lines.push("- none");
18599
- visibleSuggestedReads.forEach((path26, index) => lines.push(`${index + 1}. ${path26}`));
19558
+ visibleSuggestedReads.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
18600
19559
  lines.push("", "## Suggested Verification");
18601
19560
  if (pack.suggestedVerification.length === 0) lines.push("- none");
18602
19561
  for (const command of pack.suggestedVerification) lines.push(`- ${command}`);
@@ -18988,9 +19947,9 @@ __export(team_store_exports, {
18988
19947
  isTeamStoreInitialized: () => isTeamStoreInitialized,
18989
19948
  resetTeamStore: () => resetTeamStore
18990
19949
  });
18991
- import { randomUUID as randomUUID9 } from "crypto";
18992
- import path21 from "path";
18993
- import fs15 from "fs";
19950
+ import { randomUUID as randomUUID12 } from "crypto";
19951
+ import path22 from "path";
19952
+ import fs16 from "fs";
18994
19953
  function getTeamStore() {
18995
19954
  if (!_teamStore) {
18996
19955
  throw new Error("[memorix] TeamStore not initialized \u2014 call initTeamStore() first");
@@ -19277,7 +20236,7 @@ var init_team_store = __esm({
19277
20236
  * reactivate it (preserving agent_id). Otherwise create a new one.
19278
20237
  */
19279
20238
  registerAgent(input) {
19280
- const instanceId = input.instanceId || randomUUID9();
20239
+ const instanceId = input.instanceId || randomUUID12();
19281
20240
  const now3 = Date.now();
19282
20241
  const existing = this.stmtAgentFindByInstance.get(
19283
20242
  input.projectId,
@@ -19303,7 +20262,7 @@ var init_team_store = __esm({
19303
20262
  this.eventBus?.emit("agent:joined", { agentId: row2.agent_id, projectId: input.projectId, agentType: input.agentType });
19304
20263
  return row2;
19305
20264
  }
19306
- const agentId = randomUUID9();
20265
+ const agentId = randomUUID12();
19307
20266
  this.stmtAgentUpsert.run({
19308
20267
  agent_id: agentId,
19309
20268
  project_id: input.projectId,
@@ -19405,7 +20364,7 @@ var init_team_store = __esm({
19405
20364
  return { error: `Recipient agent '${input.recipientAgentId}' not found \u2014 cannot send to unknown agent` };
19406
20365
  }
19407
20366
  }
19408
- const id = randomUUID9();
20367
+ const id = randomUUID12();
19409
20368
  const now3 = Date.now();
19410
20369
  const row = {
19411
20370
  id,
@@ -19455,7 +20414,7 @@ var init_team_store = __esm({
19455
20414
  // Task Operations (with atomic claim semantics)
19456
20415
  // ═══════════════════════════════════════════════════════════════════
19457
20416
  createTask(input) {
19458
- const taskId = randomUUID9();
20417
+ const taskId = randomUUID12();
19459
20418
  const now3 = Date.now();
19460
20419
  const row = {
19461
20420
  task_id: taskId,
@@ -19680,19 +20639,19 @@ var init_team_store = __esm({
19680
20639
  const agentCount = this.db.prepare(`SELECT COUNT(*) AS cnt FROM team_agents`).get();
19681
20640
  if (agentCount.cnt > 0) return;
19682
20641
  const candidates = [
19683
- path21.join(this.dataDir, "team-state.json"),
19684
- path21.join(this.dataDir, "..", "team-state.json")
20642
+ path22.join(this.dataDir, "team-state.json"),
20643
+ path22.join(this.dataDir, "..", "team-state.json")
19685
20644
  ];
19686
20645
  let jsonPath = null;
19687
20646
  for (const p of candidates) {
19688
- if (fs15.existsSync(p)) {
20647
+ if (fs16.existsSync(p)) {
19689
20648
  jsonPath = p;
19690
20649
  break;
19691
20650
  }
19692
20651
  }
19693
20652
  if (!jsonPath) return;
19694
20653
  try {
19695
- const raw = fs15.readFileSync(jsonPath, "utf-8");
20654
+ const raw = fs16.readFileSync(jsonPath, "utf-8");
19696
20655
  const snap = JSON.parse(raw);
19697
20656
  if (snap.version !== 1) return;
19698
20657
  console.error(`[memorix] Migrating team-state.json to SQLite...`);
@@ -19727,7 +20686,7 @@ var init_team_store = __esm({
19727
20686
  for (const [agentId, msgs] of Object.entries(snap.messages.inboxes)) {
19728
20687
  for (const msg of msgs) {
19729
20688
  this.stmtMsgInsert.run({
19730
- id: msg.id ?? randomUUID9(),
20689
+ id: msg.id ?? randomUUID12(),
19731
20690
  project_id: "migrated",
19732
20691
  sender_agent_id: msg.from ?? "unknown",
19733
20692
  recipient_agent_id: msg.to === "__broadcast__" ? null : msg.to ?? agentId,
@@ -19786,7 +20745,7 @@ var init_team_store = __esm({
19786
20745
  `[memorix] Team migration complete: ${agentsMigrated} agents, ${messagesMigrated} messages, ${tasksMigrated} tasks, ${locksMigrated} locks.`
19787
20746
  );
19788
20747
  try {
19789
- fs15.renameSync(jsonPath, jsonPath + ".migrated");
20748
+ fs16.renameSync(jsonPath, jsonPath + ".migrated");
19790
20749
  } catch {
19791
20750
  }
19792
20751
  } catch (err) {
@@ -19821,7 +20780,7 @@ var init_team_store = __esm({
19821
20780
  * Uses projectId derived from the dataDir path.
19822
20781
  */
19823
20782
  seedDefaultRoles(dataDir) {
19824
- const projectId = path21.basename(path21.resolve(dataDir, ".."));
20783
+ const projectId = path22.basename(path22.resolve(dataDir, ".."));
19825
20784
  const count2 = this.stmtRoleCountByProject.get(projectId).cnt;
19826
20785
  if (count2 > 0) return;
19827
20786
  const now3 = Date.now();
@@ -20835,8 +21794,8 @@ __export(server_exports, {
20835
21794
  startDashboard: () => startDashboard
20836
21795
  });
20837
21796
  import { createServer } from "http";
20838
- import { promises as fs16 } from "fs";
20839
- import path22 from "path";
21797
+ import { promises as fs17 } from "fs";
21798
+ import path23 from "path";
20840
21799
  import { exec } from "child_process";
20841
21800
  function sendJson(res, data, status = 200) {
20842
21801
  res.writeHead(status, {
@@ -21314,7 +22273,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
21314
22273
  const aliasModule = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
21315
22274
  canonicalId = await aliasModule.getCanonicalId(effectiveProjectId);
21316
22275
  const { promises: fsP } = await import("fs");
21317
- const registryPath = path22.join(baseDir, ".project-aliases.json");
22276
+ const registryPath = path23.join(baseDir, ".project-aliases.json");
21318
22277
  const raw = await fsP.readFile(registryPath, "utf-8");
21319
22278
  const registry = JSON.parse(raw);
21320
22279
  aliasGroups = registry.groups || [];
@@ -21429,14 +22388,14 @@ async function serveStatic(req, res, staticDir) {
21429
22388
  if (urlPath === "/" || !urlPath.includes(".")) {
21430
22389
  urlPath = "/index.html";
21431
22390
  }
21432
- const filePath = path22.join(staticDir, urlPath);
22391
+ const filePath = path23.join(staticDir, urlPath);
21433
22392
  if (!filePath.startsWith(staticDir)) {
21434
22393
  sendError(res, "Forbidden", 403);
21435
22394
  return;
21436
22395
  }
21437
22396
  try {
21438
- const data = await fs16.readFile(filePath);
21439
- const ext = path22.extname(filePath);
22397
+ const data = await fs17.readFile(filePath);
22398
+ const ext = path23.extname(filePath);
21440
22399
  res.writeHead(200, {
21441
22400
  "Content-Type": MIME_TYPES[ext] || "application/octet-stream",
21442
22401
  "Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
@@ -21446,7 +22405,7 @@ async function serveStatic(req, res, staticDir) {
21446
22405
  res.end(data);
21447
22406
  } catch {
21448
22407
  try {
21449
- const indexData = await fs16.readFile(path22.join(staticDir, "index.html"));
22408
+ const indexData = await fs17.readFile(path23.join(staticDir, "index.html"));
21450
22409
  res.writeHead(200, {
21451
22410
  "Content-Type": "text/html; charset=utf-8",
21452
22411
  "Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
@@ -22159,7 +23118,7 @@ function collectCodeMemory(opts) {
22159
23118
  if (status.indexedAt) lines.push(`Last scan: ${status.indexedAt}`);
22160
23119
  if (suggestedReads.length > 0) {
22161
23120
  lines.push("Suggested reads:");
22162
- suggestedReads.forEach((path26, index) => lines.push(`${index + 1}. ${path26}`));
23121
+ suggestedReads.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
22163
23122
  }
22164
23123
  return lines.join("\n");
22165
23124
  } catch {
@@ -22184,7 +23143,7 @@ __export(planner_exports, {
22184
23143
  materializeTaskGraph: () => materializeTaskGraph,
22185
23144
  seedAutonomousPipeline: () => seedAutonomousPipeline
22186
23145
  });
22187
- import { randomUUID as randomUUID10 } from "crypto";
23146
+ import { randomUUID as randomUUID13 } from "crypto";
22188
23147
  function isPlannerTask(metadata) {
22189
23148
  if (!metadata) return null;
22190
23149
  try {
@@ -22242,7 +23201,7 @@ function seedAutonomousPipeline(teamStore, projectId, config, opts) {
22242
23201
  const maxIterations = config.maxIterations ?? 3;
22243
23202
  const taskBudget = config.taskBudget ?? 15;
22244
23203
  const structuredPlan = opts?.structuredPlan ?? true;
22245
- const pipelineId = randomUUID10();
23204
+ const pipelineId = randomUUID13();
22246
23205
  const meta = {
22247
23206
  plannerType: "plan",
22248
23207
  pipelineId,
@@ -22932,15 +23891,15 @@ __export(audit_exports, {
22932
23891
  removeFile: () => removeFile,
22933
23892
  saveAudit: () => saveAudit
22934
23893
  });
22935
- import * as fs17 from "fs/promises";
22936
- import * as path23 from "path";
23894
+ import * as fs18 from "fs/promises";
23895
+ import * as path24 from "path";
22937
23896
  import { homedir as homedir24 } from "os";
22938
23897
  function getAuditFilePath() {
22939
- return process.env.MEMORIX_AUDIT_FILE || path23.join(homedir24(), ".memorix", "audit.json");
23898
+ return process.env.MEMORIX_AUDIT_FILE || path24.join(homedir24(), ".memorix", "audit.json");
22940
23899
  }
22941
23900
  async function loadAudit() {
22942
23901
  try {
22943
- const content = await fs17.readFile(getAuditFilePath(), "utf-8");
23902
+ const content = await fs18.readFile(getAuditFilePath(), "utf-8");
22944
23903
  return JSON.parse(content);
22945
23904
  } catch {
22946
23905
  return {
@@ -22951,9 +23910,9 @@ async function loadAudit() {
22951
23910
  }
22952
23911
  async function saveAudit(data) {
22953
23912
  const auditFile = getAuditFilePath();
22954
- const dir = path23.dirname(auditFile);
22955
- await fs17.mkdir(dir, { recursive: true });
22956
- await fs17.writeFile(auditFile, JSON.stringify(data, null, 2), "utf-8");
23913
+ const dir = path24.dirname(auditFile);
23914
+ await fs18.mkdir(dir, { recursive: true });
23915
+ await fs18.writeFile(auditFile, JSON.stringify(data, null, 2), "utf-8");
22957
23916
  }
22958
23917
  function getProjectId(projectRoot) {
22959
23918
  try {
@@ -23035,8 +23994,8 @@ __export(installers_exports, {
23035
23994
  resolveOpenCodeHookCommand: () => resolveOpenCodeHookCommand,
23036
23995
  uninstallHooks: () => uninstallHooks
23037
23996
  });
23038
- import * as fs18 from "fs/promises";
23039
- import * as path24 from "path";
23997
+ import * as fs19 from "fs/promises";
23998
+ import * as path25 from "path";
23040
23999
  import * as os3 from "os";
23041
24000
  import { execSync as execSync3 } from "child_process";
23042
24001
  function resolveHookCommand() {
@@ -23052,7 +24011,7 @@ function resolveWindowsMemorixShim() {
23052
24011
  stdio: ["ignore", "pipe", "ignore"],
23053
24012
  windowsHide: true
23054
24013
  }));
23055
- return output.split(/\r?\n/).map((candidate) => candidate.trim()).find((candidate) => path24.isAbsolute(candidate)) ?? null;
24014
+ return output.split(/\r?\n/).map((candidate) => candidate.trim()).find((candidate) => path25.isAbsolute(candidate)) ?? null;
23056
24015
  } catch {
23057
24016
  return null;
23058
24017
  }
@@ -23247,9 +24206,9 @@ async function installOfficialSkillsForAgent(agent, projectRoot, global = false)
23247
24206
  const root = global ? os3.homedir() : projectRoot;
23248
24207
  const skillPaths = [];
23249
24208
  for (const skillEntry of OFFICIAL_MEMORIX_SKILLS) {
23250
- const skillPath = path24.join(root, relativeDir, skillEntry.name, "SKILL.md");
23251
- await fs18.mkdir(path24.dirname(skillPath), { recursive: true });
23252
- await fs18.writeFile(skillPath, skillEntry.content, "utf-8");
24209
+ const skillPath = path25.join(root, relativeDir, skillEntry.name, "SKILL.md");
24210
+ await fs19.mkdir(path25.dirname(skillPath), { recursive: true });
24211
+ await fs19.writeFile(skillPath, skillEntry.content, "utf-8");
23253
24212
  skillPaths.push(skillPath);
23254
24213
  try {
23255
24214
  const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
@@ -23462,124 +24421,124 @@ async function installOpenCodeSkills(projectRoot, global = false) {
23462
24421
  function getProjectConfigPath(agent, projectRoot) {
23463
24422
  switch (agent) {
23464
24423
  case "claude":
23465
- return path24.join(projectRoot, ".claude", "settings.local.json");
24424
+ return path25.join(projectRoot, ".claude", "settings.local.json");
23466
24425
  case "copilot":
23467
- return path24.join(projectRoot, ".github", "hooks", "memorix.json");
24426
+ return path25.join(projectRoot, ".github", "hooks", "memorix.json");
23468
24427
  case "windsurf":
23469
- return path24.join(projectRoot, ".windsurf", "hooks.json");
24428
+ return path25.join(projectRoot, ".windsurf", "hooks.json");
23470
24429
  case "cursor":
23471
- return path24.join(projectRoot, ".cursor", "hooks.json");
24430
+ return path25.join(projectRoot, ".cursor", "hooks.json");
23472
24431
  case "kiro":
23473
- return path24.join(projectRoot, ".kiro", "hooks", "memorix-agent-stop.kiro.hook");
24432
+ return path25.join(projectRoot, ".kiro", "hooks", "memorix-agent-stop.kiro.hook");
23474
24433
  case "codex":
23475
- return path24.join(projectRoot, "AGENTS.md");
24434
+ return path25.join(projectRoot, "AGENTS.md");
23476
24435
  case "trae":
23477
- return path24.join(projectRoot, ".trae", "rules", "project_rules.md");
24436
+ return path25.join(projectRoot, ".trae", "rules", "project_rules.md");
23478
24437
  case "opencode":
23479
- return path24.join(projectRoot, ".opencode", "plugins", "memorix.js");
24438
+ return path25.join(projectRoot, ".opencode", "plugins", "memorix.js");
23480
24439
  case "pi":
23481
- return path24.join(projectRoot, ".pi", "packages", "memorix", "extensions", "memorix.js");
24440
+ return path25.join(projectRoot, ".pi", "packages", "memorix", "extensions", "memorix.js");
23482
24441
  case "openclaw":
23483
- return path24.join(os3.homedir(), ".openclaw", "extensions", "memorix", "hooks", "memorix", "HOOK.md");
24442
+ return path25.join(os3.homedir(), ".openclaw", "extensions", "memorix", "hooks", "memorix", "HOOK.md");
23484
24443
  case "hermes":
23485
- return path24.join(os3.homedir(), ".hermes", "plugins", "memorix", "plugin.yaml");
24444
+ return path25.join(os3.homedir(), ".hermes", "plugins", "memorix", "plugin.yaml");
23486
24445
  case "omp":
23487
- return path24.join(projectRoot, ".omp", "packages", "memorix", "extensions", "memorix.js");
24446
+ return path25.join(projectRoot, ".omp", "packages", "memorix", "extensions", "memorix.js");
23488
24447
  case "antigravity":
23489
- return path24.join(projectRoot, ".agents", "hooks.json");
24448
+ return path25.join(projectRoot, ".agents", "hooks.json");
23490
24449
  case "gemini-cli":
23491
- return path24.join(projectRoot, ".gemini", "settings.json");
24450
+ return path25.join(projectRoot, ".gemini", "settings.json");
23492
24451
  default:
23493
- return path24.join(projectRoot, ".memorix", "hooks.json");
24452
+ return path25.join(projectRoot, ".memorix", "hooks.json");
23494
24453
  }
23495
24454
  }
23496
24455
  function getGlobalConfigPath(agent) {
23497
24456
  const home = os3.homedir();
23498
24457
  switch (agent) {
23499
24458
  case "claude":
23500
- return path24.join(home, ".claude", "settings.json");
24459
+ return path25.join(home, ".claude", "settings.json");
23501
24460
  case "copilot":
23502
24461
  return "";
23503
24462
  case "windsurf":
23504
- return path24.join(home, ".codeium", "windsurf", "hooks.json");
24463
+ return path25.join(home, ".codeium", "windsurf", "hooks.json");
23505
24464
  case "cursor":
23506
- return path24.join(home, ".cursor", "hooks.json");
24465
+ return path25.join(home, ".cursor", "hooks.json");
23507
24466
  case "antigravity":
23508
- return path24.join(home, ".gemini", "config", "hooks.json");
24467
+ return path25.join(home, ".gemini", "config", "hooks.json");
23509
24468
  case "gemini-cli":
23510
- return path24.join(home, ".gemini", "settings.json");
24469
+ return path25.join(home, ".gemini", "settings.json");
23511
24470
  case "opencode":
23512
- return path24.join(home, ".config", "opencode", "plugins", "memorix.js");
24471
+ return path25.join(home, ".config", "opencode", "plugins", "memorix.js");
23513
24472
  case "pi":
23514
- return path24.join(home, ".pi", "agent", "packages", "memorix", "extensions", "memorix.js");
24473
+ return path25.join(home, ".pi", "agent", "packages", "memorix", "extensions", "memorix.js");
23515
24474
  case "openclaw":
23516
- return path24.join(home, ".openclaw", "extensions", "memorix", "hooks", "memorix", "HOOK.md");
24475
+ return path25.join(home, ".openclaw", "extensions", "memorix", "hooks", "memorix", "HOOK.md");
23517
24476
  case "hermes":
23518
- return path24.join(home, ".hermes", "plugins", "memorix", "plugin.yaml");
24477
+ return path25.join(home, ".hermes", "plugins", "memorix", "plugin.yaml");
23519
24478
  case "omp":
23520
- return path24.join(home, ".omp", "agent", "packages", "memorix", "extensions", "memorix.js");
24479
+ return path25.join(home, ".omp", "agent", "packages", "memorix", "extensions", "memorix.js");
23521
24480
  case "trae":
23522
- return path24.join(home, ".trae", "rules", "project_rules.md");
24481
+ return path25.join(home, ".trae", "rules", "project_rules.md");
23523
24482
  default:
23524
- return path24.join(home, ".memorix", "hooks.json");
24483
+ return path25.join(home, ".memorix", "hooks.json");
23525
24484
  }
23526
24485
  }
23527
24486
  function getAgentRulesPath(agent, root, global = false) {
23528
24487
  if (global) {
23529
24488
  switch (agent) {
23530
24489
  case "windsurf":
23531
- return path24.join(root, ".codeium", "windsurf", "rules", "memorix.md");
24490
+ return path25.join(root, ".codeium", "windsurf", "rules", "memorix.md");
23532
24491
  case "cursor":
23533
- return path24.join(root, ".cursor", "rules", "memorix.mdc");
24492
+ return path25.join(root, ".cursor", "rules", "memorix.mdc");
23534
24493
  case "claude":
23535
- return path24.join(root, ".claude", "CLAUDE.md");
24494
+ return path25.join(root, ".claude", "CLAUDE.md");
23536
24495
  case "codex":
23537
- return path24.join(root, ".codex", "AGENTS.md");
24496
+ return path25.join(root, ".codex", "AGENTS.md");
23538
24497
  case "kiro":
23539
- return path24.join(root, ".kiro", "steering", "memorix.md");
24498
+ return path25.join(root, ".kiro", "steering", "memorix.md");
23540
24499
  case "opencode":
23541
- return path24.join(root, ".config", "opencode", "AGENTS.md");
24500
+ return path25.join(root, ".config", "opencode", "AGENTS.md");
23542
24501
  case "antigravity":
23543
24502
  case "gemini-cli":
23544
- return path24.join(root, ".gemini", "GEMINI.md");
24503
+ return path25.join(root, ".gemini", "GEMINI.md");
23545
24504
  case "trae":
23546
- return path24.join(root, ".trae", "rules", "project_rules.md");
24505
+ return path25.join(root, ".trae", "rules", "project_rules.md");
23547
24506
  default:
23548
- return path24.join(root, ".agent", "rules", "memorix.md");
24507
+ return path25.join(root, ".agent", "rules", "memorix.md");
23549
24508
  }
23550
24509
  }
23551
24510
  switch (agent) {
23552
24511
  case "windsurf":
23553
- return path24.join(root, ".windsurf", "rules", "memorix.md");
24512
+ return path25.join(root, ".windsurf", "rules", "memorix.md");
23554
24513
  case "cursor":
23555
- return path24.join(root, ".cursor", "rules", "memorix.mdc");
24514
+ return path25.join(root, ".cursor", "rules", "memorix.mdc");
23556
24515
  case "claude":
23557
- return path24.join(root, "CLAUDE.md");
24516
+ return path25.join(root, "CLAUDE.md");
23558
24517
  case "copilot":
23559
- return path24.join(root, ".github", "copilot-instructions.md");
24518
+ return path25.join(root, ".github", "copilot-instructions.md");
23560
24519
  case "codex":
23561
- return path24.join(root, "AGENTS.md");
24520
+ return path25.join(root, "AGENTS.md");
23562
24521
  case "kiro":
23563
- return path24.join(root, ".kiro", "steering", "memorix.md");
24522
+ return path25.join(root, ".kiro", "steering", "memorix.md");
23564
24523
  case "opencode":
23565
- return path24.join(root, "AGENTS.md");
24524
+ return path25.join(root, "AGENTS.md");
23566
24525
  case "antigravity":
23567
24526
  case "gemini-cli":
23568
- return path24.join(root, "GEMINI.md");
24527
+ return path25.join(root, "GEMINI.md");
23569
24528
  case "trae":
23570
- return path24.join(root, ".trae", "rules", "project_rules.md");
24529
+ return path25.join(root, ".trae", "rules", "project_rules.md");
23571
24530
  default:
23572
- return path24.join(root, ".agent", "rules", "memorix.md");
24531
+ return path25.join(root, ".agent", "rules", "memorix.md");
23573
24532
  }
23574
24533
  }
23575
24534
  async function detectCopilotExtension(home) {
23576
24535
  const extDirs = [
23577
- path24.join(home, ".vscode", "extensions"),
23578
- path24.join(home, ".vscode-insiders", "extensions")
24536
+ path25.join(home, ".vscode", "extensions"),
24537
+ path25.join(home, ".vscode-insiders", "extensions")
23579
24538
  ];
23580
24539
  for (const extDir of extDirs) {
23581
24540
  try {
23582
- const entries = await fs18.readdir(extDir);
24541
+ const entries = await fs19.readdir(extDir);
23583
24542
  if (entries.some((e) => e.startsWith("github.copilot-"))) {
23584
24543
  return true;
23585
24544
  }
@@ -23591,21 +24550,21 @@ async function detectCopilotExtension(home) {
23591
24550
  async function detectInstalledAgents() {
23592
24551
  const agents = [];
23593
24552
  const home = os3.homedir();
23594
- const claudeDir = path24.join(home, ".claude");
24553
+ const claudeDir = path25.join(home, ".claude");
23595
24554
  try {
23596
- await fs18.access(claudeDir);
24555
+ await fs19.access(claudeDir);
23597
24556
  agents.push("claude");
23598
24557
  } catch {
23599
24558
  }
23600
- const windsurfDir = path24.join(home, ".codeium", "windsurf");
24559
+ const windsurfDir = path25.join(home, ".codeium", "windsurf");
23601
24560
  try {
23602
- await fs18.access(windsurfDir);
24561
+ await fs19.access(windsurfDir);
23603
24562
  agents.push("windsurf");
23604
24563
  } catch {
23605
24564
  }
23606
- const cursorDir = path24.join(home, ".cursor");
24565
+ const cursorDir = path25.join(home, ".cursor");
23607
24566
  try {
23608
- await fs18.access(cursorDir);
24567
+ await fs19.access(cursorDir);
23609
24568
  agents.push("cursor");
23610
24569
  } catch {
23611
24570
  }
@@ -23613,21 +24572,21 @@ async function detectInstalledAgents() {
23613
24572
  if (copilotDetected) {
23614
24573
  agents.push("copilot");
23615
24574
  }
23616
- const kiroConfig = path24.join(home, ".kiro");
24575
+ const kiroConfig = path25.join(home, ".kiro");
23617
24576
  try {
23618
- await fs18.access(kiroConfig);
24577
+ await fs19.access(kiroConfig);
23619
24578
  agents.push("kiro");
23620
24579
  } catch {
23621
24580
  }
23622
- const codexDir = path24.join(home, ".codex");
24581
+ const codexDir = path25.join(home, ".codex");
23623
24582
  try {
23624
- await fs18.access(codexDir);
24583
+ await fs19.access(codexDir);
23625
24584
  agents.push("codex");
23626
24585
  } catch {
23627
24586
  }
23628
- const antigravityDir = path24.join(home, ".gemini", "config");
24587
+ const antigravityDir = path25.join(home, ".gemini", "config");
23629
24588
  try {
23630
- await fs18.access(antigravityDir);
24589
+ await fs19.access(antigravityDir);
23631
24590
  agents.push("antigravity");
23632
24591
  } catch {
23633
24592
  }
@@ -23638,15 +24597,15 @@ async function detectInstalledAgents() {
23638
24597
  agents.push("gemini-cli");
23639
24598
  } catch {
23640
24599
  }
23641
- const opencodeDir = path24.join(home, ".config", "opencode");
24600
+ const opencodeDir = path25.join(home, ".config", "opencode");
23642
24601
  try {
23643
- await fs18.access(opencodeDir);
24602
+ await fs19.access(opencodeDir);
23644
24603
  agents.push("opencode");
23645
24604
  } catch {
23646
24605
  }
23647
- const traeDir = path24.join(home, ".trae");
24606
+ const traeDir = path25.join(home, ".trae");
23648
24607
  try {
23649
- await fs18.access(traeDir);
24608
+ await fs19.access(traeDir);
23650
24609
  agents.push("trae");
23651
24610
  } catch {
23652
24611
  }
@@ -23680,7 +24639,7 @@ async function installHooks(agent, projectRoot, global = false) {
23680
24639
  try {
23681
24640
  const { access: access2, unlink: unlink2 } = await import("fs/promises");
23682
24641
  await access2(entry.path);
23683
- const basename3 = path24.basename(entry.path);
24642
+ const basename3 = path25.basename(entry.path);
23684
24643
  if (basename3 === "AGENTS.md" || basename3 === "GEMINI.md" || basename3 === "CONTEXT.md") {
23685
24644
  continue;
23686
24645
  }
@@ -23735,8 +24694,8 @@ async function installHooks(agent, projectRoot, global = false) {
23735
24694
  case "opencode": {
23736
24695
  const pluginContent = generateOpenCodePlugin();
23737
24696
  const pluginPath = global ? getGlobalConfigPath(agent) : getProjectConfigPath(agent, projectRoot);
23738
- await fs18.mkdir(path24.dirname(pluginPath), { recursive: true });
23739
- await fs18.writeFile(pluginPath, pluginContent, "utf-8");
24697
+ await fs19.mkdir(path25.dirname(pluginPath), { recursive: true });
24698
+ await fs19.writeFile(pluginPath, pluginContent, "utf-8");
23740
24699
  try {
23741
24700
  const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
23742
24701
  await recordFile2(projectRoot, "hook", pluginPath, agent);
@@ -23765,14 +24724,14 @@ async function installHooks(agent, projectRoot, global = false) {
23765
24724
  default:
23766
24725
  generated = generateClaudeConfig();
23767
24726
  }
23768
- await fs18.mkdir(path24.dirname(configPath), { recursive: true });
24727
+ await fs19.mkdir(path25.dirname(configPath), { recursive: true });
23769
24728
  if (agent === "kiro") {
23770
24729
  const hookFiles = generateKiroHookFiles();
23771
- const hooksDir = path24.join(path24.dirname(configPath));
23772
- await fs18.mkdir(hooksDir, { recursive: true });
24730
+ const hooksDir = path25.join(path25.dirname(configPath));
24731
+ await fs19.mkdir(hooksDir, { recursive: true });
23773
24732
  for (const hf of hookFiles) {
23774
- const hookPath = path24.join(hooksDir, hf.filename);
23775
- await fs18.writeFile(hookPath, hf.content, "utf-8");
24733
+ const hookPath = path25.join(hooksDir, hf.filename);
24734
+ await fs19.writeFile(hookPath, hf.content, "utf-8");
23776
24735
  try {
23777
24736
  const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
23778
24737
  await recordFile2(projectRoot, "hook", hookPath, agent);
@@ -23782,7 +24741,7 @@ async function installHooks(agent, projectRoot, global = false) {
23782
24741
  } else {
23783
24742
  let existing = {};
23784
24743
  try {
23785
- const content = await fs18.readFile(configPath, "utf-8");
24744
+ const content = await fs19.readFile(configPath, "utf-8");
23786
24745
  existing = JSON.parse(content);
23787
24746
  } catch {
23788
24747
  }
@@ -23817,7 +24776,7 @@ async function installHooks(agent, projectRoot, global = false) {
23817
24776
  const h = merged.hooks;
23818
24777
  if (h) delete h.preToolUse;
23819
24778
  }
23820
- await fs18.writeFile(configPath, JSON.stringify(merged, null, 2), "utf-8");
24779
+ await fs19.writeFile(configPath, JSON.stringify(merged, null, 2), "utf-8");
23821
24780
  try {
23822
24781
  const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
23823
24782
  await recordFile2(projectRoot, "hook", configPath, agent);
@@ -23865,18 +24824,18 @@ async function installAgentRules(agent, projectRoot, global = false) {
23865
24824
  const rulesRoot = global ? os3.homedir() : projectRoot;
23866
24825
  const rulesPath = getAgentRulesPath(agent, rulesRoot, global);
23867
24826
  try {
23868
- await fs18.mkdir(path24.dirname(rulesPath), { recursive: true });
24827
+ await fs19.mkdir(path25.dirname(rulesPath), { recursive: true });
23869
24828
  if (agent === "claude" || agent === "codex" || agent === "opencode" || agent === "antigravity" || agent === "gemini-cli") {
23870
24829
  try {
23871
- const existing = await fs18.readFile(rulesPath, "utf-8");
23872
- await fs18.writeFile(rulesPath, mergeMemorixRulesContent(existing, rulesContent), "utf-8");
24830
+ const existing = await fs19.readFile(rulesPath, "utf-8");
24831
+ await fs19.writeFile(rulesPath, mergeMemorixRulesContent(existing, rulesContent), "utf-8");
23873
24832
  try {
23874
24833
  const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
23875
24834
  await recordFile2(projectRoot, "rule", rulesPath, agent);
23876
24835
  } catch {
23877
24836
  }
23878
24837
  } catch {
23879
- await fs18.writeFile(rulesPath, rulesContent, "utf-8");
24838
+ await fs19.writeFile(rulesPath, rulesContent, "utf-8");
23880
24839
  try {
23881
24840
  const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
23882
24841
  await recordFile2(projectRoot, "rule", rulesPath, agent);
@@ -23885,9 +24844,9 @@ async function installAgentRules(agent, projectRoot, global = false) {
23885
24844
  }
23886
24845
  } else {
23887
24846
  try {
23888
- await fs18.access(rulesPath);
24847
+ await fs19.access(rulesPath);
23889
24848
  } catch {
23890
- await fs18.writeFile(rulesPath, rulesContent, "utf-8");
24849
+ await fs19.writeFile(rulesPath, rulesContent, "utf-8");
23891
24850
  try {
23892
24851
  const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
23893
24852
  await recordFile2(projectRoot, "rule", rulesPath, agent);
@@ -23977,6 +24936,8 @@ alwaysApply: true
23977
24936
  "- Include `filesModified` when relevant",
23978
24937
  "- Use `topicKey` for topics that evolve over time (prevents duplicates)",
23979
24938
  '- For "why" decisions, use `memorix_store_reasoning`',
24939
+ '- 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`.',
24940
+ "- 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.",
23980
24941
  "",
23981
24942
  "**Don't store:** greetings, simple file reads, trivial commands (ls, pwd, git status).",
23982
24943
  "",
@@ -24013,16 +24974,16 @@ async function uninstallHooks(agent, projectRoot, global = false) {
24013
24974
  let success = false;
24014
24975
  try {
24015
24976
  if (agent === "kiro" || agent === "opencode") {
24016
- await fs18.unlink(configPath);
24977
+ await fs19.unlink(configPath);
24017
24978
  success = true;
24018
24979
  } else {
24019
- const content = await fs18.readFile(configPath, "utf-8");
24980
+ const content = await fs19.readFile(configPath, "utf-8");
24020
24981
  const config = JSON.parse(content);
24021
24982
  delete config.hooks;
24022
24983
  if (Object.keys(config).length === 0) {
24023
- await fs18.unlink(configPath);
24984
+ await fs19.unlink(configPath);
24024
24985
  } else {
24025
- await fs18.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
24986
+ await fs19.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
24026
24987
  }
24027
24988
  success = true;
24028
24989
  }
@@ -24035,10 +24996,10 @@ async function uninstallHooks(agent, projectRoot, global = false) {
24035
24996
  const agentFiles = prevFiles.filter((e) => e.agent === agent);
24036
24997
  for (const entry of agentFiles) {
24037
24998
  try {
24038
- const basename3 = path24.basename(entry.path);
24999
+ const basename3 = path25.basename(entry.path);
24039
25000
  if (basename3 === "AGENTS.md" || basename3 === "GEMINI.md" || basename3 === "CONTEXT.md") {
24040
25001
  try {
24041
- const content = await fs18.readFile(entry.path, "utf-8");
25002
+ const content = await fs19.readFile(entry.path, "utf-8");
24042
25003
  const memorixStart = content.indexOf("# Memorix");
24043
25004
  if (memorixStart >= 0) {
24044
25005
  const afterMemorix = content.substring(memorixStart);
@@ -24050,9 +25011,9 @@ async function uninstallHooks(agent, projectRoot, global = false) {
24050
25011
  }
24051
25012
  const cleaned = (before + "\n" + after).trim();
24052
25013
  if (cleaned.length === 0) {
24053
- await fs18.unlink(entry.path);
25014
+ await fs19.unlink(entry.path);
24054
25015
  } else {
24055
- await fs18.writeFile(entry.path, cleaned + "\n", "utf-8");
25016
+ await fs19.writeFile(entry.path, cleaned + "\n", "utf-8");
24056
25017
  }
24057
25018
  }
24058
25019
  } catch {
@@ -24061,7 +25022,7 @@ async function uninstallHooks(agent, projectRoot, global = false) {
24061
25022
  auditCleaned = true;
24062
25023
  continue;
24063
25024
  }
24064
- await fs18.unlink(entry.path);
25025
+ await fs19.unlink(entry.path);
24065
25026
  await removeFile2(projectRoot, entry.path);
24066
25027
  auditCleaned = true;
24067
25028
  } catch {
@@ -24073,14 +25034,14 @@ async function uninstallHooks(agent, projectRoot, global = false) {
24073
25034
  if (success) {
24074
25035
  try {
24075
25036
  const { rm } = await import("fs/promises");
24076
- const dir = path24.dirname(configPath);
25037
+ const dir = path25.dirname(configPath);
24077
25038
  let current = dir;
24078
25039
  for (let i = 0; i < 3; i++) {
24079
25040
  try {
24080
- const entries = await fs18.readdir(current);
25041
+ const entries = await fs19.readdir(current);
24081
25042
  if (entries.length === 0) {
24082
25043
  await rm(current, { recursive: true });
24083
- current = path24.dirname(current);
25044
+ current = path25.dirname(current);
24084
25045
  } else {
24085
25046
  break;
24086
25047
  }
@@ -24104,12 +25065,12 @@ async function getHookStatus(projectRoot) {
24104
25065
  let usedPath = projectPath;
24105
25066
  const verifiedByDefault = agent !== "opencode" && agent !== "pi";
24106
25067
  try {
24107
- await fs18.access(projectPath);
25068
+ await fs19.access(projectPath);
24108
25069
  installed = true;
24109
25070
  } catch {
24110
25071
  if (globalPath) {
24111
25072
  try {
24112
- await fs18.access(globalPath);
25073
+ await fs19.access(globalPath);
24113
25074
  installed = true;
24114
25075
  usedPath = globalPath;
24115
25076
  } catch {
@@ -24118,7 +25079,7 @@ async function getHookStatus(projectRoot) {
24118
25079
  }
24119
25080
  if (installed && agent === "opencode") {
24120
25081
  try {
24121
- const content = await fs18.readFile(usedPath, "utf-8");
25082
+ const content = await fs19.readFile(usedPath, "utf-8");
24122
25083
  const match = content.match(/@generated-version\s+(\d+)/);
24123
25084
  const installedVersion = match ? parseInt(match[1], 10) : 0;
24124
25085
  outdated = installedVersion < OPENCODE_PLUGIN_VERSION;
@@ -24149,11 +25110,11 @@ var init_installers = __esm({
24149
25110
  init_official_skills();
24150
25111
  OPENCODE_PLUGIN_VERSION = 7;
24151
25112
  AGENT_SKILL_DIRS = {
24152
- cursor: { project: path24.join(".cursor", "skills"), global: path24.join(".cursor", "skills") },
24153
- windsurf: { project: path24.join(".windsurf", "skills"), global: path24.join(".windsurf", "skills") },
24154
- kiro: { project: path24.join(".kiro", "skills"), global: path24.join(".kiro", "skills") },
24155
- opencode: { project: path24.join(".opencode", "skills"), global: path24.join(".config", "opencode", "skills") },
24156
- trae: { project: path24.join(".trae", "skills"), global: path24.join(".trae", "skills") }
25113
+ cursor: { project: path25.join(".cursor", "skills"), global: path25.join(".cursor", "skills") },
25114
+ windsurf: { project: path25.join(".windsurf", "skills"), global: path25.join(".windsurf", "skills") },
25115
+ kiro: { project: path25.join(".kiro", "skills"), global: path25.join(".kiro", "skills") },
25116
+ opencode: { project: path25.join(".opencode", "skills"), global: path25.join(".config", "opencode", "skills") },
25117
+ trae: { project: path25.join(".trae", "skills"), global: path25.join(".trae", "skills") }
24157
25118
  };
24158
25119
  PACKAGE_OWNED_HOOK_AGENTS = /* @__PURE__ */ new Set(["openclaw", "hermes", "omp"]);
24159
25120
  }
@@ -24167,9 +25128,9 @@ __export(hooks_path_exports, {
24167
25128
  resolveHooksDir: () => resolveHooksDir
24168
25129
  });
24169
25130
  import { existsSync as existsSync17, readFileSync as readFileSync13, statSync as statSync4, mkdirSync as mkdirSync4 } from "fs";
24170
- import path25 from "path";
25131
+ import path26 from "path";
24171
25132
  function resolveGitDir(projectRoot) {
24172
- const dotGit = path25.join(projectRoot, ".git");
25133
+ const dotGit = path26.join(projectRoot, ".git");
24173
25134
  if (!existsSync17(dotGit)) return null;
24174
25135
  const stat = statSync4(dotGit);
24175
25136
  if (stat.isDirectory()) {
@@ -24181,7 +25142,7 @@ function resolveGitDir(projectRoot) {
24181
25142
  const match = content.match(/^gitdir:\s*(.+)$/);
24182
25143
  if (match) {
24183
25144
  const gitdir = match[1].trim();
24184
- const resolved = path25.isAbsolute(gitdir) ? gitdir : path25.resolve(projectRoot, gitdir);
25145
+ const resolved = path26.isAbsolute(gitdir) ? gitdir : path26.resolve(projectRoot, gitdir);
24185
25146
  if (existsSync17(resolved)) {
24186
25147
  return resolved;
24187
25148
  }
@@ -24194,10 +25155,10 @@ function resolveGitDir(projectRoot) {
24194
25155
  function resolveHooksDir(projectRoot) {
24195
25156
  const gitDir = resolveGitDir(projectRoot);
24196
25157
  if (!gitDir) return null;
24197
- const hooksDir = path25.join(gitDir, "hooks");
25158
+ const hooksDir = path26.join(gitDir, "hooks");
24198
25159
  return {
24199
25160
  hooksDir,
24200
- hookPath: path25.join(hooksDir, "post-commit")
25161
+ hookPath: path26.join(hooksDir, "post-commit")
24201
25162
  };
24202
25163
  }
24203
25164
  function ensureHooksDir(projectRoot) {
@@ -27437,10 +28398,10 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
27437
28398
  pathsToCheck.push(join19(homedir21(), ".gemini", "antigravity", "mcp_config.json"));
27438
28399
  }
27439
28400
  const merged = /* @__PURE__ */ new Map();
27440
- for (const path26 of pathsToCheck) {
27441
- if (existsSync10(path26)) {
28401
+ for (const path27 of pathsToCheck) {
28402
+ if (existsSync10(path27)) {
27442
28403
  try {
27443
- const content = readFileSync7(path26, "utf-8");
28404
+ const content = readFileSync7(path27, "utf-8");
27444
28405
  const servers = adapter.parse(content);
27445
28406
  for (const s of servers) {
27446
28407
  if (!merged.has(s.name)) merged.set(s.name, s);
@@ -28231,7 +29192,7 @@ The path should point to a directory containing a .git folder.`
28231
29192
  };
28232
29193
  const server = existingServer ?? new McpServer({
28233
29194
  name: "memorix",
28234
- version: true ? "1.2.9" : "1.0.1"
29195
+ version: true ? "1.3.0" : "1.0.1"
28235
29196
  });
28236
29197
  const originalRegisterTool = server.registerTool.bind(server);
28237
29198
  server.registerTool = ((name, ...args) => {
@@ -28311,7 +29272,7 @@ The path should point to a directory containing a .git folder.`
28311
29272
  "memorix_store",
28312
29273
  {
28313
29274
  title: "Store Memory",
28314
- 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.",
29275
+ 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.",
28315
29276
  inputSchema: {
28316
29277
  entityName: z2.string().describe('The entity this observation belongs to (e.g., "auth-module", "port-config")'),
28317
29278
  type: z2.enum(OBSERVATION_TYPES).describe("Observation type for classification"),
@@ -28333,30 +29294,43 @@ The path should point to a directory containing a .git folder.`
28333
29294
  visibility: z2.enum(["personal", "project", "team"]).optional().describe(
28334
29295
  "Retrieval scope. Project is the normal shared default; personal/team require memorix_session_start with joinTeam=true."
28335
29296
  ),
29297
+ longTerm: z2.object({
29298
+ kind: z2.enum(["episodic", "semantic", "procedural"]).describe("Cognitive kind for an additional long-term candidate."),
29299
+ 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."),
29300
+ tags: z2.array(z2.string()).optional().describe("Small set of retrieval tags for the candidate."),
29301
+ applicability: z2.string().optional().describe("When this durable fact or procedure applies.")
29302
+ }).optional().describe("Optional explicit request to create a source-backed long-term candidate alongside this observation. Candidates are not automatically injected."),
28336
29303
  overrideReadOnly: z2.boolean().optional().describe(
28337
29304
  "Use only when the user explicitly asks to save memory during a read-only or no-modification task."
28338
29305
  )
28339
29306
  }
28340
29307
  },
28341
- async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility, overrideReadOnly }) => {
29308
+ async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility, longTerm, overrideReadOnly }) => {
28342
29309
  const unresolved = requireResolvedProject("store memory in the current project");
28343
29310
  if (unresolved) return unresolved;
28344
29311
  const readOnlyBoundary = blockReadOnlyAutopilotWrite(overrideReadOnly);
28345
29312
  if (readOnlyBoundary) return readOnlyBoundary;
28346
- const requestedVisibility = visibility ?? "project";
29313
+ const requestedVisibility = longTerm?.scope === "user" ? "personal" : longTerm?.scope === "team" ? "team" : visibility;
29314
+ const effectiveVisibility = requestedVisibility ?? "project";
28347
29315
  const reader = getObservationReader();
28348
- if (requestedVisibility !== "project" && !currentAgentId) {
29316
+ if (effectiveVisibility !== "project" && !currentAgentId) {
28349
29317
  return {
28350
29318
  content: [{ type: "text", text: "Personal or team memory requires memorix_session_start with joinTeam=true so Memorix can bind an owner." }],
28351
29319
  isError: true
28352
29320
  };
28353
29321
  }
28354
- if (requestedVisibility === "team" && !reader.isTeamMember) {
29322
+ if (effectiveVisibility === "team" && !reader.isTeamMember) {
28355
29323
  return {
28356
29324
  content: [{ type: "text", text: "Team memory requires an active coordination membership in the current project." }],
28357
29325
  isError: true
28358
29326
  };
28359
29327
  }
29328
+ if (longTerm?.scope === "team" && !reader.isTeamMember) {
29329
+ return {
29330
+ content: [{ type: "text", text: "Team long-term memory requires an active coordination membership in the current project." }],
29331
+ isError: true
29332
+ };
29333
+ }
28360
29334
  try {
28361
29335
  return await withFreshIndex(async () => {
28362
29336
  let entityName = rawEntityName;
@@ -28378,7 +29352,7 @@ The path should point to a directory containing a .git folder.`
28378
29352
  const useFormation = formationMode === "active";
28379
29353
  let formationResult = null;
28380
29354
  let formationNote = "";
28381
- if (useFormation && !topicKey && !progress) {
29355
+ if (useFormation && !topicKey && !progress && !longTerm) {
28382
29356
  let currentFormationStage = "setup";
28383
29357
  const completedFormationStages = {};
28384
29358
  const formationStartTime = Date.now();
@@ -28529,7 +29503,7 @@ ${modeIcon} Formation[active]: ${formationResult.evaluation.category} (${formati
28529
29503
  }
28530
29504
  let compactAction = "";
28531
29505
  let compactMerged = false;
28532
- if (!useFormation && !topicKey && !progress) {
29506
+ if (!useFormation && !topicKey && !progress && !longTerm) {
28533
29507
  try {
28534
29508
  const searchResult = await compactSearch({
28535
29509
  query: `${title} ${narrative.substring(0, 200)}`,
@@ -28686,9 +29660,33 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
28686
29660
  sourceDetail: "explicit",
28687
29661
  valueCategory: formationResult?.evaluation.category,
28688
29662
  createdByAgentId: currentAgentId,
28689
- visibility,
29663
+ visibility: requestedVisibility,
28690
29664
  visibilityReader: reader
28691
29665
  });
29666
+ let longTermNote = "";
29667
+ if (longTerm) {
29668
+ try {
29669
+ const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
29670
+ const promoted = await promoteObservationToLongTermMemory2({
29671
+ dataDir: projectDir2,
29672
+ observation: obs,
29673
+ scope: longTerm.scope,
29674
+ kind: longTerm.kind,
29675
+ tags: longTerm.tags,
29676
+ applicability: longTerm.applicability,
29677
+ reader: {
29678
+ projectId: project.id,
29679
+ ...reader.agentId ? { agentId: reader.agentId } : {},
29680
+ ...reader.isTeamMember ? { isTeamMember: true } : {}
29681
+ }
29682
+ });
29683
+ longTermNote = `
29684
+ Long-term candidate: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}). Review it with the CLI before it is delivered automatically.`;
29685
+ } catch (longTermError) {
29686
+ longTermNote = `
29687
+ [WARN] Observation stored, but long-term candidate was not created: ${longTermError instanceof Error ? longTermError.message : "unknown error"}`;
29688
+ }
29689
+ }
28692
29690
  await graphManager.addObservations([
28693
29691
  { entityName, contents: [`[#${obs.id}] ${title}`] }
28694
29692
  ]);
@@ -28705,7 +29703,7 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
28705
29703
  const enrichment = enrichmentParts.length > 0 ? `
28706
29704
  Auto-enriched: ${enrichmentParts.join(", ")}` : "";
28707
29705
  const action = upserted ? "[UPDATED] Updated" : "[OK] Stored";
28708
- if (!useFormation && !topicKey && !progress) {
29706
+ if (!useFormation && !topicKey && !progress && !longTerm) {
28709
29707
  const shadowFormation = async () => {
28710
29708
  let oldCompactDecision = null;
28711
29709
  try {
@@ -28795,7 +29793,7 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
28795
29793
  {
28796
29794
  type: "text",
28797
29795
  text: `${action} observation #${obs.id} "${title}" (~${obs.tokens} tokens)
28798
- Entity: ${entityName} | Type: ${type} | Project: ${project.id}${obs.topicKey ? ` | Topic: ${obs.topicKey}` : ""}${compactAction}${compressionNote}${enrichment}${formationNote}${attributionWarning}`
29796
+ Entity: ${entityName} | Type: ${type} | Project: ${project.id}${obs.topicKey ? ` | Topic: ${obs.topicKey}` : ""}${compactAction}${compressionNote}${enrichment}${formationNote}${attributionWarning}${longTermNote}`
28799
29797
  }
28800
29798
  ]
28801
29799
  };
@@ -29168,7 +30166,8 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
29168
30166
  } : {},
29169
30167
  ...external.outline ? { semanticCode: external.outline } : {},
29170
30168
  providerQuality: external.quality,
29171
- ...external.caution ? { runtimeCautions: [{ kind: "external-codegraph-fallback", message: external.caution }] } : {}
30169
+ ...external.caution ? { runtimeCautions: [{ kind: "external-codegraph-fallback", message: external.caution }] } : {},
30170
+ reader: getObservationReader()
29172
30171
  });
29173
30172
  const text = buildContextPackPrompt2(pack);
29174
30173
  return {
@@ -29842,7 +30841,7 @@ ${actions.join("\n")}`
29842
30841
  "memorix_detail",
29843
30842
  {
29844
30843
  title: "Memory Details",
29845
- 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.',
30844
+ 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.',
29846
30845
  inputSchema: {
29847
30846
  ids: z2.array(z2.number()).optional().describe("Observation IDs to fetch (legacy, from memorix_search results)"),
29848
30847
  refs: z2.array(
@@ -29851,7 +30850,7 @@ ${actions.join("\n")}`
29851
30850
  projectId: z2.string().optional().describe("Project ID for global-search disambiguation")
29852
30851
  })
29853
30852
  ).optional().describe("Explicit observation refs. Prefer this for global search results."),
29854
- typedRefs: z2.array(z2.string()).optional().describe('Typed memory refs from search results, e.g. "obs:42", "skill:3", "obs:42@org/proj"'),
30853
+ 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"'),
29855
30854
  purpose: z2.string().optional().describe(
29856
30855
  "Why this must expand beyond the latest Autopilot brief. Name the missing fact or the user's explicit request."
29857
30856
  ),
@@ -29864,14 +30863,19 @@ ${actions.join("\n")}`
29864
30863
  const safeIds = coerceNumberArray(ids);
29865
30864
  const safeRefs = coerceObservationRefs(refs);
29866
30865
  const safeTypedRefs = coerceStringArray(typedRefs);
29867
- const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id) || safeTypedRefs.some((ref) => ref.includes("@") && !ref.endsWith(`@${project.id}`));
30866
+ const durableIds = safeTypedRefs.flatMap((ref) => {
30867
+ const match = /^durable:([0-9a-f-]{36})$/i.exec(ref.trim());
30868
+ return match ? [match[1]] : [];
30869
+ });
30870
+ const observationTypedRefs = safeTypedRefs.filter((ref) => !/^durable:/i.test(ref.trim()));
30871
+ const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id) || observationTypedRefs.some((ref) => ref.includes("@") && !ref.endsWith(`@${project.id}`));
29868
30872
  if (!hasCrossProjectRef) {
29869
30873
  const boundary = requireExplicitAutopilotExpansion("memorix_detail", purpose);
29870
30874
  if (boundary) return boundary;
29871
30875
  const requestedObservationIds = [
29872
30876
  ...safeIds,
29873
30877
  ...safeRefs.map((ref) => ref.id),
29874
- ...safeTypedRefs.flatMap((ref) => {
30878
+ ...observationTypedRefs.flatMap((ref) => {
29875
30879
  const match = /^obs:(\d+)(?:@.+)?$/i.exec(ref.trim());
29876
30880
  return match ? [Number(match[1])] : [];
29877
30881
  })
@@ -29879,15 +30883,36 @@ ${actions.join("\n")}`
29879
30883
  const duplicate = blockCoveredAutopilotEvidence("memorix_detail", requestedObservationIds, force);
29880
30884
  if (duplicate) return duplicate;
29881
30885
  }
29882
- let result;
30886
+ const formatted = [];
29883
30887
  try {
29884
- const reader = getObservationReader(hasCrossProjectRef ? "global" : "project");
29885
- if (safeTypedRefs.length > 0) {
29886
- result = await compactDetail(safeTypedRefs, { reader });
29887
- } else if (safeRefs.length > 0) {
29888
- result = await compactDetail(safeRefs, { reader });
29889
- } else {
29890
- result = await compactDetail(safeIds.map((id) => ({ id, projectId: project.id })), { reader });
30888
+ if (durableIds.length > 0) {
30889
+ const [{ getLongTermMemoryDetail: getLongTermMemoryDetail2 }, { resolveLocalMemoryOwner: resolveLocalMemoryOwner2 }] = await Promise.all([
30890
+ Promise.resolve().then(() => (init_long_term(), long_term_exports)),
30891
+ Promise.resolve().then(() => (init_owner(), owner_exports))
30892
+ ]);
30893
+ const owner = await resolveLocalMemoryOwner2(projectDir2, { create: false });
30894
+ const observationReader = getObservationReader();
30895
+ const durableReader = {
30896
+ projectId: project.id,
30897
+ ...owner ? { ownerId: owner.id } : {},
30898
+ ...observationReader.agentId ? { agentId: observationReader.agentId } : {},
30899
+ ...observationReader.isTeamMember ? { isTeamMember: true } : {}
30900
+ };
30901
+ const details = await Promise.all(durableIds.slice(0, 5).map(async (id) => {
30902
+ try {
30903
+ return await getLongTermMemoryDetail2({ dataDir: projectDir2, id, reader: durableReader });
30904
+ } catch (error) {
30905
+ return { error: error instanceof Error ? error.message : String(error), id };
30906
+ }
30907
+ }));
30908
+ formatted.push(JSON.stringify({ durableMemory: details }, null, 2));
30909
+ }
30910
+ if (observationTypedRefs.length > 0 || safeRefs.length > 0 || safeIds.length > 0) {
30911
+ const reader = getObservationReader(hasCrossProjectRef ? "global" : "project");
30912
+ 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 });
30913
+ formatted.push(
30914
+ 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(", ")}`
30915
+ );
29891
30916
  }
29892
30917
  } catch (err) {
29893
30918
  return { content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }], isError: true };
@@ -29896,7 +30921,7 @@ ${actions.join("\n")}`
29896
30921
  content: [
29897
30922
  {
29898
30923
  type: "text",
29899
- 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(", ")}`
30924
+ 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(", ")}`
29900
30925
  }
29901
30926
  ]
29902
30927
  };
@@ -30616,9 +31641,9 @@ ${skill2.content}` }]
30616
31641
  lines.push(`- **Description**: ${sk.description}`);
30617
31642
  lines.push(`- **Observations**: ${sk.content.split("\n").length} lines of knowledge`);
30618
31643
  if (write && target) {
30619
- const path26 = engine.writeSkill(sk, target);
30620
- if (path26) {
30621
- lines.push(`- [OK] **Written**: \`${path26}\``);
31644
+ const path27 = engine.writeSkill(sk, target);
31645
+ if (path27) {
31646
+ lines.push(`- [OK] **Written**: \`${path27}\``);
30622
31647
  } else {
30623
31648
  lines.push(`- [ERROR] Failed to write`);
30624
31649
  }
@@ -32049,7 +33074,7 @@ fi
32049
33074
  async function main2() {
32050
33075
  const { homedir: homedir26 } = await import("os");
32051
33076
  const { existsSync: existsSync18, readFileSync: readFileSync14 } = await import("fs");
32052
- const path26 = await import("path");
33077
+ const path27 = await import("path");
32053
33078
  const { detectProject: detectProject2, findGitInSubdirs: findGitInSubdirs2, isSystemDirectory: isSystemDirectory2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
32054
33079
  const { resolveServeProject: resolveServeProject2 } = await Promise.resolve().then(() => (init_serve_shared(), serve_shared_exports));
32055
33080
  let safeCwd;
@@ -32058,7 +33083,7 @@ async function main2() {
32058
33083
  } catch {
32059
33084
  safeCwd = homedir26();
32060
33085
  }
32061
- const lastRootFile = path26.join(homedir26(), ".memorix", "last-project-root");
33086
+ const lastRootFile = path27.join(homedir26(), ".memorix", "last-project-root");
32062
33087
  let lastKnownProjectRoot;
32063
33088
  if (existsSync18(lastRootFile)) {
32064
33089
  try {