@x-otto/provider 0.1.0-alpha.4 → 0.1.0-alpha.41

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.ts CHANGED
@@ -1,6 +1,57 @@
1
1
  import { Api, Api as Api$1, AssistantMessage, AssistantMessage as AssistantMessage$1, ContentPart, ImageContent, Message, Message as Message$1, Model, Model as Model$1, Provider, Provider as Provider$1, ProviderUsageSnapshot, StopReason, StreamEvent, StreamEvent as StreamEvent$1, TextContent, ThinkingContent, ThinkingLevel, ToolCall, ToolResultMessage, UserMessage, ZodType } from "@x-otto/interchange";
2
2
  import { LoopbackCallback, LoopbackServer, LoopbackServerOptions, PkcePair, createPkcePair, generateChallenge, generateState, generateVerifier, startLoopbackServer } from "@x-otto/shared";
3
3
 
4
+ //#region src/retry-policy.d.ts
5
+ /** RFC-454: plugins decide; the engine owns retry execution and accounting. */
6
+ interface RetryDecisionContext {
7
+ readonly executionApi: string;
8
+ readonly modelId: string;
9
+ readonly nowMs: number;
10
+ readonly retriesUsed: number;
11
+ readonly error: Readonly<{
12
+ status?: number;
13
+ code?: string;
14
+ retryAfterMs?: number;
15
+ }>;
16
+ readonly defaults: Readonly<{
17
+ maxRetries: number;
18
+ delayMs?: number;
19
+ }>;
20
+ readonly limits: Readonly<{
21
+ maxRetries: number;
22
+ maxDelayMs: number;
23
+ }>;
24
+ }
25
+ type RetryDecision = {
26
+ action: 'delegate';
27
+ } | {
28
+ action: 'stop';
29
+ reason?: string;
30
+ } | {
31
+ action: 'retry';
32
+ maxRetries?: number;
33
+ schedule: {
34
+ kind: 'default';
35
+ } | {
36
+ kind: 'at';
37
+ notBeforeMs: number;
38
+ };
39
+ reason?: string;
40
+ };
41
+ type TurnRetryPolicy = (context: RetryDecisionContext) => RetryDecision;
42
+ interface ProviderRetryPolicy {
43
+ createTurnPolicy(): TurnRetryPolicy;
44
+ }
45
+ /** A turn retains its policy snapshot; revocation still invalidates its authority. */
46
+ interface ProviderRetryPolicyBinding {
47
+ readonly source: string;
48
+ readonly policy: ProviderRetryPolicy;
49
+ isActive(): boolean;
50
+ }
51
+ /** Async permits the host to activate a lazy provider plugin before the first request. */
52
+ type ResolveRetryPolicy = (executionApi: string) => ProviderRetryPolicyBinding | undefined | Promise<ProviderRetryPolicyBinding | undefined>;
53
+ declare const MAX_PLUGIN_RETRIES = 100;
54
+ //#endregion
4
55
  //#region src/types.d.ts
5
56
  type StreamFunction = (context: StreamContext, signal: AbortSignal) => AsyncIterable<StreamEvent$1> & {
6
57
  result(): Promise<AssistantMessage$1>;
@@ -141,13 +192,18 @@ interface WireProtocolLogger {
141
192
  warn: (objOrMsg: string | Record<string, unknown>, ...args: unknown[]) => void;
142
193
  error: (objOrMsg: string | Record<string, unknown>, ...args: unknown[]) => void;
143
194
  }
195
+ /**
196
+ * `WireProtocolLogger` 的 no-op 默认实现(RFC-185 D3)——宿主未注入 logger 时的缺省值。
197
+ * 单一真源:此前 otto-wire-protocols 三协议 + plugin-github-copilot 各复制一份逐字相同的
198
+ * 定义(4 份),默认实现若需演进(如 debug 门控)要改四处。协议/provider 插件经
199
+ * `@x-otto/provider` 复用此单例,不再复制。
200
+ */
201
+ declare const noopWireProtocolLogger: WireProtocolLogger;
144
202
  interface WireProtocolFactoryInput {
145
- resolveAuth: () => Promise<{
146
- token: string;
147
- mode: 'api_key' | 'oauth';
148
- beta?: string;
149
- } | undefined>;
203
+ resolveAuth: () => Promise<ResolvedAuth | undefined>;
150
204
  resolveBaseUrl: () => Promise<string | undefined>;
205
+ /** 声明式 provider 已在 resolver 内完成进程/配置/凭据优先级,协议层不得再抢先读旧 env。 */
206
+ preferResolvedBaseUrl?: boolean;
151
207
  headers?: Record<string, string>;
152
208
  tokenDerivedHeaders?: TokenDerivedHeader[];
153
209
  /** 请求定制点——厂商个性注入唯一通道(如 plugin-anthropic 的 Claude Code 伪装)。 */
@@ -238,6 +294,12 @@ interface ResolvedAuth {
238
294
  /** OAuth 模式下需附带的 beta 头(来源于 OAuthProvider.oauthBeta)。 */
239
295
  beta?: string;
240
296
  }
297
+ /**
298
+ * 统一 baseUrl 解析(env > resolver > model.baseUrl)——三协议此前各写一份,
299
+ * 且 openai-completions 根本没有 env 覆盖层(DEEPSEEK_BASE_URL 静默无效)。
300
+ * 优先级一致:env key 优先、resolver 次之、model.baseUrl 兜底。
301
+ */
302
+ declare function resolveProviderBaseUrl(envKey: string, resolver: (() => Promise<string | undefined>) | undefined, modelBaseUrl: string, preferResolver?: boolean): Promise<string>;
241
303
  //#endregion
242
304
  //#region src/provider/chat-completions.d.ts
243
305
  declare function buildChatCompletionsMessages(context: StreamContext): unknown[];
@@ -531,7 +593,14 @@ declare function assembleAssistantMessage(meta: AssistantMessageMeta, content: A
531
593
  * 直接 `JSON.stringify` 会产出非法 UTF-8 被服务端 400 拒。
532
594
  */
533
595
  declare function sanitizeSurrogates(text: string): string;
534
- /** 把带可选 `toJSONSchema()` 的 schema 对象转为 JSON Schema;无该方法时退回空对象。 */
596
+ /**
597
+ * 把 schema 转为 JSON Schema,支持双形态:
598
+ * - 带 `toJSONSchema()` 的对象(zod 及 interchange ZodType 垫片)→ 调方法;
599
+ * - 纯 JSON Schema 字面对象(嵌入宿主/外部工具常见形态)→ 原样透传。
600
+ *
601
+ * 旧行为对纯 JSON Schema 静默降级成 `{}`(模型看到零参工具、瞎猜参数),
602
+ * 且不报错——嵌入场景实测过的坑,此处按对象形态透传消除该暗礁。
603
+ */
535
604
  declare function toJsonSchema(schema: {
536
605
  toJSONSchema?: () => unknown;
537
606
  }): unknown;
@@ -580,7 +649,7 @@ declare function buildRequestHeaders(sources: RequestHeaderSources): Record<stri
580
649
  *
581
650
  * 提取规则:
582
651
  * - 先查命名空间 `claims[namespace]?.[claim]`,未命中回退顶层 `claims[claim]`
583
- * - 按 `tokenSources` 数组顺序逐个 token 尝试,任一命中即返回
652
+ * - 按 `tokens` 数组顺序逐个 token 尝试,任一命中即返回
584
653
  * - 解析失败(非 JWT 格式/不透明 token)静默返回 `undefined`
585
654
  *
586
655
  * @param tokens - 待扫描的 JWT token 数组(如 `[idToken, accessToken]`)
@@ -606,6 +675,16 @@ interface PKCEOAuthTemplateParams {
606
675
  * 对齐 Anthropic 的 54545 白名单端口机制与 Codex 的 [1455,1457] 多端口。
607
676
  */
608
677
  loopbackPorts?: number[];
678
+ /**
679
+ * loopback 回调**路径**(厂商在授权服务器白名单里注册的 exact 值)。
680
+ * 缺省 `/auth/callback`(官方 Codex CLI 注册值);Anthropic 注册的是 `/callback`,
681
+ * 由 `plugin-anthropic` manifest 显式声明。
682
+ *
683
+ * 此前本模板把 Anthropic 的 `/callback` 硬编码进骨架,导致所有 PKCE 插件(含
684
+ * codex)都被迫使用它——codex 的注册值是 `/auth/callback`,授权服务器会以
685
+ * "Redirect URI is not supported by client" 拒绝,登录必失败。
686
+ */
687
+ callbackPath?: string;
609
688
  /**
610
689
  * loopback 是否假定始终可用、不提供手动粘贴兜底。
611
690
  * `false`(缺省,对齐 Anthropic)= 端口被占时降级手动粘贴框;
@@ -653,6 +732,7 @@ interface DeviceFlowOAuthTemplateParams {
653
732
  *
654
733
  * 行为与终态参数由 `PKCEOAuthTemplateParams` 完全控制:
655
734
  * - loopback 端口按列表顺序回退(`assumesLoopbackAlways:true` 时不提供手动粘贴兜底)
735
+ * - loopback 回调路径由 `callbackPath` 声明(缺省 `/auth/callback`,不再硬编码厂商路径)
656
736
  * - authorize URL 上拼装额外 query 参数(`extraAuthParams`)
657
737
  * - token 端点请求体编码由 `tokenExchangeEncoding` 决定(`json` vs `form`)
658
738
  *
@@ -687,14 +767,10 @@ declare function DeviceFlowOAuthTemplate(params: DeviceFlowOAuthTemplateParams):
687
767
  * Header dump:debug 级别输出全部响应头 key+value,供实证分析。
688
768
  */
689
769
  declare function extractStandardRateLimitHeaders(headers: Headers | undefined): ProviderUsageSnapshot | null;
690
- /**
691
- * @deprecated 改名为 {@link extractStandardRateLimitHeaders}(终局架构 review
692
- * 2026-07-21:函数只解析厂商无关的标准 x-ratelimit-* 头族,"OpenAi" 后缀具有误导性)。
693
- * 保留别名过渡,下个大版本移除。
694
- */
695
- declare const extractProviderUsageFromOpenAiHeaders: typeof extractStandardRateLimitHeaders;
696
770
  //#endregion
697
771
  //#region src/stream-errors.d.ts
772
+ /** 从错误链上尽力取 HTTP 状态码(provider 包装的 cause、或裸 SDK 错误的 .status/.statusCode)。 */
773
+ declare function extractStatus(error: unknown): number | undefined;
698
774
  /**
699
775
  * 上下文溢出(413 / "prompt too long" 类)——本轮请求因上下文超限被服务端拒绝。
700
776
  * 与一般可重试错误(429/5xx/断连)不同:重发同样请求仍会失败,必须先压缩上下文。
@@ -721,6 +797,7 @@ declare function extractAnthropicRateLimit(error: unknown): {
721
797
  type?: 'five_hour' | 'seven_day' | 'seven_day_opus' | 'seven_day_sonnet' | 'seven_day_overage_included';
722
798
  resetsAt?: number;
723
799
  } | null;
800
+ declare function extractCode(error: unknown): string | undefined;
724
801
  /**
725
802
  * RFC-361:从错误(含 cause 链)里提取 SSE 流的 abort 触发源。
726
803
  *
@@ -802,6 +879,12 @@ declare function isAuthError(error: unknown): boolean;
802
879
  type AuthErrorType = 'credential-missing' | 'refresh-failed' | 'server-rejected';
803
880
  declare function classifyAuthErrorType(error: unknown): AuthErrorType;
804
881
  declare function classifyStreamError(error: unknown): StreamErrorClass;
882
+ /**
883
+ * 有明确拒绝依据的 fatal 错误才禁止回合级自动重投(RFC-147 / RFC-442)。
884
+ * 引擎对未知错误仍默认 fatal,但这不代表重投无益;只读取结构化的状态、鉴权码和
885
+ * 供应商 errorType(请求无效 / 凭据无效 / 权限不足 / 配额不足),不从文案猜测。
886
+ */
887
+ declare function isNonRetryableStreamError(error: unknown): boolean;
805
888
  /**
806
889
  * 从错误链上取 `Retry-After`(秒)或 `retry-after-ms`(毫秒)头,返回毫秒。
807
890
  * 仅处理数值形式(最常见);HTTP-date 形式返回 undefined 交由退避计算。
@@ -861,5 +944,4 @@ declare class ReplayProviderStream implements ProviderStream {
861
944
  }
862
945
  declare function createReplayProviderFactory(filePath: string, options?: ReplayOptions): () => ProviderStream;
863
946
  //#endregion
864
- export { type Api, type AssistantContent, type AssistantMessage, type AssistantMessageMeta, type AuthErrorType, type AuthMode, type CacheRetention, type ChatCompletionsStreamParams, type ContentPart, DeviceFlowOAuthTemplate, type DeviceFlowOAuthTemplateParams, type FlatStreamState, type FlatToolCallAcc, type ImageContent, type LoopbackCallback, type LoopbackServer, type LoopbackServerOptions, type Message, type Model, type OAuthCredentials, type OAuthInfo, type OAuthLoginOptions, type OAuthPrompt, type OAuthProvider, type OAuthProviderFactory, type OAuthProviderId, type OAuthRefreshTokenOptions, PKCEOAuthTemplate, type PKCEOAuthTemplateParams, type PkcePair, type Provider, type ProviderFactory, type ProviderStream, REPLAY_API, type Recording, type RecordingHeader, type RecordingLine, type ReplayOptions, ReplayProviderStream, type RequestHeaderSources, type ResolvedAuth, type StopReason, type StreamBlock, type StreamContext, type StreamErrorClass, type StreamEvent, type StreamFunction, type StreamOptions, type StreamUsage, type SystemPromptSegment, type TextContent, type ThinkingContent, type TokenDerivedHeader, type ToolCall, type ToolDefinition, type ToolResultMessage, type UserMessage, type WireProtocolFactoryInput, type WireProtocolLogger, type WireProtocolMeta, type WireProtocolRegistration, type WireRequestCustomization, type WireRequestCustomizeInput, type WireSystemTextBlock, assembleAssistantMessage, assertSystemNotificationPlacement, blocksContent, buildChatCompletionsBody, buildChatCompletionsMessages, buildReplayModel, buildRequestHeaders, clampThinkingLevel, classifyAuthErrorType, classifyStreamError, createPkcePair, createReplayProviderFactory, degradedSystemNotificationText, extractAbortKind, extractAnthropicRateLimit, extractJwtClaim, extractProviderUsageFromOpenAiHeaders, extractRetryAfterMs, extractStandardRateLimitHeaders, flatContent, generateChallenge, generateState, generateVerifier, hasTrailingInvokeXml, initFlatStreamState, initStreamUsage, isAuthError, isContextOverflowError, isNetworkDisconnectError, loadRecording, mapChatFinishReason, mergeConsecutiveSystemNotifications, parseToolArguments, parseToolArgumentsWithStatus, recordStream, recordingsDir, recoverLiteralInvokeToolCalls, registeredToolNames, replayStream, sanitizeSurrogates, sanitizeToolRoundtrips, startLoopbackServer, streamChatCompletions, toJsonSchema, toolCallFromParsed };
865
- //# sourceMappingURL=index.d.ts.map
947
+ export { type Api, type AssistantContent, type AssistantMessage, type AssistantMessageMeta, type AuthErrorType, type AuthMode, type CacheRetention, type ChatCompletionsStreamParams, type ContentPart, DeviceFlowOAuthTemplate, type DeviceFlowOAuthTemplateParams, type FlatStreamState, type FlatToolCallAcc, type ImageContent, type LoopbackCallback, type LoopbackServer, type LoopbackServerOptions, MAX_PLUGIN_RETRIES, type Message, type Model, type OAuthCredentials, type OAuthInfo, type OAuthLoginOptions, type OAuthPrompt, type OAuthProvider, type OAuthProviderFactory, type OAuthProviderId, type OAuthRefreshTokenOptions, PKCEOAuthTemplate, type PKCEOAuthTemplateParams, type PkcePair, type Provider, type ProviderFactory, type ProviderRetryPolicy, type ProviderRetryPolicyBinding, type ProviderStream, REPLAY_API, type Recording, type RecordingHeader, type RecordingLine, type ReplayOptions, ReplayProviderStream, type RequestHeaderSources, type ResolveRetryPolicy, type ResolvedAuth, type RetryDecision, type RetryDecisionContext, type StopReason, type StreamBlock, type StreamContext, type StreamErrorClass, type StreamEvent, type StreamFunction, type StreamOptions, type StreamUsage, type SystemPromptSegment, type TextContent, type ThinkingContent, type TokenDerivedHeader, type ToolCall, type ToolDefinition, type ToolResultMessage, type TurnRetryPolicy, type UserMessage, type WireProtocolFactoryInput, type WireProtocolLogger, type WireProtocolMeta, type WireProtocolRegistration, type WireRequestCustomization, type WireRequestCustomizeInput, type WireSystemTextBlock, assembleAssistantMessage, assertSystemNotificationPlacement, blocksContent, buildChatCompletionsBody, buildChatCompletionsMessages, buildReplayModel, buildRequestHeaders, clampThinkingLevel, classifyAuthErrorType, classifyStreamError, createPkcePair, createReplayProviderFactory, degradedSystemNotificationText, extractAbortKind, extractAnthropicRateLimit, extractCode, extractJwtClaim, extractRetryAfterMs, extractStandardRateLimitHeaders, extractStatus, flatContent, generateChallenge, generateState, generateVerifier, hasTrailingInvokeXml, initFlatStreamState, initStreamUsage, isAuthError, isContextOverflowError, isNetworkDisconnectError, isNonRetryableStreamError, loadRecording, mapChatFinishReason, mergeConsecutiveSystemNotifications, noopWireProtocolLogger, parseToolArguments, parseToolArgumentsWithStatus, recordStream, recordingsDir, recoverLiteralInvokeToolCalls, registeredToolNames, replayStream, resolveProviderBaseUrl, sanitizeSurrogates, sanitizeToolRoundtrips, startLoopbackServer, streamChatCompletions, toJsonSchema, toolCallFromParsed };
package/dist/index.js CHANGED
@@ -1,6 +1,10 @@
1
- import{normalizeImageData as e}from"@x-otto/interchange";import{OAuthError as t,ProviderError as n,createLogger as r,createPkcePair as i,generateChallenge as a,generateState as o,generateVerifier as s,headerGet as c,startLoopbackServer as l,stream as u}from"@x-otto/shared";import{createWriteStream as d,mkdirSync as f}from"node:fs";import{readFile as p}from"node:fs/promises";import{join as m}from"node:path";const h=r(`@x-otto/provider:tool-roundtrip`);function g(e){let t=[],n=[],r=()=>{for(let e of n)h.warn({toolCallId:e.id,toolName:e.name},`Synthesizing placeholder for unanswered tool_call`),t.push(_(e));n=[]};for(let i of e){if(i.role===`tool_result`){let e=n.findIndex(e=>e.id===i.toolCallId);if(e===-1){h.warn({toolCallId:i.toolCallId,toolName:i.toolName},`Dropping orphan tool_result with no preceding tool_call`);continue}n.splice(e,1),t.push(i);continue}r(),t.push(i),i.role===`assistant`&&(n=i.content.filter(e=>e.type===`tool_call`))}return r(),t}function _(e){return{role:`tool_result`,toolCallId:e.id,toolName:e.name,content:[{type:`text`,text:`[tool result unavailable]`}],details:void 0,isError:!0,timestamp:0}}function v(e){return`[System notification] ${e}`}function y(e){let t=[];for(let n of e){let e=t.at(-1);if(n.role===`system_notification`&&e?.role===`system_notification`){t[t.length-1]={...e,content:`${e.content}\n${n.content}`,isError:e.isError||n.isError};continue}t.push(n)}return t}function b(e){for(let t=0;t<e.length;t++){let n=e[t];if(n.role!==`system_notification`)continue;let r=t>0?e[t-1]:void 0;r!==void 0&&(r.role===`tool_result`||r.role===`assistant`)||h.warn({index:t,relatedToolCallId:n.relatedToolCallId,precedingRole:r?.role??`(start of array)`},`system_notification message does not immediately follow a tool_result/assistant turn (Anthropic placement rule violation risk) — sending anyway, provider may reject`);let i=t+1<e.length?e[t+1]:void 0;i===void 0||i.role===`assistant`||h.warn({index:t,relatedToolCallId:n.relatedToolCallId,followingRole:i?.role},`system_notification message is not the last entry and is not immediately followed by an assistant turn (Anthropic placement rule violation risk) — sending anyway, provider may reject`)}}function x(e){return e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,`�`)}function ee(e){return e.toJSONSchema?.()??{}}const S=[`low`,`medium`,`high`,`xhigh`,`max`];function C(e,t){if(!t||t.length===0||t.includes(e))return e;let n=S.indexOf(e),r=[...t].sort((e,t)=>S.indexOf(e)-S.indexOf(t)),i;for(let e of r)if(S.indexOf(e)<=n)i=e;else break;return i??r[0]}function te(e){for(let t of e)if(t.role===`tool_result`||t.role===`assistant`&&t.content.some(e=>e.type===`tool_call`))return!0;return!1}function w(t){let n=[];t.systemPrompt&&n.push({role:`system`,content:x(t.systemPrompt)});for(let r of g(t.messages))if(r.role===`user`)typeof r.content==`string`?n.push({role:`user`,content:x(r.content)}):n.push({role:`user`,content:r.content.map(t=>{if(t.type===`text`)return{type:`text`,text:x(t.text)};if(t.type===`image`){let{base64:n,mediaType:r,valid:i}=e(t.source,t.mime);return i?{type:`image_url`,image_url:{url:`data:${r||`image/png`};base64,${n}`}}:{type:`text`,text:`[image omitted: corrupted or empty data]`}}return{type:`text`,text:`[unsupported]`}})});else if(r.role===`assistant`){let e=r.content.filter(e=>e.type===`tool_call`).map(e=>({id:e.id,type:`function`,function:{name:e.name,arguments:JSON.stringify(e.arguments)}})),t=r.content.filter(e=>e.type===`text`).filter(e=>e.text.trim().length>0).map(e=>x(e.text)).join(``);if(!t&&e.length===0)continue;n.push({role:`assistant`,content:t||null,...e.length>0?{tool_calls:e}:{}})}else r.role===`tool_result`?n.push({role:`tool`,tool_call_id:r.toolCallId,content:x(r.content.map(e=>e.type===`text`?e.text:``).join(``))}):r.role===`system_notification`&&n.push({role:`user`,content:x(v(r.content))});return n}function ne(e){switch(e){case`low`:case`medium`:case`high`:return`high`;case`xhigh`:case`max`:return`max`;default:return`high`}}function re(e,t,n={}){let r={model:n.modelName??e.name,messages:w(t),max_tokens:t.maxTokens??e.maxOutputTokens,stream:!0,stream_options:{include_usage:!0}};return t.temperature!==void 0&&!e.reasoning&&(r.temperature=t.temperature),e.reasoning&&t.thinkingLevel&&(r.reasoning_effort=ne(C(t.thinkingLevel,e.thinkingLevels))),t.tools&&t.tools.length>0?r.tools=t.tools.map(e=>({type:`function`,function:{name:e.name,description:e.description,parameters:ee(e.parameters)}})):te(t.messages)&&(r.tools=[]),r}const T=r(`@x-otto/provider:openai-usage`);function ie(e){let t=t=>{let n=e.get(t);if(n===null)return;let r=Number(n);return Number.isFinite(r)?r:void 0},n=t=>{let n=e.get(t);if(!n)return;let r=n.match(/^(\d+(?:\.\d+)?)(m|s|ms)$/);if(!r)return;let i=Number(r[1]),a=r[2];if(a===`ms`)return i;if(a===`s`)return i*1e3;if(a===`m`)return i*60*1e3},r={remainingRequests:t(`x-ratelimit-remaining-requests`),remainingTokens:t(`x-ratelimit-remaining-tokens`),limitRequests:t(`x-ratelimit-limit-requests`),limitTokens:t(`x-ratelimit-limit-tokens`),resetRequestsMs:n(`x-ratelimit-reset-requests`),resetTokensMs:n(`x-ratelimit-reset-tokens`)};if(!Object.values(r).every(e=>e===void 0))return r}function E(e){if(!e)return null;let t=ie(e);if(T.isLevelEnabled(`trace`)){let t=[];e.forEach((e,n)=>{let r=n.startsWith(`x-ratelimit-`)||e.length<40?e:e.slice(0,20)+`…`;t.push(` ${n}: ${r}`)}),T.trace(`OpenAI Codex response headers (%d):
1
+ import{normalizeImageData as e}from"@x-otto/interchange";import{DEFAULT_LOOPBACK_CALLBACK_PATH as t,OAuthError as n,ProviderError as r,createLogger as i,createPkcePair as a,createPkcePair as o,generateChallenge as s,generateState as c,generateVerifier as l,headerGet as u,startLoopbackServer as d,startLoopbackServer as f,stream as p}from"@x-otto/shared";import{createWriteStream as m,mkdirSync as h}from"node:fs";import{readFile as g}from"node:fs/promises";import{join as _}from"node:path";const v=100,y={debug:()=>{},info:()=>{},warn:()=>{},error:()=>{}};async function b(e,t,n,r=!1){if(r&&t){let e=await t();if(e)return e}let i=process.env[e];if(i)return i;if(t){let e=await t();if(e)return e}return n}const x=i(`@x-otto/provider:tool-roundtrip`);function S(e){let t=[],n=[],r=()=>{for(let e of n)x.warn({toolCallId:e.id,toolName:e.name},`Synthesizing placeholder for unanswered tool_call`),t.push(C(e));n=[]};for(let i of e){if(i.role===`tool_result`){let e=n.findIndex(e=>e.id===i.toolCallId);if(e===-1){x.warn({toolCallId:i.toolCallId,toolName:i.toolName},`Dropping orphan tool_result with no preceding tool_call`);continue}n.splice(e,1),t.push(i);continue}r(),t.push(i),i.role===`assistant`&&(n=i.content.filter(e=>e.type===`tool_call`))}return r(),t}function C(e){return{role:`tool_result`,toolCallId:e.id,toolName:e.name,content:[{type:`text`,text:`[tool result unavailable]`}],details:void 0,isError:!0,timestamp:0}}function w(e){return`[System notification] ${e}`}function T(e){let t=[];for(let n of e){let e=t.at(-1);if(n.role===`system_notification`&&e?.role===`system_notification`){t[t.length-1]={...e,content:`${e.content}\n${n.content}`,isError:e.isError||n.isError};continue}t.push(n)}return t}function ee(e){for(let t=0;t<e.length;t++){let n=e[t];if(n.role!==`system_notification`)continue;let r=t>0?e[t-1]:void 0;r!==void 0&&(r.role===`tool_result`||r.role===`assistant`)||x.warn({index:t,relatedToolCallId:n.relatedToolCallId,precedingRole:r?.role??`(start of array)`},`system_notification message does not immediately follow a tool_result/assistant turn (Anthropic placement rule violation risk) — sending anyway, provider may reject`);let i=t+1<e.length?e[t+1]:void 0;i===void 0||i.role===`assistant`||x.warn({index:t,relatedToolCallId:n.relatedToolCallId,followingRole:i?.role},`system_notification message is not the last entry and is not immediately followed by an assistant turn (Anthropic placement rule violation risk) — sending anyway, provider may reject`)}}function E(e){return e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,`�`)}function D(e){return!e||typeof e!=`object`||Array.isArray(e)?{}:typeof e.toJSONSchema==`function`?e.toJSONSchema():e}const O=[`low`,`medium`,`high`,`xhigh`,`max`];function k(e,t){if(!t||t.length===0||t.includes(e))return e;let n=O.indexOf(e),r=[...t].sort((e,t)=>O.indexOf(e)-O.indexOf(t)),i;for(let e of r)if(O.indexOf(e)<=n)i=e;else break;return i??r[0]}function A(e,t){return e.reasoning===!0&&!!t}function te(e){for(let t of e)if(t.role===`tool_result`||t.role===`assistant`&&t.content.some(e=>e.type===`tool_call`))return!0;return!1}function ne(e){let t=e.systemPromptSegments;if(!t||t.length===0)return e.systemPrompt?[{role:`system`,content:E(e.systemPrompt)}]:[];let n=t.filter(e=>(e.cache??`stable`)===`stable`).map(e=>e.text).filter(e=>e.length>0).join(`
2
+
3
+ `),r=t.filter(e=>e.cache===`volatile`).map(e=>e.text).filter(e=>e.length>0).join(`
4
+
5
+ `),i=[];return n&&i.push({role:`system`,content:E(n)}),r&&i.push({role:`system`,content:E(r)}),i}function re(t){let n=[],r=A(t.model,t.thinkingLevel);n.push(...ne(t));for(let i of S(t.messages))if(i.role===`user`)typeof i.content==`string`?n.push({role:`user`,content:E(i.content)}):n.push({role:`user`,content:i.content.map(t=>{if(t.type===`text`)return{type:`text`,text:E(t.text)};if(t.type===`image`){let{base64:n,mediaType:r,valid:i}=e(t.source,t.mime);return i?{type:`image_url`,image_url:{url:`data:${r||`image/png`};base64,${n}`}}:{type:`text`,text:`[image omitted: corrupted or empty data]`}}return{type:`text`,text:`[unsupported]`}})});else if(i.role===`assistant`){let e=i.content.filter(e=>e.type===`tool_call`).map(e=>({id:e.id,type:`function`,function:{name:e.name,arguments:JSON.stringify(e.arguments)}})),a=i.content.filter(e=>e.type===`text`).filter(e=>e.text.trim().length>0).map(e=>E(e.text)).join(``),o=i.content.filter(e=>e.type===`thinking`).map(e=>E(e.text)).join(``).trim();if(!a&&e.length===0&&!o)continue;let s=t.model.reasoningContentRequired===!0;n.push({role:`assistant`,content:a||null,...e.length>0?{tool_calls:e}:{},...o?{reasoning_content:o}:e.length>0&&r||s?{reasoning_content:null}:{}})}else i.role===`tool_result`?n.push({role:`tool`,tool_call_id:i.toolCallId,content:E(i.content.map(e=>e.type===`text`?e.text:``).join(``))}):i.role===`system_notification`&&n.push({role:`user`,content:E(w(i.content))});return n}function ie(e){switch(e){case`low`:case`medium`:case`high`:return`high`;case`xhigh`:case`max`:return`max`;default:return`high`}}function ae(e,t,n={}){let r={model:n.modelName??e.name,messages:re(t),max_tokens:t.maxTokens??e.maxOutputTokens,stream:!0,stream_options:{include_usage:!0}};t.temperature!==void 0&&!e.reasoning&&(r.temperature=t.temperature);let i=!!(t.tools&&t.tools.length>0);return e.reasoningToolsExclusive===!0&&i?r.reasoning_effort=`none`:A(e,t.thinkingLevel)&&t.thinkingLevel!==void 0&&(r.reasoning_effort=ie(k(t.thinkingLevel,e.thinkingLevels))),i?r.tools=t.tools.map(e=>({type:`function`,function:{name:e.name,description:e.description,parameters:D(e.parameters)}})):te(t.messages)&&(r.tools=[]),r}const j=i(`@x-otto/provider:openai-usage`);function oe(e){let t=t=>{let n=e.get(t);if(n===null)return;let r=Number(n);return Number.isFinite(r)?r:void 0},n=t=>{let n=e.get(t);if(!n)return;let r=n.match(/^(\d+(?:\.\d+)?)(m|s|ms)$/);if(!r)return;let i=Number(r[1]),a=r[2];if(a===`ms`)return i;if(a===`s`)return i*1e3;if(a===`m`)return i*60*1e3},r={remainingRequests:t(`x-ratelimit-remaining-requests`),remainingTokens:t(`x-ratelimit-remaining-tokens`),limitRequests:t(`x-ratelimit-limit-requests`),limitTokens:t(`x-ratelimit-limit-tokens`),resetRequestsMs:n(`x-ratelimit-reset-requests`),resetTokensMs:n(`x-ratelimit-reset-tokens`)};if(!Object.values(r).every(e=>e===void 0))return r}function M(e){if(!e)return null;let t=oe(e);if(j.isLevelEnabled(`trace`)){let t=[];e.forEach((e,n)=>{let r=n.startsWith(`x-ratelimit-`)||e.length<40?e:e.slice(0,20)+`…`;t.push(` ${n}: ${r}`)}),j.trace(`OpenAI Codex response headers (%d):
2
6
  %s`,t.length,t.sort().join(`
3
- `))}return t?{status:`unknown`,standardLimits:t}:null}const ae=E,oe=r(`@x-otto/provider:stream-state`);function D(){return{inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheWriteTokens:0}}function O(e,t){try{return{args:JSON.parse(e),parseFailed:!1}}catch{return oe.debug({toolName:t},`Failed to parse tool arguments JSON`),{args:{},parseFailed:!0}}}function se(e,t){return O(e,t).args}function ce(e){return new Set(e?.map(e=>e.name)??[])}function k(e,t,n){let{args:r,parseFailed:i}=O(n,t);return{type:`tool_call`,id:e,name:t,arguments:r,...i?{argumentsParseFailed:!0}:{}}}function A(e,t){if(e.content.some(e=>e.type===`tool_call`))return e;let n=e.content.findLastIndex(e=>e.type===`text`);if(n<0)return e;let r=e.content[n],i=M(r.text,t);if(!i)return e;let a=[...e.content],o=r.text.slice(0,i.matchStart).trimEnd();return o.length>0?a[n]={...r,text:o}:a.splice(n,1),a.push(k(`recovered_${n}`,i.name,i.argumentsJson)),{...e,content:a}}const le=/<invoke name="([^"]+)">((?:(?!<invoke name=")[\s\S])*?)<\/invoke>\s*$/,j=/<parameter name="([^"]+)">([\s\S]*?)<\/parameter>/g;function ue(e,t){return M(e,t)!==null}function M(e,t){let n=e.trimEnd(),r=le.exec(n);if(!r)return null;let i=r[1];if(!t.has(i))return null;let a={},o=r[2],s;for(j.lastIndex=0;(s=j.exec(o))!==null;)a[s[1]]=s[2];return{name:i,argumentsJson:JSON.stringify(a),matchStart:r.index}}function N(){return{...D(),textContent:``,thinkingText:``,toolCalls:new Map,stopReason:`end_turn`}}function P(e,t=!0){let n=[];if(e.thinkingText&&n.push({type:`thinking`,text:e.thinkingText}),e.textContent&&n.push({type:`text`,text:e.textContent}),t)for(let[,t]of e.toolCalls)n.push(k(t.id,t.name,t.argumentsJson));return n}function de(e,t=!0){let n=[],r=[...e.entries()].sort(([e],[t])=>e-t);for(let[,e]of r)e.kind===`thinking`?e.text&&n.push({type:`thinking`,text:e.text,...e.signature===void 0?{}:{signature:e.signature}}):e.kind===`text`?e.text&&n.push({type:`text`,text:e.text}):t&&n.push(k(e.id,e.name,e.argumentsJson));return n}function F(e,t,n,r){return{role:`assistant`,content:t,api:e.api,provider:e.provider,model:e.model,usage:{inputTokens:n.inputTokens,outputTokens:n.outputTokens,cacheReadTokens:n.cacheReadTokens,cacheWriteTokens:n.cacheWriteTokens},stopReason:r}}const fe=r(`@x-otto/provider:chat-completions-stream`);function I(e){switch(e){case`stop`:return`end_turn`;case`length`:return`max_tokens`;case`tool_calls`:return`tool_use`;case`content_filter`:return`refusal`;default:return fe.warn(`Unknown chat-completions finish_reason: ${e}`),`end_turn`}}async function*pe(e){let{url:t,headers:r,body:i,identity:a,signal:o,timeout:s,idleTimeout:c,onResponse:l,registeredToolNames:d}=e,f=N(),p=(e=!1)=>F(a,P(f,e),f,f.stopReason),m=!1,h=!1,g=!1,_,v=u({url:t,body:i,headers:r,signal:o,timeout:s,idleTimeout:c,onResponse:e=>{_=e,l?.(e)}});try{for await(let e of v){if(e.data===`[DONE]`)break;let t;try{t=JSON.parse(e.data)}catch{continue}if(t.error){let e=t.error,r=typeof e.message==`string`?e.message:JSON.stringify(e),i=e.code,a=typeof i==`number`?i:typeof i==`string`&&/^\d+/.test(i)?Number(i.match(/^\d+/)?.[0]):void 0;throw new n(`Chat Completions stream error: ${r}`,{code:`PROVIDER_API_ERROR`,status:a})}if(t.usage){let e=t.usage,n=e.prompt_tokens??0,r=e.completion_tokens??0,i=e.prompt_tokens_details?.cached_tokens??e.prompt_cache_hit_tokens??0;f.inputTokens=n-i,f.cacheReadTokens=i,f.outputTokens=r}let r=t.choices?.[0];if(!r)continue;let i=r.delta;if(m||(m=!0,yield{type:`start`,partial:p()}),r.finish_reason&&(f.stopReason=I(r.finish_reason)),!i)continue;if(i.content){let e=i.content;h||(h=!0,yield{type:`text_start`,index:0,partial:p()}),f.textContent+=e,yield{type:`text_delta`,index:0,delta:e,partial:p()}}let a=i.reasoning_content??i.reasoning??i.reasoning_text??null;if(a&&(g||(g=!0,yield{type:`thinking_start`,index:0,partial:p()}),f.thinkingText+=a,yield{type:`thinking_delta`,index:0,delta:a,partial:p()}),i.tool_calls)for(let e of i.tool_calls){let t=e.index,n=e.function;f.toolCalls.has(t)||(f.toolCalls.set(t,{id:e.id??`call_${t}`,name:n?.name??``,argumentsJson:``}),yield{type:`tool_call_start`,index:t,partial:p()});let r=f.toolCalls.get(t);if(n?.arguments){let e=n.arguments;r.argumentsJson+=e,yield{type:`tool_call_delta`,index:t,delta:e,partial:p()}}}}g&&(yield{type:`thinking_end`,index:0,content:f.thinkingText,partial:p()}),h&&(yield{type:`text_end`,index:0,content:f.textContent,partial:p()});for(let[e,t]of f.toolCalls)yield{type:`tool_call_end`,index:e,toolCall:k(t.id,t.name,t.argumentsJson),partial:p()};yield{type:`done`,reason:f.stopReason,message:A(p(!0),d??new Set),providerUsage:E(_)}}catch(e){yield{type:`error`,error:e instanceof Error?e:new n(`Unknown Chat Completions stream error`,{code:`PROVIDER_STREAM_ERROR`})}}}function L(e,t,n){for(let r of e){if(!r)continue;let e=r.split(`.`)[1];if(e)try{let r=JSON.parse(Buffer.from(e,`base64url`).toString(`utf-8`));if(n){let e=r[n]?.[t]??r[t];if(typeof e==`string`)return e}else{let e=r[t];if(typeof e==`string`)return e}}catch{}}}const me=new Set([`authorization`,`x-api-key`]),he=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/,ge=/[\u0000-\u001f\u007f]/g;function R(e,t,n,r){let i=new Map;if(!e)return i;for(let[a,o]of Object.entries(e)){let e=a.trim();if(!he.test(e)){n?.warn({provider:r,source:t,header:a},`dropped request header with invalid name (RFC 7230 token expected)`);continue}if(typeof o!=`string`)continue;let s=o.replace(ge,``).trim();s&&i.set(e.toLowerCase(),{name:e,value:s})}return i}function _e(e){if(!e?.headers.length)return;let t={};for(let n of e.headers){if(n.source.kind!==`jwt-claim`)continue;let r=L(n.source.tokenSources?.length?n.source.tokenSources.map(t=>t===`access_token`?e.token:void 0):[e.token],n.source.claim,n.source.namespace);r&&(t[n.header]=r)}return Object.keys(t).length>0?t:void 0}function ve(e){let{logger:t,diagnosticLabel:n,allowAuthHeaderOverride:r}=e,i=[{label:`protocolDefaults`,entries:R(e.protocolDefaults,`protocolDefaults`,t,n)},{label:`staticHeaders`,entries:R(e.staticHeaders,`staticHeaders`,t,n)},{label:`tokenDerived`,entries:R(_e(e.tokenDerived),`tokenDerived`,t,n)},{label:`customized`,entries:R(e.customized,`customized`,t,n)}],a=new Set([`staticHeaders`,`tokenDerived`,`customized`]),o=new Map;for(let{label:e,entries:s}of i)for(let[i,c]of s){if(!r&&a.has(e)&&me.has(i)){t?.warn({provider:n,source:e,header:c.name},`dropped attempt to override auth header (declare allowAuthHeaderOverride to permit)`);continue}let s=o.get(i);s&&e===`customized`&&s.value!==c.value&&t?.debug({provider:n,header:c.name},`customizeRequest overrode a previously declared request header`),o.set(i,c)}for(let[i,a]of R(e.auth,`auth`,t,n)){if(r&&o.has(i)){t?.debug({provider:n,header:a.name},`provider-declared auth header kept (allowAuthHeaderOverride); otto-resolved credential not applied`);continue}o.set(i,a)}let s={};for(let{name:e,value:t}of o.values())s[e]=t;return s}const z=r(`@x-otto/provider:oauth:templates`);function B(e,n){return new Promise((r,i)=>{if(n?.aborted){i(new t(`Login cancelled`,{code:`OAUTH_CANCELLED`}));return}let a=setTimeout(r,e);n?.addEventListener(`abort`,()=>{clearTimeout(a),i(new t(`Login cancelled`,{code:`OAUTH_CANCELLED`}))},{once:!0})})}function V(e){if(e?.aborted)throw new t(`Login cancelled`,{code:`OAUTH_CANCELLED`})}function ye(e){let{id:n,name:r,clientId:a,authorizeUrl:o,tokenUrl:s,redirectUri:c,scope:u,loopbackPorts:d,assumesLoopbackAlways:f=!1,extraAuthParams:p,tokenExchangeEncoding:m=`form`,subscriptionScoped:h=!1,oauthBeta:g}=e,_=!f&&Array.isArray(d)&&d.length>0;async function v(e,n){let i=e=>{let t=e.trim(),n=t.indexOf(`#`);return{code:n>=0?t.slice(0,n):t,returnedState:n>=0?t.slice(n+1):void 0}};if(!n){if(f)throw new t(`${r}: loopback required but all ports are unavailable. Configured ports: ${(d??[]).join(`, `)||`none`}.`,{code:`OAUTH_LOOPBACK_FAILED`});return i(await e.onManualCode())}if(f){let e=await n.waitForCallback();return{code:e.code,returnedState:e.state}}let a=new AbortController,o=n.waitForCallback().then(e=>({code:e.code,returnedState:e.state})),s=e.onManualCode?e.onManualCode(a.signal).then(i):new Promise(()=>{});o.catch(()=>{}),s.catch(()=>{});try{return await Promise.race([o,s.then(t=>t.code?t:(e.onProgress?.(`Waiting for browser authorization…`),o))])}finally{a.abort()}}function y(e,t,n){let r=new URL(o);if(r.searchParams.set(`code`,`true`),r.searchParams.set(`client_id`,a),r.searchParams.set(`response_type`,`code`),r.searchParams.set(`redirect_uri`,e),r.searchParams.set(`scope`,u),r.searchParams.set(`code_challenge`,t),r.searchParams.set(`code_challenge_method`,`S256`),r.searchParams.set(`state`,n),p)for(let[e,t]of Object.entries(p))r.searchParams.set(e,t);return r.toString().replace(/\+/g,`%20`)}async function b(e,n){let i=m===`json`,a={Accept:`application/json`},o;i?(a[`Content-Type`]=`application/json`,o=JSON.stringify(e)):(a[`Content-Type`]=`application/x-www-form-urlencoded`,o=new URLSearchParams(e).toString());let c;try{c=await fetch(s,{method:`POST`,headers:a,body:o})}catch(e){let n=e.cause;throw new t(`${r} token endpoint unreachable (${n?.code??n?.message??e.message}) — ${s}`,{code:`OAUTH_TOKEN_NETWORK`,cause:e})}let l;try{l=await c.json()}catch(e){throw new t(`${r} token endpoint returned non-JSON response (HTTP ${c.status})`,{code:`OAUTH_TOKEN_FAILED`,cause:e})}if(!l.access_token)throw new t(`${r} token exchange failed: ${l.error_description||l.error||`HTTP ${c.status}`}`,{code:`OAUTH_TOKEN_FAILED`,status:c.status,errorType:l.error});return{access:l.access_token,refresh:l.refresh_token??n??``,expires:Date.now()+(l.expires_in??0)*1e3}}return{id:n,name:r,usesCallbackServer:!1,oauthBeta:g,subscriptionScoped:h,async login(e){if(!_&&!f&&!e.onManualCode)throw new t(`${r} OAuth requires an onManualCode callback to paste the code.`,{code:`OAUTH_MANUAL_CODE_REQUIRED`});V(e.signal);let{verifier:n,challenge:o}=i(),s=n,u;if(Array.isArray(d)&&d.length>0)try{u=await l({ports:d,callbackPath:`/callback`,expectedState:s,signal:e.signal})}catch(e){if(z.info(`${r}: loopback ports [${d.join(`,`)}] unavailable; `+(f?``:`falling back to manual paste.`)+` (${e.message})`),f)throw new t(`${r}: loopback required but all ports [${d.join(`,`)}] are unavailable.`,{code:`OAUTH_LOOPBACK_FAILED`})}let p=u?`http://localhost:${u.port}/callback`:c;try{let i=y(p,o,s);e.onAuth({url:i,instructions:u?`Open the URL and authorize; login completes automatically. If it does not, paste the code shown (format: code#state).`:`Open the URL, authorize, then paste the code shown (format: code#state).`});let{code:c,returnedState:l}=await v(e,u);if(V(e.signal),!c)throw new t(`Empty authorization code.`,{code:`OAUTH_AUTHORIZE_FAILED`});if(l!==void 0&&l!==s)throw new t(`OAuth state mismatch (possible CSRF).`,{code:`OAUTH_STATE_MISMATCH`});l===void 0&&z.warn(`Authorization code returned without state; skipping CSRF state check.`);let d=await b({grant_type:`authorization_code`,code:c,state:l??s,client_id:a,redirect_uri:p,code_verifier:n});return z.info(`${r} OAuth login completed`),d}finally{await u?.close()}},async refreshToken(e){return b({grant_type:`refresh_token`,refresh_token:e.refresh,client_id:a},e.refresh)},getAccessKey(e){return e.access}}}function be(e){let{id:n,name:r,clientId:i,deviceCodeUrlTemplate:a,tokenUrlTemplate:o,refreshUrlTemplate:s,scope:c,allowCustomDomain:l=!1,userAgent:u}=e,d={Accept:`application/json`};u&&(d[`User-Agent`]=u);async function f(e,n,r){let i=await e.text();if(!e.ok)throw new t(`${r} failed: HTTP ${e.status}${i?`: ${i.slice(0,200)}`:``}`,{code:n,status:e.status});try{return JSON.parse(i)}catch{throw new t(`${r} returned a non-JSON response.`,{code:n})}}function p(e,t){return e.replace(/\{domain\}/g,t)}async function m(e,n,a,s,c){let l=Date.now()+s*1e3;for(;Date.now()<l;){if(c?.aborted)throw new t(`Login cancelled`,{code:`OAUTH_CANCELLED`});let s=p(o,e),u=await f(await fetch(s,{method:`POST`,headers:{...d,"Content-Type":`application/x-www-form-urlencoded`},body:new URLSearchParams({client_id:i,device_code:n,grant_type:`urn:ietf:params:oauth:grant-type:device_code`})}),`OAUTH_AUTHORIZE_FAILED`,`${r} access token exchange`);if(u.error){if(u.error===`authorization_pending`){await B(Math.max(1e3,Math.min(Math.floor(a*1e3),l-Date.now())),c);continue}if(u.error===`slow_down`){a+=5,await B(Math.max(1e3,Math.min(Math.floor(a*1e3),l-Date.now())),c);continue}throw new t(`Authorize failed: ${u.error}${u.error_description?`: ${u.error_description}`:``}`,{code:`OAUTH_AUTHORIZE_FAILED`})}if(u.access_token)return u.access_token}throw new t(`Timed out while waiting for ${r} device authorization.`,{code:`OAUTH_AUTHORIZE_TIMEOUT`})}async function h(e,n){if(!s)throw new t(`${r} device-flow OAuth requires refreshUrlTemplate to be declared.`,{code:`OAUTH_REFRESH_URL_MISSING`});let i=p(s,n),a=await f(await fetch(i,{headers:{...d,Authorization:`Bearer ${e}`}}),`OAUTH_REFRESH_FAILED`,`${r} token refresh`),o=a.token??a.access_token;if(!o)throw new t(`${r}: invalid token response fields.`,{code:`OAUTH_REFRESH_FAILED`});return{access:o,expires:(a.expires_at??Math.floor(Date.now()/1e3)+3600)*1e3}}return{id:n,name:r,async login(e){let n=`github.com`;if(l){let i=await e.onPrompt({message:`${r} URL/domain (blank for default)`,placeholder:`company.ghe.com`,allowEmpty:!0});if(e.signal?.aborted)throw new t(`Login cancelled`,{code:`OAUTH_CANCELLED`});let a=i.trim()||`github.com`;n=a.startsWith(`http://`)||a.startsWith(`https://`)?new URL(a).hostname:a}let o=p(a,n),s=await f(await fetch(o,{method:`POST`,headers:{...d,"Content-Type":`application/x-www-form-urlencoded`},body:new URLSearchParams({client_id:i,scope:c})}),`OAUTH_AUTHORIZE_FAILED`,`${r} device authorization`);if(!s.device_code)throw new t(`${r} device authorization returned invalid response fields.`,{code:`OAUTH_AUTHORIZE_FAILED`});await e.onAuth({url:s.verification_uri,code:s.user_code,instructions:`Enter code: ${s.user_code}`});let u=await m(n,s.device_code,s.interval,s.expires_in,e.signal),{access:g,expires:_}=await h(u,n),v={access:g,refresh:u,expires:_};return l&&n!==`github.com`&&(v.domain=n),await e.onProgress?.(`...`),z.info(`${r} OAuth login completed`),v},async refreshToken(e){let t=e.domain??`github.com`,{access:n,expires:r}=await h(e.refresh,t);return{access:n,refresh:e.refresh,expires:r,...t===`github.com`?{}:{domain:t}}},getAccessKey(e){return e.access}}}function H(e){let t=e;for(let e=0;e<3&&t!=null;e++){let e=t;if(typeof e.status==`number`)return e.status;if(typeof e.statusCode==`number`)return e.statusCode;t=e.cause}}function U(e){let t=[],n=e;for(let e=0;e<3&&n!=null;e++){let e=n;typeof e.message==`string`&&t.push(e.message),n=e.cause}return t.join(` ← `).toLowerCase()}const xe=[/prompt is too long/i,/context[\s_-]{0,3}(?:length|window|limit)/i,/context_length_exceeded/i,/maximum[\s_-]{0,3}context/i,/too many (?:input )?tokens/i,/exceeds? the maximum/i,/reduce the length/i,/input is too large/i,/request too large/i];function W(e){if(e==null)return!1;if(H(e)===413)return!0;let t=U(e);return t?!!xe.some(e=>e.test(t)):!1}const G=[/econnreset/i,/econnrefused/i,/etimedout/i,/timed? ?out/i,/enotfound/i,/eai_again/i,/socket hang ?up/i,/network/i,/fetch failed/i,/stream (?:ended|closed|disconnected|interrupted)/i,/premature close/i,/connection (?:closed|reset|error)/i,/overloaded/i,/\bterminated\b/i,/other side closed/i],K=new Set([`PROVIDER_INCOMPLETE_STREAM`,`PROVIDER_STREAM_ERROR`]),q=[/rate_limit_error/i,/overloaded_error/i,/throttling/i,/flow[\s_-]?control/i,/流量控制/,/限流/,/请求过于频繁/];function Se(e){let t=e;for(let e=0;e<3&&t!=null;e++){let e=t;if(e.headers!==void 0)return e.headers;t=e.cause}}function J(e){let t=Se(e);if(!t)return null;let n=c(t,`anthropic-ratelimit-unified-status`);if(n!==`rejected`&&n!==`allowed_warning`)return null;let r=c(t,`anthropic-ratelimit-unified-representative-claim`),i=c(t,`anthropic-ratelimit-unified-reset`);return{status:n,type:r,resetsAt:i?Number(i):void 0}}function Y(e){let t=e;for(let e=0;e<3&&t!=null;e++){let e=t;if(typeof e.code==`string`)return e.code;t=e.cause}}function X(e){let t=e;for(let e=0;e<3&&t!=null;e++){let e=t;if(e.abortKind===`idle`||e.abortKind===`wallclock`||e.abortKind===`external`)return e.abortKind;t=e.cause}}const Ce=[/many-image requests/i,/image dimensions exceed/i,/image exceeds \d+\s*(?:mb|bytes)/i];function we(e){if(e==null||H(e)!==void 0||X(e)!==void 0)return!1;let t=U(e);if(t&&q.some(e=>e.test(t)))return!1;let n=Y(e);return n&&K.has(n)?!0:!!t&&G.some(e=>e.test(t))}function Te(e){if(e==null)return!1;let t=Y(e);return t===`PROVIDER_AUTH_MISSING`||t===`PROVIDER_AUTH_REFRESH_FAILED`?!0:H(e)===401}function Ee(e){let t=Y(e);return t===`PROVIDER_AUTH_MISSING`?`credential-missing`:t===`PROVIDER_AUTH_REFRESH_FAILED`?`refresh-failed`:`server-rejected`}function De(e){if(e==null)return`fatal`;let t=H(e);if(t===400){let t=U(e);if(Ce.some(e=>e.test(t)))return`image-constraint`}if(W(e))return`context-overflow`;if(J(e)?.status===`rejected`)return`fatal`;if(t===429||t!==void 0&&t>=500&&t<=599)return`retryable`;let n=U(e);if(t===void 0&&n&&q.some(e=>e.test(n)))return`retryable`;if(t!==void 0&&t>=400&&t<500)return`fatal`;let r=Y(e);return r&&K.has(r)||n&&G.some(e=>e.test(n))?`retryable`:`fatal`}function Oe(e){let t=e;for(let e=0;e<3&&t!=null;e++){let e=t.headers,n=ke(e);if(n!==void 0)return n;t=t.cause}}function ke(e){let t=c(e,`retry-after-ms`);if(t!==void 0){let e=Number(t);if(Number.isFinite(e)&&e>=0)return e}let n=c(e,`retry-after`);if(n!==void 0){let e=Number(n);if(Number.isFinite(e)&&e>=0)return e*1e3}}function Ae(e){return m(e,`recordings`)}async function*je(e,t,n){f(m(n,`..`),{recursive:!0,mode:448});let r=d(n,{flags:`w`,mode:384}),i={version:1,modelId:t.id,api:String(t.api),recordedAt:new Date().toISOString(),contextWindow:t.contextWindow};r.write(JSON.stringify(i)+`
7
+ `))}return t?{status:`unknown`,standardLimits:t}:null}const se=i(`@x-otto/provider:stream-state`);function N(){return{inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheWriteTokens:0}}function P(e,t){try{return{args:JSON.parse(e),parseFailed:!1}}catch{return se.debug({toolName:t,argumentsJson:e.slice(0,500),length:e.length},`Failed to parse tool arguments JSON`),{args:{},parseFailed:!0}}}function ce(e,t){return P(e,t).args}function le(e){return new Set(e?.map(e=>e.name)??[])}function F(e,t,n){let{args:r,parseFailed:i}=P(n,t);return{type:`tool_call`,id:e,name:t,arguments:r,...i?{argumentsParseFailed:!0}:{}}}function I(e,t){if(e.content.some(e=>e.type===`tool_call`))return e;let n=e.content.findLastIndex(e=>e.type===`text`);if(n<0)return e;let r=e.content[n],i=R(r.text,t);if(!i)return e;let a=[...e.content],o=r.text.slice(0,i.matchStart).trimEnd();return o.length>0?a[n]={...r,text:o}:a.splice(n,1),a.push(F(`recovered_${n}`,i.name,i.argumentsJson)),{...e,content:a}}const ue=/<invoke name="([^"]+)">((?:(?!<invoke name=")[\s\S])*?)<\/invoke>\s*$/,L=/<parameter name="([^"]+)">([\s\S]*?)<\/parameter>/g;function de(e,t){return R(e,t)!==null}function R(e,t){let n=e.trimEnd(),r=ue.exec(n);if(!r)return null;let i=r[1];if(!t.has(i))return null;let a={},o=r[2],s;for(L.lastIndex=0;(s=L.exec(o))!==null;)a[s[1]]=s[2];return{name:i,argumentsJson:JSON.stringify(a),matchStart:r.index}}function z(){return{...N(),textContent:``,thinkingText:``,toolCalls:new Map,stopReason:`end_turn`}}function B(e,t=!0){let n=[];if(e.thinkingText&&n.push({type:`thinking`,text:e.thinkingText}),e.textContent&&n.push({type:`text`,text:e.textContent}),t)for(let[,t]of e.toolCalls)n.push(F(t.id,t.name,t.argumentsJson));return n}function fe(e,t=!0){let n=[],r=[...e.entries()].sort(([e],[t])=>e-t);for(let[,e]of r)e.kind===`thinking`?e.text&&n.push({type:`thinking`,text:e.text,...e.signature===void 0?{}:{signature:e.signature}}):e.kind===`text`?e.text&&n.push({type:`text`,text:e.text}):t&&n.push(F(e.id,e.name,e.argumentsJson));return n}function V(e,t,n,r){return{role:`assistant`,content:r===`max_tokens`?t:t.map(e=>{if(e.type!==`tool_call`||!(`argumentsParseFailed`in e))return e;let{argumentsParseFailed:t,...n}=e;return n}),api:e.api,provider:e.provider,model:e.model,usage:{inputTokens:n.inputTokens,outputTokens:n.outputTokens,cacheReadTokens:n.cacheReadTokens,cacheWriteTokens:n.cacheWriteTokens},stopReason:r}}const pe=i(`@x-otto/provider:chat-completions-stream`);function H(e){switch(e){case`stop`:return`end_turn`;case`length`:return`max_tokens`;case`tool_calls`:return`tool_use`;case`content_filter`:return`refusal`;default:return pe.warn(`Unknown chat-completions finish_reason: ${e}`),`end_turn`}}async function*me(e){let{url:t,headers:n,body:i,identity:a,signal:o,timeout:s,idleTimeout:c,onResponse:l,registeredToolNames:u}=e,d=z(),f=(e=!1)=>V(a,B(d,e),d,d.stopReason),m=!1,h=!1,g=!1,_=!1,v,y=p({url:t,body:i,headers:n,signal:o,timeout:s,idleTimeout:c,onResponse:e=>{v=e,l?.(e)}});try{for await(let e of y){if(e.data===`[DONE]`)break;let t;try{t=JSON.parse(e.data)}catch{continue}if(t.error){let e=t.error,n=typeof e.message==`string`?e.message:JSON.stringify(e),i=e.code,a=typeof i==`number`?i:typeof i==`string`&&/^\d+/.test(i)?Number(i.match(/^\d+/)?.[0]):void 0;throw new r(`Chat Completions stream error: ${n}`,{code:`PROVIDER_API_ERROR`,status:a})}if(t.usage){let e=t.usage,n=e.prompt_tokens??0,r=e.completion_tokens??0,i=e.prompt_tokens_details?.cached_tokens??e.prompt_cache_hit_tokens??0;d.inputTokens=n-i,d.cacheReadTokens=i,d.outputTokens=r}let n=t.choices?.[0];if(!n)continue;let i=n.delta;if(m||(m=!0,yield{type:`start`,partial:f()}),n.finish_reason&&(_=!0,d.stopReason=H(n.finish_reason)),!i)continue;if(i.content){let e=i.content;h||(h=!0,yield{type:`text_start`,index:0,partial:f()}),d.textContent+=e,yield{type:`text_delta`,index:0,delta:e,partial:f()}}let a=i.reasoning_content??i.reasoning??i.reasoning_text??null;if(a&&(g||(g=!0,yield{type:`thinking_start`,index:0,partial:f()}),d.thinkingText+=a,yield{type:`thinking_delta`,index:0,delta:a,partial:f()}),i.tool_calls)for(let e of i.tool_calls){let t=e.index,n=e.function;d.toolCalls.has(t)||(d.toolCalls.set(t,{id:e.id??`call_${t}`,name:n?.name??``,argumentsJson:``}),yield{type:`tool_call_start`,index:t,partial:f()});let r=d.toolCalls.get(t);if(n?.arguments){let e=n.arguments;r.argumentsJson+=e,yield{type:`tool_call_delta`,index:t,delta:e,partial:f()}}}}g&&(yield{type:`thinking_end`,index:0,content:d.thinkingText,partial:f()}),h&&(yield{type:`text_end`,index:0,content:d.textContent,partial:f()});for(let[e,t]of d.toolCalls)yield{type:`tool_call_end`,index:e,toolCall:F(t.id,t.name,t.argumentsJson),partial:f()};_||(d.stopReason=`incomplete`),yield{type:`done`,reason:d.stopReason,message:I(f(!0),u??new Set),providerUsage:M(v)}}catch(e){yield{type:`error`,error:e instanceof Error?e:new r(`Unknown Chat Completions stream error`,{code:`PROVIDER_STREAM_ERROR`})}}}function U(e,t,n){for(let r of e){if(!r)continue;let e=r.split(`.`)[1];if(e)try{let r=JSON.parse(Buffer.from(e,`base64url`).toString(`utf-8`));if(n){let e=r[n]?.[t]??r[t];if(typeof e==`string`)return e}else{let e=r[t];if(typeof e==`string`)return e}}catch{}}}const he=new Set([`authorization`,`x-api-key`]),ge=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/,_e=/[\u0000-\u001f\u007f]/g;function W(e,t,n,r){let i=new Map;if(!e)return i;for(let[a,o]of Object.entries(e)){let e=a.trim();if(!ge.test(e)){n?.warn({provider:r,source:t,header:a},`dropped request header with invalid name (RFC 7230 token expected)`);continue}if(typeof o!=`string`)continue;let s=o.replace(_e,``).trim();s&&i.set(e.toLowerCase(),{name:e,value:s})}return i}function ve(e){if(!e?.headers.length)return;let t={};for(let n of e.headers){if(n.source.kind!==`jwt-claim`)continue;let r=U(n.source.tokenSources?.length?n.source.tokenSources.map(t=>t===`access_token`?e.token:void 0):[e.token],n.source.claim,n.source.namespace);r&&(t[n.header]=r)}return Object.keys(t).length>0?t:void 0}function ye(e){let{logger:t,diagnosticLabel:n,allowAuthHeaderOverride:r}=e,i=[{label:`protocolDefaults`,entries:W(e.protocolDefaults,`protocolDefaults`,t,n)},{label:`staticHeaders`,entries:W(e.staticHeaders,`staticHeaders`,t,n)},{label:`tokenDerived`,entries:W(ve(e.tokenDerived),`tokenDerived`,t,n)},{label:`customized`,entries:W(e.customized,`customized`,t,n)}],a=new Set([`staticHeaders`,`tokenDerived`,`customized`]),o=new Map;for(let{label:e,entries:s}of i)for(let[i,c]of s){if(!r&&a.has(e)&&he.has(i)){t?.warn({provider:n,source:e,header:c.name},`dropped attempt to override auth header (declare allowAuthHeaderOverride to permit)`);continue}let s=o.get(i);s&&e===`customized`&&s.value!==c.value&&t?.debug({provider:n,header:c.name},`customizeRequest overrode a previously declared request header`),o.set(i,c)}for(let[i,a]of W(e.auth,`auth`,t,n)){if(r&&o.has(i)){t?.debug({provider:n,header:a.name},`provider-declared auth header kept (allowAuthHeaderOverride); otto-resolved credential not applied`);continue}o.set(i,a)}let s={};for(let{name:e,value:t}of o.values())s[e]=t;return s}const G=i(`@x-otto/provider:oauth:templates`);function K(e,t){return new Promise((r,i)=>{if(t?.aborted){i(new n(`Login cancelled`,{code:`OAUTH_CANCELLED`}));return}let a=setTimeout(r,e);t?.addEventListener(`abort`,()=>{clearTimeout(a),i(new n(`Login cancelled`,{code:`OAUTH_CANCELLED`}))},{once:!0})})}function q(e){if(e?.aborted)throw new n(`Login cancelled`,{code:`OAUTH_CANCELLED`})}function J(e){let t=e.trim(),n=t.indexOf(`#`);return{code:n>=0?t.slice(0,n):t,returnedState:n>=0?t.slice(n+1):void 0}}function be(e){let{id:r,name:i,clientId:a,authorizeUrl:s,tokenUrl:c,redirectUri:l,scope:u,loopbackPorts:d,callbackPath:p=t,assumesLoopbackAlways:m=!1,extraAuthParams:h,tokenExchangeEncoding:g=`form`,subscriptionScoped:_=!1,oauthBeta:v}=e,y=!m&&Array.isArray(d)&&d.length>0;async function b(e){return J(await e.onManualCode())}async function x(e){let t=await e.waitForCallback();return{code:t.code,returnedState:t.state}}async function S(e,t){let n=new AbortController,r=t.waitForCallback().then(e=>({code:e.code,returnedState:e.state})),i=e.onManualCode?e.onManualCode(n.signal).then(J):new Promise(()=>{});r.catch(()=>{}),i.catch(()=>{});try{return await Promise.race([r,i.then(t=>t.code?t:(e.onProgress?.(`Waiting for browser authorization…`),r))])}finally{n.abort()}}async function C(e,t){if(!t){if(m)throw new n(`${i}: loopback required but all ports are unavailable. Configured ports: ${(d??[]).join(`, `)||`none`}.`,{code:`OAUTH_LOOPBACK_FAILED`});return b(e)}return m?x(t):S(e,t)}function w(e,t,n){let r=new URL(s);if(r.searchParams.set(`client_id`,a),r.searchParams.set(`response_type`,`code`),r.searchParams.set(`redirect_uri`,e),r.searchParams.set(`scope`,u),r.searchParams.set(`code_challenge`,t),r.searchParams.set(`code_challenge_method`,`S256`),r.searchParams.set(`state`,n),h)for(let[e,t]of Object.entries(h))r.searchParams.set(e,t);return r.toString().replace(/\+/g,`%20`)}async function T(e,t){let r=g===`json`,a={Accept:`application/json`},o;r?(a[`Content-Type`]=`application/json`,o=JSON.stringify(e)):(a[`Content-Type`]=`application/x-www-form-urlencoded`,o=new URLSearchParams(e).toString());let s;try{s=await fetch(c,{method:`POST`,headers:a,body:o})}catch(e){let t=e.cause;throw new n(`${i} token endpoint unreachable (${t?.code??t?.message??e.message}) — ${c}`,{code:`OAUTH_TOKEN_NETWORK`,cause:e})}let l;try{l=await s.json()}catch(e){throw new n(`${i} token endpoint returned non-JSON response (HTTP ${s.status})`,{code:`OAUTH_TOKEN_FAILED`,cause:e})}if(!l.access_token)throw new n(`${i} token exchange failed: ${l.error_description||l.error||`HTTP ${s.status}`}`,{code:`OAUTH_TOKEN_FAILED`,status:s.status,errorType:l.error});return{access:l.access_token,refresh:l.refresh_token??t??``,expires:Date.now()+(l.expires_in??0)*1e3}}return{id:r,name:i,usesCallbackServer:!1,oauthBeta:v,subscriptionScoped:_,async login(e){if(!y&&!m&&!e.onManualCode)throw new n(`${i} OAuth requires an onManualCode callback to paste the code.`,{code:`OAUTH_MANUAL_CODE_REQUIRED`});q(e.signal);let{verifier:t,challenge:r}=o(),s=t,c;if(Array.isArray(d)&&d.length>0)try{c=await f({ports:d,callbackPath:p,expectedState:s,signal:e.signal})}catch(e){if(G.info(`${i}: loopback ports [${d.join(`,`)}] unavailable; `+(m?``:`falling back to manual paste.`)+` (${e.message})`),m)throw new n(`${i}: loopback required but all ports [${d.join(`,`)}] are unavailable.`,{code:`OAUTH_LOOPBACK_FAILED`})}let u=c?`http://localhost:${c.port}${p}`:l;try{let o=w(u,r,s);e.onAuth({url:o,instructions:c?`Open the URL and authorize; login completes automatically. If it does not, paste the code shown (format: code#state).`:`Open the URL, authorize, then paste the code shown (format: code#state).`});let{code:l,returnedState:d}=await C(e,c);if(q(e.signal),!l)throw new n(`Empty authorization code.`,{code:`OAUTH_AUTHORIZE_FAILED`});if(d!==void 0&&d!==s)throw new n(`OAuth state mismatch (possible CSRF).`,{code:`OAUTH_STATE_MISMATCH`});d===void 0&&G.warn(`Authorization code returned without state; skipping CSRF state check.`);let f=await T({grant_type:`authorization_code`,code:l,state:d??s,client_id:a,redirect_uri:u,code_verifier:t});return G.info(`${i} OAuth login completed`),f}finally{await c?.close()}},async refreshToken(e){return T({grant_type:`refresh_token`,refresh_token:e.refresh,client_id:a},e.refresh)},getAccessKey(e){return e.access}}}function xe(e){let{id:t,name:r,clientId:i,deviceCodeUrlTemplate:a,tokenUrlTemplate:o,refreshUrlTemplate:s,scope:c,allowCustomDomain:l=!1,userAgent:u}=e,d={Accept:`application/json`};u&&(d[`User-Agent`]=u);async function f(e,t,r){let i=await e.text();if(!e.ok)throw new n(`${r} failed: HTTP ${e.status}${i?`: ${i.slice(0,200)}`:``}`,{code:t,status:e.status});try{return JSON.parse(i)}catch{throw new n(`${r} returned a non-JSON response.`,{code:t})}}function p(e,t){return e.replace(/\{domain\}/g,t)}async function m(e,t,a,s,c){let l=Date.now()+s*1e3;for(;Date.now()<l;){if(c?.aborted)throw new n(`Login cancelled`,{code:`OAUTH_CANCELLED`});let s=p(o,e),u=await f(await fetch(s,{method:`POST`,headers:{...d,"Content-Type":`application/x-www-form-urlencoded`},body:new URLSearchParams({client_id:i,device_code:t,grant_type:`urn:ietf:params:oauth:grant-type:device_code`})}),`OAUTH_AUTHORIZE_FAILED`,`${r} access token exchange`);if(u.error){if(u.error===`authorization_pending`){await K(Math.max(1e3,Math.min(Math.floor(a*1e3),l-Date.now())),c);continue}if(u.error===`slow_down`){a+=5,await K(Math.max(1e3,Math.min(Math.floor(a*1e3),l-Date.now())),c);continue}throw new n(`Authorize failed: ${u.error}${u.error_description?`: ${u.error_description}`:``}`,{code:`OAUTH_AUTHORIZE_FAILED`})}if(u.access_token)return u.access_token}throw new n(`Timed out while waiting for ${r} device authorization.`,{code:`OAUTH_AUTHORIZE_TIMEOUT`})}async function h(e,t){if(!s)throw new n(`${r} device-flow OAuth requires refreshUrlTemplate to be declared.`,{code:`OAUTH_REFRESH_URL_MISSING`});let i=p(s,t),a=await f(await fetch(i,{headers:{...d,Authorization:`Bearer ${e}`}}),`OAUTH_REFRESH_FAILED`,`${r} token refresh`),o=a.token??a.access_token;if(!o)throw new n(`${r}: invalid token response fields.`,{code:`OAUTH_REFRESH_FAILED`});return{access:o,expires:(a.expires_at??Math.floor(Date.now()/1e3)+3600)*1e3}}return{id:t,name:r,async login(e){let t=`github.com`;if(l){let i=await e.onPrompt({message:`${r} URL/domain (blank for default)`,placeholder:`company.ghe.com`,allowEmpty:!0});if(e.signal?.aborted)throw new n(`Login cancelled`,{code:`OAUTH_CANCELLED`});let a=i.trim()||`github.com`;t=a.startsWith(`http://`)||a.startsWith(`https://`)?new URL(a).hostname:a}let o=p(a,t),s=await f(await fetch(o,{method:`POST`,headers:{...d,"Content-Type":`application/x-www-form-urlencoded`},body:new URLSearchParams({client_id:i,scope:c})}),`OAUTH_AUTHORIZE_FAILED`,`${r} device authorization`);if(!s.device_code)throw new n(`${r} device authorization returned invalid response fields.`,{code:`OAUTH_AUTHORIZE_FAILED`});await e.onAuth({url:s.verification_uri,code:s.user_code,instructions:`Enter code: ${s.user_code}`});let u=await m(t,s.device_code,s.interval,s.expires_in,e.signal),{access:g,expires:_}=await h(u,t),v={access:g,refresh:u,expires:_};return l&&t!==`github.com`&&(v.domain=t),await e.onProgress?.(`...`),G.info(`${r} OAuth login completed`),v},async refreshToken(e){let t=e.domain??`github.com`,{access:n,expires:r}=await h(e.refresh,t);return{access:n,refresh:e.refresh,expires:r,...t===`github.com`?{}:{domain:t}}},getAccessKey(e){return e.access}}}function Y(e,t){let n=e;for(let e=0;e<3&&n!=null;e++){let e=t(n);if(e!==void 0)return e;n=n.cause}}function X(e){return Y(e,e=>{if(typeof e.status==`number`)return e.status;if(typeof e.statusCode==`number`)return e.statusCode})}function Z(e){let t=[],n=e;for(let e=0;e<3&&n!=null;e++){let e=n.message;typeof e==`string`&&t.push(e),n=n.cause}return t.join(` ← `).toLowerCase()}const Se=[/prompt is too long/i,/context[\s_-]{0,3}(?:length|window|limit)/i,/context_length_exceeded/i,/maximum[\s_-]{0,3}context/i,/too many (?:input )?tokens/i,/exceeds? the maximum/i,/reduce the length/i,/input is too large/i,/request too large/i];function Ce(e){if(e==null)return!1;if(X(e)===413)return!0;let t=Z(e);return t?!!Se.some(e=>e.test(t)):!1}const we=[/econnreset/i,/econnrefused/i,/etimedout/i,/timed? ?out/i,/enotfound/i,/eai_again/i,/socket hang ?up/i,/network/i,/fetch failed/i,/stream (?:ended|closed|disconnected|interrupted)/i,/premature close/i,/connection (?:closed|reset|error)/i,/overloaded/i,/\bterminated\b/i,/other side closed/i],Te=new Set([`PROVIDER_INCOMPLETE_STREAM`,`PROVIDER_STREAM_ERROR`]),Ee=[/rate_limit_error/i,/overloaded_error/i,/throttling/i,/flow[\s_-]?control/i,/流量控制/,/限流/,/请求过于频繁/];function De(e){return Y(e,e=>{let t=e.headers;return t===void 0?void 0:t})}function Oe(e){let t=De(e);if(!t)return null;let n=u(t,`anthropic-ratelimit-unified-status`);if(n!==`rejected`&&n!==`allowed_warning`)return null;let r=u(t,`anthropic-ratelimit-unified-representative-claim`),i=u(t,`anthropic-ratelimit-unified-reset`);return{status:n,type:r,resetsAt:i?Number(i):void 0}}function Q(e){return Y(e,e=>typeof e.code==`string`?e.code:void 0)}function ke(e){return Y(e,e=>{let t=e.abortKind;if(t===`idle`||t===`wallclock`||t===`external`)return t})}const Ae=[/many-image requests/i,/image dimensions exceed/i,/image exceeds \d+\s*(?:mb|bytes)/i];function je(e){if(e==null||X(e)!==void 0||ke(e)!==void 0)return!1;let t=Z(e);if(t&&Ee.some(e=>e.test(t)))return!1;let n=Q(e);return n&&Te.has(n)?!0:!!t&&we.some(e=>e.test(t))}function Me(e){if(e==null)return!1;let t=Q(e);return t===`PROVIDER_AUTH_MISSING`||t===`PROVIDER_AUTH_REFRESH_FAILED`?!0:X(e)===401}function Ne(e){let t=Q(e);return t===`PROVIDER_AUTH_MISSING`?`credential-missing`:t===`PROVIDER_AUTH_REFRESH_FAILED`?`refresh-failed`:`server-rejected`}function Pe(e){if(e==null)return`fatal`;let t=X(e);if(t===400){let t=Z(e);if(Ae.some(e=>e.test(t)))return`image-constraint`}if(Ce(e))return`context-overflow`;if(Oe(e)?.status===`rejected`)return`fatal`;if(t===429||t!==void 0&&t>=500&&t<=599)return`retryable`;let n=Z(e);if(t===void 0&&n&&Ee.some(e=>e.test(n)))return`retryable`;if(t!==void 0&&t>=400&&t<500)return`fatal`;let r=Q(e);return r&&Te.has(r)||n&&we.some(e=>e.test(n))?`retryable`:`fatal`}const Fe=new Set([`invalid_request_error`,`invalid_api_key`,`authentication_error`,`permission_error`,`permission_denied`,`insufficient_quota`]);function Ie(e){if(Pe(e)!==`fatal`)return!1;let t=X(e);return t!==void 0&&t>=400&&t<500||Me(e)?!0:Y(e,e=>typeof e.errorType==`string`&&Fe.has(e.errorType)?!0:void 0)===!0}function Le(e){return Y(e,e=>Re(e.headers))}function Re(e){let t=u(e,`retry-after-ms`);if(t!==void 0){let e=Number(t);if(Number.isFinite(e)&&e>=0)return e}let n=u(e,`retry-after`);if(n!==void 0){let e=Number(n);if(Number.isFinite(e)&&e>=0)return e*1e3}}function ze(e){return _(e,`recordings`)}async function*Be(e,t,n){h(_(n,`..`),{recursive:!0,mode:448});let r=m(n,{flags:`w`,mode:384}),i={version:1,modelId:t.id,api:String(t.api),recordedAt:new Date().toISOString(),contextWindow:t.contextWindow};r.write(JSON.stringify(i)+`
4
8
  `);let a;try{for await(let t of e){let e=Date.now();a===void 0&&(a=e);let n={t:e-a,e:t};r.write(JSON.stringify(n)+`
5
- `),yield t}}finally{await new Promise(e=>r.end(()=>e()))}}async function Z(e){let t=(await p(e,`utf-8`)).split(`
6
- `).filter(e=>e.trim().length>0),n=t[0];if(n===void 0)throw Error(`录制文件为空: ${e}`);let r=JSON.parse(n);if(r.version!==1)throw Error(`不支持的录制版本 ${r.version}(本实现支持 1)`);let i=t.slice(1).map(e=>JSON.parse(e));if(i.length===0)throw Error(`录制文件无事件: ${e}`);return{header:r,events:i}}async function*Me(e,t={},n){let r=Date.now();for(let i of e.events){if(n?.aborted)throw Error(`Replay aborted`);if(!t.fast&&i.t>0){let e=i.t-(Date.now()-r);e>0&&await new Promise(t=>setTimeout(t,e))}yield i.e}}const Q=`replay`;function Ne(e){return{id:`replay/${e.header.modelId}`,name:`Replay of ${e.header.modelId}`,api:Q,provider:`replay`,contextWindow:e.header.contextWindow}}var $=class{id=Q;displayName=`Replay Provider (RFC-200)`;recording;constructor(e,t={}){this.filePath=e,this.options=t}async preload(){return this.recording??=await Z(this.filePath),this.recording}async*converse(e,t,n,r){yield*Me(await this.preload(),this.options,n.signal??r)}};function Pe(e,t={}){return()=>new $(e,t)}export{be as DeviceFlowOAuthTemplate,ye as PKCEOAuthTemplate,Q as REPLAY_API,$ as ReplayProviderStream,F as assembleAssistantMessage,b as assertSystemNotificationPlacement,de as blocksContent,re as buildChatCompletionsBody,w as buildChatCompletionsMessages,Ne as buildReplayModel,ve as buildRequestHeaders,C as clampThinkingLevel,Ee as classifyAuthErrorType,De as classifyStreamError,i as createPkcePair,Pe as createReplayProviderFactory,v as degradedSystemNotificationText,X as extractAbortKind,J as extractAnthropicRateLimit,L as extractJwtClaim,ae as extractProviderUsageFromOpenAiHeaders,Oe as extractRetryAfterMs,E as extractStandardRateLimitHeaders,P as flatContent,a as generateChallenge,o as generateState,s as generateVerifier,ue as hasTrailingInvokeXml,N as initFlatStreamState,D as initStreamUsage,Te as isAuthError,W as isContextOverflowError,we as isNetworkDisconnectError,Z as loadRecording,I as mapChatFinishReason,y as mergeConsecutiveSystemNotifications,se as parseToolArguments,O as parseToolArgumentsWithStatus,je as recordStream,Ae as recordingsDir,A as recoverLiteralInvokeToolCalls,ce as registeredToolNames,Me as replayStream,x as sanitizeSurrogates,g as sanitizeToolRoundtrips,l as startLoopbackServer,pe as streamChatCompletions,ee as toJsonSchema,k as toolCallFromParsed};
9
+ `),yield t}}finally{await new Promise(e=>r.end(()=>e()))}}async function Ve(e){let t=(await g(e,`utf-8`)).split(`
10
+ `).filter(e=>e.trim().length>0),n=t[0];if(n===void 0)throw Error(`录制文件为空: ${e}`);let r=JSON.parse(n);if(r.version!==1)throw Error(`不支持的录制版本 ${r.version}(本实现支持 1)`);let i=t.slice(1).map(e=>JSON.parse(e));if(i.length===0)throw Error(`录制文件无事件: ${e}`);return{header:r,events:i}}async function*He(e,t={},n){let r=Date.now();for(let i of e.events){if(n?.aborted)throw Error(`Replay aborted`);if(!t.fast&&i.t>0){let e=i.t-(Date.now()-r);e>0&&await new Promise(t=>setTimeout(t,e))}yield i.e}}const $=`replay`;function Ue(e){return{id:`replay/${e.header.modelId}`,name:`Replay of ${e.header.modelId}`,api:$,provider:`replay`,contextWindow:e.header.contextWindow}}var We=class{id=$;displayName=`Replay Provider (RFC-200)`;recording;constructor(e,t={}){this.filePath=e,this.options=t}async preload(){return this.recording??=await Ve(this.filePath),this.recording}async*converse(e,t,n,r){yield*He(await this.preload(),this.options,n.signal??r)}};function Ge(e,t={}){return()=>new We(e,t)}export{xe as DeviceFlowOAuthTemplate,v as MAX_PLUGIN_RETRIES,be as PKCEOAuthTemplate,$ as REPLAY_API,We as ReplayProviderStream,V as assembleAssistantMessage,ee as assertSystemNotificationPlacement,fe as blocksContent,ae as buildChatCompletionsBody,re as buildChatCompletionsMessages,Ue as buildReplayModel,ye as buildRequestHeaders,k as clampThinkingLevel,Ne as classifyAuthErrorType,Pe as classifyStreamError,a as createPkcePair,Ge as createReplayProviderFactory,w as degradedSystemNotificationText,ke as extractAbortKind,Oe as extractAnthropicRateLimit,Q as extractCode,U as extractJwtClaim,Le as extractRetryAfterMs,M as extractStandardRateLimitHeaders,X as extractStatus,B as flatContent,s as generateChallenge,c as generateState,l as generateVerifier,de as hasTrailingInvokeXml,z as initFlatStreamState,N as initStreamUsage,Me as isAuthError,Ce as isContextOverflowError,je as isNetworkDisconnectError,Ie as isNonRetryableStreamError,Ve as loadRecording,H as mapChatFinishReason,T as mergeConsecutiveSystemNotifications,y as noopWireProtocolLogger,ce as parseToolArguments,P as parseToolArgumentsWithStatus,Be as recordStream,ze as recordingsDir,I as recoverLiteralInvokeToolCalls,le as registeredToolNames,He as replayStream,b as resolveProviderBaseUrl,E as sanitizeSurrogates,S as sanitizeToolRoundtrips,d as startLoopbackServer,me as streamChatCompletions,D as toJsonSchema,F as toolCallFromParsed};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x-otto/provider",
3
- "version": "0.1.0-alpha.4",
3
+ "version": "0.1.0-alpha.41",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -19,11 +19,11 @@
19
19
  "tag": "alpha"
20
20
  },
21
21
  "dependencies": {
22
- "@x-otto/interchange": "0.1.0-alpha.2",
23
- "@x-otto/shared": "0.1.0-alpha.5"
22
+ "@x-otto/interchange": "0.1.0-alpha.41",
23
+ "@x-otto/shared": "0.1.0-alpha.41"
24
24
  },
25
25
  "scripts": {
26
- "build": "tsdown && rm -f dist/*.map && sed -i '' '\\|//# sourceMappingURL=|d' dist/*.js 2>/dev/null; true",
26
+ "build": "tsdown",
27
27
  "typecheck:project": "tsc -p tsconfig.json --noEmit",
28
28
  "typecheck:file": "tsc src/index.ts --noEmit --target ES2024 --module ESNext --moduleResolution bundler --esModuleInterop --skipLibCheck",
29
29
  "typecheck": "tsc --noEmit",