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/cli/index.js CHANGED
@@ -3221,7 +3221,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
3221
3221
 
3222
3222
  // src/cli/version.ts
3223
3223
  function getCliVersion() {
3224
- return true ? "1.4.6" : pkg.version;
3224
+ return true ? "1.5.1" : pkg.version;
3225
3225
  }
3226
3226
  var init_version = __esm({
3227
3227
  "src/cli/version.ts"() {
@@ -21713,6 +21713,7 @@ var init_resolved_config = __esm({
21713
21713
  // src/config.ts
21714
21714
  var config_exports = {};
21715
21715
  __export(config_exports, {
21716
+ defaultEmbeddingModelFor: () => defaultEmbeddingModelFor,
21716
21717
  getAgentLLMApiKey: () => getAgentLLMApiKey,
21717
21718
  getAgentLLMBaseUrl: () => getAgentLLMBaseUrl,
21718
21719
  getAgentLLMModel: () => getAgentLLMModel,
@@ -21775,7 +21776,19 @@ function getEmbeddingBaseUrl() {
21775
21776
  return getResolvedEmbeddingLane().baseUrl || "https://api.openai.com/v1";
21776
21777
  }
21777
21778
  function getEmbeddingModel() {
21778
- return getResolvedEmbeddingLane().model || "text-embedding-3-small";
21779
+ const lane = getResolvedEmbeddingLane();
21780
+ return defaultEmbeddingModelFor(lane.baseUrl, lane.model);
21781
+ }
21782
+ function defaultEmbeddingModelFor(baseUrl, explicitModel) {
21783
+ if (explicitModel) return explicitModel;
21784
+ try {
21785
+ const url2 = baseUrl ? new URL(baseUrl) : null;
21786
+ if (url2 && (url2.hostname === "openrouter.ai" || url2.hostname.endsWith(".openrouter.ai"))) {
21787
+ return "qwen/qwen3-embedding-8b";
21788
+ }
21789
+ } catch {
21790
+ }
21791
+ return "text-embedding-3-small";
21779
21792
  }
21780
21793
  function getEmbeddingDimensions() {
21781
21794
  return getResolvedEmbeddingLane().dimensions ?? null;
@@ -22314,6 +22327,8 @@ function getPreferredBatchSize(config2) {
22314
22327
  }
22315
22328
  function parseBatchLimit(error40) {
22316
22329
  if (!(error40 instanceof Error)) return null;
22330
+ const status = error40 instanceof EmbeddingAPIError ? error40.status : Number(error40.message.match(/Embedding API error \((\d{3})\)/i)?.[1] ?? 0);
22331
+ if (status > 0 && status !== 400) return null;
22317
22332
  const explicit = error40.message.match(/should not be larger than\s+(\d+)/i);
22318
22333
  if (explicit) return parseInt(explicit[1], 10);
22319
22334
  if (/batch size/i.test(error40.message)) {
@@ -22326,37 +22341,99 @@ async function fetchWithRetry(url2, apiKey, body, options = {}, attempt = 0, nat
22326
22341
  const controller = new AbortController();
22327
22342
  const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) ? Math.max(100, Math.min(Math.floor(options.timeoutMs), 6e4)) : 1e4;
22328
22343
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
22344
+ const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
22329
22345
  let response;
22330
22346
  try {
22331
22347
  response = await fetch(url2, {
22332
22348
  method: "POST",
22333
22349
  headers: nativeGemini ? { "Content-Type": "application/json", "x-goog-api-key": apiKey } : { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` },
22334
22350
  body: JSON.stringify(body),
22335
- signal: controller.signal
22351
+ signal
22336
22352
  });
22337
22353
  } catch (err) {
22338
22354
  clearTimeout(timeout);
22339
- if (err instanceof Error && err.name === "AbortError") {
22355
+ if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
22340
22356
  throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url2}`);
22341
22357
  }
22342
22358
  throw err;
22343
22359
  }
22344
- clearTimeout(timeout);
22345
- if (response.ok) {
22346
- return response.json();
22360
+ try {
22361
+ if (response.ok) {
22362
+ return await raceWithAbort(
22363
+ response.json(),
22364
+ signal
22365
+ );
22366
+ }
22367
+ if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
22368
+ const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
22369
+ const retryAfter = response.headers.get("retry-after");
22370
+ const parsedRetryAfter = retryAfter ? parseInt(retryAfter, 10) * 1e3 : NaN;
22371
+ const waitMs = Number.isFinite(parsedRetryAfter) ? parsedRetryAfter : delay2;
22372
+ console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
22373
+ await waitForRetry(waitMs, options.signal);
22374
+ return fetchWithRetry(url2, apiKey, body, options, attempt + 1, nativeGemini);
22375
+ }
22376
+ const errorText2 = await raceWithAbort(
22377
+ response.text().catch(() => "unknown error"),
22378
+ signal
22379
+ );
22380
+ throw new EmbeddingAPIError(response.status, errorText2);
22381
+ } catch (err) {
22382
+ if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
22383
+ throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url2}`);
22384
+ }
22385
+ throw err;
22386
+ } finally {
22387
+ clearTimeout(timeout);
22347
22388
  }
22348
- if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
22349
- const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
22350
- const retryAfter = response.headers.get("retry-after");
22351
- const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : delay2;
22352
- console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
22389
+ }
22390
+ function abortError() {
22391
+ const error40 = new Error("Embedding API request aborted");
22392
+ error40.name = "AbortError";
22393
+ return error40;
22394
+ }
22395
+ function raceWithAbort(promise2, signal) {
22396
+ if (signal.aborted) return Promise.reject(abortError());
22397
+ return new Promise((resolve6, reject) => {
22398
+ const onAbort = () => {
22399
+ signal.removeEventListener("abort", onAbort);
22400
+ reject(abortError());
22401
+ };
22402
+ signal.addEventListener("abort", onAbort, { once: true });
22403
+ promise2.then(
22404
+ (value) => {
22405
+ signal.removeEventListener("abort", onAbort);
22406
+ resolve6(value);
22407
+ },
22408
+ (error40) => {
22409
+ signal.removeEventListener("abort", onAbort);
22410
+ reject(error40);
22411
+ }
22412
+ );
22413
+ });
22414
+ }
22415
+ async function waitForRetry(waitMs, signal) {
22416
+ if (!signal) {
22353
22417
  await new Promise((resolve6) => setTimeout(resolve6, waitMs));
22354
- return fetchWithRetry(url2, apiKey, body, options, attempt + 1, nativeGemini);
22418
+ return;
22355
22419
  }
22356
- const errorText2 = await response.text().catch(() => "unknown error");
22357
- throw new Error(`Embedding API error (${response.status}): ${errorText2}`);
22420
+ const abortSignal = signal;
22421
+ if (abortSignal.aborted) throw new Error("Embedding API retry aborted");
22422
+ await new Promise((resolve6, reject) => {
22423
+ const timer = setTimeout(done, waitMs);
22424
+ const onAbort = () => {
22425
+ clearTimeout(timer);
22426
+ abortSignal.removeEventListener("abort", onAbort);
22427
+ reject(new Error("Embedding API retry aborted"));
22428
+ };
22429
+ function done() {
22430
+ abortSignal.removeEventListener("abort", onAbort);
22431
+ resolve6();
22432
+ }
22433
+ abortSignal.addEventListener("abort", onAbort, { once: true });
22434
+ });
22358
22435
  }
22359
- 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;
22436
+ 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;
22360
22437
  var init_api_provider = __esm({
22361
22438
  "src/embedding/api-provider.ts"() {
22362
22439
  "use strict";
@@ -22375,6 +22452,14 @@ var init_api_provider = __esm({
22375
22452
  DASHSCOPE_MAX_BATCH_SIZE = 10;
22376
22453
  MAX_RETRIES2 = 3;
22377
22454
  BASE_DELAY_MS = 500;
22455
+ EmbeddingAPIError = class extends Error {
22456
+ status;
22457
+ constructor(status, detail) {
22458
+ super(`Embedding API error (${status}): ${detail}`);
22459
+ this.name = "EmbeddingAPIError";
22460
+ this.status = status;
22461
+ }
22462
+ };
22378
22463
  APIEmbeddingProvider = class _APIEmbeddingProvider {
22379
22464
  name;
22380
22465
  dimensions;
@@ -22586,15 +22671,14 @@ var init_api_provider = __esm({
22586
22671
  }
22587
22672
  } catch (error40) {
22588
22673
  const providerLimit = parseBatchLimit(error40);
22589
- const fallbackSize = providerLimit ?? Math.ceil(chunkTexts.length / 2);
22590
- if (chunkTexts.length > 1 && fallbackSize < chunkTexts.length) {
22674
+ if (providerLimit !== null && chunkTexts.length > 1 && providerLimit < chunkTexts.length) {
22591
22675
  console.error(
22592
- `[memorix] Embedding batch too large for provider, retrying in chunks of ${fallbackSize}`
22676
+ `[memorix] Embedding batch too large for provider, retrying in chunks of ${providerLimit}`
22593
22677
  );
22594
- for (let start = 0; start < chunkTexts.length; start += fallbackSize) {
22678
+ for (let start = 0; start < chunkTexts.length; start += providerLimit) {
22595
22679
  await processChunk(
22596
- chunkTexts.slice(start, start + fallbackSize),
22597
- chunkIndices.slice(start, start + fallbackSize)
22680
+ chunkTexts.slice(start, start + providerLimit),
22681
+ chunkIndices.slice(start, start + providerLimit)
22598
22682
  );
22599
22683
  }
22600
22684
  return;
@@ -23458,22 +23542,23 @@ function getLLMConfig() {
23458
23542
  function setLLMConfig(config2) {
23459
23543
  currentConfig = config2;
23460
23544
  }
23461
- async function callLLM(systemPrompt, userMessage) {
23545
+ async function callLLM(systemPrompt, userMessage, signal) {
23462
23546
  if (!currentConfig) {
23463
23547
  throw new Error("LLM not configured. Set MEMORIX_LLM_API_KEY or OPENAI_API_KEY.");
23464
23548
  }
23465
23549
  if (currentConfig.provider === "anthropic") {
23466
- return callAnthropic(systemPrompt, userMessage);
23550
+ return callAnthropic(systemPrompt, userMessage, signal);
23467
23551
  }
23468
- return callOpenAICompatible(systemPrompt, userMessage);
23552
+ return callOpenAICompatible(systemPrompt, userMessage, signal);
23469
23553
  }
23470
- async function callOpenAICompatible(systemPrompt, userMessage) {
23554
+ async function callOpenAICompatible(systemPrompt, userMessage, signal) {
23471
23555
  const config2 = currentConfig;
23472
23556
  let base = config2.baseUrl.replace(/\/+$/, "");
23473
23557
  if (!base.endsWith("/v1")) base += "/v1";
23474
23558
  const url2 = `${base}/chat/completions`;
23559
+ const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
23475
23560
  const response = await fetch(url2, {
23476
- signal: AbortSignal.timeout(LLM_CALL_TIMEOUT_MS),
23561
+ signal: fetchSignal,
23477
23562
  method: "POST",
23478
23563
  headers: {
23479
23564
  "Content-Type": "application/json",
@@ -23490,10 +23575,10 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
23490
23575
  })
23491
23576
  });
23492
23577
  if (!response.ok) {
23493
- const error40 = await readResponseText(response, void 0, 64 * 1024).catch(() => "unknown error");
23578
+ const error40 = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
23494
23579
  throw new Error(`LLM API error (${response.status}): ${error40}`);
23495
23580
  }
23496
- const data = await readResponseJson(response);
23581
+ const data = await readResponseJson(response, signal);
23497
23582
  return {
23498
23583
  content: data.choices[0]?.message?.content ?? "",
23499
23584
  usage: data.usage ? {
@@ -23502,11 +23587,12 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
23502
23587
  } : void 0
23503
23588
  };
23504
23589
  }
23505
- async function callAnthropic(systemPrompt, userMessage) {
23590
+ async function callAnthropic(systemPrompt, userMessage, signal) {
23506
23591
  const config2 = currentConfig;
23507
23592
  const url2 = `${config2.baseUrl}/messages`;
23593
+ const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
23508
23594
  const response = await fetch(url2, {
23509
- signal: AbortSignal.timeout(LLM_CALL_TIMEOUT_MS),
23595
+ signal: fetchSignal,
23510
23596
  method: "POST",
23511
23597
  headers: {
23512
23598
  "Content-Type": "application/json",
@@ -23524,10 +23610,10 @@ async function callAnthropic(systemPrompt, userMessage) {
23524
23610
  })
23525
23611
  });
23526
23612
  if (!response.ok) {
23527
- const error40 = await readResponseText(response, void 0, 64 * 1024).catch(() => "unknown error");
23613
+ const error40 = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
23528
23614
  throw new Error(`Anthropic API error (${response.status}): ${error40}`);
23529
23615
  }
23530
- const data = await readResponseJson(response);
23616
+ const data = await readResponseJson(response, signal);
23531
23617
  return {
23532
23618
  content: data.content[0]?.text ?? "",
23533
23619
  usage: data.usage ? {
@@ -24032,6 +24118,57 @@ function withTimeout(promise2, ms2, label) {
24032
24118
  );
24033
24119
  });
24034
24120
  }
24121
+ function withTimeoutSignal(operation, ms2, label, parentSignal) {
24122
+ const controller = new AbortController();
24123
+ const signal = parentSignal ? AbortSignal.any([parentSignal, controller.signal]) : controller.signal;
24124
+ return new Promise((resolve6, reject) => {
24125
+ let settled = false;
24126
+ let timedOut = false;
24127
+ let timer;
24128
+ const cleanup = () => {
24129
+ if (timer !== void 0) clearTimeout(timer);
24130
+ parentSignal?.removeEventListener("abort", onParentAbort);
24131
+ };
24132
+ const onParentAbort = () => {
24133
+ if (settled || timedOut) return;
24134
+ settled = true;
24135
+ cleanup();
24136
+ reject(parentSignal?.reason ?? new Error(`${label} aborted`));
24137
+ };
24138
+ parentSignal?.addEventListener("abort", onParentAbort, { once: true });
24139
+ timer = setTimeout(() => {
24140
+ if (settled) return;
24141
+ timedOut = true;
24142
+ controller.abort(new Error(`${label} timed out after ${ms2}ms`));
24143
+ settled = true;
24144
+ cleanup();
24145
+ reject(new Error(`${label} timed out after ${ms2}ms`));
24146
+ }, ms2);
24147
+ let pending;
24148
+ try {
24149
+ pending = operation(signal);
24150
+ } catch (error40) {
24151
+ settled = true;
24152
+ cleanup();
24153
+ reject(error40);
24154
+ return;
24155
+ }
24156
+ pending.then(
24157
+ (value) => {
24158
+ if (settled) return;
24159
+ settled = true;
24160
+ cleanup();
24161
+ resolve6(value);
24162
+ },
24163
+ (error40) => {
24164
+ if (settled) return;
24165
+ settled = true;
24166
+ cleanup();
24167
+ reject(timedOut ? new Error(`${label} timed out after ${ms2}ms`) : error40);
24168
+ }
24169
+ );
24170
+ });
24171
+ }
24035
24172
  var init_timeout = __esm({
24036
24173
  "src/timeout.ts"() {
24037
24174
  "use strict";
@@ -178207,7 +178344,13 @@ async function maintainLongTermMemories(input) {
178207
178344
  }
178208
178345
  for (const group of groups.values()) {
178209
178346
  if (group.length < 2) continue;
178210
- group.sort((a4, b4) => Date.parse(b4.createdAt) - Date.parse(a4.createdAt));
178347
+ group.sort((a4, b4) => {
178348
+ const byCreatedAt = Date.parse(b4.createdAt) - Date.parse(a4.createdAt);
178349
+ if (byCreatedAt !== 0) return byCreatedAt;
178350
+ const byUpdatedAt = Date.parse(b4.updatedAt) - Date.parse(a4.updatedAt);
178351
+ if (byUpdatedAt !== 0) return byUpdatedAt;
178352
+ return b4.id.localeCompare(a4.id);
178353
+ });
178211
178354
  const newest = group[0];
178212
178355
  for (const older of group.slice(1)) {
178213
178356
  try {
@@ -184723,16 +184866,18 @@ async function searchObservations(options) {
184723
184866
  );
184724
184867
  } else {
184725
184868
  const EMBEDDING_TIMEOUT_MS = 15e3;
184726
- queryVector = await withTimeout(
184727
- provider2.embedInput ? provider2.embedInput(
184869
+ queryVector = await withTimeoutSignal(
184870
+ (signal) => provider2.embedInput ? provider2.embedInput(
184728
184871
  { modality: "text", text: expandedEmbeddingQuery },
184729
- { intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false }
184872
+ { intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false, signal }
184730
184873
  ) : provider2.embed(expandedEmbeddingQuery, {
184731
184874
  timeoutMs: EMBEDDING_TIMEOUT_MS,
184732
- retry: false
184875
+ retry: false,
184876
+ signal
184733
184877
  }),
184734
184878
  EMBEDDING_TIMEOUT_MS,
184735
- "Embedding"
184879
+ "Embedding",
184880
+ options.signal
184736
184881
  );
184737
184882
  mark("embedding");
184738
184883
  const cjkRatio = (originalQuery.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length / originalQuery.length;
@@ -199192,13 +199337,13 @@ function verifyType(declaredType, narrative, title) {
199192
199337
  }
199193
199338
  return { type: declaredType, corrected: false };
199194
199339
  }
199195
- async function extractFactsWithLLM(narrative, title, existingFacts) {
199340
+ async function extractFactsWithLLM(narrative, title, existingFacts, signal) {
199196
199341
  try {
199197
199342
  const { callLLM: callLLM2 } = await Promise.resolve().then(() => (init_provider2(), provider_exports2));
199198
199343
  const input = `Title: ${title}
199199
199344
  Content: ${narrative}${existingFacts.length > 0 ? `
199200
199345
  Already known facts (don't repeat): ${existingFacts.join("; ")}` : ""}`;
199201
- const response = await callLLM2(LLM_EXTRACT_PROMPT, input);
199346
+ const response = await callLLM2(LLM_EXTRACT_PROMPT, input, signal);
199202
199347
  const text2 = response.content.trim();
199203
199348
  const jsonMatch = text2.match(/\{[\s\S]*\}/);
199204
199349
  if (!jsonMatch) return [];
@@ -199207,15 +199352,16 @@ Already known facts (don't repeat): ${existingFacts.join("; ")}` : ""}`;
199207
199352
  if (!Array.isArray(facts)) return [];
199208
199353
  const existingLower = new Set(existingFacts.map((f7) => f7.toLowerCase().trim()));
199209
199354
  return facts.filter((f7) => typeof f7 === "string" && f7.length >= 5).filter((f7) => !existingLower.has(f7.toLowerCase().trim())).slice(0, 10);
199210
- } catch {
199355
+ } catch (error40) {
199356
+ if (signal?.aborted) throw error40;
199211
199357
  return [];
199212
199358
  }
199213
199359
  }
199214
- async function runExtract(input, existingEntities, useLLM = false) {
199360
+ async function runExtract(input, existingEntities, useLLM = false, signal) {
199215
199361
  const callerFacts = input.facts ?? [];
199216
199362
  let extractedFacts;
199217
199363
  if (useLLM) {
199218
- extractedFacts = await extractFactsWithLLM(input.narrative, input.title, callerFacts);
199364
+ extractedFacts = await extractFactsWithLLM(input.narrative, input.title, callerFacts, signal);
199219
199365
  if (extractedFacts.length === 0) {
199220
199366
  extractedFacts = extractFacts2(input.narrative, callerFacts);
199221
199367
  }
@@ -199435,7 +199581,7 @@ function mergeFacts2(oldFacts, newFacts) {
199435
199581
  }
199436
199582
  return merged;
199437
199583
  }
199438
- async function resolveWithLLM(extracted, hits, getObservation2) {
199584
+ async function resolveWithLLM(extracted, hits, getObservation2, signal) {
199439
199585
  try {
199440
199586
  const { callLLM: callLLM2 } = await Promise.resolve().then(() => (init_provider2(), provider_exports2));
199441
199587
  const existingMemories = hits.slice(0, 5).map((h5, i4) => ({
@@ -199452,7 +199598,7 @@ Facts: ${extracted.facts.join("; ")}
199452
199598
 
199453
199599
  EXISTING MEMORIES:
199454
199600
  ${existingMemories.map((m6) => `[ID:${m6.id}] ${m6.title} | ${m6.content} | Facts: ${m6.facts}`).join("\n")}`;
199455
- const response = await callLLM2(LLM_RESOLVE_PROMPT, input);
199601
+ const response = await callLLM2(LLM_RESOLVE_PROMPT, input, signal);
199456
199602
  const text2 = response.content.trim();
199457
199603
  const jsonMatch = text2.match(/\{[\s\S]*\}/);
199458
199604
  if (!jsonMatch) return null;
@@ -199489,7 +199635,8 @@ ${existingMemories.map((m6) => `[ID:${m6.id}] ${m6.title} | ${m6.content} | Fact
199489
199635
  };
199490
199636
  }
199491
199637
  return null;
199492
- } catch {
199638
+ } catch (error40) {
199639
+ if (signal?.aborted) throw error40;
199493
199640
  return null;
199494
199641
  }
199495
199642
  }
@@ -199507,19 +199654,20 @@ function scoreCandidate(extracted, candidate2) {
199507
199654
  const contradiction = hasContradiction(candidate2.narrative, extracted.narrative);
199508
199655
  return { score, searchSimilarity, entityMatch, richer, contradiction };
199509
199656
  }
199510
- async function runResolve(extracted, projectId, searchMemories3, getObservation2, useLLM = false) {
199657
+ async function runResolve(extracted, projectId, searchMemories3, getObservation2, useLLM = false, signal) {
199511
199658
  const query = `${extracted.title} ${extracted.narrative.substring(0, 200)}`;
199512
199659
  let hits;
199513
199660
  try {
199514
- hits = await searchMemories3(query, 5, projectId);
199515
- } catch {
199661
+ hits = await searchMemories3(query, 5, projectId, signal);
199662
+ } catch (error40) {
199663
+ if (signal?.aborted) throw error40;
199516
199664
  return { action: "new", reason: "Search unavailable, defaulting to new" };
199517
199665
  }
199518
199666
  if (hits.length === 0) {
199519
199667
  return { action: "new", reason: "No similar existing memories found" };
199520
199668
  }
199521
199669
  if (useLLM) {
199522
- const llmResult = await resolveWithLLM(extracted, hits, getObservation2);
199670
+ const llmResult = await resolveWithLLM(extracted, hits, getObservation2, signal);
199523
199671
  if (llmResult) return llmResult;
199524
199672
  }
199525
199673
  const scored = hits.map((hit) => ({
@@ -199941,7 +200089,7 @@ async function runFormation(input, config2) {
199941
200089
  const existingEntities = config2.getEntityNames();
199942
200090
  const extractStartTime = Date.now();
199943
200091
  emitStageEvent("extract", "start");
199944
- const extraction = await runExtract(input, existingEntities, config2.useLLM);
200092
+ const extraction = await runExtract(input, existingEntities, config2.useLLM, config2.signal);
199945
200093
  stageDurationsMs.extract = Date.now() - extractStartTime;
199946
200094
  emitStageEvent("extract", "success", stageDurationsMs.extract);
199947
200095
  stagesCompleted = 1;
@@ -199961,7 +200109,8 @@ async function runFormation(input, config2) {
199961
200109
  input.projectId,
199962
200110
  config2.searchMemories,
199963
200111
  config2.getObservation,
199964
- config2.useLLM
200112
+ config2.useLLM,
200113
+ config2.signal
199965
200114
  );
199966
200115
  stageDurationsMs.resolve = Date.now() - resolveStartTime;
199967
200116
  emitStageEvent("resolve", "success", stageDurationsMs.resolve);
@@ -258695,7 +258844,7 @@ The path should point to a directory containing a .git folder.`
258695
258844
  };
258696
258845
  const server = existingServer ?? new McpServer({
258697
258846
  name: "memorix",
258698
- version: true ? "1.4.6" : "1.0.1"
258847
+ version: true ? "1.5.1" : "1.0.1"
258699
258848
  });
258700
258849
  const originalRegisterTool = server.registerTool.bind(server);
258701
258850
  server.registerTool = ((name, ...args) => {
@@ -258884,8 +259033,8 @@ The path should point to a directory containing a .git folder.`
258884
259033
  mode: "active",
258885
259034
  useLLM: isLLMEnabled(),
258886
259035
  minValueScore: 0.3,
258887
- searchMemories: async (q4, limit2, pid) => {
258888
- const result = await compactSearch({ query: q4, limit: limit2, projectId: pid, status: "active", reader });
259036
+ searchMemories: async (q4, limit2, pid, signal) => {
259037
+ const result = await compactSearch({ query: q4, limit: limit2, projectId: pid, status: "active", reader, signal });
258889
259038
  if (result.entries.length === 0) return [];
258890
259039
  const details = await compactDetail(result.entries.map((e5) => e5.id), { reader });
258891
259040
  return details.documents.map((d4, i4) => ({
@@ -258915,16 +259064,19 @@ The path should point to a directory containing a .git folder.`
258915
259064
  getEntityNames: () => graphManager.getEntityNames(),
258916
259065
  onStageEvent: onFormationStageEvent
258917
259066
  };
258918
- formationResult = await withTimeout(
258919
- runFormation({
258920
- entityName,
258921
- type,
258922
- title,
258923
- narrative,
258924
- facts: safeFacts,
258925
- projectId: project.id,
258926
- source: "explicit"
258927
- }, formationConfig),
259067
+ formationResult = await withTimeoutSignal(
259068
+ async (signal) => {
259069
+ formationConfig.signal = signal;
259070
+ return runFormation({
259071
+ entityName,
259072
+ type,
259073
+ title,
259074
+ narrative,
259075
+ facts: safeFacts,
259076
+ projectId: project.id,
259077
+ source: "explicit"
259078
+ }, formationConfig);
259079
+ },
258928
259080
  FORMATION_TIMEOUT_MS,
258929
259081
  "Formation pipeline"
258930
259082
  );
@@ -259274,8 +259426,8 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
259274
259426
  mode: formationMode,
259275
259427
  useLLM: isLLMEnabled(),
259276
259428
  minValueScore: 0.3,
259277
- searchMemories: async (q4, limit2, pid) => {
259278
- const result = await compactSearch({ query: q4, limit: limit2, projectId: pid, status: "active", reader });
259429
+ searchMemories: async (q4, limit2, pid, signal) => {
259430
+ const result = await compactSearch({ query: q4, limit: limit2, projectId: pid, status: "active", reader, signal });
259279
259431
  if (result.entries.length === 0) return [];
259280
259432
  const details = await compactDetail(result.entries.map((e5) => e5.id), { reader });
259281
259433
  return details.documents.map((d4, i4) => ({
@@ -259296,8 +259448,11 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
259296
259448
  },
259297
259449
  getEntityNames: () => graphManager.getEntityNames()
259298
259450
  };
259299
- const formed = await withTimeout(
259300
- runFormation({ entityName, type, title, narrative, facts: safeFacts, projectId: project.id, source: "explicit", topicKey }, formationConfig),
259451
+ const formed = await withTimeoutSignal(
259452
+ async (signal) => {
259453
+ formationConfig.signal = signal;
259454
+ return runFormation({ entityName, type, title, narrative, facts: safeFacts, projectId: project.id, source: "explicit", topicKey }, formationConfig);
259455
+ },
259301
259456
  FORMATION_TIMEOUT_MS,
259302
259457
  "Shadow formation"
259303
259458
  );
File without changes