obsidian/wiki/agent-sdk/sessions.md
2026-04-17 13:24:18 +01:00

7 KiB

title aliases tags sources created updated
Sessions — Persisting and Resuming Conversation History
session-management
continue-resume-fork
agent-sdk
sessions
python
typescript
conversation-history
raw/Work with sessions.md
2026-04-17 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.


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.

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.

// 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.


Manual Session Options with query()

Capture the Session ID

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

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

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.

# 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.


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

Sources

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