feat(app): model backend picker on the spawn screen

Fetch the registered model backends (/claude/models) alongside the
fleet poll and offer the enabled ones in a Model select on the spawn
form, defaulting to the Claude subscription — the mobile counterpart of
the web dashboard's per-spawn dropdown. The select is hidden when no
backends are registered, and Select itself now takes value/label pairs
as well as bare strings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01731mKtVzsfeT4Vi3TvkR48
This commit is contained in:
Claude
2026-08-13 13:08:44 +00:00
parent dbf8f46df8
commit 6319bf0fd4
3 changed files with 61 additions and 12 deletions
+21 -2
View File
@@ -18,14 +18,24 @@ import { useAppState } from "../state/AppState";
export function SpawnScreen() {
const { colors } = useTheme();
const insets = useSafeAreaInsets();
const { go, projects, spawn } = useAppState();
const { go, projects, models, spawn } = useAppState();
const projectIds = projects.map((p) => p.id);
const [project, setProject] = useState("");
const [task, setTask] = useState("");
const [model, setModel] = useState("");
const [busy, setBusy] = useState(false);
const [error, setError] = useState<string | null>(null);
// Same choice the web dashboard's spawn form offers: the Claude subscription by
// default, plus every enabled registered model backend.
const modelOptions = [
{ value: "", label: "Claude (subscription)" },
...models
.filter((m) => m.enabled)
.map((m) => ({ value: String(m.id), label: `${m.name} (${m.model})` })),
];
// Default to the first project once they load (or if the current pick vanished).
useEffect(() => {
if (projectIds.length > 0 && !projectIds.includes(project)) {
@@ -45,7 +55,7 @@ export function SpawnScreen() {
setError(null);
setBusy(true);
try {
await spawn(project, task.trim());
await spawn(project, task.trim(), model ? Number(model) : null);
go("fleet");
} catch (e) {
setError(e instanceof Error ? e.message : "Couldnt spawn the agent.");
@@ -87,6 +97,15 @@ export function SpawnScreen() {
</View>
)}
{modelOptions.length > 1 ? (
<Select
label="Model"
options={modelOptions}
value={model}
onChange={setModel}
/>
) : null}
<View>
<Text style={[text.label, { color: colors.textHeading, marginBottom: 6 }]}>
Task