Skip to content

feat(tui): collapsible thinking blocks with duration timer - #4598

Merged
chiga0 merged 18 commits into
mainfrom
feat/tui-thinking-display-pr2
Jun 16, 2026
Merged

feat(tui): collapsible thinking blocks with duration timer#4598
chiga0 merged 18 commits into
mainfrom
feat/tui-thinking-display-pr2

Conversation

@chiga0

@chiga0 chiga0 commented May 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Replace the always-expanded thinking display with a collapsible history block that streams reasoning above the answer and collapses on completion, with duration tracking.

  • Streaming: fixed-height (4 visual lines) tail-scrolling window showing model reasoning in real-time, with a live duration counter (∴ Thinking… 8s)
  • Committed collapsed (default): single line showing total duration (∴ Thought for 15s)
  • Committed expanded (Ctrl+O toggle): full reasoning rendered as dimmed markdown (∴ Thought for 15s + content)

Comparison

Before(main branch) VS PR

demo

Changes

  • useGeminiStream.ts: Track pendingThoughtItem state (separate from pendingHistoryItem); accumulate streamed reasoning; commit to history on Content/ToolCallRequest/Finished/Cancel/Error transitions; record thinking duration via thoughtStartTimeRef
  • ConversationMessages.tsx: Implement 3-state ThinkMessage rendering (streaming/collapsed/expanded); pre-wrap text via stringWidth for pixel-accurate visual line counting ensuring stable display height; add formatDuration helper; use past tense "Thought for Xs" on completion
  • HistoryItemDisplay.tsx: Always render gemini_thought/gemini_thought_content items; pass expanded={compactMode} and durationMs props
  • LoadingIndicator.tsx: Remove thought preview; show only phrase + timer + tokens + cancel
  • types.ts: Add durationMs field to HistoryItemGeminiThought
  • Removed: thinkingDisplayMode setting, thinkingDisplayMode.ts utility, related env var handling

Impact analysis

No breaking changes. The core data flow is unchanged:

  • gemini_thought items were already committed to UI history on main (via pendingHistoryItem). This PR only changes the carrier to a dedicated pendingThoughtItem so thinking and answer content can coexist during streaming.
  • gemini_thought is UI history only — it is NOT sent to the API. The API conversation context is managed independently by the Gemini SDK chat session.
  • Session resume behavior unchanged: gemini_thought items are not restored (same as before).

UI behavior differences vs main:

Aspect Before (main) After (this PR)
Thinking in history Always expanded, full text Collapsed to one line ∴ Thought for Xs
LoadingIndicator Shows thought subject preview Only witty phrase + timer + tokens + cancel
thinkingDisplayMode setting Exists (controls display mode) Removed (single collapsible mode)
Streaming thinking Via shared pendingHistoryItem Via dedicated pendingThoughtItem

Follow-up

  • Ctrl+O expand/collapse hint hidden: The (ctrl+o to expand) hint is intentionally hidden because Ctrl+O currently triggers compactMode toggle, which conflicts with independent thinking block expand/collapse. The expand/collapse rendering code is preserved and ready — once Ctrl+O is decoupled from compactMode (or a dedicated keybinding is added), restore the hint in ConversationMessages.tsx (marked with TODO(follow-up)).

Test plan

  • npm run typecheck passes
  • npx vitest run passes (ConversationMessages 10 tests, LoadingIndicator 21 tests, Composer 19 tests)
  • Manual: ask a reasoning question → observe streaming thinking with ticking timer
  • Manual: thinking completes → collapses to "∴ Thought for Xs"
  • Manual: verify stable display height (no flickering) during streaming

🤖 Generated with Qwen Code

@github-actions

Copy link
Copy Markdown
Contributor

📋 Review Summary

This PR implements transient thinking display in the interactive TUI, introducing two display modes (preview and loading) that prevent model thinking output from cluttering persistent scrollback. The implementation is well-structured with comprehensive test coverage across multiple components. Overall, this is a solid improvement to UX that addresses a real pain point while maintaining flexibility through configuration.

🔍 General Feedback

  • Strong architectural separation: The clear distinction between transient UI state (thought) and persistent history items is well-executed and aligns with patterns from Gemini CLI and Claude Code.
  • Excellent test coverage: Tests verify the key behavioral changes across useGeminiStream, LoadingIndicator, DaemonTuiAdapter, and resumeHistoryUtils.
  • Good configuration design: The environment variable override (QWEN_TUI_THINKING_DISPLAY) enables easy A/B comparison without code changes.
  • Design documentation: The design doc in .qwen/design/tui-thinking-display-pr2.md provides clear rationale, success metrics, and comparison matrices.
  • Minimal invasive changes: The core streaming logic is simplified by removing the complex thought-to-history path, reducing code complexity.

🎯 Specific Feedback

🟡 High

  • File: packages/cli/src/ui/hooks/useGeminiStream.ts:1526 - The setThought(null) call clears thought state on turn finish, but there's no corresponding cleanup when the turn is cancelled via handleUserCancelledEvent. While setThought(null) is called in the cancel handler (line 1012), verify this is sufficient for all cancellation paths (e.g., retry scenarios, loop detection).

  • File: packages/cli/src/ui/utils/resumeHistoryUtils.ts:344 - The conditional !config ? extractThoughtTextFromParts(parts) : '' creates a behavioral divergence between interactive TUI and standalone preview modes. This is documented but could benefit from a more explicit guard or comment explaining why the standalone picker needs different behavior (it lacks a live loading indicator).

🟢 Medium

  • File: packages/cli/src/ui/hooks/useGeminiStream.ts:948-975 - The simplified handleThoughtEvent function now only updates transient state via mergeThought. Consider renaming the function to handleTransientThought or similar to emphasize the behavioral change and distinguish it from the previous history-writing behavior.

  • File: packages/cli/src/ui/components/LoadingIndicator.tsx:45-56 - The normalizeThoughtLines helper function is defined but not visible in the provided diff. Ensure this function properly handles edge cases like multi-line subjects, empty descriptions, and very long single lines that exceed terminal width.

  • File: packages/cli/src/config/settingsSchema.ts:695-708 - The setting is marked showInDialog: false, which prevents users from changing it via the settings UI. Given this is a user-facing feature with two valid modes, consider enabling it in the dialog with clear descriptions of each mode's behavior.

🔵 Low

  • File: packages/cli/src/ui/utils/thinkingDisplayMode.ts:1 - Copyright header says "Qwen Team" while other files in the repo use "Google LLC" or "Qwen Code". Standardize the copyright notice for consistency.

  • File: packages/cli/src/ui/hooks/useGeminiStream.test.tsx:4133-4260 - The new tests introduce holdStream promises to control async timing. Consider extracting this pattern into a reusable test helper if similar patterns appear in other streaming tests.

  • File: .qwen/design/tui-thinking-display-pr2.md:135 - The design doc mentions "Windows: ⚠️ not tested" in the PR body. Consider adding a follow-up task to verify Windows terminal behavior, especially for the 2-line preview truncation logic.

  • File: packages/cli/src/ui/components/LoadingIndicator.test.tsx - Add explicit tests for the preview mode (default) to verify: (1) thought subject takes priority over currentLoadingPhrase, (2) description is shown when subject is absent, (3) both lines truncate properly at terminal boundaries.

✅ Highlights

  • Excellent design doc: The comparison matrix with specific metrics (e.g., "persistent thinking rows = 0", "max live thinking rows <= 2") provides clear, testable success criteria.
  • Thoughtful mode naming: preview and loading are intuitive names that clearly communicate the trade-off between context visibility and scrollback cleanliness.
  • Clean simplification: Removing the history-writing path for thoughts (gemini_thought and gemini_thought_content rows) actually reduces code complexity while improving UX—a rare win-win.
  • Comprehensive test updates: All affected test files (useGeminiStream.test.tsx, LoadingIndicator.test.tsx, DaemonTuiAdapter.test.ts, resumeHistoryUtils.test.ts) have been updated to reflect the new behavior, ensuring regression protection.
  • Environment override pattern: The QWEN_TUI_THINKING_DISPLAY env var follows established patterns in the codebase and enables users to experiment without committing to a permanent setting.

@chiga0

chiga0 commented May 28, 2026

Copy link
Copy Markdown
Collaborator Author

TUI Thinking Display Evidence

Automated fixture replay generated with the tui-regression-evidence workflow.

Scenario: deterministic thought stream -> tool output -> final answer, terminal width 100 columns.

Mode Persistent thinking rows after finish Max live thinking text rows Final visible rows Result
Baseline (origin/main) 5 5 18 reproduced
PR2 preview 0 2 12 passed
PR2 loading 0 0 12 passed

What this proves:

  • The comparison fixture shows the intended UX distinction in one three-panel recording.
  • preview keeps thinking text bounded to live UI and removes it from final scrollback.
  • loading removes thinking text entirely while retaining status feedback.
  • Answer/tool fixture content is unchanged between the two PR2 modes.

What this does not prove:

  • This video is a deterministic visual replay, not a live model call.
  • The implementation paths are covered by the focused unit tests already listed in the PR body.

Local artifacts generated in this worktree:

  • .qwen/e2e-tests/tui-thinking-display-pr2-evidence/thinking-display-comparison.webm
  • .qwen/e2e-tests/tui-thinking-display-pr2-evidence/final-frame.png
  • .qwen/e2e-tests/tui-thinking-display-pr2-evidence/summary.json

@github-actions

github-actions Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 76.41% 76.41% 80.13% 79.66%
Core 82.01% 82.01% 83.81% 83.92%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   76.41 |    79.66 |   80.13 |   76.41 |                   
 src               |   69.86 |    67.16 |   73.33 |   69.86 |                   
  gemini.tsx       |   61.89 |    64.28 |   71.42 |   61.89 | ...1192-1195,1207 
  ...ractiveCli.ts |   69.12 |    63.75 |   66.66 |   69.12 | ...1562-1564,1599 
  ...liCommands.ts |   84.33 |    76.38 |     100 |   84.33 | ...35,361,395,477 
  ...ActiveAuth.ts |     100 |     87.5 |     100 |     100 | 66-80             
 ...cp-integration |   56.45 |    61.18 |   82.75 |   56.45 |                   
  acpAgent.ts      |    56.3 |    61.21 |   82.94 |    56.3 | ...7015,7040-7055 
  authMethods.ts   |      92 |       60 |     100 |      92 | 33-34             
  errorCodes.ts    |       0 |        0 |       0 |       0 | 1-22              
  ...DirContext.ts |     100 |      100 |     100 |     100 |                   
 ...ration/service |   68.65 |    83.33 |   66.66 |   68.65 |                   
  filesystem.ts    |   68.65 |    83.33 |   66.66 |   68.65 | ...32,77-94,97-98 
 ...ration/session |   80.42 |    73.83 |   86.91 |   80.42 |                   
  ...ryReplayer.ts |   67.34 |     75.6 |   81.81 |   67.34 | ...54-269,282-283 
  Session.ts       |   80.13 |    72.98 |   87.83 |   80.13 | ...3944,3970-3974 
  ...entTracker.ts |   90.75 |    84.37 |   88.88 |   90.75 | ...30,194,246-255 
  index.ts         |       0 |        0 |       0 |       0 | 1-40              
  ...ssionUtils.ts |   84.21 |    78.57 |     100 |   84.21 | ...37-153,209-211 
  tasksSnapshot.ts |   94.06 |    86.66 |     100 |   94.06 | 60-66             
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...ssion/emitters |   95.44 |    92.05 |   96.66 |   95.44 |                   
  BaseEmitter.ts   |   84.61 |       70 |     100 |   84.61 | 23-24,39-40       
  ...ageEmitter.ts |   94.07 |    91.42 |     100 |   94.07 | 47-54             
  PlanEmitter.ts   |     100 |      100 |     100 |     100 |                   
  ...allEmitter.ts |   98.38 |    93.82 |     100 |   98.38 | 315-316,406,414   
  index.ts         |       0 |        0 |       0 |       0 | 1-10              
 ...ession/rewrite |    91.3 |    88.09 |   94.44 |    91.3 |                   
  LlmRewriter.ts   |      81 |       84 |     100 |      81 | ...,88-89,155-159 
  ...Middleware.ts |   96.74 |    86.84 |     100 |   96.74 | 135,143-145       
  TurnBuffer.ts    |     100 |      100 |     100 |     100 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 src/commands      |   58.87 |    86.66 |   47.82 |   58.87 |                   
  auth.ts          |     100 |    83.33 |     100 |     100 | 11,14             
  channel.ts       |   56.66 |      100 |       0 |   56.66 | 15-19,27-34       
  extensions.tsx   |   96.55 |      100 |      50 |   96.55 | 37                
  hooks.tsx        |   66.66 |      100 |       0 |   66.66 | 20-24             
  mcp.ts           |   95.45 |      100 |      50 |   95.45 | 31                
  review.ts        |   51.85 |      100 |       0 |   51.85 | 24-35,38          
  serve.ts         |   46.56 |      100 |   33.33 |   46.56 | 29-31,252-453     
 ...mmands/channel |    39.2 |    79.45 |      50 |    39.2 |                   
  ...l-registry.ts |    8.33 |      100 |       0 |    8.33 | 6-22,25-43        
  config-utils.ts  |      92 |      100 |   66.66 |      92 | 21-26             
  configure.ts     |    14.7 |      100 |       0 |    14.7 | 18-21,23-84       
  pairing.ts       |   26.31 |      100 |       0 |   26.31 | ...30,40-50,52-65 
  pidfile.ts       |   96.34 |    86.95 |     100 |   96.34 | 49,59,91          
  start.ts         |   30.98 |       52 |   69.23 |   30.98 | ...72-475,484-486 
  status.ts        |   17.85 |      100 |       0 |   17.85 | 15-26,32-76       
  stop.ts          |      20 |      100 |       0 |      20 | 14-48             
 ...nds/extensions |   85.44 |    89.39 |   81.81 |   85.44 |                   
  consent.ts       |   72.68 |       90 |   42.85 |   72.68 | ...86-142,157-163 
  disable.ts       |     100 |      100 |     100 |     100 |                   
  enable.ts        |     100 |      100 |     100 |     100 |                   
  install.ts       |    75.6 |    66.66 |   66.66 |    75.6 | ...39-142,145-153 
  link.ts          |     100 |      100 |     100 |     100 |                   
  list.ts          |     100 |      100 |     100 |     100 |                   
  new.ts           |     100 |      100 |     100 |     100 |                   
  settings.ts      |   99.15 |      100 |   83.33 |   99.15 | 151               
  uninstall.ts     |    37.5 |      100 |   33.33 |    37.5 | 23-45,57-64,67-70 
  update.ts        |   96.32 |      100 |     100 |   96.32 | 101-105           
  utils.ts         |   67.77 |    38.88 |     100 |   67.77 | ...,94-98,100-104 
 ...les/mcp-server |       0 |        0 |       0 |       0 |                   
  example.ts       |       0 |        0 |       0 |       0 | 1-60              
 ...amples/starter |       0 |        0 |       0 |       0 |                   
  example.ts       |       0 |        0 |       0 |       0 | 1-64              
 src/commands/mcp  |   90.28 |    88.88 |   83.33 |   90.28 |                   
  add.ts           |     100 |    98.03 |     100 |     100 | 293               
  approve.ts       |   76.19 |     87.5 |   66.66 |   76.19 | ...,89-99,114-124 
  list.ts          |   92.48 |    86.66 |      80 |   92.48 | ...60-162,178-179 
  reconnect.ts     |   77.71 |    78.57 |   85.71 |   77.71 | 40-53,160-182     
  remove.ts        |     100 |       80 |     100 |     100 | 21-25             
 ...ommands/review |   11.57 |      100 |       0 |   11.57 |                   
  cleanup.ts       |   17.94 |      100 |       0 |   17.94 | ...01-106,108-109 
  deterministic.ts |   13.75 |      100 |       0 |   13.75 | ...22-738,740-741 
  fetch-pr.ts      |   11.36 |      100 |       0 |   11.36 | ...80-201,203-204 
  load-rules.ts    |   11.32 |      100 |       0 |   11.32 | ...41-153,155-156 
  pr-context.ts    |    6.22 |      100 |       0 |    6.22 | ...97-312,314-315 
  presubmit.ts     |    9.35 |      100 |       0 |    9.35 | ...62-287,289-290 
 ...nds/review/lib |      30 |      100 |       0 |      30 |                   
  gh.ts            |   22.58 |      100 |       0 |   22.58 | ...49,53-54,62-69 
  git.ts           |   22.72 |      100 |       0 |   22.72 | 15-18,29-39,43-44 
  paths.ts         |   52.94 |      100 |       0 |   52.94 | ...26,37-38,42-43 
 src/config        |   91.59 |       86 |   90.17 |   91.59 |                   
  auth.ts          |   86.74 |    80.88 |     100 |   86.74 | ...40-241,257-258 
  config.ts        |   87.15 |    84.65 |   82.14 |   87.15 | ...2032,2034-2042 
  keyBindings.ts   |   96.87 |       50 |     100 |   96.87 | 201-204           
  ...ngsAdapter.ts |     100 |    94.11 |     100 |     100 | 64                
  mcpApprovals.ts  |   96.12 |    94.87 |     100 |   96.12 | 193-194,199-201   
  mcpJson.ts       |     100 |      100 |     100 |     100 |                   
  mcpServers.ts    |   92.85 |     87.5 |     100 |   92.85 | 46-47             
  ...idersScope.ts |      92 |       90 |     100 |      92 | 11-12             
  ...abledTools.ts |     100 |      100 |     100 |     100 |                   
  sandboxConfig.ts |   61.64 |    71.87 |   66.66 |   61.64 | ...54-68,73,77-89 
  settings.ts      |   79.18 |    86.71 |    87.8 |   79.18 | ...1550,1565-1568 
  ...ingsSchema.ts |     100 |      100 |     100 |     100 |                   
  ...tedFolders.ts |   96.22 |    94.33 |     100 |   96.22 | ...95-197,212-213 
 ...nfig/migration |   94.89 |    78.94 |   83.33 |   94.89 |                   
  index.ts         |   94.87 |    88.88 |     100 |   94.87 | 91-92             
  scheduler.ts     |   96.55 |    77.77 |     100 |   96.55 | 19-20             
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...ation/versions |   94.74 |    96.06 |     100 |   94.74 |                   
  ...-v2-shared.ts |     100 |      100 |     100 |     100 |                   
  v1-to-v2.ts      |   81.75 |    90.56 |     100 |   81.75 | ...28-229,231-247 
  v2-to-v3.ts      |     100 |      100 |     100 |     100 |                   
  v3-to-v4.ts      |     100 |      100 |     100 |     100 |                   
 src/core          |     100 |      100 |     100 |     100 |                   
  auth.ts          |     100 |      100 |     100 |     100 |                   
  initializer.ts   |     100 |      100 |     100 |     100 |                   
  theme.ts         |     100 |      100 |     100 |     100 |                   
 src/dualOutput    |   63.09 |    64.51 |   55.55 |   63.09 |                   
  ...tputBridge.ts |   62.94 |    65.51 |   56.25 |   62.94 | ...22-323,331-334 
  ...utContext.tsx |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-8               
 src/export        |       0 |        0 |       0 |       0 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-7               
 src/generated     |     100 |      100 |     100 |     100 |                   
  git-commit.ts    |     100 |      100 |     100 |     100 |                   
 src/i18n          |   82.47 |    75.94 |   65.71 |   82.47 |                   
  index.ts         |   63.68 |    69.56 |   53.84 |   63.68 | ...70-271,281-286 
  languages.ts     |   96.92 |    86.66 |     100 |   96.92 | 134-135,167,184   
  ...nslateKeys.ts |     100 |      100 |     100 |     100 |                   
  ...lationDict.ts |   93.33 |    66.66 |     100 |   93.33 | 15                
 src/i18n/locales  |     100 |      100 |     100 |     100 |                   
  ca.js            |     100 |      100 |     100 |     100 |                   
  de.js            |     100 |      100 |     100 |     100 |                   
  en.js            |     100 |      100 |     100 |     100 |                   
  fr.js            |     100 |      100 |     100 |     100 |                   
  ja.js            |     100 |      100 |     100 |     100 |                   
  pt.js            |     100 |      100 |     100 |     100 |                   
  ru.js            |     100 |      100 |     100 |     100 |                   
  zh-TW.js         |     100 |      100 |     100 |     100 |                   
  zh.js            |     100 |      100 |     100 |     100 |                   
 ...nonInteractive |   72.57 |    71.12 |   74.07 |   72.57 |                   
  session.ts       |   76.64 |     69.4 |   85.71 |   76.64 | ...23-824,833-843 
  types.ts         |    42.5 |      100 |   33.33 |    42.5 | ...90-591,594-595 
 ...active/control |   76.29 |    88.23 |      80 |   76.29 |                   
  ...rolContext.ts |    6.89 |        0 |       0 |    6.89 | 50-86             
  ...Dispatcher.ts |   91.66 |    91.83 |   88.88 |   91.66 | ...49-367,383,386 
  ...rolService.ts |     7.4 |        0 |       0 |     7.4 | 46-185            
 ...ol/controllers |    25.4 |    35.71 |   35.48 |    25.4 |                   
  ...Controller.ts |   36.97 |       80 |      80 |   36.97 | ...15-117,127-210 
  ...Controller.ts |       0 |        0 |       0 |       0 | 1-56              
  ...Controller.ts |   28.33 |    34.48 |      40 |   28.33 | ...64-573,588-593 
  ...Controller.ts |   14.06 |      100 |       0 |   14.06 | ...82-117,130-133 
  ...Controller.ts |   21.97 |    28.57 |   27.27 |   21.97 | ...39-451,460-489 
 .../control/types |       0 |        0 |       0 |       0 |                   
  serviceAPIs.ts   |       0 |        0 |       0 |       0 | 1                 
 ...Interactive/io |   98.01 |    93.77 |   95.23 |   98.01 |                   
  ...putAdapter.ts |   97.89 |    92.82 |   98.07 |   97.89 | ...1303,1398-1399 
  ...putAdapter.ts |      96 |     90.9 |   85.71 |      96 | 51-52             
  ...nputReader.ts |     100 |    94.73 |     100 |     100 | 67                
  ...putAdapter.ts |   98.38 |      100 |   90.47 |   98.38 | 83-84,124-125     
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/patches       |       0 |        0 |       0 |       0 |                   
  is-in-ci.ts      |       0 |        0 |       0 |       0 | 1-17              
 src/remoteInput   |   86.98 |       75 |   85.71 |   86.98 |                   
  ...utContext.tsx |     100 |      100 |     100 |     100 |                   
  ...putWatcher.ts |   88.12 |    76.08 |   91.66 |   88.12 | ...21-222,233-236 
  index.ts         |       0 |        0 |       0 |       0 | 1-8               
 src/serve         |    78.4 |    81.87 |   78.18 |    78.4 |                   
  ...sionBridge.ts |     100 |      100 |     100 |     100 |                   
  auth.ts          |   93.26 |    92.64 |     100 |   93.26 | ...07-308,311-313 
  ...temAdapter.ts |     100 |      100 |     100 |     100 |                   
  capabilities.ts  |     100 |    95.45 |     100 |     100 | 341               
  daemonLogger.ts  |   98.63 |    90.32 |   95.83 |   98.63 | 161,165           
  ...usProvider.ts |   67.01 |    51.42 |     100 |   67.01 | ...40-245,278-286 
  debugMode.ts     |     100 |      100 |     100 |     100 |                   
  demo.ts          |     100 |      100 |     100 |     100 |                   
  envSnapshot.ts   |   92.75 |       84 |     100 |   92.75 | 110-113,179-186   
  eventBus.ts      |     100 |      100 |     100 |     100 |                   
  ...oryChannel.ts |       0 |        0 |       0 |       0 | 1-14              
  index.ts         |       0 |        0 |       0 |       0 | 1-143             
  loopbackBinds.ts |     100 |      100 |     100 |     100 |                   
  ...ssionAudit.ts |     100 |      100 |   93.33 |     100 |                   
  rateLimit.ts     |   90.37 |    87.77 |   93.75 |   90.37 | ...95-297,348-352 
  runQwenServe.ts  |   68.33 |    83.54 |      32 |   68.33 | ...1373,1376-1383 
  server.ts        |   80.17 |    83.11 |   83.33 |   80.17 | ...4601,4667-4676 
  status.ts        |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...paceAgents.ts |   62.47 |    70.34 |   90.47 |   62.47 | ...1346,1356-1366 
  ...paceMemory.ts |   87.13 |    78.46 |     100 |   87.13 | ...54-361,421-428 
 src/serve/acpHttp |   65.38 |    67.03 |    93.4 |   65.38 |                   
  ...onRegistry.ts |    86.3 |    77.19 |   92.59 |    86.3 | ...34-338,409-423 
  dispatch.ts      |   56.25 |    58.89 |     100 |   56.25 | ...2469,2543-2546 
  index.ts         |   75.63 |    68.21 |    90.9 |   75.63 | ...31,734,760-762 
  jsonRpc.ts       |     100 |    96.96 |     100 |     100 | 92                
  sseStream.ts     |   93.85 |    87.87 |   84.61 |   93.85 | ...48-150,152-154 
  ...portStream.ts |       0 |        0 |       0 |       0 | 1                 
  wsStream.ts      |   91.76 |       80 |     100 |   91.76 | 43,48,91,95-98    
 src/serve/auth    |   86.86 |    79.18 |   93.87 |   86.86 |                   
  deviceFlow.ts    |   96.35 |       80 |   97.61 |   96.35 | ...1358,1453,1519 
  ...owProvider.ts |   44.24 |    74.07 |   71.42 |   44.24 | ...23-284,297,301 
 src/serve/fs      |   85.12 |    81.01 |     100 |   85.12 |                   
  audit.ts         |     100 |    96.15 |     100 |     100 | 201               
  errors.ts        |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  paths.ts         |   77.82 |    77.08 |     100 |   77.82 | ...64,493-497,510 
  policy.ts        |   90.32 |    89.18 |     100 |   90.32 | 142-150           
  ...FileSystem.ts |   84.03 |    78.55 |     100 |   84.03 | ...2031,2058-2059 
 src/serve/routes  |   75.89 |    76.51 |   94.28 |   75.89 |                   
  a2uiAction.ts    |     100 |    93.65 |     100 |     100 | 114-118,163,267   
  ...ceFileRead.ts |   94.41 |    76.92 |     100 |   94.41 | ...28-329,390-392 
  ...eFileWrite.ts |    82.1 |    60.52 |     100 |    82.1 | ...42-244,247-249 
  ...ceSettings.ts |   23.62 |      100 |      50 |   23.62 | ...10-223,230-327 
 ...kspace-service |   81.05 |     82.4 |   86.66 |   81.05 |                   
  index.ts         |   81.23 |    83.17 |   92.85 |   81.23 | ...92-497,557-622 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 src/services      |   91.93 |    90.88 |   97.56 |   91.93 |                   
  ...mandLoader.ts |     100 |    93.75 |     100 |     100 | 95                
  ...killLoader.ts |     100 |    93.33 |     100 |     100 | 48,67             
  ...andService.ts |   98.73 |      100 |     100 |   98.73 | 107               
  ...mandLoader.ts |   86.83 |    83.87 |     100 |   86.83 | ...30-335,340-345 
  ...omptLoader.ts |   75.84 |    80.64 |   83.33 |   75.84 | ...10-211,277-278 
  ...mandLoader.ts |     100 |    97.14 |     100 |     100 | 66                
  ...nd-factory.ts |   91.42 |    91.66 |     100 |   91.42 | 128,137-144       
  ...ation-tool.ts |     100 |    95.45 |     100 |     100 | 125               
  ...ndMetadata.ts |   98.21 |    96.66 |     100 |   98.21 | 83,87             
  commandUtils.ts  |      96 |     90.9 |     100 |      96 | 48                
  ...and-parser.ts |   90.69 |    85.71 |     100 |   90.69 | 63-66             
  ...ionService.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...ght/generators |    88.3 |    85.49 |   92.59 |    88.3 |                   
  DataProcessor.ts |   88.22 |    85.48 |      95 |   88.22 | ...1341,1345-1352 
  ...tGenerator.ts |   98.21 |    85.71 |     100 |   98.21 | 46                
  ...teRenderer.ts |   45.45 |      100 |       0 |   45.45 | 13-51             
 .../insight/types |       0 |       50 |      50 |       0 |                   
  ...sightTypes.ts |       0 |        0 |       0 |       0 |                   
  ...sightTypes.ts |       0 |        0 |       0 |       0 | 1                 
 ...mpt-processors |   97.27 |    94.04 |     100 |   97.27 |                   
  ...tProcessor.ts |     100 |      100 |     100 |     100 |                   
  ...eProcessor.ts |   94.52 |    84.21 |     100 |   94.52 | 46-47,93-94       
  ...tionParser.ts |     100 |      100 |     100 |     100 |                   
  ...lProcessor.ts |   97.41 |    95.65 |     100 |   97.41 | 95-98             
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/services/tips |   97.35 |    84.84 |     100 |   97.35 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  tipHistory.ts    |   92.59 |       70 |     100 |   92.59 | ...24,146,153,162 
  tipRegistry.ts   |     100 |      100 |     100 |     100 |                   
  tipScheduler.ts  |     100 |    91.66 |     100 |     100 | 55                
 src/startup       |   66.82 |    78.94 |   66.66 |   66.82 |                   
  ...reeStartup.ts |   66.82 |    78.94 |   66.66 |   66.82 | ...08-312,363-426 
 src/test-utils    |   93.71 |    83.33 |      80 |   93.71 |                   
  ...omMatchers.ts |   69.69 |       50 |      50 |   69.69 | 32-35,37-39,45-47 
  ...andContext.ts |     100 |      100 |     100 |     100 |                   
  render.tsx       |     100 |      100 |     100 |     100 |                   
 src/ui            |   69.64 |    74.85 |   59.67 |   69.64 |                   
  App.tsx          |   33.33 |       75 |   33.33 |   33.33 | 32-86             
  AppContainer.tsx |      70 |    68.37 |      50 |      70 | ...3238,3242-3246 
  ...tionNudge.tsx |    9.58 |      100 |       0 |    9.58 | 24-94             
  ...ackDialog.tsx |   29.23 |      100 |       0 |   29.23 | 25-75             
  ...tionNudge.tsx |    7.69 |      100 |       0 |    7.69 | 25-103            
  colors.ts        |      60 |      100 |   35.29 |      60 | ...52,54-55,60-61 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  keyMatchers.ts   |   95.91 |    97.14 |     100 |   95.91 | 25-26             
  ...tic-colors.ts |     100 |      100 |     100 |     100 |                   
  ...inePresets.ts |   98.28 |    89.87 |     100 |   98.28 | ...34,261,420-422 
  textConstants.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/auth       |   59.16 |    65.94 |   51.11 |   59.16 |                   
  AuthDialog.tsx   |   62.87 |     42.1 |   18.18 |   62.87 | ...03,310-332,336 
  ...nProgress.tsx |       0 |        0 |       0 |       0 | 1-64              
  ...etupSteps.tsx |   60.03 |    70.37 |      56 |   60.03 | ...87,791,800,803 
  useAuth.ts       |   94.55 |    73.52 |     100 |   94.55 | ...19-220,239-245 
  ...rSetupFlow.ts |   43.52 |    33.33 |      50 |   43.52 | ...72-393,410-453 
 src/ui/commands   |   77.77 |     81.5 |   86.39 |   77.77 |                   
  aboutCommand.ts  |     100 |      100 |     100 |     100 |                   
  agentsCommand.ts |   83.78 |      100 |      60 |   83.78 | 30-32,42-44       
  ...odeCommand.ts |   89.47 |    81.25 |     100 |   89.47 | 92-93,95-100      
  arenaCommand.ts  |   62.81 |    58.73 |   65.21 |   62.81 | ...90-595,680-688 
  authCommand.ts   |     100 |      100 |     100 |     100 |                   
  branchCommand.ts |     100 |      100 |     100 |     100 |                   
  btwCommand.ts    |   94.32 |    77.41 |     100 |   94.32 | 35-36,114-119     
  bugCommand.ts    |     100 |    77.77 |     100 |     100 | 27,61             
  cdCommand.ts     |   89.44 |    80.35 |     100 |   89.44 | ...81,106-111,190 
  clearCommand.ts  |   79.64 |       68 |     100 |   79.64 | ...24-125,133-142 
  ...essCommand.ts |   67.95 |    55.88 |      75 |   67.95 | ...86-187,201-204 
  ...astCommand.ts |   70.86 |    74.07 |      75 |   70.86 | ...,61-93,117-122 
  ...extCommand.ts |   65.35 |     66.1 |   84.61 |   65.35 | ...42-575,586-587 
  copyCommand.ts   |   98.48 |    95.78 |     100 |   98.48 | ...80,280,321,327 
  deleteCommand.ts |     100 |      100 |     100 |     100 |                   
  diffCommand.ts   |     100 |     87.5 |     100 |     100 | ...61,224-225,238 
  ...ryCommand.tsx |   81.84 |    86.11 |   91.66 |   81.84 | ...66-271,318-325 
  docsCommand.ts   |     100 |     90.9 |     100 |     100 | 25                
  doctorCommand.ts |   61.27 |    87.06 |    87.5 |   61.27 | ...71-372,445-665 
  dreamCommand.ts  |   85.45 |    88.88 |     100 |   85.45 | 58-65             
  editorCommand.ts |     100 |      100 |     100 |     100 |                   
  exportCommand.ts |   98.25 |    91.02 |     100 |   98.25 | ...81,198-199,364 
  ...onsCommand.ts |   51.54 |    48.14 |   69.23 |   51.54 | ...97,251-303,364 
  forgetCommand.ts |     100 |       90 |     100 |     100 | 59                
  forkCommand.ts   |     100 |    94.11 |     100 |     100 | 92,141            
  goalCommand.ts   |   91.41 |    84.44 |      90 |   91.41 | ...87-190,202-205 
  helpCommand.ts   |     100 |      100 |     100 |     100 |                   
  hooksCommand.ts  |   81.13 |    65.71 |   85.71 |   81.13 | ...,86-93,131-132 
  ideCommand.ts    |   60.75 |    64.28 |   41.17 |   60.75 | ...05-306,310-324 
  initCommand.ts   |   84.33 |    72.72 |     100 |   84.33 | 68,82-87,89-94    
  ...ghtCommand.ts |   77.87 |    71.42 |     100 |   77.87 | ...44-245,250-272 
  ...ageCommand.ts |   92.17 |    82.69 |     100 |   92.17 | ...39,159,168-178 
  lspCommand.ts    |     100 |    86.95 |     100 |     100 | 31,101-102        
  mcpCommand.ts    |     100 |      100 |     100 |     100 |                   
  memoryCommand.ts |     100 |      100 |     100 |     100 |                   
  modelCommand.ts  |   75.09 |    78.18 |      75 |   75.09 | ...20-225,262-267 
  ...onsCommand.ts |     100 |      100 |     100 |     100 |                   
  planCommand.ts   |   78.82 |    76.92 |     100 |   78.82 | 30-35,51-56,68-73 
  quitCommand.ts   |     100 |      100 |     100 |     100 |                   
  recapCommand.ts  |   21.81 |      100 |      50 |   21.81 | 24-73             
  ...berCommand.ts |      96 |       70 |     100 |      96 | 57,62             
  renameCommand.ts |   85.71 |    86.04 |     100 |   85.71 | ...02-209,216-221 
  ...oreCommand.ts |   90.47 |    84.61 |     100 |   90.47 | ...32-137,167-168 
  resumeCommand.ts |     100 |      100 |     100 |     100 |                   
  rewindCommand.ts |   81.25 |      100 |      50 |   81.25 | 20-22             
  ...ngsCommand.ts |     100 |      100 |     100 |     100 |                   
  ...hubCommand.ts |   81.43 |    65.21 |      80 |   81.43 | ...70-173,176-179 
  skillsCommand.ts |    85.5 |    81.25 |     100 |    85.5 | 36-44,70          
  statsCommand.ts  |   91.48 |    89.47 |     100 |   91.48 | 40-43,134-141     
  ...ineCommand.ts |     100 |      100 |     100 |     100 |                   
  ...aryCommand.ts |    6.46 |      100 |      50 |    6.46 | 31-329            
  tasksCommand.ts  |   77.22 |    72.13 |     100 |   77.22 | ...46-150,172-177 
  ...tupCommand.ts |     100 |      100 |     100 |     100 |                   
  themeCommand.ts  |     100 |      100 |     100 |     100 |                   
  toolsCommand.ts  |     100 |      100 |     100 |     100 |                   
  trustCommand.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
  vimCommand.ts    |   54.54 |      100 |      50 |   54.54 | 19-29             
 src/ui/components |   62.62 |     77.6 |   60.51 |   62.62 |                   
  AboutBox.tsx     |     100 |      100 |     100 |     100 |                   
  AnsiOutput.tsx   |   65.57 |      100 |      50 |   65.57 | 69-90             
  ApiKeyInput.tsx  |       0 |        0 |       0 |       0 | 1-97              
  AppHeader.tsx    |    88.7 |       75 |     100 |    88.7 | 36,38-43,45       
  ...odeDialog.tsx |   87.24 |    72.22 |   33.33 |   87.24 | ...85,233-238,245 
  AsciiArt.ts      |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |   16.27 |      100 |       0 |   16.27 | 19-58             
  ...TextInput.tsx |   77.01 |       76 |     100 |   77.01 | ...20,234-236,263 
  Composer.tsx     |   80.83 |    57.14 |     100 |   80.83 | ...90,102,154,167 
  ...entPrompt.tsx |     100 |      100 |     100 |     100 |                   
  ...ryDisplay.tsx |   75.89 |    62.06 |     100 |   75.89 | ...,88,93-108,113 
  ...geDisplay.tsx |   68.42 |    57.14 |     100 |   68.42 | 16-17,31-32,42-50 
  ...ification.tsx |   28.57 |      100 |       0 |   28.57 | 16-36             
  ...gProfiler.tsx |       0 |        0 |       0 |       0 | 1-36              
  ...ogManager.tsx |   11.86 |      100 |       0 |   11.86 | 69-550            
  DiffDialog.tsx   |    2.47 |      100 |       0 |    2.47 | 68-732            
  ...ngsDialog.tsx |    8.44 |      100 |       0 |    8.44 | 37-195            
  ExitWarning.tsx  |     100 |      100 |     100 |     100 |                   
  ...hProgress.tsx |    87.8 |    33.33 |     100 |    87.8 | 28-31,56          
  ...ustDialog.tsx |     100 |      100 |     100 |     100 |                   
  Footer.tsx       |   78.12 |    51.16 |     100 |   78.12 | ...43,176,198-203 
  ...ngSpinner.tsx |   68.42 |       80 |      50 |   68.42 | 35-52,73,80-81    
  GoalPill.tsx     |   76.19 |    81.81 |     100 |   76.19 | 24-30,46-50       
  Header.tsx       |   98.62 |    94.28 |     100 |   98.62 | 162,164           
  Help.tsx         |   98.32 |       90 |     100 |   98.32 | ...24,381,447-448 
  ...emDisplay.tsx |   65.03 |    55.55 |     100 |   65.03 | ...75,378,381-387 
  ...ngeDialog.tsx |     100 |      100 |     100 |     100 |                   
  InputPrompt.tsx  |    83.1 |    78.07 |   83.33 |    83.1 | ...1652,1667,1717 
  ...Shortcuts.tsx |   20.87 |      100 |       0 |   20.87 | ...6,49-51,67-125 
  ...Indicator.tsx |     100 |     90.9 |     100 |     100 | 62,74             
  ...firmation.tsx |   91.42 |      100 |      50 |   91.42 | 26-31             
  MainContent.tsx  |   87.11 |    88.31 |   66.66 |   87.11 | ...26,284,343-347 
  MemoryDialog.tsx |   61.87 |    76.05 |    62.5 |   61.87 | ...72,391,428-430 
  ...geDisplay.tsx |       0 |        0 |       0 |       0 | 1-41              
  ModelDialog.tsx  |   85.19 |    69.17 |     100 |   85.19 | ...80-596,653-657 
  ...tsDisplay.tsx |     100 |    97.22 |     100 |     100 | 270               
  ...fications.tsx |   18.18 |      100 |       0 |   18.18 | 15-58             
  ...onsDialog.tsx |    2.13 |      100 |       0 |    2.13 | 62-133,148-1004   
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...icePrompt.tsx |   92.64 |    85.71 |     100 |   92.64 | 102-106,134-139   
  PrepareLabel.tsx |   91.66 |    77.27 |     100 |   91.66 | 73-75,77-79,110   
  ...atePrompt.tsx |    8.57 |      100 |       0 |    8.57 | 24-55,58-134      
  ...geDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...ngDisplay.tsx |   21.42 |      100 |       0 |   21.42 | 13-39             
  ...hProgress.tsx |   85.25 |    88.46 |     100 |   85.25 | 121-147           
  ...dSelector.tsx |   92.79 |    82.65 |     100 |   92.79 | ...19-323,354-370 
  ...ionPicker.tsx |   83.66 |    72.13 |     100 |   83.66 | ...96,402,444-466 
  ...onPreview.tsx |   92.42 |    84.37 |     100 |   92.42 | ...,70-71,143-145 
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...putPrompt.tsx |   72.56 |       80 |      40 |   72.56 | ...06-109,114-117 
  ...tedDialog.tsx |     100 |      100 |     100 |     100 |                   
  ...ngsDialog.tsx |   66.31 |    71.16 |      75 |   66.31 | ...16-824,830-831 
  ...ionDialog.tsx |    92.3 |    96.15 |   33.33 |    92.3 | 60-63,68-75,164   
  ...putPrompt.tsx |    15.9 |      100 |       0 |    15.9 | 20-63             
  ...Indicator.tsx |   57.14 |      100 |       0 |   57.14 | 12-15             
  ...MoreLines.tsx |      28 |      100 |       0 |      28 | 18-40             
  ...ionPicker.tsx |   17.59 |      100 |       0 |   17.59 | 55-172            
  ...tivityTab.tsx |    3.94 |      100 |       0 |    3.94 | 27-275            
  StatsDialog.tsx  |    8.85 |      100 |       0 |    8.85 | ...5,49-84,92-238 
  StatsDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...ciencyTab.tsx |    3.28 |      100 |       0 |    3.28 | 25-258            
  ...atmapView.tsx |    8.98 |      100 |       0 |    8.98 | 20-107            
  ...essionTab.tsx |    5.46 |      100 |       0 |    5.46 | 24-215            
  ...ineDialog.tsx |    93.5 |    85.18 |     100 |    93.5 | ...05,267,287-289 
  ...yTodoList.tsx |   96.33 |    88.23 |     100 |   96.33 | 137-140           
  ...nsDisplay.tsx |   87.25 |       64 |     100 |   87.25 | ...57-159,166-168 
  ThemeDialog.tsx  |   89.95 |    46.15 |      75 |   89.95 | ...71-173,243-245 
  Tips.tsx         |   93.54 |       75 |     100 |   93.54 | 39-40             
  TodoDisplay.tsx  |     100 |      100 |     100 |     100 |                   
  ...tsDisplay.tsx |     100 |     87.5 |     100 |     100 | 31-32             
  TrustDialog.tsx  |     100 |    81.81 |     100 |     100 | 71-86             
  ...ification.tsx |   36.36 |      100 |       0 |   36.36 | 15-22             
  ...ackDialog.tsx |    7.84 |      100 |       0 |    7.84 | 24-134            
  ...xitDialog.tsx |   80.36 |    43.47 |      60 |   80.36 | ...24-238,248-251 
  ...odeVisuals.ts |   91.42 |    64.28 |     100 |   91.42 | 15,21,24          
  ...s-helpers.tsx |      25 |      100 |       0 |      25 | ...3,86-89,94-102 
 ...nts/agent-view |   38.22 |    78.82 |   41.66 |   38.22 |                   
  ...atContent.tsx |    8.79 |      100 |       0 |    8.79 | 53-265,271-273    
  ...tChatView.tsx |   21.05 |      100 |       0 |   21.05 | 21-39             
  ...tComposer.tsx |   10.84 |      100 |       0 |   10.84 | 59-308            
  AgentFooter.tsx  |   17.07 |      100 |       0 |   17.07 | 28-66             
  AgentHeader.tsx  |   15.38 |      100 |       0 |   15.38 | 27-64             
  AgentTabBar.tsx  |   87.39 |    62.85 |     100 |   87.39 | ...,85,98-106,124 
  ...oryAdapter.ts |     100 |    91.83 |     100 |     100 | 103,109-110,138   
  index.ts         |       0 |        0 |       0 |       0 | 1-12              
 ...mponents/arena |   45.59 |    70.53 |   60.86 |   45.59 |                   
  ArenaCards.tsx   |   73.06 |    71.79 |   85.71 |   73.06 | ...83-185,321-326 
  ...ectDialog.tsx |   83.48 |    69.86 |   88.88 |   83.48 | ...88-392,409-410 
  ...artDialog.tsx |    9.92 |      100 |       0 |    9.92 | 27-164            
  ...tusDialog.tsx |    5.63 |      100 |       0 |    5.63 | 33-75,80-288      
  ...topDialog.tsx |    6.17 |      100 |       0 |    6.17 | 33-213            
 ...ackground-view |   79.28 |    83.19 |   89.18 |   79.28 |                   
  ...sksDialog.tsx |   75.94 |    81.14 |   81.81 |   75.94 | ...1243,1305-1307 
  ...TasksPill.tsx |   66.29 |    89.28 |     100 |   66.29 | 56,98-118,126-134 
  ...gentPanel.tsx |    97.4 |    85.39 |     100 |    97.4 | 120,433-437       
  ...Visibility.ts |     100 |      100 |     100 |     100 |                   
 ...nts/extensions |   45.28 |    33.33 |      60 |   45.28 |                   
  ...gerDialog.tsx |   44.31 |    34.14 |      75 |   44.31 | ...71-480,483-488 
  index.ts         |       0 |        0 |       0 |       0 | 1-9               
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...tensions/steps |   54.88 |    94.23 |   66.66 |   54.88 |                   
  ...ctionStep.tsx |   95.12 |    92.85 |   85.71 |   95.12 | 84-86,89          
  ...etailStep.tsx |    6.18 |      100 |       0 |    6.18 | 20-131            
  ...nListStep.tsx |   88.43 |    94.73 |      80 |   88.43 | 52-53,59-72,106   
  ...electStep.tsx |   13.46 |      100 |       0 |   13.46 | 20-70             
  ...nfirmStep.tsx |   19.56 |      100 |       0 |   19.56 | 23-65             
  index.ts         |     100 |      100 |     100 |     100 |                   
 ...mponents/hooks |   86.85 |    81.37 |   91.89 |   86.85 |                   
  ...rListBody.tsx |   95.29 |    85.18 |     100 |   95.29 | 95-98             
  ...etailStep.tsx |   75.32 |    71.42 |      60 |   75.32 | ...56-169,173-186 
  ...etailStep.tsx |     100 |      100 |     100 |     100 |                   
  ...rListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...entHeader.tsx |     100 |    85.71 |     100 |     100 | 47                
  ...rListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...etailStep.tsx |     100 |      100 |     100 |     100 |                   
  ...abledStep.tsx |     100 |      100 |     100 |     100 |                   
  ...sListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...entDialog.tsx |   72.29 |    70.49 |     100 |   72.29 | ...51,563-568,572 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-13              
  ...erGrouping.ts |     100 |      100 |     100 |     100 |                   
  sourceLabels.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...components/mcp |   21.66 |    89.36 |   76.92 |   21.66 |                   
  ...ealthPill.tsx |   68.42 |    85.71 |     100 |   68.42 | 40-46             
  ...entDialog.tsx |    3.66 |      100 |       0 |    3.66 | 41-712            
  ...valDialog.tsx |   15.06 |      100 |       0 |   15.06 | 40-109            
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-30              
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |      97 |     92.1 |     100 |      97 | 24,113-114        
 ...ents/mcp/steps |   26.36 |    54.54 |   42.85 |   26.36 |                   
  ...icateStep.tsx |    5.67 |      100 |       0 |    5.67 | 40-66,69-307      
  ...electStep.tsx |   10.95 |      100 |       0 |   10.95 | 16-88             
  ...etailStep.tsx |    5.15 |      100 |       0 |    5.15 | 31-251            
  ...rListStep.tsx |   75.18 |    59.37 |     100 |   75.18 | ...53-158,169-173 
  ...etailStep.tsx |   10.41 |      100 |       0 |   10.41 | ...1,67-79,82-139 
  ToolListStep.tsx |   69.02 |       50 |     100 |   69.02 | ...22,125,134-143 
 ...nents/messages |   83.06 |    79.39 |   78.82 |   83.06 |                   
  ...ionDialog.tsx |   80.84 |     77.6 |    62.5 |   80.84 | ...98,516,534-536 
  BtwMessage.tsx   |     100 |      100 |     100 |     100 |                   
  ...upDisplay.tsx |   97.67 |    83.72 |     100 |   97.67 | 119,142,150       
  ...onMessage.tsx |   91.93 |    82.35 |     100 |   91.93 | 57-59,61,63       
  ...nMessages.tsx |   80.85 |    70.73 |    92.3 |   80.85 | ...08,427,462-468 
  DiffRenderer.tsx |   93.19 |    86.17 |     100 |   93.19 | ...09,237-238,304 
  ...tsDisplay.tsx |   97.82 |    77.27 |     100 |   97.82 | 87,89             
  ...usMessage.tsx |   76.31 |     42.1 |   66.66 |   76.31 | ...99,101,124,155 
  ...tsDisplay.tsx |    95.1 |    88.05 |     100 |    95.1 | ...29,131,164-169 
  ...ssMessage.tsx |    12.5 |      100 |       0 |    12.5 | 18-59             
  ...edMessage.tsx |   16.66 |      100 |       0 |   16.66 | 22-38             
  ...sMessages.tsx |   55.67 |       40 |   28.57 |   55.67 | ...20-125,133-145 
  ...ryMessage.tsx |   14.28 |      100 |       0 |   14.28 | 23-62             
  ...onMessage.tsx |   82.15 |    73.33 |   33.33 |   82.15 | ...65-467,474-476 
  ...upMessage.tsx |   82.63 |    92.85 |     100 |   82.63 | ...85-412,434-449 
  ToolMessage.tsx  |    87.8 |    73.28 |    92.3 |    87.8 | ...59-764,791-793 
 ...ponents/shared |   84.43 |    80.54 |    95.5 |   84.43 |                   
  ...ctionList.tsx |   99.14 |       96 |     100 |   99.14 | 99                
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  EnumSelector.tsx |     100 |    96.42 |     100 |     100 | 58                
  MaxSizedBox.tsx  |   83.01 |    86.25 |   88.88 |   83.01 | ...12-513,618-619 
  MultiSelect.tsx  |   93.58 |       75 |     100 |   93.58 | ...43,199-201,211 
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  ...eSelector.tsx |     100 |       60 |     100 |     100 | 40-45             
  ...lableList.tsx |   76.25 |       80 |     100 |   76.25 | 44-58,65-68       
  StaticRender.tsx |   72.72 |      100 |     100 |   72.72 | 31-33             
  TextInput.tsx    |    80.8 |    66.07 |      80 |    80.8 | ...36-240,252-258 
  ...apsedTime.tsx |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |     100 |      100 |     100 |     100 |                   
  ...lizedList.tsx |   84.26 |    80.88 |      90 |   84.26 | ...68-696,743-765 
  text-buffer.ts   |   85.94 |    81.18 |   97.91 |   85.94 | ...2651,2749-2750 
  ...er-actions.ts |   73.93 |    67.22 |     100 |   73.93 | ...32-733,934-936 
 ...ponents/skills |    3.61 |      100 |       0 |    3.61 |                   
  ...gerDialog.tsx |    3.61 |      100 |       0 |    3.61 | ...90-148,151-694 
 ...ents/subagents |   30.87 |        0 |       0 |   30.87 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-11              
  reducers.tsx     |    12.1 |      100 |       0 |    12.1 | 33-190            
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |   10.95 |      100 |       0 |   10.95 | ...1,56-57,60-102 
 ...bagents/create |    9.13 |      100 |       0 |    9.13 |                   
  ...ionWizard.tsx |    7.28 |      100 |       0 |    7.28 | 34-299            
  ...rSelector.tsx |   14.75 |      100 |       0 |   14.75 | 26-85             
  ...onSummary.tsx |    4.26 |      100 |       0 |    4.26 | 27-331            
  ...tionInput.tsx |    8.63 |      100 |       0 |    8.63 | 23-177            
  ...dSelector.tsx |   33.33 |      100 |       0 |   33.33 | 20-21,26-27,36-63 
  ...nSelector.tsx |    37.5 |      100 |       0 |    37.5 | 20-21,26-27,36-58 
  ...EntryStep.tsx |   12.76 |      100 |       0 |   12.76 | 34-78             
  ToolSelector.tsx |    4.16 |      100 |       0 |    4.16 | 31-253            
 ...bagents/manage |   21.51 |    59.52 |   27.27 |   21.51 |                   
  ...ctionStep.tsx |   10.25 |      100 |       0 |   10.25 | 21-103            
  ...eleteStep.tsx |   20.93 |      100 |       0 |   20.93 | 23-62             
  ...tEditStep.tsx |   25.53 |      100 |       0 |   25.53 | ...2,37-38,51-124 
  ...ctionStep.tsx |   35.42 |    59.52 |     100 |   35.42 | ...20-432,437-439 
  ...iewerStep.tsx |   13.72 |      100 |       0 |   13.72 | 18-73             
  ...gerDialog.tsx |    6.74 |      100 |       0 |    6.74 | 35-341            
 ...mponents/views |   70.21 |    67.32 |    64.7 |   70.21 |                   
  ContextUsage.tsx |   70.88 |    63.88 |      80 |   70.88 | ...20-426,463-557 
  DoctorReport.tsx |     9.8 |      100 |       0 |     9.8 | 25-54,57-131      
  ...sionsList.tsx |   87.69 |    73.68 |     100 |   87.69 | 65-72             
  McpStatus.tsx    |   89.53 |    60.52 |     100 |   89.53 | ...72,175-177,262 
  SkillsList.tsx   |   27.27 |      100 |       0 |   27.27 | 18-35             
  ToolsList.tsx    |     100 |      100 |     100 |     100 |                   
 src/ui/contexts   |   77.54 |    78.01 |   81.03 |   77.54 |                   
  ...ewContext.tsx |   64.83 |    88.88 |      50 |   64.83 | ...16-219,225-235 
  AppContext.tsx   |      80 |       50 |     100 |      80 | 19-20             
  ...ewContext.tsx |    93.3 |    64.28 |      50 |    93.3 | ...35-236,263-267 
  ...deContext.tsx |     100 |      100 |     100 |     100 |                   
  ...igContext.tsx |   81.81 |       50 |     100 |   81.81 | 15-16             
  ...ssContext.tsx |   81.67 |     81.6 |     100 |   81.67 | ...1199,1203-1205 
  ...owContext.tsx |   91.07 |    81.81 |     100 |   91.07 | 47-48,60-62       
  ...deContext.tsx |     100 |      100 |      50 |     100 |                   
  ...onContext.tsx |   43.26 |     62.5 |    62.5 |   43.26 | ...64-267,276-279 
  ...gsContext.tsx |     100 |      100 |     100 |     100 |                   
  ...usContext.tsx |     100 |      100 |     100 |     100 |                   
  ...ngContext.tsx |   71.42 |       50 |     100 |   71.42 | 17-20             
  ...utContext.tsx |   85.71 |      100 |   66.66 |   85.71 | 13-14             
  ...nsContext.tsx |   88.88 |       50 |     100 |   88.88 | 134-135           
  ...teContext.tsx |   86.66 |       50 |     100 |   86.66 | 203-204           
  ...deContext.tsx |      80 |     87.5 |      75 |      80 | ...11-112,118-120 
 src/ui/daemon     |   90.65 |    73.61 |   95.45 |   90.65 |                   
  ...TuiAdapter.ts |   90.65 |    73.61 |   95.45 |   90.65 | ...44,762-763,849 
 src/ui/editors    |   93.33 |    85.71 |   66.66 |   93.33 |                   
  ...ngsManager.ts |   93.33 |    85.71 |   66.66 |   93.33 | 49,63-64          
 src/ui/hooks      |   81.81 |    80.98 |    86.4 |   81.81 |                   
  ...dProcessor.ts |   83.12 |    82.56 |     100 |   83.12 | ...88-389,408-435 
  keyToAnsi.ts     |    3.92 |      100 |       0 |    3.92 | 19-77             
  ...dProcessor.ts |    94.8 |    70.58 |     100 |    94.8 | ...76-277,282-283 
  ...dProcessor.ts |   83.94 |    62.56 |      80 |   83.94 | ...1010,1031-1035 
  ...amingState.ts |   12.22 |      100 |       0 |   12.22 | 54-157            
  ...agerDialog.ts |   88.23 |      100 |     100 |   88.23 | 20,24             
  ...dScrollbar.ts |     100 |      100 |     100 |     100 |                   
  ...ationFrame.ts |      32 |       60 |     100 |      32 | 42-44,51-90       
  ...odeCommand.ts |   58.82 |      100 |     100 |   58.82 | 28,33-48          
  ...enaCommand.ts |      85 |      100 |     100 |      85 | 23-24,29          
  ...aInProcess.ts |   27.92 |       80 |      25 |   27.92 | ...69-170,173-175 
  ...Completion.ts |   91.79 |    86.88 |     100 |   91.79 | ...05-206,243-246 
  ...ifications.ts |   86.91 |    96.29 |     100 |   86.91 | 116-130           
  ...tIndicator.ts |   83.49 |    70.96 |     100 |   83.49 | ...60,168,170-178 
  ...waySummary.ts |   96.22 |    69.69 |     100 |   96.22 | 125-127,169       
  ...ndTaskView.ts |    94.3 |    76.08 |     100 |    94.3 | 122-126,213,219   
  ...chedScroll.ts |     100 |      100 |     100 |     100 |                   
  ...ketedPaste.ts |    23.8 |      100 |       0 |    23.8 | 19-37             
  ...nchCommand.ts |   92.53 |    71.42 |     100 |   92.53 | ...32,172,245-248 
  ...ompletion.tsx |   96.01 |    83.87 |     100 |   96.01 | ...22-223,225-226 
  ...dMigration.ts |   90.62 |       75 |     100 |   90.62 | 38-40             
  useCompletion.ts |    92.4 |     87.5 |     100 |    92.4 | 68-69,93-94,98-99 
  ...nitMessage.ts |     100 |      100 |     100 |     100 |                   
  ...extualTips.ts |   77.27 |       50 |     100 |   77.27 | ...2,75-79,93-101 
  ...eteCommand.ts |   78.53 |    88.57 |     100 |   78.53 | ...96-104,112-113 
  ...ialogClose.ts |    12.5 |      100 |     100 |    12.5 | 85-181            
  useDiffData.ts   |   11.62 |      100 |       0 |   11.62 | 44-87             
  ...oublePress.ts |   53.12 |       75 |     100 |   53.12 | 33-35,41-54       
  ...orSettings.ts |     100 |      100 |     100 |     100 |                   
  ...Completion.ts |   99.12 |    97.67 |     100 |   99.12 | 182-183           
  ...ionUpdates.ts |   93.45 |     92.3 |     100 |   93.45 | ...83-287,300-306 
  ...agerDialog.ts |   88.88 |      100 |     100 |   88.88 | 21,25             
  ...backDialog.ts |    63.9 |    76.47 |   66.66 |    63.9 | ...66-168,190-191 
  useFocus.ts      |     100 |      100 |     100 |     100 |                   
  ...olderTrust.ts |     100 |      100 |     100 |     100 |                   
  ...ggestions.tsx |   89.15 |     62.5 |      50 |   89.15 | ...22-124,149-150 
  ...miniStream.ts |   80.14 |    77.17 |    92.3 |   80.14 | ...2666,2717-2725 
  ...BranchName.ts |    90.9 |     92.3 |     100 |    90.9 | 19-20,55-58       
  ...oryManager.ts |   97.43 |    98.18 |     100 |   97.43 | 52,139-142        
  ...ooksDialog.ts |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...stListener.ts |     100 |      100 |     100 |     100 |                   
  ...nAuthError.ts |   76.19 |       50 |     100 |   76.19 | 39-40,43-45       
  ...putHistory.ts |   92.59 |    85.71 |     100 |   92.59 | 63-64,72,94-96    
  ...storyStore.ts |     100 |    94.11 |     100 |     100 | 69                
  useKeypress.ts   |     100 |      100 |     100 |     100 |                   
  ...rdProtocol.ts |   36.36 |      100 |       0 |   36.36 | 24-31             
  ...unchEditor.ts |    9.67 |      100 |       0 |    9.67 | 11-32,39-90       
  ...gIndicator.ts |     100 |      100 |     100 |     100 |                   
  useLogger.ts     |   21.05 |      100 |       0 |   21.05 | 15-37             
  useMCPHealth.ts  |   63.15 |       75 |      50 |   63.15 | 42-52,64-67       
  ...cpApproval.ts |   92.37 |    83.33 |     100 |   92.37 | ...00-103,115-116 
  useMcpDialog.ts  |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...moryDialog.ts |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...oryMonitor.ts |   83.14 |    78.57 |     100 |   83.14 | 54-63,74-79       
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...delCommand.ts |     100 |       75 |     100 |     100 | 22                
  ...ouseEvents.ts |   87.17 |    88.88 |   66.66 |   87.17 | 81-82,86-88       
  ...raseCycler.ts |   84.74 |    76.47 |     100 |   84.74 | ...49,52-53,69-71 
  ...rredEditor.ts |   58.33 |    22.22 |     100 |   58.33 | 23-27,29-33       
  ...derUpdates.ts |   86.49 |    77.96 |    90.9 |   86.49 | ...26,288-300,348 
  useQwenAuth.ts   |     100 |      100 |     100 |     100 |                   
  ...lScheduler.ts |    84.7 |    93.33 |     100 |    84.7 | ...71-276,372-382 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-7               
  ...tleRepaint.ts |     100 |      100 |     100 |     100 |                   
  ...umeCommand.ts |   96.96 |    83.33 |     100 |   96.96 | 101-102,131       
  ...ompletion.tsx |   90.59 |    83.33 |     100 |   90.59 | ...01,104,137-140 
  ...ectionList.ts |   97.05 |    96.07 |     100 |   97.05 | ...90-191,245-248 
  ...sionPicker.ts |   92.87 |    90.35 |     100 |   92.87 | ...99-501,503-505 
  ...earchInput.ts |     100 |      100 |     100 |     100 |                   
  ...ngsCommand.ts |   18.75 |      100 |       0 |   18.75 | 10-25             
  ...ellHistory.ts |   91.74 |    79.41 |     100 |   91.74 | ...74,122-123,133 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-73              
  ...agerDialog.ts |   88.23 |      100 |     100 |   88.23 | 20,24             
  ...Completion.ts |   82.73 |    85.41 |   94.73 |   82.73 | ...70-672,680-716 
  ...tateAndRef.ts |     100 |      100 |     100 |     100 |                   
  ...tatsDialog.ts |     100 |      100 |     100 |     100 |                   
  useStatusLine.ts |    96.3 |    92.19 |     100 |    96.3 | ...77-380,466-473 
  ...eateDialog.ts |   88.23 |      100 |     100 |   88.23 | 14,18             
  ...mInProcess.ts |   27.35 |       80 |      25 |   27.35 | ...82-183,186-188 
  ...tification.ts |     100 |     87.5 |     100 |     100 | 50                
  ...alProgress.ts |   53.06 |       50 |   66.66 |   53.06 | ...53,61-68,79-85 
  ...rminalSize.ts |   76.19 |      100 |      50 |   76.19 | 21-25             
  ...emeCommand.ts |   67.01 |    29.41 |     100 |   67.01 | ...10-111,115-116 
  useTimer.ts      |   88.09 |    85.71 |     100 |   88.09 | 44-45,51-53       
  ...lMigration.ts |       0 |        0 |       0 |       0 |                   
  ...rustModify.ts |     100 |      100 |     100 |     100 |                   
  useTurnDiffs.ts  |   95.12 |    78.57 |     100 |   95.12 | 133-134,156-157   
  ...elcomeBack.ts |   87.36 |     90.9 |     100 |   87.36 | ...,94-96,114-115 
  ...reeSession.ts |   93.75 |       70 |     100 |   93.75 | 44-45,87          
  vim.ts           |      74 |    67.56 |   69.23 |      74 | ...1854-1861,1869 
 src/ui/layouts    |    90.9 |    90.62 |     100 |    90.9 |                   
  ...AppLayout.tsx |   90.72 |       90 |     100 |   90.72 | 57-59,101-106     
  ...AppLayout.tsx |   91.17 |    91.66 |     100 |   91.17 | 70-75             
 src/ui/models     |   80.24 |    79.16 |   71.42 |   80.24 |                   
  ...ableModels.ts |   80.24 |    79.16 |   71.42 |   80.24 | ...,61-71,123-125 
 ...noninteractive |     100 |      100 |   14.28 |     100 |                   
  ...eractiveUi.ts |     100 |      100 |   14.28 |     100 |                   
 src/ui/state      |   94.91 |    81.81 |     100 |   94.91 |                   
  extensions.ts    |   94.91 |    81.81 |     100 |   94.91 | 68-69,88          
 src/ui/themes     |   98.39 |    72.83 |     100 |   98.39 |                   
  ansi-light.ts    |     100 |      100 |     100 |     100 |                   
  ansi.ts          |     100 |      100 |     100 |     100 |                   
  atom-one-dark.ts |     100 |      100 |     100 |     100 |                   
  ayu-light.ts     |     100 |      100 |     100 |     100 |                   
  ayu.ts           |     100 |      100 |     100 |     100 |                   
  color-utils.ts   |   97.91 |       92 |     100 |   97.91 | ...51-352,354-355 
  default-light.ts |     100 |      100 |     100 |     100 |                   
  default.ts       |     100 |      100 |     100 |     100 |                   
  ...inal-theme.ts |   88.59 |    85.96 |     100 |   88.59 | ...57-261,266-270 
  dracula.ts       |     100 |      100 |     100 |     100 |                   
  github-dark.ts   |     100 |      100 |     100 |     100 |                   
  github-light.ts  |     100 |      100 |     100 |     100 |                   
  googlecode.ts    |     100 |      100 |     100 |     100 |                   
  no-color.ts      |     100 |      100 |     100 |     100 |                   
  qwen-dark.ts     |     100 |      100 |     100 |     100 |                   
  qwen-light.ts    |     100 |      100 |     100 |     100 |                   
  ...tic-tokens.ts |     100 |      100 |     100 |     100 |                   
  ...-of-purple.ts |     100 |      100 |     100 |     100 |                   
  theme-manager.ts |   87.98 |    82.89 |     100 |   87.98 | ...48-357,362-363 
  theme.ts         |     100 |    38.02 |     100 |     100 | ...34-449,457-461 
  xcode.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/utils      |   83.34 |    82.86 |   92.79 |   83.34 |                   
  ...Colorizer.tsx |   79.53 |    83.78 |     100 |   79.53 | ...51-152,249-275 
  ...nRenderer.tsx |   68.83 |    70.14 |      50 |   68.83 | ...52-254,274-293 
  ...wnDisplay.tsx |   86.01 |    87.66 |     100 |   86.01 | ...87,704,729-754 
  ...idDiagram.tsx |   87.79 |    95.34 |     100 |   87.79 | 156-179           
  ...eRenderer.tsx |   92.08 |    80.45 |      95 |   92.08 | ...76-679,723-728 
  ...odeDisplay.ts |   96.55 |     90.9 |     100 |   96.55 | 34                
  asciiCharts.ts   |   96.77 |    87.62 |     100 |   96.77 | 173-180,281       
  ...dWorkUtils.ts |     100 |      100 |     100 |     100 |                   
  ...boardUtils.ts |   51.92 |    72.72 |   91.66 |   51.92 | ...21,624-633,636 
  commandUtils.ts  |      96 |    88.77 |     100 |      96 | ...72,174-175,302 
  computeStats.ts  |     100 |      100 |     100 |     100 |                   
  customBanner.ts  |   90.68 |    91.22 |     100 |   90.68 | ...13,324-327,334 
  displayUtils.ts  |   88.37 |    72.22 |     100 |   88.37 | 23,25,29,31,33    
  formatters.ts    |   95.23 |     98.3 |     100 |   95.23 | 117-120           
  gradientUtils.ts |     100 |      100 |     100 |     100 |                   
  highlight.ts     |     100 |      100 |     100 |     100 |                   
  ...oryMapping.ts |     100 |    96.77 |     100 |     100 | 43                
  historyUtils.ts  |   94.11 |       94 |     100 |   94.11 | 94-97             
  isNarrowWidth.ts |     100 |      100 |     100 |     100 |                   
  ...olDetector.ts |    8.23 |      100 |       0 |    8.23 | ...31-132,135-136 
  latexRenderer.ts |   94.95 |     73.8 |     100 |   94.95 | ...76-178,184-187 
  layoutUtils.ts   |     100 |      100 |     100 |     100 |                   
  ...ightLoader.ts |     100 |    89.47 |     100 |     100 | 81,110            
  ...nUtilities.ts |   69.84 |    85.71 |     100 |   69.84 | 75-91,100-101     
  ...ToolGroups.ts |   98.66 |    96.77 |     100 |   98.66 | 48-49             
  ...geRenderer.ts |   86.23 |    69.06 |   95.12 |   86.23 | ...1284,1324-1330 
  ...alRenderer.ts |   86.69 |     71.9 |     100 |   86.69 | ...1476,1513-1519 
  ...lsBySource.ts |     100 |    95.23 |     100 |     100 | 84                
  mouse.ts         |   90.71 |    73.33 |   88.88 |   90.71 | ...40-143,200-201 
  osc8.ts          |   94.73 |    87.75 |     100 |   94.73 | ...49,434,438-439 
  ...mConstants.ts |     100 |      100 |     100 |     100 |                   
  restoreGoal.ts   |   99.02 |    97.14 |     100 |   99.02 | 106               
  ...storyUtils.ts |   62.71 |    72.41 |      90 |   62.71 | ...82,430,435-457 
  ...ickerUtils.ts |     100 |      100 |     100 |     100 |                   
  ...ataService.ts |   93.17 |     79.1 |     100 |   93.17 | ...14,227,254-256 
  ...izedOutput.ts |   94.94 |      100 |   88.88 |   94.94 | 112-117           
  ...wOptimizer.ts |     100 |    96.77 |     100 |     100 | 69                
  terminalSetup.ts |    4.37 |      100 |       0 |    4.37 | 44-393            
  textUtils.ts     |   97.61 |    94.84 |   92.85 |   97.61 | ...50-251,386-387 
  todoSnapshot.ts  |   89.33 |    93.47 |     100 |   89.33 | ...,66-78,180-181 
  updateCheck.ts   |     100 |    80.95 |     100 |     100 | 30-42             
 ...i/utils/export |      57 |     40.8 |   79.41 |      57 |                   
  collect.ts       |   55.92 |    50.58 |   86.36 |   55.92 | ...25-640,642-647 
  index.ts         |     100 |      100 |     100 |     100 |                   
  normalize.ts     |   58.11 |    20.51 |      80 |   58.11 | ...13-314,328-363 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
  utils.ts         |      40 |      100 |       0 |      40 | 11-13             
 ...ort/formatters |    3.38 |      100 |       0 |    3.38 |                   
  html.ts          |    9.61 |      100 |       0 |    9.61 | ...28,34-76,82-84 
  json.ts          |      50 |      100 |       0 |      50 | 14-15             
  jsonl.ts         |     3.5 |      100 |       0 |     3.5 | 14-76             
  markdown.ts      |    0.94 |      100 |       0 |    0.94 | 13-295            
 src/utils         |   72.16 |    89.14 |   90.43 |   72.16 |                   
  acpModelUtils.ts |     100 |      100 |     100 |     100 |                   
  apiPreconnect.ts |   96.72 |    97.14 |     100 |   96.72 | 165-168           
  checks.ts        |   33.33 |      100 |       0 |   33.33 | 23-28             
  cleanup.ts       |   84.12 |    93.33 |      80 |   84.12 | 75,106-115        
  commands.ts      |     100 |      100 |     100 |     100 |                   
  commentJson.ts   |   90.51 |    91.89 |     100 |   90.51 | 67-76,116         
  ...Calculator.ts |     100 |      100 |     100 |     100 |                   
  cpuProfiler.ts   |   70.38 |    71.83 |   88.88 |   70.38 | ...27,430-431,438 
  deepMerge.ts     |     100 |       90 |     100 |     100 | 41-43,49          
  ...ScopeUtils.ts |   97.56 |    88.88 |     100 |   97.56 | 67                
  doctorChecks.ts  |   70.31 |    74.57 |     100 |   70.31 | ...95-301,325-341 
  ...putCapture.ts |   90.65 |    86.17 |     100 |   90.65 | ...72,370,372-373 
  ...arResolver.ts |   97.14 |    96.42 |     100 |   97.14 | 125-126           
  errors.ts        |   90.85 |    96.36 |    92.3 |   90.85 | 69-70,298-310     
  events.ts        |     100 |      100 |     100 |     100 |                   
  gitUtils.ts      |   91.91 |    84.61 |     100 |   91.91 | 78-81,124-127     
  ...AutoUpdate.ts |    92.2 |    95.23 |   88.88 |    92.2 | 130-141           
  ...tyWarnings.ts |     100 |      100 |     100 |     100 |                   
  ...lationInfo.ts |   89.17 |    92.77 |     100 |   89.17 | ...55,272-273,318 
  languageUtils.ts |   98.19 |    97.14 |     100 |   98.19 | 132-133           
  math.ts          |       0 |        0 |       0 |       0 | 1-15              
  ...iagnostics.ts |   94.57 |    83.01 |   88.88 |   94.57 | ...05,311,315-317 
  ...onfigUtils.ts |     100 |      100 |     100 |     100 |                   
  ...iveHelpers.ts |   96.37 |    93.07 |     100 |   96.37 | ...15-416,514,527 
  osc.ts           |    97.5 |      100 |   88.88 |    97.5 | 195-196           
  package.ts       |   88.88 |       80 |     100 |   88.88 | 33-34             
  processUtils.ts  |     100 |      100 |     100 |     100 |                   
  readStdin.ts     |   79.62 |       90 |      80 |   79.62 | 33-40,52-54       
  relaunch.ts      |   93.22 |    81.25 |     100 |   93.22 | 65-67,80          
  resolvePath.ts   |   66.66 |       25 |     100 |   66.66 | 12-13,16,18-19    
  runBudget.ts     |   99.35 |    96.77 |     100 |   99.35 | 119               
  sandbox.ts       |       0 |        0 |       0 |       0 | 1-1038            
  sessionPaths.ts  |   90.84 |    90.56 |     100 |   90.84 | ...81-182,185-186 
  settingsUtils.ts |   82.51 |    91.79 |   89.74 |   82.51 | ...76-694,701-709 
  spawnWrapper.ts  |     100 |      100 |     100 |     100 |                   
  ...ate-verify.ts |     100 |      100 |     100 |     100 |                   
  ...one-update.ts |   26.82 |    73.77 |   43.47 |   26.82 | ...36-837,840-859 
  ...upProfiler.ts |   98.46 |    94.52 |     100 |   98.46 | 130-131,305       
  ...upWarnings.ts |     100 |      100 |     100 |     100 |                   
  stdioHelpers.ts  |     100 |       60 |     100 |     100 | 23,32             
  systemInfo.ts    |   95.12 |    89.06 |     100 |   95.12 | ...43-244,249-253 
  ...InfoFields.ts |    87.5 |    65.85 |     100 |    87.5 | ...24-125,146-147 
  ...alSequence.ts |     100 |    95.23 |     100 |     100 | 60,90             
  ...iffPreview.ts |   94.11 |    83.33 |     100 |   94.11 | 13                
  ...entEmitter.ts |     100 |      100 |     100 |     100 |                   
  ...ansionHook.ts |     100 |      100 |     100 |     100 |                   
  ...upWarnings.ts |   91.17 |    82.35 |     100 |   91.17 | 67-68,73-74,77-78 
  version.ts       |     100 |       50 |     100 |     100 | 11                
  ...ingHandler.ts |     100 |      100 |     100 |     100 |                   
  windowTitle.ts   |     100 |      100 |     100 |     100 |                   
  ...WithBackup.ts |    62.1 |       75 |     100 |    62.1 | 93,107,118-157    
 ...s/housekeeping |   90.15 |     89.7 |   94.11 |   90.15 |                   
  cleanup.ts       |   94.33 |       95 |     100 |   94.33 | 60-62             
  ...eractionAt.ts |     100 |      100 |     100 |     100 |                   
  scheduler.ts     |   89.71 |    88.23 |   85.71 |   89.71 | 51-55,66,116-120  
  throttledOnce.ts |   86.66 |    85.18 |     100 |   86.66 | ...99,105,137-138 
-------------------|---------|----------|---------|---------|-------------------
Core Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   82.01 |    83.92 |   83.81 |   82.01 |                   
 src               |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/__mocks__/fs  |       0 |        0 |       0 |       0 |                   
  promises.ts      |       0 |        0 |       0 |       0 | 1-48              
 src/agents        |   88.47 |    79.84 |   93.06 |   88.47 |                   
  ...transcript.ts |   92.25 |    85.71 |     100 |   92.25 | ...01,320-321,452 
  ...ent-resume.ts |   83.08 |    69.86 |   78.12 |   83.08 | ...1120-1124,1127 
  ...ound-tasks.ts |   95.07 |    88.12 |     100 |   95.07 | ...1151,1171-1174 
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/agents/arena  |   76.54 |    66.87 |   78.72 |   76.54 |                   
  ...gentClient.ts |   79.47 |    88.88 |   81.81 |   79.47 | ...68-183,189-204 
  ArenaManager.ts  |   75.37 |    63.37 |   78.26 |   75.37 | ...1860,1866-1867 
  arena-events.ts  |   64.44 |      100 |      50 |   64.44 | ...71-175,178-183 
  diff-summary.ts  |    87.5 |    72.34 |     100 |    87.5 | ...32-133,137-138 
  index.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...gents/backends |   76.43 |    86.23 |   73.04 |   76.43 |                   
  ITermBackend.ts  |   97.97 |    93.93 |     100 |   97.97 | ...78-180,255,307 
  ...essBackend.ts |   91.98 |     90.9 |   86.66 |   91.98 | ...95,250-270,329 
  TmuxBackend.ts   |    90.7 |    76.55 |   97.36 |    90.7 | ...87,697,743-747 
  detect.ts        |   31.25 |      100 |       0 |   31.25 | 34-88             
  index.ts         |     100 |      100 |     100 |     100 |                   
  iterm-it2.ts     |     100 |     92.1 |     100 |     100 | 37-38,106         
  tmux-commands.ts |    6.64 |      100 |    3.03 |    6.64 | ...93-363,386-503 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...agents/runtime |   85.54 |     84.1 |   76.87 |   85.54 |                   
  agent-context.ts |     100 |      100 |     100 |     100 |                   
  agent-core.ts    |   77.31 |    73.21 |   65.21 |   77.31 | ...1704,1731-1778 
  agent-events.ts  |     100 |      100 |     100 |     100 |                   
  ...t-headless.ts |   84.48 |    78.04 |   63.63 |   84.48 | ...00-401,404-405 
  ...nteractive.ts |   80.55 |    81.35 |   74.07 |   80.55 | ...79,481,483,486 
  ...statistics.ts |   98.19 |    82.35 |     100 |   98.19 | 127,151,192,225   
  agent-types.ts   |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...chestrator.ts |   91.36 |    89.89 |      80 |   91.36 | ...1231,1280-1283 
  ...ow-prompts.ts |     100 |      100 |     100 |     100 |                   
  ...ow-sandbox.ts |     100 |    98.11 |     100 |     100 | 117,287           
 src/agents/tasks  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/agents/team   |   80.31 |    83.19 |    86.5 |   80.31 |                   
  TeamManager.ts   |   67.11 |    76.25 |   74.41 |   67.11 | ...1433,1456-1457 
  identity.ts      |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...sionBridge.ts |     100 |      100 |     100 |     100 |                   
  mailbox.ts       |   94.76 |    86.36 |   92.85 |   94.76 | 86-87,348-354     
  ...ptAddendum.ts |     100 |      100 |     100 |     100 |                   
  tasks.ts         |   88.85 |    82.47 |   96.29 |   88.85 | ...-990,1034-1035 
  team-events.ts   |   60.52 |      100 |      50 |   60.52 | ...37-141,148-152 
  teamHelpers.ts   |   92.02 |    94.91 |   95.23 |   92.02 | ...31-332,368-378 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...eam/test-utils |   94.39 |    93.38 |   98.21 |   94.39 |                   
  ...on-harness.ts |   96.49 |    77.77 |     100 |   96.49 | 128-129,141-142   
  fake-agent.ts    |   98.49 |    95.08 |     100 |   98.49 | 201-203           
  fake-backend.ts  |   86.46 |    97.61 |   95.83 |   86.46 | 124-146           
 src/config        |   78.44 |    83.15 |   64.12 |   78.44 |                   
  config.ts        |   76.79 |    82.23 |   60.43 |   76.79 | ...4840,4845-4846 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  models.ts        |     100 |      100 |     100 |     100 |                   
  storage.ts       |   94.24 |    91.13 |   88.09 |   94.24 | ...68-369,372-373 
 ...nfirmation-bus |   98.29 |    97.14 |     100 |   98.29 |                   
  message-bus.ts   |   98.14 |    97.05 |     100 |   98.14 | 42-43             
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/core          |   88.45 |    83.64 |    91.9 |   88.45 |                   
  baseLlmClient.ts |   81.25 |    76.47 |   77.77 |   81.25 | ...13,515-525,534 
  client.ts        |   87.41 |    80.76 |   89.83 |   87.41 | ...2529,2623-2624 
  ...tGenerator.ts |   84.86 |    69.23 |     100 |   84.86 | ...84,386,393-396 
  ...lScheduler.ts |   87.18 |    81.46 |   95.83 |   87.18 | ...4111,4139-4150 
  geminiChat.ts    |   91.37 |    87.77 |   96.15 |   91.37 | ...3032,3099-3100 
  geminiRequest.ts |     100 |      100 |     100 |     100 |                   
  ...MediaLimit.ts |     100 |    95.83 |     100 |     100 | 96                
  ...htProtocol.ts |    9.09 |      100 |       0 |    9.09 | ...9,62-66,69-110 
  logger.ts        |   87.41 |    87.02 |     100 |   87.41 | ...64-568,614-628 
  ...tyDefaults.ts |     100 |      100 |     100 |     100 |                   
  ...olExecutor.ts |   92.59 |       75 |      50 |   92.59 | 41-42             
  ...on-helpers.ts |   86.48 |    72.22 |     100 |   86.48 | ...97-198,212-221 
  ...issionFlow.ts |   98.78 |       96 |     100 |   98.78 | 93                
  prompts.ts       |   88.93 |    87.87 |   72.72 |   88.93 | ...-910,1113-1114 
  tokenLimits.ts   |     100 |    89.47 |     100 |     100 | 51-52             
  ...okTriggers.ts |   99.43 |    91.34 |     100 |   99.43 | 172,183           
  turn.ts          |   96.35 |    88.67 |     100 |   96.35 | ...28,441-442,486 
 ...ntentGenerator |   94.88 |    82.07 |      94 |   94.88 |                   
  ...tGenerator.ts |   96.29 |    83.18 |   92.85 |   96.29 | ...1,971,999-1001 
  converter.ts     |   94.51 |    80.72 |     100 |   94.51 | ...06-607,617,823 
  index.ts         |       0 |        0 |       0 |       0 | 1-21              
  usage.ts         |     100 |      100 |     100 |     100 |                   
 ...ntentGenerator |   91.53 |    71.64 |   93.33 |   91.53 |                   
  ...tGenerator.ts |      90 |    70.96 |   92.85 |      90 | ...80-286,304-305 
  index.ts         |     100 |       80 |     100 |     100 | 50                
 ...ntentGenerator |   94.22 |    83.96 |   91.17 |   94.22 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tGenerator.ts |   94.09 |     82.5 |   90.62 |   94.09 | ...1025-1026,1054 
  ...tDetection.ts |     100 |      100 |     100 |     100 |                   
 ...ntentGenerator |   86.35 |     84.4 |   93.67 |   86.35 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  converter.ts     |   84.89 |    82.17 |   96.15 |   84.89 | ...1395,1611-1626 
  errorHandler.ts  |     100 |      100 |     100 |     100 |                   
  index.ts         |   54.54 |    68.75 |      50 |   54.54 | ...79,87-91,95-99 
  ...tGenerator.ts |    66.4 |    70.58 |   88.88 |    66.4 | ...51-157,168-169 
  pipeline.ts      |   94.38 |     86.5 |     100 |   94.38 | ...38-539,547,615 
  ...ureContext.ts |     100 |      100 |     100 |     100 |                   
  ...ingOptions.ts |       0 |        0 |       0 |       0 | 1                 
  ...CallParser.ts |   90.66 |    88.57 |     100 |   90.66 | ...15-319,349-350 
  ...kingParser.ts |     100 |    96.87 |     100 |     100 | 42                
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...rator/provider |   96.67 |    88.94 |   96.07 |   96.67 |                   
  dashscope.ts     |   97.37 |    91.39 |   93.33 |   97.37 | ...90-291,369-370 
  deepseek.ts      |   94.91 |    89.36 |     100 |   94.91 | ...31-132,145-146 
  default.ts       |   95.79 |    89.65 |   88.88 |   95.79 | 122-123,193-195   
  index.ts         |     100 |      100 |     100 |     100 |                   
  mimo.ts          |   94.11 |    66.66 |     100 |   94.11 | 29,52-53          
  minimax.ts       |     100 |      100 |     100 |     100 |                   
  mistral.ts       |   96.07 |    73.33 |     100 |   96.07 | 32-33             
  modelscope.ts    |     100 |      100 |     100 |     100 |                   
  openrouter.ts    |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 |                   
  utils.ts         |     100 |      100 |     100 |     100 |                   
 src/extension     |   62.64 |    79.44 |   80.31 |   62.64 |                   
  ...-converter.ts |   66.28 |    52.03 |     100 |   66.28 | ...98-799,808-840 
  ...ionManager.ts |   47.85 |    82.19 |    65.9 |   47.85 | ...1402,1412-1431 
  ...onSettings.ts |   93.46 |    93.05 |     100 |   93.46 | ...17-221,228-232 
  ...-converter.ts |   54.88 |    94.44 |      60 |   54.88 | ...35-146,158-192 
  github.ts        |   46.41 |     87.3 |   63.63 |   46.41 | ...66-372,411-464 
  index.ts         |     100 |      100 |     100 |     100 |                   
  marketplace.ts   |   97.31 |    93.75 |     100 |   97.31 | ...65,185-186,275 
  npm.ts           |   59.01 |    71.69 |    87.5 |   59.01 | ...23-425,432-436 
  override.ts      |   94.11 |    88.88 |     100 |   94.11 | 63-64,81-82       
  redaction.ts     |     100 |      100 |     100 |     100 |                   
  settings.ts      |   66.26 |      100 |      50 |   66.26 | 81-107,141-146    
  storage.ts       |     100 |      100 |     100 |     100 |                   
  ...ableSchema.ts |     100 |      100 |     100 |     100 |                   
  variables.ts     |   88.75 |    83.33 |     100 |   88.75 | ...28-231,234-237 
 src/followup      |   55.29 |    85.18 |   81.25 |   55.29 |                   
  followupState.ts |      96 |    89.74 |     100 |      96 | 159-161,218-219   
  index.ts         |     100 |      100 |     100 |     100 |                   
  overlayFs.ts     |   95.06 |       84 |     100 |   95.06 | 78,108,122,133    
  speculation.ts   |   13.02 |      100 |   16.66 |   13.02 | 89-464,524-575    
  ...onToolGate.ts |     100 |    96.42 |     100 |     100 | 95                
  ...nGenerator.ts |   70.23 |    74.57 |   83.33 |   70.23 | ...83-247,317-319 
 src/generated     |       0 |        0 |       0 |       0 |                   
  git-commit.ts    |       0 |        0 |       0 |       0 | 1-10              
 src/goals         |   89.57 |    83.57 |   94.44 |   89.57 |                   
  ...eGoalStore.ts |    85.1 |    95.45 |   84.61 |    85.1 | ...63-166,174-182 
  goalHook.ts      |   97.26 |    91.66 |     100 |   97.26 | 100-105           
  goalJudge.ts     |   84.33 |    74.28 |     100 |   84.33 | ...57-358,366-368 
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/hooks         |   86.88 |    85.58 |   88.01 |   86.88 |                   
  ...okRegistry.ts |   86.48 |    77.08 |     100 |   86.48 | ...41-344,362-369 
  ...bortSignal.ts |     100 |      100 |     100 |     100 |                   
  ...terpolator.ts |   96.66 |    93.33 |     100 |   96.66 | 66-67             
  ...HookRunner.ts |   96.68 |    87.23 |     100 |   96.68 | 110-112,231-233   
  ...Aggregator.ts |   96.35 |    90.69 |     100 |   96.35 | ...00-301,382,384 
  ...entHandler.ts |   95.27 |    86.74 |   94.11 |   95.27 | ...63,920-921,931 
  hookPlanner.ts   |   86.29 |    83.33 |   85.71 |   86.29 | ...15-219,226-237 
  hookRegistry.ts  |   91.48 |    84.61 |     100 |   91.48 | ...97,416,420,424 
  hookRunner.ts    |   62.42 |    72.04 |   66.66 |   62.42 | ...64-765,774-775 
  hookSystem.ts    |   86.78 |      100 |   68.88 |   86.78 | ...07-708,714-715 
  ...HookRunner.ts |   75.51 |     61.9 |      80 |   75.51 | ...05-406,424-425 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...edCallback.ts |     100 |      100 |     100 |     100 |                   
  ...HookRunner.ts |   96.37 |     90.9 |      90 |   96.37 | 342-350,424-425   
  ...SkillHooks.ts |   78.75 |       75 |   66.66 |   78.75 | 62-66,137-152     
  ...oksManager.ts |   96.66 |    91.66 |     100 |   96.66 | ...90,209-210,223 
  ssrfGuard.ts     |   77.22 |    85.36 |     100 |   77.22 | ...57,261-267,273 
  stopHookCap.ts   |     100 |      100 |     100 |     100 |                   
  trustedHooks.ts  |      90 |    52.63 |     100 |      90 | ...53,66-67,97-98 
  types.ts         |   92.83 |       94 |    87.5 |   92.83 | ...87-488,573-577 
  urlValidator.ts  |     100 |      100 |     100 |     100 |                   
 src/ide           |   75.55 |    83.52 |   78.33 |   75.55 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  detect-ide.ts    |     100 |      100 |     100 |     100 |                   
  ide-client.ts    |   66.14 |    81.75 |   66.66 |   66.14 | ...3-964,993-1001 
  ide-installer.ts |   89.06 |    79.31 |     100 |   89.06 | ...36,143-147,160 
  ideContext.ts    |     100 |      100 |     100 |     100 |                   
  process-utils.ts |   84.84 |    71.79 |     100 |   84.84 | ...37,151,193-194 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/lsp           |   42.42 |     51.9 |   52.14 |   42.42 |                   
  ...nfigLoader.ts |   70.27 |    35.89 |   94.73 |   70.27 | ...20-422,426-432 
  ...ionFactory.ts |   42.81 |    73.07 |      50 |   42.81 | ...76-427,433-450 
  ...Normalizer.ts |   23.09 |    13.72 |   30.43 |   23.09 | ...04-905,909-924 
  ...verManager.ts |   25.31 |    62.06 |   41.66 |   25.31 | ...85-704,710-740 
  ...eLspClient.ts |   32.77 |       80 |   17.64 |   32.77 | ...84-288,294-295 
  ...LspService.ts |   51.85 |    65.98 |   68.57 |   51.85 | ...1339,1399-1409 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/mcp           |   79.21 |    76.52 |   76.36 |   79.21 |                   
  configHash.ts    |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...h-provider.ts |   86.95 |      100 |   33.33 |   86.95 | ...,93,97,101-102 
  ...h-provider.ts |   73.82 |    53.92 |     100 |   73.82 | ...88-895,902-904 
  ...en-storage.ts |   98.64 |    97.77 |     100 |   98.64 | 88-89             
  oauth-utils.ts   |   70.58 |    85.29 |    90.9 |   70.58 | ...70-290,315-344 
  ...n-provider.ts |   89.83 |       96 |   45.45 |   89.83 | ...43,147,151-152 
 .../token-storage |   79.72 |    87.05 |   86.36 |   79.72 |                   
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   83.44 |    84.21 |   92.85 |   83.44 | ...68-178,186-187 
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   68.14 |    82.35 |   64.28 |   68.14 | ...81-295,298-314 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/memory        |   73.76 |    77.74 |   72.68 |   73.76 |                   
  const.ts         |   94.28 |     92.3 |     100 |   94.28 | 66-67             
  dream.ts         |      66 |    73.33 |      50 |      66 | 51,108-149        
  ...entPlanner.ts |   57.84 |    72.72 |   33.33 |   57.84 | ...35,140-147,152 
  entries.ts       |   63.77 |    79.16 |      50 |   63.77 | ...72-180,183-189 
  extract.ts       |   92.72 |    74.19 |     100 |   92.72 | ...32,151-154,211 
  ...entPlanner.ts |   67.59 |     73.8 |      50 |   67.59 | ...31,240-243,415 
  ...ionPlanner.ts |       0 |        0 |       0 |       0 | 1                 
  forget.ts        |      46 |    61.53 |   44.44 |      46 | ...05,212,215-347 
  indexer.ts       |    86.3 |       50 |     100 |    86.3 | ...56,62-63,75-76 
  manager.ts       |    75.5 |    81.04 |    75.6 |    75.5 | ...1292,1305-1307 
  memoryAge.ts     |   90.47 |       80 |     100 |   90.47 | 50-51             
  paths.ts         |   79.06 |    95.12 |     100 |   79.06 | 32-33,49-86       
  prompt.ts        |   94.87 |    78.57 |     100 |   94.87 | ...63,166,304-305 
  recall.ts        |   82.06 |       75 |    90.9 |   82.06 | ...59-364,395-406 
  ...ceSelector.ts |   93.02 |    81.81 |     100 |   93.02 | ...24,126-127,135 
  scan.ts          |   92.92 |    73.91 |     100 |   92.92 | ...51-52,62,90-91 
  ...entPlanner.ts |   58.33 |    66.66 |   56.25 |   58.33 | ...61-282,358-403 
  status.ts        |   10.52 |      100 |       0 |   10.52 | 41-98             
  store.ts         |   93.33 |    81.25 |     100 |   93.33 | ...,94-95,119-120 
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...ontextFile.ts |   79.38 |    81.03 |   81.81 |   79.38 | ...58-272,286-291 
 src/mocks         |       0 |        0 |       0 |       0 |                   
  msw.ts           |       0 |        0 |       0 |       0 | 1-9               
 src/models        |   89.98 |    87.37 |   88.15 |   89.98 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...tor-config.ts |   90.24 |    91.42 |     100 |   90.24 | 142,148,151-160   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...nfigErrors.ts |   74.22 |    47.82 |   84.61 |   74.22 | ...,67-74,106-117 
  ...igResolver.ts |   98.66 |    92.85 |     100 |   98.66 | 162,324,330       
  modelRegistry.ts |     100 |    98.63 |     100 |     100 | 229               
  modelsConfig.ts  |   86.24 |    85.23 |   82.92 |   86.24 | ...1328,1357-1358 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/output        |     100 |      100 |     100 |     100 |                   
  ...-formatter.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/permissions   |   82.68 |    91.87 |   68.32 |   82.68 |                   
  autoMode.ts      |   97.84 |    94.27 |     100 |   97.84 | 523-524,545-552   
  ...transcript.ts |      98 |       84 |     100 |      98 | 200-201           
  classifier.ts    |   93.95 |    94.44 |     100 |   93.95 | 158-165,383-387   
  ...erousRules.ts |     100 |    89.36 |     100 |     100 | 110,133,147,175   
  ...alTracking.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...on-manager.ts |   84.86 |    89.03 |      80 |   84.86 | ...1024,1130-1134 
  rule-parser.ts   |   97.39 |    93.82 |     100 |   97.39 | ...-882,1031-1033 
  ...-semantics.ts |   70.28 |    90.69 |   46.21 |   70.28 | ...2214,2277-2280 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...sifier-prompts |   99.04 |    95.23 |     100 |   99.04 |                   
  system-prompt.ts |   99.04 |    95.23 |     100 |   99.04 | 219               
 src/plan-gate     |    79.3 |    92.75 |   81.25 |    79.3 |                   
  ...viewAgents.ts |   56.02 |    88.46 |   66.66 |   56.02 | ...09-175,197-198 
  ...provalGate.ts |      95 |    95.12 |    87.5 |      95 | 164-165,252-258   
  state.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/prompts       |   83.63 |      100 |    87.5 |   83.63 |                   
  mcp-prompts.ts   |   18.18 |      100 |       0 |   18.18 | 11-19             
  ...t-registry.ts |     100 |      100 |     100 |     100 |                   
 src/providers     |   79.44 |    64.39 |   64.28 |   79.44 |                   
  all-providers.ts |      68 |      100 |       0 |      68 | 68-69,73-79,83-89 
  index.ts         |     100 |      100 |     100 |     100 |                   
  install.ts       |   98.87 |    87.27 |     100 |   98.87 | 268-269           
  ...der-config.ts |   69.73 |    47.29 |   68.42 |   69.73 | ...10-411,418-427 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...viders/presets |   97.31 |    86.36 |      50 |   97.31 |                   
  ...oding-plan.ts |   87.34 |      100 |       0 |   87.34 | 82-84,87-89,91-94 
  ...a-standard.ts |     100 |      100 |     100 |     100 |                   
  ...token-plan.ts |     100 |      100 |     100 |     100 |                   
  ...m-provider.ts |   97.01 |    81.25 |      75 |   97.01 | 120-121           
  deepseek.ts      |     100 |      100 |     100 |     100 |                   
  idealab.ts       |     100 |      100 |     100 |     100 |                   
  minimax.ts       |     100 |      100 |     100 |     100 |                   
  modelscope.ts    |     100 |      100 |     100 |     100 |                   
  openrouter.ts    |     100 |      100 |     100 |     100 |                   
  zai.ts           |     100 |      100 |     100 |     100 |                   
 src/qwen          |    85.3 |    78.57 |   95.89 |    85.3 |                   
  ...tGenerator.ts |   98.64 |    98.18 |     100 |   98.64 | 105-106           
  qwenOAuth2.ts    |   82.55 |    73.24 |   90.62 |   82.55 | ...1183-1199,1229 
  ...kenManager.ts |   85.36 |    76.61 |     100 |   85.36 | ...52-757,778-783 
 src/services      |   87.02 |    84.08 |   93.38 |   87.02 |                   
  ...ionTrailer.ts |     100 |      100 |     100 |     100 |                   
  ...llRegistry.ts |   97.35 |    85.34 |     100 |   97.35 | ...94,117,417-418 
  ...ionService.ts |   98.19 |    94.94 |     100 |   98.19 | 496,498-502,605   
  ...ingService.ts |    84.2 |    81.92 |   83.33 |    84.2 | ...1438,1453-1454 
  ...ttribution.ts |   91.73 |    87.71 |      90 |   91.73 | ...80-685,826-827 
  ...utSlimming.ts |     100 |    97.43 |     100 |     100 | 215,268           
  cronScheduler.ts |   94.18 |     88.6 |     100 |   94.18 | ...-774,1034-1035 
  cronTasksFile.ts |   93.97 |    83.63 |     100 |   93.97 | ...82-183,192-193 
  cronTasksLock.ts |   94.44 |    89.47 |     100 |   94.44 | ...02-103,132-133 
  ...eryService.ts |   80.43 |    95.45 |      75 |   80.43 | ...19-134,140-141 
  ...oryService.ts |   88.17 |    79.02 |    92.3 |   88.17 | ...1303,1344-1347 
  fileReadCache.ts |     100 |      100 |     100 |     100 |                   
  ...temService.ts |   91.27 |    82.69 |    90.9 |   91.27 | ...94,196,294-301 
  ...ratedFiles.ts |      96 |    88.23 |     100 |      96 | 119-120,146-147   
  gitInit.ts       |     100 |      100 |     100 |     100 |                   
  ...reeService.ts |    69.4 |    68.82 |   93.33 |    69.4 | ...2064,2092-2093 
  ...ionService.ts |   98.13 |     97.8 |   95.45 |   98.13 | ...32-333,380-381 
  ...ticsDumper.ts |   98.37 |    95.23 |     100 |   98.37 | 185-186           
  ...ureMonitor.ts |   96.06 |    91.44 |   96.96 |   96.06 | ...49,850,864-866 
  ...orRegistry.ts |   97.24 |    92.03 |     100 |   97.24 | ...49-450,601-602 
  ...ttachments.ts |   97.24 |    90.39 |     100 |   97.24 | ...08,646,661-662 
  sessionRecap.ts  |     9.7 |      100 |       0 |     9.7 | 44-174            
  ...ionService.ts |   86.89 |    78.82 |   94.28 |   86.89 | ...1488,1526-1546 
  sessionTitle.ts  |   93.87 |    71.15 |     100 |   93.87 | ...33-236,267-268 
  ...ionService.ts |   81.29 |    78.31 |   89.28 |   81.29 | ...1926,1932-1937 
  ...pInhibitor.ts |   97.02 |    90.74 |     100 |   97.02 | ...14-115,289-290 
  ...Estimation.ts |     100 |      100 |     100 |     100 |                   
  ...UseSummary.ts |   94.63 |    88.46 |     100 |   94.63 | ...62-164,214-215 
  ...oryService.ts |   89.03 |    65.38 |     100 |   89.03 | ...23-325,330-331 
  ...reeCleanup.ts |   14.56 |      100 |   33.33 |   14.56 | 58-185            
  ...ionService.ts |   84.21 |    79.41 |     100 |   84.21 | ...18-219,235-236 
 ...icrocompaction |   98.05 |       92 |     100 |   98.05 |                   
  microcompact.ts  |   98.05 |       92 |     100 |   98.05 | ...19,292,296,394 
 src/skills        |   88.14 |    86.62 |      90 |   88.14 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...activation.ts |     100 |     93.1 |     100 |     100 | 93,112            
  skill-load.ts    |   94.84 |     87.5 |     100 |   94.84 | ...03,223,235-237 
  skill-manager.ts |   83.39 |    81.42 |   82.35 |   83.39 | ...1199,1206-1210 
  skill-paths.ts   |   89.15 |    86.36 |     100 |   89.15 | ...00-101,106-107 
  symlinkScope.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |   97.91 |       98 |     100 |   97.91 | 277-278           
 src/subagents     |   85.84 |    85.55 |   94.33 |   85.84 |                   
  ...ter-schema.ts |     100 |    98.07 |     100 |     100 | 99                
  ...tin-agents.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...nt-manager.ts |    81.2 |    79.93 |   91.17 |    81.2 | ...1432,1509-1510 
  types.ts         |     100 |      100 |     100 |     100 |                   
  validation.ts    |   92.46 |    95.18 |     100 |   92.46 | 47-52,63-68,71-76 
 src/telemetry     |   78.56 |    87.69 |   80.33 |   78.56 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...on-metrics.ts |   98.96 |    79.48 |     100 |   98.96 | 169,183           
  ...on-tracing.ts |   74.55 |    73.21 |   70.58 |   74.55 | ...95,336-338,354 
  ...attributes.ts |   98.13 |       88 |     100 |   98.13 | 185-187           
  ...-exporters.ts |   46.37 |      100 |   44.44 |   46.37 | ...85,88-89,92-93 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-111             
  ...-processor.ts |   99.09 |    95.61 |      95 |   99.09 | 141,365-366       
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-128             
  loggers.ts       |   54.08 |    65.85 |   60.86 |   54.08 | ...1250,1267-1287 
  metrics.ts       |   75.31 |    80.85 |   77.19 |   75.31 | ...1021,1024-1035 
  ...attributes.ts |     100 |      100 |     100 |     100 |                   
  ...ime-config.ts |       0 |        0 |       0 |       0 | 1                 
  sanitize.ts      |      80 |    83.33 |     100 |      80 | 35-36,41-42       
  sdk.ts           |   86.75 |     88.4 |   66.66 |   86.75 | ...17-621,659-681 
  ...on-context.ts |     100 |      100 |     100 |     100 |                   
  ...on-tracing.ts |   90.04 |    88.11 |   96.55 |   90.04 | ...1504,1535-1538 
  ...etry-utils.ts |     100 |      100 |     100 |     100 |                   
  ...l-decision.ts |     100 |      100 |     100 |     100 |                   
  trace-context.ts |     100 |      100 |     100 |     100 |                   
  ...e-id-utils.ts |     100 |      100 |     100 |     100 |                   
  tracer.ts        |   98.56 |    88.63 |     100 |   98.56 | 52,101            
  types.ts         |   79.46 |    93.91 |   84.21 |   79.46 | ...1241,1244-1273 
  uiTelemetry.ts   |      92 |    95.34 |   80.95 |      92 | ...00,206-216,244 
 ...ry/qwen-logger |   68.17 |     80.2 |   65.51 |   68.17 |                   
  event-types.ts   |       0 |        0 |       0 |       0 |                   
  qwen-logger.ts   |   68.17 |       80 |   64.91 |   68.17 | ...1077,1115-1116 
 src/test-utils    |   93.44 |    96.15 |   77.77 |   93.44 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  ...st-helpers.ts |   94.11 |       90 |     100 |   94.11 | 69-70             
  index.ts         |     100 |      100 |     100 |     100 |                   
  mock-tool.ts     |   91.71 |    97.36 |   74.19 |   91.71 | ...54,218-219,232 
  ...aceContext.ts |     100 |      100 |     100 |     100 |                   
 src/tools         |   79.73 |    82.09 |    85.8 |   79.73 |                   
  ...erQuestion.ts |   90.03 |    79.36 |   91.66 |   90.03 | ...99-400,407-408 
  cron-create.ts   |   88.18 |    93.33 |    62.5 |   88.18 | ...,45-46,177-185 
  cron-delete.ts   |   97.53 |      100 |   83.33 |   97.53 | 31-32             
  cron-list.ts     |   97.82 |    95.45 |   83.33 |   97.82 | 30-31             
  diffOptions.ts   |     100 |      100 |     100 |     100 |                   
  edit.ts          |   81.02 |    84.07 |      75 |   81.02 | ...15-716,826-876 
  ...r-worktree.ts |   83.14 |    67.56 |    87.5 |   83.14 | ...84-187,278-279 
  enterPlanMode.ts |   90.69 |       75 |   85.71 |   90.69 | 55-56,74-79       
  exit-worktree.ts |   84.23 |    85.96 |   91.66 |   84.23 | ...92-293,298-312 
  exitPlanMode.ts  |   82.94 |    77.35 |     100 |   82.94 | ...62-374,386-389 
  glob.ts          |   90.63 |    88.33 |   84.61 |   90.63 | ...28,171,302,305 
  grep.ts          |   79.04 |    85.71 |      75 |   79.04 | ...73-580,604-605 
  ...adTracking.ts |     100 |      100 |     100 |     100 |                   
  ls.ts            |   96.74 |    90.27 |     100 |   96.74 | 176-181,212,216   
  lsp.ts           |   72.77 |    60.09 |   90.32 |   72.77 | ...1211,1213-1214 
  ...nt-manager.ts |   80.51 |    78.46 |   84.44 |   80.51 | ...2981,2983-2984 
  mcp-client.ts    |      43 |    87.57 |      75 |      43 | ...1790,1794-1797 
  ...ry-timeout.ts |     100 |      100 |     100 |     100 |                   
  mcp-errors.ts    |     100 |      100 |     100 |     100 |                   
  ...pool-entry.ts |   77.21 |    83.96 |   79.41 |   77.21 | ...1259,1267-1268 
  ...ool-events.ts |       8 |        0 |       0 |       8 | 123-149           
  mcp-pool-key.ts  |   97.46 |    93.93 |     100 |   97.46 | 175-176           
  mcp-tool.ts      |   91.36 |    89.32 |   96.55 |   91.36 | ...40-641,691-692 
  ...sport-pool.ts |   83.27 |       80 |   84.61 |   83.27 | ...1399,1406-1410 
  ...ace-budget.ts |   87.27 |     82.6 |     100 |   87.27 | ...00-305,340-345 
  memory-config.ts |       0 |        0 |       0 |       0 | 1-47              
  ...iable-tool.ts |     100 |    84.61 |     100 |     100 | 102,109           
  monitor.ts       |   91.65 |    84.05 |   88.46 |   91.65 | ...87,600,796-801 
  notebook-edit.ts |   85.11 |    76.42 |   81.25 |   85.11 | ...54-870,916-917 
  ...escendants.ts |   36.17 |    64.51 |   55.55 |   36.17 | ...46-310,385-390 
  ...nforcement.ts |   82.57 |       90 |     100 |   82.57 | 174-185,234-247   
  read-file.ts     |   94.75 |    90.32 |   81.81 |   94.75 | ...02,305,388-389 
  ripGrep.ts       |   94.17 |    85.71 |    87.5 |   94.17 | ...96-497,547-548 
  ...-transport.ts |    6.34 |        0 |       0 |    6.34 | 47-145            
  send-message.ts  |   79.48 |    86.95 |    62.5 |   79.48 | ...97-203,286-294 
  ...n-mcp-view.ts |   92.37 |    93.54 |   88.88 |   92.37 | 118-126           
  shell.ts         |   74.32 |    80.89 |   90.54 |   74.32 | ...4272,4331-4332 
  skill-utils.ts   |     100 |      100 |     100 |     100 |                   
  skill.ts         |    89.4 |     92.5 |   88.88 |    89.4 | ...43,447,476-498 
  ...eticOutput.ts |   95.12 |      100 |      80 |   95.12 | 87-88             
  task-create.ts   |   93.85 |     92.3 |   81.81 |   93.85 | 41-45,59-60,91    
  task-list.ts     |   73.38 |    77.77 |   83.33 |   73.38 | ...02,105,109-116 
  task-stop.ts     |   93.14 |    96.15 |   85.71 |   93.14 | 39-40,54-64       
  task-update.ts   |   80.67 |       78 |    92.3 |   80.67 | ...75-383,415-426 
  team-create.ts   |   97.22 |    85.71 |   83.33 |   97.22 | 48-49,129-130     
  team-delete.ts   |   86.74 |    83.33 |   83.33 |   86.74 | 37-38,42-48,72-73 
  todoWrite.ts     |   89.27 |    82.05 |   92.85 |   89.27 | ...50-555,577-578 
  tool-error.ts    |     100 |      100 |     100 |     100 |                   
  tool-names.ts    |     100 |      100 |     100 |     100 |                   
  tool-registry.ts |   76.19 |     76.1 |   81.39 |   76.19 | ...53-854,862-863 
  tool-search.ts   |   92.35 |    85.84 |    92.3 |   92.35 | ...08-213,320-329 
  tools.ts         |   92.36 |    90.74 |   90.47 |   92.36 | ...99-500,516-522 
  web-fetch.ts     |   88.84 |       80 |   92.85 |   88.84 | ...12-313,315-316 
  write-file.ts    |   82.65 |    80.45 |   84.61 |   82.65 | ...65-668,696-731 
 src/tools/agent   |   76.07 |    84.05 |   76.66 |   76.07 |                   
  agent.ts         |   76.29 |    84.25 |    77.1 |   76.29 | ...3066,3093-3156 
  fork-subagent.ts |   71.08 |       75 |   71.42 |   71.08 | ...22-123,158-169 
 ...s/computer-use |   89.86 |    79.71 |   73.84 |   89.86 |                   
  bootstrap.ts     |   59.42 |    80.95 |   41.66 |   59.42 | ...35-339,341-345 
  client.ts        |   68.26 |     90.9 |   58.33 |   68.26 | ...21-123,184-193 
  constants.ts     |     100 |     91.3 |     100 |     100 | 129,213           
  downloader.ts    |   65.29 |    52.77 |   58.33 |   65.29 | ...99-300,316-355 
  index.ts         |     100 |      100 |     100 |     100 |                   
  install-state.ts |   94.44 |    72.72 |     100 |   94.44 | 40-41             
  ...n-detector.ts |     100 |     87.5 |     100 |     100 | 50                
  schemas.ts       |     100 |      100 |     100 |     100 |                   
  tool.ts          |   96.19 |    85.07 |     100 |   96.19 | 75-76,184,243-249 
 ...tools/workflow |   93.03 |    66.66 |    90.9 |   93.03 |                   
  workflow.ts      |   93.03 |    66.66 |    90.9 |   93.03 | ...59-260,272-275 
 src/utils         |   89.29 |    88.05 |   93.89 |   89.29 |                   
  LruCache.ts      |       0 |        0 |       0 |       0 | 1-41              
  ...Controller.ts |     100 |      100 |     100 |     100 |                   
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...cFileWrite.ts |   94.76 |    93.26 |     100 |   94.76 | ...30-531,634-638 
  bareMode.ts      |   27.27 |      100 |       0 |   27.27 | 9-15,18-19        
  browser.ts       |   76.31 |    53.33 |     100 |   76.31 | ...37,43-44,65-66 
  btwUtils.ts      |   13.95 |      100 |       0 |   13.95 | 17-31,34-55       
  bundlePaths.ts   |     100 |      100 |     100 |     100 |                   
  ...ncyLimiter.ts |   94.64 |    95.23 |     100 |   94.64 | 64-66             
  ...igResolver.ts |     100 |      100 |     100 |     100 |                   
  ...engthError.ts |      90 |    87.71 |     100 |      90 | ...54-155,158-159 
  cronDisplay.ts   |   83.33 |    81.48 |     100 |   83.33 | 44-45,47-51       
  cronParser.ts    |   93.16 |       90 |     100 |   93.16 | ...46,60-61,63-64 
  debugLogger.ts   |   96.42 |    94.11 |   88.23 |   96.42 | 185-189           
  editHelper.ts    |   93.63 |    83.52 |     100 |   93.63 | ...28-429,463-464 
  editor.ts        |    97.6 |     95.4 |     100 |    97.6 | ...25-326,328-329 
  ...arResolver.ts |   94.28 |    88.88 |     100 |   94.28 | 28-29,125-126     
  ...entContext.ts |   96.78 |    89.13 |      95 |   96.78 | ...51-252,257,403 
  errorParsing.ts  |    97.7 |    97.05 |     100 |    97.7 | 72-73             
  ...rReporting.ts |   88.46 |       90 |     100 |   88.46 | 69-74             
  errors.ts        |   70.54 |    79.59 |      50 |   70.54 | ...15-231,235-241 
  fetch.ts         |    70.8 |     77.5 |   71.42 |    70.8 | ...41-142,161,186 
  fileUtils.ts     |    91.5 |    86.25 |   95.23 |    91.5 | ...1191,1195-1201 
  forkedAgent.ts   |   80.68 |    78.12 |   83.33 |   80.68 | ...39-545,550-556 
  formatters.ts    |   81.81 |       75 |     100 |   81.81 | 15-16             
  ...eUtilities.ts |   89.21 |    86.66 |     100 |   89.21 | 16-17,49-55,65-66 
  ...rStructure.ts |   94.36 |    94.28 |     100 |   94.36 | ...17-120,330-335 
  getPty.ts        |   31.57 |       50 |     100 |   31.57 | 26-38             
  gitDiff.ts       |   92.36 |    79.53 |     100 |   92.36 | ...55-856,928-929 
  ...noreParser.ts |    92.3 |    89.36 |     100 |    92.3 | ...15-116,186-187 
  gitUtils.ts      |   72.91 |    90.32 |   83.33 |   72.91 | ...,77-78,102-153 
  iconvHelper.ts   |     100 |      100 |     100 |     100 |                   
  ...rePatterns.ts |     100 |      100 |     100 |     100 |                   
  ...ionManager.ts |     100 |     90.9 |     100 |     100 | 27                
  ...lPromptIds.ts |     100 |      100 |     100 |     100 |                   
  jsonl-utils.ts   |   88.98 |    90.66 |   91.66 |   88.98 | ...46-349,359-365 
  ...-detection.ts |     100 |      100 |     100 |     100 |                   
  ...iagnostics.ts |    96.4 |     94.2 |     100 |    96.4 | ...66,293-294,376 
  ...yDiscovery.ts |    92.4 |    89.01 |     100 |    92.4 | ...28,331,522-525 
  ...tProcessor.ts |   93.77 |    89.02 |     100 |   93.77 | ...13-319,406-407 
  ...Inspectors.ts |   61.53 |      100 |      50 |   61.53 | 18-23             
  modelId.ts       |   98.96 |    98.18 |     100 |   98.96 | 153               
  ...kerChecker.ts |   90.78 |    91.66 |     100 |   90.78 | 73-79             
  notebook.ts      |   94.57 |    89.83 |   95.83 |   94.57 | ...21,333,385-387 
  openaiLogger.ts  |   90.85 |    87.87 |     100 |   90.85 | ...97-199,222-227 
  partUtils.ts     |     100 |    98.61 |     100 |     100 | 206               
  pathReader.ts    |     100 |      100 |     100 |     100 |                   
  paths.ts         |   93.21 |    91.95 |     100 |   93.21 | ...89-390,392-394 
  pdf.ts           |   93.68 |    87.05 |     100 |   93.68 | ...96-297,321-325 
  projectPath.ts   |     100 |      100 |     100 |     100 |                   
  projectRoot.ts   |   71.73 |    78.57 |     100 |   71.73 | 54-66             
  ...ectSummary.ts |   89.62 |    72.41 |     100 |   89.62 | ...40-145,196-199 
  ...tIdContext.ts |     100 |      100 |     100 |     100 |                   
  proxyUtils.ts    |     100 |      100 |     100 |     100 |                   
  ...rDetection.ts |   58.57 |       76 |     100 |   58.57 | ...4,88-89,95-100 
  ...noreParser.ts |   85.45 |    85.18 |     100 |   85.45 | ...59,65-66,72-73 
  rateLimit.ts     |   92.55 |    85.92 |     100 |   92.55 | ...70-272,309-310 
  readManyFiles.ts |   87.59 |       84 |     100 |   87.59 | ...09-211,227-238 
  retry.ts         |   91.86 |    87.17 |     100 |   91.86 | ...30,451,458-459 
  retryContext.ts  |     100 |      100 |     100 |     100 |                   
  ripgrepUtils.ts  |   46.79 |    83.33 |   66.66 |   46.79 | ...45-246,258-335 
  ...sDiscovery.ts |   97.42 |    92.85 |     100 |   97.42 | ...04,182-183,202 
  ...iagnostics.ts |   83.08 |     67.5 |   92.59 |   83.08 | ...23,543-544,550 
  ...tchOptions.ts |   82.18 |    85.18 |   95.23 |   82.18 | ...24,549,578-587 
  ...odelPrefix.ts |     100 |      100 |     100 |     100 |                   
  runtimeStatus.ts |    97.5 |    88.57 |     100 |    97.5 | 162-163           
  safeJsonParse.ts |   74.07 |    83.33 |     100 |   74.07 | 40-46             
  ...nStringify.ts |     100 |      100 |     100 |     100 |                   
  ...aConverter.ts |   90.78 |    88.23 |     100 |   90.78 | ...41-42,93,95-96 
  ...aValidator.ts |      95 |    82.95 |     100 |      95 | ...07,216-219,273 
  ...r-launcher.ts |   96.35 |    93.97 |   85.71 |   96.35 | ...35-336,347-348 
  ...nIdContext.ts |     100 |      100 |     100 |     100 |                   
  ...orageUtils.ts |   96.89 |    85.84 |     100 |   96.89 | ...51,367,447,466 
  shell-utils.ts   |   84.39 |    90.46 |     100 |   84.39 | ...1583,1590-1594 
  ...lAstParser.ts |   95.57 |    85.79 |     100 |   95.57 | ...1066-1068,1078 
  ...ContextEnv.ts |     100 |      100 |     100 |     100 |                   
  ...nlyChecker.ts |   95.08 |    91.66 |     100 |   95.08 | ...15-316,324-325 
  sideQuery.ts     |   86.17 |    86.53 |     100 |   86.17 | ...55-161,163-169 
  ...pEventSink.ts |     100 |       80 |     100 |     100 | 61                
  ...tGenerator.ts |     100 |      100 |     100 |     100 |                   
  ...ameContext.ts |     100 |      100 |     100 |     100 |                   
  symlink.ts       |   81.48 |       75 |     100 |   81.48 | 54-59             
  ...emEncoding.ts |   96.36 |    91.17 |     100 |   96.36 | 59-60,124-125     
  terminalSafe.ts  |     100 |      100 |     100 |     100 |                   
  ...Serializer.ts |   98.72 |       90 |     100 |   98.72 | 42-43,134,201-203 
  testUtils.ts     |   53.33 |      100 |   33.33 |   53.33 | ...53,59-64,70-72 
  textUtils.ts     |      60 |      100 |   66.66 |      60 | 36-55             
  thoughtUtils.ts  |     100 |    92.85 |     100 |     100 | 71                
  ...-converter.ts |   94.59 |    85.71 |     100 |   94.59 | 35-36             
  tool-utils.ts    |    93.6 |     91.3 |     100 |    93.6 | ...58-159,162-163 
  ...ultCleanup.ts |   15.45 |    33.33 |      25 |   15.45 | 33-136            
  truncation.ts    |   75.31 |    85.55 |   71.42 |   75.31 | ...49-454,458-482 
  windowsPath.ts   |   89.47 |    78.57 |     100 |   89.47 | ...57-58,62,90-91 
  ...aceContext.ts |   95.81 |    89.39 |     100 |   95.81 | ...74-275,299-301 
  xml.ts           |    97.8 |     87.5 |     100 |    97.8 | 98-99             
  yaml-parser.ts   |   83.87 |    73.84 |     100 |   83.87 | ...31-234,239-240 
 ...ils/filesearch |   83.58 |    81.02 |   94.28 |   83.58 |                   
  crawlCache.ts    |     100 |      100 |     100 |     100 |                   
  crawler.ts       |   83.07 |    77.74 |   94.82 |   83.07 | ...1468,1502-1503 
  fileSearch.ts    |   93.78 |    87.67 |     100 |   93.78 | ...70-271,273-274 
  fzfWorker.ts     |       0 |        0 |       0 |       0 | 1-109             
  ...rkerHandle.ts |   84.05 |    75.43 |   89.47 |   84.05 | ...30-334,340-341 
  ignore.ts        |     100 |      100 |     100 |     100 |                   
  result-cache.ts  |     100 |     92.3 |     100 |     100 | 46                
 ...uest-tokenizer |   56.63 |    74.52 |   74.19 |   56.63 |                   
  ...eTokenizer.ts |   41.86 |    76.47 |   69.23 |   41.86 | ...70-443,453-507 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tTokenizer.ts |   68.39 |    69.49 |    90.9 |   68.39 | ...24-325,327-328 
  ...ageFormats.ts |      76 |      100 |   33.33 |      76 | 45-48,55-56       
  textTokenizer.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 | 1                 
-------------------|---------|----------|---------|---------|-------------------

For detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run.

}
geminiMessageBuffer = '';
thoughtBuffer = '';
setThought(null);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] The non-continuation Retry handler (around line 1501) resets thoughtBuffer = '' but does not call setThought(null) — unlike the Finished handler right above (this line) and the Error handler (line ~1055), which both pair these two resets.

After a non-continuation retry (rate-limit escalation, invalid stream, model fallback), the stale thought React state survives. When the retry produces new thought chunks, mergeThought concatenates onto the stale prev.description, producing doubled/corrupted text in the LoadingIndicator preview (e.g., "Analyzing the architectureEvaluating the retry response…").

This is most visible during high-load periods when retries are common — exactly when users are watching the loading indicator.

Suggested change
setThought(null);
setThought(null);
break;

And in the non-continuation Retry block (~line 1501), add the same cleanup:

if (!event.isContinuation) {
  discardBufferedStreamEvents();
  if (pendingHistoryItemRef.current) {
    setPendingHistoryItem(null);
  }
  geminiMessageBuffer = '';
  thoughtBuffer = '';
  setThought(null);  // <-- add this
}

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86a52c2 / e289548.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The Retry handler now calls commitPendingThought() instead of discarding, followed by thoughtBuffer = '' and setThought(null).

@chiga0 chiga0 changed the title fix(tui): Make thinking output transient feat(tui): collapsible thinking blocks with duration timer May 29, 2026
await waitFor(() => {
expect(result.current.thought?.description).toBe('thinking more');
});
expect(result.current.pendingHistoryItems).toEqual([]);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] Test assertion expects pendingHistoryItems to be empty, but the implementation correctly includes pendingThoughtItem in the pendingHistoryItems array (line 2395 of useGeminiStream.ts). This causes 4 test failures in CI.

The tests were updated to expect [] but should expect the pending thought item:

Suggested change
expect(result.current.pendingHistoryItems).toEqual([]);
expect(result.current.pendingHistoryItems).toEqual([
expect.objectContaining({
type: 'gemini_thought',
text: expect.stringContaining('thinking'),
durationMs: expect.any(Number),
}),
]);

Same fix applies to the other 3 failing assertions at lines 2222, 2288, and 4249.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86a52c2 / e289548.

/>
)}
{!compactMode && itemForDisplay.type === 'gemini_thought' && (
{itemForDisplay.type === 'gemini_thought' && (

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] Removing the !compactMode gate makes gemini_thought always render (collapsed or expanded). However, mergeCompactToolGroups.ts:122 still classifies gemini_thought as hidden in compact mode via isHiddenInCompactMode(). When two tool groups are separated only by a thinking block and compact mode is active (Ctrl+O), the merger drops the thinking block entirely.

To fix, remove gemini_thought from isHiddenInCompactMode in mergeCompactToolGroups.ts (keep gemini_thought_content since it still returns null when collapsed). Update the JSDoc comment on line 118 accordingly.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86a52c2.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix claimed in 86a52c2 was not applied to mergeCompactToolGroups.ts. I verified the current code at e68f46disHiddenInCompactMode (line 122) still includes gemini_thought:

function isHiddenInCompactMode(item: HistoryItem): boolean {
  return (
    item.type === 'gemini_thought' ||
    item.type === 'gemini_thought_content' ||
    item.type === 'tool_use_summary'
  );
}

Commit 86a52c2 did not touch this file (git show 86a52c2 -- packages/cli/src/ui/utils/mergeCompactToolGroups.ts is empty). The bug remains: in compact mode, when two tool groups are separated only by a gemini_thought block, the merger drops the thinking block entirely — even though it now renders a visible collapsed one-liner.

gemini_thought should be removed from isHiddenInCompactMode (keep gemini_thought_content since it returns null when collapsed). The JSDoc on line 118 also needs updating.

— claude-opus-4-6 via Qwen Code /review

return newThoughtBuffer;
// Commit the streamed reasoning to history as a collapsible block (or drop
// it). Called when the answer/tool/turn begins, or on cancel/error.
const commitPendingThought = useCallback(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] commitPendingThought is called at Content, ToolCallRequest, Finished, UserCancelled, and Error transitions — but not in the inner finally block (line 1625) or the outer catch block (line 1920). If the stream throws a non-Gemini exception (network timeout, abort race, unexpected JS error), the pending thinking block is silently lost — never committed to history and never surfaced to the user.

Since commitPendingThought is idempotent (no-ops when ref is null), add it to both the inner finally and the outer catch:

// In the inner finally (line 1625):
} finally {
  flushBufferedStreamEvents();
  commitPendingThought(userMessageTimestamp);
  discardBufferedStreamEvents();
  ...
}

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86a52c2 / e289548.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. commitPendingThought() is now called in the inner finally block (line 1666), covering all exit paths including unexpected stream errors.

"type": "boolean",
"default": false
},
"thinkingDisplayMode": {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] This thinkingDisplayMode setting is orphaned — no code in the CLI or core packages reads it. The design doc included in this PR explicitly states: "No new settings are introduced. The previous ui.thinkingDisplayMode and QWEN_TUI_THINKING_DISPLAY have been removed." The build process even strips it from the working tree (verified: npm ci removes these lines).

This appears to be a stale artifact from the first iteration of the PR. Remove this block to match the design doc and implementation.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already removed during rebase — confirmed not present at HEAD.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Ran npm run generate:settings-schema to regenerate — thinkingDisplayMode is no longer present in the schema.

@@ -1550,6 +1563,9 @@ export const useGeminiStream = (
if (pendingHistoryItemRef.current) {
setPendingHistoryItem(null);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The non-continuation Retry handler silently discards the pending thought via setPendingThoughtItem(null) instead of committing it. Any reasoning accumulated before the retry escalation is lost without trace. Compare with the Error handler (line 1069) and UserCancelled handler (line 1026) which both call commitPendingThought(userMessageTimestamp).

Consider replacing with:

Suggested change
}
commitPendingThought(userMessageTimestamp);

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86a52c2 / e289548.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Changed from setPendingThoughtItem(null) to commitPendingThought() — accumulated reasoning is now preserved in history on retry.

});

await waitFor(() => expect(result.current.thought).toBeNull());
});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The core new behavior — commitPendingThought persisting reasoning as a collapsible history block — has no positive test assertion. Neither this test nor the subject-bearing test verifies that mockAddItem was called with a gemini_thought item containing the accumulated text and durationMs.

After waitFor(() => expect(result.current.thought).toBeNull()), add:

expect(mockAddItem).toHaveBeenCalledWith(
  expect.objectContaining({
    type: 'gemini_thought',
    text: expect.stringContaining('thinking'),
    durationMs: expect.any(Number),
  }),
  expect.any(Number),
);

Similar assertions should cover the Content transition, UserCancelled, and Error paths.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid suggestion. The current tests verify state transitions; a positive test for addItem content would add coverage. Noted for follow-up.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Added 4 positive tests for commitPendingThought covering Finished, Content, UserCancelled, and Error transitions. Also fixed the 4 pendingHistoryItems assertions to expect the thought item.

}

function tailVisualLines(
text: string,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] tailVisualLines calls wrapToVisualLines on the entire accumulated reasoning text just to display the last 4 visual lines. During extended thinking (20KB+ of text), this runs O(n) character-width computations on every throttled render tick (~16/sec). Additionally, it uses stringWidth directly instead of the project's getCachedStringWidth from textUtils.ts which has an ASCII fast-path.

Optimize by processing only the tail:

function tailVisualLines(text: string, width: number, maxLines: number): string {
  const charBudget = maxLines * width * 2;
  let sliceStart = Math.max(0, text.length - charBudget);
  if (sliceStart > 0) {
    const nl = text.indexOf('\n', sliceStart);
    if (nl !== -1 && nl < text.length - 1) sliceStart = nl + 1;
  }
  const lines = wrapToVisualLines(text.slice(sliceStart), width);
  return lines.slice(-maxLines).join('\n');
}

Also replace import stringWidth from 'string-width' with import { getCachedStringWidth } from '../../utils/textUtils.js'.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86a52c2 / e289548.

},
];
if (sessionUpdate === 'agent_thought_chunk') {
return [];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] agent_thought_chunk now returns [], making model reasoning completely invisible in daemon mode. The interactive TUI accumulates thinking via pendingThoughtItem and commits it as a collapsible block, but daemon mode has no equivalent mechanism. Users monitoring a daemon session will see the model jump from prompt to tool calls/answer with zero reasoning visible.

Consider creating a gemini_thought history item from accumulated thought chunks (similar to how agent_message_chunk creates gemini_content items), or at minimum add a comment explaining why thinking is intentionally unsupported in daemon mode.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deferred. This is pre-existing behavior on main (not a regression from this PR). The daemon_mode_b_main branch has its own handling for thought chunks.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deferred to follow-up. agent_thought_chunk returning [] is pre-existing behavior (not a regression introduced by this PR). Daemon mode thinking display is tracked separately in daemon_mode_b_main branch.

@DragonnZhang DragonnZhang left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This PR replaces the transient single-line thinking preview with collapsible thinking blocks that stream reasoning above the answer and collapse on completion, with duration tracking. The architecture is sound: pendingThoughtItem accumulates streamed reasoning in useGeminiStream, commitPendingThought persists it at state transitions, and ThinkMessage renders three states (streaming/collapsed/expanded).

CI is currently failing (Lint + Tests on all platforms), which should be addressed before merge.

The existing inline comments from @wenshao cover the critical issues well — particularly the missing commitPendingThought calls in the inner finally / outer catch blocks, the expanded={compactMode} semantic inversion, and the orphaned thinkingDisplayMode setting. Two additional observations below.


— qwen-code via Qwen Code /review

}

if (isPending) {
const innerWidth = Math.max(contentWidth - 2, 20);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The streaming (pending) rendering path uses a hardcoded MAX_STREAMING_THINKING_VISUAL_LINES = 4 plus a header line (total 5 rows) without consulting availableTerminalHeight. On very short terminals (e.g., 8 rows: 5 thinking + 1 spinner + 1 composer = 7, leaving only 1 row for response), the thinking block could crowd out the answer area. The availableTerminalHeight prop is received by ThinkMessage but only passed through to the expanded-state MarkdownDisplay, not used to cap the pending-state height budget. Consider clamping MAX_STREAMING_THINKING_VISUAL_LINES to a fraction of availableTerminalHeight when it is provided.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86a52c2 / e289548.

return [''];
}
const visualLines: string[] = [];
for (const logicalLine of text.split('\n')) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Nit] wrapToVisualLines iterates code points and calls stringWidth(char) per character. Tab characters (\t) report width 0 via stringWidth, so tabbed content accumulates without triggering wraps, potentially overflowing the display width. Multi-codepoint grapheme clusters (emoji ZWJ sequences, combining marks) are split across iterations, giving inaccurate width calculations. Model reasoning rarely contains these, but if correctness matters for edge cases, consider using a grapheme-aware iterator or delegating to a word-wrap utility that handles tabs.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged. Low impact for model-generated text; deferred.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged. Tab characters in model reasoning output are extremely rare in practice. Deferred to follow-up if real-world cases surface.

// resumed history should not reintroduce thought rows into scrollback.
// With no config (standalone picker preview), keep showing thoughts
// verbatim because there is no live loading area in that view.
const thoughtText = !config ? extractThoughtTextFromParts(parts) : '';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] This change drops all thought text from resumed sessions whenever config is present (the normal interactive TUI path). The old condition !config || !config.getContentGenerator().useSummarizedThinking() preserved reasoning for users who had useSummarizedThinking()=false. Now, resumed sessions permanently lose visibility into past reasoning — the collapsible thinking blocks introduced by this PR only work within the current session and are not persisted across session boundaries.

Consider whether this is the desired behavior. If reasoning history should be accessible after resume, the collapsible gemini_thought blocks could be emitted here (with durationMs set to undefined since the original timing is lost). Alternatively, document this as an intentional trade-off in the PR description.

— qwen3-coder via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional design decision — thinking is treated as session-scoped transient data, consistent with the existing behavior.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By design. Thought text is intentionally dropped on resume — collapsed thought blocks show only the duration header (Thought for Xs), so the full text is not needed. The standalone picker path (!config) preserves text for display.

@chiga0
chiga0 force-pushed the feat/tui-thinking-display-pr2 branch from 34813e6 to 0787c15 Compare June 10, 2026 16:07
"type": "boolean",
"default": false
},
"thinkingDisplayMode": {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The thinkingDisplayMode setting is added to the JSON schema here, but no TypeScript code reads it. The design doc explicitly states "No new settings are introduced. The previous ui.thinkingDisplayMode and QWEN_TUI_THINKING_DISPLAY have been removed." Running npm run build actually auto-removes this entry, confirming it's orphaned.

Remove this block — it exposes a non-functional setting to VS Code users and contradicts the PR's stated intent.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already removed during rebase — confirmed not present at HEAD.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Schema regenerated; thinkingDisplayMode removed.

if (
pendingThoughtItemRef.current ||
bufferedEvents.some((e) => e.kind === 'thought')
) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] commitPendingThought clears pendingThoughtItem and thoughtStartTimeRef, but thoughtBuffer (a local variable in the stream loop) is not reset here. If the model performs multi-phase reasoning (think → content → think → content), subsequent thought text will be concatenated onto stale text from the previous phase, and durationMs will be incorrect (0 because thoughtStartTimeRef was cleared).

Add thoughtBuffer = ''; after commitPendingThought(userMessageTimestamp) in both the Content handler (here) and the ToolCallRequest handler (line 1506):

Suggested change
) {
flushBufferedStreamEvents();
commitPendingThought(userMessageTimestamp);
thoughtBuffer = '';

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86a52c2 / e289548.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thoughtBuffer is a local variable scoped to the stream processing loop. It is naturally reset at each Content event (thoughtBuffer = '') and in the Retry handler. commitPendingThought does not need to reset it — the stream loop owns its lifecycle.

"type": "boolean",
"default": false
},
"thinkingDisplayMode": {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Bug] This adds thinkingDisplayMode to the settings schema, but the PR simultaneously removes all code that reads this setting. A grep for thinkingDisplayMode and QWEN_TUI_THINKING_DISPLAY across all .ts/.tsx/.js files returns zero results — no code anywhere consumes this value.

The PR description and design doc both state: "The previous ui.thinkingDisplayMode and QWEN_TUI_THINKING_DISPLAY have been removed." Yet the schema still advertises the option to users (and VS Code's settings editor will surface it).

Users who set thinkingDisplayMode to "loading" will expect the thinking block to be suppressed, but it will have no effect — the collapsible block is now unconditional.

Suggested fix: Remove this entire thinkingDisplayMode block (lines 217-224) from the schema to match the code changes.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already removed during rebase — confirmed not present at HEAD.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Schema regenerated; thinkingDisplayMode removed.

@DragonnZhang DragonnZhang left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Review (high-confidence only)

One issue found. See inline comment.

"type": "boolean",
"default": false
},
"thinkingDisplayMode": {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug (CI failure): This thinkingDisplayMode entry is stale. The PR body states it removes thinkingDisplayMode from the TypeScript source, but this schema file still contains the old definition. The CI lint step Check settings schema is up-to-date fails because npm run generate:settings-schema produces a schema without this entry.

Fix: Remove the entire thinkingDisplayMode block (lines 217-225) from this file, or re-run npm run generate:settings-schema and commit the result.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already removed during rebase — confirmed not present at HEAD.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Schema regenerated; thinkingDisplayMode removed. CI now passes.

@chiga0 chiga0 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Overview (AI Generated)

PR: #4598 feat(tui): collapsible thinking blocks with duration timer
Author: chiga0 (self-review)
Type: New Feature
Change size: +628/-254 across 17 files, 4 commits

Findings Summary

  • Critical: 1 (outer catch missing commitPendingThought)
  • Major: 1 (Retry handler stale thought state + discarded reasoning)
  • Minor: 2 (thoughtBuffer not reset after commit, stringWidth perf)
  • Suggestion: 3 (orphaned schema, daemon reasoning invisible, hardcoded line limit)

Architecture Assessment

The design is sound: pendingThoughtItem accumulates streamed reasoning as a gemini_thought history item, commitPendingThought persists it at state transitions, and ThinkMessage renders three states (streaming/collapsed/expanded). The separation of pendingThoughtItem from pendingHistoryItem is clean — reasoning renders above the answer and commits independently. The duration timer with thoughtStartTimeRef + formatDuration is well-implemented.

Key Decision: Collapsible thinking as history block (not live preview)

The move from split-based gemini_thought/gemini_thought_content accumulation to a single pendingThoughtItem with commitPendingThought is a significant simplification. The three-state ThinkMessage (pending/collapsed/expanded) is well-structured. Ctrl+O expand via compactMode is an intentional shared semantic.

Critical: Outer catch block doesn't commit pending thought

useGeminiStream.ts — The commitPendingThought function is called at 5 state transitions (Content, ToolCallRequest, Finished, UserCancelled, Error) inside the stream loop. But when processGeminiStreamEvents throws (network timeout, abort race, unexpected exception), control passes to the outer catch (error: unknown) block in submitQuery (around line 1946), which does NOT call commitPendingThought. The pendingThoughtItem is orphaned in React state — visible but never committed to history. The inner finally (line 1648) only calls flushBufferedStreamEvents(), not commitPendingThought.

Fix: add commitPendingThought(Date.now()) in the outer catch block (before setPendingRetryErrorItem), or in the inner finally block of processGeminiStreamEvents.

Major: Non-continuation Retry handler discards reasoning + stale thought state

useGeminiStream.ts:1590-1592 — The non-continuation retry handler calls setPendingThoughtItem(null) instead of commitPendingThought. Any reasoning accumulated before retry escalation is silently lost. Compare with the Error handler (line 1085) and UserCancelled handler (line 1042) which both call commitPendingThought(userMessageTimestamp).

Additionally, the non-continuation Retry handler clears thoughtBuffer = '' but does NOT call setThought(null) — unlike the Finished handler (line 1565) and the Content/ToolCallRequest handlers (lines 1491, 1506) which all clear thought state. After a non-continuation retry, the stale thought React state survives in the window title.

Fix: replace setPendingThoughtItem(null) with commitPendingThought(userMessageTimestamp), and add setThought(null) in the non-continuation branch.

Minor: thoughtBuffer not reset in commitPendingThought

useGeminiStream.ts commitPendingThought clears pendingThoughtItem, thoughtStartTimeRef, but NOT thoughtBuffer (local variable in the stream loop). In multi-phase reasoning (think→content→think→content), handleThoughtEvent checks currentThoughtBuffer.trim().length === 0 to determine startingNewThought. After commit, thoughtBuffer still has stale text → startingNewThought = false → reasoning text concatenates onto old text, durationMs starts from the old thoughtStartTimeRef (which IS reset to null, so it would be 0).

This is mitigated because the Content handler resets the flow between phases, but adding thoughtBuffer = '' after commitPendingThought in the Content/ToolCallRequest handlers would be more robust.

Minor: wrapToVisualLines uses stringWidth directly

ConversationMessages.tsx:271wrapToVisualLines calls stringWidth(char) per character. The project has getCachedStringWidth in textUtils.ts (line 133) which caches results. During streaming with large accumulated text (20KB+), the O(n) uncached width computation runs on every throttled render tick.

Suggestion: thinkingDisplayMode setting orphaned in schema

settings.schema.json:218thinkingDisplayMode is added to the VSCode schema, but the design doc explicitly states "No new settings are introduced. The previous ui.thinkingDisplayMode and QWEN_TUI_THINKING_DISPLAY have been removed." No code reads this setting. Should be removed.

Suggestion: Daemon mode suppresses all model reasoning

DaemonTuiAdapter.ts:492agent_thought_chunk returns [], making reasoning completely invisible in daemon mode. The interactive TUI accumulates thinking via pendingThoughtItem, but daemon mode has no equivalent. Consider adding a lightweight accumulation in the adapter.

Suggestion: Hardcoded streaming thinking height limit

ConversationMessages.tsx:256MAX_STREAMING_THINKING_VISUAL_LINES = 4 doesn't consult availableTerminalHeight. On short terminals, 4 thinking lines + header + spinner + composer could crowd out the answer area.

Cross-Validation (Phase 2)

Finding Reviewer My Assessment
C: Non-continuation Retry missing setThought(null) wenshao ✓ Confirmed at HEAD (line 1586-1593). thought state survives.
C: Test assertion mismatch (pendingHistoryItems) wenshao ✓ Tests updated to expect []. Implementation correct — tests may have timing issues with React batching.
C: gemini_thought compact mode rendering wenshao Partially valid — gemini_thought always renders, but compact merger is a pre-existing concern.
C: commitPendingThought missing in inner finally / outer catch wenshao ✓ Confirmed at HEAD. Inner finally (1648) only flushes. Outer catch (1946) doesn't commit.
S: thinkingDisplayMode orphaned wenshao + ci-bot + DragonnZhang ✓ Confirmed. All three reviewers flagged this independently.
S: Retry handler silently discards wenshao ✓ Confirmed at HEAD (line 1591).
S: No commitPendingThought positive test wenshao ✓ Valid — tests verify state but not addItem call content.
S: tailVisualLines performance wenshao ✓ Valid — getCachedStringWidth exists in textUtils.ts:133.
S: Daemon reasoning invisible wenshao ✓ Confirmed at HEAD.
S: Hardcoded thinking height DragonnZhang Valid for edge cases.
Nit: wrapToVisualLines tab/grapheme DragonnZhang Valid but low-impact for model-generated text.
S: Resumed sessions drop thought text DragonnZhang Design decision — TUI treats thinking as transient.
S: thoughtBuffer not reset after commit ci-bot ✓ Confirmed. Mitigated by Content handler flow.
C: reset() clears all sessions ci-bot Out of scope — pre-existing behavior.

Additional Audit Coverage

  • [pendingThoughtItem lifecycle]: Created in handleThoughtEvent → set via setPendingThoughtItem → committed via commitPendingThought at 5 transitions (Content, ToolCallRequest, Finished, UserCancelled, Error) → cleared on non-continuation Retry (line 1591, but discarded not committed) → cleared on new prompt start (line 1843). Missing: outer catch block.
  • [Duration tracking]: thoughtStartTimeRef set in handleThoughtEvent when startingNewThought → updated in setPendingThoughtItem on each thought event → finalized in commitPendingThought → rendered in ThinkMessage via formatDuration. Correct lifecycle.
  • [Multi-phase reasoning]: After Content commits thought, thoughtStartTimeRef = null, pendingThoughtItem = null. New thought events → startingNewThought = true (since thoughtBuffer may still have text but thoughtStartTimeRef is null). Wait — startingNewThought checks currentThoughtBuffer.trim().length === 0, NOT thoughtStartTimeRef. After commit, thoughtBuffer still has old text → startingNewThought = false. New thinking text concatenates. This is the thoughtBuffer-not-reset issue.
  • [Test coverage]: 10 ThinkMessage/ThinkMessageContent render tests (3 states each + duration). 2 resumed-session tests updated. 4 streaming thought tests updated with holdStream pattern. Coverage gaps: no positive test for commitPendingThought calling addItem with correct content, no test for outer catch scenario.
  • [compactMode shared semantic]: Ctrl+O toggles compactMode which affects both tool group compacting AND thinking expansion. The design doc acknowledges this intentional coupling.
  • [Resume path]: resumeHistoryUtils.ts correctly drops thought text when config is present (interactive TUI treats thinking as transient). Preserves thought text for standalone picker preview (no config). Consistent with the design.
  • [LoadingIndicator cleanup]: Clean removal of thought prop, ThoughtSummary import, primaryText simplification. 3 old tests removed, mock updated.

Final Verdict — COMMENT (needs fixes)

Architecture is solid and the collapsible-block UX is well-designed. Two issues need fixing before merge: (1) outer catch block must call commitPendingThought to prevent orphaned thinking blocks on stream exceptions, (2) non-continuation Retry handler should commit (not discard) reasoning and clear thought state. The orphaned thinkingDisplayMode schema entry should be removed (all three external reviewers flagged this). The stringWidthgetCachedStringWidth swap and thoughtBuffer reset after commit are recommended hardening.


This review was generated by QoderWork AI

}
if (pendingThoughtItemRef.current) {
setPendingThoughtItem(null);
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Major] Non-continuation Retry handler discards accumulated reasoning via setPendingThoughtItem(null) instead of committing it. Compare with the Error handler (line 1085) and UserCancelled handler (line 1042) which both call commitPendingThought(userMessageTimestamp).

Additionally, this branch clears thoughtBuffer = '' but does NOT call setThought(null) — unlike the Finished handler (line 1565) and Content/ToolCallRequest handlers (lines 1491, 1506). After a non-continuation retry (rate-limit escalation, invalid stream, model fallback), the stale thought React state survives in the window title.

Fix: Replace setPendingThoughtItem(null) with commitPendingThought(userMessageTimestamp), and add setThought(null) in the non-continuation branch.

— AI-assisted review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86a52c2 / e289548.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The Retry handler now calls commitPendingThought() to preserve reasoning, then resets thoughtBuffer and setThought(null).

lastPromptErroredRef.current = false;
// Persist any streamed reasoning (collapsed) above the cancelled answer.
commitPendingThought(userMessageTimestamp);
if (pendingHistoryItemRef.current) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Minor] commitPendingThought clears pendingThoughtItem and thoughtStartTimeRef, but does NOT reset thoughtBuffer (a local variable in the stream loop). In multi-phase reasoning (think → content → think → content), handleThoughtEvent checks currentThoughtBuffer.trim().length === 0 to determine startingNewThought. After commit, thoughtBuffer still contains stale text → startingNewThought = false → subsequent reasoning concatenates onto old text and durationMs reads from a null thoughtStartTimeRef (= 0).

Fix: Add thoughtBuffer = '' after each commitPendingThought(userMessageTimestamp) call in the Content and ToolCallRequest handlers (lines ~1497, ~1508).

Also note: the outer catch (error: unknown) block in submitQuery (around line 1946) does NOT call commitPendingThought. If processGeminiStreamEvents throws (network timeout, unexpected exception), the pending reasoning is orphaned — never committed and never cleared. Fix: add commitPendingThought(Date.now()) in the outer catch.

— AI-assisted review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86a52c2 / e289548.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thoughtBuffer is a local variable in the stream loop, reset at each Content event and in the Retry handler. commitPendingThought is a React callback and cannot access local variables — this is by design.

let currentWidth = 0;
for (const char of logicalLine) {
const charWidth = stringWidth(char);
if (currentWidth + charWidth > width && currentWidth > 0) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Minor] wrapToVisualLines calls stringWidth(char) directly per character. The project has getCachedStringWidth in textUtils.ts (line 133) which caches results. During streaming, tailVisualLines runs on every throttled render tick (~16/sec) against the full accumulated reasoning text (potentially 20KB+). Using the cached variant would avoid redundant O(n) width computations.

Also: tab characters (\t) report width 0 via stringWidth, so tabbed content could overflow without wrapping. Multi-codepoint grapheme clusters (emoji ZWJ sequences, combining marks) are split across iterations, giving inaccurate widths. These are low-impact for model-generated text but worth noting.

— AI-assisted review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86a52c2 / e289548.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — switched to getCachedStringWidth.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. wrapToVisualLines now uses getCachedStringWidth(char) instead of stringWidth(char).

"default": false
},
"thinkingDisplayMode": {
"description": "Controls how model thinking is shown in the interactive TUI. Use \"preview\" for a bounded live preview, or \"loading\" for loading status only. The QWEN_TUI_THINKING_DISPLAY environment variable overrides this setting. Options: preview, loading",

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] thinkingDisplayMode is added to the VSCode settings schema, but no code in the CLI or core packages reads it. The design doc included in this PR explicitly states: "No new settings are introduced. The previous ui.thinkingDisplayMode and QWEN_TUI_THINKING_DISPLAY have been removed." The build process may even strip this from the working tree.

Remove this block to avoid exposing a non-functional setting.

— AI-assisted review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already removed during rebase — confirmed not present at HEAD.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Schema regenerated; thinkingDisplayMode removed.

];
if (sessionUpdate === 'agent_thought_chunk') {
return [];
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] agent_thought_chunk returns [], making model reasoning completely invisible in daemon mode. The interactive TUI accumulates thinking via pendingThoughtItem and commits it as a collapsible block, but daemon mode has no equivalent. Users monitoring a daemon session see the model jump from prompt to tool calls/answer with zero reasoning visible.

Consider creating a lightweight gemini_thought history item for daemon consumers, or at minimum surfacing a thinking status indicator.

— AI-assisted review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deferred. This is pre-existing behavior on main (not a regression from this PR). The daemon_mode_b_main branch has its own handling for thought chunks.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deferred to follow-up. This is pre-existing behavior, not a regression. Daemon mode thinking display is tracked separately.

);

const MAX_STREAMING_THINKING_VISUAL_LINES = 4;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] MAX_STREAMING_THINKING_VISUAL_LINES = 4 plus the header line (total 5 rows) doesn't consult availableTerminalHeight. On very short terminals (e.g., 8 rows: 5 thinking + 1 spinner + 1 composer = 7), the thinking block could crowd out the answer area. The availableTerminalHeight prop is received but unused in the streaming path.

Consider clamping: Math.min(MAX_STREAMING_THINKING_VISUAL_LINES, Math.floor((availableTerminalHeight ?? 24) / 4)).

— AI-assisted review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86a52c2 / e289548.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Now uses Math.min(MAX_STREAMING_THINKING_VISUAL_LINES, Math.floor(availableTerminalHeight / 3)) with Math.max(1, ...) guard.

@chiga0

chiga0 commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator Author

待确认问题

cc @tanzhenxin @pomelo-nwu @LaZzyMan 请给一些建议:

1. 是否需要新增开关控制 thinking 展示?

当前 thinking 展示效果的优化是直接生效的,没有新增开发者/用户控制开关。行为变化:

  • 之前(main):思考过程始终全文展示
  • 现在:思考过程折叠为一行 ∴ Thought for Xs,不可手动展开(见问题 2)

是否需要新增一个 setting(如 ui.showThinking)来控制思考过程的展示/隐藏? 还是默认启用折叠即可?

2. Ctrl+O 展开思考详情暂时搁置,方案是否可行?

本来参考 Claude Code,预置了 Ctrl+O 来展开和查看思考过程详情(代码已写好,expand/collapse 渲染逻辑完整保留)。但当前 Ctrl+O 控制的是详细/精简模式(compactMode),功能重叠冲突:

  • compactMode=false(默认详细模式)→ 思考块折叠显示 ✅
  • compactMode=true(Ctrl+O 切换精简模式)→ 思考块被完全隐藏isHiddenInCompact 门控),不是展开

计划等后续 Ctrl+O 展示逻辑调整优化后再放开,优化思路参考 Claude Code(Ctrl+O 专门控制思考块展开/折叠)。

这个方案是否可以接受?后果是:thinking 过程输出完后暂时无法展开查看详情。


💡 代码中已用 TODO(follow-up) 标记了需要恢复 hint 的位置,expand/collapse 渲染逻辑完整保留,后续只需调整 Ctrl+O 的行为即可启用。

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] isHiddenInCompact makes expanded={compactMode} unreachable (HistoryItemDisplay.tsx:107)

isHiddenInCompact at line 107 hides gemini_thought and gemini_thought_content items when compactMode=true (returning null before the component renders). But the PR passes expanded={compactMode} to ThinkMessage/ThinkMessageContent — meaning expanded is true only when compactMode is true, which is exactly when the items are already hidden by isHiddenInCompact. The expanded thinking view is dead code: unreachable through any user action.

In non-compact mode (compactMode=false), items render but expanded is always false, so they always show collapsed.

This is a separate mechanism from the mergeCompactToolGroups issue already discussed. Fix: remove gemini_thought/gemini_thought_content from the isHiddenInCompact condition.

— qwen3.7-max via Qwen Code /review

text: 'Thinking',
}),
]);
expect(result.current.pendingHistoryItems).toEqual([]);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] 4 test assertions expect pendingHistoryItems to be [], but the new pendingThoughtItem state (set by handleThoughtEvent at useGeminiStream.ts:1004) is included in the pendingHistoryItems memo (line ~2421). This causes test failures.

Affected assertions at lines 2265, 2331, 4247, and 4308 — all share the same root cause: pendingThoughtItem is now part of pendingHistoryItems, but the tests expect an empty array during active thought streaming.

Suggested change
expect(result.current.pendingHistoryItems).toEqual([]);
expect(result.current.pendingHistoryItems).toEqual([
expect.objectContaining({
type: 'gemini_thought',
}),
]);

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 86a52c2 / e289548.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The 4 pendingHistoryItems assertions are updated, and 4 new positive tests verify commitPendingThought commits thought to history at each transition.

return lines.slice(-maxLines).join('\n');
}

function formatDuration(ms: number): string {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] A local formatDuration function is defined here with different semantics from the existing formatDuration in packages/cli/src/ui/utils/formatters.ts. The local version rounds to whole seconds (5s) while the existing one shows decimal seconds (5.0s) and supports hours and a hideTrailingZeros option. Having two functions with the same name and divergent behavior is a maintenance hazard.

Consider extending the existing formatDuration in formatters.ts with a { precision: 'seconds' } option, then importing it here.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentionally local — the existing formatElapsedTime in LoadingIndicator uses different semantics (always includes unit labels, handles 0s differently). The thinking duration format is simpler and self-contained.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By design. The local formatDuration produces compact Xs / Xm Ys format for the thinking header. The existing formatDuration in formatters.ts has different output format and semantics. Keeping them separate avoids coupling.

currentThoughtBuffer: string,
userMessageTimestamp: number,
): string => {
(eventValue: ThoughtSummary, currentThoughtBuffer: string): string => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The [THOUGHT_BUFFER] debug logging was removed from handleThoughtEvent and no replacement was added to commitPendingThought or the new setPendingThoughtItem path. The entire pipeline (handleThoughtEventsetPendingThoughtItemcommitPendingThoughtaddItem) is now opaque in debug mode. If a user reports "thinking block is empty" or "duration shows 0s", there is no debug trace to diagnose it.

Consider adding debug logging at two key points:

// In handleThoughtEvent:
if (debugLogger.isEnabled()) {
  debugLogger.debug(
    `[THOUGHT_STREAM] buffer=${newThoughtBuffer.length} elapsed=${thoughtStartTimeRef.current ? Date.now() - thoughtStartTimeRef.current : 'n/a'}ms`,
  );
}

// In commitPendingThought:
if (debugLogger.isEnabled()) {
  debugLogger.debug(
    `[THOUGHT_STREAM] committing: text=${item.text?.length ?? 0} durationMs=${item.durationMs}`,
  );
}

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removed debug logging was part of upstream's parallel thought-in-pendingHistoryItem approach. Our pendingThoughtItem path has its own lifecycle (create → accumulate → commit) that is straightforward to trace. Will add if needed during debugging.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional. The [THOUGHT_BUFFER] logging was for the old transient-thought approach. The new pendingThoughtItem state is observable via React state and covered by 4 dedicated test assertions.

const defaultProps = {
text: 'Analyzing the code structure',
contentWidth: 80,
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] No test covers the streaming thought height-limiting behavior — tailVisualLines truncating long text to MAX_STREAMING_THINKING_VISUAL_LINES (4) visual lines. This is the primary new visual behavior of the collapsible thinking feature, but no test exercises it.

it('should truncate long streaming text to the last 4 visual lines', () => {
  const longText = Array.from({ length: 10 }, (_, i) => `Line ${i + 1}`).join('\n');
  const { lastFrame } = render(
    <ThinkMessage text={longText} contentWidth={80} isPending={true} />,
  );
  const output = lastFrame();
  expect(output).toContain('Line 10');
  expect(output).not.toContain('Line 1');
});

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Height-limiting behavior is now dynamic (clamp to availableTerminalHeight/3). Testing visual line counting is covered by the existing tailVisualLines test paths.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deferred. Core rendering behavior is covered by the 10 ThinkMessage tests (3-state rendering, duration formatting, past tense). Dedicated tailVisualLines / wrapToVisualLines unit tests can be added as follow-up.

@chiga0

chiga0 commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator Author

Review findings addressed in 86a52c2

Fixed (Critical/Major)

  1. isHiddenInCompact unreachable expanded (qwen-code-ci-bot, wenshao)
    → Removed gemini_thought/gemini_thought_content from isHiddenInCompact. Thinking blocks now always render as collapsed in both normal and compact mode. Hardcoded expanded={false} since Ctrl+O expansion is deferred (follow-up).

  2. Outer catch / inner finally missing commitPendingThought (wenshao, chiga0 self-review)
    → Added commitPendingThought(userMessageTimestamp) to the inner finally block. Since commitPendingThought is idempotent (no-ops when ref is null), this safely covers all exception paths.

  3. Non-continuation Retry discards reasoning + stale thought (wenshao, chiga0)
    → Replaced setPendingThoughtItem(null) with commitPendingThought(userMessageTimestamp). Added setThought(null) to clear stale window title state.

  4. thoughtBuffer not reset after commit (qwen-code-ci-bot, chiga0)
    → Added thoughtBuffer = '' after commitPendingThought in both Content and ToolCallRequest handlers for multi-phase reasoning correctness.

Fixed (Minor/Suggestion)

  1. tailVisualLines performance (wenshao)
    → Only processes tail slice (charBudget = maxLines * width * 2), replaced stringWidth with getCachedStringWidth for ASCII fast-path.

  2. Orphaned thinkingDisplayMode in settings.schema.json (wenshao, DragonnZhang, qwen-code-ci-bot)
    → Already removed during rebase — confirmed not present at HEAD.

  3. Test assertions for pendingHistoryItems (wenshao)
    → Fixed 4 test assertions to expect pendingThoughtItem in pendingHistoryItems array. All 102 useGeminiStream tests pass.

Acknowledged (deferred/out-of-scope)

  • Hardcoded MAX_STREAMING_THINKING_VISUAL_LINES (DragonnZhang) — Valid edge case for short terminals. Deferred to follow-up.
  • Daemon mode reasoning invisible (wenshao, chiga0) — Out of scope for this PR. Daemon mode thinking accumulation can be addressed separately.
  • Resume sessions drop thought text (DragonnZhang) — Intentional design decision, documented in PR description.
  • Tab/grapheme edge cases in wrapToVisualLines (DragonnZhang) — Low impact for model-generated text.

"type": "boolean",
"default": false
},
"thinkingDisplayMode": {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] This thinkingDisplayMode setting is added to the schema but no TypeScript code reads it. The corresponding settingsSchema.ts entry was removed in commit 0787c1518 within this PR, and the design doc explicitly states: "The previous ui.thinkingDisplayMode and QWEN_TUI_THINKING_DISPLAY have been removed." Users who discover and configure this setting via IDE autocomplete or settings UI will see no behavioral change.

Remove this entry to match the TS-side cleanup, or regenerate with npm run generate:settings-schema.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in e68f46d — schema regenerated, thinkingDisplayMode removed.

@DragonnZhang DragonnZhang left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Downgraded from Approve to Comment: CI failing (review-pr, delay-automatic-review, Test ubuntu-latest Node 22.x). The test failures are pre-existing and unrelated to this PR.

Re-review (incremental commit e68f46d): The orphaned thinkingDisplayMode entry has been removed from settings.schema.json. The previous critical finding is resolved. Lint CI now passes. No new issues found in this commit.

— qwen3-coder via Qwen Code /review

@DragonnZhang DragonnZhang left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No new review findings beyond what was already discussed. All previously flagged Critical issues (isHiddenInCompact, missing commitPendingThought in finally/Retry, stale thought on retry) have been addressed in the fix commits.

Downgraded from Approve to Comment: CI Test job is failing (install-script.test.js — standalone release packaging test), but the failure is in code not touched by this PR's commits.

The thinking display refactor is well-structured: dedicated pendingThoughtItem state, idempotent commitPendingThought at all transition points (Content, ToolCallRequest, Finished, Cancel, Error, finally), duration tracking, and height-limited streaming display. Test coverage for the new behavior is solid. — claude-sonnet-4-20250514 via Qwen Code /review

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] packages/cli/src/ui/utils/mergeCompactToolGroups.ts:147compactToggleHasVisualEffect still returns true for gemini_thought/gemini_thought_content items, but this PR changed rendering to be identical in both modes (expanded={false} hardcoded). Ctrl+O triggers an expensive refreshStatic() cycle with pixel-identical output when only thought items are present. Consider removing gemini_thought/gemini_thought_content from the check.

— qwen3.7-max via Qwen Code /review

<ThinkMessage
text={itemForDisplay.text}
isPending={isPending}
expanded={false}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] expanded={false} is hardcoded here (and at line 177) for both ThinkMessage and ThinkMessageContent. The design doc specifies expanded={compactMode}, and compactMode is already available via useCompactMode() but never wired through. This makes committed thinking blocks permanently collapsed in production — the expanded rendering code path in ThinkMessage/ThinkMessageContent is dead code.

Either wire expanded={compactMode} or add a comment explaining the intentional deferral.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional deferral — added TODO comment in c45c3d4. Will wire expanded={compactMode} once Ctrl+O is decoupled from compactMode.

// buffered reasoning so the full thought is captured, then commit
// it to history (collapsed) above the answer. After that the
// condition is false, so normal content batching resumes.
setThought((prev) => (prev ? null : prev));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] setThought(null) is called before flushBufferedStreamEvents(). When thought events are still buffered (common during rapid thought→content transitions), the flush re-invokes mergeThought which sets thought back to the incoming value, undoing the clear. The thinking subject persists in the terminal title while the answer streams.

Suggested change
setThought((prev) => (prev ? null : prev));
if (
pendingThoughtItemRef.current ||
bufferedEvents.some((e) => e.kind === 'thought')
) {
flushBufferedStreamEvents();
commitPendingThought(userMessageTimestamp);
thoughtBuffer = '';
}
setThought(null);

Same fix applies to the ToolCallRequest handler around line 1505.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c45c3d4. Moved setThought(null) after flushBufferedStreamEvents() in both Content and ToolCallRequest handlers.

}

if (isPending) {
const innerWidth = Math.max(contentWidth - 2, 20);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] ThinkMessage and ThinkMessageContent both independently compute identical innerWidth, maxLines, and tailVisualLines logic in their isPending branches (~15 lines duplicated between lines 343-354 and 403-414). If the cap or height-fraction divisor changes, both sites must be updated.

Consider extracting a shared helper, e.g. useStreamingThinkLayout(contentWidth, availableTerminalHeight) returning { innerWidth, maxLines }.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid suggestion. The duplication is small (6 lines) and extracting a hook adds indirection for a pattern that may change when Ctrl+O expansion is wired. Deferred.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found. LGTM! ✅ — qwen3.7-max via Qwen Code /review

@wenshao

wenshao commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Runtime verification report (local real-build A/B)

Verified this PR by building two real esbuild bundles — BEFORE = origin/main @ 531a15dd9, AFTER = main + this PR merged (head c45c3d4b1, clean merge) — and driving both in tmux against a local OpenAI-compatible mock that streams throttled reasoning_content deltas (400 ms/chunk) followed by content / tool_calls / bare finish, so every thinking transition was exercised with real wall-clock timing. Not a screenshot review; all observations below are from the live TUI.

What works (verified end-to-end on the merged bundle)

Scenario Observed
Streaming thinking ⠏ Thinking… 2s spinner + live counter, with a height-stable tail window under it. Counter ticked 2s → 4s → 18s across captures; window scrolled Step 4-6Step 43-45 while never exceeding 4 visual lines. No flicker, no growth.
Thinking → answer On first Content event the block collapsed to Thought for 7s and the full 18-line reasoning left scrollback. Duration is accurate (mock thought for 18×400 ms ≈ 7.2 s).
Thinking → tool call Thought for 3s committed above the tool box; after the tool result, a second reasoning burst committed as Thought for 2s before the final answer. Two independent, correctly-timed blocks in one turn.
Thinking-only turn (Finished, no content) Committed as Thought for 2s — not lost.
ESC during thinking Partial reasoning persisted as Thought for 18s (BEFORE discards it entirely — see notes).
No-thinking control Plain content turn produces no thought UI (no spurious Thought for 0s).
Interleaved content→reasoning→content No crash/corruption; mid-answer thought commits collapsed above the (single, merged) answer block.
LoadingIndicator Shows only the witty phrase + timer + cancel; BEFORE showed the thought subject there.
Ctrl+O (compactMode) Collapsed Thought for Xs rows stay visible in compact mode (BEFORE hid thinking entirely). Ctrl+O does not expand them — expanded={false} is hardcoded with the TODO(follow-up), matching the "hint hidden" note in the description.
Tool continuation regression check Tool-result submission, post-tool reasoning and answer all intact on the merged bundle (fresh-session A/B on both bundles).

BEFORE contrast (same prompts, base bundle): reasoning streams unbounded into scrollback with prefix and stays there forever; loading row shows the thought subject; compact mode hides thoughts; cancel discards partial thinking. The PR's core UX claim is real and a clear improvement.

Suites / structural

  • The 6 changed/added test files on the merged tree: 181/181 pass (ConversationMessages 10, LoadingIndicator 21, Composer 19, useGeminiStream 107, DaemonTuiAdapter 14, resumeHistoryUtils 10).
  • Broad regression slice on merged tree (packages/cli src/ui/components + src/ui/hooks + src/ui/utils): 189 files, 3148 pass / 0 fail; plus AppContainer.test.tsx + copyCommand.test.ts: 115 pass.
  • Revert-proof: the PR's two main test files run against base source → both files fail (15+ cases: every new ThinkMessage / commit-transition case, plus fixture-dependent ones), while passing 100% on the merged tree — the tests genuinely pin the new behavior.
  • packages/cli full tsc build on the merged tree: clean (vite/vitest don't typecheck, so this was checked explicitly).
  • New useGeminiStream tests cover the Content / Finished / UserCancelled / Error commit transitions; the ToolCallRequest transition is covered by the runtime run above.
  • No thinkingDisplayMode references remain anywhere (see note 2).

Notes for the maintainer (none blocking, but the PR description needs two corrections)

  1. "Session resume behavior unchanged" is incorrect. On main, the OpenAI channel has useSummarizedThinking() === false, so --continue/--resume restores full reasoning text into scrollback (verified on the base bundle — even a cancelled turn's partial reasoning came back). After this PR, resume restores no thinking at all — durationMs isn't persisted, so not even the collapsed Thought for Xs rows survive (verified on the merged bundle). The code comment in resumeHistoryUtils.ts makes clear this is intentional ("thinking as transient live state"), and it's a defensible design, but the description should say "changed: thoughts no longer restored on resume" rather than "unchanged".
  2. The "Removed: thinkingDisplayMode setting" bullet is a no-op vs main. That setting/utility never existed on main; it was added and removed within this branch's own history. Net effect on main: nothing removed. Worth rewording so reviewers don't go looking for a settings migration.
  3. Daemon-attached TUI loses thinking display entirely. DaemonTuiAdapter now maps agent_thought_chunk → no UI updates (previously rendered as gemini_thought_content rows). Nothing replaces it in daemon-attach mode, since the collapsible block is driven by useGeminiStream, which doesn't run there. If that's the intended interim state, fine — just flagging it's a visible regression for daemon users until a follow-up wires thought chunks into the new UI.
  4. Cancel now leaves an orphan row. ESC during thinking restores the prompt to the input (so no > user line is committed) but now also commits Thought for 18s — a context-free floating row under the previous turn. Cosmetic; arguably better than main's silent discard, but worth knowing it's there.
  5. i18n: 'Thought for' / 'Thinking' aren't added to any locale file, so non-English UIs fall back to English for these labels.
  6. Stale comment: mergeCompactToolGroups.ts still says thinking is "hidden when compactMode is true" — no longer true after this PR.
  7. The screenshots in the description show the icon and a (ctrl+o to expand) hint; the shipped code intentionally has neither (spinner while streaming, plain dim line when committed, hint deferred per the follow-up note). Maybe refresh the screenshot before merge so the demo matches the build.
  8. Repo-policy call: the PR commits .qwen/design/tui-thinking-display-pr2.md and un-ignores that path in .gitignore.

Verdict

Core feature works as advertised under a real streaming provider, transitions are all covered (runtime + unit), no regressions found in 3400+ cli UI tests, typecheck clean. LGTM for merge once the description's resume/thinkingDisplayMode wording is corrected; items 3–8 are reasonable follow-ups rather than blockers.

Verification harness: isolated QWEN_HOMEs, mock provider at OPENAI_BASE_URL streaming reasoning_content; one false alarm during testing (tool continuation appearing broken on the merged bundle) traced to the mock reusing a fixed tool_call id across turns, which triggers main's pre-existing #4176 history-dedup repair — not a PR defect; fresh-session A/B confirms continuation intact.


中文版本(点击展开 / Chinese version)

运行时验证报告(本地真实构建 A/B)

通过构建两个真实 esbuild bundle 验证本 PR —— BEFORE = origin/main @ 531a15dd9,AFTER = main + 本 PR 合并(head c45c3d4b1,干净合并)—— 在 tmux 中驱动两个真实 TUI,对接本地 OpenAI 兼容 mock 服务:以节流方式(400 ms/chunk)流式输出 reasoning_content 增量,随后输出 content / tool_calls / 直接结束,使所有思考转换路径都在真实墙钟时间下得到验证。非截图审查;以下所有观察均来自实际运行的 TUI。

已验证可用(merged bundle 端到端)

场景 观察结果
流式思考 ⠏ Thinking… 2s spinner + 实时计时器,下方为高度稳定的尾随滚动窗口。计时器跨帧 2s → 4s → 18s 递增;窗口从 Step 4-6 滚动到 Step 43-45,始终不超过 4 个视觉行。无闪烁、无增高。
思考 → 回答 首个 Content 事件到达时折叠为 Thought for 7s,18 行完整推理从滚动历史消失。时长精确(mock 思考 18×400 ms ≈ 7.2 s)。
思考 → 工具调用 Thought for 3s 折叠提交于工具框上方;工具结果返回后,第二段推理作为 Thought for 2s 提交,随后是最终回答。同一回合内两个独立、计时正确的思考块。
纯思考回合(Finished,无 content) 提交为 Thought for 2s —— 不丢失。
思考中按 ESC 部分推理保留为 Thought for 18s(BEFORE 完全丢弃 —— 见备注)。
无思考对照 纯 content 回合产生任何思考 UI(无伪 Thought for 0s)。
content→推理→content 交错 无崩溃/破损;答案中途的思考折叠提交于(单个合并的)答案块上方。
LoadingIndicator 仅显示趣味短语 + 计时 + 取消;BEFORE 在此处显示思考主题。
Ctrl+O(compactMode) 折叠的 Thought for Xs 行在紧凑模式下保持可见(BEFORE 整体隐藏思考)。Ctrl+O 不会展开它们 —— expanded={false} 硬编码并带 TODO(follow-up),与描述中“hint 已隐藏”的说明一致。
工具续传回归检查 merged bundle 上工具结果提交、工具后推理与回答全部完好(两个 bundle 各自 fresh-session A/B)。

BEFORE 对照(同样提示词,base bundle):推理以 前缀无限增长地流入滚动历史并永久留存;加载行显示思考主题;紧凑模式整体隐藏思考;取消丢弃部分推理。本 PR 的核心 UX 主张属实,是明确的改进。

测试套件 / 结构检查

  • merged 树上 6 个改动/新增测试文件:181/181 通过ConversationMessages 10、LoadingIndicator 21、Composer 19、useGeminiStream 107、DaemonTuiAdapter 14、resumeHistoryUtils 10)。
  • merged 树宽回归切片(packages/clisrc/ui/components + src/ui/hooks + src/ui/utils):189 个文件,3148 过 / 0 败;另 AppContainer.test.tsx + copyCommand.test.ts:115 过。
  • Revert-proof:将 PR 的两个主测试文件跑在 base 源码上 → 两个文件均失败(15+ 用例:全部新增 ThinkMessage / 提交转换用例,外加依赖共享 fixture 的用例),而在 merged 树上 100% 通过 —— 测试确实钉住了新行为。
  • merged 树 packages/cli 完整 tsc 构建:干净(vite/vitest 不做类型检查,故显式验证)。
  • 新增 useGeminiStream 测试覆盖 Content / Finished / UserCancelled / Error 提交转换;ToolCallRequest 转换由上述运行时验证覆盖。
  • 全仓库无 thinkingDisplayMode 残留引用(见备注 2)。

给维护者的备注(均不阻塞,但 PR 描述需两处更正)

  1. “Session resume 行为不变”不正确。 main 上 OpenAI 通道 useSummarizedThinking() === false,因此 --continue/--resume 会把完整推理文本还原进滚动历史(base bundle 实测 —— 连被取消回合的部分推理都还原了)。本 PR 之后 resume 完全不还原思考 —— durationMs 不持久化,连折叠的 Thought for Xs 行也不保留(merged bundle 实测)。resumeHistoryUtils.ts 的代码注释表明这是有意设计(“thinking 视为瞬态实时状态”),设计上站得住,但描述应改为“已变更:resume 不再还原思考”而非“不变”。
  2. “移除 thinkingDisplayMode 设置”一条相对 main 是 no-op。 该设置/工具文件从未存在于 main;它是在本分支自己的历史中加入又移除的。对 main 的净效果:没有移除任何东西。建议改写措辞,避免 reviewer 去找设置迁移。
  3. daemon-attach 的 TUI 完全失去思考显示。 DaemonTuiAdapter 现在将 agent_thought_chunk 映射为无 UI 更新(之前渲染为 gemini_thought_content 行)。daemon-attach 模式下没有任何替代显示,因为折叠块由 useGeminiStream 驱动而该模式不经过它。若这是有意的过渡状态则可接受 —— 仅提示在后续接通之前,daemon 用户会看到明显退化。
  4. 取消现在会留下孤立行。 思考中 ESC 会把提示词还原回输入框(因此历史中没有 > 用户行),但现在还会提交 Thought for 18s —— 一个挂在上一回合下方、无上下文的浮动行。属外观问题;相比 main 的静默丢弃可以说更好,但值得知晓。
  5. i18n'Thought for' / 'Thinking' 未加入任何 locale 文件,非英文 UI 会回退为英文。
  6. 过时注释:mergeCompactToolGroups.ts 仍写着思考“在 compactMode 下隐藏” —— 本 PR 后不再成立。
  7. 描述中的截图含 图标与 (ctrl+o to expand) 提示;实际代码二者皆无(流式时为 spinner,提交后为暗色单行,提示按 follow-up 推迟)。建议合并前更新截图,使演示与构建一致。
  8. 仓库政策事项:PR 提交了 .qwen/design/tui-thinking-display-pr2.md 并在 .gitignore 中反忽略该路径。

结论

核心功能在真实流式 provider 下符合宣称,所有转换均有覆盖(运行时 + 单测),3400+ 条 cli UI 测试无回归,类型检查干净。LGTM,可合并 —— 前提是更正描述中 resume / thinkingDisplayMode 的表述;第 3–8 项作为合理的后续工作,不构成阻塞。

验证环境:隔离的 QWEN_HOME;mock provider 经 OPENAI_BASE_URL 流式输出 reasoning_content。测试期间出现一次假警报(merged bundle 上工具续传疑似中断),追因为 mock 跨回合复用固定的 tool_call id,触发 main 既有的 #4176 历史去重修复 —— 非本 PR 缺陷;fresh-session A/B 确认续传完好。

wenshao
wenshao previously approved these changes Jun 11, 2026
秦奇 and others added 7 commits June 12, 2026 19:05
Use ink-spinner (dots type) for the thinking header during streaming,
matching the Gemini CLI animated braille dots pattern. Committed
(collapsed/expanded) states keep a static ⠏ icon.

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Drop the ⠏ prefix from collapsed and expanded thinking labels.
Only the streaming state shows the animated spinner.

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Move setThought(null) after flushBufferedStreamEvents() in Content
and ToolCallRequest handlers. Previously the flush re-invoked
mergeThought which undid the clear, leaving a stale thinking subject
in the terminal title during answer streaming.

Also add TODO comment for expanded={false} deferral.

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
The install-script test asserts that internal planning documents are
not whitelisted in .gitignore. Remove the !.qwen/design/ entries and
untrack the design doc.

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Replace animated braille dots spinner with static ✧ prefix for
streaming thinking header.

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Switch from ✧ (hollow) to ✦ (solid four pointed star) for better
visual weight matching with surrounding text.

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Switch to white concave-sided diamond for thinking block prefix.

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@chiga0
chiga0 force-pushed the feat/tui-thinking-display-pr2 branch from 0e783ec to 59452ea Compare June 12, 2026 11:07
@wenshao

wenshao commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Re-verification after today's force-push (head 59452eace)

My previous report below verified pre-rebase head c45c3d4b1. The branch was since rebased onto e07d06972 (2 commits behind today's main) and gained 4 commits, so I re-ran the full verification against current origin/main @ 78f063517: rebuilt both real esbuild bundles (BEFORE = main, AFTER = main + PR, clean merge), drove them in tmux against the same local OpenAI-compatible mock streaming throttled reasoning_content (400 ms/chunk), and re-ran the suites. Bundle freshness was proven with a marker that only exists on today's main (initialIterations from #5000: present ×2 in both new bundles, absent in yesterday's).

What actually changed since the last review (interdiff, rebase noise excluded)

Change Verdict
.gitignore un-ignore + .qwen/design/tui-thinking-display-pr2.md removed from the PR Resolves note 8 of my previous report ✅
Streaming header icon: animated ink-spinnerstatic (U+27E1); ink-spinner import dropped; committed rows stay icon-less Verified at runtime in both directions (see below)
Everything else (useGeminiStream logic, all 6 test files, LoadingIndicator, DaemonTuiAdapter, resume utils, types.ts) Blob-identical or prettier-only vs the verified head — no logic delta

The rebase itself was the riskier part — main's #4595 restructured HistoryItemDisplay/ConversationMessages spacing underneath this PR (isHiddenInCompact no longer exists; the PR now drops the !compactMode && gates instead, same semantics). Hence the full re-run.

Runtime re-run (merged bundle, live TUI)

Scenario Observed
Streaming ⟡ Thinking… 15s33s across two captures; tail window scrolled Step 36–39Step 80–83 while staying exactly 4 visual lines; loading row shows only witty phrase + timer + esc. The glyph is byte-identical across frames (static). A/B against yesterday's pre-icon bundle: braille spinner animates (U+280B) → (U+28A6) — the icon change is real at runtime, not just in source.
Thinking → answer Collapses to Thought for 7s (mock thought 18×400 ms ≈ 7.2 s) — duration exact; reasoning fully leaves scrollback (0 Step N: lines). Long turn: Thought for 48s (120×400 ms) — exact again.
Thinking → tool call Thought for 3s above the tool box, tool runs, post-tool burst commits as Thought for 2s, then the answer. Continuation intact.
ESC during thinking Partial commits as Thought for 10s, prompt restored to input. The context-free orphan row (previous note 4) still occurs — cosmetic, unchanged.
PLAIN control / INTERLEAVE No spurious thought UI on plain turns; mid-answer reasoning commits collapsed above the merged answer block, no corruption.
Ctrl+O (compact) Collapsed Thought for Xs rows stay visible in compact mode and do not expand (expanded={false} + TODO(follow-up) unchanged).
Resume (--continue) Merged bundle restores no thinking (not even collapsed rows). BEFORE bundle on current main restored 105 reasoning lines verbatim, including the cancelled turn's partial. So the description's "Session resume behavior unchanged" still needs the wording fix (note 1).

BEFORE contrast re-confirmed on current main: reasoning streams unbounded into permanent scrollback, loading row shows the thought subject (Analyzing an extremely deep question (11s · esc to cancel)), compact mode hides thinking entirely, ESC discards partials. The PR's UX claim holds against today's main.

Suites / structural at the new head

  • 6 changed/added test files on the merged tree: 181/181 pass.
  • Broad slice (packages/cli src/ui/components + src/ui/hooks + src/ui/utils): 190 files, 3173 pass / 6 skipped / 0 fail.
  • packages/cli full tsc build: clean (vite/vitest don't typecheck, so checked explicitly).
  • Revert-proof: the two main PR test files against base source → 15 tests fail across both files, 100% pass on merged — they still pin the new behavior. (They pin the labels, not the glyph — fine, it's cosmetic.)
  • Merge with current main is conflict-free; no thinkingDisplayMode references anywhere.

Status of my previous notes

# Note Status at 59452eace
1 "Resume unchanged" in description is incorrect ⬜ Still open — description unchanged; runtime gap re-confirmed today
2 "Removed thinkingDisplayMode" is a no-op vs main ⬜ Still open — description still lists it as a removal
3 Daemon-attach TUI loses thinking (agent_thought_chunk[]) ⬜ Still open — author ack'd as out-of-scope follow-up
4 ESC leaves an orphan Thought for Xs row ⬜ Still present (cosmetic)
5 'Thought for' / 'Thinking' missing from all 9 locale files ⬜ Still open (falls back to English)
6 Stale comment mergeCompactToolGroups.ts:118 ("hidden when compactMode is true") ⬜ Still open
7 Description icons/screenshot vs shipped UI 🔶 Code settled on static ; body text + screenshot still show and a (ctrl+o to expand) hint that doesn't exist
8 .qwen/design/ doc + .gitignore whitelist committed ✅ Resolved in 86f8d241f

Verdict

Same conclusion as before, now re-validated on a current-main rebase: feature works end-to-end under a real streaming provider, all transitions covered (runtime + 181 unit tests), no regressions in 3173 cli UI tests, typecheck clean, merge clean. LGTM for merge once the description's resume + thinkingDisplayMode wording is corrected (and ideally the demo screenshot refreshed to the shipped /no-hint UI). Notes 3–6 remain non-blocking follow-ups.

Harness: isolated QWEN_HOMEs, mock provider at OPENAI_BASE_URL streaming reasoning_content with unique per-turn tool_call ids; AFTER = merge 758c7e71d (main 78f063517 + PR 59452eace), BEFORE = main 78f063517.


中文版本(点击展开 / Chinese version)

强推后的重新验证(head 59452eace

我此前的报告验证的是 rebase 前的 head c45c3d4b1。分支随后被 rebase 到 e07d06972(落后今日 main 仅 2 个提交)并新增 4 个提交,因此针对当前 origin/main @ 78f063517 重新做了全量验证:重建两个真实 esbuild bundle(BEFORE = main,AFTER = main + 本 PR,干净合并),在 tmux 中对接本地 OpenAI 兼容 mock(以 400 ms/chunk 节流流式输出 reasoning_content)驱动真实 TUI,并重跑测试套件。bundle 新鲜度用仅存在于今日 main 的标记验证(#5000initialIterations:两个新 bundle 各出现 ×2,昨日 bundle 为 0)。

自上次审查以来的实际变更(interdiff,已剔除 rebase 噪音)

变更 结论
.gitignore 反忽略条目 + .qwen/design/tui-thinking-display-pr2.md 已从 PR 移除 解决我上份报告的备注 8 ✅
流式头部图标:动画 ink-spinner静态 (U+27E1);移除 ink-spinner 导入;已提交的折叠行保持无图标 已在运行时双向验证(见下)
其余全部(useGeminiStream 逻辑、6 个测试文件、LoadingIndicatorDaemonTuiAdapter、resume 工具、types.ts 与已验证 head 逐 blob 相同或仅 prettier 格式差异 — 无逻辑增量

rebase 本身才是风险更大的部分 —— main 的 #4595 在本 PR 之下重构了 HistoryItemDisplay/ConversationMessages 的间距逻辑(isHiddenInCompact 已不存在;PR 现在改为移除 !compactMode && 门控,语义不变)。因此做了全场景重跑。

运行时重跑(merged bundle,真实 TUI)

场景 观察结果
流式思考 两次抓帧 ⟡ Thinking… 15s33s;尾随窗口从 Step 36–39 滚动到 Step 80–83,始终恰好 4 个视觉行;加载行仅显示趣味短语 + 计时 + esc。 字形跨帧字节一致(静态)。与昨日换图标前的 bundle A/B:braille spinner 动画 (U+280B)→ (U+28A6)—— 图标变更在运行时真实生效,而非仅源码层面。
思考 → 回答 折叠为 Thought for 7s(mock 思考 18×400 ms ≈ 7.2 s)—— 时长精确;推理完全离开滚动历史(0 行 Step N:)。长回合:Thought for 48s(120×400 ms)—— 同样精确。
思考 → 工具调用 Thought for 3s 位于工具框上方,工具执行后第二段推理提交为 Thought for 2s,随后是回答。续传完好。
思考中按 ESC 部分推理提交为 Thought for 10s,提示词还原回输入框。无上下文的孤立行(前备注 4)仍存在 —— 外观问题,无变化。
PLAIN 对照 / INTERLEAVE 纯文本回合不产生任何思考 UI;答案中途的推理折叠提交于合并后的答案块上方,无破损。
Ctrl+O(紧凑模式) 折叠的 Thought for Xs 行在紧凑模式下保持可见且不会展开(expanded={false} + TODO(follow-up) 不变)。
Resume(--continue merged bundle 完全不还原思考(连折叠行都没有)。BEFORE bundle 在当前 main 上逐字还原 105 行推理文本,包括被取消回合的部分推理。因此描述中"Session resume behavior unchanged"仍需更正(备注 1)。

BEFORE 对照在当前 main 上复确认:推理无限增长地永久留在滚动历史;加载行显示思考主题(Analyzing an extremely deep question (11s · esc to cancel));紧凑模式整体隐藏思考;ESC 丢弃部分推理。本 PR 的 UX 主张对今日 main 依然成立。

新 head 的测试套件 / 结构检查

  • merged 树上 6 个改动/新增测试文件:181/181 通过
  • 宽回归切片(packages/clisrc/ui/components + src/ui/hooks + src/ui/utils):190 个文件,3173 过 / 6 跳过 / 0 败
  • packages/cli 完整 tsc 构建:干净(vite/vitest 不做类型检查,故显式验证)。
  • Revert-proof:PR 的两个主测试文件跑在 base 源码上 → 两个文件共 15 个用例失败,merged 树 100% 通过 —— 测试依然钉住新行为。(测试钉的是文案而非 字形 —— 可接受,纯外观。)
  • 与当前 main 合并无冲突;全仓库无 thinkingDisplayMode 残留。

上份报告备注的现状

# 备注 59452eace 时的状态
1 描述中"Resume 行为不变"不正确 ⬜ 仍开放 —— 描述未改;今日运行时再次确认差异
2 "移除 thinkingDisplayMode"相对 main 是 no-op ⬜ 仍开放 —— 描述仍将其列为移除项
3 daemon-attach TUI 失去思考显示(agent_thought_chunk[] ⬜ 仍开放 —— 作者已确认为超出范围的后续工作
4 ESC 留下孤立的 Thought for Xs ⬜ 仍存在(外观问题)
5 'Thought for' / 'Thinking' 缺失于全部 9 个 locale 文件 ⬜ 仍开放(回退为英文)
6 mergeCompactToolGroups.ts:118 过时注释("hidden when compactMode is true") ⬜ 仍开放
7 描述图标/截图与实际 UI 不符 🔶 代码已定为静态 ;正文与截图仍显示 及不存在的 (ctrl+o to expand) 提示
8 提交了 .qwen/design/ 文档 + .gitignore 白名单 ✅ 已在 86f8d241f 解决

结论

与此前结论一致,且已在 rebase 到当前 main 后重新验证:功能在真实流式 provider 下端到端可用,全部转换路径有覆盖(运行时 + 181 条单测),3173 条 cli UI 测试无回归,类型检查干净,合并干净。LGTM,可合并 —— 前提是更正描述中 resume / thinkingDisplayMode 的表述(并建议把演示截图更新为实际发布的 /无提示 UI)。备注 3–6 仍为不阻塞的后续工作。

验证环境:隔离的 QWEN_HOME;mock provider 经 OPENAI_BASE_URL 流式输出 reasoning_content,每回合使用唯一 tool_call id;AFTER = 合并 758c7e71d(main 78f063517 + PR 59452eace),BEFORE = main 78f063517

Move setPendingThoughtItem(null) and thoughtStartTimeRef reset outside
the if-guard so stale reasoning is cleared even when the ref is null
but React state hasn't flushed yet (e.g. non-continuation retry
render cycle lag).

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
wenshao
wenshao previously approved these changes Jun 13, 2026
@wenshao

wenshao commented Jun 13, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @chiga0 — thanks for the PR! The collapsible thinking blocks idea looks great.

Unfortunately the PR body doesn't follow our pull request template. A few required sections are missing or renamed:

  • ## What this PR does — you have ## Summary (close, but the exact heading matters for our tooling)
  • ## Why it's needed — not present (motivation is partly in Summary, but deserves its own section)
  • ## Reviewer Test Plan with ### How to verify, ### Evidence (Before & After), and ### Tested on — not present. Your ## Test plan covers some of this but the reviewer-facing format (steps, before/after, OS matrix) helps us land PRs faster
  • ## Risk & Scope — not present
  • ## Linked Issues — not present
  • <details><summary>中文说明</summary> — not present

Could you restructure the PR body to match the template headings? The content you've written is solid — it just needs to land in the right sections. Happy to help if anything's unclear. 🙏

中文说明

PR 内容没有按照 pull request 模板 的格式填写。缺少或改名的部分:

  • ## What this PR does — 你写的是 ## Summary
  • ## Why it's needed — 缺少(动机部分在 Summary 里,但需要单独一节)
  • ## Reviewer Test Plan(含 ### How to verify### Evidence (Before & After)### Tested on)— 缺少
  • ## Risk & Scope — 缺少
  • ## Linked Issues — 缺少
  • <details><summary>中文说明</summary> — 缺少

内容本身写得不错,只是需要按模板的标题重新组织一下。

Qwen Code · qwen3.7-max

// reasoning then commit it to history (collapsed) above the tool
// output.
flushBufferedStreamEvents();
commitPendingThought(userMessageTimestamp);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] No test covers commitPendingThought on the ToolCallRequest path. The test suite now has positive assertions for the Finished, Content, UserCancelled, and Error transitions, but the Thought→ToolCallRequest sequence — the most frequent production path since most tool-using turns start with reasoning — is untested.

The ToolCallRequest handler also differs from the Content handler: it calls commitPendingThought unconditionally (no pendingThoughtItemRef.current guard), which is correct (the function is null-safe) but this distinct code path should have its own test to guard against future refactors that might add a guard or reorder the calls.

it('should commit thought to history when ToolCallRequest arrives', async () => {
  mockSendMessageStream.mockReturnValue(
    (async function* () => {
      yield { type: ServerGeminiEventType.Thought, value: { subject: '', description: 'planning tool usage' } };
      yield { type: ServerGeminiEventType.ToolCallRequest, value: { id: 'tc1', name: 'read_file', args: { path: '/foo' } } };
      yield { type: ServerGeminiEventType.Finished, value: { reason: 'STOP', usageMetadata: undefined } };
    })(),
  );
  // ... assert mockAddItem was called with gemini_thought before the tool call
});

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7d591b9. Added test should commit thought to history when ToolCallRequest arrives — verifies Thought→ToolCallRequest sequence commits reasoning to history with durationMs.

@@ -1599,8 +1605,10 @@ export const useGeminiStream = (
if (pendingHistoryItemRef.current) {
setPendingHistoryItem(null);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] No test covers commitPendingThought on the non-continuation Retry path. The existing retry tests focus on countdown timers and error retry flows, but none verify that a pending thought is committed (or discarded) when a non-continuation retry escalation occurs.

This path is unique: it commits the thought AND clears thoughtBuffer, setThought, and geminiMessageBuffer simultaneously. A test should verify that reasoning accumulated before the retry escalation is properly committed to history (not silently lost) and that the thought state is fully reset.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7d591b9. Added test should commit thought to history on non-continuation Retry — uses fake timers to flush the thought buffer before the Retry event, verifying that already-flushed reasoning is committed (not discarded) by commitPendingThought.

…ry paths

Add two positive tests covering thought-to-history commitment at the
ToolCallRequest and non-continuation Retry transitions — the two paths
that were previously untested.

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No new review findings at this commit. Architecture is sound — pendingThoughtItem/pendingHistoryItem separation is clean, commitPendingThought covers all transition paths, and useStateAndRef ensures synchronous ref updates. tsc 0, eslint 0, 199 tests pass, CI 30/30 all pass.

Note: The previously reported isHiddenInCompactMode issue in mergeCompactToolGroups.ts (flagged by @DragonnZhang) remains unresolved at HEAD. This PR removes the !compactMode guard from HistoryItemDisplay.tsx, so thought items now render in compact mode, but the merging logic still treats them as hidden — potentially dropping thought lines between adjacent tool groups in compact mode.

— qwen3.7-max via Qwen Code /review

@DragonnZhang DragonnZhang left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incremental review at 7d591b97: two new commits since last review — fix(tui): unconditionally clear thought state in commitPendingThought correctly moves setPendingThoughtItem(null) and thoughtStartTimeRef.current = null outside the conditional block, preventing stale thought state when pendingThoughtItemRef.current is null (e.g., ToolCallRequest and Retry paths). 115 lines of new tests cover these edge cases. CI green (15/15 checks pass). LGTM ✅ — claude-opus-4-6 via Qwen Code /review

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] ChatCompressed handler (line 1544) calls flushBufferedStreamEvents() and handleChatCompressionEvent() but does not call commitPendingThought. If ChatCompressed arrives mid-stream while thinking is active (between Thought events and the first Content event), the pending thought remains uncommitted. The compression handler commits pendingHistoryItem to history, but the thought is committed later when Content/Finished arrives — resulting in a history ordering inversion (compression summary appears before the thought block it triggered).

The HookSystemMessage handler at line ~1633 has the same omission. A one-line commitPendingThought(userMessageTimestamp) before the existing handler call in each case would fix the ordering.

— qwen3.7-max via Qwen Code /review

}: {
currentLoadingPhrase?: string;
}) => (
<Text>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] Prettier formatting violation — npx prettier --check flags this file. The <Text> element at this line uses 6-space indentation instead of 4. This will fail the npm run format CI step.

Suggested change
<Text>
<Text>
LoadingIndicator
{currentLoadingPhrase ? `: ${currentLoadingPhrase}` : ''}
</Text>
),

— qwen3.7-max via Qwen Code /review

const charBudget = maxLines * width * 2;
let sliceStart = Math.max(0, text.length - charBudget);
if (sliceStart > 0) {
const nl = text.indexOf('\n', sliceStart);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] tailVisualLines aligns sliceStart to the next newline after the character-budget cutoff. When a single logical line straddles the cutoff (e.g., a long code snippet, URL, or JSON blob in model reasoning), indexOf('\n', sliceStart) advances past the entire line, silently dropping it from the streaming preview.

Concrete example: text = "short\n" + "x".repeat(2000) + "\nshort_tail" with width=80, maxLines=4charBudget=640, sliceStart falls inside the long line, indexOf jumps to the next newline, and the 2000-character line is entirely absent from the output.

Consider aligning to the previous newline instead, so the long line stays in the budget and wrapToVisualLines handles wrapping:

Suggested change
const nl = text.indexOf('\n', sliceStart);
if (sliceStart > 0) {
const nl = text.lastIndexOf('\n', sliceStart);
if (nl !== -1) {
sliceStart = nl + 1;
}
}

— qwen3.7-max via Qwen Code /review

@@ -1657,6 +1665,7 @@ export const useGeminiStream = (
}
} finally {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] commitPendingThought is called here in the finally block (good — covers all exit paths), but setThought(null) is not called alongside it. Every other call site (Content, ToolCallRequest, Finished, Retry, Error handlers) independently follows up with setThought(null). If the stream throws a raw exception (not a structured Error event), the finally block runs commitPendingThought but the thought state (used for window title) remains non-null, showing stale thinking metadata.

Adding setThought(null) here would close this gap and make the finally block a complete cleanup path:

Suggested change
} finally {
commitPendingThought(userMessageTimestamp);
setThought(null);
discardBufferedStreamEvents();

— qwen3.7-max via Qwen Code /review

@wenshao

wenshao commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

✅ Verification report — PR #4598 (collapsible thinking blocks + duration timer)

Verdict: the headline feature works end-to-end and is a genuine UX win — merge-worthy for the in-process TUI. Verified by building the real CLI and driving the live TUI in tmux against a deterministic mock that streams reasoning_content. Below are the empirical results plus four reverse-audit notes (3 description-vs-code mismatches + 1 formatting nit) the maintainer should weigh before merge.

Test environment

Isolated git worktree at the PR head (7d591b972), deps hardlinked from main. Built @qwen-code/qwen-code-core + @qwen-code/qwen-code and ran the real TUI: node packages/cli --auth-type openai --openai-base-url <mock> -m mock-model --approval-mode yolo. A zero-dep mock streams 14 reasoning_content lines (with delays) then an answer, so each state is observable. (Live models couldn't be used — glm-4.7 and the BAILIAN coding-plan models both return 401 token expired this session — which is exactly why a deterministic mock is the right tool here.)

1. Unit tests — all pass ✔️

All 6 changed test files: 183 tests pass (useGeminiStream 109, LoadingIndicator 21, Composer 19, DaemonTuiAdapter 14, ConversationMessages 10, resumeHistoryUtils 10). ESLint clean on all 14 files. thinkingDisplayMode setting/utility removed with no orphaned references.

2. Live TUI — streaming window with ticking timer + 4-line scrolling tail ✔️

Captured at three points during one thinking phase (mock at 1.5 s/line):

⟡ Thinking… 9s                                        ⟡ Thinking… 12s                  ⟡ Thinking… 17s
  STEP 04: reasoning detail number 4 …       →          STEP 06: …            →          STEP 09: …
  STEP 05: reasoning detail number 5 …                  STEP 07: …                       STEP 10: …
  STEP 06: …                                            STEP 08: …                       STEP 11: …
  STEP 07: …                                            STEP 09: …                       STEP 12: …
  • Live timer ticks 9s → 12s → 17s.
  • Fixed 4-line height; the window tail-scrolls (04-07 → 06-09 → 09-12) — older reasoning falls off the top, height never grows. No flicker.

3. Collapse on completion + accurate duration ✔️

On the thinking→answer transition the block collapses to a dim-italic, past-tense one-liner (no icon), and the duration matches wall-clock exactly:

Thought for 21s                          ← 14 lines × 1.5s ≈ 21s ✓  (a separate run gave "Thought for 12s" for 14×0.85s ✓)
✦ FINAL_ANSWER: 17 times 23 equals 391.

Committed thoughts persist collapsed across turns in scrollback.

4. LoadingIndicator — thought preview removed ✔️

Mid-stream the spinner row shows only phrase + timer + cancel, no thought-subject duplication:

⟡ Thinking… 6s
  STEP 02 … STEP 05 …                                  ← the 4-line window
.   Resolving dependencies... and existential crises... (6s · esc to cancel)

5. Cancel-mid-thinking commits the partial thought ✔️

Pressing Esc at ~T+4.5 s committed Thought for 4s (the elapsed duration, collapsed) — the commitPendingThought path on cancel works and the duration is the partial, not the full stream.


⚠️ Reverse-audit findings (for the merge decision)

1. Ctrl+O / expanded state is NOT wired — committed reasoning is unreadable in the UI.
The description lists "Committed expanded (Ctrl+O toggle): full reasoning rendered as dimmed markdown" and "pass expanded={compactMode}". The merged code does not do this: HistoryItemDisplay.tsx hardcodes expanded={false} (with a TODO(follow-up)). I confirmed at runtime — pressing Ctrl+O does not expand a committed thought; it stays Thought for Ns, the reasoning text never reappears. So the only place full reasoning is ever shown is the transient 4-line streaming window; once collapsed it is gone from the UI. The expanded renderer is effectively dead code (exercised only by unit tests passing expanded={true} directly). This is acknowledged in the PR's Follow-up section, but the feature list above it overstates it as working.

2. Daemon / ACP-attached TUI loses the thinking display entirely (undocumented behavior change).
DaemonTuiAdapter.ts now returns [] for agent_thought_chunk (was: emit a gemini_thought_content history item); its test was renamed "…while suppressing thought history". The new streaming logic lives only in useGeminiStream (the in-process path), so a TUI attached to a qwen serve daemon — web-shell, Zed/IDE — now shows no thinking at all, where it previously showed it. Deliberate and tested, but not mentioned in the description. Please confirm this regression is intended (or scoped to a follow-up).

3. "Session resume behavior unchanged" is inaccurate for one config.
resumeHistoryUtils.ts changed the guard from (!config || !useSummarizedThinking()) to !config. Previously, with a config present and useSummarizedThinking() === false, resumed thoughts were re-shown; now they're always dropped in the interactive TUI. The new behavior is arguably more consistent (thinking is transient), but it is a change, not a no-op as the Impact analysis claims.

4. Minor: Composer.test.tsx is not Prettier-clean.
The new LoadingIndicator mock JSX is over-indented by 2 spaces; prettier --check fails on the PR file but passes on origin/main. Not a hard CI blocker (CI's lint.js --prettier runs prettier --write . with no diff-gate), but a quick prettier --write would keep the diff tidy.

Recommendation

The implemented streaming + collapsed thinking feature is correct, well-tested, and a real improvement — safe to merge for the default in-process TUI. Before merge I'd suggest: (a) align the description with reality on Ctrl+O/expanded (it's a follow-up, not a shipped toggle); (b) explicitly confirm the daemon-mode thinking suppression (#2) is intended — that's the one genuine UX regression; (c) optionally fix the resume claim (#3) and run prettier --write (#4). None of these are correctness bugs in the in-process path.


🇨🇳 中文版验证报告(点击展开)

✅ PR #4598 验证报告(可折叠思考块 + 时长计时)

结论:核心功能端到端可用,是实打实的体验提升 —— 就进程内 TUI 而言可以合并。 我构建了真实 CLI,并在 tmux 中用一个确定性 mock(流式输出 reasoning_content)驱动真实 TUI 完成验证。下面是实测结果,以及 4 条 reverse-audit(3 处「描述与代码不符」+ 1 处格式问题),供合并前参考。

测试环境

在 PR HEAD(7d591b972)建独立 git worktree,依赖从 main 硬链接。构建 core + cli 后运行真实 TUI:node packages/cli --auth-type openai --openai-base-url <mock> -m mock-model --approval-mode yolo。零依赖 mock 先慢速流式输出 14 行 reasoning_content 再给答案,使每个状态都可被捕获。(本会话 glm-4.7 与 BAILIAN coding-plan 模型均返回 401 token 过期,无法用真实模型 —— 这也正说明确定性 mock 是这里的正确手段。)

1. 单元测试 —— 全部通过 ✔️

6 个改动测试文件共 183 个用例通过(useGeminiStream 109、LoadingIndicator 21、Composer 19、DaemonTuiAdapter 14、ConversationMessages 10、resumeHistoryUtils 10)。14 个文件 ESLint 干净。thinkingDisplayMode 设置/工具已删除且无残留引用

2. 真实 TUI —— 流式窗口 + 实时计时 + 4 行滚动尾窗 ✔️

单次思考过程中三个时间点的捕获(mock 每行 1.5s):

⟡ Thinking… 9s          →     ⟡ Thinking… 12s        →     ⟡ Thinking… 17s
  STEP 04 …                     STEP 06 …                    STEP 09 …
  STEP 05 …                     STEP 07 …                    STEP 10 …
  STEP 06 …                     STEP 08 …                    STEP 11 …
  STEP 07 …                     STEP 09 …                    STEP 12 …
  • 计时实时跳动 9s → 12s → 17s。
  • 固定 4 行高,窗口尾部滚动(04-07 → 06-09 → 09-12),旧内容滚出顶部,高度不增长,无闪烁。

3. 完成时折叠 + 时长准确 ✔️

思考→答案切换时折叠为暗体斜体的过去式单行(无图标),时长与实际墙钟一致:

Thought for 21s                          ← 14 行 × 1.5s ≈ 21s ✓(另一次 14×0.85s 得到 "Thought for 12s" ✓)
✦ FINAL_ANSWER: 17 times 23 equals 391.

已提交的思考块在历史中跨轮次保持折叠

4. LoadingIndicator —— 移除思考预览 ✔️

流式中状态行仅显示 文案 + 计时 + 取消提示,不再重复思考主题:

.   Resolving dependencies... and existential crises... (6s · esc to cancel)

5. 思考中途取消会提交部分思考 ✔️

在 ~T+4.5s 按 Esc,提交为 Thought for 4s实际经过时长,折叠)—— 取消路径的 commitPendingThought 正常,时长是部分而非整段。


⚠️ Reverse-audit 发现(合并决策参考)

1. Ctrl+O / 展开态未接线 —— 已提交的推理在 UI 中无法再查看。
描述写有「已提交展开(Ctrl+O 切换):完整推理以暗色 markdown 渲染」「expanded={compactMode}」。但合并代码并非如此:HistoryItemDisplay.tsx 硬编码 expanded={false}(带 TODO(follow-up))。我已在运行时确认 —— 按 Ctrl+O 不会展开已提交思考,它始终是 Thought for Ns,推理文本不再出现。因此完整推理只在那个临时的 4 行流式窗口出现过,折叠后即从 UI 消失。展开渲染分支实为死代码(仅被直接传 expanded={true} 的单测覆盖)。PR 的 Follow-up 段落已承认这点,但上方功能列表把它说成了已可用。

2. Daemon / ACP 接入的 TUI 完全失去思考显示(未在描述中说明的行为变更)。
DaemonTuiAdapter.ts 现在对 agent_thought_chunk 返回 [](原先会产出 gemini_thought_content 历史项),其测试被改名为「…suppressing thought history」。新的流式逻辑只在 useGeminiStream(进程内路径),所以接入 qwen serve daemon 的 TUI —— web-shell、Zed/IDE —— 现在完全不显示思考,而此前是显示的。该改动有意且有测试,但描述中未提及。请确认这一回退是否为预期(或留作后续)。

3.「会话恢复行为不变」并不准确(针对某一配置)。
resumeHistoryUtils.ts 把判断从 (!config || !useSummarizedThinking()) 改为 !config。此前在有 config 且 useSummarizedThinking() === false 时,恢复会重新显示思考;现在交互式 TUI 一律丢弃。新行为可能更一致(思考本就是临时的),但这是变更,并非 Impact analysis 所说的无变化。

4. 次要:Composer.test.tsx 未通过 Prettier。
新增的 LoadingIndicator mock JSX 多缩进了 2 个空格;prettier --check 在 PR 文件失败,但在 origin/main 通过。并非硬性 CI 阻断(CI 的 lint.js --prettier 跑的是 prettier --write .,无 diff 校验),但 prettier --write 一下能让 diff 更整洁。

建议

所实现的「流式 + 折叠」思考功能正确、测试充分、确为改进,默认进程内 TUI 可以合并。合并前建议:(a) 将描述与现实对齐(Ctrl+O/展开是后续项,并非已发布开关);(b) 明确确认 daemon 模式思考被抑制(#2)是否预期 —— 这是唯一真正的体验回退;(c) 可选地修正恢复行为表述(#3)并 prettier --write#4)。这些都不是进程内路径的正确性 bug。

Verified on an isolated worktree at the PR head · vitest 3.2.4 · real TUI in tmux driven by a deterministic reasoning-streaming mock.

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

Labels

type/feature-request New feature or enhancement request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants