From User Message to Durable Turn

For TrueForge, the important unit of runtime behavior is not a reply but a turn that can be streamed, paused, persisted, and continued. This chapter follows what happens after an agent definition is chosen: how a session holds a conversation, how a turn advances through model and tool work, how events reach a client, and what remains recoverable when execution is interrupted. By the end, the reader should be able to distinguish the objects in that lifecycle and locate responsibility when a run finishes, pauses, fails, or needs to be replayed.

Five terms, one execution hierarchy

TrueForge describes its runtime with a hierarchy: one agent can serve many sessions; each session can contain many turns; each turn emits events; some model events arrive as deltas. A thread is an execution context inside a turn, normally the root agent’s main thread and, when work is delegated, a separate thread for each subagent. These terms are related, but they do not name the same state.

An agent is a reusable definition. It selects a model, instructions, resource mounts, and runtime behavior, but it is not a process that waits for messages. A session is the conversation boundary for one issue. It gives a client an identifier that can be stored and used to return to that issue later. A customer’s refund conversation and another customer’s shipping question should be separate sessions even if both use the same support agent.

A turn is one request/response cycle within a session. It may begin with a user message, or it may carry the user’s response to a pending approval or client-side tool. Turns chain automatically from the previous turn by default, so the application does not resend the whole conversation. The chain is not merely a convenience for the client: it tells TrueForge which prior thread snapshots and context belong to the new execution.

An event is a durable or streamable record of something the runtime did or needs the client to know. Lifecycle events indicate that a turn started or ended. Other events report model messages, tool responses, MCP initialization, sandbox creation, subagent threads, or required user action. A delta is an incremental fragment of a model message during a live stream. The base model.message and its later model.message.delta fragments share an ID, allowing the client to build the visible reply as it arrives.

This hierarchy prevents several common confusions. A session is not a single model call; it is the durable container for a continuing issue. A turn is not necessarily a completed answer; it can end while waiting for a response. An event is not always a sentence; it may be the record that a tool was called or that a new subagent thread began. A delta is useful for live rendering, but it is not the form in which a finished event is later retrieved.

Imagine a support agent handling an order. The first turn asks for the status of an order and may produce a model decision, an order-system response, and a streamed explanation. A later turn asks for a refund and can continue from the first turn’s context. If the runtime creates a subagent for a separate research task, that child’s events carry a different thread ID within the same turn. The root agent receives the child’s final result, while the client can still distinguish the child’s activity from the root’s activity in the stream.

sequenceDiagram actor Client participant TF as TrueForge runtime participant Store as Session store Client->>TF: Create session for reusable agent TF->>Store: Persist session and agent binding Store-->>TF: Session id TF-->>Client: Session id Note over Client,TF: First turn Client->>TF: User message TF->>Store: Append turn.created TF-->>Client: model.message + tool activity TF->>Store: Persist context and events TF-->>Client: model.message.delta chunks TF->>Store: Append turn.done TF-->>Client: turn.done with final state Note over Client,TF: Chained turn Client->>TF: Follow-up message TF->>Store: Read previous turn snapshot TF-->>Client: New turn events and deltas TF->>Store: Persist new context and terminal state

The diagram’s most important relationship is between the live stream and the store. TrueForge does not ask the client to be the only place where a turn exists. The event stream gives an application immediate visibility, while the session store retains the context and event history needed to reconstruct the turn’s durable meaning. A separate client can therefore treat the session ID as the conversation handle and the turn ID as the unit of execution it is currently observing.

State transitions make the lifecycle meaningful

Inside a thread, TrueForge’s loop moves among three working states. In llm-call-required, the runtime prepares the model request, including the current instruction and available tools, then streams the model response. If the response contains no tool calls, the thread can finish. If it does contain tool calls, the completed assistant message is appended to context and the thread moves toward tool response processing.

In tool-response-required, the runtime executes the open tool calls and appends their results to context. Ordinary results allow the loop to return to the model. Special results change the transition: a subagent request creates a child thread; an authentication requirement stops the work until the client handles it; and an approval or client-side tool response places the thread in user-input-required. The distinction matters because the runtime cannot safely treat every non-text result as an ordinary tool response.

In user-input-required, the assistant has already committed a tool call that needs something from outside the autonomous loop. TrueForge emits a required-action event, and the turn ends with a terminal event whose state can still contain pending actions. The next client request resolves those open calls. That design makes a pause a visible state transition rather than a hidden exception or an instruction for the application to guess what happened.

The loop is bounded as well as stateful. Each model step increments an iteration counter. The public agent specification allows an iteration limit between 1 and 1024 and defaults it to 100; reaching the limit emits an error asking for a new request instead of allowing a runaway loop to continue indefinitely. This is a safety boundary, not a quality dial. A higher limit does not make a model’s plan better, and a lower limit can stop a legitimately long task before it completes.

Before execution, TrueForge validates the incoming input against the thread’s current open tool calls. A new user message cannot be sent while an approval or client-side question remains unresolved. An approval or tool response must refer to an open call, and a batch must resolve all of the pending calls for that thread. These checks protect the relation between model messages and tool results: the runtime should not ask the LLM to interpret a conversation containing an unmatched or duplicated tool response.

The session boundary is also a persistence boundary. When a new turn is created, TrueForge rebuilds the active threads from the previous turn’s snapshot, resolves the resources needed for the new run, and sends the new input through validation and context append before committing the turn as running. The store contract requires the turn insertion and session-tip update to be one atomic operation. If preparation fails, the new turn should not leave behind a partial durable record that claims work exists when its context was never committed.

The snapshot is more than a copy of the visible transcript. It carries the contexts of active threads, current context usage, completion markers for child work, MCP initialization information, sandbox information, and capability state. On a later turn, TrueForge can rebuild the root thread and any still-relevant child threads from that stored material. A capability can also maintain a JSON-serializable state map across turns. This is how the runtime preserves behavior that is not naturally represented as a user-facing message without making the store understand every capability’s internal meaning.

Turn chaining has a deliberate shape. The default previous turn is the session tip, but a caller can start a new root or fork from a specified earlier turn. The store does not need to pretend that every branch is one linear history: a fork can preserve the selected ancestor while advancing the session’s current tip to the new turn. That makes experimentation and recovery possible, but it also means an application must know which branch it is displaying. “The session” is a useful conversation handle; the turn ancestry determines which conversation path the agent actually sees.

During execution, the core writes different kinds of state through different paths. Context appends preserve the messages the thread will need for its next model call. Model message events and their deltas serve the stream; the delta is sent live, while the completed model content is represented in the persisted context/event model. Tool responses, subagent lifecycle events, MCP initialization, sandbox state, and terminal turn state are recorded so later reads can explain not only the final text but also the path that produced it.

This is why a turn can support both streaming and non-streaming clients. A stream-first application receives turn.created, activity events, model fragments, and turn.done as the work proceeds. An application that does not need live output can start a background turn, receive a running state, and poll until the turn becomes done, cancelled, or errored. Both modes rely on the same durable turn model; they differ in when the client observes it.

Cancellation is a state transition, not merely a closed HTTP connection. A client can cancel a running session, which aborts the in-flight model request, waits for running MCP calls to finish, and stops a sandbox provisioned for the turn. The store records a cancellation reason. A new turn can then chain from the cancelled turn’s history, so stopping the current execution does not necessarily discard the conversation that led to it.

The same distinction matters when a consumer disappears without explicitly cancelling. The streaming handle is single-use, and its terminal writer determines whether the execution ended normally, was cancelled, failed, or was abandoned. In distributed mode, the API places events into a resumable subscription registry before forwarding them to the current SSE connection. If the connection drops while the turn is still running, a client that saved the session ID, turn ID, and last sequence number can ask for the turn’s status and resubscribe after the last received event. If the turn has finished, it can rebuild the event index from the persisted event log, where model deltas are already merged.

The sequence number belongs to the stream boundary, not to the meaning of the event itself. A client should use it as a cursor for delivery, while using the event ID to associate a delta with its base model message and the thread ID to keep root and child activity separate. This division is valuable in a UI: a message can be rendered incrementally, a subagent can appear under its own trace, and a reconnect can skip events already received without changing the underlying event records. It also prevents a client from treating the order in which text fragments arrived as a replacement for the durable event model.

Replay has a different purpose from resubscription. Resubscription continues observing a live turn from a sequence cursor. Replay reads a finished turn’s stored events in order, with the model deltas already folded into their base messages. An application can therefore rebuild a completed view without replaying model calls or re-running external tools. That distinction is particularly important for tools with side effects: inspection should use the event log, not a fresh execution that happens to be intended only to recover the display.

That recovery is strong enough to prevent a user interface from losing the visible history of a turn, but it is not exactly-once execution for the outside world. An MCP action may have completed before the network broke, and a later client cannot infer from a missing final event that the action did not happen. Durable event identity and replay solve observability and continuation; they do not supply idempotency semantics for an external service. Applications that perform consequential writes still need the downstream system’s own safeguards and a clear policy for retries.

Other terminal outcomes have their own meanings. A cancellation can come from a client request, a server execution timeout, or the need to make room for a new turn. An error can arise from a model, a remote MCP connection, a sandbox, or a persistence operation. A completed turn can carry a final model message, while a done-but-paused turn can carry required actions and no output. Reading only a status string is therefore insufficient for a robust client; it must inspect the terminal state and the associated events before deciding whether to show a result, ask for input, retry, or surface a failure.

Seen this way, TrueForge’s lifecycle is the operational meaning of the agent contract. The definition supplies the possible resources and behaviors; the session supplies a conversation boundary; the turn supplies a bounded execution; threads let parallel work remain distinguishable; events expose progress and required decisions; and the store preserves enough state to continue or inspect the result. With that model in place, the next systems question is not simply how to run longer. It is how to keep the information entering and accumulating in the loop usable as the work expands.

References