diff --git a/apps/desktop/src/components/assistant-ui/thread/message-parts.tsx b/apps/desktop/src/components/assistant-ui/thread/message-parts.tsx
index e50718b189..5fbd46eedd 100644
--- a/apps/desktop/src/components/assistant-ui/thread/message-parts.tsx
+++ b/apps/desktop/src/components/assistant-ui/thread/message-parts.tsx
@@ -237,10 +237,12 @@ const ThinkingDisclosure: FC<{
className={cn(
// Body sits flush with the "Thinking" header — no left indent —
// and inherits the disclosure-level opacity fade defined in
- // styles.css (~0.67 at rest, 1 on hover/focus).
- 'mt-0.5 w-full min-w-0 max-w-full overflow-hidden wrap-anywhere pb-1',
+ // styles.css (~0.67 at rest, 1 on hover/focus). overflow-auto so
+ // the max-h-40 preview is a real scroller, not a clip.
+ 'mt-0.5 w-full min-w-0 max-w-full overflow-auto overscroll-contain wrap-anywhere pb-1',
isPreview && 'max-h-40'
)}
+ data-slot="aui_thinking-body"
ref={scrollRef}
>
{children}
diff --git a/apps/desktop/src/components/assistant-ui/thread/streaming.test.tsx b/apps/desktop/src/components/assistant-ui/thread/streaming.test.tsx
index 8d2d386ec4..4f82314c48 100644
--- a/apps/desktop/src/components/assistant-ui/thread/streaming.test.tsx
+++ b/apps/desktop/src/components/assistant-ui/thread/streaming.test.tsx
@@ -621,6 +621,28 @@ describe('assistant-ui streaming renderer', () => {
expect(container.textContent).not.toContain('```ts')
})
+ it('keeps the height-capped thinking preview scrollable after the turn settles', async () => {
+ const { container, settle } = renderSettlingReasoning()
+
+ const live = container.querySelector('[data-slot="aui_thinking-body"]')?.className ?? ''
+
+ expect(live).toContain('max-h-40')
+ expect(live).toMatch(/\boverflow-auto\b/)
+ expect(live).not.toMatch(/\boverflow-hidden\b/)
+
+ settle()
+
+ await waitFor(() => {
+ expect(within(container).getByRole('button', { name: /thought/i })).toBeTruthy()
+ })
+
+ const settled = container.querySelector('[data-slot="aui_thinking-body"]')?.className ?? ''
+
+ expect(settled).toContain('max-h-40')
+ expect(settled).toMatch(/\boverflow-auto\b/)
+ expect(settled).not.toMatch(/\boverflow-hidden\b/)
+ })
+
it('does not collapse a live thinking preview when the turn settles', async () => {
const { container, settle } = renderSettlingReasoning()
const toggle = within(container).getByRole('button', { name: /thinking/i })