---
slug: "agent-halter"
source_type: "readme"
source_url: "https://cdn.jsdelivr.net/gh/fgrehm/agent-halter@main/README.md"
repo: "https://github.com/fgrehm/agent-halter"
source_file: "README.md"
branch: "main"
---
<p align="center"><img src="logo.png" width="128" alt="agent-halter logo" /></p>

# agent-halter

A session-budget extension for AI coding agents. Set a wall-clock time, a turn count, or both. The agent gets a soft nudge as the budget approaches and stops responding for the current turn when it runs out. Budget state survives reload.

Today this ships as a [pi-coding-agent](https://github.com/badlogic/pi-mono) extension. A Claude Code adapter is planned next; see [FUTURE-CLAUDE.md](https://github.com/fgrehm/agent-halter/blob/HEAD/FUTURE-CLAUDE.md).

## Status

v0.0.1: Core logic is fully tested (100% coverage) and has been tested against a few pi sessions. It's also worth noting that some models might simply ignore the "time to wrap up nudges" sent by the extension, something I'm still investigating.

## Installation

### Option 1: from npm (recommended)

```bash
pi install npm:agent-halter
```

This installs the latest published version from npm. Run `pi update` to get new versions.

### Option 2: from git

```bash
pi install git:github.com/fgrehm/agent-halter
```

This clones the repo to `~/.pi/agent/git/` and adds it to your settings.

For project-local install (stored in `.pi/git/`):

```bash
pi install git:github.com/fgrehm/agent-halter --local
```

### Option 3: `-e` flag (try without installing)

```bash
pi -e npm:agent-halter
```

### Option 4: Clone manually (if you want to make changes and "try it live")

Pi auto-discovers subdirectories under `~/.pi/agent/extensions/`:

```bash
git clone git@github.com:fgrehm/agent-halter.git ~/.pi/agent/extensions/agent-halter
```

## Usage (pi)

```sh
# from inside a pi session
/timebox 15m                            # 15-minute budget
/timebox turns:5                        # 5-turn budget
/timebox 15m turns:3                    # combined; whichever fires first wins
/timebox 15m -- lmk -m "Timebox done"   # also run a shell command on hard stop
/timebox status                         # show remaining budget
/timebox off                            # disable
```

Anything after `--` is the literal command. It runs detached via `/bin/sh -c`, so quoting, pipes, and env vars work as in your shell. It fires once on hard stop (not on the soft nudge or on `/timebox off`). [lmk](https://github.com/fgrehm/lmk) is a good fit since it pops a blocking desktop dialog. For tests / CI you can neutralize lmk with `LMK_DRY_RUN=1`.

While active, a status-bar entry under the `timebox` key updates every second:

```
Timebox: 12m 34s left (15m budget) | 3 turns left (2/5)
```

At 80% of either dimension the user gets a one-shot warning notification, and every subsequent agent turn has a budget-warning block appended to the system prompt. At 100%, the next agent turn is blocked with an error notification. The chat continues; the user can set a new timebox or keep talking.

For the full behavior contract, see [SPEC.md](https://github.com/fgrehm/agent-halter/blob/HEAD/SPEC.md).

## Project layout

```
src/
  core/               host-agnostic logic
    parse.ts          /timebox argument parsing
    decide.ts         budget decisions (ok / nudge / stop)
    format.ts         status string + system-prompt suffix
    persist.ts        record-based restore on reload
    types.ts          shared types
  adapters/
    pi/               pi-coding-agent adapter
      index.ts        default-export factory
      controller.ts   in-memory budget state
      command.ts      /timebox handler
      events.ts       session_start / turn_start / before_agent_start / session_shutdown
      entries.ts      custom session-entry helpers
      status-timer.ts setInterval-based status refresher
tests/
  core/               unit tests for pure helpers
  adapters/pi/        integration tests with a fake ExtensionAPI
SPEC.md               full behavioral spec
FUTURE-CLAUDE.md      Claude Code adapter sketch
```

## Development

```sh
npm install
npm test                # vitest
npm run test:coverage   # v8 coverage with thresholds
npm run check           # biome --write + tsc --noEmit
npm run lint            # biome (no writes)
```

The host runtime, the network, and any LLM provider are fully faked. The test suite is hermetic and runs in under a second.

## Roadmap

- [x] pi adapter
- [ ] polish + make it work properly for less capable models
- [ ] Claude Code adapter (see [FUTURE-CLAUDE.md](https://github.com/fgrehm/agent-halter/blob/HEAD/FUTURE-CLAUDE.md)).

## License

MIT. Copyright (c) Fabio Rehm.
