# Model API protocol notes for coding-agent use

**Snapshot:** 2026-10-25 (Asia/Shanghai). All URLs accessed 2026-10-25; raw markdown copies are in `research/sources/`.
**Conventions:** "Documented" = in the official docs snapshot; "Inference" = engineering inference; "Uncertain" = contradictions/omissions.

## (a) Executive summary

The four protocols converge on typed messages, tool loops, opaque reasoning round-trip, SSE streaming, prompt caching, and hosted tools, but place conversation and reasoning state differently. Chat Completions is the compatibility floor: stateless, feature-frozen for agentic work, and unable to combine reasoning with tool calling on new models (since GPT-5.4, tool calling requires `reasoning_effort:"none"`; GPT-6 Astra / GPT-6.1 Sol require Responses). Responses is OpenAI's agent runtime: typed Items, server-side built-in tools, `store`/`previous_response_id`, encrypted reasoning, compaction, background mode, and a WebSocket transport for tool-heavy loops. Anthropic Messages is stateless and strongly typed, with the strictest reasoning-integrity rules (signed thinking must be replayed; append-only history preserves thinking and cache) and the most mature caching/server-tool story. Gemini Interactions is GA (June 2026), unifying models and managed agents behind one `Interaction` resource with observable steps, server-side state, background execution, and thought signatures; legacy `generateContent` remains fully supported. For coding agents, Responses and Messages are the mature choices; Interactions is close but younger with documented gaps; Chat Completions is a portability layer.

## (b) Comparison table

| Dimension | OpenAI Chat Completions | OpenAI Responses | Anthropic Messages | Gemini Interactions |
|---|---|---|---|---|
| Endpoint / object | `POST /v1/chat/completions`; `chat.completion` | `POST /v1/responses`; `response` | `POST /v1/messages`; `message` | `POST /v1beta/interactions` (stable `/v1/interactions`); `interaction` |
| Status | Supported; not recommended for new work; degraded reasoning+tools | Recommended for OpenAI frontier features | Current native Claude API (`anthropic-version: 2026-10-21`) | GA June 2026; recommended; `generateContent` legacy but fully supported |
| Request model | `messages[]`, roles `developer/system/user/assistant/tool` | `input`: string or 30+ typed Items; `instructions` | `messages[]` user/assistant blocks; top-level `system`; `max_tokens` required | `input`: string, Content, or Step[]; `system_instruction`; `tools`; `generation_config` |
| Output model | `choices[].message` (`content`, `tool_calls`, `refusal`) + `finish_reason` | `output: Item[]` (`message`/`function_call`/`reasoning`/built-in call items) + `output_text` | `content: Block[]` (`text`/`tool_use`/`thinking`/server-tool blocks) + `stop_reason` | `status` + `steps[]` (`user_input`/`thought`/`model_output`/`function_call`/`function_result`/server-tool steps) |
| Custom tool loop | Assistant `tool_calls` → `role:"tool"` messages with `tool_call_id` | `function_call` item → `function_call_output` item with `call_id` | `tool_use` block → user `tool_result` block with `tool_use_id` | `function_call` step → `function_result` step with `call_id` |
| Reasoning round-trip | `reasoning_effort` only; tokens only in usage; no continuity | Opaque reasoning Items, `summary`, `encrypted_content`, `reasoning.context`, `phase` | `thinking` + `signature`; `redacted_thinking`; preserved-thinking prefix checks | `thought` + `signature`; stateful auto, stateless must replay exactly |
| State | Stateless; client history; `store` is for distillation/evals | `store:true` default, 30-day retention; `previous_response_id`; Conversations; compaction | Stateless; client history; compaction/context editing beta | `store:true` default (55d paid / 1d free); `previous_interaction_id`; background |
| Streaming | SSE `chat.completion.chunk` deltas + `data: [DONE]` | Typed SSE events with `sequence_number` | Named SSE events `message_start`/`content_block_*`/`message_delta`/`message_stop`/`ping`/`error` | `interaction.created`, `interaction.status_update`, `step.start/delta/stop`, `interaction.completed`, `error` |
| Caching | Implicit automatic; `prompt_cache_key`; explicit breakpoints GPT-5.6+ | Implicit + explicit breakpoints (`prompt_cache_options`) | `cache_control` (top-level auto or ≤4 explicit), 5m/1h TTL | Implicit only; stateful chaining improves hits |
| Built-in tools | None | web/file search, code interpreter, computer use, image gen, MCP, shell, apply_patch, tool search | web search/fetch, code execution, bash, text editor, memory, computer/browser, tool search; MCP connector | Google Search/Maps, URL context, code execution, file search, computer use, MCP server |
| Multimodal | text, `image_url`, `input_audio`, `file` | text/image/file in; image generation out | text/image/document/PDF; citations | text/image/audio/video/document; image/audio output |
| SDK / ecosystem | any OpenAI-compatible client/SDK | official OpenAI SDKs (Python/JS/Go/Java/C#/Ruby) | Anthropic SDKs + OpenAI-compat layer | `google-genai` ≥2.3.0 (Python/JS/Go/Java) + OpenAI-compat endpoint |

## (c) Per-protocol notes

### 1. OpenAI Chat Completions (`POST /v1/chat/completions`)

**Status.** "While Chat Completions remains supported, Responses is recommended for all new projects"; Responses claims ~3% SWE-bench and 40–80% cache-utilization improvements. Reasoning guidance adds: no function calling with GPT-6 Astra / GPT-6.1 Sol, and since GPT-5.4 tool calling requires `reasoning_effort:"none"`. GPT-6 Astra also rejects `"none"` (400), so *inference:* it has no working tool-calling configuration here.

**Data model.**

```jsonc
POST /v1/chat/completions
{
  "model": "gpt-6-astra",
  "messages": [
    {"role": "developer", "content": "You are a coding agent."},
    {"role": "user", "content": [{"type": "text", "text": "Fix the test"}]},
    {"role": "assistant", "content": null,
     "tool_calls": [{"id": "call_1", "type": "function",
                     "function": {"name": "read_file", "arguments": "{\"path\":\"a.py\"}"}}]},
    {"role": "tool", "tool_call_id": "call_1", "content": "print(1)"}
  ],
  "tools": [{"type": "function", "function": {"name": "read_file", "parameters": {...}, "strict": true}}],
  "tool_choice": "auto",              // none | auto | required | named function
  "max_completion_tokens": 8192,      // max_tokens deprecated
  "reasoning_effort": "none",         // none|minimal|low|medium|high|xhigh|max
  "stream": true
}
```

Response: `object:"chat.completion"`, `choices[{index, message:{role, content, refusal, tool_calls[]}, finish_reason}]`, `usage` (with `completion_tokens_details.reasoning_tokens`), `system_fingerprint`. Roles: `developer`, `system`, `user`, `assistant`, `tool`; `developer` replaces `system` for o1+; `function`/`function_call` are deprecated. `store` here only stores output for distillation/evals, **not** conversation state; caching keys on `prompt_cache_key`.

**Tool loop.** Client appends the assistant message with `tool_calls` and one `role:"tool"` message per call with the matching `tool_call_id`; model continues; `finish_reason:"tool_calls"` means more tools wanted. Argument strings may be invalid JSON ("the model does not always generate valid JSON") — validate first. No hosted tools here.

**Streaming.** SSE `data:` frames are `object:"chat.completion.chunk"`: first chunk carries `delta.role`, then `delta.content`, then `delta.tool_calls[]` fragments indexed by `index` (partial `function.arguments`), a final chunk with `finish_reason`, then `data: [DONE]`. `stream_options.include_usage:true` adds a final usage-only chunk with `choices: []` before `[DONE]` (possibly lost if the stream breaks). No reasoning text or summaries are streamed.

**State and caching.** Fully stateless: resend the full message array every turn. Automatic prompt caching is on by default (minimum 1,024 tokens on GPT-5.6+; writes 1.25×/2.5× for 30m/12h TTL; reads 0.1×, 0.05× on GPT-6.1 Sol; four explicit breakpoints). Tool histories grow monotonically, but earlier reasoning is discarded.

**Assessment.** Errors: 429 `rate_limit_error`/503 `server_is_overloaded` with `Retry-After`; 400s are `invalid_request_error`. Pros: simplest schema, universal ecosystem, `n` choices, logprobs, broad model/provider coverage. Cons: no hosted tools, no reasoning continuity, reasoning+tools narrowed on new models, no `output_text` helper, feature-frozen relative to Responses. Pitfalls: mismatched `tool_call_id` (400); unparseable streamed tool JSON; omitting `role:"tool"` messages; assuming `store:true` gives chat state; truncation changing cache prefixes. Best fit: legacy apps, multi-provider gateways, simple text/classification, OpenAI-compat-only providers.

### 2. OpenAI Responses (`POST /v1/responses`)

**Status.** OpenAI's recommended primitive for new projects.

**Data model.** `input` is a string or a list of **Items**, not messages: `message`, `function_call`, `function_call_output`, `reasoning`, `compaction`, `item_reference`, `configuration_update`, `additional_tools`, plus built-in-tool items (`web_search_call`, `file_search_call`, `code_interpreter_call`, `computer_call`/`output`, `image_generation_call`, `mcp_call`, `shell_call`/`local_shell_call`, `apply_patch_call`, `tool_search_call`, … 30+ variants). Messages have `role: user|assistant|system|developer`, typed content (`input_text`, `input_image`, `input_file`), and assistant `phase` (`commentary`/`final_answer`).

```jsonc
POST /v1/responses
{
  "model": "gpt-6-astra",
  "instructions": "You are a coding agent. Keep edits minimal.",
  "input": [{"role": "user", "content": [{"type": "input_text", "text": "Fix the test"}]}],
  "tools": [
    {"type": "function", "name": "read_file", "parameters": {...}, "strict": true},
    {"type": "web_search"},
    {"type": "file_search", "vector_store_ids": ["vs_1"]},
    {"type": "code_interpreter", "container": {"type": "auto"}},
    {"type": "mcp", "server_label": "repo", "server_url": "https://..."}
  ],
  "reasoning": {"effort": "medium", "summary": "auto", "mode": "standard", "context": "all_turns"},
  "text": {"format": {"type": "json_schema", "name": "patch", "schema": {...}}},
  "store": true,
  "previous_response_id": null,
  "context_management": [{"type": "compaction", "compact_threshold": 200000}],
  "include": ["reasoning.encrypted_content"],
  "background": false,
  "stream": true
}
```

Response: `object:"response"`, `id`, `status` (`in_progress|completed|incomplete|failed|cancelled|queued`), `output: Item[]`, `usage` (`input_tokens_details.cached_tokens`, `output_tokens_details.reasoning_tokens`), `error`, `incomplete_details`, effective `reasoning.context/mode`. Output messages carry `content:[{type:"output_text", text, annotations, logprobs}]`; SDKs add an `output_text` aggregate helper (Python/JS). `include` can add search sources, code-interpreter outputs, file-search results, logprobs, and (legacy) `reasoning.encrypted_content`.

**Tools.** Built-ins execute server-side inside one response — documented as "an agentic loop by default" and able to call search/file-search/code-interpreter/computer/image-gen/MCP/shell/apply_patch repeatedly before returning. Client functions are two Items correlated by `call_id` (`function_call` → your code → `function_call_output`, string or content list). Responses tools are internally tagged (`{"type":"function","name":...}`), unlike Chat Completions' nested `function` object; `strict` is attempted by default and falls back to `strict:false` if incompatible. `parallel_tool_calls` and `max_tool_calls` bound volume. MCP adds approval items.

**Reasoning round-trip.** Reasoning is an opaque Item. With `store:true`, `previous_response_id` or a Conversation replays it server-side. In stateless mode (`store:false`, ZDR), reasoning items include `encrypted_content` **by default**; `include:["reasoning.encrypted_content"]` is still accepted but no longer required. Docs say to replay every output item including reasoning and assistant `phase`; dropping them degrades tool-loop quality. When streaming, take `encrypted_content` from `response.output_item.done`, not `.added`. `reasoning.context` (`auto|current_turn|all_turns`) controls earlier-turn rendering (GPT-5.6 defaults `all_turns`; earlier models `current_turn`); reasoning is reusable only within the same model family. `reasoning.mode` (`standard|pro`, GPT-5.6/GPT-6), `configuration_update` (mid-conversation effort change), and `reasoning.summary` exist; raw chain-of-thought is never exposed.

**State, retention, compaction.** Responses stored by default for 30 days (`store:false` disables; Conversations-attached items are exempt from that TTL). Three chaining modes: manual item replay, `previous_response_id` (mutually exclusive with `conversation`; prior input tokens are billed again), and the Conversations API (durable object). Server-side compaction: `context_management:[{type:"compaction","compact_threshold":N}]` emits an opaque encrypted `compaction` Item; standalone `POST /v1/responses/compact` returns a compacted input window. With stateless replay, drop items before the latest compaction item; with `previous_response_id`, don't prune manually.

**Background and WebSocket.** `background:true` runs asynchronously; poll/stream and resume; WebSocket mode connects to `/v1/responses` and sends `response.create`; `previous_response_id` continues via a connection-local in-memory cache. `stream_id` multiplexes lanes (same lane FIFO, different lanes concurrent); a connection allows 16 in-flight responses and 32 named lanes and lasts 60 minutes. With `store:false`, an uncached ID returns `previous_response_not_found`.

**Streaming.** Typed SSE events, each with `type` and `sequence_number`: lifecycle (`response.created`, `response.in_progress`, `response.completed|incomplete|failed`, `error`), item/part events (`response.output_item.added/done`, `response.content_part.added/done`), text/reasoning deltas (`response.output_text.delta/done`, `response.reasoning_summary_text.delta/done`, `response.reasoning_text.delta/done`), tool deltas (`response.function_call_arguments.delta/done` and per-tool `*.delta/done` events), and `response.compaction.compacting`. Consumers branch on `type` rather than accumulating deltas — the main change from Chat Completions.

**Assessment.** Errors: same 429/503 model; WebSocket adds `previous_response_not_found` and connection/stream limit codes. Pros: richest agent primitives; built-ins executed in-loop; first-class reasoning lifecycle; stateful chaining/compaction; background/WebSocket for long tool-heavy work. Cons: complexity and item-typing bugs; OpenAI lock-in; 30-day storage defaults need deliberate ZDR handling; per-model gating (WebSocket, compaction, `pro`). Pitfalls: assuming `output[0]` is the message (reasoning items often precede it); ignoring `status:"incomplete"`/`incomplete_details.reason:"max_output_tokens"`; dropping `phase` or reasoning on replay; expecting `instructions` to persist across `previous_response_id`; pruning before the compaction item; stale IDs after reconnect. Best fit: OpenAI-first coding agents, long-horizon tool-heavy runs, hosted search/code/file/MCP tooling, low-latency WS agents.

### 3. Anthropic Messages (`POST /v1/messages`)

**Status and versioning.** Native Claude API. Version `2026-10-21` removed `temperature`/`top_p`/`top_k` entirely (any value → 400) and moved API keys to `Authorization: Bearer`; legacy Text Completions is gone from current SDKs. Messages alternate user/assistant (same-role turns merge); limits: 100,000 messages, 32 MB per request.

**Data model.** `system` is top-level (string or text blocks with `cache_control`). Message `content` is a typed block array; `max_tokens` is required (and `max_tokens: 0` documented as cache pre-warm without generation).

```jsonc
POST /v1/messages
{
  "model": "claude-opus-5-5",
  "max_tokens": 8192,                 // required; 0 pre-warms cache, no output
  "system": [{"type": "text", "text": "You are a coding agent.", "cache_control": {"type": "ephemeral"}}],
  "messages": [
    {"role": "user", "content": [{"type": "text", "text": "Fix the test"}]},
    {"role": "assistant", "content": [
      {"type": "thinking", "thinking": "", "signature": "EqQ..."},
      {"type": "tool_use", "id": "toolu_1", "name": "read_file", "input": {"path": "a.py"}}
    ]},
    {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "toolu_1", "content": "print(1)"}]}
  ],
  "tools": [{"name": "read_file", "description": "...",
             "input_schema": {"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]},
             "strict": true}],
  "tool_choice": {"type": "auto"},
  "thinking": {"type": "adaptive", "display": "summarized"},
  "output_config": {"effort": "high", "format": {"type": "json_schema", "schema": {...}}},
  "cache_control": {"type": "ephemeral"},   // optional top-level automatic caching
  "stream": true
}
```

Response: `type:"message"`, `id`, `role`, `content: Block[]`, `stop_reason`, `stop_sequence`, `usage` (`input_tokens`, `cache_creation_input_tokens`, `cache_read_input_tokens`, `output_tokens_details.thinking_tokens`, `server_tool_use` counts). Stop reasons: `end_turn`, `max_tokens`, `stop_sequence`, `tool_use`, `pause_turn`, `refusal`, `model_context_window_exceeded`. Blocks: `text`, `image`, `document`, `tool_use`, `tool_result`, `thinking`, `redacted_thinking`, `server_tool_use`, and result blocks (`web_search_tool_result`, `code_execution_tool_result`, `tool_search_tool_result`, …). `role:"system"` messages can appear mid-conversation as an append-only alternative to editing top-level `system`.

**Tool loop.** Canonical loop: while `stop_reason=="tool_use"`, run each `tool_use`, append the assistant content unchanged, append a user message with one `tool_result` per `tool_use_id` (`is_error` optional). Thinking blocks returned with the tool call **must** be passed back complete and unmodified in that tool-use turn (including `redacted_thinking`); filtering only `type=="thinking"` silently breaks the protocol. Server tools differ: `server_tool_use` blocks execute inside Anthropic's server-side agentic loop, results arrive in the same assistant turn, and no `tool_result` is sent. If the loop hits its cap, the response ends with `stop_reason:"pause_turn"` and the client must resend the conversation including the paused content; mixed server+client parallel groups return `stop_reason:"tool_use"` with an unpaired `server_tool_use` that runs after client results arrive. Forced tool use (`tool_choice:"any"`/named) is rejected on Opus/Sonnet 5.5, Fable/Mythos 5.1 and incompatible with manual extended thinking.

**Thinking and preserved thinking.** Claude 5-era models think by default (adaptive); `thinking` types are `adaptive`, `enabled` (manual `budget_tokens` ≥1024), `disabled`, and Sonnet 5.5's `between_tools`. `display:"summarized"|"omitted"` controls summary text; the encrypted `signature` always reconstructs full reasoning. Within a tool-use turn the thinking sequence must match exactly — edits yield 400. Across turns, models keep all prior thinking or only the last turn (per model); pass blocks back and let the API filter. Preserved-thinking validation (default for accounts created on/after 2026-08-31) checks the producing model and everything before the block: changing top-level `system`, `tools`, or earlier messages invalidates that block and later ones (400 or dropped per `block_binding`, beta `thinking-binding-controls-2026-08-01`). Append-only replacements: mid-conversation system messages, `tool_addition`/`tool_removal` blocks (beta `inline-tools-2026-09-15`), per-message effort updates, and server compaction. Thinking config/effort are rendered into the prompt, so changing them invalidates prompt cache.

**Streaming.** Named SSE events with matching `type` in `data`: `message_start` (empty content, `input_tokens`, `output_tokens:1`), then per block `content_block_start` → `content_block_delta`(s) → `content_block_stop`, then `message_delta` (cumulative usage), then `message_stop`; `ping` may appear anywhere, and errors after HTTP 200 arrive as `event: error` (e.g. `overloaded_error`). Delta types: `text_delta`, `input_json_delta` (partial JSON for `tool_use.input`), `thinking_delta`, `signature_delta` (just before `content_block_stop`). SDKs reassemble the Message and require streaming when `max_tokens > 21,333` to avoid HTTP timeouts.

**Caching.** `cache_control:{"type":"ephemeral"}` at top level applies an automatic breakpoint to the last cacheable block and walks backward; explicit per-block markers allow up to four breakpoints. Order is `tools` → `system` → `messages`; minimum cacheable length is model-dependent (512 tokens for Claude 5-era; 1,024–4,096 for older); 5-minute TTL default, 1-hour TTL at 2× write price; writes 1.25×/2×, reads 0.1× (0.025–0.05× on newest); 20-block lookback. Thinking blocks can't be marked directly but are cached with assistant history, and thinking-heavy workloads are the documented 1h-TTL case.

**Compaction (beta).** On demand (`compact-2026-09-04` beta, top-level `compaction` parameter, client-driven, keeps recent turns) or at a token threshold (beta `compact-2026-01-12`, `context_management.edits:[{"type":"compact_20260112","trigger":{"type":"input_tokens","value":N}}]`, minimum 50,000, options `pause_after_compaction` and `instructions`).

**Beta headers and OpenAI compatibility.** `anthropic-beta` is comma-separated (SDK `betas`); features include MCP connector (`mcp-client-2025-11-20`; `mcp_servers` + `tools:[{type:"mcp_toolset"}]`), compaction, context editing, fast mode, preserved-thinking controls, and inline tool changes. The OpenAI SDK compat layer (base URL `https://api.anthropic.com/v1/`, endpoint `/v1/chat/completions`) documents: `strict` ignored, prompt caching unsupported, thinking not returned, system/developer messages hoisted into top-level `system`, most sampling/penalty/`response_format` fields ignored, audio ignored, `n` must be 1.

**Assessment.** Errors: 429 `rate_limit_error` + `retry-after`, 413 `request_too_large`, 504 `timeout_error`, 529 `overloaded_error`. Pros: precise typed protocol; best reasoning integrity (signed/preserved thinking); strongest explicit caching economics; server tools + MCP; fine-grained tool streaming. Cons: client-owned state; strict prefix/thinking rules create 400 failure modes; no sampling parameters in current version; beta sprawl; 529 overload; no durable server conversation object. Pitfalls: stripping thinking/`redacted_thinking`; editing earlier messages; changing `tools`/`system` mid-conversation; toggling thinking mid-turn (silently disables it); treating server-tool results as client tools; assuming `message_start` usage is final; old SDKs unable to set version 2026-10-21. Best fit: Claude-first coding agents, long cached agentic sessions, enterprise/ZDR deployments that still want thinking continuity, server-side web/code/file tools.

### 4. Gemini Interactions (`POST /v1beta/interactions`)

**Status.** GA as of June 2026 and "recommended for all new projects"; `generateContent` "remains fully supported" but is legacy, and new capabilities launch on Interactions. The reference exposes `/v1/interactions` (stable) alongside `/v1beta/` (used by most examples).

**Resource and steps.** An `Interaction` is a complete turn/task record. `POST` returns model-generated steps only; `GET /interactions/{id}` returns the full timeline including the initial `user_input` step. Step types: `user_input`, `thought`, `model_output`, `function_call`/`function_result`, plus call/result pairs for `google_search`, `google_maps`, `url_context`, `code_execution`, `file_search`, `mcp_server_tool`, and `processing`; server-tool steps carry signatures too.

```jsonc
POST /v1beta/interactions
{
  "model": "gemini-3.8-flash",           // or "agent": "deep-research-..." / "antigravity-preview-09-2026"
  "input": "Fix the failing test",       // string | Content | Content[] | Step[]
  "system_instruction": "You are a coding agent.",
  "tools": [
    {"type": "function", "name": "read_file", "description": "...",
     "parameters": {"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]}},
    {"type": "google_search"}, {"type": "code_execution"}, {"type": "url_context"}
  ],
  "generation_config": {"thinking_level": "medium", "thinking_summaries": "auto",
                        "tool_choice": "auto", "max_output_tokens": 65536},
  "previous_interaction_id": null,
  "store": true, "background": false, "stream": true
}
```

Response: `object:"interaction"`, `id`, `status` (`in_progress|requires_action|completed|failed|cancelled|incomplete`), `steps[]`, `model`/`agent`, `usage` (`total_input_tokens`, `total_output_tokens`, `total_thought_tokens`, `total_cached_tokens`, `total_tool_use_tokens`, `total_tokens`), `environment_id` for managed agents. `requires_action` means `function_call` steps await client execution.

**Tool loop.** Function calls arrive as `function_call` steps (`name`, `arguments` object, `id`); the client executes and sends a new request with `input` containing `function_result` steps (`call_id`, `result` as text/image content or object/string) plus `previous_interaction_id`; `tools`/`generation_config` are interaction-scoped and must be re-specified each turn. In stateful mode the server owns history; in stateless mode (`store:false`) the full history must be replayed, including all `thought`/`function_call` steps exactly, plus the new `function_result`; dropping signatures causes `missing_thought_signature`. Built-in tools (search, code execution, URL context, file search, maps, computer use, MCP) execute server-side as typed call/result steps whose signatures must be replayed statelessly.

**Streaming.** SSE event types: `interaction.created`, `interaction.status_update` between steps, then per step `step.start` → `step.delta`(s) → `step.stop`, then `interaction.completed`; `error` carries `{code,message}`. Delta types include `text`, `image`, `audio`, `thought_summary`, `thought_signature`, `arguments_delta` (partial JSON for function calls, accumulate it). `step.start` for a function call delivers name/id and empty arguments. `interaction.completed` does not include `steps`, so clients assemble them or `GET` the interaction; events carry `event_id`, and `GET /interactions/{id}?stream=true&last_event_id=...` resumes a dropped stream. Synchronous HTTP connections close after ~60 s, hence `background:true` for long tasks.

**Thinking.** Thinking models emit `thought` steps with a required encrypted `signature` and optional `summary`; `generation_config.thinking_summaries` (`auto|none`) opts into summaries, and `thinking_level` (`minimal|low|medium|high`, model-dependent; on by default for most current models) controls effort. Stateful mode manages signatures server-side; stateless mode must resend all thought blocks and built-in-tool signatures. `max_output_tokens` includes thinking tokens; hitting it yields `status:"incomplete"` while still billing thoughts, so lower `thinking_level` instead. Gemini 4 Argon has a shared 1M context and a 65,536-token synchronous output cap; larger outputs need `background:true` long decode (up to 1,048,576).

**State, caching, background, agents, limits.** Default `store:true`; retention ~55 days paid / ~1 day free (configurable in AI Studio); `previous_interaction_id` preserves only history — `tools`, `system_instruction`, `generation_config` are interaction-scoped and must be repeated. `store:false` prevents chaining and is incompatible with `background:true` (400). Implicit caching only: ~4,096 input tokens minimum on current models; Gemini 4 loops fully cache the prior request only when the new request adds ≥2,050 tokens. `background:true` is required for long agents, Deep Research, and long decode; it needs `store:true` and supports polling, stream reconnects, cancel, webhooks. Managed agents are first-class: Deep Research (`deep-research-preview-04-2026`, `deep-research-max-preview-04-2026`), Antigravity (`antigravity-preview-09-2026`, a hosted Linux sandbox on Gemini 3.8 Flash), and custom agents via `agent`/`agent_config`/`environment`. Documented limits: no Batch API, no Python automatic function calling, no explicit caching; remote MCP is described as unavailable for Gemini 3 despite the `mcp_server` tool. Model mixing also requires compatible output modalities.

**`generateContent` in practice (fourth protocol).** `POST /v1beta/models/{model}:generateContent` with `contents`, `tools`, `system_instruction`, `generation_config`; response `candidates[{content:{role,parts}, finishReason, index}]` and `usageMetadata`; stateless, client-managed history. It remains the right choice for batch processing, explicit caching, existing integrations, and Python automatic function calling. Here thinking has no dedicated thought blocks; signatures attach to parts, unlike Interactions' dedicated `thought` steps.

**Assessment.** Errors: 429 `RESOURCE_EXHAUSTED` / 503 `UNAVAILABLE`; retry with exponential backoff; streams emit an `error` event. Pros: one surface for models and agents; observable typed steps; server state and cache-friendly multi-turn; background/long decode; strongest native multimodal and Google-grounded tools. Cons/pitfalls: young API with a major 2026 schema break (`outputs`→`steps`, `response_mime_type`→`response_format`, `Api-Revision` transition, legacy schema removed 2026-06-08); stateful-by-default retention; interaction-scoped parameters silently lost if not repeated; thought-signature replay; create responses omit `user_input` steps. Best fit: Gemini-native coding/research agents, Deep Research/Antigravity workflows, long-running background agents, multimodal pipelines; fall back to `generateContent` for batch and explicit caching.

## (d) Cross-cutting differences

**Where state lives.** Spectrum: Chat Completions stateless; Messages stateless with server caches and beta compaction; Interactions defaulting to server state (`store:false` available); Responses defaulting to server state with durable Conversations and stateless encrypted-reasoning mode. Central trade-off: server-owned state buys caching, reasoning continuity, and less replay code but complicates ZDR, debugging, failover, and history edits; client-owned state eases branching, redaction, and multi-provider gateways but pushes cache-prefix and reasoning-integrity work onto the implementer.

**Tool-loop shape.** All four are client-executed for custom functions; Responses, Messages, and Interactions also run hosted tools server-side within a request. Wire shapes differ (Chat `tool` roles; Responses Items with `call_id`; Messages `tool_result` with `tool_use_id`; Interactions `function_result` steps with `call_id`), as do termination signals (`finish_reason:"tool_calls"`, an output containing `function_call`, `stop_reason:"tool_use"`, `status:"requires_action"`). Adapters must translate call IDs, argument encodings, and error flags, and replay the assistant's full turn including thinking.

**Reasoning round-trip is the hard part.** OpenAI, Anthropic, and Gemini all require opaque signed reasoning replayed untouched (encrypted items / `signature` / thought signatures); Chat Completions discards reasoning per turn. Anthropic is strictest: replay order and prefix immutability are validated, so history rewriting invalidates preserved thinking and cache. Cross-provider fallback must never forward one provider's reasoning blobs to another.

**Streaming.** Chat Completions is raw token-delta streaming with a sentinel; the others are semantic event streams where consumers branch on type. Responses has the richest lifecycle; Messages the most stable named-event grammar; Interactions is step-oriented but its lifecycle names are still settling. All can fail mid-stream (`error`, `response.failed`, `incomplete`), so clients need idempotent resumption or restart.

**Caching.** Automatic prefix caching (OpenAI/Gemini implicit) versus explicit markers (Anthropic `cache_control`, OpenAI GPT-5.6+ breakpoints). Prefix stability is the shared invariant: editing tools, system prompts, thinking configuration, or earlier messages breaks hits; Anthropic can make those edits hard errors under preserved thinking. Stateful chaining improves hit rates.

**Built-in tools.** OpenAI has the broadest catalog (search, file search, code interpreter, computer use, image generation, MCP, shell, apply_patch, tool search) in one server-side loop; Anthropic is comparably deep (web search/fetch, code execution, bash, text editor, memory, computer/browser, tool search) plus a beta MCP connector; Gemini emphasizes Google-native grounding, code execution, file search, computer use, and MCP; Chat Completions has none.

**Portability and implications (inference).** Chat Completions is the lowest common denominator (Anthropic and Gemini both expose OpenAI-compat endpoints) and the safest internal normalization format, but cannot represent provider-native reasoning, server tools, or state. Gateways should keep a native escape hatch per protocol, normalize only message/tool-call shapes, and treat reasoning blobs as opaque. OpenAI-first: Responses + WebSocket/background, `store` matched to ZDR, compaction, exact `phase`/reasoning replay. Claude-first: Messages + adaptive thinking + append-only history + caching + beta server tools/compaction. Gemini-first: Interactions stateful with `background:true`, signatures managed server-side.

## (e) Confidence, ambiguities, and open questions

- **Gemini versioning:** GA per overview; the reference exposes `/v1` (stable) and `/v1beta` (labeled beta) while most docs use `/v1beta`. Use `/v1` as the stable target and expect field churn.
- **Gemini streaming names:** the streaming guide lists `interaction.status_update`; the breaking-changes guide lists `interaction.in_progress`/`interaction.requires_action`. Branch on `event_type`, ignore unknown events, resume via `GET ?last_event_id=`.
- **Gemini remote MCP/tools:** overview says Gemini 3 lacks remote MCP, but the reference defines `mcp_server`; error text also names `filesystem`/`bash`, absent from the `Tool` types. Support is likely model/agent-dependent.
- **Anthropic versions/betas:** versioning names `2026-10-21` as current while some examples show `2023-06-01`; compaction, MCP connector, inline tool changes, and preserved-thinking controls are beta and platform-gated.
- **OpenAI model gating:** WebSocket, compaction, `pro` mode, `phase`, `configuration_update` are documented for specific model families (GPT-5.5+/GPT-6); per-model availability was not exhaustively verified.
- **Inference:** the GPT-6 Astra Chat tool-calling implication and the coding-agent recommendation matrix are judgments, not doc quotes.

## (f) Source index

All URLs accessed **2026-10-25**; raw `.md`/`.md.txt` copies are under `research/sources/`.

**OpenAI:** [Chat create](https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create) · [Chat streaming events](https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events) · [Responses create](https://developers.openai.com/api/reference/resources/responses/methods/create) · [Responses streaming events](https://developers.openai.com/api/reference/resources/responses/streaming-events) · [Migrate to Responses](https://developers.openai.com/api/docs/guides/migrate-to-responses) · [Reasoning](https://developers.openai.com/api/docs/guides/reasoning) · [Conversation state](https://developers.openai.com/api/docs/guides/conversation-state) · [Compaction](https://developers.openai.com/api/docs/guides/compaction) · [Background](https://developers.openai.com/api/docs/guides/background) · [WebSocket mode](https://developers.openai.com/api/docs/guides/websocket-mode) · [Tools](https://developers.openai.com/api/docs/guides/tools) · [Prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching) · [Streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses) · [Rate limits](https://developers.openai.com/api/docs/guides/rate-limits) · [Error codes](https://developers.openai.com/api/docs/guides/error-codes) · [Function calling](https://developers.openai.com/api/docs/guides/function-calling)

**Anthropic:** [Messages reference](https://platform.claude.com/docs/en/api/messages) · [Versioning](https://platform.claude.com/docs/en/api/versioning) · [Migration 2026-10-21](https://platform.claude.com/docs/en/api/migration-2026-10-21) · [Beta headers](https://platform.claude.com/docs/en/api/beta-headers) · [Errors](https://platform.claude.com/docs/en/api/errors) · [Rate limits](https://platform.claude.com/docs/en/api/rate-limits) · [Streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) · [Thinking](https://platform.claude.com/docs/en/build-with-claude/thinking) · [Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) · [Compaction](https://platform.claude.com/docs/en/build-with-claude/compaction) · [Mid-conversation system messages](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages) · [How tool use works](https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works) · [Server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools) · [MCP connector](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector) · [OpenAI SDK compatibility](https://platform.claude.com/docs/en/cli-sdks-libraries/libraries/openai-sdk)

**Gemini:** [Interactions overview](https://ai.google.dev/gemini-api/docs/interactions-overview) · [Breaking changes](https://ai.google.dev/gemini-api/docs/interactions-breaking-changes-may-2026) · [Interactions ref v1beta](https://ai.google.dev/api/interactions-api) · [Interactions ref v1](https://ai.google.dev/api/interactions-api-v1) · [Get started](https://ai.google.dev/gemini-api/docs/get-started) · [Streaming](https://ai.google.dev/gemini-api/docs/streaming) · [Function calling](https://ai.google.dev/gemini-api/docs/function-calling) · [Thinking](https://ai.google.dev/gemini-api/docs/thinking) · [Background execution](https://ai.google.dev/gemini-api/docs/background-execution) · [Antigravity agent](https://ai.google.dev/gemini-api/docs/antigravity-agent) · [Migrate from generateContent](https://ai.google.dev/gemini-api/docs/migrate-to-interactions) · [Legacy generateContent](https://ai.google.dev/gemini-api/docs/generate-content/text-generation) · [Caching](https://ai.google.dev/gemini-api/docs/caching) · [Rate limits](https://ai.google.dev/gemini-api/docs/rate-limits) · [Interactions errors](https://ai.google.dev/gemini-api/docs/api-errors) · [OpenAI compatibility](https://ai.google.dev/gemini-api/docs/openai)
