Skip to content

feat(acp): !model owner command to list and switch channel agent model #1

Description

@lbarasti

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_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.
  • Ambiguity gate. Route through pool.channel_control_is_ambiguous(channel_id) first and surface ambiguous_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).
  • 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?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions