Skip to content

feat(tui): improve reasoning and agent activity rendering#231

Merged
elkaix merged 17 commits into
mainfrom
feat/tui-reasoning-subagent-activity
Jul 22, 2026
Merged

feat(tui): improve reasoning and agent activity rendering#231
elkaix merged 17 commits into
mainfrom
feat/tui-reasoning-subagent-activity

Conversation

@elkaix

@elkaix elkaix commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Related Issue

None. This branch was developed without a linked issue.

Description

Summary

  • Preserve ordered reasoning-summary boundaries and encrypted reasoning metadata across streaming and non-streaming OpenAI Responses paths.
  • Render single-agent and parallel subagent progress as a compact, payload-free activity tree.
  • Stabilize live terminal handoff and resize behavior so completed activity does not leave duplicate rows.

User impact

Reasoning summaries no longer expose internal Markdown delimiters or lose replay metadata, while agent progress remains readable at narrow and normal terminal widths without leaking task payloads.

Risk

  • Medium: touches provider response assembly, wire metadata, and live TUI rendering.
  • Failure handling preserves deterministic IDs, explicit boundaries, bounded nesting, and existing terminal capability fallbacks.
  • No new dependencies, telemetry, hosted services, or config keys.

Verification

  • make check-pythinker-code
  • make test-pythinker-code: 8,428 passed, 7 skipped, 1 xfailed; tests_e2e: 65 passed, 4 skipped.
  • make check-pythinker-core
  • make test-pythinker-core: 446 passed.
  • Repository pre-push hooks passed the CI-equivalent check and test gates.

Rollback and review

  • Roll back the commits in this PR as one feature series.
  • Review provider reasoning event correlation and interactive scrollback handoff as the highest-risk boundaries.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked the related issue, if any. No related issue exists.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have run make gen-changelog to update the changelog. Not applicable: CHANGELOG.md was updated directly under ## Unreleased per repository instructions.
  • I have run make gen-docs to update the user documentation. Not applicable: no generated documentation source changed.

Summary by CodeRabbit

  • New Features

    • Redesigned agent progress into a compact activity tree with clearer statuses, width-aware truncation, improved per-agent naming, and better “Agents” header/expandable payload behavior.
    • Improved “thinking”/reasoning display with grouped, consistently ordered summaries and smooth streaming boundaries.
  • Bug Fixes

    • Prevented Markdown delimiter leakage and duplicate terminal rows after refocus; stabilized resize prompt redraw behavior (including Windows-specific handling).
    • Kept encrypted reasoning available when summaries are missing, fixed reasoning summary ordering/indexing across stream/non-stream, and prevented sensitive/raw subagent payloads from leaking in live/collapsed views.
    • Updated subagent event serialization to include an optional description and improved working-indicator escape hint rendering.

elkaix added 12 commits July 21, 2026 19:01
Route single `Agent` tool calls through the same payload-free semantic
activity tree previously used only for `RunAgents`, so a lone subagent
renders per-agent status rows (waiting/running/thinking + reading…/
searching…/running command…) instead of raw nested tool calls, args, or
streamed output. Derive the running description from the tool `description`
argument, suppress stale pre-result activity once the parent returns, and
render the completed result via the dedicated result renderer.

Update the block, nested-lifecycle, integration, and card tests to the new
semantic contract (raw payloads never leak; first-result-wins dedup and
ancestry roll-up preserved), and fix the stale `RunAgents(`->`Agents(`
label assertion in the action-spacer test.
Address CodeRabbit review findings on the branch:

- core: a non-streaming OpenAI Responses reasoning item can return
  encrypted_content with an empty summary; the conversion loop dropped it
  entirely, diverging from the streaming output_item.done path and making
  the reasoning boundary non-replayable. Emit an encrypted ThinkPart in
  that case, mirroring the streaming behavior, with focused coverage.
- tui: render "1 agent" (singular) instead of "1 agents" in the grouped
  agents summary, and make the uniform-status check explicitly boolean.

The two other flagged reasoning findings (summary-index preservation and
cross-output merge collisions) were verified against the code and tests
and are non-issues: real deltas always carry valid indices, and encrypted
boundaries already block adjacent cross-output merges.
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6dcbdae8-e7d8-4df1-96c2-b8ae05823280

📥 Commits

Reviewing files that changed from the base of the PR and between a8a0b25 and e4613b9.

📒 Files selected for processing (7)
  • src/pythinker_code/ui/shell/visualize/_blocks.py
  • src/pythinker_code/ui/shell/visualize/_live_view.py
  • tests/e2e/test_shell_pty_e2e.py
  • tests/ui_and_conv/test_live_content_parts.py
  • tests/ui_and_conv/test_live_view_notifications.py
  • tests/ui_and_conv/test_subagent_live_stream.py
  • tests/ui_and_conv/test_tool_call_block.py

📝 Walkthrough

Walkthrough

The change adds indexed OpenAI Responses reasoning boundaries, semantic payload-free Agent and RunAgents activity rendering, subagent description and ownership propagation, and platform-specific terminal resize recovery with continuous PTY coverage.

Changes

Reasoning stream boundaries

Layer / File(s) Summary
Reasoning contracts and rendering
packages/pythinker-core/..., src/pythinker_code/ui/shell/visualize/...
Reasoning summaries retain normalized indices, encrypted completion boundaries, separate merge behavior, cleaned Markdown, and styled segmented rendering.
Reasoning protocol and rendering tests
packages/pythinker-core/tests/*, tests/ui_and_conv/test_live_content_parts.py, tests/ui_and_conv/test_streaming_content_block.py, CHANGELOG.md
Tests cover serialization, assembly, provider event ordering, encrypted fallbacks, Markdown cleanup, and thinking styles.

Subagent activity tree

Layer / File(s) Summary
Subagent metadata and event ownership
src/pythinker_code/subagents/runner.py, src/pythinker_code/wire/types.py, src/pythinker_code/ui/shell/visualize/_live_view.py, tests/core/test_wire_message.py
Descriptions propagate through SubagentEvent, while nested tool events are attributed to owning agents and cleared with live-view state.
Semantic activity state and layout
src/pythinker_code/ui/shell/visualize/_blocks.py, src/pythinker_code/ui/shell/tool_renderers/agent.py
Agent activity uses compact status rows, width-aware truncation, sanitized descriptions, expandable results, and payload-free nested activity.
Activity tree regression coverage
tests/ui_and_conv/*
Tests cover activity transitions, ordering, overflow, motion modes, renderer fallbacks, payload suppression, and completed-agent rendering.

Resize recovery behavior

Layer / File(s) Summary
Platform-specific resize recovery
src/pythinker_code/ui/shell/visualize/_interactive.py, src/pythinker_code/ui/shell/visualize/_live_view.py
Prompt renderer resets during resize are restricted to Windows, and cancellable turns expose an interrupt hint.
Continuous PTY and repaint validation
tests/e2e/*, tests/ui_and_conv/test_visualize_running_prompt.py, tests/ui_and_conv/test_live_view_notifications.py
Tests continuously replay PTY output, validate fossil-free layouts and platform-specific repaint behavior, synchronize cancellation after process startup, and verify the interrupt hint.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: enhancement

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.81% which is insufficient. The required threshold is 70.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title follows conventional commits and clearly summarizes the main change set.
Description check ✅ Passed The description matches the template with issue, summary, risk, verification, rollback, and checklist sections filled in.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tui-reasoning-subagent-activity

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Comment @coderabbitai help to get the list of available commands.

@elkaix elkaix changed the title Improve reasoning and subagent activity rendering feat(tui): improve reasoning and agent activity rendering Jul 22, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pythinker_code/ui/shell/visualize/_blocks.py (1)

1490-1524: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add -> None return annotations to these public methods.

append_sub_tool_call (Line 1490), append_sub_tool_call_part (Line 1508), and finish_sub_tool_call (Line 1524) return None but omit the annotation (Ruff ANN202 flags the same).

Proposed fix
-    def append_sub_tool_call(self, tool_call: ToolCall, *, agent_id: str | None = None):
+    def append_sub_tool_call(self, tool_call: ToolCall, *, agent_id: str | None = None) -> None:
     def append_sub_tool_call_part(
         self, tool_call_part: ToolCallPart, *, agent_id: str | None = None
-    ):
+    ) -> None:
-    def finish_sub_tool_call(self, tool_result: ToolResult, *, agent_id: str | None = None):
+    def finish_sub_tool_call(self, tool_result: ToolResult, *, agent_id: str | None = None) -> None:

As per path instructions: "Flag missing type annotations on public functions and methods."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pythinker_code/ui/shell/visualize/_blocks.py` around lines 1490 - 1524,
Add explicit -> None return annotations to the public methods
append_sub_tool_call, append_sub_tool_call_part, and finish_sub_tool_call,
preserving their existing behavior and parameters.

Sources: Path instructions, Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/pythinker_code/ui/shell/visualize/_blocks.py`:
- Around line 1490-1524: Add explicit -> None return annotations to the public
methods append_sub_tool_call, append_sub_tool_call_part, and
finish_sub_tool_call, preserving their existing behavior and parameters.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 70866f53-9b78-42a4-a448-f993b8cd6ed0

📥 Commits

Reviewing files that changed from the base of the PR and between f84091b and d1f1e57.

📒 Files selected for processing (24)
  • CHANGELOG.md
  • packages/pythinker-core/src/pythinker_core/contrib/chat_provider/openai_responses.py
  • packages/pythinker-core/src/pythinker_core/message.py
  • packages/pythinker-core/tests/test_message.py
  • packages/pythinker-core/tests/test_stream_message_assembler.py
  • packages/pythinker-core/tests/test_stream_tool_call_metadata.py
  • src/pythinker_code/subagents/runner.py
  • src/pythinker_code/ui/shell/tool_renderers/agent.py
  • src/pythinker_code/ui/shell/visualize/_blocks.py
  • src/pythinker_code/ui/shell/visualize/_interactive.py
  • src/pythinker_code/ui/shell/visualize/_live_view.py
  • src/pythinker_code/wire/types.py
  • tests/core/test_wire_message.py
  • tests/e2e/test_shell_pty_prompt_layout_e2e.py
  • tests/ui_and_conv/test_empty_think_part_indicator.py
  • tests/ui_and_conv/test_live_content_parts.py
  • tests/ui_and_conv/test_nested_subagent_events.py
  • tests/ui_and_conv/test_render_matrix.py
  • tests/ui_and_conv/test_streaming_content_block.py
  • tests/ui_and_conv/test_subagent_live_stream.py
  • tests/ui_and_conv/test_tool_call_block.py
  • tests/ui_and_conv/test_tui_blocks_integration.py
  • tests/ui_and_conv/test_tui_card_tool_renderers.py
  • tests/ui_and_conv/test_visualize_running_prompt.py

@elkaix

elkaix commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@elkaix

elkaix commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pythinker_code/ui/shell/visualize/_blocks.py (1)

1830-1836: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep provisional background agents in the live activity path.

finish() sets _is_background_pending for launch results, but this branch treats every non-None result as terminal and removes the activity rows. The legacy _compose() then renders the successful launch as COMPLETED, so the spinner and agent progress disappear until the terminal update.

Proposed lifecycle fix
-        if self._result is not None:
+        if self._result is not None and not self._is_background_pending:
             return []

-        if self._result is None:
+        if self._result is None or self._is_background_pending:
             streamed_output = self._streamed_output_text()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pythinker_code/ui/shell/visualize/_blocks.py` around lines 1830 - 1836,
Update the ownership branch in the activity-rendering method to distinguish
provisional background launches from terminal results: when
_is_background_pending is set, continue returning _agent_activity_children so
live progress remains visible. Only suppress activity rows for non-pending
results, and ensure the legacy _compose() path does not render pending launches
as COMPLETED.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/pythinker_code/ui/shell/visualize/_blocks.py`:
- Around line 1830-1836: Update the ownership branch in the activity-rendering
method to distinguish provisional background launches from terminal results:
when _is_background_pending is set, continue returning _agent_activity_children
so live progress remains visible. Only suppress activity rows for non-pending
results, and ensure the legacy _compose() path does not render pending launches
as COMPLETED.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e57fc246-5a82-4e50-b01b-715a04466e12

📥 Commits

Reviewing files that changed from the base of the PR and between d1f1e57 and b69b3a8.

📒 Files selected for processing (1)
  • src/pythinker_code/ui/shell/visualize/_blocks.py

@elkaix

elkaix commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pythinker_code/ui/shell/visualize/_blocks.py`:
- Around line 2069-2077: Update the card activity-building logic around
_subagent_activity_children to remove the outer condition requiring _result to
be unset, allowing the helper to run for background-pending cards. Preserve the
existing arguments and rely on _subagent_activity_children to distinguish
terminal results from background-pending activity.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a01c85ac-3758-4d71-9f5d-b152aad18971

📥 Commits

Reviewing files that changed from the base of the PR and between b69b3a8 and 866a057.

📒 Files selected for processing (3)
  • src/pythinker_code/ui/shell/visualize/_blocks.py
  • tests/e2e/test_shell_pty_e2e.py
  • tests/ui_and_conv/test_tool_call_block.py

Comment thread src/pythinker_code/ui/shell/visualize/_blocks.py Outdated
@elkaix

elkaix commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@elkaix

elkaix commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pythinker_code/ui/shell/visualize/_blocks.py (1)

765-769: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve encrypted thinking boundaries in the UI accumulator.

ThinkPart.merge_in_place() treats encrypted as a merge boundary, but _append_thinking_segment() only compares summary_index. An encrypted-only part can still be merged into a later segment with the same index, losing that boundary in the UI. Thread the encrypted/boundary state through append() and _append_thinking_segment(), and add a regression test for the encrypted-only case.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pythinker_code/ui/shell/visualize/_blocks.py` around lines 765 - 769,
Update the UI accumulator’s append and _append_thinking_segment flow to accept
and propagate the encrypted boundary state, and make segment merging respect
encrypted boundaries in addition to summary_index. Ensure encrypted-only parts
remain separate from later segments with the same index, and add a regression
test covering that case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/e2e/test_shell_pty_e2e.py`:
- Around line 930-931: Update the cancellation test around the command
constructing cancel_output.txt and the verification near line 976 so it cannot
pass before the 30-second sleep completes. Either assert that the entire process
group terminates directly, or delay the sentinel-file check until after the
command could have reached the write while preserving the expected cancellation
behavior.
- Around line 956-963: Replace the fixed stabilization deadline after the
submitted prompt with a bounded wait for an observable running-turn/Escape-ready
signal. Use the existing shell or test state exposed by the surrounding PTY flow
to confirm prompt_toolkit has installed the running-turn delegate before sending
Escape, while retaining a timeout and diagnostic failure if readiness is not
observed.

In `@tests/ui_and_conv/test_subagent_live_stream.py`:
- Around line 680-681: Add an assertion in the live-stream test alongside the
existing output checks to verify that both background task IDs, including
agent-beta-raw-id without a nested event, are suppressed from output. Keep the
existing assertions and cover the absence of each raw ID.

In `@tests/ui_and_conv/test_tool_call_block.py`:
- Around line 526-553: Strengthen
test_run_agents_background_launch_keeps_live_agent_activity_in_card_style by
asserting that the composed card-mode output does not contain the terminal “Run
Agents completed” row, while retaining the existing assertion for live waiting
activity. Ensure the test covers suppression of completion output for background
launches rather than only verifying that activity is present.

---

Outside diff comments:
In `@src/pythinker_code/ui/shell/visualize/_blocks.py`:
- Around line 765-769: Update the UI accumulator’s append and
_append_thinking_segment flow to accept and propagate the encrypted boundary
state, and make segment merging respect encrypted boundaries in addition to
summary_index. Ensure encrypted-only parts remain separate from later segments
with the same index, and add a regression test covering that case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: dc355538-2103-40b8-be2a-719ff51cbb8f

📥 Commits

Reviewing files that changed from the base of the PR and between 866a057 and a8a0b25.

📒 Files selected for processing (4)
  • src/pythinker_code/ui/shell/visualize/_blocks.py
  • tests/e2e/test_shell_pty_e2e.py
  • tests/ui_and_conv/test_subagent_live_stream.py
  • tests/ui_and_conv/test_tool_call_block.py

Comment thread tests/e2e/test_shell_pty_e2e.py Outdated
Comment thread tests/e2e/test_shell_pty_e2e.py Outdated
Comment thread tests/ui_and_conv/test_subagent_live_stream.py
Comment thread tests/ui_and_conv/test_tool_call_block.py Outdated
@elkaix

elkaix commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@elkaix
elkaix merged commit 780c276 into main Jul 22, 2026
33 checks passed
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@codecov

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant