Skip to content

v0.3.18

Choose a tag to compare

@github-actions github-actions released this 03 Aug 11:20
· 414 commits to main since this release

Tool calls reach the client again on a --api claude / --api codex-cli seat, and a
conversation stops repeating itself.

Fixes

  • codex-cli: the client never ran a tool the model asked for

    On the Responses wire the seat announced tool calls nowhere but inside the terminal
    response.completed event. Codex CLI builds its output from the incremental item events, so it
    saw a stream that announced nothing and ran nothing — the model asked for a command, the client
    never executed it, and the turn ended with the model reporting it could not read the repository.

    A call now arrives as the item sequence the dialect defines: output_item.added
    function_call_arguments.delta.doneoutput_item.done. output_index is assigned in
    emission order and reused in response.completed, so the indices a client saw stream are the
    indices it finds in the final object — it used to be hardcoded to 0, which mis-numbered every
    item after the first.

  • claude: a second question was answered with the first question's answer

    The seat replayed the transcript through --input-format stream-json, which reads every user
    line as a live turn rather than as history. A three-message replay produced two answers, the
    first re-answering the opening message, and the streaming path forwarded both — glued together
    with no separator. What the user saw was the model repeating an earlier reply in front of the
    real one, and the same tool call issued over and over.

    The transcript now travels as one prompt, one turn. It is sent as verbatim JSON rather than
    flattened into User: / Assistant: prose, so a tool_use stays a tool_use and a tool_result
    stays a tool_result instead of becoming sentences the model has to parse back.

  • claude: every non-streaming request failed with a 502

    --input-format stream-json refuses to pair with --output-format json; the CLI exited without
    writing an answer and the seat reported "no result envelope". Claude Code sends about half its
    requests non-streamed, so this was not a rare path.

  • cli-seat: a tool call was printed to the user as raw JSON

    A model writing a shell command puts real newlines inside the JSON string. The scan that reads
    tool calls back rejected those as control characters, found no call, and handed the whole
    {"tool_calls": …} object back as prose — the user saw it printed instead of run. A malformed
    call is still forwarded verbatim, never repaired or dropped, but the seat now says so in its log
    instead of passing it off silently as an ordinary answer.

  • codex-cli: an approval refusal ended the turn instead of redirecting it

    The seat answered the app-server's approval request with {"decision": "decline"}. decline is
    not a member of that enum — the valid refusal is {"denied": {"rejection": …}}, the one form
    that carries a reason and lets the agent continue and try something else. Two more requests
    (item/fileChange/requestApproval, item/tool/requestUserInput) had no handler at all and got a
    generic JSON-RPC error. Every refusal is now recorded, so a turn that quietly gave up is
    answerable after the fact.

  • codex-cli: the model list is read from the account, not a hardcoded table

    The static list had fallen behind by two — including gpt-5.6-sol, the model Codex CLI selects
    by default, which the grid answered with "No providers available for this model". The seat now
    asks the CLI's own model/list and caches the answer per account, falling back to the static
    table when the CLI cannot be asked.

  • [apps._default.tools.apply_patch] removed from the seat config

    codex --strict-config rejects it as an unknown field, so it blocked nothing and only read as
    though it had. Codex ignores unrecognised keys silently without that flag.

Improvements

  • Thinking is streamed instead of silence. A reasoning turn sent nothing until the answer
    began — measured at 4.3s of a 5.2s turn with a dead wire. The seat now forwards the model's
    reasoning as it arrives: as reasoning items on the Responses wire and reasoning_content on
    the chat wire, never mixed into the answer text. Enabled at the source too, since codex emits no
    summaries by default. On the Anthropic wire it is deliberately dropped: a thinking block is
    replayed with a vendor signature this seat cannot mint, and an unsigned one is rejected on the
    next turn.

  • The caller's tool rules are honoured. parallel_tool_calls: false and tool_choice were
    read from no request; the tool block invited several calls at once while the client had asked
    for one. Tools the caller cannot execute — a web_search with no name, a namespace container —
    are no longer offered to the model as though it could call them.

  • output_config.effort is read. Claude Code sends its effort there alongside
    thinking: {"type": "adaptive"}; grading only the latter turned a high-effort request into a
    medium one.

  • A role: "system" message inside messages[] is no longer dropped. Claude Code sends one
    alongside the top-level system field — 6379 characters of instructions that never reached the
    model.

Full Changelog: v0.3.17...v0.3.18