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 conversations. For one-off tasks — summarize this document, extract these data points, generate this report — that is fine.

For everything else, you need session management.

Session resumption

Resuming a session requires the session ID from the previous run. The session ID is in the result message:

let sessionId: string | undefined;

for await (const message of query(messages(), {
  model: "claude-haiku-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-haiku-4-5",
  resume: sessionId,
  permissionMode: "bypassPermissions",
  dangerouslyAllowBypassPermissions: true,
})) {
  if (message.type === "result") {
    console.log("Response:", message.result);
  }
}

Save the session ID the moment you get it. If you run the query again without saving it first, the old session ID is gone and you cannot resume. Build the save step into your result message handler before you do anything else with the response.

Session forking

Forking creates a new session branching off from an existing one at a specific message. The original session is preserved. You get a second session that starts from that branch point.

Use case: the agent has done research and produced a summary. You want to explore two different directions from that summary without contaminating either thread with the other's context.

for await (const message of query(forkMessages(), {
  model: "claude-haiku-4-5",
  resume: originalSessionId,
  forkSession: true,
  resumeAt: specificMessageId,  // the message ID from the assistant turn
  permissionMode: "bypassPermissions",
  dangerouslyAllowBypassPermissions: true,
})) {
  if (message.type === "result") {
    const forkSessionId = message.session_id;
    // This is now a different session from the original
  }
}

The specificMessageId comes from an assistant message in the original session. Each assistant message has its own ID — a UUID you can grab when you log messages.

Listing sessions and messages

The SDK exports two additional functions beyond query:

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

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

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

listSessions is useful for building session browsers or debugging tools. getSessionMessages is useful for loading previous conversation context into a new agent, building summary tools, or debugging what the agent did in a specific session.

The session ID is your conversation identity

In any multi-user deployment — a Slack bot serving a team, a client-facing tool accessed by multiple people — the session ID is what separates one conversation from another. Each thread gets its own session ID. Resuming a thread means passing the session ID for that thread's previous turns.

The practical pattern for Slack deployments:

// Map: thread_key → session_id
// thread_key = channel_id + thread_timestamp

const existingSessionId = sessionStore.get(threadKey);

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

sessionStore.set(threadKey, sessionId);

The session persistence guide covers the full SQLite implementation of this pattern.

> Common mistake: Using the session ID from one user's conversation for another user's request. Always scope session IDs to the specific thread or conversation they belong to. A Slack DM and a channel thread are different conversations and should always have different session IDs.

---

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