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 专属菜单", () => {