@thehammer/danx-session-telemetry 0.1.46 → 0.1.48

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.
@@ -0,0 +1 @@
1
+ 0662dcb067d30a9edb3819c5ee6ce60e7bdc40785373cb1beda5420b57d70e28
@@ -12,8 +12,8 @@
12
12
  * Claude Code's `SubagentStart` / `SubagentStop` hooks carry an `agent_type`
13
13
  * before the sub-agent has written anything and a FINISH for every sub-agent,
14
14
  * foreground or background. They ship from the danxbot plugin in the
15
- * claude-plugins repo (DX-3284, `danxbot/scripts/activity-report.mjs`), run the
16
- * `activity` subcommand of `@thehammer/danx-dashboard-mcp`, and reach the server
15
+ * claude-plugins repo (DX-3284; DX-4235 moved them into the plugin's hooks module), which calls
16
+ * `danxbot_api`, and reach the server
17
17
  * through `POST /api/plan-sessions/me/activity` — which writes the SAME
18
18
  * `dispatch_activities` rows through the same one server writer.
19
19
  *
@@ -50,12 +50,12 @@
50
50
  * Every row is an upsert keyed `(dispatch_id, kind, activity_id)`, and BOTH
51
51
  * producers derive `activity_id` from the same thing: the sub-agent's transcript
52
52
  * file basename, `agent-<agent_id>`. The watcher has it directly; a hook has the
53
- * `agent_id` and builds the same basename from it (`subagentActivityId` in
54
- * danx-dashboard-mcp's `activity.ts`, which also strips an `agent-` prefix the hooks docs show
55
- * on some `agent_id`s so both forms reach this key). A hook's `transcript_path` is NOT the
53
+ * `agent_id` and builds the same basename from it: `agent-` plus the id with one leading `agent-` stripped (the hooks docs
54
+ * show the prefix on some `agent_id`s), so both forms reach this key. That rule lives in the danxbot plugin's hooks module
55
+ * (claude-plugins repo). A hook's `transcript_path` is NOT the
56
56
  * sub-agent's file — on these events it is the MAIN session's transcript, and only
57
57
  * `SubagentStop` also carries the sub-agent's own as `agent_transcript_path`,
58
- * which the subcommand uses to refuse a key that disagrees. That basename is also
58
+ * which the plugin uses to refuse a key that disagrees. That basename is also
59
59
  * the key the ledger credits the sub-agent's tokens under, which is how
60
60
  * per-sub-agent tokens join to the running set without this table carrying a
61
61
  * token column. A task notification names the sub-agent by its bare id
@@ -12,8 +12,8 @@
12
12
  * Claude Code's `SubagentStart` / `SubagentStop` hooks carry an `agent_type`
13
13
  * before the sub-agent has written anything and a FINISH for every sub-agent,
14
14
  * foreground or background. They ship from the danxbot plugin in the
15
- * claude-plugins repo (DX-3284, `danxbot/scripts/activity-report.mjs`), run the
16
- * `activity` subcommand of `@thehammer/danx-dashboard-mcp`, and reach the server
15
+ * claude-plugins repo (DX-3284; DX-4235 moved them into the plugin's hooks module), which calls
16
+ * `danxbot_api`, and reach the server
17
17
  * through `POST /api/plan-sessions/me/activity` — which writes the SAME
18
18
  * `dispatch_activities` rows through the same one server writer.
19
19
  *
@@ -50,12 +50,12 @@
50
50
  * Every row is an upsert keyed `(dispatch_id, kind, activity_id)`, and BOTH
51
51
  * producers derive `activity_id` from the same thing: the sub-agent's transcript
52
52
  * file basename, `agent-<agent_id>`. The watcher has it directly; a hook has the
53
- * `agent_id` and builds the same basename from it (`subagentActivityId` in
54
- * danx-dashboard-mcp's `activity.ts`, which also strips an `agent-` prefix the hooks docs show
55
- * on some `agent_id`s so both forms reach this key). A hook's `transcript_path` is NOT the
53
+ * `agent_id` and builds the same basename from it: `agent-` plus the id with one leading `agent-` stripped (the hooks docs
54
+ * show the prefix on some `agent_id`s), so both forms reach this key. That rule lives in the danxbot plugin's hooks module
55
+ * (claude-plugins repo). A hook's `transcript_path` is NOT the
56
56
  * sub-agent's file — on these events it is the MAIN session's transcript, and only
57
57
  * `SubagentStop` also carries the sub-agent's own as `agent_transcript_path`,
58
- * which the subcommand uses to refuse a key that disagrees. That basename is also
58
+ * which the plugin uses to refuse a key that disagrees. That basename is also
59
59
  * the key the ledger credits the sub-agent's tokens under, which is how
60
60
  * per-sub-agent tokens join to the running set without this table carrying a
61
61
  * token column. A task notification names the sub-agent by its bare id
@@ -13,7 +13,7 @@ function emit(level, component, message, error) {
13
13
  entry.error = String(error);
14
14
  }
15
15
  // DX-2954 — every level goes to STDERR. The MCP server's
16
- // relay and the one-shot subcommands run this collector; the subcommands' STDOUT is a protocol
16
+ // relay and the `subagents-live` subcommand run this collector; the subcommand's STDOUT is a protocol
17
17
  // channel the plugin parses (and the MCP server's is JSON-RPC), so a log line there
18
18
  // would corrupt it. The worker captures both streams.
19
19
  console.error(JSON.stringify(entry));
package/dist/pricing.d.ts CHANGED
@@ -73,6 +73,8 @@ export interface PricedTokens {
73
73
  export declare const SYNTHETIC_MODEL = "<synthetic>";
74
74
  /** The `usage.speed` value a fast-mode reply carries. */
75
75
  export declare const FAST_SPEED = "fast";
76
+ /** DX-4867 - a prompt longer than this many tokens (input + cache reads + cache writes) is billed at the model's long-prompt card. */
77
+ export declare const LONG_PROMPT_TOKENS = 100000;
76
78
  /** DX-4593 - whether a `speed:"fast"` request on `model` is billed at the STANDARD row (see `FAST_BILLS_AT_STANDARD_RATE`): it has no fast row of its own. */
77
79
  export declare function billsFastAtStandardRate(model: string): boolean;
78
80
  /** One row of the list-price table: a model at a speed and its five per-million-token prices. */
@@ -119,6 +121,16 @@ export declare function priceUsage(model: string, speed: string, tokens: PricedT
119
121
  * standard table), or `null` when the tables have none. The default `PriceLookup` of {@link priceLedgerCredit}.
120
122
  */
121
123
  export declare function listedPrice(model: string, speed: string): ModelPrice | null;
124
+ /** The prompt tokens of one reply: everything it sent in, cached or not. */
125
+ export declare function promptTokensOf(tokens: PricedTokens): number;
126
+ /** DX-4867 - whether `model` has a second, long-prompt rate card (a model with one card never switches, whatever the prompt length). */
127
+ export declare function hasLongPromptCard(model: string): boolean;
128
+ /**
129
+ * DX-4867 - price ONE reply (not a bucket of replies): like {@link priceUsage}, but a reply whose prompt is longer than
130
+ * {@link LONG_PROMPT_TOKENS} on a model with a long-prompt card is charged that card. A model with one rate card prices
131
+ * exactly as `priceUsage` does.
132
+ */
133
+ export declare function priceReplyUsage(model: string, speed: string, tokens: PricedTokens): PriceResult;
122
134
  /**
123
135
  * DX-4595 - dollars for `tokens` at `price` (USD per million tokens): each bucket with its own price. The ONE copy of the formula: `priceUsage`
124
136
  * charges the list price with it, and spend pacing charges an account's own (editable) prices with it.
package/dist/pricing.js CHANGED
@@ -94,9 +94,23 @@ const PRICING = {
94
94
  "claude-sonnet-4-6": price(3, 15, 0.3),
95
95
  "claude-sonnet-4-5-20250929": price(3, 15, 0.3),
96
96
  "claude-sonnet-4-20250514": price(3, 15, 0.3),
97
+ // DX-4867 - Claude Haiku 5.5, the page's row for prompts up to 100K tokens ($0.10 in / $0.50 out, cache hits
98
+ // 0.1x = $0.01, 5m write 1.25x, 1h write 2x). It has a SECOND rate card above 100K prompt tokens
99
+ // (LONG_CONTEXT_PRICING below): this row is the card a usage BUCKET is priced with, because a bucket sums
100
+ // many replies and cannot say which of them crossed the line. `priceReplyUsage` picks the card per reply.
101
+ "claude-haiku-5-5": price(0.1, 0.5, 0.01),
97
102
  "claude-haiku-4-5": price(1, 5, 0.1),
98
103
  "claude-haiku-4-5-20251001": price(1, 5, 0.1),
99
104
  };
105
+ /** DX-4867 - a prompt longer than this many tokens (input + cache reads + cache writes) is billed at the model's long-prompt card. */
106
+ export const LONG_PROMPT_TOKENS = 100_000;
107
+ /**
108
+ * DX-4867 - the rate card of a reply whose prompt is longer than {@link LONG_PROMPT_TOKENS}, for the models that have
109
+ * one (Claude Haiku 5.5: $0.50 in / $2.50 out, cache hits 0.1x). Source: the pricing page's Claude Haiku 5.5 row.
110
+ */
111
+ const LONG_CONTEXT_PRICING = {
112
+ "claude-haiku-5-5": price(0.5, 2.5, 0.05),
113
+ };
100
114
  /**
101
115
  * Fast-mode prices (the page's "Fast mode pricing" section): premium
102
116
  * input/output with the prompt-caching multipliers applied ON TOP (so a 5m
@@ -175,6 +189,27 @@ export function listedPrice(model, speed) {
175
189
  const table = useFastTable ? FAST_PRICING : PRICING;
176
190
  return Object.hasOwn(table, model) ? table[model] : null;
177
191
  }
192
+ /** The prompt tokens of one reply: everything it sent in, cached or not. */
193
+ export function promptTokensOf(tokens) {
194
+ return tokens.input + tokens.cacheRead + tokens.cacheWrite5m + tokens.cacheWrite1h;
195
+ }
196
+ /** DX-4867 - whether `model` has a second, long-prompt rate card (a model with one card never switches, whatever the prompt length). */
197
+ export function hasLongPromptCard(model) {
198
+ return Object.hasOwn(LONG_CONTEXT_PRICING, model);
199
+ }
200
+ /**
201
+ * DX-4867 - price ONE reply (not a bucket of replies): like {@link priceUsage}, but a reply whose prompt is longer than
202
+ * {@link LONG_PROMPT_TOKENS} on a model with a long-prompt card is charged that card. A model with one rate card prices
203
+ * exactly as `priceUsage` does.
204
+ */
205
+ export function priceReplyUsage(model, speed, tokens) {
206
+ const long = promptTokensOf(tokens) > LONG_PROMPT_TOKENS && Object.hasOwn(LONG_CONTEXT_PRICING, model);
207
+ if (!long)
208
+ return priceUsage(model, speed, tokens);
209
+ if (speed === FAST_SPEED)
210
+ return { kind: "unpriced", model, speed };
211
+ return { kind: "priced", costUsd: costOfTokens(tokens, LONG_CONTEXT_PRICING[model]) };
212
+ }
178
213
  /**
179
214
  * DX-4595 - dollars for `tokens` at `price` (USD per million tokens): each bucket with its own price. The ONE copy of the formula: `priceUsage`
180
215
  * charges the list price with it, and spend pacing charges an account's own (editable) prices with it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thehammer/danx-session-telemetry",
3
- "version": "0.1.46",
3
+ "version": "0.1.48",
4
4
  "description": "Shared Claude Code transcript collector (DX-2962) — incremental JSONL tailing for the danxbot worker and the danx-dashboard-mcp MCP server's relay, so both run identical collector code.",
5
5
  "license": "MIT",
6
6
  "type": "module",