Skip to content

Model/provider not updated when switching agents #2846

@dpshde

Description

@dpshde

Bug

When switching agents via /forge, /muse, /sage, /agent, or /agent-{id}, the model and provider are not updated to reflect the new agent's configured model/provider. This affects analytics tracking and user feedback, even though the prompt display and actual LLM calls work correctly.

Root Cause

on_agent_change() at crates/forge_main/src/ui.rs:186-209 only calls set_active_agent() (which stores the agent ID). It does not:

  1. Resolve the new agent's model via get_agent_model()
  2. Call update_model() to update the analytics tracker
  3. Display the model/provider change to the user

Current code

async fn on_agent_change(&mut self, agent_id: AgentId) -> Result<()> {
    let agent = self.api.get_agents().await?
        .iter().find(|agent| agent.id == agent_id)
        .cloned()
        .ok_or(anyhow::anyhow!("Undefined agent: {agent_id}"))?;

    // Only sets the active agent ID — does NOT update model/provider
    self.api.set_active_agent(agent.id.clone()).await?;

    let name = agent.id.as_str().to_case(Case::UpperSnake).bold();
    let title = format!("∙ {}", agent.title.as_deref().unwrap_or(MISSING_AGENT_TITLE)).dimmed();
    self.writeln_title(TitleFormat::action(format!("{name} {title}")))?;
    Ok(())
}

Comparison: How model IS updated elsewhere

When the user runs /model (crates/forge_main/src/ui.rs:2749-2753):

self.api.set_default_model(model.clone()).await?;
self.update_model(Some(model.clone()));
self.writeln_title(TitleFormat::action(format!("Switched to model: {model}")))?;

When init_state() runs (crates/forge_main/src/ui.rs:2957-3008):

let mut operating_model = self.get_agent_model(active_agent.clone()).await;
self.update_model(operating_model);

Proposed Solution

Update on_agent_change() to resolve and propagate the agent's model after switching:

async fn on_agent_change(&mut self, agent_id: AgentId) -> Result<()> {
    let agent = self
        .api
        .get_agents()
        .await?
        .iter()
        .find(|agent| agent.id == agent_id)
        .cloned()
        .ok_or(anyhow::anyhow!("Undefined agent: {agent_id}"))?;

    self.api.set_active_agent(agent.id.clone()).await?;

    // Update model tracking to reflect the new agent's model
    let model = self.get_agent_model(Some(agent.id.clone())).await;
    self.update_model(model.clone());

    let name = agent.id.as_str().to_case(Case::UpperSnake).bold();
    let title = format!(
        "∙ {}",
        agent.title.as_deref().unwrap_or(MISSING_AGENT_TITLE)
    )
    .dimmed();

    // Show model info if agent uses a specific model
    let model_info = model
        .map(|m| format!(" ∙ model: {m}").dimmed().to_string())
        .unwrap_or_default();

    self.writeln_title(TitleFormat::action(format!("{name} {title}{model_info}")))?;
    Ok(())
}

Affected Call Sites

All callers of on_agent_change() are affected:

Command File Line
/forge crates/forge_main/src/ui.rs 1874
/muse crates/forge_main/src/ui.rs 1877
/sage crates/forge_main/src/ui.rs 1880
/agent (interactive selection) crates/forge_main/src/ui.rs 1994
/agent-{id} (direct switch) crates/forge_main/src/ui.rs 2017

Impact

  • Analytics/tracking: The model sent to the tracker is stale after agent switch
  • User feedback: No model/provider info shown when switching to an agent with a different model
  • Chat flow: NOT affected — ForgeApp::chat() independently resolves the agent's model/provider via AgentProviderResolver, so LLM calls use the correct model

Why This Was Missed

The built-in agents (forge, muse, sage) don't specify their own model or provider — they inherit session defaults. The bug only manifests with custom agents (in ~/.forge/agents/*.md or .forge/agents/*.md) that define their own model/provider. The AgentDefinition schema supports optional model and provider fields (crates/forge_repo/src/agent_definition.rs:34-38), but no built-in agents use them.

Additional Notes

  • The prompt display is correct — it independently resolves the model per-agent on every call via get_agent_model(). This makes the bug harder to notice visually.
  • Consider also checking if /config reflects the agent's model correctly — on_show_config() at crates/forge_main/src/ui.rs:1317-1384 uses get_agent_model(None) which returns the default model, not the agent-specific one.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions