Metadata-Version: 2.4
Name: rewind-llm
Version: 0.2.0
Summary: Record-replay debugger for AI agent runs
Author: Praash
License-Expression: MIT
Project-URL: Homepage, https://github.com/praashh/rewind
Project-URL: Repository, https://github.com/praashh/rewind
Project-URL: Issues, https://github.com/praashh/rewind/issues
Keywords: ai,agents,debugging,record-replay,trace
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Debuggers
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.24
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.20; extra == "anthropic"
Provides-Extra: openai
Requires-Dist: openai>=1.0; extra == "openai"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: build; extra == "dev"
Dynamic: license-file

# rewind

[![PyPI](https://img.shields.io/pypi/v/rewind-llm)](https://pypi.org/project/rewind-llm/)
[![Python](https://img.shields.io/pypi/pyversions/rewind-llm)](https://pypi.org/project/rewind-llm/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Record-replay debugger for AI agent runs. Time-travel debugging for agent harnesses.

An agent only touches the outside world through a narrow boundary: **model calls in, tool calls out.** `rewind` captures everything crossing that boundary, then replays it exactly — or branches from any step to explore alternate trajectories.

## Installation

```bash
pip install rewind-llm
```

With optional SDK extras:

```bash
pip install rewind-llm[anthropic]   # Anthropic SDK support
pip install rewind-llm[openai]      # OpenAI SDK support
```

## Quickstart — SDK instrumentation

```python
from rewind import Session, Mode, instrument

session = Session(mode=Mode.RECORD, trace_path="run.jsonl")

# One line — captures all HTTP traffic through the SDK client.
instrument(client, session)

# Your agent code stays exactly the same:
response = client.messages.create(model="claude-sonnet-4-20250514", messages=[...])
```

Tools still use the decorator:

```python
@session.tool
def search(query: str) -> str:
    return my_search_api(query)
```

### Record → Replay → Branch

```python
# RECORD a run
session = Session(mode=Mode.RECORD, trace_path="run.jsonl")
instrument(client, session)
run_my_agent(client)

# REPLAY it — exact same trajectory, no API calls
session = Session(mode=Mode.REPLAY, trace_path="run.jsonl")
instrument(client, session)
run_my_agent(client)  # returns recorded results

# BRANCH at step 2 — inject a different model response, go live after
session = Session.branch("run.jsonl", "branch.jsonl", at_step=2,
                         override=my_edited_response)
instrument(client, session)
run_my_agent(client)  # replays steps 0-1, injects override at 2, live from 3+
```

## CLI proxy recording

Record and replay CLI tool sessions through a transparent HTTP proxy — no code changes required.

```bash
# Record a Claude Code session
rewind record -- claude "fix the bug"

# Replay it (no API calls, instant)
rewind replay traces/anthropic_20260101_120000.jsonl -- claude "fix the bug"

# Standalone proxy (connect your tool manually)
rewind proxy --mode record --trace session.jsonl
```

The proxy intercepts HTTP traffic between your CLI tool and the AI provider, capturing every request/response pair.

### Provider auto-detection

The proxy auto-detects the AI provider from the command name and sets the appropriate environment variable:

| Command | Provider | Env var |
|---------|----------|---------|
| `claude`, `claude-code` | Anthropic | `ANTHROPIC_BASE_URL` |
| `opencode`, `aider` | OpenAI | `OPENAI_BASE_URL` |
| `gemini` | Gemini | `GOOGLE_GEMINI_BASE_URL` |
| `agy` | Antigravity | `ANTIGRAVITY_BASE_URL` |

## CLI commands

```bash
rewind ls                                           # List traces
rewind show <trace.jsonl>                           # Pretty-print
rewind diff <a.jsonl> <b.jsonl> [--html out.html]   # Diff traces
rewind branch <trace> --at N --edit|--inject|--live  # Branch
rewind migrate <traces>...                          # v0 → v1
rewind record -- <cmd>                              # Record via proxy
rewind replay <trace> -- <cmd>                      # Replay via proxy
rewind proxy --mode record|replay --trace <file>    # Standalone proxy
```

## Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `REWIND_TRACE_DIR` | `traces` | Directory for storing trace files |

```bash
# Store traces in a custom directory
export REWIND_TRACE_DIR=~/.rewind/traces
rewind record -- claude "fix the bug"

# List traces from a custom directory
REWIND_TRACE_DIR=/tmp/test rewind ls
```

## How it works

### Transport capture
`instrument(client, session)` swaps the httpx transport inside your SDK client (Anthropic or OpenAI). Every HTTP request/response is captured transparently — no changes to your agent code.

### Normalization
Real requests contain volatile fields (timestamps, request IDs, SDK versions) that change on every run. `rewind` strips these before hashing so irrelevant changes don't cause false divergences. Configure with `MatchConfig`:

```python
from rewind import MatchConfig, Session, Mode

config = MatchConfig(
    ignore_headers=["x-request-id", "date"],
    ignore_body_paths=["metadata.session_id"],
    ignore_body_patterns=[r"[0-9a-f-]{36}"],  # UUIDs
)
session = Session(mode=Mode.REPLAY, trace_path="run.jsonl", match_config=config)
```

### Structured divergence reports
When inputs don't match, `DivergenceError` carries a `DivergenceReport` showing *which fields* differ and whether each was marked as ignorable — so you can tighten config in one pass.

### Modes
- **RECORD**: executes for real, appends each request/response to a JSONL trace
- **REPLAY**: returns recorded results by step order, verifies inputs via normalized SHA-256 hash
- **BRANCH**: replays up to step N, injects an override or goes live, writes a child trace

## Development

```bash
pip install -e ".[dev]"
python -m pytest tests/ -v
```

No API key required — tests use `MockTransport` and `MockModel`.

## License

MIT
