@newrelic/preflight 1.14.34 → 1.14.36
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 +4 -3
- package/dist/dashboard/routes/api-handler.d.ts.map +1 -1
- package/dist/dashboard/routes/api-handler.js +205 -39
- package/dist/dashboard/routes/api-handler.js.map +1 -1
- package/dist/dashboard/workflow-store.d.ts +20 -0
- package/dist/dashboard/workflow-store.d.ts.map +1 -1
- package/dist/dashboard/workflow-store.js +8 -0
- package/dist/dashboard/workflow-store.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +99 -33
- package/dist/index.js.map +1 -1
- package/dist/metrics/anti-patterns.d.ts +3 -0
- package/dist/metrics/anti-patterns.d.ts.map +1 -1
- package/dist/metrics/anti-patterns.js +69 -6
- package/dist/metrics/anti-patterns.js.map +1 -1
- package/dist/metrics/cost-tracker.d.ts +17 -0
- package/dist/metrics/cost-tracker.d.ts.map +1 -1
- package/dist/metrics/cost-tracker.js +19 -0
- package/dist/metrics/cost-tracker.js.map +1 -1
- package/dist/metrics/decision-tracker.d.ts +18 -1
- package/dist/metrics/decision-tracker.d.ts.map +1 -1
- package/dist/metrics/decision-tracker.js +28 -11
- package/dist/metrics/decision-tracker.js.map +1 -1
- package/dist/metrics/git-efficiency-tracker.d.ts +14 -1
- package/dist/metrics/git-efficiency-tracker.d.ts.map +1 -1
- package/dist/metrics/git-efficiency-tracker.js +50 -3
- package/dist/metrics/git-efficiency-tracker.js.map +1 -1
- package/dist/metrics/turn-cost-attributor.d.ts +37 -7
- package/dist/metrics/turn-cost-attributor.d.ts.map +1 -1
- package/dist/metrics/turn-cost-attributor.js +140 -45
- package/dist/metrics/turn-cost-attributor.js.map +1 -1
- package/dist/security/audit-trail.d.ts +32 -1
- package/dist/security/audit-trail.d.ts.map +1 -1
- package/dist/security/audit-trail.js +142 -4
- package/dist/security/audit-trail.js.map +1 -1
- package/dist/storage/local-store.d.ts +29 -0
- package/dist/storage/local-store.d.ts.map +1 -1
- package/dist/storage/local-store.js +93 -0
- package/dist/storage/local-store.js.map +1 -1
- package/dist/tools/extended-analytics-tools.d.ts +19 -0
- package/dist/tools/extended-analytics-tools.d.ts.map +1 -1
- package/dist/tools/extended-analytics-tools.js +52 -0
- package/dist/tools/extended-analytics-tools.js.map +1 -1
- package/dist/web/assets/index-DaSOJbc6.js +64 -0
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/assets/index-BEe0_Ti_.js +0 -64
|
@@ -148,6 +148,7 @@ export interface ApiHandlerDeps {
|
|
|
148
148
|
readonly costForecast?: () => CostForecast;
|
|
149
149
|
readonly antiPatternDetector?: {
|
|
150
150
|
getCurrentPatterns: () => readonly AntiPattern[];
|
|
151
|
+
getTotalAntiPatternWaste: () => number;
|
|
151
152
|
};
|
|
152
153
|
readonly retryDetector?: {
|
|
153
154
|
getMetrics: () => RetryDetectorMetrics;
|
|
@@ -156,13 +157,13 @@ export interface ApiHandlerDeps {
|
|
|
156
157
|
getMetrics: () => InstructionDriftMetrics;
|
|
157
158
|
};
|
|
158
159
|
readonly decisionTracker?: {
|
|
159
|
-
getMetrics: () => DecisionTreeMetrics;
|
|
160
|
+
getMetrics: (sessionId?: string) => DecisionTreeMetrics;
|
|
160
161
|
};
|
|
161
162
|
readonly turnCostAttributor?: {
|
|
162
|
-
getMetrics: () => CostAttributionMetrics;
|
|
163
|
+
getMetrics: (sessionId?: string) => CostAttributionMetrics;
|
|
163
164
|
};
|
|
164
165
|
readonly auditTrailManager?: {
|
|
165
|
-
getAuditLog: () => readonly AuditRecord[];
|
|
166
|
+
getAuditLog: (limit?: number) => readonly AuditRecord[];
|
|
166
167
|
};
|
|
167
168
|
readonly weeklySummaryGenerator?: WeeklySummaryGenerator;
|
|
168
169
|
readonly budgetTracker?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-handler.d.ts","sourceRoot":"","sources":["../../../src/dashboard/routes/api-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAc5D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,mCAAmC,CAAC;AAS3C,OAAO,KAAK,EAAkC,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7F,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAC1F,OAAO,KAAK,EAAE,cAAc,EAAoB,MAAM,sBAAsB,CAAC;AAC7E,OAAO,KAAK,EACV,gBAAgB,EAChB,SAAS,EACT,qBAAqB,EACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAC7E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"api-handler.d.ts","sourceRoot":"","sources":["../../../src/dashboard/routes/api-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -46,6 +46,7 @@ function toWorkflowRunDto(row) {
|
|
|
46
46
|
agentCount: r.agent_count,
|
|
47
47
|
totalTokens: r.total_tokens,
|
|
48
48
|
totalUsd: r.total_usd ?? null,
|
|
49
|
+
costUnknown: r.cost_unknown ?? false,
|
|
49
50
|
declaredPhases: r.declared_phases ?? null,
|
|
50
51
|
observedPhases: r.observed_phases,
|
|
51
52
|
declaredParallelWidths: r.declared_parallel_widths ?? [],
|
|
@@ -93,6 +94,11 @@ function toLiveWorkflowDetail(live) {
|
|
|
93
94
|
agentCount: live.agentCount,
|
|
94
95
|
totalTokens: live.totalTokens,
|
|
95
96
|
totalUsd: live.totalUsd,
|
|
97
|
+
// A still-running workflow's cost comes straight from this process's own
|
|
98
|
+
// live SubagentWatcher state, not a cross-process/restart-vulnerable
|
|
99
|
+
// on-disk rollup — so it's never ambiguous the way a completed run's
|
|
100
|
+
// WorkflowStore-read cost can be.
|
|
101
|
+
costUnknown: false,
|
|
96
102
|
declaredPhases: live.topology?.declaredPhases ?? null,
|
|
97
103
|
// No phase-title telemetry exists mid-run (that comes from the rollup's
|
|
98
104
|
// workflowProgress), so observed phases are unknown → 0.
|
|
@@ -319,18 +325,44 @@ function computeTodayPeakConcurrency(sessions) {
|
|
|
319
325
|
}
|
|
320
326
|
return peak;
|
|
321
327
|
}
|
|
328
|
+
// Mirrors QualityProxyTracker's own getRawCounts()/countBySignal() math, but
|
|
329
|
+
// over a caller-filtered event slice — needed so GET /api/quality-proxy can
|
|
330
|
+
// day-filter this process's own live contribution (see the startMs pattern
|
|
331
|
+
// in GET /api/tool-selection-score) without adding a day-bucketed accessor
|
|
332
|
+
// to the tracker itself.
|
|
333
|
+
function rawQualityCountsFromEvents(events) {
|
|
334
|
+
const count = (signal) => events.filter((e) => e.signal === signal).length;
|
|
335
|
+
return {
|
|
336
|
+
totalSignals: events.length,
|
|
337
|
+
diffApplyCleanCount: count('diff_applied_clean'),
|
|
338
|
+
diffFailCount: count('diff_failed'),
|
|
339
|
+
testPassCount: count('test_pass'),
|
|
340
|
+
testFailCount: count('test_fail'),
|
|
341
|
+
backtrackCount: count('backtrack'),
|
|
342
|
+
selfCorrectionCount: count('self_correction'),
|
|
343
|
+
};
|
|
344
|
+
}
|
|
322
345
|
function computeDailyPeakConcurrency(sessions, days) {
|
|
323
|
-
|
|
346
|
+
// Local (not UTC) day boundaries — see localStartOfDay's doc comment. A
|
|
347
|
+
// developer's evening session commonly straddles UTC midnight, which would
|
|
348
|
+
// otherwise land that activity on the wrong calendar day for anyone not in
|
|
349
|
+
// UTC, and disagree with this same file's local-day `aggregateDailyCost`
|
|
350
|
+
// equivalent on the client (History.tsx).
|
|
351
|
+
const todayStart = localStartOfDay();
|
|
324
352
|
const result = [];
|
|
325
353
|
for (let d = days - 1; d >= 0; d--) {
|
|
326
|
-
const dayStart = new Date(
|
|
327
|
-
dayStart.
|
|
328
|
-
dayStart.setUTCHours(0, 0, 0, 0);
|
|
329
|
-
const dayEnd = new Date(dayStart);
|
|
330
|
-
dayEnd.setUTCDate(dayEnd.getUTCDate() + 1);
|
|
354
|
+
const dayStart = new Date(todayStart);
|
|
355
|
+
dayStart.setDate(dayStart.getDate() - d);
|
|
331
356
|
const dayStartMs = dayStart.getTime();
|
|
357
|
+
// `dayEnd` must be derived via the same local-date arithmetic as
|
|
358
|
+
// `dayStart`, not `dayStartMs + 86_400_000` — a local calendar day is
|
|
359
|
+
// 23h or 25h long on the ~2 DST-transition days/year, so the fixed-ms
|
|
360
|
+
// offset lands an hour into (or short of) the next/previous local day,
|
|
361
|
+
// misattributing entries near the boundary on those days.
|
|
362
|
+
const dayEnd = new Date(dayStart);
|
|
363
|
+
dayEnd.setDate(dayEnd.getDate() + 1);
|
|
332
364
|
const dayEndMs = dayEnd.getTime();
|
|
333
|
-
const dateKey =
|
|
365
|
+
const dateKey = localDateKey(dayStartMs);
|
|
334
366
|
// Find sessions that overlap with this day and have timeline data
|
|
335
367
|
const events = [];
|
|
336
368
|
for (const session of sessions) {
|
|
@@ -570,6 +602,12 @@ export function createApiHandler(deps) {
|
|
|
570
602
|
durationMs: live.sessionDurationMs,
|
|
571
603
|
toolCallCount: live.toolCallCount,
|
|
572
604
|
estimatedCostUsd: deps.costTracker?.getMetrics().sessionTotalCostUsd ?? null,
|
|
605
|
+
// costTracker.getMetrics() also exposes the model this live
|
|
606
|
+
// session is actually using; without it, aggregateModelPerformance
|
|
607
|
+
// (History.tsx) buckets this session's real-time cost/count under
|
|
608
|
+
// "unknown" until the session is persisted and rebuilt with a
|
|
609
|
+
// real `model` field.
|
|
610
|
+
model: deps.costTracker?.getMetrics().model ?? null,
|
|
573
611
|
});
|
|
574
612
|
}
|
|
575
613
|
}
|
|
@@ -1143,20 +1181,75 @@ export function createApiHandler(deps) {
|
|
|
1143
1181
|
return unavailable(res, 'instructionDriftTracker');
|
|
1144
1182
|
jsonOk(res, deps.instructionDriftTracker.getMetrics());
|
|
1145
1183
|
});
|
|
1146
|
-
routes.set('GET /api/decision-tree', (
|
|
1184
|
+
routes.set('GET /api/decision-tree', (req, res) => {
|
|
1147
1185
|
if (!deps.decisionTracker)
|
|
1148
1186
|
return unavailable(res, 'decisionTracker');
|
|
1149
|
-
|
|
1187
|
+
// Optional ?sessionId= scopes the result to one session — without
|
|
1188
|
+
// it, this process-global tracker's data can belong to a different
|
|
1189
|
+
// session than the one selected in the dashboard's trace pane (in
|
|
1190
|
+
// `--local` mode, several concurrently-live sessions blended together).
|
|
1191
|
+
const url = new URL(req.url ?? '/', 'http://localhost');
|
|
1192
|
+
const sessionId = url.searchParams.get('sessionId') ?? undefined;
|
|
1193
|
+
jsonOk(res, deps.decisionTracker.getMetrics(sessionId));
|
|
1150
1194
|
});
|
|
1151
|
-
routes.set('GET /api/turn-costs', (
|
|
1195
|
+
routes.set('GET /api/turn-costs', (req, res) => {
|
|
1152
1196
|
if (!deps.turnCostAttributor)
|
|
1153
1197
|
return unavailable(res, 'turnCostAttributor');
|
|
1154
|
-
|
|
1198
|
+
// Same reasoning as /api/decision-tree above.
|
|
1199
|
+
const url = new URL(req.url ?? '/', 'http://localhost');
|
|
1200
|
+
const sessionId = url.searchParams.get('sessionId') ?? undefined;
|
|
1201
|
+
jsonOk(res, deps.turnCostAttributor.getMetrics(sessionId));
|
|
1202
|
+
});
|
|
1203
|
+
routes.set('GET /api/compute-waste', (_req, res) => {
|
|
1204
|
+
if (!deps.retryDetector)
|
|
1205
|
+
return unavailable(res, 'retryDetector');
|
|
1206
|
+
if (!deps.antiPatternDetector)
|
|
1207
|
+
return unavailable(res, 'antiPatternDetector');
|
|
1208
|
+
const retryTokensWasted = deps.retryDetector.getMetrics().totalTokensWasted;
|
|
1209
|
+
const antiPatternTokensWasted = deps.antiPatternDetector.getTotalAntiPatternWaste();
|
|
1210
|
+
const totalTokensWasted = retryTokensWasted + antiPatternTokensWasted;
|
|
1211
|
+
const patterns = deps.antiPatternDetector.getCurrentPatterns();
|
|
1212
|
+
const breakdownMap = patterns
|
|
1213
|
+
.filter((p) => p.tokensWasted > 0)
|
|
1214
|
+
.reduce((acc, p) => {
|
|
1215
|
+
const existing = acc.get(p.type) ?? { tokens_wasted: 0, instances: 0 };
|
|
1216
|
+
acc.set(p.type, {
|
|
1217
|
+
tokens_wasted: existing.tokens_wasted + p.tokensWasted,
|
|
1218
|
+
instances: existing.instances + 1,
|
|
1219
|
+
});
|
|
1220
|
+
return acc;
|
|
1221
|
+
}, new Map());
|
|
1222
|
+
const breakdown = [...breakdownMap.entries()]
|
|
1223
|
+
.map(([type, v]) => ({ type, ...v }))
|
|
1224
|
+
.sort((a, b) => b.tokens_wasted - a.tokens_wasted);
|
|
1225
|
+
const status = totalTokensWasted >= 2000
|
|
1226
|
+
? 'needs_attention'
|
|
1227
|
+
: totalTokensWasted >= 500
|
|
1228
|
+
? 'moderate'
|
|
1229
|
+
: 'clean';
|
|
1230
|
+
jsonOk(res, {
|
|
1231
|
+
total_tokens_wasted: totalTokensWasted,
|
|
1232
|
+
retry_tokens_wasted: retryTokensWasted,
|
|
1233
|
+
anti_pattern_tokens_wasted: antiPatternTokensWasted,
|
|
1234
|
+
breakdown,
|
|
1235
|
+
status,
|
|
1236
|
+
});
|
|
1155
1237
|
});
|
|
1156
|
-
routes.set('GET /api/audit', (
|
|
1238
|
+
routes.set('GET /api/audit', (req, res) => {
|
|
1157
1239
|
if (!deps.auditTrailManager)
|
|
1158
1240
|
return unavailable(res, 'auditTrailManager');
|
|
1159
|
-
|
|
1241
|
+
// Cap here too (not just via getAuditLog()'s own default) so a caller
|
|
1242
|
+
// can't force an unbounded read by passing an absurdly large `limit` —
|
|
1243
|
+
// the audit log is unpruned on disk, so a naive pass-through would let
|
|
1244
|
+
// a single request re-read the entire history.
|
|
1245
|
+
const url = new URL(req.url ?? '/', 'http://localhost');
|
|
1246
|
+
const limitStr = url.searchParams.get('limit') ?? '';
|
|
1247
|
+
let limit = 1000;
|
|
1248
|
+
const parsedLimit = parseInt(limitStr, 10);
|
|
1249
|
+
if (!Number.isNaN(parsedLimit)) {
|
|
1250
|
+
limit = Math.min(Math.max(parsedLimit, 1), 2000);
|
|
1251
|
+
}
|
|
1252
|
+
const log = deps.auditTrailManager.getAuditLog(limit);
|
|
1160
1253
|
jsonOk(res, log.map(toAuditEntry));
|
|
1161
1254
|
});
|
|
1162
1255
|
routes.set('GET /api/weekly', (req, res) => {
|
|
@@ -1250,8 +1343,21 @@ export function createApiHandler(deps) {
|
|
|
1250
1343
|
if (!Number.isNaN(parsedDays)) {
|
|
1251
1344
|
days = Math.min(Math.max(parsedDays, 1), 365);
|
|
1252
1345
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1346
|
+
// Anchor the window to local midnight `days` ago — matching
|
|
1347
|
+
// aggregateDailyCost's local-day grouping for the sibling "Daily Spend"
|
|
1348
|
+
// chart (History.tsx) — instead of a raw rolling instant, which doesn't
|
|
1349
|
+
// align with any calendar boundary and would draw a different
|
|
1350
|
+
// "last N days" line than that chart. loadAllSessions()'s own
|
|
1351
|
+
// pre-filter still compares against session-store.ts's UTC-derived
|
|
1352
|
+
// filename date, so `since` is widened by one extra day to guarantee it
|
|
1353
|
+
// never excludes a session that actually falls inside the local window;
|
|
1354
|
+
// the startTime check below applies the real, local-day-aligned cutoff.
|
|
1355
|
+
const windowStartMs = localStartOfDay() - (days - 1) * 86_400_000;
|
|
1356
|
+
const since = new Date(windowStartMs - 86_400_000);
|
|
1357
|
+
const sessions = deps.sessionStore.loadAllSessions({ since }).filter((s) => {
|
|
1358
|
+
const startTime = s.startTime;
|
|
1359
|
+
return startTime === undefined || startTime >= windowStartMs;
|
|
1360
|
+
});
|
|
1255
1361
|
jsonOk(res, attributeSessionCosts(sessions));
|
|
1256
1362
|
});
|
|
1257
1363
|
routes.set('GET /api/personal-coach', (_req, res) => {
|
|
@@ -1362,13 +1468,19 @@ export function createApiHandler(deps) {
|
|
|
1362
1468
|
// live tracker only.
|
|
1363
1469
|
const live = deps.qualityProxyTracker.getMetrics();
|
|
1364
1470
|
const ownSessionId = deps.sessionTracker?.getMetrics().sessionId;
|
|
1471
|
+
// Day-filter this process's own live contribution the same way GET
|
|
1472
|
+
// /api/tool-selection-score does just below — QualityProxyTracker has no
|
|
1473
|
+
// concept of "day" internally (events accumulate for the tracker's whole
|
|
1474
|
+
// process lifetime), so a --local dashboard daemon running since before
|
|
1475
|
+
// midnight would otherwise blend yesterday's (or older) signals into
|
|
1476
|
+
// "today's sessions" here, inconsistent with the correctly-scoped Tool
|
|
1477
|
+
// Selection panel right next to it.
|
|
1478
|
+
const startMs = localStartOfDay(Date.now());
|
|
1479
|
+
const liveRawCounts = rawQualityCountsFromEvents(live.events.filter((e) => e.timestamp >= startMs));
|
|
1365
1480
|
const persistedCounts = (deps.sessionStore?.loadTodaySessions() ?? [])
|
|
1366
1481
|
.filter((s) => s.sessionId !== ownSessionId)
|
|
1367
1482
|
.map((s) => s.qualityProxy ?? ZERO_QUALITY_PROXY_COUNTS);
|
|
1368
|
-
const combined = combineQualityProxyRawCounts([
|
|
1369
|
-
deps.qualityProxyTracker.getRawCounts(),
|
|
1370
|
-
...persistedCounts,
|
|
1371
|
-
]);
|
|
1483
|
+
const combined = combineQualityProxyRawCounts([liveRawCounts, ...persistedCounts]);
|
|
1372
1484
|
jsonOk(res, {
|
|
1373
1485
|
...combined,
|
|
1374
1486
|
qualityByTurnBucket: live.qualityByTurnBucket,
|
|
@@ -1506,15 +1618,15 @@ export function createApiHandler(deps) {
|
|
|
1506
1618
|
// Derive the headline "today peak" from the SAME buckets shown in the
|
|
1507
1619
|
// chart — rather than from an independent computation over a
|
|
1508
1620
|
// differently-scoped session set — so the two numbers can never
|
|
1509
|
-
// disagree by construction.
|
|
1510
|
-
//
|
|
1511
|
-
//
|
|
1621
|
+
// disagree by construction. Computing them from two different
|
|
1622
|
+
// concurrency models is exactly how the headline and chart can
|
|
1623
|
+
// silently drift apart.
|
|
1512
1624
|
//
|
|
1513
1625
|
// Deliberately NOT folded with `livePeak` here (unlike allTimePeak
|
|
1514
1626
|
// below): `livePeak` is a synthetic-id-unfiltered, never-reset
|
|
1515
1627
|
// lifetime max from LiveSessionRegistry, so mixing it into today's
|
|
1516
|
-
// headline can reintroduce
|
|
1517
|
-
//
|
|
1628
|
+
// headline can reintroduce that same chart/headline disagreement.
|
|
1629
|
+
// It's unnecessary anyway:
|
|
1518
1630
|
// `windows` already unions each live session's buffered (not-yet-
|
|
1519
1631
|
// persisted) activity into the same buckets, so a currently-live
|
|
1520
1632
|
// session with any tool-call activity is already reflected here.
|
|
@@ -1538,6 +1650,15 @@ export function createApiHandler(deps) {
|
|
|
1538
1650
|
const url = new URL(req.url ?? '/', 'http://localhost');
|
|
1539
1651
|
const view = url.searchParams.get('view') ?? 'today';
|
|
1540
1652
|
if (view === 'today') {
|
|
1653
|
+
// localStartOfDay() draws "today" using this dashboard *server*
|
|
1654
|
+
// process's own OS/Intl timezone. The browser calls the same helper
|
|
1655
|
+
// client-side (src/lib/date.ts) for its own "today" filtering, so
|
|
1656
|
+
// this endpoint is only guaranteed to agree with the client when
|
|
1657
|
+
// server and browser share a timezone — the common single-machine
|
|
1658
|
+
// `--local`/`--stdio` deployment. A cloud-hosted or containerized
|
|
1659
|
+
// dashboard viewed from a different timezone can see this bucket
|
|
1660
|
+
// range anchored to the wrong midnight; neither side is ever
|
|
1661
|
+
// told the other's offset today.
|
|
1541
1662
|
const now = Date.now();
|
|
1542
1663
|
const startMs = localStartOfDay(now);
|
|
1543
1664
|
const bucketSizeMs = 900_000;
|
|
@@ -1581,25 +1702,52 @@ export function createApiHandler(deps) {
|
|
|
1581
1702
|
if (view === 'history') {
|
|
1582
1703
|
const weeksParam = url.searchParams.get('weeks');
|
|
1583
1704
|
const weeks = weeksParam ? Math.min(Math.max(parseInt(weeksParam, 10) || 12, 1), 52) : 12;
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1705
|
+
// Local (not UTC) day boundaries — see localStartOfDay's doc comment
|
|
1706
|
+
// and computeDailyPeakConcurrency's identical handling just above. A
|
|
1707
|
+
// developer's evening session commonly straddles UTC midnight, which
|
|
1708
|
+
// would otherwise land that activity on the wrong calendar day for
|
|
1709
|
+
// anyone not in UTC.
|
|
1710
|
+
const todayStart = localStartOfDay();
|
|
1711
|
+
const startDate = new Date(todayStart);
|
|
1712
|
+
startDate.setDate(startDate.getDate() - weeks * 7);
|
|
1588
1713
|
const sessions = deps.sessionStore?.loadAllSessions?.({ since: startDate }) ?? [];
|
|
1589
1714
|
const dayMap = new Map();
|
|
1590
1715
|
const cursor = new Date(startDate);
|
|
1591
|
-
while (cursor <=
|
|
1592
|
-
dayMap.set(cursor.
|
|
1593
|
-
cursor.
|
|
1716
|
+
while (cursor.getTime() <= todayStart) {
|
|
1717
|
+
dayMap.set(localDateKey(cursor.getTime()), 0);
|
|
1718
|
+
cursor.setDate(cursor.getDate() + 1);
|
|
1594
1719
|
}
|
|
1720
|
+
// Bucket by each timeline entry's own timestamp, mirroring the
|
|
1721
|
+
// `view=today` branch above — not by attributing a session's entire
|
|
1722
|
+
// toolCallCount to the single day it *started* on. A session whose
|
|
1723
|
+
// activity spans a day boundary would otherwise show a false spike
|
|
1724
|
+
// on the start day and a false gap on the day(s) it actually
|
|
1725
|
+
// continued into.
|
|
1726
|
+
//
|
|
1727
|
+
// `timeline` is optional and was only added to persisted sessions
|
|
1728
|
+
// ~2026-06-02 (session-store.ts) — this route's default 12-week
|
|
1729
|
+
// window still reaches sessions saved before that. A session with no
|
|
1730
|
+
// timeline falls back to the old, still local-day-keyed,
|
|
1731
|
+
// start-day/toolCallCount attribution so that older history doesn't
|
|
1732
|
+
// silently drop to zero; only sessions that *do* have a timeline get
|
|
1733
|
+
// the per-entry walk.
|
|
1734
|
+
const startMs = startDate.getTime();
|
|
1595
1735
|
for (const s of sessions) {
|
|
1596
1736
|
const session = s;
|
|
1597
|
-
if (
|
|
1737
|
+
if (session.timeline) {
|
|
1738
|
+
for (const entry of session.timeline) {
|
|
1739
|
+
if (entry.timestamp < startMs)
|
|
1740
|
+
continue;
|
|
1741
|
+
const key = localDateKey(entry.timestamp);
|
|
1742
|
+
if (dayMap.has(key)) {
|
|
1743
|
+
dayMap.set(key, (dayMap.get(key) ?? 0) + 1);
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1598
1746
|
continue;
|
|
1599
|
-
|
|
1600
|
-
if (
|
|
1747
|
+
}
|
|
1748
|
+
if (!session.startTime || session.startTime < startMs)
|
|
1601
1749
|
continue;
|
|
1602
|
-
const key =
|
|
1750
|
+
const key = localDateKey(session.startTime);
|
|
1603
1751
|
if (dayMap.has(key)) {
|
|
1604
1752
|
dayMap.set(key, (dayMap.get(key) ?? 0) + (session.toolCallCount ?? 0));
|
|
1605
1753
|
}
|
|
@@ -1620,7 +1768,14 @@ export function createApiHandler(deps) {
|
|
|
1620
1768
|
routes.set('GET /api/git-efficiency/repos', (_req, res) => {
|
|
1621
1769
|
if (!deps.sessionStore)
|
|
1622
1770
|
return unavailable(res, 'sessionStore');
|
|
1623
|
-
|
|
1771
|
+
// Prefer loadSessionsOverlappingToday() so a session that started
|
|
1772
|
+
// yesterday and crossed into today isn't invisible from these pills —
|
|
1773
|
+
// loadTodaySessions() filters by filename date (= start date) and would
|
|
1774
|
+
// otherwise silently drop it, even though its git activity still counts
|
|
1775
|
+
// toward the KPIs shown alongside these pills — same reasoning as the
|
|
1776
|
+
// day-boundary hydration in src/index.ts.
|
|
1777
|
+
const todaySessions = (deps.sessionStore.loadSessionsOverlappingToday?.() ??
|
|
1778
|
+
deps.sessionStore.loadTodaySessions());
|
|
1624
1779
|
const repoSet = new Set();
|
|
1625
1780
|
for (const session of todaySessions) {
|
|
1626
1781
|
if (typeof session.repoName === 'string' && session.repoName) {
|
|
@@ -1992,6 +2147,13 @@ export function createApiHandler(deps) {
|
|
|
1992
2147
|
const responseBody = { ...session };
|
|
1993
2148
|
if (quality.totalSignals > 0)
|
|
1994
2149
|
responseBody.qualityProxy = quality;
|
|
2150
|
+
// `session.timeline` is append-only in processing order, not
|
|
2151
|
+
// timestamp order (parallel tool calls can complete out of
|
|
2152
|
+
// start-order) — sort before returning, mirroring the identical
|
|
2153
|
+
// fix already applied to buildReplayResponse()'s persisted branch.
|
|
2154
|
+
if (Array.isArray(session.timeline)) {
|
|
2155
|
+
responseBody.timeline = [...session.timeline].sort((a, b) => a.timestamp - b.timestamp);
|
|
2156
|
+
}
|
|
1995
2157
|
jsonOk(res, responseBody);
|
|
1996
2158
|
return;
|
|
1997
2159
|
}
|
|
@@ -2031,13 +2193,17 @@ export function createApiHandler(deps) {
|
|
|
2031
2193
|
: undefined,
|
|
2032
2194
|
// Use the same `timeline` shape as persisted sessions so the
|
|
2033
2195
|
// Sessions and Replay views can consume one type. See
|
|
2034
|
-
// src/storage/types.ts ReplayTimelineEntry.
|
|
2035
|
-
|
|
2196
|
+
// src/storage/types.ts ReplayTimelineEntry. `toolCallTimeline`
|
|
2197
|
+
// is append-only in processing order, not timestamp order —
|
|
2198
|
+
// sort before returning, mirroring buildReplayResponse().
|
|
2199
|
+
timeline: live.toolCallTimeline
|
|
2200
|
+
.map((t) => ({
|
|
2036
2201
|
timestamp: t.timestamp,
|
|
2037
2202
|
toolName: t.toolName,
|
|
2038
2203
|
durationMs: t.durationMs,
|
|
2039
2204
|
success: t.success ?? true,
|
|
2040
|
-
}))
|
|
2205
|
+
}))
|
|
2206
|
+
.sort((a, b) => a.timestamp - b.timestamp),
|
|
2041
2207
|
});
|
|
2042
2208
|
return;
|
|
2043
2209
|
}
|