input_text and input_image parts. Core stores message boundaries, part order and image references exactly as sent and returns them unchanged in user Items. It never downloads, transcodes or repairs media. Session creation input and events.create messages share validation and admission; Sessions, events and history covers admission, request limits and errors.
Messages
A message hasrole: "user", an optional type: "message" and a non-empty content array. Event input is an array of messages; Session creation input may also be a string, which becomes one text message. An explicit null or empty message type, a string content or a string event input is invalid.
A message is valid when it has an image or at least one non-empty text part. Core never trims text. These requests return 400 invalid_request and write nothing:
- an empty
inputstring,inputarray orcontentarray; - a message whose text parts are all empty and that has no image.
["", "text"], is accepted and stored as sent.
Images
Aninput_image part carries image_url as an inline data URI: data:image/png;base64,… or data:image/jpeg;base64,…. The base64 must be canonical and the decoded image must match the declared type. Core accepts no remote URL, file_id or detail.
Admission checks the harness before anything is written; an image the harness cannot take returns 400. The Runtime must also report message image support: Core binds a Session whose input carries images only to such a Runtime, and delivery to a Runtime without it fails. Use a model that accepts images.
Whitespace-only text
Whitespace-only text such as" " or "\n\t" is valid content and is stored and returned verbatim. Whether a harness can run it is declared in its engine profile:
The rejection applies to Session creation (including streaming and
self_hosted creation) and to events.create, before any write, reservation or Turn, so a running Turn is never disturbed. Whitespace beside non-whitespace text in the same message is admitted for every harness. Whitespace is the union of Go unicode.IsSpace and ECMAScript String.prototype.trim, for example U+0085 and U+FEFF; Core admission and the Claude bridge use the same set.
Function results
Anagent.session.input.tool_result event carries success, an optional nullable error string and an optional nullable output: a string or an ordered array of input_text and input_image parts.
- Core stores the result as submitted, including which of
outputanderrorwere present, and uses it for retry identity. Public Items always carry both fields (Item rules). - The Runtime receives one ordered content list: the
outputparts, then theerrortext as a final text part. This conversion never changes the stored result. - A result with images needs a Runtime that reports function-result image support; only image-bearing results check it. When the Runtime refuses a result after admission, the Turn fails without a confirmed application, and the stored result stays readable.
Application receipts
Admission (202) does not mean the harness used the result. The pending call clears when the adapter confirms native application:- Claude Code confirms with a live root native tool result that matches the Session, call ID, success flag, exact text, block count and order, with a native image at every image position. Replayed, synthetic and Subagent records do not confirm it.
- Codex confirms with the live root dynamic-tool
item/completedobservation that matches thread, Turn, call, function name, status, success flag and the exact ordered content. A write to the harness alone does not confirm it.
Runtime boundary
The Core–Runtime wire carries messages asMessageInput for initial input, prepared start and steering, with the same ordered InputContent parts that function results use (Core–Runtime protocol). Admission checks the harness’s declared profile; binding and delivery check what the Runtime reports. Adapters own native encoding and application receipts. A text-only adapter rejects image parts instead of dropping them.
- Codex flattens a batch into its native input list with a blank-line separator between public messages. Public message boundaries stay in Core’s storage; the native history does not keep them.
- Claude Code sends native image blocks and a UUID per native user message. One public input is applied only after every message in its batch is consumed. Within one native Turn the bridge accepts at most 64 user messages, including the opening prompt; it rejects a steering batch that would exceed the bound before submitting any part of it, which ends the running Turn. The daemon requires bridge protocol 3.