memorix 1.4.6 → 1.5.1
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 +38 -0
- package/TEAM.md +86 -86
- package/dist/cli/index.js +225 -70
- package/dist/cli/memcode.js +0 -0
- package/dist/index.js +225 -70
- package/dist/maintenance-runner.js +183 -38
- package/dist/media-audio-runner.js +183 -38
- package/dist/media-video-runner.js +183 -38
- package/dist/memcode-runtime/CHANGELOG.md +38 -0
- package/dist/sdk.js +224 -69
- package/dist/types.d.ts +2 -0
- package/dist/vector-backfill-runner.js +183 -38
- package/docs/CONFIGURATION.md +12 -0
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/package.json +1 -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/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/poll.ts +58 -58
- package/src/cli/commands/reasoning.ts +118 -118
- 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/config.ts +23 -2
- package/src/dashboard/project-classification.ts +64 -64
- package/src/embedding/api-provider.ts +105 -22
- package/src/embedding/provider.ts +2 -0
- package/src/git/hooks-path.ts +85 -85
- 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/provider.ts +17 -8
- 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/formation/extract.ts +6 -3
- package/src/memory/formation/index.ts +2 -1
- package/src/memory/formation/resolve.ts +10 -6
- package/src/memory/formation/types.ts +3 -1
- package/src/memory/freshness.ts +122 -122
- package/src/memory/graph.ts +197 -197
- package/src/memory/long-term.ts +7 -1
- 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/search/query-expansion.ts +52 -52
- package/src/server/formation-timeout.ts +27 -27
- package/src/server.ts +14 -10
- 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 +6 -4
- 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/timeout.ts +67 -0
- package/src/types.ts +2 -0
- 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
|
@@ -2141,6 +2141,7 @@ var init_resolved_config = __esm({
|
|
|
2141
2141
|
// src/config.ts
|
|
2142
2142
|
var config_exports = {};
|
|
2143
2143
|
__export(config_exports, {
|
|
2144
|
+
defaultEmbeddingModelFor: () => defaultEmbeddingModelFor,
|
|
2144
2145
|
getAgentLLMApiKey: () => getAgentLLMApiKey,
|
|
2145
2146
|
getAgentLLMBaseUrl: () => getAgentLLMBaseUrl,
|
|
2146
2147
|
getAgentLLMModel: () => getAgentLLMModel,
|
|
@@ -2203,7 +2204,19 @@ function getEmbeddingBaseUrl() {
|
|
|
2203
2204
|
return getResolvedEmbeddingLane().baseUrl || "https://api.openai.com/v1";
|
|
2204
2205
|
}
|
|
2205
2206
|
function getEmbeddingModel() {
|
|
2206
|
-
|
|
2207
|
+
const lane = getResolvedEmbeddingLane();
|
|
2208
|
+
return defaultEmbeddingModelFor(lane.baseUrl, lane.model);
|
|
2209
|
+
}
|
|
2210
|
+
function defaultEmbeddingModelFor(baseUrl, explicitModel) {
|
|
2211
|
+
if (explicitModel) return explicitModel;
|
|
2212
|
+
try {
|
|
2213
|
+
const url = baseUrl ? new URL(baseUrl) : null;
|
|
2214
|
+
if (url && (url.hostname === "openrouter.ai" || url.hostname.endsWith(".openrouter.ai"))) {
|
|
2215
|
+
return "qwen/qwen3-embedding-8b";
|
|
2216
|
+
}
|
|
2217
|
+
} catch {
|
|
2218
|
+
}
|
|
2219
|
+
return "text-embedding-3-small";
|
|
2207
2220
|
}
|
|
2208
2221
|
function getEmbeddingDimensions() {
|
|
2209
2222
|
return getResolvedEmbeddingLane().dimensions ?? null;
|
|
@@ -2742,6 +2755,8 @@ function getPreferredBatchSize(config) {
|
|
|
2742
2755
|
}
|
|
2743
2756
|
function parseBatchLimit(error) {
|
|
2744
2757
|
if (!(error instanceof Error)) return null;
|
|
2758
|
+
const status = error instanceof EmbeddingAPIError ? error.status : Number(error.message.match(/Embedding API error \((\d{3})\)/i)?.[1] ?? 0);
|
|
2759
|
+
if (status > 0 && status !== 400) return null;
|
|
2745
2760
|
const explicit = error.message.match(/should not be larger than\s+(\d+)/i);
|
|
2746
2761
|
if (explicit) return parseInt(explicit[1], 10);
|
|
2747
2762
|
if (/batch size/i.test(error.message)) {
|
|
@@ -2754,37 +2769,99 @@ async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0, nati
|
|
|
2754
2769
|
const controller = new AbortController();
|
|
2755
2770
|
const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) ? Math.max(100, Math.min(Math.floor(options.timeoutMs), 6e4)) : 1e4;
|
|
2756
2771
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
2772
|
+
const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
|
|
2757
2773
|
let response;
|
|
2758
2774
|
try {
|
|
2759
2775
|
response = await fetch(url, {
|
|
2760
2776
|
method: "POST",
|
|
2761
2777
|
headers: nativeGemini ? { "Content-Type": "application/json", "x-goog-api-key": apiKey } : { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` },
|
|
2762
2778
|
body: JSON.stringify(body),
|
|
2763
|
-
signal
|
|
2779
|
+
signal
|
|
2764
2780
|
});
|
|
2765
2781
|
} catch (err) {
|
|
2766
2782
|
clearTimeout(timeout);
|
|
2767
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
2783
|
+
if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
|
|
2768
2784
|
throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
|
|
2769
2785
|
}
|
|
2770
2786
|
throw err;
|
|
2771
2787
|
}
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2788
|
+
try {
|
|
2789
|
+
if (response.ok) {
|
|
2790
|
+
return await raceWithAbort(
|
|
2791
|
+
response.json(),
|
|
2792
|
+
signal
|
|
2793
|
+
);
|
|
2794
|
+
}
|
|
2795
|
+
if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
|
|
2796
|
+
const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
2797
|
+
const retryAfter = response.headers.get("retry-after");
|
|
2798
|
+
const parsedRetryAfter = retryAfter ? parseInt(retryAfter, 10) * 1e3 : NaN;
|
|
2799
|
+
const waitMs = Number.isFinite(parsedRetryAfter) ? parsedRetryAfter : delay2;
|
|
2800
|
+
console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
|
|
2801
|
+
await waitForRetry(waitMs, options.signal);
|
|
2802
|
+
return fetchWithRetry(url, apiKey, body, options, attempt + 1, nativeGemini);
|
|
2803
|
+
}
|
|
2804
|
+
const errorText2 = await raceWithAbort(
|
|
2805
|
+
response.text().catch(() => "unknown error"),
|
|
2806
|
+
signal
|
|
2807
|
+
);
|
|
2808
|
+
throw new EmbeddingAPIError(response.status, errorText2);
|
|
2809
|
+
} catch (err) {
|
|
2810
|
+
if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
|
|
2811
|
+
throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
|
|
2812
|
+
}
|
|
2813
|
+
throw err;
|
|
2814
|
+
} finally {
|
|
2815
|
+
clearTimeout(timeout);
|
|
2775
2816
|
}
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2817
|
+
}
|
|
2818
|
+
function abortError() {
|
|
2819
|
+
const error = new Error("Embedding API request aborted");
|
|
2820
|
+
error.name = "AbortError";
|
|
2821
|
+
return error;
|
|
2822
|
+
}
|
|
2823
|
+
function raceWithAbort(promise, signal) {
|
|
2824
|
+
if (signal.aborted) return Promise.reject(abortError());
|
|
2825
|
+
return new Promise((resolve, reject) => {
|
|
2826
|
+
const onAbort = () => {
|
|
2827
|
+
signal.removeEventListener("abort", onAbort);
|
|
2828
|
+
reject(abortError());
|
|
2829
|
+
};
|
|
2830
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2831
|
+
promise.then(
|
|
2832
|
+
(value) => {
|
|
2833
|
+
signal.removeEventListener("abort", onAbort);
|
|
2834
|
+
resolve(value);
|
|
2835
|
+
},
|
|
2836
|
+
(error) => {
|
|
2837
|
+
signal.removeEventListener("abort", onAbort);
|
|
2838
|
+
reject(error);
|
|
2839
|
+
}
|
|
2840
|
+
);
|
|
2841
|
+
});
|
|
2842
|
+
}
|
|
2843
|
+
async function waitForRetry(waitMs, signal) {
|
|
2844
|
+
if (!signal) {
|
|
2781
2845
|
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
2782
|
-
return
|
|
2846
|
+
return;
|
|
2783
2847
|
}
|
|
2784
|
-
const
|
|
2785
|
-
throw new Error(
|
|
2848
|
+
const abortSignal = signal;
|
|
2849
|
+
if (abortSignal.aborted) throw new Error("Embedding API retry aborted");
|
|
2850
|
+
await new Promise((resolve, reject) => {
|
|
2851
|
+
const timer = setTimeout(done, waitMs);
|
|
2852
|
+
const onAbort = () => {
|
|
2853
|
+
clearTimeout(timer);
|
|
2854
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
2855
|
+
reject(new Error("Embedding API retry aborted"));
|
|
2856
|
+
};
|
|
2857
|
+
function done() {
|
|
2858
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
2859
|
+
resolve();
|
|
2860
|
+
}
|
|
2861
|
+
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
2862
|
+
});
|
|
2786
2863
|
}
|
|
2787
|
-
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, APIEmbeddingProvider;
|
|
2864
|
+
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;
|
|
2788
2865
|
var init_api_provider = __esm({
|
|
2789
2866
|
"src/embedding/api-provider.ts"() {
|
|
2790
2867
|
"use strict";
|
|
@@ -2803,6 +2880,14 @@ var init_api_provider = __esm({
|
|
|
2803
2880
|
DASHSCOPE_MAX_BATCH_SIZE = 10;
|
|
2804
2881
|
MAX_RETRIES2 = 3;
|
|
2805
2882
|
BASE_DELAY_MS = 500;
|
|
2883
|
+
EmbeddingAPIError = class extends Error {
|
|
2884
|
+
status;
|
|
2885
|
+
constructor(status, detail) {
|
|
2886
|
+
super(`Embedding API error (${status}): ${detail}`);
|
|
2887
|
+
this.name = "EmbeddingAPIError";
|
|
2888
|
+
this.status = status;
|
|
2889
|
+
}
|
|
2890
|
+
};
|
|
2806
2891
|
APIEmbeddingProvider = class _APIEmbeddingProvider {
|
|
2807
2892
|
name;
|
|
2808
2893
|
dimensions;
|
|
@@ -3014,15 +3099,14 @@ var init_api_provider = __esm({
|
|
|
3014
3099
|
}
|
|
3015
3100
|
} catch (error) {
|
|
3016
3101
|
const providerLimit = parseBatchLimit(error);
|
|
3017
|
-
|
|
3018
|
-
if (chunkTexts.length > 1 && fallbackSize < chunkTexts.length) {
|
|
3102
|
+
if (providerLimit !== null && chunkTexts.length > 1 && providerLimit < chunkTexts.length) {
|
|
3019
3103
|
console.error(
|
|
3020
|
-
`[memorix] Embedding batch too large for provider, retrying in chunks of ${
|
|
3104
|
+
`[memorix] Embedding batch too large for provider, retrying in chunks of ${providerLimit}`
|
|
3021
3105
|
);
|
|
3022
|
-
for (let start = 0; start < chunkTexts.length; start +=
|
|
3106
|
+
for (let start = 0; start < chunkTexts.length; start += providerLimit) {
|
|
3023
3107
|
await processChunk(
|
|
3024
|
-
chunkTexts.slice(start, start +
|
|
3025
|
-
chunkIndices.slice(start, start +
|
|
3108
|
+
chunkTexts.slice(start, start + providerLimit),
|
|
3109
|
+
chunkIndices.slice(start, start + providerLimit)
|
|
3026
3110
|
);
|
|
3027
3111
|
}
|
|
3028
3112
|
return;
|
|
@@ -3886,22 +3970,23 @@ function getLLMConfig() {
|
|
|
3886
3970
|
function setLLMConfig(config) {
|
|
3887
3971
|
currentConfig = config;
|
|
3888
3972
|
}
|
|
3889
|
-
async function callLLM(systemPrompt, userMessage) {
|
|
3973
|
+
async function callLLM(systemPrompt, userMessage, signal) {
|
|
3890
3974
|
if (!currentConfig) {
|
|
3891
3975
|
throw new Error("LLM not configured. Set MEMORIX_LLM_API_KEY or OPENAI_API_KEY.");
|
|
3892
3976
|
}
|
|
3893
3977
|
if (currentConfig.provider === "anthropic") {
|
|
3894
|
-
return callAnthropic(systemPrompt, userMessage);
|
|
3978
|
+
return callAnthropic(systemPrompt, userMessage, signal);
|
|
3895
3979
|
}
|
|
3896
|
-
return callOpenAICompatible(systemPrompt, userMessage);
|
|
3980
|
+
return callOpenAICompatible(systemPrompt, userMessage, signal);
|
|
3897
3981
|
}
|
|
3898
|
-
async function callOpenAICompatible(systemPrompt, userMessage) {
|
|
3982
|
+
async function callOpenAICompatible(systemPrompt, userMessage, signal) {
|
|
3899
3983
|
const config = currentConfig;
|
|
3900
3984
|
let base = config.baseUrl.replace(/\/+$/, "");
|
|
3901
3985
|
if (!base.endsWith("/v1")) base += "/v1";
|
|
3902
3986
|
const url = `${base}/chat/completions`;
|
|
3987
|
+
const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
|
|
3903
3988
|
const response = await fetch(url, {
|
|
3904
|
-
signal:
|
|
3989
|
+
signal: fetchSignal,
|
|
3905
3990
|
method: "POST",
|
|
3906
3991
|
headers: {
|
|
3907
3992
|
"Content-Type": "application/json",
|
|
@@ -3918,10 +4003,10 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
|
|
|
3918
4003
|
})
|
|
3919
4004
|
});
|
|
3920
4005
|
if (!response.ok) {
|
|
3921
|
-
const error = await readResponseText(response,
|
|
4006
|
+
const error = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
|
|
3922
4007
|
throw new Error(`LLM API error (${response.status}): ${error}`);
|
|
3923
4008
|
}
|
|
3924
|
-
const data = await readResponseJson(response);
|
|
4009
|
+
const data = await readResponseJson(response, signal);
|
|
3925
4010
|
return {
|
|
3926
4011
|
content: data.choices[0]?.message?.content ?? "",
|
|
3927
4012
|
usage: data.usage ? {
|
|
@@ -3930,11 +4015,12 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
|
|
|
3930
4015
|
} : void 0
|
|
3931
4016
|
};
|
|
3932
4017
|
}
|
|
3933
|
-
async function callAnthropic(systemPrompt, userMessage) {
|
|
4018
|
+
async function callAnthropic(systemPrompt, userMessage, signal) {
|
|
3934
4019
|
const config = currentConfig;
|
|
3935
4020
|
const url = `${config.baseUrl}/messages`;
|
|
4021
|
+
const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
|
|
3936
4022
|
const response = await fetch(url, {
|
|
3937
|
-
signal:
|
|
4023
|
+
signal: fetchSignal,
|
|
3938
4024
|
method: "POST",
|
|
3939
4025
|
headers: {
|
|
3940
4026
|
"Content-Type": "application/json",
|
|
@@ -3952,10 +4038,10 @@ async function callAnthropic(systemPrompt, userMessage) {
|
|
|
3952
4038
|
})
|
|
3953
4039
|
});
|
|
3954
4040
|
if (!response.ok) {
|
|
3955
|
-
const error = await readResponseText(response,
|
|
4041
|
+
const error = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
|
|
3956
4042
|
throw new Error(`Anthropic API error (${response.status}): ${error}`);
|
|
3957
4043
|
}
|
|
3958
|
-
const data = await readResponseJson(response);
|
|
4044
|
+
const data = await readResponseJson(response, signal);
|
|
3959
4045
|
return {
|
|
3960
4046
|
content: data.content[0]?.text ?? "",
|
|
3961
4047
|
usage: data.usage ? {
|
|
@@ -4460,6 +4546,57 @@ function withTimeout(promise, ms, label) {
|
|
|
4460
4546
|
);
|
|
4461
4547
|
});
|
|
4462
4548
|
}
|
|
4549
|
+
function withTimeoutSignal(operation, ms, label, parentSignal) {
|
|
4550
|
+
const controller = new AbortController();
|
|
4551
|
+
const signal = parentSignal ? AbortSignal.any([parentSignal, controller.signal]) : controller.signal;
|
|
4552
|
+
return new Promise((resolve, reject) => {
|
|
4553
|
+
let settled = false;
|
|
4554
|
+
let timedOut = false;
|
|
4555
|
+
let timer;
|
|
4556
|
+
const cleanup = () => {
|
|
4557
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
4558
|
+
parentSignal?.removeEventListener("abort", onParentAbort);
|
|
4559
|
+
};
|
|
4560
|
+
const onParentAbort = () => {
|
|
4561
|
+
if (settled || timedOut) return;
|
|
4562
|
+
settled = true;
|
|
4563
|
+
cleanup();
|
|
4564
|
+
reject(parentSignal?.reason ?? new Error(`${label} aborted`));
|
|
4565
|
+
};
|
|
4566
|
+
parentSignal?.addEventListener("abort", onParentAbort, { once: true });
|
|
4567
|
+
timer = setTimeout(() => {
|
|
4568
|
+
if (settled) return;
|
|
4569
|
+
timedOut = true;
|
|
4570
|
+
controller.abort(new Error(`${label} timed out after ${ms}ms`));
|
|
4571
|
+
settled = true;
|
|
4572
|
+
cleanup();
|
|
4573
|
+
reject(new Error(`${label} timed out after ${ms}ms`));
|
|
4574
|
+
}, ms);
|
|
4575
|
+
let pending;
|
|
4576
|
+
try {
|
|
4577
|
+
pending = operation(signal);
|
|
4578
|
+
} catch (error) {
|
|
4579
|
+
settled = true;
|
|
4580
|
+
cleanup();
|
|
4581
|
+
reject(error);
|
|
4582
|
+
return;
|
|
4583
|
+
}
|
|
4584
|
+
pending.then(
|
|
4585
|
+
(value) => {
|
|
4586
|
+
if (settled) return;
|
|
4587
|
+
settled = true;
|
|
4588
|
+
cleanup();
|
|
4589
|
+
resolve(value);
|
|
4590
|
+
},
|
|
4591
|
+
(error) => {
|
|
4592
|
+
if (settled) return;
|
|
4593
|
+
settled = true;
|
|
4594
|
+
cleanup();
|
|
4595
|
+
reject(timedOut ? new Error(`${label} timed out after ${ms}ms`) : error);
|
|
4596
|
+
}
|
|
4597
|
+
);
|
|
4598
|
+
});
|
|
4599
|
+
}
|
|
4463
4600
|
var init_timeout = __esm({
|
|
4464
4601
|
"src/timeout.ts"() {
|
|
4465
4602
|
"use strict";
|
|
@@ -5324,16 +5461,18 @@ async function searchObservations(options) {
|
|
|
5324
5461
|
);
|
|
5325
5462
|
} else {
|
|
5326
5463
|
const EMBEDDING_TIMEOUT_MS = 15e3;
|
|
5327
|
-
queryVector = await
|
|
5328
|
-
provider2.embedInput ? provider2.embedInput(
|
|
5464
|
+
queryVector = await withTimeoutSignal(
|
|
5465
|
+
(signal) => provider2.embedInput ? provider2.embedInput(
|
|
5329
5466
|
{ modality: "text", text: expandedEmbeddingQuery },
|
|
5330
|
-
{ intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false }
|
|
5467
|
+
{ intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false, signal }
|
|
5331
5468
|
) : provider2.embed(expandedEmbeddingQuery, {
|
|
5332
5469
|
timeoutMs: EMBEDDING_TIMEOUT_MS,
|
|
5333
|
-
retry: false
|
|
5470
|
+
retry: false,
|
|
5471
|
+
signal
|
|
5334
5472
|
}),
|
|
5335
5473
|
EMBEDDING_TIMEOUT_MS,
|
|
5336
|
-
"Embedding"
|
|
5474
|
+
"Embedding",
|
|
5475
|
+
options.signal
|
|
5337
5476
|
);
|
|
5338
5477
|
mark("embedding");
|
|
5339
5478
|
const cjkRatio = (originalQuery.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length / originalQuery.length;
|
|
@@ -145231,7 +145370,13 @@ async function maintainLongTermMemories(input) {
|
|
|
145231
145370
|
}
|
|
145232
145371
|
for (const group of groups.values()) {
|
|
145233
145372
|
if (group.length < 2) continue;
|
|
145234
|
-
group.sort((a, b) =>
|
|
145373
|
+
group.sort((a, b) => {
|
|
145374
|
+
const byCreatedAt = Date.parse(b.createdAt) - Date.parse(a.createdAt);
|
|
145375
|
+
if (byCreatedAt !== 0) return byCreatedAt;
|
|
145376
|
+
const byUpdatedAt = Date.parse(b.updatedAt) - Date.parse(a.updatedAt);
|
|
145377
|
+
if (byUpdatedAt !== 0) return byUpdatedAt;
|
|
145378
|
+
return b.id.localeCompare(a.id);
|
|
145379
|
+
});
|
|
145235
145380
|
const newest = group[0];
|
|
145236
145381
|
for (const older of group.slice(1)) {
|
|
145237
145382
|
try {
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -180,6 +180,18 @@ Then set `OPENROUTER_API_KEY` in your shell, user environment, or `.env`. You
|
|
|
180
180
|
can still set `MEMORIX_EMBEDDING_API_KEY` when you want an explicit embedding
|
|
181
181
|
key override; it takes priority over `OPENROUTER_API_KEY`.
|
|
182
182
|
|
|
183
|
+
When `base_url` points at OpenRouter and no `model` is configured, Memorix
|
|
184
|
+
defaults to `qwen/qwen3-embedding-8b` (4096 dimensions) instead of the
|
|
185
|
+
OpenAI-only default. The equivalent env-var form is
|
|
186
|
+
`MEMORIX_EMBEDDING=api`, `MEMORIX_EMBEDDING_BASE_URL=https://openrouter.ai/api/v1`,
|
|
187
|
+
`MEMORIX_EMBEDDING_MODEL=qwen/qwen3-embedding-8b`.
|
|
188
|
+
|
|
189
|
+
Image analysis (visual description for ingested images) runs on the LLM lane
|
|
190
|
+
through an OpenAI-compatible vision endpoint, so it can also use OpenRouter
|
|
191
|
+
with a multimodal model — for example `qwen/qwen3-vl-8b-instruct` via
|
|
192
|
+
`MEMORIX_LLM_BASE_URL=https://openrouter.ai/api/v1` and
|
|
193
|
+
`MEMORIX_LLM_MODEL=qwen/qwen3-vl-8b-instruct`.
|
|
194
|
+
|
|
183
195
|
### Controlled MiniMax media
|
|
184
196
|
|
|
185
197
|
MiniMax generation is separate from the text embedding lane. Set one of these
|