diff --git a/AGENTS.md b/AGENTS.md index 8226882d7..26b42fd9a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,6 +23,7 @@ - Naming: match source names with `*.test.ts`; e2e in `*.e2e.test.ts`. - Run `pnpm test` (or `pnpm test:coverage`) before pushing when you touch logic. - Pure test additions/fixes generally do **not** need a changelog entry unless they alter user-facing behavior or the user asks for one. +- Mobile: before using a simulator, check for connected real devices (iOS + Android) and prefer them when available. ## Commit & Pull Request Guidelines - Create commits with `scripts/committer "" `; avoid manual `git add`/`git commit` so staging stays scoped. diff --git a/CHANGELOG.md b/CHANGELOG.md index 33e51cee0..00b59c776 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - macOS menu: device list now uses `node.list` (devices only; no agent/tool presence entries). - macOS menu: device list now shows connected nodes only. - iOS node: fix ReplayKit screen recording crash caused by queue isolation assertions during capture. +- iOS Talk Mode: avoid audio tap queue assertions when starting recognition. - iOS/Android nodes: bridge auto-connect refreshes stale tokens and settings now show richer bridge/device details. - iOS/Android nodes: status pill now surfaces camera activity instead of overlay toasts. - iOS/Android/macOS nodes: camera snaps recompress to keep base64 payloads under 5 MB. diff --git a/apps/ios/Sources/RootCanvas.swift b/apps/ios/Sources/RootCanvas.swift index ab67996a7..17f02c79c 100644 --- a/apps/ios/Sources/RootCanvas.swift +++ b/apps/ios/Sources/RootCanvas.swift @@ -253,7 +253,7 @@ private struct CanvasContent: View { private struct OverlayButton: View { let systemImage: String let brighten: Bool - var tint: Color? = nil + var tint: Color? var isActive: Bool = false let action: () -> Void diff --git a/apps/ios/Sources/Voice/TalkModeManager.swift b/apps/ios/Sources/Voice/TalkModeManager.swift index 3766845b1..c11b890a9 100644 --- a/apps/ios/Sources/Voice/TalkModeManager.swift +++ b/apps/ios/Sources/Voice/TalkModeManager.swift @@ -7,6 +7,7 @@ import Speech @MainActor @Observable final class TalkModeManager: NSObject { + private typealias SpeechRequest = SFSpeechAudioBufferRecognitionRequest var isEnabled: Bool = false var isListening: Bool = false var isSpeaking: Bool = false @@ -105,9 +106,8 @@ final class TalkModeManager: NSObject { let input = self.audioEngine.inputNode let format = input.outputFormat(forBus: 0) input.removeTap(onBus: 0) - input.installTap(onBus: 0, bufferSize: 2048, format: format) { [weak request] buffer, _ in - request?.append(buffer) - } + let tapBlock = Self.makeAudioTapAppendCallback(request: request) + input.installTap(onBus: 0, bufferSize: 2048, format: format, block: tapBlock) self.audioEngine.prepare() try self.audioEngine.start() @@ -135,6 +135,12 @@ final class TalkModeManager: NSObject { self.speechRecognizer = nil } + private nonisolated static func makeAudioTapAppendCallback(request: SpeechRequest) -> AVAudioNodeTapBlock { + { buffer, _ in + request.append(buffer) + } + } + private func handleTranscript(transcript: String, isFinal: Bool) async { let trimmed = transcript.trimmingCharacters(in: .whitespacesAndNewlines) if self.isSpeaking, self.interruptOnSpeech {