fix: expose codex submit execution mode mapping

This commit is contained in:
unidesk-code-queue-runner
2026-05-23 08:27:23 +00:00
parent ac04e61937
commit f878841857
12 changed files with 287 additions and 7 deletions
@@ -0,0 +1,152 @@
import { spawnSync } from "node:child_process";
import {
normalizeCodeExecutionMode,
normalizeRequestedCodeExecutionMode,
requestedCodeExecutionModeIsRecognized,
} from "../src/components/microservices/code-queue/src/code-agent/common";
import { compactSubmitSuccessResponseForTest } 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 runCli(args: string[]): { status: number | null; stdout: string; stderr: string; json: JsonRecord | null } {
const result = spawnSync("bun", ["scripts/cli.ts", ...args], {
cwd: process.cwd(),
encoding: "utf8",
});
const stdout = String(result.stdout || "");
let json: JsonRecord | null = null;
try {
json = JSON.parse(stdout) as JsonRecord;
} catch {
json = null;
}
return {
status: result.status,
stdout,
stderr: String(result.stderr || ""),
json,
};
}
function nestedRecord(value: unknown, path: string[]): JsonRecord {
let current: unknown = value;
for (const key of path) {
assertCondition(current !== null && typeof current === "object" && !Array.isArray(current), "expected object while traversing JSON", { path, key, current });
current = (current as JsonRecord)[key];
}
assertCondition(current !== null && typeof current === "object" && !Array.isArray(current), "expected nested object", { path, current });
return current as JsonRecord;
}
function asArray(value: unknown): unknown[] {
assertCondition(Array.isArray(value), "expected JSON array", { value });
return value as unknown[];
}
function assertSecretFree(output: string): void {
const forbidden = ["GH_TOKEN=", "GITHUB_TOKEN=", "OPENAI_API_KEY=", "CRS_OAI_KEY=", "DEEPSEEK_API_KEY=", "MINIMAX_API_KEY="];
for (const needle of forbidden) {
assertCondition(!output.includes(needle), "submit execution-mode contract must not print credential assignments", { needle });
}
}
export function runCodeQueueSubmitExecutionModeContract(): JsonRecord {
assertCondition(normalizeRequestedCodeExecutionMode("full-access") === "full-access", "shared parser should preserve short requested mode ids");
assertCondition(normalizeCodeExecutionMode("full-access") === "default", "shared execution-mode normalizer should keep full-access on effective default");
assertCondition(requestedCodeExecutionModeIsRecognized("full-access") === false, "shared recognition helper should reject full-access as a runtime mode");
assertCondition(requestedCodeExecutionModeIsRecognized("default") === true, "shared recognition helper should accept default mode");
const defaultMode = runCli(["codex", "submit", "execution mode default smoke", "--dry-run"]);
assertCondition(defaultMode.status === 0 && defaultMode.json?.ok === true, "default submit dry-run should succeed", defaultMode.json ?? { stdout: defaultMode.stdout, stderr: defaultMode.stderr });
assertSecretFree(defaultMode.stdout);
const defaultData = nestedRecord(defaultMode.json?.data, []);
const defaultRequest = nestedRecord(defaultData, ["request"]);
const defaultExecutionMode = nestedRecord(defaultData, ["executionMode"]);
const defaultPermissions = nestedRecord(defaultData, ["runnerPermissions"]);
assertCondition(defaultRequest.executionMode === undefined, "default payload should omit executionMode so service default is authoritative", defaultRequest);
assertCondition(defaultExecutionMode.requested === null, "default mode should show no explicit requested mode", defaultExecutionMode);
assertCondition(defaultExecutionMode.effective === "default", "default mode should expose effective default", defaultExecutionMode);
assertCondition(defaultExecutionMode.normalized === false, "default mode should not be reported as normalized", defaultExecutionMode);
assertCondition(defaultExecutionMode.recognized === true, "default mode should be recognized", defaultExecutionMode);
assertCondition(defaultPermissions.observed === false && defaultPermissions.perTaskOverrideSupported === false, "dry-run should mark runner permissions unobserved and non per-task", defaultPermissions);
const fullAccess = runCli(["codex", "submit", "execution mode full access smoke", "--execution-mode", "full-access", "--dry-run"]);
assertCondition(fullAccess.status === 0 && fullAccess.json?.ok === true, "full-access submit dry-run should succeed", fullAccess.json ?? { stdout: fullAccess.stdout, stderr: fullAccess.stderr });
assertSecretFree(fullAccess.stdout);
const fullData = nestedRecord(fullAccess.json?.data, []);
const fullRequest = nestedRecord(fullData, ["request"]);
const fullExecutionMode = nestedRecord(fullData, ["executionMode"]);
assertCondition(fullRequest.executionMode === "full-access", "payload should preserve the requested executionMode value for backend visibility", fullRequest);
assertCondition(fullExecutionMode.requested === "full-access", "full-access request should be visible", fullExecutionMode);
assertCondition(fullExecutionMode.effective === "default", "full-access should normalize to the effective default runtime mode", fullExecutionMode);
assertCondition(fullExecutionMode.recognized === false, "full-access should not be treated as a recognized Code Queue execution mode", fullExecutionMode);
assertCondition(fullExecutionMode.normalized === true, "full-access should explicitly show normalization", fullExecutionMode);
assertCondition(fullExecutionMode.requestedLooksLikeSandbox === true, "full-access should be classified as a sandbox-like request", fullExecutionMode);
assertCondition(String(fullExecutionMode.permissionBoundary || "").includes("runnerPermissions.sandbox"), "permission boundary should point at runnerPermissions.sandbox", fullExecutionMode);
assertCondition(String(fullExecutionMode.warning || "").includes("not applied"), "full-access warning should say it is not a per-task sandbox override", fullExecutionMode);
const promptText = "submitted full-access prompt body must stay omitted";
const submitted = compactSubmitSuccessResponseForTest({
tasks: [{
id: "codex_exec_mode_contract",
queueId: "commander-efficiency",
status: "queued",
providerId: "D601",
model: "gpt-5.5",
cwd: "/workspace",
prompt: promptText,
executionMode: "default",
requestedExecutionMode: "full-access",
maxAttempts: 99,
createdAt: "2026-05-23T00:00:00.000Z",
updatedAt: "2026-05-23T00:00:00.000Z",
}],
queue: {
total: 1,
queueCount: 1,
counts: { queued: 1 },
queuedTaskIds: ["codex_exec_mode_contract"],
runnerPermissions: {
observed: true,
scope: "code-queue-service-config",
sandbox: "danger-full-access",
approvalPolicy: "never",
perTaskOverrideSupported: false,
secretsPrinted: false,
},
},
}, { ok: true, status: 200 }, { mode: "local-atomic-directory-submit-serialization", acquiredAfterMs: 1, heldMs: 2, throttleMs: 2000 });
const submittedExecutionMode = nestedRecord(submitted, ["executionMode"]);
const submittedPermissions = nestedRecord(submitted, ["runnerPermissions"]);
const firstTask = nestedRecord(asArray(nestedRecord(submitted, ["submitted"]).tasks)[0], []);
const taskExecutionMode = nestedRecord(firstTask, ["executionModeRequest"]);
const queuePermissions = nestedRecord(submitted, ["queue", "runnerPermissions"]);
const submittedJson = JSON.stringify(submitted);
assertCondition(submittedExecutionMode.requested === "full-access" && submittedExecutionMode.effective === "default", "real submit summary should show requested/effective mode", submittedExecutionMode);
assertCondition(submittedPermissions.observed === true && submittedPermissions.sandbox === "danger-full-access" && submittedPermissions.approvalPolicy === "never", "real submit summary should expose observed service-level runner permissions", submittedPermissions);
assertCondition(submittedPermissions.perTaskOverrideSupported === false, "real submit summary should not imply per-task sandbox override", submittedPermissions);
assertCondition(firstTask.requestedExecutionMode === "full-access" && firstTask.executionMode === "default", "submitted task should carry requested and effective mode", firstTask);
assertCondition(taskExecutionMode.warning === submittedExecutionMode.warning, "task-level execution mode summary should match top-level warning", { taskExecutionMode, submittedExecutionMode });
assertCondition(queuePermissions.sandbox === "danger-full-access", "queue summary should keep runner permissions visible", queuePermissions);
assertCondition(!submittedJson.includes(promptText), "real submit summary must keep prompt text omitted", submitted);
assertCondition(!submittedJson.includes("promptPreview"), "real submit summary must not reintroduce promptPreview", submitted);
return {
ok: true,
checks: [
"default codex submit dry-run omits executionMode, reports effective default, and marks runner permissions unobserved",
"--execution-mode full-access preserves requested mode, reports effective default, and warns that sandbox permissions are service-level",
"real submit summary fixture exposes requested/effective mode plus observed runnerPermissions without prompt echo",
"shared execution-mode helpers preserve requested full-access while normalizing effective runtime to default",
"execution-mode dry-run output does not print credential assignments",
],
};
}
if (import.meta.main) {
process.stdout.write(`${JSON.stringify(runCodeQueueSubmitExecutionModeContract(), null, 2)}\n`);
}
+3
View File
@@ -34,6 +34,7 @@ const syntaxFiles = [
"scripts/code-queue-prompt-lint-contract-test.ts",
"scripts/code-queue-cli-steer-test.ts",
"scripts/code-queue-cli-submit-prompt-contract-test.ts",
"scripts/code-queue-submit-execution-mode-contract-test.ts",
"scripts/code-queue-submit-summary-contract-test.ts",
"scripts/code-queue-cli-read-terminal-contract-test.ts",
"scripts/code-queue-gh-auth-redaction-contract-test.ts",
@@ -362,6 +363,7 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
items.push(commandItem("code-queue:cli-steer-contract", ["bun", "scripts/code-queue-cli-steer-test.ts"], 30_000));
items.push(commandItem("code-queue:read-terminal-contract", ["bun", "scripts/code-queue-cli-read-terminal-contract-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-execution-mode-contract", ["bun", "scripts/code-queue-submit-execution-mode-contract-test.ts"], 30_000));
items.push(commandItem("code-queue:submit-summary-contract", ["bun", "scripts/code-queue-submit-summary-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:gh-auth-redaction-contract", ["bun", "scripts/code-queue-gh-auth-redaction-contract-test.ts"], 30_000));
@@ -396,6 +398,7 @@ export function runChecks(config: UniDeskConfig, options: CheckOptions = default
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:read-terminal-contract", "Code Queue terminal read 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-execution-mode-contract", "Code Queue submit execution-mode contract is opt-in with script checks", "--scripts-typecheck or --full"));
items.push(skippedItem("code-queue:submit-summary-contract", "Code Queue submit summary 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:gh-auth-redaction-contract", "Code Queue GitHub auth output redaction contract is opt-in with script checks", "--scripts-typecheck or --full"));
+84 -2
View File
@@ -3,7 +3,16 @@ import { runCommand } from "./command";
import { type UniDeskConfig, repoRoot, rootPath } from "./config";
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";
import {
codeAgentPortForModel,
codeExecutionModes,
codeModelPorts as sharedCodeModelPorts,
defaultCodeModels as sharedDefaultCodeModels,
normalizeCodeExecutionMode,
normalizeRequestedCodeExecutionMode,
opencodeModels as sharedOpencodeModels,
requestedCodeExecutionModeIsRecognized,
} from "../../src/components/microservices/code-queue/src/code-agent/common";
const defaultToolLimit = 3;
const defaultTraceLimit = 80;
@@ -25,6 +34,7 @@ const diagnosticsIdPreviewLimit = 3;
const diagnosticsReasonPreviewLimit = 2;
const mutationQueueIdPreviewLimit = 15;
const steerPromptPreviewChars = 320;
const sandboxLikeExecutionModes = new Set(["full-access", "danger-full-access", "workspace-write", "read-only"]);
const detailAttemptReturnedLimit = 3;
const detailInitialPromptPreviewChars = 1200;
const detailBasePromptPreviewChars = 800;
@@ -3921,6 +3931,61 @@ function referenceTaskIdsFromOptions(args: string[]): string[] {
return ids;
}
function parseRequestedExecutionMode(value: string | undefined): string | undefined {
if (value === undefined) return undefined;
const requested = normalizeRequestedCodeExecutionMode(value);
if (requested === null) throw new Error("--execution-mode must be a short mode identifier");
return requested;
}
function executionModeSummary(requestedValue: string | null | undefined, effectiveValue?: unknown): Record<string, unknown> {
const requested = normalizeRequestedCodeExecutionMode(requestedValue);
const effective = typeof effectiveValue === "string" && effectiveValue.trim().length > 0
? normalizeCodeExecutionMode(effectiveValue)
: normalizeCodeExecutionMode(requested);
const requestedLooksLikeSandbox = requested !== null && sandboxLikeExecutionModes.has(requested);
const recognized = requestedCodeExecutionModeIsRecognized(requested);
return {
requested,
effective,
recognized,
normalized: requested !== null && requested !== effective,
availableModes: codeExecutionModes,
requestedLooksLikeSandbox,
permissionBoundary: "--execution-mode selects the Code Queue runtime mode; Codex sandbox permissions come from runnerPermissions.sandbox.",
...(requestedLooksLikeSandbox ? { warning: `${requested} is not a Code Queue execution mode and is not applied as a per-task sandbox override.` } : {}),
};
}
function dryRunRunnerPermissionsSummary(): Record<string, unknown> {
return {
observed: false,
scope: "code-queue-service-config",
sandbox: null,
approvalPolicy: null,
perTaskOverrideSupported: false,
note: "Dry-run does not contact Code Queue; real submit responses include observed runnerPermissions from the service.",
secretsPrinted: false,
};
}
function compactRunnerPermissions(value: unknown): Record<string, unknown> | null {
const record = asRecord(value);
if (record === null) return null;
return {
observed: record.observed ?? true,
scope: record.scope ?? "code-queue-service-config",
sandbox: record.sandbox ?? null,
approvalPolicy: record.approvalPolicy ?? null,
perTaskOverrideSupported: record.perTaskOverrideSupported ?? false,
secretsPrinted: false,
};
}
function compactTaskExecutionModeRequest(record: Record<string, unknown>): Record<string, unknown> {
return executionModeSummary(asString(record.requestedExecutionMode) || null, record.executionMode);
}
function parseSubmitOptions(args: string[]): CodexSubmitOptions {
assertKnownOptions(args, {
flags: ["--prompt-stdin", "--stdin", "--dry-run"],
@@ -3953,7 +4018,7 @@ function parseSubmitOptions(args: string[]): CodexSubmitOptions {
cwd: optionValue(args, ["--cwd", "--workdir"]),
model: optionValue(args, ["--model"]),
reasoningEffort: optionValue(args, ["--reasoning-effort"]),
executionMode: optionValue(args, ["--execution-mode", "--mode"]),
executionMode: parseRequestedExecutionMode(optionValue(args, ["--execution-mode", "--mode"])),
maxAttempts,
referenceTaskIds: referenceTaskIdsFromOptions(args),
dryRun: hasFlag(args, "--dry-run"),
@@ -4014,6 +4079,8 @@ function compactTaskMutationResponse(task: unknown, options: CompactTaskMutation
reasoningEffort: record.reasoningEffort ?? null,
cwd: record.cwd ?? null,
executionMode: record.executionMode ?? null,
requestedExecutionMode: record.requestedExecutionMode ?? null,
executionModeRequest: compactTaskExecutionModeRequest(record),
maxAttempts: record.maxAttempts ?? null,
currentAttempt: record.currentAttempt ?? null,
cancelRequested: record.cancelRequested ?? null,
@@ -4048,6 +4115,8 @@ function compactSubmitTaskConfirmation(task: unknown): Record<string, unknown> {
reasoningEffort: record.reasoningEffort ?? null,
cwd: record.cwd ?? null,
executionMode: record.executionMode ?? null,
requestedExecutionMode: record.requestedExecutionMode ?? null,
executionModeRequest: compactTaskExecutionModeRequest(record),
maxAttempts: record.maxAttempts ?? null,
createdAt: record.createdAt ?? null,
updatedAt: record.updatedAt ?? null,
@@ -4220,6 +4289,7 @@ function compactSubmitQueueConfirmation(value: unknown, options: CompactSubmitQu
databaseActiveTaskIds: databaseActivePreview,
queuedTaskIds: queuedPreview,
executionDiagnostics: compactQueueExecutionDiagnostics(record.executionDiagnostics),
runnerPermissions: compactRunnerPermissions(record.runnerPermissions),
...(submittedTasks.length === 0 ? {} : { submittedTaskIds: submittedPreview }),
countContext: {
queued: countForStatus(counts, "queued"),
@@ -4265,9 +4335,14 @@ function compactSubmitSuccessResponse(body: Record<string, unknown>, upstream: R
const queueIds = Array.from(new Set(tasks.map((task) => asString(task.queueId)).filter(Boolean))).sort();
const firstTaskId = taskIds[0] ?? null;
const firstQueueId = queueIds[0] ?? null;
const firstSubmittedTask = submittedTasks[0] ?? {};
const queueRecord = asRecord(body.queue);
const runnerPermissions = compactRunnerPermissions(queueRecord?.runnerPermissions);
return {
ok: true,
upstream,
executionMode: executionModeSummary(asString(firstSubmittedTask.requestedExecutionMode) || null, firstSubmittedTask.executionMode),
runnerPermissions,
submitted: {
accepted: true,
taskCount: allTasks.length,
@@ -4393,6 +4468,11 @@ function compactTerminalReadTask(summary: unknown, readTask: unknown, taskId: st
readAt,
providerId: summaryRecord.providerId ?? readRecord.providerId ?? null,
executionMode: summaryRecord.executionMode ?? readRecord.executionMode ?? null,
requestedExecutionMode: summaryRecord.requestedExecutionMode ?? readRecord.requestedExecutionMode ?? null,
executionModeRequest: compactTaskExecutionModeRequest({
requestedExecutionMode: summaryRecord.requestedExecutionMode ?? readRecord.requestedExecutionMode ?? null,
executionMode: summaryRecord.executionMode ?? readRecord.executionMode ?? null,
}),
executionModeInfo: summaryRecord.executionModeInfo ?? readRecord.executionModeInfo ?? null,
model: summaryRecord.model ?? readRecord.model ?? null,
agentPort: summaryRecord.agentPort ?? readRecord.agentPort ?? null,
@@ -5848,6 +5928,8 @@ function codexSubmitTask(args: string[]): unknown {
ok: true,
dryRun: true,
promptLint,
executionMode: executionModeSummary(options.executionMode),
runnerPermissions: dryRunRunnerPermissionsSummary(),
routingRecommendation: submitRoutingRecommendation(options),
modelRegistry: submitModelRegistry(),
request: {
+5
View File
@@ -276,6 +276,11 @@ function codexHelp(): unknown {
positional: "Keep positional prompt usage to short one-line smoke prompts only.",
sourceRule: "Exactly one prompt source is accepted: positional prompt, --prompt-file, or --prompt-stdin.",
},
executionMode: {
validModes: ["default", "windows-native"],
boundary: "--execution-mode selects Code Queue runtime placement, not Codex sandbox permissions.",
permissionVisibility: "Submit dry-runs show requested/effective mode; real submit responses include service runnerPermissions.sandbox and approvalPolicy.",
},
readOutput: {
default: "codex read marks a terminal task read and returns terminal metadata, final response, last error/judge, counts, and drill-down commands.",
disclosure: "Full prompt, tool logs, and feedback prompts are not printed by codex read; use codex task/detail/trace/output for progressive disclosure.",