feat(frontend): add session transport and contracts - #130
Conversation
| client.close(); | ||
| setConnectionStatus('error'); | ||
| setConnectionError(error instanceof Error ? error.message : 'WebSocket 连接失败'); | ||
| scheduleReconnect(); |
There was a problem hiding this comment.
[P2] Avoid scheduling the same reconnect twice. client.close() synchronously emits closed, and the status listener already calls scheduleReconnect(). Calling it again here clears that timer and increments reconnectAttempt a second time; the timeout path has the same pattern. As a result, retries jump 2s → 8s → 30s instead of using the configured 1s → 2s → 4s backoff. Schedule from one place only (and add a provider lifecycle test).
There was a problem hiding this comment.
Fixed in 5943819. Reconnect scheduling now happens only from the closed-status listener; the timeout and connect-failure paths close the client without incrementing the backoff again. Added a SessionProvider lifecycle test proving the first retry occurs after 1 second. All 45 tests pass.
| timestamp: string; | ||
| }; | ||
|
|
||
| export type LocationReport = WsRequest<'location.report', LocationReportPayload>; |
There was a problem hiding this comment.
[P1] This contract does not match the production backend. LocationReport is currently modeled as {request_id, payload}, but the backend validates schedule_scope, coordinates, accuracy, and timestamp at the top level. Its successful location.report.ack also has neither request_id nor payload. Code using this type with WsClient.request() will send an invalid report and cannot correlate a successful ack; the fake's correlated envelope hides the problem. Align both message shapes (or change the backend in the same dependency chain) before consumers rely on this contract.
There was a problem hiding this comment.
Fixed in 5943819. LocationReport now matches the backend top-level fields, and LocationReportAck no longer invents request_id or payload. The fake and an integration test now use the production shapes. All 45 tests pass.
| setTimeout(() => this.reply(parseResult), 0); | ||
| } | ||
|
|
||
| private handleVoiceCancel(message: VoiceStreamCancelCommand): void { |
There was a problem hiding this comment.
[P2] The fake reports voice.stream.cancel as supported, but the production router only registers voice.stream.start and voice.stream.end; remote sessions return UNKNOWN_MESSAGE_TYPE instead of voice.stream.cancelled. This makes cancellation work in development/tests and fail after switching transports. Either implement the backend route or make the fake/contracts reflect the current unsupported behavior.
Summary
device_id, build the backend-compatible session endpoint, and complete thesession.hellohandshakeWhy
The frontend previously had no durable session identity or production transport boundary. This establishes the backend contract and real connection lifecycle before feature UI is wired to it.
Impact
This is an infrastructure layer and does not replace the existing screen by itself. It is the transport dependency for the schedule and assistant PRs that follow.
Dependency
Validation
npm cinpm run check(10 suites, 43 tests)npx expo export --platform android --output-dir ../../export-session