@roberttlange/agentlens 0.2.2 → 0.3.0
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/browser.js +154 -20
- package/dist/browser.js.map +1 -1
- package/dist/main.test.js +138 -1
- package/dist/main.test.js.map +1 -1
- package/node_modules/@agentlens/contracts/dist/index.d.ts +120 -0
- package/node_modules/@agentlens/core/dist/__tests__/config.test.js +67 -2
- package/node_modules/@agentlens/core/dist/__tests__/config.test.js.map +1 -1
- package/node_modules/@agentlens/core/dist/__tests__/index.test.js +590 -2
- package/node_modules/@agentlens/core/dist/__tests__/index.test.js.map +1 -1
- package/node_modules/@agentlens/core/dist/config.js +95 -5
- package/node_modules/@agentlens/core/dist/config.js.map +1 -1
- package/node_modules/@agentlens/core/dist/generatedPricing.d.ts +3 -0
- package/node_modules/@agentlens/core/dist/generatedPricing.js +131 -0
- package/node_modules/@agentlens/core/dist/generatedPricing.js.map +1 -0
- package/node_modules/@agentlens/core/dist/metrics.d.ts +13 -0
- package/node_modules/@agentlens/core/dist/metrics.js +227 -54
- package/node_modules/@agentlens/core/dist/metrics.js.map +1 -1
- package/node_modules/@agentlens/core/dist/pricing.d.ts +15 -0
- package/node_modules/@agentlens/core/dist/pricing.js +133 -0
- package/node_modules/@agentlens/core/dist/pricing.js.map +1 -0
- package/node_modules/@agentlens/core/dist/pricing.test.d.ts +1 -0
- package/node_modules/@agentlens/core/dist/pricing.test.js +109 -0
- package/node_modules/@agentlens/core/dist/pricing.test.js.map +1 -0
- package/node_modules/@agentlens/core/dist/sourceProfiles.js +7 -67
- package/node_modules/@agentlens/core/dist/sourceProfiles.js.map +1 -1
- package/node_modules/@agentlens/core/dist/traceIndex.d.ts +34 -1
- package/node_modules/@agentlens/core/dist/traceIndex.js +374 -15
- package/node_modules/@agentlens/core/dist/traceIndex.js.map +1 -1
- package/node_modules/@agentlens/server/dist/activity-cache.d.ts +32 -0
- package/node_modules/@agentlens/server/dist/activity-cache.js +63 -0
- package/node_modules/@agentlens/server/dist/activity-cache.js.map +1 -0
- package/node_modules/@agentlens/server/dist/activity-cache.test.d.ts +1 -0
- package/node_modules/@agentlens/server/dist/activity-cache.test.js +170 -0
- package/node_modules/@agentlens/server/dist/activity-cache.test.js.map +1 -0
- package/node_modules/@agentlens/server/dist/activity.d.ts +31 -1
- package/node_modules/@agentlens/server/dist/activity.js +532 -34
- package/node_modules/@agentlens/server/dist/activity.js.map +1 -1
- package/node_modules/@agentlens/server/dist/app.d.ts +4 -2
- package/node_modules/@agentlens/server/dist/app.js +248 -5
- package/node_modules/@agentlens/server/dist/app.js.map +1 -1
- package/node_modules/@agentlens/server/dist/app.test.js +670 -9
- package/node_modules/@agentlens/server/dist/app.test.js.map +1 -1
- package/node_modules/@agentlens/server/dist/web/assets/index-CTFOBaBt.css +1 -0
- package/node_modules/@agentlens/server/dist/web/assets/index-CVf00w06.js +52 -0
- package/node_modules/@agentlens/server/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/node_modules/@agentlens/server/dist/web/assets/index-Ci8okH8M.js +0 -52
- package/node_modules/@agentlens/server/dist/web/assets/index-Cj3kmsFf.css +0 -1
|
@@ -13,6 +13,8 @@ const MAX_WEEK_DAY_COUNT = 366;
|
|
|
13
13
|
const DEFAULT_WEEK_SLOT_MINUTES = 30;
|
|
14
14
|
const DEFAULT_WEEK_HOUR_START_LOCAL = 0;
|
|
15
15
|
const DEFAULT_WEEK_HOUR_END_LOCAL = 24;
|
|
16
|
+
const DEFAULT_YEAR_SLOT_MINUTES = 30;
|
|
17
|
+
const DEFAULT_YEAR_HOUR_START_LOCAL = 7;
|
|
16
18
|
const MIN_TZ_OFFSET_MINUTES = -14 * 60;
|
|
17
19
|
const MAX_TZ_OFFSET_MINUTES = 14 * 60;
|
|
18
20
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
@@ -106,6 +108,184 @@ function createEmptyEventKindCounts() {
|
|
|
106
108
|
meta: 0,
|
|
107
109
|
};
|
|
108
110
|
}
|
|
111
|
+
function createUsageSummaryRow(agent) {
|
|
112
|
+
return {
|
|
113
|
+
agent,
|
|
114
|
+
sessionHours: 0,
|
|
115
|
+
sessionSharePct: 0,
|
|
116
|
+
uniqueSessions: 0,
|
|
117
|
+
activeSlots: 0,
|
|
118
|
+
activeDays: 0,
|
|
119
|
+
peakConcurrentSessions: 0,
|
|
120
|
+
inputTokens: 0,
|
|
121
|
+
cacheTokens: 0,
|
|
122
|
+
outputTokens: 0,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function createUsageAccumulator() {
|
|
126
|
+
const usageByAgent = new Map();
|
|
127
|
+
const uniqueSessionIdsByAgent = new Map();
|
|
128
|
+
for (const agent of AGENT_KIND_KEYS) {
|
|
129
|
+
usageByAgent.set(agent, createUsageSummaryRow(agent));
|
|
130
|
+
uniqueSessionIdsByAgent.set(agent, new Set());
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
usageByAgent,
|
|
134
|
+
uniqueSessionIdsByAgent,
|
|
135
|
+
totalUniqueSessionIds: new Set(),
|
|
136
|
+
peakAllAgentConcurrency: 0,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function createEmptyHeatmapMetricValues() {
|
|
140
|
+
return {
|
|
141
|
+
sessions: 0,
|
|
142
|
+
output_tokens: 0,
|
|
143
|
+
total_cost_usd: 0,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function sanitizeTokenValue(value) {
|
|
147
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
148
|
+
}
|
|
149
|
+
function sanitizeCostValue(value) {
|
|
150
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
151
|
+
}
|
|
152
|
+
function normalizeHexColor(value) {
|
|
153
|
+
const trimmed = value.trim();
|
|
154
|
+
const shortMatch = /^#([0-9a-fA-F]{3})$/.exec(trimmed);
|
|
155
|
+
if (shortMatch) {
|
|
156
|
+
const digits = shortMatch[1] ?? "";
|
|
157
|
+
return `#${digits
|
|
158
|
+
.split("")
|
|
159
|
+
.map((digit) => `${digit}${digit}`)
|
|
160
|
+
.join("")
|
|
161
|
+
.toLowerCase()}`;
|
|
162
|
+
}
|
|
163
|
+
const longMatch = /^#([0-9a-fA-F]{6})$/.exec(trimmed);
|
|
164
|
+
if (longMatch) {
|
|
165
|
+
return `#${(longMatch[1] ?? "").toLowerCase()}`;
|
|
166
|
+
}
|
|
167
|
+
return "#dc2626";
|
|
168
|
+
}
|
|
169
|
+
function hexChannel(color, start) {
|
|
170
|
+
return Number.parseInt(color.slice(start, start + 2), 16);
|
|
171
|
+
}
|
|
172
|
+
function toHexChannel(value) {
|
|
173
|
+
return Math.round(clamp(value, 0, 255))
|
|
174
|
+
.toString(16)
|
|
175
|
+
.padStart(2, "0");
|
|
176
|
+
}
|
|
177
|
+
function mixHexColor(color, colorWeight) {
|
|
178
|
+
const normalized = normalizeHexColor(color);
|
|
179
|
+
const baseWeight = clamp(colorWeight, 0, 1);
|
|
180
|
+
const whiteWeight = 1 - baseWeight;
|
|
181
|
+
const red = hexChannel(normalized, 1) * baseWeight + 255 * whiteWeight;
|
|
182
|
+
const green = hexChannel(normalized, 3) * baseWeight + 255 * whiteWeight;
|
|
183
|
+
const blue = hexChannel(normalized, 5) * baseWeight + 255 * whiteWeight;
|
|
184
|
+
return `#${toHexChannel(red)}${toHexChannel(green)}${toHexChannel(blue)}`;
|
|
185
|
+
}
|
|
186
|
+
function buildHeatmapPresentation(metric, color) {
|
|
187
|
+
const normalizedColor = normalizeHexColor(color);
|
|
188
|
+
return {
|
|
189
|
+
metric,
|
|
190
|
+
color: normalizedColor,
|
|
191
|
+
palette: [
|
|
192
|
+
"#ffffff",
|
|
193
|
+
mixHexColor(normalizedColor, 0.18),
|
|
194
|
+
mixHexColor(normalizedColor, 0.38),
|
|
195
|
+
mixHexColor(normalizedColor, 0.62),
|
|
196
|
+
mixHexColor(normalizedColor, 0.82),
|
|
197
|
+
],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function sumHeatmapMetricForBins(bins, metric) {
|
|
201
|
+
return bins.reduce((sum, bin) => {
|
|
202
|
+
if (metric === "sessions")
|
|
203
|
+
return sum + (bin.heatmapValues?.sessions ?? bin.activeSessionCount);
|
|
204
|
+
if (metric === "output_tokens")
|
|
205
|
+
return sum + (bin.heatmapValues?.output_tokens ?? 0);
|
|
206
|
+
return sum + (bin.heatmapValues?.total_cost_usd ?? 0);
|
|
207
|
+
}, 0);
|
|
208
|
+
}
|
|
209
|
+
function totalEventCountForBins(bins) {
|
|
210
|
+
return bins.reduce((sum, bin) => sum + bin.eventCount, 0);
|
|
211
|
+
}
|
|
212
|
+
function finalizeUsageSummary(accumulator) {
|
|
213
|
+
for (const agent of AGENT_KIND_KEYS) {
|
|
214
|
+
const row = accumulator.usageByAgent.get(agent);
|
|
215
|
+
const sessions = accumulator.uniqueSessionIdsByAgent.get(agent);
|
|
216
|
+
if (!row || !sessions)
|
|
217
|
+
continue;
|
|
218
|
+
row.uniqueSessions = sessions.size;
|
|
219
|
+
}
|
|
220
|
+
const totalSessionHours = AGENT_KIND_KEYS.reduce((sum, agent) => sum + (accumulator.usageByAgent.get(agent)?.sessionHours ?? 0), 0);
|
|
221
|
+
const rows = AGENT_KIND_KEYS.map((agent) => accumulator.usageByAgent.get(agent))
|
|
222
|
+
.filter((row) => row.sessionHours > 0 || row.uniqueSessions > 0 || row.activeSlots > 0)
|
|
223
|
+
.map((row) => ({
|
|
224
|
+
...row,
|
|
225
|
+
sessionSharePct: totalSessionHours > 0 ? (row.sessionHours / totalSessionHours) * 100 : 0,
|
|
226
|
+
}))
|
|
227
|
+
.sort((left, right) => right.sessionHours - left.sessionHours || right.uniqueSessions - left.uniqueSessions || left.agent.localeCompare(right.agent));
|
|
228
|
+
return {
|
|
229
|
+
rows,
|
|
230
|
+
totals: {
|
|
231
|
+
totalUniqueSessions: accumulator.totalUniqueSessionIds.size,
|
|
232
|
+
totalSessionHours,
|
|
233
|
+
peakAllAgentConcurrency: accumulator.peakAllAgentConcurrency,
|
|
234
|
+
mostUsedAgent: rows[0]?.agent ?? null,
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function applyUsagePointToRow(row, point) {
|
|
239
|
+
row.inputTokens += sanitizeTokenValue(point.inputTokens);
|
|
240
|
+
row.cacheTokens += sanitizeTokenValue(point.cachedReadTokens) + sanitizeTokenValue(point.cachedCreateTokens);
|
|
241
|
+
row.outputTokens += sanitizeTokenValue(point.outputTokens);
|
|
242
|
+
}
|
|
243
|
+
function buildWeeklyUsageSummaryFromDays(traceIndex, days) {
|
|
244
|
+
const accumulator = createUsageAccumulator();
|
|
245
|
+
const summaryById = new Map(traceIndex.getSummaries().map((summary) => [summary.id, summary]));
|
|
246
|
+
for (const day of days) {
|
|
247
|
+
const activeAgentsToday = new Set();
|
|
248
|
+
for (const bin of day.bins) {
|
|
249
|
+
accumulator.peakAllAgentConcurrency = Math.max(accumulator.peakAllAgentConcurrency, bin.activeSessionCount);
|
|
250
|
+
const binHours = Math.max(0, bin.endMs - bin.startMs) / 3_600_000;
|
|
251
|
+
for (const agent of AGENT_KIND_KEYS) {
|
|
252
|
+
const agentSessionsInBin = bin.activeByAgent[agent] ?? 0;
|
|
253
|
+
if (agentSessionsInBin <= 0)
|
|
254
|
+
continue;
|
|
255
|
+
const row = accumulator.usageByAgent.get(agent);
|
|
256
|
+
if (!row)
|
|
257
|
+
continue;
|
|
258
|
+
row.sessionHours += agentSessionsInBin * binHours;
|
|
259
|
+
row.activeSlots += 1;
|
|
260
|
+
row.peakConcurrentSessions = Math.max(row.peakConcurrentSessions, agentSessionsInBin);
|
|
261
|
+
activeAgentsToday.add(agent);
|
|
262
|
+
}
|
|
263
|
+
for (const traceId of bin.activeTraceIds) {
|
|
264
|
+
accumulator.totalUniqueSessionIds.add(traceId);
|
|
265
|
+
const normalizedAgent = summaryById.get(traceId)?.agent ?? "unknown";
|
|
266
|
+
accumulator.uniqueSessionIdsByAgent.get(normalizedAgent)?.add(traceId);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
for (const agent of activeAgentsToday) {
|
|
270
|
+
const row = accumulator.usageByAgent.get(agent);
|
|
271
|
+
if (!row)
|
|
272
|
+
continue;
|
|
273
|
+
row.activeDays += 1;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
for (const summary of summaryById.values()) {
|
|
277
|
+
const usageArtifacts = traceIndex.getSessionUsageArtifacts(summary.id);
|
|
278
|
+
const row = accumulator.usageByAgent.get(summary.agent);
|
|
279
|
+
if (!row)
|
|
280
|
+
continue;
|
|
281
|
+
for (const point of usageArtifacts.usagePoints) {
|
|
282
|
+
if (!days.some((day) => point.timestampMs >= day.windowStartMs && point.timestampMs < day.windowEndMs))
|
|
283
|
+
continue;
|
|
284
|
+
applyUsagePointToRow(row, point);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return finalizeUsageSummary(accumulator);
|
|
288
|
+
}
|
|
109
289
|
function resolveSessionSpan(summary) {
|
|
110
290
|
const baseStart = summary.firstEventTs ?? summary.lastEventTs ?? summary.mtimeMs;
|
|
111
291
|
const baseEnd = summary.lastEventTs ?? summary.mtimeMs;
|
|
@@ -113,35 +293,6 @@ function resolveSessionSpan(summary) {
|
|
|
113
293
|
const endMs = Math.max(baseStart, baseEnd);
|
|
114
294
|
return { startMs, endMs };
|
|
115
295
|
}
|
|
116
|
-
function collectTimestampMs(events) {
|
|
117
|
-
const timestamps = [];
|
|
118
|
-
for (const event of events) {
|
|
119
|
-
const timestampMs = event.timestampMs;
|
|
120
|
-
if (timestampMs === null || !Number.isFinite(timestampMs) || timestampMs <= 0)
|
|
121
|
-
continue;
|
|
122
|
-
timestamps.push(timestampMs);
|
|
123
|
-
}
|
|
124
|
-
timestamps.sort((left, right) => left - right);
|
|
125
|
-
return timestamps;
|
|
126
|
-
}
|
|
127
|
-
function buildActiveSegmentsFromEventTimestamps(eventTimestamps) {
|
|
128
|
-
if (eventTimestamps.length === 0)
|
|
129
|
-
return [];
|
|
130
|
-
const segments = [];
|
|
131
|
-
let segmentStartMs = eventTimestamps[0] ?? 0;
|
|
132
|
-
let previousTsMs = segmentStartMs;
|
|
133
|
-
for (let index = 1; index < eventTimestamps.length; index += 1) {
|
|
134
|
-
const nextTsMs = eventTimestamps[index] ?? previousTsMs;
|
|
135
|
-
const gapMs = Math.max(0, nextTsMs - previousTsMs);
|
|
136
|
-
if (gapMs > ACTIVE_IDLE_GAP_MS) {
|
|
137
|
-
segments.push({ startMs: segmentStartMs, endMs: previousTsMs });
|
|
138
|
-
segmentStartMs = nextTsMs;
|
|
139
|
-
}
|
|
140
|
-
previousTsMs = nextTsMs;
|
|
141
|
-
}
|
|
142
|
-
segments.push({ startMs: segmentStartMs, endMs: previousTsMs });
|
|
143
|
-
return segments;
|
|
144
|
-
}
|
|
145
296
|
function pickDominantAgent(counts) {
|
|
146
297
|
let dominant = "none";
|
|
147
298
|
let dominantCount = 0;
|
|
@@ -208,7 +359,7 @@ function applyBreakMarkers(bins, breakMinutes) {
|
|
|
208
359
|
}
|
|
209
360
|
markRunIfBreak(runStart, bins.length);
|
|
210
361
|
}
|
|
211
|
-
function
|
|
362
|
+
function computeWindowActivity(traceIndex, windowStartMs, windowEndMs, binMinutes, breakMinutes, heatmapMetric) {
|
|
212
363
|
const binMs = binMinutes * 60_000;
|
|
213
364
|
const totalWindowMs = Math.max(0, windowEndMs - windowStartMs);
|
|
214
365
|
const binCount = totalWindowMs <= 0 ? 0 : Math.ceil(totalWindowMs / binMs);
|
|
@@ -220,6 +371,8 @@ function buildWindowActivity(traceIndex, windowStartMs, windowEndMs, binMinutes,
|
|
|
220
371
|
startMs,
|
|
221
372
|
endMs,
|
|
222
373
|
activeSessionCount: 0,
|
|
374
|
+
heatmapValue: 0,
|
|
375
|
+
heatmapValues: createEmptyHeatmapMetricValues(),
|
|
223
376
|
activeTraceIds: [],
|
|
224
377
|
primaryTraceId: "",
|
|
225
378
|
activeByAgent: createEmptyAgentCounts(),
|
|
@@ -244,8 +397,8 @@ function buildWindowActivity(traceIndex, windowStartMs, windowEndMs, binMinutes,
|
|
|
244
397
|
if (binCount > 0) {
|
|
245
398
|
for (const summary of inWindowSummaries) {
|
|
246
399
|
const detail = traceIndex.getSessionDetail(summary.id);
|
|
247
|
-
const
|
|
248
|
-
const activeSegments =
|
|
400
|
+
const activityArtifacts = traceIndex.getSessionActivityArtifacts(summary.id);
|
|
401
|
+
const activeSegments = activityArtifacts.activeSegments;
|
|
249
402
|
for (const segment of activeSegments) {
|
|
250
403
|
if (segment.endMs < windowStartMs || segment.startMs >= windowEndMs)
|
|
251
404
|
continue;
|
|
@@ -263,6 +416,7 @@ function buildWindowActivity(traceIndex, windowStartMs, windowEndMs, binMinutes,
|
|
|
263
416
|
bin.activeTraceIds.push(summary.id);
|
|
264
417
|
bin.activeSessionCount += 1;
|
|
265
418
|
bin.activeByAgent[summary.agent] += 1;
|
|
419
|
+
bin.heatmapValues ??= createEmptyHeatmapMetricValues();
|
|
266
420
|
contributingSessionIds.add(summary.id);
|
|
267
421
|
}
|
|
268
422
|
}
|
|
@@ -278,6 +432,18 @@ function buildWindowActivity(traceIndex, windowStartMs, windowEndMs, binMinutes,
|
|
|
278
432
|
bin.eventCount += 1;
|
|
279
433
|
bin.eventKindCounts[event.eventKind] += 1;
|
|
280
434
|
}
|
|
435
|
+
const usageArtifacts = traceIndex.getSessionUsageArtifacts(summary.id);
|
|
436
|
+
for (const point of usageArtifacts.usagePoints) {
|
|
437
|
+
if (point.timestampMs < windowStartMs || point.timestampMs >= windowEndMs)
|
|
438
|
+
continue;
|
|
439
|
+
const binIndex = computeBinIndex(windowStartMs, binMs, binCount, point.timestampMs);
|
|
440
|
+
const bin = bins[binIndex];
|
|
441
|
+
if (!bin)
|
|
442
|
+
continue;
|
|
443
|
+
bin.heatmapValues ??= createEmptyHeatmapMetricValues();
|
|
444
|
+
bin.heatmapValues.output_tokens += sanitizeTokenValue(point.outputTokens);
|
|
445
|
+
bin.heatmapValues.total_cost_usd += sanitizeCostValue(point.costUsd);
|
|
446
|
+
}
|
|
281
447
|
}
|
|
282
448
|
}
|
|
283
449
|
let peakConcurrentSessions = 0;
|
|
@@ -293,6 +459,17 @@ function buildWindowActivity(traceIndex, windowStartMs, windowEndMs, binMinutes,
|
|
|
293
459
|
});
|
|
294
460
|
bin.primaryTraceId = bin.activeTraceIds[0] ?? "";
|
|
295
461
|
}
|
|
462
|
+
if (heatmapMetric === "sessions") {
|
|
463
|
+
bin.heatmapValue = bin.activeSessionCount;
|
|
464
|
+
}
|
|
465
|
+
bin.heatmapValues ??= createEmptyHeatmapMetricValues();
|
|
466
|
+
bin.heatmapValues.sessions = bin.activeSessionCount;
|
|
467
|
+
bin.heatmapValue =
|
|
468
|
+
heatmapMetric === "sessions"
|
|
469
|
+
? bin.heatmapValues.sessions
|
|
470
|
+
: heatmapMetric === "output_tokens"
|
|
471
|
+
? bin.heatmapValues.output_tokens
|
|
472
|
+
: bin.heatmapValues.total_cost_usd;
|
|
296
473
|
bin.dominantAgent = pickDominantAgent(bin.activeByAgent);
|
|
297
474
|
bin.dominantEventKind = pickDominantEventKind(bin.eventKindCounts);
|
|
298
475
|
if (bin.activeSessionCount > peakConcurrentSessions) {
|
|
@@ -308,6 +485,65 @@ function buildWindowActivity(traceIndex, windowStartMs, windowEndMs, binMinutes,
|
|
|
308
485
|
peakConcurrentAtMs,
|
|
309
486
|
};
|
|
310
487
|
}
|
|
488
|
+
function buildWindowActivity(traceIndex, windowStartMs, windowEndMs, binMinutes, breakMinutes, options, heatmapMetric) {
|
|
489
|
+
if (!options.cache || options.cacheVersion === undefined) {
|
|
490
|
+
return computeWindowActivity(traceIndex, windowStartMs, windowEndMs, binMinutes, breakMinutes, heatmapMetric);
|
|
491
|
+
}
|
|
492
|
+
return options.cache.getOrBuildWindow(options.cacheVersion, options.nowMs, {
|
|
493
|
+
windowStartMs,
|
|
494
|
+
windowEndMs,
|
|
495
|
+
binMinutes,
|
|
496
|
+
breakMinutes,
|
|
497
|
+
heatmapMetric,
|
|
498
|
+
}, () => computeWindowActivity(traceIndex, windowStartMs, windowEndMs, binMinutes, breakMinutes, heatmapMetric));
|
|
499
|
+
}
|
|
500
|
+
export function resolveAgentActivityDayWindow(options = {}) {
|
|
501
|
+
const nowMs = typeof options.nowMs === "number" && Number.isFinite(options.nowMs) ? options.nowMs : Date.now();
|
|
502
|
+
const requestedTzOffset = validateInt(options.tzOffsetMinutes, "tz_offset_min");
|
|
503
|
+
const tzOffsetMinutes = clamp(requestedTzOffset ?? new Date(nowMs).getTimezoneOffset(), MIN_TZ_OFFSET_MINUTES, MAX_TZ_OFFSET_MINUTES);
|
|
504
|
+
const requestedDateLocal = options.dateLocal?.trim() ?? "";
|
|
505
|
+
const dateLocal = requestedDateLocal || toLocalDateString(nowMs, tzOffsetMinutes);
|
|
506
|
+
parseDateLocal(dateLocal);
|
|
507
|
+
const dayStartMs = windowStartMsForDateLocal(dateLocal, tzOffsetMinutes);
|
|
508
|
+
return {
|
|
509
|
+
windowStartMs: dayStartMs + DEFAULT_DAY_HOUR_START_LOCAL * 60 * 60_000,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
export function resolveAgentActivityWeekWindow(options = {}) {
|
|
513
|
+
const nowMs = typeof options.nowMs === "number" && Number.isFinite(options.nowMs) ? options.nowMs : Date.now();
|
|
514
|
+
const requestedTzOffset = validateInt(options.tzOffsetMinutes, "tz_offset_min");
|
|
515
|
+
const tzOffsetMinutes = clamp(requestedTzOffset ?? new Date(nowMs).getTimezoneOffset(), MIN_TZ_OFFSET_MINUTES, MAX_TZ_OFFSET_MINUTES);
|
|
516
|
+
const requestedEndDateLocal = options.endDateLocal?.trim() ?? "";
|
|
517
|
+
const endDateLocal = requestedEndDateLocal || toLocalDateString(nowMs, tzOffsetMinutes);
|
|
518
|
+
parseDateLocal(endDateLocal);
|
|
519
|
+
const requestedDayCount = validateInt(options.dayCount, "day_count");
|
|
520
|
+
const dayCount = clamp(requestedDayCount ?? DEFAULT_WEEK_DAY_COUNT, MIN_WEEK_DAY_COUNT, MAX_WEEK_DAY_COUNT);
|
|
521
|
+
const startDateLocal = shiftDateLocal(endDateLocal, -(dayCount - 1));
|
|
522
|
+
const requestedHourStart = validateInt(options.hourStartLocal, "hour_start");
|
|
523
|
+
const hourStartLocal = clamp(requestedHourStart ?? DEFAULT_WEEK_HOUR_START_LOCAL, 0, 23);
|
|
524
|
+
const dayStartMs = windowStartMsForDateLocal(startDateLocal, tzOffsetMinutes);
|
|
525
|
+
return {
|
|
526
|
+
windowStartMs: dayStartMs + hourStartLocal * 60 * 60_000,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
export function resolveAgentActivityYearWindow(options = {}) {
|
|
530
|
+
const nowMs = typeof options.nowMs === "number" && Number.isFinite(options.nowMs) ? options.nowMs : Date.now();
|
|
531
|
+
const requestedTzOffset = validateInt(options.tzOffsetMinutes, "tz_offset_min");
|
|
532
|
+
const tzOffsetMinutes = clamp(requestedTzOffset ?? new Date(nowMs).getTimezoneOffset(), MIN_TZ_OFFSET_MINUTES, MAX_TZ_OFFSET_MINUTES);
|
|
533
|
+
const requestedEndDateLocal = options.endDateLocal?.trim() ?? "";
|
|
534
|
+
const endDateLocal = requestedEndDateLocal || toLocalDateString(nowMs, tzOffsetMinutes);
|
|
535
|
+
parseDateLocal(endDateLocal);
|
|
536
|
+
const requestedDayCount = validateInt(options.dayCount, "day_count");
|
|
537
|
+
const maxDayCount = clamp(requestedDayCount ?? MAX_WEEK_DAY_COUNT, MIN_WEEK_DAY_COUNT, MAX_WEEK_DAY_COUNT);
|
|
538
|
+
const yearStartDateLocal = `${endDateLocal.slice(0, 4)}-01-01`;
|
|
539
|
+
const nominalRangeDayCount = Math.floor((windowStartMsForDateLocal(endDateLocal, 0) - windowStartMsForDateLocal(yearStartDateLocal, 0)) / DAY_MS) + 1;
|
|
540
|
+
const dayCount = clamp(nominalRangeDayCount, MIN_WEEK_DAY_COUNT, Math.min(MAX_WEEK_DAY_COUNT, maxDayCount));
|
|
541
|
+
const startDateLocal = shiftDateLocal(endDateLocal, -(dayCount - 1));
|
|
542
|
+
const dayStartMs = windowStartMsForDateLocal(startDateLocal, tzOffsetMinutes);
|
|
543
|
+
return {
|
|
544
|
+
windowStartMs: dayStartMs + DEFAULT_YEAR_HOUR_START_LOCAL * 60 * 60_000,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
311
547
|
export function buildAgentActivityDay(traceIndex, options = {}) {
|
|
312
548
|
const nowMs = typeof options.nowMs === "number" && Number.isFinite(options.nowMs) ? options.nowMs : Date.now();
|
|
313
549
|
const requestedTzOffset = validateInt(options.tzOffsetMinutes, "tz_offset_min");
|
|
@@ -319,12 +555,30 @@ export function buildAgentActivityDay(traceIndex, options = {}) {
|
|
|
319
555
|
const requestedBreakMinutes = validateInt(options.breakMinutes, "break_min");
|
|
320
556
|
const binMinutes = clamp(requestedBinMinutes ?? DEFAULT_BIN_MINUTES, MIN_BIN_MINUTES, MAX_BIN_MINUTES);
|
|
321
557
|
const breakMinutes = clamp(requestedBreakMinutes ?? DEFAULT_BREAK_MINUTES, MIN_BREAK_MINUTES, MAX_BREAK_MINUTES);
|
|
558
|
+
if (options.cache && options.cacheVersion !== undefined) {
|
|
559
|
+
const { cache: _cache, cacheVersion: _cacheVersion, ...uncachedOptions } = options;
|
|
560
|
+
return options.cache.getOrBuildDay(options.cacheVersion, nowMs, {
|
|
561
|
+
dateLocal,
|
|
562
|
+
tzOffsetMinutes,
|
|
563
|
+
binMinutes,
|
|
564
|
+
breakMinutes,
|
|
565
|
+
}, () => buildAgentActivityDay(traceIndex, {
|
|
566
|
+
...uncachedOptions,
|
|
567
|
+
nowMs,
|
|
568
|
+
dateLocal,
|
|
569
|
+
tzOffsetMinutes,
|
|
570
|
+
binMinutes,
|
|
571
|
+
breakMinutes,
|
|
572
|
+
}));
|
|
573
|
+
}
|
|
322
574
|
const dayStartMs = windowStartMsForDateLocal(dateLocal, tzOffsetMinutes);
|
|
323
575
|
const windowStartMs = dayStartMs + DEFAULT_DAY_HOUR_START_LOCAL * 60 * 60_000;
|
|
324
576
|
const windowEndOfDayMs = windowStartMs + DAY_MS;
|
|
325
577
|
const todayLocal = toLocalDateString(nowMs, tzOffsetMinutes);
|
|
326
578
|
const windowEndMs = dateLocal === todayLocal ? Math.max(windowStartMs, Math.min(windowEndOfDayMs, nowMs)) : windowEndOfDayMs;
|
|
327
|
-
const windowActivity = buildWindowActivity(traceIndex, windowStartMs, windowEndMs, binMinutes, breakMinutes
|
|
579
|
+
const windowActivity = buildWindowActivity(traceIndex, windowStartMs, windowEndMs, binMinutes, breakMinutes, options.cache && options.cacheVersion !== undefined
|
|
580
|
+
? { cache: options.cache, cacheVersion: options.cacheVersion, nowMs }
|
|
581
|
+
: { nowMs }, "sessions");
|
|
328
582
|
return {
|
|
329
583
|
dateLocal,
|
|
330
584
|
tzOffsetMinutes,
|
|
@@ -335,6 +589,7 @@ export function buildAgentActivityDay(traceIndex, options = {}) {
|
|
|
335
589
|
totalSessionsInWindow: windowActivity.totalSessionsInWindow,
|
|
336
590
|
peakConcurrentSessions: windowActivity.peakConcurrentSessions,
|
|
337
591
|
peakConcurrentAtMs: windowActivity.peakConcurrentAtMs,
|
|
592
|
+
totalEventCount: totalEventCountForBins(windowActivity.bins),
|
|
338
593
|
bins: windowActivity.bins,
|
|
339
594
|
};
|
|
340
595
|
}
|
|
@@ -345,6 +600,8 @@ export function buildAgentActivityWeek(traceIndex, options = {}) {
|
|
|
345
600
|
const requestedEndDateLocal = options.endDateLocal?.trim() ?? "";
|
|
346
601
|
const endDateLocal = requestedEndDateLocal || toLocalDateString(nowMs, tzOffsetMinutes);
|
|
347
602
|
parseDateLocal(endDateLocal);
|
|
603
|
+
const heatmapMetric = options.heatmapMetric ?? traceIndex.getConfig().activityHeatmap.metric;
|
|
604
|
+
const heatmapColor = options.heatmapColor ?? traceIndex.getConfig().activityHeatmap.color;
|
|
348
605
|
const requestedDayCount = validateInt(options.dayCount, "day_count");
|
|
349
606
|
const dayCount = clamp(requestedDayCount ?? DEFAULT_WEEK_DAY_COUNT, MIN_WEEK_DAY_COUNT, MAX_WEEK_DAY_COUNT);
|
|
350
607
|
const startDateLocal = shiftDateLocal(endDateLocal, -(dayCount - 1));
|
|
@@ -361,6 +618,30 @@ export function buildAgentActivityWeek(traceIndex, options = {}) {
|
|
|
361
618
|
if (slotMinutes > windowMinutes) {
|
|
362
619
|
throw new Error("slot_min too large for hour window");
|
|
363
620
|
}
|
|
621
|
+
if (options.cache && options.cacheVersion !== undefined) {
|
|
622
|
+
const { cache: _cache, cacheVersion: _cacheVersion, ...uncachedOptions } = options;
|
|
623
|
+
return options.cache.getOrBuildWeek(options.cacheVersion, nowMs, {
|
|
624
|
+
endDateLocal,
|
|
625
|
+
tzOffsetMinutes,
|
|
626
|
+
dayCount,
|
|
627
|
+
slotMinutes,
|
|
628
|
+
hourStartLocal,
|
|
629
|
+
hourEndLocal,
|
|
630
|
+
heatmapMetric,
|
|
631
|
+
heatmapColor,
|
|
632
|
+
}, () => buildAgentActivityWeek(traceIndex, {
|
|
633
|
+
...uncachedOptions,
|
|
634
|
+
nowMs,
|
|
635
|
+
endDateLocal,
|
|
636
|
+
tzOffsetMinutes,
|
|
637
|
+
dayCount,
|
|
638
|
+
slotMinutes,
|
|
639
|
+
hourStartLocal,
|
|
640
|
+
hourEndLocal,
|
|
641
|
+
heatmapMetric,
|
|
642
|
+
heatmapColor,
|
|
643
|
+
}));
|
|
644
|
+
}
|
|
364
645
|
const windowDurationMs = windowMinutes * 60_000;
|
|
365
646
|
const days = [];
|
|
366
647
|
const todayLocal = toLocalDateString(nowMs, tzOffsetMinutes);
|
|
@@ -372,18 +653,30 @@ export function buildAgentActivityWeek(traceIndex, options = {}) {
|
|
|
372
653
|
const windowEndMs = dateLocal === todayLocal
|
|
373
654
|
? Math.max(windowStartMs, Math.min(nominalWindowEndMs, nowMs))
|
|
374
655
|
: nominalWindowEndMs;
|
|
375
|
-
const windowActivity = buildWindowActivity(traceIndex, windowStartMs, windowEndMs, slotMinutes, DEFAULT_BREAK_MINUTES
|
|
656
|
+
const windowActivity = buildWindowActivity(traceIndex, windowStartMs, windowEndMs, slotMinutes, DEFAULT_BREAK_MINUTES, options.cache && options.cacheVersion !== undefined
|
|
657
|
+
? { cache: options.cache, cacheVersion: options.cacheVersion, nowMs }
|
|
658
|
+
: { nowMs }, heatmapMetric);
|
|
376
659
|
days.push({
|
|
377
660
|
dateLocal,
|
|
378
661
|
windowStartMs,
|
|
379
662
|
windowEndMs,
|
|
380
663
|
totalSessionsInWindow: windowActivity.totalSessionsInWindow,
|
|
664
|
+
heatmapValue: heatmapMetric === "sessions"
|
|
665
|
+
? windowActivity.totalSessionsInWindow
|
|
666
|
+
: sumHeatmapMetricForBins(windowActivity.bins, heatmapMetric),
|
|
667
|
+
heatmapValues: {
|
|
668
|
+
sessions: windowActivity.totalSessionsInWindow,
|
|
669
|
+
output_tokens: sumHeatmapMetricForBins(windowActivity.bins, "output_tokens"),
|
|
670
|
+
total_cost_usd: sumHeatmapMetricForBins(windowActivity.bins, "total_cost_usd"),
|
|
671
|
+
},
|
|
381
672
|
peakConcurrentSessions: windowActivity.peakConcurrentSessions,
|
|
382
673
|
peakConcurrentAtMs: windowActivity.peakConcurrentAtMs,
|
|
674
|
+
totalEventCount: totalEventCountForBins(windowActivity.bins),
|
|
383
675
|
bins: windowActivity.bins,
|
|
384
676
|
});
|
|
385
677
|
}
|
|
386
678
|
return {
|
|
679
|
+
presentation: buildHeatmapPresentation(heatmapMetric, heatmapColor),
|
|
387
680
|
tzOffsetMinutes,
|
|
388
681
|
dayCount,
|
|
389
682
|
slotMinutes,
|
|
@@ -392,6 +685,211 @@ export function buildAgentActivityWeek(traceIndex, options = {}) {
|
|
|
392
685
|
startDateLocal,
|
|
393
686
|
endDateLocal,
|
|
394
687
|
days,
|
|
688
|
+
usageSummary: buildWeeklyUsageSummaryFromDays(traceIndex, days),
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
export function buildAgentActivityYear(traceIndex, options = {}) {
|
|
692
|
+
const nowMs = typeof options.nowMs === "number" && Number.isFinite(options.nowMs) ? options.nowMs : Date.now();
|
|
693
|
+
const requestedTzOffset = validateInt(options.tzOffsetMinutes, "tz_offset_min");
|
|
694
|
+
const tzOffsetMinutes = clamp(requestedTzOffset ?? new Date(nowMs).getTimezoneOffset(), MIN_TZ_OFFSET_MINUTES, MAX_TZ_OFFSET_MINUTES);
|
|
695
|
+
const requestedEndDateLocal = options.endDateLocal?.trim() ?? "";
|
|
696
|
+
const endDateLocal = requestedEndDateLocal || toLocalDateString(nowMs, tzOffsetMinutes);
|
|
697
|
+
parseDateLocal(endDateLocal);
|
|
698
|
+
const heatmapMetric = options.heatmapMetric ?? traceIndex.getConfig().activityHeatmap.metric;
|
|
699
|
+
const heatmapColor = options.heatmapColor ?? traceIndex.getConfig().activityHeatmap.color;
|
|
700
|
+
const requestedDayCount = validateInt(options.dayCount, "day_count");
|
|
701
|
+
const maxDayCount = clamp(requestedDayCount ?? MAX_WEEK_DAY_COUNT, MIN_WEEK_DAY_COUNT, MAX_WEEK_DAY_COUNT);
|
|
702
|
+
const yearStartDateLocal = `${endDateLocal.slice(0, 4)}-01-01`;
|
|
703
|
+
const todayLocal = toLocalDateString(nowMs, tzOffsetMinutes);
|
|
704
|
+
const nominalRangeDayCount = Math.floor((windowStartMsForDateLocal(endDateLocal, 0) - windowStartMsForDateLocal(yearStartDateLocal, 0)) / DAY_MS) + 1;
|
|
705
|
+
const dayCount = clamp(nominalRangeDayCount, MIN_WEEK_DAY_COUNT, Math.min(MAX_WEEK_DAY_COUNT, maxDayCount));
|
|
706
|
+
const provisionalStartDateLocal = shiftDateLocal(endDateLocal, -(dayCount - 1));
|
|
707
|
+
const summaryById = new Map(traceIndex.getSummaries().map((summary) => [summary.id, summary]));
|
|
708
|
+
let earliestActiveDateLocal = null;
|
|
709
|
+
for (const summary of summaryById.values()) {
|
|
710
|
+
const activeAtMs = Math.max(summary.firstEventTs ?? 0, 1);
|
|
711
|
+
if (activeAtMs <= 0)
|
|
712
|
+
continue;
|
|
713
|
+
const activeDateLocal = toLocalDateString(activeAtMs, tzOffsetMinutes);
|
|
714
|
+
if (activeDateLocal < provisionalStartDateLocal || activeDateLocal > endDateLocal)
|
|
715
|
+
continue;
|
|
716
|
+
if (earliestActiveDateLocal === null || activeDateLocal < earliestActiveDateLocal) {
|
|
717
|
+
earliestActiveDateLocal = activeDateLocal;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
const startDateLocal = earliestActiveDateLocal ?? provisionalStartDateLocal;
|
|
721
|
+
const effectiveDayCount = Math.floor((windowStartMsForDateLocal(endDateLocal, 0) - windowStartMsForDateLocal(startDateLocal, 0)) / DAY_MS) + 1;
|
|
722
|
+
if (options.cache && options.cacheVersion !== undefined) {
|
|
723
|
+
const { cache: _cache, cacheVersion: _cacheVersion, ...uncachedOptions } = options;
|
|
724
|
+
return options.cache.getOrBuildYear(options.cacheVersion, nowMs, {
|
|
725
|
+
endDateLocal,
|
|
726
|
+
tzOffsetMinutes,
|
|
727
|
+
dayCount: effectiveDayCount,
|
|
728
|
+
heatmapMetric,
|
|
729
|
+
heatmapColor,
|
|
730
|
+
}, () => buildAgentActivityYear(traceIndex, {
|
|
731
|
+
...uncachedOptions,
|
|
732
|
+
nowMs,
|
|
733
|
+
endDateLocal,
|
|
734
|
+
tzOffsetMinutes,
|
|
735
|
+
dayCount: effectiveDayCount,
|
|
736
|
+
heatmapMetric,
|
|
737
|
+
heatmapColor,
|
|
738
|
+
}));
|
|
739
|
+
}
|
|
740
|
+
const windowDurationMs = computeWeekWindowMinutes(DEFAULT_YEAR_HOUR_START_LOCAL, DEFAULT_YEAR_HOUR_START_LOCAL) * 60_000;
|
|
741
|
+
const slotMs = DEFAULT_YEAR_SLOT_MINUTES * 60_000;
|
|
742
|
+
const slotCount = Math.max(1, Math.ceil(windowDurationMs / slotMs));
|
|
743
|
+
const dayStates = [];
|
|
744
|
+
for (let offset = 0; offset < effectiveDayCount; offset += 1) {
|
|
745
|
+
const dateLocal = shiftDateLocal(startDateLocal, offset);
|
|
746
|
+
const dayStartMs = windowStartMsForDateLocal(dateLocal, tzOffsetMinutes);
|
|
747
|
+
const windowStartMs = dayStartMs + DEFAULT_YEAR_HOUR_START_LOCAL * 60 * 60_000;
|
|
748
|
+
const nominalWindowEndMs = windowStartMs + windowDurationMs;
|
|
749
|
+
const windowEndMs = dateLocal === todayLocal ? Math.max(windowStartMs, Math.min(nominalWindowEndMs, nowMs)) : nominalWindowEndMs;
|
|
750
|
+
dayStates.push({
|
|
751
|
+
dateLocal,
|
|
752
|
+
windowStartMs,
|
|
753
|
+
windowEndMs,
|
|
754
|
+
totalSessionIds: new Set(),
|
|
755
|
+
heatmapValue: 0,
|
|
756
|
+
heatmapValues: createEmptyHeatmapMetricValues(),
|
|
757
|
+
totalEventCount: 0,
|
|
758
|
+
boundaryEvents: [],
|
|
759
|
+
agentSlotCounts: Array.from({ length: slotCount }, () => createEmptyAgentCounts()),
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
const overallStartMs = dayStates[0]?.windowStartMs ?? 0;
|
|
763
|
+
const overallEndMs = dayStates[dayStates.length - 1]?.windowEndMs ?? overallStartMs;
|
|
764
|
+
const usageAccumulator = createUsageAccumulator();
|
|
765
|
+
for (const summary of summaryById.values()) {
|
|
766
|
+
const span = resolveSessionSpan(summary);
|
|
767
|
+
if (span.endMs < overallStartMs || span.startMs >= overallEndMs)
|
|
768
|
+
continue;
|
|
769
|
+
const activityArtifacts = traceIndex.getSessionActivityArtifacts(summary.id);
|
|
770
|
+
let summaryContributed = false;
|
|
771
|
+
for (const timestampMs of activityArtifacts.eventTimestamps) {
|
|
772
|
+
if (timestampMs < overallStartMs || timestampMs >= overallEndMs)
|
|
773
|
+
continue;
|
|
774
|
+
const dayIndex = Math.floor((timestampMs - overallStartMs) / DAY_MS);
|
|
775
|
+
const dayState = dayStates[dayIndex];
|
|
776
|
+
if (!dayState)
|
|
777
|
+
continue;
|
|
778
|
+
if (timestampMs >= dayState.windowEndMs)
|
|
779
|
+
continue;
|
|
780
|
+
dayState.totalEventCount += 1;
|
|
781
|
+
}
|
|
782
|
+
for (const segment of activityArtifacts.activeSegments) {
|
|
783
|
+
if (segment.endMs < overallStartMs || segment.startMs >= overallEndMs)
|
|
784
|
+
continue;
|
|
785
|
+
const clampedStartMs = Math.max(segment.startMs, overallStartMs);
|
|
786
|
+
const clampedEndExclusiveMs = Math.min(segment.endMs + 1, overallEndMs);
|
|
787
|
+
if (clampedEndExclusiveMs <= clampedStartMs)
|
|
788
|
+
continue;
|
|
789
|
+
summaryContributed = true;
|
|
790
|
+
const startDayIndex = Math.max(0, Math.floor((clampedStartMs - overallStartMs) / DAY_MS));
|
|
791
|
+
const endDayIndex = Math.max(0, Math.floor((clampedEndExclusiveMs - 1 - overallStartMs) / DAY_MS));
|
|
792
|
+
for (let dayIndex = startDayIndex; dayIndex <= endDayIndex; dayIndex += 1) {
|
|
793
|
+
const dayState = dayStates[dayIndex];
|
|
794
|
+
if (!dayState)
|
|
795
|
+
continue;
|
|
796
|
+
const overlapStartMs = Math.max(clampedStartMs, dayState.windowStartMs);
|
|
797
|
+
const overlapEndExclusiveMs = Math.min(clampedEndExclusiveMs, dayState.windowEndMs);
|
|
798
|
+
if (overlapEndExclusiveMs <= overlapStartMs)
|
|
799
|
+
continue;
|
|
800
|
+
dayState.totalSessionIds.add(summary.id);
|
|
801
|
+
dayState.boundaryEvents.push({ atMs: overlapStartMs, delta: 1 });
|
|
802
|
+
dayState.boundaryEvents.push({ atMs: overlapEndExclusiveMs, delta: -1 });
|
|
803
|
+
const startSlotIndex = computeBinIndex(dayState.windowStartMs, slotMs, slotCount, overlapStartMs);
|
|
804
|
+
const endSlotIndex = computeBinIndex(dayState.windowStartMs, slotMs, slotCount, Math.max(overlapStartMs, overlapEndExclusiveMs - 1));
|
|
805
|
+
for (let slotIndex = startSlotIndex; slotIndex <= endSlotIndex; slotIndex += 1) {
|
|
806
|
+
dayState.agentSlotCounts[slotIndex][summary.agent] += 1;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
if (!summaryContributed)
|
|
811
|
+
continue;
|
|
812
|
+
usageAccumulator.totalUniqueSessionIds.add(summary.id);
|
|
813
|
+
usageAccumulator.uniqueSessionIdsByAgent.get(summary.agent)?.add(summary.id);
|
|
814
|
+
}
|
|
815
|
+
for (const summary of summaryById.values()) {
|
|
816
|
+
const row = usageAccumulator.usageByAgent.get(summary.agent);
|
|
817
|
+
if (!row)
|
|
818
|
+
continue;
|
|
819
|
+
const usageArtifacts = traceIndex.getSessionUsageArtifacts(summary.id);
|
|
820
|
+
for (const point of usageArtifacts.usagePoints) {
|
|
821
|
+
if (point.timestampMs < overallStartMs || point.timestampMs >= overallEndMs)
|
|
822
|
+
continue;
|
|
823
|
+
const dayIndex = Math.floor((point.timestampMs - overallStartMs) / DAY_MS);
|
|
824
|
+
const dayState = dayStates[dayIndex];
|
|
825
|
+
if (!dayState)
|
|
826
|
+
continue;
|
|
827
|
+
if (point.timestampMs < dayState.windowStartMs || point.timestampMs >= dayState.windowEndMs)
|
|
828
|
+
continue;
|
|
829
|
+
dayState.heatmapValues.output_tokens += sanitizeTokenValue(point.outputTokens);
|
|
830
|
+
dayState.heatmapValues.total_cost_usd += sanitizeCostValue(point.costUsd);
|
|
831
|
+
applyUsagePointToRow(row, point);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
const days = dayStates.map((dayState) => {
|
|
835
|
+
let peakConcurrentSessions = 0;
|
|
836
|
+
let peakConcurrentAtMs = null;
|
|
837
|
+
let concurrentSessions = 0;
|
|
838
|
+
const sortedBoundaryEvents = [...dayState.boundaryEvents].sort((left, right) => left.atMs - right.atMs || left.delta - right.delta);
|
|
839
|
+
for (const boundaryEvent of sortedBoundaryEvents) {
|
|
840
|
+
concurrentSessions += boundaryEvent.delta;
|
|
841
|
+
if (concurrentSessions > peakConcurrentSessions) {
|
|
842
|
+
peakConcurrentSessions = concurrentSessions;
|
|
843
|
+
peakConcurrentAtMs = boundaryEvent.atMs;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
usageAccumulator.peakAllAgentConcurrency = Math.max(usageAccumulator.peakAllAgentConcurrency, peakConcurrentSessions);
|
|
847
|
+
for (const agent of AGENT_KIND_KEYS) {
|
|
848
|
+
const row = usageAccumulator.usageByAgent.get(agent);
|
|
849
|
+
if (!row)
|
|
850
|
+
continue;
|
|
851
|
+
let activeToday = false;
|
|
852
|
+
for (const slotCounts of dayState.agentSlotCounts) {
|
|
853
|
+
const concurrentAgentSessions = slotCounts[agent] ?? 0;
|
|
854
|
+
if (concurrentAgentSessions <= 0)
|
|
855
|
+
continue;
|
|
856
|
+
activeToday = true;
|
|
857
|
+
row.sessionHours += concurrentAgentSessions * (slotMs / 3_600_000);
|
|
858
|
+
row.activeSlots += 1;
|
|
859
|
+
row.peakConcurrentSessions = Math.max(row.peakConcurrentSessions, concurrentAgentSessions);
|
|
860
|
+
}
|
|
861
|
+
if (activeToday) {
|
|
862
|
+
row.activeDays += 1;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
return {
|
|
866
|
+
dateLocal: dayState.dateLocal,
|
|
867
|
+
windowStartMs: dayState.windowStartMs,
|
|
868
|
+
windowEndMs: dayState.windowEndMs,
|
|
869
|
+
totalSessionsInWindow: dayState.totalSessionIds.size,
|
|
870
|
+
heatmapValue: heatmapMetric === "sessions"
|
|
871
|
+
? dayState.totalSessionIds.size
|
|
872
|
+
: heatmapMetric === "output_tokens"
|
|
873
|
+
? dayState.heatmapValues.output_tokens
|
|
874
|
+
: dayState.heatmapValues.total_cost_usd,
|
|
875
|
+
heatmapValues: {
|
|
876
|
+
sessions: dayState.totalSessionIds.size,
|
|
877
|
+
output_tokens: dayState.heatmapValues.output_tokens,
|
|
878
|
+
total_cost_usd: dayState.heatmapValues.total_cost_usd,
|
|
879
|
+
},
|
|
880
|
+
peakConcurrentSessions,
|
|
881
|
+
peakConcurrentAtMs,
|
|
882
|
+
totalEventCount: dayState.totalEventCount,
|
|
883
|
+
};
|
|
884
|
+
});
|
|
885
|
+
return {
|
|
886
|
+
presentation: buildHeatmapPresentation(heatmapMetric, heatmapColor),
|
|
887
|
+
tzOffsetMinutes,
|
|
888
|
+
dayCount: effectiveDayCount,
|
|
889
|
+
startDateLocal,
|
|
890
|
+
endDateLocal,
|
|
891
|
+
days,
|
|
892
|
+
usageSummary: finalizeUsageSummary(usageAccumulator),
|
|
395
893
|
};
|
|
396
894
|
}
|
|
397
895
|
//# sourceMappingURL=activity.js.map
|