Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.

Commit b2efe63

Browse files
feat(mobile): add Codex task parity (#3654)
1 parent 3c29388 commit b2efe63

20 files changed

Lines changed: 1035 additions & 613 deletions

apps/mobile/src/app/task/[id].tsx

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import { 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";
34
import {
45
countUserMessages,
56
getSessionActivityPhase,
67
} from "@posthog/core/sessions/sessionActivity";
8+
import type { CloudComposerSelection } from "@posthog/core/task-detail/composerModelPolicy";
79
import { isTaskRunning } from "@posthog/core/tasks/taskArchive";
810
import {
11+
type Adapter,
12+
DEFAULT_CODEX_MODEL,
913
DEFAULT_GATEWAY_MODEL,
1014
DEFAULT_REASONING_EFFORT,
1115
type ExecutionMode,
12-
getReasoningEffortOptions,
16+
isSupportedReasoningEffort,
1317
type SupportedReasoningEffort,
1418
serializeCloudPrompt,
1519
type Task,
@@ -57,6 +61,7 @@ import {
5761
import { useTaskSessionStore } from "@/features/tasks/stores/taskSessionStore";
5862
import { useTaskStore } from "@/features/tasks/stores/taskStore";
5963
import { confirmStopRun } from "@/features/tasks/utils/archiveGuard";
64+
import { buildCloudTaskRunConfig } from "@/features/tasks/utils/cloudTaskRunConfig";
6065
import { useScreenInsets } from "@/hooks/useScreenInsets";
6166
import {
6267
ANALYTICS_EVENTS,
@@ -167,11 +172,37 @@ export default function TaskDetailScreen() {
167172
const [initialComposerMessage, setInitialComposerMessage] = useState<
168173
string | undefined
169174
>();
175+
const composerAdapter: Adapter =
176+
task?.latest_run?.runtime_adapter &&
177+
!session?.terminalStatus &&
178+
composerConfig?.adapter !== task.latest_run.runtime_adapter
179+
? task.latest_run.runtime_adapter
180+
: (composerConfig?.adapter ??
181+
task?.latest_run?.runtime_adapter ??
182+
"claude");
183+
const composerConfigMatchesAdapter =
184+
composerConfig?.adapter === undefined
185+
? composerAdapter === "claude"
186+
: composerConfig.adapter === composerAdapter;
170187
const composerMode: ExecutionMode =
171-
composerConfig?.mode ?? DEFAULT_CLAUDE_EXECUTION_MODE;
172-
const composerModel = composerConfig?.model ?? DEFAULT_GATEWAY_MODEL;
188+
(composerConfigMatchesAdapter ? composerConfig?.mode : undefined) ??
189+
getDefaultExecutionModeForAdapter(composerAdapter);
190+
const composerModel =
191+
(composerConfigMatchesAdapter ? composerConfig?.model : undefined) ??
192+
task?.latest_run?.model ??
193+
(composerAdapter === "codex" ? DEFAULT_CODEX_MODEL : DEFAULT_GATEWAY_MODEL);
194+
const requestedComposerReasoning = composerConfigMatchesAdapter
195+
? composerConfig?.reasoning
196+
: undefined;
173197
const composerReasoning: SupportedReasoningEffort =
174-
composerConfig?.reasoning ?? DEFAULT_REASONING_EFFORT;
198+
requestedComposerReasoning &&
199+
isSupportedReasoningEffort(
200+
composerAdapter,
201+
composerModel,
202+
requestedComposerReasoning,
203+
)
204+
? requestedComposerReasoning
205+
: DEFAULT_REASONING_EFFORT;
175206

176207
const messagingMode = useMessagingMode(taskId);
177208
const queuedCount = useQueuedCount(taskId);
@@ -314,18 +345,18 @@ export default function TaskDetailScreen() {
314345
)
315346
: text;
316347

317-
const supportsReasoning =
318-
getReasoningEffortOptions("claude", composerModel) !== null;
319348
const updatedTask = await getPostHogApiClient().runTaskInCloud(
320349
taskId,
321350
undefined,
322351
{
323352
resumeFromRunId: task.latest_run?.id,
324353
pendingUserMessage,
325-
adapter: "claude",
326-
model: composerModel,
327-
reasoningLevel: supportsReasoning ? composerReasoning : undefined,
328-
initialPermissionMode: composerMode,
354+
...buildCloudTaskRunConfig({
355+
adapter: composerAdapter,
356+
mode: composerMode,
357+
model: composerModel,
358+
reasoning: composerReasoning,
359+
}),
329360
rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud,
330361
},
331362
);
@@ -351,6 +382,7 @@ export default function TaskDetailScreen() {
351382
connectToTask,
352383
updateTaskInCache,
353384
composerMode,
385+
composerAdapter,
354386
composerModel,
355387
composerReasoning,
356388
],
@@ -507,6 +539,17 @@ export default function TaskDetailScreen() {
507539
[taskId, setComposerConfig, setConfigOption],
508540
);
509541

542+
const handleAdapterChange = useCallback(
543+
(selection: CloudComposerSelection) => {
544+
if (!taskId) return;
545+
setComposerConfig(taskId, selection);
546+
const preferences = usePreferencesStore.getState();
547+
preferences.setLastNewTaskMode(selection.mode);
548+
preferences.setLastUsedReasoningEffort(selection.reasoning);
549+
},
550+
[taskId, setComposerConfig],
551+
);
552+
510553
const handleModelChange = useCallback(
511554
(value: string) => {
512555
if (!taskId) return;
@@ -520,10 +563,14 @@ export default function TaskDetailScreen() {
520563
(value: SupportedReasoningEffort) => {
521564
if (!taskId) return;
522565
setComposerConfig(taskId, { reasoning: value });
523-
setConfigOption(taskId, "effort", value).catch(() => {});
566+
setConfigOption(
567+
taskId,
568+
getCloudReasoningConfigOptionId(composerAdapter),
569+
value,
570+
).catch(() => {});
524571
usePreferencesStore.getState().setLastUsedReasoningEffort(value);
525572
},
526-
[taskId, setComposerConfig, setConfigOption],
573+
[taskId, composerAdapter, setComposerConfig, setConfigOption],
527574
);
528575

529576
const handleStop = useCallback(() => {
@@ -575,6 +622,12 @@ export default function TaskDetailScreen() {
575622
undefined,
576623
{
577624
resumeFromRunId: task.latest_run?.id,
625+
...buildCloudTaskRunConfig({
626+
adapter: composerAdapter,
627+
mode: composerMode,
628+
model: composerModel,
629+
reasoning: composerReasoning,
630+
}),
578631
rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud,
579632
},
580633
);
@@ -591,7 +644,17 @@ export default function TaskDetailScreen() {
591644
"Could not restart the task. Please try again.",
592645
);
593646
}
594-
}, [taskId, task, disconnectFromTask, connectToTask, updateTaskInCache]);
647+
}, [
648+
taskId,
649+
task,
650+
disconnectFromTask,
651+
connectToTask,
652+
updateTaskInCache,
653+
composerAdapter,
654+
composerModel,
655+
composerReasoning,
656+
composerMode,
657+
]);
595658

596659
// Clear retrying once the agent finishes a turn or the run terminates.
597660
useEffect(() => {
@@ -785,6 +848,9 @@ export default function TaskDetailScreen() {
785848
/>
786849
) : null}
787850
<TaskChatComposer
851+
key={taskId}
852+
adapter={composerAdapter}
853+
canChangeAdapter={!!session?.terminalStatus}
788854
onSend={handleSendPrompt}
789855
restoredDraft={restoredDraft}
790856
editing={!!editingQueuedId}
@@ -798,6 +864,7 @@ export default function TaskDetailScreen() {
798864
mode={composerMode}
799865
model={composerModel}
800866
reasoning={composerReasoning}
867+
onAdapterChange={handleAdapterChange}
801868
onModeChange={handleModeChange}
802869
onModelChange={handleModelChange}
803870
onReasoningChange={handleReasoningChange}

0 commit comments

Comments
 (0)