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/cli/index.js CHANGED
@@ -3221,7 +3221,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
3221
3221
 
3222
3222
  // src/cli/version.ts
3223
3223
  function getCliVersion() {
3224
- return true ? "1.4.5" : pkg.version;
3224
+ return true ? "1.4.6" : pkg.version;
3225
3225
  }
3226
3226
  var init_version = __esm({
3227
3227
  "src/cli/version.ts"() {
@@ -21729,7 +21729,6 @@ __export(config_exports, {
21729
21729
  getLLMProvider: () => getLLMProvider,
21730
21730
  getLoadedEnvFiles: () => getLoadedEnvFiles,
21731
21731
  getServerConfig: () => getServerConfig,
21732
- getTeamConfig: () => getTeamConfig,
21733
21732
  loadDotenv: () => loadDotenv,
21734
21733
  loadFileConfig: () => loadFileConfig,
21735
21734
  loadYamlConfig: () => loadYamlConfig,
@@ -21801,14 +21800,10 @@ function getServerConfig() {
21801
21800
  port: server.port
21802
21801
  };
21803
21802
  }
21804
- function getTeamConfig() {
21805
- return loadYamlConfig().team ?? {};
21806
- }
21807
21803
  var init_config = __esm({
21808
21804
  "src/config.ts"() {
21809
21805
  "use strict";
21810
21806
  init_esm_shims();
21811
- init_yaml_loader();
21812
21807
  init_resolved_config();
21813
21808
  init_dotenv_loader();
21814
21809
  init_legacy_loader();
@@ -24093,9 +24088,9 @@ async function saveRegistry(baseDir) {
24093
24088
  await fs6.mkdir(path8.dirname(filePath), { recursive: true });
24094
24089
  await fs6.writeFile(filePath, JSON.stringify(registryCache, null, 2), "utf-8");
24095
24090
  }
24096
- function findMatchingGroup(registry2, projectInfo) {
24091
+ function findMatchingGroup(registry3, projectInfo) {
24097
24092
  const normalizedRoot = normalizePath(projectInfo.rootPath);
24098
- for (const group of registry2.groups) {
24093
+ for (const group of registry3.groups) {
24099
24094
  if (group.aliases.includes(projectInfo.id)) return group;
24100
24095
  if (group.rootPaths.some((rp) => rp === normalizedRoot)) return group;
24101
24096
  if (projectInfo.gitRemote && group.gitRemote && group.gitRemote === projectInfo.gitRemote) {
@@ -24108,9 +24103,9 @@ function selectCanonical(aliases) {
24108
24103
  return [...aliases].sort((a4, b4) => idPriority(b4) - idPriority(a4))[0];
24109
24104
  }
24110
24105
  async function registerAlias(projectInfo, baseDir) {
24111
- const registry2 = await loadRegistry(baseDir);
24106
+ const registry3 = await loadRegistry(baseDir);
24112
24107
  const normalizedRoot = normalizePath(projectInfo.rootPath);
24113
- const existingGroup = findMatchingGroup(registry2, projectInfo);
24108
+ const existingGroup = findMatchingGroup(registry3, projectInfo);
24114
24109
  if (existingGroup) {
24115
24110
  let changed = false;
24116
24111
  if (!existingGroup.aliases.includes(projectInfo.id)) {
@@ -24141,13 +24136,13 @@ async function registerAlias(projectInfo, baseDir) {
24141
24136
  rootPaths: [normalizedRoot],
24142
24137
  ...projectInfo.gitRemote ? { gitRemote: projectInfo.gitRemote } : {}
24143
24138
  };
24144
- registry2.groups.push(newGroup);
24139
+ registry3.groups.push(newGroup);
24145
24140
  await saveRegistry(baseDir);
24146
24141
  return newGroup.canonical;
24147
24142
  }
24148
24143
  async function resolveAliases(projectId, baseDir) {
24149
- const registry2 = await loadRegistry(baseDir);
24150
- for (const group of registry2.groups) {
24144
+ const registry3 = await loadRegistry(baseDir);
24145
+ for (const group of registry3.groups) {
24151
24146
  if (group.aliases.includes(projectId) || group.canonical === projectId) {
24152
24147
  return [...group.aliases];
24153
24148
  }
@@ -24155,8 +24150,8 @@ async function resolveAliases(projectId, baseDir) {
24155
24150
  return [projectId];
24156
24151
  }
24157
24152
  async function getCanonicalId(projectId, baseDir) {
24158
- const registry2 = await loadRegistry(baseDir);
24159
- for (const group of registry2.groups) {
24153
+ const registry3 = await loadRegistry(baseDir);
24154
+ for (const group of registry3.groups) {
24160
24155
  if (group.aliases.includes(projectId) || group.canonical === projectId) {
24161
24156
  return group.canonical;
24162
24157
  }
@@ -24164,12 +24159,12 @@ async function getCanonicalId(projectId, baseDir) {
24164
24159
  return projectId;
24165
24160
  }
24166
24161
  async function getAllAliasGroups(baseDir) {
24167
- const registry2 = await loadRegistry(baseDir);
24168
- return registry2.groups;
24162
+ const registry3 = await loadRegistry(baseDir);
24163
+ return registry3.groups;
24169
24164
  }
24170
24165
  async function autoMergeByBaseName(observedIds, baseDir) {
24171
24166
  if (observedIds.length <= 1) return 0;
24172
- const registry2 = await loadRegistry(baseDir);
24167
+ const registry3 = await loadRegistry(baseDir);
24173
24168
  const byBaseName = /* @__PURE__ */ new Map();
24174
24169
  for (const id of observedIds) {
24175
24170
  const baseName = id.split("/").pop() ?? id;
@@ -24182,7 +24177,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
24182
24177
  const existingGroups = /* @__PURE__ */ new Set();
24183
24178
  const ungroupedIds = [];
24184
24179
  for (const id of ids) {
24185
- const groupIdx = registry2.groups.findIndex(
24180
+ const groupIdx = registry3.groups.findIndex(
24186
24181
  (g4) => g4.aliases.includes(id) || g4.canonical === id
24187
24182
  );
24188
24183
  if (groupIdx >= 0) {
@@ -24196,7 +24191,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
24196
24191
  const canonical = selectCanonical(allIdsInGroup);
24197
24192
  if (existingGroups.size > 0) {
24198
24193
  const primaryIdx = [...existingGroups][0];
24199
- const primaryGroup = registry2.groups[primaryIdx];
24194
+ const primaryGroup = registry3.groups[primaryIdx];
24200
24195
  for (const id of allIdsInGroup) {
24201
24196
  if (!primaryGroup.aliases.includes(id)) {
24202
24197
  primaryGroup.aliases.push(id);
@@ -24204,7 +24199,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
24204
24199
  }
24205
24200
  const otherIdxs = [...existingGroups].slice(1).sort((a4, b4) => b4 - a4);
24206
24201
  for (const idx of otherIdxs) {
24207
- const other = registry2.groups[idx];
24202
+ const other = registry3.groups[idx];
24208
24203
  for (const alias of other.aliases) {
24209
24204
  if (!primaryGroup.aliases.includes(alias)) {
24210
24205
  primaryGroup.aliases.push(alias);
@@ -24218,12 +24213,12 @@ async function autoMergeByBaseName(observedIds, baseDir) {
24218
24213
  if (other.gitRemote && !primaryGroup.gitRemote) {
24219
24214
  primaryGroup.gitRemote = other.gitRemote;
24220
24215
  }
24221
- registry2.groups.splice(idx, 1);
24216
+ registry3.groups.splice(idx, 1);
24222
24217
  }
24223
24218
  primaryGroup.canonical = selectCanonical(primaryGroup.aliases);
24224
24219
  mergeCount++;
24225
24220
  } else {
24226
- registry2.groups.push({
24221
+ registry3.groups.push({
24227
24222
  canonical,
24228
24223
  aliases: allIdsInGroup,
24229
24224
  rootPaths: []
@@ -25105,7 +25100,8 @@ var init_maintenance_jobs = __esm({
25105
25100
  "claim-requalification",
25106
25101
  "knowledge-compile",
25107
25102
  "knowledge-lint",
25108
- "workflow-index"
25103
+ "workflow-index",
25104
+ "long-term-maintenance"
25109
25105
  ];
25110
25106
  DEFAULT_MAX_ATTEMPTS = 8;
25111
25107
  DEFAULT_LEASE_MS = 3e4;
@@ -25514,6 +25510,7 @@ __export(lifecycle_exports, {
25514
25510
  enqueueClaimRequalification: () => enqueueClaimRequalification,
25515
25511
  enqueueCodegraphRefresh: () => enqueueCodegraphRefresh,
25516
25512
  enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups,
25513
+ enqueueLongTermMaintenance: () => enqueueLongTermMaintenance,
25517
25514
  enqueueObservationQualification: () => enqueueObservationQualification
25518
25515
  });
25519
25516
  function queueFor(input) {
@@ -25557,6 +25554,14 @@ function enqueueObservationQualification(input) {
25557
25554
  payload: { source: input.source, limit: 100 }
25558
25555
  });
25559
25556
  }
25557
+ function enqueueLongTermMaintenance(input) {
25558
+ queueFor(input).enqueue({
25559
+ projectId: input.projectId,
25560
+ kind: "long-term-maintenance",
25561
+ dedupeKey: "long-term-maintenance",
25562
+ payload: { source: input.source }
25563
+ });
25564
+ }
25560
25565
  function enqueueKnowledgeFollowups(input) {
25561
25566
  const queue2 = queueFor(input);
25562
25567
  const payload = {
@@ -25736,7 +25741,8 @@ var init_isolated_maintenance = __esm({
25736
25741
  "claim-requalification",
25737
25742
  "knowledge-compile",
25738
25743
  "knowledge-lint",
25739
- "workflow-index"
25744
+ "workflow-index",
25745
+ "long-term-maintenance"
25740
25746
  ];
25741
25747
  DEFAULT_TIMEOUT_MS = 5 * 6e4;
25742
25748
  MAX_CHILD_OUTPUT_BYTES = 1e6;
@@ -177446,6 +177452,893 @@ var init_retention = __esm({
177446
177452
  }
177447
177453
  });
177448
177454
 
177455
+ // src/memory/owner.ts
177456
+ var owner_exports = {};
177457
+ __export(owner_exports, {
177458
+ resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
177459
+ });
177460
+ import { randomUUID as randomUUID6 } from "crypto";
177461
+ import { promises as fs11 } from "fs";
177462
+ import path18 from "path";
177463
+ function ownerPath(dataDir) {
177464
+ const resolved = path18.resolve(dataDir);
177465
+ const home = path18.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path18.dirname(resolved) : resolved;
177466
+ return path18.join(home, "memorix-user.json");
177467
+ }
177468
+ function normalizedOwnerId(value) {
177469
+ const trimmed = value?.trim();
177470
+ return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
177471
+ }
177472
+ async function readOwnerFile(filePath) {
177473
+ try {
177474
+ const value = JSON.parse(await fs11.readFile(filePath, "utf8"));
177475
+ const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
177476
+ return id ? { id, source: "local-file" } : void 0;
177477
+ } catch {
177478
+ return void 0;
177479
+ }
177480
+ }
177481
+ async function resolveLocalMemoryOwner(dataDir, options = {}) {
177482
+ const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
177483
+ if (configured) return { id: configured, source: "environment" };
177484
+ const filePath = ownerPath(dataDir);
177485
+ const existing = await readOwnerFile(filePath);
177486
+ if (existing || !options.create) return existing;
177487
+ await fs11.mkdir(path18.dirname(filePath), { recursive: true });
177488
+ const created = {
177489
+ version: 1,
177490
+ id: "local-" + randomUUID6(),
177491
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
177492
+ };
177493
+ try {
177494
+ await fs11.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
177495
+ return { id: created.id, source: "local-file" };
177496
+ } catch (error40) {
177497
+ if (error40?.code !== "EEXIST") throw error40;
177498
+ const concurrent = await readOwnerFile(filePath);
177499
+ if (concurrent) return concurrent;
177500
+ throw new Error("Memorix could not establish a valid local memory owner identity.");
177501
+ }
177502
+ }
177503
+ var ID_PATTERN;
177504
+ var init_owner = __esm({
177505
+ "src/memory/owner.ts"() {
177506
+ "use strict";
177507
+ init_esm_shims();
177508
+ ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
177509
+ }
177510
+ });
177511
+
177512
+ // src/memory/long-term-store.ts
177513
+ var long_term_store_exports = {};
177514
+ __export(long_term_store_exports, {
177515
+ LongTermMemoryStore: () => LongTermMemoryStore
177516
+ });
177517
+ import { randomUUID as randomUUID7 } from "crypto";
177518
+ function optionalText2(value) {
177519
+ return typeof value === "string" && value.trim() ? value : void 0;
177520
+ }
177521
+ function arrayValue2(value) {
177522
+ if (typeof value !== "string" || !value) return [];
177523
+ try {
177524
+ const parsed = JSON.parse(value);
177525
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
177526
+ } catch {
177527
+ return [];
177528
+ }
177529
+ }
177530
+ function rowToMemory(row) {
177531
+ return {
177532
+ id: row.id,
177533
+ originProjectId: row.originProjectId,
177534
+ ownerId: row.ownerId,
177535
+ scope: row.scope,
177536
+ kind: row.kind,
177537
+ state: row.state,
177538
+ portability: row.portability,
177539
+ title: row.title,
177540
+ content: row.content,
177541
+ facts: arrayValue2(row.factsJson),
177542
+ tags: arrayValue2(row.tagsJson),
177543
+ ...optionalText2(row.applicability) ? { applicability: row.applicability } : {},
177544
+ origin: row.origin,
177545
+ createdAt: row.createdAt,
177546
+ updatedAt: row.updatedAt,
177547
+ ...optionalText2(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
177548
+ ...optionalText2(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
177549
+ ...optionalText2(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
177550
+ ...optionalText2(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
177551
+ ...optionalText2(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
177552
+ accessCount: Number(row.accessCount ?? 0),
177553
+ ...optionalText2(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
177554
+ };
177555
+ }
177556
+ function rowToEvidence(row) {
177557
+ return {
177558
+ id: row.id,
177559
+ memoryId: row.memoryId,
177560
+ kind: row.kind,
177561
+ referenceId: row.referenceId,
177562
+ relation: row.relation,
177563
+ ...optionalText2(row.locator) ? { locator: row.locator } : {},
177564
+ ...optionalText2(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
177565
+ createdAt: row.createdAt
177566
+ };
177567
+ }
177568
+ function rowToEvent(row) {
177569
+ return {
177570
+ id: row.id,
177571
+ memoryId: row.memoryId,
177572
+ kind: row.kind,
177573
+ ...optionalText2(row.fromState) ? { fromState: row.fromState } : {},
177574
+ ...optionalText2(row.toState) ? { toState: row.toState } : {},
177575
+ ...optionalText2(row.detail) ? { detail: row.detail } : {},
177576
+ createdAt: row.createdAt
177577
+ };
177578
+ }
177579
+ var LongTermMemoryStore;
177580
+ var init_long_term_store = __esm({
177581
+ "src/memory/long-term-store.ts"() {
177582
+ "use strict";
177583
+ init_esm_shims();
177584
+ init_sqlite_db();
177585
+ LongTermMemoryStore = class {
177586
+ db = null;
177587
+ async init(dataDir) {
177588
+ this.db = getDatabase(dataDir);
177589
+ }
177590
+ transaction(fn2) {
177591
+ return this.db.transaction(fn2)();
177592
+ }
177593
+ get(id) {
177594
+ const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
177595
+ return row ? rowToMemory(row) : void 0;
177596
+ }
177597
+ list(options = {}) {
177598
+ const limit2 = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
177599
+ const clauses = [];
177600
+ const params = [];
177601
+ if (options.ownerId) {
177602
+ clauses.push("ownerId = ?");
177603
+ params.push(options.ownerId);
177604
+ }
177605
+ if (options.originProjectId) {
177606
+ clauses.push("originProjectId = ?");
177607
+ params.push(options.originProjectId);
177608
+ }
177609
+ const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
177610
+ const rows = this.db.prepare(
177611
+ "SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
177612
+ ).all(...params, limit2);
177613
+ return rows.map(rowToMemory);
177614
+ }
177615
+ insert(memory) {
177616
+ this.db.prepare(`
177617
+ INSERT INTO long_term_memories (
177618
+ id, originProjectId, ownerId, scope, kind, state, portability,
177619
+ title, content, factsJson, tagsJson, applicability, origin,
177620
+ createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
177621
+ supersededBy, lastValidatedAt, accessCount, lastAccessedAt
177622
+ ) VALUES (
177623
+ @id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
177624
+ @title, @content, @factsJson, @tagsJson, @applicability, @origin,
177625
+ @createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
177626
+ @supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
177627
+ )
177628
+ `).run({
177629
+ ...memory,
177630
+ factsJson: JSON.stringify(memory.facts),
177631
+ tagsJson: JSON.stringify(memory.tags),
177632
+ applicability: memory.applicability ?? null,
177633
+ qualifiedAt: memory.qualifiedAt ?? null,
177634
+ approvedAt: memory.approvedAt ?? null,
177635
+ archivedAt: memory.archivedAt ?? null,
177636
+ supersededBy: memory.supersededBy ?? null,
177637
+ lastValidatedAt: memory.lastValidatedAt ?? null,
177638
+ lastAccessedAt: memory.lastAccessedAt ?? null
177639
+ });
177640
+ }
177641
+ update(memory) {
177642
+ this.db.prepare(`
177643
+ UPDATE long_term_memories SET
177644
+ scope = @scope,
177645
+ kind = @kind,
177646
+ state = @state,
177647
+ portability = @portability,
177648
+ title = @title,
177649
+ content = @content,
177650
+ factsJson = @factsJson,
177651
+ tagsJson = @tagsJson,
177652
+ applicability = @applicability,
177653
+ updatedAt = @updatedAt,
177654
+ qualifiedAt = @qualifiedAt,
177655
+ approvedAt = @approvedAt,
177656
+ archivedAt = @archivedAt,
177657
+ supersededBy = @supersededBy,
177658
+ lastValidatedAt = @lastValidatedAt,
177659
+ accessCount = @accessCount,
177660
+ lastAccessedAt = @lastAccessedAt
177661
+ WHERE id = @id
177662
+ `).run({
177663
+ ...memory,
177664
+ factsJson: JSON.stringify(memory.facts),
177665
+ tagsJson: JSON.stringify(memory.tags),
177666
+ applicability: memory.applicability ?? null,
177667
+ qualifiedAt: memory.qualifiedAt ?? null,
177668
+ approvedAt: memory.approvedAt ?? null,
177669
+ archivedAt: memory.archivedAt ?? null,
177670
+ supersededBy: memory.supersededBy ?? null,
177671
+ lastValidatedAt: memory.lastValidatedAt ?? null,
177672
+ lastAccessedAt: memory.lastAccessedAt ?? null
177673
+ });
177674
+ }
177675
+ insertEvidence(input) {
177676
+ const evidence = {
177677
+ id: randomUUID7(),
177678
+ ...input,
177679
+ createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
177680
+ };
177681
+ this.db.prepare(`
177682
+ INSERT INTO long_term_memory_evidence (
177683
+ id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
177684
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
177685
+ `).run(
177686
+ evidence.id,
177687
+ evidence.memoryId,
177688
+ evidence.kind,
177689
+ evidence.referenceId,
177690
+ evidence.relation,
177691
+ evidence.locator ?? null,
177692
+ evidence.capturedHash ?? null,
177693
+ evidence.createdAt
177694
+ );
177695
+ return evidence;
177696
+ }
177697
+ listEvidence(memoryId) {
177698
+ return this.db.prepare(`
177699
+ SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
177700
+ `).all(memoryId).map(rowToEvidence);
177701
+ }
177702
+ recordEvent(input) {
177703
+ const event = {
177704
+ id: randomUUID7(),
177705
+ ...input,
177706
+ createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
177707
+ };
177708
+ this.db.prepare(`
177709
+ INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
177710
+ VALUES (?, ?, ?, ?, ?, ?, ?)
177711
+ `).run(
177712
+ event.id,
177713
+ event.memoryId,
177714
+ event.kind,
177715
+ event.fromState ?? null,
177716
+ event.toState ?? null,
177717
+ event.detail ?? null,
177718
+ event.createdAt
177719
+ );
177720
+ return event;
177721
+ }
177722
+ listEvents(memoryId) {
177723
+ return this.db.prepare(`
177724
+ SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
177725
+ `).all(memoryId).map(rowToEvent);
177726
+ }
177727
+ markAccess(ids, at2 = (/* @__PURE__ */ new Date()).toISOString()) {
177728
+ const unique3 = [...new Set(ids.filter(Boolean))];
177729
+ if (unique3.length === 0) return;
177730
+ const statement = this.db.prepare(`
177731
+ UPDATE long_term_memories
177732
+ SET accessCount = accessCount + 1, lastAccessedAt = ?
177733
+ WHERE id = ?
177734
+ `);
177735
+ this.db.transaction(() => {
177736
+ for (const id of unique3) statement.run(at2, id);
177737
+ })();
177738
+ }
177739
+ };
177740
+ }
177741
+ });
177742
+
177743
+ // src/memory/long-term-types.ts
177744
+ var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
177745
+ var init_long_term_types = __esm({
177746
+ "src/memory/long-term-types.ts"() {
177747
+ "use strict";
177748
+ init_esm_shims();
177749
+ LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
177750
+ LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
177751
+ LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
177752
+ LONG_TERM_MEMORY_EVIDENCE_KINDS = [
177753
+ "observation",
177754
+ "claim",
177755
+ "workflow",
177756
+ "session",
177757
+ "checkpoint",
177758
+ "git",
177759
+ "code",
177760
+ "test",
177761
+ "run",
177762
+ "document",
177763
+ "manual",
177764
+ "user"
177765
+ ];
177766
+ LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
177767
+ "supports",
177768
+ "verifies",
177769
+ "derives",
177770
+ "user-confirmed"
177771
+ ];
177772
+ }
177773
+ });
177774
+
177775
+ // src/memory/long-term.ts
177776
+ var long_term_exports = {};
177777
+ __export(long_term_exports, {
177778
+ approveLongTermMemory: () => approveLongTermMemory,
177779
+ archiveLongTermMemory: () => archiveLongTermMemory,
177780
+ canReadLongTermMemory: () => canReadLongTermMemory,
177781
+ createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
177782
+ createManualLongTermMemory: () => createManualLongTermMemory,
177783
+ getLongTermMemoryDetail: () => getLongTermMemoryDetail,
177784
+ isEligibleLongTermMemory: () => isEligibleLongTermMemory,
177785
+ listLongTermMemories: () => listLongTermMemories,
177786
+ maintainLongTermMemories: () => maintainLongTermMemories,
177787
+ maybeAutoQualifyLongTermMemory: () => maybeAutoQualifyLongTermMemory,
177788
+ promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
177789
+ qualifyLongTermMemory: () => qualifyLongTermMemory,
177790
+ renderLongTermMemorySummary: () => renderLongTermMemorySummary,
177791
+ selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
177792
+ supersedeLongTermMemory: () => supersedeLongTermMemory
177793
+ });
177794
+ import { randomUUID as randomUUID8 } from "crypto";
177795
+ function isOneOf(value, values) {
177796
+ return typeof value === "string" && values.includes(value);
177797
+ }
177798
+ function compactText(value, field, max = MAX_TEXT_LENGTH) {
177799
+ if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
177800
+ const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
177801
+ if (!safe) throw new Error("Long-term memory " + field + " is required.");
177802
+ if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
177803
+ return safe;
177804
+ }
177805
+ function compactList(values, field) {
177806
+ if (!values) return [];
177807
+ if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
177808
+ return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
177809
+ }
177810
+ function normalizeEvidence(input) {
177811
+ if (!Array.isArray(input) || input.length === 0) {
177812
+ throw new Error("A long-term memory requires at least one source evidence reference.");
177813
+ }
177814
+ return input.map((evidence) => {
177815
+ if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
177816
+ throw new Error("Long-term memory evidence kind is invalid.");
177817
+ }
177818
+ if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
177819
+ throw new Error("Long-term memory evidence relation is invalid.");
177820
+ }
177821
+ return {
177822
+ kind: evidence.kind,
177823
+ referenceId: compactText(evidence.referenceId, "evidence reference", 1e3),
177824
+ relation: evidence.relation,
177825
+ ...evidence.locator ? { locator: compactText(evidence.locator, "evidence locator", 1e3) } : {},
177826
+ ...evidence.capturedHash ? { capturedHash: compactText(evidence.capturedHash, "evidence hash", 1e3) } : {}
177827
+ };
177828
+ });
177829
+ }
177830
+ function assertScope(scope) {
177831
+ if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
177832
+ }
177833
+ function assertKind(kind) {
177834
+ if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
177835
+ }
177836
+ function assertPortability(value) {
177837
+ if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
177838
+ throw new Error("Long-term memory portability is invalid.");
177839
+ }
177840
+ }
177841
+ function assertPortableEvidence(scope, portability, evidence) {
177842
+ if (portability !== "portable") return;
177843
+ if (scope !== "user") {
177844
+ throw new Error("Only user-scoped long-term memories may be portable across projects.");
177845
+ }
177846
+ if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
177847
+ 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.");
177848
+ }
177849
+ }
177850
+ function requireTeamReader(scope, reader) {
177851
+ if (scope === "team" && reader?.isTeamMember !== true) {
177852
+ throw new Error("Team long-term memory requires an explicit active team identity for this project.");
177853
+ }
177854
+ }
177855
+ function matchesScope(memory, reader) {
177856
+ if (memory.scope === "project") return memory.originProjectId === reader.projectId;
177857
+ if (memory.scope === "team") {
177858
+ return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
177859
+ }
177860
+ if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
177861
+ return memory.portability === "portable" || memory.originProjectId === reader.projectId;
177862
+ }
177863
+ function canReadLongTermMemory(memory, reader) {
177864
+ return matchesScope(memory, reader);
177865
+ }
177866
+ function isEligibleLongTermMemory(memory) {
177867
+ return memory.state === "qualified" || memory.state === "approved";
177868
+ }
177869
+ function taskTerms(task) {
177870
+ const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
177871
+ return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
177872
+ }
177873
+ function kindFitScore(kind, task) {
177874
+ const normalized = task.toLocaleLowerCase("en-US");
177875
+ const hints = {
177876
+ episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
177877
+ semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
177878
+ procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
177879
+ };
177880
+ return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
177881
+ }
177882
+ function validationFreshnessScore(memory) {
177883
+ const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
177884
+ if (!validatedAt) return 0;
177885
+ const time3 = Date.parse(validatedAt);
177886
+ if (!Number.isFinite(time3)) return 0;
177887
+ const ageDays = Math.max(0, Date.now() - time3) / (24 * 60 * 60 * 1e3);
177888
+ if (ageDays <= 30) return 0.35;
177889
+ if (ageDays <= 90) return 0.15;
177890
+ return 0;
177891
+ }
177892
+ function scoreForTask(memory, task) {
177893
+ const terms = taskTerms(task);
177894
+ if (terms.length === 0) return void 0;
177895
+ const title = memory.title.toLocaleLowerCase("en-US");
177896
+ const content = memory.content.toLocaleLowerCase("en-US");
177897
+ const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
177898
+ const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
177899
+ const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
177900
+ let score = memory.state === "approved" ? 0.5 : 0;
177901
+ const matched = [];
177902
+ for (const term of terms) {
177903
+ let termScore = 0;
177904
+ if (title.includes(term)) termScore += 5;
177905
+ if (tags.includes(term)) termScore += 4;
177906
+ if (applicability.includes(term)) termScore += 3;
177907
+ if (facts.includes(term)) termScore += 2;
177908
+ if (content.includes(term)) termScore += 1;
177909
+ if (termScore > 0) {
177910
+ score += termScore;
177911
+ matched.push(term);
177912
+ }
177913
+ }
177914
+ if (matched.length === 0) return void 0;
177915
+ score += kindFitScore(memory.kind, task);
177916
+ score += validationFreshnessScore(memory);
177917
+ const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
177918
+ const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
177919
+ return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
177920
+ }
177921
+ function semanticText(memory) {
177922
+ return sanitizeCredentials([
177923
+ memory.title,
177924
+ memory.applicability,
177925
+ memory.tags.join(" "),
177926
+ memory.facts.join(" "),
177927
+ memory.content
177928
+ ].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
177929
+ }
177930
+ function cosineSimilarity(left, right) {
177931
+ if (left.length === 0 || left.length !== right.length) return 0;
177932
+ let dot = 0;
177933
+ let leftMagnitude = 0;
177934
+ let rightMagnitude = 0;
177935
+ for (let index = 0; index < left.length; index += 1) {
177936
+ const a4 = left[index];
177937
+ const b4 = right[index];
177938
+ if (!Number.isFinite(a4) || !Number.isFinite(b4)) return 0;
177939
+ dot += a4 * b4;
177940
+ leftMagnitude += a4 * a4;
177941
+ rightMagnitude += b4 * b4;
177942
+ }
177943
+ if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
177944
+ return dot / Math.sqrt(leftMagnitude * rightMagnitude);
177945
+ }
177946
+ function semanticSelection(memory, similarity, task) {
177947
+ if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
177948
+ const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
177949
+ const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
177950
+ return {
177951
+ score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
177952
+ reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
177953
+ };
177954
+ }
177955
+ async function resolveSemanticProvider(explicit) {
177956
+ if (explicit !== void 0) return explicit;
177957
+ try {
177958
+ const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
177959
+ return await withTimeout(
177960
+ getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
177961
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS,
177962
+ "Long-term semantic provider initialization"
177963
+ );
177964
+ } catch {
177965
+ return null;
177966
+ }
177967
+ }
177968
+ function createStore(dataDir) {
177969
+ const store2 = new LongTermMemoryStore();
177970
+ return store2.init(dataDir).then(() => store2);
177971
+ }
177972
+ async function createManualLongTermMemory(input) {
177973
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
177974
+ if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
177975
+ assertScope(input.scope);
177976
+ const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID8(), relation: "supports" }];
177977
+ return createLongTermMemoryWithDataDir({
177978
+ dataDir: input.dataDir,
177979
+ originProjectId: input.projectId,
177980
+ ownerId: owner.id,
177981
+ scope: input.scope,
177982
+ kind: input.kind,
177983
+ portability: input.portability,
177984
+ title: input.title,
177985
+ content: input.content,
177986
+ facts: input.facts,
177987
+ tags: input.tags,
177988
+ applicability: input.applicability,
177989
+ origin: "manual",
177990
+ evidence,
177991
+ reader: input.reader
177992
+ });
177993
+ }
177994
+ async function promoteObservationToLongTermMemory(input) {
177995
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
177996
+ if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
177997
+ return createLongTermMemoryWithDataDir({
177998
+ dataDir: input.dataDir,
177999
+ originProjectId: input.observation.projectId,
178000
+ ownerId: owner.id,
178001
+ scope: input.scope,
178002
+ kind: input.kind,
178003
+ portability: "project-bound",
178004
+ title: input.observation.title,
178005
+ content: input.observation.narrative,
178006
+ facts: input.observation.facts,
178007
+ tags: input.tags ?? input.observation.concepts,
178008
+ applicability: input.applicability,
178009
+ origin: input.observation.source === "git" ? "git" : "agent",
178010
+ evidence: [{
178011
+ kind: "observation",
178012
+ referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
178013
+ relation: "derives"
178014
+ }],
178015
+ reader: input.reader
178016
+ });
178017
+ }
178018
+ async function createLongTermMemoryCandidate(input) {
178019
+ return createLongTermMemoryWithDataDir(input);
178020
+ }
178021
+ async function createLongTermMemoryWithDataDir(input) {
178022
+ assertScope(input.scope);
178023
+ assertKind(input.kind);
178024
+ const portability = input.portability ?? "project-bound";
178025
+ assertPortability(portability);
178026
+ requireTeamReader(input.scope, input.reader);
178027
+ const evidenceInputs = normalizeEvidence(input.evidence);
178028
+ assertPortableEvidence(input.scope, portability, evidenceInputs);
178029
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
178030
+ const memory = {
178031
+ id: randomUUID8(),
178032
+ originProjectId: compactText(input.originProjectId, "origin project id", 1e3),
178033
+ ownerId: compactText(input.ownerId, "owner id", 160),
178034
+ scope: input.scope,
178035
+ kind: input.kind,
178036
+ state: "candidate",
178037
+ portability,
178038
+ title: compactText(input.title, "title", 500),
178039
+ content: compactText(input.content, "content"),
178040
+ facts: compactList(input.facts, "facts"),
178041
+ tags: compactList(input.tags, "tags"),
178042
+ ...input.applicability ? { applicability: compactText(input.applicability, "applicability", 1e3) } : {},
178043
+ origin: input.origin ?? "manual",
178044
+ createdAt: now3,
178045
+ updatedAt: now3,
178046
+ accessCount: 0
178047
+ };
178048
+ const store2 = await createStore(input.dataDir);
178049
+ const evidence = store2.transaction(() => {
178050
+ store2.insert(memory);
178051
+ const written = evidenceInputs.map((item) => store2.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
178052
+ store2.recordEvent({
178053
+ memoryId: memory.id,
178054
+ kind: "created",
178055
+ toState: "candidate",
178056
+ detail: "Created as a source-backed long-term memory candidate.",
178057
+ createdAt: now3
178058
+ });
178059
+ return written;
178060
+ });
178061
+ return { memory, evidence };
178062
+ }
178063
+ function transitionDetail(value) {
178064
+ return compactText(value, "transition reason", 1e3);
178065
+ }
178066
+ async function transitionMemory(input) {
178067
+ const store2 = await createStore(input.dataDir);
178068
+ return store2.transaction(() => {
178069
+ const current = store2.get(input.id);
178070
+ if (!current) throw new Error("Long-term memory was not found.");
178071
+ if (current.state !== input.expected) {
178072
+ throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
178073
+ }
178074
+ const evidence = store2.listEvidence(current.id);
178075
+ if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
178076
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
178077
+ const next = {
178078
+ ...current,
178079
+ state: input.next,
178080
+ updatedAt: now3,
178081
+ lastValidatedAt: now3,
178082
+ ...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
178083
+ };
178084
+ store2.update(next);
178085
+ store2.recordEvent({
178086
+ memoryId: next.id,
178087
+ kind: input.event,
178088
+ fromState: current.state,
178089
+ toState: next.state,
178090
+ detail: transitionDetail(input.reason),
178091
+ createdAt: now3
178092
+ });
178093
+ return next;
178094
+ });
178095
+ }
178096
+ async function qualifyLongTermMemory(input) {
178097
+ return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
178098
+ }
178099
+ async function approveLongTermMemory(input) {
178100
+ return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
178101
+ }
178102
+ async function archiveLongTermMemory(input) {
178103
+ const store2 = await createStore(input.dataDir);
178104
+ return store2.transaction(() => {
178105
+ const current = store2.get(input.id);
178106
+ if (!current) throw new Error("Long-term memory was not found.");
178107
+ if (current.state === "archived" || current.state === "superseded") {
178108
+ throw new Error("Long-term memory is already inactive.");
178109
+ }
178110
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
178111
+ const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
178112
+ store2.update(next);
178113
+ store2.recordEvent({
178114
+ memoryId: next.id,
178115
+ kind: "archived",
178116
+ fromState: current.state,
178117
+ toState: "archived",
178118
+ detail: transitionDetail(input.reason),
178119
+ createdAt: now3
178120
+ });
178121
+ return next;
178122
+ });
178123
+ }
178124
+ async function supersedeLongTermMemory(input) {
178125
+ const store2 = await createStore(input.dataDir);
178126
+ return store2.transaction(() => {
178127
+ const current = store2.get(input.id);
178128
+ if (!current) throw new Error("Long-term memory was not found.");
178129
+ if (current.state === "archived" || current.state === "superseded") {
178130
+ throw new Error("Long-term memory is already inactive.");
178131
+ }
178132
+ if (current.id === input.supersededBy) {
178133
+ throw new Error("Long-term memory cannot supersede itself.");
178134
+ }
178135
+ const replacement = store2.get(input.supersededBy);
178136
+ if (!replacement || !isEligibleLongTermMemory(replacement)) {
178137
+ throw new Error("Replacement long-term memory must be qualified or approved.");
178138
+ }
178139
+ if (replacement.scope !== current.scope) {
178140
+ throw new Error("Replacement long-term memory must use the same scope.");
178141
+ }
178142
+ if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
178143
+ throw new Error("User long-term memory may only be superseded by the same local owner.");
178144
+ }
178145
+ if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
178146
+ throw new Error("Project and team long-term memory may only be superseded within the same project.");
178147
+ }
178148
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
178149
+ const next = {
178150
+ ...current,
178151
+ state: "superseded",
178152
+ supersededBy: replacement.id,
178153
+ updatedAt: now3
178154
+ };
178155
+ store2.update(next);
178156
+ store2.recordEvent({
178157
+ memoryId: next.id,
178158
+ kind: "superseded",
178159
+ fromState: current.state,
178160
+ toState: "superseded",
178161
+ detail: transitionDetail(input.reason),
178162
+ createdAt: now3
178163
+ });
178164
+ return next;
178165
+ });
178166
+ }
178167
+ async function maybeAutoQualifyLongTermMemory(input) {
178168
+ if (input.source !== "manual" && input.sourceDetail !== "explicit") return null;
178169
+ try {
178170
+ return await qualifyLongTermMemory({
178171
+ dataDir: input.dataDir,
178172
+ id: input.id,
178173
+ reason: "auto-qualified: explicit request carries its own source evidence"
178174
+ });
178175
+ } catch {
178176
+ return null;
178177
+ }
178178
+ }
178179
+ async function maintainLongTermMemories(input) {
178180
+ const now3 = input.now ?? /* @__PURE__ */ new Date();
178181
+ const decayDays = input.decayDays ?? 30;
178182
+ const cutoff = now3.getTime() - decayDays * 24 * 60 * 60 * 1e3;
178183
+ const result = { archived: 0, superseded: 0 };
178184
+ const store2 = await createStore(input.dataDir);
178185
+ const all = store2.list({ originProjectId: input.projectId });
178186
+ for (const memory of all) {
178187
+ if (memory.state !== "qualified" && memory.state !== "approved") continue;
178188
+ const lastActivity = Date.parse(memory.lastAccessedAt ?? memory.updatedAt);
178189
+ if (!Number.isFinite(lastActivity) || lastActivity >= cutoff) continue;
178190
+ try {
178191
+ await archiveLongTermMemory({
178192
+ dataDir: input.dataDir,
178193
+ id: memory.id,
178194
+ reason: "auto-archive: no activity since " + new Date(lastActivity).toISOString().slice(0, 10)
178195
+ });
178196
+ result.archived += 1;
178197
+ } catch {
178198
+ }
178199
+ }
178200
+ const groups = /* @__PURE__ */ new Map();
178201
+ for (const memory of store2.list({ originProjectId: input.projectId })) {
178202
+ if (memory.state !== "qualified" && memory.state !== "approved") continue;
178203
+ const key = memory.scope + "\0" + memory.kind + "\0" + memory.title.trim().toLowerCase();
178204
+ const group = groups.get(key) ?? [];
178205
+ group.push(memory);
178206
+ groups.set(key, group);
178207
+ }
178208
+ for (const group of groups.values()) {
178209
+ if (group.length < 2) continue;
178210
+ group.sort((a4, b4) => Date.parse(b4.createdAt) - Date.parse(a4.createdAt));
178211
+ const newest = group[0];
178212
+ for (const older of group.slice(1)) {
178213
+ try {
178214
+ await supersedeLongTermMemory({
178215
+ dataDir: input.dataDir,
178216
+ id: older.id,
178217
+ supersededBy: newest.id,
178218
+ reason: "auto-supersede: a newer record shares the same title"
178219
+ });
178220
+ result.superseded += 1;
178221
+ } catch {
178222
+ }
178223
+ }
178224
+ }
178225
+ return result;
178226
+ }
178227
+ async function listLongTermMemories(input) {
178228
+ const store2 = await createStore(input.dataDir);
178229
+ return store2.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: store2.listEvidence(memory.id) }));
178230
+ }
178231
+ async function getLongTermMemoryDetail(input) {
178232
+ const store2 = await createStore(input.dataDir);
178233
+ const memory = store2.get(input.id);
178234
+ if (!memory || !canReadLongTermMemory(memory, input.reader)) {
178235
+ throw new Error("Long-term memory was not found in the active scope.");
178236
+ }
178237
+ store2.markAccess([memory.id]);
178238
+ return { memory, evidence: store2.listEvidence(memory.id), events: store2.listEvents(memory.id) };
178239
+ }
178240
+ async function selectLongTermMemoriesForTask(input) {
178241
+ const task = input.task?.trim();
178242
+ if (!task) return [];
178243
+ const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
178244
+ const reader = {
178245
+ projectId: input.projectId,
178246
+ ...owner ? { ownerId: owner.id } : {},
178247
+ ...input.agentId ? { agentId: input.agentId } : {},
178248
+ ...input.isTeamMember ? { isTeamMember: true } : {}
178249
+ };
178250
+ const store2 = await createStore(input.dataDir);
178251
+ const eligible = store2.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
178252
+ let selected = eligible.map((memory) => {
178253
+ const ranked = scoreForTask(memory, task);
178254
+ return ranked ? {
178255
+ memory,
178256
+ evidence: store2.listEvidence(memory.id),
178257
+ score: ranked.score,
178258
+ reason: ranked.reason
178259
+ } : void 0;
178260
+ }).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));
178261
+ if (selected.length === 0 && eligible.length > 0) {
178262
+ const provider2 = await resolveSemanticProvider(input.embeddingProvider);
178263
+ const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
178264
+ if (provider2 && candidates.length > 0) {
178265
+ try {
178266
+ const vectors = await withTimeout(
178267
+ provider2.embedBatch([task, ...candidates.map(semanticText)], {
178268
+ timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
178269
+ retry: false
178270
+ }),
178271
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS,
178272
+ "Long-term semantic retrieval"
178273
+ );
178274
+ const query = vectors[0];
178275
+ if (Array.isArray(query)) {
178276
+ selected = candidates.map((memory, index) => {
178277
+ const vector = vectors[index + 1];
178278
+ const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
178279
+ return ranked ? {
178280
+ memory,
178281
+ evidence: store2.listEvidence(memory.id),
178282
+ score: ranked.score,
178283
+ reason: ranked.reason
178284
+ } : void 0;
178285
+ }).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));
178286
+ }
178287
+ } catch {
178288
+ }
178289
+ }
178290
+ }
178291
+ selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
178292
+ store2.markAccess(selected.map((item) => item.memory.id));
178293
+ return selected;
178294
+ }
178295
+ function renderLongTermMemorySummary(memory, maxTokens = 22) {
178296
+ const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
178297
+ const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
178298
+ return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
178299
+ }
178300
+ var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
178301
+ var init_long_term = __esm({
178302
+ "src/memory/long-term.ts"() {
178303
+ "use strict";
178304
+ init_esm_shims();
178305
+ init_token_budget();
178306
+ init_timeout();
178307
+ init_secret_filter();
178308
+ init_owner();
178309
+ init_long_term_store();
178310
+ init_long_term_types();
178311
+ MAX_TEXT_LENGTH = 12e3;
178312
+ MAX_LIST_ITEMS = 40;
178313
+ MAX_SEMANTIC_CANDIDATES = 48;
178314
+ SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
178315
+ MIN_SEMANTIC_SIMILARITY = 0.58;
178316
+ PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
178317
+ STOP_TERMS = /* @__PURE__ */ new Set([
178318
+ "the",
178319
+ "and",
178320
+ "for",
178321
+ "with",
178322
+ "from",
178323
+ "that",
178324
+ "this",
178325
+ "into",
178326
+ "when",
178327
+ "what",
178328
+ "\u5982\u4F55",
178329
+ "\u4EC0\u4E48",
178330
+ "\u8FD9\u4E2A",
178331
+ "\u90A3\u4E2A",
178332
+ "\u6211\u4EEC",
178333
+ "\u9879\u76EE",
178334
+ "\u8FDB\u884C",
178335
+ "\u9700\u8981",
178336
+ "\u7EE7\u7EED",
178337
+ "\u5904\u7406"
178338
+ ]);
178339
+ }
178340
+ });
178341
+
177449
178342
  // src/codegraph/ids.ts
177450
178343
  import { createHash as createHash5 } from "crypto";
177451
178344
  function digest(input) {
@@ -177484,7 +178377,7 @@ var store_exports = {};
177484
178377
  __export(store_exports, {
177485
178378
  CodeGraphStore: () => CodeGraphStore
177486
178379
  });
177487
- import { randomUUID as randomUUID6 } from "crypto";
178380
+ import { randomUUID as randomUUID9 } from "crypto";
177488
178381
  import { posix as pathPosix } from "path";
177489
178382
  function rowToFile(row) {
177490
178383
  return {
@@ -178201,7 +179094,7 @@ var init_store = __esm({
178201
179094
  * so an interrupted scan cannot be advertised as complete.
178202
179095
  */
178203
179096
  recordCodeStateSnapshot(input) {
178204
- const id = randomUUID6();
179097
+ const id = randomUUID9();
178205
179098
  let snapshot;
178206
179099
  const tx = this.db.transaction(() => {
178207
179100
  const previous = this.db.prepare(
@@ -178979,8 +179872,8 @@ var claim_store_exports = {};
178979
179872
  __export(claim_store_exports, {
178980
179873
  ClaimStore: () => ClaimStore
178981
179874
  });
178982
- import { createHash as createHash8, randomUUID as randomUUID7 } from "crypto";
178983
- function optionalText2(value) {
179875
+ import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
179876
+ function optionalText3(value) {
178984
179877
  return typeof value === "string" && value ? value : void 0;
178985
179878
  }
178986
179879
  function rowToClaim(row) {
@@ -178996,38 +179889,38 @@ function rowToClaim(row) {
178996
179889
  status: row.status,
178997
179890
  confidence: Number(row.confidence),
178998
179891
  observedAt: row.observedAt,
178999
- ...optionalText2(row.validFrom) ? { validFrom: row.validFrom } : {},
179000
- ...optionalText2(row.validTo) ? { validTo: row.validTo } : {},
179001
- ...optionalText2(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
179892
+ ...optionalText3(row.validFrom) ? { validFrom: row.validFrom } : {},
179893
+ ...optionalText3(row.validTo) ? { validTo: row.validTo } : {},
179894
+ ...optionalText3(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
179002
179895
  reviewState: row.reviewState,
179003
179896
  origin: row.origin,
179004
179897
  createdAt: row.createdAt,
179005
179898
  updatedAt: row.updatedAt
179006
179899
  };
179007
179900
  }
179008
- function rowToEvidence(row) {
179901
+ function rowToEvidence2(row) {
179009
179902
  return {
179010
179903
  id: row.id,
179011
179904
  claimId: row.claimId,
179012
179905
  evidenceKind: row.evidenceKind,
179013
179906
  evidenceId: row.evidenceId,
179014
179907
  relation: row.relation,
179015
- ...optionalText2(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
179016
- ...optionalText2(row.locator) ? { locator: row.locator } : {},
179017
- ...optionalText2(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
179908
+ ...optionalText3(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
179909
+ ...optionalText3(row.locator) ? { locator: row.locator } : {},
179910
+ ...optionalText3(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
179018
179911
  createdAt: row.createdAt
179019
179912
  };
179020
179913
  }
179021
- function rowToEvent(row) {
179914
+ function rowToEvent2(row) {
179022
179915
  return {
179023
179916
  id: row.id,
179024
179917
  projectId: row.projectId,
179025
179918
  claimId: row.claimId,
179026
179919
  kind: row.kind,
179027
- ...optionalText2(row.fromStatus) ? { fromStatus: row.fromStatus } : {},
179028
- ...optionalText2(row.toStatus) ? { toStatus: row.toStatus } : {},
179029
- ...optionalText2(row.relatedClaimId) ? { relatedClaimId: row.relatedClaimId } : {},
179030
- ...optionalText2(row.detail) ? { detail: row.detail } : {},
179920
+ ...optionalText3(row.fromStatus) ? { fromStatus: row.fromStatus } : {},
179921
+ ...optionalText3(row.toStatus) ? { toStatus: row.toStatus } : {},
179922
+ ...optionalText3(row.relatedClaimId) ? { relatedClaimId: row.relatedClaimId } : {},
179923
+ ...optionalText3(row.detail) ? { detail: row.detail } : {},
179031
179924
  createdAt: row.createdAt
179032
179925
  };
179033
179926
  }
@@ -179163,7 +180056,7 @@ var init_claim_store = __esm({
179163
180056
  locator, capturedHash, evidenceKey, createdAt
179164
180057
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
179165
180058
  `).run(
179166
- randomUUID7(),
180059
+ randomUUID10(),
179167
180060
  input.claimId,
179168
180061
  input.evidenceKind,
179169
180062
  input.evidenceId,
@@ -179181,11 +180074,11 @@ var init_claim_store = __esm({
179181
180074
  SELECT * FROM knowledge_claim_evidence
179182
180075
  WHERE claimId = ?
179183
180076
  ORDER BY createdAt ASC, id
179184
- `).all(claimId).map(rowToEvidence);
180077
+ `).all(claimId).map(rowToEvidence2);
179185
180078
  }
179186
180079
  recordEvent(input) {
179187
180080
  const event = {
179188
- id: randomUUID7(),
180081
+ id: randomUUID10(),
179189
180082
  ...input,
179190
180083
  ...input.detail ? { detail: sanitizeCredentials(input.detail) } : {},
179191
180084
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -179213,7 +180106,7 @@ var init_claim_store = __esm({
179213
180106
  SELECT * FROM knowledge_claim_events
179214
180107
  WHERE claimId = ?
179215
180108
  ORDER BY createdAt ASC, id
179216
- `).all(claimId).map(rowToEvent);
180109
+ `).all(claimId).map(rowToEvent2);
179217
180110
  }
179218
180111
  };
179219
180112
  }
@@ -179230,8 +180123,8 @@ __export(claims_exports, {
179230
180123
  supersedeClaim: () => supersedeClaim,
179231
180124
  writeClaim: () => writeClaim
179232
180125
  });
179233
- import { createHash as createHash9, randomUUID as randomUUID8 } from "crypto";
179234
- function compactText(value, field) {
180126
+ import { createHash as createHash9, randomUUID as randomUUID11 } from "crypto";
180127
+ function compactText2(value, field) {
179235
180128
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
179236
180129
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
179237
180130
  if (!sanitized) throw new Error("Claim " + field + " is required");
@@ -179239,7 +180132,7 @@ function compactText(value, field) {
179239
180132
  return sanitized;
179240
180133
  }
179241
180134
  function normalizedTerm(value) {
179242
- return compactText(value, "identity").toLocaleLowerCase("en-US");
180135
+ return compactText2(value, "identity").toLocaleLowerCase("en-US");
179243
180136
  }
179244
180137
  function hashIdentity(parts) {
179245
180138
  return createHash9("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
@@ -179268,7 +180161,7 @@ function validateEvidence(input) {
179268
180161
  if (!evidence || typeof evidence !== "object") {
179269
180162
  throw new Error("Claim evidence must be an object");
179270
180163
  }
179271
- const evidenceId = compactText(evidence.evidenceId, "evidence id");
180164
+ const evidenceId = compactText2(evidence.evidenceId, "evidence id");
179272
180165
  if (evidenceId.length > MAX_EVIDENCE_ID_LENGTH) {
179273
180166
  throw new Error("Claim evidence id is too long");
179274
180167
  }
@@ -179282,9 +180175,9 @@ function validateEvidence(input) {
179282
180175
  evidenceKind: evidence.evidenceKind,
179283
180176
  evidenceId,
179284
180177
  relation: evidence.relation,
179285
- ...evidence.snapshotId ? { snapshotId: compactText(evidence.snapshotId, "evidence snapshot id") } : {},
179286
- ...evidence.locator ? { locator: compactText(evidence.locator, "evidence locator") } : {},
179287
- ...evidence.capturedHash ? { capturedHash: compactText(evidence.capturedHash, "evidence hash") } : {}
180178
+ ...evidence.snapshotId ? { snapshotId: compactText2(evidence.snapshotId, "evidence snapshot id") } : {},
180179
+ ...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator") } : {},
180180
+ ...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash") } : {}
179288
180181
  };
179289
180182
  });
179290
180183
  }
@@ -179292,10 +180185,10 @@ function normalizeClaimInput(input) {
179292
180185
  if (!["project", "workspace", "team", "workflow", "task"].includes(input.scope)) {
179293
180186
  throw new Error("Claim scope is invalid");
179294
180187
  }
179295
- const projectId = compactText(input.projectId, "project id");
179296
- const subject = compactText(input.subject, "subject");
179297
- const predicate = compactText(input.predicate, "predicate");
179298
- const objectValue = compactText(input.objectValue, "object value");
180188
+ const projectId = compactText2(input.projectId, "project id");
180189
+ const subject = compactText2(input.subject, "subject");
180190
+ const predicate = compactText2(input.predicate, "predicate");
180191
+ const objectValue = compactText2(input.objectValue, "object value");
179299
180192
  const identity = buildClaimIdentity({ subject, predicate, objectValue, scope: input.scope });
179300
180193
  const origin = input.origin ?? "explicit";
179301
180194
  let reviewState = input.reviewState ?? "approved";
@@ -179316,7 +180209,7 @@ function normalizeClaimInput(input) {
179316
180209
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
179317
180210
  return {
179318
180211
  claim: {
179319
- id: randomUUID8(),
180212
+ id: randomUUID11(),
179320
180213
  projectId,
179321
180214
  subject,
179322
180215
  predicate,
@@ -179416,7 +180309,7 @@ function reviewClaim(store2, input) {
179416
180309
  if (input.reviewState !== "approved" && input.reviewState !== "rejected") {
179417
180310
  throw new Error("A claim review must approve or reject the claim");
179418
180311
  }
179419
- const detail = compactText(input.detail, "review detail");
180312
+ const detail = compactText2(input.detail, "review detail");
179420
180313
  return store2.transaction(() => {
179421
180314
  const claim = store2.getClaim(input.claimId);
179422
180315
  if (!claim) throw new Error("Claim was not found");
@@ -179728,8 +180621,8 @@ var init_frontmatter = __esm({
179728
180621
 
179729
180622
  // src/knowledge/markdown.ts
179730
180623
  import { createHash as createHash10 } from "crypto";
179731
- import { promises as fs11 } from "fs";
179732
- import path18 from "path";
180624
+ import { promises as fs12 } from "fs";
180625
+ import path19 from "path";
179733
180626
  function hashContent(content) {
179734
180627
  return createHash10("sha256").update(content).digest("hex");
179735
180628
  }
@@ -179787,8 +180680,8 @@ function extractInternalMarkdownLinks(body) {
179787
180680
  return [...links].sort();
179788
180681
  }
179789
180682
  function resolvePageLink(sourceRelativePath, target) {
179790
- const sourceDir = path18.posix.dirname(sourceRelativePath.replace(/\\/g, "/"));
179791
- const normalized = path18.posix.normalize(path18.posix.join(sourceDir, target.replace(/\\/g, "/")));
180683
+ const sourceDir = path19.posix.dirname(sourceRelativePath.replace(/\\/g, "/"));
180684
+ const normalized = path19.posix.normalize(path19.posix.join(sourceDir, target.replace(/\\/g, "/")));
179792
180685
  if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../")) return void 0;
179793
180686
  return normalized;
179794
180687
  }
@@ -179810,13 +180703,13 @@ function renderKnowledgePage(frontmatter, body) {
179810
180703
  });
179811
180704
  }
179812
180705
  async function readKnowledgePage(absolutePath, workspaceRoot) {
179813
- const raw = await fs11.readFile(absolutePath, "utf8");
180706
+ const raw = await fs12.readFile(absolutePath, "utf8");
179814
180707
  try {
179815
180708
  const parsed = frontmatter_default(raw);
179816
180709
  const frontmatter = validateKnowledgePageFrontmatter(parsed.data);
179817
180710
  return {
179818
180711
  absolutePath,
179819
- relativePath: workspaceRoot ? path18.relative(workspaceRoot, absolutePath).split(path18.sep).join("/") : path18.basename(absolutePath),
180712
+ relativePath: workspaceRoot ? path19.relative(workspaceRoot, absolutePath).split(path19.sep).join("/") : path19.basename(absolutePath),
179820
180713
  frontmatter,
179821
180714
  body: parsed.content.trim(),
179822
180715
  contentHash: hashContent(raw),
@@ -179828,7 +180721,7 @@ async function readKnowledgePage(absolutePath, workspaceRoot) {
179828
180721
  }
179829
180722
  }
179830
180723
  async function writeKnowledgePage(absolutePath, content) {
179831
- await fs11.mkdir(path18.dirname(absolutePath), { recursive: true });
180724
+ await fs12.mkdir(path19.dirname(absolutePath), { recursive: true });
179832
180725
  await atomicWriteFile(absolutePath, content);
179833
180726
  return hashContent(content);
179834
180727
  }
@@ -179858,8 +180751,8 @@ __export(wiki_exports, {
179858
180751
  readKnowledgePage: () => readKnowledgePage
179859
180752
  });
179860
180753
  import { createHash as createHash11 } from "crypto";
179861
- import { promises as fs12 } from "fs";
179862
- import path19 from "path";
180754
+ import { promises as fs13 } from "fs";
180755
+ import path20 from "path";
179863
180756
  function hash2(value) {
179864
180757
  return createHash11("sha256").update(value).digest("hex");
179865
180758
  }
@@ -179991,7 +180884,7 @@ function candidatePage(workspace, title, claims, store2) {
179991
180884
  }
179992
180885
  async function fileHashIfPresent(filePath) {
179993
180886
  try {
179994
- return pageContentHash(await fs12.readFile(filePath, "utf8"));
180887
+ return pageContentHash(await fs13.readFile(filePath, "utf8"));
179995
180888
  } catch (error40) {
179996
180889
  const code = error40 && typeof error40 === "object" && "code" in error40 ? error40.code : void 0;
179997
180890
  if (code === "ENOENT") return void 0;
@@ -180015,8 +180908,8 @@ async function writeIndex(workspace, store2) {
180015
180908
  lines.push("No pending proposals.");
180016
180909
  } else {
180017
180910
  for (const proposal of pending) {
180018
- const relative3 = path19.relative(paths.root, proposal.proposalPath).split(path19.sep).join("/");
180019
- lines.push("- [" + path19.basename(proposal.proposalPath, ".md") + "](" + relative3 + ")");
180911
+ const relative3 = path20.relative(paths.root, proposal.proposalPath).split(path20.sep).join("/");
180912
+ lines.push("- [" + path20.basename(proposal.proposalPath, ".md") + "](" + relative3 + ")");
180020
180913
  }
180021
180914
  }
180022
180915
  lines.push("");
@@ -180024,7 +180917,7 @@ async function writeIndex(workspace, store2) {
180024
180917
  }
180025
180918
  async function appendLog(workspace, line) {
180026
180919
  const logPath = getKnowledgeWorkspacePaths(workspace).log;
180027
- await fs12.appendFile(logPath, "- " + now() + " " + line + "\n", "utf8");
180920
+ await fs13.appendFile(logPath, "- " + now() + " " + line + "\n", "utf8");
180028
180921
  }
180029
180922
  function toPageRecord(workspace, page, status, contentHash, manualContentHash) {
180030
180923
  return {
@@ -180109,9 +181002,9 @@ async function applyKnowledgeProposal(input) {
180109
181002
  }
180110
181003
  if (proposal.status !== "pending") throw new Error("Knowledge proposal is not pending review");
180111
181004
  const root = getKnowledgeWorkspacePaths(input.workspace).root;
180112
- const targetPath = path19.resolve(proposal.targetPath);
180113
- const proposalPath = path19.resolve(proposal.proposalPath);
180114
- if (!targetPath.startsWith(root + path19.sep) || !proposalPath.startsWith(root + path19.sep)) {
181005
+ const targetPath = path20.resolve(proposal.targetPath);
181006
+ const proposalPath = path20.resolve(proposal.proposalPath);
181007
+ if (!targetPath.startsWith(root + path20.sep) || !proposalPath.startsWith(root + path20.sep)) {
180115
181008
  throw new Error("Knowledge proposal path escapes its workspace");
180116
181009
  }
180117
181010
  await withFileLock(root, async () => {
@@ -180149,12 +181042,12 @@ async function markdownFiles(directory) {
180149
181042
  const files = [];
180150
181043
  let entries;
180151
181044
  try {
180152
- entries = await fs12.readdir(directory, { withFileTypes: true });
181045
+ entries = await fs13.readdir(directory, { withFileTypes: true });
180153
181046
  } catch {
180154
181047
  return files;
180155
181048
  }
180156
181049
  for (const entry of entries) {
180157
- const filePath = path19.join(directory, entry.name);
181050
+ const filePath = path20.join(directory, entry.name);
180158
181051
  if (entry.isDirectory()) {
180159
181052
  files.push(...await markdownFiles(filePath));
180160
181053
  } else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
@@ -180177,13 +181070,13 @@ async function lintKnowledgeWorkspace(input) {
180177
181070
  kind: "malformed-frontmatter",
180178
181071
  severity: "error",
180179
181072
  message: error40 instanceof Error ? error40.message : "Malformed knowledge page.",
180180
- relativePath: path19.relative(paths.root, filePath).split(path19.sep).join("/")
181073
+ relativePath: path20.relative(paths.root, filePath).split(path20.sep).join("/")
180181
181074
  });
180182
181075
  }
180183
181076
  }
180184
181077
  const indexed = /* @__PURE__ */ new Set();
180185
181078
  try {
180186
- const index = await fs12.readFile(paths.index, "utf8");
181079
+ const index = await fs13.readFile(paths.index, "utf8");
180187
181080
  for (const link of extractInternalMarkdownLinks(index)) {
180188
181081
  const resolved = resolvePageLink("index.md", link);
180189
181082
  if (resolved) indexed.add(resolved);
@@ -180272,7 +181165,7 @@ async function lintKnowledgeWorkspace(input) {
180272
181165
  kind: "manual-edit-protected",
180273
181166
  severity: "warning",
180274
181167
  message: "A newer proposal was held because the target page has manual edits.",
180275
- relativePath: path19.relative(paths.root, proposal.targetPath).split(path19.sep).join("/")
181168
+ relativePath: path20.relative(paths.root, proposal.targetPath).split(path20.sep).join("/")
180276
181169
  });
180277
181170
  }
180278
181171
  }
@@ -180405,7 +181298,7 @@ var workflow_store_exports = {};
180405
181298
  __export(workflow_store_exports, {
180406
181299
  WorkflowStore: () => WorkflowStore
180407
181300
  });
180408
- import { randomUUID as randomUUID9 } from "crypto";
181301
+ import { randomUUID as randomUUID12 } from "crypto";
180409
181302
  function parseJson(value, fallback) {
180410
181303
  if (typeof value !== "string" || !value) return fallback;
180411
181304
  try {
@@ -180414,7 +181307,7 @@ function parseJson(value, fallback) {
180414
181307
  return fallback;
180415
181308
  }
180416
181309
  }
180417
- function optionalText3(value) {
181310
+ function optionalText4(value) {
180418
181311
  return typeof value === "string" && value.trim() ? value : void 0;
180419
181312
  }
180420
181313
  function rowToWorkflow(row) {
@@ -180451,14 +181344,14 @@ function rowToRun(row) {
180451
181344
  workflowId: row.workflowId,
180452
181345
  projectId: row.projectId,
180453
181346
  task: row.task,
180454
- ...optionalText3(row.startingSnapshotId) ? { startingSnapshotId: row.startingSnapshotId } : {},
181347
+ ...optionalText4(row.startingSnapshotId) ? { startingSnapshotId: row.startingSnapshotId } : {},
180455
181348
  selectedEvidence: parseJson(row.selectedEvidenceJson, []),
180456
181349
  phaseState: parseJson(row.phaseStateJson, {}),
180457
181350
  outcome: row.outcome,
180458
181351
  verificationVerdict: row.verificationVerdict,
180459
- ...optionalText3(row.failureReason) ? { failureReason: row.failureReason } : {},
181352
+ ...optionalText4(row.failureReason) ? { failureReason: row.failureReason } : {},
180460
181353
  startedAt: row.startedAt,
180461
- ...optionalText3(row.completedAt) ? { completedAt: row.completedAt } : {}
181354
+ ...optionalText4(row.completedAt) ? { completedAt: row.completedAt } : {}
180462
181355
  };
180463
181356
  }
180464
181357
  var WorkflowStore;
@@ -180505,7 +181398,7 @@ var init_workflow_store = __esm({
180505
181398
  }
180506
181399
  recordRun(input) {
180507
181400
  const run = {
180508
- id: randomUUID9(),
181401
+ id: randomUUID12(),
180509
181402
  workflowId: input.workflowId,
180510
181403
  projectId: input.projectId,
180511
181404
  task: input.task.trim(),
@@ -180583,8 +181476,8 @@ var init_outcome_types = __esm({
180583
181476
  });
180584
181477
 
180585
181478
  // src/knowledge/outcome-store.ts
180586
- import { randomUUID as randomUUID10 } from "crypto";
180587
- function optionalText4(value) {
181479
+ import { randomUUID as randomUUID13 } from "crypto";
181480
+ function optionalText5(value) {
180588
181481
  return typeof value === "string" && value.trim() ? value : void 0;
180589
181482
  }
180590
181483
  function text(value, field, max = 1e3) {
@@ -180607,8 +181500,8 @@ function rowToSignal(row) {
180607
181500
  candidateId: row.candidateId,
180608
181501
  kind: row.kind,
180609
181502
  sourceRef: row.sourceRef,
180610
- ...optionalText4(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
180611
- ...optionalText4(row.detail) ? { detail: row.detail } : {},
181503
+ ...optionalText5(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
181504
+ ...optionalText5(row.detail) ? { detail: row.detail } : {},
180612
181505
  observedAt: row.observedAt
180613
181506
  };
180614
181507
  }
@@ -180627,7 +181520,7 @@ var init_outcome_store = __esm({
180627
181520
  }
180628
181521
  record(input) {
180629
181522
  const signal = {
180630
- id: randomUUID10(),
181523
+ id: randomUUID13(),
180631
181524
  projectId: text(input.projectId, "project id"),
180632
181525
  candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
180633
181526
  candidateId: text(input.candidateId, "candidate id"),
@@ -181054,8 +181947,8 @@ __export(workflows_exports, {
181054
181947
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
181055
181948
  });
181056
181949
  import { createHash as createHash12 } from "crypto";
181057
- import { promises as fs13 } from "fs";
181058
- import path20 from "path";
181950
+ import { promises as fs14 } from "fs";
181951
+ import path21 from "path";
181059
181952
  function hash3(value) {
181060
181953
  return createHash12("sha256").update(value).digest("hex");
181061
181954
  }
@@ -181076,7 +181969,7 @@ function requiredText(data, key) {
181076
181969
  }
181077
181970
  return value.trim();
181078
181971
  }
181079
- function optionalText5(data, key) {
181972
+ function optionalText6(data, key) {
181080
181973
  const value = data[key];
181081
181974
  if (value === void 0 || value === null || value === "") return void 0;
181082
181975
  if (typeof value !== "string") throw new Error("workflow frontmatter field " + key + " must be text");
@@ -181119,11 +182012,11 @@ function phaseFromValue(value, index) {
181119
182012
  }
181120
182013
  const data = value;
181121
182014
  const title = requiredText(data, "title");
181122
- const phaseId = optionalText5(data, "id") ?? slug(title) + "-" + (index + 1);
182015
+ const phaseId = optionalText6(data, "id") ?? slug(title) + "-" + (index + 1);
181123
182016
  return {
181124
182017
  id: phaseId,
181125
182018
  title,
181126
- instructions: optionalText5(data, "instructions") ?? "",
182019
+ instructions: optionalText6(data, "instructions") ?? "",
181127
182020
  branches: optionalStringArray(data, "branches"),
181128
182021
  expectedOutputs: optionalStringArray(data, "expectedOutputs"),
181129
182022
  verificationGates: optionalStringArray(data, "verificationGates")
@@ -181158,7 +182051,7 @@ function phasesFromBody(body) {
181158
182051
  }
181159
182052
  function normalizeSourcePath(sourcePath) {
181160
182053
  const normalized = sourcePath.replace(/\\/g, "/");
181161
- if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") || path20.posix.normalize(normalized) !== normalized) {
182054
+ if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") || path21.posix.normalize(normalized) !== normalized) {
181162
182055
  throw new Error("workflow source path must be a safe workflows/*.md path");
181163
182056
  }
181164
182057
  return normalized;
@@ -181232,7 +182125,7 @@ function parsedWorkflow(data, body, input) {
181232
182125
  id: requiredText(data, "id"),
181233
182126
  workspaceId: input.workspaceId,
181234
182127
  title: requiredText(data, "title"),
181235
- description: optionalText5(data, "description") ?? "",
182128
+ description: optionalText6(data, "description") ?? "",
181236
182129
  status: optionalStatus(data),
181237
182130
  version: optionalNumber(data, "version", 1),
181238
182131
  taskLenses: optionalStringArray(data, "taskLenses"),
@@ -181251,9 +182144,9 @@ function parsedWorkflow(data, body, input) {
181251
182144
  sourcePath: normalizeSourcePath(input.sourcePath),
181252
182145
  sourceHash: "",
181253
182146
  contentHash: input.contentHash,
181254
- createdAt: optionalText5(data, "createdAt") ?? now2(),
181255
- updatedAt: optionalText5(data, "updatedAt") ?? now2(),
181256
- ...optionalText5(data, "importedFrom") ? { importedFrom: optionalText5(data, "importedFrom") } : {}
182147
+ createdAt: optionalText6(data, "createdAt") ?? now2(),
182148
+ updatedAt: optionalText6(data, "updatedAt") ?? now2(),
182149
+ ...optionalText6(data, "importedFrom") ? { importedFrom: optionalText6(data, "importedFrom") } : {}
181257
182150
  };
181258
182151
  if (candidate2.phases.length === 0) {
181259
182152
  throw new Error("workflow must contain at least one phase or a non-empty body");
@@ -181307,7 +182200,7 @@ async function writeCanonicalWorkflow(input) {
181307
182200
  const filePath = resolveKnowledgeWorkspaceFile(input.workspace, workflow.sourcePath);
181308
182201
  const content = renderWorkflowMarkdown(workflow);
181309
182202
  await withFileLock(paths.root, async () => {
181310
- await fs13.mkdir(path20.dirname(filePath), { recursive: true });
182203
+ await fs14.mkdir(path21.dirname(filePath), { recursive: true });
181311
182204
  await atomicWriteFile(filePath, content);
181312
182205
  });
181313
182206
  const store2 = await storeFor2(input.workspace);
@@ -181316,13 +182209,13 @@ async function writeCanonicalWorkflow(input) {
181316
182209
  async function markdownFiles2(directory) {
181317
182210
  let entries;
181318
182211
  try {
181319
- entries = await fs13.readdir(directory, { withFileTypes: true });
182212
+ entries = await fs14.readdir(directory, { withFileTypes: true });
181320
182213
  } catch {
181321
182214
  return [];
181322
182215
  }
181323
182216
  const files = [];
181324
182217
  for (const entry of entries) {
181325
- const absolutePath = path20.join(directory, entry.name);
182218
+ const absolutePath = path21.join(directory, entry.name);
181326
182219
  if (entry.isDirectory()) {
181327
182220
  files.push(...await markdownFiles2(absolutePath));
181328
182221
  } else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
@@ -181337,9 +182230,9 @@ async function syncCanonicalWorkflows(workspace) {
181337
182230
  const workflows = [];
181338
182231
  const errors2 = [];
181339
182232
  for (const absolutePath of await markdownFiles2(paths.workflows)) {
181340
- const sourcePath = path20.relative(paths.root, absolutePath).split(path20.sep).join("/");
182233
+ const sourcePath = path21.relative(paths.root, absolutePath).split(path21.sep).join("/");
181341
182234
  try {
181342
- const raw = await fs13.readFile(absolutePath, "utf8");
182235
+ const raw = await fs14.readFile(absolutePath, "utf8");
181343
182236
  const workflow = parseWorkflowMarkdown(raw, { workspaceId: workspace.id, sourcePath });
181344
182237
  workflows.push(store2.upsertWorkflow(workflow));
181345
182238
  } catch (error40) {
@@ -181451,21 +182344,21 @@ function importedWorkflowSpec(input) {
181451
182344
  return materializeWorkflow(spec);
181452
182345
  }
181453
182346
  async function importWindsurfWorkflows(input) {
181454
- const sourceDirectory = path20.join(path20.resolve(input.projectRoot), ".windsurf", "workflows");
182347
+ const sourceDirectory = path21.join(path21.resolve(input.projectRoot), ".windsurf", "workflows");
181455
182348
  const paths = getKnowledgeWorkspacePaths(input.workspace);
181456
182349
  const imported = [];
181457
182350
  const skipped = [];
181458
182351
  let entries;
181459
182352
  try {
181460
- entries = await fs13.readdir(sourceDirectory, { withFileTypes: true });
182353
+ entries = await fs14.readdir(sourceDirectory, { withFileTypes: true });
181461
182354
  } catch {
181462
182355
  return { imported, skipped };
181463
182356
  }
181464
182357
  for (const entry of entries.filter((item) => item.isFile() && item.name.toLowerCase().endsWith(".md"))) {
181465
- const originalPath = path20.join(sourceDirectory, entry.name);
181466
- const sourcePath = path20.relative(path20.resolve(input.projectRoot), originalPath).split(path20.sep).join("/");
182358
+ const originalPath = path21.join(sourceDirectory, entry.name);
182359
+ const sourcePath = path21.relative(path21.resolve(input.projectRoot), originalPath).split(path21.sep).join("/");
181467
182360
  try {
181468
- const raw = await fs13.readFile(originalPath, "utf8");
182361
+ const raw = await fs14.readFile(originalPath, "utf8");
181469
182362
  const workflow = importedWorkflowSpec({
181470
182363
  workspace: input.workspace,
181471
182364
  sourceName: entry.name,
@@ -181474,7 +182367,7 @@ async function importWindsurfWorkflows(input) {
181474
182367
  });
181475
182368
  const targetPath = resolveKnowledgeWorkspaceFile(input.workspace, workflow.sourcePath);
181476
182369
  try {
181477
- await fs13.access(targetPath);
182370
+ await fs14.access(targetPath);
181478
182371
  skipped.push({ sourcePath, reason: "canonical workflow already exists and was preserved" });
181479
182372
  continue;
181480
182373
  } catch {
@@ -181487,12 +182380,12 @@ async function importWindsurfWorkflows(input) {
181487
182380
  });
181488
182381
  }
181489
182382
  }
181490
- await fs13.mkdir(paths.workflows, { recursive: true });
182383
+ await fs14.mkdir(paths.workflows, { recursive: true });
181491
182384
  return { imported, skipped };
181492
182385
  }
181493
182386
  function isWithin2(root, candidate2) {
181494
- const relative3 = path20.relative(root, candidate2);
181495
- return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(".." + path20.sep) && !path20.isAbsolute(relative3);
182387
+ const relative3 = path21.relative(root, candidate2);
182388
+ return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(".." + path21.sep) && !path21.isAbsolute(relative3);
181496
182389
  }
181497
182390
  function adapterRelativePath(workflow, agent) {
181498
182391
  const name = "memorix-workflow-" + slug(workflow.id.replace(/^workflow:/, ""));
@@ -181578,15 +182471,15 @@ async function previewWorkflowAdapter(input) {
181578
182471
  reason: "The canonical workflow does not declare compatibility with this agent."
181579
182472
  };
181580
182473
  }
181581
- const projectRoot = path20.resolve(input.projectRoot);
181582
- const targetPath = path20.resolve(projectRoot, adapterRelativePath(input.workflow, input.agent));
182474
+ const projectRoot = path21.resolve(input.projectRoot);
182475
+ const targetPath = path21.resolve(projectRoot, adapterRelativePath(input.workflow, input.agent));
181583
182476
  if (!isWithin2(projectRoot, targetPath)) {
181584
182477
  throw new Error("Workflow adapter target escapes the project root");
181585
182478
  }
181586
182479
  const content = adapterContent(input.workflow, input.agent);
181587
182480
  let existing;
181588
182481
  try {
181589
- existing = await fs13.readFile(targetPath, "utf8");
182482
+ existing = await fs14.readFile(targetPath, "utf8");
181590
182483
  } catch (error40) {
181591
182484
  const code = error40 && typeof error40 === "object" && "code" in error40 ? error40.code : void 0;
181592
182485
  if (code !== "ENOENT") throw error40;
@@ -181607,11 +182500,11 @@ async function applyWorkflowAdapter(input) {
181607
182500
  if (initial.status === "unsupported" || initial.status === "conflict" || initial.status === "unchanged") {
181608
182501
  return initial;
181609
182502
  }
181610
- const projectRoot = path20.resolve(input.projectRoot);
182503
+ const projectRoot = path21.resolve(input.projectRoot);
181611
182504
  return withFileLock(projectRoot, async () => {
181612
182505
  const current = await previewWorkflowAdapter(input);
181613
182506
  if (current.status !== "create" && current.status !== "update") return current;
181614
- await fs13.mkdir(path20.dirname(current.targetPath), { recursive: true });
182507
+ await fs14.mkdir(path21.dirname(current.targetPath), { recursive: true });
181615
182508
  await atomicWriteFile(current.targetPath, current.content);
181616
182509
  return current;
181617
182510
  });
@@ -181901,6 +182794,11 @@ function createProjectMaintenanceHandler(projectId, projectDir2, projectRoot, op
181901
182794
  }
181902
182795
  return { action: "complete" };
181903
182796
  }
182797
+ if (job.kind === "long-term-maintenance") {
182798
+ const { maintainLongTermMemories: maintainLongTermMemories2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
182799
+ await maintainLongTermMemories2({ dataDir: projectDir2, projectId });
182800
+ return { action: "reschedule", delayMs: 24 * 60 * 60 * 1e3, resetAttempts: true };
182801
+ }
181904
182802
  if (job.kind === "consolidation") {
181905
182803
  const limit2 = consolidationBatchSize(job.payload);
181906
182804
  const result2 = await runAutomaticConsolidation(projectId, projectDir2, {
@@ -182242,15 +183140,15 @@ __export(vector_backfill_runner_exports, {
182242
183140
  });
182243
183141
  import { spawn as spawn4 } from "child_process";
182244
183142
  import { existsSync as existsSync12 } from "fs";
182245
- import path21 from "path";
183143
+ import path22 from "path";
182246
183144
  import { fileURLToPath as fileURLToPath6 } from "url";
182247
183145
  function isNonEmptyString(value) {
182248
183146
  return typeof value === "string" && value.trim().length > 0;
182249
183147
  }
182250
183148
  function resolveVectorBackfillRunnerPath(moduleUrl = import.meta.url) {
182251
- const moduleDir = path21.dirname(fileURLToPath6(moduleUrl));
182252
- const distDir = path21.basename(moduleDir) === "cli" ? path21.dirname(moduleDir) : path21.basename(moduleDir) === "runtime" && path21.basename(path21.dirname(moduleDir)) === "src" ? path21.join(path21.dirname(path21.dirname(moduleDir)), "dist") : moduleDir;
182253
- return path21.join(distDir, "vector-backfill-runner.js");
183149
+ const moduleDir = path22.dirname(fileURLToPath6(moduleUrl));
183150
+ 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;
183151
+ return path22.join(distDir, "vector-backfill-runner.js");
182254
183152
  }
182255
183153
  function parseVectorBackfillRequest(raw) {
182256
183154
  let value;
@@ -182259,7 +183157,7 @@ function parseVectorBackfillRequest(raw) {
182259
183157
  } catch {
182260
183158
  throw new Error("Vector backfill runner received invalid JSON input");
182261
183159
  }
182262
- if (!value || typeof value !== "object" || !isNonEmptyString(value.projectId) || !isNonEmptyString(value.projectRoot) || !isNonEmptyString(value.dataDir) || !path21.isAbsolute(value.projectRoot) || !path21.isAbsolute(value.dataDir)) {
183160
+ if (!value || typeof value !== "object" || !isNonEmptyString(value.projectId) || !isNonEmptyString(value.projectRoot) || !isNonEmptyString(value.dataDir) || !path22.isAbsolute(value.projectRoot) || !path22.isAbsolute(value.dataDir)) {
182263
183161
  throw new Error("Vector backfill runner received an invalid request");
182264
183162
  }
182265
183163
  return {
@@ -183783,7 +184681,7 @@ async function searchObservations(options) {
183783
184681
  const expandedEmbeddingQuery = quality === "thorough" && tier === "heavy" ? await maybeExpandSearchQuery(options.query) : options.query;
183784
184682
  mark("queryExpansion");
183785
184683
  const intentResult = quality === "fast" || !hasQuery ? null : detectQueryIntent(originalQuery);
183786
- const requestLimit = projectIds ? (options.limit ?? 20) * 3 : options.limit ?? 20;
184684
+ const requestLimit = projectIds || options.surfacedIds && options.surfacedIds.length > 0 ? (options.limit ?? 20) * 3 : options.limit ?? 20;
183787
184685
  const defaultBoost = {
183788
184686
  title: 3,
183789
184687
  entityName: 2,
@@ -184015,6 +184913,14 @@ async function searchObservations(options) {
184015
184913
  });
184016
184914
  intermediate.sort((a4, b4) => b4.score - a4.score);
184017
184915
  }
184916
+ if (options.surfacedIds && options.surfacedIds.length > 0) {
184917
+ const SURFACED_PENALTY = 0.5;
184918
+ const surfaced = new Set(options.surfacedIds);
184919
+ intermediate = intermediate.map(
184920
+ (entry) => surfaced.has(entry.id) ? { ...entry, score: (entry.score ?? 0) * SURFACED_PENALTY } : entry
184921
+ );
184922
+ intermediate.sort((a4, b4) => (b4.score ?? 0) - (a4.score ?? 0));
184923
+ }
184018
184924
  if (options.since) {
184019
184925
  const sinceDate = new Date(options.since).getTime();
184020
184926
  intermediate = intermediate.filter((e5) => new Date(e5.rawTime).getTime() >= sinceDate);
@@ -185021,9 +185927,9 @@ __export(team_store_exports, {
185021
185927
  isTeamStoreInitialized: () => isTeamStoreInitialized,
185022
185928
  resetTeamStore: () => resetTeamStore
185023
185929
  });
185024
- import { randomUUID as randomUUID11 } from "crypto";
185025
- import path22 from "path";
185026
- import fs14 from "fs";
185930
+ import { randomUUID as randomUUID14 } from "crypto";
185931
+ import path23 from "path";
185932
+ import fs15 from "fs";
185027
185933
  function getTeamStore() {
185028
185934
  if (!_teamStore) {
185029
185935
  throw new Error("[memorix] TeamStore not initialized \u2014 call initTeamStore() first");
@@ -185310,7 +186216,7 @@ var init_team_store = __esm({
185310
186216
  * reactivate it (preserving agent_id). Otherwise create a new one.
185311
186217
  */
185312
186218
  registerAgent(input) {
185313
- const instanceId = input.instanceId || randomUUID11();
186219
+ const instanceId = input.instanceId || randomUUID14();
185314
186220
  const now3 = Date.now();
185315
186221
  const existing = this.stmtAgentFindByInstance.get(
185316
186222
  input.projectId,
@@ -185336,7 +186242,7 @@ var init_team_store = __esm({
185336
186242
  this.eventBus?.emit("agent:joined", { agentId: row2.agent_id, projectId: input.projectId, agentType: input.agentType });
185337
186243
  return row2;
185338
186244
  }
185339
- const agentId = randomUUID11();
186245
+ const agentId = randomUUID14();
185340
186246
  this.stmtAgentUpsert.run({
185341
186247
  agent_id: agentId,
185342
186248
  project_id: input.projectId,
@@ -185438,7 +186344,7 @@ var init_team_store = __esm({
185438
186344
  return { error: `Recipient agent '${input.recipientAgentId}' not found \u2014 cannot send to unknown agent` };
185439
186345
  }
185440
186346
  }
185441
- const id = randomUUID11();
186347
+ const id = randomUUID14();
185442
186348
  const now3 = Date.now();
185443
186349
  const row = {
185444
186350
  id,
@@ -185488,7 +186394,7 @@ var init_team_store = __esm({
185488
186394
  // Task Operations (with atomic claim semantics)
185489
186395
  // ═══════════════════════════════════════════════════════════════════
185490
186396
  createTask(input) {
185491
- const taskId = randomUUID11();
186397
+ const taskId = randomUUID14();
185492
186398
  const now3 = Date.now();
185493
186399
  const row = {
185494
186400
  task_id: taskId,
@@ -185713,19 +186619,19 @@ var init_team_store = __esm({
185713
186619
  const agentCount = this.db.prepare(`SELECT COUNT(*) AS cnt FROM team_agents`).get();
185714
186620
  if (agentCount.cnt > 0) return;
185715
186621
  const candidates = [
185716
- path22.join(this.dataDir, "team-state.json"),
185717
- path22.join(this.dataDir, "..", "team-state.json")
186622
+ path23.join(this.dataDir, "team-state.json"),
186623
+ path23.join(this.dataDir, "..", "team-state.json")
185718
186624
  ];
185719
186625
  let jsonPath = null;
185720
186626
  for (const p3 of candidates) {
185721
- if (fs14.existsSync(p3)) {
186627
+ if (fs15.existsSync(p3)) {
185722
186628
  jsonPath = p3;
185723
186629
  break;
185724
186630
  }
185725
186631
  }
185726
186632
  if (!jsonPath) return;
185727
186633
  try {
185728
- const raw = fs14.readFileSync(jsonPath, "utf-8");
186634
+ const raw = fs15.readFileSync(jsonPath, "utf-8");
185729
186635
  const snap = JSON.parse(raw);
185730
186636
  if (snap.version !== 1) return;
185731
186637
  console.error(`[memorix] Migrating team-state.json to SQLite...`);
@@ -185760,7 +186666,7 @@ var init_team_store = __esm({
185760
186666
  for (const [agentId, msgs] of Object.entries(snap.messages.inboxes)) {
185761
186667
  for (const msg of msgs) {
185762
186668
  this.stmtMsgInsert.run({
185763
- id: msg.id ?? randomUUID11(),
186669
+ id: msg.id ?? randomUUID14(),
185764
186670
  project_id: "migrated",
185765
186671
  sender_agent_id: msg.from ?? "unknown",
185766
186672
  recipient_agent_id: msg.to === "__broadcast__" ? null : msg.to ?? agentId,
@@ -185819,7 +186725,7 @@ var init_team_store = __esm({
185819
186725
  `[memorix] Team migration complete: ${agentsMigrated} agents, ${messagesMigrated} messages, ${tasksMigrated} tasks, ${locksMigrated} locks.`
185820
186726
  );
185821
186727
  try {
185822
- fs14.renameSync(jsonPath, jsonPath + ".migrated");
186728
+ fs15.renameSync(jsonPath, jsonPath + ".migrated");
185823
186729
  } catch {
185824
186730
  }
185825
186731
  } catch (err) {
@@ -185854,7 +186760,7 @@ var init_team_store = __esm({
185854
186760
  * Uses projectId derived from the dataDir path.
185855
186761
  */
185856
186762
  seedDefaultRoles(dataDir) {
185857
- const projectId = path22.basename(path22.resolve(dataDir, ".."));
186763
+ const projectId = path23.basename(path23.resolve(dataDir, ".."));
185858
186764
  const count3 = this.stmtRoleCountByProject.get(projectId).cnt;
185859
186765
  if (count3 > 0) return;
185860
186766
  const now3 = Date.now();
@@ -185948,10 +186854,10 @@ var init_team_store = __esm({
185948
186854
  });
185949
186855
 
185950
186856
  // src/cli/identity.ts
185951
- import { promises as fs15 } from "fs";
185952
- import path23 from "path";
186857
+ import { promises as fs16 } from "fs";
186858
+ import path24 from "path";
185953
186859
  function identityPath(dataDir) {
185954
- return path23.join(dataDir, "cli-identity.json");
186860
+ return path24.join(dataDir, "cli-identity.json");
185955
186861
  }
185956
186862
  function isCliIdentity(value) {
185957
186863
  if (!value || typeof value !== "object") return false;
@@ -185960,18 +186866,18 @@ function isCliIdentity(value) {
185960
186866
  }
185961
186867
  async function loadCliIdentity(dataDir, projectId) {
185962
186868
  try {
185963
- const parsed = JSON.parse(await fs15.readFile(identityPath(dataDir), "utf8"));
186869
+ const parsed = JSON.parse(await fs16.readFile(identityPath(dataDir), "utf8"));
185964
186870
  return isCliIdentity(parsed) && parsed.projectId === projectId ? parsed : null;
185965
186871
  } catch {
185966
186872
  return null;
185967
186873
  }
185968
186874
  }
185969
186875
  async function saveCliIdentity(dataDir, identity) {
185970
- await fs15.writeFile(identityPath(dataDir), `${JSON.stringify(identity, null, 2)}
186876
+ await fs16.writeFile(identityPath(dataDir), `${JSON.stringify(identity, null, 2)}
185971
186877
  `, "utf8");
185972
186878
  }
185973
186879
  async function clearCliIdentity(dataDir) {
185974
- await fs15.rm(identityPath(dataDir), { force: true });
186880
+ await fs16.rm(identityPath(dataDir), { force: true });
185975
186881
  }
185976
186882
  async function resolveCliIdentity(options) {
185977
186883
  const stored = await loadCliIdentity(options.dataDir, options.project.id);
@@ -187321,8 +188227,8 @@ __export(session_store_exports, {
187321
188227
  initSessionStore: () => initSessionStore,
187322
188228
  resetSessionStore: () => resetSessionStore
187323
188229
  });
187324
- import path24 from "path";
187325
- import fs16 from "fs";
188230
+ import path25 from "path";
188231
+ import fs17 from "fs";
187326
188232
  function sessionToRow(session) {
187327
188233
  return {
187328
188234
  id: session.id,
@@ -187408,10 +188314,10 @@ var init_session_store = __esm({
187408
188314
  async migrateFromJsonIfNeeded() {
187409
188315
  const count3 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM sessions`).get();
187410
188316
  if (count3.cnt > 0) return;
187411
- const jsonPath = path24.join(this.dataDir, "sessions.json");
187412
- if (!fs16.existsSync(jsonPath)) return;
188317
+ const jsonPath = path25.join(this.dataDir, "sessions.json");
188318
+ if (!fs17.existsSync(jsonPath)) return;
187413
188319
  try {
187414
- const raw = fs16.readFileSync(jsonPath, "utf-8");
188320
+ const raw = fs17.readFileSync(jsonPath, "utf-8");
187415
188321
  const sessions = JSON.parse(raw);
187416
188322
  if (!Array.isArray(sessions) || sessions.length === 0) return;
187417
188323
  console.error(`[memorix] Migrating ${sessions.length} sessions from JSON to SQLite...`);
@@ -187753,827 +188659,6 @@ var init_operator_shared = __esm({
187753
188659
  }
187754
188660
  });
187755
188661
 
187756
- // src/memory/owner.ts
187757
- var owner_exports = {};
187758
- __export(owner_exports, {
187759
- resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
187760
- });
187761
- import { randomUUID as randomUUID12 } from "crypto";
187762
- import { promises as fs17 } from "fs";
187763
- import path25 from "path";
187764
- function ownerPath(dataDir) {
187765
- const resolved = path25.resolve(dataDir);
187766
- const home = path25.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path25.dirname(resolved) : resolved;
187767
- return path25.join(home, "memorix-user.json");
187768
- }
187769
- function normalizedOwnerId(value) {
187770
- const trimmed = value?.trim();
187771
- return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
187772
- }
187773
- async function readOwnerFile(filePath) {
187774
- try {
187775
- const value = JSON.parse(await fs17.readFile(filePath, "utf8"));
187776
- const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
187777
- return id ? { id, source: "local-file" } : void 0;
187778
- } catch {
187779
- return void 0;
187780
- }
187781
- }
187782
- async function resolveLocalMemoryOwner(dataDir, options = {}) {
187783
- const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
187784
- if (configured) return { id: configured, source: "environment" };
187785
- const filePath = ownerPath(dataDir);
187786
- const existing = await readOwnerFile(filePath);
187787
- if (existing || !options.create) return existing;
187788
- await fs17.mkdir(path25.dirname(filePath), { recursive: true });
187789
- const created = {
187790
- version: 1,
187791
- id: "local-" + randomUUID12(),
187792
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
187793
- };
187794
- try {
187795
- await fs17.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
187796
- return { id: created.id, source: "local-file" };
187797
- } catch (error40) {
187798
- if (error40?.code !== "EEXIST") throw error40;
187799
- const concurrent = await readOwnerFile(filePath);
187800
- if (concurrent) return concurrent;
187801
- throw new Error("Memorix could not establish a valid local memory owner identity.");
187802
- }
187803
- }
187804
- var ID_PATTERN;
187805
- var init_owner = __esm({
187806
- "src/memory/owner.ts"() {
187807
- "use strict";
187808
- init_esm_shims();
187809
- ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
187810
- }
187811
- });
187812
-
187813
- // src/memory/long-term-store.ts
187814
- import { randomUUID as randomUUID13 } from "crypto";
187815
- function optionalText6(value) {
187816
- return typeof value === "string" && value.trim() ? value : void 0;
187817
- }
187818
- function arrayValue2(value) {
187819
- if (typeof value !== "string" || !value) return [];
187820
- try {
187821
- const parsed = JSON.parse(value);
187822
- return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
187823
- } catch {
187824
- return [];
187825
- }
187826
- }
187827
- function rowToMemory(row) {
187828
- return {
187829
- id: row.id,
187830
- originProjectId: row.originProjectId,
187831
- ownerId: row.ownerId,
187832
- scope: row.scope,
187833
- kind: row.kind,
187834
- state: row.state,
187835
- portability: row.portability,
187836
- title: row.title,
187837
- content: row.content,
187838
- facts: arrayValue2(row.factsJson),
187839
- tags: arrayValue2(row.tagsJson),
187840
- ...optionalText6(row.applicability) ? { applicability: row.applicability } : {},
187841
- origin: row.origin,
187842
- createdAt: row.createdAt,
187843
- updatedAt: row.updatedAt,
187844
- ...optionalText6(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
187845
- ...optionalText6(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
187846
- ...optionalText6(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
187847
- ...optionalText6(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
187848
- ...optionalText6(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
187849
- accessCount: Number(row.accessCount ?? 0),
187850
- ...optionalText6(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
187851
- };
187852
- }
187853
- function rowToEvidence2(row) {
187854
- return {
187855
- id: row.id,
187856
- memoryId: row.memoryId,
187857
- kind: row.kind,
187858
- referenceId: row.referenceId,
187859
- relation: row.relation,
187860
- ...optionalText6(row.locator) ? { locator: row.locator } : {},
187861
- ...optionalText6(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
187862
- createdAt: row.createdAt
187863
- };
187864
- }
187865
- function rowToEvent2(row) {
187866
- return {
187867
- id: row.id,
187868
- memoryId: row.memoryId,
187869
- kind: row.kind,
187870
- ...optionalText6(row.fromState) ? { fromState: row.fromState } : {},
187871
- ...optionalText6(row.toState) ? { toState: row.toState } : {},
187872
- ...optionalText6(row.detail) ? { detail: row.detail } : {},
187873
- createdAt: row.createdAt
187874
- };
187875
- }
187876
- var LongTermMemoryStore;
187877
- var init_long_term_store = __esm({
187878
- "src/memory/long-term-store.ts"() {
187879
- "use strict";
187880
- init_esm_shims();
187881
- init_sqlite_db();
187882
- LongTermMemoryStore = class {
187883
- db = null;
187884
- async init(dataDir) {
187885
- this.db = getDatabase(dataDir);
187886
- }
187887
- transaction(fn2) {
187888
- return this.db.transaction(fn2)();
187889
- }
187890
- get(id) {
187891
- const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
187892
- return row ? rowToMemory(row) : void 0;
187893
- }
187894
- list(options = {}) {
187895
- const limit2 = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
187896
- const clauses = [];
187897
- const params = [];
187898
- if (options.ownerId) {
187899
- clauses.push("ownerId = ?");
187900
- params.push(options.ownerId);
187901
- }
187902
- if (options.originProjectId) {
187903
- clauses.push("originProjectId = ?");
187904
- params.push(options.originProjectId);
187905
- }
187906
- const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
187907
- const rows = this.db.prepare(
187908
- "SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
187909
- ).all(...params, limit2);
187910
- return rows.map(rowToMemory);
187911
- }
187912
- insert(memory) {
187913
- this.db.prepare(`
187914
- INSERT INTO long_term_memories (
187915
- id, originProjectId, ownerId, scope, kind, state, portability,
187916
- title, content, factsJson, tagsJson, applicability, origin,
187917
- createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
187918
- supersededBy, lastValidatedAt, accessCount, lastAccessedAt
187919
- ) VALUES (
187920
- @id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
187921
- @title, @content, @factsJson, @tagsJson, @applicability, @origin,
187922
- @createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
187923
- @supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
187924
- )
187925
- `).run({
187926
- ...memory,
187927
- factsJson: JSON.stringify(memory.facts),
187928
- tagsJson: JSON.stringify(memory.tags),
187929
- applicability: memory.applicability ?? null,
187930
- qualifiedAt: memory.qualifiedAt ?? null,
187931
- approvedAt: memory.approvedAt ?? null,
187932
- archivedAt: memory.archivedAt ?? null,
187933
- supersededBy: memory.supersededBy ?? null,
187934
- lastValidatedAt: memory.lastValidatedAt ?? null,
187935
- lastAccessedAt: memory.lastAccessedAt ?? null
187936
- });
187937
- }
187938
- update(memory) {
187939
- this.db.prepare(`
187940
- UPDATE long_term_memories SET
187941
- scope = @scope,
187942
- kind = @kind,
187943
- state = @state,
187944
- portability = @portability,
187945
- title = @title,
187946
- content = @content,
187947
- factsJson = @factsJson,
187948
- tagsJson = @tagsJson,
187949
- applicability = @applicability,
187950
- updatedAt = @updatedAt,
187951
- qualifiedAt = @qualifiedAt,
187952
- approvedAt = @approvedAt,
187953
- archivedAt = @archivedAt,
187954
- supersededBy = @supersededBy,
187955
- lastValidatedAt = @lastValidatedAt,
187956
- accessCount = @accessCount,
187957
- lastAccessedAt = @lastAccessedAt
187958
- WHERE id = @id
187959
- `).run({
187960
- ...memory,
187961
- factsJson: JSON.stringify(memory.facts),
187962
- tagsJson: JSON.stringify(memory.tags),
187963
- applicability: memory.applicability ?? null,
187964
- qualifiedAt: memory.qualifiedAt ?? null,
187965
- approvedAt: memory.approvedAt ?? null,
187966
- archivedAt: memory.archivedAt ?? null,
187967
- supersededBy: memory.supersededBy ?? null,
187968
- lastValidatedAt: memory.lastValidatedAt ?? null,
187969
- lastAccessedAt: memory.lastAccessedAt ?? null
187970
- });
187971
- }
187972
- insertEvidence(input) {
187973
- const evidence = {
187974
- id: randomUUID13(),
187975
- ...input,
187976
- createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
187977
- };
187978
- this.db.prepare(`
187979
- INSERT INTO long_term_memory_evidence (
187980
- id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
187981
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
187982
- `).run(
187983
- evidence.id,
187984
- evidence.memoryId,
187985
- evidence.kind,
187986
- evidence.referenceId,
187987
- evidence.relation,
187988
- evidence.locator ?? null,
187989
- evidence.capturedHash ?? null,
187990
- evidence.createdAt
187991
- );
187992
- return evidence;
187993
- }
187994
- listEvidence(memoryId) {
187995
- return this.db.prepare(`
187996
- SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
187997
- `).all(memoryId).map(rowToEvidence2);
187998
- }
187999
- recordEvent(input) {
188000
- const event = {
188001
- id: randomUUID13(),
188002
- ...input,
188003
- createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
188004
- };
188005
- this.db.prepare(`
188006
- INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
188007
- VALUES (?, ?, ?, ?, ?, ?, ?)
188008
- `).run(
188009
- event.id,
188010
- event.memoryId,
188011
- event.kind,
188012
- event.fromState ?? null,
188013
- event.toState ?? null,
188014
- event.detail ?? null,
188015
- event.createdAt
188016
- );
188017
- return event;
188018
- }
188019
- listEvents(memoryId) {
188020
- return this.db.prepare(`
188021
- SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
188022
- `).all(memoryId).map(rowToEvent2);
188023
- }
188024
- markAccess(ids, at2 = (/* @__PURE__ */ new Date()).toISOString()) {
188025
- const unique3 = [...new Set(ids.filter(Boolean))];
188026
- if (unique3.length === 0) return;
188027
- const statement = this.db.prepare(`
188028
- UPDATE long_term_memories
188029
- SET accessCount = accessCount + 1, lastAccessedAt = ?
188030
- WHERE id = ?
188031
- `);
188032
- this.db.transaction(() => {
188033
- for (const id of unique3) statement.run(at2, id);
188034
- })();
188035
- }
188036
- };
188037
- }
188038
- });
188039
-
188040
- // src/memory/long-term-types.ts
188041
- var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
188042
- var init_long_term_types = __esm({
188043
- "src/memory/long-term-types.ts"() {
188044
- "use strict";
188045
- init_esm_shims();
188046
- LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
188047
- LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
188048
- LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
188049
- LONG_TERM_MEMORY_EVIDENCE_KINDS = [
188050
- "observation",
188051
- "claim",
188052
- "workflow",
188053
- "session",
188054
- "checkpoint",
188055
- "git",
188056
- "code",
188057
- "test",
188058
- "run",
188059
- "document",
188060
- "manual",
188061
- "user"
188062
- ];
188063
- LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
188064
- "supports",
188065
- "verifies",
188066
- "derives",
188067
- "user-confirmed"
188068
- ];
188069
- }
188070
- });
188071
-
188072
- // src/memory/long-term.ts
188073
- var long_term_exports = {};
188074
- __export(long_term_exports, {
188075
- approveLongTermMemory: () => approveLongTermMemory,
188076
- archiveLongTermMemory: () => archiveLongTermMemory,
188077
- canReadLongTermMemory: () => canReadLongTermMemory,
188078
- createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
188079
- createManualLongTermMemory: () => createManualLongTermMemory,
188080
- getLongTermMemoryDetail: () => getLongTermMemoryDetail,
188081
- isEligibleLongTermMemory: () => isEligibleLongTermMemory,
188082
- listLongTermMemories: () => listLongTermMemories,
188083
- promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
188084
- qualifyLongTermMemory: () => qualifyLongTermMemory,
188085
- renderLongTermMemorySummary: () => renderLongTermMemorySummary,
188086
- selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
188087
- supersedeLongTermMemory: () => supersedeLongTermMemory
188088
- });
188089
- import { randomUUID as randomUUID14 } from "crypto";
188090
- function isOneOf(value, values) {
188091
- return typeof value === "string" && values.includes(value);
188092
- }
188093
- function compactText2(value, field, max = MAX_TEXT_LENGTH) {
188094
- if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
188095
- const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
188096
- if (!safe) throw new Error("Long-term memory " + field + " is required.");
188097
- if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
188098
- return safe;
188099
- }
188100
- function compactList(values, field) {
188101
- if (!values) return [];
188102
- if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
188103
- return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText2(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
188104
- }
188105
- function normalizeEvidence(input) {
188106
- if (!Array.isArray(input) || input.length === 0) {
188107
- throw new Error("A long-term memory requires at least one source evidence reference.");
188108
- }
188109
- return input.map((evidence) => {
188110
- if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
188111
- throw new Error("Long-term memory evidence kind is invalid.");
188112
- }
188113
- if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
188114
- throw new Error("Long-term memory evidence relation is invalid.");
188115
- }
188116
- return {
188117
- kind: evidence.kind,
188118
- referenceId: compactText2(evidence.referenceId, "evidence reference", 1e3),
188119
- relation: evidence.relation,
188120
- ...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator", 1e3) } : {},
188121
- ...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash", 1e3) } : {}
188122
- };
188123
- });
188124
- }
188125
- function assertScope(scope) {
188126
- if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
188127
- }
188128
- function assertKind(kind) {
188129
- if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
188130
- }
188131
- function assertPortability(value) {
188132
- if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
188133
- throw new Error("Long-term memory portability is invalid.");
188134
- }
188135
- }
188136
- function assertPortableEvidence(scope, portability, evidence) {
188137
- if (portability !== "portable") return;
188138
- if (scope !== "user") {
188139
- throw new Error("Only user-scoped long-term memories may be portable across projects.");
188140
- }
188141
- if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
188142
- 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.");
188143
- }
188144
- }
188145
- function requireTeamReader(scope, reader) {
188146
- if (scope === "team" && reader?.isTeamMember !== true) {
188147
- throw new Error("Team long-term memory requires an explicit active team identity for this project.");
188148
- }
188149
- }
188150
- function matchesScope(memory, reader) {
188151
- if (memory.scope === "project") return memory.originProjectId === reader.projectId;
188152
- if (memory.scope === "team") {
188153
- return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
188154
- }
188155
- if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
188156
- return memory.portability === "portable" || memory.originProjectId === reader.projectId;
188157
- }
188158
- function canReadLongTermMemory(memory, reader) {
188159
- return matchesScope(memory, reader);
188160
- }
188161
- function isEligibleLongTermMemory(memory) {
188162
- return memory.state === "qualified" || memory.state === "approved";
188163
- }
188164
- function taskTerms(task) {
188165
- const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
188166
- return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
188167
- }
188168
- function kindFitScore(kind, task) {
188169
- const normalized = task.toLocaleLowerCase("en-US");
188170
- const hints = {
188171
- episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
188172
- semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
188173
- procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
188174
- };
188175
- return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
188176
- }
188177
- function validationFreshnessScore(memory) {
188178
- const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
188179
- if (!validatedAt) return 0;
188180
- const time3 = Date.parse(validatedAt);
188181
- if (!Number.isFinite(time3)) return 0;
188182
- const ageDays = Math.max(0, Date.now() - time3) / (24 * 60 * 60 * 1e3);
188183
- if (ageDays <= 30) return 0.35;
188184
- if (ageDays <= 90) return 0.15;
188185
- return 0;
188186
- }
188187
- function scoreForTask(memory, task) {
188188
- const terms = taskTerms(task);
188189
- if (terms.length === 0) return void 0;
188190
- const title = memory.title.toLocaleLowerCase("en-US");
188191
- const content = memory.content.toLocaleLowerCase("en-US");
188192
- const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
188193
- const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
188194
- const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
188195
- let score = memory.state === "approved" ? 0.5 : 0;
188196
- const matched = [];
188197
- for (const term of terms) {
188198
- let termScore = 0;
188199
- if (title.includes(term)) termScore += 5;
188200
- if (tags.includes(term)) termScore += 4;
188201
- if (applicability.includes(term)) termScore += 3;
188202
- if (facts.includes(term)) termScore += 2;
188203
- if (content.includes(term)) termScore += 1;
188204
- if (termScore > 0) {
188205
- score += termScore;
188206
- matched.push(term);
188207
- }
188208
- }
188209
- if (matched.length === 0) return void 0;
188210
- score += kindFitScore(memory.kind, task);
188211
- score += validationFreshnessScore(memory);
188212
- const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
188213
- const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
188214
- return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
188215
- }
188216
- function semanticText(memory) {
188217
- return sanitizeCredentials([
188218
- memory.title,
188219
- memory.applicability,
188220
- memory.tags.join(" "),
188221
- memory.facts.join(" "),
188222
- memory.content
188223
- ].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
188224
- }
188225
- function cosineSimilarity(left, right) {
188226
- if (left.length === 0 || left.length !== right.length) return 0;
188227
- let dot = 0;
188228
- let leftMagnitude = 0;
188229
- let rightMagnitude = 0;
188230
- for (let index = 0; index < left.length; index += 1) {
188231
- const a4 = left[index];
188232
- const b4 = right[index];
188233
- if (!Number.isFinite(a4) || !Number.isFinite(b4)) return 0;
188234
- dot += a4 * b4;
188235
- leftMagnitude += a4 * a4;
188236
- rightMagnitude += b4 * b4;
188237
- }
188238
- if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
188239
- return dot / Math.sqrt(leftMagnitude * rightMagnitude);
188240
- }
188241
- function semanticSelection(memory, similarity, task) {
188242
- if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
188243
- const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
188244
- const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
188245
- return {
188246
- score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
188247
- reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
188248
- };
188249
- }
188250
- async function resolveSemanticProvider(explicit) {
188251
- if (explicit !== void 0) return explicit;
188252
- try {
188253
- const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
188254
- return await withTimeout(
188255
- getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
188256
- SEMANTIC_RETRIEVAL_TIMEOUT_MS,
188257
- "Long-term semantic provider initialization"
188258
- );
188259
- } catch {
188260
- return null;
188261
- }
188262
- }
188263
- function createStore(dataDir) {
188264
- const store2 = new LongTermMemoryStore();
188265
- return store2.init(dataDir).then(() => store2);
188266
- }
188267
- async function createManualLongTermMemory(input) {
188268
- const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
188269
- if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
188270
- assertScope(input.scope);
188271
- const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID14(), relation: "supports" }];
188272
- return createLongTermMemoryWithDataDir({
188273
- dataDir: input.dataDir,
188274
- originProjectId: input.projectId,
188275
- ownerId: owner.id,
188276
- scope: input.scope,
188277
- kind: input.kind,
188278
- portability: input.portability,
188279
- title: input.title,
188280
- content: input.content,
188281
- facts: input.facts,
188282
- tags: input.tags,
188283
- applicability: input.applicability,
188284
- origin: "manual",
188285
- evidence,
188286
- reader: input.reader
188287
- });
188288
- }
188289
- async function promoteObservationToLongTermMemory(input) {
188290
- const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
188291
- if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
188292
- return createLongTermMemoryWithDataDir({
188293
- dataDir: input.dataDir,
188294
- originProjectId: input.observation.projectId,
188295
- ownerId: owner.id,
188296
- scope: input.scope,
188297
- kind: input.kind,
188298
- portability: "project-bound",
188299
- title: input.observation.title,
188300
- content: input.observation.narrative,
188301
- facts: input.observation.facts,
188302
- tags: input.tags ?? input.observation.concepts,
188303
- applicability: input.applicability,
188304
- origin: input.observation.source === "git" ? "git" : "agent",
188305
- evidence: [{
188306
- kind: "observation",
188307
- referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
188308
- relation: "derives"
188309
- }],
188310
- reader: input.reader
188311
- });
188312
- }
188313
- async function createLongTermMemoryCandidate(input) {
188314
- return createLongTermMemoryWithDataDir(input);
188315
- }
188316
- async function createLongTermMemoryWithDataDir(input) {
188317
- assertScope(input.scope);
188318
- assertKind(input.kind);
188319
- const portability = input.portability ?? "project-bound";
188320
- assertPortability(portability);
188321
- requireTeamReader(input.scope, input.reader);
188322
- const evidenceInputs = normalizeEvidence(input.evidence);
188323
- assertPortableEvidence(input.scope, portability, evidenceInputs);
188324
- const now3 = (/* @__PURE__ */ new Date()).toISOString();
188325
- const memory = {
188326
- id: randomUUID14(),
188327
- originProjectId: compactText2(input.originProjectId, "origin project id", 1e3),
188328
- ownerId: compactText2(input.ownerId, "owner id", 160),
188329
- scope: input.scope,
188330
- kind: input.kind,
188331
- state: "candidate",
188332
- portability,
188333
- title: compactText2(input.title, "title", 500),
188334
- content: compactText2(input.content, "content"),
188335
- facts: compactList(input.facts, "facts"),
188336
- tags: compactList(input.tags, "tags"),
188337
- ...input.applicability ? { applicability: compactText2(input.applicability, "applicability", 1e3) } : {},
188338
- origin: input.origin ?? "manual",
188339
- createdAt: now3,
188340
- updatedAt: now3,
188341
- accessCount: 0
188342
- };
188343
- const store2 = await createStore(input.dataDir);
188344
- const evidence = store2.transaction(() => {
188345
- store2.insert(memory);
188346
- const written = evidenceInputs.map((item) => store2.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
188347
- store2.recordEvent({
188348
- memoryId: memory.id,
188349
- kind: "created",
188350
- toState: "candidate",
188351
- detail: "Created as a source-backed long-term memory candidate.",
188352
- createdAt: now3
188353
- });
188354
- return written;
188355
- });
188356
- return { memory, evidence };
188357
- }
188358
- function transitionDetail(value) {
188359
- return compactText2(value, "transition reason", 1e3);
188360
- }
188361
- async function transitionMemory(input) {
188362
- const store2 = await createStore(input.dataDir);
188363
- return store2.transaction(() => {
188364
- const current = store2.get(input.id);
188365
- if (!current) throw new Error("Long-term memory was not found.");
188366
- if (current.state !== input.expected) {
188367
- throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
188368
- }
188369
- const evidence = store2.listEvidence(current.id);
188370
- if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
188371
- const now3 = (/* @__PURE__ */ new Date()).toISOString();
188372
- const next = {
188373
- ...current,
188374
- state: input.next,
188375
- updatedAt: now3,
188376
- lastValidatedAt: now3,
188377
- ...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
188378
- };
188379
- store2.update(next);
188380
- store2.recordEvent({
188381
- memoryId: next.id,
188382
- kind: input.event,
188383
- fromState: current.state,
188384
- toState: next.state,
188385
- detail: transitionDetail(input.reason),
188386
- createdAt: now3
188387
- });
188388
- return next;
188389
- });
188390
- }
188391
- async function qualifyLongTermMemory(input) {
188392
- return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
188393
- }
188394
- async function approveLongTermMemory(input) {
188395
- return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
188396
- }
188397
- async function archiveLongTermMemory(input) {
188398
- const store2 = await createStore(input.dataDir);
188399
- return store2.transaction(() => {
188400
- const current = store2.get(input.id);
188401
- if (!current) throw new Error("Long-term memory was not found.");
188402
- if (current.state === "archived" || current.state === "superseded") {
188403
- throw new Error("Long-term memory is already inactive.");
188404
- }
188405
- const now3 = (/* @__PURE__ */ new Date()).toISOString();
188406
- const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
188407
- store2.update(next);
188408
- store2.recordEvent({
188409
- memoryId: next.id,
188410
- kind: "archived",
188411
- fromState: current.state,
188412
- toState: "archived",
188413
- detail: transitionDetail(input.reason),
188414
- createdAt: now3
188415
- });
188416
- return next;
188417
- });
188418
- }
188419
- async function supersedeLongTermMemory(input) {
188420
- const store2 = await createStore(input.dataDir);
188421
- return store2.transaction(() => {
188422
- const current = store2.get(input.id);
188423
- if (!current) throw new Error("Long-term memory was not found.");
188424
- if (current.state === "archived" || current.state === "superseded") {
188425
- throw new Error("Long-term memory is already inactive.");
188426
- }
188427
- if (current.id === input.supersededBy) {
188428
- throw new Error("Long-term memory cannot supersede itself.");
188429
- }
188430
- const replacement = store2.get(input.supersededBy);
188431
- if (!replacement || !isEligibleLongTermMemory(replacement)) {
188432
- throw new Error("Replacement long-term memory must be qualified or approved.");
188433
- }
188434
- if (replacement.scope !== current.scope) {
188435
- throw new Error("Replacement long-term memory must use the same scope.");
188436
- }
188437
- if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
188438
- throw new Error("User long-term memory may only be superseded by the same local owner.");
188439
- }
188440
- if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
188441
- throw new Error("Project and team long-term memory may only be superseded within the same project.");
188442
- }
188443
- const now3 = (/* @__PURE__ */ new Date()).toISOString();
188444
- const next = {
188445
- ...current,
188446
- state: "superseded",
188447
- supersededBy: replacement.id,
188448
- updatedAt: now3
188449
- };
188450
- store2.update(next);
188451
- store2.recordEvent({
188452
- memoryId: next.id,
188453
- kind: "superseded",
188454
- fromState: current.state,
188455
- toState: "superseded",
188456
- detail: transitionDetail(input.reason),
188457
- createdAt: now3
188458
- });
188459
- return next;
188460
- });
188461
- }
188462
- async function listLongTermMemories(input) {
188463
- const store2 = await createStore(input.dataDir);
188464
- return store2.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: store2.listEvidence(memory.id) }));
188465
- }
188466
- async function getLongTermMemoryDetail(input) {
188467
- const store2 = await createStore(input.dataDir);
188468
- const memory = store2.get(input.id);
188469
- if (!memory || !canReadLongTermMemory(memory, input.reader)) {
188470
- throw new Error("Long-term memory was not found in the active scope.");
188471
- }
188472
- store2.markAccess([memory.id]);
188473
- return { memory, evidence: store2.listEvidence(memory.id), events: store2.listEvents(memory.id) };
188474
- }
188475
- async function selectLongTermMemoriesForTask(input) {
188476
- const task = input.task?.trim();
188477
- if (!task) return [];
188478
- const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
188479
- const reader = {
188480
- projectId: input.projectId,
188481
- ...owner ? { ownerId: owner.id } : {},
188482
- ...input.agentId ? { agentId: input.agentId } : {},
188483
- ...input.isTeamMember ? { isTeamMember: true } : {}
188484
- };
188485
- const store2 = await createStore(input.dataDir);
188486
- const eligible = store2.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
188487
- let selected = eligible.map((memory) => {
188488
- const ranked = scoreForTask(memory, task);
188489
- return ranked ? {
188490
- memory,
188491
- evidence: store2.listEvidence(memory.id),
188492
- score: ranked.score,
188493
- reason: ranked.reason
188494
- } : void 0;
188495
- }).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));
188496
- if (selected.length === 0 && eligible.length > 0) {
188497
- const provider2 = await resolveSemanticProvider(input.embeddingProvider);
188498
- const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
188499
- if (provider2 && candidates.length > 0) {
188500
- try {
188501
- const vectors = await withTimeout(
188502
- provider2.embedBatch([task, ...candidates.map(semanticText)], {
188503
- timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
188504
- retry: false
188505
- }),
188506
- SEMANTIC_RETRIEVAL_TIMEOUT_MS,
188507
- "Long-term semantic retrieval"
188508
- );
188509
- const query = vectors[0];
188510
- if (Array.isArray(query)) {
188511
- selected = candidates.map((memory, index) => {
188512
- const vector = vectors[index + 1];
188513
- const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
188514
- return ranked ? {
188515
- memory,
188516
- evidence: store2.listEvidence(memory.id),
188517
- score: ranked.score,
188518
- reason: ranked.reason
188519
- } : void 0;
188520
- }).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));
188521
- }
188522
- } catch {
188523
- }
188524
- }
188525
- }
188526
- selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
188527
- store2.markAccess(selected.map((item) => item.memory.id));
188528
- return selected;
188529
- }
188530
- function renderLongTermMemorySummary(memory, maxTokens = 22) {
188531
- const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
188532
- const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
188533
- return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
188534
- }
188535
- var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
188536
- var init_long_term = __esm({
188537
- "src/memory/long-term.ts"() {
188538
- "use strict";
188539
- init_esm_shims();
188540
- init_token_budget();
188541
- init_timeout();
188542
- init_secret_filter();
188543
- init_owner();
188544
- init_long_term_store();
188545
- init_long_term_types();
188546
- MAX_TEXT_LENGTH = 12e3;
188547
- MAX_LIST_ITEMS = 40;
188548
- MAX_SEMANTIC_CANDIDATES = 48;
188549
- SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
188550
- MIN_SEMANTIC_SIMILARITY = 0.58;
188551
- PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
188552
- STOP_TERMS = /* @__PURE__ */ new Set([
188553
- "the",
188554
- "and",
188555
- "for",
188556
- "with",
188557
- "from",
188558
- "that",
188559
- "this",
188560
- "into",
188561
- "when",
188562
- "what",
188563
- "\u5982\u4F55",
188564
- "\u4EC0\u4E48",
188565
- "\u8FD9\u4E2A",
188566
- "\u90A3\u4E2A",
188567
- "\u6211\u4EEC",
188568
- "\u9879\u76EE",
188569
- "\u8FDB\u884C",
188570
- "\u9700\u8981",
188571
- "\u7EE7\u7EED",
188572
- "\u5904\u7406"
188573
- ]);
188574
- }
188575
- });
188576
-
188577
188662
  // src/cli/commands/memory.ts
188578
188663
  var memory_exports2 = {};
188579
188664
  __export(memory_exports2, {
@@ -189052,6 +189137,7 @@ var init_memory3 = __esm({
189052
189137
  createManualLongTermMemory: createManualLongTermMemory2,
189053
189138
  getLongTermMemoryDetail: getLongTermMemoryDetail2,
189054
189139
  listLongTermMemories: listLongTermMemories2,
189140
+ maybeAutoQualifyLongTermMemory: maybeAutoQualifyLongTermMemory2,
189055
189141
  promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2,
189056
189142
  qualifyLongTermMemory: qualifyLongTermMemory2,
189057
189143
  supersedeLongTermMemory: supersedeLongTermMemory2
@@ -189122,9 +189208,14 @@ var init_memory3 = __esm({
189122
189208
  portability: portability(),
189123
189209
  reader: longTermReader
189124
189210
  });
189211
+ const qualified = await maybeAutoQualifyLongTermMemory2({
189212
+ dataDir,
189213
+ id: result.memory.id,
189214
+ source: "manual"
189215
+ });
189125
189216
  emitResult(
189126
- { project, ...result },
189127
- `Created long-term memory candidate ${result.memory.id}. Qualify it after checking its evidence.`,
189217
+ { project, ...result, memory: qualified ?? result.memory },
189218
+ `Created long-term memory ${result.memory.id}.`,
189128
189219
  asJson
189129
189220
  );
189130
189221
  return;
@@ -189150,9 +189241,14 @@ var init_memory3 = __esm({
189150
189241
  applicability: asStringArg(args.applicability),
189151
189242
  reader: longTermReader
189152
189243
  });
189244
+ const qualified = await maybeAutoQualifyLongTermMemory2({
189245
+ dataDir,
189246
+ id: result.memory.id,
189247
+ sourceDetail: "explicit"
189248
+ });
189153
189249
  emitResult(
189154
- { project, sourceObservationId: observation.id, ...result },
189155
- `Promoted observation #${observation.id} to long-term memory candidate ${result.memory.id}.`,
189250
+ { project, sourceObservationId: observation.id, ...result, memory: qualified ?? result.memory },
189251
+ `Promoted observation #${observation.id} to long-term memory ${result.memory.id}.`,
189156
189252
  asJson
189157
189253
  );
189158
189254
  return;
@@ -192200,13 +192296,28 @@ alwaysApply: true
192200
192296
  '- For "why" decisions, use `memorix_store_reasoning`',
192201
192297
  '- 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`.',
192202
192298
  "- 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.",
192299
+ '- 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.',
192203
192300
  "",
192204
192301
  "**Don't store:** greetings, simple file reads, trivial commands (ls, pwd, git status).",
192205
192302
  "",
192303
+ "**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.",
192304
+ "",
192305
+ `**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.`,
192306
+ "",
192307
+ "**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.",
192308
+ "",
192206
192309
  "## When to resolve memory",
192207
192310
  "",
192208
192311
  "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.",
192209
192312
  "",
192313
+ "## End sessions with a summary",
192314
+ "",
192315
+ "When a session finishes, call `memorix_session_end` with a short structured summary so the next agent can resume. Recommended sections:",
192316
+ "- **Goal** \u2014 what this session was working on",
192317
+ "- **Discoveries** \u2014 findings, gotchas, learnings",
192318
+ "- **Accomplished** \u2014 completed items, plus PENDING items for the next session",
192319
+ "- **Relevant Files** \u2014 paths and what changed",
192320
+ "",
192210
192321
  "## Tools quick reference",
192211
192322
  "",
192212
192323
  "| Tool | Use when |",
@@ -192590,7 +192701,9 @@ function buildMemorixServer(mcp) {
192590
192701
  return {
192591
192702
  name: "memorix",
192592
192703
  command: "memorix",
192593
- args: ["serve"]
192704
+ // The installed surface must expose every tool the generated guidance
192705
+ // teaches. lite covers the full taught set; micro would hide half of it.
192706
+ args: ["serve", "--mode", "lite"]
192594
192707
  };
192595
192708
  }
192596
192709
  function mergeJsonMcpConfig(existingContent, generatedContent) {
@@ -194313,6 +194426,37 @@ function renderTaskWorksetPrompt(input) {
194313
194426
  trust: "source-backed"
194314
194427
  });
194315
194428
  appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
194429
+ if (input.alwaysOn) {
194430
+ const hasAlwaysOn = input.alwaysOn.profile.length > 0 || Boolean(input.alwaysOn.state) || input.alwaysOn.durable.length > 0;
194431
+ if (hasAlwaysOn) {
194432
+ appendLine(lines, "", maxTokens, omitted, "always-on-heading");
194433
+ appendLine(lines, "You and this workspace", maxTokens, omitted, "always-on-heading");
194434
+ for (const profile of input.alwaysOn.profile.slice(0, 2)) {
194435
+ appendLine(lines, "- " + short(profile, 26), maxTokens, omitted, "always-on-profile", selected, {
194436
+ kind: "memory",
194437
+ id: "profile",
194438
+ reason: "user profile memory",
194439
+ trust: "source-backed"
194440
+ });
194441
+ }
194442
+ if (input.alwaysOn.state) {
194443
+ appendLine(lines, "- Recently: " + short(input.alwaysOn.state, 40), maxTokens, omitted, "always-on-state", selected, {
194444
+ kind: "continuation",
194445
+ id: "workspace-state",
194446
+ reason: "latest session summary for this workspace",
194447
+ trust: "historical"
194448
+ });
194449
+ }
194450
+ for (const durable of input.alwaysOn.durable.slice(0, 2)) {
194451
+ appendLine(lines, "- " + short(durable, 28), maxTokens, omitted, "always-on-durable", selected, {
194452
+ kind: "durable-memory",
194453
+ id: "durable-always",
194454
+ reason: "recent durable memory for this workspace",
194455
+ trust: "source-backed"
194456
+ });
194457
+ }
194458
+ }
194459
+ }
194316
194460
  const hasContinuation = Boolean(
194317
194461
  input.continuation?.previousSession || (input.continuation?.memories.length ?? 0) > 0 || input.continuation?.compactCheckpoint
194318
194462
  );
@@ -194783,6 +194927,7 @@ async function buildTaskWorkset(input) {
194783
194927
  version: "1.3",
194784
194928
  task,
194785
194929
  lens: input.lens,
194930
+ ...input.alwaysOn && (input.alwaysOn.profile.length > 0 || input.alwaysOn.state || input.alwaysOn.durable.length > 0) ? { alwaysOn: input.alwaysOn } : {},
194786
194931
  currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
194787
194932
  ...continuation ? { continuation } : {},
194788
194933
  ...input.codeState ? { codeState: short(input.codeState, 28) } : {},
@@ -196832,6 +196977,33 @@ function activeProjectObservations(observations2, projectId) {
196832
196977
  function deliveryEligibleProjectObservations(observations2, projectId) {
196833
196978
  return activeProjectObservations(observations2, projectId).filter((observation) => isEligibleForAutomaticDelivery(observation));
196834
196979
  }
196980
+ async function buildAlwaysOnBlock(input) {
196981
+ 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));
196982
+ let state;
196983
+ try {
196984
+ await initSessionStore(input.dataDir);
196985
+ const { getSessionStore: getSessionStore2 } = await Promise.resolve().then(() => (init_session_store(), session_store_exports));
196986
+ const sessions = await getSessionStore2().loadByProject(input.projectId);
196987
+ const latest = sessions.filter((session) => session.endedAt && session.summary?.trim()).sort((a4, b4) => Date.parse(b4.endedAt ?? "") - Date.parse(a4.endedAt ?? ""))[0];
196988
+ if (latest?.summary) state = compactContinuationText(latest.summary, 40);
196989
+ } catch {
196990
+ }
196991
+ const durable = [];
196992
+ try {
196993
+ const items = await listLongTermMemories({
196994
+ dataDir: input.dataDir,
196995
+ reader: {
196996
+ projectId: input.projectId,
196997
+ ...input.reader.agentId ? { agentId: input.reader.agentId } : {},
196998
+ ...input.reader.isTeamMember ? { isTeamMember: true } : {}
196999
+ },
197000
+ limit: 10
197001
+ });
197002
+ durable.push(...items.filter((item) => item.memory.state === "approved").sort((a4, b4) => (b4.memory.updatedAt ?? "").localeCompare(a4.memory.updatedAt ?? "")).slice(0, 2).map((item) => item.memory.kind + ": " + compactContinuationText(item.memory.title, 22)));
197003
+ } catch {
197004
+ }
197005
+ return { profile, state, durable };
197006
+ }
196835
197007
  function decideRefresh(input) {
196836
197008
  if (input.mode === "never") {
196837
197009
  return { performed: false, reason: "disabled", message: "Automatic project scan disabled." };
@@ -197036,12 +197208,19 @@ async function buildAutoProjectContext(input) {
197036
197208
  };
197037
197209
  }
197038
197210
  }
197211
+ const alwaysOn = await buildAlwaysOnBlock({
197212
+ dataDir: input.dataDir,
197213
+ projectId: input.project.id,
197214
+ reader: input.reader ?? { projectId: input.project.id },
197215
+ eligible: deliveryEligibleProjectObservations(input.observations, input.project.id)
197216
+ });
197039
197217
  const workset = await buildTaskWorkset({
197040
197218
  projectId: input.project.id,
197041
197219
  dataDir: input.dataDir,
197042
197220
  ...task ? { task } : {},
197043
197221
  lens: lens.id,
197044
197222
  startHere,
197223
+ ...alwaysOn.profile.length > 0 || alwaysOn.state || alwaysOn.durable.length > 0 ? { alwaysOn } : {},
197045
197224
  ...externalOutline ? { semanticCode: externalOutline } : {},
197046
197225
  providerQuality: providerQuality2,
197047
197226
  currentFacts: worksetFactLines(currentFacts),
@@ -197224,7 +197403,16 @@ function formatAutoProjectContextSummary(context) {
197224
197403
  lensLine(context),
197225
197404
  "",
197226
197405
  ...formatCurrentFactsLines(context.currentFacts),
197227
- "",
197406
+ ""
197407
+ ].filter(Boolean);
197408
+ if (context.workset.alwaysOn && (context.workset.alwaysOn.profile.length > 0 || context.workset.alwaysOn.state || context.workset.alwaysOn.durable.length > 0)) {
197409
+ lines.push("You and this workspace");
197410
+ for (const profile of context.workset.alwaysOn.profile.slice(0, 2)) lines.push(`- ${profile}`);
197411
+ if (context.workset.alwaysOn.state) lines.push(`- Recently: ${context.workset.alwaysOn.state}`);
197412
+ for (const durable of context.workset.alwaysOn.durable.slice(0, 2)) lines.push(`- ${durable}`);
197413
+ lines.push("");
197414
+ }
197415
+ lines.push(...[
197228
197416
  `- Code memory: ${context.overview.code.files} files / ${context.overview.code.symbols} symbols / ${context.overview.code.refs} memory links`,
197229
197417
  `- Code provider: ${context.providerQuality.selected} (${context.providerQuality.selectedQuality})`,
197230
197418
  `- Languages: ${formatLanguages(context.overview)}`,
@@ -197233,7 +197421,7 @@ function formatAutoProjectContextSummary(context) {
197233
197421
  `- Refresh: ${context.refresh.message}`,
197234
197422
  "",
197235
197423
  "Start here"
197236
- ].filter(Boolean);
197424
+ ].filter(Boolean));
197237
197425
  if (startHere.length > 0) {
197238
197426
  startHere.forEach((path47, index) => lines.push(`${index + 1}. ${path47}`));
197239
197427
  } else {
@@ -197357,6 +197545,8 @@ var init_auto_context = __esm({
197357
197545
  init_store();
197358
197546
  init_admission();
197359
197547
  init_session();
197548
+ init_long_term();
197549
+ init_visibility();
197360
197550
  init_session_store();
197361
197551
  init_task_lens();
197362
197552
  DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
@@ -216457,6 +216647,46 @@ var init_formation_timeout = __esm({
216457
216647
  }
216458
216648
  });
216459
216649
 
216650
+ // src/search/surfaced-registry.ts
216651
+ function touch(key) {
216652
+ const ids = registry2.get(key);
216653
+ if (!ids) return [];
216654
+ registry2.delete(key);
216655
+ registry2.set(key, ids);
216656
+ return ids;
216657
+ }
216658
+ function getSurfacedIds(key) {
216659
+ return [...touch(key)];
216660
+ }
216661
+ function recordSurfacedIds(key, ids) {
216662
+ if (ids.length === 0) return;
216663
+ let list = registry2.get(key) ?? [];
216664
+ const known = new Set(list);
216665
+ for (const id of ids) {
216666
+ if (known.has(id)) continue;
216667
+ known.add(id);
216668
+ list.push(id);
216669
+ }
216670
+ if (list.length > MAX_IDS) {
216671
+ list = list.slice(list.length - MAX_IDS);
216672
+ }
216673
+ registry2.set(key, list);
216674
+ if (registry2.size > MAX_KEYS) {
216675
+ const oldestKey = registry2.keys().next().value;
216676
+ if (oldestKey !== void 0 && oldestKey !== key) registry2.delete(oldestKey);
216677
+ }
216678
+ }
216679
+ var MAX_KEYS, MAX_IDS, registry2;
216680
+ var init_surfaced_registry = __esm({
216681
+ "src/search/surfaced-registry.ts"() {
216682
+ "use strict";
216683
+ init_esm_shims();
216684
+ MAX_KEYS = 64;
216685
+ MAX_IDS = 200;
216686
+ registry2 = /* @__PURE__ */ new Map();
216687
+ }
216688
+ });
216689
+
216460
216690
  // src/server/request-context.ts
216461
216691
  var request_context_exports = {};
216462
216692
  __export(request_context_exports, {
@@ -217660,8 +217890,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
217660
217890
  const { promises: fsP } = await import("fs");
217661
217891
  const registryPath = path39.join(baseDir, ".project-aliases.json");
217662
217892
  const raw = await fsP.readFile(registryPath, "utf-8");
217663
- const registry2 = JSON.parse(raw);
217664
- aliasGroups = registry2.groups || [];
217893
+ const registry3 = JSON.parse(raw);
217894
+ aliasGroups = registry3.groups || [];
217665
217895
  } catch {
217666
217896
  }
217667
217897
  const currentGroup = aliasGroups.find((g4) => g4.aliases?.includes(effectiveProjectId) || g4.canonical === effectiveProjectId);
@@ -258410,6 +258640,12 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
258410
258640
  kind: "consolidation",
258411
258641
  dedupeKey: "startup-consolidation"
258412
258642
  });
258643
+ lifecycle.enqueueLongTermMaintenance({
258644
+ dataDir: projectDir2,
258645
+ projectId: project.id,
258646
+ source: "startup",
258647
+ queue: queue2
258648
+ });
258413
258649
  }
258414
258650
  try {
258415
258651
  const { CodeGraphStore: CodeGraphStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
@@ -258459,7 +258695,7 @@ The path should point to a directory containing a .git folder.`
258459
258695
  };
258460
258696
  const server = existingServer ?? new McpServer({
258461
258697
  name: "memorix",
258462
- version: true ? "1.4.5" : "1.0.1"
258698
+ version: true ? "1.4.6" : "1.0.1"
258463
258699
  });
258464
258700
  const originalRegisterTool = server.registerTool.bind(server);
258465
258701
  server.registerTool = ((name, ...args) => {
@@ -258944,7 +259180,7 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
258944
259180
  let longTermNote = "";
258945
259181
  if (longTerm) {
258946
259182
  try {
258947
- const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
259183
+ const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2, maybeAutoQualifyLongTermMemory: maybeAutoQualifyLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
258948
259184
  const promoted = await promoteObservationToLongTermMemory2({
258949
259185
  dataDir: projectDir2,
258950
259186
  observation: obs,
@@ -258958,7 +259194,24 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
258958
259194
  ...reader.isTeamMember ? { isTeamMember: true } : {}
258959
259195
  }
258960
259196
  });
258961
- longTermNote = `
259197
+ const autoQualified = await maybeAutoQualifyLongTermMemory2({
259198
+ dataDir: projectDir2,
259199
+ id: promoted.memory.id,
259200
+ sourceDetail: "explicit"
259201
+ });
259202
+ if (autoQualified) {
259203
+ try {
259204
+ const { enqueueLongTermMaintenance: enqueueLongTermMaintenance2 } = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
259205
+ enqueueLongTermMaintenance2({
259206
+ dataDir: projectDir2,
259207
+ projectId: project.id,
259208
+ source: "long-term:" + promoted.memory.id
259209
+ });
259210
+ } catch {
259211
+ }
259212
+ }
259213
+ longTermNote = autoQualified ? `
259214
+ 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\`.` : `
258962
259215
  Long-term candidate: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}). Review it with the CLI before it is delivered automatically.`;
258963
259216
  } catch (longTermError) {
258964
259217
  longTermNote = `
@@ -259154,6 +259407,13 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
259154
259407
  const boundary = requireExplicitAutopilotExpansion("memorix_search", purpose);
259155
259408
  if (boundary) return boundary;
259156
259409
  }
259410
+ let surfacedKey = `project:${project.id}`;
259411
+ try {
259412
+ const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
259413
+ const active = await getActiveSession2(projectDir2, project.id);
259414
+ if (active) surfacedKey = `session:${active.id}`;
259415
+ } catch {
259416
+ }
259157
259417
  return withFreshIndex(async () => {
259158
259418
  const safeLimit2 = limit2 != null ? coerceNumber(limit2, 20) : void 0;
259159
259419
  const safeMaxTokens = maxTokens != null ? coerceNumber(maxTokens, 0) : void 0;
@@ -259171,7 +259431,8 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
259171
259431
  status: status ?? "active",
259172
259432
  source,
259173
259433
  quality,
259174
- reader: getObservationReader(scope === "global" ? "global" : "project")
259434
+ reader: getObservationReader(scope === "global" ? "global" : "project"),
259435
+ surfacedIds: getSurfacedIds(surfacedKey)
259175
259436
  });
259176
259437
  let result;
259177
259438
  try {
@@ -259190,6 +259451,7 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
259190
259451
  }
259191
259452
  throw error40;
259192
259453
  }
259454
+ recordSurfacedIds(surfacedKey, result.entries.map((entry) => entry.id));
259193
259455
  const activeBoundary = getActiveAutopilotRetrievalBoundary();
259194
259456
  if (scope !== "global" && activeBoundary && !force && result.entries.length > 0 && result.entries.every((entry) => activeBoundary.coveredObservationIds.has(entry.id))) {
259195
259457
  const duplicate = blockCoveredAutopilotEvidence("memorix_search", result.entries.map((entry) => entry.id), force);
@@ -262152,13 +262414,26 @@ ${lines.join("\n")}` }] };
262152
262414
  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";
262153
262415
  return void 0;
262154
262416
  }
262417
+ const mediaGenerationEnabled = process.env.MEMORIX_MCP_MEDIA_GENERATION === "1";
262418
+ const mediaTranscriptionEnabled = process.env.MEMORIX_MCP_MEDIA_TRANSCRIPTION === "1";
262419
+ const mediaGenerationOrTranscription = mediaGenerationEnabled || mediaTranscriptionEnabled;
262155
262420
  server.registerTool(
262156
262421
  "memorix_media",
262157
262422
  {
262158
262423
  title: "Manage Controlled Media",
262159
262424
  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.",
262160
262425
  inputSchema: {
262161
- action: external_exports2.enum(["import", "attach", "list", "show", "derive-pdf", "derive-audio", "generate-image", "generate-video", "status", "cancel"]),
262426
+ action: external_exports2.enum([
262427
+ "import",
262428
+ "attach",
262429
+ "list",
262430
+ "show",
262431
+ "derive-pdf",
262432
+ "status",
262433
+ "cancel",
262434
+ ...mediaTranscriptionEnabled ? ["derive-audio"] : [],
262435
+ ...mediaGenerationEnabled ? ["generate-image", "generate-video"] : []
262436
+ ]),
262162
262437
  path: external_exports2.string().optional().describe("Explicit local image/audio/video/PDF path for import."),
262163
262438
  assetId: external_exports2.string().optional().describe("Controlled MediaAsset ID for attach/show."),
262164
262439
  jobId: external_exports2.string().optional().describe("Durable media job ID for status."),
@@ -262166,20 +262441,26 @@ ${lines.join("\n")}` }] };
262166
262441
  limit: external_exports2.number().int().min(1).max(100).optional().describe("Maximum assets to list."),
262167
262442
  title: external_exports2.string().optional().describe("Observation title when attaching generated/imported output."),
262168
262443
  narrative: external_exports2.string().optional().describe("Short retrieval text when attaching an asset."),
262169
- prompt: external_exports2.string().optional().describe("Explicit MiniMax generation prompt."),
262170
- image: external_exports2.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
262171
- model: external_exports2.string().max(160).optional().describe("Provider model for the selected media action."),
262172
- provider: external_exports2.enum(["openai", "groq"]).optional().describe("Explicit audio transcription provider."),
262173
- language: external_exports2.string().max(32).optional().describe("Optional ISO-639-1 hint for audio transcription."),
262174
- region: external_exports2.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
262175
- n: external_exports2.number().int().min(1).max(4).optional().describe("Image output count."),
262176
- ratio: external_exports2.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."),
262177
- width: external_exports2.number().int().min(1).max(8192).optional().describe("Requested image width."),
262178
- height: external_exports2.number().int().min(1).max(8192).optional().describe("Requested image height."),
262179
- duration: external_exports2.union([external_exports2.literal(5), external_exports2.literal(10)]).optional().describe("MiniMax video duration in seconds."),
262180
262444
  maxPages: external_exports2.number().int().min(1).max(100).optional().describe("Bounded PDF extraction page limit."),
262181
262445
  maxChars: external_exports2.number().int().min(1).max(6e4).optional().describe("Bounded PDF extraction character limit."),
262182
- attach: external_exports2.boolean().optional().describe("Attach generated/imported output to normal project memory explicitly.")
262446
+ attach: external_exports2.boolean().optional().describe("Attach generated/imported output to normal project memory explicitly."),
262447
+ ...mediaGenerationOrTranscription ? {
262448
+ model: external_exports2.string().max(160).optional().describe("Provider model for the selected media action."),
262449
+ prompt: external_exports2.string().optional().describe("Explicit prompt for generation or transcription.")
262450
+ } : {},
262451
+ ...mediaTranscriptionEnabled ? {
262452
+ provider: external_exports2.enum(["openai", "groq"]).optional().describe("Explicit audio transcription provider."),
262453
+ language: external_exports2.string().max(32).optional().describe("Optional ISO-639-1 hint for audio transcription.")
262454
+ } : {},
262455
+ ...mediaGenerationEnabled ? {
262456
+ image: external_exports2.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
262457
+ region: external_exports2.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
262458
+ n: external_exports2.number().int().min(1).max(4).optional().describe("Image output count."),
262459
+ ratio: external_exports2.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."),
262460
+ width: external_exports2.number().int().min(1).max(8192).optional().describe("Requested image width."),
262461
+ height: external_exports2.number().int().min(1).max(8192).optional().describe("Requested image height."),
262462
+ duration: external_exports2.union([external_exports2.literal(5), external_exports2.literal(10)]).optional().describe("MiniMax video duration in seconds.")
262463
+ } : {}
262183
262464
  }
262184
262465
  },
262185
262466
  async ({ action, path: assetPath, assetId, jobId, kind, limit: limit2, title, narrative, prompt: prompt2, image, model, provider: provider2, language, region, n: n3, ratio, width, height, duration: duration3, maxPages, maxChars, attach }) => {
@@ -262665,6 +262946,7 @@ var init_server4 = __esm({
262665
262946
  init_formation_timeout();
262666
262947
  init_timeout();
262667
262948
  init_secret_filter();
262949
+ init_surfaced_registry();
262668
262950
  init_request_context();
262669
262951
  FORMATION_TIMEOUT_MS = parseFormationTimeoutMs(process.env.MEMORIX_FORMATION_TIMEOUT_MS);
262670
262952
  COMPACT_ON_WRITE_TIMEOUT_MS = 12e3;
@@ -265243,7 +265525,8 @@ var init_control_plane_maintenance = __esm({
265243
265525
  "claim-requalification",
265244
265526
  "knowledge-compile",
265245
265527
  "knowledge-lint",
265246
- "workflow-index"
265528
+ "workflow-index",
265529
+ "long-term-maintenance"
265247
265530
  ];
265248
265531
  TARGET_WAIT_RETRY_MS = 3e4;
265249
265532
  }
@@ -265255,7 +265538,8 @@ __export(serve_http_exports, {
265255
265538
  DEFAULT_SESSION_TIMEOUT_MS: () => DEFAULT_SESSION_TIMEOUT_MS,
265256
265539
  _testing: () => _testing2,
265257
265540
  default: () => serve_http_default,
265258
- parseSessionTimeoutMs: () => parseSessionTimeoutMs
265541
+ parseSessionTimeoutMs: () => parseSessionTimeoutMs,
265542
+ resolveServeHttpDefaults: () => resolveServeHttpDefaults
265259
265543
  });
265260
265544
  function parseSessionTimeoutMs(raw) {
265261
265545
  const value = raw?.trim();
@@ -265264,6 +265548,15 @@ function parseSessionTimeoutMs(raw) {
265264
265548
  if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_SESSION_TIMEOUT_MS;
265265
265549
  return Math.floor(parsed);
265266
265550
  }
265551
+ function resolveServeHttpDefaults(argsPort, configured) {
265552
+ const configuredPort = typeof configured.port === "number" && Number.isFinite(configured.port) ? configured.port : void 0;
265553
+ const port = parseTcpPortOrReport(argsPort, configuredPort ?? 3211);
265554
+ if (port === void 0) {
265555
+ return { port: 3211 };
265556
+ }
265557
+ const transportNotice = configured.transport === "stdio" ? '[memorix] config server.transport = "stdio"; serve-http continues on HTTP (the explicit command wins).' : void 0;
265558
+ return { port, transportNotice };
265559
+ }
265267
265560
  var DEFAULT_SESSION_TIMEOUT_MS, serve_http_default, _testing2;
265268
265561
  var init_serve_http = __esm({
265269
265562
  "src/cli/commands/serve-http.ts"() {
@@ -265314,11 +265607,15 @@ var init_serve_http = __esm({
265314
265607
  const { findGitInSubdirs: findGitInSubdirs2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
265315
265608
  const { writeFileSync: writeFileSync11, mkdirSync: mkdirSync11 } = await import("fs");
265316
265609
  const { homedir: homedir36 } = await import("os");
265317
- const port = parseTcpPortOrReport(args.port, 3211);
265318
- if (port === void 0) {
265319
- process.exitCode = 1;
265320
- return;
265610
+ let configuredDefaults = {};
265611
+ try {
265612
+ const { getServerConfig: getServerConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
265613
+ configuredDefaults = getServerConfig2();
265614
+ } catch {
265321
265615
  }
265616
+ const resolved = resolveServeHttpDefaults(args.port, configuredDefaults);
265617
+ if (resolved.transportNotice) console.error(resolved.transportNotice);
265618
+ const port = resolved.port;
265322
265619
  const host = args.host || "127.0.0.1";
265323
265620
  const toolProfile = resolveToolProfile({ explicit: args.mode, envValue: process.env.MEMORIX_MODE, fallback: "team" });
265324
265621
  let safeCwd;
@@ -266190,8 +266487,8 @@ var init_serve_http = __esm({
266190
266487
  canonicalId = await aliasModule.getCanonicalId(idProjectId);
266191
266488
  const registryPath = pathModule.default.join(baseDir, ".project-aliases.json");
266192
266489
  const raw = await fsPromises.readFile(registryPath, "utf-8");
266193
- const registry2 = JSON.parse(raw);
266194
- aliasGroups = registry2.groups || [];
266490
+ const registry3 = JSON.parse(raw);
266491
+ aliasGroups = registry3.groups || [];
266195
266492
  } catch {
266196
266493
  }
266197
266494
  const currentGroup = aliasGroups.find((g4) => g4.aliases?.includes(idProjectId) || g4.canonical === idProjectId);
@@ -269262,7 +269559,8 @@ function normalizeCommitCount(count3) {
269262
269559
  }
269263
269560
  return count3;
269264
269561
  }
269265
- function getCommitInfo(cwd, ref = "HEAD") {
269562
+ function getCommitInfo(cwd, ref = "HEAD", maxDiffChars = 500) {
269563
+ const cap = Number.isFinite(maxDiffChars) ? Math.max(0, Math.floor(maxDiffChars)) : 500;
269266
269564
  const FORMAT = "%H%n%h%n%aN%n%aI%n%s%n%b";
269267
269565
  const revision = normalizeCommitRef(ref);
269268
269566
  const raw = runGit3(cwd, ["log", "-1", `--format=${FORMAT}`, "--end-of-options", revision]).trim();
@@ -269290,7 +269588,7 @@ function getCommitInfo(cwd, ref = "HEAD") {
269290
269588
  let diffSummary = "";
269291
269589
  try {
269292
269590
  const diff = runGit3(cwd, ["diff-tree", "-p", "--no-commit-id", hash4], 1024 * 100);
269293
- diffSummary = diff.substring(0, 500);
269591
+ diffSummary = diff.substring(0, cap);
269294
269592
  } catch {
269295
269593
  }
269296
269594
  return {
@@ -269541,8 +269839,23 @@ var init_noise_filter = __esm({
269541
269839
  // src/cli/commands/ingest-commit.ts
269542
269840
  var ingest_commit_exports = {};
269543
269841
  __export(ingest_commit_exports, {
269842
+ applyGitIngestPolicy: () => applyGitIngestPolicy,
269544
269843
  default: () => ingest_commit_default
269545
269844
  });
269845
+ function applyGitIngestPolicy(commit, policy) {
269846
+ if (policy.ingestOnCommit === false) {
269847
+ return {
269848
+ skip: true,
269849
+ reason: "ingest_on_commit is disabled",
269850
+ diffSummary: commit.diffSummary
269851
+ };
269852
+ }
269853
+ const cap = policy.maxDiffSize;
269854
+ return {
269855
+ skip: false,
269856
+ diffSummary: typeof cap === "number" && Number.isFinite(cap) ? commit.diffSummary.slice(0, Math.max(0, Math.floor(cap))) : commit.diffSummary
269857
+ };
269858
+ }
269546
269859
  var ingest_commit_default;
269547
269860
  var init_ingest_commit = __esm({
269548
269861
  "src/cli/commands/ingest-commit.ts"() {
@@ -269585,11 +269898,21 @@ var init_ingest_commit = __esm({
269585
269898
  const force = !!args.force;
269586
269899
  if (!auto) we(`Ingest commit: ${ref}`);
269587
269900
  try {
269588
- const { getCommitInfo: getCommitInfo2, ingestCommit: ingestCommit2 } = await Promise.resolve().then(() => (init_extractor(), extractor_exports));
269589
- const commit = getCommitInfo2(cwd, ref);
269590
- const { shouldFilterCommit: shouldFilterCommit2 } = await Promise.resolve().then(() => (init_noise_filter(), noise_filter_exports));
269591
269901
  const { getGitConfig: getGitConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
269592
269902
  const gitCfg = getGitConfig2();
269903
+ const { getCommitInfo: getCommitInfo2, ingestCommit: ingestCommit2 } = await Promise.resolve().then(() => (init_extractor(), extractor_exports));
269904
+ const commit = getCommitInfo2(cwd, ref, gitCfg.maxDiffSize ?? 500);
269905
+ const policy = applyGitIngestPolicy(commit, {
269906
+ ingestOnCommit: gitCfg.ingestOnCommit,
269907
+ maxDiffSize: gitCfg.maxDiffSize
269908
+ });
269909
+ if (policy.skip && auto) {
269910
+ console.error(`[memorix] Skipped ${commit.shortHash}: ${policy.reason}`);
269911
+ process.exit(0);
269912
+ return;
269913
+ }
269914
+ commit.diffSummary = policy.diffSummary;
269915
+ const { shouldFilterCommit: shouldFilterCommit2 } = await Promise.resolve().then(() => (init_noise_filter(), noise_filter_exports));
269593
269916
  const filterResult = shouldFilterCommit2(commit, {
269594
269917
  skipMergeCommits: gitCfg.skipMergeCommits,
269595
269918
  excludePatterns: gitCfg.excludePatterns,
@@ -272948,21 +273271,28 @@ var init_dashboard = __esm({
272948
273271
  args: {
272949
273272
  port: {
272950
273273
  type: "string",
272951
- description: "Port to run the dashboard on (default: 3210)",
272952
- default: "3210"
273274
+ description: "Port to run the dashboard on (default: [server].dashboardPort or 3210)",
273275
+ required: false
272953
273276
  }
272954
273277
  },
272955
273278
  run: async ({ args }) => {
272956
273279
  const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
272957
273280
  const { getProjectDataDir: getProjectDataDir2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
272958
273281
  const { startDashboard: startDashboard2 } = await Promise.resolve().then(() => (init_server3(), server_exports));
273282
+ let configuredDashboardPort;
273283
+ try {
273284
+ const { getServerConfig: getServerConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
273285
+ const serverConfig = getServerConfig2();
273286
+ configuredDashboardPort = typeof serverConfig.dashboardPort === "number" ? serverConfig.dashboardPort : void 0;
273287
+ } catch {
273288
+ }
272959
273289
  const project = detectProject2();
272960
273290
  if (!project) {
272961
273291
  console.error("Memorix requires a git repo to establish project identity. Run `git init` in this workspace first.");
272962
273292
  process.exit(1);
272963
273293
  }
272964
273294
  const dataDir = await getProjectDataDir2(project.id);
272965
- const port = parseTcpPortOrReport(args.port, 3210);
273295
+ const port = parseTcpPortOrReport(args.port, configuredDashboardPort ?? 3210);
272966
273296
  if (port === void 0) {
272967
273297
  process.exitCode = 1;
272968
273298
  return;
@@ -273219,6 +273549,116 @@ Project: ${projectName} (${projectId})
273219
273549
  }
273220
273550
  });
273221
273551
 
273552
+ // src/cli/commands/purge.ts
273553
+ var purge_exports = {};
273554
+ __export(purge_exports, {
273555
+ default: () => purge_default
273556
+ });
273557
+ var purge_default;
273558
+ var init_purge = __esm({
273559
+ "src/cli/commands/purge.ts"() {
273560
+ "use strict";
273561
+ init_esm_shims();
273562
+ init_dist2();
273563
+ init_detector();
273564
+ init_persistence();
273565
+ purge_default = defineCommand({
273566
+ meta: {
273567
+ name: "purge",
273568
+ description: "Retire memories from retrieval (current project by default, --all for every project)"
273569
+ },
273570
+ args: {
273571
+ all: {
273572
+ type: "boolean",
273573
+ description: "Purge every project instead of the current one",
273574
+ default: false
273575
+ },
273576
+ yes: {
273577
+ type: "boolean",
273578
+ description: "Skip the interactive confirmation",
273579
+ default: false
273580
+ },
273581
+ json: {
273582
+ type: "boolean",
273583
+ description: "Emit a machine-readable result",
273584
+ default: false
273585
+ }
273586
+ },
273587
+ async run({ args }) {
273588
+ const purgeAll = Boolean(args.all);
273589
+ const yes = Boolean(args.yes);
273590
+ const asJson = Boolean(args.json);
273591
+ if (!yes && !process.stdin.isTTY) {
273592
+ console.error("[ERROR] Non-interactive environment. Add --yes to confirm.");
273593
+ console.error("[ERROR] Refusing to purge memories without confirmation.");
273594
+ process.exitCode = 1;
273595
+ return;
273596
+ }
273597
+ if (!yes) {
273598
+ const readline = await import("readline");
273599
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
273600
+ const question = purgeAll ? "Retire ALL memories from every project? (y/N) " : "Retire all memories for this project? (y/N) ";
273601
+ const answer = await new Promise((resolve6) => rl.question(question, resolve6));
273602
+ rl.close();
273603
+ if (answer.trim().toLowerCase() !== "y") {
273604
+ console.log("Cancelled.");
273605
+ return;
273606
+ }
273607
+ }
273608
+ let projectId = null;
273609
+ if (!purgeAll) {
273610
+ const project = detectProject();
273611
+ if (!project) {
273612
+ console.error("[ERROR] No .git found \u2014 run from a project directory or pass --all.");
273613
+ process.exitCode = 1;
273614
+ return;
273615
+ }
273616
+ projectId = project.id;
273617
+ }
273618
+ const dataDir = await getProjectDataDir(projectId ?? "");
273619
+ const { initObservationStore: initObservationStore2 } = await Promise.resolve().then(() => (init_obs_store(), obs_store_exports));
273620
+ const { getAllObservations: getAllObservations2, resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
273621
+ const { withFreshIndex: withFreshIndex2 } = await Promise.resolve().then(() => (init_freshness(), freshness_exports));
273622
+ await initObservationStore2(dataDir);
273623
+ const observations2 = await withFreshIndex2(() => getAllObservations2());
273624
+ const target = purgeAll ? observations2 : observations2.filter((o4) => o4.projectId === projectId);
273625
+ const ids = target.map((o4) => o4.id).filter((id) => typeof id === "number");
273626
+ const archived = ids.length > 0 ? (await resolveObservations2(ids, "archived")).resolved.length : 0;
273627
+ const { LongTermMemoryStore: LongTermMemoryStore2 } = await Promise.resolve().then(() => (init_long_term_store(), long_term_store_exports));
273628
+ const { archiveLongTermMemory: archiveLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
273629
+ const longTermStore = new LongTermMemoryStore2();
273630
+ await longTermStore.init(dataDir);
273631
+ let longTermArchived = 0;
273632
+ for (const memory of longTermStore.list()) {
273633
+ if (memory.state === "archived" || memory.state === "superseded") continue;
273634
+ if (!purgeAll && memory.originProjectId !== projectId) continue;
273635
+ try {
273636
+ await archiveLongTermMemory2({
273637
+ dataDir,
273638
+ id: memory.id,
273639
+ reason: purgeAll ? "purge --all" : "purge (project) " + (projectId ?? "")
273640
+ });
273641
+ longTermArchived += 1;
273642
+ } catch {
273643
+ }
273644
+ }
273645
+ const result = {
273646
+ scope: purgeAll ? "all" : projectId,
273647
+ observationsArchived: archived,
273648
+ longTermArchived
273649
+ };
273650
+ if (asJson) {
273651
+ console.log(JSON.stringify(result));
273652
+ } else {
273653
+ console.log(
273654
+ `[OK] Purged ${purgeAll ? "all projects" : result.scope}: archived ${archived} observations and ${longTermArchived} long-term memories.`
273655
+ );
273656
+ }
273657
+ }
273658
+ });
273659
+ }
273660
+ });
273661
+
273222
273662
  // src/cli/commands/uninstall.ts
273223
273663
  var uninstall_exports = {};
273224
273664
  __export(uninstall_exports, {
@@ -282119,6 +282559,7 @@ var main2 = defineCommand({
282119
282559
  repair: () => Promise.resolve().then(() => (init_repair3(), repair_exports)).then((m6) => m6.default),
282120
282560
  dashboard: () => Promise.resolve().then(() => (init_dashboard(), dashboard_exports)).then((m6) => m6.default),
282121
282561
  cleanup: () => Promise.resolve().then(() => (init_cleanup(), cleanup_exports)).then((m6) => m6.default),
282562
+ purge: () => Promise.resolve().then(() => (init_purge(), purge_exports)).then((m6) => m6.default),
282122
282563
  uninstall: () => Promise.resolve().then(() => (init_uninstall(), uninstall_exports)).then((m6) => m6.default),
282123
282564
  orchestrate: () => Promise.resolve().then(() => (init_orchestrate(), orchestrate_exports)).then((m6) => m6.default),
282124
282565
  workbench: () => Promise.resolve(defineCommand({
@@ -282194,6 +282635,7 @@ var main2 = defineCommand({
282194
282635
  "repair",
282195
282636
  "dashboard",
282196
282637
  "cleanup",
282638
+ "purge",
282197
282639
  "uninstall",
282198
282640
  "orchestrate"
282199
282641
  ];
@@ -282257,6 +282699,7 @@ var main2 = defineCommand({
282257
282699
  console.error(" dashboard Open standalone dashboard (read-mostly)");
282258
282700
  console.error(" hooks Open legacy hook installer menu");
282259
282701
  console.error(" cleanup Remove old memories");
282702
+ console.error(" purge Retire all memories (project by default, --all for everything)");
282260
282703
  console.error(" sync Rules/workspace sync plus interactive wizard");
282261
282704
  console.error(" ingest Ingest commit, log, or image knowledge");
282262
282705
  console.error("\nRun `memorix` in an interactive terminal for memcode TUI.");