Long-Running Claude Agent SDK: Resume, Idempotency, and Monitoring¶
For / Key Points
For: Developers operating research, migration, or code-change tasks that run for tens of minutes or longer
Key Points:
session_idandresumecontinue a conversation but do not replace durable business state- Split long work into restartable phases instead of one uninterrupted loop
- The application must design budgets, permissions, hooks, monitoring, and shutdown
Long-running agent reliability is not the number of uninterrupted hours a model can work. It is the ability to recover from process termination, API errors, approval waits, duplicate execution, and partial artifacts.
The current Claude Agent SDK supports session resume, hooks, usage and cost events, and OpenTelemetry integration.12 Claims about a universal “30-hour autonomous run” or automatic rollback to arbitrary checkpoints should not be treated as general SDK guarantees.
Separate three state layers¶
| State | Storage | Examples |
|---|---|---|
| Conversation | Agent SDK session transcript | Messages, tool results, session_id |
| Business workflow | Your database or durable store | Phase, input version, processed IDs, approvals, retries |
| Artifacts | Version control or object storage | Changed files, reports, validation logs, checksums |
SDK resume primarily restores conversation state. Whether a customer notification was sent or which migration records were committed belongs in application state, not model memory.
Save and resume session_id¶
Persist the session_id from the final ResultMessage and pass it to ClaudeAgentOptions.resume in a later process.2
from dataclasses import replace
import anyio
from claude_agent_sdk import (
ClaudeAgentOptions,
ClaudeSDKClient,
ResultMessage,
)
async def run_first_turn() -> str:
options = ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Glob"],
max_turns=8,
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Investigate the repository and draft a migration plan only.")
async for message in client.receive_response():
if isinstance(message, ResultMessage):
return message.session_id
raise RuntimeError("ResultMessage was not received")
async def resume_turn(session_id: str) -> None:
base = ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Glob"],
max_turns=8,
)
options = replace(base, resume=session_id)
async with ClaudeSDKClient(options=options) as client:
await client.query("Split the previous plan into verifiable phases.")
async for message in client.receive_response():
if isinstance(message, ResultMessage):
print(message.result)
anyio.run(resume_turn, "stored-session-id")
The transcript is stored on local disk and can survive a process restart. It is not a server-hosted conversation ID; another host needs access to the same session files.2
Repeated queries on one open ClaudeSDKClient fit in-process interaction. Crash recovery requires persisting the ID and using resume.
Split work into idempotent phases¶
Avoid one instruction that researches, changes, tests, and publishes.
discovered
→ planned
→ approved
→ implemented
→ validated
→ published
Each phase needs:
- Inputs and their versions
- Allowed tools and write destinations
- Expected artifacts
- Success validation
- Rerun behavior
- Approval required for the next phase
Record the target commit, patch ID, and artifact checksum so rerunning implemented does not append the same change twice. Use a business job ID as an idempotency key for external create operations.
Application checkpoints¶
A checkpoint is durable application data required for recovery, not a snapshot of model thought.
{
"job_id": "migration-2026-071",
"session_id": "...",
"phase": "implemented",
"input_revision": "a4c91d2",
"artifact_uri": "s3://agent-runs/migration-2026-071.patch",
"artifact_sha256": "...",
"validation": "pending",
"attempt": 2
}
To recover from a stop between state and side effect:
- Record the planned operation as
running - Execute the side effect with an idempotency key
- Store its result and external ID
- Mark the phase complete only after validation
Permissions and stop conditions¶
Long runs amplify one bad decision.
- Change
allowed_toolsper phase - Keep investigation to Read, Grep, and Glob
- Gate Edit and Bash with
can_use_toolor a PreToolUse hook - Return deletion, publishing, sending, and purchasing to human approval
- Limit turns, wall time, cost, changed files, and external API operations
Treat reaching a limit as a normal stop. Persist current state and the next resume instruction.
Enforce invariants with hooks¶
PreToolUse hooks can block an operation before execution; PostToolUse hooks can record and validate outcomes.3
Examples:
- Reject writes outside an approved directory
- Reject direct pushes to a production branch
- Block unsafe database configuration values
- Run syntax checks after a file update
- Check a destination allowlist before external sending
Enforce invariants with deterministic code, not only “never do this” prompt text.
Monitor the run¶
ResultMessage provides result, usage, cost, and session ID. The shared Claude Code runtime can export OpenTelemetry metrics and events.2
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317
Associate at least these values with a job ID:
- Duration and current phase
- Turns, tokens, and cost
- Tool calls, failures, and approval wait time
- Changed file count and validation status
- Retry count and last error
session_idand artifact URI
Alert on lack of progress, repeated tool failures, and excessive cost trends—not only process death.
Do not confuse Agent SDK with Managed Agents¶
Claude Agent SDK runs in a process and filesystem you operate. Claude Managed Agents is a separate service that persists sessions and sandboxes on Anthropic infrastructure.4
Compare Managed Agents when you need cross-host durable sessions, server-managed schedules, and a managed sandbox. Do not apply Managed Agents retention guarantees to local Agent SDK transcripts.
Summary¶
- Store conversation, business workflow, and artifacts separately
- Use
session_idandresumefor conversation continuity and a database for committed workflow state - Split work into verifiable, idempotent phases
- Bound side effects with hooks and limits
- Monitor cost, tools, progress, and artifacts by job
The production goal is not an agent that never stops. It is one that can stop safely, explain committed progress, and resume without duplicating side effects.