For OpenHands, the conversation composer should stay an LLM Profile picker before conversation start and during the conversation. Agent Profiles remain the agent configuration behind the scene and in Settings. Selecting an LLM Profile should always make it the LLM for the conversation currently shown in Canvas and save it as the user’s preference for future conversations. A truly single-user installation may additionally mirror that choice into only its hidden well-known default Agent Profile.
Current source snapshots: OpenHands/agent-canvas @ bc77e113 · OpenHands/OpenHands @ 1747b395 · decision record, June 2026
1Recommendation in one screen
Choose Refined Option C—with Option 1B as the canonical model. OpenHands always shows an LLM Profile picker. Selecting an LLM Profile always makes it the LLM for the conversation currently shown in Canvas and saves it as the user’s preference for future conversations. Explicitly single-user backends may also update only the hidden well-known default Agent Profile. Named Agent Profiles never change from a composer click. ACP keeps its provider model picker.
Same action invariant. Selecting Claude always means “use Claude for this conversation and remember Claude for future conversations.” If the conversation has not started, Canvas uses Claude when the first prompt starts it. If it is running, Canvas applies Claude immediately. Those are two execution paths for one behavior—not two picker meanings.
Visible controlLLM
Stable before conversation start and during the conversation.
Agent configurationprofile
Selected behind the scene; edited in Settings → Agent.
Future choiceuser
Preferred LLM belongs to a person, not a shared org profile.
Local refinementdefault
Only the hidden default may mirror the choice on single-user servers.
Constraints behind the recommendation: the current conversation-start request sends agent_profile_id and resolves its saved LLM reference agent-server-conversation-service.api.ts:368–410; current Cloud Agent Profiles are organization-owned while activation writes only the calling member’s pointer agent_profiles.py:1–24 and :386–427.
Explicit precedence
Current composer selection The LLM the person just selected before conversation start or during the conversation.
Per-user preferred LLM The last interactive choice saved for future Canvas conversations.
Active Agent Profile fallback The profile’s stable llm_profile_ref, used when no user preference applies.
System default The final seed/fallback when neither user nor Agent Profile provides a resolvable LLM.
The existing Cloud resolver already demonstrates the lower-level fallback pattern for Agent Profiles: explicit conversation-start profile → member active profile → org default → composed settings saas_settings_store.py:190–235. The proposed LLM preference layer would sit above the resolved Agent Profile’s LLM when interactive Canvas conversations start.
2Current UX: the control changes identity
Today the picker is selected by conversation phase. Before conversation start—or while a blank conversation has not substantively started—Canvas prefers Agent Profiles. During an OpenHands conversation, it uses LLM Profiles. ACP uses the model picker.
Verified in the pure picker matrix: !hasConversation || !hasStartedConversation returns agent-profile when profiles exist; a started conversation returns model for ACP and llm-profile otherwise resolve-picker-kind.ts:27–46. The renderer maps those three values to three controls chat-input-actions.tsx:411–468.
The UX delta is deliberately small: keep the visible LLM choice stable; compose it with the Agent Profile when the conversation starts.
Current conversation-start contract: Canvas sends only agent_profile_id on Cloud agent-server-conversation-service.api.ts:378–410. Current Cloud resolution replaces composed settings with the Agent Profile’s resolved settings at conversation start saas_settings_store.py:470–500. The LLM conversation-start override shown below is a proposal, not current behavior.
3Three concepts, three owners
Agent configuration
Agent Profile
A reusable agent definition: kind, tools/skills/MCP choices, limits, and an OpenHands llm_profile_ref fallback. Named profiles should be stable because other conversation starts and automations can refer to them.
Current Cloud summaries expose agent_kind, revision, llm_profile_ref, and MCP refs agent_profiles.py:75–99.
Personal future default · proposed
Preferred LLM Profile
The last interactive LLM choice for one user. It predicts the blank composer and overrides the Agent Profile fallback only when interactive conversations start.
This layer does not exist cleanly today. Historically, the equivalent behavior came from materialized member settings, but the visible active marker was org-owned; see the former Cloud flow.
Runtime state
Conversation LLM Profile
The profile used by one running conversation. Switching it should remain immediate and conversation-scoped.
Current Canvas passes the conversation id into switchProfile, updates conversation queries/metadata, and does not save an Agent Profile use-switch-llm-profile.ts:51–103.
Separate agent family
ACP model
ACP does not use OpenHands LLM Profiles. Its provider/session model selector remains the composer control in ACP contexts.
Behavior. The composer always shows LLM Profiles. The selected LLM overrides the Agent Profile for that conversation start; later selections switch only the running conversation. The next blank composer falls back to the Agent Profile again.
Changes. Add an optional LLM profile name to the start contract; resolve the Agent Profile, replace only its LLM in memory, never save it.
Good. Stable named profiles, no hidden writes, safe for shared Cloud profiles and automations.
Cost. It gives the same picker two persistence meanings before and during a conversation. That is the inconsistency this design should remove.
Option 1B · canonical multi-user model
One persistent selection, in both phases
Behavior. Every successful composer selection makes that LLM effective for the conversation currently shown in Canvas and saves it as the user’s preferred LLM for future conversations. A not-yet-started conversation uses it on the first prompt; a running conversation applies it immediately.
Changes. Persist preferred_llm_profile per user/member, show it before conversation start, send it when an interactive conversation starts, and update it after every successful in-conversation switch.
Good. Restores last-used behavior without modifying Agent Profiles. Teammates can share one agent profile and prefer different LLMs.
Cost. Adds one explicit state layer and makes the Agent Profile LLM a fallback rather than the final interactive choice.
Option 2 · not recommended generally
Mutate the active Agent Profile
Behavior. Every composer LLM selection rewrites the active Agent Profile’s llm_profile_ref; future conversation starts naturally inherit it.
Changes. Prefer a narrow revision-aware patch. A whole-profile save must fetch and preserve every field, then coordinate profile persistence with the live runtime switch.
Good. One saved object appears to contain the effective configuration.
Cost. A chat control silently edits Settings. Named/shared profiles drift, revisions churn, automations can change behavior, and runtime-switch/profile-save partial failures become visible.
Refined Option C · recommended rollout
Option 1B everywhere; local default mirroring when safe
Multi-user. Use Option 1B; never mutate a shared Agent Profile.
Single-user. Use the same preference/override behavior, and optionally mirror the selection into only the hidden well-known default Agent Profile.
Named profiles. Stay stable on every backend. A temporary model experiment must not rewrite “Deep review” or affect an automation.
Boundary. Branch on an explicit tenancy/capability contract—not Docker, hostname, localhost, or remote-vs-local guesses.
Automation or API conversation start by Agent Profile id
Caller-selected reusable profile
Ignore interactive preference unless the API explicitly supplies an LLM override.
No implicit mutation from an interactive user’s preference.
Current Canvas exposes only the coarse transport kinds local | cloudbackend-registry/types.ts:1–13. Current local Agent Profiles lazily seed a well-known default profile and active pointer agent_profiles_router.py:219–259. Current Cloud stores the collection with the organization and activation with the member agent_profiles.py:1–24. The proposed capability should describe ownership explicitly, for example profile_scope: "installation" | "organization".
Why “Docker” is not a capability. A container can serve one human or an office. A VM can be personal or shared. localhost can front a multi-user reverse proxy. The mutation rule must follow declared ownership semantics, not deployment folklore.
6Target state ownership and mutation rules
State
Owner
Written by
Consumed by
Must not be changed by
LLM Profile definitions
Installation or organization
LLM Profile Settings
Conversation-start resolver and in-conversation switch
A normal composer selection
Agent Profile definition
Installation or organization
Settings → Agent; explicit profile APIs
Interactive, automation, and API conversation starts
Composer selection—except optional hidden-default mirror on declared single-user servers
Persist the future interactive choice according to advertised scope.Canonical path: preferred LLM. Optional compatibility path: narrow, revision-aware patch of only the hidden local default Agent Profile.
4
Pass the selected LLM whenever an interactive conversation starts.Resolve Agent Profile normally, then replace only its LLM in memory. Preserve profile id/revision provenance.
Serialize selection and Send.Disable or queue Send while future-state persistence is pending so a quick choose-then-send cannot start with the old LLM. Surface split failures instead of pretending runtime and preference are identical.
Migration posture. Build Option 1B first as the durable model. Add local default-profile mirroring behind the explicit installation-scope capability. If local backends gain real identities later, remove the mirror without changing the composer UX or conversation-start contract.
7Unresolved decisions before implementation
Decision
Recommended default
Why it remains explicit
Capability name and source
Server-advertised ownership, such as profile_scope.
Current backend.kind describes transport, not who owns mutable state.
Preferred LLM storage
Backend-owned per-member field on Cloud; persisted app preference on identity-less installations.
It must roam correctly for Cloud users without becoming organization state.
Explicit named Agent Profile conversation start
Interactive Canvas preference overrides only the LLM; automation/API profile starts ignore it unless an override is explicitly supplied.
Keeps named profiles deterministic outside interactive UX.
Deleted or renamed preferred LLM
Clear stale preference, fall back to Agent Profile, and make the fallback visible.
A silent stale name would make the picker lie again.
Switch succeeds, preference save fails
Keep the runtime switch; report that the next-conversation preference did not persist and allow retry.
Rolling back a live model switch is more disruptive than acknowledging split state.
Local mirror lifetime
Compatibility optimization, not a second canonical architecture.
Prevents single-user convenience from hardening into a multi-user mutation rule.
Settings copy
Label Agent Profile’s field “Default LLM Profile” or “Fallback LLM Profile.”
The per-user interactive preference has higher precedence.
The need for explicit stale-reference and fallback handling is grounded in current Cloud resolution, which falls back rather than blocking conversation start when an active profile id is missing saas_settings_store.py:213–239. The rest of this section records product decisions, not existing behavior.
Decision summary. Preserve the user’s visible mental model—“I choose an LLM here”—while keeping the system’s richer agent configuration stable and reusable. One UI, explicit ownership underneath.