sidekick-shared 0.27.2 → 0.27.4
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.
- package/CHANGELOG.md +34 -0
- package/README.md +57 -14
- package/dist/aggregation/EventAggregator.d.ts +12 -2
- package/dist/aggregation/EventAggregator.js +49 -19
- package/dist/browser.d.ts +2 -1
- package/dist/browser.js +9 -2
- package/dist/extractors/tokenUsage.js +1 -0
- package/dist/formatters/sessionDump.d.ts +10 -2
- package/dist/formatters/sessionDump.js +45 -23
- package/dist/index.d.ts +7 -3
- package/dist/index.js +31 -10
- package/dist/node.d.ts +6 -1
- package/dist/node.js +12 -2
- package/dist/parsers/codexParser.d.ts +22 -2
- package/dist/parsers/codexParser.js +64 -5
- package/dist/parsers/subagentScanner.d.ts +9 -0
- package/dist/parsers/subagentScanner.js +45 -12
- package/dist/parsers/subagentStatsCache.d.ts +17 -0
- package/dist/parsers/subagentStatsCache.js +82 -0
- package/dist/parsers/subagentTraceParser.js +19 -10
- package/dist/providers/claudeCode.js +8 -41
- package/dist/providers/codex.js +28 -14
- package/dist/providers/codexDatabase.d.ts +16 -1
- package/dist/providers/codexDatabase.js +49 -1
- package/dist/providers/openCode.js +30 -4
- package/dist/report/htmlReportGenerator.js +33 -11
- package/dist/report/sessionReportInputs.d.ts +3 -1
- package/dist/report/sessionReportInputs.js +2 -0
- package/dist/report/types.d.ts +5 -0
- package/dist/schemas/sessionEvent.d.ts +30 -6
- package/dist/schemas/sessionEvent.js +7 -1
- package/dist/schemas/stateFile.d.ts +2 -0
- package/dist/schemas/stateFile.js +1 -0
- package/dist/sessionTokenTotals.d.ts +46 -0
- package/dist/sessionTokenTotals.js +138 -0
- package/dist/stateFile.d.ts +7 -0
- package/dist/tokenSummary.d.ts +27 -1
- package/dist/tokenSummary.js +50 -3
- package/dist/types/codex.d.ts +4 -0
- package/dist/types/sessionEvent.d.ts +22 -0
- package/dist/usage/claudeUsageDedupe.d.ts +53 -0
- package/dist/usage/claudeUsageDedupe.js +195 -0
- package/dist/usage/subagentUsage.d.ts +13 -0
- package/dist/usage/subagentUsage.js +21 -0
- package/dist/usage/usageEvents.d.ts +18 -2
- package/dist/usage/usageEvents.js +83 -8
- package/dist/watchers/eventBridge.js +5 -0
- package/dist/watchers/jsonlWatcher.d.ts +5 -0
- package/dist/watchers/jsonlWatcher.js +40 -5
- package/dist/watchers/types.d.ts +5 -0
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
8
8
|
exports.generateHtmlReport = generateHtmlReport;
|
|
9
9
|
const costProvenance_1 = require("../aggregation/costProvenance");
|
|
10
10
|
const tokenSummary_1 = require("../tokenSummary");
|
|
11
|
+
const sessionTokenTotals_1 = require("../sessionTokenTotals");
|
|
11
12
|
const htmlHelpers_1 = require("./htmlHelpers");
|
|
12
13
|
const logo_1 = require("./logo");
|
|
13
14
|
/**
|
|
@@ -16,7 +17,7 @@ const logo_1 = require("./logo");
|
|
|
16
17
|
function generateHtmlReport(metrics, transcript, options = {}) {
|
|
17
18
|
const { sessionFileName, includeThinking = true, includeToolDetail = true, theme = 'dark', } = options;
|
|
18
19
|
const duration = (0, htmlHelpers_1.formatDuration)(metrics.sessionStartTime, metrics.lastEventTime);
|
|
19
|
-
const
|
|
20
|
+
const tokens = (0, sessionTokenTotals_1.combineSessionTokenTotals)(metrics.tokens, options.subagents ?? []);
|
|
20
21
|
return `<!DOCTYPE html>
|
|
21
22
|
<html lang="en" data-theme="${theme}">
|
|
22
23
|
<head>
|
|
@@ -27,7 +28,7 @@ ${generateStyles()}
|
|
|
27
28
|
</head>
|
|
28
29
|
<body>
|
|
29
30
|
${generateHeader(metrics, sessionFileName, duration)}
|
|
30
|
-
${generateStatsCards(metrics,
|
|
31
|
+
${generateStatsCards(metrics, tokens, duration)}
|
|
31
32
|
${generateModelBreakdown(metrics)}
|
|
32
33
|
${generateToolBreakdown(metrics)}
|
|
33
34
|
${generateTranscriptControls()}
|
|
@@ -422,7 +423,9 @@ function generateHeader(metrics, sessionFileName, duration) {
|
|
|
422
423
|
</div>
|
|
423
424
|
</div>`;
|
|
424
425
|
}
|
|
425
|
-
function generateStatsCards(metrics,
|
|
426
|
+
function generateStatsCards(metrics, tokens, duration) {
|
|
427
|
+
const main = tokens.mainThread;
|
|
428
|
+
const hasSubagents = tokens.subagents.length > 0;
|
|
426
429
|
const toolCount = metrics.toolStats.reduce((sum, t) => sum + t.successCount + t.failureCount, 0);
|
|
427
430
|
const cards = [
|
|
428
431
|
{ label: 'Messages', value: String(metrics.messageCount), sub: `${metrics.eventCount} events` },
|
|
@@ -432,14 +435,28 @@ function generateStatsCards(metrics, totalTokens, duration) {
|
|
|
432
435
|
sub: metrics.sessionStartTime ? (0, htmlHelpers_1.formatTimestamp)(metrics.sessionStartTime) + ' start' : '',
|
|
433
436
|
},
|
|
434
437
|
{
|
|
435
|
-
label: tokenSummary_1.TOKEN_TOTAL_LABEL,
|
|
436
|
-
value: (0, htmlHelpers_1.fmtTokens)(
|
|
437
|
-
sub: `${(0, htmlHelpers_1.fmtTokens)(
|
|
438
|
+
label: hasSubagents ? `${tokenSummary_1.TOKEN_MAIN_THREAD_LABEL} — ${tokenSummary_1.TOKEN_TOTAL_LABEL}` : tokenSummary_1.TOKEN_TOTAL_LABEL,
|
|
439
|
+
value: (0, htmlHelpers_1.fmtTokens)(main.total),
|
|
440
|
+
sub: `${(0, htmlHelpers_1.fmtTokens)(main.input)} in · ${(0, htmlHelpers_1.fmtTokens)(main.cacheRead)} cache read · ${(0, htmlHelpers_1.fmtTokens)(main.cacheWrite)} cache write · ${(0, htmlHelpers_1.fmtTokens)(main.output)} out${main.billedOutsideBuckets > 0 ? ` · ${(0, htmlHelpers_1.fmtTokens)(main.billedOutsideBuckets)} reasoning` : ''}`,
|
|
438
441
|
},
|
|
442
|
+
...(hasSubagents
|
|
443
|
+
? [
|
|
444
|
+
{
|
|
445
|
+
label: `${tokenSummary_1.TOKEN_SUBAGENTS_LABEL} (${tokens.subagents.length})`,
|
|
446
|
+
value: (0, htmlHelpers_1.fmtTokens)(tokens.subagentTotal.total),
|
|
447
|
+
sub: `${(0, htmlHelpers_1.fmtTokens)(tokens.subagentTotal.cacheRead)} cache read · ${(0, htmlHelpers_1.fmtTokens)(tokens.subagentTotal.output)} out`,
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
label: tokenSummary_1.TOKEN_SESSION_TOTAL_LABEL,
|
|
451
|
+
value: (0, htmlHelpers_1.fmtTokens)(tokens.combined.total),
|
|
452
|
+
sub: 'main thread + subagents',
|
|
453
|
+
},
|
|
454
|
+
]
|
|
455
|
+
: []),
|
|
439
456
|
{
|
|
440
|
-
label: 'Cache',
|
|
441
|
-
value:
|
|
442
|
-
sub: `${(0, htmlHelpers_1.fmtTokens)(
|
|
457
|
+
label: 'Cache hit',
|
|
458
|
+
value: main.cacheHitRatio === null ? '—' : `${Math.round(main.cacheHitRatio * 100)}%`,
|
|
459
|
+
sub: `${(0, htmlHelpers_1.fmtTokens)(main.cacheRead)} of ${(0, htmlHelpers_1.fmtTokens)(main.context)} context tokens`,
|
|
443
460
|
},
|
|
444
461
|
{
|
|
445
462
|
label: 'Tool Calls',
|
|
@@ -474,7 +491,7 @@ function generateModelBreakdown(metrics) {
|
|
|
474
491
|
return `<div class="section">
|
|
475
492
|
<div class="section-title">Model Breakdown</div>
|
|
476
493
|
<table>
|
|
477
|
-
<thead><tr><th>Model</th><th>Calls</th><th
|
|
494
|
+
<thead><tr><th>Model</th><th>Calls</th><th>${tokenSummary_1.TOKEN_TOTAL_LABEL}</th><th>In / Out</th><th>Cost</th></tr></thead>
|
|
478
495
|
<tbody>${rows}</tbody>
|
|
479
496
|
</table>
|
|
480
497
|
</div>`;
|
|
@@ -534,7 +551,12 @@ function renderMessage(entry, includeThinking, includeToolDetail) {
|
|
|
534
551
|
? `<span class="message-model">${(0, htmlHelpers_1.escapeHtml)(entry.model)}</span>`
|
|
535
552
|
: '';
|
|
536
553
|
const tokenStr = entry.usage
|
|
537
|
-
? `<span class="message-tokens">${(0, htmlHelpers_1.fmtTokens)(
|
|
554
|
+
? `<span class="message-tokens">${(0, htmlHelpers_1.fmtTokens)((0, tokenSummary_1.summarizeTokens)({
|
|
555
|
+
inputTokens: entry.usage.input_tokens,
|
|
556
|
+
outputTokens: entry.usage.output_tokens,
|
|
557
|
+
cacheWriteTokens: entry.usage.cache_creation_input_tokens,
|
|
558
|
+
cacheReadTokens: entry.usage.cache_read_input_tokens,
|
|
559
|
+
}).total)} tokens incl. cache</span>`
|
|
538
560
|
: '';
|
|
539
561
|
const bodyParts = [];
|
|
540
562
|
for (const block of entry.content) {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { AggregatedMetrics } from '../aggregation/types';
|
|
12
12
|
import type { SessionProviderBase } from '../providers/types';
|
|
13
|
-
import type { SessionEvent } from '../types/sessionEvent';
|
|
13
|
+
import type { SessionEvent, SubagentStats } from '../types/sessionEvent';
|
|
14
14
|
import type { TranscriptEntry } from './types';
|
|
15
15
|
export interface SessionReportInputs {
|
|
16
16
|
sessionPath: string;
|
|
@@ -18,6 +18,8 @@ export interface SessionReportInputs {
|
|
|
18
18
|
events: SessionEvent[];
|
|
19
19
|
metrics: AggregatedMetrics;
|
|
20
20
|
transcript: TranscriptEntry[];
|
|
21
|
+
/** The session's subagents; pass as `HtmlReportOptions.subagents` for the session total. */
|
|
22
|
+
subagents: SubagentStats[];
|
|
21
23
|
}
|
|
22
24
|
/** Read a session once and build the aggregator metrics and report transcript from it. */
|
|
23
25
|
export declare function readSessionReportInputs(provider: SessionProviderBase, sessionPath: string): SessionReportInputs;
|
|
@@ -13,6 +13,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
13
13
|
exports.readSessionReportInputs = readSessionReportInputs;
|
|
14
14
|
const EventAggregator_1 = require("../aggregation/EventAggregator");
|
|
15
15
|
const sessionStats_1 = require("../sessionStats");
|
|
16
|
+
const sessionTokenTotals_1 = require("../sessionTokenTotals");
|
|
16
17
|
const transcriptParser_1 = require("./transcriptParser");
|
|
17
18
|
/** Read a session once and build the aggregator metrics and report transcript from it. */
|
|
18
19
|
function readSessionReportInputs(provider, sessionPath) {
|
|
@@ -30,5 +31,6 @@ function readSessionReportInputs(provider, sessionPath) {
|
|
|
30
31
|
events,
|
|
31
32
|
metrics: aggregator.getMetrics(),
|
|
32
33
|
transcript: (0, transcriptParser_1.parseTranscriptFromEvents)(events),
|
|
34
|
+
subagents: (0, sessionTokenTotals_1.scanSessionSubagents)(provider, sessionPath),
|
|
33
35
|
};
|
|
34
36
|
}
|
package/dist/report/types.d.ts
CHANGED
|
@@ -42,4 +42,9 @@ export interface HtmlReportOptions {
|
|
|
42
42
|
includeToolDetail?: boolean;
|
|
43
43
|
/** Color theme (default: 'dark'). */
|
|
44
44
|
theme?: 'dark' | 'light';
|
|
45
|
+
/**
|
|
46
|
+
* The session's subagents (`provider.scanSubagents()`). Their tokens are
|
|
47
|
+
* reported beside the main thread and added to the session total.
|
|
48
|
+
*/
|
|
49
|
+
subagents?: readonly import('../types/sessionEvent').SubagentStats[];
|
|
45
50
|
}
|
|
@@ -51,8 +51,12 @@ export declare const sessionMessageSchema: z.ZodObject<{
|
|
|
51
51
|
source: z.ZodOptional<z.ZodString>;
|
|
52
52
|
}, z.core.$strip>;
|
|
53
53
|
}, z.core.$strip>>;
|
|
54
|
+
usageKind: z.ZodOptional<z.ZodEnum<{
|
|
55
|
+
call: "call";
|
|
56
|
+
correction: "correction";
|
|
57
|
+
}>>;
|
|
54
58
|
content: z.ZodOptional<z.ZodUnknown>;
|
|
55
|
-
stop_reason: z.ZodOptional<z.ZodString
|
|
59
|
+
stop_reason: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
|
|
56
60
|
}, z.core.$strip>;
|
|
57
61
|
export declare const permissionModeSchema: z.ZodEnum<{
|
|
58
62
|
default: "default";
|
|
@@ -141,8 +145,12 @@ export declare const userSessionEventSchema: z.ZodObject<{
|
|
|
141
145
|
source: z.ZodOptional<z.ZodString>;
|
|
142
146
|
}, z.core.$strip>;
|
|
143
147
|
}, z.core.$strip>>;
|
|
148
|
+
usageKind: z.ZodOptional<z.ZodEnum<{
|
|
149
|
+
call: "call";
|
|
150
|
+
correction: "correction";
|
|
151
|
+
}>>;
|
|
144
152
|
content: z.ZodOptional<z.ZodUnknown>;
|
|
145
|
-
stop_reason: z.ZodOptional<z.ZodString
|
|
153
|
+
stop_reason: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
|
|
146
154
|
}, z.core.$strip>;
|
|
147
155
|
}, z.core.$strip>;
|
|
148
156
|
export declare const assistantSessionEventSchema: z.ZodObject<{
|
|
@@ -226,8 +234,12 @@ export declare const assistantSessionEventSchema: z.ZodObject<{
|
|
|
226
234
|
source: z.ZodOptional<z.ZodString>;
|
|
227
235
|
}, z.core.$strip>;
|
|
228
236
|
}, z.core.$strip>>;
|
|
237
|
+
usageKind: z.ZodOptional<z.ZodEnum<{
|
|
238
|
+
call: "call";
|
|
239
|
+
correction: "correction";
|
|
240
|
+
}>>;
|
|
229
241
|
content: z.ZodOptional<z.ZodUnknown>;
|
|
230
|
-
stop_reason: z.ZodOptional<z.ZodString
|
|
242
|
+
stop_reason: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
|
|
231
243
|
}, z.core.$strip>;
|
|
232
244
|
}, z.core.$strip>;
|
|
233
245
|
export declare const summarySessionEventSchema: z.ZodType<SummarySessionEvent>;
|
|
@@ -312,8 +324,12 @@ export declare const systemSessionEventSchema: z.ZodObject<{
|
|
|
312
324
|
source: z.ZodOptional<z.ZodString>;
|
|
313
325
|
}, z.core.$strip>;
|
|
314
326
|
}, z.core.$strip>>;
|
|
327
|
+
usageKind: z.ZodOptional<z.ZodEnum<{
|
|
328
|
+
call: "call";
|
|
329
|
+
correction: "correction";
|
|
330
|
+
}>>;
|
|
315
331
|
content: z.ZodOptional<z.ZodUnknown>;
|
|
316
|
-
stop_reason: z.ZodOptional<z.ZodString
|
|
332
|
+
stop_reason: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
|
|
317
333
|
}, z.core.$strip>>;
|
|
318
334
|
}, z.core.$strip>;
|
|
319
335
|
export declare const toolUseSessionEventSchema: z.ZodObject<{
|
|
@@ -401,8 +417,12 @@ export declare const toolUseSessionEventSchema: z.ZodObject<{
|
|
|
401
417
|
source: z.ZodOptional<z.ZodString>;
|
|
402
418
|
}, z.core.$strip>;
|
|
403
419
|
}, z.core.$strip>>;
|
|
420
|
+
usageKind: z.ZodOptional<z.ZodEnum<{
|
|
421
|
+
call: "call";
|
|
422
|
+
correction: "correction";
|
|
423
|
+
}>>;
|
|
404
424
|
content: z.ZodOptional<z.ZodUnknown>;
|
|
405
|
-
stop_reason: z.ZodOptional<z.ZodString
|
|
425
|
+
stop_reason: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
|
|
406
426
|
}, z.core.$strip>>;
|
|
407
427
|
}, z.core.$strip>;
|
|
408
428
|
export declare const toolResultSessionEventSchema: z.ZodObject<{
|
|
@@ -491,8 +511,12 @@ export declare const toolResultSessionEventSchema: z.ZodObject<{
|
|
|
491
511
|
source: z.ZodOptional<z.ZodString>;
|
|
492
512
|
}, z.core.$strip>;
|
|
493
513
|
}, z.core.$strip>>;
|
|
514
|
+
usageKind: z.ZodOptional<z.ZodEnum<{
|
|
515
|
+
call: "call";
|
|
516
|
+
correction: "correction";
|
|
517
|
+
}>>;
|
|
494
518
|
content: z.ZodOptional<z.ZodUnknown>;
|
|
495
|
-
stop_reason: z.ZodOptional<z.ZodString
|
|
519
|
+
stop_reason: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
|
|
496
520
|
}, z.core.$strip>>;
|
|
497
521
|
}, z.core.$strip>;
|
|
498
522
|
export declare const sessionEventSchema: z.ZodType<SessionEvent>;
|
|
@@ -29,8 +29,14 @@ exports.sessionMessageSchema = zod_1.z.object({
|
|
|
29
29
|
model: zod_1.z.string().optional(),
|
|
30
30
|
usage: exports.messageUsageSchema.optional(),
|
|
31
31
|
normalizedUsage: usageNormalization_1.normalizedUsageSchema.optional(),
|
|
32
|
+
usageKind: zod_1.z.enum(['call', 'correction']).optional(),
|
|
32
33
|
content: zod_1.z.unknown().optional(),
|
|
33
|
-
stop_reason:
|
|
34
|
+
// Streaming lines can carry `stop_reason: null`; rejecting it dropped the
|
|
35
|
+
// whole event (text, tools, and usage), so normalize it to absent.
|
|
36
|
+
stop_reason: zod_1.z
|
|
37
|
+
.string()
|
|
38
|
+
.nullish()
|
|
39
|
+
.transform((value) => value ?? undefined),
|
|
34
40
|
});
|
|
35
41
|
// ── PermissionMode ──
|
|
36
42
|
exports.permissionModeSchema = zod_1.z.enum([
|
|
@@ -58,6 +58,7 @@ export declare const stateFileContextSchema: z.ZodObject<{
|
|
|
58
58
|
contextWindowSize: z.ZodNullable<z.ZodNumber>;
|
|
59
59
|
totalInputTokens: z.ZodNullable<z.ZodNumber>;
|
|
60
60
|
totalOutputTokens: z.ZodNullable<z.ZodNumber>;
|
|
61
|
+
totalTokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
61
62
|
}, z.core.$strip>;
|
|
62
63
|
export declare const stateFileSessionSchema: z.ZodObject<{
|
|
63
64
|
sessionId: z.ZodNullable<z.ZodString>;
|
|
@@ -166,6 +167,7 @@ export declare const sidekickStateFileSchema: z.ZodObject<{
|
|
|
166
167
|
contextWindowSize: z.ZodNullable<z.ZodNumber>;
|
|
167
168
|
totalInputTokens: z.ZodNullable<z.ZodNumber>;
|
|
168
169
|
totalOutputTokens: z.ZodNullable<z.ZodNumber>;
|
|
170
|
+
totalTokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
169
171
|
}, z.core.$strip>>;
|
|
170
172
|
session: z.ZodNullable<z.ZodObject<{
|
|
171
173
|
sessionId: z.ZodNullable<z.ZodString>;
|
|
@@ -39,6 +39,7 @@ exports.stateFileContextSchema = zod_1.z.object({
|
|
|
39
39
|
contextWindowSize: zod_1.z.number().nullable(),
|
|
40
40
|
totalInputTokens: zod_1.z.number().nullable(),
|
|
41
41
|
totalOutputTokens: zod_1.z.number().nullable(),
|
|
42
|
+
totalTokens: zod_1.z.number().nullable().optional(),
|
|
42
43
|
});
|
|
43
44
|
exports.stateFileSessionSchema = zod_1.z.object({
|
|
44
45
|
sessionId: zod_1.z.string().nullable(),
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A session's token total, main thread plus subagents, in the shared
|
|
3
|
+
* `summarizeTokens` vocabulary.
|
|
4
|
+
*
|
|
5
|
+
* Subagents run in their own transcripts (Claude Code `subagents/agent-*.jsonl`,
|
|
6
|
+
* Codex spawned rollouts, OpenCode child sessions), so the main-thread reader
|
|
7
|
+
* never sees their usage. Every surface that shows "the session total" goes
|
|
8
|
+
* through this module so dump, report, and both dashboards agree.
|
|
9
|
+
*
|
|
10
|
+
* @module sessionTokenTotals
|
|
11
|
+
*/
|
|
12
|
+
import type { SessionProviderBase } from './providers/types';
|
|
13
|
+
import type { SubagentStats } from './types/sessionEvent';
|
|
14
|
+
import { type TokenSummary, type TokenTotalsLike } from './tokenSummary';
|
|
15
|
+
export interface SubagentTokenSummary {
|
|
16
|
+
agentId: string;
|
|
17
|
+
agentType?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
summary: TokenSummary;
|
|
20
|
+
}
|
|
21
|
+
export interface SessionTokenTotals {
|
|
22
|
+
/** Tokens the session's own transcript billed. */
|
|
23
|
+
mainThread: TokenSummary;
|
|
24
|
+
/** One entry per subagent, in provider order. */
|
|
25
|
+
subagents: SubagentTokenSummary[];
|
|
26
|
+
/** All subagents together. */
|
|
27
|
+
subagentTotal: TokenSummary;
|
|
28
|
+
/** Main thread plus subagents. */
|
|
29
|
+
combined: TokenSummary;
|
|
30
|
+
}
|
|
31
|
+
/** Token record of one subagent, in `TokenTotalsLike` form. */
|
|
32
|
+
export declare function subagentTokenTotals(stats: SubagentStats): TokenTotalsLike;
|
|
33
|
+
/** Combine an already-known main-thread total with a session's subagent stats. */
|
|
34
|
+
export declare function combineSessionTokenTotals(mainThread: TokenTotalsLike, subagents: readonly SubagentStats[]): SessionTokenTotals;
|
|
35
|
+
/** Sum the usage a provider reader reports for one session transcript. */
|
|
36
|
+
export declare function readMainThreadTokenTotals(provider: SessionProviderBase, sessionPath: string): TokenTotalsLike;
|
|
37
|
+
/**
|
|
38
|
+
* Main thread plus subagents for one session. Pass `mainThread` when the
|
|
39
|
+
* caller already aggregated the transcript (an `EventAggregator`'s tokens) to
|
|
40
|
+
* skip re-reading it. Subagent discovery failures degrade to "no subagents".
|
|
41
|
+
*/
|
|
42
|
+
export declare function collectSessionTokenTotals(provider: SessionProviderBase, sessionPath: string, options?: {
|
|
43
|
+
mainThread?: TokenTotalsLike;
|
|
44
|
+
}): SessionTokenTotals;
|
|
45
|
+
/** A session's subagents by transcript path; discovery failures yield none. */
|
|
46
|
+
export declare function scanSessionSubagents(provider: SessionProviderBase, sessionPath: string): SubagentStats[];
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* A session's token total, main thread plus subagents, in the shared
|
|
4
|
+
* `summarizeTokens` vocabulary.
|
|
5
|
+
*
|
|
6
|
+
* Subagents run in their own transcripts (Claude Code `subagents/agent-*.jsonl`,
|
|
7
|
+
* Codex spawned rollouts, OpenCode child sessions), so the main-thread reader
|
|
8
|
+
* never sees their usage. Every surface that shows "the session total" goes
|
|
9
|
+
* through this module so dump, report, and both dashboards agree.
|
|
10
|
+
*
|
|
11
|
+
* @module sessionTokenTotals
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.subagentTokenTotals = subagentTokenTotals;
|
|
48
|
+
exports.combineSessionTokenTotals = combineSessionTokenTotals;
|
|
49
|
+
exports.readMainThreadTokenTotals = readMainThreadTokenTotals;
|
|
50
|
+
exports.collectSessionTokenTotals = collectSessionTokenTotals;
|
|
51
|
+
exports.scanSessionSubagents = scanSessionSubagents;
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const usageNormalization_1 = require("./usageNormalization");
|
|
54
|
+
const tokenSummary_1 = require("./tokenSummary");
|
|
55
|
+
/** Token record of one subagent, in `TokenTotalsLike` form. */
|
|
56
|
+
function subagentTokenTotals(stats) {
|
|
57
|
+
return {
|
|
58
|
+
inputTokens: stats.inputTokens,
|
|
59
|
+
outputTokens: stats.outputTokens,
|
|
60
|
+
cacheReadTokens: stats.cacheReadTokens,
|
|
61
|
+
cacheWriteTokens: stats.cacheWriteTokens,
|
|
62
|
+
reasoningTokens: stats.reasoningTokens,
|
|
63
|
+
...(typeof stats.totalTokens === 'number' ? { totalTokens: stats.totalTokens } : {}),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Combine an already-known main-thread total with a session's subagent stats. */
|
|
67
|
+
function combineSessionTokenTotals(mainThread, subagents) {
|
|
68
|
+
const subagentRecords = subagents.map(subagentTokenTotals);
|
|
69
|
+
const main = (0, tokenSummary_1.summarizeTokens)(mainThread);
|
|
70
|
+
const mainRecord = {
|
|
71
|
+
inputTokens: main.input,
|
|
72
|
+
outputTokens: main.output,
|
|
73
|
+
cacheReadTokens: main.cacheRead,
|
|
74
|
+
cacheWriteTokens: main.cacheWrite,
|
|
75
|
+
reasoningTokens: main.reasoning,
|
|
76
|
+
totalTokens: main.total,
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
mainThread: main,
|
|
80
|
+
subagents: subagents.map((stats, index) => ({
|
|
81
|
+
agentId: stats.agentId,
|
|
82
|
+
...(stats.agentType ? { agentType: stats.agentType } : {}),
|
|
83
|
+
...(stats.description ? { description: stats.description } : {}),
|
|
84
|
+
summary: (0, tokenSummary_1.summarizeTokens)(subagentRecords[index]),
|
|
85
|
+
})),
|
|
86
|
+
subagentTotal: (0, tokenSummary_1.summarizeTokens)((0, tokenSummary_1.sumTokenTotals)(subagentRecords)),
|
|
87
|
+
combined: (0, tokenSummary_1.summarizeTokens)((0, tokenSummary_1.sumTokenTotals)([mainRecord, ...subagentRecords])),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/** Sum the usage a provider reader reports for one session transcript. */
|
|
91
|
+
function readMainThreadTokenTotals(provider, sessionPath) {
|
|
92
|
+
const reader = provider.createReader(sessionPath);
|
|
93
|
+
const events = reader.readAll();
|
|
94
|
+
reader.flush();
|
|
95
|
+
let inputTokens = 0;
|
|
96
|
+
let outputTokens = 0;
|
|
97
|
+
let cacheReadTokens = 0;
|
|
98
|
+
let cacheWriteTokens = 0;
|
|
99
|
+
let reasoningTokens = 0;
|
|
100
|
+
let totalTokens = 0;
|
|
101
|
+
for (const event of events) {
|
|
102
|
+
const usage = (0, usageNormalization_1.extractNormalizedUsage)(event);
|
|
103
|
+
if (!usage)
|
|
104
|
+
continue;
|
|
105
|
+
inputTokens += usage.uncachedInputTokens;
|
|
106
|
+
outputTokens += usage.outputTokens;
|
|
107
|
+
cacheReadTokens += usage.cacheReadTokens;
|
|
108
|
+
cacheWriteTokens += usage.cacheWriteTokens;
|
|
109
|
+
reasoningTokens += usage.reasoningTokens;
|
|
110
|
+
totalTokens += usage.totalTokens;
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
inputTokens,
|
|
114
|
+
outputTokens,
|
|
115
|
+
cacheReadTokens,
|
|
116
|
+
cacheWriteTokens,
|
|
117
|
+
reasoningTokens,
|
|
118
|
+
totalTokens,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Main thread plus subagents for one session. Pass `mainThread` when the
|
|
123
|
+
* caller already aggregated the transcript (an `EventAggregator`'s tokens) to
|
|
124
|
+
* skip re-reading it. Subagent discovery failures degrade to "no subagents".
|
|
125
|
+
*/
|
|
126
|
+
function collectSessionTokenTotals(provider, sessionPath, options = {}) {
|
|
127
|
+
const mainThread = options.mainThread ?? readMainThreadTokenTotals(provider, sessionPath);
|
|
128
|
+
return combineSessionTokenTotals(mainThread, scanSessionSubagents(provider, sessionPath));
|
|
129
|
+
}
|
|
130
|
+
/** A session's subagents by transcript path; discovery failures yield none. */
|
|
131
|
+
function scanSessionSubagents(provider, sessionPath) {
|
|
132
|
+
try {
|
|
133
|
+
return provider.scanSubagents(path.dirname(sessionPath), provider.getSessionId(sessionPath));
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
}
|
package/dist/stateFile.d.ts
CHANGED
|
@@ -44,8 +44,15 @@ export interface StateFileAccount {
|
|
|
44
44
|
export interface StateFileContext {
|
|
45
45
|
usedPercentage: number | null;
|
|
46
46
|
contextWindowSize: number | null;
|
|
47
|
+
/** Input tokens including cache reads and writes (Claude Code status-line semantics). */
|
|
47
48
|
totalInputTokens: number | null;
|
|
48
49
|
totalOutputTokens: number | null;
|
|
50
|
+
/**
|
|
51
|
+
* Every token the session billed, cache included — the `summarizeTokens().total`
|
|
52
|
+
* figure the dashboards show. Absent or null when the writer does not know it
|
|
53
|
+
* (the status line only sees the current context).
|
|
54
|
+
*/
|
|
55
|
+
totalTokens?: number | null;
|
|
49
56
|
}
|
|
50
57
|
export interface StateFileSession {
|
|
51
58
|
sessionId: string | null;
|
package/dist/tokenSummary.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface TokenTotalsLike {
|
|
|
28
28
|
outputTokens: number;
|
|
29
29
|
cacheWriteTokens?: number;
|
|
30
30
|
cacheReadTokens?: number;
|
|
31
|
+
/** Reasoning tokens, when the source tracks them (display only). */
|
|
32
|
+
reasoningTokens?: number;
|
|
31
33
|
/**
|
|
32
34
|
* A provider-semantics-aware total, when the source already computed one
|
|
33
35
|
* (for example `AggregatedTokens.totalTokens`). Preferred over the
|
|
@@ -51,12 +53,36 @@ export interface TokenSummary {
|
|
|
51
53
|
cacheWrite: number;
|
|
52
54
|
/** Fraction of context tokens that came from cache reads (0..1), or null with no input. */
|
|
53
55
|
cacheHitRatio: number | null;
|
|
56
|
+
/** Reasoning tokens reported by the provider (display only; may be inside `output`). */
|
|
57
|
+
reasoning: number;
|
|
58
|
+
/**
|
|
59
|
+
* Tokens in `total` beyond the four buckets — reasoning billed outside
|
|
60
|
+
* `output` by providers that report it separately. Zero when the buckets
|
|
61
|
+
* already add up to `total`.
|
|
62
|
+
*/
|
|
63
|
+
billedOutsideBuckets: number;
|
|
54
64
|
}
|
|
55
65
|
/** Column label for `TokenSummary.total` on any surface. */
|
|
56
66
|
export declare const TOKEN_TOTAL_LABEL = "Total (incl. cache)";
|
|
57
67
|
/** Column label for `TokenSummary.context` on any surface. */
|
|
58
68
|
export declare const TOKEN_CONTEXT_LABEL = "Context";
|
|
69
|
+
/** Label for a session's own (main-thread) tokens when subagents are shown beside it. */
|
|
70
|
+
export declare const TOKEN_MAIN_THREAD_LABEL = "Main thread";
|
|
71
|
+
/** Label for the tokens a session's subagents used. */
|
|
72
|
+
export declare const TOKEN_SUBAGENTS_LABEL = "Subagents";
|
|
73
|
+
/** Label for main thread plus subagents. */
|
|
74
|
+
export declare const TOKEN_SESSION_TOTAL_LABEL = "Session total (incl. subagents)";
|
|
59
75
|
/** Derive the shared token vocabulary from any four-bucket token record. */
|
|
60
76
|
export declare function summarizeTokens(totals: TokenTotalsLike): TokenSummary;
|
|
61
|
-
/**
|
|
77
|
+
/**
|
|
78
|
+
* One-line breakdown whose parts add up to the headline:
|
|
79
|
+
* `1.2M total incl. cache (12k in · 1.1M cache read · 40k cache write · 30k out)`.
|
|
80
|
+
* Pass the surface's own number formatter; the default prints plain integers.
|
|
81
|
+
*/
|
|
82
|
+
export declare function formatTokenBreakdown(summary: TokenSummary, fmt?: (n: number) => string): string;
|
|
83
|
+
/**
|
|
84
|
+
* Sum several token records bucket-by-bucket before summarizing. `totalTokens`
|
|
85
|
+
* is summed only when every record carries one, so provider-aware totals
|
|
86
|
+
* survive the sum instead of falling back to the four-bucket arithmetic.
|
|
87
|
+
*/
|
|
62
88
|
export declare function sumTokenTotals(records: readonly TokenTotalsLike[]): TokenTotalsLike;
|
package/dist/tokenSummary.js
CHANGED
|
@@ -24,13 +24,20 @@
|
|
|
24
24
|
* @module tokenSummary
|
|
25
25
|
*/
|
|
26
26
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
-
exports.TOKEN_CONTEXT_LABEL = exports.TOKEN_TOTAL_LABEL = void 0;
|
|
27
|
+
exports.TOKEN_SESSION_TOTAL_LABEL = exports.TOKEN_SUBAGENTS_LABEL = exports.TOKEN_MAIN_THREAD_LABEL = exports.TOKEN_CONTEXT_LABEL = exports.TOKEN_TOTAL_LABEL = void 0;
|
|
28
28
|
exports.summarizeTokens = summarizeTokens;
|
|
29
|
+
exports.formatTokenBreakdown = formatTokenBreakdown;
|
|
29
30
|
exports.sumTokenTotals = sumTokenTotals;
|
|
30
31
|
/** Column label for `TokenSummary.total` on any surface. */
|
|
31
32
|
exports.TOKEN_TOTAL_LABEL = 'Total (incl. cache)';
|
|
32
33
|
/** Column label for `TokenSummary.context` on any surface. */
|
|
33
34
|
exports.TOKEN_CONTEXT_LABEL = 'Context';
|
|
35
|
+
/** Label for a session's own (main-thread) tokens when subagents are shown beside it. */
|
|
36
|
+
exports.TOKEN_MAIN_THREAD_LABEL = 'Main thread';
|
|
37
|
+
/** Label for the tokens a session's subagents used. */
|
|
38
|
+
exports.TOKEN_SUBAGENTS_LABEL = 'Subagents';
|
|
39
|
+
/** Label for main thread plus subagents. */
|
|
40
|
+
exports.TOKEN_SESSION_TOTAL_LABEL = 'Session total (incl. subagents)';
|
|
34
41
|
function count(value) {
|
|
35
42
|
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
|
|
36
43
|
}
|
|
@@ -54,19 +61,59 @@ function summarizeTokens(totals) {
|
|
|
54
61
|
cacheRead,
|
|
55
62
|
cacheWrite,
|
|
56
63
|
cacheHitRatio: context > 0 ? cacheRead / context : null,
|
|
64
|
+
reasoning: count(totals.reasoningTokens),
|
|
65
|
+
billedOutsideBuckets: Math.max(0, total - fourBucket),
|
|
57
66
|
};
|
|
58
67
|
}
|
|
59
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* One-line breakdown whose parts add up to the headline:
|
|
70
|
+
* `1.2M total incl. cache (12k in · 1.1M cache read · 40k cache write · 30k out)`.
|
|
71
|
+
* Pass the surface's own number formatter; the default prints plain integers.
|
|
72
|
+
*/
|
|
73
|
+
function formatTokenBreakdown(summary, fmt = (n) => String(n)) {
|
|
74
|
+
const parts = [
|
|
75
|
+
`${fmt(summary.input)} in`,
|
|
76
|
+
`${fmt(summary.cacheRead)} cache read`,
|
|
77
|
+
`${fmt(summary.cacheWrite)} cache write`,
|
|
78
|
+
`${fmt(summary.output)} out`,
|
|
79
|
+
];
|
|
80
|
+
if (summary.billedOutsideBuckets > 0) {
|
|
81
|
+
parts.push(`${fmt(summary.billedOutsideBuckets)} reasoning`);
|
|
82
|
+
}
|
|
83
|
+
return `${fmt(summary.total)} total incl. cache (${parts.join(' · ')})`;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Sum several token records bucket-by-bucket before summarizing. `totalTokens`
|
|
87
|
+
* is summed only when every record carries one, so provider-aware totals
|
|
88
|
+
* survive the sum instead of falling back to the four-bucket arithmetic.
|
|
89
|
+
*/
|
|
60
90
|
function sumTokenTotals(records) {
|
|
61
91
|
let inputTokens = 0;
|
|
62
92
|
let outputTokens = 0;
|
|
63
93
|
let cacheWriteTokens = 0;
|
|
64
94
|
let cacheReadTokens = 0;
|
|
95
|
+
let reasoningTokens = 0;
|
|
96
|
+
let totalTokens = 0;
|
|
97
|
+
let allHaveTotal = records.length > 0;
|
|
65
98
|
for (const record of records) {
|
|
66
99
|
inputTokens += count(record.inputTokens);
|
|
67
100
|
outputTokens += count(record.outputTokens);
|
|
68
101
|
cacheWriteTokens += count(record.cacheWriteTokens);
|
|
69
102
|
cacheReadTokens += count(record.cacheReadTokens);
|
|
103
|
+
reasoningTokens += count(record.reasoningTokens);
|
|
104
|
+
if (typeof record.totalTokens === 'number' && Number.isFinite(record.totalTokens)) {
|
|
105
|
+
totalTokens += count(record.totalTokens);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
allHaveTotal = false;
|
|
109
|
+
}
|
|
70
110
|
}
|
|
71
|
-
return {
|
|
111
|
+
return {
|
|
112
|
+
inputTokens,
|
|
113
|
+
outputTokens,
|
|
114
|
+
cacheWriteTokens,
|
|
115
|
+
cacheReadTokens,
|
|
116
|
+
reasoningTokens,
|
|
117
|
+
...(allHaveTotal ? { totalTokens } : {}),
|
|
118
|
+
};
|
|
72
119
|
}
|
package/dist/types/codex.d.ts
CHANGED
|
@@ -287,4 +287,8 @@ export interface CodexDbThread {
|
|
|
287
287
|
git_branch?: string;
|
|
288
288
|
git_commit?: string;
|
|
289
289
|
forked_from_id?: string;
|
|
290
|
+
/** `cli`, `exec`, … or a JSON object string for spawned subagents. */
|
|
291
|
+
source?: string;
|
|
292
|
+
agent_nickname?: string | null;
|
|
293
|
+
agent_role?: string | null;
|
|
290
294
|
}
|
|
@@ -45,6 +45,13 @@ export interface SessionMessage {
|
|
|
45
45
|
usage?: MessageUsage;
|
|
46
46
|
/** Authoritative disjoint usage projection, when normalized by a provider parser. */
|
|
47
47
|
normalizedUsage?: import('../usageNormalization').NormalizedUsage;
|
|
48
|
+
/**
|
|
49
|
+
* `'correction'` marks usage that tops up a call already counted (a later
|
|
50
|
+
* split line of the same response reported more tokens). Add it to token and
|
|
51
|
+
* cost totals, but do not count another call or context sample. Absent means
|
|
52
|
+
* a regular call.
|
|
53
|
+
*/
|
|
54
|
+
usageKind?: 'call' | 'correction';
|
|
48
55
|
/** Message content (may be string or structured content) */
|
|
49
56
|
content?: unknown;
|
|
50
57
|
/** Provider stop reason (`end_turn`, `tool_use`, …) when the transcript carries one. */
|
|
@@ -222,6 +229,12 @@ export interface TokenUsage {
|
|
|
222
229
|
reportedCost?: number;
|
|
223
230
|
/** Number of reasoning/thinking tokens */
|
|
224
231
|
reasoningTokens?: number;
|
|
232
|
+
/**
|
|
233
|
+
* `'correction'` tops up a call already counted (a later split line of the
|
|
234
|
+
* same response reported more tokens): add it to totals, but do not count a
|
|
235
|
+
* call or treat it as a context-size sample.
|
|
236
|
+
*/
|
|
237
|
+
usageKind?: 'call' | 'correction';
|
|
225
238
|
}
|
|
226
239
|
/**
|
|
227
240
|
* Tool invocation record.
|
|
@@ -299,6 +312,7 @@ export interface TimelineEvent {
|
|
|
299
312
|
toolName?: string;
|
|
300
313
|
toolUseId?: string;
|
|
301
314
|
isError?: boolean;
|
|
315
|
+
/** Tokens billed for this response, cache included (see `summarizeTokens`). */
|
|
302
316
|
tokenCount?: number;
|
|
303
317
|
fullText?: string;
|
|
304
318
|
/** Context tokens before compaction */
|
|
@@ -395,6 +409,14 @@ export interface SubagentStats {
|
|
|
395
409
|
inputTokens: number;
|
|
396
410
|
/** Total output tokens generated by this subagent */
|
|
397
411
|
outputTokens: number;
|
|
412
|
+
/** Tokens read from the prompt cache by this subagent */
|
|
413
|
+
cacheReadTokens?: number;
|
|
414
|
+
/** Tokens written to the prompt cache by this subagent */
|
|
415
|
+
cacheWriteTokens?: number;
|
|
416
|
+
/** Reasoning tokens reported by this subagent (display only) */
|
|
417
|
+
reasoningTokens?: number;
|
|
418
|
+
/** Every billed token, cache included — the same vocabulary as `summarizeTokens().total` */
|
|
419
|
+
totalTokens?: number;
|
|
398
420
|
/** Timestamp when subagent started (first event) */
|
|
399
421
|
startTime?: Date;
|
|
400
422
|
/** Timestamp when subagent ended (last event) */
|