Skip to content

feat(web): add push-to-talk, VAD continuous listening, and voice settings#2

Open
P2Chill wants to merge 1807 commits into
mainfrom
feat/voice-modes
Open

feat(web): add push-to-talk, VAD continuous listening, and voice settings#2
P2Chill wants to merge 1807 commits into
mainfrom
feat/voice-modes

Conversation

@P2Chill

@P2Chill P2Chill commented Mar 2, 2026

Copy link
Copy Markdown
Owner

Summary

Adds two new voice input modes alongside the existing toggle:

  • Push-to-Talk (PTT): Configurable hotkey (default F13), hold to record, release to send. BroadcastChannel tab coordination prevents dual-tab recording.
  • Voice Activity Detection (VAD): Energy-based continuous listening with conversation mode button. Auto-sends after silence, auto-resumes after TTS playback. Configurable sensitivity slider in settings.
  • Voice Settings UI: PTT key picker and VAD sensitivity slider in the Voice settings tab.

Changed files

  • voice-input.js — Complete rewrite with PTT, VAD, tab coordination, health monitoring
  • page-chat.js — VAD waveform button next to mic button
  • page-settings.js — PTT key picker + VAD sensitivity slider
  • components.css — VAD button CSS states (listening glow, speech pulse)
  • input.css — Waveform icon SVG
  • locales/en,fr,zh/chat.js — i18n keys

Test plan

  • Toggle mode still works (click mic, click again to send)
  • PTT: hold configured key, speak, release - transcribes and sends
  • PTT key rebind in Settings > Voice works
  • VAD: click waveform button - listening state - speak - silence - auto-sends
  • VAD sensitivity slider adjusts detection threshold
  • VAD mutes during TTS playback, resumes after
  • VAD survives 10+ back-and-forth turns without degradation
  • No duplicate recorder starts in console logs
  • Tab coordination: only one tab records at a time

@P2Chill P2Chill force-pushed the feat/voice-modes branch 3 times, most recently from 57399d8 to f4e14b0 Compare March 3, 2026 00:42
penso and others added 27 commits April 14, 2026 10:39
rand 0.10 moved random_range from the Rng trait to RngExt.

Entire-Checkpoint: 17e9b54d784a
rand 0.10 moved RngCore into rand_core (re-exported as rand::Rng for
fill_bytes) and moved random/random_range to the RngExt trait.
Update all affected crates: oauth, vault, webhooks, browser, auth,
cli, gateway, channels.

Entire-Checkpoint: 999e29f811d7
The "Memory live" / "Memory frozen" button and its refresh companion
cluttered the chat header for a setting most users configure once.
Prompt memory status and the refresh action remain accessible via
the "..." menu → Context (full context modal).

Remove the toolbar HTML, bindPromptMemoryToolbar(), the per-session
effect that polled chat.context on every switch, and the E2E test
that exercised the now-removed toolbar buttons.  The full context
modal test already covers prompt memory display and refresh.

Entire-Checkpoint: ad104e58319b
…ders

local-llm (built-in llama.cpp with HuggingFace downloads) was offered but
buried under "All providers" instead of showing in the recommended section.
LM Studio wasn't in the default offered list at all. Both are now
recommended and offered by default so users see local inference options
prominently during onboarding.

Entire-Checkpoint: 73c6362fea07
…ters

The backend (auth_routes.rs) already enforces a 12-character minimum but
all UI strings, placeholders, client-side validation, Swift bridge, macOS
app, and docs still referenced 8 characters.

Entire-Checkpoint: 3ff3aaf1c445
Entire-Checkpoint: fa2921ba12b0
Entire-Checkpoint: 7c8ff12599b3
Entire-Checkpoint: 5f8d1d7d2a14
slack-morphism <2.20 mapped its rustls-native-certs feature to
tokio-tungstenite/rustls-native-certs, which only activates the
certificate resolver — not the actual TLS stack. This caused
Url(TlsFeatureNotEnabled) when connecting to wss://wss-primary.slack.com.

Version 2.20 adds tokio-tungstenite/rustls-tls-native-roots, enabling
the full rustls + tokio-rustls TLS implementation.

Closes moltis-org#543

Entire-Checkpoint: 1648c51b4b8b
- Run `cargo update reqwest@0.12.28` so all workspace crates resolve
  to reqwest 0.13.2 instead of 0.12.28
- Add `query` feature which became opt-in in reqwest 0.13
- Third-party crates (async-openai, chromiumoxide, matrix-sdk, oauth2)
  still pull reqwest 0.12; teloxide-core still pulls 0.11 — these
  cannot be resolved at the workspace level

Entire-Checkpoint: e7e417b27a80
GraphQL sessionKey was optional, causing session routing mismatches for
custom clients that omit it — the chat service would silently fall back
to the "main" session instead of erroring. Make sessionKey a required
String on all chat mutations (send, abort, cancelQueued, clear, compact),
queries (history, context, rawPrompt, fullContext), and the chatEvent
subscription.

Regenerate iOS GraphQL schema to match.

Closes moltis-org#542

Entire-Checkpoint: d217a837cb32
- Drop chat events without sessionKey in payload instead of forwarding
  them to all subscribers (explicit match guard)
- Remove flaky 100ms timeout from subscription validation test
- Add required-sessionKey tests for all remaining operations: abort,
  cancelQueued, clear, compact, context, rawPrompt, fullContext
- Add test case for events without sessionKey being dropped

Entire-Checkpoint: 261bd146b2d8
WhatsApp updated their protobuf message schema. waproto 0.2 no longer
covers the current wire format, so after Signal decryption the protobuf
deserialization yields a Message with all fields None. The handler falls
through to ChannelMessageKind::Other and replies with an error instead
of routing to the LLM.

Bump all 6 crates (whatsapp-rust, wacore, wacore-binary, waproto,
whatsapp-rust-tokio-transport, whatsapp-rust-ureq-http-client) from
0.2 to 0.5 and adapt to the new API:

- Add .with_runtime(TokioRuntime) to bot builder (new 4th type param)
- Implement new SignalStore::get_max_prekey_id() for both stores
- Implement new AppSyncStore::get_latest_sync_key_id() for both stores
- Implement 8 new ProtocolStore methods (TC tokens + sent message cache)
- Update SKDM methods: Vec<String> → Vec<Jid> signatures
- Add new sled trees (tc_tokens, sent_messages) and DashMap fields
- Disable default simd feature on wacore/wacore-binary (requires
  unstable core::simd::Select not available on current nightly)

Existing paired sessions may need re-pairing if wacore struct layouts
changed between 0.2 and 0.5.

Closes moltis-org#534
Entire-Checkpoint: 65e290092e0b
Entire-Checkpoint: 2860ff44e09b
WhatsApp is a fully implemented channel but was excluded from the
default offered list, requiring manual opt-in via moltis.toml. Now
that the whatsapp-rust ecosystem is upgraded to 0.5 and working
properly, include it by default so users see it in onboarding and
the channels settings page without extra configuration.

Updated default lists in:
- Config schema (default_channels_offered)
- Config template (comment)
- Gateway state (hardcoded fallback)
- JS fallbacks (onboarding-view.js, page-channels.js)

Entire-Checkpoint: a27a36510a1a
…dering

Address PR review comments:
- Replace manual SystemTime::UNIX_EPOCH arithmetic with
  time::OffsetDateTime::now_utc().unix_timestamp() per CLAUDE.md rules
- Unify sent message timestamp type to i64 across both stores
  (sled_store was u64, memory_store was i64)
- Track latest sync key ID explicitly in MemoryStore instead of
  relying on non-deterministic DashMap iteration order

Entire-Checkpoint: ab25d949d314
The WhatsApp QR code was delivered exclusively via WebSocket events,
which could be missed due to timing (event fires before subscription
is fully active) or dropped if the client is slow. Users saw
"Waiting for QR code..." indefinitely.

Two changes:
1. Expose QR code data in ChannelHealthSnapshot.extra so it's
   available via the channels.status RPC
2. Add polling fallback (every 2s) in both onboarding-view.js and
   page-channels.js that fetches QR data from channels.status
   when the WebSocket event hasn't arrived

Entire-Checkpoint: 1cf2bc56081a
penso and others added 19 commits April 24, 2026 11:18
…org#862)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
chat-autoscroll: add missing setAutoScrollMode to the state.js E2E
shim so the "always" mode test can set the scroll mode.

projects: add a project via the UI before testing the edit form since
the E2E env starts with no projects. Wait for WS connection before
interacting.
- Add codeIndexEnabled field to Project type in iOS GraphQL schema
- Remove unused network.client entitlement from macOS app
…oltis-org#845)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Port push-to-talk, VAD continuous listening, and voice settings
from the original JS implementation to the current TypeScript/Preact
codebase. The original PR (moltis-org#303) targeted files that no longer exist
after the JS-to-TS migration.

PTT: configurable hotkey (default F13), function keys work in inputs,
BroadcastChannel tab coordination prevents dual-tab recording.

VAD: energy-based RMS detection with exponential sensitivity curve,
2.5s silence auto-send, 30s max recording safety valve, TTS
mute/unmute with echo settle delay, AudioContext health monitoring,
MediaStream track auto-reacquisition, vadTranscribing race-condition
guard, EBML header validation, 15s fetch timeout.

Settings: PTT key picker (click-to-rebind), VAD sensitivity slider.
@penso penso force-pushed the feat/voice-modes branch from f4e14b0 to b624672 Compare April 24, 2026 13:57
penso added a commit that referenced this pull request Apr 24, 2026
ci: add Rust CI workflow for fmt, clippy, and build
@coderabbitai

coderabbitai Bot commented Apr 24, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 283 files, which is 133 over the limit of 150.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c50d01ed-b1f7-498b-95a7-5f02887a80bc

📥 Commits

Reviewing files that changed from the base of the PR and between b024ee3 and fc986df.

⛔ Files ignored due to path filters (17)
  • Cargo.lock is excluded by !**/*.lock
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Fragments/SessionFields.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Operations/Mutations/UpdateUserLocationMutation.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Operations/Queries/FetchModelsQuery.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Operations/Queries/FetchSessionsQuery.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Operations/Queries/FetchStatusQuery.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Operations/Queries/SearchSessionsQuery.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Schema/Objects/AgentMutation.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Schema/Objects/BoolResult.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Schema/Objects/ModelInfo.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Schema/Objects/ModelQuery.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Schema/Objects/MutationRoot.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Schema/Objects/QueryRoot.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Schema/Objects/SessionEntry.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Schema/Objects/SessionQuery.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Schema/Objects/StatusInfo.graphql.swift is excluded by !**/generated/**
  • apps/ios/Sources/GraphQL/Generated/MoltisAPI/Schema/SchemaMetadata.graphql.swift is excluded by !**/generated/**
📒 Files selected for processing (283)
  • .beads/.gitignore
  • .beads/README.md
  • .beads/config.yaml
  • .beads/issues.jsonl
  • .beads/metadata.json
  • .claude/settings.json
  • .config/nextest.toml
  • .do/deploy.template.yaml
  • .github/ISSUE_TEMPLATE/bug_report.yml
  • .github/ISSUE_TEMPLATE/feature_request.yml
  • .github/ISSUE_TEMPLATE/model_behavior.yml
  • .github/workflows/ci.yml
  • .github/workflows/codspeed.yml
  • .github/workflows/docs.yml
  • .github/workflows/e2e.yml
  • .github/workflows/homebrew.yml
  • .github/workflows/provider-integration.yml
  • .github/workflows/release.yml
  • .gitignore
  • .npmrc
  • .superset/config.json
  • .superset/setup.sh
  • CHANGELOG.md
  • CLAUDE.local.md
  • CLAUDE.md
  • Cargo.toml
  • Dockerfile
  • HANDOFF-reconfigurable-context-windows.md
  • README.md
  • SECURITY.md
  • apps/courier/src/main.rs
  • apps/ios/GraphQL/Schema/schema.graphqls
  • apps/ios/Sources/Networking/ChatEventTypes.swift
  • apps/ios/Sources/Stores/ChatStore.swift
  • apps/ios/project.yml
  • apps/macos/Moltis.entitlements
  • apps/macos/Sources/AppSettings.swift
  • apps/macos/Sources/ConfigurationPane.swift
  • apps/macos/project.yml
  • biome.json
  • cliff.toml
  • crates/agents/Cargo.toml
  • crates/agents/src/lazy_tools.rs
  • crates/agents/src/lib.rs
  • crates/agents/src/memory_writer.rs
  • crates/agents/src/model.rs
  • crates/agents/src/model/types.rs
  • crates/agents/src/prompt.rs
  • crates/agents/src/prompt/builder.rs
  • crates/agents/src/prompt/formatting.rs
  • crates/agents/src/prompt/tests.rs
  • crates/agents/src/prompt/types.rs
  • crates/agents/src/provider_chain.rs
  • crates/agents/src/response_sanitizer.rs
  • crates/agents/src/runner.rs
  • crates/agents/src/runner/helpers.rs
  • crates/agents/src/runner/mod.rs
  • crates/agents/src/runner/non_streaming.rs
  • crates/agents/src/runner/retry.rs
  • crates/agents/src/runner/streaming.rs
  • crates/agents/src/runner/tests/basic.rs
  • crates/agents/src/runner/tests/compaction.rs
  • crates/agents/src/runner/tests/helpers.rs
  • crates/agents/src/runner/tests/mod.rs
  • crates/agents/src/runner/tests/parallel.rs
  • crates/agents/src/runner/tests_legacy/core.rs
  • crates/agents/src/runner/tests_legacy/mod.rs
  • crates/agents/src/runner/tests_legacy/runner.rs
  • crates/agents/src/runner/tests_legacy/streaming.rs
  • crates/agents/src/runner/tests_legacy/tool_result.rs
  • crates/agents/src/runner/tool_result.rs
  • crates/agents/src/silent_turn.rs
  • crates/agents/src/tool_arg_validator.rs
  • crates/agents/src/tool_loop_detector.rs
  • crates/agents/src/tool_parsing.rs
  • crates/agents/src/tool_registry.rs
  • crates/auth/Cargo.toml
  • crates/auth/src/credential_store.rs
  • crates/auth/src/credential_store/api_keys.rs
  • crates/auth/src/credential_store/env_vars.rs
  • crates/auth/src/credential_store/legacy.rs
  • crates/auth/src/credential_store/passkeys.rs
  • crates/auth/src/credential_store/sessions.rs
  • crates/auth/src/credential_store/ssh.rs
  • crates/auth/src/credential_store/tests.rs
  • crates/auth/src/credential_store/types.rs
  • crates/auth/src/credential_store/util.rs
  • crates/auth/src/error.rs
  • crates/auth/src/lib.rs
  • crates/auth/src/webauthn.rs
  • crates/benchmarks/Cargo.toml
  • crates/benchmarks/benches/boot.rs
  • crates/benchmarks/benches/code_index.rs
  • crates/browser/Cargo.toml
  • crates/browser/src/container.rs
  • crates/browser/src/pool.rs
  • crates/browser/src/types.rs
  • crates/caldav/Cargo.toml
  • crates/caldav/src/client.rs
  • crates/caldav/src/error.rs
  • crates/caldav/src/ical.rs
  • crates/caldav/src/lib.rs
  • crates/caldav/src/tool.rs
  • crates/channels/src/commands.rs
  • crates/channels/src/config_view.rs
  • crates/channels/src/lib.rs
  • crates/channels/src/media_download.rs
  • crates/channels/src/otp.rs
  • crates/channels/src/plugin.rs
  • crates/channels/src/registry.rs
  • crates/chat/Cargo.toml
  • crates/chat/src/agent_loop.rs
  • crates/chat/src/channels.rs
  • crates/chat/src/chat_error.rs
  • crates/chat/src/compaction.rs
  • crates/chat/src/compaction_run/deterministic.rs
  • crates/chat/src/compaction_run/llm_replace.rs
  • crates/chat/src/compaction_run/mod.rs
  • crates/chat/src/compaction_run/recency_preserving.rs
  • crates/chat/src/compaction_run/runner.rs
  • crates/chat/src/compaction_run/shared.rs
  • crates/chat/src/compaction_run/structured.rs
  • crates/chat/src/compaction_run/test_support.rs
  • crates/chat/src/lib.rs
  • crates/chat/src/memory_tools.rs
  • crates/chat/src/memory_tools/tests.rs
  • crates/chat/src/message.rs
  • crates/chat/src/models.rs
  • crates/chat/src/prompt.rs
  • crates/chat/src/run_with_tools.rs
  • crates/chat/src/runtime.rs
  • crates/chat/src/service/chat_impl.rs
  • crates/chat/src/service/chat_impl/send.rs
  • crates/chat/src/service/mod.rs
  • crates/chat/src/service/types.rs
  • crates/chat/src/streaming.rs
  • crates/chat/src/types.rs
  • crates/cli/Cargo.toml
  • crates/cli/src/auth_commands.rs
  • crates/cli/src/channel_commands.rs
  • crates/cli/src/doctor_commands.rs
  • crates/cli/src/doctor_commands_tests.rs
  • crates/cli/src/main.rs
  • crates/cli/src/node_commands.rs
  • crates/cli/src/sandbox_commands.rs
  • crates/cli/src/service_commands.rs
  • crates/code-index/Cargo.toml
  • crates/code-index/migrations/20260416200000_code_index_init.sql
  • crates/code-index/src/backend_qmd.rs
  • crates/code-index/src/chunker.rs
  • crates/code-index/src/config.rs
  • crates/code-index/src/delta.rs
  • crates/code-index/src/discover.rs
  • crates/code-index/src/error.rs
  • crates/code-index/src/filter.rs
  • crates/code-index/src/index.rs
  • crates/code-index/src/lib.rs
  • crates/code-index/src/log.rs
  • crates/code-index/src/search.rs
  • crates/code-index/src/snapshot_store.rs
  • crates/code-index/src/store.rs
  • crates/code-index/src/store_sqlite.rs
  • crates/code-index/src/tools.rs
  • crates/code-index/src/types.rs
  • crates/code-index/src/watcher.rs
  • crates/common/Cargo.toml
  • crates/common/src/hooks.rs
  • crates/common/src/http_client.rs
  • crates/common/src/lib.rs
  • crates/common/src/secret_serde.rs
  • crates/common/src/ssrf.rs
  • crates/common/src/types.rs
  • crates/config/src/env_subst.rs
  • crates/config/src/lib.rs
  • crates/config/src/loader.rs
  • crates/config/src/loader/config_io.rs
  • crates/config/src/loader/tests.rs
  • crates/config/src/loader/workspace.rs
  • crates/config/src/provider_env.rs
  • crates/config/src/schema.rs
  • crates/config/src/schema/agents.rs
  • crates/config/src/schema/chat.rs
  • crates/config/src/schema/code_index.rs
  • crates/config/src/schema/hooks.rs
  • crates/config/src/schema/memory.rs
  • crates/config/src/schema/providers.rs
  • crates/config/src/schema/runtime.rs
  • crates/config/src/schema/system.rs
  • crates/config/src/schema/tests.rs
  • crates/config/src/schema/tools.rs
  • crates/config/src/schema/voice.rs
  • crates/config/src/template.rs
  • crates/config/src/validate.rs
  • crates/config/src/validate/schema_map.rs
  • crates/config/src/validate/semantic.rs
  • crates/config/src/validate/tests.rs
  • crates/config/src/validate/tests/agents.rs
  • crates/config/src/validate/tests/channels.rs
  • crates/config/src/validate/tests/common.rs
  • crates/config/src/validate/tests/memory.rs
  • crates/config/src/validate/tests/providers.rs
  • crates/config/src/validate/tests/security.rs
  • crates/config/src/validate/tests/structural.rs
  • crates/config/src/validate/tests/tools.rs
  • crates/config/src/validate/tests/voice.rs
  • crates/config/src/version.rs
  • crates/cron/migrations/20260406100000_cron_runs_session_key.sql
  • crates/cron/src/service.rs
  • crates/cron/src/service/tests.rs
  • crates/cron/src/store.rs
  • crates/cron/src/store_file.rs
  • crates/cron/src/store_memory.rs
  • crates/cron/src/store_sqlite.rs
  • crates/cron/src/types.rs
  • crates/discord/src/commands.rs
  • crates/discord/src/config.rs
  • crates/discord/src/handler.rs
  • crates/discord/src/handler/implementation.rs
  • crates/discord/src/handler/tests.rs
  • crates/discord/src/outbound.rs
  • crates/discord/src/plugin.rs
  • crates/gateway/Cargo.toml
  • crates/gateway/migrations/20260301100000_device_pairing.sql
  • crates/gateway/migrations/20260328093000_ssh_keys_and_targets.sql
  • crates/gateway/migrations/20260328113000_ssh_target_known_host.sql
  • crates/gateway/migrations/20260412180000_api_keys_salt.sql
  • crates/gateway/migrations/20260412180100_auth_audit_log.sql
  • crates/gateway/src/agent_persona.rs
  • crates/gateway/src/approval.rs
  • crates/gateway/src/auth.rs
  • crates/gateway/src/auth_middleware.rs
  • crates/gateway/src/auth_webauthn.rs
  • crates/gateway/src/broadcast.rs
  • crates/gateway/src/channel.rs
  • crates/gateway/src/channel_agent_tools.rs
  • crates/gateway/src/channel_events.rs
  • crates/gateway/src/channel_events/commands/attachments.rs
  • crates/gateway/src/channel_events/commands/control_handlers.rs
  • crates/gateway/src/channel_events/commands/dispatch.rs
  • crates/gateway/src/channel_events/commands/formatting.rs
  • crates/gateway/src/channel_events/commands/location.rs
  • crates/gateway/src/channel_events/commands/media.rs
  • crates/gateway/src/channel_events/commands/mod.rs
  • crates/gateway/src/channel_events/commands/session_handlers.rs
  • crates/gateway/src/channel_events/control.rs
  • crates/gateway/src/channel_events/dispatch.rs
  • crates/gateway/src/channel_events/helpers.rs
  • crates/gateway/src/channel_events/sink.rs
  • crates/gateway/src/channel_events/tests.rs
  • crates/gateway/src/channel_store.rs
  • crates/gateway/src/channel_webhook_middleware.rs
  • crates/gateway/src/chat.rs
  • crates/gateway/src/lib.rs
  • crates/gateway/src/local_llm_setup.rs
  • crates/gateway/src/local_llm_setup/cache.rs
  • crates/gateway/src/local_llm_setup/config.rs
  • crates/gateway/src/local_llm_setup/service.rs
  • crates/gateway/src/local_llm_setup/tests.rs
  • crates/gateway/src/mcp_service.rs
  • crates/gateway/src/mdns.rs
  • crates/gateway/src/message_log_store.rs
  • crates/gateway/src/methods/dispatch.rs
  • crates/gateway/src/methods/mod.rs
  • crates/gateway/src/methods/node.rs
  • crates/gateway/src/methods/pairing.rs
  • crates/gateway/src/methods/services.rs
  • crates/gateway/src/methods/services/admin.rs
  • crates/gateway/src/methods/services/agents.rs
  • crates/gateway/src/methods/services/channels.rs
  • crates/gateway/src/methods/services/core.rs
  • crates/gateway/src/methods/services/sessions.rs
  • crates/gateway/src/methods/services/system.rs
  • crates/gateway/src/methods/voice.rs
  • crates/gateway/src/node_exec.rs
  • crates/gateway/src/nodes.rs
  • crates/gateway/src/onboarding.rs
  • crates/gateway/src/pairing.rs
  • crates/gateway/src/project.rs
  • crates/gateway/src/project_aware_tools.rs
  • crates/gateway/src/push.rs
  • crates/gateway/src/server.rs
  • crates/gateway/src/server/helpers.rs
  • crates/gateway/src/server/hooks.rs

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/voice-modes

Comment @coderabbitai help to get the list of available commands and usage tips.

penso added 8 commits April 24, 2026 15:17
- Extract PttKeyPicker component with useEffect cleanup to prevent
  stale keydown listener leaking after settings panel unmount (P1)
- Move clearTimeout into finally block so fetch timeout is always
  cleared on network errors (P2)
- Add .catch() to AudioContext.resume() to prevent unhandled promise
  rejection when context is closed concurrently (P2)
- Remove continuous console.debug VAD monitor logging from production
  code (P2)
- Stop old vadStream tracks before replacing in vadReacquireStream to
  prevent microphone device leak on stream reacquisition (P1)
- Stop active VAD session when STT becomes unconfigured in
  checkSttStatus (P2)
- Replace hardcoded error strings with i18n keys in transcribeAudio:
  voiceNoSpeech, voiceTranscriptionError, voiceTranscriptionFailed,
  voiceUploadFailed — with en/fr/zh translations (P2)
- Add recordingCancelled flag to prevent cancelled audio from being
  transcribed: the browser flushes a final ondataavailable chunk after
  mediaRecorder.stop(), repopulating audioChunks after cancel (P1)
- Guard vadReacquireStream with vadReacquiring flag to prevent
  concurrent getUserMedia calls at 60fps when a mic track dies (P1)
- Fix PTT race condition: quick key tap could orphan a recording when
  getUserMedia resolves after keyup. Now sets recordingCancelled flag
  in onPttKeyUp when isStarting, and startRecording checks the flag
  after getUserMedia resolves to bail out cleanly (P1)
- Fix MediaStreamSourceNode leak in vadReacquireStream: disconnect old
  source node before connecting a new one on stream reacquisition.
  Track source node in vadSourceNode variable, clean up in stopVad (P2)
- Guard vadStartContinuousRecorder against being called while a
  recorder is already in "recording" state, preventing silent audio
  loss from overwriting an active recorder (P1)
- Fix onTtsPlay to null vadMediaRecorder before calling stop() via a
  local reference, closing the window where vadMonitorLoop could see
  null and start a concurrent recorder before onstop fires (P1)
Add vadStarting flag so a second click while getUserMedia is in-flight
is ignored, preventing two parallel VAD sessions from being created
with leaked streams and racing shared state.
Escape handler leak

- Add isRecording/isStarting guard to startVad so clicking the VAD
  button during an active toggle recording is ignored, preventing
  audioChunks corruption from concurrent recorders (P1)
- Extract anonymous Escape keydown and mic keydown handlers into named
  functions (onEscapeKeydown, onMicKeydown) so they can be properly
  removed in teardownVoiceInput, preventing listener accumulation
  across SPA mount cycles (P1)
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.

4 participants