fix(web-probe): report recent observe window
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user