Serves eureka-body cut connector-contract-v3 and questions connector-v3-wire, connector-v2-coexistence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
29 KiB
Connector contract v3: prior art and probes
Serves: cut eureka-body:cut_spec:cut-connector-contract-v3.r1 (map line B3,
GameCult/ModelConnector), questions eureka-body:question:connector-v3-wire and
eureka-body:question:connector-v2-coexistence, and the map line connector-spend
(B3b), which is mapped from section 2 when it is next. Also answers follow-up
eureka-body:follow_up:connector-reasoning-state-cheap-providers (section 1d) and
v3-ciphertext-as-bytes (section 4).
Sections 1-3 are an Eyes facts pass (2026-10-28, session self-2026-10-28-eureka), fetched
from current primary docs; UNVERIFIED marks what it could not confirm. Section 4 is
Imagination's probes on Yggdrasil against ModelConnector ec0519d and CultLib a4eae90.
Section 5 is what Imagination drew from them for the cut. Recommendations live in the
questions; this page is their evidence.
Facts only, each with a pointer. Fetched 2026-10-28 from current docs. UNVERIFIED marks what I could not confirm. Provider docs have moved hosts: docs.claude.com -> platform.claude.com/docs; platform.openai.com -> developers.openai.com/api/docs. Model names in current Anthropic docs: Fable 5.1, Mythos 5.1, Opus 5.5, Sonnet 5.5, Haiku 5.5 (and older).
1. Opaque / reasoning state a caller must replay
1a. Anthropic Messages API
Pointers: T = https://platform.claude.com/docs/en/build-with-claude/thinking ; TS = .../thinking-troubleshooting ; P = .../preserved-thinking (linked from T, NOT fetched)
- Thinking arrives as
thinkingcontent blocks (fieldsthinking,signature) andredacted_thinkingblocks (fielddata, opaque, encrypted). [T "Thinking encryption", "Redacted thinking blocks"] signatureholds the full encrypted thinking; API uses it to verify blocks were generated by Claude; opaque, "don't interpret or parse". Streaming: arrives assignature_deltain acontent_block_deltajust beforecontent_block_stop. [T "Thinking encryption"]display: "omitted"(default on newer models) returns thinking blocks with emptythinkingstring and populatedsignature; such a block is complete and must be replayed; text put into the empty field is ignored not rejected. [TS "The thinking field is empty"; T "Preserving thinking blocks" Note]- Replay rules [T "Thinking with tool use", "Preserving thinking blocks"]:
- Required: within a tool-use turn, pass thinking blocks back, complete and unmodified, with the
tool_useblock they accompanied. - Recommended: across turns pass everything back. Allowed: outside tool use, omit prior turns' thinking.
- A tool-use loop is one assistant turn. Consecutive thinking blocks in the latest assistant message must match what the model generated: no reorder, edit, partial drop; includes
redacted_thinking. - Filtering by
block.type == "thinking"silently dropsredacted_thinkingand breaks the protocol. [T "Redacted thinking blocks" Tip]
- Required: within a tool-use turn, pass thinking blocks back, complete and unmodified, with the
- Errors [TS]:
- Modified/dropped in latest assistant message: 400
invalid_request_error, message contains "thinkingorredacted_thinkingblocks in the latest assistant message cannot be modified". - Signature present but fails verify (truncated/altered/empty): 400 "Invalid
signatureinthinkingblock" (ends there; no prefix clause). - Fable 5.1, Opus 5.5, Sonnet 5.5, Haiku 5.5: replayed block valid only while
system,toolsand preceding messages are unchanged ("prefix check"): 400 "...The block is bound to a different conversation. Remove the block, or setthinking.block_binding.prefix_mismatch_behaviorto "drop_block"." Enforced by default for accounts created on/after 2026-08-31; older accounts only when the request sets that field. Option needs beta headerthinking-binding-controls-2026-08-01; response then carries aninput_transformationsarray listing dropped blocks. [T "Preserved thinking"; TS] - Mid-turn toggle of thinking: no error; API silently disables thinking for that request and may strip blocks. [T "Thinking with tool use"]
- Manual mode (
type:"enabled"): final assistant turn must start with a thinking block;type:"enabled"is 400 on 4.7+ models. [extended-thinking page "Turn structure in manual mode"; TS table]
- Modified/dropped in latest assistant message: 400
- Earlier-turn thinking stripped or kept: per model. Keep all prior turns: Opus 4.5+, Sonnet 4.6+, Haiku 5.5, Fable/Mythos 5/5.1. Keep last turn only: earlier Opus/Sonnet and Haiku through 4.5; API strips automatically on pass-back. Kept blocks bill as input. Override:
clear_thinking_20251015context-editing strategy. [T "Thinking block preservation by model", "Preserving thinking blocks"] - Portability:
- Across platforms:
signaturevalues are compatible across Claude API, Bedrock, Google Cloud (Google managed); exception: Sonnet 5.5 and Haiku 5.5 blocks work only in the producing account or a linked account. [T "Thinking encryption"; P#account-bound-thinking not fetched] - Across models: "readable only by the model that produced it and certain other models"; API drops unreadable blocks without error and without billing. Fable 5.1 reads Opus 5 and (Claude API) Opus 5.5 blocks; Opus 5/5.5 do NOT read Fable 5.1 blocks; Fable 5.1 and Mythos 5.1 read each other only. Switching to them keeps reasoning; away from them drops it. [T "Thinking block preservation by model: Switching models", "Preserved thinking"]
- Across platforms:
- Prompt caching [T "Thinking and prompt caching"]:
- Thinking config and resolved effort are rendered into the prompt; changing mode,
budget_tokensor effort invalidates message-level breakpoints (and may invalidate tool/system ones). - Thinking blocks in a tool loop are cached automatically with tool results and count as input when read from cache.
- Last-turn-only models: after a non-tool-result user message, earlier thinking is stripped from context/cache.
- 1-hour cache suggested for long thinking sessions.
- Thinking config and resolved effort are rendered into the prompt; changing mode,
- Billing: thinking tokens billed as output;
usage.output_tokens_details.thinking_tokensreports them (finalmessage_deltawhen streaming). [extended-thinking page "Budget rules and tuning"]
Stop reasons (https://platform.claude.com/docs/en/build-with-claude/handling-stop-reasons, first 100k chars read):
end_turn,max_tokens,stop_sequence,tool_use,pause_turn(server-tool loop hit iteration limit, default 10; resend assistant content to continue),refusal(HTTP 200,stop_detailsnames policy category, null for other reasons),model_context_window_exceeded(treat as truncated).- Streaming:
stop_reasonis null inmessage_start, appears inmessage_delta.max_tokenscan end in an incompletetool_useblock.
Usage object (https://platform.claude.com/docs/en/build-with-claude/prompt-caching "Usage fields"):
input_tokens= tokens after last cache breakpoint (uncached);cache_creation_input_tokens= 5m + 1h writes;cache_read_input_tokens;cache_creation{ephemeral_5m_input_tokens, ephemeral_1h_input_tokens};output_tokens. total_input = cache_read + cache_creation + input_tokens.output_tokens_details.thinking_tokens(above).server_tool_useandservice_tierfields: only seen via fetch-tool general-knowledge answer, API reference page was truncated before the schema: UNVERIFIED against docs. Token counts depend on tokenizer: Claude 4.7+ models (incl. Fable/Mythos 5/5.1) count ~30% more tokens for the same text. [token-counting page "Supported models", "Token counts on Claude Fable and Claude Mythos models"]- Min cacheable prompt: 512 (Fable/Mythos/Opus 5.5/Opus 5/Sonnet 5.5/Haiku 5.5), 1024 (Opus 4.8, Sonnet 5/4.6/4.5), 2048 (Opus 4.7, Mythos Preview), 4096 (Opus 4.6/4.5, Haiku 4.5). Lookback 20 block positions. [prompt-caching page]
1b. OpenAI Responses API
Pointer: https://developers.openai.com/api/docs/guides/reasoning (via fetch summary)
- Reasoning items appear in
output; opaque, no reasoning text returned (optionalsummary). Example item id prefixrs_; page gives no other id rules. - Stateless mode (
store:falseor ZDR org): reasoning items includeencrypted_contentby default;include: ["reasoning.encrypted_content"]still accepted as legacy/no-op. - Replay: stored ->
previous_response_id. Manual -> pass prior output items. Function calling: pass back reasoning items returned with the last function call plus outputs; with several calls in a row, pass back all reasoning, call and output items since last user message.all_turnswithstore:false: keep every output item (encrypted reasoning, assistantphase) and replay full history.reasoning.contextin the response reports effective mode (current_turn/all_turns). - Other models: reusable only within the same model family; switching families, API omits incompatible reasoning even with
all_turns(GPT-5.6 Sol/Terra/Luna interchange, not GPT-5.5). - Usage:
usage.output_tokens_details.reasoning_tokens,usage.input_tokens_details.cached_tokens; reasoning billed as output.
1c. Google Gemini
Official pages fetched were thin or truncated:
- https://ai.google.dev/gemini-api/docs/thought-signatures redirects to thinking guide; page that did render describes the Interactions API: signatures are "encrypted representations of the model's internal reasoning"; limited to
thoughtsteps and built-in tool steps; stateless mode "MUST always resend allthoughtblocks exactly as they were received"; stateful (store:true,previous_interaction_id) server handles it; ongenerateContentsignatures "can be attached to any part, such as living insidefunctionCallparts or the final part of a response"; streamingthought_signaturedelta arrives in last delta beforestep.stop; a thought block may carry only a signature. - https://ai.google.dev/gemini-api/docs/gemini-3 : stateless callers must include thought blocks with signatures (summary only).
generateContentfunction-calling rules (first functionCall part in parallel calls, each step in sequential, 400 on missing,skip_thought_signature_validatordummy): UNVERIFIED in official docs (Vertex page docs.cloud.google.com/vertex-ai/generative-ai/docs/thought-signatures body was truncated in fetch).- Observed third-party evidence (not official): error text "Unable to submit request because function call
Xin the N. content block is missing athought_signature" and reports that only the first 1-2 parallel calls get signatures, https://discuss.ai.google.dev/t/gemini-3-flash-preview-inconsistent-thought-signature-generation-in-parallel-function-calls-causes-400-errors-and-potential-silent-data-loss/118936 ; thread quotes Google doc line "If you use the official SDKs and standard chat history, thought signatures are handled automatically". Text turns without a signature reportedly don't 400 (apiyi FAQ, https://docs.apiyi.com/en/faq/gemini-thought-signature-missing, third party). "Signature present but fails validation" is a distinct 400 reported when route/model/key changes mid-conversation (same FAQ, third party).
1d. OpenAI-compatible providers (cheap-providers follow-up)
| Provider | Field | Form | Must replay in tool loop | Across turns | Pointer |
|---|---|---|---|---|---|
| DeepSeek (deepseek-flash, deepseek-v4-pro on page) | reasoning_content (sibling of content) |
plaintext | Yes. With tools: omitting it -> documented 400. Each sub-request in a turn's loop must carry that turn's reasoning even if no tool call |
With tools: pass back all previous turns' reasoning (concatenated into context). Without tools: ignored if sent, not carried | https://api-docs.deepseek.com/guides/thinking_mode |
| Moonshot Kimi | reasoning_content at level of content |
plaintext | Within a task keep all and send each request; append complete assistant message unchanged. Page names no error for omission | Per model: kimi-k3.1-pro/k3 require it multi-turn; k2.7-code always keeps; k2.6 drops by default unless thinking.keep:"all" |
https://platform.kimi.ai/docs/guide/use-kimi-k2-thinking-model (page covers k2.6/k2.7/k3, not "K2 thinking" naming) |
| Zhipu GLM (z.ai) | reasoning_content; request thinking:{type, clear_thinking} |
plaintext; page silent on signing | Return with the assistant message that made tool calls; interleaved thinking on by default | "Preserved thinking": clear_thinking:false keeps earlier turns (default on for Coding Plan endpoint, off standard); when on, must return complete unmodified; reorder/edit "may degrade" and lowers cache hits. GLM-5.3 cannot disable thinking |
https://docs.z.ai/guides/capabilities/thinking-mode |
| Qwen (Alibaba Model Studio) | reasoning_content; enable_thinking, thinking_budget, preserve_thinking |
plaintext | Page doesn't say; links to function-calling docs | preserve_thinking:true (qwen3.8-max, 3.7-max/plus, 3.6-plus, some Kimi) makes model read historical reasoning, billed as input; omission not an error |
https://www.alibabacloud.com/help/en/model-studio/deep-thinking |
| MiniMax (page = M3.1-Flash; M2 not on that page) | reasoning_content (OpenAI) / thinking blocks (Anthropic SDK) |
plaintext | Append entire assistant message incl. reasoning_content; "essential" for best performance; no benchmark or error stated. Thinking is optional per turn |
n/a | https://platform.minimax.io/docs/guides/text-m2-function-call . M2-specific <think> tags / reasoning_details / reasoning_split: UNVERIFIED (not on fetched page) |
- None of these pages describe signatures or encryption; the DeepSeek 400 is the only documented hard failure.
1e. Multi-provider libraries
- LiteLLM (https://docs.litellm.ai/docs/reasoning_content):
reasoning_contentstring on message "returned across all providers";thinking_blockslist (Anthropic only;type,thinking,signature) must be included in the assistant message withtool_callswhen returning tool results;provider_specific_fieldsunspecified (Bedrock examplereasoningContentBlocks[].reasoningText{signature,text}). Known failure: Anthropic 400 whenthinking_blocksmissing ("assistant message doesn't start with a thinking block"). OpenAI-compatible clients (LibreChat, Open WebUI, Vercel AI SDK) ignorethinking_blocksand lose them. Anthropic->DeepSeek switch needsdrop_params=True.litellm.modify_params=Truedisablesthinkingfor a turn when blocks are missing. - OpenRouter (https://openrouter.ai/docs/guides/best-practices/reasoning-tokens):
reasoning_detailsarray atchoices[].message/choices[].delta; common fieldsid(string|null),format,index; typesreasoning.summary(summary),reasoning.text(text, optionalsignature),reasoning.encrypted(data; may stream as[REDACTED]).formatvalues: unknown, openai-responses-v1, azure-openai-responses-v1, bedrock-openai-responses-v1, bedrock-xai-responses-v1, xai-responses-v1, meta-responses-v1, anthropic-claude-v1 (default), google-gemini-v1. Must preserve full consecutive sequence, no rearrange/modify.reasoningstring field for plain-string models;reasoning_contentalias. Doc claims switching OpenAI/Anthropic works without code change. - Vercel AI SDK:
sendReasoning(default true) controls whether reasoning is included in requests; Anthropic provider surfacesproviderMetadata.anthropic.inputTransformations;blockBinding.prefixMismatchBehavioroption;providerOptionsescape hatch; between-tools thinking blocks "sent back unchanged on the next step" (https://ai-sdk.dev/providers/ai-sdk-providers/anthropic, https://ai-sdk.dev/docs/ai-sdk-core/reasoning). Thinkingsignature/redactedDataparts: UNVERIFIED (not on fetched pages). - Cross-provider replay failure evidence: LiteLLM page above (Anthropic<->DeepSeek, UI clients dropping fields); Gemini third-party reports of dropped
thoughtSignatureby OpenAI-compatible clients (https://discuss.ai.google.dev/t/openai-api-compatibility-broken-due-to-thought-signature-on-gemini-3-pro-preview/109823 , title only, body not fetched).
2. Spend accounting and budget enforcement
2a. Usage and money
- Anthropic usage: tokens only (fields in 1a); no money field seen in docs fetched. Cost must be computed from pricing page. UNVERIFIED that the full Message schema has no cost field (schema page truncated).
- OpenRouter:
usage.cost= total charged in credits;usage.cost_details.upstream_inference_cost(BYOK only via generation endpoint);usage.prompt_tokens_details.cached_tokensand.cache_write_tokens;usage.completion_tokens_details.reasoning_tokens;usage:{include:true}deprecated/no effect, full usage always present; streaming usage in last SSE message. https://openrouter.ai/docs/guides/guides/usage-accounting - OpenAI: tokens (1b).
2b. LiteLLM
- Per-request cost in header
x-litellm-response-cost; spend log field isspend(USD). The cost-tracking page does not name aresponse_costfield ormodel_prices_and_context_window.json; says it uses the "model cost map" and can sync from GitHub. https://docs.litellm.ai/docs/proxy/cost_tracking (file namemodel_prices_and_context_window.json: UNVERIFIED on page). Zero-priced usage on non-zero-rate model logs spend 0 with WARNING and counterlitellm_zero_cost_requests_total. - Budgets:
max_budgeton keys (/key/generate;budget_limitsmultiple windows), users, teams, team members, customers; globallitellm_settings.max_budget;budget_duration("30s/30m/30h/30d"; unset = never resets); reset check default every 10 min. https://docs.litellm.ai/docs/proxy/users - Timing: with budget reservation (default on) LiteLLM estimates max cost from the request and reserves before calling; rejects pre-provider if reservation exceeds budget; after response, reservation is replaced with actual cost. Batch: reservation released, final cost recorded at completion. Zero-cost models allowed over budget.
- Errors vary by scope:
ExceededTokenBudget(key),budget_exceededcode 400 or 429 (per-model),ExceededBudgetwith 401 (customer), 429 (agent sessions);fail_closed_budget_enforcement: true-> 503 when budget cannot be verified. - No DB: "Budgets require a database". Global
max_budgetfails open (warning, requests continue); key/team/user budgets unavailable ("No connected db.").
2c. OpenRouter limits
https://openrouter.ai/docs/api-reference/limits
- Per-key
limit,limit_reset,limit_remaining. HTTP 402 witherror.metadata.limit_source:openrouter_key_limit,openrouter_credits,openrouter_in_flight_budget(waitRetry-After). - In-flight budget (certain prepaid accounts): request whose estimated cost doesn't fit is rejected pre-provider even with positive balance; estimate = input tokens + completion tokens allowed by
max_tokens(fixed per-request cap if unset), excludes per-request fees/plugins/images; charged on finish. - Negative balance causes errors incl. free models.
2d. Pre-call reservation / bound patterns
- LiteLLM reservation (2b) and OpenRouter in-flight estimate (2c) both = estimated input + max_tokens-bounded output, hold, settle on actual.
- Anthropic token counting:
POST /v1/messages/count_tokens, same inputs as Messages (system, tools, images, PDFs, thinking blocks); returns{input_tokens}; free; separate rate limit (5,000/10,000/20,000 RPM by tier Start/Build/Scale); result is an "estimate" and may include system-added tokens not billed; no caching logic; rejects server tools, MCP connector, url/file image/document sources; uses tokenizer of passed model. https://platform.claude.com/docs/en/build-with-claude/token-counting - Portkey / Helicone / cloud billing ledgers: NOT searched (budget). UNVERIFIED.
2e. Anthropic pricing (https://platform.claude.com/docs/en/about-claude/pricing, $/MTok: input / 5m write / 1h write / cache read / output)
- Fable 5.1 and Mythos 5.1: 10 / 12.50 / 20 / 0.25 / 50 (read = 0.025x)
- Opus 5.5: 4 / 5 / 8 / 0.20 / 20 (read = 0.05x)
- Opus 5, 4.8, 4.7, 4.6, 4.5: 5 / 6.25 / 10 / 0.50 / 25
- Sonnet 5.5: 2 / 2.50 / 4 / 0.10 / 10 (read = 0.05x)
- Sonnet 5: 2 / 2.50 / 4 / 0.20 / 10 ; Sonnet 4.6, 4.5: 3 / 3.75 / 6 / 0.30 / 15
- Haiku 5.5: prompts <=100k tokens 0.10 / 0.125 / 0.20 / 0.01 / 0.50 ; prompts >100k 0.50 / 0.625 / 1 / 0.05 / 2.50 (long-context tier keyed on prompt size)
- Haiku 4.5: 1 / 1.25 / 2 / 0.10 / 5
- Multipliers: 5m write 1.25x, 1h write 2x, read 0.1x base unless noted above.
inference_geo:"us"= 1.1x on all categories (4.6+; 400 on earlier models). Batch API discount: table exists on page (lines show batch input/output at about half), exact multiplier not extracted. Multipliers stack with batch and residency. - Opus 5.5 / Sonnet 5.5 / Haiku 5.5 / Fable 5.1 listed as current at fetch time.
3. Wire choice for a typed local RPC
3a. MCP (spec revision 2026-07-28 is "latest" at fetch)
- JSON-RPC 2.0, UTF-8. Standard transports: stdio (newline-delimited) and Streamable HTTP (POST per message; JSON or request-scoped SSE reply). https://modelcontextprotocol.io/specification/latest/basic/transports
- Custom transports allowed; those over reliable byte streams (Unix sockets, TCP) "SHOULD reuse the stdio framing" (same page).
- Versioning: latest revision has NO initialize handshake; every request carries protocol version in
_meta.io.modelcontextprotocol/*(HTTP alsoMCP-Protocol-Versionheader); unsupported -> error -32022 withdata{supported[], requested}; client retries;server/discoverMUST be implemented. Legacy (<= 2025-11-25) usedinitializenegotiation. Dual-era servers MAY serve both on one endpoint, selecting by how the client opens (modern_metavsinitialize). https://modelcontextprotocol.io/specification/latest/basic/lifecycle (rendered as "Versioning and Compatibility") - Binary: base64 strings paired with
mimeType(ImageContent.data,AudioContent.data,BlobResourceContents.blob). - Error codes: -32700, -32600, -32601, -32602, -32603 (JSON-RPC); -32020 HEADER_MISMATCH, -32021 MISSING_REQUIRED_CLIENT_CAPABILITY, -32022 UNSUPPORTED_PROTOCOL_VERSION. https://modelcontextprotocol.io/specification/2026-07-28/schema
3b. gRPC / protobuf (https://protobuf.dev/programming-guides/proto3/)
- Unknown fields preserved on parse and re-serialized (lost when going through JSON or field-by-field copy). Never reuse field numbers; reserve removed numbers/names. Adding fields, removing (no reuse), adding enum values are wire-safe; changing a field number or moving a field into an existing oneof is not. Unknown enum values preserved (open enums store int; closed enums, e.g. Java, special-case).
bytes: arbitrary byte sequence (length limit garbled in fetch).packageprevents name clashes; page does NOT frame packages as a versioning mechanism ("versioning by package names" claim: UNVERIFIED on this page).
3c. MessagePack / rmp-serde
- msgpack spec: bin 8/16/32 = 0xc4/0xc5/0xc6 with 8/16/32-bit BE length; str family separate (0xd9-0xdb, fixstr 0xa0-0xbf); ext 0xc7-0xc9 and fixext 0xd4-0xd8, app ext types 0-127, -1 = Timestamp; array 0x90-0x9f/0xdc/0xdd; map 0x80-0x8f/0xde/0xdf. Spec recommends deserializers accept bin and str8 in minor releases, serializers distinguish in major. https://github.com/msgpack/msgpack/blob/master/spec.md
- rmp-serde: serde derive treats
Vec<u8>as an array of values (about 50% overhead per docs); wrap withserde_bytesor useSerializer::with_bytesto emit bin. Re-exportsto_vecandto_vec_named(the page does not explain the struct encoding difference; array-vs-map: UNVERIFIED here). Unknown-field handling not covered. https://docs.rs/rmp-serde/latest/rmp_serde/ - Cap'n Proto: not searched. UNVERIFIED.
3d. Keeping two wire versions alive
- MCP dual-era server (3a): one endpoint serves both generations; discriminator is how the first message opens (per-request
_metavsinitialize); client probesserver/discover(stdio) or attempts modern request and inspects 400 body (HTTP) and caches the result per server process/origin; a modern-only server SHOULD name its supported versions in the error it returns toinitialize; compatibility matrix in the versioning page. - Protobuf field-number discipline (3b).
- Version byte / frame tag, side-by-side schema ids, generic dual-stack guidance: no source fetched. UNVERIFIED.
Not done (budget)
Portkey, Helicone, Cap'n Proto, OpenAI pricing, Gemini usage fields (usageMetadata.thoughtsTokenCount, cachedContentTokenCount), Anthropic Messages API reference schema (page too large), P preserved-thinking page, Bedrock/Vertex-specific usage differences.
4. Probes (Imagination, Yggdrasil ygg-verify rust image, 2026-10-28)
Scratch clone of ModelConnector at ec0519d plus a probe-only commit (never pushed to the
forge). Baseline: cargo test --workspace and --features model-connector/daemon green, 77
tests passed in all.
- P2 Decoding the real v2 frame
tests/fixtures/wire_v2/envelope_invocation.mp(the bytes CodexConnectorfd7cec4produced): 735-byte frame for 429 bytes of ciphertext (209 of them= 128), ratio 1.713. First byte
0x96: a positional six-element array. It decrypts underparity-distinct-test-keyto callerparity-caller, requestrequest-1, modelgpt-5.4, 4 input items, 1 tool, schemagamecult.codex.transport_invocation.v2. - P2 The same envelope with the ciphertext as MessagePack
bin(serde_bytes): 526 bytes, ratio 1.226 (the rest is ids and nonce). A 1 MiB all-high ciphertext: v2 frame 2,097,251 bytes, bin frame 1,048,675; the kit'sframe_bytes_for_payload(1 MiB)= 2,097,750. - P2 The v2
TransportEnvelopetype decodes a bin-encoded ciphertext and compares equal (rmp-serde reads bin intoVec<u8>). Encoding is the only change bin needs. - P3
ProviderRequestpositional 267 bytes (0xdcarray16), named (to_vec_named) 590 bytes (0xdemap16). The v2 type decodes the named encoding and compares equal. - P4a A struct with one added field: positional encoding fails to decode in the old type ("array had incorrect length, expected 3"); named encoding decodes with the field ignored. So positional MessagePack cannot add a field without breaking every reader, and named encoding drops unknown fields silently unless the type denies them.
- P4b A v2
InputItemdecoding aReasoningStatevariant fails: "unknown variant". A v2 peer cannot read a v3 transcript at all; there is no partial compatibility to preserve. - P5 A v3 envelope schema id under v2
decrypt_invocation:Err(Envelope), before any decryption. A v2 daemon given a v3 envelope drops the connection; it never answers. - PC CultNet carriage.
CultNetMessage::OperationRequest(cultnet-rs at CultLiba4eae90) carrying the sealed envelope (named, bin ciphertext, ids at 256 bytes) as itsmessagepack-base64payload, encoded withencode_cultnet_message_to_vec(.., CultNetSchemaV0)and decoded back, round-trips equal. Frame over ciphertext: 4.45 at 429 bytes (fixed overhead), 1.354 at 64 KiB, 1.335 at 1 MiB, 1.334 at 8 MiB; encode 6.5 ms and decode 1.0 ms at 1 MiB (release). The worst case is deterministic (base64 is exactly 4*ceil(n/3)), where v2's integer array is up to 2.0x and depends on the bytes.
Source reads behind the wire question (not probes):
- CultNet's
TcpFramedtransport protocol (contracts.rs:112-119) uses the same four-byte big-endian length prefix as the kit (framing.rs:6-15; kitenvelope.rs:152-184). validate_messagerequirespayload_encoding == "messagepack-base64"on operation messages (contracts.rs:780): CultNet has no binary operation payload today.- CultNet's security options hold one connection key per server (
security.rs:51-75); the kit binds one key and epoch per caller and seals ids into the AES-GCM AAD (service.rs:19-65,envelope.rs:245-253). - Huginn already speaks this shape:
huginn-mind/src/envelope.rscarries named MessagePack as base64 incultnet.operation_request.v0, checksservice_id,payload_schemaand the operation against the decoded payload, and publishes its JSON Schema withschemars.
5. What the cut takes from this
- Reasoning item granularity follows the providers: Anthropic requires the latest assistant
turn's thinking blocks complete, unmodified and in place beside their
tool_use; OpenRouter carriesreasoning_detailsper block with an index. So oneReasoningStateitem per provider block, in transcript order, holding the connector's encoding of the whole block (Anthropic:thinkingtext andsignature, orredacted_thinkingdata, since a summarized block must come back with both). - Foreign state: every provider that tags reasoning by model drops what it cannot read across
completed turns (Anthropic drops unreadable blocks silently; OpenAI omits other families).
Only the in-flight tool exchange fails hard (Anthropic 400, DeepSeek 400). So the kit refuses
foreign state only after the last user text, and drops it before the backend sees it
elsewhere, counting the drop on the receipt (as Anthropic's
input_transformationsdoes). - Anthropic's prefix check binds a block to the unchanged system, tools and earlier messages: the caller's verbatim replay (ruling connector-opaque-state-item) is necessary, and the Claude connector's lowering must be deterministic (B4).
- The cheap providers (1d) all keep plaintext
reasoning_contentand want it replayed inside a tool loop; the item stays opaque to callers either way. - Spend (B3b): LiteLLM and OpenRouter both bound a request before the call as estimated input
plus
max_tokenstimes the output price, hold it, and settle to actual; LiteLLM without a database fails open, which ruling connector-owner refuses. Anthropic returns tokens only, so cost is the connector's arithmetic over a price table it owns. v3 therefore makesmax_output_tokensrequired, so the bound exists.