> ## Documentation Index
> Fetch the complete documentation index at: https://trueforge.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK Concepts

> The mental model behind the SDK — Agent, Session, Turn, Event, and Delta — with one worked example.

Already running? Jump to the [SDK Quickstart](/api/quickstart) to install and stream your first turn. This page is the mental model behind that code: how an [agent](#agent), a [session](#session), a [turn](#turn), an [event](#event), and a [delta](#delta) fit together. Once it clicks, [Use an agent](/api/use-agent) is the runnable cookbook, and the **API Reference** tab has the raw HTTP schemas.

## Core concepts

Talking to an agent follows one hierarchy: **one Agent → many Sessions → many Turns → many Events → some Deltas**. The sections below walk through each layer with one running example: a customer support agent named **`support-bot`**.

<Frame caption="One agent serves many sessions; each session has many turns; each turn emits events; some events stream as deltas">
  <img src="https://mintcdn.com/trueforge/FI96UcvnIsBhjhdI/images/agent-session-hierarchy.png?fit=max&auto=format&n=FI96UcvnIsBhjhdI&q=85&s=9bad3759d88fde658897b4efb9037637" alt="Hierarchy diagram. A single agent (support-bot), defined once, is reused across many sessions. Two example sessions are shown, Jane's refund issue and Bob's shipping question, each containing turns. Turn 1 in each session expands to show the events emitted on the stream (turn.created, user.message, mcp.initialize, model.message, tool.response, tool.approval_required, turn.done) and the deltas that events like model.message produce as streaming chunks. A legend lists each event type and a pyramid summarizes Agent to Sessions to Turns to Events to Deltas." width="1536" height="1024" data-path="images/agent-session-hierarchy.png" />
</Frame>

### Agent

An **agent** is a definition, not a running process. You define it once (the model, instructions, tools, and config), and any number of conversations can use it. `support-bot` is a support assistant that looks up orders and processes refunds through an MCP server. Its [AgentSpec](/create-agent/overview#create-an-agent-via-the-api) looks like this:

```json support-bot AgentSpec wrap theme={null}
{
  "model": {
    "name": "anthropic/claude-sonnet-4-6",
    "params": { "max_tokens": 4096 }
  },
  "instructions": "You help customers with orders. Look up order details before taking action. Always confirm before processing refunds.",
  "mcp_servers": [
    {
      "name": "orders-api",
      "enable_tools": ["get_order", "process_refund"],
      "require_approval_for_tools": ["process_refund"]
    }
  ],
  "config": { "iteration_limit": 25 }
}
```

Save this spec as a named agent and reference it by name in every session, or pass it inline when you create a session. The [agent spec reference](/create-agent/overview#create-an-agent-via-the-api) lists every field.

### Session

A **session** is one issue worked through with the agent. It holds the conversation context: every turn on that issue chains together, and the agent remembers what happened earlier in the same session. Each new issue gets its own session, whether it comes from the same customer or a different one.

| Session | Issue | How it starts |
| - | - | - |
| `sess-7f2a9c1b` | Jane, refund for order ORD-2031 | *"What's the status of order ORD-2031?"* |
| `sess-9d4e2a88` | Bob, shipping question | A different customer starts their own chat |

Jane's follow-ups about the refund stay in `sess-7f2a9c1b`. Bob's question runs in a separate session, so the two conversations never share context.

```json Session for Jane's refund issue wrap theme={null}
{
  "id": "sess-7f2a9c1b",
  "agent": { "type": "reference", "id": "agt_01jc...", "name": "support-bot" },
  "title": "Refund for ORD-2031",
  "created_by": "trueforge-default",
  "created_at": "2026-06-24T10:00:00Z",
  "updated_at": "2026-06-24T10:00:00Z"
}
```

Persist the session `id` so Jane can come back tomorrow and pick up where she left off. Only one turn runs in a session at a time.

Every session you run this way is also recorded in the [Sessions](/sessions) view, where you can inspect its turns, tool calls, subagents, and timing.

### Turn

A **turn** is one request in the conversation: a single back-and-forth between your app and the agent. Each time Jane sends a message (or your app sends an approval), you create a new turn. Turns in a session **chain automatically** (`previous_turn_id` defaults to `"auto"`), so the agent sees every earlier turn without you resending history.

```mermaid theme={null}
flowchart LR
  T1["Turn 1<br/>Jane: status of ORD-2031?"]
  T2["Turn 2<br/>Jane: issue full refund"]
  T3["Turn 3<br/>Jane: approves refund"]
  T1 -->|"agent replies, Jane follows up"| T2
  T2 -->|"agent pauses for approval"| T3
```

| Turn | Who sends it | Input | What happens |
| - | - | - | - |
| **Turn 1** | Jane (via your app) | `"What's the status of order ORD-2031?"` | Agent looks up the order and replies with shipping status. |
| **Turn 2** | Jane | `"Please issue a full refund for that order."` | Agent prepares a refund, hits an approval gate, and **pauses**. |
| **Turn 3** | Jane (or a support rep) | Approval: allow `process_refund` | Agent runs the refund and sends a confirmation. |

<Note>
  A turn can also pause when the agent asks a clarifying question ([`tool.response_required`](/api/use-agent#tool-response_required))
  or needs MCP OAuth ([`mcp.auth_required`](/mcp-servers#in-chat-authentication)). You resume with a new turn, just like
  Turn 3 above.
</Note>

### Event

While a turn runs, the agent emits **events** on the SDK stream, one JSON object at a time. Each event tells your app what the agent is doing: calling a tool, getting a result, writing a reply, or finishing.

**Events during Turn 1** (Jane asks for order status):

| Order | Event type | What your app learns |
| - | - | - |
| 1 | `turn.created` | Turn started; carries `turn_id` |
| 2 | `mcp.initialize` | Agent connected to `orders-api` |
| 3 | `model.message` | Agent decided to call `get_order` |
| 4 | `tool.response` | Order data: shipped, \$1,240.00 |
| 5 | `model.message` | Agent starts composing the reply (empty shell) |
| 6–8 | `model.message.delta` | Reply text arriving in chunks (see [Delta](#delta)) |
| 9 | `turn.done` | Turn finished; final reply in `state.output` |

Sample payload:

```json turn.done wrap theme={null}
{
  "type": "turn.done",
  "id": "01jc...",
  "thread_id": null,
  "created_at": "2026-06-24T10:00:09Z",
  "state": {
    "status": "done",
    "output": {
      "type": "model.message",
      "content": "Your order ORD-2031 shipped on June 12. Total: $1,240.00."
    },
    "required_actions": [],
    "completed_at": "2026-06-24T10:00:09Z"
  }
}
```

Every event carries an `id` and a `thread_id` (`"main"` for the root agent, a generated id for [subagents](/key-features/subagents), or `null` for run-level events), plus a **sequence number** used for [resuming after a disconnect](/api/use-agent#resume-a-stream). The stream always opens with `turn.created` and closes with `turn.done`. Field-level schemas for every event type are in the [turn events reference](/api/use-agent#turn-events-reference).

### Delta

Most events arrive as a complete payload. Model output is the exception: the LLM streams token by token, so the harness sends a base `model.message` event first, then a series of `model.message.delta` fragments that you merge into it. All deltas share the base event's `id`.

The agent's full reply in Turn 1 is *"Your order ORD-2031 shipped on June 12. Total: \$1,240.00."* Your client receives:

```json Base event (empty shell) wrap theme={null}
{ "type": "model.message", "id": "msg-a1", "thread_id": "main", "content": "" }
```

```json Deltas (merge into msg-a1 as they arrive) wrap theme={null}
{ "type": "model.message.delta", "id": "msg-a1", "thread_id": "main", "content": "Your order ORD-2031" }
{ "type": "model.message.delta", "id": "msg-a1", "thread_id": "main", "content": " shipped on June 12." }
{ "type": "model.message.delta", "id": "msg-a1", "thread_id": "main", "content": " Total: $1,240.00.", "finish_reason": "stop" }
```

Append each delta's `content` to the event with the matching `id` and re-render on every chunk. That's how the chat UI shows the reply word by word:

```text wrap theme={null}
Your order ORD-2031
Your order ORD-2031 shipped on June 12.
Your order ORD-2031 shipped on June 12. Total: $1,240.00.
```

In the SDK, use TypeScript `isEventDelta` / `mergeEventDelta` or Python `is_event_delta` / `merge_event_delta` from `trueforge_sdk.events`. See [Create and stream a turn](/api/use-agent#create-and-stream-a-turn).

Deltas only exist on the live stream. When you list a turn's events afterwards (`GET .../events`), each `model.message` is already merged, so you use it directly.

<Note>
  A **thread** is a related idea: an execution context inside a turn. The root agent runs on `thread_id: "main"`, and
  [subagents](/key-features/subagents) get their own thread ids, announced by `thread.created` / `thread.done`
  events. Events carry `thread_id` so you can separate a single turn stream when subagents run in parallel.
</Note>

## The three turns, end to end

Here is Jane's whole refund session: your app opens a session, runs turns, and reads the streamed events.

<Frame caption="Three chained turns in one session: your app creates the session and turns; TrueForge streams events back">
  <img src="https://mintcdn.com/trueforge/FI96UcvnIsBhjhdI/images/api-sequence-diagram.png?fit=max&auto=format&n=FI96UcvnIsBhjhdI&q=85&s=da31195d61208c1191a869fcabf70dcc" alt="Sequence diagram of the support-bot session across three turns. Turn 1 streams tool calls and a reply; Turn 2 pauses for approval; Turn 3 resumes with the approval and streams the confirmation." width="1024" height="1536" data-path="images/api-sequence-diagram.png" />
</Frame>

| Turn | Jane's action | Ends with | What your app does next |
| - | - | - | - |
| 1 | Asks for order status | Reply in `output` | Show reply, wait for the next message |
| 2 | Requests refund | `tool.approval_required` | Show approval UI → Turn 3 |
| 3 | Approves refund | Confirmation in `output` | Show confirmation, issue closed |

A separate issue (Bob's shipping question, or Jane's next problem) runs in its own session, where Turn 1 starts fresh with no refund history.

## Next steps

* [SDK Quickstart](/api/quickstart) installs the SDK, connects with a token, and streams your first turn.
* [Use an agent](/api/use-agent) is the full cookbook: streaming, approvals, questions, threads, and reconnects.
* The **API Reference** tab has the raw HTTP endpoints and OpenAPI schemas.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.