fix: clarify codex supervisor active counts
This commit is contained in:
+191
-3
@@ -189,6 +189,7 @@ interface CompactTaskMutationResponseOptions {
|
||||
|
||||
interface CodexTasksOptions {
|
||||
queueId: string | undefined;
|
||||
requestedLimit: number;
|
||||
limit: number;
|
||||
beforeId: string | undefined;
|
||||
unreadOnly: boolean;
|
||||
@@ -288,6 +289,14 @@ interface CodexTasksDegraded {
|
||||
reason: string;
|
||||
}
|
||||
|
||||
interface SupervisorStatusCounts {
|
||||
counts: Record<string, number>;
|
||||
exact: boolean;
|
||||
source: "queue-summary-counts" | "overview-page-fallback";
|
||||
scope: "all-queues" | "queue";
|
||||
queueId: string | null;
|
||||
}
|
||||
|
||||
interface CodexQueuesOptions {
|
||||
full: boolean;
|
||||
limit: number;
|
||||
@@ -1851,9 +1860,11 @@ function parseTasksOptions(args: string[]): CodexTasksOptions {
|
||||
const unsupported = statusFilter.filter((status) => !supported.has(status));
|
||||
if (unsupported.length > 0) throw new Error(`unsupported --status value: ${unsupported.join(", ")}; supported: ${Array.from(supported).join(", ")}`);
|
||||
}
|
||||
const requestedLimit = positiveIntegerOption(args, ["--limit"], defaultTasksLimit);
|
||||
return {
|
||||
queueId: optionValue(args, ["--queue", "--queue-id"]),
|
||||
limit: positiveIntegerOption(args, ["--limit"], defaultTasksLimit, maxTasksLimit),
|
||||
requestedLimit,
|
||||
limit: Math.min(requestedLimit, maxTasksLimit),
|
||||
beforeId: optionValue(args, ["--before-id", "--beforeId"]),
|
||||
unreadOnly: hasFlag(args, "--unread-only") || hasFlag(args, "--unread"),
|
||||
statusFilter,
|
||||
@@ -2376,13 +2387,25 @@ function filterTasksForOptions(tasks: Record<string, unknown>[], options: CodexT
|
||||
.filter((task) => !options.unreadOnly || taskUnreadTerminal(task));
|
||||
}
|
||||
|
||||
function baseTaskListOptions(options: CodexTasksOptions): CodexTasksOptions {
|
||||
return {
|
||||
queueId: options.queueId,
|
||||
requestedLimit: options.requestedLimit,
|
||||
limit: options.limit,
|
||||
beforeId: options.beforeId,
|
||||
unreadOnly: options.unreadOnly,
|
||||
statusFilter: options.statusFilter,
|
||||
view: options.view,
|
||||
};
|
||||
}
|
||||
|
||||
function taskListCommand(options: CodexTasksOptions, extra: string[] = []): string {
|
||||
const args = ["codex", "tasks"];
|
||||
if (options.view !== "supervisor") args.push("--view", options.view);
|
||||
if (options.queueId !== undefined) args.push("--queue", options.queueId);
|
||||
if (options.unreadOnly) args.push("--unread");
|
||||
if (options.statusFilter !== null) args.push("--status", options.statusFilter.join(","));
|
||||
if (options.limit !== defaultTasksLimit) args.push("--limit", String(options.limit));
|
||||
if (options.requestedLimit !== defaultTasksLimit) args.push("--limit", String(options.requestedLimit));
|
||||
if (options.beforeId !== undefined) args.push("--before-id", options.beforeId);
|
||||
args.push(...extra);
|
||||
return `bun scripts/cli.ts ${args.join(" ")}`;
|
||||
@@ -2406,6 +2429,155 @@ function sectionNextCommand(
|
||||
return sourceNextCommand;
|
||||
}
|
||||
|
||||
function countRecordValues(value: unknown): Record<string, number> {
|
||||
const record = asRecord(value);
|
||||
const counts: Record<string, number> = {};
|
||||
if (record === null) return counts;
|
||||
for (const [status, rawCount] of Object.entries(record)) {
|
||||
const count = typeof rawCount === "number" ? rawCount : typeof rawCount === "string" ? Number(rawCount) : Number.NaN;
|
||||
if (Number.isFinite(count) && count >= 0) counts[status] = Math.floor(count);
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
function queueSummaryCountsForOptions(taskPage: CodexTasksTaskPage, options: CodexTasksOptions): SupervisorStatusCounts | null {
|
||||
const queue = taskPage.queue;
|
||||
if (queue === null) return null;
|
||||
if (options.queueId !== undefined) {
|
||||
for (const row of asArray(queue.queues)) {
|
||||
const record = asRecord(row);
|
||||
if (record === null || asString(record.id) !== options.queueId) continue;
|
||||
if (asRecord(record.counts) === null) return null;
|
||||
return {
|
||||
counts: countRecordValues(record.counts),
|
||||
exact: true,
|
||||
source: "queue-summary-counts",
|
||||
scope: "queue",
|
||||
queueId: options.queueId,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (asRecord(queue.counts) === null) return null;
|
||||
return {
|
||||
counts: countRecordValues(queue.counts),
|
||||
exact: true,
|
||||
source: "queue-summary-counts",
|
||||
scope: "all-queues",
|
||||
queueId: null,
|
||||
};
|
||||
}
|
||||
|
||||
function fallbackStatusCountsForOptions(taskPage: CodexTasksTaskPage, options: CodexTasksOptions): SupervisorStatusCounts {
|
||||
const counts: Record<string, number> = {};
|
||||
for (const task of taskPage.tasks) {
|
||||
if (options.queueId !== undefined && asString(task.queueId) !== options.queueId) continue;
|
||||
const status = asString(task.status);
|
||||
if (status.length === 0) continue;
|
||||
counts[status] = (counts[status] ?? 0) + 1;
|
||||
}
|
||||
return {
|
||||
counts,
|
||||
exact: false,
|
||||
source: "overview-page-fallback",
|
||||
scope: options.queueId === undefined ? "all-queues" : "queue",
|
||||
queueId: options.queueId ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
function statusCountsForOptions(taskPage: CodexTasksTaskPage, options: CodexTasksOptions): SupervisorStatusCounts {
|
||||
return queueSummaryCountsForOptions(taskPage, options) ?? fallbackStatusCountsForOptions(taskPage, options);
|
||||
}
|
||||
|
||||
function positiveCount(value: unknown): number | null {
|
||||
const count = asNumber(value, Number.NaN);
|
||||
return Number.isFinite(count) && count >= 0 ? Math.floor(count) : null;
|
||||
}
|
||||
|
||||
function runtimeMaxActiveQueues(taskPage: CodexTasksTaskPage): number | null {
|
||||
const value = positiveCount(taskPage.queue?.maxActiveQueues);
|
||||
return value !== null && value > 0 ? value : null;
|
||||
}
|
||||
|
||||
function activeRunningRedlineState(activeCount: number, hardRedline: number, burstRedline: number, routineTarget: number): string {
|
||||
if (activeCount >= hardRedline) return "at-or-over-hard-redline";
|
||||
if (activeCount >= burstRedline) return "burst-redline";
|
||||
if (activeCount >= routineTarget) return "above-routine-target";
|
||||
return "below-routine-target";
|
||||
}
|
||||
|
||||
function supervisorActiveRunningSummary(
|
||||
taskPage: CodexTasksTaskPage,
|
||||
options: CodexTasksOptions,
|
||||
runningSection: CodexTasksSection<CodexTasksSupervisorEntry>,
|
||||
diagnostics: Record<string, unknown> | null,
|
||||
): Record<string, unknown> {
|
||||
const statusCounts = statusCountsForOptions(taskPage, options);
|
||||
const runningCount = statusCounts.counts.running ?? 0;
|
||||
const judgingCount = statusCounts.counts.judging ?? 0;
|
||||
const activeStatusCount = runningCount + judgingCount;
|
||||
const heartbeatActiveCount = positiveCount(diagnostics?.activeHeartbeatCount);
|
||||
const schedulerLocalActiveRunSlotCount = positiveCount(diagnostics?.schedulerActiveRunSlotCount);
|
||||
const effectiveActiveRunnerCount = Math.max(activeStatusCount, heartbeatActiveCount ?? 0, schedulerLocalActiveRunSlotCount ?? 0);
|
||||
const policy = submitPolicyContract().concurrency;
|
||||
const routineTarget = policy.gpt55Routine;
|
||||
const burstRedline = Math.max(policy.gpt55BurstMax, policy.minimaxSimpleMax);
|
||||
const hardRedline = 15;
|
||||
const runtimeLimit = runtimeMaxActiveQueues(taskPage);
|
||||
const redlineState = activeRunningRedlineState(activeStatusCount, hardRedline, burstRedline, routineTarget);
|
||||
const exactInterpretation = statusCounts.exact
|
||||
? "activeRunning.count is exact from queue status counts; row pagination does not change this count"
|
||||
: "activeRunning.count is page-scoped because queue status counts were absent; do not use it for commander redline decisions without a queue summary/raw cross-check";
|
||||
const baseOptions = baseTaskListOptions({ ...options, beforeId: undefined, unreadOnly: false, statusFilter: null });
|
||||
const runningOptions: CodexTasksOptions = { ...baseOptions, statusFilter: ["running", "judging"] };
|
||||
const queuedOptions: CodexTasksOptions = { ...baseOptions, statusFilter: ["queued", "retry_wait"] };
|
||||
return {
|
||||
count: activeStatusCount,
|
||||
exact: statusCounts.exact,
|
||||
source: statusCounts.source,
|
||||
scope: statusCounts.scope,
|
||||
queueId: statusCounts.queueId,
|
||||
statuses: {
|
||||
running: runningCount,
|
||||
judging: judgingCount,
|
||||
},
|
||||
effectiveActiveRunnerCount,
|
||||
effectiveActiveRunnerCountSources: {
|
||||
activeStatusCount,
|
||||
heartbeatActiveCount,
|
||||
schedulerLocalActiveRunSlotCount,
|
||||
},
|
||||
rowPage: {
|
||||
returned: runningSection.returned,
|
||||
availableInCurrentOverview: runningSection.count,
|
||||
returnedLimit: taskSectionLimit(options),
|
||||
truncated: runningSection.truncated,
|
||||
hasMore: runningSection.hasMore,
|
||||
distinction: "returned is the compact row page; count is the active running total used for supervision",
|
||||
},
|
||||
redline: {
|
||||
countField: "supervisor.activeRunning.count",
|
||||
routineTarget,
|
||||
burstRedline,
|
||||
hardRedline,
|
||||
runtimeMaxActiveQueues: runtimeLimit,
|
||||
state: redlineState,
|
||||
remainingToRoutineTarget: Math.max(0, routineTarget - activeStatusCount),
|
||||
remainingToBurstRedline: Math.max(0, burstRedline - activeStatusCount),
|
||||
remainingToHardRedline: Math.max(0, hardRedline - activeStatusCount),
|
||||
decisionReady: statusCounts.exact,
|
||||
interpretation: exactInterpretation,
|
||||
},
|
||||
commands: {
|
||||
running: taskListCommand(runningOptions),
|
||||
runningFull: taskListCommand({ ...runningOptions, view: "full" }),
|
||||
queued: taskListCommand(queuedOptions),
|
||||
queues: "bun scripts/cli.ts codex queues",
|
||||
nextRunningPage: runningSection.commands.next,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
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>>();
|
||||
@@ -2504,6 +2676,7 @@ function codexTasksOverviewResult(
|
||||
const diagnostics = supervisorExecutionDiagnostics(asRecord(taskPage.queue)?.executionDiagnostics);
|
||||
const activity = compactCodeQueueActivity(asRecord(taskPage.queue) ?? {}, diagnostics);
|
||||
const commanderConcurrency = asRecord(activity.commanderConcurrency) ?? {};
|
||||
const activeRunning = supervisorActiveRunningSummary(taskPage, options, runningSection, diagnostics);
|
||||
const visibleSupervisorItems = [...runningSection.items, ...unreadSection.items, ...recentSection.items, ...queuedSection.items];
|
||||
const classifierCounts = visibleSupervisorItems.reduce((counts, item) => {
|
||||
const key = item.kind;
|
||||
@@ -2517,7 +2690,10 @@ function codexTasksOverviewResult(
|
||||
filters: {
|
||||
view: options.view,
|
||||
queueId: options.queueId ?? null,
|
||||
requestedLimit: options.requestedLimit,
|
||||
limit: options.limit,
|
||||
effectiveLimit: options.limit,
|
||||
limitCapped: options.limit < options.requestedLimit,
|
||||
unreadOnly: options.unreadOnly,
|
||||
status: options.statusFilter,
|
||||
beforeId: options.beforeId ?? null,
|
||||
@@ -2526,6 +2702,7 @@ function codexTasksOverviewResult(
|
||||
endpoint: "/api/tasks/overview",
|
||||
queueId: options.queueId ?? null,
|
||||
limit: asNumber(pagination.limit, 0) || null,
|
||||
limitSemantics: "upstream overview fetch limit; separate from filters.requestedLimit/effectiveLimit and compact section row counts",
|
||||
returned: asNumber(pagination.returned, 0) || null,
|
||||
total: asNumber(pagination.total, 0) || null,
|
||||
hasMore: asBoolean(pagination.hasMore),
|
||||
@@ -2536,9 +2713,13 @@ function codexTasksOverviewResult(
|
||||
disclosure: {
|
||||
defaultView: "supervisor",
|
||||
policy: "bounded summary rows only; prompt/body are short previews and raw detail requires explicit task/full/trace/output commands",
|
||||
limitSemantics: "--limit is request/scanning/page budget; supervisor sections still return compact bounded row pages and expose counts separately",
|
||||
outputBudget: {
|
||||
promptPreviewChars: supervisorPromptPreviewChars,
|
||||
bodyPreviewChars: supervisorBodyPreviewChars,
|
||||
requestedLimit: options.requestedLimit,
|
||||
effectiveLimit: options.limit,
|
||||
limitCapped: options.limit < options.requestedLimit,
|
||||
sectionReturnedLimit: sectionLimit,
|
||||
recentCompletedReturnedLimit: recentLimit,
|
||||
recentCompletedBodyPreviewChars: supervisorRecentBodyPreviewChars,
|
||||
@@ -2551,6 +2732,9 @@ function codexTasksOverviewResult(
|
||||
counts: {
|
||||
scanned: allTasks.length,
|
||||
running: runningSection.count,
|
||||
activeRunningCount: activeRunning.count,
|
||||
activeRunningExact: activeRunning.exact,
|
||||
activeRunningRowsReturned: runningSection.returned,
|
||||
completedUnread: unreadSection.count,
|
||||
recentCompleted: recentSection.count,
|
||||
queued: queuedSection.count,
|
||||
@@ -2563,12 +2747,13 @@ function codexTasksOverviewResult(
|
||||
classifierCounts,
|
||||
commanderConcurrency,
|
||||
activity,
|
||||
activeRunning,
|
||||
executionDiagnostics: diagnostics,
|
||||
degraded,
|
||||
commands: {
|
||||
refresh: taskListCommand(options),
|
||||
unread: taskListCommand({ ...options, unreadOnly: true }),
|
||||
byStatus: `bun scripts/cli.ts codex tasks --status <status>${options.queueId === undefined ? "" : ` --queue ${options.queueId}`}${options.limit === defaultTasksLimit ? "" : ` --limit ${options.limit}`}`,
|
||||
byStatus: `bun scripts/cli.ts codex tasks --status <status>${options.queueId === undefined ? "" : ` --queue ${options.queueId}`}${options.requestedLimit === defaultTasksLimit ? "" : ` --limit ${options.requestedLimit}`}`,
|
||||
full: fullCommand,
|
||||
next: nextCommand,
|
||||
},
|
||||
@@ -2599,7 +2784,10 @@ function codexTasksFullResult(
|
||||
filters: {
|
||||
view: "full",
|
||||
queueId: options.queueId ?? null,
|
||||
requestedLimit: options.requestedLimit,
|
||||
limit: options.limit,
|
||||
effectiveLimit: options.limit,
|
||||
limitCapped: options.limit < options.requestedLimit,
|
||||
unreadOnly: options.unreadOnly,
|
||||
status: options.statusFilter,
|
||||
beforeId: options.beforeId ?? null,
|
||||
|
||||
Reference in New Issue
Block a user