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
@@ -1868,6 +1868,7 @@ var init_resolved_config = __esm({
1868
1868
  // src/config.ts
1869
1869
  var config_exports = {};
1870
1870
  __export(config_exports, {
1871
+ defaultEmbeddingModelFor: () => defaultEmbeddingModelFor,
1871
1872
  getAgentLLMApiKey: () => getAgentLLMApiKey,
1872
1873
  getAgentLLMBaseUrl: () => getAgentLLMBaseUrl,
1873
1874
  getAgentLLMModel: () => getAgentLLMModel,
@@ -1930,7 +1931,19 @@ function getEmbeddingBaseUrl() {
1930
1931
  return getResolvedEmbeddingLane().baseUrl || "https://api.openai.com/v1";
1931
1932
  }
1932
1933
  function getEmbeddingModel() {
1933
- return getResolvedEmbeddingLane().model || "text-embedding-3-small";
1934
+ const lane = getResolvedEmbeddingLane();
1935
+ return defaultEmbeddingModelFor(lane.baseUrl, lane.model);
1936
+ }
1937
+ function defaultEmbeddingModelFor(baseUrl, explicitModel) {
1938
+ if (explicitModel) return explicitModel;
1939
+ try {
1940
+ const url = baseUrl ? new URL(baseUrl) : null;
1941
+ if (url && (url.hostname === "openrouter.ai" || url.hostname.endsWith(".openrouter.ai"))) {
1942
+ return "qwen/qwen3-embedding-8b";
1943
+ }
1944
+ } catch {
1945
+ }
1946
+ return "text-embedding-3-small";
1934
1947
  }
1935
1948
  function getEmbeddingDimensions() {
1936
1949
  return getResolvedEmbeddingLane().dimensions ?? null;
@@ -2070,22 +2083,23 @@ function getLLMConfig() {
2070
2083
  function setLLMConfig(config) {
2071
2084
  currentConfig = config;
2072
2085
  }
2073
- async function callLLM(systemPrompt, userMessage) {
2086
+ async function callLLM(systemPrompt, userMessage, signal) {
2074
2087
  if (!currentConfig) {
2075
2088
  throw new Error("LLM not configured. Set MEMORIX_LLM_API_KEY or OPENAI_API_KEY.");
2076
2089
  }
2077
2090
  if (currentConfig.provider === "anthropic") {
2078
- return callAnthropic(systemPrompt, userMessage);
2091
+ return callAnthropic(systemPrompt, userMessage, signal);
2079
2092
  }
2080
- return callOpenAICompatible(systemPrompt, userMessage);
2093
+ return callOpenAICompatible(systemPrompt, userMessage, signal);
2081
2094
  }
2082
- async function callOpenAICompatible(systemPrompt, userMessage) {
2095
+ async function callOpenAICompatible(systemPrompt, userMessage, signal) {
2083
2096
  const config = currentConfig;
2084
2097
  let base = config.baseUrl.replace(/\/+$/, "");
2085
2098
  if (!base.endsWith("/v1")) base += "/v1";
2086
2099
  const url = `${base}/chat/completions`;
2100
+ const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
2087
2101
  const response = await fetch(url, {
2088
- signal: AbortSignal.timeout(LLM_CALL_TIMEOUT_MS),
2102
+ signal: fetchSignal,
2089
2103
  method: "POST",
2090
2104
  headers: {
2091
2105
  "Content-Type": "application/json",
@@ -2102,10 +2116,10 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
2102
2116
  })
2103
2117
  });
2104
2118
  if (!response.ok) {
2105
- const error = await readResponseText(response, void 0, 64 * 1024).catch(() => "unknown error");
2119
+ const error = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
2106
2120
  throw new Error(`LLM API error (${response.status}): ${error}`);
2107
2121
  }
2108
- const data = await readResponseJson(response);
2122
+ const data = await readResponseJson(response, signal);
2109
2123
  return {
2110
2124
  content: data.choices[0]?.message?.content ?? "",
2111
2125
  usage: data.usage ? {
@@ -2114,11 +2128,12 @@ async function callOpenAICompatible(systemPrompt, userMessage) {
2114
2128
  } : void 0
2115
2129
  };
2116
2130
  }
2117
- async function callAnthropic(systemPrompt, userMessage) {
2131
+ async function callAnthropic(systemPrompt, userMessage, signal) {
2118
2132
  const config = currentConfig;
2119
2133
  const url = `${config.baseUrl}/messages`;
2134
+ const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
2120
2135
  const response = await fetch(url, {
2121
- signal: AbortSignal.timeout(LLM_CALL_TIMEOUT_MS),
2136
+ signal: fetchSignal,
2122
2137
  method: "POST",
2123
2138
  headers: {
2124
2139
  "Content-Type": "application/json",
@@ -2136,10 +2151,10 @@ async function callAnthropic(systemPrompt, userMessage) {
2136
2151
  })
2137
2152
  });
2138
2153
  if (!response.ok) {
2139
- const error = await readResponseText(response, void 0, 64 * 1024).catch(() => "unknown error");
2154
+ const error = await readResponseText(response, signal, 64 * 1024).catch(() => "unknown error");
2140
2155
  throw new Error(`Anthropic API error (${response.status}): ${error}`);
2141
2156
  }
2142
- const data = await readResponseJson(response);
2157
+ const data = await readResponseJson(response, signal);
2143
2158
  return {
2144
2159
  content: data.content[0]?.text ?? "",
2145
2160
  usage: data.usage ? {
@@ -3842,6 +3857,8 @@ function getPreferredBatchSize(config) {
3842
3857
  }
3843
3858
  function parseBatchLimit(error) {
3844
3859
  if (!(error instanceof Error)) return null;
3860
+ const status = error instanceof EmbeddingAPIError ? error.status : Number(error.message.match(/Embedding API error \((\d{3})\)/i)?.[1] ?? 0);
3861
+ if (status > 0 && status !== 400) return null;
3845
3862
  const explicit = error.message.match(/should not be larger than\s+(\d+)/i);
3846
3863
  if (explicit) return parseInt(explicit[1], 10);
3847
3864
  if (/batch size/i.test(error.message)) {
@@ -3854,37 +3871,99 @@ async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0, nati
3854
3871
  const controller = new AbortController();
3855
3872
  const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) ? Math.max(100, Math.min(Math.floor(options.timeoutMs), 6e4)) : 1e4;
3856
3873
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
3874
+ const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
3857
3875
  let response;
3858
3876
  try {
3859
3877
  response = await fetch(url, {
3860
3878
  method: "POST",
3861
3879
  headers: nativeGemini ? { "Content-Type": "application/json", "x-goog-api-key": apiKey } : { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` },
3862
3880
  body: JSON.stringify(body),
3863
- signal: controller.signal
3881
+ signal
3864
3882
  });
3865
3883
  } catch (err) {
3866
3884
  clearTimeout(timeout);
3867
- if (err instanceof Error && err.name === "AbortError") {
3885
+ if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
3868
3886
  throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
3869
3887
  }
3870
3888
  throw err;
3871
3889
  }
3872
- clearTimeout(timeout);
3873
- if (response.ok) {
3874
- return response.json();
3890
+ try {
3891
+ if (response.ok) {
3892
+ return await raceWithAbort(
3893
+ response.json(),
3894
+ signal
3895
+ );
3896
+ }
3897
+ if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
3898
+ const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
3899
+ const retryAfter = response.headers.get("retry-after");
3900
+ const parsedRetryAfter = retryAfter ? parseInt(retryAfter, 10) * 1e3 : NaN;
3901
+ const waitMs = Number.isFinite(parsedRetryAfter) ? parsedRetryAfter : delay2;
3902
+ console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
3903
+ await waitForRetry(waitMs, options.signal);
3904
+ return fetchWithRetry(url, apiKey, body, options, attempt + 1, nativeGemini);
3905
+ }
3906
+ const errorText2 = await raceWithAbort(
3907
+ response.text().catch(() => "unknown error"),
3908
+ signal
3909
+ );
3910
+ throw new EmbeddingAPIError(response.status, errorText2);
3911
+ } catch (err) {
3912
+ if (err instanceof Error && (err.name === "AbortError" || controller.signal.aborted || options.signal?.aborted)) {
3913
+ throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
3914
+ }
3915
+ throw err;
3916
+ } finally {
3917
+ clearTimeout(timeout);
3875
3918
  }
3876
- if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
3877
- const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
3878
- const retryAfter = response.headers.get("retry-after");
3879
- const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : delay2;
3880
- console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
3919
+ }
3920
+ function abortError() {
3921
+ const error = new Error("Embedding API request aborted");
3922
+ error.name = "AbortError";
3923
+ return error;
3924
+ }
3925
+ function raceWithAbort(promise, signal) {
3926
+ if (signal.aborted) return Promise.reject(abortError());
3927
+ return new Promise((resolve, reject) => {
3928
+ const onAbort = () => {
3929
+ signal.removeEventListener("abort", onAbort);
3930
+ reject(abortError());
3931
+ };
3932
+ signal.addEventListener("abort", onAbort, { once: true });
3933
+ promise.then(
3934
+ (value) => {
3935
+ signal.removeEventListener("abort", onAbort);
3936
+ resolve(value);
3937
+ },
3938
+ (error) => {
3939
+ signal.removeEventListener("abort", onAbort);
3940
+ reject(error);
3941
+ }
3942
+ );
3943
+ });
3944
+ }
3945
+ async function waitForRetry(waitMs, signal) {
3946
+ if (!signal) {
3881
3947
  await new Promise((resolve) => setTimeout(resolve, waitMs));
3882
- return fetchWithRetry(url, apiKey, body, options, attempt + 1, nativeGemini);
3948
+ return;
3883
3949
  }
3884
- const errorText2 = await response.text().catch(() => "unknown error");
3885
- throw new Error(`Embedding API error (${response.status}): ${errorText2}`);
3950
+ const abortSignal = signal;
3951
+ if (abortSignal.aborted) throw new Error("Embedding API retry aborted");
3952
+ await new Promise((resolve, reject) => {
3953
+ const timer = setTimeout(done, waitMs);
3954
+ const onAbort = () => {
3955
+ clearTimeout(timer);
3956
+ abortSignal.removeEventListener("abort", onAbort);
3957
+ reject(new Error("Embedding API retry aborted"));
3958
+ };
3959
+ function done() {
3960
+ abortSignal.removeEventListener("abort", onAbort);
3961
+ resolve();
3962
+ }
3963
+ abortSignal.addEventListener("abort", onAbort, { once: true });
3964
+ });
3886
3965
  }
3887
- 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;
3966
+ 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;
3888
3967
  var init_api_provider = __esm({
3889
3968
  "src/embedding/api-provider.ts"() {
3890
3969
  "use strict";
@@ -3903,6 +3982,14 @@ var init_api_provider = __esm({
3903
3982
  DASHSCOPE_MAX_BATCH_SIZE = 10;
3904
3983
  MAX_RETRIES2 = 3;
3905
3984
  BASE_DELAY_MS = 500;
3985
+ EmbeddingAPIError = class extends Error {
3986
+ status;
3987
+ constructor(status, detail) {
3988
+ super(`Embedding API error (${status}): ${detail}`);
3989
+ this.name = "EmbeddingAPIError";
3990
+ this.status = status;
3991
+ }
3992
+ };
3906
3993
  APIEmbeddingProvider = class _APIEmbeddingProvider {
3907
3994
  name;
3908
3995
  dimensions;
@@ -4114,15 +4201,14 @@ var init_api_provider = __esm({
4114
4201
  }
4115
4202
  } catch (error) {
4116
4203
  const providerLimit = parseBatchLimit(error);
4117
- const fallbackSize = providerLimit ?? Math.ceil(chunkTexts.length / 2);
4118
- if (chunkTexts.length > 1 && fallbackSize < chunkTexts.length) {
4204
+ if (providerLimit !== null && chunkTexts.length > 1 && providerLimit < chunkTexts.length) {
4119
4205
  console.error(
4120
- `[memorix] Embedding batch too large for provider, retrying in chunks of ${fallbackSize}`
4206
+ `[memorix] Embedding batch too large for provider, retrying in chunks of ${providerLimit}`
4121
4207
  );
4122
- for (let start = 0; start < chunkTexts.length; start += fallbackSize) {
4208
+ for (let start = 0; start < chunkTexts.length; start += providerLimit) {
4123
4209
  await processChunk(
4124
- chunkTexts.slice(start, start + fallbackSize),
4125
- chunkIndices.slice(start, start + fallbackSize)
4210
+ chunkTexts.slice(start, start + providerLimit),
4211
+ chunkIndices.slice(start, start + providerLimit)
4126
4212
  );
4127
4213
  }
4128
4214
  return;
@@ -4946,6 +5032,57 @@ function withTimeout(promise, ms, label) {
4946
5032
  );
4947
5033
  });
4948
5034
  }
5035
+ function withTimeoutSignal(operation, ms, label, parentSignal) {
5036
+ const controller = new AbortController();
5037
+ const signal = parentSignal ? AbortSignal.any([parentSignal, controller.signal]) : controller.signal;
5038
+ return new Promise((resolve, reject) => {
5039
+ let settled = false;
5040
+ let timedOut = false;
5041
+ let timer;
5042
+ const cleanup = () => {
5043
+ if (timer !== void 0) clearTimeout(timer);
5044
+ parentSignal?.removeEventListener("abort", onParentAbort);
5045
+ };
5046
+ const onParentAbort = () => {
5047
+ if (settled || timedOut) return;
5048
+ settled = true;
5049
+ cleanup();
5050
+ reject(parentSignal?.reason ?? new Error(`${label} aborted`));
5051
+ };
5052
+ parentSignal?.addEventListener("abort", onParentAbort, { once: true });
5053
+ timer = setTimeout(() => {
5054
+ if (settled) return;
5055
+ timedOut = true;
5056
+ controller.abort(new Error(`${label} timed out after ${ms}ms`));
5057
+ settled = true;
5058
+ cleanup();
5059
+ reject(new Error(`${label} timed out after ${ms}ms`));
5060
+ }, ms);
5061
+ let pending;
5062
+ try {
5063
+ pending = operation(signal);
5064
+ } catch (error) {
5065
+ settled = true;
5066
+ cleanup();
5067
+ reject(error);
5068
+ return;
5069
+ }
5070
+ pending.then(
5071
+ (value) => {
5072
+ if (settled) return;
5073
+ settled = true;
5074
+ cleanup();
5075
+ resolve(value);
5076
+ },
5077
+ (error) => {
5078
+ if (settled) return;
5079
+ settled = true;
5080
+ cleanup();
5081
+ reject(timedOut ? new Error(`${label} timed out after ${ms}ms`) : error);
5082
+ }
5083
+ );
5084
+ });
5085
+ }
4949
5086
  var init_timeout = __esm({
4950
5087
  "src/timeout.ts"() {
4951
5088
  "use strict";
@@ -5810,16 +5947,18 @@ async function searchObservations(options) {
5810
5947
  );
5811
5948
  } else {
5812
5949
  const EMBEDDING_TIMEOUT_MS = 15e3;
5813
- queryVector = await withTimeout(
5814
- provider2.embedInput ? provider2.embedInput(
5950
+ queryVector = await withTimeoutSignal(
5951
+ (signal) => provider2.embedInput ? provider2.embedInput(
5815
5952
  { modality: "text", text: expandedEmbeddingQuery },
5816
- { intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false }
5953
+ { intent: "query", timeoutMs: EMBEDDING_TIMEOUT_MS, retry: false, signal }
5817
5954
  ) : provider2.embed(expandedEmbeddingQuery, {
5818
5955
  timeoutMs: EMBEDDING_TIMEOUT_MS,
5819
- retry: false
5956
+ retry: false,
5957
+ signal
5820
5958
  }),
5821
5959
  EMBEDDING_TIMEOUT_MS,
5822
- "Embedding"
5960
+ "Embedding",
5961
+ options.signal
5823
5962
  );
5824
5963
  mark("embedding");
5825
5964
  const cjkRatio = (originalQuery.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length / originalQuery.length;
@@ -143282,7 +143421,13 @@ async function maintainLongTermMemories(input) {
143282
143421
  }
143283
143422
  for (const group of groups.values()) {
143284
143423
  if (group.length < 2) continue;
143285
- group.sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt));
143424
+ group.sort((a, b) => {
143425
+ const byCreatedAt = Date.parse(b.createdAt) - Date.parse(a.createdAt);
143426
+ if (byCreatedAt !== 0) return byCreatedAt;
143427
+ const byUpdatedAt = Date.parse(b.updatedAt) - Date.parse(a.updatedAt);
143428
+ if (byUpdatedAt !== 0) return byUpdatedAt;
143429
+ return b.id.localeCompare(a.id);
143430
+ });
143286
143431
  const newest = group[0];
143287
143432
  for (const older of group.slice(1)) {
143288
143433
  try {