Skip to content

Commit 01d941c

Browse files
refactor(mobile): share agent configuration controls
Keep task creation, in-task changes, and terminal retries on the same adapter configuration policy. Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22
1 parent 7cd93a4 commit 01d941c

7 files changed

Lines changed: 430 additions & 369 deletions

File tree

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

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
DEFAULT_GATEWAY_MODEL,
1313
DEFAULT_REASONING_EFFORT,
1414
type ExecutionMode,
15-
getReasoningEffortOptions,
1615
isSupportedReasoningEffort,
1716
type SupportedReasoningEffort,
1817
serializeCloudPrompt,
@@ -61,6 +60,7 @@ import {
6160
import { useTaskSessionStore } from "@/features/tasks/stores/taskSessionStore";
6261
import { useTaskStore } from "@/features/tasks/stores/taskStore";
6362
import { confirmStopRun } from "@/features/tasks/utils/archiveGuard";
63+
import { buildCloudTaskRunConfig } from "@/features/tasks/utils/cloudTaskRunConfig";
6464
import { useScreenInsets } from "@/hooks/useScreenInsets";
6565
import {
6666
ANALYTICS_EVENTS,
@@ -341,18 +341,18 @@ export default function TaskDetailScreen() {
341341
)
342342
: text;
343343

344-
const supportsReasoning =
345-
getReasoningEffortOptions(composerAdapter, composerModel) !== null;
346344
const updatedTask = await getPostHogApiClient().runTaskInCloud(
347345
taskId,
348346
undefined,
349347
{
350348
resumeFromRunId: task.latest_run?.id,
351349
pendingUserMessage,
352-
adapter: composerAdapter,
353-
model: composerModel,
354-
reasoningLevel: supportsReasoning ? composerReasoning : undefined,
355-
initialPermissionMode: composerMode,
350+
...buildCloudTaskRunConfig({
351+
adapter: composerAdapter,
352+
mode: composerMode,
353+
model: composerModel,
354+
reasoning: composerReasoning,
355+
}),
356356
rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud,
357357
},
358358
);
@@ -616,13 +616,12 @@ export default function TaskDetailScreen() {
616616
undefined,
617617
{
618618
resumeFromRunId: task.latest_run?.id,
619-
adapter: composerAdapter,
620-
model: composerModel,
621-
reasoningLevel:
622-
getReasoningEffortOptions(composerAdapter, composerModel) !== null
623-
? composerReasoning
624-
: undefined,
625-
initialPermissionMode: composerMode,
619+
...buildCloudTaskRunConfig({
620+
adapter: composerAdapter,
621+
mode: composerMode,
622+
model: composerModel,
623+
reasoning: composerReasoning,
624+
}),
626625
rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud,
627626
},
628627
);

apps/mobile/src/app/task/index.tsx

Lines changed: 24 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@ import { Text } from "@components/text";
22
import {
33
DEFAULT_CLAUDE_EXECUTION_MODE,
44
getAvailableModesForAdapter,
5-
getDefaultExecutionModeForAdapter,
65
} from "@posthog/core/sessions/executionModes";
76
import { resolveCloudComposerModelChange } from "@posthog/core/task-detail/composerModelPolicy";
87
import {
98
type Adapter,
10-
DEFAULT_CODEX_MODEL,
119
DEFAULT_GATEWAY_MODEL,
1210
DEFAULT_REASONING_EFFORT,
1311
type ExecutionMode,
@@ -20,17 +18,10 @@ import { LinearGradient } from "expo-linear-gradient";
2018
import { Stack, useLocalSearchParams, useRouter } from "expo-router";
2119
import {
2220
ArrowUp,
23-
BrainIcon,
2421
CaretDown,
25-
Cpu,
2622
GithubLogo,
2723
MicrophoneIcon,
2824
PaperclipIcon,
29-
PauseIcon,
30-
PencilIcon,
31-
Robot,
32-
ShieldCheck,
33-
Sparkle,
3425
StopIcon,
3526
} from "phosphor-react-native";
3627
import { useCallback, useEffect, useState } from "react";
@@ -50,6 +41,7 @@ import { useVoiceRecording } from "@/features/chat";
5041
import { usePreferencesStore } from "@/features/preferences/stores/preferencesStore";
5142
import { GitHubConnectionPrompt } from "@/features/tasks/components/GitHubConnectionPrompt";
5243
import { GitHubLoadNotice } from "@/features/tasks/components/GitHubLoadNotice";
44+
import { AgentConfigControls } from "@/features/tasks/composer/AgentConfigControls";
5345
import { AttachmentSheet } from "@/features/tasks/composer/attachments/AttachmentSheet";
5446
import { AttachmentsBar } from "@/features/tasks/composer/attachments/AttachmentsBar";
5547
import { buildCloudPromptBlocks } from "@/features/tasks/composer/attachments/buildCloudPrompt";
@@ -60,14 +52,8 @@ import {
6052
} from "@/features/tasks/composer/attachments/pickers";
6153
import type { PendingAttachment } from "@/features/tasks/composer/attachments/types";
6254
import { DotBackground } from "@/features/tasks/composer/DotBackground";
63-
import {
64-
getComposerModelOptions,
65-
getConfigOptionLabel,
66-
getModelConfigOption,
67-
} from "@/features/tasks/composer/options";
68-
import { Pill } from "@/features/tasks/composer/Pill";
55+
import { getModelConfigOption } from "@/features/tasks/composer/options";
6956
import { RepositoryPickerInline } from "@/features/tasks/composer/RepositoryPickerInline";
70-
import { SelectSheet } from "@/features/tasks/composer/SelectSheet";
7157
import { useCloudTaskConfigOptions } from "@/features/tasks/hooks/useCloudTaskConfigOptions";
7258
import { useUserIntegrations } from "@/features/tasks/hooks/useUserIntegrations";
7359
import { useWarmTask } from "@/features/tasks/hooks/useWarmTask";
@@ -81,6 +67,7 @@ import type {
8167
CreateTaskOptions,
8268
RepositorySelection,
8369
} from "@/features/tasks/types";
70+
import { buildCloudTaskRunConfig } from "@/features/tasks/utils/cloudTaskRunConfig";
8471
import {
8572
findRepositoryOption,
8673
isRepositorySelectionComplete,
@@ -92,31 +79,12 @@ import { getPostHogApiClient } from "@/lib/posthogApiClient";
9279
import { toRgba, useThemeColors } from "@/lib/theme";
9380

9481
const log = logger.scope("task-create");
95-
const SWITCH_ADAPTER_VALUE = "__switch_adapter__";
9682
const SUGGESTIONS = [
9783
"Create or update my CLAUDE.md file",
9884
"Search for a TODO comment and fix it",
9985
"Recommend areas to improve our tests",
10086
] as const;
10187

102-
function modeIcon(mode: ExecutionMode, color: string, size = 14) {
103-
switch (mode) {
104-
case "plan":
105-
return <PauseIcon size={size} color={color} weight="bold" />;
106-
case "default":
107-
return <PencilIcon size={size} color={color} />;
108-
case "acceptEdits":
109-
return <ShieldCheck size={size} color={color} />;
110-
case "bypassPermissions":
111-
case "full-access":
112-
return <ShieldCheck size={size} color={color} weight="fill" />;
113-
case "read-only":
114-
return <PauseIcon size={size} color={color} />;
115-
case "auto":
116-
return <Sparkle size={size} color={color} weight="fill" />;
117-
}
118-
}
119-
12088
export default function NewTaskScreen() {
12189
const {
12290
prompt: initialPrompt,
@@ -135,9 +103,7 @@ export default function NewTaskScreen() {
135103
const [adapter, setAdapter] = useState<Adapter>("claude");
136104
const { configOptions, hasLiveConfig, isConfigReady } =
137105
useCloudTaskConfigOptions(adapter);
138-
const executionModes = getAvailableModesForAdapter(adapter);
139106
const modelConfigOption = getModelConfigOption(configOptions);
140-
const mobileModelOptions = getComposerModelOptions(modelConfigOption);
141107
const {
142108
error,
143109
hasGithubIntegration,
@@ -233,9 +199,6 @@ export default function NewTaskScreen() {
233199
}, [adapter, hasLiveConfig, model, modelConfigOption, reasoning]);
234200
const [creating, setCreating] = useState(false);
235201
const [repoSheetOpen, setRepoSheetOpen] = useState(false);
236-
const [modeSheetOpen, setModeSheetOpen] = useState(false);
237-
const [modelSheetOpen, setModelSheetOpen] = useState(false);
238-
const [reasoningSheetOpen, setReasoningSheetOpen] = useState(false);
239202
const [attachments, setAttachments] = useState<PendingAttachment[]>([]);
240203
const [attachmentSheetOpen, setAttachmentSheetOpen] = useState(false);
241204

@@ -378,15 +341,9 @@ export default function NewTaskScreen() {
378341
)
379342
: trimmedPrompt;
380343

381-
const supportsReasoning =
382-
getReasoningEffortOptions(adapter, model) !== null;
383-
384344
await client.runTaskInCloud(task.id, undefined, {
385345
pendingUserMessage,
386-
adapter,
387-
model,
388-
reasoningLevel: supportsReasoning ? reasoning : undefined,
389-
initialPermissionMode: mode,
346+
...buildCloudTaskRunConfig({ adapter, mode, model, reasoning }),
390347
autoPublish: usePreferencesStore.getState().autoPublishCloudRuns,
391348
rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud,
392349
...(signalReport
@@ -615,51 +572,27 @@ export default function NewTaskScreen() {
615572
paddingRight: 16,
616573
}}
617574
>
618-
<Pill
619-
icon={modeIcon(
620-
mode,
621-
mode === "plan"
622-
? themeColors.accent[11]
623-
: themeColors.gray[11],
624-
)}
625-
label={
626-
executionModes.find((option) => option.id === mode)
627-
?.name ?? mode
628-
}
629-
accent={mode === "plan"}
630-
onPress={() => setModeSheetOpen(true)}
575+
<AgentConfigControls
576+
adapter={adapter}
577+
mode={mode}
578+
model={model}
579+
reasoning={reasoning}
580+
configOptions={configOptions}
581+
onAdapterChange={setAdapter}
582+
onModeChange={(next) => {
583+
setMode(next);
584+
usePreferencesStore
585+
.getState()
586+
.setLastNewTaskMode(next);
587+
}}
588+
onModelChange={setModel}
589+
onReasoningChange={(next) => {
590+
setReasoning(next);
591+
usePreferencesStore
592+
.getState()
593+
.setLastUsedReasoningEffort(next);
594+
}}
631595
/>
632-
633-
<Pill
634-
icon={
635-
adapter === "codex" ? (
636-
<Cpu size={14} color={themeColors.gray[11]} />
637-
) : (
638-
<Robot size={14} color={themeColors.gray[11]} />
639-
)
640-
}
641-
label={
642-
getConfigOptionLabel(
643-
modelConfigOption.options,
644-
model,
645-
) ?? model
646-
}
647-
onPress={() => setModelSheetOpen(true)}
648-
/>
649-
650-
{showReasoningPill ? (
651-
<Pill
652-
icon={
653-
<BrainIcon size={14} color={themeColors.gray[11]} />
654-
}
655-
label={
656-
reasoningOptions.find(
657-
(option) => option.value === reasoning,
658-
)?.name ?? reasoning
659-
}
660-
onPress={() => setReasoningSheetOpen(true)}
661-
/>
662-
) : null}
663596
</ScrollView>
664597
{/* Right-edge fade hints that more pills exist when the row
665598
overflows. Non-interactive so taps fall through. */}
@@ -768,98 +701,6 @@ export default function NewTaskScreen() {
768701
</Animated.View>
769702
</View>
770703

771-
<SelectSheet
772-
open={modeSheetOpen}
773-
title="Execution mode"
774-
value={mode}
775-
onChange={(value) => {
776-
const next = value as ExecutionMode;
777-
setMode(next);
778-
usePreferencesStore.getState().setLastNewTaskMode(next);
779-
}}
780-
onClose={() => setModeSheetOpen(false)}
781-
options={executionModes.map((executionMode) => ({
782-
value: executionMode.id,
783-
label: executionMode.name,
784-
description: executionMode.description,
785-
icon: modeIcon(
786-
executionMode.id as ExecutionMode,
787-
executionMode.id === "plan"
788-
? themeColors.accent[11]
789-
: themeColors.gray[11],
790-
16,
791-
),
792-
}))}
793-
/>
794-
795-
<SelectSheet
796-
open={modelSheetOpen}
797-
title="Model"
798-
value={model}
799-
onChange={(value) => {
800-
if (value === SWITCH_ADAPTER_VALUE) {
801-
const nextAdapter: Adapter =
802-
adapter === "claude" ? "codex" : "claude";
803-
setAdapter(nextAdapter);
804-
setMode(getDefaultExecutionModeForAdapter(nextAdapter));
805-
setModel(
806-
nextAdapter === "codex"
807-
? DEFAULT_CODEX_MODEL
808-
: DEFAULT_GATEWAY_MODEL,
809-
);
810-
setReasoning(DEFAULT_REASONING_EFFORT);
811-
return;
812-
}
813-
const next = resolveCloudComposerModelChange({
814-
adapter,
815-
modelOption: modelConfigOption,
816-
requestedModel: value,
817-
reasoning,
818-
});
819-
setModel(next.model);
820-
setReasoning(next.reasoning);
821-
}}
822-
onClose={() => setModelSheetOpen(false)}
823-
options={[
824-
...mobileModelOptions.map((modelOption) => ({
825-
value: modelOption.value,
826-
label: modelOption.label,
827-
description: modelOption.description,
828-
disabled: modelOption.disabled,
829-
icon:
830-
adapter === "codex" ? (
831-
<Cpu size={16} color={themeColors.gray[11]} />
832-
) : (
833-
<Robot size={16} color={themeColors.gray[11]} />
834-
),
835-
})),
836-
{
837-
value: SWITCH_ADAPTER_VALUE,
838-
label: `Switch to ${adapter === "claude" ? "Codex" : "Claude Code"}`,
839-
description: "Change coding agent",
840-
disabled: false,
841-
icon: <Cpu size={16} color={themeColors.accent[11]} />,
842-
},
843-
]}
844-
/>
845-
846-
<SelectSheet
847-
open={reasoningSheetOpen}
848-
title="Reasoning"
849-
value={reasoning}
850-
onChange={(value) => {
851-
const next = value as SupportedReasoningEffort;
852-
setReasoning(next);
853-
usePreferencesStore.getState().setLastUsedReasoningEffort(next);
854-
}}
855-
onClose={() => setReasoningSheetOpen(false)}
856-
options={reasoningOptions.map((reasoningLevel) => ({
857-
value: reasoningLevel.value,
858-
label: reasoningLevel.name,
859-
icon: <BrainIcon size={16} color={themeColors.gray[11]} />,
860-
}))}
861-
/>
862-
863704
<AttachmentSheet
864705
open={attachmentSheetOpen}
865706
onClose={() => setAttachmentSheetOpen(false)}

0 commit comments

Comments
 (0)