@anuma/sdk 6.30.1 → 6.31.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.
@@ -2138,6 +2138,21 @@ type ToolConfig = LlmapiChatCompletionTool & {
2138
2138
  * execution fails, allowing the model to retry.
2139
2139
  */
2140
2140
  removeAfterExecution?: boolean;
2141
+ /**
2142
+ * Remove this tool for the rest of the turn once ANY tool returns a
2143
+ * successful result this predicate accepts — its own or another's. Errors
2144
+ * never count, as for `removeAfterExecution`.
2145
+ *
2146
+ * Only results of tools executed on this client are offered; tools the
2147
+ * portal runs server-side never reach the predicate. A predicate that throws
2148
+ * is treated as not matching.
2149
+ *
2150
+ * For a family of tools that has to leave together: when one of them
2151
+ * declines an out-of-scope request, the model otherwise works through its
2152
+ * siblings round after round until the round cap. Give every member the same
2153
+ * predicate and the first refusal removes the whole family.
2154
+ */
2155
+ removeAfterResult?: (result: unknown) => boolean;
2141
2156
  /**
2142
2157
  * Timeout in milliseconds for this tool's executor.
2143
2158
  * Overrides the default 30-second timeout.
@@ -4995,6 +5010,7 @@ type MessageFeedback = "like" | "dislike" | null;
4995
5010
  * - `tool_result`: the hidden `[Tool Execution Results]` row built from a
4996
5011
  * turn's auto-executed tool results. Skipped by the embedding sweep (see
4997
5012
  * `memoryEngine/embeddings`) AND hidden from the transcript.
5013
+ * - `message`: an ordinary message with an index after explicit repair.
4998
5014
  * - `chunks_discarded`: an ordinary, still-rendered message whose chunk vectors
4999
5015
  * were built over `enc:v3:` ciphertext (sdk#864) and have been discarded
5000
5016
  * rather than re-embedded at the user's own expense (client#5618). Skipped by
@@ -5007,7 +5023,7 @@ type MessageFeedback = "like" | "dislike" | null;
5007
5023
  * encrypted flag would be unreadable exactly where it matters and the skip
5008
5024
  * would fail open.
5009
5025
  */
5010
- type MessageOrigin = "tool_result" | "chunks_discarded";
5026
+ type MessageOrigin = "tool_result" | "chunks_discarded" | "message";
5011
5027
  /**
5012
5028
  * Metadata for files attached to messages.
5013
5029
  *
@@ -6529,7 +6545,12 @@ type PortalLlmFailureReason =
6529
6545
  /** Content parsed to a literal `null`, which is never a valid response. */
6530
6546
  | "null-completion"
6531
6547
  /** `totalTimeoutMs` was spent before the next attempt could run. */
6532
- | "time-budget-exhausted";
6548
+ | "time-budget-exhausted"
6549
+ /**
6550
+ * The portal's moderation gate refused the request. Terminal: the same input
6551
+ * is flagged again on every retry. See {@link isModerationResponse}.
6552
+ */
6553
+ | "content-flagged";
6533
6554
  /**
6534
6555
  * One attempt of a {@link callPortalJsonCompletion} call, reported to
6535
6556
  * `onAttempt` as it settles — success included. The wire-level diagnostic the
@@ -7766,7 +7787,9 @@ interface ExtractFactsOptions extends PortalLlmAuth {
7766
7787
  * Optional per-call request path override, forwarded to
7767
7788
  * {@link callPortalJsonCompletion}. When set, the extraction call POSTs to
7768
7789
  * `baseUrl + endpointOverride` instead of the default
7769
- * `/api/v1/chat/completions` — path only, body unchanged. Lets callers route
7790
+ * `/api/v1/chat/completions`. The body follows the transport, not the path: a
7791
+ * model that needs the Responses API is moved from a `.../chat/completions`
7792
+ * override to its sibling `.../responses` with a Responses-shaped body. Lets callers route
7770
7793
  * this internal-utility pass to a dedicated endpoint. Invalid values throw at
7771
7794
  * call time (see {@link validateEndpointOverride}).
7772
7795
  *
@@ -8706,7 +8729,8 @@ declare function stampTopicsExtractedAtOp(ctx: VaultMemoryOperationsContext, mem
8706
8729
  * `topics_user_managed` in particular is left exactly as it arrived, so the
8707
8730
  * autotagger stays off a curated memory whose links this just restored.
8708
8731
  *
8709
- * Skips deleted, foreign-user, and record-less rows. Returns the ids relinked.
8732
+ * Skips deleted rows, foreign-user rows, and rows with null topics.
8733
+ * Empty topics remove stale links. Returns the relinked IDs.
8710
8734
  */
8711
8735
  declare function relinkMemoryTopicsOp(ctx: VaultMemoryOperationsContext, memoryIds: readonly string[]): Promise<string[]>;
8712
8736
  /**
@@ -10414,7 +10438,9 @@ interface TopicExtractOptions extends PortalLlmAuth {
10414
10438
  * Optional per-call request path override, forwarded to
10415
10439
  * {@link callPortalJsonCompletion}. When set, topic extraction POSTs to
10416
10440
  * `baseUrl + endpointOverride` instead of the default
10417
- * `/api/v1/chat/completions` — path only, body unchanged. Lets callers route
10441
+ * `/api/v1/chat/completions`. The body follows the transport, not the path: a
10442
+ * model that needs the Responses API is moved from a `.../chat/completions`
10443
+ * override to its sibling `.../responses` with a Responses-shaped body. Lets callers route
10418
10444
  * this internal-utility pass to a dedicated endpoint. Invalid values throw at
10419
10445
  * call time (see {@link validateEndpointOverride}).
10420
10446
  */
@@ -11040,14 +11066,10 @@ declare function generateEmbeddings(texts: string[], options: EmbeddingOptions):
11040
11066
  * re-index. Off by default, so no background pass can spend a user's credits
11041
11067
  * without being asked to.
11042
11068
  *
11043
- * That flag opens the gate for one call and nothing more. It does not heal the
11044
- * row: a message that re-indexes successfully still carries `chunks_discarded`
11045
- * afterwards, so the next pass skips it again unless that pass sets the flag
11046
- * too. An embedding-model migration is where this bites. It reaches this gate
11047
- * before it can do anything, so the repaired rows stay on the old model, and
11048
- * `searchChunksOp` already drops stale-model vectors — they go quiet with
11049
- * nothing telling them apart from rows that were never repaired. Clearing the
11050
- * marker on a successful re-index is the real fix, tracked in #879.
11069
+ * A successful repair replaces the marker with `message` in the index write.
11070
+ * The new marker preserves ordinary message provenance during chat replay.
11071
+ * Later model migrations can then include the repaired row without this flag.
11072
+ * Failed embedding requests and empty index data do not clear the marker.
11051
11073
  *
11052
11074
  * `satisfies MessageOrigin` so the constant and the union cannot drift apart
11053
11075
  * silently — the column's type is the union, and a typo here would otherwise
@@ -11084,9 +11106,8 @@ declare function embedAllMessages(ctx: StorageOperationsContext, options: Embedd
11084
11106
  * to an explicit user action, never to a background pass. Opens that marker
11085
11107
  * only — `tool_result` rows stay excluded.
11086
11108
  *
11087
- * Per call, not a state change: a row that re-indexes successfully keeps its
11088
- * marker, so a later pass that omits this flag skips it again. See
11089
- * {@link CHUNKS_DISCARDED_ORIGIN}.
11109
+ * A successful repair clears the marker. Later model migrations include
11110
+ * the repaired row without this flag. See {@link CHUNKS_DISCARDED_ORIGIN}.
11090
11111
  */
11091
11112
  reembedDiscarded?: boolean;
11092
11113
  }): Promise<number>;
@@ -11144,9 +11165,8 @@ declare function chunkAndEmbedAllMessages(ctx: StorageOperationsContext, options
11144
11165
  * chunks nor vector, so it never reaches that check and falls straight to the
11145
11166
  * origin gate.
11146
11167
  *
11147
- * Per call, not a state change: a row that re-indexes successfully keeps its
11148
- * marker, so a later pass that omits this flag skips it again. See
11149
- * {@link CHUNKS_DISCARDED_ORIGIN}.
11168
+ * A successful repair clears the marker. Later model migrations include
11169
+ * the repaired row without this flag. See {@link CHUNKS_DISCARDED_ORIGIN}.
11150
11170
  */
11151
11171
  reembedDiscarded?: boolean;
11152
11172
  }): Promise<number>;
@@ -18,7 +18,7 @@ import {
18
18
  taskTypeHeader,
19
19
  validateEndpointOverride,
20
20
  withInternalFlowMarker
21
- } from "./chunk-LBIKGBXE.mjs";
21
+ } from "./chunk-XNQRJBIH.mjs";
22
22
  import {
23
23
  BASE_URL,
24
24
  MCP_R2_DOMAIN,
@@ -2316,6 +2316,9 @@ async function updateMessageEmbeddingOp(ctx, uniqueId, vector, embeddingModel) {
2316
2316
  await ctx.database.write(async () => {
2317
2317
  await message.update((msg) => {
2318
2318
  msg._setRaw("vector", JSON.stringify(vector));
2319
+ if (msg.origin === "chunks_discarded" && vector.length > 0) {
2320
+ msg._setRaw("origin", "message");
2321
+ }
2319
2322
  msg._setRaw("embedding_model", embeddingModel);
2320
2323
  });
2321
2324
  });
@@ -2332,6 +2335,9 @@ async function updateMessageChunksOp(ctx, uniqueId, chunks, embeddingModel) {
2332
2335
  await ctx.database.write(async () => {
2333
2336
  await message.update((msg) => {
2334
2337
  msg._setRaw("chunks", JSON.stringify(storedChunks));
2338
+ if (msg.origin === "chunks_discarded" && storedChunks.length > 0 && storedChunks.every((chunk) => chunk.vector.length > 0)) {
2339
+ msg._setRaw("origin", "message");
2340
+ }
2335
2341
  msg._setRaw("embedding_model", embeddingModel);
2336
2342
  });
2337
2343
  });
@@ -4474,6 +4480,9 @@ function clientToolToResponsesFormat(tool) {
4474
4480
  ...toolConfig.removeAfterExecution !== void 0 && {
4475
4481
  removeAfterExecution: toolConfig.removeAfterExecution
4476
4482
  },
4483
+ ...toolConfig.removeAfterResult !== void 0 && {
4484
+ removeAfterResult: toolConfig.removeAfterResult
4485
+ },
4477
4486
  ...toolConfig.executorTimeout !== void 0 && {
4478
4487
  executorTimeout: toolConfig.executorTimeout
4479
4488
  },
@@ -4507,6 +4516,9 @@ function clientToolToCompletionsFormat(tool) {
4507
4516
  ...toolConfig.removeAfterExecution !== void 0 && {
4508
4517
  removeAfterExecution: toolConfig.removeAfterExecution
4509
4518
  },
4519
+ ...toolConfig.removeAfterResult !== void 0 && {
4520
+ removeAfterResult: toolConfig.removeAfterResult
4521
+ },
4510
4522
  ...toolConfig.executorTimeout !== void 0 && {
4511
4523
  executorTimeout: toolConfig.executorTimeout
4512
4524
  },
@@ -5485,6 +5497,7 @@ function toolsToApiFormat(tools, apiType) {
5485
5497
  executor: _executor,
5486
5498
  skipContinuation: _skipContinuation,
5487
5499
  removeAfterExecution: _removeAfterExecution,
5500
+ removeAfterResult: _removeAfterResult,
5488
5501
  executorTimeout: _executorTimeout,
5489
5502
  dependsOn: _dependsOn,
5490
5503
  deAnonymizeArgs: _deAnonymizeArgs,
@@ -7929,9 +7942,11 @@ Executing tool: ${toolInfo}
7929
7942
  }
7930
7943
  if (tools && apiTools) {
7931
7944
  const successfullyExecutedNames = /* @__PURE__ */ new Set();
7945
+ const successfulResults = [];
7932
7946
  for (const r of executionResults) {
7933
7947
  if (!r.error && !isToolErrorResult(r.result) && "name" in r && r.name) {
7934
7948
  successfullyExecutedNames.add(r.name);
7949
+ successfulResults.push(r.result);
7935
7950
  }
7936
7951
  }
7937
7952
  if (successfullyExecutedNames.size > 0) {
@@ -7940,7 +7955,15 @@ Executing tool: ${toolInfo}
7940
7955
  const tc = t;
7941
7956
  const func = tc.function;
7942
7957
  const toolName = typeof func?.name === "string" ? func.name : typeof tc.name === "string" ? tc.name : void 0;
7943
- if (tc.removeAfterExecution === true && toolName && successfullyExecutedNames.has(toolName)) {
7958
+ if (!toolName) continue;
7959
+ const removeAfterResult = tc.removeAfterResult;
7960
+ if (tc.removeAfterExecution === true && successfullyExecutedNames.has(toolName) || typeof removeAfterResult === "function" && successfulResults.some((result) => {
7961
+ try {
7962
+ return removeAfterResult(result);
7963
+ } catch {
7964
+ return false;
7965
+ }
7966
+ })) {
7944
7967
  toolsToRemove.add(toolName);
7945
7968
  }
7946
7969
  }
@@ -13030,7 +13053,7 @@ async function getMemoriesNeedingTopicExtractionOp(ctx, options) {
13030
13053
  const id = raw.id;
13031
13054
  const topics = parseTopics(raw.topics);
13032
13055
  const linked = linkedNames.get(id);
13033
- if (topics !== null && topics.length > 0 && linksDivergeFromTopics(topics, linked ?? /* @__PURE__ */ new Set())) {
13056
+ if (topics !== null && linksDivergeFromTopics(topics, linked ?? /* @__PURE__ */ new Set())) {
13034
13057
  topicsToRelinkAll.push(id);
13035
13058
  continue;
13036
13059
  }
@@ -13124,18 +13147,20 @@ async function relinkMemoryTopicsOp(ctx, memoryIds) {
13124
13147
  }
13125
13148
  const relinked = [];
13126
13149
  for (const id of Array.from(new Set(memoryIds))) {
13127
- let record;
13128
13150
  try {
13129
- record = await ctx.vaultMemoryCollection.find(id);
13130
- } catch {
13131
- continue;
13132
- }
13133
- if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) continue;
13134
- const topics = parseTopics(record.topics);
13135
- if (topics === null || topics.length === 0) continue;
13136
- try {
13137
- await relinkMemoryEntitiesFromTopicsOp(entityCtx, id, topics);
13138
- relinked.push(id);
13151
+ await ctx.database.write(async (writer) => {
13152
+ let record;
13153
+ try {
13154
+ record = await ctx.vaultMemoryCollection.find(id);
13155
+ } catch {
13156
+ return;
13157
+ }
13158
+ if (record.isDeleted || !isOwnedByCtxUser(ctx, record)) return;
13159
+ const topics = parseTopics(record.topics);
13160
+ if (topics === null) return;
13161
+ await writer.callWriter(() => relinkMemoryEntitiesFromTopicsOp(entityCtx, id, topics));
13162
+ relinked.push(id);
13163
+ });
13139
13164
  } catch (err) {
13140
13165
  getLogger().warn("[memory/topics] relink failed", err);
13141
13166
  }
@@ -16054,7 +16079,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
16054
16079
  content: m.content,
16055
16080
  similarity: m.similarity
16056
16081
  }));
16057
- const { consolidateMemory: doConsolidate } = await import("./consolidate-2NLZAEWR.mjs");
16082
+ const { consolidateMemory: doConsolidate } = await import("./consolidate-5DVINCUE.mjs");
16058
16083
  const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
16059
16084
  if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
16060
16085
  if (decision.action === "supersede" && decision.content) {
@@ -19701,6 +19726,28 @@ var BatchFailureError = class extends Error {
19701
19726
  this.terminal = terminal;
19702
19727
  }
19703
19728
  };
19729
+ var FlaggedBatchError = class extends Error {
19730
+ };
19731
+ function extracted(result) {
19732
+ return result.failedCount === 0 && result.outcome !== "empty-after-retry";
19733
+ }
19734
+ function batchError(result) {
19735
+ const failure = result.failure;
19736
+ if (failure?.reason === "http-terminal") {
19737
+ const status = failure.httpStatus;
19738
+ if (status !== void 0 && REQUEST_REJECTED_STATUSES.has(status))
19739
+ return new BatchFailureError(
19740
+ `Extraction rejected the batch (HTTP ${status}); not retried this session`,
19741
+ true
19742
+ );
19743
+ return new AccountFailureError(
19744
+ `Extraction refused for the account (HTTP ${status ?? "unknown"}); retried next session`
19745
+ );
19746
+ }
19747
+ if (result.failedCount > 0 || failure !== void 0 && CONTENT_FAILURE_REASONS.has(failure.reason))
19748
+ return new BatchFailureError("Extraction batch incomplete; retained for retry", false);
19749
+ return new Error("Extraction batch incomplete; retained for retry");
19750
+ }
19704
19751
  var AccountFailureError = class extends Error {
19705
19752
  };
19706
19753
  var LockedSourcesError = class extends Error {
@@ -19709,6 +19756,16 @@ var LockedSourcesError = class extends Error {
19709
19756
  this.persistentIds = persistentIds;
19710
19757
  }
19711
19758
  };
19759
+ function splitTurns(messages) {
19760
+ const turns = [];
19761
+ for (const message of messages) {
19762
+ const last = turns[turns.length - 1];
19763
+ if (!last || message.role === "user" && last.some((m) => m.role === "user"))
19764
+ turns.push([message]);
19765
+ else last.push(message);
19766
+ }
19767
+ return turns;
19768
+ }
19712
19769
  function idsOf(job) {
19713
19770
  return JSON.parse(String(job._getRaw("message_ids")));
19714
19771
  }
@@ -19859,6 +19916,10 @@ function createDurableAutoExtractor(options) {
19859
19916
  skippedThisSession.add(job.id);
19860
19917
  return false;
19861
19918
  }
19919
+ if (error instanceof FlaggedBatchError) {
19920
+ await acknowledge(job, ids, loaded, true);
19921
+ return true;
19922
+ }
19862
19923
  const counted = error instanceof BatchFailureError || error instanceof LockedSourcesError && error.persistentIds.length > 0;
19863
19924
  if (!counted) return false;
19864
19925
  const prior = sessionFailures.get(job.id);
@@ -19985,37 +20046,50 @@ function createDurableAutoExtractor(options) {
19985
20046
  const messages = loaded.flatMap(
19986
20047
  (m) => m && m.conversationId === conversationId && (m.role === "user" || m.role === "assistant") ? [{ id: m.uniqueId, role: m.role, content: m.content }] : []
19987
20048
  );
19988
- let success = messages.length === 0;
19989
- let result;
19990
- if (messages.length) {
19991
- result = await extractBatch(
19992
- messages,
20049
+ const jobScope = currentScope() === "private" ? "private" : String(job._getRaw("scope"));
20050
+ const jobFolderId = job._getRaw("folder_id") ?? null;
20051
+ const runExtraction = async (batch, batchIds) => {
20052
+ const result = await extractBatch(
20053
+ batch,
19993
20054
  conversationId,
19994
- ids,
19995
- currentScope() === "private" ? "private" : String(job._getRaw("scope")),
19996
- job._getRaw("folder_id") ?? null
20055
+ batchIds,
20056
+ jobScope,
20057
+ jobFolderId
19997
20058
  );
19998
- success = result.failedCount === 0 && result.outcome !== "empty-after-retry";
19999
20059
  options.onTurnComplete?.(result);
20000
- }
20001
- if (!success) {
20002
- const failure = result?.failure;
20003
- if (failure?.reason === "http-terminal") {
20004
- const status = failure.httpStatus;
20005
- if (status !== void 0 && REQUEST_REJECTED_STATUSES.has(status))
20006
- throw new BatchFailureError(
20007
- `Extraction rejected the batch (HTTP ${status}); not retried this session`,
20008
- true
20060
+ return result;
20061
+ };
20062
+ const flaggedIds = /* @__PURE__ */ new Set();
20063
+ if (messages.length) {
20064
+ const result = await runExtraction(messages, ids);
20065
+ if (result.failure?.reason === "content-flagged") {
20066
+ const turns = splitTurns(messages);
20067
+ if (turns.length < 2)
20068
+ throw new FlaggedBatchError("Extraction batch refused by moderation; abandoned");
20069
+ for (const turn of turns) {
20070
+ const turnResult = await runExtraction(
20071
+ turn,
20072
+ turn.map((m) => m.id)
20009
20073
  );
20010
- throw new AccountFailureError(
20011
- `Extraction refused for the account (HTTP ${status ?? "unknown"}); retried next session`
20012
- );
20013
- }
20014
- if ((result?.failedCount ?? 0) > 0 || failure !== void 0 && CONTENT_FAILURE_REASONS.has(failure.reason))
20015
- throw new BatchFailureError("Extraction batch incomplete; retained for retry", false);
20016
- throw new Error("Extraction batch incomplete; retained for retry");
20074
+ if (turnResult.failure?.reason === "content-flagged")
20075
+ for (const m of turn) flaggedIds.add(m.id);
20076
+ else if (!extracted(turnResult)) throw batchError(turnResult);
20077
+ }
20078
+ if (flaggedIds.size)
20079
+ reportError(
20080
+ new Error(
20081
+ `Dropped ${flaggedIds.size} source id(s) of an extraction batch refused by moderation`
20082
+ ),
20083
+ conversationId
20084
+ );
20085
+ } else if (!extracted(result)) throw batchError(result);
20017
20086
  }
20018
- await acknowledge(job, ids, loaded, false);
20087
+ await acknowledge(
20088
+ job,
20089
+ ids,
20090
+ loaded,
20091
+ ids.slice(-2).some((id) => flaggedIds.has(id))
20092
+ );
20019
20093
  scheduleAfterSuccess = true;
20020
20094
  } catch (error) {
20021
20095
  attempts.set(job.id, attempted + 1);
@@ -23244,10 +23318,10 @@ function useChatStorage(options) {
23244
23318
  let skipStorageEmbeddings = null;
23245
23319
  let skipStorageEmbeddingsFailed = false;
23246
23320
  if (needsEmbeddings2 && getToken) {
23247
- const extracted2 = extractUserMessageFromMessages(messages);
23321
+ const extracted3 = extractUserMessageFromMessages(messages);
23248
23322
  const messageContent = resolveStoredUserContent(
23249
23323
  storedUserContent,
23250
- extracted2?.content ?? ""
23324
+ extracted3?.content ?? ""
23251
23325
  );
23252
23326
  if (messageContent.length >= MIN_CONTENT_LENGTH_FOR_TOOLS) {
23253
23327
  const embeddingOptions = {
@@ -23404,15 +23478,15 @@ function useChatStorage(options) {
23404
23478
  skipped: true
23405
23479
  };
23406
23480
  }
23407
- const extracted = extractUserMessageFromMessages(messages);
23408
- if (!extracted || !extracted.content && !extracted.files?.length) {
23481
+ const extracted2 = extractUserMessageFromMessages(messages);
23482
+ if (!extracted2 || !extracted2.content && !extracted2.files?.length) {
23409
23483
  return {
23410
23484
  data: null,
23411
23485
  error: "No user message found in messages array"
23412
23486
  };
23413
23487
  }
23414
- const contentForStorage = resolveStoredUserContent(storedUserContent, extracted.content);
23415
- const filesForStorage = files ?? extracted.files;
23488
+ const contentForStorage = resolveStoredUserContent(storedUserContent, extracted2.content);
23489
+ const filesForStorage = files ?? extracted2.files;
23416
23490
  let fileContextForRequest;
23417
23491
  let fileContextIsCurrentTurn = false;
23418
23492
  let preprocessedFileIds = [];
@@ -23871,9 +23945,9 @@ function useChatStorage(options) {
23871
23945
  if (result.error || !result.data) {
23872
23946
  const abortedResult = result;
23873
23947
  if (abortedResult.error === "Request aborted") {
23874
- const extracted2 = abortedResult.data ? extractAssistantText(abortedResult.data) : { content: "", thinking: void 0 };
23875
- const assistantContent2 = extracted2.content;
23876
- const abortedThinkingContent = extracted2.thinking;
23948
+ const extracted3 = abortedResult.data ? extractAssistantText(abortedResult.data) : { content: "", thinking: void 0 };
23949
+ const assistantContent2 = extracted3.content;
23950
+ const abortedThinkingContent = extracted3.thinking;
23877
23951
  const responseModel = abortedResult.data?.model || model || "";
23878
23952
  let storedAssistantMessage2;
23879
23953
  try {
@@ -35,9 +35,25 @@ var consoleLogger = {
35
35
  warn: console.warn.bind(console),
36
36
  error: console.error.bind(console)
37
37
  };
38
- var currentLogger = consoleLogger;
38
+ var LOGGER_STATE_KEY = /* @__PURE__ */ Symbol.for("@anuma/sdk/logger/v1");
39
+ var loggerGlobal = globalThis;
40
+ var fallbackLoggerState = { logger: consoleLogger };
41
+ var fallbackWarned = false;
42
+ function getLoggerState() {
43
+ try {
44
+ return loggerGlobal[LOGGER_STATE_KEY] ?? (loggerGlobal[LOGGER_STATE_KEY] = fallbackLoggerState);
45
+ } catch {
46
+ if (!fallbackWarned) {
47
+ fallbackWarned = true;
48
+ fallbackLoggerState.logger.warn(
49
+ "@anuma/sdk: the global object rejects new properties. This entrypoint keeps its own logger, so setLogger does not reach other entrypoints."
50
+ );
51
+ }
52
+ return fallbackLoggerState;
53
+ }
54
+ }
39
55
  function getLogger() {
40
- return currentLogger;
56
+ return getLoggerState().logger;
41
57
  }
42
58
 
43
59
  // src/lib/taskType.ts
@@ -74,13 +90,21 @@ function responsesExtra(extra) {
74
90
  }
75
91
  return out;
76
92
  }
93
+ function splitEndpoint(endpoint) {
94
+ const cut = endpoint.search(/[?#]/);
95
+ const rawPath = cut === -1 ? endpoint : endpoint.slice(0, cut);
96
+ return {
97
+ path: rawPath.replace(/\/+$/, ""),
98
+ rest: cut === -1 ? "" : endpoint.slice(cut)
99
+ };
100
+ }
77
101
  var FOREIGN_ENDPOINT_SUFFIX = {
78
102
  chat: "/responses",
79
103
  responses: "/chat/completions"
80
104
  };
81
105
  function assertTransportMatchesEndpoint(transport, endpoint) {
82
106
  const foreign = FOREIGN_ENDPOINT_SUFFIX[transport];
83
- if (!endpoint.endsWith(foreign)) return;
107
+ if (!splitEndpoint(endpoint).path.endsWith(foreign)) return;
84
108
  throw new Error(
85
109
  `endpointOverride "${endpoint}" is the other transport's endpoint, but transport is "${transport}". The request body is built for the transport, so this sends the wrong shape and 400s without retry.`
86
110
  );
@@ -126,14 +150,20 @@ async function resolvePortalAuthHeaders(auth, tag) {
126
150
  }
127
151
  async function callPortalJsonCompletion(req) {
128
152
  const log = getLogger();
129
- let endpoint = req.transport === "responses" ? "/api/v1/responses" : "/api/v1/chat/completions";
153
+ const transport = req.transport ?? (requiresResponsesTransport(req.model) ? "responses" : "chat");
154
+ const autoUpgraded = req.transport === void 0 && transport === "responses";
155
+ let endpoint = transport === "responses" ? "/api/v1/responses" : "/api/v1/chat/completions";
130
156
  if (req.endpointOverride !== void 0) {
131
157
  const overrideValidation = validateEndpointOverride(req.endpointOverride);
132
158
  if (!overrideValidation.valid) {
133
159
  throw new Error(overrideValidation.message);
134
160
  }
135
161
  endpoint = overrideValidation.endpoint;
136
- assertTransportMatchesEndpoint(req.transport ?? "chat", endpoint);
162
+ const { path, rest } = splitEndpoint(endpoint);
163
+ if (autoUpgraded && path.endsWith("/chat/completions")) {
164
+ endpoint = `${path.slice(0, -"/chat/completions".length)}/responses${rest}`;
165
+ }
166
+ assertTransportMatchesEndpoint(transport, endpoint);
137
167
  }
138
168
  const maxAttempts = Math.max(1, req.maxAttempts ?? 3);
139
169
  const startedAt = Date.now();
@@ -158,7 +188,10 @@ async function callPortalJsonCompletion(req) {
158
188
  ),
159
189
  ...reinforce && { reinforceJsonContract: true }
160
190
  } : reinforce ? { ...req, reinforceJsonContract: true } : req;
161
- const outcome = await attemptPortalJson(attemptReq, endpoint);
191
+ const outcome = await attemptPortalJson(
192
+ { ...attemptReq, transport },
193
+ endpoint
194
+ );
162
195
  if (outcome.kind === "ok") {
163
196
  req.onAttempt?.({ attempt, ok: true });
164
197
  return outcome.value;
@@ -310,6 +343,13 @@ async function attemptPortalJson(req, endpoint) {
310
343
  }
311
344
  clearTimeout(timer);
312
345
  logPortalUsage(body, req.tag);
346
+ if (isModerationResponse(body)) {
347
+ return {
348
+ kind: "terminal",
349
+ code: "content-flagged",
350
+ reason: "portal moderation refused the request"
351
+ };
352
+ }
313
353
  const rawContent = extractCompletionContent(body);
314
354
  if (!rawContent) {
315
355
  return {
@@ -395,6 +435,9 @@ function logPortalUsage(body, tag) {
395
435
  } catch {
396
436
  }
397
437
  }
438
+ function isModerationResponse(body) {
439
+ return typeof body === "object" && body !== null && body.id === "moderation";
440
+ }
398
441
  function extractCompletionContent(body) {
399
442
  if (typeof body !== "object" || body === null) return null;
400
443
  const responsesText = extractResponsesContent(body);
@@ -2,7 +2,7 @@ import {
2
2
  callPortalJsonCompletion,
3
3
  notifyConsolidationFallback,
4
4
  resolvePiiRedactor
5
- } from "./chunk-Z2QBL4HB.mjs";
5
+ } from "./chunk-KSYV7UO2.mjs";
6
6
  import "./chunk-3RPGXXPZ.mjs";
7
7
 
8
8
  // src/lib/memory/consolidate.ts