// SPEC: PJ2026-01040111 long-running Workbench observation. // Responsibility: Analyzer API-to-DOM lag and trace-events page-read source fragment. export function nodeWebObserveAnalyzerApiDomLagSource(): string { return String.raw`function buildApiDomLagReport(samples, network) { const windowMs = 30_000; const budgetMs = Number.isFinite(Number(alertThresholds.sameOriginApiSlowMs)) ? Number(alertThresholds.sameOriginApiSlowMs) : 10_000; const sampleRows = (Array.isArray(samples) ? samples : []) .map((sample) => { const tsMs = timestampMs(sample?.ts); return { sample, tsMs, pageKey: samplePageKey(sample), digest: digestSample(sample), sessionIds: new Set([sample?.routeSessionId, sample?.activeSessionId].filter(Boolean).map(String)), traceIds: sampleTraceIds(sample) }; }) .filter((item) => Number.isFinite(item.tsMs)) .sort((a, b) => a.tsMs - b.tsMs); const samplesByPage = new Map(); for (const row of sampleRows) { const rows = samplesByPage.get(row.pageKey) || []; rows.push(row); samplesByPage.set(row.pageKey, rows); } const naturalApiResponses = (Array.isArray(network) ? network : []) .filter((item) => item?.observerInitiated !== true && item?.type === "response" && isApiLikePath(urlPath(item?.url))); const telemetryExcluded = []; const nonStateRelevant = []; const stateRelevantResponses = []; for (const item of naturalApiResponses) { const event = compactApiDomLagResponseEvent(item); if (!Number.isFinite(event.tsMs)) { nonStateRelevant.push(event); continue; } if (isApiDomLagTelemetryPath(event.path)) telemetryExcluded.push(event); else if (!isApiDomLagStateRelevantPath(event.path)) nonStateRelevant.push(event); else stateRelevantResponses.push(event); } const candidates = []; for (const event of stateRelevantResponses) { const pageSamples = samplesByPage.get(event.pageKey) || []; const before = lastSampleAtOrBefore(pageSamples, event.tsMs, event); const firstAfter = firstSampleAfter(pageSamples, event.tsMs, event.tsMs + windowMs, event); const baselineDigest = before?.digest ?? null; const change = firstSampleAfter(pageSamples, event.tsMs, event.tsMs + windowMs, event, (row) => !baselineDigest || row.digest !== baselineDigest); candidates.push({ ...event, windowMs, budgetMs, firstSampleDeltaMs: firstAfter ? Math.max(0, Math.round(firstAfter.tsMs - event.tsMs)) : null, domChangeDeltaMs: change ? Math.max(0, Math.round(change.tsMs - event.tsMs)) : null, overBudget: change ? (change.tsMs - event.tsMs) > budgetMs : false, domChanged: Boolean(change), noDomChangeWithinWindow: !change, beforeSample: compactApiDomLagSample(before), firstAfterSample: compactApiDomLagSample(firstAfter), changeSample: compactApiDomLagSample(change), confidence: apiDomLagConfidence(event.path), valuesRedacted: true }); } const changedDeltas = candidates.map((item) => nullableNumber(item.domChangeDeltaMs)).filter(Number.isFinite).sort((a, b) => a - b); const groups = groupApiDomLagCandidates(candidates); const overBudget = candidates.filter((item) => item.overBudget === true); return { summary: { windowMs, budgetMs, naturalApiResponseCount: naturalApiResponses.length, telemetryExcludedCount: telemetryExcluded.length, nonStateRelevantResponseCount: nonStateRelevant.length, stateRelevantResponseCount: stateRelevantResponses.length, candidateCount: candidates.length, domChangedCount: changedDeltas.length, noDomChangeWithinWindowCount: candidates.filter((item) => item.noDomChangeWithinWindow === true).length, lowConfidenceStreamOpenCount: candidates.filter((item) => item.confidence === "low-stream-open-only").length, overBudgetCount: overBudget.length, p50DomChangeDeltaMs: percentile(changedDeltas, 50), p95DomChangeDeltaMs: percentile(changedDeltas, 95), maxDomChangeDeltaMs: changedDeltas.length > 0 ? Math.max(...changedDeltas) : null, groupCount: groups.length, valuesRedacted: true }, groups, worstCandidates: candidates .filter((item) => Number.isFinite(nullableNumber(item.domChangeDeltaMs))) .sort((a, b) => nullableNumber(b.domChangeDeltaMs) - nullableNumber(a.domChangeDeltaMs)) .slice(0, 20), recentCandidates: candidates.slice(-40), telemetryExcluded: telemetryExcluded.slice(0, 20), nonStateRelevant: nonStateRelevant.slice(0, 20), valuesRedacted: true }; } function compactApiDomLagResponseEvent(item) { const parsed = parseApiDomLagUrl(item?.url); const tsMs = timestampMs(item?.ts); return { ts: item?.ts ?? null, tsMs, pageRole: item?.pageRole ?? null, pageId: item?.pageId ?? null, pageKey: String(item?.pageRole || "control") + ":" + String(item?.pageId || "default"), commandId: item?.commandId ?? null, method: String(item?.method || "GET").toUpperCase(), status: Number.isFinite(Number(item?.status)) ? Number(item.status) : null, path: parsed.path, rawPath: parsed.rawPath, queryKeys: parsed.queryKeys, sessionId: parsed.sessionId, traceId: parsed.traceId, urlHash: item?.url ? sha256(item.url) : null, routeKind: apiDomLagRouteKind(parsed.path), valuesRedacted: true }; } function parseApiDomLagUrl(value) { try { const parsed = new URL(String(value || "http://invalid.local/")); const rawPath = parsed.pathname || "-"; const queryKeys = Array.from(parsed.searchParams.keys()).sort().slice(0, 12); const sessionId = parsed.searchParams.get("sessionId") || parsed.searchParams.get("includeSessionId") || firstIdInText(parsed.pathname + " " + parsed.search, /\bses_[A-Za-z0-9_-]+\b/u); const traceId = parsed.searchParams.get("traceId") || firstIdInText(parsed.pathname + " " + parsed.search, /\btrc_[A-Za-z0-9_-]+\b/u); return { rawPath, path: normalizeApiPath(rawPath), queryKeys, sessionId, traceId }; } catch { const rawPath = urlPath(value); return { rawPath, path: normalizeApiPath(rawPath), queryKeys: [], sessionId: firstIdInText(String(value || ""), /\bses_[A-Za-z0-9_-]+\b/u), traceId: firstIdInText(String(value || ""), /\btrc_[A-Za-z0-9_-]+\b/u) }; } } function firstIdInText(text, pattern) { const match = String(text || "").match(pattern); return match ? match[0] : null; } function nullableNumber(value) { if (value === null || value === undefined || value === "") return NaN; const numeric = Number(value); return Number.isFinite(numeric) ? numeric : NaN; } function isApiDomLagTelemetryPath(path) { const value = String(path || ""); return value === "/v1/web-performance" || value === "/v1/health" || value === "/health"; } function isApiDomLagStateRelevantPath(path) { const value = String(path || ""); return value.startsWith("/auth/") || value.startsWith("/v1/workbench/") || value === "/v1/agent/chat" || value === "/v1/agent/chat/steer"; } function apiDomLagRouteKind(path) { const value = String(path || ""); if (value === "/v1/workbench/events") return "workbench-events-stream"; if (value.startsWith("/v1/workbench/sessions")) return "workbench-sessions"; if (value.startsWith("/v1/workbench/traces")) return "workbench-traces"; if (value.startsWith("/v1/workbench/turns")) return "workbench-turns"; if (value === "/v1/agent/chat" || value === "/v1/agent/chat/steer") return "agent-chat-submit"; if (value.startsWith("/auth/")) return "auth"; return "state-api"; } function apiDomLagConfidence(path) { return String(path || "") === "/v1/workbench/events" ? "low-stream-open-only" : "medium-response-to-dom"; } function sampleTraceIds(sample) { const ids = new Set(); for (const group of [sample?.messages, sample?.traceRows, sample?.turns, sample?.diagnostics]) { if (!Array.isArray(group)) continue; for (const item of group) if (item?.traceId) ids.add(String(item.traceId)); } return ids; } function lastSampleAtOrBefore(rows, tsMs, event) { let result = null; for (const row of rows) { if (row.tsMs > tsMs) break; if (apiDomLagSampleMatchesEvent(row, event)) result = row; } return result; } function firstSampleAfter(rows, startMs, endMs, event, predicate = null) { for (const row of rows) { if (row.tsMs < startMs) continue; if (row.tsMs > endMs) break; if (!apiDomLagSampleMatchesEvent(row, event)) continue; if (typeof predicate === "function" && !predicate(row)) continue; return row; } return null; } function apiDomLagSampleMatchesEvent(row, event) { if (!row || !event) return false; if (event.sessionId && !row.sessionIds.has(String(event.sessionId))) return false; if (event.traceId && row.traceIds.size > 0 && !row.traceIds.has(String(event.traceId))) return false; return true; } function compactApiDomLagSample(row) { if (!row) return null; const sample = row.sample || {}; return { seq: sample.seq ?? null, ts: sample.ts ?? null, pageRole: sample.pageRole ?? null, pageId: sample.pageId ?? null, routeSessionId: sample.routeSessionId ?? null, activeSessionId: sample.activeSessionId ?? null, messageCount: Array.isArray(sample.messages) ? sample.messages.length : null, traceRowCount: Array.isArray(sample.traceRows) ? sample.traceRows.length : null, diagnosticCount: Array.isArray(sample.diagnostics) ? sample.diagnostics.length : null, valuesRedacted: true }; } function groupApiDomLagCandidates(candidates) { const groups = new Map(); for (const item of candidates || []) { const key = [item.method || "-", item.path || "-", item.status ?? "-", item.confidence || "-"].join(" "); const group = groups.get(key) || { method: item.method ?? null, path: item.path ?? "-", routeKind: item.routeKind ?? null, status: item.status ?? null, confidence: item.confidence ?? null, count: 0, domChangedCount: 0, noDomChangeWithinWindowCount: 0, overBudgetCount: 0, firstAt: item.ts ?? null, lastAt: item.ts ?? null, deltas: [], examples: [] }; group.count += 1; group.firstAt = minIso(group.firstAt, item.ts ?? null); group.lastAt = maxIso(group.lastAt, item.ts ?? null); if (item.domChanged === true && Number.isFinite(Number(item.domChangeDeltaMs))) { group.domChangedCount += 1; group.deltas.push(Number(item.domChangeDeltaMs)); } if (item.noDomChangeWithinWindow === true) group.noDomChangeWithinWindowCount += 1; if (item.overBudget === true) group.overBudgetCount += 1; if (group.examples.length < 6) { group.examples.push({ ts: item.ts ?? null, sessionId: item.sessionId ?? null, traceId: item.traceId ?? null, domChangeDeltaMs: item.domChangeDeltaMs ?? null, firstSampleDeltaMs: item.firstSampleDeltaMs ?? null, changeSeq: item.changeSample?.seq ?? null, beforeSeq: item.beforeSample?.seq ?? null, valuesRedacted: true }); } groups.set(key, group); } return Array.from(groups.values()) .map((item) => { const deltas = item.deltas.slice().sort((a, b) => a - b); return { method: item.method, path: item.path, routeKind: item.routeKind, status: item.status, confidence: item.confidence, count: item.count, domChangedCount: item.domChangedCount, noDomChangeWithinWindowCount: item.noDomChangeWithinWindowCount, overBudgetCount: item.overBudgetCount, p50DomChangeDeltaMs: percentile(deltas, 50), p95DomChangeDeltaMs: percentile(deltas, 95), maxDomChangeDeltaMs: deltas.length > 0 ? Math.max(...deltas) : null, firstAt: item.firstAt, lastAt: item.lastAt, examples: item.examples, valuesRedacted: true }; }) .sort((a, b) => Number(b.maxDomChangeDeltaMs ?? -1) - Number(a.maxDomChangeDeltaMs ?? -1) || b.count - a.count || String(a.path).localeCompare(String(b.path))); } function detectTraceEventsPageReadIssues(network) { const events = (Array.isArray(network) ? network : []) .filter((item) => item?.observerInitiated !== true && (item?.type === "response" || item?.type === "requestfailed")) .map(compactTraceEventsPageReadEvent) .filter((item) => item !== null); const http404 = events.filter((item) => item.type === "response" && Number(item.status) === 404); const httpErrors = events.filter((item) => item.type === "response" && Number(item.status) >= 400 && Number(item.status) !== 404); const requestFailed = events.filter((item) => item.type === "requestfailed"); return { events, http404, httpErrors, requestFailed, summary: traceEventsPageReadIssueSummary(events), valuesRedacted: true }; } function compactTraceEventsPageReadEvent(item) { const parsed = parseTraceEventsPageReadUrl(item?.url); if (!parsed.match) return null; const failureText = item?.failureKind ?? item?.failure ?? item?.errorText ?? null; return { ts: item?.ts ?? null, pageRole: item?.pageRole ?? null, pageId: item?.pageId ?? null, commandId: item?.commandId ?? null, method: String(item?.method || "GET").toUpperCase(), type: item?.type ?? null, status: Number.isFinite(Number(item?.status)) ? Number(item.status) : null, path: "/v1/workbench/traces/:traceId/events", rawPath: parsed.rawPath, traceId: parsed.traceId, afterProjectedSeq: parsed.afterProjectedSeq, sinceSeq: parsed.sinceSeq, limit: parsed.limit, tail: parsed.tail, queryKeys: parsed.queryKeys, failureKind: failureText ? limitText(String(failureText), 120) : null, urlHash: item?.url ? sha256(item.url) : null, valuesRedacted: true }; } function parseTraceEventsPageReadUrl(value) { const fallback = { match: false, rawPath: urlPath(value), traceId: firstIdInText(String(value || ""), /\btrc_[A-Za-z0-9_-]+\b/u), afterProjectedSeq: null, sinceSeq: null, limit: null, tail: null, queryKeys: [], }; try { const parsed = new URL(String(value || ""), "http://invalid.local/"); const rawPath = parsed.pathname || "-"; const match = rawPath.match(/^\/v1\/workbench\/traces\/([^/]+)\/events$/u); return { match: Boolean(match), rawPath, traceId: match ? decodeURIComponent(match[1]) : fallback.traceId, afterProjectedSeq: numericSearchParam(parsed.searchParams, "afterProjectedSeq"), sinceSeq: numericSearchParam(parsed.searchParams, "sinceSeq") ?? numericSearchParam(parsed.searchParams, "afterSeq"), limit: numericSearchParam(parsed.searchParams, "limit"), tail: numericSearchParam(parsed.searchParams, "tail"), queryKeys: Array.from(parsed.searchParams.keys()).sort().slice(0, 12), }; } catch { return fallback; } } function numericSearchParam(searchParams, key) { const raw = searchParams?.get?.(key); if (raw === null || raw === undefined || raw === "") return null; const parsed = Number(raw); return Number.isFinite(parsed) ? parsed : null; } function traceEventsPageReadIssueSummary(events) { const items = Array.isArray(events) ? events : []; const statuses = uniqueSorted(items.map((item) => item.status).filter((item) => item !== null && item !== undefined).map(String)); const traceIds = uniqueSorted(items.map((item) => item.traceId).filter(Boolean).map(String)).slice(0, 8); const afterProjectedSeqs = uniqueSorted(items.map((item) => item.afterProjectedSeq).filter((item) => item !== null && item !== undefined).map(String)).slice(0, 8); const sinceSeqs = uniqueSorted(items.map((item) => item.sinceSeq).filter((item) => item !== null && item !== undefined).map(String)).slice(0, 8); const failureKinds = uniqueSorted(items.map((item) => item.failureKind).filter(Boolean).map(String)).slice(0, 6); return { eventCount: items.length, responseErrorCount: items.filter((item) => item.type === "response" && Number(item.status) >= 400).length, http404Count: items.filter((item) => item.type === "response" && Number(item.status) === 404).length, requestFailedCount: items.filter((item) => item.type === "requestfailed").length, statuses, traceIds, afterProjectedSeqs, sinceSeqs, failureKinds, firstAt: items.reduce((value, item) => minIso(value, item.ts ?? null), null), lastAt: items.reduce((value, item) => maxIso(value, item.ts ?? null), null), rootCauseVisibility: "browser network rows identify trace-events page-read path; OTel trace_events_read should confirm backend paging fields", valuesRedacted: true }; } function uniqueSorted(values) { return Array.from(new Set((values || []).filter((item) => item !== null && item !== undefined).map(String))).sort(); } function compactApiDomLagForOutput(report) { if (!report || typeof report !== "object") return null; return { summary: report.summary ?? null, groups: Array.isArray(report.groups) ? report.groups.slice(0, 8) : [], worstCandidates: Array.isArray(report.worstCandidates) ? report.worstCandidates.slice(0, 8) : [], recentCandidates: Array.isArray(report.recentCandidates) ? report.recentCandidates.slice(-8) : [], valuesRedacted: true }; } `; }