diff --git a/scripts/src/hwlab-node-web-observe-runner-source.ts b/scripts/src/hwlab-node-web-observe-runner-source.ts index b812c411..11bf900b 100644 --- a/scripts/src/hwlab-node-web-observe-runner-source.ts +++ b/scripts/src/hwlab-node-web-observe-runner-source.ts @@ -4790,7 +4790,7 @@ function buildCodeAgentCardDurationUnderreportedMetrics(samples, timeline) { const traceMatched = traceRows.filter((row) => traceRowMatchesCard(row, card, terminalCards.length)); const traceEvidence = maxTraceDurationEvidence(traceMatched); const textEvidence = maxSelfReportedDurationEvidence([card.text, card.preview, card.finalResponseText, card.runningRecordText, terminalCards.length === 1 ? sampleText : ''].filter(Boolean).join('\n')); - const evidences = [traceEvidence, textEvidence].filter((item) => item && Number.isFinite(item.seconds)); + const evidences = [traceEvidence, textEvidence].filter((item) => item && item.exact === true && Number.isFinite(item.seconds)); if (!evidences.length) continue; const strongest = evidences.sort((a, b) => b.seconds - a.seconds)[0]; const tolerance = Math.max(slackSeconds, Math.ceil(strongest.seconds * 0.05)); @@ -4835,7 +4835,7 @@ function buildCodeAgentCardDurationMismatchMetrics(samples, timeline) { const traceMatched = traceRows.filter((row) => traceRowMatchesCard(row, card, terminalCards.length)); const traceEvidence = maxTraceDurationEvidence(traceMatched); const textEvidence = maxSelfReportedDurationEvidence([card.text, card.preview, card.finalResponseText, card.runningRecordText, terminalCards.length === 1 ? sampleText : ''].filter(Boolean).join('\n')); - const evidences = [traceEvidence, textEvidence].filter((item) => item && Number.isFinite(item.seconds)); + const evidences = [traceEvidence, textEvidence].filter((item) => item && item.exact === true && Number.isFinite(item.seconds)); if (!evidences.length) continue; const strongest = evidences.sort((a, b) => b.seconds - a.seconds)[0]; const exactEvidence = strongest.exact === true || strongest.kind === 'trace-completion-total' || strongest.kind === 'final-response-duration'; @@ -5272,11 +5272,19 @@ function codeAgentCardText(item) { } function isCodeAgentCardTerminal(item) { - if (isTerminalTurnStatus(item?.status)) return true; + const status = String(item?.status ?? item?.state ?? item?.phase ?? "").trim().toLowerCase().replace(/_/gu, "-"); + if (isActiveTurnStatus(status)) return false; + if (isTerminalTurnStatus(status)) return true; + if (item?.terminal === true || item?.sealed === true) return true; const text = codeAgentCardText(item); return isTerminalTraceText(text) || /轮次完成|轮次失败|轮次取消|已记录|completed|failed|canceled|cancelled|blocked/iu.test(text); } +function isActiveTurnStatus(value) { + const status = String(value ?? "").trim().toLowerCase().replace(/_/gu, "-"); + return ["pending", "running", "queued", "admitted", "dispatching", "in-progress", "inprogress"].includes(status); +} + function buildRoundCompletionMetrics(samples, timeline, turnTiming) { const events = []; const elapsedMismatchToleranceSeconds = Math.max(5, Number(alertThresholds.turnTimingSampleSlackSeconds || 0));