Skip to content

temporal-claude-agent: durable Claude Agent SDK agents on Temporal #135

Open

Description

@osamastro7-droid

Project link

https://github.com/osamastro7-droid/temporal-claude-agent

Language

Python

Short description (max 256 chars)

A Python package that runs Claude Agent SDK agents on Temporal. Every tool call becomes its own Activity: a crash resumes from the last finished step, a refund can't happen twice, and risky tools wait for a human approval (a validated Update).

Long Description

temporal-claude-agent runs agents built with Anthropic's Claude Agent SDK inside a Temporal Workflow, so they survive crashes and never repeat a finished tool call.

The problem. The Claude Agent SDK runs its own agent loop inside the Claude Code engine. If the machine dies mid-task, the agent loses its place, and a retry can repeat risky actions, like paying a refund twice.

How it works

  1. Durable tools are declared to Claude. A PreToolUse hook answers "defer", so Claude stops at every tool call. The engine never runs the tool itself.
  2. The Workflow runs each tool as its own Temporal Activity with a fixed ID (tool-<tool_use_id>), after a human approval if the tool needs one.
  3. The next Claude step resumes the session from a session store and receives the saved result as a normal tool_result.

What you get

  • Finished steps never run again, on any worker. A crash resumes from the last finished step.
  • Every tool call gets a stable receipt number (its Activity ID) to pass on as an idempotency key. In the demo, a crash after the money moved gives 2 attempts and 1 refund.
  • Risky tools wait for a human, for minutes or days, through a Temporal Update with a validator and an allowed-approvers list. Refused approvals never reach the history.
  • Progress and cost through Queries, and clear Activity names in the Temporal UI.

Temporal features used: Activities with fixed IDs and retries, heartbeats on Claude steps, Updates with validators, Queries, wait_condition, and a worker plugin (ClaudeAgentPlugin).

Tested

  • 12 automated tests, including worker crashes (SIGKILL) and the real Claude Code engine running on Temporal with a stand-in model.
  • A real-Claude matrix (8 scenarios, 2 runs each) passed on Haiku 4.5, Sonnet 5, Opus 5.5 and Fable 5.1. The same scenarios also run in Amazon Bedrock mode.
  • GitHub Actions runs everything on every push, and daily against the newest SDK and Claude Code release.
  • Six engine behaviours I ran into, and the workaround for each, are written up in spike/FINDINGS.md.

See it interactively. A playable, pencil-drawn walkthrough of one refund: type your name, buy a teapot, ask for a refund, approve it as the manager, and pull the plug at almost any moment to watch the agent recover:
https://osamastro7-droid.github.io/temporal-claude-agent/map/

The game at stage 6: the agent pauses at the issue_refund tool call while you decide as the manager
The plug pulled at stage 7: the room is dark, while Temporal's notebook and the approved refund glow

Status. v0.1, a tested prototype and a community project, not an official Temporal or Anthropic integration. It relies on how today's Claude Code engine handles "defer". Each Claude step starts a new engine process (about 1 to 1.5 s of overhead). Only tools declared as durable are durable. The included file session store is for development. The README lists every limitation.

Quickstart (Python 3.10+ and the Temporal CLI). The demo uses a scripted stand-in Claude by default, so no API key is needed:

git clone https://github.com/osamastro7-droid/temporal-claude-agent && cd temporal-claude-agent
pip install -e ".[dev]"
temporal server start-dev                          # window 1
cd examples && python -m refund_agent.worker       # window 2
cd examples && python -m refund_agent.run_demo     # window 3

Author(s)

Osamah Al-Harazi — GitHub — headshot/avatar

Activity

  1. github-actions commented on Sep 24, 2026

    @github-actions

    ZiggyBot — automated pre-evaluation


    Criterion Assessment
    Useful to Temporal users ✅ Yes — Addresses durability for Claude Agent SDK agents, a specific integration gap for users combining LLM agents with Temporal.
    Benefits clearly explained ✅ Yes — README and issue describe the mechanism, failure modes, security considerations, and limitations in concrete terms.
    OSI Approved License ✅ Yes — MIT license, confirmed by GitHub classification and license reference in the README.
    README with working instructions ✅ Yes — Quickstart lists prerequisites and three-window setup; demo defaults to a scripted stand-in model so no API key is required.

    Overall: Looks good ✅

    Notes:

    • Core mechanism relies on the Claude Code engine's undocumented handling of a "defer" response from a PreToolUse hook. The submitter states this explicitly and documents the engine quirks and workarounds in spike/FINDINGS.md, along with tested engine versions (2.1.277, 2.1.280). This is a stated fragility, not a hidden one.
    • Idempotency approach is sound: tool calls run as Activities with fixed IDs (tool-<tool_use_id>), and the Activity ID is passed to external systems as an idempotency key. The refund Activity demonstrates this and enforces the amount cap in code rather than trusting model output.
    • Crash-recovery claims are backed by three SIGKILL-based tests covering distinct failure points, including a crash after the money moved but before the reply.
    • Limitations are enumerated candidly: per-step process overhead (1–1.5 s), serialized parallel tool calls, non-durable built-in tools, file-based session store unsuitable for production, and no Continue-As-New for long histories.
    • Repository has 3 stars and is v0.1; maturity is limited and the test scope covers a single demo domain.
    • The workflow-side module documents the determinism constraint but stores conversation state in an external session store; correctness depends on that store being shared and durable in production, which is noted.

    Suggested questions for submitter:

    • What is the expected failure behavior if a future Claude Code engine version changes or removes the "defer" handling that the pause mechanism depends on, beyond CI detection?
    • Is Continue-As-New planned for long-running agents, given the stated payload and history-length limits?
    • Does the session store interface (append/load) have a reference non-file implementation, or is that left entirely to the user?

    Notable Temporal pattern:

    Modeling each LLM tool call as a separate Activity with a deterministic, fixed Activity ID (tool-<tool_use_id>) so that the ID doubles as an idempotency key for external side effects, ensuring at-least-once Activity execution does not cause duplicate real-world actions. This is representative because it maps Temporal's Activity retry semantics directly onto the agent's need to avoid repeating irreversible tool calls.
    View in source ↗


    Automated AI pre-screen by ZiggyBot. Final decisions are made by the community team.

    Reviewer: Check the box below to generate a Contentful-ready long description for this submission.

    • Generate long description if needed
  2. osamastro7-droid commented on Sep 24, 2026

    @osamastro7-droid
    Author

    Thanks for the review! Here are the answers, with the evidence.

    1. If a future Claude Code engine changes or removes "defer"

    Pausing uses a documented feature. Anthropic's hooks docs describe the PreToolUse "defer" decision: the tool doesn't execute, the run ends with stop_reason "tool_deferred", and the SDK result carries deferred_tool_use. The docs describe one pause and resume; chaining several pauses in one session is not documented, and that is where the quirks in spike/FINDINGS.md come from. The docs also say "defer" is ignored when Claude makes several tool calls in one turn, which is why the package defers one call and asks Claude to repeat the others.

    If an engine ever stops honoring "defer", the agent now fails closed:

    • Durable tools never run inside the engine: they are stubs that only return an error, so nothing happens outside Temporal.
    • The runner detects that the engine ran a durable tool itself, did not pause at the call the hook deferred, or paused again at an answered call. It stops the step with a non-retryable error that names the engine version, and the workflow fails loudly.
    • The workflow never runs the same tool call twice, whatever Claude asks.

    Before this change, a simulated engine that ignores "defer" made the workflow complete "successfully" while the agent told the customer the order did not exist. Now the workflow stops. Tests: tests/test_fail_closed.py (the real Claude Code engine with a stand-in model).

    CI also runs every day against the newest claude-agent-sdk and Claude Code. Today that is SDK 0.2.159 with Claude Code 2.1.281: 17/17 tests pass, and 8/8 scenarios in both Anthropic API mode and Amazon Bedrock mode (stand-in model).

    2. Continue-As-New

    Planned, not implemented yet. Measured with the real agent loop on Temporal Server 1.32 defaults:

    • Each tool call adds 12 events and about 3.2 KiB of history for the demo agent. The conversation itself lives in the session store, not in history.
    • Each tool result is stored twice (as the tool's output and as the next Claude step's input).
    • Temporal first suggested Continue-As-New at tool call 342 (4,101 events; the default thresholds are 4,096 events or 4 MiB), or after about 41 calls with 50 KB results.
    • The default max_segments=50 caps a run at 50 Claude steps, so runs with small results stay far below that.

    The plan: at a step boundary with no approval pending, when workflow.info().is_continue_as_new_suggested() is true, continue as new with the session id, the tool result not yet delivered and the counters, then resume the same session. Until then, large tool results should be passed by reference (Temporal rejects any payload over 2 MiB by default).

    3. Session store

    The runner accepts any Claude Agent SDK SessionStore (only append and load are required), and Anthropic's SDK repo includes reference S3, Redis and Postgres adapters (examples/session_stores). I ran the full refund flow on the real engine with the Redis reference adapter (with fakeredis, not a live server), every step on a fresh machine with an empty Claude folder: it completed, and the engine ran no tool itself. FileSessionStore now passes the six required contracts of the SDK's run_session_store_conformance suite in CI (tests/test_session_store.py). A packaged production store is still on the roadmap; until then the README points to those adapters.

    The numbers are also in the README limitations. Happy to answer anything else.

  3. added
    website ready!This is ready to be added to the Code Exchange site by a Temporalite
    and removed
    triageIssues that Temporal folk need to look at
    on Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

code exchange submissionCode and/or content about Temporal!website ready!This is ready to be added to the Code Exchange site by a Temporaliteziggy reviewedPre-screened by ZiggyBot

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions