From b0c99bcc52d6bc2d226cd9ea7e077e1efa73d727 Mon Sep 17 00:00:00 2001 From: Codex Date: Sun, 21 Jun 2026 18:25:29 +0000 Subject: [PATCH] fix: compact web observe analyze and extend runner timeout --- config/agentrun.yaml | 2 +- scripts/src/hwlab-node-impl.ts | 36 +++++++++++++++++-- .../hwlab-node-web-observe-runner-source.ts | 32 +++++++++++++---- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/config/agentrun.yaml b/config/agentrun.yaml index eccc1ddd..691679c9 100644 --- a/config/agentrun.yaml +++ b/config/agentrun.yaml @@ -325,7 +325,7 @@ controlPlane: serviceAccount: agentrun-v02-runner jobNamePrefix: agentrun-v02-runner idleTimeoutMs: 172800000 - missingTerminalAfterToolTimeoutMs: 30000 + missingTerminalAfterToolTimeoutMs: 180000 apiKeySecretRef: name: agentrun-v02-api-key key: HWLAB_API_KEY diff --git a/scripts/src/hwlab-node-impl.ts b/scripts/src/hwlab-node-impl.ts index ce5f9aa9..00724bbe 100644 --- a/scripts/src/hwlab-node-impl.ts +++ b/scripts/src/hwlab-node-impl.ts @@ -7319,7 +7319,12 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec "set -eu", nodeWebObserveResolveStateDirShell(options), "analyzer=\"$state_dir/observer-analyzer.mjs\"", - `node -e "require('fs').writeFileSync(process.argv[1], Buffer.from(process.argv[2], 'base64'))" "$analyzer" ${shellQuote(analyzerB64)}`, + "analyzer_b64=\"$state_dir/observer-analyzer.mjs.b64\"", + "cat >\"$analyzer_b64\" <<'UNIDESK_WEB_OBSERVE_ANALYZER_B64'", + analyzerB64, + "UNIDESK_WEB_OBSERVE_ANALYZER_B64", + "node -e \"const fs=require('fs'); fs.writeFileSync(process.argv[1], Buffer.from(fs.readFileSync(process.argv[2], 'utf8').replace(/\\s+/g, ''), 'base64'))\" \"$analyzer\" \"$analyzer_b64\"", + "rm -f \"$analyzer_b64\"", "chmod 700 \"$analyzer\"", "UNIDESK_WEB_OBSERVE_STATE_DIR=\"$state_dir\" node \"$analyzer\"", ].join("\n"); @@ -7356,6 +7361,10 @@ function renderWebObserveAnalyzeTable(value: Record): string { const runtimeAlerts = record(analysis?.runtimeAlerts); const pagePerformance = record(analysis?.pagePerformance); const promptNetwork = record(analysis?.promptNetwork); + const rounds = webObserveArray(sampleMetrics?.roundItems ?? sampleMetrics?.rounds).map(record).filter((item): item is Record => item !== null).slice(-8); + const turnColumns = webObserveArray(sampleMetrics?.turnColumns).map(record).filter((item): item is Record => item !== null).slice(-12); + const roundCount = Array.isArray(sampleMetrics?.rounds) ? sampleMetrics.rounds.length : sampleMetrics?.rounds; + 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 => item !== null).slice(0, 8) : []; const findings = Array.isArray(analysis?.findings) ? analysis.findings.map(record).filter((item): item is Record => item !== null).slice(0, 8) : []; const domDiagnostics = Array.isArray(analysis?.domDiagnosticGroups) ? analysis.domDiagnosticGroups.map(record).filter((item): item is Record => item !== null).slice(0, 5) : []; @@ -7391,8 +7400,8 @@ function renderWebObserveAnalyzeTable(value: Record): string { "", "Turn timing:", webObserveTable(["ROUNDS", "TURNS", "ROWS", "NON_MONO", "ELAPSED_DEC", "RECENT_JUMP", "MAX_RECENT_STEP"], [[ - webObserveText(sampleMetrics?.rounds), - webObserveText(sampleMetrics?.turnColumns), + webObserveText(roundCount), + webObserveText(turnColumnCount), webObserveText(sampleMetrics?.turnTimingRows), webObserveText(sampleMetrics?.turnTimingNonMonotonicCount), webObserveText(sampleMetrics?.turnTimingTotalElapsedDecreaseCount), @@ -7400,6 +7409,27 @@ function renderWebObserveAnalyzeTable(value: Record): string { webObserveText(sampleMetrics?.turnTimingRecentUpdateMaxIncreaseSeconds), ]]), "", + "Rounds:", + webObserveTable(["ROUND", "SAMPLES", "TOTAL_LAST", "RECENT_LAST", "DIAG", "TERMINAL", "PROMPT_HASH"], rounds.length > 0 ? rounds.map((item) => [ + webObserveText(item.promptIndex), + webObserveText(item.sampleCount), + webObserveText(item.lastTotalElapsedSeconds), + webObserveText(item.lastRecentUpdateSeconds), + webObserveText(item.diagnosticSamples), + webObserveText(item.terminalSamples), + webObserveShort(webObserveText(item.promptTextHash), 24), + ]) : [["-", "-", "-", "-", "-", "-", "-"]]), + "", + "Turn columns:", + webObserveTable(["TURN", "PROMPT", "TRACE", "FIRST_SEQ", "LAST_SEQ", "SOURCE"], turnColumns.length > 0 ? turnColumns.map((item) => [ + webObserveText(item.label), + webObserveText(item.promptIndex ?? item.lastPromptIndex), + webObserveShort(webObserveText(item.traceId), 28), + webObserveText(item.firstSeq), + webObserveText(item.lastSeq), + webObserveShort(webObserveText(item.source), 24), + ]) : [["-", "-", "-", "-", "-", "-"]]), + "", "Runtime alerts:", webObserveTable(["HTTP_ERR", "REQUEST_FAILED", "DOM_DIAG", "CONSOLE_ALERT", "PROMPT_SEGMENTS", "PROMPT_NETWORK"], [[ webObserveText(runtimeAlerts?.httpErrorCount), diff --git a/scripts/src/hwlab-node-web-observe-runner-source.ts b/scripts/src/hwlab-node-web-observe-runner-source.ts index 46ff2e74..0c196acc 100644 --- a/scripts/src/hwlab-node-web-observe-runner-source.ts +++ b/scripts/src/hwlab-node-web-observe-runner-source.ts @@ -1080,13 +1080,31 @@ const report = { await writeFile(reportJsonPath, JSON.stringify(report, null, 2) + "\n", { mode: 0o600 }); await writeFile(reportMdPath, renderMarkdown(report), { mode: 0o600 }); const [jsonMeta, mdMeta] = await Promise.all([fileMeta(reportJsonPath), fileMeta(reportMdPath)]); -const compactRuntimeAlerts = { - ...runtimeAlerts.summary, - domDiagnostics: runtimeAlerts.domDiagnostics.slice(-80), - domDiagnosticsByText: runtimeAlerts.domDiagnosticsByText.slice(0, 80), - domDiagnosticsByFingerprint: runtimeAlerts.domDiagnosticsByFingerprint.slice(0, 80), -}; -console.log(JSON.stringify({ ok: true, command: "web-probe-observe analyze", stateDir, reportJsonPath, reportJsonSha256: jsonMeta.sha256, reportMdPath, reportMdSha256: mdMeta.sha256, counts: report.counts, jsonlReadIssues: jsonlReadIssues.slice(0, 20), readIssues: jsonlReadIssues.slice(0, 20), sampleMetrics: { ...sampleMetrics.summary, rounds: sampleMetrics.rounds.slice(-20), turnColumns: sampleMetrics.turnColumns.slice(-50) }, pageProvenance: pageProvenance.summary, pagePerformance: pagePerformance.summary, promptNetwork: promptNetwork.summary, runtimeAlerts: compactRuntimeAlerts, domDiagnosticGroups: runtimeAlerts.domDiagnosticsByFingerprint.slice(0, 20), turnTimingRecentUpdateSawtoothJumps: sampleMetrics.turnTimingRecentUpdateSawtoothJumps.slice(0, 20), turnTimingRecentUpdateLargestSteps: sampleMetrics.turnTimingRecentUpdateLargestSteps.slice(0, 20), pagePerformanceSlowApi: pagePerformance.sameOriginApiByPath.filter((item) => item.overFiveSecondCount > 0).slice(0, 20), pagePerformanceLongLivedStreams: pagePerformance.sameOriginApiByPath.filter((item) => item.isLongLivedStream).slice(0, 20), findings: findings.slice(0, 20), valuesRedacted: true }, null, 2)); +console.log(JSON.stringify({ + ok: true, + command: "web-probe-observe analyze", + stateDir, + reportJsonPath, + reportJsonSha256: jsonMeta.sha256, + reportMdPath, + reportMdSha256: mdMeta.sha256, + counts: report.counts, + 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 })), + }, + pageProvenance: pageProvenance.summary, + pagePerformance: pagePerformance.summary, + promptNetwork: promptNetwork.summary, + runtimeAlerts: runtimeAlerts.summary, + domDiagnosticGroups: runtimeAlerts.domDiagnosticsByFingerprint.slice(0, 5).map((item) => ({ count: item.count, firstAt: item.firstAt, lastAt: item.lastAt, text: String(item.text ?? "").slice(0, 160) })), + 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) })), + valuesRedacted: true, +})); async function readJson(file) { try { return JSON.parse(await readFile(file, "utf8")); } catch { return null; }