Skip to content

[FEATURE]: Publish tui.session.select bus event on internal session navigation #20996

Description

@c0dn

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

Background

I'm building a plugin called opencode-cache-guard that warns users when they switch to a session whose Anthropic prompt cache has likely expired (idle > 55 min, > 10K cached tokens). Stale caches silently re-warm at 10-20x the cost of a cache hit, so the plugin exists to surface this before the user is surprised.

The ideal UX is: warn the moment you switch to a stale session, not when you hit send. To do that, a plugin needs to know when session focus changes.

What's missing

The `tui.session.select` BusEvent already exists in `src/cli/cmd/tui/event.ts`, the server route `/tui/select-session` publishes it, and the TUI already listens for it in `app.tsx`:

```ts
sdk.event.on(TuiEvent.SessionSelect.type, (evt) => {
route.navigate({ type: "session", sessionID: evt.properties.sessionID })
})
```

But internal TUI navigation calls `route.navigate()` directly, so the bus event never fires and plugins never see it. The three affected paths are:

  1. `dialog-session-list.tsx` `onSelect` — user picks a session from the list
  2. `app.tsx` `onMount` with `args.sessionID` — `opencode -s ` startup
  3. `app.tsx` `createEffect` with `args.continue` — `opencode -c` resume last session

Because `bus.subscribeAll()` forwards all bus events to plugin `event` hooks, wiring up these paths would give plugins a clean way to react to session focus changes without any new plugin API surface.

Proposed fix

For the session list dialog: replace `route.navigate()` with `sdk.client.tui.selectSession()`. The existing event handler in `app.tsx` will call `route.navigate()`, so navigation is unchanged. The bus event now fires, plugins can see it.

PR submitted for the dialog case: #20997

The startup args cases (`-s` and `-c`) in `app.tsx` would need the same treatment but left for follow-up since they have slightly different async timing considerations.

Activity

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