Files
boss/tests/master-agent-chat-controls.test.ts

1353 lines
47 KiB
TypeScript

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 saveAiAccount: (typeof import("../src/lib/boss-data"))["saveAiAccount"];
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;
saveAiAccount = data.saveAiAccount;
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();
});
async function ensureOrdinaryProject(projectId = "ordinary-project") {
await setup();
const state = await readState();
if (state.projects.some((item) => item.id === projectId)) {
return projectId;
}
state.projects.push({
id: projectId,
name: "普通项目线程",
pinned: false,
systemPinned: false,
deviceIds: ["mac-studio"],
preview: "普通项目测试线程。",
updatedAt: "2026-04-04T12:00:00+08:00",
lastMessageAt: "2026-04-04T12:00:00+08:00",
isGroup: false,
threadMeta: {
projectId,
threadId: `thread-${projectId}`,
threadDisplayName: "普通项目线程",
folderName: "普通项目",
activityIconCount: 0,
updatedAt: "2026-04-04T12:00:00+08:00",
codexThreadRef: `thread-${projectId}`,
codexFolderRef: projectId,
},
groupMembers: [],
createdByAgent: true,
collaborationMode: "development",
approvalState: "not_required",
unreadCount: 0,
riskLevel: "low",
messages: [],
goals: [],
versions: [],
});
await writeState(state);
return projectId;
}
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",
fastModelOverride: "gpt-5.4-mini",
fastReasoningEffortOverride: "low",
smartModelOverride: "gpt-5.4",
smartReasoningEffortOverride: "high",
});
const controls = await getProjectAgentControls("master-agent");
assert.equal(controls?.modelOverride, "gpt-5.4");
assert.equal(controls?.reasoningEffortOverride, "high");
assert.equal(controls?.fastModelOverride, "gpt-5.4-mini");
assert.equal(controls?.fastReasoningEffortOverride, "low");
assert.equal(controls?.smartModelOverride, "gpt-5.4");
assert.equal(controls?.smartReasoningEffortOverride, "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");
assert.equal(project?.agentControls?.fastModelOverride, "gpt-5.4-mini");
assert.equal(project?.agentControls?.fastReasoningEffortOverride, "low");
const detail = getProjectDetailView(state, "master-agent");
assert.equal(detail?.agentControls?.modelOverride, "gpt-5.4");
assert.equal(detail?.agentControls?.reasoningEffortOverride, "high");
assert.equal(detail?.agentControls?.smartModelOverride, "gpt-5.4");
assert.equal(detail?.agentControls?.smartReasoningEffortOverride, "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",
fastModelOverride: "gpt-5.4-mini",
fastReasoningEffortOverride: "low",
smartModelOverride: "gpt-5.4",
smartReasoningEffortOverride: "high",
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;
fastModelOverride?: string;
fastReasoningEffortOverride?: string;
smartModelOverride?: string;
smartReasoningEffortOverride?: 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?.fastModelOverride, "gpt-5.4-mini");
assert.equal(postPayload.controls?.fastReasoningEffortOverride, "low");
assert.equal(postPayload.controls?.smartModelOverride, "gpt-5.4");
assert.equal(postPayload.controls?.smartReasoningEffortOverride, "high");
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;
fastModelOverride?: string;
fastReasoningEffortOverride?: string;
smartModelOverride?: string;
smartReasoningEffortOverride?: 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?.fastModelOverride, "gpt-5.4-mini");
assert.equal(getPayload.controls?.fastReasoningEffortOverride, "low");
assert.equal(getPayload.controls?.smartModelOverride, "gpt-5.4");
assert.equal(getPayload.controls?.smartReasoningEffortOverride, "high");
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;
fastModelOverride?: string;
fastReasoningEffortOverride?: string;
smartModelOverride?: string;
smartReasoningEffortOverride?: 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?.fastModelOverride, "gpt-5.4-mini");
assert.equal(projectPayload.agentControls?.fastReasoningEffortOverride, "low");
assert.equal(projectPayload.agentControls?.smartModelOverride, "gpt-5.4");
assert.equal(projectPayload.agentControls?.smartReasoningEffortOverride, "high");
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 对话控制路由可读写 Hermes backendOverride", async () => {
await setup();
const tempDir = await mkdtemp(path.join(os.tmpdir(), "boss-hermes-agent-controls-"));
const scriptPath = path.join(tempDir, "hermes-runtime.mjs");
await writeFile(scriptPath, "console.log('ok');\n", "utf8");
const previousEnv = {
BOSS_HERMES_ENABLED: process.env.BOSS_HERMES_ENABLED,
BOSS_HERMES_COMMAND: process.env.BOSS_HERMES_COMMAND,
BOSS_HERMES_ARGS: process.env.BOSS_HERMES_ARGS,
BOSS_HERMES_WORKDIR: process.env.BOSS_HERMES_WORKDIR,
};
process.env.BOSS_HERMES_ENABLED = "true";
process.env.BOSS_HERMES_COMMAND = process.execPath;
process.env.BOSS_HERMES_ARGS = scriptPath;
process.env.BOSS_HERMES_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({
backendOverride: "hermes-runtime",
}),
}),
{ params: Promise.resolve({ projectId: "master-agent" }) },
);
assert.equal(postResponse.status, 200);
const postPayload = (await postResponse.json()) as {
ok: boolean;
controls: {
backendOverride?: string;
} | null;
hermesAvailability?: {
selectable?: boolean;
};
};
assert.equal(postPayload.ok, true);
assert.equal(postPayload.controls?.backendOverride, "hermes-runtime");
assert.equal(postPayload.hermesAvailability?.selectable, true);
const controls = await getProjectAgentControls("master-agent", "17600003315");
assert.equal(controls?.backendOverride, "hermes-runtime");
} finally {
process.env.BOSS_HERMES_ENABLED = previousEnv.BOSS_HERMES_ENABLED;
process.env.BOSS_HERMES_COMMAND = previousEnv.BOSS_HERMES_COMMAND;
process.env.BOSS_HERMES_ARGS = previousEnv.BOSS_HERMES_ARGS;
process.env.BOSS_HERMES_WORKDIR = previousEnv.BOSS_HERMES_WORKDIR;
await rm(tempDir, { recursive: true, force: true });
}
});
test("普通线程对话控制路由可读写 Hermes backendOverride", async () => {
await setup();
const projectId = await ensureOrdinaryProject("ordinary-hermes-project");
const tempDir = await mkdtemp(path.join(os.tmpdir(), "boss-hermes-thread-controls-"));
const scriptPath = path.join(tempDir, "hermes-runtime.mjs");
await writeFile(scriptPath, "console.log('ok');\n", "utf8");
const previousEnv = {
BOSS_HERMES_ENABLED: process.env.BOSS_HERMES_ENABLED,
BOSS_HERMES_COMMAND: process.env.BOSS_HERMES_COMMAND,
BOSS_HERMES_ARGS: process.env.BOSS_HERMES_ARGS,
BOSS_HERMES_WORKDIR: process.env.BOSS_HERMES_WORKDIR,
};
process.env.BOSS_HERMES_ENABLED = "true";
process.env.BOSS_HERMES_COMMAND = process.execPath;
process.env.BOSS_HERMES_ARGS = scriptPath;
process.env.BOSS_HERMES_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/${projectId}/agent-controls`, {
method: "POST",
headers,
body: JSON.stringify({
backendOverride: "hermes-runtime",
}),
}),
{ params: Promise.resolve({ projectId }) },
);
assert.equal(postResponse.status, 200);
const postPayload = (await postResponse.json()) as {
ok: boolean;
controls: {
backendOverride?: string;
} | null;
hermesAvailability?: {
selectable?: boolean;
};
};
assert.equal(postPayload.ok, true);
assert.equal(postPayload.controls?.backendOverride, "hermes-runtime");
assert.equal(postPayload.hermesAvailability?.selectable, true);
const controls = await getProjectAgentControls(projectId, "17600003315");
assert.equal(controls?.backendOverride, "hermes-runtime");
const projectResponse = await getProjectRoute(
new NextRequest(`http://127.0.0.1:3000/api/v1/projects/${projectId}`, {
method: "GET",
headers,
}),
{ params: Promise.resolve({ projectId }) },
);
assert.equal(projectResponse.status, 200);
const projectPayload = (await projectResponse.json()) as {
ok: boolean;
agentControls: {
backendOverride?: string;
} | null;
};
assert.equal(projectPayload.ok, true);
assert.equal(projectPayload.agentControls?.backendOverride, "hermes-runtime");
} finally {
process.env.BOSS_HERMES_ENABLED = previousEnv.BOSS_HERMES_ENABLED;
process.env.BOSS_HERMES_COMMAND = previousEnv.BOSS_HERMES_COMMAND;
process.env.BOSS_HERMES_ARGS = previousEnv.BOSS_HERMES_ARGS;
process.env.BOSS_HERMES_WORKDIR = previousEnv.BOSS_HERMES_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 对话控制 GET 会返回当前可用模型与预设模型清单", async () => {
await setup();
await saveAiAccount({
accountId: "openai-model-catalog",
label: "OpenAI 主账号",
role: "primary",
provider: "openai_api",
displayName: "OpenAI 主账号",
model: "gpt-5.4-mini",
apiKey: "sk-openai-model-catalog",
enabled: true,
setActive: true,
loginStatusNote: "用于模型目录测试。",
});
await saveAiAccount({
accountId: "qwen-model-catalog",
label: "Qwen 备用",
role: "backup",
provider: "aliyun_qwen_api",
displayName: "Qwen 备用",
model: "qwen3.5-plus",
apiKey: "sk-qwen-model-catalog",
enabled: true,
setActive: false,
loginStatusNote: "用于模型目录测试。",
});
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/master-agent/agent-controls", {
method: "GET",
headers: {
cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`,
},
}),
{ params: Promise.resolve({ projectId: "master-agent" }) },
);
assert.equal(response.status, 200);
const payload = (await response.json()) as {
ok: boolean;
modelCatalog?: {
availableModels?: string[];
selectableModels?: string[];
presetModels?: string[];
};
};
assert.equal(payload.ok, true);
assert.deepEqual(payload.modelCatalog?.availableModels, ["gpt-5.4-mini", "qwen3.5-plus"]);
assert.ok(payload.modelCatalog?.selectableModels?.includes("gpt-5.4"));
assert.ok(payload.modelCatalog?.selectableModels?.includes("gpt-5.4-mini"));
assert.ok(payload.modelCatalog?.selectableModels?.includes("gpt-4.1"));
assert.ok(payload.modelCatalog?.selectableModels?.includes("gpt-4.1-mini"));
assert.ok(payload.modelCatalog?.selectableModels?.includes("qwen3.5-plus"));
assert.deepEqual(payload.modelCatalog?.presetModels, [
"gpt-5.4",
"gpt-5.4-mini",
"gpt-4.1",
"gpt-4.1-mini",
"qwen3.5-plus",
]);
});
test("全局接管默认会透传到普通线程会话详情", async () => {
await setup();
const projectId = await ensureOrdinaryProject("ordinary-takeover-project");
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 response = await postAgentControlsRoute(
new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/agent-controls", {
method: "POST",
headers,
body: JSON.stringify({
globalTakeoverEnabled: true,
}),
}),
{ params: Promise.resolve({ projectId: "master-agent" }) },
);
assert.equal(response.status, 200);
const projectResponse = await getProjectRoute(
new NextRequest(`http://127.0.0.1:3000/api/v1/projects/${projectId}`, {
method: "GET",
headers,
}),
{ params: Promise.resolve({ projectId }) },
);
assert.equal(projectResponse.status, 200);
const payload = (await projectResponse.json()) as {
ok: boolean;
agentControls: {
effectiveTakeoverEnabled?: boolean;
takeoverInheritedFromGlobal?: boolean;
updatedAt?: string;
} | null;
};
assert.equal(payload.ok, true);
assert.equal(payload.agentControls?.effectiveTakeoverEnabled, true);
assert.equal(payload.agentControls?.takeoverInheritedFromGlobal, true);
});
test("普通线程会话可以单独关闭主 Agent 协同接管并覆盖全局默认", async () => {
await setup();
const projectId = await ensureOrdinaryProject("ordinary-project-override");
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({
globalTakeoverEnabled: true,
}),
}),
{ params: Promise.resolve({ projectId: "master-agent" }) },
);
const response = await postAgentControlsRoute(
new NextRequest(`http://127.0.0.1:3000/api/v1/projects/${projectId}/agent-controls`, {
method: "POST",
headers,
body: JSON.stringify({
takeoverEnabled: false,
}),
}),
{ params: Promise.resolve({ projectId }) },
);
assert.equal(response.status, 200);
const detail = getProjectDetailView(await readState(), projectId, "17600003315");
assert.equal(detail?.agentControls?.effectiveTakeoverEnabled, false);
assert.equal(detail?.agentControls?.takeoverInheritedFromGlobal, false);
});
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("普通线程项目详情会回传接管控制占位", async () => {
await setup();
const ordinaryProjectId = await ensureOrdinaryProject();
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/${ordinaryProjectId}`, {
method: "GET",
headers: {
cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`,
},
}),
{ params: Promise.resolve({ projectId: ordinaryProjectId }) },
);
assert.equal(response.status, 200);
const payload = (await response.json()) as Record<string, unknown>;
assert.equal(payload.ok, true);
assert.equal(Object.prototype.hasOwnProperty.call(payload, "agentControls"), true);
assert.equal(payload.agentControls, null);
});
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 supports ordinary projects for takeover settings", async () => {
await setup();
const ordinaryProjectId = await ensureOrdinaryProject();
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/${ordinaryProjectId}/agent-controls`, {
method: "GET",
headers: {
cookie: `${AUTH_SESSION_COOKIE}=${session.sessionToken}`,
},
}),
{ params: Promise.resolve({ projectId: ordinaryProjectId }) },
);
assert.equal(response.status, 200);
assert.equal((await response.json()).controls, null);
});
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 专属控制字段", async () => {
await setup();
const ordinaryProjectId = await ensureOrdinaryProject();
await assert.rejects(
() =>
updateProjectAgentControls(ordinaryProjectId, {
modelOverride: "gpt-5.4",
reasoningEffortOverride: "low",
}),
/PROJECT_AGENT_CONTROLS_SCOPE_RESTRICTED/,
);
const state = await readState();
const project = state.projects.find((item) => item.id === ordinaryProjectId);
assert.equal(project?.agentControls, undefined);
assert.equal(await getProjectAgentControls(ordinaryProjectId), null);
});