diff --git a/scripts/src/hwlab-node-web-observe-runner-source.ts b/scripts/src/hwlab-node-web-observe-runner-source.ts index a386b6c9..154886ac 100644 --- a/scripts/src/hwlab-node-web-observe-runner-source.ts +++ b/scripts/src/hwlab-node-web-observe-runner-source.ts @@ -3398,6 +3398,11 @@ function buildRuntimeAlerts(samples, control, network, consoleEvents, errors) { .map((item) => Date.parse(item.ts)) .filter(Number.isFinite) .sort((a, b) => a - b); + const observerRefreshTimes = control + .filter((item) => item.type === "observer-periodic-refresh") + .map((item) => Date.parse(item.ts)) + .filter(Number.isFinite) + .sort((a, b) => a - b); const naturalNetwork = network.filter((item) => item?.observerInitiated !== true); const httpErrors = naturalNetwork .filter((item) => item?.type === "response" && Number(item.status) >= 400) @@ -3405,7 +3410,9 @@ function buildRuntimeAlerts(samples, control, network, consoleEvents, errors) { const requestFailed = naturalNetwork .filter((item) => item?.type === "requestfailed") .map((item) => networkAlertEvent(item, promptTimes)); - const significantRequestFailed = requestFailed.filter((item) => !isBenignLongLivedStreamClosureAlert(item)); + const significantRequestFailed = requestFailed.filter( + (item) => !isBenignLongLivedStreamClosureAlert(item) && !isObserverRefreshClosureAlert(item, observerRefreshTimes), + ); const domDiagnostics = []; const executionErrors = []; const baselineExecutionErrors = []; @@ -3789,6 +3796,15 @@ function isBenignLongLivedStreamClosureAlert(event) { return /ERR_NETWORK_CHANGED|ERR_ABORTED|net::ERR_NETWORK_CHANGED|net::ERR_ABORTED|aborted|network changed/iu.test(text); } +function isObserverRefreshClosureAlert(event, observerRefreshTimes) { + if (!["/v1/workbench/events", "/v1/web-performance"].includes(String(event?.urlPath || ""))) return false; + if (event.status !== null && event.status !== undefined && Number(event.status) > 0) return false; + const text = String(event.failureKind || event.errorPreview || event.preview || ""); + if (!/ERR_NETWORK_CHANGED|ERR_ABORTED|net::ERR_NETWORK_CHANGED|net::ERR_ABORTED|aborted|network changed/iu.test(text)) return false; + const ts = Date.parse(String(event.ts || "")); + return Number.isFinite(ts) && observerRefreshTimes.some((refreshTs) => Math.abs(ts - refreshTs) <= 8000); +} + function networkAlertEvent(item, promptTimes) { const failureText = item.failureKind ?? item.failure ?? item.errorText ?? null; return { @@ -5176,8 +5192,8 @@ function buildCodeAgentCardTimingMetrics(samples, timeline, turnTiming) { const timelineItem = timeline[index] || {}; for (const card of codeAgentCardsForSample(sample)) { const text = codeAgentCardText(card); - const totalElapsedValues = parseTotalElapsedSeconds(text).filter(Number.isFinite); - const recentUpdateValues = parseRecentUpdateSeconds(text).filter(Number.isFinite); + const totalElapsedValues = parseTotalElapsedSeconds(card?.durationText).filter(Number.isFinite); + const recentUpdateValues = parseRecentUpdateSeconds(card?.activityText).filter(Number.isFinite); const terminal = isCodeAgentCardTerminal(card); const row = { ...ref(sample), @@ -5427,7 +5443,7 @@ function detectPostCompletionTimingChanges(turnTiming, event) { if (!cell) continue; if (event.traceId && cell.traceId && cell.traceId !== event.traceId) continue; if (event.messageId && cell.messageId && cell.messageId !== event.messageId) continue; - const total = Number(cell.totalElapsedSeconds); + const total = cell.totalElapsedSeconds === null || cell.totalElapsedSeconds === undefined ? NaN : Number(cell.totalElapsedSeconds); if (Number.isFinite(total)) { if (previousTotal && Math.abs(total - previousTotal.value) > alertThresholds.turnTimingSampleSlackSeconds) { timingChanges.push({ @@ -5450,7 +5466,7 @@ function detectPostCompletionTimingChanges(turnTiming, event) { } previousTotal = { value: total, seq: row.seq ?? null, ts: row.ts ?? null }; } - const recent = Number(cell.recentUpdateSeconds); + const recent = cell.recentUpdateSeconds === null || cell.recentUpdateSeconds === undefined ? NaN : Number(cell.recentUpdateSeconds); if (Number.isFinite(recent)) { recentUpdateVisible.push({ ...eventRef(event),