智能体分权

This commit is contained in:
吉鑫升 committed 2026-05-13 11:36:17 +08:00
1 parent e7a455f22e
commit cf2e4bdd9c
31 files changed
+1237 -436

No files matched your search

+2 -2
View File
@@ -134,11 +134,11 @@ export function App() {
}
/>
<Route
path="/workspace/agents/:agent_name/chats"
path="/workspace/agents/:agent_id/chats"
element={<Navigate to="/workspace/agents" replace />}
/>
<Route
path="/workspace/agents/:agent_name/chats/:thread_id"
path="/workspace/agents/:agent_id/chats/:thread_id"
element={
<WorkspaceLayout>
<ChatRuntime>
@@ -1,8 +1,15 @@
"use client";
import { BotIcon, MessageSquareIcon, SquarePenIcon, Trash2Icon } from "lucide-react";
import {
BotIcon,
EyeIcon,
EyeOffIcon,
MessageSquareIcon,
SquarePenIcon,
Trash2Icon,
} from "lucide-react";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { toast } from "sonner";
import { Badge } from "@/components/ui/badge";
@@ -24,9 +31,11 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import { useDeleteAgent, useUpdateAgent } from "@/core/agents";
import type { Agent } from "@/core/agents";
import { getStoredAuth } from "@/core/auth";
import { useI18n } from "@/core/i18n/hooks";
import { useSkills } from "@/core/skills/hooks";
@@ -42,28 +51,45 @@ export function AgentCard({ agent }: AgentCardProps) {
const { skills, isLoading: isSkillsLoading } = useSkills();
const [deleteOpen, setDeleteOpen] = useState(false);
const [editOpen, setEditOpen] = useState(false);
const [editName, setEditName] = useState(agent.name);
const [editDescription, setEditDescription] = useState(agent.description ?? "");
const [editSoul, setEditSoul] = useState(agent.soul ?? "");
const [editPublished, setEditPublished] = useState(agent.published);
const [selectedSkills, setSelectedSkills] = useState<string[]>(
agent.skills ?? [],
);
const currentUserId = useMemo(() => getStoredAuth()?.user_id ?? null, []);
const isBuiltin = agent.builtin;
const isOwned =
!isBuiltin && agent.user_id !== null && agent.user_id === currentUserId;
const isFromOthers = !isBuiltin && !isOwned;
useEffect(() => {
if (!editOpen) {
return;
}
setEditName(agent.name);
setEditDescription(agent.description ?? "");
setEditSoul(agent.soul ?? "");
setEditPublished(agent.published);
setSelectedSkills(agent.skills ?? []);
}, [agent.description, agent.skills, agent.soul, editOpen]);
}, [
agent.description,
agent.name,
agent.published,
agent.skills,
agent.soul,
editOpen,
]);
function handleChat() {
router.push(`/workspace/agents/${agent.name}/chats/new`);
router.push(`/workspace/agents/${agent.id}/chats/new`);
}
async function handleDelete() {
try {
await deleteAgent.mutateAsync(agent.name);
await deleteAgent.mutateAsync(agent.id);
toast.success(t.agents.deleteSuccess);
setDeleteOpen(false);
} catch (err) {
@@ -72,12 +98,19 @@ export function AgentCard({ agent }: AgentCardProps) {
}
async function handleSaveEdit() {
const trimmedName = editName.trim();
if (!trimmedName) {
toast.error(t.agents.nameStepInvalidError ?? "名称不能为空");
return;
}
try {
await updateAgent.mutateAsync({
name: agent.name,
id: agent.id,
request: {
name: trimmedName,
description: editDescription,
soul: editSoul,
published: editPublished,
skills: selectedSkills,
},
});
@@ -88,6 +121,18 @@ export function AgentCard({ agent }: AgentCardProps) {
}
}
async function handleTogglePublished() {
try {
await updateAgent.mutateAsync({
id: agent.id,
request: { published: !agent.published },
});
toast.success(agent.published ? "已取消发布" : "已发布");
} catch (err) {
toast.error(err instanceof Error ? err.message : String(err));
}
}
function toggleSkill(skillName: string) {
setSelectedSkills((prev) => {
if (prev.includes(skillName)) {
@@ -119,6 +164,39 @@ export function AgentCard({ agent }: AgentCardProps) {
)}
</div>
</div>
<div className="flex shrink-0 items-center gap-1">
{isBuiltin && (
<Badge variant="secondary" className="text-xs">
内置
</Badge>
)}
{isOwned && (
<Badge
variant="default"
className="bg-emerald-500/15 text-emerald-700 hover:bg-emerald-500/15 dark:text-emerald-300 text-xs"
>
我的
</Badge>
)}
{isFromOthers && (
<Badge
variant="outline"
className="border-blue-500/30 text-blue-700 dark:text-blue-300 text-xs"
title={agent.user_id ? `来自用户 ${agent.user_id}` : "已发布"}
>
来自他人
</Badge>
)}
{isOwned && agent.published && (
<Badge
variant="outline"
className="border-sky-500/30 text-sky-700 dark:text-sky-300 text-xs"
title="该智能体已对所有用户可见"
>
已发布
</Badge>
)}
</div>
</div>
{agent.description && (
<CardDescription className="mt-2 line-clamp-2 text-sm">
@@ -157,41 +235,61 @@ export function AgentCard({ agent }: AgentCardProps) {
<MessageSquareIcon className="mr-1.5 h-3.5 w-3.5" />
{t.agents.chat}
</Button>
<div className="flex gap-1">
<Button
size="icon"
variant="ghost"
className="h-8 w-8 shrink-0"
onClick={() => setEditOpen(true)}
title="编辑智能体"
>
<SquarePenIcon className="h-3.5 w-3.5" />
</Button>
<Button
size="icon"
variant="ghost"
className="text-destructive hover:text-destructive h-8 w-8 shrink-0"
onClick={() => setDeleteOpen(true)}
title={t.agents.delete}
>
<Trash2Icon className="h-3.5 w-3.5" />
</Button>
</div>
{isOwned && (
<div className="flex gap-1">
<Button
size="icon"
variant="ghost"
className="h-8 w-8 shrink-0"
onClick={() => void handleTogglePublished()}
disabled={updateAgent.isPending}
title={agent.published ? "取消发布" : "发布"}
>
{agent.published ? (
<EyeOffIcon className="h-3.5 w-3.5" />
) : (
<EyeIcon className="h-3.5 w-3.5" />
)}
</Button>
<Button
size="icon"
variant="ghost"
className="h-8 w-8 shrink-0"
onClick={() => setEditOpen(true)}
title="编辑智能体"
>
<SquarePenIcon className="h-3.5 w-3.5" />
</Button>
<Button
size="icon"
variant="ghost"
className="text-destructive hover:text-destructive h-8 w-8 shrink-0"
onClick={() => setDeleteOpen(true)}
title={t.agents.delete}
>
<Trash2Icon className="h-3.5 w-3.5" />
</Button>
</div>
)}
</CardFooter>
</Card>
<Dialog open={editOpen} onOpenChange={setEditOpen}>
<DialogContent className="max-h-[85vh] overflow-y-auto sm:max-w-2xl">
<DialogHeader>
<DialogTitle>编辑智能体:{agent.name}</DialogTitle>
<DialogTitle>编辑智能体:{agent.name}</DialogTitle>
<DialogDescription>
可以修改描述、技能白名单和 SOUL 内容。
可以修改名称、描述、技能白名单、SOUL 内容与发布状态。
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-2">
<div className="space-y-2">
<label className="text-sm font-medium">名称</label>
<Input value={agent.name} disabled />
<Input
value={editName}
onChange={(e) => setEditName(e.target.value)}
placeholder="输入智能体名称(允许中文)"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">描述</label>
@@ -238,7 +336,7 @@ export function AgentCard({ agent }: AgentCardProps) {
)}
</div>
<p className="text-muted-foreground text-xs">
不选任何技能会保存为无技能(skills = [])。
不选任何技能会保存为无技能(skills = [])。
</p>
</div>
<div className="space-y-2">
@@ -250,6 +348,18 @@ export function AgentCard({ agent }: AgentCardProps) {
className="min-h-40 font-mono text-xs"
/>
</div>
<div className="flex items-center justify-between rounded-md border p-3">
<div>
<p className="text-sm font-medium">公开发布</p>
<p className="text-muted-foreground text-xs">
打开后,该智能体对所有用户可见。
</p>
</div>
<Switch
checked={editPublished}
onCheckedChange={setEditPublished}
/>
</div>
</div>
<DialogFooter>
<Button
@@ -59,7 +59,7 @@ export function AgentGallery() {
) : (
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{agents.map((agent) => (
<AgentCard key={agent.name} agent={agent} />
<AgentCard key={agent.id} agent={agent} />
))}
</div>
)}
@@ -62,10 +62,10 @@ export function RecentChatList() {
const { t } = useI18n();
const router = useRouter();
const pathname = usePathname();
const { thread_id: threadIdFromPath, agent_name: agentNameFromPath } =
const { thread_id: threadIdFromPath, agent_id: agentIdFromPath } =
useParams<{
thread_id: string;
agent_name?: string;
agent_id?: string;
}>();
const { data: threads = [] } = useThreads();
const { mutate: deleteThread } = useDeleteThread();
@@ -82,7 +82,7 @@ export function RecentChatList() {
if (threadId === threadIdFromPath) {
const threadIndex = threads.findIndex((t) => t.thread_id === threadId);
let nextThreadPath = pathOfThread("new", {
agent_name: agentNameFromPath,
agent_id: agentIdFromPath,
});
if (threadIndex > -1) {
if (threads[threadIndex + 1]) {
@@ -94,7 +94,7 @@ export function RecentChatList() {
void router.push(nextThreadPath);
}
},
[agentNameFromPath, deleteThread, router, threadIdFromPath, threads],
[agentIdFromPath, deleteThread, router, threadIdFromPath, threads],
);
const handleRenameClick = useCallback(
+7 -7
View File
@@ -22,9 +22,9 @@ export async function listAgents(): Promise<Agent[]> {
return data.agents;
}
export async function getAgent(name: string): Promise<Agent> {
const res = await apiFetch(`${getBackendBaseURL()}/api/agents/${name}`);
if (!res.ok) throw new Error(`Agent '${name}' not found`);
export async function getAgent(id: string): Promise<Agent> {
const res = await apiFetch(`${getBackendBaseURL()}/api/agents/${id}`);
if (!res.ok) throw new Error(`Agent '${id}' not found`);
return res.json() as Promise<Agent>;
}
@@ -42,10 +42,10 @@ export async function createAgent(request: CreateAgentRequest): Promise<Agent> {
}
export async function updateAgent(
name: string,
id: string,
request: UpdateAgentRequest,
): Promise<Agent> {
const res = await apiFetch(`${getBackendBaseURL()}/api/agents/${name}`, {
const res = await apiFetch(`${getBackendBaseURL()}/api/agents/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
@@ -57,8 +57,8 @@ export async function updateAgent(
return res.json() as Promise<Agent>;
}
export async function deleteAgent(name: string): Promise<void> {
const res = await apiFetch(`${getBackendBaseURL()}/api/agents/${name}`, {
export async function deleteAgent(id: string): Promise<void> {
const res = await apiFetch(`${getBackendBaseURL()}/api/agents/${id}`, {
method: "DELETE",
});
if (!res.ok) throw new Error(`Failed to delete agent: ${res.statusText}`);
+10 -10
View File
@@ -17,11 +17,11 @@ export function useAgents() {
return { agents: data ?? [], isLoading, error };
}
export function useAgent(name: string | null | undefined) {
export function useAgent(id: string | null | undefined) {
const { data, isLoading, error } = useQuery({
queryKey: ["agents", name],
queryFn: () => getAgent(name!),
enabled: !!name,
queryKey: ["agents", id],
queryFn: () => getAgent(id!),
enabled: !!id,
});
return { agent: data ?? null, isLoading, error };
}
@@ -40,15 +40,15 @@ export function useUpdateAgent() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({
name,
id,
request,
}: {
name: string;
id: string;
request: UpdateAgentRequest;
}) => updateAgent(name, request),
onSuccess: (_data, { name }) => {
}) => updateAgent(id, request),
onSuccess: (_data, { id }) => {
void queryClient.invalidateQueries({ queryKey: ["agents"] });
void queryClient.invalidateQueries({ queryKey: ["agents", name] });
void queryClient.invalidateQueries({ queryKey: ["agents", id] });
},
});
}
@@ -56,7 +56,7 @@ export function useUpdateAgent() {
export function useDeleteAgent() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (name: string) => deleteAgent(name),
mutationFn: (id: string) => deleteAgent(id),
onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: ["agents"] });
},
+16 -7
View File
@@ -1,25 +1,34 @@
export interface Agent {
id: string;
name: string;
description: string;
model: string | null;
tool_groups: string[] | null;
skills: string[] | null;
user_id: string | null;
published: boolean;
builtin: boolean;
created_at?: string | null;
updated_at?: string | null;
soul?: string | null;
model?: string | null;
tool_groups?: string[] | null;
skills?: string[] | null;
}
export interface CreateAgentRequest {
name: string;
description?: string;
soul?: string;
published?: boolean;
skills?: string[] | null;
model?: string | null;
tool_groups?: string[] | null;
skills?: string[] | null;
soul?: string;
}
export interface UpdateAgentRequest {
name?: string | null;
description?: string | null;
soul?: string | null;
published?: boolean | null;
skills?: string[] | null;
model?: string | null;
tool_groups?: string[] | null;
skills?: string[] | null;
soul?: string | null;
}
+6 -3
View File
@@ -28,6 +28,7 @@ export type ThreadStreamOptions = {
threadId?: string | null | undefined;
context: LocalSettings["context"];
isMock?: boolean;
assistantId?: string;
onStart?: (threadId: string) => void;
onFinish?: (state: AgentThreadState) => void;
onToolEnd?: (event: ToolEndEvent) => void;
@@ -139,6 +140,7 @@ export function useThreadStream({
threadId,
context,
isMock,
assistantId,
onStart,
onFinish,
onToolEnd,
@@ -204,7 +206,7 @@ export function useThreadStream({
const thread = useStream<AgentThreadState>({
client: getAPIClient(isMock),
assistantId: "lead_agent",
assistantId: assistantId ?? "lead_agent",
threadId: onStreamThreadId,
reconnectOnMount: runMetadataStorageRef.current
? () => runMetadataStorageRef.current!
@@ -213,10 +215,11 @@ export function useThreadStream({
onCreated(meta) {
handleStreamStart(meta.thread_id);
setOnStreamThreadId(meta.thread_id);
if (context.agent_name && !isMock) {
const metadataAgentId = context.agent_id ?? context.agent_name;
if (metadataAgentId && !isMock) {
void getAPIClient()
.threads.update(meta.thread_id, {
metadata: { agent_name: context.agent_name },
metadata: { agent_id: metadataAgentId },
})
.catch(() => ({}));
}
+2
View File
@@ -16,6 +16,8 @@ export interface AgentThreadContext extends Record<string, unknown> {
is_plan_mode: boolean;
subagent_enabled: boolean;
reasoning_effort?: "minimal" | "low" | "medium" | "high";
agent_id?: string;
/** @deprecated kept for reading legacy thread metadata; prefer agent_id */
agent_name?: string;
}
+23 -16
View File
@@ -2,34 +2,41 @@ import type { Message } from "@langchain/langgraph-sdk";
import type { AgentThread, AgentThreadContext } from "./types";
type ThreadAgentRef = Pick<AgentThreadContext, "agent_id" | "agent_name">;
type ThreadRouteTarget =
| string
| {
thread_id: string;
context?: Pick<AgentThreadContext, "agent_name"> | null;
context?: ThreadAgentRef | null;
metadata?: Record<string, unknown> | null;
};
function pickAgentRef(
context: ThreadAgentRef | null | undefined,
metadata: Record<string, unknown> | null | undefined,
): string | undefined {
if (context?.agent_id) return context.agent_id;
if (context?.agent_name) return context.agent_name;
const metaId = metadata?.agent_id;
if (typeof metaId === "string" && metaId) return metaId;
const metaName = metadata?.agent_name;
if (typeof metaName === "string" && metaName) return metaName;
return undefined;
}
export function pathOfThread(
thread: ThreadRouteTarget,
context?: Pick<AgentThreadContext, "agent_name"> | null,
context?: ThreadAgentRef | null,
) {
const threadId = typeof thread === "string" ? thread : thread.thread_id;
let agentName: string | undefined;
if (typeof thread === "string") {
agentName = context?.agent_name;
} else {
agentName = thread.context?.agent_name;
if (!agentName) {
const metaAgent = thread.metadata?.agent_name;
if (typeof metaAgent === "string") {
agentName = metaAgent;
}
}
}
const agentRef =
typeof thread === "string"
? pickAgentRef(context, null)
: pickAgentRef(thread.context, thread.metadata);
return agentName
? `/workspace/agents/${encodeURIComponent(agentName)}/chats/${threadId}`
return agentRef
? `/workspace/agents/${encodeURIComponent(agentRef)}/chats/${threadId}`
: `/workspace/chats/${threadId}`;
}
+11 -10
View File
@@ -37,11 +37,11 @@ export default function AgentChatPage() {
const [hasStartedConversation, setHasStartedConversation] = useState(false);
const router = useRouter();
const { agent_name } = useParams<{
agent_name: string;
const { agent_id } = useParams<{
agent_id: string;
}>();
const { agent } = useAgent(agent_name);
const { agent } = useAgent(agent_id);
const { threadId, setThreadId, isNewThread, setIsNewThread } =
useThreadChat();
@@ -57,7 +57,8 @@ export default function AgentChatPage() {
const { showNotification } = useNotification();
const [thread, sendMessage] = useThreadStream({
threadId: isNewThread ? undefined : threadId,
context: { ...settings.context, agent_name: agent_name },
assistantId: agent_id,
context: { ...settings.context, agent_id },
onStart: (createdThreadId) => {
setThreadId(createdThreadId);
setIsNewThread(false);
@@ -65,7 +66,7 @@ export default function AgentChatPage() {
history.replaceState(
null,
"",
`${window.location.pathname}${window.location.search}#/workspace/agents/${agent_name}/chats/${createdThreadId}`,
`${window.location.pathname}${window.location.search}#/workspace/agents/${agent_id}/chats/${createdThreadId}`,
);
},
onFinish: (state) => {
@@ -92,9 +93,9 @@ export default function AgentChatPage() {
window.dispatchEvent(
new CustomEvent("workspace:sandbox-resume-autoscroll"),
);
void sendMessage(threadId, message, { agent_name });
void sendMessage(threadId, message, { agent_id });
},
[sendMessage, threadId, agent_name],
[sendMessage, threadId, agent_id],
);
const handleStop = useCallback(async () => {
@@ -123,7 +124,7 @@ export default function AgentChatPage() {
<div className="flex shrink-0 items-center gap-1.5 rounded-md border px-2 py-1">
<BotIcon className="text-primary h-3.5 w-3.5" />
<span className="text-xs font-medium">
{agent?.name ?? agent_name}
{agent?.name ?? agent_id}
</span>
</div>
@@ -136,7 +137,7 @@ export default function AgentChatPage() {
size="sm"
variant="secondary"
onClick={() => {
router.push(`/workspace/agents/${agent_name}/chats/new`);
router.push(`/workspace/agents/${agent_id}/chats/new`);
}}
>
<PlusSquare /> {t.agents.newChat}
@@ -199,7 +200,7 @@ export default function AgentChatPage() {
context={settings.context}
extraHeader={
isLandingState && (
<AgentWelcome agent={agent} agentName={agent_name} />
<AgentWelcome agent={agent} agentName={agent?.name ?? agent_id} />
)
}
disabled={env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true"}
+21 -23
View File
@@ -49,7 +49,6 @@ import { cn } from "@/lib/utils";
type Step = "name" | "chat";
type SetupAgentStatus = "idle" | "requested" | "completed";
const NAME_RE = /^[A-Za-z0-9-]+$/;
const SAVE_HINT_STORAGE_KEY = "deerflow.agent-create.save-hint-seen";
const AGENT_READ_RETRY_DELAYS_MS = [200, 500, 1_000, 2_000];
const DEFAULT_SELECTED_SKILL_COUNT = 3;
@@ -58,14 +57,14 @@ function wait(ms: number) {
return new Promise((resolve) => window.setTimeout(resolve, ms));
}
async function getAgentWithRetry(agentName: string) {
async function getAgentWithRetry(agentId: string) {
for (const delay of [0, ...AGENT_READ_RETRY_DELAYS_MS]) {
if (delay > 0) {
await wait(delay);
}
try {
return await getAgent(agentName);
return await getAgent(agentId);
} catch {
// Retry until the write settles or the attempts are exhausted.
}
@@ -97,7 +96,7 @@ export default function NewAgentPage() {
const [nameError, setNameError] = useState("");
const [isCheckingName, setIsCheckingName] = useState(false);
const [isCreatingAgent, setIsCreatingAgent] = useState(false);
const [agentName, setAgentName] = useState("");
const [agentId, setAgentId] = useState("");
const [agent, setAgent] = useState<Agent | null>(null);
const [showSaveHint, setShowSaveHint] = useState(false);
const [setupAgentStatus, setSetupAgentStatus] =
@@ -125,6 +124,7 @@ export default function NewAgentPage() {
const [thread, sendMessage] = useThreadStream({
threadId: step === "chat" ? threadId : undefined,
assistantId: agentId || undefined,
context: {
mode: "flash",
is_bootstrap: true,
@@ -135,9 +135,9 @@ export default function NewAgentPage() {
}
},
onToolEnd({ name }) {
if (name !== "setup_agent" || !agentName) return;
if (name !== "setup_agent" || !agentId) return;
setSetupAgentStatus("completed");
void getAgentWithRetry(agentName).then((fetched) => {
void getAgentWithRetry(agentId).then((fetched) => {
if (fetched) {
setAgent(fetched);
return;
@@ -171,8 +171,7 @@ export default function NewAgentPage() {
const handleConfirmName = useCallback(async () => {
const trimmed = nameInput.trim();
if (!trimmed) return;
if (!NAME_RE.test(trimmed)) {
if (!trimmed) {
setNameError(t.agents.nameStepInvalidError);
return;
}
@@ -185,11 +184,7 @@ export default function NewAgentPage() {
setNameError("");
setIsCheckingName(true);
try {
const result = await checkAgentName(trimmed);
if (!result.available) {
setNameError(t.agents.nameStepAlreadyExistsError);
return;
}
await checkAgentName(trimmed);
} catch (err) {
if (
err instanceof AgentNameCheckError &&
@@ -204,14 +199,18 @@ export default function NewAgentPage() {
setIsCheckingName(false);
}
let createdId: string;
let createdName: string;
setIsCreatingAgent(true);
try {
await createAgent({
const created = await createAgent({
name: trimmed,
description: "",
skills: selectedSkills,
soul: "",
});
createdId = created.id;
createdName = created.name;
} catch (err) {
setNameError(
getCreateAgentErrorMessage(
@@ -225,17 +224,16 @@ export default function NewAgentPage() {
setIsCreatingAgent(false);
}
setAgentName(trimmed);
setAgentId(createdId);
setStep("chat");
await ensureThreadExists();
await sendMessage(threadId, {
text: t.agents.nameStepBootstrapMessage.replace("{name}", trimmed),
text: t.agents.nameStepBootstrapMessage.replace("{name}", createdName),
files: [],
});
}, [
nameInput,
sendMessage,
t.agents.nameStepAlreadyExistsError,
t.agents.nameStepNetworkError,
t.agents.nameStepBootstrapMessage,
t.agents.nameStepCheckError,
@@ -272,15 +270,15 @@ export default function NewAgentPage() {
await sendMessage(
threadId,
{ text: trimmed, files: [] },
{ agent_name: agentName },
{ agent_id: agentId },
);
},
[agentName, sendMessage, thread.isLoading, threadId, ensureThreadExists],
[agentId, sendMessage, thread.isLoading, threadId, ensureThreadExists],
);
const handleSaveAgent = useCallback(async () => {
if (
!agentName ||
!agentId ||
agent ||
thread.isLoading ||
setupAgentStatus !== "idle"
@@ -294,7 +292,7 @@ export default function NewAgentPage() {
await sendMessage(
threadId,
{ text: t.agents.saveCommandMessage, files: [] },
{ agent_name: agentName },
{ agent_id: agentId },
{ additionalKwargs: { hide_from_ui: true } },
);
toast.success(t.agents.saveRequested);
@@ -304,7 +302,7 @@ export default function NewAgentPage() {
}
}, [
agent,
agentName,
agentId,
sendMessage,
setupAgentStatus,
t.agents.saveCommandMessage,
@@ -480,7 +478,7 @@ export default function NewAgentPage() {
<Button
onClick={() =>
router.push(
`/workspace/agents/${agentName}/chats/new`,
`/workspace/agents/${agentId}/chats/new`,
)
}
>