@anuma/sdk 6.15.0 → 6.15.2

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.
@@ -70,6 +70,10 @@ function supportsResponseFormat(model, variant = "json_object") {
70
70
  const allow = variant === "json_schema" ? RESPONSE_SCHEMA_OK : RESPONSE_FORMAT_OK;
71
71
  return model.split("/").some((seg) => allow.has(seg));
72
72
  }
73
+ var RESPONSES_ONLY_MODEL_PREFIXES = ["gpt-5.6"];
74
+ function requiresResponsesTransport(model) {
75
+ return model.split("/").some((seg) => RESPONSES_ONLY_MODEL_PREFIXES.some((prefix) => seg.startsWith(prefix)));
76
+ }
73
77
  var CHAT_TO_RESPONSES_FIELDS = {
74
78
  max_completion_tokens: "max_output_tokens"
75
79
  };
@@ -424,7 +428,9 @@ export {
424
428
  withInternalFlowMarker,
425
429
  taskTypeHeader,
426
430
  supportsResponseFormat,
431
+ requiresResponsesTransport,
427
432
  resolvePortalAuthHeaders,
428
433
  callPortalJsonCompletion,
429
- extractJsonCandidate
434
+ extractJsonCandidate,
435
+ extractCompletionContent
430
436
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getLogger
3
- } from "./chunk-3V3BBJLH.mjs";
3
+ } from "./chunk-4Z2X4UYQ.mjs";
4
4
 
5
5
  // src/lib/pii/patterns.ts
6
6
  function luhnCheck(digits) {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  callPortalJsonCompletion,
3
3
  getLogger
4
- } from "./chunk-3V3BBJLH.mjs";
4
+ } from "./chunk-4Z2X4UYQ.mjs";
5
5
  import {
6
6
  BASE_URL,
7
7
  createClientConfig
@@ -1680,6 +1680,29 @@ async function getMessageSkeletonsOp(ctx, convId) {
1680
1680
  }
1681
1681
  return skeletons;
1682
1682
  }
1683
+ async function getToolCallEventIdsOp(ctx, convId) {
1684
+ const results = await ctx.messagesCollection.query(Q.where("conversation_id", convId)).unsafeFetchRaw();
1685
+ const ids = /* @__PURE__ */ new Set();
1686
+ for (const raw of results) {
1687
+ const value = raw.tool_call_events;
1688
+ if (!value) continue;
1689
+ let events;
1690
+ if (typeof value === "string") {
1691
+ try {
1692
+ events = JSON.parse(value);
1693
+ } catch {
1694
+ continue;
1695
+ }
1696
+ } else if (Array.isArray(value)) {
1697
+ events = value;
1698
+ }
1699
+ if (!Array.isArray(events)) continue;
1700
+ for (const evt of events) {
1701
+ if (evt && evt.id) ids.add(evt.id);
1702
+ }
1703
+ }
1704
+ return ids;
1705
+ }
1683
1706
  async function clearMessagesOp(ctx, convId) {
1684
1707
  const messages = await ctx.messagesCollection.query(Q.where("conversation_id", convId)).fetch();
1685
1708
  await ctx.database.write(async () => {
@@ -1918,14 +1941,14 @@ function resolveChunkText(content, chunk, chunksInRow) {
1918
1941
  if (covered !== content.length) return content;
1919
1942
  return content.slice(start, end);
1920
1943
  }
1921
- async function readJsonField(model, column, walletAddress) {
1922
- const raw = model._getRaw(column);
1923
- if (!raw) return void 0;
1924
- if (walletAddress && isEncrypted(raw)) {
1925
- return await decryptJsonField(raw, walletAddress);
1944
+ async function readRawJsonField(raw, column, walletAddress) {
1945
+ const value = raw[column];
1946
+ if (!value) return void 0;
1947
+ if (walletAddress && isEncrypted(value)) {
1948
+ return await decryptJsonField(value, walletAddress);
1926
1949
  }
1927
1950
  try {
1928
- return JSON.parse(raw);
1951
+ return JSON.parse(value);
1929
1952
  } catch {
1930
1953
  return void 0;
1931
1954
  }
@@ -1944,21 +1967,21 @@ async function searchChunksOp(ctx, queryVector, options) {
1944
1967
  activeConversations.map((c) => String(c._getRaw("conversation_id")))
1945
1968
  );
1946
1969
  const queryConditions = conversationId ? [Q.where("conversation_id", conversationId)] : [];
1947
- const messages = await ctx.messagesCollection.query(...queryConditions).fetch();
1970
+ const messages = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
1948
1971
  const candidates = [];
1949
1972
  let staleSkipped = 0;
1950
1973
  let unreadableVectors = 0;
1951
1974
  for (const message of messages) {
1952
- const msgConvId = String(message._getRaw("conversation_id"));
1975
+ const msgConvId = String(message.conversation_id);
1953
1976
  if (!activeConversationIds.has(msgConvId)) continue;
1954
1977
  if (embeddingModel) {
1955
- const storedModel = message._getRaw("embedding_model");
1978
+ const storedModel = message.embedding_model;
1956
1979
  if (storedModel !== null && storedModel !== void 0 && storedModel !== embeddingModel) {
1957
1980
  staleSkipped++;
1958
1981
  continue;
1959
1982
  }
1960
1983
  }
1961
- const version = Number(message._getRaw("updated_at")) || 0;
1984
+ const version = Number(message.updated_at) || 0;
1962
1985
  const cached = chunkCache?.get(message.id);
1963
1986
  if (cached && cached.version === version) {
1964
1987
  if (cached.chunks.length > 0) {
@@ -1982,7 +2005,7 @@ async function searchChunksOp(ctx, queryVector, options) {
1982
2005
  }
1983
2006
  continue;
1984
2007
  }
1985
- const chunks = await readJsonField(message, "chunks", ctx.walletAddress);
2008
+ const chunks = await readRawJsonField(message, "chunks", ctx.walletAddress);
1986
2009
  if (chunks && chunks.length > 0) {
1987
2010
  const vectors = chunks.map(
1988
2011
  (chunk) => decodeChunkVector(chunk.vector, () => {
@@ -2003,7 +2026,7 @@ async function searchChunksOp(ctx, queryVector, options) {
2003
2026
  }
2004
2027
  }
2005
2028
  } else {
2006
- const messageVector = await readJsonField(message, "vector", ctx.walletAddress);
2029
+ const messageVector = await readRawJsonField(message, "vector", ctx.walletAddress);
2007
2030
  if (!messageVector || messageVector.length === 0) continue;
2008
2031
  const fallback = Float32Array.from(messageVector);
2009
2032
  chunkCache?.set(message.id, { version, chunks: [], fallback });
@@ -2029,7 +2052,7 @@ async function searchChunksOp(ctx, queryVector, options) {
2029
2052
  const storedById = new Map(
2030
2053
  await Promise.all(
2031
2054
  Array.from(uniqueMessages, async ([id, m]) => {
2032
- const stored = await messageToStored(
2055
+ const stored = await messageRawToStored(
2033
2056
  m,
2034
2057
  ctx.walletAddress,
2035
2058
  ctx.signMessage,
@@ -2047,8 +2070,8 @@ async function searchChunksOp(ctx, queryVector, options) {
2047
2070
  await Promise.all(
2048
2071
  Array.from(needsChunkText, async (id) => {
2049
2072
  const m = uniqueMessages.get(id);
2050
- const parsed = await readJsonField(m, "chunks", ctx.walletAddress);
2051
- const version = Number(m._getRaw("updated_at")) || 0;
2073
+ const parsed = await readRawJsonField(m, "chunks", ctx.walletAddress);
2074
+ const version = Number(m.updated_at) || 0;
2052
2075
  if (parsed) chunkStateByMsgId.set(id, { version, chunks: parsed });
2053
2076
  })
2054
2077
  );
@@ -6885,7 +6908,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
6885
6908
  subQueries = decomp.subQueries;
6886
6909
  }
6887
6910
  }
6888
- const { recall: recall2 } = await import("./recall-GKYVPD2B.mjs");
6911
+ const { recall: recall2 } = await import("./recall-TGRI5MN4.mjs");
6889
6912
  let recallDegraded = [];
6890
6913
  const result = await recall2(
6891
6914
  query,
@@ -7889,6 +7912,7 @@ export {
7889
7912
  getMessageCountOp,
7890
7913
  getMessagesPageOp,
7891
7914
  getMessageSkeletonsOp,
7915
+ getToolCallEventIdsOp,
7892
7916
  clearMessagesOp,
7893
7917
  createMessageOp,
7894
7918
  upsertMessageOp,
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  notifyConsolidationFallback,
3
3
  resolvePiiRedactor
4
- } from "./chunk-2GNOTRG4.mjs";
4
+ } from "./chunk-A2G6TRUK.mjs";
5
5
  import {
6
6
  callPortalJsonCompletion
7
- } from "./chunk-3V3BBJLH.mjs";
7
+ } from "./chunk-4Z2X4UYQ.mjs";
8
8
  import "./chunk-GYKJOWEO.mjs";
9
9
 
10
10
  // src/lib/memory/consolidate.ts
@@ -1963,6 +1963,29 @@ async function getMessageSkeletonsOp(ctx, convId) {
1963
1963
  }
1964
1964
  return skeletons;
1965
1965
  }
1966
+ async function getToolCallEventIdsOp(ctx, convId) {
1967
+ const results = await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId)).unsafeFetchRaw();
1968
+ const ids = /* @__PURE__ */ new Set();
1969
+ for (const raw of results) {
1970
+ const value = raw.tool_call_events;
1971
+ if (!value) continue;
1972
+ let events;
1973
+ if (typeof value === "string") {
1974
+ try {
1975
+ events = JSON.parse(value);
1976
+ } catch {
1977
+ continue;
1978
+ }
1979
+ } else if (Array.isArray(value)) {
1980
+ events = value;
1981
+ }
1982
+ if (!Array.isArray(events)) continue;
1983
+ for (const evt of events) {
1984
+ if (evt && evt.id) ids.add(evt.id);
1985
+ }
1986
+ }
1987
+ return ids;
1988
+ }
1966
1989
  async function clearMessagesOp(ctx, convId) {
1967
1990
  const messages = await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId)).fetch();
1968
1991
  await ctx.database.write(async () => {
@@ -2201,14 +2224,14 @@ function resolveChunkText(content, chunk, chunksInRow) {
2201
2224
  if (covered !== content.length) return content;
2202
2225
  return content.slice(start, end);
2203
2226
  }
2204
- async function readJsonField(model, column, walletAddress) {
2205
- const raw = model._getRaw(column);
2206
- if (!raw) return void 0;
2207
- if (walletAddress && isEncrypted(raw)) {
2208
- return await decryptJsonField(raw, walletAddress);
2227
+ async function readRawJsonField(raw, column, walletAddress) {
2228
+ const value = raw[column];
2229
+ if (!value) return void 0;
2230
+ if (walletAddress && isEncrypted(value)) {
2231
+ return await decryptJsonField(value, walletAddress);
2209
2232
  }
2210
2233
  try {
2211
- return JSON.parse(raw);
2234
+ return JSON.parse(value);
2212
2235
  } catch {
2213
2236
  return void 0;
2214
2237
  }
@@ -2227,21 +2250,21 @@ async function searchChunksOp(ctx, queryVector, options) {
2227
2250
  activeConversations.map((c) => String(c._getRaw("conversation_id")))
2228
2251
  );
2229
2252
  const queryConditions = conversationId ? [import_watermelondb.Q.where("conversation_id", conversationId)] : [];
2230
- const messages = await ctx.messagesCollection.query(...queryConditions).fetch();
2253
+ const messages = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
2231
2254
  const candidates = [];
2232
2255
  let staleSkipped = 0;
2233
2256
  let unreadableVectors = 0;
2234
2257
  for (const message of messages) {
2235
- const msgConvId = String(message._getRaw("conversation_id"));
2258
+ const msgConvId = String(message.conversation_id);
2236
2259
  if (!activeConversationIds.has(msgConvId)) continue;
2237
2260
  if (embeddingModel) {
2238
- const storedModel = message._getRaw("embedding_model");
2261
+ const storedModel = message.embedding_model;
2239
2262
  if (storedModel !== null && storedModel !== void 0 && storedModel !== embeddingModel) {
2240
2263
  staleSkipped++;
2241
2264
  continue;
2242
2265
  }
2243
2266
  }
2244
- const version = Number(message._getRaw("updated_at")) || 0;
2267
+ const version = Number(message.updated_at) || 0;
2245
2268
  const cached = chunkCache?.get(message.id);
2246
2269
  if (cached && cached.version === version) {
2247
2270
  if (cached.chunks.length > 0) {
@@ -2265,7 +2288,7 @@ async function searchChunksOp(ctx, queryVector, options) {
2265
2288
  }
2266
2289
  continue;
2267
2290
  }
2268
- const chunks = await readJsonField(message, "chunks", ctx.walletAddress);
2291
+ const chunks = await readRawJsonField(message, "chunks", ctx.walletAddress);
2269
2292
  if (chunks && chunks.length > 0) {
2270
2293
  const vectors = chunks.map(
2271
2294
  (chunk) => decodeChunkVector(chunk.vector, () => {
@@ -2286,7 +2309,7 @@ async function searchChunksOp(ctx, queryVector, options) {
2286
2309
  }
2287
2310
  }
2288
2311
  } else {
2289
- const messageVector = await readJsonField(message, "vector", ctx.walletAddress);
2312
+ const messageVector = await readRawJsonField(message, "vector", ctx.walletAddress);
2290
2313
  if (!messageVector || messageVector.length === 0) continue;
2291
2314
  const fallback = Float32Array.from(messageVector);
2292
2315
  chunkCache?.set(message.id, { version, chunks: [], fallback });
@@ -2312,7 +2335,7 @@ async function searchChunksOp(ctx, queryVector, options) {
2312
2335
  const storedById = new Map(
2313
2336
  await Promise.all(
2314
2337
  Array.from(uniqueMessages, async ([id, m]) => {
2315
- const stored = await messageToStored(
2338
+ const stored = await messageRawToStored(
2316
2339
  m,
2317
2340
  ctx.walletAddress,
2318
2341
  ctx.signMessage,
@@ -2330,8 +2353,8 @@ async function searchChunksOp(ctx, queryVector, options) {
2330
2353
  await Promise.all(
2331
2354
  Array.from(needsChunkText, async (id) => {
2332
2355
  const m = uniqueMessages.get(id);
2333
- const parsed = await readJsonField(m, "chunks", ctx.walletAddress);
2334
- const version = Number(m._getRaw("updated_at")) || 0;
2356
+ const parsed = await readRawJsonField(m, "chunks", ctx.walletAddress);
2357
+ const version = Number(m.updated_at) || 0;
2335
2358
  if (parsed) chunkStateByMsgId.set(id, { version, chunks: parsed });
2336
2359
  })
2337
2360
  );
@@ -6145,6 +6168,9 @@ function supportsResponseFormat(model, variant = "json_object") {
6145
6168
  const allow = variant === "json_schema" ? RESPONSE_SCHEMA_OK : RESPONSE_FORMAT_OK;
6146
6169
  return model.split("/").some((seg) => allow.has(seg));
6147
6170
  }
6171
+ function requiresResponsesTransport(model) {
6172
+ return model.split("/").some((seg) => RESPONSES_ONLY_MODEL_PREFIXES.some((prefix) => seg.startsWith(prefix)));
6173
+ }
6148
6174
  function responsesExtra(extra) {
6149
6175
  if (!extra) return {};
6150
6176
  const out = {};
@@ -6480,7 +6506,7 @@ function extractResponsesContent(body) {
6480
6506
  const text12 = root.output.filter((item) => asRecord(item)?.type === "message").flatMap((item) => Array.isArray(item.content) ? item.content : []).map((part) => asRecord(part)?.text).filter((t) => typeof t === "string").join("");
6481
6507
  return text12;
6482
6508
  }
6483
- var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, CHAT_TO_RESPONSES_FIELDS, FOREIGN_ENDPOINT_SUFFIX, RETRYABLE_HTTP;
6509
+ var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, RESPONSES_ONLY_MODEL_PREFIXES, CHAT_TO_RESPONSES_FIELDS, FOREIGN_ENDPOINT_SUFFIX, RETRYABLE_HTTP;
6484
6510
  var init_portalLlm = __esm({
6485
6511
  "src/lib/memory/portalLlm.ts"() {
6486
6512
  "use strict";
@@ -6492,6 +6518,7 @@ var init_portalLlm = __esm({
6492
6518
  RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
6493
6519
  RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
6494
6520
  JSON_CONTRACT_REMINDER = "Output ONLY the strict JSON object requested. No prose, no explanation, no markdown.";
6521
+ RESPONSES_ONLY_MODEL_PREFIXES = ["gpt-5.6"];
6495
6522
  CHAT_TO_RESPONSES_FIELDS = {
6496
6523
  max_completion_tokens: "max_output_tokens"
6497
6524
  };
@@ -17817,6 +17844,9 @@ var DEFAULT_BASE_URL2 = BASE_URL;
17817
17844
  var DEFAULT_MODEL4 = "anthropic/claude-sonnet-4-6";
17818
17845
  var DEFAULT_MAX_TOKENS = 4096;
17819
17846
  var REQUEST_TIMEOUT_MS = 6e4;
17847
+ var MIN_RETRY_BUDGET_MS = 2e3;
17848
+ var MIN_RESPONSES_OUTPUT_TOKENS = 2048;
17849
+ var SCHEMA_FALLBACK_SKIP_STATUSES = /* @__PURE__ */ new Set([401, 403, 404, 408, 409, 413, 425, 429]);
17820
17850
  var DEFAULT_SYSTEM_PROMPT = `You are a personal assistant with access to the user's memory. Answer the user's question using the supplied memories as evidence.
17821
17851
 
17822
17852
  Rules:
@@ -17848,14 +17878,9 @@ ${m.content}`).join("\n\n");
17848
17878
  const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL2;
17849
17879
  const model = options.llmModel ?? DEFAULT_MODEL4;
17850
17880
  const wantsStructured = !!options.responseSchema;
17851
- const sendResponseFormat = wantsStructured && supportsResponseFormat(model, "json_schema");
17881
+ const useResponsesTransport = requiresResponsesTransport(model);
17882
+ const sendResponseFormat = wantsStructured && !useResponsesTransport && supportsResponseFormat(model, "json_schema");
17852
17883
  const basePrompt = options.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
17853
- const systemPrompt = wantsStructured && !sendResponseFormat ? `${basePrompt}
17854
-
17855
- Respond with ONLY a single JSON object conforming to this JSON Schema, with no prose, comments, or code fences:
17856
- ${JSON.stringify(
17857
- options.responseSchema
17858
- )}` : basePrompt;
17859
17884
  const userMessage = [
17860
17885
  `Question:
17861
17886
  ${trimmed}`,
@@ -17865,85 +17890,121 @@ ${evidence}`
17865
17890
  ].join("\n\n");
17866
17891
  const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
17867
17892
  const fetchImpl = options.fetchFn ?? fetch;
17893
+ const buildBody = (useResponseFormat) => {
17894
+ const systemPrompt = wantsStructured && !useResponseFormat ? `${basePrompt}
17895
+
17896
+ Respond with ONLY a single JSON object conforming to this JSON Schema, with no prose, comments, or code fences:
17897
+ ${JSON.stringify(
17898
+ options.responseSchema
17899
+ )}` : basePrompt;
17900
+ const messages = [
17901
+ { role: "system", content: systemPrompt },
17902
+ { role: "user", content: userMessage }
17903
+ ];
17904
+ if (useResponsesTransport) {
17905
+ return JSON.stringify({
17906
+ model,
17907
+ // Same system+user pair under the Responses-API field names: `input`
17908
+ // for the turns, `max_output_tokens` for the cap. Sending the
17909
+ // chat-completions spelling here is silently ignored, which caps the
17910
+ // answer at the portal default.
17911
+ input: messages,
17912
+ max_output_tokens: Math.max(maxTokens, MIN_RESPONSES_OUTPUT_TOKENS)
17913
+ });
17914
+ }
17915
+ return JSON.stringify({
17916
+ model,
17917
+ // Modern OpenAI field; the portal reads only `max_completion_tokens`
17918
+ // (the deprecated `max_tokens` is silently ignored → falls back to the
17919
+ // portal's default output cap and truncates the answer).
17920
+ max_completion_tokens: maxTokens,
17921
+ messages,
17922
+ ...useResponseFormat && {
17923
+ response_format: {
17924
+ type: "json_schema",
17925
+ json_schema: { name: "reflect_output", schema: options.responseSchema }
17926
+ }
17927
+ }
17928
+ });
17929
+ };
17868
17930
  const log = getLogger();
17869
17931
  const authHeaders = await resolvePortalAuthHeaders(options, "memory/reflect");
17870
17932
  if (authHeaders === null) return baseResult;
17871
- const controller = new AbortController();
17872
17933
  const deadline = Date.now() + REQUEST_TIMEOUT_MS;
17873
17934
  const remaining = () => Math.max(0, deadline - Date.now());
17874
- const timer = setTimeout(() => controller.abort(), remaining());
17875
- let response;
17876
- try {
17877
- response = await fetchImpl(`${baseUrl}/api/v1/chat/completions`, {
17878
- method: "POST",
17879
- // No task type unless the caller named one — an undeclared task adds no
17880
- // header at all, which is what keeps user-facing reflect() unlabelled.
17881
- headers: {
17882
- ...authHeaders,
17883
- ...taskTypeHeader(options.taskType),
17884
- "Content-Type": "application/json"
17885
- },
17886
- body: JSON.stringify({
17887
- model,
17888
- // Modern OpenAI field; the portal reads only `max_completion_tokens`
17889
- // (the deprecated `max_tokens` is silently ignored → falls back to the
17890
- // portal's default output cap and truncates the answer).
17891
- max_completion_tokens: maxTokens,
17892
- messages: [
17893
- { role: "system", content: systemPrompt },
17894
- { role: "user", content: userMessage }
17895
- ],
17896
- ...sendResponseFormat && {
17897
- response_format: {
17898
- type: "json_schema",
17899
- json_schema: { name: "reflect_output", schema: options.responseSchema }
17900
- }
17901
- }
17902
- }),
17903
- signal: controller.signal
17904
- });
17905
- } catch (err) {
17935
+ const endpoint = useResponsesTransport ? "/api/v1/responses" : "/api/v1/chat/completions";
17936
+ const sendOnce = async (useResponseFormat) => {
17937
+ const controller = new AbortController();
17938
+ const timer = setTimeout(() => controller.abort(), remaining());
17939
+ let response;
17940
+ try {
17941
+ response = await fetchImpl(`${baseUrl}${endpoint}`, {
17942
+ method: "POST",
17943
+ // No task type unless the caller named one — an undeclared task adds no
17944
+ // header at all, which is what keeps user-facing reflect() unlabelled.
17945
+ headers: {
17946
+ ...authHeaders,
17947
+ ...taskTypeHeader(options.taskType),
17948
+ "Content-Type": "application/json"
17949
+ },
17950
+ body: buildBody(useResponseFormat),
17951
+ signal: controller.signal
17952
+ });
17953
+ } catch (err) {
17954
+ clearTimeout(timer);
17955
+ log.warn("[memory/reflect] portal request failed", {
17956
+ err: err instanceof Error ? err.message : String(err),
17957
+ baseUrl
17958
+ });
17959
+ return { kind: "error" };
17960
+ }
17906
17961
  clearTimeout(timer);
17907
- log.warn("[memory/reflect] portal request failed", {
17908
- err: err instanceof Error ? err.message : String(err),
17909
- baseUrl
17962
+ if (!response.ok) {
17963
+ return { kind: "http", status: response.status, statusText: response.statusText };
17964
+ }
17965
+ const bodyTimer = setTimeout(() => controller.abort(), remaining());
17966
+ try {
17967
+ const body = await response.json();
17968
+ clearTimeout(bodyTimer);
17969
+ return { kind: "ok", body };
17970
+ } catch (err) {
17971
+ clearTimeout(bodyTimer);
17972
+ log.warn("[memory/reflect] failed to parse portal response body", {
17973
+ err: err instanceof Error ? err.message : String(err)
17974
+ });
17975
+ return { kind: "error" };
17976
+ }
17977
+ };
17978
+ let attempt = await sendOnce(sendResponseFormat);
17979
+ let schemaFallbackUsed = false;
17980
+ const budgetLeftMs = remaining();
17981
+ if (attempt.kind === "http" && sendResponseFormat && !SCHEMA_FALLBACK_SKIP_STATUSES.has(attempt.status) && budgetLeftMs >= MIN_RETRY_BUDGET_MS) {
17982
+ log.warn("[memory/reflect] response_format json_schema rejected; retrying schema-in-prompt", {
17983
+ status: attempt.status,
17984
+ statusText: attempt.statusText,
17985
+ model,
17986
+ taskType: options.taskType,
17987
+ budgetLeftMs
17910
17988
  });
17911
- return baseResult;
17989
+ schemaFallbackUsed = true;
17990
+ attempt = await sendOnce(false);
17912
17991
  }
17913
- clearTimeout(timer);
17914
- if (!response.ok) {
17992
+ if (attempt.kind === "error") return baseResult;
17993
+ if (attempt.kind === "http") {
17915
17994
  log.warn("[memory/reflect] portal returned non-OK", {
17916
- status: response.status,
17917
- statusText: response.statusText
17918
- });
17919
- return baseResult;
17920
- }
17921
- const bodyTimer = setTimeout(() => controller.abort(), remaining());
17922
- let body;
17923
- try {
17924
- body = await response.json();
17925
- } catch (err) {
17926
- clearTimeout(bodyTimer);
17927
- log.warn("[memory/reflect] failed to parse portal response body", {
17928
- err: err instanceof Error ? err.message : String(err)
17995
+ status: attempt.status,
17996
+ statusText: attempt.statusText,
17997
+ // Distinguishes "the fallback ran and still failed" from "never tried".
17998
+ schemaFallbackUsed
17929
17999
  });
17930
18000
  return baseResult;
17931
18001
  }
17932
- clearTimeout(bodyTimer);
17933
- return parseAnswer(body, baseResult, !!options.responseSchema);
18002
+ return parseAnswer(attempt.body, baseResult, !!options.responseSchema);
17934
18003
  }
17935
18004
  function parseAnswer(body, base, parseSchema) {
17936
18005
  if (typeof body !== "object" || body === null) return base;
17937
18006
  const obj = body;
17938
- const choices = obj.choices;
17939
- let text12 = "";
17940
- if (Array.isArray(choices) && choices.length > 0) {
17941
- const message = choices[0].message;
17942
- if (typeof message === "object" && message !== null) {
17943
- const content = message.content;
17944
- if (typeof content === "string") text12 = content;
17945
- }
17946
- }
18007
+ const text12 = extractCompletionContent(obj) ?? "";
17947
18008
  const usage = obj.usage;
17948
18009
  let structuredOutput;
17949
18010
  if (parseSchema && text12) {
@@ -17955,14 +18016,19 @@ function parseAnswer(body, base, parseSchema) {
17955
18016
  });
17956
18017
  }
17957
18018
  }
18019
+ const promptTokens = usage?.prompt_tokens ?? usage?.input_tokens ?? 0;
18020
+ const completionTokens = usage?.completion_tokens ?? usage?.output_tokens ?? 0;
17958
18021
  return {
17959
18022
  text: text12,
17960
18023
  ...structuredOutput !== void 0 && { structuredOutput },
17961
18024
  basedOn: base.basedOn,
17962
18025
  usage: {
17963
- promptTokens: usage?.prompt_tokens ?? 0,
17964
- completionTokens: usage?.completion_tokens ?? 0,
17965
- totalTokens: usage?.total_tokens ?? 0
18026
+ promptTokens,
18027
+ completionTokens,
18028
+ // Derived when absent: the Responses API reports the two component counts
18029
+ // and does not always send a total, which would otherwise report zero
18030
+ // spend for a call that plainly cost something.
18031
+ totalTokens: usage?.total_tokens ?? promptTokens + completionTokens
17966
18032
  }
17967
18033
  };
17968
18034
  }
@@ -21285,22 +21351,31 @@ function useChatStorage(options) {
21285
21351
  const serverToolsPromise = getTokenRef.current && !(Array.isArray(serverToolsFilter) && serverToolsFilter.length === 0) ? fetchServerToolsSettled(getTokenRef.current) : null;
21286
21352
  const userMessageEmbeddingPromise = getTokenRef.current && contentForStorage.length >= MIN_CONTENT_LENGTH_FOR_TOOLS && (isServerToolsFunction && !(serverToolsConfig?.deferLoading?.enabled && effectiveApiType === "responses") || !!clientTools?.length) ? embedForToolsSettled(getTokenRef.current) : null;
21287
21353
  let messagesToSend;
21288
- const storedMessages = await getMessages(convId);
21289
- const knownToolCallEventIds = /* @__PURE__ */ new Set();
21290
- for (const msg of storedMessages) {
21291
- if (msg.toolCallEvents) {
21292
- for (const evt of msg.toolCallEvents) {
21293
- if (evt.id) knownToolCallEventIds.add(evt.id);
21294
- }
21295
- }
21296
- }
21354
+ const knownToolCallEventIds = await getToolCallEventIdsOp(storageCtx, convId);
21297
21355
  if (includeHistory) {
21298
- const validMessages = storedMessages.filter((msg) => !msg.error);
21299
- const replayableMessages = prepareToolResultsForReplay(validMessages, {
21300
- fold: foldToolResultsInHistoryRef.current === true,
21301
- exclude: toolResultsHistoryExcludeRef.current,
21302
- placeholder: DISPLAY_CARD_PLACEHOLDER
21303
- });
21356
+ let tail = [];
21357
+ let replayableMessages = [];
21358
+ let beforeMessageId;
21359
+ let boundaryExcludeUniqueIds;
21360
+ for (; ; ) {
21361
+ const page = await getMessagesPageOp(storageCtx, convId, {
21362
+ limit: maxHistoryMessages,
21363
+ beforeMessageId,
21364
+ boundaryExcludeUniqueIds
21365
+ });
21366
+ if (page.length === 0) break;
21367
+ tail = [...page, ...tail];
21368
+ const validMessages = tail.filter((msg) => !msg.error);
21369
+ replayableMessages = prepareToolResultsForReplay(validMessages, {
21370
+ fold: foldToolResultsInHistoryRef.current === true,
21371
+ exclude: toolResultsHistoryExcludeRef.current,
21372
+ placeholder: DISPLAY_CARD_PLACEHOLDER
21373
+ });
21374
+ if (replayableMessages.length >= maxHistoryMessages) break;
21375
+ if (page.length < maxHistoryMessages) break;
21376
+ beforeMessageId = page[0].messageId;
21377
+ boundaryExcludeUniqueIds = tail.filter((msg) => msg.messageId === beforeMessageId).map((msg) => msg.uniqueId);
21378
+ }
21304
21379
  const limitedMessages = replayableMessages.slice(-maxHistoryMessages);
21305
21380
  const foldedHistory = limitedMessages;
21306
21381
  if (summarizeHistory && !getTokenRef.current) {