diff --git a/src/lib/boss-master-agent.ts b/src/lib/boss-master-agent.ts index 597d9f0..bf350c7 100644 --- a/src/lib/boss-master-agent.ts +++ b/src/lib/boss-master-agent.ts @@ -1972,6 +1972,12 @@ async function tryHandleMasterAgentModelCommand(params: { } const visibleModels = context.visibleModels; const usableModels = context.usableModels; + const availableModelsSuffix = + usableModels.length > 0 + ? `当前已就绪模型:${usableModels.join("、")}。` + : visibleModels.length > 0 + ? `已登记/可选模型:${visibleModels.join("、")}。` + : "暂时还没有可展示的模型清单。"; if (isModelListRequest(params.requestText) && !isModelSwitchRequest(params.requestText)) { const configuredSummary = visibleModels.length > 0 ? visibleModels.join("、") : "暂时还没有"; @@ -1985,12 +1991,12 @@ async function tryHandleMasterAgentModelCommand(params: { const requestedModel = detectRequestedModelName(params.requestText, visibleModels); if (!requestedModel) { - const reply = `我收到的是模型切换请求,但没有识别到具体模型名。当前可用模型:${usableModels.length > 0 ? usableModels.join("、") : "暂无"}。`; + const reply = `我收到的是模型切换请求,但没有识别到具体模型名。${availableModelsSuffix}`; return appendFastPathError(reply, "MODEL_NAME_REQUIRED", buildMasterAgentModelSenderLabel(context.effectiveChatPolicy.model)); } if (!visibleModels.includes(requestedModel)) { - const reply = `我没找到可切换到的模型 ${requestedModel}。当前可用模型:${usableModels.length > 0 ? usableModels.join("、") : "暂无"};已登记/可选模型:${visibleModels.join("、")}。`; + const reply = `我没找到可切换到的模型 ${requestedModel}。${availableModelsSuffix}`; return appendFastPathError(reply, "MODEL_NOT_AVAILABLE", buildMasterAgentModelSenderLabel(context.effectiveChatPolicy.model)); } @@ -2005,7 +2011,7 @@ async function tryHandleMasterAgentModelCommand(params: { : { modelOverride: requestedModel }; await updateProjectAgentControls("master-agent", patch, params.requestedByAccount); - const reply = `已把主 Agent 的${scopeLabel}切到 ${requestedModel}。当前可用模型:${usableModels.length > 0 ? usableModels.join("、") : "暂无"}。`; + const reply = `已把主 Agent 的${scopeLabel}切到 ${requestedModel}。${availableModelsSuffix}`; return appendFastPathReply(reply, buildMasterAgentModelSenderLabel(requestedModel)); } diff --git a/tests/master-agent-message-queue.test.ts b/tests/master-agent-message-queue.test.ts index c8c3c24..a676304 100644 --- a/tests/master-agent-message-queue.test.ts +++ b/tests/master-agent-message-queue.test.ts @@ -176,6 +176,50 @@ test("master-agent 明确要求切快模型时直接更新 controls 并返回完 assert.equal(reply?.senderLabel ?? "", "主Agent·gpt-5.4-mini"); }); +test("master-agent 切换模型成功时不会因为当前在线账号为空就回报可用模型暂无", async () => { + await saveAiAccount({ + accountId: "master-codex-offline-switch", + label: "主 GPT", + role: "primary", + provider: "master_codex_node", + displayName: "Mac 上的 Master Codex Node", + nodeId: "offline-master-node", + nodeLabel: "离线 Codex", + model: "gpt-5.4", + enabled: true, + setActive: true, + loginStatusNote: "用于离线主节点模型切换文案测试。", + }); + + const response = await POST( + await createAuthedRequest("master-agent", { + body: "把主agent模型换成gpt5.4-mini", + }), + { 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; + }; + assert.equal(payload.ok, true); + assert.equal(payload.task ?? null, null); + assert.equal(payload.masterReplyState, "completed"); + + const controls = await getProjectAgentControls("master-agent", "17600003315"); + assert.equal(controls?.modelOverride ?? null, "gpt-5.4-mini"); + + const state = await readState(); + const masterProject = state.projects.find((project) => project.id === "master-agent"); + const reply = masterProject?.messages.at(-1); + assert.ok(reply, "expected the master-agent offline switch reply to be persisted"); + assert.match(reply?.body ?? "", /已把主 Agent 的当前主模型切到 gpt-5\.4-mini/); + assert.doesNotMatch(reply?.body ?? "", /当前可用模型:暂无/); + assert.match(reply?.body ?? "", /已登记\/可选模型:/); +}); + test("master-agent 识别自然写法的模型名并切当前主模型", async () => { await saveAiAccount({ accountId: "openai-main-switch",