One turn for every channel
Every channel needs the same answer from the assistant, so start by taking the web chat's turn out of its route. A turn is the code that takes one message and produces one reply.
Study first
- In the adapter pattern, each channel's adapter turns what arrives into one shape,
{ channel, address, text, thread }, and renders what goes back. The agent in the middle does not know which channel it is on, except for a line about how to write. Enterprise Integration Patterns describes the same idea as a normalizer, with one translator per incoming format and one common message out. - Where a conversation lives decides where it can continue, so a history kept in the browser cannot be continued from a phone.
- Latency differs by channel. The web streams and SMS waits for the whole reply, so the same turn serves both and only the call that runs it differs. The AI SDK's guide to generating text shows both calls,
streamTextfor the web andgenerateTextfor a channel that waits.
Build
- Move the web chat's turn out of its route, into
prepareinsrc/lib/assistant.ts. It puts the versioned prompt from Day 19 first, so the provider can cache it. Then it adds what is known about the person, and the conversation trimmed to the context budget from Day 10. The web streams the turn, and the other channels wait for it. - Add a
conversationstable andsrc/lib/history.ts. Each conversation has its person and its messages, and each message is marked with the channel it came by. - Make the web route,
POST /api/chat, start from the server's copy when there is one, so turns taken on another channel are part of the next answer. Update the copy after each reply. When the chat page opens a conversation, it fetches the server's copy and labels each message that came by another channel.
Done when
- A conversation that includes WhatsApp turns reaches the model whole, even when the browser sends only its own older messages and the new question.
- A turn whose save has not landed yet is kept from the browser's copy.
Technical notes
- Guard the save in SQL, so an update applies only where the stored person matches. A request that carries someone else's conversation id then cannot overwrite it.
- Taking the server's copy as the conversation is safe, because the web chat retries only after a failed turn and a failed turn saves nothing.





