coding-agent-cost 0.1.0__tar.gz → 0.2.0__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 (37) hide show
  1. coding_agent_cost-0.2.0/PKG-INFO +437 -0
  2. coding_agent_cost-0.2.0/README.md +411 -0
  3. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/agent_cost/__init__.py +1 -1
  4. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/agent_cost/aggregate.py +58 -0
  5. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/agent_cost/cli.py +55 -5
  6. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/agent_cost/facts.py +6 -3
  7. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/agent_cost/rates.json +77 -8
  8. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/agent_cost/readers/__init__.py +11 -0
  9. coding_agent_cost-0.2.0/agent_cost/readers/claude.py +667 -0
  10. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/agent_cost/readers/codex.py +134 -3
  11. coding_agent_cost-0.2.0/coding_agent_cost.egg-info/PKG-INFO +437 -0
  12. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/coding_agent_cost.egg-info/SOURCES.txt +6 -1
  13. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/pyproject.toml +11 -1
  14. coding_agent_cost-0.2.0/tests/test_astra_pricing.py +338 -0
  15. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/tests/test_cli.py +38 -3
  16. coding_agent_cost-0.2.0/tests/test_e0a_fable_5_1_and_sonnet_5_correction.py +198 -0
  17. coding_agent_cost-0.2.0/tests/test_e0a_review3_hardening.py +276 -0
  18. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/tests/test_measure_v1_contract.py +14 -6
  19. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/tests/test_rates.py +14 -5
  20. coding_agent_cost-0.2.0/tests/test_reader_dedup.py +901 -0
  21. coding_agent_cost-0.2.0/tests/test_version_consistency.py +33 -0
  22. coding_agent_cost-0.1.0/PKG-INFO +0 -247
  23. coding_agent_cost-0.1.0/README.md +0 -224
  24. coding_agent_cost-0.1.0/agent_cost/readers/claude.py +0 -177
  25. coding_agent_cost-0.1.0/coding_agent_cost.egg-info/PKG-INFO +0 -247
  26. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/LICENSE +0 -0
  27. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/agent_cost/config.py +0 -0
  28. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/agent_cost/rates.py +0 -0
  29. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/agent_cost/renderers.py +0 -0
  30. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/coding_agent_cost.egg-info/dependency_links.txt +0 -0
  31. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/coding_agent_cost.egg-info/entry_points.txt +0 -0
  32. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/coding_agent_cost.egg-info/top_level.txt +0 -0
  33. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/setup.cfg +0 -0
  34. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/tests/test_aggregate.py +0 -0
  35. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/tests/test_facts.py +0 -0
  36. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/tests/test_reader_claude.py +0 -0
  37. {coding_agent_cost-0.1.0 → coding_agent_cost-0.2.0}/tests/test_reader_codex.py +0 -0
@@ -0,0 +1,437 @@
1
+ Metadata-Version: 2.4
2
+ Name: coding-agent-cost
3
+ Version: 0.2.0
4
+ Summary: Estimate AI coding agent (Claude Code / Codex CLI) token usage and cost from local logs
5
+ Author: shiki-yusuke
6
+ License: MIT
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
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Software Development :: Build Tools
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Dynamic: license-file
26
+
27
+ # agent-cost
28
+
29
+ See Claude Code and Codex CLI token usage and estimated costs from the logs already on your
30
+ machine.
31
+
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:
66
+
67
+ ```bash
68
+ pip install coding-agent-cost
69
+ agent-cost doctor
70
+ agent-cost report
71
+ ```
72
+
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.
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
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
182
+ agent-cost report --since 2026-06-01 --until 2026-07-01 --format table
183
+ agent-cost report --group-by month,agent --format csv
184
+ agent-cost report --format json > usage.json
185
+ agent-cost export --agent claude --out facts.jsonl # raw canonical facts
186
+ ```
187
+
188
+ Useful flags: `--since`/`--until` (half-open window; date-only values are
189
+ interpreted in `--timezone`, default UTC), `--agent claude,codex`,
190
+ `--group-by month,agent,model,token-kind`, `--rates PATH` (use a different
191
+ catalog entirely, see below), `--exclude-archived` (Codex threads).
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
+
198
+ If another program wants to parse agent-cost's output for one or more
199
+ specific session ids, see `agent-cost measure` below rather than
200
+ scraping `report`.
201
+
202
+ ## What this measures, and what it doesn't
203
+
204
+ agent-cost only reads data that is already on disk. It never talks to the
205
+ network, never calls `gh`, and never resolves branches or PRs.
206
+
207
+ - **Claude Code**: every logical assistant message is one billing event,
208
+ attributed to the exact model on that event (a session that switches
209
+ models mid-conversation is not folded into one "primary model"). Claude
210
+ Code's transcript writes one JSONL line per content block of the same
211
+ message, and those lines are deduplicated first -- but only when a row
212
+ carries a full `message.id` + `requestId` pair; a row missing either
213
+ half is emitted on its own (never merged) and flagged
214
+ `source_quality: "identity_missing"` rather than assumed
215
+ billing-accurate. `identity_missing` facts are still priced and included
216
+ in rows/totals; the flag is a warning, not an exclusion or an unpriced
217
+ status. A message's lines don't necessarily repeat an identical `usage`
218
+ block: `model` and the input-side fields (input tokens, cache read,
219
+ cache-write TTL breakdown) stay the same across a message's lines, but
220
+ `output_tokens` typically grows line by line as the response streams in,
221
+ and intermediate lines usually lack `usage.speed` entirely (reported as
222
+ mode `"unknown"`), with only the final line carrying a concrete mode.
223
+ Within a deduplicated group, `model` or an input-side field that
224
+ actually differs across the group's rows, two rows disagreeing on a
225
+ *concrete* mode (`"normal"` vs `"fast"`), or an `output_tokens` value
226
+ that decreases or is non-monotonic across them, is counted in
227
+ `data_quality.conflicting_duplicate_groups` as a real billing
228
+ disagreement -- `output_tokens` growing row by row, and mode
229
+ `"unknown"` mixed with a single concrete mode elsewhere in the group,
230
+ are both the ordinary streaming pattern just described and are not
231
+ flagged, since the reader's own cross-check of real transcripts found
232
+ exactly that pattern in every observed duplicated group. See
233
+ `CHANGELOG.md`'s 0.2.0 entry.
234
+ When Anthropic's prompt-cache TTL breakdown (5-minute vs 1-hour writes) is
235
+ present in the log, it's used; otherwise the cache-write tokens are priced
236
+ at the 5-minute rate as an explicit **lower bound** and flagged
237
+ `lower_bound` rather than guessed at the (more expensive) 1-hour rate.
238
+ - **Codex CLI**: rollout files record a *cumulative* token count after each
239
+ turn; agent-cost turns that into per-turn deltas. Under the Codex token-rate
240
+ tariff used here, cache writes are not added as a separate charge. The current
241
+ reader does not emit independent cache-write facts; newer logs can include
242
+ `cache_write_input_tokens`, but this field is not used to add a cache-write
243
+ charge. This differs from older logs where that signal was unavailable.
244
+ Codex's `output` fact is `output_tokens` alone: cross-checking
245
+ real rollout files confirms `total_tokens == input_tokens + output_tokens`
246
+ in every sample, which means `reasoning_output_tokens` is a breakdown of
247
+ output tokens already counted, not an additional charge -- adding it in
248
+ would double it.
249
+ - **Anthropic prices are standard (non-batch) API list prices.** The Batch
250
+ API is roughly 50% cheaper, but agent-cost's logs carry no signal for
251
+ whether a request went through Batch, so all Claude usage is priced at
252
+ standard rates; this overstates cost for anyone using Batch.
253
+ - **Cost is always an estimate.** The output field is `estimated_cost_usd`,
254
+ never `cost_usd`: it is a list-price calculation from token counts, not a
255
+ bill. For Codex, whose provider bills in credits, the row also carries a
256
+ `credits` figure; `credits × usd_per_credit` is an illustrative USD
257
+ conversion, not what you were actually charged (enterprise allowances, overage rules, and
258
+ fast-mode multipliers with unknown values are exactly why the raw credits
259
+ number is kept alongside the USD estimate rather than only the USD).
260
+ - An unrecognized model, or a token kind a model's rate period doesn't
261
+ define, is reported as `unpriced` with `estimated_cost_usd: null`-like
262
+ zero and the tokens broken out in `unpriced_tokens` -- agent-cost never
263
+ invents a price for something it doesn't have a rate for.
264
+ - Corrupted log lines, files that vanish mid-read, and Codex cumulative
265
+ counters that go backwards (e.g. after a session reset) are all counted
266
+ in the report's `data_quality` block instead of being silently dropped or
267
+ clamped to zero.
268
+ - **Known catalog gaps**, tracked in `agent_cost/rates.json`'s `notes`:
269
+ `claude-opus-5`'s launch date could not be confirmed from an authoritative
270
+ source, so its rate period's `effective_from` is a placeholder. `gpt-5.6`
271
+ (Sol/Terra/Luna) credits could not be confirmed from the primary source
272
+ (`help.openai.com`'s Codex rate card returns HTTP 403 to automated
273
+ fetches); the values in the catalog come from several independent
274
+ secondary sources that agree with each other and are internally
275
+ consistent with `usd_per_credit`, but are not primary-source-verified --
276
+ re-check them once the rate card is reachable. Update either via a
277
+ custom `--rates` file if you have a confirmed number.
278
+
279
+ ## Updating the rate catalog
280
+
281
+ **GPT-6 Astra:** Codex Standard/Fast estimates use exact ID `gpt-6-astra` and
282
+ owned settings matched to turn/model context in the public Codex 0.153.4 JSONL
283
+ format. Explicit `default` uses Standard; `priority` uses 2.5x. Missing/ambiguous
284
+ tier or model attribution stays `unpriced`, with tokens preserved. These are
285
+ request-setting estimates, not confirmed processing tiers or actual bills.
286
+ The catalog observation cutoff is not an official launch time; earlier usage
287
+ is `unpriced`. See [evidence, synthetic tests and model-switch limits](docs/astra-pricing.md).
288
+ API pricing and legacy message billing are outside this entry.
289
+
290
+ Prices live in `agent_cost/rates.json`, not in code. It's a historical
291
+ catalog: each model can have several time-bounded rate periods, so a price
292
+ change is recorded as a new period rather than overwriting the old one (see
293
+ `claude-sonnet-5`'s launch-promo period for a worked example). Every catalog
294
+ carries a `catalog_version`, a list of `sources` (the pricing page a rate
295
+ came from), and is validated on load (no duplicate model keys or aliases,
296
+ no negative rates, no overlapping periods for the same model).
297
+
298
+ To use your own catalog instead of the one bundled with the package, pass
299
+ `--rates path/to/rates.json` to `report` or `export` -- this fully replaces
300
+ the bundled catalog, it does not merge with it. Inspect any catalog with:
301
+
302
+ ```bash
303
+ agent-cost rates show # list every model_key
304
+ agent-cost rates show --model gpt-5.5 # one model's rate history
305
+ agent-cost rates validate path/to/rates.json
306
+ ```
307
+
308
+ `agent-cost report`'s JSON output always echoes the catalog's
309
+ `catalog_version` and the sha256 of the exact rates file used, so a report
310
+ can be traced back to the prices that produced it.
311
+
312
+ ## Machine consumption: `agent-cost measure`
313
+
314
+ `report` is for a person reading a table. `measure` is for another program
315
+ calling agent-cost as a subprocess and parsing its stdout -- e.g. a build
316
+ orchestrator attributing cost to a specific unit of work it already knows
317
+ the session id(s) for.
318
+
319
+ ```bash
320
+ agent-cost measure --session-id <id> [--session-id <id> ...] \
321
+ [--since --until --timezone] [--agent claude,codex] [--rates PATH] --format json
322
+ ```
323
+
324
+ - One or more `--session-id` is required (repeat the flag for more than
325
+ one); `measure` never scans "everything," only the sessions you name.
326
+ - `--since`/`--until` accept a date-only value (interpreted in
327
+ `--timezone`), an offset-qualified ISO 8601 datetime (`+00:00`, `+09:00`,
328
+ ...), or the same datetime with a trailing `Z` instead of an offset
329
+ (`2026-07-31T00:00:00Z`, exactly what `Date.toISOString()` in JS emits)
330
+ -- all three are accepted by `report`/`export`/`measure` alike.
331
+ - Exit code is `0` on success -- including when none of the given session
332
+ ids matched any usage at all, which is a valid, representable answer
333
+ (empty totals, `"matched": false` per session), not a failure. Exit code
334
+ `2` means bad input (no `--session-id`, an unparseable `--since`/
335
+ `--until`/`--timezone`, or an invalid `--rates` catalog) -- nothing was
336
+ measured, don't trust any partial output.
337
+ - Output is one JSON object on stdout with a `protocol_version` field
338
+ (currently `"measure/v1"`) a caller should check before trusting the
339
+ shape below. Within a major version, only additive changes (new fields)
340
+ are made; a field being removed or changing meaning bumps the version.
341
+
342
+ ```json
343
+ {
344
+ "protocol_version": "measure/v1",
345
+ "producer_version": "0.2.0",
346
+ "accounting_basis": "agent-cost-raw-total/v2",
347
+ "generated_at": "...",
348
+ "window": { "since": "...", "until": null },
349
+ "timezone": "UTC",
350
+ "agent": ["claude", "codex"],
351
+ "rates": { "catalog_version": "2026-07-29", "sha256": "..." },
352
+ "session_ids": ["sess-1", "sess-2"],
353
+ "sessions": {
354
+ "sess-1": { "matched": true, "rows": [ /* same row shape as report --format json */ ], "totals": { "tokens": 12345, "priced_tokens": 12345, "unpriced_tokens": 0, "estimated_cost_usd": 0.42, "credits": 0.0 } },
355
+ "sess-2": { "matched": false, "rows": [], "totals": { "tokens": 0, "priced_tokens": 0, "unpriced_tokens": 0, "estimated_cost_usd": 0.0, "credits": 0.0 } }
356
+ },
357
+ "total": { "rows": [ /* union across every requested session_id */ ], "totals": { "...": "..." } },
358
+ "data_quality": {
359
+ "malformed_events": 0,
360
+ "skipped_files": 0,
361
+ "negative_deltas": 0,
362
+ "unpriced_tokens": 0,
363
+ "duplicate_rows_skipped": 0,
364
+ "conflicting_duplicate_groups": 0,
365
+ "missing_dedup_identity_rows": 0,
366
+ "source_quality": { "ok": 41, "first_event_delta": 2, "identity_missing": 0 }
367
+ }
368
+ }
369
+ ```
370
+
371
+ Rows are grouped by agent/model/token-kind only -- `measure` never buckets
372
+ by month, since a query is already scoped to specific sessions. `total` is
373
+ the union of every requested `session_id` (not a global report), so it's
374
+ the number to attribute to whatever unit of work those sessions represent.
375
+ `producer_version` is the agent-cost package version that produced this
376
+ payload; `accounting_basis` identifies the token-accounting semantics
377
+ behind the numbers (separate from `protocol_version`, which only tracks
378
+ the JSON shape) -- a consumer that persists historical measurements should
379
+ key comparability on `accounting_basis`, not `producer_version` alone,
380
+ since a future release can bump the latter while keeping the former.
381
+ `data_quality.unpriced_tokens` and `.source_quality` are scoped to the
382
+ requested sessions; so are the three dedup counters
383
+ (`duplicate_rows_skipped`, `conflicting_duplicate_groups`,
384
+ `missing_dedup_identity_rows`), which are Claude-only and computed over
385
+ the intersection of the requested session ids and the `--since`/`--until`
386
+ window, never over an unrequested session's rows.
387
+ `.malformed_events`/`.skipped_files`/`.negative_deltas` describe the
388
+ health of the underlying log read within `--since`/`--until` and are not
389
+ attributable to one session.
390
+
391
+ ## Privacy
392
+
393
+ agent-cost makes zero network calls. `agent-cost export`'s JSONL never
394
+ includes absolute file paths, rollout paths, prompt/message content, or git
395
+ branch names -- only the fields needed to reproduce a cost estimate:
396
+ `occurred_at_utc`, `agent`, `session_id`, `model_raw`, `model_key`,
397
+ `token_kind`, `tokens`, `mode`, and `source_quality` (a fixed-vocabulary
398
+ caveat about how that one fact was derived, e.g. `"ok"` or Codex's
399
+ `"first_event_delta"` -- never null).
400
+
401
+ ## License
402
+
403
+ MIT. See [LICENSE](LICENSE).
404
+
405
+ ---
406
+
407
+ ## 日本語サマリ
408
+
409
+ `agent-cost` は Claude Code / Codex CLI がローカルに残すログ(`~/.claude/projects/**/*.jsonl`
410
+ と `~/.codex/state_5.sqlite` + rollout ファイル)だけを読み、トークン使用量とおおよそのコストを
411
+ 見積もる CLI です。ネットワークアクセスは一切行いません。
412
+
413
+ - 集計の最小単位は「1 イベント = 1 モデル × 1 token 種別」の fact であり、session 単位でモデルを
414
+ 丸めません。Claude の prompt cache は TTL 内訳(5分/1時間)が取れればそれを使い、取れない場合は
415
+ 5分単価で **下限推計**(`lower_bound`)として明示します。対象の Codex トークン料金体系では
416
+ cache write を別料金として加算せず、現行 reader は独立した cache-write fact を出力しません。
417
+ 新しいログに `cache_write_input_tokens` が含まれていても追加料金の計算には使用しません。
418
+ これは、旧ログでその情報を観測できないこととは区別します。
419
+ - 出力フィールドは `estimated_cost_usd`(推計であることを明示)。Codex は `credits` も併記します。
420
+ 未知のモデル・単価表にない token 種別は `unpriced` として扱い、憶測の価格を出しません。
421
+ - 単価表 (`agent_cost/rates.json`) は履歴型カタログで、値上げは新しい期間として追加します。
422
+ `--rates PATH` で別カタログに完全差し替えできます。
423
+ - `agent-cost measure --session-id ID [--session-id ID ...] --format json` は他プログラムから
424
+ subprocess で叩くための機械可読な契約です(`protocol_version: "measure/v1"`)。指定した
425
+ session_id が1件も見つからなくても終了コードは0(空集計として表現)、`--session-id` 未指定など
426
+ の入力エラーのみ終了コード2です。
427
+ - 破損したログ行、読めなくなったファイル、Codex の累積カウンタが逆行するケースなどは、すべて
428
+ `data_quality` に件数として記録し、黙って丸めたり捨てたりしません。
429
+ - Codex の `output` は `output_tokens` のみです。実 rollout データを突合した結果
430
+ `total_tokens == input_tokens + output_tokens` が常に成立することを確認しており、
431
+ `reasoning_output_tokens` は output の内訳(二重計上してはいけない)と判断しています。
432
+ - Anthropic の単価は標準(非 Batch)API 価格です。Batch API は約50%安いですが、ログからは
433
+ Batch 利用かどうか判別できないため、常に標準単価で推計します(Batch 利用者には過大推計)。
434
+ - `claude-opus-5` のローンチ日は根拠を確認できず `effective_from` はプレースホルダです。
435
+ `gpt-5.6`(sol/terra/luna)は一次情報(help.openai.com の rate card)が 403 で取得できなかったため、
436
+ 相互に整合する複数の二次情報源の値を採用しています(一次情報での裏取りは未完了、詳細は
437
+ `rates.json` の `notes`)。