diff --git a/src/app/me/master-agent/takeover/page.tsx b/src/app/me/master-agent/takeover/page.tsx
new file mode 100644
index 0000000..2b77ba5
--- /dev/null
+++ b/src/app/me/master-agent/takeover/page.tsx
@@ -0,0 +1,29 @@
+import { AppShell, PageNav, StatusBar } from "@/components/app-ui";
+import { MasterAgentTakeoverClient } from "@/components/master-agent-takeover-client";
+import { requirePageSession } from "@/lib/boss-auth";
+import { getProjectAgentControls } from "@/lib/boss-data";
+import { formatTimestampLabel } from "@/lib/boss-projections";
+
+export const dynamic = "force-dynamic";
+
+export default async function MasterAgentTakeoverPage() {
+ const session = await requirePageSession();
+ const projectControls = await getProjectAgentControls("master-agent", session.account);
+
+ return (
+
+
+
+
+
+ 全局接管单独放在这里,不再混进提示词设置。当前登录账号:
+ {session.account}
+
+
+
+
+ );
+}
diff --git a/src/components/master-agent-takeover-client.tsx b/src/components/master-agent-takeover-client.tsx
new file mode 100644
index 0000000..99abf24
--- /dev/null
+++ b/src/components/master-agent-takeover-client.tsx
@@ -0,0 +1,84 @@
+"use client";
+
+import { useState } from "react";
+import { useRouter } from "next/navigation";
+
+type Props = {
+ enabled: boolean;
+ updatedAt?: string | null;
+};
+
+export function MasterAgentTakeoverClient({ enabled, updatedAt }: Props) {
+ const router = useRouter();
+ const [takeoverEnabled, setTakeoverEnabled] = useState(enabled);
+ const [busy, setBusy] = useState(false);
+ const [message, setMessage] = useState("");
+
+ async function save() {
+ setBusy(true);
+ setMessage("");
+
+ const response = await fetch("/api/v1/projects/master-agent/agent-controls", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ globalTakeoverEnabled: takeoverEnabled }),
+ });
+ const result = (await response.json()) as { ok: boolean; message?: string };
+
+ setBusy(false);
+ setMessage(
+ result.ok
+ ? takeoverEnabled
+ ? "已开启全局主 Agent 协同接管。"
+ : "已关闭全局主 Agent 协同接管。"
+ : result.message ?? "保存失败。",
+ );
+
+ if (result.ok) {
+ router.refresh();
+ }
+ }
+
+ return (
+
+
+
+
+
全局主 Agent 协同接管
+
+ 这是全局默认开关。开启后,新线程默认允许主 Agent 协同推进,但不会抢走你继续直接控制线程开发的能力。
+
+
+ 某个线程如果单独关闭了协同接管,会优先按线程自己的开关处理。
+
+
+
+
+
+
+ {updatedAt ? 最近更新:{updatedAt} : null}
+
+
+ {message ? (
+
+ {message}
+
+ ) : null}
+
+ );
+}
diff --git a/src/lib/master-agent-chat-menu.ts b/src/lib/master-agent-chat-menu.ts
index d513b6a..5eec85f 100644
--- a/src/lib/master-agent-chat-menu.ts
+++ b/src/lib/master-agent-chat-menu.ts
@@ -5,10 +5,12 @@ export const MASTER_AGENT_CHAT_PAGE_ANCHORS = {
memory: "/me/master-agent#memory-section",
} as const;
+export const MASTER_AGENT_TAKEOVER_PAGE_HREF = "/me/master-agent/takeover";
+
export type MasterAgentChatPageAnchors = typeof MASTER_AGENT_CHAT_PAGE_ANCHORS;
export type MasterAgentChatMenuItem = {
- key: "prompt" | "model" | "reasoning_effort" | "memory" | "refresh";
+ key: "prompt" | "model" | "reasoning_effort" | "takeover" | "memory" | "refresh";
label: string;
href?: string;
action?: "refresh";
@@ -20,11 +22,6 @@ export function getMasterAgentChatMenuItems(projectId: string): MasterAgentChatM
}
return [
- {
- key: "prompt",
- label: "提示词",
- href: MASTER_AGENT_CHAT_PAGE_ANCHORS.prompt,
- },
{
key: "model",
label: "模型",
@@ -35,6 +32,16 @@ export function getMasterAgentChatMenuItems(projectId: string): MasterAgentChatM
label: "推理强度",
href: MASTER_AGENT_CHAT_PAGE_ANCHORS.reasoningEffort,
},
+ {
+ key: "takeover",
+ label: "全局接管",
+ href: MASTER_AGENT_TAKEOVER_PAGE_HREF,
+ },
+ {
+ key: "prompt",
+ label: "提示词",
+ href: MASTER_AGENT_CHAT_PAGE_ANCHORS.prompt,
+ },
{
key: "memory",
label: "记忆",
diff --git a/tests/master-agent-chat-menu.test.ts b/tests/master-agent-chat-menu.test.ts
index 342837a..c0145e8 100644
--- a/tests/master-agent-chat-menu.test.ts
+++ b/tests/master-agent-chat-menu.test.ts
@@ -2,17 +2,18 @@ import test from "node:test";
import assert from "node:assert/strict";
import { getMasterAgentChatMenuItems } from "../src/lib/master-agent-chat-menu";
-test("master-agent 聊天页菜单包含提示词、模型、推理强度、记忆和刷新", () => {
+test("master-agent 聊天页菜单包含全局接管、提示词、模型、推理强度、记忆和刷新", () => {
const items = getMasterAgentChatMenuItems("master-agent");
assert.deepEqual(
items.map((item) => item.key),
- ["prompt", "model", "reasoning_effort", "memory", "refresh"],
+ ["model", "reasoning_effort", "takeover", "prompt", "memory", "refresh"],
);
- assert.equal(items[0]?.href, "/me/master-agent#prompt-section");
- assert.equal(items[1]?.href, "/me/master-agent#model-section");
- assert.equal(items[2]?.href, "/me/master-agent#reasoning-effort-section");
- assert.equal(items[3]?.href, "/me/master-agent#memory-section");
- assert.equal(items[4]?.action, "refresh");
+ assert.equal(items[0]?.href, "/me/master-agent#model-section");
+ assert.equal(items[1]?.href, "/me/master-agent#reasoning-effort-section");
+ assert.equal(items[2]?.href, "/me/master-agent/takeover");
+ assert.equal(items[3]?.href, "/me/master-agent#prompt-section");
+ assert.equal(items[4]?.href, "/me/master-agent#memory-section");
+ assert.equal(items[5]?.action, "refresh");
});
test("普通会话不返回主 Agent 专属菜单", () => {