Skip to content

feat(cli): add experimental voice gateway - #8429

Merged
cv merged 10 commits into
mainfrom
codex/issue-8378-voice-gateway
Aug 6, 2026
Merged

feat(cli): add experimental voice gateway#8429
cv merged 10 commits into
mainfrom
codex/issue-8378-voice-gateway

Conversation

@jyaunches

@jyaunches jyaunches commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Add a hidden, explicitly gated OpenClaw voice gateway that routes one committed text turn for one authenticated runtime session. The adapter keeps routing authority in NemoClaw, binds only to loopback, and exposes no supported public product surface.

Related Issue

Fixes #8378

Changes

  • Add nemoclaw internal voice-gateway serve, gated by the exact value NEMOCLAW_EXPERIMENTAL_VOICE_GATEWAY=1 before credentials are read or a socket is opened.
  • Add a loopback HTTP/NDJSON adapter with deployment bearer authentication, per-session grants, bounded inputs and outputs, timeouts, disconnect cleanup, and content-free failure responses.
  • Route through a fixed loopback OpenClaw WebSocket endpoint using protocol v4 chat.send, generated session and idempotency identifiers, and exact session/run event filtering.
  • Require owner-only, no-follow credential files and document startup-only credential loading and restart-based rotation.
  • Add unit, integration, package-contract, and CLI coverage, including a pinned fake runtime adapter and negative security paths.
  • Document the experimental command, flags, defaults, trust boundaries, lifecycle, limitations, and non-goals.

The service boundary is required by #8378 so a separately authenticated voice runtime can submit one committed turn without receiving authority to choose an agent, sandbox, upstream endpoint, OpenClaw session, or run. The session service and integration tests protect that boundary.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification:
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: Codex Desktop reviewed the completed diff against the repository's nine security categories. Result: PASS. Coverage includes authentication and authorization, credential-file safety, fixed routing authority, SSRF avoidance, bounded resource use, timeout and disconnect cleanup, diagnostic redaction, and negative-path tests.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Documentation Writer Review

  • Documentation writer subagent reviewed the completed changes
  • Result: docs-updated
  • Evidence: docs/reference/commands.mdx; exact-head review PASS with no actionable findings; npm run docs completed with 0 errors and 2 existing unprinted warnings.
  • Agent: Codex Desktop

DGX Station Hardware Evidence

  • Tested on DGX Station
  • Tested commit:
  • Station profile/scenario:
  • Result:
  • Supporting evidence:

Verification

  • PR description includes a Signed-off-by: line and every commit appears as Verified in GitHub
  • Normal pre-commit, commit-msg, and pre-push hooks passed, or npm run validate:pr passed after refreshing origin/main when hooks were skipped or unavailable
  • Targeted behavior tests pass for the current change set, or tests are marked not applicable above — npm run typecheck:cli; 37/37 focused CLI tests; 10/10 focused integration tests; npm run build:cli; 5/5 package metadata contract tests; npm run checks:repository; and npm run docs all pass on the rebased change set.
  • Applicable broad gate passed — npm test for broad runtime/test-harness changes; npm run check for repo-wide validation/coverage changes — Initial npm test: 26,648 passed and 19 failed. Three documentation-parity failures caused by this change were fixed and their focused suites pass. The remaining 16 failures are unrelated environment/current-main failures involving future npm registry artifacts and package output, DGX detection, Hermes privilege topology, remediation fixtures, and plugin type-check output. No maintainer waiver is claimed.
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only) — 0 errors and 2 existing unprinted warnings.
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Julie Yaunches jyaunches@nvidia.com

Summary by CodeRabbit

  • New Features
    • Added an experimental OpenClaw voice gateway with authenticated sessions, text turns, streamed responses, and session closure.
    • Added secure credential-file validation and loopback-only gateway connectivity.
    • Added safeguards for session access, request sizes, timeouts, malformed requests, and unsupported voice features.
  • Documentation
    • Documented setup requirements, feature gating, configuration, limits, responses, cleanup, and unsupported integrations.
  • Tests
    • Added comprehensive unit, integration, CLI, security, and error-handling coverage.

Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
@jyaunches jyaunches added integration: openclaw OpenClaw integration behavior experimental Has architectural implications, requires consensus from all maintainers before landing area: cli Command line interface, flags, terminal UX, or output area: integrations Third-party service integration behavior area: security Security controls, permissions, secrets, or hardening feature PR adds or expands user-visible functionality voiceclaw labels Aug 6, 2026
@jyaunches jyaunches self-assigned this Aug 6, 2026
@jyaunches jyaunches added feature PR adds or expands user-visible functionality voiceclaw labels Aug 6, 2026
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an opt-in OpenClaw voice gateway with secure credential loading, authenticated expiring sessions, one committed streamed text turn, an HTTP/NDJSON interface, graceful lifecycle handling, CLI integration, documentation, and comprehensive tests.

Changes

Voice gateway

Layer / File(s) Summary
Contracts and credential validation
src/lib/voice-gateway/contracts.ts, src/lib/voice-gateway/credential-file.ts, src/lib/voice-gateway/credential-file.test.ts
Defines gateway contracts and validates owner-only bearer credential files.
OpenClaw WebSocket transport
src/lib/voice-gateway/openclaw-client.ts, src/lib/voice-gateway/openclaw-client.test.ts
Adds authenticated WebSocket turns, event filtering, size limits, timeouts, failure handling, and socket cleanup.
Session and turn lifecycle
src/lib/voice-gateway/session-service.ts, src/lib/voice-gateway/session-service.test.ts
Adds expiring authenticated sessions, one committed turn per session, ordered response events, limits, diagnostics, and cleanup.
HTTP and NDJSON boundary
src/lib/adapters/http/voice-gateway-server.ts, test/fixtures/voice-gateway/pinned-runtime-adapter.ts, test/voice-gateway-integration.test.ts
Adds authenticated health, session, turn, and close routes with strict validation and streamed NDJSON responses.
CLI and server lifecycle
src/commands/internal/voice-gateway/serve.ts, src/lib/actions/voice-gateway/serve.ts, src/lib/actions/voice-gateway/serve.test.ts, test/internal-cli.test.ts, test/package-contract/cli/oclif-metadata.test.ts, docs/reference/commands.mdx, ci/source-architecture-budget.json
Adds the hidden feature-gated command, loopback server startup, graceful shutdown, documentation, CLI checks, and the updated fan-in budget.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Runtime
  participant VoiceGatewayServer
  participant VoiceSessionService
  participant OpenClawVoiceClient
  Runtime->>VoiceGatewayServer: Create authenticated session
  VoiceGatewayServer->>VoiceSessionService: Create session
  Runtime->>VoiceGatewayServer: Submit one text turn
  VoiceGatewayServer->>VoiceSessionService: Commit turn
  VoiceSessionService->>OpenClawVoiceClient: Send chat turn
  OpenClawVoiceClient-->>VoiceSessionService: Stream agent events
  VoiceSessionService-->>VoiceGatewayServer: Return ordered events
  VoiceGatewayServer-->>Runtime: Stream NDJSON response
Loading

Possibly related PRs

  • NVIDIA/NemoClaw#8427: Evolves the same voice-gateway command, server, session service, OpenClaw client, credential handling, and tests.

Suggested reviewers: apurvvkumaria

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: adding an experimental voice gateway to the CLI.
Linked Issues check ✅ Passed The implementation and tests address the opt-in loopback gateway, secure session routing, limits, cleanup, diagnostics, and exclusions in issue #8378.
Out of Scope Changes check ✅ Passed The code, documentation, tests, and CI budget update directly support the experimental voice gateway requirements in issue #8378.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/issue-8378-voice-gateway

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

@github-code-quality

github-code-quality Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in commit 79b1425 in the codex/issue-8378-voi... branch remains at 96%, unchanged from commit 0d33997 in the main branch.

TypeScript / code-coverage/cli

The overall coverage in commit 79b1425 in the codex/issue-8378-voi... branch remains at 81%, unchanged from commit 0d33997 in the main branch.

Show a code coverage summary of the most impacted files.
File main 0d33997 codex/issue-8378-voi... 79b1425 +/-
src/lib/inferen...a/model-size.ts 96% 83% -13%
src/lib/onboard...shboard-port.ts 96% 90% -6%
src/lib/shields...nsition-lock.ts 84% 83% -1%
src/lib/policy/...ne-exclusion.ts 92% 96% +4%
src/lib/adapter...teway-server.ts 0% 79% +79%
src/lib/voice-g...nclaw-client.ts 0% 83% +83%
src/lib/voice-g...dential-file.ts 0% 88% +88%
src/lib/actions...ateway/serve.ts 0% 91% +91%
src/lib/voice-g...sion-service.ts 0% 93% +93%
src/lib/voice-g...ay/contracts.ts 0% 100% +100%

Updated August 06, 2026 15:24 UTC

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (3)
src/lib/voice-gateway/openclaw-client.ts (2)

114-285: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Split runTurn into smaller units.

runTurn spans about 170 lines and declares eight nested closures that share mutable turn state: clearPending, request, finish, cancelCurrent, handleChat, onmessage, onerror, and onclose. The coding guidelines require low function complexity.

Extract the per-turn state and its closures into a private turn-scoped helper class or factory, for example a TurnSession that owns pending, activeRunId, previousText, and terminal. runTurn then performs the handshake, sends chat.send, and awaits the terminal result.

This refactor also gives the transport teardown in the comment on finish a single owner.

Based on coding guidelines: "Keep function complexity low."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/voice-gateway/openclaw-client.ts` around lines 114 - 285, Refactor
runTurn into smaller units by introducing a private turn-scoped helper such as
TurnSession to own pending requests, queued events, activeRunId, previousText,
terminal state, request handling, event handlers, finish, and cancellation. Keep
runTurn focused on socket creation, handshake, chat.send, and awaiting the
terminal result, while preserving existing outcomes and event behavior. Make the
helper the single owner of transport teardown associated with finish.

Source: Coding guidelines


218-225: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Bound the queued chat events by bytes, not only by count.

MAX_QUEUED_CHAT_EVENTS caps the queue at 128 entries. Each entry can carry text close to MAX_NATIVE_FRAME_BYTES, which is about 2 MB. The queue can therefore retain roughly 270 MB before activeRunId is set. The window is short, and it requires a hostile or faulty gateway, so this is a resource bound rather than an exploitable defect.

Track the accumulated queued text size and fail with agent_protocol_error once it passes VOICE_GATEWAY_MAX_RESPONSE_BYTES.

🛠️ Proposed fix
+    let queuedChatBytes = 0;
       if (activeRunId === null) {
-        if (queuedChatEvents.length >= MAX_QUEUED_CHAT_EVENTS) {
+        queuedChatBytes += Buffer.byteLength(chat.text);
+        if (
+          queuedChatEvents.length >= MAX_QUEUED_CHAT_EVENTS ||
+          queuedChatBytes > VOICE_GATEWAY_MAX_RESPONSE_BYTES
+        ) {
           finish({ outcome: "failed", reason: "agent_protocol_error" });
           socket.close();
           return;
         }
         queuedChatEvents.push(chat);
       } else handleChat(chat);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/voice-gateway/openclaw-client.ts` around lines 218 - 225, Update the
queueing logic around activeRunId and queuedChatEvents to track the accumulated
text byte size of queued chat events, using the same encoding used for
payload-size limits. Reject the event and finish with outcome "failed" and
reason "agent_protocol_error" before enqueueing when the accumulated size
exceeds VOICE_GATEWAY_MAX_RESPONSE_BYTES, while preserving the existing
MAX_QUEUED_CHAT_EVENTS limit and reset the byte counter when the queued events
are drained or otherwise discarded.
src/lib/adapters/http/voice-gateway-server.ts (1)

90-93: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Accept application/json with media-type parameters.

Line 90 compares the raw header with strict equality. A conforming client that sends content-type: application/json; charset=utf-8 receives 415. Parse the media type instead of comparing the full header value.

♻️ Proposed fix for the media-type gate
-    if (request.headers["content-type"] !== "application/json") {
+    const mediaType = (request.headers["content-type"] ?? "").split(";")[0].trim().toLowerCase();
+    if (mediaType !== "application/json") {
       reject(new BodyError(415));
       return;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/adapters/http/voice-gateway-server.ts` around lines 90 - 93, Update
the content-type validation in the request handling flow to parse the header’s
media type and accept application/json when parameters such as charset are
present, while continuing to reject other media types with BodyError(415).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/actions/voice-gateway/serve.test.ts`:
- Around line 77-82: Remove the typeof check in the fake listener’s listen
method and invoke the final argument directly as the callback, preserving the
existing argument capture, listening state, and fluent return behavior.

In `@src/lib/actions/voice-gateway/serve.ts`:
- Around line 69-84: Update the OpenClaw gateway URL validation in the
surrounding serve flow to require authenticated encrypted transport instead of
allowing ws:// loopback connections: accept only wss: URLs with appropriate peer
validation, or route the credential-bearing OpenClawVoiceClient connection
through authenticated local IPC. Preserve the existing credential-free,
explicit-port, /ws-path, and URL-field restrictions.

In `@src/lib/adapters/http/voice-gateway-server.ts`:
- Around line 142-144: Update the request-body listeners near the existing
`aborted`, `error`, and `end` handlers to remove the deprecated `aborted`
listener and add a `close` listener that calls `finish(new BodyError(400))` only
when `request.readableEnded` is false. Preserve the existing `settled` guard
used by the normal `end` completion path.

In `@src/lib/voice-gateway/credential-file.ts`:
- Around line 43-53: Update the error check in the credential file open logic
around fs.openSync to treat both ELOOP and EMLINK as symbolic-link errors.
Preserve the existing descriptive error and rethrow behavior for all other error
codes.

In `@src/lib/voice-gateway/openclaw-client.test.ts`:
- Around line 34-58: Refactor FakeWebSocket.send to remove all method-dispatch
conditionals and the oversizedFrameAfterConnect flag. Add a per-method Handler
map supplied by each test, make send queue the matching handler linearly, and
expose respond and event for handlers; move connect/chat.send response and frame
scenarios into the individual test handlers, removing firstReply and finalReply
from the fake state.
- Around line 146-163: Update the revocation test around
OpenClawVoiceClient.runTurn to be asynchronous: await the FakeWebSocket open
handshake before calling client.close(), retain the runTurn promise, and assert
its resolved outcome after revocation. Add the required vi import from vitest if
needed to coordinate the handshake, while preserving the existing socket-closed
assertion.

In `@src/lib/voice-gateway/openclaw-client.ts`:
- Around line 160-167: Update the turn lifecycle around finish, settleOpen, and
the socket reference so finish settles any pending open handshake, closes the
WebSocket, and then resolves the terminal result. Assign settleOpen alongside
rejectOpen during handshake setup, and remove redundant direct socket.close()
calls from terminal branches so finish is the single transport-release owner.

In `@test/voice-gateway-integration.test.ts`:
- Around line 51-63: Move the conditional helper logic from the test file into a
new server-harness fixture module alongside PinnedVoiceRuntimeAdapter. Relocate
listen and the afterEach server-cleanup loop into exported listen and
closeListeningServers helpers, then update the test to import and call them so
the test file contains only linear helper calls.

---

Nitpick comments:
In `@src/lib/adapters/http/voice-gateway-server.ts`:
- Around line 90-93: Update the content-type validation in the request handling
flow to parse the header’s media type and accept application/json when
parameters such as charset are present, while continuing to reject other media
types with BodyError(415).

In `@src/lib/voice-gateway/openclaw-client.ts`:
- Around line 114-285: Refactor runTurn into smaller units by introducing a
private turn-scoped helper such as TurnSession to own pending requests, queued
events, activeRunId, previousText, terminal state, request handling, event
handlers, finish, and cancellation. Keep runTurn focused on socket creation,
handshake, chat.send, and awaiting the terminal result, while preserving
existing outcomes and event behavior. Make the helper the single owner of
transport teardown associated with finish.
- Around line 218-225: Update the queueing logic around activeRunId and
queuedChatEvents to track the accumulated text byte size of queued chat events,
using the same encoding used for payload-size limits. Reject the event and
finish with outcome "failed" and reason "agent_protocol_error" before enqueueing
when the accumulated size exceeds VOICE_GATEWAY_MAX_RESPONSE_BYTES, while
preserving the existing MAX_QUEUED_CHAT_EVENTS limit and reset the byte counter
when the queued events are drained or otherwise discarded.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 0e53fb0c-8aa2-4dab-bc79-17306f12e96d

📥 Commits

Reviewing files that changed from the base of the PR and between 26bd9f5 and 85ec008.

📒 Files selected for processing (17)
  • ci/source-architecture-budget.json
  • docs/reference/commands.mdx
  • src/commands/internal/voice-gateway/serve.ts
  • src/lib/actions/voice-gateway/serve.test.ts
  • src/lib/actions/voice-gateway/serve.ts
  • src/lib/adapters/http/voice-gateway-server.ts
  • src/lib/voice-gateway/contracts.ts
  • src/lib/voice-gateway/credential-file.test.ts
  • src/lib/voice-gateway/credential-file.ts
  • src/lib/voice-gateway/openclaw-client.test.ts
  • src/lib/voice-gateway/openclaw-client.ts
  • src/lib/voice-gateway/session-service.test.ts
  • src/lib/voice-gateway/session-service.ts
  • test/fixtures/voice-gateway/pinned-runtime-adapter.ts
  • test/internal-cli.test.ts
  • test/package-contract/cli/oclif-metadata.test.ts
  • test/voice-gateway-integration.test.ts

Comment thread src/lib/actions/voice-gateway/serve.test.ts
Comment thread src/lib/actions/voice-gateway/serve.ts
Comment thread src/lib/adapters/http/voice-gateway-server.ts Outdated
Comment thread src/lib/voice-gateway/credential-file.ts
Comment thread src/lib/voice-gateway/openclaw-client.test.ts
Comment thread src/lib/voice-gateway/openclaw-client.test.ts Outdated
Comment thread src/lib/voice-gateway/openclaw-client.ts
Comment thread test/voice-gateway-integration.test.ts
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings reported

Advisor assessment: No blocking advisor findings reported
Next action: No advisor follow-up needed.
Findings: 0 blockers · 0 warnings · 0 suggestions

Model lanes

  • GPT-5.6 Terra (primary): Completed · high confidence · 0 blockers · 0 warnings · 0 suggestions
  • Nemotron 3 Ultra (second opinion): Completed · high confidence · 0 blockers · 0 warnings · 0 suggestions
  • Model comparison: normalized findings match; normalized terminology decisions differ; normalized E2E selections differ; severity counts match.
8 terminology differences from the second opinion

Advisory only. These are normalized differences from the primary terminology receipt.

  • pinned runtime at test/fixtures/voice-gateway/pinned-runtime-adapter.ts:55: selected only by the second-opinion lane as established.
  • NDJSON at src/lib/adapters/http/voice-gateway-server.ts:210: selected only by the second-opinion lane as established.
  • turnId at src/lib/voice-gateway/contracts.ts:25: selected only by the second-opinion lane as justified.
  • committed turn at src/lib/voice-gateway/session-service.ts:69: selected only by the second-opinion lane as justified.
  • voice gateway at docs/reference/commands.mdx:3871: selected only by the second-opinion lane as justified.
  • voice session at src/lib/voice-gateway/session-service.ts:69: selected only by the second-opinion lane as justified.
  • runtimeConversationId at src/lib/voice-gateway/contracts.ts:78: selected only by the second-opinion lane as justified.
  • deployment credential at docs/reference/commands.mdx:3897: selected only by the second-opinion lane as justified.
2 additional E2E selections from the second opinion

Advisory only. The primary lane did not select these E2E jobs or targets.

  • openclaw-inference-switch: The completed second-opinion lane identified E2E coverage that the primary lane omitted.
  • openclaw-discord-pairing: The completed second-opinion lane identified E2E coverage that the primary lane omitted.

Second-opinion terminology and E2E selections are advisory. Live E2E does not run automatically for pull requests.

4 semantic terminology decisions

Terminology decisions are advisory. They affect the assessment only when a separate finding identifies concrete semantic impact.

  • define — session grant at docs/reference/commands.mdx:3928: Define session grant as the per-session bearer and retain the documented lifetime and removal rules.
  • define — committed text turn at docs/reference/commands.mdx:3887: Define committed text turn as the single protocol input for one voice session.
  • justified — private HTTP adapter at docs/reference/commands.mdx:3885: Retain private because the documented loopback and no-ingress restrictions define the contrast.
  • define — experimental adapter at docs/reference/commands.mdx:3869: Define experimental adapter for this gated integration and do not use agent adapter for this different protocol boundary.

E2E guidance

Advisory only. A maintainer can dispatch the default E2E suite against this exact revision.

Recommended E2E: cloud-inference, security-posture

Workflow run details

This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/lib/voice-gateway/openclaw-client.test.ts (1)

46-56: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Exercise events that arrive before the chat.send response.

FakeWebSocket always calls respond on Line 49 before it queues chat events on Lines 50-55. This does not exercise the queuedChatEvents path in OpenClawVoiceClient.runTurn, which handles matching events received before activeRunId is set.

Add a test-specific scenario that emits a matching event before the response, then assert the ordered result through runTurn.

As per path instructions, verify this through the public runTurn result and emitted events rather than fake internals.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/voice-gateway/openclaw-client.test.ts` around lines 46 - 56, Update
the FakeWebSocket chat.send scenario and its test to emit a matching
expected-run event before respond assigns the run ID, exercising
queuedChatEvents in OpenClawVoiceClient.runTurn. Verify through the public
runTurn result and emitted event ordering, without asserting fake websocket
internals.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/lib/voice-gateway/openclaw-client.test.ts`:
- Around line 46-56: Update the FakeWebSocket chat.send scenario and its test to
emit a matching expected-run event before respond assigns the run ID, exercising
queuedChatEvents in OpenClawVoiceClient.runTurn. Verify through the public
runTurn result and emitted event ordering, without asserting fake websocket
internals.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8f7b35f9-7b08-4975-aef7-3f3e247cb0c1

📥 Commits

Reviewing files that changed from the base of the PR and between 85ec008 and 19db569.

📒 Files selected for processing (3)
  • src/lib/actions/voice-gateway/serve.test.ts
  • src/lib/voice-gateway/openclaw-client.test.ts
  • test/voice-gateway-integration.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/actions/voice-gateway/serve.test.ts

Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/lib/voice-gateway/credential-file.test.ts (1)

95-97: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Assert that credential errors do not expose bearer material.

The current matcher accepts an error that contains both message and CREDENTIAL. Assert that each caught error message excludes CREDENTIAL. This verifies the content-free failure contract.

Proposed test update
   ])("rejects a $name before returning credential material (`#8378`)", ({ arrange, message }) => {
-    expect(() => readPrivateBearerFile(arrange(), "Test credential")).toThrow(message);
+    let error: unknown;
+    try {
+      readPrivateBearerFile(arrange(), "Test credential");
+    } catch (caught) {
+      error = caught;
+    }
+
+    expect(error).toBeInstanceOf(Error);
+    expect((error as Error).message).toContain(message);
+    expect((error as Error).message).not.toContain(CREDENTIAL);
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/voice-gateway/credential-file.test.ts` around lines 95 - 97, Update
the parameterized test around readPrivateBearerFile to capture the thrown error
and assert its message excludes the literal CREDENTIAL, while retaining the
existing expected-message assertion. Ensure every credential rejection case
verifies that bearer material is not exposed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/lib/voice-gateway/credential-file.test.ts`:
- Around line 95-97: Update the parameterized test around readPrivateBearerFile
to capture the thrown error and assert its message excludes the literal
CREDENTIAL, while retaining the existing expected-message assertion. Ensure
every credential rejection case verifies that bearer material is not exposed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: fcdefd2c-db2d-46dd-831b-537018ce76c6

📥 Commits

Reviewing files that changed from the base of the PR and between 3311296 and 8c5759b.

📒 Files selected for processing (17)
  • ci/source-architecture-budget.json
  • docs/reference/commands.mdx
  • src/commands/internal/voice-gateway/serve.ts
  • src/lib/actions/voice-gateway/serve.test.ts
  • src/lib/actions/voice-gateway/serve.ts
  • src/lib/adapters/http/voice-gateway-server.ts
  • src/lib/voice-gateway/contracts.ts
  • src/lib/voice-gateway/credential-file.test.ts
  • src/lib/voice-gateway/credential-file.ts
  • src/lib/voice-gateway/openclaw-client.test.ts
  • src/lib/voice-gateway/openclaw-client.ts
  • src/lib/voice-gateway/session-service.test.ts
  • src/lib/voice-gateway/session-service.ts
  • test/fixtures/voice-gateway/pinned-runtime-adapter.ts
  • test/internal-cli.test.ts
  • test/package-contract/cli/oclif-metadata.test.ts
  • test/voice-gateway-integration.test.ts
🚧 Files skipped from review as they are similar to previous changes (14)
  • ci/source-architecture-budget.json
  • docs/reference/commands.mdx
  • src/lib/actions/voice-gateway/serve.ts
  • src/lib/voice-gateway/contracts.ts
  • src/lib/actions/voice-gateway/serve.test.ts
  • test/fixtures/voice-gateway/pinned-runtime-adapter.ts
  • src/commands/internal/voice-gateway/serve.ts
  • src/lib/voice-gateway/credential-file.ts
  • test/internal-cli.test.ts
  • src/lib/voice-gateway/session-service.test.ts
  • test/package-contract/cli/oclif-metadata.test.ts
  • src/lib/voice-gateway/session-service.ts
  • test/voice-gateway-integration.test.ts
  • src/lib/voice-gateway/openclaw-client.ts

Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
@jyaunches
jyaunches requested a review from cv August 6, 2026 15:35
@cv
cv merged commit e46017a into main Aug 6, 2026
64 of 68 checks passed
@cv
cv deleted the codex/issue-8378-voice-gateway branch August 6, 2026 15:40
@cjagwani cjagwani mentioned this pull request Aug 7, 2026
23 tasks
cjagwani added a commit that referenced this pull request Aug 7, 2026
<!-- markdownlint-disable MD041 -->
## Summary

Adds the canonical dated changelog entry required before cutting
`v0.0.104`.
The entry reconciles user-facing changes merged from `v0.0.103` through
`8d2b86aaf44968b4f7bc3b714222a73bd28e0403` while excluding hidden and
experimental product surfaces.

## Changes

- Added `docs/changelog/2026-08-06.mdx` with the exact `## v0.0.104`
heading and release themes for local inference, private endpoints,
network policy, state authority, lifecycle recovery, uninstall, Hermes,
MCP diagnostics, credential safety, and installation guidance.
- Source summary links:
- [#8399](#8399) ->
`docs/changelog/2026-08-06.mdx`: fixed DGX Spark local serving profiles.
- [#8418](#8418) ->
`docs/changelog/2026-08-06.mdx`: durable llama.cpp lifecycle management.
- [#8422](#8422) ->
`docs/changelog/2026-08-06.mdx`: recoverable llama.cpp receipt
publication.
- [#8402](#8402) ->
`docs/changelog/2026-08-06.mdx`: remediable DGX Spark storage admission.
- [#8391](#8391) ->
`docs/changelog/2026-08-06.mdx`: host-local serving recipe contracts.
- [#8401](#8401) ->
`docs/changelog/2026-08-06.mdx`: serving profile lifecycle provenance.
- [#8322](#8322) ->
`docs/changelog/2026-08-06.mdx`: guarded llama.cpp route compatibility.
- [#8272](#8272) ->
`docs/changelog/2026-08-06.mdx`: explicitly trusted private endpoints
with stable policy pins and CA trust.
- [#8431](#8431) ->
`docs/changelog/2026-08-06.mdx`: Personal onboarding policy tier and its
trust boundary.
- [#8143](#8143) ->
`docs/changelog/2026-08-06.mdx`: manifest-derived state authority.
- [#7859](#7859) ->
`docs/changelog/2026-08-06.mdx`: side-effect-free lifecycle lock
timeouts.
- [#8262](#8262) ->
`docs/changelog/2026-08-06.mdx`: managed gateway lease waiting.
- [#8339](#8339) ->
`docs/changelog/2026-08-06.mdx`: continued journaled rebuild recreation.
- [#8373](#8373) ->
`docs/changelog/2026-08-06.mdx`: restore readiness after compatibility
decisions.
- [#8443](#8443) ->
`docs/changelog/2026-08-06.mdx`: fail-closed malformed registry
handling.
- [#8419](#8419) ->
`docs/changelog/2026-08-06.mdx`: bounded recovery for a gateway that
never served.
- [#8486](#8486) ->
`docs/changelog/2026-08-06.mdx`: target-scoped registry recovery.
- [#8259](#8259) ->
`docs/changelog/2026-08-06.mdx`: scoped uninstall ordering and retry
safety.
- [#8457](#8457) ->
`docs/changelog/2026-08-06.mdx`: desktop metadata exclusion during
uninstall.
- [#8026](#8026) ->
`docs/changelog/2026-08-06.mdx`: typed Hermes configuration policy.
- [#8242](#8242) ->
`docs/changelog/2026-08-06.mdx`: Hermes WhatsApp session diagnostics.
- [#8344](#8344) ->
`docs/changelog/2026-08-06.mdx`: patched Hermes image and dependency
checks.
- [#8491](#8491) ->
`docs/changelog/2026-08-06.mdx`: bounded MCP discovery timeout.
- [#8490](#8490) ->
`docs/changelog/2026-08-06.mdx`: MCP shadow diagnostics.
- [#7619](#7619) ->
`docs/changelog/2026-08-06.mdx`: web-search credential isolation.
- [#8476](#8476) ->
`docs/changelog/2026-08-06.mdx`: stable preflight advisory identifiers.
- [#8452](#8452) ->
`docs/changelog/2026-08-06.mdx`: user-local CLI resolution.
- [#8481](#8481) ->
`docs/changelog/2026-08-06.mdx`: remote network-policy terminal
guidance.
- Product-scope exclusions:
[#8429](#8429) remains
experimental; [#8261](#8261)
remains feature-gated; and portable-profile changes
[#8408](#8408),
[#8415](#8415),
[#8446](#8446),
[#8458](#8458),
[#8462](#8462), and
[#8506](#8506) are not promoted
as supported product surfaces.

## Type of Change

- [ ] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [x] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates

- [ ] Tests added or updated for changed behavior
- [x] Existing tests cover changed behavior — justification: `npx vitest
run test/changelog-docs.test.ts` passed 6/6 and validates dated
changelog structure and published links.
- [ ] Tests not applicable — justification:
- [x] Docs updated for user-facing behavior changes
- [ ] Docs not applicable — justification:
- [ ] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [ ] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification:
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Documentation Writer Review

- [x] Documentation writer subagent reviewed the completed changes
- Result: `docs-updated`
- Evidence: `docs/changelog/2026-08-06.mdx`; release-range scope,
writing rules, documentation style, skip terms, exact names,
threat-boundary wording, and published routes reviewed; changelog tests
and docs build passed.
- Agent: Codex Desktop
<!-- docs-review-head-sha: 02b51ae -->
<!-- docs-review-agents-blob-sha: c69aad4 -->

## DGX Station Hardware Evidence

- [ ] Tested on DGX Station
- Tested commit: Not applicable; no DGX Station host preparation script
changed.
- Station profile/scenario: Not applicable.
- Result: Not applicable.
- Supporting evidence: Not applicable.

## Verification

- [x] PR description includes a `Signed-off-by:` line and every commit
appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run validate:pr` passed after refreshing `origin/main` when hooks
were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — command/result or justification: `npx
vitest run test/changelog-docs.test.ts` passed 6/6.
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result: Not applicable to a single
changelog entry.
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [x] `npm run docs` builds without warnings (doc changes only)
- [x] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

The new dated changelog file includes the required parser-safe SPDX
header and intentionally has no frontmatter, matching the changelog
contract and existing entries.

---
Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
  * Added release notes for v0.0.104.
* Documented fixes for local model runtimes, private endpoints, network
policies, state recovery, uninstall behavior, safety updates, MCP
diagnostics, credential isolation, and installation guidance.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command line interface, flags, terminal UX, or output area: integrations Third-party service integration behavior area: security Security controls, permissions, secrets, or hardening experimental Has architectural implications, requires consensus from all maintainers before landing feature PR adds or expands user-visible functionality integration: openclaw OpenClaw integration behavior voiceclaw

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Voice gateway] Route one committed turn for an authenticated runtime session

2 participants