export function redactText(value: string): string { return value .replace(/1[3-9]\d{9}/g, '[PHONE]') .replace(/\b\d{17}[\dXx]\b/g, '[ID_CARD]') .replace(/Bearer\s+[A-Za-z0-9._~+/-]+=*/gi, 'Bearer [REDACTED]') .replace(/(sk-|api[_-]?key["'=:\s]+)[A-Za-z0-9._-]{8,}/gi, '$1[REDACTED]'); } export function makeRedactingReplacer(redact: (value: string) => string) { return (key: string, value: unknown): unknown => { if (/password|token|secret|api.?key|authorization|phone|mobile|id.?card|身份证/i.test(key)) { return '[REDACTED]'; } if (typeof value === 'string') return redact(value); return value; }; } export function parseToolArguments(value: string): unknown { try { return JSON.parse(value || '{}') as unknown; } catch { return null; } } export function safeToolName(name: string): string { return name.replace(/[^a-zA-Z0-9_]/g, '_').slice(0, 64) || '_invalid'; } export function throwIfAborted(signal: AbortSignal): void { if (signal.aborted) throw signal.reason ?? new Error('aborted'); } export function errorCode(error: unknown): string { if (error && typeof error === 'object' && 'status' in error) { const status = Number(error.status); if (status === 408) return 'UPSTREAM_TIMEOUT'; if (status >= 400 && status < 500) return 'UPSTREAM_REQUEST_ERROR'; } return 'UPSTREAM_ERROR'; } export function assertGeneratedLength(reasoning: string, content: string): void { if (reasoning.length + content.length > 256 * 1024) { throw new Error('AI response exceeded limit'); } }