| title | Adapters |
|---|---|
| description | The seven provider adapters — what each targets, how it builds requests, and its quirks. |
An adapter translates between opencodex's internal request/response model and one provider wire
format. Every adapter implements the ProviderAdapter interface (src/adapters/base.ts):
interface ProviderAdapter {
name: string;
buildRequest(parsed, incoming?): AdapterRequest | Promise<AdapterRequest>;
fetchResponse?(request, context): Promise<Response>; // custom retry/transport
parseStream(response): AsyncGenerator<AdapterEvent>;
parseResponse?(response): Promise<AdapterEvent[]>; // non-streaming
runTurn?(parsed, incoming, emit): Promise<void>; // bidirectional transport
}buildRequest lowers an OcxParsedRequest into an upstream HTTP request; parseStream /
parseResponse lift the provider's reply back into internal AdapterEvents. fetchResponse lets an
adapter own retries/timeouts, while runTurn supports transports that cannot be represented as one
HTTP fetch followed by one response stream. bridge.ts
then turns the events into Responses SSE.
Targets: OpenAI Chat Completions (POST {baseUrl}/chat/completions) and every compatible
provider — xAI, Kimi, DeepSeek, GLM, Groq, OpenRouter, Ollama (local & cloud), and more.
Auth: key (Bearer).
- Converts internal messages to OpenAI roles; maps tools to
{type:"function", function:{…}}andtool_choice(auto/none/requiredor a named function). - Tool-result images ride in a follow-up user vision message (
image_urlparts) released once the tool round closes, sincerole:"tool"content is text-only; the[image]marker stays in the tool message as the anchor. - Rewrites Codex's GPT-5 identity prompt to a model-agnostic intro so routed models don't claim to be OpenAI.
- Clamps
reasoning_effortto the model's advertised subset when an exact tier is unavailable;xhighandmaxremain distinct labels unless a provider explicitly configures an alias. The adapter omits it entirely for ids inprovider.noReasoningModels. - Streams
delta.content(text),delta.reasoning_content(thinking), anddelta.tool_calls[]; collectsusage.
Targets: the OpenAI Responses API. passthrough: true — forwards the raw request body and
streams the response back untranslated.
Auth: forward (relay the caller's headers) or key.
forwardURL →{baseUrl}/responses. Akeyprovider defaults to the legacy{baseUrl}/v1/responsesconstruction.- A
keyprovider may set a validated relativeresponsesPath; the adapter removes one trailing slash frombaseUrland sends{trimmedBaseUrl}{responsesPath}. For Ark Agent Plan, usebaseUrl: "https://ark.cn-beijing.volces.com/api/plan/v3"withresponsesPath: "/responses". - In
forwardmode only a safe header allowlist is relayed (FORWARD_HEADERS): authorization, ChatGPT account id, and the OpenAI beta/originator/session headers. This is the ChatGPT-login path that also powers the sidecars.
Targets: Anthropic Messages (/v1/messages).
Auth: key (x-api-key by default, or Authorization: Bearer with apiKeyTransport: "bearer") or oauth (Bearer + anthropic-beta, for Claude Pro/Max).
- Converts messages to Anthropic content blocks (text, base64 image,
tool_use,thinking). - Extended thinking math: Anthropic requires
max_tokens > thinking.budget_tokens. The adapter maps reasoning effort to a budget (minimal 1024 … max 32000), then computes a safemax_tokenswith output headroom, and dropstemperature/top_pwhen thinking is enabled (Anthropic forbids them there). - Always sends
anthropic-version: 2023-06-01. Streamscontent_block_delta(text_delta,thinking_delta, compatiblereasoning_delta,input_json_delta). The SSE decoder preserves event state across fetch chunks and accepts a terminalmessage_stopwithout a trailing newline. - For routed Anthropic Responses turns with client tools, a bounded terminal guard detects the high-confidence case where the user requested an action but Claude ends with an execution claim and no tool call. It performs at most one internal continuation; normal answers, clarification questions, tool-using turns, and transport-incomplete responses are not auto-retried.
Targets: Google Gemini, Vertex AI, and Antigravity Cloud Code Assist. AI Studio uses
/v1beta/models/{model}:streamGenerateContent; the other modes use their native Google endpoints.
Auth: API key, Vertex ADC, or Google Antigravity OAuth, selected by googleMode.
- System prompt →
systemInstruction; messages →contents[](assistant →model); tools →functionDeclarations. Data-URL images →inline_data. - Tool-call ids are synthesized when Gemini omits them. Antigravity preserves and replays real
thoughtSignaturevalues so reasoning continuity survives later turns. - Inline image output: when the model is one of the explicit image-capable chat IDs
(
gemini-3.1-flash-image,gemini-2.0-flash-preview-image-generation, orgemini-3-pro-image-preview), the adapter sendsresponseModalities: ["TEXT", "IMAGE"]. Standalone media-generation IDs such asgemini-3-pro-imageare not included. ReturnedinlineDataparts are materialized under the configured OpenCodexartifacts/directory and surfaced as markdown image links to the authenticated opaque route/v1/opencodex/artifacts/<id>(notfile:URIs or host filesystem paths). Each image is capped at 50 MB and each response at 100 MB of decoded data; malformed base64 payloads are rejected. Artifacts are pruned automatically when the count exceeds 200 files.
Targets: the Amazon CodeWhisperer Streaming GenerateAssistantResponse service used by Kiro
(https://runtime.{region}.kiro.dev/).
Auth: Kiro OAuth access token as Bearer, with region/profile metadata from the Kiro credential.
- Builds Kiro
conversationState, maps Codex tools and tool results, and sends image blocks supported by the Kiro wire. - Decodes
application/vnd.amazon.eventstream, reconstructs text/thinking/tool events, detects truncated tool JSON, and estimates usage because the upstream does not return token counts. - Uses the configured
baseUrlverbatim when it is custom. A canonicalruntime.{region}.kiro.devURL follows the imported credential's API region; only that canonical shape is eligible for one bounded fallback toq.{region}.amazonaws.comafter an endpoint, signature, DNS, or connection failure. - Owns replay-safe connection-reset recovery, that single eligible endpoint fallback, one OAuth refresh/replay after HTTP 401, and bounded recovery for transient Kiro 429s. A shared cooldown and single post-cooldown probe prevent concurrent requests from exhausting independent retry budgets; hard quota failures and ordinary service errors are not replayed.
- Its non-streaming parser drains the same event stream for the web-search loop.
Kiro assistant text carries no dependable end-turn phase of its own. Its terminal metadataEvent
can carry a native stopReason, but Kiro can label progress prose as END_TURN. On tool-enabled
turns, END_TURN and STOP_SEQUENCE therefore prove only that the inference stopped; ordinary text
remains commentary and enters the one bounded completion validation.
END_TURN, STOP_SEQUENCE, or a missing stop reason may use the compatibility path. Other explicit
reasons have already terminated the inference upstream, so the adapter reports them instead of
spending another model request: an output-token limit surfaces as incomplete output that a client may continue, while
context-window exhaustion surfaces as a non-retryable context-length error rather than as truncated
output. Filtering and guardrail stops surface as filtered incomplete output, and a TOOL_USE stop
that arrives without an actual tool call is reported as a contradiction rather than treated as
progress.
When an ordinary client tool exists, opencodex adds a private
codex_kiro_final_answer tool to the upstream request; progress text streams as commentary and
cannot terminate the turn. The adapter consumes the private call, emits its answer as final text,
and never exposes the private tool to Codex or Claude Code. Because the stop reason only arrives at
the end of the stream, assistant text in a tool-enabled turn is held until either a real tool call
starts or the stream ends, then releases it as commentary unless the private tool supplied the final
answer. When the web-search sidecar is active, released
commentary still streams ahead of the terminal event; only the events needed to decide whether the
model requested a synthetic search remain buffered.
If Kiro stops without calling the completion tool, the adapter makes one continuation. Reasoning-
only retries preserve the original valid user/tool-result turn rather than manufacturing an empty
assistant message; visible progress is replayed with a non-empty adapter-owned instruction. Before
transport, the generated conversation is checked for alternating roles, non-empty structural turns,
and matched tool-use/result ids. Empty tool output receives a neutral non-empty placeholder. The
retry cannot recurse: an empty or reasoning-only retry is returned as retryable incomplete, while a
real client tool call keeps the turn open. A completion-tool answer is always emitted as
final_answer, even when it exactly repeats prior commentary, because phase correctness is more
important than cosmetic de-duplication. Tool-free requests retain normal text completion behavior.
gpt-5.6-sol and claude-opus-5 have verified native effort support, and each model family names
the request field differently. A selected low, medium, high, xhigh, or max value is sent
as additionalModelRequestFields.reasoning.effort for gpt-5.6-sol and as
additionalModelRequestFields.output_config.effort for claude-opus-5. Other Kiro models currently
use emulated reasoning: opencodex converts the selected level into bounded thinking instructions in
the user content because their native effort field has not been verified. Do not interpret an
advertised effort control on those models as proof of upstream-native reasoning support.
Targets: Cursor's agent.v1.AgentService/Run over HTTP/2 Connect streaming at api2.cursor.sh.
Auth: Cursor OAuth/access token from provider.apiKey or the forwarded authorization header.
- Uses
runTurnrather than the ordinary fetch/parse path. Requests, server events, tool arguments, usage checkpoints, and client replies are encoded with@bufbuild/protobufschemas incursor/gen/agent_pb.tsand framed as Connect messages. - Replays conversation state through content-addressed blobs, maps server tool calls back to Codex,
discovers live Cursor models through the protobuf
GetUsableModelsRPC, and retries only before a run request is committed to the wire. - Exposes Cursor Router as
cursor/autoplus explicitcursor/auto-cost,cursor/auto-balance, andcursor/auto-intelligenceentries. Explicit levels are encoded inrequested_model.parameterswhile the legacycursor/autoentry retains the account/team default. - Cursor-native local filesystem/shell/network execution is denied by default. Explicit
mcpServersanddesktopExecutorintegrations have separate opt-ins;nativeLocalExec: "on"enables the broader built-in executor and bypasses Codex approval/sandbox semantics, and legacyunsafeAllowNativeLocalExec: trueremains equivalent only whennativeLocalExecis unset.
Targets: Azure OpenAI. Wraps openai-responses (so also passthrough: true).
Auth: key via the api-key header (not Bearer).
- Delegates request building to the Responses passthrough, validates that
baseUrlcontains no unresolved template placeholder, and replacesAuthorizationwithapi-key. The configured URL targets Azure's v1 Responses API directly, so the adapter does not appendapi-version.
Shared helpers used by the vision-aware adapters:
parseDataUrl(url)— split adata:<type>;base64,<data>URL into{ mediaType, base64 }for Anthropic/Google image blocks.contentPartsToText(content)— flatten content parts to text for text-only tool messages (an undescribed image becomes a short[image]marker, never a token-exploding base64 blob).