@klars/agentobs 0.2.1 → 0.2.2
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.
|
@@ -135,14 +135,19 @@ export async function importTranscript(db, file) {
|
|
|
135
135
|
const message = (row.message ?? {});
|
|
136
136
|
const usage = message.usage;
|
|
137
137
|
if (usage && typeof usage === 'object') {
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
|
|
138
|
+
// Fresh input = uncached input + cache writes. A cache write is real
|
|
139
|
+
// new content being sent for the first time (just stored for reuse), so
|
|
140
|
+
// excluding it made "tokens in" absurd: 24K in against 4.6M out, when
|
|
141
|
+
// real agent usage is heavily input-weighted. It is still tracked
|
|
142
|
+
// separately for costing, since it bills at 1.25x.
|
|
143
|
+
//
|
|
144
|
+
// Cache *reads* stay out of this total: they replay the entire context
|
|
145
|
+
// on every turn, so counting them would report the same tokens hundreds
|
|
146
|
+
// of times (2.4 billion across three sessions).
|
|
147
|
+
const cacheWrite = usage.cache_creation_input_tokens ?? 0;
|
|
148
|
+
result.tokensIn += (usage.input_tokens ?? 0) + cacheWrite;
|
|
144
149
|
result.tokensOut += usage.output_tokens ?? 0;
|
|
145
|
-
result.cacheWriteTokens +=
|
|
150
|
+
result.cacheWriteTokens += cacheWrite;
|
|
146
151
|
// cache_read is the whole conversation context replayed on every single
|
|
147
152
|
// message, so it re-counts the same tokens on each turn - summing it
|
|
148
153
|
// reported 413 million tokens for one session. It is tracked separately
|
|
@@ -186,6 +191,8 @@ export async function importTranscript(db, file) {
|
|
|
186
191
|
}
|
|
187
192
|
if (!sessionStarted)
|
|
188
193
|
return result;
|
|
194
|
+
// tokensIn already contains the cache-write tokens, so they are costed once
|
|
195
|
+
// at the base rate here and only topped up by the extra 0.25x premium.
|
|
189
196
|
const baseCost = computeCost(result.model, result.tokensIn, result.tokensOut);
|
|
190
197
|
const readCost = computeCost(result.model, result.cacheReadTokens, 0);
|
|
191
198
|
const writeCost = computeCost(result.model, result.cacheWriteTokens, 0);
|
|
@@ -194,7 +201,7 @@ export async function importTranscript(db, file) {
|
|
|
194
201
|
? null
|
|
195
202
|
: baseCost +
|
|
196
203
|
(readCost ?? 0) * CACHE_READ_RATE +
|
|
197
|
-
(writeCost ?? 0) * CACHE_WRITE_RATE;
|
|
204
|
+
(writeCost ?? 0) * (CACHE_WRITE_RATE - 1);
|
|
198
205
|
// Session totals come from the transcript's own usage blocks, which are
|
|
199
206
|
// authoritative - the per-call rows have no tokens to sum.
|
|
200
207
|
db.prepare(`UPDATE sessions
|
|
@@ -191,10 +191,10 @@ function renderTools(rows) {
|
|
|
191
191
|
body.replaceChildren();
|
|
192
192
|
if (rows.length === 0) {
|
|
193
193
|
const tr = document.createElement('tr');
|
|
194
|
-
const msg =
|
|
195
|
-
state.summary && state.summary.coarse_sessions > 0
|
|
196
|
-
? 'Coarse sessions record no tool calls — connect the Claude Code hook for per-tool detail.'
|
|
197
|
-
: 'No tool calls recorded yet.';
|
|
194
|
+
const msg =
|
|
195
|
+
state.summary && state.summary.coarse_sessions > 0
|
|
196
|
+
? 'Coarse sessions record no tool calls — connect the Claude Code hook for per-tool detail.'
|
|
197
|
+
: 'No tool calls recorded yet.';
|
|
198
198
|
tr.append(Object.assign(cell(msg, 'empty'), { colSpan: 5 }));
|
|
199
199
|
body.append(tr);
|
|
200
200
|
return;
|
|
@@ -630,6 +630,16 @@ function renderDelta(el, current, previous, { goodWhenUp = true } = {}) {
|
|
|
630
630
|
return;
|
|
631
631
|
}
|
|
632
632
|
const change = ((current - previous) / previous) * 100;
|
|
633
|
+
// A tiny previous period produces a huge, meaningless percentage - "+2300%"
|
|
634
|
+
// says nothing except that yesterday was nearly empty. Cap the display so
|
|
635
|
+
// the chip stays informative rather than theatrical.
|
|
636
|
+
if (Math.abs(change) > 999) {
|
|
637
|
+
el.className = `delta ${change > 0 === goodWhenUp ? 'delta-good' : 'delta-bad'}`;
|
|
638
|
+
el.textContent = `${change > 0 ? '▲' : '▼'} vs ${previous}`;
|
|
639
|
+
el.title = `Previous period had only ${previous}; a percentage would be misleading`;
|
|
640
|
+
el.removeAttribute('hidden');
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
633
643
|
if (!Number.isFinite(change) || Math.abs(change) < 0.5) {
|
|
634
644
|
// Below half a percent is noise; a chip there implies a signal that
|
|
635
645
|
// isn't real.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@klars/agentobs",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Observability and control layer for AI coding agents - see every tool call, token, and dollar your agents spend, and stop them before they do something risky.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Klars AI",
|