@kenkaiiii/gg-ai 5.16.0 → 5.18.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.
package/dist/index.d.cts CHANGED
@@ -444,6 +444,27 @@ declare function formatErrorForDisplay(err: unknown): string;
444
444
  */
445
445
  declare function classifyProviderError(message: string): string;
446
446
 
447
+ declare const REDACTED = "[REDACTED]";
448
+ interface RedactionOptions {
449
+ /** Exact secret values to remove in addition to high-confidence formats. */
450
+ secrets?: Iterable<string>;
451
+ /** Maximum recursive object depth before a stable truncation marker is emitted. */
452
+ maxDepth?: number;
453
+ /** Maximum total array/object entries cloned before truncation markers are emitted. */
454
+ maxEntries?: number;
455
+ /** Maximum retained string length after sanitization. */
456
+ maxStringLength?: number;
457
+ }
458
+ /** Collect sufficiently distinctive secrets from security-sensitive environment variables. */
459
+ declare function environmentSecrets(env: Record<string, string | undefined>): string[];
460
+ /** Redact credentials from arbitrary text without mutating its source. */
461
+ declare function redactText(text: string, options?: RedactionOptions): string;
462
+ /**
463
+ * Recursively clone and sanitize transport/persistence payloads.
464
+ * Cycles, excessive depth, and excessive collection sizes become stable markers.
465
+ */
466
+ declare function redactValue<T>(value: T, options?: RedactionOptions): T;
467
+
447
468
  /**
448
469
  * Provider-level diagnostic hook. Mirrors the pattern used by gg-agent's
449
470
  * setStreamDiagnostic — the host app wires a callback (typically writing to
@@ -454,6 +475,12 @@ type ProviderDiagnosticFn = (phase: string, data?: Record<string, unknown>) => v
454
475
  /** Register a diagnostic callback for provider-level tracing. */
455
476
  declare function setProviderDiagnostic(fn: ProviderDiagnosticFn | null): void;
456
477
 
478
+ /**
479
+ * Cap historical images before provider dispatch, removing the oldest first.
480
+ * The persisted/live conversation is never mutated; only modified messages and
481
+ * tool results are cloned for the outgoing request.
482
+ */
483
+ declare function clampProviderContextImages(messages: Message[], provider: Provider, supportsImages: boolean | undefined): Message[];
457
484
  declare function toAnthropicMessages(messages: Message[], cacheControl?: {
458
485
  type: "ephemeral";
459
486
  ttl?: "1h";
@@ -558,4 +585,4 @@ interface PalsuProviderConfig {
558
585
  */
559
586
  declare function registerPalsuProvider(config?: PalsuProviderConfig): PalsuProviderHandle;
560
587
 
561
- export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderDiagnosticFn, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, type VideoContent, classifyProviderError, formatError, formatErrorForDisplay, isHardBillingMessage, isUsageLimitError, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, prewarmAnthropicCache, providerRegistry, registerPalsuProvider, setProviderDiagnostic, stream, toAnthropicMessages, toOpenAIMessages };
588
+ export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderDiagnosticFn, type ProviderEntry, ProviderError, type ProviderStreamFn, REDACTED as REDACTION_MARKER, type RawContent, type RedactionOptions, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, type VideoContent, clampProviderContextImages, classifyProviderError, environmentSecrets, formatError, formatErrorForDisplay, isHardBillingMessage, isUsageLimitError, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, prewarmAnthropicCache, providerRegistry, redactText, redactValue, registerPalsuProvider, setProviderDiagnostic, stream, toAnthropicMessages, toOpenAIMessages };
package/dist/index.d.ts CHANGED
@@ -444,6 +444,27 @@ declare function formatErrorForDisplay(err: unknown): string;
444
444
  */
445
445
  declare function classifyProviderError(message: string): string;
446
446
 
447
+ declare const REDACTED = "[REDACTED]";
448
+ interface RedactionOptions {
449
+ /** Exact secret values to remove in addition to high-confidence formats. */
450
+ secrets?: Iterable<string>;
451
+ /** Maximum recursive object depth before a stable truncation marker is emitted. */
452
+ maxDepth?: number;
453
+ /** Maximum total array/object entries cloned before truncation markers are emitted. */
454
+ maxEntries?: number;
455
+ /** Maximum retained string length after sanitization. */
456
+ maxStringLength?: number;
457
+ }
458
+ /** Collect sufficiently distinctive secrets from security-sensitive environment variables. */
459
+ declare function environmentSecrets(env: Record<string, string | undefined>): string[];
460
+ /** Redact credentials from arbitrary text without mutating its source. */
461
+ declare function redactText(text: string, options?: RedactionOptions): string;
462
+ /**
463
+ * Recursively clone and sanitize transport/persistence payloads.
464
+ * Cycles, excessive depth, and excessive collection sizes become stable markers.
465
+ */
466
+ declare function redactValue<T>(value: T, options?: RedactionOptions): T;
467
+
447
468
  /**
448
469
  * Provider-level diagnostic hook. Mirrors the pattern used by gg-agent's
449
470
  * setStreamDiagnostic — the host app wires a callback (typically writing to
@@ -454,6 +475,12 @@ type ProviderDiagnosticFn = (phase: string, data?: Record<string, unknown>) => v
454
475
  /** Register a diagnostic callback for provider-level tracing. */
455
476
  declare function setProviderDiagnostic(fn: ProviderDiagnosticFn | null): void;
456
477
 
478
+ /**
479
+ * Cap historical images before provider dispatch, removing the oldest first.
480
+ * The persisted/live conversation is never mutated; only modified messages and
481
+ * tool results are cloned for the outgoing request.
482
+ */
483
+ declare function clampProviderContextImages(messages: Message[], provider: Provider, supportsImages: boolean | undefined): Message[];
457
484
  declare function toAnthropicMessages(messages: Message[], cacheControl?: {
458
485
  type: "ephemeral";
459
486
  ttl?: "1h";
@@ -558,4 +585,4 @@ interface PalsuProviderConfig {
558
585
  */
559
586
  declare function registerPalsuProvider(config?: PalsuProviderConfig): PalsuProviderHandle;
560
587
 
561
- export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderDiagnosticFn, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, type VideoContent, classifyProviderError, formatError, formatErrorForDisplay, isHardBillingMessage, isUsageLimitError, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, prewarmAnthropicCache, providerRegistry, registerPalsuProvider, setProviderDiagnostic, stream, toAnthropicMessages, toOpenAIMessages };
588
+ export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderDiagnosticFn, type ProviderEntry, ProviderError, type ProviderStreamFn, REDACTED as REDACTION_MARKER, type RawContent, type RedactionOptions, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, type VideoContent, clampProviderContextImages, classifyProviderError, environmentSecrets, formatError, formatErrorForDisplay, isHardBillingMessage, isUsageLimitError, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, prewarmAnthropicCache, providerRegistry, redactText, redactValue, registerPalsuProvider, setProviderDiagnostic, stream, toAnthropicMessages, toOpenAIMessages };
package/dist/index.js CHANGED
@@ -102,13 +102,20 @@ function isRawJsonErrorEcho(message) {
102
102
  return false;
103
103
  }
104
104
  }
105
+ function isRawHtmlErrorEcho(message) {
106
+ const withoutStatus = message.trimStart().replace(/^\d{3}\s+/, "").trimStart();
107
+ return /^<!doctype\s+html(?:\s|>)/i.test(withoutStatus) || /^<html(?:\s|>)/i.test(withoutStatus);
108
+ }
109
+ function providerHtmlErrorMessage(statusCode) {
110
+ return statusCode ? `The provider returned an HTML error page (HTTP ${statusCode}) instead of an API response.` : "The provider returned an HTML error page instead of an API response.";
111
+ }
105
112
  function emptyProviderErrorMessage(statusCode) {
106
113
  return statusCode ? `The provider returned an empty error response (HTTP ${statusCode}), with no further detail.` : "The provider returned an empty error response, with no further detail.";
107
114
  }
108
115
  function formatError(err) {
109
116
  if (err instanceof ProviderError) {
110
117
  const name = providerDisplayName(err.provider);
111
- const cleanMessage = cleanProviderMessage(err.message);
118
+ const cleanMessage = cleanProviderMessage(err.message, err.statusCode);
112
119
  if (isMythosAccessError(cleanMessage)) {
113
120
  return {
114
121
  headline: "Claude Mythos 5 is invitation-only.",
@@ -203,8 +210,9 @@ function formatErrorForDisplay(err) {
203
210
  lines.push(` \u2192 ${f.guidance}`);
204
211
  return lines.join("\n");
205
212
  }
206
- function cleanProviderMessage(message) {
207
- return message.replace(/^\[[^\]]+\]\s*/, "").trim();
213
+ function cleanProviderMessage(message, statusCode) {
214
+ const clean = message.replace(/^\[[^\]]+\]\s*/, "").trim();
215
+ return isRawHtmlErrorEcho(clean) ? providerHtmlErrorMessage(statusCode) : clean;
208
216
  }
209
217
  function inferSource(err) {
210
218
  const msg = err.message.toLowerCase();
@@ -565,6 +573,66 @@ function toAnthropicAssistantContent(content, preserveThinking, idMap) {
565
573
  return true;
566
574
  }).map((part) => toAnthropicAssistantPart(part, idMap)).filter((b) => b !== null);
567
575
  }
576
+ var PROVIDER_IMAGE_LIMIT_PLACEHOLDER = "[image omitted: provider image limit]";
577
+ var PROVIDER_IMAGE_BUDGETS = {
578
+ anthropic: 90,
579
+ minimax: 90,
580
+ openai: 200,
581
+ gemini: 200,
582
+ openrouter: 90
583
+ };
584
+ function countContextImages(messages) {
585
+ let count = 0;
586
+ for (const message of messages) {
587
+ if (message.role === "user" && Array.isArray(message.content)) {
588
+ count += message.content.filter((part) => part.type === "image").length;
589
+ } else if (message.role === "tool") {
590
+ for (const result of message.content) {
591
+ if (Array.isArray(result.content)) {
592
+ count += result.content.filter((part) => part.type === "image").length;
593
+ }
594
+ }
595
+ }
596
+ }
597
+ return count;
598
+ }
599
+ function clampProviderContextImages(messages, provider, supportsImages) {
600
+ if (supportsImages === false) return messages;
601
+ const budget = PROVIDER_IMAGE_BUDGETS[provider] ?? 5;
602
+ let remainingToRemove = countContextImages(messages) - budget;
603
+ if (remainingToRemove <= 0) return messages;
604
+ return messages.map((message) => {
605
+ if (message.role === "user" && Array.isArray(message.content)) {
606
+ const content = message.content.filter((part) => {
607
+ if (part.type !== "image" || remainingToRemove <= 0) return true;
608
+ remainingToRemove--;
609
+ return false;
610
+ });
611
+ return {
612
+ ...message,
613
+ content: content.length > 0 ? content : [{ type: "text", text: PROVIDER_IMAGE_LIMIT_PLACEHOLDER }]
614
+ };
615
+ }
616
+ if (message.role === "tool") {
617
+ return {
618
+ ...message,
619
+ content: message.content.map((result) => {
620
+ if (!Array.isArray(result.content)) return result;
621
+ const content = result.content.filter((part) => {
622
+ if (part.type !== "image" || remainingToRemove <= 0) return true;
623
+ remainingToRemove--;
624
+ return false;
625
+ });
626
+ return {
627
+ ...result,
628
+ content: content.length > 0 ? content : [{ type: "text", text: PROVIDER_IMAGE_LIMIT_PLACEHOLDER }]
629
+ };
630
+ })
631
+ };
632
+ }
633
+ return message;
634
+ });
635
+ }
568
636
  var NON_VISION_USER_IMAGE_PLACEHOLDER = "(image omitted: model does not support images)";
569
637
  var NON_VISION_TOOL_IMAGE_PLACEHOLDER = "(tool image omitted: model does not support images)";
570
638
  var NON_VIDEO_USER_PLACEHOLDER = "(video omitted: model does not support video)";
@@ -1581,7 +1649,8 @@ function toError(err) {
1581
1649
  const bodyMessage = typeof nestedError?.message === "string" && nestedError.message.trim() ? nestedError.message.trim() : typeof errorBody?.message === "string" && errorBody.message.trim() ? errorBody.message.trim() : void 0;
1582
1650
  const bodyType = typeof nestedError?.type === "string" ? nestedError.type : typeof errorBody?.type === "string" ? errorBody.type : typeof err.type === "string" ? err.type : void 0;
1583
1651
  const fallbackMessage = isRawJsonErrorEcho(err.message) ? emptyProviderErrorMessage(err.status) : err.message;
1584
- const message = bodyType && bodyMessage ? `${bodyType}: ${bodyMessage}` : bodyMessage ?? fallbackMessage;
1652
+ const messageCandidate = bodyMessage ?? err.message;
1653
+ const message = isRawHtmlErrorEcho(messageCandidate) ? providerHtmlErrorMessage(err.status) : bodyType && bodyMessage ? `${bodyType}: ${bodyMessage}` : bodyMessage ?? fallbackMessage;
1585
1654
  if (err.status === 429) {
1586
1655
  const limit = readUnifiedRateLimit(err.headers);
1587
1656
  const farOff = limit.resetsAt != null && limit.resetsAt * 1e3 - Date.now() > 6e4;
@@ -1763,7 +1832,11 @@ async function* runStream2(options) {
1763
1832
  const providerName = options.provider ?? "openai";
1764
1833
  const useStreaming = options.streaming !== false;
1765
1834
  const client = createClient2(options);
1766
- const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot" || options.provider === "xiaomi";
1835
+ const isKimiK3 = options.provider === "moonshot" && options.model === "kimi-k3";
1836
+ const isManagedKimiK3 = isKimiK3 && options.baseUrl?.replace(/\/+$/, "").endsWith("/coding/v1") === true;
1837
+ const isKimiK27 = options.provider === "moonshot" && options.model.startsWith("kimi-k2.7-code");
1838
+ const hasFixedKimiSampling = isKimiK3 || isKimiK27;
1839
+ const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot" && !isKimiK3 && !isKimiK27 || options.provider === "xiaomi";
1767
1840
  const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
1768
1841
  const downgradedMessages = downgradeUnsupportedVideos(downgradedImages, options.supportsVideo);
1769
1842
  if (options.provider === "moonshot") {
@@ -1775,7 +1848,9 @@ async function* runStream2(options) {
1775
1848
  }
1776
1849
  const messages = toOpenAIMessages(downgradedMessages, {
1777
1850
  provider: options.provider,
1778
- thinking: !!options.thinking,
1851
+ // K3 and K2.7 preserve reasoning even when the user hides thinking in the
1852
+ // UI; keep assistant tool-call history wire-valid in that display mode.
1853
+ thinking: isKimiK3 || isKimiK27 || !!options.thinking,
1779
1854
  supportsImages: options.supportsImages
1780
1855
  });
1781
1856
  const defaultTemp = options.provider === "glm" ? 0.6 : void 0;
@@ -1785,10 +1860,10 @@ async function* runStream2(options) {
1785
1860
  messages,
1786
1861
  stream: useStreaming,
1787
1862
  ...options.maxTokens ? { max_completion_tokens: options.maxTokens } : {},
1788
- ...effectiveTemp != null && !options.thinking ? { temperature: effectiveTemp } : {},
1789
- ...options.topP != null ? { top_p: options.topP } : {},
1863
+ ...effectiveTemp != null && !options.thinking && !hasFixedKimiSampling ? { temperature: effectiveTemp } : {},
1864
+ ...options.topP != null && !hasFixedKimiSampling ? { top_p: options.topP } : {},
1790
1865
  ...options.stop ? { stop: options.stop } : {},
1791
- ...options.thinking && !usesThinkingParam ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking, options.model) } : {},
1866
+ ...options.thinking && !usesThinkingParam && !isKimiK3 && !isKimiK27 ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking, options.model) } : {},
1792
1867
  ...options.tools?.length ? { tools: toOpenAITools(options.tools) } : {},
1793
1868
  ...options.toolChoice && options.tools?.length ? { tool_choice: toOpenAIToolChoice(options.toolChoice) } : {},
1794
1869
  ...useStreaming ? { stream_options: { include_usage: true } } : {}
@@ -1798,13 +1873,21 @@ async function* runStream2(options) {
1798
1873
  paramsAny.prompt_cache_key = normalizePromptCacheKey(options.promptCacheKey ?? "ggcoder");
1799
1874
  if (options.provider === "openai" && options.model.startsWith("gpt-5.6")) {
1800
1875
  paramsAny.prompt_cache_options = { mode: "implicit", ttl: "30m" };
1801
- } else if ((options.cacheRetention ?? "short") === "long") {
1876
+ } else if (!isKimiK3 && (options.cacheRetention ?? "short") === "long") {
1802
1877
  paramsAny.prompt_cache_retention = "24h";
1803
1878
  }
1804
1879
  }
1805
1880
  if (options.provider === "openai" && options.serviceTier) {
1806
1881
  params.service_tier = options.serviceTier;
1807
1882
  }
1883
+ if (isKimiK3) {
1884
+ const paramsAny = params;
1885
+ if (isManagedKimiK3) {
1886
+ paramsAny.thinking = { type: "enabled", effort: "max", keep: "all" };
1887
+ } else {
1888
+ paramsAny.reasoning_effort = "max";
1889
+ }
1890
+ }
1808
1891
  if (usesThinkingParam) {
1809
1892
  if (options.thinking) {
1810
1893
  params.thinking = { type: "enabled" };
@@ -2049,7 +2132,8 @@ function toError2(err, provider = "openai") {
2049
2132
  const body = err.error;
2050
2133
  const bodyMessage = typeof body?.message === "string" && body.message.trim() ? body.message.trim() : void 0;
2051
2134
  const modelName = typeof body?.model === "string" ? body.model : "";
2052
- const cleanMessage = bodyMessage ?? (isRawJsonErrorEcho(err.message) ? emptyProviderErrorMessage(err.status) : err.message);
2135
+ const messageCandidate = bodyMessage ?? err.message;
2136
+ const cleanMessage = isRawHtmlErrorEcho(messageCandidate) ? providerHtmlErrorMessage(err.status) : bodyMessage ? bodyMessage : isRawJsonErrorEcho(err.message) ? emptyProviderErrorMessage(err.status) : err.message;
2053
2137
  let hint;
2054
2138
  if (modelName === "codex-mini-latest" || cleanMessage.includes("codex-mini-latest")) {
2055
2139
  hint = "codex-mini-latest requires an OpenAI Pro or Max subscription. Your account currently has access to GPT-5.4 and GPT-5.4 Mini.";
@@ -2163,6 +2247,21 @@ function outputTextKey(itemId, contentIndex) {
2163
2247
  function isVisibleOutputItem(itemType) {
2164
2248
  return itemType === "message";
2165
2249
  }
2250
+ function toCodexToolChoice(choice, tools) {
2251
+ const resolved = choice ?? "auto";
2252
+ if (typeof resolved === "object") {
2253
+ throw new GGAIError(
2254
+ `OpenAI Codex does not support selecting the named tool \`${resolved.name}\`; use auto, none, or required.`,
2255
+ { source: "capability" }
2256
+ );
2257
+ }
2258
+ if (resolved === "required" && !tools?.length) {
2259
+ throw new GGAIError("OpenAI Codex cannot require a tool call when no tools are configured.", {
2260
+ source: "capability"
2261
+ });
2262
+ }
2263
+ return resolved;
2264
+ }
2166
2265
  function streamOpenAICodex(options) {
2167
2266
  return new StreamResult(runStream3(options), options.signal);
2168
2267
  }
@@ -2179,7 +2278,7 @@ async function* runStream3(options) {
2179
2278
  stream: true,
2180
2279
  instructions: system,
2181
2280
  input,
2182
- tool_choice: "auto",
2281
+ tool_choice: toCodexToolChoice(options.toolChoice, options.tools),
2183
2282
  parallel_tool_calls: !responsesLite,
2184
2283
  include: ["reasoning.encrypted_content"]
2185
2284
  };
@@ -2212,8 +2311,9 @@ async function* runStream3(options) {
2212
2311
  headers["chatgpt-account-id"] = options.accountId;
2213
2312
  }
2214
2313
  if (options.transportSessionId) {
2215
- headers["session_id"] = options.transportSessionId;
2216
- headers["x-client-request-id"] = options.transportSessionId;
2314
+ const transportSessionId = normalizePromptCacheKey(options.transportSessionId);
2315
+ headers["session_id"] = transportSessionId;
2316
+ headers["x-client-request-id"] = transportSessionId;
2217
2317
  }
2218
2318
  const response = await fetch(url, {
2219
2319
  method: "POST",
@@ -2223,7 +2323,7 @@ async function* runStream3(options) {
2223
2323
  });
2224
2324
  if (!response.ok) {
2225
2325
  const text = await response.text().catch(() => "");
2226
- const parsed = parseCodexErrorBody(text);
2326
+ const parsed = parseCodexErrorBody(text, response.status);
2227
2327
  const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
2228
2328
  const requestId = parsed.requestId ?? readHeader(response.headers, "x-request-id", "openai-request-id", "x-oai-request-id");
2229
2329
  const usageLimit = codexUsageLimitError(parsed.errorObj, response.status, requestId);
@@ -2617,13 +2717,14 @@ function toCodexTools(tools) {
2617
2717
  strict: null
2618
2718
  }));
2619
2719
  }
2620
- function parseCodexErrorBody(text) {
2720
+ function parseCodexErrorBody(text, statusCode) {
2621
2721
  if (!text) return {};
2622
2722
  try {
2623
2723
  const parsed = JSON.parse(text);
2624
2724
  const error = parsed.error;
2625
2725
  const detail = parsed.detail;
2626
- const message = error?.message ?? parsed.message ?? (typeof detail === "string" ? detail : void 0);
2726
+ const rawMessage = error?.message ?? parsed.message ?? (typeof detail === "string" ? detail : void 0);
2727
+ const message = rawMessage && isRawHtmlErrorEcho(rawMessage) ? providerHtmlErrorMessage(statusCode) : rawMessage;
2627
2728
  const requestId = parsed.request_id ?? error?.request_id ?? (message ? extractRequestIdFromMessage(message) : void 0);
2628
2729
  const errorObj = error ?? parsed;
2629
2730
  return {
@@ -2632,8 +2733,12 @@ function parseCodexErrorBody(text) {
2632
2733
  ...errorObj ? { errorObj } : {}
2633
2734
  };
2634
2735
  } catch {
2635
- const trimmed = text.trim().slice(0, 240);
2636
- return trimmed ? { message: trimmed } : {};
2736
+ const trimmed = text.trim();
2737
+ if (isRawHtmlErrorEcho(trimmed)) {
2738
+ return { message: providerHtmlErrorMessage(statusCode) };
2739
+ }
2740
+ const bounded = trimmed.slice(0, 240);
2741
+ return bounded ? { message: bounded } : {};
2637
2742
  }
2638
2743
  }
2639
2744
  var CODEX_USAGE_LIMIT_CODE = /usage_limit_reached|usage_not_included/i;
@@ -3268,7 +3373,12 @@ function stream(options) {
3268
3373
  if (options.supportsVideo !== true && messagesContainVideo(options.messages)) {
3269
3374
  throw new VideoUnsupportedError();
3270
3375
  }
3271
- return entry.stream(options);
3376
+ const messages = clampProviderContextImages(
3377
+ options.messages,
3378
+ options.provider,
3379
+ options.supportsImages
3380
+ );
3381
+ return entry.stream(messages === options.messages ? options : { ...options, messages });
3272
3382
  }
3273
3383
  function messagesContainVideo(messages) {
3274
3384
  for (const msg of messages) {
@@ -3373,6 +3483,125 @@ Original: ${message}`;
3373
3483
  return message;
3374
3484
  }
3375
3485
 
3486
+ // src/redaction.ts
3487
+ var REDACTED = "[REDACTED]";
3488
+ var TRUNCATED = "[TRUNCATED]";
3489
+ var CIRCULAR = "[CIRCULAR]";
3490
+ var SENSITIVE_NAME = /(?:^|[_-])(?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|key|auth(?:orization)?|bearer|cookie|credential|private[_-]?key|password|passwd|secret)(?:$|[_-])/i;
3491
+ var SENSITIVE_ASSIGNMENT = /\b((?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|key|auth(?:orization)?|bearer|cookie|credential|private[_-]?key|password|passwd|secret))\b(\s*[=:]\s*)(["']?)([^\s,"';}]+)\3/gi;
3492
+ function escaped(value) {
3493
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3494
+ }
3495
+ function normalizedSecrets(secrets) {
3496
+ if (!secrets) return [];
3497
+ return [...new Set([...secrets].filter((value) => value.length >= 8 && value !== REDACTED))].sort(
3498
+ (a, b) => b.length - a.length
3499
+ );
3500
+ }
3501
+ function environmentSecrets(env) {
3502
+ const values = /* @__PURE__ */ new Set();
3503
+ for (const [name, value] of Object.entries(env)) {
3504
+ if (!value || value.length < 8 || value === REDACTED || !SENSITIVE_NAME.test(name)) continue;
3505
+ values.add(value);
3506
+ }
3507
+ return [...values].sort((a, b) => b.length - a.length);
3508
+ }
3509
+ function redactText(text, options = {}) {
3510
+ let result = text;
3511
+ result = result.replace(
3512
+ /-----BEGIN (?:[A-Z0-9 ]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z0-9 ]+ )?PRIVATE KEY-----/g,
3513
+ REDACTED
3514
+ );
3515
+ result = result.replace(/\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+:[^\s/@]+@/gi, `$1${REDACTED}@`);
3516
+ result = result.replace(
3517
+ /\b(authorization\s*[:=]\s*)(?:bearer|basic)\s+[^\s,;]+/gi,
3518
+ `$1${REDACTED}`
3519
+ );
3520
+ result = result.replace(/\b(bearer|basic)\s+[A-Za-z0-9+/_.=-]{8,}/gi, `$1 ${REDACTED}`);
3521
+ result = result.replace(/\b(cookie|set-cookie)(\s*[:=]\s*)[^\r\n]+/gi, `$1$2${REDACTED}`);
3522
+ result = result.replace(
3523
+ /\beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\b/g,
3524
+ REDACTED
3525
+ );
3526
+ result = result.replace(
3527
+ /\b(?:sk-(?:ant-|proj-)?|xox[baprs]-|gh[pousr]_|github_pat_|AIza)[A-Za-z0-9_-]{12,}\b/g,
3528
+ REDACTED
3529
+ );
3530
+ result = result.replace(
3531
+ SENSITIVE_ASSIGNMENT,
3532
+ (_match, name, separator) => `${name}${separator}${REDACTED}`
3533
+ );
3534
+ for (const secret of normalizedSecrets(options.secrets)) {
3535
+ result = result.replace(new RegExp(escaped(secret), "g"), REDACTED);
3536
+ }
3537
+ const maxStringLength = options.maxStringLength ?? 1e6;
3538
+ if (result.length > maxStringLength) {
3539
+ result = `${result.slice(0, maxStringLength)}${TRUNCATED}`;
3540
+ }
3541
+ return result;
3542
+ }
3543
+ function isBinary(value) {
3544
+ return value instanceof ArrayBuffer || ArrayBuffer.isView(value) || typeof Blob !== "undefined" && value instanceof Blob;
3545
+ }
3546
+ function isMediaObject(value) {
3547
+ return (value.type === "image" || value.type === "video") && (typeof value.data === "string" || typeof value.url === "string");
3548
+ }
3549
+ function redactValue(value, options = {}) {
3550
+ const maxDepth = options.maxDepth ?? 20;
3551
+ const maxEntries = options.maxEntries ?? 1e4;
3552
+ const seen = /* @__PURE__ */ new WeakSet();
3553
+ let entries = 0;
3554
+ const visit = (current, depth, sensitive = false) => {
3555
+ if (typeof current === "string") {
3556
+ if (sensitive && current.length > 0 && current !== REDACTED) return REDACTED;
3557
+ return redactText(current, options);
3558
+ }
3559
+ if (current === null || current === void 0 || typeof current === "number" || typeof current === "boolean" || typeof current === "bigint") {
3560
+ return current;
3561
+ }
3562
+ if (typeof current !== "object") return current;
3563
+ if (isBinary(current)) return current;
3564
+ if (current instanceof Date) return new Date(current.getTime());
3565
+ if (depth >= maxDepth) return TRUNCATED;
3566
+ if (seen.has(current)) return CIRCULAR;
3567
+ seen.add(current);
3568
+ if (current instanceof Error) {
3569
+ const error = {
3570
+ name: current.name,
3571
+ message: visit(current.message, depth + 1),
3572
+ stack: visit(current.stack, depth + 1)
3573
+ };
3574
+ for (const [key, child] of Object.entries(current)) {
3575
+ error[key] = visit(child, depth + 1, SENSITIVE_NAME.test(key));
3576
+ }
3577
+ return error;
3578
+ }
3579
+ if (Array.isArray(current)) {
3580
+ const clone2 = [];
3581
+ for (const child of current) {
3582
+ if (++entries > maxEntries) {
3583
+ clone2.push(TRUNCATED);
3584
+ break;
3585
+ }
3586
+ clone2.push(visit(child, depth + 1));
3587
+ }
3588
+ return clone2;
3589
+ }
3590
+ const record = current;
3591
+ if (isMediaObject(record)) return { ...record };
3592
+ const clone = {};
3593
+ for (const [key, child] of Object.entries(record)) {
3594
+ if (++entries > maxEntries) {
3595
+ clone[TRUNCATED] = true;
3596
+ break;
3597
+ }
3598
+ clone[key] = visit(child, depth + 1, SENSITIVE_NAME.test(key));
3599
+ }
3600
+ return clone;
3601
+ };
3602
+ return visit(value, 0);
3603
+ }
3604
+
3376
3605
  // src/providers/palsu.ts
3377
3606
  function palsuText(text) {
3378
3607
  return { role: "assistant", content: text ? [{ type: "text", text }] : [] };
@@ -3528,8 +3757,11 @@ export {
3528
3757
  EventStream,
3529
3758
  GGAIError,
3530
3759
  ProviderError,
3760
+ REDACTED as REDACTION_MARKER,
3531
3761
  StreamResult,
3762
+ clampProviderContextImages,
3532
3763
  classifyProviderError,
3764
+ environmentSecrets,
3533
3765
  formatError,
3534
3766
  formatErrorForDisplay,
3535
3767
  isHardBillingMessage,
@@ -3540,6 +3772,8 @@ export {
3540
3772
  palsuToolCall,
3541
3773
  prewarmAnthropicCache,
3542
3774
  providerRegistry,
3775
+ redactText,
3776
+ redactValue,
3543
3777
  registerPalsuProvider,
3544
3778
  setProviderDiagnostic,
3545
3779
  stream,