import test from "node:test"; import assert from "node:assert/strict"; import os from "node:os"; import path from "node:path"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { execFile as execFileCallback } from "node:child_process"; import { promisify } from "node:util"; import { NextRequest } from "next/server"; let runtimeRoot = ""; let readState: (typeof import("../src/lib/boss-data"))["readState"]; let writeState: (typeof import("../src/lib/boss-data"))["writeState"]; let updateProjectAgentControls: (typeof import("../src/lib/boss-data"))["updateProjectAgentControls"]; let getProjectAgentControls: (typeof import("../src/lib/boss-data"))["getProjectAgentControls"]; let getProjectDetailView: (typeof import("../src/lib/boss-projections"))["getProjectDetailView"]; let getProjectRoute: (typeof import("../src/app/api/v1/projects/[projectId]/route"))["GET"]; let getAgentControlsRoute: (typeof import("../src/app/api/v1/projects/[projectId]/agent-controls/route"))["GET"]; let postAgentControlsRoute: (typeof import("../src/app/api/v1/projects/[projectId]/agent-controls/route"))["POST"]; let createAuthSession: (typeof import("../src/lib/boss-data"))["createAuthSession"]; let AUTH_SESSION_COOKIE = ""; const execFile = promisify(execFileCallback); async function setup() { if (runtimeRoot) return; runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-master-agent-controls-")); process.env.BOSS_RUNTIME_ROOT = runtimeRoot; process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json"); const [data, projections, projectRouteModule, agentControlsRouteModule, auth] = await Promise.all([ import("../src/lib/boss-data.ts"), import("../src/lib/boss-projections.ts"), import("../src/app/api/v1/projects/[projectId]/route.ts"), import("../src/app/api/v1/projects/[projectId]/agent-controls/route.ts"), import("../src/lib/boss-auth.ts"), ]); readState = data.readState; writeState = data.writeState; updateProjectAgentControls = data.updateProjectAgentControls; getProjectAgentControls = data.getProjectAgentControls; getProjectDetailView = projections.getProjectDetailView; getProjectRoute = projectRouteModule.GET; getAgentControlsRoute = agentControlsRouteModule.GET; postAgentControlsRoute = agentControlsRouteModule.POST; createAuthSession = data.createAuthSession; AUTH_SESSION_COOKIE = auth.AUTH_SESSION_COOKIE; } async function resetMasterAgentControls() { await setup(); const state = await readState(); const project = state.projects.find((item) => item.id === "master-agent"); assert.ok(project, "expected seeded master-agent project"); delete project.agentControls; state.userProjectAgentControls = state.userProjectAgentControls.filter( (item) => item.projectId !== "master-agent", ); await writeState(state); } test.beforeEach(async () => { await resetMasterAgentControls(); }); test.after(async () => { if (runtimeRoot) { await rm(runtimeRoot, { recursive: true, force: true }); } }); test("master-agent 会话可保存并读取模型与推理强度覆盖", async () => { await setup(); await updateProjectAgentControls("master-agent", { modelOverride: "gpt-5.4", reasoningEffortOverride: "high", }); const controls = await getProjectAgentControls("master-agent"); assert.equal(controls?.modelOverride, "gpt-5.4"); assert.equal(controls?.reasoningEffortOverride, "high"); const state = await readState(); const project = state.projects.find((item) => item.id === "master-agent"); assert.equal(project?.agentControls?.modelOverride, "gpt-5.4"); assert.equal(project?.agentControls?.reasoningEffortOverride, "high"); const detail = getProjectDetailView(state, "master-agent"); assert.equal(detail?.agentControls?.modelOverride, "gpt-5.4"); assert.equal(detail?.agentControls?.reasoningEffortOverride, "high"); }); test("master-agent 对话控制路由可读写并回显到项目详情", async () => { await setup(); const tempDir = await mkdtemp(path.join(os.tmpdir(), "boss-claw-agent-controls-")); const scriptPath = path.join(tempDir, "claw-runtime.mjs"); await writeFile(scriptPath, "console.log('ok');\n", "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_WORKDIR: process.env.BOSS_CLAW_WORKDIR, }; process.env.BOSS_CLAW_ENABLED = "true"; process.env.BOSS_CLAW_COMMAND = process.execPath; process.env.BOSS_CLAW_ARGS = scriptPath; process.env.BOSS_CLAW_WORKDIR = tempDir; try { const session = await createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); const headers = { "content-type": "application/json", cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, }; const postResponse = await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers, body: JSON.stringify({ modelOverride: "gpt-5.4", reasoningEffortOverride: "medium", backendOverride: "claw-runtime", }), }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(postResponse.status, 200); const postPayload = (await postResponse.json()) as { ok: boolean; controls: { modelOverride?: string; reasoningEffortOverride?: string; backendOverride?: string; updatedAt: string; } | null; }; assert.equal(postPayload.ok, true); assert.equal(postPayload.controls?.modelOverride, "gpt-5.4"); assert.equal(postPayload.controls?.reasoningEffortOverride, "medium"); assert.equal(postPayload.controls?.backendOverride, "claw-runtime"); const getResponse = await getAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "GET", headers, }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(getResponse.status, 200); const getPayload = (await getResponse.json()) as { ok: boolean; controls: { modelOverride?: string; reasoningEffortOverride?: string; backendOverride?: string; updatedAt: string; } | null; }; assert.equal(getPayload.ok, true); assert.equal(getPayload.controls?.modelOverride, "gpt-5.4"); assert.equal(getPayload.controls?.reasoningEffortOverride, "medium"); assert.equal(getPayload.controls?.backendOverride, "claw-runtime"); const projectResponse = await getProjectRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent", { method: "GET", headers, }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(projectResponse.status, 200); const projectPayload = (await projectResponse.json()) as { ok: boolean; agentControls: { modelOverride?: string; reasoningEffortOverride?: string; backendOverride?: string; updatedAt: string; } | null; }; assert.equal(projectPayload.ok, true); assert.equal(projectPayload.agentControls?.modelOverride, "gpt-5.4"); assert.equal(projectPayload.agentControls?.reasoningEffortOverride, "medium"); assert.equal(projectPayload.agentControls?.backendOverride, "claw-runtime"); } finally { if (previousEnv.BOSS_CLAW_ENABLED === undefined) delete process.env.BOSS_CLAW_ENABLED; else process.env.BOSS_CLAW_ENABLED = previousEnv.BOSS_CLAW_ENABLED; if (previousEnv.BOSS_CLAW_COMMAND === undefined) delete process.env.BOSS_CLAW_COMMAND; else process.env.BOSS_CLAW_COMMAND = previousEnv.BOSS_CLAW_COMMAND; if (previousEnv.BOSS_CLAW_ARGS === undefined) delete process.env.BOSS_CLAW_ARGS; else process.env.BOSS_CLAW_ARGS = previousEnv.BOSS_CLAW_ARGS; if (previousEnv.BOSS_CLAW_WORKDIR === undefined) delete process.env.BOSS_CLAW_WORKDIR; else process.env.BOSS_CLAW_WORKDIR = previousEnv.BOSS_CLAW_WORKDIR; await rm(tempDir, { recursive: true, force: true }); } }); test("master-agent 对话控制按当前账号隔离,不会串到其他用户", async () => { await setup(); const adminSession = await createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); const memberSession = await createAuthSession({ account: "18800001111", role: "member", displayName: "普通成员", loginMethod: "password", }); const adminHeaders = { "content-type": "application/json", cookie: `${AUTH_SESSION_COOKIE}=${adminSession.sessionToken}`, }; const memberHeaders = { "content-type": "application/json", cookie: `${AUTH_SESSION_COOKIE}=${memberSession.sessionToken}`, }; await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers: adminHeaders, body: JSON.stringify({ modelOverride: "gpt-5.4", reasoningEffortOverride: "high", }), }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); const adminGet = await getAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "GET", headers: adminHeaders, }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); const memberGet = await getAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "GET", headers: memberHeaders, }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); const adminPayload = (await adminGet.json()) as { controls: { modelOverride?: string; reasoningEffortOverride?: string } | null }; const memberPayload = (await memberGet.json()) as { controls: { modelOverride?: string; reasoningEffortOverride?: string } | null }; assert.equal(adminPayload.controls?.modelOverride, "gpt-5.4"); assert.equal(adminPayload.controls?.reasoningEffortOverride, "high"); assert.equal(memberPayload.controls, null); }); test("master-agent 对话控制路由单字段更新不会清掉另一字段", async () => { await setup(); const session = await createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); const headers = { "content-type": "application/json", cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, }; await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers, body: JSON.stringify({ modelOverride: "gpt-5.4", reasoningEffortOverride: "high", }), }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); const response = await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers, body: JSON.stringify({ reasoningEffortOverride: "low", }), }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; controls: { modelOverride?: string; reasoningEffortOverride?: string; } | null; }; assert.equal(payload.ok, true); assert.equal(payload.controls?.modelOverride, "gpt-5.4"); assert.equal(payload.controls?.reasoningEffortOverride, "low"); }); test("master-agent 对话控制 POST 清空后仍稳定回传 controls null", async () => { await setup(); const session = await createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); const headers = { "content-type": "application/json", cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, }; await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers, body: JSON.stringify({ modelOverride: "gpt-5.4", reasoningEffortOverride: "high", }), }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); const clearResponse = await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers, body: JSON.stringify({ modelOverride: null, reasoningEffortOverride: null, }), }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(clearResponse.status, 200); const clearPayload = (await clearResponse.json()) as { ok: boolean; controls: unknown; }; assert.equal(clearPayload.ok, true); assert.equal(Object.prototype.hasOwnProperty.call(clearPayload, "controls"), true); assert.equal(clearPayload.controls, null); }); test("非 master-agent 项目详情不应回传 agentControls 字段", async () => { await setup(); const session = await createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); const response = await getProjectRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/boss-console", { method: "GET", headers: { cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, }, }), { params: Promise.resolve({ projectId: "boss-console" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as Record; assert.equal(payload.ok, true); assert.equal(Object.prototype.hasOwnProperty.call(payload, "agentControls"), false); }); test("master-agent 对话控制 POST 允许当前用户修改自己的 master-agent 会话配置", async () => { await setup(); const session = await createAuthSession({ account: "viewer-0001", role: "member", displayName: "普通成员", loginMethod: "password", }); const response = await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers: { "content-type": "application/json", cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, }, body: JSON.stringify({ modelOverride: "gpt-5.4", reasoningEffortOverride: "low", }), }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 200); const payload = (await response.json()) as { ok: boolean; controls: { modelOverride?: string; reasoningEffortOverride?: string } | null; }; assert.equal(payload.ok, true); assert.equal(payload.controls?.modelOverride, "gpt-5.4"); assert.equal(payload.controls?.reasoningEffortOverride, "low"); const controls = await getProjectAgentControls("master-agent", "viewer-0001"); assert.equal(controls?.modelOverride, "gpt-5.4"); assert.equal(controls?.reasoningEffortOverride, "low"); }); test("master-agent 对话控制 POST 会稳定拒绝非法 modelOverride", async () => { await setup(); const session = await createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); const response = await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers: { "content-type": "application/json", cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, }, body: JSON.stringify({ modelOverride: 123, }), }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 400); const payload = (await response.json()) as { ok: boolean; message: string }; assert.equal(payload.ok, false); assert.equal(payload.message, "INVALID_MODEL_OVERRIDE"); const controls = await getProjectAgentControls("master-agent"); assert.equal(controls, null); }); test("master-agent 对话控制 POST 会稳定拒绝 malformed JSON 和空对象", async () => { await setup(); const session = await createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); const headers = { "content-type": "application/json", cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, }; const beforeState = await readState(); const beforeProject = beforeState.projects.find((item) => item.id === "master-agent"); assert.ok(beforeProject, "expected seeded master-agent project"); const beforeUpdatedAt = beforeProject.updatedAt; const beforeThreadMetaUpdatedAt = beforeProject.threadMeta.updatedAt; const malformedResponse = await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers, body: "{", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(malformedResponse.status, 400); assert.equal((await malformedResponse.json()).message, "INVALID_JSON_PAYLOAD"); const emptyObjectResponse = await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers, body: JSON.stringify({}), }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(emptyObjectResponse.status, 400); assert.equal((await emptyObjectResponse.json()).message, "INVALID_AGENT_CONTROLS_PAYLOAD"); const nullResponse = await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers, body: "null", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(nullResponse.status, 400); assert.equal((await nullResponse.json()).message, "INVALID_AGENT_CONTROLS_PAYLOAD"); const arrayResponse = await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers, body: "[]", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(arrayResponse.status, 400); assert.equal((await arrayResponse.json()).message, "INVALID_AGENT_CONTROLS_PAYLOAD"); const primitiveResponse = await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers, body: "1", }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(primitiveResponse.status, 400); assert.equal((await primitiveResponse.json()).message, "INVALID_AGENT_CONTROLS_PAYLOAD"); const controls = await getProjectAgentControls("master-agent"); assert.equal(controls, null); const state = await readState(); const project = state.projects.find((item) => item.id === "master-agent"); assert.equal(project?.agentControls, undefined); assert.equal(project?.updatedAt, beforeUpdatedAt); assert.equal(project?.threadMeta.updatedAt, beforeThreadMetaUpdatedAt); }); test("master-agent 对话控制 helper 会安全忽略非法 modelOverride 输入", async () => { await setup(); await updateProjectAgentControls("master-agent", { modelOverride: "gpt-5.4", reasoningEffortOverride: "high", }); const beforeState = await readState(); const beforeProject = beforeState.projects.find((item) => item.id === "master-agent"); assert.ok(beforeProject, "expected seeded master-agent project"); const beforeUpdatedAt = beforeProject.updatedAt; const beforeThreadMetaUpdatedAt = beforeProject.threadMeta.updatedAt; await assert.rejects( () => updateProjectAgentControls("master-agent", { modelOverride: { bad: true } as never, reasoningEffortOverride: "medium", }), /INVALID_MODEL_OVERRIDE/, ); const controls = await getProjectAgentControls("master-agent"); assert.equal(controls?.modelOverride, "gpt-5.4"); assert.equal(controls?.reasoningEffortOverride, "high"); const afterState = await readState(); const afterProject = afterState.projects.find((item) => item.id === "master-agent"); assert.equal(afterProject?.updatedAt, beforeUpdatedAt); assert.equal(afterProject?.threadMeta.updatedAt, beforeThreadMetaUpdatedAt); }); test("master-agent 对话控制 helper 重复提交或重复清空不应刷新更新时间", async () => { await setup(); await updateProjectAgentControls("master-agent", { modelOverride: "gpt-5.4", reasoningEffortOverride: "high", }); const beforeRepeatState = await readState(); const beforeRepeatProject = beforeRepeatState.projects.find((item) => item.id === "master-agent"); assert.ok(beforeRepeatProject, "expected seeded master-agent project"); const beforeRepeatUpdatedAt = beforeRepeatProject.updatedAt; const beforeRepeatThreadMetaUpdatedAt = beforeRepeatProject.threadMeta.updatedAt; await updateProjectAgentControls("master-agent", { modelOverride: "gpt-5.4", reasoningEffortOverride: "high", }); const afterRepeatState = await readState(); const afterRepeatProject = afterRepeatState.projects.find((item) => item.id === "master-agent"); assert.equal(afterRepeatProject?.updatedAt, beforeRepeatUpdatedAt); assert.equal(afterRepeatProject?.threadMeta.updatedAt, beforeRepeatThreadMetaUpdatedAt); await updateProjectAgentControls("master-agent", { modelOverride: undefined, reasoningEffortOverride: undefined, }); const afterClearState = await readState(); const afterClearProject = afterClearState.projects.find((item) => item.id === "master-agent"); const clearedUpdatedAt = afterClearProject?.updatedAt; const clearedThreadMetaUpdatedAt = afterClearProject?.threadMeta.updatedAt; await updateProjectAgentControls("master-agent", { modelOverride: undefined, reasoningEffortOverride: undefined, }); const afterRepeatClearState = await readState(); const afterRepeatClearProject = afterRepeatClearState.projects.find((item) => item.id === "master-agent"); assert.equal(afterRepeatClearProject?.updatedAt, clearedUpdatedAt); assert.equal(afterRepeatClearProject?.threadMeta.updatedAt, clearedThreadMetaUpdatedAt); assert.equal(await getProjectAgentControls("master-agent"), null); }); test("master-agent 对话控制 helper 单字段更新不会清掉另一字段", async () => { await setup(); await updateProjectAgentControls("master-agent", { modelOverride: "gpt-5.4", reasoningEffortOverride: "high", }); await updateProjectAgentControls("master-agent", { reasoningEffortOverride: "low", }); const controls = await getProjectAgentControls("master-agent"); assert.equal(controls?.modelOverride, "gpt-5.4"); assert.equal(controls?.reasoningEffortOverride, "low"); }); test("master-agent 对话控制可清空为未覆盖状态", async () => { await setup(); await updateProjectAgentControls("master-agent", { modelOverride: "gpt-5.4", reasoningEffortOverride: "high", }); await updateProjectAgentControls("master-agent", { modelOverride: undefined, reasoningEffortOverride: undefined, }); const controls = await getProjectAgentControls("master-agent"); assert.equal(controls, null); const state = await readState(); const project = state.projects.find((item) => item.id === "master-agent"); assert.equal(project?.agentControls, undefined); const detail = getProjectDetailView(state, "master-agent"); assert.equal(detail?.agentControls, null); }); test("GET /agent-controls returns 404 for missing project", async () => { await setup(); const session = await createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); const response = await getAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/missing-project/agent-controls", { method: "GET", headers: { cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, }, }), { params: Promise.resolve({ projectId: "missing-project" }) }, ); assert.equal(response.status, 404); assert.equal((await response.json()).message, "PROJECT_NOT_FOUND"); }); test( "GET /agent-controls returns 404 when master-agent record is missing from state", { concurrency: false }, async () => { await setup(); const script = String.raw` import { mkdtemp, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { NextRequest } from "next/server"; (async () => { const runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-master-agent-missing-")); process.env.BOSS_RUNTIME_ROOT = runtimeRoot; process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json"); const dataModule = await import("./src/lib/boss-data.ts"); const authModule = await import("./src/lib/boss-auth.ts"); const routeModule = await import("./src/app/api/v1/projects/[projectId]/agent-controls/route.ts"); const data = dataModule.default ?? dataModule; const auth = authModule.default ?? authModule; const route = routeModule.default ?? routeModule; const session = await data.createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); const state = await data.readState(); state.projects = state.projects.filter((item) => item.id !== "master-agent"); await writeFile(process.env.BOSS_STATE_FILE!, JSON.stringify(state, null, 2), "utf8"); if (await data.hasPersistedProject("master-agent")) { throw new Error("expected raw state to miss master-agent"); } const response = await route.GET( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "GET", headers: { cookie: auth.AUTH_SESSION_COOKIE + "=" + session.sessionToken, }, }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); const text = await response.text(); if (response.status !== 404) { throw new Error("expected 404, got " + response.status + ": " + text); } if (!text.includes("PROJECT_NOT_FOUND")) { throw new Error("expected PROJECT_NOT_FOUND, got " + text); } console.log(text); })().catch((error) => { console.error(error); process.exit(1); }); `; const tsxBin = path.resolve("node_modules/.bin/tsx"); const { stdout } = await execFile(tsxBin, ["--eval", script], { cwd: process.cwd(), env: { ...process.env }, encoding: "utf8", maxBuffer: 1024 * 1024, }); assert.match(stdout, /PROJECT_NOT_FOUND/); }, ); test("GET /agent-controls 在未显式设置 BOSS_STATE_FILE 时仍可正常读取 controls", async () => { const runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-master-agent-default-state-")); const childEnv = { ...process.env }; delete childEnv.BOSS_STATE_FILE; const script = String.raw` import { NextRequest } from "next/server"; (async () => { const runtimeRoot = process.env.BOSS_RUNTIME_ROOT; if (!runtimeRoot) { throw new Error("missing BOSS_RUNTIME_ROOT"); } delete process.env.BOSS_STATE_FILE; const dataModule = await import("./src/lib/boss-data.ts"); const authModule = await import("./src/lib/boss-auth.ts"); const routeModule = await import("./src/app/api/v1/projects/[projectId]/agent-controls/route.ts"); const data = dataModule.default ?? dataModule; const auth = authModule.default ?? authModule; const route = routeModule.default ?? routeModule; await data.updateProjectAgentControls("master-agent", { modelOverride: "gpt-5.4", reasoningEffortOverride: "medium", }); const session = await data.createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); const response = await route.GET( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "GET", headers: { cookie: auth.AUTH_SESSION_COOKIE + "=" + session.sessionToken, }, }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); const payload = await response.json(); if (response.status !== 200) { throw new Error("expected 200, got " + response.status + ": " + JSON.stringify(payload)); } if (!payload.ok || payload.controls?.modelOverride !== "gpt-5.4" || payload.controls?.reasoningEffortOverride !== "medium") { throw new Error("unexpected payload: " + JSON.stringify(payload)); } console.log("OK"); })().catch((error) => { console.error(error); process.exit(1); }); `; const tsxBin = path.resolve("node_modules/.bin/tsx"); const { stdout } = await execFile(tsxBin, ["--eval", script], { cwd: process.cwd(), env: { ...childEnv, BOSS_RUNTIME_ROOT: runtimeRoot }, encoding: "utf8", maxBuffer: 1024 * 1024, }); assert.match(stdout, /OK/); }); test("GET /agent-controls rejects ordinary projects", async () => { await setup(); const session = await createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); const response = await getAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/boss-console/agent-controls", { method: "GET", headers: { cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, }, }), { params: Promise.resolve({ projectId: "boss-console" }) }, ); assert.equal(response.status, 404); assert.equal((await response.json()).message, "PROJECT_NOT_FOUND"); }); test("POST /agent-controls rejects unknown-key payload and preserves controls", async () => { await setup(); const session = await createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); await updateProjectAgentControls("master-agent", { modelOverride: "gpt-5.4", reasoningEffortOverride: "high", }); const beforeState = await readState(); const beforeProject = beforeState.projects.find((item) => item.id === "master-agent"); assert.ok(beforeProject, "expected seeded master-agent project"); const beforeUpdatedAt = beforeProject.updatedAt; const response = await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers: { "content-type": "application/json", cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, }, body: JSON.stringify({ unexpectedKey: "value", }), }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 400); assert.equal((await response.json()).message, "INVALID_AGENT_CONTROLS_PAYLOAD"); const controls = await getProjectAgentControls("master-agent"); assert.equal(controls?.modelOverride, "gpt-5.4"); assert.equal(controls?.reasoningEffortOverride, "high"); const afterState = await readState(); const afterProject = afterState.projects.find((item) => item.id === "master-agent"); assert.equal(afterProject?.updatedAt, beforeUpdatedAt); }); test("master-agent 对话控制 POST 会稳定拒绝非法 backendOverride", async () => { await setup(); const session = await createAuthSession({ account: "17600003315", role: "highest_admin", displayName: "Boss 超级管理员", loginMethod: "password", }); const response = await postAgentControlsRoute( new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", { method: "POST", headers: { "content-type": "application/json", cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`, }, body: JSON.stringify({ backendOverride: "bad-backend", }), }), { params: Promise.resolve({ projectId: "master-agent" }) }, ); assert.equal(response.status, 400); const payload = (await response.json()) as { ok: boolean; message?: string }; assert.equal(payload.ok, false); assert.equal(payload.message, "INVALID_BACKEND_OVERRIDE"); }); test("master-agent controls helper 不会写入普通项目", async () => { await setup(); await assert.rejects( () => updateProjectAgentControls("boss-console", { modelOverride: "gpt-5.4", reasoningEffortOverride: "low", }), /MASTER_AGENT_CONTROLS_SCOPE_RESTRICTED/, ); const state = await readState(); const project = state.projects.find((item) => item.id === "boss-console"); assert.equal(project?.agentControls, undefined); assert.equal(await getProjectAgentControls("boss-console"), null); });