← Back to PRs

#17449: feat(extension): add cost-guard — budget enforcement and cost alerts

by miloudbelarebia open 2026-02-15 19:27 View on GitHub →
size: L
## Summary - **New extension** `cost-guard` that monitors API costs in real time and enforces budget limits - Listens to `model.usage` diagnostic events via `onDiagnosticEvent()` (same pattern as `diagnostics-otel`) - In-memory tracker accumulates spend per provider, per day/month - `before_agent_start` hook injects budget warnings into agent context when approaching limits - `message_sending` hook blocks responses when budget exceeded (configurable hard stop) - `/cost` slash command shows current spend and budget status - Configurable: daily/monthly budgets, warning threshold, per-provider caps ## Motivation Multiple community members have reported unexpected high API costs: - $100/month on Gemini API - $40 in a single morning on OpenAI The codebase has excellent cost tracking infrastructure (diagnostic events, session-cost-usage) but **zero enforcement** — costs are calculated and emitted but never checked against any budget. This extension fills that gap by adding a lightweight enforcement layer via the existing plugin system. ## Configuration ```yaml # openclaw.yaml plugins: cost-guard: dailyBudgetUsd: 5.0 # Default: $5/day monthlyBudgetUsd: 50.0 # Default: $50/month warningThreshold: 0.8 # Alert at 80% of budget hardStop: true # Block responses when exceeded providerLimits: # Optional per-provider caps anthropic: dailyUsd: 3.0 openai: dailyUsd: 2.0 ``` ## Files | File | Purpose | |------|---------| | `extensions/cost-guard/index.ts` | Plugin entry point — registers service, hooks, command | | `extensions/cost-guard/package.json` | Package metadata | | `extensions/cost-guard/src/config.ts` | Config schema with validation and UI hints | | `extensions/cost-guard/src/tracker.ts` | In-memory cost accumulator with budget checking | | `extensions/cost-guard/src/service.ts` | Service subscribing to diagnostic events | | `extensions/cost-guard/src/format.ts` | USD formatting and summary display | ## Change Type (select all) - [ ] Bug fix - [x] Feature - [ ] Refactor - [ ] Docs - [ ] Security hardening - [ ] Chore/infra ## Scope (select all touched areas) - [ ] Gateway / orchestration - [ ] Skills / tool execution - [ ] Auth / tokens - [ ] Memory / storage - [x] Integrations - [ ] API / contracts - [ ] UI / DX - [ ] CI/CD / infra ## Linked Issue/PR - Related #12299 (programmatic cost access) - Related #8485 (token/cost display) - Related #14779 (plugin hooks for cost management) - Related #9812 (agent-level rate limiting) ## User-visible / Behavior Changes New `/cost` command available when extension is enabled. Budget warnings injected into agent context. Optional hard stop when budget exceeded. ## Security Impact No security impact. Extension only reads diagnostic events (read-only) and uses standard plugin hooks. No new API keys, no external network calls, no file system writes. ## Repro + Verification 1. Enable the `cost-guard` extension in config 2. Send messages that trigger API calls 3. Use `/cost` to verify spend tracking 4. Observe budget warnings in logs at 80% threshold 5. Verify hard stop blocks responses when budget exceeded ### Environment - OS: macOS (Apple Silicon) - Runtime: Node.js v22.22.0 - TypeScript: passes `tsc --noEmit` - Formatting: passes `oxfmt --check` ## Human Verification (required) - Verified TypeScript compilation passes with zero errors - Verified oxfmt formatting passes - Reviewed all plugin-sdk imports match exported members - Cross-referenced hook signatures against `src/plugins/types.ts` - Followed `diagnostics-otel` pattern for service + event subscription - Followed `memory-lancedb` pattern for config schema + hooks + commands ## Risks and Mitigations - **In-memory tracker resets on restart**: Acceptable since historical costs are already stored in `session-cost-usage.ts`. The tracker only needs real-time data for enforcement. - **costUsd may be undefined**: Gracefully handled — entries with missing cost are ignored. ## Test plan - [ ] TypeScript compilation passes - [ ] oxfmt formatting passes - [ ] CI checks pass - [ ] Extension loads without errors - [ ] `/cost` command returns formatted output - [ ] Budget warnings appear in logs at threshold - [ ] Hard stop blocks messages when exceeded ## Local Validation - `pnpm check` (tsgo): ✅ passes - `pnpm test`: ✅ passes (including plugin validation tests) - Formatting: verified with `oxfmt --check` - CI: all checks pass ✅ ## Scope New extension at `extensions/cost-guard/` — 7 files, self-contained, no modifications to core codebase. ## AI Assistance AI-assisted (Claude Code) for codebase exploration, pattern discovery, and drafting. The architecture decisions (diagnostic event subscription, budget enforcement via hooks), implementation, and Greptile review fixes are my own work. Testing level: Fully tested — all CI checks pass (check, bun tests, node tests, windows tests). <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR adds a new `cost-guard` extension that provides real-time API cost monitoring and budget enforcement. The extension subscribes to `model.usage` diagnostic events, tracks spending in-memory per provider/day/month, and uses lifecycle hooks to inject warnings and block responses when budgets are exceeded. **Key changes:** - New extension at `extensions/cost-guard/` with 7 files (index, service, tracker, config, format, package.json, plugin manifest) - Service subscribes to diagnostic events and accumulates costs via an in-memory tracker - `before_agent_start` hook injects budget warnings into agent context at configurable threshold (default 80%) - `message_sending` hook blocks responses when budget exceeded (optional hard stop) - `/cost` slash command shows current spend and budget status with provider breakdown - Configurable daily/monthly budgets, warning threshold, hard stop toggle, and per-provider limits - Follows established patterns from `diagnostics-otel` (event subscription) and `memory-lancedb` (config schema) **Implementation quality:** - Clean architecture with separation of concerns (tracker, service, config, formatting) - Proper TypeScript types throughout - Gracefully handles missing cost data - Periodic pruning (hourly) prevents unbounded memory growth - Timer properly calls `.unref()` to avoid blocking process shutdown (line 77 of service.ts) - Previous review feedback has been addressed in commits e03f62a4 and ee7f139e The extension is self-contained with no modifications to core codebase. All CI checks pass including TypeScript compilation, formatting, and tests. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with minimal risk - The extension is well-implemented, self-contained, addresses a real user need, follows established patterns, and all CI checks pass. Previous review feedback has been addressed. No security concerns, no core code modifications, and the implementation shows good practices (proper cleanup, type safety, error handling). - No files require special attention <sub>Last reviewed commit: e03f62a</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs