Skip to content

Centralize model-change TUI notification in the main loop#2044

Open
dgageot wants to merge 1 commit intodocker:mainfrom
dgageot:board/each-time-i-have-a-feature-that-enables-28a6003f
Open

Centralize model-change TUI notification in the main loop#2044
dgageot wants to merge 1 commit intodocker:mainfrom
dgageot:board/each-time-i-have-a-feature-that-enables-28a6003f

Conversation

@dgageot
Copy link
Member

@dgageot dgageot commented Mar 10, 2026

Problem

Every feature that switches models (routing, alloy, fallback, model picker, per-tool overrides…) had to manually emit an AgentInfo event to update the TUI sidebar. This was scattered across 6+ call sites and easy to forget when adding new features.

Solution

Add a change-detection mechanism in RunStream: a lastEmittedModelID variable and emitModelInfo closure that automatically emits AgentInfo only when the effective model actually changes. Called before and after each LLM call, it covers all model-switching features uniformly.

Changes

  • loop.go: Replace 3 scattered manual AgentInfo emissions with emitModelInfo calls
  • model_picker.go: Remove AgentInfo emission from tool handler; rename setModelAndEmitInfosetCurrentAgentModel
  • agent_delegation.go: Use getEffectiveModelID instead of getAgentModelID so agent-switch events reflect active fallback cooldowns

Any future model-switching feature no longer needs to worry about TUI notification — the loop handles it automatically.

Add a change-detection mechanism (lastEmittedModelID + emitModelInfo
closure) in RunStream that automatically emits AgentInfo only when the
effective model actually changes. This is checked before and after each
LLM call, covering per-tool overrides, fallback, model picker, cooldowns,
and any future model-switching feature — without each one having to
remember to notify the TUI.

- loop.go: replace 3 scattered manual AgentInfo emissions with
  emitModelInfo calls driven by the closure
- model_picker.go: remove AgentInfo emission from the tool handler;
  rename setModelAndEmitInfo to setCurrentAgentModel (no longer emits)
- agent_delegation.go: use getEffectiveModelID instead of getAgentModelID
  so agent-switch events reflect active fallback cooldowns

Assisted-By: docker-agent
@dgageot dgageot requested a review from a team as a code owner March 10, 2026 17:33
Copy link

@docker-agent docker-agent bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Assessment: 🟡 NEEDS ATTENTION

The PR successfully centralizes model-change TUI notification, removing scattered AgentInfo emissions and introducing a clean change-detection mechanism. However, there is a medium-severity issue with TUI updates when agents are idle.

Key Finding

1 Medium Severity Issue Found: The TUI notification system assumes the main loop is actively running. When the change_model tool is called and the agent becomes idle (e.g., conversation ends or agent is waiting), the TUI won't be notified of the model change until the next user message triggers a new loop iteration.

Recommendation

Consider emitting an AgentInfo event immediately after SetAgentModel succeeds in model_picker.go, or ensure the main loop processes the model change before the agent enters an idle state.

}

return r.setModelAndEmitInfo(ctx, params.Model, events)
return r.setCurrentAgentModel(ctx, params.Model)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM SEVERITY: TUI not notified of model change when agent is idle

The removal of the AgentInfo emission relies on the main loop's emitModelInfo closure to detect and notify the TUI of model changes. However, emitModelInfo is only called at specific points:

  • At loop initialization (line 114 in loop.go)
  • Before each model call (line 264 in loop.go)
  • After each model execution (line 347 in loop.go)

If the agent is idle after change_model completes (e.g., the conversation ended or the agent is waiting for the next user message), the main loop won't iterate through these checkpoints. The TUI will continue showing the old model until the next user message triggers a new loop iteration.

Impact: Users won't see the model change reflected in the sidebar immediately after calling change_model, potentially causing confusion about which model is active.

Suggestion: Consider either:

  1. Keeping a single AgentInfo emission here after successful SetAgentModel
  2. Ensuring the main loop processes at least one iteration after tool calls complete
  3. Adding a post-tool-processing phase that explicitly checks for model changes

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