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
package/dist/sdk.js
CHANGED
|
@@ -3377,6 +3377,7 @@ var init_resolved_config = __esm({
|
|
|
3377
3377
|
// src/config.ts
|
|
3378
3378
|
var config_exports = {};
|
|
3379
3379
|
__export(config_exports, {
|
|
3380
|
+
defaultEmbeddingModelFor: () => defaultEmbeddingModelFor,
|
|
3380
3381
|
getAgentLLMApiKey: () => getAgentLLMApiKey,
|
|
3381
3382
|
getAgentLLMBaseUrl: () => getAgentLLMBaseUrl,
|
|
3382
3383
|
getAgentLLMModel: () => getAgentLLMModel,
|
|
@@ -3439,7 +3440,19 @@ function getEmbeddingBaseUrl() {
|
|
|
3439
3440
|
return getResolvedEmbeddingLane().baseUrl || "https://api.openai.com/v1";
|
|
3440
3441
|
}
|
|
3441
3442
|
function getEmbeddingModel() {
|
|
3442
|
-
|
|
3443
|
+
const lane = getResolvedEmbeddingLane();
|
|
3444
|
+
return defaultEmbeddingModelFor(lane.baseUrl, lane.model);
|
|
3445
|
+
}
|
|
3446
|
+
function defaultEmbeddingModelFor(baseUrl, explicitModel) {
|
|
3447
|
+
if (explicitModel) return explicitModel;
|
|
3448
|
+
try {
|
|
3449
|
+
const url = baseUrl ? new URL(baseUrl) : null;
|
|
3450
|
+
if (url && (url.hostname === "openrouter.ai" || url.hostname.endsWith(".openrouter.ai"))) {
|
|
3451
|
+
return "qwen/qwen3-embedding-8b";
|
|
3452
|
+
}
|
|
3453
|
+
} catch {
|
|
3454
|
+
}
|
|
3455
|
+
return "text-embedding-3-small";
|
|
3443
3456
|
}
|
|
3444
3457
|
function getEmbeddingDimensions() {
|
|
3445
3458
|
return getResolvedEmbeddingLane().dimensions ?? null;
|
|
@@ -3978,6 +3991,8 @@ function getPreferredBatchSize(config) {
|
|
|
3978
3991
|
}
|
|
3979
3992
|
function parseBatchLimit(error) {
|
|
3980
3993
|
if (!(error instanceof Error)) return null;
|
|
3994
|
+
const status = error instanceof EmbeddingAPIError ? error.status : Number(error.message.match(/Embedding API error \((\d{3})\)/i)?.[1] ?? 0);
|
|
3995
|
+
if (status > 0 && status !== 400) return null;
|
|
3981
3996
|
const explicit = error.message.match(/should not be larger than\s+(\d+)/i);
|
|
3982
3997
|
if (explicit) return parseInt(explicit[1], 10);
|
|
3983
3998
|
if (/batch size/i.test(error.message)) {
|
|
@@ -3990,37 +4005,99 @@ async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0, nati
|
|
|
3990
4005
|
const controller = new AbortController();
|
|
3991
4006
|
const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) ? Math.max(100, Math.min(Math.floor(options.timeoutMs), 6e4)) : 1e4;
|
|
3992
4007
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
4008
|
+
const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
|
|
3993
4009
|
let response;
|
|
3994
4010
|
try {
|
|
3995
4011
|
response = await fetch(url, {
|
|
3996
4012
|
method: "POST",
|
|
3997
4013
|
headers: nativeGemini ? { "Content-Type": "application/json", "x-goog-api-key": apiKey } : { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` },
|
|
3998
4014
|
body: JSON.stringify(body),
|
|
3999
|
-
signal
|
|
4015
|
+
signal
|
|
4000
4016
|
});
|
|
4001
4017
|
} catch (err) {
|
|
4002
4018
|
clearTimeout(timeout);
|
|
4003
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
4019
|
+
if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
|
|
4004
4020
|
throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
|
|
4005
4021
|
}
|
|
4006
4022
|
throw err;
|
|
4007
4023
|
}
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4024
|
+
try {
|
|
4025
|
+
if (response.ok) {
|
|
4026
|
+
return await raceWithAbort(
|
|
4027
|
+
response.json(),
|
|
4028
|
+
signal
|
|
4029
|
+
);
|
|
4030
|
+
}
|
|
4031
|
+
if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
|
|
4032
|
+
const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
4033
|
+
const retryAfter = response.headers.get("retry-after");
|
|
4034
|
+
const parsedRetryAfter = retryAfter ? parseInt(retryAfter, 10) * 1e3 : NaN;
|
|
4035
|
+
const waitMs = Number.isFinite(parsedRetryAfter) ? parsedRetryAfter : delay2;
|
|
4036
|
+
console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
|
|
4037
|
+
await waitForRetry(waitMs, options.signal);
|
|
4038
|
+
return fetchWithRetry(url, apiKey, body, options, attempt + 1, nativeGemini);
|
|
4039
|
+
}
|
|
4040
|
+
const errorText2 = await raceWithAbort(
|
|
4041
|
+
response.text().catch(() => "unknown error"),
|
|
4042
|
+
signal
|
|
4043
|
+
);
|
|
4044
|
+
throw new EmbeddingAPIError(response.status, errorText2);
|
|
4045
|
+
} catch (err) {
|
|
4046
|
+
if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
|
|
4047
|
+
throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
|
|
4048
|
+
}
|
|
4049
|
+
throw err;
|
|
4050
|
+
} finally {
|
|
4051
|
+
clearTimeout(timeout);
|
|
4011
4052
|
}
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4053
|
+
}
|
|
4054
|
+
function abortError() {
|
|
4055
|
+
const error = new Error("Embedding API request aborted");
|
|
4056
|
+
error.name = "AbortError";
|
|
4057
|
+
return error;
|
|
4058
|
+
}
|
|
4059
|
+
function raceWithAbort(promise, signal) {
|
|
4060
|
+
if (signal.aborted) return Promise.reject(abortError());
|
|
4061
|
+
return new Promise((resolve2, reject) => {
|
|
4062
|
+
const onAbort = () => {
|
|
4063
|
+
signal.removeEventListener("abort", onAbort);
|
|
4064
|
+
reject(abortError());
|
|
4065
|
+
};
|
|
4066
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
4067
|
+
promise.then(
|
|
4068
|
+
(value) => {
|
|
4069
|
+
signal.removeEventListener("abort", onAbort);
|
|
4070
|
+
resolve2(value);
|
|
4071
|
+
},
|
|
4072
|
+
(error) => {
|
|
4073
|
+
signal.removeEventListener("abort", onAbort);
|
|
4074
|
+
reject(error);
|
|
4075
|
+
}
|
|
4076
|
+
);
|
|
4077
|
+
});
|
|
4078
|
+
}
|
|
4079
|
+
async function waitForRetry(waitMs, signal) {
|
|
4080
|
+
if (!signal) {
|
|
4017
4081
|
await new Promise((resolve2) => setTimeout(resolve2, waitMs));
|
|
4018
|
-
return
|
|
4082
|
+
return;
|
|
4019
4083
|
}
|
|
4020
|
-
const
|
|
4021
|
-
throw new Error(
|
|
4084
|
+
const abortSignal = signal;
|
|
4085
|
+
if (abortSignal.aborted) throw new Error("Embedding API retry aborted");
|
|
4086
|
+
await new Promise((resolve2, reject) => {
|
|
4087
|
+
const timer = setTimeout(done, waitMs);
|
|
4088
|
+
const onAbort = () => {
|
|
4089
|
+
clearTimeout(timer);
|
|
4090
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
4091
|
+
reject(new Error("Embedding API retry aborted"));
|
|
4092
|
+
};
|
|
4093
|
+
function done() {
|
|
4094
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
4095
|
+
resolve2();
|
|
4096
|
+
}
|
|
4097
|
+
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
4098
|
+
});
|
|
4022
4099
|
}
|
|
4023
|
-
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;
|
|
4100
|
+
var cache3, MAX_CACHE_SIZE3, diskCacheDirty2, diskSaveTimer, diskCacheLoaded, diskCacheLoadPromise, MAX_INPUT_CHARS, MAX_CONCURRENCY, DISK_SAVE_DEBOUNCE_MS, DEFAULT_MAX_BATCH_SIZE, DASHSCOPE_MAX_BATCH_SIZE, MAX_RETRIES2, BASE_DELAY_MS, EmbeddingAPIError, APIEmbeddingProvider;
|
|
4024
4101
|
var init_api_provider = __esm({
|
|
4025
4102
|
"src/embedding/api-provider.ts"() {
|
|
4026
4103
|
"use strict";
|
|
@@ -4039,6 +4116,14 @@ var init_api_provider = __esm({
|
|
|
4039
4116
|
DASHSCOPE_MAX_BATCH_SIZE = 10;
|
|
4040
4117
|
MAX_RETRIES2 = 3;
|
|
4041
4118
|
BASE_DELAY_MS = 500;
|
|
4119
|
+
EmbeddingAPIError = class extends Error {
|
|
4120
|
+
status;
|
|
4121
|
+
constructor(status, detail) {
|
|
4122
|
+
super(`Embedding API error (${status}): ${detail}`);
|
|
4123
|
+
this.name = "EmbeddingAPIError";
|
|
4124
|
+
this.status = status;
|
|
4125
|
+
}
|
|
4126
|
+
};
|
|
4042
4127
|
APIEmbeddingProvider = class _APIEmbeddingProvider {
|
|
4043
4128
|
name;
|
|
4044
4129
|
dimensions;
|
|
@@ -4250,15 +4335,14 @@ var init_api_provider = __esm({
|
|
|
4250
4335
|
}
|
|
4251
4336
|
} catch (error) {
|
|
4252
4337
|
const providerLimit = parseBatchLimit(error);
|
|
4253
|
-
|
|
4254
|
-
if (chunkTexts.length > 1 && fallbackSize < chunkTexts.length) {
|
|
4338
|
+
if (providerLimit !== null && chunkTexts.length > 1 && providerLimit < chunkTexts.length) {
|
|
4255
4339
|
console.error(
|
|
4256
|
-
`[memorix] Embedding batch too large for provider, retrying in chunks of ${
|
|
4340
|
+
`[memorix] Embedding batch too large for provider, retrying in chunks of ${providerLimit}`
|
|
4257
4341
|
);
|
|
4258
|
-
for (let start = 0; start < chunkTexts.length; start +=
|
|
4342
|
+
for (let start = 0; start < chunkTexts.length; start += providerLimit) {
|
|
4259
4343
|
await processChunk(
|
|
4260
|
-
chunkTexts.slice(start, start +
|
|
4261
|
-
chunkIndices.slice(start, start +
|
|
4344
|
+
chunkTexts.slice(start, start + providerLimit),
|
|
4345
|
+
chunkIndices.slice(start, start + providerLimit)
|
|
4262
4346
|
);
|
|
4263
4347
|
}
|
|
4264
4348
|
return;
|
|
@@ -5122,22 +5206,23 @@ function getLLMConfig() {
|
|
|
5122
5206
|
function setLLMConfig(config) {
|
|
5123
5207
|
currentConfig = config;
|
|
5124
5208
|
}
|
|
5125
|
-
async function callLLM(systemPrompt, userMessage) {
|
|
5209
|
+
async function callLLM(systemPrompt, userMessage, signal) {
|
|
5126
5210
|
if (!currentConfig) {
|
|
5127
5211
|
throw new Error("LLM not configured. Set MEMORIX_LLM_API_KEY or OPENAI_API_KEY.");
|
|
5128
5212
|
}
|
|
5129
5213
|
if (currentConfig.provider === "anthropic") {
|
|
5130
|
-
return callAnthropic(systemPrompt, userMessage);
|
|
5214
|
+
return callAnthropic(systemPrompt, userMessage, signal);
|
|
5131
5215
|
}
|
|
5132
|
-
return callOpenAICompatible(systemPrompt, userMessage);
|
|
5216
|
+
return callOpenAICompatible(systemPrompt, userMessage, signal);
|
|
5133
5217
|
}
|
|
5134
|
-
async function callOpenAICompatible(systemPrompt, userMessage) {
|
|
5218
|
+
async function callOpenAICompatible(systemPrompt, userMessage, signal) {
|
|
5135
5219
|
const config = currentConfig;
|
|
5136
5220
|
let base = config.baseUrl.replace(/\/+$/, "");
|
|
5137
5221
|
if (!base.endsWith("/v1")) base += "/v1";
|
|
5138
5222
|
const url = `${base}/chat/completions`;
|
|
5223
|
+
const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
|
|
5139
5224
|
const response = await fetch(url, {
|
|
5140
|
-
signal:
|
|
5225
|
+
signal: fetchSignal,
|
|
5141
5226
|
method: "POST",
|
|
5142
5227
|
headers: {
|
|
5143
5228
|
"Content-Type": "application/json",
|
|
@@ -5154,10 +5239,10 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
|
|
|
5154
5239
|
})
|
|
5155
5240
|
});
|
|
5156
5241
|
if (!response.ok) {
|
|
5157
|
-
const error = await readResponseText(response,
|
|
5242
|
+
const error = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
|
|
5158
5243
|
throw new Error(`LLM API error (${response.status}): ${error}`);
|
|
5159
5244
|
}
|
|
5160
|
-
const data = await readResponseJson(response);
|
|
5245
|
+
const data = await readResponseJson(response, signal);
|
|
5161
5246
|
return {
|
|
5162
5247
|
content: data.choices[0]?.message?.content ?? "",
|
|
5163
5248
|
usage: data.usage ? {
|
|
@@ -5166,11 +5251,12 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
|
|
|
5166
5251
|
} : void 0
|
|
5167
5252
|
};
|
|
5168
5253
|
}
|
|
5169
|
-
async function callAnthropic(systemPrompt, userMessage) {
|
|
5254
|
+
async function callAnthropic(systemPrompt, userMessage, signal) {
|
|
5170
5255
|
const config = currentConfig;
|
|
5171
5256
|
const url = `${config.baseUrl}/messages`;
|
|
5257
|
+
const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
|
|
5172
5258
|
const response = await fetch(url, {
|
|
5173
|
-
signal:
|
|
5259
|
+
signal: fetchSignal,
|
|
5174
5260
|
method: "POST",
|
|
5175
5261
|
headers: {
|
|
5176
5262
|
"Content-Type": "application/json",
|
|
@@ -5188,10 +5274,10 @@ async function callAnthropic(systemPrompt, userMessage) {
|
|
|
5188
5274
|
})
|
|
5189
5275
|
});
|
|
5190
5276
|
if (!response.ok) {
|
|
5191
|
-
const error = await readResponseText(response,
|
|
5277
|
+
const error = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
|
|
5192
5278
|
throw new Error(`Anthropic API error (${response.status}): ${error}`);
|
|
5193
5279
|
}
|
|
5194
|
-
const data = await readResponseJson(response);
|
|
5280
|
+
const data = await readResponseJson(response, signal);
|
|
5195
5281
|
return {
|
|
5196
5282
|
content: data.content[0]?.text ?? "",
|
|
5197
5283
|
usage: data.usage ? {
|
|
@@ -5696,6 +5782,57 @@ function withTimeout(promise, ms, label) {
|
|
|
5696
5782
|
);
|
|
5697
5783
|
});
|
|
5698
5784
|
}
|
|
5785
|
+
function withTimeoutSignal(operation, ms, label, parentSignal) {
|
|
5786
|
+
const controller = new AbortController();
|
|
5787
|
+
const signal = parentSignal ? AbortSignal.any([parentSignal, controller.signal]) : controller.signal;
|
|
5788
|
+
return new Promise((resolve2, reject) => {
|
|
5789
|
+
let settled = false;
|
|
5790
|
+
let timedOut = false;
|
|
5791
|
+
let timer;
|
|
5792
|
+
const cleanup = () => {
|
|
5793
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
5794
|
+
parentSignal?.removeEventListener("abort", onParentAbort);
|
|
5795
|
+
};
|
|
5796
|
+
const onParentAbort = () => {
|
|
5797
|
+
if (settled || timedOut) return;
|
|
5798
|
+
settled = true;
|
|
5799
|
+
cleanup();
|
|
5800
|
+
reject(parentSignal?.reason ?? new Error(`${label} aborted`));
|
|
5801
|
+
};
|
|
5802
|
+
parentSignal?.addEventListener("abort", onParentAbort, { once: true });
|
|
5803
|
+
timer = setTimeout(() => {
|
|
5804
|
+
if (settled) return;
|
|
5805
|
+
timedOut = true;
|
|
5806
|
+
controller.abort(new Error(`${label} timed out after ${ms}ms`));
|
|
5807
|
+
settled = true;
|
|
5808
|
+
cleanup();
|
|
5809
|
+
reject(new Error(`${label} timed out after ${ms}ms`));
|
|
5810
|
+
}, ms);
|
|
5811
|
+
let pending;
|
|
5812
|
+
try {
|
|
5813
|
+
pending = operation(signal);
|
|
5814
|
+
} catch (error) {
|
|
5815
|
+
settled = true;
|
|
5816
|
+
cleanup();
|
|
5817
|
+
reject(error);
|
|
5818
|
+
return;
|
|
5819
|
+
}
|
|
5820
|
+
pending.then(
|
|
5821
|
+
(value) => {
|
|
5822
|
+
if (settled) return;
|
|
5823
|
+
settled = true;
|
|
5824
|
+
cleanup();
|
|
5825
|
+
resolve2(value);
|
|
5826
|
+
},
|
|
5827
|
+
(error) => {
|
|
5828
|
+
if (settled) return;
|
|
5829
|
+
settled = true;
|
|
5830
|
+
cleanup();
|
|
5831
|
+
reject(timedOut ? new Error(`${label} timed out after ${ms}ms`) : error);
|
|
5832
|
+
}
|
|
5833
|
+
);
|
|
5834
|
+
});
|
|
5835
|
+
}
|
|
5699
5836
|
var init_timeout = __esm({
|
|
5700
5837
|
"src/timeout.ts"() {
|
|
5701
5838
|
"use strict";
|
|
@@ -6560,16 +6697,18 @@ async function searchObservations(options) {
|
|
|
6560
6697
|
);
|
|
6561
6698
|
} else {
|
|
6562
6699
|
const EMBEDDING_TIMEOUT_MS = 15e3;
|
|
6563
|
-
queryVector = await
|
|
6564
|
-
provider2.embedInput ? provider2.embedInput(
|
|
6700
|
+
queryVector = await withTimeoutSignal(
|
|
6701
|
+
(signal) => provider2.embedInput ? provider2.embedInput(
|
|
6565
6702
|
{ modality: "text", text: expandedEmbeddingQuery },
|
|
6566
|
-
{ intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false }
|
|
6703
|
+
{ intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false, signal }
|
|
6567
6704
|
) : provider2.embed(expandedEmbeddingQuery, {
|
|
6568
6705
|
timeoutMs: EMBEDDING_TIMEOUT_MS,
|
|
6569
|
-
retry: false
|
|
6706
|
+
retry: false,
|
|
6707
|
+
signal
|
|
6570
6708
|
}),
|
|
6571
6709
|
EMBEDDING_TIMEOUT_MS,
|
|
6572
|
-
"Embedding"
|
|
6710
|
+
"Embedding",
|
|
6711
|
+
options.signal
|
|
6573
6712
|
);
|
|
6574
6713
|
mark("embedding");
|
|
6575
6714
|
const cjkRatio = (originalQuery.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length / originalQuery.length;
|
|
@@ -144989,7 +145128,13 @@ async function maintainLongTermMemories(input) {
|
|
|
144989
145128
|
}
|
|
144990
145129
|
for (const group of groups.values()) {
|
|
144991
145130
|
if (group.length < 2) continue;
|
|
144992
|
-
group.sort((a, b) =>
|
|
145131
|
+
group.sort((a, b) => {
|
|
145132
|
+
const byCreatedAt = Date.parse(b.createdAt) - Date.parse(a.createdAt);
|
|
145133
|
+
if (byCreatedAt !== 0) return byCreatedAt;
|
|
145134
|
+
const byUpdatedAt = Date.parse(b.updatedAt) - Date.parse(a.updatedAt);
|
|
145135
|
+
if (byUpdatedAt !== 0) return byUpdatedAt;
|
|
145136
|
+
return b.id.localeCompare(a.id);
|
|
145137
|
+
});
|
|
144993
145138
|
const newest = group[0];
|
|
144994
145139
|
for (const older of group.slice(1)) {
|
|
144995
145140
|
try {
|
|
@@ -151475,13 +151620,13 @@ function verifyType(declaredType, narrative, title) {
|
|
|
151475
151620
|
}
|
|
151476
151621
|
return { type: declaredType, corrected: false };
|
|
151477
151622
|
}
|
|
151478
|
-
async function extractFactsWithLLM(narrative, title, existingFacts) {
|
|
151623
|
+
async function extractFactsWithLLM(narrative, title, existingFacts, signal) {
|
|
151479
151624
|
try {
|
|
151480
151625
|
const { callLLM: callLLM2 } = await Promise.resolve().then(() => (init_provider2(), provider_exports2));
|
|
151481
151626
|
const input = `Title: ${title}
|
|
151482
151627
|
Content: ${narrative}${existingFacts.length > 0 ? `
|
|
151483
151628
|
Already known facts (don't repeat): ${existingFacts.join("; ")}` : ""}`;
|
|
151484
|
-
const response = await callLLM2(LLM_EXTRACT_PROMPT, input);
|
|
151629
|
+
const response = await callLLM2(LLM_EXTRACT_PROMPT, input, signal);
|
|
151485
151630
|
const text2 = response.content.trim();
|
|
151486
151631
|
const jsonMatch = text2.match(/\{[\s\S]*\}/);
|
|
151487
151632
|
if (!jsonMatch) return [];
|
|
@@ -151490,15 +151635,16 @@ Already known facts (don't repeat): ${existingFacts.join("; ")}` : ""}`;
|
|
|
151490
151635
|
if (!Array.isArray(facts)) return [];
|
|
151491
151636
|
const existingLower = new Set(existingFacts.map((f3) => f3.toLowerCase().trim()));
|
|
151492
151637
|
return facts.filter((f3) => typeof f3 === "string" && f3.length >= 5).filter((f3) => !existingLower.has(f3.toLowerCase().trim())).slice(0, 10);
|
|
151493
|
-
} catch {
|
|
151638
|
+
} catch (error) {
|
|
151639
|
+
if (signal?.aborted) throw error;
|
|
151494
151640
|
return [];
|
|
151495
151641
|
}
|
|
151496
151642
|
}
|
|
151497
|
-
async function runExtract(input, existingEntities, useLLM = false) {
|
|
151643
|
+
async function runExtract(input, existingEntities, useLLM = false, signal) {
|
|
151498
151644
|
const callerFacts = input.facts ?? [];
|
|
151499
151645
|
let extractedFacts;
|
|
151500
151646
|
if (useLLM) {
|
|
151501
|
-
extractedFacts = await extractFactsWithLLM(input.narrative, input.title, callerFacts);
|
|
151647
|
+
extractedFacts = await extractFactsWithLLM(input.narrative, input.title, callerFacts, signal);
|
|
151502
151648
|
if (extractedFacts.length === 0) {
|
|
151503
151649
|
extractedFacts = extractFacts2(input.narrative, callerFacts);
|
|
151504
151650
|
}
|
|
@@ -151718,7 +151864,7 @@ function mergeFacts2(oldFacts, newFacts) {
|
|
|
151718
151864
|
}
|
|
151719
151865
|
return merged;
|
|
151720
151866
|
}
|
|
151721
|
-
async function resolveWithLLM(extracted, hits, getObservation2) {
|
|
151867
|
+
async function resolveWithLLM(extracted, hits, getObservation2, signal) {
|
|
151722
151868
|
try {
|
|
151723
151869
|
const { callLLM: callLLM2 } = await Promise.resolve().then(() => (init_provider2(), provider_exports2));
|
|
151724
151870
|
const existingMemories = hits.slice(0, 5).map((h2, i2) => ({
|
|
@@ -151735,7 +151881,7 @@ Facts: ${extracted.facts.join("; ")}
|
|
|
151735
151881
|
|
|
151736
151882
|
EXISTING MEMORIES:
|
|
151737
151883
|
${existingMemories.map((m2) => `[ID:${m2.id}] ${m2.title} | ${m2.content} | Facts: ${m2.facts}`).join("\n")}`;
|
|
151738
|
-
const response = await callLLM2(LLM_RESOLVE_PROMPT, input);
|
|
151884
|
+
const response = await callLLM2(LLM_RESOLVE_PROMPT, input, signal);
|
|
151739
151885
|
const text2 = response.content.trim();
|
|
151740
151886
|
const jsonMatch = text2.match(/\{[\s\S]*\}/);
|
|
151741
151887
|
if (!jsonMatch) return null;
|
|
@@ -151772,7 +151918,8 @@ ${existingMemories.map((m2) => `[ID:${m2.id}] ${m2.title} | ${m2.content} | Fact
|
|
|
151772
151918
|
};
|
|
151773
151919
|
}
|
|
151774
151920
|
return null;
|
|
151775
|
-
} catch {
|
|
151921
|
+
} catch (error) {
|
|
151922
|
+
if (signal?.aborted) throw error;
|
|
151776
151923
|
return null;
|
|
151777
151924
|
}
|
|
151778
151925
|
}
|
|
@@ -151790,19 +151937,20 @@ function scoreCandidate(extracted, candidate) {
|
|
|
151790
151937
|
const contradiction = hasContradiction(candidate.narrative, extracted.narrative);
|
|
151791
151938
|
return { score, searchSimilarity, entityMatch, richer, contradiction };
|
|
151792
151939
|
}
|
|
151793
|
-
async function runResolve(extracted, projectId, searchMemories, getObservation2, useLLM = false) {
|
|
151940
|
+
async function runResolve(extracted, projectId, searchMemories, getObservation2, useLLM = false, signal) {
|
|
151794
151941
|
const query = `${extracted.title} ${extracted.narrative.substring(0, 200)}`;
|
|
151795
151942
|
let hits;
|
|
151796
151943
|
try {
|
|
151797
|
-
hits = await searchMemories(query, 5, projectId);
|
|
151798
|
-
} catch {
|
|
151944
|
+
hits = await searchMemories(query, 5, projectId, signal);
|
|
151945
|
+
} catch (error) {
|
|
151946
|
+
if (signal?.aborted) throw error;
|
|
151799
151947
|
return { action: "new", reason: "Search unavailable, defaulting to new" };
|
|
151800
151948
|
}
|
|
151801
151949
|
if (hits.length === 0) {
|
|
151802
151950
|
return { action: "new", reason: "No similar existing memories found" };
|
|
151803
151951
|
}
|
|
151804
151952
|
if (useLLM) {
|
|
151805
|
-
const llmResult = await resolveWithLLM(extracted, hits, getObservation2);
|
|
151953
|
+
const llmResult = await resolveWithLLM(extracted, hits, getObservation2, signal);
|
|
151806
151954
|
if (llmResult) return llmResult;
|
|
151807
151955
|
}
|
|
151808
151956
|
const scored = hits.map((hit) => ({
|
|
@@ -152224,7 +152372,7 @@ async function runFormation(input, config) {
|
|
|
152224
152372
|
const existingEntities = config.getEntityNames();
|
|
152225
152373
|
const extractStartTime = Date.now();
|
|
152226
152374
|
emitStageEvent("extract", "start");
|
|
152227
|
-
const extraction = await runExtract(input, existingEntities, config.useLLM);
|
|
152375
|
+
const extraction = await runExtract(input, existingEntities, config.useLLM, config.signal);
|
|
152228
152376
|
stageDurationsMs.extract = Date.now() - extractStartTime;
|
|
152229
152377
|
emitStageEvent("extract", "success", stageDurationsMs.extract);
|
|
152230
152378
|
stagesCompleted = 1;
|
|
@@ -152244,7 +152392,8 @@ async function runFormation(input, config) {
|
|
|
152244
152392
|
input.projectId,
|
|
152245
152393
|
config.searchMemories,
|
|
152246
152394
|
config.getObservation,
|
|
152247
|
-
config.useLLM
|
|
152395
|
+
config.useLLM,
|
|
152396
|
+
config.signal
|
|
152248
152397
|
);
|
|
152249
152398
|
stageDurationsMs.resolve = Date.now() - resolveStartTime;
|
|
152250
152399
|
emitStageEvent("resolve", "success", stageDurationsMs.resolve);
|
|
@@ -165629,7 +165778,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
165629
165778
|
};
|
|
165630
165779
|
const server = existingServer ?? new McpServer({
|
|
165631
165780
|
name: "memorix",
|
|
165632
|
-
version: true ? "1.
|
|
165781
|
+
version: true ? "1.5.1" : "1.0.1"
|
|
165633
165782
|
});
|
|
165634
165783
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
165635
165784
|
server.registerTool = ((name, ...args) => {
|
|
@@ -165818,8 +165967,8 @@ The path should point to a directory containing a .git folder.`
|
|
|
165818
165967
|
mode: "active",
|
|
165819
165968
|
useLLM: isLLMEnabled(),
|
|
165820
165969
|
minValueScore: 0.3,
|
|
165821
|
-
searchMemories: async (q, limit2, pid) => {
|
|
165822
|
-
const result = await compactSearch({ query: q, limit: limit2, projectId: pid, status: "active", reader });
|
|
165970
|
+
searchMemories: async (q, limit2, pid, signal) => {
|
|
165971
|
+
const result = await compactSearch({ query: q, limit: limit2, projectId: pid, status: "active", reader, signal });
|
|
165823
165972
|
if (result.entries.length === 0) return [];
|
|
165824
165973
|
const details = await compactDetail(result.entries.map((e2) => e2.id), { reader });
|
|
165825
165974
|
return details.documents.map((d, i2) => ({
|
|
@@ -165849,16 +165998,19 @@ The path should point to a directory containing a .git folder.`
|
|
|
165849
165998
|
getEntityNames: () => graphManager.getEntityNames(),
|
|
165850
165999
|
onStageEvent: onFormationStageEvent
|
|
165851
166000
|
};
|
|
165852
|
-
formationResult = await
|
|
165853
|
-
|
|
165854
|
-
|
|
165855
|
-
|
|
165856
|
-
|
|
165857
|
-
|
|
165858
|
-
|
|
165859
|
-
|
|
165860
|
-
|
|
165861
|
-
|
|
166001
|
+
formationResult = await withTimeoutSignal(
|
|
166002
|
+
async (signal) => {
|
|
166003
|
+
formationConfig.signal = signal;
|
|
166004
|
+
return runFormation({
|
|
166005
|
+
entityName,
|
|
166006
|
+
type,
|
|
166007
|
+
title,
|
|
166008
|
+
narrative,
|
|
166009
|
+
facts: safeFacts,
|
|
166010
|
+
projectId: project.id,
|
|
166011
|
+
source: "explicit"
|
|
166012
|
+
}, formationConfig);
|
|
166013
|
+
},
|
|
165862
166014
|
FORMATION_TIMEOUT_MS,
|
|
165863
166015
|
"Formation pipeline"
|
|
165864
166016
|
);
|
|
@@ -166208,8 +166360,8 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
|
166208
166360
|
mode: formationMode,
|
|
166209
166361
|
useLLM: isLLMEnabled(),
|
|
166210
166362
|
minValueScore: 0.3,
|
|
166211
|
-
searchMemories: async (q, limit2, pid) => {
|
|
166212
|
-
const result = await compactSearch({ query: q, limit: limit2, projectId: pid, status: "active", reader });
|
|
166363
|
+
searchMemories: async (q, limit2, pid, signal) => {
|
|
166364
|
+
const result = await compactSearch({ query: q, limit: limit2, projectId: pid, status: "active", reader, signal });
|
|
166213
166365
|
if (result.entries.length === 0) return [];
|
|
166214
166366
|
const details = await compactDetail(result.entries.map((e2) => e2.id), { reader });
|
|
166215
166367
|
return details.documents.map((d, i2) => ({
|
|
@@ -166230,8 +166382,11 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
|
166230
166382
|
},
|
|
166231
166383
|
getEntityNames: () => graphManager.getEntityNames()
|
|
166232
166384
|
};
|
|
166233
|
-
const formed = await
|
|
166234
|
-
|
|
166385
|
+
const formed = await withTimeoutSignal(
|
|
166386
|
+
async (signal) => {
|
|
166387
|
+
formationConfig.signal = signal;
|
|
166388
|
+
return runFormation({ entityName, type, title, narrative, facts: safeFacts, projectId: project.id, source: "explicit", topicKey }, formationConfig);
|
|
166389
|
+
},
|
|
166235
166390
|
FORMATION_TIMEOUT_MS,
|
|
166236
166391
|
"Shadow formation"
|
|
166237
166392
|
);
|
package/dist/types.d.ts
CHANGED
|
@@ -360,6 +360,8 @@ interface SearchOptions {
|
|
|
360
360
|
* `thorough` explicitly permits optional LLM query rewrite and reranking.
|
|
361
361
|
*/
|
|
362
362
|
quality?: RetrievalQuality;
|
|
363
|
+
/** Abort provider-backed retrieval when the owning request is cancelled. */
|
|
364
|
+
signal?: AbortSignal;
|
|
363
365
|
}
|
|
364
366
|
/** Topic key family heuristics for suggesting stable topic keys */
|
|
365
367
|
declare const TOPIC_KEY_FAMILIES: Record<string, string[]>;
|