Repository navigation
lm15 2.3.1 for TypeScript
A conversation that changes provider keeps working (lm15-contract 81b519f, new rule MAP-24, issue lm15-dev/lm15-contract#3; 4,269 of 4,269 checks, the same in every lm15 language; the rule is decided and waits for ratification).
- Fixed: a conversation answered by an OpenAI Responses model and continued on Claude failed on every request with
messages: text content blocks must be non-empty. OpenAI's hidden reasoning is kept in the history as a thinking part with empty text and OpenAI's own state. lm15 wrote it to Claude as an empty text block, which Claude refuses, and since the history is stored every retry failed the same way. A part with nothing for the provider it is sent to now writes nothing there, and a message left with nothing is not sent. - Your messages are not changed. Only the request leaves those parts out. Send the same history back to OpenAI and its reasoning goes with it. A stored conversation that was stuck works again as it is.
- The same on every wire, for every such part: an empty text part (what lm15 reads for a reply with no parts), a citation that quotes nothing, an empty system prompt given as parts. Anthropic, Gemini, OpenAI Responses and Chat Completions requests no longer carry an empty text block or an empty message. Checked live on 14 servers: Moonshot refuses an empty text block on all three of its endpoints, DeepSeek and Moonshot refuse an assistant row with nothing in it, Gemini refuses a message with no parts; all 14 accept the message being left out.
- What must be sent still is. Claude's own thinking with display omitted (empty text and its signature) goes back as a thinking block. A tool result is always sent; an empty output is the empty string.
- Nothing is recorded as an adaptation for this: no word anyone wrote is withheld.
- Fixed: a structured answer kept in the history was dropped on OpenAI. An assistant data part (a judgment or structured answer) reached Anthropic and Gemini as its JSON and the OpenAI Responses and Chat Completions wires not at all. It is sent as its compact JSON there too.
- Checked live with this build on
claude-opus-5-5andclaude-haiku-4-5-20251001: the history of the report (a recorded hidden OpenAI reasoning item before a tool call and before text, reasoning effort medium) is answered.
Small wire changes for requests that worked before: text joined into one string has no blank line for an empty part; a message that is only an empty string is not sent, so a request whose only message is empty now reaches the provider with no message and is refused there (13 of 14 servers); a stored conversation that carried empty blocks may miss the prompt cache once.
Not changed, and still refused by the provider when it happens (each has an issue with its receipts): thinking signed by another server of the same API family, for example DeepSeek's or Meta's Anthropic-compatible endpoint sent to Anthropic, or Azure's reasoning items sent to OpenAI (lm15-dev/lm15-contract#5); a tool loop moved to Gemini 3 or DeepSeek between a call and its result (lm15-dev/lm15-contract#6); a message whose only text is whitespace sent to Anthropic (lm15-dev/lm15-contract#4). Change model between turns, not inside one.
Nothing is removed and no API changes.