Agent Signal
New: OpenAI Agents SDK v0.24 approval + resume starter →

Agent templates that actually run.

Every starter is executed end-to-end before it ships, with the real output and the upgrade gotchas written down. Skip the docs archaeology and start from code that works.

terminal — real captured output
$ unzip openai-agents-v024-starter.zip && cd openai-agents-v024
$ pip install -r requirements.txt
$ python 02_approval_resume.py
interruptions: [('delete_file', '{"path": "/tmp/x"}'), ('send_email', '{"to": "a@b.c"}')]
serialized state: 12612 chars
interruptions after resume: []
final: Model saw tool outputs: call_0=deleted /tmp/x | call_1=Human declined sending email.

Run before it ships

Each template is executed on a clean box. The captured output is published next to the code.

Honest about mocks

If something is mocked (like the LLM, so no API key is needed), we say exactly what and why.

Gotchas included

Version-specific breakages and migration traps, written up from the release notes.

Inside a template

Code, run log, and the traps, in one place.

The OpenAI Agents SDK starter pauses risky tools for approval, checkpoints the run to a string, and resumes after a human approves or rejects. It also shows the new run-wide tool_error_formatter scrubbing secrets from crashed tools.

Open the template →
02_approval_resume.py
"""Human-in-the-loop: needs_approval -> interruption -> serialize RunState -> restore -> approve/reject -> resume."""
import asyncio
from agents import Agent, Runner, RunConfig, RunState, function_tool
from fake_model import ScriptedModel

@function_tool(needs_approval=True)
def delete_file(path: str) -> str:
    """Delete a file."""
    return f"deleted {path}"

@function_tool(needs_approval=True)
def send_email(to: str) -> str:
    """Send an email."""
    return f"emailed {to}"

def make_agent():
    return Agent(name="Ops", instructions="ops",
                 model=ScriptedModel([("delete_file", {"path": "/tmp/x"}), ("send_email", {"to": "a@b.c"})]),
                 tools=[delete_file, send_email])

async def main():
    cfg = RunConfig(tracing_disabled=True)
    agent = make_agent()
    r = await Runner.run(agent, "clean up and notify", run_config=cfg)
    print("interruptions:", [(i.tool_name, i.arguments) for i in r.interruptions])
    saved = r.to_state().to_string()          # durable checkpoint (e.g. DB)

8 tested and live. The rest are on the roadmap and clearly marked until they pass a real run.

OpenAI Agents SDK · v0.24.0 · Python✓ Tested

Approval + resume & tool error formatting

Pause tool calls for human approval, checkpoint the run to a string, approve/reject, and resume. Plus the new run-wide tool_error_formatter that scrubs secrets from crashed tools.

human-in-the-loopdurabilityerror handlingFree
LangGraph · 1.2.15 · Python✓ Tested

Human-in-the-loop approval graph

A propose → approval → execute StateGraph that pauses with interrupt(), saves the pause in a checkpointer under a thread_id, and resumes with Command(resume=...) down the approve or reject path.

human-in-the-loopcheckpointingFree
LangGraph · 1.2.15 · Python✓ Tested

Idempotent approval: no double charges on resume

When a run resumes after interrupt(), LangGraph re-runs the paused node from the top. This template shows the double-charge bug and the fix: approval-only node, separate execute node, and an idempotency key derived from thread_id.

human-in-the-loopidempotencycheckpointingPro
OpenAI Agents SDK · v0.24.0 · Python✓ Tested

Idempotent approved side effects

The Agents SDK runs an approved tool once per resume, but side effects still repeat on a duplicate resume of the same RunState or a whole-run retry after a crash. Shows both failures (2 charges) and the fix (1 charge).

human-in-the-loopidempotencydurabilityPro
LangGraph · 1.2.15 · Python✓ Tested

Multiple interrupts in one node

One node calls interrupt() three times and is answered one resume at a time (matched by position), plus two parallel nodes that interrupt in the same step and are resumed together with a dict keyed by Interrupt.id.

human-in-the-loopinterruptsFree
LangGraph · 1.2.15 · Python✓ Tested

Minimal web UI to approve paused runs

A stdlib-only web page (plain HTML forms, no JavaScript) that lists paused LangGraph runs and approves or rejects them with Command(resume=...), with 409/400/404 handling.

human-in-the-loopapproval UIFree

New tested templates, every week.

The Agent Signal newsletter: what shipped in agent frameworks, what to skip, and one starter you can run today. Free.

Guides