fix(desktop): QA fixes — model picker, slash commands, voice input - #810
Merged
Conversation
Two surfaces the desktop QA found broken, both because serve answered too
thinly:
Model config:
- model.options returned {providers: []} whenever there was no live session
— but the composer opens the picker on the WELCOME screen, before the
first prompt. The picker showed 'No models found'. Now model.options falls
back to a session-independent catalog built straight from config
(provider_catalog + default provider/model), so the full provider-grouped
list renders immediately.
- Added config.set: model switching (config.set key=model) went nowhere —
serve had no handler. Ported the TUI client's setModel, including the
provider-mismatch → set_provider → re-apply-model retry the picker relies
on, plus permission_mode/effort/provider/thinking/logoColor keys.
Verified live: sonnet-4-6 → opus-5 applies and get_settings confirms.
Slash commands:
- commands.catalog / complete.slash were empty stubs, so the '/' menu showed
'No matches. Try /help.' Ported the built-in command list (desktop_commands
.py, mirroring the TUI's SLASHES) and merge live skills (list_skills) +
workflow commands (list_workflow_commands). The menu now shows the full
catalog (89 commands with a session).
- Added slash.exec / command.dispatch (desktop_slash.py), porting the TUI's
dispatchSlash map: each command relays to its agent control and formats a
one-line result, with a skill_command fallback for user skills/workflows.
Verified live: /context, /cost, /version, /eco, /thinking all return real
data.
18 new pytest cases (catalog build/merge/complete, dispatch mapping +
skill fallback + never-raises, catalog-from-config). All 45 desktop server
tests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ssage) (QA round 2) The mic button posted to POST /api/audio/transcribe, which serve didn't implement — the request hit the 404 catch-all and the renderer silently returned an empty transcript, so the mic looked dead. Backend: - New /api/audio/transcribe (src/server/desktop_audio.py): decodes the recorded data URL and transcribes it via an OpenAI-compatible /audio/transcriptions (Whisper) endpoint, using a configured provider's key + base URL (openai/groq host Whisper). Provider + model are configurable (voice.stt_provider / voice.stt_model). The agent core's STT is an abstract stub, so this is the first working transcription path. - Actionable failures instead of raw upstream JSON: no STT-capable provider configured, or a chat-only gateway that rejects the transcription model (the user's LiteLLM gateway has no Whisper), both return a clear 'configure a Whisper provider' message. Renderer: - transcribeVoiceAudio threw away result.ok/error and returned the empty transcript on failure — now it throws the backend's actionable message, which the voice recorder already surfaces via notifyError. Added the field to AudioTranscriptionResponse and an sttFailed string across all five locales. 7 new pytest cases (data-url decode, success via mocked Whisper endpoint asserting the multipart request, model-rejection + no-provider actionable errors, empty clip, config model override). Renderer typecheck green; lint back to the reference baseline (0 errors / 88 warnings). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
agentforce314
enabled auto-merge (squash)
August 8, 2026 15:39
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.
Hands-on QA of the desktop app surfaced several broken surfaces where
clawcodex serveanswered too thinly. Each was found by driving the real app over CDP and fixed + verified live.Model config (round 1)
model.optionsreturned{providers: []}with no live session, but the composer opens the picker on the welcome screen. Now falls back to a session-independent catalog from config (provider_catalog). The full provider-grouped list renders.config.sethandler. Ported the TUI'ssetModel(incl. the provider-mismatch →set_provider→ re-apply retry the picker relies on) + permission_mode/effort/provider/thinking/logoColor keys. Verified: sonnet-4-6 → opus-5 applies.Slash commands (round 1)
/menu showed "No matches":commands.catalog/complete.slashwere empty stubs. Ported the built-in command list + merge live skills & workflow commands — the menu shows all 89 commands.slash.exec/command.dispatch(porteddispatchSlash) with askill_commandfallback. Verified:/context,/cost,/version,/eco,/thinkingreturn real data.Voice input (round 2)
POST /api/audio/transcribewas unimplemented (404). Added it — transcribes via an OpenAI-compatible Whisper endpoint from provider config (configurable viavoice.stt_provider/stt_model). The renderer was also throwing awayok/errorand returning an empty transcript; now it surfaces the backend's actionable message (which the recorder already toasts).Verification
🤖 Generated with Claude Code