Don't lose your work when an AI coding agent hits its quota.
baton monitors usage for Claude Code and Codex, warns you before the threshold, and writes a rich Markdown handoff — with git evidence — so the next agent picks up exactly where you left off.
You're deep in a task — three files modified, a bug half-fixed. Claude or Codex hits its hourly usage limit. You either:
- Lose context and start explaining from scratch in a new session.
- Manually copy-paste diffs and notes into the next agent.
Baton fixes that. It intercepts at the threshold, gives you a choice, and writes a structured handoff that the next agent can read cold.
- Install
- Quick Start
- How It Works
- Supported Agents
- Commands
- Configuration
- Troubleshooting
- Current Limits
npm install -g @codeprakhar25/agent-batonRequirements: Node.js 18+
# 1. Initialize once per project
cd ~/your-project
baton init
# 2. Work normally — baton hooks into Claude automatically
# For Codex, use the baton wrapper
baton codex "continue the auth refactor"
# 3. When baton warns you, choose: continue or hand off
# Then pick up in the next agent
baton pickup --to codexEvery handoff is written to ~/.local/state/agent-baton/projects/<slug>/handoffs/ and never touches your repo.
Baton has two integration paths:
Claude Code — baton init installs global hooks into ~/.claude/settings.json. On SessionStart, UserPromptSubmit, and PreToolUse, baton guard reads cached usage with a three-tier TTL:
| Usage range | Cache TTL |
|---|---|
| Below 50% | 15 min |
| 50% → warning band | 5 min |
| Warning band → limit | none (always fresh) |
Within a Claude session, Baton asks at most once in the warning band and once at the hard threshold. If you choose to continue in Claude, later prompt-submit and tool hooks stay quiet until the next Claude session resets the notification state.
Codex — baton codex wraps the codex binary. Usage is checked before launch. If you're over the threshold, Baton asks whether to continue, write a handoff, or run baton pickup.
baton guard / baton codex
│
▼
fetch usage (cached)
│
over threshold?
┌────┴────┐
no yes
│ │
allow ask user
work │ │
continue handoff
│
agent writes Markdown
+ Baton appends git evidence
│
write HANDOFF-latest.md
│
baton pickup --to <agent>
Each handoff is a Markdown file with:
- Agent-authored task summary, progress, decisions, remaining work, and next step
- Baton metadata and source handoff path
- Git branch, status, diff stat, and last commits
- Full uncommitted diff appended as evidence (truncated at
handoff_extraction.max_diff_chars) - Instructions for the next agent
Git state is the durable source of truth — if transcript extraction is incomplete, the diff tells the full story.
~/.local/state/agent-baton/projects/<project-slug>-<hash>/
handoffs/
HANDOFF-latest.md ← always the most recent
HANDOFF-<timestamp>.md ← timestamped copy
usage-cache.json
pending-transfer.json
| Agent | Integration | Detection |
|---|---|---|
| Claude Code | SessionStart, UserPromptSubmit, PreToolUse hooks |
OAuth token → Claude usage API |
| Codex | baton codex wrapper |
~/.codex/sessions/**/rollout-*.jsonl token_count.rate_limits events |
| Cursor | Transcript fallback | Hard-limit error text in transcripts |
| Gemini CLI | Transcript fallback | Hard-limit error text in transcripts |
Claude and Codex have proactive detection — Baton knows you're near the limit before it's hit. Cursor and Gemini rely on hard-limit text appearing in transcripts.
| Command | Description |
|---|---|
baton init |
Install global config and Claude hooks for the current project |
baton usage --from <agent> |
Print current usage-limit status |
baton guard --from claude --hook |
Claude hook driver (called automatically) |
baton codex [-- <args>] [prompt] |
Launch Codex with usage preflight |
baton handoff --from <agent> |
Manually capture task state and write a handoff |
baton handoff --from <agent> --file <path> |
Register a Markdown handoff written by the agent |
baton pickup [--to <agent>] |
Launch next agent with the handoff |
# Check usage
baton usage --from claude
baton usage --from claude --json # machine-readable
baton usage --from claude --refresh # skip cache
# Codex wrapper
baton codex # bare launch with preflight
baton codex "finish the login flow" # with initial prompt
baton codex -- --model o4-mini # pass codex flags
# Handoff
baton handoff --from codex
baton handoff --from claude --reason rate-limit
baton handoff --from claude --reason rate-limit --file /tmp/baton-handoff.md
baton handoff --from claude --launch # write + immediately run pickup
# Pickup
baton pickup # choose agent interactively
baton pickup --to claude
baton pickup --to codexGlobal config lives at ~/.config/agent-baton/config.json. Per-project overrides (never committed) at .baton/config.json. baton init adds .baton/ to .gitignore automatically.
{
"agents": {
"cursor": { "enabled": true, "priority": 1 },
"claude": { "enabled": true, "priority": 2 },
"codex": { "enabled": true, "priority": 3 },
"gemini": { "enabled": true, "priority": 4 }
},
"limits": {
"mode": "ask",
"handoff_percent": 95,
"warning_buffer_percent": 5,
"auto_handoff_on_hard_limit": true,
"windows": {
"claude": {
"five_hour": { "enabled": true, "handoff_percent": 95 },
"weekly": { "enabled": true, "handoff_percent": 98 },
"extra": { "enabled": true, "handoff_percent": 95 }
},
"codex": {
"five_hour": { "enabled": true, "handoff_percent": 95 },
"weekly": { "enabled": true, "handoff_percent": 95 },
"unknown": { "enabled": true, "handoff_percent": 95 }
}
}
},
"usage_cache": {
"safe_ttl_ms": 900000,
"approach_percent": 50,
"approach_ttl_ms": 300000,
"near_limit_ttl_ms": 0,
"near_limit_percent": 75,
"pretool_ttl_ms": 60000,
"notify_cooldown_ms": 900000
},
"usage_sources": {
"claude": {
"oauth_credentials_path": "~/.claude/.credentials.json"
}
},
"handoff_extraction": {
"max_transcript_lines": 100,
"include_git_diff": true,
"max_diff_chars": 8000,
"scan_secrets": true
}
}Key options:
| Key | Default | Description |
|---|---|---|
limits.mode |
"ask" |
ask — prompt before acting; auto_handoff — write immediately; warn_only — log only |
limits.handoff_percent |
95 |
Hard threshold |
limits.warning_buffer_percent |
5 |
Warning band starts this many points below handoff_percent (default: warns at 90%) |
limits.auto_handoff_on_hard_limit |
true |
Auto-write handoff when hard-limit text appears |
usage_cache.safe_ttl_ms |
900000 |
Cache TTL below approach_percent (15 min) |
usage_cache.approach_percent |
50 |
Usage % where 5-min approach TTL kicks in |
usage_cache.approach_ttl_ms |
300000 |
Cache TTL from approach_percent to warning band (5 min) |
usage_cache.near_limit_ttl_ms |
0 |
Cache TTL inside warning band; 0 = always fresh |
usage_cache.pretool_ttl_ms |
60000 |
Min interval between fresh API fetches on PreToolUse |
usage_cache.notify_cooldown_ms |
900000 |
Legacy setting; Claude prompts are now limited by per-session notification state |
handoff_extraction.max_diff_chars |
8000 |
Per-file diff truncation cap |
Environment overrides: AGENT_BATON_CONFIG_HOME, AGENT_BATON_STATE_HOME, XDG_CONFIG_HOME, XDG_STATE_HOME.
No Codex usage found
Start or continue a Codex session so it emits token_count.rate_limits events into its rollout JSONL. Baton reads from ~/.codex/sessions/**/rollout-*.jsonl.
Claude usage unavailable
Ensure Claude Code is OAuth-authenticated and ~/.claude/.credentials.json exists. Run baton usage --from claude --refresh to force a fresh fetch.
Pickup says agent missing
The target CLI is not on PATH. Install it or check your shell config.
Hooks not firing
Re-run baton init. Check that ~/.claude/settings.json contains Baton guard hooks under hooks. Codex hooks live in ~/.codex/hooks.json.
- Context-window fullness is not monitored — Baton only handles usage-limit signals.
- Cursor and Gemini CLI have partial support — proactive detection requires hard-limit text to appear in transcripts.
- There is no automatic agent exit. Baton warns, writes the handoff, then you exit the current agent and run
baton pickupmanually.
MIT — see LICENSE.