Skip to content

TUI: local agent not synced when a plugin switches agent via #17024

Closed as not planned

Description

@kev1n77

Description

Summary

In the TUI, when a plugin tool calls session.promptAsync with a different agent parameter (e.g. switching from test-plan to test-build), the message header correctly shows the new agent, but the prompt status bar (below the input box) still displays the old agent. The user has to manually press Tab to cycle the agent, even though the session is already running under the new agent.

Expected Behavior

The prompt status bar should update to reflect the agent used by the most recent user message, consistent with how the web app behaves.

Root Cause

The TUI's prompt/index.tsx only syncs local.agent from lastUserMessage() when the session ID changes:

// packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx, lines 151-169
let syncedSessionID: string | undefined
createEffect(() => {
  const sessionID = props.sessionID
  const msg = lastUserMessage()
  if (sessionID !== syncedSessionID) {   // <-- only on session change
    if (!sessionID || !msg) return
    syncedSessionID = sessionID
    const isPrimaryAgent = local.agent.list().some((x) => x.name === msg.agent)
    if (msg.agent && isPrimaryAgent) {
      local.agent.set(msg.agent)
      // ...
    }
  }
})

When promptAsync creates a new user message with a different agent within the same session, sessionID hasn't changed, so local.agent is never updated.
The web app (packages/app/src/pages/session.tsx) does not have this issue because it watches lastUserMessage()?.id instead:

// packages/app/src/pages/session.tsx, lines 422-430
createEffect(
  on(
    () => lastUserMessage()?.id,   // <-- triggers on every new message
    () => {
      const msg = lastUserMessage()
      if (!msg) return
      syncSessionModel(local, msg)  // calls local.agent.set(msg.agent)
    },
  ),
)

Suggested Fix

Update the TUI's prompt/index.tsx to also sync local.agent when lastUserMessage() changes within the same session, similar to the web app's approach. For example:

createEffect(
  on(
    () => lastUserMessage()?.id,
    () => {
      const msg = lastUserMessage()
      if (!msg) return
      const isPrimaryAgent = local.agent.list().some((x) => x.name === msg.agent)
      if (msg.agent && isPrimaryAgent) {
        local.agent.set(msg.agent)
        if (msg.model) local.model.set(msg.model)
        if (msg.variant) local.model.variant.set(msg.variant)
      }
    },
  ),
)

Plugins

none

OpenCode version

opencode: latest dev branch(commit: db57fe6) Platform: Windows11 (TUI mode)

Steps to reproduce

  1. Configure a custom agent (e.g. test-plan) with a plugin tool that calls client.session.promptAsync({ ..., agent: "test-build", ... }) to switch to another agent.
  2. In the TUI, start a session using the test-plan agent.
  3. Trigger the tool that calls promptAsync with agent: "test-build".
  4. Observe the new assistant message header — it correctly shows test-build.
  5. Observe the prompt status bar below the input box — it still shows test-plan.

Screenshot and/or share link

No response

Operating System

Windows11

Terminal

powershell 5.1

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Mar 11, 2026
  2. github-actions commented on Mar 11, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of or closely related to existing issues. Please check:

    If these are addressing the same underlying issue, it may be worth coordinating with the author of #14528.

  3. removed
    bugSomething isn't working
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on May 3, 2026
  4. github-actions commented on Jul 4, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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

Metadata

Metadata

Assignees

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