zidane 7.0.12 → 7.0.14

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.
Files changed (47) hide show
  1. package/README.md +2 -2
  2. package/dist/{acp-bTPYx7Sd.js → acp-CZpX91YE.js} +2 -2
  3. package/dist/{acp-bTPYx7Sd.js.map → acp-CZpX91YE.js.map} +1 -1
  4. package/dist/acp-cli.js +4 -4
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-DRNT6DbC.js → agent-B1jsFaDq.js} +2 -2
  7. package/dist/agent-B1jsFaDq.js.map +1 -0
  8. package/dist/agent.js +1 -1
  9. package/dist/{anthropic-BDOGzhtj.js → anthropic-Da39cE-d.js} +2 -2
  10. package/dist/{anthropic-BDOGzhtj.js.map → anthropic-Da39cE-d.js.map} +1 -1
  11. package/dist/{approvals-B4PYwOnW.js → approvals-S100xf1d.js} +4 -4
  12. package/dist/{approvals-B4PYwOnW.js.map → approvals-S100xf1d.js.map} +1 -1
  13. package/dist/{auth-BcG1Y3QL.js → auth-DfEWnkC7.js} +22 -16
  14. package/dist/auth-DfEWnkC7.js.map +1 -0
  15. package/dist/chat.js +3 -3
  16. package/dist/daemon-cli.js +1 -1
  17. package/dist/extensions.js +1 -1
  18. package/dist/headless.js +1 -1
  19. package/dist/index-DFGECPfm.d.ts.map +1 -1
  20. package/dist/index-DP7xYXr9.d.ts.map +1 -1
  21. package/dist/index.js +3 -3
  22. package/dist/{ipc-CB6NoNxG.js → ipc-BLLscoIl.js} +2 -2
  23. package/dist/{ipc-CB6NoNxG.js.map → ipc-BLLscoIl.js.map} +1 -1
  24. package/dist/ipc.js +3 -3
  25. package/dist/presets.js +1 -1
  26. package/dist/providers/anthropic.js +1 -1
  27. package/dist/providers/baseten.js +1 -1
  28. package/dist/providers/baseten.js.map +1 -1
  29. package/dist/providers/openai.js +1 -1
  30. package/dist/providers/openai.js.map +1 -1
  31. package/dist/providers/openrouter.js +5 -2
  32. package/dist/providers/openrouter.js.map +1 -1
  33. package/dist/providers/xai.js +1 -1
  34. package/dist/providers/xai.js.map +1 -1
  35. package/dist/{providers-DX91jwir.js → providers-BYgyPb15.js} +2 -2
  36. package/dist/{providers-DX91jwir.js.map → providers-BYgyPb15.js.map} +1 -1
  37. package/dist/providers.js +2 -2
  38. package/dist/restate.js +1 -1
  39. package/dist/{server-BuqLVgQA.js → server-C8gULJ0d.js} +4 -4
  40. package/dist/{server-BuqLVgQA.js.map → server-C8gULJ0d.js.map} +1 -1
  41. package/dist/tools.js +1 -1
  42. package/dist/transcript-anchors-D1ToF-EW.d.ts.map +1 -1
  43. package/dist/tui.js +4 -4
  44. package/docs/SKILL.md +2 -2
  45. package/package.json +2 -2
  46. package/dist/agent-DRNT6DbC.js.map +0 -1
  47. package/dist/auth-BcG1Y3QL.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"openai.js","names":["getModel","catalogLookup","streamOpenAIResponses","streamOpenAICodexResponses","stream"],"sources":["../../src/providers/openai.ts"],"sourcesContent":["import type {\n AssistantMessage as PiAssistantMessage,\n Context as PiContext,\n Message as PiMessage,\n Model as PiModel,\n Tool as PiTool,\n Usage as PiUsage,\n} from '@earendil-works/pi-ai'\nimport type { Provider, StreamCallbacks, StreamOptions, ToolSpec, TurnResult } from '.'\nimport type { ClassifiedError } from '../errors'\nimport type { SessionContentBlock, SessionMessage, ToolResultDocumentContent, TurnFinishReason, TurnUsage } from '../types'\nimport type { ModelCatalog } from './model-catalog'\nimport type { OAuthParams } from './oauth'\nimport { stream as streamOpenAICodexResponses } from '@earendil-works/pi-ai/api/openai-codex-responses'\nimport { stream as streamOpenAIResponses } from '@earendil-works/pi-ai/api/openai-responses'\nimport { getBuiltinModel as getModel } from '@earendil-works/pi-ai/providers/all'\nimport { classifyErrorPrelude, isRetryableHttpStatus, matchesContextExceeded, matchesToolPairingError } from '../errors'\nimport { unsupportedMediaError } from '../prompt'\nimport { reasoningWireAction, SYNTHETIC_TOOL_RESULT_PLACEHOLDER } from '../session/messages'\nimport { renderSystemForWire } from '../system-prompt'\nimport { assertResolvedMediaBlock, documentBlockMarker } from '../types'\nimport { fillEstimatedCost } from './cost'\n// `lookupModel` is aliased: that name is already the pi-ai registry getter below.\nimport { aliasCatalogProviders, lookupModel as catalogLookup, mergeCatalogEntry } from './model-catalog'\nimport { extractRuntimeCredentials, resolveOAuthApiKey } from './oauth'\nimport {\n assistantMessage,\n toolResultsMessage,\n userMessage,\n} from './openai-compat'\nimport { sanitizeToolSpecs } from './schema-sanitize'\nimport { createToolNameBoundary } from './tool-name-boundary'\n\nconst PROVIDER_ID = 'openai-codex'\n/**\n * pi-ai registry id for the standard platform API (`api.openai.com`).\n * The provider auto-detects the backend per stream from the credential\n * shape: a Codex OAuth JWT (`eyJ…`) streams via the ChatGPT Codex backend,\n * a platform API key (`sk-…`) via the standard Responses API. Same model\n * ids, same event contract — only the wire endpoint and billing differ.\n */\nconst PLATFORM_PROVIDER_ID = 'openai'\nconst DEFAULT_MODEL = 'gpt-5.5'\n\n// Metadata aliases for model ids OpenAI publishes as shorthand (e.g. `gpt-5.6`\n// → the Sol flagship). Used ONLY to resolve extra-model metadata (cost,\n// context window, output ceiling); the requested id is preserved on the wire,\n// which OpenAI accepts. Mirrors `openaiDescriptor.modelAliases` in the chat\n// layer (`src/chat/providers.ts`) — the provider layer can't import that\n// descriptor without a layering cycle. Keep the two in sync.\nconst MODEL_ALIASES: Readonly<Record<string, string>> = {\n 'gpt-5.6': 'gpt-5.6-sol',\n}\n\ntype CodexModel = PiModel<'openai-codex-responses'>\ntype PlatformModel = PiModel<'openai-responses'>\n\nexport interface OpenAIParams extends OAuthParams {\n accountId?: string\n defaultModel?: string\n transport?: 'sse' | 'websocket' | 'auto'\n /**\n * Extra HTTP headers merged into every request (the streaming Responses call\n * and the token-count endpoint). Use for routing/attribution headers a proxy\n * in front of the OpenAI Codex endpoint consumes. Merged with provider\n * defaults; pi-ai lets these override defaults, so avoid `authorization` /\n * `content-type` unless you intend to replace them.\n */\n extraHeaders?: Record<string, string>\n}\n\n// pi-ai's `getModel` is typed for `KnownProvider` × `keyof models[P]`;\n// we look up by free-form `modelId` (the registry holds string ids at\n// runtime) and let pi-ai's miss-throw fall through via the wider lookup.\nconst lookupModel = getModel as (provider: string, modelId: string) => CodexModel | undefined\n\nfunction resolveModel(\n modelId: string,\n providerId: string = PROVIDER_ID,\n catalog?: ModelCatalog,\n): CodexModel {\n // A catalog entry overlays whatever the registry resolves below (including\n // the alias and default-model paths), so an override fixes metadata without\n // having to restate the whole model record.\n const entry = catalogLookup(providerId, modelId, catalog)\n const model = lookupModel(providerId, modelId)\n if (model)\n return mergeCatalogEntry(model, entry)\n\n // Shorthand aliases (`gpt-5.6` → `gpt-5.6-sol`): use the canonical\n // registry entry's metadata (cost, context window, output ceiling) while\n // keeping the requested id on the wire — OpenAI accepts the shorthand,\n // and pi-ai's usage accounting must bill at the real rates rather than\n // the default-model fallback's.\n const aliasId = MODEL_ALIASES[modelId]\n const aliased = aliasId ? lookupModel(providerId, aliasId) : undefined\n if (aliased)\n return mergeCatalogEntry({ ...aliased, id: modelId }, entry)\n\n // Registry miss with a catalog entry: the entry is all we know, so build the\n // record from the default model's shape and let the entry override. Without\n // one this stays the historical \"unknown id, default metadata\" fallback.\n const fallback = lookupModel(providerId, DEFAULT_MODEL)\n if (!fallback)\n throw new Error(`OpenAI model registry (${providerId}) is missing the default model: ${DEFAULT_MODEL}`)\n\n return mergeCatalogEntry({ ...fallback, id: modelId, name: modelId }, entry)\n}\n\nfunction emptyUsage(): PiUsage {\n return {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: 0,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n }\n}\n\nfunction formatTools(tools: ToolSpec[]): PiTool[] {\n // Codex's Responses API runs the same schema validator as the OpenAI\n // Chat Completions endpoint — apply the same sanitisation pass so MCP\n // tools with non-standard root shapes don't 400 here either.\n const sanitized = sanitizeToolSpecs(tools, { profile: 'openai' })\n // `?? ''`: strict validators (and anthropic/openai-format gateways) treat\n // `description` as a required string; MCP tools routinely omit it.\n return sanitized.map(t => ({\n name: t.name,\n description: t.description ?? '',\n parameters: t.inputSchema as PiTool['parameters'],\n }))\n}\n\nfunction documentMarker(doc: ToolResultDocumentContent): string {\n return documentBlockMarker(doc, 'document omitted')\n}\n\nfunction piToolResultMessage(\n result: Extract<SessionContentBlock, { type: 'tool_result' }>,\n toolName = '',\n): PiMessage {\n // pi-ai's ToolResultMessage natively accepts (TextContent | ImageContent)[].\n // Map zidane's canonical union directly — no flattening needed.\n const content = typeof result.output === 'string'\n ? [{ type: 'text' as const, text: result.output }]\n : result.output.map((block) => {\n if (block.type === 'image') {\n assertResolvedMediaBlock(block, 'OpenAI wire messages')\n return { type: 'image' as const, data: block.data, mimeType: block.mediaType }\n }\n if (block.type === 'audio')\n throw unsupportedMediaError('audio', 'openai')\n if (block.type === 'video')\n throw unsupportedMediaError('video', 'openai')\n if (block.type === 'document') {\n assertResolvedMediaBlock(block, 'OpenAI wire messages')\n return { type: 'text' as const, text: documentMarker(block) }\n }\n return { type: 'text' as const, text: block.text }\n })\n\n return {\n role: 'toolResult',\n toolCallId: result.callId,\n toolName,\n content,\n isError: result.isError ?? false,\n timestamp: Date.now(),\n }\n}\n\nfunction piUserMessage(\n content: SessionContentBlock[],\n): PiMessage | null {\n for (const b of content) {\n if (b.type === 'audio')\n throw unsupportedMediaError('audio', 'openai')\n if (b.type === 'video')\n throw unsupportedMediaError('video', 'openai')\n }\n\n const textBlocks = content.filter(b => b.type === 'text')\n const imageBlocks = content.filter(b => b.type === 'image')\n const documentBlocks = content.filter(b => b.type === 'document')\n\n if (imageBlocks.length === 0 && textBlocks.length === 0 && documentBlocks.length === 0)\n return null\n\n if (imageBlocks.length === 0 && documentBlocks.length === 0 && textBlocks.length === 1)\n return { role: 'user', content: textBlocks[0].text, timestamp: Date.now() }\n\n return {\n role: 'user',\n content: [\n ...imageBlocks.map((img) => {\n assertResolvedMediaBlock(img, 'OpenAI wire messages')\n return { type: 'image' as const, data: img.data, mimeType: img.mediaType }\n }),\n // NOTE: OpenAI's API natively accepts PDFs (Responses `input_file` /\n // Chat Completions `file`), but pi-ai's message content union is\n // text|image only — it has no file content type — so documents degrade to\n // a marker here. To forward PDFs to OpenAI, route via:\n // openaiCompat({ name: 'openai', apiKey, baseURL: 'https://api.openai.com/v1',\n // capabilities: { documents: true } })\n // which emits the `{type:'file'}` Chat Completions part directly. (That\n // path accepts application/pdf only; spreadsheets/.docx need the\n // Responses API, which would require an input_file content type in pi-ai.)\n ...documentBlocks.map((block) => {\n assertResolvedMediaBlock(block, 'OpenAI wire messages')\n return { type: 'text' as const, text: documentMarker(block) }\n }),\n ...textBlocks.map(block => ({ type: 'text' as const, text: block.text })),\n ],\n timestamp: Date.now(),\n }\n}\n\nfunction piAssistantMessage(\n content: SessionContentBlock[],\n modelId: string,\n): PiAssistantMessage {\n const piContent: PiAssistantMessage['content'] = []\n for (const block of content) {\n if (block.type === 'text') {\n piContent.push({ type: 'text', text: block.text })\n }\n else if (block.type === 'thinking') {\n // Shared cross-provider policy (`reasoningWireAction`): drop blocks\n // minted by Anthropic (their HMAC is not valid `encrypted_content`),\n // strip the signature from untagged legacy blocks (no signature-400\n // recovery exists on this path), forward openai-tagged ones intact.\n const action = reasoningWireAction(block, 'openai')\n if (action === 'drop')\n continue\n piContent.push({\n type: 'thinking',\n thinking: block.text,\n ...(action === 'keep' && block.signature !== undefined ? { thinkingSignature: block.signature } : {}),\n })\n }\n else if (block.type === 'tool_call') {\n piContent.push({ type: 'toolCall', id: block.id, name: block.name, arguments: block.input })\n }\n // `redacted_thinking` (Anthropic-only) and `provider_reasoning` (provider-\n // bound reasoning state) are intentionally dropped — Codex Responses\n // doesn't accept them.\n }\n\n return {\n role: 'assistant',\n content: piContent,\n api: 'openai-codex-responses',\n provider: PROVIDER_ID,\n model: modelId,\n usage: emptyUsage(),\n stopReason: 'stop',\n timestamp: Date.now(),\n }\n}\n\nexport function toPiMessages(messages: SessionMessage[], modelId: string): PiMessage[] {\n const out: PiMessage[] = []\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n const toolCalls = msg.content.filter((b): b is Extract<SessionContentBlock, { type: 'tool_call' }> => b.type === 'tool_call')\n\n if (msg.role === 'assistant' && toolCalls.length > 0) {\n const next = messages[i + 1]\n const nextToolResults = next?.role === 'user'\n ? next.content.filter((b): b is Extract<SessionContentBlock, { type: 'tool_result' }> => b.type === 'tool_result')\n : []\n const resultById = new Map(nextToolResults.map(result => [result.callId, result]))\n\n let assistantContent: SessionContentBlock[] = []\n for (const block of msg.content) {\n assistantContent.push(block)\n if (block.type !== 'tool_call')\n continue\n\n out.push(piAssistantMessage(assistantContent, modelId))\n assistantContent = []\n\n const result = resultById.get(block.id)\n if (result) {\n out.push(piToolResultMessage(result, block.name))\n }\n else {\n out.push(piToolResultMessage({\n type: 'tool_result',\n callId: block.id,\n output: SYNTHETIC_TOOL_RESULT_PLACEHOLDER,\n isError: true,\n }, block.name))\n }\n }\n if (assistantContent.length > 0)\n out.push(piAssistantMessage(assistantContent, modelId))\n\n if (next?.role === 'user') {\n const remainingUserContent = next.content.filter((block) => {\n if (block.type !== 'tool_result')\n return true\n // Orphan/duplicate tool results should already be removed by the\n // canonical repair pass. If one reaches this adapter, do not send it\n // as an unpaired provider message.\n return false\n })\n const userMessage = piUserMessage(remainingUserContent)\n if (userMessage)\n out.push(userMessage)\n i++\n }\n continue\n }\n\n const toolResults = msg.content.filter(b => b.type === 'tool_result')\n if (toolResults.length > 0) {\n const userMsg = msg.role === 'user'\n ? piUserMessage(msg.content.filter(b => b.type !== 'tool_result'))\n : null\n if (userMsg)\n out.push(userMsg)\n continue\n }\n\n if (msg.role === 'user') {\n const userMsg = piUserMessage(msg.content)\n if (userMsg)\n out.push(userMsg)\n continue\n }\n\n out.push(piAssistantMessage(msg.content, modelId))\n }\n\n return out\n}\n\n/**\n * Map pi-ai's `StopReason` to the zidane `TurnFinishReason` union — the same\n * native-stop-reason fidelity anthropic's `mapStopReason` / openai-compat's\n * `mapOAIFinishReason` provide. Notably `length` (max-token truncation) is no\n * longer misreported as a clean `stop`. `hasToolCalls` is the fallback for\n * messages whose stop reason is missing or doesn't carry the tool-call signal.\n */\nexport function mapCodexStopReason(stopReason: string | undefined, hasToolCalls: boolean): TurnFinishReason {\n switch (stopReason) {\n case 'toolUse':\n return 'tool-calls'\n case 'length':\n return 'length'\n case 'error':\n return 'error'\n case 'stop':\n return hasToolCalls ? 'tool-calls' : 'stop'\n case 'aborted':\n return 'other'\n default:\n return hasToolCalls ? 'tool-calls' : 'stop'\n }\n}\n\nfunction fromPiAssistantMessage(message: PiAssistantMessage): SessionMessage {\n const content: SessionContentBlock[] = []\n\n for (const block of message.content) {\n if (block.type === 'text') {\n content.push({ type: 'text', text: block.text })\n }\n else if (block.type === 'thinking') {\n const out: Extract<SessionContentBlock, { type: 'thinking' }> = {\n type: 'thinking',\n text: block.thinking,\n }\n if (typeof block.thinkingSignature === 'string') {\n out.signature = block.thinkingSignature\n out.signatureProducer = 'openai'\n }\n content.push(out)\n }\n else if (block.type === 'toolCall') {\n content.push({ type: 'tool_call', id: block.id, name: block.name, input: block.arguments })\n }\n }\n\n return { role: 'assistant', content }\n}\n\nfunction extractToolCalls(message: PiAssistantMessage) {\n return message.content\n .filter(block => block.type === 'toolCall')\n .map(block => ({\n id: block.id,\n name: block.name,\n input: block.arguments as Record<string, unknown>,\n }))\n}\n\nfunction extractText(message: PiAssistantMessage): string {\n return message.content\n .filter((block): block is Extract<PiAssistantMessage['content'][number], { type: 'text' }> => block.type === 'text')\n .map(block => block.text)\n .join('')\n}\n\nfunction toTurnUsage(usage: PiUsage, finishReason: TurnFinishReason | undefined, modelId: string, catalog?: ModelCatalog): Promise<TurnUsage> {\n return fillEstimatedCost({\n input: usage.input,\n output: usage.output,\n cacheRead: usage.cacheRead || undefined,\n cacheCreation: usage.cacheWrite || undefined,\n cost: usage.cost.total || undefined,\n ...(finishReason ? { finishReason } : {}),\n modelId,\n }, 'openai', undefined, catalog)\n}\n\n/**\n * Transient pi-ai error patterns worth a loop-level retry. pi-ai already retries\n * 429 / 5xx pre-stream 3× internally (see `openai-codex-responses.js`); by the\n * time the error reaches us, that budget is exhausted. The loop-level retry\n * with its longer backoff is a second line of defense for capacity events\n * that persist past pi-ai's short retry window, plus mid-stream emissions\n * (pi-ai surfaces those as plain `Error`s with no status).\n *\n * Mirrors pi-ai's own `isRetryableError` regex so the two retry layers\n * recognize the same failure modes.\n */\nconst TRANSIENT_OPENAI_MESSAGE_RE = /rate.?limit|overloaded|service.?unavailable|upstream.?connect|connection.?refused|gateway.?time.?out|temporarily.?unavailable/i\n\n/** Numeric HTTP status codes pi-ai sometimes attaches when bubbling structured errors. */\nfunction isRetryableStatusCode(err: object): boolean {\n const status = (err as { status?: unknown }).status\n if (typeof status !== 'number')\n return false\n return isRetryableHttpStatus(status)\n}\n\n/**\n * Classify an OpenAI Codex error. pi-ai surfaces errors either as thrown `Error`s\n * (wrapping `event.error.errorMessage`) or via stream event types.\n *\n * Retryable hint is set when pi-ai's own retry budget is exhausted on a\n * transient failure — pattern-matched against `message` since pi-ai strips\n * structured fields on plain-Error throws. Set `behavior.retry.maxAttempts: 1`\n * to disable the loop-level retry.\n */\nexport function classifyOpenAIError(err: unknown): ClassifiedError | null {\n const prelude = classifyErrorPrelude(err)\n if (prelude === 'not-object')\n return null\n if (prelude === 'aborted')\n return { kind: 'aborted' }\n\n const anyErr = err as { name?: string, message?: string, code?: string, type?: string }\n\n const message = anyErr.message ?? ''\n const code = anyErr.code ?? anyErr.type\n\n if (code === 'context_length_exceeded' || matchesContextExceeded(message)) {\n return {\n kind: 'context_exceeded',\n providerCode: code ?? 'context_length_exceeded',\n message,\n }\n }\n\n if (matchesToolPairingError(message)) {\n return {\n kind: 'tool_pairing_corruption',\n providerCode: code ?? 'invalid_request_error',\n message,\n }\n }\n\n // pi-ai wraps API errors in generic `Error` — treat as provider_error when we have a message.\n if (message.length > 0) {\n const retryable = isRetryableStatusCode(anyErr) || TRANSIENT_OPENAI_MESSAGE_RE.test(message)\n return {\n kind: 'provider_error',\n providerCode: code,\n message,\n ...(retryable ? { retryable: true } : {}),\n }\n }\n\n return null\n}\n\n/**\n * A standard OpenAI API key usable against `api.openai.com` (for the exact\n * token-count endpoint), or `null` when only a Codex-OAuth credential is\n * available. Codex OAuth access tokens are JWTs (`eyJ…`) and are NOT valid\n * bearers for the standard API; a real key looks like `sk-…` (but not the\n * Anthropic `sk-ant-…` form). Checks `params.apiKey` then `OPENAI_API_KEY`.\n */\n/**\n * OpenAI's org-verification rejection for `reasoning.summary` — e.g.\n * \"Your organization must be verified to generate reasoning summaries\".\n * Matched loosely on the two load-bearing words so minor copy changes\n * don't defeat the platform-branch fallback.\n */\nfunction isReasoningSummaryVerificationError(err: unknown): boolean {\n if (!(err instanceof Error))\n return false\n return /reasoning summar/i.test(err.message) && /verif/i.test(err.message)\n}\n\nfunction pickStandardOpenAIKey(params?: OpenAIParams): string | null {\n const candidates = [params?.apiKey, process.env.OPENAI_API_KEY]\n for (const key of candidates) {\n if (typeof key === 'string' && key.startsWith('sk-') && !key.startsWith('sk-ant-'))\n return key\n }\n return null\n}\n\n/**\n * Convert canonical {@link SessionMessage}s to OpenAI Responses-API `input`\n * items for the token-count endpoint. Only the text-bearing shape is needed\n * here (the count path sends a tiny dummy message); images/tool-results are\n * down-converted to text so the count never throws on an exotic block.\n */\nfunction toResponsesInput(messages: SessionMessage[]): unknown[] {\n return messages.map((msg) => {\n const text = msg.content\n .map(b => (b.type === 'text' ? b.text : ''))\n .filter(Boolean)\n .join('\\n')\n const part = msg.role === 'assistant' ? 'output_text' : 'input_text'\n return { role: msg.role === 'assistant' ? 'assistant' : 'user', content: [{ type: part, text }] }\n })\n}\n\n/** Convert pi-ai `PiTool` specs to Responses-API function tools. */\nfunction toResponsesTools(tools: unknown[]): unknown[] {\n return tools.map((t) => {\n const tool = t as { name?: string, description?: string, parameters?: unknown }\n return {\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n }\n })\n}\n\nfunction applyPayloadOverrides(payload: unknown, options: StreamOptions, toWireToolName: (name: string) => string = name => name): unknown {\n const body = payload as Record<string, unknown>\n\n if (options.toolChoice) {\n if (options.toolChoice.type === 'tool' && options.toolChoice.name)\n body.tool_choice = { type: 'function', name: toWireToolName(options.toolChoice.name) }\n else if (options.toolChoice.type === 'required')\n body.tool_choice = 'required'\n else\n body.tool_choice = 'auto'\n }\n\n return body\n}\n\nexport function openai(params?: OpenAIParams): Provider {\n const defaultModel = params?.defaultModel || DEFAULT_MODEL\n const baseCredentials = extractRuntimeCredentials(params)\n let runtimeCredentials = baseCredentials\n ? { ...baseCredentials, ...(params?.accountId ? { accountId: params.accountId } : {}) }\n : undefined\n\n return {\n name: 'openai',\n meta: {\n defaultModel,\n isOAuth: true,\n capabilities: {\n vision: true,\n imageInToolResult: true,\n // pi-ai's Codex Responses wire has no audio/video input content type.\n audio: false,\n video: false,\n },\n },\n formatTools,\n userMessage,\n assistantMessage,\n toolResultsMessage,\n classifyError: classifyOpenAIError,\n\n async countTokens(payload, signal): Promise<number | null> {\n // The exact count endpoint (`POST /v1/responses/input_tokens`) lives on\n // the standard OpenAI API and needs a standard `OPENAI_API_KEY` bearer.\n // This provider authenticates via Codex OAuth against the Codex backend,\n // whose token is NOT valid for that endpoint — so we only attempt the\n // count when a standard key is present (env or params.apiKey that isn't\n // an OAuth/JWT token). Otherwise return null → heuristic fallback.\n const standardKey = pickStandardOpenAIKey(params)\n if (!standardKey)\n return null\n try {\n const tools = payload.tools as PiTool[]\n const nameBoundary = createToolNameBoundary(payload.messages, tools.map(tool => tool.name))\n const res = await fetch('https://api.openai.com/v1/responses/input_tokens', {\n method: 'POST',\n headers: {\n ...params?.extraHeaders,\n 'authorization': `Bearer ${standardKey}`,\n 'content-type': 'application/json',\n },\n // The Responses `input_tokens` endpoint takes the same shape as\n // `responses.create`: `instructions` (system), `input` (Responses\n // input items — NOT pi-ai messages), and function-shaped `tools`.\n body: JSON.stringify({\n model: payload.model || defaultModel,\n instructions: renderSystemForWire(payload.system),\n input: toResponsesInput(nameBoundary.messages),\n tools: toResponsesTools(tools.map(tool => ({ ...tool, name: nameBoundary.toWire(tool.name) }))),\n }),\n ...(signal ? { signal } : {}),\n })\n if (!res.ok)\n return null\n const json = (await res.json()) as { input_tokens?: unknown }\n return typeof json.input_tokens === 'number' ? json.input_tokens : null\n }\n catch {\n return null\n }\n },\n\n async stream(options: StreamOptions, callbacks: StreamCallbacks): Promise<TurnResult> {\n const modelId = options.model || defaultModel\n const apiKey = await resolveOAuthApiKey(\n {\n provider: 'openai',\n providerId: PROVIDER_ID,\n params: runtimeCredentials ? { ...params, ...runtimeCredentials } : params,\n envKey: 'OPENAI_CODEX_API_KEY',\n extraCredentialKeys: ['accountId'],\n missingError: 'No OpenAI Codex OAuth token found. Run `bun run auth --openai` first.',\n refreshError: reason => `OpenAI Codex OAuth token refresh failed. Run \\`bun run auth --openai\\` again. ${reason}`,\n },\n {\n ...callbacks,\n async onOAuthRefresh(ctx) {\n if (ctx.source === 'params') {\n runtimeCredentials = {\n access: ctx.credentials.access,\n refresh: ctx.credentials.refresh,\n expires: ctx.credentials.expires,\n ...(typeof ctx.credentials.accountId === 'string' ? { accountId: ctx.credentials.accountId } : {}),\n }\n }\n await callbacks.onOAuthRefresh?.(ctx)\n },\n },\n )\n // Backend auto-detection by credential shape. A Codex OAuth access\n // token is a JWT whose claims carry the `chatgpt_account_id` the Codex\n // backend requires per request; a standard `sk-…` platform key can\n // NEVER stream there (pi-ai fails with an opaque \"Failed to extract\n // accountId from token\") — but it's a first-class credential for the\n // standard Responses API, so route it there instead. Scoped to the\n // unambiguous `sk-` shape: exotic bearers (test harnesses, relays)\n // keep flowing to the Codex path untouched.\n const usePlatformApi = apiKey.startsWith('sk-')\n const model = resolveModel(modelId, usePlatformApi ? PLATFORM_PROVIDER_ID : PROVIDER_ID, options.modelCatalog)\n const tools = options.tools as PiTool[]\n const nameBoundary = createToolNameBoundary(options.messages, tools.map(tool => tool.name))\n // Codex's Responses API has no `cache_control` analogue, but the\n // system-prompt boundary marker is structural metadata that should\n // never reach the model. Strip it via `renderSystemForWire`; the\n // doctrine + env are collapsed into a single byte stream the model\n // sees as one logical prompt.\n const context: PiContext = {\n systemPrompt: renderSystemForWire(options.system),\n messages: toPiMessages(nameBoundary.messages, modelId),\n tools: tools.map(tool => ({ ...tool, name: nameBoundary.toWire(tool.name) })),\n }\n // OpenAI's `reasoning_effort` accepts minimal/low/medium/high/xhigh, and\n // the GPT-5.6 line adds a native `max` tier. Clamp `max` to `xhigh` on\n // older models only; `'adaptive'` degrades to no reasoning rather than\n // forwarding an unknown value the API would reject.\n const supportsMaxEffort = modelId.startsWith('gpt-5.6')\n const reasoningLevel\n = options.thinking && options.thinking !== 'off' && options.thinking !== 'adaptive'\n ? options.thinking === 'max' && !supportsMaxEffort ? 'xhigh' : options.thinking\n : undefined\n // Options are shared verbatim across both backends — the platform\n // Responses API and the Codex bridge take the same reasoning / service\n // tier knobs; only the websocket `transport` toggle is Codex-specific.\n const makeStream = (reasoningSummary: 'auto' | undefined) => {\n const streamOptions = {\n apiKey,\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n signal: options.signal,\n reasoningEffort: reasoningLevel,\n reasoningSummary,\n ...(options.modelOptions?.fast ? { serviceTier: 'priority' as const } : {}),\n ...(params?.extraHeaders ? { headers: params.extraHeaders } : {}),\n // pi-ai types the payload as `unknown`; both backends send the\n // Responses-API body shape `applyPayloadOverrides` expects.\n onPayload: (payload: unknown) => applyPayloadOverrides(payload as Record<string, unknown>, options, nameBoundary.toWire),\n }\n return usePlatformApi\n ? streamOpenAIResponses(model as unknown as PlatformModel, context, streamOptions)\n : streamOpenAICodexResponses(model, context, { ...streamOptions, transport: params?.transport })\n }\n let stream = makeStream(reasoningLevel ? 'auto' : undefined)\n\n let finalMessage: PiAssistantMessage | undefined\n let text = ''\n let thinking = ''\n\n const consume = async (s: ReturnType<typeof makeStream>) => {\n for await (const event of s) {\n if (event.type === 'text_delta') {\n text += event.delta\n callbacks.onText(event.delta)\n }\n else if (event.type === 'thinking_delta') {\n thinking += event.delta\n callbacks.onThinking?.(event.delta)\n }\n else if (event.type === 'thinking_end') {\n const delta = event.content.startsWith(thinking)\n ? event.content.slice(thinking.length)\n : (thinking ? '' : event.content)\n if (delta) {\n thinking += delta\n callbacks.onThinking?.(delta)\n }\n }\n else if (event.type === 'done') {\n finalMessage = event.message\n }\n else if (event.type === 'error') {\n throw new Error(event.error.errorMessage || (usePlatformApi ? 'OpenAI API error' : 'OpenAI Codex API error'))\n }\n }\n }\n\n try {\n await consume(stream)\n }\n catch (err) {\n // The platform Responses API rejects reasoning summaries for\n // organizations that haven't completed verification (the Codex\n // backend has no such gate). That 400 fails request validation, so\n // nothing has streamed yet — retry once without the summary rather\n // than bricking every reasoning-enabled stream for those orgs.\n const nothingStreamed = text.length === 0 && thinking.length === 0 && finalMessage === undefined\n if (usePlatformApi && reasoningLevel && nothingStreamed && isReasoningSummaryVerificationError(err)) {\n stream = makeStream(undefined)\n await consume(stream)\n }\n else {\n throw err\n }\n }\n\n finalMessage ??= await stream.result()\n text ||= extractText(finalMessage)\n\n const toolCalls = extractToolCalls(finalMessage)\n .map(call => ({ ...call, name: nameBoundary.fromWire(call.name) }))\n const assistantTurn = fromPiAssistantMessage(finalMessage)\n assistantTurn.content = assistantTurn.content.map(block =>\n block.type === 'tool_call' ? { ...block, name: nameBoundary.fromWire(block.name) } : block,\n )\n const finishReason = mapCodexStopReason(finalMessage.stopReason, toolCalls.length > 0)\n // Metadata resolves under `openai-codex` (or `openai` on the platform\n // path) while cost estimation runs under `openai` — accept a catalog\n // entry keyed either way so one entry fixes both.\n const usage = await toTurnUsage(finalMessage.usage, finishReason, modelId, aliasCatalogProviders(\n options.modelCatalog,\n [usePlatformApi ? PLATFORM_PROVIDER_ID : PROVIDER_ID, PLATFORM_PROVIDER_ID],\n ))\n\n return {\n assistantMessage: assistantTurn,\n text,\n toolCalls,\n done: toolCalls.length === 0,\n usage,\n }\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"openai.js","names":["getModel","catalogLookup","streamOpenAIResponses","streamOpenAICodexResponses","stream"],"sources":["../../src/providers/openai.ts"],"sourcesContent":["import type {\n AssistantMessage as PiAssistantMessage,\n Context as PiContext,\n Message as PiMessage,\n Model as PiModel,\n Tool as PiTool,\n Usage as PiUsage,\n} from '@earendil-works/pi-ai'\nimport type { Provider, StreamCallbacks, StreamOptions, ToolSpec, TurnResult } from '.'\nimport type { ClassifiedError } from '../errors'\nimport type { SessionContentBlock, SessionMessage, ToolResultDocumentContent, TurnFinishReason, TurnUsage } from '../types'\nimport type { ModelCatalog } from './model-catalog'\nimport type { OAuthParams } from './oauth'\nimport { stream as streamOpenAICodexResponses } from '@earendil-works/pi-ai/api/openai-codex-responses'\nimport { stream as streamOpenAIResponses } from '@earendil-works/pi-ai/api/openai-responses'\nimport { getBuiltinModel as getModel } from '@earendil-works/pi-ai/providers/all'\nimport { classifyErrorPrelude, isRetryableHttpStatus, matchesContextExceeded, matchesToolPairingError } from '../errors'\nimport { unsupportedMediaError } from '../prompt'\nimport { reasoningWireAction, SYNTHETIC_TOOL_RESULT_PLACEHOLDER } from '../session/messages'\nimport { renderSystemForWire } from '../system-prompt'\nimport { assertResolvedMediaBlock, documentBlockMarker } from '../types'\nimport { fillEstimatedCost } from './cost'\n// `lookupModel` is aliased: that name is already the pi-ai registry getter below.\nimport { aliasCatalogProviders, lookupModel as catalogLookup, mergeCatalogEntry } from './model-catalog'\nimport { extractRuntimeCredentials, resolveOAuthApiKey } from './oauth'\nimport {\n assistantMessage,\n toolResultsMessage,\n userMessage,\n} from './openai-compat'\nimport { sanitizeToolSpecs } from './schema-sanitize'\nimport { createToolNameBoundary } from './tool-name-boundary'\n\nconst PROVIDER_ID = 'openai-codex'\n/**\n * pi-ai registry id for the standard platform API (`api.openai.com`).\n * The provider auto-detects the backend per stream from the credential\n * shape: a Codex OAuth JWT (`eyJ…`) streams via the ChatGPT Codex backend,\n * a platform API key (`sk-…`) via the standard Responses API. Same model\n * ids, same event contract — only the wire endpoint and billing differ.\n */\nconst PLATFORM_PROVIDER_ID = 'openai'\nconst DEFAULT_MODEL = 'gpt-6-astra'\n\n// Metadata aliases for model ids OpenAI publishes as shorthand (e.g. `gpt-5.6`\n// → the Sol flagship). Used ONLY to resolve extra-model metadata (cost,\n// context window, output ceiling); the requested id is preserved on the wire,\n// which OpenAI accepts. Mirrors `openaiDescriptor.modelAliases` in the chat\n// layer (`src/chat/providers.ts`) — the provider layer can't import that\n// descriptor without a layering cycle. Keep the two in sync.\nconst MODEL_ALIASES: Readonly<Record<string, string>> = {\n 'gpt-5.6': 'gpt-5.6-sol',\n}\n\ntype CodexModel = PiModel<'openai-codex-responses'>\ntype PlatformModel = PiModel<'openai-responses'>\n\nexport interface OpenAIParams extends OAuthParams {\n accountId?: string\n defaultModel?: string\n transport?: 'sse' | 'websocket' | 'auto'\n /**\n * Extra HTTP headers merged into every request (the streaming Responses call\n * and the token-count endpoint). Use for routing/attribution headers a proxy\n * in front of the OpenAI Codex endpoint consumes. Merged with provider\n * defaults; pi-ai lets these override defaults, so avoid `authorization` /\n * `content-type` unless you intend to replace them.\n */\n extraHeaders?: Record<string, string>\n}\n\n// pi-ai's `getModel` is typed for `KnownProvider` × `keyof models[P]`;\n// we look up by free-form `modelId` (the registry holds string ids at\n// runtime) and let pi-ai's miss-throw fall through via the wider lookup.\nconst lookupModel = getModel as (provider: string, modelId: string) => CodexModel | undefined\n\nfunction resolveModel(\n modelId: string,\n providerId: string = PROVIDER_ID,\n catalog?: ModelCatalog,\n): CodexModel {\n // A catalog entry overlays whatever the registry resolves below (including\n // the alias and default-model paths), so an override fixes metadata without\n // having to restate the whole model record.\n const entry = catalogLookup(providerId, modelId, catalog)\n const model = lookupModel(providerId, modelId)\n if (model)\n return mergeCatalogEntry(model, entry)\n\n // Shorthand aliases (`gpt-5.6` → `gpt-5.6-sol`): use the canonical\n // registry entry's metadata (cost, context window, output ceiling) while\n // keeping the requested id on the wire — OpenAI accepts the shorthand,\n // and pi-ai's usage accounting must bill at the real rates rather than\n // the default-model fallback's.\n const aliasId = MODEL_ALIASES[modelId]\n const aliased = aliasId ? lookupModel(providerId, aliasId) : undefined\n if (aliased)\n return mergeCatalogEntry({ ...aliased, id: modelId }, entry)\n\n // Registry miss with a catalog entry: the entry is all we know, so build the\n // record from the default model's shape and let the entry override. Without\n // one this stays the historical \"unknown id, default metadata\" fallback.\n const fallback = lookupModel(providerId, DEFAULT_MODEL)\n if (!fallback)\n throw new Error(`OpenAI model registry (${providerId}) is missing the default model: ${DEFAULT_MODEL}`)\n\n return mergeCatalogEntry({ ...fallback, id: modelId, name: modelId }, entry)\n}\n\nfunction emptyUsage(): PiUsage {\n return {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: 0,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n }\n}\n\nfunction formatTools(tools: ToolSpec[]): PiTool[] {\n // Codex's Responses API runs the same schema validator as the OpenAI\n // Chat Completions endpoint — apply the same sanitisation pass so MCP\n // tools with non-standard root shapes don't 400 here either.\n const sanitized = sanitizeToolSpecs(tools, { profile: 'openai' })\n // `?? ''`: strict validators (and anthropic/openai-format gateways) treat\n // `description` as a required string; MCP tools routinely omit it.\n return sanitized.map(t => ({\n name: t.name,\n description: t.description ?? '',\n parameters: t.inputSchema as PiTool['parameters'],\n }))\n}\n\nfunction documentMarker(doc: ToolResultDocumentContent): string {\n return documentBlockMarker(doc, 'document omitted')\n}\n\nfunction piToolResultMessage(\n result: Extract<SessionContentBlock, { type: 'tool_result' }>,\n toolName = '',\n): PiMessage {\n // pi-ai's ToolResultMessage natively accepts (TextContent | ImageContent)[].\n // Map zidane's canonical union directly — no flattening needed.\n const content = typeof result.output === 'string'\n ? [{ type: 'text' as const, text: result.output }]\n : result.output.map((block) => {\n if (block.type === 'image') {\n assertResolvedMediaBlock(block, 'OpenAI wire messages')\n return { type: 'image' as const, data: block.data, mimeType: block.mediaType }\n }\n if (block.type === 'audio')\n throw unsupportedMediaError('audio', 'openai')\n if (block.type === 'video')\n throw unsupportedMediaError('video', 'openai')\n if (block.type === 'document') {\n assertResolvedMediaBlock(block, 'OpenAI wire messages')\n return { type: 'text' as const, text: documentMarker(block) }\n }\n return { type: 'text' as const, text: block.text }\n })\n\n return {\n role: 'toolResult',\n toolCallId: result.callId,\n toolName,\n content,\n isError: result.isError ?? false,\n timestamp: Date.now(),\n }\n}\n\nfunction piUserMessage(\n content: SessionContentBlock[],\n): PiMessage | null {\n for (const b of content) {\n if (b.type === 'audio')\n throw unsupportedMediaError('audio', 'openai')\n if (b.type === 'video')\n throw unsupportedMediaError('video', 'openai')\n }\n\n const textBlocks = content.filter(b => b.type === 'text')\n const imageBlocks = content.filter(b => b.type === 'image')\n const documentBlocks = content.filter(b => b.type === 'document')\n\n if (imageBlocks.length === 0 && textBlocks.length === 0 && documentBlocks.length === 0)\n return null\n\n if (imageBlocks.length === 0 && documentBlocks.length === 0 && textBlocks.length === 1)\n return { role: 'user', content: textBlocks[0].text, timestamp: Date.now() }\n\n return {\n role: 'user',\n content: [\n ...imageBlocks.map((img) => {\n assertResolvedMediaBlock(img, 'OpenAI wire messages')\n return { type: 'image' as const, data: img.data, mimeType: img.mediaType }\n }),\n // NOTE: OpenAI's API natively accepts PDFs (Responses `input_file` /\n // Chat Completions `file`), but pi-ai's message content union is\n // text|image only — it has no file content type — so documents degrade to\n // a marker here. To forward PDFs to OpenAI, route via:\n // openaiCompat({ name: 'openai', apiKey, baseURL: 'https://api.openai.com/v1',\n // capabilities: { documents: true } })\n // which emits the `{type:'file'}` Chat Completions part directly. (That\n // path accepts application/pdf only; spreadsheets/.docx need the\n // Responses API, which would require an input_file content type in pi-ai.)\n ...documentBlocks.map((block) => {\n assertResolvedMediaBlock(block, 'OpenAI wire messages')\n return { type: 'text' as const, text: documentMarker(block) }\n }),\n ...textBlocks.map(block => ({ type: 'text' as const, text: block.text })),\n ],\n timestamp: Date.now(),\n }\n}\n\nfunction piAssistantMessage(\n content: SessionContentBlock[],\n modelId: string,\n): PiAssistantMessage {\n const piContent: PiAssistantMessage['content'] = []\n for (const block of content) {\n if (block.type === 'text') {\n piContent.push({ type: 'text', text: block.text })\n }\n else if (block.type === 'thinking') {\n // Shared cross-provider policy (`reasoningWireAction`): drop blocks\n // minted by Anthropic (their HMAC is not valid `encrypted_content`),\n // strip the signature from untagged legacy blocks (no signature-400\n // recovery exists on this path), forward openai-tagged ones intact.\n const action = reasoningWireAction(block, 'openai')\n if (action === 'drop')\n continue\n piContent.push({\n type: 'thinking',\n thinking: block.text,\n ...(action === 'keep' && block.signature !== undefined ? { thinkingSignature: block.signature } : {}),\n })\n }\n else if (block.type === 'tool_call') {\n piContent.push({ type: 'toolCall', id: block.id, name: block.name, arguments: block.input })\n }\n // `redacted_thinking` (Anthropic-only) and `provider_reasoning` (provider-\n // bound reasoning state) are intentionally dropped — Codex Responses\n // doesn't accept them.\n }\n\n return {\n role: 'assistant',\n content: piContent,\n api: 'openai-codex-responses',\n provider: PROVIDER_ID,\n model: modelId,\n usage: emptyUsage(),\n stopReason: 'stop',\n timestamp: Date.now(),\n }\n}\n\nexport function toPiMessages(messages: SessionMessage[], modelId: string): PiMessage[] {\n const out: PiMessage[] = []\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n const toolCalls = msg.content.filter((b): b is Extract<SessionContentBlock, { type: 'tool_call' }> => b.type === 'tool_call')\n\n if (msg.role === 'assistant' && toolCalls.length > 0) {\n const next = messages[i + 1]\n const nextToolResults = next?.role === 'user'\n ? next.content.filter((b): b is Extract<SessionContentBlock, { type: 'tool_result' }> => b.type === 'tool_result')\n : []\n const resultById = new Map(nextToolResults.map(result => [result.callId, result]))\n\n let assistantContent: SessionContentBlock[] = []\n for (const block of msg.content) {\n assistantContent.push(block)\n if (block.type !== 'tool_call')\n continue\n\n out.push(piAssistantMessage(assistantContent, modelId))\n assistantContent = []\n\n const result = resultById.get(block.id)\n if (result) {\n out.push(piToolResultMessage(result, block.name))\n }\n else {\n out.push(piToolResultMessage({\n type: 'tool_result',\n callId: block.id,\n output: SYNTHETIC_TOOL_RESULT_PLACEHOLDER,\n isError: true,\n }, block.name))\n }\n }\n if (assistantContent.length > 0)\n out.push(piAssistantMessage(assistantContent, modelId))\n\n if (next?.role === 'user') {\n const remainingUserContent = next.content.filter((block) => {\n if (block.type !== 'tool_result')\n return true\n // Orphan/duplicate tool results should already be removed by the\n // canonical repair pass. If one reaches this adapter, do not send it\n // as an unpaired provider message.\n return false\n })\n const userMessage = piUserMessage(remainingUserContent)\n if (userMessage)\n out.push(userMessage)\n i++\n }\n continue\n }\n\n const toolResults = msg.content.filter(b => b.type === 'tool_result')\n if (toolResults.length > 0) {\n const userMsg = msg.role === 'user'\n ? piUserMessage(msg.content.filter(b => b.type !== 'tool_result'))\n : null\n if (userMsg)\n out.push(userMsg)\n continue\n }\n\n if (msg.role === 'user') {\n const userMsg = piUserMessage(msg.content)\n if (userMsg)\n out.push(userMsg)\n continue\n }\n\n out.push(piAssistantMessage(msg.content, modelId))\n }\n\n return out\n}\n\n/**\n * Map pi-ai's `StopReason` to the zidane `TurnFinishReason` union — the same\n * native-stop-reason fidelity anthropic's `mapStopReason` / openai-compat's\n * `mapOAIFinishReason` provide. Notably `length` (max-token truncation) is no\n * longer misreported as a clean `stop`. `hasToolCalls` is the fallback for\n * messages whose stop reason is missing or doesn't carry the tool-call signal.\n */\nexport function mapCodexStopReason(stopReason: string | undefined, hasToolCalls: boolean): TurnFinishReason {\n switch (stopReason) {\n case 'toolUse':\n return 'tool-calls'\n case 'length':\n return 'length'\n case 'error':\n return 'error'\n case 'stop':\n return hasToolCalls ? 'tool-calls' : 'stop'\n case 'aborted':\n return 'other'\n default:\n return hasToolCalls ? 'tool-calls' : 'stop'\n }\n}\n\nfunction fromPiAssistantMessage(message: PiAssistantMessage): SessionMessage {\n const content: SessionContentBlock[] = []\n\n for (const block of message.content) {\n if (block.type === 'text') {\n content.push({ type: 'text', text: block.text })\n }\n else if (block.type === 'thinking') {\n const out: Extract<SessionContentBlock, { type: 'thinking' }> = {\n type: 'thinking',\n text: block.thinking,\n }\n if (typeof block.thinkingSignature === 'string') {\n out.signature = block.thinkingSignature\n out.signatureProducer = 'openai'\n }\n content.push(out)\n }\n else if (block.type === 'toolCall') {\n content.push({ type: 'tool_call', id: block.id, name: block.name, input: block.arguments })\n }\n }\n\n return { role: 'assistant', content }\n}\n\nfunction extractToolCalls(message: PiAssistantMessage) {\n return message.content\n .filter(block => block.type === 'toolCall')\n .map(block => ({\n id: block.id,\n name: block.name,\n input: block.arguments as Record<string, unknown>,\n }))\n}\n\nfunction extractText(message: PiAssistantMessage): string {\n return message.content\n .filter((block): block is Extract<PiAssistantMessage['content'][number], { type: 'text' }> => block.type === 'text')\n .map(block => block.text)\n .join('')\n}\n\nfunction toTurnUsage(usage: PiUsage, finishReason: TurnFinishReason | undefined, modelId: string, catalog?: ModelCatalog): Promise<TurnUsage> {\n return fillEstimatedCost({\n input: usage.input,\n output: usage.output,\n cacheRead: usage.cacheRead || undefined,\n cacheCreation: usage.cacheWrite || undefined,\n cost: usage.cost.total || undefined,\n ...(finishReason ? { finishReason } : {}),\n modelId,\n }, 'openai', undefined, catalog)\n}\n\n/**\n * Transient pi-ai error patterns worth a loop-level retry. pi-ai already retries\n * 429 / 5xx pre-stream 3× internally (see `openai-codex-responses.js`); by the\n * time the error reaches us, that budget is exhausted. The loop-level retry\n * with its longer backoff is a second line of defense for capacity events\n * that persist past pi-ai's short retry window, plus mid-stream emissions\n * (pi-ai surfaces those as plain `Error`s with no status).\n *\n * Mirrors pi-ai's own `isRetryableError` regex so the two retry layers\n * recognize the same failure modes.\n */\nconst TRANSIENT_OPENAI_MESSAGE_RE = /rate.?limit|overloaded|service.?unavailable|upstream.?connect|connection.?refused|gateway.?time.?out|temporarily.?unavailable/i\n\n/** Numeric HTTP status codes pi-ai sometimes attaches when bubbling structured errors. */\nfunction isRetryableStatusCode(err: object): boolean {\n const status = (err as { status?: unknown }).status\n if (typeof status !== 'number')\n return false\n return isRetryableHttpStatus(status)\n}\n\n/**\n * Classify an OpenAI Codex error. pi-ai surfaces errors either as thrown `Error`s\n * (wrapping `event.error.errorMessage`) or via stream event types.\n *\n * Retryable hint is set when pi-ai's own retry budget is exhausted on a\n * transient failure — pattern-matched against `message` since pi-ai strips\n * structured fields on plain-Error throws. Set `behavior.retry.maxAttempts: 1`\n * to disable the loop-level retry.\n */\nexport function classifyOpenAIError(err: unknown): ClassifiedError | null {\n const prelude = classifyErrorPrelude(err)\n if (prelude === 'not-object')\n return null\n if (prelude === 'aborted')\n return { kind: 'aborted' }\n\n const anyErr = err as { name?: string, message?: string, code?: string, type?: string }\n\n const message = anyErr.message ?? ''\n const code = anyErr.code ?? anyErr.type\n\n if (code === 'context_length_exceeded' || matchesContextExceeded(message)) {\n return {\n kind: 'context_exceeded',\n providerCode: code ?? 'context_length_exceeded',\n message,\n }\n }\n\n if (matchesToolPairingError(message)) {\n return {\n kind: 'tool_pairing_corruption',\n providerCode: code ?? 'invalid_request_error',\n message,\n }\n }\n\n // pi-ai wraps API errors in generic `Error` — treat as provider_error when we have a message.\n if (message.length > 0) {\n const retryable = isRetryableStatusCode(anyErr) || TRANSIENT_OPENAI_MESSAGE_RE.test(message)\n return {\n kind: 'provider_error',\n providerCode: code,\n message,\n ...(retryable ? { retryable: true } : {}),\n }\n }\n\n return null\n}\n\n/**\n * A standard OpenAI API key usable against `api.openai.com` (for the exact\n * token-count endpoint), or `null` when only a Codex-OAuth credential is\n * available. Codex OAuth access tokens are JWTs (`eyJ…`) and are NOT valid\n * bearers for the standard API; a real key looks like `sk-…` (but not the\n * Anthropic `sk-ant-…` form). Checks `params.apiKey` then `OPENAI_API_KEY`.\n */\n/**\n * OpenAI's org-verification rejection for `reasoning.summary` — e.g.\n * \"Your organization must be verified to generate reasoning summaries\".\n * Matched loosely on the two load-bearing words so minor copy changes\n * don't defeat the platform-branch fallback.\n */\nfunction isReasoningSummaryVerificationError(err: unknown): boolean {\n if (!(err instanceof Error))\n return false\n return /reasoning summar/i.test(err.message) && /verif/i.test(err.message)\n}\n\nfunction pickStandardOpenAIKey(params?: OpenAIParams): string | null {\n const candidates = [params?.apiKey, process.env.OPENAI_API_KEY]\n for (const key of candidates) {\n if (typeof key === 'string' && key.startsWith('sk-') && !key.startsWith('sk-ant-'))\n return key\n }\n return null\n}\n\n/**\n * Convert canonical {@link SessionMessage}s to OpenAI Responses-API `input`\n * items for the token-count endpoint. Only the text-bearing shape is needed\n * here (the count path sends a tiny dummy message); images/tool-results are\n * down-converted to text so the count never throws on an exotic block.\n */\nfunction toResponsesInput(messages: SessionMessage[]): unknown[] {\n return messages.map((msg) => {\n const text = msg.content\n .map(b => (b.type === 'text' ? b.text : ''))\n .filter(Boolean)\n .join('\\n')\n const part = msg.role === 'assistant' ? 'output_text' : 'input_text'\n return { role: msg.role === 'assistant' ? 'assistant' : 'user', content: [{ type: part, text }] }\n })\n}\n\n/** Convert pi-ai `PiTool` specs to Responses-API function tools. */\nfunction toResponsesTools(tools: unknown[]): unknown[] {\n return tools.map((t) => {\n const tool = t as { name?: string, description?: string, parameters?: unknown }\n return {\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n }\n })\n}\n\nfunction applyPayloadOverrides(payload: unknown, options: StreamOptions, toWireToolName: (name: string) => string = name => name): unknown {\n const body = payload as Record<string, unknown>\n\n if (options.toolChoice) {\n if (options.toolChoice.type === 'tool' && options.toolChoice.name)\n body.tool_choice = { type: 'function', name: toWireToolName(options.toolChoice.name) }\n else if (options.toolChoice.type === 'required')\n body.tool_choice = 'required'\n else\n body.tool_choice = 'auto'\n }\n\n return body\n}\n\nexport function openai(params?: OpenAIParams): Provider {\n const defaultModel = params?.defaultModel || DEFAULT_MODEL\n const baseCredentials = extractRuntimeCredentials(params)\n let runtimeCredentials = baseCredentials\n ? { ...baseCredentials, ...(params?.accountId ? { accountId: params.accountId } : {}) }\n : undefined\n\n return {\n name: 'openai',\n meta: {\n defaultModel,\n isOAuth: true,\n capabilities: {\n vision: true,\n imageInToolResult: true,\n // pi-ai's Codex Responses wire has no audio/video input content type.\n audio: false,\n video: false,\n },\n },\n formatTools,\n userMessage,\n assistantMessage,\n toolResultsMessage,\n classifyError: classifyOpenAIError,\n\n async countTokens(payload, signal): Promise<number | null> {\n // The exact count endpoint (`POST /v1/responses/input_tokens`) lives on\n // the standard OpenAI API and needs a standard `OPENAI_API_KEY` bearer.\n // This provider authenticates via Codex OAuth against the Codex backend,\n // whose token is NOT valid for that endpoint — so we only attempt the\n // count when a standard key is present (env or params.apiKey that isn't\n // an OAuth/JWT token). Otherwise return null → heuristic fallback.\n const standardKey = pickStandardOpenAIKey(params)\n if (!standardKey)\n return null\n try {\n const tools = payload.tools as PiTool[]\n const nameBoundary = createToolNameBoundary(payload.messages, tools.map(tool => tool.name))\n const res = await fetch('https://api.openai.com/v1/responses/input_tokens', {\n method: 'POST',\n headers: {\n ...params?.extraHeaders,\n 'authorization': `Bearer ${standardKey}`,\n 'content-type': 'application/json',\n },\n // The Responses `input_tokens` endpoint takes the same shape as\n // `responses.create`: `instructions` (system), `input` (Responses\n // input items — NOT pi-ai messages), and function-shaped `tools`.\n body: JSON.stringify({\n model: payload.model || defaultModel,\n instructions: renderSystemForWire(payload.system),\n input: toResponsesInput(nameBoundary.messages),\n tools: toResponsesTools(tools.map(tool => ({ ...tool, name: nameBoundary.toWire(tool.name) }))),\n }),\n ...(signal ? { signal } : {}),\n })\n if (!res.ok)\n return null\n const json = (await res.json()) as { input_tokens?: unknown }\n return typeof json.input_tokens === 'number' ? json.input_tokens : null\n }\n catch {\n return null\n }\n },\n\n async stream(options: StreamOptions, callbacks: StreamCallbacks): Promise<TurnResult> {\n const modelId = options.model || defaultModel\n const apiKey = await resolveOAuthApiKey(\n {\n provider: 'openai',\n providerId: PROVIDER_ID,\n params: runtimeCredentials ? { ...params, ...runtimeCredentials } : params,\n envKey: 'OPENAI_CODEX_API_KEY',\n extraCredentialKeys: ['accountId'],\n missingError: 'No OpenAI Codex OAuth token found. Run `bun run auth --openai` first.',\n refreshError: reason => `OpenAI Codex OAuth token refresh failed. Run \\`bun run auth --openai\\` again. ${reason}`,\n },\n {\n ...callbacks,\n async onOAuthRefresh(ctx) {\n if (ctx.source === 'params') {\n runtimeCredentials = {\n access: ctx.credentials.access,\n refresh: ctx.credentials.refresh,\n expires: ctx.credentials.expires,\n ...(typeof ctx.credentials.accountId === 'string' ? { accountId: ctx.credentials.accountId } : {}),\n }\n }\n await callbacks.onOAuthRefresh?.(ctx)\n },\n },\n )\n // Backend auto-detection by credential shape. A Codex OAuth access\n // token is a JWT whose claims carry the `chatgpt_account_id` the Codex\n // backend requires per request; a standard `sk-…` platform key can\n // NEVER stream there (pi-ai fails with an opaque \"Failed to extract\n // accountId from token\") — but it's a first-class credential for the\n // standard Responses API, so route it there instead. Scoped to the\n // unambiguous `sk-` shape: exotic bearers (test harnesses, relays)\n // keep flowing to the Codex path untouched.\n const usePlatformApi = apiKey.startsWith('sk-')\n const model = resolveModel(modelId, usePlatformApi ? PLATFORM_PROVIDER_ID : PROVIDER_ID, options.modelCatalog)\n const tools = options.tools as PiTool[]\n const nameBoundary = createToolNameBoundary(options.messages, tools.map(tool => tool.name))\n // Codex's Responses API has no `cache_control` analogue, but the\n // system-prompt boundary marker is structural metadata that should\n // never reach the model. Strip it via `renderSystemForWire`; the\n // doctrine + env are collapsed into a single byte stream the model\n // sees as one logical prompt.\n const context: PiContext = {\n systemPrompt: renderSystemForWire(options.system),\n messages: toPiMessages(nameBoundary.messages, modelId),\n tools: tools.map(tool => ({ ...tool, name: nameBoundary.toWire(tool.name) })),\n }\n // OpenAI's `reasoning_effort` accepts minimal/low/medium/high/xhigh, and\n // the GPT-5.6 line adds a native `max` tier. Clamp `max` to `xhigh` on\n // older models only; `'adaptive'` degrades to no reasoning rather than\n // forwarding an unknown value the API would reject.\n const supportsMaxEffort = modelId.startsWith('gpt-5.6')\n const reasoningLevel\n = options.thinking && options.thinking !== 'off' && options.thinking !== 'adaptive'\n ? options.thinking === 'max' && !supportsMaxEffort ? 'xhigh' : options.thinking\n : undefined\n // Options are shared verbatim across both backends — the platform\n // Responses API and the Codex bridge take the same reasoning / service\n // tier knobs; only the websocket `transport` toggle is Codex-specific.\n const makeStream = (reasoningSummary: 'auto' | undefined) => {\n const streamOptions = {\n apiKey,\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n signal: options.signal,\n reasoningEffort: reasoningLevel,\n reasoningSummary,\n ...(options.modelOptions?.fast ? { serviceTier: 'priority' as const } : {}),\n ...(params?.extraHeaders ? { headers: params.extraHeaders } : {}),\n // pi-ai types the payload as `unknown`; both backends send the\n // Responses-API body shape `applyPayloadOverrides` expects.\n onPayload: (payload: unknown) => applyPayloadOverrides(payload as Record<string, unknown>, options, nameBoundary.toWire),\n }\n return usePlatformApi\n ? streamOpenAIResponses(model as unknown as PlatformModel, context, streamOptions)\n : streamOpenAICodexResponses(model, context, { ...streamOptions, transport: params?.transport })\n }\n let stream = makeStream(reasoningLevel ? 'auto' : undefined)\n\n let finalMessage: PiAssistantMessage | undefined\n let text = ''\n let thinking = ''\n\n const consume = async (s: ReturnType<typeof makeStream>) => {\n for await (const event of s) {\n if (event.type === 'text_delta') {\n text += event.delta\n callbacks.onText(event.delta)\n }\n else if (event.type === 'thinking_delta') {\n thinking += event.delta\n callbacks.onThinking?.(event.delta)\n }\n else if (event.type === 'thinking_end') {\n const delta = event.content.startsWith(thinking)\n ? event.content.slice(thinking.length)\n : (thinking ? '' : event.content)\n if (delta) {\n thinking += delta\n callbacks.onThinking?.(delta)\n }\n }\n else if (event.type === 'done') {\n finalMessage = event.message\n }\n else if (event.type === 'error') {\n throw new Error(event.error.errorMessage || (usePlatformApi ? 'OpenAI API error' : 'OpenAI Codex API error'))\n }\n }\n }\n\n try {\n await consume(stream)\n }\n catch (err) {\n // The platform Responses API rejects reasoning summaries for\n // organizations that haven't completed verification (the Codex\n // backend has no such gate). That 400 fails request validation, so\n // nothing has streamed yet — retry once without the summary rather\n // than bricking every reasoning-enabled stream for those orgs.\n const nothingStreamed = text.length === 0 && thinking.length === 0 && finalMessage === undefined\n if (usePlatformApi && reasoningLevel && nothingStreamed && isReasoningSummaryVerificationError(err)) {\n stream = makeStream(undefined)\n await consume(stream)\n }\n else {\n throw err\n }\n }\n\n finalMessage ??= await stream.result()\n text ||= extractText(finalMessage)\n\n const toolCalls = extractToolCalls(finalMessage)\n .map(call => ({ ...call, name: nameBoundary.fromWire(call.name) }))\n const assistantTurn = fromPiAssistantMessage(finalMessage)\n assistantTurn.content = assistantTurn.content.map(block =>\n block.type === 'tool_call' ? { ...block, name: nameBoundary.fromWire(block.name) } : block,\n )\n const finishReason = mapCodexStopReason(finalMessage.stopReason, toolCalls.length > 0)\n // Metadata resolves under `openai-codex` (or `openai` on the platform\n // path) while cost estimation runs under `openai` — accept a catalog\n // entry keyed either way so one entry fixes both.\n const usage = await toTurnUsage(finalMessage.usage, finishReason, modelId, aliasCatalogProviders(\n options.modelCatalog,\n [usePlatformApi ? PLATFORM_PROVIDER_ID : PROVIDER_ID, PLATFORM_PROVIDER_ID],\n ))\n\n return {\n assistantMessage: assistantTurn,\n text,\n toolCalls,\n done: toolCalls.length === 0,\n usage,\n }\n },\n }\n}\n"],"mappings":"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"}
@@ -1,8 +1,11 @@
1
1
  import { m as openaiCompat } from "../openai-compat-DY-AKxCg.js";
2
2
  //#region src/providers/openrouter.ts
3
3
  const BASE_URL = "https://openrouter.ai/api/v1";
4
- const DEFAULT_MODEL = "anthropic/claude-sonnet-4.6";
5
- const MODEL_ALIASES = { "anthropic/claude-sonnet-4-6": DEFAULT_MODEL };
4
+ const DEFAULT_MODEL = "anthropic/claude-fable-5.1";
5
+ const MODEL_ALIASES = {
6
+ "anthropic/claude-sonnet-4-6": "anthropic/claude-sonnet-4.6",
7
+ "anthropic/claude-fable-5-1": DEFAULT_MODEL
8
+ };
6
9
  function getApiKey(params) {
7
10
  if (typeof params?.apiKey === "string" && params.apiKey.length > 0) return params.apiKey;
8
11
  if (process.env.OPENROUTER_API_KEY) return process.env.OPENROUTER_API_KEY;
@@ -1 +1 @@
1
- {"version":3,"file":"openrouter.js","names":[],"sources":["../../src/providers/openrouter.ts"],"sourcesContent":["import type { Provider, ProviderCapabilities } from '.'\nimport type { PromptCacheKeyOption } from './prompt-cache-key'\nimport { openaiCompat } from './openai-compat'\n\nconst BASE_URL = 'https://openrouter.ai/api/v1'\nconst DEFAULT_MODEL = 'anthropic/claude-sonnet-4.6'\n\n// Back-compat for sessions persisted before pi-ai switched the OpenRouter\n// Sonnet slug from `-4-6` to `.4.6`. Normalized on the wire so an old persisted\n// id keeps streaming. Keep in sync with `openrouterDescriptor.modelAliases`\n// (chat layer) which mirrors this for metadata lookups — the provider layer\n// can't import the chat descriptor without a layering cycle.\nconst MODEL_ALIASES: Readonly<Record<string, string>> = {\n 'anthropic/claude-sonnet-4-6': DEFAULT_MODEL,\n}\n\nexport interface OpenRouterParams {\n apiKey?: string\n defaultModel?: string\n /**\n * Provider capability flags. OpenRouter itself is a router — whether vision,\n * native image-in-tool-result, or document/PDF input are supported depends on\n * the downstream model. Default: `{ vision: true, imageInToolResult: false,\n * documents: true }` — matches the default `anthropic/claude-sonnet-4.6` model\n * (vision + PDF capable; OpenRouter forwards the OpenAI `file` content part to\n * Anthropic/OpenAI/Gemini routes).\n *\n * Override when routing to a known-text-only model (e.g. `meta-llama/llama-3-8b-instruct`),\n * setting the relevant flags to `false`.\n */\n capabilities?: ProviderCapabilities\n /**\n * PDF-parsing engine for OpenRouter's `file-parser` plugin. Applied only when\n * a request carries a forwarded PDF/`file` part. Default `'native'` (free;\n * uses the downstream model's own file support). Set `'mistral-ocr'` for\n * scanned/image PDFs, or `'pdf-text'` for free text-only extraction. See\n * {@link OpenAICompatParams.pdfEngine}.\n */\n pdfEngine?: 'native' | 'pdf-text' | 'mistral-ocr'\n /**\n * Prompt-cache affinity key, sent as `prompt_cache_key`. Off by default.\n *\n * OpenRouter reads this field as its sticky-routing key, so enabling it pins a\n * session's turns to the provider endpoint that already holds its cache —\n * complementing the automatic stickiness it applies after a cached request.\n * See `./prompt-cache-key.ts`.\n */\n promptCacheKey?: PromptCacheKeyOption\n /**\n * Additionally send the affinity key as this HTTP header, for gateways in\n * front of OpenRouter that route on a header rather than a body field.\n */\n promptCacheKeyHeader?: string\n}\n\nfunction getApiKey(params?: OpenRouterParams): string {\n // Resolution order aligned with other providers — explicit params win, env\n // is the fallback. See cerebras.ts for the rationale.\n if (typeof params?.apiKey === 'string' && params.apiKey.length > 0)\n return params.apiKey\n\n if (process.env.OPENROUTER_API_KEY)\n return process.env.OPENROUTER_API_KEY\n\n throw new Error('No OpenRouter API key found. Pass `apiKey` or set OPENROUTER_API_KEY in your environment.')\n}\n\nexport function normalizeOpenRouterModelId(modelId: string): string {\n return MODEL_ALIASES[modelId] ?? modelId\n}\n\n/**\n * OpenRouter provider.\n *\n * Thin wrapper around {@link openaiCompat} with OpenRouter-specific defaults\n * (base URL, default model) and required attribution headers.\n */\nexport function openrouter(params?: OpenRouterParams): Provider {\n const apiKey = getApiKey(params)\n const base = openaiCompat({\n name: 'openrouter',\n apiKey,\n baseURL: BASE_URL,\n defaultModel: normalizeOpenRouterModelId(params?.defaultModel || DEFAULT_MODEL),\n extraHeaders: {\n 'HTTP-Referer': 'https://github.com/Tahul/zidane',\n 'X-Title': 'zidane',\n },\n capabilities: params?.capabilities ?? { vision: true, imageInToolResult: false, documents: true },\n // Make PDF parsing deterministic. OpenRouter routes to many models, so the\n // default must work on ANY of them: `'pdf-text'` (free, text-only\n // extraction) never 400s regardless of the downstream model's file support.\n // `'native'` would route to the model's own file handling (richer — page\n // images — for gpt-4o / Gemini / Claude) but 400s on text-only models, so\n // it's opt-in via `pdfEngine: 'native'` (or `'mistral-ocr'` for scans).\n pdfEngine: params?.pdfEngine ?? 'pdf-text',\n ...(params?.promptCacheKey !== undefined ? { promptCacheKey: params.promptCacheKey } : {}),\n ...(params?.promptCacheKeyHeader !== undefined ? { promptCacheKeyHeader: params.promptCacheKeyHeader } : {}),\n // OpenRouter honors `cache_control` markers for Anthropic + Gemini routes and\n // silently ignores them for routes that cache automatically. Safe to turn on\n // by default — the caller can still flip `behavior.cache = false` to opt out\n // without needing to re-instantiate the provider.\n cacheBreakpoints: true,\n // OpenRouter speaks the normalized `reasoning` request field and round-trips\n // structured `reasoning_details` on assistant messages. Captured into\n // `provider_reasoning` blocks and echoed back to preserve extended-reasoning\n // state across turns on the same upstream route.\n supportsReasoning: true,\n })\n\n return {\n ...base,\n async stream(options, callbacks) {\n return await base.stream({\n ...options,\n model: normalizeOpenRouterModelId(options.model),\n }, callbacks)\n },\n }\n}\n"],"mappings":";;AAIA,MAAM,WAAW;AACjB,MAAM,gBAAgB;AAOtB,MAAM,gBAAkD,EACtD,+BAA+B,cACjC;AAyCA,SAAS,UAAU,QAAmC;CAGpD,IAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,OAAO,SAAS,GAC/D,OAAO,OAAO;CAEhB,IAAI,QAAQ,IAAI,oBACd,OAAO,QAAQ,IAAI;CAErB,MAAM,IAAI,MAAM,2FAA2F;AAC7G;AAEA,SAAgB,2BAA2B,SAAyB;CAClE,OAAO,cAAc,YAAY;AACnC;;;;;;;AAQA,SAAgB,WAAW,QAAqC;CAE9D,MAAM,OAAO,aAAa;EACxB,MAAM;EACN,QAHa,UAAU,MAGlB;EACL,SAAS;EACT,cAAc,2BAA2B,QAAQ,gBAAgB,aAAa;EAC9E,cAAc;GACZ,gBAAgB;GAChB,WAAW;EACb;EACA,cAAc,QAAQ,gBAAgB;GAAE,QAAQ;GAAM,mBAAmB;GAAO,WAAW;EAAK;EAOhG,WAAW,QAAQ,aAAa;EAChC,GAAI,QAAQ,mBAAmB,KAAA,IAAY,EAAE,gBAAgB,OAAO,eAAe,IAAI,CAAC;EACxF,GAAI,QAAQ,yBAAyB,KAAA,IAAY,EAAE,sBAAsB,OAAO,qBAAqB,IAAI,CAAC;EAK1G,kBAAkB;EAKlB,mBAAmB;CACrB,CAAC;CAED,OAAO;EACL,GAAG;EACH,MAAM,OAAO,SAAS,WAAW;GAC/B,OAAO,MAAM,KAAK,OAAO;IACvB,GAAG;IACH,OAAO,2BAA2B,QAAQ,KAAK;GACjD,GAAG,SAAS;EACd;CACF;AACF"}
1
+ {"version":3,"file":"openrouter.js","names":[],"sources":["../../src/providers/openrouter.ts"],"sourcesContent":["import type { Provider, ProviderCapabilities } from '.'\nimport type { PromptCacheKeyOption } from './prompt-cache-key'\nimport { openaiCompat } from './openai-compat'\n\nconst BASE_URL = 'https://openrouter.ai/api/v1'\nconst DEFAULT_MODEL = 'anthropic/claude-fable-5.1'\n\n// Back-compat for sessions persisted before pi-ai switched the OpenRouter\n// Sonnet slug from `-4-6` to `.4.6`. Normalized on the wire so an old persisted\n// id keeps streaming. Keep in sync with `openrouterDescriptor.modelAliases`\n// (chat layer) which mirrors this for metadata lookups — the provider layer\n// can't import the chat descriptor without a layering cycle.\nconst MODEL_ALIASES: Readonly<Record<string, string>> = {\n 'anthropic/claude-sonnet-4-6': 'anthropic/claude-sonnet-4.6',\n 'anthropic/claude-fable-5-1': DEFAULT_MODEL,\n}\n\nexport interface OpenRouterParams {\n apiKey?: string\n defaultModel?: string\n /**\n * Provider capability flags. OpenRouter itself is a router — whether vision,\n * native image-in-tool-result, or document/PDF input are supported depends on\n * the downstream model. Default: `{ vision: true, imageInToolResult: false,\n * documents: true }` — matches the default `anthropic/claude-sonnet-4.6` model\n * (vision + PDF capable; OpenRouter forwards the OpenAI `file` content part to\n * Anthropic/OpenAI/Gemini routes).\n *\n * Override when routing to a known-text-only model (e.g. `meta-llama/llama-3-8b-instruct`),\n * setting the relevant flags to `false`.\n */\n capabilities?: ProviderCapabilities\n /**\n * PDF-parsing engine for OpenRouter's `file-parser` plugin. Applied only when\n * a request carries a forwarded PDF/`file` part. Default `'native'` (free;\n * uses the downstream model's own file support). Set `'mistral-ocr'` for\n * scanned/image PDFs, or `'pdf-text'` for free text-only extraction. See\n * {@link OpenAICompatParams.pdfEngine}.\n */\n pdfEngine?: 'native' | 'pdf-text' | 'mistral-ocr'\n /**\n * Prompt-cache affinity key, sent as `prompt_cache_key`. Off by default.\n *\n * OpenRouter reads this field as its sticky-routing key, so enabling it pins a\n * session's turns to the provider endpoint that already holds its cache —\n * complementing the automatic stickiness it applies after a cached request.\n * See `./prompt-cache-key.ts`.\n */\n promptCacheKey?: PromptCacheKeyOption\n /**\n * Additionally send the affinity key as this HTTP header, for gateways in\n * front of OpenRouter that route on a header rather than a body field.\n */\n promptCacheKeyHeader?: string\n}\n\nfunction getApiKey(params?: OpenRouterParams): string {\n // Resolution order aligned with other providers — explicit params win, env\n // is the fallback. See cerebras.ts for the rationale.\n if (typeof params?.apiKey === 'string' && params.apiKey.length > 0)\n return params.apiKey\n\n if (process.env.OPENROUTER_API_KEY)\n return process.env.OPENROUTER_API_KEY\n\n throw new Error('No OpenRouter API key found. Pass `apiKey` or set OPENROUTER_API_KEY in your environment.')\n}\n\nexport function normalizeOpenRouterModelId(modelId: string): string {\n return MODEL_ALIASES[modelId] ?? modelId\n}\n\n/**\n * OpenRouter provider.\n *\n * Thin wrapper around {@link openaiCompat} with OpenRouter-specific defaults\n * (base URL, default model) and required attribution headers.\n */\nexport function openrouter(params?: OpenRouterParams): Provider {\n const apiKey = getApiKey(params)\n const base = openaiCompat({\n name: 'openrouter',\n apiKey,\n baseURL: BASE_URL,\n defaultModel: normalizeOpenRouterModelId(params?.defaultModel || DEFAULT_MODEL),\n extraHeaders: {\n 'HTTP-Referer': 'https://github.com/Tahul/zidane',\n 'X-Title': 'zidane',\n },\n capabilities: params?.capabilities ?? { vision: true, imageInToolResult: false, documents: true },\n // Make PDF parsing deterministic. OpenRouter routes to many models, so the\n // default must work on ANY of them: `'pdf-text'` (free, text-only\n // extraction) never 400s regardless of the downstream model's file support.\n // `'native'` would route to the model's own file handling (richer — page\n // images — for gpt-4o / Gemini / Claude) but 400s on text-only models, so\n // it's opt-in via `pdfEngine: 'native'` (or `'mistral-ocr'` for scans).\n pdfEngine: params?.pdfEngine ?? 'pdf-text',\n ...(params?.promptCacheKey !== undefined ? { promptCacheKey: params.promptCacheKey } : {}),\n ...(params?.promptCacheKeyHeader !== undefined ? { promptCacheKeyHeader: params.promptCacheKeyHeader } : {}),\n // OpenRouter honors `cache_control` markers for Anthropic + Gemini routes and\n // silently ignores them for routes that cache automatically. Safe to turn on\n // by default — the caller can still flip `behavior.cache = false` to opt out\n // without needing to re-instantiate the provider.\n cacheBreakpoints: true,\n // OpenRouter speaks the normalized `reasoning` request field and round-trips\n // structured `reasoning_details` on assistant messages. Captured into\n // `provider_reasoning` blocks and echoed back to preserve extended-reasoning\n // state across turns on the same upstream route.\n supportsReasoning: true,\n })\n\n return {\n ...base,\n async stream(options, callbacks) {\n return await base.stream({\n ...options,\n model: normalizeOpenRouterModelId(options.model),\n }, callbacks)\n },\n }\n}\n"],"mappings":";;AAIA,MAAM,WAAW;AACjB,MAAM,gBAAgB;AAOtB,MAAM,gBAAkD;CACtD,+BAA+B;CAC/B,8BAA8B;AAChC;AAyCA,SAAS,UAAU,QAAmC;CAGpD,IAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,OAAO,SAAS,GAC/D,OAAO,OAAO;CAEhB,IAAI,QAAQ,IAAI,oBACd,OAAO,QAAQ,IAAI;CAErB,MAAM,IAAI,MAAM,2FAA2F;AAC7G;AAEA,SAAgB,2BAA2B,SAAyB;CAClE,OAAO,cAAc,YAAY;AACnC;;;;;;;AAQA,SAAgB,WAAW,QAAqC;CAE9D,MAAM,OAAO,aAAa;EACxB,MAAM;EACN,QAHa,UAAU,MAGlB;EACL,SAAS;EACT,cAAc,2BAA2B,QAAQ,gBAAgB,aAAa;EAC9E,cAAc;GACZ,gBAAgB;GAChB,WAAW;EACb;EACA,cAAc,QAAQ,gBAAgB;GAAE,QAAQ;GAAM,mBAAmB;GAAO,WAAW;EAAK;EAOhG,WAAW,QAAQ,aAAa;EAChC,GAAI,QAAQ,mBAAmB,KAAA,IAAY,EAAE,gBAAgB,OAAO,eAAe,IAAI,CAAC;EACxF,GAAI,QAAQ,yBAAyB,KAAA,IAAY,EAAE,sBAAsB,OAAO,qBAAqB,IAAI,CAAC;EAK1G,kBAAkB;EAKlB,mBAAmB;CACrB,CAAC;CAED,OAAO;EACL,GAAG;EACH,MAAM,OAAO,SAAS,WAAW;GAC/B,OAAO,MAAM,KAAK,OAAO;IACvB,GAAG;IACH,OAAO,2BAA2B,QAAQ,KAAK;GACjD,GAAG,SAAS;EACd;CACF;AACF"}
@@ -2,7 +2,7 @@ import { m as openaiCompat } from "../openai-compat-DY-AKxCg.js";
2
2
  import { i as isOAuthAccessToken, r as extractRuntimeCredentials, s as resolveOAuthApiKey } from "../oauth-C4GvY_p1.js";
3
3
  import { r as xaiBaseUrl, t as XAI_OAUTH_PROVIDER_ID } from "../xai-DILdFGzN.js";
4
4
  //#region src/providers/xai.ts
5
- const DEFAULT_MODEL = "grok-4.3";
5
+ const DEFAULT_MODEL = "grok-4.6";
6
6
  function xai(params) {
7
7
  const defaultModel = params?.defaultModel || DEFAULT_MODEL;
8
8
  const baseURL = xaiBaseUrl();
@@ -1 +1 @@
1
- {"version":3,"file":"xai.js","names":[],"sources":["../../src/providers/xai.ts"],"sourcesContent":["import type { Provider, ProviderCapabilities, StreamCallbacks, StreamOptions, TurnResult } from '.'\nimport type { OAuthParams } from './oauth'\nimport { XAI_OAUTH_PROVIDER_ID, xaiBaseUrl } from '../chat/oauth-page/xai'\nimport { extractRuntimeCredentials, isOAuthAccessToken, resolveOAuthApiKey } from './oauth'\nimport { openaiCompat } from './openai-compat'\n\nconst DEFAULT_MODEL = 'grok-4.3'\n\n/**\n * xAI Grok provider — supports both a static API key (`XAI_API_KEY`) and the\n * SuperGrok / X Premium+ OAuth subscription.\n *\n * Both auth modes hit the same OpenAI-compatible endpoint (`https://api.x.ai/v1`),\n * so the wire handling is entirely {@link openaiCompat}. The only difference is\n * the bearer:\n *\n * - **API key**: `XAI_API_KEY` (or `params.apiKey`) → used verbatim.\n * - **OAuth**: no static key → {@link resolveOAuthApiKey} resolves + lazily\n * refreshes the stored `xai-oauth` token on every turn.\n *\n * `resolveOAuthApiKey` unifies the two: a real API key in `XAI_API_KEY` is\n * returned as-is, while an OAuth access token (or no env value at all) falls\n * through to the refreshable stored credentials. So this single factory serves\n * both paths without branching at construction time.\n */\nexport interface XaiParams extends OAuthParams {\n defaultModel?: string\n /**\n * Provider capability flags. Grok models are vision-capable; default\n * `{ vision: true, imageInToolResult: false }`. Override for text-only\n * deployments.\n */\n capabilities?: ProviderCapabilities\n /** Extra HTTP headers sent on every request. */\n extraHeaders?: Record<string, string>\n}\n\nexport function xai(params?: XaiParams): Provider {\n const defaultModel = params?.defaultModel || DEFAULT_MODEL\n const baseURL = xaiBaseUrl()\n const capabilities: ProviderCapabilities = params?.capabilities ?? {\n vision: true,\n imageInToolResult: false,\n }\n const baseCredentials = extractRuntimeCredentials(params)\n let runtimeCredentials = baseCredentials\n\n // Factory-time snapshot for the static `meta.isOAuth` display flag only (the\n // terminal header's `oauth`/`key` badge). The real bearer resolves per turn\n // in `stream`, so this is best-effort, mirroring anthropic's pattern. It's\n // OAuth unless a genuine static API key is present: a `xai-…` key → key mode;\n // runtime OAuth creds, a JWT access token, or no static key (stored creds\n // used) → oauth mode.\n const staticKey = params?.apiKey ?? process.env.XAI_API_KEY\n const isOAuth = baseCredentials !== undefined || !staticKey || isOAuthAccessToken(staticKey)\n\n /**\n * Build the openai-compat delegate for a resolved bearer. Cheap — the factory\n * only wires closures — so we re-create it per turn with the freshly resolved\n * (possibly refreshed) OAuth token rather than baking a stale key in once.\n */\n function delegateFor(apiKey: string): Provider {\n return openaiCompat({\n name: 'xai',\n apiKey,\n baseURL,\n defaultModel,\n capabilities,\n extraHeaders: params?.extraHeaders,\n })\n }\n\n // A delegate built with a placeholder key supplies the non-stream methods\n // (formatTools, userMessage, message builders, classifyError). Only `stream`\n // resolves the real bearer, so the placeholder never reaches the wire.\n const skeleton = delegateFor('placeholder-resolved-at-stream')\n\n return {\n ...skeleton,\n name: 'xai',\n // Reuse the delegate's normalized capability flags (openaiCompat fills the\n // audio/video/documents defaults) so the advertised shape matches the wire.\n meta: { ...skeleton.meta, defaultModel, isOAuth },\n\n async stream(options: StreamOptions, callbacks: StreamCallbacks): Promise<TurnResult> {\n const apiKey = await resolveOAuthApiKey(\n {\n provider: 'xai',\n providerId: XAI_OAUTH_PROVIDER_ID,\n params: runtimeCredentials ? { ...params, ...runtimeCredentials } : params,\n envKey: 'XAI_API_KEY',\n missingError: 'No xAI credentials found. Set XAI_API_KEY or run `bun run auth --xai` first.',\n refreshError: reason => `xAI OAuth token refresh failed. Run \\`bun run auth --xai\\` again. ${reason}`,\n },\n {\n ...callbacks,\n async onOAuthRefresh(ctx) {\n // Keep param-sourced credentials current so a long-lived provider\n // instance reuses the rotated token instead of re-refreshing.\n if (ctx.source === 'params') {\n runtimeCredentials = {\n access: ctx.credentials.access,\n refresh: ctx.credentials.refresh,\n expires: ctx.credentials.expires,\n }\n }\n await callbacks.onOAuthRefresh?.(ctx)\n },\n },\n )\n\n return delegateFor(apiKey).stream(options, callbacks)\n },\n }\n}\n"],"mappings":";;;;AAMA,MAAM,gBAAgB;AA+BtB,SAAgB,IAAI,QAA8B;CAChD,MAAM,eAAe,QAAQ,gBAAgB;CAC7C,MAAM,UAAU,WAAW;CAC3B,MAAM,eAAqC,QAAQ,gBAAgB;EACjE,QAAQ;EACR,mBAAmB;CACrB;CACA,MAAM,kBAAkB,0BAA0B,MAAM;CACxD,IAAI,qBAAqB;CAQzB,MAAM,YAAY,QAAQ,UAAU,QAAQ,IAAI;CAChD,MAAM,UAAU,oBAAoB,KAAA,KAAa,CAAC,aAAa,mBAAmB,SAAS;;;;;;CAO3F,SAAS,YAAY,QAA0B;EAC7C,OAAO,aAAa;GAClB,MAAM;GACN;GACA;GACA;GACA;GACA,cAAc,QAAQ;EACxB,CAAC;CACH;CAKA,MAAM,WAAW,YAAY,gCAAgC;CAE7D,OAAO;EACL,GAAG;EACH,MAAM;EAGN,MAAM;GAAE,GAAG,SAAS;GAAM;GAAc;EAAQ;EAEhD,MAAM,OAAO,SAAwB,WAAiD;GA2BpF,OAAO,YAAY,MA1BE,mBACnB;IACE,UAAU;IACV,YAAY;IACZ,QAAQ,qBAAqB;KAAE,GAAG;KAAQ,GAAG;IAAmB,IAAI;IACpE,QAAQ;IACR,cAAc;IACd,eAAc,WAAU,qEAAqE;GAC/F,GACA;IACE,GAAG;IACH,MAAM,eAAe,KAAK;KAGxB,IAAI,IAAI,WAAW,UACjB,qBAAqB;MACnB,QAAQ,IAAI,YAAY;MACxB,SAAS,IAAI,YAAY;MACzB,SAAS,IAAI,YAAY;KAC3B;KAEF,MAAM,UAAU,iBAAiB,GAAG;IACtC;GACF,CACF,CAEyB,CAAC,CAAC,OAAO,SAAS,SAAS;EACtD;CACF;AACF"}
1
+ {"version":3,"file":"xai.js","names":[],"sources":["../../src/providers/xai.ts"],"sourcesContent":["import type { Provider, ProviderCapabilities, StreamCallbacks, StreamOptions, TurnResult } from '.'\nimport type { OAuthParams } from './oauth'\nimport { XAI_OAUTH_PROVIDER_ID, xaiBaseUrl } from '../chat/oauth-page/xai'\nimport { extractRuntimeCredentials, isOAuthAccessToken, resolveOAuthApiKey } from './oauth'\nimport { openaiCompat } from './openai-compat'\n\nconst DEFAULT_MODEL = 'grok-4.6'\n\n/**\n * xAI Grok provider — supports both a static API key (`XAI_API_KEY`) and the\n * SuperGrok / X Premium+ OAuth subscription.\n *\n * Both auth modes hit the same OpenAI-compatible endpoint (`https://api.x.ai/v1`),\n * so the wire handling is entirely {@link openaiCompat}. The only difference is\n * the bearer:\n *\n * - **API key**: `XAI_API_KEY` (or `params.apiKey`) → used verbatim.\n * - **OAuth**: no static key → {@link resolveOAuthApiKey} resolves + lazily\n * refreshes the stored `xai-oauth` token on every turn.\n *\n * `resolveOAuthApiKey` unifies the two: a real API key in `XAI_API_KEY` is\n * returned as-is, while an OAuth access token (or no env value at all) falls\n * through to the refreshable stored credentials. So this single factory serves\n * both paths without branching at construction time.\n */\nexport interface XaiParams extends OAuthParams {\n defaultModel?: string\n /**\n * Provider capability flags. Grok models are vision-capable; default\n * `{ vision: true, imageInToolResult: false }`. Override for text-only\n * deployments.\n */\n capabilities?: ProviderCapabilities\n /** Extra HTTP headers sent on every request. */\n extraHeaders?: Record<string, string>\n}\n\nexport function xai(params?: XaiParams): Provider {\n const defaultModel = params?.defaultModel || DEFAULT_MODEL\n const baseURL = xaiBaseUrl()\n const capabilities: ProviderCapabilities = params?.capabilities ?? {\n vision: true,\n imageInToolResult: false,\n }\n const baseCredentials = extractRuntimeCredentials(params)\n let runtimeCredentials = baseCredentials\n\n // Factory-time snapshot for the static `meta.isOAuth` display flag only (the\n // terminal header's `oauth`/`key` badge). The real bearer resolves per turn\n // in `stream`, so this is best-effort, mirroring anthropic's pattern. It's\n // OAuth unless a genuine static API key is present: a `xai-…` key → key mode;\n // runtime OAuth creds, a JWT access token, or no static key (stored creds\n // used) → oauth mode.\n const staticKey = params?.apiKey ?? process.env.XAI_API_KEY\n const isOAuth = baseCredentials !== undefined || !staticKey || isOAuthAccessToken(staticKey)\n\n /**\n * Build the openai-compat delegate for a resolved bearer. Cheap — the factory\n * only wires closures — so we re-create it per turn with the freshly resolved\n * (possibly refreshed) OAuth token rather than baking a stale key in once.\n */\n function delegateFor(apiKey: string): Provider {\n return openaiCompat({\n name: 'xai',\n apiKey,\n baseURL,\n defaultModel,\n capabilities,\n extraHeaders: params?.extraHeaders,\n })\n }\n\n // A delegate built with a placeholder key supplies the non-stream methods\n // (formatTools, userMessage, message builders, classifyError). Only `stream`\n // resolves the real bearer, so the placeholder never reaches the wire.\n const skeleton = delegateFor('placeholder-resolved-at-stream')\n\n return {\n ...skeleton,\n name: 'xai',\n // Reuse the delegate's normalized capability flags (openaiCompat fills the\n // audio/video/documents defaults) so the advertised shape matches the wire.\n meta: { ...skeleton.meta, defaultModel, isOAuth },\n\n async stream(options: StreamOptions, callbacks: StreamCallbacks): Promise<TurnResult> {\n const apiKey = await resolveOAuthApiKey(\n {\n provider: 'xai',\n providerId: XAI_OAUTH_PROVIDER_ID,\n params: runtimeCredentials ? { ...params, ...runtimeCredentials } : params,\n envKey: 'XAI_API_KEY',\n missingError: 'No xAI credentials found. Set XAI_API_KEY or run `bun run auth --xai` first.',\n refreshError: reason => `xAI OAuth token refresh failed. Run \\`bun run auth --xai\\` again. ${reason}`,\n },\n {\n ...callbacks,\n async onOAuthRefresh(ctx) {\n // Keep param-sourced credentials current so a long-lived provider\n // instance reuses the rotated token instead of re-refreshing.\n if (ctx.source === 'params') {\n runtimeCredentials = {\n access: ctx.credentials.access,\n refresh: ctx.credentials.refresh,\n expires: ctx.credentials.expires,\n }\n }\n await callbacks.onOAuthRefresh?.(ctx)\n },\n },\n )\n\n return delegateFor(apiKey).stream(options, callbacks)\n },\n }\n}\n"],"mappings":";;;;AAMA,MAAM,gBAAgB;AA+BtB,SAAgB,IAAI,QAA8B;CAChD,MAAM,eAAe,QAAQ,gBAAgB;CAC7C,MAAM,UAAU,WAAW;CAC3B,MAAM,eAAqC,QAAQ,gBAAgB;EACjE,QAAQ;EACR,mBAAmB;CACrB;CACA,MAAM,kBAAkB,0BAA0B,MAAM;CACxD,IAAI,qBAAqB;CAQzB,MAAM,YAAY,QAAQ,UAAU,QAAQ,IAAI;CAChD,MAAM,UAAU,oBAAoB,KAAA,KAAa,CAAC,aAAa,mBAAmB,SAAS;;;;;;CAO3F,SAAS,YAAY,QAA0B;EAC7C,OAAO,aAAa;GAClB,MAAM;GACN;GACA;GACA;GACA;GACA,cAAc,QAAQ;EACxB,CAAC;CACH;CAKA,MAAM,WAAW,YAAY,gCAAgC;CAE7D,OAAO;EACL,GAAG;EACH,MAAM;EAGN,MAAM;GAAE,GAAG,SAAS;GAAM;GAAc;EAAQ;EAEhD,MAAM,OAAO,SAAwB,WAAiD;GA2BpF,OAAO,YAAY,MA1BE,mBACnB;IACE,UAAU;IACV,YAAY;IACZ,QAAQ,qBAAqB;KAAE,GAAG;KAAQ,GAAG;IAAmB,IAAI;IACpE,QAAQ;IACR,cAAc;IACd,eAAc,WAAU,qEAAqE;GAC/F,GACA;IACE,GAAG;IACH,MAAM,eAAe,KAAK;KAGxB,IAAI,IAAI,WAAW,UACjB,qBAAqB;MACnB,QAAQ,IAAI,YAAY;MACxB,SAAS,IAAI,YAAY;MACzB,SAAS,IAAI,YAAY;KAC3B;KAEF,MAAM,UAAU,iBAAiB,GAAG;IACtC;GACF,CACF,CAEyB,CAAC,CAAC,OAAO,SAAS,SAAS;EACtD;CACF;AACF"}
@@ -119,7 +119,7 @@ function zai(params) {
119
119
  name: "zai",
120
120
  apiKey: getApiKey(params),
121
121
  baseURL: params?.baseURL || BASE_URL,
122
- defaultModel: params?.defaultModel || "glm-4.7-flash",
122
+ defaultModel: params?.defaultModel || "glm-5.3-flash",
123
123
  capabilities: params?.capabilities ?? {
124
124
  vision: false,
125
125
  imageInToolResult: false
@@ -134,4 +134,4 @@ function zai(params) {
134
134
  //#endregion
135
135
  export { cursor as a, planMistralReasoning as i, zai as n, mistral as r, planZaiReasoning as t };
136
136
 
137
- //# sourceMappingURL=providers-DX91jwir.js.map
137
+ //# sourceMappingURL=providers-BYgyPb15.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"providers-DX91jwir.js","names":["BASE_URL","getApiKey"],"sources":["../src/providers/cursor.ts","../src/providers/mistral.ts","../src/providers/zai.ts"],"sourcesContent":["import type { Provider, StreamCallbacks } from '.'\nimport { CURSOR_OAUTH_PROVIDER_ID } from '../chat/oauth-page/cursor'\nimport { resolveOAuthApiKey } from './oauth'\nimport { openaiCompat } from './openai-compat'\n\n/**\n * Cursor provider (OAuth).\n *\n * Auth is fully wired: `bun run auth --cursor` (or the TUI wizard) logs in via\n * Cursor's poll-based PKCE flow and persists credentials; this provider resolves\n * + refreshes the OAuth token on every turn via {@link resolveOAuthApiKey}.\n *\n * Inference is **not** wired yet. Cursor does not expose an OpenAI-compatible\n * HTTP endpoint — it speaks a protobuf/Connect agent protocol over HTTP/2 to\n * `api2.cursor.sh`. Implementing `stream()` requires porting that transport\n * (generated protobuf schemas + an HTTP/2 streaming client) in-process, which\n * is intentionally left as a follow-up. Until then `stream()` resolves the\n * token (proving auth works) and then throws a clear, actionable error.\n *\n * The `openaiCompat` base supplies the message-format helpers (`formatTools`,\n * `userMessage`, `toolResultsMessage`, …) so the provider satisfies the full\n * `Provider` contract and slots into the registry; only `stream` is overridden.\n */\nexport interface CursorParams {\n /** Bypass OAuth resolution with an explicit token (mainly for tests). */\n apiKey?: string\n defaultModel?: string\n /**\n * Extra HTTP headers for the eventual Cursor transport. Accepted for API\n * parity with the other providers and forwarded to the openai-compat base,\n * but inert until Cursor inference is implemented (the `stream` override\n * currently throws before any request is made).\n */\n extraHeaders?: Record<string, string>\n}\n\nconst DEFAULT_MODEL = 'claude-4.6-sonnet'\n\nconst NOT_IMPLEMENTED_MESSAGE\n = 'Cursor OAuth login works, but inference over Cursor is not implemented yet. '\n + 'Cursor uses a protobuf/HTTP-2 agent protocol (not an OpenAI-compatible API), '\n + 'so the streaming transport still needs to be ported. Use another provider for now.'\n\nexport function cursor(params?: CursorParams): Provider {\n const defaultModel = params?.defaultModel || DEFAULT_MODEL\n\n // Base only provides the format/serialization methods; `baseURL` is never\n // hit because we override `stream` below.\n const base = openaiCompat({\n name: 'cursor',\n apiKey: params?.apiKey ?? 'oauth',\n baseURL: 'https://api2.cursor.sh',\n defaultModel,\n capabilities: { vision: true, imageInToolResult: false },\n extraHeaders: params?.extraHeaders,\n })\n\n return {\n ...base,\n meta: { ...base.meta, defaultModel },\n async stream(options, callbacks: StreamCallbacks) {\n // Resolve (and lazily refresh) the OAuth token so auth failures surface\n // here with the right remediation, distinct from the not-implemented path.\n await resolveOAuthApiKey(\n {\n provider: 'cursor',\n providerId: CURSOR_OAUTH_PROVIDER_ID,\n params,\n missingError: 'No Cursor credentials found. Run `bun run auth --cursor` first.',\n refreshError: reason => `Cursor OAuth token refresh failed. Run \\`bun run auth --cursor\\` again. ${reason}`,\n },\n callbacks,\n )\n void options\n throw new Error(NOT_IMPLEMENTED_MESSAGE)\n },\n }\n}\n","import type { Provider, ProviderCapabilities } from '.'\nimport type { ThinkingLevel } from '../types'\nimport { openaiCompat } from './openai-compat'\n\n// Mistral La Plateforme — OpenAI-compatible `/v1/chat/completions`. Model ids\n// are plain slugs (`mistral-medium-latest`, `devstral-2512`, …). Reasoning is\n// NOT a separate wire field here: with `reasoning_effort: 'high'` the response\n// `content` becomes a list of `thinking` / `text` chunks (handled by\n// `openaiCompat`'s content-chunk path), and with `'none'` it stays a plain\n// string. See https://docs.mistral.ai/studio-api/conversations/reasoning.\nconst BASE_URL = 'https://api.mistral.ai/v1'\n\nexport interface MistralParams {\n apiKey?: string\n defaultModel?: string\n /**\n * Override the base URL — e.g. point at a regional endpoint or a gateway in\n * front of La Plateforme.\n */\n baseURL?: string\n /**\n * Provider capability flags. The `mistral-medium` / `mistral-large` /\n * `pixtral` families accept `image_url` parts — default:\n * `{ vision: true, imageInToolResult: false }`. Set `vision: false` when\n * pinning a text-only model (`codestral`, `devstral`, `ministral`).\n */\n capabilities?: ProviderCapabilities\n /**\n * Extra HTTP headers sent on every request. Forwarded to the underlying\n * {@link openaiCompat} client.\n */\n extraHeaders?: Record<string, string>\n}\n\nfunction getApiKey(params?: MistralParams): string {\n // Resolution order aligned with the other providers: explicit > env > fail.\n if (typeof params?.apiKey === 'string' && params.apiKey.length > 0)\n return params.apiKey\n\n if (process.env.MISTRAL_API_KEY)\n return process.env.MISTRAL_API_KEY\n\n throw new Error('No Mistral API key found. Pass `apiKey` or set MISTRAL_API_KEY in your environment.')\n}\n\n/**\n * Model ids that accept `reasoning_effort`. Mistral rejects unknown request\n * fields on some routes, so the knob is sent only for the families that\n * document it (`mistral-small-*`, `mistral-medium-3.5` / `-2604`, and the\n * deprecated-but-live `magistral-*`); `codestral` / `devstral` / `ministral`\n * requests stay byte-identical to what they were without this planner.\n */\nconst REASONING_MODELS = /^(?:magistral-|mistral-small-|mistral-medium-(?:3\\.5|2604))/\n\n/**\n * Map zidane's thinking level to Mistral's `reasoning_effort`. Only the two\n * documented values are emitted — `'none'` (thinking chunk omitted, `content`\n * stays a plain string) and `'high'` (full thinking chunk). `adaptive` sends\n * nothing so the model's own default applies.\n *\n * Exported for tests.\n */\nexport function planMistralReasoning(\n ctx: { model: string, thinking?: ThinkingLevel },\n): Record<string, unknown> | undefined {\n if (!REASONING_MODELS.test(ctx.model))\n return undefined\n if (ctx.thinking === undefined || ctx.thinking === 'adaptive')\n return undefined\n return { reasoning_effort: ctx.thinking === 'off' ? 'none' : 'high' }\n}\n\n/**\n * Mistral provider (La Plateforme).\n *\n * Thin wrapper around {@link openaiCompat} with Mistral-specific defaults\n * (base URL, default model, `reasoning_effort` planner). `supportsReasoning`\n * stays off: Mistral carries thinking inside `content` chunks rather than a\n * `reasoning_content` field, so there is nothing to echo back upstream —\n * thinking streams to the UI and is dropped from subsequent requests.\n */\nexport function mistral(params?: MistralParams): Provider {\n return openaiCompat({\n name: 'mistral',\n apiKey: getApiKey(params),\n baseURL: params?.baseURL || BASE_URL,\n defaultModel: params?.defaultModel || 'mistral-medium-latest',\n capabilities: params?.capabilities ?? { vision: true, imageInToolResult: false },\n planReasoningRequest: planMistralReasoning,\n extraHeaders: params?.extraHeaders,\n })\n}\n","import type { Provider, ProviderCapabilities } from '.'\nimport type { ThinkingLevel } from '../types'\nimport { openaiCompat } from './openai-compat'\n\n// Z.ai (Zhipu) GLM Open Platform — OpenAI-compatible endpoint. Model ids are\n// plain lowercase slugs (`glm-4.7`, `glm-4.7-flash`, …), reasoning streams in\n// the `reasoning_content` delta field, and thinking is toggled via an\n// `extra_body.thinking` object rather than a `reasoning_effort` string.\n// See https://docs.z.ai/guides/develop/http/introduction and\n// https://docs.z.ai/guides/capabilities/thinking-mode.\nconst BASE_URL = 'https://api.z.ai/api/paas/v4'\n\nexport interface ZaiParams {\n apiKey?: string\n defaultModel?: string\n /**\n * Override the base URL — e.g. point at the dedicated Coding Plan endpoint\n * instead of the standard API endpoint.\n */\n baseURL?: string\n /**\n * Provider capability flags. GLM text models are text-only — default:\n * `{ vision: false, imageInToolResult: false }`.\n */\n capabilities?: ProviderCapabilities\n /**\n * Extra HTTP headers sent on every request. Forwarded to the underlying\n * {@link openaiCompat} client.\n */\n extraHeaders?: Record<string, string>\n}\n\nfunction getApiKey(params?: ZaiParams): string {\n // Resolution order aligned with the other providers: explicit > env > fail.\n if (typeof params?.apiKey === 'string' && params.apiKey.length > 0)\n return params.apiKey\n\n if (process.env.ZAI_API_KEY)\n return process.env.ZAI_API_KEY\n\n throw new Error('No Z.ai API key found. Pass `apiKey` or set ZAI_API_KEY in your environment.')\n}\n\n/**\n * Map zidane's thinking level to Z.ai's `extra_body.thinking` request shape.\n * GLM has no depth knob — thinking is a binary switch — so any non-`off`\n * level enables it and `off` explicitly disables it (GLM-5.x/4.7 think by\n * default, so a silent omission would not turn thinking off).\n *\n * Exported for tests.\n */\nexport function planZaiReasoning(\n ctx: { thinking?: ThinkingLevel },\n): Record<string, unknown> {\n const enabled = !!ctx.thinking && ctx.thinking !== 'off'\n return { thinking: { type: enabled ? 'enabled' : 'disabled' } }\n}\n\n/**\n * Z.ai (Zhipu) GLM provider.\n *\n * Thin wrapper around {@link openaiCompat} pinned to the `reasoning_content`\n * round-trip mode: thinking arrives in the `reasoning_content` delta field and\n * is echoed back on assistant messages under the same name, keeping GLM's\n * interleaved/preserved thinking coherent across tool calls.\n */\nexport function zai(params?: ZaiParams): Provider {\n return openaiCompat({\n name: 'zai',\n apiKey: getApiKey(params),\n baseURL: params?.baseURL || BASE_URL,\n defaultModel: params?.defaultModel || 'glm-4.7-flash',\n capabilities: params?.capabilities ?? { vision: false, imageInToolResult: false },\n supportsReasoning: true,\n reasoningMode: 'reasoning_content',\n reasoningContentField: 'reasoning_content',\n planReasoningRequest: planZaiReasoning,\n extraHeaders: params?.extraHeaders,\n })\n}\n"],"mappings":";;;;AAoCA,MAAM,gBAAgB;AAEtB,MAAM,0BACF;AAIJ,SAAgB,OAAO,QAAiC;CACtD,MAAM,eAAe,QAAQ,gBAAgB;CAI7C,MAAM,OAAO,aAAa;EACxB,MAAM;EACN,QAAQ,QAAQ,UAAU;EAC1B,SAAS;EACT;EACA,cAAc;GAAE,QAAQ;GAAM,mBAAmB;EAAM;EACvD,cAAc,QAAQ;CACxB,CAAC;CAED,OAAO;EACL,GAAG;EACH,MAAM;GAAE,GAAG,KAAK;GAAM;EAAa;EACnC,MAAM,OAAO,SAAS,WAA4B;GAGhD,MAAM,mBACJ;IACE,UAAU;IACV,YAAY;IACZ;IACA,cAAc;IACd,eAAc,WAAU,2EAA2E;GACrG,GACA,SACF;GAEA,MAAM,IAAI,MAAM,uBAAuB;EACzC;CACF;AACF;;;ACnEA,MAAMA,aAAW;AAwBjB,SAASC,YAAU,QAAgC;CAEjD,IAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,OAAO,SAAS,GAC/D,OAAO,OAAO;CAEhB,IAAI,QAAQ,IAAI,iBACd,OAAO,QAAQ,IAAI;CAErB,MAAM,IAAI,MAAM,qFAAqF;AACvG;;;;;;;;AASA,MAAM,mBAAmB;;;;;;;;;AAUzB,SAAgB,qBACd,KACqC;CACrC,IAAI,CAAC,iBAAiB,KAAK,IAAI,KAAK,GAClC,OAAO,KAAA;CACT,IAAI,IAAI,aAAa,KAAA,KAAa,IAAI,aAAa,YACjD,OAAO,KAAA;CACT,OAAO,EAAE,kBAAkB,IAAI,aAAa,QAAQ,SAAS,OAAO;AACtE;;;;;;;;;;AAWA,SAAgB,QAAQ,QAAkC;CACxD,OAAO,aAAa;EAClB,MAAM;EACN,QAAQA,YAAU,MAAM;EACxB,SAAS,QAAQ,WAAWD;EAC5B,cAAc,QAAQ,gBAAgB;EACtC,cAAc,QAAQ,gBAAgB;GAAE,QAAQ;GAAM,mBAAmB;EAAM;EAC/E,sBAAsB;EACtB,cAAc,QAAQ;CACxB,CAAC;AACH;;;ACjFA,MAAM,WAAW;AAsBjB,SAAS,UAAU,QAA4B;CAE7C,IAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,OAAO,SAAS,GAC/D,OAAO,OAAO;CAEhB,IAAI,QAAQ,IAAI,aACd,OAAO,QAAQ,IAAI;CAErB,MAAM,IAAI,MAAM,8EAA8E;AAChG;;;;;;;;;AAUA,SAAgB,iBACd,KACyB;CAEzB,OAAO,EAAE,UAAU,EAAE,MADL,CAAC,CAAC,IAAI,YAAY,IAAI,aAAa,QACd,YAAY,WAAW,EAAE;AAChE;;;;;;;;;AAUA,SAAgB,IAAI,QAA8B;CAChD,OAAO,aAAa;EAClB,MAAM;EACN,QAAQ,UAAU,MAAM;EACxB,SAAS,QAAQ,WAAW;EAC5B,cAAc,QAAQ,gBAAgB;EACtC,cAAc,QAAQ,gBAAgB;GAAE,QAAQ;GAAO,mBAAmB;EAAM;EAChF,mBAAmB;EACnB,eAAe;EACf,uBAAuB;EACvB,sBAAsB;EACtB,cAAc,QAAQ;CACxB,CAAC;AACH"}
1
+ {"version":3,"file":"providers-BYgyPb15.js","names":["BASE_URL","getApiKey"],"sources":["../src/providers/cursor.ts","../src/providers/mistral.ts","../src/providers/zai.ts"],"sourcesContent":["import type { Provider, StreamCallbacks } from '.'\nimport { CURSOR_OAUTH_PROVIDER_ID } from '../chat/oauth-page/cursor'\nimport { resolveOAuthApiKey } from './oauth'\nimport { openaiCompat } from './openai-compat'\n\n/**\n * Cursor provider (OAuth).\n *\n * Auth is fully wired: `bun run auth --cursor` (or the TUI wizard) logs in via\n * Cursor's poll-based PKCE flow and persists credentials; this provider resolves\n * + refreshes the OAuth token on every turn via {@link resolveOAuthApiKey}.\n *\n * Inference is **not** wired yet. Cursor does not expose an OpenAI-compatible\n * HTTP endpoint — it speaks a protobuf/Connect agent protocol over HTTP/2 to\n * `api2.cursor.sh`. Implementing `stream()` requires porting that transport\n * (generated protobuf schemas + an HTTP/2 streaming client) in-process, which\n * is intentionally left as a follow-up. Until then `stream()` resolves the\n * token (proving auth works) and then throws a clear, actionable error.\n *\n * The `openaiCompat` base supplies the message-format helpers (`formatTools`,\n * `userMessage`, `toolResultsMessage`, …) so the provider satisfies the full\n * `Provider` contract and slots into the registry; only `stream` is overridden.\n */\nexport interface CursorParams {\n /** Bypass OAuth resolution with an explicit token (mainly for tests). */\n apiKey?: string\n defaultModel?: string\n /**\n * Extra HTTP headers for the eventual Cursor transport. Accepted for API\n * parity with the other providers and forwarded to the openai-compat base,\n * but inert until Cursor inference is implemented (the `stream` override\n * currently throws before any request is made).\n */\n extraHeaders?: Record<string, string>\n}\n\nconst DEFAULT_MODEL = 'claude-4.6-sonnet'\n\nconst NOT_IMPLEMENTED_MESSAGE\n = 'Cursor OAuth login works, but inference over Cursor is not implemented yet. '\n + 'Cursor uses a protobuf/HTTP-2 agent protocol (not an OpenAI-compatible API), '\n + 'so the streaming transport still needs to be ported. Use another provider for now.'\n\nexport function cursor(params?: CursorParams): Provider {\n const defaultModel = params?.defaultModel || DEFAULT_MODEL\n\n // Base only provides the format/serialization methods; `baseURL` is never\n // hit because we override `stream` below.\n const base = openaiCompat({\n name: 'cursor',\n apiKey: params?.apiKey ?? 'oauth',\n baseURL: 'https://api2.cursor.sh',\n defaultModel,\n capabilities: { vision: true, imageInToolResult: false },\n extraHeaders: params?.extraHeaders,\n })\n\n return {\n ...base,\n meta: { ...base.meta, defaultModel },\n async stream(options, callbacks: StreamCallbacks) {\n // Resolve (and lazily refresh) the OAuth token so auth failures surface\n // here with the right remediation, distinct from the not-implemented path.\n await resolveOAuthApiKey(\n {\n provider: 'cursor',\n providerId: CURSOR_OAUTH_PROVIDER_ID,\n params,\n missingError: 'No Cursor credentials found. Run `bun run auth --cursor` first.',\n refreshError: reason => `Cursor OAuth token refresh failed. Run \\`bun run auth --cursor\\` again. ${reason}`,\n },\n callbacks,\n )\n void options\n throw new Error(NOT_IMPLEMENTED_MESSAGE)\n },\n }\n}\n","import type { Provider, ProviderCapabilities } from '.'\nimport type { ThinkingLevel } from '../types'\nimport { openaiCompat } from './openai-compat'\n\n// Mistral La Plateforme — OpenAI-compatible `/v1/chat/completions`. Model ids\n// are plain slugs (`mistral-medium-latest`, `devstral-2512`, …). Reasoning is\n// NOT a separate wire field here: with `reasoning_effort: 'high'` the response\n// `content` becomes a list of `thinking` / `text` chunks (handled by\n// `openaiCompat`'s content-chunk path), and with `'none'` it stays a plain\n// string. See https://docs.mistral.ai/studio-api/conversations/reasoning.\nconst BASE_URL = 'https://api.mistral.ai/v1'\n\nexport interface MistralParams {\n apiKey?: string\n defaultModel?: string\n /**\n * Override the base URL — e.g. point at a regional endpoint or a gateway in\n * front of La Plateforme.\n */\n baseURL?: string\n /**\n * Provider capability flags. The `mistral-medium` / `mistral-large` /\n * `pixtral` families accept `image_url` parts — default:\n * `{ vision: true, imageInToolResult: false }`. Set `vision: false` when\n * pinning a text-only model (`codestral`, `devstral`, `ministral`).\n */\n capabilities?: ProviderCapabilities\n /**\n * Extra HTTP headers sent on every request. Forwarded to the underlying\n * {@link openaiCompat} client.\n */\n extraHeaders?: Record<string, string>\n}\n\nfunction getApiKey(params?: MistralParams): string {\n // Resolution order aligned with the other providers: explicit > env > fail.\n if (typeof params?.apiKey === 'string' && params.apiKey.length > 0)\n return params.apiKey\n\n if (process.env.MISTRAL_API_KEY)\n return process.env.MISTRAL_API_KEY\n\n throw new Error('No Mistral API key found. Pass `apiKey` or set MISTRAL_API_KEY in your environment.')\n}\n\n/**\n * Model ids that accept `reasoning_effort`. Mistral rejects unknown request\n * fields on some routes, so the knob is sent only for the families that\n * document it (`mistral-small-*`, `mistral-medium-3.5` / `-2604`, and the\n * deprecated-but-live `magistral-*`); `codestral` / `devstral` / `ministral`\n * requests stay byte-identical to what they were without this planner.\n */\nconst REASONING_MODELS = /^(?:magistral-|mistral-small-|mistral-medium-(?:3\\.5|2604))/\n\n/**\n * Map zidane's thinking level to Mistral's `reasoning_effort`. Only the two\n * documented values are emitted — `'none'` (thinking chunk omitted, `content`\n * stays a plain string) and `'high'` (full thinking chunk). `adaptive` sends\n * nothing so the model's own default applies.\n *\n * Exported for tests.\n */\nexport function planMistralReasoning(\n ctx: { model: string, thinking?: ThinkingLevel },\n): Record<string, unknown> | undefined {\n if (!REASONING_MODELS.test(ctx.model))\n return undefined\n if (ctx.thinking === undefined || ctx.thinking === 'adaptive')\n return undefined\n return { reasoning_effort: ctx.thinking === 'off' ? 'none' : 'high' }\n}\n\n/**\n * Mistral provider (La Plateforme).\n *\n * Thin wrapper around {@link openaiCompat} with Mistral-specific defaults\n * (base URL, default model, `reasoning_effort` planner). `supportsReasoning`\n * stays off: Mistral carries thinking inside `content` chunks rather than a\n * `reasoning_content` field, so there is nothing to echo back upstream —\n * thinking streams to the UI and is dropped from subsequent requests.\n */\nexport function mistral(params?: MistralParams): Provider {\n return openaiCompat({\n name: 'mistral',\n apiKey: getApiKey(params),\n baseURL: params?.baseURL || BASE_URL,\n defaultModel: params?.defaultModel || 'mistral-medium-latest',\n capabilities: params?.capabilities ?? { vision: true, imageInToolResult: false },\n planReasoningRequest: planMistralReasoning,\n extraHeaders: params?.extraHeaders,\n })\n}\n","import type { Provider, ProviderCapabilities } from '.'\nimport type { ThinkingLevel } from '../types'\nimport { openaiCompat } from './openai-compat'\n\n// Z.ai (Zhipu) GLM Open Platform — OpenAI-compatible endpoint. Model ids are\n// plain lowercase slugs (`glm-4.7`, `glm-4.7-flash`, …), reasoning streams in\n// the `reasoning_content` delta field, and thinking is toggled via an\n// `extra_body.thinking` object rather than a `reasoning_effort` string.\n// See https://docs.z.ai/guides/develop/http/introduction and\n// https://docs.z.ai/guides/capabilities/thinking-mode.\nconst BASE_URL = 'https://api.z.ai/api/paas/v4'\n\nexport interface ZaiParams {\n apiKey?: string\n defaultModel?: string\n /**\n * Override the base URL — e.g. point at the dedicated Coding Plan endpoint\n * instead of the standard API endpoint.\n */\n baseURL?: string\n /**\n * Provider capability flags. GLM text models are text-only — default:\n * `{ vision: false, imageInToolResult: false }`.\n */\n capabilities?: ProviderCapabilities\n /**\n * Extra HTTP headers sent on every request. Forwarded to the underlying\n * {@link openaiCompat} client.\n */\n extraHeaders?: Record<string, string>\n}\n\nfunction getApiKey(params?: ZaiParams): string {\n // Resolution order aligned with the other providers: explicit > env > fail.\n if (typeof params?.apiKey === 'string' && params.apiKey.length > 0)\n return params.apiKey\n\n if (process.env.ZAI_API_KEY)\n return process.env.ZAI_API_KEY\n\n throw new Error('No Z.ai API key found. Pass `apiKey` or set ZAI_API_KEY in your environment.')\n}\n\n/**\n * Map zidane's thinking level to Z.ai's `extra_body.thinking` request shape.\n * GLM has no depth knob — thinking is a binary switch — so any non-`off`\n * level enables it and `off` explicitly disables it (GLM-5.x/4.7 think by\n * default, so a silent omission would not turn thinking off).\n *\n * Exported for tests.\n */\nexport function planZaiReasoning(\n ctx: { thinking?: ThinkingLevel },\n): Record<string, unknown> {\n const enabled = !!ctx.thinking && ctx.thinking !== 'off'\n return { thinking: { type: enabled ? 'enabled' : 'disabled' } }\n}\n\n/**\n * Z.ai (Zhipu) GLM provider.\n *\n * Thin wrapper around {@link openaiCompat} pinned to the `reasoning_content`\n * round-trip mode: thinking arrives in the `reasoning_content` delta field and\n * is echoed back on assistant messages under the same name, keeping GLM's\n * interleaved/preserved thinking coherent across tool calls.\n */\nexport function zai(params?: ZaiParams): Provider {\n return openaiCompat({\n name: 'zai',\n apiKey: getApiKey(params),\n baseURL: params?.baseURL || BASE_URL,\n defaultModel: params?.defaultModel || 'glm-5.3-flash',\n capabilities: params?.capabilities ?? { vision: false, imageInToolResult: false },\n supportsReasoning: true,\n reasoningMode: 'reasoning_content',\n reasoningContentField: 'reasoning_content',\n planReasoningRequest: planZaiReasoning,\n extraHeaders: params?.extraHeaders,\n })\n}\n"],"mappings":";;;;AAoCA,MAAM,gBAAgB;AAEtB,MAAM,0BACF;AAIJ,SAAgB,OAAO,QAAiC;CACtD,MAAM,eAAe,QAAQ,gBAAgB;CAI7C,MAAM,OAAO,aAAa;EACxB,MAAM;EACN,QAAQ,QAAQ,UAAU;EAC1B,SAAS;EACT;EACA,cAAc;GAAE,QAAQ;GAAM,mBAAmB;EAAM;EACvD,cAAc,QAAQ;CACxB,CAAC;CAED,OAAO;EACL,GAAG;EACH,MAAM;GAAE,GAAG,KAAK;GAAM;EAAa;EACnC,MAAM,OAAO,SAAS,WAA4B;GAGhD,MAAM,mBACJ;IACE,UAAU;IACV,YAAY;IACZ;IACA,cAAc;IACd,eAAc,WAAU,2EAA2E;GACrG,GACA,SACF;GAEA,MAAM,IAAI,MAAM,uBAAuB;EACzC;CACF;AACF;;;ACnEA,MAAMA,aAAW;AAwBjB,SAASC,YAAU,QAAgC;CAEjD,IAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,OAAO,SAAS,GAC/D,OAAO,OAAO;CAEhB,IAAI,QAAQ,IAAI,iBACd,OAAO,QAAQ,IAAI;CAErB,MAAM,IAAI,MAAM,qFAAqF;AACvG;;;;;;;;AASA,MAAM,mBAAmB;;;;;;;;;AAUzB,SAAgB,qBACd,KACqC;CACrC,IAAI,CAAC,iBAAiB,KAAK,IAAI,KAAK,GAClC,OAAO,KAAA;CACT,IAAI,IAAI,aAAa,KAAA,KAAa,IAAI,aAAa,YACjD,OAAO,KAAA;CACT,OAAO,EAAE,kBAAkB,IAAI,aAAa,QAAQ,SAAS,OAAO;AACtE;;;;;;;;;;AAWA,SAAgB,QAAQ,QAAkC;CACxD,OAAO,aAAa;EAClB,MAAM;EACN,QAAQA,YAAU,MAAM;EACxB,SAAS,QAAQ,WAAWD;EAC5B,cAAc,QAAQ,gBAAgB;EACtC,cAAc,QAAQ,gBAAgB;GAAE,QAAQ;GAAM,mBAAmB;EAAM;EAC/E,sBAAsB;EACtB,cAAc,QAAQ;CACxB,CAAC;AACH;;;ACjFA,MAAM,WAAW;AAsBjB,SAAS,UAAU,QAA4B;CAE7C,IAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,OAAO,SAAS,GAC/D,OAAO,OAAO;CAEhB,IAAI,QAAQ,IAAI,aACd,OAAO,QAAQ,IAAI;CAErB,MAAM,IAAI,MAAM,8EAA8E;AAChG;;;;;;;;;AAUA,SAAgB,iBACd,KACyB;CAEzB,OAAO,EAAE,UAAU,EAAE,MADL,CAAC,CAAC,IAAI,YAAY,IAAI,aAAa,QACd,YAAY,WAAW,EAAE;AAChE;;;;;;;;;AAUA,SAAgB,IAAI,QAA8B;CAChD,OAAO,aAAa;EAClB,MAAM;EACN,QAAQ,UAAU,MAAM;EACxB,SAAS,QAAQ,WAAW;EAC5B,cAAc,QAAQ,gBAAgB;EACtC,cAAc,QAAQ,gBAAgB;GAAE,QAAQ;GAAO,mBAAmB;EAAM;EAChF,mBAAmB;EACnB,eAAe;EACf,uBAAuB;EACvB,sBAAsB;EACtB,cAAc,QAAQ;CACxB,CAAC;AACH"}
package/dist/providers.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { E as getModelCatalog, S as resolvePromptCacheKey, b as sanitizeToolSpecs, f as mapOAIFinishReason, j as setModelCatalog, l as classifyOpenAICompatError, m as openaiCompat, n as OpenAICompatHttpError, x as derivePromptCacheKey, y as sanitizeToolSchema } from "./openai-compat-DY-AKxCg.js";
2
- import { n as anthropic, r as applyAnthropicCacheBreakpoints } from "./anthropic-BDOGzhtj.js";
2
+ import { n as anthropic, r as applyAnthropicCacheBreakpoints } from "./anthropic-Da39cE-d.js";
3
3
  import { a as readOAuthCredentials, c as writeOAuthCredentials, d as refreshAnthropicToken, f as refreshOpenAICodexToken, i as isOAuthAccessToken, l as getOAuthApiKey, n as credentialsFromParams, o as registerCustomOAuthProvider, p as registerOAuthProvider, r as extractRuntimeCredentials, s as resolveOAuthApiKey, t as credentialsFilePath, u as getOAuthProvider } from "./oauth-C4GvY_p1.js";
4
4
  import { arcee } from "./providers/arcee.js";
5
5
  import { baseten, planBasetenReasoning } from "./providers/baseten.js";
6
6
  import { cerebras } from "./providers/cerebras.js";
7
- import { a as cursor, i as planMistralReasoning, n as zai, r as mistral, t as planZaiReasoning } from "./providers-DX91jwir.js";
7
+ import { a as cursor, i as planMistralReasoning, n as zai, r as mistral, t as planZaiReasoning } from "./providers-BYgyPb15.js";
8
8
  import { local } from "./providers/local.js";
9
9
  import { openai } from "./providers/openai.js";
10
10
  import { openrouter } from "./providers/openrouter.js";
package/dist/restate.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { L as buildAliasMaps } from "./openai-compat-DY-AKxCg.js";
2
2
  import { i as coerceToolResultContent, l as toolOutputByteLength, u as toolResultToText } from "./types-CWgENVLY.js";
3
- import { Dt as reserveChildLabel, Wn as hydrateReadStateFromSessionAsync, a as stagedToolState, i as isToolStateOps } from "./agent-DRNT6DbC.js";
3
+ import { Dt as reserveChildLabel, Wn as hydrateReadStateFromSessionAsync, a as stagedToolState, i as isToolStateOps } from "./agent-B1jsFaDq.js";
4
4
  import { r as formatTokenUsage } from "./stats-DAKBEKjc.js";
5
5
  import { createHash } from "node:crypto";
6
6
  //#region src/restate/awakeable.ts
@@ -1,12 +1,12 @@
1
- import { Mn as serializedPayloadBytes, at as createExtensionHud, ht as createExtensionHostRuntimeBridge, jn as createExtensionBus, ot as discoverAndBuildExtensionRegistry } from "./agent-DRNT6DbC.js";
1
+ import { Mn as serializedPayloadBytes, at as createExtensionHud, ht as createExtensionHostRuntimeBridge, jn as createExtensionBus, ot as discoverAndBuildExtensionRegistry } from "./agent-B1jsFaDq.js";
2
2
  import { S as resolveInlineSuggestion, b as createPathAutocompleteProvider } from "./src-DzzhE2YO.js";
3
3
  import { t as writeFileAtomic } from "./atomic-write-B7zr1ddF.js";
4
4
  import { p as resolveStorageDirs } from "./plugins-B531Tz5b.js";
5
5
  import { a as successResponse, i as errorResponse, n as JsonRpcRemoteError, r as createJsonRpcConnection } from "./json-rpc-rSWMUh9b.js";
6
6
  import { Pt as isMainSession } from "./turn-operations-CZLsNgw-.js";
7
7
  import { c as searchSessionHistoryIndexAsync, n as buildSessionSearchIndexAsync, u as searchSessionIndexAsync } from "./history-search-C4bk1Wz_.js";
8
- import { Gt as modelChainMetadata, In as resolveConfig, It as createChatHost, Kt as sessionModelFallbacks, Lt as toTodosSnapshot, cn as DEFAULT_SETTINGS, fn as SETTINGS_TOGGLES, ft as createFileMcpCredentialStore, hn as enforceSettingsInvariants, oi as deriveSessionTitle, si as eventsFromTurns } from "./approvals-B4PYwOnW.js";
9
- import { d as BUILTIN_PROVIDERS, n as applyApiKeyEnv, r as credentialsPath } from "./auth-BcG1Y3QL.js";
8
+ import { Gt as modelChainMetadata, In as resolveConfig, It as createChatHost, Kt as sessionModelFallbacks, Lt as toTodosSnapshot, cn as DEFAULT_SETTINGS, fn as SETTINGS_TOGGLES, ft as createFileMcpCredentialStore, hn as enforceSettingsInvariants, oi as deriveSessionTitle, si as eventsFromTurns } from "./approvals-S100xf1d.js";
9
+ import { d as BUILTIN_PROVIDERS, n as applyApiKeyEnv, r as credentialsPath } from "./auth-DfEWnkC7.js";
10
10
  import { basename, dirname, join } from "node:path";
11
11
  import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync } from "node:fs";
12
12
  import { spawn } from "node:child_process";
@@ -1329,4 +1329,4 @@ async function startIpcDaemon(options) {
1329
1329
  //#endregion
1330
1330
  export { connectIpcClient as a, IpcErrorCode as c, defaultSocketPath as d, ensureDaemonRunning as f, IpcVersionMismatchError as i, IPC_BUILD_ID as l, selfDaemonSpawnCommand as m, createDaemonEnvironment as n, connectOrSpawnDaemon as o, probeSocket as p, createFileApprovalsStore as r, IPC_PROTOCOL_VERSION as s, startIpcDaemon as t, daemonSocketName as u };
1331
1331
 
1332
- //# sourceMappingURL=server-BuqLVgQA.js.map
1332
+ //# sourceMappingURL=server-C8gULJ0d.js.map