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.
- package/CHANGELOG.md +22 -0
- package/TEAM.md +86 -86
- package/dist/cli/index.js +1574 -1063
- package/dist/index.js +588 -107
- package/dist/maintenance-runner.js +565 -84
- package/dist/media-audio-runner.js +565 -84
- package/dist/media-video-runner.js +565 -84
- package/dist/memcode-runtime/CHANGELOG.md +22 -0
- package/dist/sdk.js +621 -111
- package/dist/vector-backfill-runner.js +565 -84
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/PERFORMANCE.md +15 -0
- package/package.json +2 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/audit/index.ts +156 -156
- package/src/cli/commands/background.ts +34 -2
- package/src/cli/commands/formation.ts +48 -48
- package/src/cli/commands/git-hook-install.ts +111 -111
- package/src/cli/commands/handoff.ts +54 -54
- package/src/cli/commands/hooks-status.ts +63 -63
- package/src/cli/commands/ingest.ts +44 -44
- package/src/cli/commands/integrate-shared.ts +15 -15
- package/src/cli/commands/lock.ts +82 -82
- package/src/cli/commands/message.ts +104 -104
- package/src/cli/commands/operator-shared.ts +6 -3
- package/src/cli/commands/poll.ts +58 -58
- package/src/cli/commands/reasoning.ts +118 -118
- package/src/cli/commands/serve-http.ts +4 -0
- package/src/cli/commands/skills.ts +114 -114
- package/src/cli/commands/task.ts +167 -167
- package/src/cli/commands/transfer.ts +47 -47
- package/src/cli/index.ts +406 -406
- package/src/cli/tui/ChatView.tsx +234 -234
- package/src/cli/tui/CommandBar.tsx +312 -312
- package/src/cli/tui/ContextRail.tsx +118 -118
- package/src/cli/tui/HeaderBar.tsx +72 -72
- package/src/cli/tui/LogoBanner.tsx +51 -51
- package/src/cli/tui/index.ts +41 -41
- package/src/cli/version.ts +7 -7
- package/src/cli/workbench.ts +1 -1
- package/src/dashboard/project-classification.ts +64 -64
- package/src/embedding/api-provider.ts +503 -27
- package/src/embedding/provider.ts +20 -3
- package/src/git/hooks-path.ts +85 -85
- package/src/hooks/lifecycle.ts +1 -0
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/significance-filter.ts +250 -250
- package/src/llm/memory-manager.ts +328 -328
- package/src/llm/quality.ts +248 -248
- package/src/memory/attribution-guard.ts +249 -249
- package/src/memory/disclosure-policy.ts +135 -135
- package/src/memory/entity-extractor.ts +197 -197
- package/src/memory/formation/evaluate.ts +217 -217
- package/src/memory/freshness.ts +122 -122
- package/src/memory/graph.ts +197 -197
- package/src/memory/observations.ts +68 -18
- package/src/memory/refs.ts +94 -94
- package/src/memory/secret-filter.ts +79 -79
- package/src/orchestrate/adapters/claude-stream.ts +192 -192
- package/src/orchestrate/adapters/claude.ts +111 -111
- package/src/orchestrate/adapters/codex-stream.ts +134 -134
- package/src/orchestrate/adapters/codex.ts +41 -41
- package/src/orchestrate/adapters/gemini-stream.ts +166 -166
- package/src/orchestrate/adapters/gemini.ts +42 -42
- package/src/orchestrate/adapters/index.ts +73 -73
- package/src/orchestrate/adapters/opencode-stream.ts +143 -143
- package/src/orchestrate/adapters/opencode.ts +47 -47
- package/src/orchestrate/adapters/types.ts +77 -77
- package/src/orchestrate/capability-router.ts +284 -284
- package/src/orchestrate/cost-tracker.ts +219 -219
- package/src/orchestrate/error-recovery.ts +191 -191
- package/src/orchestrate/evidence.ts +140 -140
- package/src/orchestrate/ledger.ts +110 -110
- package/src/orchestrate/memorix-bridge.ts +343 -343
- package/src/orchestrate/output-budget.ts +80 -80
- package/src/orchestrate/permission.ts +152 -152
- package/src/orchestrate/pipeline-trace.ts +131 -131
- package/src/orchestrate/prompt-builder.ts +155 -155
- package/src/orchestrate/ring-buffer.ts +37 -37
- package/src/orchestrate/task-graph.ts +389 -389
- package/src/project/aliases.ts +374 -374
- package/src/rules/adapters/trae.ts +56 -56
- package/src/rules/syncer.ts +235 -235
- package/src/sdk.ts +12 -8
- package/src/search/query-expansion.ts +52 -52
- package/src/server/formation-timeout.ts +27 -27
- package/src/skills/mini-skills.ts +386 -386
- package/src/store/chat-store.ts +119 -119
- package/src/store/graph-store.ts +249 -249
- package/src/store/mini-skill-store.ts +349 -349
- package/src/store/orama-store.ts +21 -3
- package/src/store/persistence-json.ts +212 -212
- package/src/store/persistence.ts +291 -291
- package/src/store/project-affinity.ts +195 -195
- package/src/team/event-bus.ts +76 -76
- package/src/team/handoff.ts +161 -161
- package/src/team/poll.ts +132 -132
- package/src/workspace/mcp-adapters/codex.ts +191 -191
- package/src/workspace/mcp-adapters/copilot.ts +105 -105
- package/src/workspace/mcp-adapters/cursor.ts +53 -53
- package/src/workspace/mcp-adapters/kiro.ts +64 -64
- package/src/workspace/mcp-adapters/opencode.ts +123 -123
- package/src/workspace/mcp-adapters/trae.ts +134 -134
- package/src/workspace/mcp-adapters/windsurf.ts +91 -91
- package/src/workspace/sanitizer.ts +60 -60
|
@@ -1369,8 +1369,8 @@ async function acquireLock(lockPath) {
|
|
|
1369
1369
|
const code = err instanceof Error && "code" in err ? err.code : void 0;
|
|
1370
1370
|
if (code === "EEXIST" || code === "EPERM") {
|
|
1371
1371
|
try {
|
|
1372
|
-
const
|
|
1373
|
-
if (Date.now() -
|
|
1372
|
+
const stat5 = await fs2.stat(lockPath);
|
|
1373
|
+
if (Date.now() - stat5.mtimeMs > LOCK_STALE_MS) {
|
|
1374
1374
|
await fs2.unlink(lockPath).catch(() => {
|
|
1375
1375
|
});
|
|
1376
1376
|
continue;
|
|
@@ -2481,24 +2481,131 @@ var init_transformers_provider = __esm({
|
|
|
2481
2481
|
// src/embedding/api-provider.ts
|
|
2482
2482
|
var api_provider_exports = {};
|
|
2483
2483
|
__export(api_provider_exports, {
|
|
2484
|
-
APIEmbeddingProvider: () => APIEmbeddingProvider
|
|
2484
|
+
APIEmbeddingProvider: () => APIEmbeddingProvider,
|
|
2485
|
+
convertArrayCacheToJsonl: () => convertArrayCacheToJsonl,
|
|
2486
|
+
ensureDiskCacheLoaded: () => ensureDiskCacheLoaded,
|
|
2487
|
+
getApiEmbeddingCachePayloadBytesForTests: () => getApiEmbeddingCachePayloadBytesForTests,
|
|
2488
|
+
getApiEmbeddingDiskCacheLoadCountForTests: () => getApiEmbeddingDiskCacheLoadCountForTests,
|
|
2489
|
+
getApiEmbeddingJsonlLineCountCallsForTests: () => getApiEmbeddingJsonlLineCountCallsForTests,
|
|
2490
|
+
getApiEmbeddingMaxEntriesForDimensionsForTests: () => getApiEmbeddingMaxEntriesForDimensionsForTests,
|
|
2491
|
+
migrateJsonArrayCacheToJsonl: () => migrateJsonArrayCacheToJsonl,
|
|
2492
|
+
parseCacheJsonInWorker: () => parseCacheJsonInWorker,
|
|
2493
|
+
resetApiEmbeddingCacheForTests: () => resetApiEmbeddingCacheForTests,
|
|
2494
|
+
saveDiskCacheNow: () => saveDiskCacheNow,
|
|
2495
|
+
seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
|
|
2485
2496
|
});
|
|
2486
2497
|
import { createHash as createHash2 } from "crypto";
|
|
2487
|
-
import {
|
|
2498
|
+
import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
|
|
2499
|
+
import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
|
|
2488
2500
|
import { join as join3 } from "path";
|
|
2489
2501
|
import { homedir as homedir7 } from "os";
|
|
2502
|
+
import { createInterface } from "readline";
|
|
2490
2503
|
function cacheDir() {
|
|
2491
2504
|
return process.env.MEMORIX_DATA_DIR || join3(homedir7(), ".memorix", "data");
|
|
2492
2505
|
}
|
|
2493
2506
|
function cacheFile() {
|
|
2494
2507
|
return join3(cacheDir(), ".embedding-api-cache.json");
|
|
2495
2508
|
}
|
|
2509
|
+
function cacheJsonlFile() {
|
|
2510
|
+
return join3(cacheDir(), ".embedding-api-cache.jsonl");
|
|
2511
|
+
}
|
|
2496
2512
|
function dimsCacheFile() {
|
|
2497
2513
|
return join3(cacheDir(), ".embedding-dims-cache.json");
|
|
2498
2514
|
}
|
|
2499
2515
|
function cacheMetaFile() {
|
|
2500
2516
|
return join3(cacheDir(), ".embedding-api-cache-meta.json");
|
|
2501
2517
|
}
|
|
2518
|
+
function cacheStatFile() {
|
|
2519
|
+
return join3(cacheDir(), ".embedding-api-cache-stat.json");
|
|
2520
|
+
}
|
|
2521
|
+
function payloadBytes(vector) {
|
|
2522
|
+
return vector.length * BYTES_PER_DIM;
|
|
2523
|
+
}
|
|
2524
|
+
function maxCachePayloadBytes() {
|
|
2525
|
+
const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
|
|
2526
|
+
if (raw) {
|
|
2527
|
+
const parsed = Number.parseInt(raw, 10);
|
|
2528
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
2529
|
+
}
|
|
2530
|
+
return MAX_CACHE_PAYLOAD_BYTES;
|
|
2531
|
+
}
|
|
2532
|
+
function maxEntriesForDimensions(dimensions) {
|
|
2533
|
+
const dim = Math.max(1, Math.floor(dimensions));
|
|
2534
|
+
const byBytes = Math.floor(maxCachePayloadBytes() / (dim * BYTES_PER_DIM));
|
|
2535
|
+
return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
|
|
2536
|
+
}
|
|
2537
|
+
function rememberDimensions(vector) {
|
|
2538
|
+
if (vector.length > 0 && (cacheDimensions === null || vector.length > cacheDimensions)) {
|
|
2539
|
+
cacheDimensions = vector.length;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
function evictOldest() {
|
|
2543
|
+
const firstKey = cache3.keys().next().value;
|
|
2544
|
+
if (firstKey === void 0) return;
|
|
2545
|
+
const old = cache3.get(firstKey);
|
|
2546
|
+
cache3.delete(firstKey);
|
|
2547
|
+
if (old) cachePayloadBytes -= payloadBytes(old);
|
|
2548
|
+
}
|
|
2549
|
+
function evictToFit(incomingBytes, incomingDims) {
|
|
2550
|
+
const limit2 = maxEntriesForDimensions(incomingDims);
|
|
2551
|
+
while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes + incomingBytes > maxCachePayloadBytes())) {
|
|
2552
|
+
evictOldest();
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
function isNumberArray(value) {
|
|
2556
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "number");
|
|
2557
|
+
}
|
|
2558
|
+
function parseCacheRow(raw) {
|
|
2559
|
+
if (Array.isArray(raw)) {
|
|
2560
|
+
const hash4 = raw[0];
|
|
2561
|
+
const vector = raw[1];
|
|
2562
|
+
if (typeof hash4 === "string" && isNumberArray(vector)) {
|
|
2563
|
+
return { hash: hash4, vector };
|
|
2564
|
+
}
|
|
2565
|
+
return null;
|
|
2566
|
+
}
|
|
2567
|
+
if (raw && typeof raw === "object") {
|
|
2568
|
+
const row = raw;
|
|
2569
|
+
if (typeof row.h === "string" && isNumberArray(row.v)) {
|
|
2570
|
+
return { hash: row.h, vector: row.v };
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
return null;
|
|
2574
|
+
}
|
|
2575
|
+
function cacheInsert(hash4, value, dirty) {
|
|
2576
|
+
rememberDimensions(value);
|
|
2577
|
+
const incoming = payloadBytes(value);
|
|
2578
|
+
const existing = cache3.get(hash4);
|
|
2579
|
+
if (existing) {
|
|
2580
|
+
cachePayloadBytes -= payloadBytes(existing);
|
|
2581
|
+
cache3.delete(hash4);
|
|
2582
|
+
}
|
|
2583
|
+
evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
|
|
2584
|
+
cache3.set(hash4, value);
|
|
2585
|
+
cachePayloadBytes += incoming;
|
|
2586
|
+
if (dirty) diskCacheDirty2 = true;
|
|
2587
|
+
}
|
|
2588
|
+
async function readCacheStat() {
|
|
2589
|
+
try {
|
|
2590
|
+
const data = JSON.parse(await readFile2(cacheStatFile(), "utf-8"));
|
|
2591
|
+
if (typeof data.entryCount === "number" && Number.isFinite(data.entryCount) && data.entryCount >= 0) {
|
|
2592
|
+
return {
|
|
2593
|
+
version: 1,
|
|
2594
|
+
entryCount: Math.floor(data.entryCount),
|
|
2595
|
+
dimensions: isValidDimension(data.dimensions) ? data.dimensions : null
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
} catch {
|
|
2599
|
+
}
|
|
2600
|
+
return null;
|
|
2601
|
+
}
|
|
2602
|
+
async function writeCacheStat(stat5) {
|
|
2603
|
+
try {
|
|
2604
|
+
await mkdir2(cacheDir(), { recursive: true });
|
|
2605
|
+
await writeFile2(cacheStatFile(), JSON.stringify(stat5));
|
|
2606
|
+
} catch {
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2502
2609
|
function normalizeText(text2) {
|
|
2503
2610
|
return text2.replace(/\s+/g, " ").trim().slice(0, MAX_INPUT_CHARS);
|
|
2504
2611
|
}
|
|
@@ -2584,29 +2691,300 @@ function toProviderInput(input, baseUrl) {
|
|
|
2584
2691
|
}
|
|
2585
2692
|
return { type: input.modality, url: input.url };
|
|
2586
2693
|
}
|
|
2694
|
+
function parseCacheJsonInWorker(filePath) {
|
|
2695
|
+
return new Promise((resolve, reject) => {
|
|
2696
|
+
void import("worker_threads").then(({ Worker }) => {
|
|
2697
|
+
const worker = new Worker(
|
|
2698
|
+
`
|
|
2699
|
+
const { parentPort, workerData } = require('node:worker_threads');
|
|
2700
|
+
const { readFileSync } = require('node:fs');
|
|
2701
|
+
try {
|
|
2702
|
+
const raw = readFileSync(workerData.filePath, 'utf8');
|
|
2703
|
+
parentPort.postMessage({ ok: true, entries: JSON.parse(raw) });
|
|
2704
|
+
} catch (err) {
|
|
2705
|
+
parentPort.postMessage({
|
|
2706
|
+
ok: false,
|
|
2707
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2708
|
+
});
|
|
2709
|
+
}
|
|
2710
|
+
`,
|
|
2711
|
+
{ eval: true, workerData: { filePath } }
|
|
2712
|
+
);
|
|
2713
|
+
worker.once("message", (msg) => {
|
|
2714
|
+
void worker.terminate();
|
|
2715
|
+
if (msg?.ok && Array.isArray(msg.entries)) {
|
|
2716
|
+
resolve(msg.entries);
|
|
2717
|
+
return;
|
|
2718
|
+
}
|
|
2719
|
+
reject(new Error(msg?.error || "embedding cache worker parse failed"));
|
|
2720
|
+
});
|
|
2721
|
+
worker.once("error", reject);
|
|
2722
|
+
}).catch(reject);
|
|
2723
|
+
});
|
|
2724
|
+
}
|
|
2725
|
+
function findJsonValueEnd(source) {
|
|
2726
|
+
let i2 = 0;
|
|
2727
|
+
while (i2 < source.length && /\s/.test(source[i2])) i2 += 1;
|
|
2728
|
+
if (i2 >= source.length) return -1;
|
|
2729
|
+
const start = source[i2];
|
|
2730
|
+
if (start === '"') {
|
|
2731
|
+
i2 += 1;
|
|
2732
|
+
while (i2 < source.length) {
|
|
2733
|
+
if (source[i2] === "\\") {
|
|
2734
|
+
i2 += 2;
|
|
2735
|
+
continue;
|
|
2736
|
+
}
|
|
2737
|
+
if (source[i2] === '"') return i2 + 1;
|
|
2738
|
+
i2 += 1;
|
|
2739
|
+
}
|
|
2740
|
+
return -1;
|
|
2741
|
+
}
|
|
2742
|
+
if (start === "{" || start === "[") {
|
|
2743
|
+
const close = start === "{" ? "}" : "]";
|
|
2744
|
+
let depth = 0;
|
|
2745
|
+
let inString = false;
|
|
2746
|
+
for (; i2 < source.length; i2 += 1) {
|
|
2747
|
+
const ch = source[i2];
|
|
2748
|
+
if (inString) {
|
|
2749
|
+
if (ch === "\\") {
|
|
2750
|
+
i2 += 1;
|
|
2751
|
+
continue;
|
|
2752
|
+
}
|
|
2753
|
+
if (ch === '"') inString = false;
|
|
2754
|
+
continue;
|
|
2755
|
+
}
|
|
2756
|
+
if (ch === '"') {
|
|
2757
|
+
inString = true;
|
|
2758
|
+
continue;
|
|
2759
|
+
}
|
|
2760
|
+
if (ch === start) depth += 1;
|
|
2761
|
+
else if (ch === close) {
|
|
2762
|
+
depth -= 1;
|
|
2763
|
+
if (depth === 0) return i2 + 1;
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
return -1;
|
|
2767
|
+
}
|
|
2768
|
+
const rest = source.slice(i2);
|
|
2769
|
+
const match2 = rest.match(/^(?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)/);
|
|
2770
|
+
return match2 ? i2 + match2[0].length : -1;
|
|
2771
|
+
}
|
|
2772
|
+
async function streamJsonArrayToJsonl(filePath, tmpPath) {
|
|
2773
|
+
const input = createReadStream(filePath, { encoding: "utf8" });
|
|
2774
|
+
const out = createWriteStream(tmpPath);
|
|
2775
|
+
let buf = "";
|
|
2776
|
+
let started = false;
|
|
2777
|
+
let count2 = 0;
|
|
2778
|
+
const writeLine = async (line) => {
|
|
2779
|
+
if (!out.write(line)) {
|
|
2780
|
+
await new Promise((resolve, reject) => {
|
|
2781
|
+
out.once("drain", resolve);
|
|
2782
|
+
out.once("error", reject);
|
|
2783
|
+
});
|
|
2784
|
+
}
|
|
2785
|
+
};
|
|
2786
|
+
try {
|
|
2787
|
+
for await (const chunk of input) {
|
|
2788
|
+
buf += chunk;
|
|
2789
|
+
if (!started) {
|
|
2790
|
+
const idx = buf.search(/\S/);
|
|
2791
|
+
if (idx < 0) continue;
|
|
2792
|
+
if (buf[idx] !== "[") throw new Error("embedding cache is not a JSON array");
|
|
2793
|
+
buf = buf.slice(idx + 1);
|
|
2794
|
+
started = true;
|
|
2795
|
+
}
|
|
2796
|
+
while (true) {
|
|
2797
|
+
buf = buf.replace(/^\s*,?\s*/, "");
|
|
2798
|
+
if (!buf) break;
|
|
2799
|
+
if (buf[0] === "]") {
|
|
2800
|
+
buf = "";
|
|
2801
|
+
break;
|
|
2802
|
+
}
|
|
2803
|
+
const end = findJsonValueEnd(buf);
|
|
2804
|
+
if (end < 0) break;
|
|
2805
|
+
const value = JSON.parse(buf.slice(0, end));
|
|
2806
|
+
buf = buf.slice(end);
|
|
2807
|
+
const row = parseCacheRow(value);
|
|
2808
|
+
if (row) {
|
|
2809
|
+
await writeLine(`${JSON.stringify({ h: row.hash, v: row.vector })}
|
|
2810
|
+
`);
|
|
2811
|
+
count2 += 1;
|
|
2812
|
+
if (count2 % 50 === 0) {
|
|
2813
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
if (buf.length > MAX_MIGRATION_VALUE_BYTES) {
|
|
2818
|
+
throw new Error("embedding cache entry too large to migrate safely");
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
} finally {
|
|
2822
|
+
await new Promise((resolve, reject) => {
|
|
2823
|
+
out.end(() => resolve());
|
|
2824
|
+
out.once("error", reject);
|
|
2825
|
+
});
|
|
2826
|
+
}
|
|
2827
|
+
return count2;
|
|
2828
|
+
}
|
|
2829
|
+
async function migrateJsonArrayCacheToJsonl(filePath, jsonlPath) {
|
|
2830
|
+
const tmpPath = `${jsonlPath}.tmp`;
|
|
2831
|
+
try {
|
|
2832
|
+
const count2 = await streamJsonArrayToJsonl(filePath, tmpPath);
|
|
2833
|
+
await rename(tmpPath, jsonlPath);
|
|
2834
|
+
diskCacheEntryCount = count2;
|
|
2835
|
+
await writeCacheStat({
|
|
2836
|
+
version: 1,
|
|
2837
|
+
entryCount: count2,
|
|
2838
|
+
dimensions: cacheDimensions
|
|
2839
|
+
});
|
|
2840
|
+
return count2;
|
|
2841
|
+
} catch (err) {
|
|
2842
|
+
await unlink(tmpPath).catch(() => void 0);
|
|
2843
|
+
throw err;
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
function convertArrayCacheToJsonl(filePath, jsonlPath) {
|
|
2847
|
+
return migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
|
|
2848
|
+
}
|
|
2849
|
+
async function loadCacheJsonl(filePath) {
|
|
2850
|
+
const rl = createInterface({
|
|
2851
|
+
input: createReadStream(filePath, { encoding: "utf8" }),
|
|
2852
|
+
crlfDelay: Infinity
|
|
2853
|
+
});
|
|
2854
|
+
let loaded = 0;
|
|
2855
|
+
let onDiskCount = 0;
|
|
2856
|
+
for await (const line of rl) {
|
|
2857
|
+
const trimmed = line.trim();
|
|
2858
|
+
if (!trimmed) continue;
|
|
2859
|
+
try {
|
|
2860
|
+
const row = parseCacheRow(JSON.parse(trimmed));
|
|
2861
|
+
if (!row) continue;
|
|
2862
|
+
onDiskCount += 1;
|
|
2863
|
+
cacheInsert(row.hash, row.vector, false);
|
|
2864
|
+
loaded = cache3.size;
|
|
2865
|
+
} catch {
|
|
2866
|
+
}
|
|
2867
|
+
if (onDiskCount % 250 === 0) {
|
|
2868
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
diskCacheEntryCount = onDiskCount;
|
|
2872
|
+
await writeCacheStat({
|
|
2873
|
+
version: 1,
|
|
2874
|
+
entryCount: onDiskCount,
|
|
2875
|
+
dimensions: cacheDimensions
|
|
2876
|
+
});
|
|
2877
|
+
return loaded;
|
|
2878
|
+
}
|
|
2879
|
+
async function countJsonlLines(filePath) {
|
|
2880
|
+
jsonlLineCountCalls += 1;
|
|
2881
|
+
try {
|
|
2882
|
+
const rl = createInterface({
|
|
2883
|
+
input: createReadStream(filePath, { encoding: "utf8" }),
|
|
2884
|
+
crlfDelay: Infinity
|
|
2885
|
+
});
|
|
2886
|
+
let count2 = 0;
|
|
2887
|
+
for await (const line of rl) {
|
|
2888
|
+
if (line.trim()) count2 += 1;
|
|
2889
|
+
}
|
|
2890
|
+
return count2;
|
|
2891
|
+
} catch {
|
|
2892
|
+
return 0;
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
async function resolveOnDiskCount(jsonlPath) {
|
|
2896
|
+
if (diskCacheEntryCount > 0) return diskCacheEntryCount;
|
|
2897
|
+
const sidecar = await readCacheStat();
|
|
2898
|
+
if (sidecar) {
|
|
2899
|
+
diskCacheEntryCount = sidecar.entryCount;
|
|
2900
|
+
if (sidecar.dimensions && cacheDimensions === null) cacheDimensions = sidecar.dimensions;
|
|
2901
|
+
return sidecar.entryCount;
|
|
2902
|
+
}
|
|
2903
|
+
try {
|
|
2904
|
+
await access(jsonlPath, fsConstants.R_OK);
|
|
2905
|
+
} catch {
|
|
2906
|
+
return 0;
|
|
2907
|
+
}
|
|
2908
|
+
const counted = await countJsonlLines(jsonlPath);
|
|
2909
|
+
diskCacheEntryCount = counted;
|
|
2910
|
+
await writeCacheStat({
|
|
2911
|
+
version: 1,
|
|
2912
|
+
entryCount: counted,
|
|
2913
|
+
dimensions: cacheDimensions
|
|
2914
|
+
});
|
|
2915
|
+
return counted;
|
|
2916
|
+
}
|
|
2587
2917
|
async function loadDiskCache2() {
|
|
2588
|
-
if (diskCacheLoaded) return;
|
|
2918
|
+
if (diskCacheLoaded) return cache3.size;
|
|
2919
|
+
let loaded = 0;
|
|
2589
2920
|
try {
|
|
2590
|
-
const
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2921
|
+
const jsonlPath = cacheJsonlFile();
|
|
2922
|
+
let hasJsonl = false;
|
|
2923
|
+
try {
|
|
2924
|
+
await access(jsonlPath, fsConstants.R_OK);
|
|
2925
|
+
hasJsonl = true;
|
|
2926
|
+
} catch {
|
|
2927
|
+
hasJsonl = false;
|
|
2928
|
+
}
|
|
2929
|
+
if (hasJsonl) {
|
|
2930
|
+
loaded = await loadCacheJsonl(jsonlPath);
|
|
2931
|
+
} else {
|
|
2932
|
+
const filePath = cacheFile();
|
|
2933
|
+
await stat(filePath);
|
|
2934
|
+
await migrateJsonArrayCacheToJsonl(filePath, jsonlPath);
|
|
2935
|
+
loaded = await loadCacheJsonl(jsonlPath);
|
|
2936
|
+
}
|
|
2937
|
+
diskCacheLoadCount += 1;
|
|
2938
|
+
console.error(`[memorix] Loaded ${loaded} cached API embeddings from disk`);
|
|
2594
2939
|
} catch {
|
|
2595
2940
|
}
|
|
2596
2941
|
diskCacheLoaded = true;
|
|
2942
|
+
return loaded;
|
|
2597
2943
|
}
|
|
2598
2944
|
function startDiskCacheLoad() {
|
|
2599
2945
|
if (diskCacheLoaded || diskCacheLoadPromise) return;
|
|
2600
|
-
diskCacheLoadPromise = loadDiskCache2().catch(() => {
|
|
2946
|
+
diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
|
|
2601
2947
|
});
|
|
2602
2948
|
}
|
|
2603
2949
|
async function ensureDiskCacheLoaded() {
|
|
2604
|
-
if (diskCacheLoaded) return;
|
|
2605
|
-
if (diskCacheLoadPromise) {
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
}
|
|
2609
|
-
await
|
|
2950
|
+
if (diskCacheLoaded) return cache3.size;
|
|
2951
|
+
if (!diskCacheLoadPromise) {
|
|
2952
|
+
diskCacheLoadPromise = loadDiskCache2().then(() => void 0).catch(() => {
|
|
2953
|
+
});
|
|
2954
|
+
}
|
|
2955
|
+
await diskCacheLoadPromise;
|
|
2956
|
+
return cache3.size;
|
|
2957
|
+
}
|
|
2958
|
+
function getApiEmbeddingDiskCacheLoadCountForTests() {
|
|
2959
|
+
return diskCacheLoadCount;
|
|
2960
|
+
}
|
|
2961
|
+
function resetApiEmbeddingCacheForTests() {
|
|
2962
|
+
cache3.clear();
|
|
2963
|
+
cachePayloadBytes = 0;
|
|
2964
|
+
cacheDimensions = null;
|
|
2965
|
+
diskCacheEntryCount = 0;
|
|
2966
|
+
diskCacheLoaded = false;
|
|
2967
|
+
diskCacheLoadPromise = null;
|
|
2968
|
+
diskCacheLoadCount = 0;
|
|
2969
|
+
diskCacheDirty2 = false;
|
|
2970
|
+
jsonlLineCountCalls = 0;
|
|
2971
|
+
}
|
|
2972
|
+
function seedApiEmbeddingCacheForTests(entries) {
|
|
2973
|
+
cache3.clear();
|
|
2974
|
+
cachePayloadBytes = 0;
|
|
2975
|
+
cacheDimensions = null;
|
|
2976
|
+
for (const [key, value] of entries) cacheInsert(key, value, true);
|
|
2977
|
+
diskCacheLoaded = true;
|
|
2978
|
+
diskCacheDirty2 = true;
|
|
2979
|
+
}
|
|
2980
|
+
function getApiEmbeddingCachePayloadBytesForTests() {
|
|
2981
|
+
return cachePayloadBytes;
|
|
2982
|
+
}
|
|
2983
|
+
function getApiEmbeddingJsonlLineCountCallsForTests() {
|
|
2984
|
+
return jsonlLineCountCalls;
|
|
2985
|
+
}
|
|
2986
|
+
function getApiEmbeddingMaxEntriesForDimensionsForTests(dimensions) {
|
|
2987
|
+
return maxEntriesForDimensions(dimensions);
|
|
2610
2988
|
}
|
|
2611
2989
|
function dimsCacheKey(config) {
|
|
2612
2990
|
return [
|
|
@@ -2722,8 +3100,46 @@ async function saveDiskCacheNow() {
|
|
|
2722
3100
|
if (!diskCacheDirty2) return;
|
|
2723
3101
|
try {
|
|
2724
3102
|
await mkdir2(cacheDir(), { recursive: true });
|
|
2725
|
-
const
|
|
2726
|
-
await
|
|
3103
|
+
const jsonlPath = cacheJsonlFile();
|
|
3104
|
+
const existing = await resolveOnDiskCount(jsonlPath);
|
|
3105
|
+
if (existing > 0 && cache3.size < Math.ceil(existing * 0.5)) {
|
|
3106
|
+
console.error(
|
|
3107
|
+
`[memorix] Refusing to overwrite embedding cache (${existing} on disk vs ${cache3.size} in memory)`
|
|
3108
|
+
);
|
|
3109
|
+
diskCacheDirty2 = false;
|
|
3110
|
+
return;
|
|
3111
|
+
}
|
|
3112
|
+
const tmpPath = `${jsonlPath}.tmp`;
|
|
3113
|
+
await new Promise((resolve, reject) => {
|
|
3114
|
+
const out = createWriteStream(tmpPath);
|
|
3115
|
+
const entries = cache3.entries();
|
|
3116
|
+
const writeBatch = () => {
|
|
3117
|
+
for (let i2 = 0; i2 < 50; i2++) {
|
|
3118
|
+
const next = entries.next();
|
|
3119
|
+
if (next.done) {
|
|
3120
|
+
out.end();
|
|
3121
|
+
return;
|
|
3122
|
+
}
|
|
3123
|
+
const [hash4, vector] = next.value;
|
|
3124
|
+
if (!out.write(`${JSON.stringify({ h: hash4, v: vector })}
|
|
3125
|
+
`)) {
|
|
3126
|
+
out.once("drain", writeBatch);
|
|
3127
|
+
return;
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
setImmediate(writeBatch);
|
|
3131
|
+
};
|
|
3132
|
+
out.on("finish", resolve);
|
|
3133
|
+
out.on("error", reject);
|
|
3134
|
+
writeBatch();
|
|
3135
|
+
});
|
|
3136
|
+
await rename(tmpPath, jsonlPath);
|
|
3137
|
+
diskCacheEntryCount = cache3.size;
|
|
3138
|
+
await writeCacheStat({
|
|
3139
|
+
version: 1,
|
|
3140
|
+
entryCount: cache3.size,
|
|
3141
|
+
dimensions: cacheDimensions
|
|
3142
|
+
});
|
|
2727
3143
|
diskCacheDirty2 = false;
|
|
2728
3144
|
} catch {
|
|
2729
3145
|
}
|
|
@@ -2737,12 +3153,7 @@ function scheduleDiskSave() {
|
|
|
2737
3153
|
}, DISK_SAVE_DEBOUNCE_MS);
|
|
2738
3154
|
}
|
|
2739
3155
|
function cacheSet(hash4, value) {
|
|
2740
|
-
|
|
2741
|
-
const firstKey = cache3.keys().next().value;
|
|
2742
|
-
if (firstKey !== void 0) cache3.delete(firstKey);
|
|
2743
|
-
}
|
|
2744
|
-
cache3.set(hash4, value);
|
|
2745
|
-
diskCacheDirty2 = true;
|
|
3156
|
+
cacheInsert(hash4, value, true);
|
|
2746
3157
|
}
|
|
2747
3158
|
function resolveEnvEmbeddingApiKey() {
|
|
2748
3159
|
return process.env.MEMORIX_EMBEDDING_API_KEY;
|
|
@@ -2861,18 +3272,27 @@ async function waitForRetry(waitMs, signal) {
|
|
|
2861
3272
|
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
2862
3273
|
});
|
|
2863
3274
|
}
|
|
2864
|
-
var cache3,
|
|
3275
|
+
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;
|
|
2865
3276
|
var init_api_provider = __esm({
|
|
2866
3277
|
"src/embedding/api-provider.ts"() {
|
|
2867
3278
|
"use strict";
|
|
2868
3279
|
init_esm_shims();
|
|
2869
3280
|
init_provider();
|
|
2870
3281
|
cache3 = /* @__PURE__ */ new Map();
|
|
2871
|
-
|
|
3282
|
+
MAX_CACHE_ENTRIES = 2e5;
|
|
3283
|
+
MAX_CACHE_PAYLOAD_BYTES = 256 * 1024 * 1024;
|
|
3284
|
+
BYTES_PER_DIM = 8;
|
|
3285
|
+
DEFAULT_ASSUMED_DIMENSIONS = 4096;
|
|
3286
|
+
MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
|
|
3287
|
+
cachePayloadBytes = 0;
|
|
3288
|
+
cacheDimensions = null;
|
|
3289
|
+
diskCacheEntryCount = 0;
|
|
2872
3290
|
diskCacheDirty2 = false;
|
|
2873
3291
|
diskSaveTimer = null;
|
|
2874
3292
|
diskCacheLoaded = false;
|
|
2875
3293
|
diskCacheLoadPromise = null;
|
|
3294
|
+
diskCacheLoadCount = 0;
|
|
3295
|
+
jsonlLineCountCalls = 0;
|
|
2876
3296
|
MAX_INPUT_CHARS = 32e3;
|
|
2877
3297
|
MAX_CONCURRENCY = 4;
|
|
2878
3298
|
DISK_SAVE_DEBOUNCE_MS = 5e3;
|
|
@@ -2930,7 +3350,9 @@ var init_api_provider = __esm({
|
|
|
2930
3350
|
if (config.requestedDimensions) {
|
|
2931
3351
|
console.error(`[memorix] Dimension shortening: ${config.requestedDimensions}d requested`);
|
|
2932
3352
|
}
|
|
2933
|
-
|
|
3353
|
+
if (allowNetworkProbe) {
|
|
3354
|
+
startDiskCacheLoad();
|
|
3355
|
+
}
|
|
2934
3356
|
return new _APIEmbeddingProvider(config, probeDimensions);
|
|
2935
3357
|
}
|
|
2936
3358
|
static resolveConfig() {
|
|
@@ -3268,7 +3690,12 @@ function warnOnce(message) {
|
|
|
3268
3690
|
lastEmbeddingWarningAt = now3;
|
|
3269
3691
|
console.error(message);
|
|
3270
3692
|
}
|
|
3693
|
+
function embeddingModeToken() {
|
|
3694
|
+
return process.env.MEMORIX_EMBEDDING?.toLowerCase()?.trim() ?? "";
|
|
3695
|
+
}
|
|
3271
3696
|
function getEmbeddingMode2() {
|
|
3697
|
+
const token = embeddingModeToken();
|
|
3698
|
+
if (cachedEmbeddingMode && cachedEmbeddingModeToken === token) return cachedEmbeddingMode;
|
|
3272
3699
|
try {
|
|
3273
3700
|
let cfg;
|
|
3274
3701
|
try {
|
|
@@ -3277,10 +3704,17 @@ function getEmbeddingMode2() {
|
|
|
3277
3704
|
cfg = (init_config(), __toCommonJS(config_exports));
|
|
3278
3705
|
}
|
|
3279
3706
|
const { getEmbeddingMode: cfgMode } = cfg;
|
|
3280
|
-
|
|
3707
|
+
cachedEmbeddingMode = cfgMode();
|
|
3708
|
+
cachedEmbeddingModeToken = token;
|
|
3709
|
+
return cachedEmbeddingMode;
|
|
3281
3710
|
} catch {
|
|
3282
|
-
|
|
3283
|
-
|
|
3711
|
+
if (token === "fastembed" || token === "transformers" || token === "api" || token === "auto") {
|
|
3712
|
+
cachedEmbeddingMode = token;
|
|
3713
|
+
cachedEmbeddingModeToken = token;
|
|
3714
|
+
return token;
|
|
3715
|
+
}
|
|
3716
|
+
cachedEmbeddingMode = "off";
|
|
3717
|
+
cachedEmbeddingModeToken = token;
|
|
3284
3718
|
return "off";
|
|
3285
3719
|
}
|
|
3286
3720
|
}
|
|
@@ -3536,13 +3970,15 @@ function isEmbeddingExplicitlyDisabled() {
|
|
|
3536
3970
|
function resetProvider() {
|
|
3537
3971
|
provider = null;
|
|
3538
3972
|
initPromise = null;
|
|
3973
|
+
cachedEmbeddingMode = null;
|
|
3974
|
+
cachedEmbeddingModeToken = null;
|
|
3539
3975
|
lastInitWasTemporaryFailure = false;
|
|
3540
3976
|
lastFailureTimestamp = 0;
|
|
3541
3977
|
lastEmbeddingFailure = null;
|
|
3542
3978
|
lastEmbeddingWarning = "";
|
|
3543
3979
|
lastEmbeddingWarningAt = 0;
|
|
3544
3980
|
}
|
|
3545
|
-
var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, lastInitWasTemporaryFailure, lastEmbeddingFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
|
|
3981
|
+
var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, cachedEmbeddingMode, cachedEmbeddingModeToken, lastInitWasTemporaryFailure, lastEmbeddingFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
|
|
3546
3982
|
var init_provider = __esm({
|
|
3547
3983
|
"src/embedding/provider.ts"() {
|
|
3548
3984
|
"use strict";
|
|
@@ -3566,6 +4002,8 @@ var init_provider = __esm({
|
|
|
3566
4002
|
MAX_INLINE_EMBEDDING_BYTES = 5 * 1024 * 1024;
|
|
3567
4003
|
provider = null;
|
|
3568
4004
|
initPromise = null;
|
|
4005
|
+
cachedEmbeddingMode = null;
|
|
4006
|
+
cachedEmbeddingModeToken = null;
|
|
3569
4007
|
lastInitWasTemporaryFailure = false;
|
|
3570
4008
|
lastEmbeddingFailure = null;
|
|
3571
4009
|
lastEmbeddingWarning = "";
|
|
@@ -5323,6 +5761,9 @@ async function attachCachedVectors(database, candidates) {
|
|
|
5323
5761
|
await update(database, candidates[index].id, { ...existing, embedding: vector });
|
|
5324
5762
|
} catch {
|
|
5325
5763
|
}
|
|
5764
|
+
if (index % 50 === 0) {
|
|
5765
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
5766
|
+
}
|
|
5326
5767
|
}
|
|
5327
5768
|
}
|
|
5328
5769
|
async function hydrateIndex(observations2, options = {}) {
|
|
@@ -5373,17 +5814,24 @@ async function hydrateIndex(observations2, options = {}) {
|
|
|
5373
5814
|
await insert2(database, doc);
|
|
5374
5815
|
rememberObservationDoc(doc);
|
|
5375
5816
|
inserted++;
|
|
5817
|
+
if (inserted % 50 === 0) {
|
|
5818
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
5819
|
+
}
|
|
5820
|
+
if (inserted % 200 === 0) {
|
|
5821
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
5822
|
+
}
|
|
5376
5823
|
} catch {
|
|
5377
5824
|
}
|
|
5378
5825
|
}
|
|
5379
|
-
deferredCachedVectorHydration = deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
5826
|
+
deferredCachedVectorHydration = options.skipCachedVectors ? null : deferCachedVectors ? attachCachedVectors(database, candidates).catch(() => {
|
|
5380
5827
|
}) : null;
|
|
5381
5828
|
return inserted;
|
|
5382
5829
|
}
|
|
5383
|
-
async function hydrateIndexForStartup(observations2) {
|
|
5830
|
+
async function hydrateIndexForStartup(observations2, options = {}) {
|
|
5384
5831
|
return hydrateIndex(observations2, {
|
|
5385
5832
|
allowNetworkProbe: false,
|
|
5386
|
-
deferCachedVectors: true
|
|
5833
|
+
deferCachedVectors: true,
|
|
5834
|
+
skipCachedVectors: options.skipCachedVectors
|
|
5387
5835
|
});
|
|
5388
5836
|
}
|
|
5389
5837
|
function getDeferredCachedVectorHydration() {
|
|
@@ -8171,12 +8619,25 @@ __export(observations_exports, {
|
|
|
8171
8619
|
prepareSearchIndex: () => prepareSearchIndex,
|
|
8172
8620
|
probeSearchIndex: () => probeSearchIndex,
|
|
8173
8621
|
reindexObservations: () => reindexObservations,
|
|
8622
|
+
resetObservationRuntime: () => resetObservationRuntime,
|
|
8174
8623
|
resolveObservations: () => resolveObservations,
|
|
8175
8624
|
storeObservation: () => storeObservation,
|
|
8176
8625
|
suggestTopicKey: () => suggestTopicKey,
|
|
8177
8626
|
updateObservationAdmission: () => updateObservationAdmission,
|
|
8178
8627
|
withFreshObservations: () => withFreshObservations
|
|
8179
8628
|
});
|
|
8629
|
+
function resetObservationRuntime() {
|
|
8630
|
+
observations = [];
|
|
8631
|
+
nextId = 1;
|
|
8632
|
+
projectDir = null;
|
|
8633
|
+
searchIndexPrepared = false;
|
|
8634
|
+
corpusLoaded = false;
|
|
8635
|
+
loadedObservationStore = null;
|
|
8636
|
+
embeddingWriteMode = "background";
|
|
8637
|
+
embeddingWorkerProjectRoot = void 0;
|
|
8638
|
+
vectorMissingIds.clear();
|
|
8639
|
+
vectorSchemaUpgradePromise = null;
|
|
8640
|
+
}
|
|
8180
8641
|
function logEmbeddingFailureOnce(key, message) {
|
|
8181
8642
|
const now3 = Date.now();
|
|
8182
8643
|
const last = embeddingFailureLogTimestamps.get(key) ?? 0;
|
|
@@ -8285,23 +8746,33 @@ async function upgradeVectorSchemaAfterFirstEmbedding(embedding) {
|
|
|
8285
8746
|
async function initObservations(dir, options = {}) {
|
|
8286
8747
|
embeddingWriteMode = options.embeddingWriteMode ?? "background";
|
|
8287
8748
|
embeddingWorkerProjectRoot = options.projectRoot;
|
|
8288
|
-
if (projectDir === dir) return;
|
|
8289
8749
|
await initObservationStore(dir);
|
|
8290
8750
|
const store = getObservationStore();
|
|
8291
|
-
|
|
8751
|
+
if (projectDir === dir && loadedObservationStore === store) {
|
|
8752
|
+
if (options.skipCorpusLoad || corpusLoaded) return;
|
|
8753
|
+
}
|
|
8292
8754
|
nextId = await store.loadIdCounter();
|
|
8293
8755
|
projectDir = dir;
|
|
8294
8756
|
searchIndexPrepared = false;
|
|
8295
8757
|
vectorSchemaUpgradePromise = null;
|
|
8758
|
+
if (options.skipCorpusLoad) {
|
|
8759
|
+
observations = [];
|
|
8760
|
+
corpusLoaded = false;
|
|
8761
|
+
loadedObservationStore = store;
|
|
8762
|
+
return;
|
|
8763
|
+
}
|
|
8764
|
+
observations = await store.loadAll();
|
|
8765
|
+
corpusLoaded = true;
|
|
8766
|
+
loadedObservationStore = store;
|
|
8296
8767
|
}
|
|
8297
8768
|
function scheduleObservationEmbedding(input) {
|
|
8298
8769
|
const { observationId: observationId2, projectId, searchableText, doc } = input;
|
|
8299
8770
|
vectorMissingIds.add(observationId2);
|
|
8771
|
+
if (isEmbeddingExplicitlyDisabled()) {
|
|
8772
|
+
vectorMissingIds.delete(observationId2);
|
|
8773
|
+
return;
|
|
8774
|
+
}
|
|
8300
8775
|
if (embeddingWriteMode === "deferred") {
|
|
8301
|
-
if (isEmbeddingExplicitlyDisabled()) {
|
|
8302
|
-
vectorMissingIds.delete(observationId2);
|
|
8303
|
-
return;
|
|
8304
|
-
}
|
|
8305
8776
|
queueVectorBackfill(projectId, {
|
|
8306
8777
|
detachedWorker: true,
|
|
8307
8778
|
projectRoot: embeddingWorkerProjectRoot
|
|
@@ -8348,7 +8819,7 @@ function scheduleObservationEmbedding(input) {
|
|
|
8348
8819
|
});
|
|
8349
8820
|
}
|
|
8350
8821
|
async function ensureFreshObservations() {
|
|
8351
|
-
if (!projectDir) return false;
|
|
8822
|
+
if (!projectDir || !corpusLoaded) return false;
|
|
8352
8823
|
try {
|
|
8353
8824
|
const store = getObservationStore();
|
|
8354
8825
|
const wasStale = await store.ensureFresh();
|
|
@@ -8389,7 +8860,7 @@ async function storeObservation(input) {
|
|
|
8389
8860
|
});
|
|
8390
8861
|
if (safeAttachments) input = { ...input, attachments: safeAttachments };
|
|
8391
8862
|
await ensureFreshObservations();
|
|
8392
|
-
if (input.topicKey) {
|
|
8863
|
+
if (input.topicKey && corpusLoaded) {
|
|
8393
8864
|
const existing = observations.find(
|
|
8394
8865
|
(o) => o.topicKey === input.topicKey && o.projectId === input.projectId
|
|
8395
8866
|
);
|
|
@@ -8486,13 +8957,17 @@ async function storeObservation(input) {
|
|
|
8486
8957
|
});
|
|
8487
8958
|
observation.writeGeneration = store.getGeneration();
|
|
8488
8959
|
if (upsertedInsideLock || reloadCacheAfterCommit) {
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8960
|
+
if (corpusLoaded) {
|
|
8961
|
+
observations = await store.loadAll();
|
|
8962
|
+
nextId = await store.loadIdCounter();
|
|
8963
|
+
if (upsertedInsideLock) {
|
|
8964
|
+
observation = observations.find((candidate) => candidate.id === observation.id) ?? observation;
|
|
8965
|
+
}
|
|
8966
|
+
} else {
|
|
8967
|
+
nextId = await store.loadIdCounter();
|
|
8493
8968
|
}
|
|
8494
8969
|
} else {
|
|
8495
|
-
observations.push(observation);
|
|
8970
|
+
if (corpusLoaded) observations.push(observation);
|
|
8496
8971
|
nextId = observation.id + 1;
|
|
8497
8972
|
}
|
|
8498
8973
|
if (upsertedInsideLock) return;
|
|
@@ -8560,7 +9035,9 @@ async function storeObservation(input) {
|
|
|
8560
9035
|
createdByAgentId: input.createdByAgentId ?? "",
|
|
8561
9036
|
sharedWithAgentIds: JSON.stringify(input.sharedWithAgentIds ?? [])
|
|
8562
9037
|
};
|
|
8563
|
-
|
|
9038
|
+
if (corpusLoaded) {
|
|
9039
|
+
await insertObservation(doc);
|
|
9040
|
+
}
|
|
8564
9041
|
};
|
|
8565
9042
|
await assignAndPersist();
|
|
8566
9043
|
if (upsertedInsideLock) {
|
|
@@ -8922,9 +9399,11 @@ async function reindexObservations() {
|
|
|
8922
9399
|
searchIndexPrepared = true;
|
|
8923
9400
|
return count2;
|
|
8924
9401
|
}
|
|
8925
|
-
async function prepareSearchIndex() {
|
|
9402
|
+
async function prepareSearchIndex(options = {}) {
|
|
8926
9403
|
if (searchIndexPrepared) return 0;
|
|
8927
|
-
const count2 = await hydrateIndexForStartup(observations
|
|
9404
|
+
const count2 = await hydrateIndexForStartup(observations, {
|
|
9405
|
+
skipCachedVectors: options.skipCachedVectors
|
|
9406
|
+
});
|
|
8928
9407
|
if (count2 === 0) {
|
|
8929
9408
|
searchIndexPrepared = true;
|
|
8930
9409
|
return 0;
|
|
@@ -9116,7 +9595,7 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
9116
9595
|
...lastFailure ? { lastError: lastFailure } : {}
|
|
9117
9596
|
};
|
|
9118
9597
|
}
|
|
9119
|
-
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
9598
|
+
var observations, nextId, projectDir, searchIndexPrepared, embeddingWriteMode, embeddingWorkerProjectRoot, corpusLoaded, loadedObservationStore, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
9120
9599
|
var init_observations = __esm({
|
|
9121
9600
|
"src/memory/observations.ts"() {
|
|
9122
9601
|
"use strict";
|
|
@@ -9135,6 +9614,8 @@ var init_observations = __esm({
|
|
|
9135
9614
|
projectDir = null;
|
|
9136
9615
|
searchIndexPrepared = false;
|
|
9137
9616
|
embeddingWriteMode = "background";
|
|
9617
|
+
corpusLoaded = false;
|
|
9618
|
+
loadedObservationStore = null;
|
|
9138
9619
|
vectorMissingIds = /* @__PURE__ */ new Set();
|
|
9139
9620
|
vectorBackfillRunning = false;
|
|
9140
9621
|
vectorSchemaUpgradePromise = null;
|
|
@@ -10480,7 +10961,7 @@ var init_types2 = __esm({
|
|
|
10480
10961
|
|
|
10481
10962
|
// src/media/asset-store.ts
|
|
10482
10963
|
import { createHash as createHash5, randomUUID as randomUUID6 } from "crypto";
|
|
10483
|
-
import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename, rm, stat, writeFile as writeFile3 } from "fs/promises";
|
|
10964
|
+
import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
10484
10965
|
import path9 from "path";
|
|
10485
10966
|
function mediaRoot(dataDir) {
|
|
10486
10967
|
return path9.resolve(dataDir, "media");
|
|
@@ -10553,7 +11034,7 @@ async function persistBuffer(bytes, destination) {
|
|
|
10553
11034
|
const temporary = `${destination}.${randomUUID6()}.tmp`;
|
|
10554
11035
|
try {
|
|
10555
11036
|
await writeFile3(temporary, bytes, { flag: "wx" });
|
|
10556
|
-
await
|
|
11037
|
+
await rename2(temporary, destination);
|
|
10557
11038
|
return true;
|
|
10558
11039
|
} catch (error) {
|
|
10559
11040
|
if (error?.code === "EEXIST" && await destinationExists(destination)) return false;
|
|
@@ -61633,9 +62114,9 @@ var require_node_domexception = __commonJS({
|
|
|
61633
62114
|
});
|
|
61634
62115
|
|
|
61635
62116
|
// node_modules/fetch-blob/from.js
|
|
61636
|
-
import { statSync as statSync2, createReadStream, promises as fs7 } from "fs";
|
|
62117
|
+
import { statSync as statSync2, createReadStream as createReadStream2, promises as fs7 } from "fs";
|
|
61637
62118
|
import { basename } from "path";
|
|
61638
|
-
var import_node_domexception,
|
|
62119
|
+
var import_node_domexception, stat3, blobFromSync, blobFrom, fileFrom, fileFromSync, fromBlob, fromFile, BlobDataItem;
|
|
61639
62120
|
var init_from = __esm({
|
|
61640
62121
|
"node_modules/fetch-blob/from.js"() {
|
|
61641
62122
|
"use strict";
|
|
@@ -61643,23 +62124,23 @@ var init_from = __esm({
|
|
|
61643
62124
|
import_node_domexception = __toESM(require_node_domexception(), 1);
|
|
61644
62125
|
init_file();
|
|
61645
62126
|
init_fetch_blob();
|
|
61646
|
-
({ stat:
|
|
62127
|
+
({ stat: stat3 } = fs7);
|
|
61647
62128
|
blobFromSync = (path20, type) => fromBlob(statSync2(path20), path20, type);
|
|
61648
|
-
blobFrom = (path20, type) =>
|
|
61649
|
-
fileFrom = (path20, type) =>
|
|
62129
|
+
blobFrom = (path20, type) => stat3(path20).then((stat5) => fromBlob(stat5, path20, type));
|
|
62130
|
+
fileFrom = (path20, type) => stat3(path20).then((stat5) => fromFile(stat5, path20, type));
|
|
61650
62131
|
fileFromSync = (path20, type) => fromFile(statSync2(path20), path20, type);
|
|
61651
|
-
fromBlob = (
|
|
62132
|
+
fromBlob = (stat5, path20, type = "") => new fetch_blob_default([new BlobDataItem({
|
|
61652
62133
|
path: path20,
|
|
61653
|
-
size:
|
|
61654
|
-
lastModified:
|
|
62134
|
+
size: stat5.size,
|
|
62135
|
+
lastModified: stat5.mtimeMs,
|
|
61655
62136
|
start: 0
|
|
61656
62137
|
})], { type });
|
|
61657
|
-
fromFile = (
|
|
62138
|
+
fromFile = (stat5, path20, type = "") => new file_default([new BlobDataItem({
|
|
61658
62139
|
path: path20,
|
|
61659
|
-
size:
|
|
61660
|
-
lastModified:
|
|
62140
|
+
size: stat5.size,
|
|
62141
|
+
lastModified: stat5.mtimeMs,
|
|
61661
62142
|
start: 0
|
|
61662
|
-
})], basename(path20), { type, lastModified:
|
|
62143
|
+
})], basename(path20), { type, lastModified: stat5.mtimeMs });
|
|
61663
62144
|
BlobDataItem = class _BlobDataItem {
|
|
61664
62145
|
#path;
|
|
61665
62146
|
#start;
|
|
@@ -61682,11 +62163,11 @@ var init_from = __esm({
|
|
|
61682
62163
|
});
|
|
61683
62164
|
}
|
|
61684
62165
|
async *stream() {
|
|
61685
|
-
const { mtimeMs } = await
|
|
62166
|
+
const { mtimeMs } = await stat3(this.#path);
|
|
61686
62167
|
if (mtimeMs > this.lastModified) {
|
|
61687
62168
|
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");
|
|
61688
62169
|
}
|
|
61689
|
-
yield*
|
|
62170
|
+
yield* createReadStream2(this.#path, {
|
|
61690
62171
|
start: this.#start,
|
|
61691
62172
|
end: this.#start + this.size - 1
|
|
61692
62173
|
});
|
|
@@ -77316,7 +77797,7 @@ var init_wrapper = __esm({
|
|
|
77316
77797
|
});
|
|
77317
77798
|
|
|
77318
77799
|
// node_modules/@google/genai/dist/node/index.mjs
|
|
77319
|
-
import { createWriteStream } from "fs";
|
|
77800
|
+
import { createWriteStream as createWriteStream2 } from "fs";
|
|
77320
77801
|
import * as fs8 from "fs/promises";
|
|
77321
77802
|
import { writeFile as writeFile4 } from "fs/promises";
|
|
77322
77803
|
import { Readable } from "stream";
|
|
@@ -94596,7 +95077,7 @@ ${underline}`);
|
|
|
94596
95077
|
if (params.downloadPath) {
|
|
94597
95078
|
const response = await downloadFile(params, apiClient);
|
|
94598
95079
|
if (response instanceof HttpResponse) {
|
|
94599
|
-
const writer =
|
|
95080
|
+
const writer = createWriteStream2(params.downloadPath);
|
|
94600
95081
|
const body = Readable.fromWeb(response.responseInternal.body);
|
|
94601
95082
|
body.pipe(writer);
|
|
94602
95083
|
await finished(writer);
|
|
@@ -144514,7 +144995,7 @@ function explainRetention(doc, referenceTime) {
|
|
|
144514
144995
|
};
|
|
144515
144996
|
}
|
|
144516
144997
|
function toRetentionDocument(obs, accessMap) {
|
|
144517
|
-
const
|
|
144998
|
+
const access2 = accessMap?.get(obs.id);
|
|
144518
144999
|
return {
|
|
144519
145000
|
id: `obs-${obs.id}`,
|
|
144520
145001
|
observationId: obs.id,
|
|
@@ -144528,8 +145009,8 @@ function toRetentionDocument(obs, accessMap) {
|
|
|
144528
145009
|
tokens: obs.tokens,
|
|
144529
145010
|
createdAt: obs.createdAt,
|
|
144530
145011
|
projectId: obs.projectId,
|
|
144531
|
-
accessCount:
|
|
144532
|
-
lastAccessedAt:
|
|
145012
|
+
accessCount: access2?.accessCount ?? 0,
|
|
145013
|
+
lastAccessedAt: access2?.lastAccessedAt ?? "",
|
|
144533
145014
|
status: obs.status ?? "active",
|
|
144534
145015
|
source: obs.source ?? "agent",
|
|
144535
145016
|
sourceDetail: obs.sourceDetail ?? "",
|
|
@@ -145808,7 +146289,7 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
|
|
|
145808
146289
|
const rel = normalizeCodePath(relative(projectRoot, abs));
|
|
145809
146290
|
try {
|
|
145810
146291
|
const text2 = readFileSync6(abs, "utf-8");
|
|
145811
|
-
const
|
|
146292
|
+
const stat5 = fileStat ?? statSync3(abs);
|
|
145812
146293
|
const file = {
|
|
145813
146294
|
id: makeCodeFileId(projectId, rel),
|
|
145814
146295
|
projectId,
|
|
@@ -145817,8 +146298,8 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
|
|
|
145817
146298
|
contentHash: hashText(text2),
|
|
145818
146299
|
// Preserve filesystem precision. Rounding here made same-size edits in
|
|
145819
146300
|
// a short Windows timestamp window indistinguishable to refresh.
|
|
145820
|
-
mtimeMs: Number(
|
|
145821
|
-
sizeBytes: Number(
|
|
146301
|
+
mtimeMs: Number(stat5.mtimeMs),
|
|
146302
|
+
sizeBytes: Number(stat5.size),
|
|
145822
146303
|
indexedAt
|
|
145823
146304
|
};
|
|
145824
146305
|
return {
|
|
@@ -145845,18 +146326,18 @@ async function indexProjectLite(options) {
|
|
|
145845
146326
|
let skippedOversizedFiles = 0;
|
|
145846
146327
|
let unreadableFiles = 0;
|
|
145847
146328
|
for (const abs of paths) {
|
|
145848
|
-
let
|
|
146329
|
+
let stat5;
|
|
145849
146330
|
try {
|
|
145850
|
-
|
|
146331
|
+
stat5 = statSync3(abs);
|
|
145851
146332
|
} catch {
|
|
145852
146333
|
unreadableFiles++;
|
|
145853
146334
|
continue;
|
|
145854
146335
|
}
|
|
145855
|
-
if (Number(
|
|
146336
|
+
if (Number(stat5.size) > maxFileBytes) {
|
|
145856
146337
|
skippedOversizedFiles++;
|
|
145857
146338
|
continue;
|
|
145858
146339
|
}
|
|
145859
|
-
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt,
|
|
146340
|
+
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
|
|
145860
146341
|
if (!attempt.delta) {
|
|
145861
146342
|
if (attempt.unreadable) unreadableFiles++;
|
|
145862
146343
|
continue;
|
|
@@ -145884,16 +146365,16 @@ async function refreshProjectLite(store, options) {
|
|
|
145884
146365
|
for (const abs of paths) {
|
|
145885
146366
|
const rel = normalizeCodePath(relative(options.projectRoot, abs));
|
|
145886
146367
|
seenPaths.add(rel);
|
|
145887
|
-
let
|
|
146368
|
+
let stat5;
|
|
145888
146369
|
try {
|
|
145889
|
-
|
|
146370
|
+
stat5 = statSync3(abs);
|
|
145890
146371
|
} catch {
|
|
145891
146372
|
unreadableFiles++;
|
|
145892
146373
|
continue;
|
|
145893
146374
|
}
|
|
145894
146375
|
const existing = existingByPath.get(rel);
|
|
145895
|
-
const mtimeMs = Number(
|
|
145896
|
-
const sizeBytes = Number(
|
|
146376
|
+
const mtimeMs = Number(stat5.mtimeMs);
|
|
146377
|
+
const sizeBytes = Number(stat5.size);
|
|
145897
146378
|
if (sizeBytes > maxFileBytes) {
|
|
145898
146379
|
skippedOversizedFiles++;
|
|
145899
146380
|
if (existing) oversizedExistingFileIds.add(existing.id);
|
|
@@ -145903,7 +146384,7 @@ async function refreshProjectLite(store, options) {
|
|
|
145903
146384
|
unchangedFiles++;
|
|
145904
146385
|
continue;
|
|
145905
146386
|
}
|
|
145906
|
-
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt,
|
|
146387
|
+
const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
|
|
145907
146388
|
if (!attempt.delta) {
|
|
145908
146389
|
if (attempt.unreadable) unreadableFiles++;
|
|
145909
146390
|
continue;
|