Persist sessions to external storage changedagent-sdk/session-storage
Upstream edited this page at 10 Oct 2026 17:34 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+130added
Lines−134removed
From line
14
where the diff opens
First seen
14 Aug 2026
this site's first read of the page
Recorded edits10to this page, all time
The whole hunk
from line 14, old and new numbered
/
from line 14
1414
1515A `SessionStore` is an object with two required methods, `append` and `load`, and four optional methods. The SDK calls `append` to write transcript entries during a query and `load` to read them back for resume.
1616
17<CodeGroup>
18 ```typescript TypeScript theme={null}
19 // Exported from @anthropic-ai/claude-agent-sdk as
20 // SessionStore, SessionKey, SessionStoreEntry, SessionSummaryEntry.
17```typescript TypeScript theme={null}
18// Exported from @anthropic-ai/claude-agent-sdk as
19// SessionStore, SessionKey, SessionStoreEntry, SessionSummaryEntry.
2120
22 type SessionKey = {
23 projectKey: string;
24 sessionId: string;
25 subpath?: string;
26 };
21type SessionKey = {
22 projectKey: string;
23 sessionId: string;
24 subpath?: string;
25};
2726
28 type SessionStore = {
29 // Required
30 append(key: SessionKey, entries: SessionStoreEntry[]): Promise<void>;
31 load(key: SessionKey): Promise<SessionStoreEntry[] | null>;
27type SessionStore = {
28 // Required
29 append(key: SessionKey, entries: SessionStoreEntry[]): Promise<void>;
30 load(key: SessionKey): Promise<SessionStoreEntry[] | null>;
3231
33 // Optional
34 listSessions?(
35 projectKey: string,
36 ): Promise<Array<{ sessionId: string; mtime: number }>>;
37 listSessionSummaries?(projectKey: string): Promise<SessionSummaryEntry[]>;
38 delete?(key: SessionKey): Promise<void>;
39 listSubkeys?(key: {
40 projectKey: string;
41 sessionId: string;
42 }): Promise<string[]>;
43 };
44
45 type SessionSummaryEntry = {
32 // Optional
33 listSessions?(
34 projectKey: string,
35 ): Promise<Array<{ sessionId: string; mtime: number }>>;
36 listSessionSummaries?(projectKey: string): Promise<SessionSummaryEntry[]>;
37 delete?(key: SessionKey): Promise<void>;
38 listSubkeys?(key: {
39 projectKey: string;
4640 sessionId: string;
47 mtime: number;
48 data: Record<string, unknown>;
49 };
50 ```
41 }): Promise<string[]>;
42};
5143
52 ```python Python theme={null}
53 # Exported from claude_agent_sdk as
54 # SessionStore, SessionKey, SessionStoreEntry, SessionSummaryEntry.
44type SessionSummaryEntry = {
45 sessionId: string;
46 mtime: number;
47 data: Record<string, unknown>;
48};
49```
5550
56 class SessionKey(TypedDict):
57 project_key: str
58 session_id: str
59 subpath: NotRequired[str]
51```python Python theme={null}
52# Exported from claude_agent_sdk as
53# SessionStore, SessionKey, SessionStoreEntry, SessionSummaryEntry.
6054
61 class SessionStore(Protocol):
62 # Required
63 async def append(
64 self, key: SessionKey, entries: list[SessionStoreEntry]
65 ) -> None: ...
66 async def load(self, key: SessionKey) -> list[SessionStoreEntry] | None: ...
55class SessionKey(TypedDict):
56 project_key: str
57 session_id: str
58 subpath: NotRequired[str]
6759
68 # Optional — omit or raise NotImplementedError
69 async def list_sessions(
70 self, project_key: str
71 ) -> list[SessionStoreListEntry]: ...
72 async def list_session_summaries(
73 self, project_key: str
74 ) -> list[SessionSummaryEntry]: ...
75 async def delete(self, key: SessionKey) -> None: ...
76 async def list_subkeys(self, key: SessionListSubkeysKey) -> list[str]: ...
60class SessionStore(Protocol):
61 # Required
62 async def append(
63 self, key: SessionKey, entries: list[SessionStoreEntry]
64 ) -> None: ...
65 async def load(self, key: SessionKey) -> list[SessionStoreEntry] | None: ...
7766
78 class SessionSummaryEntry(TypedDict):
79 session_id: str
80 mtime: int
81 data: dict[str, Any]
82 ```
83</CodeGroup>
67 # Optional — omit or raise NotImplementedError
68 async def list_sessions(
69 self, project_key: str
70 ) -> list[SessionStoreListEntry]: ...
71 async def list_session_summaries(
72 self, project_key: str
73 ) -> list[SessionSummaryEntry]: ...
74 async def delete(self, key: SessionKey) -> None: ...
75 async def list_subkeys(self, key: SessionListSubkeysKey) -> list[str]: ...
8476
77class SessionSummaryEntry(TypedDict):
78 session_id: str
79 mtime: int
80 data: dict[str, Any]
81```
82
8583`SessionKey` addresses one transcript. `projectKey` is a stable, filesystem-safe encoding of the working directory, `sessionId` is the session UUID, and `subpath` is set when the entry belongs to a subagent transcript or sidecar file rather than the main conversation.
8684
8785Because `projectKey` encodes the working directory, resume or continue from the store from a working directory matching the original run's. In TypeScript, if you set [`CLAUDE_CODE_PROJECT_DIR_NAME`](/docs/en/sessions#name-the-project-directory-yourself) beside `CLAUDE_CONFIG_DIR` in a query's [`env` option](/docs/en/agent-sdk/typescript#options), the SDK keys that query's entries, and its `resume` and `continue` lookups, by that name instead. Because standalone helpers such as `listSessions` and `deleteSession` take no `env` and read the process environment, set `CLAUDE_CONFIG_DIR` and the same name in the host process environment too. Requires Agent SDK v0.3.234 or later.
from line 105
107105
108106The SDK ships an `InMemorySessionStore` for development and testing. The example below runs a query with the store attached, captures the session ID from the result message, then resumes from the store in a second `query()` call. The second call passes the same store instance plus `resume`, so the SDK loads the transcript from the store instead of the local filesystem:
109107
110<CodeGroup>
111 ```typescript TypeScript theme={null}
112 import { query, InMemorySessionStore } from "@anthropic-ai/claude-agent-sdk";
108```typescript TypeScript theme={null}
109import { query, InMemorySessionStore } from "@anthropic-ai/claude-agent-sdk";
113110
114 const store = new InMemorySessionStore();
111const store = new InMemorySessionStore();
115112
116 let sessionId: string | undefined;
117 try {
118 for await (const message of query({
119 prompt: "List the TypeScript files under src/",
120 options: { sessionStore: store },
121 })) {
122 if (message.type === "result") {
123 sessionId = message.session_id;
124 }
125 }
126 } catch (error) {
127 // A single-shot query() throws after yielding an error result. If the
128 // failure was an error result, sessionId was already captured by the loop
129 // above; connection or process failures yield no result message.
130 console.error(`Session ended with an error: ${error}`);
131 }
132
133 // Resume from the store. The agent has full context from the first call.
113let sessionId: string | undefined;
114try {
134115 for await (const message of query({
135 prompt: "Summarize what those files do",
136 options: { sessionStore: store, resume: sessionId },
116 prompt: "List the TypeScript files under src/",
117 options: { sessionStore: store },
137118 })) {
138 if (message.type === "result" && message.subtype === "success") {
139 console.log(message.result);
119 if (message.type === "result") {
120 sessionId = message.session_id;
140121 }
141122 }
142 ```
123} catch (error) {
124 // A single-shot query() throws after yielding an error result. If the
125 // failure was an error result, sessionId was already captured by the loop
126 // above; connection or process failures yield no result message.
127 console.error(`Session ended with an error: ${error}`);
128}
143129
144 ```python Python theme={null}
145 import asyncio
146 from claude_agent_sdk import (
147 ClaudeAgentOptions,
148 InMemorySessionStore,
149 ResultMessage,
150 query,
151 )
130// Resume from the store. The agent has full context from the first call.
131for await (const message of query({
132 prompt: "Summarize what those files do",
133 options: { sessionStore: store, resume: sessionId },
134})) {
135 if (message.type === "result" && message.subtype === "success") {
136 console.log(message.result);
137 }
138}
139```
152140
153 store = InMemorySessionStore()
141```python Python theme={null}
142import asyncio
143from claude_agent_sdk import (
144 ClaudeAgentOptions,
145 InMemorySessionStore,
146 ResultMessage,
147 query,
148)
154149
150store = InMemorySessionStore()
155151
156 async def main():
157 session_id = None
158 try:
159 async for message in query(
160 prompt="List the Python files under src/",
161 options=ClaudeAgentOptions(session_store=store),
162 ):
163 if isinstance(message, ResultMessage):
164 session_id = message.session_id
165 except Exception as error:
166 # A single-shot query() raises after yielding an error result. If the
167 # failure was an error result, session_id was already captured by the
168 # loop above; connection or process failures yield no result message.
169 print(f"Session ended with an error: {error}")
170152
171 # Resume from the store. The agent has full context from the first call.
172 async for message in query(
173 prompt="Summarize what those files do",
174 options=ClaudeAgentOptions(session_store=store, resume=session_id),
175 ):
176 if isinstance(message, ResultMessage) and message.subtype == "success":
177 print(message.result)
153async def main():
154 session_id = None
155 try:
156 async for message in query(
157 prompt="List the Python files under src/",
158 options=ClaudeAgentOptions(session_store=store),
159 ):
160 if isinstance(message, ResultMessage):
161 session_id = message.session_id
162 except Exception as error:
163 # A single-shot query() raises after yielding an error result. If the
164 # failure was an error result, session_id was already captured by the
165 # loop above; connection or process failures yield no result message.
166 print(f"Session ended with an error: {error}")
178167
168 # Resume from the store. The agent has full context from the first call.
169 async for message in query(
170 prompt="Summarize what those files do",
171 options=ClaudeAgentOptions(session_store=store, resume=session_id),
172 ):
173 if isinstance(message, ResultMessage) and message.subtype == "success":
174 print(message.result)
179175
180 asyncio.run(main())
181 ```
182</CodeGroup>
176
177asyncio.run(main())
178```
183179
184180The second query prints a summary of the files from the first query, which shows the agent resumed with full context from the store.
185181
No line in this hunk matches that.