// SPEC: PJ2026-01040111 long-running Workbench observation. // Responsibility: Analyzer recent-window, page provenance, and Navigation Timing API performance source fragment. export function nodeWebObserveAnalyzerWindowPageSource(): string { return String.raw`async function buildRecentAnalysisWindow({ samples, control, network, consoleEvents, errors, artifacts, browserProcessRows, performanceRows, manifest }) { const latestSampleMs = latestTimestampMs(samples); const windowMs = 5 * 60 * 1000; const fromMs = Number.isFinite(latestSampleMs) ? latestSampleMs - windowMs : Number.NEGATIVE_INFINITY; const toMs = Number.isFinite(latestSampleMs) ? latestSampleMs : Number.POSITIVE_INFINITY; const inWindow = (item) => { const tsMs = Date.parse(item?.ts); return Number.isFinite(tsMs) && tsMs >= fromMs && tsMs <= toMs; }; const windowSamples = samples.filter(inWindow); const windowControl = control.filter(inWindow); const windowNetwork = network.filter(inWindow); const windowConsole = consoleEvents.filter(inWindow); const windowErrors = errors.filter(inWindow); const windowArtifacts = (artifacts || []).filter(inWindow); const windowBrowserProcessRows = (browserProcessRows || []).filter(inWindow); const windowPerformanceRows = (performanceRows || []).filter(inWindow); const sampleMetrics = buildSampleMetrics(windowSamples, control); const pageProvenance = buildPageProvenanceReport(windowSamples, windowControl, manifest); const pagePerformance = buildPagePerformanceReport(windowSamples, manifest); const requestRate = buildRequestRateReport(windowNetwork); const promptNetwork = buildPromptNetworkReport(windowControl, windowNetwork); const runtimeAlerts = buildRuntimeAlerts(windowSamples, control, windowNetwork, windowConsole, windowErrors); const apiDomLag = buildApiDomLagReport(windowSamples, windowNetwork); const browserProcess = buildBrowserProcessReport(windowBrowserProcessRows); const frontendPerformance = await buildFrontendPerformanceReport(windowPerformanceRows, windowArtifacts, windowSamples, windowNetwork); const findings = buildFindings(windowSamples, control, windowNetwork, windowErrors, sampleMetrics, promptNetwork, runtimeAlerts, pagePerformance, requestRate, pageProvenance, [], {}, apiDomLag, browserProcess, frontendPerformance); return { summary: { name: "recent-5m", windowMs, fromAt: Number.isFinite(fromMs) ? new Date(fromMs).toISOString() : null, toAt: Number.isFinite(toMs) ? new Date(toMs).toISOString() : null, samples: windowSamples.length, control: windowControl.length, network: windowNetwork.length, console: windowConsole.length, errors: windowErrors.length, artifacts: windowArtifacts.length, browserProcess: windowBrowserProcessRows.length, performance: windowPerformanceRows.length, valuesRedacted: true }, sampleMetrics, pageProvenance, pagePerformance, requestRate, promptNetwork, runtimeAlerts, apiDomLag, browserProcess, frontendPerformance, findings, valuesRedacted: true }; } function latestTimestampMs(items) { let latest = Number.NEGATIVE_INFINITY; for (const item of items || []) { const tsMs = Date.parse(item?.ts); if (Number.isFinite(tsMs) && tsMs > latest) latest = tsMs; } return latest; } function buildPageProvenanceReport(samples, control, manifest) { const groups = new Map(); for (const sample of samples) { const provenance = sample?.pageProvenance; if (!provenance) continue; const key = provenance.assetFingerprint || "unknown"; const group = groups.get(key) || { assetFingerprint: provenance.assetFingerprint || null, pageLoadSeqs: [], sampleCount: 0, firstSeq: sample.seq ?? null, lastSeq: sample.seq ?? null, firstAt: sample.ts ?? null, lastAt: sample.ts ?? null, urlPaths: [], scriptCount: provenance.scriptCount ?? null, stylesheetCount: provenance.stylesheetCount ?? null, metaCount: provenance.metaCount ?? null, scripts: Array.isArray(provenance.scripts) ? provenance.scripts.slice(0, 12) : [], stylesheets: Array.isArray(provenance.stylesheets) ? provenance.stylesheets.slice(0, 12) : [], valuesRedacted: true }; group.sampleCount += 1; group.lastSeq = sample.seq ?? null; group.lastAt = sample.ts ?? null; if (provenance.pageLoadSeq !== null && provenance.pageLoadSeq !== undefined && !group.pageLoadSeqs.includes(provenance.pageLoadSeq)) group.pageLoadSeqs.push(provenance.pageLoadSeq); if (provenance.urlPath && !group.urlPaths.includes(provenance.urlPath)) group.urlPaths.push(provenance.urlPath); groups.set(key, group); } const segments = Array.from(groups.values()).sort((a, b) => Number(a.firstSeq ?? 0) - Number(b.firstSeq ?? 0)); const controlSegments = control .filter((item) => item.type === "page-provenance" || item?.pageProvenance) .map((item) => ({ ts: item.ts ?? null, reason: item.reason ?? item.detail?.reason ?? null, httpStatus: item.httpStatus ?? item.detail?.httpStatus ?? null, pageProvenance: item.pageProvenance ?? item.detail?.pageProvenance ?? null, })) .slice(0, 80); return { summary: { segmentCount: segments.length, sampleCount: segments.reduce((sum, item) => sum + item.sampleCount, 0), manifestFingerprint: manifest?.pageProvenance?.assetFingerprint ?? null, controlSegmentCount: controlSegments.length }, segments, controlSegments, valuesRedacted: true }; } function buildPagePerformanceReport(samples, manifest) { const base = manifest?.baseUrl || "http://invalid.local"; const seen = new Set(); const groups = new Map(); const sampleTimes = samples.map((sample) => Date.parse(sample?.ts || "")).filter(Number.isFinite); const windowStartMs = sampleTimes.length > 0 ? Math.min(...sampleTimes) : null; const windowEndMs = sampleTimes.length > 0 ? Math.max(...sampleTimes) : null; for (const sample of samples) { const entries = Array.isArray(sample?.performance) ? sample.performance : []; for (const entry of entries) { const durationMs = Number(entry?.duration); if (!Number.isFinite(durationMs) || durationMs < 0) continue; const entryCompletedMs = performanceEntryCompletedEpochMs(sample, entry); if (windowStartMs !== null && entryCompletedMs !== null && entryCompletedMs < windowStartMs) continue; if (windowEndMs !== null && entryCompletedMs !== null && entryCompletedMs > windowEndMs + 1000) continue; const entryTs = entryCompletedMs === null ? (sample.ts ?? null) : new Date(entryCompletedMs).toISOString(); const parsed = parsePerformanceUrl(entry?.name, base); if (!parsed.sameOrigin || !isApiLikePath(parsed.path)) continue; const normalizedPath = normalizeApiPath(parsed.path); const routeKind = classifyApiPerformanceRoute(normalizedPath, entry); const isLongLivedStream = routeKind === "same-origin-api-stream"; const streamOpenMs = streamOpenLatencyMs(entry); const timingStatus = resourceTimingPhaseStatus(entry); const dedupeKey = [parsed.path, entry.initiatorType || "", sample?.pageProvenance?.pageLoadSeq ?? "", sample?.pageProvenance?.timeOrigin ?? "", entry.startTime ?? "", Math.round(durationMs)].join("|"); if (seen.has(dedupeKey)) continue; seen.add(dedupeKey); const group = groups.get(normalizedPath) || { routeKind, path: normalizedPath, isLongLivedStream, budgetMetric: isLongLivedStream ? "streamOpenMs" : "durationMs", rawPathSamples: [], sampleCount: 0, completeTimingSampleCount: 0, partialTimingSampleCount: 0, durationsMs: [], streamOpenDurationsMs: [], overFiveSecondCount: 0, overBudgetCount: 0, partialOverFiveSecondCount: 0, partialOverBudgetCount: 0, streamLifetimeOverFiveSecondCount: 0, streamOpenOverFiveSecondCount: 0, streamOpenOverBudgetCount: 0, firstAt: entryTs, lastAt: entryTs, firstSeq: sample.seq ?? null, lastSeq: sample.seq ?? null, initiatorTypes: [], pageAssetFingerprints: [], slowSamples: [], partialSamples: [], valuesRedacted: true }; group.sampleCount += 1; const partialOrdinaryTiming = !isLongLivedStream && timingStatus.status !== "complete"; let overBudget = false; if (partialOrdinaryTiming) { group.partialTimingSampleCount += 1; if (durationMs > 5000) group.partialOverFiveSecondCount += 1; if (durationMs > alertThresholds.partialApiSlowMs) { group.partialOverBudgetCount += 1; if (group.partialSamples.length < 80) group.partialSamples.push(compactPagePerformanceSlowSample({ sample, entry, entryTs, normalizedPath, rawPath: parsed.path, durationMs, streamOpenMs })); } } else { group.completeTimingSampleCount += 1; group.durationsMs.push(durationMs); if (isLongLivedStream) { if (durationMs > 5000) group.streamLifetimeOverFiveSecondCount += 1; if (streamOpenMs !== null) { group.streamOpenDurationsMs.push(streamOpenMs); if (streamOpenMs > 5000) { group.streamOpenOverFiveSecondCount += 1; group.overFiveSecondCount += 1; } if (streamOpenMs > alertThresholds.longLivedStreamOpenSlowMs) { group.streamOpenOverBudgetCount += 1; group.overBudgetCount += 1; overBudget = true; } } } else { if (durationMs > 5000) group.overFiveSecondCount += 1; if (durationMs > alertThresholds.sameOriginApiSlowMs) { group.overBudgetCount += 1; overBudget = true; } } } if (overBudget && group.slowSamples.length < 80) group.slowSamples.push(compactPagePerformanceSlowSample({ sample, entry, entryTs, normalizedPath, rawPath: parsed.path, durationMs, streamOpenMs })); group.lastAt = entryTs; group.lastSeq = sample.seq ?? null; if (parsed.path && !group.rawPathSamples.includes(parsed.path)) group.rawPathSamples.push(parsed.path); if (entry.initiatorType && !group.initiatorTypes.includes(entry.initiatorType)) group.initiatorTypes.push(entry.initiatorType); const assetFingerprint = sample?.pageProvenance?.assetFingerprint; if (assetFingerprint && !group.pageAssetFingerprints.includes(assetFingerprint)) group.pageAssetFingerprints.push(assetFingerprint); groups.set(normalizedPath, group); } } const sameOriginApiByPath = Array.from(groups.values()).map((group) => { const durations = group.durationsMs.slice().sort((a, b) => a - b); const streamOpenDurations = group.streamOpenDurationsMs.slice().sort((a, b) => a - b); return { routeKind: group.routeKind, path: group.path, isLongLivedStream: group.isLongLivedStream === true, budgetMetric: group.budgetMetric, sampleCount: group.sampleCount, budgetMs: group.isLongLivedStream === true ? alertThresholds.longLivedStreamOpenSlowMs : alertThresholds.sameOriginApiSlowMs, partialBudgetMs: alertThresholds.partialApiSlowMs, streamOpenBudgetMs: alertThresholds.longLivedStreamOpenSlowMs, completeTimingSampleCount: group.completeTimingSampleCount, partialTimingSampleCount: group.partialTimingSampleCount, p50Ms: percentile(durations, 50), p75Ms: percentile(durations, 75), p95Ms: percentile(durations, 95), maxMs: durations.length > 0 ? durations[durations.length - 1] : null, streamOpenSampleCount: streamOpenDurations.length, streamOpenP50Ms: percentile(streamOpenDurations, 50), streamOpenP75Ms: percentile(streamOpenDurations, 75), streamOpenP95Ms: percentile(streamOpenDurations, 95), streamOpenMaxMs: streamOpenDurations.length > 0 ? streamOpenDurations[streamOpenDurations.length - 1] : null, streamOpenOverFiveSecondCount: group.streamOpenOverFiveSecondCount, streamOpenOverBudgetCount: group.streamOpenOverBudgetCount, streamLifetimeOverFiveSecondCount: group.streamLifetimeOverFiveSecondCount, overFiveSecondCount: group.overFiveSecondCount, overBudgetCount: group.overBudgetCount, partialOverFiveSecondCount: group.partialOverFiveSecondCount, partialOverBudgetCount: group.partialOverBudgetCount, overFiveSecondRatio: group.sampleCount > 0 ? Number((group.overFiveSecondCount / group.sampleCount).toFixed(3)) : 0, overBudgetRatio: group.sampleCount > 0 ? Number((group.overBudgetCount / group.sampleCount).toFixed(3)) : 0, firstAt: group.firstAt, lastAt: group.lastAt, firstSeq: group.firstSeq, lastSeq: group.lastSeq, initiatorTypes: group.initiatorTypes, rawPathSamples: group.rawPathSamples.slice(0, 8), pageAssetFingerprints: group.pageAssetFingerprints.slice(0, 8), slowSamples: group.slowSamples .slice() .sort((a, b) => Number(b.durationMs ?? 0) - Number(a.durationMs ?? 0)) .slice(0, 12), partialSamples: group.partialSamples .slice() .sort((a, b) => Number(b.durationMs ?? 0) - Number(a.durationMs ?? 0)) .slice(0, 12), valuesRedacted: true }; }).sort((a, b) => (Number(b.overBudgetCount ?? b.overFiveSecondCount ?? 0) - Number(a.overBudgetCount ?? a.overFiveSecondCount ?? 0)) || (Number(b.p95Ms ?? 0) - Number(a.p95Ms ?? 0)) || a.path.localeCompare(b.path)); const longLivedStreams = sameOriginApiByPath.filter((item) => item.isLongLivedStream); const ordinaryApi = sameOriginApiByPath.filter((item) => item.isLongLivedStream !== true); const slow = ordinaryApi.filter((item) => Number(item.overBudgetCount ?? item.overFiveSecondCount ?? 0) > 0); const slowFiveSecond = ordinaryApi.filter((item) => Number(item.overFiveSecondCount ?? 0) > 0); const partialSlow = ordinaryApi.filter((item) => Number(item.partialOverBudgetCount ?? item.partialOverFiveSecondCount ?? 0) > 0); const partialFiveSecond = ordinaryApi.filter((item) => Number(item.partialOverFiveSecondCount ?? 0) > 0); const slowStreamOpen = longLivedStreams.filter((item) => Number(item.streamOpenOverBudgetCount ?? item.streamOpenOverFiveSecondCount ?? 0) > 0); const slowStreamOpenFiveSecond = longLivedStreams.filter((item) => Number(item.streamOpenOverFiveSecondCount ?? 0) > 0); const budgetP95Values = sameOriginApiByPath .map((item) => Number(item.isLongLivedStream ? (item.streamOpenP95Ms ?? 0) : (item.p95Ms ?? 0))) .filter((value) => Number.isFinite(value)); return { summary: { budgetMs: alertThresholds.sameOriginApiSlowMs, alertThresholds, sameOriginApiPathCount: sameOriginApiByPath.length, sameOriginApiSampleCount: sameOriginApiByPath.reduce((sum, item) => sum + item.sampleCount, 0), longLivedStreamPathCount: longLivedStreams.length, longLivedStreamSampleCount: longLivedStreams.reduce((sum, item) => sum + item.sampleCount, 0), longLivedStreamOpenOverFiveSecondPathCount: slowStreamOpenFiveSecond.length, longLivedStreamOpenOverFiveSecondSampleCount: slowStreamOpenFiveSecond.reduce((sum, item) => sum + Number(item.streamOpenOverFiveSecondCount ?? 0), 0), longLivedStreamOpenOverBudgetPathCount: slowStreamOpen.length, longLivedStreamOpenOverBudgetSampleCount: slowStreamOpen.reduce((sum, item) => sum + Number(item.streamOpenOverBudgetCount ?? item.streamOpenOverFiveSecondCount ?? 0), 0), longLivedStreamLifetimeOverFiveSecondSampleCount: longLivedStreams.reduce((sum, item) => sum + Number(item.streamLifetimeOverFiveSecondCount ?? 0), 0), slowPathCount: slow.length, slowSampleCount: slow.reduce((sum, item) => sum + Number(item.overBudgetCount ?? item.overFiveSecondCount ?? 0), 0), overFiveSecondPathCount: slowFiveSecond.length, overFiveSecondSampleCount: slowFiveSecond.reduce((sum, item) => sum + Number(item.overFiveSecondCount ?? 0), 0), partialTimingSampleCount: ordinaryApi.reduce((sum, item) => sum + Number(item.partialTimingSampleCount ?? 0), 0), partialOverFiveSecondPathCount: partialFiveSecond.length, partialOverFiveSecondSampleCount: partialFiveSecond.reduce((sum, item) => sum + Number(item.partialOverFiveSecondCount ?? 0), 0), partialOverBudgetPathCount: partialSlow.length, partialOverBudgetSampleCount: partialSlow.reduce((sum, item) => sum + Number(item.partialOverBudgetCount ?? item.partialOverFiveSecondCount ?? 0), 0), worstP95Ms: budgetP95Values.length > 0 ? Math.max(...budgetP95Values) : null, valuesRedacted: true }, sameOriginApiByPath, valuesRedacted: true }; } function performanceEntryCompletedEpochMs(sample, entry) { const origin = Number(sample?.pageProvenance?.timeOrigin); const responseEnd = Number(entry?.responseEnd); const startTime = Number(entry?.startTime); const offset = Number.isFinite(responseEnd) && responseEnd > 0 ? responseEnd : startTime; if (Number.isFinite(origin) && origin > 0 && Number.isFinite(offset) && offset >= 0) return Math.round(origin + offset); const sampleTs = Date.parse(sample?.ts || ""); return Number.isFinite(sampleTs) ? sampleTs : null; } function compactPagePerformanceSlowSample({ sample, entry, entryTs, normalizedPath, rawPath, durationMs, streamOpenMs }) { const timingStatus = resourceTimingPhaseStatus(entry); const serverTiming = compactServerTiming(entry?.serverTiming); return { ts: entryTs ?? sample?.ts ?? null, sampleTs: sample?.ts ?? null, seq: sample?.seq ?? null, path: normalizedPath ?? null, rawPath: rawPath ?? null, initiatorType: entry?.initiatorType ?? null, durationMs: roundFinite(durationMs), startTimeMs: roundFinite(entry?.startTime), fetchStartMs: roundFinite(entry?.fetchStart), requestStartMs: roundFinite(entry?.requestStart), responseStartMs: roundFinite(entry?.responseStart), responseEndMs: roundFinite(entry?.responseEnd), streamOpenMs: roundFinite(streamOpenMs), dnsMs: phaseDeltaMs(entry, "domainLookupEnd", "domainLookupStart"), tcpMs: phaseDeltaMs(entry, "connectEnd", "connectStart"), tlsStartMs: roundFinite(entry?.secureConnectionStart), requestToResponseStartMs: phaseDeltaMs(entry, "responseStart", "requestStart"), responseTransferMs: phaseDeltaMs(entry, "responseEnd", "responseStart"), timingStatus: timingStatus.status, invalidTimingPhases: timingStatus.invalidPhases, partialTimingPhases: timingStatus.partialPhases, transferSize: Number.isFinite(Number(entry?.transferSize)) ? Number(entry.transferSize) : null, encodedBodySize: Number.isFinite(Number(entry?.encodedBodySize)) ? Number(entry.encodedBodySize) : null, decodedBodySize: Number.isFinite(Number(entry?.decodedBodySize)) ? Number(entry.decodedBodySize) : null, nextHopProtocol: entry?.nextHopProtocol ?? null, serverTiming, serverTimingNames: serverTiming.map((item) => item.name).filter(Boolean).slice(0, 8), otelTraceId: extractOtelTraceIdFromServerTiming(serverTiming), valuesRedacted: true }; } function phaseDeltaMs(entry, endKey, startKey) { const end = Number(entry?.[endKey]); const start = Number(entry?.[startKey]); if (!Number.isFinite(end) || !Number.isFinite(start) || end <= 0 || start <= 0 || end < start) return null; return Math.round(end - start); } function resourceTimingPhaseStatus(entry) { const pairs = [ ["requestToResponseStart", "requestStart", "responseStart"], ["responseTransfer", "responseStart", "responseEnd"], ]; const invalidPhases = []; const partialPhases = []; for (const [label, startKey, endKey] of pairs) { const start = Number(entry?.[startKey]); const end = Number(entry?.[endKey]); if (!Number.isFinite(start) || !Number.isFinite(end) || start <= 0 || end <= 0) { partialPhases.push(label); } else if (end < start) { invalidPhases.push(label); } } return { status: invalidPhases.length > 0 ? "invalid" : (partialPhases.length > 0 ? "partial" : "complete"), invalidPhases, partialPhases, }; } function compactServerTiming(value) { const items = Array.isArray(value) ? value : []; return items.slice(0, 8).map((item) => ({ name: truncate(String(item?.name || ""), 80), duration: Number.isFinite(Number(item?.duration)) ? Math.round(Number(item.duration)) : null, description: truncate(String(item?.description || ""), 120), })).filter((item) => item.name || item.description || item.duration !== null); } function extractOtelTraceIdFromServerTiming(items) { const text = (Array.isArray(items) ? items : []).map((item) => [item.name, item.description].filter(Boolean).join(" ")).join(" "); const match = text.match(/\b[0-9a-f]{32}\b/iu); return match ? match[0].toLowerCase() : null; } function roundFinite(value) { const numeric = Number(value); return Number.isFinite(numeric) ? Math.round(numeric) : null; } function classifyApiPerformanceRoute(normalizedPath, entry = {}) { if (normalizedPath === "/v1/workbench/events") return "same-origin-api-stream"; if (String(entry?.initiatorType ?? "").toLowerCase() === "eventsource") return "same-origin-api-stream"; return "same-origin-api"; } function streamOpenLatencyMs(entry = {}) { const responseStart = Number(entry?.responseStart); const startTime = Number(entry?.startTime); if (!Number.isFinite(responseStart) || responseStart <= 0) return null; if (!Number.isFinite(startTime) || startTime < 0) return Math.max(0, responseStart); if (responseStart < startTime) return null; return Math.max(0, responseStart - startTime); } function parsePerformanceUrl(value, base) { try { const url = new URL(String(value || ""), base); const origin = new URL(String(base || "http://invalid.local")).origin; return { sameOrigin: url.origin === origin, path: url.pathname }; } catch { return { sameOrigin: false, path: "-" }; } } function isApiLikePath(path) { return /^\/(?:v1(?:\/|$)|auth(?:\/|$)|health(?:\/|$))/u.test(String(path || "")); } function normalizeApiPath(path) { return String(path || "-") .replace(/\/v1\/workbench\/sessions\/ses_[^/]+/gu, "/v1/workbench/sessions/:id") .replace(/\/v1\/workbench\/turns\/trc_[^/]+/gu, "/v1/workbench/turns/:traceId") .replace(/\/v1\/workbench\/traces\/trc_[^/]+/gu, "/v1/workbench/traces/:traceId") .replace(/\/v1\/workbench\/sessions\/[0-9a-f-]{12,}/giu, "/v1/workbench/sessions/:id") .replace(/\/v1\/[^/]+\/[0-9a-f-]{16,}(?=\/|$)/giu, (match) => match.replace(/\/[0-9a-f-]{16,}$/iu, "/:id")); } function percentile(sortedValues, percentileValue) { if (!Array.isArray(sortedValues) || sortedValues.length === 0) return null; if (sortedValues.length === 1) return Math.round(sortedValues[0]); const rank = (percentileValue / 100) * (sortedValues.length - 1); const lower = Math.floor(rank); const upper = Math.ceil(rank); if (lower === upper) return Math.round(sortedValues[lower]); const weight = rank - lower; return Math.round(sortedValues[lower] * (1 - weight) + sortedValues[upper] * weight); } `; }