memorix 1.6.1 → 1.6.2

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 (113) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/TEAM.md +86 -86
  3. package/dist/cli/index.js +1574 -1063
  4. package/dist/index.js +588 -107
  5. package/dist/maintenance-runner.js +565 -84
  6. package/dist/media-audio-runner.js +565 -84
  7. package/dist/media-video-runner.js +565 -84
  8. package/dist/memcode-runtime/CHANGELOG.md +22 -0
  9. package/dist/sdk.js +621 -111
  10. package/dist/vector-backfill-runner.js +565 -84
  11. package/docs/DESIGN_DECISIONS.md +357 -357
  12. package/docs/PERFORMANCE.md +15 -0
  13. package/package.json +2 -1
  14. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  15. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  16. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  17. package/plugins/copilot/memorix/plugin.json +1 -1
  18. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  19. package/plugins/omp/memorix/package.json +1 -1
  20. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  21. package/plugins/pi/memorix/package.json +1 -1
  22. package/server.json +2 -2
  23. package/src/audit/index.ts +156 -156
  24. package/src/cli/commands/background.ts +34 -2
  25. package/src/cli/commands/formation.ts +48 -48
  26. package/src/cli/commands/git-hook-install.ts +111 -111
  27. package/src/cli/commands/handoff.ts +54 -54
  28. package/src/cli/commands/hooks-status.ts +63 -63
  29. package/src/cli/commands/ingest.ts +44 -44
  30. package/src/cli/commands/integrate-shared.ts +15 -15
  31. package/src/cli/commands/lock.ts +82 -82
  32. package/src/cli/commands/message.ts +104 -104
  33. package/src/cli/commands/operator-shared.ts +6 -3
  34. package/src/cli/commands/poll.ts +58 -58
  35. package/src/cli/commands/reasoning.ts +118 -118
  36. package/src/cli/commands/serve-http.ts +4 -0
  37. package/src/cli/commands/skills.ts +114 -114
  38. package/src/cli/commands/task.ts +167 -167
  39. package/src/cli/commands/transfer.ts +47 -47
  40. package/src/cli/index.ts +406 -406
  41. package/src/cli/tui/ChatView.tsx +234 -234
  42. package/src/cli/tui/CommandBar.tsx +312 -312
  43. package/src/cli/tui/ContextRail.tsx +118 -118
  44. package/src/cli/tui/HeaderBar.tsx +72 -72
  45. package/src/cli/tui/LogoBanner.tsx +51 -51
  46. package/src/cli/tui/index.ts +41 -41
  47. package/src/cli/version.ts +7 -7
  48. package/src/cli/workbench.ts +1 -1
  49. package/src/dashboard/project-classification.ts +64 -64
  50. package/src/embedding/api-provider.ts +503 -27
  51. package/src/embedding/provider.ts +20 -3
  52. package/src/git/hooks-path.ts +85 -85
  53. package/src/hooks/lifecycle.ts +1 -0
  54. package/src/hooks/pattern-detector.ts +173 -173
  55. package/src/hooks/significance-filter.ts +250 -250
  56. package/src/llm/memory-manager.ts +328 -328
  57. package/src/llm/quality.ts +248 -248
  58. package/src/memory/attribution-guard.ts +249 -249
  59. package/src/memory/disclosure-policy.ts +135 -135
  60. package/src/memory/entity-extractor.ts +197 -197
  61. package/src/memory/formation/evaluate.ts +217 -217
  62. package/src/memory/freshness.ts +122 -122
  63. package/src/memory/graph.ts +197 -197
  64. package/src/memory/observations.ts +68 -18
  65. package/src/memory/refs.ts +94 -94
  66. package/src/memory/secret-filter.ts +79 -79
  67. package/src/orchestrate/adapters/claude-stream.ts +192 -192
  68. package/src/orchestrate/adapters/claude.ts +111 -111
  69. package/src/orchestrate/adapters/codex-stream.ts +134 -134
  70. package/src/orchestrate/adapters/codex.ts +41 -41
  71. package/src/orchestrate/adapters/gemini-stream.ts +166 -166
  72. package/src/orchestrate/adapters/gemini.ts +42 -42
  73. package/src/orchestrate/adapters/index.ts +73 -73
  74. package/src/orchestrate/adapters/opencode-stream.ts +143 -143
  75. package/src/orchestrate/adapters/opencode.ts +47 -47
  76. package/src/orchestrate/adapters/types.ts +77 -77
  77. package/src/orchestrate/capability-router.ts +284 -284
  78. package/src/orchestrate/cost-tracker.ts +219 -219
  79. package/src/orchestrate/error-recovery.ts +191 -191
  80. package/src/orchestrate/evidence.ts +140 -140
  81. package/src/orchestrate/ledger.ts +110 -110
  82. package/src/orchestrate/memorix-bridge.ts +343 -343
  83. package/src/orchestrate/output-budget.ts +80 -80
  84. package/src/orchestrate/permission.ts +152 -152
  85. package/src/orchestrate/pipeline-trace.ts +131 -131
  86. package/src/orchestrate/prompt-builder.ts +155 -155
  87. package/src/orchestrate/ring-buffer.ts +37 -37
  88. package/src/orchestrate/task-graph.ts +389 -389
  89. package/src/project/aliases.ts +374 -374
  90. package/src/rules/adapters/trae.ts +56 -56
  91. package/src/rules/syncer.ts +235 -235
  92. package/src/sdk.ts +12 -8
  93. package/src/search/query-expansion.ts +52 -52
  94. package/src/server/formation-timeout.ts +27 -27
  95. package/src/skills/mini-skills.ts +386 -386
  96. package/src/store/chat-store.ts +119 -119
  97. package/src/store/graph-store.ts +249 -249
  98. package/src/store/mini-skill-store.ts +349 -349
  99. package/src/store/orama-store.ts +21 -3
  100. package/src/store/persistence-json.ts +212 -212
  101. package/src/store/persistence.ts +291 -291
  102. package/src/store/project-affinity.ts +195 -195
  103. package/src/team/event-bus.ts +76 -76
  104. package/src/team/handoff.ts +161 -161
  105. package/src/team/poll.ts +132 -132
  106. package/src/workspace/mcp-adapters/codex.ts +191 -191
  107. package/src/workspace/mcp-adapters/copilot.ts +105 -105
  108. package/src/workspace/mcp-adapters/cursor.ts +53 -53
  109. package/src/workspace/mcp-adapters/kiro.ts +64 -64
  110. package/src/workspace/mcp-adapters/opencode.ts +123 -123
  111. package/src/workspace/mcp-adapters/trae.ts +134 -134
  112. package/src/workspace/mcp-adapters/windsurf.ts +91 -91
  113. package/src/workspace/sanitizer.ts +60 -60
package/dist/sdk.js CHANGED
@@ -249,6 +249,14 @@ var init_bun_sqlite_compat = __esm({
249
249
  });
250
250
 
251
251
  // src/store/sqlite-db.ts
252
+ var sqlite_db_exports = {};
253
+ __export(sqlite_db_exports, {
254
+ closeAllDatabases: () => closeAllDatabases,
255
+ closeDatabase: () => closeDatabase,
256
+ getDatabase: () => getDatabase,
257
+ isSqliteAvailable: () => isSqliteAvailable,
258
+ loadBetterSqlite3: () => loadBetterSqlite3
259
+ });
252
260
  import "module";
253
261
  import path2 from "path";
254
262
  import fs from "fs";
@@ -354,6 +362,17 @@ function getDatabase(dataDir) {
354
362
  _dbCache.set(normalized, db2);
355
363
  return db2;
356
364
  }
365
+ function closeDatabase(dataDir) {
366
+ const normalized = path2.resolve(dataDir);
367
+ const db2 = _dbCache.get(normalized);
368
+ if (db2) {
369
+ try {
370
+ db2.close();
371
+ } catch {
372
+ }
373
+ _dbCache.delete(normalized);
374
+ }
375
+ }
357
376
  function closeAllDatabases() {
358
377
  for (const [key, db2] of _dbCache) {
359
378
  try {
@@ -363,6 +382,14 @@ function closeAllDatabases() {
363
382
  _dbCache.delete(key);
364
383
  }
365
384
  }
385
+ function isSqliteAvailable() {
386
+ try {
387
+ loadBetterSqlite3();
388
+ return true;
389
+ } catch {
390
+ return false;
391
+ }
392
+ }
366
393
  var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEMORY_OUTCOME_SIGNALS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
367
394
  var init_sqlite_db = __esm({
368
395
  "src/store/sqlite-db.ts"() {
@@ -1246,8 +1273,8 @@ async function acquireLock(lockPath) {
1246
1273
  const code = err instanceof Error && "code" in err ? err.code : void 0;
1247
1274
  if (code === "EEXIST" || code === "EPERM") {
1248
1275
  try {
1249
- const stat4 = await fs2.stat(lockPath);
1250
- if (Date.now() - stat4.mtimeMs > LOCK_STALE_MS) {
1276
+ const stat5 = await fs2.stat(lockPath);
1277
+ if (Date.now() - stat5.mtimeMs > LOCK_STALE_MS) {
1251
1278
  await fs2.unlink(lockPath).catch(() => {
1252
1279
  });
1253
1280
  continue;
@@ -3717,24 +3744,131 @@ var init_transformers_provider = __esm({
3717
3744
  // src/embedding/api-provider.ts
3718
3745
  var api_provider_exports = {};
3719
3746
  __export(api_provider_exports, {
3720
- APIEmbeddingProvider: () => APIEmbeddingProvider
3747
+ APIEmbeddingProvider: () => APIEmbeddingProvider,
3748
+ convertArrayCacheToJsonl: () => convertArrayCacheToJsonl,
3749
+ ensureDiskCacheLoaded: () => ensureDiskCacheLoaded,
3750
+ getApiEmbeddingCachePayloadBytesForTests: () => getApiEmbeddingCachePayloadBytesForTests,
3751
+ getApiEmbeddingDiskCacheLoadCountForTests: () => getApiEmbeddingDiskCacheLoadCountForTests,
3752
+ getApiEmbeddingJsonlLineCountCallsForTests: () => getApiEmbeddingJsonlLineCountCallsForTests,
3753
+ getApiEmbeddingMaxEntriesForDimensionsForTests: () => getApiEmbeddingMaxEntriesForDimensionsForTests,
3754
+ migrateJsonArrayCacheToJsonl: () => migrateJsonArrayCacheToJsonl,
3755
+ parseCacheJsonInWorker: () => parseCacheJsonInWorker,
3756
+ resetApiEmbeddingCacheForTests: () => resetApiEmbeddingCacheForTests,
3757
+ saveDiskCacheNow: () => saveDiskCacheNow,
3758
+ seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
3721
3759
  });
3722
3760
  import { createHash as createHash2 } from "crypto";
3723
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
3761
+ import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
3762
+ import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
3724
3763
  import { join as join3 } from "path";
3725
3764
  import { homedir as homedir7 } from "os";
3765
+ import { createInterface } from "readline";
3726
3766
  function cacheDir() {
3727
3767
  return process.env.MEMORIX_DATA_DIR || join3(homedir7(), ".memorix", "data");
3728
3768
  }
3729
3769
  function cacheFile() {
3730
3770
  return join3(cacheDir(), ".embedding-api-cache.json");
3731
3771
  }
3772
+ function cacheJsonlFile() {
3773
+ return join3(cacheDir(), ".embedding-api-cache.jsonl");
3774
+ }
3732
3775
  function dimsCacheFile() {
3733
3776
  return join3(cacheDir(), ".embedding-dims-cache.json");
3734
3777
  }
3735
3778
  function cacheMetaFile() {
3736
3779
  return join3(cacheDir(), ".embedding-api-cache-meta.json");
3737
3780
  }
3781
+ function cacheStatFile() {
3782
+ return join3(cacheDir(), ".embedding-api-cache-stat.json");
3783
+ }
3784
+ function payloadBytes(vector) {
3785
+ return vector.length * BYTES_PER_DIM;
3786
+ }
3787
+ function maxCachePayloadBytes() {
3788
+ const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
3789
+ if (raw) {
3790
+ const parsed = Number.parseInt(raw, 10);
3791
+ if (Number.isFinite(parsed) && parsed > 0) return parsed;
3792
+ }
3793
+ return MAX_CACHE_PAYLOAD_BYTES;
3794
+ }
3795
+ function maxEntriesForDimensions(dimensions) {
3796
+ const dim = Math.max(1, Math.floor(dimensions));
3797
+ const byBytes = Math.floor(maxCachePayloadBytes() / (dim * BYTES_PER_DIM));
3798
+ return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
3799
+ }
3800
+ function rememberDimensions(vector) {
3801
+ if (vector.length > 0 && (cacheDimensions === null || vector.length > cacheDimensions)) {
3802
+ cacheDimensions = vector.length;
3803
+ }
3804
+ }
3805
+ function evictOldest() {
3806
+ const firstKey = cache3.keys().next().value;
3807
+ if (firstKey === void 0) return;
3808
+ const old = cache3.get(firstKey);
3809
+ cache3.delete(firstKey);
3810
+ if (old) cachePayloadBytes -= payloadBytes(old);
3811
+ }
3812
+ function evictToFit(incomingBytes, incomingDims) {
3813
+ const limit2 = maxEntriesForDimensions(incomingDims);
3814
+ while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes + incomingBytes > maxCachePayloadBytes())) {
3815
+ evictOldest();
3816
+ }
3817
+ }
3818
+ function isNumberArray(value) {
3819
+ return Array.isArray(value) && value.every((entry) => typeof entry === "number");
3820
+ }
3821
+ function parseCacheRow(raw) {
3822
+ if (Array.isArray(raw)) {
3823
+ const hash4 = raw[0];
3824
+ const vector = raw[1];
3825
+ if (typeof hash4 === "string" && isNumberArray(vector)) {
3826
+ return { hash: hash4, vector };
3827
+ }
3828
+ return null;
3829
+ }
3830
+ if (raw && typeof raw === "object") {
3831
+ const row = raw;
3832
+ if (typeof row.h === "string" && isNumberArray(row.v)) {
3833
+ return { hash: row.h, vector: row.v };
3834
+ }
3835
+ }
3836
+ return null;
3837
+ }
3838
+ function cacheInsert(hash4, value, dirty) {
3839
+ rememberDimensions(value);
3840
+ const incoming = payloadBytes(value);
3841
+ const existing = cache3.get(hash4);
3842
+ if (existing) {
3843
+ cachePayloadBytes -= payloadBytes(existing);
3844
+ cache3.delete(hash4);
3845
+ }
3846
+ evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
3847
+ cache3.set(hash4, value);
3848
+ cachePayloadBytes += incoming;
3849
+ if (dirty) diskCacheDirty2 = true;
3850
+ }
3851
+ async function readCacheStat() {
3852
+ try {
3853
+ const data = JSON.parse(await readFile2(cacheStatFile(), "utf-8"));
3854
+ if (typeof data.entryCount === "number" && Number.isFinite(data.entryCount) && data.entryCount >= 0) {
3855
+ return {
3856
+ version: 1,
3857
+ entryCount: Math.floor(data.entryCount),
3858
+ dimensions: isValidDimension(data.dimensions) ? data.dimensions : null
3859
+ };
3860
+ }
3861
+ } catch {
3862
+ }
3863
+ return null;
3864
+ }
3865
+ async function writeCacheStat(stat5) {
3866
+ try {
3867
+ await mkdir2(cacheDir(), { recursive: true });
3868
+ await writeFile2(cacheStatFile(), JSON.stringify(stat5));
3869
+ } catch {
3870
+ }
3871
+ }
3738
3872
  function normalizeText(text2) {
3739
3873
  return text2.replace(/\s+/g, " ").trim().slice(0, MAX_INPUT_CHARS);
3740
3874
  }
@@ -3820,29 +3954,300 @@ function toProviderInput(input, baseUrl) {
3820
3954
  }
3821
3955
  return { type: input.modality, url: input.url };
3822
3956
  }
3957
+ function parseCacheJsonInWorker(filePath) {
3958
+ return new Promise((resolve2, reject) => {
3959
+ void import("worker_threads").then(({ Worker }) => {
3960
+ const worker = new Worker(
3961
+ `
3962
+ const { parentPort, workerData } = require('node:worker_threads');
3963
+ const { readFileSync } = require('node:fs');
3964
+ try {
3965
+ const raw = readFileSync(workerData.filePath, 'utf8');
3966
+ parentPort.postMessage({ ok: true, entries: JSON.parse(raw) });
3967
+ } catch (err) {
3968
+ parentPort.postMessage({
3969
+ ok: false,
3970
+ error: err instanceof Error ? err.message : String(err),
3971
+ });
3972
+ }
3973
+ `,
3974
+ { eval: true, workerData: { filePath } }
3975
+ );
3976
+ worker.once("message", (msg) => {
3977
+ void worker.terminate();
3978
+ if (msg?.ok && Array.isArray(msg.entries)) {
3979
+ resolve2(msg.entries);
3980
+ return;
3981
+ }
3982
+ reject(new Error(msg?.error || "embedding cache worker parse failed"));
3983
+ });
3984
+ worker.once("error", reject);
3985
+ }).catch(reject);
3986
+ });
3987
+ }
3988
+ function findJsonValueEnd(source) {
3989
+ let i2 = 0;
3990
+ while (i2 < source.length && /\s/.test(source[i2])) i2 += 1;
3991
+ if (i2 >= source.length) return -1;
3992
+ const start = source[i2];
3993
+ if (start === '"') {
3994
+ i2 += 1;
3995
+ while (i2 < source.length) {
3996
+ if (source[i2] === "\\") {
3997
+ i2 += 2;
3998
+ continue;
3999
+ }
4000
+ if (source[i2] === '"') return i2 + 1;
4001
+ i2 += 1;
4002
+ }
4003
+ return -1;
4004
+ }
4005
+ if (start === "{" || start === "[") {
4006
+ const close = start === "{" ? "}" : "]";
4007
+ let depth = 0;
4008
+ let inString = false;
4009
+ for (; i2 < source.length; i2 += 1) {
4010
+ const ch = source[i2];
4011
+ if (inString) {
4012
+ if (ch === "\\") {
4013
+ i2 += 1;
4014
+ continue;
4015
+ }
4016
+ if (ch === '"') inString = false;
4017
+ continue;
4018
+ }
4019
+ if (ch === '"') {
4020
+ inString = true;
4021
+ continue;
4022
+ }
4023
+ if (ch === start) depth += 1;
4024
+ else if (ch === close) {
4025
+ depth -= 1;
4026
+ if (depth === 0) return i2 + 1;
4027
+ }
4028
+ }
4029
+ return -1;
4030
+ }
4031
+ const rest = source.slice(i2);
4032
+ const match2 = rest.match(/^(?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)/);
4033
+ return match2 ? i2 + match2[0].length : -1;
4034
+ }
4035
+ async function streamJsonArrayToJsonl(filePath, tmpPath) {
4036
+ const input = createReadStream(filePath, { encoding: "utf8" });
4037
+ const out = createWriteStream(tmpPath);
4038
+ let buf = "";
4039
+ let started = false;
4040
+ let count2 = 0;
4041
+ const writeLine = async (line) => {
4042
+ if (!out.write(line)) {
4043
+ await new Promise((resolve2, reject) => {
4044
+ out.once("drain", resolve2);
4045
+ out.once("error", reject);
4046
+ });
4047
+ }
4048
+ };
4049
+ try {
4050
+ for await (const chunk of input) {
4051
+ buf += chunk;
4052
+ if (!started) {
4053
+ const idx = buf.search(/\S/);
4054
+ if (idx < 0) continue;
4055
+ if (buf[idx] !== "[") throw new Error("embedding cache is not a JSON array");
4056
+ buf = buf.slice(idx + 1);
4057
+ started = true;
4058
+ }
4059
+ while (true) {
4060
+ buf = buf.replace(/^\s*,?\s*/, "");
4061
+ if (!buf) break;
4062
+ if (buf[0] === "]") {
4063
+ buf = "";
4064
+ break;
4065
+ }
4066
+ const end = findJsonValueEnd(buf);
4067
+ if (end < 0) break;
4068
+ const value = JSON.parse(buf.slice(0, end));
4069
+ buf = buf.slice(end);
4070
+ const row = parseCacheRow(value);
4071
+ if (row) {
4072
+ await writeLine(`${JSON.stringify({ h: row.hash, v: row.vector })}
4073
+ `);
4074
+ count2 += 1;
4075
+ if (count2 % 50 === 0) {
4076
+ await new Promise((resolve2) => setImmediate(resolve2));
4077
+ }
4078
+ }
4079
+ }
4080
+ if (buf.length > MAX_MIGRATION_VALUE_BYTES) {
4081
+ throw new Error("embedding cache entry too large to migrate safely");
4082
+ }
4083
+ }
4084
+ } finally {
4085
+ await new Promise((resolve2, reject) => {
4086
+ out.end(() => resolve2());
4087
+ out.once("error", reject);
4088
+ });
4089
+ }
4090
+ return count2;
4091
+ }
4092
+ async function migrateJsonArrayCacheToJsonl(filePath, jsonlPath) {
4093
+ const tmpPath = `${jsonlPath}.tmp`;
4094
+ try {
4095
+ const count2 = await streamJsonArrayToJsonl(filePath, tmpPath);
4096
+ await rename(tmpPath, jsonlPath);
4097
+ diskCacheEntryCount = count2;
4098
+ await writeCacheStat({
4099
+ version: 1,
4100
+ entryCount: count2,
4101
+ dimensions: cacheDimensions
4102
+ });
4103
+ return count2;
4104
+ } catch (err) {
4105
+ await unlink(tmpPath).catch(() => void 0);
4106
+ throw err;
4107
+ }
4108
+ }
4109
+ function convertArrayCacheToJsonl(filePath, jsonlPath) {
4110
+ return migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
4111
+ }
4112
+ async function loadCacheJsonl(filePath) {
4113
+ const rl = createInterface({
4114
+ input: createReadStream(filePath, { encoding: "utf8" }),
4115
+ crlfDelay: Infinity
4116
+ });
4117
+ let loaded = 0;
4118
+ let onDiskCount = 0;
4119
+ for await (const line of rl) {
4120
+ const trimmed = line.trim();
4121
+ if (!trimmed) continue;
4122
+ try {
4123
+ const row = parseCacheRow(JSON.parse(trimmed));
4124
+ if (!row) continue;
4125
+ onDiskCount += 1;
4126
+ cacheInsert(row.hash, row.vector, false);
4127
+ loaded = cache3.size;
4128
+ } catch {
4129
+ }
4130
+ if (onDiskCount % 250 === 0) {
4131
+ await new Promise((resolve2) => setImmediate(resolve2));
4132
+ }
4133
+ }
4134
+ diskCacheEntryCount = onDiskCount;
4135
+ await writeCacheStat({
4136
+ version: 1,
4137
+ entryCount: onDiskCount,
4138
+ dimensions: cacheDimensions
4139
+ });
4140
+ return loaded;
4141
+ }
4142
+ async function countJsonlLines(filePath) {
4143
+ jsonlLineCountCalls += 1;
4144
+ try {
4145
+ const rl = createInterface({
4146
+ input: createReadStream(filePath, { encoding: "utf8" }),
4147
+ crlfDelay: Infinity
4148
+ });
4149
+ let count2 = 0;
4150
+ for await (const line of rl) {
4151
+ if (line.trim()) count2 += 1;
4152
+ }
4153
+ return count2;
4154
+ } catch {
4155
+ return 0;
4156
+ }
4157
+ }
4158
+ async function resolveOnDiskCount(jsonlPath) {
4159
+ if (diskCacheEntryCount > 0) return diskCacheEntryCount;
4160
+ const sidecar = await readCacheStat();
4161
+ if (sidecar) {
4162
+ diskCacheEntryCount = sidecar.entryCount;
4163
+ if (sidecar.dimensions && cacheDimensions === null) cacheDimensions = sidecar.dimensions;
4164
+ return sidecar.entryCount;
4165
+ }
4166
+ try {
4167
+ await access(jsonlPath, fsConstants.R_OK);
4168
+ } catch {
4169
+ return 0;
4170
+ }
4171
+ const counted = await countJsonlLines(jsonlPath);
4172
+ diskCacheEntryCount = counted;
4173
+ await writeCacheStat({
4174
+ version: 1,
4175
+ entryCount: counted,
4176
+ dimensions: cacheDimensions
4177
+ });
4178
+ return counted;
4179
+ }
3823
4180
  async function loadDiskCache2() {
3824
- if (diskCacheLoaded) return;
4181
+ if (diskCacheLoaded) return cache3.size;
4182
+ let loaded = 0;
3825
4183
  try {
3826
- const raw = await readFile2(cacheFile(), "utf-8");
3827
- const entries = JSON.parse(raw);
3828
- for (const [k, v] of entries) cache3.set(k, v);
3829
- console.error(`[memorix] Loaded ${entries.length} cached API embeddings from disk`);
4184
+ const jsonlPath = cacheJsonlFile();
4185
+ let hasJsonl = false;
4186
+ try {
4187
+ await access(jsonlPath, fsConstants.R_OK);
4188
+ hasJsonl = true;
4189
+ } catch {
4190
+ hasJsonl = false;
4191
+ }
4192
+ if (hasJsonl) {
4193
+ loaded = await loadCacheJsonl(jsonlPath);
4194
+ } else {
4195
+ const filePath = cacheFile();
4196
+ await stat(filePath);
4197
+ await migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
4198
+ loaded = await loadCacheJsonl(jsonlPath);
4199
+ }
4200
+ diskCacheLoadCount += 1;
4201
+ console.error(`[memorix] Loaded ${loaded} cached API embeddings from disk`);
3830
4202
  } catch {
3831
4203
  }
3832
4204
  diskCacheLoaded = true;
4205
+ return loaded;
3833
4206
  }
3834
4207
  function startDiskCacheLoad() {
3835
4208
  if (diskCacheLoaded || diskCacheLoadPromise) return;
3836
- diskCacheLoadPromise = loadDiskCache2().catch(() => {
4209
+ diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
3837
4210
  });
3838
4211
  }
3839
4212
  async function ensureDiskCacheLoaded() {
3840
- if (diskCacheLoaded) return;
3841
- if (diskCacheLoadPromise) {
3842
- await diskCacheLoadPromise;
3843
- return;
3844
- }
3845
- await loadDiskCache2();
4213
+ if (diskCacheLoaded) return cache3.size;
4214
+ if (!diskCacheLoadPromise) {
4215
+ diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
4216
+ });
4217
+ }
4218
+ await diskCacheLoadPromise;
4219
+ return cache3.size;
4220
+ }
4221
+ function getApiEmbeddingDiskCacheLoadCountForTests() {
4222
+ return diskCacheLoadCount;
4223
+ }
4224
+ function resetApiEmbeddingCacheForTests() {
4225
+ cache3.clear();
4226
+ cachePayloadBytes = 0;
4227
+ cacheDimensions = null;
4228
+ diskCacheEntryCount = 0;
4229
+ diskCacheLoaded = false;
4230
+ diskCacheLoadPromise = null;
4231
+ diskCacheLoadCount = 0;
4232
+ diskCacheDirty2 = false;
4233
+ jsonlLineCountCalls = 0;
4234
+ }
4235
+ function seedApiEmbeddingCacheForTests(entries) {
4236
+ cache3.clear();
4237
+ cachePayloadBytes = 0;
4238
+ cacheDimensions = null;
4239
+ for (const [key, value] of entries) cacheInsert(key, value, true);
4240
+ diskCacheLoaded = true;
4241
+ diskCacheDirty2 = true;
4242
+ }
4243
+ function getApiEmbeddingCachePayloadBytesForTests() {
4244
+ return cachePayloadBytes;
4245
+ }
4246
+ function getApiEmbeddingJsonlLineCountCallsForTests() {
4247
+ return jsonlLineCountCalls;
4248
+ }
4249
+ function getApiEmbeddingMaxEntriesForDimensionsForTests(dimensions) {
4250
+ return maxEntriesForDimensions(dimensions);
3846
4251
  }
3847
4252
  function dimsCacheKey(config) {
3848
4253
  return [
@@ -3958,8 +4363,46 @@ async function saveDiskCacheNow() {
3958
4363
  if (!diskCacheDirty2) return;
3959
4364
  try {
3960
4365
  await mkdir2(cacheDir(), { recursive: true });
3961
- const entries = Array.from(cache3.entries());
3962
- await writeFile2(cacheFile(), JSON.stringify(entries));
4366
+ const jsonlPath = cacheJsonlFile();
4367
+ const existing = await resolveOnDiskCount(jsonlPath);
4368
+ if (existing > 0 && cache3.size < Math.ceil(existing * 0.5)) {
4369
+ console.error(
4370
+ `[memorix] Refusing to overwrite embedding cache (${existing} on disk vs ${cache3.size} in memory)`
4371
+ );
4372
+ diskCacheDirty2 = false;
4373
+ return;
4374
+ }
4375
+ const tmpPath = `${jsonlPath}.tmp`;
4376
+ await new Promise((resolve2, reject) => {
4377
+ const out = createWriteStream(tmpPath);
4378
+ const entries = cache3.entries();
4379
+ const writeBatch = () => {
4380
+ for (let i2 = 0; i2 < 50; i2++) {
4381
+ const next = entries.next();
4382
+ if (next.done) {
4383
+ out.end();
4384
+ return;
4385
+ }
4386
+ const [hash4, vector] = next.value;
4387
+ if (!out.write(`${JSON.stringify({ h: hash4, v: vector })}
4388
+ `)) {
4389
+ out.once("drain", writeBatch);
4390
+ return;
4391
+ }
4392
+ }
4393
+ setImmediate(writeBatch);
4394
+ };
4395
+ out.on("finish", resolve2);
4396
+ out.on("error", reject);
4397
+ writeBatch();
4398
+ });
4399
+ await rename(tmpPath, jsonlPath);
4400
+ diskCacheEntryCount = cache3.size;
4401
+ await writeCacheStat({
4402
+ version: 1,
4403
+ entryCount: cache3.size,
4404
+ dimensions: cacheDimensions
4405
+ });
3963
4406
  diskCacheDirty2 = false;
3964
4407
  } catch {
3965
4408
  }
@@ -3973,12 +4416,7 @@ function scheduleDiskSave() {
3973
4416
  }, DISK_SAVE_DEBOUNCE_MS);
3974
4417
  }
3975
4418
  function cacheSet(hash4, value) {
3976
- if (cache3.size >= MAX_CACHE_SIZE3) {
3977
- const firstKey = cache3.keys().next().value;
3978
- if (firstKey !== void 0) cache3.delete(firstKey);
3979
- }
3980
- cache3.set(hash4, value);
3981
- diskCacheDirty2 = true;
4419
+ cacheInsert(hash4, value, true);
3982
4420
  }
3983
4421
  function resolveEnvEmbeddingApiKey() {
3984
4422
  return process.env.MEMORIX_EMBEDDING_API_KEY;
@@ -4097,18 +4535,27 @@ async function waitForRetry(waitMs, signal) {
4097
4535
  abortSignal.addEventListener("abort", onAbort, { once: true });
4098
4536
  });
4099
4537
  }
4100
- var cache3, MAX_CACHE_SIZE3, diskCacheDirty2, diskSaveTimer, diskCacheLoaded, diskCacheLoadPromise, MAX_INPUT_CHARS, MAX_CONCURRENCY, DISK_SAVE_DEBOUNCE_MS, DEFAULT_MAX_BATCH_SIZE, DASHSCOPE_MAX_BATCH_SIZE, MAX_RETRIES2, BASE_DELAY_MS, EmbeddingAPIError, APIEmbeddingProvider;
4538
+ var cache3, MAX_CACHE_ENTRIES, MAX_CACHE_PAYLOAD_BYTES, BYTES_PER_DIM, DEFAULT_ASSUMED_DIMENSIONS, MAX_MIGRATION_VALUE_BYTES, cachePayloadBytes, cacheDimensions, diskCacheEntryCount, diskCacheDirty2, diskSaveTimer, diskCacheLoaded, diskCacheLoadPromise, diskCacheLoadCount, jsonlLineCountCalls, MAX_INPUT_CHARS, MAX_CONCURRENCY, DISK_SAVE_DEBOUNCE_MS, DEFAULT_MAX_BATCH_SIZE, DASHSCOPE_MAX_BATCH_SIZE, MAX_RETRIES2, BASE_DELAY_MS, EmbeddingAPIError, APIEmbeddingProvider;
4101
4539
  var init_api_provider = __esm({
4102
4540
  "src/embedding/api-provider.ts"() {
4103
4541
  "use strict";
4104
4542
  init_esm_shims();
4105
4543
  init_provider();
4106
4544
  cache3 = /* @__PURE__ */ new Map();
4107
- MAX_CACHE_SIZE3 = 1e4;
4545
+ MAX_CACHE_ENTRIES = 2e5;
4546
+ MAX_CACHE_PAYLOAD_BYTES = 256 * 1024 * 1024;
4547
+ BYTES_PER_DIM = 8;
4548
+ DEFAULT_ASSUMED_DIMENSIONS = 4096;
4549
+ MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
4550
+ cachePayloadBytes = 0;
4551
+ cacheDimensions = null;
4552
+ diskCacheEntryCount = 0;
4108
4553
  diskCacheDirty2 = false;
4109
4554
  diskSaveTimer = null;
4110
4555
  diskCacheLoaded = false;
4111
4556
  diskCacheLoadPromise = null;
4557
+ diskCacheLoadCount = 0;
4558
+ jsonlLineCountCalls = 0;
4112
4559
  MAX_INPUT_CHARS = 32e3;
4113
4560
  MAX_CONCURRENCY = 4;
4114
4561
  DISK_SAVE_DEBOUNCE_MS = 5e3;
@@ -4166,7 +4613,9 @@ var init_api_provider = __esm({
4166
4613
  if (config.requestedDimensions) {
4167
4614
  console.error(`[memorix] Dimension shortening: ${config.requestedDimensions}d requested`);
4168
4615
  }
4169
- startDiskCacheLoad();
4616
+ if (allowNetworkProbe) {
4617
+ startDiskCacheLoad();
4618
+ }
4170
4619
  return new _APIEmbeddingProvider(config, probeDimensions);
4171
4620
  }
4172
4621
  static resolveConfig() {
@@ -4504,7 +4953,12 @@ function warnOnce(message) {
4504
4953
  lastEmbeddingWarningAt = now3;
4505
4954
  console.error(message);
4506
4955
  }
4956
+ function embeddingModeToken() {
4957
+ return process.env.MEMORIX_EMBEDDING?.toLowerCase()?.trim() ?? "";
4958
+ }
4507
4959
  function getEmbeddingMode2() {
4960
+ const token = embeddingModeToken();
4961
+ if (cachedEmbeddingMode && cachedEmbeddingModeToken === token) return cachedEmbeddingMode;
4508
4962
  try {
4509
4963
  let cfg;
4510
4964
  try {
@@ -4513,10 +4967,17 @@ function getEmbeddingMode2() {
4513
4967
  cfg = (init_config(), __toCommonJS(config_exports));
4514
4968
  }
4515
4969
  const { getEmbeddingMode: cfgMode } = cfg;
4516
- return cfgMode();
4970
+ cachedEmbeddingMode = cfgMode();
4971
+ cachedEmbeddingModeToken = token;
4972
+ return cachedEmbeddingMode;
4517
4973
  } catch {
4518
- const env2 = process.env.MEMORIX_EMBEDDING?.toLowerCase()?.trim();
4519
- if (env2 === "fastembed" || env2 === "transformers" || env2 === "api" || env2 === "auto") return env2;
4974
+ if (token === "fastembed" || token === "transformers" || token === "api" || token === "auto") {
4975
+ cachedEmbeddingMode = token;
4976
+ cachedEmbeddingModeToken = token;
4977
+ return token;
4978
+ }
4979
+ cachedEmbeddingMode = "off";
4980
+ cachedEmbeddingModeToken = token;
4520
4981
  return "off";
4521
4982
  }
4522
4983
  }
@@ -4772,13 +5233,15 @@ function isEmbeddingExplicitlyDisabled() {
4772
5233
  function resetProvider() {
4773
5234
  provider = null;
4774
5235
  initPromise = null;
5236
+ cachedEmbeddingMode = null;
5237
+ cachedEmbeddingModeToken = null;
4775
5238
  lastInitWasTemporaryFailure = false;
4776
5239
  lastFailureTimestamp = 0;
4777
5240
  lastEmbeddingFailure = null;
4778
5241
  lastEmbeddingWarning = "";
4779
5242
  lastEmbeddingWarningAt = 0;
4780
5243
  }
4781
- var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, lastInitWasTemporaryFailure, lastEmbeddingFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
5244
+ var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, cachedEmbeddingMode, cachedEmbeddingModeToken, lastInitWasTemporaryFailure, lastEmbeddingFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
4782
5245
  var init_provider = __esm({
4783
5246
  "src/embedding/provider.ts"() {
4784
5247
  "use strict";
@@ -4802,6 +5265,8 @@ var init_provider = __esm({
4802
5265
  MAX_INLINE_EMBEDDING_BYTES = 5 * 1024 * 1024;
4803
5266
  provider = null;
4804
5267
  initPromise = null;
5268
+ cachedEmbeddingMode = null;
5269
+ cachedEmbeddingModeToken = null;
4805
5270
  lastInitWasTemporaryFailure = false;
4806
5271
  lastEmbeddingFailure = null;
4807
5272
  lastEmbeddingWarning = "";
@@ -6559,6 +7024,9 @@ async function attachCachedVectors(database, candidates) {
6559
7024
  await update(database, candidates[index].id, { ...existing, embedding: vector });
6560
7025
  } catch {
6561
7026
  }
7027
+ if (index % 50 === 0) {
7028
+ await new Promise((resolve2) => setImmediate(resolve2));
7029
+ }
6562
7030
  }
6563
7031
  }
6564
7032
  async function hydrateIndex(observations2, options = {}) {
@@ -6609,17 +7077,24 @@ async function hydrateIndex(observations2, options = {}) {
6609
7077
  await insert2(database, doc);
6610
7078
  rememberObservationDoc(doc);
6611
7079
  inserted++;
7080
+ if (inserted % 50 === 0) {
7081
+ await new Promise((resolve2) => setImmediate(resolve2));
7082
+ }
7083
+ if (inserted % 200 === 0) {
7084
+ await new Promise((resolve2) => setTimeout(resolve2, 0));
7085
+ }
6612
7086
  } catch {
6613
7087
  }
6614
7088
  }
6615
- deferredCachedVectorHydration = deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
7089
+ deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
6616
7090
  }) : null;
6617
7091
  return inserted;
6618
7092
  }
6619
- async function hydrateIndexForStartup(observations2) {
7093
+ async function hydrateIndexForStartup(observations2, options = {}) {
6620
7094
  return hydrateIndex(observations2, {
6621
7095
  allowNetworkProbe: false,
6622
- deferCachedVectors: true
7096
+ deferCachedVectors: true,
7097
+ skipCachedVectors: options.skipCachedVectors
6623
7098
  });
6624
7099
  }
6625
7100
  function getDeferredCachedVectorHydration() {
@@ -9942,7 +10417,7 @@ __export(asset_store_exports, {
9942
10417
  removeMediaAsset: () => removeMediaAsset
9943
10418
  });
9944
10419
  import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
9945
- import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename, rm, stat, writeFile as writeFile3 } from "fs/promises";
10420
+ import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
9946
10421
  import path13 from "path";
9947
10422
  function mediaRoot(dataDir) {
9948
10423
  return path13.resolve(dataDir, "media");
@@ -10032,7 +10507,7 @@ async function persistFile(source, destination) {
10032
10507
  const temporary = `${destination}.${randomUUID5()}.tmp`;
10033
10508
  try {
10034
10509
  await copyFile(source, temporary);
10035
- await rename(temporary, destination);
10510
+ await rename2(temporary, destination);
10036
10511
  return true;
10037
10512
  } catch (error) {
10038
10513
  if (error?.code === "EEXIST" && await destinationExists(destination)) return false;
@@ -10048,7 +10523,7 @@ async function persistBuffer(bytes, destination) {
10048
10523
  const temporary = `${destination}.${randomUUID5()}.tmp`;
10049
10524
  try {
10050
10525
  await writeFile3(temporary, bytes, { flag: "wx" });
10051
- await rename(temporary, destination);
10526
+ await rename2(temporary, destination);
10052
10527
  return true;
10053
10528
  } catch (error) {
10054
10529
  if (error?.code === "EEXIST" && await destinationExists(destination)) return false;
@@ -10088,11 +10563,11 @@ async function importKnownMedia(input) {
10088
10563
  async function importMediaFile(input) {
10089
10564
  const maxBytes = input.maxBytes ?? DEFAULT_MAX_MEDIA_BYTES;
10090
10565
  const source = path13.resolve(input.filePath);
10091
- const stat4 = await lstat(source);
10092
- if (stat4.isSymbolicLink()) throw new Error("Media import does not follow symbolic links");
10093
- if (!stat4.isFile()) throw new Error("Media import accepts regular files only");
10094
- if (stat4.size <= 0) throw new Error("Media import rejects empty files");
10095
- if (stat4.size > maxBytes) throw new Error(`Media file is too large (${stat4.size} bytes; limit ${maxBytes})`);
10566
+ const stat5 = await lstat(source);
10567
+ if (stat5.isSymbolicLink()) throw new Error("Media import does not follow symbolic links");
10568
+ if (!stat5.isFile()) throw new Error("Media import accepts regular files only");
10569
+ if (stat5.size <= 0) throw new Error("Media import rejects empty files");
10570
+ if (stat5.size > maxBytes) throw new Error(`Media file is too large (${stat5.size} bytes; limit ${maxBytes})`);
10096
10571
  const header = await readHeader(source);
10097
10572
  const detected = detectMedia(header);
10098
10573
  const sha256 = await sha256File(source);
@@ -10100,7 +10575,7 @@ async function importMediaFile(input) {
10100
10575
  dataDir: input.dataDir,
10101
10576
  projectId: input.projectId,
10102
10577
  sha256,
10103
- bytes: stat4.size,
10578
+ bytes: stat5.size,
10104
10579
  detected,
10105
10580
  persist: (destination) => persistFile(source, destination),
10106
10581
  sourceKind: input.sourceKind ?? "import",
@@ -10147,13 +10622,13 @@ async function removeMediaAsset(input) {
10147
10622
  const assetPath = getMediaAssetPath(input.dataDir, asset);
10148
10623
  const stagedPath = deletionStagingPath(mediaRoot(input.dataDir), asset.id);
10149
10624
  await mkdir3(path13.dirname(stagedPath), { recursive: true });
10150
- await rename(assetPath, stagedPath);
10625
+ await rename2(assetPath, stagedPath);
10151
10626
  let detachedLinks;
10152
10627
  try {
10153
10628
  ({ detachedLinks } = store.removeAssetMetadata(input.projectId, asset.id, { force: input.force }));
10154
10629
  } catch (error) {
10155
10630
  try {
10156
- await rename(stagedPath, assetPath);
10631
+ await rename2(stagedPath, assetPath);
10157
10632
  } catch (restoreError) {
10158
10633
  const original = error instanceof Error ? error.message : String(error);
10159
10634
  const restore = restoreError instanceof Error ? restoreError.message : String(restoreError);
@@ -10189,7 +10664,7 @@ async function cleanupMediaTrash(dataDir) {
10189
10664
  } catch (error) {
10190
10665
  if (error?.code === "ENOENT") continue;
10191
10666
  try {
10192
- pendingBytes += (await stat(candidate)).size;
10667
+ pendingBytes += (await stat2(candidate)).size;
10193
10668
  } catch {
10194
10669
  }
10195
10670
  pendingFiles += 1;
@@ -61271,9 +61746,9 @@ var require_node_domexception = __commonJS({
61271
61746
  });
61272
61747
 
61273
61748
  // node_modules/fetch-blob/from.js
61274
- import { statSync as statSync2, createReadStream, promises as fs10 } from "fs";
61749
+ import { statSync as statSync2, createReadStream as createReadStream2, promises as fs10 } from "fs";
61275
61750
  import { basename } from "path";
61276
- var import_node_domexception, stat2, blobFromSync, blobFrom, fileFrom, fileFromSync, fromBlob, fromFile, BlobDataItem;
61751
+ var import_node_domexception, stat3, blobFromSync, blobFrom, fileFrom, fileFromSync, fromBlob, fromFile, BlobDataItem;
61277
61752
  var init_from = __esm({
61278
61753
  "node_modules/fetch-blob/from.js"() {
61279
61754
  "use strict";
@@ -61281,23 +61756,23 @@ var init_from = __esm({
61281
61756
  import_node_domexception = __toESM(require_node_domexception(), 1);
61282
61757
  init_file();
61283
61758
  init_fetch_blob();
61284
- ({ stat: stat2 } = fs10);
61759
+ ({ stat: stat3 } = fs10);
61285
61760
  blobFromSync = (path34, type) => fromBlob(statSync2(path34), path34, type);
61286
- blobFrom = (path34, type) => stat2(path34).then((stat4) => fromBlob(stat4, path34, type));
61287
- fileFrom = (path34, type) => stat2(path34).then((stat4) => fromFile(stat4, path34, type));
61761
+ blobFrom = (path34, type) => stat3(path34).then((stat5) => fromBlob(stat5, path34, type));
61762
+ fileFrom = (path34, type) => stat3(path34).then((stat5) => fromFile(stat5, path34, type));
61288
61763
  fileFromSync = (path34, type) => fromFile(statSync2(path34), path34, type);
61289
- fromBlob = (stat4, path34, type = "") => new fetch_blob_default([new BlobDataItem({
61764
+ fromBlob = (stat5, path34, type = "") => new fetch_blob_default([new BlobDataItem({
61290
61765
  path: path34,
61291
- size: stat4.size,
61292
- lastModified: stat4.mtimeMs,
61766
+ size: stat5.size,
61767
+ lastModified: stat5.mtimeMs,
61293
61768
  start: 0
61294
61769
  })], { type });
61295
- fromFile = (stat4, path34, type = "") => new file_default([new BlobDataItem({
61770
+ fromFile = (stat5, path34, type = "") => new file_default([new BlobDataItem({
61296
61771
  path: path34,
61297
- size: stat4.size,
61298
- lastModified: stat4.mtimeMs,
61772
+ size: stat5.size,
61773
+ lastModified: stat5.mtimeMs,
61299
61774
  start: 0
61300
- })], basename(path34), { type, lastModified: stat4.mtimeMs });
61775
+ })], basename(path34), { type, lastModified: stat5.mtimeMs });
61301
61776
  BlobDataItem = class _BlobDataItem {
61302
61777
  #path;
61303
61778
  #start;
@@ -61320,11 +61795,11 @@ var init_from = __esm({
61320
61795
  });
61321
61796
  }
61322
61797
  async *stream() {
61323
- const { mtimeMs } = await stat2(this.#path);
61798
+ const { mtimeMs } = await stat3(this.#path);
61324
61799
  if (mtimeMs > this.lastModified) {
61325
61800
  throw new import_node_domexception.default("The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.", "NotReadableError");
61326
61801
  }
61327
- yield* createReadStream(this.#path, {
61802
+ yield* createReadStream2(this.#path, {
61328
61803
  start: this.#start,
61329
61804
  end: this.#start + this.size - 1
61330
61805
  });
@@ -76954,7 +77429,7 @@ var init_wrapper = __esm({
76954
77429
  });
76955
77430
 
76956
77431
  // node_modules/@google/genai/dist/node/index.mjs
76957
- import { createWriteStream } from "fs";
77432
+ import { createWriteStream as createWriteStream2 } from "fs";
76958
77433
  import * as fs11 from "fs/promises";
76959
77434
  import { writeFile as writeFile4 } from "fs/promises";
76960
77435
  import { Readable } from "stream";
@@ -94234,7 +94709,7 @@ ${underline}`);
94234
94709
  if (params.downloadPath) {
94235
94710
  const response = await downloadFile(params, apiClient);
94236
94711
  if (response instanceof HttpResponse) {
94237
- const writer = createWriteStream(params.downloadPath);
94712
+ const writer = createWriteStream2(params.downloadPath);
94238
94713
  const body = Readable.fromWeb(response.responseInternal.body);
94239
94714
  body.pipe(writer);
94240
94715
  await finished(writer);
@@ -144268,7 +144743,7 @@ function explainRetention(doc, referenceTime) {
144268
144743
  };
144269
144744
  }
144270
144745
  function toRetentionDocument(obs, accessMap) {
144271
- const access2 = accessMap?.get(obs.id);
144746
+ const access3 = accessMap?.get(obs.id);
144272
144747
  return {
144273
144748
  id: `obs-${obs.id}`,
144274
144749
  observationId: obs.id,
@@ -144282,8 +144757,8 @@ function toRetentionDocument(obs, accessMap) {
144282
144757
  tokens: obs.tokens,
144283
144758
  createdAt: obs.createdAt,
144284
144759
  projectId: obs.projectId,
144285
- accessCount: access2?.accessCount ?? 0,
144286
- lastAccessedAt: access2?.lastAccessedAt ?? "",
144760
+ accessCount: access3?.accessCount ?? 0,
144761
+ lastAccessedAt: access3?.lastAccessedAt ?? "",
144287
144762
  status: obs.status ?? "active",
144288
144763
  source: obs.source ?? "agent",
144289
144764
  sourceDetail: obs.sourceDetail ?? "",
@@ -146397,7 +146872,7 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
146397
146872
  const rel = normalizeCodePath(relative(projectRoot, abs));
146398
146873
  try {
146399
146874
  const text2 = readFileSync6(abs, "utf-8");
146400
- const stat4 = fileStat ?? statSync3(abs);
146875
+ const stat5 = fileStat ?? statSync3(abs);
146401
146876
  const file = {
146402
146877
  id: makeCodeFileId(projectId, rel),
146403
146878
  projectId,
@@ -146406,8 +146881,8 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
146406
146881
  contentHash: hashText(text2),
146407
146882
  // Preserve filesystem precision. Rounding here made same-size edits in
146408
146883
  // a short Windows timestamp window indistinguishable to refresh.
146409
- mtimeMs: Number(stat4.mtimeMs),
146410
- sizeBytes: Number(stat4.size),
146884
+ mtimeMs: Number(stat5.mtimeMs),
146885
+ sizeBytes: Number(stat5.size),
146411
146886
  indexedAt
146412
146887
  };
146413
146888
  return {
@@ -146434,18 +146909,18 @@ async function indexProjectLite(options) {
146434
146909
  let skippedOversizedFiles = 0;
146435
146910
  let unreadableFiles = 0;
146436
146911
  for (const abs of paths) {
146437
- let stat4;
146912
+ let stat5;
146438
146913
  try {
146439
- stat4 = statSync3(abs);
146914
+ stat5 = statSync3(abs);
146440
146915
  } catch {
146441
146916
  unreadableFiles++;
146442
146917
  continue;
146443
146918
  }
146444
- if (Number(stat4.size) > maxFileBytes) {
146919
+ if (Number(stat5.size) > maxFileBytes) {
146445
146920
  skippedOversizedFiles++;
146446
146921
  continue;
146447
146922
  }
146448
- const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat4);
146923
+ const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
146449
146924
  if (!attempt.delta) {
146450
146925
  if (attempt.unreadable) unreadableFiles++;
146451
146926
  continue;
@@ -146473,16 +146948,16 @@ async function refreshProjectLite(store, options) {
146473
146948
  for (const abs of paths) {
146474
146949
  const rel = normalizeCodePath(relative(options.projectRoot, abs));
146475
146950
  seenPaths.add(rel);
146476
- let stat4;
146951
+ let stat5;
146477
146952
  try {
146478
- stat4 = statSync3(abs);
146953
+ stat5 = statSync3(abs);
146479
146954
  } catch {
146480
146955
  unreadableFiles++;
146481
146956
  continue;
146482
146957
  }
146483
146958
  const existing = existingByPath.get(rel);
146484
- const mtimeMs = Number(stat4.mtimeMs);
146485
- const sizeBytes = Number(stat4.size);
146959
+ const mtimeMs = Number(stat5.mtimeMs);
146960
+ const sizeBytes = Number(stat5.size);
146486
146961
  if (sizeBytes > maxFileBytes) {
146487
146962
  skippedOversizedFiles++;
146488
146963
  if (existing) oversizedExistingFileIds.add(existing.id);
@@ -146492,7 +146967,7 @@ async function refreshProjectLite(store, options) {
146492
146967
  unchangedFiles++;
146493
146968
  continue;
146494
146969
  }
146495
- const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat4);
146970
+ const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
146496
146971
  if (!attempt.delta) {
146497
146972
  if (attempt.unreadable) unreadableFiles++;
146498
146973
  continue;
@@ -150240,12 +150715,25 @@ __export(observations_exports, {
150240
150715
  prepareSearchIndex: () => prepareSearchIndex,
150241
150716
  probeSearchIndex: () => probeSearchIndex,
150242
150717
  reindexObservations: () => reindexObservations,
150718
+ resetObservationRuntime: () => resetObservationRuntime,
150243
150719
  resolveObservations: () => resolveObservations,
150244
150720
  storeObservation: () => storeObservation,
150245
150721
  suggestTopicKey: () => suggestTopicKey,
150246
150722
  updateObservationAdmission: () => updateObservationAdmission,
150247
150723
  withFreshObservations: () => withFreshObservations
150248
150724
  });
150725
+ function resetObservationRuntime() {
150726
+ observations = [];
150727
+ nextId = 1;
150728
+ projectDir = null;
150729
+ searchIndexPrepared = false;
150730
+ corpusLoaded = false;
150731
+ loadedObservationStore = null;
150732
+ embeddingWriteMode = "background";
150733
+ embeddingWorkerProjectRoot = void 0;
150734
+ vectorMissingIds.clear();
150735
+ vectorSchemaUpgradePromise = null;
150736
+ }
150249
150737
  function logEmbeddingFailureOnce(key, message) {
150250
150738
  const now3 = Date.now();
150251
150739
  const last = embeddingFailureLogTimestamps.get(key) ?? 0;
@@ -150354,23 +150842,33 @@ async function upgradeVectorSchemaAfterFirstEmbedding(embedding) {
150354
150842
  async function initObservations(dir, options = {}) {
150355
150843
  embeddingWriteMode = options.embeddingWriteMode ?? "background";
150356
150844
  embeddingWorkerProjectRoot = options.projectRoot;
150357
- if (projectDir === dir) return;
150358
150845
  await initObservationStore(dir);
150359
150846
  const store = getObservationStore();
150360
- observations = await store.loadAll();
150847
+ if (projectDir === dir && loadedObservationStore === store) {
150848
+ if (options.skipCorpusLoad || corpusLoaded) return;
150849
+ }
150361
150850
  nextId = await store.loadIdCounter();
150362
150851
  projectDir = dir;
150363
150852
  searchIndexPrepared = false;
150364
150853
  vectorSchemaUpgradePromise = null;
150854
+ if (options.skipCorpusLoad) {
150855
+ observations = [];
150856
+ corpusLoaded = false;
150857
+ loadedObservationStore = store;
150858
+ return;
150859
+ }
150860
+ observations = await store.loadAll();
150861
+ corpusLoaded = true;
150862
+ loadedObservationStore = store;
150365
150863
  }
150366
150864
  function scheduleObservationEmbedding(input) {
150367
150865
  const { observationId: observationId2, projectId, searchableText, doc } = input;
150368
150866
  vectorMissingIds.add(observationId2);
150867
+ if (isEmbeddingExplicitlyDisabled()) {
150868
+ vectorMissingIds.delete(observationId2);
150869
+ return;
150870
+ }
150369
150871
  if (embeddingWriteMode === "deferred") {
150370
- if (isEmbeddingExplicitlyDisabled()) {
150371
- vectorMissingIds.delete(observationId2);
150372
- return;
150373
- }
150374
150872
  queueVectorBackfill(projectId, {
150375
150873
  detachedWorker: true,
150376
150874
  projectRoot: embeddingWorkerProjectRoot
@@ -150417,7 +150915,7 @@ function scheduleObservationEmbedding(input) {
150417
150915
  });
150418
150916
  }
150419
150917
  async function ensureFreshObservations() {
150420
- if (!projectDir) return false;
150918
+ if (!projectDir || !corpusLoaded) return false;
150421
150919
  try {
150422
150920
  const store = getObservationStore();
150423
150921
  const wasStale = await store.ensureFresh();
@@ -150458,7 +150956,7 @@ async function storeObservation(input) {
150458
150956
  });
150459
150957
  if (safeAttachments) input = { ...input, attachments: safeAttachments };
150460
150958
  await ensureFreshObservations();
150461
- if (input.topicKey) {
150959
+ if (input.topicKey && corpusLoaded) {
150462
150960
  const existing = observations.find(
150463
150961
  (o) => o.topicKey === input.topicKey && o.projectId === input.projectId
150464
150962
  );
@@ -150555,13 +151053,17 @@ async function storeObservation(input) {
150555
151053
  });
150556
151054
  observation.writeGeneration = store.getGeneration();
150557
151055
  if (upsertedInsideLock || reloadCacheAfterCommit) {
150558
- observations = await store.loadAll();
150559
- nextId = await store.loadIdCounter();
150560
- if (upsertedInsideLock) {
150561
- observation = observations.find((candidate) => candidate.id === observation.id) ?? observation;
151056
+ if (corpusLoaded) {
151057
+ observations = await store.loadAll();
151058
+ nextId = await store.loadIdCounter();
151059
+ if (upsertedInsideLock) {
151060
+ observation = observations.find((candidate) => candidate.id === observation.id) ?? observation;
151061
+ }
151062
+ } else {
151063
+ nextId = await store.loadIdCounter();
150562
151064
  }
150563
151065
  } else {
150564
- observations.push(observation);
151066
+ if (corpusLoaded) observations.push(observation);
150565
151067
  nextId = observation.id + 1;
150566
151068
  }
150567
151069
  if (upsertedInsideLock) return;
@@ -150629,7 +151131,9 @@ async function storeObservation(input) {
150629
151131
  createdByAgentId: input.createdByAgentId ?? "",
150630
151132
  sharedWithAgentIds: JSON.stringify(input.sharedWithAgentIds ?? [])
150631
151133
  };
150632
- await insertObservation(doc);
151134
+ if (corpusLoaded) {
151135
+ await insertObservation(doc);
151136
+ }
150633
151137
  };
150634
151138
  await assignAndPersist();
150635
151139
  if (upsertedInsideLock) {
@@ -150991,9 +151495,11 @@ async function reindexObservations() {
150991
151495
  searchIndexPrepared = true;
150992
151496
  return count2;
150993
151497
  }
150994
- async function prepareSearchIndex() {
151498
+ async function prepareSearchIndex(options = {}) {
150995
151499
  if (searchIndexPrepared) return 0;
150996
- const count2 = await hydrateIndexForStartup(observations);
151500
+ const count2 = await hydrateIndexForStartup(observations, {
151501
+ skipCachedVectors: options.skipCachedVectors
151502
+ });
150997
151503
  if (count2 === 0) {
150998
151504
  searchIndexPrepared = true;
150999
151505
  return 0;
@@ -151185,7 +151691,7 @@ async function backfillVectorEmbeddings(options = {}) {
151185
151691
  ...lastFailure ? { lastError: lastFailure } : {}
151186
151692
  };
151187
151693
  }
151188
- var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
151694
+ var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
151189
151695
  var init_observations = __esm({
151190
151696
  "src/memory/observations.ts"() {
151191
151697
  "use strict";
@@ -151204,6 +151710,8 @@ var init_observations = __esm({
151204
151710
  projectDir = null;
151205
151711
  searchIndexPrepared = false;
151206
151712
  embeddingWriteMode = "background";
151713
+ corpusLoaded = false;
151714
+ loadedObservationStore = null;
151207
151715
  vectorMissingIds = /* @__PURE__ */ new Set();
151208
151716
  vectorBackfillRunning = false;
151209
151717
  vectorSchemaUpgradePromise = null;
@@ -155399,9 +155907,9 @@ function existingLensCandidates(rootPath, lens) {
155399
155907
  const absolute = path28.join(rootPath, candidate);
155400
155908
  try {
155401
155909
  if (!existsSync16(absolute)) continue;
155402
- const stat4 = statSync4(absolute);
155403
- if (stat4.isFile()) out.push(candidate);
155404
- if (stat4.isDirectory()) out.push(candidate.replace(/\\/g, "/"));
155910
+ const stat5 = statSync4(absolute);
155911
+ if (stat5.isFile()) out.push(candidate);
155912
+ if (stat5.isDirectory()) out.push(candidate.replace(/\\/g, "/"));
155405
155913
  } catch {
155406
155914
  }
155407
155915
  }
@@ -161221,13 +161729,13 @@ async function installHooks(agent, projectRoot, global2 = false) {
161221
161729
  const agentPrev = prevFiles.filter((e2) => e2.agent === agent);
161222
161730
  for (const entry of agentPrev) {
161223
161731
  try {
161224
- const { access: access2, unlink: unlink2 } = await import("fs/promises");
161225
- await access2(entry.path);
161732
+ const { access: access3, unlink: unlink3 } = await import("fs/promises");
161733
+ await access3(entry.path);
161226
161734
  const basename5 = path32.basename(entry.path);
161227
161735
  if (basename5 === "AGENTS.md" || basename5 === "GEMINI.md" || basename5 === "CONTEXT.md") {
161228
161736
  continue;
161229
161737
  }
161230
- await unlink2(entry.path);
161738
+ await unlink3(entry.path);
161231
161739
  await removeFile2(projectRoot, entry.path);
161232
161740
  } catch {
161233
161741
  }
@@ -161747,11 +162255,11 @@ import path33 from "path";
161747
162255
  function resolveGitDir(projectRoot) {
161748
162256
  const dotGit = path33.join(projectRoot, ".git");
161749
162257
  if (!existsSync19(dotGit)) return null;
161750
- const stat4 = statSync5(dotGit);
161751
- if (stat4.isDirectory()) {
162258
+ const stat5 = statSync5(dotGit);
162259
+ if (stat5.isDirectory()) {
161752
162260
  return dotGit;
161753
162261
  }
161754
- if (stat4.isFile()) {
162262
+ if (stat5.isFile()) {
161755
162263
  try {
161756
162264
  const content = readFileSync13(dotGit, "utf-8").trim();
161757
162265
  const match2 = content.match(/^gitdir:\s*(.+)$/);
@@ -163895,8 +164403,8 @@ var RulesSyncer = class {
163895
164403
  const dir = path25.dirname(pattern);
163896
164404
  const fileGlob = path25.basename(pattern);
163897
164405
  try {
163898
- const stat4 = await fs17.stat(dir);
163899
- if (!stat4.isDirectory()) {
164406
+ const stat5 = await fs17.stat(dir);
164407
+ if (!stat5.isDirectory()) {
163900
164408
  try {
163901
164409
  await fs17.access(pattern);
163902
164410
  return [pattern];
@@ -165951,7 +166459,7 @@ The path should point to a directory containing a .git folder.`
165951
166459
  };
165952
166460
  const server = existingServer ?? new McpServer({
165953
166461
  name: "memorix",
165954
- version: true ? "1.6.1" : "1.0.1"
166462
+ version: true ? "1.6.2" : "1.0.1"
165955
166463
  });
165956
166464
  const originalRegisterTool = server.registerTool.bind(server);
165957
166465
  server.registerTool = ((name, ...args) => {
@@ -170379,6 +170887,11 @@ var MemoryClient = class {
170379
170887
  await this._oramaStore.resetDb();
170380
170888
  } catch {
170381
170889
  }
170890
+ try {
170891
+ const { closeDatabase: closeDatabase3 } = await Promise.resolve().then(() => (init_sqlite_db(), sqlite_db_exports));
170892
+ closeDatabase3(this._dataDir);
170893
+ } catch {
170894
+ }
170382
170895
  }
170383
170896
  };
170384
170897
  async function createMemoryClient(options) {
@@ -170390,11 +170903,8 @@ async function createMemoryClient(options) {
170390
170903
  `[memorix-sdk] No Git repository found at "${projectRoot}". Memorix requires a Git-tracked project for identity resolution.`
170391
170904
  );
170392
170905
  }
170393
- const path34 = await import("path");
170394
- const os4 = await import("os");
170395
- const dataDir = path34.join(os4.homedir(), ".memorix", "data", project.id.replace(/\//g, path34.sep));
170396
- const fs22 = await import("fs");
170397
- fs22.mkdirSync(dataDir, { recursive: true });
170906
+ const { getProjectDataDir: getProjectDataDir2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
170907
+ const dataDir = await getProjectDataDir2(project.id);
170398
170908
  const client = new MemoryClient(project.id, projectRoot, dataDir);
170399
170909
  await client._init(silent);
170400
170910
  return client;