mirror of
https://github.com/coder/coder.git
synced 2026-09-21 12:44:32 +08:00
feat(agents): add unarchive agent support (#22579)
This commit is contained in:
@@ -1292,6 +1292,95 @@ func TestArchiveChat(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestUnarchiveChat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client := newChatClient(t)
|
||||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
_ = createChatModelConfig(t, client)
|
||||
|
||||
chat, err := client.CreateChat(ctx, codersdk.CreateChatRequest{
|
||||
Content: []codersdk.ChatInputPart{
|
||||
{
|
||||
Type: codersdk.ChatInputPartTypeText,
|
||||
Text: "archive then unarchive me",
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Archive the chat first.
|
||||
err = client.ArchiveChat(ctx, chat.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify it's archived.
|
||||
archivedChats, err := client.ListChats(ctx, &codersdk.ListChatsOptions{
|
||||
Archived: ptr.Ref(true),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, archivedChats, 1)
|
||||
require.True(t, archivedChats[0].Archived)
|
||||
|
||||
// Unarchive the chat.
|
||||
err = client.UnarchiveChat(ctx, chat.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify it's no longer archived.
|
||||
activeChats, err := client.ListChats(ctx, &codersdk.ListChatsOptions{
|
||||
Archived: ptr.Ref(false),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, activeChats, 1)
|
||||
require.Equal(t, chat.ID, activeChats[0].ID)
|
||||
require.False(t, activeChats[0].Archived)
|
||||
|
||||
// No archived chats remain.
|
||||
archivedChats, err = client.ListChats(ctx, &codersdk.ListChatsOptions{
|
||||
Archived: ptr.Ref(true),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, archivedChats)
|
||||
})
|
||||
|
||||
t.Run("NotArchived", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client := newChatClient(t)
|
||||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
_ = createChatModelConfig(t, client)
|
||||
|
||||
chat, err := client.CreateChat(ctx, codersdk.CreateChatRequest{
|
||||
Content: []codersdk.ChatInputPart{
|
||||
{
|
||||
Type: codersdk.ChatInputPartTypeText,
|
||||
Text: "not archived",
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Trying to unarchive a non-archived chat should fail.
|
||||
err = client.UnarchiveChat(ctx, chat.ID)
|
||||
requireSDKError(t, err, http.StatusBadRequest)
|
||||
})
|
||||
|
||||
t.Run("NotFound", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client := newChatClient(t)
|
||||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
err := client.UnarchiveChat(ctx, uuid.New())
|
||||
requireSDKError(t, err, http.StatusNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPostChatMessages(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -2944,6 +2944,10 @@ class ApiMethods {
|
||||
await this.axios.post(`/api/experimental/chats/${chatId}/archive`);
|
||||
};
|
||||
|
||||
unarchiveChat = async (chatId: string): Promise<void> => {
|
||||
await this.axios.post(`/api/experimental/chats/${chatId}/unarchive`);
|
||||
};
|
||||
|
||||
createChatMessage = async (
|
||||
chatId: string,
|
||||
req: TypesGen.CreateChatMessageRequest,
|
||||
|
||||
@@ -30,6 +30,13 @@ export const archiveChat = (queryClient: QueryClient) => ({
|
||||
},
|
||||
});
|
||||
|
||||
export const unarchiveChat = (queryClient: QueryClient) => ({
|
||||
mutationFn: (chatId: string) => API.unarchiveChat(chatId),
|
||||
onSuccess: async () => {
|
||||
await queryClient.invalidateQueries({ queryKey: chatsKey });
|
||||
},
|
||||
});
|
||||
|
||||
export const createChatMessage = (
|
||||
queryClient: QueryClient,
|
||||
chatId: string,
|
||||
|
||||
@@ -44,6 +44,7 @@ const AgentDetailLayout: FC = () => {
|
||||
clearChatErrorReason: () => {},
|
||||
requestArchiveAgent: () => {},
|
||||
requestArchiveAndDeleteWorkspace: () => {},
|
||||
requestUnarchiveAgent: () => {},
|
||||
isSidebarCollapsed: false,
|
||||
onToggleSidebarCollapsed: () => {},
|
||||
} satisfies AgentsOutletContext
|
||||
|
||||
@@ -81,6 +81,8 @@ const noopRequestArchiveAgent: AgentsOutletContext["requestArchiveAgent"] =
|
||||
() => {};
|
||||
const noopRequestArchiveAndDeleteWorkspace: AgentsOutletContext["requestArchiveAndDeleteWorkspace"] =
|
||||
() => {};
|
||||
const noopRequestUnarchiveAgent: AgentsOutletContext["requestUnarchiveAgent"] =
|
||||
() => {};
|
||||
const lastModelConfigIDStorageKey = "agents.last-model-config-id";
|
||||
type ChatStoreHandle = ReturnType<typeof useChatStore>["store"];
|
||||
|
||||
@@ -407,6 +409,8 @@ const AgentDetail: FC = () => {
|
||||
const requestArchiveAndDeleteWorkspace =
|
||||
outletContext?.requestArchiveAndDeleteWorkspace ??
|
||||
noopRequestArchiveAndDeleteWorkspace;
|
||||
const requestUnarchiveAgent =
|
||||
outletContext?.requestUnarchiveAgent ?? noopRequestUnarchiveAgent;
|
||||
const isSidebarCollapsed = outletContext?.isSidebarCollapsed ?? false;
|
||||
const onToggleSidebarCollapsed =
|
||||
outletContext?.onToggleSidebarCollapsed ?? (() => {});
|
||||
@@ -766,6 +770,13 @@ const AgentDetail: FC = () => {
|
||||
requestArchiveAndDeleteWorkspace(agentId, workspaceId);
|
||||
};
|
||||
|
||||
const handleUnarchiveAgentAction = () => {
|
||||
if (!agentId || !isArchived) {
|
||||
return;
|
||||
}
|
||||
requestUnarchiveAgent(agentId);
|
||||
};
|
||||
|
||||
if (chatQuery.isLoading) {
|
||||
return (
|
||||
<div className="relative flex h-full min-h-0 min-w-0 flex-1 flex-col">
|
||||
@@ -786,6 +797,7 @@ const AgentDetail: FC = () => {
|
||||
}}
|
||||
onOpenParentChat={() => {}}
|
||||
onArchiveAgent={() => {}}
|
||||
onUnarchiveAgent={() => {}}
|
||||
onArchiveAndDeleteWorkspace={() => {}}
|
||||
hasWorkspace={false}
|
||||
isSidebarCollapsed={isSidebarCollapsed}
|
||||
@@ -860,6 +872,7 @@ const AgentDetail: FC = () => {
|
||||
}}
|
||||
onOpenParentChat={() => {}}
|
||||
onArchiveAgent={() => {}}
|
||||
onUnarchiveAgent={() => {}}
|
||||
onArchiveAndDeleteWorkspace={() => {}}
|
||||
hasWorkspace={false}
|
||||
isSidebarCollapsed={isSidebarCollapsed}
|
||||
@@ -900,6 +913,7 @@ const AgentDetail: FC = () => {
|
||||
sshCommand,
|
||||
}}
|
||||
onArchiveAgent={handleArchiveAgentAction}
|
||||
onUnarchiveAgent={handleUnarchiveAgentAction}
|
||||
onArchiveAndDeleteWorkspace={handleArchiveAndDeleteWorkspaceAction}
|
||||
hasWorkspace={Boolean(workspaceId)}
|
||||
isArchived={isArchived}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { MockUserOwner } from "testHelpers/entities";
|
||||
import { withAuthProvider, withDashboardProvider } from "testHelpers/storybook";
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import type { ChatDiffStatusResponse } from "api/api";
|
||||
import { expect, userEvent, waitFor, within } from "storybook/test";
|
||||
import { AgentDetailTopBar } from "./TopBar";
|
||||
|
||||
const mockDiffStatus: ChatDiffStatusResponse = {
|
||||
@@ -31,6 +32,7 @@ const defaultProps = {
|
||||
},
|
||||
onArchiveAgent: () => {},
|
||||
onArchiveAndDeleteWorkspace: () => {},
|
||||
onUnarchiveAgent: () => {},
|
||||
isSidebarCollapsed: false,
|
||||
onToggleSidebarCollapsed: () => {},
|
||||
} satisfies React.ComponentProps<typeof AgentDetailTopBar>;
|
||||
@@ -105,3 +107,26 @@ export const NoTitle: Story = {
|
||||
chatTitle: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
export const ArchivedWithUnarchive: Story = {
|
||||
args: {
|
||||
isArchived: true,
|
||||
onUnarchiveAgent: () => {},
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
// Open the actions dropdown
|
||||
const trigger = canvas.getByLabelText("Open agent actions");
|
||||
await userEvent.click(trigger);
|
||||
// Verify "Unarchive Agent" is shown instead of "Archive Agent"
|
||||
await waitFor(() => {
|
||||
const body = within(document.body);
|
||||
expect(body.getByText("Unarchive Agent")).toBeInTheDocument();
|
||||
});
|
||||
const body = within(document.body);
|
||||
expect(body.queryByText("Archive Agent")).not.toBeInTheDocument();
|
||||
expect(
|
||||
body.queryByText("Archive & Delete Workspace"),
|
||||
).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { useAuthenticated } from "hooks";
|
||||
import {
|
||||
ArchiveIcon,
|
||||
ArchiveRestoreIcon,
|
||||
ArrowLeftIcon,
|
||||
ChevronRightIcon,
|
||||
CopyIcon,
|
||||
@@ -88,6 +89,7 @@ type AgentDetailTopBarProps = {
|
||||
diff: DiffPanelState;
|
||||
workspace: WorkspaceActions;
|
||||
onArchiveAgent: () => void;
|
||||
onUnarchiveAgent: () => void;
|
||||
onArchiveAndDeleteWorkspace: () => void;
|
||||
hasWorkspace?: boolean;
|
||||
isArchived?: boolean;
|
||||
@@ -102,6 +104,7 @@ export const AgentDetailTopBar: FC<AgentDetailTopBarProps> = ({
|
||||
diff,
|
||||
workspace,
|
||||
onArchiveAgent,
|
||||
onUnarchiveAgent,
|
||||
onArchiveAndDeleteWorkspace,
|
||||
hasWorkspace,
|
||||
isArchived,
|
||||
@@ -235,25 +238,32 @@ export const AgentDetailTopBar: FC<AgentDetailTopBarProps> = ({
|
||||
View Workspace
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
{!isArchived && (
|
||||
<DropdownMenuItem
|
||||
className="text-content-destructive focus:text-content-destructive"
|
||||
onSelect={onArchiveAgent}
|
||||
>
|
||||
<ArchiveIcon className="h-3.5 w-3.5" />
|
||||
Archive Agent
|
||||
{isArchived ? (
|
||||
<DropdownMenuItem onSelect={onUnarchiveAgent}>
|
||||
<ArchiveRestoreIcon className="h-3.5 w-3.5" />
|
||||
Unarchive Agent
|
||||
</DropdownMenuItem>
|
||||
) : (
|
||||
<>
|
||||
<DropdownMenuItem
|
||||
className="text-content-destructive focus:text-content-destructive"
|
||||
onSelect={onArchiveAgent}
|
||||
>
|
||||
<ArchiveIcon className="h-3.5 w-3.5" />
|
||||
Archive Agent
|
||||
</DropdownMenuItem>
|
||||
{hasWorkspace && (
|
||||
<DropdownMenuItem
|
||||
className="text-content-destructive focus:text-content-destructive"
|
||||
onSelect={onArchiveAndDeleteWorkspace}
|
||||
>
|
||||
<Trash2Icon className="h-3.5 w-3.5" />
|
||||
Archive & Delete Workspace
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!isArchived && hasWorkspace && (
|
||||
<DropdownMenuItem
|
||||
className="text-content-destructive focus:text-content-destructive"
|
||||
onSelect={onArchiveAndDeleteWorkspace}
|
||||
>
|
||||
<Trash2Icon className="h-3.5 w-3.5" />
|
||||
Archive & Delete Workspace
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>{" "}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<WebPushButton />
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
chats,
|
||||
chatsKey,
|
||||
createChat,
|
||||
unarchiveChat,
|
||||
} from "api/queries/chats";
|
||||
import { workspaces } from "api/queries/workspaces";
|
||||
import type * as TypesGen from "api/typesGenerated";
|
||||
@@ -88,6 +89,7 @@ export interface AgentsOutletContext {
|
||||
setChatErrorReason: (chatId: string, reason: string) => void;
|
||||
clearChatErrorReason: (chatId: string) => void;
|
||||
requestArchiveAgent: (chatId: string) => void;
|
||||
requestUnarchiveAgent: (chatId: string) => void;
|
||||
requestArchiveAndDeleteWorkspace: (
|
||||
chatId: string,
|
||||
workspaceId: string,
|
||||
@@ -188,6 +190,16 @@ const AgentsPage: FC = () => {
|
||||
toast.error(getErrorMessage(error, "Failed to archive agent."));
|
||||
},
|
||||
});
|
||||
const unarchiveAgentMutation = useMutation({
|
||||
...unarchiveChat(queryClient),
|
||||
onSuccess: async (_data, chatId) => {
|
||||
await queryClient.invalidateQueries({ queryKey: chatKey(chatId) });
|
||||
toast.success("Agent unarchived.");
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(getErrorMessage(error, "Failed to unarchive agent."));
|
||||
},
|
||||
});
|
||||
|
||||
const [isConfigureAgentsDialogOpen, setConfigureAgentsDialogOpen] =
|
||||
useState(false);
|
||||
@@ -276,6 +288,12 @@ const AgentsPage: FC = () => {
|
||||
},
|
||||
[isArchiving, archiveAndDeleteMutation],
|
||||
);
|
||||
const requestUnarchiveAgent = useCallback(
|
||||
(chatId: string) => {
|
||||
unarchiveAgentMutation.mutate(chatId);
|
||||
},
|
||||
[unarchiveAgentMutation],
|
||||
);
|
||||
const handleToggleSidebarCollapsed = useCallback(
|
||||
() => setIsSidebarCollapsed((prev) => !prev),
|
||||
[],
|
||||
@@ -286,6 +304,7 @@ const AgentsPage: FC = () => {
|
||||
setChatErrorReason,
|
||||
clearChatErrorReason,
|
||||
requestArchiveAgent,
|
||||
requestUnarchiveAgent,
|
||||
requestArchiveAndDeleteWorkspace,
|
||||
isSidebarCollapsed,
|
||||
onToggleSidebarCollapsed: handleToggleSidebarCollapsed,
|
||||
@@ -295,6 +314,7 @@ const AgentsPage: FC = () => {
|
||||
setChatErrorReason,
|
||||
clearChatErrorReason,
|
||||
requestArchiveAgent,
|
||||
requestUnarchiveAgent,
|
||||
requestArchiveAndDeleteWorkspace,
|
||||
isSidebarCollapsed,
|
||||
handleToggleSidebarCollapsed,
|
||||
@@ -452,6 +472,7 @@ const AgentsPage: FC = () => {
|
||||
modelConfigs={chatModelConfigsQuery.data ?? []}
|
||||
logoUrl={appearance.logo_url}
|
||||
onArchiveAgent={requestArchiveAgent}
|
||||
onUnarchiveAgent={requestUnarchiveAgent}
|
||||
onArchiveAndDeleteWorkspace={requestArchiveAndDeleteWorkspace}
|
||||
onNewAgent={handleNewAgent}
|
||||
isCreating={createMutation.isPending}
|
||||
|
||||
@@ -61,6 +61,7 @@ const meta: Meta<typeof AgentsSidebar> = {
|
||||
modelOptions: defaultModelOptions,
|
||||
modelConfigs: defaultModelConfigs,
|
||||
onArchiveAgent: fn(),
|
||||
onUnarchiveAgent: fn(),
|
||||
onArchiveAndDeleteWorkspace: fn(),
|
||||
onNewAgent: fn(),
|
||||
isCreating: false,
|
||||
@@ -459,3 +460,49 @@ export const DefaultShowsTimestampHidesMenu: Story = {
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
export const ArchivedAgentUnarchiveOption: Story = {
|
||||
args: {
|
||||
chats: [
|
||||
buildChat({
|
||||
id: "archived-unarchive",
|
||||
title: "Archived agent with unarchive",
|
||||
archived: true,
|
||||
}),
|
||||
],
|
||||
},
|
||||
parameters: {
|
||||
reactRouter: reactRouterParameters({
|
||||
location: { path: "/agents" },
|
||||
routing: agentsRouting,
|
||||
}),
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
// Expand archived section
|
||||
await waitFor(() => {
|
||||
expect(canvas.getByText("Archived (1)")).toBeInTheDocument();
|
||||
});
|
||||
await userEvent.click(canvas.getByText("Archived (1)"));
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
canvas.getByText("Archived agent with unarchive"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
// Open the dropdown menu for the archived agent
|
||||
const trigger = canvas.getByLabelText(
|
||||
"Open actions for Archived agent with unarchive",
|
||||
);
|
||||
await userEvent.click(trigger);
|
||||
// Verify "Unarchive agent" is shown instead of "Archive agent"
|
||||
await waitFor(() => {
|
||||
const body = within(document.body);
|
||||
expect(body.getByText("Unarchive agent")).toBeInTheDocument();
|
||||
});
|
||||
const body = within(document.body);
|
||||
expect(body.queryByText("Archive agent")).not.toBeInTheDocument();
|
||||
expect(
|
||||
body.queryByText("Archive & delete workspace"),
|
||||
).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -25,6 +25,7 @@ import { Skeleton } from "components/Skeleton/Skeleton";
|
||||
import {
|
||||
AlertTriangleIcon,
|
||||
ArchiveIcon,
|
||||
ArchiveRestoreIcon,
|
||||
CheckIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronRightIcon,
|
||||
@@ -56,6 +57,7 @@ interface AgentsSidebarProps {
|
||||
modelConfigs: readonly ChatModelConfig[];
|
||||
logoUrl?: string;
|
||||
onArchiveAgent: (chatId: string) => void;
|
||||
onUnarchiveAgent: (chatId: string) => void;
|
||||
onArchiveAndDeleteWorkspace: (chatId: string, workspaceId: string) => void;
|
||||
onNewAgent: () => void;
|
||||
isCreating: boolean;
|
||||
@@ -270,6 +272,7 @@ interface ChatTreeContextValue {
|
||||
readonly archivingChatId: string | null;
|
||||
readonly toggleExpanded: (chatID: string) => void;
|
||||
readonly onArchiveAgent: (chatId: string) => void;
|
||||
readonly onUnarchiveAgent: (chatId: string) => void;
|
||||
readonly onArchiveAndDeleteWorkspace: (
|
||||
chatId: string,
|
||||
workspaceId: string,
|
||||
@@ -305,6 +308,7 @@ const ChatTreeNode = memo<ChatTreeNodeProps>(({ chat, isChildNode }) => {
|
||||
archivingChatId,
|
||||
toggleExpanded,
|
||||
onArchiveAgent,
|
||||
onUnarchiveAgent,
|
||||
onArchiveAndDeleteWorkspace,
|
||||
} = useChatTree();
|
||||
const chatID = chat.id;
|
||||
@@ -470,25 +474,38 @@ const ChatTreeNode = memo<ChatTreeNodeProps>(({ chat, isChildNode }) => {
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
className="text-content-destructive focus:text-content-destructive"
|
||||
disabled={isArchiving}
|
||||
onSelect={() => onArchiveAgent(chat.id)}
|
||||
>
|
||||
<ArchiveIcon className="h-3.5 w-3.5" />
|
||||
Archive agent
|
||||
</DropdownMenuItem>
|
||||
{workspaceId && (
|
||||
{chat.archived ? (
|
||||
<DropdownMenuItem
|
||||
className="text-content-destructive focus:text-content-destructive"
|
||||
disabled={isArchiving}
|
||||
onSelect={() =>
|
||||
onArchiveAndDeleteWorkspace(chat.id, workspaceId)
|
||||
}
|
||||
onSelect={() => onUnarchiveAgent(chat.id)}
|
||||
>
|
||||
<Trash2Icon className="h-3.5 w-3.5" />
|
||||
Archive & delete workspace
|
||||
<ArchiveRestoreIcon className="h-3.5 w-3.5" />
|
||||
Unarchive agent
|
||||
</DropdownMenuItem>
|
||||
) : (
|
||||
<>
|
||||
{" "}
|
||||
<DropdownMenuItem
|
||||
className="text-content-destructive focus:text-content-destructive"
|
||||
disabled={isArchiving}
|
||||
onSelect={() => onArchiveAgent(chat.id)}
|
||||
>
|
||||
<ArchiveIcon className="h-3.5 w-3.5" />
|
||||
Archive agent
|
||||
</DropdownMenuItem>
|
||||
{workspaceId && (
|
||||
<DropdownMenuItem
|
||||
className="text-content-destructive focus:text-content-destructive"
|
||||
disabled={isArchiving}
|
||||
onSelect={() =>
|
||||
onArchiveAndDeleteWorkspace(chat.id, workspaceId)
|
||||
}
|
||||
>
|
||||
<Trash2Icon className="h-3.5 w-3.5" />
|
||||
Archive & delete workspace
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
@@ -521,6 +538,7 @@ export const AgentsSidebar: FC<AgentsSidebarProps> = (props) => {
|
||||
modelConfigs,
|
||||
logoUrl,
|
||||
onArchiveAgent,
|
||||
onUnarchiveAgent,
|
||||
onArchiveAndDeleteWorkspace,
|
||||
onNewAgent,
|
||||
isCreating,
|
||||
@@ -618,6 +636,7 @@ export const AgentsSidebar: FC<AgentsSidebarProps> = (props) => {
|
||||
archivingChatId,
|
||||
toggleExpanded,
|
||||
onArchiveAgent,
|
||||
onUnarchiveAgent,
|
||||
onArchiveAndDeleteWorkspace,
|
||||
}),
|
||||
[
|
||||
@@ -632,6 +651,7 @@ export const AgentsSidebar: FC<AgentsSidebarProps> = (props) => {
|
||||
archivingChatId,
|
||||
toggleExpanded,
|
||||
onArchiveAgent,
|
||||
onUnarchiveAgent,
|
||||
onArchiveAndDeleteWorkspace,
|
||||
],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user