API discovery: Mastra’s public LiveKit recipe documents a small but consequential memory option. LiveKit can start a reply while a caller is still speaking, then discard that run when the transcript changes. Before the merged integration change, turning on memory meant those speculative turns could persist a partial user message and a partial reply the caller never heard. Passing options: { readOnly: true } lets the turn consult thread history without writing to it.
Try it
Use this workflow in a disposable support-line integration: a caller begins, “I need to change my—” and corrects to, “I need to cancel my subscription.” Preemptive generation can begin working before the sentence is complete. Read-only memory means the abandoned first guess can use the existing account context without becoming a fake durable turn. When LiveKit commits the actual user or assistant message, save that event using its LiveKit ID; retries remain idempotent because the same message keeps the same identity.
Copy-paste agent instruction
Implement Mastra + LiveKit speculative voice memory as a two-phase flow: map memory with { thread, resource, options: { readOnly: true } } while preemptiveGeneration is enabled; let speculative turns read but never persist; subscribe to ConversationItemAdded and save only committed user or assistant messages using item.id as the durable idempotency key. Do not write interim transcripts or replies the caller never heard.
Test caveat
Test this in a disposable environment: the plugin cannot tell by itself whether a LiveKit call is speculative, so this is not a switch-and-forget safety feature. The application must own the committed-event persistence and retry policy. The public change includes in-process and remote forwarding tests, but it is not evidence of a live telephony deployment, a released package in your environment, or a correct memory backend configuration.