176 lines
7 KiB
Markdown
176 lines
7 KiB
Markdown
---
|
|
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
|