feat(desktop): choose which agent config a chat runs as

RE
4h ago
+1613-113

Links

  • Depends on #9506 (host side). This branch contains its commit; the two commits on top are this PR.
  • Issue: #9505

Summary

  • Chats opened from the app now run as the agent config the user picked, so two Claude configs with their own CLAUDE_CONFIG_DIR (e.g. a work account and a personal account) chat on their own accounts.
  • The chat model picker shows an Account group in an agent's tab when several configs chat as that agent; picking another account offers fork or fresh, as switching provider does.
  • Every agent config can set an icon (not only custom ones), and the picker shows each account's icon, so the two Claudes are told apart.

Why / Context

#9506 makes the host honour agentConfigId for chat sessions, but the renderer never sent it: a chat pane stored only the preset (agent: { id: "claude" }). A chat opened from the presets bar, the "+" menu or "new chat" therefore ran on the default account, and a custom agent that runs claude/codex could not open as a chat at all.

How It Works

  • ChatPaneData.agent gains an optional configId. The launcher stores it, createSession sends it as agentConfigId, and new chats, forks and agent switches keep it. "New chat" reopens the last chat's config (the existing chatLastAgent key now holds a config id; a preset id from older builds still resolves).
  • A pane without a saved config (opened before this change, or adopted from the CLI) takes it from the host's session row (session.agentConfigId), and shows no current account when neither knows it, instead of guessing.
  • resolveChatLaunchPresetId (custom config running claude/codex chats as that agent) moves from host-service to @superset/shared/agent-models, so host and renderer use one rule.
  • A conversation cannot move between config dirs (each account keeps its own session files), so switching account mid-chat uses the existing fork-or-fresh dialog with account wording.
  • Settings → Agents shows the icon picker for preset agents too; "Default" keeps the preset's icon.

Manual QA Checklist

Done in a packaged local build (2.0.6 + #9506 + this PR, macOS arm64):

  • Chat model picker in a Claude chat shows the Account group with all configs that chat as Claude.
  • A chat created via the CLI before this change shows its real account as current (taken from the host session row).
  • Settings → Agents shows the icon picker for a preset (Claude) agent, with "Default" and the Claude icon.

Not yet exercised by hand (covered by unit tests only):

  • Picking the other account mid-chat → fork / fresh dialog → new chat runs on that account.
  • "+" → Chat / presets bar / "new chat" open on the chosen config.
  • Uploaded icon on a preset agent shows in the picker tabs and Account rows.

Testing

  • bun run typecheck in apps/desktop and packages/host-service, also against the 2.0.6 release base
  • bun run lint (repo), bun run check:i18n (new dialog string translated in all locales)
  • bun test: new chatPresetIdForConfig, chatAgentChoices, newChatConfig, resolveChatLaunchPresetId tests; related desktop suites (59 tests), host-service agents (72), shared agent-models (90), i18n (169)

Compatibility

  • agentConfigId is optional on a non-strict schema: an older host ignores it and the chat runs as before.
  • Older panes without configId behave as before; resume still inherits the config on the host.

Known Limitations

  • The new-workspace screen still resolves a custom config's preset with resolveAgentLaunchPresetId, so a custom agent running claude does not start as a chat from there. That value also feeds automations, tasks and model/mode validation; left for a focused follow-up.

Summary by cubic

Chats opened from the app now run as the agent config the user picked, so two Claude configs with their own CLAUDE_CONFIG_DIR stay on their own accounts, and a custom agent that runs the claude/codex CLI can open as a chat.

  • Chat panes store an optional configId and send it as agentConfigId when creating sessions; new chats, forks, agent switches, and "new chat" keep it, and fork-to-worktree carries it.
  • The host persists the config on each session (new migration 0004_session_agent_config) and launches the chat with that config's env, so resumed, forked, rewound, and recovered chats reopen on the same account. If the config is gone or now runs another CLI, the chat falls back to the default account and shows a notice above the composer; that fallback passes an explicit agentConfigId: null so it inherits no config from another chat on the same agent session.
  • The model picker shows an Account group when several configs chat as one agent; switching account uses the existing fork-or-fresh flow since a session can't move between config dirs.
  • Preset agents can set a custom icon in Settings, and the picker shows each account's icon to tell them apart.

Compatibility

  • agentConfigId is optional, so older hosts ignore it and chats behave as before.
  • Requires the new chat-runtime migration (0004_session_agent_config) to persist the config.

Summary by CodeRabbit

  • New Features
    • Choose between configured accounts for an agent when starting a chat, and see which account is active.
    • New chats can use the account from your most recent chat; resumed and forked conversations retain their account when available.
    • Custom agents configured with Claude or Codex commands can launch chats with the corresponding chat experience.
    • Agent icons can be customized for built-in agents as well as custom agents.
  • Updates
    • If an account can’t be changed during an active conversation, you’ll be prompted to start a new chat or fork it.
    • If an agent or account is no longer available, chats use the default account and display a notice.