mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-24 21:41:08 +02:00
feat(x): one model-selection experience — unified settings, provider lifecycle, split-view picker
Task slots (config + runtime): - taskModels gains backgroundTask and subagent; getBackgroundTaskAgentModel resolves its own override instead of mirroring live-notes (migration materializes the old mirror rule so effective models are unchanged) - spawn-agent precedence: explicit per-spawn args > configured subagent override > parent turn's model - repo.setProvider rejects auth-derived flavors (rowboat/codex) and merges over existing entries so key rotation keeps contextLength/reasoningEffort Models settings, rebuilt: - ModelSelectionSection: ONE Assistant model picker (with an Unavailable badge when the saved model drops off its provider's live list) + all seven tasks defaulting to "Same as Assistant" with resolve subtext and a clear-override link. Replaces both the signed-in and BYOK screens; the "Auto (recommended)" sentinel and both hardcoded preferredDefaults maps are gone — connect-time resolution uses backend recommendations - ProvidersSection replaces the old ModelSettings card grid: status cards (Connected · N models / error + Retry), an add-provider dialog (choose → creds → loading → first-vs-more result states → error with manual-model fallback), and a manage dialog (masked key + Replace, endpoint, Refresh models, Used-by list, disconnect with consequences spelled out). Rowboat and ChatGPT are cards in the same list - connecting a provider seeds the assistant ONLY when none is configured — never replaces a saved choice - models:getConfig IPC serves selections + credential-free provider metadata; the probe machinery (use-provider-models, liveCredentials) is deleted — the add flow validates credentials click-driven Onboarding: - screen 2 is the same ProvidersSection (onboarding variant) for BOTH paths: "Add more providers" after sign-in, "Connect a model provider" otherwise; Continue gates on an assistant model existing. The tile grid, per-provider key forms, and ~350 lines of duplicate state are deleted; one step sequence replaces the two path-dependent indicators Picker: - split-view browse (Popover + cmdk Command): providers left with counts and error dots, selected provider's models right, ←/→ switches provider, ↑/↓ navigates, typing collapses to a flat cross-provider list that matches provider NAMES as well as model ids (searching "rowboat" now works). Scoped/static pickers stay flat; public API unchanged Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
f39daa9f5d
commit
c695ddf1c1
24 changed files with 1599 additions and 1917 deletions
|
|
@ -656,7 +656,7 @@ const ipcSchemas = {
|
|||
}).nullable(),
|
||||
res: z.object({
|
||||
providers: z.array(z.object({
|
||||
// Provider INSTANCE id — what ModelRef.provider / defaultSelection /
|
||||
// Provider INSTANCE id — what ModelRef.provider / assistantModel /
|
||||
// refreshProvider reference. One instance per flavor today, so it
|
||||
// always equals the flavor key; kept distinct so a future
|
||||
// multi-instance setup ("openai-work") is additive.
|
||||
|
|
@ -748,6 +748,33 @@ const ipcSchemas = {
|
|||
success: z.literal(true),
|
||||
}),
|
||||
},
|
||||
// Current model selections plus credential-FREE provider metadata (the
|
||||
// renderer never needs keys to render pickers or provider cards). Null
|
||||
// assistantModel = not configured yet.
|
||||
'models:getConfig': {
|
||||
req: z.null(),
|
||||
res: z.object({
|
||||
// Configured BYOK provider entries, secrets stripped: enough to
|
||||
// render manage/edit surfaces (masked key indicator, endpoint).
|
||||
providers: z.array(z.object({
|
||||
id: z.string(),
|
||||
flavor: z.string(),
|
||||
baseURL: z.string().optional(),
|
||||
hasApiKey: z.boolean(),
|
||||
})),
|
||||
assistantModel: ModelRef.nullable(),
|
||||
taskModels: z.object({
|
||||
knowledgeGraph: ModelRef.nullable(),
|
||||
meetingNotes: ModelRef.nullable(),
|
||||
liveNoteAgent: ModelRef.nullable(),
|
||||
autoPermissionDecision: ModelRef.nullable(),
|
||||
chatTitle: ModelRef.nullable(),
|
||||
backgroundTask: ModelRef.nullable(),
|
||||
subagent: ModelRef.nullable(),
|
||||
}),
|
||||
deferBackgroundTasks: z.boolean(),
|
||||
}),
|
||||
},
|
||||
// Partial merge of model selections into models.json. Omitted keys are
|
||||
// untouched; null clears a key (a cleared task override inherits the
|
||||
// assistant model again). taskModels merges per-key.
|
||||
|
|
@ -760,6 +787,8 @@ const ipcSchemas = {
|
|||
liveNoteAgent: ModelRef.nullable().optional(),
|
||||
autoPermissionDecision: ModelRef.nullable().optional(),
|
||||
chatTitle: ModelRef.nullable().optional(),
|
||||
backgroundTask: ModelRef.nullable().optional(),
|
||||
subagent: ModelRef.nullable().optional(),
|
||||
}).optional(),
|
||||
deferBackgroundTasks: z.boolean().nullable().optional(),
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -41,13 +41,17 @@ export const ModelRef = z.object({
|
|||
model: z.string(),
|
||||
});
|
||||
|
||||
// The per-task model override slots. Absence = inherit the assistant model.
|
||||
// The per-task model override slots. Absence = inherit the assistant model
|
||||
// (except `subagent`, whose default is the PARENT turn's model — which is
|
||||
// the assistant for a top-level chat).
|
||||
export const TaskModels = z.object({
|
||||
knowledgeGraph: ModelRef.optional(),
|
||||
meetingNotes: ModelRef.optional(),
|
||||
liveNoteAgent: ModelRef.optional(),
|
||||
autoPermissionDecision: ModelRef.optional(),
|
||||
chatTitle: ModelRef.optional(),
|
||||
backgroundTask: ModelRef.optional(),
|
||||
subagent: ModelRef.optional(),
|
||||
});
|
||||
export type TaskModelKey = keyof z.infer<typeof TaskModels>;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue