GitHub - ixchio/agent-vcr: Time-travel debugging for AI agents replay, edit, and resume executions without reruns.

9 min read Original article ↗

Your AI agent corrupted the codebase. Now what?

The only tool that physically deletes hallucinated files — git reset --hard, not just state rollback.

PyPI CI Coverage License Python

No API keys. No cloud. No vendor lock-in. Works with TERX — memory layer for browser agents.



The Problem Every AI Agent Developer Has Hit

You ran Claude Code, OpenHands, or a LangGraph agent autonomously.

It wrote 40 files. It failed at step 8. Now you have:

  • 23 files that shouldn't exist
  • A broken import chain
  • State that says "success" on steps that half-ran
  • No way to know what the repo looked like before step 6
Your repo after a bad autonomous run:

/src/handlers.py          ← hallucinated, breaks import
/src/auth_v2.py           ← duplicate of auth.py, never needed
/src/models_refactor.py   ← partial rewrite, syntax error
/tests/test_fake.py       ← tests for code that doesn't exist
/config/settings_new.py   ← overwrote working config

Every other tool shows you logs. Agent VCR runs git reset --hard and deletes every one of those files.


ACID Rollback — The Feature Nobody Else Has

from agent_vcr import VCRRecorder
from agent_vcr.integrations.openhands import ACIDWorkspace

recorder = VCRRecorder()
acid = ACIDWorkspace("/my/workspace", recorder=recorder)

acid.begin(session_id="task-001")        # isolated git branch
acid.savepoint(state, node_name="coder") # checkpoint state + filesystem
acid.savepoint(state, node_name="tester")

# Agent writes 47 files. 23 are hallucinated garbage. Step 6 failed.
acid.rollback(to_frame_index=1)
# git reset --hard
# All 23 files: physically deleted from disk. Not hidden. Gone.

acid.commit()                            # merge only the clean branch
Before rollback:          After rollback:
/src/handlers.py    ✗     DELETED
/src/auth_v2.py     ✗     DELETED
/tests/fake_test.py ✗     DELETED
/src/utils.py       ✓     kept
/src/models.py      ✓     kept

LangSmith shows you what happened. LangFuse shows you what happened. Arize shows you what happened.

Agent VCR changes what happened.


Ghost Replay — Never Pay for the Same Task Twice

Agent succeeds? Save it. Run it again for free forever.

from agent_vcr.golden_cache import GoldenRunCache

cache = GoldenRunCache()
cache.save_golden_run("Build a REST API with JWT auth", recorder)

# Every future run of the same task:
outputs, ledger = cache.replay("Build a REST API with JWT auth")
print(ledger)
RUN 1 (original)      RUN 2 (Ghost Replay)
─────────────────     ─────────────────────
Tokens:   4,100       Tokens:      0
Cost:   $0.0123       Cost:    $0.00
Time:  2,350ms        Time:      1ms

💰 100% savings · $0.0123 saved · 4,100 tokens · 2,349ms faster

Time-Travel Debugging

Agent fails at step 8 of 10? Don't re-run from zero.

from agent_vcr import VCRPlayer
from agent_vcr.models import ResumeConfig

player = VCRPlayer.load(".vcr/my_run.vcr")

# See exact state at every step
print(player.goto_frame(6))   # {'files_written': [...], 'plan': '...'}
print(player.get_errors())    # what broke and where

# Fix the prompt. Resume from step 6. Skip steps 0-5.
player.resume(
    agent_callable=coder,
    config=ResumeConfig(
        from_frame=6,
        state_overrides={"plan": "use SQLAlchemy instead of raw SQL"}
    )
)
Without Agent VCR          With Agent VCR
──────────────────         ──────────────────────────
Agent fails step 8         Agent fails step 8
Patch the code             player.goto_frame(7)
Re-run ALL 10 steps        Fix the state
$0.04 + 2 min wasted       Resume from step 7
Repeat for every bug       Done. $0.00 extra.

Who This Is For

You need this if you're running:

  • Claude Code / Cursor autonomous mode
  • OpenHands on real codebases
  • LangGraph agents that write files
  • CrewAI pipelines with filesystem access
  • Any autonomous coding agent on a repo you care about

You don't need this if you're only:

  • Doing RAG / chatbots (no filesystem risk)
  • Already happy with LangSmith for tracing

Quick Start

Record

from agent_vcr import VCRRecorder

recorder = VCRRecorder()
recorder.start_session("my_run")

state = {"query": "build a REST API"}
state = planner(state)
recorder.record_step("planner", input_state, state)

state = coder(state)
recorder.record_step("coder", input_state, state)

recorder.save()  # → .vcr/my_run.vcr

Or use the context manager — frames are saved even if the agent crashes:

with VCRRecorder() as recorder:
    recorder.start_session("my_run")
    # ... your agent code ...

Rewind & Fix

player = VCRPlayer.load(".vcr/my_run.vcr")

diff = player.compare_frames(5, 6)
# {'added': {'bad_file': '...'}, 'modified': {'plan': '...'}}

player.resume(
    agent_callable=coder,
    config=ResumeConfig(from_frame=5, state_overrides={"plan": "fixed"})
)

Integrations

LangGraph — one line

from langgraph.graph import StateGraph
from agent_vcr import VCRRecorder
from agent_vcr.integrations.langgraph import VCRLangGraph

recorder = VCRRecorder()
graph = VCRLangGraph(recorder).wrap_graph(graph)  # ← one line, that's it

result = graph.invoke({"query": "Build a todo app"})
recorder.save()

CrewAI

from agent_vcr.integrations.crewai import VCRCrewAI

recorder = VCRRecorder()
recorder.start_session("crew_run")
result = VCRCrewAI(recorder).kickoff(crew)
recorder.save()
pip install "ai-agent-vcr[crewai]"
pip install "ai-agent-vcr[langgraph]"

Raw Python (decorator)

from agent_vcr.integrations.langgraph import vcr_record

@vcr_record(recorder, node_name="research_step")
def research(state: dict) -> dict:
    return {"findings": search(state["query"])}

Sentinel — Real-Time Code Guardian

Catches what the agent wrote before it moves to the next step.

from openhands_sentinel import Sentinel

sentinel = Sentinel(recorder=recorder)
sentinel.attach(runtime.event_stream)  # 3 lines. auto-intercepts every write.
STEP 2: Agent writes handlers.py
🛡️ SENTINEL: VIOLATIONS DETECTED
  CRITICAL  hash_password() already exists in auth/utils.py:8 — reuse it
  CRITICAL  handle_auth_request() is 109 lines (limit: 40) — break it up
  CRITICAL  Cyclomatic complexity: 32 (limit: 8)

STEP 3: Agent self-corrects
🛡️ SENTINEL: handlers.py — CLEAN ✓
Without Sentinel With Sentinel
Agent writes bad code
Sentinel catches it < 10ms
Agent self-corrects done
Human reviews PR manual zero
Cost 2× LLM + human time 1 extra LLM call

Standalone scan:

sentinel scan ./my-ai-project

TUI Debugger

┌──────────────────────────────────────────────────────────┐
│ 📼 Agent VCR                  Session: my_run · 8 frames │
├──────────────────────────────────────────────────────────┤
│ ▶ Frame 0  │ planner     │ 100ms  │ ●                    │
│   Frame 1  │ researcher  │  250ms │ ●                    │
│   Frame 2  │ coder       │  480ms │ ✗ ERROR              │
│   Frame 3  │ tester      │   80ms │ ●                    │
├──────────────────────────────────────────────────────────┤
│  { "query": "build a todo app", "plan": null }           │
├──────────────────────────────────────────────────────────┤
│ ←/→ navigate  │ e edit  │ d diff  │ r resume  │ q quit   │
└──────────────────────────────────────────────────────────┘

Keybindings: ↑/↓ or j/k navigate · e edit state · 1/2/3 input/output/diff · r resume · s search · q quit

Claude Code hooks:


DAG Visualization

vcr-server .vcr/
# localhost:8000
original_run ──────────────────────────────────────────► [done]
               │ frame 3
               ╰──► fork_v1 ──► [coder] ──► [tester] ──► [done]
               ╰──► fork_v2 ──► [coder] ──► [done]

Live WebSocket streaming. Every fork is a branch. Errors in red.


vs Everything Else

Honest take: LangSmith, Langfuse, Arize Phoenix, and AgentOps are serious platforms with large teams. They are observability tools — they show you what happened. Agent VCR is an intervention tool — it lets you change what happened. Different category. The overlap is tracing. Everything else diverges.

Capability 📼 Agent VCR LangSmith LangFuse AgentOps Arize Phoenix
Record execution traces
Production dashboards Local ✅ best-in-class
Eval / scoring pipelines
Time-travel / session replay ✅ (view only)
Edit state & resume mid-chain
ACID filesystem rollback
Ghost Replay (zero tokens)
Sentinel (real-time code guard)
Fork from any frame
TUI debugger
Fully local / self-hosted ❌ Cloud ❌ Cloud
Framework-agnostic ⚠️ LangChain

AgentOps — closest competitor on time-travel. It lets you view past sessions. It does not let you edit state and resume, fork a session, rollback the filesystem, or replay for zero tokens. If you need view-only replay, AgentOps is mature. If you need to actually intervene, you need Agent VCR.

Use LangSmith/Langfuse/Phoenix/AgentOps for production tracing and evals. Use Agent VCR when you need to actually fix a broken run without re-running it, rollback filesystem damage, or replay a successful run for free.


vs LangGraph's Built-In Checkpointer

LangGraph's checkpointer is solid if you're 100% LangGraph and only need state inspection.

The gap: when your agent writes files to disk and fails, the checkpointer rolls back the state object. The files stay. Agent VCR runs git reset --hard. The files are gone.

LangGraph Checkpointer Agent VCR
Checkpoint in-memory state
Rollback files from disk
Ghost Replay (zero tokens)
Sentinel (code guardian)
Works with CrewAI, raw Python
JSONL format (git-diffable)
Session forking

Performance

Every benchmark is enforced in CI. If it regresses, CI fails.

pip install -e ".[dev]"
pytest tests/benchmarks/ -v --benchmark-only
Benchmark Limit What it measures
test_benchmark_recorder_overhead < 5ms mean Serialize + buffer one state snapshot
test_benchmark_file_write_speed > 1,000 frames/sec Sustained write throughput (10K frames)
test_benchmark_load_speed < 500ms Load a 10,000-frame session from disk
test_benchmark_goto_frame < 1ms Random-access time-travel to any frame

Historical results: ixchio.github.io/agent-vcr/dev/bench/


Storage Format

Plain JSONL. One object per line.

{"type": "session", "data": {"session_id": "my_run", "created_at": "..."}}
{"type": "frame", "data": {"node_name": "planner", "input_state": {...}, "output_state": {...}}}
{"type": "frame", "data": {"node_name": "coder", ...}}
  • Human-readable — open in any text editor
  • Git-diffable — review agent state in PRs
  • Append-only — safe for concurrent agents, no full-file rewrites
  • Streamable — parse line-by-line without loading the full file

API Reference

VCRRecorder

recorder = VCRRecorder(
    output_dir=".vcr",
    auto_save=True,
    diff_mode=False,
)

recorder.start_session(session_id="my_run", tags=["prod"])
recorder.record_step(node_name, input_state, output_state, metadata)
recorder.record_llm_call(model, messages, response, tokens_input, tokens_output, latency_ms)
recorder.record_tool_call(tool_name, tool_input, tool_output, latency_ms)
recorder.record_error(node_name, input_state, error)
recorder.save() -> Path
recorder.fork(from_frame=3) -> VCRRecorder

VCRPlayer

player = VCRPlayer.load(".vcr/my_run.vcr")

player.goto_frame(index)           # → output state at frame N
player.get_input_state(index)      # → input state at frame N
player.get_errors()                # → [Frame, ...]
player.compare_frames(a, b)        # → {'added': {}, 'removed': {}, 'modified': {}}
player.get_total_cost()            # → float (USD)

player.resume(
    agent_callable,
    config=ResumeConfig(
        from_frame=7,
        state_overrides={"k": "v"},
        mode=ResumeMode.FORK,      # FORK | REPLAY | MOCK
    )
)

ACIDWorkspace

acid = ACIDWorkspace("/workspace", recorder=recorder)
acid.begin(session_id="task-001")
acid.savepoint(state, node_name="coder")
acid.rollback(to_frame_index=2)    # git reset --hard
acid.commit()

GoldenRunCache

cache = GoldenRunCache(cache_dir=".vcr/golden")
cache.save_golden_run(task_description, recorder)
outputs, ledger = cache.replay(task_description)
cache.invalidate(task_description)
cache.list_golden_runs()

Examples

# ACID rollback + Ghost Replay — start here
python examples/acid_golden_run.py

# Time-travel: rewind, edit state, resume
python examples/time_travel_demo.py

# Sentinel: watch agent self-correct in real time
python examples/sentinel_demo.py

# LangGraph auto-instrumentation
python examples/langgraph_integration.py

# Basic recording and playback
python examples/basic_usage.py

Roadmap

  • Core recording and playback
  • Time-travel resume with state injection
  • LangGraph + CrewAI integrations
  • Async recorder and player
  • Terminal TUI debugger (vcr)
  • Claude Code hook scaffolding (vcr init --claude-code)
  • Live dashboard with DAG visualization
  • ACID Transactions (git-backed filesystem rollback)
  • Ghost Replay (zero-cost replay of successful runs)
  • Sentinel — real-time code quality guardian
  • Context manager (with VCRRecorder() as r:)
  • Claude Code / Cursor integration
  • AutoGen integration
  • Replay regression tests (golden paths as CI assertions)
  • Collaborative debugging (share sessions)
  • Cloud storage backend (S3, GCS)

Community

If Agent VCR saved your repo from a bad autonomous run, share it:

  • OpenHandsDiscord #tools channel
  • LangGraphDiscord #community channel
  • r/LocalLLaMA — post your ACID rollback story
  • Hacker News — Show HN posts with real before/after diffs get traction

The best growth comes from developers sharing the moment it saved them. If that's you, a post with your actual corrupted-repo story (even anonymized) is worth more than any ad.


Contributing

git clone https://github.com/ixchio/agent-vcr.git
cd agent-vcr
pip install -e ".[dev,tui]"
pytest tests/unit/ -v

See CONTRIBUTING.md for guidelines.


License

MIT — see LICENSE.