@kenkaiiii/gg-ai 5.16.0 → 5.17.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;
@@ -2049,7 +2118,8 @@ function toError2(err, provider = "openai") {
2049
2118
  const body = err.error;
2050
2119
  const bodyMessage = typeof body?.message === "string" && body.message.trim() ? body.message.trim() : void 0;
2051
2120
  const modelName = typeof body?.model === "string" ? body.model : "";
2052
- const cleanMessage = bodyMessage ?? (isRawJsonErrorEcho(err.message) ? emptyProviderErrorMessage(err.status) : err.message);
2121
+ const messageCandidate = bodyMessage ?? err.message;
2122
+ const cleanMessage = isRawHtmlErrorEcho(messageCandidate) ? providerHtmlErrorMessage(err.status) : bodyMessage ? bodyMessage : isRawJsonErrorEcho(err.message) ? emptyProviderErrorMessage(err.status) : err.message;
2053
2123
  let hint;
2054
2124
  if (modelName === "codex-mini-latest" || cleanMessage.includes("codex-mini-latest")) {
2055
2125
  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 +2233,21 @@ function outputTextKey(itemId, contentIndex) {
2163
2233
  function isVisibleOutputItem(itemType) {
2164
2234
  return itemType === "message";
2165
2235
  }
2236
+ function toCodexToolChoice(choice, tools) {
2237
+ const resolved = choice ?? "auto";
2238
+ if (typeof resolved === "object") {
2239
+ throw new GGAIError(
2240
+ `OpenAI Codex does not support selecting the named tool \`${resolved.name}\`; use auto, none, or required.`,
2241
+ { source: "capability" }
2242
+ );
2243
+ }
2244
+ if (resolved === "required" && !tools?.length) {
2245
+ throw new GGAIError("OpenAI Codex cannot require a tool call when no tools are configured.", {
2246
+ source: "capability"
2247
+ });
2248
+ }
2249
+ return resolved;
2250
+ }
2166
2251
  function streamOpenAICodex(options) {
2167
2252
  return new StreamResult(runStream3(options), options.signal);
2168
2253
  }
@@ -2179,7 +2264,7 @@ async function* runStream3(options) {
2179
2264
  stream: true,
2180
2265
  instructions: system,
2181
2266
  input,
2182
- tool_choice: "auto",
2267
+ tool_choice: toCodexToolChoice(options.toolChoice, options.tools),
2183
2268
  parallel_tool_calls: !responsesLite,
2184
2269
  include: ["reasoning.encrypted_content"]
2185
2270
  };
@@ -2212,8 +2297,9 @@ async function* runStream3(options) {
2212
2297
  headers["chatgpt-account-id"] = options.accountId;
2213
2298
  }
2214
2299
  if (options.transportSessionId) {
2215
- headers["session_id"] = options.transportSessionId;
2216
- headers["x-client-request-id"] = options.transportSessionId;
2300
+ const transportSessionId = normalizePromptCacheKey(options.transportSessionId);
2301
+ headers["session_id"] = transportSessionId;
2302
+ headers["x-client-request-id"] = transportSessionId;
2217
2303
  }
2218
2304
  const response = await fetch(url, {
2219
2305
  method: "POST",
@@ -2223,7 +2309,7 @@ async function* runStream3(options) {
2223
2309
  });
2224
2310
  if (!response.ok) {
2225
2311
  const text = await response.text().catch(() => "");
2226
- const parsed = parseCodexErrorBody(text);
2312
+ const parsed = parseCodexErrorBody(text, response.status);
2227
2313
  const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
2228
2314
  const requestId = parsed.requestId ?? readHeader(response.headers, "x-request-id", "openai-request-id", "x-oai-request-id");
2229
2315
  const usageLimit = codexUsageLimitError(parsed.errorObj, response.status, requestId);
@@ -2617,13 +2703,14 @@ function toCodexTools(tools) {
2617
2703
  strict: null
2618
2704
  }));
2619
2705
  }
2620
- function parseCodexErrorBody(text) {
2706
+ function parseCodexErrorBody(text, statusCode) {
2621
2707
  if (!text) return {};
2622
2708
  try {
2623
2709
  const parsed = JSON.parse(text);
2624
2710
  const error = parsed.error;
2625
2711
  const detail = parsed.detail;
2626
- const message = error?.message ?? parsed.message ?? (typeof detail === "string" ? detail : void 0);
2712
+ const rawMessage = error?.message ?? parsed.message ?? (typeof detail === "string" ? detail : void 0);
2713
+ const message = rawMessage && isRawHtmlErrorEcho(rawMessage) ? providerHtmlErrorMessage(statusCode) : rawMessage;
2627
2714
  const requestId = parsed.request_id ?? error?.request_id ?? (message ? extractRequestIdFromMessage(message) : void 0);
2628
2715
  const errorObj = error ?? parsed;
2629
2716
  return {
@@ -2632,8 +2719,12 @@ function parseCodexErrorBody(text) {
2632
2719
  ...errorObj ? { errorObj } : {}
2633
2720
  };
2634
2721
  } catch {
2635
- const trimmed = text.trim().slice(0, 240);
2636
- return trimmed ? { message: trimmed } : {};
2722
+ const trimmed = text.trim();
2723
+ if (isRawHtmlErrorEcho(trimmed)) {
2724
+ return { message: providerHtmlErrorMessage(statusCode) };
2725
+ }
2726
+ const bounded = trimmed.slice(0, 240);
2727
+ return bounded ? { message: bounded } : {};
2637
2728
  }
2638
2729
  }
2639
2730
  var CODEX_USAGE_LIMIT_CODE = /usage_limit_reached|usage_not_included/i;
@@ -3268,7 +3359,12 @@ function stream(options) {
3268
3359
  if (options.supportsVideo !== true && messagesContainVideo(options.messages)) {
3269
3360
  throw new VideoUnsupportedError();
3270
3361
  }
3271
- return entry.stream(options);
3362
+ const messages = clampProviderContextImages(
3363
+ options.messages,
3364
+ options.provider,
3365
+ options.supportsImages
3366
+ );
3367
+ return entry.stream(messages === options.messages ? options : { ...options, messages });
3272
3368
  }
3273
3369
  function messagesContainVideo(messages) {
3274
3370
  for (const msg of messages) {
@@ -3373,6 +3469,125 @@ Original: ${message}`;
3373
3469
  return message;
3374
3470
  }
3375
3471
 
3472
+ // src/redaction.ts
3473
+ var REDACTED = "[REDACTED]";
3474
+ var TRUNCATED = "[TRUNCATED]";
3475
+ var CIRCULAR = "[CIRCULAR]";
3476
+ var SENSITIVE_NAME = /(?:^|[_-])(?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|key|auth(?:orization)?|bearer|cookie|credential|private[_-]?key|password|passwd|secret)(?:$|[_-])/i;
3477
+ 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;
3478
+ function escaped(value) {
3479
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3480
+ }
3481
+ function normalizedSecrets(secrets) {
3482
+ if (!secrets) return [];
3483
+ return [...new Set([...secrets].filter((value) => value.length >= 8 && value !== REDACTED))].sort(
3484
+ (a, b) => b.length - a.length
3485
+ );
3486
+ }
3487
+ function environmentSecrets(env) {
3488
+ const values = /* @__PURE__ */ new Set();
3489
+ for (const [name, value] of Object.entries(env)) {
3490
+ if (!value || value.length < 8 || value === REDACTED || !SENSITIVE_NAME.test(name)) continue;
3491
+ values.add(value);
3492
+ }
3493
+ return [...values].sort((a, b) => b.length - a.length);
3494
+ }
3495
+ function redactText(text, options = {}) {
3496
+ let result = text;
3497
+ result = result.replace(
3498
+ /-----BEGIN (?:[A-Z0-9 ]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z0-9 ]+ )?PRIVATE KEY-----/g,
3499
+ REDACTED
3500
+ );
3501
+ result = result.replace(/\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+:[^\s/@]+@/gi, `$1${REDACTED}@`);
3502
+ result = result.replace(
3503
+ /\b(authorization\s*[:=]\s*)(?:bearer|basic)\s+[^\s,;]+/gi,
3504
+ `$1${REDACTED}`
3505
+ );
3506
+ result = result.replace(/\b(bearer|basic)\s+[A-Za-z0-9+/_.=-]{8,}/gi, `$1 ${REDACTED}`);
3507
+ result = result.replace(/\b(cookie|set-cookie)(\s*[:=]\s*)[^\r\n]+/gi, `$1$2${REDACTED}`);
3508
+ result = result.replace(
3509
+ /\beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\b/g,
3510
+ REDACTED
3511
+ );
3512
+ result = result.replace(
3513
+ /\b(?:sk-(?:ant-|proj-)?|xox[baprs]-|gh[pousr]_|github_pat_|AIza)[A-Za-z0-9_-]{12,}\b/g,
3514
+ REDACTED
3515
+ );
3516
+ result = result.replace(
3517
+ SENSITIVE_ASSIGNMENT,
3518
+ (_match, name, separator) => `${name}${separator}${REDACTED}`
3519
+ );
3520
+ for (const secret of normalizedSecrets(options.secrets)) {
3521
+ result = result.replace(new RegExp(escaped(secret), "g"), REDACTED);
3522
+ }
3523
+ const maxStringLength = options.maxStringLength ?? 1e6;
3524
+ if (result.length > maxStringLength) {
3525
+ result = `${result.slice(0, maxStringLength)}${TRUNCATED}`;
3526
+ }
3527
+ return result;
3528
+ }
3529
+ function isBinary(value) {
3530
+ return value instanceof ArrayBuffer || ArrayBuffer.isView(value) || typeof Blob !== "undefined" && value instanceof Blob;
3531
+ }
3532
+ function isMediaObject(value) {
3533
+ return (value.type === "image" || value.type === "video") && (typeof value.data === "string" || typeof value.url === "string");
3534
+ }
3535
+ function redactValue(value, options = {}) {
3536
+ const maxDepth = options.maxDepth ?? 20;
3537
+ const maxEntries = options.maxEntries ?? 1e4;
3538
+ const seen = /* @__PURE__ */ new WeakSet();
3539
+ let entries = 0;
3540
+ const visit = (current, depth, sensitive = false) => {
3541
+ if (typeof current === "string") {
3542
+ if (sensitive && current.length > 0 && current !== REDACTED) return REDACTED;
3543
+ return redactText(current, options);
3544
+ }
3545
+ if (current === null || current === void 0 || typeof current === "number" || typeof current === "boolean" || typeof current === "bigint") {
3546
+ return current;
3547
+ }
3548
+ if (typeof current !== "object") return current;
3549
+ if (isBinary(current)) return current;
3550
+ if (current instanceof Date) return new Date(current.getTime());
3551
+ if (depth >= maxDepth) return TRUNCATED;
3552
+ if (seen.has(current)) return CIRCULAR;
3553
+ seen.add(current);
3554
+ if (current instanceof Error) {
3555
+ const error = {
3556
+ name: current.name,
3557
+ message: visit(current.message, depth + 1),
3558
+ stack: visit(current.stack, depth + 1)
3559
+ };
3560
+ for (const [key, child] of Object.entries(current)) {
3561
+ error[key] = visit(child, depth + 1, SENSITIVE_NAME.test(key));
3562
+ }
3563
+ return error;
3564
+ }
3565
+ if (Array.isArray(current)) {
3566
+ const clone2 = [];
3567
+ for (const child of current) {
3568
+ if (++entries > maxEntries) {
3569
+ clone2.push(TRUNCATED);
3570
+ break;
3571
+ }
3572
+ clone2.push(visit(child, depth + 1));
3573
+ }
3574
+ return clone2;
3575
+ }
3576
+ const record = current;
3577
+ if (isMediaObject(record)) return { ...record };
3578
+ const clone = {};
3579
+ for (const [key, child] of Object.entries(record)) {
3580
+ if (++entries > maxEntries) {
3581
+ clone[TRUNCATED] = true;
3582
+ break;
3583
+ }
3584
+ clone[key] = visit(child, depth + 1, SENSITIVE_NAME.test(key));
3585
+ }
3586
+ return clone;
3587
+ };
3588
+ return visit(value, 0);
3589
+ }
3590
+
3376
3591
  // src/providers/palsu.ts
3377
3592
  function palsuText(text) {
3378
3593
  return { role: "assistant", content: text ? [{ type: "text", text }] : [] };
@@ -3528,8 +3743,11 @@ export {
3528
3743
  EventStream,
3529
3744
  GGAIError,
3530
3745
  ProviderError,
3746
+ REDACTED as REDACTION_MARKER,
3531
3747
  StreamResult,
3748
+ clampProviderContextImages,
3532
3749
  classifyProviderError,
3750
+ environmentSecrets,
3533
3751
  formatError,
3534
3752
  formatErrorForDisplay,
3535
3753
  isHardBillingMessage,
@@ -3540,6 +3758,8 @@ export {
3540
3758
  palsuToolCall,
3541
3759
  prewarmAnthropicCache,
3542
3760
  providerRegistry,
3761
+ redactText,
3762
+ redactValue,
3543
3763
  registerPalsuProvider,
3544
3764
  setProviderDiagnostic,
3545
3765
  stream,