diff --git a/src/renderer/src/components/native-chat/native-chat-availability.ts b/src/renderer/src/components/native-chat/native-chat-availability.ts index cccbb956f3c..4e520af1b9a 100644 --- a/src/renderer/src/components/native-chat/native-chat-availability.ts +++ b/src/renderer/src/components/native-chat/native-chat-availability.ts @@ -2,7 +2,7 @@ import type { Tab, TuiAgent } from '../../../../shared/types' import type { AgentType } from '../../../../shared/agent-status-types' import { isNativeChatSupportedAgent } from '@/lib/native-chat-supported-agent' -export { isNativeChatSupportedAgent } from '@/lib/native-chat-supported-agent' +export { isNativeChatSupportedAgent } /** Inputs that decide whether a tab may toggle into the native chat view. * Kept as a plain shape (not the live store) so the decision stays pure and diff --git a/src/renderer/src/components/native-chat/native-chat-pending.ts b/src/renderer/src/components/native-chat/native-chat-pending.ts index e76349a9a62..3021dc7bba4 100644 --- a/src/renderer/src/components/native-chat/native-chat-pending.ts +++ b/src/renderer/src/components/native-chat/native-chat-pending.ts @@ -176,6 +176,9 @@ export function launchPromptAsMessage( } } +// Why: prune only once an assistant turn has landed after the matching user +// text — keeping the optimistic bubble through the user-only phase avoids a +// first-turn flash before the transcript's own copy of the turn catches up. export function shouldPruneLaunchPrompt( entry: NativeChatLaunchPrompt, messages: NativeChatMessage[] diff --git a/src/renderer/src/lib/launch-agent-in-new-tab.ts b/src/renderer/src/lib/launch-agent-in-new-tab.ts index f815d450c9d..42d577b4d14 100644 --- a/src/renderer/src/lib/launch-agent-in-new-tab.ts +++ b/src/renderer/src/lib/launch-agent-in-new-tab.ts @@ -268,10 +268,19 @@ export function launchAgentInNewTab(args: LaunchAgentInNewTabArgs): LaunchAgentI // lands after mount the agent binary never starts; the user sees a bare shell. // Since both calls happen synchronously in the same React batch, the queue // is in place by the time the pane commits. + // Why: the followup path pastes the prompt as an unsubmitted draft (submit + // stays false), so gate the initial chat view like a `draft` launch — + // otherwise a default `auto-submit` followup would open native chat with no + // submitted turn to render. + const viewModePromptDelivery = + hasPrompt && isFollowupPath && promptDelivery === 'auto-submit' ? 'draft' : promptDelivery const tab = store.createTab(worktreeId, groupId, undefined, { launchAgent: agent, quickCommandLabel, - ...initialAgentTabViewModeProps(store.settings, { agent, promptDelivery }) + ...initialAgentTabViewModeProps(store.settings, { + agent, + promptDelivery: viewModePromptDelivery + }) }) store.queueTabStartupCommand(tab.id, { command: startupPlan.launchCommand, diff --git a/src/renderer/src/lib/worktree-activation.test.ts b/src/renderer/src/lib/worktree-activation.test.ts index 107a573eeeb..53e6a5fe8ed 100644 --- a/src/renderer/src/lib/worktree-activation.test.ts +++ b/src/renderer/src/lib/worktree-activation.test.ts @@ -495,6 +495,61 @@ describe('ensureWorktreeHasInitialTerminal', () => { }) }) + it('opens the startup default tab in native chat when configured', () => { + let createdIndex = 0 + const createTab = vi.fn(() => ({ id: `tab-${++createdIndex}` })) + const store = createMockStore({ + createTab, + settings: { + experimentalNativeChat: true, + openAgentTabsInChatByDefault: true + } + }) + + ensureWorktreeHasInitialTerminal( + store, + 'wt-1', + { command: 'claude', launchAgent: 'claude' }, + undefined, + undefined, + { runCommands: true, tabs: [{ title: 'Claude', command: 'claude' }] } + ) + + expect(createTab).toHaveBeenNthCalledWith(1, 'wt-1', undefined, undefined, { + pendingActivationSpawn: true, + recordInteraction: false, + launchAgent: 'claude', + viewMode: 'chat' + }) + }) + + it('keeps a draft startup default tab in terminal mode even when native chat is configured', () => { + let createdIndex = 0 + const createTab = vi.fn(() => ({ id: `tab-${++createdIndex}` })) + const store = createMockStore({ + createTab, + settings: { + experimentalNativeChat: true, + openAgentTabsInChatByDefault: true + } + }) + + ensureWorktreeHasInitialTerminal( + store, + 'wt-1', + { command: 'claude', launchAgent: 'claude', draftPrompt: 'Review before sending' }, + undefined, + undefined, + { runCommands: true, tabs: [{ title: 'Claude', command: 'claude' }] } + ) + + expect(createTab).toHaveBeenNthCalledWith(1, 'wt-1', undefined, undefined, { + pendingActivationSpawn: true, + recordInteraction: false, + launchAgent: 'claude' + }) + }) + it('gates startup behind setup completion when both are provided in new-tab mode', () => { setSetupScriptLaunchMode('new-tab') let createdIndex = 0