跳轉到

Agent quota warmup (agent-warmup)

agent-warmup starts the Claude Pro/Max 5-hour subscription usage window at a scheduled time (e.g. 06:00 on workdays), so a fresh window is already running before you begin real work — plus an ad-hoc "warm up at this time" one-shot.

The CLI lives at dot_dotfiles/bin/executable_agent-warmup (deployed to ~/.dotfiles/bin/agent-warmup, on PATH). Claude-only by design — Codex / OpenCode have different quota systems.

Why it drives an interactive session (not claude -p)

The 2026-06-15 billing split changed everything

Since 2026-06-15, only interactive Claude Code usage advances the rolling 5-hour subscription window. Headless claude -p (and the Agent SDK, GitHub Actions, third-party agents) now draws from a separate metered Agent SDK credit pool — it does not move the window. A claude -p "warmup" would silently burn metered API credit and accomplish nothing. See pitfalls/headless-claude-p-does-not-move-5h-window.md.

So agent-warmup run drives a real interactive claude TUI:

  1. prefers an existing Herdr session and creates a no-focus transient workspace; falls back to a transient session in the existing GUI-login tmux server when no unambiguous Herdr session is running;
  2. in an isolated cwd ($XDG_CACHE_HOME/agent-warmup/claude) so no project CLAUDE.md / context is loaded;
  3. with ANTHROPIC_API_KEY unset (env -u) so it uses your subscription auth (an API key would route to metered credit and not move the window);
  4. accepts the first-run folder-trust prompt, sends a tiny prompt + Enter, waits for the reply, logs the pane (and /usage with --verify), then kills that workspace/session (unless --keep).

Default model is $AICAP_CLAUDE_MODEL (→ haiku) to keep window-token consumption tiny.

The tmux fallback must borrow your existing server

claude's TUI renders nothing when its tmux server was created by a background daemon (launchd/pueue) — but renders fine when a daemon connects to your already-running GUI-session server and spawns claude there. So the tmux fallback injects into your default tmux server. Constraint: a tmux server must be running at warmup time when Herdr is unavailable. If none exists and the caller is not a TTY, run notifies and exits rather than silently no-op'ing. Full four-trap story (uv launcher hang, amfid/pyexpat stall, secondary-volume Python, daemon-server blank pane): pitfalls/agent-warmup-scheduled-run-hangs-or-blank-under-daemon.md.

Confirmed working (2026-06-20)

Both backends verified end-to-end (pueue one-shot and launchd timer): the scheduled run rendered claude, sent the prompt, got ⏺ hi, and captured /usage showing the usage on the "Current session" (5-hour) bucket with the metered "Usage credits" pool off — i.e. genuine interactive subscription usage that advances the window.

Commands

# One-off: warm up now, keep the session + capture /usage for inspection
agent-warmup verify

# Recurring: 06:00 every weekday (launchd on macOS, systemd user timer on Linux)
agent-warmup install --daily weekdays --time 06:00
agent-warmup uninstall

# Ad-hoc one-shot via pueue
agent-warmup at --at '06:00'
agent-warmup at --delay 8h

# Backend control (auto prefers ambient/default/unique running Herdr)
agent-warmup run --backend herdr --herdr-session default
agent-warmup run --backend tmux

# Inspection
agent-warmup status            # timer state + pending one-shots + recent run logs
agent-warmup status --json
agent-warmup cancel            # remove pending one-shots

# The primitive the schedulers call
agent-warmup run [--backend auto|herdr|tmux] [--herdr-session NAME] [--model haiku] [--prompt 'Reply with just: hi'] [--timeout 120] [--keep] [--verify]

The public CLI is generated from Tyro dataclasses. at and install forward the shared run flags, including backend selection, into the scheduled run.

Backends

Schedule macOS Linux
Recurring (install) ~/Library/LaunchAgents/com.<user>.agent-warmup.plist (StartCalendarInterval), loaded with launchctl bootstrap; validated with plutil -lint ~/.config/systemd/user/agent-warmup.{service,timer} (OnCalendar=, Persistent=true), systemctl --user enable --now; validated with systemd-analyze verify
One-shot (at) pueue group agent-warmup, label agent-warmup:oneshot:<ts> same

The recurring timer is imperative and opt-in — generated by install, it is not chezmoi-managed (so there is no .chezmoi.toml.tmpl prompt and no forced install on every machine). Run install on the host where you want the warmup.

Both install and at enter through a main-volume Python and a hidden, stdlib-only bootstrap. The bootstrap tries the pinned PEP 723/Tyro environment with uv first (--no-project --no-env-file --offline --python <main-python>). The public run touches a startup sentinel before any interactive side effect. Only a timeout or failure before that sentinel can fall back to the stdlib core; once the sentinel exists, fallback is forbidden so a prompt cannot be sent twice. Logs record runtime: uv or runtime: direct-fallback; uv bootstrap diagnostics live in ~/.cache/agent-warmup/uv-bootstrap.log.

On 2026-08-25, uv 0.9.24 was re-probed successfully through both pueue and a temporary gui/<uid> LaunchAgent with the exact flags above. No uv upgrade was needed; the old blanket "uv cannot run under daemons" assumption was too broad.

Caveats

  • Herdr mode requires an already-running Herdr session. agent-warmup does not start a daemon. Ambient Herdr wins, then the default or sole running session; ambiguous sessions require --herdr-session.
  • Tmux fallback requires a login-session tmux server. A daemon-created server still renders a blank Claude TUI.
  • Machine asleep at the scheduled time → the job fires on wake (launchd coalesces missed calendar events; systemd uses Persistent=true), so the window geometry shifts to wake time.
  • Keychain must be unlocked (you must be logged into the GUI session) for subscription auth.
  • Auth expiry / blank pane → run logs the capture and fires a desktop notification (status warning: blank pane) rather than reporting a false ok.
  • Brief workspace blip → a no-focus warmup-<ts> Herdr workspace or tmux session exists for about a minute, then only that created resource is closed.

Relationship to agent-wakeup

agent-warmup is a separate tool from agent-wakeup (quota recovery — scheduling a continue into an existing blocked pane). Warmup needs no existing pane, starts a fresh isolated session, and must not continue real work. The broader auto-continue loop and a unified dashboard remain backlog items.