Suppose an assistant is replying in Slack. While it works, another message arrives, a background task finishes and a setting changes. Those arrivals belong to the conversation, but they cannot change the model call already in progress. They have to wait for the next call.
Meanwhile, every reply makes the conversation history longer. Eventually its early turns have to be shortened, while the newer turns remain unchanged.
Both changes wait until the current model call is over.
An agent loop repeats a simple cycle: call the model, save its response, then build the next call. The gap after one response is saved and before the next call starts is a breakpoint. prepareTurn() runs there. It combines the saved conversation with the oldest waiting events that fit, producing the input for the next model call.
Start with the new arrivals.
New work waits in order
Each arrival is an event: a message, a system change or a result from background work. It is added to an ordered queue before it can affect the conversation. In the diagram, A arrived before B, B before C and C before D.
At the breakpoint, prepareTurn() reads the conversation so far and everything waiting in the queue. If A, B and C fit in the next model call, it prepares them together with the existing history. D stays in the queue. An older event is never skipped to reach a newer one.
At this point A, B and C are still queued. A successful call records the input and response and advances the queue to D as one change.
If this step or the model call fails, the queue remains A, B, C, D.
Older history is compacted while the conversation continues
Suppose turns 1–4 no longer fit comfortably. A background job starts shortening them. The conversation does not stop: turns 5 and 6 can be added while that work runs.
The ordinary, unshortened history is already epoch 0. When the summary is ready, it waits for the current model call to finish. At the next breakpoint, the conversation adopts epoch 1. prepareTurn() can then build the next call from S1 followed by the unchanged turns 5 and 6.
Later, turns 7 and 8 arrive. If the history needs shortening again, S2 can replace epoch 1 through turn 6. Epoch 2 starts with S2 and carries turns 7 and 8 forward. S2 points to epoch 1, where S1 still points to epoch 0.
The original turns remain stored. Following the links through later epochs leads back to the original conversation. Over several rounds, those links form a tree similar in shape to a Merkle tree, using ranges of turns rather than content hashes.