Session Management in the Claude Agent SDK

Resume sessions, fork conversations from specific messages, and list session history. The session ID patterns that power multi-turn agents and persistent conversation tools.

4 min read
session resumption SDK fork session agent listSessions SDK getSessionMessages session ID management

How sessions work by default

Every time you call query, the SDK creates a new session. The agent starts with a clean context — no memory of previous runs. For one-off tasks, that is fine. Generate a report, summarize a document, extract data from a file — these do not need memory.

For agents you interact with over time — a research assistant you return to across multiple calls, a client brief agent that builds context session by session, a Slack bot that remembers what it already told a teammate — you need session management.

Session resumption

Resuming a session requires the session ID from the previous run. The SDK returns it in the result message:

let sessionId: string | undefined;

for await (const message of query(messages(), {
  model: "claude-sonnet-4-5",
  permissionMode: "bypassPermissions",
  dangerouslyAllowBypassPermissions: true,
})) {
  if (message.type === "result") {
    sessionId = message.session_id;
    console.log("Response:", message.result);
  }
}

// Next turn — resume the same session
for await (const message of query(nextMessages(), {
  model: "claude-sonnet-4-5",
  resume: sessionId,
  permissionMode: "bypassPermissions",
  dangerouslyAllowBypassPermissions: true,
})) {
  if (message.type === "result") {
    console.log("Response:", message.result);
  }
}

Save the session ID the moment you receive it. If you run another query before capturing it, the session ID is gone and resumption is impossible. Build the save step into your result handler before anything else.

The practical pattern: persist session IDs to a SQLite database or a JSON file, keyed by whatever identifier makes sense for your workflow — a client name, a project slug, a Slack thread timestamp.

Session forking

Forking creates a new session branched from an existing one at a specific message. The original session stays intact. You get a parallel thread starting from that point.

The operator use case: you have gathered research and produced a first synthesis. You want to explore two different angles — one for an investor audience, one for an operational team — without mixing their contexts.

for await (const message of query(forkMessages(), {
  model: "claude-sonnet-4-5",
  resume: originalSessionId,
  forkSession: true,
  resumeAt: specificMessageId,
  permissionMode: "bypassPermissions",
  dangerouslyAllowBypassPermissions: true,
})) {
  if (message.type === "result") {
    const forkSessionId = message.session_id;
    // Independent session from this point forward
  }
}

resumeAt takes the message ID from an assistant turn in the original session — a UUID logged when you captured that message. Each fork produces its own session ID and evolves independently from the branch point.

Listing sessions and messages

The SDK exports two utility functions for working with session history:

import { query, listSessions, getSessionMessages } from "@anthropic-ai/claude-agent-sdk";

// List all sessions in a directory
const sessions = await listSessions(process.cwd());

// Get all messages for a specific session
const messages = await getSessionMessages(sessionId);

listSessions is useful for building session browsers or debugging which session holds the context you need. getSessionMessages lets you load prior conversation history into a new agent — useful for building brief-generation tools that summarize what happened across a client engagement.

Session IDs in multi-user deployments

For any agent serving more than one person — a team Slack bot, a client-facing research tool — the session ID is what separates one conversation thread from another. Each thread gets its own session. Resuming a thread means passing that thread's session ID.

The practical Slack pattern:

// Key: channel_id + thread_timestamp → session_id
const existingSessionId = sessionStore.get(threadKey);

const { response, sessionId } = await agentChat(message, existingSessionId);

sessionStore.set(threadKey, sessionId);

A new Slack thread gets a new session. Every message in the same thread resumes the same session. The agent remembers what it said two messages ago, what data it pulled, what it already summarized. The conversation builds naturally instead of resetting every time.

The mistake to avoid: Using one user's session ID for another user's request. If your Slack bot serves five team members, each thread needs its own session ID scoped to that thread. A DM and a channel thread are different conversations — mixing their sessions corrupts both.

---

Author: FractionalSkill

Keep Going

Ready to Start Building?

Pick the next step that matches where you are right now.

Tutorial
Claude Code Basics

Start with the terminal basics. A hands-on, step-by-step guide to your first 10 minutes with Claude Code.

Start the Tutorial
Guide
AI-Powered Workflows

Automate your client work. Learn how to connect AI tools into workflows that handle repetitive tasks for you.

Read the Guide
Community
Join the Community

Connect with other fractional leaders building with AI. Share workflows, get feedback, and learn from operators who are ahead of you.

Apply to Join