You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Owners have no way to change an agent's model from chat. The Desktop model picker can switch the model of an idle or in-flight turn, but an owner working from the CLI, mobile, or another desktop sees no lever. The ACP harness already resolves a model per turn, and the Desktop already speaks the switch_model control protocol — the missing piece is an owner-facing chat command over the same path.
Proposed behavior
Two new owner control commands (alongside !shutdown / !cancel / !rotate), parsed from kind:9 stream messages that mention the agent and come from the resolved owner:
Command
Effect
!model
Lists the channel agent's model catalog and the model that applies on its next turn. Replies in-channel.
!model <id>
Switches the channel agent's model via the same path as the Desktop model picker. If a turn is in-flight it is superseded — cancelled and its batch re-queued on the new model; otherwise the next turn starts on the new model. Unsupported ids are rejected against the agent's cached catalog. Replies in-channel.
Reply copy should be terse, e.g. model updated to <id>, <id> is not supported — send '!model' to list available ids, and for the listing model: <id> + available: <ids>.
Design notes (from a working prototype)
A prototype exists on local branch feat/acp-model-command (commit bec949446, rebased onto current main) and can be revived as the starting point.
Shared routing. Extract a switch_channel_model(pool, channel_id, model_id, request_id) -> status helper used by both the Desktop switch_model control frame and the !model command. A turn is in-flight iff a task_map entry exists for the channel (the agent is checked out of the pool); busy channels signal ControlSignal::SwitchModel over the in-flight task's oneshot, idle channels pre-validate against the cached catalog and then set desired_model + invalidate the session.
Catalog lookup. New AgentPool::channel_model_info(channel_id) returns the effective model (desired_model override, else catalog currentModelId), whether it's overridden, and the available ids — stable configOptions values first, falling back to availableModels[].modelId, deduped. Lookups must be scope-aware (SessionScope::channel_id() == channel_id) now that sessions are keyed by SessionScope, not raw channel ids.
Reply channel. The harness authors the in-channel reply itself via a new HarnessRelay::build_stream_message(channel_id, content, reply_to) (kind:9, h tag, optional e reply tag) — no round-trip through the agent. Failed publishes are logged, never fatal.
Parsing.parse_owner_model_command mirrors the other owner commands but takes an optional argument; near-misses (!models, !modelx, hey !model) fall through as normal messages. Ownership is checked by the caller like !shutdown/!cancel/!rotate.
Tests. Unit tests pinned parsing (bare/arg/whitespace/near-misses), reply copy per status, and channel_model_info catalog/override behavior plus both idle switch paths.
Open questions
Should !model respect the thread session policy the way !cancel/!rotate now do, or stay channel-scoped (the prototype is channel-scoped)?
Should the reply include the harness-level --model fallback when the catalog hasn't filled in yet, or omit the catalog line entirely until the agent completes a turn in the channel?
Problem
Owners have no way to change an agent's model from chat. The Desktop model picker can switch the model of an idle or in-flight turn, but an owner working from the CLI, mobile, or another desktop sees no lever. The ACP harness already resolves a model per turn, and the Desktop already speaks the
switch_modelcontrol protocol — the missing piece is an owner-facing chat command over the same path.Proposed behavior
Two new owner control commands (alongside
!shutdown/!cancel/!rotate), parsed from kind:9 stream messages that mention the agent and come from the resolved owner:!model!model <id>Reply copy should be terse, e.g.
model updated to <id>,<id> is not supported — send '!model' to list available ids, and for the listingmodel: <id>+available: <ids>.Design notes (from a working prototype)
A prototype exists on local branch
feat/acp-model-command(commitbec949446, rebased onto currentmain) and can be revived as the starting point.switch_channel_model(pool, channel_id, model_id, request_id) -> statushelper used by both the Desktopswitch_modelcontrol frame and the!modelcommand. A turn is in-flight iff atask_mapentry exists for the channel (the agent is checked out of the pool); busy channels signalControlSignal::SwitchModelover the in-flight task's oneshot, idle channels pre-validate against the cached catalog and then setdesired_model+ invalidate the session.pool.channel_control_is_ambiguous(channel_id)first and surfaceambiguous_target— never switch one arbitrary sibling session and report a channel-wide success (see fix(buzz-acp): bound busy-owner hold to prevent cross-channel starvation block/buzz#7337's busy-owner hold work).AgentPool::channel_model_info(channel_id)returns the effective model (desired_modeloverride, else catalogcurrentModelId), whether it's overridden, and the available ids — stableconfigOptionsvalues first, falling back toavailableModels[].modelId, deduped. Lookups must be scope-aware (SessionScope::channel_id() == channel_id) now that sessions are keyed bySessionScope, not raw channel ids.HarnessRelay::build_stream_message(channel_id, content, reply_to)(kind:9,htag, optionalereply tag) — no round-trip through the agent. Failed publishes are logged, never fatal.parse_owner_model_commandmirrors the other owner commands but takes an optional argument; near-misses (!models,!modelx,hey !model) fall through as normal messages. Ownership is checked by the caller like!shutdown/!cancel/!rotate.channel_model_infocatalog/override behavior plus both idle switch paths.Open questions
!modelrespect thethreadsession policy the way!cancel/!rotatenow do, or stay channel-scoped (the prototype is channel-scoped)?--modelfallback when the catalog hasn't filled in yet, or omit the catalog line entirely until the agent completes a turn in the channel?