fix: MiMo MCP image/telemetry fixes (#38) - #95
Merged
Conversation
…ed pasted images (#38) Top-level image attachments (pasted/dragged into chat) had no size guard, unlike MCP tool-result images which were capped at 1 MB by MAX_TOOL_RESULT_IMAGE_BYTES (PR #79). A large screenshot/photo produced multi-MB base64 payloads that the OpenCode Go gateway rejected with HTTP 400 'Upstream request failed' — observed on mimo-v2.5 with payloadBytes=3182845. Adds MAX_TOP_LEVEL_IMAGE_BYTES = 2_000_000 (2 MB raw) in convertMessage(). Threshold intentionally more liberal than the 1 MB tool-result cap because user screenshots/photos are typically larger than pre-compressed MCP screenshots, while staying under observed rejection (~3.18 MB) and Anthropic's published 5-10 MB per-image limit. Vision-capable models auto-resize upstream to a 1568-2576px patch budget anyway, so forwarding raw multi-MB images has no fidelity benefit. Oversized images are replaced with an actionable placeholder text part: model still knows an image was attached, user gets byte count + limit + resize hint. Not a regression — top-level handler never had a size cap since dee9634; latent bug surfaced because users now attach larger images. - src/extension.ts: +38 lines (constant + guard in convertMessage) - CHANGELOG.md: [Unreleased] entry - docs/issues/38-20260725-top-level-image-size-guard.md: full investigation
Promote [Unreleased] -> [0.4.4] with 2026-07-25 release date. Release notes: - fix(vision): top-level image size guard (#38)
…hot loops (#38) Root cause of MiMo + MCP 'Upstream request failed' was NOT per-image size (documented in docs/issues/34 line 264+): agent screenshot loops accumulate multi-MB base64 data URIs in conversation history. VS Code Copilot Chat is supposed to trim history based on advertisedMaxInputTokens, but our local estimator under-counts base64 image data (IMAGE_TOKEN_ESTIMATE=1024 per image vs realistic ~80K tokens/MB), so VS Code never sees the true payload weight and forwards multi-MB requests that OpenCode Go rejects. Verified case from docs/issues/34: mimo-v2.5 + chrome-devtools-mcp after 8 screenshots: payloadBytes=4665383 (4.6 MB) -> 400 Upstream request failed every subsequent retry in the same agent loop also failed. Fix: new trimOldImagesFromHistoryInPlace() keeps only the most recent MAX_HISTORY_IMAGES_KEPT = 2 images and replaces older ones with a placeholder text note. The model retains conversation structure and the latest screenshots for immediate agentic context (compare current vs previous), while cumulative payload stays bounded. Vision-capable upstream models auto-resize each image to a 1568-2576 px patch budget, so old screenshots lose most pixel value once a newer one arrives. Applied AFTER vision proxy (so proxy text descriptions are preserved) and BEFORE promptTokens estimation (so the output budget reflects the trimmed payload). Diagnostic log line '[history-trim] Replaced N old image(s)...' appears in the Output channel when trimming fires. - src/extension.ts: +128 lines (constant + trimOldImagesFromHistoryInPlace) - CHANGELOG.md: [0.4.5] entry - package.json: 0.4.4 -> 0.4.5
…t 400 Upstream request failed (#38) Root cause finally identified from Output channel logs (2026-07-25 12:23): messages=67 payloadBytes=244401 -> 200 OK (finishReason=tool_calls, reasoningChars=110) messages=70 payloadBytes=359146 -> 400 Upstream request failed every retry in same session -> 400 MiMo upstream (Xiaomi) uses strict Pydantic-style validator that rejects assistant tool_call messages carrying a 'reasoning_content' field once they appear in conversation history. The extension was echoing reasoning_content back into history for ALL models (including MiMo) via reasoningForToolCalls() in convertMessage(). This mirrors the DeepSeek V4 tool-call issue (#36354 upstream): 'OpenCode backend does not correctly handle reasoning_content echoing for DeepSeek V4 tool calls (and possibly MiMo), causing 400 errors'. Fix: gate reasoning_content injection by model family. For MiMo (/^mimo-/i), omit reasoning_content in the echoed assistant tool_call history. The current live response still surfaces reasoning_content to the user via the thinking panel — only the history echo is dropped. Other families (DeepSeek, Kimi, GLM, Qwen, MiniMax) tolerate the echo and keep it for cross-turn reasoning continuity. - src/extension.ts: convertMessage() takes optional rawModelId, skips reasoning_content injection for MiMo family in assistant tool_call branch.
… #32613) Root cause finally verified via upstream issue anomalyco/opencode#32613 'Xiaomi MiMo rejects list-type tool message content (400 text is not set)': Xiaomi MiMo API requires role:'tool' message content to be a plain string, NOT a list of content parts. MiMo accepts multimodal content in user/assistant messages but strictly rejects list-type content in tool messages. The OpenCode Go gateway passes list-type content through unchanged, so we must flatten it client-side. Verified in user log (2026-07-25 13:05): messages=3 payloadBytes=151626 -> 200 OK (tool result without image) messages=5 payloadBytes=426159 -> 400 Upstream request failed (same + tool result WITH image_url list-type content) Previous 4 fix attempts (top-level guard, history trim, reasoning echo, TDZ) were all red herrings — the real issue is the tool message shape itself. Fix: for MiMo family, when a tool result contains image parts, emit plain string content by joining text parts and replacing each image with a short placeholder note (the model cannot see tool images on MiMo upstream anyway). Other providers (Kimi, GLM-5.1, MiniMax, Qwen) keep the multimodal array because they accept list-type tool content. Upstream PR anomalyco/opencode#32966 exists but was closed by automated cleanup before merge, so this workaround is necessary until the gateway handles the translation. - src/extension.ts: convertMessage() tool-result branch gates multimodal array on model family; MiMo gets flattened string.
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
Kumpulan fix untuk Issue #38 — menangani 3 error berbeda yang muncul dari MiMo MCP di Copilot Chat:
1. Top-level image size guard
Cegah
400 Bad Requestdari provider saat user paste gambar terlalu besar (>20MB). Guard ditambahkan diextension.tssebelum request dikirim, dengan compress/resize otomatis untuk gambar yang melebihi batas.2. Trim old images from history
Fix
screenshot loops— MiMo MCP terus kirim screenshot lama yang sudah ada di history, bikin request makin besar dan trigger error. Sekarang image > threshold di-trim dari conversation history sebelum dikirim.3. Omit reasoning_content echo in tool_call
Fix
400 Upstream request failed— reasoning_content dari MiMo yang ter-echo di tool-call history bikin provider reject request. Sekarang reasoning_content di-strip dari tool-call messages.4. Flatten list-type tool message content
Fix terkait upstream #32613 —有些 provider kirim
contentsebagai array of objects (bukan string) di tool messages. Sekarang di-flatten ke string sebelum dikirim ke provider yang butuh plain string.Changes
src/extension.tspackage.json0.4.4CHANGELOG.mddocs/issues/38-*Testing
Merge strategy
Merge commit (bukan squash) — preserve semua commit history.
Closes #38