Skip to content

[Provider compatibility] opencode-go/mimo-v2.6: native XML tool-call envelope reaches visible assistant text #5499

Closed

Description

@c787297017

Client or integration

Codex App

Area

Provider adapter

Summary

MiMo v2.6 emits its tool call in the Xiaomi native XML envelope, and on the routed freeform path that envelope text reaches the assistant message body, so Codex renders raw call syntax as visible output.

Envelope taken verbatim from a Codex App subagent session routed to opencode-go/mimo-v2.6-flash:

...并立即给出 canonical final。<tool_call><function=exec>const results = await Promise.allSettled([
  tools.exec_command({ cmd: "git status --short --branch", workdir: "...", yield_time_ms: 10000 }),
  ...
</parameter></function></tool_call>

The call itself executed: 2 ms later in the same turn the rollout records a custom_tool_call whose input is the identical JavaScript, so the visible text is a duplicate in the content channel. 6 of 11 exec turns in that session carried the envelope; earlier turns in the same session on opencode-go/deepseek-v4.1-flash produced 107 structured calls and 0 textual leaks.

Where the shape comes from: opencodex lowers the Codex freeform exec custom tool to a function with a single required string parameter named input (src/responses/custom-tool-compat.ts, rewriteForUpstream). MiMo renders function arguments in the <parameter=name> syntax, so the native call is <tool_call><function=exec><parameter=input>...</parameter></function></tool_call>.

Expected: a recognized XML envelope is either consumed into the structured call or stripped from content. It must not reach the client as assistant text.

Reproduction

Deterministic half - the envelope survives into the wire on the routed freeform path.

  1. ocx start (proxy on 127.0.0.1:10100), provider opencode-go as shipped (adapter: openai-chat, baseUrl: https://opencode.ai/zen/go/v1).
  2. POST to http://127.0.0.1:10100/v1/responses with the Codex wire shape:
{
  "model": "opencode-go/mimo-v2.6-flash",
  "input": [{"role": "user", "content": [{"type": "input_text", "text": "用 exec 工具列出 /tmp 下的文件。"}]}],
  "tools": [{"type": "custom", "name": "exec", "description": "Run JavaScript...", "format": {"type": "text"}}],
  "tool_choice": "auto",
  "stream": false
}

Result: HTTP 200, and the returned custom_tool_call.input begins with the literal <parameter=input>.

  1. Repeat with a replayed turn - append a custom_tool_call item and its custom_tool_call_output to input, then ask for another call. Result: HTTP 200 and the returned input ends with a stray </parameter>.

Control: the same conversation sent straight to https://opencode.ai/zen/go/v1/chat/completions (with x-opencode-session) returned clean JSON arguments and empty content in 8/8 probes covering mimo-v2.6-flash, mimo-v2.6-pro and deepseek-v4.1-flash. Zen Go parses this envelope cleanly on plain chat; the residue appears only on the routed freeform path.

Intermittent half: the content-channel leak is not reproducible on demand yet. Probes that all came back clean: a system prompt forcing plan-then-call, multi-turn replay (including replaying the envelope itself as assistant text), stream: true and stream: false, custom and JSON-schema function tools, both mimo-v2.6 ids. The observed frequency (6/11) points at a long-context trigger: the Codex code-mode system prompt, 8 declared tools, and a long replayed history.

Version

2.59.0 (bun 1.4.0 runtime). 2.60.0 is not installed because npm latest still resolves to 2.59.0 (#5385).

Operating system

macOS 26.5.2 (25F84), arm64

Provider and model

opencode-go / mimo-v2.6-flash (also seen with mimo-v2.6-pro); Codex CLI 0.155.0-alpha.9.2

Logs or error output

No error is raised; the defect is in the delivered output. The two rollout items for one leaked turn:

2026-09-22T02:34:14.528Z  response_item  message/assistant   (content ends with the XML envelope)
2026-09-22T02:34:14.530Z  response_item  custom_tool_call   name=exec  input=<identical JS, 1563 bytes>

Redacted configuration

Provider-level workaround for the secondary gap below; no credentials:

"modelReasoningEfforts": {
  "mimo-v2.6-flash": ["low", "medium", "high"],
  "mimo-v2.6-pro": ["low", "medium", "high"]
},
"modelReasoningEffortMap": {
  "mimo-v2.6-flash": {"low":"low","medium":"medium","high":"high","xhigh":"high","max":"high","ultra":"high"},
  "mimo-v2.6-pro": {"low":"low","medium":"medium","high":"high","xhigh":"high","max":"high","ultra":"high"}
}

Notes

Why the existing wrapper fixes do not cover this: #5047 / #5113 / #5151 / #5129 / #5395 all act on the JSON freeform wrapper ({"input": ...} / {"code": ...}) inside the tool-input channel. On current dev, progressiveFreeformInput is called only next to emit("response.custom_tool_call_input.delta", ...) in src/bridge/sse.ts, while the response.output_text.delta emissions are untouched. A source search for <tool_call> / <parameter= returns nothing outside the cursor adapter [TOOL_CALL] quarantine (#4815/#4816), so content is pure passthrough today.

Secondary gap on the same route: the registry declares no effort ladder for the new mimo-v2.6-* ids, so the Codex catalog advertises low...ultra while Zen Go accepts only none|low|medium|high and answers 400 Invalid request parameters for xhigh/max/ultra (the Codex global default is max). Same class as #1483 / #2410; the snippet above is the local workaround.

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

  1. xunhe730 commented on Sep 22, 2026

    @xunhe730

    We are seeing the same failure on a separate route, which suggests that the scope is broader than opencode-go.

    Environment:

    • OpenCodex 2.59.0
    • Codex App / CLI runtime 0.155.0-alpha.9.2
    • Provider: Xiaomi MiMo Token Plan (https://token-plan-cn.xiaomimimo.com/v1)
    • Adapter: openai-chat
    • Model: mimo-v2.6-flash
    • This was a main Codex session directly using MiMo, not a subagent, fallback, or native-worker relay.

    In the affected rollout, there were 4 structured custom_tool_call records. Three of them were immediately preceded by a visible assistant message containing the corresponding native XML envelope:

    <tool_call><function=exec>...
    </parameter></function></tool_call>
    

    The structured call still executed successfully, so this is the same duplicate-content behavior described here: the call appears once in the assistant text channel and once as the actual tool event. DeepSeek routed through the same local OpenCodex installation does not show this behavior.

    This also reproduced after a clean OpenCodex restart and is independent of our subagent-role configuration. It appears to be a MiMo v2.6 plus routed openai-chat normalization issue rather than an opencode-go-specific issue.

    We would appreciate a fix that consumes or removes a recognized XML tool-call envelope from assistant content when the same call has already been emitted as a structured tool event, while preserving any genuine commentary around it.

  2. marciodps commented on Sep 23, 2026

    @marciodps
    Contributor

    Same family of leak reproduced through command-code with xiaomi/mimo-v2.6-pro on 2.64.0 — there it bypasses CommandCodeToolTextFilter because the markup arrives after prose inside the same text block and the filter only holds blocks that open with the marker. Details, repro shape and a validated local patch sketch in #5698.

  3. added
    priority: P2Medium: provider/client-specific bug with a workaround, bounded enhancement tied to a tracked issue,
    on Sep 24, 2026
  4. lidge-jun commented on Sep 26, 2026

    @lidge-jun
    Owner

    Closing: this is fixed on dev by #5739 (a8d526f). OpenCode Go is pinned to the openai-chat adapter, and #5739 taught that adapter's reconciler (src/adapters/openai-chat/serialized-tool-call-content.ts, wired into both the streaming and buffered paths of openai-chat.ts) to consume MiMo's bare <tool_call><function=…> envelope from assistant text when it duplicates a structured call, and to repair doubled or <parameter=input>-prefixed arguments. Genuine commentary around the envelope is kept; only agreeing duplicates are stripped. If you still see raw call syntax on a current build, please reopen with the model id and a redacted stream.

  5. c787297017 commented on Sep 27, 2026

    @c787297017
    Author

    This still reproduces on OpenCodex 2.68.0 after #5739. Could a maintainer reopen #5499? GitHub currently reports viewerCanReopen: false for my account.

    Environment: Codex App/CLI 0.158.0-alpha.2.1; provider opencode-go; model mimo-v2.6-flash; reasoning effort high. The OpenCodex 2.68.0 package was installed before the proxy started, and the proxy started before this task.

    Redacted rollout evidence from one task on 2026-09-27 (UTC):

    • 10:36:03.222 — assistant commentary contains ordinary prose immediately followed on the same line by <tool_call><function=exec>…</tool_call>.
    • 10:36:03.223 — structured custom_tool_call for exec; its complete JavaScript input also appears in the preceding visible message.
    • 10:36:55.156 — another assistant commentary message contains the same-line XML echo (this variant includes a </parameter> close tag).
    • 10:36:55.157 — structured custom_tool_call for exec; again its complete input appears in the preceding visible message.

    Thus 2 of 14 custom tool calls in this task leaked their native XML envelope into assistant text while the corresponding structured call still executed. No prompt, tool arguments, paths, or credentials are included here.

    A direct check against the installed 2.68.0 splitAtPossibleSerializedToolCall confirms the boundary: with prose。<tool_call>... it returns hasOpenTag=false and emits the XML as text; with prose。\n<tool_call>... it returns hasOpenTag=true and holds the marker. The current parser only probes for the opening tag at line start. This matches the exact same-line shape in the original report.

    This run did not show <parameter> residue in the structured tool inputs. The confirmed remaining defect is the content-channel leak.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority: P2Medium: provider/client-specific bug with a workaround, bounded enhancement tied to a tracked issue,

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions