Transcript overview
What it is. The append-only log of what actually happened in a run: every message, every tool call, every result, in the order they occurred. It isn’t a summary, and it isn’t the thing you hand the model. It’s the record.
Who writes it. The system, never a person. The loop appends an item every time something happens, and nothing ever goes back to tidy up. Items are added, never edited, never removed.
That last part is the load-bearing one. A log you can edit is a story about the run. A log you can only append to is evidence of it, which is what makes resuming, replaying and debugging possible at all.
Here is one, seven items in. They sit in the order they happened, each labelled with what it is, and every one of them was added at the bottom without disturbing a row above it.
Where it lives. Somewhere durable, and you get to pick whose disk. Most systems keep it on your side, in your process or your database. OpenAI’s Responses API will hold it for you instead and hand back an ID you pass to the next call. Same log either way.
Three nouns, and they nest inside each other.
| Term | What it is |
|---|---|
| Thread | The container for one continuous run of work. It has an ID, and the ID is how you find it again. |
| Transcript | The log inside the thread. Entries get added at the end, never edited, never removed. |
| Item | One entry. Not all of them are messages. |
Transcript authors
Four things write to a transcript, and only one of them is a person typing.
That’s worth sitting with, because it explains the surprise of opening a log and finding it four times longer than the conversation you remember having. Your words are in there. So is a file you never mentioned, reasoning you never read, and a summary of a stretch you can no longer see.
User
Everything you hand over on purpose.
- What you typed text
- A screenshot you pasted image
- A file you dragged in document
- …
Surface
Signals the client attaches on its own.
- The file you have open text
- Your branch and cwd text
- A picture of your screen image
- …
Model
What the model produces on its turn.
- The reply text
- Reasoning you rarely see thinking
- A request to run a tool tool_use
- …
Agent Harness
What the machinery adds unasked.
- What a tool returned tool_result
- Old turns, summarized compaction
- A point it can rewind to checkpoint
- …
The second box is the one that catches people out. On the wire, surface context is indistinguishable from something you wrote: same block type, same turn, and the model can’t tell them apart either. Only the client that assembled the turn knows which half you typed. You send more than you write on every turn, and the difference is billed to you.
The fourth is the one that surprises engineers. A tool_result travels back to the model as user content, so the API’s idea of a user turn and yours stopped agreeing a while ago. On a working agent, most of what’s filed under your name was written by machinery.
Transcript mechanism
One turn, end to end. It’s the run from the top of the chapter with everything that drawing left out put back: the thread around the log, the four authors at the foot of it, and the checkpoints the harness lays across it. Someone asks for an hour with a colleague, the model goes looking for free slots, the harness runs the tool and files what came back, and an invite goes out.
The append rule
One sentence in, five rows out. That’s the loop, drawn: the model asked for a tool, the harness ran it and filed the answer, the model read the answer and asked for another, and only then did it have anything to say back to you. Nobody decided this log would be seven items long. It’s seven because that’s how much work the request took, and exactly one of the seven is the sentence you typed.
Rows land at the end, and nothing re-sorts them afterwards. So where an item sits is when it happened, and that is the entire indexing scheme: no separate clock to consult, no id to resolve, the row above happened before the row below. It’s what makes “go back to before the invite went out” an instruction a system can carry out rather than a wish.
Checkpoints
A checkpoint is a mark on the log rather than a line in it. The two running across the drawing aren’t rows: each one names the gap between two items, and that gap is the position the whole next section aims at. No provider has a block type for one, so the model is never shown a checkpoint and can’t ask for one. That part doesn’t vary. What a checkpoint holds and where it’s kept do, and the two harnesses below answer both differently.
When one gets written. On the harness’s own schedule. Claude Code writes one per user turn; LangGraph writes one per super-step of the graph. Both are boundaries, a moment when the run is between things, so what gets recorded is a clean edge rather than half a turn. The second checkpoint in the drawing sits immediately before the invite goes out, which is the position worth having: the last moment at which everything after it can still be taken back.
What’s in one. Enough to identify a position, always: which thread, and how far along. Past that, a checkpoint holds as much of the world as its harness happens to own, and there are two shapes. LangGraph saves the state itself at every super-step, along with which node was about to run next, so its checkpoints contain the conversation rather than point at it. Claude Code marks the place and snapshots your files beside it, which is why /rewind can offer to put the code back and not only the conversation. Neither can recall the invite. Once a side effect has left the machine it is outside every checkpoint anybody wrote, and that is the hard edge on all of this.
What it costs. Nothing you pay for. A checkpoint is never assembled into a request, so it is never billed as tokens. It costs disk, on whoever is holding the thread, and that bill scales with how much of the world each one copies rather than with how long the conversation ran.
Not every harness writes them, and the concept survives that. OpenAI’s Responses API has no checkpoint object at all: every response it hands back carries an id, and passing an older id to the next call is the same move against the same append-only list. A checkpoint is a convenience for naming a position, not something a log needs in order to have them.
Which is the whole reason the next section exists: every move in it is a move on a position.
Transcript actions
You can’t edit a transcript, so none of these try. Each one picks what to carry on from and what to leave behind, and they come in the order of how much they take with them.
Resume
Reopen a thread by its ID and carry on, possibly days later. The log picks up where it stopped.
Fork
Continue from an earlier point without destroying what came after. Both lines survive.
Rewind
Go back to a checkpoint and drop everything after it. Only the conversation moves.
Restore
Rewind, and put the world back with it: revert the files and state that changed after that point.
Where a fork lands is the part that differs by system. Claude Code’s /branch copies the transcript into a new session with its own ID and leaves the original on disk. LangGraph keeps the same thread_id and branches the checkpoint lineage underneath it. The OpenAI Responses API has no thread object at all, so a fork is just two calls pointing at the same previous_response_id. Worth checking which one you have before you build a UI on it.
Rewind and restore are the pair worth keeping straight. They do the same thing to the log and completely different things to your machine: rewind an agent that spent five minutes editing files and you get a clean conversation about a repository that is still half-rewritten. Claude Code makes you say which one you meant, and its /rewind menu offers three choices for it: restore the conversation, restore the code, or restore both.
Transcript views
Nobody reads the transcript. Two things read it, and they disagree about what happened.
Your screen is an edit. A chat surface renders the messages and drops the rest: two bubbles out of seven items, with the tool calls, the results and the block your client attached nowhere on the page. That isn’t the client failing at its job, it’s the client doing it. It does mean “it sent one message” and “it wrote seven entries” are both true accounts of the same minute, which is worth having in mind the first time you open a log expecting the conversation you remember.
The wire is the whole log, flattened. What actually crosses the network is one document, the request, and it holds every item in order, each behind a role, plus a system prompt that was never in the transcript at all. The two faces the log sets in (a message in the reading face, machinery in mono) are gone. On the wire there are characters and there is a role, and that is the entire structure.
Three of those roles say user and you wrote one of them. The context your surface attached and both tool results travel back as user content, which is the rule from the authors section in the format it actually applies to. On the wire, a user turn means whatever the model didn’t say.
Compaction
Neither view is the log. Your screen is an edit of it for a person, the request is an assembly of it for a model, and the record behind both is untouched. That last part starts earning its keep the day the assembly stops fitting.
Compaction rebuilds the request, not the record. A request is two parts: the instructions the harness assembles fresh every turn, which is your system prompt plus whatever memory it carries between sessions, and the history under them. Only the history grows, so only the history gets folded. The harness takes the stretch that no longer fits, has the model write a summary of it, and builds the next request out of that summary plus the turns since. Nothing in the transcript moved, which is why a resume or a rewind still lands exactly where it would have.
It happens more than once, and the second fold is the expensive one. A compacted run keeps running, fills up again, and gets folded again, and the second fold swallows the first summary along with everything after it. The oldest part of the conversation is now a summary of a summary: through the model twice, and whatever the first pass dropped is not coming back. Long sessions don’t fade gradually. They lose a step at a time, one per fold.
When it fires is the harness’s call, and they disagree. Claude Code compacts on its own as the window fills, and /compact does it early with whatever instructions you hand it about what to keep. LangGraph ships the pieces and expects you to wire the summarizing step yourself. OpenAI’s Responses API doesn’t summarize at all: set truncation: "auto" and it drops items out of the middle of the request to make room. Vercel’s AI SDK gives you the array and the decision with it.
What it costs is a cache. Because the log only appends, each request is normally the last one plus a bit, which is exactly the shape a prompt cache wants, so most of a long conversation gets re-read at cache rates. A summary rewrites the front of the request, and the turn after it pays full price for everything. A harness that compacts eagerly can lose more on cache misses than it saves on tokens.