Sockets, because the platform ruled out the alternative
With no streamable response body in React Native, the server emits discrete events over Socket.IO — start, thinking, chunk, end — rather than a chunked HTTP body. Nivram attaches its own listener instead of using the app's shared WebSocket hook, because that hook keeps only the latest event in state and would drop chunks the moment they arrive faster than React re-renders.
One render per frame, not one per token
Tokens arrive far faster than a screen refreshes. Each one is coalesced into a ref and flushed to the store on a `requestAnimationFrame` tick, so the list re-renders about sixty times a second regardless of whether sixty tokens arrived or six hundred. Dispatching per token is the obvious implementation and it makes the UI stutter exactly when the user is watching it most closely.
A listener that survives the conversation being renamed
When a draft is promoted to a real thread the chat key changes underneath an in-flight reply. Re-binding the socket listener on that change would drop the rest of the message, so the handler is held in a ref and reads the current key rather than closing over it. It is the kind of bug that only appears on the first message of a new chat, which is the first thing any user does.
Merge chunks defensively, in both shapes
Chunks arrive sometimes as deltas to append and sometimes as cumulative snapshots of everything so far. The merge checks whether the incoming chunk already starts with what is held and appends or replaces accordingly, so a change of server behaviour degrades into a redundant assignment instead of duplicated text.
Stream the prose, wait for the structure
The envelope carries a message alongside component and data fields. The text is pulled from the partial buffer and rendered as markdown while it arrives, so the answer reads as it is written; anything the user can press mounts only once the object is complete. Those controls are not decorative — they call the same production endpoints as the app's own screens, and some of them move money. A button rendered from a half-parsed payload is a transfer with a missing field behind a confirm the user has already trusted. Arriving a second late is the correct failure.
A registry the model is allowed to outrun
Component types resolve through a central map, and an unrecognised one renders nothing — a warning in development, silence in production. That sounds like giving up and is the opposite: the assistant releases continuously while the app crawls through two review queues, so a user on last month's build will certainly be offered a card their copy has never heard of. The choice is between a missing card and a crash, and only one of those lets the rest of the answer still be useful.
The PIN gate wraps the action instead of living inside it
Money-moving cards are not mounted and then asked for authorisation. A gate component opens the transaction PIN modal first, merges the PIN into the payload, and only then mounts the real action with the authorisation already present — so the component never exists in an unauthorised state. Putting the check inside each card would have meant seventy chances to forget it; this way there is one, and forgetting is not among the available mistakes.
The result is whispered back into the conversation
When a card succeeds it does not just close. A hidden message goes back up the same socket carrying what happened, so the assistant's next turn knows the customer now exists or the transfer went through. Fire-and-forget would have been one line instead of the round trip — and would leave the model confidently offering to create a record it just created, because nothing ever told it otherwise. The conversation is the state; anything that changes the world has to say so.
Only the newest card is live
Every assistant card except the most recent is disabled. Scrolling up and pressing a confirm from three answers ago would fire an action against a payload the conversation has moved past — on a financial app, most likely a second transfer. The stale ones stay visible as a record of what was offered, and refuse to do anything.