coding-agent-cost 0.1.0__tar.gz → 0.1.1__tar.gz

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 (29) hide show
  1. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/PKG-INFO +172 -22
  2. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/README.md +168 -21
  3. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/agent_cost/__init__.py +1 -1
  4. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/agent_cost/aggregate.py +8 -0
  5. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/agent_cost/rates.json +42 -2
  6. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/agent_cost/readers/codex.py +134 -3
  7. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/coding_agent_cost.egg-info/PKG-INFO +172 -22
  8. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/coding_agent_cost.egg-info/SOURCES.txt +1 -0
  9. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/pyproject.toml +11 -1
  10. coding_agent_cost-0.1.1/tests/test_astra_pricing.py +338 -0
  11. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/LICENSE +0 -0
  12. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/agent_cost/cli.py +0 -0
  13. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/agent_cost/config.py +0 -0
  14. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/agent_cost/facts.py +0 -0
  15. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/agent_cost/rates.py +0 -0
  16. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/agent_cost/readers/__init__.py +0 -0
  17. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/agent_cost/readers/claude.py +0 -0
  18. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/agent_cost/renderers.py +0 -0
  19. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/coding_agent_cost.egg-info/dependency_links.txt +0 -0
  20. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/coding_agent_cost.egg-info/entry_points.txt +0 -0
  21. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/coding_agent_cost.egg-info/top_level.txt +0 -0
  22. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/setup.cfg +0 -0
  23. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/tests/test_aggregate.py +0 -0
  24. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/tests/test_cli.py +0 -0
  25. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/tests/test_facts.py +0 -0
  26. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/tests/test_measure_v1_contract.py +0 -0
  27. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/tests/test_rates.py +0 -0
  28. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/tests/test_reader_claude.py +0 -0
  29. {coding_agent_cost-0.1.0 → coding_agent_cost-0.1.1}/tests/test_reader_codex.py +0 -0
@@ -1,10 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coding-agent-cost
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Estimate AI coding agent (Claude Code / Codex CLI) token usage and cost from local logs
5
5
  Author: shiki-yusuke
6
6
  License: MIT
7
7
  Project-URL: Homepage, https://github.com/shiki-yusuke/agent-cost
8
+ Project-URL: Repository, https://github.com/shiki-yusuke/agent-cost
9
+ Project-URL: Issues, https://github.com/shiki-yusuke/agent-cost/issues
10
+ Keywords: ai-agents,claude-code,codex-cli,cost-estimation,local-first,token-usage
8
11
  Classifier: Development Status :: 3 - Alpha
9
12
  Classifier: Environment :: Console
10
13
  Classifier: Intended Audience :: Developers
@@ -23,30 +26,159 @@ Dynamic: license-file
23
26
 
24
27
  # agent-cost
25
28
 
26
- > PyPI distribution: [`coding-agent-cost`](https://pypi.org/project/coding-agent-cost/) — the
27
- > command (`agent-cost`) and import name (`agent_cost`) are unchanged. The name `agent-cost`
28
- > itself is blocked by PyPI's name-similarity rule against `agentcost`, an unrelated project
29
- > by a different author; this tool is not affiliated with it.
30
-
31
- Estimate how many tokens Claude Code and Codex CLI actually used, and roughly
32
- what that cost, by reading the local logs those tools already write to your
29
+ See Claude Code and Codex CLI token usage and estimated costs from the logs already on your
33
30
  machine.
34
31
 
35
- ## Quick start
32
+ Keep pricing gaps visible: unknown models are marked `unpriced`, and cache-write estimates
33
+ with missing TTL information are marked `lower_bound`. JSON reports include the rate catalog
34
+ version and SHA-256 used for the calculation; the catalog records its pricing sources.
35
+
36
+ **These are estimates based on the selected rate catalog, not your actual bill.** The CLI makes
37
+ no runtime network calls and has zero runtime dependencies. On macOS and Linux with IANA
38
+ timezone data, no account, service, or project configuration is required. See the
39
+ [platform note](#report-and-export) for minimal Windows Python environments.
40
+
41
+ Need the result in another program? [`agent-cost measure`](#machine-consumption-agent-cost-measure)
42
+ returns versioned `measure/v1` JSON for session IDs you supply. Task and PR attribution remain
43
+ the caller's responsibility.
44
+
45
+ [日本語サマリ](#日本語サマリ) · [Synthetic output example](#synthetic-output-example)
46
+
47
+ ## Try it on your machine
48
+
49
+ With [`uvx`](https://docs.astral.sh/uv/guides/tools/), no persistent install is needed:
50
+
51
+ ```bash
52
+ uvx --from coding-agent-cost agent-cost doctor
53
+ uvx --from coding-agent-cost agent-cost report
54
+ ```
55
+
56
+ The package runner may need network access to download the tool. The CLI itself does not upload
57
+ your logs.
58
+
59
+ The first command checks the expected local paths, the Codex database, and the bundled rate
60
+ catalog. It does not open every Claude JSONL file. The second command performs the real scan,
61
+ prints the result, and exposes unreadable inputs in `data_quality.skipped_files`. This path was
62
+ exercised on macOS from a clean temporary directory against the published `0.1.0` package on
63
+ 2026-08-23.
64
+
65
+ Prefer a persistent command? Install the PyPI distribution, then run the same two commands:
36
66
 
37
67
  ```bash
38
- pip install coding-agent-cost # or, from a checkout: pip install -e .
39
- agent-cost doctor # sanity-check log locations and the rate catalog
68
+ pip install coding-agent-cost
69
+ agent-cost doctor
40
70
  agent-cost report
41
71
  ```
42
72
 
43
- `agent-cost report` scans `~/.claude/projects/**/*.jsonl` and
44
- `~/.codex/state_5.sqlite` (+ its rollout files), turns every token-usage
45
- event into a canonical "fact" (one model, one token kind, one timestamp,
46
- one count), prices each fact against a bundled rate catalog, and prints an
47
- aggregated table:
73
+ The PyPI distribution is named [`coding-agent-cost`](https://pypi.org/project/coding-agent-cost/),
74
+ while the command remains `agent-cost` and the import remains `agent_cost`. The shorter PyPI
75
+ name is unavailable because of PyPI's similarity rule; this project is not affiliated with the
76
+ unrelated `agentcost` distribution.
77
+
78
+ ## Synthetic output example
79
+
80
+ This is **synthetic Claude data**, not observed usage, a bill, or evidence of savings. The
81
+ CLI output below was reproduced with PyPI package `coding-agent-cost==0.1.0` and source
82
+ commit `d170ea301ed0c46351749214bd299e75ae8a7786` (only trailing space padding is omitted).
83
+ The [two-event fixture](examples/synthetic-claude.jsonl) contains known-model input, cache
84
+ writes without a TTL breakdown, and unknown-model input.
48
85
 
86
+ ```text
87
+ Month Agent Model Token Kind Tokens Priced Unpriced Est. Cost (USD) Credits Status
88
+ ------- ------ -------------------- ------------------- ------- ------- -------- --------------- ------- -----------
89
+ 2026-06 claude claude-opus-4-8 cache_write_unknown 1000000 1000000 0 6.2500 - lower_bound
90
+ 2026-06 claude claude-opus-4-8 input_nocache 1000000 1000000 0 5.0000 - priced
91
+ 2026-06 claude model-not-in-catalog input_nocache 500 0 500 0.0000 - unpriced
92
+
93
+ Total tokens: 2,000,500 Total estimated cost: $11.2500
94
+ Rates catalog: 2026-07-29 (sha256=5d86e11b3c95...)
95
+ Data quality: malformed_events=0 skipped_files=0 negative_deltas=0 unpriced_tokens=500
49
96
  ```
97
+
98
+ - `priced`: $5.0000 is a catalog-based estimate for 1,000,000 input tokens, not a confirmed charge.
99
+ - `lower_bound`: $6.2500 uses the 5-minute cache-write rate because the TTL is missing. This is
100
+ a lower bound under the catalog and observed token data, not a lower bound on your actual bill.
101
+ - `unpriced`: `0.0000` does not mean free. Read `pricing_status` (the `Status` column) together
102
+ with `unpriced_tokens` (`Unpriced`): 500 tokens have no price and are excluded from the dollar
103
+ total. The $11.2500 total also includes the cache-write lower-bound estimate.
104
+
105
+ <details>
106
+ <summary>Reproduce with synthetic data only (Python 3.9+ and uvx)</summary>
107
+
108
+ Run from this repository's root. Both log locations and the config file are explicitly set
109
+ inside a temporary directory. Inherited log-location overrides are removed for the child
110
+ process, so it cannot fall back to your real Claude or Codex logs. The temporary data is
111
+ removed when the command exits. The package runner may download the pinned package.
112
+
113
+ ```bash
114
+ python3 - <<'PY'
115
+ import json
116
+ import os
117
+ from pathlib import Path
118
+ import shutil
119
+ import subprocess
120
+ import tempfile
121
+
122
+ fixture = Path("examples/synthetic-claude.jsonl").resolve()
123
+ with tempfile.TemporaryDirectory(prefix="agent-cost-example-") as directory:
124
+ root = Path(directory)
125
+ project = root / "claude" / "projects" / "synthetic"
126
+ project.mkdir(parents=True)
127
+ (root / "codex").mkdir()
128
+ shutil.copyfile(fixture, project / "session.jsonl")
129
+ config = root / "config.json"
130
+ config.write_text(json.dumps({
131
+ "claude_home": str(root / "claude"),
132
+ "codex_home": str(root / "codex"),
133
+ }))
134
+ env = dict(os.environ)
135
+ for key in ("CLAUDE_HOME", "CODEX_HOME", "PYTHONPATH"):
136
+ env.pop(key, None)
137
+ env["AGENT_COST_CONFIG"] = str(config)
138
+ subprocess.run([
139
+ "uvx", "--from", "coding-agent-cost==0.1.0", "agent-cost",
140
+ "report", "--agent", "claude", "--since", "2026-06-01",
141
+ "--until", "2026-06-02", "--format", "table",
142
+ ], cwd=root, env=env, check=True)
143
+ PY
144
+ ```
145
+
146
+ Use `--format json` in the same command to see the full catalog SHA-256 and field names.
147
+
148
+ </details>
149
+
150
+ ## Why this exists
151
+
152
+ Many usage trackers are designed as rich, convenient dashboards. That can be the right choice
153
+ when broad agent coverage and interactive exploration matter most. `agent-cost` is for a
154
+ different trust model:
155
+
156
+ - **Local-first and zero-network:** source logs stay on disk; the package has zero runtime
157
+ dependencies.
158
+ - **Auditable:** token facts, the versioned rate catalog, its digest, and data-quality counters
159
+ are available for inspection and machine consumption.
160
+ - **Fail-closed pricing:** unknown models remain `unpriced`; ambiguous cache-write TTLs are
161
+ labeled `lower_bound` instead of silently receiving a best-guess price.
162
+ - **Explicit attribution boundary:** `measure` accepts named session ids, but agent-cost does
163
+ not infer a task from a branch or PR. A workflow such as
164
+ [`spec-lane`](https://github.com/shiki-yusuke/spec-lane) can own the session-to-task binding
165
+ and consume the versioned JSON result.
166
+
167
+ This is not a claim that local CLI accounting is universally better than a dashboard. It is a
168
+ smaller primitive for environments where data egress, dependency surface, custom metrics, or
169
+ task attribution need to remain under the operator's control.
170
+
171
+ See [Choosing by use case and trust model](https://github.com/shiki-yusuke/agent-cost/blob/main/docs/comparison.md)
172
+ for a dated, source-linked comparison with broader CLI trackers, local dashboards, and
173
+ observability stacks.
174
+
175
+ ## Report and export
176
+
177
+ `agent-cost report` turns each local usage event into a canonical fact (one model, one token
178
+ kind, one timestamp, one count), prices each fact against the bundled rate catalog, and prints
179
+ an aggregated table:
180
+
181
+ ```bash
50
182
  agent-cost report --since 2026-06-01 --until 2026-07-01 --format table
51
183
  agent-cost report --group-by month,agent --format csv
52
184
  agent-cost report --format json > usage.json
@@ -58,6 +190,11 @@ interpreted in `--timezone`, default UTC), `--agent claude,codex`,
58
190
  `--group-by month,agent,model,token-kind`, `--rates PATH` (use a different
59
191
  catalog entirely, see below), `--exclude-archived` (Codex threads).
60
192
 
193
+ **Platform note:** `report`, `export`, and `measure` use Python's IANA timezone database even
194
+ when the timezone is `UTC`. macOS and typical Linux installations provide it through the
195
+ operating system. A minimal Windows Python environment may require `pip install tzdata` first;
196
+ `tzdata` is not currently a declared package dependency.
197
+
61
198
  If another program wants to parse agent-cost's output for one or more
62
199
  specific session ids, see `agent-cost measure` below rather than
63
200
  scraping `report`.
@@ -75,10 +212,12 @@ network, never calls `gh`, and never resolves branches or PRs.
75
212
  at the 5-minute rate as an explicit **lower bound** and flagged
76
213
  `lower_bound` rather than guessed at the (more expensive) 1-hour rate.
77
214
  - **Codex CLI**: rollout files record a *cumulative* token count after each
78
- turn; agent-cost turns that into per-turn deltas. Codex does not expose a
79
- separate cache-write signal at all, so cache writes are never reported for
80
- Codex (not zero -- simply not observable, and left out of the row rather
81
- than implied). Codex's `output` fact is `output_tokens` alone: cross-checking
215
+ turn; agent-cost turns that into per-turn deltas. Under the Codex token-rate
216
+ tariff used here, cache writes are not added as a separate charge. The current
217
+ reader does not emit independent cache-write facts; newer logs can include
218
+ `cache_write_input_tokens`, but this field is not used to add a cache-write
219
+ charge. This differs from older logs where that signal was unavailable.
220
+ Codex's `output` fact is `output_tokens` alone: cross-checking
82
221
  real rollout files confirms `total_tokens == input_tokens + output_tokens`
83
222
  in every sample, which means `reasoning_output_tokens` is a breakdown of
84
223
  output tokens already counted, not an additional charge -- adding it in
@@ -115,6 +254,15 @@ network, never calls `gh`, and never resolves branches or PRs.
115
254
 
116
255
  ## Updating the rate catalog
117
256
 
257
+ **GPT-6 Astra:** Codex Standard/Fast estimates use exact ID `gpt-6-astra` and
258
+ owned settings matched to turn/model context in the public Codex 0.153.4 JSONL
259
+ format. Explicit `default` uses Standard; `priority` uses 2.5x. Missing/ambiguous
260
+ tier or model attribution stays `unpriced`, with tokens preserved. These are
261
+ request-setting estimates, not confirmed processing tiers or actual bills.
262
+ The catalog observation cutoff is not an official launch time; earlier usage
263
+ is `unpriced`. See [evidence, synthetic tests and model-switch limits](docs/astra-pricing.md).
264
+ API pricing and legacy message billing are outside this entry.
265
+
118
266
  Prices live in `agent_cost/rates.json`, not in code. It's a historical
119
267
  catalog: each model can have several time-bounded rate periods, so a price
120
268
  change is recorded as a new period rather than overwriting the old one (see
@@ -224,8 +372,10 @@ MIT. See [LICENSE](LICENSE).
224
372
 
225
373
  - 集計の最小単位は「1 イベント = 1 モデル × 1 token 種別」の fact であり、session 単位でモデルを
226
374
  丸めません。Claude の prompt cache は TTL 内訳(5分/1時間)が取れればそれを使い、取れない場合は
227
- 5分単価で **下限推計**(`lower_bound`)として明示します。Codex の cache write は観測不能なため
228
- 出力しません(0 とは区別)。
375
+ 5分単価で **下限推計**(`lower_bound`)として明示します。対象の Codex トークン料金体系では
376
+ cache write を別料金として加算せず、現行 reader は独立した cache-write fact を出力しません。
377
+ 新しいログに `cache_write_input_tokens` が含まれていても追加料金の計算には使用しません。
378
+ これは、旧ログでその情報を観測できないこととは区別します。
229
379
  - 出力フィールドは `estimated_cost_usd`(推計であることを明示)。Codex は `credits` も併記します。
230
380
  未知のモデル・単価表にない token 種別は `unpriced` として扱い、憶測の価格を出しません。
231
381
  - 単価表 (`agent_cost/rates.json`) は履歴型カタログで、値上げは新しい期間として追加します。
@@ -1,29 +1,158 @@
1
1
  # agent-cost
2
2
 
3
- > PyPI distribution: [`coding-agent-cost`](https://pypi.org/project/coding-agent-cost/) — the
4
- > command (`agent-cost`) and import name (`agent_cost`) are unchanged. The name `agent-cost`
5
- > itself is blocked by PyPI's name-similarity rule against `agentcost`, an unrelated project
6
- > by a different author; this tool is not affiliated with it.
7
-
8
- Estimate how many tokens Claude Code and Codex CLI actually used, and roughly
9
- what that cost, by reading the local logs those tools already write to your
3
+ See Claude Code and Codex CLI token usage and estimated costs from the logs already on your
10
4
  machine.
11
5
 
12
- ## Quick start
6
+ Keep pricing gaps visible: unknown models are marked `unpriced`, and cache-write estimates
7
+ with missing TTL information are marked `lower_bound`. JSON reports include the rate catalog
8
+ version and SHA-256 used for the calculation; the catalog records its pricing sources.
9
+
10
+ **These are estimates based on the selected rate catalog, not your actual bill.** The CLI makes
11
+ no runtime network calls and has zero runtime dependencies. On macOS and Linux with IANA
12
+ timezone data, no account, service, or project configuration is required. See the
13
+ [platform note](#report-and-export) for minimal Windows Python environments.
14
+
15
+ Need the result in another program? [`agent-cost measure`](#machine-consumption-agent-cost-measure)
16
+ returns versioned `measure/v1` JSON for session IDs you supply. Task and PR attribution remain
17
+ the caller's responsibility.
18
+
19
+ [日本語サマリ](#日本語サマリ) · [Synthetic output example](#synthetic-output-example)
20
+
21
+ ## Try it on your machine
22
+
23
+ With [`uvx`](https://docs.astral.sh/uv/guides/tools/), no persistent install is needed:
24
+
25
+ ```bash
26
+ uvx --from coding-agent-cost agent-cost doctor
27
+ uvx --from coding-agent-cost agent-cost report
28
+ ```
29
+
30
+ The package runner may need network access to download the tool. The CLI itself does not upload
31
+ your logs.
32
+
33
+ The first command checks the expected local paths, the Codex database, and the bundled rate
34
+ catalog. It does not open every Claude JSONL file. The second command performs the real scan,
35
+ prints the result, and exposes unreadable inputs in `data_quality.skipped_files`. This path was
36
+ exercised on macOS from a clean temporary directory against the published `0.1.0` package on
37
+ 2026-08-23.
38
+
39
+ Prefer a persistent command? Install the PyPI distribution, then run the same two commands:
13
40
 
14
41
  ```bash
15
- pip install coding-agent-cost # or, from a checkout: pip install -e .
16
- agent-cost doctor # sanity-check log locations and the rate catalog
42
+ pip install coding-agent-cost
43
+ agent-cost doctor
17
44
  agent-cost report
18
45
  ```
19
46
 
20
- `agent-cost report` scans `~/.claude/projects/**/*.jsonl` and
21
- `~/.codex/state_5.sqlite` (+ its rollout files), turns every token-usage
22
- event into a canonical "fact" (one model, one token kind, one timestamp,
23
- one count), prices each fact against a bundled rate catalog, and prints an
24
- aggregated table:
47
+ The PyPI distribution is named [`coding-agent-cost`](https://pypi.org/project/coding-agent-cost/),
48
+ while the command remains `agent-cost` and the import remains `agent_cost`. The shorter PyPI
49
+ name is unavailable because of PyPI's similarity rule; this project is not affiliated with the
50
+ unrelated `agentcost` distribution.
51
+
52
+ ## Synthetic output example
53
+
54
+ This is **synthetic Claude data**, not observed usage, a bill, or evidence of savings. The
55
+ CLI output below was reproduced with PyPI package `coding-agent-cost==0.1.0` and source
56
+ commit `d170ea301ed0c46351749214bd299e75ae8a7786` (only trailing space padding is omitted).
57
+ The [two-event fixture](examples/synthetic-claude.jsonl) contains known-model input, cache
58
+ writes without a TTL breakdown, and unknown-model input.
25
59
 
60
+ ```text
61
+ Month Agent Model Token Kind Tokens Priced Unpriced Est. Cost (USD) Credits Status
62
+ ------- ------ -------------------- ------------------- ------- ------- -------- --------------- ------- -----------
63
+ 2026-06 claude claude-opus-4-8 cache_write_unknown 1000000 1000000 0 6.2500 - lower_bound
64
+ 2026-06 claude claude-opus-4-8 input_nocache 1000000 1000000 0 5.0000 - priced
65
+ 2026-06 claude model-not-in-catalog input_nocache 500 0 500 0.0000 - unpriced
66
+
67
+ Total tokens: 2,000,500 Total estimated cost: $11.2500
68
+ Rates catalog: 2026-07-29 (sha256=5d86e11b3c95...)
69
+ Data quality: malformed_events=0 skipped_files=0 negative_deltas=0 unpriced_tokens=500
26
70
  ```
71
+
72
+ - `priced`: $5.0000 is a catalog-based estimate for 1,000,000 input tokens, not a confirmed charge.
73
+ - `lower_bound`: $6.2500 uses the 5-minute cache-write rate because the TTL is missing. This is
74
+ a lower bound under the catalog and observed token data, not a lower bound on your actual bill.
75
+ - `unpriced`: `0.0000` does not mean free. Read `pricing_status` (the `Status` column) together
76
+ with `unpriced_tokens` (`Unpriced`): 500 tokens have no price and are excluded from the dollar
77
+ total. The $11.2500 total also includes the cache-write lower-bound estimate.
78
+
79
+ <details>
80
+ <summary>Reproduce with synthetic data only (Python 3.9+ and uvx)</summary>
81
+
82
+ Run from this repository's root. Both log locations and the config file are explicitly set
83
+ inside a temporary directory. Inherited log-location overrides are removed for the child
84
+ process, so it cannot fall back to your real Claude or Codex logs. The temporary data is
85
+ removed when the command exits. The package runner may download the pinned package.
86
+
87
+ ```bash
88
+ python3 - <<'PY'
89
+ import json
90
+ import os
91
+ from pathlib import Path
92
+ import shutil
93
+ import subprocess
94
+ import tempfile
95
+
96
+ fixture = Path("examples/synthetic-claude.jsonl").resolve()
97
+ with tempfile.TemporaryDirectory(prefix="agent-cost-example-") as directory:
98
+ root = Path(directory)
99
+ project = root / "claude" / "projects" / "synthetic"
100
+ project.mkdir(parents=True)
101
+ (root / "codex").mkdir()
102
+ shutil.copyfile(fixture, project / "session.jsonl")
103
+ config = root / "config.json"
104
+ config.write_text(json.dumps({
105
+ "claude_home": str(root / "claude"),
106
+ "codex_home": str(root / "codex"),
107
+ }))
108
+ env = dict(os.environ)
109
+ for key in ("CLAUDE_HOME", "CODEX_HOME", "PYTHONPATH"):
110
+ env.pop(key, None)
111
+ env["AGENT_COST_CONFIG"] = str(config)
112
+ subprocess.run([
113
+ "uvx", "--from", "coding-agent-cost==0.1.0", "agent-cost",
114
+ "report", "--agent", "claude", "--since", "2026-06-01",
115
+ "--until", "2026-06-02", "--format", "table",
116
+ ], cwd=root, env=env, check=True)
117
+ PY
118
+ ```
119
+
120
+ Use `--format json` in the same command to see the full catalog SHA-256 and field names.
121
+
122
+ </details>
123
+
124
+ ## Why this exists
125
+
126
+ Many usage trackers are designed as rich, convenient dashboards. That can be the right choice
127
+ when broad agent coverage and interactive exploration matter most. `agent-cost` is for a
128
+ different trust model:
129
+
130
+ - **Local-first and zero-network:** source logs stay on disk; the package has zero runtime
131
+ dependencies.
132
+ - **Auditable:** token facts, the versioned rate catalog, its digest, and data-quality counters
133
+ are available for inspection and machine consumption.
134
+ - **Fail-closed pricing:** unknown models remain `unpriced`; ambiguous cache-write TTLs are
135
+ labeled `lower_bound` instead of silently receiving a best-guess price.
136
+ - **Explicit attribution boundary:** `measure` accepts named session ids, but agent-cost does
137
+ not infer a task from a branch or PR. A workflow such as
138
+ [`spec-lane`](https://github.com/shiki-yusuke/spec-lane) can own the session-to-task binding
139
+ and consume the versioned JSON result.
140
+
141
+ This is not a claim that local CLI accounting is universally better than a dashboard. It is a
142
+ smaller primitive for environments where data egress, dependency surface, custom metrics, or
143
+ task attribution need to remain under the operator's control.
144
+
145
+ See [Choosing by use case and trust model](https://github.com/shiki-yusuke/agent-cost/blob/main/docs/comparison.md)
146
+ for a dated, source-linked comparison with broader CLI trackers, local dashboards, and
147
+ observability stacks.
148
+
149
+ ## Report and export
150
+
151
+ `agent-cost report` turns each local usage event into a canonical fact (one model, one token
152
+ kind, one timestamp, one count), prices each fact against the bundled rate catalog, and prints
153
+ an aggregated table:
154
+
155
+ ```bash
27
156
  agent-cost report --since 2026-06-01 --until 2026-07-01 --format table
28
157
  agent-cost report --group-by month,agent --format csv
29
158
  agent-cost report --format json > usage.json
@@ -35,6 +164,11 @@ interpreted in `--timezone`, default UTC), `--agent claude,codex`,
35
164
  `--group-by month,agent,model,token-kind`, `--rates PATH` (use a different
36
165
  catalog entirely, see below), `--exclude-archived` (Codex threads).
37
166
 
167
+ **Platform note:** `report`, `export`, and `measure` use Python's IANA timezone database even
168
+ when the timezone is `UTC`. macOS and typical Linux installations provide it through the
169
+ operating system. A minimal Windows Python environment may require `pip install tzdata` first;
170
+ `tzdata` is not currently a declared package dependency.
171
+
38
172
  If another program wants to parse agent-cost's output for one or more
39
173
  specific session ids, see `agent-cost measure` below rather than
40
174
  scraping `report`.
@@ -52,10 +186,12 @@ network, never calls `gh`, and never resolves branches or PRs.
52
186
  at the 5-minute rate as an explicit **lower bound** and flagged
53
187
  `lower_bound` rather than guessed at the (more expensive) 1-hour rate.
54
188
  - **Codex CLI**: rollout files record a *cumulative* token count after each
55
- turn; agent-cost turns that into per-turn deltas. Codex does not expose a
56
- separate cache-write signal at all, so cache writes are never reported for
57
- Codex (not zero -- simply not observable, and left out of the row rather
58
- than implied). Codex's `output` fact is `output_tokens` alone: cross-checking
189
+ turn; agent-cost turns that into per-turn deltas. Under the Codex token-rate
190
+ tariff used here, cache writes are not added as a separate charge. The current
191
+ reader does not emit independent cache-write facts; newer logs can include
192
+ `cache_write_input_tokens`, but this field is not used to add a cache-write
193
+ charge. This differs from older logs where that signal was unavailable.
194
+ Codex's `output` fact is `output_tokens` alone: cross-checking
59
195
  real rollout files confirms `total_tokens == input_tokens + output_tokens`
60
196
  in every sample, which means `reasoning_output_tokens` is a breakdown of
61
197
  output tokens already counted, not an additional charge -- adding it in
@@ -92,6 +228,15 @@ network, never calls `gh`, and never resolves branches or PRs.
92
228
 
93
229
  ## Updating the rate catalog
94
230
 
231
+ **GPT-6 Astra:** Codex Standard/Fast estimates use exact ID `gpt-6-astra` and
232
+ owned settings matched to turn/model context in the public Codex 0.153.4 JSONL
233
+ format. Explicit `default` uses Standard; `priority` uses 2.5x. Missing/ambiguous
234
+ tier or model attribution stays `unpriced`, with tokens preserved. These are
235
+ request-setting estimates, not confirmed processing tiers or actual bills.
236
+ The catalog observation cutoff is not an official launch time; earlier usage
237
+ is `unpriced`. See [evidence, synthetic tests and model-switch limits](docs/astra-pricing.md).
238
+ API pricing and legacy message billing are outside this entry.
239
+
95
240
  Prices live in `agent_cost/rates.json`, not in code. It's a historical
96
241
  catalog: each model can have several time-bounded rate periods, so a price
97
242
  change is recorded as a new period rather than overwriting the old one (see
@@ -201,8 +346,10 @@ MIT. See [LICENSE](LICENSE).
201
346
 
202
347
  - 集計の最小単位は「1 イベント = 1 モデル × 1 token 種別」の fact であり、session 単位でモデルを
203
348
  丸めません。Claude の prompt cache は TTL 内訳(5分/1時間)が取れればそれを使い、取れない場合は
204
- 5分単価で **下限推計**(`lower_bound`)として明示します。Codex の cache write は観測不能なため
205
- 出力しません(0 とは区別)。
349
+ 5分単価で **下限推計**(`lower_bound`)として明示します。対象の Codex トークン料金体系では
350
+ cache write を別料金として加算せず、現行 reader は独立した cache-write fact を出力しません。
351
+ 新しいログに `cache_write_input_tokens` が含まれていても追加料金の計算には使用しません。
352
+ これは、旧ログでその情報を観測できないこととは区別します。
206
353
  - 出力フィールドは `estimated_cost_usd`(推計であることを明示)。Codex は `credits` も併記します。
207
354
  未知のモデル・単価表にない token 種別は `unpriced` として扱い、憶測の価格を出しません。
208
355
  - 単価表 (`agent_cost/rates.json`) は履歴型カタログで、値上げは新しい期間として追加します。
@@ -6,4 +6,4 @@ against a versioned rate catalog. Everything happens locally; there are no
6
6
  network calls.
7
7
  """
8
8
 
9
- __version__ = "0.1.0"
9
+ __version__ = "0.1.1"
@@ -33,6 +33,14 @@ def price_fact(catalog: RateCatalog, fact: Fact) -> Tuple[Optional[Decimal], str
33
33
  if resolved_key is None or period is None:
34
34
  return None, "unpriced", None
35
35
 
36
+ # Astra's verified rates cover only the exact Codex model ID. Generic
37
+ # suffix normalization must not implicitly price unverified snapshots,
38
+ # speed variants or usage from a different agent with this new entry.
39
+ if resolved_key == "gpt-6-astra" and (
40
+ fact.agent != "codex" or fact.model_raw != "gpt-6-astra" or fact.mode == "unknown"
41
+ ):
42
+ return None, "unpriced", None
43
+
36
44
  rate_field = fact.token_kind
37
45
  lower_bound = False
38
46
  if fact.token_kind == "cache_write_unknown":
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "schema_version": "1",
3
- "catalog_version": "2026-07-29",
3
+ "catalog_version": "2026-09-06",
4
4
  "currency": "USD",
5
5
  "unit": "per_mtok",
6
6
  "usd_per_credit": "0.04",
7
7
  "notes": [
8
8
  "All Claude rates are standard (non-batch, non-priority) API prices. Batch API is roughly 50% off, but agent-cost's logs have no signal for whether a given request went through Batch, so batch usage is priced at standard rates and will overstate cost for anyone using Batch.",
9
9
  "claude-opus-5's launch date could not be confirmed from an authoritative source as of this catalog_version; its rate period's effective_from (2026-07-01) is a placeholder -- update it once confirmed.",
10
- "gpt-5.6 (sol/terra/luna) credits could not be confirmed from the primary source (help.openai.com's Codex rate card returned HTTP 403 to automated fetches). The values here were cross-checked across several independent secondary sources that agreed with each other and are internally consistent (Sol's $5/$30 per-MTok API price x 25 = 125/750 credits, matching usd_per_credit=0.04); see the gpt-5.6-* sources entries below. Treat effective_from (2026-07-01) as a placeholder and re-verify against the primary rate card when it becomes reachable."
10
+ "gpt-5.6 (sol/terra/luna) credits could not be confirmed from the primary source (help.openai.com's Codex rate card returned HTTP 403 to automated fetches). The values here were cross-checked across several independent secondary sources that agreed with each other and are internally consistent (Sol's $5/$30 per-MTok API price x 25 = 125/750 credits, matching usd_per_credit=0.04); see the gpt-5.6-* sources entries below. Treat effective_from (2026-07-01) as a placeholder and re-verify against the primary rate card when it becomes reachable.",
11
+ "gpt-6-astra: Codex standard token-based rates only; not API pricing, legacy message metering, or actual contractual charges. Confirmed 2026-09-06 JST (2026-09-05T17:18:23Z). effective_from is this observation cutoff, NOT an official launch or price-start time; earlier events remain unpriced. No Codex long-context surcharge or cache-write charge is applied. Fast multiplier 2.5 applies to explicit priority request settings with matching owned turn/model context; explicit default uses Standard. Missing or ambiguous Astra modes remain unpriced. These settings do not confirm the backend processing tier. See docs/astra-pricing.md for compatibility limits and the separate GPT-5.6 discrepancy report."
11
12
  ],
12
13
  "sources": [
13
14
  {
@@ -32,6 +33,21 @@
32
33
  "url": "https://rohitai.com/blog/openai-gpt-5-6-codex-rate-card-agentic-credit-pool",
33
34
  "retrieved_at": "2026-07-29",
34
35
  "note": "secondary source, used only for gpt-5.6 (sol/terra/luna) credits pending primary confirmation"
36
+ },
37
+ {
38
+ "url": "https://help.openai.com/en/articles/11481834-chatgpt-rate-card",
39
+ "retrieved_at": "2026-09-05T17:18:23Z",
40
+ "note": "Primary source: ChatGPT Work / Codex token credits and Astra Fast 2.5x. Existing GPT-5.6 values are preserved pending historical-period review; see docs/astra-pricing.md."
41
+ },
42
+ {
43
+ "url": "https://help.openai.com/en/articles/20001415-chatgpt-rate-card-enterprise-token-based-pricing",
44
+ "retrieved_at": "2026-09-05T17:18:23Z",
45
+ "note": "Primary source: Work / Codex standard USD token rates, Astra Fast 2.5x, no Codex Astra long-context surcharge and no Codex cache-write charge. Individual agreements may differ."
46
+ },
47
+ {
48
+ "url": "https://developers.openai.com/api/docs/models/gpt-6-astra",
49
+ "retrieved_at": "2026-09-05T17:18:23Z",
50
+ "note": "Boundary check only: API cache writes, long context and API Fast rates are NOT applied to this Codex entry."
35
51
  }
36
52
  ],
37
53
  "models": [
@@ -485,6 +501,30 @@
485
501
  "output": "10.0"
486
502
  }
487
503
  ]
504
+ },
505
+ {
506
+ "model_key": "gpt-6-astra",
507
+ "aliases": [],
508
+ "fast_multiplier": "2.5",
509
+ "credits_per_mtok": {
510
+ "input_nocache": "250.0",
511
+ "cache_read": "25.0",
512
+ "cache_write_5m": null,
513
+ "cache_write_1h": null,
514
+ "output": "1250.0"
515
+ },
516
+ "rates": [
517
+ {
518
+ "rate_id": "gpt-6-astra-codex-observed-2026-09-05",
519
+ "effective_from": "2026-09-05T17:18:23+00:00",
520
+ "effective_until": null,
521
+ "input_nocache": "10.0",
522
+ "cache_read": "1.0",
523
+ "cache_write_5m": null,
524
+ "cache_write_1h": null,
525
+ "output": "50.0"
526
+ }
527
+ ]
488
528
  }
489
529
  ]
490
530
  }