feat: ship enterprise control and desktop governance
This commit is contained in:
@@ -9,6 +9,7 @@ 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 updateAiAccountHealth: (typeof import("../src/lib/boss-data"))["updateAiAccountHealth"];
|
||||
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"];
|
||||
@@ -32,6 +33,7 @@ async function setup() {
|
||||
POST = messageRoute.POST;
|
||||
saveAiAccount = data.saveAiAccount;
|
||||
updateProjectAgentControls = data.updateProjectAgentControls;
|
||||
updateAiAccountHealth = data.updateAiAccountHealth;
|
||||
readState = data.readState;
|
||||
createAuthSession = data.createAuthSession;
|
||||
appendProjectMessages = data.appendProjectMessages;
|
||||
@@ -40,7 +42,7 @@ async function setup() {
|
||||
|
||||
async function createAuthedRequest(projectId: string, body: unknown) {
|
||||
const session = await createAuthSession({
|
||||
account: "17600003315",
|
||||
account: "krisolo",
|
||||
role: "highest_admin",
|
||||
displayName: "Boss 超级管理员",
|
||||
loginMethod: "password",
|
||||
@@ -187,6 +189,7 @@ test("POST /api/v1/projects/master-agent/messages 快速返回队列态并在异
|
||||
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(masterProject?.unreadCount, 1);
|
||||
|
||||
assert.equal(fetchCalls.length, 1);
|
||||
assert.equal(fetchCalls[0]?.url, "https://api.openai.com/v1/responses");
|
||||
@@ -201,6 +204,54 @@ test("POST /api/v1/projects/master-agent/messages 快速返回队列态并在异
|
||||
}
|
||||
});
|
||||
|
||||
test("POST /api/v1/projects/master-agent/messages returns browser control task metadata for browser asks", async () => {
|
||||
await saveAiAccount({
|
||||
accountId: "openai-master-agent-browser-control",
|
||||
label: "API 容灾",
|
||||
role: "api_fallback",
|
||||
provider: "openai_api",
|
||||
displayName: "OpenAI API Browser Control",
|
||||
model: "gpt-5.4-mini",
|
||||
apiKey: "sk-test-openai-browser-control",
|
||||
enabled: true,
|
||||
setActive: true,
|
||||
loginStatusNote: "用于 browser control 测试。",
|
||||
});
|
||||
|
||||
const response = await POST(
|
||||
await createAuthedRequest("master-agent", {
|
||||
body: "打开 Chrome 去后台看一下订单页",
|
||||
}),
|
||||
{ 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;
|
||||
executionMode?: string;
|
||||
riskLevel?: string;
|
||||
requiresConfirmation?: boolean;
|
||||
};
|
||||
|
||||
assert.equal(payload.ok, true);
|
||||
assert.equal(payload.task?.taskType, "browser_control");
|
||||
assert.equal(payload.task?.status, "queued");
|
||||
assert.equal(payload.executionMode, "browser");
|
||||
assert.equal(payload.riskLevel, "medium");
|
||||
assert.equal(payload.requiresConfirmation, false);
|
||||
|
||||
const state = await readState();
|
||||
const task = state.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId);
|
||||
assert.equal(task?.taskType, "browser_control");
|
||||
assert.equal(task?.intentCategory, "browser_control");
|
||||
assert.equal(task?.runtimeKind, "browser-automation-runtime");
|
||||
assert.equal(task?.riskLevel, "medium");
|
||||
assert.equal(task?.confirmationPolicy, "light_confirm");
|
||||
assert.equal(task?.requiresUserConfirmation, undefined);
|
||||
});
|
||||
|
||||
test("POST /api/v1/projects/master-agent/messages 在快速反应模式下会对简单问题走同步快路径", async () => {
|
||||
await saveAiAccount({
|
||||
accountId: "openai-master-agent-fast-sync",
|
||||
@@ -359,6 +410,125 @@ test("POST /api/v1/projects/master-agent/messages 对模型状态类问题会本
|
||||
}
|
||||
});
|
||||
|
||||
test("POST /api/v1/projects/master-agent/messages 本地快反问候会保持职业经理人口吻且不调用模型", async () => {
|
||||
await saveAiAccount({
|
||||
accountId: "master-codex-primary-local-greeting",
|
||||
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 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 greeting 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 } | 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-mini");
|
||||
assert.match(payload.replyMessage?.body ?? "", /我在/);
|
||||
assert.match(payload.replyMessage?.body ?? "", /先给你结论/);
|
||||
assert.match(payload.replyMessage?.body ?? "", /需要我协调线程|需要我直接推进/);
|
||||
assert.doesNotMatch(payload.replyMessage?.body ?? "", /简单问题我会快速回复/);
|
||||
assert.equal(fetchCalled, false);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
test("POST /api/v1/projects/master-agent/messages 英文问候也走本地职业经理人快反", async () => {
|
||||
await saveAiAccount({
|
||||
accountId: "master-codex-primary-local-english-greeting",
|
||||
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 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 English greeting replies");
|
||||
}) as typeof fetch;
|
||||
|
||||
try {
|
||||
const response = await POST(
|
||||
await createAuthedRequest("master-agent", {
|
||||
body: "hello",
|
||||
}),
|
||||
{ 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 } | 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 ?? "", /我在/);
|
||||
assert.match(payload.replyMessage?.body ?? "", /先给你结论/);
|
||||
assert.equal(fetchCalled, false);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
test("POST /api/v1/projects/master-agent/messages 对可用模型查询会本地秒回并返回模式配置", async () => {
|
||||
await saveAiAccount({
|
||||
accountId: "hyzq-fast-local-list",
|
||||
@@ -475,7 +645,7 @@ test("POST /api/v1/projects/master-agent/messages 对深度思考切换请求会
|
||||
|
||||
const controls = await readState().then((state) =>
|
||||
state.userProjectAgentControls.find(
|
||||
(entry) => entry.projectId === "master-agent" && entry.account === "17600003315",
|
||||
(entry) => entry.projectId === "master-agent" && entry.account === "krisolo",
|
||||
)?.controls,
|
||||
);
|
||||
assert.equal(controls?.modelOverride, "gpt-5.4");
|
||||
@@ -898,6 +1068,57 @@ test("master-agent enqueue 在首选主节点离线时会回退到可用的备
|
||||
assert.equal(task?.deviceId, "mac-studio");
|
||||
});
|
||||
|
||||
test("master-agent enqueue 会继续使用设备在线但账号处于 degraded 的 Master Codex Node", async () => {
|
||||
await saveAiAccount({
|
||||
accountId: "master-codex-primary",
|
||||
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 updateAiAccountHealth({
|
||||
accountId: "master-codex-primary",
|
||||
status: "degraded",
|
||||
lastError: "MASTER_CODEX_NODE_EXEC_FAILED",
|
||||
lastValidatedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
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.masterReplyState, "queued");
|
||||
assert.equal(payload.masterReply?.accountId, "master-codex-primary");
|
||||
assert.equal(payload.task?.taskType, "conversation_reply");
|
||||
|
||||
const state = await readState();
|
||||
const task = state.masterAgentTasks.find((item) => item.taskId === payload.task?.taskId);
|
||||
assert.equal(task?.accountId, "master-codex-primary");
|
||||
assert.equal(task?.deviceId, "mac-studio");
|
||||
const account = state.aiAccounts.find((item) => item.accountId === "master-codex-primary");
|
||||
assert.equal(account?.status, "ready");
|
||||
const masterProject = state.projects.find((project) => project.id === "master-agent");
|
||||
assert.doesNotMatch(masterProject?.messages.at(-1)?.body ?? "", /当前没有可用的 master 节点账号/);
|
||||
});
|
||||
|
||||
test("master-agent enqueue 会在首个 API 候选失败后切到下一条备用链并重写任务账号", async () => {
|
||||
await saveAiAccount({
|
||||
accountId: "openai-primary-queue",
|
||||
|
||||
Reference in New Issue
Block a user