copilot-session-usage 0.2.1__tar.gz → 0.2.2__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 (107) hide show
  1. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/CHANGELOG.md +6 -0
  2. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/PKG-INFO +3 -3
  3. copilot_session_usage-0.2.2/docs/source/explanation/how-cost-estimation-works.md +138 -0
  4. copilot_session_usage-0.2.2/docs/source/reference/pricing.md +122 -0
  5. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/justfile +1 -0
  6. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/_schema/Base.schema.yaml +10 -1
  7. copilot_session_usage-0.2.2/knowledge/_schema/Experiment.schema.yaml +44 -0
  8. copilot_session_usage-0.2.2/knowledge/_schema/Finding.schema.yaml +49 -0
  9. copilot_session_usage-0.2.2/knowledge/_schema/Principle.schema.yaml +25 -0
  10. copilot_session_usage-0.2.2/knowledge/_schema/Structure.schema.yaml +26 -0
  11. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/concepts/overview.md +2 -2
  12. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/concepts/session-cost-analysis.md +1 -1
  13. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/experiments/verify-subagent-cost-attribution.md +2 -1
  14. copilot_session_usage-0.2.2/knowledge/findings/cache-write-cost-not-tracked.md +83 -0
  15. copilot_session_usage-0.2.2/knowledge/findings/index.md +7 -0
  16. copilot_session_usage-0.2.2/knowledge/findings/title-jsonl-not-counted-as-model-turn.md +50 -0
  17. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/reference/debug-log-format.md +3 -2
  18. copilot_session_usage-0.2.2/knowledge/structures/cache-cost-approximation.md +86 -0
  19. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/structures/index.md +1 -0
  20. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/structures/subagent-cost-tracking.md +13 -5
  21. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/structures/vscode-copilot-extension.md +9 -1
  22. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/src/copilot_session_usage/_internal/core.py +53 -14
  23. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/src/copilot_session_usage/_internal/vscode.py +54 -0
  24. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/src/copilot_session_usage/api.py +7 -1
  25. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/src/copilot_session_usage/cli.py +5 -0
  26. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/src/copilot_session_usage/data/custom-models-pricing.yml +3 -2
  27. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/tests/conftest.py +4 -6
  28. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/tests/test_api.py +100 -109
  29. copilot_session_usage-0.2.2/tests/test_cli.py +280 -0
  30. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/tests/test_coverage_gaps.py +32 -33
  31. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/tests/test_vscode.py +42 -0
  32. copilot_session_usage-0.2.1/docs/source/explanation/how-cost-estimation-works.md +0 -96
  33. copilot_session_usage-0.2.1/docs/source/reference/pricing.md +0 -62
  34. copilot_session_usage-0.2.1/knowledge/_schema/Experiment.schema.yaml +0 -55
  35. copilot_session_usage-0.2.1/knowledge/_schema/Finding.schema.yaml +0 -64
  36. copilot_session_usage-0.2.1/knowledge/_schema/Principle.schema.yaml +0 -40
  37. copilot_session_usage-0.2.1/knowledge/_schema/Structure.schema.yaml +0 -41
  38. copilot_session_usage-0.2.1/knowledge/findings/index.md +0 -5
  39. copilot_session_usage-0.2.1/tests/test_cli.py +0 -274
  40. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/.editorconfig +0 -0
  41. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  42. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  43. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/.github/guidelines/knowledge-base.guidelines.md +0 -0
  44. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/.github/pull_request_template.md +0 -0
  45. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/.github/skills/consolidate-knowledge-base/SKILL.md +0 -0
  46. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/.github/skills/record-finding/SKILL.md +0 -0
  47. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/.github/workflows/ci.yml +0 -0
  48. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/.github/workflows/publish.yml +0 -0
  49. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/.gitignore +0 -0
  50. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/.readthedocs.yaml +0 -0
  51. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/AGENTS.md +0 -0
  52. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/CONTRIBUTING.md +0 -0
  53. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/LICENSE +0 -0
  54. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/README.md +2 -2
  55. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/_static/changelog.js +0 -0
  56. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/_static/custom.css +0 -0
  57. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/changelog.md +0 -0
  58. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/conf.py +0 -0
  59. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/explanation/index.md +0 -0
  60. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/how-to/analyze-specific-session.md +0 -0
  61. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/how-to/batch-and-spending.md +0 -0
  62. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/how-to/export-json.md +0 -0
  63. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/how-to/index.md +0 -0
  64. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/how-to/wsl2.md +0 -0
  65. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/index.md +0 -0
  66. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/installation.md +0 -0
  67. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/reference/api.md +0 -0
  68. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/reference/cli.md +0 -0
  69. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/reference/index.md +0 -0
  70. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/tutorials/getting-started.md +0 -0
  71. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/docs/source/tutorials/index.md +0 -0
  72. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/_schema/Concept.schema.yaml +0 -0
  73. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/_schema/Playbook.schema.yaml +0 -0
  74. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/_schema/Reference.schema.yaml +0 -0
  75. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/concepts/copilot-cli.md +0 -0
  76. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/concepts/index.md +0 -0
  77. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/concepts/threshold-based-pricing.md +0 -0
  78. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/experiments/index.md +0 -0
  79. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/findings/subagent-logs-use-runsubagent-prefix.md +0 -0
  80. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/guides/automation-scripts.md +0 -0
  81. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/guides/cost-optimization.md +0 -0
  82. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/guides/index.md +0 -0
  83. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/guides/wsl2-setup.md +0 -0
  84. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/ideas/index.md +0 -0
  85. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/ideas/multi-session-efficiency-analytics.md +0 -0
  86. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/index.md +0 -0
  87. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/log.md +0 -0
  88. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/principles/findings-are-immutable.md +0 -0
  89. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/principles/index.md +0 -0
  90. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/reference/index.md +0 -0
  91. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/reference/pricing-formats.md +0 -0
  92. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/structures/knowledge-base-information-types.md +0 -0
  93. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/knowledge/structures/session-discovery-algorithm.md +0 -0
  94. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/pyproject.toml +0 -0
  95. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/scripts/refresh_pricing.py +0 -0
  96. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/skills/copilot-session-usage/SKILL.md +0 -0
  97. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/src/copilot_session_usage/__init__.py +0 -0
  98. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/src/copilot_session_usage/_internal/__init__.py +0 -0
  99. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/src/copilot_session_usage/_internal/copilot_cli.py +0 -0
  100. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/src/copilot_session_usage/data/__init__.py +0 -0
  101. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/src/copilot_session_usage/data/models-and-pricing.lock +0 -0
  102. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/src/copilot_session_usage/data/models-and-pricing.yml +0 -0
  103. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/tests/test_core.py +0 -0
  104. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/tests/test_rendering.py +0 -0
  105. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/tests/test_vscode_platform.py +0 -0
  106. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/uv.lock +0 -0
  107. {copilot_session_usage-0.2.1 → copilot_session_usage-0.2.2}/uv.toml +0 -0
@@ -1,5 +1,11 @@
1
1
  ## Unreleased
2
2
 
3
+ ### Feat
4
+
5
+ - accurate AIC cost computation using nanoAiu and cache_write
6
+
7
+ ## v0.2.1 (2026-07-02)
8
+
3
9
  ## v0.2.0 (2026-07-02)
4
10
 
5
11
  ### Fix
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: copilot-session-usage
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Usage and cost analytics for GitHub Copilot and Copilot-CLI session logs
5
5
  Project-URL: Homepage, https://github.com/gsemet/copilot-session-usage
6
6
  Project-URL: Documentation, https://copilot-session-usage.readthedocs.io/en/stable/
@@ -27,6 +27,8 @@ Description-Content-Type: text/markdown
27
27
 
28
28
  Extract VS Code Copilot session cost KPIs (tokens, estimated USD, model, duration) from local debug logs.
29
29
 
30
+ **Full documentation:** [copilot-session-usage.readthedocs.io](https://copilot-session-usage.readthedocs.io/)
31
+
30
32
  ## Installation
31
33
 
32
34
  ```bash
@@ -183,8 +185,6 @@ just docs
183
185
  just docs-serve
184
186
  ```
185
187
 
186
- **Full documentation:** [copilot-session-usage.readthedocs.io](https://copilot-session-usage.readthedocs.io/)
187
-
188
188
  ## License
189
189
 
190
190
  MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,138 @@
1
+ # How Cost Estimation Works
2
+
3
+ `copilot-session-usage` estimates session costs from VS Code debug logs.
4
+ This page explains each step.
5
+
6
+ ---
7
+
8
+ ## Where the logs live
9
+
10
+ VS Code stores one directory per workspace under `workspaceStorage`. Inside
11
+ each workspace directory, the Copilot extension writes debug logs:
12
+
13
+ ```
14
+ workspaceStorage/
15
+ └── <workspace-hash>/
16
+ └── GitHub.copilot-chat/
17
+ └── debug-logs/
18
+ └── <session-uuid>/
19
+ ├── *.jsonl ← token events
20
+ └── ...
21
+ ```
22
+
23
+ Default locations by platform:
24
+
25
+ | Platform | Path |
26
+ |----------|------|
27
+ | macOS | `~/Library/Application Support/Code/User/workspaceStorage/` |
28
+ | Linux | `~/.config/Code/User/workspaceStorage/` |
29
+ | Windows | `%APPDATA%\Code\User\workspaceStorage\` |
30
+ | WSL2 (remote) | `~/.vscode-server/data/User/workspaceStorage/` |
31
+
32
+ The tool also checks `Code - Insiders` variants automatically.
33
+
34
+ ---
35
+
36
+ ## How sessions are discovered
37
+
38
+ The tool reads the session title from VS Code's SQLite workspace database
39
+ (`state.vscdb`). Sessions are sorted by the last-modified time of their
40
+ debug-log directory, most recent first.
41
+
42
+ The `list` command reads only metadata (no JSONL parsing). The `analyze`,
43
+ `id`, and `batch` commands parse the full JSONL files.
44
+
45
+ ---
46
+
47
+ ## Parsing JSONL logs
48
+
49
+ Each `.jsonl` file contains one JSON object per line. The tool extracts
50
+ token-count events emitted by the Copilot extension for each LLM call:
51
+
52
+ - `input_tokens` — tokens sent to the model
53
+ - `output_tokens` — tokens generated by the model
54
+ - `cached_tokens` — input tokens served from the provider's prompt cache
55
+ - `model` — the model name as reported by the provider
56
+
57
+ A single session may call multiple models (e.g., Claude Sonnet for the main
58
+ request and Claude Haiku for a subagent). Each model's tokens are summed
59
+ separately, then costs are computed per model and aggregated.
60
+
61
+ ---
62
+
63
+ ## Cost calculation
64
+
65
+ The VS Code debug log reports `inputTokens` as the **total** prompt sent
66
+ (cached + non-cached combined). `cachedTokens` is the subset served from
67
+ the provider's cache. The non-cached portion is `inputTokens − cachedTokens`.
68
+
69
+ Each `llm_request` event also carries `copilotUsageNanoAiu` — VS Code's own
70
+ cost for that call in nano-AI Credits (nanoAIU). It is present for all
71
+ Copilot-plan models (Claude, GPT, and others). It is absent for Azure-hosted
72
+ models not billed via Copilot AIC — `Kimi-K2.6-azure` is one such model:
73
+ it is billed through Azure separately and correctly reports $0 AIC.
74
+
75
+ When `copilotUsageNanoAiu` is present and non-zero, the tool uses it directly:
76
+
77
+ ```
78
+ cost_usd = copilotUsageNanoAiu / 100_000_000_000 (1 nanoAIU = 1e-11 USD)
79
+ ```
80
+
81
+ For models that do not report `copilotUsageNanoAiu` — Azure-hosted models billed
82
+ outside the Copilot plan — the tool falls back to token-based computation
83
+ (or $0 when the model has no Copilot pricing entry):
84
+
85
+ ```
86
+ cost_usd = (
87
+ (inputTokens - cachedTokens) × rate.input # fresh tokens
88
+ + cachedTokens × rate.cached_input # cache-read tokens
89
+ + outputTokens × rate.output
90
+ ) / 1_000_000
91
+ ```
92
+
93
+ plus the Anthropic cache-write approximation when applicable.
94
+
95
+ Verified on a real session: `copilotUsageNanoAiu`-based cost matches the
96
+ VS Code AIC panel at **0.000% error** for Claude models.
97
+
98
+ ---
99
+
100
+ ## Subagent attribution
101
+
102
+ `runSubagent` calls appear in the JSONL as a distinct event type. The tool
103
+ tracks them separately so `--detail full` can show which fraction of tokens
104
+ was consumed by subagents vs. the main conversation.
105
+
106
+ ---
107
+
108
+ ## Accuracy
109
+
110
+ For Copilot-plan models (Claude, GPT, etc.), the tool reads
111
+ `copilotUsageNanoAiu` — VS Code's own per-call cost field — directly from the
112
+ JSONL, so the session total matches the VS Code AIC panel at **0% error**.
113
+
114
+ For Azure-hosted models billed outside the Copilot plan (`Kimi-K2.6-azure` is
115
+ the current example), `copilotUsageNanoAiu` is absent. The tool falls back to
116
+ token-based pricing using rates in `custom-models-pricing.yml`. These models
117
+ are billed through Azure separately; setting their prices to `$0.00` in
118
+ `custom-models-pricing.yml` correctly reflects that they do not consume AIC.
119
+
120
+ ### Why the LLM-call count differs by 1 from the panel
121
+
122
+ The VS Code Agent Debug panel excludes the `title-*.jsonl` file (background
123
+ title-generation calls). `copilot-session-usage` counts them because they
124
+ consume real tokens. A session with one title-generation call will show one
125
+ more LLM call and the corresponding (small) token counts compared to the panel.
126
+
127
+ ### Why token counts can differ between tool and panel
128
+
129
+ Token counts in the tool include `title-*.jsonl`; the VS Code panel does not.
130
+ For session `438d24a8`, the delta is exactly one Kimi call: +441 input, +1,245
131
+ output.
132
+
133
+ ### Pricing table updates
134
+
135
+ The bundled pricing table (`data/models-and-pricing.yml`) is updated with each
136
+ release. Run `just refresh-pricing` to pull the latest rates. Stale rates only
137
+ affect the token-based fallback path; `copilotUsageNanoAiu`-based costs are
138
+ unaffected by the table.
@@ -0,0 +1,122 @@
1
+ # Pricing Reference
2
+
3
+ `copilot-session-usage` ships a bundled pricing table at
4
+ `src/copilot_session_usage/data/models-and-pricing.yml`.
5
+
6
+ ## Upstream source
7
+
8
+ Prices are synced from the GitHub Copilot official rate card:
9
+ [`github/docs` — `data/tables/copilot/models-and-pricing.yml`](https://github.com/github/docs/blob/main/data/tables/copilot/models-and-pricing.yml)
10
+
11
+ The bundled copy is updated with each release.
12
+
13
+ ## Cost formula
14
+
15
+ The VS Code debug log reports three token counts per LLM call:
16
+
17
+ | Field in log | Meaning |
18
+ |---|---|
19
+ | `inputTokens` | **Total** prompt tokens sent — cached and non-cached combined |
20
+ | `cachedTokens` | Subset of `inputTokens` served from the provider's prompt cache |
21
+ | `outputTokens` | Completion tokens generated |
22
+
23
+ Non-cached input = `inputTokens − cachedTokens`. The two input
24
+ components are billed at different rates, so the formula splits them:
25
+
26
+ ```
27
+ cost_usd = (
28
+ (inputTokens - cachedTokens) × rate.input # fresh prompt tokens
29
+ + cachedTokens × rate.cached_input # cache-read tokens
30
+ + outputTokens × rate.output # completion tokens
31
+ ) / 1_000_000
32
+ ```
33
+
34
+ Results are summed across all models called in the session.
35
+
36
+ The equivalent statement using Anthropic-style variable names
37
+ (where `input` already excludes cached tokens) is:
38
+
39
+ ```
40
+ cost_usd = (
41
+ input × rate.input
42
+ + cache_read × rate.cached_input
43
+ + cache_creation × (rate.cache_write ?? rate.input)
44
+ + output × rate.output
45
+ ) / 1_000_000
46
+ ```
47
+
48
+ VS Code debug logs do not expose `cache_creation` tokens directly.
49
+ For Anthropic models, the tool approximates the incremental cache-creation
50
+ cost using fresh input as a proxy (see note below).
51
+
52
+ :::{note}
53
+ **Anthropic `cache_write` — approximated via fresh input.**
54
+
55
+ Anthropic models have a `cache_write` rate for tokens written to the provider's
56
+ cache for the first time. VS Code JSONL logs do not expose `cacheCreationTokens`;
57
+ `agent-traces.db` has the schema column but VS Code does not populate it for
58
+ Claude models (verified: 0 rows, VS Code 1.103+).
59
+
60
+ The tool approximates the incremental cost as:
61
+
62
+ ```
63
+ delta = (inputTokens - cachedTokens) × (cache_write_per_m - input_per_m) / 1_000_000
64
+ ```
65
+
66
+ Verified on a real 92-call Claude Sonnet 4.6 session: this matches the VS Code
67
+ AIC panel exactly ($5.9037 both ways). Models without `cache_write` (OpenAI,
68
+ Google) are unaffected — the delta is zero.
69
+
70
+ `agent_traces_db_paths()` in `vscode.py` locates `agent-traces.db` on all
71
+ platforms. When VS Code starts populating `gen_ai.usage.cache_creation.input_tokens`,
72
+ reading the exact value from the DB will replace this proxy without API changes.
73
+ :::
74
+
75
+ ## AI Credits and USD
76
+
77
+ Post-2026-06-01, GitHub Copilot bills in **AI Credits (AIC)**.
78
+ The upstream rate card publishes prices in USD per million tokens.
79
+ The conversion is:
80
+
81
+ ```
82
+ 1 AIC = $0.01 USD → 100 AIC = $1.00 USD
83
+ ```
84
+
85
+ A model priced at `input: $3.00` per million tokens costs **300 AIC**
86
+ per million input tokens. This tool reports USD; multiply by 100 to get AIC.
87
+
88
+ The per-token AIC rate is identical across all Copilot plans. Plans differ
89
+ only in the monthly AIC allowance included — that allowance is not tracked
90
+ by this tool.
91
+
92
+ ## Cache discounts
93
+
94
+ All providers discount tokens served from their prompt cache:
95
+
96
+ | Provider | Cache-read discount vs. input |
97
+ |----------|-------------------------------|
98
+ | OpenAI | ~10× cheaper |
99
+ | Anthropic | ~10× cheaper |
100
+ | Google | ~10× cheaper |
101
+
102
+ A session with 85% cache hit ratio costs significantly less than raw
103
+ token counts suggest.
104
+
105
+ ## Long-context tier switching
106
+
107
+ Some models have two pricing tiers based on input token count:
108
+
109
+ | Model | Threshold | Effect |
110
+ |-------|-----------|--------|
111
+ | GPT-5.4 | > 272K tokens | Input/cached/output prices double |
112
+ | GPT-5.5 | > 272K tokens | Input/cached/output prices double |
113
+ | Gemini 3.1 Pro | > 200K tokens | Input/cached/output prices increase |
114
+
115
+ `copilot-session-usage` selects the correct tier automatically based on
116
+ the session's total input tokens per model.
117
+
118
+ ## Custom pricing
119
+
120
+ Override any model's price by editing
121
+ `src/copilot_session_usage/data/custom-models-pricing.yml`. Entries in
122
+ this file take precedence over the main table.
@@ -116,6 +116,7 @@ preflight:
116
116
  just typecheck
117
117
  just tests-coverage
118
118
  just knowledge-lint
119
+ just knowledge-validate
119
120
  just docs
120
121
 
121
122
  # ─── Cleanup ──────────────────────────────────────────────────────────────────
@@ -23,4 +23,13 @@ properties:
23
23
  description: ISO 8601 creation or last-updated timestamp.
24
24
  type: string
25
25
  format: date-time
26
- additionalProperties: false
26
+ links:
27
+ type: array
28
+ items:
29
+ type: string
30
+ description: "Bundle-relative paths this concept links to"
31
+ backlinks:
32
+ type: array
33
+ items:
34
+ type: string
35
+ description: "Bundle-relative paths of concepts that link to this one"
@@ -0,0 +1,44 @@
1
+ $schema: "http://json-schema.org/draft-07/schema#"
2
+ title: Experiment
3
+ description: >-
4
+ A reusable, prepared procedure that can be executed one or two times to test a
5
+ hypothesis. Each execution produces one Finding. The Experiment is a template;
6
+ its recorded outcomes live as Finding documents.
7
+ allOf:
8
+ - $ref: "Base.schema.yaml"
9
+ - type: object
10
+ properties:
11
+ type:
12
+ description: Document type — must be "Experiment".
13
+ type: string
14
+ const: Experiment
15
+ hypothesis:
16
+ description: The claim the experiment is designed to confirm or falsify.
17
+ type: string
18
+ minLength: 1
19
+ steps:
20
+ description: Ordered procedure to run the experiment reproducibly.
21
+ type: array
22
+ items: { type: string }
23
+ minItems: 1
24
+ expected_signals:
25
+ description: Observable signals that indicate the hypothesis holds or fails.
26
+ type: array
27
+ items: { type: string }
28
+ max_runs:
29
+ description: How many times this experiment is meant to be run (typically 1–2).
30
+ type: integer
31
+ minimum: 1
32
+ status:
33
+ description: Lifecycle state of the experiment template.
34
+ type: string
35
+ enum: [proposed, active, retired, superseded]
36
+ derived_findings:
37
+ description: IDs of Findings produced by running this experiment.
38
+ type: array
39
+ items: { type: string }
40
+ superseded_by:
41
+ description: IDs of documents that supersede this experiment.
42
+ type: array
43
+ items: { type: string }
44
+ required: [type, hypothesis, steps]
@@ -0,0 +1,49 @@
1
+ $schema: "http://json-schema.org/draft-07/schema#"
2
+ title: Finding
3
+ description: >-
4
+ An empirical, dated, falsifiable record of what an agent or human truthfully
5
+ observed and believed at a point in time. The body is immutable once written;
6
+ only lifecycle frontmatter (status, contradicted_by, superseded_by) may be
7
+ appended later.
8
+ allOf:
9
+ - $ref: "Base.schema.yaml"
10
+ - type: object
11
+ properties:
12
+ type:
13
+ description: Document type — must be "Finding".
14
+ type: string
15
+ const: Finding
16
+ confidence:
17
+ description: How confident the reporter was at the time of recording.
18
+ type: string
19
+ enum: [low, medium, high, confirmed]
20
+ context:
21
+ description: >-
22
+ What the reporter believed and the situation at the time — the honest
23
+ basis for the finding, including assumptions that may later prove wrong.
24
+ type: string
25
+ minLength: 1
26
+ status:
27
+ description: Lifecycle state. Appended non-destructively; the body never changes.
28
+ type: string
29
+ enum: [active, contradicted, superseded]
30
+ derived_from:
31
+ description: ID of the Experiment whose run produced this finding, if any.
32
+ type: string
33
+ contradicts:
34
+ description: IDs of earlier Findings this finding contradicts.
35
+ type: array
36
+ items: { type: string }
37
+ supersedes:
38
+ description: IDs of earlier Findings this finding replaces.
39
+ type: array
40
+ items: { type: string }
41
+ contradicted_by:
42
+ description: IDs of later Findings that contradict this one (appended by review).
43
+ type: array
44
+ items: { type: string }
45
+ superseded_by:
46
+ description: IDs of later Findings that replace this one (appended by review).
47
+ type: array
48
+ items: { type: string }
49
+ required: [type, timestamp, confidence, context]
@@ -0,0 +1,25 @@
1
+ $schema: "http://json-schema.org/draft-07/schema#"
2
+ title: Principle
3
+ description: >-
4
+ A standard or convention, usually defined with humans. Principles are stable,
5
+ normative statements ("we always…", "never…") — not dated observations. They
6
+ may be supported by Findings but are agreed, not merely observed.
7
+ allOf:
8
+ - $ref: "Base.schema.yaml"
9
+ - type: object
10
+ properties:
11
+ type:
12
+ description: Document type — must be "Principle".
13
+ type: string
14
+ const: Principle
15
+ rationale:
16
+ description: Why this principle exists and what it protects against.
17
+ type: string
18
+ authority:
19
+ description: Who agreed this principle (e.g. "team", "human-reviewed").
20
+ type: string
21
+ supported_by:
22
+ description: IDs of Findings that provide empirical support for this principle.
23
+ type: array
24
+ items: { type: string }
25
+ required: [type]
@@ -0,0 +1,26 @@
1
+ $schema: "http://json-schema.org/draft-07/schema#"
2
+ title: Structure
3
+ description: >-
4
+ A description of how an object, system, or artifact is composed and how its
5
+ parts relate — the "how it works" view. Distinct from a Concept (an idea) and
6
+ from a Playbook (a procedure to perform).
7
+ allOf:
8
+ - $ref: "Base.schema.yaml"
9
+ - type: object
10
+ properties:
11
+ type:
12
+ description: Document type — must be "Structure".
13
+ type: string
14
+ const: Structure
15
+ subject:
16
+ description: The object or system whose structure is described.
17
+ type: string
18
+ parts:
19
+ description: The components, fields, or sub-parts that make up the subject.
20
+ type: array
21
+ items: { type: string }
22
+ related_concepts:
23
+ description: IDs of Concept documents that explain the ideas behind this structure.
24
+ type: array
25
+ items: { type: string }
26
+ required: [type]
@@ -19,9 +19,9 @@ Every LLM request made by a coding agent (parent or subagent) is logged as a str
19
19
 
20
20
  | Metric | Source Field | Description |
21
21
  |--------|-------------|-------------|
22
- | Input Tokens | `attrs.inputTokens` | Tokens sent to the model (prompt + context) |
22
+ | Input Tokens | `attrs.inputTokens` | **Total** prompt tokens sent (cached + non-cached) |
23
23
  | Output Tokens | `attrs.outputTokens` | Tokens generated by the model |
24
- | Cached Tokens | `attrs.cachedTokens` | Prompt tokens served from cache (not billed) |
24
+ | Cached Tokens | `attrs.cachedTokens` | Subset of input tokens served from the provider's prompt cache (billed at a lower rate) |
25
25
  | LLM Calls | Count of `llm_request` events | Number of individual model invocations |
26
26
  | Model | `attrs.model` | Which model handled the request |
27
27
 
@@ -6,7 +6,7 @@ description: Process of extracting token usage metrics from Copilot debug logs
6
6
  tags: [cost-analysis, tokens, pricing, debug-logs]
7
7
  timestamp: 2026-06-30T22:00:00Z
8
8
  links: []
9
- backlinks: []
9
+ backlinks: [structures/cache-cost-approximation.md]
10
10
  ---
11
11
 
12
12
  # Session Cost Analysis
@@ -18,7 +18,8 @@ steps: [Pick a session directory that contains at least one runSubagent-*.jsonl
18
18
  expected_signals: ['Tool total equals independent sum (no double-count, no omission).',
19
19
  Each subagent subtotal is attributed to its own runSubagent id.]
20
20
  max_runs: 2
21
- status: proposed
21
+ status: superseded
22
+ superseded_by: [structures/subagent-cost-tracking.md]
22
23
  links: []
23
24
  backlinks: []
24
25
  ---
@@ -0,0 +1,83 @@
1
+ ---
2
+ type: Finding
3
+ title: "cache_write approximated via fresh_input — matches AIC panel exactly"
4
+ description: >
5
+ VS Code JSONL logs and agent-traces.db both lack cache_creation token counts.
6
+ Approximating cache_creation as fresh_input = inputTokens - cachedTokens and
7
+ billing only the incremental delta (cache_write - input) / 1M produces an exact
8
+ match to the VS Code AIC panel. Implemented in estimate_cost since v0.3.
9
+ tags: [cache-write, anthropic, aic, approximation, verified, implemented]
10
+ confidence: confirmed
11
+ context: >-
12
+ Verified by comparing copilot-session-usage output against the VS Code AIC
13
+ panel for multiple sessions. The approximation holds exactly when cache_write
14
+ is computed as fresh_input = inputTokens - cachedTokens.
15
+ timestamp: 2026-07-02T23:00:00Z
16
+ links: []
17
+ backlinks: [structures/cache-cost-approximation.md]
18
+ ---
19
+
20
+ # Finding: cache_write approximated via fresh_input — now implemented
21
+
22
+ ## Background
23
+
24
+ VS Code JSONL debug logs do not expose `cacheCreationTokens`. The OTel SQLite
25
+ store (`agent-traces.db`) has the schema column
26
+ `gen_ai.usage.cache_creation.input_tokens` but VS Code does not populate it for
27
+ Claude models (verified: 0 rows on VS Code 1.103+).
28
+
29
+ ## The correct approximation
30
+
31
+ The key insight is that the current code already bills fresh input at `input_per_m`.
32
+ The incremental cost of cache creation is only the **difference**:
33
+
34
+ ```
35
+ delta = fresh_input × (cache_write_per_m − input_per_m) / 1_000_000
36
+ ```
37
+
38
+ where `fresh_input = inputTokens − cachedTokens`.
39
+
40
+ ## Verification on session 438d24a8 (Claude Sonnet 4.6, 92 calls)
41
+
42
+ | Metric | Value |
43
+ |--------|-------|
44
+ | Fresh input | 720,122 tokens |
45
+ | Delta formula: 720K × ($3.75 − $3.00)/M | **$0.5401** |
46
+ | Old tool cost (without delta) | $5.3636 |
47
+ | New tool cost (with delta) | $5.9037 |
48
+ | VS Code panel (590.37 AIC ÷ 100) | **$5.9037** |
49
+
50
+ Exact match.
51
+
52
+ Note: an earlier version of this finding incorrectly computed
53
+ `720K × $3.75/M = $2.70` (the full rate, not the incremental delta),
54
+ concluding a "5× overestimate". That calculation was wrong; the delta
55
+ formula gives the correct $0.54 increment.
56
+
57
+ ## OTel DB fallback
58
+
59
+ `vscode.agent_traces_db_paths()` discovers all existing `agent-traces.db` paths
60
+ across Code / Code - Insiders on every platform. When VS Code eventually populates
61
+ `gen_ai.usage.cache_creation.input_tokens`, reading the exact value from the DB
62
+ will replace the proxy. The DB is not required for current operation.
63
+
64
+ ## Implication
65
+
66
+ For Anthropic-heavy sessions, `copilot-session-usage` now closely matches the
67
+ VS Code AIC panel. The approximation is accurate when the full context prefix is
68
+ cached (typical for long Copilot sessions). Sessions that do not cache (very short
69
+ contexts, no system prompt) may over-count by at most `fresh_input × delta_rate`.
70
+
71
+ ## Follow-up fix: model_breakdown now uses nano_aiu (2026-07-03)
72
+
73
+ The `model_breakdown` (per-model cost table) previously used `estimate_cost`
74
+ (token-based) on `global_per_model`, which did not accumulate `nano_aiu`.
75
+ This created a minor inconsistency: the session total used nano_aiu while the
76
+ per-model breakdown used token-based pricing (difference ~$0.0002 for session
77
+ 438d24a8 = 0.001% error, well within tolerance).
78
+
79
+ Fix: `global_per_model` now accumulates `nano_aiu` from each file's per-model
80
+ bucket, and `model_breakdown` calls `estimate_cost_for_file({model: v}, pricing)`
81
+ instead of `estimate_cost(...)`. Both session total and per-model breakdown now
82
+ use the same nano_aiu-first logic. Verified: model_breakdown sum = $14.709213
83
+ matches session total $14.7092 (rounding only).
@@ -0,0 +1,7 @@
1
+ # Finding
2
+
3
+ An empirical, dated, falsifiable record of what an agent or human truthfully observed and believed at a point in time. The body is immutable once written; only lifecycle frontmatter (status, contradicted_by, superseded_by) may be appended later.
4
+
5
+ - [cache_write approximated via fresh_input — matches AIC panel exactly](cache-write-cost-not-tracked.md) — VS Code JSONL logs and agent-traces.db both lack cache_creation token counts. Approximating cache_creation as fresh_input = inputTokens - cachedTokens and billing only the incremental delta (cache_write - input) / 1M produces an exact match to the VS Code AIC panel. Implemented in estimate_cost since v0.3. [Finding]
6
+ - [Subagent Logs Use runSubagent Prefix](subagent-logs-use-runsubagent-prefix.md) — Subagent activity is recorded in separate JSONL files prefixed with "runSubagent-" inside the session debug-logs directory. [Finding]
7
+ - [title-*.jsonl adds 1 LLM call and its tokens to tool totals; VS Code panel excludes it](title-jsonl-not-counted-as-model-turn.md) — The VS Code Agent Debug panel "Model Turns" counter excludes title-generation calls. copilot-session-usage counts them. The delta is exactly the title-*.jsonl file's token counts. [Finding]
@@ -0,0 +1,50 @@
1
+ ---
2
+ type: Finding
3
+ title: "title-*.jsonl adds 1 LLM call and its tokens to tool totals; VS Code panel
4
+ excludes it"
5
+ description: The VS Code Agent Debug panel "Model Turns" counter excludes
6
+ title-generation calls. copilot-session-usage counts them. The delta is
7
+ exactly the title-*.jsonl file's token counts.
8
+ tags: [token-counting, title-generation, panel-vs-tool, verified]
9
+ confidence: confirmed
10
+ context: >-
11
+ Observed by comparing VS Code Agent Debug panel metrics against
12
+ copilot-session-usage output for session 438d24a8. The delta in turns,
13
+ input tokens, and output tokens matched exactly the title-generation
14
+ JSONL file.
15
+ timestamp: 2026-07-02T22:00:00Z
16
+ links: []
17
+ backlinks: [structures/subagent-cost-tracking.md,
18
+ structures/vscode-copilot-extension.md]
19
+ ---
20
+
21
+ # Finding: title-*.jsonl not counted as "Model Turn" by VS Code panel
22
+
23
+ ## Observed
24
+
25
+ Session `438d24a8` at the same snapshot:
26
+
27
+ | Metric | VS Code panel | copilot-session-usage | Delta |
28
+ |--------|---------------|-----------------------|-------|
29
+ | Turns/calls | 96 | 97 | +1 |
30
+ | Input tokens | 8,831,919 | 8,832,360 | +441 |
31
+ | Output tokens | 58,784 | 60,029 | +1,245 |
32
+ | Cached tokens | 8,097,846 | 8,097,846 | 0 |
33
+
34
+ The delta matches exactly the `title-ca686498-...jsonl` file:
35
+ `model=Kimi-K2.6-azure input=441 cached=0 output=1,245`
36
+
37
+ ## Explanation
38
+
39
+ VS Code "Model Turns" counts only the main conversation turns.
40
+ The title-generation call (one `llm_request` in `title-*.jsonl`) is a
41
+ background call VS Code excludes from the panel display.
42
+
43
+ `copilot-session-usage` counts **all** `llm_request` events across all
44
+ `*.jsonl` files in the session directory, including `title-*.jsonl`.
45
+
46
+ ## Implication
47
+
48
+ The tool over-counts by 1 call and the title call's tokens vs. the panel.
49
+ This is intentional: title-generation consumes real tokens and should be
50
+ included in cost tracking.
@@ -56,9 +56,10 @@ This is the only event type that contains cost information. All other events (`s
56
56
  | Field | Type | Meaning |
57
57
  |-------|------|---------|
58
58
  | `attrs.model` | string | Model identifier (e.g., `Kimi-K2.6-azure`, `gpt-4o`) |
59
- | `attrs.inputTokens` | integer | Tokens consumed from context window |
59
+ | `attrs.inputTokens` | integer | **Total** prompt tokens sent cached and non-cached combined |
60
60
  | `attrs.outputTokens` | integer | Tokens generated |
61
- | `attrs.cachedTokens` | integer | Tokens served from prompt cache |
61
+ | `attrs.cachedTokens` | integer | Subset of `inputTokens` served from the provider's prompt cache (billed at the lower `cached_input` rate) |
62
+ | `attrs.copilotUsageNanoAiu` | integer | VS Code's own AIC cost for this call in nano-units (÷ 1e9 = AIC, ÷ 1e11 = USD). Present for all Copilot-plan models (Claude, GPT, etc.). Absent for Azure-hosted models not billed via Copilot AIC (e.g. `Kimi-K2.6-azure`). |
62
63
  | `attrs.ttft` | integer | Time to first token (ms) |
63
64
 
64
65
  > **Mixed-model sessions:** A single `.jsonl` file can contain `llm_request` events from