Follow Discord
Sweep 22 Oct 2026 · 04:32Z Build v2.1.308 527 read Stable v2.1.308 Latest v2.1.308 Next v2.1.308 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One change · claude-code

Work with sessions changedagent-sdk/sessions

Upstream edited this page at 10 Oct 2026 18:00 UTC, give or take a minute or two: the time comes from Anthropic’s own sitemap rather than from a commit. This site recorded the change at 20 Oct 2026 18:37 UTC.

Upstream edited
Recorded here
Lines+220added
Lines−226removed
From line 6 where the diff opens
First seen 14 Aug 2026 this site's first read of the page
Recorded edits6to this page, all time

The whole hunk

from line 6, old and new numbered
/
lines
from line 6
66 
77Returning to a session means the agent has full context from before: files it already read, analysis it already performed, decisions it already made. You can ask a follow-up question, recover from an interruption, or branch off to try a different approach.
88 
9<Note>
10 Sessions persist the **conversation**, not the filesystem. To snapshot and revert file changes the agent made, use [file checkpointing](/docs/en/agent-sdk/file-checkpointing).
11</Note>
9> [!NOTE]
10>
11> Sessions persist the **conversation**, not the filesystem. To snapshot and revert file changes the agent made, use [file checkpointing](/docs/en/agent-sdk/file-checkpointing).
1212 
1313This guide covers how to pick the right approach for your app, the SDK interfaces that track sessions automatically, how to capture session IDs and use `resume` and `fork` manually, and what to know about resuming sessions across hosts.
1414 
from line 135
135135}
136136```
137137 
138<Note>
139 The experimental [V2 session API](/docs/en/agent-sdk/typescript-v2-preview), which provided `createSession()` with a `send` / `stream` pattern, was removed in TypeScript Agent SDK 0.3.142. Use the `query()` function and the session options described on this page instead.
140</Note>
138> [!NOTE]
139>
140> The experimental [V2 session API](/docs/en/agent-sdk/typescript-v2-preview), which provided `createSession()` with a `send` / `stream` pattern, was removed in TypeScript Agent SDK 0.3.142. Use the `query()` function and the session options described on this page instead.
141141 
142142## Use session options with `query()`
143143 
from line 145
145145 
146146Resume and fork require a session ID. Read it from the `session_id` field on the result message ([`ResultMessage`](/docs/en/agent-sdk/python#resultmessage) in Python, [`SDKResultMessage`](/docs/en/agent-sdk/typescript#sdkresultmessage) in TypeScript), which is present on every result regardless of success or error. In TypeScript the ID is also available earlier as a direct field on the init `SystemMessage`; in Python it's nested inside `SystemMessage.data`.
147147 
148<CodeGroup>
149 ```python Python theme={null}
150 import asyncio
151 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
148```python Python theme={null}
149import asyncio
150from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
152151 
153152 
154 async def main():
155 session_id = None
153async def main():
154 session_id = None
156155 
157 try:
158 async for message in query(
159 prompt="Analyze the auth module and suggest improvements",
160 options=ClaudeAgentOptions(
161 allowed_tools=["Read", "Glob", "Grep"],
162 ),
163 ):
164 if isinstance(message, ResultMessage):
165 session_id = message.session_id
166 if message.subtype == "success":
167 print(message.result)
168 except Exception as error:
169 # A single-shot query() raises after yielding an error result. If the
170 # failure was an error result, the loop above already captured session_id;
171 # connection or process failures yield no result message, so session_id stays None.
172 print(f"Session ended with an error: {error}")
156 try:
157 async for message in query(
158 prompt="Analyze the auth module and suggest improvements",
159 options=ClaudeAgentOptions(
160 allowed_tools=["Read", "Glob", "Grep"],
161 ),
162 ):
163 if isinstance(message, ResultMessage):
164 session_id = message.session_id
165 if message.subtype == "success":
166 print(message.result)
167 except Exception as error:
168 # A single-shot query() raises after yielding an error result. If the
169 # failure was an error result, the loop above already captured session_id;
170 # connection or process failures yield no result message, so session_id stays None.
171 print(f"Session ended with an error: {error}")
173172 
174 print(f"Session ID: {session_id}")
175 return session_id
173 print(f"Session ID: {session_id}")
174 return session_id
176175 
177176 
178 session_id = asyncio.run(main())
179 ```
177session_id = asyncio.run(main())
178```
180179 
181 ```typescript TypeScript theme={null}
182 import { query } from "@anthropic-ai/claude-agent-sdk";
180```typescript TypeScript theme={null}
181import { query } from "@anthropic-ai/claude-agent-sdk";
183182 
184 let sessionId: string | undefined;
183let sessionId: string | undefined;
185184 
186 try {
187 for await (const message of query({
188 prompt: "Analyze the auth module and suggest improvements",
189 options: { allowedTools: ["Read", "Glob", "Grep"] }
190 })) {
191 if (message.type === "result") {
192 sessionId = message.session_id;
193 if (message.subtype === "success") {
194 console.log(message.result);
195 }
185try {
186 for await (const message of query({
187 prompt: "Analyze the auth module and suggest improvements",
188 options: { allowedTools: ["Read", "Glob", "Grep"] }
189 })) {
190 if (message.type === "result") {
191 sessionId = message.session_id;
192 if (message.subtype === "success") {
193 console.log(message.result);
196194 }
197195 }
198 } catch (error) {
199 // A single-shot query() throws after yielding an error result. If the
200 // failure was an error result, the loop above already captured sessionId;
201 // connection or process failures yield no result message, so sessionId stays undefined.
202 console.error(`Session ended with an error: ${error}`);
203196 }
197} catch (error) {
198 // A single-shot query() throws after yielding an error result. If the
199 // failure was an error result, the loop above already captured sessionId;
200 // connection or process failures yield no result message, so sessionId stays undefined.
201 console.error(`Session ended with an error: ${error}`);
202}
204203 
205 console.log(`Session ID: ${sessionId}`);
206 ```
207</CodeGroup>
204console.log(`Session ID: ${sessionId}`);
205```
208206 
209207When the query completes, the script prints the agent's response followed by a line such as `Session ID: 5b3f2c1a-8d4e-4f6b-9a7c-2e1d0f9b8a6c`. In the next sections, you pass this ID to `resume`.
210208 
from line 216
218216 
219217This example resumes the session from [Capture the session ID](#capture-the-session-id) with a follow-up prompt. Because you're resuming, the agent already has the prior analysis in context:
220218 
221<CodeGroup>
222 ```python Python theme={null}
223 import asyncio
224 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
219```python Python theme={null}
220import asyncio
221from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
225222 
226 session_id = "..." # The ID you captured in the previous example
223session_id = "..." # The ID you captured in the previous example
227224 
228225 
229 async def main():
230 # Earlier session analyzed the code; now build on that analysis
231 async for message in query(
232 prompt="Now implement the refactoring you suggested",
233 options=ClaudeAgentOptions(
234 resume=session_id,
235 allowed_tools=["Read", "Edit", "Write", "Glob", "Grep"],
236 ),
237 ):
238 if isinstance(message, ResultMessage) and message.subtype == "success":
239 print(message.result)
226async def main():
227 # Earlier session analyzed the code; now build on that analysis
228 async for message in query(
229 prompt="Now implement the refactoring you suggested",
230 options=ClaudeAgentOptions(
231 resume=session_id,
232 allowed_tools=["Read", "Edit", "Write", "Glob", "Grep"],
233 ),
234 ):
235 if isinstance(message, ResultMessage) and message.subtype == "success":
236 print(message.result)
240237 
241238 
242 asyncio.run(main())
243 ```
239asyncio.run(main())
240```
244241 
245 ```typescript TypeScript theme={null}
246 import { query } from "@anthropic-ai/claude-agent-sdk";
242```typescript TypeScript theme={null}
243import { query } from "@anthropic-ai/claude-agent-sdk";
247244 
248 const sessionId = "..."; // The ID you captured in the previous example
245const sessionId = "..."; // The ID you captured in the previous example
249246 
250 // Earlier session analyzed the code; now build on that analysis
251 for await (const message of query({
252 prompt: "Now implement the refactoring you suggested",
253 options: {
254 resume: sessionId,
255 allowedTools: ["Read", "Edit", "Write", "Glob", "Grep"]
256 }
257 })) {
258 if (message.type === "result" && message.subtype === "success") {
259 console.log(message.result);
260 }
247// Earlier session analyzed the code; now build on that analysis
248for await (const message of query({
249 prompt: "Now implement the refactoring you suggested",
250 options: {
251 resume: sessionId,
252 allowedTools: ["Read", "Edit", "Write", "Glob", "Grep"]
261253 }
262 ```
263</CodeGroup>
254})) {
255 if (message.type === "result" && message.subtype === "success") {
256 console.log(message.result);
257 }
258}
259```
264260 
265261You should see a response that builds on the earlier analysis instead of starting fresh. That confirms the agent resumed the session with its prior context intact.
266262 
267<Tip>
268 Claude Code stores sessions under `~/.claude/projects/<encoded-cwd>/*.jsonl`. If you set the `CLAUDE_CONFIG_DIR` environment variable, look under `$CLAUDE_CONFIG_DIR/projects/` instead.
263> [!TIP]
264>
265> Claude Code stores sessions under `~/.claude/projects/<encoded-cwd>/*.jsonl`. If you set the `CLAUDE_CONFIG_DIR` environment variable, look under `$CLAUDE_CONFIG_DIR/projects/` instead.
266>
267> To find your session's directory, replace every non-alphanumeric character in the absolute working directory with `-`: `/Users/me/proj` becomes `-Users-me-proj`. For a working directory whose converted name exceeds 200 characters, Claude Code [truncates the name and appends a hash](/docs/en/sessions#where-transcripts-are-stored), so match the first 200 characters of the converted name when you list `projects/`.
268>
269> If you set [`CLAUDE_CODE_PROJECT_DIR_NAME`](/docs/en/sessions#name-the-project-directory-yourself) beside `CLAUDE_CONFIG_DIR`, look under that name in `projects/` instead. Requires TypeScript Agent SDK v0.3.234 or later, or Python Agent SDK v0.2.140 or later.
270>
271> You can resume from any working directory:
272>
273> * **Cross-directory lookup**: Claude Code searches beyond the current project directory to find the ID; see [Resume a session](/docs/en/sessions#where-the-session-picker-looks) for the exact lookup order and how duplicate copies are handled.
274> * **Same machine only**: the session file still needs to exist on the current machine.
275>
276> Before v2.1.223, the lookup was scoped to the current project directory and its git worktrees; SDK versions that bundle an older CLI still behave this way.
269277 
270 To find your session's directory, replace every non-alphanumeric character in the absolute working directory with `-`: `/Users/me/proj` becomes `-Users-me-proj`. For a working directory whose converted name exceeds 200 characters, Claude Code [truncates the name and appends a hash](/docs/en/sessions#where-transcripts-are-stored), so match the first 200 characters of the converted name when you list `projects/`.
271 
272 If you set [`CLAUDE_CODE_PROJECT_DIR_NAME`](/docs/en/sessions#name-the-project-directory-yourself) beside `CLAUDE_CONFIG_DIR`, look under that name in `projects/` instead. Requires TypeScript Agent SDK v0.3.234 or later, or Python Agent SDK v0.2.140 or later.
273 
274 You can resume from any working directory:
275 
276 * **Cross-directory lookup**: Claude Code searches beyond the current project directory to find the ID; see [Resume a session](/docs/en/sessions#where-the-session-picker-looks) for the exact lookup order and how duplicate copies are handled.
277 * **Same machine only**: the session file still needs to exist on the current machine.
278 
279 Before v2.1.223, the lookup was scoped to the current project directory and its git worktrees; SDK versions that bundle an older CLI still behave this way.
280</Tip>
281 
282278To resume sessions across machines or in serverless environments, mirror transcripts to shared storage with a [`SessionStore` adapter](/docs/en/agent-sdk/session-storage).
283279 
284280### Fork to explore alternatives
from line 281
285281 
286282Forking creates a new session that starts with a copy of the original's history but diverges from that point. The fork gets its own session ID; the original's ID and history stay unchanged. You end up with two independent sessions you can resume separately.
287283 
288<Note>
289 Forking branches the conversation history, not the filesystem. If a forked agent edits files, those changes are real and visible to any session working in the same directory. To branch and revert file changes, use [file checkpointing](/docs/en/agent-sdk/file-checkpointing).
290</Note>
284> [!NOTE]
285>
286> Forking branches the conversation history, not the filesystem. If a forked agent edits files, those changes are real and visible to any session working in the same directory. To branch and revert file changes, use [file checkpointing](/docs/en/agent-sdk/file-checkpointing).
291287 
292288This example builds on [Capture the session ID](#capture-the-session-id): you've already analyzed an auth module in `session_id` and want to explore OAuth2 without losing the JWT-focused thread. The first block forks the session and captures the fork's ID (`forked_id`); the second block resumes the original `session_id` to continue down the JWT path. You now have two session IDs pointing at two separate histories:
293289 
294<CodeGroup>
295 ```python Python theme={null}
296 import asyncio
297 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
290```python Python theme={null}
291import asyncio
292from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
298293 
299 session_id = "..." # The ID you captured in the previous example
294session_id = "..." # The ID you captured in the previous example
300295 
301296 
302 async def main():
303 # Fork: branch from session_id into a new session
304 forked_id = None
305 try:
306 async for message in query(
307 prompt="Instead of JWT, outline how OAuth2 would work for the auth module",
308 options=ClaudeAgentOptions(
309 resume=session_id,
310 fork_session=True,
311 max_turns=5,
312 ),
313 ):
314 if isinstance(message, ResultMessage):
315 forked_id = message.session_id # The fork's ID, distinct from session_id
316 if message.subtype == "success":
317 print(message.result)
318 except Exception as error:
319 # A single-shot query() raises after yielding an error result. If the
320 # failure was an error result, forked_id was already captured by the
321 # loop above; connection or process failures yield no result message.
322 print(f"Session ended with an error: {error}")
297async def main():
298 # Fork: branch from session_id into a new session
299 forked_id = None
300 try:
301 async for message in query(
302 prompt="Instead of JWT, outline how OAuth2 would work for the auth module",
303 options=ClaudeAgentOptions(
304 resume=session_id,
305 fork_session=True,
306 max_turns=5,
307 ),
308 ):
309 if isinstance(message, ResultMessage):
310 forked_id = message.session_id # The fork's ID, distinct from session_id
311 if message.subtype == "success":
312 print(message.result)
313 except Exception as error:
314 # A single-shot query() raises after yielding an error result. If the
315 # failure was an error result, forked_id was already captured by the
316 # loop above; connection or process failures yield no result message.
317 print(f"Session ended with an error: {error}")
323318 
324 print(f"Forked session: {forked_id}")
319 print(f"Forked session: {forked_id}")
325320 
326 # Original session is untouched; resuming it continues the JWT thread
327 try:
328 async for message in query(
329 prompt="Continue with the JWT approach",
330 options=ClaudeAgentOptions(resume=session_id),
331 ):
332 if isinstance(message, ResultMessage) and message.subtype == "success":
333 print(message.result)
334 except Exception as error:
335 # A single-shot query() raises after yielding an error result.
336 print(f"Session ended with an error: {error}")
321 # Original session is untouched; resuming it continues the JWT thread
322 try:
323 async for message in query(
324 prompt="Continue with the JWT approach",
325 options=ClaudeAgentOptions(resume=session_id),
326 ):
327 if isinstance(message, ResultMessage) and message.subtype == "success":
328 print(message.result)
329 except Exception as error:
330 # A single-shot query() raises after yielding an error result.
331 print(f"Session ended with an error: {error}")
337332 
338333 
339 asyncio.run(main())
340 ```
334asyncio.run(main())
335```
341336 
342 ```typescript TypeScript theme={null}
343 import { query } from "@anthropic-ai/claude-agent-sdk";
337```typescript TypeScript theme={null}
338import { query } from "@anthropic-ai/claude-agent-sdk";
344339 
345 const sessionId = "..."; // The ID you captured in the previous example
340const sessionId = "..."; // The ID you captured in the previous example
346341 
347 // Fork: branch from sessionId into a new session
348 let forkedId: string | undefined;
342// Fork: branch from sessionId into a new session
343let forkedId: string | undefined;
349344 
350 try {
351 for await (const message of query({
352 prompt: "Instead of JWT, outline how OAuth2 would work for the auth module",
353 options: {
354 resume: sessionId,
355 forkSession: true,
356 maxTurns: 5
357 }
358 })) {
359 if (message.type === "system" && message.subtype === "init") {
360 forkedId = message.session_id; // The fork's ID, distinct from sessionId
361 }
362 if (message.type === "result" && message.subtype === "success") {
363 console.log(message.result);
364 }
345try {
346 for await (const message of query({
347 prompt: "Instead of JWT, outline how OAuth2 would work for the auth module",
348 options: {
349 resume: sessionId,
350 forkSession: true,
351 maxTurns: 5
365352 }
366 } catch (error) {
367 // A single-shot query() throws after yielding an error result. If the
368 // failure was an error result, forkedId was already captured by the loop
369 // above; connection or process failures yield no result message.
370 console.error(`Session ended with an error: ${error}`);
353 })) {
354 if (message.type === "system" && message.subtype === "init") {
355 forkedId = message.session_id; // The fork's ID, distinct from sessionId
356 }
357 if (message.type === "result" && message.subtype === "success") {
358 console.log(message.result);
359 }
371360 }
361} catch (error) {
362 // A single-shot query() throws after yielding an error result. If the
363 // failure was an error result, forkedId was already captured by the loop
364 // above; connection or process failures yield no result message.
365 console.error(`Session ended with an error: ${error}`);
366}
372367 
373 console.log(`Forked session: ${forkedId}`);
368console.log(`Forked session: ${forkedId}`);
374369 
375 // Original session is untouched; resuming it continues the JWT thread
376 try {
377 for await (const message of query({
378 prompt: "Continue with the JWT approach",
379 options: { resume: sessionId }
380 })) {
381 if (message.type === "result" && message.subtype === "success") {
382 console.log(message.result);
383 }
370// Original session is untouched; resuming it continues the JWT thread
371try {
372 for await (const message of query({
373 prompt: "Continue with the JWT approach",
374 options: { resume: sessionId }
375 })) {
376 if (message.type === "result" && message.subtype === "success") {
377 console.log(message.result);
384378 }
385 } catch (error) {
386 // A single-shot query() throws after yielding an error result.
387 console.error(`Session ended with an error: ${error}`);
388379 }
389 ```
390</CodeGroup>
380} catch (error) {
381 // A single-shot query() throws after yielding an error result.
382 console.error(`Session ended with an error: ${error}`);
383}
384```
391385 
392386You should see that `forkedId` differs from the original session ID. Resuming the original session still continues the JWT thread, which confirms the fork did not modify the original history.
393387 
Feedback