fix: bound code queue cli noise

This commit is contained in:
Codex
2026-05-23 01:56:59 +00:00
parent 774c32515e
commit 7ea2f77b70
9 changed files with 286 additions and 61 deletions
@@ -0,0 +1,155 @@
import { codexOutputQuery, codexTaskQuery } from "./src/code-queue";
type JsonRecord = Record<string, unknown>;
function assertCondition(condition: unknown, message: string, detail: unknown = {}): void {
if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
}
function asRecord(value: unknown): JsonRecord {
assertCondition(value !== null && typeof value === "object" && !Array.isArray(value), "expected object", { value });
return value as JsonRecord;
}
function asArray(value: unknown): unknown[] {
assertCondition(Array.isArray(value), "expected array", { value });
return value as unknown[];
}
function longText(marker: string, repeat = 220): string {
return Array.from({ length: repeat }, (_, index) => `${marker}-${String(index + 1).padStart(3, "0")} ${"abcdefghijklmnopqrstuvwxyz0123456789".repeat(3)}`).join("\n");
}
function detailFixture(path: string): JsonRecord {
assertCondition(path.includes("/summary"), "detail fixture should only fetch summary", { path });
return {
ok: true,
status: 200,
body: {
ok: true,
summary: {
id: "codex_disclosure_fixture",
queueId: "noise",
status: "failed",
providerId: "D601",
model: "gpt-5.5",
cwd: "/workspace",
prompt: longText("prompt-tail-marker"),
basePrompt: longText("base-tail-marker"),
transcriptCount: 300,
outputCount: 180,
eventCount: 40,
lastAssistantMessage: {
at: "2026-05-23T00:00:00.000Z",
seq: 300,
source: "assistant",
text: longText("assistant-tail-marker"),
},
toolSummary: {
count: 12,
returned: 8,
limit: 8,
truncated: true,
items: Array.from({ length: 8 }, (_, index) => ({
seq: index + 1,
at: "2026-05-23T00:00:00.000Z",
kind: "ran",
title: `tool-${index + 1}`,
status: "ok",
commandPreview: longText(`command-tail-marker-${index + 1}`, 20),
outputPreview: longText(`tool-output-tail-marker-${index + 1}`, 20),
rawSeqs: [index + 1],
})),
},
attempts: Array.from({ length: 8 }, (_, index) => ({
index: index + 1,
mode: index === 0 ? "initial" : "retry",
terminalStatus: "failed",
stderrTail: longText(`stderr-tail-marker-${index + 1}`, 20),
finalResponse: longText(`attempt-response-tail-marker-${index + 1}`, 30),
feedbackPromptPreview: longText(`feedback-tail-marker-${index + 1}`, 20),
runnerErrorClassification: { scope: "runner-local", globalBlocker: false },
})),
},
},
};
}
function outputFixture(path: string): JsonRecord {
assertCondition(path.includes("/output"), "output fixture should fetch output", { path });
assertCondition(path.includes("limit=20"), "default output should cap large requested limit to 20", { path });
assertCondition(path.includes("maxTextChars=500"), "default output should cap text preview chars", { path });
const output = Array.from({ length: 20 }, (_, index) => ({
seq: index + 101,
at: "2026-05-23T00:00:00.000Z",
channel: index % 2 === 0 ? "command" : "assistant",
method: "fixture",
text: longText(`raw-output-tail-marker-${index + 1}`, 40),
}));
return {
ok: true,
status: 200,
body: {
ok: true,
taskId: "codex_disclosure_fixture",
queueId: "noise",
status: "running",
updatedAt: "2026-05-23T00:00:00.000Z",
mode: "tail",
limit: 20,
total: 240,
maxSeq: 1200,
afterSeq: 0,
nextAfterSeq: 120,
previousBeforeSeq: 101,
hasMore: true,
hasBefore: true,
output,
},
};
}
export function runCodeQueueCliDisclosureContract(): JsonRecord {
const detail = codexTaskQuery("codex_disclosure_fixture", ["--detail"], detailFixture) as JsonRecord;
const detailJson = JSON.stringify(detail);
const summary = asRecord(detail.summary);
const attempts = asRecord(summary.attempts);
const attemptRecords = asArray(attempts.attemptRecords);
const toolSummary = asRecord(summary.toolSummary);
const toolItems = asArray(toolSummary.items);
assertCondition(attemptRecords.length === 3, "detail should cap attempt records by default", attempts);
assertCondition(attempts.attemptRecordCount === 8 && attempts.attemptRecordsTruncated === true, "detail should expose omitted attempt metadata", attempts);
assertCondition(detailJson.includes("detailOutputPolicy"), "detail should disclose progressive detail policy", summary);
assertCondition(!detailJson.includes("prompt-tail-marker-220"), "detail should not include full prompt tail by default", summary);
assertCondition(!detailJson.includes("assistant-tail-marker-220"), "detail should not include full assistant tail by default", summary);
assertCondition(!detailJson.includes("attempt-response-tail-marker-1-030"), "detail should not include full attempt response by default", attempts);
assertCondition(toolItems.length === 3 && toolSummary.truncated === true, "detail should cap tool summary rows by default", toolSummary);
assertCondition(!detailJson.includes("tool-output-tail-marker-1-020"), "detail should compact tool output previews", toolSummary);
const output = codexOutputQuery("codex_disclosure_fixture", ["--tail", "--limit", "120"], outputFixture) as JsonRecord;
const page = asRecord(output.outputPage);
const outputRows = asArray(page.output).map(asRecord);
const outputJson = JSON.stringify(output);
const disclosure = asRecord(page.disclosure);
assertCondition(page.requestedLimit === 120 && page.limit === 20 && page.returned === 20, "output should cap large requested limit by default", page);
assertCondition(disclosure.limitCapped === true && disclosure.fullText === false, "output should disclose capped default policy", disclosure);
assertCondition(outputRows.every((row) => row.textTruncated === true && Number(row.textChars) > String(row.text).length), "output rows should expose bounded text previews", page);
assertCondition(!outputJson.includes("raw-output-tail-marker-1-040"), "output should not include full raw text tail by default", page);
assertCondition(String(asRecord(page.commands).fullText).includes("--full-text"), "output should provide explicit full-text command", page);
return {
ok: true,
checks: [
"codex task --detail caps attempts and long text by default",
"codex output caps large requested limits by default",
"codex output preserves progressive full-text command",
],
detailChars: detailJson.length,
outputChars: outputJson.length,
};
}
if (import.meta.main) {
process.stdout.write(`${JSON.stringify(runCodeQueueCliDisclosureContract(), null, 2)}\n`);
}
+5
View File
@@ -142,6 +142,10 @@ export function runCodeQueueCliSteerContract(): JsonRecord {
assertCondition(fetchMethod === "POST", "non-dry-run should POST", { fetchMethod });
assertCondition(fetchPrompt === "send this", "non-dry-run should send raw prompt in body", { fetchPrompt });
assertCondition(nestedRecord(success, ["steer"]).accepted === true, "successful steer should report accepted=true", success);
const successJson = JSON.stringify(success);
assertCondition(nestedRecord(success, ["steer"]).promptOmitted === true, "successful steer should mark prompt omitted", success);
assertCondition(!successJson.includes("send this"), "successful steer must not echo prompt text", success);
assertCondition(!successJson.includes("promptPreview"), "successful steer must not include promptPreview", success);
assertReason(codexSteerTaskForTest("direct_task", ["p"], () => ({ ok: false, exitCode: 1, stderrTail: "Cannot connect to the Docker daemon" })), "backend-core-unreachable", null);
assertReason(codexSteerTaskForTest("direct_task", ["p"], () => ({ ok: false, status: 404, body: { ok: false, error: "microservice not found: code-queue" } })), "code-queue-microservice-unregistered", 404);
@@ -164,6 +168,7 @@ export function runCodeQueueCliSteerContract(): JsonRecord {
"dry-run does not call stable proxy helper",
"dry-run prompt preview is bounded",
"non-dry-run uses stable proxy helper",
"successful steer confirms write without echoing prompt",
"steer failure classification is JSON-consumable",
],
};
@@ -203,8 +203,8 @@ export function runCodeQueueSupervisorDisclosureContract(): JsonRecord {
assertCondition(supervisorBody.length < fullBody.length * 0.55, "supervisor output should be materially smaller than full output", { supervisorChars: supervisorBody.length, fullChars: fullBody.length });
assertCondition(supervisorBody.length < 45_000, "supervisor output should remain bounded even with large diagnostics", { supervisorChars: supervisorBody.length });
assertCondition(recentItems.length === 5, "recentCompleted should be capped below --limit by default", { returned: recentItems.length });
assertCondition(asArray(completedUnread.items).length === 5, "completedUnread should be locally paged and kept separate from recentCompleted", completedUnread);
assertCondition(recentItems.length === 3, "recentCompleted should be capped below --limit by default", { returned: recentItems.length });
assertCondition(asArray(completedUnread.items).length === 3, "completedUnread should be locally paged and kept separate from recentCompleted", completedUnread);
assertCondition(recentItems.every((item) => asRecord(item).unreadTerminal === false), "recentCompleted should not duplicate unread terminal tasks", { recentItems });
assertCondition(diagnostics.databaseActiveTaskIds === undefined, "supervisor diagnostics should not expose verbose databaseActiveTaskIds by default", diagnostics);
assertCondition(omittedCounts.databaseActiveTaskIds === 77, "diagnostic omitted counts should preserve full visibility metadata", omittedCounts);
@@ -224,12 +224,12 @@ export function runCodeQueueSupervisorDisclosureContract(): JsonRecord {
assertCondition(asRecord(fullItem.promptPreview).chars !== undefined && fullItem.lastAssistantMessage !== undefined, "full view must retain detailed task row fields", fullItem);
assertCondition(fullTasks.returned === 15, "full view must not inherit supervisor recentCompleted cap", fullTasks);
const budget = asRecord(disclosure.outputBudget);
assertCondition(budget.recentCompletedReturnedLimit === 5 && budget.sectionReturnedLimit === 5, "supervisor must expose output budget metadata", disclosure);
assertCondition(asArray(runningFilteredSection.items).length === 5, "running status filter should be locally paged below --limit", runningFilteredSection);
assertCondition(budget.recentCompletedReturnedLimit === 3 && budget.sectionReturnedLimit === 3, "supervisor must expose output budget metadata", disclosure);
assertCondition(asArray(runningFilteredSection.items).length === 3, "running status filter should be locally paged below --limit", runningFilteredSection);
assertCondition(runningFilteredSection.count === 40 && runningFilteredSection.hasMore === true, "running status filter should preserve count and hasMore", runningFilteredSection);
assertCondition(String(asRecord(runningFilteredSection.commands).next ?? "").includes("--before-id task-running-05"), "running status filter should provide next page command", runningFilteredSection);
assertCondition(String(asRecord(runningFilteredSection.commands).next ?? "").includes("--before-id task-running-03"), "running status filter should provide next page command", runningFilteredSection);
assertCondition(runningFilteredBody.length < 14_000, "running status filter output should remain bounded", { chars: runningFilteredBody.length });
assertCondition(asArray(unreadFilteredSection.items).length <= 5, "unread list should be locally paged below --limit", unreadFilteredSection);
assertCondition(asArray(unreadFilteredSection.items).length <= 3, "unread list should be locally paged below --limit", unreadFilteredSection);
assertCondition(unreadFilteredBody.length < 14_000, "unread output should remain bounded", { chars: unreadFilteredBody.length });
return {
+8
View File
@@ -30,6 +30,8 @@ const syntaxFiles = [
"scripts/host-codex-commander-no-daemon-smoke-contract-test.ts",
"scripts/host-codex-commander-skeleton-contract-test.ts",
"scripts/auth-broker-contract-test.ts",
"scripts/code-queue-cli-disclosure-contract-test.ts",
"scripts/code-queue-cli-steer-test.ts",
"scripts/code-queue-cli-submit-prompt-contract-test.ts",
"scripts/code-queue-supervisor-disclosure-contract-test.ts",
"src/components/frontend/src/index.ts",
@@ -305,6 +307,8 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
fileItem("scripts/code-queue-trace-summary-contract-test.ts"),
fileItem("scripts/code-queue-pr-preflight-contract-test.ts"),
fileItem("scripts/code-queue-runner-skills-contract-test.ts"),
fileItem("scripts/code-queue-cli-disclosure-contract-test.ts"),
fileItem("scripts/code-queue-cli-steer-test.ts"),
fileItem("scripts/code-queue-submit-routing-contract-test.ts"),
fileItem("scripts/code-queue-supervisor-disclosure-contract-test.ts"),
fileItem("scripts/host-codex-commander-skeleton-contract-test.ts"),
@@ -342,6 +346,8 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
items.push(commandItem("code-queue:trace-summary-contract", ["bun", "scripts/code-queue-trace-summary-contract-test.ts"], 30_000));
items.push(commandItem("code-queue:pr-preflight-contract", ["bun", "scripts/code-queue-pr-preflight-contract-test.ts"], 30_000));
items.push(commandItem("code-queue:runner-skills-contract", ["bun", "scripts/code-queue-runner-skills-contract-test.ts"], 30_000));
items.push(commandItem("code-queue:cli-disclosure-contract", ["bun", "scripts/code-queue-cli-disclosure-contract-test.ts"], 30_000));
items.push(commandItem("code-queue:cli-steer-contract", ["bun", "scripts/code-queue-cli-steer-test.ts"], 30_000));
items.push(commandItem("code-queue:submit-prompt-contract", ["bun", "scripts/code-queue-cli-submit-prompt-contract-test.ts"], 30_000));
items.push(commandItem("code-queue:submit-routing-contract", ["bun", "scripts/code-queue-submit-routing-contract-test.ts"], 30_000));
items.push(commandItem("code-queue:supervisor-disclosure-contract", ["bun", "scripts/code-queue-supervisor-disclosure-contract-test.ts"], 30_000));
@@ -369,6 +375,8 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
items.push(skippedItem("code-queue:trace-summary-contract", "Code Queue trace summary contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("code-queue:pr-preflight-contract", "Code Queue PR preflight contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("code-queue:runner-skills-contract", "Code Queue runner skill availability contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("code-queue:cli-disclosure-contract", "Code Queue CLI disclosure/noise contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("code-queue:cli-steer-contract", "Code Queue steer CLI contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("code-queue:submit-prompt-contract", "Code Queue submit prompt contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("code-queue:submit-routing-contract", "Code Queue submit routing contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("code-queue:supervisor-disclosure-contract", "Code Queue supervisor disclosure contract is opt-in with script checks", "--scripts-typecheck or --full"));
+94 -41
View File
@@ -5,22 +5,27 @@ import { coreInternalFetch } from "./microservices";
import { previewJson } from "./preview";
import { codeAgentPortForModel, codeModelPorts as sharedCodeModelPorts, defaultCodeModels as sharedDefaultCodeModels, opencodeModels as sharedOpencodeModels } from "../../src/components/microservices/code-queue/src/code-agent/common";
const defaultToolLimit = 8;
const defaultToolLimit = 3;
const defaultTraceLimit = 80;
const maxTraceLimit = 500;
const defaultOutputLimit = 20;
const defaultTextPreviewChars = 12_000;
const defaultOutputPreviewChars = 500;
const maxOutputPreviewChars = 1200;
const defaultTasksLimit = 20;
const defaultQueuesLimit = 8;
const maxTasksLimit = 100;
const supervisorSectionReturnedLimit = 5;
const supervisorRecentCompletedLimit = 5;
const supervisorPromptPreviewChars = 90;
const supervisorBodyPreviewChars = 90;
const supervisorRecentBodyPreviewChars = 60;
const supervisorSectionReturnedLimit = 3;
const supervisorRecentCompletedLimit = 3;
const supervisorPromptPreviewChars = 70;
const supervisorBodyPreviewChars = 70;
const supervisorRecentBodyPreviewChars = 50;
const diagnosticsIdPreviewLimit = 3;
const diagnosticsReasonPreviewLimit = 2;
const steerPromptPreviewChars = 320;
const detailAttemptReturnedLimit = 3;
const detailInitialPromptPreviewChars = 1200;
const detailBasePromptPreviewChars = 800;
const detailLastAssistantPreviewChars = 1200;
const minimaxSubmitModel = "minimax-m2.7";
const deepseekSubmitModel = "deepseek-chat";
const gptSubmitModel = "gpt-5.5";
@@ -42,6 +47,7 @@ interface CodexTaskOptions {
}
interface CodexOutputOptions {
requestedLimit: number;
limit: number;
mode: "tail" | "after" | "before";
afterSeq: number;
@@ -654,13 +660,13 @@ function compactText(text: unknown, full: boolean, maxChars: number): Record<str
return textView(asString(text), full, maxChars);
}
function compactLastAssistant(value: unknown, full: boolean): Record<string, unknown> {
function compactLastAssistant(value: unknown, full: boolean, maxChars = 4000): Record<string, unknown> {
const record = asRecord(value) ?? {};
return {
at: record.at ?? null,
seq: record.seq ?? null,
source: record.source ?? "none",
...textView(asString(record.text), full, 4000),
...textView(asString(record.text), full, maxChars),
};
}
@@ -1255,9 +1261,11 @@ function supervisorExecutionDiagnostics(value: unknown): Record<string, unknown>
};
}
function compactToolSummary(value: unknown, full: boolean): Record<string, unknown> {
function compactToolSummary(value: unknown, full: boolean, limit = defaultToolLimit): Record<string, unknown> {
const record = asRecord(value) ?? {};
const items = asArray(record.items).map((item) => {
const allItems = asArray(record.items);
const sourceItems = full ? allItems : allItems.slice(0, limit);
const items = sourceItems.map((item) => {
const line = asRecord(item) ?? {};
return {
seq: line.seq ?? null,
@@ -1265,18 +1273,18 @@ function compactToolSummary(value: unknown, full: boolean): Record<string, unkno
kind: line.kind ?? null,
title: line.title ?? "",
status: line.status ?? null,
commandPreview: compactText(line.commandPreview, full, 1200),
commandPreview: compactText(line.commandPreview, full, 360),
commandOmittedLines: line.commandOmittedLines ?? 0,
outputPreview: compactText(line.outputPreview, full, 800),
outputPreview: compactText(line.outputPreview, full, 360),
outputOmittedLines: line.outputOmittedLines ?? 0,
rawSeqs: line.rawSeqs ?? [],
};
});
return {
count: record.count ?? 0,
returned: record.returned ?? items.length,
limit: record.limit ?? items.length,
truncated: record.truncated ?? false,
count: record.count ?? allItems.length,
returned: items.length,
limit,
truncated: record.truncated === true || (!full && allItems.length > items.length),
items,
};
}
@@ -1286,8 +1294,11 @@ function compactSummary(summary: unknown, options: CodexTaskOptions, taskId: str
const transcriptCount = asNumber(record.transcriptCount, 0);
const transcriptMaxSeq = transcriptCount > 0 ? record.transcriptMaxSeq ?? null : null;
const initialPrompt = asString(record.initialPrompt ?? record.prompt);
const initialPromptView = textView(initialPrompt, options.full, 3000);
const basePromptView = textView(asString(record.basePrompt), options.full, 2000);
const initialPromptView = textView(initialPrompt, options.full, detailInitialPromptPreviewChars);
const basePromptView = textView(asString(record.basePrompt), options.full, detailBasePromptPreviewChars);
const attemptRecordsSource = asArray(record.attempts);
const attemptRecords = (options.full ? attemptRecordsSource : attemptRecordsSource.slice(0, detailAttemptReturnedLimit))
.map((attempt) => compactAttemptCycle(attempt, options.full));
return {
id: record.id ?? taskId,
queueId: record.queueId ?? null,
@@ -1304,7 +1315,10 @@ function compactSummary(summary: unknown, options: CodexTaskOptions, taskId: str
currentMode: record.currentMode ?? null,
judgeFailCount: record.judgeFailCount ?? null,
judgeFailRetryLimit: record.judgeFailRetryLimit ?? null,
attemptRecords: asArray(record.attempts).map((attempt) => compactAttemptCycle(attempt, options.full)),
attemptRecordCount: attemptRecordsSource.length,
attemptRecordsReturned: attemptRecords.length,
attemptRecordsTruncated: !options.full && attemptRecordsSource.length > attemptRecords.length,
attemptRecords,
},
thread: {
codexThreadId: record.codexThreadId ?? null,
@@ -1322,10 +1336,10 @@ function compactSummary(summary: unknown, options: CodexTaskOptions, taskId: str
: { initialPromptPreview: initialPromptView, basePromptPreview: basePromptView }),
referenceTaskIds: record.referenceTaskIds ?? [],
referenceInjection: record.referenceInjection ?? null,
lastAssistantMessage: compactLastAssistant(record.lastAssistantMessage, options.full),
lastAssistantMessage: compactLastAssistant(record.lastAssistantMessage, options.full, detailLastAssistantPreviewChars),
lastJudge: record.lastJudge ?? null,
lastError: record.lastError ?? null,
toolSummary: compactToolSummary(record.toolSummary, options.full),
toolSummary: compactToolSummary(record.toolSummary, options.full, options.toolLimit),
counts: {
transcript: record.transcriptCount ?? null,
output: record.outputCount ?? null,
@@ -1336,6 +1350,7 @@ function compactSummary(summary: unknown, options: CodexTaskOptions, taskId: str
renderer: "shared trace-summary/trace-steps progressive abstraction; CLI and WebUI diverge only at final rendering",
total: record.transcriptCount ?? null,
maxSeq: transcriptMaxSeq,
detailOutputPolicy: "bounded detail by default; use --full, --trace, --tool-limit, or codex output for progressive disclosure",
defaultPage: `bun scripts/cli.ts codex task ${taskId} --trace --limit ${defaultTraceLimit}`,
firstPage: `bun scripts/cli.ts codex task ${taskId} --trace --from-start --limit ${defaultTraceLimit}`,
nextPageTemplate: `bun scripts/cli.ts codex task ${taskId} --trace --after-seq <nextAfterSeq> --limit ${defaultTraceLimit}`,
@@ -1449,16 +1464,16 @@ function compactAttemptCycle(value: unknown, full: boolean): Record<string, unkn
appServerExitCode: record.appServerExitCode ?? null,
appServerSignal: record.appServerSignal ?? null,
error: record.error ?? null,
stderrTail: textView(asString(record.stderrTail), full, 1200),
stderrTail: textView(asString(record.stderrTail), full, 600),
startedAt: record.startedAt ?? null,
finishedAt: record.finishedAt ?? null,
startSeq: record.startSeq ?? null,
endSeq: record.endSeq ?? null,
execution: record.execution ?? null,
finalResponse: textView(asString(record.finalResponsePreview ?? record.finalResponse), full, 3000),
finalResponse: textView(asString(record.finalResponsePreview ?? record.finalResponse), full, 1200),
judge: record.judge ?? null,
runnerErrorClassification: record.runnerErrorClassification ?? null,
feedbackPrompt: textView(asString(record.feedbackPromptPreview), full, 1800),
feedbackPrompt: textView(asString(record.feedbackPromptPreview), full, 800),
};
}
@@ -1588,13 +1603,16 @@ function parseOutputOptions(args: string[]): CodexOutputOptions {
const afterSeq = nonNegativeNumberOption(args, ["--after-seq", "--afterSeq"], 0);
const fromStart = hasFlag(args, "--from-start") || hasFlag(args, "--first");
const mode = beforeSeq !== null ? "before" : fromStart || args.some((arg) => arg === "--after-seq" || arg === "--afterSeq") ? "after" : "tail";
const fullText = hasFlag(args, "--full-text") || hasFlag(args, "--raw");
const requestedLimit = positiveIntegerOption(args, ["--limit"], defaultOutputLimit, maxTraceLimit);
return {
limit: positiveIntegerOption(args, ["--limit"], defaultOutputLimit, maxTraceLimit),
requestedLimit,
limit: fullText ? requestedLimit : Math.min(requestedLimit, defaultOutputLimit),
mode,
afterSeq,
beforeSeq,
fullText: hasFlag(args, "--full-text") || hasFlag(args, "--raw"),
maxTextChars: positiveIntegerOption(args, ["--max-text-chars"], defaultTextPreviewChars, 500_000),
fullText,
maxTextChars: positiveIntegerOption(args, ["--max-text-chars"], defaultOutputPreviewChars, fullText ? 500_000 : maxOutputPreviewChars),
};
}
@@ -1706,8 +1724,23 @@ function codexTaskSummary(taskId: string, options: CodexTaskOptions, fetcher: Co
return result;
}
function compactOutputPage(body: Record<string, unknown>, taskId: string, limit: number): Record<string, unknown> {
const output = asArray(body.output);
function compactOutputRecord(item: unknown, options: CodexOutputOptions): Record<string, unknown> {
const record = asRecord(item) ?? {};
const text = textView(asString(record.text), options.fullText, options.maxTextChars);
return {
seq: record.seq ?? null,
at: record.at ?? null,
channel: record.channel ?? null,
method: record.method ?? null,
itemId: record.itemId ?? null,
text: text.text,
textChars: text.chars,
...(text.truncated ? { textTruncated: true, textOmittedChars: text.omittedChars } : {}),
};
}
function compactOutputPage(body: Record<string, unknown>, taskId: string, options: CodexOutputOptions): Record<string, unknown> {
const output = asArray(body.output).map((item) => compactOutputRecord(item, options));
const nextAfterSeq = body.nextAfterSeq ?? null;
const previousBeforeSeq = body.previousBeforeSeq ?? null;
return {
@@ -1716,7 +1749,8 @@ function compactOutputPage(body: Record<string, unknown>, taskId: string, limit:
status: body.status ?? null,
updatedAt: body.updatedAt ?? null,
mode: body.mode ?? null,
limit,
requestedLimit: options.requestedLimit,
limit: options.limit,
returned: output.length,
total: body.total ?? null,
maxSeq: body.maxSeq ?? null,
@@ -1726,13 +1760,21 @@ function compactOutputPage(body: Record<string, unknown>, taskId: string, limit:
previousBeforeSeq,
hasMore: body.hasMore ?? false,
hasBefore: body.hasBefore ?? false,
disclosure: {
defaultPolicy: "bounded output rows and bounded text previews; use --full-text with an explicit seq window only when raw text is required",
limitCapped: !options.fullText && options.limit < options.requestedLimit,
fullText: options.fullText,
textPreviewChars: options.maxTextChars,
requestedLimit: options.requestedLimit,
effectiveLimit: options.limit,
},
output,
commands: {
next: body.hasMore === true && nextAfterSeq !== null ? `bun scripts/cli.ts codex output ${taskId} --after-seq ${nextAfterSeq} --limit ${limit}` : null,
previous: body.hasBefore === true && previousBeforeSeq !== null ? `bun scripts/cli.ts codex output ${taskId} --before-seq ${previousBeforeSeq} --limit ${limit}` : null,
tail: `bun scripts/cli.ts codex output ${taskId} --tail --limit ${limit}`,
first: `bun scripts/cli.ts codex output ${taskId} --from-start --limit ${limit}`,
fullText: `bun scripts/cli.ts codex output ${taskId} --after-seq <rawSeqBefore> --limit ${limit} --full-text`,
next: body.hasMore === true && nextAfterSeq !== null ? `bun scripts/cli.ts codex output ${taskId} --after-seq ${nextAfterSeq} --limit ${options.limit}` : null,
previous: body.hasBefore === true && previousBeforeSeq !== null ? `bun scripts/cli.ts codex output ${taskId} --before-seq ${previousBeforeSeq} --limit ${options.limit}` : null,
tail: `bun scripts/cli.ts codex output ${taskId} --tail --limit ${options.limit}`,
first: `bun scripts/cli.ts codex output ${taskId} --from-start --limit ${options.limit}`,
fullText: `bun scripts/cli.ts codex output ${taskId} --after-seq <rawSeqBefore> --limit ${Math.min(options.requestedLimit, defaultOutputLimit)} --full-text`,
},
};
}
@@ -1747,7 +1789,7 @@ function codexTaskOutput(taskId: string, options: CodexOutputOptions, fetcher: C
if (options.mode === "after") params.afterSeq = options.afterSeq;
if (options.mode === "before") params.beforeSeq = options.beforeSeq;
const response = unwrapCodexResponse(fetcher(codeQueueProxyPath(`/api/tasks/${encodeURIComponent(taskId)}/output${queryString(params)}`)));
return { upstream: response.upstream, outputPage: compactOutputPage(response.body, taskId, options.limit) };
return { upstream: response.upstream, outputPage: compactOutputPage(response.body, taskId, options) };
}
function codexTaskJudge(taskId: string, options: CodexJudgeOptions, fetcher: CodexResponseFetcher): unknown {
@@ -2389,7 +2431,7 @@ async function codexTaskOutputAsync(taskId: string, options: CodexOutputOptions,
if (options.mode === "after") params.afterSeq = options.afterSeq;
if (options.mode === "before") params.beforeSeq = options.beforeSeq;
const response = unwrapCodexResponse(await fetcher(codeQueueProxyPath(`/api/tasks/${encodeURIComponent(taskId)}/output${queryString(params)}`)));
return { upstream: response.upstream, outputPage: compactOutputPage(response.body, taskId, options.limit) };
return { upstream: response.upstream, outputPage: compactOutputPage(response.body, taskId, options) };
}
async function codexTaskJudgeAsync(taskId: string, options: CodexJudgeOptions, fetcher: AsyncCodexResponseFetcher): Promise<unknown> {
@@ -3882,17 +3924,28 @@ function codexSteerTask(taskId: string, args: string[], fetcher: CodexResponseFe
};
}
return {
ok: true,
upstream: response.upstream,
steer: {
accepted: true,
prompt,
taskId,
promptChars: options.prompt.length,
promptOmitted: true,
outputPolicy: {
default: "write-confirmation",
promptEchoed: false,
taskDetailEchoed: false,
reason: "codex steer is a write operation; default output confirms delivery and provides drill-down commands without echoing prompt text or full task state.",
},
},
task: compactTaskMutationResponse(response.body.task),
queue: compactQueueMutationSummary(response.body.queue),
task: compactSubmitTaskConfirmation(response.body.task),
queue: compactSubmitQueueConfirmation(response.body.queue),
commands: {
show: `bun scripts/cli.ts codex task ${taskId}`,
detail: `bun scripts/cli.ts codex task ${taskId} --detail`,
trace: `bun scripts/cli.ts codex task ${taskId} --trace --tail --limit ${defaultTraceLimit}`,
output: `bun scripts/cli.ts codex output ${taskId} --tail --limit ${defaultOutputLimit}`,
supervisor: `bun scripts/cli.ts codex tasks --view supervisor --limit ${defaultTasksLimit}`,
},
};
}
+9 -5
View File
@@ -54,13 +54,13 @@ export function rootHelp(): unknown {
{ command: "codex deploy <commitId> [--provider-id D601] [--timeout-ms N]", description: "Disabled legacy Code Queue deploy path; use the dev-only artifact consumer instead." },
{ command: "codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue queueId] [--provider-id id] [--cwd path] [--model model] [--execution-mode mode] [--max-attempts N] [--reference-task-id id] [--dry-run]", description: "Submit a Code Queue task through backend-core -> code-queue proxy; --dry-run shows the structured request, while real success only confirms the write and task id." },
{ command: "codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N]", description: "Read-only PR admission check against the D601 scheduler/runner token, GitHub egress, repo visibility, optional push dry-run, and PR body/create dry-run guard." },
{ command: "codex task <taskId> [--detail] [--trace --tail|--from-start|--after-seq N|--before-seq N --limit N] [--full]", description: "Fetch the bounded review view by default: original prompt, final response, and drill-down commands; detail and trace are opt-in." },
{ command: "codex tasks [--view supervisor|full] [--queue id] [--status status[,status]] [--unread|--unread-only] [--limit N] [--before-id id]", description: "Show the low-noise supervisor view by default: compact task rows, capped recent completions, diagnostics, and drill-down commands; use --view full for detailed rows." },
{ command: "codex output <taskId> [--tail|--from-start|--after-seq N|--before-seq N --limit N] [--full-text]", description: "Fetch paged raw Code Queue output records by seq when a trace row has omitted command/output text." },
{ command: "codex task <taskId> [--detail] [--trace --tail|--from-start|--after-seq N|--before-seq N --limit N] [--full]", description: "Fetch the bounded review view by default; --detail is still capped, while --full/trace/output explicitly expand evidence." },
{ command: "codex tasks [--view supervisor|full] [--queue id] [--status status[,status]] [--unread|--unread-only] [--limit N] [--before-id id]", description: "Show the low-noise supervisor view by default: compact task rows, tiny local sections, diagnostics, and drill-down commands; use --view full for detailed rows." },
{ command: "codex output <taskId> [--tail|--from-start|--after-seq N|--before-seq N --limit N] [--full-text]", description: "Fetch paged raw Code Queue output records; default caps large limits/text previews, --full-text explicitly expands one seq window." },
{ command: "codex read <taskId>", description: "Mark one reviewed terminal task read; never run automatically as part of listing." },
{ command: "codex dev-ready", description: "Fetch execution-container readiness, including sanitized skill injection status from /api/dev-ready." },
{ command: "codex judge <taskId> --attempt N [--dry-run] [--include-prompt]", description: "Replay one stored Code Queue attempt through the same judge context builder and MiniMax judge call path used by the live queue worker." },
{ command: "codex steer <taskId> [prompt|--prompt-file path|--prompt-stdin] [--dry-run]", description: "Push a bounded corrective prompt into a running Code Queue task through the stable private proxy path." },
{ command: "codex steer <taskId> [prompt|--prompt-file path|--prompt-stdin] [--dry-run]", description: "Push a corrective prompt into a running Code Queue task; real success only confirms the write and does not echo prompt text." },
{ command: "codex interrupt|cancel <taskId>", description: "Request interrupt for a running Code Queue task, or cancel a queued/retry_wait task, through the same private proxy." },
{ command: "codex (queues [--full|--all] | queue create <queueId> | queue merge <sourceQueueId> --into <targetQueueId> | move <taskId> --queue <queueId>)", description: "List low-noise queue summaries by default; full queue rows require --full/--all." },
{ command: "job list [--limit N] [--include-command]", description: "List async jobs from .state/jobs with a bounded default page." },
@@ -278,7 +278,11 @@ function codexHelp(): unknown {
file: "bun scripts/cli.ts codex submit --prompt-file /tmp/code-queue-prompt.md --queue <id> --dry-run",
dryRunThenSubmit: "Run with --dry-run first; remove --dry-run to submit exactly the same payload.",
},
description: "Operate Code Queue through the stable backend-core private proxy path. Real submit success is a low-noise write confirmation and does not echo prompt text.",
disclosure: {
defaultPolicy: "low-noise JSON by default; write commands confirm persistence, list/detail/output commands return bounded summaries with drill-down commands",
expand: ["codex task <taskId> --full", "codex task <taskId> --trace --limit N", "codex output <taskId> --after-seq N --limit N --full-text", "codex tasks --view full --limit N"],
},
description: "Operate Code Queue through the stable backend-core private proxy path. Real submit/steer success is a low-noise write confirmation and does not echo prompt text.",
};
}