From 3bc52fb9dfd97c4a32573b2f068887aadfb853ab Mon Sep 17 00:00:00 2001 From: Cary Palmer Date: Sat, 15 Aug 2026 19:13:12 -0500 Subject: [PATCH] feat(desktop): running is not busy Gate composer submit and plugin host busy on the target session slice, not a leftover foreground busyRef. Staff can keep typing while a worker session is running. Includes the follow-up test that submit uses the target session busy flag. --- .../src/app/chat/composer/controls.test.tsx | 12 +++++-- .../src/app/chat/composer/controls.tsx | 33 ++++++------------- .../hooks/use-composer-submit.test.tsx | 2 +- .../desktop/src/app/chat/session-view.test.ts | 12 ++++++- apps/desktop/src/app/chat/session-view.tsx | 13 +++++++- .../hooks/use-prompt-actions/index.test.tsx | 14 ++++++-- .../session/hooks/use-prompt-actions/index.ts | 3 +- .../hooks/use-prompt-actions/utils.test.ts | 14 ++++++++ .../session/hooks/use-prompt-actions/utils.ts | 14 +++++--- .../hooks/use-session-actions/index.ts | 11 +++++-- .../session/hooks/use-session-state-cache.ts | 4 +-- apps/desktop/src/sdk/host-state.test.ts | 30 +++++++++++++++++ apps/desktop/src/sdk/index.ts | 17 ++++++++-- .../references/desktop-plugins.md | 7 ++-- .../developer-guide/desktop-plugin-sdk.md | 15 +++++++-- 15 files changed, 153 insertions(+), 48 deletions(-) create mode 100644 apps/desktop/src/sdk/host-state.test.ts diff --git a/apps/desktop/src/app/chat/composer/controls.test.tsx b/apps/desktop/src/app/chat/composer/controls.test.tsx index 8a84be8ea4..cceb970b9f 100644 --- a/apps/desktop/src/app/chat/composer/controls.test.tsx +++ b/apps/desktop/src/app/chat/composer/controls.test.tsx @@ -66,13 +66,19 @@ describe('ComposerControls shortcut tooltips', () => { await expectShortcutTooltip('Send', '↵') }) - it('shows Enter for Steer', async () => { + it('keeps Send (not Steer) while a turn is running if there is a payload', async () => { renderControls({ busy: true, busyAction: 'steer' }) - await expectShortcutTooltip('Steer the current run', '↵') + await expectShortcutTooltip('Send', '↵') }) - it('shows Ctrl+Enter for Queue', async () => { + it('shows Stop only when the composer is empty mid-turn', async () => { + renderControls({ busy: true, busyAction: 'stop', canSubmit: true, hasComposerPayload: false }) + + await expectShortcutTooltip('Stop', '↵') + }) + + it('shows Ctrl+Enter for Queue as the secondary mid-turn action', async () => { renderControls({ busy: true, busyAction: 'queue' }) await expectShortcutTooltip('Queue message', 'Ctrl+↵') diff --git a/apps/desktop/src/app/chat/composer/controls.tsx b/apps/desktop/src/app/chat/composer/controls.tsx index 06a3869894..13fc063c6f 100644 --- a/apps/desktop/src/app/chat/composer/controls.tsx +++ b/apps/desktop/src/app/chat/composer/controls.tsx @@ -13,7 +13,6 @@ import { Layers3, Loader2, Square, - SteeringWheel, Volume2, VolumeX } from '@/lib/icons' @@ -87,7 +86,10 @@ export function ComposerControls({ } const showVoicePrimary = !busy && !hasComposerPayload - const busyLabel = busyAction === 'queue' ? c.queueMessage : busyAction === 'steer' ? c.steer : c.stop + // Steer is just send: a payload keeps the Send affordance mid-turn. Stop + // only when the composer is empty and a turn is running. + const showStop = busy && !hasComposerPayload + const showQueueButton = busyAction !== 'stop' && hasComposerPayload return (
@@ -95,7 +97,7 @@ export function ComposerControls({ - {busyAction === 'steer' ? ( + {showQueueButton ? ( }>