Skip to content

Desktop: agent picker never renders (t.agent.visible() is always false) #57812

Open

Description

@lekker-9

Description

Summary

In OpenCode Desktop on Windows, the agent control is never rendered in the
composer, and its keybind (Mod/Ctrl + .) does nothing. The API reports
both plan and build as visible primary agents, and switching agents through
POST /api/session/:id/agent works — so this is a UI gating problem, not a
config or backend problem.

Reproduced on 2.1.1 and 2.1.2, in both existing and brand-new sessions.

Environment

opencode v2.1.2 (desktop), also reproduced on v2.1.1
OS Windows 10.0.26200.9711 (x64)
Shell cmd.exe
Install desktop installer, @opencodedesktop per-user install
Config ~/.config/opencode/opencode.json, default_agent: "plan"
MCP one stdio server (open-meteo), connected
Plugins one local plugin (.opencode/plugins/replicate.ts)

Reproduction

  1. Launch OpenCode Desktop 2.1.2 on Windows.
  2. Open an existing session, or create a new one.
  3. Look at the composer: the model selector and model variant are present, the
    agent control is not.
  4. Press Ctrl + . — nothing happens.
  5. Confirm the backend state:
    opencode api get /api/agent
    → returns plan and build, both primary, both hidden: false.
  6. Confirm switching works outside the UI:
    opencode api post /api/session/<id>/agent --data '{"agent":"build"}'
    → succeeds, and subsequent requests in that session run as build.

Expected

An agent chip in the composer (as in the terminal client), listing the visible
primary agents; Mod/Ctrl + . opens the picker.

Actual

No control is rendered and the keybind never registers, so the only way to
change agents is the HTTP API.

Additional Context

Reading the shipped app.asar (2.1.2), the control is guarded here:

get agent() {
  let t = e.controls().agents
  return t.visible && t.options.length > 0
    ? { options: () => e.controls().agents.options.map(...), ... }
    : undefined
}

The composer attaches the keybind to that component:

R(q, {
  get when() { return a.agent },
  keyed: true,
  children: (e) => R(lo, { title: n.t('ui.promptInput.chooseAgent'), keybind: ['Mod', '.'], control: e })
})

and the control object is built with:

visible: t.agent.visible(),
options: t.agent.list().map(e => e.name),
current: t.agent.current()?.name ?? ''

Because the agent list is non-empty (/api/agent returns two visible
primaries), the leading suspect is t.agent.visible() evaluating to false,
which returns undefined and suppresses both the chip and the keybind.

Things ruled out:

  • Config. The control was missing before any permissions / agents edits,
    and /api/agent returns correct data with the current config.
  • Version. The gate is byte-identical in 2.1.1 and 2.1.2.
  • Session state. Reproduced in a freshly created session.
  • Missing toggle. The only agent-related i18n keys are
    ui.promptInput.chooseAgent, ui.tool.agent[.default|.delegating] and
    ui.sessionTimeline.notice.agentChanged — there is no settings entry that
    could hide the control.

Impact: with default_agent: "plan" (a read-only agent by design), a new
session cannot be switched to build from the UI at all. Users who configure
sessions.default_agent to a restricted agent have no escape hatch.

Possible angle: check whatever populates t.agent.visible() — if it depends on
a feature flag or on the agent list being fetched for the current workspace, it
appears to be failing silently on Windows.

Plugins

No response

OpenCode version

No response

Steps to reproduce

No response

Screenshot

No response

Operating System

No response

Terminal

No response

Activity

  1. opencode-agent commented on Oct 21, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed write-up and for digging into the bundle.

    This looks like intended behaviour rather than a bug. In v2 the agent picker in the composer is hidden by default when only the built-in agents (plan/build) are available. There's a setting to show it:

    Settings → General → "Show agent", described as "Switch between agents in the composer. When hidden, defaults to Build agent."

    With it turned on, t.agent.visible() becomes true, so the agent chip appears and Ctrl + . opens the picker. (It's stored as general.showCustomAgents, which is why it didn't turn up when you searched the ui.* strings.) Also, while the picker is hidden, the app falls back to the Build agent rather than keeping you on plan.

    I'll leave this open as a request: show the picker by default, or at least when default_agent is set to something other than build. Maintainers will consider it.

    Related issues:

    If the picker still doesn't appear after you turn on "Show agent", please reply here and say so.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions