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
@@ -2352,8 +2352,8 @@ async function acquireLock(lockPath) {
2352
2352
  const code = err instanceof Error && "code" in err ? err.code : void 0;
2353
2353
  if (code === "EEXIST" || code === "EPERM") {
2354
2354
  try {
2355
- const stat4 = await fs2.stat(lockPath);
2356
- if (Date.now() - stat4.mtimeMs > LOCK_STALE_MS) {
2355
+ const stat5 = await fs2.stat(lockPath);
2356
+ if (Date.now() - stat5.mtimeMs > LOCK_STALE_MS) {
2357
2357
  await fs2.unlink(lockPath).catch(() => {
2358
2358
  });
2359
2359
  continue;
@@ -3464,24 +3464,131 @@ var init_transformers_provider = __esm({
3464
3464
  // src/embedding/api-provider.ts
3465
3465
  var api_provider_exports = {};
3466
3466
  __export(api_provider_exports, {
3467
- APIEmbeddingProvider: () => APIEmbeddingProvider
3467
+ APIEmbeddingProvider: () => APIEmbeddingProvider,
3468
+ convertArrayCacheToJsonl: () => convertArrayCacheToJsonl,
3469
+ ensureDiskCacheLoaded: () => ensureDiskCacheLoaded,
3470
+ getApiEmbeddingCachePayloadBytesForTests: () => getApiEmbeddingCachePayloadBytesForTests,
3471
+ getApiEmbeddingDiskCacheLoadCountForTests: () => getApiEmbeddingDiskCacheLoadCountForTests,
3472
+ getApiEmbeddingJsonlLineCountCallsForTests: () => getApiEmbeddingJsonlLineCountCallsForTests,
3473
+ getApiEmbeddingMaxEntriesForDimensionsForTests: () => getApiEmbeddingMaxEntriesForDimensionsForTests,
3474
+ migrateJsonArrayCacheToJsonl: () => migrateJsonArrayCacheToJsonl,
3475
+ parseCacheJsonInWorker: () => parseCacheJsonInWorker,
3476
+ resetApiEmbeddingCacheForTests: () => resetApiEmbeddingCacheForTests,
3477
+ saveDiskCacheNow: () => saveDiskCacheNow,
3478
+ seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
3468
3479
  });
3469
3480
  import { createHash as createHash2 } from "crypto";
3470
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
3481
+ import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
3482
+ import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
3471
3483
  import { join as join3 } from "path";
3472
3484
  import { homedir as homedir7 } from "os";
3485
+ import { createInterface } from "readline";
3473
3486
  function cacheDir() {
3474
3487
  return process.env.MEMORIX_DATA_DIR || join3(homedir7(), ".memorix", "data");
3475
3488
  }
3476
3489
  function cacheFile() {
3477
3490
  return join3(cacheDir(), ".embedding-api-cache.json");
3478
3491
  }
3492
+ function cacheJsonlFile() {
3493
+ return join3(cacheDir(), ".embedding-api-cache.jsonl");
3494
+ }
3479
3495
  function dimsCacheFile() {
3480
3496
  return join3(cacheDir(), ".embedding-dims-cache.json");
3481
3497
  }
3482
3498
  function cacheMetaFile() {
3483
3499
  return join3(cacheDir(), ".embedding-api-cache-meta.json");
3484
3500
  }
3501
+ function cacheStatFile() {
3502
+ return join3(cacheDir(), ".embedding-api-cache-stat.json");
3503
+ }
3504
+ function payloadBytes(vector) {
3505
+ return vector.length * BYTES_PER_DIM;
3506
+ }
3507
+ function maxCachePayloadBytes() {
3508
+ const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
3509
+ if (raw) {
3510
+ const parsed = Number.parseInt(raw, 10);
3511
+ if (Number.isFinite(parsed) && parsed > 0) return parsed;
3512
+ }
3513
+ return MAX_CACHE_PAYLOAD_BYTES;
3514
+ }
3515
+ function maxEntriesForDimensions(dimensions) {
3516
+ const dim = Math.max(1, Math.floor(dimensions));
3517
+ const byBytes = Math.floor(maxCachePayloadBytes() / (dim * BYTES_PER_DIM));
3518
+ return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
3519
+ }
3520
+ function rememberDimensions(vector) {
3521
+ if (vector.length > 0 && (cacheDimensions === null || vector.length > cacheDimensions)) {
3522
+ cacheDimensions = vector.length;
3523
+ }
3524
+ }
3525
+ function evictOldest() {
3526
+ const firstKey = cache3.keys().next().value;
3527
+ if (firstKey === void 0) return;
3528
+ const old = cache3.get(firstKey);
3529
+ cache3.delete(firstKey);
3530
+ if (old) cachePayloadBytes -= payloadBytes(old);
3531
+ }
3532
+ function evictToFit(incomingBytes, incomingDims) {
3533
+ const limit2 = maxEntriesForDimensions(incomingDims);
3534
+ while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes + incomingBytes > maxCachePayloadBytes())) {
3535
+ evictOldest();
3536
+ }
3537
+ }
3538
+ function isNumberArray(value) {
3539
+ return Array.isArray(value) && value.every((entry) => typeof entry === "number");
3540
+ }
3541
+ function parseCacheRow(raw) {
3542
+ if (Array.isArray(raw)) {
3543
+ const hash4 = raw[0];
3544
+ const vector = raw[1];
3545
+ if (typeof hash4 === "string" && isNumberArray(vector)) {
3546
+ return { hash: hash4, vector };
3547
+ }
3548
+ return null;
3549
+ }
3550
+ if (raw && typeof raw === "object") {
3551
+ const row = raw;
3552
+ if (typeof row.h === "string" && isNumberArray(row.v)) {
3553
+ return { hash: row.h, vector: row.v };
3554
+ }
3555
+ }
3556
+ return null;
3557
+ }
3558
+ function cacheInsert(hash4, value, dirty) {
3559
+ rememberDimensions(value);
3560
+ const incoming = payloadBytes(value);
3561
+ const existing = cache3.get(hash4);
3562
+ if (existing) {
3563
+ cachePayloadBytes -= payloadBytes(existing);
3564
+ cache3.delete(hash4);
3565
+ }
3566
+ evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
3567
+ cache3.set(hash4, value);
3568
+ cachePayloadBytes += incoming;
3569
+ if (dirty) diskCacheDirty2 = true;
3570
+ }
3571
+ async function readCacheStat() {
3572
+ try {
3573
+ const data = JSON.parse(await readFile2(cacheStatFile(), "utf-8"));
3574
+ if (typeof data.entryCount === "number" && Number.isFinite(data.entryCount) && data.entryCount >= 0) {
3575
+ return {
3576
+ version: 1,
3577
+ entryCount: Math.floor(data.entryCount),
3578
+ dimensions: isValidDimension(data.dimensions) ? data.dimensions : null
3579
+ };
3580
+ }
3581
+ } catch {
3582
+ }
3583
+ return null;
3584
+ }
3585
+ async function writeCacheStat(stat5) {
3586
+ try {
3587
+ await mkdir2(cacheDir(), { recursive: true });
3588
+ await writeFile2(cacheStatFile(), JSON.stringify(stat5));
3589
+ } catch {
3590
+ }
3591
+ }
3485
3592
  function normalizeText(text2) {
3486
3593
  return text2.replace(/\s+/g, " ").trim().slice(0, MAX_INPUT_CHARS);
3487
3594
  }
@@ -3567,29 +3674,300 @@ function toProviderInput(input, baseUrl) {
3567
3674
  }
3568
3675
  return { type: input.modality, url: input.url };
3569
3676
  }
3677
+ function parseCacheJsonInWorker(filePath) {
3678
+ return new Promise((resolve, reject) => {
3679
+ void import("worker_threads").then(({ Worker }) => {
3680
+ const worker = new Worker(
3681
+ `
3682
+ const { parentPort, workerData } = require('node:worker_threads');
3683
+ const { readFileSync } = require('node:fs');
3684
+ try {
3685
+ const raw = readFileSync(workerData.filePath, 'utf8');
3686
+ parentPort.postMessage({ ok: true, entries: JSON.parse(raw) });
3687
+ } catch (err) {
3688
+ parentPort.postMessage({
3689
+ ok: false,
3690
+ error: err instanceof Error ? err.message : String(err),
3691
+ });
3692
+ }
3693
+ `,
3694
+ { eval: true, workerData: { filePath } }
3695
+ );
3696
+ worker.once("message", (msg) => {
3697
+ void worker.terminate();
3698
+ if (msg?.ok && Array.isArray(msg.entries)) {
3699
+ resolve(msg.entries);
3700
+ return;
3701
+ }
3702
+ reject(new Error(msg?.error || "embedding cache worker parse failed"));
3703
+ });
3704
+ worker.once("error", reject);
3705
+ }).catch(reject);
3706
+ });
3707
+ }
3708
+ function findJsonValueEnd(source) {
3709
+ let i2 = 0;
3710
+ while (i2 < source.length && /\s/.test(source[i2])) i2 += 1;
3711
+ if (i2 >= source.length) return -1;
3712
+ const start = source[i2];
3713
+ if (start === '"') {
3714
+ i2 += 1;
3715
+ while (i2 < source.length) {
3716
+ if (source[i2] === "\\") {
3717
+ i2 += 2;
3718
+ continue;
3719
+ }
3720
+ if (source[i2] === '"') return i2 + 1;
3721
+ i2 += 1;
3722
+ }
3723
+ return -1;
3724
+ }
3725
+ if (start === "{" || start === "[") {
3726
+ const close = start === "{" ? "}" : "]";
3727
+ let depth = 0;
3728
+ let inString = false;
3729
+ for (; i2 < source.length; i2 += 1) {
3730
+ const ch = source[i2];
3731
+ if (inString) {
3732
+ if (ch === "\\") {
3733
+ i2 += 1;
3734
+ continue;
3735
+ }
3736
+ if (ch === '"') inString = false;
3737
+ continue;
3738
+ }
3739
+ if (ch === '"') {
3740
+ inString = true;
3741
+ continue;
3742
+ }
3743
+ if (ch === start) depth += 1;
3744
+ else if (ch === close) {
3745
+ depth -= 1;
3746
+ if (depth === 0) return i2 + 1;
3747
+ }
3748
+ }
3749
+ return -1;
3750
+ }
3751
+ const rest = source.slice(i2);
3752
+ const match2 = rest.match(/^(?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)/);
3753
+ return match2 ? i2 + match2[0].length : -1;
3754
+ }
3755
+ async function streamJsonArrayToJsonl(filePath, tmpPath) {
3756
+ const input = createReadStream(filePath, { encoding: "utf8" });
3757
+ const out = createWriteStream(tmpPath);
3758
+ let buf = "";
3759
+ let started = false;
3760
+ let count2 = 0;
3761
+ const writeLine = async (line) => {
3762
+ if (!out.write(line)) {
3763
+ await new Promise((resolve, reject) => {
3764
+ out.once("drain", resolve);
3765
+ out.once("error", reject);
3766
+ });
3767
+ }
3768
+ };
3769
+ try {
3770
+ for await (const chunk of input) {
3771
+ buf += chunk;
3772
+ if (!started) {
3773
+ const idx = buf.search(/\S/);
3774
+ if (idx < 0) continue;
3775
+ if (buf[idx] !== "[") throw new Error("embedding cache is not a JSON array");
3776
+ buf = buf.slice(idx + 1);
3777
+ started = true;
3778
+ }
3779
+ while (true) {
3780
+ buf = buf.replace(/^\s*,?\s*/, "");
3781
+ if (!buf) break;
3782
+ if (buf[0] === "]") {
3783
+ buf = "";
3784
+ break;
3785
+ }
3786
+ const end = findJsonValueEnd(buf);
3787
+ if (end < 0) break;
3788
+ const value = JSON.parse(buf.slice(0, end));
3789
+ buf = buf.slice(end);
3790
+ const row = parseCacheRow(value);
3791
+ if (row) {
3792
+ await writeLine(`${JSON.stringify({ h: row.hash, v: row.vector })}
3793
+ `);
3794
+ count2 += 1;
3795
+ if (count2 % 50 === 0) {
3796
+ await new Promise((resolve) => setImmediate(resolve));
3797
+ }
3798
+ }
3799
+ }
3800
+ if (buf.length > MAX_MIGRATION_VALUE_BYTES) {
3801
+ throw new Error("embedding cache entry too large to migrate safely");
3802
+ }
3803
+ }
3804
+ } finally {
3805
+ await new Promise((resolve, reject) => {
3806
+ out.end(() => resolve());
3807
+ out.once("error", reject);
3808
+ });
3809
+ }
3810
+ return count2;
3811
+ }
3812
+ async function migrateJsonArrayCacheToJsonl(filePath, jsonlPath) {
3813
+ const tmpPath = `${jsonlPath}.tmp`;
3814
+ try {
3815
+ const count2 = await streamJsonArrayToJsonl(filePath, tmpPath);
3816
+ await rename(tmpPath, jsonlPath);
3817
+ diskCacheEntryCount = count2;
3818
+ await writeCacheStat({
3819
+ version: 1,
3820
+ entryCount: count2,
3821
+ dimensions: cacheDimensions
3822
+ });
3823
+ return count2;
3824
+ } catch (err) {
3825
+ await unlink(tmpPath).catch(() => void 0);
3826
+ throw err;
3827
+ }
3828
+ }
3829
+ function convertArrayCacheToJsonl(filePath, jsonlPath) {
3830
+ return migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
3831
+ }
3832
+ async function loadCacheJsonl(filePath) {
3833
+ const rl = createInterface({
3834
+ input: createReadStream(filePath, { encoding: "utf8" }),
3835
+ crlfDelay: Infinity
3836
+ });
3837
+ let loaded = 0;
3838
+ let onDiskCount = 0;
3839
+ for await (const line of rl) {
3840
+ const trimmed = line.trim();
3841
+ if (!trimmed) continue;
3842
+ try {
3843
+ const row = parseCacheRow(JSON.parse(trimmed));
3844
+ if (!row) continue;
3845
+ onDiskCount += 1;
3846
+ cacheInsert(row.hash, row.vector, false);
3847
+ loaded = cache3.size;
3848
+ } catch {
3849
+ }
3850
+ if (onDiskCount % 250 === 0) {
3851
+ await new Promise((resolve) => setImmediate(resolve));
3852
+ }
3853
+ }
3854
+ diskCacheEntryCount = onDiskCount;
3855
+ await writeCacheStat({
3856
+ version: 1,
3857
+ entryCount: onDiskCount,
3858
+ dimensions: cacheDimensions
3859
+ });
3860
+ return loaded;
3861
+ }
3862
+ async function countJsonlLines(filePath) {
3863
+ jsonlLineCountCalls += 1;
3864
+ try {
3865
+ const rl = createInterface({
3866
+ input: createReadStream(filePath, { encoding: "utf8" }),
3867
+ crlfDelay: Infinity
3868
+ });
3869
+ let count2 = 0;
3870
+ for await (const line of rl) {
3871
+ if (line.trim()) count2 += 1;
3872
+ }
3873
+ return count2;
3874
+ } catch {
3875
+ return 0;
3876
+ }
3877
+ }
3878
+ async function resolveOnDiskCount(jsonlPath) {
3879
+ if (diskCacheEntryCount > 0) return diskCacheEntryCount;
3880
+ const sidecar = await readCacheStat();
3881
+ if (sidecar) {
3882
+ diskCacheEntryCount = sidecar.entryCount;
3883
+ if (sidecar.dimensions && cacheDimensions === null) cacheDimensions = sidecar.dimensions;
3884
+ return sidecar.entryCount;
3885
+ }
3886
+ try {
3887
+ await access(jsonlPath, fsConstants.R_OK);
3888
+ } catch {
3889
+ return 0;
3890
+ }
3891
+ const counted = await countJsonlLines(jsonlPath);
3892
+ diskCacheEntryCount = counted;
3893
+ await writeCacheStat({
3894
+ version: 1,
3895
+ entryCount: counted,
3896
+ dimensions: cacheDimensions
3897
+ });
3898
+ return counted;
3899
+ }
3570
3900
  async function loadDiskCache2() {
3571
- if (diskCacheLoaded) return;
3901
+ if (diskCacheLoaded) return cache3.size;
3902
+ let loaded = 0;
3572
3903
  try {
3573
- const raw = await readFile2(cacheFile(), "utf-8");
3574
- const entries = JSON.parse(raw);
3575
- for (const [k, v] of entries) cache3.set(k, v);
3576
- console.error(`[memorix] Loaded ${entries.length} cached API embeddings from disk`);
3904
+ const jsonlPath = cacheJsonlFile();
3905
+ let hasJsonl = false;
3906
+ try {
3907
+ await access(jsonlPath, fsConstants.R_OK);
3908
+ hasJsonl = true;
3909
+ } catch {
3910
+ hasJsonl = false;
3911
+ }
3912
+ if (hasJsonl) {
3913
+ loaded = await loadCacheJsonl(jsonlPath);
3914
+ } else {
3915
+ const filePath = cacheFile();
3916
+ await stat(filePath);
3917
+ await migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
3918
+ loaded = await loadCacheJsonl(jsonlPath);
3919
+ }
3920
+ diskCacheLoadCount += 1;
3921
+ console.error(`[memorix] Loaded ${loaded} cached API embeddings from disk`);
3577
3922
  } catch {
3578
3923
  }
3579
3924
  diskCacheLoaded = true;
3925
+ return loaded;
3580
3926
  }
3581
3927
  function startDiskCacheLoad() {
3582
3928
  if (diskCacheLoaded || diskCacheLoadPromise) return;
3583
- diskCacheLoadPromise = loadDiskCache2().catch(() => {
3929
+ diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
3584
3930
  });
3585
3931
  }
3586
3932
  async function ensureDiskCacheLoaded() {
3587
- if (diskCacheLoaded) return;
3588
- if (diskCacheLoadPromise) {
3589
- await diskCacheLoadPromise;
3590
- return;
3591
- }
3592
- await loadDiskCache2();
3933
+ if (diskCacheLoaded) return cache3.size;
3934
+ if (!diskCacheLoadPromise) {
3935
+ diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
3936
+ });
3937
+ }
3938
+ await diskCacheLoadPromise;
3939
+ return cache3.size;
3940
+ }
3941
+ function getApiEmbeddingDiskCacheLoadCountForTests() {
3942
+ return diskCacheLoadCount;
3943
+ }
3944
+ function resetApiEmbeddingCacheForTests() {
3945
+ cache3.clear();
3946
+ cachePayloadBytes = 0;
3947
+ cacheDimensions = null;
3948
+ diskCacheEntryCount = 0;
3949
+ diskCacheLoaded = false;
3950
+ diskCacheLoadPromise = null;
3951
+ diskCacheLoadCount = 0;
3952
+ diskCacheDirty2 = false;
3953
+ jsonlLineCountCalls = 0;
3954
+ }
3955
+ function seedApiEmbeddingCacheForTests(entries) {
3956
+ cache3.clear();
3957
+ cachePayloadBytes = 0;
3958
+ cacheDimensions = null;
3959
+ for (const [key, value] of entries) cacheInsert(key, value, true);
3960
+ diskCacheLoaded = true;
3961
+ diskCacheDirty2 = true;
3962
+ }
3963
+ function getApiEmbeddingCachePayloadBytesForTests() {
3964
+ return cachePayloadBytes;
3965
+ }
3966
+ function getApiEmbeddingJsonlLineCountCallsForTests() {
3967
+ return jsonlLineCountCalls;
3968
+ }
3969
+ function getApiEmbeddingMaxEntriesForDimensionsForTests(dimensions) {
3970
+ return maxEntriesForDimensions(dimensions);
3593
3971
  }
3594
3972
  function dimsCacheKey(config) {
3595
3973
  return [
@@ -3705,8 +4083,46 @@ async function saveDiskCacheNow() {
3705
4083
  if (!diskCacheDirty2) return;
3706
4084
  try {
3707
4085
  await mkdir2(cacheDir(), { recursive: true });
3708
- const entries = Array.from(cache3.entries());
3709
- await writeFile2(cacheFile(), JSON.stringify(entries));
4086
+ const jsonlPath = cacheJsonlFile();
4087
+ const existing = await resolveOnDiskCount(jsonlPath);
4088
+ if (existing > 0 && cache3.size < Math.ceil(existing * 0.5)) {
4089
+ console.error(
4090
+ `[memorix] Refusing to overwrite embedding cache (${existing} on disk vs ${cache3.size} in memory)`
4091
+ );
4092
+ diskCacheDirty2 = false;
4093
+ return;
4094
+ }
4095
+ const tmpPath = `${jsonlPath}.tmp`;
4096
+ await new Promise((resolve, reject) => {
4097
+ const out = createWriteStream(tmpPath);
4098
+ const entries = cache3.entries();
4099
+ const writeBatch = () => {
4100
+ for (let i2 = 0; i2 < 50; i2++) {
4101
+ const next = entries.next();
4102
+ if (next.done) {
4103
+ out.end();
4104
+ return;
4105
+ }
4106
+ const [hash4, vector] = next.value;
4107
+ if (!out.write(`${JSON.stringify({ h: hash4, v: vector })}
4108
+ `)) {
4109
+ out.once("drain", writeBatch);
4110
+ return;
4111
+ }
4112
+ }
4113
+ setImmediate(writeBatch);
4114
+ };
4115
+ out.on("finish", resolve);
4116
+ out.on("error", reject);
4117
+ writeBatch();
4118
+ });
4119
+ await rename(tmpPath, jsonlPath);
4120
+ diskCacheEntryCount = cache3.size;
4121
+ await writeCacheStat({
4122
+ version: 1,
4123
+ entryCount: cache3.size,
4124
+ dimensions: cacheDimensions
4125
+ });
3710
4126
  diskCacheDirty2 = false;
3711
4127
  } catch {
3712
4128
  }
@@ -3720,12 +4136,7 @@ function scheduleDiskSave() {
3720
4136
  }, DISK_SAVE_DEBOUNCE_MS);
3721
4137
  }
3722
4138
  function cacheSet(hash4, value) {
3723
- if (cache3.size >= MAX_CACHE_SIZE3) {
3724
- const firstKey = cache3.keys().next().value;
3725
- if (firstKey !== void 0) cache3.delete(firstKey);
3726
- }
3727
- cache3.set(hash4, value);
3728
- diskCacheDirty2 = true;
4139
+ cacheInsert(hash4, value, true);
3729
4140
  }
3730
4141
  function resolveEnvEmbeddingApiKey() {
3731
4142
  return process.env.MEMORIX_EMBEDDING_API_KEY;
@@ -3844,18 +4255,27 @@ async function waitForRetry(waitMs, signal) {
3844
4255
  abortSignal.addEventListener("abort", onAbort, { once: true });
3845
4256
  });
3846
4257
  }
3847
- 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;
4258
+ 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;
3848
4259
  var init_api_provider = __esm({
3849
4260
  "src/embedding/api-provider.ts"() {
3850
4261
  "use strict";
3851
4262
  init_esm_shims();
3852
4263
  init_provider();
3853
4264
  cache3 = /* @__PURE__ */ new Map();
3854
- MAX_CACHE_SIZE3 = 1e4;
4265
+ MAX_CACHE_ENTRIES = 2e5;
4266
+ MAX_CACHE_PAYLOAD_BYTES = 256 * 1024 * 1024;
4267
+ BYTES_PER_DIM = 8;
4268
+ DEFAULT_ASSUMED_DIMENSIONS = 4096;
4269
+ MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
4270
+ cachePayloadBytes = 0;
4271
+ cacheDimensions = null;
4272
+ diskCacheEntryCount = 0;
3855
4273
  diskCacheDirty2 = false;
3856
4274
  diskSaveTimer = null;
3857
4275
  diskCacheLoaded = false;
3858
4276
  diskCacheLoadPromise = null;
4277
+ diskCacheLoadCount = 0;
4278
+ jsonlLineCountCalls = 0;
3859
4279
  MAX_INPUT_CHARS = 32e3;
3860
4280
  MAX_CONCURRENCY = 4;
3861
4281
  DISK_SAVE_DEBOUNCE_MS = 5e3;
@@ -3913,7 +4333,9 @@ var init_api_provider = __esm({
3913
4333
  if (config.requestedDimensions) {
3914
4334
  console.error(`[memorix] Dimension shortening: ${config.requestedDimensions}d requested`);
3915
4335
  }
3916
- startDiskCacheLoad();
4336
+ if (allowNetworkProbe) {
4337
+ startDiskCacheLoad();
4338
+ }
3917
4339
  return new _APIEmbeddingProvider(config, probeDimensions);
3918
4340
  }
3919
4341
  static resolveConfig() {
@@ -4251,7 +4673,12 @@ function warnOnce(message) {
4251
4673
  lastEmbeddingWarningAt = now3;
4252
4674
  console.error(message);
4253
4675
  }
4676
+ function embeddingModeToken() {
4677
+ return process.env.MEMORIX_EMBEDDING?.toLowerCase()?.trim() ?? "";
4678
+ }
4254
4679
  function getEmbeddingMode2() {
4680
+ const token = embeddingModeToken();
4681
+ if (cachedEmbeddingMode && cachedEmbeddingModeToken === token) return cachedEmbeddingMode;
4255
4682
  try {
4256
4683
  let cfg;
4257
4684
  try {
@@ -4260,10 +4687,17 @@ function getEmbeddingMode2() {
4260
4687
  cfg = (init_config(), __toCommonJS(config_exports));
4261
4688
  }
4262
4689
  const { getEmbeddingMode: cfgMode } = cfg;
4263
- return cfgMode();
4690
+ cachedEmbeddingMode = cfgMode();
4691
+ cachedEmbeddingModeToken = token;
4692
+ return cachedEmbeddingMode;
4264
4693
  } catch {
4265
- const env2 = process.env.MEMORIX_EMBEDDING?.toLowerCase()?.trim();
4266
- if (env2 === "fastembed" || env2 === "transformers" || env2 === "api" || env2 === "auto") return env2;
4694
+ if (token === "fastembed" || token === "transformers" || token === "api" || token === "auto") {
4695
+ cachedEmbeddingMode = token;
4696
+ cachedEmbeddingModeToken = token;
4697
+ return token;
4698
+ }
4699
+ cachedEmbeddingMode = "off";
4700
+ cachedEmbeddingModeToken = token;
4267
4701
  return "off";
4268
4702
  }
4269
4703
  }
@@ -4519,13 +4953,15 @@ function isEmbeddingExplicitlyDisabled() {
4519
4953
  function resetProvider() {
4520
4954
  provider = null;
4521
4955
  initPromise = null;
4956
+ cachedEmbeddingMode = null;
4957
+ cachedEmbeddingModeToken = null;
4522
4958
  lastInitWasTemporaryFailure = false;
4523
4959
  lastFailureTimestamp = 0;
4524
4960
  lastEmbeddingFailure = null;
4525
4961
  lastEmbeddingWarning = "";
4526
4962
  lastEmbeddingWarningAt = 0;
4527
4963
  }
4528
- var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, lastInitWasTemporaryFailure, lastEmbeddingFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
4964
+ var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, cachedEmbeddingMode, cachedEmbeddingModeToken, lastInitWasTemporaryFailure, lastEmbeddingFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
4529
4965
  var init_provider = __esm({
4530
4966
  "src/embedding/provider.ts"() {
4531
4967
  "use strict";
@@ -4549,6 +4985,8 @@ var init_provider = __esm({
4549
4985
  MAX_INLINE_EMBEDDING_BYTES = 5 * 1024 * 1024;
4550
4986
  provider = null;
4551
4987
  initPromise = null;
4988
+ cachedEmbeddingMode = null;
4989
+ cachedEmbeddingModeToken = null;
4552
4990
  lastInitWasTemporaryFailure = false;
4553
4991
  lastEmbeddingFailure = null;
4554
4992
  lastEmbeddingWarning = "";
@@ -6306,6 +6744,9 @@ async function attachCachedVectors(database, candidates) {
6306
6744
  await update(database, candidates[index].id, { ...existing, embedding: vector });
6307
6745
  } catch {
6308
6746
  }
6747
+ if (index % 50 === 0) {
6748
+ await new Promise((resolve) => setImmediate(resolve));
6749
+ }
6309
6750
  }
6310
6751
  }
6311
6752
  async function hydrateIndex(observations2, options = {}) {
@@ -6356,17 +6797,24 @@ async function hydrateIndex(observations2, options = {}) {
6356
6797
  await insert2(database, doc);
6357
6798
  rememberObservationDoc(doc);
6358
6799
  inserted++;
6800
+ if (inserted % 50 === 0) {
6801
+ await new Promise((resolve) => setImmediate(resolve));
6802
+ }
6803
+ if (inserted % 200 === 0) {
6804
+ await new Promise((resolve) => setTimeout(resolve, 0));
6805
+ }
6359
6806
  } catch {
6360
6807
  }
6361
6808
  }
6362
- deferredCachedVectorHydration = deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
6809
+ deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
6363
6810
  }) : null;
6364
6811
  return inserted;
6365
6812
  }
6366
- async function hydrateIndexForStartup(observations2) {
6813
+ async function hydrateIndexForStartup(observations2, options = {}) {
6367
6814
  return hydrateIndex(observations2, {
6368
6815
  allowNetworkProbe: false,
6369
- deferCachedVectors: true
6816
+ deferCachedVectors: true,
6817
+ skipCachedVectors: options.skipCachedVectors
6370
6818
  });
6371
6819
  }
6372
6820
  function getDeferredCachedVectorHydration() {
@@ -8457,7 +8905,7 @@ var init_types2 = __esm({
8457
8905
 
8458
8906
  // src/media/asset-store.ts
8459
8907
  import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
8460
- import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename, rm, stat, writeFile as writeFile3 } from "fs/promises";
8908
+ import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
8461
8909
  import path9 from "path";
8462
8910
  function mediaRoot(dataDir) {
8463
8911
  return path9.resolve(dataDir, "media");
@@ -8530,7 +8978,7 @@ async function persistBuffer(bytes, destination) {
8530
8978
  const temporary = `${destination}.${randomUUID5()}.tmp`;
8531
8979
  try {
8532
8980
  await writeFile3(temporary, bytes, { flag: "wx" });
8533
- await rename(temporary, destination);
8981
+ await rename2(temporary, destination);
8534
8982
  return true;
8535
8983
  } catch (error) {
8536
8984
  if (error?.code === "EEXIST" && await destinationExists(destination)) return false;
@@ -59610,9 +60058,9 @@ var require_node_domexception = __commonJS({
59610
60058
  });
59611
60059
 
59612
60060
  // node_modules/fetch-blob/from.js
59613
- import { statSync as statSync2, createReadStream, promises as fs7 } from "fs";
60061
+ import { statSync as statSync2, createReadStream as createReadStream2, promises as fs7 } from "fs";
59614
60062
  import { basename } from "path";
59615
- var import_node_domexception, stat2, blobFromSync, blobFrom, fileFrom, fileFromSync, fromBlob, fromFile, BlobDataItem;
60063
+ var import_node_domexception, stat3, blobFromSync, blobFrom, fileFrom, fileFromSync, fromBlob, fromFile, BlobDataItem;
59616
60064
  var init_from = __esm({
59617
60065
  "node_modules/fetch-blob/from.js"() {
59618
60066
  "use strict";
@@ -59620,23 +60068,23 @@ var init_from = __esm({
59620
60068
  import_node_domexception = __toESM(require_node_domexception(), 1);
59621
60069
  init_file();
59622
60070
  init_fetch_blob();
59623
- ({ stat: stat2 } = fs7);
60071
+ ({ stat: stat3 } = fs7);
59624
60072
  blobFromSync = (path21, type) => fromBlob(statSync2(path21), path21, type);
59625
- blobFrom = (path21, type) => stat2(path21).then((stat4) => fromBlob(stat4, path21, type));
59626
- fileFrom = (path21, type) => stat2(path21).then((stat4) => fromFile(stat4, path21, type));
60073
+ blobFrom = (path21, type) => stat3(path21).then((stat5) => fromBlob(stat5, path21, type));
60074
+ fileFrom = (path21, type) => stat3(path21).then((stat5) => fromFile(stat5, path21, type));
59627
60075
  fileFromSync = (path21, type) => fromFile(statSync2(path21), path21, type);
59628
- fromBlob = (stat4, path21, type = "") => new fetch_blob_default([new BlobDataItem({
60076
+ fromBlob = (stat5, path21, type = "") => new fetch_blob_default([new BlobDataItem({
59629
60077
  path: path21,
59630
- size: stat4.size,
59631
- lastModified: stat4.mtimeMs,
60078
+ size: stat5.size,
60079
+ lastModified: stat5.mtimeMs,
59632
60080
  start: 0
59633
60081
  })], { type });
59634
- fromFile = (stat4, path21, type = "") => new file_default([new BlobDataItem({
60082
+ fromFile = (stat5, path21, type = "") => new file_default([new BlobDataItem({
59635
60083
  path: path21,
59636
- size: stat4.size,
59637
- lastModified: stat4.mtimeMs,
60084
+ size: stat5.size,
60085
+ lastModified: stat5.mtimeMs,
59638
60086
  start: 0
59639
- })], basename(path21), { type, lastModified: stat4.mtimeMs });
60087
+ })], basename(path21), { type, lastModified: stat5.mtimeMs });
59640
60088
  BlobDataItem = class _BlobDataItem {
59641
60089
  #path;
59642
60090
  #start;
@@ -59659,11 +60107,11 @@ var init_from = __esm({
59659
60107
  });
59660
60108
  }
59661
60109
  async *stream() {
59662
- const { mtimeMs } = await stat2(this.#path);
60110
+ const { mtimeMs } = await stat3(this.#path);
59663
60111
  if (mtimeMs > this.lastModified) {
59664
60112
  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");
59665
60113
  }
59666
- yield* createReadStream(this.#path, {
60114
+ yield* createReadStream2(this.#path, {
59667
60115
  start: this.#start,
59668
60116
  end: this.#start + this.size - 1
59669
60117
  });
@@ -75293,7 +75741,7 @@ var init_wrapper = __esm({
75293
75741
  });
75294
75742
 
75295
75743
  // node_modules/@google/genai/dist/node/index.mjs
75296
- import { createWriteStream } from "fs";
75744
+ import { createWriteStream as createWriteStream2 } from "fs";
75297
75745
  import * as fs8 from "fs/promises";
75298
75746
  import { writeFile as writeFile4 } from "fs/promises";
75299
75747
  import { Readable } from "stream";
@@ -92573,7 +93021,7 @@ ${underline}`);
92573
93021
  if (params.downloadPath) {
92574
93022
  const response = await downloadFile(params, apiClient);
92575
93023
  if (response instanceof HttpResponse) {
92576
- const writer = createWriteStream(params.downloadPath);
93024
+ const writer = createWriteStream2(params.downloadPath);
92577
93025
  const body = Readable.fromWeb(response.responseInternal.body);
92578
93026
  body.pipe(writer);
92579
93027
  await finished(writer);
@@ -142197,7 +142645,7 @@ function explainRetention(doc, referenceTime) {
142197
142645
  };
142198
142646
  }
142199
142647
  function toRetentionDocument(obs, accessMap) {
142200
- const access = accessMap?.get(obs.id);
142648
+ const access2 = accessMap?.get(obs.id);
142201
142649
  return {
142202
142650
  id: `obs-${obs.id}`,
142203
142651
  observationId: obs.id,
@@ -142211,8 +142659,8 @@ function toRetentionDocument(obs, accessMap) {
142211
142659
  tokens: obs.tokens,
142212
142660
  createdAt: obs.createdAt,
142213
142661
  projectId: obs.projectId,
142214
- accessCount: access?.accessCount ?? 0,
142215
- lastAccessedAt: access?.lastAccessedAt ?? "",
142662
+ accessCount: access2?.accessCount ?? 0,
142663
+ lastAccessedAt: access2?.lastAccessedAt ?? "",
142216
142664
  status: obs.status ?? "active",
142217
142665
  source: obs.source ?? "agent",
142218
142666
  sourceDetail: obs.sourceDetail ?? "",
@@ -144322,7 +144770,7 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
144322
144770
  const rel = normalizeCodePath(relative(projectRoot, abs));
144323
144771
  try {
144324
144772
  const text2 = readFileSync6(abs, "utf-8");
144325
- const stat4 = fileStat ?? statSync3(abs);
144773
+ const stat5 = fileStat ?? statSync3(abs);
144326
144774
  const file = {
144327
144775
  id: makeCodeFileId(projectId, rel),
144328
144776
  projectId,
@@ -144331,8 +144779,8 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
144331
144779
  contentHash: hashText(text2),
144332
144780
  // Preserve filesystem precision. Rounding here made same-size edits in
144333
144781
  // a short Windows timestamp window indistinguishable to refresh.
144334
- mtimeMs: Number(stat4.mtimeMs),
144335
- sizeBytes: Number(stat4.size),
144782
+ mtimeMs: Number(stat5.mtimeMs),
144783
+ sizeBytes: Number(stat5.size),
144336
144784
  indexedAt
144337
144785
  };
144338
144786
  return {
@@ -144359,18 +144807,18 @@ async function indexProjectLite(options) {
144359
144807
  let skippedOversizedFiles = 0;
144360
144808
  let unreadableFiles = 0;
144361
144809
  for (const abs of paths) {
144362
- let stat4;
144810
+ let stat5;
144363
144811
  try {
144364
- stat4 = statSync3(abs);
144812
+ stat5 = statSync3(abs);
144365
144813
  } catch {
144366
144814
  unreadableFiles++;
144367
144815
  continue;
144368
144816
  }
144369
- if (Number(stat4.size) > maxFileBytes) {
144817
+ if (Number(stat5.size) > maxFileBytes) {
144370
144818
  skippedOversizedFiles++;
144371
144819
  continue;
144372
144820
  }
144373
- const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat4);
144821
+ const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
144374
144822
  if (!attempt.delta) {
144375
144823
  if (attempt.unreadable) unreadableFiles++;
144376
144824
  continue;
@@ -144398,16 +144846,16 @@ async function refreshProjectLite(store, options) {
144398
144846
  for (const abs of paths) {
144399
144847
  const rel = normalizeCodePath(relative(options.projectRoot, abs));
144400
144848
  seenPaths.add(rel);
144401
- let stat4;
144849
+ let stat5;
144402
144850
  try {
144403
- stat4 = statSync3(abs);
144851
+ stat5 = statSync3(abs);
144404
144852
  } catch {
144405
144853
  unreadableFiles++;
144406
144854
  continue;
144407
144855
  }
144408
144856
  const existing = existingByPath.get(rel);
144409
- const mtimeMs = Number(stat4.mtimeMs);
144410
- const sizeBytes = Number(stat4.size);
144857
+ const mtimeMs = Number(stat5.mtimeMs);
144858
+ const sizeBytes = Number(stat5.size);
144411
144859
  if (sizeBytes > maxFileBytes) {
144412
144860
  skippedOversizedFiles++;
144413
144861
  if (existing) oversizedExistingFileIds.add(existing.id);
@@ -144417,7 +144865,7 @@ async function refreshProjectLite(store, options) {
144417
144865
  unchangedFiles++;
144418
144866
  continue;
144419
144867
  }
144420
- const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat4);
144868
+ const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
144421
144869
  if (!attempt.delta) {
144422
144870
  if (attempt.unreadable) unreadableFiles++;
144423
144871
  continue;
@@ -147814,12 +148262,25 @@ __export(observations_exports, {
147814
148262
  prepareSearchIndex: () => prepareSearchIndex,
147815
148263
  probeSearchIndex: () => probeSearchIndex,
147816
148264
  reindexObservations: () => reindexObservations,
148265
+ resetObservationRuntime: () => resetObservationRuntime,
147817
148266
  resolveObservations: () => resolveObservations,
147818
148267
  storeObservation: () => storeObservation,
147819
148268
  suggestTopicKey: () => suggestTopicKey,
147820
148269
  updateObservationAdmission: () => updateObservationAdmission,
147821
148270
  withFreshObservations: () => withFreshObservations
147822
148271
  });
148272
+ function resetObservationRuntime() {
148273
+ observations = [];
148274
+ nextId = 1;
148275
+ projectDir = null;
148276
+ searchIndexPrepared = false;
148277
+ corpusLoaded = false;
148278
+ loadedObservationStore = null;
148279
+ embeddingWriteMode = "background";
148280
+ embeddingWorkerProjectRoot = void 0;
148281
+ vectorMissingIds.clear();
148282
+ vectorSchemaUpgradePromise = null;
148283
+ }
147823
148284
  function logEmbeddingFailureOnce(key, message) {
147824
148285
  const now3 = Date.now();
147825
148286
  const last = embeddingFailureLogTimestamps.get(key) ?? 0;
@@ -147928,23 +148389,33 @@ async function upgradeVectorSchemaAfterFirstEmbedding(embedding) {
147928
148389
  async function initObservations(dir, options = {}) {
147929
148390
  embeddingWriteMode = options.embeddingWriteMode ?? "background";
147930
148391
  embeddingWorkerProjectRoot = options.projectRoot;
147931
- if (projectDir === dir) return;
147932
148392
  await initObservationStore(dir);
147933
148393
  const store = getObservationStore();
147934
- observations = await store.loadAll();
148394
+ if (projectDir === dir && loadedObservationStore === store) {
148395
+ if (options.skipCorpusLoad || corpusLoaded) return;
148396
+ }
147935
148397
  nextId = await store.loadIdCounter();
147936
148398
  projectDir = dir;
147937
148399
  searchIndexPrepared = false;
147938
148400
  vectorSchemaUpgradePromise = null;
148401
+ if (options.skipCorpusLoad) {
148402
+ observations = [];
148403
+ corpusLoaded = false;
148404
+ loadedObservationStore = store;
148405
+ return;
148406
+ }
148407
+ observations = await store.loadAll();
148408
+ corpusLoaded = true;
148409
+ loadedObservationStore = store;
147939
148410
  }
147940
148411
  function scheduleObservationEmbedding(input) {
147941
148412
  const { observationId: observationId2, projectId, searchableText, doc } = input;
147942
148413
  vectorMissingIds.add(observationId2);
148414
+ if (isEmbeddingExplicitlyDisabled()) {
148415
+ vectorMissingIds.delete(observationId2);
148416
+ return;
148417
+ }
147943
148418
  if (embeddingWriteMode === "deferred") {
147944
- if (isEmbeddingExplicitlyDisabled()) {
147945
- vectorMissingIds.delete(observationId2);
147946
- return;
147947
- }
147948
148419
  queueVectorBackfill(projectId, {
147949
148420
  detachedWorker: true,
147950
148421
  projectRoot: embeddingWorkerProjectRoot
@@ -147991,7 +148462,7 @@ function scheduleObservationEmbedding(input) {
147991
148462
  });
147992
148463
  }
147993
148464
  async function ensureFreshObservations() {
147994
- if (!projectDir) return false;
148465
+ if (!projectDir || !corpusLoaded) return false;
147995
148466
  try {
147996
148467
  const store = getObservationStore();
147997
148468
  const wasStale = await store.ensureFresh();
@@ -148032,7 +148503,7 @@ async function storeObservation(input) {
148032
148503
  });
148033
148504
  if (safeAttachments) input = { ...input, attachments: safeAttachments };
148034
148505
  await ensureFreshObservations();
148035
- if (input.topicKey) {
148506
+ if (input.topicKey && corpusLoaded) {
148036
148507
  const existing = observations.find(
148037
148508
  (o) => o.topicKey === input.topicKey && o.projectId === input.projectId
148038
148509
  );
@@ -148129,13 +148600,17 @@ async function storeObservation(input) {
148129
148600
  });
148130
148601
  observation.writeGeneration = store.getGeneration();
148131
148602
  if (upsertedInsideLock || reloadCacheAfterCommit) {
148132
- observations = await store.loadAll();
148133
- nextId = await store.loadIdCounter();
148134
- if (upsertedInsideLock) {
148135
- observation = observations.find((candidate) => candidate.id === observation.id) ?? observation;
148603
+ if (corpusLoaded) {
148604
+ observations = await store.loadAll();
148605
+ nextId = await store.loadIdCounter();
148606
+ if (upsertedInsideLock) {
148607
+ observation = observations.find((candidate) => candidate.id === observation.id) ?? observation;
148608
+ }
148609
+ } else {
148610
+ nextId = await store.loadIdCounter();
148136
148611
  }
148137
148612
  } else {
148138
- observations.push(observation);
148613
+ if (corpusLoaded) observations.push(observation);
148139
148614
  nextId = observation.id + 1;
148140
148615
  }
148141
148616
  if (upsertedInsideLock) return;
@@ -148203,7 +148678,9 @@ async function storeObservation(input) {
148203
148678
  createdByAgentId: input.createdByAgentId ?? "",
148204
148679
  sharedWithAgentIds: JSON.stringify(input.sharedWithAgentIds ?? [])
148205
148680
  };
148206
- await insertObservation(doc);
148681
+ if (corpusLoaded) {
148682
+ await insertObservation(doc);
148683
+ }
148207
148684
  };
148208
148685
  await assignAndPersist();
148209
148686
  if (upsertedInsideLock) {
@@ -148565,9 +149042,11 @@ async function reindexObservations() {
148565
149042
  searchIndexPrepared = true;
148566
149043
  return count2;
148567
149044
  }
148568
- async function prepareSearchIndex() {
149045
+ async function prepareSearchIndex(options = {}) {
148569
149046
  if (searchIndexPrepared) return 0;
148570
- const count2 = await hydrateIndexForStartup(observations);
149047
+ const count2 = await hydrateIndexForStartup(observations, {
149048
+ skipCachedVectors: options.skipCachedVectors
149049
+ });
148571
149050
  if (count2 === 0) {
148572
149051
  searchIndexPrepared = true;
148573
149052
  return 0;
@@ -148759,7 +149238,7 @@ async function backfillVectorEmbeddings(options = {}) {
148759
149238
  ...lastFailure ? { lastError: lastFailure } : {}
148760
149239
  };
148761
149240
  }
148762
- var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
149241
+ var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
148763
149242
  var init_observations = __esm({
148764
149243
  "src/memory/observations.ts"() {
148765
149244
  "use strict";
@@ -148778,6 +149257,8 @@ var init_observations = __esm({
148778
149257
  projectDir = null;
148779
149258
  searchIndexPrepared = false;
148780
149259
  embeddingWriteMode = "background";
149260
+ corpusLoaded = false;
149261
+ loadedObservationStore = null;
148781
149262
  vectorMissingIds = /* @__PURE__ */ new Set();
148782
149263
  vectorBackfillRunning = false;
148783
149264
  vectorSchemaUpgradePromise = null;