From a05404645ca6ae447e5301f3e5b014b5c712ebf0 Mon Sep 17 00:00:00 2001 From: Merge Sim Date: Sun, 30 Aug 2026 17:49:06 -0700 Subject: [PATCH] Fix mobile structured turn state --- .../MobileStructuredAgentSessionView.test.tsx | 25 +++++++++++++++++++ .../MobileStructuredAgentSessionView.tsx | 5 ++-- .../mobile-structured-composer-command.ts | 16 +++++++++--- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/mobile/src/session/MobileStructuredAgentSessionView.test.tsx b/mobile/src/session/MobileStructuredAgentSessionView.test.tsx index 1bc33da31ec..d4d00ddd5be 100644 --- a/mobile/src/session/MobileStructuredAgentSessionView.test.tsx +++ b/mobile/src/session/MobileStructuredAgentSessionView.test.tsx @@ -1,6 +1,7 @@ import { createElement, type ComponentProps } from 'react' import { act, create, type ReactTestRenderer } from 'react-test-renderer' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import type { AgentJournalRenderItem } from '../../../src/shared/agent-session-journal-types' import type { MobileNativeChatSessionOptionsController } from './use-mobile-native-chat-session-options' import { MobileStructuredAgentSessionView } from './MobileStructuredAgentSessionView' @@ -47,6 +48,18 @@ function options(): MobileNativeChatSessionOptionsController { } } +const ACTIVE_TURN_ITEM: AgentJournalRenderItem = { + itemId: 'status-1', + revision: 1, + body: { + kind: 'status', + text: 'working', + turnLifecycle: { turnId: 'turn-1', state: 'running' } + }, + sequence: 1, + observedAt: 1 +} + describe('MobileStructuredAgentSessionView command seam', () => { let renderer: ReactTestRenderer | null = null const onSend = vi.fn(async () => true) @@ -144,6 +157,18 @@ describe('MobileStructuredAgentSessionView command seam', () => { expect(empty.props.children[0].props.children).toBe('New Claude chat') }) + it('marks session options working while a turn is active', () => { + act(() => { + renderer!.update( + createElement(MobileStructuredAgentSessionView, props({ items: [ACTIVE_TURN_ITEM] })) + ) + }) + + expect( + renderer!.root.findByType('MobileNativeChatComposer').props.sessionOptions + ).toMatchObject({ isWorking: true }) + }) + it('keeps the composer enabled and routes stable TUI ownership through its bridge', async () => { act(() => { renderer!.update( diff --git a/mobile/src/session/MobileStructuredAgentSessionView.tsx b/mobile/src/session/MobileStructuredAgentSessionView.tsx index 2dde1b492cb..15eabf2a535 100644 --- a/mobile/src/session/MobileStructuredAgentSessionView.tsx +++ b/mobile/src/session/MobileStructuredAgentSessionView.tsx @@ -100,6 +100,7 @@ export function MobileStructuredAgentSessionView(props: Props): React.JSX.Elemen props.handoff === null || props.handoff === undefined || (props.handoff.owner === 'native' && props.handoff.phase === 'idle') + const isWorking = turnId !== null const composerEnabled = props.status === 'ready' && (stableNativeOwner || stableTuiOwner) const rows = useMemo( () => buildMobileStructuredTimeline(props.items, props.outbox), @@ -131,7 +132,7 @@ export function MobileStructuredAgentSessionView(props: Props): React.JSX.Elemen { diff --git a/mobile/src/session/mobile-structured-composer-command.ts b/mobile/src/session/mobile-structured-composer-command.ts index 95d525ade00..fb2aba78c3b 100644 --- a/mobile/src/session/mobile-structured-composer-command.ts +++ b/mobile/src/session/mobile-structured-composer-command.ts @@ -1,18 +1,28 @@ import { dispatchStructuredAgentSessionComposerCommand, isStructuredAgentSessionComposerCommand, - structuredAgentSessionSlashCommands, STRUCTURED_AGENT_SESSION_SLASH_COMMANDS, type StructuredAgentSessionCommandOutcome } from '../../../src/shared/structured-agent-session-composer' +import { getVerifiedNativeChatCommands } from '../../../src/shared/native-chat-agent-profiles' +import type { SlashCommandSuggestion } from '../../../src/shared/native-chat-slash-commands' import type { MobileStructuredAgent } from './mobile-structured-session-create' import type { MobileNativeChatSessionOptionsController } from './use-mobile-native-chat-session-options' export const MOBILE_STRUCTURED_SLASH_COMMANDS = STRUCTURED_AGENT_SESSION_SLASH_COMMANDS export type MobileStructuredCommandOutcome = StructuredAgentSessionCommandOutcome -export function mobileStructuredSlashCommands(agent: MobileStructuredAgent) { - return structuredAgentSessionSlashCommands(agent) +const STRUCTURED_COMMAND_PREFIX: readonly SlashCommandSuggestion[] = [ + { name: 'model', description: 'Choose the model and reasoning effort' }, + { name: 'effort', description: 'Choose reasoning effort' } +] + +export function mobileStructuredSlashCommands( + agent: MobileStructuredAgent +): readonly SlashCommandSuggestion[] { + return agent === 'codex' + ? STRUCTURED_AGENT_SESSION_SLASH_COMMANDS + : [...STRUCTURED_COMMAND_PREFIX, ...getVerifiedNativeChatCommands(agent)] } export function isMobileStructuredComposerCommand(