feat(cli): render inline terminal images - #8305
Conversation
E2E test reportResult: automated changed-path and build-gate coverage passed on commit Passed on the submitted commit
Broader regression evidenceEarlier full-suite runs on the same implementation passed all 566 core files (18,940 tests, 11 skipped) and all 31 SDK files (1,442 tests). The full CLI run passed 725 files and 16,261 tests with 6 skipped; its 11 failures were reproduced as unrelated baseline/concurrency cases (10 Hardware/manual status
|
|
Re-run triggered by the maintainer — same head Template looks good ✓ — all required sections present, bilingual body, and a concrete reviewer test plan. Problem: this is an observed, maintainer-acknowledged feature gap, not theoretical hardening. Issue #8090 documents that qwen-code drops model Direction: aligned. Inline image rendering is squarely within the terminal-UX roadmap the issue is labeled under, and the PR follows the issue's explicit recommendation of a focused render-and-forget first slice — Kitty deletion, resize replacement, and global scroll lifecycle stay deferred to #8520. Size: 1,200 production-logic lines across 19 files, 2,655 test lines across 17, and 235 lines of design doc + E2E plan. The core touch is small and additive ( Approach: the scope feels right for a first slice. It threads ordered text/image Risk: no elevated risk signals — none of the changed files match the revert-correlated high-risk paths. Moving on to code review. 🔍 中文说明由维护者触发的 re-run——head 仍为上次的 模板完整 ✓ —— 所有必需章节齐全,含双语正文与具体的评审者测试计划。 问题:这是一个已观测、且经维护者确认的功能缺口,而非理论性加固。Issue #8090 记录了 qwen-code 即使在支持图形协议的终端上,也会在核心→TUI 边界丢弃模型 方向:对齐。内联图片渲染完全落在该 issue 所属的 terminal-UX 路线图内,且 PR 遵循 issue 明确建议的聚焦"渲染即忘"首版——Kitty 删除、resize 替换、全局滚动生命周期仍延后至 #8520。 规模:1,200 行生产逻辑(19 个文件)、2,655 行测试(17 个文件)、235 行设计文档与 E2E 计划。核心改动很小且为增量( 方案:对首版而言范围合理。它把有序的文本/图片 风险:无升级风险信号——改动文件均未命中与回滚相关的高风险路径。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewReviewed at The core addition is the right shape and I verified every downstream consumer of the content event tolerates it: The renderer entry point ( Ordering across retry / fallback / cancel / resume is handled by a staging region in The one thing a maintainer should weigh (not a blocker — it's the documented first-slice scope): this is render-and-forget, so Kitty images are never deleted or re-placed on scroll/resize. That's the part most likely to need a follow-up (#8520), and the PR says so plainly. The runtime flow, for orientation: sequenceDiagram
participant P1 as Model stream
participant P2 as Turn core
participant P3 as useGeminiStream
participant P4 as inline-image-parts
participant P5 as terminal-image-renderer
participant P6 as Terminal
P1->>P2: chunk with text and inlineData parts
P2->>P3: Content event, value plus optional ordered parts
P3->>P3: stage runs to keep order across retry and resume
P3->>P4: extract ordered runs, cap images per item
P4->>P5: validated PNG data and mimeType
P5->>P5: validate base64, read IHDR, fit to cells
P5->>P6: kitty placement or chafa stdin, else placeholder
Files changed (30 of 38 shown)
TestingThis is an unattended CI run, so I did not build or execute any PR code — the signal below is the PR's own CI plus the sandboxed verification runs, read through the API. CI has settled on this head. Final CI results for
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 The skipped checks are by design, not a gap: Sandboxed verification (this head): the sponsored Real-terminal rendering: covered by the maintainer's own local real-stack verification — a real interactive TUI driven against a real HTTP model endpoint and a real stdio MCP server, on Ghostty (native Kitty placement) and tmux+ Not verified: assistant-image persistence through 中文说明代码审查审查于 核心增量形态正确,且我核对了内容事件的每一个下游消费者都能容忍它: 渲染器入口( 重试/回退/取消/恢复间的顺序由 维护者应权衡的一点(非阻塞——属文档化的首版范围):这是"渲染即忘",因此 Kitty 图片在滚动/resize 时从不被删除或重新放置。这是最可能需要后续跟进的部分(#8520),PR 也坦率说明了。 测试这是无人值守的 CI 运行,因此我未构建或执行任何 PR 代码——下方信号是通过 API 读取的 PR 自身 CI 与沙箱验证运行。 CI 已在该 head 上收敛。 沙箱验证(本 head): 真实终端渲染:已由维护者本人的本地真实链路验证覆盖——在真实交互 TUI 中驱动真实 HTTP 模型端点与真实 stdio MCP 服务器,覆盖 Ghostty(原生 Kitty 放置)与 tmux+ 未验证:助手图片经 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — clean across every stage; the remaining point is the honest residue of things no PR stage can settle here (platform coverage rides the merge queue, follow-ups #8520/#8521 are deliberately deferred), not doubt about the code. Stepping back: this is the right first slice of a maintainer-blessed feature, and it's built carefully. My independent read of the problem landed on essentially the same shape the PR took — an additive, optional The three reasons the last pass deferred are all settled, each on this exact commit:
Acting on the verdict: approving, pinned to the reviewed commit. I'm also dismissing this bot's two stale Merge is the maintainer's call — with the stale reviews dismissed and this approval in place, nothing on the bot's side blocks it anymore. 中文说明置信度:4/5 —— 各阶段均干净;扣掉的一分是 PR 阶段在此无法收口的剩余项(平台覆盖由合并队列承担,后续 #8520/#8521 为有意延后),而非对代码的疑虑。 退一步看:这是经维护者认可的功能的正确首版,且构建得很用心。我对问题的独立判断与 PR 采取的形态基本一致——在内容事件上新增可选的 上次暂缓的三个原因都已在这一提交上解决:
按结论执行:批准,并钉在已审查的提交上。同时 dismiss 本机器人的两个过期 合并由维护者决定——过期评审被 dismiss、本批准就位后,机器人一侧不再有任何阻塞。 — Qwen Code · qwen3.8-max Reviewed at |
89071ef to
7b772c9
Compare
Rebase updateRebased onto current The two initial conflicts were resolved by preserving the new Goal v3 flow while retaining ordered assistant image staging. A final hook-dependency audit exposed one additional integration edge: displayed Validation on the new commit:
The branch is now one commit ahead of and zero commits behind |
|
Please do not rebase or force-push to an active PR as it invalidates existing review comments. Note for future reference, the bots always squash all changes into a single commit automatically as part of the integration. 中文请勿对活跃的 PR 执行 rebase 或 force-push,因为这会使已有的评审评论失效。另外,供日后参考:作为集成流程的一部分,机器人始终会自动将所有改动压缩(squash)为单个提交。 |
…rminal-images # Conflicts: # packages/cli/src/ui/components/TerminalImage.test.tsx # packages/cli/src/ui/components/TerminalImage.tsx # packages/cli/src/ui/components/messages/ToolMessage.test.tsx
Upstream integration updateMerged current The important change is architectural: #8217 landed while this PR was under review, so this branch now reuses its accepted
The integration audit also fixed two boundary bugs: truncation/session-limit notices now follow the final assistant runs, and a displayed Goal-state event followed by another content chunk no longer duplicates the pre-Goal text buffer. Final validation on
The final PR diff is 25 files with 825 production additions / 132 deletions, down substantially from the earlier parallel-renderer version. The PR body and reviewer steps have been rewritten to match the new scope. |
ReviewOverviewThis PR extends the #8217 terminal-image infrastructure from workspace-file previews to model/tool Overall this is a careful, well-tested change. I verified the main correctness invariants and found no blocking bugs:
Security posture is good: size bounded before decode, canonical base64 round-trip check, no temp files, PNG bytes to Suggestions (non-blocking)
Test coverageStrong: Turn ordering (incl. thought filtering and non-PNG mime passthrough), stream lifecycle (fresh/continuation retry, fallback, cancel, MAX_TOKENS/MaxSessionTurns/SessionTokenLimit/Goal-state boundaries, thrown streams), whitespace-adjacency edge cases, resume ordering for assistant and nested tool images, compaction for both assistant and tool payloads, renderer validation (oversize, malformed base64, bad IHDR, screen-reader, env-leak, stdin bytes). The The buffer resets added to the |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
中文说明
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
— qwen3.8-max-preview via Qwen Code /review
Review follow-upAddressed all four findings in
The citation regression failed before the fix with expected Validation on
The skipped Integration Tests (CLI, No Sandbox) job is configured to run only for |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline.
中文说明
已审查——无阻断问题。 建议见行内评论。
— qwen3.8-max-preview via Qwen Code /review (v0.21.3)
doudouOUC
left a comment
There was a problem hiding this comment.
Reviewed. 1 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here. Not reviewed: reverse audit — an auditor ran and opened its brief, but no agent was launched with the prompt the CLI built — the launch was written by hand, and what the agent was actually asked is not what this skill certifies.
— qwen3.7-max via Qwen Code /review
Latest review follow-upAddressed all five suggestion threads in
Validation on this head:
All five new review threads have been replied to and resolved. The latest automated review reported no blockers, but the earlier @wenshao, when convenient, could you dismiss the stale changes-requested review or submit a formal approval if the current head looks good? |
|
Qwen Code review timed out. Qwen review timed out after 10800 seconds (of the 180-minute budget). For large PRs, retry with a longer timeout by commenting: |
|
@qwen-code /review --timeout=240 |
Review:
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline. Test Plan (not a blocker): 443 tests passed — this review observed 16885, 18976 passed; 873 tests passed — this review observed 16885, 18976 passed; 30 tests passed — this review observed 16885, 18976 passed.
中文说明
已审查——无阻断问题。 建议见行内评论。 Test Plan(非阻断):443 tests passed — this review observed 16885, 18976 passed; 873 tests passed — this review observed 16885, 18976 passed; 30 tests passed — this review observed 16885, 18976 passed。
— qwen3.8-max-preview via Qwen Code /review (v0.21.3)
|
@wenshao The synchronized-head CI run now needs fork approval: https://github.com/QwenLM/qwen-code/actions/runs/30914560077. The exact previously failing Chrome-extension package + artifact scan passes locally on |
|
Qwen Code review timed out. Qwen review timed out after 21600 seconds (of the 360-minute budget). This run already used the maximum 360 minute timeout. See workflow logs. |
|
Qwen Code review timed out. Qwen review timed out after 21600 seconds (of the 360-minute budget). This run already used the maximum 360 minute timeout. See workflow logs. |
|
@wenshao Thanks for syncing |
|
@wenshao The approved CI run |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline. Not reviewed: reverse audit — stopped before round 3 by the review time budget. Test Plan (not a blocker): 579 tests passed — this review observed 17831, 19162, 1067, 1455, 475, 2825, 444 passed.
中文说明
已审查。 建议见行内评论。 未审查:反向审计——评审时间预算不足,未能开始第 3 轮。 Test Plan(非阻断):579 tests passed — this review observed 17831, 19162, 1067, 1455, 475, 2825, 444 passed。
— qwen3.8-max via Qwen Code /review (v0.21.6)
|
@wenshao The latest automatic review completed successfully on |
|
@qwen-code /triage |
|
Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check. Scripted assertions: 55 passed · 0 failed · 55 total 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:55 通过 · 0 失败 · 55 总计 Verification reportPR 8305 — feat(cli): render inline terminal imagesVerdict: 中文摘要
Central claim and A/BCentral claim: model Harness
Base-side build note: Secondary evidence (mock-free harnesses)In-memory PNG entry point —
chafa stdin wire oracle — ReDoS ladder on the three new regexes ( Vacuity / mutation matrixWitness
Reviewer Test Plan walk-through
Targeted gates (head)
FindingsNo blocking findings. Non-blocking observations, each bounded:
Not covered
MethodologyEnvironment: CI verify container ( Evidence imagesHarness scripts and raw logs are in the workflow run artifacts (7-day retention). — Qwen Code · sandboxed verification |
|
@wenshao Thanks for approving and running verification. Current-head CI is fully green and the sandbox report says merge-ready. GitHub still shows |
|
@qwen-code /triage |
|
Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check. Scripted assertions: 55 passed · 0 failed · 55 total 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:55 通过 · 0 失败 · 55 总计 Verification reportPR 8305 — feat(cli): render inline terminal images (follow-up round)Verdict: 中文摘要
Previous-finding status table (follow-up round)
No declined or deferred rows existed in the previous report; nothing to re-measure beyond the above. Central claim and A/BCentral claim: model Harness
Base-side build note (methodology correction to the previous round's note): compiling base core in the scratch worktree required two links, not one — Secondary evidence (mock-free harnesses)In-memory PNG entry point — chafa stdin wire oracle — Vacuity / mutation matrix — witness
Re-measurement of carried finding #1 (empty-
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅ CI green on this head, sandboxed verification merge-ready (55/55), and the maintainer has verified it on real hardware and approved. Dismissing the two stale bot reviews from superseded heads alongside this.
Superseded: this gated on Integration Tests (CLI, No Sandbox), which current ci.yml only runs in the merge queue (event_name == merge_group) — it never runs at PR stage. Qwen Code CI is green on the current head b9ebfdd and the maintainer has approved it.
|
Released in v0.21.7. |








What this PR does
This PR extends the terminal-image infrastructure merged in #8217 from workspace-file previews to model and tool
inlineDatain the interactive CLI.ServerGeminiContentEventwhile keeping the existing concatenatedvaluecontract and text-only event shape unchanged.TerminalImageandterminal-image-renderer: validated and bounded to 8 MiB and 64 million pixels, rendered through native Kitty/Ghostty placement orchafastdin, with deterministic placeholders, a bounded decode/render cache, and screen-reader-safe output.--continue. Failed and cancelled tool records do not currently carry inline image parts from Core.text -> image -> textorder across fresh/continuation retry, model fallback, cancellation, finished/status boundaries, displayed Goal-state events, and later content chunks.The earlier duplicate renderer and direct iTerm2 placement implementation were removed. #8217 is now the single terminal rendering source of truth.
Why it's needed
Qwen Code currently drops model
inlineDataat the Core-to-TUI event boundary and reduces tool media to text in the display. Image-generating models, screenshots, and chart-producing tools therefore cannot show their output in the conversation. The current Core recorder also flattens assistant output when writing session history; assistant-image persistence is tracked in #8521. Issue #8090 explicitly recommends a focused render-and-forget first slice.Reviewer Test Plan
How to verify
inlineDatapart between two text parts. Expect native placement between the two text runs. Repeat with PNG media in a successful tool'sfunctionResponse.partsand after session resume. Assistant output resumes its persisted text; assistant inline images are not persisted by the current Core recorder.chafainstalled, repeat the cases. Expect ANSI symbol rows produced from PNG bytes supplied over stdin.chafa, with screen-reader mode enabled, or with invalid/non-PNG data, expect a deterministic placeholder such as[image: 1024x768 png]and no raw image sequence. Payloads above the encoded-length limit are dropped before UI history and do not produce a placeholder.Automated evidence on follow-up head
1e1a6e8a4npm run lint:ci: passed.npm run typecheck: passed for every workspace.git diff --check: passed.scan:artifacts: passed after synchronizing the fork head with currentmain.npm run buildandnpm run check:serve-fast-path-bundle(Startup bundle closure checks passed).The local environment is not a real Kitty/Ghostty session, so this update does not claim a new hardware screenshot. #8217 already carries manual Kitty, Ghostty, cmux, and Warp evidence for the reused protocol layer; this PR's new in-memory entry point is covered through forced-protocol and
chafa-stdin tests.Tested on
Risk & Scope
chafareceives the existing allowlisted child environment.value.Linked Issues
Fixes #8090
Follow-ups: #8520, #8521
中文说明
本 PR 做了什么
本 PR 基于已合并的 #8217,将其工作区 PNG 预览能力扩展到交互式 CLI 中的模型与工具
inlineData:parts,现有拼接文本value与纯文本事件形状保持不变。TerminalImage与 renderer,内存 PNG 经 8 MiB 上限、Base64、签名和尺寸校验后,走 Kitty/Ghostty 原生放置或chafastdin;不可渲染时显示确定性占位文本。--continue不会恢复助手图片。失败、取消工具记录目前不会携带 Core 生成的图片部件。文本 -> 图片 -> 文本顺序且不重复。此前 PR 内自研的重复 renderer 和直接 iTerm2 放置路径已删除,#8217 成为唯一的终端渲染实现。
验证结果
后续 HEAD
1e1a6e8a4:CLI 聚焦回归共 11 个文件、579 项测试通过,覆盖历史替换清理、内容/工具边界顺序、引用显示、跨事件图片上限、retry/fallback 重置、总像素上限与多图高度预算。lint:ci、全 workspacetypecheck、Prettier 与 diff check 均通过;同步当前main后,Chrome 扩展打包与scan:artifacts也已通过。风险与范围
本次仍是 issue triage 建议的“渲染即忘”首版;Kitty 删除、resize 重放、终端 cell 像素查询和全局滚动生命周期由 #8520 跟踪,助手图片持久化由 #8521 跟踪。复用的 #8217 renderer 当前只实际渲染 PNG,其他图片格式保留确定性占位文本。无破坏性变更。