Demo Silent Speech from OpenPhone AI Home#135
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
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. |
There was a problem hiding this comment.
💡 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".
| if (granted && startAfterCameraPermission) { | ||
| startAfterCameraPermission = false | ||
| silentSpeechClient?.start() |
There was a problem hiding this comment.
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 👍 / 👎.
| AssistantOutput output = explicitAssistantOutput(payload); | ||
| if (output != null) { | ||
| emitRuntimeOutput(sessionKeyFromPayload(payload), output, true); | ||
| return; |
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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 👍 / 👎.
| if (!output.sessionId.equals(surface.sessionId) | ||
| || (!sessionKey.isEmpty() && !sessionKey.contains(output.sessionId))) { |
There was a problem hiding this comment.
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 👍 / 👎.
| 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."); |
There was a problem hiding this comment.
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 👍 / 👎.
| } catch (_: RuntimeException) { | ||
| false |
There was a problem hiding this comment.
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 👍 / 👎.
| if (terminal && (!unread | ||
| || now - session.updatedAtMillis > TERMINAL_VISIBILITY_MILLIS)) { |
There was a problem hiding this comment.
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 👍 / 👎.
Summary
Validation
./scripts/check.shgit diff --checkDevice 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.