fix: summarize truncated debug output

This commit is contained in:
Codex
2026-06-29 13:02:01 +00:00
parent 1733572f13
commit 4ebebe9357
+187
View File
@@ -271,6 +271,10 @@ function summarizeEnvelope(envelope: JsonEnvelope<unknown>): Record<string, unkn
summary.topLevelKeys = Object.keys(source).slice(0, 40);
const arrayCounts = summarizeArrayCounts(source);
if (Object.keys(arrayCounts).length > 0) summary.arrayCounts = arrayCounts;
const debugDispatch = summarizeDebugDispatch(source);
if (debugDispatch !== null) summary.debugDispatch = debugDispatch;
const providerTriage = summarizeProviderTriage(source);
if (providerTriage !== null) summary.providerTriage = providerTriage;
const issue = source.issue;
if (typeof issue === "object" && issue !== null) {
const issueRecord = issue as Record<string, unknown>;
@@ -303,6 +307,189 @@ function summarizeArrayCounts(source: Record<string, unknown>): Record<string, n
return result;
}
function summarizeDebugDispatch(source: Record<string, unknown>): Record<string, unknown> | null {
const dispatch = recordOrNull(source.dispatch);
const wait = recordOrNull(source.wait);
if (dispatch === null && wait === null) return null;
const dispatchBody = recordOrNull(dispatch?.body);
const waitTask = recordOrNull(wait?.task);
const waitPayload = recordOrNull(waitTask?.payload);
const waitResult = recordOrNull(waitTask?.result);
const result: Record<string, unknown> = {
dispatch: dispatch === null ? null : {
...pickSummary(dispatch, ["ok", "status", "responseBytesRead", "responseContentLength", "responseTruncated"]),
body: dispatchBody === null ? null : pickSummary(dispatchBody, ["ok", "providerOnline", "status", "taskId"]),
},
wait: wait === null ? null : pickSummary(wait, ["ok", "timeoutMs"]),
task: waitTask === null ? null : {
...pickSummary(waitTask, ["id", "status", "command", "providerId", "createdAt", "updatedAt"]),
payload: waitPayload === null ? null : pickSummary(waitPayload, ["mode", "source"]),
},
};
const providerUpgrade = summarizeProviderUpgradeResult(waitResult);
if (providerUpgrade !== null) result.providerUpgrade = providerUpgrade;
return result;
}
function summarizeProviderUpgradeResult(result: Record<string, unknown> | null): Record<string, unknown> | null {
if (result === null) return null;
const schedulerResult = recordOrNull(result.schedulerResult);
const plan = recordOrNull(result.plan) ?? recordOrNull(schedulerResult?.plan);
const finalPromotedContainer = recordOrNull(result.finalPromotedContainer)
?? recordOrNull(schedulerResult?.finalPromotedContainer)
?? recordOrNull(plan?.finalPromotedContainer);
const heartbeat = recordOrNull(result.heartbeat);
const heartbeatConfirmation = recordOrNull(plan?.finalPromotedHeartbeatConfirmation)
?? recordOrNull(schedulerResult?.finalPromotedHeartbeatConfirmation);
const summary: Record<string, unknown> = {
...pickSummary(result, ["ok", "reason", "taskId", "providerId", "targetProviderGatewayVersion", "candidateValidationWasTerminal"]),
};
const mode = result.mode ?? schedulerResult?.mode;
if (mode !== undefined) summary.mode = mode;
const message = stringOrNull(result.message) ?? stringOrNull(schedulerResult?.message);
if (message !== null) summary.message = shortText(message, 240);
const providerGatewayVersion = result.providerGatewayVersion ?? schedulerResult?.providerGatewayVersion ?? plan?.providerGatewayVersion;
if (providerGatewayVersion !== undefined) summary.providerGatewayVersion = providerGatewayVersion;
if (schedulerResult !== null) {
summary.schedulerResult = {
...pickSummary(schedulerResult, [
"mode",
"message",
"providerId",
"providerName",
"providerGatewayVersion",
"targetProviderGatewayVersion",
"updaterContainerId",
"candidateValidationIsNotTerminalSuccess",
"terminalStatusManagedByBackendCore",
]),
};
}
if (plan !== null) {
summary.plan = {
...pickSummary(plan, [
"taskId",
"providerId",
"providerName",
"policy",
"hostProjectRoot",
"workspace",
"workspaceStateMount",
"providerGatewayName",
"providerGatewayVersion",
"targetProviderGatewayName",
"targetProviderGatewayVersion",
"runnerImage",
"updaterName",
"candidateName",
]),
compose: pickSummary(recordOrNull(plan.compose) ?? {}, ["composeFile", "envFile", "project", "service"]),
heartbeatConfirmation: heartbeatConfirmation === null ? null : pickSummary(heartbeatConfirmation, [
"providerId",
"required",
"heartbeatTimeoutMs",
"targetProviderGatewayVersion",
"finalContainerMustNotEqualCandidate",
]),
};
}
if (finalPromotedContainer !== null) {
summary.finalPromotedContainer = pickSummary(finalPromotedContainer, [
"containerId",
"containerName",
"version",
"restartPolicy",
"pidMode",
"heartbeatTimestamp",
"containerIdUnavailableReason",
"versionUnavailableReason",
"restartPolicyUnavailableReason",
"pidModeUnavailableReason",
"heartbeatTimestampUnavailableReason",
]);
}
if (heartbeat !== null) {
const heartbeatSummary = pickSummary(heartbeat, ["providerId", "version", "containerId", "containerName", "restartPolicy", "pidMode", "timestamp"]);
if (Object.keys(heartbeatSummary).length > 0) summary.heartbeat = heartbeatSummary;
}
return summary;
}
function summarizeProviderTriage(source: Record<string, unknown>): Record<string, unknown> | null {
if (!("decision" in source) || !("providerId" in source) || !Array.isArray(source.signals)) return null;
const signals = source.signals.map(recordOrNull).filter((item): item is Record<string, unknown> => item !== null);
const issueSignals = signals
.filter((item) => item.status === "failed" || item.status === "degraded" || item.status === "unknown")
.sort((left, right) => signalStatusRank(left.status) - signalStatusRank(right.status));
const previewSignals = (issueSignals.length > 0 ? issueSignals : signals).slice(0, 8);
return {
...pickSummary(source, ["ok", "providerId", "decision", "scope", "retryable", "blockingDisposition", "observedAt"]),
failedScopes: boundedStringArray(source.failedScopes, 12),
degradedScopes: boundedStringArray(source.degradedScopes, 12),
healthyScopes: boundedStringArray(source.healthyScopes, 12),
failedIndependentScopes: boundedStringArray(source.failedIndependentScopes, 12),
healthyIndependentScopes: boundedStringArray(source.healthyIndependentScopes, 12),
signalCounts: providerSignalCounts(signals, previewSignals.length),
recommendedCrossChecks: boundedStringArray(source.recommendedCrossChecks, 8),
rationale: boundedStringArray(source.rationale, 8),
signalsPreview: previewSignals.map((item) => ({
...pickSummary(item, ["id", "scope", "status", "independentPath", "observedAt"]),
summary: shortText(stringOrNull(item.summary) ?? "", 220),
})),
};
}
function providerSignalCounts(signals: Record<string, unknown>[], returned: number): Record<string, unknown> {
const counts = {
total: signals.length,
returned,
ok: 0,
degraded: 0,
failed: 0,
unknown: 0,
};
for (const signal of signals) {
if (signal.status === "ok") counts.ok += 1;
else if (signal.status === "degraded") counts.degraded += 1;
else if (signal.status === "failed") counts.failed += 1;
else counts.unknown += 1;
}
return {
...counts,
omittedSignals: Math.max(0, signals.length - returned),
};
}
function signalStatusRank(status: unknown): number {
if (status === "failed") return 0;
if (status === "degraded") return 1;
if (status === "unknown") return 2;
if (status === "ok") return 3;
return 4;
}
function boundedStringArray(value: unknown, limit: number): Record<string, unknown> {
const items = Array.from(new Set((Array.isArray(value) ? value : []).map((item) => String(item ?? "")).filter(Boolean)));
return {
items: items.slice(0, limit),
count: items.length,
truncated: items.length > limit,
omitted: Math.max(0, items.length - limit),
};
}
function recordOrNull(value: unknown): Record<string, unknown> | null {
return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : null;
}
function stringOrNull(value: unknown): string | null {
return typeof value === "string" ? value : null;
}
function shortText(value: string, maxChars: number): string {
return value.length <= maxChars ? value : `${value.slice(0, Math.max(0, maxChars - 3))}...`;
}
function pickSummary(source: Record<string, unknown>, keys: string[]): Record<string, unknown> {
const result: Record<string, unknown> = {};
for (const key of keys) {