@prestyj/ai 5.5.0 → 5.7.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, EZCoderAIError, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, 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, EZCoderAIError, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, 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, EZCoderAIError, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, 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, EZCoderAIError, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, 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)";
@@ -1582,7 +1650,8 @@ function toError(err) {
1582
1650
  const bodyMessage = typeof nestedError?.message === "string" && nestedError.message.trim() ? nestedError.message.trim() : typeof errorBody?.message === "string" && errorBody.message.trim() ? errorBody.message.trim() : void 0;
1583
1651
  const bodyType = typeof nestedError?.type === "string" ? nestedError.type : typeof errorBody?.type === "string" ? errorBody.type : typeof err.type === "string" ? err.type : void 0;
1584
1652
  const fallbackMessage = isRawJsonErrorEcho(err.message) ? emptyProviderErrorMessage(err.status) : err.message;
1585
- const message = bodyType && bodyMessage ? `${bodyType}: ${bodyMessage}` : bodyMessage ?? fallbackMessage;
1653
+ const messageCandidate = bodyMessage ?? err.message;
1654
+ const message = isRawHtmlErrorEcho(messageCandidate) ? providerHtmlErrorMessage(err.status) : bodyType && bodyMessage ? `${bodyType}: ${bodyMessage}` : bodyMessage ?? fallbackMessage;
1586
1655
  if (err.status === 429) {
1587
1656
  const limit = readUnifiedRateLimit(err.headers);
1588
1657
  const farOff = limit.resetsAt != null && limit.resetsAt * 1e3 - Date.now() > 6e4;
@@ -2050,7 +2119,8 @@ function toError2(err, provider = "openai") {
2050
2119
  const body = err.error;
2051
2120
  const bodyMessage = typeof body?.message === "string" && body.message.trim() ? body.message.trim() : void 0;
2052
2121
  const modelName = typeof body?.model === "string" ? body.model : "";
2053
- const cleanMessage = bodyMessage ?? (isRawJsonErrorEcho(err.message) ? emptyProviderErrorMessage(err.status) : err.message);
2122
+ const messageCandidate = bodyMessage ?? err.message;
2123
+ const cleanMessage = isRawHtmlErrorEcho(messageCandidate) ? providerHtmlErrorMessage(err.status) : bodyMessage ? bodyMessage : isRawJsonErrorEcho(err.message) ? emptyProviderErrorMessage(err.status) : err.message;
2054
2124
  let hint;
2055
2125
  if (modelName === "codex-mini-latest" || cleanMessage.includes("codex-mini-latest")) {
2056
2126
  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.";
@@ -2164,6 +2234,24 @@ function outputTextKey(itemId, contentIndex) {
2164
2234
  function isVisibleOutputItem(itemType) {
2165
2235
  return itemType === "message";
2166
2236
  }
2237
+ function toCodexToolChoice(choice, tools) {
2238
+ const resolved = choice ?? "auto";
2239
+ if (typeof resolved === "object") {
2240
+ throw new EZCoderAIError(
2241
+ `OpenAI Codex does not support selecting the named tool \`${resolved.name}\`; use auto, none, or required.`,
2242
+ { source: "capability" }
2243
+ );
2244
+ }
2245
+ if (resolved === "required" && !tools?.length) {
2246
+ throw new EZCoderAIError(
2247
+ "OpenAI Codex cannot require a tool call when no tools are configured.",
2248
+ {
2249
+ source: "capability"
2250
+ }
2251
+ );
2252
+ }
2253
+ return resolved;
2254
+ }
2167
2255
  function streamOpenAICodex(options) {
2168
2256
  return new StreamResult(runStream3(options), options.signal);
2169
2257
  }
@@ -2180,7 +2268,7 @@ async function* runStream3(options) {
2180
2268
  stream: true,
2181
2269
  instructions: system,
2182
2270
  input,
2183
- tool_choice: "auto",
2271
+ tool_choice: toCodexToolChoice(options.toolChoice, options.tools),
2184
2272
  parallel_tool_calls: !responsesLite,
2185
2273
  include: ["reasoning.encrypted_content"]
2186
2274
  };
@@ -2213,8 +2301,9 @@ async function* runStream3(options) {
2213
2301
  headers["chatgpt-account-id"] = options.accountId;
2214
2302
  }
2215
2303
  if (options.transportSessionId) {
2216
- headers["session_id"] = options.transportSessionId;
2217
- headers["x-client-request-id"] = options.transportSessionId;
2304
+ const transportSessionId = normalizePromptCacheKey(options.transportSessionId);
2305
+ headers["session_id"] = transportSessionId;
2306
+ headers["x-client-request-id"] = transportSessionId;
2218
2307
  }
2219
2308
  const response = await fetch(url, {
2220
2309
  method: "POST",
@@ -2224,7 +2313,7 @@ async function* runStream3(options) {
2224
2313
  });
2225
2314
  if (!response.ok) {
2226
2315
  const text = await response.text().catch(() => "");
2227
- const parsed = parseCodexErrorBody(text);
2316
+ const parsed = parseCodexErrorBody(text, response.status);
2228
2317
  const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
2229
2318
  const requestId = parsed.requestId ?? readHeader(response.headers, "x-request-id", "openai-request-id", "x-oai-request-id");
2230
2319
  const usageLimit = codexUsageLimitError(parsed.errorObj, response.status, requestId);
@@ -2618,13 +2707,14 @@ function toCodexTools(tools) {
2618
2707
  strict: null
2619
2708
  }));
2620
2709
  }
2621
- function parseCodexErrorBody(text) {
2710
+ function parseCodexErrorBody(text, statusCode) {
2622
2711
  if (!text) return {};
2623
2712
  try {
2624
2713
  const parsed = JSON.parse(text);
2625
2714
  const error = parsed.error;
2626
2715
  const detail = parsed.detail;
2627
- const message = error?.message ?? parsed.message ?? (typeof detail === "string" ? detail : void 0);
2716
+ const rawMessage = error?.message ?? parsed.message ?? (typeof detail === "string" ? detail : void 0);
2717
+ const message = rawMessage && isRawHtmlErrorEcho(rawMessage) ? providerHtmlErrorMessage(statusCode) : rawMessage;
2628
2718
  const requestId = parsed.request_id ?? error?.request_id ?? (message ? extractRequestIdFromMessage(message) : void 0);
2629
2719
  const errorObj = error ?? parsed;
2630
2720
  return {
@@ -2633,8 +2723,12 @@ function parseCodexErrorBody(text) {
2633
2723
  ...errorObj ? { errorObj } : {}
2634
2724
  };
2635
2725
  } catch {
2636
- const trimmed = text.trim().slice(0, 240);
2637
- return trimmed ? { message: trimmed } : {};
2726
+ const trimmed = text.trim();
2727
+ if (isRawHtmlErrorEcho(trimmed)) {
2728
+ return { message: providerHtmlErrorMessage(statusCode) };
2729
+ }
2730
+ const bounded = trimmed.slice(0, 240);
2731
+ return bounded ? { message: bounded } : {};
2638
2732
  }
2639
2733
  }
2640
2734
  var CODEX_USAGE_LIMIT_CODE = /usage_limit_reached|usage_not_included/i;
@@ -3275,7 +3369,12 @@ function stream(options) {
3275
3369
  if (options.supportsVideo !== true && messagesContainVideo(options.messages)) {
3276
3370
  throw new VideoUnsupportedError();
3277
3371
  }
3278
- return entry.stream(options);
3372
+ const messages = clampProviderContextImages(
3373
+ options.messages,
3374
+ options.provider,
3375
+ options.supportsImages
3376
+ );
3377
+ return entry.stream(messages === options.messages ? options : { ...options, messages });
3279
3378
  }
3280
3379
  function messagesContainVideo(messages) {
3281
3380
  for (const msg of messages) {
@@ -3380,6 +3479,125 @@ Original: ${message}`;
3380
3479
  return message;
3381
3480
  }
3382
3481
 
3482
+ // src/redaction.ts
3483
+ var REDACTED = "[REDACTED]";
3484
+ var TRUNCATED = "[TRUNCATED]";
3485
+ var CIRCULAR = "[CIRCULAR]";
3486
+ var SENSITIVE_NAME = /(?:^|[_-])(?:api[_-]?key|access[_-]?token|refresh[_-]?token|token|key|auth(?:orization)?|bearer|cookie|credential|private[_-]?key|password|passwd|secret)(?:$|[_-])/i;
3487
+ 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;
3488
+ function escaped(value) {
3489
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3490
+ }
3491
+ function normalizedSecrets(secrets) {
3492
+ if (!secrets) return [];
3493
+ return [...new Set([...secrets].filter((value) => value.length >= 8 && value !== REDACTED))].sort(
3494
+ (a, b) => b.length - a.length
3495
+ );
3496
+ }
3497
+ function environmentSecrets(env) {
3498
+ const values = /* @__PURE__ */ new Set();
3499
+ for (const [name, value] of Object.entries(env)) {
3500
+ if (!value || value.length < 8 || value === REDACTED || !SENSITIVE_NAME.test(name)) continue;
3501
+ values.add(value);
3502
+ }
3503
+ return [...values].sort((a, b) => b.length - a.length);
3504
+ }
3505
+ function redactText(text, options = {}) {
3506
+ let result = text;
3507
+ result = result.replace(
3508
+ /-----BEGIN (?:[A-Z0-9 ]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z0-9 ]+ )?PRIVATE KEY-----/g,
3509
+ REDACTED
3510
+ );
3511
+ result = result.replace(/\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+:[^\s/@]+@/gi, `$1${REDACTED}@`);
3512
+ result = result.replace(
3513
+ /\b(authorization\s*[:=]\s*)(?:bearer|basic)\s+[^\s,;]+/gi,
3514
+ `$1${REDACTED}`
3515
+ );
3516
+ result = result.replace(/\b(bearer|basic)\s+[A-Za-z0-9+/_.=-]{8,}/gi, `$1 ${REDACTED}`);
3517
+ result = result.replace(/\b(cookie|set-cookie)(\s*[:=]\s*)[^\r\n]+/gi, `$1$2${REDACTED}`);
3518
+ result = result.replace(
3519
+ /\beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\b/g,
3520
+ REDACTED
3521
+ );
3522
+ result = result.replace(
3523
+ /\b(?:sk-(?:ant-|proj-)?|xox[baprs]-|gh[pousr]_|github_pat_|AIza)[A-Za-z0-9_-]{12,}\b/g,
3524
+ REDACTED
3525
+ );
3526
+ result = result.replace(
3527
+ SENSITIVE_ASSIGNMENT,
3528
+ (_match, name, separator) => `${name}${separator}${REDACTED}`
3529
+ );
3530
+ for (const secret of normalizedSecrets(options.secrets)) {
3531
+ result = result.replace(new RegExp(escaped(secret), "g"), REDACTED);
3532
+ }
3533
+ const maxStringLength = options.maxStringLength ?? 1e6;
3534
+ if (result.length > maxStringLength) {
3535
+ result = `${result.slice(0, maxStringLength)}${TRUNCATED}`;
3536
+ }
3537
+ return result;
3538
+ }
3539
+ function isBinary(value) {
3540
+ return value instanceof ArrayBuffer || ArrayBuffer.isView(value) || typeof Blob !== "undefined" && value instanceof Blob;
3541
+ }
3542
+ function isMediaObject(value) {
3543
+ return (value.type === "image" || value.type === "video") && (typeof value.data === "string" || typeof value.url === "string");
3544
+ }
3545
+ function redactValue(value, options = {}) {
3546
+ const maxDepth = options.maxDepth ?? 20;
3547
+ const maxEntries = options.maxEntries ?? 1e4;
3548
+ const seen = /* @__PURE__ */ new WeakSet();
3549
+ let entries = 0;
3550
+ const visit = (current, depth, sensitive = false) => {
3551
+ if (typeof current === "string") {
3552
+ if (sensitive && current.length > 0 && current !== REDACTED) return REDACTED;
3553
+ return redactText(current, options);
3554
+ }
3555
+ if (current === null || current === void 0 || typeof current === "number" || typeof current === "boolean" || typeof current === "bigint") {
3556
+ return current;
3557
+ }
3558
+ if (typeof current !== "object") return current;
3559
+ if (isBinary(current)) return current;
3560
+ if (current instanceof Date) return new Date(current.getTime());
3561
+ if (depth >= maxDepth) return TRUNCATED;
3562
+ if (seen.has(current)) return CIRCULAR;
3563
+ seen.add(current);
3564
+ if (current instanceof Error) {
3565
+ const error = {
3566
+ name: current.name,
3567
+ message: visit(current.message, depth + 1),
3568
+ stack: visit(current.stack, depth + 1)
3569
+ };
3570
+ for (const [key, child] of Object.entries(current)) {
3571
+ error[key] = visit(child, depth + 1, SENSITIVE_NAME.test(key));
3572
+ }
3573
+ return error;
3574
+ }
3575
+ if (Array.isArray(current)) {
3576
+ const clone2 = [];
3577
+ for (const child of current) {
3578
+ if (++entries > maxEntries) {
3579
+ clone2.push(TRUNCATED);
3580
+ break;
3581
+ }
3582
+ clone2.push(visit(child, depth + 1));
3583
+ }
3584
+ return clone2;
3585
+ }
3586
+ const record = current;
3587
+ if (isMediaObject(record)) return { ...record };
3588
+ const clone = {};
3589
+ for (const [key, child] of Object.entries(record)) {
3590
+ if (++entries > maxEntries) {
3591
+ clone[TRUNCATED] = true;
3592
+ break;
3593
+ }
3594
+ clone[key] = visit(child, depth + 1, SENSITIVE_NAME.test(key));
3595
+ }
3596
+ return clone;
3597
+ };
3598
+ return visit(value, 0);
3599
+ }
3600
+
3383
3601
  // src/providers/palsu.ts
3384
3602
  function palsuText(text) {
3385
3603
  return { role: "assistant", content: text ? [{ type: "text", text }] : [] };
@@ -3535,8 +3753,11 @@ export {
3535
3753
  EZCoderAIError,
3536
3754
  EventStream,
3537
3755
  ProviderError,
3756
+ REDACTED as REDACTION_MARKER,
3538
3757
  StreamResult,
3758
+ clampProviderContextImages,
3539
3759
  classifyProviderError,
3760
+ environmentSecrets,
3540
3761
  formatError,
3541
3762
  formatErrorForDisplay,
3542
3763
  isHardBillingMessage,
@@ -3547,6 +3768,8 @@ export {
3547
3768
  palsuToolCall,
3548
3769
  prewarmAnthropicCache,
3549
3770
  providerRegistry,
3771
+ redactText,
3772
+ redactValue,
3550
3773
  registerPalsuProvider,
3551
3774
  setProviderDiagnostic,
3552
3775
  stream,