-
Notifications
You must be signed in to change notification settings - Fork 23
Description
Summary
Allow users to configure a different model for each agent role (mayor, refinery, polecat) in town settings. Currently there is a single default_model that applies to all agents. Users should be able to, for example, run the mayor on a cheaper/faster model (Sonnet) while running the refinery on a more capable model (Opus) for thorough code review.
Motivation
Different roles have different requirements:
- Mayor: Conversational, needs to be fast and responsive. A lighter model is often fine.
- Refinery: Reviews code quality, correctness, and security. Benefits from the most capable model available.
- Polecat: Implements features and fixes. Benefits from strong coding models, but cost scales with the number of concurrent polecats.
A single default_model forces a trade-off between cost and quality across all roles. Per-role configuration lets users optimize each independently.
Design
Town Config Schema
// townConfig — extend existing config
{
default_model: string; // Existing — fallback for any role without a specific model
role_models?: { // New — per-role overrides
mayor?: string; // e.g. "anthropic/claude-sonnet-4.6"
refinery?: string; // e.g. "anthropic/claude-opus-4.6"
polecat?: string; // e.g. "anthropic/claude-sonnet-4.6"
};
}When dispatching an agent, the model is resolved as:
const model = townConfig.role_models?.[agent.role] ?? townConfig.default_model;This is backward compatible — existing towns without role_models continue using default_model for everything.
Settings UI
In the "Agent Defaults" section of town settings, replace the single model selector with a section showing:
Agent Models
Default Model [anthropic/claude-sonnet-4.6 ▾]
Override by role (optional):
Mayor [Use default ▾]
Refinery [anthropic/claude-opus-4.6 ▾]
Polecat [Use default ▾]
Each role dropdown includes a "Use default" option (which maps to undefined in the config, falling back to default_model). The dropdowns should use the model selector component from #1430 once that ships, or plain text inputs as a stopgap.
Dispatch Changes
buildKiloConfigContent() in container/src/agent-runner.ts currently receives a single model string. Extend StartAgentRequest to include the role-specific model:
interface StartAgentRequest {
// ... existing fields
model: string; // Already resolved per-role by the caller
}The resolution happens in TownDO before dispatch — scheduling.dispatchAgent or the reconciler's applyAction('dispatch_agent') resolves the model from role_models?.[role] ?? default_model and passes it to the container.
Mayor Model Change
If the mayor's model is changed while it's running, this ties into #1438 (mayor session restart on model change). The same mechanism applies — detect the change in updateTownConfig and restart the mayor session with the new model.
Acceptance Criteria
-
role_modelsfield added to town config schema (optional, backward compatible) - Settings UI shows per-role model overrides with "Use default" option
- Agent dispatch resolves model per-role with fallback to
default_model - Changing a role's model takes effect on the next agent dispatch for that role
- Mayor model change triggers session restart (fix(gastown): Changing default model in town settings does not update the mayor'\''s running session #1438)
- Existing towns without
role_modelscontinue working unchanged
References
- feat(gastown): Replace default model text input with model selector dropdown in town settings #1430 — Model selector dropdown (replaces text input)
- fix(gastown): Changing default model in town settings does not update the mayor'\''s running session #1438 — Mayor session restart on model change
container/src/agent-runner.ts—buildKiloConfigContent(), model resolutionsrc/dos/town/actions.ts—dispatch_agentaction, model passed to container