@newrelic/preflight 1.51.4 → 1.52.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/dist/dashboard/routes/api-handler.d.ts +1 -2
- package/dist/dashboard/routes/api-handler.d.ts.map +1 -1
- package/dist/dashboard/routes/api-handler.js +94 -3
- package/dist/dashboard/routes/api-handler.js.map +1 -1
- package/dist/hooks/event-processor.d.ts.map +1 -1
- package/dist/hooks/event-processor.js +1 -0
- package/dist/hooks/event-processor.js.map +1 -1
- package/dist/hooks/parent-transcript-watcher.d.ts +4 -0
- package/dist/hooks/parent-transcript-watcher.d.ts.map +1 -1
- package/dist/hooks/parent-transcript-watcher.js +61 -5
- package/dist/hooks/parent-transcript-watcher.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/subagent-transcript-parser.d.ts +11 -0
- package/dist/lib/subagent-transcript-parser.d.ts.map +1 -1
- package/dist/lib/subagent-transcript-parser.js +15 -0
- package/dist/lib/subagent-transcript-parser.js.map +1 -1
- package/dist/metrics/cost-tracker.d.ts +41 -6
- package/dist/metrics/cost-tracker.d.ts.map +1 -1
- package/dist/metrics/cost-tracker.js +41 -10
- package/dist/metrics/cost-tracker.js.map +1 -1
- package/dist/metrics/transcript-message-tracker.d.ts.map +1 -1
- package/dist/metrics/transcript-message-tracker.js +2 -7
- package/dist/metrics/transcript-message-tracker.js.map +1 -1
- package/dist/metrics/turn-cost-attributor.d.ts +9 -0
- package/dist/metrics/turn-cost-attributor.d.ts.map +1 -1
- package/dist/metrics/turn-cost-attributor.js +3 -0
- package/dist/metrics/turn-cost-attributor.js.map +1 -1
- package/dist/metrics/usage-insights.d.ts +62 -0
- package/dist/metrics/usage-insights.d.ts.map +1 -0
- package/dist/metrics/usage-insights.js +251 -0
- package/dist/metrics/usage-insights.js.map +1 -0
- package/dist/storage/session-store.d.ts +6 -9
- package/dist/storage/session-store.d.ts.map +1 -1
- package/dist/storage/session-store.js +155 -0
- package/dist/storage/session-store.js.map +1 -1
- package/dist/storage/types.d.ts +44 -0
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js +2 -1
- package/dist/storage/types.js.map +1 -1
- package/dist/tools/cost-tools.d.ts.map +1 -1
- package/dist/tools/cost-tools.js +4 -1
- package/dist/tools/cost-tools.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Constants
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
5
|
+
const LONG_SESSION_MS = 8 * 60 * 60 * 1000;
|
|
6
|
+
/** A session counts as "subagent-heavy" once subagent cost reaches this share of its total. */
|
|
7
|
+
const SUBAGENT_HEAVY_SHARE = 0.25;
|
|
8
|
+
/** Every share table (skills/subagents/plugins/loops) is capped to its top N rows by cost. */
|
|
9
|
+
const TABLE_CAP = 10;
|
|
10
|
+
/**
|
|
11
|
+
* Headline/advice copy for each insight, keyed by id so the per-session pass
|
|
12
|
+
* below only ever computes numbers — no insight-specific string-building is
|
|
13
|
+
* scattered through it. `plugins`' headline is the only one that depends on
|
|
14
|
+
* more than its own percentage (it names the contributing plugin, or falls
|
|
15
|
+
* back to the plural when more than one contributed).
|
|
16
|
+
*/
|
|
17
|
+
const INSIGHT_COPY = {
|
|
18
|
+
high_context: {
|
|
19
|
+
headline: (pct) => `${pct}% of your spend was at >150k context`,
|
|
20
|
+
advice: 'Longer sessions are more expensive even when cached. /compact mid-task, /clear when switching to new tasks.',
|
|
21
|
+
},
|
|
22
|
+
subagent_heavy: {
|
|
23
|
+
headline: (pct) => `${pct}% of your spend came from subagent-heavy sessions`,
|
|
24
|
+
advice: 'Each subagent runs its own requests. Be deliberate about spawning them, and consider a cheaper model for simpler subagents.',
|
|
25
|
+
},
|
|
26
|
+
long_sessions: {
|
|
27
|
+
headline: (pct) => `${pct}% of your spend came from sessions active for 8+ hours`,
|
|
28
|
+
advice: 'These are often background or loop sessions. Continuous usage adds up quickly, so make sure it is intentional.',
|
|
29
|
+
},
|
|
30
|
+
loops: {
|
|
31
|
+
headline: (pct) => `${pct}% of your spend came from /loop sessions`,
|
|
32
|
+
advice: 'Heavy loops can be scoped down or run with a cheaper model via skill frontmatter.',
|
|
33
|
+
},
|
|
34
|
+
plugins: {
|
|
35
|
+
headline: (pct, topPlugin) => topPlugin !== null
|
|
36
|
+
? `${pct}% of your spend came from the plugin "${topPlugin}"`
|
|
37
|
+
: `${pct}% of your spend came from plugins`,
|
|
38
|
+
advice: 'Review what this plugin contributes; its agents, skills, and MCP tools all count toward your spend.',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Helpers
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
function sharePct(costUsd, totalCostUsd) {
|
|
45
|
+
return totalCostUsd > 0 ? Math.round((costUsd / totalCostUsd) * 100) : 0;
|
|
46
|
+
}
|
|
47
|
+
function sessionTotalTokens(s) {
|
|
48
|
+
return s.tokensInput + s.tokensOutput + s.tokensCacheRead + s.tokensCacheCreation;
|
|
49
|
+
}
|
|
50
|
+
/** Prefix before the first `:` in a plugin-namespaced skill/agent-type key (`pstack:unslop` -> `pstack`); null when the key carries no plugin prefix. */
|
|
51
|
+
function pluginPrefixOf(key) {
|
|
52
|
+
const idx = key.indexOf(':');
|
|
53
|
+
return idx > 0 ? key.slice(0, idx) : null;
|
|
54
|
+
}
|
|
55
|
+
function addToAccum(map, key, bucket) {
|
|
56
|
+
const existing = map.get(key);
|
|
57
|
+
if (existing) {
|
|
58
|
+
existing.costUsd += bucket.costUsd;
|
|
59
|
+
existing.tokens += bucket.tokens;
|
|
60
|
+
existing.count += bucket.count;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
map.set(key, { costUsd: bucket.costUsd, tokens: bucket.tokens, count: bucket.count });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Builds one share table (skills/subagents/plugins) from an accumulated per-key map: sorted by cost descending, capped to {@link TABLE_CAP}. */
|
|
67
|
+
function shareTable(rows, totalCostUsd) {
|
|
68
|
+
return [...rows.entries()]
|
|
69
|
+
.map(([key, acc]) => ({
|
|
70
|
+
key,
|
|
71
|
+
costUsd: acc.costUsd,
|
|
72
|
+
tokens: acc.tokens,
|
|
73
|
+
count: acc.count,
|
|
74
|
+
sharePct: sharePct(acc.costUsd, totalCostUsd),
|
|
75
|
+
}))
|
|
76
|
+
.sort((a, b) => b.costUsd - a.costUsd)
|
|
77
|
+
.slice(0, TABLE_CAP);
|
|
78
|
+
}
|
|
79
|
+
/** Latest timeline timestamp among `ScheduleWakeup` entries, or null when the session has no timeline or no such entry. */
|
|
80
|
+
function lastScheduleWakeupMs(s) {
|
|
81
|
+
let max = null;
|
|
82
|
+
for (const entry of s.timeline ?? []) {
|
|
83
|
+
if (entry.toolName === 'ScheduleWakeup' && (max === null || entry.timestamp > max)) {
|
|
84
|
+
max = entry.timestamp;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return max;
|
|
88
|
+
}
|
|
89
|
+
function isLoopSession(s, scheduleWakeupCount) {
|
|
90
|
+
return scheduleWakeupCount > 0 || 'loop' in (s.attribution?.buckets.skill ?? {});
|
|
91
|
+
}
|
|
92
|
+
function makeInsight(id, costUsd, tokens, sessionCount, totalCostUsd, topPlugin) {
|
|
93
|
+
const pct = sharePct(costUsd, totalCostUsd);
|
|
94
|
+
if (pct === 0)
|
|
95
|
+
return null;
|
|
96
|
+
const copy = INSIGHT_COPY[id];
|
|
97
|
+
return {
|
|
98
|
+
id,
|
|
99
|
+
key: id,
|
|
100
|
+
costUsd,
|
|
101
|
+
tokens,
|
|
102
|
+
count: sessionCount,
|
|
103
|
+
sharePct: pct,
|
|
104
|
+
sessionCount,
|
|
105
|
+
headline: copy.headline(pct, topPlugin),
|
|
106
|
+
advice: copy.advice,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// computeUsageInsights
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
export function computeUsageInsights(sessions, opts) {
|
|
113
|
+
const cutoffMs = opts.nowMs - opts.windowDays * DAY_MS;
|
|
114
|
+
const windowSessions = sessions.filter((s) => s.startTime >= cutoffMs);
|
|
115
|
+
let totalCostUsd = 0;
|
|
116
|
+
let totalTokens = 0;
|
|
117
|
+
const skillsAccum = new Map();
|
|
118
|
+
const subagentsAccum = new Map();
|
|
119
|
+
const pluginsAccum = new Map();
|
|
120
|
+
const pluginSessionIds = new Set();
|
|
121
|
+
const loopRows = [];
|
|
122
|
+
let highContextCostUsd = 0;
|
|
123
|
+
let highContextTokens = 0;
|
|
124
|
+
let highContextSessions = 0;
|
|
125
|
+
let subagentHeavyCostUsd = 0;
|
|
126
|
+
let subagentHeavyTokens = 0;
|
|
127
|
+
let subagentHeavySessions = 0;
|
|
128
|
+
let longSessionsCostUsd = 0;
|
|
129
|
+
let longSessionsTokens = 0;
|
|
130
|
+
let longSessionsSessions = 0;
|
|
131
|
+
let loopsCostUsd = 0;
|
|
132
|
+
let loopsTokens = 0;
|
|
133
|
+
let loopsSessions = 0;
|
|
134
|
+
let attributedCostUsd = 0;
|
|
135
|
+
let anyAttribution = false;
|
|
136
|
+
// One per-session pass: every table and insight below is built from the
|
|
137
|
+
// accumulators this loop fills, rather than re-scanning `windowSessions`
|
|
138
|
+
// once per characteristic.
|
|
139
|
+
for (const s of windowSessions) {
|
|
140
|
+
const cost = s.estimatedCostUsd ?? 0;
|
|
141
|
+
totalCostUsd += cost;
|
|
142
|
+
const tokens = sessionTotalTokens(s);
|
|
143
|
+
totalTokens += tokens;
|
|
144
|
+
const buckets = s.attribution?.buckets;
|
|
145
|
+
if (s.attribution) {
|
|
146
|
+
anyAttribution = true;
|
|
147
|
+
let toolCostSum = 0;
|
|
148
|
+
for (const bucket of Object.values(buckets?.tool ?? {}))
|
|
149
|
+
toolCostSum += bucket.costUsd;
|
|
150
|
+
attributedCostUsd += toolCostSum;
|
|
151
|
+
}
|
|
152
|
+
for (const [key, bucket] of Object.entries(buckets?.skill ?? {})) {
|
|
153
|
+
addToAccum(skillsAccum, key, bucket);
|
|
154
|
+
const prefix = pluginPrefixOf(key);
|
|
155
|
+
if (prefix) {
|
|
156
|
+
addToAccum(pluginsAccum, prefix, bucket);
|
|
157
|
+
pluginSessionIds.add(s.sessionId);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
for (const [key, bucket] of Object.entries(buckets?.subagent ?? {})) {
|
|
161
|
+
addToAccum(subagentsAccum, key, bucket);
|
|
162
|
+
const prefix = pluginPrefixOf(key);
|
|
163
|
+
if (prefix) {
|
|
164
|
+
addToAccum(pluginsAccum, prefix, bucket);
|
|
165
|
+
pluginSessionIds.add(s.sessionId);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const highContext = s.attribution?.highContextCostUsd ?? 0;
|
|
169
|
+
if (highContext > 0) {
|
|
170
|
+
highContextCostUsd += highContext;
|
|
171
|
+
highContextTokens += tokens;
|
|
172
|
+
highContextSessions += 1;
|
|
173
|
+
}
|
|
174
|
+
if (cost > 0 && s.subagentCostUsd >= SUBAGENT_HEAVY_SHARE * cost) {
|
|
175
|
+
subagentHeavyCostUsd += cost;
|
|
176
|
+
subagentHeavyTokens += tokens;
|
|
177
|
+
subagentHeavySessions += 1;
|
|
178
|
+
}
|
|
179
|
+
if (s.durationMs >= LONG_SESSION_MS) {
|
|
180
|
+
longSessionsCostUsd += cost;
|
|
181
|
+
longSessionsTokens += tokens;
|
|
182
|
+
longSessionsSessions += 1;
|
|
183
|
+
}
|
|
184
|
+
const scheduleWakeupCount = s.toolBreakdown.ScheduleWakeup ?? 0;
|
|
185
|
+
if (isLoopSession(s, scheduleWakeupCount)) {
|
|
186
|
+
loopsCostUsd += cost;
|
|
187
|
+
loopsTokens += tokens;
|
|
188
|
+
loopsSessions += 1;
|
|
189
|
+
const runs = scheduleWakeupCount + 1;
|
|
190
|
+
loopRows.push({
|
|
191
|
+
sessionId: s.sessionId,
|
|
192
|
+
sessionName: s.sessionName,
|
|
193
|
+
runs,
|
|
194
|
+
tokens,
|
|
195
|
+
tokensPerRun: Math.round(tokens / runs),
|
|
196
|
+
costUsd: cost,
|
|
197
|
+
lastRunMs: lastScheduleWakeupMs(s) ?? s.endTime ?? s.startTime,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const pluginsCostUsd = [...pluginsAccum.values()].reduce((sum, a) => sum + a.costUsd, 0);
|
|
202
|
+
const pluginsTokens = [...pluginsAccum.values()].reduce((sum, a) => sum + a.tokens, 0);
|
|
203
|
+
const topPlugin = pluginsAccum.size === 1 ? [...pluginsAccum.keys()][0] : null;
|
|
204
|
+
const insights = [
|
|
205
|
+
{
|
|
206
|
+
id: 'high_context',
|
|
207
|
+
costUsd: highContextCostUsd,
|
|
208
|
+
tokens: highContextTokens,
|
|
209
|
+
sessionCount: highContextSessions,
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
id: 'subagent_heavy',
|
|
213
|
+
costUsd: subagentHeavyCostUsd,
|
|
214
|
+
tokens: subagentHeavyTokens,
|
|
215
|
+
sessionCount: subagentHeavySessions,
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: 'long_sessions',
|
|
219
|
+
costUsd: longSessionsCostUsd,
|
|
220
|
+
tokens: longSessionsTokens,
|
|
221
|
+
sessionCount: longSessionsSessions,
|
|
222
|
+
},
|
|
223
|
+
{ id: 'loops', costUsd: loopsCostUsd, tokens: loopsTokens, sessionCount: loopsSessions },
|
|
224
|
+
{
|
|
225
|
+
id: 'plugins',
|
|
226
|
+
costUsd: pluginsCostUsd,
|
|
227
|
+
tokens: pluginsTokens,
|
|
228
|
+
sessionCount: pluginSessionIds.size,
|
|
229
|
+
},
|
|
230
|
+
]
|
|
231
|
+
.map((input) => makeInsight(input.id, input.costUsd, input.tokens, input.sessionCount, totalCostUsd, topPlugin))
|
|
232
|
+
.filter((insight) => insight !== null)
|
|
233
|
+
.sort((a, b) => b.sharePct - a.sharePct);
|
|
234
|
+
return {
|
|
235
|
+
windowDays: opts.windowDays,
|
|
236
|
+
sessionCount: windowSessions.length,
|
|
237
|
+
totalCostUsd,
|
|
238
|
+
totalTokens,
|
|
239
|
+
insights,
|
|
240
|
+
skills: shareTable(skillsAccum, totalCostUsd),
|
|
241
|
+
skillsTotalCount: skillsAccum.size,
|
|
242
|
+
subagents: shareTable(subagentsAccum, totalCostUsd),
|
|
243
|
+
subagentsTotalCount: subagentsAccum.size,
|
|
244
|
+
plugins: shareTable(pluginsAccum, totalCostUsd),
|
|
245
|
+
pluginsTotalCount: pluginsAccum.size,
|
|
246
|
+
loops: loopRows.sort((a, b) => b.costUsd - a.costUsd).slice(0, TABLE_CAP),
|
|
247
|
+
loopsTotalCount: loopRows.length,
|
|
248
|
+
attributionRatePct: anyAttribution ? sharePct(attributedCostUsd, totalCostUsd) : null,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=usage-insights.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-insights.js","sourceRoot":"","sources":["../../src/metrics/usage-insights.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
* task-level data into a single FullSessionSummary.
|
|
13
13
|
*/
|
|
14
14
|
import { type SessionNameSource } from '../hooks/session-resolver.js';
|
|
15
|
-
import type { SessionSummary, ReplayTimelineEntry } from './types.js';
|
|
15
|
+
import type { SessionSummary, ReplayTimelineEntry, SessionAttribution } from './types.js';
|
|
16
16
|
import type { SessionTracker } from '../metrics/session-tracker.js';
|
|
17
17
|
import type { CostTracker } from '../metrics/cost-tracker.js';
|
|
18
|
+
import type { TurnCostAttributor } from '../metrics/turn-cost-attributor.js';
|
|
18
19
|
import type { TaskDetector } from '../metrics/task-detector.js';
|
|
19
20
|
import type { AntiPatternDetector } from '../metrics/anti-patterns.js';
|
|
20
21
|
import type { EfficiencyScorer, EfficiencyScoreComponents } from '../metrics/efficiency-score.js';
|
|
@@ -25,7 +26,6 @@ import { ToolSelectionScorer, type ToolSelectionSummary } from '../metrics/tool-
|
|
|
25
26
|
import type { ModelUsageTracker, ModelBreakdownEntry } from '../metrics/model-usage-tracker.js';
|
|
26
27
|
import type { QualityProxyTracker } from '../metrics/quality-proxy-tracker.js';
|
|
27
28
|
import { type QualityProxyRawCounts } from '../metrics/quality-proxy-tracker.js';
|
|
28
|
-
import type { TurnCostAttributor } from '../metrics/turn-cost-attributor.js';
|
|
29
29
|
/**
|
|
30
30
|
* One detected anti-pattern occurrence, persisted per incident rather than
|
|
31
31
|
* grouped by type — the target (`file`/`command`) and the detector's own
|
|
@@ -156,6 +156,8 @@ export interface FullSessionSummary extends SessionSummary {
|
|
|
156
156
|
* and for sessions with no token events.
|
|
157
157
|
*/
|
|
158
158
|
readonly modelBreakdown: Readonly<Record<string, ModelBreakdownEntry>>;
|
|
159
|
+
/** Spend sliced by tool, skill, and subagent type, plus high-context spend and API wait time. Absent on legacy files. */
|
|
160
|
+
readonly attribution?: SessionAttribution;
|
|
159
161
|
/**
|
|
160
162
|
* Per-workflow-run cost, split by local-day, keyed by workflow_run_id then
|
|
161
163
|
* day key — the exact shape `CostMetrics.costByWorkflowRunId` already
|
|
@@ -219,12 +221,6 @@ export interface ListSessionsOptions {
|
|
|
219
221
|
since?: Date;
|
|
220
222
|
developer?: string;
|
|
221
223
|
}
|
|
222
|
-
/**
|
|
223
|
-
* Combine two views of the same session so neither writer can lose the other's
|
|
224
|
-
* data. Counters take the max rather than the sum: the two processes observe
|
|
225
|
-
* overlapping (not disjoint) slices of one session, so summing would inflate
|
|
226
|
-
* and taking the incoming value alone would regress.
|
|
227
|
-
*/
|
|
228
224
|
export declare function mergeSummaries(existing: FullSessionSummary, incoming: FullSessionSummary): FullSessionSummary;
|
|
229
225
|
export declare class SessionStore {
|
|
230
226
|
private readonly sessionsDir;
|
|
@@ -254,6 +250,7 @@ export declare class SessionStore {
|
|
|
254
250
|
export interface BuildSessionSummarySources {
|
|
255
251
|
sessionTracker: SessionTracker;
|
|
256
252
|
costTracker?: CostTracker;
|
|
253
|
+
turnCostAttributor?: TurnCostAttributor;
|
|
257
254
|
taskDetector?: TaskDetector;
|
|
258
255
|
antiPatternDetector?: AntiPatternDetector;
|
|
259
256
|
efficiencyScorer?: EfficiencyScorer;
|
|
@@ -261,7 +258,6 @@ export interface BuildSessionSummarySources {
|
|
|
261
258
|
toolSelectionScorer?: ToolSelectionScorer;
|
|
262
259
|
modelUsageTracker?: ModelUsageTracker;
|
|
263
260
|
qualityProxyTracker?: QualityProxyTracker;
|
|
264
|
-
turnCostAttributor?: TurnCostAttributor;
|
|
265
261
|
developer: string;
|
|
266
262
|
repoName?: string | null;
|
|
267
263
|
/**
|
|
@@ -346,6 +342,7 @@ interface SerializedFullSessionSummary {
|
|
|
346
342
|
readonly modelBreakdown?: Record<string, unknown>;
|
|
347
343
|
readonly costByWorkflowRunId?: Record<string, unknown>;
|
|
348
344
|
readonly qualityProxy?: Record<string, unknown>;
|
|
345
|
+
readonly attribution?: Record<string, unknown>;
|
|
349
346
|
}
|
|
350
347
|
/**
|
|
351
348
|
* Explicitly extract known fields from a raw session object (already parsed
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../src/storage/session-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH,OAAO,EAAwB,KAAK,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAE5F,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../src/storage/session-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -69,6 +69,50 @@ export function hasAttributableActivity(s) {
|
|
|
69
69
|
* overlapping (not disjoint) slices of one session, so summing would inflate
|
|
70
70
|
* and taking the incoming value alone would regress.
|
|
71
71
|
*/
|
|
72
|
+
/**
|
|
73
|
+
* Merge two `SessionAttribution` snapshots facet-by-facet, key-by-key,
|
|
74
|
+
* taking a field-wise max on each bucket's four numbers — the same
|
|
75
|
+
* no-single-writer-loses-data rule as `modelBreakdown` above. `undefined`
|
|
76
|
+
* on either side is treated as an empty attribution rather than short-
|
|
77
|
+
* circuiting, so a session that only recently gained a `turnCostAttributor`
|
|
78
|
+
* still merges cleanly against an earlier write that had none.
|
|
79
|
+
*/
|
|
80
|
+
function mergeAttribution(existing, incoming) {
|
|
81
|
+
if (!existing && !incoming)
|
|
82
|
+
return undefined;
|
|
83
|
+
const maxNum = (a, b) => Math.max(a ?? 0, b ?? 0);
|
|
84
|
+
const maxNullable = (a, b) => (a == null && b == null ? null : Math.max(a ?? 0, b ?? 0));
|
|
85
|
+
const a = existing ?? { buckets: {}, highContextCostUsd: 0, apiDurationMs: null };
|
|
86
|
+
const b = incoming ?? { buckets: {}, highContextCostUsd: 0, apiDurationMs: null };
|
|
87
|
+
const facets = new Set([
|
|
88
|
+
...Object.keys(a.buckets),
|
|
89
|
+
...Object.keys(b.buckets),
|
|
90
|
+
]);
|
|
91
|
+
const buckets = {};
|
|
92
|
+
for (const facet of facets) {
|
|
93
|
+
const facetA = a.buckets[facet] ?? {};
|
|
94
|
+
const facetB = b.buckets[facet] ?? {};
|
|
95
|
+
const keys = new Set([...Object.keys(facetA), ...Object.keys(facetB)]);
|
|
96
|
+
const merged = {};
|
|
97
|
+
for (const key of keys) {
|
|
98
|
+
const ba = facetA[key];
|
|
99
|
+
const bb = facetB[key];
|
|
100
|
+
merged[key] = {
|
|
101
|
+
costUsd: maxNum(ba?.costUsd, bb?.costUsd),
|
|
102
|
+
tokens: maxNum(ba?.tokens, bb?.tokens),
|
|
103
|
+
count: maxNum(ba?.count, bb?.count),
|
|
104
|
+
durationMs: maxNum(ba?.durationMs, bb?.durationMs),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (Object.keys(merged).length > 0)
|
|
108
|
+
buckets[facet] = merged;
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
buckets,
|
|
112
|
+
highContextCostUsd: maxNum(a.highContextCostUsd, b.highContextCostUsd),
|
|
113
|
+
apiDurationMs: maxNullable(a.apiDurationMs, b.apiDurationMs),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
72
116
|
export function mergeSummaries(existing, incoming) {
|
|
73
117
|
const maxNum = (a, b) => Math.max(typeof a === 'number' ? a : 0, typeof b === 'number' ? b : 0);
|
|
74
118
|
const maxNullable = (a, b) => a === null && b === null ? null : Math.max(a ?? 0, b ?? 0);
|
|
@@ -227,6 +271,7 @@ export function mergeSummaries(existing, incoming) {
|
|
|
227
271
|
toolSelectionMetrics,
|
|
228
272
|
modelBreakdown,
|
|
229
273
|
qualityProxy,
|
|
274
|
+
attribution: mergeAttribution(existing.attribution, incoming.attribution),
|
|
230
275
|
};
|
|
231
276
|
}
|
|
232
277
|
export class SessionStore {
|
|
@@ -462,10 +507,55 @@ function skillInvocationCounts(metrics) {
|
|
|
462
507
|
}
|
|
463
508
|
return result;
|
|
464
509
|
}
|
|
510
|
+
/**
|
|
511
|
+
* Assemble `SessionAttribution` from the turn attributor's tool/skill
|
|
512
|
+
* buckets and the cost tracker's subagent/high-context/API-duration fields.
|
|
513
|
+
* Facets with no entries are omitted entirely rather than persisted as `{}`.
|
|
514
|
+
*/
|
|
515
|
+
function buildAttribution(turnMetrics, costMetrics) {
|
|
516
|
+
const buckets = {};
|
|
517
|
+
if (turnMetrics) {
|
|
518
|
+
const toolBuckets = {};
|
|
519
|
+
for (const [tool, entry] of Object.entries(turnMetrics.costByToolType)) {
|
|
520
|
+
toolBuckets[tool] = {
|
|
521
|
+
costUsd: entry.totalCost,
|
|
522
|
+
tokens: 0,
|
|
523
|
+
count: entry.callCount,
|
|
524
|
+
durationMs: 0,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
if (Object.keys(toolBuckets).length > 0)
|
|
528
|
+
buckets.tool = toolBuckets;
|
|
529
|
+
const skillBuckets = {};
|
|
530
|
+
for (const [skill, entry] of Object.entries(turnMetrics.costBySkill)) {
|
|
531
|
+
skillBuckets[skill] = {
|
|
532
|
+
costUsd: entry.totalCost,
|
|
533
|
+
tokens: entry.inputTokens + entry.outputTokens + entry.cacheReadTokens,
|
|
534
|
+
count: entry.callCount,
|
|
535
|
+
durationMs: entry.totalDurationMs,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
if (Object.keys(skillBuckets).length > 0)
|
|
539
|
+
buckets.skill = skillBuckets;
|
|
540
|
+
}
|
|
541
|
+
const subagentBuckets = costMetrics?.subagentByAgentType;
|
|
542
|
+
if (subagentBuckets && Object.keys(subagentBuckets).length > 0) {
|
|
543
|
+
buckets.subagent = { ...subagentBuckets };
|
|
544
|
+
}
|
|
545
|
+
return {
|
|
546
|
+
buckets,
|
|
547
|
+
highContextCostUsd: costMetrics?.highContextCostUsd ?? 0,
|
|
548
|
+
apiDurationMs: costMetrics?.apiDurationMs ?? null,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
465
551
|
export function buildSessionSummary(sources) {
|
|
466
552
|
const { sessionTracker, costTracker, taskDetector, antiPatternDetector, efficiencyScorer, developer, } = sources;
|
|
467
553
|
const sessionMetrics = sessionTracker.getMetrics();
|
|
468
554
|
const costMetrics = costTracker?.getMetrics() ?? null;
|
|
555
|
+
const turnAttributionMetrics = sources.turnCostAttributor?.getMetrics() ?? null;
|
|
556
|
+
const attribution = sources.costTracker || sources.turnCostAttributor
|
|
557
|
+
? buildAttribution(turnAttributionMetrics, costMetrics)
|
|
558
|
+
: undefined;
|
|
469
559
|
const taskMetrics = taskDetector?.getMetrics() ?? null;
|
|
470
560
|
const transcriptMessageMetrics = sources.transcriptMessageTracker?.getMetrics();
|
|
471
561
|
// Aggregate task-level data
|
|
@@ -546,6 +636,8 @@ export function buildSessionSummary(sources) {
|
|
|
546
636
|
isBuildCommand: tc.isBuildCommand || undefined,
|
|
547
637
|
isLintCommand: tc.isLintCommand || undefined,
|
|
548
638
|
errorType: tc.errorType || undefined,
|
|
639
|
+
skillName: tc.toolName === 'Skill' && tc.skillName ? tc.skillName : undefined,
|
|
640
|
+
agentType: tc.toolName === 'Agent' && tc.agentType ? tc.agentType : undefined,
|
|
549
641
|
}));
|
|
550
642
|
const now = Date.now();
|
|
551
643
|
return {
|
|
@@ -609,6 +701,7 @@ export function buildSessionSummary(sources) {
|
|
|
609
701
|
modelBreakdown: sources.modelUsageTracker?.getRawBreakdown() ?? {},
|
|
610
702
|
costByWorkflowRunId: costMetrics?.costByWorkflowRunId ?? {},
|
|
611
703
|
qualityProxy: sources.qualityProxyTracker?.getRawCounts() ?? ZERO_QUALITY_PROXY_COUNTS,
|
|
704
|
+
attribution,
|
|
612
705
|
};
|
|
613
706
|
}
|
|
614
707
|
/**
|
|
@@ -640,6 +733,65 @@ export function sessionSummaryToDriftRecord(summary) {
|
|
|
640
733
|
function formatDate(date) {
|
|
641
734
|
return date.toISOString().slice(0, 10);
|
|
642
735
|
}
|
|
736
|
+
/**
|
|
737
|
+
* Accept a bucket only when all four fields are finite numbers; any other
|
|
738
|
+
* shape is dropped rather than partially hydrated. Used to parse each key of
|
|
739
|
+
* every facet in an on-disk `SessionAttribution.buckets`.
|
|
740
|
+
*/
|
|
741
|
+
function parseAttributionBucket(raw) {
|
|
742
|
+
if (typeof raw !== 'object' || raw === null)
|
|
743
|
+
return undefined;
|
|
744
|
+
const r = raw;
|
|
745
|
+
const { costUsd, tokens, count, durationMs } = r;
|
|
746
|
+
if (typeof costUsd === 'number' &&
|
|
747
|
+
Number.isFinite(costUsd) &&
|
|
748
|
+
typeof tokens === 'number' &&
|
|
749
|
+
Number.isFinite(tokens) &&
|
|
750
|
+
typeof count === 'number' &&
|
|
751
|
+
Number.isFinite(count) &&
|
|
752
|
+
typeof durationMs === 'number' &&
|
|
753
|
+
Number.isFinite(durationMs)) {
|
|
754
|
+
return { costUsd, tokens, count, durationMs };
|
|
755
|
+
}
|
|
756
|
+
return undefined;
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Parse an on-disk `attribution` field. Returns undefined when the field is
|
|
760
|
+
* absent or not an object (a legacy session summary written before this
|
|
761
|
+
* field existed), so it loads unchanged. Invalid facet keys/buckets are
|
|
762
|
+
* dropped individually rather than invalidating the whole field.
|
|
763
|
+
*/
|
|
764
|
+
function parseAttribution(raw) {
|
|
765
|
+
if (typeof raw !== 'object' || raw === null)
|
|
766
|
+
return undefined;
|
|
767
|
+
const r = raw;
|
|
768
|
+
const rawBuckets = r.buckets;
|
|
769
|
+
const buckets = {};
|
|
770
|
+
if (typeof rawBuckets === 'object' && rawBuckets !== null) {
|
|
771
|
+
for (const facet of ['tool', 'skill', 'subagent']) {
|
|
772
|
+
const facetRaw = rawBuckets[facet];
|
|
773
|
+
if (typeof facetRaw !== 'object' || facetRaw === null)
|
|
774
|
+
continue;
|
|
775
|
+
const facetBuckets = {};
|
|
776
|
+
for (const [key, bucketRaw] of Object.entries(facetRaw)) {
|
|
777
|
+
const bucket = parseAttributionBucket(bucketRaw);
|
|
778
|
+
if (bucket)
|
|
779
|
+
facetBuckets[key] = bucket;
|
|
780
|
+
}
|
|
781
|
+
if (Object.keys(facetBuckets).length > 0)
|
|
782
|
+
buckets[facet] = facetBuckets;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
return {
|
|
786
|
+
buckets,
|
|
787
|
+
highContextCostUsd: typeof r.highContextCostUsd === 'number' && Number.isFinite(r.highContextCostUsd)
|
|
788
|
+
? r.highContextCostUsd
|
|
789
|
+
: 0,
|
|
790
|
+
apiDurationMs: typeof r.apiDurationMs === 'number' && Number.isFinite(r.apiDurationMs)
|
|
791
|
+
? r.apiDurationMs
|
|
792
|
+
: null,
|
|
793
|
+
};
|
|
794
|
+
}
|
|
643
795
|
/**
|
|
644
796
|
* Parse an untrusted value into a `Record<string, number>`, dropping any
|
|
645
797
|
* non-finite or non-numeric entries. Used to hydrate the per-day cost maps
|
|
@@ -895,12 +1047,15 @@ export function deserializeFullSessionSummary(obj) {
|
|
|
895
1047
|
isBuildCommand: typeof e.isBuildCommand === 'boolean' ? e.isBuildCommand : undefined,
|
|
896
1048
|
isLintCommand: typeof e.isLintCommand === 'boolean' ? e.isLintCommand : undefined,
|
|
897
1049
|
errorType: typeof e.errorType === 'string' ? e.errorType : undefined,
|
|
1050
|
+
skillName: typeof e.skillName === 'string' ? e.skillName : undefined,
|
|
1051
|
+
agentType: typeof e.agentType === 'string' ? e.agentType : undefined,
|
|
898
1052
|
}))
|
|
899
1053
|
: undefined,
|
|
900
1054
|
toolSelectionMetrics,
|
|
901
1055
|
modelBreakdown,
|
|
902
1056
|
costByWorkflowRunId,
|
|
903
1057
|
qualityProxy,
|
|
1058
|
+
attribution: parseAttribution(obj.attribution),
|
|
904
1059
|
};
|
|
905
1060
|
}
|
|
906
1061
|
/**
|