# @agentoom/pi-spending-guard

Track LLM spending per task and pause when a configurable threshold is reached for [pi](https://pi.dev) by [agentoom.com](https://agentoom.com).

When your spending limit is hit, spending-guard pauses the task and asks you to choose: continue (double the limit or reset the counter), refine the prompt, or stop entirely — so you never lose control of your API costs.

Beyond per-session limits, spending-guard now tracks **project-level** and **global** cumulative spending that persists across sessions and auto-updates on every cost received.

**New in 1.2:** Tool execution tracking (all extensions) + compaction tracking (summary LLM calls cost money).

**Fixed in 1.2.1:** Session accumulated cost no longer carries over across sessions, and tool tracking state is now persisted immediately (see [Changelog](#changelog)).

## Install

```bash
pi install npm:@agentoom/pi-spending-guard
```

## Quick start

Out of the box, spending-guard is **enabled** with a **$3.00 default limit**. Start pi and spend away — it'll pause automatically at $3.00.

Project, global, and tool cumulative tracking are also enabled by default.

## Usage

### Commands

| Command | Description |
|---------|-------------|
| `/spending` | Show current spending, all totals, tool stats, compactions, and enabled state |
| `/spending limit [amount]` | Set or view the session spending limit (e.g. `/spending limit 5`) |
| `/spending toggle [on\|off]` | Enable/disable session threshold tracking; no argument toggles |
| `/spending track project [on\|off]` | Enable/disable project-level cumulative tracking |
| `/spending track global [on\|off]` | Enable/disable global cumulative tracking |
| `/spending track tools [on\|off]` | Enable/disable tool execution tracking |
| `/spending tools` | Show per-tool execution stats (calls + nested costs) |
| `/spending project` | Show project-level cumulative total |
| `/spending global` | Show global cumulative total |
| `/spending reset [project\|global\|session\|tools\|meta]` | Reset a total (default: session counter) |
| `/spending-limit [amount]` | Shortcut: set or view the limit |
| `/spending-toggle [on\|off]` | Shortcut: enable/disable |
| `/spending-status` | Shortcut: show all status including tool stats + compactions |

### Threshold dialog

When accumulated cost reaches the configured limit, spending-guard pauses and presents a 4-option dialog:

| Option | Behaviour |
|--------|-----------|
| **Continue (double the limit)** | Raises the cap to 2× the current limit and keeps going |
| **Continue (reset counter)** | Keeps the same limit, resets accumulated cost to $0.00 |
| **Refine the prompt** | Opens an input field so you can write a more focused prompt |
| **Stop the task** | Gracefully shuts down pi |

### Cost display

While any tracking is enabled, current spending is shown live in the footer status bar:

```
💵 $1.23 / $3.00  📁 $10.50  🌍 $45.00  🗜️ 3c  🔧 17 calls $0.015
```

| Icon | Meaning |
|------|---------|
| 💵 | Current session spending / limit |
| 📁 | Project-level cumulative total (across all sessions in this project) |
| 🌍 | Global cumulative total (across all projects) |
| 🗜️ | Session compaction count (each compaction costs a summary LLM call) |
| 🔧 | Tool execution count + nested LLM costs (across all extensions) |

## What's tracked (comprehensive)

### Always tracked (costs flow through all totals)

| Source | Event | What's captured |
|--------|-------|-----------------|
| **Assistant messages** | `message_end` | `event.message.usage.cost.total` — the primary LLM cost |
| **Tool nested LLM calls** | `tool_result` | `event.usage.cost.total` — costs from LLM calls made inside tools (any extension) |
| **Compaction summaries** | `session_compact` | Compaction count (manual vs auto) — each compaction fires a summary LLM call whose cost flows through `message_end` |

### Always tracked (metadata, no direct cost)

| Source | Event | What's captured |
|--------|-------|-----------------|
| **Tool invocations** | `tool_execution_start` | Call count per tool name (any extension, built-in or third-party) |

### Not tracked (pi's event system doesn't expose this data)

| What | Why it's invisible |
|------|--------------------|
| **Direct HTTP calls from extensions** | If an extension calls `fetch("https://paid-api.com/...")` directly, bypassing pi's agent, no cost event is emitted. Pi has no network interception layer at the extension level. |
| **MCP server-side billing** | If an MCP server charges per call or per token, that cost is on the server side and never reaches pi's event stream. Only the LLM usage that invoked the MCP tool is visible. |
| **Extension background work** | File watchers, sockets, timers, periodic fetches started by extensions don't emit cost events. |
| **Provider-specific billing nuances** | Some providers charge differently for cache reads/writes, tool-use tokens, or thinking tokens. Pi only surfaces `cost.total` — if the provider doesn't report it, it's invisible. |
| **Retry costs as a separate category** | Retries generate additional LLM calls whose costs flow through `message_end` and are tracked, but there's no built-in event to label them as "retry" vs "initial attempt." |

> **Key limitation:** Everything that flows through `pi`'s agent (LLM calls + tool usage from those calls) is fully tracked. Only costs incurred outside pi's agent event pipe are invisible.

## Tool execution tracking

Spending-guard monitors every tool call made by the LLM — including tools registered by third-party extensions — and tracks:

- **Call count** per tool (incremented on `tool_execution_start`)
- **Nested LLM costs** incurred during tool runs (captured from `tool_result.usage`)

This means if a third-party extension's tool makes its own LLM calls through pi's agent, those costs are tracked and counted against your session, project, and global spending totals.

**What's tracked:**
- Built-in tools (`bash`, `read`, `write`, `edit`, `grep`, `find`, `ls`, etc.)
- Custom tools registered by any extension
- All third-party extension tools

View per-tool stats with `/spending tools`:

```
🔧 Tool Execution Stats
  bash: 14 calls
  read: 8 calls
  write: 4 calls
  edit: 3 calls
  my_search_tool: 2 calls $0.0032
  code_review: 1 call $0.0120
  ─────────────────────
  Total: 32 calls across 6 tools, $0.0152 nested cost
```

Enable/disable tracking with `/spending track tools on|off`. Reset per-session tool stats with `/spending reset tools`.

## Compaction tracking

Each session compaction fires a summary LLM call (costs money). Spending-guard tracks:

- **Total compactions** this session
- **Manual vs auto** breakdown (`/compact` vs threshold/overflow triggered)

The compaction count is shown in the footer (`🗜️ 3c`) and in `/spending status`. Reset with `/spending reset meta`.

> Compaction summary costs already flow through `message_end` and are counted in your totals. The compaction tracker just surfaces *how many* happened so you can spot runaway compaction loops.

## Project & global cumulative tracking

Beyond per-session limits, spending-guard persists cumulative totals to disk so you can see how much you've spent over time:

- **Project total** → `.pi/spending-guard-project-total.json` (one per project)
- **Global total** → `~/.pi/agent/spending-guard-global-total.json` (shared across all projects)

Both totals auto-update on every LLM cost received and persist across sessions, restarts, and reloads.

View them with `/spending project`, `/spending global`, or `/spending-status`. Reset them with `/spending reset project` or `/spending reset global`.

## Configuration

Spending-guard reads from these files (project takes priority):

1. `.pi/spending-guard.json` — project-local
2. `~/.pi/agent/spending-guard.json` — global

If neither file exists, it defaults to **enabled** with a **$3.00 limit**.

### Config format

```json
{
  "enabled": true,
  "limit": 5.00,
  "trackProject": true,
  "trackGlobal": true,
  "trackTools": true
}
```

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `enabled` | `boolean` | `true` | Whether session threshold tracking starts active |
| `limit` | `number` | `3.00` | Session spending threshold in USD |
| `trackProject` | `boolean` | `true` | Persist cumulative project-level total to disk |
| `trackGlobal` | `boolean` | `true` | Persist cumulative global total to disk |
| `trackTools` | `boolean` | `true` | Track tool executions (calls + nested LLM costs) |

### Per-project vs global

- Place the extension globally to track spending across all projects:
  ```bash
  pi install npm:@agentoom/pi-spending-guard
  ```
- Place a `.pi/spending-guard.json` in a specific project to override the default for that project only.

## Development

```
spending/
├── package.json
├── index.ts          # Extension entry point
├── .gitignore
└── README.md
```

Run against a local path:

```bash
pi install ./spending
```

## Changelog

### 1.2.1

- **Fix: session accumulated cost carryover.** The per-session `accumulatedCost` counter is now reset to zero on session start/restart, matching the behaviour of `toolSpending` and `sessionMeta`. Previously, when pi auto-resumed a session on startup, the previous session's accumulated cost was restored from entries and new spending was added on top, inflating the displayed session total.
- **Fix: tool execution state not persisted immediately.** `persist()` is now called in both `tool_execution_start` (call counts) and `tool_result` (nested costs). Previously, tool tracking data was only persisted when the next `message_end` fired — if no assistant message followed, tracked calls and nested costs were lost. The `tool_result` handler also now persists state even when the session guard is paused or disabled, so tool cost tracking works regardless of guard state.

### 1.2.0

- Tool execution tracking (all extensions): call counts + nested LLM costs.
- Compaction tracking (manual vs auto).
- Project-level and global cumulative spending totals persisted to disk.

### 1.0.x

- Initial release: per-session spending limit with threshold dialog.

## Links

- [agentoom.com](https://agentoom.com)
- [npm: @agentoom/pi-spending-guard](https://www.npmjs.com/package/@agentoom/pi-spending-guard)
