---
title: "Sessions — Persisting and Resuming Conversation History"
aliases: [session-management, continue-resume-fork]
tags: [agent-sdk, sessions, python, typescript, conversation-history]
sources: [raw/Work with sessions.md]
created: 2026-04-17
updated: 2026-04-17
---

# Sessions — Persisting and Resuming Conversation History

A **session** is the full conversation history the SDK accumulates during a `query()` run: your prompt, every tool call, every tool result, every response. The SDK writes it to disk automatically under `~/.claude/projects/<encoded-cwd>/<session-id>.jsonl`.

Sessions persist the **conversation**, not the filesystem. To snapshot/revert file changes use [[wiki/agent-sdk/file-checkpointing|file-checkpointing]].

---

## Choosing an Approach

| Scenario | What to use |
|---|---|
| One-shot task, no follow-up | Nothing extra. One `query()` call. |
| Multi-turn chat in one process | `ClaudeSDKClient` (Python) or `continue: true` (TypeScript) |
| Pick up after a process restart | `continue_conversation=True` / `continue: true` — resumes most recent session, no ID needed |
| Resume a specific past session | Capture session ID → pass to `resume` |
| Try an alternative without losing original | Fork the session |
| Stateless, no disk writes (TypeScript only) | `persistSession: false` |

---

## Automatic Session Management

### Python — `ClaudeSDKClient`

`ClaudeSDKClient` tracks the session ID internally. Every `client.query()` call automatically continues the same session. Use it as an async context manager.

```python
async with ClaudeSDKClient(options=options) as client:
    await client.query("Analyze the auth module")
    async for message in client.receive_response():
        print_response(message)

    # automatically continues same session
    await client.query("Now refactor it to use JWT")
    async for message in client.receive_response():
        print_response(message)
```

### TypeScript — `continue: true`

The stable V1 `query()` has no session-holding client object. Pass `continue: true` on each subsequent call — SDK finds and resumes the most recent session on disk.

```typescript
// First query — creates a new session
for await (const message of query({ prompt: "Analyze the auth module", ... })) { ... }

// Second query — resumes most recent session
for await (const message of query({
  prompt: "Now refactor it to use JWT",
  options: { continue: true, allowedTools: [...] }
})) { ... }
```

> V2 preview (`createSession()` / `send` / `stream`) is closer to Python's client feel but is unstable — stick to V1 for production. See [[wiki/agent-sdk/typescript-v2-interface|typescript-v2-interface]].

---

## Manual Session Options with `query()`

### Capture the Session ID

Read `session_id` from the `ResultMessage` — present on every result, success or error.

```python
async for message in query(prompt="...", options=ClaudeAgentOptions(...)):
    if isinstance(message, ResultMessage):
        session_id = message.session_id
```

In TypeScript the ID is also on the init `SystemMessage` (earlier than `ResultMessage`).

### Resume by ID

```python
async for message in query(
    prompt="Now implement the refactoring you suggested",
    options=ClaudeAgentOptions(
        resume=session_id,
        allowed_tools=["Read", "Edit", "Write", "Glob", "Grep"],
    ),
): ...
```

**Common resume use cases:**
- Follow up on a completed task without re-reading files
- Recover from `error_max_turns` or `error_max_budget_usd` with higher limits
- Restore a conversation after process restart

**Gotcha — mismatched `cwd`:** Sessions are stored under `~/.claude/projects/<encoded-cwd>/`. If the resume call runs from a different directory, the SDK looks in the wrong folder and returns a fresh session. The `cwd` must match exactly.

### Fork to Explore Alternatives

Fork creates a new session starting with a copy of the original's history. The original stays unchanged. Both sessions get their own IDs and can be resumed independently.

```python
# Fork: branch from session_id, try OAuth2
forked_id = None
async for message in query(
    prompt="Instead of JWT, implement OAuth2 for the auth module",
    options=ClaudeAgentOptions(resume=session_id, fork_session=True),
):
    if isinstance(message, ResultMessage):
        forked_id = message.session_id

# Original session untouched — continue JWT thread
async for message in query(
    prompt="Continue with the JWT approach",
    options=ClaudeAgentOptions(resume=session_id),
): ...
```

> Fork branches conversation history only. File changes from a forked agent are real and visible to all sessions in the directory. To branch files too, combine with [[wiki/agent-sdk/file-checkpointing|file-checkpointing]].

---

## Resume Across Hosts

Session files are **local** to the machine that created them.

| Option | How |
|---|---|
| Move the file | Persist `~/.claude/projects/<encoded-cwd>/<session-id>.jsonl`, restore it to the same path on the new host, ensure `cwd` matches |
| Skip session resume | Capture the output you need (analysis, decisions, diffs) and pass it into a fresh session's prompt — often more robust |

---

## Session Utility Functions

Both SDKs expose helpers for working with sessions on disk:

| Purpose | Python | TypeScript |
|---|---|---|
| List sessions | `list_sessions()` | `listSessions()` |
| Read messages | `get_session_messages()` | `getSessionMessages()` |
| Get session info | `get_session_info()` | `getSessionInfo()` |
| Rename session | `rename_session()` | `renameSession()` |
| Tag session | `tag_session()` | `tagSession()` |

Use these to build custom session pickers, cleanup logic, or transcript viewers.

---

## Key Takeaways

- Sessions = serialized conversation history (prompts + tool calls + results), written as `.jsonl` files
- **Continue** = resume most recent session by directory; no ID needed
- **Resume** = return to a specific session by ID; required for multi-user or non-latest sessions
- **Fork** = copy history into a new session; original untouched; both independently resumable
- Mismatched `cwd` is the #1 cause of resume returning a blank session
- For cross-host resuming, moving the `.jsonl` file is possible but passing output as a fresh prompt is more robust
- `persistSession: false` (TypeScript only) keeps the session in memory only — nothing written to disk

---

## Related

- [[wiki/agent-sdk/agent-loop|agent-loop]] — how turns, messages, and context accumulate within a session
- [[wiki/agent-sdk/file-checkpointing|file-checkpointing]] — track and revert file changes across sessions
- [[wiki/agent-sdk/python-api-reference|python-api-reference]] — `ClaudeAgentOptions` session fields reference
- [[wiki/agent-sdk/typescript-api-reference|typescript-api-reference]] — `Options` session fields reference
- [[wiki/agent-sdk/typescript-v2-interface|typescript-v2-interface]] — V2 `createSession()` / `resumeSession()` preview
- [[wiki/agent-sdk/streaming-input|streaming-input]] — notes on session resuming with streaming input

## Sources

- `raw/Work with sessions.md` — official Agent SDK sessions documentation
