memorix 1.6.0 → 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 (41) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/cli/index.js +1888 -1122
  3. package/dist/cli/memcode.js +12 -1
  4. package/dist/index.js +641 -113
  5. package/dist/maintenance-runner.js +618 -90
  6. package/dist/media-audio-runner.js +618 -90
  7. package/dist/media-video-runner.js +618 -90
  8. package/dist/memcode-runtime/CHANGELOG.md +46 -0
  9. package/dist/sdk.js +674 -117
  10. package/dist/vector-backfill-runner.js +618 -90
  11. package/docs/AGENT_OPERATOR_PLAYBOOK.md +11 -3
  12. package/docs/API_REFERENCE.md +1 -1
  13. package/docs/DOCKER.md +5 -1
  14. package/docs/PERFORMANCE.md +17 -2
  15. package/docs/README.md +3 -1
  16. package/docs/SETUP.md +8 -2
  17. package/package.json +2 -1
  18. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  19. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  20. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  21. package/plugins/copilot/memorix/plugin.json +1 -1
  22. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  23. package/plugins/omp/memorix/package.json +1 -1
  24. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  25. package/plugins/pi/memorix/package.json +1 -1
  26. package/server.json +2 -2
  27. package/src/cli/commands/agent-integrations.ts +9 -3
  28. package/src/cli/commands/background.ts +34 -2
  29. package/src/cli/commands/doctor.ts +46 -15
  30. package/src/cli/commands/hook.ts +13 -1
  31. package/src/cli/commands/operator-shared.ts +6 -3
  32. package/src/cli/commands/serve-http.ts +72 -7
  33. package/src/cli/heap.ts +76 -0
  34. package/src/cli/invocation.ts +7 -4
  35. package/src/embedding/api-provider.ts +503 -27
  36. package/src/embedding/provider.ts +96 -9
  37. package/src/hooks/handler.ts +9 -18
  38. package/src/hooks/lifecycle.ts +174 -0
  39. package/src/memory/observations.ts +68 -18
  40. package/src/sdk.ts +12 -8
  41. package/src/store/orama-store.ts +21 -3
@@ -3126,8 +3126,8 @@ async function acquireLock(lockPath) {
3126
3126
  const code = err instanceof Error && "code" in err ? err.code : void 0;
3127
3127
  if (code === "EEXIST" || code === "EPERM") {
3128
3128
  try {
3129
- const stat4 = await fs3.stat(lockPath);
3130
- if (Date.now() - stat4.mtimeMs > LOCK_STALE_MS) {
3129
+ const stat5 = await fs3.stat(lockPath);
3130
+ if (Date.now() - stat5.mtimeMs > LOCK_STALE_MS) {
3131
3131
  await fs3.unlink(lockPath).catch(() => {
3132
3132
  });
3133
3133
  continue;
@@ -3583,24 +3583,131 @@ var init_transformers_provider = __esm({
3583
3583
  // src/embedding/api-provider.ts
3584
3584
  var api_provider_exports = {};
3585
3585
  __export(api_provider_exports, {
3586
- APIEmbeddingProvider: () => APIEmbeddingProvider
3586
+ APIEmbeddingProvider: () => APIEmbeddingProvider,
3587
+ convertArrayCacheToJsonl: () => convertArrayCacheToJsonl,
3588
+ ensureDiskCacheLoaded: () => ensureDiskCacheLoaded,
3589
+ getApiEmbeddingCachePayloadBytesForTests: () => getApiEmbeddingCachePayloadBytesForTests,
3590
+ getApiEmbeddingDiskCacheLoadCountForTests: () => getApiEmbeddingDiskCacheLoadCountForTests,
3591
+ getApiEmbeddingJsonlLineCountCallsForTests: () => getApiEmbeddingJsonlLineCountCallsForTests,
3592
+ getApiEmbeddingMaxEntriesForDimensionsForTests: () => getApiEmbeddingMaxEntriesForDimensionsForTests,
3593
+ migrateJsonArrayCacheToJsonl: () => migrateJsonArrayCacheToJsonl,
3594
+ parseCacheJsonInWorker: () => parseCacheJsonInWorker,
3595
+ resetApiEmbeddingCacheForTests: () => resetApiEmbeddingCacheForTests,
3596
+ saveDiskCacheNow: () => saveDiskCacheNow,
3597
+ seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
3587
3598
  });
3588
3599
  import { createHash as createHash2 } from "crypto";
3589
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
3600
+ import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
3601
+ import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
3590
3602
  import { join as join3 } from "path";
3591
3603
  import { homedir as homedir7 } from "os";
3604
+ import { createInterface } from "readline";
3592
3605
  function cacheDir() {
3593
3606
  return process.env.MEMORIX_DATA_DIR || join3(homedir7(), ".memorix", "data");
3594
3607
  }
3595
3608
  function cacheFile() {
3596
3609
  return join3(cacheDir(), ".embedding-api-cache.json");
3597
3610
  }
3611
+ function cacheJsonlFile() {
3612
+ return join3(cacheDir(), ".embedding-api-cache.jsonl");
3613
+ }
3598
3614
  function dimsCacheFile() {
3599
3615
  return join3(cacheDir(), ".embedding-dims-cache.json");
3600
3616
  }
3601
3617
  function cacheMetaFile() {
3602
3618
  return join3(cacheDir(), ".embedding-api-cache-meta.json");
3603
3619
  }
3620
+ function cacheStatFile() {
3621
+ return join3(cacheDir(), ".embedding-api-cache-stat.json");
3622
+ }
3623
+ function payloadBytes(vector) {
3624
+ return vector.length * BYTES_PER_DIM;
3625
+ }
3626
+ function maxCachePayloadBytes() {
3627
+ const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
3628
+ if (raw) {
3629
+ const parsed = Number.parseInt(raw, 10);
3630
+ if (Number.isFinite(parsed) && parsed > 0) return parsed;
3631
+ }
3632
+ return MAX_CACHE_PAYLOAD_BYTES;
3633
+ }
3634
+ function maxEntriesForDimensions(dimensions) {
3635
+ const dim = Math.max(1, Math.floor(dimensions));
3636
+ const byBytes = Math.floor(maxCachePayloadBytes() / (dim * BYTES_PER_DIM));
3637
+ return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
3638
+ }
3639
+ function rememberDimensions(vector) {
3640
+ if (vector.length > 0 && (cacheDimensions === null || vector.length > cacheDimensions)) {
3641
+ cacheDimensions = vector.length;
3642
+ }
3643
+ }
3644
+ function evictOldest() {
3645
+ const firstKey = cache3.keys().next().value;
3646
+ if (firstKey === void 0) return;
3647
+ const old = cache3.get(firstKey);
3648
+ cache3.delete(firstKey);
3649
+ if (old) cachePayloadBytes -= payloadBytes(old);
3650
+ }
3651
+ function evictToFit(incomingBytes, incomingDims) {
3652
+ const limit2 = maxEntriesForDimensions(incomingDims);
3653
+ while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes + incomingBytes > maxCachePayloadBytes())) {
3654
+ evictOldest();
3655
+ }
3656
+ }
3657
+ function isNumberArray(value) {
3658
+ return Array.isArray(value) && value.every((entry) => typeof entry === "number");
3659
+ }
3660
+ function parseCacheRow(raw) {
3661
+ if (Array.isArray(raw)) {
3662
+ const hash4 = raw[0];
3663
+ const vector = raw[1];
3664
+ if (typeof hash4 === "string" && isNumberArray(vector)) {
3665
+ return { hash: hash4, vector };
3666
+ }
3667
+ return null;
3668
+ }
3669
+ if (raw && typeof raw === "object") {
3670
+ const row = raw;
3671
+ if (typeof row.h === "string" && isNumberArray(row.v)) {
3672
+ return { hash: row.h, vector: row.v };
3673
+ }
3674
+ }
3675
+ return null;
3676
+ }
3677
+ function cacheInsert(hash4, value, dirty) {
3678
+ rememberDimensions(value);
3679
+ const incoming = payloadBytes(value);
3680
+ const existing = cache3.get(hash4);
3681
+ if (existing) {
3682
+ cachePayloadBytes -= payloadBytes(existing);
3683
+ cache3.delete(hash4);
3684
+ }
3685
+ evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
3686
+ cache3.set(hash4, value);
3687
+ cachePayloadBytes += incoming;
3688
+ if (dirty) diskCacheDirty2 = true;
3689
+ }
3690
+ async function readCacheStat() {
3691
+ try {
3692
+ const data = JSON.parse(await readFile2(cacheStatFile(), "utf-8"));
3693
+ if (typeof data.entryCount === "number" && Number.isFinite(data.entryCount) && data.entryCount >= 0) {
3694
+ return {
3695
+ version: 1,
3696
+ entryCount: Math.floor(data.entryCount),
3697
+ dimensions: isValidDimension(data.dimensions) ? data.dimensions : null
3698
+ };
3699
+ }
3700
+ } catch {
3701
+ }
3702
+ return null;
3703
+ }
3704
+ async function writeCacheStat(stat5) {
3705
+ try {
3706
+ await mkdir2(cacheDir(), { recursive: true });
3707
+ await writeFile2(cacheStatFile(), JSON.stringify(stat5));
3708
+ } catch {
3709
+ }
3710
+ }
3604
3711
  function normalizeText(text2) {
3605
3712
  return text2.replace(/\s+/g, " ").trim().slice(0, MAX_INPUT_CHARS);
3606
3713
  }
@@ -3686,29 +3793,300 @@ function toProviderInput(input, baseUrl) {
3686
3793
  }
3687
3794
  return { type: input.modality, url: input.url };
3688
3795
  }
3796
+ function parseCacheJsonInWorker(filePath) {
3797
+ return new Promise((resolve, reject) => {
3798
+ void import("worker_threads").then(({ Worker }) => {
3799
+ const worker = new Worker(
3800
+ `
3801
+ const { parentPort, workerData } = require('node:worker_threads');
3802
+ const { readFileSync } = require('node:fs');
3803
+ try {
3804
+ const raw = readFileSync(workerData.filePath, 'utf8');
3805
+ parentPort.postMessage({ ok: true, entries: JSON.parse(raw) });
3806
+ } catch (err) {
3807
+ parentPort.postMessage({
3808
+ ok: false,
3809
+ error: err instanceof Error ? err.message : String(err),
3810
+ });
3811
+ }
3812
+ `,
3813
+ { eval: true, workerData: { filePath } }
3814
+ );
3815
+ worker.once("message", (msg) => {
3816
+ void worker.terminate();
3817
+ if (msg?.ok && Array.isArray(msg.entries)) {
3818
+ resolve(msg.entries);
3819
+ return;
3820
+ }
3821
+ reject(new Error(msg?.error || "embedding cache worker parse failed"));
3822
+ });
3823
+ worker.once("error", reject);
3824
+ }).catch(reject);
3825
+ });
3826
+ }
3827
+ function findJsonValueEnd(source) {
3828
+ let i2 = 0;
3829
+ while (i2 < source.length && /\s/.test(source[i2])) i2 += 1;
3830
+ if (i2 >= source.length) return -1;
3831
+ const start = source[i2];
3832
+ if (start === '"') {
3833
+ i2 += 1;
3834
+ while (i2 < source.length) {
3835
+ if (source[i2] === "\\") {
3836
+ i2 += 2;
3837
+ continue;
3838
+ }
3839
+ if (source[i2] === '"') return i2 + 1;
3840
+ i2 += 1;
3841
+ }
3842
+ return -1;
3843
+ }
3844
+ if (start === "{" || start === "[") {
3845
+ const close = start === "{" ? "}" : "]";
3846
+ let depth = 0;
3847
+ let inString = false;
3848
+ for (; i2 < source.length; i2 += 1) {
3849
+ const ch = source[i2];
3850
+ if (inString) {
3851
+ if (ch === "\\") {
3852
+ i2 += 1;
3853
+ continue;
3854
+ }
3855
+ if (ch === '"') inString = false;
3856
+ continue;
3857
+ }
3858
+ if (ch === '"') {
3859
+ inString = true;
3860
+ continue;
3861
+ }
3862
+ if (ch === start) depth += 1;
3863
+ else if (ch === close) {
3864
+ depth -= 1;
3865
+ if (depth === 0) return i2 + 1;
3866
+ }
3867
+ }
3868
+ return -1;
3869
+ }
3870
+ const rest = source.slice(i2);
3871
+ const match2 = rest.match(/^(?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)/);
3872
+ return match2 ? i2 + match2[0].length : -1;
3873
+ }
3874
+ async function streamJsonArrayToJsonl(filePath, tmpPath) {
3875
+ const input = createReadStream(filePath, { encoding: "utf8" });
3876
+ const out = createWriteStream(tmpPath);
3877
+ let buf = "";
3878
+ let started = false;
3879
+ let count2 = 0;
3880
+ const writeLine = async (line) => {
3881
+ if (!out.write(line)) {
3882
+ await new Promise((resolve, reject) => {
3883
+ out.once("drain", resolve);
3884
+ out.once("error", reject);
3885
+ });
3886
+ }
3887
+ };
3888
+ try {
3889
+ for await (const chunk of input) {
3890
+ buf += chunk;
3891
+ if (!started) {
3892
+ const idx = buf.search(/\S/);
3893
+ if (idx < 0) continue;
3894
+ if (buf[idx] !== "[") throw new Error("embedding cache is not a JSON array");
3895
+ buf = buf.slice(idx + 1);
3896
+ started = true;
3897
+ }
3898
+ while (true) {
3899
+ buf = buf.replace(/^\s*,?\s*/, "");
3900
+ if (!buf) break;
3901
+ if (buf[0] === "]") {
3902
+ buf = "";
3903
+ break;
3904
+ }
3905
+ const end = findJsonValueEnd(buf);
3906
+ if (end < 0) break;
3907
+ const value = JSON.parse(buf.slice(0, end));
3908
+ buf = buf.slice(end);
3909
+ const row = parseCacheRow(value);
3910
+ if (row) {
3911
+ await writeLine(`${JSON.stringify({ h: row.hash, v: row.vector })}
3912
+ `);
3913
+ count2 += 1;
3914
+ if (count2 % 50 === 0) {
3915
+ await new Promise((resolve) => setImmediate(resolve));
3916
+ }
3917
+ }
3918
+ }
3919
+ if (buf.length > MAX_MIGRATION_VALUE_BYTES) {
3920
+ throw new Error("embedding cache entry too large to migrate safely");
3921
+ }
3922
+ }
3923
+ } finally {
3924
+ await new Promise((resolve, reject) => {
3925
+ out.end(() => resolve());
3926
+ out.once("error", reject);
3927
+ });
3928
+ }
3929
+ return count2;
3930
+ }
3931
+ async function migrateJsonArrayCacheToJsonl(filePath, jsonlPath) {
3932
+ const tmpPath = `${jsonlPath}.tmp`;
3933
+ try {
3934
+ const count2 = await streamJsonArrayToJsonl(filePath, tmpPath);
3935
+ await rename(tmpPath, jsonlPath);
3936
+ diskCacheEntryCount = count2;
3937
+ await writeCacheStat({
3938
+ version: 1,
3939
+ entryCount: count2,
3940
+ dimensions: cacheDimensions
3941
+ });
3942
+ return count2;
3943
+ } catch (err) {
3944
+ await unlink(tmpPath).catch(() => void 0);
3945
+ throw err;
3946
+ }
3947
+ }
3948
+ function convertArrayCacheToJsonl(filePath, jsonlPath) {
3949
+ return migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
3950
+ }
3951
+ async function loadCacheJsonl(filePath) {
3952
+ const rl = createInterface({
3953
+ input: createReadStream(filePath, { encoding: "utf8" }),
3954
+ crlfDelay: Infinity
3955
+ });
3956
+ let loaded = 0;
3957
+ let onDiskCount = 0;
3958
+ for await (const line of rl) {
3959
+ const trimmed = line.trim();
3960
+ if (!trimmed) continue;
3961
+ try {
3962
+ const row = parseCacheRow(JSON.parse(trimmed));
3963
+ if (!row) continue;
3964
+ onDiskCount += 1;
3965
+ cacheInsert(row.hash, row.vector, false);
3966
+ loaded = cache3.size;
3967
+ } catch {
3968
+ }
3969
+ if (onDiskCount % 250 === 0) {
3970
+ await new Promise((resolve) => setImmediate(resolve));
3971
+ }
3972
+ }
3973
+ diskCacheEntryCount = onDiskCount;
3974
+ await writeCacheStat({
3975
+ version: 1,
3976
+ entryCount: onDiskCount,
3977
+ dimensions: cacheDimensions
3978
+ });
3979
+ return loaded;
3980
+ }
3981
+ async function countJsonlLines(filePath) {
3982
+ jsonlLineCountCalls += 1;
3983
+ try {
3984
+ const rl = createInterface({
3985
+ input: createReadStream(filePath, { encoding: "utf8" }),
3986
+ crlfDelay: Infinity
3987
+ });
3988
+ let count2 = 0;
3989
+ for await (const line of rl) {
3990
+ if (line.trim()) count2 += 1;
3991
+ }
3992
+ return count2;
3993
+ } catch {
3994
+ return 0;
3995
+ }
3996
+ }
3997
+ async function resolveOnDiskCount(jsonlPath) {
3998
+ if (diskCacheEntryCount > 0) return diskCacheEntryCount;
3999
+ const sidecar = await readCacheStat();
4000
+ if (sidecar) {
4001
+ diskCacheEntryCount = sidecar.entryCount;
4002
+ if (sidecar.dimensions && cacheDimensions === null) cacheDimensions = sidecar.dimensions;
4003
+ return sidecar.entryCount;
4004
+ }
4005
+ try {
4006
+ await access(jsonlPath, fsConstants.R_OK);
4007
+ } catch {
4008
+ return 0;
4009
+ }
4010
+ const counted = await countJsonlLines(jsonlPath);
4011
+ diskCacheEntryCount = counted;
4012
+ await writeCacheStat({
4013
+ version: 1,
4014
+ entryCount: counted,
4015
+ dimensions: cacheDimensions
4016
+ });
4017
+ return counted;
4018
+ }
3689
4019
  async function loadDiskCache2() {
3690
- if (diskCacheLoaded) return;
4020
+ if (diskCacheLoaded) return cache3.size;
4021
+ let loaded = 0;
3691
4022
  try {
3692
- const raw = await readFile2(cacheFile(), "utf-8");
3693
- const entries = JSON.parse(raw);
3694
- for (const [k, v] of entries) cache3.set(k, v);
3695
- console.error(`[memorix] Loaded ${entries.length} cached API embeddings from disk`);
4023
+ const jsonlPath = cacheJsonlFile();
4024
+ let hasJsonl = false;
4025
+ try {
4026
+ await access(jsonlPath, fsConstants.R_OK);
4027
+ hasJsonl = true;
4028
+ } catch {
4029
+ hasJsonl = false;
4030
+ }
4031
+ if (hasJsonl) {
4032
+ loaded = await loadCacheJsonl(jsonlPath);
4033
+ } else {
4034
+ const filePath = cacheFile();
4035
+ await stat(filePath);
4036
+ await migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
4037
+ loaded = await loadCacheJsonl(jsonlPath);
4038
+ }
4039
+ diskCacheLoadCount += 1;
4040
+ console.error(`[memorix] Loaded ${loaded} cached API embeddings from disk`);
3696
4041
  } catch {
3697
4042
  }
3698
4043
  diskCacheLoaded = true;
4044
+ return loaded;
3699
4045
  }
3700
4046
  function startDiskCacheLoad() {
3701
4047
  if (diskCacheLoaded || diskCacheLoadPromise) return;
3702
- diskCacheLoadPromise = loadDiskCache2().catch(() => {
4048
+ diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
3703
4049
  });
3704
4050
  }
3705
4051
  async function ensureDiskCacheLoaded() {
3706
- if (diskCacheLoaded) return;
3707
- if (diskCacheLoadPromise) {
3708
- await diskCacheLoadPromise;
3709
- return;
3710
- }
3711
- await loadDiskCache2();
4052
+ if (diskCacheLoaded) return cache3.size;
4053
+ if (!diskCacheLoadPromise) {
4054
+ diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
4055
+ });
4056
+ }
4057
+ await diskCacheLoadPromise;
4058
+ return cache3.size;
4059
+ }
4060
+ function getApiEmbeddingDiskCacheLoadCountForTests() {
4061
+ return diskCacheLoadCount;
4062
+ }
4063
+ function resetApiEmbeddingCacheForTests() {
4064
+ cache3.clear();
4065
+ cachePayloadBytes = 0;
4066
+ cacheDimensions = null;
4067
+ diskCacheEntryCount = 0;
4068
+ diskCacheLoaded = false;
4069
+ diskCacheLoadPromise = null;
4070
+ diskCacheLoadCount = 0;
4071
+ diskCacheDirty2 = false;
4072
+ jsonlLineCountCalls = 0;
4073
+ }
4074
+ function seedApiEmbeddingCacheForTests(entries) {
4075
+ cache3.clear();
4076
+ cachePayloadBytes = 0;
4077
+ cacheDimensions = null;
4078
+ for (const [key, value] of entries) cacheInsert(key, value, true);
4079
+ diskCacheLoaded = true;
4080
+ diskCacheDirty2 = true;
4081
+ }
4082
+ function getApiEmbeddingCachePayloadBytesForTests() {
4083
+ return cachePayloadBytes;
4084
+ }
4085
+ function getApiEmbeddingJsonlLineCountCallsForTests() {
4086
+ return jsonlLineCountCalls;
4087
+ }
4088
+ function getApiEmbeddingMaxEntriesForDimensionsForTests(dimensions) {
4089
+ return maxEntriesForDimensions(dimensions);
3712
4090
  }
3713
4091
  function dimsCacheKey(config) {
3714
4092
  return [
@@ -3824,8 +4202,46 @@ async function saveDiskCacheNow() {
3824
4202
  if (!diskCacheDirty2) return;
3825
4203
  try {
3826
4204
  await mkdir2(cacheDir(), { recursive: true });
3827
- const entries = Array.from(cache3.entries());
3828
- await writeFile2(cacheFile(), JSON.stringify(entries));
4205
+ const jsonlPath = cacheJsonlFile();
4206
+ const existing = await resolveOnDiskCount(jsonlPath);
4207
+ if (existing > 0 && cache3.size < Math.ceil(existing * 0.5)) {
4208
+ console.error(
4209
+ `[memorix] Refusing to overwrite embedding cache (${existing} on disk vs ${cache3.size} in memory)`
4210
+ );
4211
+ diskCacheDirty2 = false;
4212
+ return;
4213
+ }
4214
+ const tmpPath = `${jsonlPath}.tmp`;
4215
+ await new Promise((resolve, reject) => {
4216
+ const out = createWriteStream(tmpPath);
4217
+ const entries = cache3.entries();
4218
+ const writeBatch = () => {
4219
+ for (let i2 = 0; i2 < 50; i2++) {
4220
+ const next = entries.next();
4221
+ if (next.done) {
4222
+ out.end();
4223
+ return;
4224
+ }
4225
+ const [hash4, vector] = next.value;
4226
+ if (!out.write(`${JSON.stringify({ h: hash4, v: vector })}
4227
+ `)) {
4228
+ out.once("drain", writeBatch);
4229
+ return;
4230
+ }
4231
+ }
4232
+ setImmediate(writeBatch);
4233
+ };
4234
+ out.on("finish", resolve);
4235
+ out.on("error", reject);
4236
+ writeBatch();
4237
+ });
4238
+ await rename(tmpPath, jsonlPath);
4239
+ diskCacheEntryCount = cache3.size;
4240
+ await writeCacheStat({
4241
+ version: 1,
4242
+ entryCount: cache3.size,
4243
+ dimensions: cacheDimensions
4244
+ });
3829
4245
  diskCacheDirty2 = false;
3830
4246
  } catch {
3831
4247
  }
@@ -3839,12 +4255,7 @@ function scheduleDiskSave() {
3839
4255
  }, DISK_SAVE_DEBOUNCE_MS);
3840
4256
  }
3841
4257
  function cacheSet(hash4, value) {
3842
- if (cache3.size >= MAX_CACHE_SIZE3) {
3843
- const firstKey = cache3.keys().next().value;
3844
- if (firstKey !== void 0) cache3.delete(firstKey);
3845
- }
3846
- cache3.set(hash4, value);
3847
- diskCacheDirty2 = true;
4258
+ cacheInsert(hash4, value, true);
3848
4259
  }
3849
4260
  function resolveEnvEmbeddingApiKey() {
3850
4261
  return process.env.MEMORIX_EMBEDDING_API_KEY;
@@ -3963,18 +4374,27 @@ async function waitForRetry(waitMs, signal) {
3963
4374
  abortSignal.addEventListener("abort", onAbort, { once: true });
3964
4375
  });
3965
4376
  }
3966
- 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;
4377
+ 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;
3967
4378
  var init_api_provider = __esm({
3968
4379
  "src/embedding/api-provider.ts"() {
3969
4380
  "use strict";
3970
4381
  init_esm_shims();
3971
4382
  init_provider2();
3972
4383
  cache3 = /* @__PURE__ */ new Map();
3973
- MAX_CACHE_SIZE3 = 1e4;
4384
+ MAX_CACHE_ENTRIES = 2e5;
4385
+ MAX_CACHE_PAYLOAD_BYTES = 256 * 1024 * 1024;
4386
+ BYTES_PER_DIM = 8;
4387
+ DEFAULT_ASSUMED_DIMENSIONS = 4096;
4388
+ MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
4389
+ cachePayloadBytes = 0;
4390
+ cacheDimensions = null;
4391
+ diskCacheEntryCount = 0;
3974
4392
  diskCacheDirty2 = false;
3975
4393
  diskSaveTimer = null;
3976
4394
  diskCacheLoaded = false;
3977
4395
  diskCacheLoadPromise = null;
4396
+ diskCacheLoadCount = 0;
4397
+ jsonlLineCountCalls = 0;
3978
4398
  MAX_INPUT_CHARS = 32e3;
3979
4399
  MAX_CONCURRENCY = 4;
3980
4400
  DISK_SAVE_DEBOUNCE_MS = 5e3;
@@ -4032,7 +4452,9 @@ var init_api_provider = __esm({
4032
4452
  if (config.requestedDimensions) {
4033
4453
  console.error(`[memorix] Dimension shortening: ${config.requestedDimensions}d requested`);
4034
4454
  }
4035
- startDiskCacheLoad();
4455
+ if (allowNetworkProbe) {
4456
+ startDiskCacheLoad();
4457
+ }
4036
4458
  return new _APIEmbeddingProvider(config, probeDimensions);
4037
4459
  }
4038
4460
  static resolveConfig() {
@@ -4308,6 +4730,7 @@ __export(provider_exports2, {
4308
4730
  MAX_INLINE_EMBEDDING_BYTES: () => MAX_INLINE_EMBEDDING_BYTES,
4309
4731
  UnsupportedEmbeddingModalityError: () => UnsupportedEmbeddingModalityError,
4310
4732
  getEmbeddingProvider: () => getEmbeddingProvider,
4733
+ getEmbeddingRuntimeHealth: () => getEmbeddingRuntimeHealth,
4311
4734
  isEmbeddingExplicitlyDisabled: () => isEmbeddingExplicitlyDisabled,
4312
4735
  isVectorSearchAvailable: () => isVectorSearchAvailable,
4313
4736
  resetProvider: () => resetProvider,
@@ -4369,7 +4792,12 @@ function warnOnce(message) {
4369
4792
  lastEmbeddingWarningAt = now3;
4370
4793
  console.error(message);
4371
4794
  }
4795
+ function embeddingModeToken() {
4796
+ return process.env.MEMORIX_EMBEDDING?.toLowerCase()?.trim() ?? "";
4797
+ }
4372
4798
  function getEmbeddingMode2() {
4799
+ const token = embeddingModeToken();
4800
+ if (cachedEmbeddingMode && cachedEmbeddingModeToken === token) return cachedEmbeddingMode;
4373
4801
  try {
4374
4802
  let cfg;
4375
4803
  try {
@@ -4378,10 +4806,17 @@ function getEmbeddingMode2() {
4378
4806
  cfg = (init_config(), __toCommonJS(config_exports));
4379
4807
  }
4380
4808
  const { getEmbeddingMode: cfgMode } = cfg;
4381
- return cfgMode();
4809
+ cachedEmbeddingMode = cfgMode();
4810
+ cachedEmbeddingModeToken = token;
4811
+ return cachedEmbeddingMode;
4382
4812
  } catch {
4383
- const env2 = process.env.MEMORIX_EMBEDDING?.toLowerCase()?.trim();
4384
- if (env2 === "fastembed" || env2 === "transformers" || env2 === "api" || env2 === "auto") return env2;
4813
+ if (token === "fastembed" || token === "transformers" || token === "api" || token === "auto") {
4814
+ cachedEmbeddingMode = token;
4815
+ cachedEmbeddingModeToken = token;
4816
+ return token;
4817
+ }
4818
+ cachedEmbeddingMode = "off";
4819
+ cachedEmbeddingModeToken = token;
4385
4820
  return "off";
4386
4821
  }
4387
4822
  }
@@ -4415,13 +4850,36 @@ function isTemporaryEmbeddingFailure(error) {
4415
4850
  if (!(error instanceof Error)) return false;
4416
4851
  return /embedding api error \(401\)/i.test(error.message) || /invalid_api_key/i.test(error.message) || /incorrect api key/i.test(error.message) || /unauthorized/i.test(error.message) || /embedding api timeout/i.test(error.message) || /embedding api error \((402|429|5\d\d)\)/i.test(error.message) || /quota exceeded/i.test(error.message) || /account balance/i.test(error.message) || /fetch failed/i.test(error.message) || /econnreset/i.test(error.message) || /econnrefused/i.test(error.message) || /temporarily unavailable/i.test(error.message);
4417
4852
  }
4853
+ function classifyEmbeddingFailure(error) {
4854
+ const message = error instanceof Error ? error.message : String(error);
4855
+ if (/embedding api error \(401\)|invalid_api_key|incorrect api key|unauthorized/i.test(message)) {
4856
+ return "authorization";
4857
+ }
4858
+ if (/embedding api error \(402\)|quota exceeded|account balance/i.test(message)) {
4859
+ return "billing";
4860
+ }
4861
+ if (/embedding api timeout|abort/i.test(message)) {
4862
+ return "timeout";
4863
+ }
4864
+ return "upstream";
4865
+ }
4418
4866
  function markTemporaryFailure(reason) {
4419
4867
  provider = null;
4420
4868
  initPromise = null;
4421
4869
  lastInitWasTemporaryFailure = true;
4422
4870
  lastFailureTimestamp = Date.now();
4871
+ lastEmbeddingFailure = {
4872
+ kind: classifyEmbeddingFailure(reason),
4873
+ occurredAt: lastFailureTimestamp
4874
+ };
4423
4875
  const message = reason instanceof Error ? reason.message : String(reason);
4424
- warnOnce(`[memorix] Embedding provider temporarily unavailable at runtime: ${message}`);
4876
+ warnOnce(`[memorix] Embedding provider unavailable at runtime: ${message}`);
4877
+ }
4878
+ function markProviderReady(candidate) {
4879
+ provider = wrapProvider(candidate);
4880
+ lastInitWasTemporaryFailure = false;
4881
+ lastEmbeddingFailure = null;
4882
+ return provider;
4425
4883
  }
4426
4884
  async function createFastEmbedProvider() {
4427
4885
  try {
@@ -4537,35 +4995,35 @@ async function getEmbeddingProvider(options = {}) {
4537
4995
  if (mode === "fastembed") {
4538
4996
  const initialized = await createFastEmbedProvider();
4539
4997
  if (!initialized) return null;
4540
- provider = wrapProvider(initialized);
4998
+ provider = markProviderReady(initialized);
4541
4999
  warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
4542
5000
  return provider;
4543
5001
  }
4544
5002
  if (mode === "transformers") {
4545
5003
  const initialized = await createTransformersProvider();
4546
5004
  if (!initialized) return null;
4547
- provider = wrapProvider(initialized);
5005
+ provider = markProviderReady(initialized);
4548
5006
  warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
4549
5007
  return provider;
4550
5008
  }
4551
5009
  if (mode === "api") {
4552
5010
  const initialized = await createAPIProvider(options);
4553
5011
  if (!initialized) return null;
4554
- provider = wrapProvider(initialized);
5012
+ provider = markProviderReady(initialized);
4555
5013
  warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
4556
5014
  return provider;
4557
5015
  }
4558
5016
  if (hasAPIEmbeddingConfig()) {
4559
5017
  const initialized = await createAPIProvider(options);
4560
5018
  if (initialized) {
4561
- provider = wrapProvider(initialized);
5019
+ provider = markProviderReady(initialized);
4562
5020
  warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
4563
5021
  return provider;
4564
5022
  }
4565
5023
  }
4566
5024
  const localFallback = await createLocalFallbackProvider();
4567
5025
  if (localFallback) {
4568
- provider = wrapProvider(localFallback);
5026
+ provider = markProviderReady(localFallback);
4569
5027
  warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
4570
5028
  return provider;
4571
5029
  }
@@ -4587,18 +5045,42 @@ async function isVectorSearchAvailable() {
4587
5045
  const p = await getEmbeddingProvider();
4588
5046
  return p !== null;
4589
5047
  }
5048
+ function getEmbeddingRuntimeHealth() {
5049
+ const mode = getEmbeddingMode2();
5050
+ if (mode === "off") return { status: "disabled" };
5051
+ if (lastInitWasTemporaryFailure && lastEmbeddingFailure) {
5052
+ return {
5053
+ status: "degraded",
5054
+ failure: {
5055
+ kind: lastEmbeddingFailure.kind,
5056
+ occurredAt: new Date(lastEmbeddingFailure.occurredAt).toISOString(),
5057
+ retryAfterMs: Math.max(0, RETRY_COOLDOWN_MS - (Date.now() - lastEmbeddingFailure.occurredAt))
5058
+ }
5059
+ };
5060
+ }
5061
+ if (provider) {
5062
+ return {
5063
+ status: "ready",
5064
+ provider: { name: provider.name, dimensions: provider.dimensions }
5065
+ };
5066
+ }
5067
+ return { status: "uninitialized" };
5068
+ }
4590
5069
  function isEmbeddingExplicitlyDisabled() {
4591
5070
  return getEmbeddingMode2() === "off";
4592
5071
  }
4593
5072
  function resetProvider() {
4594
5073
  provider = null;
4595
5074
  initPromise = null;
5075
+ cachedEmbeddingMode = null;
5076
+ cachedEmbeddingModeToken = null;
4596
5077
  lastInitWasTemporaryFailure = false;
4597
5078
  lastFailureTimestamp = 0;
5079
+ lastEmbeddingFailure = null;
4598
5080
  lastEmbeddingWarning = "";
4599
5081
  lastEmbeddingWarningAt = 0;
4600
5082
  }
4601
- var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, lastInitWasTemporaryFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
5083
+ var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, cachedEmbeddingMode, cachedEmbeddingModeToken, lastInitWasTemporaryFailure, lastEmbeddingFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
4602
5084
  var init_provider2 = __esm({
4603
5085
  "src/embedding/provider.ts"() {
4604
5086
  "use strict";
@@ -4622,7 +5104,10 @@ var init_provider2 = __esm({
4622
5104
  MAX_INLINE_EMBEDDING_BYTES = 5 * 1024 * 1024;
4623
5105
  provider = null;
4624
5106
  initPromise = null;
5107
+ cachedEmbeddingMode = null;
5108
+ cachedEmbeddingModeToken = null;
4625
5109
  lastInitWasTemporaryFailure = false;
5110
+ lastEmbeddingFailure = null;
4626
5111
  lastEmbeddingWarning = "";
4627
5112
  lastEmbeddingWarningAt = 0;
4628
5113
  WARNING_COOLDOWN_MS = 3e4;
@@ -5762,6 +6247,9 @@ async function attachCachedVectors(database, candidates) {
5762
6247
  await update(database, candidates[index].id, { ...existing, embedding: vector });
5763
6248
  } catch {
5764
6249
  }
6250
+ if (index % 50 === 0) {
6251
+ await new Promise((resolve) => setImmediate(resolve));
6252
+ }
5765
6253
  }
5766
6254
  }
5767
6255
  async function hydrateIndex(observations2, options = {}) {
@@ -5812,17 +6300,24 @@ async function hydrateIndex(observations2, options = {}) {
5812
6300
  await insert2(database, doc);
5813
6301
  rememberObservationDoc(doc);
5814
6302
  inserted++;
6303
+ if (inserted % 50 === 0) {
6304
+ await new Promise((resolve) => setImmediate(resolve));
6305
+ }
6306
+ if (inserted % 200 === 0) {
6307
+ await new Promise((resolve) => setTimeout(resolve, 0));
6308
+ }
5815
6309
  } catch {
5816
6310
  }
5817
6311
  }
5818
- deferredCachedVectorHydration = deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
6312
+ deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
5819
6313
  }) : null;
5820
6314
  return inserted;
5821
6315
  }
5822
- async function hydrateIndexForStartup(observations2) {
6316
+ async function hydrateIndexForStartup(observations2, options = {}) {
5823
6317
  return hydrateIndex(observations2, {
5824
6318
  allowNetworkProbe: false,
5825
- deferCachedVectors: true
6319
+ deferCachedVectors: true,
6320
+ skipCachedVectors: options.skipCachedVectors
5826
6321
  });
5827
6322
  }
5828
6323
  function getDeferredCachedVectorHydration() {
@@ -8484,7 +8979,7 @@ var init_types2 = __esm({
8484
8979
 
8485
8980
  // src/media/asset-store.ts
8486
8981
  import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
8487
- import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename, rm, stat, writeFile as writeFile3 } from "fs/promises";
8982
+ import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
8488
8983
  import path10 from "path";
8489
8984
  function mediaRoot(dataDir) {
8490
8985
  return path10.resolve(dataDir, "media");
@@ -8557,7 +9052,7 @@ async function persistBuffer(bytes, destination) {
8557
9052
  const temporary = `${destination}.${randomUUID5()}.tmp`;
8558
9053
  try {
8559
9054
  await writeFile3(temporary, bytes, { flag: "wx" });
8560
- await rename(temporary, destination);
9055
+ await rename2(temporary, destination);
8561
9056
  return true;
8562
9057
  } catch (error) {
8563
9058
  if (error?.code === "EEXIST" && await destinationExists(destination)) return false;
@@ -59637,9 +60132,9 @@ var require_node_domexception = __commonJS({
59637
60132
  });
59638
60133
 
59639
60134
  // node_modules/fetch-blob/from.js
59640
- import { statSync as statSync2, createReadStream, promises as fs7 } from "fs";
60135
+ import { statSync as statSync2, createReadStream as createReadStream2, promises as fs7 } from "fs";
59641
60136
  import { basename } from "path";
59642
- var import_node_domexception, stat2, blobFromSync, blobFrom, fileFrom, fileFromSync, fromBlob, fromFile, BlobDataItem;
60137
+ var import_node_domexception, stat3, blobFromSync, blobFrom, fileFrom, fileFromSync, fromBlob, fromFile, BlobDataItem;
59643
60138
  var init_from = __esm({
59644
60139
  "node_modules/fetch-blob/from.js"() {
59645
60140
  "use strict";
@@ -59647,23 +60142,23 @@ var init_from = __esm({
59647
60142
  import_node_domexception = __toESM(require_node_domexception(), 1);
59648
60143
  init_file();
59649
60144
  init_fetch_blob();
59650
- ({ stat: stat2 } = fs7);
60145
+ ({ stat: stat3 } = fs7);
59651
60146
  blobFromSync = (path21, type) => fromBlob(statSync2(path21), path21, type);
59652
- blobFrom = (path21, type) => stat2(path21).then((stat4) => fromBlob(stat4, path21, type));
59653
- fileFrom = (path21, type) => stat2(path21).then((stat4) => fromFile(stat4, path21, type));
60147
+ blobFrom = (path21, type) => stat3(path21).then((stat5) => fromBlob(stat5, path21, type));
60148
+ fileFrom = (path21, type) => stat3(path21).then((stat5) => fromFile(stat5, path21, type));
59654
60149
  fileFromSync = (path21, type) => fromFile(statSync2(path21), path21, type);
59655
- fromBlob = (stat4, path21, type = "") => new fetch_blob_default([new BlobDataItem({
60150
+ fromBlob = (stat5, path21, type = "") => new fetch_blob_default([new BlobDataItem({
59656
60151
  path: path21,
59657
- size: stat4.size,
59658
- lastModified: stat4.mtimeMs,
60152
+ size: stat5.size,
60153
+ lastModified: stat5.mtimeMs,
59659
60154
  start: 0
59660
60155
  })], { type });
59661
- fromFile = (stat4, path21, type = "") => new file_default([new BlobDataItem({
60156
+ fromFile = (stat5, path21, type = "") => new file_default([new BlobDataItem({
59662
60157
  path: path21,
59663
- size: stat4.size,
59664
- lastModified: stat4.mtimeMs,
60158
+ size: stat5.size,
60159
+ lastModified: stat5.mtimeMs,
59665
60160
  start: 0
59666
- })], basename(path21), { type, lastModified: stat4.mtimeMs });
60161
+ })], basename(path21), { type, lastModified: stat5.mtimeMs });
59667
60162
  BlobDataItem = class _BlobDataItem {
59668
60163
  #path;
59669
60164
  #start;
@@ -59686,11 +60181,11 @@ var init_from = __esm({
59686
60181
  });
59687
60182
  }
59688
60183
  async *stream() {
59689
- const { mtimeMs } = await stat2(this.#path);
60184
+ const { mtimeMs } = await stat3(this.#path);
59690
60185
  if (mtimeMs > this.lastModified) {
59691
60186
  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");
59692
60187
  }
59693
- yield* createReadStream(this.#path, {
60188
+ yield* createReadStream2(this.#path, {
59694
60189
  start: this.#start,
59695
60190
  end: this.#start + this.size - 1
59696
60191
  });
@@ -75320,7 +75815,7 @@ var init_wrapper = __esm({
75320
75815
  });
75321
75816
 
75322
75817
  // node_modules/@google/genai/dist/node/index.mjs
75323
- import { createWriteStream } from "fs";
75818
+ import { createWriteStream as createWriteStream2 } from "fs";
75324
75819
  import * as fs8 from "fs/promises";
75325
75820
  import { writeFile as writeFile4 } from "fs/promises";
75326
75821
  import { Readable } from "stream";
@@ -92600,7 +93095,7 @@ ${underline}`);
92600
93095
  if (params.downloadPath) {
92601
93096
  const response = await downloadFile(params, apiClient);
92602
93097
  if (response instanceof HttpResponse) {
92603
- const writer = createWriteStream(params.downloadPath);
93098
+ const writer = createWriteStream2(params.downloadPath);
92604
93099
  const body = Readable.fromWeb(response.responseInternal.body);
92605
93100
  body.pipe(writer);
92606
93101
  await finished(writer);
@@ -142518,7 +143013,7 @@ function explainRetention(doc, referenceTime) {
142518
143013
  };
142519
143014
  }
142520
143015
  function toRetentionDocument(obs, accessMap) {
142521
- const access = accessMap?.get(obs.id);
143016
+ const access2 = accessMap?.get(obs.id);
142522
143017
  return {
142523
143018
  id: `obs-${obs.id}`,
142524
143019
  observationId: obs.id,
@@ -142532,8 +143027,8 @@ function toRetentionDocument(obs, accessMap) {
142532
143027
  tokens: obs.tokens,
142533
143028
  createdAt: obs.createdAt,
142534
143029
  projectId: obs.projectId,
142535
- accessCount: access?.accessCount ?? 0,
142536
- lastAccessedAt: access?.lastAccessedAt ?? "",
143030
+ accessCount: access2?.accessCount ?? 0,
143031
+ lastAccessedAt: access2?.lastAccessedAt ?? "",
142537
143032
  status: obs.status ?? "active",
142538
143033
  source: obs.source ?? "agent",
142539
143034
  sourceDetail: obs.sourceDetail ?? "",
@@ -144643,7 +145138,7 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
144643
145138
  const rel = normalizeCodePath(relative(projectRoot, abs));
144644
145139
  try {
144645
145140
  const text2 = readFileSync6(abs, "utf-8");
144646
- const stat4 = fileStat ?? statSync3(abs);
145141
+ const stat5 = fileStat ?? statSync3(abs);
144647
145142
  const file = {
144648
145143
  id: makeCodeFileId(projectId, rel),
144649
145144
  projectId,
@@ -144652,8 +145147,8 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
144652
145147
  contentHash: hashText(text2),
144653
145148
  // Preserve filesystem precision. Rounding here made same-size edits in
144654
145149
  // a short Windows timestamp window indistinguishable to refresh.
144655
- mtimeMs: Number(stat4.mtimeMs),
144656
- sizeBytes: Number(stat4.size),
145150
+ mtimeMs: Number(stat5.mtimeMs),
145151
+ sizeBytes: Number(stat5.size),
144657
145152
  indexedAt
144658
145153
  };
144659
145154
  return {
@@ -144680,18 +145175,18 @@ async function indexProjectLite(options) {
144680
145175
  let skippedOversizedFiles = 0;
144681
145176
  let unreadableFiles = 0;
144682
145177
  for (const abs of paths) {
144683
- let stat4;
145178
+ let stat5;
144684
145179
  try {
144685
- stat4 = statSync3(abs);
145180
+ stat5 = statSync3(abs);
144686
145181
  } catch {
144687
145182
  unreadableFiles++;
144688
145183
  continue;
144689
145184
  }
144690
- if (Number(stat4.size) > maxFileBytes) {
145185
+ if (Number(stat5.size) > maxFileBytes) {
144691
145186
  skippedOversizedFiles++;
144692
145187
  continue;
144693
145188
  }
144694
- const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat4);
145189
+ const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
144695
145190
  if (!attempt.delta) {
144696
145191
  if (attempt.unreadable) unreadableFiles++;
144697
145192
  continue;
@@ -144719,16 +145214,16 @@ async function refreshProjectLite(store, options) {
144719
145214
  for (const abs of paths) {
144720
145215
  const rel = normalizeCodePath(relative(options.projectRoot, abs));
144721
145216
  seenPaths.add(rel);
144722
- let stat4;
145217
+ let stat5;
144723
145218
  try {
144724
- stat4 = statSync3(abs);
145219
+ stat5 = statSync3(abs);
144725
145220
  } catch {
144726
145221
  unreadableFiles++;
144727
145222
  continue;
144728
145223
  }
144729
145224
  const existing = existingByPath.get(rel);
144730
- const mtimeMs = Number(stat4.mtimeMs);
144731
- const sizeBytes = Number(stat4.size);
145225
+ const mtimeMs = Number(stat5.mtimeMs);
145226
+ const sizeBytes = Number(stat5.size);
144732
145227
  if (sizeBytes > maxFileBytes) {
144733
145228
  skippedOversizedFiles++;
144734
145229
  if (existing) oversizedExistingFileIds.add(existing.id);
@@ -144738,7 +145233,7 @@ async function refreshProjectLite(store, options) {
144738
145233
  unchangedFiles++;
144739
145234
  continue;
144740
145235
  }
144741
- const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat4);
145236
+ const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
144742
145237
  if (!attempt.delta) {
144743
145238
  if (attempt.unreadable) unreadableFiles++;
144744
145239
  continue;
@@ -148135,12 +148630,25 @@ __export(observations_exports, {
148135
148630
  prepareSearchIndex: () => prepareSearchIndex,
148136
148631
  probeSearchIndex: () => probeSearchIndex,
148137
148632
  reindexObservations: () => reindexObservations,
148633
+ resetObservationRuntime: () => resetObservationRuntime,
148138
148634
  resolveObservations: () => resolveObservations,
148139
148635
  storeObservation: () => storeObservation,
148140
148636
  suggestTopicKey: () => suggestTopicKey,
148141
148637
  updateObservationAdmission: () => updateObservationAdmission,
148142
148638
  withFreshObservations: () => withFreshObservations
148143
148639
  });
148640
+ function resetObservationRuntime() {
148641
+ observations = [];
148642
+ nextId = 1;
148643
+ projectDir = null;
148644
+ searchIndexPrepared = false;
148645
+ corpusLoaded = false;
148646
+ loadedObservationStore = null;
148647
+ embeddingWriteMode = "background";
148648
+ embeddingWorkerProjectRoot = void 0;
148649
+ vectorMissingIds.clear();
148650
+ vectorSchemaUpgradePromise = null;
148651
+ }
148144
148652
  function logEmbeddingFailureOnce(key, message) {
148145
148653
  const now3 = Date.now();
148146
148654
  const last = embeddingFailureLogTimestamps.get(key) ?? 0;
@@ -148249,23 +148757,33 @@ async function upgradeVectorSchemaAfterFirstEmbedding(embedding) {
148249
148757
  async function initObservations(dir, options = {}) {
148250
148758
  embeddingWriteMode = options.embeddingWriteMode ?? "background";
148251
148759
  embeddingWorkerProjectRoot = options.projectRoot;
148252
- if (projectDir === dir) return;
148253
148760
  await initObservationStore(dir);
148254
148761
  const store = getObservationStore();
148255
- observations = await store.loadAll();
148762
+ if (projectDir === dir && loadedObservationStore === store) {
148763
+ if (options.skipCorpusLoad || corpusLoaded) return;
148764
+ }
148256
148765
  nextId = await store.loadIdCounter();
148257
148766
  projectDir = dir;
148258
148767
  searchIndexPrepared = false;
148259
148768
  vectorSchemaUpgradePromise = null;
148769
+ if (options.skipCorpusLoad) {
148770
+ observations = [];
148771
+ corpusLoaded = false;
148772
+ loadedObservationStore = store;
148773
+ return;
148774
+ }
148775
+ observations = await store.loadAll();
148776
+ corpusLoaded = true;
148777
+ loadedObservationStore = store;
148260
148778
  }
148261
148779
  function scheduleObservationEmbedding(input) {
148262
148780
  const { observationId: observationId2, projectId, searchableText, doc } = input;
148263
148781
  vectorMissingIds.add(observationId2);
148782
+ if (isEmbeddingExplicitlyDisabled()) {
148783
+ vectorMissingIds.delete(observationId2);
148784
+ return;
148785
+ }
148264
148786
  if (embeddingWriteMode === "deferred") {
148265
- if (isEmbeddingExplicitlyDisabled()) {
148266
- vectorMissingIds.delete(observationId2);
148267
- return;
148268
- }
148269
148787
  queueVectorBackfill(projectId, {
148270
148788
  detachedWorker: true,
148271
148789
  projectRoot: embeddingWorkerProjectRoot
@@ -148312,7 +148830,7 @@ function scheduleObservationEmbedding(input) {
148312
148830
  });
148313
148831
  }
148314
148832
  async function ensureFreshObservations() {
148315
- if (!projectDir) return false;
148833
+ if (!projectDir || !corpusLoaded) return false;
148316
148834
  try {
148317
148835
  const store = getObservationStore();
148318
148836
  const wasStale = await store.ensureFresh();
@@ -148353,7 +148871,7 @@ async function storeObservation(input) {
148353
148871
  });
148354
148872
  if (safeAttachments) input = { ...input, attachments: safeAttachments };
148355
148873
  await ensureFreshObservations();
148356
- if (input.topicKey) {
148874
+ if (input.topicKey && corpusLoaded) {
148357
148875
  const existing = observations.find(
148358
148876
  (o) => o.topicKey === input.topicKey && o.projectId === input.projectId
148359
148877
  );
@@ -148450,13 +148968,17 @@ async function storeObservation(input) {
148450
148968
  });
148451
148969
  observation.writeGeneration = store.getGeneration();
148452
148970
  if (upsertedInsideLock || reloadCacheAfterCommit) {
148453
- observations = await store.loadAll();
148454
- nextId = await store.loadIdCounter();
148455
- if (upsertedInsideLock) {
148456
- observation = observations.find((candidate) => candidate.id === observation.id) ?? observation;
148971
+ if (corpusLoaded) {
148972
+ observations = await store.loadAll();
148973
+ nextId = await store.loadIdCounter();
148974
+ if (upsertedInsideLock) {
148975
+ observation = observations.find((candidate) => candidate.id === observation.id) ?? observation;
148976
+ }
148977
+ } else {
148978
+ nextId = await store.loadIdCounter();
148457
148979
  }
148458
148980
  } else {
148459
- observations.push(observation);
148981
+ if (corpusLoaded) observations.push(observation);
148460
148982
  nextId = observation.id + 1;
148461
148983
  }
148462
148984
  if (upsertedInsideLock) return;
@@ -148524,7 +149046,9 @@ async function storeObservation(input) {
148524
149046
  createdByAgentId: input.createdByAgentId ?? "",
148525
149047
  sharedWithAgentIds: JSON.stringify(input.sharedWithAgentIds ?? [])
148526
149048
  };
148527
- await insertObservation(doc);
149049
+ if (corpusLoaded) {
149050
+ await insertObservation(doc);
149051
+ }
148528
149052
  };
148529
149053
  await assignAndPersist();
148530
149054
  if (upsertedInsideLock) {
@@ -148886,9 +149410,11 @@ async function reindexObservations() {
148886
149410
  searchIndexPrepared = true;
148887
149411
  return count2;
148888
149412
  }
148889
- async function prepareSearchIndex() {
149413
+ async function prepareSearchIndex(options = {}) {
148890
149414
  if (searchIndexPrepared) return 0;
148891
- const count2 = await hydrateIndexForStartup(observations);
149415
+ const count2 = await hydrateIndexForStartup(observations, {
149416
+ skipCachedVectors: options.skipCachedVectors
149417
+ });
148892
149418
  if (count2 === 0) {
148893
149419
  searchIndexPrepared = true;
148894
149420
  return 0;
@@ -149080,7 +149606,7 @@ async function backfillVectorEmbeddings(options = {}) {
149080
149606
  ...lastFailure ? { lastError: lastFailure } : {}
149081
149607
  };
149082
149608
  }
149083
- var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
149609
+ var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
149084
149610
  var init_observations = __esm({
149085
149611
  "src/memory/observations.ts"() {
149086
149612
  "use strict";
@@ -149099,6 +149625,8 @@ var init_observations = __esm({
149099
149625
  projectDir = null;
149100
149626
  searchIndexPrepared = false;
149101
149627
  embeddingWriteMode = "background";
149628
+ corpusLoaded = false;
149629
+ loadedObservationStore = null;
149102
149630
  vectorMissingIds = /* @__PURE__ */ new Set();
149103
149631
  vectorBackfillRunning = false;
149104
149632
  vectorSchemaUpgradePromise = null;