Open source · Python · Apache-2.0
Stop AI agents from repeating actions after a crash, retry or resume
Call4Agents is a small Python library for agents that send emails, take payments or change data. It sits on top of LangGraph, Pydantic AI, the OpenAI Agents SDK, MCP servers or plain Python. Each action runs once even if the agent crashes, retries or resumes; a human "yes" covers only the exact action that was shown; and you can undo a run. Standard library only.
pip install call4agents
call4agents quickstart # offline, no API keys: 2 emails without the kit, 1 with it
See it in 30 seconds
Real output of call4agents quickstart: an agent crashes right after sending an email, then resumes.
What it does
- Once-only actions: an intent is written before the action and the result after; a repeat returns the result. An action interrupted half-way is reported as in doubt, never repeated blindly.
- Approvals bound to exact arguments: one "yes" covers one action; a later "no" is enforced.
- Idempotency keys that stay the same across crashes, for Stripe-style APIs.
- Undo: reverse a run newest first, each undo once only.
- Adapters for LangGraph, Pydantic AI, the OpenAI Agents SDK and MCP, tested on Linux, macOS and Windows with Python 3.10 to 3.13.
Guides
- Why did my AI agent send the email twice?
An agent crashes, retries or resumes right after a tool ran, and the tool runs again. Why it happens, and three honest ways to fix it in Python. - LangGraph: idempotent tool calls after interrupt, resume and crashes
LangGraph restarts a node when it resumes after interrupt(), and a crash can repeat work done after the last checkpoint. How to stop repeats. - OpenAI Agents SDK: stop a tool from running twice on retry or resume
How to make function tools in the OpenAI Agents SDK once-only across retries, resumed runs and approvals, with a runnable example. - Pydantic AI: crash-safe tools and deferred tool approvals
Pydantic AI continues an approved run as a new run with the saved message history. How to keep the tool from acting twice if the process stops in between. - Why does a long tool call run twice when I have several workers?
With several workers or replicas, a long agent tool call can be picked up by a second worker while the first is still running it. How to make the second one stop. - MCP servers: handle duplicate tool calls from client retries
An MCP client that loses a response may send the same tools/call again. How an MCP server in Python can return the first result instead of acting twice. - Human approval that binds to the exact arguments
A "yes" to pay(10, alice) must not run pay(9999, mallory), and one "yes" should cover one action. How to bind an approval to what was shown. - Idempotency keys for AI agent tool calls
Give an agent's API calls an idempotency key that stays the same across crashes and retries, so Stripe-style APIs drop the duplicate.
Honest limits
Version 0.5, early. Not yet proven with real payment systems in production. Several machines can share one run record in PostgreSQL; that is tested with separate processes and one local server, not yet on real networks. Every number on this site can be reproduced from the repository.