memorix 1.4.5 → 1.4.6

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 +54 -0
  2. package/CLAUDE.md +7 -3
  3. package/README.md +5 -2
  4. package/dist/cli/index.js +1456 -1013
  5. package/dist/index.js +1265 -988
  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 +1755 -829
  9. package/dist/media-audio-runner.js +1760 -835
  10. package/dist/media-video-runner.js +1755 -830
  11. package/dist/memcode-runtime/CHANGELOG.md +54 -0
  12. package/dist/sdk.js +1257 -987
  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 +1751 -826
  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 +17 -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 +0 -5
  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/index.js CHANGED
@@ -3389,7 +3389,6 @@ __export(config_exports, {
3389
3389
  getLLMProvider: () => getLLMProvider,
3390
3390
  getLoadedEnvFiles: () => getLoadedEnvFiles,
3391
3391
  getServerConfig: () => getServerConfig,
3392
- getTeamConfig: () => getTeamConfig,
3393
3392
  loadDotenv: () => loadDotenv,
3394
3393
  loadFileConfig: () => loadFileConfig,
3395
3394
  loadYamlConfig: () => loadYamlConfig,
@@ -3461,14 +3460,10 @@ function getServerConfig() {
3461
3460
  port: server.port
3462
3461
  };
3463
3462
  }
3464
- function getTeamConfig() {
3465
- return loadYamlConfig().team ?? {};
3466
- }
3467
3463
  var init_config = __esm({
3468
3464
  "src/config.ts"() {
3469
3465
  "use strict";
3470
3466
  init_esm_shims();
3471
- init_yaml_loader();
3472
3467
  init_resolved_config();
3473
3468
  init_dotenv_loader();
3474
3469
  init_legacy_loader();
@@ -5753,9 +5748,9 @@ async function saveRegistry(baseDir) {
5753
5748
  await fs7.mkdir(path9.dirname(filePath), { recursive: true });
5754
5749
  await fs7.writeFile(filePath, JSON.stringify(registryCache, null, 2), "utf-8");
5755
5750
  }
5756
- function findMatchingGroup(registry, projectInfo) {
5751
+ function findMatchingGroup(registry2, projectInfo) {
5757
5752
  const normalizedRoot = normalizePath(projectInfo.rootPath);
5758
- for (const group of registry.groups) {
5753
+ for (const group of registry2.groups) {
5759
5754
  if (group.aliases.includes(projectInfo.id)) return group;
5760
5755
  if (group.rootPaths.some((rp) => rp === normalizedRoot)) return group;
5761
5756
  if (projectInfo.gitRemote && group.gitRemote && group.gitRemote === projectInfo.gitRemote) {
@@ -5768,9 +5763,9 @@ function selectCanonical(aliases) {
5768
5763
  return [...aliases].sort((a, b) => idPriority(b) - idPriority(a))[0];
5769
5764
  }
5770
5765
  async function registerAlias(projectInfo, baseDir) {
5771
- const registry = await loadRegistry(baseDir);
5766
+ const registry2 = await loadRegistry(baseDir);
5772
5767
  const normalizedRoot = normalizePath(projectInfo.rootPath);
5773
- const existingGroup = findMatchingGroup(registry, projectInfo);
5768
+ const existingGroup = findMatchingGroup(registry2, projectInfo);
5774
5769
  if (existingGroup) {
5775
5770
  let changed = false;
5776
5771
  if (!existingGroup.aliases.includes(projectInfo.id)) {
@@ -5801,13 +5796,13 @@ async function registerAlias(projectInfo, baseDir) {
5801
5796
  rootPaths: [normalizedRoot],
5802
5797
  ...projectInfo.gitRemote ? { gitRemote: projectInfo.gitRemote } : {}
5803
5798
  };
5804
- registry.groups.push(newGroup);
5799
+ registry2.groups.push(newGroup);
5805
5800
  await saveRegistry(baseDir);
5806
5801
  return newGroup.canonical;
5807
5802
  }
5808
5803
  async function resolveAliases(projectId, baseDir) {
5809
- const registry = await loadRegistry(baseDir);
5810
- for (const group of registry.groups) {
5804
+ const registry2 = await loadRegistry(baseDir);
5805
+ for (const group of registry2.groups) {
5811
5806
  if (group.aliases.includes(projectId) || group.canonical === projectId) {
5812
5807
  return [...group.aliases];
5813
5808
  }
@@ -5815,8 +5810,8 @@ async function resolveAliases(projectId, baseDir) {
5815
5810
  return [projectId];
5816
5811
  }
5817
5812
  async function getCanonicalId(projectId, baseDir) {
5818
- const registry = await loadRegistry(baseDir);
5819
- for (const group of registry.groups) {
5813
+ const registry2 = await loadRegistry(baseDir);
5814
+ for (const group of registry2.groups) {
5820
5815
  if (group.aliases.includes(projectId) || group.canonical === projectId) {
5821
5816
  return group.canonical;
5822
5817
  }
@@ -5824,12 +5819,12 @@ async function getCanonicalId(projectId, baseDir) {
5824
5819
  return projectId;
5825
5820
  }
5826
5821
  async function getAllAliasGroups(baseDir) {
5827
- const registry = await loadRegistry(baseDir);
5828
- return registry.groups;
5822
+ const registry2 = await loadRegistry(baseDir);
5823
+ return registry2.groups;
5829
5824
  }
5830
5825
  async function autoMergeByBaseName(observedIds, baseDir) {
5831
5826
  if (observedIds.length <= 1) return 0;
5832
- const registry = await loadRegistry(baseDir);
5827
+ const registry2 = await loadRegistry(baseDir);
5833
5828
  const byBaseName = /* @__PURE__ */ new Map();
5834
5829
  for (const id of observedIds) {
5835
5830
  const baseName = id.split("/").pop() ?? id;
@@ -5842,7 +5837,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
5842
5837
  const existingGroups = /* @__PURE__ */ new Set();
5843
5838
  const ungroupedIds = [];
5844
5839
  for (const id of ids) {
5845
- const groupIdx = registry.groups.findIndex(
5840
+ const groupIdx = registry2.groups.findIndex(
5846
5841
  (g) => g.aliases.includes(id) || g.canonical === id
5847
5842
  );
5848
5843
  if (groupIdx >= 0) {
@@ -5856,7 +5851,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
5856
5851
  const canonical = selectCanonical(allIdsInGroup);
5857
5852
  if (existingGroups.size > 0) {
5858
5853
  const primaryIdx = [...existingGroups][0];
5859
- const primaryGroup = registry.groups[primaryIdx];
5854
+ const primaryGroup = registry2.groups[primaryIdx];
5860
5855
  for (const id of allIdsInGroup) {
5861
5856
  if (!primaryGroup.aliases.includes(id)) {
5862
5857
  primaryGroup.aliases.push(id);
@@ -5864,7 +5859,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
5864
5859
  }
5865
5860
  const otherIdxs = [...existingGroups].slice(1).sort((a, b) => b - a);
5866
5861
  for (const idx of otherIdxs) {
5867
- const other = registry.groups[idx];
5862
+ const other = registry2.groups[idx];
5868
5863
  for (const alias of other.aliases) {
5869
5864
  if (!primaryGroup.aliases.includes(alias)) {
5870
5865
  primaryGroup.aliases.push(alias);
@@ -5878,12 +5873,12 @@ async function autoMergeByBaseName(observedIds, baseDir) {
5878
5873
  if (other.gitRemote && !primaryGroup.gitRemote) {
5879
5874
  primaryGroup.gitRemote = other.gitRemote;
5880
5875
  }
5881
- registry.groups.splice(idx, 1);
5876
+ registry2.groups.splice(idx, 1);
5882
5877
  }
5883
5878
  primaryGroup.canonical = selectCanonical(primaryGroup.aliases);
5884
5879
  mergeCount++;
5885
5880
  } else {
5886
- registry.groups.push({
5881
+ registry2.groups.push({
5887
5882
  canonical,
5888
5883
  aliases: allIdsInGroup,
5889
5884
  rootPaths: []
@@ -6519,7 +6514,7 @@ async function searchObservations(options) {
6519
6514
  const expandedEmbeddingQuery = quality === "thorough" && tier === "heavy" ? await maybeExpandSearchQuery(options.query) : options.query;
6520
6515
  mark("queryExpansion");
6521
6516
  const intentResult = quality === "fast" || !hasQuery ? null : detectQueryIntent(originalQuery);
6522
- const requestLimit = projectIds ? (options.limit ?? 20) * 3 : options.limit ?? 20;
6517
+ const requestLimit = projectIds || options.surfacedIds && options.surfacedIds.length > 0 ? (options.limit ?? 20) * 3 : options.limit ?? 20;
6523
6518
  const defaultBoost = {
6524
6519
  title: 3,
6525
6520
  entityName: 2,
@@ -6751,6 +6746,14 @@ async function searchObservations(options) {
6751
6746
  });
6752
6747
  intermediate.sort((a, b) => b.score - a.score);
6753
6748
  }
6749
+ if (options.surfacedIds && options.surfacedIds.length > 0) {
6750
+ const SURFACED_PENALTY = 0.5;
6751
+ const surfaced = new Set(options.surfacedIds);
6752
+ intermediate = intermediate.map(
6753
+ (entry) => surfaced.has(entry.id) ? { ...entry, score: (entry.score ?? 0) * SURFACED_PENALTY } : entry
6754
+ );
6755
+ intermediate.sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
6756
+ }
6754
6757
  if (options.since) {
6755
6758
  const sinceDate = new Date(options.since).getTime();
6756
6759
  intermediate = intermediate.filter((e2) => new Date(e2.rawTime).getTime() >= sinceDate);
@@ -7742,7 +7745,8 @@ var init_maintenance_jobs = __esm({
7742
7745
  "claim-requalification",
7743
7746
  "knowledge-compile",
7744
7747
  "knowledge-lint",
7745
- "workflow-index"
7748
+ "workflow-index",
7749
+ "long-term-maintenance"
7746
7750
  ];
7747
7751
  DEFAULT_MAX_ATTEMPTS = 8;
7748
7752
  DEFAULT_LEASE_MS = 3e4;
@@ -8151,6 +8155,7 @@ __export(lifecycle_exports, {
8151
8155
  enqueueClaimRequalification: () => enqueueClaimRequalification,
8152
8156
  enqueueCodegraphRefresh: () => enqueueCodegraphRefresh,
8153
8157
  enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups,
8158
+ enqueueLongTermMaintenance: () => enqueueLongTermMaintenance,
8154
8159
  enqueueObservationQualification: () => enqueueObservationQualification
8155
8160
  });
8156
8161
  function queueFor(input) {
@@ -8194,6 +8199,14 @@ function enqueueObservationQualification(input) {
8194
8199
  payload: { source: input.source, limit: 100 }
8195
8200
  });
8196
8201
  }
8202
+ function enqueueLongTermMaintenance(input) {
8203
+ queueFor(input).enqueue({
8204
+ projectId: input.projectId,
8205
+ kind: "long-term-maintenance",
8206
+ dedupeKey: "long-term-maintenance",
8207
+ payload: { source: input.source }
8208
+ });
8209
+ }
8197
8210
  function enqueueKnowledgeFollowups(input) {
8198
8211
  const queue = queueFor(input);
8199
8212
  const payload = {
@@ -8373,7 +8386,8 @@ var init_isolated_maintenance = __esm({
8373
8386
  "claim-requalification",
8374
8387
  "knowledge-compile",
8375
8388
  "knowledge-lint",
8376
- "workflow-index"
8389
+ "workflow-index",
8390
+ "long-term-maintenance"
8377
8391
  ];
8378
8392
  DEFAULT_TIMEOUT_MS = 5 * 6e4;
8379
8393
  MAX_CHILD_OUTPUT_BYTES = 1e6;
@@ -144220,6 +144234,889 @@ var init_retention = __esm({
144220
144234
  }
144221
144235
  });
144222
144236
 
144237
+ // src/memory/owner.ts
144238
+ var owner_exports = {};
144239
+ __export(owner_exports, {
144240
+ resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
144241
+ });
144242
+ import { randomUUID as randomUUID6 } from "crypto";
144243
+ import { promises as fs12 } from "fs";
144244
+ import path19 from "path";
144245
+ function ownerPath(dataDir) {
144246
+ const resolved = path19.resolve(dataDir);
144247
+ const home = path19.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path19.dirname(resolved) : resolved;
144248
+ return path19.join(home, "memorix-user.json");
144249
+ }
144250
+ function normalizedOwnerId(value) {
144251
+ const trimmed = value?.trim();
144252
+ return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
144253
+ }
144254
+ async function readOwnerFile(filePath) {
144255
+ try {
144256
+ const value = JSON.parse(await fs12.readFile(filePath, "utf8"));
144257
+ const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
144258
+ return id ? { id, source: "local-file" } : void 0;
144259
+ } catch {
144260
+ return void 0;
144261
+ }
144262
+ }
144263
+ async function resolveLocalMemoryOwner(dataDir, options = {}) {
144264
+ const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
144265
+ if (configured) return { id: configured, source: "environment" };
144266
+ const filePath = ownerPath(dataDir);
144267
+ const existing = await readOwnerFile(filePath);
144268
+ if (existing || !options.create) return existing;
144269
+ await fs12.mkdir(path19.dirname(filePath), { recursive: true });
144270
+ const created = {
144271
+ version: 1,
144272
+ id: "local-" + randomUUID6(),
144273
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
144274
+ };
144275
+ try {
144276
+ await fs12.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
144277
+ return { id: created.id, source: "local-file" };
144278
+ } catch (error) {
144279
+ if (error?.code !== "EEXIST") throw error;
144280
+ const concurrent = await readOwnerFile(filePath);
144281
+ if (concurrent) return concurrent;
144282
+ throw new Error("Memorix could not establish a valid local memory owner identity.");
144283
+ }
144284
+ }
144285
+ var ID_PATTERN;
144286
+ var init_owner = __esm({
144287
+ "src/memory/owner.ts"() {
144288
+ "use strict";
144289
+ init_esm_shims();
144290
+ ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
144291
+ }
144292
+ });
144293
+
144294
+ // src/memory/long-term-store.ts
144295
+ import { randomUUID as randomUUID7 } from "crypto";
144296
+ function optionalText2(value) {
144297
+ return typeof value === "string" && value.trim() ? value : void 0;
144298
+ }
144299
+ function arrayValue2(value) {
144300
+ if (typeof value !== "string" || !value) return [];
144301
+ try {
144302
+ const parsed = JSON.parse(value);
144303
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
144304
+ } catch {
144305
+ return [];
144306
+ }
144307
+ }
144308
+ function rowToMemory(row) {
144309
+ return {
144310
+ id: row.id,
144311
+ originProjectId: row.originProjectId,
144312
+ ownerId: row.ownerId,
144313
+ scope: row.scope,
144314
+ kind: row.kind,
144315
+ state: row.state,
144316
+ portability: row.portability,
144317
+ title: row.title,
144318
+ content: row.content,
144319
+ facts: arrayValue2(row.factsJson),
144320
+ tags: arrayValue2(row.tagsJson),
144321
+ ...optionalText2(row.applicability) ? { applicability: row.applicability } : {},
144322
+ origin: row.origin,
144323
+ createdAt: row.createdAt,
144324
+ updatedAt: row.updatedAt,
144325
+ ...optionalText2(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
144326
+ ...optionalText2(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
144327
+ ...optionalText2(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
144328
+ ...optionalText2(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
144329
+ ...optionalText2(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
144330
+ accessCount: Number(row.accessCount ?? 0),
144331
+ ...optionalText2(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
144332
+ };
144333
+ }
144334
+ function rowToEvidence(row) {
144335
+ return {
144336
+ id: row.id,
144337
+ memoryId: row.memoryId,
144338
+ kind: row.kind,
144339
+ referenceId: row.referenceId,
144340
+ relation: row.relation,
144341
+ ...optionalText2(row.locator) ? { locator: row.locator } : {},
144342
+ ...optionalText2(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
144343
+ createdAt: row.createdAt
144344
+ };
144345
+ }
144346
+ function rowToEvent(row) {
144347
+ return {
144348
+ id: row.id,
144349
+ memoryId: row.memoryId,
144350
+ kind: row.kind,
144351
+ ...optionalText2(row.fromState) ? { fromState: row.fromState } : {},
144352
+ ...optionalText2(row.toState) ? { toState: row.toState } : {},
144353
+ ...optionalText2(row.detail) ? { detail: row.detail } : {},
144354
+ createdAt: row.createdAt
144355
+ };
144356
+ }
144357
+ var LongTermMemoryStore;
144358
+ var init_long_term_store = __esm({
144359
+ "src/memory/long-term-store.ts"() {
144360
+ "use strict";
144361
+ init_esm_shims();
144362
+ init_sqlite_db();
144363
+ LongTermMemoryStore = class {
144364
+ db = null;
144365
+ async init(dataDir) {
144366
+ this.db = getDatabase(dataDir);
144367
+ }
144368
+ transaction(fn) {
144369
+ return this.db.transaction(fn)();
144370
+ }
144371
+ get(id) {
144372
+ const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
144373
+ return row ? rowToMemory(row) : void 0;
144374
+ }
144375
+ list(options = {}) {
144376
+ const limit2 = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
144377
+ const clauses = [];
144378
+ const params = [];
144379
+ if (options.ownerId) {
144380
+ clauses.push("ownerId = ?");
144381
+ params.push(options.ownerId);
144382
+ }
144383
+ if (options.originProjectId) {
144384
+ clauses.push("originProjectId = ?");
144385
+ params.push(options.originProjectId);
144386
+ }
144387
+ const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
144388
+ const rows = this.db.prepare(
144389
+ "SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
144390
+ ).all(...params, limit2);
144391
+ return rows.map(rowToMemory);
144392
+ }
144393
+ insert(memory) {
144394
+ this.db.prepare(`
144395
+ INSERT INTO long_term_memories (
144396
+ id, originProjectId, ownerId, scope, kind, state, portability,
144397
+ title, content, factsJson, tagsJson, applicability, origin,
144398
+ createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
144399
+ supersededBy, lastValidatedAt, accessCount, lastAccessedAt
144400
+ ) VALUES (
144401
+ @id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
144402
+ @title, @content, @factsJson, @tagsJson, @applicability, @origin,
144403
+ @createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
144404
+ @supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
144405
+ )
144406
+ `).run({
144407
+ ...memory,
144408
+ factsJson: JSON.stringify(memory.facts),
144409
+ tagsJson: JSON.stringify(memory.tags),
144410
+ applicability: memory.applicability ?? null,
144411
+ qualifiedAt: memory.qualifiedAt ?? null,
144412
+ approvedAt: memory.approvedAt ?? null,
144413
+ archivedAt: memory.archivedAt ?? null,
144414
+ supersededBy: memory.supersededBy ?? null,
144415
+ lastValidatedAt: memory.lastValidatedAt ?? null,
144416
+ lastAccessedAt: memory.lastAccessedAt ?? null
144417
+ });
144418
+ }
144419
+ update(memory) {
144420
+ this.db.prepare(`
144421
+ UPDATE long_term_memories SET
144422
+ scope = @scope,
144423
+ kind = @kind,
144424
+ state = @state,
144425
+ portability = @portability,
144426
+ title = @title,
144427
+ content = @content,
144428
+ factsJson = @factsJson,
144429
+ tagsJson = @tagsJson,
144430
+ applicability = @applicability,
144431
+ updatedAt = @updatedAt,
144432
+ qualifiedAt = @qualifiedAt,
144433
+ approvedAt = @approvedAt,
144434
+ archivedAt = @archivedAt,
144435
+ supersededBy = @supersededBy,
144436
+ lastValidatedAt = @lastValidatedAt,
144437
+ accessCount = @accessCount,
144438
+ lastAccessedAt = @lastAccessedAt
144439
+ WHERE id = @id
144440
+ `).run({
144441
+ ...memory,
144442
+ factsJson: JSON.stringify(memory.facts),
144443
+ tagsJson: JSON.stringify(memory.tags),
144444
+ applicability: memory.applicability ?? null,
144445
+ qualifiedAt: memory.qualifiedAt ?? null,
144446
+ approvedAt: memory.approvedAt ?? null,
144447
+ archivedAt: memory.archivedAt ?? null,
144448
+ supersededBy: memory.supersededBy ?? null,
144449
+ lastValidatedAt: memory.lastValidatedAt ?? null,
144450
+ lastAccessedAt: memory.lastAccessedAt ?? null
144451
+ });
144452
+ }
144453
+ insertEvidence(input) {
144454
+ const evidence = {
144455
+ id: randomUUID7(),
144456
+ ...input,
144457
+ createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
144458
+ };
144459
+ this.db.prepare(`
144460
+ INSERT INTO long_term_memory_evidence (
144461
+ id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
144462
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
144463
+ `).run(
144464
+ evidence.id,
144465
+ evidence.memoryId,
144466
+ evidence.kind,
144467
+ evidence.referenceId,
144468
+ evidence.relation,
144469
+ evidence.locator ?? null,
144470
+ evidence.capturedHash ?? null,
144471
+ evidence.createdAt
144472
+ );
144473
+ return evidence;
144474
+ }
144475
+ listEvidence(memoryId) {
144476
+ return this.db.prepare(`
144477
+ SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
144478
+ `).all(memoryId).map(rowToEvidence);
144479
+ }
144480
+ recordEvent(input) {
144481
+ const event = {
144482
+ id: randomUUID7(),
144483
+ ...input,
144484
+ createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
144485
+ };
144486
+ this.db.prepare(`
144487
+ INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
144488
+ VALUES (?, ?, ?, ?, ?, ?, ?)
144489
+ `).run(
144490
+ event.id,
144491
+ event.memoryId,
144492
+ event.kind,
144493
+ event.fromState ?? null,
144494
+ event.toState ?? null,
144495
+ event.detail ?? null,
144496
+ event.createdAt
144497
+ );
144498
+ return event;
144499
+ }
144500
+ listEvents(memoryId) {
144501
+ return this.db.prepare(`
144502
+ SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
144503
+ `).all(memoryId).map(rowToEvent);
144504
+ }
144505
+ markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
144506
+ const unique2 = [...new Set(ids.filter(Boolean))];
144507
+ if (unique2.length === 0) return;
144508
+ const statement = this.db.prepare(`
144509
+ UPDATE long_term_memories
144510
+ SET accessCount = accessCount + 1, lastAccessedAt = ?
144511
+ WHERE id = ?
144512
+ `);
144513
+ this.db.transaction(() => {
144514
+ for (const id of unique2) statement.run(at, id);
144515
+ })();
144516
+ }
144517
+ };
144518
+ }
144519
+ });
144520
+
144521
+ // src/memory/long-term-types.ts
144522
+ var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
144523
+ var init_long_term_types = __esm({
144524
+ "src/memory/long-term-types.ts"() {
144525
+ "use strict";
144526
+ init_esm_shims();
144527
+ LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
144528
+ LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
144529
+ LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
144530
+ LONG_TERM_MEMORY_EVIDENCE_KINDS = [
144531
+ "observation",
144532
+ "claim",
144533
+ "workflow",
144534
+ "session",
144535
+ "checkpoint",
144536
+ "git",
144537
+ "code",
144538
+ "test",
144539
+ "run",
144540
+ "document",
144541
+ "manual",
144542
+ "user"
144543
+ ];
144544
+ LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
144545
+ "supports",
144546
+ "verifies",
144547
+ "derives",
144548
+ "user-confirmed"
144549
+ ];
144550
+ }
144551
+ });
144552
+
144553
+ // src/memory/long-term.ts
144554
+ var long_term_exports = {};
144555
+ __export(long_term_exports, {
144556
+ approveLongTermMemory: () => approveLongTermMemory,
144557
+ archiveLongTermMemory: () => archiveLongTermMemory,
144558
+ canReadLongTermMemory: () => canReadLongTermMemory,
144559
+ createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
144560
+ createManualLongTermMemory: () => createManualLongTermMemory,
144561
+ getLongTermMemoryDetail: () => getLongTermMemoryDetail,
144562
+ isEligibleLongTermMemory: () => isEligibleLongTermMemory,
144563
+ listLongTermMemories: () => listLongTermMemories,
144564
+ maintainLongTermMemories: () => maintainLongTermMemories,
144565
+ maybeAutoQualifyLongTermMemory: () => maybeAutoQualifyLongTermMemory,
144566
+ promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
144567
+ qualifyLongTermMemory: () => qualifyLongTermMemory,
144568
+ renderLongTermMemorySummary: () => renderLongTermMemorySummary,
144569
+ selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
144570
+ supersedeLongTermMemory: () => supersedeLongTermMemory
144571
+ });
144572
+ import { randomUUID as randomUUID8 } from "crypto";
144573
+ function isOneOf(value, values) {
144574
+ return typeof value === "string" && values.includes(value);
144575
+ }
144576
+ function compactText(value, field, max = MAX_TEXT_LENGTH) {
144577
+ if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
144578
+ const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
144579
+ if (!safe) throw new Error("Long-term memory " + field + " is required.");
144580
+ if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
144581
+ return safe;
144582
+ }
144583
+ function compactList(values, field) {
144584
+ if (!values) return [];
144585
+ if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
144586
+ return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
144587
+ }
144588
+ function normalizeEvidence(input) {
144589
+ if (!Array.isArray(input) || input.length === 0) {
144590
+ throw new Error("A long-term memory requires at least one source evidence reference.");
144591
+ }
144592
+ return input.map((evidence) => {
144593
+ if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
144594
+ throw new Error("Long-term memory evidence kind is invalid.");
144595
+ }
144596
+ if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
144597
+ throw new Error("Long-term memory evidence relation is invalid.");
144598
+ }
144599
+ return {
144600
+ kind: evidence.kind,
144601
+ referenceId: compactText(evidence.referenceId, "evidence reference", 1e3),
144602
+ relation: evidence.relation,
144603
+ ...evidence.locator ? { locator: compactText(evidence.locator, "evidence locator", 1e3) } : {},
144604
+ ...evidence.capturedHash ? { capturedHash: compactText(evidence.capturedHash, "evidence hash", 1e3) } : {}
144605
+ };
144606
+ });
144607
+ }
144608
+ function assertScope(scope) {
144609
+ if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
144610
+ }
144611
+ function assertKind(kind) {
144612
+ if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
144613
+ }
144614
+ function assertPortability(value) {
144615
+ if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
144616
+ throw new Error("Long-term memory portability is invalid.");
144617
+ }
144618
+ }
144619
+ function assertPortableEvidence(scope, portability, evidence) {
144620
+ if (portability !== "portable") return;
144621
+ if (scope !== "user") {
144622
+ throw new Error("Only user-scoped long-term memories may be portable across projects.");
144623
+ }
144624
+ if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
144625
+ 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.");
144626
+ }
144627
+ }
144628
+ function requireTeamReader(scope, reader) {
144629
+ if (scope === "team" && reader?.isTeamMember !== true) {
144630
+ throw new Error("Team long-term memory requires an explicit active team identity for this project.");
144631
+ }
144632
+ }
144633
+ function matchesScope(memory, reader) {
144634
+ if (memory.scope === "project") return memory.originProjectId === reader.projectId;
144635
+ if (memory.scope === "team") {
144636
+ return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
144637
+ }
144638
+ if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
144639
+ return memory.portability === "portable" || memory.originProjectId === reader.projectId;
144640
+ }
144641
+ function canReadLongTermMemory(memory, reader) {
144642
+ return matchesScope(memory, reader);
144643
+ }
144644
+ function isEligibleLongTermMemory(memory) {
144645
+ return memory.state === "qualified" || memory.state === "approved";
144646
+ }
144647
+ function taskTerms(task) {
144648
+ const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
144649
+ return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
144650
+ }
144651
+ function kindFitScore(kind, task) {
144652
+ const normalized = task.toLocaleLowerCase("en-US");
144653
+ const hints = {
144654
+ episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
144655
+ semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
144656
+ procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
144657
+ };
144658
+ return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
144659
+ }
144660
+ function validationFreshnessScore(memory) {
144661
+ const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
144662
+ if (!validatedAt) return 0;
144663
+ const time = Date.parse(validatedAt);
144664
+ if (!Number.isFinite(time)) return 0;
144665
+ const ageDays = Math.max(0, Date.now() - time) / (24 * 60 * 60 * 1e3);
144666
+ if (ageDays <= 30) return 0.35;
144667
+ if (ageDays <= 90) return 0.15;
144668
+ return 0;
144669
+ }
144670
+ function scoreForTask(memory, task) {
144671
+ const terms = taskTerms(task);
144672
+ if (terms.length === 0) return void 0;
144673
+ const title = memory.title.toLocaleLowerCase("en-US");
144674
+ const content = memory.content.toLocaleLowerCase("en-US");
144675
+ const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
144676
+ const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
144677
+ const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
144678
+ let score = memory.state === "approved" ? 0.5 : 0;
144679
+ const matched = [];
144680
+ for (const term of terms) {
144681
+ let termScore = 0;
144682
+ if (title.includes(term)) termScore += 5;
144683
+ if (tags.includes(term)) termScore += 4;
144684
+ if (applicability.includes(term)) termScore += 3;
144685
+ if (facts.includes(term)) termScore += 2;
144686
+ if (content.includes(term)) termScore += 1;
144687
+ if (termScore > 0) {
144688
+ score += termScore;
144689
+ matched.push(term);
144690
+ }
144691
+ }
144692
+ if (matched.length === 0) return void 0;
144693
+ score += kindFitScore(memory.kind, task);
144694
+ score += validationFreshnessScore(memory);
144695
+ const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
144696
+ const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
144697
+ return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
144698
+ }
144699
+ function semanticText(memory) {
144700
+ return sanitizeCredentials([
144701
+ memory.title,
144702
+ memory.applicability,
144703
+ memory.tags.join(" "),
144704
+ memory.facts.join(" "),
144705
+ memory.content
144706
+ ].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
144707
+ }
144708
+ function cosineSimilarity(left, right) {
144709
+ if (left.length === 0 || left.length !== right.length) return 0;
144710
+ let dot = 0;
144711
+ let leftMagnitude = 0;
144712
+ let rightMagnitude = 0;
144713
+ for (let index = 0; index < left.length; index += 1) {
144714
+ const a = left[index];
144715
+ const b = right[index];
144716
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return 0;
144717
+ dot += a * b;
144718
+ leftMagnitude += a * a;
144719
+ rightMagnitude += b * b;
144720
+ }
144721
+ if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
144722
+ return dot / Math.sqrt(leftMagnitude * rightMagnitude);
144723
+ }
144724
+ function semanticSelection(memory, similarity, task) {
144725
+ if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
144726
+ const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
144727
+ const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
144728
+ return {
144729
+ score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
144730
+ reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
144731
+ };
144732
+ }
144733
+ async function resolveSemanticProvider(explicit) {
144734
+ if (explicit !== void 0) return explicit;
144735
+ try {
144736
+ const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
144737
+ return await withTimeout(
144738
+ getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
144739
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS,
144740
+ "Long-term semantic provider initialization"
144741
+ );
144742
+ } catch {
144743
+ return null;
144744
+ }
144745
+ }
144746
+ function createStore(dataDir) {
144747
+ const store = new LongTermMemoryStore();
144748
+ return store.init(dataDir).then(() => store);
144749
+ }
144750
+ async function createManualLongTermMemory(input) {
144751
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
144752
+ if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
144753
+ assertScope(input.scope);
144754
+ const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID8(), relation: "supports" }];
144755
+ return createLongTermMemoryWithDataDir({
144756
+ dataDir: input.dataDir,
144757
+ originProjectId: input.projectId,
144758
+ ownerId: owner.id,
144759
+ scope: input.scope,
144760
+ kind: input.kind,
144761
+ portability: input.portability,
144762
+ title: input.title,
144763
+ content: input.content,
144764
+ facts: input.facts,
144765
+ tags: input.tags,
144766
+ applicability: input.applicability,
144767
+ origin: "manual",
144768
+ evidence,
144769
+ reader: input.reader
144770
+ });
144771
+ }
144772
+ async function promoteObservationToLongTermMemory(input) {
144773
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
144774
+ if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
144775
+ return createLongTermMemoryWithDataDir({
144776
+ dataDir: input.dataDir,
144777
+ originProjectId: input.observation.projectId,
144778
+ ownerId: owner.id,
144779
+ scope: input.scope,
144780
+ kind: input.kind,
144781
+ portability: "project-bound",
144782
+ title: input.observation.title,
144783
+ content: input.observation.narrative,
144784
+ facts: input.observation.facts,
144785
+ tags: input.tags ?? input.observation.concepts,
144786
+ applicability: input.applicability,
144787
+ origin: input.observation.source === "git" ? "git" : "agent",
144788
+ evidence: [{
144789
+ kind: "observation",
144790
+ referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
144791
+ relation: "derives"
144792
+ }],
144793
+ reader: input.reader
144794
+ });
144795
+ }
144796
+ async function createLongTermMemoryCandidate(input) {
144797
+ return createLongTermMemoryWithDataDir(input);
144798
+ }
144799
+ async function createLongTermMemoryWithDataDir(input) {
144800
+ assertScope(input.scope);
144801
+ assertKind(input.kind);
144802
+ const portability = input.portability ?? "project-bound";
144803
+ assertPortability(portability);
144804
+ requireTeamReader(input.scope, input.reader);
144805
+ const evidenceInputs = normalizeEvidence(input.evidence);
144806
+ assertPortableEvidence(input.scope, portability, evidenceInputs);
144807
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
144808
+ const memory = {
144809
+ id: randomUUID8(),
144810
+ originProjectId: compactText(input.originProjectId, "origin project id", 1e3),
144811
+ ownerId: compactText(input.ownerId, "owner id", 160),
144812
+ scope: input.scope,
144813
+ kind: input.kind,
144814
+ state: "candidate",
144815
+ portability,
144816
+ title: compactText(input.title, "title", 500),
144817
+ content: compactText(input.content, "content"),
144818
+ facts: compactList(input.facts, "facts"),
144819
+ tags: compactList(input.tags, "tags"),
144820
+ ...input.applicability ? { applicability: compactText(input.applicability, "applicability", 1e3) } : {},
144821
+ origin: input.origin ?? "manual",
144822
+ createdAt: now3,
144823
+ updatedAt: now3,
144824
+ accessCount: 0
144825
+ };
144826
+ const store = await createStore(input.dataDir);
144827
+ const evidence = store.transaction(() => {
144828
+ store.insert(memory);
144829
+ const written = evidenceInputs.map((item) => store.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
144830
+ store.recordEvent({
144831
+ memoryId: memory.id,
144832
+ kind: "created",
144833
+ toState: "candidate",
144834
+ detail: "Created as a source-backed long-term memory candidate.",
144835
+ createdAt: now3
144836
+ });
144837
+ return written;
144838
+ });
144839
+ return { memory, evidence };
144840
+ }
144841
+ function transitionDetail(value) {
144842
+ return compactText(value, "transition reason", 1e3);
144843
+ }
144844
+ async function transitionMemory(input) {
144845
+ const store = await createStore(input.dataDir);
144846
+ return store.transaction(() => {
144847
+ const current = store.get(input.id);
144848
+ if (!current) throw new Error("Long-term memory was not found.");
144849
+ if (current.state !== input.expected) {
144850
+ throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
144851
+ }
144852
+ const evidence = store.listEvidence(current.id);
144853
+ if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
144854
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
144855
+ const next = {
144856
+ ...current,
144857
+ state: input.next,
144858
+ updatedAt: now3,
144859
+ lastValidatedAt: now3,
144860
+ ...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
144861
+ };
144862
+ store.update(next);
144863
+ store.recordEvent({
144864
+ memoryId: next.id,
144865
+ kind: input.event,
144866
+ fromState: current.state,
144867
+ toState: next.state,
144868
+ detail: transitionDetail(input.reason),
144869
+ createdAt: now3
144870
+ });
144871
+ return next;
144872
+ });
144873
+ }
144874
+ async function qualifyLongTermMemory(input) {
144875
+ return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
144876
+ }
144877
+ async function approveLongTermMemory(input) {
144878
+ return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
144879
+ }
144880
+ async function archiveLongTermMemory(input) {
144881
+ const store = await createStore(input.dataDir);
144882
+ return store.transaction(() => {
144883
+ const current = store.get(input.id);
144884
+ if (!current) throw new Error("Long-term memory was not found.");
144885
+ if (current.state === "archived" || current.state === "superseded") {
144886
+ throw new Error("Long-term memory is already inactive.");
144887
+ }
144888
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
144889
+ const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
144890
+ store.update(next);
144891
+ store.recordEvent({
144892
+ memoryId: next.id,
144893
+ kind: "archived",
144894
+ fromState: current.state,
144895
+ toState: "archived",
144896
+ detail: transitionDetail(input.reason),
144897
+ createdAt: now3
144898
+ });
144899
+ return next;
144900
+ });
144901
+ }
144902
+ async function supersedeLongTermMemory(input) {
144903
+ const store = await createStore(input.dataDir);
144904
+ return store.transaction(() => {
144905
+ const current = store.get(input.id);
144906
+ if (!current) throw new Error("Long-term memory was not found.");
144907
+ if (current.state === "archived" || current.state === "superseded") {
144908
+ throw new Error("Long-term memory is already inactive.");
144909
+ }
144910
+ if (current.id === input.supersededBy) {
144911
+ throw new Error("Long-term memory cannot supersede itself.");
144912
+ }
144913
+ const replacement = store.get(input.supersededBy);
144914
+ if (!replacement || !isEligibleLongTermMemory(replacement)) {
144915
+ throw new Error("Replacement long-term memory must be qualified or approved.");
144916
+ }
144917
+ if (replacement.scope !== current.scope) {
144918
+ throw new Error("Replacement long-term memory must use the same scope.");
144919
+ }
144920
+ if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
144921
+ throw new Error("User long-term memory may only be superseded by the same local owner.");
144922
+ }
144923
+ if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
144924
+ throw new Error("Project and team long-term memory may only be superseded within the same project.");
144925
+ }
144926
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
144927
+ const next = {
144928
+ ...current,
144929
+ state: "superseded",
144930
+ supersededBy: replacement.id,
144931
+ updatedAt: now3
144932
+ };
144933
+ store.update(next);
144934
+ store.recordEvent({
144935
+ memoryId: next.id,
144936
+ kind: "superseded",
144937
+ fromState: current.state,
144938
+ toState: "superseded",
144939
+ detail: transitionDetail(input.reason),
144940
+ createdAt: now3
144941
+ });
144942
+ return next;
144943
+ });
144944
+ }
144945
+ async function maybeAutoQualifyLongTermMemory(input) {
144946
+ if (input.source !== "manual" && input.sourceDetail !== "explicit") return null;
144947
+ try {
144948
+ return await qualifyLongTermMemory({
144949
+ dataDir: input.dataDir,
144950
+ id: input.id,
144951
+ reason: "auto-qualified: explicit request carries its own source evidence"
144952
+ });
144953
+ } catch {
144954
+ return null;
144955
+ }
144956
+ }
144957
+ async function maintainLongTermMemories(input) {
144958
+ const now3 = input.now ?? /* @__PURE__ */ new Date();
144959
+ const decayDays = input.decayDays ?? 30;
144960
+ const cutoff = now3.getTime() - decayDays * 24 * 60 * 60 * 1e3;
144961
+ const result = { archived: 0, superseded: 0 };
144962
+ const store = await createStore(input.dataDir);
144963
+ const all = store.list({ originProjectId: input.projectId });
144964
+ for (const memory of all) {
144965
+ if (memory.state !== "qualified" && memory.state !== "approved") continue;
144966
+ const lastActivity = Date.parse(memory.lastAccessedAt ?? memory.updatedAt);
144967
+ if (!Number.isFinite(lastActivity) || lastActivity >= cutoff) continue;
144968
+ try {
144969
+ await archiveLongTermMemory({
144970
+ dataDir: input.dataDir,
144971
+ id: memory.id,
144972
+ reason: "auto-archive: no activity since " + new Date(lastActivity).toISOString().slice(0, 10)
144973
+ });
144974
+ result.archived += 1;
144975
+ } catch {
144976
+ }
144977
+ }
144978
+ const groups = /* @__PURE__ */ new Map();
144979
+ for (const memory of store.list({ originProjectId: input.projectId })) {
144980
+ if (memory.state !== "qualified" && memory.state !== "approved") continue;
144981
+ const key = memory.scope + "\0" + memory.kind + "\0" + memory.title.trim().toLowerCase();
144982
+ const group = groups.get(key) ?? [];
144983
+ group.push(memory);
144984
+ groups.set(key, group);
144985
+ }
144986
+ for (const group of groups.values()) {
144987
+ if (group.length < 2) continue;
144988
+ group.sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt));
144989
+ const newest = group[0];
144990
+ for (const older of group.slice(1)) {
144991
+ try {
144992
+ await supersedeLongTermMemory({
144993
+ dataDir: input.dataDir,
144994
+ id: older.id,
144995
+ supersededBy: newest.id,
144996
+ reason: "auto-supersede: a newer record shares the same title"
144997
+ });
144998
+ result.superseded += 1;
144999
+ } catch {
145000
+ }
145001
+ }
145002
+ }
145003
+ return result;
145004
+ }
145005
+ async function listLongTermMemories(input) {
145006
+ const store = await createStore(input.dataDir);
145007
+ 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) }));
145008
+ }
145009
+ async function getLongTermMemoryDetail(input) {
145010
+ const store = await createStore(input.dataDir);
145011
+ const memory = store.get(input.id);
145012
+ if (!memory || !canReadLongTermMemory(memory, input.reader)) {
145013
+ throw new Error("Long-term memory was not found in the active scope.");
145014
+ }
145015
+ store.markAccess([memory.id]);
145016
+ return { memory, evidence: store.listEvidence(memory.id), events: store.listEvents(memory.id) };
145017
+ }
145018
+ async function selectLongTermMemoriesForTask(input) {
145019
+ const task = input.task?.trim();
145020
+ if (!task) return [];
145021
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
145022
+ const reader = {
145023
+ projectId: input.projectId,
145024
+ ...owner ? { ownerId: owner.id } : {},
145025
+ ...input.agentId ? { agentId: input.agentId } : {},
145026
+ ...input.isTeamMember ? { isTeamMember: true } : {}
145027
+ };
145028
+ const store = await createStore(input.dataDir);
145029
+ const eligible = store.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
145030
+ let selected = eligible.map((memory) => {
145031
+ const ranked = scoreForTask(memory, task);
145032
+ return ranked ? {
145033
+ memory,
145034
+ evidence: store.listEvidence(memory.id),
145035
+ score: ranked.score,
145036
+ reason: ranked.reason
145037
+ } : void 0;
145038
+ }).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));
145039
+ if (selected.length === 0 && eligible.length > 0) {
145040
+ const provider2 = await resolveSemanticProvider(input.embeddingProvider);
145041
+ const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
145042
+ if (provider2 && candidates.length > 0) {
145043
+ try {
145044
+ const vectors = await withTimeout(
145045
+ provider2.embedBatch([task, ...candidates.map(semanticText)], {
145046
+ timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
145047
+ retry: false
145048
+ }),
145049
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS,
145050
+ "Long-term semantic retrieval"
145051
+ );
145052
+ const query = vectors[0];
145053
+ if (Array.isArray(query)) {
145054
+ selected = candidates.map((memory, index) => {
145055
+ const vector = vectors[index + 1];
145056
+ const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
145057
+ return ranked ? {
145058
+ memory,
145059
+ evidence: store.listEvidence(memory.id),
145060
+ score: ranked.score,
145061
+ reason: ranked.reason
145062
+ } : void 0;
145063
+ }).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));
145064
+ }
145065
+ } catch {
145066
+ }
145067
+ }
145068
+ }
145069
+ selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
145070
+ store.markAccess(selected.map((item) => item.memory.id));
145071
+ return selected;
145072
+ }
145073
+ function renderLongTermMemorySummary(memory, maxTokens = 22) {
145074
+ const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
145075
+ const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
145076
+ return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
145077
+ }
145078
+ var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
145079
+ var init_long_term = __esm({
145080
+ "src/memory/long-term.ts"() {
145081
+ "use strict";
145082
+ init_esm_shims();
145083
+ init_token_budget();
145084
+ init_timeout();
145085
+ init_secret_filter();
145086
+ init_owner();
145087
+ init_long_term_store();
145088
+ init_long_term_types();
145089
+ MAX_TEXT_LENGTH = 12e3;
145090
+ MAX_LIST_ITEMS = 40;
145091
+ MAX_SEMANTIC_CANDIDATES = 48;
145092
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
145093
+ MIN_SEMANTIC_SIMILARITY = 0.58;
145094
+ PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
145095
+ STOP_TERMS = /* @__PURE__ */ new Set([
145096
+ "the",
145097
+ "and",
145098
+ "for",
145099
+ "with",
145100
+ "from",
145101
+ "that",
145102
+ "this",
145103
+ "into",
145104
+ "when",
145105
+ "what",
145106
+ "\u5982\u4F55",
145107
+ "\u4EC0\u4E48",
145108
+ "\u8FD9\u4E2A",
145109
+ "\u90A3\u4E2A",
145110
+ "\u6211\u4EEC",
145111
+ "\u9879\u76EE",
145112
+ "\u8FDB\u884C",
145113
+ "\u9700\u8981",
145114
+ "\u7EE7\u7EED",
145115
+ "\u5904\u7406"
145116
+ ]);
145117
+ }
145118
+ });
145119
+
144223
145120
  // src/codegraph/ids.ts
144224
145121
  import { createHash as createHash5 } from "crypto";
144225
145122
  function digest(input) {
@@ -144258,7 +145155,7 @@ var store_exports = {};
144258
145155
  __export(store_exports, {
144259
145156
  CodeGraphStore: () => CodeGraphStore
144260
145157
  });
144261
- import { randomUUID as randomUUID6 } from "crypto";
145158
+ import { randomUUID as randomUUID9 } from "crypto";
144262
145159
  import { posix as pathPosix } from "path";
144263
145160
  function rowToFile(row) {
144264
145161
  return {
@@ -144975,7 +145872,7 @@ var init_store = __esm({
144975
145872
  * so an interrupted scan cannot be advertised as complete.
144976
145873
  */
144977
145874
  recordCodeStateSnapshot(input) {
144978
- const id = randomUUID6();
145875
+ const id = randomUUID9();
144979
145876
  let snapshot;
144980
145877
  const tx = this.db.transaction(() => {
144981
145878
  const previous = this.db.prepare(
@@ -145753,8 +146650,8 @@ var claim_store_exports = {};
145753
146650
  __export(claim_store_exports, {
145754
146651
  ClaimStore: () => ClaimStore
145755
146652
  });
145756
- import { createHash as createHash8, randomUUID as randomUUID7 } from "crypto";
145757
- function optionalText2(value) {
146653
+ import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
146654
+ function optionalText3(value) {
145758
146655
  return typeof value === "string" && value ? value : void 0;
145759
146656
  }
145760
146657
  function rowToClaim(row) {
@@ -145770,38 +146667,38 @@ function rowToClaim(row) {
145770
146667
  status: row.status,
145771
146668
  confidence: Number(row.confidence),
145772
146669
  observedAt: row.observedAt,
145773
- ...optionalText2(row.validFrom) ? { validFrom: row.validFrom } : {},
145774
- ...optionalText2(row.validTo) ? { validTo: row.validTo } : {},
145775
- ...optionalText2(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
146670
+ ...optionalText3(row.validFrom) ? { validFrom: row.validFrom } : {},
146671
+ ...optionalText3(row.validTo) ? { validTo: row.validTo } : {},
146672
+ ...optionalText3(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
145776
146673
  reviewState: row.reviewState,
145777
146674
  origin: row.origin,
145778
146675
  createdAt: row.createdAt,
145779
146676
  updatedAt: row.updatedAt
145780
146677
  };
145781
146678
  }
145782
- function rowToEvidence(row) {
146679
+ function rowToEvidence2(row) {
145783
146680
  return {
145784
146681
  id: row.id,
145785
146682
  claimId: row.claimId,
145786
146683
  evidenceKind: row.evidenceKind,
145787
146684
  evidenceId: row.evidenceId,
145788
146685
  relation: row.relation,
145789
- ...optionalText2(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
145790
- ...optionalText2(row.locator) ? { locator: row.locator } : {},
145791
- ...optionalText2(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
146686
+ ...optionalText3(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
146687
+ ...optionalText3(row.locator) ? { locator: row.locator } : {},
146688
+ ...optionalText3(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
145792
146689
  createdAt: row.createdAt
145793
146690
  };
145794
146691
  }
145795
- function rowToEvent(row) {
146692
+ function rowToEvent2(row) {
145796
146693
  return {
145797
146694
  id: row.id,
145798
146695
  projectId: row.projectId,
145799
146696
  claimId: row.claimId,
145800
146697
  kind: row.kind,
145801
- ...optionalText2(row.fromStatus) ? { fromStatus: row.fromStatus } : {},
145802
- ...optionalText2(row.toStatus) ? { toStatus: row.toStatus } : {},
145803
- ...optionalText2(row.relatedClaimId) ? { relatedClaimId: row.relatedClaimId } : {},
145804
- ...optionalText2(row.detail) ? { detail: row.detail } : {},
146698
+ ...optionalText3(row.fromStatus) ? { fromStatus: row.fromStatus } : {},
146699
+ ...optionalText3(row.toStatus) ? { toStatus: row.toStatus } : {},
146700
+ ...optionalText3(row.relatedClaimId) ? { relatedClaimId: row.relatedClaimId } : {},
146701
+ ...optionalText3(row.detail) ? { detail: row.detail } : {},
145805
146702
  createdAt: row.createdAt
145806
146703
  };
145807
146704
  }
@@ -145937,7 +146834,7 @@ var init_claim_store = __esm({
145937
146834
  locator, capturedHash, evidenceKey, createdAt
145938
146835
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
145939
146836
  `).run(
145940
- randomUUID7(),
146837
+ randomUUID10(),
145941
146838
  input.claimId,
145942
146839
  input.evidenceKind,
145943
146840
  input.evidenceId,
@@ -145955,11 +146852,11 @@ var init_claim_store = __esm({
145955
146852
  SELECT * FROM knowledge_claim_evidence
145956
146853
  WHERE claimId = ?
145957
146854
  ORDER BY createdAt ASC, id
145958
- `).all(claimId).map(rowToEvidence);
146855
+ `).all(claimId).map(rowToEvidence2);
145959
146856
  }
145960
146857
  recordEvent(input) {
145961
146858
  const event = {
145962
- id: randomUUID7(),
146859
+ id: randomUUID10(),
145963
146860
  ...input,
145964
146861
  ...input.detail ? { detail: sanitizeCredentials(input.detail) } : {},
145965
146862
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -145987,7 +146884,7 @@ var init_claim_store = __esm({
145987
146884
  SELECT * FROM knowledge_claim_events
145988
146885
  WHERE claimId = ?
145989
146886
  ORDER BY createdAt ASC, id
145990
- `).all(claimId).map(rowToEvent);
146887
+ `).all(claimId).map(rowToEvent2);
145991
146888
  }
145992
146889
  };
145993
146890
  }
@@ -146004,8 +146901,8 @@ __export(claims_exports, {
146004
146901
  supersedeClaim: () => supersedeClaim,
146005
146902
  writeClaim: () => writeClaim
146006
146903
  });
146007
- import { createHash as createHash9, randomUUID as randomUUID8 } from "crypto";
146008
- function compactText(value, field) {
146904
+ import { createHash as createHash9, randomUUID as randomUUID11 } from "crypto";
146905
+ function compactText2(value, field) {
146009
146906
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
146010
146907
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
146011
146908
  if (!sanitized) throw new Error("Claim " + field + " is required");
@@ -146013,7 +146910,7 @@ function compactText(value, field) {
146013
146910
  return sanitized;
146014
146911
  }
146015
146912
  function normalizedTerm(value) {
146016
- return compactText(value, "identity").toLocaleLowerCase("en-US");
146913
+ return compactText2(value, "identity").toLocaleLowerCase("en-US");
146017
146914
  }
146018
146915
  function hashIdentity(parts) {
146019
146916
  return createHash9("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
@@ -146042,7 +146939,7 @@ function validateEvidence(input) {
146042
146939
  if (!evidence || typeof evidence !== "object") {
146043
146940
  throw new Error("Claim evidence must be an object");
146044
146941
  }
146045
- const evidenceId = compactText(evidence.evidenceId, "evidence id");
146942
+ const evidenceId = compactText2(evidence.evidenceId, "evidence id");
146046
146943
  if (evidenceId.length > MAX_EVIDENCE_ID_LENGTH) {
146047
146944
  throw new Error("Claim evidence id is too long");
146048
146945
  }
@@ -146056,9 +146953,9 @@ function validateEvidence(input) {
146056
146953
  evidenceKind: evidence.evidenceKind,
146057
146954
  evidenceId,
146058
146955
  relation: evidence.relation,
146059
- ...evidence.snapshotId ? { snapshotId: compactText(evidence.snapshotId, "evidence snapshot id") } : {},
146060
- ...evidence.locator ? { locator: compactText(evidence.locator, "evidence locator") } : {},
146061
- ...evidence.capturedHash ? { capturedHash: compactText(evidence.capturedHash, "evidence hash") } : {}
146956
+ ...evidence.snapshotId ? { snapshotId: compactText2(evidence.snapshotId, "evidence snapshot id") } : {},
146957
+ ...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator") } : {},
146958
+ ...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash") } : {}
146062
146959
  };
146063
146960
  });
146064
146961
  }
@@ -146066,10 +146963,10 @@ function normalizeClaimInput(input) {
146066
146963
  if (!["project", "workspace", "team", "workflow", "task"].includes(input.scope)) {
146067
146964
  throw new Error("Claim scope is invalid");
146068
146965
  }
146069
- const projectId = compactText(input.projectId, "project id");
146070
- const subject = compactText(input.subject, "subject");
146071
- const predicate = compactText(input.predicate, "predicate");
146072
- const objectValue = compactText(input.objectValue, "object value");
146966
+ const projectId = compactText2(input.projectId, "project id");
146967
+ const subject = compactText2(input.subject, "subject");
146968
+ const predicate = compactText2(input.predicate, "predicate");
146969
+ const objectValue = compactText2(input.objectValue, "object value");
146073
146970
  const identity = buildClaimIdentity({ subject, predicate, objectValue, scope: input.scope });
146074
146971
  const origin = input.origin ?? "explicit";
146075
146972
  let reviewState = input.reviewState ?? "approved";
@@ -146090,7 +146987,7 @@ function normalizeClaimInput(input) {
146090
146987
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
146091
146988
  return {
146092
146989
  claim: {
146093
- id: randomUUID8(),
146990
+ id: randomUUID11(),
146094
146991
  projectId,
146095
146992
  subject,
146096
146993
  predicate,
@@ -146190,7 +147087,7 @@ function reviewClaim(store, input) {
146190
147087
  if (input.reviewState !== "approved" && input.reviewState !== "rejected") {
146191
147088
  throw new Error("A claim review must approve or reject the claim");
146192
147089
  }
146193
- const detail = compactText(input.detail, "review detail");
147090
+ const detail = compactText2(input.detail, "review detail");
146194
147091
  return store.transaction(() => {
146195
147092
  const claim = store.getClaim(input.claimId);
146196
147093
  if (!claim) throw new Error("Claim was not found");
@@ -146502,8 +147399,8 @@ var init_frontmatter = __esm({
146502
147399
 
146503
147400
  // src/knowledge/markdown.ts
146504
147401
  import { createHash as createHash10 } from "crypto";
146505
- import { promises as fs12 } from "fs";
146506
- import path19 from "path";
147402
+ import { promises as fs13 } from "fs";
147403
+ import path20 from "path";
146507
147404
  function hashContent(content) {
146508
147405
  return createHash10("sha256").update(content).digest("hex");
146509
147406
  }
@@ -146561,8 +147458,8 @@ function extractInternalMarkdownLinks(body) {
146561
147458
  return [...links].sort();
146562
147459
  }
146563
147460
  function resolvePageLink(sourceRelativePath, target) {
146564
- const sourceDir = path19.posix.dirname(sourceRelativePath.replace(/\\/g, "/"));
146565
- const normalized = path19.posix.normalize(path19.posix.join(sourceDir, target.replace(/\\/g, "/")));
147461
+ const sourceDir = path20.posix.dirname(sourceRelativePath.replace(/\\/g, "/"));
147462
+ const normalized = path20.posix.normalize(path20.posix.join(sourceDir, target.replace(/\\/g, "/")));
146566
147463
  if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../")) return void 0;
146567
147464
  return normalized;
146568
147465
  }
@@ -146584,13 +147481,13 @@ function renderKnowledgePage(frontmatter, body) {
146584
147481
  });
146585
147482
  }
146586
147483
  async function readKnowledgePage(absolutePath, workspaceRoot) {
146587
- const raw = await fs12.readFile(absolutePath, "utf8");
147484
+ const raw = await fs13.readFile(absolutePath, "utf8");
146588
147485
  try {
146589
147486
  const parsed = frontmatter_default(raw);
146590
147487
  const frontmatter = validateKnowledgePageFrontmatter(parsed.data);
146591
147488
  return {
146592
147489
  absolutePath,
146593
- relativePath: workspaceRoot ? path19.relative(workspaceRoot, absolutePath).split(path19.sep).join("/") : path19.basename(absolutePath),
147490
+ relativePath: workspaceRoot ? path20.relative(workspaceRoot, absolutePath).split(path20.sep).join("/") : path20.basename(absolutePath),
146594
147491
  frontmatter,
146595
147492
  body: parsed.content.trim(),
146596
147493
  contentHash: hashContent(raw),
@@ -146602,7 +147499,7 @@ async function readKnowledgePage(absolutePath, workspaceRoot) {
146602
147499
  }
146603
147500
  }
146604
147501
  async function writeKnowledgePage(absolutePath, content) {
146605
- await fs12.mkdir(path19.dirname(absolutePath), { recursive: true });
147502
+ await fs13.mkdir(path20.dirname(absolutePath), { recursive: true });
146606
147503
  await atomicWriteFile(absolutePath, content);
146607
147504
  return hashContent(content);
146608
147505
  }
@@ -146632,8 +147529,8 @@ __export(wiki_exports, {
146632
147529
  readKnowledgePage: () => readKnowledgePage
146633
147530
  });
146634
147531
  import { createHash as createHash11 } from "crypto";
146635
- import { promises as fs13 } from "fs";
146636
- import path20 from "path";
147532
+ import { promises as fs14 } from "fs";
147533
+ import path21 from "path";
146637
147534
  function hash2(value) {
146638
147535
  return createHash11("sha256").update(value).digest("hex");
146639
147536
  }
@@ -146765,7 +147662,7 @@ function candidatePage(workspace, title, claims, store) {
146765
147662
  }
146766
147663
  async function fileHashIfPresent(filePath) {
146767
147664
  try {
146768
- return pageContentHash(await fs13.readFile(filePath, "utf8"));
147665
+ return pageContentHash(await fs14.readFile(filePath, "utf8"));
146769
147666
  } catch (error) {
146770
147667
  const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
146771
147668
  if (code === "ENOENT") return void 0;
@@ -146789,8 +147686,8 @@ async function writeIndex(workspace, store) {
146789
147686
  lines.push("No pending proposals.");
146790
147687
  } else {
146791
147688
  for (const proposal of pending) {
146792
- const relative3 = path20.relative(paths.root, proposal.proposalPath).split(path20.sep).join("/");
146793
- lines.push("- [" + path20.basename(proposal.proposalPath, ".md") + "](" + relative3 + ")");
147689
+ const relative3 = path21.relative(paths.root, proposal.proposalPath).split(path21.sep).join("/");
147690
+ lines.push("- [" + path21.basename(proposal.proposalPath, ".md") + "](" + relative3 + ")");
146794
147691
  }
146795
147692
  }
146796
147693
  lines.push("");
@@ -146798,7 +147695,7 @@ async function writeIndex(workspace, store) {
146798
147695
  }
146799
147696
  async function appendLog(workspace, line) {
146800
147697
  const logPath = getKnowledgeWorkspacePaths(workspace).log;
146801
- await fs13.appendFile(logPath, "- " + now() + " " + line + "\n", "utf8");
147698
+ await fs14.appendFile(logPath, "- " + now() + " " + line + "\n", "utf8");
146802
147699
  }
146803
147700
  function toPageRecord(workspace, page, status, contentHash, manualContentHash) {
146804
147701
  return {
@@ -146883,9 +147780,9 @@ async function applyKnowledgeProposal(input) {
146883
147780
  }
146884
147781
  if (proposal.status !== "pending") throw new Error("Knowledge proposal is not pending review");
146885
147782
  const root = getKnowledgeWorkspacePaths(input.workspace).root;
146886
- const targetPath = path20.resolve(proposal.targetPath);
146887
- const proposalPath = path20.resolve(proposal.proposalPath);
146888
- if (!targetPath.startsWith(root + path20.sep) || !proposalPath.startsWith(root + path20.sep)) {
147783
+ const targetPath = path21.resolve(proposal.targetPath);
147784
+ const proposalPath = path21.resolve(proposal.proposalPath);
147785
+ if (!targetPath.startsWith(root + path21.sep) || !proposalPath.startsWith(root + path21.sep)) {
146889
147786
  throw new Error("Knowledge proposal path escapes its workspace");
146890
147787
  }
146891
147788
  await withFileLock(root, async () => {
@@ -146923,12 +147820,12 @@ async function markdownFiles(directory) {
146923
147820
  const files = [];
146924
147821
  let entries;
146925
147822
  try {
146926
- entries = await fs13.readdir(directory, { withFileTypes: true });
147823
+ entries = await fs14.readdir(directory, { withFileTypes: true });
146927
147824
  } catch {
146928
147825
  return files;
146929
147826
  }
146930
147827
  for (const entry of entries) {
146931
- const filePath = path20.join(directory, entry.name);
147828
+ const filePath = path21.join(directory, entry.name);
146932
147829
  if (entry.isDirectory()) {
146933
147830
  files.push(...await markdownFiles(filePath));
146934
147831
  } else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
@@ -146951,13 +147848,13 @@ async function lintKnowledgeWorkspace(input) {
146951
147848
  kind: "malformed-frontmatter",
146952
147849
  severity: "error",
146953
147850
  message: error instanceof Error ? error.message : "Malformed knowledge page.",
146954
- relativePath: path20.relative(paths.root, filePath).split(path20.sep).join("/")
147851
+ relativePath: path21.relative(paths.root, filePath).split(path21.sep).join("/")
146955
147852
  });
146956
147853
  }
146957
147854
  }
146958
147855
  const indexed = /* @__PURE__ */ new Set();
146959
147856
  try {
146960
- const index = await fs13.readFile(paths.index, "utf8");
147857
+ const index = await fs14.readFile(paths.index, "utf8");
146961
147858
  for (const link of extractInternalMarkdownLinks(index)) {
146962
147859
  const resolved = resolvePageLink("index.md", link);
146963
147860
  if (resolved) indexed.add(resolved);
@@ -147046,7 +147943,7 @@ async function lintKnowledgeWorkspace(input) {
147046
147943
  kind: "manual-edit-protected",
147047
147944
  severity: "warning",
147048
147945
  message: "A newer proposal was held because the target page has manual edits.",
147049
- relativePath: path20.relative(paths.root, proposal.targetPath).split(path20.sep).join("/")
147946
+ relativePath: path21.relative(paths.root, proposal.targetPath).split(path21.sep).join("/")
147050
147947
  });
147051
147948
  }
147052
147949
  }
@@ -147179,7 +148076,7 @@ var workflow_store_exports = {};
147179
148076
  __export(workflow_store_exports, {
147180
148077
  WorkflowStore: () => WorkflowStore
147181
148078
  });
147182
- import { randomUUID as randomUUID9 } from "crypto";
148079
+ import { randomUUID as randomUUID12 } from "crypto";
147183
148080
  function parseJson(value, fallback) {
147184
148081
  if (typeof value !== "string" || !value) return fallback;
147185
148082
  try {
@@ -147188,7 +148085,7 @@ function parseJson(value, fallback) {
147188
148085
  return fallback;
147189
148086
  }
147190
148087
  }
147191
- function optionalText3(value) {
148088
+ function optionalText4(value) {
147192
148089
  return typeof value === "string" && value.trim() ? value : void 0;
147193
148090
  }
147194
148091
  function rowToWorkflow(row) {
@@ -147225,14 +148122,14 @@ function rowToRun(row) {
147225
148122
  workflowId: row.workflowId,
147226
148123
  projectId: row.projectId,
147227
148124
  task: row.task,
147228
- ...optionalText3(row.startingSnapshotId) ? { startingSnapshotId: row.startingSnapshotId } : {},
148125
+ ...optionalText4(row.startingSnapshotId) ? { startingSnapshotId: row.startingSnapshotId } : {},
147229
148126
  selectedEvidence: parseJson(row.selectedEvidenceJson, []),
147230
148127
  phaseState: parseJson(row.phaseStateJson, {}),
147231
148128
  outcome: row.outcome,
147232
148129
  verificationVerdict: row.verificationVerdict,
147233
- ...optionalText3(row.failureReason) ? { failureReason: row.failureReason } : {},
148130
+ ...optionalText4(row.failureReason) ? { failureReason: row.failureReason } : {},
147234
148131
  startedAt: row.startedAt,
147235
- ...optionalText3(row.completedAt) ? { completedAt: row.completedAt } : {}
148132
+ ...optionalText4(row.completedAt) ? { completedAt: row.completedAt } : {}
147236
148133
  };
147237
148134
  }
147238
148135
  var WorkflowStore;
@@ -147279,7 +148176,7 @@ var init_workflow_store = __esm({
147279
148176
  }
147280
148177
  recordRun(input) {
147281
148178
  const run = {
147282
- id: randomUUID9(),
148179
+ id: randomUUID12(),
147283
148180
  workflowId: input.workflowId,
147284
148181
  projectId: input.projectId,
147285
148182
  task: input.task.trim(),
@@ -147357,8 +148254,8 @@ var init_outcome_types = __esm({
147357
148254
  });
147358
148255
 
147359
148256
  // src/knowledge/outcome-store.ts
147360
- import { randomUUID as randomUUID10 } from "crypto";
147361
- function optionalText4(value) {
148257
+ import { randomUUID as randomUUID13 } from "crypto";
148258
+ function optionalText5(value) {
147362
148259
  return typeof value === "string" && value.trim() ? value : void 0;
147363
148260
  }
147364
148261
  function text(value, field, max = 1e3) {
@@ -147381,8 +148278,8 @@ function rowToSignal(row) {
147381
148278
  candidateId: row.candidateId,
147382
148279
  kind: row.kind,
147383
148280
  sourceRef: row.sourceRef,
147384
- ...optionalText4(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
147385
- ...optionalText4(row.detail) ? { detail: row.detail } : {},
148281
+ ...optionalText5(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
148282
+ ...optionalText5(row.detail) ? { detail: row.detail } : {},
147386
148283
  observedAt: row.observedAt
147387
148284
  };
147388
148285
  }
@@ -147401,7 +148298,7 @@ var init_outcome_store = __esm({
147401
148298
  }
147402
148299
  record(input) {
147403
148300
  const signal = {
147404
- id: randomUUID10(),
148301
+ id: randomUUID13(),
147405
148302
  projectId: text(input.projectId, "project id"),
147406
148303
  candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
147407
148304
  candidateId: text(input.candidateId, "candidate id"),
@@ -147828,8 +148725,8 @@ __export(workflows_exports, {
147828
148725
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
147829
148726
  });
147830
148727
  import { createHash as createHash12 } from "crypto";
147831
- import { promises as fs14 } from "fs";
147832
- import path21 from "path";
148728
+ import { promises as fs15 } from "fs";
148729
+ import path22 from "path";
147833
148730
  function hash3(value) {
147834
148731
  return createHash12("sha256").update(value).digest("hex");
147835
148732
  }
@@ -147850,7 +148747,7 @@ function requiredText(data, key) {
147850
148747
  }
147851
148748
  return value.trim();
147852
148749
  }
147853
- function optionalText5(data, key) {
148750
+ function optionalText6(data, key) {
147854
148751
  const value = data[key];
147855
148752
  if (value === void 0 || value === null || value === "") return void 0;
147856
148753
  if (typeof value !== "string") throw new Error("workflow frontmatter field " + key + " must be text");
@@ -147893,11 +148790,11 @@ function phaseFromValue(value, index) {
147893
148790
  }
147894
148791
  const data = value;
147895
148792
  const title = requiredText(data, "title");
147896
- const phaseId = optionalText5(data, "id") ?? slug(title) + "-" + (index + 1);
148793
+ const phaseId = optionalText6(data, "id") ?? slug(title) + "-" + (index + 1);
147897
148794
  return {
147898
148795
  id: phaseId,
147899
148796
  title,
147900
- instructions: optionalText5(data, "instructions") ?? "",
148797
+ instructions: optionalText6(data, "instructions") ?? "",
147901
148798
  branches: optionalStringArray(data, "branches"),
147902
148799
  expectedOutputs: optionalStringArray(data, "expectedOutputs"),
147903
148800
  verificationGates: optionalStringArray(data, "verificationGates")
@@ -147932,7 +148829,7 @@ function phasesFromBody(body) {
147932
148829
  }
147933
148830
  function normalizeSourcePath(sourcePath) {
147934
148831
  const normalized = sourcePath.replace(/\\/g, "/");
147935
- if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") || path21.posix.normalize(normalized) !== normalized) {
148832
+ if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") || path22.posix.normalize(normalized) !== normalized) {
147936
148833
  throw new Error("workflow source path must be a safe workflows/*.md path");
147937
148834
  }
147938
148835
  return normalized;
@@ -148006,7 +148903,7 @@ function parsedWorkflow(data, body, input) {
148006
148903
  id: requiredText(data, "id"),
148007
148904
  workspaceId: input.workspaceId,
148008
148905
  title: requiredText(data, "title"),
148009
- description: optionalText5(data, "description") ?? "",
148906
+ description: optionalText6(data, "description") ?? "",
148010
148907
  status: optionalStatus(data),
148011
148908
  version: optionalNumber(data, "version", 1),
148012
148909
  taskLenses: optionalStringArray(data, "taskLenses"),
@@ -148025,9 +148922,9 @@ function parsedWorkflow(data, body, input) {
148025
148922
  sourcePath: normalizeSourcePath(input.sourcePath),
148026
148923
  sourceHash: "",
148027
148924
  contentHash: input.contentHash,
148028
- createdAt: optionalText5(data, "createdAt") ?? now2(),
148029
- updatedAt: optionalText5(data, "updatedAt") ?? now2(),
148030
- ...optionalText5(data, "importedFrom") ? { importedFrom: optionalText5(data, "importedFrom") } : {}
148925
+ createdAt: optionalText6(data, "createdAt") ?? now2(),
148926
+ updatedAt: optionalText6(data, "updatedAt") ?? now2(),
148927
+ ...optionalText6(data, "importedFrom") ? { importedFrom: optionalText6(data, "importedFrom") } : {}
148031
148928
  };
148032
148929
  if (candidate.phases.length === 0) {
148033
148930
  throw new Error("workflow must contain at least one phase or a non-empty body");
@@ -148081,7 +148978,7 @@ async function writeCanonicalWorkflow(input) {
148081
148978
  const filePath = resolveKnowledgeWorkspaceFile(input.workspace, workflow.sourcePath);
148082
148979
  const content = renderWorkflowMarkdown(workflow);
148083
148980
  await withFileLock(paths.root, async () => {
148084
- await fs14.mkdir(path21.dirname(filePath), { recursive: true });
148981
+ await fs15.mkdir(path22.dirname(filePath), { recursive: true });
148085
148982
  await atomicWriteFile(filePath, content);
148086
148983
  });
148087
148984
  const store = await storeFor2(input.workspace);
@@ -148090,13 +148987,13 @@ async function writeCanonicalWorkflow(input) {
148090
148987
  async function markdownFiles2(directory) {
148091
148988
  let entries;
148092
148989
  try {
148093
- entries = await fs14.readdir(directory, { withFileTypes: true });
148990
+ entries = await fs15.readdir(directory, { withFileTypes: true });
148094
148991
  } catch {
148095
148992
  return [];
148096
148993
  }
148097
148994
  const files = [];
148098
148995
  for (const entry of entries) {
148099
- const absolutePath = path21.join(directory, entry.name);
148996
+ const absolutePath = path22.join(directory, entry.name);
148100
148997
  if (entry.isDirectory()) {
148101
148998
  files.push(...await markdownFiles2(absolutePath));
148102
148999
  } else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
@@ -148111,9 +149008,9 @@ async function syncCanonicalWorkflows(workspace) {
148111
149008
  const workflows = [];
148112
149009
  const errors = [];
148113
149010
  for (const absolutePath of await markdownFiles2(paths.workflows)) {
148114
- const sourcePath = path21.relative(paths.root, absolutePath).split(path21.sep).join("/");
149011
+ const sourcePath = path22.relative(paths.root, absolutePath).split(path22.sep).join("/");
148115
149012
  try {
148116
- const raw = await fs14.readFile(absolutePath, "utf8");
149013
+ const raw = await fs15.readFile(absolutePath, "utf8");
148117
149014
  const workflow = parseWorkflowMarkdown(raw, { workspaceId: workspace.id, sourcePath });
148118
149015
  workflows.push(store.upsertWorkflow(workflow));
148119
149016
  } catch (error) {
@@ -148225,21 +149122,21 @@ function importedWorkflowSpec(input) {
148225
149122
  return materializeWorkflow(spec);
148226
149123
  }
148227
149124
  async function importWindsurfWorkflows(input) {
148228
- const sourceDirectory = path21.join(path21.resolve(input.projectRoot), ".windsurf", "workflows");
149125
+ const sourceDirectory = path22.join(path22.resolve(input.projectRoot), ".windsurf", "workflows");
148229
149126
  const paths = getKnowledgeWorkspacePaths(input.workspace);
148230
149127
  const imported = [];
148231
149128
  const skipped = [];
148232
149129
  let entries;
148233
149130
  try {
148234
- entries = await fs14.readdir(sourceDirectory, { withFileTypes: true });
149131
+ entries = await fs15.readdir(sourceDirectory, { withFileTypes: true });
148235
149132
  } catch {
148236
149133
  return { imported, skipped };
148237
149134
  }
148238
149135
  for (const entry of entries.filter((item) => item.isFile() && item.name.toLowerCase().endsWith(".md"))) {
148239
- const originalPath = path21.join(sourceDirectory, entry.name);
148240
- const sourcePath = path21.relative(path21.resolve(input.projectRoot), originalPath).split(path21.sep).join("/");
149136
+ const originalPath = path22.join(sourceDirectory, entry.name);
149137
+ const sourcePath = path22.relative(path22.resolve(input.projectRoot), originalPath).split(path22.sep).join("/");
148241
149138
  try {
148242
- const raw = await fs14.readFile(originalPath, "utf8");
149139
+ const raw = await fs15.readFile(originalPath, "utf8");
148243
149140
  const workflow = importedWorkflowSpec({
148244
149141
  workspace: input.workspace,
148245
149142
  sourceName: entry.name,
@@ -148248,7 +149145,7 @@ async function importWindsurfWorkflows(input) {
148248
149145
  });
148249
149146
  const targetPath = resolveKnowledgeWorkspaceFile(input.workspace, workflow.sourcePath);
148250
149147
  try {
148251
- await fs14.access(targetPath);
149148
+ await fs15.access(targetPath);
148252
149149
  skipped.push({ sourcePath, reason: "canonical workflow already exists and was preserved" });
148253
149150
  continue;
148254
149151
  } catch {
@@ -148261,12 +149158,12 @@ async function importWindsurfWorkflows(input) {
148261
149158
  });
148262
149159
  }
148263
149160
  }
148264
- await fs14.mkdir(paths.workflows, { recursive: true });
149161
+ await fs15.mkdir(paths.workflows, { recursive: true });
148265
149162
  return { imported, skipped };
148266
149163
  }
148267
149164
  function isWithin2(root, candidate) {
148268
- const relative3 = path21.relative(root, candidate);
148269
- return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(".." + path21.sep) && !path21.isAbsolute(relative3);
149165
+ const relative3 = path22.relative(root, candidate);
149166
+ return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(".." + path22.sep) && !path22.isAbsolute(relative3);
148270
149167
  }
148271
149168
  function adapterRelativePath(workflow, agent) {
148272
149169
  const name = "memorix-workflow-" + slug(workflow.id.replace(/^workflow:/, ""));
@@ -148352,15 +149249,15 @@ async function previewWorkflowAdapter(input) {
148352
149249
  reason: "The canonical workflow does not declare compatibility with this agent."
148353
149250
  };
148354
149251
  }
148355
- const projectRoot = path21.resolve(input.projectRoot);
148356
- const targetPath = path21.resolve(projectRoot, adapterRelativePath(input.workflow, input.agent));
149252
+ const projectRoot = path22.resolve(input.projectRoot);
149253
+ const targetPath = path22.resolve(projectRoot, adapterRelativePath(input.workflow, input.agent));
148357
149254
  if (!isWithin2(projectRoot, targetPath)) {
148358
149255
  throw new Error("Workflow adapter target escapes the project root");
148359
149256
  }
148360
149257
  const content = adapterContent(input.workflow, input.agent);
148361
149258
  let existing;
148362
149259
  try {
148363
- existing = await fs14.readFile(targetPath, "utf8");
149260
+ existing = await fs15.readFile(targetPath, "utf8");
148364
149261
  } catch (error) {
148365
149262
  const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
148366
149263
  if (code !== "ENOENT") throw error;
@@ -148381,11 +149278,11 @@ async function applyWorkflowAdapter(input) {
148381
149278
  if (initial.status === "unsupported" || initial.status === "conflict" || initial.status === "unchanged") {
148382
149279
  return initial;
148383
149280
  }
148384
- const projectRoot = path21.resolve(input.projectRoot);
149281
+ const projectRoot = path22.resolve(input.projectRoot);
148385
149282
  return withFileLock(projectRoot, async () => {
148386
149283
  const current = await previewWorkflowAdapter(input);
148387
149284
  if (current.status !== "create" && current.status !== "update") return current;
148388
- await fs14.mkdir(path21.dirname(current.targetPath), { recursive: true });
149285
+ await fs15.mkdir(path22.dirname(current.targetPath), { recursive: true });
148389
149286
  await atomicWriteFile(current.targetPath, current.content);
148390
149287
  return current;
148391
149288
  });
@@ -148675,6 +149572,11 @@ function createProjectMaintenanceHandler(projectId, projectDir2, projectRoot, op
148675
149572
  }
148676
149573
  return { action: "complete" };
148677
149574
  }
149575
+ if (job.kind === "long-term-maintenance") {
149576
+ const { maintainLongTermMemories: maintainLongTermMemories2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
149577
+ await maintainLongTermMemories2({ dataDir: projectDir2, projectId });
149578
+ return { action: "reschedule", delayMs: 24 * 60 * 60 * 1e3, resetAttempts: true };
149579
+ }
148678
149580
  if (job.kind === "consolidation") {
148679
149581
  const limit2 = consolidationBatchSize(job.payload);
148680
149582
  const result2 = await runAutomaticConsolidation(projectId, projectDir2, {
@@ -149016,15 +149918,15 @@ __export(vector_backfill_runner_exports, {
149016
149918
  });
149017
149919
  import { spawn as spawn4 } from "child_process";
149018
149920
  import { existsSync as existsSync10 } from "fs";
149019
- import path22 from "path";
149921
+ import path23 from "path";
149020
149922
  import { fileURLToPath as fileURLToPath5 } from "url";
149021
149923
  function isNonEmptyString(value) {
149022
149924
  return typeof value === "string" && value.trim().length > 0;
149023
149925
  }
149024
149926
  function resolveVectorBackfillRunnerPath(moduleUrl = import.meta.url) {
149025
- const moduleDir = path22.dirname(fileURLToPath5(moduleUrl));
149026
- 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;
149027
- return path22.join(distDir, "vector-backfill-runner.js");
149927
+ const moduleDir = path23.dirname(fileURLToPath5(moduleUrl));
149928
+ 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;
149929
+ return path23.join(distDir, "vector-backfill-runner.js");
149028
149930
  }
149029
149931
  function parseVectorBackfillRequest(raw) {
149030
149932
  let value;
@@ -149033,7 +149935,7 @@ function parseVectorBackfillRequest(raw) {
149033
149935
  } catch {
149034
149936
  throw new Error("Vector backfill runner received invalid JSON input");
149035
149937
  }
149036
- if (!value || typeof value !== "object" || !isNonEmptyString(value.projectId) || !isNonEmptyString(value.projectRoot) || !isNonEmptyString(value.dataDir) || !path22.isAbsolute(value.projectRoot) || !path22.isAbsolute(value.dataDir)) {
149938
+ if (!value || typeof value !== "object" || !isNonEmptyString(value.projectId) || !isNonEmptyString(value.projectRoot) || !isNonEmptyString(value.dataDir) || !path23.isAbsolute(value.projectRoot) || !path23.isAbsolute(value.dataDir)) {
149037
149939
  throw new Error("Vector backfill runner received an invalid request");
149038
149940
  }
149039
149941
  return {
@@ -150115,8 +151017,16 @@ var init_observations = __esm({
150115
151017
  });
150116
151018
 
150117
151019
  // src/store/session-store.ts
150118
- import path23 from "path";
150119
- import fs15 from "fs";
151020
+ var session_store_exports = {};
151021
+ __export(session_store_exports, {
151022
+ SessionGracefulDegrade: () => SessionGracefulDegrade,
151023
+ SessionSqliteStore: () => SessionSqliteStore,
151024
+ getSessionStore: () => getSessionStore,
151025
+ initSessionStore: () => initSessionStore,
151026
+ resetSessionStore: () => resetSessionStore
151027
+ });
151028
+ import path24 from "path";
151029
+ import fs16 from "fs";
150120
151030
  function sessionToRow(session) {
150121
151031
  return {
150122
151032
  id: session.id,
@@ -150145,6 +151055,10 @@ function getSessionStore() {
150145
151055
  }
150146
151056
  return _store3;
150147
151057
  }
151058
+ function resetSessionStore() {
151059
+ _store3 = null;
151060
+ _storeDataDir3 = null;
151061
+ }
150148
151062
  async function initSessionStore(dataDir) {
150149
151063
  if (_store3 && _storeDataDir3 === dataDir) return _store3;
150150
151064
  _store3 = null;
@@ -150198,10 +151112,10 @@ var init_session_store = __esm({
150198
151112
  async migrateFromJsonIfNeeded() {
150199
151113
  const count2 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM sessions`).get();
150200
151114
  if (count2.cnt > 0) return;
150201
- const jsonPath = path23.join(this.dataDir, "sessions.json");
150202
- if (!fs15.existsSync(jsonPath)) return;
151115
+ const jsonPath = path24.join(this.dataDir, "sessions.json");
151116
+ if (!fs16.existsSync(jsonPath)) return;
150203
151117
  try {
150204
- const raw = fs15.readFileSync(jsonPath, "utf-8");
151118
+ const raw = fs16.readFileSync(jsonPath, "utf-8");
150205
151119
  const sessions = JSON.parse(raw);
150206
151120
  if (!Array.isArray(sessions) || sessions.length === 0) return;
150207
151121
  console.error(`[memorix] Migrating ${sessions.length} sessions from JSON to SQLite...`);
@@ -152555,8 +153469,8 @@ var init_trae = __esm({
152555
153469
  });
152556
153470
 
152557
153471
  // src/rules/syncer.ts
152558
- import { promises as fs16 } from "fs";
152559
- import path24 from "path";
153472
+ import { promises as fs17 } from "fs";
153473
+ import path25 from "path";
152560
153474
  var RulesSyncer;
152561
153475
  var init_syncer = __esm({
152562
153476
  "src/rules/syncer.ts"() {
@@ -152601,8 +153515,8 @@ var init_syncer = __esm({
152601
153515
  const found = await this.findFiles(scanPath);
152602
153516
  for (const filePath of found) {
152603
153517
  try {
152604
- const content = await fs16.readFile(filePath, "utf-8");
152605
- const relativePath = path24.relative(this.projectRoot, filePath).replace(/\\/g, "/");
153518
+ const content = await fs17.readFile(filePath, "utf-8");
153519
+ const relativePath = path25.relative(this.projectRoot, filePath).replace(/\\/g, "/");
152606
153520
  const parsed = entry.adapter.parse(relativePath, content);
152607
153521
  rules.push(...parsed);
152608
153522
  } catch {
@@ -152670,7 +153584,7 @@ var init_syncer = __esm({
152670
153584
  const entries = [];
152671
153585
  for (const adapter of this.adapters.values()) {
152672
153586
  const absolutePaths = adapter.filePatterns.map(
152673
- (p) => path24.join(this.projectRoot, p)
153587
+ (p) => path25.join(this.projectRoot, p)
152674
153588
  );
152675
153589
  entries.push({ adapter, paths: absolutePaths });
152676
153590
  }
@@ -152678,13 +153592,13 @@ var init_syncer = __esm({
152678
153592
  }
152679
153593
  /** Find files matching a glob-like path (simple implementation) */
152680
153594
  async findFiles(pattern) {
152681
- const dir = path24.dirname(pattern);
152682
- const fileGlob = path24.basename(pattern);
153595
+ const dir = path25.dirname(pattern);
153596
+ const fileGlob = path25.basename(pattern);
152683
153597
  try {
152684
- const stat4 = await fs16.stat(dir);
153598
+ const stat4 = await fs17.stat(dir);
152685
153599
  if (!stat4.isDirectory()) {
152686
153600
  try {
152687
- await fs16.access(pattern);
153601
+ await fs17.access(pattern);
152688
153602
  return [pattern];
152689
153603
  } catch {
152690
153604
  return [];
@@ -152692,7 +153606,7 @@ var init_syncer = __esm({
152692
153606
  }
152693
153607
  } catch {
152694
153608
  try {
152695
- await fs16.access(pattern);
153609
+ await fs17.access(pattern);
152696
153610
  return [pattern];
152697
153611
  } catch {
152698
153612
  return [];
@@ -152700,16 +153614,16 @@ var init_syncer = __esm({
152700
153614
  }
152701
153615
  if (fileGlob.includes("*")) {
152702
153616
  try {
152703
- const files = await fs16.readdir(dir);
153617
+ const files = await fs17.readdir(dir);
152704
153618
  const ext = fileGlob.replace("*", "");
152705
- return files.filter((f3) => ext ? f3.endsWith(ext) : true).map((f3) => path24.join(dir, f3));
153619
+ return files.filter((f3) => ext ? f3.endsWith(ext) : true).map((f3) => path25.join(dir, f3));
152706
153620
  } catch {
152707
153621
  return [];
152708
153622
  }
152709
153623
  }
152710
153624
  try {
152711
- await fs16.access(path24.join(dir, fileGlob));
152712
- return [path24.join(dir, fileGlob)];
153625
+ await fs17.access(path25.join(dir, fileGlob));
153626
+ return [path25.join(dir, fileGlob)];
152713
153627
  } catch {
152714
153628
  return [];
152715
153629
  }
@@ -155288,12 +156202,52 @@ var init_formation_timeout = __esm({
155288
156202
  }
155289
156203
  });
155290
156204
 
156205
+ // src/search/surfaced-registry.ts
156206
+ function touch(key) {
156207
+ const ids = registry.get(key);
156208
+ if (!ids) return [];
156209
+ registry.delete(key);
156210
+ registry.set(key, ids);
156211
+ return ids;
156212
+ }
156213
+ function getSurfacedIds(key) {
156214
+ return [...touch(key)];
156215
+ }
156216
+ function recordSurfacedIds(key, ids) {
156217
+ if (ids.length === 0) return;
156218
+ let list = registry.get(key) ?? [];
156219
+ const known = new Set(list);
156220
+ for (const id of ids) {
156221
+ if (known.has(id)) continue;
156222
+ known.add(id);
156223
+ list.push(id);
156224
+ }
156225
+ if (list.length > MAX_IDS) {
156226
+ list = list.slice(list.length - MAX_IDS);
156227
+ }
156228
+ registry.set(key, list);
156229
+ if (registry.size > MAX_KEYS) {
156230
+ const oldestKey = registry.keys().next().value;
156231
+ if (oldestKey !== void 0 && oldestKey !== key) registry.delete(oldestKey);
156232
+ }
156233
+ }
156234
+ var MAX_KEYS, MAX_IDS, registry;
156235
+ var init_surfaced_registry = __esm({
156236
+ "src/search/surfaced-registry.ts"() {
156237
+ "use strict";
156238
+ init_esm_shims();
156239
+ MAX_KEYS = 64;
156240
+ MAX_IDS = 200;
156241
+ registry = /* @__PURE__ */ new Map();
156242
+ }
156243
+ });
156244
+
155291
156245
  // src/server/request-context.ts
155292
- import path25 from "path";
156246
+ import path26 from "path";
155293
156247
  function resolveRoot(projectRoot) {
155294
156248
  const value = projectRoot.trim();
155295
156249
  if (!value) throw new Error("Project binding requires a non-empty project root");
155296
- return path25.resolve(value);
156250
+ return path26.resolve(value);
155297
156251
  }
155298
156252
  function createProjectBindingController(projectRoot) {
155299
156253
  return new ProjectBindingController(projectRoot);
@@ -155942,827 +156896,6 @@ var init_session = __esm({
155942
156896
  }
155943
156897
  });
155944
156898
 
155945
- // src/memory/owner.ts
155946
- var owner_exports = {};
155947
- __export(owner_exports, {
155948
- resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
155949
- });
155950
- import { randomUUID as randomUUID11 } from "crypto";
155951
- import { promises as fs17 } from "fs";
155952
- import path26 from "path";
155953
- function ownerPath(dataDir) {
155954
- const resolved = path26.resolve(dataDir);
155955
- const home = path26.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path26.dirname(resolved) : resolved;
155956
- return path26.join(home, "memorix-user.json");
155957
- }
155958
- function normalizedOwnerId(value) {
155959
- const trimmed = value?.trim();
155960
- return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
155961
- }
155962
- async function readOwnerFile(filePath) {
155963
- try {
155964
- const value = JSON.parse(await fs17.readFile(filePath, "utf8"));
155965
- const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
155966
- return id ? { id, source: "local-file" } : void 0;
155967
- } catch {
155968
- return void 0;
155969
- }
155970
- }
155971
- async function resolveLocalMemoryOwner(dataDir, options = {}) {
155972
- const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
155973
- if (configured) return { id: configured, source: "environment" };
155974
- const filePath = ownerPath(dataDir);
155975
- const existing = await readOwnerFile(filePath);
155976
- if (existing || !options.create) return existing;
155977
- await fs17.mkdir(path26.dirname(filePath), { recursive: true });
155978
- const created = {
155979
- version: 1,
155980
- id: "local-" + randomUUID11(),
155981
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
155982
- };
155983
- try {
155984
- await fs17.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
155985
- return { id: created.id, source: "local-file" };
155986
- } catch (error) {
155987
- if (error?.code !== "EEXIST") throw error;
155988
- const concurrent = await readOwnerFile(filePath);
155989
- if (concurrent) return concurrent;
155990
- throw new Error("Memorix could not establish a valid local memory owner identity.");
155991
- }
155992
- }
155993
- var ID_PATTERN;
155994
- var init_owner = __esm({
155995
- "src/memory/owner.ts"() {
155996
- "use strict";
155997
- init_esm_shims();
155998
- ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
155999
- }
156000
- });
156001
-
156002
- // src/memory/long-term-store.ts
156003
- import { randomUUID as randomUUID12 } from "crypto";
156004
- function optionalText6(value) {
156005
- return typeof value === "string" && value.trim() ? value : void 0;
156006
- }
156007
- function arrayValue2(value) {
156008
- if (typeof value !== "string" || !value) return [];
156009
- try {
156010
- const parsed = JSON.parse(value);
156011
- return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
156012
- } catch {
156013
- return [];
156014
- }
156015
- }
156016
- function rowToMemory(row) {
156017
- return {
156018
- id: row.id,
156019
- originProjectId: row.originProjectId,
156020
- ownerId: row.ownerId,
156021
- scope: row.scope,
156022
- kind: row.kind,
156023
- state: row.state,
156024
- portability: row.portability,
156025
- title: row.title,
156026
- content: row.content,
156027
- facts: arrayValue2(row.factsJson),
156028
- tags: arrayValue2(row.tagsJson),
156029
- ...optionalText6(row.applicability) ? { applicability: row.applicability } : {},
156030
- origin: row.origin,
156031
- createdAt: row.createdAt,
156032
- updatedAt: row.updatedAt,
156033
- ...optionalText6(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
156034
- ...optionalText6(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
156035
- ...optionalText6(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
156036
- ...optionalText6(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
156037
- ...optionalText6(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
156038
- accessCount: Number(row.accessCount ?? 0),
156039
- ...optionalText6(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
156040
- };
156041
- }
156042
- function rowToEvidence2(row) {
156043
- return {
156044
- id: row.id,
156045
- memoryId: row.memoryId,
156046
- kind: row.kind,
156047
- referenceId: row.referenceId,
156048
- relation: row.relation,
156049
- ...optionalText6(row.locator) ? { locator: row.locator } : {},
156050
- ...optionalText6(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
156051
- createdAt: row.createdAt
156052
- };
156053
- }
156054
- function rowToEvent2(row) {
156055
- return {
156056
- id: row.id,
156057
- memoryId: row.memoryId,
156058
- kind: row.kind,
156059
- ...optionalText6(row.fromState) ? { fromState: row.fromState } : {},
156060
- ...optionalText6(row.toState) ? { toState: row.toState } : {},
156061
- ...optionalText6(row.detail) ? { detail: row.detail } : {},
156062
- createdAt: row.createdAt
156063
- };
156064
- }
156065
- var LongTermMemoryStore;
156066
- var init_long_term_store = __esm({
156067
- "src/memory/long-term-store.ts"() {
156068
- "use strict";
156069
- init_esm_shims();
156070
- init_sqlite_db();
156071
- LongTermMemoryStore = class {
156072
- db = null;
156073
- async init(dataDir) {
156074
- this.db = getDatabase(dataDir);
156075
- }
156076
- transaction(fn) {
156077
- return this.db.transaction(fn)();
156078
- }
156079
- get(id) {
156080
- const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
156081
- return row ? rowToMemory(row) : void 0;
156082
- }
156083
- list(options = {}) {
156084
- const limit2 = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
156085
- const clauses = [];
156086
- const params = [];
156087
- if (options.ownerId) {
156088
- clauses.push("ownerId = ?");
156089
- params.push(options.ownerId);
156090
- }
156091
- if (options.originProjectId) {
156092
- clauses.push("originProjectId = ?");
156093
- params.push(options.originProjectId);
156094
- }
156095
- const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
156096
- const rows = this.db.prepare(
156097
- "SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
156098
- ).all(...params, limit2);
156099
- return rows.map(rowToMemory);
156100
- }
156101
- insert(memory) {
156102
- this.db.prepare(`
156103
- INSERT INTO long_term_memories (
156104
- id, originProjectId, ownerId, scope, kind, state, portability,
156105
- title, content, factsJson, tagsJson, applicability, origin,
156106
- createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
156107
- supersededBy, lastValidatedAt, accessCount, lastAccessedAt
156108
- ) VALUES (
156109
- @id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
156110
- @title, @content, @factsJson, @tagsJson, @applicability, @origin,
156111
- @createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
156112
- @supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
156113
- )
156114
- `).run({
156115
- ...memory,
156116
- factsJson: JSON.stringify(memory.facts),
156117
- tagsJson: JSON.stringify(memory.tags),
156118
- applicability: memory.applicability ?? null,
156119
- qualifiedAt: memory.qualifiedAt ?? null,
156120
- approvedAt: memory.approvedAt ?? null,
156121
- archivedAt: memory.archivedAt ?? null,
156122
- supersededBy: memory.supersededBy ?? null,
156123
- lastValidatedAt: memory.lastValidatedAt ?? null,
156124
- lastAccessedAt: memory.lastAccessedAt ?? null
156125
- });
156126
- }
156127
- update(memory) {
156128
- this.db.prepare(`
156129
- UPDATE long_term_memories SET
156130
- scope = @scope,
156131
- kind = @kind,
156132
- state = @state,
156133
- portability = @portability,
156134
- title = @title,
156135
- content = @content,
156136
- factsJson = @factsJson,
156137
- tagsJson = @tagsJson,
156138
- applicability = @applicability,
156139
- updatedAt = @updatedAt,
156140
- qualifiedAt = @qualifiedAt,
156141
- approvedAt = @approvedAt,
156142
- archivedAt = @archivedAt,
156143
- supersededBy = @supersededBy,
156144
- lastValidatedAt = @lastValidatedAt,
156145
- accessCount = @accessCount,
156146
- lastAccessedAt = @lastAccessedAt
156147
- WHERE id = @id
156148
- `).run({
156149
- ...memory,
156150
- factsJson: JSON.stringify(memory.facts),
156151
- tagsJson: JSON.stringify(memory.tags),
156152
- applicability: memory.applicability ?? null,
156153
- qualifiedAt: memory.qualifiedAt ?? null,
156154
- approvedAt: memory.approvedAt ?? null,
156155
- archivedAt: memory.archivedAt ?? null,
156156
- supersededBy: memory.supersededBy ?? null,
156157
- lastValidatedAt: memory.lastValidatedAt ?? null,
156158
- lastAccessedAt: memory.lastAccessedAt ?? null
156159
- });
156160
- }
156161
- insertEvidence(input) {
156162
- const evidence = {
156163
- id: randomUUID12(),
156164
- ...input,
156165
- createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
156166
- };
156167
- this.db.prepare(`
156168
- INSERT INTO long_term_memory_evidence (
156169
- id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
156170
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
156171
- `).run(
156172
- evidence.id,
156173
- evidence.memoryId,
156174
- evidence.kind,
156175
- evidence.referenceId,
156176
- evidence.relation,
156177
- evidence.locator ?? null,
156178
- evidence.capturedHash ?? null,
156179
- evidence.createdAt
156180
- );
156181
- return evidence;
156182
- }
156183
- listEvidence(memoryId) {
156184
- return this.db.prepare(`
156185
- SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
156186
- `).all(memoryId).map(rowToEvidence2);
156187
- }
156188
- recordEvent(input) {
156189
- const event = {
156190
- id: randomUUID12(),
156191
- ...input,
156192
- createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
156193
- };
156194
- this.db.prepare(`
156195
- INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
156196
- VALUES (?, ?, ?, ?, ?, ?, ?)
156197
- `).run(
156198
- event.id,
156199
- event.memoryId,
156200
- event.kind,
156201
- event.fromState ?? null,
156202
- event.toState ?? null,
156203
- event.detail ?? null,
156204
- event.createdAt
156205
- );
156206
- return event;
156207
- }
156208
- listEvents(memoryId) {
156209
- return this.db.prepare(`
156210
- SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
156211
- `).all(memoryId).map(rowToEvent2);
156212
- }
156213
- markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
156214
- const unique2 = [...new Set(ids.filter(Boolean))];
156215
- if (unique2.length === 0) return;
156216
- const statement = this.db.prepare(`
156217
- UPDATE long_term_memories
156218
- SET accessCount = accessCount + 1, lastAccessedAt = ?
156219
- WHERE id = ?
156220
- `);
156221
- this.db.transaction(() => {
156222
- for (const id of unique2) statement.run(at, id);
156223
- })();
156224
- }
156225
- };
156226
- }
156227
- });
156228
-
156229
- // src/memory/long-term-types.ts
156230
- var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
156231
- var init_long_term_types = __esm({
156232
- "src/memory/long-term-types.ts"() {
156233
- "use strict";
156234
- init_esm_shims();
156235
- LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
156236
- LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
156237
- LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
156238
- LONG_TERM_MEMORY_EVIDENCE_KINDS = [
156239
- "observation",
156240
- "claim",
156241
- "workflow",
156242
- "session",
156243
- "checkpoint",
156244
- "git",
156245
- "code",
156246
- "test",
156247
- "run",
156248
- "document",
156249
- "manual",
156250
- "user"
156251
- ];
156252
- LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
156253
- "supports",
156254
- "verifies",
156255
- "derives",
156256
- "user-confirmed"
156257
- ];
156258
- }
156259
- });
156260
-
156261
- // src/memory/long-term.ts
156262
- var long_term_exports = {};
156263
- __export(long_term_exports, {
156264
- approveLongTermMemory: () => approveLongTermMemory,
156265
- archiveLongTermMemory: () => archiveLongTermMemory,
156266
- canReadLongTermMemory: () => canReadLongTermMemory,
156267
- createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
156268
- createManualLongTermMemory: () => createManualLongTermMemory,
156269
- getLongTermMemoryDetail: () => getLongTermMemoryDetail,
156270
- isEligibleLongTermMemory: () => isEligibleLongTermMemory,
156271
- listLongTermMemories: () => listLongTermMemories,
156272
- promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
156273
- qualifyLongTermMemory: () => qualifyLongTermMemory,
156274
- renderLongTermMemorySummary: () => renderLongTermMemorySummary,
156275
- selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
156276
- supersedeLongTermMemory: () => supersedeLongTermMemory
156277
- });
156278
- import { randomUUID as randomUUID13 } from "crypto";
156279
- function isOneOf(value, values) {
156280
- return typeof value === "string" && values.includes(value);
156281
- }
156282
- function compactText2(value, field, max = MAX_TEXT_LENGTH) {
156283
- if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
156284
- const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
156285
- if (!safe) throw new Error("Long-term memory " + field + " is required.");
156286
- if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
156287
- return safe;
156288
- }
156289
- function compactList(values, field) {
156290
- if (!values) return [];
156291
- if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
156292
- return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText2(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
156293
- }
156294
- function normalizeEvidence(input) {
156295
- if (!Array.isArray(input) || input.length === 0) {
156296
- throw new Error("A long-term memory requires at least one source evidence reference.");
156297
- }
156298
- return input.map((evidence) => {
156299
- if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
156300
- throw new Error("Long-term memory evidence kind is invalid.");
156301
- }
156302
- if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
156303
- throw new Error("Long-term memory evidence relation is invalid.");
156304
- }
156305
- return {
156306
- kind: evidence.kind,
156307
- referenceId: compactText2(evidence.referenceId, "evidence reference", 1e3),
156308
- relation: evidence.relation,
156309
- ...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator", 1e3) } : {},
156310
- ...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash", 1e3) } : {}
156311
- };
156312
- });
156313
- }
156314
- function assertScope(scope) {
156315
- if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
156316
- }
156317
- function assertKind(kind) {
156318
- if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
156319
- }
156320
- function assertPortability(value) {
156321
- if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
156322
- throw new Error("Long-term memory portability is invalid.");
156323
- }
156324
- }
156325
- function assertPortableEvidence(scope, portability, evidence) {
156326
- if (portability !== "portable") return;
156327
- if (scope !== "user") {
156328
- throw new Error("Only user-scoped long-term memories may be portable across projects.");
156329
- }
156330
- if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
156331
- 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.");
156332
- }
156333
- }
156334
- function requireTeamReader(scope, reader) {
156335
- if (scope === "team" && reader?.isTeamMember !== true) {
156336
- throw new Error("Team long-term memory requires an explicit active team identity for this project.");
156337
- }
156338
- }
156339
- function matchesScope(memory, reader) {
156340
- if (memory.scope === "project") return memory.originProjectId === reader.projectId;
156341
- if (memory.scope === "team") {
156342
- return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
156343
- }
156344
- if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
156345
- return memory.portability === "portable" || memory.originProjectId === reader.projectId;
156346
- }
156347
- function canReadLongTermMemory(memory, reader) {
156348
- return matchesScope(memory, reader);
156349
- }
156350
- function isEligibleLongTermMemory(memory) {
156351
- return memory.state === "qualified" || memory.state === "approved";
156352
- }
156353
- function taskTerms(task) {
156354
- const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
156355
- return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
156356
- }
156357
- function kindFitScore(kind, task) {
156358
- const normalized = task.toLocaleLowerCase("en-US");
156359
- const hints = {
156360
- episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
156361
- semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
156362
- procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
156363
- };
156364
- return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
156365
- }
156366
- function validationFreshnessScore(memory) {
156367
- const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
156368
- if (!validatedAt) return 0;
156369
- const time = Date.parse(validatedAt);
156370
- if (!Number.isFinite(time)) return 0;
156371
- const ageDays = Math.max(0, Date.now() - time) / (24 * 60 * 60 * 1e3);
156372
- if (ageDays <= 30) return 0.35;
156373
- if (ageDays <= 90) return 0.15;
156374
- return 0;
156375
- }
156376
- function scoreForTask(memory, task) {
156377
- const terms = taskTerms(task);
156378
- if (terms.length === 0) return void 0;
156379
- const title = memory.title.toLocaleLowerCase("en-US");
156380
- const content = memory.content.toLocaleLowerCase("en-US");
156381
- const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
156382
- const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
156383
- const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
156384
- let score = memory.state === "approved" ? 0.5 : 0;
156385
- const matched = [];
156386
- for (const term of terms) {
156387
- let termScore = 0;
156388
- if (title.includes(term)) termScore += 5;
156389
- if (tags.includes(term)) termScore += 4;
156390
- if (applicability.includes(term)) termScore += 3;
156391
- if (facts.includes(term)) termScore += 2;
156392
- if (content.includes(term)) termScore += 1;
156393
- if (termScore > 0) {
156394
- score += termScore;
156395
- matched.push(term);
156396
- }
156397
- }
156398
- if (matched.length === 0) return void 0;
156399
- score += kindFitScore(memory.kind, task);
156400
- score += validationFreshnessScore(memory);
156401
- const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
156402
- const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
156403
- return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
156404
- }
156405
- function semanticText(memory) {
156406
- return sanitizeCredentials([
156407
- memory.title,
156408
- memory.applicability,
156409
- memory.tags.join(" "),
156410
- memory.facts.join(" "),
156411
- memory.content
156412
- ].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
156413
- }
156414
- function cosineSimilarity(left, right) {
156415
- if (left.length === 0 || left.length !== right.length) return 0;
156416
- let dot = 0;
156417
- let leftMagnitude = 0;
156418
- let rightMagnitude = 0;
156419
- for (let index = 0; index < left.length; index += 1) {
156420
- const a = left[index];
156421
- const b = right[index];
156422
- if (!Number.isFinite(a) || !Number.isFinite(b)) return 0;
156423
- dot += a * b;
156424
- leftMagnitude += a * a;
156425
- rightMagnitude += b * b;
156426
- }
156427
- if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
156428
- return dot / Math.sqrt(leftMagnitude * rightMagnitude);
156429
- }
156430
- function semanticSelection(memory, similarity, task) {
156431
- if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
156432
- const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
156433
- const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
156434
- return {
156435
- score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
156436
- reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
156437
- };
156438
- }
156439
- async function resolveSemanticProvider(explicit) {
156440
- if (explicit !== void 0) return explicit;
156441
- try {
156442
- const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
156443
- return await withTimeout(
156444
- getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
156445
- SEMANTIC_RETRIEVAL_TIMEOUT_MS,
156446
- "Long-term semantic provider initialization"
156447
- );
156448
- } catch {
156449
- return null;
156450
- }
156451
- }
156452
- function createStore(dataDir) {
156453
- const store = new LongTermMemoryStore();
156454
- return store.init(dataDir).then(() => store);
156455
- }
156456
- async function createManualLongTermMemory(input) {
156457
- const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
156458
- if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
156459
- assertScope(input.scope);
156460
- const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID13(), relation: "supports" }];
156461
- return createLongTermMemoryWithDataDir({
156462
- dataDir: input.dataDir,
156463
- originProjectId: input.projectId,
156464
- ownerId: owner.id,
156465
- scope: input.scope,
156466
- kind: input.kind,
156467
- portability: input.portability,
156468
- title: input.title,
156469
- content: input.content,
156470
- facts: input.facts,
156471
- tags: input.tags,
156472
- applicability: input.applicability,
156473
- origin: "manual",
156474
- evidence,
156475
- reader: input.reader
156476
- });
156477
- }
156478
- async function promoteObservationToLongTermMemory(input) {
156479
- const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
156480
- if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
156481
- return createLongTermMemoryWithDataDir({
156482
- dataDir: input.dataDir,
156483
- originProjectId: input.observation.projectId,
156484
- ownerId: owner.id,
156485
- scope: input.scope,
156486
- kind: input.kind,
156487
- portability: "project-bound",
156488
- title: input.observation.title,
156489
- content: input.observation.narrative,
156490
- facts: input.observation.facts,
156491
- tags: input.tags ?? input.observation.concepts,
156492
- applicability: input.applicability,
156493
- origin: input.observation.source === "git" ? "git" : "agent",
156494
- evidence: [{
156495
- kind: "observation",
156496
- referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
156497
- relation: "derives"
156498
- }],
156499
- reader: input.reader
156500
- });
156501
- }
156502
- async function createLongTermMemoryCandidate(input) {
156503
- return createLongTermMemoryWithDataDir(input);
156504
- }
156505
- async function createLongTermMemoryWithDataDir(input) {
156506
- assertScope(input.scope);
156507
- assertKind(input.kind);
156508
- const portability = input.portability ?? "project-bound";
156509
- assertPortability(portability);
156510
- requireTeamReader(input.scope, input.reader);
156511
- const evidenceInputs = normalizeEvidence(input.evidence);
156512
- assertPortableEvidence(input.scope, portability, evidenceInputs);
156513
- const now3 = (/* @__PURE__ */ new Date()).toISOString();
156514
- const memory = {
156515
- id: randomUUID13(),
156516
- originProjectId: compactText2(input.originProjectId, "origin project id", 1e3),
156517
- ownerId: compactText2(input.ownerId, "owner id", 160),
156518
- scope: input.scope,
156519
- kind: input.kind,
156520
- state: "candidate",
156521
- portability,
156522
- title: compactText2(input.title, "title", 500),
156523
- content: compactText2(input.content, "content"),
156524
- facts: compactList(input.facts, "facts"),
156525
- tags: compactList(input.tags, "tags"),
156526
- ...input.applicability ? { applicability: compactText2(input.applicability, "applicability", 1e3) } : {},
156527
- origin: input.origin ?? "manual",
156528
- createdAt: now3,
156529
- updatedAt: now3,
156530
- accessCount: 0
156531
- };
156532
- const store = await createStore(input.dataDir);
156533
- const evidence = store.transaction(() => {
156534
- store.insert(memory);
156535
- const written = evidenceInputs.map((item) => store.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
156536
- store.recordEvent({
156537
- memoryId: memory.id,
156538
- kind: "created",
156539
- toState: "candidate",
156540
- detail: "Created as a source-backed long-term memory candidate.",
156541
- createdAt: now3
156542
- });
156543
- return written;
156544
- });
156545
- return { memory, evidence };
156546
- }
156547
- function transitionDetail(value) {
156548
- return compactText2(value, "transition reason", 1e3);
156549
- }
156550
- async function transitionMemory(input) {
156551
- const store = await createStore(input.dataDir);
156552
- return store.transaction(() => {
156553
- const current = store.get(input.id);
156554
- if (!current) throw new Error("Long-term memory was not found.");
156555
- if (current.state !== input.expected) {
156556
- throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
156557
- }
156558
- const evidence = store.listEvidence(current.id);
156559
- if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
156560
- const now3 = (/* @__PURE__ */ new Date()).toISOString();
156561
- const next = {
156562
- ...current,
156563
- state: input.next,
156564
- updatedAt: now3,
156565
- lastValidatedAt: now3,
156566
- ...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
156567
- };
156568
- store.update(next);
156569
- store.recordEvent({
156570
- memoryId: next.id,
156571
- kind: input.event,
156572
- fromState: current.state,
156573
- toState: next.state,
156574
- detail: transitionDetail(input.reason),
156575
- createdAt: now3
156576
- });
156577
- return next;
156578
- });
156579
- }
156580
- async function qualifyLongTermMemory(input) {
156581
- return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
156582
- }
156583
- async function approveLongTermMemory(input) {
156584
- return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
156585
- }
156586
- async function archiveLongTermMemory(input) {
156587
- const store = await createStore(input.dataDir);
156588
- return store.transaction(() => {
156589
- const current = store.get(input.id);
156590
- if (!current) throw new Error("Long-term memory was not found.");
156591
- if (current.state === "archived" || current.state === "superseded") {
156592
- throw new Error("Long-term memory is already inactive.");
156593
- }
156594
- const now3 = (/* @__PURE__ */ new Date()).toISOString();
156595
- const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
156596
- store.update(next);
156597
- store.recordEvent({
156598
- memoryId: next.id,
156599
- kind: "archived",
156600
- fromState: current.state,
156601
- toState: "archived",
156602
- detail: transitionDetail(input.reason),
156603
- createdAt: now3
156604
- });
156605
- return next;
156606
- });
156607
- }
156608
- async function supersedeLongTermMemory(input) {
156609
- const store = await createStore(input.dataDir);
156610
- return store.transaction(() => {
156611
- const current = store.get(input.id);
156612
- if (!current) throw new Error("Long-term memory was not found.");
156613
- if (current.state === "archived" || current.state === "superseded") {
156614
- throw new Error("Long-term memory is already inactive.");
156615
- }
156616
- if (current.id === input.supersededBy) {
156617
- throw new Error("Long-term memory cannot supersede itself.");
156618
- }
156619
- const replacement = store.get(input.supersededBy);
156620
- if (!replacement || !isEligibleLongTermMemory(replacement)) {
156621
- throw new Error("Replacement long-term memory must be qualified or approved.");
156622
- }
156623
- if (replacement.scope !== current.scope) {
156624
- throw new Error("Replacement long-term memory must use the same scope.");
156625
- }
156626
- if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
156627
- throw new Error("User long-term memory may only be superseded by the same local owner.");
156628
- }
156629
- if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
156630
- throw new Error("Project and team long-term memory may only be superseded within the same project.");
156631
- }
156632
- const now3 = (/* @__PURE__ */ new Date()).toISOString();
156633
- const next = {
156634
- ...current,
156635
- state: "superseded",
156636
- supersededBy: replacement.id,
156637
- updatedAt: now3
156638
- };
156639
- store.update(next);
156640
- store.recordEvent({
156641
- memoryId: next.id,
156642
- kind: "superseded",
156643
- fromState: current.state,
156644
- toState: "superseded",
156645
- detail: transitionDetail(input.reason),
156646
- createdAt: now3
156647
- });
156648
- return next;
156649
- });
156650
- }
156651
- async function listLongTermMemories(input) {
156652
- const store = await createStore(input.dataDir);
156653
- 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) }));
156654
- }
156655
- async function getLongTermMemoryDetail(input) {
156656
- const store = await createStore(input.dataDir);
156657
- const memory = store.get(input.id);
156658
- if (!memory || !canReadLongTermMemory(memory, input.reader)) {
156659
- throw new Error("Long-term memory was not found in the active scope.");
156660
- }
156661
- store.markAccess([memory.id]);
156662
- return { memory, evidence: store.listEvidence(memory.id), events: store.listEvents(memory.id) };
156663
- }
156664
- async function selectLongTermMemoriesForTask(input) {
156665
- const task = input.task?.trim();
156666
- if (!task) return [];
156667
- const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
156668
- const reader = {
156669
- projectId: input.projectId,
156670
- ...owner ? { ownerId: owner.id } : {},
156671
- ...input.agentId ? { agentId: input.agentId } : {},
156672
- ...input.isTeamMember ? { isTeamMember: true } : {}
156673
- };
156674
- const store = await createStore(input.dataDir);
156675
- const eligible = store.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
156676
- let selected = eligible.map((memory) => {
156677
- const ranked = scoreForTask(memory, task);
156678
- return ranked ? {
156679
- memory,
156680
- evidence: store.listEvidence(memory.id),
156681
- score: ranked.score,
156682
- reason: ranked.reason
156683
- } : void 0;
156684
- }).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));
156685
- if (selected.length === 0 && eligible.length > 0) {
156686
- const provider2 = await resolveSemanticProvider(input.embeddingProvider);
156687
- const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
156688
- if (provider2 && candidates.length > 0) {
156689
- try {
156690
- const vectors = await withTimeout(
156691
- provider2.embedBatch([task, ...candidates.map(semanticText)], {
156692
- timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
156693
- retry: false
156694
- }),
156695
- SEMANTIC_RETRIEVAL_TIMEOUT_MS,
156696
- "Long-term semantic retrieval"
156697
- );
156698
- const query = vectors[0];
156699
- if (Array.isArray(query)) {
156700
- selected = candidates.map((memory, index) => {
156701
- const vector = vectors[index + 1];
156702
- const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
156703
- return ranked ? {
156704
- memory,
156705
- evidence: store.listEvidence(memory.id),
156706
- score: ranked.score,
156707
- reason: ranked.reason
156708
- } : void 0;
156709
- }).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));
156710
- }
156711
- } catch {
156712
- }
156713
- }
156714
- }
156715
- selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
156716
- store.markAccess(selected.map((item) => item.memory.id));
156717
- return selected;
156718
- }
156719
- function renderLongTermMemorySummary(memory, maxTokens = 22) {
156720
- const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
156721
- const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
156722
- return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
156723
- }
156724
- var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
156725
- var init_long_term = __esm({
156726
- "src/memory/long-term.ts"() {
156727
- "use strict";
156728
- init_esm_shims();
156729
- init_token_budget();
156730
- init_timeout();
156731
- init_secret_filter();
156732
- init_owner();
156733
- init_long_term_store();
156734
- init_long_term_types();
156735
- MAX_TEXT_LENGTH = 12e3;
156736
- MAX_LIST_ITEMS = 40;
156737
- MAX_SEMANTIC_CANDIDATES = 48;
156738
- SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
156739
- MIN_SEMANTIC_SIMILARITY = 0.58;
156740
- PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
156741
- STOP_TERMS = /* @__PURE__ */ new Set([
156742
- "the",
156743
- "and",
156744
- "for",
156745
- "with",
156746
- "from",
156747
- "that",
156748
- "this",
156749
- "into",
156750
- "when",
156751
- "what",
156752
- "\u5982\u4F55",
156753
- "\u4EC0\u4E48",
156754
- "\u8FD9\u4E2A",
156755
- "\u90A3\u4E2A",
156756
- "\u6211\u4EEC",
156757
- "\u9879\u76EE",
156758
- "\u8FDB\u884C",
156759
- "\u9700\u8981",
156760
- "\u7EE7\u7EED",
156761
- "\u5904\u7406"
156762
- ]);
156763
- }
156764
- });
156765
-
156766
156899
  // src/knowledge/workset.ts
156767
156900
  function unique(values) {
156768
156901
  return [...new Set(values.filter(Boolean))];
@@ -156970,6 +157103,37 @@ function renderTaskWorksetPrompt(input) {
156970
157103
  trust: "source-backed"
156971
157104
  });
156972
157105
  appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
157106
+ if (input.alwaysOn) {
157107
+ const hasAlwaysOn = input.alwaysOn.profile.length > 0 || Boolean(input.alwaysOn.state) || input.alwaysOn.durable.length > 0;
157108
+ if (hasAlwaysOn) {
157109
+ appendLine(lines, "", maxTokens, omitted, "always-on-heading");
157110
+ appendLine(lines, "You and this workspace", maxTokens, omitted, "always-on-heading");
157111
+ for (const profile of input.alwaysOn.profile.slice(0, 2)) {
157112
+ appendLine(lines, "- " + short(profile, 26), maxTokens, omitted, "always-on-profile", selected, {
157113
+ kind: "memory",
157114
+ id: "profile",
157115
+ reason: "user profile memory",
157116
+ trust: "source-backed"
157117
+ });
157118
+ }
157119
+ if (input.alwaysOn.state) {
157120
+ appendLine(lines, "- Recently: " + short(input.alwaysOn.state, 40), maxTokens, omitted, "always-on-state", selected, {
157121
+ kind: "continuation",
157122
+ id: "workspace-state",
157123
+ reason: "latest session summary for this workspace",
157124
+ trust: "historical"
157125
+ });
157126
+ }
157127
+ for (const durable of input.alwaysOn.durable.slice(0, 2)) {
157128
+ appendLine(lines, "- " + short(durable, 28), maxTokens, omitted, "always-on-durable", selected, {
157129
+ kind: "durable-memory",
157130
+ id: "durable-always",
157131
+ reason: "recent durable memory for this workspace",
157132
+ trust: "source-backed"
157133
+ });
157134
+ }
157135
+ }
157136
+ }
156973
157137
  const hasContinuation = Boolean(
156974
157138
  input.continuation?.previousSession || (input.continuation?.memories.length ?? 0) > 0 || input.continuation?.compactCheckpoint
156975
157139
  );
@@ -157440,6 +157604,7 @@ async function buildTaskWorkset(input) {
157440
157604
  version: "1.3",
157441
157605
  task,
157442
157606
  lens: input.lens,
157607
+ ...input.alwaysOn && (input.alwaysOn.profile.length > 0 || input.alwaysOn.state || input.alwaysOn.durable.length > 0) ? { alwaysOn: input.alwaysOn } : {},
157443
157608
  currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
157444
157609
  ...continuation ? { continuation } : {},
157445
157610
  ...input.codeState ? { codeState: short(input.codeState, 28) } : {},
@@ -158592,6 +158757,33 @@ function activeProjectObservations(observations2, projectId) {
158592
158757
  function deliveryEligibleProjectObservations(observations2, projectId) {
158593
158758
  return activeProjectObservations(observations2, projectId).filter((observation) => isEligibleForAutomaticDelivery(observation));
158594
158759
  }
158760
+ async function buildAlwaysOnBlock(input) {
158761
+ 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));
158762
+ let state;
158763
+ try {
158764
+ await initSessionStore(input.dataDir);
158765
+ const { getSessionStore: getSessionStore2 } = await Promise.resolve().then(() => (init_session_store(), session_store_exports));
158766
+ const sessions = await getSessionStore2().loadByProject(input.projectId);
158767
+ const latest = sessions.filter((session) => session.endedAt && session.summary?.trim()).sort((a, b) => Date.parse(b.endedAt ?? "") - Date.parse(a.endedAt ?? ""))[0];
158768
+ if (latest?.summary) state = compactContinuationText(latest.summary, 40);
158769
+ } catch {
158770
+ }
158771
+ const durable = [];
158772
+ try {
158773
+ const items = await listLongTermMemories({
158774
+ dataDir: input.dataDir,
158775
+ reader: {
158776
+ projectId: input.projectId,
158777
+ ...input.reader.agentId ? { agentId: input.reader.agentId } : {},
158778
+ ...input.reader.isTeamMember ? { isTeamMember: true } : {}
158779
+ },
158780
+ limit: 10
158781
+ });
158782
+ 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)));
158783
+ } catch {
158784
+ }
158785
+ return { profile, state, durable };
158786
+ }
158595
158787
  function decideRefresh(input) {
158596
158788
  if (input.mode === "never") {
158597
158789
  return { performed: false, reason: "disabled", message: "Automatic project scan disabled." };
@@ -158796,12 +158988,19 @@ async function buildAutoProjectContext(input) {
158796
158988
  };
158797
158989
  }
158798
158990
  }
158991
+ const alwaysOn = await buildAlwaysOnBlock({
158992
+ dataDir: input.dataDir,
158993
+ projectId: input.project.id,
158994
+ reader: input.reader ?? { projectId: input.project.id },
158995
+ eligible: deliveryEligibleProjectObservations(input.observations, input.project.id)
158996
+ });
158799
158997
  const workset = await buildTaskWorkset({
158800
158998
  projectId: input.project.id,
158801
158999
  dataDir: input.dataDir,
158802
159000
  ...task ? { task } : {},
158803
159001
  lens: lens.id,
158804
159002
  startHere,
159003
+ ...alwaysOn.profile.length > 0 || alwaysOn.state || alwaysOn.durable.length > 0 ? { alwaysOn } : {},
158805
159004
  ...externalOutline ? { semanticCode: externalOutline } : {},
158806
159005
  providerQuality: providerQuality2,
158807
159006
  currentFacts: worksetFactLines(currentFacts),
@@ -158984,7 +159183,16 @@ function formatAutoProjectContextSummary(context) {
158984
159183
  lensLine(context),
158985
159184
  "",
158986
159185
  ...formatCurrentFactsLines(context.currentFacts),
158987
- "",
159186
+ ""
159187
+ ].filter(Boolean);
159188
+ if (context.workset.alwaysOn && (context.workset.alwaysOn.profile.length > 0 || context.workset.alwaysOn.state || context.workset.alwaysOn.durable.length > 0)) {
159189
+ lines.push("You and this workspace");
159190
+ for (const profile of context.workset.alwaysOn.profile.slice(0, 2)) lines.push(`- ${profile}`);
159191
+ if (context.workset.alwaysOn.state) lines.push(`- Recently: ${context.workset.alwaysOn.state}`);
159192
+ for (const durable of context.workset.alwaysOn.durable.slice(0, 2)) lines.push(`- ${durable}`);
159193
+ lines.push("");
159194
+ }
159195
+ lines.push(...[
158988
159196
  `- Code memory: ${context.overview.code.files} files / ${context.overview.code.symbols} symbols / ${context.overview.code.refs} memory links`,
158989
159197
  `- Code provider: ${context.providerQuality.selected} (${context.providerQuality.selectedQuality})`,
158990
159198
  `- Languages: ${formatLanguages(context.overview)}`,
@@ -158993,7 +159201,7 @@ function formatAutoProjectContextSummary(context) {
158993
159201
  `- Refresh: ${context.refresh.message}`,
158994
159202
  "",
158995
159203
  "Start here"
158996
- ].filter(Boolean);
159204
+ ].filter(Boolean));
158997
159205
  if (startHere.length > 0) {
158998
159206
  startHere.forEach((path37, index) => lines.push(`${index + 1}. ${path37}`));
158999
159207
  } else {
@@ -159117,6 +159325,8 @@ var init_auto_context = __esm({
159117
159325
  init_store();
159118
159326
  init_admission();
159119
159327
  init_session();
159328
+ init_long_term();
159329
+ init_visibility();
159120
159330
  init_session_store();
159121
159331
  init_task_lens();
159122
159332
  DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
@@ -162067,8 +162277,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
162067
162277
  const { promises: fsP } = await import("fs");
162068
162278
  const registryPath = path30.join(baseDir, ".project-aliases.json");
162069
162279
  const raw = await fsP.readFile(registryPath, "utf-8");
162070
- const registry = JSON.parse(raw);
162071
- aliasGroups = registry.groups || [];
162280
+ const registry2 = JSON.parse(raw);
162281
+ aliasGroups = registry2.groups || [];
162072
162282
  } catch {
162073
162283
  }
162074
162284
  const currentGroup = aliasGroups.find((g) => g.aliases?.includes(effectiveProjectId) || g.canonical === effectiveProjectId);
@@ -164984,13 +165194,28 @@ alwaysApply: true
164984
165194
  '- For "why" decisions, use `memorix_store_reasoning`',
164985
165195
  '- 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`.',
164986
165196
  "- 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.",
165197
+ '- 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.',
164987
165198
  "",
164988
165199
  "**Don't store:** greetings, simple file reads, trivial commands (ls, pwd, git status).",
164989
165200
  "",
165201
+ "**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.",
165202
+ "",
165203
+ `**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.`,
165204
+ "",
165205
+ "**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.",
165206
+ "",
164990
165207
  "## When to resolve memory",
164991
165208
  "",
164992
165209
  "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.",
164993
165210
  "",
165211
+ "## End sessions with a summary",
165212
+ "",
165213
+ "When a session finishes, call `memorix_session_end` with a short structured summary so the next agent can resume. Recommended sections:",
165214
+ "- **Goal** \u2014 what this session was working on",
165215
+ "- **Discoveries** \u2014 findings, gotchas, learnings",
165216
+ "- **Accomplished** \u2014 completed items, plus PENDING items for the next session",
165217
+ "- **Relevant Files** \u2014 paths and what changed",
165218
+ "",
164994
165219
  "## Tools quick reference",
164995
165220
  "",
164996
165221
  "| Tool | Use when |",
@@ -165541,6 +165766,12 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
165541
165766
  kind: "consolidation",
165542
165767
  dedupeKey: "startup-consolidation"
165543
165768
  });
165769
+ lifecycle.enqueueLongTermMaintenance({
165770
+ dataDir: projectDir2,
165771
+ projectId: project.id,
165772
+ source: "startup",
165773
+ queue
165774
+ });
165544
165775
  }
165545
165776
  try {
165546
165777
  const { CodeGraphStore: CodeGraphStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
@@ -165590,7 +165821,7 @@ The path should point to a directory containing a .git folder.`
165590
165821
  };
165591
165822
  const server = existingServer ?? new McpServer({
165592
165823
  name: "memorix",
165593
- version: true ? "1.4.5" : "1.0.1"
165824
+ version: true ? "1.4.6" : "1.0.1"
165594
165825
  });
165595
165826
  const originalRegisterTool = server.registerTool.bind(server);
165596
165827
  server.registerTool = ((name, ...args) => {
@@ -166075,7 +166306,7 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
166075
166306
  let longTermNote = "";
166076
166307
  if (longTerm) {
166077
166308
  try {
166078
- const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
166309
+ const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2, maybeAutoQualifyLongTermMemory: maybeAutoQualifyLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
166079
166310
  const promoted = await promoteObservationToLongTermMemory2({
166080
166311
  dataDir: projectDir2,
166081
166312
  observation: obs,
@@ -166089,7 +166320,24 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
166089
166320
  ...reader.isTeamMember ? { isTeamMember: true } : {}
166090
166321
  }
166091
166322
  });
166092
- longTermNote = `
166323
+ const autoQualified = await maybeAutoQualifyLongTermMemory2({
166324
+ dataDir: projectDir2,
166325
+ id: promoted.memory.id,
166326
+ sourceDetail: "explicit"
166327
+ });
166328
+ if (autoQualified) {
166329
+ try {
166330
+ const { enqueueLongTermMaintenance: enqueueLongTermMaintenance2 } = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
166331
+ enqueueLongTermMaintenance2({
166332
+ dataDir: projectDir2,
166333
+ projectId: project.id,
166334
+ source: "long-term:" + promoted.memory.id
166335
+ });
166336
+ } catch {
166337
+ }
166338
+ }
166339
+ longTermNote = autoQualified ? `
166340
+ 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\`.` : `
166093
166341
  Long-term candidate: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}). Review it with the CLI before it is delivered automatically.`;
166094
166342
  } catch (longTermError) {
166095
166343
  longTermNote = `
@@ -166285,6 +166533,13 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
166285
166533
  const boundary = requireExplicitAutopilotExpansion("memorix_search", purpose);
166286
166534
  if (boundary) return boundary;
166287
166535
  }
166536
+ let surfacedKey = `project:${project.id}`;
166537
+ try {
166538
+ const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
166539
+ const active = await getActiveSession2(projectDir2, project.id);
166540
+ if (active) surfacedKey = `session:${active.id}`;
166541
+ } catch {
166542
+ }
166288
166543
  return withFreshIndex(async () => {
166289
166544
  const safeLimit2 = limit2 != null ? coerceNumber(limit2, 20) : void 0;
166290
166545
  const safeMaxTokens = maxTokens != null ? coerceNumber(maxTokens, 0) : void 0;
@@ -166302,7 +166557,8 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
166302
166557
  status: status ?? "active",
166303
166558
  source,
166304
166559
  quality,
166305
- reader: getObservationReader(scope === "global" ? "global" : "project")
166560
+ reader: getObservationReader(scope === "global" ? "global" : "project"),
166561
+ surfacedIds: getSurfacedIds(surfacedKey)
166306
166562
  });
166307
166563
  let result;
166308
166564
  try {
@@ -166321,6 +166577,7 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
166321
166577
  }
166322
166578
  throw error;
166323
166579
  }
166580
+ recordSurfacedIds(surfacedKey, result.entries.map((entry) => entry.id));
166324
166581
  const activeBoundary = getActiveAutopilotRetrievalBoundary();
166325
166582
  if (scope !== "global" && activeBoundary && !force && result.entries.length > 0 && result.entries.every((entry) => activeBoundary.coveredObservationIds.has(entry.id))) {
166326
166583
  const duplicate = blockCoveredAutopilotEvidence("memorix_search", result.entries.map((entry) => entry.id), force);
@@ -169283,13 +169540,26 @@ ${lines.join("\n")}` }] };
169283
169540
  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";
169284
169541
  return void 0;
169285
169542
  }
169543
+ const mediaGenerationEnabled = process.env.MEMORIX_MCP_MEDIA_GENERATION === "1";
169544
+ const mediaTranscriptionEnabled = process.env.MEMORIX_MCP_MEDIA_TRANSCRIPTION === "1";
169545
+ const mediaGenerationOrTranscription = mediaGenerationEnabled || mediaTranscriptionEnabled;
169286
169546
  server.registerTool(
169287
169547
  "memorix_media",
169288
169548
  {
169289
169549
  title: "Manage Controlled Media",
169290
169550
  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.",
169291
169551
  inputSchema: {
169292
- action: z626.enum(["import", "attach", "list", "show", "derive-pdf", "derive-audio", "generate-image", "generate-video", "status", "cancel"]),
169552
+ action: z626.enum([
169553
+ "import",
169554
+ "attach",
169555
+ "list",
169556
+ "show",
169557
+ "derive-pdf",
169558
+ "status",
169559
+ "cancel",
169560
+ ...mediaTranscriptionEnabled ? ["derive-audio"] : [],
169561
+ ...mediaGenerationEnabled ? ["generate-image", "generate-video"] : []
169562
+ ]),
169293
169563
  path: z626.string().optional().describe("Explicit local image/audio/video/PDF path for import."),
169294
169564
  assetId: z626.string().optional().describe("Controlled MediaAsset ID for attach/show."),
169295
169565
  jobId: z626.string().optional().describe("Durable media job ID for status."),
@@ -169297,20 +169567,26 @@ ${lines.join("\n")}` }] };
169297
169567
  limit: z626.number().int().min(1).max(100).optional().describe("Maximum assets to list."),
169298
169568
  title: z626.string().optional().describe("Observation title when attaching generated/imported output."),
169299
169569
  narrative: z626.string().optional().describe("Short retrieval text when attaching an asset."),
169300
- prompt: z626.string().optional().describe("Explicit MiniMax generation prompt."),
169301
- image: z626.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
169302
- model: z626.string().max(160).optional().describe("Provider model for the selected media action."),
169303
- provider: z626.enum(["openai", "groq"]).optional().describe("Explicit audio transcription provider."),
169304
- language: z626.string().max(32).optional().describe("Optional ISO-639-1 hint for audio transcription."),
169305
- region: z626.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
169306
- n: z626.number().int().min(1).max(4).optional().describe("Image output count."),
169307
- 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."),
169308
- width: z626.number().int().min(1).max(8192).optional().describe("Requested image width."),
169309
- height: z626.number().int().min(1).max(8192).optional().describe("Requested image height."),
169310
- duration: z626.union([z626.literal(5), z626.literal(10)]).optional().describe("MiniMax video duration in seconds."),
169311
169570
  maxPages: z626.number().int().min(1).max(100).optional().describe("Bounded PDF extraction page limit."),
169312
169571
  maxChars: z626.number().int().min(1).max(6e4).optional().describe("Bounded PDF extraction character limit."),
169313
- attach: z626.boolean().optional().describe("Attach generated/imported output to normal project memory explicitly.")
169572
+ attach: z626.boolean().optional().describe("Attach generated/imported output to normal project memory explicitly."),
169573
+ ...mediaGenerationOrTranscription ? {
169574
+ model: z626.string().max(160).optional().describe("Provider model for the selected media action."),
169575
+ prompt: z626.string().optional().describe("Explicit prompt for generation or transcription.")
169576
+ } : {},
169577
+ ...mediaTranscriptionEnabled ? {
169578
+ provider: z626.enum(["openai", "groq"]).optional().describe("Explicit audio transcription provider."),
169579
+ language: z626.string().max(32).optional().describe("Optional ISO-639-1 hint for audio transcription.")
169580
+ } : {},
169581
+ ...mediaGenerationEnabled ? {
169582
+ image: z626.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
169583
+ region: z626.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
169584
+ n: z626.number().int().min(1).max(4).optional().describe("Image output count."),
169585
+ 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."),
169586
+ width: z626.number().int().min(1).max(8192).optional().describe("Requested image width."),
169587
+ height: z626.number().int().min(1).max(8192).optional().describe("Requested image height."),
169588
+ duration: z626.union([z626.literal(5), z626.literal(10)]).optional().describe("MiniMax video duration in seconds.")
169589
+ } : {}
169314
169590
  }
169315
169591
  },
169316
169592
  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 }) => {
@@ -169794,6 +170070,7 @@ var init_server2 = __esm({
169794
170070
  init_formation_timeout();
169795
170071
  init_timeout();
169796
170072
  init_secret_filter();
170073
+ init_surfaced_registry();
169797
170074
  init_request_context();
169798
170075
  FORMATION_TIMEOUT_MS = parseFormationTimeoutMs(process.env.MEMORIX_FORMATION_TIMEOUT_MS);
169799
170076
  COMPACT_ON_WRITE_TIMEOUT_MS = 12e3;
@@ -169923,7 +170200,7 @@ var init_serve_shared = __esm({
169923
170200
 
169924
170201
  // src/cli/version.ts
169925
170202
  function getCliVersion() {
169926
- return true ? "1.4.5" : pkg.version;
170203
+ return true ? "1.4.6" : pkg.version;
169927
170204
  }
169928
170205
  var init_version3 = __esm({
169929
170206
  "src/cli/version.ts"() {