fix(client): tolerate transient control-poll failures in useVoice - #39
Conversation
A single failed GET /voice/calls/:id poll ended the call immediately, even for a short network flap the peer would have recovered from. Track consecutive poll failures on the session and only declare the control connection lost after three in a row; any successful poll resets the count. Mirrors the grace period CopilotKit#26 gives the peer connection. Adds regression tests covering the new behavior. Fixes CopilotKit#38
NathanTarbert
left a comment
There was a problem hiding this comment.
Thanks @sx4im, and thanks for building on #26 rather than opening a competing fix. Ending on the third failure in a row lines up nicely with #26's five seconds. The test that checks the call ends exactly once is the one I'd most want to see here.
On the open question about one shared window: with this and #26 merged together, the two separate windows already behave the way #38 asks for. A short drop on both sides leaves the call up. A poll-only flap with a healthy peer leaves the call up. A lasting outage ends the call once, with the peer's message, because its five seconds runs out first. jibraaan's shared window on #38 would be tidier, with one timer and one message. It doesn't look necessary for correctness, so I'd be happy either way.
The only friction is that both PRs add a field next to timer in the session in useVoice.ts. If #26 goes in first, the rebase here is just keeping both lines.
Looks good to me.
jerelvelarde
left a comment
There was a problem hiding this comment.
Useful resilience improvement: transient control-poll failures no longer immediately end a voice call, while repeated failures remain bounded. Fits the existing voice lifecycle and preserves the current disconnect grace timer after conflict resolution. No actionable security or correctness findings. Validated the integrated branch with 201 tests, type checking, lint, formatting, and production build; the five-PR combined tree passes 233 tests and the same checks.
What
useVoicepollsGET /voice/calls/:idevery 2 seconds. The.catch()on that poll setCall control connection was lost.and ended the call on the first failure, so a short network flap hung up a call the peer would have recovered from. #26 adds a five-second grace period forRTCPeerConnectiondisconnectedbut does not cover this path.Changes
src/client/useVoice.ts: count consecutive control-poll failures on the session. The call ends after three in a row; a successful poll resets the count. Same idea as the grace period in fix: allow voice connections five seconds to recover #26.tests/use-voice-control-poll.test.ts: regression tests with a mocked peer, media, and API. One failed poll keeps the call alive, a later success resets the count, and three consecutive failures end the call exactly once withCall control connection was lost.react-test-rendererand its types as dev dependencies for the hook tests, same as fix: allow voice connections five seconds to recover #26.Tradeoff
If the server is really gone, the call now stays up for about six more seconds before ending. The design question in #38 is still open, so say the word if you want a different threshold or one window shared with the peer state.
Tests
New tests fail on the base commit (2 failed, 2 passed) and pass with the change (4 passed). Full suite: 166/168 pass; the 2 failures are
tests/transport.test.ts(DNS-pinned transport needs live network) and fail on the base commit too.npm run check-format,npm run lint,npm run typecheck, andnpm run buildall pass.Fixes #38