diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/ConversationAwarenessEvent.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/ConversationAwarenessEvent.kt index e2077ea8..4b6eddc0 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/ConversationAwarenessEvent.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/aap/protocol/ConversationAwarenessEvent.kt @@ -12,9 +12,10 @@ package eu.darken.capod.pods.core.apple.aap.protocol * - `5` → [RESUME] (speech resumed after a pause; the wind-down was aborted, CA stays engaged). In * bursty speech the pod cycles `3,5,3,5,…`; `5` is NEVER a terminal. Misreading `5` as a stop was * the root cause of the premature-resume bug — see [ConversationReaction]. - * - `8`, `9` → [STOP] (conversation ended → disengage). The terminal is always the `8`→`9` pair. - * Pod removal / case-close also emit `8`,`9` (sometimes with no prior `1`,`2`). - * - any other value (`3` pause, `4` / `0x0B` wind-down, `7` abort, `6`, and anything unrecognised) + * - `6`, `8`, `9` → [STOP] (conversation ended → disengage). The usual terminal is the `8`→`9` + * pair; `6` is a standalone terminal seen live on Pro 2 USB-C fw `…6814`. Pod removal / case-close + * also emit `8`,`9` (sometimes with no prior `1`,`2`). + * - any other value (`3` pause, `4` / `0x0B` wind-down, `7` abort, and anything unrecognised) * → [HOLD]: a transitional "possible/real wind-down" frame. The real wind-down runs `3→0x0B→4` * then the `8,9` terminal; `7` precedes an aborted terminal. Unknown values are deliberately * classified as HOLD (arm the safety fuse, never resume immediately) rather than guessed at. @@ -37,7 +38,7 @@ enum class ConversationAwarenessEvent { companion object { val SPEAKING_STATUSES = setOf(1, 2) val RESUME_STATUSES = setOf(5) - val STOPPED_STATUSES = setOf(8, 9) + val STOPPED_STATUSES = setOf(6, 8, 9) fun fromStatus(status: Int): ConversationAwarenessEvent = when (status) { in SPEAKING_STATUSES -> START diff --git a/app/src/test/java/eu/darken/capod/pods/core/apple/aap/engine/AapSessionEngineTest.kt b/app/src/test/java/eu/darken/capod/pods/core/apple/aap/engine/AapSessionEngineTest.kt index 69defffb..66f86d53 100644 --- a/app/src/test/java/eu/darken/capod/pods/core/apple/aap/engine/AapSessionEngineTest.kt +++ b/app/src/test/java/eu/darken/capod/pods/core/apple/aap/engine/AapSessionEngineTest.kt @@ -570,9 +570,10 @@ class AapSessionEngineTest : BaseTest() { } @Test - fun `terminal statuses 8 and 9 emit STOP`() = runTest(UnconfinedTestDispatcher()) { - // The conversation terminal is always the 8→9 pair (both pods); also emitted by pod - // removal / case-close. 6 is never observed as a terminal, so it falls through to HOLD. + fun `terminal statuses 6, 8 and 9 emit STOP`() = runTest(UnconfinedTestDispatcher()) { + // The usual terminal is the 8→9 pair (both pods; also emitted by pod removal / case-close). + // 6 is a standalone terminal seen live on Pro 2 USB-C fw …6814. + firstEventFor(6) shouldBe ConversationAwarenessEvent.STOP firstEventFor(8) shouldBe ConversationAwarenessEvent.STOP firstEventFor(9) shouldBe ConversationAwarenessEvent.STOP } @@ -580,12 +581,11 @@ class AapSessionEngineTest : BaseTest() { @Test fun `transitional and unknown statuses emit HOLD (stay engaged)`() = runTest(UnconfinedTestDispatcher()) { // Must never resume immediately on these — they arm the short wind-down fuse instead. - // 3 = pause, 0x0B/4 = wind-down, 7 = abort; 6 and any unknown value default to HOLD. + // 3 = pause, 0x0B/4 = wind-down, 7 = abort; any unknown value defaults to HOLD. firstEventFor(3) shouldBe ConversationAwarenessEvent.HOLD firstEventFor(4) shouldBe ConversationAwarenessEvent.HOLD firstEventFor(0x0B) shouldBe ConversationAwarenessEvent.HOLD firstEventFor(7) shouldBe ConversationAwarenessEvent.HOLD - firstEventFor(6) shouldBe ConversationAwarenessEvent.HOLD firstEventFor(0) shouldBe ConversationAwarenessEvent.HOLD firstEventFor(0xFF) shouldBe ConversationAwarenessEvent.HOLD }