Skip to content

feat(ios): record and send voice notes from the chat composer - #100946

Merged
steipete merged 5 commits into
mainfrom
feat/ios-voice-notes-100709
Jul 6, 2026
Merged

steipete merged 5 commits into
mainfrom
feat/ios-voice-notes-100709

Conversation

@steipete

@steipete steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Related: #100709

What Problem This Solves

iOS users have no way to send a quick, fire-and-forget voice message to their agent from the chat composer. Talk mode is a live synchronous session and wake word drives immediate voice turns; neither covers "record 30 seconds now, get an answer whenever it's ready." Channels such as Telegram and WhatsApp already accept voice notes and transcribe them through media understanding — only the first-party app lacked a way to produce one.

Why This Change Was Made

Adds tap-to-record voice notes to the shared chat composer (OpenClawChatUI), reusing the existing chat.send attachment path end to end: a new OpenClawVoiceNoteRecorder state machine (idle → requesting permission → recording → finished/failed) records m4a/AAC mono (24 kHz, 32 kbps) with a named 3-minute cap that auto-finishes, and the finished note is staged as a regular pending attachment (audio/mp4, existing 5 MB bound) sent with the normal send button. No server changes: gateway chat.send already accepts non-image attachments and the reply pipeline already transcribes audio via media understanding. Mic ownership is coordinated, not fought: VoiceWakeManager suppression is reason-scoped (talk, voiceNote) so ending Talk cannot restart wake listening over an active recording; recording is unavailable while Talk or push-to-talk owns the microphone; and enabling Talk cancels an in-flight recording. The voice-note button follows the same attachment-availability gate as the picker, while asynchronous staging and send reconciliation retain the captured attachment draft rather than mutable composer state. The control is injected host-side (same pattern as talkControl), so macOS and other OpenClawKit consumers are unaffected. Non-goals per the tracking issue: playback of sent notes, waveform visualization, slide-to-cancel, and offline queueing of attachments (attachments stay online-only per the outbox contract).

User Impact

iOS users can tap the mic button next to the attachment picker, record up to 3 minutes, cancel or finish, see the note as a "Voice note · m:ss" chip (optionally alongside text), and send it. The sent bubble renders a voice-note row with duration, preserved across same-session history reconciliation and the local transcript cache. Mic permission is requested lazily on first use. If the gateway is offline, the existing attachments-online-only behavior applies unchanged.

Evidence

  • Recorder and attachment regressions cover cancel/cap/permission/start-failure cleanup, staging/oversize/temp-file deletion, wire encoding, captured-draft duration preservation, and attachment-input gating.
  • iOS regressions cover reason-scoped Voice Wake suppression and mutual exclusion with Talk and push-to-talk.
  • Fresh post-rebase autoreview: clean (0.82). SwiftFormat, native i18n inventory, and git diff --check: clean.
  • Exact-head hosted CI: https://github.com/openclaw/openclaw/actions/runs/28828962530 (native i18n, macOS Swift, and iOS build are the merge gates).
  • Real microphone capture/send remains manual physical-device proof; simulator/build evidence is not represented as a physical microphone E2E.

@openclaw-barnacle openclaw-barnacle Bot added app: ios App: ios size: XL maintainer Maintainer-authored PR labels Jul 6, 2026
@clawsweeper

clawsweeper Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 6, 2026, 6:41 PM ET / 22:41 UTC.

Summary
The PR adds iOS voice-note recording from the chat composer, stages recorded audio as chat attachments, coordinates microphone ownership with Talk and Voice Wake, and adds Swift tests plus native localization inventory updates.

PR surface: Other +1121. Total +1121 across 18 files.

Reproducibility: not applicable. as a bug reproduction: this PR adds a new iOS composer capability. Source review confirms current main lacks a voice-note recorder in the first-party iOS composer while the gateway already supports non-image chat attachments.

Review metrics: 2 noteworthy metrics.

  • Public content model field: 1 optional field added. OpenClawChatMessageContent.durationSeconds is serialized through display and transcript cache paths, so reviewers should notice the additive chat-content shape change.
  • Voice-note bounds: 3-minute cap, 5 MB staging limit. The new microphone path relies on these measured limits to keep recorded audio within the existing mobile attachment and gateway media constraints.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #100709
Summary: This PR is the iOS implementation candidate for the canonical mobile async voice-notes feature request, with an Android counterpart open separately.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🐚 platinum hermit
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Add redacted iOS device or simulator proof showing record, finish, staged voice-note chip, and send behavior.
  • [P1] Let the remaining exact-head CI finish or rerun the failed shard before merge.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The PR body has tests/builds but no artifact showing the after-fix voice-note record, finish, staged chip, and send path; add a redacted device/simulator video, terminal/device logs, or live output and update the PR body so ClawSweeper can re-review.

Risk before merge

  • [P1] The core user path still lacks artifact-backed proof: tests exercise the state-machine seams, but no redacted device/simulator recording or logs show microphone permission, capture, staging, and send working together.
  • [P1] CI was not fully settled at review time because one node shard had failed and two checks were still running; this is ordinary pre-merge validation state rather than a diff-specific merge-risk label.

Maintainer options:

  1. Decide the mitigation before merge
    Land a scoped iOS v1 only after maintainers accept the product direction, exact-head CI is green, and redacted device or simulator proof shows record -> staged audio chip -> send through the existing attachment path.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P1] Protected maintainer label plus new native voice feature means the remaining action is maintainer product/merge review and proof, not an automated repair.

Maintainer decision needed

  • Question: Should maintainers accept this scoped iOS async voice-note v1 now, with Android handled by the sibling PR and real device or simulator proof required before merge?
  • Rationale: The implementation is a new first-party native mobile input mode and the PR carries the protected maintainer label, so automation should not decide final product acceptance even though the code path looks coherent.
  • Likely owner: steipete — He owns the linked feature request and has the strongest recent history on the iOS chat, Talk, and voice surfaces touched here.
  • Options:
    • Accept scoped iOS v1 after proof (recommended): Proceed with this iOS-only implementation once redacted record/stage/send proof and exact-head CI are clean.
    • Request narrower follow-up: Ask for a smaller proof or scope pass if maintainers want device capture, duration bounds, or mic ownership tightened before landing.
    • Defer mobile voice notes: Pause the PR and keep the linked tracking issue open if maintainers are not ready to add async voice notes to first-party mobile chat.

Security
Cleared: No concrete security or supply-chain regression was found; the diff does not change workflows, dependencies, lockfiles, auth, secrets, or network permissions.

Review details

Best possible solution:

Land a scoped iOS v1 only after maintainers accept the product direction, exact-head CI is green, and redacted device or simulator proof shows record -> staged audio chip -> send through the existing attachment path.

Do we have a high-confidence way to reproduce the issue?

Not applicable as a bug reproduction: this PR adds a new iOS composer capability. Source review confirms current main lacks a voice-note recorder in the first-party iOS composer while the gateway already supports non-image chat attachments.

Is this the best way to solve the issue?

Yes, this is the right implementation layer if maintainers accept the feature: the native app owns recording and UI, while the existing chat.send attachment and media-understanding path owns server-side handling. The remaining gap is proof of the device-level mic and send path, not a reason to add a new server API.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against fc77c2b04b4d.

Label changes

Label changes:

  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • add feature: ✨ showcase: ClawSweeper spotlight: unusually compelling feature idea for maintainer attention. Async mobile voice notes fill a common on-the-go chat input gap while reusing existing media-understanding instead of adding a new server-side voice path.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The PR body has tests/builds but no artifact showing the after-fix voice-note record, finish, staged chip, and send path; add a redacted device/simulator video, terminal/device logs, or live output and update the PR body so ClawSweeper can re-review.

Label justifications:

  • P2: This is a normal-priority first-party iOS chat feature with limited blast radius and no evidence of an urgent regression or blocked setup path.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • feature: ✨ showcase: ClawSweeper spotlight: unusually compelling feature idea for maintainer attention. Async mobile voice notes fill a common on-the-go chat input gap while reusing existing media-understanding instead of adding a new server-side voice path.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The PR body has tests/builds but no artifact showing the after-fix voice-note record, finish, staged chip, and send path; add a redacted device/simulator video, terminal/device logs, or live output and update the PR body so ClawSweeper can re-review.
Evidence reviewed

PR surface:

Other +1121. Total +1121 across 18 files.

View PR surface stats
Area Files Added Removed Net
Source 0 0 0 0
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 18 1223 102 +1121
Total 18 1223 102 +1121

What I checked:

  • Repository policy read: Root AGENTS.md and apps/ios/AGENTS.md were read fully; the protected maintainer label and iOS native app guidance route this to maintainer review rather than cleanup close. (AGENTS.md:17, fc77c2b04b4d)
  • Protected live PR state: Live GitHub PR data shows the PR is open, assigned to steipete, mergeable but behind, and labeled maintainer, app: ios, size: XL, and P2. (7e78d5c86b2a)
  • iOS host wiring: ChatProTab passes gateway-connected attachment availability into OpenClawChatView and injects a host-owned OpenClawChatVoiceNoteControl backed by NodeAppModel.voiceNoteRecorder. (apps/ios/Sources/Design/ChatProTab.swift:147, 7e78d5c86b2a)
  • Offline attachment gate fixed: The current head gates recording on both composer enabled state and attachment-input availability, addressing the prior review concern about offline-but-queueable chats creating unsendable audio drafts. (apps/shared/OpenClawKit/Sources/OpenClawChatUI/VoiceNoteComposerViews.swift:20, 7e78d5c86b2a)
  • Recorder and staging path: The new recorder has explicit permission, recording, finished, failed, cancel, duration-cap, and AVAudioRecorder-backed capture paths, and the composer stages a completed recording as an attachment before clearing it. (apps/shared/OpenClawKit/Sources/OpenClawChatUI/VoiceNoteRecorder.swift:102, 7e78d5c86b2a)
  • Existing gateway attachment contract: Current main's chat.send path calls parseMessageWithAttachments with acceptNonImage: true, so the iOS implementation can reuse the existing non-image attachment/media-understanding path instead of adding a server method. (src/gateway/server-methods/chat.ts:4200, fc77c2b04b4d)

Likely related people:

  • steipete: He is the PR author/assignee, the linked mobile voice-notes issue owner, and recent merged history shows him touching iOS chat, Talk controls, offline chat sends, and voice delivery surfaces. (role: feature owner and recent area contributor; confidence: high; commits: 2723628dd9, 4b7661e9a0c4, 3e50f41dd6ea; files: apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift, apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatViewModel.swift, apps/ios/Sources/Model/NodeAppModel.swift)
  • marvkr: Merged PR history for the native iOS reskin changed ChatProTab, ChatComposer, ChatView, ChatMessageViews, and Talk UI surfaces that this PR builds on. (role: recent iOS UI contributor; confidence: medium; commits: 5370f64cf09c; files: apps/ios/Sources/Design/ChatProTab.swift, apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift, apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatView.swift)
  • VicZhang6: Merged PR history for the slash-command picker changed the same shared ChatComposer, ChatModels, ChatTransport, and ChatViewModel surfaces used by this voice-note composer work. (role: adjacent chat composer contributor; confidence: medium; commits: 7acfb6353566; files: apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift, apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatModels.swift, apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatViewModel.swift)
  • BunsDev: Merged PR history for iOS chat image attachment processing introduced adjacent ChatViewModel attachment tests and processing paths that this PR extends to audio attachments. (role: attachment path contributor; confidence: medium; commits: faa443a45220; files: apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatViewModel.swift, apps/shared/OpenClawKit/Tests/OpenClawKitTests/ChatViewModelAttachmentTests.swift)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (2 earlier review cycles)
  • reviewed 2026-07-06T15:07:23.336Z sha 25826f4 :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-06T22:21:58.578Z sha f110a66769c29c59a825c827b072135162b31df2 :: needs real behavior proof before merge. :: none

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jul 6, 2026
@steipete steipete self-assigned this Jul 6, 2026
@steipete
steipete force-pushed the feat/ios-voice-notes-100709 branch from 25826f4 to 0e220ee Compare July 6, 2026 22:03

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0e220eeb8a

ℹ️ 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 +38 to +41
.disabled(
!self.isComposerEnabled
|| self.control.isTalkActive
|| self.control.recorder.isRequestingPermission)

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.

P2 Badge Disable voice-note recording when attachments are offline

When the iOS chat is offline but the durable text outbox is available, ChatProTab passes isComposerEnabled: gatewayConnected || canQueueOffline while isAttachmentInputEnabled stays tied to gatewayConnected. This new button only gates on isComposerEnabled, so that offline-but-queueable state lets users record and stage an audio attachment; canSendMessage then refuses to send because attachments require isAttachmentInputEnabled, leaving an unsendable draft until reconnect. Gate voice-note recording on the same attachment-input availability as the paperclip.

Useful? React with 👍 / 👎.

@steipete
steipete force-pushed the feat/ios-voice-notes-100709 branch 2 times, most recently from f110a66 to 7e78d5c Compare July 6, 2026 22:27
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. feature: ✨ showcase ClawSweeper spotlight: unusually compelling feature idea for maintainer attention. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 6, 2026
@steipete
steipete force-pushed the feat/ios-voice-notes-100709 branch from 7e78d5c to a9d4a52 Compare July 6, 2026 22:50
steipete added 5 commits July 6, 2026 18:57
Adds tap-to-record voice notes to the shared chat composer: m4a/AAC mono
capture with a 3-minute cap, lazy mic permission, cancel/finish controls,
audio attachment chips with duration, and a voice-note transcript row.
Voice-wake suppression becomes reason-scoped so recording and Talk cannot
clobber each other. Attachments stay online-only per the outbox contract.

Related: #100709
@steipete
steipete force-pushed the feat/ios-voice-notes-100709 branch from a9d4a52 to d352bf6 Compare July 6, 2026 22:57
@steipete

steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Land-ready review complete for exact head d352bf6837d61d1761abdfd580ff455b862a9ade.

  • Rebased over current iOS composer/link-preview work; only generated localization inventory overlapped the later Android voice-note landing.
  • Fixed push-to-talk/recorder microphone contention, mutable-draft duration reconciliation, attachment staging ownership, and recording while attachment input is unavailable.
  • Fresh final autoreview: clean (0.82). SwiftFormat, native i18n inventory, and git diff --check: clean.
  • Exact-head hosted CI passed: https://github.com/openclaw/openclaw/actions/runs/28828962530 (native i18n, macOS Swift build/tests, iOS simulator build, and repository gates).

Known proof gap: physical microphone permission, record, send, and rendered-bubble interaction remains manual-device proof; automated simulator/build evidence does not claim that physical-device flow.

@steipete
steipete merged commit b978dcc into main Jul 6, 2026
83 of 103 checks passed
@steipete
steipete deleted the feat/ios-voice-notes-100709 branch July 6, 2026 23:14
@steipete

steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 7, 2026
…aw#100946)

* feat(ios): record and send voice notes from the chat composer

Adds tap-to-record voice notes to the shared chat composer: m4a/AAC mono
capture with a 3-minute cap, lazy mic permission, cancel/finish controls,
audio attachment chips with duration, and a voice-note transcript row.
Voice-wake suppression becomes reason-scoped so recording and Talk cannot
clobber each other. Attachments stay online-only per the outbox contract.

Related: openclaw#100709

* fix(ios): harden voice note attachment staging

* chore(ios): keep voice note changelog release-owned

* fix(ios): gate voice notes with attachment availability

* chore(ios): refresh native localization inventory
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: ios App: ios feature: ✨ showcase ClawSweeper spotlight: unusually compelling feature idea for maintainer attention. maintainer Maintainer-authored PR P2 Normal backlog priority with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: XL status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant