Skip to content

fix(tui): keep MoA reference blocks visible when the thinking section is hidden - #64701

Closed
wesleysimplicio wants to merge 3 commits into
NousResearch:mainfrom
wesleysimplicio:fix/tui-moa-reference-visibility
Closed

fix(tui): keep MoA reference blocks visible when the thinking section is hidden#64701
wesleysimplicio wants to merge 3 commits into
NousResearch:mainfrom
wesleysimplicio:fix/tui-moa-reference-visibility

Conversation

@wesleysimplicio

Copy link
Copy Markdown
Contributor

What does this PR do?

The Ink TUI shows Mixture-of-Agents (MoA) reference-model output as labelled blocks in the
"Thinking" trail (introduced in #53855: each reference model's output is shown before the
aggregator's final answer, since it's the mixture-of-agents process the user explicitly opted
into by selecting a MoA preset — not private model reasoning). However, moa.reference events are
stored via recordMoaReference into a Msg's generic thinking field — the same field ordinary
model reasoning uses — and both messageLine.tsx and the ToolTrail component (thinking.tsx)
gate visibility of that field purely on display.sections.thinking's resolved mode. When a user
sets sections.thinking: hidden (to suppress ordinary reasoning noise), the MoA reference blocks
were suppressed right along with it, making /moa appear to skip its reference model entirely —
the classic CLI shows the same run's reference block correctly, confirming this is TUI-specific.

The fix tags Msg segments produced by recordMoaReference with isMoaReference: true and
threads a bypass through every gate that currently keys purely off thinking mode: the
messageLine.tsx wrapper (whether to render the trail block at all), ToolTrail's allHidden
backstop and panel-push condition (whether the reasoning panel exists in the tree), and — found
during review — the panel's initial open/collapsed state and the shift-click "expand all" gesture
(so the panel isn't just present but technically collapsed-and-unreachable). The initial-mount
useState gets the bypass but the reactive re-sync useEffect deliberately does not, per
an existing code comment warning that OR-ing a force-open flag into that effect "locks the panel
open and silently breaks manual chevron clicks" (regression history: #14968) — so a user can still
manually collapse an MoA panel after it opens.

How it works

flowchart TD
    A["moa.reference event"] --> B["recordMoaReference pushes Msg\nwith isMoaReference: true"]
    B --> C{"display.sections.thinking === hidden?"}
    C -->|"before this PR"| D["messageLine.tsx / ToolTrail gate\non thinkingMode alone\n→ block fully suppressed"]
    C -->|"after this PR"| E["shouldShowThinkingTrail() / reasoningAlwaysVisible\nbypass the thinkingMode gate\n→ block renders, opens on mount,\nremains reachable via expand-all"]
Loading

Related Issue

Fixes #64657

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • ui-tui/src/types.ts: Msg gets a new optional isMoaReference?: boolean field.
  • ui-tui/src/app/turnController.ts: recordMoaReference sets isMoaReference: true on the segment it pushes.
  • ui-tui/src/components/messageLine.tsx: extracts shouldShowThinkingTrail (a pure helper, following the file's existing shouldShowResponseSeparator convention) that bypasses the hidden-mode gate when msg.isMoaReference; passes reasoningAlwaysVisible={msg.isMoaReference} to ToolTrail.
  • ui-tui/src/components/thinking.tsx (ToolTrail): accepts the new reasoningAlwaysVisible prop and uses it in allHidden, the panel-push condition, the panel's initial openThinking mount state, and expandAll()'s thinking branch.
  • ui-tui/src/__tests__/messageLine.test.ts: new describe('shouldShowThinkingTrail', ...) block — hidden-with-no-flag, visible-if-any-section-visible, and the MoA-reference-bypass case from the issue.

Step-by-step

  1. Traced the suppression to messageLine.tsx's thinkingMode !== 'hidden' || toolsMode !== 'hidden' || activityMode !== 'hidden' gate and ToolTrail's allHidden/panel-push checks in thinking.tsx, none of which distinguish MoA content from ordinary reasoning.
  2. Added Msg.isMoaReference, set it in recordMoaReference, and extracted shouldShowThinkingTrail as a directly-unit-testable pure function (this file already tests pure helpers this way, no component-render harness needed).
  3. Wrote a regression test for the reported case; confirmed it fails against the pre-fix logic (shouldShowThinkingTrail is not a function before the export existed / logic missing after), passes after.
  4. Ran an adversarial review pass (/simplicio-review). It confirmed the core plumbing was correct but found the panel would still mount collapsed (initial useState(visible.thinking === 'expanded') has no bypass) and be unreachable via the shift-click expand-all gesture — a partial defeat of the "always visible" intent even with the main gates fixed. Fixed both, being careful to only bypass the one-time mount useState and the user-initiated expandAll() action — not the reactive re-sync useEffect, which an existing comment explicitly warns against forcing open (would silently break manual collapse, the exact regression from feat(tui): per-section visibility for the details accordion #14968).

Acceptance Criteria

  • Given display.sections.thinking: hidden and an MoA preset with at least one reference model, when a moa.reference event arrives, then its labelled block renders, opens by default, and is not collapsed-and-hidden.
  • Given the same setup, when the user shift/ctrl-clicks a chevron to expand everything, then the MoA reference panel expands too (not skipped).
  • Given the same setup, when the user manually collapses the MoA reference panel after it opens, then it stays collapsed (no forced re-open fighting the user's action).
  • Adjacent behavior unchanged: ordinary reasoning content is still hidden under thinking: hidden; existing shouldShowResponseSeparator tests untouched.
  • Test suite passes locally with the new tests included.

How to Test

  1. On main, set display.sections.thinking: hidden, configure an MoA preset with ≥1 reference model, run /moa <prompt> in the Ink TUI — no reference block appears.
  2. Check out this branch.
  3. Same setup — the reference block renders open by default; shift-click expand-all also includes it; manually collapsing it via chevron-click still works afterward.

Tests Performed

Check Command Result
New shouldShowThinkingTrail tests npx vitest run src/__tests__/messageLine.test.ts (from ui-tui) 6 passed
Full ui-tui suite npx vitest run (from ui-tui) 103 test files passed, 1114 tests passed; 3 files / 8 tests fail — confirmed identical on unmodified main (Windows-path-vs-POSIX-path and $PATH-format assumptions in editor.test.ts/terminalSetup.test.ts/terminalParity.test.ts, unrelated to this change, pre-existing in this dev environment)
TypeScript npx tsc -b . --noEmit (from ui-tui) ✅ no errors
ESLint npx eslint src/components/messageLine.tsx src/components/thinking.tsx src/app/turnController.ts src/types.ts src/__tests__/messageLine.test.ts ✅ clean
Prettier npx prettier --check on all touched files ✅ clean
Fail-before/pass-after New tests run with the fix stashed TypeError: shouldShowThinkingTrail is not a function (3 of 6 fail); all 6 pass with the fix restored

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix
  • I've run the tests and all pass
  • I've added tests for my changes
  • I've tested on my platform: Windows 11

Documentation & Housekeeping

  • Relevant documentation updated — N/A (internal render-gating detail, no user-facing docs describe it)
  • cli-config.yaml.example updated if config keys changed — N/A
  • CONTRIBUTING.md/AGENTS.md updated if architecture/workflow changed — N/A
  • Cross-platform impact considered (Windows, macOS) — Ink TUI rendering logic, platform-agnostic; no OS-specific code touched
  • Tool descriptions/schemas updated if tool behavior changed — N/A

Screenshots / Logs

$ npx vitest run src/__tests__/messageLine.test.ts
 Test Files  1 passed (1)
      Tests  6 passed (6)

… is hidden

Every moa.reference gateway event stores its labelled reference-model
output in a Msg's generic `thinking` field (turnController's
recordMoaReference), which messageLine.tsx and the ToolTrail component gate
on `display.sections.thinking`'s resolved mode. When that mode resolves to
`hidden`, MoA reference blocks were suppressed along with ordinary model
reasoning — even though (per NousResearch#53855) references are the mixture-of-agents
process the user explicitly opted into, not private reasoning, and should
stay visible regardless of the thinking-section setting.

Adds Msg.isMoaReference (set by recordMoaReference), a shouldShowThinkingTrail
helper mirroring the existing shouldShowResponseSeparator pattern, and a
reasoningAlwaysVisible prop threaded into ToolTrail to bypass the two
suppression gates (the trail-wrapper return-null check and the
allHidden/panel-push checks) plus the panel's initial open state and the
shift-click expand-all gesture, so a MoA reference panel is not just present
in the tree but actually visible and openable on first paint.

Fixes NousResearch#64657
@alt-glitch alt-glitch added type/bug Something isn't working comp/tui Terminal UI (ui-tui/ + tui_gateway/) P3 Low — cosmetic, nice to have labels Jul 15, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Approved

Overview

TUI fix: keeps MoA (Mixture of Agents) reference blocks visible when the thinking section is hidden, rather than collapsing them out of view.

Assessment

  • Correctness: Preserving reference blocks during thinking collapse is the right UX behavior.
  • Security: No security changes.
  • Debug artifacts: None.

Summary

Clean UX fix. LGTM.


Reviewed by Hermes Agent

@teknium1 teknium1 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.

Thanks for tracing the generic-thinking visibility path; the premise is confirmed on current main (ui-tui/src/app/turnController.ts:715-720, ui-tui/src/components/messageLine.tsx:77-92).

Problems

  • ui-tui/src/components/thinking.tsx:738 seeds openThinking from reasoningAlwaysVisible, but the mount effect at :754-759 immediately assigns visible.thinking === 'expanded'. For thinking: hidden, the newly visible MoA panel therefore collapses immediately.
  • The live path is still blocked before MessageLine is reached. On the PR head, ui-tui/src/app/useMainApp.ts:1012-1042 falls back to activity-only when all panels are hidden; it does not recognize an MoA segment in streamSegments. StreamingAssistant then returns early at ui-tui/src/components/streamingAssistant.tsx:40.

Suggested changes

  • Preserve the MoA initial-open state through the initial visibility sync without restoring the prior manual-chevron lock; cover it with a mounted-component test.
  • Include isMoaReference in the all-hidden live-progress predicate and test both live arrival and the settled transcript with sections.thinking: hidden.

Automated hermes-sweeper review.

Comment thread ui-tui/src/components/thinking.tsx
…ce panels

Maintainer review (hermes-sweeper) on this PR found the fix was
incomplete: two paths still hid the MoA reference panel under
thinking: hidden.

1. thinking.tsx: the mount useState correctly seeds openThinking from
   (visible.thinking === 'expanded' || reasoningAlwaysVisible), but the
   re-sync effect on [visible] fires after the FIRST render too, not
   just later updates, and lacks the reasoningAlwaysVisible OR — so it
   immediately collapsed a just-opened MoA panel right after mount.
   Skip only the effect's very first run (a ref flag); every later
   visible change still re-syncs without the override, preserving the
   documented no-OR-at-effect-time contract (manual collapse sticks).

2. useMainApp.ts: showProgressArea's streamSegments predicate gated
   thinking content on thinkingPanelVisible alone, so an MoA reference
   segment (segment.isMoaReference, same flag messageLine.tsx's
   shouldShowThinkingTrail already honors per NousResearch#64657) never kept the
   live progress area up when thinking was hidden — StreamingAssistant
   then returned early before MessageLine was ever reached. Added the
   same override.

Added tests/thinkingMoaReferenceVisibility.test.tsx: mounts ToolTrail
with reasoningAlwaysVisible + sections.thinking: hidden, awaits queued
effects, and asserts the chevron is still open (▾, not ▸) once they
settle.

Validation:
  npx vitest run src/__tests__/thinkingMoaReferenceVisibility.test.tsx
  -> 1 passed
  Fail-before: reverting only the thinking.tsx ref-guard reproduces the
  exact regression -- the same test's frame capture shows the panel
  open on first paint then collapsing to ▸ once the effect fires, and
  the 'not.toContain(▸)' assertion fails as expected.
  npx vitest run (full ui-tui suite): 1115 passed, 8 failed -- all 8
  pre-existing and unrelated (terminalSetup/terminalParity/editor
  resolution env-path tests), confirmed by running them in isolation
  with the same result regardless of this diff.
  npx tsc --noEmit: clean.
  npx eslint src/components/thinking.tsx src/app/useMainApp.ts: clean.
@wesleysimplicio

Copy link
Copy Markdown
Contributor Author

Both issues fixed:

  1. thinking.tsx: the re-sync effect on [visible] fires after the first render too, not just later updates, and lacked the reasoningAlwaysVisible OR — so it immediately collapsed a just-opened MoA panel right after mount. Fixed by skipping only the effect's very first run (a ref flag); every later visible change still re-syncs without the override, so the documented no-OR-at-effect-time contract (manual collapse sticks) is preserved.
  2. useMainApp.ts: showProgressArea's streamSegments predicate gated thinking content on thinkingPanelVisible alone. Added segment.isMoaReference as an override, matching messageLine.tsx's shouldShowThinkingTrail ([Bug]: TUI hides MoA reference blocks when the thinking section is hidden #64657) — this is what unblocks the live path before StreamingAssistant's early return.

Added ui-tui/src/__tests__/thinkingMoaReferenceVisibility.test.tsx: mounts ToolTrail with reasoningAlwaysVisible + sections.thinking: hidden, awaits queued effects, asserts the chevron is still open once they settle.

npx vitest run src/__tests__/thinkingMoaReferenceVisibility.test.tsx
1 passed

Fail-before verified: reverting only the thinking.tsx ref-guard reproduces the regression exactly — the captured frame shows the panel open on first paint then collapsing to once the effect fires, and the test's not.toContain('▸ ') assertion fails as expected.

Full suite: 1115 passed, 8 failed — all 8 pre-existing/unrelated (terminalSetup, terminalParity, editor-resolution env-path tests), confirmed by running them in isolation with the same result regardless of this diff. tsc --noEmit and eslint clean on both changed files.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 16, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Merged via cluster PR #70283 (commit 385a065) — both commits cherry-picked with your authorship preserved. The first-run-skip guard preserving the manual-collapse contract was careful work. Fixes #64657. Thanks!

@teknium1 teknium1 closed this Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tui Terminal UI (ui-tui/ + tui_gateway/) P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: TUI hides MoA reference blocks when the thinking section is hidden

4 participants