@dreb/ai 2.46.0 → 2.48.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.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessageEventStream } from \"./utils/event-stream.js\";\n\nexport type { AssistantMessageEventStream } from \"./utils/event-stream.js\";\n\nexport type KnownApi =\n\t| \"openai-completions\"\n\t| \"mistral-conversations\"\n\t| \"openai-responses\"\n\t| \"azure-openai-responses\"\n\t| \"openai-codex-responses\"\n\t| \"anthropic-messages\"\n\t| \"bedrock-converse-stream\"\n\t| \"google-generative-ai\"\n\t| \"google-gemini-cli\"\n\t| \"google-vertex\";\n\nexport type Api = KnownApi | (string & {});\n\nexport type KnownProvider =\n\t| \"amazon-bedrock\"\n\t| \"anthropic\"\n\t| \"google\"\n\t| \"google-gemini-cli\"\n\t| \"google-antigravity\"\n\t| \"google-vertex\"\n\t| \"openai\"\n\t| \"azure-openai-responses\"\n\t| \"openai-codex\"\n\t| \"github-copilot\"\n\t| \"xai\"\n\t| \"groq\"\n\t| \"cerebras\"\n\t| \"openrouter\"\n\t| \"vercel-ai-gateway\"\n\t| \"mistral\"\n\t| \"minimax\"\n\t| \"minimax-cn\"\n\t| \"huggingface\"\n\t| \"opencode\"\n\t| \"opencode-go\"\n\t| \"kimi-coding\"\n\t| \"kimi-coding-oauth\";\nexport type Provider = KnownProvider | string;\n\nexport type ThinkingLevel = \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\n/** Token budgets for each thinking level (token-based providers only) */\nexport interface ThinkingBudgets {\n\tminimal?: number;\n\tlow?: number;\n\tmedium?: number;\n\thigh?: number;\n}\n\n// Base options all providers share\nexport type CacheRetention = \"none\" | \"short\" | \"long\";\n\nexport type Transport = \"sse\" | \"websocket\" | \"auto\";\n\n/** Explicit credential transport for providers that support multiple auth schemes. */\nexport type AuthMode = \"api-key\" | \"bearer\";\n\nexport interface StreamOptions {\n\ttemperature?: number;\n\tmaxTokens?: number;\n\tsignal?: AbortSignal;\n\tapiKey?: string;\n\t/**\n\t * Preferred transport for providers that support multiple transports.\n\t * Providers that do not support this option ignore it.\n\t */\n\ttransport?: Transport;\n\t/**\n\t * Prompt cache retention preference. Providers map this to their supported values.\n\t * Default: \"short\".\n\t */\n\tcacheRetention?: CacheRetention;\n\t/**\n\t * Optional session identifier for providers that support session-based caching.\n\t * Providers can use this to enable prompt caching, request routing, or other\n\t * session-aware features. Ignored by providers that don't support it.\n\t */\n\tsessionId?: string;\n\t/**\n\t * Optional callback for inspecting or replacing provider payloads before sending.\n\t * Return undefined to keep the payload unchanged.\n\t */\n\tonPayload?: (payload: unknown, model: Model<Api>) => unknown | undefined | Promise<unknown | undefined>;\n\t/**\n\t * Optional custom HTTP headers to include in API requests.\n\t * Merged with provider defaults; can override default headers.\n\t * Not supported by all providers (e.g., AWS Bedrock uses SDK auth).\n\t */\n\theaders?: Record<string, string>;\n\t/**\n\t * Maximum delay in milliseconds to wait for a retry when the server requests a long wait.\n\t * If the server's requested delay exceeds this value, the request fails immediately\n\t * with an error containing the requested delay, allowing higher-level retry logic\n\t * to handle it with user visibility.\n\t * Default: 60000 (60 seconds). Set to 0 to disable the cap.\n\t */\n\tmaxRetryDelayMs?: number;\n\t/**\n\t * Optional metadata to include in API requests.\n\t * Providers extract the fields they understand and ignore the rest.\n\t * For example, Anthropic uses `user_id` for abuse tracking and rate limiting.\n\t */\n\tmetadata?: Record<string, unknown>;\n\t/**\n\t * Optional callback for non-fatal warnings during streaming (e.g. malformed JSON chunks).\n\t * Called by providers when errors are caught but don't terminate the stream.\n\t * The session layer wires this to surface warnings in the conversation.\n\t */\n\tonWarning?: (code: string, message: string) => void;\n}\n\nexport type ProviderStreamOptions = StreamOptions & Record<string, unknown>;\n\n// Unified options with reasoning passed to streamSimple() and completeSimple()\nexport interface SimpleStreamOptions extends StreamOptions {\n\treasoning?: ThinkingLevel;\n\t/** Custom token budgets for thinking levels (token-based providers only) */\n\tthinkingBudgets?: ThinkingBudgets;\n\t/**\n\t * Controls how thinking content is returned by providers that support it.\n\t * \"summarized\": return summarized thinking text. \"omitted\": return empty thinking\n\t * with only an encrypted signature (lower latency, nothing to display).\n\t * Only honored by adaptive-thinking Anthropic/Bedrock Claude models; ignored elsewhere.\n\t * When unset, the provider default applies (Opus 4.7+ default to \"omitted\").\n\t */\n\tthinkingDisplay?: \"summarized\" | \"omitted\";\n}\n\n// Generic StreamFunction with typed options.\n//\n// Contract:\n// - Must return an AssistantMessageEventStream.\n// - Once invoked, request/model/runtime failures should be encoded in the\n// returned stream, not thrown.\n// - Error termination must produce an AssistantMessage with stopReason\n// \"error\" or \"aborted\" and errorMessage, emitted via the stream protocol.\nexport type StreamFunction<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> = (\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: TOptions,\n) => AssistantMessageEventStream;\n\nexport interface TextSignatureV1 {\n\tv: 1;\n\tid: string;\n\tphase?: \"commentary\" | \"final_answer\";\n}\n\nexport interface TextContent {\n\ttype: \"text\";\n\ttext: string;\n\ttextSignature?: string; // e.g., for OpenAI responses, message metadata (legacy id string or TextSignatureV1 JSON)\n}\n\nexport interface ThinkingContent {\n\ttype: \"thinking\";\n\tthinking: string;\n\tthinkingSignature?: string; // e.g., for OpenAI responses, the reasoning item ID\n\t/** When true, the thinking content was redacted by safety filters. The opaque\n\t * encrypted payload is stored in `thinkingSignature` so it can be passed back\n\t * to the API for multi-turn continuity. */\n\tredacted?: boolean;\n}\n\nexport interface ImageContent {\n\ttype: \"image\";\n\tdata: string; // base64 encoded image data\n\tmimeType: string; // e.g., \"image/jpeg\", \"image/png\"\n}\n\nexport interface ToolCall {\n\ttype: \"toolCall\";\n\tid: string;\n\tname: string;\n\targuments: Record<string, any>;\n\tthoughtSignature?: string; // Google-specific: opaque signature for reusing thought context\n}\n\nexport interface Usage {\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\ttotalTokens: number;\n\tcost: {\n\t\tinput: number;\n\t\toutput: number;\n\t\tcacheRead: number;\n\t\tcacheWrite: number;\n\t\ttotal: number;\n\t};\n}\n\nexport type StopReason = \"stop\" | \"length\" | \"toolUse\" | \"error\" | \"aborted\";\n\nexport interface UserMessage {\n\trole: \"user\";\n\tcontent: string | (TextContent | ImageContent)[];\n\ttimestamp: number; // Unix timestamp in milliseconds\n}\n\nexport interface AssistantMessage {\n\trole: \"assistant\";\n\tcontent: (TextContent | ThinkingContent | ToolCall)[];\n\tapi: Api;\n\tprovider: Provider;\n\tmodel: string;\n\tresponseId?: string; // Provider-specific response/message identifier when the upstream API exposes one\n\tusage: Usage;\n\tstopReason: StopReason;\n\terrorMessage?: string;\n\ttimestamp: number; // Unix timestamp in milliseconds\n\tdurationMs?: number; // Time from stream start to completion in milliseconds\n}\n\nexport interface ToolResultMessage<TDetails = any> {\n\trole: \"toolResult\";\n\ttoolCallId: string;\n\ttoolName: string;\n\tcontent: (TextContent | ImageContent)[]; // Supports text and images\n\tdetails?: TDetails;\n\tisError: boolean;\n\ttimestamp: number; // Unix timestamp in milliseconds\n}\n\nexport type Message = UserMessage | AssistantMessage | ToolResultMessage;\n\nimport type { TSchema } from \"@sinclair/typebox\";\n\nexport interface Tool<TParameters extends TSchema = TSchema> {\n\tname: string;\n\tdescription: string;\n\tparameters: TParameters;\n}\n\nexport interface Context {\n\tsystemPrompt?: string;\n\tmessages: Message[];\n\ttools?: Tool[];\n}\n\n/**\n * Event protocol for AssistantMessageEventStream.\n *\n * Streams should emit `start` before partial updates, then terminate with either:\n * - `done` carrying the final successful AssistantMessage, or\n * - `error` carrying the final AssistantMessage with stopReason \"error\" or \"aborted\"\n * and errorMessage.\n */\nexport type AssistantMessageEvent =\n\t| { type: \"start\"; partial: AssistantMessage }\n\t| { type: \"text_start\"; contentIndex: number; partial: AssistantMessage }\n\t| { type: \"text_delta\"; contentIndex: number; delta: string; partial: AssistantMessage }\n\t| { type: \"text_end\"; contentIndex: number; content: string; partial: AssistantMessage }\n\t| { type: \"thinking_start\"; contentIndex: number; partial: AssistantMessage }\n\t| { type: \"thinking_delta\"; contentIndex: number; delta: string; partial: AssistantMessage }\n\t| { type: \"thinking_end\"; contentIndex: number; content: string; partial: AssistantMessage }\n\t| { type: \"toolcall_start\"; contentIndex: number; partial: AssistantMessage }\n\t| { type: \"toolcall_delta\"; contentIndex: number; delta: string; partial: AssistantMessage }\n\t| { type: \"toolcall_end\"; contentIndex: number; toolCall: ToolCall; partial: AssistantMessage }\n\t| { type: \"done\"; reason: Extract<StopReason, \"stop\" | \"length\" | \"toolUse\">; message: AssistantMessage }\n\t| { type: \"error\"; reason: Extract<StopReason, \"aborted\" | \"error\">; error: AssistantMessage };\n\n/**\n * Compatibility settings for OpenAI-compatible completions APIs.\n * Use this to override URL-based auto-detection for custom providers.\n */\nexport interface OpenAICompletionsCompat {\n\t/** Whether the provider supports the `store` field. Default: auto-detected from URL. */\n\tsupportsStore?: boolean;\n\t/** Whether the provider supports the `developer` role (vs `system`). Default: auto-detected from URL. */\n\tsupportsDeveloperRole?: boolean;\n\t/** Whether the provider supports `reasoning_effort`. Default: auto-detected from URL. */\n\tsupportsReasoningEffort?: boolean;\n\t/** Optional mapping from dreb reasoning levels to provider/model-specific `reasoning_effort` values. */\n\treasoningEffortMap?: Partial<Record<ThinkingLevel, string>>;\n\t/** Whether the provider supports `stream_options: { include_usage: true }` for token usage in streaming responses. Default: true. */\n\tsupportsUsageInStreaming?: boolean;\n\t/** Which field to use for max tokens. Default: auto-detected from URL. */\n\tmaxTokensField?: \"max_completion_tokens\" | \"max_tokens\";\n\t/** Whether tool results require the `name` field. Default: auto-detected from URL. */\n\trequiresToolResultName?: boolean;\n\t/** Whether a user message after tool results requires an assistant message in between. Default: auto-detected from URL. */\n\trequiresAssistantAfterToolResult?: boolean;\n\t/** Whether thinking blocks must be converted to text blocks with <thinking> delimiters. Default: auto-detected from URL. */\n\trequiresThinkingAsText?: boolean;\n\t/** Format for reasoning/thinking parameter. \"openai\" uses reasoning_effort, \"openrouter\" uses reasoning: { effort }, \"zai\" uses top-level enable_thinking: boolean, \"qwen\" uses top-level enable_thinking: boolean, \"qwen-chat-template\" uses chat_template_kwargs.enable_thinking, and \"kimi\" uses nested thinking: { type, effort? } plus prompt_cache_key. Default: \"openai\". */\n\tthinkingFormat?: \"openai\" | \"openrouter\" | \"zai\" | \"qwen\" | \"qwen-chat-template\" | \"kimi\";\n\t/** OpenRouter-specific routing preferences. Only used when baseUrl points to OpenRouter. */\n\topenRouterRouting?: OpenRouterRouting;\n\t/** Vercel AI Gateway routing preferences. Only used when baseUrl points to Vercel AI Gateway. */\n\tvercelGatewayRouting?: VercelGatewayRouting;\n\t/** Whether the provider supports the `strict` field in tool definitions. Default: true. */\n\tsupportsStrictMode?: boolean;\n}\n\n/** Compatibility settings for OpenAI Responses APIs. */\nexport interface OpenAIResponsesCompat {\n\t// Reserved for future use\n}\n\n/**\n * OpenRouter provider routing preferences.\n * Controls which upstream providers OpenRouter routes requests to.\n * @see https://openrouter.ai/docs/provider-routing\n */\nexport interface OpenRouterRouting {\n\t/** List of provider slugs to exclusively use for this request (e.g., [\"amazon-bedrock\", \"anthropic\"]). */\n\tonly?: string[];\n\t/** List of provider slugs to try in order (e.g., [\"anthropic\", \"openai\"]). */\n\torder?: string[];\n}\n\n/**\n * Vercel AI Gateway routing preferences.\n * Controls which upstream providers the gateway routes requests to.\n * @see https://vercel.com/docs/ai-gateway/models-and-providers/provider-options\n */\nexport interface VercelGatewayRouting {\n\t/** List of provider slugs to exclusively use for this request (e.g., [\"bedrock\", \"anthropic\"]). */\n\tonly?: string[];\n\t/** List of provider slugs to try in order (e.g., [\"anthropic\", \"openai\"]). */\n\torder?: string[];\n}\n\n// Model interface for the unified model system\nexport interface Model<TApi extends Api> {\n\tid: string;\n\tname: string;\n\tapi: TApi;\n\tprovider: Provider;\n\tbaseUrl: string;\n\treasoning: boolean;\n\tinput: (\"text\" | \"image\")[];\n\tcost: {\n\t\tinput: number; // $/million tokens\n\t\toutput: number; // $/million tokens\n\t\tcacheRead: number; // $/million tokens\n\t\tcacheWrite: number; // $/million tokens\n\t};\n\tcontextWindow: number;\n\tmaxTokens: number;\n\theaders?: Record<string, string>;\n\t/** Explicit auth transport. When unset, the provider's default and token heuristics apply. */\n\tauthMode?: AuthMode;\n\t/** Compatibility overrides for OpenAI-compatible APIs. If not set, auto-detected from baseUrl. */\n\tcompat?: TApi extends \"openai-completions\"\n\t\t? OpenAICompletionsCompat\n\t\t: TApi extends \"openai-responses\"\n\t\t\t? OpenAIResponsesCompat\n\t\t\t: never;\n}\n"]}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessageEventStream } from \"./utils/event-stream.js\";\n\nexport type { AssistantMessageEventStream } from \"./utils/event-stream.js\";\n\nexport type KnownApi =\n\t| \"openai-completions\"\n\t| \"mistral-conversations\"\n\t| \"openai-responses\"\n\t| \"azure-openai-responses\"\n\t| \"openai-codex-responses\"\n\t| \"anthropic-messages\"\n\t| \"bedrock-converse-stream\"\n\t| \"google-generative-ai\"\n\t| \"google-gemini-cli\"\n\t| \"google-vertex\";\n\nexport type Api = KnownApi | (string & {});\n\nexport type KnownProvider =\n\t| \"amazon-bedrock\"\n\t| \"anthropic\"\n\t| \"google\"\n\t| \"google-gemini-cli\"\n\t| \"google-antigravity\"\n\t| \"google-vertex\"\n\t| \"openai\"\n\t| \"azure-openai-responses\"\n\t| \"openai-codex\"\n\t| \"github-copilot\"\n\t| \"xai\"\n\t| \"groq\"\n\t| \"cerebras\"\n\t| \"openrouter\"\n\t| \"vercel-ai-gateway\"\n\t| \"mistral\"\n\t| \"minimax\"\n\t| \"minimax-cn\"\n\t| \"huggingface\"\n\t| \"opencode\"\n\t| \"opencode-go\"\n\t| \"kimi-coding\"\n\t| \"kimi-coding-oauth\";\nexport type Provider = KnownProvider | string;\n\nexport type ThinkingLevel = \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\n/** Token budgets for each thinking level (token-based providers only) */\nexport interface ThinkingBudgets {\n\tminimal?: number;\n\tlow?: number;\n\tmedium?: number;\n\thigh?: number;\n}\n\n// Base options all providers share\nexport type CacheRetention = \"none\" | \"short\" | \"long\";\n\nexport type Transport = \"sse\" | \"websocket\" | \"auto\";\n\n/** Explicit credential transport for providers that support multiple auth schemes. */\nexport type AuthMode = \"api-key\" | \"bearer\";\n\nexport interface StreamOptions {\n\ttemperature?: number;\n\tmaxTokens?: number;\n\tsignal?: AbortSignal;\n\tapiKey?: string;\n\t/**\n\t * Preferred transport for providers that support multiple transports.\n\t * Providers that do not support this option ignore it.\n\t */\n\ttransport?: Transport;\n\t/**\n\t * Prompt cache retention preference. Providers map this to their supported values.\n\t * Default: \"short\".\n\t */\n\tcacheRetention?: CacheRetention;\n\t/**\n\t * Optional session identifier for providers that support session-based caching.\n\t * Providers can use this to enable prompt caching, request routing, or other\n\t * session-aware features. Ignored by providers that don't support it.\n\t */\n\tsessionId?: string;\n\t/**\n\t * Optional callback for inspecting or replacing provider payloads before sending.\n\t * Return undefined to keep the payload unchanged.\n\t */\n\tonPayload?: (payload: unknown, model: Model<Api>) => unknown | undefined | Promise<unknown | undefined>;\n\t/**\n\t * Optional custom HTTP headers to include in API requests.\n\t * Merged with provider defaults; can override default headers.\n\t * Not supported by all providers (e.g., AWS Bedrock uses SDK auth).\n\t */\n\theaders?: Record<string, string>;\n\t/**\n\t * Maximum delay in milliseconds to wait for a retry when the server requests a long wait.\n\t * If the server's requested delay exceeds this value, the request fails immediately\n\t * with an error containing the requested delay, allowing higher-level retry logic\n\t * to handle it with user visibility.\n\t * Default: 60000 (60 seconds). Set to 0 to disable the cap.\n\t */\n\tmaxRetryDelayMs?: number;\n\t/**\n\t * Optional metadata to include in API requests.\n\t * Providers extract the fields they understand and ignore the rest.\n\t * For example, Anthropic uses `user_id` for abuse tracking and rate limiting.\n\t */\n\tmetadata?: Record<string, unknown>;\n\t/**\n\t * Optional callback for non-fatal warnings during streaming (e.g. malformed JSON chunks).\n\t * Called by providers when errors are caught but don't terminate the stream.\n\t * The session layer wires this to surface warnings in the conversation.\n\t */\n\tonWarning?: (code: string, message: string) => void;\n}\n\nexport type ProviderStreamOptions = StreamOptions & Record<string, unknown>;\n\n// Unified options with reasoning passed to streamSimple() and completeSimple()\nexport interface SimpleStreamOptions extends StreamOptions {\n\treasoning?: ThinkingLevel;\n\t/** Custom token budgets for thinking levels (token-based providers only) */\n\tthinkingBudgets?: ThinkingBudgets;\n\t/**\n\t * Controls how thinking content is returned by providers that support it.\n\t * \"summarized\": return summarized thinking text. \"omitted\": return empty thinking\n\t * with only an encrypted signature (lower latency, nothing to display).\n\t * Only honored by adaptive-thinking Anthropic/Bedrock Claude models; ignored elsewhere.\n\t * When unset, the provider default applies (Opus 4.7+ default to \"omitted\").\n\t */\n\tthinkingDisplay?: \"summarized\" | \"omitted\";\n}\n\n// Generic StreamFunction with typed options.\n//\n// Contract:\n// - Must return an AssistantMessageEventStream.\n// - Once invoked, request/model/runtime failures should be encoded in the\n// returned stream, not thrown.\n// - Error termination must produce an AssistantMessage with stopReason\n// \"error\" or \"aborted\" and errorMessage, emitted via the stream protocol.\nexport type StreamFunction<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> = (\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: TOptions,\n) => AssistantMessageEventStream;\n\nexport interface TextSignatureV1 {\n\tv: 1;\n\tid: string;\n\tphase?: \"commentary\" | \"final_answer\";\n}\n\nexport interface TextContent {\n\ttype: \"text\";\n\ttext: string;\n\ttextSignature?: string; // e.g., for OpenAI responses, message metadata (legacy id string or TextSignatureV1 JSON)\n}\n\nexport interface ThinkingContent {\n\ttype: \"thinking\";\n\tthinking: string;\n\tthinkingSignature?: string; // e.g., for OpenAI responses, the reasoning item ID\n\t/** When true, the thinking content was redacted by safety filters. The opaque\n\t * encrypted payload is stored in `thinkingSignature` so it can be passed back\n\t * to the API for multi-turn continuity. */\n\tredacted?: boolean;\n}\n\nexport interface ImageContent {\n\ttype: \"image\";\n\tdata: string; // base64 encoded image data\n\tmimeType: string; // e.g., \"image/jpeg\", \"image/png\"\n}\n\nexport interface ToolCall {\n\ttype: \"toolCall\";\n\tid: string;\n\tname: string;\n\targuments: Record<string, any>;\n\tthoughtSignature?: string; // Google-specific: opaque signature for reusing thought context\n}\n\nexport interface Usage {\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\ttotalTokens: number;\n\tcost: {\n\t\tinput: number;\n\t\toutput: number;\n\t\tcacheRead: number;\n\t\tcacheWrite: number;\n\t\ttotal: number;\n\t};\n}\n\nexport type StopReason = \"stop\" | \"length\" | \"toolUse\" | \"error\" | \"aborted\";\n\nexport interface UserMessage {\n\trole: \"user\";\n\tcontent: string | (TextContent | ImageContent)[];\n\ttimestamp: number; // Unix timestamp in milliseconds\n}\n\nexport interface AssistantMessage {\n\trole: \"assistant\";\n\tcontent: (TextContent | ThinkingContent | ToolCall)[];\n\tapi: Api;\n\tprovider: Provider;\n\tmodel: string;\n\tresponseId?: string; // Provider-specific response/message identifier when the upstream API exposes one\n\tusage: Usage;\n\tstopReason: StopReason;\n\terrorMessage?: string;\n\ttimestamp: number; // Unix timestamp in milliseconds\n\tdurationMs?: number; // Time from stream start to completion in milliseconds\n}\n\nexport interface ToolResultMessage<TDetails = any> {\n\trole: \"toolResult\";\n\ttoolCallId: string;\n\ttoolName: string;\n\tcontent: (TextContent | ImageContent)[]; // Supports text and images\n\tdetails?: TDetails;\n\tisError: boolean;\n\ttimestamp: number; // Unix timestamp in milliseconds\n}\n\nexport type Message = UserMessage | AssistantMessage | ToolResultMessage;\n\nimport type { TSchema } from \"@sinclair/typebox\";\n\nexport interface Tool<TParameters extends TSchema = TSchema> {\n\tname: string;\n\tdescription: string;\n\tparameters: TParameters;\n}\n\nexport interface Context {\n\tsystemPrompt?: string;\n\tmessages: Message[];\n\ttools?: Tool[];\n}\n\n/**\n * Event protocol for AssistantMessageEventStream.\n *\n * Streams should emit `start` before partial updates, then terminate with either:\n * - `done` carrying the final successful AssistantMessage, or\n * - `error` carrying the final AssistantMessage with stopReason \"error\" or \"aborted\"\n * and errorMessage.\n */\nexport type AssistantMessageEvent =\n\t| { type: \"start\"; partial: AssistantMessage }\n\t| { type: \"text_start\"; contentIndex: number; partial: AssistantMessage }\n\t| { type: \"text_delta\"; contentIndex: number; delta: string; partial: AssistantMessage }\n\t| { type: \"text_end\"; contentIndex: number; content: string; partial: AssistantMessage }\n\t| { type: \"thinking_start\"; contentIndex: number; partial: AssistantMessage }\n\t| { type: \"thinking_delta\"; contentIndex: number; delta: string; partial: AssistantMessage }\n\t| { type: \"thinking_end\"; contentIndex: number; content: string; partial: AssistantMessage }\n\t| { type: \"toolcall_start\"; contentIndex: number; partial: AssistantMessage }\n\t| { type: \"toolcall_delta\"; contentIndex: number; delta: string; partial: AssistantMessage }\n\t| { type: \"toolcall_end\"; contentIndex: number; toolCall: ToolCall; partial: AssistantMessage }\n\t| { type: \"done\"; reason: Extract<StopReason, \"stop\" | \"length\" | \"toolUse\">; message: AssistantMessage }\n\t| { type: \"error\"; reason: Extract<StopReason, \"aborted\" | \"error\">; error: AssistantMessage };\n\n/**\n * Compatibility settings for OpenAI-compatible completions APIs.\n * Use this to override URL-based auto-detection for custom providers.\n */\nexport interface OpenAICompletionsCompat {\n\t/** Whether the provider supports the `store` field. Default: auto-detected from URL. */\n\tsupportsStore?: boolean;\n\t/** Whether the provider supports the `developer` role (vs `system`). Default: auto-detected from URL. */\n\tsupportsDeveloperRole?: boolean;\n\t/** Whether the provider supports `reasoning_effort`. Default: auto-detected from URL. */\n\tsupportsReasoningEffort?: boolean;\n\t/** Optional mapping from dreb reasoning levels to provider/model-specific `reasoning_effort` values. */\n\treasoningEffortMap?: Partial<Record<ThinkingLevel, string>>;\n\t/** Whether the provider supports `stream_options: { include_usage: true }` for token usage in streaming responses. Default: true. */\n\tsupportsUsageInStreaming?: boolean;\n\t/** Which field to use for max tokens. Default: auto-detected from URL. */\n\tmaxTokensField?: \"max_completion_tokens\" | \"max_tokens\";\n\t/** Whether tool results require the `name` field. Default: auto-detected from URL. */\n\trequiresToolResultName?: boolean;\n\t/** Whether a user message after tool results requires an assistant message in between. Default: auto-detected from URL. */\n\trequiresAssistantAfterToolResult?: boolean;\n\t/** Whether thinking blocks must be converted to text blocks with <thinking> delimiters. Default: auto-detected from URL. */\n\trequiresThinkingAsText?: boolean;\n\t/** Format for reasoning/thinking parameter. \"openai\" uses reasoning_effort, \"openrouter\" uses reasoning: { effort }, \"zai\" uses top-level enable_thinking: boolean, \"qwen\" uses top-level enable_thinking: boolean, \"qwen-chat-template\" uses chat_template_kwargs.enable_thinking, and \"kimi\" uses nested thinking: { type, effort? } plus prompt_cache_key. Default: \"openai\". */\n\tthinkingFormat?: \"openai\" | \"openrouter\" | \"zai\" | \"qwen\" | \"qwen-chat-template\" | \"kimi\";\n\t/** OpenRouter-specific routing preferences. Only used when baseUrl points to OpenRouter. */\n\topenRouterRouting?: OpenRouterRouting;\n\t/** Vercel AI Gateway routing preferences. Only used when baseUrl points to Vercel AI Gateway. */\n\tvercelGatewayRouting?: VercelGatewayRouting;\n\t/** Whether the provider supports the `strict` field in tool definitions. Default: true. */\n\tsupportsStrictMode?: boolean;\n}\n\n/** Compatibility settings for OpenAI Responses APIs. */\nexport interface OpenAIResponsesCompat {\n\t// Reserved for future use\n}\n\n/**\n * OpenRouter provider routing preferences.\n * Controls which upstream providers OpenRouter routes requests to.\n * @see https://openrouter.ai/docs/provider-routing\n */\nexport interface OpenRouterRouting {\n\t/** List of provider slugs to exclusively use for this request (e.g., [\"amazon-bedrock\", \"anthropic\"]). */\n\tonly?: string[];\n\t/** List of provider slugs to try in order (e.g., [\"anthropic\", \"openai\"]). */\n\torder?: string[];\n}\n\n/**\n * Vercel AI Gateway routing preferences.\n * Controls which upstream providers the gateway routes requests to.\n * @see https://vercel.com/docs/ai-gateway/models-and-providers/provider-options\n */\nexport interface VercelGatewayRouting {\n\t/** List of provider slugs to exclusively use for this request (e.g., [\"bedrock\", \"anthropic\"]). */\n\tonly?: string[];\n\t/** List of provider slugs to try in order (e.g., [\"anthropic\", \"openai\"]). */\n\torder?: string[];\n}\n\n// Model interface for the unified model system\nexport interface Model<TApi extends Api> {\n\tid: string;\n\tname: string;\n\tapi: TApi;\n\tprovider: Provider;\n\tbaseUrl: string;\n\treasoning: boolean;\n\tinput: (\"text\" | \"image\")[];\n\tcost: {\n\t\tinput: number; // $/million tokens\n\t\toutput: number; // $/million tokens\n\t\tcacheRead: number; // $/million tokens\n\t\tcacheWrite: number; // $/million tokens\n\t};\n\tcontextWindow: number;\n\tmaxTokens: number;\n\theaders?: Record<string, string>;\n\t/**\n\t * Model identifier sent to the API when it differs from the registry id.\n\t * Used by context-tiered models such as Kimi K3, where dreb exposes a\n\t * single user-facing model (`k3`) but sends the cheaper 256k variant\n\t * (`k3-256k`) on the wire until the session context outgrows it.\n\t */\n\twireModelId?: string;\n\t/** Explicit auth transport. When unset, the provider's default and token heuristics apply. */\n\tauthMode?: AuthMode;\n\t/** Compatibility overrides for OpenAI-compatible APIs. If not set, auto-detected from baseUrl. */\n\tcompat?: TApi extends \"openai-completions\"\n\t\t? OpenAICompletionsCompat\n\t\t: TApi extends \"openai-responses\"\n\t\t\t? OpenAIResponsesCompat\n\t\t\t: never;\n}\n"]}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { AssistantMessageEventStream } from \"./utils/event-stream.js\";\n\nexport type { AssistantMessageEventStream } from \"./utils/event-stream.js\";\n\nexport type KnownApi =\n\t| \"openai-completions\"\n\t| \"mistral-conversations\"\n\t| \"openai-responses\"\n\t| \"azure-openai-responses\"\n\t| \"openai-codex-responses\"\n\t| \"anthropic-messages\"\n\t| \"bedrock-converse-stream\"\n\t| \"google-generative-ai\"\n\t| \"google-gemini-cli\"\n\t| \"google-vertex\";\n\nexport type Api = KnownApi | (string & {});\n\nexport type KnownProvider =\n\t| \"amazon-bedrock\"\n\t| \"anthropic\"\n\t| \"google\"\n\t| \"google-gemini-cli\"\n\t| \"google-antigravity\"\n\t| \"google-vertex\"\n\t| \"openai\"\n\t| \"azure-openai-responses\"\n\t| \"openai-codex\"\n\t| \"github-copilot\"\n\t| \"xai\"\n\t| \"groq\"\n\t| \"cerebras\"\n\t| \"openrouter\"\n\t| \"vercel-ai-gateway\"\n\t| \"mistral\"\n\t| \"minimax\"\n\t| \"minimax-cn\"\n\t| \"huggingface\"\n\t| \"opencode\"\n\t| \"opencode-go\"\n\t| \"kimi-coding\"\n\t| \"kimi-coding-oauth\";\nexport type Provider = KnownProvider | string;\n\nexport type ThinkingLevel = \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\n/** Token budgets for each thinking level (token-based providers only) */\nexport interface ThinkingBudgets {\n\tminimal?: number;\n\tlow?: number;\n\tmedium?: number;\n\thigh?: number;\n}\n\n// Base options all providers share\nexport type CacheRetention = \"none\" | \"short\" | \"long\";\n\nexport type Transport = \"sse\" | \"websocket\" | \"auto\";\n\n/** Explicit credential transport for providers that support multiple auth schemes. */\nexport type AuthMode = \"api-key\" | \"bearer\";\n\nexport interface StreamOptions {\n\ttemperature?: number;\n\tmaxTokens?: number;\n\tsignal?: AbortSignal;\n\tapiKey?: string;\n\t/**\n\t * Preferred transport for providers that support multiple transports.\n\t * Providers that do not support this option ignore it.\n\t */\n\ttransport?: Transport;\n\t/**\n\t * Prompt cache retention preference. Providers map this to their supported values.\n\t * Default: \"short\".\n\t */\n\tcacheRetention?: CacheRetention;\n\t/**\n\t * Optional session identifier for providers that support session-based caching.\n\t * Providers can use this to enable prompt caching, request routing, or other\n\t * session-aware features. Ignored by providers that don't support it.\n\t */\n\tsessionId?: string;\n\t/**\n\t * Optional callback for inspecting or replacing provider payloads before sending.\n\t * Return undefined to keep the payload unchanged.\n\t */\n\tonPayload?: (payload: unknown, model: Model<Api>) => unknown | undefined | Promise<unknown | undefined>;\n\t/**\n\t * Optional custom HTTP headers to include in API requests.\n\t * Merged with provider defaults; can override default headers.\n\t * Not supported by all providers (e.g., AWS Bedrock uses SDK auth).\n\t */\n\theaders?: Record<string, string>;\n\t/**\n\t * Maximum delay in milliseconds to wait for a retry when the server requests a long wait.\n\t * If the server's requested delay exceeds this value, the request fails immediately\n\t * with an error containing the requested delay, allowing higher-level retry logic\n\t * to handle it with user visibility.\n\t * Default: 60000 (60 seconds). Set to 0 to disable the cap.\n\t */\n\tmaxRetryDelayMs?: number;\n\t/**\n\t * Optional metadata to include in API requests.\n\t * Providers extract the fields they understand and ignore the rest.\n\t * For example, Anthropic uses `user_id` for abuse tracking and rate limiting.\n\t */\n\tmetadata?: Record<string, unknown>;\n\t/**\n\t * Optional callback for non-fatal warnings during streaming (e.g. malformed JSON chunks).\n\t * Called by providers when errors are caught but don't terminate the stream.\n\t * The session layer wires this to surface warnings in the conversation.\n\t */\n\tonWarning?: (code: string, message: string) => void;\n}\n\nexport type ProviderStreamOptions = StreamOptions & Record<string, unknown>;\n\n// Unified options with reasoning passed to streamSimple() and completeSimple()\nexport interface SimpleStreamOptions extends StreamOptions {\n\treasoning?: ThinkingLevel;\n\t/** Custom token budgets for thinking levels (token-based providers only) */\n\tthinkingBudgets?: ThinkingBudgets;\n\t/**\n\t * Controls how thinking content is returned by providers that support it.\n\t * \"summarized\": return summarized thinking text. \"omitted\": return empty thinking\n\t * with only an encrypted signature (lower latency, nothing to display).\n\t * Only honored by adaptive-thinking Anthropic/Bedrock Claude models; ignored elsewhere.\n\t * When unset, the provider default applies (Opus 4.7+ default to \"omitted\").\n\t */\n\tthinkingDisplay?: \"summarized\" | \"omitted\";\n}\n\n// Generic StreamFunction with typed options.\n//\n// Contract:\n// - Must return an AssistantMessageEventStream.\n// - Once invoked, request/model/runtime failures should be encoded in the\n// returned stream, not thrown.\n// - Error termination must produce an AssistantMessage with stopReason\n// \"error\" or \"aborted\" and errorMessage, emitted via the stream protocol.\nexport type StreamFunction<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> = (\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: TOptions,\n) => AssistantMessageEventStream;\n\nexport interface TextSignatureV1 {\n\tv: 1;\n\tid: string;\n\tphase?: \"commentary\" | \"final_answer\";\n}\n\nexport interface TextContent {\n\ttype: \"text\";\n\ttext: string;\n\ttextSignature?: string; // e.g., for OpenAI responses, message metadata (legacy id string or TextSignatureV1 JSON)\n}\n\nexport interface ThinkingContent {\n\ttype: \"thinking\";\n\tthinking: string;\n\tthinkingSignature?: string; // e.g., for OpenAI responses, the reasoning item ID\n\t/** When true, the thinking content was redacted by safety filters. The opaque\n\t * encrypted payload is stored in `thinkingSignature` so it can be passed back\n\t * to the API for multi-turn continuity. */\n\tredacted?: boolean;\n}\n\nexport interface ImageContent {\n\ttype: \"image\";\n\tdata: string; // base64 encoded image data\n\tmimeType: string; // e.g., \"image/jpeg\", \"image/png\"\n}\n\nexport interface ToolCall {\n\ttype: \"toolCall\";\n\tid: string;\n\tname: string;\n\targuments: Record<string, any>;\n\tthoughtSignature?: string; // Google-specific: opaque signature for reusing thought context\n}\n\nexport interface Usage {\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\ttotalTokens: number;\n\tcost: {\n\t\tinput: number;\n\t\toutput: number;\n\t\tcacheRead: number;\n\t\tcacheWrite: number;\n\t\ttotal: number;\n\t};\n}\n\nexport type StopReason = \"stop\" | \"length\" | \"toolUse\" | \"error\" | \"aborted\";\n\nexport interface UserMessage {\n\trole: \"user\";\n\tcontent: string | (TextContent | ImageContent)[];\n\ttimestamp: number; // Unix timestamp in milliseconds\n}\n\nexport interface AssistantMessage {\n\trole: \"assistant\";\n\tcontent: (TextContent | ThinkingContent | ToolCall)[];\n\tapi: Api;\n\tprovider: Provider;\n\tmodel: string;\n\tresponseId?: string; // Provider-specific response/message identifier when the upstream API exposes one\n\tusage: Usage;\n\tstopReason: StopReason;\n\terrorMessage?: string;\n\ttimestamp: number; // Unix timestamp in milliseconds\n\tdurationMs?: number; // Time from stream start to completion in milliseconds\n}\n\nexport interface ToolResultMessage<TDetails = any> {\n\trole: \"toolResult\";\n\ttoolCallId: string;\n\ttoolName: string;\n\tcontent: (TextContent | ImageContent)[]; // Supports text and images\n\tdetails?: TDetails;\n\tisError: boolean;\n\ttimestamp: number; // Unix timestamp in milliseconds\n}\n\nexport type Message = UserMessage | AssistantMessage | ToolResultMessage;\n\nimport type { TSchema } from \"@sinclair/typebox\";\n\nexport interface Tool<TParameters extends TSchema = TSchema> {\n\tname: string;\n\tdescription: string;\n\tparameters: TParameters;\n}\n\nexport interface Context {\n\tsystemPrompt?: string;\n\tmessages: Message[];\n\ttools?: Tool[];\n}\n\n/**\n * Event protocol for AssistantMessageEventStream.\n *\n * Streams should emit `start` before partial updates, then terminate with either:\n * - `done` carrying the final successful AssistantMessage, or\n * - `error` carrying the final AssistantMessage with stopReason \"error\" or \"aborted\"\n * and errorMessage.\n */\nexport type AssistantMessageEvent =\n\t| { type: \"start\"; partial: AssistantMessage }\n\t| { type: \"text_start\"; contentIndex: number; partial: AssistantMessage }\n\t| { type: \"text_delta\"; contentIndex: number; delta: string; partial: AssistantMessage }\n\t| { type: \"text_end\"; contentIndex: number; content: string; partial: AssistantMessage }\n\t| { type: \"thinking_start\"; contentIndex: number; partial: AssistantMessage }\n\t| { type: \"thinking_delta\"; contentIndex: number; delta: string; partial: AssistantMessage }\n\t| { type: \"thinking_end\"; contentIndex: number; content: string; partial: AssistantMessage }\n\t| { type: \"toolcall_start\"; contentIndex: number; partial: AssistantMessage }\n\t| { type: \"toolcall_delta\"; contentIndex: number; delta: string; partial: AssistantMessage }\n\t| { type: \"toolcall_end\"; contentIndex: number; toolCall: ToolCall; partial: AssistantMessage }\n\t| { type: \"done\"; reason: Extract<StopReason, \"stop\" | \"length\" | \"toolUse\">; message: AssistantMessage }\n\t| { type: \"error\"; reason: Extract<StopReason, \"aborted\" | \"error\">; error: AssistantMessage };\n\n/**\n * Compatibility settings for OpenAI-compatible completions APIs.\n * Use this to override URL-based auto-detection for custom providers.\n */\nexport interface OpenAICompletionsCompat {\n\t/** Whether the provider supports the `store` field. Default: auto-detected from URL. */\n\tsupportsStore?: boolean;\n\t/** Whether the provider supports the `developer` role (vs `system`). Default: auto-detected from URL. */\n\tsupportsDeveloperRole?: boolean;\n\t/** Whether the provider supports `reasoning_effort`. Default: auto-detected from URL. */\n\tsupportsReasoningEffort?: boolean;\n\t/** Optional mapping from dreb reasoning levels to provider/model-specific `reasoning_effort` values. */\n\treasoningEffortMap?: Partial<Record<ThinkingLevel, string>>;\n\t/** Whether the provider supports `stream_options: { include_usage: true }` for token usage in streaming responses. Default: true. */\n\tsupportsUsageInStreaming?: boolean;\n\t/** Which field to use for max tokens. Default: auto-detected from URL. */\n\tmaxTokensField?: \"max_completion_tokens\" | \"max_tokens\";\n\t/** Whether tool results require the `name` field. Default: auto-detected from URL. */\n\trequiresToolResultName?: boolean;\n\t/** Whether a user message after tool results requires an assistant message in between. Default: auto-detected from URL. */\n\trequiresAssistantAfterToolResult?: boolean;\n\t/** Whether thinking blocks must be converted to text blocks with <thinking> delimiters. Default: auto-detected from URL. */\n\trequiresThinkingAsText?: boolean;\n\t/** Format for reasoning/thinking parameter. \"openai\" uses reasoning_effort, \"openrouter\" uses reasoning: { effort }, \"zai\" uses top-level enable_thinking: boolean, \"qwen\" uses top-level enable_thinking: boolean, \"qwen-chat-template\" uses chat_template_kwargs.enable_thinking, and \"kimi\" uses nested thinking: { type, effort? } plus prompt_cache_key. Default: \"openai\". */\n\tthinkingFormat?: \"openai\" | \"openrouter\" | \"zai\" | \"qwen\" | \"qwen-chat-template\" | \"kimi\";\n\t/** OpenRouter-specific routing preferences. Only used when baseUrl points to OpenRouter. */\n\topenRouterRouting?: OpenRouterRouting;\n\t/** Vercel AI Gateway routing preferences. Only used when baseUrl points to Vercel AI Gateway. */\n\tvercelGatewayRouting?: VercelGatewayRouting;\n\t/** Whether the provider supports the `strict` field in tool definitions. Default: true. */\n\tsupportsStrictMode?: boolean;\n}\n\n/** Compatibility settings for OpenAI Responses APIs. */\nexport interface OpenAIResponsesCompat {\n\t// Reserved for future use\n}\n\n/**\n * OpenRouter provider routing preferences.\n * Controls which upstream providers OpenRouter routes requests to.\n * @see https://openrouter.ai/docs/provider-routing\n */\nexport interface OpenRouterRouting {\n\t/** List of provider slugs to exclusively use for this request (e.g., [\"amazon-bedrock\", \"anthropic\"]). */\n\tonly?: string[];\n\t/** List of provider slugs to try in order (e.g., [\"anthropic\", \"openai\"]). */\n\torder?: string[];\n}\n\n/**\n * Vercel AI Gateway routing preferences.\n * Controls which upstream providers the gateway routes requests to.\n * @see https://vercel.com/docs/ai-gateway/models-and-providers/provider-options\n */\nexport interface VercelGatewayRouting {\n\t/** List of provider slugs to exclusively use for this request (e.g., [\"bedrock\", \"anthropic\"]). */\n\tonly?: string[];\n\t/** List of provider slugs to try in order (e.g., [\"anthropic\", \"openai\"]). */\n\torder?: string[];\n}\n\n// Model interface for the unified model system\nexport interface Model<TApi extends Api> {\n\tid: string;\n\tname: string;\n\tapi: TApi;\n\tprovider: Provider;\n\tbaseUrl: string;\n\treasoning: boolean;\n\tinput: (\"text\" | \"image\")[];\n\tcost: {\n\t\tinput: number; // $/million tokens\n\t\toutput: number; // $/million tokens\n\t\tcacheRead: number; // $/million tokens\n\t\tcacheWrite: number; // $/million tokens\n\t};\n\tcontextWindow: number;\n\tmaxTokens: number;\n\theaders?: Record<string, string>;\n\t/** Explicit auth transport. When unset, the provider's default and token heuristics apply. */\n\tauthMode?: AuthMode;\n\t/** Compatibility overrides for OpenAI-compatible APIs. If not set, auto-detected from baseUrl. */\n\tcompat?: TApi extends \"openai-completions\"\n\t\t? OpenAICompletionsCompat\n\t\t: TApi extends \"openai-responses\"\n\t\t\t? OpenAIResponsesCompat\n\t\t\t: never;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { AssistantMessageEventStream } from \"./utils/event-stream.js\";\n\nexport type { AssistantMessageEventStream } from \"./utils/event-stream.js\";\n\nexport type KnownApi =\n\t| \"openai-completions\"\n\t| \"mistral-conversations\"\n\t| \"openai-responses\"\n\t| \"azure-openai-responses\"\n\t| \"openai-codex-responses\"\n\t| \"anthropic-messages\"\n\t| \"bedrock-converse-stream\"\n\t| \"google-generative-ai\"\n\t| \"google-gemini-cli\"\n\t| \"google-vertex\";\n\nexport type Api = KnownApi | (string & {});\n\nexport type KnownProvider =\n\t| \"amazon-bedrock\"\n\t| \"anthropic\"\n\t| \"google\"\n\t| \"google-gemini-cli\"\n\t| \"google-antigravity\"\n\t| \"google-vertex\"\n\t| \"openai\"\n\t| \"azure-openai-responses\"\n\t| \"openai-codex\"\n\t| \"github-copilot\"\n\t| \"xai\"\n\t| \"groq\"\n\t| \"cerebras\"\n\t| \"openrouter\"\n\t| \"vercel-ai-gateway\"\n\t| \"mistral\"\n\t| \"minimax\"\n\t| \"minimax-cn\"\n\t| \"huggingface\"\n\t| \"opencode\"\n\t| \"opencode-go\"\n\t| \"kimi-coding\"\n\t| \"kimi-coding-oauth\";\nexport type Provider = KnownProvider | string;\n\nexport type ThinkingLevel = \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\n/** Token budgets for each thinking level (token-based providers only) */\nexport interface ThinkingBudgets {\n\tminimal?: number;\n\tlow?: number;\n\tmedium?: number;\n\thigh?: number;\n}\n\n// Base options all providers share\nexport type CacheRetention = \"none\" | \"short\" | \"long\";\n\nexport type Transport = \"sse\" | \"websocket\" | \"auto\";\n\n/** Explicit credential transport for providers that support multiple auth schemes. */\nexport type AuthMode = \"api-key\" | \"bearer\";\n\nexport interface StreamOptions {\n\ttemperature?: number;\n\tmaxTokens?: number;\n\tsignal?: AbortSignal;\n\tapiKey?: string;\n\t/**\n\t * Preferred transport for providers that support multiple transports.\n\t * Providers that do not support this option ignore it.\n\t */\n\ttransport?: Transport;\n\t/**\n\t * Prompt cache retention preference. Providers map this to their supported values.\n\t * Default: \"short\".\n\t */\n\tcacheRetention?: CacheRetention;\n\t/**\n\t * Optional session identifier for providers that support session-based caching.\n\t * Providers can use this to enable prompt caching, request routing, or other\n\t * session-aware features. Ignored by providers that don't support it.\n\t */\n\tsessionId?: string;\n\t/**\n\t * Optional callback for inspecting or replacing provider payloads before sending.\n\t * Return undefined to keep the payload unchanged.\n\t */\n\tonPayload?: (payload: unknown, model: Model<Api>) => unknown | undefined | Promise<unknown | undefined>;\n\t/**\n\t * Optional custom HTTP headers to include in API requests.\n\t * Merged with provider defaults; can override default headers.\n\t * Not supported by all providers (e.g., AWS Bedrock uses SDK auth).\n\t */\n\theaders?: Record<string, string>;\n\t/**\n\t * Maximum delay in milliseconds to wait for a retry when the server requests a long wait.\n\t * If the server's requested delay exceeds this value, the request fails immediately\n\t * with an error containing the requested delay, allowing higher-level retry logic\n\t * to handle it with user visibility.\n\t * Default: 60000 (60 seconds). Set to 0 to disable the cap.\n\t */\n\tmaxRetryDelayMs?: number;\n\t/**\n\t * Optional metadata to include in API requests.\n\t * Providers extract the fields they understand and ignore the rest.\n\t * For example, Anthropic uses `user_id` for abuse tracking and rate limiting.\n\t */\n\tmetadata?: Record<string, unknown>;\n\t/**\n\t * Optional callback for non-fatal warnings during streaming (e.g. malformed JSON chunks).\n\t * Called by providers when errors are caught but don't terminate the stream.\n\t * The session layer wires this to surface warnings in the conversation.\n\t */\n\tonWarning?: (code: string, message: string) => void;\n}\n\nexport type ProviderStreamOptions = StreamOptions & Record<string, unknown>;\n\n// Unified options with reasoning passed to streamSimple() and completeSimple()\nexport interface SimpleStreamOptions extends StreamOptions {\n\treasoning?: ThinkingLevel;\n\t/** Custom token budgets for thinking levels (token-based providers only) */\n\tthinkingBudgets?: ThinkingBudgets;\n\t/**\n\t * Controls how thinking content is returned by providers that support it.\n\t * \"summarized\": return summarized thinking text. \"omitted\": return empty thinking\n\t * with only an encrypted signature (lower latency, nothing to display).\n\t * Only honored by adaptive-thinking Anthropic/Bedrock Claude models; ignored elsewhere.\n\t * When unset, the provider default applies (Opus 4.7+ default to \"omitted\").\n\t */\n\tthinkingDisplay?: \"summarized\" | \"omitted\";\n}\n\n// Generic StreamFunction with typed options.\n//\n// Contract:\n// - Must return an AssistantMessageEventStream.\n// - Once invoked, request/model/runtime failures should be encoded in the\n// returned stream, not thrown.\n// - Error termination must produce an AssistantMessage with stopReason\n// \"error\" or \"aborted\" and errorMessage, emitted via the stream protocol.\nexport type StreamFunction<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> = (\n\tmodel: Model<TApi>,\n\tcontext: Context,\n\toptions?: TOptions,\n) => AssistantMessageEventStream;\n\nexport interface TextSignatureV1 {\n\tv: 1;\n\tid: string;\n\tphase?: \"commentary\" | \"final_answer\";\n}\n\nexport interface TextContent {\n\ttype: \"text\";\n\ttext: string;\n\ttextSignature?: string; // e.g., for OpenAI responses, message metadata (legacy id string or TextSignatureV1 JSON)\n}\n\nexport interface ThinkingContent {\n\ttype: \"thinking\";\n\tthinking: string;\n\tthinkingSignature?: string; // e.g., for OpenAI responses, the reasoning item ID\n\t/** When true, the thinking content was redacted by safety filters. The opaque\n\t * encrypted payload is stored in `thinkingSignature` so it can be passed back\n\t * to the API for multi-turn continuity. */\n\tredacted?: boolean;\n}\n\nexport interface ImageContent {\n\ttype: \"image\";\n\tdata: string; // base64 encoded image data\n\tmimeType: string; // e.g., \"image/jpeg\", \"image/png\"\n}\n\nexport interface ToolCall {\n\ttype: \"toolCall\";\n\tid: string;\n\tname: string;\n\targuments: Record<string, any>;\n\tthoughtSignature?: string; // Google-specific: opaque signature for reusing thought context\n}\n\nexport interface Usage {\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\ttotalTokens: number;\n\tcost: {\n\t\tinput: number;\n\t\toutput: number;\n\t\tcacheRead: number;\n\t\tcacheWrite: number;\n\t\ttotal: number;\n\t};\n}\n\nexport type StopReason = \"stop\" | \"length\" | \"toolUse\" | \"error\" | \"aborted\";\n\nexport interface UserMessage {\n\trole: \"user\";\n\tcontent: string | (TextContent | ImageContent)[];\n\ttimestamp: number; // Unix timestamp in milliseconds\n}\n\nexport interface AssistantMessage {\n\trole: \"assistant\";\n\tcontent: (TextContent | ThinkingContent | ToolCall)[];\n\tapi: Api;\n\tprovider: Provider;\n\tmodel: string;\n\tresponseId?: string; // Provider-specific response/message identifier when the upstream API exposes one\n\tusage: Usage;\n\tstopReason: StopReason;\n\terrorMessage?: string;\n\ttimestamp: number; // Unix timestamp in milliseconds\n\tdurationMs?: number; // Time from stream start to completion in milliseconds\n}\n\nexport interface ToolResultMessage<TDetails = any> {\n\trole: \"toolResult\";\n\ttoolCallId: string;\n\ttoolName: string;\n\tcontent: (TextContent | ImageContent)[]; // Supports text and images\n\tdetails?: TDetails;\n\tisError: boolean;\n\ttimestamp: number; // Unix timestamp in milliseconds\n}\n\nexport type Message = UserMessage | AssistantMessage | ToolResultMessage;\n\nimport type { TSchema } from \"@sinclair/typebox\";\n\nexport interface Tool<TParameters extends TSchema = TSchema> {\n\tname: string;\n\tdescription: string;\n\tparameters: TParameters;\n}\n\nexport interface Context {\n\tsystemPrompt?: string;\n\tmessages: Message[];\n\ttools?: Tool[];\n}\n\n/**\n * Event protocol for AssistantMessageEventStream.\n *\n * Streams should emit `start` before partial updates, then terminate with either:\n * - `done` carrying the final successful AssistantMessage, or\n * - `error` carrying the final AssistantMessage with stopReason \"error\" or \"aborted\"\n * and errorMessage.\n */\nexport type AssistantMessageEvent =\n\t| { type: \"start\"; partial: AssistantMessage }\n\t| { type: \"text_start\"; contentIndex: number; partial: AssistantMessage }\n\t| { type: \"text_delta\"; contentIndex: number; delta: string; partial: AssistantMessage }\n\t| { type: \"text_end\"; contentIndex: number; content: string; partial: AssistantMessage }\n\t| { type: \"thinking_start\"; contentIndex: number; partial: AssistantMessage }\n\t| { type: \"thinking_delta\"; contentIndex: number; delta: string; partial: AssistantMessage }\n\t| { type: \"thinking_end\"; contentIndex: number; content: string; partial: AssistantMessage }\n\t| { type: \"toolcall_start\"; contentIndex: number; partial: AssistantMessage }\n\t| { type: \"toolcall_delta\"; contentIndex: number; delta: string; partial: AssistantMessage }\n\t| { type: \"toolcall_end\"; contentIndex: number; toolCall: ToolCall; partial: AssistantMessage }\n\t| { type: \"done\"; reason: Extract<StopReason, \"stop\" | \"length\" | \"toolUse\">; message: AssistantMessage }\n\t| { type: \"error\"; reason: Extract<StopReason, \"aborted\" | \"error\">; error: AssistantMessage };\n\n/**\n * Compatibility settings for OpenAI-compatible completions APIs.\n * Use this to override URL-based auto-detection for custom providers.\n */\nexport interface OpenAICompletionsCompat {\n\t/** Whether the provider supports the `store` field. Default: auto-detected from URL. */\n\tsupportsStore?: boolean;\n\t/** Whether the provider supports the `developer` role (vs `system`). Default: auto-detected from URL. */\n\tsupportsDeveloperRole?: boolean;\n\t/** Whether the provider supports `reasoning_effort`. Default: auto-detected from URL. */\n\tsupportsReasoningEffort?: boolean;\n\t/** Optional mapping from dreb reasoning levels to provider/model-specific `reasoning_effort` values. */\n\treasoningEffortMap?: Partial<Record<ThinkingLevel, string>>;\n\t/** Whether the provider supports `stream_options: { include_usage: true }` for token usage in streaming responses. Default: true. */\n\tsupportsUsageInStreaming?: boolean;\n\t/** Which field to use for max tokens. Default: auto-detected from URL. */\n\tmaxTokensField?: \"max_completion_tokens\" | \"max_tokens\";\n\t/** Whether tool results require the `name` field. Default: auto-detected from URL. */\n\trequiresToolResultName?: boolean;\n\t/** Whether a user message after tool results requires an assistant message in between. Default: auto-detected from URL. */\n\trequiresAssistantAfterToolResult?: boolean;\n\t/** Whether thinking blocks must be converted to text blocks with <thinking> delimiters. Default: auto-detected from URL. */\n\trequiresThinkingAsText?: boolean;\n\t/** Format for reasoning/thinking parameter. \"openai\" uses reasoning_effort, \"openrouter\" uses reasoning: { effort }, \"zai\" uses top-level enable_thinking: boolean, \"qwen\" uses top-level enable_thinking: boolean, \"qwen-chat-template\" uses chat_template_kwargs.enable_thinking, and \"kimi\" uses nested thinking: { type, effort? } plus prompt_cache_key. Default: \"openai\". */\n\tthinkingFormat?: \"openai\" | \"openrouter\" | \"zai\" | \"qwen\" | \"qwen-chat-template\" | \"kimi\";\n\t/** OpenRouter-specific routing preferences. Only used when baseUrl points to OpenRouter. */\n\topenRouterRouting?: OpenRouterRouting;\n\t/** Vercel AI Gateway routing preferences. Only used when baseUrl points to Vercel AI Gateway. */\n\tvercelGatewayRouting?: VercelGatewayRouting;\n\t/** Whether the provider supports the `strict` field in tool definitions. Default: true. */\n\tsupportsStrictMode?: boolean;\n}\n\n/** Compatibility settings for OpenAI Responses APIs. */\nexport interface OpenAIResponsesCompat {\n\t// Reserved for future use\n}\n\n/**\n * OpenRouter provider routing preferences.\n * Controls which upstream providers OpenRouter routes requests to.\n * @see https://openrouter.ai/docs/provider-routing\n */\nexport interface OpenRouterRouting {\n\t/** List of provider slugs to exclusively use for this request (e.g., [\"amazon-bedrock\", \"anthropic\"]). */\n\tonly?: string[];\n\t/** List of provider slugs to try in order (e.g., [\"anthropic\", \"openai\"]). */\n\torder?: string[];\n}\n\n/**\n * Vercel AI Gateway routing preferences.\n * Controls which upstream providers the gateway routes requests to.\n * @see https://vercel.com/docs/ai-gateway/models-and-providers/provider-options\n */\nexport interface VercelGatewayRouting {\n\t/** List of provider slugs to exclusively use for this request (e.g., [\"bedrock\", \"anthropic\"]). */\n\tonly?: string[];\n\t/** List of provider slugs to try in order (e.g., [\"anthropic\", \"openai\"]). */\n\torder?: string[];\n}\n\n// Model interface for the unified model system\nexport interface Model<TApi extends Api> {\n\tid: string;\n\tname: string;\n\tapi: TApi;\n\tprovider: Provider;\n\tbaseUrl: string;\n\treasoning: boolean;\n\tinput: (\"text\" | \"image\")[];\n\tcost: {\n\t\tinput: number; // $/million tokens\n\t\toutput: number; // $/million tokens\n\t\tcacheRead: number; // $/million tokens\n\t\tcacheWrite: number; // $/million tokens\n\t};\n\tcontextWindow: number;\n\tmaxTokens: number;\n\theaders?: Record<string, string>;\n\t/**\n\t * Model identifier sent to the API when it differs from the registry id.\n\t * Used by context-tiered models such as Kimi K3, where dreb exposes a\n\t * single user-facing model (`k3`) but sends the cheaper 256k variant\n\t * (`k3-256k`) on the wire until the session context outgrows it.\n\t */\n\twireModelId?: string;\n\t/** Explicit auth transport. When unset, the provider's default and token heuristics apply. */\n\tauthMode?: AuthMode;\n\t/** Compatibility overrides for OpenAI-compatible APIs. If not set, auto-detected from baseUrl. */\n\tcompat?: TApi extends \"openai-completions\"\n\t\t? OpenAICompletionsCompat\n\t\t: TApi extends \"openai-responses\"\n\t\t\t? OpenAIResponsesCompat\n\t\t\t: never;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"kimi-coding.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/kimi-coding.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAAE,gBAAgB,EAAuB,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAoHhG;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWzD;AAmCD,MAAM,MAAM,aAAa,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC;IAChD,aAAa,CAAC,EAAE;QACf,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,cAAc,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB,CAAC;AAuIF;;;GAGG;AACH,wBAAsB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAmBpG;AAyND,wBAAsB,eAAe,CAAC,OAAO,EAAE;IAC9C,MAAM,EAAE,CAAC,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/D,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAqD5B;AAMD,wBAAsB,sBAAsB,CAC3C,WAAW,EAAE,gBAAgB,EAC7B,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,gBAAgB,CAAC,CAiC3B;AAMD,eAAO,MAAM,uBAAuB,EAAE,sBA2KrC,CAAC","sourcesContent":["/**\n * Kimi For Coding OAuth flow (device code)\n *\n * Authenticates against Moonshot's Kimi API (auth.kimi.com) using the current Kimi Code device identity.\n * Uses the device authorization grant flow to obtain access/refresh tokens,\n * then discovers the user's model entitlement via the /models endpoint.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { Api, Model } from \"../../types.js\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from \"./types.js\";\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nfunction readDrebVersion(): string {\n\ttry {\n\t\tconst pkg = JSON.parse(fs.readFileSync(new URL(\"../../../package.json\", import.meta.url), \"utf-8\")) as {\n\t\t\tversion?: unknown;\n\t\t};\n\t\tif (typeof pkg.version === \"string\" && pkg.version.length > 0) return pkg.version;\n\t} catch {\n\t\t// Fall back for unusual bundlers that omit package.json.\n\t}\n\treturn \"unknown\";\n}\n\nconst DREB_VERSION = readDrebVersion();\nconst USER_AGENT = `dreb/${DREB_VERSION}`;\nconst OAUTH_HOST = \"https://auth.kimi.com\";\nconst OAUTH_DEVICE_AUTH_URL = `${OAUTH_HOST}/api/oauth/device_authorization`;\nconst OAUTH_TOKEN_URL = `${OAUTH_HOST}/api/oauth/token`;\nconst OAUTH_CLIENT_ID = \"17e5f671-d194-4dfb-9706-5516cb48c098\";\nconst OAUTH_DEVICE_GRANT = \"urn:ietf:params:oauth:grant-type:device_code\";\nconst OAUTH_REFRESH_GRANT = \"refresh_token\";\nconst API_BASE_URL = \"https://api.kimi.com/coding/v1\";\n\nconst KIMI_CODE_HOME = path.join(os.homedir(), \".kimi-code\");\nconst DEVICE_ID_PATH = path.join(KIMI_CODE_HOME, \"device_id\");\n\nconst MAX_REFRESH_RETRIES = 3;\nconst MAX_DEVICE_FLOW_MS = 15 * 60 * 1000;\n\n// ============================================================================\n// Device ID\n// ============================================================================\n\nlet sessionDeviceId: string | undefined;\n\nfunction getDeviceId(): string {\n\tif (sessionDeviceId) return sessionDeviceId;\n\ttry {\n\t\tif (fs.existsSync(DEVICE_ID_PATH)) {\n\t\t\tconst id = fs.readFileSync(DEVICE_ID_PATH, \"utf-8\").trim();\n\t\t\tif (id.length > 0) {\n\t\t\t\tsessionDeviceId = id;\n\t\t\t\treturn id;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Fall through to generate.\n\t}\n\n\tconst id = randomUUID();\n\tsessionDeviceId = id;\n\ttry {\n\t\tfs.mkdirSync(KIMI_CODE_HOME, { recursive: true, mode: 0o700 });\n\t\tfs.writeFileSync(DEVICE_ID_PATH, id, { encoding: \"utf-8\", mode: 0o600 });\n\t} catch {\n\t\t// If we can't persist, just use the generated ID for this session.\n\t}\n\treturn id;\n}\n\n// ============================================================================\n// Header helpers\n// ============================================================================\n\n/**\n * Strip non-ASCII characters from a string for use in HTTP header values.\n */\nfunction asciiHeaderValue(value: string, fallback = \"unknown\"): string {\n\tconst cleaned = value.replace(/[^\\x20-\\x7E]/g, \"\").trim();\n\treturn cleaned.length > 0 ? cleaned : fallback;\n}\n\nfunction customKimiHeaders(): Record<string, string> {\n\tconst raw = process.env.KIMI_CODE_CUSTOM_HEADERS?.trim();\n\tif (!raw) return {};\n\tconst headers: Record<string, string> = {};\n\tfor (const line of raw.split(\"\\n\")) {\n\t\tconst colon = line.indexOf(\":\");\n\t\tif (colon < 0) continue;\n\t\tconst name = line.slice(0, colon).trim();\n\t\tif (!name) continue;\n\t\theaders[name] = line.slice(colon + 1).trim();\n\t}\n\treturn headers;\n}\n\n/**\n * Determine the device model string, mirroring kimi-cli logic.\n */\nfunction kimiDeviceModel(): string {\n\tconst platform = os.platform();\n\tconst machine = os.arch();\n\n\tif (platform === \"darwin\") {\n\t\tlet version: string;\n\t\ttry {\n\t\t\tversion = execFileSync(\"/usr/bin/sw_vers\", [\"-productVersion\"], { encoding: \"utf-8\", timeout: 3000 }).trim();\n\t\t} catch {\n\t\t\tversion = os.release();\n\t\t}\n\t\treturn `macOS ${version} ${machine}`;\n\t}\n\n\tif (platform === \"win32\") {\n\t\treturn `Windows ${os.release()} ${machine}`;\n\t}\n\n\t// Linux and other\n\treturn `${os.type()} ${os.release()} ${machine}`.trim();\n}\n\n/**\n * Build the standard set of headers required on every Kimi API request.\n */\nexport function buildKimiHeaders(): Record<string, string> {\n\treturn {\n\t\t...customKimiHeaders(),\n\t\t\"User-Agent\": USER_AGENT,\n\t\t\"X-Msh-Platform\": \"kimi_code_cli\",\n\t\t\"X-Msh-Version\": DREB_VERSION,\n\t\t\"X-Msh-Device-Name\": asciiHeaderValue(os.hostname()),\n\t\t\"X-Msh-Device-Model\": asciiHeaderValue(kimiDeviceModel()),\n\t\t\"X-Msh-Device-Id\": getDeviceId(),\n\t\t\"X-Msh-Os-Version\": asciiHeaderValue(os.release()),\n\t};\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\ntype DeviceCodeResponse = {\n\tdevice_code: string;\n\tuser_code: string;\n\tverification_uri_complete: string;\n\tinterval: number;\n\texpires_in: number;\n};\n\ntype TokenSuccessResponse = {\n\taccess_token: string;\n\trefresh_token: string;\n\texpires_in: number;\n};\n\nfunction parseTokenSuccess(response: Record<string, unknown>): TokenSuccessResponse {\n\tif (\n\t\ttypeof response.access_token !== \"string\" ||\n\t\tresponse.access_token.length === 0 ||\n\t\ttypeof response.refresh_token !== \"string\" ||\n\t\tresponse.refresh_token.length === 0 ||\n\t\ttypeof response.expires_in !== \"number\" ||\n\t\t!Number.isFinite(response.expires_in) ||\n\t\tresponse.expires_in <= 0\n\t) {\n\t\tthrow new Error(\"Invalid token response fields\");\n\t}\n\treturn response as unknown as TokenSuccessResponse;\n}\n\nexport type KimiModelInfo = {\n\tid: string;\n\tdisplay_name?: string;\n\tcontext_length: number;\n\tsupports_reasoning?: boolean;\n\tsupports_image_in?: boolean;\n\tsupports_video_in?: boolean;\n\tsupports_tool_use?: boolean;\n\tsupports_thinking_type?: \"only\" | \"no\" | \"both\";\n\tthink_efforts?: {\n\t\tsupport?: boolean;\n\t\tvalid_efforts?: string[];\n\t\tdefault_effort?: string;\n\t};\n\tprotocol?: string;\n\t[key: string]: unknown;\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction parseStringArray(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\tconst out = value.filter((v): v is string => typeof v === \"string\" && v.length > 0);\n\treturn out.length > 0 ? out : undefined;\n}\n\nfunction parseSupportsThinkingType(value: unknown): KimiModelInfo[\"supports_thinking_type\"] | undefined {\n\treturn value === \"only\" || value === \"no\" || value === \"both\" ? value : undefined;\n}\n\nfunction parseModelProtocol(value: unknown): \"anthropic\" | undefined {\n\treturn value === \"anthropic\" ? \"anthropic\" : undefined;\n}\n\nfunction parseThinkEfforts(value: unknown): KimiModelInfo[\"think_efforts\"] | undefined {\n\tif (!isRecord(value) || value.support !== true) return undefined;\n\treturn {\n\t\tsupport: true,\n\t\tvalid_efforts: parseStringArray(value.valid_efforts),\n\t\tdefault_effort:\n\t\t\ttypeof value.default_effort === \"string\" && value.default_effort.length > 0 ? value.default_effort : undefined,\n\t};\n}\n\nfunction toKimiModelInfo(item: unknown): KimiModelInfo | undefined {\n\tif (!isRecord(item) || typeof item.id !== \"string\" || item.id.length === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst contextLength = Number(item.context_length);\n\tif (!Number.isInteger(contextLength) || contextLength <= 0) {\n\t\tthrow new Error(`Kimi Code model \"${item.id}\" must include a positive context_length.`);\n\t}\n\n\tconst displayName = item.display_name;\n\tconst normalizedDisplayName = typeof displayName === \"string\" && displayName.length > 0 ? displayName : undefined;\n\n\tconst optionalBoolean = (value: unknown): boolean | undefined => (typeof value === \"boolean\" ? value : undefined);\n\n\tconst parsed: KimiModelInfo = {\n\t\tid: item.id,\n\t\tcontext_length: contextLength,\n\t\tdisplay_name: normalizedDisplayName,\n\t};\n\n\tconst supportsReasoning = optionalBoolean(item.supports_reasoning);\n\tif (supportsReasoning !== undefined) parsed.supports_reasoning = supportsReasoning;\n\n\tconst supportsImageIn = optionalBoolean(item.supports_image_in);\n\tif (supportsImageIn !== undefined) parsed.supports_image_in = supportsImageIn;\n\n\tconst supportsVideoIn = optionalBoolean(item.supports_video_in);\n\tif (supportsVideoIn !== undefined) parsed.supports_video_in = supportsVideoIn;\n\n\tif (Object.hasOwn(item, \"supports_tool_use\")) {\n\t\tconst supportsToolUse = optionalBoolean(item.supports_tool_use);\n\t\tif (supportsToolUse !== undefined) parsed.supports_tool_use = supportsToolUse;\n\t}\n\n\tconst supportsThinkingType = parseSupportsThinkingType(item.supports_thinking_type);\n\tif (supportsThinkingType !== undefined) parsed.supports_thinking_type = supportsThinkingType;\n\n\tconst protocol = parseModelProtocol(item.protocol);\n\tif (protocol !== undefined) parsed.protocol = protocol;\n\n\tconst thinkEfforts = parseThinkEfforts(item.think_efforts);\n\tif (thinkEfforts !== undefined) parsed.think_efforts = thinkEfforts;\n\n\treturn parsed;\n}\n\ntype KimiCredentials = OAuthCredentials & {\n\t/** Full list of models discovered from the Kimi API. */\n\tmodels?: KimiModelInfo[];\n\t/** @deprecated Kept for legacy credential compatibility; derived from models[0]. */\n\tmodelId?: string;\n\t/** @deprecated Kept for legacy credential compatibility; derived from models[0]. */\n\tcontextLength?: number;\n\t/** @deprecated Kept for legacy credential compatibility; derived from models[0]. */\n\tmodelDisplay?: string;\n};\n\n// ============================================================================\n// Network helpers\n// ============================================================================\n\nconst REQUEST_TIMEOUT_MS = 30_000;\n\nfunction requestSignal(signal?: AbortSignal): AbortSignal {\n\tconst timeout = AbortSignal.timeout(REQUEST_TIMEOUT_MS);\n\treturn signal ? AbortSignal.any([signal, timeout]) : timeout;\n}\n\nasync function fetchJson(url: string, init: RequestInit): Promise<unknown> {\n\tconst response = await fetch(url, init);\n\tif (!response.ok) {\n\t\tconst text = await response.text();\n\t\tthrow new Error(`${response.status} ${response.statusText}: ${text}`);\n\t}\n\treturn response.json();\n}\n\n/**\n * Sleep that can be interrupted by an AbortSignal.\n */\nfunction abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(new Error(\"Login cancelled\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst timeout = setTimeout(resolve, ms);\n\n\t\tsignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\treject(new Error(\"Login cancelled\"));\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\n// ============================================================================\n// Model discovery\n// ============================================================================\n\n/**\n * List available models from the Kimi API.\n * Returns the model info array from the response's `data` field.\n */\nexport async function listModels(accessToken: string, signal?: AbortSignal): Promise<KimiModelInfo[]> {\n\tconst raw = await fetchJson(`${API_BASE_URL}/models`, {\n\t\tsignal: requestSignal(signal),\n\t\theaders: {\n\t\t\t...buildKimiHeaders(),\n\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t},\n\t});\n\n\tif (!raw || typeof raw !== \"object\") {\n\t\tthrow new Error(\"Invalid models response\");\n\t}\n\n\tconst data = (raw as Record<string, unknown>).data;\n\tif (!Array.isArray(data)) {\n\t\tthrow new Error(\"Invalid models response: expected data array\");\n\t}\n\n\treturn data.map((item) => toKimiModelInfo(item)).filter((item): item is KimiModelInfo => item !== undefined);\n}\n\n// ============================================================================\n// Device flow\n// ============================================================================\n\nasync function startDeviceFlow(signal?: AbortSignal): Promise<DeviceCodeResponse> {\n\tconst data = await fetchJson(OAUTH_DEVICE_AUTH_URL, {\n\t\tmethod: \"POST\",\n\t\tsignal: requestSignal(signal),\n\t\theaders: {\n\t\t\t...buildKimiHeaders(),\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t},\n\t\tbody: new URLSearchParams({ client_id: OAUTH_CLIENT_ID }),\n\t});\n\n\tif (!data || typeof data !== \"object\") {\n\t\tthrow new Error(\"Invalid device code response\");\n\t}\n\n\tconst d = data as Record<string, unknown>;\n\tconst device_code = d.device_code;\n\tconst user_code = d.user_code;\n\tconst verification_uri_complete =\n\t\ttypeof d.verification_uri_complete === \"string\" && d.verification_uri_complete.length > 0\n\t\t\t? d.verification_uri_complete\n\t\t\t: typeof d.verification_uri === \"string\" && d.verification_uri.length > 0\n\t\t\t\t? `${d.verification_uri}${d.verification_uri.includes(\"?\") ? \"&\" : \"?\"}user_code=${encodeURIComponent(user_code as string)}`\n\t\t\t\t: undefined;\n\tconst interval = d.interval;\n\tconst expires_in = d.expires_in;\n\n\tif (\n\t\ttypeof device_code !== \"string\" ||\n\t\ttypeof user_code !== \"string\" ||\n\t\ttypeof verification_uri_complete !== \"string\" ||\n\t\ttypeof interval !== \"number\" ||\n\t\ttypeof expires_in !== \"number\"\n\t) {\n\t\tthrow new Error(\"Invalid device code response fields\");\n\t}\n\n\treturn { device_code, user_code, verification_uri_complete, interval, expires_in };\n}\n\nclass DeviceCodeExpiredError extends Error {\n\tconstructor() {\n\t\tsuper(\"Device code expired\");\n\t\tthis.name = \"DeviceCodeExpiredError\";\n\t}\n}\n\nasync function pollForAccessToken(\n\tdeviceCode: string,\n\tintervalSeconds: number,\n\texpiresIn: number,\n\tsignal?: AbortSignal,\n\toverallDeadline = Date.now() + MAX_DEVICE_FLOW_MS,\n): Promise<TokenSuccessResponse> {\n\tconst deadline = Math.min(Date.now() + expiresIn * 1000, overallDeadline);\n\tlet intervalMs = Math.max(1000, Math.floor(intervalSeconds * 1000));\n\n\twhile (Date.now() < deadline) {\n\t\tif (signal?.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\n\t\tconst remainingMs = deadline - Date.now();\n\t\tconst waitMs = Math.min(intervalMs, remainingMs);\n\t\tawait abortableSleep(waitMs, signal);\n\n\t\tconst tokenResponse = await fetch(OAUTH_TOKEN_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\tsignal: requestSignal(signal),\n\t\t\theaders: {\n\t\t\t\t...buildKimiHeaders(),\n\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t},\n\t\t\tbody: new URLSearchParams({\n\t\t\t\tclient_id: OAUTH_CLIENT_ID,\n\t\t\t\tdevice_code: deviceCode,\n\t\t\t\tgrant_type: OAUTH_DEVICE_GRANT,\n\t\t\t}),\n\t\t});\n\n\t\t// The token endpoint returns 400 for authorization_pending / slow_down / expired_token.\n\t\t// We must read the body regardless of status to handle the OAuth error codes.\n\t\tconst resp = (await tokenResponse.json()) as Record<string, unknown>;\n\n\t\t// Success: has access_token\n\t\tif (typeof resp.access_token === \"string\") {\n\t\t\treturn parseTokenSuccess(resp);\n\t\t}\n\n\t\t// Error response (RFC 8628 §3.5)\n\t\tif (typeof resp.error === \"string\") {\n\t\t\tconst error = resp.error;\n\t\t\tconst description = resp.error_description as string | undefined;\n\t\t\tconst newInterval = resp.interval as number | undefined;\n\n\t\t\tif (error === \"authorization_pending\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (error === \"slow_down\") {\n\t\t\t\tintervalMs =\n\t\t\t\t\ttypeof newInterval === \"number\" && newInterval > 0\n\t\t\t\t\t\t? newInterval * 1000\n\t\t\t\t\t\t: Math.max(1000, intervalMs + 5000);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (error === \"expired_token\") {\n\t\t\t\tthrow new DeviceCodeExpiredError();\n\t\t\t}\n\n\t\t\tconst descriptionSuffix = description ? `: ${description}` : \"\";\n\t\t\tthrow new Error(`Device flow failed: ${error}${descriptionSuffix}`);\n\t\t}\n\n\t\t// Unexpected response: valid object but no access_token or error field\n\t\tthrow new Error(`Unexpected token response: ${JSON.stringify(resp)}`);\n\t}\n\n\tthrow new Error(\"Device flow timed out\");\n}\n\n// ============================================================================\n// Refresh with retry\n// ============================================================================\n\nconst RETRYABLE_STATUS_CODES = [429, 500, 502, 503, 504];\n\nclass RetriableError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"RetriableError\";\n\t}\n}\n\n/**\n * Heuristic to detect network-level errors that should be retried.\n * Fetch throws TypeError on network failures; some runtimes include\n * recognizable substrings in the message.\n */\nfunction isNetworkError(error: Error): boolean {\n\tif (error instanceof TypeError || error.name === \"TimeoutError\") return true;\n\tconst msg = error.message.toLowerCase();\n\treturn [\"fetch failed\", \"econnrefused\", \"etimedout\", \"enotfound\", \"econnreset\", \"socket hang up\"].some((s) =>\n\t\tmsg.includes(s),\n\t);\n}\n\nasync function refreshWithRetry(refreshToken: string, signal?: AbortSignal): Promise<TokenSuccessResponse> {\n\tlet lastError: Error | undefined;\n\n\tfor (let attempt = 0; attempt < MAX_REFRESH_RETRIES; attempt++) {\n\t\tif (signal?.aborted) {\n\t\t\tthrow new Error(\"Refresh cancelled\");\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(OAUTH_TOKEN_URL, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tsignal: requestSignal(signal),\n\t\t\t\theaders: {\n\t\t\t\t\t...buildKimiHeaders(),\n\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t},\n\t\t\t\tbody: new URLSearchParams({\n\t\t\t\t\tclient_id: OAUTH_CLIENT_ID,\n\t\t\t\t\trefresh_token: refreshToken,\n\t\t\t\t\tgrant_type: OAUTH_REFRESH_GRANT,\n\t\t\t\t}),\n\t\t\t});\n\n\t\t\t// Retry on retriable status codes\n\t\t\tif (RETRYABLE_STATUS_CODES.includes(response.status)) {\n\t\t\t\tthrow new RetriableError(`Token refresh failed with status ${response.status}`);\n\t\t\t}\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text();\n\t\t\t\tthrow new Error(`Token refresh failed: ${response.status} ${response.statusText}: ${text}`);\n\t\t\t}\n\n\t\t\tconst raw = await response.json();\n\t\t\tif (!raw || typeof raw !== \"object\") throw new Error(\"Invalid token refresh response\");\n\t\t\treturn parseTokenSuccess(raw as Record<string, unknown>);\n\t\t} catch (error) {\n\t\t\tif (signal?.aborted) throw new Error(\"Refresh cancelled\");\n\t\t\tlastError = error instanceof Error ? error : new Error(String(error));\n\n\t\t\t// Wrap network errors (TypeError from fetch, or common network failure indicators) as retriable\n\t\t\tif (!(lastError instanceof RetriableError) && isNetworkError(lastError)) {\n\t\t\t\tlastError = new RetriableError(lastError.message);\n\t\t\t}\n\n\t\t\t// Retry on retriable errors (network failures or retriable HTTP status codes)\n\t\t\tif (lastError instanceof RetriableError && attempt < MAX_REFRESH_RETRIES - 1) {\n\t\t\t\tconst backoffMs = Math.min(1000 * 2 ** attempt, 10000);\n\t\t\t\tawait abortableSleep(backoffMs, signal);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tthrow lastError;\n\t\t}\n\t}\n\n\tthrow lastError ?? new Error(\"Token refresh failed after retries\");\n}\n\n// ============================================================================\n// Login flow\n// ============================================================================\n\nexport async function loginKimiCoding(options: {\n\tonAuth: (info: { url: string; instructions?: string }) => void;\n\tonProgress?: (message: string) => void;\n\tsignal?: AbortSignal;\n}): Promise<OAuthCredentials> {\n\tconst overallDeadline = Date.now() + MAX_DEVICE_FLOW_MS;\n\tlet tokenResp: TokenSuccessResponse;\n\twhile (true) {\n\t\tconst device = await startDeviceFlow(options.signal);\n\t\toptions.onAuth({\n\t\t\turl: device.verification_uri_complete,\n\t\t\tinstructions: `Enter code: ${device.user_code}`,\n\t\t});\n\n\t\ttry {\n\t\t\ttokenResp = await pollForAccessToken(\n\t\t\t\tdevice.device_code,\n\t\t\t\tdevice.interval,\n\t\t\t\tdevice.expires_in,\n\t\t\t\toptions.signal,\n\t\t\t\toverallDeadline,\n\t\t\t);\n\t\t\tbreak;\n\t\t} catch (error) {\n\t\t\tif (error instanceof DeviceCodeExpiredError && Date.now() < overallDeadline) continue;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// Discover model entitlement. Undefined means discovery failed; an empty\n\t// array is a successful, authoritative response.\n\toptions.onProgress?.(\"Discovering available models...\");\n\tlet models: KimiModelInfo[] | undefined;\n\ttry {\n\t\tmodels = await listModels(tokenResp.access_token, options.signal);\n\t} catch {\n\t\tif (options.signal?.aborted) throw new Error(\"Login cancelled\");\n\t\t// Proceed without model enrichment if the models endpoint fails\n\t}\n\n\tconst credentials: KimiCredentials = {\n\t\trefresh: tokenResp.refresh_token,\n\t\taccess: tokenResp.access_token,\n\t\texpires: Date.now() + tokenResp.expires_in * 1000,\n\t};\n\n\tif (models) {\n\t\tcredentials.models = models;\n\t\tconst primary = models[0];\n\t\tif (primary) {\n\t\t\tcredentials.modelId = primary.id;\n\t\t\tcredentials.contextLength = primary.context_length;\n\t\t\tcredentials.modelDisplay = primary.display_name;\n\t\t}\n\t}\n\n\treturn credentials;\n}\n\n// ============================================================================\n// Refresh\n// ============================================================================\n\nexport async function refreshKimiCodingToken(\n\tcredentials: OAuthCredentials,\n\tsignal?: AbortSignal,\n): Promise<OAuthCredentials> {\n\tconst tokenResp = await refreshWithRetry(credentials.refresh, signal);\n\n\t// Re-discover model entitlement. Undefined means discovery failed; an empty\n\t// array is a successful, authoritative response.\n\tlet models: KimiModelInfo[] | undefined;\n\ttry {\n\t\tmodels = await listModels(tokenResp.access_token, signal);\n\t} catch {\n\t\tif (signal?.aborted) throw new Error(\"Refresh cancelled\");\n\t\t// Proceed without model enrichment if the models endpoint fails\n\t}\n\n\tconst oldCreds = credentials as KimiCredentials;\n\tconst fresh: KimiCredentials = {\n\t\trefresh: tokenResp.refresh_token ?? credentials.refresh,\n\t\taccess: tokenResp.access_token,\n\t\texpires: Date.now() + tokenResp.expires_in * 1000,\n\t\tmodels: oldCreds.models,\n\t\tmodelId: oldCreds.modelId,\n\t\tcontextLength: oldCreds.contextLength,\n\t\tmodelDisplay: oldCreds.modelDisplay,\n\t};\n\n\tif (models) {\n\t\tfresh.models = models;\n\t\tconst primary = models[0];\n\t\tfresh.modelId = primary?.id;\n\t\tfresh.contextLength = primary?.context_length;\n\t\tfresh.modelDisplay = primary?.display_name;\n\t}\n\n\treturn fresh;\n}\n\n// ============================================================================\n// Provider\n// ============================================================================\n\nexport const kimiCodingOAuthProvider: OAuthProviderInterface = {\n\tid: \"kimi-coding-oauth\",\n\tname: \"Kimi For Coding\",\n\n\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\treturn loginKimiCoding({\n\t\t\tonAuth: callbacks.onAuth,\n\t\t\tonProgress: callbacks.onProgress,\n\t\t\tsignal: callbacks.signal,\n\t\t});\n\t},\n\n\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\treturn refreshKimiCodingToken(credentials);\n\t},\n\n\tgetApiKey(credentials: OAuthCredentials): string {\n\t\treturn credentials.access;\n\t},\n\n\tmodifyModels(models: Model<Api>[], credentials: OAuthCredentials): Model<Api>[] {\n\t\tconst creds = credentials as KimiCredentials;\n\t\tconst headers = buildKimiHeaders();\n\n\t\tconst staticModels = models.filter((m) => m.provider === \"kimi-coding-oauth\");\n\t\tif (staticModels.length === 0) {\n\t\t\treturn models;\n\t\t}\n\n\t\tconst injectHeaders = (m: Model<Api>): Model<Api> => ({\n\t\t\t...m,\n\t\t\theaders: { ...(m.headers || {}), ...headers },\n\t\t});\n\n\t\tconst staticById = new Map(staticModels.map((m) => [m.id, m]));\n\t\tconst fallbackTemplate = staticById.get(\"kimi-for-coding\") ?? staticModels[0];\n\n\t\t// No discovery (or failed discovery): keep the static fallback list intact.\n\t\t// Legacy credentials describe one discovered model, so enrich or append only\n\t\t// that model rather than collapsing every static entry to the same ID.\n\t\tconst discovered = creds.models;\n\t\tif (!discovered) {\n\t\t\tconst fallbackModels = models.map((m) => {\n\t\t\t\tif (m.provider !== \"kimi-coding-oauth\") return m;\n\n\t\t\t\tconst updated: Model<Api> = {\n\t\t\t\t\t...m,\n\t\t\t\t\t// The OAuth coding endpoint accepts OpenAI-style image_url data URLs;\n\t\t\t\t\t// keep this capability even if static metadata is stale.\n\t\t\t\t\tinput: Array.from(new Set([...m.input, \"image\" as const])),\n\t\t\t\t};\n\t\t\t\tif (creds.modelId === m.id && creds.contextLength) {\n\t\t\t\t\tupdated.contextWindow = creds.contextLength;\n\t\t\t\t}\n\t\t\t\tif (creds.modelId === m.id && creds.modelDisplay) {\n\t\t\t\t\tupdated.name = creds.modelDisplay;\n\t\t\t\t}\n\t\t\t\treturn injectHeaders(updated);\n\t\t\t});\n\n\t\t\tif (creds.modelId && !staticById.has(creds.modelId)) {\n\t\t\t\tfallbackModels.push(\n\t\t\t\t\tinjectHeaders({\n\t\t\t\t\t\t...fallbackTemplate,\n\t\t\t\t\t\tid: creds.modelId,\n\t\t\t\t\t\tname: creds.modelDisplay || creds.modelId,\n\t\t\t\t\t\tcontextWindow: creds.contextLength || fallbackTemplate.contextWindow,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn fallbackModels;\n\t\t}\n\n\t\tconst discoveredInput = (base: Model<Api>, info: KimiModelInfo): Model<Api>[\"input\"] => {\n\t\t\tif (info.supports_image_in === true) return Array.from(new Set([...base.input, \"image\" as const]));\n\t\t\tif (info.supports_image_in === false) return base.input.filter((input) => input !== \"image\");\n\t\t\treturn base.input;\n\t\t};\n\n\t\tconst discoveredCompat = (base: Model<Api>, info: KimiModelInfo): Model<Api>[\"compat\"] => {\n\t\t\tconst valid = info.think_efforts?.support ? info.think_efforts.valid_efforts : undefined;\n\t\t\tif (!valid || valid.length === 0) return base.compat;\n\t\t\tconst efforts = new Set(valid);\n\t\t\tconst declaredDefault = info.think_efforts?.default_effort;\n\t\t\tconst validDefault = declaredDefault && efforts.has(declaredDefault) ? declaredDefault : undefined;\n\t\t\tconst choose = (...preferences: string[]): string =>\n\t\t\t\tpreferences.find((effort) => efforts.has(effort)) ?? validDefault ?? valid[0];\n\t\t\treturn {\n\t\t\t\t...base.compat,\n\t\t\t\treasoningEffortMap: {\n\t\t\t\t\tminimal: choose(\"minimal\", \"low\", \"medium\", \"high\", \"max\"),\n\t\t\t\t\tlow: choose(\"low\", \"minimal\", \"medium\", \"high\", \"max\"),\n\t\t\t\t\tmedium: choose(\"medium\", \"high\", \"low\", \"max\"),\n\t\t\t\t\thigh: choose(\"high\", \"medium\", \"max\", \"low\"),\n\t\t\t\t\txhigh: choose(\"max\", \"xhigh\", \"high\", \"medium\", \"low\"),\n\t\t\t\t},\n\t\t\t};\n\t\t};\n\n\t\t// Apply discovered metadata to a static model, preserving its static shape.\n\t\tconst applyDiscovery = (staticModel: Model<Api>, info: KimiModelInfo): Model<Api> => {\n\t\t\tconst updated: Model<Api> = {\n\t\t\t\t...staticModel,\n\t\t\t\tid: info.id,\n\t\t\t\tname: info.display_name || info.id,\n\t\t\t\tcontextWindow: info.context_length || staticModel.contextWindow,\n\t\t\t\tinput: discoveredInput(staticModel, info),\n\t\t\t\tcompat: discoveredCompat(staticModel, info),\n\t\t\t};\n\t\t\tif (info.supports_thinking_type === \"only\" || info.supports_thinking_type === \"both\") {\n\t\t\t\tupdated.reasoning = true;\n\t\t\t} else if (info.supports_thinking_type === \"no\") {\n\t\t\t\tupdated.reasoning = false;\n\t\t\t} else if (typeof info.supports_reasoning === \"boolean\") {\n\t\t\t\tupdated.reasoning = info.supports_reasoning;\n\t\t\t}\n\t\t\treturn injectHeaders(updated);\n\t\t};\n\n\t\t// Safely template a future/discovered model ID using the static fallback.\n\t\tconst templateDiscovery = (info: KimiModelInfo): Model<Api> => {\n\t\t\tconst templated: Model<Api> = {\n\t\t\t\t...fallbackTemplate,\n\t\t\t\tid: info.id,\n\t\t\t\tname: info.display_name || info.id,\n\t\t\t\tcontextWindow: info.context_length || fallbackTemplate.contextWindow,\n\t\t\t\treasoning:\n\t\t\t\t\tinfo.supports_thinking_type === undefined\n\t\t\t\t\t\t? (info.supports_reasoning ?? false)\n\t\t\t\t\t\t: info.supports_thinking_type !== \"no\",\n\t\t\t\tinput: info.supports_image_in === true ? [\"text\", \"image\"] : [\"text\"],\n\t\t\t\tcompat: discoveredCompat(fallbackTemplate, info),\n\t\t\t};\n\t\t\treturn injectHeaders(templated);\n\t\t};\n\n\t\t// The official client treats only the explicit \"anthropic\" protocol as a\n\t\t// separate wire format; absent and future values use the default Kimi route.\n\t\tconst supportedDiscovered = discovered.filter(\n\t\t\t(info) => info.supports_tool_use !== false && info.protocol !== \"anthropic\",\n\t\t);\n\t\tconst result: Model<Api>[] = [];\n\t\tconst seen = new Set<string>();\n\n\t\t// 1. Walk the original model list to preserve order, replacing discovered\n\t\t// static models and dropping undiscovered entries. A successful response\n\t\t// is authoritative for the subscription's current entitlements.\n\t\tfor (const m of models) {\n\t\t\tif (m.provider !== \"kimi-coding-oauth\") {\n\t\t\t\tresult.push(m);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst info = staticById.has(m.id) ? supportedDiscovered.find((d) => d.id === m.id) : undefined;\n\t\t\tif (info) {\n\t\t\t\tresult.push(applyDiscovery(m, info));\n\t\t\t\tseen.add(info.id);\n\t\t\t}\n\t\t}\n\n\t\t// 2. Discovered models with IDs not present in the static list are templated\n\t\t// from the fallback so future model IDs are safely usable.\n\t\tfor (const info of supportedDiscovered) {\n\t\t\tif (!seen.has(info.id)) {\n\t\t\t\tresult.push(templateDiscovery(info));\n\t\t\t\tseen.add(info.id);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t},\n};\n"]}
1
+ {"version":3,"file":"kimi-coding.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/kimi-coding.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAAE,gBAAgB,EAAuB,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAoHhG;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWzD;AAmCD,MAAM,MAAM,aAAa,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC;IAChD,aAAa,CAAC,EAAE;QACf,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,cAAc,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB,CAAC;AAuIF;;;GAGG;AACH,wBAAsB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAmBpG;AAyND,wBAAsB,eAAe,CAAC,OAAO,EAAE;IAC9C,MAAM,EAAE,CAAC,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/D,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAqD5B;AAMD,wBAAsB,sBAAsB,CAC3C,WAAW,EAAE,gBAAgB,EAC7B,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,gBAAgB,CAAC,CAiC3B;AAMD,eAAO,MAAM,uBAAuB,EAAE,sBA8KrC,CAAC","sourcesContent":["/**\n * Kimi For Coding OAuth flow (device code)\n *\n * Authenticates against Moonshot's Kimi API (auth.kimi.com) using the current Kimi Code device identity.\n * Uses the device authorization grant flow to obtain access/refresh tokens,\n * then discovers the user's model entitlement via the /models endpoint.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { Api, Model } from \"../../types.js\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from \"./types.js\";\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nfunction readDrebVersion(): string {\n\ttry {\n\t\tconst pkg = JSON.parse(fs.readFileSync(new URL(\"../../../package.json\", import.meta.url), \"utf-8\")) as {\n\t\t\tversion?: unknown;\n\t\t};\n\t\tif (typeof pkg.version === \"string\" && pkg.version.length > 0) return pkg.version;\n\t} catch {\n\t\t// Fall back for unusual bundlers that omit package.json.\n\t}\n\treturn \"unknown\";\n}\n\nconst DREB_VERSION = readDrebVersion();\nconst USER_AGENT = `dreb/${DREB_VERSION}`;\nconst OAUTH_HOST = \"https://auth.kimi.com\";\nconst OAUTH_DEVICE_AUTH_URL = `${OAUTH_HOST}/api/oauth/device_authorization`;\nconst OAUTH_TOKEN_URL = `${OAUTH_HOST}/api/oauth/token`;\nconst OAUTH_CLIENT_ID = \"17e5f671-d194-4dfb-9706-5516cb48c098\";\nconst OAUTH_DEVICE_GRANT = \"urn:ietf:params:oauth:grant-type:device_code\";\nconst OAUTH_REFRESH_GRANT = \"refresh_token\";\nconst API_BASE_URL = \"https://api.kimi.com/coding/v1\";\n\nconst KIMI_CODE_HOME = path.join(os.homedir(), \".kimi-code\");\nconst DEVICE_ID_PATH = path.join(KIMI_CODE_HOME, \"device_id\");\n\nconst MAX_REFRESH_RETRIES = 3;\nconst MAX_DEVICE_FLOW_MS = 15 * 60 * 1000;\n\n// ============================================================================\n// Device ID\n// ============================================================================\n\nlet sessionDeviceId: string | undefined;\n\nfunction getDeviceId(): string {\n\tif (sessionDeviceId) return sessionDeviceId;\n\ttry {\n\t\tif (fs.existsSync(DEVICE_ID_PATH)) {\n\t\t\tconst id = fs.readFileSync(DEVICE_ID_PATH, \"utf-8\").trim();\n\t\t\tif (id.length > 0) {\n\t\t\t\tsessionDeviceId = id;\n\t\t\t\treturn id;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Fall through to generate.\n\t}\n\n\tconst id = randomUUID();\n\tsessionDeviceId = id;\n\ttry {\n\t\tfs.mkdirSync(KIMI_CODE_HOME, { recursive: true, mode: 0o700 });\n\t\tfs.writeFileSync(DEVICE_ID_PATH, id, { encoding: \"utf-8\", mode: 0o600 });\n\t} catch {\n\t\t// If we can't persist, just use the generated ID for this session.\n\t}\n\treturn id;\n}\n\n// ============================================================================\n// Header helpers\n// ============================================================================\n\n/**\n * Strip non-ASCII characters from a string for use in HTTP header values.\n */\nfunction asciiHeaderValue(value: string, fallback = \"unknown\"): string {\n\tconst cleaned = value.replace(/[^\\x20-\\x7E]/g, \"\").trim();\n\treturn cleaned.length > 0 ? cleaned : fallback;\n}\n\nfunction customKimiHeaders(): Record<string, string> {\n\tconst raw = process.env.KIMI_CODE_CUSTOM_HEADERS?.trim();\n\tif (!raw) return {};\n\tconst headers: Record<string, string> = {};\n\tfor (const line of raw.split(\"\\n\")) {\n\t\tconst colon = line.indexOf(\":\");\n\t\tif (colon < 0) continue;\n\t\tconst name = line.slice(0, colon).trim();\n\t\tif (!name) continue;\n\t\theaders[name] = line.slice(colon + 1).trim();\n\t}\n\treturn headers;\n}\n\n/**\n * Determine the device model string, mirroring kimi-cli logic.\n */\nfunction kimiDeviceModel(): string {\n\tconst platform = os.platform();\n\tconst machine = os.arch();\n\n\tif (platform === \"darwin\") {\n\t\tlet version: string;\n\t\ttry {\n\t\t\tversion = execFileSync(\"/usr/bin/sw_vers\", [\"-productVersion\"], { encoding: \"utf-8\", timeout: 3000 }).trim();\n\t\t} catch {\n\t\t\tversion = os.release();\n\t\t}\n\t\treturn `macOS ${version} ${machine}`;\n\t}\n\n\tif (platform === \"win32\") {\n\t\treturn `Windows ${os.release()} ${machine}`;\n\t}\n\n\t// Linux and other\n\treturn `${os.type()} ${os.release()} ${machine}`.trim();\n}\n\n/**\n * Build the standard set of headers required on every Kimi API request.\n */\nexport function buildKimiHeaders(): Record<string, string> {\n\treturn {\n\t\t...customKimiHeaders(),\n\t\t\"User-Agent\": USER_AGENT,\n\t\t\"X-Msh-Platform\": \"kimi_code_cli\",\n\t\t\"X-Msh-Version\": DREB_VERSION,\n\t\t\"X-Msh-Device-Name\": asciiHeaderValue(os.hostname()),\n\t\t\"X-Msh-Device-Model\": asciiHeaderValue(kimiDeviceModel()),\n\t\t\"X-Msh-Device-Id\": getDeviceId(),\n\t\t\"X-Msh-Os-Version\": asciiHeaderValue(os.release()),\n\t};\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\ntype DeviceCodeResponse = {\n\tdevice_code: string;\n\tuser_code: string;\n\tverification_uri_complete: string;\n\tinterval: number;\n\texpires_in: number;\n};\n\ntype TokenSuccessResponse = {\n\taccess_token: string;\n\trefresh_token: string;\n\texpires_in: number;\n};\n\nfunction parseTokenSuccess(response: Record<string, unknown>): TokenSuccessResponse {\n\tif (\n\t\ttypeof response.access_token !== \"string\" ||\n\t\tresponse.access_token.length === 0 ||\n\t\ttypeof response.refresh_token !== \"string\" ||\n\t\tresponse.refresh_token.length === 0 ||\n\t\ttypeof response.expires_in !== \"number\" ||\n\t\t!Number.isFinite(response.expires_in) ||\n\t\tresponse.expires_in <= 0\n\t) {\n\t\tthrow new Error(\"Invalid token response fields\");\n\t}\n\treturn response as unknown as TokenSuccessResponse;\n}\n\nexport type KimiModelInfo = {\n\tid: string;\n\tdisplay_name?: string;\n\tcontext_length: number;\n\tsupports_reasoning?: boolean;\n\tsupports_image_in?: boolean;\n\tsupports_video_in?: boolean;\n\tsupports_tool_use?: boolean;\n\tsupports_thinking_type?: \"only\" | \"no\" | \"both\";\n\tthink_efforts?: {\n\t\tsupport?: boolean;\n\t\tvalid_efforts?: string[];\n\t\tdefault_effort?: string;\n\t};\n\tprotocol?: string;\n\t[key: string]: unknown;\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction parseStringArray(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\tconst out = value.filter((v): v is string => typeof v === \"string\" && v.length > 0);\n\treturn out.length > 0 ? out : undefined;\n}\n\nfunction parseSupportsThinkingType(value: unknown): KimiModelInfo[\"supports_thinking_type\"] | undefined {\n\treturn value === \"only\" || value === \"no\" || value === \"both\" ? value : undefined;\n}\n\nfunction parseModelProtocol(value: unknown): \"anthropic\" | undefined {\n\treturn value === \"anthropic\" ? \"anthropic\" : undefined;\n}\n\nfunction parseThinkEfforts(value: unknown): KimiModelInfo[\"think_efforts\"] | undefined {\n\tif (!isRecord(value) || value.support !== true) return undefined;\n\treturn {\n\t\tsupport: true,\n\t\tvalid_efforts: parseStringArray(value.valid_efforts),\n\t\tdefault_effort:\n\t\t\ttypeof value.default_effort === \"string\" && value.default_effort.length > 0 ? value.default_effort : undefined,\n\t};\n}\n\nfunction toKimiModelInfo(item: unknown): KimiModelInfo | undefined {\n\tif (!isRecord(item) || typeof item.id !== \"string\" || item.id.length === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst contextLength = Number(item.context_length);\n\tif (!Number.isInteger(contextLength) || contextLength <= 0) {\n\t\tthrow new Error(`Kimi Code model \"${item.id}\" must include a positive context_length.`);\n\t}\n\n\tconst displayName = item.display_name;\n\tconst normalizedDisplayName = typeof displayName === \"string\" && displayName.length > 0 ? displayName : undefined;\n\n\tconst optionalBoolean = (value: unknown): boolean | undefined => (typeof value === \"boolean\" ? value : undefined);\n\n\tconst parsed: KimiModelInfo = {\n\t\tid: item.id,\n\t\tcontext_length: contextLength,\n\t\tdisplay_name: normalizedDisplayName,\n\t};\n\n\tconst supportsReasoning = optionalBoolean(item.supports_reasoning);\n\tif (supportsReasoning !== undefined) parsed.supports_reasoning = supportsReasoning;\n\n\tconst supportsImageIn = optionalBoolean(item.supports_image_in);\n\tif (supportsImageIn !== undefined) parsed.supports_image_in = supportsImageIn;\n\n\tconst supportsVideoIn = optionalBoolean(item.supports_video_in);\n\tif (supportsVideoIn !== undefined) parsed.supports_video_in = supportsVideoIn;\n\n\tif (Object.hasOwn(item, \"supports_tool_use\")) {\n\t\tconst supportsToolUse = optionalBoolean(item.supports_tool_use);\n\t\tif (supportsToolUse !== undefined) parsed.supports_tool_use = supportsToolUse;\n\t}\n\n\tconst supportsThinkingType = parseSupportsThinkingType(item.supports_thinking_type);\n\tif (supportsThinkingType !== undefined) parsed.supports_thinking_type = supportsThinkingType;\n\n\tconst protocol = parseModelProtocol(item.protocol);\n\tif (protocol !== undefined) parsed.protocol = protocol;\n\n\tconst thinkEfforts = parseThinkEfforts(item.think_efforts);\n\tif (thinkEfforts !== undefined) parsed.think_efforts = thinkEfforts;\n\n\treturn parsed;\n}\n\ntype KimiCredentials = OAuthCredentials & {\n\t/** Full list of models discovered from the Kimi API. */\n\tmodels?: KimiModelInfo[];\n\t/** @deprecated Kept for legacy credential compatibility; derived from models[0]. */\n\tmodelId?: string;\n\t/** @deprecated Kept for legacy credential compatibility; derived from models[0]. */\n\tcontextLength?: number;\n\t/** @deprecated Kept for legacy credential compatibility; derived from models[0]. */\n\tmodelDisplay?: string;\n};\n\n// ============================================================================\n// Network helpers\n// ============================================================================\n\nconst REQUEST_TIMEOUT_MS = 30_000;\n\nfunction requestSignal(signal?: AbortSignal): AbortSignal {\n\tconst timeout = AbortSignal.timeout(REQUEST_TIMEOUT_MS);\n\treturn signal ? AbortSignal.any([signal, timeout]) : timeout;\n}\n\nasync function fetchJson(url: string, init: RequestInit): Promise<unknown> {\n\tconst response = await fetch(url, init);\n\tif (!response.ok) {\n\t\tconst text = await response.text();\n\t\tthrow new Error(`${response.status} ${response.statusText}: ${text}`);\n\t}\n\treturn response.json();\n}\n\n/**\n * Sleep that can be interrupted by an AbortSignal.\n */\nfunction abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(new Error(\"Login cancelled\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst timeout = setTimeout(resolve, ms);\n\n\t\tsignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\treject(new Error(\"Login cancelled\"));\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\n// ============================================================================\n// Model discovery\n// ============================================================================\n\n/**\n * List available models from the Kimi API.\n * Returns the model info array from the response's `data` field.\n */\nexport async function listModels(accessToken: string, signal?: AbortSignal): Promise<KimiModelInfo[]> {\n\tconst raw = await fetchJson(`${API_BASE_URL}/models`, {\n\t\tsignal: requestSignal(signal),\n\t\theaders: {\n\t\t\t...buildKimiHeaders(),\n\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t},\n\t});\n\n\tif (!raw || typeof raw !== \"object\") {\n\t\tthrow new Error(\"Invalid models response\");\n\t}\n\n\tconst data = (raw as Record<string, unknown>).data;\n\tif (!Array.isArray(data)) {\n\t\tthrow new Error(\"Invalid models response: expected data array\");\n\t}\n\n\treturn data.map((item) => toKimiModelInfo(item)).filter((item): item is KimiModelInfo => item !== undefined);\n}\n\n// ============================================================================\n// Device flow\n// ============================================================================\n\nasync function startDeviceFlow(signal?: AbortSignal): Promise<DeviceCodeResponse> {\n\tconst data = await fetchJson(OAUTH_DEVICE_AUTH_URL, {\n\t\tmethod: \"POST\",\n\t\tsignal: requestSignal(signal),\n\t\theaders: {\n\t\t\t...buildKimiHeaders(),\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t},\n\t\tbody: new URLSearchParams({ client_id: OAUTH_CLIENT_ID }),\n\t});\n\n\tif (!data || typeof data !== \"object\") {\n\t\tthrow new Error(\"Invalid device code response\");\n\t}\n\n\tconst d = data as Record<string, unknown>;\n\tconst device_code = d.device_code;\n\tconst user_code = d.user_code;\n\tconst verification_uri_complete =\n\t\ttypeof d.verification_uri_complete === \"string\" && d.verification_uri_complete.length > 0\n\t\t\t? d.verification_uri_complete\n\t\t\t: typeof d.verification_uri === \"string\" && d.verification_uri.length > 0\n\t\t\t\t? `${d.verification_uri}${d.verification_uri.includes(\"?\") ? \"&\" : \"?\"}user_code=${encodeURIComponent(user_code as string)}`\n\t\t\t\t: undefined;\n\tconst interval = d.interval;\n\tconst expires_in = d.expires_in;\n\n\tif (\n\t\ttypeof device_code !== \"string\" ||\n\t\ttypeof user_code !== \"string\" ||\n\t\ttypeof verification_uri_complete !== \"string\" ||\n\t\ttypeof interval !== \"number\" ||\n\t\ttypeof expires_in !== \"number\"\n\t) {\n\t\tthrow new Error(\"Invalid device code response fields\");\n\t}\n\n\treturn { device_code, user_code, verification_uri_complete, interval, expires_in };\n}\n\nclass DeviceCodeExpiredError extends Error {\n\tconstructor() {\n\t\tsuper(\"Device code expired\");\n\t\tthis.name = \"DeviceCodeExpiredError\";\n\t}\n}\n\nasync function pollForAccessToken(\n\tdeviceCode: string,\n\tintervalSeconds: number,\n\texpiresIn: number,\n\tsignal?: AbortSignal,\n\toverallDeadline = Date.now() + MAX_DEVICE_FLOW_MS,\n): Promise<TokenSuccessResponse> {\n\tconst deadline = Math.min(Date.now() + expiresIn * 1000, overallDeadline);\n\tlet intervalMs = Math.max(1000, Math.floor(intervalSeconds * 1000));\n\n\twhile (Date.now() < deadline) {\n\t\tif (signal?.aborted) {\n\t\t\tthrow new Error(\"Login cancelled\");\n\t\t}\n\n\t\tconst remainingMs = deadline - Date.now();\n\t\tconst waitMs = Math.min(intervalMs, remainingMs);\n\t\tawait abortableSleep(waitMs, signal);\n\n\t\tconst tokenResponse = await fetch(OAUTH_TOKEN_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\tsignal: requestSignal(signal),\n\t\t\theaders: {\n\t\t\t\t...buildKimiHeaders(),\n\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t},\n\t\t\tbody: new URLSearchParams({\n\t\t\t\tclient_id: OAUTH_CLIENT_ID,\n\t\t\t\tdevice_code: deviceCode,\n\t\t\t\tgrant_type: OAUTH_DEVICE_GRANT,\n\t\t\t}),\n\t\t});\n\n\t\t// The token endpoint returns 400 for authorization_pending / slow_down / expired_token.\n\t\t// We must read the body regardless of status to handle the OAuth error codes.\n\t\tconst resp = (await tokenResponse.json()) as Record<string, unknown>;\n\n\t\t// Success: has access_token\n\t\tif (typeof resp.access_token === \"string\") {\n\t\t\treturn parseTokenSuccess(resp);\n\t\t}\n\n\t\t// Error response (RFC 8628 §3.5)\n\t\tif (typeof resp.error === \"string\") {\n\t\t\tconst error = resp.error;\n\t\t\tconst description = resp.error_description as string | undefined;\n\t\t\tconst newInterval = resp.interval as number | undefined;\n\n\t\t\tif (error === \"authorization_pending\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (error === \"slow_down\") {\n\t\t\t\tintervalMs =\n\t\t\t\t\ttypeof newInterval === \"number\" && newInterval > 0\n\t\t\t\t\t\t? newInterval * 1000\n\t\t\t\t\t\t: Math.max(1000, intervalMs + 5000);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (error === \"expired_token\") {\n\t\t\t\tthrow new DeviceCodeExpiredError();\n\t\t\t}\n\n\t\t\tconst descriptionSuffix = description ? `: ${description}` : \"\";\n\t\t\tthrow new Error(`Device flow failed: ${error}${descriptionSuffix}`);\n\t\t}\n\n\t\t// Unexpected response: valid object but no access_token or error field\n\t\tthrow new Error(`Unexpected token response: ${JSON.stringify(resp)}`);\n\t}\n\n\tthrow new Error(\"Device flow timed out\");\n}\n\n// ============================================================================\n// Refresh with retry\n// ============================================================================\n\nconst RETRYABLE_STATUS_CODES = [429, 500, 502, 503, 504];\n\nclass RetriableError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"RetriableError\";\n\t}\n}\n\n/**\n * Heuristic to detect network-level errors that should be retried.\n * Fetch throws TypeError on network failures; some runtimes include\n * recognizable substrings in the message.\n */\nfunction isNetworkError(error: Error): boolean {\n\tif (error instanceof TypeError || error.name === \"TimeoutError\") return true;\n\tconst msg = error.message.toLowerCase();\n\treturn [\"fetch failed\", \"econnrefused\", \"etimedout\", \"enotfound\", \"econnreset\", \"socket hang up\"].some((s) =>\n\t\tmsg.includes(s),\n\t);\n}\n\nasync function refreshWithRetry(refreshToken: string, signal?: AbortSignal): Promise<TokenSuccessResponse> {\n\tlet lastError: Error | undefined;\n\n\tfor (let attempt = 0; attempt < MAX_REFRESH_RETRIES; attempt++) {\n\t\tif (signal?.aborted) {\n\t\t\tthrow new Error(\"Refresh cancelled\");\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(OAUTH_TOKEN_URL, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tsignal: requestSignal(signal),\n\t\t\t\theaders: {\n\t\t\t\t\t...buildKimiHeaders(),\n\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t},\n\t\t\t\tbody: new URLSearchParams({\n\t\t\t\t\tclient_id: OAUTH_CLIENT_ID,\n\t\t\t\t\trefresh_token: refreshToken,\n\t\t\t\t\tgrant_type: OAUTH_REFRESH_GRANT,\n\t\t\t\t}),\n\t\t\t});\n\n\t\t\t// Retry on retriable status codes\n\t\t\tif (RETRYABLE_STATUS_CODES.includes(response.status)) {\n\t\t\t\tthrow new RetriableError(`Token refresh failed with status ${response.status}`);\n\t\t\t}\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text();\n\t\t\t\tthrow new Error(`Token refresh failed: ${response.status} ${response.statusText}: ${text}`);\n\t\t\t}\n\n\t\t\tconst raw = await response.json();\n\t\t\tif (!raw || typeof raw !== \"object\") throw new Error(\"Invalid token refresh response\");\n\t\t\treturn parseTokenSuccess(raw as Record<string, unknown>);\n\t\t} catch (error) {\n\t\t\tif (signal?.aborted) throw new Error(\"Refresh cancelled\");\n\t\t\tlastError = error instanceof Error ? error : new Error(String(error));\n\n\t\t\t// Wrap network errors (TypeError from fetch, or common network failure indicators) as retriable\n\t\t\tif (!(lastError instanceof RetriableError) && isNetworkError(lastError)) {\n\t\t\t\tlastError = new RetriableError(lastError.message);\n\t\t\t}\n\n\t\t\t// Retry on retriable errors (network failures or retriable HTTP status codes)\n\t\t\tif (lastError instanceof RetriableError && attempt < MAX_REFRESH_RETRIES - 1) {\n\t\t\t\tconst backoffMs = Math.min(1000 * 2 ** attempt, 10000);\n\t\t\t\tawait abortableSleep(backoffMs, signal);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tthrow lastError;\n\t\t}\n\t}\n\n\tthrow lastError ?? new Error(\"Token refresh failed after retries\");\n}\n\n// ============================================================================\n// Login flow\n// ============================================================================\n\nexport async function loginKimiCoding(options: {\n\tonAuth: (info: { url: string; instructions?: string }) => void;\n\tonProgress?: (message: string) => void;\n\tsignal?: AbortSignal;\n}): Promise<OAuthCredentials> {\n\tconst overallDeadline = Date.now() + MAX_DEVICE_FLOW_MS;\n\tlet tokenResp: TokenSuccessResponse;\n\twhile (true) {\n\t\tconst device = await startDeviceFlow(options.signal);\n\t\toptions.onAuth({\n\t\t\turl: device.verification_uri_complete,\n\t\t\tinstructions: `Enter code: ${device.user_code}`,\n\t\t});\n\n\t\ttry {\n\t\t\ttokenResp = await pollForAccessToken(\n\t\t\t\tdevice.device_code,\n\t\t\t\tdevice.interval,\n\t\t\t\tdevice.expires_in,\n\t\t\t\toptions.signal,\n\t\t\t\toverallDeadline,\n\t\t\t);\n\t\t\tbreak;\n\t\t} catch (error) {\n\t\t\tif (error instanceof DeviceCodeExpiredError && Date.now() < overallDeadline) continue;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// Discover model entitlement. Undefined means discovery failed; an empty\n\t// array is a successful, authoritative response.\n\toptions.onProgress?.(\"Discovering available models...\");\n\tlet models: KimiModelInfo[] | undefined;\n\ttry {\n\t\tmodels = await listModels(tokenResp.access_token, options.signal);\n\t} catch {\n\t\tif (options.signal?.aborted) throw new Error(\"Login cancelled\");\n\t\t// Proceed without model enrichment if the models endpoint fails\n\t}\n\n\tconst credentials: KimiCredentials = {\n\t\trefresh: tokenResp.refresh_token,\n\t\taccess: tokenResp.access_token,\n\t\texpires: Date.now() + tokenResp.expires_in * 1000,\n\t};\n\n\tif (models) {\n\t\tcredentials.models = models;\n\t\tconst primary = models[0];\n\t\tif (primary) {\n\t\t\tcredentials.modelId = primary.id;\n\t\t\tcredentials.contextLength = primary.context_length;\n\t\t\tcredentials.modelDisplay = primary.display_name;\n\t\t}\n\t}\n\n\treturn credentials;\n}\n\n// ============================================================================\n// Refresh\n// ============================================================================\n\nexport async function refreshKimiCodingToken(\n\tcredentials: OAuthCredentials,\n\tsignal?: AbortSignal,\n): Promise<OAuthCredentials> {\n\tconst tokenResp = await refreshWithRetry(credentials.refresh, signal);\n\n\t// Re-discover model entitlement. Undefined means discovery failed; an empty\n\t// array is a successful, authoritative response.\n\tlet models: KimiModelInfo[] | undefined;\n\ttry {\n\t\tmodels = await listModels(tokenResp.access_token, signal);\n\t} catch {\n\t\tif (signal?.aborted) throw new Error(\"Refresh cancelled\");\n\t\t// Proceed without model enrichment if the models endpoint fails\n\t}\n\n\tconst oldCreds = credentials as KimiCredentials;\n\tconst fresh: KimiCredentials = {\n\t\trefresh: tokenResp.refresh_token ?? credentials.refresh,\n\t\taccess: tokenResp.access_token,\n\t\texpires: Date.now() + tokenResp.expires_in * 1000,\n\t\tmodels: oldCreds.models,\n\t\tmodelId: oldCreds.modelId,\n\t\tcontextLength: oldCreds.contextLength,\n\t\tmodelDisplay: oldCreds.modelDisplay,\n\t};\n\n\tif (models) {\n\t\tfresh.models = models;\n\t\tconst primary = models[0];\n\t\tfresh.modelId = primary?.id;\n\t\tfresh.contextLength = primary?.context_length;\n\t\tfresh.modelDisplay = primary?.display_name;\n\t}\n\n\treturn fresh;\n}\n\n// ============================================================================\n// Provider\n// ============================================================================\n\nexport const kimiCodingOAuthProvider: OAuthProviderInterface = {\n\tid: \"kimi-coding-oauth\",\n\tname: \"Kimi For Coding\",\n\n\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\treturn loginKimiCoding({\n\t\t\tonAuth: callbacks.onAuth,\n\t\t\tonProgress: callbacks.onProgress,\n\t\t\tsignal: callbacks.signal,\n\t\t});\n\t},\n\n\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\treturn refreshKimiCodingToken(credentials);\n\t},\n\n\tgetApiKey(credentials: OAuthCredentials): string {\n\t\treturn credentials.access;\n\t},\n\n\tmodifyModels(models: Model<Api>[], credentials: OAuthCredentials): Model<Api>[] {\n\t\tconst creds = credentials as KimiCredentials;\n\t\tconst headers = buildKimiHeaders();\n\n\t\tconst staticModels = models.filter((m) => m.provider === \"kimi-coding-oauth\");\n\t\tif (staticModels.length === 0) {\n\t\t\treturn models;\n\t\t}\n\n\t\tconst injectHeaders = (m: Model<Api>): Model<Api> => ({\n\t\t\t...m,\n\t\t\theaders: { ...(m.headers || {}), ...headers },\n\t\t});\n\n\t\tconst staticById = new Map(staticModels.map((m) => [m.id, m]));\n\t\tconst fallbackTemplate = staticById.get(\"kimi-for-coding\") ?? staticModels[0];\n\n\t\t// No discovery (or failed discovery): keep the static fallback list intact.\n\t\t// Legacy credentials describe one discovered model, so enrich or append only\n\t\t// that model rather than collapsing every static entry to the same ID.\n\t\tconst discovered = creds.models;\n\t\tif (!discovered) {\n\t\t\tconst fallbackModels = models.map((m) => {\n\t\t\t\tif (m.provider !== \"kimi-coding-oauth\") return m;\n\n\t\t\t\tconst updated: Model<Api> = {\n\t\t\t\t\t...m,\n\t\t\t\t\t// The OAuth coding endpoint accepts OpenAI-style image_url data URLs;\n\t\t\t\t\t// keep this capability even if static metadata is stale.\n\t\t\t\t\tinput: Array.from(new Set([...m.input, \"image\" as const])),\n\t\t\t\t};\n\t\t\t\tif (creds.modelId === m.id && creds.contextLength) {\n\t\t\t\t\tupdated.contextWindow = creds.contextLength;\n\t\t\t\t}\n\t\t\t\tif (creds.modelId === m.id && creds.modelDisplay) {\n\t\t\t\t\tupdated.name = creds.modelDisplay;\n\t\t\t\t}\n\t\t\t\treturn injectHeaders(updated);\n\t\t\t});\n\n\t\t\tif (creds.modelId && !staticById.has(creds.modelId)) {\n\t\t\t\tfallbackModels.push(\n\t\t\t\t\tinjectHeaders({\n\t\t\t\t\t\t...fallbackTemplate,\n\t\t\t\t\t\tid: creds.modelId,\n\t\t\t\t\t\tname: creds.modelDisplay || creds.modelId,\n\t\t\t\t\t\tcontextWindow: creds.contextLength || fallbackTemplate.contextWindow,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn fallbackModels;\n\t\t}\n\n\t\tconst discoveredInput = (base: Model<Api>, info: KimiModelInfo): Model<Api>[\"input\"] => {\n\t\t\tif (info.supports_image_in === true) return Array.from(new Set([...base.input, \"image\" as const]));\n\t\t\tif (info.supports_image_in === false) return base.input.filter((input) => input !== \"image\");\n\t\t\treturn base.input;\n\t\t};\n\n\t\tconst discoveredCompat = (base: Model<Api>, info: KimiModelInfo): Model<Api>[\"compat\"] => {\n\t\t\tconst valid = info.think_efforts?.support ? info.think_efforts.valid_efforts : undefined;\n\t\t\tif (!valid || valid.length === 0) return base.compat;\n\t\t\tconst efforts = new Set(valid);\n\t\t\tconst declaredDefault = info.think_efforts?.default_effort;\n\t\t\tconst validDefault = declaredDefault && efforts.has(declaredDefault) ? declaredDefault : undefined;\n\t\t\tconst choose = (...preferences: string[]): string =>\n\t\t\t\tpreferences.find((effort) => efforts.has(effort)) ?? validDefault ?? valid[0];\n\t\t\treturn {\n\t\t\t\t...base.compat,\n\t\t\t\treasoningEffortMap: {\n\t\t\t\t\tminimal: choose(\"minimal\", \"low\", \"medium\", \"high\", \"max\"),\n\t\t\t\t\tlow: choose(\"low\", \"minimal\", \"medium\", \"high\", \"max\"),\n\t\t\t\t\tmedium: choose(\"medium\", \"high\", \"low\", \"max\"),\n\t\t\t\t\thigh: choose(\"high\", \"medium\", \"max\", \"low\"),\n\t\t\t\t\txhigh: choose(\"max\", \"xhigh\", \"high\", \"medium\", \"low\"),\n\t\t\t\t},\n\t\t\t};\n\t\t};\n\n\t\t// Apply discovered metadata to a static model, preserving its static shape.\n\t\tconst applyDiscovery = (staticModel: Model<Api>, info: KimiModelInfo): Model<Api> => {\n\t\t\tconst updated: Model<Api> = {\n\t\t\t\t...staticModel,\n\t\t\t\tid: info.id,\n\t\t\t\tname: info.display_name || info.id,\n\t\t\t\tcontextWindow: info.context_length || staticModel.contextWindow,\n\t\t\t\tinput: discoveredInput(staticModel, info),\n\t\t\t\tcompat: discoveredCompat(staticModel, info),\n\t\t\t};\n\t\t\tif (info.supports_thinking_type === \"only\" || info.supports_thinking_type === \"both\") {\n\t\t\t\tupdated.reasoning = true;\n\t\t\t} else if (info.supports_thinking_type === \"no\") {\n\t\t\t\tupdated.reasoning = false;\n\t\t\t} else if (typeof info.supports_reasoning === \"boolean\") {\n\t\t\t\tupdated.reasoning = info.supports_reasoning;\n\t\t\t}\n\t\t\treturn injectHeaders(updated);\n\t\t};\n\n\t\t// Safely template a future/discovered model ID using the static fallback.\n\t\tconst templateDiscovery = (info: KimiModelInfo): Model<Api> => {\n\t\t\tconst templated: Model<Api> = {\n\t\t\t\t...fallbackTemplate,\n\t\t\t\tid: info.id,\n\t\t\t\tname: info.display_name || info.id,\n\t\t\t\tcontextWindow: info.context_length || fallbackTemplate.contextWindow,\n\t\t\t\treasoning:\n\t\t\t\t\tinfo.supports_thinking_type === undefined\n\t\t\t\t\t\t? (info.supports_reasoning ?? false)\n\t\t\t\t\t\t: info.supports_thinking_type !== \"no\",\n\t\t\t\tinput: info.supports_image_in === true ? [\"text\", \"image\"] : [\"text\"],\n\t\t\t\tcompat: discoveredCompat(fallbackTemplate, info),\n\t\t\t};\n\t\t\treturn injectHeaders(templated);\n\t\t};\n\n\t\t// The official client treats only the explicit \"anthropic\" protocol as a\n\t\t// separate wire format; absent and future values use the default Kimi route.\n\t\t// `k3-256k` is the cheaper 256k wire variant of `k3`; dreb surfaces both\n\t\t// through the single user-facing `k3` model (auto context tier), so it\n\t\t// must not appear as a separate selectable model.\n\t\tconst supportedDiscovered = discovered.filter(\n\t\t\t(info) => info.supports_tool_use !== false && info.protocol !== \"anthropic\" && info.id !== \"k3-256k\",\n\t\t);\n\t\tconst result: Model<Api>[] = [];\n\t\tconst seen = new Set<string>();\n\n\t\t// 1. Walk the original model list to preserve order, replacing discovered\n\t\t// static models and dropping undiscovered entries. A successful response\n\t\t// is authoritative for the subscription's current entitlements.\n\t\tfor (const m of models) {\n\t\t\tif (m.provider !== \"kimi-coding-oauth\") {\n\t\t\t\tresult.push(m);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst info = staticById.has(m.id) ? supportedDiscovered.find((d) => d.id === m.id) : undefined;\n\t\t\tif (info) {\n\t\t\t\tresult.push(applyDiscovery(m, info));\n\t\t\t\tseen.add(info.id);\n\t\t\t}\n\t\t}\n\n\t\t// 2. Discovered models with IDs not present in the static list are templated\n\t\t// from the fallback so future model IDs are safely usable.\n\t\tfor (const info of supportedDiscovered) {\n\t\t\tif (!seen.has(info.id)) {\n\t\t\t\tresult.push(templateDiscovery(info));\n\t\t\t\tseen.add(info.id);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t},\n};\n"]}
@@ -646,7 +646,10 @@ export const kimiCodingOAuthProvider = {
646
646
  };
647
647
  // The official client treats only the explicit "anthropic" protocol as a
648
648
  // separate wire format; absent and future values use the default Kimi route.
649
- const supportedDiscovered = discovered.filter((info) => info.supports_tool_use !== false && info.protocol !== "anthropic");
649
+ // `k3-256k` is the cheaper 256k wire variant of `k3`; dreb surfaces both
650
+ // through the single user-facing `k3` model (auto context tier), so it
651
+ // must not appear as a separate selectable model.
652
+ const supportedDiscovered = discovered.filter((info) => info.supports_tool_use !== false && info.protocol !== "anthropic" && info.id !== "k3-256k");
650
653
  const result = [];
651
654
  const seen = new Set();
652
655
  // 1. Walk the original model list to preserve order, replacing discovered