fix: bound codex task supervisor output

This commit is contained in:
Codex
2026-05-22 18:06:14 +00:00
parent 05b77151b8
commit f54645ba07
4 changed files with 227 additions and 109 deletions
+155 -84
View File
@@ -12,12 +12,13 @@ const defaultOutputLimit = 20;
const defaultTextPreviewChars = 12_000;
const defaultTasksLimit = 20;
const maxTasksLimit = 100;
const supervisorSectionReturnedLimit = 5;
const supervisorRecentCompletedLimit = 5;
const supervisorPromptPreviewChars = 160;
const supervisorBodyPreviewChars = 180;
const supervisorRecentBodyPreviewChars = 80;
const diagnosticsIdPreviewLimit = 12;
const diagnosticsReasonPreviewLimit = 6;
const supervisorPromptPreviewChars = 90;
const supervisorBodyPreviewChars = 90;
const supervisorRecentBodyPreviewChars = 60;
const diagnosticsIdPreviewLimit = 3;
const diagnosticsReasonPreviewLimit = 2;
const steerPromptPreviewChars = 320;
const minimaxSubmitModel = "minimax-m2.7";
const deepseekSubmitModel = "deepseek-chat";
@@ -193,32 +194,37 @@ interface CodexTasksEntry {
};
}
interface SupervisorTextSummary {
text: string;
chars: number;
truncated: boolean;
}
interface SupervisorMessageSummary extends SupervisorTextSummary {
at: unknown;
source: unknown;
}
interface CodexTasksSupervisorEntry {
taskId: string;
queueId: string | null;
id: string;
queue: string | null;
status: string | null;
currentAttempt: number | null;
attempt: number | null;
updatedAt: string | null;
finishedAt: string | null;
unreadTerminal: boolean;
issueRefs: string[];
classification: {
kind: "direct-progress" | "deployment-fix" | "verification" | "management-noise" | "documentation" | "unknown";
labels: string[];
managementNoise: boolean;
reason: string;
};
prompt: Record<string, unknown>;
lastMessage: Record<string, unknown> | null;
queuedReason: Record<string, unknown> | null;
commands: {
show: string;
detail: string;
trace: string;
output: string;
full: string;
read: string;
};
finishedAt?: string | null;
unreadTerminal?: boolean;
issues: string[];
kind: "direct-progress" | "deployment-fix" | "verification" | "management-noise" | "documentation" | "unknown";
noise?: boolean;
prompt: string;
promptChars: number;
promptTruncated?: boolean;
last?: string;
lastAt?: unknown;
lastChars?: number;
lastTruncated?: boolean;
queuedReason?: Record<string, unknown>;
read?: string;
}
interface CodexTasksSection<T = CodexTasksEntry> {
@@ -229,9 +235,12 @@ interface CodexTasksSection<T = CodexTasksEntry> {
commands: {
next: string | null;
full: string;
showTemplate?: string;
detailTemplate?: string;
traceTemplate?: string;
outputTemplate?: string;
taskFullTemplate?: string;
readTemplate?: string;
};
items: T[];
}
@@ -404,6 +413,16 @@ function compactInlinePreview(value: string, maxChars: number): Record<string, u
return textPreview(value.replace(/\s+/gu, " ").trim(), maxChars);
}
function supervisorTextSummary(value: string, maxChars: number): SupervisorTextSummary {
const compact = value.replace(/\s+/gu, " ").trim();
const truncated = compact.length > maxChars;
return {
text: truncated ? compact.slice(0, maxChars) : compact,
chars: compact.length,
truncated,
};
}
function fmtDuration(ms: unknown): string {
const value = Number(ms);
if (!Number.isFinite(value) || value < 0) return "--";
@@ -1157,6 +1176,35 @@ function compactExecutionDiagnostics(value: unknown): Record<string, unknown> |
};
}
function supervisorExecutionDiagnostics(value: unknown): Record<string, unknown> | null {
const diagnostics = compactExecutionDiagnostics(value);
if (diagnostics === null) return null;
const liveness = asRecord(diagnostics.liveness) ?? {};
const listBudget = asRecord(diagnostics.listBudget) ?? {};
return {
state: diagnostics.state ?? null,
effectiveLiveness: diagnostics.effectiveLiveness ?? null,
recommendedAction: diagnostics.recommendedAction ?? null,
splitBrainLive: diagnostics.splitBrainLive ?? null,
activeHeartbeatCount: diagnostics.activeHeartbeatCount ?? null,
databaseActiveTaskCount: diagnostics.databaseActiveTaskCount ?? null,
schedulerActiveRunSlotCount: diagnostics.schedulerActiveRunSlotCount ?? null,
lastObservedAgentEventAt: diagnostics.lastObservedAgentEventAt ?? null,
heartbeatFreshTaskIds: diagnostics.heartbeatFreshTaskIds ?? [],
heartbeatRiskTaskIds: diagnostics.heartbeatRiskTaskIds ?? [],
traceGapTaskIds: diagnostics.traceGapTaskIds ?? [],
reasons: diagnostics.reasons ?? [],
interpretation: liveness.interpretation ?? null,
listBudget: {
idPreviewLimit: listBudget.idPreviewLimit ?? diagnosticsIdPreviewLimit,
reasonPreviewLimit: listBudget.reasonPreviewLimit ?? diagnosticsReasonPreviewLimit,
truncated: listBudget.truncated ?? false,
omittedCounts: listBudget.omittedCounts ?? {},
rawCommand: listBudget.rawCommand ?? "bun scripts/cli.ts microservice proxy code-queue /api/tasks/overview?limit=30 --raw --full",
},
};
}
function compactToolSummary(value: unknown, full: boolean): Record<string, unknown> {
const record = asRecord(value) ?? {};
const items = asArray(record.items).map((item) => {
@@ -1730,7 +1778,12 @@ function taskIssueRefs(task: Record<string, unknown>, summary: Record<string, un
return Array.from(new Set(Array.from(text.matchAll(/#(\d{1,6})\b/gu)).map((match) => `#${match[1]}`))).slice(0, 8);
}
function taskClassification(task: Record<string, unknown>, summary: Record<string, unknown> | null): CodexTasksSupervisorEntry["classification"] {
function taskClassification(task: Record<string, unknown>, summary: Record<string, unknown> | null): {
kind: CodexTasksSupervisorEntry["kind"];
labels: string[];
managementNoise: boolean;
reason: string;
} {
const text = [
asString(task.displayPrompt),
asString(task.basePrompt),
@@ -1765,23 +1818,14 @@ function taskClassification(task: Record<string, unknown>, summary: Record<strin
return { kind: "unknown", labels, managementNoise: false, reason: "no strong classifier term matched" };
}
function supervisorLastMessage(summaryLastAssistant: unknown, maxChars: number): Record<string, unknown> | null {
function supervisorLastMessage(summaryLastAssistant: unknown, maxChars: number): SupervisorMessageSummary | null {
if (summaryLastAssistant === undefined || summaryLastAssistant === null) return null;
const record = asRecord(summaryLastAssistant) ?? {};
const text = asString(record.text);
if (text.length === 0) {
return {
at: record.at ?? null,
seq: record.seq ?? null,
source: record.source ?? "none",
...compactInlinePreview("", maxChars),
};
}
return {
at: record.at ?? null,
seq: record.seq ?? null,
source: record.source ?? "none",
...compactInlinePreview(text, maxChars),
...supervisorTextSummary(text, maxChars),
};
}
@@ -1825,35 +1869,34 @@ function taskWatchEntry(task: Record<string, unknown>, summary: Record<string, u
function taskSupervisorEntry(task: Record<string, unknown>, summary: Record<string, unknown> | null, bodyPreviewChars = supervisorBodyPreviewChars): CodexTasksSupervisorEntry {
const taskId = asString(task.id);
const summaryCommands = summary === null ? null : asRecord(summary.commands);
const summaryLastAssistant = summary?.lastAssistantMessage ?? task.lastAssistantMessage;
const showCommand = typeof summary?.cliHint === "string" && summary.cliHint.length > 0
? summary.cliHint
: `bun scripts/cli.ts codex task ${taskId}`;
const traceCommand = typeof summary?.traceHint === "string" && summary.traceHint.length > 0
? summary.traceHint
: `bun scripts/cli.ts codex task ${taskId} --trace --tail --limit ${defaultTraceLimit}`;
const status = asString(task.status) || null;
const unreadTerminal = taskUnreadTerminal(task);
const classification = taskClassification(task, summary);
const queuedReason = compactQueuedReason(task.queuedReason);
const prompt = supervisorTextSummary(asString(task.displayPrompt ?? task.basePrompt ?? task.prompt), supervisorPromptPreviewChars);
const lastMessage = supervisorLastMessage(summaryLastAssistant, bodyPreviewChars);
return {
taskId,
queueId: asString(task.queueId) || null,
status: asString(task.status) || null,
currentAttempt: typeof task.currentAttempt === "number" && Number.isFinite(task.currentAttempt) ? task.currentAttempt : null,
id: taskId,
queue: asString(task.queueId) || null,
status,
attempt: typeof task.currentAttempt === "number" && Number.isFinite(task.currentAttempt) ? task.currentAttempt : null,
updatedAt: asString(task.updatedAt) || null,
finishedAt: asString(task.finishedAt) || null,
unreadTerminal: taskUnreadTerminal(task),
issueRefs: taskIssueRefs(task, summary),
classification: taskClassification(task, summary),
prompt: compactInlinePreview(asString(task.displayPrompt ?? task.basePrompt ?? task.prompt), supervisorPromptPreviewChars),
lastMessage: supervisorLastMessage(summaryLastAssistant, bodyPreviewChars),
queuedReason: compactQueuedReason(task.queuedReason),
commands: {
show: typeof summaryCommands?.show === "string" && summaryCommands.show.length > 0 ? summaryCommands.show : showCommand,
detail: `bun scripts/cli.ts codex task ${taskId} --detail`,
trace: typeof summaryCommands?.trace === "string" && summaryCommands.trace.length > 0 ? summaryCommands.trace : traceCommand,
output: `bun scripts/cli.ts codex output ${taskId} --tail --limit ${defaultOutputLimit}`,
full: `bun scripts/cli.ts codex task ${taskId} --full`,
read: `bun scripts/cli.ts codex read ${taskId}`,
},
...(isTerminalTaskStatus(status) ? { finishedAt: asString(task.finishedAt) || null, unreadTerminal } : {}),
issues: taskIssueRefs(task, summary),
kind: classification.kind,
...(classification.managementNoise ? { noise: true } : {}),
prompt: prompt.text,
promptChars: prompt.chars,
...(prompt.truncated ? { promptTruncated: true } : {}),
...(lastMessage === null ? {} : {
last: lastMessage.text,
lastAt: lastMessage.at,
lastChars: lastMessage.chars,
...(lastMessage.truncated ? { lastTruncated: true } : {}),
}),
...(queuedReason === null ? {} : { queuedReason }),
...(unreadTerminal ? { read: `bun scripts/cli.ts codex read ${taskId}` } : {}),
};
}
@@ -1891,18 +1934,24 @@ function buildSupervisorTaskSection(
const visibleTasks = tasks.slice(0, limit);
const items = visibleTasks.map((task) => taskSupervisorEntry(task, summaries.get(taskOverviewCandidateKey(task)) ?? null, bodyPreviewChars));
const truncated = tasks.length > limit;
const commands = {
next: truncated || nextCommand !== null ? nextCommand : null,
full: fullCommand,
...(items.length === 0 ? {} : {
showTemplate: "bun scripts/cli.ts codex task <taskId>",
detailTemplate: "bun scripts/cli.ts codex task <taskId> --detail",
traceTemplate: `bun scripts/cli.ts codex task <taskId> --trace --tail --limit ${defaultTraceLimit}`,
outputTemplate: `bun scripts/cli.ts codex output <taskId> --tail --limit ${defaultOutputLimit}`,
taskFullTemplate: "bun scripts/cli.ts codex task <taskId> --full",
readTemplate: "bun scripts/cli.ts codex read <taskId>",
}),
};
return {
count: tasks.length,
returned: items.length,
truncated,
hasMore: truncated,
commands: {
next: truncated ? nextCommand : null,
full: fullCommand,
detailTemplate: "bun scripts/cli.ts codex task <taskId> --detail",
traceTemplate: `bun scripts/cli.ts codex task <taskId> --trace --tail --limit ${defaultTraceLimit}`,
outputTemplate: `bun scripts/cli.ts codex output <taskId> --tail --limit ${defaultOutputLimit}`,
},
hasMore: truncated || nextCommand !== null,
commands,
items,
};
}
@@ -1976,6 +2025,24 @@ function taskListCommand(options: CodexTasksOptions, extra: string[] = []): stri
return `bun scripts/cli.ts ${args.join(" ")}`;
}
function taskSectionLimit(options: CodexTasksOptions, maxReturned = supervisorSectionReturnedLimit): number {
return Math.min(options.limit, maxReturned);
}
function sectionNextCommand(
tasks: Record<string, unknown>[],
limit: number,
options: CodexTasksOptions,
sourceNextCommand: string | null,
): string | null {
if (tasks.length > limit) {
const lastVisibleTask = tasks[limit - 1];
const beforeId = lastVisibleTask === undefined ? "" : taskOverviewCandidateKey(lastVisibleTask);
if (beforeId.length > 0) return taskListCommand({ ...options, beforeId });
}
return sourceNextCommand;
}
function fetchTaskSummaries(taskIds: string[], fetcher: CodexResponseFetcher): { summaries: Map<string, Record<string, unknown>>; degraded: CodexTasksDegraded | null } {
const boundedIds = taskIds.slice(0, maxTasksLimit);
const summaries = new Map<string, Record<string, unknown>>();
@@ -2064,18 +2131,19 @@ function codexTasksOverviewResult(
const sourceHasMore = asBoolean(taskPage.pagination.hasMore);
const nextCommand = sourceHasMore && nextBeforeId !== null ? taskListCommand({ ...options, beforeId: nextBeforeId }) : null;
const fullCommand = taskListCommand({ ...options, view: "full" });
const sectionLimit = taskSectionLimit(options);
const recentLimit = Math.min(options.limit, supervisorRecentCompletedLimit);
const runningSection = buildSupervisorTaskSection(runningTasks, summaries, options.limit, nextCommand, fullCommand);
const unreadSection = buildSupervisorTaskSection(unreadCompletedTasks, summaries, options.limit, nextCommand, fullCommand);
const recentSection = buildSupervisorTaskSection(recentCompletedTasks, summaries, recentLimit, nextCommand, fullCommand, supervisorRecentBodyPreviewChars);
const queuedSection = buildSupervisorTaskSection(queuedTasks, summaries, options.limit, nextCommand, fullCommand);
const runningSection = buildSupervisorTaskSection(runningTasks, summaries, sectionLimit, sectionNextCommand(runningTasks, sectionLimit, options, nextCommand), fullCommand);
const unreadSection = buildSupervisorTaskSection(unreadCompletedTasks, summaries, sectionLimit, sectionNextCommand(unreadCompletedTasks, sectionLimit, options, nextCommand), fullCommand);
const recentSection = buildSupervisorTaskSection(recentCompletedTasks, summaries, recentLimit, sectionNextCommand(recentCompletedTasks, recentLimit, options, nextCommand), fullCommand, supervisorRecentBodyPreviewChars);
const queuedSection = buildSupervisorTaskSection(queuedTasks, summaries, sectionLimit, sectionNextCommand(queuedTasks, sectionLimit, options, nextCommand), fullCommand);
const pagination = taskPage.pagination;
const diagnostics = compactExecutionDiagnostics(asRecord(taskPage.queue)?.executionDiagnostics);
const diagnostics = supervisorExecutionDiagnostics(asRecord(taskPage.queue)?.executionDiagnostics);
const visibleSupervisorItems = [...runningSection.items, ...unreadSection.items, ...recentSection.items, ...queuedSection.items];
const classifierCounts = visibleSupervisorItems.reduce((counts, item) => {
const key = item.classification.kind;
const key = item.kind;
counts[key] = (counts[key] ?? 0) + 1;
if (item.classification.managementNoise) counts.managementNoise = (counts.managementNoise ?? 0) + 1;
if (item.noise) counts.managementNoise = (counts.managementNoise ?? 0) + 1;
return counts;
}, {} as Record<string, number>);
return {
@@ -2106,8 +2174,10 @@ function codexTasksOverviewResult(
outputBudget: {
promptPreviewChars: supervisorPromptPreviewChars,
bodyPreviewChars: supervisorBodyPreviewChars,
sectionReturnedLimit: sectionLimit,
recentCompletedReturnedLimit: recentLimit,
recentCompletedBodyPreviewChars: supervisorRecentBodyPreviewChars,
rowCommands: "task id is the row drill-down key; show/detail/trace/output/full/read are section templates to avoid repeated noise",
},
fullCommand,
next: nextCommand,
@@ -2188,11 +2258,12 @@ function visibleTaskIdsForOverview(tasks: Record<string, unknown>[], options: Co
if (options.view === "full") {
return filtered.slice(0, options.limit).map((task) => taskOverviewCandidateKey(task)).filter((taskId) => taskId.length > 0);
}
const sectionLimit = taskSectionLimit(options);
return Array.from(new Set([
...sortRunningWatchTasks(filtered).slice(0, options.limit),
...sortCompletedWatchTasks(filtered).filter((task) => taskUnreadTerminal(task)).slice(0, options.limit),
...sortRunningWatchTasks(filtered).slice(0, sectionLimit),
...sortCompletedWatchTasks(filtered).filter((task) => taskUnreadTerminal(task)).slice(0, sectionLimit),
...sortCompletedWatchTasks(filtered).filter((task) => !taskUnreadTerminal(task)).slice(0, Math.min(options.limit, supervisorRecentCompletedLimit)),
...sortQueuedWatchTasks(filtered).slice(0, options.limit),
...sortQueuedWatchTasks(filtered).slice(0, sectionLimit),
].map((task) => taskOverviewCandidateKey(task))))
.filter((taskId) => taskId.length > 0);
}