feat: move code queue to d601

This commit is contained in:
Codex
2026-05-14 19:13:43 +00:00
parent cbbed004a6
commit c930607316
28 changed files with 359 additions and 242 deletions
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -42,18 +42,18 @@ const ROUTE_REGISTRY = createRouteRegistry(MODULES);
const fastCodeQueueService = {
id: "code-queue",
name: "Code Queue",
providerId: "main-server",
providerId: "D601",
description: "Code Queue",
repository: { containerName: "code-queue-backend" },
backend: {
nodeBaseUrl: "http://code-queue:4222",
nodeBindHost: "code-queue",
nodeBaseUrl: "http://host.docker.internal:4222",
nodeBindHost: "127.0.0.1",
nodePort: 4222,
public: false,
},
runtime: {
providerStatus: "loading",
providerName: "main-server",
providerName: "D601",
},
};
@@ -29,18 +29,18 @@ const apiBaseUrl = String(clientConfig.apiBaseUrl || "/api");
const standaloneCodeQueueService = {
id: "code-queue",
name: "Code Queue",
providerId: "main-server",
providerId: "D601",
description: "Code Queue 独立入口,使用 summary 首屏和按需全量加载保持信息完整。",
repository: { containerName: "code-queue-backend" },
backend: {
nodeBaseUrl: "http://code-queue:4222",
nodeBindHost: "code-queue",
nodeBaseUrl: "http://host.docker.internal:4222",
nodeBindHost: "127.0.0.1",
nodePort: 4222,
public: false,
},
runtime: {
providerStatus: "online",
providerName: "main-server",
providerName: "D601",
},
};
+15 -13
View File
@@ -1253,11 +1253,11 @@ function codexProviderOptions(queue: any, currentProviderId: string): any[] {
kind: String(item?.kind || "").trim(),
}))
.filter((item: any) => item.id.length > 0);
const fallbackMain = String(queue?.mainProviderId || queue?.defaultProviderId || "main-server").trim() || "main-server";
const fallbackMain = String(queue?.mainProviderId || queue?.defaultProviderId || "D601").trim() || "D601";
const byId = new Map<string, any>();
for (const item of [
...rows,
{ id: fallbackMain, label: `${fallbackMain} (master)`, defaultWorkdir: String(queue?.defaultWorkdir || "/root/unidesk"), supportsWindowsNativeCodex: false, windowsNativeDefaultWorkdir: "", kind: "local" },
{ id: fallbackMain, label: `${fallbackMain} (local)`, defaultWorkdir: String(queue?.defaultWorkdir || "/workspace"), supportsWindowsNativeCodex: false, windowsNativeDefaultWorkdir: "", kind: "local" },
currentProviderId ? { id: currentProviderId, label: currentProviderId, defaultWorkdir: providerDefaultWorkdir(queue, currentProviderId), supportsWindowsNativeCodex: currentProviderId !== fallbackMain, windowsNativeDefaultWorkdir: String(queue?.windowsNativeCodexDefaultWorkdir || "/mnt/f/Work/ConStart"), kind: "" } : null,
].filter(Boolean) as any[]) {
if (!byId.has(item.id)) byId.set(item.id, item);
@@ -1278,7 +1278,7 @@ function codexExecutionModeOptions(queue: any, currentMode: string): any[] {
}))
.filter((item: any) => item.id.length > 0);
const fallback = [
{ id: "default", label: "默认容器/本机", description: "主 server 用本机 Codex;远程 Provider 用执行容器 Codex。", defaultWorkdir: "", requiresProvider: false, requiresWindowsCwd: false },
{ id: "default", label: "默认容器/本机", description: "Code Queue 所在节点用本机 Codex;远程 Provider 用执行容器 Codex。", defaultWorkdir: "", requiresProvider: false, requiresWindowsCwd: false },
{ id: "windows-native", label: "Windows 原生 Codex", description: "启动执行容器,但容器只做 stdio relayCodex 运行在 Provider 的 Windows 宿主。", defaultWorkdir: String(queue?.windowsNativeCodexDefaultWorkdir || "/mnt/f/Work/ConStart"), requiresProvider: true, requiresWindowsCwd: true },
];
const byId = new Map<string, any>();
@@ -1301,8 +1301,8 @@ function providerDefaultWorkdir(queue: any, providerId: string): string {
if (typeof map[id] === "string" && String(map[id]).trim().length > 0) return String(map[id]).trim();
const option = Array.isArray(queue?.executionProviders) ? queue.executionProviders.find((item: any) => String(item?.id || "") === id) : null;
if (typeof option?.defaultWorkdir === "string" && option.defaultWorkdir.trim().length > 0) return option.defaultWorkdir.trim();
const mainProvider = String(queue?.mainProviderId || queue?.defaultProviderId || "main-server");
return id === mainProvider ? String(queue?.defaultWorkdir || "/root/unidesk") : String(queue?.remoteDefaultWorkdir || "/home/ubuntu");
const mainProvider = String(queue?.mainProviderId || queue?.defaultProviderId || "D601");
return id === mainProvider ? String(queue?.defaultWorkdir || "/workspace") : String(queue?.remoteDefaultWorkdir || "/home/ubuntu");
}
function taskStepCount(task: any): number | null {
@@ -1375,7 +1375,7 @@ function TaskCard({ task, selected, onSelect, onCopy, onReference, onMarkRead, c
h("strong", null, shortText(taskDisplayPrompt(task), 120) || "空任务"),
h("div", { className: "codex-task-meta" },
h("span", null, `queue=${taskQueueLabel(task)}`),
h("span", null, `provider=${task?.providerId || "main-server"}`),
h("span", null, `provider=${task?.providerId || "D601"}`),
h("span", null, `mode=${task?.executionMode || "default"}`),
h("span", null, task?.model || "--"),
h("span", null, taskDurationLabel(task)),
@@ -1952,16 +1952,18 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi
const [searchTasksData, setSearchTasksData] = useState(null);
const [searchLoading, setSearchLoading] = useState(false);
const [searchLoadingMoreTasks, setSearchLoadingMoreTasks] = useState(false);
const initialQueueConfig = initialTasksData?.queue || {};
const initialProviderId = String(initialQueueConfig?.mainProviderId || initialQueueConfig?.defaultProviderId || "D601").trim() || "D601";
const [prompt, setPrompt] = useState("");
const [referenceTaskId, setReferenceTaskId] = useState("");
const [queueId, setQueueId] = useState("default");
const [selectedQueueId, setSelectedQueueId] = useState(allQueuesId);
const [mergeDialogOpen, setMergeDialogOpen] = useState(false);
const [mergeSourceQueueId, setMergeSourceQueueId] = useState("");
const [providerId, setProviderId] = useState("main-server");
const [providerId, setProviderId] = useState(initialProviderId);
const [executionMode, setExecutionMode] = useState("default");
const [model, setModel] = useState("gpt-5.5");
const [cwd, setCwd] = useState("/root/unidesk");
const [cwd, setCwd] = useState(executionModeDefaultWorkdir(initialQueueConfig, "default", initialProviderId) || "/workspace");
const [maxAttempts, setMaxAttempts] = useState(99);
const [repeatCount, setRepeatCount] = useState(1);
const [batchConfirmed, setBatchConfirmed] = useState(false);
@@ -2108,7 +2110,7 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi
}
function changeSubmitProvider(nextProviderId: string): void {
const next = String(nextProviderId || queue?.mainProviderId || "main-server").trim() || "main-server";
const next = String(nextProviderId || queue?.mainProviderId || "D601").trim() || "D601";
setProviderId(next);
setCwd(executionModeDefaultWorkdir(queue, executionMode, next));
}
@@ -3383,7 +3385,7 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi
h("span", { className: "codex-trace-status-chip queued" }, h("b", null, "排队"), String(overallQueuedCount)),
h("span", { className: "codex-trace-status-chip running" }, h("b", null, "运行"), String(overallRunningCount)),
h("span", { className: `codex-trace-status-chip unread ${overallUnreadTerminalCount > 0 ? "warn" : ""}` }, h("b", null, "结束未读"), String(overallUnreadTerminalCount)),
h("span", { className: "codex-trace-status-chip service" }, h("b", null, "服务"), `${runtime.providerStatus || "unknown"} · ${service?.providerId || "main-server"} · ${backend.public ? "公网暴露" : "仅 UniDesk frontend 代理访问"}`),
h("span", { className: "codex-trace-status-chip service" }, h("b", null, "服务"), `${runtime.providerStatus || "unknown"} · ${service?.providerId || "D601"} · ${backend.public ? "公网暴露" : "仅 UniDesk frontend 代理访问"}`),
h("span", { className: "codex-trace-status-chip" }, h("b", null, "执行节点"), providerOptions.map((provider: any) => provider.id).join(" / ")),
h("span", { className: "codex-trace-status-chip" }, h("b", null, "执行模式"), executionModeRows.map((mode: any) => mode.id).join(" / ")),
h("span", { className: "codex-trace-status-chip" }, h("b", null, "模型"), codexModels.join(" / ")),
@@ -3393,7 +3395,7 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi
const sessionPanel = h(Panel, {
title: selectedTask ? `Trace ${String(selectedTask.id).slice(0, 22)}` : "Trace 输出",
eyebrow: selectedTask ? `${selectedTask.status} / view=${selectedQueueName} / task queue=${taskQueueLabel(selectedTask)} / provider=${selectedTask.providerId || "main-server"} / mode=${selectedTask.executionMode || "default"} / ${selectedTask.model} / agent loop trace` : `Agent loop trace / view=${selectedQueueName}`,
eyebrow: selectedTask ? `${selectedTask.status} / view=${selectedQueueName} / task queue=${taskQueueLabel(selectedTask)} / provider=${selectedTask.providerId || "D601"} / mode=${selectedTask.executionMode || "default"} / ${selectedTask.model} / agent loop trace` : `Agent loop trace / view=${selectedQueueName}`,
summary: traceStatusSummary,
loading: selectedDetailLoading || loadingMoreTasks || searchLoading || searchLoadingMoreTasks || loadStats?.phase === "loading",
actions: h("div", { className: "panel-actions" },
@@ -3520,7 +3522,7 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi
),
),
h("label", null, "执行 Provider",
h("select", { value: providerId, disabled: submitting, onChange: (event: any) => changeSubmitProvider(String(event.target.value || "main-server")), "data-testid": "codex-provider-select" },
h("select", { value: providerId, disabled: submitting, onChange: (event: any) => changeSubmitProvider(String(event.target.value || "D601")), "data-testid": "codex-provider-select" },
providerOptions.map((provider: any) => h("option", { key: provider.id, value: provider.id }, `${provider.label || provider.id} · ${provider.defaultWorkdir || providerDefaultWorkdir(queue, provider.id)}${provider.supportsWindowsNativeCodex ? " · Windows native" : ""}`)),
),
),
@@ -3529,7 +3531,7 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi
executionModeRows.map((mode: any) => h("option", { key: mode.id, value: mode.id }, `${mode.label || mode.id}${mode.id === "windows-native" ? " · 宿主 Codex" : ""}`)),
),
),
h("label", null, "工作目录", h("input", { value: cwd, disabled: submitting, onChange: (event: any) => setCwd(event.target.value), placeholder: currentProviderDefaultWorkdir || queue?.defaultWorkdir || "/root/unidesk", "data-testid": "codex-cwd-input" })),
h("label", null, "工作目录", h("input", { value: cwd, disabled: submitting, onChange: (event: any) => setCwd(event.target.value), placeholder: currentProviderDefaultWorkdir || queue?.defaultWorkdir || "/workspace", "data-testid": "codex-cwd-input" })),
h("label", null, "最大尝试", h("input", { type: "number", min: 1, max: 99, value: maxAttempts, disabled: submitting, onChange: (event: any) => setMaxAttempts(Number(event.target.value)), "data-testid": "codex-max-attempts-input" })),
h("label", null, "入队份数", h("input", { type: "number", min: 1, max: 50, value: repeatCount, disabled: submitting, onChange: (event: any) => setRepeatCount(Number(event.target.value)), "data-testid": "codex-repeat-count-input" })),
),
+3 -2
View File
@@ -99,7 +99,8 @@ async function refreshCodeQueueOverview(pathWithQuery: string, timeoutMs = 800):
async function refreshCodeQueueOverviewUncached(pathWithQuery: string, timeoutMs = 800): Promise<JsonValue | null> {
const started = performance.now();
try {
const response = await fetch(`http://code-queue:4222${pathWithQuery}`, {
const upstreamUrl = new URL(`/api/microservices/code-queue/proxy${pathWithQuery}`, config.coreInternalUrl);
const response = await fetch(upstreamUrl, {
signal: AbortSignal.timeout(timeoutMs),
headers: { accept: "application/json" },
});
@@ -718,7 +719,7 @@ async function proxyCodeQueueDirect(req: Request, url: URL): Promise<Response> {
return new Response(cached.text, { headers: { "content-type": "application/json; charset=utf-8" } });
}
}
const upstreamUrl = new URL(`${suffix}${url.search}`, "http://code-queue:4222");
const upstreamUrl = new URL(`/api/microservices/code-queue/proxy${suffix}${url.search}`, config.coreInternalUrl);
const headers = new Headers(req.headers);
headers.delete("host");
headers.delete("connection");
@@ -0,0 +1,72 @@
services:
code-queue:
image: unidesk-code-queue:d601
build:
context: ../../../..
dockerfile: src/components/microservices/code-queue/Dockerfile
container_name: code-queue-backend
restart: unless-stopped
mem_limit: "${CODE_QUEUE_MEM_LIMIT:-3g}"
memswap_limit: "${CODE_QUEUE_MEMSWAP_LIMIT:-4g}"
env_file:
- path: ${CODE_QUEUE_ENV_FILE:-../../../../.state/code-queue-d601.env}
required: false
ports:
- "127.0.0.1:${CODE_QUEUE_HOST_PORT:-4222}:4222"
environment:
HOST: "0.0.0.0"
PORT: "4222"
CODE_QUEUE_DATA_DIR: "/var/lib/unidesk/code-queue"
CODE_QUEUE_WORKDIR: "${CODE_QUEUE_WORKDIR:-/workspace}"
CODE_QUEUE_CODEX_HOME: "/var/lib/unidesk/code-queue/codex-home"
CODE_QUEUE_OPENCODE_XDG_DIR: "/var/lib/unidesk/code-queue/opencode-xdg"
CODE_QUEUE_SOURCE_CODEX_CONFIG: "/root/.codex/config.toml"
CODE_QUEUE_DEFAULT_MODEL: "${CODE_QUEUE_DEFAULT_MODEL:-gpt-5.5}"
CODE_QUEUE_MODELS: "${CODE_QUEUE_MODELS:-gpt-5.5,gpt-5.4-mini,gpt-5.4,minimax-m2.7}"
CODE_QUEUE_MODEL_REASONING_EFFORTS: "${CODE_QUEUE_MODEL_REASONING_EFFORTS:-gpt-5.5=xhigh}"
CODE_QUEUE_SANDBOX: "${CODE_QUEUE_SANDBOX:-danger-full-access}"
CODE_QUEUE_APPROVAL_POLICY: "${CODE_QUEUE_APPROVAL_POLICY:-never}"
CODE_QUEUE_MAX_ATTEMPTS: "${CODE_QUEUE_MAX_ATTEMPTS:-99}"
CODE_QUEUE_MAX_ACTIVE_QUEUES: "${CODE_QUEUE_MAX_ACTIVE_QUEUES:-0}"
NODE_OPTIONS: "${CODE_QUEUE_NODE_OPTIONS:---max-old-space-size=1024}"
CODE_QUEUE_IN_MEMORY_OUTPUT_RECORDS: "${CODE_QUEUE_IN_MEMORY_OUTPUT_RECORDS:-10}"
CODE_QUEUE_IN_MEMORY_EVENT_RECORDS: "${CODE_QUEUE_IN_MEMORY_EVENT_RECORDS:-10}"
CODE_QUEUE_CODEX_SQLITE_LOG_EXPORT_BATCH_SIZE: "${CODE_QUEUE_CODEX_SQLITE_LOG_EXPORT_BATCH_SIZE:-500}"
CODE_QUEUE_MAIN_PROVIDER_ID: "${CODE_QUEUE_MAIN_PROVIDER_ID:-D601}"
CODE_QUEUE_REMOTE_WORKDIR: "${CODE_QUEUE_REMOTE_WORKDIR:-/home/ubuntu}"
CODE_QUEUE_EXECUTION_PROVIDER_IDS: "${CODE_QUEUE_EXECUTION_PROVIDER_IDS:-D601}"
CODE_QUEUE_DEV_CONTAINER_MASTER_HOST: "${CODE_QUEUE_DEV_CONTAINER_MASTER_HOST:-74.48.78.17}"
CODE_QUEUE_DEV_CONTAINER_DEFAULT_PROVIDER_ID: "${CODE_QUEUE_DEV_CONTAINER_DEFAULT_PROVIDER_ID:-D601}"
CODE_QUEUE_DEV_CONTAINER_IMAGE: "${CODE_QUEUE_DEV_CONTAINER_IMAGE:-}"
CODE_QUEUE_DEV_CONTAINER_WORKDIR: "${CODE_QUEUE_DEV_CONTAINER_WORKDIR:-/home/ubuntu}"
CODE_QUEUE_WINDOWS_NATIVE_CODEX_DEFAULT_WORKDIR: "${CODE_QUEUE_WINDOWS_NATIVE_CODEX_DEFAULT_WORKDIR:-/mnt/f/Work/ConStart}"
CODE_QUEUE_WINDOWS_NATIVE_CODEX_BRIDGE_DIR: "${CODE_QUEUE_WINDOWS_NATIVE_CODEX_BRIDGE_DIR:-/home/ubuntu/.unidesk/code-queue/windows-native-codex}"
CODE_QUEUE_WINDOWS_NATIVE_CODEX_COMMAND: "${CODE_QUEUE_WINDOWS_NATIVE_CODEX_COMMAND:-codex app-server --listen stdio://}"
CODE_QUEUE_WINDOWS_NATIVE_CODEX_CONNECT_HOST: "${CODE_QUEUE_WINDOWS_NATIVE_CODEX_CONNECT_HOST:-host.docker.internal}"
CODE_QUEUE_WINDOWS_NATIVE_CODEX_IDLE_TIMEOUT_MS: "${CODE_QUEUE_WINDOWS_NATIVE_CODEX_IDLE_TIMEOUT_MS:-600000}"
OA_EVENT_FLOW_BASE_URL: "${OA_EVENT_FLOW_BASE_URL:-http://74.48.78.17:4255}"
CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED: "${CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED:-true}"
CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL: "${CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL:-http://host.docker.internal:3290}"
CODE_QUEUE_NOTIFY_CLAUDEQQ_TARGET_TYPE: "${CODE_QUEUE_NOTIFY_CLAUDEQQ_TARGET_TYPE:-private}"
CODE_QUEUE_NOTIFY_CLAUDEQQ_USER_ID: "${CODE_QUEUE_NOTIFY_CLAUDEQQ_USER_ID:-645275593}"
CODE_QUEUE_NOTIFY_CLAUDEQQ_GROUP_ID: "${CODE_QUEUE_NOTIFY_CLAUDEQQ_GROUP_ID:-}"
CODE_QUEUE_NOTIFY_CLAUDEQQ_MAX_RESPONSE_CHARS: "${CODE_QUEUE_NOTIFY_CLAUDEQQ_MAX_RESPONSE_CHARS:-12000}"
CODE_QUEUE_NOTIFY_CLAUDEQQ_TIMEOUT_MS: "${CODE_QUEUE_NOTIFY_CLAUDEQQ_TIMEOUT_MS:-15000}"
CODE_QUEUE_NOTIFY_CLAUDEQQ_SEND_ATTEMPTS: "${CODE_QUEUE_NOTIFY_CLAUDEQQ_SEND_ATTEMPTS:-3}"
LOG_FILE: "/var/log/unidesk/code-queue.jsonl"
UNIDESK_LOG_RETENTION_BYTES: "${UNIDESK_LOG_RETENTION_BYTES:-1GiB}"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${CODE_QUEUE_WORKSPACE_PATH:-../../../..}:/root/unidesk
- ${CODE_QUEUE_WORKSPACE_PATH:-../../../..}:/workspace
- ${CODE_QUEUE_CODEX_CONFIG_PATH:-/home/ubuntu/.codex/config.toml}:/root/.codex/config.toml:ro
- ${CODE_QUEUE_SSH_DIR:-/home/ubuntu/.ssh}:/root/.ssh:ro
- ${CODE_QUEUE_LOG_DIR:-../../../../.state/code-queue/logs}:/var/log/unidesk
- ${CODE_QUEUE_STATE_DIR:-../../../../.state/code-queue}:/var/lib/unidesk/code-queue
extra_hosts:
- "host.docker.internal:host-gateway"
healthcheck:
test: ["CMD-SHELL", "curl -fsS --max-time 2 http://127.0.0.1:4222/health >/dev/null"]
interval: 5s
timeout: 3s
retries: 20
@@ -65,7 +65,7 @@ export function codeExecutionModeInfo(mode: CodeExecutionMode): Record<string, J
return {
kind: mode,
label: "Default Codex runtime",
description: "Use the main-server Codex runtime or the provider execution container.",
description: "Use the Code Queue node Codex runtime or the provider execution container.",
codexRunsInContainer: true,
requiresProvider: false,
requiresWindowsCwd: false,
@@ -279,7 +279,7 @@ function readConfig(): RuntimeConfig {
const codeModels = uniqueModels(withRequiredModel(envList("CODE_QUEUE_MODELS", defaultCodeModels).map(normalizeCodeModel), defaultModel));
const dataDir = envString("CODE_QUEUE_DATA_DIR", "/var/lib/unidesk/code-queue");
const notifyTargetTypeRaw = envString("CODE_QUEUE_NOTIFY_CLAUDEQQ_TARGET_TYPE", "private").toLowerCase();
const mainProviderId = envString("CODE_QUEUE_MAIN_PROVIDER_ID", "main-server");
const mainProviderId = envString("CODE_QUEUE_MAIN_PROVIDER_ID", "D601");
const devContainerDefaultProviderId = envString("CODE_QUEUE_DEV_CONTAINER_DEFAULT_PROVIDER_ID", "D601");
const remoteDefaultWorkdir = envString("CODE_QUEUE_REMOTE_WORKDIR", "/home/ubuntu");
const executionProviderIds = Array.from(new Set([
@@ -292,7 +292,7 @@ function readConfig(): RuntimeConfig {
dataDir,
outputArchiveDir: envString("CODE_QUEUE_OUTPUT_ARCHIVE_DIR", resolve(dataDir, "output-archive")),
logFile: envString("LOG_FILE", "/var/log/unidesk/code-queue.jsonl"),
defaultWorkdir: envString("CODE_QUEUE_WORKDIR", "/root/unidesk"),
defaultWorkdir: envString("CODE_QUEUE_WORKDIR", "/workspace"),
mainProviderId,
remoteDefaultWorkdir,
executionProviderIds,
@@ -290,7 +290,7 @@ export const defaultJudgeProbeCases: JudgeProbeCase[] = [
{
id: "codex_1778476426776_a2bf19_not_deployed_should_retry",
prompt: "originprompt -> 执行过程 Summary 1 -> finalresponse1 -> judge1 -> 执行过程 Summary 2 -> finalresponse2 judge2 还不够,还要有 judge feedback prompt,例如 originprompt -> 执行过程 Summary 1 -> finalresponse1 -> judge1 -> judge feedback prompt -> 执行过程 Summary 2 -> finalresponse2 judge2",
finalResponse: "已把 Trace 顶级链路补成:origin prompt -> Summary 1 -> final response 1 -> judge 1 -> judge feedback prompt -> Summary 2 -> final response 2 -> judge 2。后端扩展 attempt 数据,trace-summary 现在返回 feedback prompt preview/chars/lines/source/api/tasks/:id/prompt?part=feedback&attempt=N 支持按需拉取完整内容,前端显示 feedback prompt cards。验证已通过:bun run --cwd src/components/microservices/code-queue check、bun run --cwd src/components/frontend check、bun scripts/cli.ts check、git diff --check。当前 git diff clean。我没有重建运行中的容器。若要上线验证:1. bun scripts/cli.ts server rebuild code-queue 2. bun scripts/cli.ts server rebuild frontend。",
finalResponse: "已把 Trace 顶级链路补成:origin prompt -> Summary 1 -> final response 1 -> judge 1 -> judge feedback prompt -> Summary 2 -> final response 2 -> judge 2。后端扩展 attempt 数据,trace-summary 现在返回 feedback prompt preview/chars/lines/source/api/tasks/:id/prompt?part=feedback&attempt=N 支持按需拉取完整内容,前端显示 feedback prompt cards。验证已通过:bun run --cwd src/components/microservices/code-queue check、bun run --cwd src/components/frontend check、bun scripts/cli.ts check、git diff --check。当前 git diff clean。我没有重建运行中的容器。若要上线验证:1. 在 D601 用 docker compose 重建 code-queue-backend 2. bun scripts/cli.ts server rebuild frontend。",
expected: "retry",
terminalStatus: "completed",
outputs: [
@@ -118,7 +118,8 @@ function fallbackJudge(result: CodexRunResult, minimaxError?: string | JudgeFail
}
export const retryInstruction = "这是同一个 Codex thread 的 continuation,不是新任务。请基于上文继续完成原始任务;只做最小必要状态核查,避免从头重新摸索、避免重复已经完成的修改。";
const codeQueueRestartSafetyGuidance = "Code Queue 服务具备 restart-recovery,允许在任何时候重启、重建或替换 `code-queue-backend`;当当前任务修改 Code Queue 自身时,禁止等待当前 Code Queue task 退出或等待队列归零后再重启,因为这会等待自己退出形成自锁。正确做法是直接触发 `server rebuild code-queue` 或等价 no-deps force-recreate,并在恢复后用 live health/task 查询验证。";
const codeQueueD601DeployCommand = "bun scripts/cli.ts ssh D601 'cd /home/ubuntu/unidesk-code-queue-deploy && CODE_QUEUE_ENV_FILE=/home/ubuntu/unidesk-code-queue-deploy/.state/code-queue-d601.env docker compose -f src/components/microservices/code-queue/docker-compose.d601.yml up -d --build --force-recreate code-queue'";
const codeQueueRestartSafetyGuidance = `Code Queue 服务具备 restart-recovery,允许在任何时候重启、重建或替换 D601 的 \`code-queue-backend\`;当当前任务修改 Code Queue 自身时,禁止等待当前 Code Queue task 退出或等待队列归零后再重启,因为这会等待自己退出形成自锁。正确做法是在 D601 直接执行 \`${codeQueueD601DeployCommand}\` 或等价 no-deps force-recreate,并在恢复后用 live health/task 查询验证。`;
export function explicitUserInterrupt(task: QueueTask, result: CodexRunResult): boolean {
return result.terminalStatus === "interrupted"
@@ -278,7 +279,7 @@ function judgePrompt(task: QueueTask, result: CodexRunResult): string {
"列出缺失的验收证据,并要求下一轮在最终回复中给出真实命令/API/UI 结果。",
"如果 agent 在交付中途停下来询问用户如何处理一个它本不需要触碰的并发修改文件,continuePrompt 必须要求它忽略该并发文件并继续交付自己的变更范围,而不是让用户选择。",
"对于未部署/未上线的服务或 WebUI 工作,continuePrompt 必须明确要求重建/重启所有受影响的 service/container/bundle,并在部署后验证运行中的真实行为。",
"如果受影响服务包含 Code Queue 和 frontend,部署反馈应点名 `bun scripts/cli.ts server rebuild code-queue`、`bun scripts/cli.ts server rebuild frontend`,并按场景要求 live API/WebUI 验证,例如 `/api/judge/probe`、`/trace-summary` 或已服务的 Code Queue 页面。",
`如果受影响服务包含 Code Queue 和 frontend,部署反馈应点名 D601 Code Queue 重建命令 \`${codeQueueD601DeployCommand}\`\`bun scripts/cli.ts server rebuild frontend\`,并按场景要求 live API/WebUI 验证,例如 \`/api/judge/probe\`\`/trace-summary\` 或已服务的 Code Queue 页面。`,
"如果受影响服务包含 Code Queue 自身,continuePrompt 禁止要求等待当前 task 结束、等待 queue idle 或等待 `0 running` 后再重启;这会形成自锁。应明确 Code Queue 可以立即重启/重建,并依赖 restart-recovery 恢复本任务后继续验证。",
],
hallucinationNoiseGuardrails: [
@@ -348,7 +349,7 @@ function judgePrompt(task: QueueTask, result: CodexRunResult): string {
},
{
pattern: "原始任务要求 Code Queue 在 trace 链路和 WebUI 中暴露 judge feedback prompt,例如 task codex_1778476426776_a2bf19。",
incompleteEvidence: "执行代理编辑 backend/frontend 源码并通过 type check,但没有 `server rebuild code-queue`,没有 `server rebuild frontend` 或等价 served bundle 部署,也没有 live API/UI 验证证明 `/trace-summary` 或 WebUI 包含 judge feedback prompt。最终回复说“我没有重建运行中的容器”,或把 rebuild/deploy 当作建议/未来工作。",
incompleteEvidence: "执行代理编辑 backend/frontend 源码并通过 type check,但没有重建 D601 `code-queue-backend`,没有 `server rebuild frontend` 或等价 served bundle 部署,也没有 live API/UI 验证证明 `/trace-summary` 或 WebUI 包含 judge feedback prompt。最终回复说“我没有重建运行中的容器”,或把 rebuild/deploy 当作建议/未来工作。",
requiredDecision: "retry",
requiredContinuePrompt: "请让同一个 Codex thread 部署已修改的 Code Queue/frontend 服务,然后在声明完成前验证 live API/UI 证据。",
reason: "这是 service/UI 功能。只有运行中的 Code Queue backend 和 served frontend 已更新并验证后才算完成;否则用户仍看不到该功能。",
@@ -357,7 +358,7 @@ function judgePrompt(task: QueueTask, result: CodexRunResult): string {
pattern: "原始任务要求修改或验证 Code Queue 自身,并且完成条件需要重启/重建 `code-queue-backend`。",
incompleteEvidence: "执行代理表示要等当前 Code Queue task 结束、等队列空闲、等 0 running、或等自己退出后再重启 Code Queue,因此没有执行重启/重建和恢复后 live 验证。",
requiredDecision: "retry",
requiredContinuePrompt: "不要等待当前 Code Queue task 退出;Code Queue 可随时重启/重建。请直接执行 `server rebuild code-queue` 或等价 no-deps force-recreate,依赖 restart-recovery 恢复本任务,然后验证 live health/task 证据。",
requiredContinuePrompt: `不要等待当前 Code Queue task 退出;D601 Code Queue 可随时重启/重建。请直接执行 \`${codeQueueD601DeployCommand}\` 或等价 no-deps force-recreate,依赖 restart-recovery 恢复本任务,然后验证 live health/task 证据。`,
reason: "等待当前任务结束后再重启 Code Queue 会让任务等待自己退出,属于自锁;正确交付路径是先重启/重建再由 restart-recovery 继续。",
},
],
@@ -755,7 +756,7 @@ function deploymentFeedbackPrompt(task: QueueTask, reason: string): string {
`judge 未完成原因:${judgeReasonForPrompt(reason)}`,
"请先确认受影响范围;凡是 Code Queue、frontend、backend-core、provider-gateway 或其他运行服务/前端 bundle 的行为变化,都必须更新运行中的服务后再验收。",
"若受影响的是 Code Queue 自身,不要等待当前 Code Queue task 退出或等待队列空闲;可以立即重启/重建 `code-queue-backend`,由 restart-recovery 恢复本任务后继续验证。",
"如果本轮改动影响 Code Queue 和 frontend,请执行并记录真实结果:`bun scripts/cli.ts server rebuild code-queue`、`bun scripts/cli.ts server rebuild frontend`。",
`如果本轮改动影响 Code Queue 和 frontend,请执行并记录真实结果:\`${codeQueueD601DeployCommand}\`\`bun scripts/cli.ts server rebuild frontend\``,
"部署后必须做 live verification:至少用运行中的 API 或公网 WebUI 证明目标行为已经生效,例如 Code Queue `/api/judge/probe` 命中该样例、`/trace-summary` 返回 judge feedback prompt,或 served Code Queue 页面展示对应 feedback prompt。",
"最终 response 必须列出实际执行的部署命令和 live verification 结果;如果不能上线或验证,请说明阻塞并保持 retry 语义。",
"原始任务摘要/按需查询:",
@@ -70,7 +70,7 @@ function executionProviderOptions(): JsonValue[] {
].map(normalizeProviderId).filter((value): value is string => value !== null)));
return ids.map((providerId) => ({
id: providerId,
label: providerIsMain(providerId) ? `${providerId} (master)` : providerId,
label: providerIsMain(providerId) ? `${providerId} (local)` : providerId,
kind: providerIsMain(providerId) ? "local" : "remote-dev-container",
defaultWorkdir: defaultWorkdirForProvider(providerId),
supportsWindowsNativeCodex: !providerIsMain(providerId),