Skip to content

Demo Silent Speech from OpenPhone AI Home#135

Open
adamcohenhillel wants to merge 35 commits into
mainfrom
demo/silent-speech-ai-home
Open

Demo Silent Speech from OpenPhone AI Home#135
adamcohenhillel wants to merge 35 commits into
mainfrom
demo/silent-speech-ai-home

Conversation

@adamcohenhillel

@adamcohenhillel adamcohenhillel commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add the iOS-style hold-to-record Silent Speech composer to AI Home
  • add first-class Interfaces/Firebase browser sign-in using loopback PKCE
  • protect durable Firebase credentials with Android Keystore AES-GCM
  • record a real H.264 MP4 and decode it through the authenticated Interfaces API
  • remove the legacy Settings.Secure bearer-token configuration path
  • forward decoded text into the normal OpenPhone agent path

Validation

  • ./scripts/check.sh
  • git diff --check

Device validation

The previous camera/JPEG implementation was exercised on the Pixel 9a. This authenticated MP4 revision still requires the assistant APK build and physical-device sign-in/decode pass.

@vercel

vercel Bot commented Jul 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openphone-docs Ready Ready Preview, Comment Jul 22, 2026 12:10am

Request Review

@adamcohenhillel

Copy link
Copy Markdown
Contributor Author

AOSP validation passed in GCP Lab run 29856868879. The exported APK checksum was verified, source-ref is 89afaaf, and the exact artifact is installed on the physical Pixel 9a. Production web authorization and API support are live; awaiting the user-completed Google OAuth step before final authenticated decode/device evaluation.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 89afaaf8d2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +166 to +168
if (granted && startAfterCameraPermission) {
startAfterCameraPermission = false
silentSpeechClient?.start()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Require a new hold after granting camera permission

When Silent Speech requests CAMERA for the first time, the permission dialog interrupts/cancels the press-and-hold gesture, but this callback immediately starts the camera after the grant. At that point there is no matching pointer release left to call stopAndDecode(), so the front camera can keep recording until the frame cap and then upload/decode without an active hold; clear the pending flag on cancel/finish or ask the user to press and hold again after permission is granted.

Useful? React with 👍 / 👎.

Comment on lines +511 to +514
AssistantOutput output = explicitAssistantOutput(payload);
if (output != null) {
emitRuntimeOutput(sessionKeyFromPayload(payload), output, true);
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Mark output-only chat finals as terminal

When OpenClaw sends a terminal chat frame as an explicit AssistantOutput, this branch emits it and returns before markSessionTerminal(). The session therefore remains in mActiveSessionKeys, so the next socket close or runtime-manager stop will call emitDisconnectedSessions() and report the already-completed run as disconnected/failed; mark the session terminal before returning here.

Useful? React with 👍 / 👎.

surface.revision,
actionId,
input,
"surface:${surface.surfaceId}:${surface.revision}:$actionId",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Bind surface confirmations to the input payload

When a surface action includes editable inputs, this stable idempotency key ignores the input payload. If the user taps a mutating action, gets confirmation, edits a text/choice/toggle value, and taps the same action again before resolving, RuntimeToolBridge reuses the original pending confirmation for this key, so Approve executes the stale first parameters rather than the values being reviewed; include a digest of the merged params, or generate a new key per distinct input, so confirmation remains bound to the exact request.

AGENTS.md reference: AGENTS.md:L36-L38

Useful? React with 👍 / 👎.

Comment on lines +285 to +286
if (!output.sessionId.equals(surface.sessionId)
|| (!sessionKey.isEmpty() && !sessionKey.contains(output.sessionId))) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Match surface output sessions exactly

For explicit AssistantOutput surfaces, this accepts any session_id that is merely a substring of sessionKey. With OpenClaw keys shaped like openphone:<principal>:phone-sess-..., a malformed runtime output can use a prefix such as phone and still persist a surface under a non-existent/wrong session, so later surface action audit/result events are correlated to that wrong session; compare against the extracted phone session id exactly instead of using contains.

Useful? React with 👍 / 👎.

Comment on lines +221 to +223
if ("text_input".equals(type)
&& !ACTION_ID.matcher(node.optString("input_key", "")).matches()) {
return reject("input_key_invalid", "Text input requires a stable input key.");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require input keys for toggles and choices

For choice_group and toggle components, this validation allows input_key to be absent even though the renderer writes selection state into inputs[key] and submits that map with the next action. When a runtime emits one of these controls without an input_key, selecting/toggling stores the empty-string key and mergeValidatedInput() rejects every action with input_key_invalid, so a phone-validated surface can render but cannot be acted on; require a stable key for these input-producing component types.

Useful? React with 👍 / 👎.

Comment on lines +199 to +200
} catch (_: RuntimeException) {
false

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Continue past unusable App Space candidates

When a candidate component exists but startActivity() fails, this catch returns false from the whole method instead of trying the remaining openphone_app_space_components entries. On builds where the first Launcher3 variant is present but disabled or otherwise not launchable while a later fallback is usable, the Apps affordance reports App Space as unavailable; treat this as a failed candidate and continue the loop.

Useful? React with 👍 / 👎.

Comment on lines +261 to +262
if (terminal && (!unread
|| now - session.updatedAtMillis > TERMINAL_VISIBILITY_MILLIS)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep unread runtime sessions visible

For terminal runtime sessions, this condition drops the run once it is older than 24 hours even when unread is still true. Jobs in the same projection keep unread terminal results visible until the user reads or dismisses them, so a runtime answer that completes while the user is away can disappear from AI Home/SystemUI before being seen; only apply the age cutoff after the session is marked read.

Useful? React with 👍 / 👎.

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.

1 participant