feat: add web master-agent chat menu
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
AppShell,
|
||||
ChatBubble,
|
||||
ChatComposer,
|
||||
MasterAgentChatMenu,
|
||||
MasterIdentityPill,
|
||||
PageNav,
|
||||
ProjectHeaderActions,
|
||||
@@ -45,7 +46,18 @@ export default async function ProjectChatPage({
|
||||
<PageNav
|
||||
title={detail.project.name}
|
||||
backHref="/conversations"
|
||||
rightNode={detail.masterIdentity ? <MasterIdentityPill identity={detail.masterIdentity} /> : undefined}
|
||||
rightNode={
|
||||
detail.masterIdentity ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<MasterIdentityPill identity={detail.masterIdentity} />
|
||||
{detail.project.id === "master-agent" ? (
|
||||
<MasterAgentChatMenu projectId={detail.project.id} />
|
||||
) : null}
|
||||
</div>
|
||||
) : detail.project.id === "master-agent" ? (
|
||||
<MasterAgentChatMenu projectId={detail.project.id} />
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
<div className="flex min-h-0 flex-1 flex-col px-[18px] pb-0">
|
||||
<div className="text-[12px] text-[#8C8C8C]">
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
popAppHistoryEntry,
|
||||
resolveAppBackAction,
|
||||
} from "@/lib/boss-app-client";
|
||||
import { getMasterAgentChatMenuItems } from "@/lib/master-agent-chat-menu";
|
||||
import {
|
||||
extractApprovedTargetProjectIds,
|
||||
summarizeDispatchPlan,
|
||||
@@ -810,6 +811,52 @@ export function MasterIdentityPill({ identity }: { identity: MasterIdentitySumma
|
||||
);
|
||||
}
|
||||
|
||||
export function MasterAgentChatMenu({ projectId }: { projectId: string }) {
|
||||
const router = useRouter();
|
||||
const items = getMasterAgentChatMenuItems(projectId);
|
||||
|
||||
if (items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<details className="relative">
|
||||
<summary
|
||||
className="flex h-9 w-9 list-none items-center justify-center rounded-full border border-[#E5E5EA] bg-white text-[18px] font-semibold leading-none text-[#57606A] shadow-sm marker:content-none"
|
||||
aria-label="更多"
|
||||
>
|
||||
…
|
||||
</summary>
|
||||
<div className="absolute right-0 top-11 z-20 min-w-[116px] rounded-2xl border border-[#E5E5EA] bg-white p-2 shadow-[0_12px_28px_rgba(15,23,42,0.14)]">
|
||||
{items.map((item) =>
|
||||
item.href ? (
|
||||
<Link
|
||||
key={item.key}
|
||||
href={item.href}
|
||||
className="flex rounded-xl px-3 py-2 text-[13px] text-[#111111] hover:bg-[#F7F8FA]"
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
key={item.key}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (item.action === "refresh") {
|
||||
router.refresh();
|
||||
}
|
||||
}}
|
||||
className="flex w-full rounded-xl px-3 py-2 text-left text-[13px] text-[#111111] hover:bg-[#F7F8FA]"
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</details>
|
||||
);
|
||||
}
|
||||
|
||||
type PendingDispatchPlanState = {
|
||||
planId: string;
|
||||
summary?: string;
|
||||
|
||||
@@ -326,7 +326,7 @@ export function MasterAgentPromptMemoryClient({
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 px-[18px] pb-6">
|
||||
<div className="rounded-2xl border border-[#E5E5EA] bg-white px-4 py-4">
|
||||
<div id="prompt-section" className="rounded-2xl border border-[#E5E5EA] bg-white px-4 py-4 scroll-mt-4">
|
||||
<div className="text-[16px] font-semibold text-[#111111]">主 Agent 提示词</div>
|
||||
<div className="mt-2 text-[12px] leading-6 text-[#8C8C8C]">
|
||||
管理员全局主提示词不可被覆盖;用户提示词和当前对话提示词只会追加在后面。
|
||||
@@ -453,7 +453,7 @@ export function MasterAgentPromptMemoryClient({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl border border-[#E5E5EA] bg-white px-4 py-4">
|
||||
<div id="memory-section" className="rounded-2xl border border-[#E5E5EA] bg-white px-4 py-4 scroll-mt-4">
|
||||
<div className="text-[16px] font-semibold text-[#111111]">新增记忆</div>
|
||||
<div className="mt-2 text-[12px] leading-6 text-[#8C8C8C]">
|
||||
支持自动沉淀后的手动增补、编辑和归档。项目记忆需要绑定到真实项目,而不是 master-agent 会话本身。
|
||||
|
||||
30
src/lib/master-agent-chat-menu.ts
Normal file
30
src/lib/master-agent-chat-menu.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export type MasterAgentChatMenuItem = {
|
||||
key: "prompt" | "memory" | "refresh";
|
||||
label: string;
|
||||
href?: string;
|
||||
action?: "refresh";
|
||||
};
|
||||
|
||||
export function getMasterAgentChatMenuItems(projectId: string): MasterAgentChatMenuItem[] {
|
||||
if (projectId !== "master-agent") {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
key: "prompt",
|
||||
label: "提示词",
|
||||
href: "/me/master-agent#prompt-section",
|
||||
},
|
||||
{
|
||||
key: "memory",
|
||||
label: "记忆",
|
||||
href: "/me/master-agent#memory-section",
|
||||
},
|
||||
{
|
||||
key: "refresh",
|
||||
label: "刷新",
|
||||
action: "refresh",
|
||||
},
|
||||
];
|
||||
}
|
||||
19
tests/master-agent-chat-menu.test.ts
Normal file
19
tests/master-agent-chat-menu.test.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { getMasterAgentChatMenuItems } from "../src/lib/master-agent-chat-menu";
|
||||
|
||||
test("master-agent 聊天页菜单包含提示词、记忆和刷新", () => {
|
||||
const items = getMasterAgentChatMenuItems("master-agent");
|
||||
assert.deepEqual(
|
||||
items.map((item) => item.key),
|
||||
["prompt", "memory", "refresh"],
|
||||
);
|
||||
assert.equal(items[0]?.href, "/me/master-agent#prompt-section");
|
||||
assert.equal(items[1]?.href, "/me/master-agent#memory-section");
|
||||
assert.equal(items[2]?.action, "refresh");
|
||||
});
|
||||
|
||||
test("普通会话不返回主 Agent 专属菜单", () => {
|
||||
const items = getMasterAgentChatMenuItems("boss-console");
|
||||
assert.deepEqual(items, []);
|
||||
});
|
||||
Reference in New Issue
Block a user