memorix 1.5.0 → 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 +14 -0
- package/TEAM.md +86 -86
- package/dist/cli/index.js +211 -69
- package/dist/cli/memcode.js +0 -0
- package/dist/index.js +211 -69
- package/dist/maintenance-runner.js +169 -37
- package/dist/media-audio-runner.js +169 -37
- package/dist/media-video-runner.js +169 -37
- package/dist/memcode-runtime/CHANGELOG.md +14 -0
- package/dist/sdk.js +210 -68
- package/dist/types.d.ts +2 -0
- package/dist/vector-backfill-runner.js +169 -37
- 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/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
|
@@ -3991,6 +3991,8 @@ function getPreferredBatchSize(config) {
|
|
|
3991
3991
|
}
|
|
3992
3992
|
function parseBatchLimit(error) {
|
|
3993
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;
|
|
3994
3996
|
const explicit = error.message.match(/should not be larger than\s+(\d+)/i);
|
|
3995
3997
|
if (explicit) return parseInt(explicit[1], 10);
|
|
3996
3998
|
if (/batch size/i.test(error.message)) {
|
|
@@ -4003,37 +4005,99 @@ async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0, nati
|
|
|
4003
4005
|
const controller = new AbortController();
|
|
4004
4006
|
const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) ? Math.max(100, Math.min(Math.floor(options.timeoutMs), 6e4)) : 1e4;
|
|
4005
4007
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
4008
|
+
const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
|
|
4006
4009
|
let response;
|
|
4007
4010
|
try {
|
|
4008
4011
|
response = await fetch(url, {
|
|
4009
4012
|
method: "POST",
|
|
4010
4013
|
headers: nativeGemini ? { "Content-Type": "application/json", "x-goog-api-key": apiKey } : { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` },
|
|
4011
4014
|
body: JSON.stringify(body),
|
|
4012
|
-
signal
|
|
4015
|
+
signal
|
|
4013
4016
|
});
|
|
4014
4017
|
} catch (err) {
|
|
4015
4018
|
clearTimeout(timeout);
|
|
4016
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
4019
|
+
if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
|
|
4017
4020
|
throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
|
|
4018
4021
|
}
|
|
4019
4022
|
throw err;
|
|
4020
4023
|
}
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
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);
|
|
4024
4052
|
}
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
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) {
|
|
4030
4081
|
await new Promise((resolve2) => setTimeout(resolve2, waitMs));
|
|
4031
|
-
return
|
|
4082
|
+
return;
|
|
4032
4083
|
}
|
|
4033
|
-
const
|
|
4034
|
-
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
|
+
});
|
|
4035
4099
|
}
|
|
4036
|
-
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;
|
|
4037
4101
|
var init_api_provider = __esm({
|
|
4038
4102
|
"src/embedding/api-provider.ts"() {
|
|
4039
4103
|
"use strict";
|
|
@@ -4052,6 +4116,14 @@ var init_api_provider = __esm({
|
|
|
4052
4116
|
DASHSCOPE_MAX_BATCH_SIZE = 10;
|
|
4053
4117
|
MAX_RETRIES2 = 3;
|
|
4054
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
|
+
};
|
|
4055
4127
|
APIEmbeddingProvider = class _APIEmbeddingProvider {
|
|
4056
4128
|
name;
|
|
4057
4129
|
dimensions;
|
|
@@ -4263,15 +4335,14 @@ var init_api_provider = __esm({
|
|
|
4263
4335
|
}
|
|
4264
4336
|
} catch (error) {
|
|
4265
4337
|
const providerLimit = parseBatchLimit(error);
|
|
4266
|
-
|
|
4267
|
-
if (chunkTexts.length > 1 && fallbackSize < chunkTexts.length) {
|
|
4338
|
+
if (providerLimit !== null && chunkTexts.length > 1 && providerLimit < chunkTexts.length) {
|
|
4268
4339
|
console.error(
|
|
4269
|
-
`[memorix] Embedding batch too large for provider, retrying in chunks of ${
|
|
4340
|
+
`[memorix] Embedding batch too large for provider, retrying in chunks of ${providerLimit}`
|
|
4270
4341
|
);
|
|
4271
|
-
for (let start = 0; start < chunkTexts.length; start +=
|
|
4342
|
+
for (let start = 0; start < chunkTexts.length; start += providerLimit) {
|
|
4272
4343
|
await processChunk(
|
|
4273
|
-
chunkTexts.slice(start, start +
|
|
4274
|
-
chunkIndices.slice(start, start +
|
|
4344
|
+
chunkTexts.slice(start, start + providerLimit),
|
|
4345
|
+
chunkIndices.slice(start, start + providerLimit)
|
|
4275
4346
|
);
|
|
4276
4347
|
}
|
|
4277
4348
|
return;
|
|
@@ -5135,22 +5206,23 @@ function getLLMConfig() {
|
|
|
5135
5206
|
function setLLMConfig(config) {
|
|
5136
5207
|
currentConfig = config;
|
|
5137
5208
|
}
|
|
5138
|
-
async function callLLM(systemPrompt, userMessage) {
|
|
5209
|
+
async function callLLM(systemPrompt, userMessage, signal) {
|
|
5139
5210
|
if (!currentConfig) {
|
|
5140
5211
|
throw new Error("LLM not configured. Set MEMORIX_LLM_API_KEY or OPENAI_API_KEY.");
|
|
5141
5212
|
}
|
|
5142
5213
|
if (currentConfig.provider === "anthropic") {
|
|
5143
|
-
return callAnthropic(systemPrompt, userMessage);
|
|
5214
|
+
return callAnthropic(systemPrompt, userMessage, signal);
|
|
5144
5215
|
}
|
|
5145
|
-
return callOpenAICompatible(systemPrompt, userMessage);
|
|
5216
|
+
return callOpenAICompatible(systemPrompt, userMessage, signal);
|
|
5146
5217
|
}
|
|
5147
|
-
async function callOpenAICompatible(systemPrompt, userMessage) {
|
|
5218
|
+
async function callOpenAICompatible(systemPrompt, userMessage, signal) {
|
|
5148
5219
|
const config = currentConfig;
|
|
5149
5220
|
let base = config.baseUrl.replace(/\/+$/, "");
|
|
5150
5221
|
if (!base.endsWith("/v1")) base += "/v1";
|
|
5151
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);
|
|
5152
5224
|
const response = await fetch(url, {
|
|
5153
|
-
signal:
|
|
5225
|
+
signal: fetchSignal,
|
|
5154
5226
|
method: "POST",
|
|
5155
5227
|
headers: {
|
|
5156
5228
|
"Content-Type": "application/json",
|
|
@@ -5167,10 +5239,10 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
|
|
|
5167
5239
|
})
|
|
5168
5240
|
});
|
|
5169
5241
|
if (!response.ok) {
|
|
5170
|
-
const error = await readResponseText(response,
|
|
5242
|
+
const error = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
|
|
5171
5243
|
throw new Error(`LLM API error (${response.status}): ${error}`);
|
|
5172
5244
|
}
|
|
5173
|
-
const data = await readResponseJson(response);
|
|
5245
|
+
const data = await readResponseJson(response, signal);
|
|
5174
5246
|
return {
|
|
5175
5247
|
content: data.choices[0]?.message?.content ?? "",
|
|
5176
5248
|
usage: data.usage ? {
|
|
@@ -5179,11 +5251,12 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
|
|
|
5179
5251
|
} : void 0
|
|
5180
5252
|
};
|
|
5181
5253
|
}
|
|
5182
|
-
async function callAnthropic(systemPrompt, userMessage) {
|
|
5254
|
+
async function callAnthropic(systemPrompt, userMessage, signal) {
|
|
5183
5255
|
const config = currentConfig;
|
|
5184
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);
|
|
5185
5258
|
const response = await fetch(url, {
|
|
5186
|
-
signal:
|
|
5259
|
+
signal: fetchSignal,
|
|
5187
5260
|
method: "POST",
|
|
5188
5261
|
headers: {
|
|
5189
5262
|
"Content-Type": "application/json",
|
|
@@ -5201,10 +5274,10 @@ async function callAnthropic(systemPrompt, userMessage) {
|
|
|
5201
5274
|
})
|
|
5202
5275
|
});
|
|
5203
5276
|
if (!response.ok) {
|
|
5204
|
-
const error = await readResponseText(response,
|
|
5277
|
+
const error = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
|
|
5205
5278
|
throw new Error(`Anthropic API error (${response.status}): ${error}`);
|
|
5206
5279
|
}
|
|
5207
|
-
const data = await readResponseJson(response);
|
|
5280
|
+
const data = await readResponseJson(response, signal);
|
|
5208
5281
|
return {
|
|
5209
5282
|
content: data.content[0]?.text ?? "",
|
|
5210
5283
|
usage: data.usage ? {
|
|
@@ -5709,6 +5782,57 @@ function withTimeout(promise, ms, label) {
|
|
|
5709
5782
|
);
|
|
5710
5783
|
});
|
|
5711
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
|
+
}
|
|
5712
5836
|
var init_timeout = __esm({
|
|
5713
5837
|
"src/timeout.ts"() {
|
|
5714
5838
|
"use strict";
|
|
@@ -6573,16 +6697,18 @@ async function searchObservations(options) {
|
|
|
6573
6697
|
);
|
|
6574
6698
|
} else {
|
|
6575
6699
|
const EMBEDDING_TIMEOUT_MS = 15e3;
|
|
6576
|
-
queryVector = await
|
|
6577
|
-
provider2.embedInput ? provider2.embedInput(
|
|
6700
|
+
queryVector = await withTimeoutSignal(
|
|
6701
|
+
(signal) => provider2.embedInput ? provider2.embedInput(
|
|
6578
6702
|
{ modality: "text", text: expandedEmbeddingQuery },
|
|
6579
|
-
{ intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false }
|
|
6703
|
+
{ intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false, signal }
|
|
6580
6704
|
) : provider2.embed(expandedEmbeddingQuery, {
|
|
6581
6705
|
timeoutMs: EMBEDDING_TIMEOUT_MS,
|
|
6582
|
-
retry: false
|
|
6706
|
+
retry: false,
|
|
6707
|
+
signal
|
|
6583
6708
|
}),
|
|
6584
6709
|
EMBEDDING_TIMEOUT_MS,
|
|
6585
|
-
"Embedding"
|
|
6710
|
+
"Embedding",
|
|
6711
|
+
options.signal
|
|
6586
6712
|
);
|
|
6587
6713
|
mark("embedding");
|
|
6588
6714
|
const cjkRatio = (originalQuery.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length / originalQuery.length;
|
|
@@ -145002,7 +145128,13 @@ async function maintainLongTermMemories(input) {
|
|
|
145002
145128
|
}
|
|
145003
145129
|
for (const group of groups.values()) {
|
|
145004
145130
|
if (group.length < 2) continue;
|
|
145005
|
-
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
|
+
});
|
|
145006
145138
|
const newest = group[0];
|
|
145007
145139
|
for (const older of group.slice(1)) {
|
|
145008
145140
|
try {
|
|
@@ -151488,13 +151620,13 @@ function verifyType(declaredType, narrative, title) {
|
|
|
151488
151620
|
}
|
|
151489
151621
|
return { type: declaredType, corrected: false };
|
|
151490
151622
|
}
|
|
151491
|
-
async function extractFactsWithLLM(narrative, title, existingFacts) {
|
|
151623
|
+
async function extractFactsWithLLM(narrative, title, existingFacts, signal) {
|
|
151492
151624
|
try {
|
|
151493
151625
|
const { callLLM: callLLM2 } = await Promise.resolve().then(() => (init_provider2(), provider_exports2));
|
|
151494
151626
|
const input = `Title: ${title}
|
|
151495
151627
|
Content: ${narrative}${existingFacts.length > 0 ? `
|
|
151496
151628
|
Already known facts (don't repeat): ${existingFacts.join("; ")}` : ""}`;
|
|
151497
|
-
const response = await callLLM2(LLM_EXTRACT_PROMPT, input);
|
|
151629
|
+
const response = await callLLM2(LLM_EXTRACT_PROMPT, input, signal);
|
|
151498
151630
|
const text2 = response.content.trim();
|
|
151499
151631
|
const jsonMatch = text2.match(/\{[\s\S]*\}/);
|
|
151500
151632
|
if (!jsonMatch) return [];
|
|
@@ -151503,15 +151635,16 @@ Already known facts (don't repeat): ${existingFacts.join("; ")}` : ""}`;
|
|
|
151503
151635
|
if (!Array.isArray(facts)) return [];
|
|
151504
151636
|
const existingLower = new Set(existingFacts.map((f3) => f3.toLowerCase().trim()));
|
|
151505
151637
|
return facts.filter((f3) => typeof f3 === "string" && f3.length >= 5).filter((f3) => !existingLower.has(f3.toLowerCase().trim())).slice(0, 10);
|
|
151506
|
-
} catch {
|
|
151638
|
+
} catch (error) {
|
|
151639
|
+
if (signal?.aborted) throw error;
|
|
151507
151640
|
return [];
|
|
151508
151641
|
}
|
|
151509
151642
|
}
|
|
151510
|
-
async function runExtract(input, existingEntities, useLLM = false) {
|
|
151643
|
+
async function runExtract(input, existingEntities, useLLM = false, signal) {
|
|
151511
151644
|
const callerFacts = input.facts ?? [];
|
|
151512
151645
|
let extractedFacts;
|
|
151513
151646
|
if (useLLM) {
|
|
151514
|
-
extractedFacts = await extractFactsWithLLM(input.narrative, input.title, callerFacts);
|
|
151647
|
+
extractedFacts = await extractFactsWithLLM(input.narrative, input.title, callerFacts, signal);
|
|
151515
151648
|
if (extractedFacts.length === 0) {
|
|
151516
151649
|
extractedFacts = extractFacts2(input.narrative, callerFacts);
|
|
151517
151650
|
}
|
|
@@ -151731,7 +151864,7 @@ function mergeFacts2(oldFacts, newFacts) {
|
|
|
151731
151864
|
}
|
|
151732
151865
|
return merged;
|
|
151733
151866
|
}
|
|
151734
|
-
async function resolveWithLLM(extracted, hits, getObservation2) {
|
|
151867
|
+
async function resolveWithLLM(extracted, hits, getObservation2, signal) {
|
|
151735
151868
|
try {
|
|
151736
151869
|
const { callLLM: callLLM2 } = await Promise.resolve().then(() => (init_provider2(), provider_exports2));
|
|
151737
151870
|
const existingMemories = hits.slice(0, 5).map((h2, i2) => ({
|
|
@@ -151748,7 +151881,7 @@ Facts: ${extracted.facts.join("; ")}
|
|
|
151748
151881
|
|
|
151749
151882
|
EXISTING MEMORIES:
|
|
151750
151883
|
${existingMemories.map((m2) => `[ID:${m2.id}] ${m2.title} | ${m2.content} | Facts: ${m2.facts}`).join("\n")}`;
|
|
151751
|
-
const response = await callLLM2(LLM_RESOLVE_PROMPT, input);
|
|
151884
|
+
const response = await callLLM2(LLM_RESOLVE_PROMPT, input, signal);
|
|
151752
151885
|
const text2 = response.content.trim();
|
|
151753
151886
|
const jsonMatch = text2.match(/\{[\s\S]*\}/);
|
|
151754
151887
|
if (!jsonMatch) return null;
|
|
@@ -151785,7 +151918,8 @@ ${existingMemories.map((m2) => `[ID:${m2.id}] ${m2.title} | ${m2.content} | Fact
|
|
|
151785
151918
|
};
|
|
151786
151919
|
}
|
|
151787
151920
|
return null;
|
|
151788
|
-
} catch {
|
|
151921
|
+
} catch (error) {
|
|
151922
|
+
if (signal?.aborted) throw error;
|
|
151789
151923
|
return null;
|
|
151790
151924
|
}
|
|
151791
151925
|
}
|
|
@@ -151803,19 +151937,20 @@ function scoreCandidate(extracted, candidate) {
|
|
|
151803
151937
|
const contradiction = hasContradiction(candidate.narrative, extracted.narrative);
|
|
151804
151938
|
return { score, searchSimilarity, entityMatch, richer, contradiction };
|
|
151805
151939
|
}
|
|
151806
|
-
async function runResolve(extracted, projectId, searchMemories, getObservation2, useLLM = false) {
|
|
151940
|
+
async function runResolve(extracted, projectId, searchMemories, getObservation2, useLLM = false, signal) {
|
|
151807
151941
|
const query = `${extracted.title} ${extracted.narrative.substring(0, 200)}`;
|
|
151808
151942
|
let hits;
|
|
151809
151943
|
try {
|
|
151810
|
-
hits = await searchMemories(query, 5, projectId);
|
|
151811
|
-
} catch {
|
|
151944
|
+
hits = await searchMemories(query, 5, projectId, signal);
|
|
151945
|
+
} catch (error) {
|
|
151946
|
+
if (signal?.aborted) throw error;
|
|
151812
151947
|
return { action: "new", reason: "Search unavailable, defaulting to new" };
|
|
151813
151948
|
}
|
|
151814
151949
|
if (hits.length === 0) {
|
|
151815
151950
|
return { action: "new", reason: "No similar existing memories found" };
|
|
151816
151951
|
}
|
|
151817
151952
|
if (useLLM) {
|
|
151818
|
-
const llmResult = await resolveWithLLM(extracted, hits, getObservation2);
|
|
151953
|
+
const llmResult = await resolveWithLLM(extracted, hits, getObservation2, signal);
|
|
151819
151954
|
if (llmResult) return llmResult;
|
|
151820
151955
|
}
|
|
151821
151956
|
const scored = hits.map((hit) => ({
|
|
@@ -152237,7 +152372,7 @@ async function runFormation(input, config) {
|
|
|
152237
152372
|
const existingEntities = config.getEntityNames();
|
|
152238
152373
|
const extractStartTime = Date.now();
|
|
152239
152374
|
emitStageEvent("extract", "start");
|
|
152240
|
-
const extraction = await runExtract(input, existingEntities, config.useLLM);
|
|
152375
|
+
const extraction = await runExtract(input, existingEntities, config.useLLM, config.signal);
|
|
152241
152376
|
stageDurationsMs.extract = Date.now() - extractStartTime;
|
|
152242
152377
|
emitStageEvent("extract", "success", stageDurationsMs.extract);
|
|
152243
152378
|
stagesCompleted = 1;
|
|
@@ -152257,7 +152392,8 @@ async function runFormation(input, config) {
|
|
|
152257
152392
|
input.projectId,
|
|
152258
152393
|
config.searchMemories,
|
|
152259
152394
|
config.getObservation,
|
|
152260
|
-
config.useLLM
|
|
152395
|
+
config.useLLM,
|
|
152396
|
+
config.signal
|
|
152261
152397
|
);
|
|
152262
152398
|
stageDurationsMs.resolve = Date.now() - resolveStartTime;
|
|
152263
152399
|
emitStageEvent("resolve", "success", stageDurationsMs.resolve);
|
|
@@ -165642,7 +165778,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
165642
165778
|
};
|
|
165643
165779
|
const server = existingServer ?? new McpServer({
|
|
165644
165780
|
name: "memorix",
|
|
165645
|
-
version: true ? "1.5.
|
|
165781
|
+
version: true ? "1.5.1" : "1.0.1"
|
|
165646
165782
|
});
|
|
165647
165783
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
165648
165784
|
server.registerTool = ((name, ...args) => {
|
|
@@ -165831,8 +165967,8 @@ The path should point to a directory containing a .git folder.`
|
|
|
165831
165967
|
mode: "active",
|
|
165832
165968
|
useLLM: isLLMEnabled(),
|
|
165833
165969
|
minValueScore: 0.3,
|
|
165834
|
-
searchMemories: async (q, limit2, pid) => {
|
|
165835
|
-
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 });
|
|
165836
165972
|
if (result.entries.length === 0) return [];
|
|
165837
165973
|
const details = await compactDetail(result.entries.map((e2) => e2.id), { reader });
|
|
165838
165974
|
return details.documents.map((d, i2) => ({
|
|
@@ -165862,16 +165998,19 @@ The path should point to a directory containing a .git folder.`
|
|
|
165862
165998
|
getEntityNames: () => graphManager.getEntityNames(),
|
|
165863
165999
|
onStageEvent: onFormationStageEvent
|
|
165864
166000
|
};
|
|
165865
|
-
formationResult = await
|
|
165866
|
-
|
|
165867
|
-
|
|
165868
|
-
|
|
165869
|
-
|
|
165870
|
-
|
|
165871
|
-
|
|
165872
|
-
|
|
165873
|
-
|
|
165874
|
-
|
|
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
|
+
},
|
|
165875
166014
|
FORMATION_TIMEOUT_MS,
|
|
165876
166015
|
"Formation pipeline"
|
|
165877
166016
|
);
|
|
@@ -166221,8 +166360,8 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
|
166221
166360
|
mode: formationMode,
|
|
166222
166361
|
useLLM: isLLMEnabled(),
|
|
166223
166362
|
minValueScore: 0.3,
|
|
166224
|
-
searchMemories: async (q, limit2, pid) => {
|
|
166225
|
-
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 });
|
|
166226
166365
|
if (result.entries.length === 0) return [];
|
|
166227
166366
|
const details = await compactDetail(result.entries.map((e2) => e2.id), { reader });
|
|
166228
166367
|
return details.documents.map((d, i2) => ({
|
|
@@ -166243,8 +166382,11 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
|
166243
166382
|
},
|
|
166244
166383
|
getEntityNames: () => graphManager.getEntityNames()
|
|
166245
166384
|
};
|
|
166246
|
-
const formed = await
|
|
166247
|
-
|
|
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
|
+
},
|
|
166248
166390
|
FORMATION_TIMEOUT_MS,
|
|
166249
166391
|
"Shadow formation"
|
|
166250
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[]>;
|