sidekick-shared 0.28.3 → 0.29.1
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 +86 -0
- package/README.md +188 -27
- package/dist/aggregation/EventAggregator.d.ts +19 -0
- package/dist/aggregation/EventAggregator.js +174 -35
- package/dist/aggregation/snapshot.d.ts +37 -8
- package/dist/aggregation/snapshot.js +31 -9
- package/dist/aggregation/types.d.ts +13 -0
- package/dist/authStatus.d.ts +54 -0
- package/dist/authStatus.js +91 -0
- package/dist/browser.d.ts +16 -6
- package/dist/browser.js +24 -2
- package/dist/codex.d.ts +23 -0
- package/dist/codex.js +44 -0
- package/dist/codexProfiles.d.ts +1 -0
- package/dist/codexProfiles.js +8 -11
- package/dist/context/sessionContext.js +4 -0
- package/dist/esm/aggregation/EventAggregator.js +174 -35
- package/dist/esm/aggregation/snapshot.js +31 -9
- package/dist/esm/authStatus.js +87 -0
- package/dist/esm/browser.js +5 -2
- package/dist/esm/codex.js +18 -0
- package/dist/esm/codexProfiles.js +8 -11
- package/dist/esm/context/sessionContext.js +4 -0
- package/dist/esm/formatters/toolSummary.js +4 -1
- package/dist/esm/formatting.js +10 -1
- package/dist/esm/index.js +11 -6
- package/dist/esm/modelCatalog.js +7 -5
- package/dist/esm/modelContext.js +4 -3
- package/dist/esm/modelInfo.js +425 -47
- package/dist/esm/node.js +9 -3
- package/dist/esm/observedContextWindows.js +2 -1
- package/dist/esm/ownKey.js +26 -0
- package/dist/esm/parsers/codexParser.js +351 -54
- package/dist/esm/parsers/codexRolloutOrigin.js +107 -0
- package/dist/esm/parsers/openCodeParser.js +85 -22
- package/dist/esm/parsers/subagentScanner.js +33 -12
- package/dist/esm/parsers/subagentTraceParser.js +97 -37
- package/dist/esm/pricing.js +18 -0
- package/dist/esm/pricingCatalog.js +17 -9
- package/dist/esm/promptHistory.js +66 -4
- package/dist/esm/providerFailure.js +124 -3
- package/dist/esm/providers/claudeCode.js +10 -2
- package/dist/esm/providers/codex.js +84 -62
- package/dist/esm/providers/codexDatabase.js +3 -19
- package/dist/esm/providers/codexRollouts.js +24 -1
- package/dist/esm/providers/openCode.js +9 -17
- package/dist/esm/report/htmlReportGenerator.js +42 -5
- package/dist/esm/report/transcriptParser.js +13 -11
- package/dist/esm/schemas/providerFailure.js +3 -1
- package/dist/esm/schemas/sessionEvent.js +35 -14
- package/dist/esm/schemas/transcript.js +4 -0
- package/dist/esm/sessionPromptHistory.js +2 -2
- package/dist/esm/statusline/payload.js +9 -0
- package/dist/esm/subagentSpawn.js +163 -0
- package/dist/esm/testing/credentialGuard.js +87 -5
- package/dist/esm/toolCallOrigins.js +310 -0
- package/dist/esm/transcript.js +46 -3
- package/dist/esm/turns/assistantTurn.js +3 -2
- package/dist/esm/usage/claudeUsageDedupe.js +68 -0
- package/dist/esm/usage/usageEvents.js +146 -17
- package/dist/esm/usage/usageReports.js +39 -6
- package/dist/esm/watchers/eventBridge.js +15 -0
- package/dist/esm/watchers/factory.js +5 -3
- package/dist/esm/watchers/jsonlTail.js +442 -58
- package/dist/esm/watchers/jsonlWatcher.js +51 -4
- package/dist/esm/watchers/providerReaderWatcher.js +13 -0
- package/dist/esm/zaiQuota.js +2 -1
- package/dist/formatters/toolSummary.js +4 -1
- package/dist/formatting.d.ts +26 -1
- package/dist/formatting.js +11 -1
- package/dist/index.d.ts +22 -11
- package/dist/index.js +44 -12
- package/dist/modelCatalog.js +7 -5
- package/dist/modelContext.js +4 -3
- package/dist/modelInfo.d.ts +185 -16
- package/dist/modelInfo.js +434 -49
- package/dist/node.d.ts +16 -5
- package/dist/node.js +33 -2
- package/dist/observedContextWindows.js +2 -1
- package/dist/ownKey.d.ts +16 -0
- package/dist/ownKey.js +30 -0
- package/dist/parsers/codexParser.d.ts +118 -0
- package/dist/parsers/codexParser.js +352 -54
- package/dist/parsers/codexRolloutOrigin.d.ts +49 -0
- package/dist/parsers/codexRolloutOrigin.js +111 -0
- package/dist/parsers/openCodeParser.d.ts +38 -0
- package/dist/parsers/openCodeParser.js +88 -22
- package/dist/parsers/subagentScanner.d.ts +19 -6
- package/dist/parsers/subagentScanner.js +33 -12
- package/dist/parsers/subagentTraceParser.d.ts +25 -5
- package/dist/parsers/subagentTraceParser.js +97 -37
- package/dist/pricing.d.ts +22 -0
- package/dist/pricing.js +50 -0
- package/dist/pricingCatalog.d.ts +10 -0
- package/dist/pricingCatalog.js +16 -8
- package/dist/promptHistory.d.ts +65 -4
- package/dist/promptHistory.js +68 -5
- package/dist/providerFailure.d.ts +44 -0
- package/dist/providerFailure.js +126 -4
- package/dist/providers/claudeCode.js +9 -1
- package/dist/providers/codex.d.ts +3 -22
- package/dist/providers/codex.js +85 -64
- package/dist/providers/codexDatabase.d.ts +3 -2
- package/dist/providers/codexDatabase.js +4 -22
- package/dist/providers/codexRollouts.d.ts +22 -0
- package/dist/providers/codexRollouts.js +24 -0
- package/dist/providers/openCode.js +8 -16
- package/dist/providers/types.d.ts +14 -0
- package/dist/report/htmlReportGenerator.js +42 -5
- package/dist/report/transcriptParser.d.ts +10 -1
- package/dist/report/transcriptParser.js +13 -11
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/providerFailure.d.ts +39 -0
- package/dist/schemas/providerFailure.js +2 -0
- package/dist/schemas/sessionEvent.d.ts +27 -10
- package/dist/schemas/sessionEvent.js +35 -14
- package/dist/schemas/transcript.d.ts +2 -0
- package/dist/schemas/transcript.js +4 -0
- package/dist/sessionPromptHistory.d.ts +7 -1
- package/dist/sessionPromptHistory.js +1 -1
- package/dist/statusline/payload.d.ts +10 -0
- package/dist/statusline/payload.js +14 -0
- package/dist/subagentSpawn.d.ts +80 -0
- package/dist/subagentSpawn.js +174 -0
- package/dist/testing/credentialGuard.d.ts +14 -0
- package/dist/testing/credentialGuard.js +87 -5
- package/dist/toolCallOrigins.d.ts +70 -0
- package/dist/toolCallOrigins.js +346 -0
- package/dist/transcript.d.ts +12 -0
- package/dist/transcript.js +46 -3
- package/dist/turns/assistantTurn.d.ts +1 -0
- package/dist/turns/assistantTurn.js +3 -2
- package/dist/types/opencode.d.ts +2 -0
- package/dist/usage/claudeUsageDedupe.d.ts +31 -0
- package/dist/usage/claudeUsageDedupe.js +68 -0
- package/dist/usage/usageEvents.d.ts +54 -6
- package/dist/usage/usageEvents.js +146 -17
- package/dist/usage/usageReports.d.ts +35 -4
- package/dist/usage/usageReports.js +39 -6
- package/dist/watchers/eventBridge.js +15 -0
- package/dist/watchers/factory.d.ts +6 -0
- package/dist/watchers/factory.js +5 -3
- package/dist/watchers/jsonlTail.d.ts +124 -4
- package/dist/watchers/jsonlTail.js +446 -59
- package/dist/watchers/jsonlWatcher.d.ts +18 -1
- package/dist/watchers/jsonlWatcher.js +50 -3
- package/dist/watchers/providerReaderWatcher.d.ts +7 -0
- package/dist/watchers/providerReaderWatcher.js +13 -0
- package/dist/watchers/types.d.ts +12 -0
- package/dist/zaiQuota.js +2 -1
- package/package.json +41 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Where a Codex rollout came from, read from its `session_meta` record:
|
|
4
|
+
* a session a person started, a fork of another session, or a subagent.
|
|
5
|
+
*
|
|
6
|
+
* Codex writes `session_meta.source` as a plain string for sessions started
|
|
7
|
+
* from a surface (`cli`, `vscode`, `exec`, …) and as an object for subagents:
|
|
8
|
+
* `{ subagent: { thread_spawn: { parent_thread_id, … } } }` for a spawned
|
|
9
|
+
* agent, `{ subagent: { other: 'guardian' } }` for internal reviewers, or
|
|
10
|
+
* `{ subagent: 'review' }`. The same JSON appears as a string in the state
|
|
11
|
+
* database's `threads.source`. A forked rollout records `forked_from_id`, and
|
|
12
|
+
* embeds its parent's `session_meta` after its own, so only the first counts.
|
|
13
|
+
*
|
|
14
|
+
* Pure: no filesystem access. Browser-safe.
|
|
15
|
+
*
|
|
16
|
+
* @module parsers/codexRolloutOrigin
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.codexSpawnParentFromSource = codexSpawnParentFromSource;
|
|
20
|
+
exports.codexRolloutOrigin = codexRolloutOrigin;
|
|
21
|
+
function record(value) {
|
|
22
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
23
|
+
? value
|
|
24
|
+
: undefined;
|
|
25
|
+
}
|
|
26
|
+
function nonEmpty(value) {
|
|
27
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
28
|
+
}
|
|
29
|
+
/** A `source` value as an object: rollouts store it parsed, the database as JSON text. */
|
|
30
|
+
function sourceObject(source) {
|
|
31
|
+
if (typeof source !== 'string')
|
|
32
|
+
return record(source);
|
|
33
|
+
if (!source.startsWith('{'))
|
|
34
|
+
return undefined;
|
|
35
|
+
try {
|
|
36
|
+
return record(JSON.parse(source));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Parent thread id of a spawned subagent's `source` (object, or JSON text as
|
|
44
|
+
* stored in `threads.source`): `subagent.thread_spawn.parent_thread_id`.
|
|
45
|
+
*/
|
|
46
|
+
function codexSpawnParentFromSource(source) {
|
|
47
|
+
const spawn = record(record(sourceObject(source)?.subagent)?.thread_spawn);
|
|
48
|
+
const parent = spawn?.parent_thread_id;
|
|
49
|
+
return typeof parent === 'string' ? parent : undefined;
|
|
50
|
+
}
|
|
51
|
+
function subagentKindOf(subagent) {
|
|
52
|
+
if (typeof subagent === 'string')
|
|
53
|
+
return nonEmpty(subagent);
|
|
54
|
+
const variant = record(subagent);
|
|
55
|
+
if (!variant)
|
|
56
|
+
return undefined;
|
|
57
|
+
const other = nonEmpty(variant.other);
|
|
58
|
+
if (other)
|
|
59
|
+
return other;
|
|
60
|
+
return Object.keys(variant)[0];
|
|
61
|
+
}
|
|
62
|
+
/** The `session_meta` payload of a record, of a bare payload, or of the first in a list. */
|
|
63
|
+
function metaPayload(input) {
|
|
64
|
+
if (Array.isArray(input)) {
|
|
65
|
+
for (const item of input) {
|
|
66
|
+
const row = record(item);
|
|
67
|
+
if (row?.type === 'session_meta')
|
|
68
|
+
return record(row.payload);
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const row = record(input);
|
|
73
|
+
if (!row)
|
|
74
|
+
return undefined;
|
|
75
|
+
if (row.type === 'session_meta')
|
|
76
|
+
return record(row.payload);
|
|
77
|
+
// A payload on its own, as `readSessionMeta()` returns it.
|
|
78
|
+
if (row.type === undefined && row.payload === undefined)
|
|
79
|
+
return row;
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Origin of a Codex rollout from its `session_meta`. Accepts the parsed
|
|
84
|
+
* `session_meta` record, its payload, or the rollout's records in order (the
|
|
85
|
+
* first `session_meta` wins: a fork embeds its parent's after its own).
|
|
86
|
+
* Anything else yields `{ isSubagent: false }`.
|
|
87
|
+
*/
|
|
88
|
+
function codexRolloutOrigin(input) {
|
|
89
|
+
const payload = metaPayload(input);
|
|
90
|
+
if (!payload)
|
|
91
|
+
return { isSubagent: false };
|
|
92
|
+
const forkedFromId = nonEmpty(payload.forked_from_id);
|
|
93
|
+
const object = sourceObject(payload.source);
|
|
94
|
+
const isSubagent = object !== undefined && 'subagent' in object;
|
|
95
|
+
const source = typeof payload.source === 'string' && object === undefined
|
|
96
|
+
? nonEmpty(payload.source)
|
|
97
|
+
: isSubagent
|
|
98
|
+
? 'subagent'
|
|
99
|
+
: undefined;
|
|
100
|
+
const parentId = forkedFromId ??
|
|
101
|
+
nonEmpty(codexSpawnParentFromSource(object)) ??
|
|
102
|
+
(isSubagent ? nonEmpty(payload.parent_thread_id) : undefined);
|
|
103
|
+
const subagentKind = isSubagent ? subagentKindOf(object?.subagent) : undefined;
|
|
104
|
+
return {
|
|
105
|
+
...(forkedFromId ? { forkedFromId } : {}),
|
|
106
|
+
...(source ? { source } : {}),
|
|
107
|
+
isSubagent,
|
|
108
|
+
...(parentId ? { parentId } : {}),
|
|
109
|
+
...(subagentKind ? { subagentKind } : {}),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { SessionEvent } from '../types/sessionEvent';
|
|
14
14
|
import type { OpenCodeMessage, OpenCodePart, DbMessage, DbPart } from '../types/opencode';
|
|
15
|
+
import { type NormalizedUsage } from '../usageNormalization';
|
|
15
16
|
/**
|
|
16
17
|
* Normalizes an OpenCode tool name to PascalCase.
|
|
17
18
|
*
|
|
@@ -38,6 +39,43 @@ export declare function detectPlanModeFromText(text: string): 'enter' | 'exit' |
|
|
|
38
39
|
* This adds snake_case aliases without removing the originals.
|
|
39
40
|
*/
|
|
40
41
|
export declare function normalizeToolInput(input: Record<string, unknown>): Record<string, unknown>;
|
|
42
|
+
/** OpenCode token counts as stored on an assistant message or step. */
|
|
43
|
+
export interface OpenCodeTokenCounts {
|
|
44
|
+
input?: number;
|
|
45
|
+
output?: number;
|
|
46
|
+
reasoning?: number;
|
|
47
|
+
cacheRead?: number;
|
|
48
|
+
cacheWrite?: number;
|
|
49
|
+
/** Provider-reported total, stored by OpenCode since late 2025. */
|
|
50
|
+
total?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Token counts from a message in either storage shape: database rows are
|
|
54
|
+
* mapped to flat `cacheRead` / `cacheWrite`, while file-storage messages keep
|
|
55
|
+
* OpenCode's nested `cache: { read, write }`.
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
export declare function openCodeTokenCounts(tokens: unknown): OpenCodeTokenCounts;
|
|
59
|
+
/**
|
|
60
|
+
* How a stored OpenCode message splits its tokens. Current OpenCode stores
|
|
61
|
+
* every bucket disjoint for every provider: `input` excludes cache reads and
|
|
62
|
+
* writes (since 2025-11) and `output` excludes `reasoning` (since 2026-04),
|
|
63
|
+
* with `total` equal to their sum. Older messages either counted cached input
|
|
64
|
+
* in `input` (OpenAI-compatible providers) or reasoning in `output`. The
|
|
65
|
+
* stored `total` tells the layouts apart; without it the current layout is
|
|
66
|
+
* assumed.
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
export declare function openCodeTokenLayout(tokens: OpenCodeTokenCounts): {
|
|
70
|
+
inputIncludesCache: boolean;
|
|
71
|
+
reasoningInOutput: boolean;
|
|
72
|
+
};
|
|
73
|
+
/** Normalizes one OpenCode message's or step's stored tokens. @internal */
|
|
74
|
+
export declare function normalizeOpenCodeUsage(tokens: OpenCodeTokenCounts, providerID: string | undefined, extra?: {
|
|
75
|
+
source?: string;
|
|
76
|
+
model?: string;
|
|
77
|
+
reportedCostUsd?: number;
|
|
78
|
+
}): NormalizedUsage;
|
|
41
79
|
/**
|
|
42
80
|
* Converts an OpenCode message and its parts into SessionEvent array.
|
|
43
81
|
*
|
|
@@ -15,10 +15,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.normalizeToolName = normalizeToolName;
|
|
16
16
|
exports.detectPlanModeFromText = detectPlanModeFromText;
|
|
17
17
|
exports.normalizeToolInput = normalizeToolInput;
|
|
18
|
+
exports.openCodeTokenCounts = openCodeTokenCounts;
|
|
19
|
+
exports.openCodeTokenLayout = openCodeTokenLayout;
|
|
20
|
+
exports.normalizeOpenCodeUsage = normalizeOpenCodeUsage;
|
|
18
21
|
exports.convertOpenCodeMessage = convertOpenCodeMessage;
|
|
19
22
|
exports.parseDbMessageData = parseDbMessageData;
|
|
20
23
|
exports.parseDbPartData = parseDbPartData;
|
|
21
24
|
const usageNormalization_1 = require("../usageNormalization");
|
|
25
|
+
const ownKey_1 = require("../ownKey");
|
|
22
26
|
/**
|
|
23
27
|
* OpenCode → Claude Code tool name mapping.
|
|
24
28
|
* OpenCode uses lowercase names; downstream code expects PascalCase.
|
|
@@ -54,7 +58,7 @@ function normalizeToolName(name) {
|
|
|
54
58
|
if (!name)
|
|
55
59
|
return name;
|
|
56
60
|
// Check mapping (case-insensitive lookup via lowercase key)
|
|
57
|
-
const mapped = TOOL_NAME_MAP
|
|
61
|
+
const mapped = (0, ownKey_1.ownValue)(TOOL_NAME_MAP, name.toLowerCase());
|
|
58
62
|
if (mapped)
|
|
59
63
|
return mapped;
|
|
60
64
|
// Already PascalCase (starts with uppercase)? Pass through.
|
|
@@ -123,6 +127,78 @@ function toISOString(time) {
|
|
|
123
127
|
return time;
|
|
124
128
|
return new Date(time).toISOString();
|
|
125
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Token counts from a message in either storage shape: database rows are
|
|
132
|
+
* mapped to flat `cacheRead` / `cacheWrite`, while file-storage messages keep
|
|
133
|
+
* OpenCode's nested `cache: { read, write }`.
|
|
134
|
+
* @internal
|
|
135
|
+
*/
|
|
136
|
+
function openCodeTokenCounts(tokens) {
|
|
137
|
+
const raw = (tokens ?? {});
|
|
138
|
+
const cache = (raw.cache ?? {});
|
|
139
|
+
const num = (value) => (typeof value === 'number' ? value : undefined);
|
|
140
|
+
return {
|
|
141
|
+
input: num(raw.input),
|
|
142
|
+
output: num(raw.output),
|
|
143
|
+
reasoning: num(raw.reasoning),
|
|
144
|
+
cacheRead: num(raw.cacheRead) ?? num(cache.read),
|
|
145
|
+
cacheWrite: num(raw.cacheWrite) ?? num(cache.write),
|
|
146
|
+
total: num(raw.total),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* How a stored OpenCode message splits its tokens. Current OpenCode stores
|
|
151
|
+
* every bucket disjoint for every provider: `input` excludes cache reads and
|
|
152
|
+
* writes (since 2025-11) and `output` excludes `reasoning` (since 2026-04),
|
|
153
|
+
* with `total` equal to their sum. Older messages either counted cached input
|
|
154
|
+
* in `input` (OpenAI-compatible providers) or reasoning in `output`. The
|
|
155
|
+
* stored `total` tells the layouts apart; without it the current layout is
|
|
156
|
+
* assumed.
|
|
157
|
+
* @internal
|
|
158
|
+
*/
|
|
159
|
+
function openCodeTokenLayout(tokens) {
|
|
160
|
+
const n = (value) => typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : 0;
|
|
161
|
+
const input = n(tokens.input);
|
|
162
|
+
const output = n(tokens.output);
|
|
163
|
+
const reasoning = n(tokens.reasoning);
|
|
164
|
+
const cache = n(tokens.cacheRead) + n(tokens.cacheWrite);
|
|
165
|
+
const total = n(tokens.total);
|
|
166
|
+
if (total > 0 && total !== input + cache + output + reasoning) {
|
|
167
|
+
if (reasoning > 0 && total === input + cache + output) {
|
|
168
|
+
return { inputIncludesCache: false, reasoningInOutput: true };
|
|
169
|
+
}
|
|
170
|
+
if (cache > 0 && total === input + output + reasoning) {
|
|
171
|
+
return { inputIncludesCache: true, reasoningInOutput: false };
|
|
172
|
+
}
|
|
173
|
+
if (cache > 0 && reasoning > 0 && total === input + output) {
|
|
174
|
+
return { inputIncludesCache: true, reasoningInOutput: true };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return { inputIncludesCache: false, reasoningInOutput: false };
|
|
178
|
+
}
|
|
179
|
+
/** Normalizes one OpenCode message's or step's stored tokens. @internal */
|
|
180
|
+
function normalizeOpenCodeUsage(tokens, providerID, extra = {}) {
|
|
181
|
+
const layout = openCodeTokenLayout(tokens);
|
|
182
|
+
return (0, usageNormalization_1.normalizeProviderUsage)({
|
|
183
|
+
// `openai` semantics subtract cache reads from input; only an old
|
|
184
|
+
// cache-inclusive message needs that.
|
|
185
|
+
semantics: layout.inputIncludesCache
|
|
186
|
+
? 'openai'
|
|
187
|
+
: providerID === 'anthropic'
|
|
188
|
+
? 'anthropic'
|
|
189
|
+
: 'sidekick',
|
|
190
|
+
provider: providerID ?? 'opencode',
|
|
191
|
+
...(extra.source ? { source: extra.source } : {}),
|
|
192
|
+
...(extra.model ? { model: extra.model } : {}),
|
|
193
|
+
inputTokens: tokens.input,
|
|
194
|
+
outputTokens: tokens.output,
|
|
195
|
+
cacheReadTokens: tokens.cacheRead,
|
|
196
|
+
cacheWriteTokens: tokens.cacheWrite,
|
|
197
|
+
reasoningTokens: tokens.reasoning,
|
|
198
|
+
reasoningIncludedInOutput: layout.reasoningInOutput,
|
|
199
|
+
...(extra.reportedCostUsd !== undefined ? { reportedCostUsd: extra.reportedCostUsd } : {}),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
126
202
|
/**
|
|
127
203
|
* Converts an OpenCode message and its parts into SessionEvent array.
|
|
128
204
|
*
|
|
@@ -328,31 +404,20 @@ function convertAssistantMessage(message, parts) {
|
|
|
328
404
|
: stepFinishCost > 0
|
|
329
405
|
? stepFinishCost
|
|
330
406
|
: undefined;
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
output_tokens: message.tokens.output || 0,
|
|
334
|
-
cache_creation_input_tokens: message.tokens.cacheWrite || 0,
|
|
335
|
-
cache_read_input_tokens: message.tokens.cacheRead || 0,
|
|
336
|
-
reported_cost: reportedCost,
|
|
337
|
-
reasoning_tokens: message.tokens.reasoning || 0,
|
|
338
|
-
};
|
|
339
|
-
const normalizedUsage = (0, usageNormalization_1.normalizeProviderUsage)({
|
|
340
|
-
semantics: message.providerID === 'openai'
|
|
341
|
-
? 'openai'
|
|
342
|
-
: message.providerID === 'anthropic'
|
|
343
|
-
? 'anthropic'
|
|
344
|
-
: 'sidekick',
|
|
345
|
-
provider: message.providerID ?? 'opencode',
|
|
407
|
+
const counts = openCodeTokenCounts(message.tokens);
|
|
408
|
+
const normalizedUsage = normalizeOpenCodeUsage(counts, message.providerID, {
|
|
346
409
|
source: 'opencode-message',
|
|
347
410
|
model: message.modelID,
|
|
348
|
-
inputTokens: message.tokens.input,
|
|
349
|
-
outputTokens: message.tokens.output,
|
|
350
|
-
cacheReadTokens: message.tokens.cacheRead,
|
|
351
|
-
cacheWriteTokens: message.tokens.cacheWrite,
|
|
352
|
-
reasoningTokens: message.tokens.reasoning,
|
|
353
|
-
reasoningIncludedInOutput: message.providerID === 'openai',
|
|
354
411
|
reportedCostUsd: reportedCost,
|
|
355
412
|
});
|
|
413
|
+
const usage = {
|
|
414
|
+
input_tokens: normalizedUsage.uncachedInputTokens,
|
|
415
|
+
output_tokens: counts.output || 0,
|
|
416
|
+
cache_creation_input_tokens: counts.cacheWrite || 0,
|
|
417
|
+
cache_read_input_tokens: counts.cacheRead || 0,
|
|
418
|
+
reported_cost: reportedCost,
|
|
419
|
+
reasoning_tokens: counts.reasoning || 0,
|
|
420
|
+
};
|
|
356
421
|
const timestamp = toISOString(message.time.completed || message.time.created);
|
|
357
422
|
const retryAttempts = parts
|
|
358
423
|
.filter((part) => part.type === 'retry')
|
|
@@ -491,6 +556,7 @@ function parseDbMessageData(row) {
|
|
|
491
556
|
cacheRead: cache?.read || 0,
|
|
492
557
|
cacheWrite: cache?.write || 0,
|
|
493
558
|
reasoning: tokens?.reasoning || 0,
|
|
559
|
+
...(typeof tokens?.total === 'number' ? { total: tokens.total } : {}),
|
|
494
560
|
},
|
|
495
561
|
time: {
|
|
496
562
|
created: time?.created || row.time_created,
|
|
@@ -8,15 +8,28 @@
|
|
|
8
8
|
* @module parsers/subagentScanner
|
|
9
9
|
*/
|
|
10
10
|
import type { SubagentStats, TaskState } from '../types/sessionEvent';
|
|
11
|
+
/** Fields of a Claude Code `agent-<id>.meta.json`; each is present only when well-typed. */
|
|
12
|
+
export interface AgentMeta {
|
|
13
|
+
agentType?: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
/** The tool_use id of the `Agent` / `Task` call that spawned this agent. */
|
|
16
|
+
toolUseId?: string;
|
|
17
|
+
/** The agent that spawned this one, for spawns nested inside a subagent. */
|
|
18
|
+
parentAgentId?: string;
|
|
19
|
+
/** `'background'` for an agent launched in the background. */
|
|
20
|
+
requestShape?: string;
|
|
21
|
+
/** Written as `true` once a background agent is done (newer Claude Code). */
|
|
22
|
+
finished?: boolean;
|
|
23
|
+
/** The user stopped the agent. */
|
|
24
|
+
stoppedByUser?: boolean;
|
|
25
|
+
}
|
|
11
26
|
/**
|
|
12
27
|
* Reads `agent-<id>.meta.json` (written by Claude Code next to the transcript)
|
|
13
|
-
* for the spawn's agent type
|
|
14
|
-
* yield an empty object
|
|
28
|
+
* for the spawn's agent type, description, spawning tool_use id, parent agent,
|
|
29
|
+
* and completion flags. Missing or malformed files yield an empty object;
|
|
30
|
+
* wrongly typed fields are left out.
|
|
15
31
|
*/
|
|
16
|
-
export declare function readAgentMeta(agentFilePath: string):
|
|
17
|
-
agentType?: string;
|
|
18
|
-
description?: string;
|
|
19
|
-
};
|
|
32
|
+
export declare function readAgentMeta(agentFilePath: string): AgentMeta;
|
|
20
33
|
/**
|
|
21
34
|
* Scans subagent directory for agent JSONL files and extracts tool calls.
|
|
22
35
|
*
|
|
@@ -51,28 +51,49 @@ const path = __importStar(require("path"));
|
|
|
51
51
|
const claudeUsageDedupe_1 = require("../usage/claudeUsageDedupe");
|
|
52
52
|
const subagentUsage_1 = require("../usage/subagentUsage");
|
|
53
53
|
const subagentStatsCache_1 = require("./subagentStatsCache");
|
|
54
|
+
const subagentSpawn_1 = require("../subagentSpawn");
|
|
54
55
|
/**
|
|
55
56
|
* Pattern for matching subagent JSONL files.
|
|
56
57
|
* Files are named like: agent-<hash>.jsonl
|
|
57
58
|
*/
|
|
58
59
|
const AGENT_FILE_PATTERN = /^agent-(.+)\.jsonl$/;
|
|
60
|
+
function metaString(value) {
|
|
61
|
+
return typeof value === 'string' && value ? value : undefined;
|
|
62
|
+
}
|
|
59
63
|
/**
|
|
60
64
|
* Reads `agent-<id>.meta.json` (written by Claude Code next to the transcript)
|
|
61
|
-
* for the spawn's agent type
|
|
62
|
-
* yield an empty object
|
|
65
|
+
* for the spawn's agent type, description, spawning tool_use id, parent agent,
|
|
66
|
+
* and completion flags. Missing or malformed files yield an empty object;
|
|
67
|
+
* wrongly typed fields are left out.
|
|
63
68
|
*/
|
|
64
69
|
function readAgentMeta(agentFilePath) {
|
|
65
70
|
try {
|
|
66
71
|
const metaPath = agentFilePath.replace(/\.jsonl$/, '.meta.json');
|
|
67
72
|
const parsed = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
74
|
+
return {};
|
|
75
|
+
const record = parsed;
|
|
76
|
+
const meta = {};
|
|
77
|
+
const agentType = metaString(record.agentType);
|
|
78
|
+
if (agentType)
|
|
79
|
+
meta.agentType = agentType;
|
|
80
|
+
const description = metaString(record.description);
|
|
81
|
+
if (description)
|
|
82
|
+
meta.description = description;
|
|
83
|
+
const toolUseId = metaString(record.toolUseId);
|
|
84
|
+
if (toolUseId)
|
|
85
|
+
meta.toolUseId = toolUseId;
|
|
86
|
+
const parentAgentId = metaString(record.parentAgentId);
|
|
87
|
+
if (parentAgentId)
|
|
88
|
+
meta.parentAgentId = parentAgentId;
|
|
89
|
+
const requestShape = metaString(record.requestShape);
|
|
90
|
+
if (requestShape)
|
|
91
|
+
meta.requestShape = requestShape;
|
|
92
|
+
if (typeof record.finished === 'boolean')
|
|
93
|
+
meta.finished = record.finished;
|
|
94
|
+
if (typeof record.stoppedByUser === 'boolean')
|
|
95
|
+
meta.stoppedByUser = record.stoppedByUser;
|
|
96
|
+
return meta;
|
|
76
97
|
}
|
|
77
98
|
catch {
|
|
78
99
|
return {};
|
|
@@ -247,8 +268,8 @@ function parseAgentFile(filePath, agentId, log) {
|
|
|
247
268
|
}
|
|
248
269
|
}
|
|
249
270
|
toolCalls.push(toolCall);
|
|
250
|
-
// If this is
|
|
251
|
-
if (toolUse.name
|
|
271
|
+
// If this is an Agent/Task call (spawning another subagent), extract info
|
|
272
|
+
if ((0, subagentSpawn_1.isSubagentSpawnTool)(toolUse.name) && toolUse.input) {
|
|
252
273
|
if (toolUse.input.subagent_type && !agentType) {
|
|
253
274
|
agentType = String(toolUse.input.subagent_type);
|
|
254
275
|
}
|
|
@@ -5,10 +5,13 @@
|
|
|
5
5
|
* (not just stats) for drill-down trace visualization. Applies noise classification
|
|
6
6
|
* and tool summary formatting from the formatters module.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* 1.
|
|
10
|
-
* 2.
|
|
11
|
-
*
|
|
8
|
+
* Parent-child linking, exact links first:
|
|
9
|
+
* 1. Meta `parentAgentId` (Claude Code writes it for nested spawns)
|
|
10
|
+
* 2. The child's spawning tool_use id (meta `toolUseId`) found in another
|
|
11
|
+
* trace's `Agent` / `Task` calls
|
|
12
|
+
* Only traces without that spawn metadata (older Claude Code) fall back to:
|
|
13
|
+
* 3. Team summary: parse <teammate-message> XML blocks
|
|
14
|
+
* 4. Positional fallback: timestamp ordering + proximity
|
|
12
15
|
*
|
|
13
16
|
* @module parsers/subagentTraceParser
|
|
14
17
|
*/
|
|
@@ -41,8 +44,25 @@ export interface SubagentTrace {
|
|
|
41
44
|
children: SubagentTrace[];
|
|
42
45
|
/** Basic stats (tokens, duration, etc.) */
|
|
43
46
|
stats: SubagentStats;
|
|
44
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Parent tool_use_id that spawned this agent: the spawning `Agent` / `Task`
|
|
49
|
+
* call's id when Claude Code recorded it (also for top-level traces, whose
|
|
50
|
+
* spawn is in the main session). For a heuristic link without that
|
|
51
|
+
* metadata it holds the parent's agent id, as before; see `parentAgentId`.
|
|
52
|
+
*/
|
|
45
53
|
parentToolUseId?: string;
|
|
54
|
+
/** Path of this agent's `agent-<id>.jsonl` transcript. */
|
|
55
|
+
filePath?: string;
|
|
56
|
+
/** The tool_use id of the call that spawned this agent (meta `toolUseId`). */
|
|
57
|
+
spawnToolUseId?: string;
|
|
58
|
+
/** Agent id of the parent trace, for agents spawned by another subagent. */
|
|
59
|
+
parentAgentId?: string;
|
|
60
|
+
/** Launched in the background (meta `requestShape: 'background'`). */
|
|
61
|
+
background?: boolean;
|
|
62
|
+
/** Claude Code marked the background agent done (meta `finished`). */
|
|
63
|
+
finished?: boolean;
|
|
64
|
+
/** The user stopped the agent (meta `stoppedByUser`). */
|
|
65
|
+
stoppedByUser?: boolean;
|
|
46
66
|
}
|
|
47
67
|
/**
|
|
48
68
|
* Scans and parses subagent traces with full conversation events.
|