Skip to content

Desktop: voice mode, the one time the mic listens, started from the chat header - #340

Merged
Khoality-dev merged 1 commit into
mainfrom
feat/253-voice-mode-control
Sep 28, 2026
Merged

Khoality-dev merged 1 commit into
mainfrom
feat/253-voice-mode-control

Conversation

@Khoality-dev

Copy link
Copy Markdown
Owner

Closes #253

The flow as diagrams: https://claude.ai/artifact/TJbMdQFqz9NdJPuYcC3u3K

The model (the owner's, agreed in session)

Three words, used the same way in the UI, the code and the docs: voice mode, the wake word, an interaction. Nothing is a "call" or an "interactive mode" any more.

  • Voice mode is the only time the mic listens.
    • It is turned on and off from a headset button in the chat header, shown where the host can capture the microphone (a new microphone capability: always in Electron, a secure context in a browser).
    • It replaces the "Always listen" setting, and this device remembers it.
    • While it is on, the voice bar (VoiceModeBar) replaces the message box.
  • The wake word starts an interaction, and only in voice mode. Speech without it is ignored while voice mode waits. With voice mode off nothing is heard: no dictation, and the wake word means nothing.
  • An interaction sends everything said, with no wake word needed again.
    • Each interaction is a new conversation.
    • It ends 30 s after the assistant's last reply has finished (streaming and speaking) with nothing said since. Voice mode then waits for the wake word again.
    • The assistant's app_end_interaction tool ends it the same way.
  • Push-to-talk (Ctrl+Space) is removed.

This changes #337's behaviour, merged earlier today: outside voice mode the wake word no longer starts anything.

Tests

New tests, each run first against the unfixed code and failed:

  • state/voiceMode.test.ts (4): voice mode on waits without an interaction; off ends it and stops the mic; an interaction ending leaves voice mode on; the setting is remembered and restored.
  • ui/chat/voiceMode.test.tsx (9), replacing wakeWord.test.tsx:
    • the wake word starts an interaction in a new conversation;
    • later messages go to that conversation, and the next interaction is another new conversation;
    • speech without the wake word is ignored;
    • the timeout fires 30 s after the reply finished, not while it streams or speaks, and starts over when you speak;
    • nothing is used with voice mode off.
  • Desktop Playwright voiceMode.spec.ts (3), using Chromium's fake mic:
    • the header starts voice mode and the voice bar waits for the wake word;
    • both buttons end it, and it survives a reload;
    • Settings no longer has "Always listen".

After the fix:

  • The new voice-mode Playwright specs, with settings.spec.ts: 13/13.
  • settings.spec.ts's voice-picker locator is now getByRole('combobox', { name: 'Voice' }), because getByLabel('Voice') also matched "Start voice mode".
  • Workspace npm test: all suites pass. npm run typecheck: clean.

Not covered

  • The e2e build cannot load the VAD model (ort-wasm-simd-threaded.mjs over file://). The specs therefore check the controls and state, not real speech.
  • Android is not changed here. It still enters its voice mode by the wake word and has no button. That is a follow-up issue.

🤖 Generated with Claude Code

https://claude.ai/code/session_014L9pxjp1MTSJe5DZG9A9sv

…m the chat header (#253)

The owner's model, and the only three words for it: voice mode, the wake
word, an interaction.

- Voice mode, on and off from a headset button in the chat header (where
  the host can capture the microphone, a new `microphone` capability), is
  the only time the mic listens. It replaces the "Always listen" setting
  and is remembered on this device. The voice bar stands in for the
  composer while it is on.
- In voice mode the wake word starts an interaction; speech without it is
  ignored. Outside voice mode nothing is heard — no dictation, and the wake
  word means nothing.
- An interaction sends everything said, no wake word again. Each one is a
  new conversation. It ends 30 s after the assistant's last reply has
  finished streaming and speaking with nothing said since, and voice mode
  waits for the wake word again; the assistant's app_end_interaction does
  the same.
- Push-to-talk (Ctrl+Space) is removed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014L9pxjp1MTSJe5DZG9A9sv
@Khoality-dev
Khoality-dev merged commit 6e05b65 into main Sep 28, 2026
3 checks passed
@Khoality-dev
Khoality-dev deleted the feat/253-voice-mode-control branch September 28, 2026 06:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Interactive voice mode has no way in from the UI

2 participants