@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.
@@ -16830,14 +16830,14 @@ function resolveChunkText(content, chunk, chunksInRow) {
16830
16830
  if (covered !== content.length) return content;
16831
16831
  return content.slice(start, end);
16832
16832
  }
16833
- async function readJsonField(model, column, walletAddress) {
16834
- const raw = model._getRaw(column);
16835
- if (!raw) return void 0;
16836
- if (walletAddress && isEncrypted(raw)) {
16837
- return await decryptJsonField(raw, walletAddress);
16833
+ async function readRawJsonField(raw, column, walletAddress) {
16834
+ const value = raw[column];
16835
+ if (!value) return void 0;
16836
+ if (walletAddress && isEncrypted(value)) {
16837
+ return await decryptJsonField(value, walletAddress);
16838
16838
  }
16839
16839
  try {
16840
- return JSON.parse(raw);
16840
+ return JSON.parse(value);
16841
16841
  } catch {
16842
16842
  return void 0;
16843
16843
  }
@@ -16850,12 +16850,12 @@ async function searchMessagesOp(ctx, queryVector, options) {
16850
16850
  activeConversations.map((c) => String(c._getRaw("conversation_id")))
16851
16851
  );
16852
16852
  const queryConditions = conversationId ? [import_watermelondb14.Q.where("conversation_id", conversationId)] : [];
16853
- const messages = await ctx.messagesCollection.query(...queryConditions).fetch();
16853
+ const messages = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
16854
16854
  const candidates = [];
16855
16855
  for (const message of messages) {
16856
- const msgConvId = String(message._getRaw("conversation_id"));
16856
+ const msgConvId = String(message.conversation_id);
16857
16857
  if (!activeConversationIds.has(msgConvId)) continue;
16858
- const messageVector = await readJsonField(message, "vector", ctx.walletAddress);
16858
+ const messageVector = await readRawJsonField(message, "vector", ctx.walletAddress);
16859
16859
  if (!messageVector || messageVector.length === 0) continue;
16860
16860
  const similarity = cosineSimilarity(queryVector, messageVector);
16861
16861
  if (similarity >= minSimilarity) {
@@ -16865,7 +16865,7 @@ async function searchMessagesOp(ctx, queryVector, options) {
16865
16865
  const topK = candidates.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
16866
16866
  return Promise.all(
16867
16867
  topK.map(async ({ message, similarity }) => {
16868
- const stored = await messageToStored(
16868
+ const stored = await messageRawToStored(
16869
16869
  message,
16870
16870
  ctx.walletAddress,
16871
16871
  ctx.signMessage,
@@ -16889,21 +16889,21 @@ async function searchChunksOp(ctx, queryVector, options) {
16889
16889
  activeConversations.map((c) => String(c._getRaw("conversation_id")))
16890
16890
  );
16891
16891
  const queryConditions = conversationId ? [import_watermelondb14.Q.where("conversation_id", conversationId)] : [];
16892
- const messages = await ctx.messagesCollection.query(...queryConditions).fetch();
16892
+ const messages = await ctx.messagesCollection.query(...queryConditions).unsafeFetchRaw();
16893
16893
  const candidates = [];
16894
16894
  let staleSkipped = 0;
16895
16895
  let unreadableVectors = 0;
16896
16896
  for (const message of messages) {
16897
- const msgConvId = String(message._getRaw("conversation_id"));
16897
+ const msgConvId = String(message.conversation_id);
16898
16898
  if (!activeConversationIds.has(msgConvId)) continue;
16899
16899
  if (embeddingModel) {
16900
- const storedModel = message._getRaw("embedding_model");
16900
+ const storedModel = message.embedding_model;
16901
16901
  if (storedModel !== null && storedModel !== void 0 && storedModel !== embeddingModel) {
16902
16902
  staleSkipped++;
16903
16903
  continue;
16904
16904
  }
16905
16905
  }
16906
- const version = Number(message._getRaw("updated_at")) || 0;
16906
+ const version = Number(message.updated_at) || 0;
16907
16907
  const cached = chunkCache?.get(message.id);
16908
16908
  if (cached && cached.version === version) {
16909
16909
  if (cached.chunks.length > 0) {
@@ -16927,7 +16927,7 @@ async function searchChunksOp(ctx, queryVector, options) {
16927
16927
  }
16928
16928
  continue;
16929
16929
  }
16930
- const chunks = await readJsonField(message, "chunks", ctx.walletAddress);
16930
+ const chunks = await readRawJsonField(message, "chunks", ctx.walletAddress);
16931
16931
  if (chunks && chunks.length > 0) {
16932
16932
  const vectors = chunks.map(
16933
16933
  (chunk) => decodeChunkVector(chunk.vector, () => {
@@ -16948,7 +16948,7 @@ async function searchChunksOp(ctx, queryVector, options) {
16948
16948
  }
16949
16949
  }
16950
16950
  } else {
16951
- const messageVector = await readJsonField(message, "vector", ctx.walletAddress);
16951
+ const messageVector = await readRawJsonField(message, "vector", ctx.walletAddress);
16952
16952
  if (!messageVector || messageVector.length === 0) continue;
16953
16953
  const fallback = Float32Array.from(messageVector);
16954
16954
  chunkCache?.set(message.id, { version, chunks: [], fallback });
@@ -16974,7 +16974,7 @@ async function searchChunksOp(ctx, queryVector, options) {
16974
16974
  const storedById = new Map(
16975
16975
  await Promise.all(
16976
16976
  Array.from(uniqueMessages, async ([id, m]) => {
16977
- const stored = await messageToStored(
16977
+ const stored = await messageRawToStored(
16978
16978
  m,
16979
16979
  ctx.walletAddress,
16980
16980
  ctx.signMessage,
@@ -16992,8 +16992,8 @@ async function searchChunksOp(ctx, queryVector, options) {
16992
16992
  await Promise.all(
16993
16993
  Array.from(needsChunkText, async (id) => {
16994
16994
  const m = uniqueMessages.get(id);
16995
- const parsed = await readJsonField(m, "chunks", ctx.walletAddress);
16996
- const version = Number(m._getRaw("updated_at")) || 0;
16995
+ const parsed = await readRawJsonField(m, "chunks", ctx.walletAddress);
16996
+ const version = Number(m.updated_at) || 0;
16997
16997
  if (parsed) chunkStateByMsgId.set(id, { version, chunks: parsed });
16998
16998
  })
16999
16999
  );
@@ -20611,6 +20611,9 @@ function supportsResponseFormat(model, variant = "json_object") {
20611
20611
  const allow = variant === "json_schema" ? RESPONSE_SCHEMA_OK : RESPONSE_FORMAT_OK;
20612
20612
  return model.split("/").some((seg) => allow.has(seg));
20613
20613
  }
20614
+ function requiresResponsesTransport(model) {
20615
+ return model.split("/").some((seg) => RESPONSES_ONLY_MODEL_PREFIXES.some((prefix) => seg.startsWith(prefix)));
20616
+ }
20614
20617
  function responsesExtra(extra) {
20615
20618
  if (!extra) return {};
20616
20619
  const out = {};
@@ -20946,7 +20949,7 @@ function extractResponsesContent(body) {
20946
20949
  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("");
20947
20950
  return text12;
20948
20951
  }
20949
- var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, CHAT_TO_RESPONSES_FIELDS, FOREIGN_ENDPOINT_SUFFIX, RETRYABLE_HTTP;
20952
+ var RESPONSE_FORMAT_OK, RESPONSE_SCHEMA_OK, JSON_CONTRACT_REMINDER, RESPONSES_ONLY_MODEL_PREFIXES, CHAT_TO_RESPONSES_FIELDS, FOREIGN_ENDPOINT_SUFFIX, RETRYABLE_HTTP;
20950
20953
  var init_portalLlm = __esm({
20951
20954
  "src/lib/memory/portalLlm.ts"() {
20952
20955
  "use strict";
@@ -20958,6 +20961,7 @@ var init_portalLlm = __esm({
20958
20961
  RESPONSE_FORMAT_OK = /* @__PURE__ */ new Set(["openai", "inclusionai", "deepseek"]);
20959
20962
  RESPONSE_SCHEMA_OK = /* @__PURE__ */ new Set(["openai"]);
20960
20963
  JSON_CONTRACT_REMINDER = "Output ONLY the strict JSON object requested. No prose, no explanation, no markdown.";
20964
+ RESPONSES_ONLY_MODEL_PREFIXES = ["gpt-5.6"];
20961
20965
  CHAT_TO_RESPONSES_FIELDS = {
20962
20966
  max_completion_tokens: "max_output_tokens"
20963
20967
  };
@@ -30733,6 +30737,9 @@ var DEFAULT_BASE_URL2 = BASE_URL;
30733
30737
  var DEFAULT_MODEL4 = "anthropic/claude-sonnet-4-6";
30734
30738
  var DEFAULT_MAX_TOKENS = 4096;
30735
30739
  var REQUEST_TIMEOUT_MS = 6e4;
30740
+ var MIN_RETRY_BUDGET_MS = 2e3;
30741
+ var MIN_RESPONSES_OUTPUT_TOKENS = 2048;
30742
+ var SCHEMA_FALLBACK_SKIP_STATUSES = /* @__PURE__ */ new Set([401, 403, 404, 408, 409, 413, 425, 429]);
30736
30743
  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.
30737
30744
 
30738
30745
  Rules:
@@ -30764,14 +30771,9 @@ ${m.content}`).join("\n\n");
30764
30771
  const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL2;
30765
30772
  const model = options.llmModel ?? DEFAULT_MODEL4;
30766
30773
  const wantsStructured = !!options.responseSchema;
30767
- const sendResponseFormat = wantsStructured && supportsResponseFormat(model, "json_schema");
30774
+ const useResponsesTransport = requiresResponsesTransport(model);
30775
+ const sendResponseFormat = wantsStructured && !useResponsesTransport && supportsResponseFormat(model, "json_schema");
30768
30776
  const basePrompt = options.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
30769
- const systemPrompt = wantsStructured && !sendResponseFormat ? `${basePrompt}
30770
-
30771
- Respond with ONLY a single JSON object conforming to this JSON Schema, with no prose, comments, or code fences:
30772
- ${JSON.stringify(
30773
- options.responseSchema
30774
- )}` : basePrompt;
30775
30777
  const userMessage = [
30776
30778
  `Question:
30777
30779
  ${trimmed}`,
@@ -30781,85 +30783,121 @@ ${evidence}`
30781
30783
  ].join("\n\n");
30782
30784
  const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
30783
30785
  const fetchImpl = options.fetchFn ?? fetch;
30786
+ const buildBody = (useResponseFormat) => {
30787
+ const systemPrompt = wantsStructured && !useResponseFormat ? `${basePrompt}
30788
+
30789
+ Respond with ONLY a single JSON object conforming to this JSON Schema, with no prose, comments, or code fences:
30790
+ ${JSON.stringify(
30791
+ options.responseSchema
30792
+ )}` : basePrompt;
30793
+ const messages = [
30794
+ { role: "system", content: systemPrompt },
30795
+ { role: "user", content: userMessage }
30796
+ ];
30797
+ if (useResponsesTransport) {
30798
+ return JSON.stringify({
30799
+ model,
30800
+ // Same system+user pair under the Responses-API field names: `input`
30801
+ // for the turns, `max_output_tokens` for the cap. Sending the
30802
+ // chat-completions spelling here is silently ignored, which caps the
30803
+ // answer at the portal default.
30804
+ input: messages,
30805
+ max_output_tokens: Math.max(maxTokens, MIN_RESPONSES_OUTPUT_TOKENS)
30806
+ });
30807
+ }
30808
+ return JSON.stringify({
30809
+ model,
30810
+ // Modern OpenAI field; the portal reads only `max_completion_tokens`
30811
+ // (the deprecated `max_tokens` is silently ignored → falls back to the
30812
+ // portal's default output cap and truncates the answer).
30813
+ max_completion_tokens: maxTokens,
30814
+ messages,
30815
+ ...useResponseFormat && {
30816
+ response_format: {
30817
+ type: "json_schema",
30818
+ json_schema: { name: "reflect_output", schema: options.responseSchema }
30819
+ }
30820
+ }
30821
+ });
30822
+ };
30784
30823
  const log = getLogger();
30785
30824
  const authHeaders = await resolvePortalAuthHeaders(options, "memory/reflect");
30786
30825
  if (authHeaders === null) return baseResult;
30787
- const controller = new AbortController();
30788
30826
  const deadline = Date.now() + REQUEST_TIMEOUT_MS;
30789
30827
  const remaining = () => Math.max(0, deadline - Date.now());
30790
- const timer = setTimeout(() => controller.abort(), remaining());
30791
- let response;
30792
- try {
30793
- response = await fetchImpl(`${baseUrl}/api/v1/chat/completions`, {
30794
- method: "POST",
30795
- // No task type unless the caller named one — an undeclared task adds no
30796
- // header at all, which is what keeps user-facing reflect() unlabelled.
30797
- headers: {
30798
- ...authHeaders,
30799
- ...taskTypeHeader(options.taskType),
30800
- "Content-Type": "application/json"
30801
- },
30802
- body: JSON.stringify({
30803
- model,
30804
- // Modern OpenAI field; the portal reads only `max_completion_tokens`
30805
- // (the deprecated `max_tokens` is silently ignored → falls back to the
30806
- // portal's default output cap and truncates the answer).
30807
- max_completion_tokens: maxTokens,
30808
- messages: [
30809
- { role: "system", content: systemPrompt },
30810
- { role: "user", content: userMessage }
30811
- ],
30812
- ...sendResponseFormat && {
30813
- response_format: {
30814
- type: "json_schema",
30815
- json_schema: { name: "reflect_output", schema: options.responseSchema }
30816
- }
30817
- }
30818
- }),
30819
- signal: controller.signal
30820
- });
30821
- } catch (err) {
30828
+ const endpoint = useResponsesTransport ? "/api/v1/responses" : "/api/v1/chat/completions";
30829
+ const sendOnce = async (useResponseFormat) => {
30830
+ const controller = new AbortController();
30831
+ const timer = setTimeout(() => controller.abort(), remaining());
30832
+ let response;
30833
+ try {
30834
+ response = await fetchImpl(`${baseUrl}${endpoint}`, {
30835
+ method: "POST",
30836
+ // No task type unless the caller named one — an undeclared task adds no
30837
+ // header at all, which is what keeps user-facing reflect() unlabelled.
30838
+ headers: {
30839
+ ...authHeaders,
30840
+ ...taskTypeHeader(options.taskType),
30841
+ "Content-Type": "application/json"
30842
+ },
30843
+ body: buildBody(useResponseFormat),
30844
+ signal: controller.signal
30845
+ });
30846
+ } catch (err) {
30847
+ clearTimeout(timer);
30848
+ log.warn("[memory/reflect] portal request failed", {
30849
+ err: err instanceof Error ? err.message : String(err),
30850
+ baseUrl
30851
+ });
30852
+ return { kind: "error" };
30853
+ }
30822
30854
  clearTimeout(timer);
30823
- log.warn("[memory/reflect] portal request failed", {
30824
- err: err instanceof Error ? err.message : String(err),
30825
- baseUrl
30855
+ if (!response.ok) {
30856
+ return { kind: "http", status: response.status, statusText: response.statusText };
30857
+ }
30858
+ const bodyTimer = setTimeout(() => controller.abort(), remaining());
30859
+ try {
30860
+ const body = await response.json();
30861
+ clearTimeout(bodyTimer);
30862
+ return { kind: "ok", body };
30863
+ } catch (err) {
30864
+ clearTimeout(bodyTimer);
30865
+ log.warn("[memory/reflect] failed to parse portal response body", {
30866
+ err: err instanceof Error ? err.message : String(err)
30867
+ });
30868
+ return { kind: "error" };
30869
+ }
30870
+ };
30871
+ let attempt = await sendOnce(sendResponseFormat);
30872
+ let schemaFallbackUsed = false;
30873
+ const budgetLeftMs = remaining();
30874
+ if (attempt.kind === "http" && sendResponseFormat && !SCHEMA_FALLBACK_SKIP_STATUSES.has(attempt.status) && budgetLeftMs >= MIN_RETRY_BUDGET_MS) {
30875
+ log.warn("[memory/reflect] response_format json_schema rejected; retrying schema-in-prompt", {
30876
+ status: attempt.status,
30877
+ statusText: attempt.statusText,
30878
+ model,
30879
+ taskType: options.taskType,
30880
+ budgetLeftMs
30826
30881
  });
30827
- return baseResult;
30882
+ schemaFallbackUsed = true;
30883
+ attempt = await sendOnce(false);
30828
30884
  }
30829
- clearTimeout(timer);
30830
- if (!response.ok) {
30885
+ if (attempt.kind === "error") return baseResult;
30886
+ if (attempt.kind === "http") {
30831
30887
  log.warn("[memory/reflect] portal returned non-OK", {
30832
- status: response.status,
30833
- statusText: response.statusText
30888
+ status: attempt.status,
30889
+ statusText: attempt.statusText,
30890
+ // Distinguishes "the fallback ran and still failed" from "never tried".
30891
+ schemaFallbackUsed
30834
30892
  });
30835
30893
  return baseResult;
30836
30894
  }
30837
- const bodyTimer = setTimeout(() => controller.abort(), remaining());
30838
- let body;
30839
- try {
30840
- body = await response.json();
30841
- } catch (err) {
30842
- clearTimeout(bodyTimer);
30843
- log.warn("[memory/reflect] failed to parse portal response body", {
30844
- err: err instanceof Error ? err.message : String(err)
30845
- });
30846
- return baseResult;
30847
- }
30848
- clearTimeout(bodyTimer);
30849
- return parseAnswer(body, baseResult, !!options.responseSchema);
30895
+ return parseAnswer(attempt.body, baseResult, !!options.responseSchema);
30850
30896
  }
30851
30897
  function parseAnswer(body, base, parseSchema) {
30852
30898
  if (typeof body !== "object" || body === null) return base;
30853
30899
  const obj = body;
30854
- const choices = obj.choices;
30855
- let text12 = "";
30856
- if (Array.isArray(choices) && choices.length > 0) {
30857
- const message = choices[0].message;
30858
- if (typeof message === "object" && message !== null) {
30859
- const content = message.content;
30860
- if (typeof content === "string") text12 = content;
30861
- }
30862
- }
30900
+ const text12 = extractCompletionContent(obj) ?? "";
30863
30901
  const usage = obj.usage;
30864
30902
  let structuredOutput;
30865
30903
  if (parseSchema && text12) {
@@ -30871,14 +30909,19 @@ function parseAnswer(body, base, parseSchema) {
30871
30909
  });
30872
30910
  }
30873
30911
  }
30912
+ const promptTokens = usage?.prompt_tokens ?? usage?.input_tokens ?? 0;
30913
+ const completionTokens = usage?.completion_tokens ?? usage?.output_tokens ?? 0;
30874
30914
  return {
30875
30915
  text: text12,
30876
30916
  ...structuredOutput !== void 0 && { structuredOutput },
30877
30917
  basedOn: base.basedOn,
30878
30918
  usage: {
30879
- promptTokens: usage?.prompt_tokens ?? 0,
30880
- completionTokens: usage?.completion_tokens ?? 0,
30881
- totalTokens: usage?.total_tokens ?? 0
30919
+ promptTokens,
30920
+ completionTokens,
30921
+ // Derived when absent: the Responses API reports the two component counts
30922
+ // and does not always send a total, which would otherwise report zero
30923
+ // spend for a call that plainly cost something.
30924
+ totalTokens: usage?.total_tokens ?? promptTokens + completionTokens
30882
30925
  }
30883
30926
  };
30884
30927
  }
@@ -141,23 +141,25 @@ import {
141
141
  updateVaultMemoryOp,
142
142
  upsertMessageOp,
143
143
  vaultMemoryToStored
144
- } from "./chunk-2QFK4L7Y.mjs";
144
+ } from "./chunk-K7H7ICPK.mjs";
145
145
  import {
146
146
  createStreamingDeAnonymizer,
147
147
  notifyConsolidationFallback,
148
148
  resolvePiiRedactor
149
- } from "./chunk-JPLFOSB2.mjs";
149
+ } from "./chunk-4LPSEROP.mjs";
150
150
  import {
151
151
  INTERNAL_FLOW_MARKER,
152
152
  callPortalJsonCompletion,
153
+ extractCompletionContent,
153
154
  extractJsonCandidate,
154
155
  getLogger,
156
+ requiresResponsesTransport,
155
157
  resolvePortalAuthHeaders,
156
158
  supportsResponseFormat,
157
159
  taskTypeHeader,
158
160
  validateEndpointOverride,
159
161
  withInternalFlowMarker
160
- } from "./chunk-H4G3DLDJ.mjs";
162
+ } from "./chunk-BSCEZOIW.mjs";
161
163
  import {
162
164
  BASE_URL,
163
165
  __commonJS,
@@ -5700,7 +5702,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
5700
5702
  content: m.content,
5701
5703
  similarity: m.similarity
5702
5704
  }));
5703
- const { consolidateMemory: doConsolidate } = await import("./consolidate-CMMDTZ37.mjs");
5705
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-MSAXASHL.mjs");
5704
5706
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
5705
5707
  if (decision.action === "create") return null;
5706
5708
  if (decision.action === "supersede" && decision.content) {
@@ -6924,6 +6926,9 @@ var DEFAULT_BASE_URL = BASE_URL;
6924
6926
  var DEFAULT_MODEL3 = "anthropic/claude-sonnet-4-6";
6925
6927
  var DEFAULT_MAX_TOKENS = 4096;
6926
6928
  var REQUEST_TIMEOUT_MS = 6e4;
6929
+ var MIN_RETRY_BUDGET_MS = 2e3;
6930
+ var MIN_RESPONSES_OUTPUT_TOKENS = 2048;
6931
+ var SCHEMA_FALLBACK_SKIP_STATUSES = /* @__PURE__ */ new Set([401, 403, 404, 408, 409, 413, 425, 429]);
6927
6932
  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.
6928
6933
 
6929
6934
  Rules:
@@ -6955,14 +6960,9 @@ ${m.content}`).join("\n\n");
6955
6960
  const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
6956
6961
  const model = options.llmModel ?? DEFAULT_MODEL3;
6957
6962
  const wantsStructured = !!options.responseSchema;
6958
- const sendResponseFormat = wantsStructured && supportsResponseFormat(model, "json_schema");
6963
+ const useResponsesTransport = requiresResponsesTransport(model);
6964
+ const sendResponseFormat = wantsStructured && !useResponsesTransport && supportsResponseFormat(model, "json_schema");
6959
6965
  const basePrompt = options.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
6960
- const systemPrompt = wantsStructured && !sendResponseFormat ? `${basePrompt}
6961
-
6962
- Respond with ONLY a single JSON object conforming to this JSON Schema, with no prose, comments, or code fences:
6963
- ${JSON.stringify(
6964
- options.responseSchema
6965
- )}` : basePrompt;
6966
6966
  const userMessage = [
6967
6967
  `Question:
6968
6968
  ${trimmed}`,
@@ -6972,85 +6972,121 @@ ${evidence}`
6972
6972
  ].join("\n\n");
6973
6973
  const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
6974
6974
  const fetchImpl = options.fetchFn ?? fetch;
6975
+ const buildBody = (useResponseFormat) => {
6976
+ const systemPrompt = wantsStructured && !useResponseFormat ? `${basePrompt}
6977
+
6978
+ Respond with ONLY a single JSON object conforming to this JSON Schema, with no prose, comments, or code fences:
6979
+ ${JSON.stringify(
6980
+ options.responseSchema
6981
+ )}` : basePrompt;
6982
+ const messages = [
6983
+ { role: "system", content: systemPrompt },
6984
+ { role: "user", content: userMessage }
6985
+ ];
6986
+ if (useResponsesTransport) {
6987
+ return JSON.stringify({
6988
+ model,
6989
+ // Same system+user pair under the Responses-API field names: `input`
6990
+ // for the turns, `max_output_tokens` for the cap. Sending the
6991
+ // chat-completions spelling here is silently ignored, which caps the
6992
+ // answer at the portal default.
6993
+ input: messages,
6994
+ max_output_tokens: Math.max(maxTokens, MIN_RESPONSES_OUTPUT_TOKENS)
6995
+ });
6996
+ }
6997
+ return JSON.stringify({
6998
+ model,
6999
+ // Modern OpenAI field; the portal reads only `max_completion_tokens`
7000
+ // (the deprecated `max_tokens` is silently ignored → falls back to the
7001
+ // portal's default output cap and truncates the answer).
7002
+ max_completion_tokens: maxTokens,
7003
+ messages,
7004
+ ...useResponseFormat && {
7005
+ response_format: {
7006
+ type: "json_schema",
7007
+ json_schema: { name: "reflect_output", schema: options.responseSchema }
7008
+ }
7009
+ }
7010
+ });
7011
+ };
6975
7012
  const log = getLogger();
6976
7013
  const authHeaders = await resolvePortalAuthHeaders(options, "memory/reflect");
6977
7014
  if (authHeaders === null) return baseResult;
6978
- const controller = new AbortController();
6979
7015
  const deadline = Date.now() + REQUEST_TIMEOUT_MS;
6980
7016
  const remaining = () => Math.max(0, deadline - Date.now());
6981
- const timer = setTimeout(() => controller.abort(), remaining());
6982
- let response;
6983
- try {
6984
- response = await fetchImpl(`${baseUrl}/api/v1/chat/completions`, {
6985
- method: "POST",
6986
- // No task type unless the caller named one — an undeclared task adds no
6987
- // header at all, which is what keeps user-facing reflect() unlabelled.
6988
- headers: {
6989
- ...authHeaders,
6990
- ...taskTypeHeader(options.taskType),
6991
- "Content-Type": "application/json"
6992
- },
6993
- body: JSON.stringify({
6994
- model,
6995
- // Modern OpenAI field; the portal reads only `max_completion_tokens`
6996
- // (the deprecated `max_tokens` is silently ignored → falls back to the
6997
- // portal's default output cap and truncates the answer).
6998
- max_completion_tokens: maxTokens,
6999
- messages: [
7000
- { role: "system", content: systemPrompt },
7001
- { role: "user", content: userMessage }
7002
- ],
7003
- ...sendResponseFormat && {
7004
- response_format: {
7005
- type: "json_schema",
7006
- json_schema: { name: "reflect_output", schema: options.responseSchema }
7007
- }
7008
- }
7009
- }),
7010
- signal: controller.signal
7011
- });
7012
- } catch (err) {
7017
+ const endpoint = useResponsesTransport ? "/api/v1/responses" : "/api/v1/chat/completions";
7018
+ const sendOnce = async (useResponseFormat) => {
7019
+ const controller = new AbortController();
7020
+ const timer = setTimeout(() => controller.abort(), remaining());
7021
+ let response;
7022
+ try {
7023
+ response = await fetchImpl(`${baseUrl}${endpoint}`, {
7024
+ method: "POST",
7025
+ // No task type unless the caller named one — an undeclared task adds no
7026
+ // header at all, which is what keeps user-facing reflect() unlabelled.
7027
+ headers: {
7028
+ ...authHeaders,
7029
+ ...taskTypeHeader(options.taskType),
7030
+ "Content-Type": "application/json"
7031
+ },
7032
+ body: buildBody(useResponseFormat),
7033
+ signal: controller.signal
7034
+ });
7035
+ } catch (err) {
7036
+ clearTimeout(timer);
7037
+ log.warn("[memory/reflect] portal request failed", {
7038
+ err: err instanceof Error ? err.message : String(err),
7039
+ baseUrl
7040
+ });
7041
+ return { kind: "error" };
7042
+ }
7013
7043
  clearTimeout(timer);
7014
- log.warn("[memory/reflect] portal request failed", {
7015
- err: err instanceof Error ? err.message : String(err),
7016
- baseUrl
7044
+ if (!response.ok) {
7045
+ return { kind: "http", status: response.status, statusText: response.statusText };
7046
+ }
7047
+ const bodyTimer = setTimeout(() => controller.abort(), remaining());
7048
+ try {
7049
+ const body = await response.json();
7050
+ clearTimeout(bodyTimer);
7051
+ return { kind: "ok", body };
7052
+ } catch (err) {
7053
+ clearTimeout(bodyTimer);
7054
+ log.warn("[memory/reflect] failed to parse portal response body", {
7055
+ err: err instanceof Error ? err.message : String(err)
7056
+ });
7057
+ return { kind: "error" };
7058
+ }
7059
+ };
7060
+ let attempt = await sendOnce(sendResponseFormat);
7061
+ let schemaFallbackUsed = false;
7062
+ const budgetLeftMs = remaining();
7063
+ if (attempt.kind === "http" && sendResponseFormat && !SCHEMA_FALLBACK_SKIP_STATUSES.has(attempt.status) && budgetLeftMs >= MIN_RETRY_BUDGET_MS) {
7064
+ log.warn("[memory/reflect] response_format json_schema rejected; retrying schema-in-prompt", {
7065
+ status: attempt.status,
7066
+ statusText: attempt.statusText,
7067
+ model,
7068
+ taskType: options.taskType,
7069
+ budgetLeftMs
7017
7070
  });
7018
- return baseResult;
7071
+ schemaFallbackUsed = true;
7072
+ attempt = await sendOnce(false);
7019
7073
  }
7020
- clearTimeout(timer);
7021
- if (!response.ok) {
7074
+ if (attempt.kind === "error") return baseResult;
7075
+ if (attempt.kind === "http") {
7022
7076
  log.warn("[memory/reflect] portal returned non-OK", {
7023
- status: response.status,
7024
- statusText: response.statusText
7077
+ status: attempt.status,
7078
+ statusText: attempt.statusText,
7079
+ // Distinguishes "the fallback ran and still failed" from "never tried".
7080
+ schemaFallbackUsed
7025
7081
  });
7026
7082
  return baseResult;
7027
7083
  }
7028
- const bodyTimer = setTimeout(() => controller.abort(), remaining());
7029
- let body;
7030
- try {
7031
- body = await response.json();
7032
- } catch (err) {
7033
- clearTimeout(bodyTimer);
7034
- log.warn("[memory/reflect] failed to parse portal response body", {
7035
- err: err instanceof Error ? err.message : String(err)
7036
- });
7037
- return baseResult;
7038
- }
7039
- clearTimeout(bodyTimer);
7040
- return parseAnswer(body, baseResult, !!options.responseSchema);
7084
+ return parseAnswer(attempt.body, baseResult, !!options.responseSchema);
7041
7085
  }
7042
7086
  function parseAnswer(body, base, parseSchema) {
7043
7087
  if (typeof body !== "object" || body === null) return base;
7044
7088
  const obj = body;
7045
- const choices = obj.choices;
7046
- let text12 = "";
7047
- if (Array.isArray(choices) && choices.length > 0) {
7048
- const message = choices[0].message;
7049
- if (typeof message === "object" && message !== null) {
7050
- const content = message.content;
7051
- if (typeof content === "string") text12 = content;
7052
- }
7053
- }
7089
+ const text12 = extractCompletionContent(obj) ?? "";
7054
7090
  const usage = obj.usage;
7055
7091
  let structuredOutput;
7056
7092
  if (parseSchema && text12) {
@@ -7062,14 +7098,19 @@ function parseAnswer(body, base, parseSchema) {
7062
7098
  });
7063
7099
  }
7064
7100
  }
7101
+ const promptTokens = usage?.prompt_tokens ?? usage?.input_tokens ?? 0;
7102
+ const completionTokens = usage?.completion_tokens ?? usage?.output_tokens ?? 0;
7065
7103
  return {
7066
7104
  text: text12,
7067
7105
  ...structuredOutput !== void 0 && { structuredOutput },
7068
7106
  basedOn: base.basedOn,
7069
7107
  usage: {
7070
- promptTokens: usage?.prompt_tokens ?? 0,
7071
- completionTokens: usage?.completion_tokens ?? 0,
7072
- totalTokens: usage?.total_tokens ?? 0
7108
+ promptTokens,
7109
+ completionTokens,
7110
+ // Derived when absent: the Responses API reports the two component counts
7111
+ // and does not always send a total, which would otherwise report zero
7112
+ // spend for a call that plainly cost something.
7113
+ totalTokens: usage?.total_tokens ?? promptTokens + completionTokens
7073
7114
  }
7074
7115
  };
7075
7116
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-2QFK4L7Y.mjs";
4
- import "./chunk-H4G3DLDJ.mjs";
3
+ } from "./chunk-K7H7ICPK.mjs";
4
+ import "./chunk-BSCEZOIW.mjs";
5
5
  import "./chunk-3RPGXXPZ.mjs";
6
6
  export {
7
7
  recall
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "6.15.0",
3
+ "version": "6.15.2",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",