11import { Text } from "@components/text" ;
2- import { DEFAULT_CLAUDE_EXECUTION_MODE } from "@posthog/core/sessions/executionModes" ;
2+ import { getCloudReasoningConfigOptionId } from "@posthog/core/sessions/cloudSessionConfig" ;
3+ import { getDefaultExecutionModeForAdapter } from "@posthog/core/sessions/executionModes" ;
34import {
45 countUserMessages ,
56 getSessionActivityPhase ,
67} from "@posthog/core/sessions/sessionActivity" ;
8+ import type { CloudComposerSelection } from "@posthog/core/task-detail/composerModelPolicy" ;
79import { isTaskRunning } from "@posthog/core/tasks/taskArchive" ;
810import {
11+ type Adapter ,
12+ DEFAULT_CODEX_MODEL ,
913 DEFAULT_GATEWAY_MODEL ,
1014 DEFAULT_REASONING_EFFORT ,
1115 type ExecutionMode ,
12- getReasoningEffortOptions ,
16+ isModalModelId ,
17+ isSupportedReasoningEffort ,
18+ KIMI_MODEL_FLAG ,
1319 type SupportedReasoningEffort ,
1420 serializeCloudPrompt ,
1521 type Task ,
1622} from "@posthog/shared" ;
1723import { useQueryClient } from "@tanstack/react-query" ;
1824import * as Haptics from "expo-haptics" ;
1925import { useLocalSearchParams , useRouter } from "expo-router" ;
26+ import { useFeatureFlag } from "posthog-react-native" ;
2027import { useCallback , useEffect , useRef , useState } from "react" ;
2128import {
2229 ActivityIndicator ,
@@ -57,6 +64,7 @@ import {
5764import { useTaskSessionStore } from "@/features/tasks/stores/taskSessionStore" ;
5865import { useTaskStore } from "@/features/tasks/stores/taskStore" ;
5966import { confirmStopRun } from "@/features/tasks/utils/archiveGuard" ;
67+ import { buildCloudTaskRunConfig } from "@/features/tasks/utils/cloudTaskRunConfig" ;
6068import { useScreenInsets } from "@/hooks/useScreenInsets" ;
6169import {
6270 ANALYTICS_EVENTS ,
@@ -167,11 +175,45 @@ export default function TaskDetailScreen() {
167175 const [ initialComposerMessage , setInitialComposerMessage ] = useState <
168176 string | undefined
169177 > ( ) ;
178+ const composerAdapter : Adapter =
179+ task ?. latest_run ?. runtime_adapter &&
180+ ! session ?. terminalStatus &&
181+ composerConfig ?. adapter !== task . latest_run . runtime_adapter
182+ ? task . latest_run . runtime_adapter
183+ : ( composerConfig ?. adapter ??
184+ task ?. latest_run ?. runtime_adapter ??
185+ "claude" ) ;
186+ const composerConfigMatchesAdapter =
187+ composerConfig ?. adapter === undefined
188+ ? composerAdapter === "claude"
189+ : composerConfig . adapter === composerAdapter ;
170190 const composerMode : ExecutionMode =
171- composerConfig ?. mode ?? DEFAULT_CLAUDE_EXECUTION_MODE ;
172- const composerModel = composerConfig ?. model ?? DEFAULT_GATEWAY_MODEL ;
191+ ( composerConfigMatchesAdapter ? composerConfig ?. mode : undefined ) ??
192+ getDefaultExecutionModeForAdapter ( composerAdapter ) ;
193+ const kimiEnabled = ! ! useFeatureFlag ( KIMI_MODEL_FLAG ) ;
194+ const persistedComposerModel =
195+ ( composerConfigMatchesAdapter ? composerConfig ?. model : undefined ) ??
196+ task ?. latest_run ?. model ??
197+ ( composerAdapter === "codex" ? DEFAULT_CODEX_MODEL : DEFAULT_GATEWAY_MODEL ) ;
198+ // Fall a persisted Kimi selection back to the default when the flag is off so
199+ // a hidden model never gets sent on the retry-after-terminal path. The
200+ // composer independently re-resolves against the live config once mounted.
201+ const composerModel =
202+ ! kimiEnabled && isModalModelId ( persistedComposerModel )
203+ ? DEFAULT_GATEWAY_MODEL
204+ : persistedComposerModel ;
205+ const requestedComposerReasoning = composerConfigMatchesAdapter
206+ ? composerConfig ?. reasoning
207+ : undefined ;
173208 const composerReasoning : SupportedReasoningEffort =
174- composerConfig ?. reasoning ?? DEFAULT_REASONING_EFFORT ;
209+ requestedComposerReasoning &&
210+ isSupportedReasoningEffort (
211+ composerAdapter ,
212+ composerModel ,
213+ requestedComposerReasoning ,
214+ )
215+ ? requestedComposerReasoning
216+ : DEFAULT_REASONING_EFFORT ;
175217
176218 const messagingMode = useMessagingMode ( taskId ) ;
177219 const queuedCount = useQueuedCount ( taskId ) ;
@@ -314,18 +356,18 @@ export default function TaskDetailScreen() {
314356 )
315357 : text ;
316358
317- const supportsReasoning =
318- getReasoningEffortOptions ( "claude" , composerModel ) !== null ;
319359 const updatedTask = await getPostHogApiClient ( ) . runTaskInCloud (
320360 taskId ,
321361 undefined ,
322362 {
323363 resumeFromRunId : task . latest_run ?. id ,
324364 pendingUserMessage,
325- adapter : "claude" ,
326- model : composerModel ,
327- reasoningLevel : supportsReasoning ? composerReasoning : undefined ,
328- initialPermissionMode : composerMode ,
365+ ...buildCloudTaskRunConfig ( {
366+ adapter : composerAdapter ,
367+ mode : composerMode ,
368+ model : composerModel ,
369+ reasoning : composerReasoning ,
370+ } ) ,
329371 rtkEnabled : usePreferencesStore . getState ( ) . rtkEnabledCloud ,
330372 } ,
331373 ) ;
@@ -351,6 +393,7 @@ export default function TaskDetailScreen() {
351393 connectToTask ,
352394 updateTaskInCache ,
353395 composerMode ,
396+ composerAdapter ,
354397 composerModel ,
355398 composerReasoning ,
356399 ] ,
@@ -507,6 +550,17 @@ export default function TaskDetailScreen() {
507550 [ taskId , setComposerConfig , setConfigOption ] ,
508551 ) ;
509552
553+ const handleAdapterChange = useCallback (
554+ ( selection : CloudComposerSelection ) => {
555+ if ( ! taskId ) return ;
556+ setComposerConfig ( taskId , selection ) ;
557+ const preferences = usePreferencesStore . getState ( ) ;
558+ preferences . setLastNewTaskMode ( selection . mode ) ;
559+ preferences . setLastUsedReasoningEffort ( selection . reasoning ) ;
560+ } ,
561+ [ taskId , setComposerConfig ] ,
562+ ) ;
563+
510564 const handleModelChange = useCallback (
511565 ( value : string ) => {
512566 if ( ! taskId ) return ;
@@ -520,10 +574,14 @@ export default function TaskDetailScreen() {
520574 ( value : SupportedReasoningEffort ) => {
521575 if ( ! taskId ) return ;
522576 setComposerConfig ( taskId , { reasoning : value } ) ;
523- setConfigOption ( taskId , "effort" , value ) . catch ( ( ) => { } ) ;
577+ setConfigOption (
578+ taskId ,
579+ getCloudReasoningConfigOptionId ( composerAdapter ) ,
580+ value ,
581+ ) . catch ( ( ) => { } ) ;
524582 usePreferencesStore . getState ( ) . setLastUsedReasoningEffort ( value ) ;
525583 } ,
526- [ taskId , setComposerConfig , setConfigOption ] ,
584+ [ taskId , composerAdapter , setComposerConfig , setConfigOption ] ,
527585 ) ;
528586
529587 const handleStop = useCallback ( ( ) => {
@@ -575,6 +633,12 @@ export default function TaskDetailScreen() {
575633 undefined ,
576634 {
577635 resumeFromRunId : task . latest_run ?. id ,
636+ ...buildCloudTaskRunConfig ( {
637+ adapter : composerAdapter ,
638+ mode : composerMode ,
639+ model : composerModel ,
640+ reasoning : composerReasoning ,
641+ } ) ,
578642 rtkEnabled : usePreferencesStore . getState ( ) . rtkEnabledCloud ,
579643 } ,
580644 ) ;
@@ -591,7 +655,17 @@ export default function TaskDetailScreen() {
591655 "Could not restart the task. Please try again." ,
592656 ) ;
593657 }
594- } , [ taskId , task , disconnectFromTask , connectToTask , updateTaskInCache ] ) ;
658+ } , [
659+ taskId ,
660+ task ,
661+ disconnectFromTask ,
662+ connectToTask ,
663+ updateTaskInCache ,
664+ composerAdapter ,
665+ composerModel ,
666+ composerReasoning ,
667+ composerMode ,
668+ ] ) ;
595669
596670 // Clear retrying once the agent finishes a turn or the run terminates.
597671 useEffect ( ( ) => {
@@ -785,6 +859,9 @@ export default function TaskDetailScreen() {
785859 />
786860 ) : null }
787861 < TaskChatComposer
862+ key = { taskId }
863+ adapter = { composerAdapter }
864+ canChangeAdapter = { ! ! session ?. terminalStatus }
788865 onSend = { handleSendPrompt }
789866 restoredDraft = { restoredDraft }
790867 editing = { ! ! editingQueuedId }
@@ -798,6 +875,7 @@ export default function TaskDetailScreen() {
798875 mode = { composerMode }
799876 model = { composerModel }
800877 reasoning = { composerReasoning }
878+ onAdapterChange = { handleAdapterChange }
801879 onModeChange = { handleModeChange }
802880 onModelChange = { handleModelChange }
803881 onReasoningChange = { handleReasoningChange }
0 commit comments