fix(runtime): codex tool loop, full-trajectory events, and protocol fidelity#593
Merged
Conversation
…idelity Follow-up to #592. Makes the codex runtime work for multi-step tool turns and forward the whole turn faithfully, by aligning the shim and the ADK mapping with the Codex Responses protocol and the genai/ADK event shapes. proxy.py (the Responses shim): - Stream `function_call` output items (output_item.added -> function_call_arguments.delta/.done -> output_item.done). Previously only message/reasoning were streamed, so a tool call was dropped and the turn ended at the model's preamble. - Backfill `status="completed"` on replayed assistant messages in `input`; Ark's Responses API requires it (MissingParameter: input.status) and Codex replays them without it on multi-step turns. translate.py (result -> ADK events): - Forward every Codex thread item in order instead of collapsing to final_response: reasoning -> thought text; commandExecution / mcpToolCall / dynamicToolCall / fileChange / webSearch -> function_call + function_response; agentMessage / plan / any text-bearing item -> text; userMessage skipped. - Coerce tool-call arguments to a dict and normalize status enums; fall back to final_response so a turn is never silently empty.
zakahan
approved these changes
Jun 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #592 (the remaining codex-runtime fixes that landed on the branch after #592 was merged).
Makes
Agent(runtime="codex")work for multi-step tool turns and forward the whole turn faithfully, by aligning the shim and the ADK mapping with the Codex Responses protocol and genai/ADK event shapes.proxy.py— the Responses shimfunction_calloutput items (output_item.added→function_call_arguments.delta/.done→output_item.done). Previously onlymessage/reasoningwere streamed, so the model's tool call was dropped, Codex never executed it, and the turn ended at the preamble ("let me look…"). Now the tool loop runs to a real answer.status="completed"on replayed assistant messages ininput. On multi-step turns Codex replays a prior assistant message withoutstatus; Ark's Responses API requires it and rejects withMissingParameter: input.status, which Codex retries until it degrades to a generic "high demand" error. Reproduced (a "state your plan, then ls" prompt failed every time before; 5/5 after).translate.py— Codex result → ADK eventsForward every Codex
ThreadItemin order instead of collapsing tofinal_response, mapping each onto the correct genai part:reasoningPart(text=…, thought=True)commandExecutionfunction_call(exec_command)+function_responsemcpToolCallfunction_call(server.tool)+function_response(result/error)dynamicToolCallfunction_call(namespace.tool)+function_response(content/success)fileChangefunction_call(apply_patch)+function_response(status)webSearchfunction_call(web_search)+function_responseagentMessage/plan/ any text-bearing itemPart(text=…)userMessageTool-call
argumentsare coerced to a dict and status enums normalized to their value. Falls back tofinal_responseso a turn is never silently empty. Previously onlycommandExecutionwas handled, so MCP/dynamic tool calls and file changes were dropped.Verification
A multi-step turn now emits, in order:
thought → text(preamble) → function_call:exec_command → function_response → thought → text(final answer), and returns the real result. ruff + pyright clean.