feat(companion): make all enabled backends selectable (Codex + Claude) - #418
Merged
claudiusthebot merged 1 commit intoJul 1, 2026
Merged
Conversation
Dylan has both the Claude and Codex backends enabled on the gateway but
the companion app only ever showed Claude β the native bridge never
exposed the backend layer. The multi-backend machinery already exists
(per-chat override, listAvailableBackends, rebindChat, the Telegram
/model backend submenu); this wires it through the bridge and adds a
picker to the app.
Bridge/engine:
- protocol: ClientChat gains `backend`; new BackendOption type.
- server: GET /backends (chatId) and POST /backend (chatId, backend).
/backend always returns 200 with {ok,error} β the client's decoder
drops >=400 bodies, and this is an app result, not an HTTP error.
- index: toClientChat reports the chat's backend; listBackends() returns
the enabled backends + the chat's active one; setBackend() mirrors the
Telegram flow β verify enabled, rebindChat, pin override, reset the
session/history/pulse (sessions aren't portable across backends),
broadcast chat_updated + status. listModels() now takes an optional
chatId so the active-model hint tracks the chat's backend.
Client:
- BackendOption model; ClientChat.backend; BridgeClient.backends()/
setBackend(); models() takes an optional chatId; AppState.backends()/
setBackend()/refreshModels(chatId).
- model sheet: a BACKEND chip row (shown when >1 backend is enabled) with
a switching spinner, a "starts a fresh conversation" note, and a
SnackBar on failure. Backends are fully gateway-driven β nothing is
hardcoded, so whatever the gateway reports as enabled is what shows.
tsc + flutter analyze clean; native-frontend + bridge suites (25) pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Dylan has both Codex and Claude backends enabled on the gateway, but the companion app only ever showed Claude. Root cause: the native bridge never exposed the backend layer at all β it had
listModels/setModelbut no concept of backends. (Telegram already has a/modelbackend submenu; the bridge was the gap.)Fix β fully gateway-driven, nothing hardcoded
Bridge / engine
protocol:ClientChatgainsbackend; newBackendOptiontype.server:GET /backends?chatId=andPOST /backend./backendalways returns 200 with{ok,error}(the client decoder drops β₯400 bodies, and this is an app-level result).index:listBackends()returns the enabled backends (listAvailableBackends(config), honouringenabledBackends) + the chat's active one;setBackend()mirrors the Telegram flow β verify enabled βrebindChatβ pin override β reset session/history/pulse (sessions aren't portable across backends) β broadcastchat_updated+status.listModels()takes an optionalchatIdso the active-model hint tracks the chat's backend.Client
BackendOptionmodel;ClientChat.backend;BridgeClient.backends()/setBackend();AppState.backends()/setBackend()/refreshModels(chatId).Whatever the gateway reports as enabled is what shows β no assumptions about specific backends/models.
Test
tsc --noEmitclean Β·flutter analyze libβ No issues Β· native-frontend + bridge suites (25) pass.π€ Generated with Claude Code