@anuma/sdk 6.3.0 → 6.4.0

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.
@@ -5350,6 +5350,7 @@ async function cleanupConversationSummary(database, conversationId) {
5350
5350
  var HEADER = "[Tool Execution Results]\n\n";
5351
5351
  var FOOTER = "\n\nBased on these results, continue with the task.";
5352
5352
  var ENTRY_SEPARATOR = "\n\n";
5353
+ var TOOL_RESULT_FOOTER_LINE = FOOTER.trim();
5353
5354
  var MAX_PERSISTED_TOOL_RESULT_CHARS = 1e5;
5354
5355
  function buildToolResultContent(results) {
5355
5356
  const entries = results.map((r) => `Tool "${r.name}" returned: ${JSON.stringify(r.result)}`);
@@ -5359,6 +5360,9 @@ function buildToolResultContent(results) {
5359
5360
  );
5360
5361
  return `${HEADER}${summary}${FOOTER}`;
5361
5362
  }
5363
+ function capToolResultEntries(entries, budget) {
5364
+ return capEntries(entries, budget);
5365
+ }
5362
5366
  function capEntries(entries, budget) {
5363
5367
  const smallestFirst = entries.map((_, index) => index).sort((a, b) => entries[a].length - entries[b].length);
5364
5368
  const capped = [...entries];
@@ -5383,6 +5387,112 @@ function truncationMarker(omitted) {
5383
5387
  ... (tool output truncated, ${omitted} characters omitted)`;
5384
5388
  }
5385
5389
 
5390
+ // src/lib/chat/toolResults.ts
5391
+ var TOOL_RESULTS_PREFIX = "[Tool Execution Results]";
5392
+ var DISPLAY_CARD_PLACEHOLDER = "[displayed a card to the user with the tool result]";
5393
+ var TOOL_LINE = /^Tool "([^"]+)" returned: (.*)$/;
5394
+ var TOOL_RESULT_ORIGIN = "tool_result";
5395
+ function isToolResultsRow(row) {
5396
+ if (row.origin === TOOL_RESULT_ORIGIN) return true;
5397
+ if (typeof row.origin === "string") return false;
5398
+ if (row.role !== "user" || !row.content.startsWith(TOOL_RESULTS_PREFIX)) return false;
5399
+ return parseToolResultSegments(row.content).length > 0;
5400
+ }
5401
+ function parseToolResultSegments(content) {
5402
+ const segments = [];
5403
+ for (const raw of content.split("\n")) {
5404
+ const line = raw.trim();
5405
+ const match = TOOL_LINE.exec(line);
5406
+ if (match) {
5407
+ segments.push({ name: match[1], line });
5408
+ continue;
5409
+ }
5410
+ const current = segments[segments.length - 1];
5411
+ if (!current || line === "" || line === TOOL_RESULT_FOOTER_LINE) continue;
5412
+ if (line === TOOL_RESULTS_PREFIX) continue;
5413
+ current.line = `${current.line}
5414
+ ${line}`;
5415
+ }
5416
+ return segments;
5417
+ }
5418
+ function prepareToolResultsForReplay(rows, options) {
5419
+ if (options.fold) {
5420
+ return foldToolResultsRows(rows, {
5421
+ exclude: options.exclude,
5422
+ placeholder: options.placeholder
5423
+ });
5424
+ }
5425
+ return rows.filter((row) => !isToolResultsRow(row));
5426
+ }
5427
+ function foldToolResultsRows(rows, options) {
5428
+ const exclude = new Set(options?.exclude ?? []);
5429
+ const out = [];
5430
+ const pending = [];
5431
+ const assistantIndexById = /* @__PURE__ */ new Map();
5432
+ for (const row of rows) {
5433
+ if (isToolResultsRow(row)) {
5434
+ pending.push({ row, insertionPoint: out.length });
5435
+ continue;
5436
+ }
5437
+ out.push(row);
5438
+ if (row.role === "assistant" && row.uniqueId) {
5439
+ assistantIndexById.set(row.uniqueId, out.length - 1);
5440
+ }
5441
+ }
5442
+ const byTarget = /* @__PURE__ */ new Map();
5443
+ for (const { row, insertionPoint } of pending) {
5444
+ const target = resolveFoldTarget(out, row, insertionPoint, assistantIndexById);
5445
+ if (target === void 0) continue;
5446
+ const group = byTarget.get(target);
5447
+ if (group) group.push(row);
5448
+ else byTarget.set(target, [row]);
5449
+ }
5450
+ for (const [target, groupedRows] of byTarget) {
5451
+ const assistant = out[target];
5452
+ const kept = groupedRows.flatMap((row) => parseToolResultSegments(row.content)).filter((segment) => !exclude.has(segment.name));
5453
+ if (kept.length > 0) {
5454
+ out[target] = { ...assistant, content: appendToolResults(assistant.content, kept) };
5455
+ } else if (!assistant.content.trim() && options?.placeholder) {
5456
+ out[target] = { ...assistant, content: options.placeholder };
5457
+ }
5458
+ }
5459
+ return out;
5460
+ }
5461
+ function resolveFoldTarget(out, row, insertionPoint, assistantIndexById) {
5462
+ const linked = row.parentMessageId ? assistantIndexById.get(row.parentMessageId) : void 0;
5463
+ if (linked !== void 0) return linked;
5464
+ let previous;
5465
+ for (let i = insertionPoint - 1; i >= 0; i--) {
5466
+ if (out[i].role === "assistant") {
5467
+ previous = i;
5468
+ break;
5469
+ }
5470
+ }
5471
+ let next;
5472
+ for (let i = insertionPoint; i < out.length; i++) {
5473
+ if (out[i].role === "assistant") {
5474
+ next = i;
5475
+ break;
5476
+ }
5477
+ }
5478
+ if (previous === void 0) return next;
5479
+ if (next === void 0) return previous;
5480
+ return next - insertionPoint < insertionPoint - previous ? next : previous;
5481
+ }
5482
+ var MAX_FOLDED_APPENDIX_CHARS = 2e4;
5483
+ function appendToolResults(assistantContent, kept) {
5484
+ const framing = TOOL_RESULTS_PREFIX.length + 1 + (kept.length - 1);
5485
+ const capped = capToolResultEntries(
5486
+ kept.map((segment) => segment.line),
5487
+ MAX_FOLDED_APPENDIX_CHARS - framing
5488
+ );
5489
+ const appendix = `${TOOL_RESULTS_PREFIX}
5490
+ ${capped.join("\n")}`;
5491
+ return assistantContent.trim() ? `${assistantContent}
5492
+
5493
+ ${appendix}` : appendix;
5494
+ }
5495
+
5386
5496
  // src/lib/db/chat/lazyDecrypt.ts
5387
5497
  var LRU_CAPACITY = 256;
5388
5498
  var titleCache = /* @__PURE__ */ new Map();
@@ -7915,6 +8025,9 @@ async function extractFacts(messages, options) {
7915
8025
  ...options.apiKey !== void 0 && { apiKey: options.apiKey },
7916
8026
  ...options.getToken !== void 0 && { getToken: options.getToken },
7917
8027
  ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
8028
+ ...options.endpointOverride !== void 0 && {
8029
+ endpointOverride: options.endpointOverride
8030
+ },
7918
8031
  model: options.model ?? DEFAULT_EXTRACTION_MODEL,
7919
8032
  systemPrompt: SYSTEM_PROMPT2,
7920
8033
  userMessage: `Today's date is ${today}. Resolve any relative dates ("yesterday", "next week") against it.
@@ -12609,11 +12722,17 @@ function useChatStorage(options) {
12609
12722
  preProcessors,
12610
12723
  piiRedaction,
12611
12724
  onPiiRedacted,
12612
- nerDetector
12725
+ nerDetector,
12726
+ toolResultsHistoryExclude,
12727
+ foldToolResultsInHistory
12613
12728
  } = options;
12614
12729
  const [currentConversationId, setCurrentConversationId] = useState4(
12615
12730
  initialConversationId || null
12616
12731
  );
12732
+ const toolResultsHistoryExcludeRef = useRef4(toolResultsHistoryExclude);
12733
+ toolResultsHistoryExcludeRef.current = toolResultsHistoryExclude;
12734
+ const foldToolResultsInHistoryRef = useRef4(foldToolResultsInHistory);
12735
+ foldToolResultsInHistoryRef.current = foldToolResultsInHistory;
12617
12736
  const nerDetectorRef = useRef4(nerDetector);
12618
12737
  nerDetectorRef.current = nerDetector;
12619
12738
  const resolvedPiiRedaction = useMemo3(
@@ -13621,7 +13740,12 @@ function useChatStorage(options) {
13621
13740
  }
13622
13741
  if (includeHistory) {
13623
13742
  const validMessages = storedMessages.filter((msg) => !msg.error);
13624
- const limitedMessages = validMessages.slice(-maxHistoryMessages);
13743
+ const replayableMessages = prepareToolResultsForReplay(validMessages, {
13744
+ fold: foldToolResultsInHistoryRef.current === true,
13745
+ exclude: toolResultsHistoryExcludeRef.current,
13746
+ placeholder: DISPLAY_CARD_PLACEHOLDER
13747
+ });
13748
+ const limitedMessages = replayableMessages.slice(-maxHistoryMessages);
13625
13749
  if (!fileContextForRequest) {
13626
13750
  for (let i = limitedMessages.length - 1; i >= 0; i--) {
13627
13751
  const msg = limitedMessages[i];
@@ -13631,6 +13755,7 @@ function useChatStorage(options) {
13631
13755
  }
13632
13756
  }
13633
13757
  }
13758
+ const foldedHistory = limitedMessages;
13634
13759
  let encryptionKey;
13635
13760
  if (walletAddress && hasEncryptionKey(walletAddress) && isOPFSSupported()) {
13636
13761
  try {
@@ -13647,7 +13772,7 @@ function useChatStorage(options) {
13647
13772
  const { messagesToConvert, summarySystemMessage } = await maybeSummarizeHistory({
13648
13773
  database,
13649
13774
  conversationId: convId,
13650
- messages: limitedMessages,
13775
+ messages: foldedHistory,
13651
13776
  summarizeHistory,
13652
13777
  summaryTokenThreshold,
13653
13778
  summaryMinWindowMessages,
@@ -14105,37 +14230,25 @@ function useChatStorage(options) {
14105
14230
  }
14106
14231
  }
14107
14232
  const autoToolResults = result.autoExecutedToolResults;
14233
+ let storedToolResultsMessage;
14108
14234
  if (autoToolResults && autoToolResults.length > 0) {
14109
- const toolResultContent = buildToolResultContent(autoToolResults);
14235
+ const toolResultsOpts = {
14236
+ conversationId: convId,
14237
+ role: "user",
14238
+ content: buildToolResultContent(autoToolResults),
14239
+ model: "",
14240
+ parentMessageId: storedAssistantMessage.uniqueId,
14241
+ origin: TOOL_RESULT_ORIGIN
14242
+ };
14110
14243
  try {
14111
- await writeOrQueue(
14244
+ const toolResultsWrite = await writeOrQueue(
14112
14245
  "createMessage",
14113
- {
14114
- conversationId: convId,
14115
- role: "user",
14116
- content: toolResultContent,
14117
- model: "",
14118
- parentMessageId: storedAssistantMessage.uniqueId,
14119
- origin: "tool_result"
14120
- },
14121
- () => createMessageOp(storageCtx, {
14122
- conversationId: convId,
14123
- role: "user",
14124
- content: toolResultContent,
14125
- model: "",
14126
- parentMessageId: storedAssistantMessage.uniqueId,
14127
- origin: "tool_result"
14128
- }),
14129
- () => makeSyntheticStoredMessage({
14130
- conversationId: convId,
14131
- role: "user",
14132
- content: toolResultContent,
14133
- model: "",
14134
- parentMessageId: storedAssistantMessage.uniqueId,
14135
- origin: "tool_result"
14136
- }),
14246
+ toolResultsOpts,
14247
+ () => createMessageOp(storageCtx, toolResultsOpts),
14248
+ () => makeSyntheticStoredMessage(toolResultsOpts),
14137
14249
  assistantMsgQueueId ? [assistantMsgQueueId] : []
14138
14250
  );
14251
+ storedToolResultsMessage = toolResultsWrite.result;
14139
14252
  } catch {
14140
14253
  }
14141
14254
  }
@@ -14158,7 +14271,8 @@ function useChatStorage(options) {
14158
14271
  error: null,
14159
14272
  userMessage: storedUserMessage,
14160
14273
  assistantMessage: storedAssistantMessage,
14161
- autoExecutedToolResults: result.autoExecutedToolResults
14274
+ autoExecutedToolResults: autoToolResults,
14275
+ toolResultsMessage: storedToolResultsMessage
14162
14276
  };
14163
14277
  },
14164
14278
  // eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally omitting stable refs and config values that don't change identity
@@ -29293,6 +29293,9 @@ async function extractFacts(messages, options) {
29293
29293
  ...options.apiKey !== void 0 && { apiKey: options.apiKey },
29294
29294
  ...options.getToken !== void 0 && { getToken: options.getToken },
29295
29295
  ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
29296
+ ...options.endpointOverride !== void 0 && {
29297
+ endpointOverride: options.endpointOverride
29298
+ },
29296
29299
  model: options.model ?? DEFAULT_EXTRACTION_MODEL,
29297
29300
  systemPrompt: SYSTEM_PROMPT4,
29298
29301
  userMessage: `Today's date is ${today}. Resolve any relative dates ("yesterday", "next week") against it.
@@ -5777,6 +5777,31 @@ interface QuarantinedMemoryInfo {
5777
5777
  interface ExtractFactsOptions extends PortalLlmAuth {
5778
5778
  baseUrl?: string;
5779
5779
  model?: string;
5780
+ /**
5781
+ * Optional per-call request path override, forwarded to
5782
+ * {@link callPortalJsonCompletion}. When set, the extraction call POSTs to
5783
+ * `baseUrl + endpointOverride` instead of the default
5784
+ * `/api/v1/chat/completions` — path only, body unchanged. Lets callers route
5785
+ * this internal-utility pass to a dedicated endpoint. Invalid values throw at
5786
+ * call time (see {@link validateEndpointOverride}).
5787
+ *
5788
+ * Why this exists (anuma-ai/ai-memoryless-client#5536): auto-extraction is the
5789
+ * highest-volume first-party background call in the product — one per
5790
+ * extracting turn, on every platform — and it carries no flow fingerprint, so
5791
+ * the portal's freeloader detector classifies it as scripted abuse. In reject
5792
+ * mode that 403s every basic-tier extraction, which surfaces here as
5793
+ * `onExhaustedEmpty` → `empty-after-retry` and leaves free-tier vaults empty.
5794
+ * {@link TopicExtractOptions.endpointOverride} already exists for the same
5795
+ * reason on the topic pass; this is the fact pass catching up.
5796
+ *
5797
+ * IMPORTANT — the utility endpoint clamps to a PRICE CEILING and never
5798
+ * rejects, so pointing this at `/api/v1/utility/chat/completions` while the
5799
+ * portal's ceiling prices below {@link DEFAULT_EXTRACTION_MODEL} silently
5800
+ * rewrites the model instead of 403-ing. That trades a visible failure for an
5801
+ * invisible quality regression: raise `PORTAL_UTILITY_CEILING_MODEL` to at
5802
+ * least the extraction model's rate BEFORE setting this in a client.
5803
+ */
5804
+ endpointOverride?: string;
5780
5805
  /** Override the global fetch implementation (useful for tests). */
5781
5806
  fetchFn?: typeof fetch;
5782
5807
  /**
@@ -5777,6 +5777,31 @@ interface QuarantinedMemoryInfo {
5777
5777
  interface ExtractFactsOptions extends PortalLlmAuth {
5778
5778
  baseUrl?: string;
5779
5779
  model?: string;
5780
+ /**
5781
+ * Optional per-call request path override, forwarded to
5782
+ * {@link callPortalJsonCompletion}. When set, the extraction call POSTs to
5783
+ * `baseUrl + endpointOverride` instead of the default
5784
+ * `/api/v1/chat/completions` — path only, body unchanged. Lets callers route
5785
+ * this internal-utility pass to a dedicated endpoint. Invalid values throw at
5786
+ * call time (see {@link validateEndpointOverride}).
5787
+ *
5788
+ * Why this exists (anuma-ai/ai-memoryless-client#5536): auto-extraction is the
5789
+ * highest-volume first-party background call in the product — one per
5790
+ * extracting turn, on every platform — and it carries no flow fingerprint, so
5791
+ * the portal's freeloader detector classifies it as scripted abuse. In reject
5792
+ * mode that 403s every basic-tier extraction, which surfaces here as
5793
+ * `onExhaustedEmpty` → `empty-after-retry` and leaves free-tier vaults empty.
5794
+ * {@link TopicExtractOptions.endpointOverride} already exists for the same
5795
+ * reason on the topic pass; this is the fact pass catching up.
5796
+ *
5797
+ * IMPORTANT — the utility endpoint clamps to a PRICE CEILING and never
5798
+ * rejects, so pointing this at `/api/v1/utility/chat/completions` while the
5799
+ * portal's ceiling prices below {@link DEFAULT_EXTRACTION_MODEL} silently
5800
+ * rewrites the model instead of 403-ing. That trades a visible failure for an
5801
+ * invisible quality regression: raise `PORTAL_UTILITY_CEILING_MODEL` to at
5802
+ * least the extraction model's rate BEFORE setting this in a client.
5803
+ */
5804
+ endpointOverride?: string;
5780
5805
  /** Override the global fetch implementation (useful for tests). */
5781
5806
  fetchFn?: typeof fetch;
5782
5807
  /**
@@ -5900,6 +5900,9 @@ async function extractFacts(messages, options) {
5900
5900
  ...options.apiKey !== void 0 && { apiKey: options.apiKey },
5901
5901
  ...options.getToken !== void 0 && { getToken: options.getToken },
5902
5902
  ...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
5903
+ ...options.endpointOverride !== void 0 && {
5904
+ endpointOverride: options.endpointOverride
5905
+ },
5903
5906
  model: options.model ?? DEFAULT_EXTRACTION_MODEL,
5904
5907
  systemPrompt: SYSTEM_PROMPT2,
5905
5908
  userMessage: `Today's date is ${today}. Resolve any relative dates ("yesterday", "next week") against it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "6.3.0",
3
+ "version": "6.4.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",