Repository navigation
fix: flatten top-level namespace tools for Codex 0.165+ (builds on #152) - #169
Open
thinker1008-commits wants to merge 3 commits into
Open
thinker1008-commits wants to merge 3 commits into
thinker1008-commits wants to merge 3 commits into
Conversation
…ol round-trip Newer Codex CLI builds (confirmed on v0.146.0) stop sending tool declarations in the top-level "tools" field of a /v1/responses request. Instead, every tool -- including Codex's own built-in "exec" custom tool, which fronts shell/file/git access and MCP tool namespaces -- is embedded as a developer-role "additional_tools" item inside "input". Kiro-Go only read the top-level field, so the model received zero tool definitions and correctly (but confusingly) reported having no tool access at all. - extractResponsesTools/decodeResponsesTool (responses_input.go): pull tool declarations out of "additional_tools" input items, flattening "namespace"-wrapped groups (e.g. Codex's sub-agent tools) by their own plain name. - convertOpenAITools (translator.go): accept OpenAI "custom" tools (freeform string input, no JSON schema) by pinning them to a single-"input"-field schema, so the round trip has a stable key to extract from. - responses_input.go: parse custom_tool_call / custom_tool_call_output input items analogous to function_call / function_call_output, wrapping the raw string under the same "input" key the outbound schema declares. - responses_handler.go: emit custom_tool_call output items (both the non-streaming response object and the SSE stream) for tool_use results whose tool was declared as type "custom", instead of function_call. Fixes Quorinex#151
…pace field, agent_message parsing)
Codex's spawn_agent (and the rest of its "collaboration" multi-agent v2
tool group) was completely non-functional through this proxy -- calls
either got rejected outright with a local "unsupported call: spawn_agent"
router error, or (once partially fixed) spawned an agent that never
received its actual task. Root-caused via codex-rs source and confirmed
live end-to-end against a real Codex CLI session. Three independent gaps,
all required together:
1. Codex's FunctionCall wire format carries a namespaced tool's namespace
as a sibling "namespace" field alongside the bare "name" (confirmed in
codex-rs/protocol/src/models.rs tests, e.g. {"name":"spawn_agent",
"namespace":"collaboration",...}) -- it is NOT concatenated into the
name. This proxy flattened Codex's "collaboration" namespace group but
never re-attached that namespace on the way back, so Codex's own
registry (keyed on ToolName{name, namespace}) never matched the
response and rejected the call before any network round-trip.
- OpenAITool gains a Namespace field, set by decodeResponsesTool when
flattening a "namespace" wrapper.
- responses_handler.go's toolNamespaceMap threads it onto the
"namespace" field of function_call output items, in both the
non-streaming response object and the SSE stream path.
2. Codex marks the "message" property on spawn_agent/send_message/
followup_task with a non-standard "encrypted" JSON Schema keyword for
its own client-side transport; cleanSchema now strips it like the
other Kiro-incompatible fields it already handles.
3. A spawned sub-agent's actual task arrives as an "agent_message" input
item (author/recipient/content), not a plain "message" -- previously
unparsed and silently dropped, leaving the sub-agent with no task at
all. Its content parts are tagged "input_text" or "encrypted_content";
the latter is genuinely plaintext when talking to any non-ChatGPT-
backend provider (confirmed live: {"type":"encrypted_content",
"encrypted_content":"reply with exactly: PONG"}), so both are read
as text.
Verified against real Codex CLI v0.146.0: spawn_agent now creates a
working sub-agent, delivers its task, and the parent correctly retrieves
and reports the sub-agent's actual answer via wait_agent.
Codex CLI 0.165.0 sends the "collaboration" namespace (spawn_agent,
wait_agent, ...) in the top-level "tools" field rather than in an
"additional_tools" input item. Those entries were passed straight to
convertOpenAITools, which drops anything that is not "function" or
"custom", so the model never saw the sub-agent tools.
Run top-level tools through the same decodeResponsesTool flattening used
for additional_tools so namespace children get OpenAITool.Namespace set
and are restored as {"name":..., "namespace":...} on output.
Verified end-to-end on Codex CLI 0.165.0: spawn_agent -> wait_agent
returns the sub-agent's reply.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Summary
Builds on #152 (includes its two commits) and adds one fix needed for Codex CLI 0.165.0.
On 0.165.0, Codex sends the
collaborationnamespace (spawn_agent,wait_agent, ...) in the top-leveltoolsfield, not in anadditional_toolsinput item. #152 only flattens namespaces found inadditional_tools, so the top-level namespace still reachesconvertOpenAITools, gets dropped (type is neitherfunctionnorcustom), and the model reportsspawn_agent/wait_agentas unavailable.Change
proxy/responses_input.go: run top-levelreq.Toolsthrough the samedecodeResponsesToolflattening (namespace -> child tools withOpenAITool.Namespaceset).proxy/responses_handler.go: apply it before merging theadditional_toolsextras.proxy/responses_toplevel_namespace_test.go: unit test for top-level namespace flattening.If #152 is merged first, this PR reduces to the single commit
82e05df.Test plan
go build ./...,go vet ./...f8f6071as well (different tests fail per run, same on unmodified upstream).wire_api = "responses",multi_agent_v2 = true):spawn_agent(fork_turns none, "reply with exactly: PONG") ->wait_agentreturnsPONG. Without this commit the model says the tools are unavailable.See also the comment on #152.
🤖 Generated with Claude Code