Simpan sesi ke penyimpanan eksternal
Cerminkan transkrip sesi ke S3, Redis, atau backend Anda sendiri sehingga host apa pun dapat melanjutkannya.
Secara default, SDK menulis transkrip sesi ke file JSONL di bawah ~/.claude/projects/ pada sistem file lokal. Adaptor SessionStore memungkinkan Anda mencerminkan transkrip tersebut ke backend Anda sendiri, seperti S3, Redis, atau database, sehingga sesi yang dibuat di satu host dapat dilanjutkan di host lain yang menjalankan dari direktori kerja yang cocok.
Alasan umum untuk menggunakan session store:
- Penerapan multi-host. Fungsi serverless, pekerja yang diskalakan otomatis, dan runner CI tidak berbagi sistem file. Penyimpanan bersama memungkinkan replika melanjutkan sesi satu sama lain.
- Daya tahan. Kontainer lokal bersifat sementara. Penyimpanan yang didukung oleh S3 atau database bertahan melalui restart dan redeploy.
- Kepatuhan dan audit. Simpan transkrip dalam penyimpanan yang sudah Anda kelola, dengan aturan retensi, enkripsi, dan kontrol akses Anda sendiri.
Antarmuka `SessionStore`
SessionStore adalah objek dengan dua metode yang diperlukan, append dan load, serta empat metode opsional. SDK memanggil append untuk menulis entri transkrip selama kueri dan load untuk membacanya kembali untuk resume.
// Exported from @anthropic-ai/claude-agent-sdk as
// SessionStore, SessionKey, SessionStoreEntry, SessionSummaryEntry.
type SessionKey = {
projectKey: string;
sessionId: string;
subpath?: string;
};
type SessionStore = {
// Required
append(key: SessionKey, entries: SessionStoreEntry[]): Promise<void>;
load(key: SessionKey): Promise<SessionStoreEntry[] | null>;
// Optional
listSessions?(
projectKey: string,
): Promise<Array<{ sessionId: string; mtime: number }>>;
listSessionSummaries?(projectKey: string): Promise<SessionSummaryEntry[]>;
delete?(key: SessionKey): Promise<void>;
listSubkeys?(key: {
projectKey: string;
sessionId: string;
}): Promise<string[]>;
};
type SessionSummaryEntry = {
sessionId: string;
mtime: number;
data: Record<string, unknown>;
};
# Exported from claude_agent_sdk as
# SessionStore, SessionKey, SessionStoreEntry, SessionSummaryEntry.
class SessionKey(TypedDict):
project_key: str
session_id: str
subpath: NotRequired[str]
class SessionStore(Protocol):
# Required
async def append(
self, key: SessionKey, entries: list[SessionStoreEntry]
) -> None: ...
async def load(self, key: SessionKey) -> list[SessionStoreEntry] | None: ...
# Optional — omit or raise NotImplementedError
async def list_sessions(
self, project_key: str
) -> list[SessionStoreListEntry]: ...
async def list_session_summaries(
self, project_key: str
) -> list[SessionSummaryEntry]: ...
async def delete(self, key: SessionKey) -> None: ...
async def list_subkeys(self, key: SessionListSubkeysKey) -> list[str]: ...
class SessionSummaryEntry(TypedDict):
session_id: str
mtime: int
data: dict[str, Any]
SessionKey mengatasi satu transkrip. projectKey adalah pengkodean stabil dan aman sistem file dari direktori kerja, sessionId adalah UUID sesi, dan subpath diatur ketika entri milik transkrip subagent atau file sidecar daripada percakapan utama.
Karena projectKey mengkodekan direktori kerja, resume atau lanjutkan dari toko dari direktori kerja yang cocok dengan run asli. Di TypeScript, jika Anda menetapkan CLAUDE_CODE_PROJECT_DIR_NAME di samping CLAUDE_CONFIG_DIR dalam opsi env kueri, SDK menentukan kunci entri kueri itu, dan pencarian resume dan continue miliknya, dengan nama itu sebagai gantinya. Karena pembantu mandiri seperti listSessions dan deleteSession tidak mengambil env dan membaca lingkungan proses, atur CLAUDE_CONFIG_DIR dan nama yang sama di lingkungan proses host juga. Memerlukan Agent SDK v0.3.234 atau lebih baru.
Perlakukan subpath sebagai sufiks kunci yang tidak transparan; ini mengikuti tata letak on-disk, misalnya subagents/agent-<id>. Ketika subpath tidak ditentukan, kunci merujuk ke transkrip utama.
| Metode | Diperlukan | Dipanggil ketika |
|---|---|---|
append |
Ya | Setelah setiap batch entri transkrip ditulis secara lokal. Entri adalah objek yang aman JSON, satu per baris dalam JSONL lokal. |
load |
Ya | Sebelum subprocess spawn ketika resume diatur atau continue: true menyelesaikan sesi toko terbaru, dan sekali per sesi ketika listing kembali dari listSessionSummaries. Kembalikan null jika sesi tidak dikenal. |
listSessions |
Tidak | Oleh listSessions({ sessionStore }) dan oleh query()/startup() dengan continue: true. Jika tidak ditentukan, continue: true melempar, dan listSessions({ sessionStore }) melempar kecuali listSessionSummaries diimplementasikan. |
listSessionSummaries |
Tidak | Oleh listSessions({ sessionStore }) untuk membaca metadata untuk semua sesi dalam satu panggilan. Pertahankan ringkasan di dalam append. Jika tidak ditentukan, listing kembali ke listSessions ditambah per-sesi load. |
delete |
Tidak | Oleh deleteSession({ sessionStore }). Menghapus kunci utama (tanpa subpath) harus cascade ke semua subkey untuk sesi itu dan juga menghapus entri ringkasan sesi, sehingga sesi yang dihapus berhenti muncul di listSessionSummaries. Jika tidak ditentukan, penghapusan adalah no-op, yang cocok untuk backend append-only. |
listSubkeys |
Tidak | Selama resume, untuk menemukan transkrip subagent. Jika tidak ditentukan, hanya transkrip utama yang dipulihkan. |
Dalam SessionSummaryEntry, mtime adalah waktu penulisan penyimpanan sidecar dan harus berbagi sumber jam dengan nilai mtime yang dikembalikan listSessions. data adalah status SDK-owned yang tidak transparan; pertahankan secara verbatim tanpa menginterpretasinya.
Bangun entri dengan memanggil pembantu foldSessionSummary yang diekspor, fold_session_summary di Python, pada setiap batch di dalam append. Lewati batch yang kuncinya memiliki subpath; transkrip subagent tidak boleh berkontribusi pada ringkasan sesi utama. Fold tidak pernah menetapkan mtime: cap pada waktu persist, melalui argumen options.mtime di TypeScript atau dengan menimpa field pada entri yang dikembalikan di Python. Panggilan append bersamaan untuk sesi yang sama dapat race pada sidecar, jadi serialisasi read-fold-write dengan transaksi, compare-and-swap, atau per-session lock; fold itu sendiri adalah pure.
Untuk apa yang dilakukan SDK dengan transkrip load yang dikembalikan, lihat Resume dari toko.
Mulai cepat
SDK mengirimkan InMemorySessionStore untuk pengembangan dan pengujian. Contoh di bawah menjalankan kueri dengan penyimpanan yang terpasang, menangkap ID sesi dari pesan hasil, kemudian melanjutkan dari penyimpanan dalam panggilan query() kedua. Panggilan kedua melewatkan instance penyimpanan yang sama ditambah resume, sehingga SDK memuat transkrip dari penyimpanan daripada sistem file lokal:
import { query, InMemorySessionStore } from "@anthropic-ai/claude-agent-sdk";
const store = new InMemorySessionStore();
let sessionId: string | undefined;
try {
for await (const message of query({
prompt: "List the TypeScript files under src/",
options: { sessionStore: store },
})) {
if (message.type === "result") {
sessionId = message.session_id;
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result. If the
// failure was an error result, sessionId was already captured by the loop
// above; connection or process failures yield no result message.
console.error(`Session ended with an error: ${error}`);
}
// Resume from the store. The agent has full context from the first call.
for await (const message of query({
prompt: "Summarize what those files do",
options: { sessionStore: store, resume: sessionId },
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
import asyncio
from claude_agent_sdk import (
ClaudeAgentOptions,
InMemorySessionStore,
ResultMessage,
query,
)
store = InMemorySessionStore()
async def main():
session_id = None
try:
async for message in query(
prompt="List the Python files under src/",
options=ClaudeAgentOptions(session_store=store),
):
if isinstance(message, ResultMessage):
session_id = message.session_id
except Exception as error:
# A single-shot query() raises after yielding an error result. If the
# failure was an error result, session_id was already captured by the
# loop above; connection or process failures yield no result message.
print(f"Session ended with an error: {error}")
# Resume from the store. The agent has full context from the first call.
async for message in query(
prompt="Summarize what those files do",
options=ClaudeAgentOptions(session_store=store, resume=session_id),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
asyncio.run(main())
Kueri kedua mencetak ringkasan file dari kueri pertama, yang menunjukkan bahwa agen melanjutkan dengan konteks penuh dari penyimpanan.
Tulis adaptor Anda sendiri
Implementasikan append dan load terhadap backend Anda. Tambahkan listSessions, listSessionSummaries, delete, dan listSubkeys jika Anda ingin listSessions(), pembacaan metadata satu panggilan, deleteSession(), dan subagent resume bekerja terhadap penyimpanan.
Entri yang dilewatkan ke append diketik sebagai SessionStoreEntry (objek { type: string; ... }). Perlakukan mereka sebagai nilai yang aman JSON yang tidak transparan: simpan dalam urutan dan kembalikan dari load dalam urutan yang sama. load harus mengembalikan entri yang deep-equal dengan apa yang ditambahkan; serialisasi byte-equal tidak diperlukan, jadi backend seperti Postgres jsonb yang mengurutkan ulang kunci objek tidak masalah.
Implementasi referensi
Repositori TypeScript SDK mencakup adaptor referensi yang dapat dijalankan untuk S3, Redis, dan Postgres di bawah examples/session-stores/. Mereka tidak dipublikasikan ke npm; salin file src/ yang Anda butuhkan ke proyek Anda dan instal klien backend yang sesuai.
| Adaptor | Klien backend | Model penyimpanan |
|---|---|---|
S3SessionStore |
@aws-sdk/client-s3 |
Satu file bagian JSONL per append(); load() mencantumkan, mengurutkan, dan menggabungkan. |
RedisSessionStore |
ioredis |
RPUSH/LRANGE list per transkrip, ditambah indeks sorted-set sesi. |
PostgresSessionStore |
pg |
Satu baris per entri dalam tabel jsonb, diurutkan oleh BIGSERIAL. |
Setiap adaptor mengambil instance klien yang telah dikonfigurasi sebelumnya, sehingga Anda mengontrol kredensial, TLS, region, dan pooling. Misalnya, dengan S3:
import { query } from "@anthropic-ai/claude-agent-sdk";
import { S3Client } from "@aws-sdk/client-s3";
import { S3SessionStore } from "./S3SessionStore"; // copied from examples/session-stores/s3
const store = new S3SessionStore({
bucket: "my-claude-sessions",
prefix: "transcripts",
client: new S3Client({ region: "us-east-1" }),
});
for await (const message of query({
prompt: "Hello!",
options: { sessionStore: store },
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
// Later, possibly on a different host:
for await (const message of query({
prompt: "Continue where we left off",
options: { sessionStore: store, resume: "previous-session-id" },
})) {
// ...
}
Validasi adaptor Anda
Kedua SDK mengirimkan suite conformance yang menegaskan kontrak perilaku append, load, dan metode opsional harus memuaskan. Tes untuk metode opsional melewati secara otomatis ketika metode tersebut tidak diimplementasikan.
Di TypeScript, salin shared/conformance.ts dari direktori contoh ke dalam suite pengujian Anda. Di Python, suite dikirimkan dalam paket. Untuk menjalankannya dengan pytest, yang bukan merupakan dependensi SDK, instal pytest terlebih dahulu:
pip install pytest
Kemudian teruskan adaptor Anda ke suite dalam file pengujian sebagai factory tanpa argumen, yang run_session_store_conformance panggil sekali per kontrak untuk membangun toko yang segar:
import pytest
from claude_agent_sdk.testing import run_session_store_conformance
@pytest.mark.anyio
async def test_my_store_conformance():
await run_session_store_conformance(MyRedisStore)
Melewatkan kelas MyRedisStore itu sendiri, seperti yang dilakukan contoh ini, berfungsi ketika konstruktor tidak mengambil argumen. Untuk adaptor yang mengambil klien yang telah dikonfigurasi sebelumnya, teruskan lambda yang membangun toko sebagai gantinya. Karena kontrak menggunakan kembali kunci sesi yang sama, setiap toko yang dikembalikan factory harus dimulai dengan penyimpanan kosong, jadi buat lambda menyediakan penyimpanan backing terisolasi per panggilan, seperti fake in-memory yang segar, prefix kunci unik, atau database pengujian baru.
Catatan perilaku
Arsitektur dual-write
Subprocess Claude Code selalu menulis setiap batch entri transkrip ke disk lokal terlebih dahulu, dan SDK kemudian meneruskan batch yang sama ke append() penyimpanan Anda, sehingga penyimpanan adalah cerminan dari transkrip lokal daripada pengganti untuknya. Salinan mana yang bertahan dari run tergantung pada bagaimana run dimulai:
- Sesi segar, atau resume ketika penyimpanan tidak memiliki apa pun untuk sesi: transkrip lokal di bawah direktori konfigurasi Anda bertahan dari run, dan penyimpanan menerima salinan.
- Run dilanjutkan dari penyimpanan: salinan lokal dihapus di akhir run, sehingga penyimpanan menyimpan satu-satunya salinan yang tahan lama.
Jika Anda tidak ingin sesi segar meninggalkan transkrip di disk lokal, atur CLAUDE_CONFIG_DIR ke direktori temp di options.env. Run yang dilanjutkan dari penyimpanan sudah menghapus salinan lokalnya, jadi tidak memerlukan pengaturan seperti itu. Di TypeScript, sebarkan process.env ke env juga, karena opsi env menggantikan lingkungan subprocess.
Jika aplikasi Anda masuk melalui file di direktori konfigurasi, seperti kredensial OAuth atau apiKeyHelper di settings.json pengguna Anda, salin file-file tersebut ke direktori temp terlebih dahulu, atau atur ANTHROPIC_API_KEY di env sebagai gantinya. Jika tidak, run gagal dengan Not logged in.
Dua opsi bertentangan dengan cerminan, dan SDK melempar pada startup jika Anda menggabungkan salah satu dengan penyimpanan:
persistSession: falsedi TypeScript: mematikan penulisan lokal yang dibangun cerminan. Python SDK tidak memiliki opsi yang setara.- File checkpointing,
enableFileCheckpointingdi TypeScript atauenable_file_checkpointingdi Python: menulis cadangan file langsung ke disk lokal, dan SDK tidak mencerminkannya ke penyimpanan.
Resume dari penyimpanan
Ketika Anda melewatkan resume, atau continue: true di TypeScript atau continue_conversation=True di Python, bersama dengan penyimpanan, SDK meminta transkrip dari penyimpanan sebelum ia menelurkan subprocess:
resume: SDK meminta sesi yang ID-nya Anda lewatkan.continue: trueataucontinue_conversation=True: SDK meminta sesi terbaru penyimpanan.
Ketika penyimpanan mengembalikan transkrip, SDK menulisnya ke direktori konfigurasi sementara, menjalankan subprocess dengan CLAUDE_CONFIG_DIR menunjuk ke sana, dan menghapus direktori ketika run berakhir. Transkrip lokal yang run itu tulis dihapus bersama dengannya, itulah mengapa penyimpanan menyimpan satu-satunya salinan yang tahan lama di jalur ini.
SDK juga menyemai direktori sementara dengan file dari direktori konfigurasi nyata Anda. Apa yang disalinnya berbeda menurut bahasa:
- TypeScript: kredensial,
.claude.json, dansettings.jsonpengguna Anda. Darisettings.jsonia menghilangkan kunci yang berperilaku buruk di bawah direktori konfigurasi sementara:enabledPlugins,extraKnownMarketplaces, aliasadditionalMarketplaces-nya, danCLAUDE_CONFIG_DIRapa pun di blokenvfile. Sebelum Agent SDK v0.3.232, SDK tidak menghilangkan alias. Auth yang dikonfigurasi dalam pengaturan, sepertiapiKeyHelper, bekerja ketika Anda resume dari penyimpanan. Sebelum Agent SDK v0.3.222, TypeScript SDK hanya menyalin kredensial dan.claude.json. - Python: kredensial dan
.claude.jsonsaja, jadi aplikasi yang mengautentikasi melaluiapiKeyHelperdisettings.jsonpengguna Anda gagal denganNot logged inketika resume dari penyimpanan.apiKeyHelperdalam pengaturan terkelola atau proyek masih bekerja, karena Claude Code membaca file-file tersebut dari lokasi yang tidak dipengaruhi olehCLAUDE_CONFIG_DIR.
Ketika penyimpanan tidak memiliki apa pun untuk sesi, SDK berjalan di bawah direktori konfigurasi nyata Anda sebagai gantinya, dan hasilnya tergantung pada opsi mana yang Anda lewatkan:
resume: kedua SDK melewatkan ID melalui ke subprocess, yang melanjutkan transkrip lokal persis sepertiresumetanpa penyimpanan.continue: truedi TypeScript: SDK memulai sesi segar.continue_conversation=Truedi Python: SDK melanjutkan dari sesi lokal terbaru.
Penulisan cerminan adalah best-effort
Jika append() menolak, SDK mencoba ulang batch hingga dua kali lagi dengan backoff singkat, untuk maksimal tiga percobaan total. Panggilan yang timeout tidak dicoba ulang, karena panggilan asli mungkin masih mendarat. Jika batch masih gagal, SDK mencatat kesalahan, memancarkan pesan { type: "system", subtype: "mirror_error" } ke iterator, menjatuhkan batch, dan melanjutkan kueri. Karena batch yang dicoba ulang dapat mengirimkan ulang entri yang sudah mendarat, deduplikasi berdasarkan entry.uuid dalam implementasi append() Anda.
Pemadaman penyimpanan tidak mengganggu agen, karena subprocess menulis lokal terlebih dahulu. Pantau mirror_error jika Anda perlu mendeteksi kehilangan data penyimpanan. Pada run dilanjutkan dari penyimpanan, batch yang dijatuhkan tidak memiliki salinan yang bertahan setelah run berakhir.
`getSessionMessages` mengembalikan rantai post-compaction
getSessionMessages({ sessionStore }) mengembalikan rantai pesan tertaut yang akan dilihat agen pada resume. Setelah auto-compaction, giliran sebelumnya diganti dengan ringkasan, jadi sesi yang penyimpanannya menyimpan 503 entri mentah dapat mengembalikan 18 pesan dari getSessionMessages. Untuk riwayat mentah lengkap, termasuk giliran pre-compaction dan entri metadata, panggil store.load(key) secara langsung.
`forkSession` bukan salinan byte
forkSession({ sessionStore }) membaca entri sumber, menulis ulang setiap bidang sessionId dan memetakan ulang UUID pesan, kemudian menambahkan entri yang ditransformasi di bawah kunci baru. Salinan tingkat adaptor atau shortcut CopyObject akan menghasilkan transkrip yang masih mereferensikan ID sesi lama, jadi SDK tidak menggunakannya.
Transkrip subagent
Transkrip subagent dicerminkan di bawah subpath: "subagents/agent-<id>". listSubagents({ sessionStore }) memerlukan adaptor untuk mengimplementasikan listSubkeys; getSubagentMessages({ sessionStore }) menggunakannya ketika tersedia tetapi kembali ke subpath langsung ketika tidak ditentukan. Resume juga memanggil listSubkeys untuk memulihkan file subagent; tanpanya, hanya transkrip utama yang dimaterialisasi.
Retensi
SDK tidak pernah menghapus dari penyimpanan Anda sendiri. Retensi adalah tanggung jawab adaptor: implementasikan TTL, kebijakan lifecycle S3, atau pembersihan terjadwal sesuai dengan persyaratan kepatuhan Anda.
Transkrip lokal di bawah CLAUDE_CONFIG_DIR disapu secara independen oleh pengaturan cleanupPeriodDays, mengikuti aturan penyapuan retensi. Run dilanjutkan dari penyimpanan tidak meninggalkan transkrip lokal, jadi untuk run tersebut retensi penyimpanan Anda adalah satu-satunya retensi yang ada.
Didukung pada
Fungsi SDK TypeScript berikut menerima opsi sessionStore dan beroperasi terhadap penyimpanan daripada sistem file lokal ketika disediakan:
query()startup()listSessions()getSessionInfo()getSessionMessages()renameSession()tagSession()deleteSession()forkSession()listSubagents()getSubagentMessages()
Dalam SDK Python, atur session_store dalam ClaudeAgentOptions untuk menjalankan query() terhadap penyimpanan. Operasi yang tersisa masing-masing memiliki fungsi Python yang didukung penyimpanan yang mengambil penyimpanan sebagai argumen: list_sessions_from_store(), get_session_info_from_store(), get_session_messages_from_store(), list_subagents_from_store(), get_subagent_messages_from_store(), rename_session_via_store(), tag_session_via_store(), delete_session_via_store(), dan fork_session_via_store(). startup() tidak memiliki padanan Python. Fungsi mandiri yang didokumentasikan dalam referensi SDK Python, seperti list_sessions(), membaca file sesi lokal.
Sumber daya terkait
- Bekerja dengan sesi: Lanjutkan, resume, dan fork tanpa penyimpanan kustom
- Host SDK: Pola penerapan untuk lingkungan multi-host
- TypeScript
Options: Referensi opsi lengkap examples/session-stores/: Adaptor referensi S3, Redis, dan Postgres yang dapat dijalankan