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
@@ -3124,6 +3124,7 @@ var init_resolved_config = __esm({
3124
3124
  // src/config.ts
3125
3125
  var config_exports = {};
3126
3126
  __export(config_exports, {
3127
+ defaultEmbeddingModelFor: () => defaultEmbeddingModelFor,
3127
3128
  getAgentLLMApiKey: () => getAgentLLMApiKey,
3128
3129
  getAgentLLMBaseUrl: () => getAgentLLMBaseUrl,
3129
3130
  getAgentLLMModel: () => getAgentLLMModel,
@@ -3186,7 +3187,19 @@ function getEmbeddingBaseUrl() {
3186
3187
  return getResolvedEmbeddingLane().baseUrl || "https://api.openai.com/v1";
3187
3188
  }
3188
3189
  function getEmbeddingModel() {
3189
- return getResolvedEmbeddingLane().model || "text-embedding-3-small";
3190
+ const lane = getResolvedEmbeddingLane();
3191
+ return defaultEmbeddingModelFor(lane.baseUrl, lane.model);
3192
+ }
3193
+ function defaultEmbeddingModelFor(baseUrl, explicitModel) {
3194
+ if (explicitModel) return explicitModel;
3195
+ try {
3196
+ const url = baseUrl ? new URL(baseUrl) : null;
3197
+ if (url && (url.hostname === "openrouter.ai" || url.hostname.endsWith(".openrouter.ai"))) {
3198
+ return "qwen/qwen3-embedding-8b";
3199
+ }
3200
+ } catch {
3201
+ }
3202
+ return "text-embedding-3-small";
3190
3203
  }
3191
3204
  function getEmbeddingDimensions() {
3192
3205
  return getResolvedEmbeddingLane().dimensions ?? null;
@@ -3725,6 +3738,8 @@ function getPreferredBatchSize(config) {
3725
3738
  }
3726
3739
  function parseBatchLimit(error) {
3727
3740
  if (!(error instanceof Error)) return null;
3741
+ const status = error instanceof EmbeddingAPIError ? error.status : Number(error.message.match(/Embedding API error \((\d{3})\)/i)?.[1] ?? 0);
3742
+ if (status > 0 && status !== 400) return null;
3728
3743
  const explicit = error.message.match(/should not be larger than\s+(\d+)/i);
3729
3744
  if (explicit) return parseInt(explicit[1], 10);
3730
3745
  if (/batch size/i.test(error.message)) {
@@ -3737,37 +3752,99 @@ async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0, nati
3737
3752
  const controller = new AbortController();
3738
3753
  const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) ? Math.max(100, Math.min(Math.floor(options.timeoutMs), 6e4)) : 1e4;
3739
3754
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
3755
+ const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
3740
3756
  let response;
3741
3757
  try {
3742
3758
  response = await fetch(url, {
3743
3759
  method: "POST",
3744
3760
  headers: nativeGemini ? { "Content-Type": "application/json", "x-goog-api-key": apiKey } : { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` },
3745
3761
  body: JSON.stringify(body),
3746
- signal: controller.signal
3762
+ signal
3747
3763
  });
3748
3764
  } catch (err) {
3749
3765
  clearTimeout(timeout);
3750
- if (err instanceof Error && err.name === "AbortError") {
3766
+ if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
3751
3767
  throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
3752
3768
  }
3753
3769
  throw err;
3754
3770
  }
3755
- clearTimeout(timeout);
3756
- if (response.ok) {
3757
- return response.json();
3771
+ try {
3772
+ if (response.ok) {
3773
+ return await raceWithAbort(
3774
+ response.json(),
3775
+ signal
3776
+ );
3777
+ }
3778
+ if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
3779
+ const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
3780
+ const retryAfter = response.headers.get("retry-after");
3781
+ const parsedRetryAfter = retryAfter ? parseInt(retryAfter, 10) * 1e3 : NaN;
3782
+ const waitMs = Number.isFinite(parsedRetryAfter) ? parsedRetryAfter : delay2;
3783
+ console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
3784
+ await waitForRetry(waitMs, options.signal);
3785
+ return fetchWithRetry(url, apiKey, body, options, attempt + 1, nativeGemini);
3786
+ }
3787
+ const errorText2 = await raceWithAbort(
3788
+ response.text().catch(() => "unknown error"),
3789
+ signal
3790
+ );
3791
+ throw new EmbeddingAPIError(response.status, errorText2);
3792
+ } catch (err) {
3793
+ if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
3794
+ throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
3795
+ }
3796
+ throw err;
3797
+ } finally {
3798
+ clearTimeout(timeout);
3758
3799
  }
3759
- if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
3760
- const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
3761
- const retryAfter = response.headers.get("retry-after");
3762
- const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : delay2;
3763
- console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
3800
+ }
3801
+ function abortError() {
3802
+ const error = new Error("Embedding API request aborted");
3803
+ error.name = "AbortError";
3804
+ return error;
3805
+ }
3806
+ function raceWithAbort(promise, signal) {
3807
+ if (signal.aborted) return Promise.reject(abortError());
3808
+ return new Promise((resolve, reject) => {
3809
+ const onAbort = () => {
3810
+ signal.removeEventListener("abort", onAbort);
3811
+ reject(abortError());
3812
+ };
3813
+ signal.addEventListener("abort", onAbort, { once: true });
3814
+ promise.then(
3815
+ (value) => {
3816
+ signal.removeEventListener("abort", onAbort);
3817
+ resolve(value);
3818
+ },
3819
+ (error) => {
3820
+ signal.removeEventListener("abort", onAbort);
3821
+ reject(error);
3822
+ }
3823
+ );
3824
+ });
3825
+ }
3826
+ async function waitForRetry(waitMs, signal) {
3827
+ if (!signal) {
3764
3828
  await new Promise((resolve) => setTimeout(resolve, waitMs));
3765
- return fetchWithRetry(url, apiKey, body, options, attempt + 1, nativeGemini);
3829
+ return;
3766
3830
  }
3767
- const errorText2 = await response.text().catch(() => "unknown error");
3768
- throw new Error(`Embedding API error (${response.status}): ${errorText2}`);
3831
+ const abortSignal = signal;
3832
+ if (abortSignal.aborted) throw new Error("Embedding API retry aborted");
3833
+ await new Promise((resolve, reject) => {
3834
+ const timer = setTimeout(done, waitMs);
3835
+ const onAbort = () => {
3836
+ clearTimeout(timer);
3837
+ abortSignal.removeEventListener("abort", onAbort);
3838
+ reject(new Error("Embedding API retry aborted"));
3839
+ };
3840
+ function done() {
3841
+ abortSignal.removeEventListener("abort", onAbort);
3842
+ resolve();
3843
+ }
3844
+ abortSignal.addEventListener("abort", onAbort, { once: true });
3845
+ });
3769
3846
  }
3770
- 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;
3847
+ 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;
3771
3848
  var init_api_provider = __esm({
3772
3849
  "src/embedding/api-provider.ts"() {
3773
3850
  "use strict";
@@ -3786,6 +3863,14 @@ var init_api_provider = __esm({
3786
3863
  DASHSCOPE_MAX_BATCH_SIZE = 10;
3787
3864
  MAX_RETRIES2 = 3;
3788
3865
  BASE_DELAY_MS = 500;
3866
+ EmbeddingAPIError = class extends Error {
3867
+ status;
3868
+ constructor(status, detail) {
3869
+ super(`Embedding API error (${status}): ${detail}`);
3870
+ this.name = "EmbeddingAPIError";
3871
+ this.status = status;
3872
+ }
3873
+ };
3789
3874
  APIEmbeddingProvider = class _APIEmbeddingProvider {
3790
3875
  name;
3791
3876
  dimensions;
@@ -3997,15 +4082,14 @@ var init_api_provider = __esm({
3997
4082
  }
3998
4083
  } catch (error) {
3999
4084
  const providerLimit = parseBatchLimit(error);
4000
- const fallbackSize = providerLimit ?? Math.ceil(chunkTexts.length / 2);
4001
- if (chunkTexts.length > 1 && fallbackSize < chunkTexts.length) {
4085
+ if (providerLimit !== null && chunkTexts.length > 1 && providerLimit < chunkTexts.length) {
4002
4086
  console.error(
4003
- `[memorix] Embedding batch too large for provider, retrying in chunks of ${fallbackSize}`
4087
+ `[memorix] Embedding batch too large for provider, retrying in chunks of ${providerLimit}`
4004
4088
  );
4005
- for (let start = 0; start < chunkTexts.length; start += fallbackSize) {
4089
+ for (let start = 0; start < chunkTexts.length; start += providerLimit) {
4006
4090
  await processChunk(
4007
- chunkTexts.slice(start, start + fallbackSize),
4008
- chunkIndices.slice(start, start + fallbackSize)
4091
+ chunkTexts.slice(start, start + providerLimit),
4092
+ chunkIndices.slice(start, start + providerLimit)
4009
4093
  );
4010
4094
  }
4011
4095
  return;
@@ -4869,22 +4953,23 @@ function getLLMConfig() {
4869
4953
  function setLLMConfig(config) {
4870
4954
  currentConfig = config;
4871
4955
  }
4872
- async function callLLM(systemPrompt, userMessage) {
4956
+ async function callLLM(systemPrompt, userMessage, signal) {
4873
4957
  if (!currentConfig) {
4874
4958
  throw new Error("LLM not configured. Set MEMORIX_LLM_API_KEY or OPENAI_API_KEY.");
4875
4959
  }
4876
4960
  if (currentConfig.provider === "anthropic") {
4877
- return callAnthropic(systemPrompt, userMessage);
4961
+ return callAnthropic(systemPrompt, userMessage, signal);
4878
4962
  }
4879
- return callOpenAICompatible(systemPrompt, userMessage);
4963
+ return callOpenAICompatible(systemPrompt, userMessage, signal);
4880
4964
  }
4881
- async function callOpenAICompatible(systemPrompt, userMessage) {
4965
+ async function callOpenAICompatible(systemPrompt, userMessage, signal) {
4882
4966
  const config = currentConfig;
4883
4967
  let base = config.baseUrl.replace(/\/+$/, "");
4884
4968
  if (!base.endsWith("/v1")) base += "/v1";
4885
4969
  const url = `${base}/chat/completions`;
4970
+ const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
4886
4971
  const response = await fetch(url, {
4887
- signal: AbortSignal.timeout(LLM_CALL_TIMEOUT_MS),
4972
+ signal: fetchSignal,
4888
4973
  method: "POST",
4889
4974
  headers: {
4890
4975
  "Content-Type": "application/json",
@@ -4901,10 +4986,10 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
4901
4986
  })
4902
4987
  });
4903
4988
  if (!response.ok) {
4904
- const error = await readResponseText(response, void 0, 64 * 1024).catch(() => "unknown error");
4989
+ const error = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
4905
4990
  throw new Error(`LLM API error (${response.status}): ${error}`);
4906
4991
  }
4907
- const data = await readResponseJson(response);
4992
+ const data = await readResponseJson(response, signal);
4908
4993
  return {
4909
4994
  content: data.choices[0]?.message?.content ?? "",
4910
4995
  usage: data.usage ? {
@@ -4913,11 +4998,12 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
4913
4998
  } : void 0
4914
4999
  };
4915
5000
  }
4916
- async function callAnthropic(systemPrompt, userMessage) {
5001
+ async function callAnthropic(systemPrompt, userMessage, signal) {
4917
5002
  const config = currentConfig;
4918
5003
  const url = `${config.baseUrl}/messages`;
5004
+ const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
4919
5005
  const response = await fetch(url, {
4920
- signal: AbortSignal.timeout(LLM_CALL_TIMEOUT_MS),
5006
+ signal: fetchSignal,
4921
5007
  method: "POST",
4922
5008
  headers: {
4923
5009
  "Content-Type": "application/json",
@@ -4935,10 +5021,10 @@ async function callAnthropic(systemPrompt, userMessage) {
4935
5021
  })
4936
5022
  });
4937
5023
  if (!response.ok) {
4938
- const error = await readResponseText(response, void 0, 64 * 1024).catch(() => "unknown error");
5024
+ const error = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
4939
5025
  throw new Error(`Anthropic API error (${response.status}): ${error}`);
4940
5026
  }
4941
- const data = await readResponseJson(response);
5027
+ const data = await readResponseJson(response, signal);
4942
5028
  return {
4943
5029
  content: data.content[0]?.text ?? "",
4944
5030
  usage: data.usage ? {
@@ -5443,6 +5529,57 @@ function withTimeout(promise, ms, label) {
5443
5529
  );
5444
5530
  });
5445
5531
  }
5532
+ function withTimeoutSignal(operation, ms, label, parentSignal) {
5533
+ const controller = new AbortController();
5534
+ const signal = parentSignal ? AbortSignal.any([parentSignal, controller.signal]) : controller.signal;
5535
+ return new Promise((resolve, reject) => {
5536
+ let settled = false;
5537
+ let timedOut = false;
5538
+ let timer;
5539
+ const cleanup = () => {
5540
+ if (timer !== void 0) clearTimeout(timer);
5541
+ parentSignal?.removeEventListener("abort", onParentAbort);
5542
+ };
5543
+ const onParentAbort = () => {
5544
+ if (settled || timedOut) return;
5545
+ settled = true;
5546
+ cleanup();
5547
+ reject(parentSignal?.reason ?? new Error(`${label} aborted`));
5548
+ };
5549
+ parentSignal?.addEventListener("abort", onParentAbort, { once: true });
5550
+ timer = setTimeout(() => {
5551
+ if (settled) return;
5552
+ timedOut = true;
5553
+ controller.abort(new Error(`${label} timed out after ${ms}ms`));
5554
+ settled = true;
5555
+ cleanup();
5556
+ reject(new Error(`${label} timed out after ${ms}ms`));
5557
+ }, ms);
5558
+ let pending;
5559
+ try {
5560
+ pending = operation(signal);
5561
+ } catch (error) {
5562
+ settled = true;
5563
+ cleanup();
5564
+ reject(error);
5565
+ return;
5566
+ }
5567
+ pending.then(
5568
+ (value) => {
5569
+ if (settled) return;
5570
+ settled = true;
5571
+ cleanup();
5572
+ resolve(value);
5573
+ },
5574
+ (error) => {
5575
+ if (settled) return;
5576
+ settled = true;
5577
+ cleanup();
5578
+ reject(timedOut ? new Error(`${label} timed out after ${ms}ms`) : error);
5579
+ }
5580
+ );
5581
+ });
5582
+ }
5446
5583
  var init_timeout = __esm({
5447
5584
  "src/timeout.ts"() {
5448
5585
  "use strict";
@@ -6307,16 +6444,18 @@ async function searchObservations(options) {
6307
6444
  );
6308
6445
  } else {
6309
6446
  const EMBEDDING_TIMEOUT_MS = 15e3;
6310
- queryVector = await withTimeout(
6311
- provider2.embedInput ? provider2.embedInput(
6447
+ queryVector = await withTimeoutSignal(
6448
+ (signal) => provider2.embedInput ? provider2.embedInput(
6312
6449
  { modality: "text", text: expandedEmbeddingQuery },
6313
- { intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false }
6450
+ { intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false, signal }
6314
6451
  ) : provider2.embed(expandedEmbeddingQuery, {
6315
6452
  timeoutMs: EMBEDDING_TIMEOUT_MS,
6316
- retry: false
6453
+ retry: false,
6454
+ signal
6317
6455
  }),
6318
6456
  EMBEDDING_TIMEOUT_MS,
6319
- "Embedding"
6457
+ "Embedding",
6458
+ options.signal
6320
6459
  );
6321
6460
  mark("embedding");
6322
6461
  const cjkRatio = (originalQuery.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length / originalQuery.length;
@@ -142914,7 +143053,13 @@ async function maintainLongTermMemories(input) {
142914
143053
  }
142915
143054
  for (const group of groups.values()) {
142916
143055
  if (group.length < 2) continue;
142917
- group.sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt));
143056
+ group.sort((a, b) => {
143057
+ const byCreatedAt = Date.parse(b.createdAt) - Date.parse(a.createdAt);
143058
+ if (byCreatedAt !== 0) return byCreatedAt;
143059
+ const byUpdatedAt = Date.parse(b.updatedAt) - Date.parse(a.updatedAt);
143060
+ if (byUpdatedAt !== 0) return byUpdatedAt;
143061
+ return b.id.localeCompare(a.id);
143062
+ });
142918
143063
  const newest = group[0];
142919
143064
  for (const older of group.slice(1)) {
142920
143065
  try {