Skip to content

Pick each bot's Claude model - #18

Merged
httpsphl merged 6 commits into
mainfrom
feat/bot-models
Sep 29, 2026
Merged

httpsphl merged 6 commits into
mainfrom
feat/bot-models

Conversation

@httpsphl

Copy link
Copy Markdown
Owner

The owner picks which Claude model each bot runs on. Stacked on #17.

Until now no bot passed --model, so every bot ran on the account's default (Opus 5.5 on a Max plan) whatever the work. Claude Code does not switch models by task on its own.

What changes

  • Model picker in the composer, next to Send: Plan default, Fable, Opus, Sonnet and Haiku, each described with the bot's name ("Scout is fast and capable, good for most work"). The menu's footer says stronger models use up the plan's limit faster. The new-bot and edit-bot dialogs have the same choice.
  • The model in use, by name. system/init reports the model id; the daemon keeps it in Bot.modelInUse, so the picker shows "Opus 5.5" for the plan's default and "Haiku 4.5" once a chosen model is confirmed.
  • Restart on a new model, like a new mode: once nothing is in progress, with --resume (a resumed session takes the --model it is started with). The relaunch code now serves both, and plan exit now follows the mode the process started with, so a pending model change no longer hides an approved plan.
  • A model the plan lacks fails the turn with model_not_found; it is now its own notice (model_unavailable), worded by the app in the owner's language, and the bot keeps running.
  • Storage: migration 0005 (bots.model, bots.model_in_use); protocol: bots.setModel, bots.create {model?}, Bot.model, Bot.modelInUse.

Spec: 7.4, 8.1, 8.2, 11.2, 12, 15.1 and a section 19 row.

Tested

  • cargo fmt, clippy -D warnings, cargo test (new bot_models tests: no flag by default, restart idle and busy bots, the reported model, the notice; and a plan approved while a model change waits) and pnpm check (picker, the plan default named, busy note, notice, new bot on a chosen model).
  • With Claude Code 2.1.284 on a Max plan: without --model, system/init says claude-opus-5-5; an unknown model starts the process and each turn ends with assistant.error: "model_not_found"; /model haiku as a message switches models mid-session (not used: the daemon restarts instead, as for modes).
  • A live crew ran its chief on --model sonnet and a second bot on --model haiku (see A chief for every crew, who suggests new bots #20).

A bot's avatar is now just the mascot in its color, with a thin edge
(dark on the light theme, light on the dark one) so light colors such as
the palette's beige keep their shape. Botloft's own white mascot, in the
title bar and on the daemon's messages, keeps the black square of the
app icon: without it, it would vanish on the light theme.
The whole window is now drawn at 125% (the webview's zoom), so text,
spacing, icons and avatars grow together; at 100% the app looked tiny on
a monitor without Windows scaling. A Size menu in the title bar picks
100%, 110%, 125% or 150%, Ctrl+= and Ctrl+- step through them and Ctrl+0
goes back to the default. The choice is remembered and applied before
the first paint, so the window never flashes small.
Like the account corner of a chat app: the owner's name (the Windows
account's display name), their Claude plan and a menu with Usage,
Settings, Language, What's new and Help. Usage shows each window of the
plan the bots have used and when it resets; Settings holds the theme,
size and language, and the versions. Theme, size and language leave the
title bar, which keeps only the language on the setup screens that have
no sidebar. The sidebar now shows from the welcome screen on.

The daemon reads the owner's display name once (GetUserNameExW) and the
email, plan and organization from `claude auth status`, and sends them
in `system.status.account`.
Buttons, fields and menu rows get 8 px corners, cards and menus 12 px,
the chat's bubbles, cards and dialogs 16 px. Menus and dialogs float on
a light themed shadow. The chat is a centered column with the day as a
pill, and the open conversation in the sidebar is an inset rounded
block instead of a full-width bar. Menus share one popover look and one
hook that closes them.
Like Claude Code's mode picker, below the chat: Auto, Manual, Accept
edits and Plan, each described by what the bot does ("Scout decides
what needs your OK"), and Bypass permissions behind a confirmation that
says the bot is then not held to its folder. A bot that asks nothing is
marked in red in the picker and its header.

The daemon stores the mode (migration 0004) and starts the bot with it.
Claude Code reads --permission-mode only at start, so a new mode
restarts the process with --resume once nothing is in progress, and
never cuts a turn short. When an approved plan moves the bot out of plan
mode, the system/init of the next turn says so and the stored mode
follows.

A bot in plan mode asks to go ahead with ExitPlanMode: the chat shows
the whole plan (kept up to 32 KB) with Approve plan and Keep planning,
which sends back what should change.

Spec: 7.4, 10.1, 11.2, 12, 13 (what bypassing means on Windows), 15.1,
15.3 and two rows in section 19 to check with the real Claude Code.
Bots ran on whatever the account's default is. The owner now picks
Fable, Opus, Sonnet, Haiku or the plan's default per bot, in the chat
and when creating or editing a bot. Claude Code only reads --model at
start, so a new model restarts the bot with --resume once nothing is in
progress, like a new permission mode (the relaunch code now serves both).

system/init names the model in use; the daemon keeps it so the app can
say which model the plan's default is. A model the account cannot use
fails the turn with model_not_found, now told as its own notice.

Plan exit now follows the mode the process started with, so a pending
model change no longer hides an approved plan.
@httpsphl
httpsphl changed the base branch from feat/permission-modes to main September 29, 2026 04:25
@httpsphl
httpsphl enabled auto-merge September 29, 2026 04:25
@httpsphl
httpsphl merged commit 94fc3af into main Sep 29, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant