@anuma/sdk 3.11.1 → 3.11.3

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.
@@ -5164,7 +5164,20 @@ type RecallDegradation =
5164
5164
  "rerank-unavailable"
5165
5165
  /** `budget: 'high'` requested but no `decomposeOptions`, so query
5166
5166
  * decomposition was skipped and the budget downgraded to mid. */
5167
- | "decompose-unavailable";
5167
+ | "decompose-unavailable"
5168
+ /** There was no usable cosine lane, so results were ranked on BM25 (lexical)
5169
+ * alone: either the query embedding failed (or came back empty), or no
5170
+ * candidate had a vector to score against it because the row (re)embed failed.
5171
+ * Also set when the chunk lane — cosine-only, with no lexical equivalent — was
5172
+ * skipped for the same reason. The embeddings provider is a single upstream
5173
+ * with no fallback, so this is a whole-provider outage rather than a per-item
5174
+ * miss — expect a quality drop, not a total loss.
5175
+ *
5176
+ * NOT set for a partial failure that leaves cosine running (a sub-query embed
5177
+ * failing back to the single-query path, or a row batch failing while other
5178
+ * rows keep usable vectors) — those are logged, not reported, so this stays a
5179
+ * reliable outage signal rather than a general embedding-error counter. */
5180
+ | "embeddings-unavailable";
5168
5181
  /**
5169
5182
  * Per-call recall observability payload (see {@link RecallOptions.onDiagnostics}).
5170
5183
  * All timings are wall-clock milliseconds. Lane counts are post-dedupe,
@@ -5164,7 +5164,20 @@ type RecallDegradation =
5164
5164
  "rerank-unavailable"
5165
5165
  /** `budget: 'high'` requested but no `decomposeOptions`, so query
5166
5166
  * decomposition was skipped and the budget downgraded to mid. */
5167
- | "decompose-unavailable";
5167
+ | "decompose-unavailable"
5168
+ /** There was no usable cosine lane, so results were ranked on BM25 (lexical)
5169
+ * alone: either the query embedding failed (or came back empty), or no
5170
+ * candidate had a vector to score against it because the row (re)embed failed.
5171
+ * Also set when the chunk lane — cosine-only, with no lexical equivalent — was
5172
+ * skipped for the same reason. The embeddings provider is a single upstream
5173
+ * with no fallback, so this is a whole-provider outage rather than a per-item
5174
+ * miss — expect a quality drop, not a total loss.
5175
+ *
5176
+ * NOT set for a partial failure that leaves cosine running (a sub-query embed
5177
+ * failing back to the single-query path, or a row batch failing while other
5178
+ * rows keep usable vectors) — those are logged, not reported, so this stays a
5179
+ * reliable outage signal rather than a general embedding-error counter. */
5180
+ | "embeddings-unavailable";
5168
5181
  /**
5169
5182
  * Per-call recall observability payload (see {@link RecallOptions.onDiagnostics}).
5170
5183
  * All timings are wall-clock milliseconds. Lane counts are post-dedupe,
@@ -4,11 +4,14 @@ import {
4
4
  DEFAULT_CHUNK_SIZE,
5
5
  DEFAULT_MIN_CHUNK_SIZE,
6
6
  DEFAULT_VAULT_CACHE_SIZE,
7
+ EMBEDDINGS_DEGRADED_EMPTY,
7
8
  ENTITY_FANOUT,
8
9
  ENTITY_KINDS,
9
10
  LRUCache,
10
11
  MAX_HOPS,
11
12
  NODE_BUDGET,
13
+ RECALL_MAX_LIMIT,
14
+ RECALL_TOOL_NAME,
12
15
  RerankerUnavailableError,
13
16
  TOPICS_EXTRACTION_VERSION,
14
17
  TREND_RECENT_WINDOW_DAYS,
@@ -128,7 +131,7 @@ import {
128
131
  updateVaultMemoryOp,
129
132
  upsertMessageOp,
130
133
  vaultMemoryToStored
131
- } from "./chunk-NCKDSZPQ.mjs";
134
+ } from "./chunk-FZZCEZL2.mjs";
132
135
  import {
133
136
  createStreamingDeAnonymizer,
134
137
  resolvePiiRedactor
@@ -5334,6 +5337,11 @@ async function retain(content, ctx, options = {}) {
5334
5337
  ...options.folderId !== void 0 && { folderId: options.folderId }
5335
5338
  }
5336
5339
  );
5340
+ if (prepared.embeddingFailure || prepared.embeddingsUnavailable) {
5341
+ throw new Error(
5342
+ "retain: embeddings unavailable \u2014 refusing to auto-merge against an inert cosine lane, which would create a duplicate instead of merging into the existing memory. Retry when embeddings recover, or pass enableAutoMerge: false to force a create."
5343
+ );
5344
+ }
5337
5345
  if (options.consolidateOptions) {
5338
5346
  const outcome = await tryConsolidate(trimmed, ctx, options, prepared);
5339
5347
  if (outcome) {
@@ -6566,10 +6574,6 @@ function rankProfileCandidates(inputs, now = Date.now(), options = {}) {
6566
6574
  return ranked;
6567
6575
  }
6568
6576
 
6569
- // src/lib/memory/recallConstants.ts
6570
- var RECALL_TOOL_NAME = "recall_memory";
6571
- var RECALL_MAX_LIMIT = 50;
6572
-
6573
6577
  // src/lib/memory/recallTool.ts
6574
6578
  var DEFAULT_LIMIT = 8;
6575
6579
  var DEFAULT_BUDGET = "low";
@@ -6720,7 +6724,11 @@ function createRecallTool(ctx, toolOptions, callbacks) {
6720
6724
  conversationMemories += effectiveLimit;
6721
6725
  let surfaced = 0;
6722
6726
  try {
6727
+ let recallDegraded = [];
6723
6728
  const recallOpts = {
6729
+ onDiagnostics: (d) => {
6730
+ recallDegraded = d.degraded;
6731
+ },
6724
6732
  types: defaultTypes,
6725
6733
  limit: effectiveLimit,
6726
6734
  budget: defaultBudget,
@@ -6752,6 +6760,9 @@ function createRecallTool(ctx, toolOptions, callbacks) {
6752
6760
  }
6753
6761
  }
6754
6762
  surfaced = result.memories.length;
6763
+ if (result.memories.length === 0 && recallDegraded.includes("embeddings-unavailable")) {
6764
+ return EMBEDDINGS_DEGRADED_EMPTY;
6765
+ }
6755
6766
  const formatted = formatRecallResult(result.memories);
6756
6767
  return truncatedByBudget && surfaced > 0 ? `${formatted}
6757
6768
 
@@ -10175,6 +10186,9 @@ var CompletionsStrategy = class {
10175
10186
  }
10176
10187
  }
10177
10188
  }
10189
+ if (choice.finish_reason) {
10190
+ accumulator.finishReason = choice.finish_reason;
10191
+ }
10178
10192
  if (choice.finish_reason === "tool_calls" || choice.finish_reason === "stop") {
10179
10193
  if (accumulator.toolCalls.size > 0) {
10180
10194
  accumulator.content = mergeXaiInlineParameterTags(
@@ -10257,7 +10271,9 @@ var CompletionsStrategy = class {
10257
10271
  content: finalContent,
10258
10272
  ...toolCalls && { tool_calls: toolCalls }
10259
10273
  },
10260
- finish_reason: toolCalls ? "tool_calls" : "stop"
10274
+ // Prefer what the provider actually said; fall back to the derived
10275
+ // value only when the stream carried no finish_reason at all.
10276
+ finish_reason: accumulator.finishReason ?? (toolCalls ? "tool_calls" : "stop")
10261
10277
  }
10262
10278
  ],
10263
10279
  ...usage && { usage },
@@ -10928,7 +10944,7 @@ var ResponsesStrategy = class {
10928
10944
  }
10929
10945
  return result;
10930
10946
  }
10931
- if (typedChunk.type === "response.completed") {
10947
+ if (typedChunk.type === "response.completed" || typedChunk.type === "response.incomplete") {
10932
10948
  if (typedChunk.response?.usage) {
10933
10949
  const u = typedChunk.response.usage;
10934
10950
  const promptTokens = u.input_tokens ?? u.prompt_tokens ?? 0;
@@ -10944,6 +10960,11 @@ var ResponsesStrategy = class {
10944
10960
  ...creditsExhausted !== void 0 && { credits_exhausted: creditsExhausted }
10945
10961
  };
10946
10962
  }
10963
+ const resp = typedChunk.response;
10964
+ const looksIncomplete = typedChunk.type === "response.incomplete" || resp?.status === "incomplete";
10965
+ if (looksIncomplete && resp?.incomplete_details?.reason === "max_output_tokens") {
10966
+ accumulator.finishReason = "length";
10967
+ }
10947
10968
  if (typedChunk.response?.tools_checksum && !accumulator.toolsChecksum) {
10948
10969
  accumulator.toolsChecksum = typedChunk.response.tools_checksum;
10949
10970
  }
@@ -12480,7 +12501,17 @@ Executing tool: ${toolInfo}
12480
12501
  flushDeAnon();
12481
12502
  currentAccumulator.content += tip;
12482
12503
  }
12504
+ const truncatedToNothing = (acc) => acc.finishReason === "length" && acc.toolCalls.size === 0 && acc.content.trim() === "";
12505
+ const TRUNCATION_ERROR = "Model response was truncated at the output-token limit before it produced any usable content or tool call. Retry with a higher `maxOutputTokens`, or prompt for fewer/smaller tool calls per turn.";
12483
12506
  if (toolIteration > 0) {
12507
+ if (truncatedToNothing(currentAccumulator)) {
12508
+ await fireRunError({ error: TRUNCATION_ERROR, stage: "model" });
12509
+ return {
12510
+ data: buildResponseFinal(currentAccumulator),
12511
+ error: TRUNCATION_ERROR,
12512
+ toolsChecksum: currentAccumulator.toolsChecksum
12513
+ };
12514
+ }
12484
12515
  const finalResponse = buildResponseFinal(currentAccumulator);
12485
12516
  if (onFinish) onFinish(finalResponse);
12486
12517
  await fireRunEnd({
@@ -12494,6 +12525,14 @@ Executing tool: ${toolInfo}
12494
12525
  autoExecutedToolResults: accumulatedToolResults
12495
12526
  };
12496
12527
  }
12528
+ if (truncatedToNothing(accumulator)) {
12529
+ await fireRunError({ error: TRUNCATION_ERROR, stage: "model" });
12530
+ return {
12531
+ data: response,
12532
+ error: TRUNCATION_ERROR,
12533
+ toolsChecksum: accumulator.toolsChecksum
12534
+ };
12535
+ }
12497
12536
  if (onFinish) onFinish(response);
12498
12537
  await fireRunEnd({
12499
12538
  finalContent: finalContentText(accumulator),
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-NCKDSZPQ.mjs";
3
+ } from "./chunk-FZZCEZL2.mjs";
4
4
  import "./chunk-OBDRKQQI.mjs";
5
5
  import "./chunk-3RPGXXPZ.mjs";
6
6
  export {
@@ -5655,6 +5655,20 @@ function createAppGenerationTools({
5655
5655
  if (!Array.isArray(filesArg) || filesArg.length === 0) {
5656
5656
  return { error: "files array is required and must not be empty" };
5657
5657
  }
5658
+ const malformed = filesArg.map((f, i) => {
5659
+ if (!f || typeof f !== "object") return `files[${i}] is not an object`;
5660
+ if (typeof f.path !== "string" || f.path.trim() === "")
5661
+ return `files[${i}] is missing a non-empty "path"`;
5662
+ if (typeof f.content !== "string")
5663
+ return `files[${i}] ("${f.path}") is missing a string "content"`;
5664
+ return null;
5665
+ }).filter((m) => m !== null);
5666
+ if (malformed.length > 0) {
5667
+ return {
5668
+ error: `Malformed files array \u2014 nothing was written. Every item needs both "path" and "content" as strings: ${malformed.join("; ")}`,
5669
+ malformed
5670
+ };
5671
+ }
5658
5672
  const normalizedPaths = filesArg.map((f) => normalizePath(f.path));
5659
5673
  const existsInStorage = await Promise.all(
5660
5674
  normalizedPaths.map((p) => storage.getFile(conversationId, p))
@@ -5506,6 +5506,20 @@ function createAppGenerationTools({
5506
5506
  if (!Array.isArray(filesArg) || filesArg.length === 0) {
5507
5507
  return { error: "files array is required and must not be empty" };
5508
5508
  }
5509
+ const malformed = filesArg.map((f, i) => {
5510
+ if (!f || typeof f !== "object") return `files[${i}] is not an object`;
5511
+ if (typeof f.path !== "string" || f.path.trim() === "")
5512
+ return `files[${i}] is missing a non-empty "path"`;
5513
+ if (typeof f.content !== "string")
5514
+ return `files[${i}] ("${f.path}") is missing a string "content"`;
5515
+ return null;
5516
+ }).filter((m) => m !== null);
5517
+ if (malformed.length > 0) {
5518
+ return {
5519
+ error: `Malformed files array \u2014 nothing was written. Every item needs both "path" and "content" as strings: ${malformed.join("; ")}`,
5520
+ malformed
5521
+ };
5522
+ }
5509
5523
  const normalizedPaths = filesArg.map((f) => normalizePath(f.path));
5510
5524
  const existsInStorage = await Promise.all(
5511
5525
  normalizedPaths.map((p) => storage.getFile(conversationId, p))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "3.11.1",
3
+ "version": "3.11.3",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",