Repository navigation
feat(tui): show the model's thinking in the main chat - #118
Merged
openjiuwen-release-bot[bot] merged 4 commits intoOct 25, 2026
Merged
Conversation
AgentMessage's streaming machinery moves to StreamedMarkdownMessage: the throttled body sync, the held rows that keep a view following the bottom steady, the single transcript layout per height change, the chrome strip fast path and the collapsed state. AgentMessage keeps its header, copy button, timestamp and intermediate/compressed styles, and reacts through two hooks: _refresh_header and _on_final. The chat's thinking message will be the base's second subclass, so queries for AgentMessage keep meaning the answer. A streamed message now hands its text to markdown at most every 200 ms instead of 150 ms: each update parses the whole text again and allocates a new parse.
The main chat now shows the model's readable thinking (the reasoning and reasoning-summary channels the backend already publishes) as a muted "Thinking" block above what it led to, streamed with the same throttle as the answer. A completed block stays open and folds on a header click; it has no copy button, and copying replies leaves it out. Encrypted thinking has no readable text and still shows nothing. A new setting, ui.chat.show_thinking (General → Appearance, on by default), decides it for each segment when it opens; thinking already shown stays. A wire retry's InvocationContentAbandoned removes the blocks of the response it gave up, in arrival order through the render gate. When a turn whose only output was thinking failed or was interrupted, the next prompt removes it with the prompt it followed, as history does. Sub-agent and workflow views are unchanged.
A resumed session now shows the model's saved readable thinking where it showed live: above the answer it led to, as a folded "Thinking" block whose body is built the first time it opens (and then handed to the GC freeze). Whether it shows is read once per replay from ui.chat.show_thinking; with it off, replay is as before. Shown thinking before tool calls ends the tool group above it, as live; blank thinking does not. An answer that thinking followed stays the answer unless another answer follows. Thinking in a compressed part is styled as compressed. Thinking with no readable text still shows nothing. The ✓ of a turn that ended with structured hosted output is now a plan entry of its own, placed after the thinking that followed that output: live it showed when the turn ended. It reads every tool group shown thinking split the turn's last group into, as it reads the one group otherwise.
/fold now folds and unfolds the main chat's thinking blocks together with the tool groups: any one open folds them all, else all open. Thinking that is still being written stays open, as a click on its header leaves it. Replayed thinking opening this way builds its body then, as on a click, and thinking still waiting to be prepended takes the choice as it mounts. The command's description, its manual page and the guide say so.
Contributor
|
head_sha: 变更摘要本 PR 在主聊天区(TUI)中原样展示模型返回的可读思考内容:新增配置项 主要改动
|
Contributor
|
head_sha: 代码审查✅ 未发现问题 |
Contributor
openjiuwen-release-bot
Bot
merged commit Oct 25, 2026
cf34ba8
into
openJiuwen-ai:main
18 checks passed
Contributor
|
🤖 This pull request has been merged, and the target branch is up to date. |
openjiuwen-release-bot Bot
pushed a commit
that referenced
this pull request
Oct 25, 2026
feat(tui): show the model's thinking in the main chat Created-by: Ox7c13 Commit-by: Jiaqi (0x7c13) Liu Merged-by: Ox7c13 Description: Paired: [GitHub #118](#118) ↔ [GitCode !85](https://gitcode.com/openJiuwen/iCode/merge_requests/85) ## Summary **The main chat shows the model's readable thinking.** When the model service returns thinking as readable text (reasoning or a reasoning summary), it streams in grey under a **Thinking** header above what it led to. It uses the same 200 ms throttle as the answer. Encrypted thinking has no readable text and still shows nothing. - A completed block stays open, and a click on its header folds it. It has no copy button, and copying replies or the conversation leaves it out. - **New setting: Show model thinking** (`ui.chat.show_thinking`). - It is in General → Appearance, on by default. A project's settings can't set it. - It is read as each thinking segment opens, and once per replay. Thinking already shown stays. - With it off, live and replayed transcripts are as before. - **Removal:** - A wire retry's `InvocationContentAbandoned` removes the thinking of the response it gave up. - When a turn whose only output was thinking fails or is interrupted, the next prompt removes that thinking together with the prompt it followed, as history does. - **Resumed sessions** show the saved thinking where it showed live, folded. - Its body is built the first time it opens, then handed to the GC freeze. - Shown thinking before tool calls ends the tool group above it, as it does live. - The ✓ of a turn that ended with structured hosted output is now a replay entry of its own. It is placed after the thinking that followed that output. - **`/fold`** folds and unfolds thinking together with tool groups. Thinking still being written stays open. - Sub-agent cards and details, and workflow node transcripts, are unchanged. ACP already sends thinking. **Refactor:** `AgentMessage`'s streaming machinery moves to a base class, `StreamedMarkdownMessage`, which `ThinkingMessage` shares. - That machinery is the throttled body sync, the held rows, the single layout per height change, the chrome strip fast path and the collapsed state. - A streamed message now hands its text to markdown at most every 200 ms instead of 150 ms. **Removal hardening:** `app/tui/util/removal.py::remove_all_shielded` removes several widgets in one shielded operation, so a cancel (an interrupt during a retry) can't leave some of them behind. It is used for abandoned thinking, for a failed turn's prompt and its thinking, and for answer withdrawal. **Docs and notes:** - The en and zh-Hans docs are updated: getting started, sessions, the settings guide and reference, and slash commands. - The AGENTS.md "Model text" section covers the TUI thinking. The four commits are one per step: refactor → live → replay → `/fold`. Each was reviewed on its own, then all four together. ## Known limitations - A bare Continue drops landed reasoning from history, but the block already shown live stays on screen. - Thinking from a stream that was cut off isn't saved, so a resumed session doesn't show it. - Mounted thinking headers aren't relabelled when the display language changes. - Responses reasoning-summary parts are joined without a separator. ## Test plan - [x] **New tests:** - `tests/app/tui/widgets/chat/test_live_thinking.py`, `test_replayed_thinking.py` and `tests/app/tui/screens/main/test_streamed_thinking.py`; - additions to `test_replay_tail_first.py`, `tests/app/tui/util/test_removal.py` and the settings tests; - `test_replay_planner.py`: one existing test now expects the structured-output ✓ as its own entry. For each new rule, an in-memory mutant breaks it, and its test catches that mutant. - [x] **Setting off:** a randomized planner diff over 20,000 histories and main-vs-branch transcript comparisons (live and replay) found no difference from main. - [x] **Setting on:** live and replayed transcripts match in 44 scenarios built from the wire codecs' real content shapes. - [x] **Smart Test** `--paths` on all 51 changed files: 33039 passed, 60 skipped; architecture 1241 passed. The full suite was not run locally. - [x] **Gates:** `ruff check`, `ruff format --check`, `ty check` (linux, darwin, win32) and `scripts/i18n.py check`. - [x] **Review:** Codex + subagent rounds for each step and for the whole branch. The final round found nothing at medium or above. <!-- bot4-pr-sync-meta schema_version: 1 source: gitcode gitcode_repo: openJiuwen/iCode gitcode_mr: 85 github_repo: openJiuwen-ai/iCode github_pr: 118 github_branch: sync/pr-85 --> See merge request: openJiuwen/iCode!85
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.
Paired: GitHub #118 ↔ GitCode !85
Summary
The main chat shows the model's readable thinking. When the model service returns thinking as readable text (reasoning or a reasoning summary), it streams in grey under a Thinking header above what it led to. It uses the same 200 ms throttle as the answer. Encrypted thinking has no readable text and still shows nothing.
ui.chat.show_thinking).InvocationContentAbandonedremoves the thinking of the response it gave up./foldfolds and unfolds thinking together with tool groups. Thinking still being written stays open.Refactor:
AgentMessage's streaming machinery moves to a base class,StreamedMarkdownMessage, whichThinkingMessageshares.Removal hardening:
app/tui/util/removal.py::remove_all_shieldedremoves several widgets in one shielded operation, so a cancel (an interrupt during a retry) can't leave some of them behind. It is used for abandoned thinking, for a failed turn's prompt and its thinking, and for answer withdrawal.Docs and notes:
The four commits are one per step: refactor → live → replay →
/fold. Each was reviewed on its own, then all four together.Known limitations
Test plan
New tests:
tests/app/tui/widgets/chat/test_live_thinking.py,test_replayed_thinking.pyandtests/app/tui/screens/main/test_streamed_thinking.py;test_replay_tail_first.py,tests/app/tui/util/test_removal.pyand the settings tests;test_replay_planner.py: one existing test now expects the structured-output ✓ as its own entry.For each new rule, an in-memory mutant breaks it, and its test catches that mutant.
Setting off: a randomized planner diff over 20,000 histories and main-vs-branch transcript comparisons (live and replay) found no difference from main.
Setting on: live and replayed transcripts match in 44 scenarios built from the wire codecs' real content shapes.
Smart Test
--pathson all 51 changed files: 33039 passed, 60 skipped; architecture 1241 passed. The full suite was not run locally.Gates:
ruff check,ruff format --check,ty check(linux, darwin, win32) andscripts/i18n.py check.Review: Codex + subagent rounds for each step and for the whole branch. The final round found nothing at medium or above.