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.
Files changed (119) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/TEAM.md +86 -86
  3. package/dist/cli/index.js +225 -70
  4. package/dist/cli/memcode.js +0 -0
  5. package/dist/index.js +225 -70
  6. package/dist/maintenance-runner.js +183 -38
  7. package/dist/media-audio-runner.js +183 -38
  8. package/dist/media-video-runner.js +183 -38
  9. package/dist/memcode-runtime/CHANGELOG.md +38 -0
  10. package/dist/sdk.js +224 -69
  11. package/dist/types.d.ts +2 -0
  12. package/dist/vector-backfill-runner.js +183 -38
  13. package/docs/CONFIGURATION.md +12 -0
  14. package/docs/DESIGN_DECISIONS.md +357 -357
  15. package/package.json +1 -1
  16. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  17. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  18. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  19. package/plugins/copilot/memorix/plugin.json +1 -1
  20. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  21. package/plugins/omp/memorix/package.json +1 -1
  22. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  23. package/plugins/pi/memorix/package.json +1 -1
  24. package/server.json +2 -2
  25. package/src/audit/index.ts +156 -156
  26. package/src/cli/commands/formation.ts +48 -48
  27. package/src/cli/commands/git-hook-install.ts +111 -111
  28. package/src/cli/commands/handoff.ts +54 -54
  29. package/src/cli/commands/hooks-status.ts +63 -63
  30. package/src/cli/commands/ingest.ts +44 -44
  31. package/src/cli/commands/integrate-shared.ts +15 -15
  32. package/src/cli/commands/lock.ts +82 -82
  33. package/src/cli/commands/message.ts +104 -104
  34. package/src/cli/commands/poll.ts +58 -58
  35. package/src/cli/commands/reasoning.ts +118 -118
  36. package/src/cli/commands/skills.ts +114 -114
  37. package/src/cli/commands/task.ts +167 -167
  38. package/src/cli/commands/transfer.ts +47 -47
  39. package/src/cli/index.ts +406 -406
  40. package/src/cli/tui/ChatView.tsx +234 -234
  41. package/src/cli/tui/CommandBar.tsx +312 -312
  42. package/src/cli/tui/ContextRail.tsx +118 -118
  43. package/src/cli/tui/HeaderBar.tsx +72 -72
  44. package/src/cli/tui/LogoBanner.tsx +51 -51
  45. package/src/cli/tui/index.ts +41 -41
  46. package/src/cli/version.ts +7 -7
  47. package/src/cli/workbench.ts +1 -1
  48. package/src/config.ts +23 -2
  49. package/src/dashboard/project-classification.ts +64 -64
  50. package/src/embedding/api-provider.ts +105 -22
  51. package/src/embedding/provider.ts +2 -0
  52. package/src/git/hooks-path.ts +85 -85
  53. package/src/hooks/pattern-detector.ts +173 -173
  54. package/src/hooks/significance-filter.ts +250 -250
  55. package/src/llm/memory-manager.ts +328 -328
  56. package/src/llm/provider.ts +17 -8
  57. package/src/llm/quality.ts +248 -248
  58. package/src/memory/attribution-guard.ts +249 -249
  59. package/src/memory/disclosure-policy.ts +135 -135
  60. package/src/memory/entity-extractor.ts +197 -197
  61. package/src/memory/formation/evaluate.ts +217 -217
  62. package/src/memory/formation/extract.ts +6 -3
  63. package/src/memory/formation/index.ts +2 -1
  64. package/src/memory/formation/resolve.ts +10 -6
  65. package/src/memory/formation/types.ts +3 -1
  66. package/src/memory/freshness.ts +122 -122
  67. package/src/memory/graph.ts +197 -197
  68. package/src/memory/long-term.ts +7 -1
  69. package/src/memory/refs.ts +94 -94
  70. package/src/memory/secret-filter.ts +79 -79
  71. package/src/orchestrate/adapters/claude-stream.ts +192 -192
  72. package/src/orchestrate/adapters/claude.ts +111 -111
  73. package/src/orchestrate/adapters/codex-stream.ts +134 -134
  74. package/src/orchestrate/adapters/codex.ts +41 -41
  75. package/src/orchestrate/adapters/gemini-stream.ts +166 -166
  76. package/src/orchestrate/adapters/gemini.ts +42 -42
  77. package/src/orchestrate/adapters/index.ts +73 -73
  78. package/src/orchestrate/adapters/opencode-stream.ts +143 -143
  79. package/src/orchestrate/adapters/opencode.ts +47 -47
  80. package/src/orchestrate/adapters/types.ts +77 -77
  81. package/src/orchestrate/capability-router.ts +284 -284
  82. package/src/orchestrate/cost-tracker.ts +219 -219
  83. package/src/orchestrate/error-recovery.ts +191 -191
  84. package/src/orchestrate/evidence.ts +140 -140
  85. package/src/orchestrate/ledger.ts +110 -110
  86. package/src/orchestrate/memorix-bridge.ts +343 -343
  87. package/src/orchestrate/output-budget.ts +80 -80
  88. package/src/orchestrate/permission.ts +152 -152
  89. package/src/orchestrate/pipeline-trace.ts +131 -131
  90. package/src/orchestrate/prompt-builder.ts +155 -155
  91. package/src/orchestrate/ring-buffer.ts +37 -37
  92. package/src/orchestrate/task-graph.ts +389 -389
  93. package/src/project/aliases.ts +374 -374
  94. package/src/rules/adapters/trae.ts +56 -56
  95. package/src/rules/syncer.ts +235 -235
  96. package/src/search/query-expansion.ts +52 -52
  97. package/src/server/formation-timeout.ts +27 -27
  98. package/src/server.ts +14 -10
  99. package/src/skills/mini-skills.ts +386 -386
  100. package/src/store/chat-store.ts +119 -119
  101. package/src/store/graph-store.ts +249 -249
  102. package/src/store/mini-skill-store.ts +349 -349
  103. package/src/store/orama-store.ts +6 -4
  104. package/src/store/persistence-json.ts +212 -212
  105. package/src/store/persistence.ts +291 -291
  106. package/src/store/project-affinity.ts +195 -195
  107. package/src/team/event-bus.ts +76 -76
  108. package/src/team/handoff.ts +161 -161
  109. package/src/team/poll.ts +132 -132
  110. package/src/timeout.ts +67 -0
  111. package/src/types.ts +2 -0
  112. package/src/workspace/mcp-adapters/codex.ts +191 -191
  113. package/src/workspace/mcp-adapters/copilot.ts +105 -105
  114. package/src/workspace/mcp-adapters/cursor.ts +53 -53
  115. package/src/workspace/mcp-adapters/kiro.ts +64 -64
  116. package/src/workspace/mcp-adapters/opencode.ts +123 -123
  117. package/src/workspace/mcp-adapters/trae.ts +134 -134
  118. package/src/workspace/mcp-adapters/windsurf.ts +91 -91
  119. package/src/workspace/sanitizer.ts +60 -60
package/dist/index.js CHANGED
@@ -3373,6 +3373,7 @@ var init_resolved_config = __esm({
3373
3373
  // src/config.ts
3374
3374
  var config_exports = {};
3375
3375
  __export(config_exports, {
3376
+ defaultEmbeddingModelFor: () => defaultEmbeddingModelFor,
3376
3377
  getAgentLLMApiKey: () => getAgentLLMApiKey,
3377
3378
  getAgentLLMBaseUrl: () => getAgentLLMBaseUrl,
3378
3379
  getAgentLLMModel: () => getAgentLLMModel,
@@ -3435,7 +3436,19 @@ function getEmbeddingBaseUrl() {
3435
3436
  return getResolvedEmbeddingLane().baseUrl || "https://api.openai.com/v1";
3436
3437
  }
3437
3438
  function getEmbeddingModel() {
3438
- return getResolvedEmbeddingLane().model || "text-embedding-3-small";
3439
+ const lane = getResolvedEmbeddingLane();
3440
+ return defaultEmbeddingModelFor(lane.baseUrl, lane.model);
3441
+ }
3442
+ function defaultEmbeddingModelFor(baseUrl, explicitModel) {
3443
+ if (explicitModel) return explicitModel;
3444
+ try {
3445
+ const url = baseUrl ? new URL(baseUrl) : null;
3446
+ if (url && (url.hostname === "openrouter.ai" || url.hostname.endsWith(".openrouter.ai"))) {
3447
+ return "qwen/qwen3-embedding-8b";
3448
+ }
3449
+ } catch {
3450
+ }
3451
+ return "text-embedding-3-small";
3439
3452
  }
3440
3453
  function getEmbeddingDimensions() {
3441
3454
  return getResolvedEmbeddingLane().dimensions ?? null;
@@ -3974,6 +3987,8 @@ function getPreferredBatchSize(config) {
3974
3987
  }
3975
3988
  function parseBatchLimit(error) {
3976
3989
  if (!(error instanceof Error)) return null;
3990
+ const status = error instanceof EmbeddingAPIError ? error.status : Number(error.message.match(/Embedding API error \((\d{3})\)/i)?.[1] ?? 0);
3991
+ if (status > 0 && status !== 400) return null;
3977
3992
  const explicit = error.message.match(/should not be larger than\s+(\d+)/i);
3978
3993
  if (explicit) return parseInt(explicit[1], 10);
3979
3994
  if (/batch size/i.test(error.message)) {
@@ -3986,37 +4001,99 @@ async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0, nati
3986
4001
  const controller = new AbortController();
3987
4002
  const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) ? Math.max(100, Math.min(Math.floor(options.timeoutMs), 6e4)) : 1e4;
3988
4003
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
4004
+ const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
3989
4005
  let response;
3990
4006
  try {
3991
4007
  response = await fetch(url, {
3992
4008
  method: "POST",
3993
4009
  headers: nativeGemini ? { "Content-Type": "application/json", "x-goog-api-key": apiKey } : { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` },
3994
4010
  body: JSON.stringify(body),
3995
- signal: controller.signal
4011
+ signal
3996
4012
  });
3997
4013
  } catch (err) {
3998
4014
  clearTimeout(timeout);
3999
- if (err instanceof Error && err.name === "AbortError") {
4015
+ if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
4000
4016
  throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
4001
4017
  }
4002
4018
  throw err;
4003
4019
  }
4004
- clearTimeout(timeout);
4005
- if (response.ok) {
4006
- return response.json();
4020
+ try {
4021
+ if (response.ok) {
4022
+ return await raceWithAbort(
4023
+ response.json(),
4024
+ signal
4025
+ );
4026
+ }
4027
+ if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
4028
+ const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
4029
+ const retryAfter = response.headers.get("retry-after");
4030
+ const parsedRetryAfter = retryAfter ? parseInt(retryAfter, 10) * 1e3 : NaN;
4031
+ const waitMs = Number.isFinite(parsedRetryAfter) ? parsedRetryAfter : delay2;
4032
+ console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
4033
+ await waitForRetry(waitMs, options.signal);
4034
+ return fetchWithRetry(url, apiKey, body, options, attempt + 1, nativeGemini);
4035
+ }
4036
+ const errorText2 = await raceWithAbort(
4037
+ response.text().catch(() => "unknown error"),
4038
+ signal
4039
+ );
4040
+ throw new EmbeddingAPIError(response.status, errorText2);
4041
+ } catch (err) {
4042
+ if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
4043
+ throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
4044
+ }
4045
+ throw err;
4046
+ } finally {
4047
+ clearTimeout(timeout);
4007
4048
  }
4008
- if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
4009
- const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
4010
- const retryAfter = response.headers.get("retry-after");
4011
- const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : delay2;
4012
- console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
4049
+ }
4050
+ function abortError() {
4051
+ const error = new Error("Embedding API request aborted");
4052
+ error.name = "AbortError";
4053
+ return error;
4054
+ }
4055
+ function raceWithAbort(promise, signal) {
4056
+ if (signal.aborted) return Promise.reject(abortError());
4057
+ return new Promise((resolve2, reject) => {
4058
+ const onAbort = () => {
4059
+ signal.removeEventListener("abort", onAbort);
4060
+ reject(abortError());
4061
+ };
4062
+ signal.addEventListener("abort", onAbort, { once: true });
4063
+ promise.then(
4064
+ (value) => {
4065
+ signal.removeEventListener("abort", onAbort);
4066
+ resolve2(value);
4067
+ },
4068
+ (error) => {
4069
+ signal.removeEventListener("abort", onAbort);
4070
+ reject(error);
4071
+ }
4072
+ );
4073
+ });
4074
+ }
4075
+ async function waitForRetry(waitMs, signal) {
4076
+ if (!signal) {
4013
4077
  await new Promise((resolve2) => setTimeout(resolve2, waitMs));
4014
- return fetchWithRetry(url, apiKey, body, options, attempt + 1, nativeGemini);
4078
+ return;
4015
4079
  }
4016
- const errorText2 = await response.text().catch(() => "unknown error");
4017
- throw new Error(`Embedding API error (${response.status}): ${errorText2}`);
4080
+ const abortSignal = signal;
4081
+ if (abortSignal.aborted) throw new Error("Embedding API retry aborted");
4082
+ await new Promise((resolve2, reject) => {
4083
+ const timer = setTimeout(done, waitMs);
4084
+ const onAbort = () => {
4085
+ clearTimeout(timer);
4086
+ abortSignal.removeEventListener("abort", onAbort);
4087
+ reject(new Error("Embedding API retry aborted"));
4088
+ };
4089
+ function done() {
4090
+ abortSignal.removeEventListener("abort", onAbort);
4091
+ resolve2();
4092
+ }
4093
+ abortSignal.addEventListener("abort", onAbort, { once: true });
4094
+ });
4018
4095
  }
4019
- 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;
4096
+ 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;
4020
4097
  var init_api_provider = __esm({
4021
4098
  "src/embedding/api-provider.ts"() {
4022
4099
  "use strict";
@@ -4035,6 +4112,14 @@ var init_api_provider = __esm({
4035
4112
  DASHSCOPE_MAX_BATCH_SIZE = 10;
4036
4113
  MAX_RETRIES2 = 3;
4037
4114
  BASE_DELAY_MS = 500;
4115
+ EmbeddingAPIError = class extends Error {
4116
+ status;
4117
+ constructor(status, detail) {
4118
+ super(`Embedding API error (${status}): ${detail}`);
4119
+ this.name = "EmbeddingAPIError";
4120
+ this.status = status;
4121
+ }
4122
+ };
4038
4123
  APIEmbeddingProvider = class _APIEmbeddingProvider {
4039
4124
  name;
4040
4125
  dimensions;
@@ -4246,15 +4331,14 @@ var init_api_provider = __esm({
4246
4331
  }
4247
4332
  } catch (error) {
4248
4333
  const providerLimit = parseBatchLimit(error);
4249
- const fallbackSize = providerLimit ?? Math.ceil(chunkTexts.length / 2);
4250
- if (chunkTexts.length > 1 && fallbackSize < chunkTexts.length) {
4334
+ if (providerLimit !== null && chunkTexts.length > 1 && providerLimit < chunkTexts.length) {
4251
4335
  console.error(
4252
- `[memorix] Embedding batch too large for provider, retrying in chunks of ${fallbackSize}`
4336
+ `[memorix] Embedding batch too large for provider, retrying in chunks of ${providerLimit}`
4253
4337
  );
4254
- for (let start = 0; start < chunkTexts.length; start += fallbackSize) {
4338
+ for (let start = 0; start < chunkTexts.length; start += providerLimit) {
4255
4339
  await processChunk(
4256
- chunkTexts.slice(start, start + fallbackSize),
4257
- chunkIndices.slice(start, start + fallbackSize)
4340
+ chunkTexts.slice(start, start + providerLimit),
4341
+ chunkIndices.slice(start, start + providerLimit)
4258
4342
  );
4259
4343
  }
4260
4344
  return;
@@ -5118,22 +5202,23 @@ function getLLMConfig() {
5118
5202
  function setLLMConfig(config) {
5119
5203
  currentConfig = config;
5120
5204
  }
5121
- async function callLLM(systemPrompt, userMessage) {
5205
+ async function callLLM(systemPrompt, userMessage, signal) {
5122
5206
  if (!currentConfig) {
5123
5207
  throw new Error("LLM not configured. Set MEMORIX_LLM_API_KEY or OPENAI_API_KEY.");
5124
5208
  }
5125
5209
  if (currentConfig.provider === "anthropic") {
5126
- return callAnthropic(systemPrompt, userMessage);
5210
+ return callAnthropic(systemPrompt, userMessage, signal);
5127
5211
  }
5128
- return callOpenAICompatible(systemPrompt, userMessage);
5212
+ return callOpenAICompatible(systemPrompt, userMessage, signal);
5129
5213
  }
5130
- async function callOpenAICompatible(systemPrompt, userMessage) {
5214
+ async function callOpenAICompatible(systemPrompt, userMessage, signal) {
5131
5215
  const config = currentConfig;
5132
5216
  let base = config.baseUrl.replace(/\/+$/, "");
5133
5217
  if (!base.endsWith("/v1")) base += "/v1";
5134
5218
  const url = `${base}/chat/completions`;
5219
+ const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
5135
5220
  const response = await fetch(url, {
5136
- signal: AbortSignal.timeout(LLM_CALL_TIMEOUT_MS),
5221
+ signal: fetchSignal,
5137
5222
  method: "POST",
5138
5223
  headers: {
5139
5224
  "Content-Type": "application/json",
@@ -5150,10 +5235,10 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
5150
5235
  })
5151
5236
  });
5152
5237
  if (!response.ok) {
5153
- const error = await readResponseText(response, void 0, 64 * 1024).catch(() => "unknown error");
5238
+ const error = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
5154
5239
  throw new Error(`LLM API error (${response.status}): ${error}`);
5155
5240
  }
5156
- const data = await readResponseJson(response);
5241
+ const data = await readResponseJson(response, signal);
5157
5242
  return {
5158
5243
  content: data.choices[0]?.message?.content ?? "",
5159
5244
  usage: data.usage ? {
@@ -5162,11 +5247,12 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
5162
5247
  } : void 0
5163
5248
  };
5164
5249
  }
5165
- async function callAnthropic(systemPrompt, userMessage) {
5250
+ async function callAnthropic(systemPrompt, userMessage, signal) {
5166
5251
  const config = currentConfig;
5167
5252
  const url = `${config.baseUrl}/messages`;
5253
+ const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
5168
5254
  const response = await fetch(url, {
5169
- signal: AbortSignal.timeout(LLM_CALL_TIMEOUT_MS),
5255
+ signal: fetchSignal,
5170
5256
  method: "POST",
5171
5257
  headers: {
5172
5258
  "Content-Type": "application/json",
@@ -5184,10 +5270,10 @@ async function callAnthropic(systemPrompt, userMessage) {
5184
5270
  })
5185
5271
  });
5186
5272
  if (!response.ok) {
5187
- const error = await readResponseText(response, void 0, 64 * 1024).catch(() => "unknown error");
5273
+ const error = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
5188
5274
  throw new Error(`Anthropic API error (${response.status}): ${error}`);
5189
5275
  }
5190
- const data = await readResponseJson(response);
5276
+ const data = await readResponseJson(response, signal);
5191
5277
  return {
5192
5278
  content: data.content[0]?.text ?? "",
5193
5279
  usage: data.usage ? {
@@ -5692,6 +5778,57 @@ function withTimeout(promise, ms, label) {
5692
5778
  );
5693
5779
  });
5694
5780
  }
5781
+ function withTimeoutSignal(operation, ms, label, parentSignal) {
5782
+ const controller = new AbortController();
5783
+ const signal = parentSignal ? AbortSignal.any([parentSignal, controller.signal]) : controller.signal;
5784
+ return new Promise((resolve2, reject) => {
5785
+ let settled = false;
5786
+ let timedOut = false;
5787
+ let timer;
5788
+ const cleanup = () => {
5789
+ if (timer !== void 0) clearTimeout(timer);
5790
+ parentSignal?.removeEventListener("abort", onParentAbort);
5791
+ };
5792
+ const onParentAbort = () => {
5793
+ if (settled || timedOut) return;
5794
+ settled = true;
5795
+ cleanup();
5796
+ reject(parentSignal?.reason ?? new Error(`${label} aborted`));
5797
+ };
5798
+ parentSignal?.addEventListener("abort", onParentAbort, { once: true });
5799
+ timer = setTimeout(() => {
5800
+ if (settled) return;
5801
+ timedOut = true;
5802
+ controller.abort(new Error(`${label} timed out after ${ms}ms`));
5803
+ settled = true;
5804
+ cleanup();
5805
+ reject(new Error(`${label} timed out after ${ms}ms`));
5806
+ }, ms);
5807
+ let pending;
5808
+ try {
5809
+ pending = operation(signal);
5810
+ } catch (error) {
5811
+ settled = true;
5812
+ cleanup();
5813
+ reject(error);
5814
+ return;
5815
+ }
5816
+ pending.then(
5817
+ (value) => {
5818
+ if (settled) return;
5819
+ settled = true;
5820
+ cleanup();
5821
+ resolve2(value);
5822
+ },
5823
+ (error) => {
5824
+ if (settled) return;
5825
+ settled = true;
5826
+ cleanup();
5827
+ reject(timedOut ? new Error(`${label} timed out after ${ms}ms`) : error);
5828
+ }
5829
+ );
5830
+ });
5831
+ }
5695
5832
  var init_timeout = __esm({
5696
5833
  "src/timeout.ts"() {
5697
5834
  "use strict";
@@ -6556,16 +6693,18 @@ async function searchObservations(options) {
6556
6693
  );
6557
6694
  } else {
6558
6695
  const EMBEDDING_TIMEOUT_MS = 15e3;
6559
- queryVector = await withTimeout(
6560
- provider2.embedInput ? provider2.embedInput(
6696
+ queryVector = await withTimeoutSignal(
6697
+ (signal) => provider2.embedInput ? provider2.embedInput(
6561
6698
  { modality: "text", text: expandedEmbeddingQuery },
6562
- { intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false }
6699
+ { intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false, signal }
6563
6700
  ) : provider2.embed(expandedEmbeddingQuery, {
6564
6701
  timeoutMs: EMBEDDING_TIMEOUT_MS,
6565
- retry: false
6702
+ retry: false,
6703
+ signal
6566
6704
  }),
6567
6705
  EMBEDDING_TIMEOUT_MS,
6568
- "Embedding"
6706
+ "Embedding",
6707
+ options.signal
6569
6708
  );
6570
6709
  mark("embedding");
6571
6710
  const cjkRatio = (originalQuery.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length / originalQuery.length;
@@ -144985,7 +145124,13 @@ async function maintainLongTermMemories(input) {
144985
145124
  }
144986
145125
  for (const group of groups.values()) {
144987
145126
  if (group.length < 2) continue;
144988
- group.sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt));
145127
+ group.sort((a, b) => {
145128
+ const byCreatedAt = Date.parse(b.createdAt) - Date.parse(a.createdAt);
145129
+ if (byCreatedAt !== 0) return byCreatedAt;
145130
+ const byUpdatedAt = Date.parse(b.updatedAt) - Date.parse(a.updatedAt);
145131
+ if (byUpdatedAt !== 0) return byUpdatedAt;
145132
+ return b.id.localeCompare(a.id);
145133
+ });
144989
145134
  const newest = group[0];
144990
145135
  for (const older of group.slice(1)) {
144991
145136
  try {
@@ -155320,13 +155465,13 @@ function verifyType(declaredType, narrative, title) {
155320
155465
  }
155321
155466
  return { type: declaredType, corrected: false };
155322
155467
  }
155323
- async function extractFactsWithLLM(narrative, title, existingFacts) {
155468
+ async function extractFactsWithLLM(narrative, title, existingFacts, signal) {
155324
155469
  try {
155325
155470
  const { callLLM: callLLM2 } = await Promise.resolve().then(() => (init_provider2(), provider_exports2));
155326
155471
  const input = `Title: ${title}
155327
155472
  Content: ${narrative}${existingFacts.length > 0 ? `
155328
155473
  Already known facts (don't repeat): ${existingFacts.join("; ")}` : ""}`;
155329
- const response = await callLLM2(LLM_EXTRACT_PROMPT, input);
155474
+ const response = await callLLM2(LLM_EXTRACT_PROMPT, input, signal);
155330
155475
  const text2 = response.content.trim();
155331
155476
  const jsonMatch = text2.match(/\{[\s\S]*\}/);
155332
155477
  if (!jsonMatch) return [];
@@ -155335,15 +155480,16 @@ Already known facts (don't repeat): ${existingFacts.join("; ")}` : ""}`;
155335
155480
  if (!Array.isArray(facts)) return [];
155336
155481
  const existingLower = new Set(existingFacts.map((f3) => f3.toLowerCase().trim()));
155337
155482
  return facts.filter((f3) => typeof f3 === "string" && f3.length >= 5).filter((f3) => !existingLower.has(f3.toLowerCase().trim())).slice(0, 10);
155338
- } catch {
155483
+ } catch (error) {
155484
+ if (signal?.aborted) throw error;
155339
155485
  return [];
155340
155486
  }
155341
155487
  }
155342
- async function runExtract(input, existingEntities, useLLM = false) {
155488
+ async function runExtract(input, existingEntities, useLLM = false, signal) {
155343
155489
  const callerFacts = input.facts ?? [];
155344
155490
  let extractedFacts;
155345
155491
  if (useLLM) {
155346
- extractedFacts = await extractFactsWithLLM(input.narrative, input.title, callerFacts);
155492
+ extractedFacts = await extractFactsWithLLM(input.narrative, input.title, callerFacts, signal);
155347
155493
  if (extractedFacts.length === 0) {
155348
155494
  extractedFacts = extractFacts2(input.narrative, callerFacts);
155349
155495
  }
@@ -155563,7 +155709,7 @@ function mergeFacts2(oldFacts, newFacts) {
155563
155709
  }
155564
155710
  return merged;
155565
155711
  }
155566
- async function resolveWithLLM(extracted, hits, getObservation2) {
155712
+ async function resolveWithLLM(extracted, hits, getObservation2, signal) {
155567
155713
  try {
155568
155714
  const { callLLM: callLLM2 } = await Promise.resolve().then(() => (init_provider2(), provider_exports2));
155569
155715
  const existingMemories = hits.slice(0, 5).map((h2, i2) => ({
@@ -155580,7 +155726,7 @@ Facts: ${extracted.facts.join("; ")}
155580
155726
 
155581
155727
  EXISTING MEMORIES:
155582
155728
  ${existingMemories.map((m2) => `[ID:${m2.id}] ${m2.title} | ${m2.content} | Facts: ${m2.facts}`).join("\n")}`;
155583
- const response = await callLLM2(LLM_RESOLVE_PROMPT, input);
155729
+ const response = await callLLM2(LLM_RESOLVE_PROMPT, input, signal);
155584
155730
  const text2 = response.content.trim();
155585
155731
  const jsonMatch = text2.match(/\{[\s\S]*\}/);
155586
155732
  if (!jsonMatch) return null;
@@ -155617,7 +155763,8 @@ ${existingMemories.map((m2) => `[ID:${m2.id}] ${m2.title} | ${m2.content} | Fact
155617
155763
  };
155618
155764
  }
155619
155765
  return null;
155620
- } catch {
155766
+ } catch (error) {
155767
+ if (signal?.aborted) throw error;
155621
155768
  return null;
155622
155769
  }
155623
155770
  }
@@ -155635,19 +155782,20 @@ function scoreCandidate(extracted, candidate) {
155635
155782
  const contradiction = hasContradiction(candidate.narrative, extracted.narrative);
155636
155783
  return { score, searchSimilarity, entityMatch, richer, contradiction };
155637
155784
  }
155638
- async function runResolve(extracted, projectId, searchMemories, getObservation2, useLLM = false) {
155785
+ async function runResolve(extracted, projectId, searchMemories, getObservation2, useLLM = false, signal) {
155639
155786
  const query = `${extracted.title} ${extracted.narrative.substring(0, 200)}`;
155640
155787
  let hits;
155641
155788
  try {
155642
- hits = await searchMemories(query, 5, projectId);
155643
- } catch {
155789
+ hits = await searchMemories(query, 5, projectId, signal);
155790
+ } catch (error) {
155791
+ if (signal?.aborted) throw error;
155644
155792
  return { action: "new", reason: "Search unavailable, defaulting to new" };
155645
155793
  }
155646
155794
  if (hits.length === 0) {
155647
155795
  return { action: "new", reason: "No similar existing memories found" };
155648
155796
  }
155649
155797
  if (useLLM) {
155650
- const llmResult = await resolveWithLLM(extracted, hits, getObservation2);
155798
+ const llmResult = await resolveWithLLM(extracted, hits, getObservation2, signal);
155651
155799
  if (llmResult) return llmResult;
155652
155800
  }
155653
155801
  const scored = hits.map((hit) => ({
@@ -156069,7 +156217,7 @@ async function runFormation(input, config) {
156069
156217
  const existingEntities = config.getEntityNames();
156070
156218
  const extractStartTime = Date.now();
156071
156219
  emitStageEvent("extract", "start");
156072
- const extraction = await runExtract(input, existingEntities, config.useLLM);
156220
+ const extraction = await runExtract(input, existingEntities, config.useLLM, config.signal);
156073
156221
  stageDurationsMs.extract = Date.now() - extractStartTime;
156074
156222
  emitStageEvent("extract", "success", stageDurationsMs.extract);
156075
156223
  stagesCompleted = 1;
@@ -156089,7 +156237,8 @@ async function runFormation(input, config) {
156089
156237
  input.projectId,
156090
156238
  config.searchMemories,
156091
156239
  config.getObservation,
156092
- config.useLLM
156240
+ config.useLLM,
156241
+ config.signal
156093
156242
  );
156094
156243
  stageDurationsMs.resolve = Date.now() - resolveStartTime;
156095
156244
  emitStageEvent("resolve", "success", stageDurationsMs.resolve);
@@ -165821,7 +165970,7 @@ The path should point to a directory containing a .git folder.`
165821
165970
  };
165822
165971
  const server = existingServer ?? new McpServer({
165823
165972
  name: "memorix",
165824
- version: true ? "1.4.6" : "1.0.1"
165973
+ version: true ? "1.5.1" : "1.0.1"
165825
165974
  });
165826
165975
  const originalRegisterTool = server.registerTool.bind(server);
165827
165976
  server.registerTool = ((name, ...args) => {
@@ -166010,8 +166159,8 @@ The path should point to a directory containing a .git folder.`
166010
166159
  mode: "active",
166011
166160
  useLLM: isLLMEnabled(),
166012
166161
  minValueScore: 0.3,
166013
- searchMemories: async (q, limit2, pid) => {
166014
- const result = await compactSearch({ query: q, limit: limit2, projectId: pid, status: "active", reader });
166162
+ searchMemories: async (q, limit2, pid, signal) => {
166163
+ const result = await compactSearch({ query: q, limit: limit2, projectId: pid, status: "active", reader, signal });
166015
166164
  if (result.entries.length === 0) return [];
166016
166165
  const details = await compactDetail(result.entries.map((e2) => e2.id), { reader });
166017
166166
  return details.documents.map((d, i2) => ({
@@ -166041,16 +166190,19 @@ The path should point to a directory containing a .git folder.`
166041
166190
  getEntityNames: () => graphManager.getEntityNames(),
166042
166191
  onStageEvent: onFormationStageEvent
166043
166192
  };
166044
- formationResult = await withTimeout(
166045
- runFormation({
166046
- entityName,
166047
- type,
166048
- title,
166049
- narrative,
166050
- facts: safeFacts,
166051
- projectId: project.id,
166052
- source: "explicit"
166053
- }, formationConfig),
166193
+ formationResult = await withTimeoutSignal(
166194
+ async (signal) => {
166195
+ formationConfig.signal = signal;
166196
+ return runFormation({
166197
+ entityName,
166198
+ type,
166199
+ title,
166200
+ narrative,
166201
+ facts: safeFacts,
166202
+ projectId: project.id,
166203
+ source: "explicit"
166204
+ }, formationConfig);
166205
+ },
166054
166206
  FORMATION_TIMEOUT_MS,
166055
166207
  "Formation pipeline"
166056
166208
  );
@@ -166400,8 +166552,8 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
166400
166552
  mode: formationMode,
166401
166553
  useLLM: isLLMEnabled(),
166402
166554
  minValueScore: 0.3,
166403
- searchMemories: async (q, limit2, pid) => {
166404
- const result = await compactSearch({ query: q, limit: limit2, projectId: pid, status: "active", reader });
166555
+ searchMemories: async (q, limit2, pid, signal) => {
166556
+ const result = await compactSearch({ query: q, limit: limit2, projectId: pid, status: "active", reader, signal });
166405
166557
  if (result.entries.length === 0) return [];
166406
166558
  const details = await compactDetail(result.entries.map((e2) => e2.id), { reader });
166407
166559
  return details.documents.map((d, i2) => ({
@@ -166422,8 +166574,11 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
166422
166574
  },
166423
166575
  getEntityNames: () => graphManager.getEntityNames()
166424
166576
  };
166425
- const formed = await withTimeout(
166426
- runFormation({ entityName, type, title, narrative, facts: safeFacts, projectId: project.id, source: "explicit", topicKey }, formationConfig),
166577
+ const formed = await withTimeoutSignal(
166578
+ async (signal) => {
166579
+ formationConfig.signal = signal;
166580
+ return runFormation({ entityName, type, title, narrative, facts: safeFacts, projectId: project.id, source: "explicit", topicKey }, formationConfig);
166581
+ },
166427
166582
  FORMATION_TIMEOUT_MS,
166428
166583
  "Shadow formation"
166429
166584
  );
@@ -170200,7 +170355,7 @@ var init_serve_shared = __esm({
170200
170355
 
170201
170356
  // src/cli/version.ts
170202
170357
  function getCliVersion() {
170203
- return true ? "1.4.6" : pkg.version;
170358
+ return true ? "1.5.1" : pkg.version;
170204
170359
  }
170205
170360
  var init_version3 = __esm({
170206
170361
  "src/cli/version.ts"() {