fix(web-probe): report recent observe window

This commit is contained in:
Codex
2026-06-22 00:51:56 +00:00
parent 99f1658091
commit 4bed2421b6
2 changed files with 139 additions and 14 deletions
+43
View File
@@ -7475,6 +7475,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
"const slimSlowApi = (item) => { const v = objectOrNull(item) || {}; return { path: clip(v.path ?? v.route, 96), route: clip(v.route ?? v.path, 96), sampleCount: v.sampleCount ?? null, p95Ms: v.p95Ms ?? v.p95 ?? null, maxMs: v.maxMs ?? v.max ?? null, overFiveSecondCount: v.overFiveSecondCount ?? null }; };",
"const slimFinding = (item) => { const v = objectOrNull(item) || {}; return { kind: clip(v.kind ?? v.code, 48), code: clip(v.code ?? v.kind, 48), severity: clip(v.severity ?? v.level, 24), level: clip(v.level ?? v.severity, 24), count: v.count ?? v.sampleCount ?? null, sampleCount: v.sampleCount ?? v.count ?? null, summary: clip(v.summary ?? v.message, 180), message: clip(v.message ?? v.summary, 180) }; };",
"const slimDomGroup = (item) => { const v = objectOrNull(item) || {}; return { count: v.count ?? null, firstAt: v.firstAt ?? null, lastAt: v.lastAt ?? null, text: clip(v.text ?? v.preview, 180) }; };",
"const slimNetworkGroup = (item) => { const v = objectOrNull(item) || {}; return { count: v.count ?? null, method: clip(v.method, 12), status: v.status ?? null, path: clip(v.path, 96), firstAt: v.firstAt ?? null, lastAt: v.lastAt ?? null, promptIndexes: Array.isArray(v.promptIndexes) ? v.promptIndexes.slice(0, 6) : [], failureKinds: Array.isArray(v.failureKinds) ? v.failureKinds.slice(0, 4).map((x) => clip(x, 48)) : [] }; };",
"const slimDomSample = (item) => { const v = objectOrNull(item) || {}; return { seq: v.seq ?? null, ts: v.ts ?? null, source: clip(v.source, 32), diagnosticCode: clip(v.diagnosticCode, 48), traceId: clip(v.traceId, 64), httpStatus: v.httpStatus ?? null, idleSeconds: v.idleSeconds ?? null, waitingFor: clip(v.waitingFor, 48), lastEventLabel: clip(v.lastEventLabel, 80), text: clip(v.text ?? v.preview, 180) }; };",
"const slimConsoleGroup = (item) => { const v = objectOrNull(item) || {}; return { count: v.count ?? null, type: clip(v.type, 24), status: v.status ?? null, path: clip(v.path, 96), lastAt: v.lastAt ?? v.firstAt ?? null, firstAt: v.firstAt ?? null, traceIds: Array.isArray(v.traceIds) ? v.traceIds.slice(0, 3).map((x) => clip(x, 64)) : [] }; };",
"const slimConsoleSample = (item) => { const v = objectOrNull(item) || {}; return { ts: v.ts ?? null, type: clip(v.type, 24), status: v.status ?? null, path: clip(v.path, 96), traceId: clip(v.traceId, 64), text: clip(v.text, 180) }; };",
@@ -7502,12 +7503,16 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
"const compact = source ? {",
" ok: analyzerExit === 0 && source.ok !== false,",
" counts: source.counts ?? null,",
" analysisWindow: objectOrNull(source.analysisWindow),",
" archiveSummary: objectOrNull(source.archiveSummary),",
" sampleMetrics: metrics,",
" runtimeAlerts,",
" pagePerformance,",
" promptNetwork,",
" pagePerformanceSlowApi: takeHead(source.pagePerformanceSlowApi, 5).map(slimSlowApi),",
" findings: takeHead(source.findings, 8).map(slimFinding),",
" httpErrorGroups: takeHead(source.httpErrorGroups, 8).map(slimNetworkGroup),",
" requestFailedGroups: takeHead(source.requestFailedGroups, 8).map(slimNetworkGroup),",
" domDiagnosticGroups: takeHead(source.domDiagnosticGroups, 5).map(slimDomGroup),",
" domDiagnosticSamples: takeHead(source.domDiagnosticSamples, 5).map(slimDomSample),",
" consoleAlertGroups: takeHead(source.consoleAlertGroups, 5).map(slimConsoleGroup),",
@@ -7571,6 +7576,8 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
const analyzer = record(analysis?.analyzer);
const result = record(value.result);
const counts = record(analysis?.counts);
const analysisWindow = record(analysis?.analysisWindow);
const archiveSummary = record(analysis?.archiveSummary);
const sampleMetrics = record(analysis?.sampleMetrics);
const runtimeAlerts = record(analysis?.runtimeAlerts);
const pagePerformance = record(analysis?.pagePerformance);
@@ -7581,6 +7588,8 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
const turnColumnCount = Array.isArray(sampleMetrics?.turnColumns) ? sampleMetrics.turnColumns.length : sampleMetrics?.turnColumns;
const slowApis = Array.isArray(analysis?.pagePerformanceSlowApi) ? analysis.pagePerformanceSlowApi.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8) : [];
const findings = Array.isArray(analysis?.findings) ? analysis.findings.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8) : [];
const httpErrorGroups = Array.isArray(analysis?.httpErrorGroups) ? analysis.httpErrorGroups.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8) : [];
const requestFailedGroups = Array.isArray(analysis?.requestFailedGroups) ? analysis.requestFailedGroups.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8) : [];
const domDiagnostics = Array.isArray(analysis?.domDiagnosticGroups) ? analysis.domDiagnosticGroups.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 5) : [];
const domDiagnosticSamples = Array.isArray(analysis?.domDiagnosticSamples) ? analysis.domDiagnosticSamples.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8) : [];
const consoleAlertGroups = Array.isArray(analysis?.consoleAlertGroups) ? analysis.consoleAlertGroups.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8) : [];
@@ -7635,6 +7644,22 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
webObserveText(item.allowedIncreaseSeconds),
webObserveShort(webObserveText(item.traceId), 24),
]);
const httpErrorRows = httpErrorGroups.map((item) => [
webObserveText(item.count),
webObserveText(item.method),
webObserveText(item.status),
webObserveShort(webObserveText(item.path), 52),
webObserveShort(webObserveText(item.lastAt ?? item.firstAt), 24),
webObserveShort(webObserveArray(item.failureKinds).join(",") || "-", 40),
]);
const requestFailedRows = requestFailedGroups.map((item) => [
webObserveText(item.count),
webObserveText(item.method),
webObserveText(item.status),
webObserveShort(webObserveText(item.path), 52),
webObserveShort(webObserveText(item.lastAt ?? item.firstAt), 24),
webObserveShort(webObserveArray(item.failureKinds).join(",") || "-", 40),
]);
const lines = [
`hwlab nodes web-probe observe analyze (${webObserveText(value.status)})`,
"",
@@ -7665,6 +7690,18 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
webObserveText(counts?.artifacts),
]]),
"",
"Analysis window:",
webObserveTable(["WINDOW", "FROM", "TO", "SAMPLES", "NETWORK", "CONSOLE", "ARCHIVE_SAMPLES", "ARCHIVE_RED"], [[
webObserveText(analysisWindow?.name ?? "recent-5m"),
webObserveShort(webObserveText(analysisWindow?.fromAt), 24),
webObserveShort(webObserveText(analysisWindow?.toAt), 24),
webObserveText(analysisWindow?.samples),
webObserveText(analysisWindow?.network),
webObserveText(analysisWindow?.console),
webObserveText(record(archiveSummary?.sampleMetrics)?.sampleCount ?? counts?.samples),
webObserveText(archiveSummary?.redFindingCount),
]]),
"",
"Reports:",
webObserveTable(["KIND", "PATH", "SHA256"], [
["json", webObserveShort(webObserveText(analysis?.reportJsonPath), 96), webObserveShort(webObserveText(analysis?.reportJsonSha256), 24)],
@@ -7717,6 +7754,12 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
webObserveText(promptNetwork?.promptSegments),
]]),
"",
"HTTP error groups:",
webObserveTable(["COUNT", "METHOD", "STATUS", "PATH", "LAST", "FAILURE"], httpErrorRows.length > 0 ? httpErrorRows : [["-", "-", "-", "-", "-", "-"]]),
"",
"Request failed groups:",
webObserveTable(["COUNT", "METHOD", "STATUS", "PATH", "LAST", "FAILURE"], requestFailedRows.length > 0 ? requestFailedRows : [["-", "-", "-", "-", "-", "-"]]),
"",
"Slow API (>5s):",
webObserveTable(["PATH", "SAMPLES", "P95", "MAX", "OVER5S"], slowApis.length > 0 ? slowApis.map((item) => [
webObserveShort(webObserveText(item.path ?? item.route), 52),
@@ -1114,6 +1114,7 @@ const promptNetwork = buildPromptNetworkReport(control, network);
const runtimeAlerts = buildRuntimeAlerts(samples, control, network, consoleEvents, errors);
const findings = buildFindings(samples, control, network, errors, sampleMetrics, promptNetwork, runtimeAlerts, pagePerformance, pageProvenance);
if (jsonlReadIssues.length > 0) findings.unshift({ id: "jsonl-read-issues", severity: "red", summary: "observer analyzer hit JSONL read/parse issues", count: jsonlReadIssues.length, issues: jsonlReadIssues.slice(0, 20) });
const recentWindow = buildRecentAnalysisWindow({ samples, control, network, consoleEvents, errors, manifest });
const commandTimeline = control.filter((item) => item.phase === "completed" || item.phase === "failed").map((item) => ({ ts: item.ts, phase: item.phase, commandId: item.commandId, type: item.type, input: item.input, afterUrl: item.afterUrl }));
const report = {
ok: findings.filter((item) => item.severity === "red").length === 0,
@@ -1131,6 +1132,7 @@ const report = {
promptNetwork,
runtimeAlerts,
findings,
windows: { recent: recentWindow },
readIssues: jsonlReadIssues,
artifactSummary: await artifactSummary(artifacts),
safety: { offlineOnly: true, browserDriven: false, apiFetch: false, valuesRedacted: true },
@@ -1147,18 +1149,46 @@ console.log(JSON.stringify({
reportMdPath,
reportMdSha256: mdMeta.sha256,
counts: report.counts,
archiveSummary: {
sampleMetrics: sampleMetrics.summary,
pagePerformance: pagePerformance.summary,
runtimeAlerts: runtimeAlerts.summary,
findingCount: findings.length,
redFindingCount: findings.filter((item) => item.severity === "red").length,
},
analysisWindow: recentWindow.summary,
jsonlReadIssues: jsonlReadIssues.slice(0, 3).map((item) => ({ file: item.file, line: item.line ?? null, error: String(item.error ?? "").slice(0, 160) })),
readIssues: jsonlReadIssues.slice(0, 3).map((item) => ({ file: item.file, line: item.line ?? null, error: String(item.error ?? "").slice(0, 160) })),
sampleMetrics: {
...sampleMetrics.summary,
rounds: sampleMetrics.rounds.slice(-8).map((item) => ({ promptIndex: item.promptIndex, promptTextHash: item.promptTextHash, sampleCount: item.sampleCount, firstSeq: item.firstSeq, lastSeq: item.lastSeq, lastTotalElapsedSeconds: item.lastTotalElapsedSeconds, lastRecentUpdateSeconds: item.lastRecentUpdateSeconds, diagnosticSamples: item.diagnosticSamples, terminalSamples: item.terminalSamples, finalTextSamples: item.finalTextSamples, turnTimingRecentUpdateJumpCount: item.turnTimingRecentUpdateJumpCount, turnTimingRecentUpdateMaxIncreaseSeconds: item.turnTimingRecentUpdateMaxIncreaseSeconds })),
turnColumns: sampleMetrics.turnColumns.slice(-12).map((item) => ({ label: item.label, source: item.source, promptIndex: item.promptIndex, lastPromptIndex: item.lastPromptIndex, firstSeq: item.firstSeq, lastSeq: item.lastSeq, traceId: item.traceId, messageId: item.messageId })),
...recentWindow.sampleMetrics.summary,
rounds: recentWindow.sampleMetrics.rounds.slice(-8).map((item) => ({ promptIndex: item.promptIndex, promptTextHash: item.promptTextHash, sampleCount: item.sampleCount, firstSeq: item.firstSeq, lastSeq: item.lastSeq, lastTotalElapsedSeconds: item.lastTotalElapsedSeconds, lastRecentUpdateSeconds: item.lastRecentUpdateSeconds, diagnosticSamples: item.diagnosticSamples, terminalSamples: item.terminalSamples, finalTextSamples: item.finalTextSamples, turnTimingRecentUpdateJumpCount: item.turnTimingRecentUpdateJumpCount, turnTimingRecentUpdateMaxIncreaseSeconds: item.turnTimingRecentUpdateMaxIncreaseSeconds })),
turnColumns: recentWindow.sampleMetrics.turnColumns.slice(-12).map((item) => ({ label: item.label, source: item.source, promptIndex: item.promptIndex, lastPromptIndex: item.lastPromptIndex, firstSeq: item.firstSeq, lastSeq: item.lastSeq, traceId: item.traceId, messageId: item.messageId })),
},
pageProvenance: pageProvenance.summary,
pagePerformance: pagePerformance.summary,
promptNetwork: promptNetwork.summary,
runtimeAlerts: runtimeAlerts.summary,
domDiagnosticGroups: runtimeAlerts.domDiagnosticsByFingerprint.slice(0, 5).map((item) => ({
pageProvenance: recentWindow.pageProvenance.summary,
pagePerformance: recentWindow.pagePerformance.summary,
promptNetwork: recentWindow.promptNetwork.summary,
runtimeAlerts: recentWindow.runtimeAlerts.summary,
httpErrorGroups: recentWindow.runtimeAlerts.networkHttpErrorsByPath.slice(0, 8).map((item) => ({
method: item.method ?? null,
status: item.status ?? null,
path: item.urlPath ?? null,
count: item.count,
firstAt: item.firstAt ?? null,
lastAt: item.lastAt ?? null,
promptIndexes: Array.isArray(item.promptIndexes) ? item.promptIndexes.slice(0, 6) : [],
failureKinds: Array.isArray(item.failureKinds) ? item.failureKinds.slice(0, 4) : [],
})),
requestFailedGroups: recentWindow.runtimeAlerts.networkRequestFailedByPath.slice(0, 8).map((item) => ({
method: item.method ?? null,
status: item.status ?? null,
path: item.urlPath ?? null,
count: item.count,
firstAt: item.firstAt ?? null,
lastAt: item.lastAt ?? null,
promptIndexes: Array.isArray(item.promptIndexes) ? item.promptIndexes.slice(0, 6) : [],
failureKinds: Array.isArray(item.failureKinds) ? item.failureKinds.slice(0, 4) : [],
})),
domDiagnosticGroups: recentWindow.runtimeAlerts.domDiagnosticsByFingerprint.slice(0, 5).map((item) => ({
diagnosticCode: item.diagnosticCode ?? null,
count: item.count,
firstAt: item.firstAt,
@@ -1167,7 +1197,7 @@ console.log(JSON.stringify({
traceIds: Array.isArray(item.traceIds) ? item.traceIds.slice(0, 4) : [],
text: String(item.preview ?? item.normalizedPreview ?? item.text ?? "").slice(0, 160),
})),
domDiagnosticSamples: runtimeAlerts.domDiagnostics.slice(-8).map((item) => ({
domDiagnosticSamples: recentWindow.runtimeAlerts.domDiagnostics.slice(-8).map((item) => ({
seq: item.seq ?? null,
ts: item.ts ?? null,
promptIndex: item.promptIndex ?? null,
@@ -1180,7 +1210,7 @@ console.log(JSON.stringify({
lastEventLabel: item.lastEventLabel ?? null,
text: String(item.preview ?? item.text ?? "").slice(0, 180),
})),
consoleAlertGroups: runtimeAlerts.consoleAlertsByPath.slice(0, 8).map((item) => ({
consoleAlertGroups: recentWindow.runtimeAlerts.consoleAlertsByPath.slice(0, 8).map((item) => ({
type: item.type ?? null,
status: item.status ?? null,
path: item.urlPath ?? null,
@@ -1190,7 +1220,7 @@ console.log(JSON.stringify({
promptIndexes: Array.isArray(item.promptIndexes) ? item.promptIndexes.slice(0, 6) : [],
traceIds: Array.isArray(item.traceIds) ? item.traceIds.slice(0, 4) : [],
})),
consoleAlertSamples: runtimeAlerts.consoleAlerts.slice(-8).map((item) => ({
consoleAlertSamples: recentWindow.runtimeAlerts.consoleAlerts.slice(-8).map((item) => ({
ts: item.ts ?? null,
promptIndex: item.promptIndex ?? null,
type: item.type ?? null,
@@ -1199,7 +1229,7 @@ console.log(JSON.stringify({
traceId: item.traceId ?? null,
text: String(item.preview ?? item.text ?? "").slice(0, 180),
})),
turnTimingRecentUpdateJumps: sampleMetrics.turnTimingRecentUpdateSawtoothJumps.slice(0, 8).map((item) => ({
turnTimingRecentUpdateJumps: recentWindow.sampleMetrics.turnTimingRecentUpdateSawtoothJumps.slice(0, 8).map((item) => ({
columnLabel: item.columnLabel ?? item.columnId ?? null,
promptIndex: item.promptIndex ?? null,
traceId: item.traceId ?? null,
@@ -1214,8 +1244,8 @@ console.log(JSON.stringify({
allowedIncreaseSeconds: item.allowedIncreaseSeconds ?? null,
excessiveIncreaseSeconds: item.excessiveIncreaseSeconds ?? null,
})),
pagePerformanceSlowApi: pagePerformance.sameOriginApiByPath.filter((item) => item.overFiveSecondCount > 0).slice(0, 5).map((item) => ({ path: item.path, route: item.route, sampleCount: item.sampleCount, p95Ms: item.p95Ms, maxMs: item.maxMs, overFiveSecondCount: item.overFiveSecondCount })),
findings: findings.slice(0, 8).map((item) => ({ kind: item.id ?? item.kind ?? item.code, severity: item.severity, count: item.count ?? item.sampleCount ?? null, summary: String(item.summary ?? item.message ?? "").slice(0, 180) })),
pagePerformanceSlowApi: recentWindow.pagePerformance.sameOriginApiByPath.filter((item) => item.overFiveSecondCount > 0).slice(0, 5).map((item) => ({ path: item.path, route: item.route, sampleCount: item.sampleCount, p95Ms: item.p95Ms, maxMs: item.maxMs, overFiveSecondCount: item.overFiveSecondCount })),
findings: recentWindow.findings.slice(0, 8).map((item) => ({ kind: item.id ?? item.kind ?? item.code, severity: item.severity, count: item.count ?? item.sampleCount ?? null, summary: String(item.summary ?? item.message ?? "").slice(0, 180) })),
valuesRedacted: true,
}));
@@ -1389,6 +1419,58 @@ function buildFindings(samples, control, network, errors, sampleMetrics, promptN
return findings;
}
function buildRecentAnalysisWindow({ samples, control, network, consoleEvents, errors, 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 sampleMetrics = buildSampleMetrics(windowSamples, control);
const pageProvenance = buildPageProvenanceReport(windowSamples, windowControl, manifest);
const pagePerformance = buildPagePerformanceReport(windowSamples, manifest);
const promptNetwork = buildPromptNetworkReport(control, windowNetwork);
const runtimeAlerts = buildRuntimeAlerts(windowSamples, control, windowNetwork, windowConsole, windowErrors);
const findings = buildFindings(windowSamples, control, windowNetwork, windowErrors, sampleMetrics, promptNetwork, runtimeAlerts, pagePerformance, pageProvenance);
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,
valuesRedacted: true
},
sampleMetrics,
pageProvenance,
pagePerformance,
promptNetwork,
runtimeAlerts,
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) {