ShortLeash — stop runaway agents
A circuit breaker for AI coding-agent runs. Wraps an agent command, tails its telemetry in real time, and kills the run the moment it breaches a guardrail — budget, subagent-spawn cap, token cap, or wall-clock timeout. Emits a per-run receipt showing what it cost and why it stopped.
Vendors' "budgets" only warn while the meter keeps running. This one halts.
Install
Zero dependencies — Python 3.10+ stdlib only.
git clone <repo> && cd shortleash
# run directly:
python3 -m shortleash run --budget 2.00 -- <command>
# or add an alias (from the repo root, no install):
alias shortleash="python3 -m shortleash"
# or install for the `shortleash` console script:
pip install .
Usage
# Guard a Claude Code run: kill it past $2 or 10 subagent spawns.
shortleash run --budget 2.00 --max-spawns 10 -- \
claude -p "refactor the auth module"
# All guardrails:
shortleash run --budget 5.00 --max-spawns 10 --max-tokens 500000 \
--timeout 600 --receipt ./receipt.json -- \
claude -p "migrate the database layer"
Anomaly mode (the smart default)
Static caps punish legitimate big tasks. Anomaly mode learns instead:
every receipt is appended to ~/.shortleash/history.jsonl, and once an
agent has 5+ past runs, the guard trips only when the live run exceeds
3× the historical median cost or tokens for that agent.
shortleash run -- claude -p "migrate the database layer"
# [shortleash] anomaly=3.0x (needs 5 runs)
# ...
# STOPPED: anomaly: estimated $4.21 is 5.3x the typical $0.79 across 23 past runs
A naturally large task establishes its own baseline over time; only true
runaways trip. Breached runs are excluded from baselines. Tune with
--anomaly 5.0, --min-history 10, --profile big-refactor (separate
baseline), or --no-anomaly to disable.
Fleet mode (--fleet)
Running several agents on the same machine? --fleet holds a fleet
claim on paths for the run — a lockfile for agents, merged from the
retired claimlock:
shortleash run --fleet --budget 3 -- claude -p "refactor the api layer"
# [shortleash] fleet claim ce63a732a87b: /home/dan/project
- Default claim: the current directory; override with
--claim /path/one /path/two. - Conflict = warning, never a block. If another agent holds an overlapping path, you get a loud stderr warning and the run proceeds anyway — a lock must never be a deadlock.
- Released automatically on exit (normal, signal, or crash via the TTL
backstop). Tune with
--fleet-owner "dan"and--fleet-ttl 3600.
Prompt cache optimizer
Stop the bleed and shrink the bill. Prompt caching discounts repeated prefix tokens (up to 90% off) — but only when stable content sits first and per-request variables sit last:
shortleash cache guide # provider docs + the core rule
shortleash cache check -i prompt.txt # how cacheable is this prompt?
shortleash cache make -i prompt.txt -o prompt.cached.txt --provider anthropic
cache make restructures your {{variable}}-templated prompt into
cache-optimal layout and emits integration snippets for Anthropic (explicit
cache_control breakpoints), OpenAI/DeepSeek (automatic prefix caching),
and Gemini (explicit context caching).
When wrapping claude, --output-format stream-json --verbose are added
automatically (the guard needs the telemetry stream). The agent's stdout
passes through untouched, so you still watch it work.
On breach the whole process tree gets SIGTERM, then SIGKILL after
--grace-period (default 5s). Exit code is 124 on a guard kill, otherwise
the child's exit code.
The receipt
Every run ends with a receipt on stderr and optionally as JSON
(--receipt path.json):
── shortleash receipt ──────────────────────
cost: $0.1271 (estimated)
tokens: 51,000 (in=15,000 out=4,500 cache_r=30,000 cache_w=1,500)
turns: 0 spawns: 1
duration: 0.16s exit: 124
STOPPED: budget exceeded: estimated $0.1271 > $0.05 cap
──────────────────────────────────────────────────
Mid-run cost is estimated from a fallback pricing table (clearly marked);
the final receipt prefers the agent-reported total_cost_usd when the run
completes. Override pricing with --pricing pricing.json:
{"sonnet": [3.0, 15.0], "opus": [15.0, 75.0], "haiku": [0.8, 4.0]}
How it works
- Child runs in its own process group (
start_new_session=True) — a kill takes the whole tree, never your shell. - A monitor thread parses each stream-json line (
system/init,assistantusage +Tasktool_use spawns,resultcost) and checks caps after every observation. - A watchdog thread enforces
--timeouteven if the child goes silent. - Subagent spawns are counted from
Tasktool_use blocks in the stream.
Trust boundary
Be honest about what the guard can and can't see:
- Token, budget, spawn, and anomaly caps all depend on the child
agent's own telemetry. Token counts come from its stream-json
usageevents; mid-run cost is estimated from those counts; the final receipt prefers the child's reportedtotal_cost_usd. - A misbehaving or compromised child that under-reports (or reports negative) usage can blind those caps. The guard clamps every child-reported usage and cost value to ≥ 0 so negatives can't drive totals down, but it cannot see spend the child simply hides.
- Only
--timeoutis enforced independently of the child — a wall-clock watchdog, immune to telemetry lies.
Treat this as a guardrail against runaway cost, not a security boundary against an adversarial agent.
Testing
python3 -m unittest discover -s tests -v
sim/runaway.py is a fake agent emitting realistic stream-json — the suite
kills it for real (budget, spawn, token, and timeout breaches) without
spending a cent.
Scope / limits (v0.1.0)
- Claude Code
-p(non-interactive) is the supported path. Interactive sessions and other agents (Codex, Cursor) are on the roadmap — the event parser is isolated inevents.pyfor exactly this. - Cost mid-run is an estimate; treat
--budgetas a guardrail, not accounting.
Metadata
Release files for shortleash 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| shortleash-0.1.0.tar.gz | 31.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| shortleash-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 54.4 kB
Release files / shortleash-0.1.0.tar.gz
| Download URL | shortleash-0.1.0.tar.gz |
|---|---|
| Size | 31.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5199a9ebf85a545021dd0b4a7e6d13e4bde3220b1b5784e0f9c256f2b1cfbd41
|
|
BLAKE2b-256 checksum How to use checksums |
9b62676f5264eaf72a138306b40f337095ccb847551c9abdea671463c61d0290
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / shortleash-0.1.0-py3-none-any.whl
| Download URL | shortleash-0.1.0-py3-none-any.whl |
|---|---|
| Size | 23.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
97f343d847b221ac54d00af90668c5d4e557e6639302dbd0a014fdb58f1cd956
|
|
BLAKE2b-256 checksum How to use checksums |
34b0feeee3b0544009c33709cc50f50c631cb511cc13a5176d8429520747f49d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Security for shortleash 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.