@agentguard-run/burn 0.2.3 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +30 -2
  2. package/README.md +105 -20
  3. package/dist/src/adapters/codex.js +11 -1
  4. package/dist/src/adapters/cursor.js +2 -2
  5. package/dist/src/calibrate.js +2 -3
  6. package/dist/src/cli.js +53 -15
  7. package/dist/src/conformance.d.ts +5 -2
  8. package/dist/src/conformance.js +30 -17
  9. package/dist/src/defaults.d.ts +7 -4
  10. package/dist/src/defaults.js +9 -6
  11. package/dist/src/detectors/evaluate.d.ts +4 -5
  12. package/dist/src/detectors/evaluate.js +13 -11
  13. package/dist/src/eligibility.d.ts +17 -0
  14. package/dist/src/eligibility.js +29 -0
  15. package/dist/src/gateway.d.ts +2 -0
  16. package/dist/src/gateway.js +4 -8
  17. package/dist/src/history/claude-transcript.d.ts +20 -2
  18. package/dist/src/history/claude-transcript.js +56 -15
  19. package/dist/src/hook/pre-tool-use.d.ts +13 -9
  20. package/dist/src/hook/pre-tool-use.js +63 -31
  21. package/dist/src/insights/attribution.d.ts +4 -0
  22. package/dist/src/insights/attribution.js +151 -0
  23. package/dist/src/insights/blocks.d.ts +61 -0
  24. package/dist/src/insights/blocks.js +243 -0
  25. package/dist/src/insights/live.d.ts +53 -0
  26. package/dist/src/insights/live.js +211 -0
  27. package/dist/src/insights/pace.d.ts +34 -0
  28. package/dist/src/insights/pace.js +54 -0
  29. package/dist/src/insights/pricing.d.ts +48 -0
  30. package/dist/src/insights/pricing.js +139 -0
  31. package/dist/src/insights/render.d.ts +8 -0
  32. package/dist/src/insights/render.js +126 -0
  33. package/dist/src/insights/sessions.d.ts +12 -0
  34. package/dist/src/insights/sessions.js +51 -0
  35. package/dist/src/insights/transcript.d.ts +14 -0
  36. package/dist/src/insights/transcript.js +505 -0
  37. package/dist/src/insights/types.d.ts +164 -0
  38. package/dist/src/insights/types.js +4 -0
  39. package/dist/src/install.js +14 -5
  40. package/dist/src/policy.d.ts +4 -0
  41. package/dist/src/policy.js +57 -0
  42. package/dist/src/replay/render.js +4 -2
  43. package/dist/src/replay/simulate.d.ts +5 -0
  44. package/dist/src/replay/simulate.js +23 -8
  45. package/dist/src/state/reservations.d.ts +7 -5
  46. package/dist/src/state/reservations.js +60 -45
  47. package/dist/src/state/spawn-window.d.ts +10 -0
  48. package/dist/src/state/spawn-window.js +25 -0
  49. package/dist/src/types.d.ts +8 -1
  50. package/docs/USAGE_AND_PRICING.md +132 -0
  51. package/fixtures/usage-dedup-session/subagents/agent-synthetic-first.jsonl +5 -0
  52. package/fixtures/usage-dedup-session/subagents/agent-synthetic-second.jsonl +4 -0
  53. package/fixtures/usage-dedup-session.jsonl +4 -0
  54. package/package.json +4 -3
@@ -0,0 +1,132 @@
1
+ # Understand local usage
2
+
3
+ Burn reads the usage metadata already saved by your coding agent. It separates fresh input, cache writes, cache reads and output. The dollar estimate is the equivalent cost at the recorded API list rates. It is not a subscription bill, an account allowance or a conversion from tokens into your plan's limits.
4
+
5
+ The observer and reports run locally without network requests. Reports contain usage counts, model identifiers, timestamps and explanations supported by metadata. They do not copy prompt text, file contents, tool arguments or model output into the usage report.
6
+
7
+ ## Commands
8
+
9
+ ```sh
10
+ agentguard-burn why
11
+ agentguard-burn why SESSION_ID json
12
+ agentguard-burn rewrites all
13
+ agentguard-burn pace SESSION_ID
14
+ agentguard-burn statusline SESSION_ID
15
+ agentguard-burn pricing
16
+ ```
17
+
18
+ `why` breaks down a session's recorded usage. `rewrites` lists large prefix writes and the evidence available for their causes. `pace` and `statusline` show a short local summary. The optional final `json` argument on `why` and `rewrites` makes the report suitable for another local program.
19
+
20
+ ## Attribution method in 0.2.5
21
+
22
+ The first distinct assistant response establishes its session's fixed-prefix baseline from input, cache creation and cache read tokens. The instruction stack + system row uses that initial measured context and the corresponding prefix portion of later cache reads. This baseline includes the initial user message. A rewrite classified as prefix change resets it to that response's context, which can include existing history. The label is a baseline attribution rule, not a claim that instruction files alone contain that many tokens.
23
+
24
+ History re-sent is each response's cache reads above the fixed prefix, floored at zero. The arriving increment is fresh input plus cache creation after subtracting the previous response's output. Intervening user messages receive conversation tokens. Tool results receive tool output tokens, except a recorded Read result for a previously read path receives re-read file tokens. When several results or user messages share an increment, their recorded UTF-8 byte sizes determine proportions of the measured token total. Integer rounding uses largest remainders so no token disappears. Mixed user and tool intervals are marked shared and counted in the footer.
25
+
26
+ A qualifying full-prefix rewrite retains its measured cache-creation tokens and is excluded from event-increment attribution. Explicit child transcripts retain their existing fan-out attribution after rewrites. Generated output is counted once. To conserve all recorded usage, the input tokens reserved by previous-output subtraction remain in the unattributed residual, together with intervals without usable event evidence. A single event category can own an increment even when its payload is empty; mixed empty payloads have no measurable byte proportions and remain residual.
27
+
28
+ Repeated usage snapshots for one provider response keep the original interval rather than consuming later events again. Read paths, tool IDs and event identities are hashed in local state. Only byte counts and metadata survive parsing, never text. Shell commands that happen to read a file remain tool output unless the host records a Read call. Existing explicit host token-category instrumentation takes precedence where available.
29
+
30
+ The table's Method footer has one sentence per bucket. Zero now means a computed zero rather than a missing measurement. Old pace caches without interval metadata are rebuilt once from the local transcript. When auditing a date range, establish baselines from complete session histories before selecting the responses in that range.
31
+
32
+ ## Cache writes and long sessions
33
+
34
+ For Claude, `cache_creation_input_tokens` is the total written. When the transcript also includes `cache_creation.ephemeral_5m_input_tokens` and `cache_creation.ephemeral_1h_input_tokens`, Burn uses that split. If the lifetime is unavailable, Burn returns a range between the two published write rates. A long pause alone does not establish the requested cache lifetime. See the [provider's cache usage fields](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#1-hour-cache-duration).
35
+
36
+ A large rewrite after a recorded compaction, a first subagent turn, a recorded prefix change or an idle interval can explain expensive turns. These are evidence labels, not proof that one cause was solely responsible. When metadata does not identify a cause or lifetime, the report says so.
37
+
38
+ For `claude-fable-5-1`, 500,000 cache-read tokens have a $0.125 API equivalent. Writing that many tokens costs $6.25 with a five-minute lifetime or $10 with a one-hour lifetime. At the same verified cache-read rate, 1.69 billion reads cost $422.50. Multiplying all token categories by the fresh-input rate would give the wrong result.
39
+
40
+ ## Pricing sources and overrides
41
+
42
+ Every bundled row includes its exact model identifier, `sourceUrl` and `verifiedAt`. The rates were checked on 2026-09-17. The runtime never downloads pricing. Run `agentguard-burn pricing` to inspect the installed table.
43
+
44
+ The Claude rows use the [official model price list](https://platform.claude.com/docs/en/about-claude/pricing). Amounts below are USD per million tokens in the order fresh input, cache read, five-minute write, one-hour write, output:
45
+
46
+ * `claude-fable-5-1`: 10, 0.25, 12.5, 20, 50.
47
+ * `claude-fable-5`: 10, 1, 12.5, 20, 50.
48
+ * `claude-opus-5` and `claude-opus-4-8`: 5, 0.5, 6.25, 10, 25.
49
+ * `claude-sonnet-5`: 2, 0.2, 2.5, 4, 10.
50
+ * `claude-haiku-4-5-20251001`: 1, 0.1, 1.25, 2, 5.
51
+
52
+ The [OpenAI API price list](https://developers.openai.com/api/docs/pricing) supplies these standard rates in the order fresh input, cache read, cache write, output:
53
+
54
+ * `gpt-6-astra`: 10, 1, 12.5, 50.
55
+ * `gpt-5.6-sol`: 4, 0.4, 5, 20. These are the published promotional rates on the verification date.
56
+ * `gpt-5.6-luna`: 0.2, 0.02, 0.25, 1.2.
57
+
58
+ These OpenAI rows have one published cache-write price rather than separate lifetime prices. For requests with more than 272,000 input tokens, Burn applies the documented API multiplier of two to fresh input, reads and writes, and 1.5 to output. It uses the request's reported input counts, not the model's advertised context capacity.
59
+
60
+ The [GPT-5.4 Mini model page](https://developers.openai.com/api/docs/models/gpt-5.4-mini) supplies 0.75 input, 0.075 cached input and 4.5 output. Its separate cache-write rate is unverified and remains `null`. Unknown model identifiers, including unlisted aliases, keep their token counts but have no dollar estimate. Burn does not infer prices from a model family name.
61
+
62
+ **Codex charges can differ from the API equivalent.** OpenAI's [Enterprise rate card](https://help.openai.com/en/articles/20001415) says that Codex does not charge for cache writes and that Astra in Codex does not incur the API long-context surcharge. Its other billing rules and included subscription allowances also differ. Burn's default table intentionally describes API list prices, not what a Codex subscriber owes. Fast processing, regional premiums, batch discounts, tools, taxes and negotiated rates are outside this standard estimate.
63
+
64
+ To use different rates, create a local JSON file and set `AGENTGUARD_BURN_PRICING_FILE` to its path. You can also set `insights.pricingFile` in the Burn policy. Otherwise Burn checks `burn-pricing.json` under `AGENTGUARD_HOME`, or the normal Burn home when that variable is absent. Listed models replace the matching bundled row; other bundled rows remain available.
65
+
66
+ ```json
67
+ {
68
+ "models": {
69
+ "example-model": {
70
+ "input": 2,
71
+ "cacheRead": 0.2,
72
+ "cacheWrite5m": null,
73
+ "cacheWrite1h": null,
74
+ "output": 8,
75
+ "sourceUrl": "https://example.com/your-agreed-rates",
76
+ "verifiedAt": "2026-09-17"
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ These example prices are placeholders. Use a source that establishes your actual rates. Every category must be a nonnegative number or `null`; `null` means unknown, and zero means a verified zero price. If a turn has tokens in an unknown category, its full dollar estimate remains unavailable. Partial known categories remain visible in the pricing result.
83
+
84
+ For a single write rate, set equal `cacheWrite5m` and `cacheWrite1h` values and add `"cacheWriteTtl": "not_tiered"`. An optional `longContext` object takes `aboveInputTokens`, `inputMultiplier` and `outputMultiplier`. An override without that object has no automatic context surcharge. This lets an operator express a verified contract without inheriting an incompatible API rule.
85
+
86
+ ## What the hosts expose
87
+
88
+ Claude Code supplies `session_id` and `transcript_path` to hooks. Its transcript is written asynchronously, so a hook may observe usage a little after the corresponding response. The hook schema does not promise account rate-limit percentages. See [Claude Code hook input](https://code.claude.com/docs/en/hooks#common-input-fields).
89
+
90
+ The [Claude Code statusline contract](https://code.claude.com/docs/en/statusline) separately exposes `rate_limits.five_hour.used_percentage`, `rate_limits.seven_day.used_percentage` and each window's `resets_at` in Unix seconds. A window may be absent, and expired windows are removed. `context_window.used_percentage` measures current context occupancy; it is not an account-limit percentage. A custom statusline can invoke Burn with the JSON supplied on stdin:
91
+
92
+ ```json
93
+ {
94
+ "statusLine": {
95
+ "type": "command",
96
+ "command": "agentguard-burn statusline"
97
+ }
98
+ }
99
+ ```
100
+
101
+ This is an example for operator review. Running the reporting commands does not install this setting. If another statusline is already configured, combine its output with Burn in your existing script instead of replacing it.
102
+
103
+ In Codex 0.154.0, transcript `token_count` records can contain `rate_limits.primary` and `rate_limits.secondary`. Each window uses `used_percent`, optional `window_minutes` and optional `resets_at` in Unix seconds. Primary and secondary names alone do not establish a five-hour or weekly duration. The released [protocol definitions](https://github.com/openai/codex/blob/rust-v0.154.0/codex-rs/protocol/src/protocol.rs#L2318) establish these fields. The hook contract supplies a transcript path, not a separate promise of quota fields.
104
+
105
+ Codex input totals include cache reads and writes. Burn separates them before pricing fresh input. Reasoning output is already part of total output and is not added again. The released [usage decoder test](https://github.com/openai/codex/blob/rust-v0.154.0/codex-rs/codex-api/src/sse/responses.rs#L898) demonstrates an input total of 100 consisting of 40 reads and 60 writes, plus 10 output tokens that include five reasoning tokens.
106
+
107
+ Codex 0.154.0 supports a fixed list of built-in footer items, not an external status command. Its [released statusline implementation](https://github.com/openai/codex/blob/rust-v0.154.0/codex-rs/tui/src/bottom_pane/status_line_setup.rs#L56) and [configuration reference](https://learn.chatgpt.com/docs/config-file/config-reference#tuistatus_line) document that boundary. Use its native limit indicators and run the one-line Burn companion in a terminal:
108
+
109
+ ```toml
110
+ [tui]
111
+ status_line = ["model", "context-remaining", "five-hour-limit", "weekly-limit"]
112
+ ```
113
+
114
+ ```sh
115
+ agentguard-burn statusline SESSION_ID
116
+ ```
117
+
118
+ ## Forecast limits without inventing a quota
119
+
120
+ The last ten minutes of recorded usage establish cached and uncached token pace and a next-hour projection. An account-limit forecast requires multiple explicit percentage observations from the same unexpired host window. It follows the observed change in percentage over time. Neither a token maximum nor a cache discount is inferred from subscription price, context size or API cost.
121
+
122
+ When snapshots are missing, stale, unchanged, reset or otherwise insufficient, the line reports an unknown limit with a reason. A forecast reflects recent activity only; other sessions or a change in workload can change the result. The host's own usage screen remains the authority for the remaining allowance.
123
+
124
+ ## Unknown inherited history
125
+
126
+ Some older Codex fork transcripts copy a parent's usage without an authoritative boundary. Burn excludes that inherited history and reports usage unavailable rather than charging the copy again. Reports across sessions identify how many histories were excluded. Those totals are incomplete. Newer transcripts with an explicit inherited-history ordinal or a child-owned thread settings event can separate the copied prefix from new usage.
127
+
128
+ ## Reservation lock recovery
129
+
130
+ The reservation lock waits up to eight seconds. A process that fails closed because it could not acquire that lock does not contribute to the decision count or the seven-day age required for enforcement eligibility. Other old ledger rows keep their existing interpretation.
131
+
132
+ Retiring an abandoned lock is serialized before renaming it, so a stale observer cannot temporarily move a live replacement lock. If a process crashes during the retirement claim itself, the conservative result can be continued fail-closed lock waits. A directory without a valid owner record also requires manual recovery: its creator might be paused before publishing ownership, and directory age cannot prove that process is dead. Recovery requires stopping all clients using that Burn home, confirming that every PID recorded in `burn.lock/owner` and `burn.lock/retiring/owner` is dead, moving that abandoned lock directory aside, then restarting the clients. If either owner record is absent or unreadable, keep every client stopped during recovery. Do not remove a lock while any of those clients is running.
@@ -0,0 +1,5 @@
1
+ {"type":"fork-context-ref"}
2
+ {"type":"assistant","timestamp":"2026-09-01T00:00:02Z","uuid":"synthetic-root-block-3","message":{"id":"synthetic-response-a","model":"claude-sonnet-5","usage":{"input_tokens":10,"cache_creation_input_tokens":20,"cache_read_input_tokens":100,"output_tokens":9},"content":[]}}
3
+ {"type":"assistant","timestamp":"2026-09-01T00:00:03Z","uuid":"synthetic-root-spawn","message":{"id":"synthetic-response-b","model":"claude-sonnet-5","usage":{"input_tokens":11,"cache_read_input_tokens":130,"output_tokens":2},"content":[{"type":"tool_use","id":"synthetic-spawn-1","name":"Agent","input":{"description":"synthetic task"}}]}}
4
+ {"type":"assistant","timestamp":"2026-09-01T00:00:04Z","uuid":"synthetic-child-block-1","message":{"id":"synthetic-response-c","model":"claude-sonnet-5","usage":{"input_tokens":5,"cache_creation_input_tokens":30,"cache_read_input_tokens":150,"output_tokens":10},"content":[]}}
5
+ {"type":"assistant","timestamp":"2026-09-01T00:00:05Z","uuid":"synthetic-child-block-2","message":{"id":"synthetic-response-c","model":"claude-sonnet-5","usage":{"input_tokens":5,"cache_creation_input_tokens":30,"cache_read_input_tokens":150,"output_tokens":12},"content":[]}}
@@ -0,0 +1,4 @@
1
+ {"type":"fork-context-ref"}
2
+ {"type":"assistant","timestamp":"2026-09-01T00:00:02Z","uuid":"synthetic-root-block-3","message":{"id":"synthetic-response-a","model":"claude-sonnet-5","usage":{"input_tokens":10,"cache_creation_input_tokens":20,"cache_read_input_tokens":100,"output_tokens":9},"content":[]}}
3
+ {"type":"assistant","timestamp":"2026-09-01T00:00:05Z","uuid":"synthetic-copy-c","message":{"id":"synthetic-response-c","model":"claude-sonnet-5","usage":{"input_tokens":5,"cache_creation_input_tokens":30,"cache_read_input_tokens":150,"output_tokens":12},"content":[]}}
4
+ {"type":"assistant","timestamp":"2026-09-01T00:00:06Z","uuid":"synthetic-child-d","message":{"id":"synthetic-response-d","model":"claude-sonnet-5","usage":{"input_tokens":2,"cache_read_input_tokens":200,"output_tokens":3},"content":[]}}
@@ -0,0 +1,4 @@
1
+ {"type":"assistant","timestamp":"2026-09-01T00:00:00Z","uuid":"synthetic-root-block-1","message":{"id":"synthetic-response-a","model":"claude-sonnet-5","usage":{"input_tokens":10,"cache_creation_input_tokens":20,"cache_read_input_tokens":100,"output_tokens":5},"content":[]}}
2
+ {"type":"assistant","timestamp":"2026-09-01T00:00:01Z","uuid":"synthetic-root-block-2","message":{"id":"synthetic-response-a","model":"claude-sonnet-5","usage":{"input_tokens":10,"cache_creation_input_tokens":20,"cache_read_input_tokens":100,"output_tokens":5},"content":[]}}
3
+ {"type":"assistant","timestamp":"2026-09-01T00:00:02Z","uuid":"synthetic-root-block-3","message":{"id":"synthetic-response-a","model":"claude-sonnet-5","usage":{"input_tokens":10,"cache_creation_input_tokens":20,"cache_read_input_tokens":100,"output_tokens":9},"content":[]}}
4
+ {"type":"assistant","timestamp":"2026-09-01T00:00:03Z","uuid":"synthetic-root-spawn","message":{"id":"synthetic-response-b","model":"claude-sonnet-5","usage":{"input_tokens":11,"cache_read_input_tokens":130,"output_tokens":2},"content":[{"type":"tool_use","id":"synthetic-spawn-1","name":"Agent","input":{"description":"synthetic task"}}]}}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agentguard-run/burn",
3
- "version": "0.2.3",
4
- "description": "Local runaway-agent circuit breaker for AI coding agents and local model runtimes. One policy across Claude Code, Cursor, Codex, Ollama, vLLM, LM Studio and raw orchestrators: detects fan-out storms and sustained token burn, blocks the next spawn, and proves what happened with content-free signed receipts. Nothing leaves the machine.",
3
+ "version": "0.2.6",
4
+ "description": "Local session usage and runaway-agent circuit breaker. Explain tokens, cache rewrites and API list cost, track pace, warn on heavy turns, and gate agent fan-out with signed receipts. Nothing leaves the machine.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "commonjs",
7
7
  "main": "dist/src/index.js",
@@ -37,7 +37,8 @@
37
37
  "fixtures",
38
38
  "README.md",
39
39
  "CHANGELOG.md",
40
- "LICENSE"
40
+ "LICENSE",
41
+ "docs"
41
42
  ],
42
43
  "engines": {
43
44
  "node": ">=20.0.0"