import test from "node:test"; import assert from "node:assert/strict"; import os from "node:os"; import path from "node:path"; import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; import { NextRequest } from "next/server"; let runtimeRoot = ""; let POST: (typeof import("../src/app/api/v1/projects/[projectId]/messages/route"))["POST"]; let saveAiAccount: (typeof import("../src/lib/boss-data"))["saveAiAccount"]; let updateProjectAgentControls: (typeof import("../src/lib/boss-data"))["updateProjectAgentControls"]; let readState: (typeof import("../src/lib/boss-data"))["readState"]; let createAuthSession: (typeof import("../src/lib/boss-data"))["createAuthSession"]; let appendProjectMessages: (typeof import("../src/lib/boss-data"))["appendProjectMessages"]; let AUTH_SESSION_COOKIE = ""; async function setup() { if (runtimeRoot) { return; } runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-master-agent-message-queue-")); process.env.BOSS_RUNTIME_ROOT = runtimeRoot; process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json"); const [messageRoute, data, auth] = await Promise.all([ import("../src/app/api/v1/projects/[projectId]/messages/route.ts"), import("../src/lib/boss-data.ts"), import("../src/lib/boss-auth.ts"), ]); POST = messageRoute.POST; saveAiAccount = data.saveAiAccount; updateProjectAgentControls = data.updateProjectAgentControls; readState = data.readState; createAuthSession = data.createAuthSession; appendProjectMessages = data.appendProjectMessages; AUTH_SESSION_COOKIE = auth.AUTH_SESSION_COOKIE; } async function createAuthedRequest(projectId: string, body: unknown) { const session = await createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); return new NextRequest(`http://127.0.0.1:3000/api/v1/projects/${projectId}/messages`, { method: "POST", headers: { "content-type": "application/json", cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, }, body: JSON.stringify(body), }); } async function waitFor(predicate: () => Promise, timeoutMs = 5_000) { const startedAt = Date.now(); while (Date.now() - startedAt < timeoutMs) { if (await predicate()) { return; } await new Promise((resolve) => setTimeout(resolve, 50)); } throw new Error("waitFor timed out"); } test.after(async () => { if (runtimeRoot) { await rm(runtimeRoot, { recursive: true, force: true }); } }); test.beforeEach(async () => { await setup(); await rm(runtimeRoot, { recursive: true, force: true }); await mkdir(runtimeRoot, { recursive: true }); }); test("appendProjectMessages 可以一次写入用户消息和主 Agent 本地快反回复", async () => { const messages = await appendProjectMessages({ projectId: "master-agent", messages: [ { senderLabel: "Boss 超级管理员", body: "你现在是什么模型", kind: "text", }, { sender: "master", senderLabel: "主 Agent · gpt-5.4-mini", body: "当前主 Agent 是快速反应模式。", kind: "text", }, ], }); assert.equal(messages.length, 2); assert.equal(messages[0]?.sender, "user"); assert.equal(messages[1]?.sender, "master"); const state = await readState(); const masterProject = state.projects.find((project) => project.id === "master-agent"); assert.equal( masterProject?.messages.find((message) => message.id === messages[0]?.id)?.body, "你现在是什么模型", ); assert.equal( masterProject?.messages.find((message) => message.id === messages[1]?.id)?.body, "当前主 Agent 是快速反应模式。", ); }); test("POST /api/v1/projects/master-agent/messages 快速返回队列态并在异步实际回复时继承当前会话覆盖", async () => { await saveAiAccount({ accountId: "openai-master-agent-queue", label: "API 容灾", role: "api_fallback", provider: "openai_api", displayName: "OpenAI API 队列测试", model: "gpt-5.4", apiKey: "sk-test-openai-queue", enabled: true, setActive: true, loginStatusNote: "用于 master-agent 队列测试。", }); await updateProjectAgentControls("master-agent", { modelOverride: "gpt-4.1-mini", reasoningEffortOverride: "high", }); const fetchCalls: Array<{ url: string; body: unknown }> = []; const originalFetch = globalThis.fetch; globalThis.fetch = (async (input, init) => { const body = typeof init?.body === "string" ? JSON.parse(init.body) : init?.body ?? null; fetchCalls.push({ url: String(input), body }); return new Response(JSON.stringify({ output_text: "已切到异步队列回复。" }), { status: 200, headers: { "content-type": "application/json", "x-request-id": "req-master-agent-queue", }, }); }) as typeof fetch; try { const response = await POST( await createAuthedRequest("master-agent", { body: "请同步 master-agent 当前阻塞点", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; task?: { taskId: string; taskType: string; status: string } | null; masterReplyState?: "queued" | "running" | "completed"; masterReply?: { accountId?: string } | null; }; assert.equal(payload.ok, true); assert.equal(payload.masterReply?.accountId, "openai-master-agent-queue"); assert.equal(payload.masterReplyState, "queued"); assert.ok(payload.task, "expected master-agent message to return a task envelope"); assert.equal(payload.task?.taskType, "conversation_reply"); assert.equal(payload.task?.status, "queued"); assert.ok(payload.task?.taskId, "expected a stable taskId in the response"); await waitFor(async () => { const state = await readState(); const task = state.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId); return task?.status === "completed"; }); const nextState = await readState(); const task = nextState.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId); assert.ok(task, "expected the queued task to remain in state"); assert.equal(task?.status, "completed"); assert.equal(task?.replyBody, "已切到异步队列回复。"); const masterProject = nextState.projects.find((project) => project.id === "master-agent"); const mirroredReply = masterProject?.messages.at(-1); assert.ok(mirroredReply, "expected the async reply to be written back to the master-agent ledger"); assert.match(mirroredReply?.body ?? "", /已切到异步队列回复/); assert.equal(fetchCalls.length, 1); assert.equal(fetchCalls[0]?.url, "https://api.openai.com/v1/responses"); const requestBody = fetchCalls[0]?.body as { model?: string; reasoning?: { effort?: string }; }; assert.equal(requestBody?.model, "gpt-4.1-mini"); assert.equal(requestBody?.reasoning?.effort, "high"); } finally { globalThis.fetch = originalFetch; } }); test("POST /api/v1/projects/master-agent/messages 在快速反应模式下会对简单问题走同步快路径", async () => { await saveAiAccount({ accountId: "openai-master-agent-fast-sync", label: "API 容灾", role: "api_fallback", provider: "openai_api", displayName: "OpenAI API 快反测试", model: "gpt-5.4", apiKey: "sk-test-openai-fast-sync", enabled: true, setActive: true, loginStatusNote: "用于 master-agent 快速反应同步路径测试。", }); await updateProjectAgentControls("master-agent", { modelOverride: "gpt-4.1", reasoningEffortOverride: "low", fastModelOverride: "gpt-4.1", deepModelOverride: "gpt-5.4", }); const fetchCalls: Array<{ url: string; body: unknown }> = []; const originalFetch = globalThis.fetch; globalThis.fetch = (async (input, init) => { const body = typeof init?.body === "string" ? JSON.parse(init.body) : init?.body ?? null; fetchCalls.push({ url: String(input), body }); return new Response(JSON.stringify({ output_text: "快速反应正常。" }), { status: 200, headers: { "content-type": "application/json", "x-request-id": "req-master-agent-fast-sync", }, }); }) as typeof fetch; try { const response = await POST( await createAuthedRequest("master-agent", { body: "请说:快速反应正常。", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; task?: { taskId: string } | null; masterReplyState?: "queued" | "running" | "completed" | null; masterReply?: { accountId?: string; requestId?: string; autoEscalated?: boolean } | null; replyMessage?: { sender?: string; body?: string } | null; }; assert.equal(payload.ok, true); assert.equal(payload.task ?? null, null); assert.equal(payload.masterReplyState, "completed"); assert.equal(payload.masterReply?.accountId, "openai-master-agent-fast-sync"); assert.equal(payload.masterReply?.requestId, "req-master-agent-fast-sync"); assert.equal(payload.masterReply?.autoEscalated, false); assert.equal(payload.replyMessage?.sender, "master"); assert.match(payload.replyMessage?.body ?? "", /快速反应正常/); const nextState = await readState(); const queuedTask = nextState.masterAgentTasks.find((item) => item.requestText === "请说:快速反应正常。"); assert.equal(queuedTask, undefined); const masterProject = nextState.projects.find((project) => project.id === "master-agent"); const reply = masterProject?.messages.at(-1); assert.ok(reply, "expected the sync reply to be written back immediately"); assert.match(reply?.body ?? "", /快速反应正常/); assert.equal(fetchCalls.length, 1); const requestBody = fetchCalls[0]?.body as { model?: string; reasoning?: { effort?: string }; }; assert.equal(requestBody?.model, "gpt-4.1"); assert.equal(requestBody?.reasoning?.effort, "low"); } finally { globalThis.fetch = originalFetch; } }); test("POST /api/v1/projects/master-agent/messages 对模型状态类问题会本地秒回且不调用模型", async () => { await saveAiAccount({ accountId: "master-codex-primary-local-status", label: "主 GPT", role: "primary", provider: "master_codex_node", displayName: "在线 Master Codex Node", nodeId: "mac-studio", nodeLabel: "Mac Studio", model: "gpt-5.4", enabled: true, setActive: true, loginStatusNote: "在线主节点。", }); await saveAiAccount({ accountId: "hyzq-fast-local-status", label: "环宇快反", role: "backup", provider: "hyzq_api", displayName: "环宇智擎 API", model: "gpt-5.4-mini", apiKey: "hyzq-fast-local-status-key", enabled: true, setActive: false, loginStatusNote: "环宇智擎快反账号。", }); await updateProjectAgentControls("master-agent", { modelOverride: "gpt-5.4-mini", reasoningEffortOverride: "low", fastModelOverride: "gpt-5.4-mini", deepModelOverride: "gpt-5.4", }); const originalFetch = globalThis.fetch; let fetchCalled = false; globalThis.fetch = (async () => { fetchCalled = true; throw new Error("model call should not happen for local status replies"); }) as typeof fetch; try { const response = await POST( await createAuthedRequest("master-agent", { body: "你现在是什么模型", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; task?: { taskId: string } | null; masterReplyState?: "queued" | "running" | "completed" | null; masterReply?: { accountId?: string; requestId?: string; effectiveModel?: string } | null; replyMessage?: { sender?: string; body?: string } | null; }; assert.equal(payload.ok, true); assert.equal(payload.task ?? null, null); assert.equal(payload.masterReplyState, "completed"); assert.equal(payload.masterReply?.requestId, "local-fast-path"); assert.equal(payload.masterReply?.effectiveModel, "gpt-5.4-mini"); assert.equal(payload.replyMessage?.sender, "master"); assert.match(payload.replyMessage?.body ?? "", /gpt-5.4-mini/); assert.equal(fetchCalled, false); const nextState = await readState(); const reply = nextState.projects.find((project) => project.id === "master-agent")?.messages.at(-1); assert.match(reply?.body ?? "", /gpt-5.4-mini/); assert.match(reply?.body ?? "", /快速反应/); } finally { globalThis.fetch = originalFetch; } }); test("POST /api/v1/projects/master-agent/messages 对可用模型查询会本地秒回并返回模式配置", async () => { await saveAiAccount({ accountId: "hyzq-fast-local-list", label: "环宇快反", role: "primary", provider: "hyzq_api", displayName: "环宇智擎 API", model: "gpt-5.4-mini", apiKey: "hyzq-fast-local-list-key", enabled: true, setActive: true, loginStatusNote: "环宇智擎快反账号。", }); await updateProjectAgentControls("master-agent", { modelOverride: "gpt-5.4-mini", reasoningEffortOverride: "low", fastModelOverride: "gpt-5.4-mini", deepModelOverride: "gpt-5.4", }); const originalFetch = globalThis.fetch; let fetchCalled = false; globalThis.fetch = (async () => { fetchCalled = true; throw new Error("model call should not happen for local model listing replies"); }) as typeof fetch; try { const response = await POST( await createAuthedRequest("master-agent", { body: "有哪些模型可用", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; task?: { taskId: string } | null; masterReplyState?: "queued" | "running" | "completed" | null; masterReply?: { requestId?: string } | null; replyMessage?: { body?: string } | null; }; assert.equal(payload.ok, true); assert.equal(payload.task ?? null, null); assert.equal(payload.masterReplyState, "completed"); assert.equal(payload.masterReply?.requestId, "local-fast-path"); assert.match(payload.replyMessage?.body ?? "", /gpt-5\.4-mini/); assert.match(payload.replyMessage?.body ?? "", /gpt-5\.4/); assert.match(payload.replyMessage?.body ?? "", /gpt-5\.1/); assert.match(payload.replyMessage?.body ?? "", /gpt-4\.1/); assert.equal(fetchCalled, false); } finally { globalThis.fetch = originalFetch; } }); test("POST /api/v1/projects/master-agent/messages 对深度思考切换请求会本地更新 controls 并秒回", async () => { await saveAiAccount({ accountId: "hyzq-fast-local-switch", label: "环宇快反", role: "primary", provider: "hyzq_api", displayName: "环宇智擎 API", model: "gpt-5.4-mini", apiKey: "hyzq-fast-local-switch-key", enabled: true, setActive: true, loginStatusNote: "环宇智擎快反账号。", }); await updateProjectAgentControls("master-agent", { modelOverride: "gpt-5.4-mini", reasoningEffortOverride: "low", fastModelOverride: "gpt-5.4-mini", deepModelOverride: "gpt-5.4", }); const originalFetch = globalThis.fetch; let fetchCalled = false; globalThis.fetch = (async () => { fetchCalled = true; throw new Error("model call should not happen for local mode switching replies"); }) as typeof fetch; try { const response = await POST( await createAuthedRequest("master-agent", { body: "切到深度思考", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; task?: { taskId: string } | null; masterReplyState?: "queued" | "running" | "completed" | null; masterReply?: { requestId?: string; effectiveModel?: string; effectiveReasoningEffort?: string } | null; replyMessage?: { body?: string } | null; }; assert.equal(payload.ok, true); assert.equal(payload.task ?? null, null); assert.equal(payload.masterReplyState, "completed"); assert.equal(payload.masterReply?.requestId, "local-fast-path"); assert.equal(payload.masterReply?.effectiveModel, "gpt-5.4"); assert.equal(payload.masterReply?.effectiveReasoningEffort, "high"); assert.match(payload.replyMessage?.body ?? "", /深度思考/); assert.match(payload.replyMessage?.body ?? "", /gpt-5\.4/); assert.equal(fetchCalled, false); const controls = await readState().then((state) => state.userProjectAgentControls.find( (entry) => entry.projectId === "master-agent" && entry.account === "17600003315", )?.controls, ); assert.equal(controls?.modelOverride, "gpt-5.4"); assert.equal(controls?.reasoningEffortOverride, "high"); assert.equal(controls?.fastModelOverride, "gpt-5.4-mini"); assert.equal(controls?.deepModelOverride, "gpt-5.4"); } finally { globalThis.fetch = originalFetch; } }); test("POST /api/v1/projects/master-agent/messages 在主节点在线时也会优先用环宇智擎执行快速反应", async () => { await saveAiAccount({ accountId: "master-codex-primary-online-fast", label: "主 GPT", role: "primary", provider: "master_codex_node", displayName: "在线 Master Codex Node", nodeId: "mac-studio", nodeLabel: "Mac Studio", model: "gpt-5.4", enabled: true, setActive: true, loginStatusNote: "在线主节点。", }); await saveAiAccount({ accountId: "hyzq-fast-backup", label: "环宇快反", role: "backup", provider: "hyzq_api", displayName: "环宇智擎 API", model: "gpt-5.4-mini", apiKey: "hyzq-fast-key", enabled: true, setActive: false, loginStatusNote: "环宇智擎快反账号。", }); await updateProjectAgentControls("master-agent", { modelOverride: "gpt-5.4-mini", reasoningEffortOverride: "low", fastModelOverride: "gpt-5.4-mini", deepModelOverride: "gpt-5.4", }); const fetchCalls: Array<{ url: string; body: unknown }> = []; const originalFetch = globalThis.fetch; globalThis.fetch = (async (input, init) => { const body = typeof init?.body === "string" ? JSON.parse(init.body) : init?.body ?? null; fetchCalls.push({ url: String(input), body }); return new Response(JSON.stringify({ output_text: "环宇快反正常。" }), { status: 200, headers: { "content-type": "application/json", "x-request-id": "req-master-agent-hyzq-fast", }, }); }) as typeof fetch; try { const response = await POST( await createAuthedRequest("master-agent", { body: "请说:环宇快反正常。", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; task?: { taskId: string } | null; masterReplyState?: "queued" | "running" | "completed" | null; masterReply?: { accountId?: string; requestId?: string } | null; }; assert.equal(payload.ok, true); assert.equal(payload.task ?? null, null); assert.equal(payload.masterReplyState, "completed"); assert.equal(payload.masterReply?.accountId, "hyzq-fast-backup"); assert.equal(payload.masterReply?.requestId, "req-master-agent-hyzq-fast"); const nextState = await readState(); const queuedTask = nextState.masterAgentTasks.find((item) => item.requestText === "请说:环宇快反正常。"); assert.equal(queuedTask, undefined); const reply = nextState.projects.find((project) => project.id === "master-agent")?.messages.at(-1); assert.match(reply?.body ?? "", /环宇快反正常/); assert.equal(fetchCalls.length, 1); assert.equal(fetchCalls[0]?.url, "https://api.hyzq2046.com/v1/responses"); const requestBody = fetchCalls[0]?.body as { model?: string; reasoning?: { effort?: string }; }; assert.equal(requestBody?.model, "gpt-5.4-mini"); assert.equal(requestBody?.reasoning?.effort, "low"); } finally { globalThis.fetch = originalFetch; } }); test("POST /api/v1/projects/master-agent/messages 在主节点在线时复杂快反请求会自动升档到环宇智擎深度队列", async () => { await saveAiAccount({ accountId: "master-codex-primary-online-deep", label: "主 GPT", role: "primary", provider: "master_codex_node", displayName: "在线 Master Codex Node", nodeId: "mac-studio", nodeLabel: "Mac Studio", model: "gpt-5.4", enabled: true, setActive: true, loginStatusNote: "在线主节点。", }); await saveAiAccount({ accountId: "hyzq-deep-backup", label: "环宇深思", role: "backup", provider: "hyzq_api", displayName: "环宇智擎 API", model: "gpt-5.4-mini", apiKey: "hyzq-deep-key", enabled: true, setActive: false, loginStatusNote: "环宇智擎深思账号。", }); await updateProjectAgentControls("master-agent", { modelOverride: "gpt-5.4-mini", reasoningEffortOverride: "low", fastModelOverride: "gpt-5.4-mini", deepModelOverride: "gpt-5.4", }); const fetchCalls: Array<{ url: string; body: unknown }> = []; const originalFetch = globalThis.fetch; globalThis.fetch = (async (input, init) => { const body = typeof init?.body === "string" ? JSON.parse(init.body) : init?.body ?? null; fetchCalls.push({ url: String(input), body }); return new Response(JSON.stringify({ output_text: "已经升档到环宇深度思考。" }), { status: 200, headers: { "content-type": "application/json", "x-request-id": "req-master-agent-hyzq-deep", }, }); }) as typeof fetch; try { const response = await POST( await createAuthedRequest("master-agent", { body: "请深入分析主 Agent 快速反应链路的性能瓶颈,并给出实现方案、风险和回归测试建议。", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; task?: { taskId: string; taskType: string; status: string } | null; masterReplyState?: "queued" | "running" | "completed" | null; masterReply?: { accountId?: string; autoEscalated?: boolean } | null; }; assert.equal(payload.ok, true); assert.equal(payload.masterReplyState, "queued"); assert.equal(payload.masterReply?.accountId, "hyzq-deep-backup"); assert.equal(payload.masterReply?.autoEscalated, true); assert.equal(payload.task?.taskType, "conversation_reply"); await waitFor(async () => { const state = await readState(); const task = state.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId); return task?.status === "completed"; }); const nextState = await readState(); const task = nextState.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId); assert.equal(task?.accountId, "hyzq-deep-backup"); assert.equal(task?.deviceId, "master-agent-openai"); assert.equal(task?.replyBody, "已经升档到环宇深度思考。"); assert.equal(fetchCalls.length, 1); assert.equal(fetchCalls[0]?.url, "https://api.hyzq2046.com/v1/responses"); const requestBody = fetchCalls[0]?.body as { model?: string; reasoning?: { effort?: string }; }; assert.equal(requestBody?.model, "gpt-5.4"); assert.equal(requestBody?.reasoning?.effort, "high"); } finally { globalThis.fetch = originalFetch; } }); test("master-agent enqueue 在主节点离线时会自动切到 OpenAI 后台队列而不是挂到本机设备队列", async () => { await saveAiAccount({ accountId: "master-codex-primary-offline", label: "主 GPT", role: "primary", provider: "master_codex_node", displayName: "离线 Master Codex Node", nodeId: "offline-node", nodeLabel: "离线节点", model: "gpt-5.4", enabled: true, setActive: true, loginStatusNote: "离线主节点", }); await saveAiAccount({ accountId: "openai-backup-queue", label: "备用 GPT", role: "backup", provider: "openai_api", displayName: "OpenAI 备用账号", accountIdentifier: "sk-queue-demo", model: "gpt-5.4", apiKey: "sk-queue-demo", enabled: true, setActive: false, loginStatusNote: "备用 API 账号", }); const originalFetch = globalThis.fetch; globalThis.fetch = (async () => new Response(JSON.stringify({ output_text: "离线主节点已切到 API 后台队列。" }), { status: 200, headers: { "content-type": "application/json", "x-request-id": "req-master-agent-offline-fallback-queue", }, })) as typeof fetch; try { const response = await POST( await createAuthedRequest("master-agent", { body: "请走备用 API 队列", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; task?: { taskId: string; taskType: string; status: string } | null; masterReplyState?: "queued" | "running" | "completed"; masterReply?: { accountId?: string } | null; }; assert.equal(payload.ok, true); assert.equal(payload.masterReply?.accountId, "openai-backup-queue"); assert.equal(payload.masterReplyState, "queued"); assert.equal(payload.task?.taskType, "conversation_reply"); await waitFor(async () => { const state = await readState(); const task = state.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId); return task?.status === "completed"; }); const nextState = await readState(); const task = nextState.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId); assert.equal(task?.deviceId, "master-agent-openai"); assert.equal(task?.status, "completed"); assert.equal(task?.accountId, "openai-backup-queue"); } finally { globalThis.fetch = originalFetch; } }); test("master-agent enqueue 在显式选择 claw-runtime 时会通过 Claw 异步回写回复", async () => { const clawDir = await mkdtemp(path.join(os.tmpdir(), "boss-claw-queue-")); const clawScriptPath = path.join(clawDir, "claw-runtime.mjs"); await writeFile( clawScriptPath, ` let stdin = ""; process.stdin.setEncoding("utf8"); for await (const chunk of process.stdin) { stdin += chunk; } const payload = JSON.parse(stdin); process.stdout.write(JSON.stringify({ status: "completed", output: "Claw 已接管当前主 Agent 会话:" + payload.body })); `, "utf8", ); const previousEnv = { BOSS_CLAW_ENABLED: process.env.BOSS_CLAW_ENABLED, BOSS_CLAW_COMMAND: process.env.BOSS_CLAW_COMMAND, BOSS_CLAW_ARGS: process.env.BOSS_CLAW_ARGS, BOSS_CLAW_TIMEOUT_MS: process.env.BOSS_CLAW_TIMEOUT_MS, }; process.env.BOSS_CLAW_ENABLED = "true"; process.env.BOSS_CLAW_COMMAND = process.execPath; process.env.BOSS_CLAW_ARGS = clawScriptPath; process.env.BOSS_CLAW_TIMEOUT_MS = "1000"; await saveAiAccount({ accountId: "master-codex-primary-claw", label: "主 GPT", role: "primary", provider: "master_codex_node", displayName: "Mac 上的 Master Codex Node", nodeId: "local-codex-node", nodeLabel: "本机 Codex", model: "gpt-5.4", enabled: true, setActive: true, loginStatusNote: "用于 Claw backend 队列测试。", }); await updateProjectAgentControls("master-agent", { backendOverride: "claw-runtime", }); try { const response = await POST( await createAuthedRequest("master-agent", { body: "请走 Claw runtime", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; task?: { taskId: string; status: string } | null; masterReply?: { accountId?: string } | null; masterReplyState?: string | null; }; assert.equal(payload.ok, true); assert.equal(payload.masterReply?.accountId, "claw-runtime"); assert.equal(payload.masterReplyState, "queued"); assert.ok(payload.task?.taskId); await waitFor(async () => { const state = await readState(); const task = state.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId); return task?.status === "completed"; }); const nextState = await readState(); const task = nextState.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId); assert.equal(task?.status, "completed"); assert.equal(task?.replyBody, "Claw 已接管当前主 Agent 会话:请走 Claw runtime"); const masterProject = nextState.projects.find((project) => project.id === "master-agent"); const mirroredReply = masterProject?.messages.at(-1); assert.match(mirroredReply?.body ?? "", /Claw 已接管当前主 Agent 会话/); } finally { process.env.BOSS_CLAW_ENABLED = previousEnv.BOSS_CLAW_ENABLED; process.env.BOSS_CLAW_COMMAND = previousEnv.BOSS_CLAW_COMMAND; process.env.BOSS_CLAW_ARGS = previousEnv.BOSS_CLAW_ARGS; process.env.BOSS_CLAW_TIMEOUT_MS = previousEnv.BOSS_CLAW_TIMEOUT_MS; await rm(clawDir, { recursive: true, force: true }); } }); test("master-agent enqueue 在首选主节点离线时会回退到可用的备用主节点并返回实际账号", async () => { await saveAiAccount({ accountId: "master-codex-primary-offline", label: "主 GPT", role: "primary", provider: "master_codex_node", displayName: "离线 Master Codex Node", nodeId: "offline-node", nodeLabel: "离线节点", model: "gpt-5.4", enabled: true, setActive: true, loginStatusNote: "离线主节点", }); await saveAiAccount({ accountId: "master-codex-backup-online", label: "备用主节点", role: "backup", provider: "master_codex_node", displayName: "在线备用 Master Codex Node", nodeId: "mac-studio", nodeLabel: "Mac Studio", model: "gpt-5.4", enabled: true, setActive: false, loginStatusNote: "在线备用主节点", }); const response = await POST( await createAuthedRequest("master-agent", { body: "请走备用主节点队列", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; task?: { taskId: string; taskType: string; status: string } | null; masterReplyState?: "queued" | "running" | "completed"; masterReply?: { accountId?: string } | null; }; assert.equal(payload.ok, true); assert.equal(payload.masterReply?.accountId, "master-codex-backup-online"); assert.equal(payload.masterReplyState, "queued"); assert.equal(payload.task?.taskType, "conversation_reply"); assert.equal(payload.task?.status, "queued"); const state = await readState(); const task = state.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId); assert.ok(task, "expected queued master-agent task"); assert.equal(task?.accountId, "master-codex-backup-online"); assert.equal(task?.deviceId, "mac-studio"); }); test("master-agent enqueue 会在首个 API 候选失败后切到下一条备用链并重写任务账号", async () => { await saveAiAccount({ accountId: "openai-primary-queue", label: "OpenAI 主控", role: "primary", provider: "openai_api", displayName: "OpenAI 主账号", model: "gpt-5.4", apiKey: "sk-openai-primary-queue", enabled: true, setActive: true, loginStatusNote: "OpenAI 主控", }); await saveAiAccount({ accountId: "aliyun-qwen-backup-queue", label: "阿里备用", role: "backup", provider: "aliyun_qwen_api", displayName: "阿里百炼备用账号", model: "qwen3.5-plus", apiKey: "sk-aliyun-backup-queue", enabled: true, setActive: false, loginStatusNote: "阿里备用账号", }); const fetchCalls: string[] = []; const originalFetch = globalThis.fetch; globalThis.fetch = (async (input) => { fetchCalls.push(String(input)); if (typeof input === "string" && input === "https://api.openai.com/v1/responses") { return new Response(JSON.stringify({ error: { message: "openai queue failure" } }), { status: 500, headers: { "content-type": "application/json" }, }); } if (typeof input === "string" && input === "https://dashscope.aliyuncs.com/compatible-mode/v1/responses") { return new Response(JSON.stringify({ output_text: "后台队列已切到阿里备用。" }), { status: 200, headers: { "content-type": "application/json", "x-request-id": "req-master-agent-queue-fallback-chain", }, }); } throw new Error(`unexpected fetch: ${String(input)}`); }) as typeof fetch; try { const response = await POST( await createAuthedRequest("master-agent", { body: "请让后台队列自动切备用链", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; task?: { taskId: string; taskType: string; status: string } | null; masterReplyState?: "queued" | "running" | "completed"; masterReply?: { accountId?: string } | null; }; assert.equal(payload.ok, true); assert.equal(payload.masterReply?.accountId, "openai-primary-queue"); assert.equal(payload.masterReplyState, "queued"); await waitFor(async () => { const state = await readState(); const task = state.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId); return task?.status === "completed"; }); const state = await readState(); const task = state.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId); assert.ok(task, "expected queued task to remain in state"); assert.equal(task?.status, "completed"); assert.equal(task?.accountId, "aliyun-qwen-backup-queue"); assert.equal(task?.deviceId, "master-agent-aliyun-qwen"); const aliyunAccount = state.aiAccounts.find((item) => item.accountId === "aliyun-qwen-backup-queue"); assert.equal(aliyunAccount?.isActive, true); assert.equal(fetchCalls[0], "https://api.openai.com/v1/responses"); assert.equal(fetchCalls[1], "https://dashscope.aliyuncs.com/compatible-mode/v1/responses"); } finally { globalThis.fetch = originalFetch; } });