agent-session-inspector 0.1.6 → 0.1.8

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 (123) hide show
  1. package/VERSION +1 -1
  2. package/app/.next/BUILD_ID +1 -1
  3. package/app/.next/app-path-routes-manifest.json +1 -0
  4. package/app/.next/build-manifest.json +3 -3
  5. package/app/.next/prerender-manifest.json +3 -3
  6. package/app/.next/required-server-files.json +1 -1
  7. package/app/.next/routes-manifest.json +9 -0
  8. package/app/.next/server/app/_global-error.html +1 -1
  9. package/app/.next/server/app/_global-error.rsc +1 -1
  10. package/app/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/app/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/app/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/app/.next/server/app/_not-found/page.js.nft.json +1 -1
  14. package/app/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  15. package/app/.next/server/app/_not-found.html +1 -1
  16. package/app/.next/server/app/_not-found.rsc +2 -2
  17. package/app/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  18. package/app/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  19. package/app/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  20. package/app/.next/server/app/api/analytics/route.js +2 -1
  21. package/app/.next/server/app/api/analytics/route.js.nft.json +1 -1
  22. package/app/.next/server/app/api/logs/route.js +2 -1
  23. package/app/.next/server/app/api/logs/route.js.nft.json +1 -1
  24. package/app/.next/server/app/api/pricing/route.js +2 -1
  25. package/app/.next/server/app/api/pricing/route.js.nft.json +1 -1
  26. package/app/.next/server/app/api/providers/route.js +2 -1
  27. package/app/.next/server/app/api/providers/route.js.nft.json +1 -1
  28. package/app/.next/server/app/api/search/route.js +3 -2
  29. package/app/.next/server/app/api/search/route.js.nft.json +1 -1
  30. package/app/.next/server/app/api/sessions/[provider]/[id]/record/route/app-paths-manifest.json +3 -0
  31. package/app/.next/server/app/api/sessions/[provider]/[id]/record/route/build-manifest.json +12 -0
  32. package/app/.next/server/app/api/sessions/[provider]/[id]/record/route/server-reference-manifest.json +4 -0
  33. package/app/.next/server/app/api/sessions/[provider]/[id]/record/route.js +8 -0
  34. package/app/.next/server/app/api/sessions/[provider]/[id]/record/route.js.map +5 -0
  35. package/app/.next/server/app/api/sessions/[provider]/[id]/record/route.js.nft.json +1 -0
  36. package/app/.next/server/app/api/sessions/[provider]/[id]/record/route_client-reference-manifest.js +3 -0
  37. package/app/.next/server/app/api/sessions/[provider]/[id]/reveal/route.js +2 -1
  38. package/app/.next/server/app/api/sessions/[provider]/[id]/reveal/route.js.nft.json +1 -1
  39. package/app/.next/server/app/api/sessions/[provider]/[id]/route.js +2 -1
  40. package/app/.next/server/app/api/sessions/[provider]/[id]/route.js.nft.json +1 -1
  41. package/app/.next/server/app/api/sessions/route.js +2 -1
  42. package/app/.next/server/app/api/sessions/route.js.nft.json +1 -1
  43. package/app/.next/server/app/index.html +1 -1
  44. package/app/.next/server/app/index.rsc +3 -3
  45. package/app/.next/server/app/index.segments/__PAGE__.segment.rsc +3 -3
  46. package/app/.next/server/app/index.segments/_full.segment.rsc +3 -3
  47. package/app/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  48. package/app/.next/server/app/page.js.nft.json +1 -1
  49. package/app/.next/server/app/page_client-reference-manifest.js +1 -1
  50. package/app/.next/server/app/sessions/[provider]/[id]/page.js.nft.json +1 -1
  51. package/app/.next/server/app/sessions/[provider]/[id]/page_client-reference-manifest.js +1 -1
  52. package/app/.next/server/app-paths-manifest.json +1 -0
  53. package/app/.next/server/chunks/1oeh_server_app_api_sessions_[provider]_[id]_record_route_actions_13lvgh0.js +3 -0
  54. package/app/.next/server/chunks/[externals]__0xll52b._.js +3 -0
  55. package/app/.next/server/chunks/[root-of-the-server]__014br-h._.js +3 -0
  56. package/app/.next/server/chunks/[root-of-the-server]__03bee2l._.js +3 -0
  57. package/app/.next/server/chunks/[root-of-the-server]__093lyxv._.js +3 -0
  58. package/app/.next/server/chunks/[root-of-the-server]__0co499t._.js +3 -0
  59. package/app/.next/server/chunks/[root-of-the-server]__0ne74op._.js +3 -0
  60. package/app/.next/server/chunks/[root-of-the-server]__0p_hyq8._.js +3 -0
  61. package/app/.next/server/chunks/[root-of-the-server]__0ryrg2h._.js +3 -0
  62. package/app/.next/server/chunks/[root-of-the-server]__0uv5tnc._.js +3 -0
  63. package/app/.next/server/chunks/[root-of-the-server]__0yxic1z._.js +3 -3
  64. package/app/.next/server/chunks/[root-of-the-server]__1lfc-ga._.js +3 -0
  65. package/app/.next/server/chunks/[root-of-the-server]__1n_sepq._.js +3 -0
  66. package/app/.next/server/chunks/ssr/_0o294sf._.js +1 -1
  67. package/app/.next/server/chunks/ssr/_0wfo2z0._.js +1 -1
  68. package/app/.next/server/chunks/ssr/src_app_sessions_[provider]_[id]_page_tsx_0uf635h._.js +3 -3
  69. package/app/.next/server/middleware-build-manifest.js +3 -3
  70. package/app/.next/server/pages/404.html +1 -1
  71. package/app/.next/server/pages/500.html +1 -1
  72. package/app/.next/server/server-reference-manifest.js +1 -1
  73. package/app/.next/server/server-reference-manifest.json +1 -1
  74. package/app/.next/static/chunks/1lmw5sxsr57p6.css +1 -0
  75. package/app/.next/static/chunks/{1z_genu21d6mc.js → 1nuneih_2_kul.js} +1 -1
  76. package/app/.next/static/chunks/21ckxredfe3pr.js +6 -0
  77. package/app/package.json +1 -1
  78. package/app/server.js +1 -1
  79. package/cli/cli.mjs +159 -52
  80. package/package.json +1 -1
  81. package/app/.next/server/chunks/[root-of-the-server]__0dyq0rc._.js +0 -3
  82. package/app/.next/server/chunks/[root-of-the-server]__0kgkqhn._.js +0 -3
  83. package/app/.next/server/chunks/[root-of-the-server]__10p8y9z._.js +0 -3
  84. package/app/.next/server/chunks/[root-of-the-server]__1cceere._.js +0 -3
  85. package/app/.next/server/chunks/[root-of-the-server]__1eclfq8._.js +0 -3
  86. package/app/.next/server/chunks/[root-of-the-server]__1lz20wm._.js +0 -3
  87. package/app/.next/server/chunks/[root-of-the-server]__1zdh1mi._.js +0 -3
  88. package/app/.next/server/chunks/[root-of-the-server]__20epvou._.js +0 -3
  89. package/app/.next/static/chunks/1uvlni5bd236c.js +0 -6
  90. package/app/.next/static/chunks/3xq2kqjs_2byl.css +0 -1
  91. package/app/AGENTS.md +0 -9
  92. package/app/CLAUDE.md +0 -1
  93. package/app/CONTRIBUTING.md +0 -90
  94. package/app/Dockerfile +0 -34
  95. package/app/LICENSE +0 -21
  96. package/app/README.md +0 -369
  97. package/app/bin/agent-session-inspector.mjs +0 -185
  98. package/app/components.json +0 -25
  99. package/app/docs/screenshots/analytics-dark.png +0 -0
  100. package/app/docs/screenshots/analytics.png +0 -0
  101. package/app/docs/screenshots/session-overview-dark.png +0 -0
  102. package/app/docs/screenshots/session-overview.png +0 -0
  103. package/app/docs/screenshots/sessions-dark.png +0 -0
  104. package/app/docs/screenshots/sessions.png +0 -0
  105. package/app/docs/screenshots/timeline-dark.png +0 -0
  106. package/app/docs/screenshots/timeline.png +0 -0
  107. package/app/docs/screenshots/token-optimizer-dark.png +0 -0
  108. package/app/docs/screenshots/token-optimizer.png +0 -0
  109. package/app/docs/social-preview.png +0 -0
  110. package/app/eslint.config.mjs +0 -20
  111. package/app/install.sh +0 -85
  112. package/app/next.config.ts +0 -17
  113. package/app/pnpm-lock.yaml +0 -7031
  114. package/app/pnpm-workspace.yaml +0 -3
  115. package/app/postcss.config.mjs +0 -7
  116. package/app/scripts/build-cli.mjs +0 -26
  117. package/app/scripts/demo-data.mjs +0 -335
  118. package/app/scripts/deploy-docker.sh +0 -71
  119. package/app/scripts/package-release.sh +0 -86
  120. package/app/tsconfig.json +0 -34
  121. /package/app/.next/static/{rpBXcMOzD9z_w4Y9uQJJ- → muuOFZsnyOfIG_5aUuqFV}/_buildManifest.js +0 -0
  122. /package/app/.next/static/{rpBXcMOzD9z_w4Y9uQJJ- → muuOFZsnyOfIG_5aUuqFV}/_clientMiddlewareManifest.js +0 -0
  123. /package/app/.next/static/{rpBXcMOzD9z_w4Y9uQJJ- → muuOFZsnyOfIG_5aUuqFV}/_ssgManifest.js +0 -0
package/app/README.md DELETED
@@ -1,369 +0,0 @@
1
- # Agent Session Inspector
2
-
3
- [![npm version](https://img.shields.io/npm/v/agent-session-inspector)](https://www.npmjs.com/package/agent-session-inspector)
4
- [![CI](https://github.com/kishanmundha/agent-session-inspector/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/kishanmundha/agent-session-inspector/actions/workflows/ci.yml)
5
- [![node](https://img.shields.io/node/v/agent-session-inspector)](https://nodejs.org)
6
- [![license](https://img.shields.io/npm/l/agent-session-inspector)](LICENSE)
7
-
8
- See exactly where a coding-agent session's tokens and dollars went. A local
9
- inspector for Claude Code, Codex, GitHub Copilot, Cursor, Gemini CLI, OpenCode
10
- and Hermes transcripts: every prompt, thought, tool call and injected piece of
11
- context in order, each with its own token count and cost.
12
-
13
- It answers the questions a usage total cannot: why one session cost $12, which
14
- turn blew up the context, how much of it was system prompt and tool results
15
- rather than conversation, and which tool kept failing.
16
-
17
- > **Your sessions never leave your machine.** The app collects nothing: no
18
- > telemetry, no analytics, no account, no cloud. It reads the transcript files
19
- > already on your disk, read-only, and serves them to your own browser on
20
- > `127.0.0.1`. See [Privacy](#privacy) for the details and how to check.
21
-
22
- <picture>
23
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kishanmundha/agent-session-inspector/main/docs/screenshots/session-overview-dark.png">
24
- <img alt="Session overview: token, cost, health and timing stats above the event timeline" src="https://raw.githubusercontent.com/kishanmundha/agent-session-inspector/main/docs/screenshots/session-overview.png">
25
- </picture>
26
-
27
- ## Quick start
28
-
29
- Run it with [Node.js](https://nodejs.org) 22.13 or newer, nothing to install:
30
-
31
- ```bash
32
- npx agent-session-inspector
33
- ```
34
-
35
- It serves on [http://localhost:3000](http://localhost:3000) (or the next free
36
- port), bound to `127.0.0.1` only, and opens your browser. `--port`, `--host` and
37
- `--no-open` change that; `--help` lists everything.
38
-
39
- `npx agent-session-inspector@latest` picks up a new release. To keep the command
40
- around instead, install it globally:
41
-
42
- ```bash
43
- npm install -g agent-session-inspector
44
- ```
45
-
46
- ### Install script
47
-
48
- The same build is on GitHub Releases, with an installer that needs only Node,
49
- `curl` and `tar`:
50
-
51
- ```bash
52
- curl -fsSL https://github.com/kishanmundha/agent-session-inspector/releases/latest/download/install.sh | bash
53
- ```
54
-
55
- It puts the app in `~/.agent-session-inspector` and links an
56
- `agent-session-inspector` command into `~/.local/bin`. Re-run the install
57
- command to update. To uninstall:
58
-
59
- ```bash
60
- rm -rf ~/.agent-session-inspector ~/.local/bin/agent-session-inspector
61
- ```
62
-
63
- The install script needs bash, so it covers macOS, Linux and WSL. On Windows
64
- use `npx`.
65
-
66
- ## Features
67
-
68
- ### Inspect one session
69
-
70
- **Step-by-step timeline.** Prompts, reasoning, tool calls and their results in
71
- order, with per-step token counts and cost. What the agent was given shows up
72
- too: system prompt, tool catalog, MCP instructions and skill listings appear as
73
- events with their size. Expand any event to see its content, or open the raw
74
- record.
75
-
76
- <picture>
77
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kishanmundha/agent-session-inspector/main/docs/screenshots/timeline-dark.png">
78
- <img alt="Event timeline showing user prompt, thinking, and Grep/Read tool calls" src="https://raw.githubusercontent.com/kishanmundha/agent-session-inspector/main/docs/screenshots/timeline.png">
79
- </picture>
80
-
81
- **Cost and Token Optimizer.** The Cost tab shows what the session cost by token
82
- class, model and turn, with a table of every model request (context size,
83
- uncached input, cache write, cache read, output) grouped by turn. The Token
84
- Optimizer tab shows hints for trimming expensive sessions, where the context
85
- went (system prompt vs. tool results vs. replies) and which tools ran most.
86
-
87
- <picture>
88
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kishanmundha/agent-session-inspector/main/docs/screenshots/token-optimizer-dark.png">
89
- <img alt="Token Optimizer: estimated cost by token class and model, and a cost-by-turn chart" src="https://raw.githubusercontent.com/kishanmundha/agent-session-inspector/main/docs/screenshots/token-optimizer.png">
90
- </picture>
91
-
92
- **Session health.** A score out of 100 and a grade for how smoothly the session
93
- ran, with links to the failed tools, retry loops and interrupted turns that cost
94
- it points. See [Session health](#session-health).
95
-
96
- **Edits per turn.** A session's Edits tab lists each prompt with the files the
97
- agent wrote, edited or patched in response, linked to the event in the timeline.
98
-
99
- ### Find the session worth inspecting
100
-
101
- **Full-text search.** `⌘K` / `Ctrl+K` searches every transcript of every agent:
102
- prompts, replies, reasoning, tool arguments and tool results, as well as titles,
103
- repos and branches. Each hit shows the matching line and what kind of event it
104
- came from.
105
-
106
- **All sessions in one place.** Every session of every supported agent on the
107
- machine, filterable by agent. Each card shows turns, tool calls, health grade,
108
- cost and tokens in/out at a glance.
109
-
110
- <picture>
111
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kishanmundha/agent-session-inspector/main/docs/screenshots/sessions-dark.png">
112
- <img alt="Session list with agent filters, search and per-session health, cost and token counts" src="https://raw.githubusercontent.com/kishanmundha/agent-session-inspector/main/docs/screenshots/sessions.png">
113
- </picture>
114
-
115
- **Analytics across sessions.** Cost, messages, tool calls and active time for
116
- the selected range, with an activity calendar and filters for project, agent
117
- and model.
118
-
119
- <picture>
120
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/kishanmundha/agent-session-inspector/main/docs/screenshots/analytics-dark.png">
121
- <img alt="Analytics tab: cost, session and token totals above an activity calendar" src="https://raw.githubusercontent.com/kishanmundha/agent-session-inspector/main/docs/screenshots/analytics.png">
122
- </picture>
123
-
124
- **Projects.** Sessions are grouped by the repository they ran in, so work
125
- started from a subfolder or a git worktree lands in the same project. The
126
- Projects tab lists each one with its sessions, cost and tokens, and picking a
127
- project filters Analytics, Usage and Sessions together. Outside a repository,
128
- the working directory is the project; the dated scratch folders the Codex app
129
- creates for project-less chats count as one project.
130
-
131
- **Activity.** One day at a time: how many sessions were working at once in each
132
- 15-minute slot, the agent time and cost of each session, and the day's split by
133
- project, model and agent.
134
-
135
- **Skills and MCP tools.** The Analytics tab counts which skills were loaded and
136
- which MCP servers and tools were called, for the selected range.
137
-
138
- > Screenshots use synthetic demo data. To reproduce them locally, see
139
- > [CONTRIBUTING.md](CONTRIBUTING.md#demo-data).
140
-
141
- ## Terminal reports
142
-
143
- The same numbers are available without the web UI. Each command reads the
144
- transcripts, prints a report and exits:
145
-
146
- ```bash
147
- npx agent-session-inspector usage # tokens and estimated cost per day
148
- npx agent-session-inspector stats # sessions, messages, tools and time, by agent
149
- npx agent-session-inspector sessions # recent sessions with cost and health
150
- ```
151
-
152
- ```
153
- $ npx agent-session-inspector usage --by model --all
154
- Token usage · all time · by model
155
-
156
- Model Sessions Input Output Cache read Cache write Total Est. cost
157
- ───────────────────────── ──────── ───── ────── ────────── ─────────── ────── ─────────
158
- claude-opus-5-5 1 2.5K 3.4K 318.0K 23.1K 347.0K $0.26 ████████████████
159
- claude-sonnet-5 1 2.0K 2.5K 235.1K 19.1K 258.8K $0.12 ███████▊
160
- gpt-5.1 1 11.6K 1.6K 27.3K 0 40.5K $0.03 ██▏
161
- ───────────────────────── ──────── ───── ────── ────────── ─────────── ────── ─────────
162
- Total 3 16.1K 7.5K 580.4K 42.2K 646.3K $0.41
163
- ```
164
-
165
- Reports cover the last 30 days unless `--days <n>` or `--all` says otherwise.
166
- `--agent`, `--project` and `--model` narrow them, and `--json` prints the data
167
- for scripts. `usage` groups by day by default; `--by` takes `week`, `month`,
168
- `project`, `model` or `agent`. `sessions --sort cost` lists the most expensive
169
- sessions first. `<command> --help` lists every option.
170
-
171
- #### Scripting
172
-
173
- The tables are for reading: values are rounded, and piping one drops the rules
174
- and bars but keeps the rounding. For automation use `--json`, which prints exact
175
- numbers under stable names:
176
-
177
- ```bash
178
- npx agent-session-inspector usage --days 1 --json | jq '.total.costUSD'
179
- npx agent-session-inspector sessions --sort cost --json | jq -r '.sessions[].id'
180
- ```
181
-
182
- Every report carries `schemaVersion` (currently `1`) and `days` (`null` for all
183
- time). New fields may appear within a version; a field is renamed or removed
184
- only with a new one. Costs are US dollars, or `null` where no price is known.
185
-
186
- | Command | Fields |
187
- | ---------- | -------------------------------------------------------------------------------------------------------- |
188
- | `usage` | `by`, `rows[]`, `total`, `unpricedModels`. A row has `key`, `sessions`, `inputTokens`, `outputTokens`, `cacheReadTokens`, `cacheWriteTokens`, `totalTokens`, `costUSD`; `total` has the same without `key`. |
189
- | `stats` | `totals`, `messagesPerSession`, `agents[]`, `projects[]`, `tools[]`, `skills[]`, `mcpServers[]`, `unpricedModels`. |
190
- | `sessions` | `sort`, `total` (matches before `--limit`), `sessions[]`. A session has `agent`, `id`, `title`, `project`, `model`, `createdAt`, `updatedAt`, `userMessages`, `toolCalls`, `inputTokens`, `outputTokens`, `estimatedCostUSD`, `health`. |
191
-
192
- ## Supported agents
193
-
194
- | Agent | Reads from | Session titles | Logs |
195
- | ----------------------------- | ---------------------------------------------- | --------------------- | ---- |
196
- | GitHub Copilot CLI | `~/.copilot/session-state`, `~/.copilot/logs` | checkpoint DB | yes |
197
- | GitHub Copilot Chat (VS Code) | `<Code>/User/workspaceStorage/*/chatSessions` | chat title | no |
198
- | Claude Code | `~/.claude/projects/<project>/<session>.jsonl` | `custom`/`ai` titles | no |
199
- | Claude Cowork | `<Claude>/local-agent-mode-sessions` | session descriptor | no |
200
- | OpenAI Codex CLI | `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl` | `session_index.jsonl` | no |
201
- | OpenCode | `~/.local/share/opencode/opencode.db` | `session` table | no |
202
- | Hermes Agent | `~/.hermes/state.db` | `sessions` table | no |
203
- | Gemini CLI | `~/.gemini/tmp/<project>/chats/session-*` | session summary | no |
204
- | Cursor | `<Cursor>/User/globalStorage/state.vscdb` | chat name | no |
205
-
206
- `<Code>`, `<Claude>` and `<Cursor>` are the desktop apps' data folders:
207
- `~/Library/Application Support/…` on macOS, `%APPDATA%\…` on Windows,
208
- `~/.config/…` on Linux. VS Code Insiders is read too. The SQLite stores are
209
- opened read-only.
210
-
211
- Three things differ from the CLIs that log every request:
212
-
213
- - **Copilot Chat** meters a whole request, and its prompt count is that of the
214
- last model round. For a request that called tools, input tokens and cost are a
215
- floor.
216
- - **Hermes** counts tokens per session, so its usage appears as one checkpoint at
217
- the end of the timeline rather than per message.
218
- - **Cursor** rarely writes token counts to disk, so most of its sessions show the
219
- timeline, tools and timing but no tokens or cost. Only the chat table of
220
- `state.vscdb` is read. A session that is not in it, such as one from the
221
- Cursor CLI, is read from the plainer transcript under `~/.cursor/projects`,
222
- which has no tool results and is timed to the minute.
223
-
224
- Whichever directories exist on the machine show up; the rest are hidden.
225
-
226
- ## Privacy
227
-
228
- Transcripts hold your prompts, code and file contents, so the app is built to
229
- keep them on the machine. Nothing about you or your sessions is collected, by
230
- this project or anyone else:
231
-
232
- - **No data collection.** There is no telemetry, analytics, crash reporting,
233
- account or sync, and Next.js's own telemetry is switched off.
234
- - **No outbound requests.** The app reads the session files in place and talks
235
- only to your browser. Fonts and the price table used for cost estimates are
236
- bundled, not fetched. The one download is the app itself, when `npx` or the
237
- install script fetches it.
238
- - **Local only.** The server binds to `127.0.0.1`, so other machines on your
239
- network cannot reach it unless you pass `--host`.
240
- - **Read-only.** Session files are only read, and the SQLite stores are opened
241
- read-only. The app never edits or deletes a transcript.
242
-
243
- You don't have to take this on trust. The code is open, and the browser's
244
- Network tab shows every request going to `localhost` and nowhere else; the app
245
- works the same with the network disconnected.
246
-
247
- ## How it compares
248
-
249
- [AgentsView](https://github.com/kenn-io/agentsview) and
250
- [ccusage](https://github.com/ryoppippi/ccusage) read the same transcript files.
251
- AgentsView is an archive and search tool across all your sessions; ccusage
252
- prints usage and cost reports in the terminal. This project is for the step
253
- after that: opening one session to see what the agent was given, what it did
254
- with it, and what each step cost. It keeps no database and makes no outbound
255
- requests.
256
-
257
- ## How it works
258
-
259
- Every agent writes a different transcript format, so the app normalizes them into
260
- one canonical event vocabulary and the UI only ever sees that.
261
-
262
- ```
263
- src/lib/providers/
264
- types.ts canonical model: SessionMeta, AgentEvent, SessionDetail, SessionProvider
265
- analysis.ts provider-agnostic stats + token-cost hints
266
- pricing.ts dated price table lookup, per-event and per-session cost
267
- prices.json bundled list prices, USD per million tokens
268
- fs-utils.ts jsonl reading, directory walking, mtime-keyed caching
269
- sqlite-utils.ts read-only access to agents that keep sessions in SQLite
270
- copilot.ts ~/.copilot adapter
271
- vscode.ts Copilot Chat in VS Code: replays the editor's chat change logs
272
- claude.ts ~/.claude adapter
273
- cowork.ts Claude desktop agent mode; reuses the Claude transcript parser
274
- codex.ts ~/.codex adapter
275
- opencode.ts OpenCode's opencode.db
276
- hermes.ts Hermes Agent's state.db
277
- gemini.ts Gemini CLI: replays the session logs under ~/.gemini/tmp
278
- cursor.ts Cursor's state.vscdb, else its ~/.cursor/projects transcripts
279
- index.ts registry: listSessions / getSession / listLogs across providers
280
- ```
281
-
282
- Events are typed `category.subCategory` — `user.message`, `assistant.message`,
283
- `assistant.thinking`, `tool.execution_start`, `tool.execution_complete`,
284
- `external_tool.requested`, `context.attachment`, `file.patch_applied`,
285
- `web.search`, `session.*`. Anything an adapter emits outside that list still
286
- renders through a generic card, so a new record type is never a crash.
287
-
288
- Token accounting is centralized: adapters put per-message usage on the event
289
- (`inputTokens` / `outputTokens` / `cacheReadTokens`) or, when a provider reports
290
- running totals, attach `data.sessionTotals`. The latest `sessionTotals` wins over
291
- summation.
292
-
293
- ### Cost estimates
294
-
295
- Each session, and each request in the timeline, shows an estimated cost in
296
- dollars. Adapters attach `data.billedUsage` to the events that carry usage, with
297
- uncached input, cache reads, cache writes and output kept as separate,
298
- non-overlapping counts, and `pricing.ts` multiplies them by the model's list
299
- price. The Cost tab splits the total by token class and by model.
300
-
301
- The figure is an API-equivalent estimate, not a bill: subscription and
302
- request-based plans charge differently, and requests an agent makes outside the
303
- transcript (title generation, for example) are not counted.
304
-
305
- Prices are never stored with a session; cost is computed on every load. To
306
- correct a price, add a model the bundled table does not know, or record a price
307
- change, create `~/.agent-session-inspector/pricing.json`. It is merged over
308
- [`prices.json`](src/lib/providers/prices.json) per model and picked up without a
309
- restart:
310
-
311
- ```json
312
- {
313
- "my-local-model": { "input": 0, "output": 0 },
314
- "claude-opus-5-5": [
315
- {
316
- "input": 4,
317
- "output": 20,
318
- "cacheRead": 0.2,
319
- "cacheWrite": 5,
320
- "cacheWrite1h": 8
321
- },
322
- {
323
- "from": "2027-01-01",
324
- "input": 3,
325
- "output": 15,
326
- "cacheRead": 0.15,
327
- "cacheWrite": 3.75,
328
- "cacheWrite1h": 6
329
- }
330
- ]
331
- }
332
- ```
333
-
334
- A model with several entries is priced by date: each request uses the entry
335
- whose `from` day is the latest one on or before the request, so a price change
336
- does not rewrite the cost of older sessions. Models with no price show as
337
- unpriced and are left out of the total rather than counted as free.
338
-
339
- ### Session health
340
-
341
- Every session gets a score out of 100 and a grade (A to F), shown on its card
342
- and in its header. The score is rule-based and read straight off the transcript
343
- by `health.ts`: it measures how smoothly the session ran, not whether the result
344
- was any good. A session starts at 100 and loses points for:
345
-
346
- | Signal | What it means | Points |
347
- | ----------------- | ---------------------------------------------------- | -------------- |
348
- | Unfinished | Ends on an API error, or stops without a final reply | 25 / 15 |
349
- | Tool failures | Share of tool results that failed | up to 25 |
350
- | Retry loops | The same tool failing three times in a row | 6 each, max 18 |
351
- | API errors | Errors and retried requests | 2 each, max 10 |
352
- | Interrupted turns | Turns stopped before the agent finished | 4 each, max 12 |
353
- | Compactions | Context summarized mid-session | 3 each, max 9 |
354
-
355
- 90 and up is an A, 80 a B, 70 a C, 60 a D. A session that is still running is not
356
- marked down for being unfinished. Click the Health card on a session to see what
357
- it lost points for and jump to those events; the Quality tab on the home page
358
- shows grades, outcomes and the most common problems across sessions.
359
-
360
- ## Contributing
361
-
362
- Bug reports and pull requests are welcome; open an
363
- [issue](https://github.com/kishanmundha/agent-session-inspector/issues) for anything that looks wrong, ideally with the
364
- agent, OS and Node version. [CONTRIBUTING.md](CONTRIBUTING.md) covers running
365
- from source, demo data, adding a provider and releasing.
366
-
367
- ## License
368
-
369
- [MIT](LICENSE) © Kishan Mundha
@@ -1,185 +0,0 @@
1
- #!/usr/bin/env node
2
- // Launcher for the packaged (standalone) build, run by npx or the installed
3
- // command. Lives at <package>/bin/ beside app/, which holds the server.js that
4
- // `next build` emits; see scripts/package-release.sh.
5
- import { spawn } from "node:child_process";
6
- import { existsSync, readFileSync } from "node:fs";
7
- import { createRequire } from "node:module";
8
- import net from "node:net";
9
- import path from "node:path";
10
- import { fileURLToPath, pathToFileURL } from "node:url";
11
-
12
- const NAME = "agent-session-inspector";
13
- const MIN_NODE = [22, 13];
14
- const PKG_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
15
- const SERVER = path.join(PKG_DIR, "app", "server.js");
16
-
17
- // Report commands print to the terminal instead of starting the server; they
18
- // live in the bundle scripts/build-cli.mjs writes to <package>/cli/.
19
- const CLI = path.join(PKG_DIR, "cli", "cli.mjs");
20
- const COMMANDS = ["usage", "stats", "sessions"];
21
-
22
- const HELP = `Usage: ${NAME} [options]
23
- ${NAME} <command> [options]
24
-
25
- Browse Claude Code, Codex, Copilot, Cursor, Gemini CLI, OpenCode and Hermes
26
- session transcripts in a local web UI, or print a report in the terminal.
27
-
28
- Commands:
29
- usage Tokens and estimated cost by day, model, project or agent
30
- stats Sessions, messages, tool calls and time, by agent
31
- sessions Recent sessions with their cost and health
32
-
33
- Run a command with --help for its options.
34
-
35
- Options:
36
- -p, --port <port> Port to listen on (default: 3000, or the next free one)
37
- -H, --host <host> Interface to bind (default: 127.0.0.1)
38
- --no-open Don't open the browser
39
- -v, --version Print the version
40
- -h, --help Show this help
41
- `;
42
-
43
- function fail(message) {
44
- console.error(`${NAME}: ${message}`);
45
- process.exit(1);
46
- }
47
-
48
- function parseArgs(argv) {
49
- const opts = { port: null, host: "127.0.0.1", open: true };
50
- for (let i = 0; i < argv.length; i++) {
51
- const arg = argv[i];
52
- const [flag, inline] = arg.startsWith("--") ? arg.split("=", 2) : [arg];
53
- const value = () => inline ?? argv[++i] ?? fail(`${flag} needs a value`);
54
- switch (flag) {
55
- case "-p":
56
- case "--port": {
57
- const port = Number(value());
58
- if (!Number.isInteger(port) || port < 1 || port > 65535) fail("invalid port");
59
- opts.port = port;
60
- break;
61
- }
62
- case "-H":
63
- case "--host":
64
- opts.host = value();
65
- break;
66
- case "--no-open":
67
- opts.open = false;
68
- break;
69
- case "-v":
70
- case "--version":
71
- console.log(readVersion());
72
- process.exit(0);
73
- break;
74
- case "-h":
75
- case "--help":
76
- console.log(HELP);
77
- process.exit(0);
78
- break;
79
- default:
80
- fail(`unknown option ${arg}\n\n${HELP}`);
81
- }
82
- }
83
- return opts;
84
- }
85
-
86
- function readVersion() {
87
- try {
88
- return readFileSync(path.join(PKG_DIR, "VERSION"), "utf8").trim();
89
- } catch {
90
- return "dev";
91
- }
92
- }
93
-
94
- function isFree(port, host) {
95
- return new Promise((resolve) => {
96
- const probe = net.createServer();
97
- probe.once("error", () => resolve(false));
98
- probe.listen(port, host, () => probe.close(() => resolve(true)));
99
- });
100
- }
101
-
102
- async function pickPort(preferred, host) {
103
- for (let port = preferred; port < preferred + 20; port++) {
104
- if (await isFree(port, host)) return port;
105
- }
106
- fail(`no free port between ${preferred} and ${preferred + 19}; pass --port`);
107
- }
108
-
109
- async function waitUntilUp(url) {
110
- for (let i = 0; i < 100; i++) {
111
- try {
112
- await fetch(url, { method: "HEAD" });
113
- return true;
114
- } catch {
115
- await new Promise((r) => setTimeout(r, 100));
116
- }
117
- }
118
- return false;
119
- }
120
-
121
- function openBrowser(url) {
122
- const [cmd, args] =
123
- process.platform === "darwin"
124
- ? ["open", [url]]
125
- : process.platform === "win32"
126
- ? ["cmd", ["/c", "start", "", url]]
127
- : ["xdg-open", [url]];
128
- // No browser (ssh, container) is fine; the URL is already printed.
129
- spawn(cmd, args, { stdio: "ignore", detached: true })
130
- .on("error", () => {})
131
- .unref();
132
- }
133
-
134
- const argv = process.argv.slice(2);
135
- const command = COMMANDS.includes(argv[0]) ? argv[0] : null;
136
- if (!command && argv[0] && !argv[0].startsWith("-")) {
137
- fail(`unknown command ${argv[0]}\n\n${HELP}`);
138
- }
139
- const opts = command ? null : parseArgs(argv);
140
-
141
- // npx only warns about package.json "engines", and the app needs node:sqlite.
142
- const [major, minor] = process.versions.node.split(".").map(Number);
143
- if (major < MIN_NODE[0] || (major === MIN_NODE[0] && minor < MIN_NODE[1])) {
144
- fail(`Node.js ${MIN_NODE.join(".")} or newer is required (found ${process.version}).`);
145
- }
146
-
147
- // node:sqlite still announces itself as experimental on Node 22; not useful here.
148
- const emitWarning = process.emitWarning;
149
- process.emitWarning = (warning, ...rest) => {
150
- if (String(warning).includes("SQLite is an experimental feature")) return;
151
- emitWarning.call(process, warning, ...rest);
152
- };
153
-
154
- if (command) {
155
- if (!existsSync(CLI)) {
156
- fail(`no command bundle found at ${CLI}\nRun \`pnpm cli ${command}\` from a checkout.`);
157
- }
158
- const { run } = await import(pathToFileURL(CLI).href);
159
- process.exit(run(command, argv.slice(1)));
160
- }
161
-
162
- if (!existsSync(SERVER)) {
163
- fail(`no build found at ${SERVER}\nRun scripts/package-release.sh and use dist/${NAME}/bin/${NAME}.mjs.`);
164
- }
165
-
166
- let port = opts.port;
167
- if (port === null) {
168
- port = await pickPort(Number(process.env.PORT) || 3000, opts.host);
169
- } else if (!(await isFree(port, opts.host))) {
170
- fail(`port ${port} is already in use`);
171
- }
172
-
173
- process.env.NODE_ENV = "production";
174
- process.env.NEXT_TELEMETRY_DISABLED = "1";
175
- process.env.PORT = String(port);
176
- process.env.HOSTNAME = opts.host;
177
-
178
- // server.js is CommonJS and starts listening on load.
179
- createRequire(import.meta.url)(SERVER);
180
-
181
- if (opts.open) {
182
- const browseHost = opts.host === "0.0.0.0" || opts.host === "::" ? "localhost" : opts.host;
183
- const url = `http://${browseHost.includes(":") ? `[${browseHost}]` : browseHost}:${port}`;
184
- if (await waitUntilUp(url)) openBrowser(url);
185
- }
@@ -1,25 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema.json",
3
- "style": "base-nova",
4
- "rsc": true,
5
- "tsx": true,
6
- "tailwind": {
7
- "config": "",
8
- "css": "src/app/globals.css",
9
- "baseColor": "neutral",
10
- "cssVariables": true,
11
- "prefix": ""
12
- },
13
- "iconLibrary": "lucide",
14
- "rtl": false,
15
- "aliases": {
16
- "components": "@/components",
17
- "utils": "@/lib/utils",
18
- "ui": "@/components/ui",
19
- "lib": "@/lib",
20
- "hooks": "@/hooks"
21
- },
22
- "menuColor": "default",
23
- "menuAccent": "subtle",
24
- "registries": {}
25
- }
Binary file
Binary file
Binary file
Binary file
@@ -1,20 +0,0 @@
1
- import { defineConfig, globalIgnores } from "eslint/config";
2
- import nextVitals from "eslint-config-next/core-web-vitals";
3
- import nextTs from "eslint-config-next/typescript";
4
-
5
- const eslintConfig = defineConfig([
6
- ...nextVitals,
7
- ...nextTs,
8
- // Override default ignores of eslint-config-next.
9
- globalIgnores([
10
- // Default ignores of eslint-config-next:
11
- ".next/**",
12
- "out/**",
13
- "build/**",
14
- "dist/**",
15
- "cli/**",
16
- "next-env.d.ts",
17
- ]),
18
- ]);
19
-
20
- export default eslintConfig;