Skip to content

codeprakhar25/agent-baton

Repository files navigation

agent-baton

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.

npm npm downloads license node Platform: macOS Platform: Linux Platform: WSL PRs Welcome

asciicast


The problem

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.


Table of Contents


Install

npm install -g @codeprakhar25/agent-baton

Requirements: Node.js 18+


Quick Start

# 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 codex

Every handoff is written to ~/.local/state/agent-baton/projects/<slug>/handoffs/ and never touches your repo.


How It Works

Baton has two integration paths:

Claude Codebaton 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.

Codexbaton 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>

Handoff format

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

Supported Agents

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.


Commands

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 codex

Configuration

Global 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.


Troubleshooting

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.


Current Limits

  • 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 pickup manually.

License

MIT — see LICENSE.

About

Don't stop by an agent limit

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors