Chapter 09 of 14 · Part 3: Run Your Agent

Chapter 9: Sessions — The Durable Work Unit

By the end of this chapter, you will understand what a session is, how it differs from a chat conversation, how to create and manage sessions, and how to attach files and memory stores at session creation.


The Big Idea

A conversation ends when the window closes. A session is different.

A session is a persistent work unit — it maintains complete conversation history, file system state, and event logs across multiple interactions. You can come back to a session hours later and pick up exactly where you left off. The session remembers every message, every tool call, every result.

This is the architectural foundation for the kind of work that takes time. A coding agent that spends 45 minutes refactoring a codebase can't rely on a conversation that expires. A research agent that runs through 200 web pages needs durable state to track what it has and hasn't read. Sessions provide that durability.

The sessions documentation describes a session as "a running agent instance within an environment. Each session references an agent and an environment (both created separately), and maintains conversation history across multiple interactions."

The key phrase: "multiple interactions." A session isn't a single prompt-and-response. It's the container for an ongoing working relationship with your agent.

Session Lifecycle — Durable Sessions That Can Pause and Resume A horizontal timeline showing session states: idle, running, rescheduling, running, idle, then terminated. A moving indicator traces the timeline. Event markers appear above the line showing user.message, agent.tool_use, tool_result, and session.status_idle events. Session Lifecycle Sessions are durable state machines — they pause, resume, and survive infrastructure failures idle start running rescheduling auto-retry running idle end_turn terminated user.message kick-off task agent.tool_use bash, read, write… tool_result result → string session.status_idle stop_reason: end_turn idle running rescheduling terminated ← state machine driven by events you send append-only session log survives crashes
Two parallel tracks. Left track labeled "Chat Conversation" — three alternating bubbles (user → AI → user → AI), then a closed door icon labeled "Window closed. History gone." Right track labeled "Session" — same exchanges, but connected by a continuous ribbon labeled "Durable event log." Door icon at the right: "Come back anytime. Everything's still there."

The Analogy

Think of a session like a shared document workspace, not a phone call.

A phone call is stateless. Once it ends, there's no persistent record unless you made notes. If you call back, you start from scratch unless you remember the context yourself.

A shared document workspace is different. Every edit is tracked. Every comment is timestamped. You can leave and come back a week later and see exactly what happened, in what order, and who did what. You can pick up from the last edit, not from the beginning.

Sessions work like the shared workspace. Every event — every message you sent, every tool Claude used, every result it got back — is logged in order. When you reconnect to a session, the full history is available. The agent picks up from the last event in the log, not from a blank slate.

DiagramTwo workspace illustrations. Left: Phone call with no record, just two stick figures on phones. Right: Shared document with version history panel on the right side showing timestamped events. Caption: "A session is the shared document. Every event is a tracked change."

How It Actually Works

Creating a Session

A session requires two things: an agent ID and an environment_id. Both must be created beforehand.

session = client.beta.sessions.create(
    agent=agent.id,
    environment_id=environment.id,
)

(Sessions)

The returned session object includes:

  • id — e.g., "sesn_01..." — your reference for all subsequent operations
  • status — starts at idle

Creating a session provisions the environment and agent but does not start any work. To delegate a task, you send events.

Session Statuses

Sessions operate as a state machine with four statuses:

Status Description
idle Agent is waiting for input. Sessions start in idle.
running Agent is actively executing.
rescheduling Transient error occurred; retrying automatically.
terminated Session ended due to an unrecoverable error.

(Sessions)

The most important operational distinction: when a session is idle, it's either waiting for your first task, finished with a task, or paused waiting for tool confirmation. The stop_reason field on the accompanying session.status_idle event tells you which one.

Pinning Agent Versions

By default, creating a session uses the latest agent version. To run a specific version:

pinned_session = client.beta.sessions.create(
    agent={"type": "agent", "id": agent.id, "version": 1},
    environment_id=environment.id,
)

"This lets you control exactly which version runs and stage rollouts of new versions independently." (Sessions)

Use version pinning for production deployments where you're rolling out a new system prompt. Test the new version on a small percentage of sessions before making it the default.

Attaching Resources: Files, Memory Stores, Repositories

The most powerful aspect of session creation is the resources array — it lets you pre-load the session with files, memory stores, and repositories before the agent starts any work.

Attaching a file:

session = client.beta.sessions.create(
    agent=agent.id,
    environment_id=environment.id,
    resources=[
        {
            "type": "file",
            "file_id": file.id,
            "mount_path": "/workspace/data.csv",
        },
    ],
)

Attaching a memory store:

session = client.beta.sessions.create(
    agent=agent.id,
    environment_id=environment.id,
    resources=[
        {
            "type": "memory_store",
            "memory_store_id": store.id,
            "access": "read_write",
            "prompt": "User preferences and project context.",
        },
    ],
)

Attaching a GitHub repository:

session = client.beta.sessions.create(
    agent=agent.id,
    environment_id=environment.id,
    resources=[
        {
            "type": "github_repository",
            "url": "https://github.com/org/repo",
            "mount_path": "/workspace/repo",
            "authorization_token": "ghp_your_github_token",
        },
    ],
)

(Sessions)

All three resource types can be combined in a single resources array. A session can have up to 100 files, 8 memory stores, and multiple repositories attached.

MCP Authentication at Session Creation

If your agent uses MCP tools that require authentication, pass vault_ids at session creation:

vault_session = client.beta.sessions.create(
    agent=agent.id,
    environment_id=environment.id,
    vault_ids=[vault.id],
)

"If your agent uses MCP tools that require authentication, pass vault_ids at session creation to reference a vault containing stored OAuth credentials. Anthropic manages token refresh on your behalf." (Sessions)

Session Operations

# Retrieve a session
retrieved = client.beta.sessions.retrieve(session.id)
print(f"Status: {retrieved.status}")

# List all sessions
for session in client.beta.sessions.list():
    print(f"{session.id}: {session.status}")

# Archive a session (prevents new events, preserves history)
client.beta.sessions.archive(session.id)

# Delete a session (permanent; cannot delete a running session)
client.beta.sessions.delete(session.id)

(Sessions)

Important notes on deletion: "Delete a session to permanently remove its record, events, and associated container. A running session cannot be deleted; send an interrupt event if you need to delete it immediately." Also: "Files, memory stores, environments, and agents are independent resources and are not affected by session deletion."

Tracking Token Usage

The session object includes cumulative token statistics. Fetch the session after it goes idle to read the latest totals:

{
  "id": "sesn_01...",
  "status": "idle",
  "usage": {
    "input_tokens": 5000,
    "output_tokens": 3200,
    "cache_creation_input_tokens": 2000,
    "cache_read_input_tokens": 20000
  }
}

"input_tokens reports uncached input tokens and output_tokens reports total output tokens across all model calls in the session. Cache entries use a 5-minute TTL." (Session event stream)

This is your cost tracking tool. Compare input_tokens to cache_read_input_tokens to see how much caching is saving you. High cache_read_input_tokens relative to input_tokens means your prompt structure is cache-friendly.

Sessions vs. Chat Conversations — The Key Differences

Chat Conversation Session
Stateless (history in client only) Stateful (history stored server-side)
Ends when the window closes Persists until archived or deleted
No tools available Full tool access
Single context window Supports context compaction for long tasks
Synchronous request-response Asynchronous event stream
No resource attachments Files, memory stores, repositories
DiagramComparison table rendered as two side-by-side cards. Each card has 6 property rows. Chat Conversation card on the left in grey/muted tones. Session card on the right in full color with each row highlighting the upgrade. The "Persists until archived or deleted" row especially emphasized.

Try it yourself

Try It Yourself

  1. Create a session with a file attachment. First, upload a sample file:

    from pathlib import Path
    import anthropic
    client = anthropic.Anthropic()
    
    file = client.beta.files.upload(file=Path("sample_data.csv"))
    print(f"File ID: {file.id}")
    
  2. Create a session with the file mounted:

    session = client.beta.sessions.create(
        agent=agent.id,
        environment_id=environment.id,
        resources=[
            {
                "type": "file",
                "file_id": file.id,
                "mount_path": "/workspace/data.csv",
            },
        ],
    )
    print(f"Session ID: {session.id}, Status: {session.status}")
    
  3. Send a task asking the agent to analyze the file. Use the streaming pattern from Chapter 5. Send: "Read the data.csv file at /workspace/data.csv and give me a summary of what's in it."

  4. After the session finishes, check usage:

    retrieved = client.beta.sessions.retrieve(session.id)
    print(retrieved.usage)
    

    Note the token counts. How does the input token count compare to what you'd expect?

  5. Send a follow-up message in the same session. Don't create a new session — send another user.message event to the existing session. Confirm the agent remembers the file analysis from the previous turn.

  6. Archive the session when done:

    client.beta.sessions.archive(session.id)
    

    This prevents new events but preserves the history for auditing.

DiagramSession lifecycle diagram. Line from left to right showing: "Create" → "Idle" → "Send Event" → "Running" → "Idle (end_turn)" → "Send Follow-up" → "Running" → "Idle" → "Archive." Below: "History preserved at every step." Separate branch from "Running" showing: "Transient Error" → "Rescheduling" → "Running" (recovery).

Common pitfalls

Common Pitfalls

  • Creating a new session for every follow-up message. Sessions persist history. If you want the agent to remember what it did in a previous turn, use the same session — send a new user.message to the existing session ID. Creating a new session every time is like starting a fresh chat with no memory of what was discussed.

  • Deleting a running session. You can't delete a session in running status. Send an interrupt event first, wait for the status to change to idle or terminated, then delete.

  • Assuming files survive between sessions. Each session gets its own isolated container instance. Files written during Session A are not available in Session B unless you explicitly save them to outputs and re-mount them. For cross-session persistence, use memory stores or the Files API.

  • Not reading the usage field. Token costs accumulate per session and the usage field gives you the breakdown. Ignoring it means you won't notice when sessions are unexpectedly token-heavy.

  • Forgetting that session creation doesn't start work. After client.beta.sessions.create(), the agent is waiting. Nothing happens until you send events. This surprises developers who expect creation to immediately trigger action.


Toolkit

Toolkit

  • Session Lifecycle Cheat Sheet — Visual lifecycle diagram plus all four status descriptions and the operations available in each status. Includes the archiving vs. deletion decision tree.

  • Resource Attachment Quick Reference — Code snippets for attaching each resource type (file, memory store, GitHub repository) with annotation on required vs. optional fields.


Chapter Recap

  • A session is a durable work unit that maintains the complete event log, file system state, and conversation history across multiple interactions. It's not a chat conversation — it persists until you archive or delete it.
  • Creating a session provisions the infrastructure but doesn't start work. Work begins when you send events. Sessions move through four statuses: idle, running, rescheduling, terminated.
  • Attach resources at session creation — files, memory stores, and GitHub repositories all go in the resources array. Track token usage via session.usage after each idle period.