memorix 1.4.5 → 1.5.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 (75) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/CLAUDE.md +7 -3
  3. package/README.md +5 -2
  4. package/dist/cli/index.js +1470 -1014
  5. package/dist/index.js +1279 -989
  6. package/dist/{maintenance-jobs-B1-W0ssz.d.ts → maintenance-jobs-DVRF-46r.d.ts} +1 -1
  7. package/dist/maintenance-runner.d.ts +1 -1
  8. package/dist/maintenance-runner.js +1769 -830
  9. package/dist/media-audio-runner.js +1774 -836
  10. package/dist/media-video-runner.js +1769 -831
  11. package/dist/memcode-runtime/CHANGELOG.md +78 -0
  12. package/dist/sdk.js +1271 -988
  13. package/dist/types.d.ts +6 -0
  14. package/dist/vector-backfill-runner.d.ts +1 -1
  15. package/dist/vector-backfill-runner.js +1765 -827
  16. package/docs/1.4.5-AGENT-AUTO-MEMORY-RESEARCH.md +95 -0
  17. package/docs/API_REFERENCE.md +23 -4
  18. package/docs/CONFIGURATION.md +29 -3
  19. package/docs/SETUP.md +5 -3
  20. package/package.json +1 -1
  21. package/plugins/antigravity/memorix/mcp_config.json +3 -1
  22. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  23. package/plugins/claude/memorix/.mcp.json +1 -1
  24. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  25. package/plugins/codebuddy/memorix/.mcp.json +1 -1
  26. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  27. package/plugins/codex/memorix/.mcp.json +1 -1
  28. package/plugins/copilot/memorix/.mcp.json +1 -1
  29. package/plugins/copilot/memorix/plugin.json +1 -1
  30. package/plugins/gemini/memorix/gemini-extension.json +4 -2
  31. package/plugins/omp/memorix/package.json +1 -1
  32. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  33. package/plugins/openclaw/memorix/.mcp.json +3 -1
  34. package/plugins/pi/memorix/package.json +1 -1
  35. package/server.json +2 -2
  36. package/src/cli/commands/dashboard.ts +14 -3
  37. package/src/cli/commands/ingest-commit.ts +47 -3
  38. package/src/cli/commands/memory.ts +17 -4
  39. package/src/cli/commands/purge.ts +120 -0
  40. package/src/cli/commands/serve-http.ts +39 -5
  41. package/src/cli/commands/setup.ts +3 -1
  42. package/src/cli/index.ts +3 -804
  43. package/src/codegraph/auto-context.ts +82 -1
  44. package/src/config/yaml-loader.ts +3 -8
  45. package/src/config.ts +23 -7
  46. package/src/evaluation/session-replay.ts +98 -0
  47. package/src/git/extractor.ts +5 -3
  48. package/src/hooks/installers/index.ts +15 -0
  49. package/src/knowledge/workset.ts +58 -0
  50. package/src/memory/long-term.ts +97 -2
  51. package/src/runtime/control-plane-maintenance.ts +1 -0
  52. package/src/runtime/isolated-maintenance.ts +1 -0
  53. package/src/runtime/lifecycle.ts +19 -0
  54. package/src/runtime/maintenance-jobs.ts +1 -0
  55. package/src/runtime/project-maintenance.ts +8 -0
  56. package/src/search/surfaced-registry.ts +62 -0
  57. package/src/server.ts +74 -14
  58. package/src/store/orama-store.ts +17 -1
  59. package/src/types.ts +6 -0
  60. package/src/cli/commands/audit-list.ts +0 -89
  61. package/src/cli/commands/purge-all-memory.ts +0 -85
  62. package/src/cli/commands/purge-project-memory.ts +0 -83
  63. package/src/cli/commands/uninstall-project-artifacts.ts +0 -86
  64. package/src/cli/tui/Sidebar.tsx +0 -179
  65. package/src/cli/tui/interactive-commands.ts +0 -261
  66. package/src/cli/tui/markdown-render.tsx +0 -371
  67. package/src/cli/tui/use-mouse.ts +0 -157
  68. package/src/cli/tui/useNavigation.ts +0 -56
  69. package/src/orchestrate/context-compact.ts +0 -188
  70. package/src/team/file-locks.ts +0 -173
  71. package/src/team/index.ts +0 -23
  72. package/src/team/messages.ts +0 -203
  73. package/src/team/persistence.ts +0 -96
  74. package/src/team/registry.ts +0 -158
  75. package/src/team/tasks.ts +0 -211
package/dist/sdk.js CHANGED
@@ -3377,6 +3377,7 @@ var init_resolved_config = __esm({
3377
3377
  // src/config.ts
3378
3378
  var config_exports = {};
3379
3379
  __export(config_exports, {
3380
+ defaultEmbeddingModelFor: () => defaultEmbeddingModelFor,
3380
3381
  getAgentLLMApiKey: () => getAgentLLMApiKey,
3381
3382
  getAgentLLMBaseUrl: () => getAgentLLMBaseUrl,
3382
3383
  getAgentLLMModel: () => getAgentLLMModel,
@@ -3393,7 +3394,6 @@ __export(config_exports, {
3393
3394
  getLLMProvider: () => getLLMProvider,
3394
3395
  getLoadedEnvFiles: () => getLoadedEnvFiles,
3395
3396
  getServerConfig: () => getServerConfig,
3396
- getTeamConfig: () => getTeamConfig,
3397
3397
  loadDotenv: () => loadDotenv,
3398
3398
  loadFileConfig: () => loadFileConfig,
3399
3399
  loadYamlConfig: () => loadYamlConfig,
@@ -3440,7 +3440,19 @@ function getEmbeddingBaseUrl() {
3440
3440
  return getResolvedEmbeddingLane().baseUrl || "https://api.openai.com/v1";
3441
3441
  }
3442
3442
  function getEmbeddingModel() {
3443
- return getResolvedEmbeddingLane().model || "text-embedding-3-small";
3443
+ const lane = getResolvedEmbeddingLane();
3444
+ return defaultEmbeddingModelFor(lane.baseUrl, lane.model);
3445
+ }
3446
+ function defaultEmbeddingModelFor(baseUrl, explicitModel) {
3447
+ if (explicitModel) return explicitModel;
3448
+ try {
3449
+ const url = baseUrl ? new URL(baseUrl) : null;
3450
+ if (url && (url.hostname === "openrouter.ai" || url.hostname.endsWith(".openrouter.ai"))) {
3451
+ return "qwen/qwen3-embedding-8b";
3452
+ }
3453
+ } catch {
3454
+ }
3455
+ return "text-embedding-3-small";
3444
3456
  }
3445
3457
  function getEmbeddingDimensions() {
3446
3458
  return getResolvedEmbeddingLane().dimensions ?? null;
@@ -3465,14 +3477,10 @@ function getServerConfig() {
3465
3477
  port: server.port
3466
3478
  };
3467
3479
  }
3468
- function getTeamConfig() {
3469
- return loadYamlConfig().team ?? {};
3470
- }
3471
3480
  var init_config = __esm({
3472
3481
  "src/config.ts"() {
3473
3482
  "use strict";
3474
3483
  init_esm_shims();
3475
- init_yaml_loader();
3476
3484
  init_resolved_config();
3477
3485
  init_dotenv_loader();
3478
3486
  init_legacy_loader();
@@ -5757,9 +5765,9 @@ async function saveRegistry(baseDir) {
5757
5765
  await fs7.mkdir(path9.dirname(filePath), { recursive: true });
5758
5766
  await fs7.writeFile(filePath, JSON.stringify(registryCache, null, 2), "utf-8");
5759
5767
  }
5760
- function findMatchingGroup(registry, projectInfo) {
5768
+ function findMatchingGroup(registry2, projectInfo) {
5761
5769
  const normalizedRoot = normalizePath(projectInfo.rootPath);
5762
- for (const group of registry.groups) {
5770
+ for (const group of registry2.groups) {
5763
5771
  if (group.aliases.includes(projectInfo.id)) return group;
5764
5772
  if (group.rootPaths.some((rp) => rp === normalizedRoot)) return group;
5765
5773
  if (projectInfo.gitRemote && group.gitRemote && group.gitRemote === projectInfo.gitRemote) {
@@ -5772,9 +5780,9 @@ function selectCanonical(aliases) {
5772
5780
  return [...aliases].sort((a, b) => idPriority(b) - idPriority(a))[0];
5773
5781
  }
5774
5782
  async function registerAlias(projectInfo, baseDir) {
5775
- const registry = await loadRegistry(baseDir);
5783
+ const registry2 = await loadRegistry(baseDir);
5776
5784
  const normalizedRoot = normalizePath(projectInfo.rootPath);
5777
- const existingGroup = findMatchingGroup(registry, projectInfo);
5785
+ const existingGroup = findMatchingGroup(registry2, projectInfo);
5778
5786
  if (existingGroup) {
5779
5787
  let changed = false;
5780
5788
  if (!existingGroup.aliases.includes(projectInfo.id)) {
@@ -5805,13 +5813,13 @@ async function registerAlias(projectInfo, baseDir) {
5805
5813
  rootPaths: [normalizedRoot],
5806
5814
  ...projectInfo.gitRemote ? { gitRemote: projectInfo.gitRemote } : {}
5807
5815
  };
5808
- registry.groups.push(newGroup);
5816
+ registry2.groups.push(newGroup);
5809
5817
  await saveRegistry(baseDir);
5810
5818
  return newGroup.canonical;
5811
5819
  }
5812
5820
  async function resolveAliases(projectId, baseDir) {
5813
- const registry = await loadRegistry(baseDir);
5814
- for (const group of registry.groups) {
5821
+ const registry2 = await loadRegistry(baseDir);
5822
+ for (const group of registry2.groups) {
5815
5823
  if (group.aliases.includes(projectId) || group.canonical === projectId) {
5816
5824
  return [...group.aliases];
5817
5825
  }
@@ -5819,8 +5827,8 @@ async function resolveAliases(projectId, baseDir) {
5819
5827
  return [projectId];
5820
5828
  }
5821
5829
  async function getCanonicalId(projectId, baseDir) {
5822
- const registry = await loadRegistry(baseDir);
5823
- for (const group of registry.groups) {
5830
+ const registry2 = await loadRegistry(baseDir);
5831
+ for (const group of registry2.groups) {
5824
5832
  if (group.aliases.includes(projectId) || group.canonical === projectId) {
5825
5833
  return group.canonical;
5826
5834
  }
@@ -5828,12 +5836,12 @@ async function getCanonicalId(projectId, baseDir) {
5828
5836
  return projectId;
5829
5837
  }
5830
5838
  async function getAllAliasGroups(baseDir) {
5831
- const registry = await loadRegistry(baseDir);
5832
- return registry.groups;
5839
+ const registry2 = await loadRegistry(baseDir);
5840
+ return registry2.groups;
5833
5841
  }
5834
5842
  async function autoMergeByBaseName(observedIds, baseDir) {
5835
5843
  if (observedIds.length <= 1) return 0;
5836
- const registry = await loadRegistry(baseDir);
5844
+ const registry2 = await loadRegistry(baseDir);
5837
5845
  const byBaseName = /* @__PURE__ */ new Map();
5838
5846
  for (const id of observedIds) {
5839
5847
  const baseName = id.split("/").pop() ?? id;
@@ -5846,7 +5854,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
5846
5854
  const existingGroups = /* @__PURE__ */ new Set();
5847
5855
  const ungroupedIds = [];
5848
5856
  for (const id of ids) {
5849
- const groupIdx = registry.groups.findIndex(
5857
+ const groupIdx = registry2.groups.findIndex(
5850
5858
  (g) => g.aliases.includes(id) || g.canonical === id
5851
5859
  );
5852
5860
  if (groupIdx >= 0) {
@@ -5860,7 +5868,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
5860
5868
  const canonical = selectCanonical(allIdsInGroup);
5861
5869
  if (existingGroups.size > 0) {
5862
5870
  const primaryIdx = [...existingGroups][0];
5863
- const primaryGroup = registry.groups[primaryIdx];
5871
+ const primaryGroup = registry2.groups[primaryIdx];
5864
5872
  for (const id of allIdsInGroup) {
5865
5873
  if (!primaryGroup.aliases.includes(id)) {
5866
5874
  primaryGroup.aliases.push(id);
@@ -5868,7 +5876,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
5868
5876
  }
5869
5877
  const otherIdxs = [...existingGroups].slice(1).sort((a, b) => b - a);
5870
5878
  for (const idx of otherIdxs) {
5871
- const other = registry.groups[idx];
5879
+ const other = registry2.groups[idx];
5872
5880
  for (const alias of other.aliases) {
5873
5881
  if (!primaryGroup.aliases.includes(alias)) {
5874
5882
  primaryGroup.aliases.push(alias);
@@ -5882,12 +5890,12 @@ async function autoMergeByBaseName(observedIds, baseDir) {
5882
5890
  if (other.gitRemote && !primaryGroup.gitRemote) {
5883
5891
  primaryGroup.gitRemote = other.gitRemote;
5884
5892
  }
5885
- registry.groups.splice(idx, 1);
5893
+ registry2.groups.splice(idx, 1);
5886
5894
  }
5887
5895
  primaryGroup.canonical = selectCanonical(primaryGroup.aliases);
5888
5896
  mergeCount++;
5889
5897
  } else {
5890
- registry.groups.push({
5898
+ registry2.groups.push({
5891
5899
  canonical,
5892
5900
  aliases: allIdsInGroup,
5893
5901
  rootPaths: []
@@ -6523,7 +6531,7 @@ async function searchObservations(options) {
6523
6531
  const expandedEmbeddingQuery = quality === "thorough" && tier === "heavy" ? await maybeExpandSearchQuery(options.query) : options.query;
6524
6532
  mark("queryExpansion");
6525
6533
  const intentResult = quality === "fast" || !hasQuery ? null : detectQueryIntent(originalQuery);
6526
- const requestLimit = projectIds ? (options.limit ?? 20) * 3 : options.limit ?? 20;
6534
+ const requestLimit = projectIds || options.surfacedIds && options.surfacedIds.length > 0 ? (options.limit ?? 20) * 3 : options.limit ?? 20;
6527
6535
  const defaultBoost = {
6528
6536
  title: 3,
6529
6537
  entityName: 2,
@@ -6755,6 +6763,14 @@ async function searchObservations(options) {
6755
6763
  });
6756
6764
  intermediate.sort((a, b) => b.score - a.score);
6757
6765
  }
6766
+ if (options.surfacedIds && options.surfacedIds.length > 0) {
6767
+ const SURFACED_PENALTY = 0.5;
6768
+ const surfaced = new Set(options.surfacedIds);
6769
+ intermediate = intermediate.map(
6770
+ (entry) => surfaced.has(entry.id) ? { ...entry, score: (entry.score ?? 0) * SURFACED_PENALTY } : entry
6771
+ );
6772
+ intermediate.sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
6773
+ }
6758
6774
  if (options.since) {
6759
6775
  const sinceDate = new Date(options.since).getTime();
6760
6776
  intermediate = intermediate.filter((e2) => new Date(e2.rawTime).getTime() >= sinceDate);
@@ -7746,7 +7762,8 @@ var init_maintenance_jobs = __esm({
7746
7762
  "claim-requalification",
7747
7763
  "knowledge-compile",
7748
7764
  "knowledge-lint",
7749
- "workflow-index"
7765
+ "workflow-index",
7766
+ "long-term-maintenance"
7750
7767
  ];
7751
7768
  DEFAULT_MAX_ATTEMPTS = 8;
7752
7769
  DEFAULT_LEASE_MS = 3e4;
@@ -8155,6 +8172,7 @@ __export(lifecycle_exports, {
8155
8172
  enqueueClaimRequalification: () => enqueueClaimRequalification,
8156
8173
  enqueueCodegraphRefresh: () => enqueueCodegraphRefresh,
8157
8174
  enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups,
8175
+ enqueueLongTermMaintenance: () => enqueueLongTermMaintenance,
8158
8176
  enqueueObservationQualification: () => enqueueObservationQualification
8159
8177
  });
8160
8178
  function queueFor(input) {
@@ -8198,6 +8216,14 @@ function enqueueObservationQualification(input) {
8198
8216
  payload: { source: input.source, limit: 100 }
8199
8217
  });
8200
8218
  }
8219
+ function enqueueLongTermMaintenance(input) {
8220
+ queueFor(input).enqueue({
8221
+ projectId: input.projectId,
8222
+ kind: "long-term-maintenance",
8223
+ dedupeKey: "long-term-maintenance",
8224
+ payload: { source: input.source }
8225
+ });
8226
+ }
8201
8227
  function enqueueKnowledgeFollowups(input) {
8202
8228
  const queue = queueFor(input);
8203
8229
  const payload = {
@@ -8377,7 +8403,8 @@ var init_isolated_maintenance = __esm({
8377
8403
  "claim-requalification",
8378
8404
  "knowledge-compile",
8379
8405
  "knowledge-lint",
8380
- "workflow-index"
8406
+ "workflow-index",
8407
+ "long-term-maintenance"
8381
8408
  ];
8382
8409
  DEFAULT_TIMEOUT_MS = 5 * 6e4;
8383
8410
  MAX_CHILD_OUTPUT_BYTES = 1e6;
@@ -144224,6 +144251,889 @@ var init_retention = __esm({
144224
144251
  }
144225
144252
  });
144226
144253
 
144254
+ // src/memory/owner.ts
144255
+ var owner_exports = {};
144256
+ __export(owner_exports, {
144257
+ resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
144258
+ });
144259
+ import { randomUUID as randomUUID6 } from "crypto";
144260
+ import { promises as fs12 } from "fs";
144261
+ import path19 from "path";
144262
+ function ownerPath(dataDir) {
144263
+ const resolved = path19.resolve(dataDir);
144264
+ const home = path19.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path19.dirname(resolved) : resolved;
144265
+ return path19.join(home, "memorix-user.json");
144266
+ }
144267
+ function normalizedOwnerId(value) {
144268
+ const trimmed = value?.trim();
144269
+ return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
144270
+ }
144271
+ async function readOwnerFile(filePath) {
144272
+ try {
144273
+ const value = JSON.parse(await fs12.readFile(filePath, "utf8"));
144274
+ const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
144275
+ return id ? { id, source: "local-file" } : void 0;
144276
+ } catch {
144277
+ return void 0;
144278
+ }
144279
+ }
144280
+ async function resolveLocalMemoryOwner(dataDir, options = {}) {
144281
+ const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
144282
+ if (configured) return { id: configured, source: "environment" };
144283
+ const filePath = ownerPath(dataDir);
144284
+ const existing = await readOwnerFile(filePath);
144285
+ if (existing || !options.create) return existing;
144286
+ await fs12.mkdir(path19.dirname(filePath), { recursive: true });
144287
+ const created = {
144288
+ version: 1,
144289
+ id: "local-" + randomUUID6(),
144290
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
144291
+ };
144292
+ try {
144293
+ await fs12.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
144294
+ return { id: created.id, source: "local-file" };
144295
+ } catch (error) {
144296
+ if (error?.code !== "EEXIST") throw error;
144297
+ const concurrent = await readOwnerFile(filePath);
144298
+ if (concurrent) return concurrent;
144299
+ throw new Error("Memorix could not establish a valid local memory owner identity.");
144300
+ }
144301
+ }
144302
+ var ID_PATTERN;
144303
+ var init_owner = __esm({
144304
+ "src/memory/owner.ts"() {
144305
+ "use strict";
144306
+ init_esm_shims();
144307
+ ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
144308
+ }
144309
+ });
144310
+
144311
+ // src/memory/long-term-store.ts
144312
+ import { randomUUID as randomUUID7 } from "crypto";
144313
+ function optionalText2(value) {
144314
+ return typeof value === "string" && value.trim() ? value : void 0;
144315
+ }
144316
+ function arrayValue2(value) {
144317
+ if (typeof value !== "string" || !value) return [];
144318
+ try {
144319
+ const parsed = JSON.parse(value);
144320
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
144321
+ } catch {
144322
+ return [];
144323
+ }
144324
+ }
144325
+ function rowToMemory(row) {
144326
+ return {
144327
+ id: row.id,
144328
+ originProjectId: row.originProjectId,
144329
+ ownerId: row.ownerId,
144330
+ scope: row.scope,
144331
+ kind: row.kind,
144332
+ state: row.state,
144333
+ portability: row.portability,
144334
+ title: row.title,
144335
+ content: row.content,
144336
+ facts: arrayValue2(row.factsJson),
144337
+ tags: arrayValue2(row.tagsJson),
144338
+ ...optionalText2(row.applicability) ? { applicability: row.applicability } : {},
144339
+ origin: row.origin,
144340
+ createdAt: row.createdAt,
144341
+ updatedAt: row.updatedAt,
144342
+ ...optionalText2(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
144343
+ ...optionalText2(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
144344
+ ...optionalText2(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
144345
+ ...optionalText2(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
144346
+ ...optionalText2(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
144347
+ accessCount: Number(row.accessCount ?? 0),
144348
+ ...optionalText2(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
144349
+ };
144350
+ }
144351
+ function rowToEvidence(row) {
144352
+ return {
144353
+ id: row.id,
144354
+ memoryId: row.memoryId,
144355
+ kind: row.kind,
144356
+ referenceId: row.referenceId,
144357
+ relation: row.relation,
144358
+ ...optionalText2(row.locator) ? { locator: row.locator } : {},
144359
+ ...optionalText2(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
144360
+ createdAt: row.createdAt
144361
+ };
144362
+ }
144363
+ function rowToEvent(row) {
144364
+ return {
144365
+ id: row.id,
144366
+ memoryId: row.memoryId,
144367
+ kind: row.kind,
144368
+ ...optionalText2(row.fromState) ? { fromState: row.fromState } : {},
144369
+ ...optionalText2(row.toState) ? { toState: row.toState } : {},
144370
+ ...optionalText2(row.detail) ? { detail: row.detail } : {},
144371
+ createdAt: row.createdAt
144372
+ };
144373
+ }
144374
+ var LongTermMemoryStore;
144375
+ var init_long_term_store = __esm({
144376
+ "src/memory/long-term-store.ts"() {
144377
+ "use strict";
144378
+ init_esm_shims();
144379
+ init_sqlite_db();
144380
+ LongTermMemoryStore = class {
144381
+ db = null;
144382
+ async init(dataDir) {
144383
+ this.db = getDatabase(dataDir);
144384
+ }
144385
+ transaction(fn) {
144386
+ return this.db.transaction(fn)();
144387
+ }
144388
+ get(id) {
144389
+ const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
144390
+ return row ? rowToMemory(row) : void 0;
144391
+ }
144392
+ list(options = {}) {
144393
+ const limit2 = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
144394
+ const clauses = [];
144395
+ const params = [];
144396
+ if (options.ownerId) {
144397
+ clauses.push("ownerId = ?");
144398
+ params.push(options.ownerId);
144399
+ }
144400
+ if (options.originProjectId) {
144401
+ clauses.push("originProjectId = ?");
144402
+ params.push(options.originProjectId);
144403
+ }
144404
+ const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
144405
+ const rows = this.db.prepare(
144406
+ "SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
144407
+ ).all(...params, limit2);
144408
+ return rows.map(rowToMemory);
144409
+ }
144410
+ insert(memory) {
144411
+ this.db.prepare(`
144412
+ INSERT INTO long_term_memories (
144413
+ id, originProjectId, ownerId, scope, kind, state, portability,
144414
+ title, content, factsJson, tagsJson, applicability, origin,
144415
+ createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
144416
+ supersededBy, lastValidatedAt, accessCount, lastAccessedAt
144417
+ ) VALUES (
144418
+ @id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
144419
+ @title, @content, @factsJson, @tagsJson, @applicability, @origin,
144420
+ @createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
144421
+ @supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
144422
+ )
144423
+ `).run({
144424
+ ...memory,
144425
+ factsJson: JSON.stringify(memory.facts),
144426
+ tagsJson: JSON.stringify(memory.tags),
144427
+ applicability: memory.applicability ?? null,
144428
+ qualifiedAt: memory.qualifiedAt ?? null,
144429
+ approvedAt: memory.approvedAt ?? null,
144430
+ archivedAt: memory.archivedAt ?? null,
144431
+ supersededBy: memory.supersededBy ?? null,
144432
+ lastValidatedAt: memory.lastValidatedAt ?? null,
144433
+ lastAccessedAt: memory.lastAccessedAt ?? null
144434
+ });
144435
+ }
144436
+ update(memory) {
144437
+ this.db.prepare(`
144438
+ UPDATE long_term_memories SET
144439
+ scope = @scope,
144440
+ kind = @kind,
144441
+ state = @state,
144442
+ portability = @portability,
144443
+ title = @title,
144444
+ content = @content,
144445
+ factsJson = @factsJson,
144446
+ tagsJson = @tagsJson,
144447
+ applicability = @applicability,
144448
+ updatedAt = @updatedAt,
144449
+ qualifiedAt = @qualifiedAt,
144450
+ approvedAt = @approvedAt,
144451
+ archivedAt = @archivedAt,
144452
+ supersededBy = @supersededBy,
144453
+ lastValidatedAt = @lastValidatedAt,
144454
+ accessCount = @accessCount,
144455
+ lastAccessedAt = @lastAccessedAt
144456
+ WHERE id = @id
144457
+ `).run({
144458
+ ...memory,
144459
+ factsJson: JSON.stringify(memory.facts),
144460
+ tagsJson: JSON.stringify(memory.tags),
144461
+ applicability: memory.applicability ?? null,
144462
+ qualifiedAt: memory.qualifiedAt ?? null,
144463
+ approvedAt: memory.approvedAt ?? null,
144464
+ archivedAt: memory.archivedAt ?? null,
144465
+ supersededBy: memory.supersededBy ?? null,
144466
+ lastValidatedAt: memory.lastValidatedAt ?? null,
144467
+ lastAccessedAt: memory.lastAccessedAt ?? null
144468
+ });
144469
+ }
144470
+ insertEvidence(input) {
144471
+ const evidence = {
144472
+ id: randomUUID7(),
144473
+ ...input,
144474
+ createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
144475
+ };
144476
+ this.db.prepare(`
144477
+ INSERT INTO long_term_memory_evidence (
144478
+ id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
144479
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
144480
+ `).run(
144481
+ evidence.id,
144482
+ evidence.memoryId,
144483
+ evidence.kind,
144484
+ evidence.referenceId,
144485
+ evidence.relation,
144486
+ evidence.locator ?? null,
144487
+ evidence.capturedHash ?? null,
144488
+ evidence.createdAt
144489
+ );
144490
+ return evidence;
144491
+ }
144492
+ listEvidence(memoryId) {
144493
+ return this.db.prepare(`
144494
+ SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
144495
+ `).all(memoryId).map(rowToEvidence);
144496
+ }
144497
+ recordEvent(input) {
144498
+ const event = {
144499
+ id: randomUUID7(),
144500
+ ...input,
144501
+ createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
144502
+ };
144503
+ this.db.prepare(`
144504
+ INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
144505
+ VALUES (?, ?, ?, ?, ?, ?, ?)
144506
+ `).run(
144507
+ event.id,
144508
+ event.memoryId,
144509
+ event.kind,
144510
+ event.fromState ?? null,
144511
+ event.toState ?? null,
144512
+ event.detail ?? null,
144513
+ event.createdAt
144514
+ );
144515
+ return event;
144516
+ }
144517
+ listEvents(memoryId) {
144518
+ return this.db.prepare(`
144519
+ SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
144520
+ `).all(memoryId).map(rowToEvent);
144521
+ }
144522
+ markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
144523
+ const unique2 = [...new Set(ids.filter(Boolean))];
144524
+ if (unique2.length === 0) return;
144525
+ const statement = this.db.prepare(`
144526
+ UPDATE long_term_memories
144527
+ SET accessCount = accessCount + 1, lastAccessedAt = ?
144528
+ WHERE id = ?
144529
+ `);
144530
+ this.db.transaction(() => {
144531
+ for (const id of unique2) statement.run(at, id);
144532
+ })();
144533
+ }
144534
+ };
144535
+ }
144536
+ });
144537
+
144538
+ // src/memory/long-term-types.ts
144539
+ var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
144540
+ var init_long_term_types = __esm({
144541
+ "src/memory/long-term-types.ts"() {
144542
+ "use strict";
144543
+ init_esm_shims();
144544
+ LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
144545
+ LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
144546
+ LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
144547
+ LONG_TERM_MEMORY_EVIDENCE_KINDS = [
144548
+ "observation",
144549
+ "claim",
144550
+ "workflow",
144551
+ "session",
144552
+ "checkpoint",
144553
+ "git",
144554
+ "code",
144555
+ "test",
144556
+ "run",
144557
+ "document",
144558
+ "manual",
144559
+ "user"
144560
+ ];
144561
+ LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
144562
+ "supports",
144563
+ "verifies",
144564
+ "derives",
144565
+ "user-confirmed"
144566
+ ];
144567
+ }
144568
+ });
144569
+
144570
+ // src/memory/long-term.ts
144571
+ var long_term_exports = {};
144572
+ __export(long_term_exports, {
144573
+ approveLongTermMemory: () => approveLongTermMemory,
144574
+ archiveLongTermMemory: () => archiveLongTermMemory,
144575
+ canReadLongTermMemory: () => canReadLongTermMemory,
144576
+ createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
144577
+ createManualLongTermMemory: () => createManualLongTermMemory,
144578
+ getLongTermMemoryDetail: () => getLongTermMemoryDetail,
144579
+ isEligibleLongTermMemory: () => isEligibleLongTermMemory,
144580
+ listLongTermMemories: () => listLongTermMemories,
144581
+ maintainLongTermMemories: () => maintainLongTermMemories,
144582
+ maybeAutoQualifyLongTermMemory: () => maybeAutoQualifyLongTermMemory,
144583
+ promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
144584
+ qualifyLongTermMemory: () => qualifyLongTermMemory,
144585
+ renderLongTermMemorySummary: () => renderLongTermMemorySummary,
144586
+ selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
144587
+ supersedeLongTermMemory: () => supersedeLongTermMemory
144588
+ });
144589
+ import { randomUUID as randomUUID8 } from "crypto";
144590
+ function isOneOf(value, values) {
144591
+ return typeof value === "string" && values.includes(value);
144592
+ }
144593
+ function compactText(value, field, max = MAX_TEXT_LENGTH) {
144594
+ if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
144595
+ const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
144596
+ if (!safe) throw new Error("Long-term memory " + field + " is required.");
144597
+ if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
144598
+ return safe;
144599
+ }
144600
+ function compactList(values, field) {
144601
+ if (!values) return [];
144602
+ if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
144603
+ return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
144604
+ }
144605
+ function normalizeEvidence(input) {
144606
+ if (!Array.isArray(input) || input.length === 0) {
144607
+ throw new Error("A long-term memory requires at least one source evidence reference.");
144608
+ }
144609
+ return input.map((evidence) => {
144610
+ if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
144611
+ throw new Error("Long-term memory evidence kind is invalid.");
144612
+ }
144613
+ if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
144614
+ throw new Error("Long-term memory evidence relation is invalid.");
144615
+ }
144616
+ return {
144617
+ kind: evidence.kind,
144618
+ referenceId: compactText(evidence.referenceId, "evidence reference", 1e3),
144619
+ relation: evidence.relation,
144620
+ ...evidence.locator ? { locator: compactText(evidence.locator, "evidence locator", 1e3) } : {},
144621
+ ...evidence.capturedHash ? { capturedHash: compactText(evidence.capturedHash, "evidence hash", 1e3) } : {}
144622
+ };
144623
+ });
144624
+ }
144625
+ function assertScope(scope) {
144626
+ if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
144627
+ }
144628
+ function assertKind(kind) {
144629
+ if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
144630
+ }
144631
+ function assertPortability(value) {
144632
+ if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
144633
+ throw new Error("Long-term memory portability is invalid.");
144634
+ }
144635
+ }
144636
+ function assertPortableEvidence(scope, portability, evidence) {
144637
+ if (portability !== "portable") return;
144638
+ if (scope !== "user") {
144639
+ throw new Error("Only user-scoped long-term memories may be portable across projects.");
144640
+ }
144641
+ if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
144642
+ 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.");
144643
+ }
144644
+ }
144645
+ function requireTeamReader(scope, reader) {
144646
+ if (scope === "team" && reader?.isTeamMember !== true) {
144647
+ throw new Error("Team long-term memory requires an explicit active team identity for this project.");
144648
+ }
144649
+ }
144650
+ function matchesScope(memory, reader) {
144651
+ if (memory.scope === "project") return memory.originProjectId === reader.projectId;
144652
+ if (memory.scope === "team") {
144653
+ return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
144654
+ }
144655
+ if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
144656
+ return memory.portability === "portable" || memory.originProjectId === reader.projectId;
144657
+ }
144658
+ function canReadLongTermMemory(memory, reader) {
144659
+ return matchesScope(memory, reader);
144660
+ }
144661
+ function isEligibleLongTermMemory(memory) {
144662
+ return memory.state === "qualified" || memory.state === "approved";
144663
+ }
144664
+ function taskTerms(task) {
144665
+ const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
144666
+ return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
144667
+ }
144668
+ function kindFitScore(kind, task) {
144669
+ const normalized = task.toLocaleLowerCase("en-US");
144670
+ const hints = {
144671
+ episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
144672
+ semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
144673
+ procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
144674
+ };
144675
+ return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
144676
+ }
144677
+ function validationFreshnessScore(memory) {
144678
+ const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
144679
+ if (!validatedAt) return 0;
144680
+ const time = Date.parse(validatedAt);
144681
+ if (!Number.isFinite(time)) return 0;
144682
+ const ageDays = Math.max(0, Date.now() - time) / (24 * 60 * 60 * 1e3);
144683
+ if (ageDays <= 30) return 0.35;
144684
+ if (ageDays <= 90) return 0.15;
144685
+ return 0;
144686
+ }
144687
+ function scoreForTask(memory, task) {
144688
+ const terms = taskTerms(task);
144689
+ if (terms.length === 0) return void 0;
144690
+ const title = memory.title.toLocaleLowerCase("en-US");
144691
+ const content = memory.content.toLocaleLowerCase("en-US");
144692
+ const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
144693
+ const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
144694
+ const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
144695
+ let score = memory.state === "approved" ? 0.5 : 0;
144696
+ const matched = [];
144697
+ for (const term of terms) {
144698
+ let termScore = 0;
144699
+ if (title.includes(term)) termScore += 5;
144700
+ if (tags.includes(term)) termScore += 4;
144701
+ if (applicability.includes(term)) termScore += 3;
144702
+ if (facts.includes(term)) termScore += 2;
144703
+ if (content.includes(term)) termScore += 1;
144704
+ if (termScore > 0) {
144705
+ score += termScore;
144706
+ matched.push(term);
144707
+ }
144708
+ }
144709
+ if (matched.length === 0) return void 0;
144710
+ score += kindFitScore(memory.kind, task);
144711
+ score += validationFreshnessScore(memory);
144712
+ const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
144713
+ const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
144714
+ return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
144715
+ }
144716
+ function semanticText(memory) {
144717
+ return sanitizeCredentials([
144718
+ memory.title,
144719
+ memory.applicability,
144720
+ memory.tags.join(" "),
144721
+ memory.facts.join(" "),
144722
+ memory.content
144723
+ ].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
144724
+ }
144725
+ function cosineSimilarity(left, right) {
144726
+ if (left.length === 0 || left.length !== right.length) return 0;
144727
+ let dot = 0;
144728
+ let leftMagnitude = 0;
144729
+ let rightMagnitude = 0;
144730
+ for (let index = 0; index < left.length; index += 1) {
144731
+ const a = left[index];
144732
+ const b = right[index];
144733
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return 0;
144734
+ dot += a * b;
144735
+ leftMagnitude += a * a;
144736
+ rightMagnitude += b * b;
144737
+ }
144738
+ if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
144739
+ return dot / Math.sqrt(leftMagnitude * rightMagnitude);
144740
+ }
144741
+ function semanticSelection(memory, similarity, task) {
144742
+ if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
144743
+ const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
144744
+ const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
144745
+ return {
144746
+ score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
144747
+ reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
144748
+ };
144749
+ }
144750
+ async function resolveSemanticProvider(explicit) {
144751
+ if (explicit !== void 0) return explicit;
144752
+ try {
144753
+ const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
144754
+ return await withTimeout(
144755
+ getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
144756
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS,
144757
+ "Long-term semantic provider initialization"
144758
+ );
144759
+ } catch {
144760
+ return null;
144761
+ }
144762
+ }
144763
+ function createStore(dataDir) {
144764
+ const store = new LongTermMemoryStore();
144765
+ return store.init(dataDir).then(() => store);
144766
+ }
144767
+ async function createManualLongTermMemory(input) {
144768
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
144769
+ if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
144770
+ assertScope(input.scope);
144771
+ const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID8(), relation: "supports" }];
144772
+ return createLongTermMemoryWithDataDir({
144773
+ dataDir: input.dataDir,
144774
+ originProjectId: input.projectId,
144775
+ ownerId: owner.id,
144776
+ scope: input.scope,
144777
+ kind: input.kind,
144778
+ portability: input.portability,
144779
+ title: input.title,
144780
+ content: input.content,
144781
+ facts: input.facts,
144782
+ tags: input.tags,
144783
+ applicability: input.applicability,
144784
+ origin: "manual",
144785
+ evidence,
144786
+ reader: input.reader
144787
+ });
144788
+ }
144789
+ async function promoteObservationToLongTermMemory(input) {
144790
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
144791
+ if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
144792
+ return createLongTermMemoryWithDataDir({
144793
+ dataDir: input.dataDir,
144794
+ originProjectId: input.observation.projectId,
144795
+ ownerId: owner.id,
144796
+ scope: input.scope,
144797
+ kind: input.kind,
144798
+ portability: "project-bound",
144799
+ title: input.observation.title,
144800
+ content: input.observation.narrative,
144801
+ facts: input.observation.facts,
144802
+ tags: input.tags ?? input.observation.concepts,
144803
+ applicability: input.applicability,
144804
+ origin: input.observation.source === "git" ? "git" : "agent",
144805
+ evidence: [{
144806
+ kind: "observation",
144807
+ referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
144808
+ relation: "derives"
144809
+ }],
144810
+ reader: input.reader
144811
+ });
144812
+ }
144813
+ async function createLongTermMemoryCandidate(input) {
144814
+ return createLongTermMemoryWithDataDir(input);
144815
+ }
144816
+ async function createLongTermMemoryWithDataDir(input) {
144817
+ assertScope(input.scope);
144818
+ assertKind(input.kind);
144819
+ const portability = input.portability ?? "project-bound";
144820
+ assertPortability(portability);
144821
+ requireTeamReader(input.scope, input.reader);
144822
+ const evidenceInputs = normalizeEvidence(input.evidence);
144823
+ assertPortableEvidence(input.scope, portability, evidenceInputs);
144824
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
144825
+ const memory = {
144826
+ id: randomUUID8(),
144827
+ originProjectId: compactText(input.originProjectId, "origin project id", 1e3),
144828
+ ownerId: compactText(input.ownerId, "owner id", 160),
144829
+ scope: input.scope,
144830
+ kind: input.kind,
144831
+ state: "candidate",
144832
+ portability,
144833
+ title: compactText(input.title, "title", 500),
144834
+ content: compactText(input.content, "content"),
144835
+ facts: compactList(input.facts, "facts"),
144836
+ tags: compactList(input.tags, "tags"),
144837
+ ...input.applicability ? { applicability: compactText(input.applicability, "applicability", 1e3) } : {},
144838
+ origin: input.origin ?? "manual",
144839
+ createdAt: now3,
144840
+ updatedAt: now3,
144841
+ accessCount: 0
144842
+ };
144843
+ const store = await createStore(input.dataDir);
144844
+ const evidence = store.transaction(() => {
144845
+ store.insert(memory);
144846
+ const written = evidenceInputs.map((item) => store.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
144847
+ store.recordEvent({
144848
+ memoryId: memory.id,
144849
+ kind: "created",
144850
+ toState: "candidate",
144851
+ detail: "Created as a source-backed long-term memory candidate.",
144852
+ createdAt: now3
144853
+ });
144854
+ return written;
144855
+ });
144856
+ return { memory, evidence };
144857
+ }
144858
+ function transitionDetail(value) {
144859
+ return compactText(value, "transition reason", 1e3);
144860
+ }
144861
+ async function transitionMemory(input) {
144862
+ const store = await createStore(input.dataDir);
144863
+ return store.transaction(() => {
144864
+ const current = store.get(input.id);
144865
+ if (!current) throw new Error("Long-term memory was not found.");
144866
+ if (current.state !== input.expected) {
144867
+ throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
144868
+ }
144869
+ const evidence = store.listEvidence(current.id);
144870
+ if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
144871
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
144872
+ const next = {
144873
+ ...current,
144874
+ state: input.next,
144875
+ updatedAt: now3,
144876
+ lastValidatedAt: now3,
144877
+ ...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
144878
+ };
144879
+ store.update(next);
144880
+ store.recordEvent({
144881
+ memoryId: next.id,
144882
+ kind: input.event,
144883
+ fromState: current.state,
144884
+ toState: next.state,
144885
+ detail: transitionDetail(input.reason),
144886
+ createdAt: now3
144887
+ });
144888
+ return next;
144889
+ });
144890
+ }
144891
+ async function qualifyLongTermMemory(input) {
144892
+ return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
144893
+ }
144894
+ async function approveLongTermMemory(input) {
144895
+ return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
144896
+ }
144897
+ async function archiveLongTermMemory(input) {
144898
+ const store = await createStore(input.dataDir);
144899
+ return store.transaction(() => {
144900
+ const current = store.get(input.id);
144901
+ if (!current) throw new Error("Long-term memory was not found.");
144902
+ if (current.state === "archived" || current.state === "superseded") {
144903
+ throw new Error("Long-term memory is already inactive.");
144904
+ }
144905
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
144906
+ const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
144907
+ store.update(next);
144908
+ store.recordEvent({
144909
+ memoryId: next.id,
144910
+ kind: "archived",
144911
+ fromState: current.state,
144912
+ toState: "archived",
144913
+ detail: transitionDetail(input.reason),
144914
+ createdAt: now3
144915
+ });
144916
+ return next;
144917
+ });
144918
+ }
144919
+ async function supersedeLongTermMemory(input) {
144920
+ const store = await createStore(input.dataDir);
144921
+ return store.transaction(() => {
144922
+ const current = store.get(input.id);
144923
+ if (!current) throw new Error("Long-term memory was not found.");
144924
+ if (current.state === "archived" || current.state === "superseded") {
144925
+ throw new Error("Long-term memory is already inactive.");
144926
+ }
144927
+ if (current.id === input.supersededBy) {
144928
+ throw new Error("Long-term memory cannot supersede itself.");
144929
+ }
144930
+ const replacement = store.get(input.supersededBy);
144931
+ if (!replacement || !isEligibleLongTermMemory(replacement)) {
144932
+ throw new Error("Replacement long-term memory must be qualified or approved.");
144933
+ }
144934
+ if (replacement.scope !== current.scope) {
144935
+ throw new Error("Replacement long-term memory must use the same scope.");
144936
+ }
144937
+ if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
144938
+ throw new Error("User long-term memory may only be superseded by the same local owner.");
144939
+ }
144940
+ if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
144941
+ throw new Error("Project and team long-term memory may only be superseded within the same project.");
144942
+ }
144943
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
144944
+ const next = {
144945
+ ...current,
144946
+ state: "superseded",
144947
+ supersededBy: replacement.id,
144948
+ updatedAt: now3
144949
+ };
144950
+ store.update(next);
144951
+ store.recordEvent({
144952
+ memoryId: next.id,
144953
+ kind: "superseded",
144954
+ fromState: current.state,
144955
+ toState: "superseded",
144956
+ detail: transitionDetail(input.reason),
144957
+ createdAt: now3
144958
+ });
144959
+ return next;
144960
+ });
144961
+ }
144962
+ async function maybeAutoQualifyLongTermMemory(input) {
144963
+ if (input.source !== "manual" && input.sourceDetail !== "explicit") return null;
144964
+ try {
144965
+ return await qualifyLongTermMemory({
144966
+ dataDir: input.dataDir,
144967
+ id: input.id,
144968
+ reason: "auto-qualified: explicit request carries its own source evidence"
144969
+ });
144970
+ } catch {
144971
+ return null;
144972
+ }
144973
+ }
144974
+ async function maintainLongTermMemories(input) {
144975
+ const now3 = input.now ?? /* @__PURE__ */ new Date();
144976
+ const decayDays = input.decayDays ?? 30;
144977
+ const cutoff = now3.getTime() - decayDays * 24 * 60 * 60 * 1e3;
144978
+ const result = { archived: 0, superseded: 0 };
144979
+ const store = await createStore(input.dataDir);
144980
+ const all = store.list({ originProjectId: input.projectId });
144981
+ for (const memory of all) {
144982
+ if (memory.state !== "qualified" && memory.state !== "approved") continue;
144983
+ const lastActivity = Date.parse(memory.lastAccessedAt ?? memory.updatedAt);
144984
+ if (!Number.isFinite(lastActivity) || lastActivity >= cutoff) continue;
144985
+ try {
144986
+ await archiveLongTermMemory({
144987
+ dataDir: input.dataDir,
144988
+ id: memory.id,
144989
+ reason: "auto-archive: no activity since " + new Date(lastActivity).toISOString().slice(0, 10)
144990
+ });
144991
+ result.archived += 1;
144992
+ } catch {
144993
+ }
144994
+ }
144995
+ const groups = /* @__PURE__ */ new Map();
144996
+ for (const memory of store.list({ originProjectId: input.projectId })) {
144997
+ if (memory.state !== "qualified" && memory.state !== "approved") continue;
144998
+ const key = memory.scope + "\0" + memory.kind + "\0" + memory.title.trim().toLowerCase();
144999
+ const group = groups.get(key) ?? [];
145000
+ group.push(memory);
145001
+ groups.set(key, group);
145002
+ }
145003
+ for (const group of groups.values()) {
145004
+ if (group.length < 2) continue;
145005
+ group.sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt));
145006
+ const newest = group[0];
145007
+ for (const older of group.slice(1)) {
145008
+ try {
145009
+ await supersedeLongTermMemory({
145010
+ dataDir: input.dataDir,
145011
+ id: older.id,
145012
+ supersededBy: newest.id,
145013
+ reason: "auto-supersede: a newer record shares the same title"
145014
+ });
145015
+ result.superseded += 1;
145016
+ } catch {
145017
+ }
145018
+ }
145019
+ }
145020
+ return result;
145021
+ }
145022
+ async function listLongTermMemories(input) {
145023
+ const store = await createStore(input.dataDir);
145024
+ 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) }));
145025
+ }
145026
+ async function getLongTermMemoryDetail(input) {
145027
+ const store = await createStore(input.dataDir);
145028
+ const memory = store.get(input.id);
145029
+ if (!memory || !canReadLongTermMemory(memory, input.reader)) {
145030
+ throw new Error("Long-term memory was not found in the active scope.");
145031
+ }
145032
+ store.markAccess([memory.id]);
145033
+ return { memory, evidence: store.listEvidence(memory.id), events: store.listEvents(memory.id) };
145034
+ }
145035
+ async function selectLongTermMemoriesForTask(input) {
145036
+ const task = input.task?.trim();
145037
+ if (!task) return [];
145038
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
145039
+ const reader = {
145040
+ projectId: input.projectId,
145041
+ ...owner ? { ownerId: owner.id } : {},
145042
+ ...input.agentId ? { agentId: input.agentId } : {},
145043
+ ...input.isTeamMember ? { isTeamMember: true } : {}
145044
+ };
145045
+ const store = await createStore(input.dataDir);
145046
+ const eligible = store.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
145047
+ let selected = eligible.map((memory) => {
145048
+ const ranked = scoreForTask(memory, task);
145049
+ return ranked ? {
145050
+ memory,
145051
+ evidence: store.listEvidence(memory.id),
145052
+ score: ranked.score,
145053
+ reason: ranked.reason
145054
+ } : void 0;
145055
+ }).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));
145056
+ if (selected.length === 0 && eligible.length > 0) {
145057
+ const provider2 = await resolveSemanticProvider(input.embeddingProvider);
145058
+ const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
145059
+ if (provider2 && candidates.length > 0) {
145060
+ try {
145061
+ const vectors = await withTimeout(
145062
+ provider2.embedBatch([task, ...candidates.map(semanticText)], {
145063
+ timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
145064
+ retry: false
145065
+ }),
145066
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS,
145067
+ "Long-term semantic retrieval"
145068
+ );
145069
+ const query = vectors[0];
145070
+ if (Array.isArray(query)) {
145071
+ selected = candidates.map((memory, index) => {
145072
+ const vector = vectors[index + 1];
145073
+ const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
145074
+ return ranked ? {
145075
+ memory,
145076
+ evidence: store.listEvidence(memory.id),
145077
+ score: ranked.score,
145078
+ reason: ranked.reason
145079
+ } : void 0;
145080
+ }).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));
145081
+ }
145082
+ } catch {
145083
+ }
145084
+ }
145085
+ }
145086
+ selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
145087
+ store.markAccess(selected.map((item) => item.memory.id));
145088
+ return selected;
145089
+ }
145090
+ function renderLongTermMemorySummary(memory, maxTokens = 22) {
145091
+ const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
145092
+ const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
145093
+ return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
145094
+ }
145095
+ var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
145096
+ var init_long_term = __esm({
145097
+ "src/memory/long-term.ts"() {
145098
+ "use strict";
145099
+ init_esm_shims();
145100
+ init_token_budget();
145101
+ init_timeout();
145102
+ init_secret_filter();
145103
+ init_owner();
145104
+ init_long_term_store();
145105
+ init_long_term_types();
145106
+ MAX_TEXT_LENGTH = 12e3;
145107
+ MAX_LIST_ITEMS = 40;
145108
+ MAX_SEMANTIC_CANDIDATES = 48;
145109
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
145110
+ MIN_SEMANTIC_SIMILARITY = 0.58;
145111
+ PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
145112
+ STOP_TERMS = /* @__PURE__ */ new Set([
145113
+ "the",
145114
+ "and",
145115
+ "for",
145116
+ "with",
145117
+ "from",
145118
+ "that",
145119
+ "this",
145120
+ "into",
145121
+ "when",
145122
+ "what",
145123
+ "\u5982\u4F55",
145124
+ "\u4EC0\u4E48",
145125
+ "\u8FD9\u4E2A",
145126
+ "\u90A3\u4E2A",
145127
+ "\u6211\u4EEC",
145128
+ "\u9879\u76EE",
145129
+ "\u8FDB\u884C",
145130
+ "\u9700\u8981",
145131
+ "\u7EE7\u7EED",
145132
+ "\u5904\u7406"
145133
+ ]);
145134
+ }
145135
+ });
145136
+
144227
145137
  // src/codegraph/ids.ts
144228
145138
  import { createHash as createHash5 } from "crypto";
144229
145139
  function digest(input) {
@@ -144262,7 +145172,7 @@ var store_exports = {};
144262
145172
  __export(store_exports, {
144263
145173
  CodeGraphStore: () => CodeGraphStore
144264
145174
  });
144265
- import { randomUUID as randomUUID6 } from "crypto";
145175
+ import { randomUUID as randomUUID9 } from "crypto";
144266
145176
  import { posix as pathPosix } from "path";
144267
145177
  function rowToFile(row) {
144268
145178
  return {
@@ -144979,7 +145889,7 @@ var init_store = __esm({
144979
145889
  * so an interrupted scan cannot be advertised as complete.
144980
145890
  */
144981
145891
  recordCodeStateSnapshot(input) {
144982
- const id = randomUUID6();
145892
+ const id = randomUUID9();
144983
145893
  let snapshot;
144984
145894
  const tx = this.db.transaction(() => {
144985
145895
  const previous = this.db.prepare(
@@ -145757,8 +146667,8 @@ var claim_store_exports = {};
145757
146667
  __export(claim_store_exports, {
145758
146668
  ClaimStore: () => ClaimStore
145759
146669
  });
145760
- import { createHash as createHash8, randomUUID as randomUUID7 } from "crypto";
145761
- function optionalText2(value) {
146670
+ import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
146671
+ function optionalText3(value) {
145762
146672
  return typeof value === "string" && value ? value : void 0;
145763
146673
  }
145764
146674
  function rowToClaim(row) {
@@ -145774,38 +146684,38 @@ function rowToClaim(row) {
145774
146684
  status: row.status,
145775
146685
  confidence: Number(row.confidence),
145776
146686
  observedAt: row.observedAt,
145777
- ...optionalText2(row.validFrom) ? { validFrom: row.validFrom } : {},
145778
- ...optionalText2(row.validTo) ? { validTo: row.validTo } : {},
145779
- ...optionalText2(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
146687
+ ...optionalText3(row.validFrom) ? { validFrom: row.validFrom } : {},
146688
+ ...optionalText3(row.validTo) ? { validTo: row.validTo } : {},
146689
+ ...optionalText3(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
145780
146690
  reviewState: row.reviewState,
145781
146691
  origin: row.origin,
145782
146692
  createdAt: row.createdAt,
145783
146693
  updatedAt: row.updatedAt
145784
146694
  };
145785
146695
  }
145786
- function rowToEvidence(row) {
146696
+ function rowToEvidence2(row) {
145787
146697
  return {
145788
146698
  id: row.id,
145789
146699
  claimId: row.claimId,
145790
146700
  evidenceKind: row.evidenceKind,
145791
146701
  evidenceId: row.evidenceId,
145792
146702
  relation: row.relation,
145793
- ...optionalText2(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
145794
- ...optionalText2(row.locator) ? { locator: row.locator } : {},
145795
- ...optionalText2(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
146703
+ ...optionalText3(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
146704
+ ...optionalText3(row.locator) ? { locator: row.locator } : {},
146705
+ ...optionalText3(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
145796
146706
  createdAt: row.createdAt
145797
146707
  };
145798
146708
  }
145799
- function rowToEvent(row) {
146709
+ function rowToEvent2(row) {
145800
146710
  return {
145801
146711
  id: row.id,
145802
146712
  projectId: row.projectId,
145803
146713
  claimId: row.claimId,
145804
146714
  kind: row.kind,
145805
- ...optionalText2(row.fromStatus) ? { fromStatus: row.fromStatus } : {},
145806
- ...optionalText2(row.toStatus) ? { toStatus: row.toStatus } : {},
145807
- ...optionalText2(row.relatedClaimId) ? { relatedClaimId: row.relatedClaimId } : {},
145808
- ...optionalText2(row.detail) ? { detail: row.detail } : {},
146715
+ ...optionalText3(row.fromStatus) ? { fromStatus: row.fromStatus } : {},
146716
+ ...optionalText3(row.toStatus) ? { toStatus: row.toStatus } : {},
146717
+ ...optionalText3(row.relatedClaimId) ? { relatedClaimId: row.relatedClaimId } : {},
146718
+ ...optionalText3(row.detail) ? { detail: row.detail } : {},
145809
146719
  createdAt: row.createdAt
145810
146720
  };
145811
146721
  }
@@ -145941,7 +146851,7 @@ var init_claim_store = __esm({
145941
146851
  locator, capturedHash, evidenceKey, createdAt
145942
146852
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
145943
146853
  `).run(
145944
- randomUUID7(),
146854
+ randomUUID10(),
145945
146855
  input.claimId,
145946
146856
  input.evidenceKind,
145947
146857
  input.evidenceId,
@@ -145959,11 +146869,11 @@ var init_claim_store = __esm({
145959
146869
  SELECT * FROM knowledge_claim_evidence
145960
146870
  WHERE claimId = ?
145961
146871
  ORDER BY createdAt ASC, id
145962
- `).all(claimId).map(rowToEvidence);
146872
+ `).all(claimId).map(rowToEvidence2);
145963
146873
  }
145964
146874
  recordEvent(input) {
145965
146875
  const event = {
145966
- id: randomUUID7(),
146876
+ id: randomUUID10(),
145967
146877
  ...input,
145968
146878
  ...input.detail ? { detail: sanitizeCredentials(input.detail) } : {},
145969
146879
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -145991,7 +146901,7 @@ var init_claim_store = __esm({
145991
146901
  SELECT * FROM knowledge_claim_events
145992
146902
  WHERE claimId = ?
145993
146903
  ORDER BY createdAt ASC, id
145994
- `).all(claimId).map(rowToEvent);
146904
+ `).all(claimId).map(rowToEvent2);
145995
146905
  }
145996
146906
  };
145997
146907
  }
@@ -146008,8 +146918,8 @@ __export(claims_exports, {
146008
146918
  supersedeClaim: () => supersedeClaim,
146009
146919
  writeClaim: () => writeClaim
146010
146920
  });
146011
- import { createHash as createHash9, randomUUID as randomUUID8 } from "crypto";
146012
- function compactText(value, field) {
146921
+ import { createHash as createHash9, randomUUID as randomUUID11 } from "crypto";
146922
+ function compactText2(value, field) {
146013
146923
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
146014
146924
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
146015
146925
  if (!sanitized) throw new Error("Claim " + field + " is required");
@@ -146017,7 +146927,7 @@ function compactText(value, field) {
146017
146927
  return sanitized;
146018
146928
  }
146019
146929
  function normalizedTerm(value) {
146020
- return compactText(value, "identity").toLocaleLowerCase("en-US");
146930
+ return compactText2(value, "identity").toLocaleLowerCase("en-US");
146021
146931
  }
146022
146932
  function hashIdentity(parts) {
146023
146933
  return createHash9("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
@@ -146046,7 +146956,7 @@ function validateEvidence(input) {
146046
146956
  if (!evidence || typeof evidence !== "object") {
146047
146957
  throw new Error("Claim evidence must be an object");
146048
146958
  }
146049
- const evidenceId = compactText(evidence.evidenceId, "evidence id");
146959
+ const evidenceId = compactText2(evidence.evidenceId, "evidence id");
146050
146960
  if (evidenceId.length > MAX_EVIDENCE_ID_LENGTH) {
146051
146961
  throw new Error("Claim evidence id is too long");
146052
146962
  }
@@ -146060,9 +146970,9 @@ function validateEvidence(input) {
146060
146970
  evidenceKind: evidence.evidenceKind,
146061
146971
  evidenceId,
146062
146972
  relation: evidence.relation,
146063
- ...evidence.snapshotId ? { snapshotId: compactText(evidence.snapshotId, "evidence snapshot id") } : {},
146064
- ...evidence.locator ? { locator: compactText(evidence.locator, "evidence locator") } : {},
146065
- ...evidence.capturedHash ? { capturedHash: compactText(evidence.capturedHash, "evidence hash") } : {}
146973
+ ...evidence.snapshotId ? { snapshotId: compactText2(evidence.snapshotId, "evidence snapshot id") } : {},
146974
+ ...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator") } : {},
146975
+ ...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash") } : {}
146066
146976
  };
146067
146977
  });
146068
146978
  }
@@ -146070,10 +146980,10 @@ function normalizeClaimInput(input) {
146070
146980
  if (!["project", "workspace", "team", "workflow", "task"].includes(input.scope)) {
146071
146981
  throw new Error("Claim scope is invalid");
146072
146982
  }
146073
- const projectId = compactText(input.projectId, "project id");
146074
- const subject = compactText(input.subject, "subject");
146075
- const predicate = compactText(input.predicate, "predicate");
146076
- const objectValue = compactText(input.objectValue, "object value");
146983
+ const projectId = compactText2(input.projectId, "project id");
146984
+ const subject = compactText2(input.subject, "subject");
146985
+ const predicate = compactText2(input.predicate, "predicate");
146986
+ const objectValue = compactText2(input.objectValue, "object value");
146077
146987
  const identity = buildClaimIdentity({ subject, predicate, objectValue, scope: input.scope });
146078
146988
  const origin = input.origin ?? "explicit";
146079
146989
  let reviewState = input.reviewState ?? "approved";
@@ -146094,7 +147004,7 @@ function normalizeClaimInput(input) {
146094
147004
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
146095
147005
  return {
146096
147006
  claim: {
146097
- id: randomUUID8(),
147007
+ id: randomUUID11(),
146098
147008
  projectId,
146099
147009
  subject,
146100
147010
  predicate,
@@ -146194,7 +147104,7 @@ function reviewClaim(store, input) {
146194
147104
  if (input.reviewState !== "approved" && input.reviewState !== "rejected") {
146195
147105
  throw new Error("A claim review must approve or reject the claim");
146196
147106
  }
146197
- const detail = compactText(input.detail, "review detail");
147107
+ const detail = compactText2(input.detail, "review detail");
146198
147108
  return store.transaction(() => {
146199
147109
  const claim = store.getClaim(input.claimId);
146200
147110
  if (!claim) throw new Error("Claim was not found");
@@ -146506,8 +147416,8 @@ var init_frontmatter = __esm({
146506
147416
 
146507
147417
  // src/knowledge/markdown.ts
146508
147418
  import { createHash as createHash10 } from "crypto";
146509
- import { promises as fs12 } from "fs";
146510
- import path19 from "path";
147419
+ import { promises as fs13 } from "fs";
147420
+ import path20 from "path";
146511
147421
  function hashContent(content) {
146512
147422
  return createHash10("sha256").update(content).digest("hex");
146513
147423
  }
@@ -146565,8 +147475,8 @@ function extractInternalMarkdownLinks(body) {
146565
147475
  return [...links].sort();
146566
147476
  }
146567
147477
  function resolvePageLink(sourceRelativePath, target) {
146568
- const sourceDir = path19.posix.dirname(sourceRelativePath.replace(/\\/g, "/"));
146569
- const normalized = path19.posix.normalize(path19.posix.join(sourceDir, target.replace(/\\/g, "/")));
147478
+ const sourceDir = path20.posix.dirname(sourceRelativePath.replace(/\\/g, "/"));
147479
+ const normalized = path20.posix.normalize(path20.posix.join(sourceDir, target.replace(/\\/g, "/")));
146570
147480
  if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../")) return void 0;
146571
147481
  return normalized;
146572
147482
  }
@@ -146588,13 +147498,13 @@ function renderKnowledgePage(frontmatter, body) {
146588
147498
  });
146589
147499
  }
146590
147500
  async function readKnowledgePage(absolutePath, workspaceRoot) {
146591
- const raw = await fs12.readFile(absolutePath, "utf8");
147501
+ const raw = await fs13.readFile(absolutePath, "utf8");
146592
147502
  try {
146593
147503
  const parsed = frontmatter_default(raw);
146594
147504
  const frontmatter = validateKnowledgePageFrontmatter(parsed.data);
146595
147505
  return {
146596
147506
  absolutePath,
146597
- relativePath: workspaceRoot ? path19.relative(workspaceRoot, absolutePath).split(path19.sep).join("/") : path19.basename(absolutePath),
147507
+ relativePath: workspaceRoot ? path20.relative(workspaceRoot, absolutePath).split(path20.sep).join("/") : path20.basename(absolutePath),
146598
147508
  frontmatter,
146599
147509
  body: parsed.content.trim(),
146600
147510
  contentHash: hashContent(raw),
@@ -146606,7 +147516,7 @@ async function readKnowledgePage(absolutePath, workspaceRoot) {
146606
147516
  }
146607
147517
  }
146608
147518
  async function writeKnowledgePage(absolutePath, content) {
146609
- await fs12.mkdir(path19.dirname(absolutePath), { recursive: true });
147519
+ await fs13.mkdir(path20.dirname(absolutePath), { recursive: true });
146610
147520
  await atomicWriteFile(absolutePath, content);
146611
147521
  return hashContent(content);
146612
147522
  }
@@ -146636,8 +147546,8 @@ __export(wiki_exports, {
146636
147546
  readKnowledgePage: () => readKnowledgePage
146637
147547
  });
146638
147548
  import { createHash as createHash11 } from "crypto";
146639
- import { promises as fs13 } from "fs";
146640
- import path20 from "path";
147549
+ import { promises as fs14 } from "fs";
147550
+ import path21 from "path";
146641
147551
  function hash2(value) {
146642
147552
  return createHash11("sha256").update(value).digest("hex");
146643
147553
  }
@@ -146769,7 +147679,7 @@ function candidatePage(workspace, title, claims, store) {
146769
147679
  }
146770
147680
  async function fileHashIfPresent(filePath) {
146771
147681
  try {
146772
- return pageContentHash(await fs13.readFile(filePath, "utf8"));
147682
+ return pageContentHash(await fs14.readFile(filePath, "utf8"));
146773
147683
  } catch (error) {
146774
147684
  const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
146775
147685
  if (code === "ENOENT") return void 0;
@@ -146793,8 +147703,8 @@ async function writeIndex(workspace, store) {
146793
147703
  lines.push("No pending proposals.");
146794
147704
  } else {
146795
147705
  for (const proposal of pending) {
146796
- const relative3 = path20.relative(paths.root, proposal.proposalPath).split(path20.sep).join("/");
146797
- lines.push("- [" + path20.basename(proposal.proposalPath, ".md") + "](" + relative3 + ")");
147706
+ const relative3 = path21.relative(paths.root, proposal.proposalPath).split(path21.sep).join("/");
147707
+ lines.push("- [" + path21.basename(proposal.proposalPath, ".md") + "](" + relative3 + ")");
146798
147708
  }
146799
147709
  }
146800
147710
  lines.push("");
@@ -146802,7 +147712,7 @@ async function writeIndex(workspace, store) {
146802
147712
  }
146803
147713
  async function appendLog(workspace, line) {
146804
147714
  const logPath = getKnowledgeWorkspacePaths(workspace).log;
146805
- await fs13.appendFile(logPath, "- " + now() + " " + line + "\n", "utf8");
147715
+ await fs14.appendFile(logPath, "- " + now() + " " + line + "\n", "utf8");
146806
147716
  }
146807
147717
  function toPageRecord(workspace, page, status, contentHash, manualContentHash) {
146808
147718
  return {
@@ -146887,9 +147797,9 @@ async function applyKnowledgeProposal(input) {
146887
147797
  }
146888
147798
  if (proposal.status !== "pending") throw new Error("Knowledge proposal is not pending review");
146889
147799
  const root = getKnowledgeWorkspacePaths(input.workspace).root;
146890
- const targetPath = path20.resolve(proposal.targetPath);
146891
- const proposalPath = path20.resolve(proposal.proposalPath);
146892
- if (!targetPath.startsWith(root + path20.sep) || !proposalPath.startsWith(root + path20.sep)) {
147800
+ const targetPath = path21.resolve(proposal.targetPath);
147801
+ const proposalPath = path21.resolve(proposal.proposalPath);
147802
+ if (!targetPath.startsWith(root + path21.sep) || !proposalPath.startsWith(root + path21.sep)) {
146893
147803
  throw new Error("Knowledge proposal path escapes its workspace");
146894
147804
  }
146895
147805
  await withFileLock(root, async () => {
@@ -146927,12 +147837,12 @@ async function markdownFiles(directory) {
146927
147837
  const files = [];
146928
147838
  let entries;
146929
147839
  try {
146930
- entries = await fs13.readdir(directory, { withFileTypes: true });
147840
+ entries = await fs14.readdir(directory, { withFileTypes: true });
146931
147841
  } catch {
146932
147842
  return files;
146933
147843
  }
146934
147844
  for (const entry of entries) {
146935
- const filePath = path20.join(directory, entry.name);
147845
+ const filePath = path21.join(directory, entry.name);
146936
147846
  if (entry.isDirectory()) {
146937
147847
  files.push(...await markdownFiles(filePath));
146938
147848
  } else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
@@ -146955,13 +147865,13 @@ async function lintKnowledgeWorkspace(input) {
146955
147865
  kind: "malformed-frontmatter",
146956
147866
  severity: "error",
146957
147867
  message: error instanceof Error ? error.message : "Malformed knowledge page.",
146958
- relativePath: path20.relative(paths.root, filePath).split(path20.sep).join("/")
147868
+ relativePath: path21.relative(paths.root, filePath).split(path21.sep).join("/")
146959
147869
  });
146960
147870
  }
146961
147871
  }
146962
147872
  const indexed = /* @__PURE__ */ new Set();
146963
147873
  try {
146964
- const index = await fs13.readFile(paths.index, "utf8");
147874
+ const index = await fs14.readFile(paths.index, "utf8");
146965
147875
  for (const link of extractInternalMarkdownLinks(index)) {
146966
147876
  const resolved = resolvePageLink("index.md", link);
146967
147877
  if (resolved) indexed.add(resolved);
@@ -147050,7 +147960,7 @@ async function lintKnowledgeWorkspace(input) {
147050
147960
  kind: "manual-edit-protected",
147051
147961
  severity: "warning",
147052
147962
  message: "A newer proposal was held because the target page has manual edits.",
147053
- relativePath: path20.relative(paths.root, proposal.targetPath).split(path20.sep).join("/")
147963
+ relativePath: path21.relative(paths.root, proposal.targetPath).split(path21.sep).join("/")
147054
147964
  });
147055
147965
  }
147056
147966
  }
@@ -147183,7 +148093,7 @@ var workflow_store_exports = {};
147183
148093
  __export(workflow_store_exports, {
147184
148094
  WorkflowStore: () => WorkflowStore
147185
148095
  });
147186
- import { randomUUID as randomUUID9 } from "crypto";
148096
+ import { randomUUID as randomUUID12 } from "crypto";
147187
148097
  function parseJson(value, fallback) {
147188
148098
  if (typeof value !== "string" || !value) return fallback;
147189
148099
  try {
@@ -147192,7 +148102,7 @@ function parseJson(value, fallback) {
147192
148102
  return fallback;
147193
148103
  }
147194
148104
  }
147195
- function optionalText3(value) {
148105
+ function optionalText4(value) {
147196
148106
  return typeof value === "string" && value.trim() ? value : void 0;
147197
148107
  }
147198
148108
  function rowToWorkflow(row) {
@@ -147229,14 +148139,14 @@ function rowToRun(row) {
147229
148139
  workflowId: row.workflowId,
147230
148140
  projectId: row.projectId,
147231
148141
  task: row.task,
147232
- ...optionalText3(row.startingSnapshotId) ? { startingSnapshotId: row.startingSnapshotId } : {},
148142
+ ...optionalText4(row.startingSnapshotId) ? { startingSnapshotId: row.startingSnapshotId } : {},
147233
148143
  selectedEvidence: parseJson(row.selectedEvidenceJson, []),
147234
148144
  phaseState: parseJson(row.phaseStateJson, {}),
147235
148145
  outcome: row.outcome,
147236
148146
  verificationVerdict: row.verificationVerdict,
147237
- ...optionalText3(row.failureReason) ? { failureReason: row.failureReason } : {},
148147
+ ...optionalText4(row.failureReason) ? { failureReason: row.failureReason } : {},
147238
148148
  startedAt: row.startedAt,
147239
- ...optionalText3(row.completedAt) ? { completedAt: row.completedAt } : {}
148149
+ ...optionalText4(row.completedAt) ? { completedAt: row.completedAt } : {}
147240
148150
  };
147241
148151
  }
147242
148152
  var WorkflowStore;
@@ -147283,7 +148193,7 @@ var init_workflow_store = __esm({
147283
148193
  }
147284
148194
  recordRun(input) {
147285
148195
  const run = {
147286
- id: randomUUID9(),
148196
+ id: randomUUID12(),
147287
148197
  workflowId: input.workflowId,
147288
148198
  projectId: input.projectId,
147289
148199
  task: input.task.trim(),
@@ -147361,8 +148271,8 @@ var init_outcome_types = __esm({
147361
148271
  });
147362
148272
 
147363
148273
  // src/knowledge/outcome-store.ts
147364
- import { randomUUID as randomUUID10 } from "crypto";
147365
- function optionalText4(value) {
148274
+ import { randomUUID as randomUUID13 } from "crypto";
148275
+ function optionalText5(value) {
147366
148276
  return typeof value === "string" && value.trim() ? value : void 0;
147367
148277
  }
147368
148278
  function text(value, field, max = 1e3) {
@@ -147385,8 +148295,8 @@ function rowToSignal(row) {
147385
148295
  candidateId: row.candidateId,
147386
148296
  kind: row.kind,
147387
148297
  sourceRef: row.sourceRef,
147388
- ...optionalText4(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
147389
- ...optionalText4(row.detail) ? { detail: row.detail } : {},
148298
+ ...optionalText5(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
148299
+ ...optionalText5(row.detail) ? { detail: row.detail } : {},
147390
148300
  observedAt: row.observedAt
147391
148301
  };
147392
148302
  }
@@ -147405,7 +148315,7 @@ var init_outcome_store = __esm({
147405
148315
  }
147406
148316
  record(input) {
147407
148317
  const signal = {
147408
- id: randomUUID10(),
148318
+ id: randomUUID13(),
147409
148319
  projectId: text(input.projectId, "project id"),
147410
148320
  candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
147411
148321
  candidateId: text(input.candidateId, "candidate id"),
@@ -147832,8 +148742,8 @@ __export(workflows_exports, {
147832
148742
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
147833
148743
  });
147834
148744
  import { createHash as createHash12 } from "crypto";
147835
- import { promises as fs14 } from "fs";
147836
- import path21 from "path";
148745
+ import { promises as fs15 } from "fs";
148746
+ import path22 from "path";
147837
148747
  function hash3(value) {
147838
148748
  return createHash12("sha256").update(value).digest("hex");
147839
148749
  }
@@ -147854,7 +148764,7 @@ function requiredText(data, key) {
147854
148764
  }
147855
148765
  return value.trim();
147856
148766
  }
147857
- function optionalText5(data, key) {
148767
+ function optionalText6(data, key) {
147858
148768
  const value = data[key];
147859
148769
  if (value === void 0 || value === null || value === "") return void 0;
147860
148770
  if (typeof value !== "string") throw new Error("workflow frontmatter field " + key + " must be text");
@@ -147897,11 +148807,11 @@ function phaseFromValue(value, index) {
147897
148807
  }
147898
148808
  const data = value;
147899
148809
  const title = requiredText(data, "title");
147900
- const phaseId = optionalText5(data, "id") ?? slug(title) + "-" + (index + 1);
148810
+ const phaseId = optionalText6(data, "id") ?? slug(title) + "-" + (index + 1);
147901
148811
  return {
147902
148812
  id: phaseId,
147903
148813
  title,
147904
- instructions: optionalText5(data, "instructions") ?? "",
148814
+ instructions: optionalText6(data, "instructions") ?? "",
147905
148815
  branches: optionalStringArray(data, "branches"),
147906
148816
  expectedOutputs: optionalStringArray(data, "expectedOutputs"),
147907
148817
  verificationGates: optionalStringArray(data, "verificationGates")
@@ -147936,7 +148846,7 @@ function phasesFromBody(body) {
147936
148846
  }
147937
148847
  function normalizeSourcePath(sourcePath) {
147938
148848
  const normalized = sourcePath.replace(/\\/g, "/");
147939
- if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") || path21.posix.normalize(normalized) !== normalized) {
148849
+ if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") || path22.posix.normalize(normalized) !== normalized) {
147940
148850
  throw new Error("workflow source path must be a safe workflows/*.md path");
147941
148851
  }
147942
148852
  return normalized;
@@ -148010,7 +148920,7 @@ function parsedWorkflow(data, body, input) {
148010
148920
  id: requiredText(data, "id"),
148011
148921
  workspaceId: input.workspaceId,
148012
148922
  title: requiredText(data, "title"),
148013
- description: optionalText5(data, "description") ?? "",
148923
+ description: optionalText6(data, "description") ?? "",
148014
148924
  status: optionalStatus(data),
148015
148925
  version: optionalNumber(data, "version", 1),
148016
148926
  taskLenses: optionalStringArray(data, "taskLenses"),
@@ -148029,9 +148939,9 @@ function parsedWorkflow(data, body, input) {
148029
148939
  sourcePath: normalizeSourcePath(input.sourcePath),
148030
148940
  sourceHash: "",
148031
148941
  contentHash: input.contentHash,
148032
- createdAt: optionalText5(data, "createdAt") ?? now2(),
148033
- updatedAt: optionalText5(data, "updatedAt") ?? now2(),
148034
- ...optionalText5(data, "importedFrom") ? { importedFrom: optionalText5(data, "importedFrom") } : {}
148942
+ createdAt: optionalText6(data, "createdAt") ?? now2(),
148943
+ updatedAt: optionalText6(data, "updatedAt") ?? now2(),
148944
+ ...optionalText6(data, "importedFrom") ? { importedFrom: optionalText6(data, "importedFrom") } : {}
148035
148945
  };
148036
148946
  if (candidate.phases.length === 0) {
148037
148947
  throw new Error("workflow must contain at least one phase or a non-empty body");
@@ -148085,7 +148995,7 @@ async function writeCanonicalWorkflow(input) {
148085
148995
  const filePath = resolveKnowledgeWorkspaceFile(input.workspace, workflow.sourcePath);
148086
148996
  const content = renderWorkflowMarkdown(workflow);
148087
148997
  await withFileLock(paths.root, async () => {
148088
- await fs14.mkdir(path21.dirname(filePath), { recursive: true });
148998
+ await fs15.mkdir(path22.dirname(filePath), { recursive: true });
148089
148999
  await atomicWriteFile(filePath, content);
148090
149000
  });
148091
149001
  const store = await storeFor2(input.workspace);
@@ -148094,13 +149004,13 @@ async function writeCanonicalWorkflow(input) {
148094
149004
  async function markdownFiles2(directory) {
148095
149005
  let entries;
148096
149006
  try {
148097
- entries = await fs14.readdir(directory, { withFileTypes: true });
149007
+ entries = await fs15.readdir(directory, { withFileTypes: true });
148098
149008
  } catch {
148099
149009
  return [];
148100
149010
  }
148101
149011
  const files = [];
148102
149012
  for (const entry of entries) {
148103
- const absolutePath = path21.join(directory, entry.name);
149013
+ const absolutePath = path22.join(directory, entry.name);
148104
149014
  if (entry.isDirectory()) {
148105
149015
  files.push(...await markdownFiles2(absolutePath));
148106
149016
  } else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
@@ -148115,9 +149025,9 @@ async function syncCanonicalWorkflows(workspace) {
148115
149025
  const workflows = [];
148116
149026
  const errors = [];
148117
149027
  for (const absolutePath of await markdownFiles2(paths.workflows)) {
148118
- const sourcePath = path21.relative(paths.root, absolutePath).split(path21.sep).join("/");
149028
+ const sourcePath = path22.relative(paths.root, absolutePath).split(path22.sep).join("/");
148119
149029
  try {
148120
- const raw = await fs14.readFile(absolutePath, "utf8");
149030
+ const raw = await fs15.readFile(absolutePath, "utf8");
148121
149031
  const workflow = parseWorkflowMarkdown(raw, { workspaceId: workspace.id, sourcePath });
148122
149032
  workflows.push(store.upsertWorkflow(workflow));
148123
149033
  } catch (error) {
@@ -148229,21 +149139,21 @@ function importedWorkflowSpec(input) {
148229
149139
  return materializeWorkflow(spec);
148230
149140
  }
148231
149141
  async function importWindsurfWorkflows(input) {
148232
- const sourceDirectory = path21.join(path21.resolve(input.projectRoot), ".windsurf", "workflows");
149142
+ const sourceDirectory = path22.join(path22.resolve(input.projectRoot), ".windsurf", "workflows");
148233
149143
  const paths = getKnowledgeWorkspacePaths(input.workspace);
148234
149144
  const imported = [];
148235
149145
  const skipped = [];
148236
149146
  let entries;
148237
149147
  try {
148238
- entries = await fs14.readdir(sourceDirectory, { withFileTypes: true });
149148
+ entries = await fs15.readdir(sourceDirectory, { withFileTypes: true });
148239
149149
  } catch {
148240
149150
  return { imported, skipped };
148241
149151
  }
148242
149152
  for (const entry of entries.filter((item) => item.isFile() && item.name.toLowerCase().endsWith(".md"))) {
148243
- const originalPath = path21.join(sourceDirectory, entry.name);
148244
- const sourcePath = path21.relative(path21.resolve(input.projectRoot), originalPath).split(path21.sep).join("/");
149153
+ const originalPath = path22.join(sourceDirectory, entry.name);
149154
+ const sourcePath = path22.relative(path22.resolve(input.projectRoot), originalPath).split(path22.sep).join("/");
148245
149155
  try {
148246
- const raw = await fs14.readFile(originalPath, "utf8");
149156
+ const raw = await fs15.readFile(originalPath, "utf8");
148247
149157
  const workflow = importedWorkflowSpec({
148248
149158
  workspace: input.workspace,
148249
149159
  sourceName: entry.name,
@@ -148252,7 +149162,7 @@ async function importWindsurfWorkflows(input) {
148252
149162
  });
148253
149163
  const targetPath = resolveKnowledgeWorkspaceFile(input.workspace, workflow.sourcePath);
148254
149164
  try {
148255
- await fs14.access(targetPath);
149165
+ await fs15.access(targetPath);
148256
149166
  skipped.push({ sourcePath, reason: "canonical workflow already exists and was preserved" });
148257
149167
  continue;
148258
149168
  } catch {
@@ -148265,12 +149175,12 @@ async function importWindsurfWorkflows(input) {
148265
149175
  });
148266
149176
  }
148267
149177
  }
148268
- await fs14.mkdir(paths.workflows, { recursive: true });
149178
+ await fs15.mkdir(paths.workflows, { recursive: true });
148269
149179
  return { imported, skipped };
148270
149180
  }
148271
149181
  function isWithin2(root, candidate) {
148272
- const relative3 = path21.relative(root, candidate);
148273
- return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(".." + path21.sep) && !path21.isAbsolute(relative3);
149182
+ const relative3 = path22.relative(root, candidate);
149183
+ return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(".." + path22.sep) && !path22.isAbsolute(relative3);
148274
149184
  }
148275
149185
  function adapterRelativePath(workflow, agent) {
148276
149186
  const name = "memorix-workflow-" + slug(workflow.id.replace(/^workflow:/, ""));
@@ -148356,15 +149266,15 @@ async function previewWorkflowAdapter(input) {
148356
149266
  reason: "The canonical workflow does not declare compatibility with this agent."
148357
149267
  };
148358
149268
  }
148359
- const projectRoot = path21.resolve(input.projectRoot);
148360
- const targetPath = path21.resolve(projectRoot, adapterRelativePath(input.workflow, input.agent));
149269
+ const projectRoot = path22.resolve(input.projectRoot);
149270
+ const targetPath = path22.resolve(projectRoot, adapterRelativePath(input.workflow, input.agent));
148361
149271
  if (!isWithin2(projectRoot, targetPath)) {
148362
149272
  throw new Error("Workflow adapter target escapes the project root");
148363
149273
  }
148364
149274
  const content = adapterContent(input.workflow, input.agent);
148365
149275
  let existing;
148366
149276
  try {
148367
- existing = await fs14.readFile(targetPath, "utf8");
149277
+ existing = await fs15.readFile(targetPath, "utf8");
148368
149278
  } catch (error) {
148369
149279
  const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
148370
149280
  if (code !== "ENOENT") throw error;
@@ -148385,11 +149295,11 @@ async function applyWorkflowAdapter(input) {
148385
149295
  if (initial.status === "unsupported" || initial.status === "conflict" || initial.status === "unchanged") {
148386
149296
  return initial;
148387
149297
  }
148388
- const projectRoot = path21.resolve(input.projectRoot);
149298
+ const projectRoot = path22.resolve(input.projectRoot);
148389
149299
  return withFileLock(projectRoot, async () => {
148390
149300
  const current = await previewWorkflowAdapter(input);
148391
149301
  if (current.status !== "create" && current.status !== "update") return current;
148392
- await fs14.mkdir(path21.dirname(current.targetPath), { recursive: true });
149302
+ await fs15.mkdir(path22.dirname(current.targetPath), { recursive: true });
148393
149303
  await atomicWriteFile(current.targetPath, current.content);
148394
149304
  return current;
148395
149305
  });
@@ -148679,6 +149589,11 @@ function createProjectMaintenanceHandler(projectId, projectDir2, projectRoot, op
148679
149589
  }
148680
149590
  return { action: "complete" };
148681
149591
  }
149592
+ if (job.kind === "long-term-maintenance") {
149593
+ const { maintainLongTermMemories: maintainLongTermMemories2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
149594
+ await maintainLongTermMemories2({ dataDir: projectDir2, projectId });
149595
+ return { action: "reschedule", delayMs: 24 * 60 * 60 * 1e3, resetAttempts: true };
149596
+ }
148682
149597
  if (job.kind === "consolidation") {
148683
149598
  const limit2 = consolidationBatchSize(job.payload);
148684
149599
  const result2 = await runAutomaticConsolidation(projectId, projectDir2, {
@@ -149020,15 +149935,15 @@ __export(vector_backfill_runner_exports, {
149020
149935
  });
149021
149936
  import { spawn as spawn4 } from "child_process";
149022
149937
  import { existsSync as existsSync10 } from "fs";
149023
- import path22 from "path";
149938
+ import path23 from "path";
149024
149939
  import { fileURLToPath as fileURLToPath5 } from "url";
149025
149940
  function isNonEmptyString(value) {
149026
149941
  return typeof value === "string" && value.trim().length > 0;
149027
149942
  }
149028
149943
  function resolveVectorBackfillRunnerPath(moduleUrl = import.meta.url) {
149029
- const moduleDir = path22.dirname(fileURLToPath5(moduleUrl));
149030
- const distDir = path22.basename(moduleDir) === "cli" ? path22.dirname(moduleDir) : path22.basename(moduleDir) === "runtime" && path22.basename(path22.dirname(moduleDir)) === "src" ? path22.join(path22.dirname(path22.dirname(moduleDir)), "dist") : moduleDir;
149031
- return path22.join(distDir, "vector-backfill-runner.js");
149944
+ const moduleDir = path23.dirname(fileURLToPath5(moduleUrl));
149945
+ const distDir = path23.basename(moduleDir) === "cli" ? path23.dirname(moduleDir) : path23.basename(moduleDir) === "runtime" && path23.basename(path23.dirname(moduleDir)) === "src" ? path23.join(path23.dirname(path23.dirname(moduleDir)), "dist") : moduleDir;
149946
+ return path23.join(distDir, "vector-backfill-runner.js");
149032
149947
  }
149033
149948
  function parseVectorBackfillRequest(raw) {
149034
149949
  let value;
@@ -149037,7 +149952,7 @@ function parseVectorBackfillRequest(raw) {
149037
149952
  } catch {
149038
149953
  throw new Error("Vector backfill runner received invalid JSON input");
149039
149954
  }
149040
- if (!value || typeof value !== "object" || !isNonEmptyString(value.projectId) || !isNonEmptyString(value.projectRoot) || !isNonEmptyString(value.dataDir) || !path22.isAbsolute(value.projectRoot) || !path22.isAbsolute(value.dataDir)) {
149955
+ if (!value || typeof value !== "object" || !isNonEmptyString(value.projectId) || !isNonEmptyString(value.projectRoot) || !isNonEmptyString(value.dataDir) || !path23.isAbsolute(value.projectRoot) || !path23.isAbsolute(value.dataDir)) {
149041
149956
  throw new Error("Vector backfill runner received an invalid request");
149042
149957
  }
149043
149958
  return {
@@ -150119,8 +151034,16 @@ var init_observations = __esm({
150119
151034
  });
150120
151035
 
150121
151036
  // src/store/session-store.ts
150122
- import path23 from "path";
150123
- import fs15 from "fs";
151037
+ var session_store_exports = {};
151038
+ __export(session_store_exports, {
151039
+ SessionGracefulDegrade: () => SessionGracefulDegrade,
151040
+ SessionSqliteStore: () => SessionSqliteStore,
151041
+ getSessionStore: () => getSessionStore,
151042
+ initSessionStore: () => initSessionStore,
151043
+ resetSessionStore: () => resetSessionStore
151044
+ });
151045
+ import path24 from "path";
151046
+ import fs16 from "fs";
150124
151047
  function sessionToRow(session) {
150125
151048
  return {
150126
151049
  id: session.id,
@@ -150149,6 +151072,10 @@ function getSessionStore() {
150149
151072
  }
150150
151073
  return _store3;
150151
151074
  }
151075
+ function resetSessionStore() {
151076
+ _store3 = null;
151077
+ _storeDataDir3 = null;
151078
+ }
150152
151079
  async function initSessionStore(dataDir) {
150153
151080
  if (_store3 && _storeDataDir3 === dataDir) return _store3;
150154
151081
  _store3 = null;
@@ -150202,10 +151129,10 @@ var init_session_store = __esm({
150202
151129
  async migrateFromJsonIfNeeded() {
150203
151130
  const count2 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM sessions`).get();
150204
151131
  if (count2.cnt > 0) return;
150205
- const jsonPath = path23.join(this.dataDir, "sessions.json");
150206
- if (!fs15.existsSync(jsonPath)) return;
151132
+ const jsonPath = path24.join(this.dataDir, "sessions.json");
151133
+ if (!fs16.existsSync(jsonPath)) return;
150207
151134
  try {
150208
- const raw = fs15.readFileSync(jsonPath, "utf-8");
151135
+ const raw = fs16.readFileSync(jsonPath, "utf-8");
150209
151136
  const sessions = JSON.parse(raw);
150210
151137
  if (!Array.isArray(sessions) || sessions.length === 0) return;
150211
151138
  console.error(`[memorix] Migrating ${sessions.length} sessions from JSON to SQLite...`);
@@ -152001,827 +152928,6 @@ var init_session = __esm({
152001
152928
  }
152002
152929
  });
152003
152930
 
152004
- // src/memory/owner.ts
152005
- var owner_exports = {};
152006
- __export(owner_exports, {
152007
- resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
152008
- });
152009
- import { randomUUID as randomUUID11 } from "crypto";
152010
- import { promises as fs17 } from "fs";
152011
- import path26 from "path";
152012
- function ownerPath(dataDir) {
152013
- const resolved = path26.resolve(dataDir);
152014
- const home = path26.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path26.dirname(resolved) : resolved;
152015
- return path26.join(home, "memorix-user.json");
152016
- }
152017
- function normalizedOwnerId(value) {
152018
- const trimmed = value?.trim();
152019
- return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
152020
- }
152021
- async function readOwnerFile(filePath) {
152022
- try {
152023
- const value = JSON.parse(await fs17.readFile(filePath, "utf8"));
152024
- const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
152025
- return id ? { id, source: "local-file" } : void 0;
152026
- } catch {
152027
- return void 0;
152028
- }
152029
- }
152030
- async function resolveLocalMemoryOwner(dataDir, options = {}) {
152031
- const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
152032
- if (configured) return { id: configured, source: "environment" };
152033
- const filePath = ownerPath(dataDir);
152034
- const existing = await readOwnerFile(filePath);
152035
- if (existing || !options.create) return existing;
152036
- await fs17.mkdir(path26.dirname(filePath), { recursive: true });
152037
- const created = {
152038
- version: 1,
152039
- id: "local-" + randomUUID11(),
152040
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
152041
- };
152042
- try {
152043
- await fs17.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
152044
- return { id: created.id, source: "local-file" };
152045
- } catch (error) {
152046
- if (error?.code !== "EEXIST") throw error;
152047
- const concurrent = await readOwnerFile(filePath);
152048
- if (concurrent) return concurrent;
152049
- throw new Error("Memorix could not establish a valid local memory owner identity.");
152050
- }
152051
- }
152052
- var ID_PATTERN;
152053
- var init_owner = __esm({
152054
- "src/memory/owner.ts"() {
152055
- "use strict";
152056
- init_esm_shims();
152057
- ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
152058
- }
152059
- });
152060
-
152061
- // src/memory/long-term-store.ts
152062
- import { randomUUID as randomUUID12 } from "crypto";
152063
- function optionalText6(value) {
152064
- return typeof value === "string" && value.trim() ? value : void 0;
152065
- }
152066
- function arrayValue2(value) {
152067
- if (typeof value !== "string" || !value) return [];
152068
- try {
152069
- const parsed = JSON.parse(value);
152070
- return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
152071
- } catch {
152072
- return [];
152073
- }
152074
- }
152075
- function rowToMemory(row) {
152076
- return {
152077
- id: row.id,
152078
- originProjectId: row.originProjectId,
152079
- ownerId: row.ownerId,
152080
- scope: row.scope,
152081
- kind: row.kind,
152082
- state: row.state,
152083
- portability: row.portability,
152084
- title: row.title,
152085
- content: row.content,
152086
- facts: arrayValue2(row.factsJson),
152087
- tags: arrayValue2(row.tagsJson),
152088
- ...optionalText6(row.applicability) ? { applicability: row.applicability } : {},
152089
- origin: row.origin,
152090
- createdAt: row.createdAt,
152091
- updatedAt: row.updatedAt,
152092
- ...optionalText6(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
152093
- ...optionalText6(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
152094
- ...optionalText6(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
152095
- ...optionalText6(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
152096
- ...optionalText6(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
152097
- accessCount: Number(row.accessCount ?? 0),
152098
- ...optionalText6(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
152099
- };
152100
- }
152101
- function rowToEvidence2(row) {
152102
- return {
152103
- id: row.id,
152104
- memoryId: row.memoryId,
152105
- kind: row.kind,
152106
- referenceId: row.referenceId,
152107
- relation: row.relation,
152108
- ...optionalText6(row.locator) ? { locator: row.locator } : {},
152109
- ...optionalText6(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
152110
- createdAt: row.createdAt
152111
- };
152112
- }
152113
- function rowToEvent2(row) {
152114
- return {
152115
- id: row.id,
152116
- memoryId: row.memoryId,
152117
- kind: row.kind,
152118
- ...optionalText6(row.fromState) ? { fromState: row.fromState } : {},
152119
- ...optionalText6(row.toState) ? { toState: row.toState } : {},
152120
- ...optionalText6(row.detail) ? { detail: row.detail } : {},
152121
- createdAt: row.createdAt
152122
- };
152123
- }
152124
- var LongTermMemoryStore;
152125
- var init_long_term_store = __esm({
152126
- "src/memory/long-term-store.ts"() {
152127
- "use strict";
152128
- init_esm_shims();
152129
- init_sqlite_db();
152130
- LongTermMemoryStore = class {
152131
- db = null;
152132
- async init(dataDir) {
152133
- this.db = getDatabase(dataDir);
152134
- }
152135
- transaction(fn) {
152136
- return this.db.transaction(fn)();
152137
- }
152138
- get(id) {
152139
- const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
152140
- return row ? rowToMemory(row) : void 0;
152141
- }
152142
- list(options = {}) {
152143
- const limit2 = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
152144
- const clauses = [];
152145
- const params = [];
152146
- if (options.ownerId) {
152147
- clauses.push("ownerId = ?");
152148
- params.push(options.ownerId);
152149
- }
152150
- if (options.originProjectId) {
152151
- clauses.push("originProjectId = ?");
152152
- params.push(options.originProjectId);
152153
- }
152154
- const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
152155
- const rows = this.db.prepare(
152156
- "SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
152157
- ).all(...params, limit2);
152158
- return rows.map(rowToMemory);
152159
- }
152160
- insert(memory) {
152161
- this.db.prepare(`
152162
- INSERT INTO long_term_memories (
152163
- id, originProjectId, ownerId, scope, kind, state, portability,
152164
- title, content, factsJson, tagsJson, applicability, origin,
152165
- createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
152166
- supersededBy, lastValidatedAt, accessCount, lastAccessedAt
152167
- ) VALUES (
152168
- @id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
152169
- @title, @content, @factsJson, @tagsJson, @applicability, @origin,
152170
- @createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
152171
- @supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
152172
- )
152173
- `).run({
152174
- ...memory,
152175
- factsJson: JSON.stringify(memory.facts),
152176
- tagsJson: JSON.stringify(memory.tags),
152177
- applicability: memory.applicability ?? null,
152178
- qualifiedAt: memory.qualifiedAt ?? null,
152179
- approvedAt: memory.approvedAt ?? null,
152180
- archivedAt: memory.archivedAt ?? null,
152181
- supersededBy: memory.supersededBy ?? null,
152182
- lastValidatedAt: memory.lastValidatedAt ?? null,
152183
- lastAccessedAt: memory.lastAccessedAt ?? null
152184
- });
152185
- }
152186
- update(memory) {
152187
- this.db.prepare(`
152188
- UPDATE long_term_memories SET
152189
- scope = @scope,
152190
- kind = @kind,
152191
- state = @state,
152192
- portability = @portability,
152193
- title = @title,
152194
- content = @content,
152195
- factsJson = @factsJson,
152196
- tagsJson = @tagsJson,
152197
- applicability = @applicability,
152198
- updatedAt = @updatedAt,
152199
- qualifiedAt = @qualifiedAt,
152200
- approvedAt = @approvedAt,
152201
- archivedAt = @archivedAt,
152202
- supersededBy = @supersededBy,
152203
- lastValidatedAt = @lastValidatedAt,
152204
- accessCount = @accessCount,
152205
- lastAccessedAt = @lastAccessedAt
152206
- WHERE id = @id
152207
- `).run({
152208
- ...memory,
152209
- factsJson: JSON.stringify(memory.facts),
152210
- tagsJson: JSON.stringify(memory.tags),
152211
- applicability: memory.applicability ?? null,
152212
- qualifiedAt: memory.qualifiedAt ?? null,
152213
- approvedAt: memory.approvedAt ?? null,
152214
- archivedAt: memory.archivedAt ?? null,
152215
- supersededBy: memory.supersededBy ?? null,
152216
- lastValidatedAt: memory.lastValidatedAt ?? null,
152217
- lastAccessedAt: memory.lastAccessedAt ?? null
152218
- });
152219
- }
152220
- insertEvidence(input) {
152221
- const evidence = {
152222
- id: randomUUID12(),
152223
- ...input,
152224
- createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
152225
- };
152226
- this.db.prepare(`
152227
- INSERT INTO long_term_memory_evidence (
152228
- id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
152229
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
152230
- `).run(
152231
- evidence.id,
152232
- evidence.memoryId,
152233
- evidence.kind,
152234
- evidence.referenceId,
152235
- evidence.relation,
152236
- evidence.locator ?? null,
152237
- evidence.capturedHash ?? null,
152238
- evidence.createdAt
152239
- );
152240
- return evidence;
152241
- }
152242
- listEvidence(memoryId) {
152243
- return this.db.prepare(`
152244
- SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
152245
- `).all(memoryId).map(rowToEvidence2);
152246
- }
152247
- recordEvent(input) {
152248
- const event = {
152249
- id: randomUUID12(),
152250
- ...input,
152251
- createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
152252
- };
152253
- this.db.prepare(`
152254
- INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
152255
- VALUES (?, ?, ?, ?, ?, ?, ?)
152256
- `).run(
152257
- event.id,
152258
- event.memoryId,
152259
- event.kind,
152260
- event.fromState ?? null,
152261
- event.toState ?? null,
152262
- event.detail ?? null,
152263
- event.createdAt
152264
- );
152265
- return event;
152266
- }
152267
- listEvents(memoryId) {
152268
- return this.db.prepare(`
152269
- SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
152270
- `).all(memoryId).map(rowToEvent2);
152271
- }
152272
- markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
152273
- const unique2 = [...new Set(ids.filter(Boolean))];
152274
- if (unique2.length === 0) return;
152275
- const statement = this.db.prepare(`
152276
- UPDATE long_term_memories
152277
- SET accessCount = accessCount + 1, lastAccessedAt = ?
152278
- WHERE id = ?
152279
- `);
152280
- this.db.transaction(() => {
152281
- for (const id of unique2) statement.run(at, id);
152282
- })();
152283
- }
152284
- };
152285
- }
152286
- });
152287
-
152288
- // src/memory/long-term-types.ts
152289
- var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
152290
- var init_long_term_types = __esm({
152291
- "src/memory/long-term-types.ts"() {
152292
- "use strict";
152293
- init_esm_shims();
152294
- LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
152295
- LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
152296
- LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
152297
- LONG_TERM_MEMORY_EVIDENCE_KINDS = [
152298
- "observation",
152299
- "claim",
152300
- "workflow",
152301
- "session",
152302
- "checkpoint",
152303
- "git",
152304
- "code",
152305
- "test",
152306
- "run",
152307
- "document",
152308
- "manual",
152309
- "user"
152310
- ];
152311
- LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
152312
- "supports",
152313
- "verifies",
152314
- "derives",
152315
- "user-confirmed"
152316
- ];
152317
- }
152318
- });
152319
-
152320
- // src/memory/long-term.ts
152321
- var long_term_exports = {};
152322
- __export(long_term_exports, {
152323
- approveLongTermMemory: () => approveLongTermMemory,
152324
- archiveLongTermMemory: () => archiveLongTermMemory,
152325
- canReadLongTermMemory: () => canReadLongTermMemory,
152326
- createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
152327
- createManualLongTermMemory: () => createManualLongTermMemory,
152328
- getLongTermMemoryDetail: () => getLongTermMemoryDetail,
152329
- isEligibleLongTermMemory: () => isEligibleLongTermMemory,
152330
- listLongTermMemories: () => listLongTermMemories,
152331
- promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
152332
- qualifyLongTermMemory: () => qualifyLongTermMemory,
152333
- renderLongTermMemorySummary: () => renderLongTermMemorySummary,
152334
- selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
152335
- supersedeLongTermMemory: () => supersedeLongTermMemory
152336
- });
152337
- import { randomUUID as randomUUID13 } from "crypto";
152338
- function isOneOf(value, values) {
152339
- return typeof value === "string" && values.includes(value);
152340
- }
152341
- function compactText2(value, field, max = MAX_TEXT_LENGTH) {
152342
- if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
152343
- const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
152344
- if (!safe) throw new Error("Long-term memory " + field + " is required.");
152345
- if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
152346
- return safe;
152347
- }
152348
- function compactList(values, field) {
152349
- if (!values) return [];
152350
- if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
152351
- return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText2(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
152352
- }
152353
- function normalizeEvidence(input) {
152354
- if (!Array.isArray(input) || input.length === 0) {
152355
- throw new Error("A long-term memory requires at least one source evidence reference.");
152356
- }
152357
- return input.map((evidence) => {
152358
- if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
152359
- throw new Error("Long-term memory evidence kind is invalid.");
152360
- }
152361
- if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
152362
- throw new Error("Long-term memory evidence relation is invalid.");
152363
- }
152364
- return {
152365
- kind: evidence.kind,
152366
- referenceId: compactText2(evidence.referenceId, "evidence reference", 1e3),
152367
- relation: evidence.relation,
152368
- ...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator", 1e3) } : {},
152369
- ...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash", 1e3) } : {}
152370
- };
152371
- });
152372
- }
152373
- function assertScope(scope) {
152374
- if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
152375
- }
152376
- function assertKind(kind) {
152377
- if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
152378
- }
152379
- function assertPortability(value) {
152380
- if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
152381
- throw new Error("Long-term memory portability is invalid.");
152382
- }
152383
- }
152384
- function assertPortableEvidence(scope, portability, evidence) {
152385
- if (portability !== "portable") return;
152386
- if (scope !== "user") {
152387
- throw new Error("Only user-scoped long-term memories may be portable across projects.");
152388
- }
152389
- if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
152390
- 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.");
152391
- }
152392
- }
152393
- function requireTeamReader(scope, reader) {
152394
- if (scope === "team" && reader?.isTeamMember !== true) {
152395
- throw new Error("Team long-term memory requires an explicit active team identity for this project.");
152396
- }
152397
- }
152398
- function matchesScope(memory, reader) {
152399
- if (memory.scope === "project") return memory.originProjectId === reader.projectId;
152400
- if (memory.scope === "team") {
152401
- return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
152402
- }
152403
- if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
152404
- return memory.portability === "portable" || memory.originProjectId === reader.projectId;
152405
- }
152406
- function canReadLongTermMemory(memory, reader) {
152407
- return matchesScope(memory, reader);
152408
- }
152409
- function isEligibleLongTermMemory(memory) {
152410
- return memory.state === "qualified" || memory.state === "approved";
152411
- }
152412
- function taskTerms(task) {
152413
- const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
152414
- return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
152415
- }
152416
- function kindFitScore(kind, task) {
152417
- const normalized = task.toLocaleLowerCase("en-US");
152418
- const hints = {
152419
- episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
152420
- semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
152421
- procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
152422
- };
152423
- return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
152424
- }
152425
- function validationFreshnessScore(memory) {
152426
- const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
152427
- if (!validatedAt) return 0;
152428
- const time = Date.parse(validatedAt);
152429
- if (!Number.isFinite(time)) return 0;
152430
- const ageDays = Math.max(0, Date.now() - time) / (24 * 60 * 60 * 1e3);
152431
- if (ageDays <= 30) return 0.35;
152432
- if (ageDays <= 90) return 0.15;
152433
- return 0;
152434
- }
152435
- function scoreForTask(memory, task) {
152436
- const terms = taskTerms(task);
152437
- if (terms.length === 0) return void 0;
152438
- const title = memory.title.toLocaleLowerCase("en-US");
152439
- const content = memory.content.toLocaleLowerCase("en-US");
152440
- const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
152441
- const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
152442
- const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
152443
- let score = memory.state === "approved" ? 0.5 : 0;
152444
- const matched = [];
152445
- for (const term of terms) {
152446
- let termScore = 0;
152447
- if (title.includes(term)) termScore += 5;
152448
- if (tags.includes(term)) termScore += 4;
152449
- if (applicability.includes(term)) termScore += 3;
152450
- if (facts.includes(term)) termScore += 2;
152451
- if (content.includes(term)) termScore += 1;
152452
- if (termScore > 0) {
152453
- score += termScore;
152454
- matched.push(term);
152455
- }
152456
- }
152457
- if (matched.length === 0) return void 0;
152458
- score += kindFitScore(memory.kind, task);
152459
- score += validationFreshnessScore(memory);
152460
- const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
152461
- const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
152462
- return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
152463
- }
152464
- function semanticText(memory) {
152465
- return sanitizeCredentials([
152466
- memory.title,
152467
- memory.applicability,
152468
- memory.tags.join(" "),
152469
- memory.facts.join(" "),
152470
- memory.content
152471
- ].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
152472
- }
152473
- function cosineSimilarity(left, right) {
152474
- if (left.length === 0 || left.length !== right.length) return 0;
152475
- let dot = 0;
152476
- let leftMagnitude = 0;
152477
- let rightMagnitude = 0;
152478
- for (let index = 0; index < left.length; index += 1) {
152479
- const a = left[index];
152480
- const b = right[index];
152481
- if (!Number.isFinite(a) || !Number.isFinite(b)) return 0;
152482
- dot += a * b;
152483
- leftMagnitude += a * a;
152484
- rightMagnitude += b * b;
152485
- }
152486
- if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
152487
- return dot / Math.sqrt(leftMagnitude * rightMagnitude);
152488
- }
152489
- function semanticSelection(memory, similarity, task) {
152490
- if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
152491
- const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
152492
- const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
152493
- return {
152494
- score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
152495
- reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
152496
- };
152497
- }
152498
- async function resolveSemanticProvider(explicit) {
152499
- if (explicit !== void 0) return explicit;
152500
- try {
152501
- const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
152502
- return await withTimeout(
152503
- getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
152504
- SEMANTIC_RETRIEVAL_TIMEOUT_MS,
152505
- "Long-term semantic provider initialization"
152506
- );
152507
- } catch {
152508
- return null;
152509
- }
152510
- }
152511
- function createStore(dataDir) {
152512
- const store = new LongTermMemoryStore();
152513
- return store.init(dataDir).then(() => store);
152514
- }
152515
- async function createManualLongTermMemory(input) {
152516
- const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
152517
- if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
152518
- assertScope(input.scope);
152519
- const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID13(), relation: "supports" }];
152520
- return createLongTermMemoryWithDataDir({
152521
- dataDir: input.dataDir,
152522
- originProjectId: input.projectId,
152523
- ownerId: owner.id,
152524
- scope: input.scope,
152525
- kind: input.kind,
152526
- portability: input.portability,
152527
- title: input.title,
152528
- content: input.content,
152529
- facts: input.facts,
152530
- tags: input.tags,
152531
- applicability: input.applicability,
152532
- origin: "manual",
152533
- evidence,
152534
- reader: input.reader
152535
- });
152536
- }
152537
- async function promoteObservationToLongTermMemory(input) {
152538
- const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
152539
- if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
152540
- return createLongTermMemoryWithDataDir({
152541
- dataDir: input.dataDir,
152542
- originProjectId: input.observation.projectId,
152543
- ownerId: owner.id,
152544
- scope: input.scope,
152545
- kind: input.kind,
152546
- portability: "project-bound",
152547
- title: input.observation.title,
152548
- content: input.observation.narrative,
152549
- facts: input.observation.facts,
152550
- tags: input.tags ?? input.observation.concepts,
152551
- applicability: input.applicability,
152552
- origin: input.observation.source === "git" ? "git" : "agent",
152553
- evidence: [{
152554
- kind: "observation",
152555
- referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
152556
- relation: "derives"
152557
- }],
152558
- reader: input.reader
152559
- });
152560
- }
152561
- async function createLongTermMemoryCandidate(input) {
152562
- return createLongTermMemoryWithDataDir(input);
152563
- }
152564
- async function createLongTermMemoryWithDataDir(input) {
152565
- assertScope(input.scope);
152566
- assertKind(input.kind);
152567
- const portability = input.portability ?? "project-bound";
152568
- assertPortability(portability);
152569
- requireTeamReader(input.scope, input.reader);
152570
- const evidenceInputs = normalizeEvidence(input.evidence);
152571
- assertPortableEvidence(input.scope, portability, evidenceInputs);
152572
- const now3 = (/* @__PURE__ */ new Date()).toISOString();
152573
- const memory = {
152574
- id: randomUUID13(),
152575
- originProjectId: compactText2(input.originProjectId, "origin project id", 1e3),
152576
- ownerId: compactText2(input.ownerId, "owner id", 160),
152577
- scope: input.scope,
152578
- kind: input.kind,
152579
- state: "candidate",
152580
- portability,
152581
- title: compactText2(input.title, "title", 500),
152582
- content: compactText2(input.content, "content"),
152583
- facts: compactList(input.facts, "facts"),
152584
- tags: compactList(input.tags, "tags"),
152585
- ...input.applicability ? { applicability: compactText2(input.applicability, "applicability", 1e3) } : {},
152586
- origin: input.origin ?? "manual",
152587
- createdAt: now3,
152588
- updatedAt: now3,
152589
- accessCount: 0
152590
- };
152591
- const store = await createStore(input.dataDir);
152592
- const evidence = store.transaction(() => {
152593
- store.insert(memory);
152594
- const written = evidenceInputs.map((item) => store.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
152595
- store.recordEvent({
152596
- memoryId: memory.id,
152597
- kind: "created",
152598
- toState: "candidate",
152599
- detail: "Created as a source-backed long-term memory candidate.",
152600
- createdAt: now3
152601
- });
152602
- return written;
152603
- });
152604
- return { memory, evidence };
152605
- }
152606
- function transitionDetail(value) {
152607
- return compactText2(value, "transition reason", 1e3);
152608
- }
152609
- async function transitionMemory(input) {
152610
- const store = await createStore(input.dataDir);
152611
- return store.transaction(() => {
152612
- const current = store.get(input.id);
152613
- if (!current) throw new Error("Long-term memory was not found.");
152614
- if (current.state !== input.expected) {
152615
- throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
152616
- }
152617
- const evidence = store.listEvidence(current.id);
152618
- if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
152619
- const now3 = (/* @__PURE__ */ new Date()).toISOString();
152620
- const next = {
152621
- ...current,
152622
- state: input.next,
152623
- updatedAt: now3,
152624
- lastValidatedAt: now3,
152625
- ...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
152626
- };
152627
- store.update(next);
152628
- store.recordEvent({
152629
- memoryId: next.id,
152630
- kind: input.event,
152631
- fromState: current.state,
152632
- toState: next.state,
152633
- detail: transitionDetail(input.reason),
152634
- createdAt: now3
152635
- });
152636
- return next;
152637
- });
152638
- }
152639
- async function qualifyLongTermMemory(input) {
152640
- return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
152641
- }
152642
- async function approveLongTermMemory(input) {
152643
- return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
152644
- }
152645
- async function archiveLongTermMemory(input) {
152646
- const store = await createStore(input.dataDir);
152647
- return store.transaction(() => {
152648
- const current = store.get(input.id);
152649
- if (!current) throw new Error("Long-term memory was not found.");
152650
- if (current.state === "archived" || current.state === "superseded") {
152651
- throw new Error("Long-term memory is already inactive.");
152652
- }
152653
- const now3 = (/* @__PURE__ */ new Date()).toISOString();
152654
- const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
152655
- store.update(next);
152656
- store.recordEvent({
152657
- memoryId: next.id,
152658
- kind: "archived",
152659
- fromState: current.state,
152660
- toState: "archived",
152661
- detail: transitionDetail(input.reason),
152662
- createdAt: now3
152663
- });
152664
- return next;
152665
- });
152666
- }
152667
- async function supersedeLongTermMemory(input) {
152668
- const store = await createStore(input.dataDir);
152669
- return store.transaction(() => {
152670
- const current = store.get(input.id);
152671
- if (!current) throw new Error("Long-term memory was not found.");
152672
- if (current.state === "archived" || current.state === "superseded") {
152673
- throw new Error("Long-term memory is already inactive.");
152674
- }
152675
- if (current.id === input.supersededBy) {
152676
- throw new Error("Long-term memory cannot supersede itself.");
152677
- }
152678
- const replacement = store.get(input.supersededBy);
152679
- if (!replacement || !isEligibleLongTermMemory(replacement)) {
152680
- throw new Error("Replacement long-term memory must be qualified or approved.");
152681
- }
152682
- if (replacement.scope !== current.scope) {
152683
- throw new Error("Replacement long-term memory must use the same scope.");
152684
- }
152685
- if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
152686
- throw new Error("User long-term memory may only be superseded by the same local owner.");
152687
- }
152688
- if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
152689
- throw new Error("Project and team long-term memory may only be superseded within the same project.");
152690
- }
152691
- const now3 = (/* @__PURE__ */ new Date()).toISOString();
152692
- const next = {
152693
- ...current,
152694
- state: "superseded",
152695
- supersededBy: replacement.id,
152696
- updatedAt: now3
152697
- };
152698
- store.update(next);
152699
- store.recordEvent({
152700
- memoryId: next.id,
152701
- kind: "superseded",
152702
- fromState: current.state,
152703
- toState: "superseded",
152704
- detail: transitionDetail(input.reason),
152705
- createdAt: now3
152706
- });
152707
- return next;
152708
- });
152709
- }
152710
- async function listLongTermMemories(input) {
152711
- const store = await createStore(input.dataDir);
152712
- 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) }));
152713
- }
152714
- async function getLongTermMemoryDetail(input) {
152715
- const store = await createStore(input.dataDir);
152716
- const memory = store.get(input.id);
152717
- if (!memory || !canReadLongTermMemory(memory, input.reader)) {
152718
- throw new Error("Long-term memory was not found in the active scope.");
152719
- }
152720
- store.markAccess([memory.id]);
152721
- return { memory, evidence: store.listEvidence(memory.id), events: store.listEvents(memory.id) };
152722
- }
152723
- async function selectLongTermMemoriesForTask(input) {
152724
- const task = input.task?.trim();
152725
- if (!task) return [];
152726
- const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
152727
- const reader = {
152728
- projectId: input.projectId,
152729
- ...owner ? { ownerId: owner.id } : {},
152730
- ...input.agentId ? { agentId: input.agentId } : {},
152731
- ...input.isTeamMember ? { isTeamMember: true } : {}
152732
- };
152733
- const store = await createStore(input.dataDir);
152734
- const eligible = store.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
152735
- let selected = eligible.map((memory) => {
152736
- const ranked = scoreForTask(memory, task);
152737
- return ranked ? {
152738
- memory,
152739
- evidence: store.listEvidence(memory.id),
152740
- score: ranked.score,
152741
- reason: ranked.reason
152742
- } : void 0;
152743
- }).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));
152744
- if (selected.length === 0 && eligible.length > 0) {
152745
- const provider2 = await resolveSemanticProvider(input.embeddingProvider);
152746
- const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
152747
- if (provider2 && candidates.length > 0) {
152748
- try {
152749
- const vectors = await withTimeout(
152750
- provider2.embedBatch([task, ...candidates.map(semanticText)], {
152751
- timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
152752
- retry: false
152753
- }),
152754
- SEMANTIC_RETRIEVAL_TIMEOUT_MS,
152755
- "Long-term semantic retrieval"
152756
- );
152757
- const query = vectors[0];
152758
- if (Array.isArray(query)) {
152759
- selected = candidates.map((memory, index) => {
152760
- const vector = vectors[index + 1];
152761
- const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
152762
- return ranked ? {
152763
- memory,
152764
- evidence: store.listEvidence(memory.id),
152765
- score: ranked.score,
152766
- reason: ranked.reason
152767
- } : void 0;
152768
- }).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));
152769
- }
152770
- } catch {
152771
- }
152772
- }
152773
- }
152774
- selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
152775
- store.markAccess(selected.map((item) => item.memory.id));
152776
- return selected;
152777
- }
152778
- function renderLongTermMemorySummary(memory, maxTokens = 22) {
152779
- const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
152780
- const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
152781
- return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
152782
- }
152783
- var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
152784
- var init_long_term = __esm({
152785
- "src/memory/long-term.ts"() {
152786
- "use strict";
152787
- init_esm_shims();
152788
- init_token_budget();
152789
- init_timeout();
152790
- init_secret_filter();
152791
- init_owner();
152792
- init_long_term_store();
152793
- init_long_term_types();
152794
- MAX_TEXT_LENGTH = 12e3;
152795
- MAX_LIST_ITEMS = 40;
152796
- MAX_SEMANTIC_CANDIDATES = 48;
152797
- SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
152798
- MIN_SEMANTIC_SIMILARITY = 0.58;
152799
- PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
152800
- STOP_TERMS = /* @__PURE__ */ new Set([
152801
- "the",
152802
- "and",
152803
- "for",
152804
- "with",
152805
- "from",
152806
- "that",
152807
- "this",
152808
- "into",
152809
- "when",
152810
- "what",
152811
- "\u5982\u4F55",
152812
- "\u4EC0\u4E48",
152813
- "\u8FD9\u4E2A",
152814
- "\u90A3\u4E2A",
152815
- "\u6211\u4EEC",
152816
- "\u9879\u76EE",
152817
- "\u8FDB\u884C",
152818
- "\u9700\u8981",
152819
- "\u7EE7\u7EED",
152820
- "\u5904\u7406"
152821
- ]);
152822
- }
152823
- });
152824
-
152825
152931
  // src/knowledge/workset.ts
152826
152932
  function unique(values) {
152827
152933
  return [...new Set(values.filter(Boolean))];
@@ -153029,6 +153135,37 @@ function renderTaskWorksetPrompt(input) {
153029
153135
  trust: "source-backed"
153030
153136
  });
153031
153137
  appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
153138
+ if (input.alwaysOn) {
153139
+ const hasAlwaysOn = input.alwaysOn.profile.length > 0 || Boolean(input.alwaysOn.state) || input.alwaysOn.durable.length > 0;
153140
+ if (hasAlwaysOn) {
153141
+ appendLine(lines, "", maxTokens, omitted, "always-on-heading");
153142
+ appendLine(lines, "You and this workspace", maxTokens, omitted, "always-on-heading");
153143
+ for (const profile of input.alwaysOn.profile.slice(0, 2)) {
153144
+ appendLine(lines, "- " + short(profile, 26), maxTokens, omitted, "always-on-profile", selected, {
153145
+ kind: "memory",
153146
+ id: "profile",
153147
+ reason: "user profile memory",
153148
+ trust: "source-backed"
153149
+ });
153150
+ }
153151
+ if (input.alwaysOn.state) {
153152
+ appendLine(lines, "- Recently: " + short(input.alwaysOn.state, 40), maxTokens, omitted, "always-on-state", selected, {
153153
+ kind: "continuation",
153154
+ id: "workspace-state",
153155
+ reason: "latest session summary for this workspace",
153156
+ trust: "historical"
153157
+ });
153158
+ }
153159
+ for (const durable of input.alwaysOn.durable.slice(0, 2)) {
153160
+ appendLine(lines, "- " + short(durable, 28), maxTokens, omitted, "always-on-durable", selected, {
153161
+ kind: "durable-memory",
153162
+ id: "durable-always",
153163
+ reason: "recent durable memory for this workspace",
153164
+ trust: "source-backed"
153165
+ });
153166
+ }
153167
+ }
153168
+ }
153032
153169
  const hasContinuation = Boolean(
153033
153170
  input.continuation?.previousSession || (input.continuation?.memories.length ?? 0) > 0 || input.continuation?.compactCheckpoint
153034
153171
  );
@@ -153499,6 +153636,7 @@ async function buildTaskWorkset(input) {
153499
153636
  version: "1.3",
153500
153637
  task,
153501
153638
  lens: input.lens,
153639
+ ...input.alwaysOn && (input.alwaysOn.profile.length > 0 || input.alwaysOn.state || input.alwaysOn.durable.length > 0) ? { alwaysOn: input.alwaysOn } : {},
153502
153640
  currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
153503
153641
  ...continuation ? { continuation } : {},
153504
153642
  ...input.codeState ? { codeState: short(input.codeState, 28) } : {},
@@ -154651,6 +154789,33 @@ function activeProjectObservations(observations2, projectId) {
154651
154789
  function deliveryEligibleProjectObservations(observations2, projectId) {
154652
154790
  return activeProjectObservations(observations2, projectId).filter((observation) => isEligibleForAutomaticDelivery(observation));
154653
154791
  }
154792
+ async function buildAlwaysOnBlock(input) {
154793
+ const profile = filterReadableObservations(input.eligible, input.reader).filter((observation) => observation.visibility === "personal" && observation.entityName === "user-profile").slice(0, 2).map((observation) => compactContinuationText(observation.title, 22));
154794
+ let state;
154795
+ try {
154796
+ await initSessionStore(input.dataDir);
154797
+ const { getSessionStore: getSessionStore2 } = await Promise.resolve().then(() => (init_session_store(), session_store_exports));
154798
+ const sessions = await getSessionStore2().loadByProject(input.projectId);
154799
+ const latest = sessions.filter((session) => session.endedAt && session.summary?.trim()).sort((a, b) => Date.parse(b.endedAt ?? "") - Date.parse(a.endedAt ?? ""))[0];
154800
+ if (latest?.summary) state = compactContinuationText(latest.summary, 40);
154801
+ } catch {
154802
+ }
154803
+ const durable = [];
154804
+ try {
154805
+ const items = await listLongTermMemories({
154806
+ dataDir: input.dataDir,
154807
+ reader: {
154808
+ projectId: input.projectId,
154809
+ ...input.reader.agentId ? { agentId: input.reader.agentId } : {},
154810
+ ...input.reader.isTeamMember ? { isTeamMember: true } : {}
154811
+ },
154812
+ limit: 10
154813
+ });
154814
+ durable.push(...items.filter((item) => item.memory.state === "approved").sort((a, b) => (b.memory.updatedAt ?? "").localeCompare(a.memory.updatedAt ?? "")).slice(0, 2).map((item) => item.memory.kind + ": " + compactContinuationText(item.memory.title, 22)));
154815
+ } catch {
154816
+ }
154817
+ return { profile, state, durable };
154818
+ }
154654
154819
  function decideRefresh(input) {
154655
154820
  if (input.mode === "never") {
154656
154821
  return { performed: false, reason: "disabled", message: "Automatic project scan disabled." };
@@ -154855,12 +155020,19 @@ async function buildAutoProjectContext(input) {
154855
155020
  };
154856
155021
  }
154857
155022
  }
155023
+ const alwaysOn = await buildAlwaysOnBlock({
155024
+ dataDir: input.dataDir,
155025
+ projectId: input.project.id,
155026
+ reader: input.reader ?? { projectId: input.project.id },
155027
+ eligible: deliveryEligibleProjectObservations(input.observations, input.project.id)
155028
+ });
154858
155029
  const workset = await buildTaskWorkset({
154859
155030
  projectId: input.project.id,
154860
155031
  dataDir: input.dataDir,
154861
155032
  ...task ? { task } : {},
154862
155033
  lens: lens.id,
154863
155034
  startHere,
155035
+ ...alwaysOn.profile.length > 0 || alwaysOn.state || alwaysOn.durable.length > 0 ? { alwaysOn } : {},
154864
155036
  ...externalOutline ? { semanticCode: externalOutline } : {},
154865
155037
  providerQuality: providerQuality2,
154866
155038
  currentFacts: worksetFactLines(currentFacts),
@@ -155043,7 +155215,16 @@ function formatAutoProjectContextSummary(context) {
155043
155215
  lensLine(context),
155044
155216
  "",
155045
155217
  ...formatCurrentFactsLines(context.currentFacts),
155046
- "",
155218
+ ""
155219
+ ].filter(Boolean);
155220
+ if (context.workset.alwaysOn && (context.workset.alwaysOn.profile.length > 0 || context.workset.alwaysOn.state || context.workset.alwaysOn.durable.length > 0)) {
155221
+ lines.push("You and this workspace");
155222
+ for (const profile of context.workset.alwaysOn.profile.slice(0, 2)) lines.push(`- ${profile}`);
155223
+ if (context.workset.alwaysOn.state) lines.push(`- Recently: ${context.workset.alwaysOn.state}`);
155224
+ for (const durable of context.workset.alwaysOn.durable.slice(0, 2)) lines.push(`- ${durable}`);
155225
+ lines.push("");
155226
+ }
155227
+ lines.push(...[
155047
155228
  `- Code memory: ${context.overview.code.files} files / ${context.overview.code.symbols} symbols / ${context.overview.code.refs} memory links`,
155048
155229
  `- Code provider: ${context.providerQuality.selected} (${context.providerQuality.selectedQuality})`,
155049
155230
  `- Languages: ${formatLanguages(context.overview)}`,
@@ -155052,7 +155233,7 @@ function formatAutoProjectContextSummary(context) {
155052
155233
  `- Refresh: ${context.refresh.message}`,
155053
155234
  "",
155054
155235
  "Start here"
155055
- ].filter(Boolean);
155236
+ ].filter(Boolean));
155056
155237
  if (startHere.length > 0) {
155057
155238
  startHere.forEach((path34, index) => lines.push(`${index + 1}. ${path34}`));
155058
155239
  } else {
@@ -155176,6 +155357,8 @@ var init_auto_context = __esm({
155176
155357
  init_store();
155177
155358
  init_admission();
155178
155359
  init_session();
155360
+ init_long_term();
155361
+ init_visibility();
155179
155362
  init_session_store();
155180
155363
  init_task_lens();
155181
155364
  DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
@@ -158126,8 +158309,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
158126
158309
  const { promises: fsP } = await import("fs");
158127
158310
  const registryPath = path30.join(baseDir, ".project-aliases.json");
158128
158311
  const raw = await fsP.readFile(registryPath, "utf-8");
158129
- const registry = JSON.parse(raw);
158130
- aliasGroups = registry.groups || [];
158312
+ const registry2 = JSON.parse(raw);
158313
+ aliasGroups = registry2.groups || [];
158131
158314
  } catch {
158132
158315
  }
158133
158316
  const currentGroup = aliasGroups.find((g) => g.aliases?.includes(effectiveProjectId) || g.canonical === effectiveProjectId);
@@ -161043,13 +161226,28 @@ alwaysApply: true
161043
161226
  '- For "why" decisions, use `memorix_store_reasoning`',
161044
161227
  '- 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`.',
161045
161228
  "- 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.",
161229
+ '- Record the user profile: the user\u2019s role, expertise, preferences, and goals. Save these with `entityName: "user-profile"` and `visibility: "personal"` so they stay private and appear in every brief as the "who you are" context.',
161046
161230
  "",
161047
161231
  "**Don't store:** greetings, simple file reads, trivial commands (ls, pwd, git status).",
161048
161232
  "",
161233
+ "**Only store what a future session cannot re-derive.** Code structure, file contents, and Git history are live in the checkout \u2014 do not store facts already visible there. A memory earns its place by capturing the why, the context, or a conclusion the checkout alone cannot show.",
161234
+ "",
161235
+ `**Record what worked, not only what failed.** Store validated approaches and explicit user confirmations alongside corrections. Saving only failures drifts behavior away from what the user already accepted; a clear "yes, that's right" is feedback worth keeping too.`,
161236
+ "",
161237
+ "**Recalled memory is a claim about the past.** A memory naming a specific file, function, or flag describes the past at write time \u2014 check the file exists or grep the symbol before recommending it. If the user says to ignore or not use memory, proceed as if memory were empty: do not apply, cite, compare, or mention stored content.",
161238
+ "",
161049
161239
  "## When to resolve memory",
161050
161240
  "",
161051
161241
  "Use `memorix_resolve` when a task is done or a bug is fixed. This keeps future searches focused on active work instead of surfacing completed items.",
161052
161242
  "",
161243
+ "## End sessions with a summary",
161244
+ "",
161245
+ "When a session finishes, call `memorix_session_end` with a short structured summary so the next agent can resume. Recommended sections:",
161246
+ "- **Goal** \u2014 what this session was working on",
161247
+ "- **Discoveries** \u2014 findings, gotchas, learnings",
161248
+ "- **Accomplished** \u2014 completed items, plus PENDING items for the next session",
161249
+ "- **Relevant Files** \u2014 paths and what changed",
161250
+ "",
161053
161251
  "## Tools quick reference",
161054
161252
  "",
161055
161253
  "| Tool | Use when |",
@@ -162644,8 +162842,8 @@ init_persistence();
162644
162842
 
162645
162843
  // src/rules/syncer.ts
162646
162844
  init_esm_shims();
162647
- import { promises as fs16 } from "fs";
162648
- import path24 from "path";
162845
+ import { promises as fs17 } from "fs";
162846
+ import path25 from "path";
162649
162847
 
162650
162848
  // src/rules/adapters/cursor.ts
162651
162849
  init_esm_shims();
@@ -163308,8 +163506,8 @@ var RulesSyncer = class {
163308
163506
  const found = await this.findFiles(scanPath);
163309
163507
  for (const filePath of found) {
163310
163508
  try {
163311
- const content = await fs16.readFile(filePath, "utf-8");
163312
- const relativePath = path24.relative(this.projectRoot, filePath).replace(/\\/g, "/");
163509
+ const content = await fs17.readFile(filePath, "utf-8");
163510
+ const relativePath = path25.relative(this.projectRoot, filePath).replace(/\\/g, "/");
163313
163511
  const parsed = entry.adapter.parse(relativePath, content);
163314
163512
  rules.push(...parsed);
163315
163513
  } catch {
@@ -163377,7 +163575,7 @@ var RulesSyncer = class {
163377
163575
  const entries = [];
163378
163576
  for (const adapter of this.adapters.values()) {
163379
163577
  const absolutePaths = adapter.filePatterns.map(
163380
- (p) => path24.join(this.projectRoot, p)
163578
+ (p) => path25.join(this.projectRoot, p)
163381
163579
  );
163382
163580
  entries.push({ adapter, paths: absolutePaths });
163383
163581
  }
@@ -163385,13 +163583,13 @@ var RulesSyncer = class {
163385
163583
  }
163386
163584
  /** Find files matching a glob-like path (simple implementation) */
163387
163585
  async findFiles(pattern) {
163388
- const dir = path24.dirname(pattern);
163389
- const fileGlob = path24.basename(pattern);
163586
+ const dir = path25.dirname(pattern);
163587
+ const fileGlob = path25.basename(pattern);
163390
163588
  try {
163391
- const stat4 = await fs16.stat(dir);
163589
+ const stat4 = await fs17.stat(dir);
163392
163590
  if (!stat4.isDirectory()) {
163393
163591
  try {
163394
- await fs16.access(pattern);
163592
+ await fs17.access(pattern);
163395
163593
  return [pattern];
163396
163594
  } catch {
163397
163595
  return [];
@@ -163399,7 +163597,7 @@ var RulesSyncer = class {
163399
163597
  }
163400
163598
  } catch {
163401
163599
  try {
163402
- await fs16.access(pattern);
163600
+ await fs17.access(pattern);
163403
163601
  return [pattern];
163404
163602
  } catch {
163405
163603
  return [];
@@ -163407,16 +163605,16 @@ var RulesSyncer = class {
163407
163605
  }
163408
163606
  if (fileGlob.includes("*")) {
163409
163607
  try {
163410
- const files = await fs16.readdir(dir);
163608
+ const files = await fs17.readdir(dir);
163411
163609
  const ext = fileGlob.replace("*", "");
163412
- return files.filter((f3) => ext ? f3.endsWith(ext) : true).map((f3) => path24.join(dir, f3));
163610
+ return files.filter((f3) => ext ? f3.endsWith(ext) : true).map((f3) => path25.join(dir, f3));
163413
163611
  } catch {
163414
163612
  return [];
163415
163613
  }
163416
163614
  }
163417
163615
  try {
163418
- await fs16.access(path24.join(dir, fileGlob));
163419
- return [path24.join(dir, fileGlob)];
163616
+ await fs17.access(path25.join(dir, fileGlob));
163617
+ return [path25.join(dir, fileGlob)];
163420
163618
  } catch {
163421
163619
  return [];
163422
163620
  }
@@ -164958,13 +165156,47 @@ function parseFormationTimeoutMs(raw) {
164958
165156
  init_timeout();
164959
165157
  init_secret_filter();
164960
165158
 
165159
+ // src/search/surfaced-registry.ts
165160
+ init_esm_shims();
165161
+ var MAX_KEYS = 64;
165162
+ var MAX_IDS = 200;
165163
+ var registry = /* @__PURE__ */ new Map();
165164
+ function touch(key) {
165165
+ const ids = registry.get(key);
165166
+ if (!ids) return [];
165167
+ registry.delete(key);
165168
+ registry.set(key, ids);
165169
+ return ids;
165170
+ }
165171
+ function getSurfacedIds(key) {
165172
+ return [...touch(key)];
165173
+ }
165174
+ function recordSurfacedIds(key, ids) {
165175
+ if (ids.length === 0) return;
165176
+ let list = registry.get(key) ?? [];
165177
+ const known = new Set(list);
165178
+ for (const id of ids) {
165179
+ if (known.has(id)) continue;
165180
+ known.add(id);
165181
+ list.push(id);
165182
+ }
165183
+ if (list.length > MAX_IDS) {
165184
+ list = list.slice(list.length - MAX_IDS);
165185
+ }
165186
+ registry.set(key, list);
165187
+ if (registry.size > MAX_KEYS) {
165188
+ const oldestKey = registry.keys().next().value;
165189
+ if (oldestKey !== void 0 && oldestKey !== key) registry.delete(oldestKey);
165190
+ }
165191
+ }
165192
+
164961
165193
  // src/server/request-context.ts
164962
165194
  init_esm_shims();
164963
- import path25 from "path";
165195
+ import path26 from "path";
164964
165196
  function resolveRoot(projectRoot) {
164965
165197
  const value = projectRoot.trim();
164966
165198
  if (!value) throw new Error("Project binding requires a non-empty project root");
164967
- return path25.resolve(value);
165199
+ return path26.resolve(value);
164968
165200
  }
164969
165201
  var ProjectBindingController = class {
164970
165202
  current;
@@ -165355,6 +165587,12 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
165355
165587
  kind: "consolidation",
165356
165588
  dedupeKey: "startup-consolidation"
165357
165589
  });
165590
+ lifecycle.enqueueLongTermMaintenance({
165591
+ dataDir: projectDir2,
165592
+ projectId: project.id,
165593
+ source: "startup",
165594
+ queue
165595
+ });
165358
165596
  }
165359
165597
  try {
165360
165598
  const { CodeGraphStore: CodeGraphStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
@@ -165404,7 +165642,7 @@ The path should point to a directory containing a .git folder.`
165404
165642
  };
165405
165643
  const server = existingServer ?? new McpServer({
165406
165644
  name: "memorix",
165407
- version: true ? "1.4.5" : "1.0.1"
165645
+ version: true ? "1.5.0" : "1.0.1"
165408
165646
  });
165409
165647
  const originalRegisterTool = server.registerTool.bind(server);
165410
165648
  server.registerTool = ((name, ...args) => {
@@ -165889,7 +166127,7 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
165889
166127
  let longTermNote = "";
165890
166128
  if (longTerm) {
165891
166129
  try {
165892
- const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
166130
+ const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2, maybeAutoQualifyLongTermMemory: maybeAutoQualifyLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
165893
166131
  const promoted = await promoteObservationToLongTermMemory2({
165894
166132
  dataDir: projectDir2,
165895
166133
  observation: obs,
@@ -165903,7 +166141,24 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
165903
166141
  ...reader.isTeamMember ? { isTeamMember: true } : {}
165904
166142
  }
165905
166143
  });
165906
- longTermNote = `
166144
+ const autoQualified = await maybeAutoQualifyLongTermMemory2({
166145
+ dataDir: projectDir2,
166146
+ id: promoted.memory.id,
166147
+ sourceDetail: "explicit"
166148
+ });
166149
+ if (autoQualified) {
166150
+ try {
166151
+ const { enqueueLongTermMaintenance: enqueueLongTermMaintenance2 } = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
166152
+ enqueueLongTermMaintenance2({
166153
+ dataDir: projectDir2,
166154
+ projectId: project.id,
166155
+ source: "long-term:" + promoted.memory.id
166156
+ });
166157
+ } catch {
166158
+ }
166159
+ }
166160
+ longTermNote = autoQualified ? `
166161
+ Long-term memory: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}, auto-qualified) and now enters briefs as durable context. Audit with \`memorix memory long-term list\`.` : `
165907
166162
  Long-term candidate: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}). Review it with the CLI before it is delivered automatically.`;
165908
166163
  } catch (longTermError) {
165909
166164
  longTermNote = `
@@ -166099,6 +166354,13 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
166099
166354
  const boundary = requireExplicitAutopilotExpansion("memorix_search", purpose);
166100
166355
  if (boundary) return boundary;
166101
166356
  }
166357
+ let surfacedKey = `project:${project.id}`;
166358
+ try {
166359
+ const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
166360
+ const active = await getActiveSession2(projectDir2, project.id);
166361
+ if (active) surfacedKey = `session:${active.id}`;
166362
+ } catch {
166363
+ }
166102
166364
  return withFreshIndex(async () => {
166103
166365
  const safeLimit2 = limit2 != null ? coerceNumber(limit2, 20) : void 0;
166104
166366
  const safeMaxTokens = maxTokens != null ? coerceNumber(maxTokens, 0) : void 0;
@@ -166116,7 +166378,8 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
166116
166378
  status: status ?? "active",
166117
166379
  source,
166118
166380
  quality,
166119
- reader: getObservationReader(scope === "global" ? "global" : "project")
166381
+ reader: getObservationReader(scope === "global" ? "global" : "project"),
166382
+ surfacedIds: getSurfacedIds(surfacedKey)
166120
166383
  });
166121
166384
  let result;
166122
166385
  try {
@@ -166135,6 +166398,7 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
166135
166398
  }
166136
166399
  throw error;
166137
166400
  }
166401
+ recordSurfacedIds(surfacedKey, result.entries.map((entry) => entry.id));
166138
166402
  const activeBoundary = getActiveAutopilotRetrievalBoundary();
166139
166403
  if (scope !== "global" && activeBoundary && !force && result.entries.length > 0 && result.entries.every((entry) => activeBoundary.coveredObservationIds.has(entry.id))) {
166140
166404
  const duplicate = blockCoveredAutopilotEvidence("memorix_search", result.entries.map((entry) => entry.id), force);
@@ -169097,13 +169361,26 @@ ${lines.join("\n")}` }] };
169097
169361
  if (bytes.length >= 12 && Buffer.from(bytes.subarray(0, 4)).toString("ascii") === "RIFF" && Buffer.from(bytes.subarray(8, 12)).toString("ascii") === "WEBP") return "image/webp";
169098
169362
  return void 0;
169099
169363
  }
169364
+ const mediaGenerationEnabled = process.env.MEMORIX_MCP_MEDIA_GENERATION === "1";
169365
+ const mediaTranscriptionEnabled = process.env.MEMORIX_MCP_MEDIA_TRANSCRIPTION === "1";
169366
+ const mediaGenerationOrTranscription = mediaGenerationEnabled || mediaTranscriptionEnabled;
169100
169367
  server.registerTool(
169101
169368
  "memorix_media",
169102
169369
  {
169103
169370
  title: "Manage Controlled Media",
169104
169371
  description: "Use the controlled local media library for an explicit import, attachment, inspection, or MiniMax generation request. Assets stay outside the Git worktree and enter normal memory only when attach is explicitly true. Use the CLI for destructive removal, quota cleanup, and direct generation. MCP generation is disabled by default and requires MEMORIX_MCP_MEDIA_GENERATION=1 after the operator reviews provider billing.",
169105
169372
  inputSchema: {
169106
- action: z626.enum(["import", "attach", "list", "show", "derive-pdf", "derive-audio", "generate-image", "generate-video", "status", "cancel"]),
169373
+ action: z626.enum([
169374
+ "import",
169375
+ "attach",
169376
+ "list",
169377
+ "show",
169378
+ "derive-pdf",
169379
+ "status",
169380
+ "cancel",
169381
+ ...mediaTranscriptionEnabled ? ["derive-audio"] : [],
169382
+ ...mediaGenerationEnabled ? ["generate-image", "generate-video"] : []
169383
+ ]),
169107
169384
  path: z626.string().optional().describe("Explicit local image/audio/video/PDF path for import."),
169108
169385
  assetId: z626.string().optional().describe("Controlled MediaAsset ID for attach/show."),
169109
169386
  jobId: z626.string().optional().describe("Durable media job ID for status."),
@@ -169111,20 +169388,26 @@ ${lines.join("\n")}` }] };
169111
169388
  limit: z626.number().int().min(1).max(100).optional().describe("Maximum assets to list."),
169112
169389
  title: z626.string().optional().describe("Observation title when attaching generated/imported output."),
169113
169390
  narrative: z626.string().optional().describe("Short retrieval text when attaching an asset."),
169114
- prompt: z626.string().optional().describe("Explicit MiniMax generation prompt."),
169115
- image: z626.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
169116
- model: z626.string().max(160).optional().describe("Provider model for the selected media action."),
169117
- provider: z626.enum(["openai", "groq"]).optional().describe("Explicit audio transcription provider."),
169118
- language: z626.string().max(32).optional().describe("Optional ISO-639-1 hint for audio transcription."),
169119
- region: z626.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
169120
- n: z626.number().int().min(1).max(4).optional().describe("Image output count."),
169121
- ratio: z626.enum(["adaptive", "1:1", "16:9", "4:3", "3:2", "2:3", "3:4", "9:16", "21:9"]).optional().describe("Image or video aspect ratio."),
169122
- width: z626.number().int().min(1).max(8192).optional().describe("Requested image width."),
169123
- height: z626.number().int().min(1).max(8192).optional().describe("Requested image height."),
169124
- duration: z626.union([z626.literal(5), z626.literal(10)]).optional().describe("MiniMax video duration in seconds."),
169125
169391
  maxPages: z626.number().int().min(1).max(100).optional().describe("Bounded PDF extraction page limit."),
169126
169392
  maxChars: z626.number().int().min(1).max(6e4).optional().describe("Bounded PDF extraction character limit."),
169127
- attach: z626.boolean().optional().describe("Attach generated/imported output to normal project memory explicitly.")
169393
+ attach: z626.boolean().optional().describe("Attach generated/imported output to normal project memory explicitly."),
169394
+ ...mediaGenerationOrTranscription ? {
169395
+ model: z626.string().max(160).optional().describe("Provider model for the selected media action."),
169396
+ prompt: z626.string().optional().describe("Explicit prompt for generation or transcription.")
169397
+ } : {},
169398
+ ...mediaTranscriptionEnabled ? {
169399
+ provider: z626.enum(["openai", "groq"]).optional().describe("Explicit audio transcription provider."),
169400
+ language: z626.string().max(32).optional().describe("Optional ISO-639-1 hint for audio transcription.")
169401
+ } : {},
169402
+ ...mediaGenerationEnabled ? {
169403
+ image: z626.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
169404
+ region: z626.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
169405
+ n: z626.number().int().min(1).max(4).optional().describe("Image output count."),
169406
+ ratio: z626.enum(["adaptive", "1:1", "16:9", "4:3", "3:2", "2:3", "3:4", "9:16", "21:9"]).optional().describe("Image or video aspect ratio."),
169407
+ width: z626.number().int().min(1).max(8192).optional().describe("Requested image width."),
169408
+ height: z626.number().int().min(1).max(8192).optional().describe("Requested image height."),
169409
+ duration: z626.union([z626.literal(5), z626.literal(10)]).optional().describe("MiniMax video duration in seconds.")
169410
+ } : {}
169128
169411
  }
169129
169412
  },
169130
169413
  async ({ action, path: assetPath, assetId, jobId, kind, limit: limit2, title, narrative, prompt, image, model, provider: provider2, language, region, n, ratio, width, height, duration, maxPages, maxChars, attach }) => {