Files
pikasTech-unidesk/scripts/src/hwlab-node-web-observe-analyzer-timing-source.ts
T
2026-06-29 12:17:52 +00:00

2540 lines
151 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// SPEC: PJ2026-01040111 long-running Workbench observation.
// Responsibility: Timing, trace-order, Code Agent card, and report-rendering source for the offline web-probe observe analyzer.
export function nodeWebObserveAnalyzerTimingSource(): string {
return String.raw`function buildTurnTimingTable(samples, timeline) {
const columns = [];
const registry = new Map();
const promptAssignmentByKey = new Map();
const rows = [];
for (let index = 0; index < samples.length; index += 1) {
const sample = samples[index];
const timelineItem = timeline[index] || {};
const cells = {};
const rawMetrics = turnMetricItems(sample, timelineItem);
const domIndexes = rawMetrics.map((item) => Number(item.domIndex)).filter(Number.isFinite);
const maxDomIndex = domIndexes.length > 0 ? Math.max(...domIndexes) : null;
for (const rawMetric of rawMetrics) {
const scopedKey = turnTimingScopedMetricKey(rawMetric, sample);
const samplePromptIndex = Number(timelineItem.promptIndex ?? 0);
const evidencePromptIndex = inferTurnMetricPromptIndex(rawMetric, samplePromptIndex, maxDomIndex);
if (evidencePromptIndex !== null) {
const existingPromptIndex = promptAssignmentByKey.get(scopedKey);
if (existingPromptIndex === undefined || evidencePromptIndex > existingPromptIndex) promptAssignmentByKey.set(scopedKey, evidencePromptIndex);
}
const assignedPromptIndex = promptAssignmentByKey.get(scopedKey) ?? null;
const metric = { ...rawMetric, key: scopedKey, baseKey: rawMetric.key, promptIndex: assignedPromptIndex, samplePromptIndex, pageEpoch: Number(sample?.pageEpoch ?? rawMetric.pageEpoch ?? 0) || 0 };
let column = registry.get(metric.key);
if (!column) {
column = {
id: "T" + String(columns.length + 1),
label: "T" + String(columns.length + 1),
keyHash: sha256(metric.key),
source: metric.source,
firstSeq: sample.seq ?? null,
firstTs: sample.ts ?? null,
lastSeq: sample.seq ?? null,
lastTs: sample.ts ?? null,
promptIndex: metric.promptIndex ?? null,
lastPromptIndex: metric.promptIndex ?? null,
traceId: metric.traceId ?? null,
messageId: metric.messageId ?? null,
domIndex: metric.domIndex ?? null,
pageRole: metric.pageRole ?? sample.pageRole ?? null,
pageId: metric.pageId ?? sample.pageId ?? null,
pageEpoch: metric.pageEpoch ?? null
};
registry.set(metric.key, column);
columns.push(column);
} else {
column.lastSeq = sample.seq ?? null;
column.lastTs = sample.ts ?? null;
if (column.source !== "turn" && metric.source === "turn") column.source = "turn";
if (metric.promptIndex && column.promptIndex !== metric.promptIndex) column.promptIndex = metric.promptIndex;
column.lastPromptIndex = metric.promptIndex ?? column.lastPromptIndex ?? null;
if (!column.traceId && metric.traceId) column.traceId = metric.traceId;
if (!column.messageId && metric.messageId) column.messageId = metric.messageId;
}
cells[column.id] = {
totalElapsedSeconds: metric.totalElapsedSeconds,
recentUpdateSeconds: metric.recentUpdateSeconds,
status: metric.status ?? null,
promptIndex: metric.promptIndex ?? null,
source: metric.source,
pageRole: metric.pageRole ?? sample.pageRole ?? null,
pageId: metric.pageId ?? sample.pageId ?? null,
pageEpoch: metric.pageEpoch ?? null,
sampleGroupSeq: sample.sampleGroupSeq ?? null,
traceId: metric.traceId ?? null,
messageId: metric.messageId ?? null,
textHash: metric.textHash ?? null,
samplePromptIndex: metric.samplePromptIndex ?? null
};
}
rows.push({
ts: sample.ts ?? null,
seq: sample.seq ?? null,
sampleGroupSeq: sample.sampleGroupSeq ?? null,
pageRole: sample.pageRole ?? null,
pageId: sample.pageId ?? null,
pageEpoch: Number(sample?.pageEpoch ?? 0) || 0,
promptIndex: timelineItem.promptIndex ?? 0,
routeSessionId: sample.routeSessionId ?? null,
activeSessionId: sample.activeSessionId ?? null,
cells
});
}
const timingEvents = detectTurnTimingNonMonotonic(columns, rows);
return {
columns,
rows,
nonMonotonic: timingEvents.anomalies,
elapsedZeroResets: timingEvents.elapsedZeroResets,
totalElapsedForwardJumps: timingEvents.totalElapsedForwardJumps,
terminalElapsedGrowth: timingEvents.terminalElapsedGrowth,
terminalElapsedCorrections: timingEvents.terminalElapsedCorrections,
recentUpdateResets: timingEvents.recentUpdateResets,
recentUpdateSteps: timingEvents.recentUpdateSteps
};
}
function turnTimingScopedMetricKey(metric, sample) {
return [
metric?.key ?? "unknown",
metric?.pageRole ?? sample?.pageRole ?? "unknown-role",
metric?.pageId ?? sample?.pageId ?? "unknown-page",
Number(sample?.pageEpoch ?? metric?.pageEpoch ?? 0) || 0
].join("|page:");
}
function inferTurnMetricPromptIndex(metric, samplePromptIndex, maxDomIndex) {
const promptIndex = Number(samplePromptIndex);
if (!Number.isFinite(promptIndex) || promptIndex <= 0) return null;
if (metric?.source === "aggregate") return promptIndex;
if (isActiveTurnStatus(metric?.status)) return promptIndex;
const domIndex = Number(metric?.domIndex);
if (!isTerminalTurnStatus(metric?.status) && Number.isFinite(domIndex) && Number.isFinite(maxDomIndex) && domIndex === maxDomIndex && metric?.recentUpdateSeconds !== null && metric?.recentUpdateSeconds !== undefined) return promptIndex;
return null;
}
function detectTurnTimingNonMonotonic(columns, rows) {
const anomalies = [];
const elapsedZeroResets = [];
const totalElapsedForwardJumps = [];
const terminalElapsedGrowth = [];
const terminalElapsedCorrections = [];
const recentUpdateResets = [];
const recentUpdateSteps = [];
for (const column of columns) {
const previousByMetric = new Map();
let previousTerminalTotal = null;
for (const row of rows) {
const cell = row.cells?.[column.id];
if (!cell) continue;
for (const metric of ["totalElapsedSeconds", "recentUpdateSeconds"]) {
const value = cell[metric];
if (value === null || value === undefined || !Number.isFinite(Number(value))) continue;
const current = Number(value);
const previous = previousByMetric.get(metric);
if (previous && metric === "totalElapsedSeconds" && current < previous.value) {
const anomaly = previous.value > 0 && current === 0 ? "zero-reset" : "decrease";
const terminalTransition = !isTerminalTurnStatus(previous.status) && isTerminalTurnStatus(cell.status);
const dropSeconds = previous.value - current;
const allowedDropSeconds = Math.max(1, Number(alertThresholds?.turnTimingSampleSlackSeconds || 0));
const event = {
columnId: column.id,
columnLabel: column.label,
...timingFindingMeta("dom-card-total-elapsed-sequence", timingStatusFromTurnStatus(cell.status ?? previous.status), {
cardElapsedSource: cell.source ?? column.source ?? "dom-card",
}),
metric,
anomaly,
expectedPattern: anomaly === "zero-reset" ? "total-elapsed-should-not-return-to-zero" : "total-elapsed-monotonic",
fromSeq: previous.seq,
fromTs: previous.ts,
fromValue: previous.value,
fromStatus: previous.status ?? null,
toSeq: row.seq ?? null,
toTs: row.ts ?? null,
toValue: current,
toStatus: cell.status ?? null,
delta: current - previous.value,
traceId: cell.traceId ?? column.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
promptIndex: cell.promptIndex ?? null,
samplePromptIndex: row.promptIndex ?? null,
source: cell.source ?? column.source ?? null,
pageRole: cell.pageRole ?? column.pageRole ?? null,
pageId: cell.pageId ?? column.pageId ?? null,
pageEpoch: cell.pageEpoch ?? row.pageEpoch ?? column.pageEpoch ?? null,
valuesRedacted: true
};
if (terminalTransition && anomaly === "decrease" && dropSeconds <= allowedDropSeconds) {
terminalElapsedCorrections.push({
...event,
anomaly: "terminal-boundary-correction",
expectedPattern: "terminal-sealed-duration-may-correct-running-elapsed-within-slack",
allowedDropSeconds,
});
} else {
anomalies.push(event);
if (anomaly === "zero-reset") elapsedZeroResets.push(event);
}
}
if (previous && metric === "totalElapsedSeconds" && current > previous.value) {
const sampleDeltaSeconds = elapsedSecondsBetween(previous.ts, row.ts);
const delta = current - previous.value;
const allowedIncreaseSeconds = sampleDeltaSeconds + alertThresholds.turnTimingSampleSlackSeconds;
const terminalTransition = !isTerminalTurnStatus(previous.status) && isTerminalTurnStatus(cell.status);
if (delta > allowedIncreaseSeconds && !terminalTransition) {
totalElapsedForwardJumps.push({
columnId: column.id,
columnLabel: column.label,
...timingFindingMeta("dom-card-total-elapsed-sequence", timingStatusFromTurnStatus(cell.status ?? previous.status), {
cardElapsedSource: cell.source ?? column.source ?? "dom-card",
}),
metric,
anomaly: "forward-jump",
expectedPattern: "total-elapsed-increase-should-match-browser-sample-interval",
fromSeq: previous.seq,
fromTs: previous.ts,
fromValue: previous.value,
fromStatus: previous.status ?? null,
toSeq: row.seq ?? null,
toTs: row.ts ?? null,
toValue: current,
toStatus: cell.status ?? null,
delta,
sampleDeltaSeconds,
allowedIncreaseSeconds,
excessiveIncreaseSeconds: Number((delta - allowedIncreaseSeconds).toFixed(3)),
traceId: cell.traceId ?? column.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
promptIndex: cell.promptIndex ?? null,
samplePromptIndex: row.promptIndex ?? null,
source: cell.source ?? column.source ?? null,
pageRole: cell.pageRole ?? column.pageRole ?? null,
pageId: cell.pageId ?? column.pageId ?? null,
pageEpoch: cell.pageEpoch ?? row.pageEpoch ?? column.pageEpoch ?? null,
valuesRedacted: true
});
}
}
if (metric === "totalElapsedSeconds" && isTerminalTurnStatus(cell.status)) {
if (previousTerminalTotal && current > previousTerminalTotal.value) {
terminalElapsedGrowth.push({
columnId: column.id,
columnLabel: column.label,
...timingFindingMeta("terminal-card-total-elapsed-seal", timingStatusFromTurnStatus(cell.status, "business-turn-completed"), {
cardElapsedSource: cell.source ?? column.source ?? "dom-card",
}),
metric,
anomaly: "terminal-growth",
expectedPattern: "terminal-total-elapsed-sealed",
fromSeq: previousTerminalTotal.seq,
fromTs: previousTerminalTotal.ts,
fromValue: previousTerminalTotal.value,
fromStatus: previousTerminalTotal.status,
toSeq: row.seq ?? null,
toTs: row.ts ?? null,
toValue: current,
toStatus: cell.status ?? null,
delta: current - previousTerminalTotal.value,
traceId: cell.traceId ?? column.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
promptIndex: cell.promptIndex ?? null,
samplePromptIndex: row.promptIndex ?? null,
source: cell.source ?? column.source ?? null,
pageRole: cell.pageRole ?? column.pageRole ?? null,
pageId: cell.pageId ?? column.pageId ?? null,
pageEpoch: cell.pageEpoch ?? row.pageEpoch ?? column.pageEpoch ?? null,
valuesRedacted: true
});
}
previousTerminalTotal = { value: current, seq: row.seq ?? null, ts: row.ts ?? null, status: cell.status ?? null };
}
if (previous && metric === "recentUpdateSeconds") {
const elapsedMs = Date.parse(String(row.ts ?? "")) - Date.parse(String(previous.ts ?? ""));
const elapsedSeconds = Number.isFinite(elapsedMs) && elapsedMs >= 0 ? elapsedMs / 1000 : null;
const increase = current - previous.value;
const allowedIncrease = elapsedSeconds === null
? alertThresholds.turnTimingSampleSlackSeconds
: Math.max(alertThresholds.turnTimingSampleSlackSeconds, elapsedSeconds + alertThresholds.turnTimingSampleSlackSeconds);
const excessiveIncrease = increase > allowedIncrease ? increase - allowedIncrease : 0;
recentUpdateSteps.push({
columnId: column.id,
columnLabel: column.label,
metric: "recentUpdateSeconds",
event: increase < 0 ? "reset" : excessiveIncrease > 0 ? "jump" : "increase",
expectedPattern: "sawtooth-increase-or-reset",
fromSeq: previous.seq,
fromTs: previous.ts,
fromValue: previous.value,
toSeq: row.seq ?? null,
toTs: row.ts ?? null,
toValue: current,
delta: increase,
sampleDeltaSeconds: elapsedSeconds,
allowedIncreaseSeconds: allowedIncrease,
excessiveIncreaseSeconds: excessiveIncrease,
traceId: cell.traceId ?? column.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
promptIndex: cell.promptIndex ?? null,
samplePromptIndex: row.promptIndex ?? null,
source: cell.source ?? column.source ?? null,
pageRole: cell.pageRole ?? column.pageRole ?? null,
pageId: cell.pageId ?? column.pageId ?? null,
pageEpoch: cell.pageEpoch ?? row.pageEpoch ?? column.pageEpoch ?? null,
valuesRedacted: true
});
if (increase < 0) {
recentUpdateResets.push({
columnId: column.id,
columnLabel: column.label,
metric: "recentUpdateSeconds",
event: "reset",
fromSeq: previous.seq,
fromTs: previous.ts,
fromValue: previous.value,
toSeq: row.seq ?? null,
toTs: row.ts ?? null,
toValue: current,
delta: increase,
sampleDeltaSeconds: elapsedSeconds,
traceId: cell.traceId ?? column.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
promptIndex: cell.promptIndex ?? null,
samplePromptIndex: row.promptIndex ?? null,
source: cell.source ?? column.source ?? null,
pageRole: cell.pageRole ?? column.pageRole ?? null,
pageId: cell.pageId ?? column.pageId ?? null,
pageEpoch: cell.pageEpoch ?? row.pageEpoch ?? column.pageEpoch ?? null,
valuesRedacted: true
});
}
if (excessiveIncrease > 0) {
anomalies.push({
columnId: column.id,
columnLabel: column.label,
metric: "recentUpdateSeconds",
anomaly: "jump",
expectedPattern: "sawtooth-increase-or-reset",
fromSeq: previous.seq,
fromTs: previous.ts,
fromValue: previous.value,
toSeq: row.seq ?? null,
toTs: row.ts ?? null,
toValue: current,
delta: increase,
sampleDeltaSeconds: elapsedSeconds,
allowedIncreaseSeconds: allowedIncrease,
excessiveIncreaseSeconds: excessiveIncrease,
traceId: cell.traceId ?? column.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
promptIndex: cell.promptIndex ?? null,
samplePromptIndex: row.promptIndex ?? null,
source: cell.source ?? column.source ?? null,
pageRole: cell.pageRole ?? column.pageRole ?? null,
pageId: cell.pageId ?? column.pageId ?? null,
pageEpoch: cell.pageEpoch ?? row.pageEpoch ?? column.pageEpoch ?? null,
valuesRedacted: true
});
}
}
previousByMetric.set(metric, { value: current, seq: row.seq ?? null, ts: row.ts ?? null, status: cell.status ?? null });
}
}
}
return { anomalies, elapsedZeroResets, totalElapsedForwardJumps, terminalElapsedGrowth, terminalElapsedCorrections, recentUpdateResets, recentUpdateSteps };
}
function elapsedSecondsBetween(fromTs, toTs) {
const from = Date.parse(fromTs);
const to = Date.parse(toTs);
if (!Number.isFinite(from) || !Number.isFinite(to) || to < from) return 0;
return Number(((to - from) / 1000).toFixed(3));
}
function maxPositiveDelta(items) {
const values = (Array.isArray(items) ? items : [])
.map((item) => Number(item.delta))
.filter((value) => Number.isFinite(value) && value > 0);
return values.length > 0 ? Math.max(...values) : 0;
}
function isTerminalTurnStatus(value) {
const status = String(value ?? "").trim().toLowerCase().replace(/_/gu, "-");
return ["completed", "succeeded", "success", "failed", "error", "blocked", "timeout", "canceled", "cancelled", "stale", "done", "terminal", "thread-resume-failed"].includes(status);
}
function buildTraceOrderMetrics(samples, timeline) {
const rows = [];
const orderAnomalies = [];
const completionNotLast = [];
const groups = new Map();
for (let sampleIndex = 0; sampleIndex < samples.length; sampleIndex += 1) {
const sample = samples[sampleIndex] || {};
const sampleRows = traceTimingRowsForSample(sample, timeline[sampleIndex] || {});
for (const row of sampleRows) {
const normalized = {
...row,
sampleIndex,
sampleSeq: sample.seq ?? null,
timestamp: sample.ts || sample.timestamp || sample.collectedAt || sample.time || null,
pageRole: row.pageRole || sample.pageRole || sample.role || sample.contextRole || null,
pageId: row.pageId || sample.pageId || sample.contextId || null,
sessionId: row.sessionId || sample.sessionId || sample.workbenchSessionId || null,
};
rows.push(normalized);
const key = traceRowGroupKey(normalized);
if (!groups.has(key)) groups.set(key, []);
groups.get(key).push(normalized);
}
}
const slackSeconds = Math.max(1, Number(alertThresholds?.turnTimingSampleSlackSeconds || 0));
for (const groupRows of groups.values()) {
const sorted = groupRows.slice().sort((a, b) => {
if (a.sampleIndex !== b.sampleIndex) return a.sampleIndex - b.sampleIndex;
return (a.rowIndex ?? 0) - (b.rowIndex ?? 0);
});
let previous = null;
for (const row of sorted) {
if (previous) {
const reasons = [];
if (Number.isFinite(previous.totalSeconds) && Number.isFinite(row.totalSeconds) && row.totalSeconds + slackSeconds < previous.totalSeconds) {
reasons.push('total-decreased');
}
if (Number.isFinite(previous.projectedSeq) && Number.isFinite(row.projectedSeq) && row.projectedSeq < previous.projectedSeq) {
reasons.push('projected-seq-decreased');
}
if (Number.isFinite(previous.clockSeconds) && Number.isFinite(row.clockSeconds)) {
const diff = previous.clockSeconds - row.clockSeconds;
if (diff > slackSeconds && diff < 43200) reasons.push('clock-decreased');
}
if (Number.isFinite(previous.timestampMs) && Number.isFinite(row.timestampMs) && row.timestampMs + slackSeconds * 1000 < previous.timestampMs) {
reasons.push('timestamp-decreased');
}
if (reasons.length) {
orderAnomalies.push({
sampleIndex: row.sampleIndex,
sampleSeq: row.sampleSeq,
timestamp: row.timestamp,
pageRole: row.pageRole,
pageId: row.pageId,
sessionId: row.sessionId,
traceId: row.traceId || previous.traceId || null,
previousRowIndex: previous.rowIndex,
currentRowIndex: row.rowIndex,
reasons,
previousTotalSeconds: previous.totalSeconds,
currentTotalSeconds: row.totalSeconds,
previousProjectedSeq: previous.projectedSeq,
currentProjectedSeq: row.projectedSeq,
previousSourceSeq: previous.sourceSeq,
currentSourceSeq: row.sourceSeq,
previousEventSeq: previous.eventSeq,
currentEventSeq: row.eventSeq,
previousClockSeconds: previous.clockSeconds,
currentClockSeconds: row.clockSeconds,
previousTimestampMs: previous.timestampMs,
currentTimestampMs: row.timestampMs,
previousPreview: previous.preview,
currentPreview: row.preview,
});
}
}
previous = row;
}
for (let index = 0; index < sorted.length; index += 1) {
const row = sorted[index];
if (!row.isCompletion) continue;
const later = sorted.slice(index + 1).find((candidate) => {
if (candidate.isCompletion && candidate.preview === row.preview) return false;
return Number.isFinite(candidate.totalSeconds) || Number.isFinite(candidate.clockSeconds) || Number.isFinite(candidate.projectedSeq);
});
if (later) {
completionNotLast.push({
sampleIndex: row.sampleIndex,
sampleSeq: row.sampleSeq,
timestamp: row.timestamp,
pageRole: row.pageRole,
pageId: row.pageId,
sessionId: row.sessionId,
traceId: row.traceId || later.traceId || null,
completionRowIndex: row.rowIndex,
laterRowIndex: later.rowIndex,
completionTotalSeconds: row.totalSeconds,
laterTotalSeconds: later.totalSeconds,
completionProjectedSeq: row.projectedSeq,
laterProjectedSeq: later.projectedSeq,
completionSourceSeq: row.sourceSeq,
laterSourceSeq: later.sourceSeq,
completionEventSeq: row.eventSeq,
laterEventSeq: later.eventSeq,
completionPreview: row.preview,
laterPreview: later.preview,
});
}
}
}
return {
summary: {
sampleCount: samples.length,
traceRowCount: rows.length,
orderAnomalyCount: orderAnomalies.length,
completionNotLastCount: completionNotLast.length,
},
rows: rows.slice(0, 200),
orderAnomalies: orderAnomalies.slice(0, 100),
completionNotLast: completionNotLast.slice(0, 100),
};
}
function buildCodeAgentCardDurationUnderreportedMetrics(samples, timeline) {
const findings = [];
const slackSeconds = Math.max(5, Number(alertThresholds?.turnTimingSampleSlackSeconds || 0));
for (let sampleIndex = 0; sampleIndex < samples.length; sampleIndex += 1) {
const sample = samples[sampleIndex] || {};
const cards = codeAgentCardsForSample(sample);
if (!cards.length) continue;
const traceRows = traceTimingRowsForSample(sample, timeline[sampleIndex] || {});
const terminalCards = cards.filter((card) => isCodeAgentCardTerminal(card));
const sampleText = sampleVisibleText(sample, timeline[sampleIndex] || {});
for (const card of terminalCards) {
const cardText = codeAgentCardText(card);
const parsedCardSeconds = parseTotalElapsedSeconds(cardText).filter(Number.isFinite);
const cardSeconds = Number.isFinite(Number(card.totalElapsedSeconds)) ? Number(card.totalElapsedSeconds) : parsedCardSeconds.length > 0 ? Math.max(...parsedCardSeconds) : NaN;
if (!Number.isFinite(cardSeconds)) continue;
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 && 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));
if (strongest.seconds > cardSeconds + tolerance) {
findings.push({
sampleIndex,
...timingFindingMeta(strongest.kind, timingStatusFromTurnStatus(card.status || card.state || card.phase, "business-turn-completed"), {
cardElapsedSource: "dom-card-total-elapsed",
expectedElapsedSource: strongest.kind,
}),
timestamp: sample.timestamp || sample.collectedAt || sample.time || null,
pageRole: card.pageRole || sample.pageRole || sample.role || sample.contextRole || null,
pageId: card.pageId || sample.pageId || sample.contextId || null,
sessionId: card.sessionId || sample.sessionId || sample.workbenchSessionId || null,
traceId: card.traceId || strongest.traceId || null,
status: card.status || card.state || card.phase || null,
cardTotalElapsedSeconds: cardSeconds,
expectedElapsedSeconds: strongest.seconds,
deltaSeconds: strongest.seconds - cardSeconds,
toleranceSeconds: tolerance,
evidenceKind: strongest.kind,
evidencePreview: strongest.preview,
cardPreview: card.preview || compactOneLine(cardText || ''),
});
}
}
}
return findings.slice(0, 100);
}
function buildCodeAgentCardDurationMismatchMetrics(samples, timeline) {
const findings = [];
const slackSeconds = Math.max(5, Number(alertThresholds?.turnTimingSampleSlackSeconds || 0));
for (let sampleIndex = 0; sampleIndex < samples.length; sampleIndex += 1) {
const sample = samples[sampleIndex] || {};
const cards = codeAgentCardsForSample(sample);
if (!cards.length) continue;
const traceRows = traceTimingRowsForSample(sample, timeline[sampleIndex] || {});
const terminalCards = cards.filter((card) => isCodeAgentCardTerminal(card));
const sampleText = sampleVisibleText(sample, timeline[sampleIndex] || {});
for (const card of terminalCards) {
const cardText = codeAgentCardText(card);
const parsedCardSeconds = parseTotalElapsedSeconds(cardText).filter(Number.isFinite);
const cardSeconds = Number.isFinite(Number(card.totalElapsedSeconds)) ? Number(card.totalElapsedSeconds) : parsedCardSeconds.length > 0 ? Math.max(...parsedCardSeconds) : NaN;
if (!Number.isFinite(cardSeconds)) continue;
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 && 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';
const tolerance = Math.max(slackSeconds, Math.ceil(strongest.seconds * 0.05));
const signedDelta = Number((cardSeconds - strongest.seconds).toFixed(3));
const absoluteDelta = Math.abs(signedDelta);
const underreported = strongest.seconds > cardSeconds + tolerance;
const overreported = exactEvidence && cardSeconds > strongest.seconds + tolerance;
if (!underreported && !overreported) continue;
findings.push({
sampleIndex,
...timingFindingMeta(strongest.kind, timingStatusFromTurnStatus(card.status || card.state || card.phase, "business-turn-completed"), {
cardElapsedSource: "dom-card-total-elapsed",
expectedElapsedSource: strongest.kind,
}),
timestamp: sample.timestamp || sample.collectedAt || sample.time || sample.ts || null,
pageRole: card.pageRole || sample.pageRole || sample.role || sample.contextRole || null,
pageId: card.pageId || sample.pageId || sample.contextId || null,
sessionId: card.sessionId || sample.sessionId || sample.workbenchSessionId || null,
traceId: card.traceId || strongest.traceId || null,
status: card.status || card.state || card.phase || null,
direction: underreported ? 'underreported' : 'overreported',
cardTotalElapsedSeconds: cardSeconds,
expectedElapsedSeconds: strongest.seconds,
signedDeltaSeconds: signedDelta,
deltaSeconds: Number(absoluteDelta.toFixed(3)),
toleranceSeconds: tolerance,
evidenceKind: strongest.kind,
exactEvidence,
evidencePreview: strongest.preview,
cardPreview: card.preview || compactOneLine(cardText || ''),
});
}
}
return findings.slice(0, 100);
}
function traceTimingRowsForSample(sample, timelineItem) {
const rows = [];
const seen = new Set();
const appendRowsFromText = (text, source, baseIndex, meta = {}) => {
for (const extracted of extractTraceRowsFromText(text, source, baseIndex, meta)) {
const key = [extracted.pageRole || '', extracted.pageId || '', extracted.rowIndex, extracted.preview].join('|');
if (seen.has(key)) continue;
seen.add(key);
rows.push(extracted);
}
};
for (const candidate of traceRowCandidateArrays(sample, timelineItem)) {
const array = Array.isArray(candidate.rows) ? candidate.rows : [];
array.forEach((item, index) => {
if (typeof item === 'string') {
appendRowsFromText(item, candidate.source, index, candidate.meta || {});
return;
}
if (!item || typeof item !== 'object') return;
const text = objectText(item);
if (!text) return;
appendRowsFromText(text, candidate.source, Number.isFinite(Number(item.index)) ? Number(item.index) : index, {
...(candidate.meta || {}),
pageRole: item.pageRole || item.role || candidate.meta?.pageRole || null,
pageId: item.pageId || item.contextId || candidate.meta?.pageId || null,
sessionId: item.sessionId || item.workbenchSessionId || candidate.meta?.sessionId || null,
traceId: item.traceId || item.trace_id || candidate.meta?.traceId || null,
projectedSeq: item.projectedSeq ?? item.projected_seq ?? item.projectedSequence ?? null,
sourceSeq: item.sourceSeq ?? item.source_seq ?? item.sourceSequence ?? null,
eventSeq: item.eventSeq ?? item.event_seq ?? item.sequence ?? null,
eventTimestamp: item.eventTimestamp ?? item.event_ts ?? item.timestamp ?? item.ts ?? null,
eventTimeText: item.eventTimeText ?? item.timeText ?? null,
eventKind: item.eventKind ?? item.kind ?? item.status ?? null,
});
});
}
if (!rows.length) {
appendRowsFromText(sampleVisibleText(sample, timelineItem), 'visible-text', 0, {
pageRole: sample.pageRole || sample.role || sample.contextRole || null,
pageId: sample.pageId || sample.contextId || null,
sessionId: sample.sessionId || sample.workbenchSessionId || null,
});
}
rows.sort((a, b) => (a.rowIndex ?? 0) - (b.rowIndex ?? 0));
return rows;
}
function extractTraceRowsFromText(text, source, baseIndex, meta = {}) {
const result = [];
const normalized = String(text || '').replace(/\r/g, '\n');
if (!normalized.trim()) return result;
const lines = normalized.split('\n').map((line) => line.trim()).filter(Boolean);
for (let index = 0; index < lines.length; index += 1) {
const line = lines[index];
if (!traceLineLooksRelevant(line)) continue;
const nextLine = index + 1 < lines.length && !/^\d{1,2}:\d{2}:\d{2}\b/.test(lines[index + 1]) ? lines[index + 1] : '';
const preview = compactOneLine(nextLine ? line + ' ' + nextLine : line);
result.push(normalizeTraceTimingRow(preview, source, Number(baseIndex || 0) * 1000 + index, meta));
}
return result;
}
function normalizeTraceTimingRow(text, source, rowIndex, meta = {}) {
const preview = compactOneLine(text).slice(0, 240);
const projectedSeq = firstFiniteNumber(meta.projectedSeq, parseTraceRowProjectedSeq(preview));
const sourceSeq = firstFiniteNumber(meta.sourceSeq);
const eventSeq = firstFiniteNumber(meta.eventSeq);
const timestampMs = parseTraceRowTimestampMs(meta.eventTimestamp || meta.eventTimeText || preview);
return {
source,
rowIndex,
preview,
pageRole: meta.pageRole || null,
pageId: meta.pageId || null,
sessionId: meta.sessionId || null,
traceId: meta.traceId || parseTraceRowTraceId(preview),
clockSeconds: parseTraceRowClockSeconds(preview) ?? parseTraceRowClockSeconds(meta.eventTimeText || ""),
timestampMs,
totalSeconds: parseTraceRowTotalSeconds(preview),
projectedSeq,
sourceSeq,
eventSeq,
eventTimestamp: meta.eventTimestamp || null,
eventTimeText: meta.eventTimeText || null,
eventKind: meta.eventKind || null,
isCompletion: traceRowIsTerminalCompletionText(preview, meta.eventKind || ""),
};
}
function traceRowIsTerminalCompletionText(preview, eventKind = "") {
const value = [preview, eventKind].map((item) => String(item || "")).join(" ");
if (/\bnon[-_ ]?terminal\b/i.test(value)) return false;
if (/轮次完成|turn\s+completed|completed\s+turn|backend[_: -]?turn[_: -]?finished/i.test(value)) return true;
return /\bterminal(?:[_: -]?status|Status)?\s*[=: -]\s*(?:completed|failed|cancelled|canceled|timeout)\b/i.test(value);
}
function traceRowIsRoundCompletionText(preview, eventKind = "") {
const value = [preview, eventKind].map((item) => String(item || "")).join(" ");
if (/\bnon[-_ ]?terminal\b/i.test(value)) return false;
return /轮次完成|turn\s+completed|completed\s+turn|backend[_: -]?turn[_: -]?finished/i.test(value);
}
function traceRowCandidateArrays(sample, timelineItem) {
const candidates = [];
const pushArray = (rows, source, meta = {}) => {
if (Array.isArray(rows) && rows.length) candidates.push({ rows, source, meta });
};
const directSources = [
[sample?.traceRows, 'sample.traceRows'],
[sample?.eventRows, 'sample.eventRows'],
[sample?.activityRows, 'sample.activityRows'],
[sample?.timelineRows, 'sample.timelineRows'],
[sample?.dom?.traceRows, 'sample.dom.traceRows'],
[sample?.dom?.eventRows, 'sample.dom.eventRows'],
[sample?.dom?.activityRows, 'sample.dom.activityRows'],
[sample?.dom?.timelineRows, 'sample.dom.timelineRows'],
[timelineItem?.traceRows, 'timeline.traceRows'],
[timelineItem?.eventRows, 'timeline.eventRows'],
[timelineItem?.activityRows, 'timeline.activityRows'],
[timelineItem?.rows, 'timeline.rows'],
[timelineItem?.events, 'timeline.events'],
];
for (const [rows, source] of directSources) pushArray(rows, source, {});
if (!candidates.length) collectNamedTraceArrays(sample, candidates, 'sample', 0);
if (!candidates.length) collectNamedTraceArrays(timelineItem, candidates, 'timeline', 0);
return candidates;
}
function collectNamedTraceArrays(value, candidates, path, depth) {
if (!value || depth > 5) return;
if (Array.isArray(value)) {
const pathLooksTrace = /trace|timeline|activity|event|log|record/i.test(path);
const valueLooksTrace = value.slice(0, 5).some((item) => traceLineLooksRelevant(typeof item === 'string' ? item : objectText(item)));
if (pathLooksTrace || valueLooksTrace) candidates.push({ rows: value, source: path, meta: {} });
return;
}
if (typeof value !== 'object') return;
for (const [key, child] of Object.entries(value)) {
if (!child) continue;
if (Array.isArray(child)) {
const childPath = path + '.' + key;
const pathLooksTrace = /trace|timeline|activity|event|log|record/i.test(key);
const valueLooksTrace = child.slice(0, 5).some((item) => traceLineLooksRelevant(typeof item === 'string' ? item : objectText(item)));
if (pathLooksTrace || valueLooksTrace) candidates.push({ rows: child, source: childPath, meta: {} });
continue;
}
if (typeof child === 'object' && /dom|trace|timeline|activity|event|log|record|page|card|message|panel|diagnostic/i.test(key)) {
collectNamedTraceArrays(child, candidates, path + '.' + key, depth + 1);
}
}
}
function traceLineLooksRelevant(text) {
const value = String(text || '').trim();
if (!value) return false;
if (/^\d{1,2}:\d{2}:\d{2}\b/.test(value)) return true;
if (/\btotal=\d/.test(value)) return true;
if (/轮次完成(总耗时/.test(value)) return true;
if (/\bseq(?:uence)?[=:]\s*\d+/i.test(value)) return true;
return false;
}
function parseTraceRowClockSeconds(text) {
const match = String(text || '').match(/^\s*(\d{1,2}):(\d{2}):(\d{2})\b/);
if (!match) return null;
return Number(match[1]) * 3600 + Number(match[2]) * 60 + Number(match[3]);
}
function parseTraceRowTotalSeconds(text) {
const value = String(text || '');
const totalMatch = value.match(/\btotal=([0-9:.]+)/i);
if (totalMatch) return parseTraceDurationSeconds(totalMatch[1]);
const completionMatch = value.match(/总耗时\s*([0-9:.]+)/);
if (completionMatch) return parseTraceDurationSeconds(completionMatch[1]);
return null;
}
function parseTraceDurationSeconds(value) {
const text = String(value || '').trim();
if (!text) return null;
const parts = text.split(':').map((part) => Number(part));
if (parts.some((part) => !Number.isFinite(part))) return null;
if (parts.length === 3) return parts[0] * 3600 + parts[1] * 60 + parts[2];
if (parts.length === 2) return parts[0] * 60 + parts[1];
if (parts.length === 1) return parts[0];
return null;
}
function parseTraceRowProjectedSeq(text) {
const value = String(text || '');
const match = value.match(/\b(?:projected[_-]?seq|seq(?:uence)?|event[_-]?seq)\s*[=:]\s*(\d+)/i);
return match ? Number(match[1]) : null;
}
function parseTraceRowTimestampMs(value) {
const text = String(value || '').trim();
if (!text) return null;
const parsed = Date.parse(text);
return Number.isFinite(parsed) ? parsed : null;
}
function firstFiniteNumber(...values) {
for (const value of values) {
const numeric = Number(value);
if (Number.isFinite(numeric)) return numeric;
}
return null;
}
function parseTraceRowTraceId(text) {
const match = String(text || '').match(/\b(?:trace_id=|traceId[:=]?\s*)(trc_[a-z0-9_-]+|[a-f0-9]{16,})\b/i);
return match ? match[1] : null;
}
function traceRowGroupKey(row) {
const identity = row.traceId ? 'trace:' + row.traceId : 'sample:' + (row.sampleIndex ?? '-') + ':' + (row.source || 'unknown');
return [row.pageRole || '', row.pageId || '', row.sessionId || '', row.source || '', row.sampleIndex ?? '', identity].join('|');
}
function traceRowMatchesCard(row, card, terminalCardCount) {
if (!row) return false;
if (card.traceId) return row.traceId === card.traceId;
if (row.traceId) return false;
if (row.sessionId && card.sessionId) return terminalCardCount === 1 && row.sessionId === card.sessionId;
if (terminalCardCount === 1) return true;
return false;
}
function maxTraceDurationEvidence(rows) {
const finiteTotals = rows.map((row) => Number(row.totalSeconds)).filter(Number.isFinite);
const finiteClocks = rows.map((row) => Number(row.clockSeconds)).filter(Number.isFinite);
const evidences = [];
if (finiteTotals.length) {
const maxTotal = Math.max(...finiteTotals);
const source = rows.find((row) => Number(row.totalSeconds) === maxTotal);
const exact = source?.isCompletion === true;
evidences.push({ kind: exact ? 'trace-completion-total' : 'trace-total', seconds: maxTotal, traceId: source?.traceId || null, preview: source?.preview || '', exact });
}
if (finiteClocks.length >= 2) {
const minClock = Math.min(...finiteClocks);
const maxClock = Math.max(...finiteClocks);
const span = maxClock - minClock;
if (span >= 0 && span < 43200) evidences.push({ kind: 'trace-clock-span', seconds: span, traceId: null, preview: 'visible trace row clock span', exact: false });
}
if (!evidences.length) return null;
evidences.sort((a, b) => b.seconds - a.seconds);
return evidences[0];
}
function maxSelfReportedDurationEvidence(text) {
const value = String(text || '');
const lines = value.split(/\n+/).map((line) => line.trim()).filter(Boolean);
let best = null;
for (let index = 0; index < lines.length; index += 1) {
const line = lines[index];
const previous = index > 0 ? lines[index - 1] : '';
const next = index + 1 < lines.length ? lines[index + 1] : '';
const candidateText = selfReportedDurationCandidateText(previous, line, next);
if (!candidateText) continue;
const seconds = parseSelfReportedRoundDurationSeconds(candidateText);
if (!Number.isFinite(seconds)) continue;
if (!best || seconds > best.seconds) {
best = { kind: 'final-response-duration', seconds, preview: compactOneLine(candidateText).slice(0, 240), exact: true };
}
}
return best;
}
function selfReportedDurationCandidateText(previous, line, next) {
const current = String(line || '');
const before = String(previous || '');
const after = String(next || '');
const windowText = [before, current, after].filter(Boolean).join(' ');
const hasDurationKeyword = /耗时|用时|duration|elapsed/i.test(current);
const hasNearbyDurationHeading = /(?:本轮|整轮|全程|任务|round|turn)?\s*(?:耗时|用时|duration|elapsed)/i.test(before)
|| /(?:本轮|整轮|全程|任务|round|turn)?\s*(?:耗时|用时|duration|elapsed)/i.test(after);
const hasDurationValue = /(?:约|大约|around|about)?\s*\d+(?:\.\d+)?\s*(?:小时|分钟|分|秒|hour|hours|hr|hrs|min|mins|minute|minutes|sec|secs|second|seconds)/i.test(windowText);
const hasRoundContext = /本轮|整轮|全程|从.+到|全部通过|smoke|benchmark|round|turn|completed|passed/i.test(windowText);
if (hasDurationKeyword && hasDurationValue) return current;
if (hasNearbyDurationHeading && hasDurationValue) return windowText;
if (hasRoundContext && hasDurationValue && /(?:约|大约|around|about)\s*\d|\d+(?:\.\d+)?\s*(?:分钟|小时|minute|hour)/i.test(current)) return windowText;
return '';
}
function parseSelfReportedRoundDurationSeconds(text) {
const value = String(text || '');
const clock = value.match(/(?:耗时|用时|duration|elapsed)[^0-9]{0,24}(\d{1,2}:\d{2}:\d{2}|\d{1,2}:\d{2})/i);
if (clock) return parseTraceDurationSeconds(clock[1]);
const hour = value.match(/(?:约|大约|around|about)?\s*(\d+(?:\.\d+)?)\s*(?:小时|hour|hours|hr|hrs)/i);
const minute = value.match(/(?:约|大约|around|about)?\s*(\d+(?:\.\d+)?)\s*(?:分钟|分|min|mins|minute|minutes)/i);
const second = value.match(/(?:约|大约|around|about)?\s*(\d+(?:\.\d+)?)\s*(?:秒|sec|secs|second|seconds)/i);
let total = 0;
if (hour) total += Number(hour[1]) * 3600;
if (minute) total += Number(minute[1]) * 60;
if (second) total += Number(second[1]);
return total > 0 ? total : null;
}
function sampleVisibleText(sample, timelineItem) {
const chunks = [];
for (const source of [sample?.visibleText, sample?.text, sample?.innerText, sample?.dom?.visibleText, sample?.dom?.text, timelineItem?.visibleText, timelineItem?.text, timelineItem?.message, timelineItem?.summary]) {
if (typeof source === 'string' && source.trim()) chunks.push(source);
}
return chunks.join('\n');
}
function objectText(value) {
if (!value || typeof value !== 'object') return typeof value === 'string' ? value : '';
const keys = ['text', 'innerText', 'visibleText', 'label', 'title', 'summary', 'message', 'content', 'body', 'preview', 'description'];
const chunks = [];
for (const key of keys) {
const part = value[key];
if (typeof part === 'string' && part.trim()) chunks.push(part);
}
return chunks.join('\n');
}
function compactOneLine(value) {
return String(value || '').replace(/\s+/g, ' ').trim();
}
function buildCodeAgentCardTimingMetrics(samples, timeline, turnTiming) {
const missingElapsed = [];
const missingRecentUpdate = [];
const cardRows = [];
for (let index = 0; index < (Array.isArray(samples) ? samples : []).length; index += 1) {
const sample = samples[index];
const timelineItem = timeline[index] || {};
for (const card of codeAgentCardsForSample(sample)) {
const text = codeAgentCardText(card);
const totalElapsedValues = parseTotalElapsedSeconds(card?.durationText).filter(Number.isFinite);
const recentUpdateValues = parseRecentUpdateSeconds(card?.activityText).filter(Number.isFinite);
const terminal = isCodeAgentCardTerminal(card);
const row = {
...ref(sample),
promptIndex: timelineItem.promptIndex ?? 0,
source: card.source ?? "turn",
status: card.status ?? null,
messageId: card.messageId ?? null,
traceId: card.traceId ?? firstTraceId([text]),
sessionId: card.sessionId ?? sample.sessionId ?? sample.workbenchSessionId ?? sample.routeSessionId ?? sample.activeSessionId ?? null,
durationText: limitText(card.durationText, 120),
activityText: limitText(card.activityText, 120),
totalElapsedSeconds: totalElapsedValues.length > 0 ? Math.max(...totalElapsedValues) : null,
recentUpdateSeconds: recentUpdateValues.length > 0 ? Math.max(...recentUpdateValues) : null,
terminal,
textHash: card.textHash ?? sha256(text),
textPreview: limitText(text, 180),
valuesRedacted: true
};
cardRows.push(row);
if (row.totalElapsedSeconds === null) missingElapsed.push(row);
if (!terminal && row.recentUpdateSeconds === null) missingRecentUpdate.push(row);
}
}
const roundCompletion = buildRoundCompletionMetrics(samples, timeline, turnTiming);
return {
summary: {
cardSampleCount: cardRows.length,
terminalCardSampleCount: cardRows.filter((item) => item.terminal).length,
runningCardSampleCount: cardRows.filter((item) => !item.terminal).length,
missingElapsedCount: missingElapsed.length,
missingRecentUpdateCount: missingRecentUpdate.length,
roundCompletionEventCount: roundCompletion.events.length,
roundCompletionElapsedMismatchCount: roundCompletion.elapsedMismatches.length,
roundCompletionFinalResponseMissingCount: roundCompletion.finalResponseMissing.length,
roundCompletionPostTimingChangeCount: roundCompletion.postCompletionTimingChanges.length,
roundCompletionPostRecentUpdateVisibleCount: roundCompletion.postCompletionRecentUpdateVisible.length,
elapsedMismatchToleranceSeconds: alertThresholds.turnTimingSampleSlackSeconds,
},
cardSamples: cardRows.slice(0, 200),
missingElapsed: missingElapsed.slice(0, 200),
missingRecentUpdate: missingRecentUpdate.slice(0, 200),
roundCompletion,
valuesRedacted: true
};
}
function codeAgentCardsForSample(sample) {
const turnCards = (Array.isArray(sample?.turns) ? sample.turns : [])
.map((item) => ({ ...item, source: item?.source || "turn" }))
.filter(isCodeAgentCardLike);
if (turnCards.length > 0) return turnCards;
return (Array.isArray(sample?.messages) ? sample.messages : [])
.map((item) => ({ ...item, source: item?.source || "message" }))
.filter(isCodeAgentCardLike);
}
function isCodeAgentCardLike(item) {
const text = codeAgentCardText(item);
const role = String(item?.dataRole || item?.role || "").toLowerCase();
if (/agent|assistant/u.test(role)) return true;
return /Code Agent|运行记录|耗时|最近\s*(?:\d+|一|两|三)|轮次完成|trace_id=trc_/iu.test(text);
}
function codeAgentCardText(item) {
return [
item?.durationText,
item?.activityText,
item?.text,
item?.textPreview,
item?.status
].map((value) => String(value || "")).filter((value) => value.trim().length > 0).join("\n");
}
function isCodeAgentCardTerminal(item) {
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 timingFindingMeta(sourceOfTruth, status, extra = {}) {
return {
timingSourceOfTruth: sourceOfTruth,
timingStatus: status || "non-blocking-timing-alert",
timingAlert: true,
blocking: false,
...extra,
};
}
function timingStatusFromTurnStatus(status, fallback = "observer-timeout") {
const normalized = String(status ?? "").trim().toLowerCase().replace(/_/gu, "-");
if (isSuccessfulTurnStatus(normalized)) return "business-turn-completed";
if (isTerminalTurnStatus(normalized)) return "scenario-incomplete";
if (isActiveTurnStatus(normalized)) return "observer-timeout";
return fallback;
}
function timingStatusFromRows(rows, fallback = "observer-timeout") {
const list = Array.isArray(rows) ? rows : [];
if (list.some((item) => isSuccessfulTurnStatus(item?.status ?? item?.toStatus ?? item?.fromStatus))) return "business-turn-completed";
if (list.some((item) => Number(item?.finalTextSamples ?? 0) > 0)) return "business-turn-completed";
if (list.some((item) => isTerminalTurnStatus(item?.status ?? item?.toStatus ?? item?.fromStatus))) return "scenario-incomplete";
if (list.some((item) => isActiveTurnStatus(item?.status ?? item?.toStatus ?? item?.fromStatus))) return "observer-timeout";
return fallback;
}
function isSuccessfulTurnStatus(status) {
const normalized = String(status ?? "").trim().toLowerCase().replace(/_/gu, "-");
return ["completed", "complete", "succeeded", "success", "done", "ok", "passed"].includes(normalized);
}
function isActiveTurnStatus(value) {
const status = String(value ?? "").trim().toLowerCase().replace(/_/gu, "-");
return ["pending", "running", "queued", "admitted", "dispatching", "in-progress", "inprogress", "executing", "progress", "thinking", "working", "active", "streaming", "created", "started"].includes(status);
}
function buildRoundCompletionMetrics(samples, timeline, turnTiming) {
const events = [];
const elapsedMismatchToleranceSeconds = Math.max(5, Number(alertThresholds.turnTimingSampleSlackSeconds || 0));
for (let index = 0; index < (Array.isArray(samples) ? samples : []).length; index += 1) {
const sample = samples[index];
const timelineItem = timeline[index] || {};
for (const event of roundCompletionEventsForSample(sample, timelineItem)) events.push(event);
}
const completionEvents = dedupeRoundCompletionEvents(events);
const elapsedMismatches = [];
const finalResponseMissing = [];
const postCompletionTimingChanges = [];
const postCompletionRecentUpdateVisible = [];
for (const event of completionEvents) {
const sampleIndex = samples.findIndex((sample) => sample?.seq === event.seq && sample?.pageRole === event.pageRole && sample?.pageId === event.pageId);
const sameSample = sampleIndex >= 0 ? samples[sampleIndex] : null;
const sameTimelineItem = sampleIndex >= 0 ? timeline[sampleIndex] || {} : {};
const cards = sameSample ? cardMetricItemsForCompletion(sameSample, sameTimelineItem, event) : [];
const bestCard = cards.filter((card) => Number.isFinite(Number(card.totalElapsedSeconds)))[0] || null;
if (Number.isFinite(Number(event.elapsedSeconds)) && bestCard) {
const delta = Math.abs(Number(bestCard.totalElapsedSeconds) - Number(event.elapsedSeconds));
if (delta > elapsedMismatchToleranceSeconds) {
elapsedMismatches.push({
...eventRef(event),
...timingFindingMeta("trace-round-completion-total", timingStatusFromTurnStatus(bestCard.status, "business-turn-completed"), {
cardElapsedSource: bestCard.source ?? "dom-card",
completionElapsedSource: "trace-round-completion",
}),
cardTotalElapsedSeconds: Number(bestCard.totalElapsedSeconds),
completionElapsedSeconds: Number(event.elapsedSeconds),
deltaSeconds: Number(delta.toFixed(3)),
toleranceSeconds: elapsedMismatchToleranceSeconds,
cardTraceId: bestCard.traceId ?? null,
cardMessageId: bestCard.messageId ?? null,
valuesRedacted: true
});
}
}
if (!(sameSample && sampleHasTerminalAgentResultCard(sameSample, event)) && !(sameSample && sampleHasVisibleFinalResponse(sameSample, event)) && !hasFinalResponseAfterCompletion(samples, timeline, event)) {
finalResponseMissing.push({
...eventRef(event),
completionElapsedSeconds: event.elapsedSeconds,
finalResponseProbe: sameSample ? terminalAgentResultProbe(sameSample, event) : { ok: false, reason: "sample-not-found" },
valuesRedacted: true
});
}
const postTiming = detectPostCompletionTimingChanges(turnTiming, event);
postCompletionTimingChanges.push(...postTiming.timingChanges);
postCompletionRecentUpdateVisible.push(...postTiming.recentUpdateVisible);
}
return {
events: completionEvents.slice(0, 200),
elapsedMismatches: dedupeRoundCompletionRows(elapsedMismatches).slice(0, 200),
finalResponseMissing: dedupeRoundCompletionRows(finalResponseMissing).slice(0, 200),
postCompletionTimingChanges: dedupeRoundCompletionRows(postCompletionTimingChanges).slice(0, 200),
postCompletionRecentUpdateVisible: dedupeRoundCompletionRows(postCompletionRecentUpdateVisible).slice(0, 200),
valuesRedacted: true
};
}
function roundCompletionEventsForSample(sample, timelineItem) {
const rows = [];
for (const item of traceTimingRowsForSample(sample, timelineItem)) {
const text = String(item?.preview || item?.text || item?.textPreview || "");
if (!traceRowIsRoundCompletionText(text, item?.eventKind || "")) continue;
const elapsedValues = [
Number(item?.totalSeconds),
...parseTotalElapsedSeconds(text).filter(Number.isFinite)
].filter(Number.isFinite);
const attributed = inferRoundCompletionCard(sample, text);
rows.push({
...ref(sample),
promptIndex: timelineItem.promptIndex ?? 0,
traceId: item?.traceId ?? firstTraceId([text]) ?? attributed?.traceId ?? null,
messageId: item?.messageId ?? attributed?.messageId ?? null,
attributed: Boolean(item?.traceId || item?.messageId || attributed?.traceId || attributed?.messageId),
traceRowIndex: item?.rowIndex ?? item?.index ?? null,
elapsedSeconds: elapsedValues.length > 0 ? Math.max(...elapsedValues) : null,
timingSourceOfTruth: "trace-round-completion-total",
timingStatus: "business-turn-completed",
textHash: item?.textHash ?? sha256(text),
preview: limitText(text, 180),
valuesRedacted: true
});
}
return rows;
}
function inferRoundCompletionCard(sample, text) {
const cards = codeAgentCardsForSample(sample)
.filter((card) => isCodeAgentCardTerminal(card))
.filter((card) => card?.traceId || card?.messageId);
const textHash = sha256(String(text || ""));
const direct = cards.filter((card) => {
const cardText = codeAgentCardText(card);
return card?.textHash === textHash || cardText.includes(String(text || "").slice(0, 80)) || /轮次完成/iu.test(cardText);
});
const candidates = direct.length > 0 ? direct : cards;
if (candidates.length !== 1) return null;
const card = candidates[0];
return { traceId: card.traceId ?? null, messageId: card.messageId ?? card.id ?? null };
}
function cardMetricItemsForCompletion(sample, timelineItem, event) {
const metrics = turnMetricItems(sample, timelineItem)
.filter((item) => item.promptIndex === event.promptIndex)
.filter((item) => item.pageRole === event.pageRole || !event.pageRole)
.filter((item) => item.pageId === event.pageId || !event.pageId);
if (!event.traceId && !event.messageId) {
const withElapsed = metrics.filter((item) => Number.isFinite(Number(item.totalElapsedSeconds)));
return withElapsed.length === 1 ? withElapsed : [];
}
return metrics
.filter((item) => !event.traceId || !item.traceId || item.traceId === event.traceId)
.filter((item) => !event.messageId || !item.messageId || item.messageId === event.messageId)
.sort((left, right) => {
const leftTraceMatch = event.traceId && left.traceId === event.traceId ? 0 : 1;
const rightTraceMatch = event.traceId && right.traceId === event.traceId ? 0 : 1;
const leftMessageMatch = event.messageId && left.messageId === event.messageId ? 0 : 1;
const rightMessageMatch = event.messageId && right.messageId === event.messageId ? 0 : 1;
return leftTraceMatch - rightTraceMatch || leftMessageMatch - rightMessageMatch || String(left.source || "").localeCompare(String(right.source || ""));
});
}
function hasFinalResponseAfterCompletion(samples, timeline, event) {
if (!event.traceId && !event.messageId && !event.promptIndex) return true;
const eventTsMs = Date.parse(String(event.ts || ""));
for (let index = 0; index < (Array.isArray(samples) ? samples : []).length; index += 1) {
const sample = samples[index];
const tsMs = Date.parse(String(sample?.ts || ""));
if (Number.isFinite(eventTsMs) && Number.isFinite(tsMs) && tsMs < eventTsMs) continue;
if (sample?.pageRole !== event.pageRole) continue;
const sampleSession = sample?.routeSessionId || sample?.activeSessionId || null;
const eventSession = event.routeSessionId || event.activeSessionId || null;
if (sampleSession && eventSession && sampleSession !== eventSession) continue;
const promptIndex = timeline[index]?.promptIndex ?? 0;
if (!event.traceId && !event.messageId && event.promptIndex && promptIndex !== event.promptIndex) continue;
if (sampleHasTerminalAgentResultCard(sample, event)) return true;
if (sampleHasVisibleFinalResponse(sample, event)) return true;
}
return false;
}
function sampleHasTerminalAgentResultCard(sample, event = {}) {
return terminalAgentResultProbe(sample, event).ok === true;
}
function terminalAgentResultProbe(sample, event = {}) {
const decisions = [];
for (const item of codeAgentCardsForSample(sample)) {
const text = normalizedText(codeAgentCardText(item));
const traceMatched = Boolean(event.traceId && item?.traceId && item.traceId === event.traceId);
const decision = {
source: item?.source || null,
role: item?.role ?? null,
dataRole: item?.dataRole ?? null,
status: item?.status ?? null,
traceId: item?.traceId ?? null,
messageId: item?.messageId ?? null,
textBytes: Buffer.byteLength(text),
valuesRedacted: true
};
if (event.traceId && item?.traceId && item.traceId !== event.traceId) {
decisions.push({ ...decision, decision: "skip-trace" });
continue;
}
if (!traceMatched && event.messageId && item?.messageId && item.messageId !== event.messageId) {
decisions.push({ ...decision, decision: "skip-message" });
continue;
}
if (!isAssistantFinalResponseCandidate(item, item?.source || "turn")) {
decisions.push({ ...decision, decision: "skip-role" });
continue;
}
if (!isCodeAgentCardTerminal(item)) {
decisions.push({ ...decision, decision: "skip-non-terminal" });
continue;
}
if (text.length < 24) {
decisions.push({ ...decision, decision: "skip-short" });
continue;
}
decisions.push({ ...decision, decision: "accept-terminal-agent-card" });
return { ok: true, decisions: decisions.slice(-8), valuesRedacted: true };
}
return { ok: false, decisions: decisions.slice(-8), valuesRedacted: true };
}
function sampleHasVisibleFinalResponse(sample, event = {}) {
for (const [groupName, group] of [["messages", sample?.messages], ["turns", sample?.turns]]) {
if (!Array.isArray(group)) continue;
for (const item of group) {
const traceMatched = Boolean(event.traceId && item?.traceId && item.traceId === event.traceId);
if (event.traceId && item?.traceId && item.traceId !== event.traceId) continue;
if (!traceMatched && event.messageId && item?.messageId && item.messageId !== event.messageId) continue;
if (!isAssistantFinalResponseCandidate(item, groupName)) continue;
const text = normalizedText([item?.text, item?.textPreview].filter(Boolean).join(" "));
if (text.length < 24) continue;
if (groupName === "messages" && isTerminalTurnStatus(item?.status)) return true;
if (isDiagnosticText(text)) continue;
if (isFinalResultText(text)) return true;
if (/运行记录/iu.test(text) && /(?:已完成|完成|新增|实现|验证|测试|结果|README|文件|summary)/iu.test(text)) return true;
}
}
return false;
}
function normalizedDomRole(item) {
return String(item?.dataRole ?? item?.role ?? item?.ariaRole ?? "")
.trim()
.toLowerCase()
.replace(/[_\s]+/gu, "-");
}
function isAssistantFinalResponseCandidate(item, groupName) {
const role = normalizedDomRole(item);
if (/agent|assistant|code-agent|bot/iu.test(role)) return true;
if (/user|human|client|prompt/iu.test(role)) return false;
if (groupName === "turns") return isCodeAgentCardLike(item);
const text = codeAgentCardText(item);
if (isTerminalTurnStatus(item?.status) && /Code Agent|运行记录|assistant|agent/iu.test(text)) return true;
return false;
}
function detectPostCompletionTimingChanges(turnTiming, event) {
const timingChanges = [];
const recentUpdateVisible = [];
if (!event.traceId && !event.messageId) return { timingChanges, recentUpdateVisible };
const rows = Array.isArray(turnTiming?.rows) ? turnTiming.rows : [];
const columns = Array.isArray(turnTiming?.columns) ? turnTiming.columns : [];
const eventTsMs = Date.parse(String(event.ts || ""));
for (const column of columns) {
if (column.pageRole && event.pageRole && column.pageRole !== event.pageRole) continue;
if (event.traceId && column.traceId && column.traceId !== event.traceId) continue;
if (event.messageId && column.messageId && column.messageId !== event.messageId) continue;
if (event.promptIndex && column.promptIndex && column.promptIndex !== event.promptIndex && column.lastPromptIndex !== event.promptIndex) continue;
let previousTotal = null;
let previousRecent = null;
for (const row of rows) {
const rowTsMs = Date.parse(String(row.ts || ""));
if (Number.isFinite(eventTsMs) && Number.isFinite(rowTsMs) && rowTsMs < eventTsMs) continue;
if (row.pageRole && event.pageRole && row.pageRole !== event.pageRole) continue;
const cell = row.cells?.[column.id];
if (!cell) continue;
if (event.traceId && cell.traceId && cell.traceId !== event.traceId) continue;
if (event.messageId && cell.messageId && cell.messageId !== event.messageId) continue;
const total = cell.totalElapsedSeconds === null || cell.totalElapsedSeconds === undefined ? NaN : Number(cell.totalElapsedSeconds);
if (Number.isFinite(total)) {
if (previousTotal && Math.abs(total - previousTotal.value) > alertThresholds.turnTimingSampleSlackSeconds) {
timingChanges.push({
...eventRef(event),
columnId: column.id,
columnLabel: column.label,
metric: "totalElapsedSeconds",
fromSeq: previousTotal.seq,
fromTs: previousTotal.ts,
fromValue: previousTotal.value,
toSeq: row.seq ?? null,
toTs: row.ts ?? null,
toValue: total,
delta: Number((total - previousTotal.value).toFixed(3)),
toleranceSeconds: alertThresholds.turnTimingSampleSlackSeconds,
traceId: cell.traceId ?? column.traceId ?? event.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
valuesRedacted: true
});
}
previousTotal = { value: total, seq: row.seq ?? null, ts: row.ts ?? null };
}
const recent = cell.recentUpdateSeconds === null || cell.recentUpdateSeconds === undefined ? NaN : Number(cell.recentUpdateSeconds);
if (Number.isFinite(recent)) {
recentUpdateVisible.push({
...eventRef(event),
columnId: column.id,
columnLabel: column.label,
metric: "recentUpdateSeconds",
seq: row.seq ?? null,
ts: row.ts ?? null,
value: recent,
traceId: cell.traceId ?? column.traceId ?? event.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
valuesRedacted: true
});
if (previousRecent && recent !== previousRecent.value) {
timingChanges.push({
...eventRef(event),
columnId: column.id,
columnLabel: column.label,
metric: "recentUpdateSeconds",
fromSeq: previousRecent.seq,
fromTs: previousRecent.ts,
fromValue: previousRecent.value,
toSeq: row.seq ?? null,
toTs: row.ts ?? null,
toValue: recent,
delta: Number((recent - previousRecent.value).toFixed(3)),
traceId: cell.traceId ?? column.traceId ?? event.traceId ?? null,
messageId: cell.messageId ?? column.messageId ?? null,
valuesRedacted: true
});
}
previousRecent = { value: recent, seq: row.seq ?? null, ts: row.ts ?? null };
}
}
}
return { timingChanges, recentUpdateVisible };
}
function dedupeRoundCompletionEvents(rows) {
const result = [];
const seen = new Set();
for (const row of Array.isArray(rows) ? rows : []) {
const key = [
row?.pageRole ?? "",
row?.pageId ?? "",
row?.promptIndex ?? "",
row?.traceId ?? "",
row?.messageId ?? "",
row?.textHash ?? "",
row?.elapsedSeconds ?? ""
].join("|");
if (seen.has(key)) continue;
seen.add(key);
result.push(row);
}
return result;
}
function eventRef(event) {
return {
seq: event?.seq ?? null,
sampleGroupSeq: event?.sampleGroupSeq ?? null,
ts: event?.ts ?? null,
pageRole: event?.pageRole ?? null,
pageId: event?.pageId ?? null,
routeSessionId: event?.routeSessionId ?? null,
activeSessionId: event?.activeSessionId ?? null,
promptIndex: event?.promptIndex ?? null,
traceId: event?.traceId ?? null,
messageId: event?.messageId ?? null,
};
}
function dedupeRoundCompletionRows(rows) {
const result = [];
const seen = new Set();
for (const row of Array.isArray(rows) ? rows : []) {
const key = [
row?.seq ?? row?.fromSeq ?? "",
row?.toSeq ?? "",
row?.pageRole ?? "",
row?.promptIndex ?? "",
row?.traceId ?? "",
row?.metric ?? "",
row?.textHash ?? "",
row?.columnId ?? ""
].join("|");
if (seen.has(key)) continue;
seen.add(key);
result.push(row);
}
return result;
}
function turnMetricItems(sample, timelineItem) {
const promptIndex = timelineItem.promptIndex ?? 0;
const pageRole = sample?.pageRole || "control";
const pageId = sample?.pageId || "unknown-page";
const sessionKey = pageRole + ":" + pageId + ":" + (sample?.routeSessionId || sample?.activeSessionId || "unknown-session");
const roundKey = String(promptIndex);
const items = [];
if (Array.isArray(sample?.turns) && sample.turns.length > 0) {
for (const turn of sample.turns) {
const texts = turnTexts(turn);
const totalElapsedValues = texts.flatMap(parseTotalElapsedSeconds).filter(Number.isFinite);
const recentUpdateValues = texts.flatMap(parseRecentUpdateSeconds).filter(Number.isFinite);
const traceId = turn.traceId || firstTraceId(texts);
const messageId = turn.messageId || null;
const turnId = turn.turnId || traceId || null;
const stableId = traceId || messageId || turnId || null;
const domIndex = Number.isFinite(Number(turn.index)) ? Number(turn.index) : items.length;
const key = stableId
? "timing:" + sessionKey + ":id-" + stableId
: "turn:" + sessionKey + ":round-" + roundKey + ":dom-index-" + String(domIndex);
items.push({
key,
source: "turn",
pageRole,
pageId,
promptIndex,
traceId,
messageId,
turnId,
domIndex,
status: turn.status ?? null,
totalElapsedSeconds: totalElapsedValues.length > 0 ? Math.max(...totalElapsedValues) : null,
recentUpdateSeconds: recentUpdateValues.length > 0 ? Math.max(...recentUpdateValues) : null,
textHash: turn.textHash || sha256(texts.join("\n"))
});
}
return items;
}
if (Array.isArray(sample?.messages) && sample.messages.length > 0) {
for (const message of sample.messages) {
const text = [message?.durationText, message?.activityText].map((value) => String(value || "")).filter((value) => value.trim().length > 0).join("\n");
const totalElapsedValues = parseTotalElapsedSeconds(text).filter(Number.isFinite);
const recentUpdateValues = parseRecentUpdateSeconds(text).filter(Number.isFinite);
if (totalElapsedValues.length === 0 && recentUpdateValues.length === 0) continue;
const domIndex = Number.isFinite(Number(message.index)) ? Number(message.index) : items.length;
const traceId = message.traceId || firstTraceId([text]);
const messageId = message.messageId || null;
const stableId = traceId || messageId || message.turnId || null;
items.push({
key: stableId
? "timing:" + sessionKey + ":id-" + stableId
: "message:" + sessionKey + ":round-" + roundKey + ":dom-index-" + String(domIndex),
source: "message",
pageRole,
pageId,
promptIndex,
traceId,
messageId,
turnId: message.turnId || traceId || null,
domIndex,
status: message.status ?? null,
totalElapsedSeconds: totalElapsedValues.length > 0 ? Math.max(...totalElapsedValues) : null,
recentUpdateSeconds: recentUpdateValues.length > 0 ? Math.max(...recentUpdateValues) : null,
textHash: message.textHash || sha256(text)
});
}
if (items.length > 0) return items;
}
if (timelineItem.totalElapsedSeconds !== null || timelineItem.recentUpdateSeconds !== null) {
return [{
key: "aggregate:" + sessionKey + ":round-" + roundKey,
source: "aggregate",
pageRole,
pageId,
promptIndex,
traceId: null,
messageId: null,
domIndex: null,
status: null,
totalElapsedSeconds: timelineItem.totalElapsedSeconds ?? null,
recentUpdateSeconds: timelineItem.recentUpdateSeconds ?? null,
textHash: timelineItem.textDigest ?? null
}];
}
return [];
}
function turnTexts(turn) {
return [
turn?.durationText,
turn?.activityText
].map((value) => String(value || "")).filter((value) => value.trim().length > 0);
}
function firstTraceId(texts) {
for (const text of texts) {
const match = String(text || "").match(/\btrc_[A-Za-z0-9_-]+\b/u);
if (match) return match[0];
}
return null;
}
function buildRoundMetricSummaries(timeline, promptCommands, timing = {}) {
const rounds = [];
const timingColumns = Array.isArray(timing.columns) ? timing.columns : [];
const timingRows = Array.isArray(timing.rows) ? timing.rows : [];
const nonMonotonic = Array.isArray(timing.nonMonotonic) ? timing.nonMonotonic : [];
const totalElapsedForwardJumps = Array.isArray(timing.totalElapsedForwardJumps) ? timing.totalElapsedForwardJumps : [];
const elapsedZeroResets = Array.isArray(timing.elapsedZeroResets) ? timing.elapsedZeroResets : [];
const terminalElapsedGrowth = Array.isArray(timing.terminalElapsedGrowth) ? timing.terminalElapsedGrowth : [];
const recentUpdateResets = Array.isArray(timing.recentUpdateResets) ? timing.recentUpdateResets : [];
const recentUpdateSteps = Array.isArray(timing.recentUpdateSteps) ? timing.recentUpdateSteps : [];
for (let index = 0; index < promptCommands.length; index += 1) {
const promptIndex = index + 1;
const items = timeline.filter((item) => item.promptIndex === promptIndex);
const aggregateTotalElapsed = items.map((item) => item.totalElapsedSeconds).filter((value) => value !== null);
const aggregateRecentUpdate = items.map((item) => item.recentUpdateSeconds).filter((value) => value !== null);
const promptTurnTiming = roundPromptTurnTimingValues(timingRows, timingColumns, promptIndex, {
firstSeq: items[0]?.seq ?? null,
firstSampleAt: items[0]?.ts ?? null
});
const totalElapsed = promptTurnTiming.totalElapsed.length > 0 ? promptTurnTiming.totalElapsed : aggregateTotalElapsed;
const recentUpdate = promptTurnTiming.recentUpdate.length > 0 ? promptTurnTiming.recentUpdate : aggregateRecentUpdate;
const loadingCounts = items.map((item) => Number(item.loadingCount ?? 0)).filter(Number.isFinite);
const loadingOwners = new Set();
for (const item of items) {
for (const owner of Array.isArray(item.loadingOwners) ? item.loadingOwners : []) {
if (owner?.ownerKey) loadingOwners.add(owner.ownerKey);
}
}
const timingAnomalies = nonMonotonic.filter((item) => item.promptIndex === promptIndex);
const timingForwardJumps = totalElapsedForwardJumps.filter((item) => item.promptIndex === promptIndex);
const timingZeroResets = elapsedZeroResets.filter((item) => item.promptIndex === promptIndex);
const timingTerminalGrowth = terminalElapsedGrowth.filter((item) => item.promptIndex === promptIndex);
const timingResets = recentUpdateResets.filter((item) => item.promptIndex === promptIndex);
const timingSteps = recentUpdateSteps.filter((item) => item.promptIndex === promptIndex);
const terminalGrowthDeltas = timingTerminalGrowth.map((item) => Number(item.delta)).filter((value) => Number.isFinite(value) && value > 0);
const timingStepDeltas = timingSteps.map((item) => Number(item.delta)).filter((value) => Number.isFinite(value) && value >= 0);
const timingStepExcess = timingSteps.map((item) => Number(item.excessiveIncreaseSeconds)).filter((value) => Number.isFinite(value) && value > 0);
rounds.push({
promptIndex,
promptCommandId: promptCommands[index].commandId,
promptTextHash: promptCommands[index].textHash,
promptTextBytes: promptCommands[index].textBytes,
promptCompletedAt: promptCommands[index].ts,
sampleCount: items.length,
firstSeq: items[0]?.seq ?? null,
lastSeq: items[items.length - 1]?.seq ?? null,
firstSampleAt: items[0]?.ts ?? null,
lastSampleAt: items[items.length - 1]?.ts ?? null,
withTotalElapsed: totalElapsed.length,
withRecentUpdate: recentUpdate.length,
loadingSamples: loadingCounts.filter((value) => value > 0).length,
maxLoadingCount: loadingCounts.length > 0 ? Math.max(...loadingCounts) : 0,
loadingOwnerCount: loadingOwners.size,
maxTotalElapsedSeconds: totalElapsed.length > 0 ? Math.max(...totalElapsed) : null,
lastTotalElapsedSeconds: lastNonNull(totalElapsed),
maxRecentUpdateSeconds: recentUpdate.length > 0 ? Math.max(...recentUpdate) : null,
lastRecentUpdateSeconds: lastNonNull(recentUpdate),
diagnosticSamples: items.filter((item) => item.diagnosticSeen).length,
terminalSamples: items.filter((item) => item.terminalSeen).length,
finalTextSamples: items.filter((item) => item.finalResultTextSeen).length,
turnTimingNonMonotonicCount: timingAnomalies.length,
turnTimingTotalElapsedDecreaseCount: timingAnomalies.filter((item) => item.metric === "totalElapsedSeconds").length,
turnTimingTotalElapsedZeroResetCount: timingZeroResets.length,
turnTimingTotalElapsedForwardJumpCount: timingForwardJumps.length,
turnTimingTotalElapsedForwardJumpMaxSeconds: maxPositiveDelta(timingForwardJumps),
turnTimingTerminalElapsedGrowthCount: timingTerminalGrowth.length,
turnTimingTerminalElapsedGrowthMaxSeconds: terminalGrowthDeltas.length > 0 ? Math.max(...terminalGrowthDeltas) : 0,
turnTimingRecentUpdateJumpCount: timingAnomalies.filter((item) => item.metric === "recentUpdateSeconds" && item.anomaly === "jump").length,
turnTimingRecentUpdateSawtoothJumpCount: timingAnomalies.filter((item) => item.metric === "recentUpdateSeconds" && item.anomaly === "jump").length,
turnTimingRecentUpdateStepCount: timingSteps.length,
turnTimingRecentUpdateMaxIncreaseSeconds: timingStepDeltas.length > 0 ? Math.max(...timingStepDeltas) : null,
turnTimingRecentUpdateMaxExcessSeconds: timingStepExcess.length > 0 ? Math.max(...timingStepExcess) : 0,
turnTimingRecentUpdateResetCount: timingResets.length,
turnTimingRecentUpdateDecreaseCount: timingResets.length
});
}
return rounds;
}
function roundPromptTurnTimingValues(rows, columns, promptIndex, round = {}) {
const roundFirstSeq = Number(round.firstSeq);
const roundFirstMs = Date.parse(String(round.firstSampleAt ?? ""));
const seqSlack = 3;
const timeSlackMs = 3000;
const promptColumnIds = new Set(columns
.filter((column) => {
if (Number(column?.promptIndex) === Number(promptIndex)) return true;
const firstSeq = Number(column?.firstSeq);
if (Number.isFinite(roundFirstSeq) && Number.isFinite(firstSeq) && firstSeq >= roundFirstSeq - seqSlack) return true;
const firstMs = Date.parse(String(column?.firstTs ?? ""));
return Number.isFinite(roundFirstMs) && Number.isFinite(firstMs) && firstMs >= roundFirstMs - timeSlackMs;
})
.map((column) => String(column?.id || ""))
.filter(Boolean));
if (promptColumnIds.size === 0) return { totalElapsed: [], recentUpdate: [] };
const totalElapsed = [];
const recentUpdate = [];
for (const row of rows) {
if (Number(row?.promptIndex ?? 0) !== Number(promptIndex)) continue;
const cells = row?.cells && typeof row.cells === "object" ? row.cells : {};
for (const [columnId, cell] of Object.entries(cells)) {
if (!promptColumnIds.has(columnId)) continue;
const total = Number(cell?.totalElapsedSeconds);
if (Number.isFinite(total)) totalElapsed.push(total);
const recent = Number(cell?.recentUpdateSeconds);
if (Number.isFinite(recent)) recentUpdate.push(recent);
}
}
return { totalElapsed, recentUpdate };
}
function sampleTexts(sample) {
const rows = [];
for (const group of [sample?.messages, sample?.traceRows, sample?.diagnostics]) {
if (!Array.isArray(group)) continue;
for (const item of group) {
const text = String(item?.textPreview || "");
if (text.trim()) rows.push(text);
}
}
return rows;
}
function sampleTurnTimingTexts(sample) {
const rows = [];
for (const group of [sample?.turns, sample?.messages]) {
if (!Array.isArray(group)) continue;
for (const item of group) {
for (const value of [item?.durationText, item?.activityText]) {
const text = String(value || "");
if (text.trim()) rows.push(text);
}
}
}
return rows;
}
function parseTotalElapsedSeconds(text) {
const values = [];
for (const match of String(text || "").matchAll(/(?:总耗时|耗时)\s*[=:]?\s*(\d{1,2}):(\d{2}):(\d{2})/giu)) {
values.push(Number(match[1]) * 3600 + Number(match[2]) * 60 + Number(match[3]));
}
for (const match of String(text || "").matchAll(/(?:总耗时|耗时)\s*[=:]?\s*(\d{1,2}):(\d{2})(?!:)/giu)) {
values.push(Number(match[1]) * 60 + Number(match[2]));
}
for (const match of String(text || "").matchAll(/(?:总耗时|耗时)\s*[=:]?\s*(?:(\d+)\s*天)?\s*(?:(\d+)\s*小时)?\s*(?:(\d+)\s*(?:分钟|分))?\s*(?:(\d+)\s*秒)?/giu)) {
const days = Number(match[1] || 0);
const hours = Number(match[2] || 0);
const minutes = Number(match[3] || 0);
const seconds = Number(match[4] || 0);
if (days || hours || minutes || seconds || /(?:天|小时|分钟|分|秒)/u.test(match[0] || "")) values.push(days * 86400 + hours * 3600 + minutes * 60 + seconds);
}
return values;
}
function lastNonNull(values) {
for (let index = values.length - 1; index >= 0; index -= 1) if (values[index] !== null && values[index] !== undefined) return values[index];
return null;
}
function parseRecentUpdateSeconds(text) {
const values = [];
for (const match of String(text || "").matchAll(/最近\s*(?:(\d+)\s*天)?\s*(?:(\d+)\s*小时)?\s*(?:(\d+)\s*(?:分钟|分))?\s*(?:(\d+)\s*秒)?\s*前/giu)) {
const days = Number(match[1] || 0);
const hours = Number(match[2] || 0);
const minutes = Number(match[3] || 0);
const seconds = Number(match[4] || 0);
values.push(days * 86400 + hours * 3600 + minutes * 60 + seconds);
}
return values;
}
function latestPromptIndex(promptTimes, tsMs) {
let index = 0;
for (let i = 0; i < promptTimes.length; i += 1) {
if (promptTimes[i] <= tsMs) index = i + 1;
else break;
}
return index;
}
function promptIndexForTs(promptTimes, ts) {
const tsMs = Date.parse(ts);
return Number.isFinite(tsMs) ? latestPromptIndex(promptTimes, tsMs) : 0;
}
function buildTransitions(samples) {
const rows = [];
let last = null;
for (const sample of samples) {
const digest = digestSample(sample);
if (digest !== last) {
rows.push({ seq: sample.seq, ts: sample.ts, url: sample.url, routeSessionId: sample.routeSessionId || null, activeSessionId: sample.activeSessionId || null, messageCount: Array.isArray(sample.messages) ? sample.messages.length : 0, traceRowCount: Array.isArray(sample.traceRows) ? sample.traceRows.length : 0, digest });
last = digest;
}
}
return rows.slice(0, 200);
}
function detectFinalFlicker(samples) {
const flickers = [];
const lastNonEmptyByScope = new Map();
for (const sample of samples) {
const scope = finalFlickerScope(sample);
if (!scope) continue;
const messageText = Array.isArray(sample.messages) ? sample.messages.map((item) => item.textPreview || "").join("\n") : "";
const nonEmpty = messageText.trim().length > 0;
const finalLike = /轮次完成|已记录|已完成第\d+轮|final response|terminal result/iu.test(messageText);
const diagnosticLike = /temporarily|timeout|无法连接|暂时|error|failed|超时/iu.test(messageText);
const lastNonEmpty = lastNonEmptyByScope.get(scope);
if (nonEmpty && finalLike && !diagnosticLike) lastNonEmptyByScope.set(scope, { sample, messageText });
if (lastNonEmpty && nonEmpty && diagnosticLike) flickers.push({ scope, from: ref(lastNonEmpty.sample), to: ref(sample) });
if (lastNonEmpty && !nonEmpty) flickers.push({ scope, from: ref(lastNonEmpty.sample), to: ref(sample), reason: "text-disappeared" });
}
return flickers;
}
function finalFlickerScope(sample) {
const pathname = samplePathname(sample);
const sessionId = sample?.routeSessionId || sample?.activeSessionId || workbenchSessionIdFromPath(pathname);
if (!sessionId) return null;
if (!pathname.startsWith("/workbench/sessions/" + sessionId)) return null;
return (sample?.pageRole || "control") + ":" + sessionId;
}
function samplePathname(sample) {
const raw = String(sample?.path || sample?.url || "").trim();
if (!raw) return "";
try {
return new URL(raw, "http://hwlab.local").pathname || raw;
} catch {
return raw.split(/[?#]/u, 1)[0] || raw;
}
}
function workbenchSessionIdFromPath(pathname) {
const match = String(pathname || "").match(/^\/workbench\/sessions\/([^/?#]+)/u);
return match ? match[1] : null;
}
function detectTerminalZeroElapsed(samples) {
const rows = [];
for (const sample of samples) {
const turns = Array.isArray(sample?.turns) ? sample.turns : [];
const messages = Array.isArray(sample?.messages) ? sample.messages : [];
for (const item of [...turns, ...messages]) {
const text = [item?.durationText, item?.activityText, item?.status, item?.textPreview, item?.text].filter(Boolean).join("\n");
if (!/(?:Code Agent|运行记录|completed|failed|canceled|blocked)/iu.test(text)) continue;
if (!/(?:completed|failed|canceled|blocked|已完成|失败|取消)/iu.test(text)) continue;
const timingText = [item?.durationText, item?.activityText].filter(Boolean).join("\n");
const elapsedValues = parseTotalElapsedSeconds(timingText);
if (!elapsedValues.includes(0)) continue;
rows.push({
...ref(sample),
status: item?.status ?? null,
messageId: item?.messageId ?? null,
traceId: item?.traceId ?? null,
durationText: item?.durationText ?? null,
activityText: item?.activityText ?? null,
textPreview: limitText(item?.textPreview || item?.text || "", 180)
});
}
}
return rows;
}
function detectCrossPageProjectionDiffs(samples) {
const groups = new Map();
for (const sample of samples) {
const key = sample?.sampleGroupSeq ?? sample?.seq;
if (key === null || key === undefined) continue;
const group = groups.get(key) || {};
if (sample?.pageRole === "control") group.control = sample;
else if (sample?.pageRole === "observer") group.observer = sample;
groups.set(key, group);
}
const rows = [];
for (const [sampleGroupSeq, group] of groups.entries()) {
const control = group.control;
const observer = group.observer;
if (!control || !observer) continue;
const controlTraceIds = visibleTraceIds(control);
const observerTraceIds = visibleTraceIds(observer);
const missingInObserver = [...controlTraceIds].filter((item) => !observerTraceIds.has(item));
const controlMessages = Array.isArray(control.messages) ? control.messages.length : 0;
const observerMessages = Array.isArray(observer.messages) ? observer.messages.length : 0;
const controlTraceRows = Array.isArray(control.traceRows) ? control.traceRows.length : 0;
const observerTraceRows = Array.isArray(observer.traceRows) ? observer.traceRows.length : 0;
const controlZero = detectTerminalZeroElapsed([control]).length > 0;
const observerZero = detectTerminalZeroElapsed([observer]).length > 0;
const sameSession = (control.routeSessionId || control.activeSessionId || null) && (control.routeSessionId || control.activeSessionId || null) === (observer.routeSessionId || observer.activeSessionId || null);
const controlMessageDigest = digestMessageTexts(control);
const observerMessageDigest = digestMessageTexts(observer);
const messageTextDigestDiff = controlMessageDigest !== observerMessageDigest;
const projectionDivergent = sameSession && (Math.abs(controlMessages - observerMessages) > 0 || controlZero !== observerZero);
const traceVisibilityDivergent = sameSession && !projectionDivergent && (missingInObserver.length > 0 || Math.abs(controlTraceRows - observerTraceRows) > 0);
if (!projectionDivergent && !traceVisibilityDivergent) continue;
rows.push({
sampleGroupSeq,
diffKind: traceVisibilityDivergent ? "trace-visibility" : "projection",
control: ref(control),
observer: ref(observer),
controlTraceIds: [...controlTraceIds].slice(0, 8),
observerTraceIds: [...observerTraceIds].slice(0, 8),
missingTraceIdsInObserver: missingInObserver.slice(0, 8),
controlMessageCount: controlMessages,
observerMessageCount: observerMessages,
controlTraceRowCount: controlTraceRows,
observerTraceRowCount: observerTraceRows,
terminalZeroElapsedDiff: controlZero !== observerZero,
messageTextDigestDiff,
controlMessageDigest,
observerMessageDigest
});
}
return rows;
}
function mergeCrossPageDiffRows(...groups) {
const rows = [];
const seen = new Set();
for (const group of groups) {
if (!Array.isArray(group)) continue;
for (const row of group) {
const key = [
row?.diffKind || "projection",
row?.control?.seq ?? null,
row?.observer?.seq ?? null,
row?.controlMessageCount ?? null,
row?.observerMessageCount ?? null,
row?.controlTraceRowCount ?? null,
row?.observerTraceRowCount ?? null
].join(":");
if (seen.has(key)) continue;
seen.add(key);
rows.push(row);
}
}
return rows;
}
function annotateCrossPageDiffTiming(rows) {
const groups = new Map();
for (const row of Array.isArray(rows) ? rows : []) {
const controlAt = Date.parse(String(row?.control?.ts || ""));
const observerAt = Date.parse(String(row?.observer?.ts || ""));
const timestamps = [controlAt, observerAt].filter(Number.isFinite);
const startMs = timestamps.length > 0 ? Math.min(...timestamps) : null;
const endMs = timestamps.length > 0 ? Math.max(...timestamps) : null;
const sessionId = row?.control?.routeSessionId || row?.control?.activeSessionId || row?.observer?.routeSessionId || row?.observer?.activeSessionId || "unknown-session";
const key = [row?.diffKind || "projection", sessionId].join(":");
const group = groups.get(key) || { rows: [], firstMs: null, lastMs: null };
const annotated = {
...row,
sampleStartAt: startMs === null ? null : new Date(startMs).toISOString(),
sampleEndAt: endMs === null ? null : new Date(endMs).toISOString(),
pairSkewMs: startMs === null || endMs === null ? null : endMs - startMs,
};
group.rows.push(annotated);
if (startMs !== null && (group.firstMs === null || startMs < group.firstMs)) group.firstMs = startMs;
if (endMs !== null && (group.lastMs === null || endMs > group.lastMs)) group.lastMs = endMs;
groups.set(key, group);
}
const result = [];
for (const group of groups.values()) {
const sortedRows = group.rows.slice().sort((a, b) => Number(Date.parse(String(a.sampleStartAt || ""))) - Number(Date.parse(String(b.sampleStartAt || ""))));
const segments = [];
const splitGapMs = Math.max(1000, Number(alertThresholds.crossPageProjectionDivergenceRedMs || alertThresholds.visibleLoadingSlowMs || 10_000));
for (const row of sortedRows) {
const startMs = Date.parse(String(row.sampleStartAt || ""));
const endMs = Date.parse(String(row.sampleEndAt || row.sampleStartAt || ""));
const last = segments.at(-1);
const lastEndMs = last && last.lastMs !== null ? last.lastMs : null;
if (!last || (Number.isFinite(startMs) && lastEndMs !== null && startMs - lastEndMs > splitGapMs)) {
segments.push({ rows: [row], firstMs: Number.isFinite(startMs) ? startMs : null, lastMs: Number.isFinite(endMs) ? endMs : Number.isFinite(startMs) ? startMs : null });
continue;
}
last.rows.push(row);
if (Number.isFinite(startMs) && (last.firstMs === null || startMs < last.firstMs)) last.firstMs = startMs;
const effectiveEndMs = Number.isFinite(endMs) ? endMs : Number.isFinite(startMs) ? startMs : null;
if (effectiveEndMs !== null && (last.lastMs === null || effectiveEndMs > last.lastMs)) last.lastMs = effectiveEndMs;
}
for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex += 1) {
const segment = segments[segmentIndex];
const observedSpanMs = segment.firstMs === null || segment.lastMs === null ? null : segment.lastMs - segment.firstMs;
for (const row of segment.rows) {
result.push({
...row,
segmentIndex,
observedFirstAt: segment.firstMs === null ? null : new Date(segment.firstMs).toISOString(),
observedLastAt: segment.lastMs === null ? null : new Date(segment.lastMs).toISOString(),
observedSpanMs,
});
}
}
}
return result;
}
function detectAdjacentCrossPageProjectionDiffs(samples) {
const rows = [];
const ordered = (Array.isArray(samples) ? samples : []).slice().sort((a, b) => Number(a?.seq ?? 0) - Number(b?.seq ?? 0));
for (let i = 1; i < ordered.length; i += 1) {
const a = ordered[i - 1];
const b = ordered[i];
const roles = new Set([a?.pageRole, b?.pageRole]);
if (!roles.has("control") || !roles.has("observer")) continue;
const control = a?.pageRole === "control" ? a : b;
const observer = a?.pageRole === "observer" ? a : b;
const controlSession = control.routeSessionId || control.activeSessionId || null;
const observerSession = observer.routeSessionId || observer.activeSessionId || null;
if (!controlSession || controlSession !== observerSession) continue;
const controlAt = Date.parse(String(control.ts || ""));
const observerAt = Date.parse(String(observer.ts || ""));
if (Number.isFinite(controlAt) && Number.isFinite(observerAt) && Math.abs(controlAt - observerAt) > 1500) continue;
const controlMessages = Array.isArray(control.messages) ? control.messages.length : 0;
const observerMessages = Array.isArray(observer.messages) ? observer.messages.length : 0;
const controlTraceRows = Array.isArray(control.traceRows) ? control.traceRows.length : 0;
const observerTraceRows = Array.isArray(observer.traceRows) ? observer.traceRows.length : 0;
const controlZero = detectTerminalZeroElapsed([control]).length > 0;
const observerZero = detectTerminalZeroElapsed([observer]).length > 0;
const missingInObserver = [...visibleTraceIds(control)].filter((item) => !visibleTraceIds(observer).has(item));
const controlTraceIds = visibleTraceIds(control);
const observerTraceIds = visibleTraceIds(observer);
const controlMessageDigest = digestMessageTexts(control);
const observerMessageDigest = digestMessageTexts(observer);
const messageTextDigestDiff = controlMessageDigest !== observerMessageDigest;
const projectionDivergent = controlMessages !== observerMessages || controlZero !== observerZero;
const traceVisibilityDivergent = !projectionDivergent && (missingInObserver.length > 0 || controlTraceRows !== observerTraceRows);
if (!projectionDivergent && !traceVisibilityDivergent) continue;
rows.push({
sampleGroupSeq: control.sampleGroupSeq ?? observer.sampleGroupSeq ?? null,
adjacentPair: true,
diffKind: traceVisibilityDivergent ? "trace-visibility" : "projection",
control: ref(control),
observer: ref(observer),
controlTraceIds: [...controlTraceIds].slice(0, 8),
observerTraceIds: [...observerTraceIds].slice(0, 8),
missingTraceIdsInObserver: missingInObserver.slice(0, 8),
controlMessageCount: controlMessages,
observerMessageCount: observerMessages,
controlTraceRowCount: controlTraceRows,
observerTraceRowCount: observerTraceRows,
terminalZeroElapsedDiff: controlZero !== observerZero,
messageTextDigestDiff,
controlMessageDigest,
observerMessageDigest
});
}
return rows;
}
function detectTraceMessageDuplication(samples) {
const rows = [];
for (const sample of samples) {
const traceRows = Array.isArray(sample?.traceRows) ? sample.traceRows : [];
const groups = new Map();
for (let fallbackIndex = 0; fallbackIndex < traceRows.length; fallbackIndex += 1) {
const row = traceRows[fallbackIndex];
const rowTextRaw = String(row?.textPreview || row?.text || "");
if (!/(?:助手消息|assistant\s+message|assistant)/iu.test(rowTextRaw)) continue;
const rowText = normalizedText(rowTextRaw);
if (rowText.length < 24) continue;
const traceId = row?.traceId === undefined || row?.traceId === null ? "" : String(row.traceId);
const key = traceId + "\u0000" + rowText;
const group = groups.get(key) ?? { traceId: traceId || null, rowText, rowTextRaw, rows: [] };
group.rows.push({ row, fallbackIndex });
groups.set(key, group);
}
for (const group of groups.values()) {
if (group.rows.length < 2) continue;
rows.push({
...ref(sample),
traceId: group.traceId,
visibleAssistantFinalRowCount: group.rows.length,
rowIndexes: group.rows.map((item) => item.row?.index ?? item.fallbackIndex).slice(0, 12),
rowTextHash: sha256(group.rowText),
rowTextPreview: limitText(group.rowTextRaw, 180),
finalResponseSummaryBlockCounted: false,
traceFrameSource: "traceRows-only"
});
}
}
return rows;
}
function detectTurnTraceIdMissing(samples) {
const rows = [];
for (const sample of samples) {
for (const item of Array.isArray(sample?.turns) ? sample.turns : []) {
const text = [item?.status, item?.durationText, item?.activityText, item?.textPreview, item?.text].filter(Boolean).join("\n");
if (!/(?:Code Agent|运行记录|耗时|最近)/iu.test(text)) continue;
if (item?.traceId) continue;
rows.push({ ...ref(sample), status: item?.status ?? null, messageId: item?.messageId ?? null, textPreview: limitText(item?.textPreview || item?.text || "", 180) });
}
}
return rows;
}
function visibleTraceIds(sample) {
const ids = new Set();
for (const group of [sample?.turns, sample?.messages, sample?.traceRows]) {
if (!Array.isArray(group)) continue;
for (const item of group) if (item?.traceId) ids.add(String(item.traceId));
}
return ids;
}
function digestMessageTexts(sample) {
return sha256((Array.isArray(sample?.messages) ? sample.messages : []).map((item) => item?.textHash || normalizedText(item?.textPreview || item?.text || "")).join("|"));
}
function normalizedText(value) {
return String(value || "").replace(/\s+/gu, " ").trim();
}
function longestSharedSubstringLength(a, b) {
if (!a || !b) return 0;
const left = a.length <= b.length ? a : b;
const right = a.length <= b.length ? b : a;
const max = Math.min(left.length, 280);
let best = 0;
for (let start = 0; start < max; start += 1) {
for (let end = Math.min(max, start + 160); end > start + best; end -= 1) {
if (right.includes(left.slice(start, end))) {
best = end - start;
break;
}
}
}
return best;
}
function digestSample(sample) {
const messages = Array.isArray(sample.messages) ? sample.messages.map((item) => stableDigestItem(item, ["dataRole", "role", "status", "sessionId", "messageId", "traceId", "turnId"])).join("|") : "";
const trace = Array.isArray(sample.traceRows) ? sample.traceRows.map((item) => stableDigestItem(item, ["status", "sessionId", "messageId", "traceId", "turnId", "projectedSeq", "sourceSeq", "eventSeq", "eventKind"])).join("|") : "";
const diagnostics = Array.isArray(sample.diagnostics) ? sample.diagnostics.map((item) => stableDigestItem(item, ["className", "status", "sessionId", "messageId", "traceId", "turnId", "diagnosticCode"])).join("|") : "";
return sha256((sample.routeSessionId || "") + "|" + (sample.activeSessionId || "") + "|" + messages + "|" + trace + "|" + diagnostics);
}
function samplePageKey(sample) {
return String(sample?.pageRole || "control") + ":" + String(sample?.pageId || "default");
}
function stableDigestItem(item, fields) {
if (!item || typeof item !== "object") return "";
const identity = fields.map((field) => String(item?.[field] ?? "")).join(":");
return identity + ":" + stableVisibleDigestText(item?.textPreview || item?.text || item?.textHash || "");
}
function stableVisibleDigestText(value) {
return normalizedText(value)
.replace(/\btotal=\d{1,2}:\d{2}:\d{2}\b/giu, "total=<duration>")
.replace(/(?:总耗时|耗时)\s*[=:]?\s*(?:(?:\d+\s*天)?\s*(?:\d+\s*小时)?\s*(?:\d+\s*(?:分钟|分))?\s*(?:\d+\s*秒)?|\d{1,2}:\d{2}(?::\d{2})?)/giu, "耗时 <duration>")
.replace(/最近\s*(?:(?:\d+\s*天)?\s*(?:\d+\s*小时)?\s*(?:\d+\s*(?:分钟|分))?\s*(?:\d+\s*秒)?)\s*前/giu, "最近 <duration>前");
}
function nearCommand(sample, commandTimes, windowMs) {
const ts = Date.parse(sample.ts);
return Number.isFinite(ts) && commandTimes.some((item) => Math.abs(ts - item) <= windowMs);
}
function sampleRefs(samples, pick) {
const seen = new Set();
const refs = [];
for (const sample of samples) {
const value = pick(sample);
if (!value || seen.has(value)) continue;
seen.add(value);
refs.push({ ...ref(sample), value });
}
return refs.slice(0, 20);
}
function ref(sample) {
if (!sample) return null;
return { seq: sample.seq ?? null, sampleGroupSeq: sample.sampleGroupSeq ?? null, ts: sample.ts ?? null, pageRole: sample.pageRole ?? null, pageId: sample.pageId ?? null, url: sample.url ?? null, routeSessionId: sample.routeSessionId ?? null, activeSessionId: sample.activeSessionId ?? null };
}
async function artifactSummary(artifacts) {
const items = artifacts.slice(-30).map((item) => ({ kind: item.kind, reason: item.reason, sampleSeq: item.sampleSeq, path: item.path, sha256: item.sha256, byteCount: item.byteCount }));
return { count: artifacts.length, latest: items };
}
function compactManifest(value) {
if (!value) return null;
return { jobId: value.jobId, stateDir: value.stateDir, baseUrl: value.baseUrl, targetPath: value.targetPath, startedAt: value.startedAt, status: value.status, pageAuthority: value.pageAuthority ?? null, navigation: value.navigation ?? null, sampling: value.sampling, pageProvenance: value.pageProvenance ?? null, safety: value.safety };
}
function compactHeartbeat(value) {
if (!value) return null;
return { jobId: value.jobId, pid: value.pid, status: value.status, sampleSeq: value.sampleSeq, commandSeq: value.commandSeq, pageId: value.pageId ?? null, observerPageId: value.observerPageId ?? null, currentUrl: value.currentUrl, observerUrl: value.observerUrl ?? null, observerRefreshIntervalMs: value.observerRefreshIntervalMs ?? null, lastObserverRefreshAt: value.lastObserverRefreshAt ?? null, pageProvenance: value.pageProvenance ?? null, updatedAt: value.updatedAt, uptimeMs: value.uptimeMs };
}
function renderTurnTimingTable(sampleMetrics) {
const columns = Array.isArray(sampleMetrics?.turnColumns) ? sampleMetrics.turnColumns : [];
const rows = Array.isArray(sampleMetrics?.turnTimingTable) ? sampleMetrics.turnTimingTable : [];
const disclosure = sampleMetrics?.turnTimingTableDisclosure || null;
if (columns.length === 0 || rows.length === 0) return "- 无 turn 时间表。";
const header = ["时间戳"];
for (const column of columns) {
const columnLabel = formatTurnColumnDisplayLabel(column);
header.push(columnLabel + " 总耗时(s)");
header.push(columnLabel + " 最近更新(s)");
}
const lines = [];
lines.push("| " + header.map(escapeMarkdownCell).join(" | ") + " |");
lines.push("| " + header.map(() => "---").join(" | ") + " |");
for (const row of rows) {
const cells = [row.ts || "-"];
for (const column of columns) {
const cell = row.cells?.[column.id] || {};
cells.push(formatMetricCell(cell.totalElapsedSeconds));
cells.push(formatMetricCell(cell.recentUpdateSeconds));
}
lines.push("| " + cells.map(escapeMarkdownCell).join(" | ") + " |");
}
const columnLines = columns.map((column) => "- " + formatTurnColumnDisplayLabel(column) + ": pageRole=" + (column.pageRole || "-") + " pageId=" + (column.pageId || "-") + " source=" + (column.source || "-") + " prompt=" + (column.promptIndex ?? "-") + " lastPrompt=" + (column.lastPromptIndex ?? "-") + " firstSeq=" + (column.firstSeq ?? "-") + " lastSeq=" + (column.lastSeq ?? "-") + " traceId=" + (column.traceId || "-") + " messageId=" + (column.messageId || "-")).join("\n");
const nonMonotonic = Array.isArray(sampleMetrics?.turnTimingNonMonotonic) ? sampleMetrics.turnTimingNonMonotonic : [];
const nonMonotonicLines = nonMonotonic.length > 0
? nonMonotonic.slice(0, 80).map((item) => "- " + formatTurnEventDisplayLabel(item) + " " + item.metric + (item.anomaly ? " " + item.anomaly : "") + " " + (item.fromValue ?? "-") + " -> " + (item.toValue ?? "-") + " delta=" + (item.delta ?? "-") + " sampleDelta=" + (item.sampleDeltaSeconds ?? "-") + " allowed=" + (item.allowedIncreaseSeconds ?? "-") + formatTimingMeta(item) + " seq " + (item.fromSeq ?? "-") + " -> " + (item.toSeq ?? "-") + " ts " + (item.fromTs || "-") + " -> " + (item.toTs || "-") + " traceId=" + (item.traceId || "-")).join("\n")
: "- 未观察到总耗时下降或最近更新异常跳增。";
const terminalGrowth = Array.isArray(sampleMetrics?.turnTimingTerminalElapsedGrowth) ? sampleMetrics.turnTimingTerminalElapsedGrowth : [];
const terminalGrowthLines = terminalGrowth.length > 0
? terminalGrowth.slice(0, 80).map((item) => "- " + formatTurnEventDisplayLabel(item) + " terminal totalElapsed growth " + (item.fromValue ?? "-") + " -> " + (item.toValue ?? "-") + " delta=" + (item.delta ?? "-") + " status " + (item.fromStatus || "-") + " -> " + (item.toStatus || "-") + formatTimingMeta(item) + " seq " + (item.fromSeq ?? "-") + " -> " + (item.toSeq ?? "-") + " ts " + (item.fromTs || "-") + " -> " + (item.toTs || "-") + " traceId=" + (item.traceId || "-")).join("\n")
: "- 未观察到 terminal 后总耗时增长。";
const elapsedZeroResets = Array.isArray(sampleMetrics?.turnTimingElapsedZeroResets) ? sampleMetrics.turnTimingElapsedZeroResets : [];
const elapsedZeroResetLines = elapsedZeroResets.length > 0
? elapsedZeroResets.slice(0, 80).map((item) => "- " + formatTurnEventDisplayLabel(item) + " totalElapsed zero-reset " + (item.fromValue ?? "-") + " -> " + (item.toValue ?? "-") + " delta=" + (item.delta ?? "-") + formatTimingMeta(item) + " seq " + (item.fromSeq ?? "-") + " -> " + (item.toSeq ?? "-") + " ts " + (item.fromTs || "-") + " -> " + (item.toTs || "-") + " traceId=" + (item.traceId || "-")).join("\n")
: "- 未观察到总耗时从非零跳回 0 秒。";
const totalElapsedForwardJumps = Array.isArray(sampleMetrics?.turnTimingTotalElapsedForwardJumps) ? sampleMetrics.turnTimingTotalElapsedForwardJumps : [];
const totalElapsedForwardJumpLines = totalElapsedForwardJumps.length > 0
? totalElapsedForwardJumps.slice(0, 80).map((item) => "- " + formatTurnEventDisplayLabel(item) + " totalElapsed forward-jump " + (item.fromValue ?? "-") + " -> " + (item.toValue ?? "-") + " delta=" + (item.delta ?? "-") + " sampleDelta=" + (item.sampleDeltaSeconds ?? "-") + " allowed=" + (item.allowedIncreaseSeconds ?? "-") + formatTimingMeta(item) + " seq " + (item.fromSeq ?? "-") + " -> " + (item.toSeq ?? "-") + " ts " + (item.fromTs || "-") + " -> " + (item.toTs || "-") + " traceId=" + (item.traceId || "-")).join("\n")
: "- 未观察到总耗时超出采样间隔的异常前跳。";
const sawtoothJumps = Array.isArray(sampleMetrics?.turnTimingRecentUpdateSawtoothJumps)
? sampleMetrics.turnTimingRecentUpdateSawtoothJumps
: nonMonotonic.filter((item) => item.metric === "recentUpdateSeconds" && item.anomaly === "jump");
const sawtoothJumpLines = sawtoothJumps.length > 0
? sawtoothJumps.slice(0, 80).map((item) => "- " + formatTurnEventDisplayLabel(item) + " recentUpdate sawtooth-jump " + (item.fromValue ?? "-") + " -> " + (item.toValue ?? "-") + " delta=" + (item.delta ?? "-") + " sampleDelta=" + (item.sampleDeltaSeconds ?? "-") + " allowed=" + (item.allowedIncreaseSeconds ?? "-") + " seq " + (item.fromSeq ?? "-") + " -> " + (item.toSeq ?? "-") + " ts " + (item.fromTs || "-") + " -> " + (item.toTs || "-") + " traceId=" + (item.traceId || "-")).join("\n")
: "- 未观察到最近更新三角波异常跳增。";
const recentUpdateSteps = Array.isArray(sampleMetrics?.turnTimingRecentUpdateLargestSteps)
? sampleMetrics.turnTimingRecentUpdateLargestSteps
: Array.isArray(sampleMetrics?.turnTimingRecentUpdateSteps)
? sampleMetrics.turnTimingRecentUpdateSteps.filter((item) => Number.isFinite(Number(item.delta))).slice().sort((a, b) => Number(b.delta) - Number(a.delta)).slice(0, 200)
: [];
const stepLines = recentUpdateSteps.length > 0
? recentUpdateSteps.slice(0, 80).map((item) => "- " + formatTurnEventDisplayLabel(item) + " recentUpdate step " + (item.fromValue ?? "-") + " -> " + (item.toValue ?? "-") + " delta=" + (item.delta ?? "-") + " sampleDelta=" + (item.sampleDeltaSeconds ?? "-") + " allowed=" + (item.allowedIncreaseSeconds ?? "-") + " excess=" + (item.excessiveIncreaseSeconds ?? 0) + " event=" + (item.event || "-") + " seq " + (item.fromSeq ?? "-") + " -> " + (item.toSeq ?? "-") + " ts " + (item.fromTs || "-") + " -> " + (item.toTs || "-") + " traceId=" + (item.traceId || "-")).join("\n")
: "- 未观察到最近更新相邻采样 step。";
const recentUpdateResets = Array.isArray(sampleMetrics?.turnTimingRecentUpdateResets) ? sampleMetrics.turnTimingRecentUpdateResets : [];
const resetLines = recentUpdateResets.length > 0
? recentUpdateResets.slice(0, 80).map((item) => "- " + formatTurnEventDisplayLabel(item) + " reset " + (item.fromValue ?? "-") + " -> " + (item.toValue ?? "-") + " delta=" + (item.delta ?? "-") + " sampleDelta=" + (item.sampleDeltaSeconds ?? "-") + " seq " + (item.fromSeq ?? "-") + " -> " + (item.toSeq ?? "-") + " ts " + (item.fromTs || "-") + " -> " + (item.toTs || "-") + " traceId=" + (item.traceId || "-")).join("\n")
: "- 未观察到最近更新归零/回落。";
const disclosureLine = disclosure?.truncated
? "表格披露:已按 head/tail 有界输出,totalRows=" + disclosure.totalRows + " includedRows=" + disclosure.includedRows + " omittedRows=" + disclosure.omittedRows + ";异常计数在截断前基于全量采样计算。"
: "表格披露:完整输出当前分析窗口的采样点。";
return disclosureLine + "\n\n" + lines.join("\n") + "\n\n列说明:\n" + columnLines + "\n\n时间源说明:totalElapsed 异常以 DOM Code Agent card 采样序列为观察源;completion/final-response 可用时作为 sealed source-of-truthstatus=business-turn-completed 表示 timing 仅为非阻塞告警,status=observer-timeout 表示观察等待超预算,status=scenario-incomplete 表示业务回合失败或未完成。\n\n异常事件(仅报表暴露,不做下游 repair;最近更新按三角波模型检测异常跳增):\n" + nonMonotonicLines + "\n\nTerminal 后总耗时增长事件(终态 turn 的总耗时应 sealed,不应继续增长):\n" + terminalGrowthLines + "\n\n总耗时归零跳变事件(例如已显示真实耗时后又变成 0 秒):\n" + elapsedZeroResetLines + "\n\n总耗时异常前跳事件(预期按采样间隔近似递增;例如 14 秒 -> 1137 秒应被列入这里):\n" + totalElapsedForwardJumpLines + "\n\n最近更新 sawtooth jump 事件(预期每秒增长约 1,遇到新活动归零;例如 1 秒 -> 1 分 4 秒应被列入这里):\n" + sawtoothJumpLines + "\n\n最近更新相邻采样 step(按 delta 降序;用于人工识别一秒跳几十秒/一分钟的瞬态):\n" + stepLines + "\n\n最近更新 reset 事件(预期三角波归零,不计为异常):\n" + resetLines;
}
function formatTurnColumnDisplayLabel(column) {
const base = String(column?.label || column?.id || "-");
const role = String(column?.pageRole || "unknown");
const pageId = compactPageId(column?.pageId);
return pageId ? base + "@" + role + "/" + pageId : base + "@" + role;
}
function formatTurnEventDisplayLabel(item) {
const base = String(item?.columnLabel || item?.columnId || "-");
const role = String(item?.pageRole || "unknown");
const pageId = compactPageId(item?.pageId);
return pageId ? base + "@" + role + "/" + pageId : base + "@" + role;
}
function compactPageId(value) {
if (value === null || value === undefined || value === "") return "";
const text = String(value);
if (text.length <= 18) return text;
return text.slice(0, 12) + ".." + text.slice(-4);
}
function formatMetricCell(value) {
if (value === null || value === undefined || !Number.isFinite(Number(value))) return "-";
return String(Number(value));
}
function escapeMarkdownCell(value) {
return String(value ?? "-").replace(/\|/gu, "\\|");
}
function formatTimingMeta(item) {
const source = item?.timingSourceOfTruth || item?.expectedElapsedSource || item?.evidenceKind || "-";
const status = item?.timingStatus || "-";
return " source=" + source + " status=" + status;
}
function renderMarkdown(report) {
const findingLines = report.findings.length === 0 ? "- 无红灯项。" : report.findings.map((item) => "- " + item.severity + ": " + item.id + " - " + item.summary).join("\n");
const commandLines = report.commandTimeline.length === 0 ? "- 无控制命令。" : report.commandTimeline.map((item) => "- " + item.ts + " " + item.phase + " " + item.type + " " + item.commandId + " " + (item.afterUrl || "")).join("\n");
const commandFailureLines = Array.isArray(report.commandFailures) && report.commandFailures.length > 0
? report.commandFailures.slice(0, 80).map((item) => "- " + (item.ts || "-") + " type=" + (item.type || "-") + " commandId=" + (item.commandId || "-") + " durationMs=" + (item.durationMs ?? "-") + " sampleSeq=" + (item.sampleSeq ?? "-") + " path=" + (item.beforePath || "-") + "->" + (item.afterPath || "-") + " message=" + escapeMarkdownCell(item.message || item.failureKind || item.name || "-")).join("\n")
: "- 无失败控制命令。";
const transitionLines = report.transitions.length === 0 ? "- 无状态变化。" : report.transitions.slice(0, 80).map((item) => "- #" + item.seq + " " + item.ts + " messages=" + item.messageCount + " traceRows=" + item.traceRowCount + " route=" + (item.routeSessionId || "-") + " active=" + (item.activeSessionId || "-")).join("\n");
const metricSummary = report.sampleMetrics?.summary || {};
const loading = report.sampleMetrics?.loading || {};
const loadingSummary = loading.summary || {};
const sessionRailTitles = report.sampleMetrics?.sessionRailTitles || {};
const sessionRailTitleSummary = sessionRailTitles.summary || {};
const codeAgentCardTiming = report.sampleMetrics?.codeAgentCardTiming || {};
const codeAgentCardTimingSummary = codeAgentCardTiming.summary || {};
const roundCompletion = codeAgentCardTiming.roundCompletion || {};
const traceOrder = report.sampleMetrics?.traceOrder || {};
const traceOrderSummary = traceOrder.summary || {};
const alertSummary = report.runtimeAlerts?.summary || {};
const apiDomLag = report.apiDomLag || {};
const apiDomLagSummary = apiDomLag.summary || {};
const projectManagement = report.projectManagement || {};
const projectSummary = projectManagement.summary || {};
const projectCommandLines = Array.isArray(projectManagement.commands) && projectManagement.commands.length > 0
? projectManagement.commands.slice(0, 40).map((item) => "- " + (item.ts || "-") + " " + (item.phase || "-") + " " + (item.type || "-") + " command=" + (item.commandId || "-") + " status=" + (item.launchStatus ?? "-") + " session=" + (item.sessionId || "-") + " otel=" + (item.otelTraceId || "-") + " taskHash=" + (item.selectedTaskRefHash || "-")).join("\n")
: "- 无项目管理控制命令。";
const projectApiLines = Array.isArray(projectManagement.projectApiByPath) && projectManagement.projectApiByPath.length > 0
? projectManagement.projectApiByPath.slice(0, 40).map((item) => "- " + (item.method || "-") + " " + (item.path || "-") + " type=" + (item.type || "-") + " status=" + (item.status ?? "-") + " count=" + (item.count ?? 0) + " first=" + (item.firstAt || "-") + " last=" + (item.lastAt || "-")).join("\n")
: "- 无项目管理自然 API 记录。";
const projectSampleLines = Array.isArray(projectManagement.samples) && projectManagement.samples.length > 0
? projectManagement.samples.slice(-40).map((item) => "- #" + (item.seq ?? "-") + " " + (item.ts || "-") + " role=" + (item.pageRole || "-") + " kind=" + (item.pageKind || "-") + " src=" + (item.sourceCount ?? "-") + " files=" + (item.fileCount ?? "-") + " tasks=" + (item.taskCount ?? "-") + " selectedTask=" + (item.selectedTaskRefHash || "-") + " launchEnabled=" + String(item.launchButtonEnabled === true) + " links=" + (item.workbenchLinkCount ?? 0)).join("\n")
: "- 无项目管理 DOM 采样。";
const httpAlertLines = Array.isArray(report.runtimeAlerts?.networkHttpErrorsByPath) && report.runtimeAlerts.networkHttpErrorsByPath.length > 0
? report.runtimeAlerts.networkHttpErrorsByPath.slice(0, 40).map((item) => "- HTTP " + (item.status ?? "-") + " " + item.method + " " + item.urlPath + " count=" + item.count + " prompts=" + (item.promptIndexes?.join(",") || "-") + " first=" + (item.firstAt || "-") + " last=" + (item.lastAt || "-")).join("\n")
: "- 无 HTTP 错误。";
const requestFailedLines = Array.isArray(report.runtimeAlerts?.networkRequestFailedByPath) && report.runtimeAlerts.networkRequestFailedByPath.length > 0
? report.runtimeAlerts.networkRequestFailedByPath.slice(0, 40).map((item) => "- requestfailed " + item.method + " " + item.urlPath + " count=" + item.count + " failure=" + (item.failureKinds?.slice(0, 4).join(",") || "-") + " prompts=" + (item.promptIndexes?.join(",") || "-") + " first=" + (item.firstAt || "-") + " last=" + (item.lastAt || "-")).join("\n")
: "- 无 requestfailed。";
const domDiagnosticLines = Array.isArray(report.runtimeAlerts?.domDiagnostics) && report.runtimeAlerts.domDiagnostics.length > 0
? report.runtimeAlerts.domDiagnostics.slice(0, 40).map((item) => "- #" + (item.seq ?? "-") + " " + (item.ts || "-") + " prompt=" + (item.promptIndex ?? "-") + " source=" + (item.source || "-") + " code=" + (item.diagnosticCode || "-") + " traceId=" + (item.traceId || "-") + " http=" + (item.httpStatus ?? "-") + " idle=" + (item.idleSeconds ?? "-") + " waitingFor=" + (item.waitingFor || "-") + " lastEventLabel=" + (item.lastEventLabel || "-") + " textHash=" + (item.textHash || "-") + " preview=" + escapeMarkdownCell(item.preview || "")).join("\n")
: "- 无 DOM 诊断文本。";
const consoleAlertLines = Array.isArray(report.runtimeAlerts?.consoleAlerts) && report.runtimeAlerts.consoleAlerts.length > 0
? report.runtimeAlerts.consoleAlerts.slice(0, 40).map((item) => "- " + (item.ts || "-") + " prompt=" + (item.promptIndex ?? "-") + " type=" + (item.type || "-") + " status=" + (item.status ?? "-") + " path=" + (item.urlPath || "-") + " traceId=" + (item.traceId || "-") + " textHash=" + (item.textHash || "-") + " preview=" + escapeMarkdownCell(item.preview || "")).join("\n")
: "- 无 console warning/error。";
const consoleAlertGroupLines = Array.isArray(report.runtimeAlerts?.consoleAlertsByPath) && report.runtimeAlerts.consoleAlertsByPath.length > 0
? report.runtimeAlerts.consoleAlertsByPath.slice(0, 40).map((item) => "- console " + (item.type || "-") + " status=" + (item.status ?? "-") + " path=" + (item.urlPath || "-") + " count=" + item.count + " prompts=" + (item.promptIndexes?.join(",") || "-") + " traces=" + (item.traceIds?.slice(0, 6).join(",") || "-")).join("\n")
: "- 无 console 分组。";
const promptNetworkLines = Array.isArray(report.promptNetwork?.rounds) && report.promptNetwork.rounds.length > 0
? report.promptNetwork.rounds.map((item) => "- round " + item.promptIndex + " promptHash=" + (item.promptTextHash || "-") + " chatPostOk=" + String(item.chatPostOk) + " modes=" + (Array.isArray(item.submitModes) && item.submitModes.length > 0 ? item.submitModes.join(",") : "-") + " failure=" + (item.failureKind || "-") + " statuses=" + (Array.isArray(item.responseStatuses) && item.responseStatuses.length > 0 ? item.responseStatuses.join(",") : "-") + " firstChat=" + (item.firstChatEventAt || "-") + " lastChat=" + (item.lastChatEventAt || "-")).join("\n")
: "- 无 prompt 网络记录。";
const roundLines = Array.isArray(report.sampleMetrics?.rounds) && report.sampleMetrics.rounds.length > 0
? report.sampleMetrics.rounds.map((item) => "- round " + item.promptIndex + " promptHash=" + (item.promptTextHash || "-") + " samples=" + item.sampleCount + " loadingSamples=" + (item.loadingSamples ?? 0) + " maxLoading=" + (item.maxLoadingCount ?? 0) + " loadingOwners=" + (item.loadingOwnerCount ?? 0) + " totalMax=" + (item.maxTotalElapsedSeconds ?? "-") + " totalLast=" + (item.lastTotalElapsedSeconds ?? "-") + " recentMax=" + (item.maxRecentUpdateSeconds ?? "-") + " recentLast=" + (item.lastRecentUpdateSeconds ?? "-") + " totalDecrease=" + (item.turnTimingTotalElapsedDecreaseCount ?? 0) + " totalForwardJump=" + (item.turnTimingTotalElapsedForwardJumpCount ?? 0) + " totalForwardJumpMax=" + (item.turnTimingTotalElapsedForwardJumpMaxSeconds ?? 0) + " terminalGrowth=" + (item.turnTimingTerminalElapsedGrowthCount ?? 0) + " terminalGrowthMax=" + (item.turnTimingTerminalElapsedGrowthMaxSeconds ?? 0) + " recentJump=" + (item.turnTimingRecentUpdateJumpCount ?? 0) + " recentSawtoothJump=" + (item.turnTimingRecentUpdateSawtoothJumpCount ?? item.turnTimingRecentUpdateJumpCount ?? 0) + " recentStep=" + (item.turnTimingRecentUpdateStepCount ?? 0) + " recentMaxIncrease=" + (item.turnTimingRecentUpdateMaxIncreaseSeconds ?? "-") + " recentMaxExcess=" + (item.turnTimingRecentUpdateMaxExcessSeconds ?? 0) + " recentReset=" + (item.turnTimingRecentUpdateResetCount ?? 0) + " diagnostics=" + item.diagnosticSamples + " terminal=" + item.terminalSamples + " finalText=" + item.finalTextSamples).join("\n")
: "- 无轮次指标。";
const cardMissingElapsedLines = Array.isArray(codeAgentCardTiming.missingElapsed) && codeAgentCardTiming.missingElapsed.length > 0
? codeAgentCardTiming.missingElapsed.slice(0, 80).map((item) => "- #" + (item.seq ?? "-") + " " + (item.ts || "-") + " prompt=" + (item.promptIndex ?? "-") + " role=" + (item.pageRole || "-") + " status=" + (item.status || "-") + " traceId=" + (item.traceId || "-") + " messageId=" + (item.messageId || "-") + " preview=" + escapeMarkdownCell(item.textPreview || "")).join("\n")
: "- 未观察到 Code Agent 卡片缺少耗时。";
const cardMissingRecentLines = Array.isArray(codeAgentCardTiming.missingRecentUpdate) && codeAgentCardTiming.missingRecentUpdate.length > 0
? codeAgentCardTiming.missingRecentUpdate.slice(0, 80).map((item) => "- #" + (item.seq ?? "-") + " " + (item.ts || "-") + " prompt=" + (item.promptIndex ?? "-") + " role=" + (item.pageRole || "-") + " status=" + (item.status || "-") + " traceId=" + (item.traceId || "-") + " messageId=" + (item.messageId || "-") + " total=" + (item.totalElapsedSeconds ?? "-") + " preview=" + escapeMarkdownCell(item.textPreview || "")).join("\n")
: "- 未观察到未终态 Code Agent 卡片缺少最近更新。";
const roundCompletionLines = Array.isArray(roundCompletion.events) && roundCompletion.events.length > 0
? roundCompletion.events.slice(0, 80).map((item) => "- #" + (item.seq ?? "-") + " " + (item.ts || "-") + " prompt=" + (item.promptIndex ?? "-") + " role=" + (item.pageRole || "-") + " traceId=" + (item.traceId || "-") + " completionElapsed=" + (item.elapsedSeconds ?? "-") + " preview=" + escapeMarkdownCell(item.preview || "")).join("\n")
: "- 未观察到“轮次完成(总耗时 ...)”trace 行。";
const roundCompletionMismatchLines = Array.isArray(roundCompletion.elapsedMismatches) && roundCompletion.elapsedMismatches.length > 0
? roundCompletion.elapsedMismatches.slice(0, 80).map((item) => "- #" + (item.seq ?? "-") + " " + (item.ts || "-") + " prompt=" + (item.promptIndex ?? "-") + " traceId=" + (item.traceId || "-") + " completion=" + (item.completionElapsedSeconds ?? "-") + " card=" + (item.cardTotalElapsedSeconds ?? "-") + " delta=" + (item.deltaSeconds ?? "-") + " tolerance=" + (item.toleranceSeconds ?? "-") + formatTimingMeta(item)).join("\n")
: "- 未观察到轮次完成耗时与卡片耗时不一致。";
const roundCompletionFinalMissingLines = Array.isArray(roundCompletion.finalResponseMissing) && roundCompletion.finalResponseMissing.length > 0
? roundCompletion.finalResponseMissing.slice(0, 80).map((item) => "- #" + (item.seq ?? "-") + " " + (item.ts || "-") + " prompt=" + (item.promptIndex ?? "-") + " role=" + (item.pageRole || "-") + " traceId=" + (item.traceId || "-") + " completionElapsed=" + (item.completionElapsedSeconds ?? "-")).join("\n")
: "- 未观察到轮次完成后 final response 缺失。";
const roundCompletionPostTimingLines = Array.isArray(roundCompletion.postCompletionTimingChanges) && roundCompletion.postCompletionTimingChanges.length > 0
? roundCompletion.postCompletionTimingChanges.slice(0, 80).map((item) => "- " + (item.columnLabel || item.columnId || "-") + " " + (item.metric || "-") + " " + (item.fromValue ?? "-") + " -> " + (item.toValue ?? "-") + " delta=" + (item.delta ?? "-") + " completionSeq=" + (item.seq ?? "-") + " seq " + (item.fromSeq ?? "-") + " -> " + (item.toSeq ?? "-") + " traceId=" + (item.traceId || "-")).join("\n")
: "- 未观察到轮次完成后耗时/最近更新继续变化。";
const durationUnderreportedLines = Array.isArray(codeAgentCardTiming.durationUnderreported) && codeAgentCardTiming.durationUnderreported.length > 0
? codeAgentCardTiming.durationUnderreported.slice(0, 80).map((item) => "- sample=" + (item.sampleIndex ?? "-") + " " + (item.timestamp || "-") + " role=" + (item.pageRole || "-") + " status=" + (item.status || "-") + " traceId=" + (item.traceId || "-") + " card=" + (item.cardTotalElapsedSeconds ?? "-") + "s expected=" + (item.expectedElapsedSeconds ?? "-") + "s delta=" + (item.deltaSeconds ?? "-") + "s evidence=" + (item.evidenceKind || "-") + formatTimingMeta(item) + " preview=" + escapeMarkdownCell(item.evidencePreview || item.cardPreview || "")).join("\n")
: "- 未观察到 Code Agent 卡片耗时低于 trace/final-response 证据。";
const durationMismatchLines = Array.isArray(codeAgentCardTiming.durationMismatches) && codeAgentCardTiming.durationMismatches.length > 0
? codeAgentCardTiming.durationMismatches.slice(0, 80).map((item) => "- sample=" + (item.sampleIndex ?? "-") + " " + (item.timestamp || "-") + " role=" + (item.pageRole || "-") + " status=" + (item.status || "-") + " traceId=" + (item.traceId || "-") + " direction=" + (item.direction || "-") + " card=" + (item.cardTotalElapsedSeconds ?? "-") + "s expected=" + (item.expectedElapsedSeconds ?? "-") + "s signedDelta=" + (item.signedDeltaSeconds ?? "-") + "s delta=" + (item.deltaSeconds ?? "-") + "s tolerance=" + (item.toleranceSeconds ?? "-") + "s evidence=" + (item.evidenceKind || "-") + " exact=" + String(item.exactEvidence === true) + formatTimingMeta(item) + " preview=" + escapeMarkdownCell(item.evidencePreview || item.cardPreview || "")).join("\n")
: "- 未观察到 Code Agent 卡片耗时与 completion/final-response 封口证据不一致。";
const traceOrderAnomalyLines = Array.isArray(traceOrder.orderAnomalies) && traceOrder.orderAnomalies.length > 0
? traceOrder.orderAnomalies.slice(0, 80).map((item) => "- sample=" + (item.sampleIndex ?? "-") + " " + (item.timestamp || "-") + " role=" + (item.pageRole || "-") + " traceId=" + (item.traceId || "-") + " rows=" + (item.previousRowIndex ?? "-") + "->" + (item.currentRowIndex ?? "-") + " reasons=" + (Array.isArray(item.reasons) ? item.reasons.join(",") : "-") + " total=" + (item.previousTotalSeconds ?? "-") + "->" + (item.currentTotalSeconds ?? "-") + " clock=" + (item.previousClockSeconds ?? "-") + "->" + (item.currentClockSeconds ?? "-") + " preview=" + escapeMarkdownCell((item.previousPreview || "") + " / " + (item.currentPreview || ""))).join("\n")
: "- 未观察到可见 trace 行顺序非单调。";
const traceCompletionNotLastLines = Array.isArray(traceOrder.completionNotLast) && traceOrder.completionNotLast.length > 0
? traceOrder.completionNotLast.slice(0, 80).map((item) => "- sample=" + (item.sampleIndex ?? "-") + " " + (item.timestamp || "-") + " role=" + (item.pageRole || "-") + " traceId=" + (item.traceId || "-") + " rows=" + (item.completionRowIndex ?? "-") + "->" + (item.laterRowIndex ?? "-") + " total=" + (item.completionTotalSeconds ?? "-") + "->" + (item.laterTotalSeconds ?? "-") + " completion=" + escapeMarkdownCell(item.completionPreview || "") + " later=" + escapeMarkdownCell(item.laterPreview || "")).join("\n")
: "- 未观察到 completion 行后还有同 trace 后续行。";
const loadingSegmentLines = Array.isArray(loading.segments) && loading.segments.length > 0
? loading.segments.slice(0, 80).map((item) => "- observedDuration=" + (item.durationSeconds ?? 0) + "s upperBound=" + (item.upperBoundSeconds ?? item.durationSeconds ?? 0) + "s endedGap=" + (item.endedGapSeconds ?? "-") + "s samples=" + (item.sampleCount ?? 0) + " countMax=" + (item.maxCount ?? 0) + " owners=" + (item.ownerCount ?? 0) + " seq=" + (item.firstSeq ?? "-") + ".." + (item.lastSeq ?? "-") + " ts=" + (item.firstAt || "-") + ".." + (item.lastAt || "-") + " endedAt=" + (item.endedAt || (item.ongoing ? "ongoing" : "-")) + " ownerLabels=" + ((Array.isArray(item.owners) ? item.owners : []).slice(0, 6).map((owner) => (owner.ownerKind || "-") + ":" + (owner.ownerLabel || "-") + "x" + (owner.count ?? 0)).join(",") || "-")).join("\n")
: "- 未观察到“加载中”可见区间。";
const loadingOwnerLines = Array.isArray(loading.owners) && loading.owners.length > 0
? loading.owners.slice(0, 80).map((item) => "- " + (item.ownerKind || "-") + " " + escapeMarkdownCell(item.ownerLabel || item.ownerKey || "-") + " traceId=" + (item.ownerTraceId || "-") + " messageId=" + (item.ownerMessageId || "-") + " sessionId=" + (item.ownerSessionId || "-") + " samples=" + (item.sampleCount ?? 0) + " occurrences=" + (item.occurrenceCount ?? 0) + " maxCount=" + (item.maxSimultaneousCount ?? 0) + " longest=" + (item.longestContinuousSeconds ?? 0) + "s seq=" + (item.firstSeq ?? "-") + ".." + (item.lastSeq ?? "-") + " prompts=" + (Array.isArray(item.promptIndexes) ? item.promptIndexes.join(",") : "-")).join("\n")
: "- 未观察到“加载中”归属。";
const loadingTimelineLines = Array.isArray(loading.timeline) && loading.timeline.length > 0
? loading.timeline.slice(0, 160).map((item) => "- #" + (item.seq ?? "-") + " " + (item.ts || "-") + " prompt=" + (item.promptIndex ?? "-") + " loadingCount=" + (item.loadingCount ?? 0) + " ownerCount=" + (item.ownerCount ?? 0) + " owners=" + ((Array.isArray(item.owners) ? item.owners : []).slice(0, 6).map((owner) => (owner.ownerKind || "-") + ":" + (owner.ownerLabel || "-") + " trace=" + (owner.ownerTraceId || "-") + "x" + (owner.count ?? 0)).join(",") || "-")).join("\n")
: "- 未观察到“加载中”采样点。";
const sessionRailTitleSampleLines = Array.isArray(sessionRailTitles.samples) && sessionRailTitles.samples.length > 0
? sessionRailTitles.samples.slice(0, 80).map((item) => "- #" + (item.seq ?? "-") + " " + (item.ts || "-") + " role=" + (item.pageRole || "-") + " visible=" + (item.visibleCount ?? 0) + " fallback=" + (item.fallbackTitleCount ?? 0) + " ratio=" + (item.fallbackTitleRatio ?? 0) + " examples=" + ((Array.isArray(item.examples) ? item.examples : []).slice(0, 4).map((example) => escapeMarkdownCell(example.titlePreview || example.titleHash || "-")).join(",") || "-")).join("\n")
: "- 未观察到超过一半 fallback 的 session 列表采样点。";
const sessionRailTitleExampleLines = Array.isArray(sessionRailTitles.examples) && sessionRailTitles.examples.length > 0
? sessionRailTitles.examples.slice(0, 80).map((item) => "- firstSeq=" + (item.firstSeq ?? "-") + " role=" + (item.pageRole || "-") + " active=" + String(item.active === true) + " sessionPrefix=" + (item.sessionIdPrefix || "-") + " titleHash=" + (item.titleHash || "-") + " preview=" + escapeMarkdownCell(item.titlePreview || "")).join("\n")
: "- 无 fallback 标题示例。";
const provenanceLines = Array.isArray(report.pageProvenance?.segments) && report.pageProvenance.segments.length > 0
? report.pageProvenance.segments.slice(0, 40).map((item) => "- fingerprint=" + (item.assetFingerprint || "-") + " samples=" + item.sampleCount + " seq=" + (item.firstSeq ?? "-") + ".." + (item.lastSeq ?? "-") + " ts=" + (item.firstAt || "-") + ".." + (item.lastAt || "-") + " scripts=" + (item.scriptCount ?? "-") + " styles=" + (item.stylesheetCount ?? "-") + " urlPaths=" + (Array.isArray(item.urlPaths) ? item.urlPaths.slice(0, 4).join(",") : "-")).join("\n")
: "- 无页面 provenance segment。";
const ordinaryPerformanceItems = Array.isArray(report.pagePerformance?.sameOriginApiByPath) ? report.pagePerformance.sameOriginApiByPath.filter((item) => item.isLongLivedStream !== true) : [];
const streamPerformanceItems = Array.isArray(report.pagePerformance?.sameOriginApiByPath) ? report.pagePerformance.sameOriginApiByPath.filter((item) => item.isLongLivedStream === true) : [];
const sameOriginApiBudgetMs = Number(report.alertThresholds?.sameOriginApiSlowMs ?? report.pagePerformance?.summary?.budgetMs);
const streamOpenBudgetMs = Number(report.alertThresholds?.longLivedStreamOpenSlowMs);
const performanceLines = ordinaryPerformanceItems.length > 0
? ordinaryPerformanceItems.slice(0, 80).map((item) => "- " + item.path + " kind=" + (item.routeKind || "same-origin-api") + " budgetMetric=" + (item.budgetMetric || "durationMs") + " samples=" + item.sampleCount + " p50=" + (item.p50Ms ?? "-") + "ms p75=" + (item.p75Ms ?? "-") + "ms p95=" + (item.p95Ms ?? "-") + "ms max=" + (item.maxMs ?? "-") + "ms >budget=" + (item.overBudgetCount ?? item.overFiveSecondCount ?? 0) + " budgetMs=" + (item.budgetMs ?? sameOriginApiBudgetMs) + " legacy>5s=" + (item.overFiveSecondCount ?? 0) + " window=" + (item.firstAt || "-") + ".." + (item.lastAt || "-")).join("\n")
: "- 无同源 API Resource Timing 样本。";
const streamPerformanceLines = streamPerformanceItems.length > 0
? streamPerformanceItems.slice(0, 80).map((item) => "- " + item.path + " kind=" + (item.routeKind || "same-origin-api-stream") + " samples=" + item.sampleCount + " streamOpenP50=" + (item.streamOpenP50Ms ?? "-") + "ms streamOpenP75=" + (item.streamOpenP75Ms ?? "-") + "ms streamOpenP95=" + (item.streamOpenP95Ms ?? "-") + "ms streamOpenMax=" + (item.streamOpenMaxMs ?? "-") + "ms streamOpen>budget=" + (item.streamOpenOverBudgetCount ?? item.streamOpenOverFiveSecondCount ?? 0) + " streamOpenBudgetMs=" + (item.streamOpenBudgetMs ?? streamOpenBudgetMs) + " streamOpenLegacy>5s=" + (item.streamOpenOverFiveSecondCount ?? 0) + " streamLifetime>5s=" + (item.streamLifetimeOverFiveSecondCount ?? 0) + " lifetimeMax=" + (item.maxMs ?? "-") + "ms window=" + (item.firstAt || "-") + ".." + (item.lastAt || "-")).join("\n")
: "- 无同源长连接 Resource Timing 样本。";
const apiDomLagGroupLines = Array.isArray(apiDomLag.groups) && apiDomLag.groups.length > 0
? apiDomLag.groups.slice(0, 80).map((item) => "- " + (item.method || "-") + " " + (item.path || "-") + " status=" + (item.status ?? "-") + " kind=" + (item.routeKind || "-") + " confidence=" + (item.confidence || "-") + " count=" + (item.count ?? 0) + " changed=" + (item.domChangedCount ?? 0) + " noChange=" + (item.noDomChangeWithinWindowCount ?? 0) + " p50=" + (item.p50DomChangeDeltaMs ?? "-") + "ms p95=" + (item.p95DomChangeDeltaMs ?? "-") + "ms max=" + (item.maxDomChangeDeltaMs ?? "-") + "ms overBudget=" + (item.overBudgetCount ?? 0) + " window=" + (item.firstAt || "-") + ".." + (item.lastAt || "-")).join("\n")
: "- 无 API-DOM 候选分组。";
const apiDomLagWorstLines = Array.isArray(apiDomLag.worstCandidates) && apiDomLag.worstCandidates.length > 0
? apiDomLag.worstCandidates.slice(0, 40).map((item) => "- " + (item.ts || "-") + " " + (item.method || "-") + " " + (item.path || "-") + " status=" + (item.status ?? "-") + " confidence=" + (item.confidence || "-") + " domChange=" + (item.domChangeDeltaMs ?? "-") + "ms firstSample=" + (item.firstSampleDeltaMs ?? "-") + "ms session=" + (item.sessionId || "-") + " traceId=" + (item.traceId || "-") + " beforeSeq=" + (item.beforeSample?.seq ?? "-") + " changeSeq=" + (item.changeSample?.seq ?? "-")).join("\n")
: "- 无 API-DOM digest 变化候选。";
const metricLines = Array.isArray(report.sampleMetrics?.timeline) && report.sampleMetrics.timeline.length > 0
? report.sampleMetrics.timeline.slice(0, 120).map((item) => "- #" + item.seq + " " + item.ts + " prompt=" + item.promptIndex + " loadingCount=" + (item.loadingCount ?? 0) + " loadingOwners=" + (item.loadingOwnerCount ?? 0) + " totalElapsedSeconds=" + (item.totalElapsedSeconds ?? "-") + " recentUpdateSeconds=" + (item.recentUpdateSeconds ?? "-") + " terminal=" + item.terminalSeen + " finalText=" + item.finalResultTextSeen + " diagnostic=" + item.diagnosticSeen).join("\n")
: "- 无采样指标。";
const turnTimingTable = renderTurnTimingTable(report.sampleMetrics);
return "# web-probe observe analysis\n\n"
+ "- stateDir: " + report.stateDir + "\n"
+ "- generatedAt: " + report.generatedAt + "\n"
+ "- samples: " + report.counts.samples + "\n"
+ "- control: " + report.counts.control + "\n"
+ "- network: " + report.counts.network + "\n"
+ "- console: " + (report.counts.console ?? 0) + "\n"
+ "- errors: " + report.counts.errors + "\n\n"
+ "## Findings\n\n" + findingLines + "\n\n"
+ "## Project management\n\n"
+ "- enabled: " + String(projectSummary.enabled === true) + "\n"
+ "- projectSampleCount: " + (projectSummary.projectSampleCount ?? 0) + "\n"
+ "- mdtodoSampleCount: " + (projectSummary.mdtodoSampleCount ?? 0) + "\n"
+ "- latestPageKind: " + (projectSummary.latestPageKind || "-") + "\n"
+ "- latestPath: " + (projectSummary.latestPath || "-") + "\n"
+ "- latestCounts: source=" + (projectSummary.latestSourceCount ?? "-") + " file=" + (projectSummary.latestFileCount ?? "-") + " task=" + (projectSummary.latestTaskCount ?? "-") + "\n"
+ "- latestSelectedTaskRefHash: " + (projectSummary.latestSelectedTaskRefHash || "-") + "\n"
+ "- paneGap: actionable=" + (projectSummary.severePaneGapSampleCount ?? 0) + " ignoredInitialEmptyDetail=" + (projectSummary.ignoredPaneGapSampleCount ?? 0) + "\n"
+ "- launch: commands=" + (projectSummary.launchCommandCount ?? 0) + " success=" + (projectSummary.launchSuccessCount ?? 0) + " failure=" + (projectSummary.launchFailureCount ?? 0) + " otelTraceHeader=" + (projectSummary.launchWithOtelTraceHeaderCount ?? 0) + "\n"
+ "- projectApi: responses=" + (projectSummary.projectApiResponseCount ?? 0) + " failures=" + (projectSummary.projectApiFailureCount ?? 0) + " requestfailed=" + (projectSummary.projectApiRequestFailedCount ?? 0) + " slowPaths=" + (projectSummary.projectApiSlowPathCount ?? 0) + "\n\n"
+ "### Project samples\n\n" + projectSampleLines + "\n\n"
+ "### Project commands\n\n" + projectCommandLines + "\n\n"
+ "### Project API\n\n" + projectApiLines + "\n\n"
+ "## Command failures\n\n" + commandFailureLines + "\n\n"
+ "## Sample metrics\n\n"
+ "- sampleCount: " + (metricSummary.sampleCount ?? 0) + "\n"
+ "- withTotalElapsed: " + (metricSummary.withTotalElapsed ?? 0) + "\n"
+ "- withRecentUpdate: " + (metricSummary.withRecentUpdate ?? 0) + "\n"
+ "- diagnostics: " + (metricSummary.diagnostics ?? 0) + "\n"
+ "- loadingSamples: " + (metricSummary.loadingSampleCount ?? 0) + "\n"
+ "- loadingMaxCount: " + (metricSummary.loadingMaxCount ?? 0) + "\n"
+ "- loadingMaxOwnerCount: " + (metricSummary.loadingMaxOwnerCount ?? 0) + "\n"
+ "- loadingOwnerCount: " + (metricSummary.loadingOwnerCount ?? 0) + "\n"
+ "- loadingLongestContinuousSeconds: " + (metricSummary.loadingLongestContinuousSeconds ?? 0) + "\n"
+ "- loadingCurrentContinuousSeconds: " + (metricSummary.loadingCurrentContinuousSeconds ?? 0) + "\n"
+ "- loadingOverFiveSecondSegmentCount: " + (metricSummary.loadingOverFiveSecondSegmentCount ?? 0) + "\n"
+ "- sessionRailFallbackMajoritySampleCount: " + (metricSummary.sessionRailFallbackMajoritySampleCount ?? 0) + "\n"
+ "- sessionRailFallbackMaxRatio: " + (metricSummary.sessionRailFallbackMaxRatio ?? 0) + "\n"
+ "- sessionRailFallbackMaxCount: " + (metricSummary.sessionRailFallbackMaxCount ?? 0) + "\n"
+ "- promptSegments: " + (metricSummary.promptSegments ?? 0) + "\n\n"
+ "- turnColumns: " + (metricSummary.turnColumns ?? 0) + "\n"
+ "- turnTimingRows: " + (metricSummary.turnTimingRows ?? 0) + "\n"
+ "- turnTimingNonMonotonicCount: " + (metricSummary.turnTimingNonMonotonicCount ?? 0) + "\n"
+ "- turnTimingTotalElapsedDecreaseCount: " + (metricSummary.turnTimingTotalElapsedDecreaseCount ?? 0) + "\n"
+ "- turnTimingTotalElapsedForwardJumpCount: " + (metricSummary.turnTimingTotalElapsedForwardJumpCount ?? 0) + "\n"
+ "- turnTimingTotalElapsedForwardJumpMaxSeconds: " + (metricSummary.turnTimingTotalElapsedForwardJumpMaxSeconds ?? 0) + "\n"
+ "- turnTimingTerminalElapsedGrowthCount: " + (metricSummary.turnTimingTerminalElapsedGrowthCount ?? 0) + "\n"
+ "- turnTimingTerminalElapsedGrowthMaxSeconds: " + (metricSummary.turnTimingTerminalElapsedGrowthMaxSeconds ?? 0) + "\n"
+ "- turnTimingRecentUpdateJumpCount: " + (metricSummary.turnTimingRecentUpdateJumpCount ?? 0) + "\n"
+ "- turnTimingRecentUpdateSawtoothJumpCount: " + (metricSummary.turnTimingRecentUpdateSawtoothJumpCount ?? metricSummary.turnTimingRecentUpdateJumpCount ?? 0) + "\n"
+ "- turnTimingRecentUpdateStepCount: " + (metricSummary.turnTimingRecentUpdateStepCount ?? 0) + "\n"
+ "- turnTimingRecentUpdateMaxIncreaseSeconds: " + (metricSummary.turnTimingRecentUpdateMaxIncreaseSeconds ?? "-") + "\n"
+ "- turnTimingRecentUpdateMaxExcessSeconds: " + (metricSummary.turnTimingRecentUpdateMaxExcessSeconds ?? 0) + "\n"
+ "- turnTimingRecentUpdateResetCount: " + (metricSummary.turnTimingRecentUpdateResetCount ?? 0) + "\n\n"
+ "- codeAgentCardSampleCount: " + (metricSummary.codeAgentCardSampleCount ?? 0) + "\n"
+ "- codeAgentCardMissingElapsedCount: " + (metricSummary.codeAgentCardMissingElapsedCount ?? 0) + "\n"
+ "- codeAgentCardMissingRecentUpdateCount: " + (metricSummary.codeAgentCardMissingRecentUpdateCount ?? 0) + "\n"
+ "- codeAgentCardDurationUnderreportedCount: " + (metricSummary.codeAgentCardDurationUnderreportedCount ?? 0) + "\n"
+ "- codeAgentCardDurationMismatchCount: " + (metricSummary.codeAgentCardDurationMismatchCount ?? 0) + "\n"
+ "- traceRowCount: " + (metricSummary.traceRowCount ?? 0) + "\n"
+ "- traceRowOrderAnomalyCount: " + (metricSummary.traceRowOrderAnomalyCount ?? 0) + "\n"
+ "- traceRowCompletionNotLastCount: " + (metricSummary.traceRowCompletionNotLastCount ?? 0) + "\n"
+ "- roundCompletionEventCount: " + (metricSummary.roundCompletionEventCount ?? 0) + "\n"
+ "- roundCompletionElapsedMismatchCount: " + (metricSummary.roundCompletionElapsedMismatchCount ?? 0) + "\n"
+ "- roundCompletionFinalResponseMissingCount: " + (metricSummary.roundCompletionFinalResponseMissingCount ?? 0) + "\n"
+ "- roundCompletionPostTimingChangeCount: " + (metricSummary.roundCompletionPostTimingChangeCount ?? 0) + "\n\n"
+ "### Rounds\n\n" + roundLines + "\n\n"
+ "### Code Agent card timing display\n\n"
+ "- cardSampleCount: " + (codeAgentCardTimingSummary.cardSampleCount ?? 0) + "\n"
+ "- runningCardSampleCount: " + (codeAgentCardTimingSummary.runningCardSampleCount ?? 0) + "\n"
+ "- terminalCardSampleCount: " + (codeAgentCardTimingSummary.terminalCardSampleCount ?? 0) + "\n"
+ "- missingElapsedCount: " + (codeAgentCardTimingSummary.missingElapsedCount ?? 0) + "\n"
+ "- missingRecentUpdateCount: " + (codeAgentCardTimingSummary.missingRecentUpdateCount ?? 0) + "\n"
+ "- durationUnderreportedCount: " + (codeAgentCardTimingSummary.durationUnderreportedCount ?? 0) + "\n"
+ "- durationMismatchCount: " + (codeAgentCardTimingSummary.durationMismatchCount ?? 0) + "\n"
+ "- policy: Code Agent 卡片无论终态/非终态都必须显示耗时;非终态必须显示最近更新。completion/final-response 可用时是 sealed source-of-truthDOM card elapsed 是显示源。该 analyzer 只报告采样到的页面表现,不做下游 repair。\n\n"
+ "#### Missing elapsed samples\n\n" + cardMissingElapsedLines + "\n\n"
+ "#### Missing recent update samples\n\n" + cardMissingRecentLines + "\n\n"
+ "#### Duration underreported samples\n\n" + durationUnderreportedLines + "\n\n"
+ "#### Duration mismatch samples\n\n" + durationMismatchLines + "\n\n"
+ "### Trace row visual order\n\n"
+ "- traceRowCount: " + (traceOrderSummary.traceRowCount ?? 0) + "\n"
+ "- orderAnomalyCount: " + (traceOrderSummary.orderAnomalyCount ?? 0) + "\n"
+ "- completionNotLastCount: " + (traceOrderSummary.completionNotLastCount ?? 0) + "\n"
+ "- policy: 可见 trace 行在同一 trace 内必须按 total/时钟/projected seq 单调展示;completion 行不得出现在同 trace 后续行之前。\n\n"
+ "#### Trace order anomalies\n\n" + traceOrderAnomalyLines + "\n\n"
+ "#### Completion row not last samples\n\n" + traceCompletionNotLastLines + "\n\n"
+ "### Round completion consistency\n\n"
+ "- completionEventCount: " + (codeAgentCardTimingSummary.roundCompletionEventCount ?? 0) + "\n"
+ "- elapsedMismatchCount: " + (codeAgentCardTimingSummary.roundCompletionElapsedMismatchCount ?? 0) + "\n"
+ "- finalResponseMissingCount: " + (codeAgentCardTimingSummary.roundCompletionFinalResponseMissingCount ?? 0) + "\n"
+ "- postTimingChangeCount: " + (codeAgentCardTimingSummary.roundCompletionPostTimingChangeCount ?? 0) + "\n"
+ "- postRecentUpdateVisibleCount: " + (codeAgentCardTimingSummary.roundCompletionPostRecentUpdateVisibleCount ?? 0) + "\n"
+ "- elapsedMismatchToleranceSeconds: " + (codeAgentCardTimingSummary.elapsedMismatchToleranceSeconds ?? "-") + "\n"
+ "- policy: 轮次完成(总耗时 ... 是 trace sealed source-of-truth;卡片总耗时必须与它一致。完成后 final response 必须可见,耗时/最近更新不得继续跳变。\n\n"
+ "#### Round completion events\n\n" + roundCompletionLines + "\n\n"
+ "#### Completion elapsed mismatches\n\n" + roundCompletionMismatchLines + "\n\n"
+ "#### Final response missing after completion\n\n" + roundCompletionFinalMissingLines + "\n\n"
+ "#### Post-completion timing changes\n\n" + roundCompletionPostTimingLines + "\n\n"
+ "### Loading visibility: visible 加载中\n\n"
+ "- sampleCount: " + (loadingSummary.sampleCount ?? 0) + "\n"
+ "- loadingSampleCount: " + (loadingSummary.loadingSampleCount ?? 0) + "\n"
+ "- maxSimultaneousCount: " + (loadingSummary.maxSimultaneousCount ?? 0) + "\n"
+ "- maxSimultaneousOwnerCount: " + (loadingSummary.maxSimultaneousOwnerCount ?? 0) + "\n"
+ "- concurrentLoadingSampleCount: " + (loadingSummary.concurrentLoadingSampleCount ?? 0) + "\n"
+ "- ownerCount: " + (loadingSummary.ownerCount ?? 0) + "\n"
+ "- segmentCount: " + (loadingSummary.segmentCount ?? 0) + "\n"
+ "- overFiveSecondSegmentCount: " + (loadingSummary.overFiveSecondSegmentCount ?? 0) + "\n"
+ "- longestContinuousSeconds: " + (loadingSummary.longestContinuousSeconds ?? 0) + "\n"
+ "- currentContinuousSeconds: " + (loadingSummary.currentContinuousSeconds ?? 0) + "\n"
+ "- budgetSeconds: " + (loadingSummary.budgetSeconds ?? (Number.isFinite(Number(report.alertThresholds?.visibleLoadingSlowMs)) ? Number(report.alertThresholds.visibleLoadingSlowMs) / 1000 : "unconfigured")) + "\n"
+ "- policy: 该指标只能证明用户真实看到“加载中”的持续时间;修复必须降低真实请求/投影/渲染耗时,禁止提前展示未加载完内容来压低该指标。\n\n"
+ "#### Loading segments\n\n" + loadingSegmentLines + "\n\n"
+ "#### Loading owners\n\n" + loadingOwnerLines + "\n\n"
+ "#### Loading sample timeline\n\n" + loadingTimelineLines + "\n\n"
+ "### Session rail titles\n\n"
+ "- sampleCount: " + (sessionRailTitleSummary.sampleCount ?? 0) + "\n"
+ "- visibleSampleCount: " + (sessionRailTitleSummary.visibleSampleCount ?? 0) + "\n"
+ "- fallbackSampleCount: " + (sessionRailTitleSummary.fallbackSampleCount ?? 0) + "\n"
+ "- majorityFallbackSampleCount: " + (sessionRailTitleSummary.majorityFallbackSampleCount ?? 0) + "\n"
+ "- maxFallbackRatio: " + (sessionRailTitleSummary.maxFallbackRatio ?? 0) + "\n"
+ "- maxVisibleCount: " + (sessionRailTitleSummary.maxVisibleCount ?? 0) + "\n"
+ "- maxFallbackTitleCount: " + (sessionRailTitleSummary.maxFallbackTitleCount ?? 0) + "\n"
+ "- policy: 可见 session 列表中 'Session ses_...' fallback 标题超过一半必须报警;修复应让上游 session list projection 直接携带名称,不能靠点击详情后下游修补。\n\n"
+ "#### Session rail fallback samples\n\n" + sessionRailTitleSampleLines + "\n\n"
+ "#### Session rail fallback examples\n\n" + sessionRailTitleExampleLines + "\n\n"
+ "### Page provenance\n\n"
+ "- segmentCount: " + (report.pageProvenance?.summary?.segmentCount ?? 0) + "\n"
+ "- controlSegmentCount: " + (report.pageProvenance?.summary?.controlSegmentCount ?? 0) + "\n\n"
+ provenanceLines + "\n\n"
+ "### Page performance: same-origin API Resource Timing\n\n"
+ "- budgetMs: " + (report.pagePerformance?.summary?.budgetMs ?? sameOriginApiBudgetMs) + "\n"
+ "- sameOriginApiPathCount: " + (report.pagePerformance?.summary?.sameOriginApiPathCount ?? 0) + "\n"
+ "- sameOriginApiSampleCount: " + (report.pagePerformance?.summary?.sameOriginApiSampleCount ?? 0) + "\n"
+ "- longLivedStreamPathCount: " + (report.pagePerformance?.summary?.longLivedStreamPathCount ?? 0) + "\n"
+ "- longLivedStreamSampleCount: " + (report.pagePerformance?.summary?.longLivedStreamSampleCount ?? 0) + "\n"
+ "- longLivedStreamOpenOverFiveSecondPathCount: " + (report.pagePerformance?.summary?.longLivedStreamOpenOverFiveSecondPathCount ?? 0) + "\n"
+ "- longLivedStreamOpenOverFiveSecondSampleCount: " + (report.pagePerformance?.summary?.longLivedStreamOpenOverFiveSecondSampleCount ?? 0) + "\n"
+ "- longLivedStreamLifetimeOverFiveSecondSampleCount: " + (report.pagePerformance?.summary?.longLivedStreamLifetimeOverFiveSecondSampleCount ?? 0) + "\n"
+ "- slowPathCount: " + (report.pagePerformance?.summary?.slowPathCount ?? 0) + "\n"
+ "- slowSampleCount: " + (report.pagePerformance?.summary?.slowSampleCount ?? 0) + "\n"
+ "- worstP95Ms: " + (report.pagePerformance?.summary?.worstP95Ms ?? "-") + "\n\n"
+ performanceLines + "\n\n"
+ "### Page performance: long-lived streams\n\n"
+ "- policy: SSE/long-lived stream lifetime is not ordinary API load latency; only stream open latency is compared with the YAML usability budget, while disconnects remain runtime alerts.\n\n"
+ streamPerformanceLines + "\n\n"
+ "### Natural API to DOM lag candidates\n\n"
+ "- naturalApiResponseCount: " + (apiDomLagSummary.naturalApiResponseCount ?? 0) + "\n"
+ "- stateRelevantResponseCount: " + (apiDomLagSummary.stateRelevantResponseCount ?? 0) + "\n"
+ "- candidateCount: " + (apiDomLagSummary.candidateCount ?? 0) + "\n"
+ "- domChangedCount: " + (apiDomLagSummary.domChangedCount ?? 0) + "\n"
+ "- noDomChangeWithinWindowCount: " + (apiDomLagSummary.noDomChangeWithinWindowCount ?? 0) + "\n"
+ "- budgetMs: " + (apiDomLagSummary.budgetMs ?? "-") + "\n"
+ "- p95DomChangeDeltaMs: " + (apiDomLagSummary.p95DomChangeDeltaMs ?? "-") + "\n"
+ "- maxDomChangeDeltaMs: " + (apiDomLagSummary.maxDomChangeDeltaMs ?? "-") + "\n"
+ "- overBudgetCount: " + (apiDomLagSummary.overBudgetCount ?? 0) + "\n"
+ "- lowConfidenceStreamOpenCount: " + (apiDomLagSummary.lowConfidenceStreamOpenCount ?? 0) + "\n"
+ "- policy: 该指标是调查证据,不作为 Code Agent 阻塞项;/v1/workbench/events 只能代表 SSE stream-open 到后续 DOM 变化的低置信度候选。\n\n"
+ "#### API-DOM groups\n\n" + apiDomLagGroupLines + "\n\n"
+ "#### Worst API-DOM candidates\n\n" + apiDomLagWorstLines + "\n\n"
+ "### Prompt network\n\n" + promptNetworkLines + "\n\n"
+ "### Runtime alerts\n\n"
+ "- httpErrorCount: " + (alertSummary.httpErrorCount ?? 0) + "\n"
+ "- requestFailedCount: " + (alertSummary.requestFailedCount ?? 0) + "\n"
+ "- domDiagnosticSampleCount: " + (alertSummary.domDiagnosticSampleCount ?? 0) + "\n"
+ "- consoleAlertCount: " + (alertSummary.consoleAlertCount ?? 0) + "\n"
+ "- pageErrorCount: " + (alertSummary.pageErrorCount ?? 0) + "\n\n"
+ "#### HTTP errors\n\n" + httpAlertLines + "\n\n"
+ "#### Request failed\n\n" + requestFailedLines + "\n\n"
+ "#### DOM diagnostics\n\n" + domDiagnosticLines + "\n\n"
+ "#### Console alerts\n\n" + consoleAlertLines + "\n\n"
+ "#### Console alert groups\n\n" + consoleAlertGroupLines + "\n\n"
+ "### Turn timing table\n\n"
+ turnTimingTable + "\n\n"
+ "### Aggregate timeline\n\n"
+ metricLines + "\n\n"
+ "## Command timeline\n\n" + commandLines + "\n\n"
+ "## State transitions\n\n" + transitionLines + "\n";
}
async function fileMeta(file) {
const [buffer, stats] = await Promise.all([readFile(file), stat(file)]);
return { byteCount: stats.size, sha256: "sha256:" + createHash("sha256").update(buffer).digest("hex") };
}
function sha256(value) {
return "sha256:" + createHash("sha256").update(String(value)).digest("hex");
}
function urlPath(value) {
try {
const url = new URL(String(value || "http://invalid.local/"));
return url.pathname;
} catch {
return "-";
}
}
function compactLocation(value) {
if (!value || typeof value !== "object") return null;
return { urlPath: urlPath(value.url), lineNumber: value.lineNumber ?? null, columnNumber: value.columnNumber ?? null };
}
function limitText(value, limit) {
const text = String(value ?? "");
if (text.length <= limit) return text;
return text.slice(0, Math.max(0, limit - 1)) + "…";
}
`;
}