From f213f755ee6fed7229433a2863214860f02d36e1 Mon Sep 17 00:00:00 2001 From: brooklyn! Date: Thu, 24 Sep 2026 13:01:36 -0500 Subject: [PATCH 1/2] fix(ci): join continuation fragments as pairs The retry path stored fragment text as strings, and the joiner unpacks (text, stub) pairs. Also sort the composer-clamp props so desktop lint stops failing every merge. --- agent/turn_truncation.py | 9 ++++++++- .../assistant-ui/thread/list-session-scroll.test.tsx | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/agent/turn_truncation.py b/agent/turn_truncation.py index 895b9251e1..61c44e56fc 100644 --- a/agent/turn_truncation.py +++ b/agent/turn_truncation.py @@ -76,8 +76,15 @@ def collapse_continuation_trail( return "" messages[turn_start:] = retained from agent.conversation_loop import _join_truncated_parts + raw = fragment_parts if parts is None else parts + join_parts = [] + for item in raw: + if isinstance(item, tuple) and len(item) == 2 and isinstance(item[0], str): + join_parts.append(item) + elif isinstance(item, str) and item: + join_parts.append((item, False)) partial = agent._strip_think_blocks( - _join_truncated_parts(fragment_parts if parts is None else parts) + _join_truncated_parts(join_parts) ).strip() if partial: append_message(messages, {"role": "assistant", "content": partial, "finish_reason": finish_reason}) diff --git a/apps/desktop/src/components/assistant-ui/thread/list-session-scroll.test.tsx b/apps/desktop/src/components/assistant-ui/thread/list-session-scroll.test.tsx index d6f88e9245..065ff46d27 100644 --- a/apps/desktop/src/components/assistant-ui/thread/list-session-scroll.test.tsx +++ b/apps/desktop/src/components/assistant-ui/thread/list-session-scroll.test.tsx @@ -114,10 +114,10 @@ function ScrollHarness({ {} }}> @@ -250,7 +250,7 @@ describe('list session-scroll restore', () => { ) const { container, unmount } = render( - + ) const vp = viewportEl(container) const clearance = vp.querySelector('[data-slot="aui_composer-clearance"]') From e8acdc5fc899d7e9220dd6ac0dfeedebb4a42a9a Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Thu, 24 Sep 2026 08:02:36 -0700 Subject: [PATCH 2/2] test(desktop): stop reveal-focus test importing the whole app in a hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reveal-focus.test.ts did `await import('@/app/contrib/controller')` in beforeAll to get the terminal pane, its collapse binding, and the ⌘K row. The controller statically imports the entire app graph (every pane, eager bundled-plugin glob, HUD, context menu), so that hook paid a full cold transform of it: >30s on CI's parallel ui run (hook timeout, red twice on #121409) and ~124s here even with the file alone. The terminal ⌘K row moves verbatim into reveal-focus.ts as `terminalPaletteToggle`; the controller registers that constant. The test wires the terminal slice through the same production functions (pane registration, `bindToolPaneCollapse` with `$terminalTakeover` and `$showsAdvancedChrome`, the real palette row) and keeps every assertion. Sabotage check: making the reveal not schedule focus and routing the row back to `togglePaneVisible`, turns 5 of 6 cases red. --- apps/desktop/src/app/contrib/controller.tsx | 21 ++------ .../terminal/reveal-focus.test.ts | 52 +++++++++++++++++-- .../right-sidebar/terminal/reveal-focus.ts | 22 ++++++++ 3 files changed, 73 insertions(+), 22 deletions(-) diff --git a/apps/desktop/src/app/contrib/controller.tsx b/apps/desktop/src/app/contrib/controller.tsx index 72b55229c1..dcea643049 100644 --- a/apps/desktop/src/app/contrib/controller.tsx +++ b/apps/desktop/src/app/contrib/controller.tsx @@ -49,7 +49,6 @@ import { PanelBottom, PanelTop, SlidersHorizontal, - Terminal, Upload, Users, Zap @@ -106,7 +105,7 @@ import { import { AppContextMenu } from '../context-menu/app-context-menu' import { HudShell } from '../hud/hud-shell' import { $terminalTakeover, setTerminalTakeover } from '../right-sidebar/store' -import { toggleTerminalPane } from '../right-sidebar/terminal/reveal-focus' +import { terminalPaletteToggle } from '../right-sidebar/terminal/reveal-focus' import { $workspaceIsPage, WORKSPACE_PAGE_HEADER_AREA } from '../routes' import { BASIC_TREE, DEFAULT_TREE, registerLayoutPresets } from './layout-presets' @@ -598,22 +597,8 @@ bindToolPaneCollapse( // Without the statusbar, the rail is the only way to switch profiles or gateways. $profiles.subscribe(profiles => setModeContext({ profileCount: profiles.length })) $connectionsRegistry.subscribe(registry => setModeContext({ connectionCount: registry?.connections.length ?? 0 })) -// ⌘K door onto the same pane the keybind and statusbar pill flip — was a -// one-way "open" row under Go to, so it never showed on/off and couldn't hide. -// Reads the TREE like every other pane toggle: `$terminalTakeover` stays true -// behind a stacked sibling tab or a minimized zone, which would light the row -// "on" for a terminal that isn't on screen. -registry.register( - paletteToggle({ - id: 'view.showTerminal', - label: 'Toggle terminal', - action: 'view.showTerminal', - icon: Terminal, - keywords: ['terminal', 'shell', 'console', 'pty'], - get: () => isPaneVisible('terminal'), - set: () => toggleTerminalPane() - }) -) +// ⌘K door onto the same pane the keybind and statusbar pill flip. +registry.register(terminalPaletteToggle) // Logs are ⌘K-ONLY chrome: the pane contribution EXISTS only while $logsOpen // is on. Off (the default) keeps logs out of the registry and the tree diff --git a/apps/desktop/src/app/right-sidebar/terminal/reveal-focus.test.ts b/apps/desktop/src/app/right-sidebar/terminal/reveal-focus.test.ts index bacc9a7e95..3674e267e6 100644 --- a/apps/desktop/src/app/right-sidebar/terminal/reveal-focus.test.ts +++ b/apps/desktop/src/app/right-sidebar/terminal/reveal-focus.test.ts @@ -9,10 +9,20 @@ vi.mock('@/themes/context', () => ({ import { PALETTE_AREA, type PaletteContribution } from '@/app/command-palette/contrib' import { useKeybinds } from '@/app/hooks/use-keybinds' -import { setTerminalTakeover } from '@/app/right-sidebar/store' +import { $terminalTakeover, setTerminalTakeover } from '@/app/right-sidebar/store' +import { terminalPaletteToggle } from '@/app/right-sidebar/terminal/reveal-focus' import { useStatusbarItems } from '@/app/shell/hooks/use-statusbar-items' -import { collapseTreePane, isPaneVisible, revealTreePane, setPaneCollapsed } from '@/components/pane-shell/tree/store' +import { group, split } from '@/components/pane-shell/tree/model' +import { + $layoutTree, + bindToolPaneCollapse, + collapseTreePane, + isPaneVisible, + revealTreePane, + setPaneCollapsed +} from '@/components/pane-shell/tree/store' import { registry } from '@/contrib/registry' +import { $showsAdvancedChrome } from '@/store/interface-mode' // Ctrl+`, the ⌘K row, and the statusbar pill reveal the terminal. That reveal // must make the on-screen xterm the keyboard target, and the claim has to @@ -22,8 +32,42 @@ const wrapper = ({ children }: { children: ReactNode }) => createElement(MemoryR let unmountKeybinds: (() => void) | undefined -beforeAll(async () => { - await import('@/app/contrib/controller') +// The contrib controller owns this wiring in the app, but importing it pulls +// the whole app graph (every pane, bundled plugin, HUD) into a hook: minutes of +// cold transform under the parallel ui run, which blew the 30s hook timeout in +// CI. Wire the terminal slice here through the same production functions and +// the same palette row the controller registers. +beforeAll(() => { + registry.registerMany([ + { + area: 'panes', + data: { placement: 'main', uncloseable: true }, + id: 'workspace', + render: () => null, + title: 'workspace' + }, + { + area: 'panes', + data: { placement: 'bottom', lifecycleKeepAlive: true }, + id: 'terminal', + render: () => null, + title: 'terminal' + }, + terminalPaletteToggle + ]) + $layoutTree.set( + split('column', [ + group(['workspace'], { active: 'workspace', id: 'grp-main' }), + group(['terminal'], { active: 'terminal', id: 'grp-terminal' }) + ]) + ) + bindToolPaneCollapse( + 'terminal', + $terminalTakeover, + () => setTerminalTakeover(false), + () => setTerminalTakeover(true), + $showsAdvancedChrome + ) }) beforeEach(() => { diff --git a/apps/desktop/src/app/right-sidebar/terminal/reveal-focus.ts b/apps/desktop/src/app/right-sidebar/terminal/reveal-focus.ts index 5d073fec81..6f56e24471 100644 --- a/apps/desktop/src/app/right-sidebar/terminal/reveal-focus.ts +++ b/apps/desktop/src/app/right-sidebar/terminal/reveal-focus.ts @@ -1,4 +1,6 @@ +import { paletteToggle } from '@/app/command-palette/contrib' import { isPaneVisible, togglePaneVisible } from '@/components/pane-shell/tree/store' +import { Terminal } from '@/lib/icons' import { isFocusWithin } from '@/lib/keybinds/combo' const TERMINAL_FOCUS_SCOPE = '[data-terminal]' @@ -56,3 +58,23 @@ export function toggleTerminalPane(): void { focusRevealedTerminal() } } + +/** + * ⌘K door onto the same pane the keybind and statusbar pill flip — was a + * one-way "open" row under Go to, so it never showed on/off and couldn't hide. + * Reads the TREE like every other pane toggle: `$terminalTakeover` stays true + * behind a stacked sibling tab or a minimized zone, which would light the row + * "on" for a terminal that isn't on screen. + * + * Lives here, not inline in the contrib controller, so the reveal-focus test + * drives the real row without importing the whole app graph. + */ +export const terminalPaletteToggle = paletteToggle({ + id: 'view.showTerminal', + label: 'Toggle terminal', + action: 'view.showTerminal', + icon: Terminal, + keywords: ['terminal', 'shell', 'console', 'pty'], + get: () => isPaneVisible('terminal'), + set: () => toggleTerminalPane() +})