agenttelemetry-app 2.2.0__tar.gz → 2.3.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.
- {agenttelemetry_app-2.2.0/agenttelemetry_app.egg-info → agenttelemetry_app-2.3.0}/PKG-INFO +33 -18
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/README.md +32 -17
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/__init__.py +1 -1
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/accounts.py +3 -3
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0/agenttelemetry_app.egg-info}/PKG-INFO +33 -18
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/dashboard.py +25 -15
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/index.html +4 -2
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/native.py +99 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/native_activity.py +7 -3
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/parser.py +36 -13
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/pyproject.toml +1 -1
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/release_notes.py +3 -1
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/session_names.py +28 -12
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/core.js +7 -1
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/views.js +9 -3
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/LICENSE +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/MANIFEST.in +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/__main__.py +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/agenttelemetry_app.egg-info/SOURCES.txt +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/agenttelemetry_app.egg-info/dependency_links.txt +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/agenttelemetry_app.egg-info/entry_points.txt +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/agenttelemetry_app.egg-info/top_level.txt +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/antigravity.py +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/chart.umd.min.js +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/cli.py +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/cursor_data.py +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/diagnostics.py +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/docs/antigravity.md +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/docs/native-activity.md +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/docs/python-package.md +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/docs/release-notes.md +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/editor_snapshot.py +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/ledger_backups.py +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/local_servers.py +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/local_servers_windows.py +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/manifest.json +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/session_timeline.py +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/setup.cfg +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/setup.py +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/app.css +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/apple-touch-icon.png +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/charts.js +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/icon-192.png +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/icon-512.png +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/maintenance.js +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/release-notes.js +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/timeline.js +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/LICENSE.antigravity.txt +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/LICENSE.hermes.txt +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/LICENSE.openclaw.txt +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/LICENSE.simple-icons.txt +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/README.md +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/antigravity.svg +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/claude.svg +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/copilot.svg +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/cursor.svg +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/hermes.svg +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/openai.svg +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/openclaw.svg +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/opencode.svg +0 -0
- {agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/sw.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agenttelemetry-app
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: A private, local dashboard for AI coding agent usage
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/uttamdeb/agent-telemetry
|
|
@@ -47,6 +47,12 @@ Covers **Claude Code · Claude Desktop · Codex · GitHub Copilot · Cursor · A
|
|
|
47
47
|
[Browser dashboard (Python or source)](#quick-start) ·
|
|
48
48
|
[Product page](https://agenttelemetry-app.vercel.app/)
|
|
49
49
|
|
|
50
|
+
**AgentTelemetry Pro (optional).** Everything above stays free, open source and local. [AgentTelemetry
|
|
51
|
+
Pro](https://agenttelemetry-app.vercel.app/#pro) is a separate add-on with a sign-in: your dashboard in any browser or
|
|
52
|
+
on your phone; chat with your running Claude Code and Codex sessions from the web, your phone or the Pro app on Mac
|
|
53
|
+
and Windows; answering Claude Code's permission prompts from your phone; sessions that message each other across
|
|
54
|
+
your devices; Optimize; and the local MCP bridge. See [Free vs Pro](https://agenttelemetry-app.vercel.app/#pro).
|
|
55
|
+
|
|
50
56
|
## Download the app
|
|
51
57
|
|
|
52
58
|
Choose the installer for your platform from the release's **Assets** section.
|
|
@@ -70,9 +76,9 @@ These builds are unsigned/unnotarized. macOS may require **System Settings → P
|
|
|
70
76
|
Security → Open Anyway**; Windows SmartScreen may require **More info → Run anyway**.
|
|
71
77
|
[Product and install page](https://agenttelemetry-app.vercel.app/) ·
|
|
72
78
|
[All releases](https://github.com/uttamdeb/agent-telemetry/releases) ·
|
|
73
|
-
[What's new in 2.
|
|
79
|
+
[What's new in 2.3.0](docs/releases/v2.3.0.md) ·
|
|
74
80
|
[SHA-256 checksums in release assets](https://github.com/uttamdeb/agent-telemetry/releases/latest) ·
|
|
75
|
-
[PyPI package 2.
|
|
81
|
+
[PyPI package 2.3.0](https://pypi.org/project/agenttelemetry-app/2.3.0/)
|
|
76
82
|
|
|
77
83
|
---
|
|
78
84
|
|
|
@@ -386,12 +392,16 @@ anything is saved. Close the session in its original tool before confirming, and
|
|
|
386
392
|
that tool if needed. Token, cost, prompt, tool-call and compaction records stay intact.
|
|
387
393
|
Previous title metadata is backed up privately in
|
|
388
394
|
`AgentTelemetryNative/session-title-backups/`, separately from the usage ledger.
|
|
389
|
-
This supports local Claude Code transcripts, Codex through its
|
|
395
|
+
This supports local Claude Code transcripts, Codex through its
|
|
390
396
|
[official name API](https://learn.chatgpt.com/docs/app-server), and OpenCode’s SQLite
|
|
391
|
-
session stores.
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
397
|
+
session stores. Codex's program is found wherever its apps install it (the ChatGPT app,
|
|
398
|
+
the Codex editor extension, the Codex desktop app on Windows, or a Codex CLI), with no
|
|
399
|
+
need for it on your PATH. Remote sessions and deleted logs cannot be renamed.
|
|
400
|
+
Recognized local Claude subagent transcripts, and sessions from tools whose own title
|
|
401
|
+
can't be edited safely (Copilot, Cursor, Claude Desktop, Antigravity, Hermes, OpenClaw),
|
|
402
|
+
instead offer **Name in AgentTelemetry**. This saves a private display name on this
|
|
403
|
+
device without editing the tool's files, Claude's transcript, native subagent name, or
|
|
404
|
+
parent session. The preview states this scope explicitly.
|
|
395
405
|
These names live in `AgentTelemetryNative/session-display-names.json`, outside the
|
|
396
406
|
usage ledger, summaries, and device sharing. Enter the source title again to remove
|
|
397
407
|
a display-name override. See [subagent name behavior](docs/subagent-sessions.md).
|
|
@@ -479,9 +489,10 @@ dollars**, so cost is always derived. Rates live in `parser.py → PRICING` as
|
|
|
479
489
|
`(input, output, cache_write_5m, cache_write_1h, cache_read)` per 1M tokens; edit freely
|
|
480
490
|
(recomputed on each request, no re-parse needed).
|
|
481
491
|
|
|
482
|
-
- **Anthropic** rates are current list prices (
|
|
483
|
-
|
|
484
|
-
read 0.1× — 0.05× on Opus 5.5
|
|
492
|
+
- **Anthropic** rates are current list prices (Fable & Mythos 5.2 $8/$40, Fable & Mythos 5/5.1
|
|
493
|
+
$10/$50, Opus 5.5 $4/$20, Opus 5 & 4.x $5/$25, Sonnet 5 & 5.5 $2/$10, Sonnet 4.x $3/$15, Haiku
|
|
494
|
+
4.5 $1/$5; cache write 1.25×/2× input for 5-min/1-hour, cache read 0.1× — 0.05× on Opus 5.5 and
|
|
495
|
+
Sonnet 5.5, 0.025× on Fable & Mythos 5.1/5.2). **OpenAI** GPT-5.4/5.5/5.6/6 are verified from OpenAI docs;
|
|
485
496
|
older/other models are estimates.
|
|
486
497
|
- **Haiku 5.5** uses [Anthropic's published rates](https://platform.claude.com/docs/en/about-claude/pricing):
|
|
487
498
|
$0.10/$0.50 per million input/output tokens up to 100K prompt tokens, $0.50/$2.50
|
|
@@ -490,7 +501,8 @@ dollars**, so cost is always derived. Rates live in `parser.py → PRICING` as
|
|
|
490
501
|
message counts stay unchanged. Older archives/peers and other tools without that
|
|
491
502
|
detail use the short-prompt rate.
|
|
492
503
|
- **Price changes are date-aware.** When a vendor cuts a price (OpenAI's GPT-5.6 cuts of
|
|
493
|
-
2026-07-30 and 2026-08-21
|
|
504
|
+
2026-07-30 and 2026-08-21, Anthropic's Sonnet 5.5 cache-read cut of 2026-10-07), usage
|
|
505
|
+
from before the change keeps the rate it had then —
|
|
494
506
|
see `parser.py → PRICE_HISTORY`.
|
|
495
507
|
- **These are API-equivalent values.** If you're on a subscription (Claude Max/Pro, Codex,
|
|
496
508
|
Copilot), you don't pay per token — the $ is "what this would cost at API rates."
|
|
@@ -512,12 +524,15 @@ dollars**, so cost is always derived. Rates live in `parser.py → PRICING` as
|
|
|
512
524
|
when it runs through supported tools, at the Standard rate for prompts up to 200K
|
|
513
525
|
tokens. Live, TTS, image and embedding models are not priced, and nor are the higher Pro rate
|
|
514
526
|
for longer prompts or audio input. The Gemini CLI itself writes no token counts to read.
|
|
515
|
-
- **Gemini 4 Argon
|
|
516
|
-
[
|
|
517
|
-
$2 input, $10 output and $0.10 cached input per 1M tokens
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
527
|
+
- **Gemini 4 Argon and Gemini 4 Carbon** (`gemini-4-argon`, `gemini-4-carbon`) are recognized
|
|
528
|
+
across the supported tools and use Google's [published rates](https://aistudio.google.com/docs/pricing):
|
|
529
|
+
$2 input, $10 output and $0.10 cached input per 1M tokens through November 30, 2026. Google
|
|
530
|
+
doubles them to $4, $20 and $0.20 on December 1, 2026. The higher rate is not applied ahead
|
|
531
|
+
of time: it goes in once it takes effect, and earlier usage keeps the rate it had then.
|
|
532
|
+
Cache storage charges are not modeled.
|
|
533
|
+
- **Gemini 4 Hydrogen** (`gemini-4-hydrogen`) uses Google's paid rate: $1.50 input, $7.50 output
|
|
534
|
+
and $0.075 cached input per 1M tokens. Its free tier can't be told apart in logs, so usage
|
|
535
|
+
is shown at the paid rate.
|
|
521
536
|
|
|
522
537
|
## Note on log retention
|
|
523
538
|
|
|
@@ -23,6 +23,12 @@ Covers **Claude Code · Claude Desktop · Codex · GitHub Copilot · Cursor · A
|
|
|
23
23
|
[Browser dashboard (Python or source)](#quick-start) ·
|
|
24
24
|
[Product page](https://agenttelemetry-app.vercel.app/)
|
|
25
25
|
|
|
26
|
+
**AgentTelemetry Pro (optional).** Everything above stays free, open source and local. [AgentTelemetry
|
|
27
|
+
Pro](https://agenttelemetry-app.vercel.app/#pro) is a separate add-on with a sign-in: your dashboard in any browser or
|
|
28
|
+
on your phone; chat with your running Claude Code and Codex sessions from the web, your phone or the Pro app on Mac
|
|
29
|
+
and Windows; answering Claude Code's permission prompts from your phone; sessions that message each other across
|
|
30
|
+
your devices; Optimize; and the local MCP bridge. See [Free vs Pro](https://agenttelemetry-app.vercel.app/#pro).
|
|
31
|
+
|
|
26
32
|
## Download the app
|
|
27
33
|
|
|
28
34
|
Choose the installer for your platform from the release's **Assets** section.
|
|
@@ -46,9 +52,9 @@ These builds are unsigned/unnotarized. macOS may require **System Settings → P
|
|
|
46
52
|
Security → Open Anyway**; Windows SmartScreen may require **More info → Run anyway**.
|
|
47
53
|
[Product and install page](https://agenttelemetry-app.vercel.app/) ·
|
|
48
54
|
[All releases](https://github.com/uttamdeb/agent-telemetry/releases) ·
|
|
49
|
-
[What's new in 2.
|
|
55
|
+
[What's new in 2.3.0](docs/releases/v2.3.0.md) ·
|
|
50
56
|
[SHA-256 checksums in release assets](https://github.com/uttamdeb/agent-telemetry/releases/latest) ·
|
|
51
|
-
[PyPI package 2.
|
|
57
|
+
[PyPI package 2.3.0](https://pypi.org/project/agenttelemetry-app/2.3.0/)
|
|
52
58
|
|
|
53
59
|
---
|
|
54
60
|
|
|
@@ -362,12 +368,16 @@ anything is saved. Close the session in its original tool before confirming, and
|
|
|
362
368
|
that tool if needed. Token, cost, prompt, tool-call and compaction records stay intact.
|
|
363
369
|
Previous title metadata is backed up privately in
|
|
364
370
|
`AgentTelemetryNative/session-title-backups/`, separately from the usage ledger.
|
|
365
|
-
This supports local Claude Code transcripts, Codex through its
|
|
371
|
+
This supports local Claude Code transcripts, Codex through its
|
|
366
372
|
[official name API](https://learn.chatgpt.com/docs/app-server), and OpenCode’s SQLite
|
|
367
|
-
session stores.
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
373
|
+
session stores. Codex's program is found wherever its apps install it (the ChatGPT app,
|
|
374
|
+
the Codex editor extension, the Codex desktop app on Windows, or a Codex CLI), with no
|
|
375
|
+
need for it on your PATH. Remote sessions and deleted logs cannot be renamed.
|
|
376
|
+
Recognized local Claude subagent transcripts, and sessions from tools whose own title
|
|
377
|
+
can't be edited safely (Copilot, Cursor, Claude Desktop, Antigravity, Hermes, OpenClaw),
|
|
378
|
+
instead offer **Name in AgentTelemetry**. This saves a private display name on this
|
|
379
|
+
device without editing the tool's files, Claude's transcript, native subagent name, or
|
|
380
|
+
parent session. The preview states this scope explicitly.
|
|
371
381
|
These names live in `AgentTelemetryNative/session-display-names.json`, outside the
|
|
372
382
|
usage ledger, summaries, and device sharing. Enter the source title again to remove
|
|
373
383
|
a display-name override. See [subagent name behavior](docs/subagent-sessions.md).
|
|
@@ -455,9 +465,10 @@ dollars**, so cost is always derived. Rates live in `parser.py → PRICING` as
|
|
|
455
465
|
`(input, output, cache_write_5m, cache_write_1h, cache_read)` per 1M tokens; edit freely
|
|
456
466
|
(recomputed on each request, no re-parse needed).
|
|
457
467
|
|
|
458
|
-
- **Anthropic** rates are current list prices (
|
|
459
|
-
|
|
460
|
-
read 0.1× — 0.05× on Opus 5.5
|
|
468
|
+
- **Anthropic** rates are current list prices (Fable & Mythos 5.2 $8/$40, Fable & Mythos 5/5.1
|
|
469
|
+
$10/$50, Opus 5.5 $4/$20, Opus 5 & 4.x $5/$25, Sonnet 5 & 5.5 $2/$10, Sonnet 4.x $3/$15, Haiku
|
|
470
|
+
4.5 $1/$5; cache write 1.25×/2× input for 5-min/1-hour, cache read 0.1× — 0.05× on Opus 5.5 and
|
|
471
|
+
Sonnet 5.5, 0.025× on Fable & Mythos 5.1/5.2). **OpenAI** GPT-5.4/5.5/5.6/6 are verified from OpenAI docs;
|
|
461
472
|
older/other models are estimates.
|
|
462
473
|
- **Haiku 5.5** uses [Anthropic's published rates](https://platform.claude.com/docs/en/about-claude/pricing):
|
|
463
474
|
$0.10/$0.50 per million input/output tokens up to 100K prompt tokens, $0.50/$2.50
|
|
@@ -466,7 +477,8 @@ dollars**, so cost is always derived. Rates live in `parser.py → PRICING` as
|
|
|
466
477
|
message counts stay unchanged. Older archives/peers and other tools without that
|
|
467
478
|
detail use the short-prompt rate.
|
|
468
479
|
- **Price changes are date-aware.** When a vendor cuts a price (OpenAI's GPT-5.6 cuts of
|
|
469
|
-
2026-07-30 and 2026-08-21
|
|
480
|
+
2026-07-30 and 2026-08-21, Anthropic's Sonnet 5.5 cache-read cut of 2026-10-07), usage
|
|
481
|
+
from before the change keeps the rate it had then —
|
|
470
482
|
see `parser.py → PRICE_HISTORY`.
|
|
471
483
|
- **These are API-equivalent values.** If you're on a subscription (Claude Max/Pro, Codex,
|
|
472
484
|
Copilot), you don't pay per token — the $ is "what this would cost at API rates."
|
|
@@ -488,12 +500,15 @@ dollars**, so cost is always derived. Rates live in `parser.py → PRICING` as
|
|
|
488
500
|
when it runs through supported tools, at the Standard rate for prompts up to 200K
|
|
489
501
|
tokens. Live, TTS, image and embedding models are not priced, and nor are the higher Pro rate
|
|
490
502
|
for longer prompts or audio input. The Gemini CLI itself writes no token counts to read.
|
|
491
|
-
- **Gemini 4 Argon
|
|
492
|
-
[
|
|
493
|
-
$2 input, $10 output and $0.10 cached input per 1M tokens
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
503
|
+
- **Gemini 4 Argon and Gemini 4 Carbon** (`gemini-4-argon`, `gemini-4-carbon`) are recognized
|
|
504
|
+
across the supported tools and use Google's [published rates](https://aistudio.google.com/docs/pricing):
|
|
505
|
+
$2 input, $10 output and $0.10 cached input per 1M tokens through November 30, 2026. Google
|
|
506
|
+
doubles them to $4, $20 and $0.20 on December 1, 2026. The higher rate is not applied ahead
|
|
507
|
+
of time: it goes in once it takes effect, and earlier usage keeps the rate it had then.
|
|
508
|
+
Cache storage charges are not modeled.
|
|
509
|
+
- **Gemini 4 Hydrogen** (`gemini-4-hydrogen`) uses Google's paid rate: $1.50 input, $7.50 output
|
|
510
|
+
and $0.075 cached input per 1M tokens. Its free tier can't be told apart in logs, so usage
|
|
511
|
+
is shown at the paid rate.
|
|
497
512
|
|
|
498
513
|
## Note on log retention
|
|
499
514
|
|
|
@@ -148,8 +148,8 @@ def _environment(source):
|
|
|
148
148
|
|
|
149
149
|
|
|
150
150
|
def _codex_read():
|
|
151
|
-
executable =
|
|
152
|
-
if not executable: raise ValueError("Codex
|
|
151
|
+
executable = N.codex_executable()
|
|
152
|
+
if not executable: raise ValueError("Codex's command-line program wasn't found on this device.")
|
|
153
153
|
# This child exposes only read methods. Never start/resume a thread/model turn.
|
|
154
154
|
process = subprocess.Popen([executable, "app-server", "--listen", "stdio://", "-c", "analytics.enabled=false"],
|
|
155
155
|
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
|
|
@@ -183,7 +183,7 @@ def _codex_read():
|
|
|
183
183
|
if not isinstance(result, dict): raise ValueError("Codex returned an unsupported account response.")
|
|
184
184
|
return result
|
|
185
185
|
try:
|
|
186
|
-
request(1, "initialize", {"clientInfo": {"name": "agent_telemetry", "title": "AgentTelemetry", "version": "2.
|
|
186
|
+
request(1, "initialize", {"clientInfo": {"name": "agent_telemetry", "title": "AgentTelemetry", "version": "2.3.0"}})
|
|
187
187
|
process.stdin.write('{"method":"initialized","params":{}}\n'); process.stdin.flush()
|
|
188
188
|
before = request(2, "account/read", {"refreshToken": False}).get("account")
|
|
189
189
|
limits = request(3, "account/rateLimits/read", {}) if isinstance(before, dict) and before.get("type") == "chatgpt" else {}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agenttelemetry-app
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: A private, local dashboard for AI coding agent usage
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/uttamdeb/agent-telemetry
|
|
@@ -47,6 +47,12 @@ Covers **Claude Code · Claude Desktop · Codex · GitHub Copilot · Cursor · A
|
|
|
47
47
|
[Browser dashboard (Python or source)](#quick-start) ·
|
|
48
48
|
[Product page](https://agenttelemetry-app.vercel.app/)
|
|
49
49
|
|
|
50
|
+
**AgentTelemetry Pro (optional).** Everything above stays free, open source and local. [AgentTelemetry
|
|
51
|
+
Pro](https://agenttelemetry-app.vercel.app/#pro) is a separate add-on with a sign-in: your dashboard in any browser or
|
|
52
|
+
on your phone; chat with your running Claude Code and Codex sessions from the web, your phone or the Pro app on Mac
|
|
53
|
+
and Windows; answering Claude Code's permission prompts from your phone; sessions that message each other across
|
|
54
|
+
your devices; Optimize; and the local MCP bridge. See [Free vs Pro](https://agenttelemetry-app.vercel.app/#pro).
|
|
55
|
+
|
|
50
56
|
## Download the app
|
|
51
57
|
|
|
52
58
|
Choose the installer for your platform from the release's **Assets** section.
|
|
@@ -70,9 +76,9 @@ These builds are unsigned/unnotarized. macOS may require **System Settings → P
|
|
|
70
76
|
Security → Open Anyway**; Windows SmartScreen may require **More info → Run anyway**.
|
|
71
77
|
[Product and install page](https://agenttelemetry-app.vercel.app/) ·
|
|
72
78
|
[All releases](https://github.com/uttamdeb/agent-telemetry/releases) ·
|
|
73
|
-
[What's new in 2.
|
|
79
|
+
[What's new in 2.3.0](docs/releases/v2.3.0.md) ·
|
|
74
80
|
[SHA-256 checksums in release assets](https://github.com/uttamdeb/agent-telemetry/releases/latest) ·
|
|
75
|
-
[PyPI package 2.
|
|
81
|
+
[PyPI package 2.3.0](https://pypi.org/project/agenttelemetry-app/2.3.0/)
|
|
76
82
|
|
|
77
83
|
---
|
|
78
84
|
|
|
@@ -386,12 +392,16 @@ anything is saved. Close the session in its original tool before confirming, and
|
|
|
386
392
|
that tool if needed. Token, cost, prompt, tool-call and compaction records stay intact.
|
|
387
393
|
Previous title metadata is backed up privately in
|
|
388
394
|
`AgentTelemetryNative/session-title-backups/`, separately from the usage ledger.
|
|
389
|
-
This supports local Claude Code transcripts, Codex through its
|
|
395
|
+
This supports local Claude Code transcripts, Codex through its
|
|
390
396
|
[official name API](https://learn.chatgpt.com/docs/app-server), and OpenCode’s SQLite
|
|
391
|
-
session stores.
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
397
|
+
session stores. Codex's program is found wherever its apps install it (the ChatGPT app,
|
|
398
|
+
the Codex editor extension, the Codex desktop app on Windows, or a Codex CLI), with no
|
|
399
|
+
need for it on your PATH. Remote sessions and deleted logs cannot be renamed.
|
|
400
|
+
Recognized local Claude subagent transcripts, and sessions from tools whose own title
|
|
401
|
+
can't be edited safely (Copilot, Cursor, Claude Desktop, Antigravity, Hermes, OpenClaw),
|
|
402
|
+
instead offer **Name in AgentTelemetry**. This saves a private display name on this
|
|
403
|
+
device without editing the tool's files, Claude's transcript, native subagent name, or
|
|
404
|
+
parent session. The preview states this scope explicitly.
|
|
395
405
|
These names live in `AgentTelemetryNative/session-display-names.json`, outside the
|
|
396
406
|
usage ledger, summaries, and device sharing. Enter the source title again to remove
|
|
397
407
|
a display-name override. See [subagent name behavior](docs/subagent-sessions.md).
|
|
@@ -479,9 +489,10 @@ dollars**, so cost is always derived. Rates live in `parser.py → PRICING` as
|
|
|
479
489
|
`(input, output, cache_write_5m, cache_write_1h, cache_read)` per 1M tokens; edit freely
|
|
480
490
|
(recomputed on each request, no re-parse needed).
|
|
481
491
|
|
|
482
|
-
- **Anthropic** rates are current list prices (
|
|
483
|
-
|
|
484
|
-
read 0.1× — 0.05× on Opus 5.5
|
|
492
|
+
- **Anthropic** rates are current list prices (Fable & Mythos 5.2 $8/$40, Fable & Mythos 5/5.1
|
|
493
|
+
$10/$50, Opus 5.5 $4/$20, Opus 5 & 4.x $5/$25, Sonnet 5 & 5.5 $2/$10, Sonnet 4.x $3/$15, Haiku
|
|
494
|
+
4.5 $1/$5; cache write 1.25×/2× input for 5-min/1-hour, cache read 0.1× — 0.05× on Opus 5.5 and
|
|
495
|
+
Sonnet 5.5, 0.025× on Fable & Mythos 5.1/5.2). **OpenAI** GPT-5.4/5.5/5.6/6 are verified from OpenAI docs;
|
|
485
496
|
older/other models are estimates.
|
|
486
497
|
- **Haiku 5.5** uses [Anthropic's published rates](https://platform.claude.com/docs/en/about-claude/pricing):
|
|
487
498
|
$0.10/$0.50 per million input/output tokens up to 100K prompt tokens, $0.50/$2.50
|
|
@@ -490,7 +501,8 @@ dollars**, so cost is always derived. Rates live in `parser.py → PRICING` as
|
|
|
490
501
|
message counts stay unchanged. Older archives/peers and other tools without that
|
|
491
502
|
detail use the short-prompt rate.
|
|
492
503
|
- **Price changes are date-aware.** When a vendor cuts a price (OpenAI's GPT-5.6 cuts of
|
|
493
|
-
2026-07-30 and 2026-08-21
|
|
504
|
+
2026-07-30 and 2026-08-21, Anthropic's Sonnet 5.5 cache-read cut of 2026-10-07), usage
|
|
505
|
+
from before the change keeps the rate it had then —
|
|
494
506
|
see `parser.py → PRICE_HISTORY`.
|
|
495
507
|
- **These are API-equivalent values.** If you're on a subscription (Claude Max/Pro, Codex,
|
|
496
508
|
Copilot), you don't pay per token — the $ is "what this would cost at API rates."
|
|
@@ -512,12 +524,15 @@ dollars**, so cost is always derived. Rates live in `parser.py → PRICING` as
|
|
|
512
524
|
when it runs through supported tools, at the Standard rate for prompts up to 200K
|
|
513
525
|
tokens. Live, TTS, image and embedding models are not priced, and nor are the higher Pro rate
|
|
514
526
|
for longer prompts or audio input. The Gemini CLI itself writes no token counts to read.
|
|
515
|
-
- **Gemini 4 Argon
|
|
516
|
-
[
|
|
517
|
-
$2 input, $10 output and $0.10 cached input per 1M tokens
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
527
|
+
- **Gemini 4 Argon and Gemini 4 Carbon** (`gemini-4-argon`, `gemini-4-carbon`) are recognized
|
|
528
|
+
across the supported tools and use Google's [published rates](https://aistudio.google.com/docs/pricing):
|
|
529
|
+
$2 input, $10 output and $0.10 cached input per 1M tokens through November 30, 2026. Google
|
|
530
|
+
doubles them to $4, $20 and $0.20 on December 1, 2026. The higher rate is not applied ahead
|
|
531
|
+
of time: it goes in once it takes effect, and earlier usage keeps the rate it had then.
|
|
532
|
+
Cache storage charges are not modeled.
|
|
533
|
+
- **Gemini 4 Hydrogen** (`gemini-4-hydrogen`) uses Google's paid rate: $1.50 input, $7.50 output
|
|
534
|
+
and $0.075 cached input per 1M tokens. Its free tier can't be told apart in logs, so usage
|
|
535
|
+
is shown at the paid rate.
|
|
521
536
|
|
|
522
537
|
## Note on log retention
|
|
523
538
|
|
|
@@ -661,7 +661,8 @@ def build_native_activity(observed_at=None):
|
|
|
661
661
|
local_items = [(path_by_object.get(id(aggregate), aggregate.get("path", "")), aggregate)
|
|
662
662
|
for aggregate in unique]
|
|
663
663
|
snapshot = NA.project(local_items, observed_at=observed_at,
|
|
664
|
-
identity=SN.identity, local_device=local_device
|
|
664
|
+
identity=SN.identity, local_device=local_device,
|
|
665
|
+
display_scope=SN.display_scope)
|
|
665
666
|
names = {}
|
|
666
667
|
if NA.has_display_aliases(snapshot):
|
|
667
668
|
try:
|
|
@@ -863,10 +864,11 @@ def set_session_name(body):
|
|
|
863
864
|
aggregate, session = matches[0]
|
|
864
865
|
path = aggregate["path"]
|
|
865
866
|
original = session.get("title") or session.get("project") or session_id
|
|
866
|
-
|
|
867
|
+
subagent = bool(session.get("subagent"))
|
|
868
|
+
display_only = SN.display_scope(source, path, subagent)
|
|
867
869
|
if body.get("action") == "preview":
|
|
868
870
|
if display_only:
|
|
869
|
-
return SN.prepare_display(device, source, path, session_id, body.get("name"), original)
|
|
871
|
+
return SN.prepare_display(device, source, path, session_id, body.get("name"), original, subagent)
|
|
870
872
|
return SN.prepare(device, source, path, session_id, body.get("name"), original)
|
|
871
873
|
if body.get("action") != "rename":
|
|
872
874
|
raise ValueError("Review the name-change preview before saving.")
|
|
@@ -962,14 +964,16 @@ def build_editor_snapshot(query):
|
|
|
962
964
|
except ValueError:
|
|
963
965
|
names, alias_error = {}, True
|
|
964
966
|
for path, agg in items if names else ():
|
|
965
|
-
if
|
|
966
|
-
or agg.get("_device") not in (None, "", local)):
|
|
967
|
+
if str(path).startswith("peer:") or agg.get("_device") not in (None, "", local):
|
|
967
968
|
continue
|
|
968
969
|
for session in agg.get("sessions", []):
|
|
970
|
+
if not SN.display_scope(agg.get("source"), agg.get("path", ""),
|
|
971
|
+
bool(session.get("subagent") or agg.get("subagent"))):
|
|
972
|
+
continue
|
|
969
973
|
native_id = SN.identity(agg, session)
|
|
970
974
|
if not native_id:
|
|
971
975
|
continue
|
|
972
|
-
alias = SN.display_name(names, local, "
|
|
976
|
+
alias = SN.display_name(names, local, agg.get("source"), agg.get("path", ""), native_id)
|
|
973
977
|
if alias:
|
|
974
978
|
aliases[(agg.get("path", ""), native_id)] = alias
|
|
975
979
|
result = ES.build_snapshot(items, local, query, refresh_sync()["revision"],
|
|
@@ -1623,10 +1627,11 @@ def _build_payload_locked():
|
|
|
1623
1627
|
s2["account_metadata"] = A.session_metadata(agg)
|
|
1624
1628
|
available, reason = SN.availability(source, agg.get("path", ""), remote)
|
|
1625
1629
|
s2["rename_id"] = _session_identity(agg, session)
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
+
subagent = bool(session.get("subagent"))
|
|
1631
|
+
if SN.display_scope(source, agg.get("path", ""), subagent):
|
|
1632
|
+
# Claude subagents and tools whose own title can't be edited safely are
|
|
1633
|
+
# named in AgentTelemetry only; their source files are never written.
|
|
1634
|
+
available, reason = SN.display_availability(source, agg.get("path", ""), subagent, remote)
|
|
1630
1635
|
if not remote:
|
|
1631
1636
|
s2["rename_scope"] = "display"
|
|
1632
1637
|
if display_name_error:
|
|
@@ -1636,6 +1641,8 @@ def _build_payload_locked():
|
|
|
1636
1641
|
s2["original_title"] = session.get("title") or session.get("project") or s2["rename_id"]
|
|
1637
1642
|
s2["title"] = alias
|
|
1638
1643
|
s2["display_alias"] = True
|
|
1644
|
+
if not s2["rename_id"]:
|
|
1645
|
+
available, reason = False, "The full native session identity is unavailable."
|
|
1639
1646
|
if session.get("retained"):
|
|
1640
1647
|
available, reason = False, "This session was deleted in its tool. Its telemetry is preserved."
|
|
1641
1648
|
s2["rename_available"], s2["rename_reason"] = available, reason
|
|
@@ -1808,21 +1815,24 @@ def _build_payload_locked():
|
|
|
1808
1815
|
"pricing_long_context": {m: list(P.price_of(m, long_context=True))
|
|
1809
1816
|
for m in set(P.LONG_CONTEXT_PRICING) | set(model_meta)
|
|
1810
1817
|
if P.price_of(m, long_context=True) != P.price_of(m)},
|
|
1811
|
-
"pricing_note": ("Anthropic costs use current list pricing (Fable 5 $
|
|
1818
|
+
"pricing_note": ("Anthropic costs use current list pricing (Fable & Mythos 5.2 $8/$40, "
|
|
1819
|
+
"Fable & Mythos 5/5.1 $10/$50, Opus 5.5 "
|
|
1812
1820
|
"$4/$20, Opus 5 & 4.x $5/$25, Sonnet 5 & 5.5 $2/$10, Sonnet 4.x "
|
|
1813
1821
|
"$3/$15, Haiku 4.5 $1/$5 per Mtok). Haiku 5.5 uses $0.10/$0.50 "
|
|
1814
1822
|
"up to 100K prompt tokens and $0.50/$2.50 above 100K, including cache "
|
|
1815
1823
|
"reads/writes in each request's threshold; available Claude logs retain "
|
|
1816
1824
|
"that split, while older archives/peers and other tools without request "
|
|
1817
1825
|
"detail use the short-prompt rate. Cache write is billed at 1.25x (5-min) "
|
|
1818
|
-
"/ 2x (1-hour) input and cache read at 0.1x (0.05x on Opus 5.5
|
|
1826
|
+
"/ 2x (1-hour) input and cache read at 0.1x (0.05x on Opus 5.5 and on "
|
|
1827
|
+
"Sonnet 5.5 from 2026-10-07, 0.025x on Fable & Mythos 5.1/5.2); OpenAI/"
|
|
1819
1828
|
"Codex/Copilot/Cursor prices are estimates. Codex GPT-5.4/5.5/5.6/6 use "
|
|
1820
1829
|
"verified OpenAI list rates (e.g. GPT-6 Sol $2/$10, cached $0.20; GPT-6.1 Sol cached $0.10), each "
|
|
1821
1830
|
"day priced at the rate in force then — OpenAI cut GPT-5.6 prices on "
|
|
1822
1831
|
"2026-07-30 and 2026-08-21; a >272K-input surcharge is not modeled, so "
|
|
1823
|
-
"heavy-context sessions may be higher. Gemini 4 Argon
|
|
1824
|
-
"
|
|
1825
|
-
"per Mtok)
|
|
1832
|
+
"heavy-context sessions may be higher. Gemini 4 Argon and Carbon use "
|
|
1833
|
+
"Google's rates through 2026-11-30 ($2 input/$10 output, cached input "
|
|
1834
|
+
"$0.10 per Mtok); Gemini 4 Hydrogen is $1.50/$7.50, cached $0.075. "
|
|
1835
|
+
"Other Gemini text models use Standard rates for prompts "
|
|
1826
1836
|
"up to 200K tokens. "
|
|
1827
1837
|
"Actual "
|
|
1828
1838
|
"billing may differ. Claude Code/Desktop & Codex can run on either "
|
|
@@ -45,7 +45,9 @@
|
|
|
45
45
|
<symbol id="i-storage" viewBox="0 0 16 16"><ellipse cx="8" cy="3.9" rx="5.2" ry="1.9"/><path d="M2.8 3.9v8.2c0 1.1 2.3 1.9 5.2 1.9s5.2-.8 5.2-1.9V3.9M2.8 8c0 1.1 2.3 1.9 5.2 1.9s5.2-.8 5.2-1.9"/></symbol>
|
|
46
46
|
<symbol id="i-servers" viewBox="0 0 16 16"><rect x="2.3" y="2.2" width="11.4" height="4.1" rx="1.2"/><rect x="2.3" y="9.7" width="11.4" height="4.1" rx="1.2"/><path d="M4.5 4.2h.1M4.5 11.7h.1M7.2 4.2h4.2M7.2 11.7h4.2"/></symbol>
|
|
47
47
|
<symbol id="i-refresh" viewBox="0 0 16 16"><path d="M13.2 8a5.2 5.2 0 1 1-1.6-3.8"/><path d="M13.2 2.6v2.9h-2.9"/></symbol>
|
|
48
|
-
<symbol id="i-theme" viewBox="0 0 16 16"><circle cx="8" cy="8" r="5.6"/><path d="M8 2.4v11.2a5.6 5.6 0 0 0 0-11.2z" fill="currentColor" stroke="none"/></symbol>
|
|
48
|
+
<symbol id="i-theme-auto" viewBox="0 0 16 16"><circle cx="8" cy="8" r="5.6"/><path d="M8 2.4v11.2a5.6 5.6 0 0 0 0-11.2z" fill="currentColor" stroke="none"/></symbol>
|
|
49
|
+
<symbol id="i-theme-light" viewBox="0 0 16 16"><circle cx="8" cy="8" r="2.7"/><path d="M8 1.6v1.5M8 12.9v1.5M1.6 8h1.5M12.9 8h1.5M3.5 3.5l1.1 1.1M11.4 11.4l1.1 1.1M3.5 12.5l1.1-1.1M11.4 4.6l1.1-1.1"/></symbol>
|
|
50
|
+
<symbol id="i-theme-dark" viewBox="0 0 16 16"><path d="M13.3 9.7A5.6 5.6 0 0 1 6.3 2.7a5.6 5.6 0 1 0 7 7z"/></symbol>
|
|
49
51
|
<symbol id="i-settings" viewBox="0 0 16 16"><path d="M12.76 6.86 L14.34 7.15 L14.34 8.85 L12.76 9.14 L12.18 10.56 L13.09 11.88 L11.88 13.09 L10.56 12.18 L9.14 12.76 L8.85 14.34 L7.15 14.34 L6.86 12.76 L5.44 12.18 L4.12 13.09 L2.91 11.88 L3.82 10.56 L3.24 9.14 L1.66 8.85 L1.66 7.15 L3.24 6.86 L3.82 5.44 L2.91 4.12 L4.12 2.91 L5.44 3.82 L6.86 3.24 L7.15 1.66 L8.85 1.66 L9.14 3.24 L10.56 3.82 L11.88 2.91 L13.09 4.12 L12.18 5.44Z" stroke-linejoin="round"/><circle cx="8" cy="8" r="2.1"/></symbol>
|
|
50
52
|
<symbol id="i-sidebar" viewBox="0 0 16 16"><rect x="2" y="2.6" width="12" height="10.8" rx="1.8"/><path d="M6.2 2.6v10.8"/><path d="M4 5.4h.6M4 7.4h.6M4 9.4h.6"/></symbol>
|
|
51
53
|
<symbol id="i-filter" viewBox="0 0 16 16"><path d="M2.3 3.2h11.4L9.3 8.5v4.3l-2.6 1.3V8.5z"/></symbol>
|
|
@@ -93,7 +95,7 @@
|
|
|
93
95
|
</button>
|
|
94
96
|
<div class="side-actions">
|
|
95
97
|
<button class="icon-btn" id="refreshBtn" title="Refresh now (r)" aria-label="Refresh now"><svg class="ic"><use href="#i-refresh"/></svg></button>
|
|
96
|
-
<button class="icon-btn" id="themeBtn" title="Theme (t)" aria-label="Theme"><svg class="ic"><use href="#i-theme"/></svg></button>
|
|
98
|
+
<button class="icon-btn" id="themeBtn" title="Theme: Auto (t)" aria-label="Theme: Auto. Change theme"><svg class="ic"><use href="#i-theme-auto"/></svg></button>
|
|
97
99
|
<button class="icon-btn" id="settingsBtn" title="Settings" aria-label="Settings"><svg class="ic"><use href="#i-settings"/></svg></button>
|
|
98
100
|
</div>
|
|
99
101
|
<div class="side-note coverage-meta" id="coverage"></div>
|
|
@@ -3,7 +3,9 @@ import json
|
|
|
3
3
|
import ntpath
|
|
4
4
|
import os
|
|
5
5
|
from pathlib import Path
|
|
6
|
+
import platform
|
|
6
7
|
import posixpath
|
|
8
|
+
import re
|
|
7
9
|
import subprocess
|
|
8
10
|
import sys
|
|
9
11
|
import threading
|
|
@@ -12,6 +14,103 @@ import uuid
|
|
|
12
14
|
|
|
13
15
|
_lock = threading.Lock()
|
|
14
16
|
REFRESH_INTERVALS = (0, 15, 60, 300, 900)
|
|
17
|
+
_CODEX_FOUND = {"at": None, "path": None}
|
|
18
|
+
_CODEX_TTL = 60
|
|
19
|
+
_EDITOR_DIR = re.compile(r"\.(vscode|cursor|windsurf|antigravity)")
|
|
20
|
+
_CODEX_EXTENSION = re.compile(r"openai\.chatgpt-(\d+(?:\.\d+)*)")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def codex_candidates(env=None, home=None, system=None, machine=None):
|
|
24
|
+
"""Where Codex's command-line program may be, in the Pro connector's order.
|
|
25
|
+
|
|
26
|
+
A service started by the macOS or Windows app does not see the user's shell
|
|
27
|
+
PATH, and Codex's own apps never put their copy on it: the ChatGPT app keeps
|
|
28
|
+
one inside its bundle, each editor extension one per version, and the Codex
|
|
29
|
+
desktop app on Windows one per build. Paths only; nothing is started here.
|
|
30
|
+
"""
|
|
31
|
+
env = os.environ if env is None else env
|
|
32
|
+
home = str(Path.home()) if home is None else home
|
|
33
|
+
system = sys.platform if system is None else system
|
|
34
|
+
windows = system == "win32"
|
|
35
|
+
join = ntpath.join if windows else posixpath.join
|
|
36
|
+
exe = "codex.exe" if windows else "codex"
|
|
37
|
+
machine = (platform.machine() if machine is None else machine).lower()
|
|
38
|
+
arm = machine in ("arm64", "aarch64")
|
|
39
|
+
found = []
|
|
40
|
+
override = env.get("CODEX_BIN")
|
|
41
|
+
if override:
|
|
42
|
+
found.append(override)
|
|
43
|
+
path_key = next((k for k in env if k.lower() == "path"), None) if windows else "PATH"
|
|
44
|
+
for folder in (env.get(path_key) or "").split(";" if windows else ":"):
|
|
45
|
+
if folder:
|
|
46
|
+
found.append(join(folder, exe))
|
|
47
|
+
if windows:
|
|
48
|
+
# The Codex desktop app's own copy, newest build first; it is never on the saved PATH.
|
|
49
|
+
root = join(env.get("LOCALAPPDATA") or join(home, "AppData", "Local"), "OpenAI", "Codex", "bin")
|
|
50
|
+
builds = []
|
|
51
|
+
try:
|
|
52
|
+
names = os.listdir(root)
|
|
53
|
+
except OSError:
|
|
54
|
+
names = []
|
|
55
|
+
for name in names:
|
|
56
|
+
path = join(root, name, exe)
|
|
57
|
+
try:
|
|
58
|
+
builds.append((os.stat(path).st_mtime, path))
|
|
59
|
+
except OSError:
|
|
60
|
+
continue
|
|
61
|
+
found += [path for _, path in sorted(builds, reverse=True)]
|
|
62
|
+
target = ("windows" if windows else "macos" if system == "darwin" else "linux") + (
|
|
63
|
+
"-aarch64" if arm else "-x86_64")
|
|
64
|
+
extensions = []
|
|
65
|
+
try:
|
|
66
|
+
editors = sorted(name for name in os.listdir(home) if _EDITOR_DIR.match(name))
|
|
67
|
+
except OSError:
|
|
68
|
+
editors = []
|
|
69
|
+
for editor in editors:
|
|
70
|
+
try:
|
|
71
|
+
names = os.listdir(join(home, editor, "extensions"))
|
|
72
|
+
except OSError:
|
|
73
|
+
continue
|
|
74
|
+
for name in names:
|
|
75
|
+
match = _CODEX_EXTENSION.match(name)
|
|
76
|
+
if match:
|
|
77
|
+
version = tuple(int(part) for part in match.group(1).split("."))
|
|
78
|
+
extensions.append((version, join(home, editor, "extensions", name, "bin", target, exe)))
|
|
79
|
+
found += [path for _, path in sorted(extensions, key=lambda item: item[0], reverse=True)]
|
|
80
|
+
if windows:
|
|
81
|
+
# An npm global install puts only codex.cmd on PATH; its real program is in a platform package.
|
|
82
|
+
package = "codex-win32-arm64" if arm else "codex-win32-x64"
|
|
83
|
+
binary = join("vendor", "aarch64-pc-windows-msvc" if arm else "x86_64-pc-windows-msvc", "bin", exe)
|
|
84
|
+
prefix = join(env.get("APPDATA") or join(home, "AppData", "Roaming"), "npm")
|
|
85
|
+
found += [join(prefix, "node_modules", "@openai", "codex", "node_modules", "@openai", package, binary),
|
|
86
|
+
join(prefix, "node_modules", "@openai", package, binary)]
|
|
87
|
+
else:
|
|
88
|
+
if system == "darwin":
|
|
89
|
+
app = join("Applications", "ChatGPT.app", "Contents", "Resources", "codex-cli",
|
|
90
|
+
"CodexCLI.app", "Contents", "MacOS", "codex")
|
|
91
|
+
found += ["/" + app, join(home, app)]
|
|
92
|
+
# Where a CLI install usually goes; a service's PATH often lacks them.
|
|
93
|
+
found += [join(folder, exe) for folder in ("/opt/homebrew/bin", "/usr/local/bin",
|
|
94
|
+
join(home, ".local", "bin"))]
|
|
95
|
+
unique = []
|
|
96
|
+
for path in found:
|
|
97
|
+
if path not in unique:
|
|
98
|
+
unique.append(path)
|
|
99
|
+
return unique
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def codex_executable():
|
|
103
|
+
"""The first runnable Codex program (see codex_candidates), or None. Cached for a minute."""
|
|
104
|
+
now = time.monotonic()
|
|
105
|
+
with _lock:
|
|
106
|
+
cached, at = _CODEX_FOUND["path"], _CODEX_FOUND["at"]
|
|
107
|
+
if at is not None and now - at < _CODEX_TTL and (cached is None or os.path.isfile(cached)):
|
|
108
|
+
return cached
|
|
109
|
+
path = next((candidate for candidate in codex_candidates()
|
|
110
|
+
if os.path.isfile(candidate) and os.access(candidate, os.X_OK)), None)
|
|
111
|
+
with _lock:
|
|
112
|
+
_CODEX_FOUND.update(at=now, path=path)
|
|
113
|
+
return path
|
|
15
114
|
|
|
16
115
|
|
|
17
116
|
def _project_path(value):
|
|
@@ -235,7 +235,7 @@ def is_local_live_aggregate(aggregate, local_device=None) -> bool:
|
|
|
235
235
|
|
|
236
236
|
|
|
237
237
|
def project(items: Iterable, *, observed_at=None, identity: Optional[Callable] = None,
|
|
238
|
-
local_device: Optional[str] = None) -> dict:
|
|
238
|
+
local_device: Optional[str] = None, display_scope: Optional[Callable] = None) -> dict:
|
|
239
239
|
"""Build the bounded public snapshot and private alias references.
|
|
240
240
|
|
|
241
241
|
``items`` is the local ``(path, aggregate)`` list after the dashboard's
|
|
@@ -305,7 +305,11 @@ def project(items: Iterable, *, observed_at=None, identity: Optional[Callable] =
|
|
|
305
305
|
if safe_availability:
|
|
306
306
|
row["usage_availability"] = safe_availability
|
|
307
307
|
alias = None
|
|
308
|
-
|
|
308
|
+
# A private AgentTelemetry-only name: Claude subagents, and tools whose own
|
|
309
|
+
# title is never edited (display_scope decides; without it, Claude children only).
|
|
310
|
+
scoped = (display_scope(source, path, subagent) if callable(display_scope)
|
|
311
|
+
else source == "claude" and subagent)
|
|
312
|
+
if scoped and isinstance(path, str):
|
|
309
313
|
alias_id = source_id[len("native:"):] if source_id.startswith("native:") else source_id
|
|
310
314
|
alias = (local_device, source, path, alias_id)
|
|
311
315
|
entry = {"row": row, "_alias": alias, "_sort": end}
|
|
@@ -330,7 +334,7 @@ def has_display_aliases(snapshot) -> bool:
|
|
|
330
334
|
|
|
331
335
|
|
|
332
336
|
def apply_display_aliases(snapshot, names, display_name: Callable) -> dict:
|
|
333
|
-
"""Apply private
|
|
337
|
+
"""Apply private AgentTelemetry-only names only after the session passed eligibility."""
|
|
334
338
|
public = []
|
|
335
339
|
for row in snapshot.get("sessions", []):
|
|
336
340
|
result = {key: value for key, value in row.items() if key != "_display_alias"}
|
|
@@ -200,23 +200,27 @@ EDITOR_LABEL = {
|
|
|
200
200
|
# (input, output, cache_write_5m, cache_write_1h, cache_read)
|
|
201
201
|
# Anthropic prices verified against the current model table (Opus 4.x = $5/$25,
|
|
202
202
|
# NOT the old $15/$75 — Opus pricing dropped with 4.5). Cache write = 1.25x input
|
|
203
|
-
# (5-min TTL) / 2x input (1-hour TTL); cache read = 0.1x input, EXCEPT Opus 5.5
|
|
204
|
-
# (0.05x) and Fable/Mythos 5.1 (0.025x) — so the cache_read slot is
|
|
205
|
-
# row, never derived from input. Most OpenAI rows have no cache-write tier
|
|
203
|
+
# (5-min TTL) / 2x input (1-hour TTL); cache read = 0.1x input, EXCEPT Opus 5.5 and
|
|
204
|
+
# Sonnet 5.5 (0.05x) and Fable/Mythos 5.1 and 5.2 (0.025x) — so the cache_read slot is
|
|
205
|
+
# written out per row, never derived from input. Most OpenAI rows have no cache-write tier
|
|
206
206
|
# (cw5/cw1 = 0, billed at the input rate); cached input goes in the cache_read slot.
|
|
207
207
|
# Codex/Copilot/Cursor are subscription-billed, so their $ is an API-equivalent
|
|
208
208
|
# estimate, not an actual charge. Costs are computed at request time — edit
|
|
209
209
|
# freely, no re-parse needed.
|
|
210
210
|
# ---------------------------------------------------------------------------
|
|
211
211
|
PRICING = {
|
|
212
|
-
# Anthropic Claude 5 family — verified against platform.claude.com pricing (2026-
|
|
212
|
+
# Anthropic Claude 5 family — verified against platform.claude.com pricing (2026-11-10).
|
|
213
|
+
# 5.2 (launched 2026-11-09) is $8/$40 with 5.1's 0.025x cache read ($0.20).
|
|
214
|
+
"Claude Fable 5.2": (8, 40, 10, 16, 0.20),
|
|
215
|
+
"Claude Mythos 5.2": (8, 40, 10, 16, 0.20),
|
|
213
216
|
# 5.1 keeps 5's $10/$50 but cuts cache reads to 0.025x input ($0.25, not $1).
|
|
214
217
|
"Claude Fable 5.1": (10, 50, 12.5, 20, 0.25),
|
|
215
218
|
"Claude Mythos 5.1": (10, 50, 12.5, 20, 0.25),
|
|
216
219
|
"Claude Fable 5": (10, 50, 12.5, 20, 1.0),
|
|
217
220
|
"Claude Mythos 5": (10, 50, 12.5, 20, 1.0),
|
|
218
|
-
# Sonnet 5.5 lists the same $2/$10 as Sonnet 5
|
|
219
|
-
|
|
221
|
+
# Sonnet 5.5 lists the same $2/$10 as Sonnet 5. Its cache read was cut from 0.1x to
|
|
222
|
+
# 0.05x ($0.10) on 2026-10-07 (Anthropic's release notes); the old rate is in PRICE_HISTORY.
|
|
223
|
+
"Claude Sonnet 5.5": (2, 10, 2.5, 4, 0.10),
|
|
220
224
|
# Sonnet 5's $2/$10 launch "intro" rate became its standard price — Anthropic
|
|
221
225
|
# cancelled the $3/$15 increase scheduled for 2026-09-01, so there's no date split.
|
|
222
226
|
"Claude Sonnet 5": (2, 10, 2.5, 4, 0.20),
|
|
@@ -311,6 +315,15 @@ PRICING = {
|
|
|
311
315
|
# this tuple into PRICE_HISTORY as [("2026-11-30", ...)] and put (4, 20, 0, 0, 0.20)
|
|
312
316
|
# here. Implicit caching only; cache storage is not modeled.
|
|
313
317
|
"Gemini 4 Argon": (2.00, 10.00, 0, 0, 0.10),
|
|
318
|
+
# Gemini 4 Carbon — gemini-4-carbon; verified against aistudio.google.com/docs/pricing
|
|
319
|
+
# (2026-11-12): the same rate card as Argon, $2 / $10 / $0.10 cached "through November
|
|
320
|
+
# 30, 2026", then $4 / $20 / $0.20 "starting December 1, 2026". Move it on that date
|
|
321
|
+
# exactly as Argon above, never before. Implicit caching only.
|
|
322
|
+
"Gemini 4 Carbon": (2.00, 10.00, 0, 0, 0.10),
|
|
323
|
+
# Gemini 4 Hydrogen — gemini-4-hydrogen; verified against aistudio.google.com/docs/pricing
|
|
324
|
+
# (2026-11-13): $1.50 / $7.50 / $0.075 cached, one rate (no prompt-size tier, no dated
|
|
325
|
+
# change). Implicit caching only.
|
|
326
|
+
"Gemini 4 Hydrogen": (1.50, 7.50, 0, 0, 0.075),
|
|
314
327
|
# 3.6 to 3.8 Flash are a promo "through December 31, 2026" and double on 2027-01-01:
|
|
315
328
|
# then move these tuples into PRICE_HISTORY dated 2026-12-31 and put the doubled
|
|
316
329
|
# ones here ($1.50 / $7.50, cache read $0.15).
|
|
@@ -346,6 +359,9 @@ PRICE_HISTORY = {
|
|
|
346
359
|
# "Starting today", 2026-08-21: Sol -20% "for the next 3 months"
|
|
347
360
|
# (community.openai.com/t/1391726).
|
|
348
361
|
"GPT-5.6 Sol": [("2026-08-20", (5, 30, 0, 0, 0.50))],
|
|
362
|
+
# 2026-10-07: Sonnet 5.5 cache reads $0.20 -> $0.10; nothing else changed
|
|
363
|
+
# (platform.claude.com release notes).
|
|
364
|
+
"Claude Sonnet 5.5": [("2026-10-06", (2, 10, 2.5, 4, 0.20))],
|
|
349
365
|
}
|
|
350
366
|
|
|
351
367
|
|
|
@@ -353,9 +369,12 @@ PRICE_HISTORY = {
|
|
|
353
369
|
# model name so they show as their own row and price without a new record field.
|
|
354
370
|
FAST_SUFFIX = " (fast)"
|
|
355
371
|
US_SUFFIX = " (US)"
|
|
356
|
-
# Fast mode's own rate card (platform.claude.com pricing, 2026-09
|
|
357
|
-
# multipliers apply on top of the fast input rate, same as
|
|
372
|
+
# Fast mode's own rate card (platform.claude.com pricing, 2026-09; Fable/Mythos 5.2
|
|
373
|
+
# added 2026-11-10): the cache multipliers apply on top of the fast input rate, same as
|
|
374
|
+
# standard speed.
|
|
358
375
|
FAST_PRICING = {
|
|
376
|
+
"Claude Fable 5.2": (16, 80, 20, 32, 0.40),
|
|
377
|
+
"Claude Mythos 5.2": (16, 80, 20, 32, 0.40),
|
|
359
378
|
"Claude Opus 5.5": (8, 40, 10, 16, 0.40),
|
|
360
379
|
"Claude Opus 5": (10, 50, 12.5, 20, 1.0),
|
|
361
380
|
"Claude Opus 4.8": (10, 50, 12.5, 20, 1.0),
|
|
@@ -465,8 +484,11 @@ def _canonicalize(name):
|
|
|
465
484
|
if "raptor" in low:
|
|
466
485
|
return "Raptor Mini"
|
|
467
486
|
|
|
468
|
-
# Claude — "Claude Sonnet 4.5", "claude-sonnet-4-5", "claude-3.5-sonnet"
|
|
469
|
-
|
|
487
|
+
# Claude — "Claude Sonnet 4.5", "claude-sonnet-4-5", "claude-3.5-sonnet", "claude-fable-5-2".
|
|
488
|
+
# Fable and Mythos were missing here, so OpenCode, Hermes and Cursor ids for them priced at $0.
|
|
489
|
+
if "claude" in low and "mythos" in low and "preview" in low:
|
|
490
|
+
return "Claude Mythos Preview"
|
|
491
|
+
tier = next((t.capitalize() for t in ("opus", "sonnet", "haiku", "fable", "mythos") if t in low), None)
|
|
470
492
|
if "claude" in low and tier:
|
|
471
493
|
nums = re.findall(r"\d+(?:\.\d+)?", low)
|
|
472
494
|
ver = ""
|
|
@@ -476,13 +498,14 @@ def _canonicalize(name):
|
|
|
476
498
|
ver = nums[0]
|
|
477
499
|
return f"Claude {tier} {ver}".strip()
|
|
478
500
|
|
|
479
|
-
# Gemini — "gemini-3.1-pro-preview", "Google: Gemini 4 Argon", "gemini-4-
|
|
501
|
+
# Gemini — "gemini-3.1-pro-preview", "Google: Gemini 4 Argon", "gemini-4-hydrogen"
|
|
480
502
|
# → one display name. Preview/date/effort suffixes drop; Live, TTS, image, embedding and
|
|
481
503
|
# the like are different products with their own rates, so they keep their own spelling.
|
|
482
|
-
mgm = re.match(r"(?:models/)?gemini-?\s*(\d+(?:\.\d+)?)-?\s*(argon|pro|flash[\s\-]*lite|flash|computer-use)\b(.*)$", low)
|
|
504
|
+
mgm = re.match(r"(?:models/)?gemini-?\s*(\d+(?:\.\d+)?)-?\s*(argon|carbon|hydrogen|pro|flash[\s\-]*lite|flash|computer-use)\b(.*)$", low)
|
|
483
505
|
if mgm and not re.search(r"live|tts|image|audio|embed|transcribe|translate|robot|omni",
|
|
484
506
|
mgm.group(3)):
|
|
485
|
-
kind = {"argon": "Argon", "
|
|
507
|
+
kind = {"argon": "Argon", "carbon": "Carbon", "hydrogen": "Hydrogen", "pro": "Pro",
|
|
508
|
+
"flash": "Flash", "computer-use": "Computer Use"}.get(
|
|
486
509
|
mgm.group(2), "Flash-Lite")
|
|
487
510
|
return f"Gemini {mgm.group(1)} {kind}"
|
|
488
511
|
|
|
@@ -498,7 +498,9 @@ def _fetch_release(url: str, required_version: Optional[str] = None):
|
|
|
498
498
|
|
|
499
499
|
def _placeholder(version: Optional[str], tag_hint: Optional[str] = None) -> dict:
|
|
500
500
|
tag = tag_hint if tag_hint and _tag_version(tag_hint) and _tag_version(tag_hint)[0] == version else None
|
|
501
|
-
|
|
501
|
+
# Releases are tagged vX.Y.Z, but a packaged app knows only its bare version
|
|
502
|
+
# ("2.2.0"), and /releases/tag/2.2.0 is a 404.
|
|
503
|
+
url = _tag_url("v" + tag.lstrip("v")) if tag else ""
|
|
502
504
|
label = "AgentTelemetry " + (version or "release")
|
|
503
505
|
return {
|
|
504
506
|
"key": tag or version,
|
|
@@ -6,7 +6,6 @@ import os
|
|
|
6
6
|
from pathlib import Path, PurePosixPath
|
|
7
7
|
import queue
|
|
8
8
|
import re
|
|
9
|
-
import shutil
|
|
10
9
|
import sqlite3
|
|
11
10
|
import subprocess
|
|
12
11
|
import threading
|
|
@@ -49,13 +48,29 @@ def claude_child_identity(path):
|
|
|
49
48
|
return ""
|
|
50
49
|
|
|
51
50
|
|
|
51
|
+
def display_scope(source, path, subagent=False):
|
|
52
|
+
"""Whether this session's name is kept only in AgentTelemetry.
|
|
53
|
+
|
|
54
|
+
Claude subagents, and every tool whose own title can't be edited safely
|
|
55
|
+
(Copilot, Cursor, Claude Desktop, Antigravity, Hermes, OpenClaw, OpenCode's
|
|
56
|
+
older JSON storage): their source files are never written for a name.
|
|
57
|
+
"""
|
|
58
|
+
if source == "claude": return bool(subagent)
|
|
59
|
+
if source == "opencode": return not str(path).endswith(".db")
|
|
60
|
+
return source not in SUPPORTED
|
|
61
|
+
|
|
62
|
+
|
|
52
63
|
def display_availability(source, path, subagent=False, remote=False):
|
|
53
64
|
if remote: return False, "Name this session on its source device."
|
|
54
|
-
if source
|
|
65
|
+
if source == "claude" and (not subagent or not claude_child_identity(path)):
|
|
55
66
|
return False, "This Claude child transcript layout is not supported for a local display name."
|
|
67
|
+
if not display_scope(source, path, subagent):
|
|
68
|
+
return False, "This session is renamed in its own tool."
|
|
56
69
|
if not os.path.isfile(path):
|
|
57
70
|
return False, "The original log is unavailable; its saved telemetry is preserved."
|
|
58
|
-
|
|
71
|
+
if source == "claude":
|
|
72
|
+
return True, "Names this subagent only in AgentTelemetry; Claude and its parent session stay unchanged."
|
|
73
|
+
return True, "Names this session only in AgentTelemetry; the tool keeps its own title."
|
|
59
74
|
|
|
60
75
|
|
|
61
76
|
def display_names():
|
|
@@ -134,11 +149,11 @@ def _write_display_names(names):
|
|
|
134
149
|
except FileNotFoundError: pass
|
|
135
150
|
|
|
136
151
|
|
|
137
|
-
def prepare_display(device, source, path, session_id, name, original_title):
|
|
152
|
+
def prepare_display(device, source, path, session_id, name, original_title, subagent=True):
|
|
138
153
|
name = validate_name(name)
|
|
139
|
-
ok, reason = display_availability(source, path,
|
|
154
|
+
ok, reason = display_availability(source, path, subagent)
|
|
140
155
|
if not ok: raise ValueError(reason)
|
|
141
|
-
if session_id != claude_child_identity(path):
|
|
156
|
+
if source == "claude" and session_id != claude_child_identity(path):
|
|
142
157
|
raise ValueError("The Claude child identity does not match its discovered transcript.")
|
|
143
158
|
names = display_names()
|
|
144
159
|
stat = os.stat(path)
|
|
@@ -162,7 +177,7 @@ def commit_display(ticket, device, source, path, session_id, confirmed):
|
|
|
162
177
|
plan = _plans.pop(ticket, None) if isinstance(ticket, str) else None
|
|
163
178
|
if (plan is None or plan.get("scope") != "display" or time.monotonic() - plan["at"] > 300
|
|
164
179
|
or [plan[k] for k in ("device", "source", "path", "id")] != [device, source, path, session_id]
|
|
165
|
-
or session_id != claude_child_identity(path)):
|
|
180
|
+
or (source == "claude" and session_id != claude_child_identity(path))):
|
|
166
181
|
raise ValueError("Display-name preview expired or the source changed. Review it again.")
|
|
167
182
|
stat = os.stat(path)
|
|
168
183
|
if ([stat.st_dev, stat.st_ino] != plan["file_identity"]
|
|
@@ -239,8 +254,9 @@ def availability(source, path, remote=False):
|
|
|
239
254
|
try: uuid.UUID(Path(path).stem)
|
|
240
255
|
except ValueError:
|
|
241
256
|
return False, "This Claude transcript has no verified session UUID."
|
|
242
|
-
if source == "codex" and not
|
|
243
|
-
return False, "Codex's
|
|
257
|
+
if source == "codex" and not N.codex_executable():
|
|
258
|
+
return False, ("Codex's command-line program wasn't found. Install the ChatGPT app, "
|
|
259
|
+
"the Codex editor extension or the Codex CLI to rename Codex sessions.")
|
|
244
260
|
if source == "codex":
|
|
245
261
|
try: _codex_home(path)
|
|
246
262
|
except ValueError as error: return False, str(error)
|
|
@@ -257,9 +273,9 @@ def _codex_home(path):
|
|
|
257
273
|
def _codex_rpc(method, params, home=None):
|
|
258
274
|
if method not in ("thread/read", "thread/name/set"):
|
|
259
275
|
raise ValueError("Only Codex title metadata methods are permitted.")
|
|
260
|
-
executable =
|
|
276
|
+
executable = N.codex_executable()
|
|
261
277
|
if not executable:
|
|
262
|
-
raise ValueError("Codex
|
|
278
|
+
raise ValueError("Codex's command-line program wasn't found.")
|
|
263
279
|
# A dedicated local metadata connection: never start/resume a thread or turn.
|
|
264
280
|
environment = os.environ.copy()
|
|
265
281
|
environment.pop("AGENT_TELEMETRY_CONTROL_TOKEN", None)
|
|
@@ -291,7 +307,7 @@ def _codex_rpc(method, params, home=None):
|
|
|
291
307
|
return message.get("result", {})
|
|
292
308
|
try:
|
|
293
309
|
send({"id": 1, "method": "initialize", "params": {"clientInfo": {
|
|
294
|
-
"name": "agent_telemetry", "title": "AgentTelemetry", "version": "2.
|
|
310
|
+
"name": "agent_telemetry", "title": "AgentTelemetry", "version": "2.3.0"}}})
|
|
295
311
|
receive(1); send({"method": "initialized", "params": {}})
|
|
296
312
|
send({"id": 2, "method": method, "params": params})
|
|
297
313
|
return receive(2)
|
|
@@ -574,7 +574,13 @@ function applyTheme(mode){
|
|
|
574
574
|
matchMedia("(prefers-color-scheme: dark)").matches);
|
|
575
575
|
root.dataset.resolved = dark?"dark":"light";
|
|
576
576
|
try{ localStorage.setItem("aiu.theme", mode); }catch(e){}
|
|
577
|
-
|
|
577
|
+
// The button shows the mode in use: a sun for Light, a moon for Dark, half and half for Auto (follow the system).
|
|
578
|
+
const shown = mode==="light"||mode==="dark" ? mode : "auto";
|
|
579
|
+
const name = {auto:"Auto", light:"Light", dark:"Dark"}[shown];
|
|
580
|
+
const btn=document.getElementById("themeBtn");
|
|
581
|
+
btn.title = "Theme: "+name+" (t)";
|
|
582
|
+
btn.setAttribute("aria-label", "Theme: "+name+". Change theme");
|
|
583
|
+
btn.querySelector("use").setAttribute("href", "#i-theme-"+shown);
|
|
578
584
|
if(RAW) renderAll();
|
|
579
585
|
}
|
|
580
586
|
function cycleTheme(){
|
|
@@ -1498,6 +1498,12 @@ function renameButton(s,text=false){
|
|
|
1498
1498
|
'<svg viewBox="0 0 16 16" aria-hidden="true"><path d="m10.5 2.5 3 3-7.5 7.5-4 1 1-4zM9 4l3 3"/></svg>'}</button>`;
|
|
1499
1499
|
}
|
|
1500
1500
|
let sessionRenameTarget=null, sessionRenameSaving=false;
|
|
1501
|
+
// What stays as it was when a name is kept in AgentTelemetry only: a Claude subagent's
|
|
1502
|
+
// transcript and parent, or another tool's own title (its files are never written).
|
|
1503
|
+
function displayNameKeeps(source){
|
|
1504
|
+
return source==='claude'?'Claude and the parent session stay unchanged.'
|
|
1505
|
+
:`${(SRC[source]||{label:source}).label} keeps its own title; its files are not changed.`;
|
|
1506
|
+
}
|
|
1501
1507
|
function openSessionRename(identity){
|
|
1502
1508
|
if(sessionRenameSaving) return;
|
|
1503
1509
|
const s=RAW.sessions.find(x=>sessionIdentity(x)===identity); if(!s||s.rename_available===false) return;
|
|
@@ -1505,7 +1511,7 @@ function openSessionRename(identity){
|
|
|
1505
1511
|
const display=s.rename_scope==='display';
|
|
1506
1512
|
document.getElementById('sessionNameHeading').textContent=display?'Name in AgentTelemetry':'Rename session';
|
|
1507
1513
|
document.getElementById('sessionNameHelp').textContent=display
|
|
1508
|
-
|
|
1514
|
+
?`This display name is saved only in AgentTelemetry on this device. ${displayNameKeeps(s.source)} You will review the change before saving.`
|
|
1509
1515
|
:'Renaming changes the original tool’s saved session name. You’ll review a warning before it is saved.';
|
|
1510
1516
|
const input=document.getElementById('sessionNameInput');
|
|
1511
1517
|
input.value=s.title||s.project||s.id;
|
|
@@ -1530,7 +1536,7 @@ async function saveSessionName(name){
|
|
|
1530
1536
|
if(!await askConfirm({title:display?'Name in AgentTelemetry?':'Rename in '+(SRC[target.source]||{label:target.source}).label+'?',ok:display?'Save display name':'Rename in tool',
|
|
1531
1537
|
body:display?`<p>This changes only the local display name in <b>AgentTelemetry</b>.</p>
|
|
1532
1538
|
<p><b>${esc(preview.current_name)}</b> → <b>${esc(preview.name)}</b></p>
|
|
1533
|
-
<div class="modal-note"
|
|
1539
|
+
<div class="modal-note">${target.source==='claude'?'Claude’s transcript, native subagent name, and parent session stay unchanged.':esc(displayNameKeeps(target.source))} The private name is not included in the usage ledger or device sharing.</div>
|
|
1534
1540
|
<p>Target: <code>${esc(preview.target)}</code></p>`:
|
|
1535
1541
|
`<p>This changes the name in the <b>original coding tool</b>, not only AgentTelemetry.</p>
|
|
1536
1542
|
<p><b>${esc(preview.current_name)}</b> → <b>${esc(preview.name)}</b></p>
|
|
@@ -1657,7 +1663,7 @@ function openSessionDetails(s,related=false){
|
|
|
1657
1663
|
${sessionUsageDetails(s)}
|
|
1658
1664
|
${cursorDetailStatusDetails(s)}
|
|
1659
1665
|
${sessionUndatedUsageDetails(s)}
|
|
1660
|
-
${row("Name scope",s.rename_scope==='display'?'AgentTelemetry only — Claude and its parent session are unchanged.':null)}
|
|
1666
|
+
${row("Name scope",s.rename_scope==='display'?(s.source==='claude'?'AgentTelemetry only — Claude and its parent session are unchanged.':`AgentTelemetry only — ${esc((SRC[s.source]||{label:s.source}).label)} keeps its own title.`):null)}
|
|
1661
1667
|
${row("Source title",s.display_alias?esc(s.original_title):null)}
|
|
1662
1668
|
${sessionDelegationDetails(s)}
|
|
1663
1669
|
${row("Device",multiDevice()?esc(deviceName(s.device||RAW.device.id)):null)}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/agenttelemetry_app.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/agenttelemetry_app.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/agenttelemetry_app.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/LICENSE.antigravity.txt
RENAMED
|
File without changes
|
|
File without changes
|
{agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/LICENSE.openclaw.txt
RENAMED
|
File without changes
|
{agenttelemetry_app-2.2.0 → agenttelemetry_app-2.3.0}/static/tool-icons/LICENSE.simple-icons.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|