feat: finish master-agent prompt and memory runtime

This commit is contained in:
kris
2026-04-01 04:56:07 +08:00
parent d316f0490e
commit ba01ae5393
19 changed files with 461 additions and 70 deletions

View File

@@ -171,6 +171,66 @@ test("master-agent 对话控制路由可读写并回显到项目详情", async (
assert.equal(projectPayload.agentControls?.reasoningEffortOverride, "medium");
});
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();
@@ -297,7 +357,7 @@ test("非 master-agent 项目详情不应回传 agentControls 字段", async ()
assert.equal(Object.prototype.hasOwnProperty.call(payload, "agentControls"), false);
});
test("master-agent 对话控制 POST 允许 highest_admin 修改", async () => {
test("master-agent 对话控制 POST 允许当前用户修改自己的 master-agent 会话配置", async () => {
await setup();
const session = await createAuthSession({
@@ -322,14 +382,19 @@ test("master-agent 对话控制 POST 仅允许 highest_admin 修改", async () =
{ params: Promise.resolve({ projectId: "master-agent" }) },
);
assert.equal(response.status, 403);
assert.equal(response.status, 200);
const payload = (await response.json()) as { ok: boolean; message: string };
assert.equal(payload.ok, false);
assert.equal(payload.message, "FORBIDDEN");
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");
assert.equal(controls, null);
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 () => {

View File

@@ -12,6 +12,8 @@ let getMasterAgentPromptPolicyRoute: typeof import("../src/app/api/v1/master-age
let getUserMasterPromptRoute: typeof import("../src/app/api/v1/master-agent/prompt/route");
let getUserMasterMemoriesRoute: typeof import("../src/app/api/v1/master-agent/memories/route");
let patchUserMasterMemoryRoute: typeof import("../src/app/api/v1/master-agent/memories/[memoryId]/route");
let getProjectMemoriesRoute: typeof import("../src/app/api/v1/projects/[projectId]/memories/route");
let getPromptProfileRoute: typeof import("../src/app/api/v1/projects/[projectId]/prompt-profile/route");
async function setup() {
if (runtimeRoot) return;
@@ -20,13 +22,15 @@ async function setup() {
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
const [data, auth, promptPolicyRoute, userPromptRoute, memoriesRoute, memoryRoute] = await Promise.all([
const [data, auth, promptPolicyRoute, userPromptRoute, memoriesRoute, memoryRoute, projectMemoriesRoute, promptProfileRoute] = await Promise.all([
import("../src/lib/boss-data.ts"),
import("../src/lib/boss-auth.ts"),
import("../src/app/api/v1/master-agent/prompt-policy/route.ts"),
import("../src/app/api/v1/master-agent/prompt/route.ts"),
import("../src/app/api/v1/master-agent/memories/route.ts"),
import("../src/app/api/v1/master-agent/memories/[memoryId]/route.ts"),
import("../src/app/api/v1/projects/[projectId]/memories/route.ts"),
import("../src/app/api/v1/projects/[projectId]/prompt-profile/route.ts"),
]);
createAuthSession = data.createAuthSession;
@@ -35,6 +39,8 @@ async function setup() {
getUserMasterPromptRoute = userPromptRoute;
getUserMasterMemoriesRoute = memoriesRoute;
patchUserMasterMemoryRoute = memoryRoute;
getProjectMemoriesRoute = projectMemoriesRoute.GET;
getPromptProfileRoute = promptProfileRoute.POST;
}
async function createAuthedRequest(account = "17600003315", role: "member" | "admin" | "highest_admin" = "highest_admin") {
@@ -129,3 +135,81 @@ test("master-agent prompt and memory routes support admin prompt, user prompt, a
);
assert.equal(patchResponse.status, 200);
});
test("master-agent 记忆页会返回当前用户所有项目记忆", async () => {
await setup();
const adminRequest = await createAuthedRequest();
await getUserMasterMemoriesRoute.POST(
new NextRequest("http://127.0.0.1:3000/api/v1/master-agent/memories", {
method: "POST",
headers: adminRequest.headers,
body: JSON.stringify({
scope: "project",
projectId: "boss-console",
title: "Boss 进度",
content: "Boss 项目聊天主链已接通。",
memoryType: "project_progress",
}),
}),
);
await getUserMasterMemoriesRoute.POST(
new NextRequest("http://127.0.0.1:3000/api/v1/master-agent/memories", {
method: "POST",
headers: adminRequest.headers,
body: JSON.stringify({
scope: "project",
projectId: "wenshenapp",
title: "纹身项目进度",
content: "wenshenapp 当前只保留一个主线程。",
memoryType: "project_progress",
}),
}),
);
const response = await getProjectMemoriesRoute(
new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/memories", {
method: "GET",
headers: adminRequest.headers,
}),
{ params: Promise.resolve({ projectId: "master-agent" }) },
);
assert.equal(response.status, 200);
const payload = (await response.json()) as {
ok: boolean;
memories: { project: Array<{ projectId?: string }> };
};
assert.equal(payload.ok, true);
assert.deepEqual(
payload.memories.project.map((memory) => memory.projectId).sort(),
["boss-console", "master-agent", "wenshenapp"].sort(),
);
});
test("prompt-profile 写入当前对话提示词时按当前账号隔离", async () => {
await setup();
const memberRequest = await createAuthedRequest("18800001111", "member");
const response = await getPromptProfileRoute(
new NextRequest("http://127.0.0.1:3000/api/v1/projects/master-agent/prompt-profile", {
method: "POST",
headers: memberRequest.headers,
body: JSON.stringify({
promptOverride: "成员自己的当前对话提示词",
}),
}),
{ params: Promise.resolve({ projectId: "master-agent" }) },
);
assert.equal(response.status, 200);
const payload = (await response.json()) as {
ok: boolean;
projectPromptOverride: string | null;
account: string;
};
assert.equal(payload.ok, true);
assert.equal(payload.account, "18800001111");
assert.equal(payload.projectPromptOverride, "成员自己的当前对话提示词");
});