{chatTitle && (
-
+
{parentChat && (
<>
)}
@@ -227,7 +252,23 @@ export const AgentDetailTopBar: FC
= ({
View Workspace
-
+ {!isArchived && (
+ <>
+
+ {onRegenerateTitle && (
+ <>
+
+
+ Generate new title
+
+
+ >
+ )}
+ >
+ )}
{isArchived ? (
diff --git a/site/src/pages/AgentsPage/components/AgentDetailView.stories.tsx b/site/src/pages/AgentsPage/components/AgentDetailView.stories.tsx
index 6894dd9510..8f6d62f9c5 100644
--- a/site/src/pages/AgentsPage/components/AgentDetailView.stories.tsx
+++ b/site/src/pages/AgentsPage/components/AgentDetailView.stories.tsx
@@ -142,6 +142,7 @@ const StoryAgentDetailView: FC = ({ editing, ...overrides }) => {
handleArchiveAgentAction: fn(),
handleUnarchiveAgentAction: fn(),
handleArchiveAndDeleteWorkspaceAction: fn(),
+ handleRegenerateTitle: fn(),
scrollContainerRef: { current: null },
hasMoreMessages: false,
isFetchingMoreMessages: false,
diff --git a/site/src/pages/AgentsPage/components/AgentDetailView.tsx b/site/src/pages/AgentsPage/components/AgentDetailView.tsx
index 8b095b738a..fd3ff693a4 100644
--- a/site/src/pages/AgentsPage/components/AgentDetailView.tsx
+++ b/site/src/pages/AgentsPage/components/AgentDetailView.tsx
@@ -117,6 +117,9 @@ interface AgentDetailViewProps {
handleArchiveAgentAction: () => void;
handleUnarchiveAgentAction: () => void;
handleArchiveAndDeleteWorkspaceAction: () => void;
+ handleRegenerateTitle?: () => void;
+ isRegeneratingTitle?: boolean;
+ isRegenerateTitleDisabled?: boolean;
// Scroll container ref.
scrollContainerRef: RefObject;
@@ -179,6 +182,9 @@ export const AgentDetailView: FC = ({
handleArchiveAgentAction,
handleUnarchiveAgentAction,
handleArchiveAndDeleteWorkspaceAction,
+ handleRegenerateTitle,
+ isRegeneratingTitle,
+ isRegenerateTitleDisabled,
scrollContainerRef,
scrollToBottomRef,
hasMoreMessages,
@@ -261,6 +267,11 @@ export const AgentDetailView: FC = ({
onArchiveAndDeleteWorkspace={
handleArchiveAndDeleteWorkspaceAction
}
+ {...(handleRegenerateTitle
+ ? { onRegenerateTitle: handleRegenerateTitle }
+ : {})}
+ isRegeneratingTitle={isRegeneratingTitle}
+ isRegenerateTitleDisabled={isRegenerateTitleDisabled}
hasWorkspace={hasWorkspace}
isArchived={isArchived}
diffStatusData={diffStatusData}
@@ -435,6 +446,7 @@ export const AgentDetailLoadingView: FC = ({
}}
onArchiveAgent={() => {}}
onUnarchiveAgent={() => {}}
+ onRegenerateTitle={() => {}}
onArchiveAndDeleteWorkspace={() => {}}
hasWorkspace={false}
isSidebarCollapsed={isSidebarCollapsed}
@@ -507,6 +519,7 @@ export const AgentDetailNotFoundView: FC = ({
}}
onArchiveAgent={() => {}}
onUnarchiveAgent={() => {}}
+ onRegenerateTitle={() => {}}
onArchiveAndDeleteWorkspace={() => {}}
hasWorkspace={false}
isSidebarCollapsed={isSidebarCollapsed}
diff --git a/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.stories.tsx b/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.stories.tsx
index 7c0ef418d5..294820d1b2 100644
--- a/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.stories.tsx
+++ b/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.stories.tsx
@@ -74,8 +74,11 @@ const meta: Meta = {
onArchiveAndDeleteWorkspace: fn(),
onPinAgent: fn(),
onUnpinAgent: fn(),
+ onRegenerateTitle: fn(),
onBeforeNewAgent: fn(),
isCreating: false,
+ isRegeneratingTitle: false,
+ regeneratingTitleChatId: null,
archivedFilter: "active" as const,
onArchivedFilterChange: fn(),
},
diff --git a/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.test.tsx b/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.test.tsx
index cf883ae12b..50d2829f60 100644
--- a/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.test.tsx
+++ b/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.test.tsx
@@ -108,6 +108,9 @@ const defaultProps: React.ComponentProps = {
onArchiveAndDeleteWorkspace: vi.fn(),
onPinAgent: vi.fn(),
onUnpinAgent: vi.fn(),
+ onRegenerateTitle: vi.fn(),
+ isRegeneratingTitle: false,
+ regeneratingTitleChatId: null,
onBeforeNewAgent: vi.fn(),
isCreating: false,
archivedFilter: "active" as const,
diff --git a/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.tsx b/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.tsx
index 4e8331fe54..cc811a8c8c 100644
--- a/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.tsx
+++ b/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.tsx
@@ -72,6 +72,7 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
+ DropdownMenuSeparator,
DropdownMenuTrigger,
} from "#/components/DropdownMenu/DropdownMenu";
import { ExternalImage } from "#/components/ExternalImage/ExternalImage";
@@ -123,10 +124,13 @@ interface AgentsSidebarProps {
onPinAgent: (chatId: string) => void;
onUnpinAgent: (chatId: string) => void;
onReorderPinnedAgent?: (chatId: string, pinOrder: number) => void;
+ onRegenerateTitle: (chatId: string) => void;
onBeforeNewAgent?: () => void;
isCreating: boolean;
isArchiving?: boolean;
archivingChatId?: string | null;
+ isRegeneratingTitle?: boolean;
+ regeneratingTitleChatId?: string | null;
isLoading?: boolean;
loadError?: unknown;
onRetryLoad?: () => void;
@@ -367,6 +371,8 @@ interface ChatTreeContextValue {
readonly chatErrorReasons: Record;
readonly isArchiving: boolean;
readonly archivingChatId: string | null;
+ readonly isRegeneratingTitle: boolean;
+ readonly regeneratingTitleChatId: string | null;
readonly toggleExpanded: (chatID: string) => void;
readonly onArchiveAgent: (chatId: string) => void;
readonly onUnarchiveAgent: (chatId: string) => void;
@@ -376,6 +382,7 @@ interface ChatTreeContextValue {
) => void;
readonly onPinAgent: (chatId: string) => void;
readonly onUnpinAgent: (chatId: string) => void;
+ readonly onRegenerateTitle: (chatId: string) => void;
}
const ChatTreeContext = createContext(null);
@@ -405,12 +412,15 @@ const ChatTreeNode: FC = ({ chat, isChildNode }) => {
chatErrorReasons,
isArchiving,
archivingChatId,
+ isRegeneratingTitle,
+ regeneratingTitleChatId,
toggleExpanded,
onArchiveAgent,
onUnarchiveAgent,
onArchiveAndDeleteWorkspace,
onPinAgent,
onUnpinAgent,
+ onRegenerateTitle,
} = useChatTree();
const chatID = chat.id;
const childIDs = (chatTree.childrenById.get(chatID) ?? []).filter((childID) =>
@@ -448,6 +458,8 @@ const ChatTreeNode: FC = ({ chat, isChildNode }) => {
}`;
const workspaceId = chat.workspace_id;
const isArchivingThisChat = isArchiving && archivingChatId === chat.id;
+ const isRegeneratingThisChat =
+ isRegeneratingTitle && regeneratingTitleChatId === chat.id;
const isExpanded = normalizedSearch ? true : (expandedById[chatID] ?? false);
return (
@@ -509,13 +521,21 @@ const ChatTreeNode: FC = ({ chat, isChildNode }) => {
{chat.title}
+ {isRegeneratingThisChat && (
+
+ Regenerating title…
+
+ )}
{hasLinkedDiffStatus && hasLineStats && (
@@ -598,6 +618,14 @@ const ChatTreeNode: FC
= ({ chat, isChildNode }) => {
) : (
<>
+ onRegenerateTitle(chat.id)}
+ >
+
+ Generate new title
+
+
= (props) => {
onPinAgent,
onUnpinAgent,
onReorderPinnedAgent,
+ onRegenerateTitle,
onBeforeNewAgent,
isCreating,
isArchiving = false,
archivingChatId = null,
+ isRegeneratingTitle = false,
+ regeneratingTitleChatId = null,
isLoading = false,
loadError,
onRetryLoad,
@@ -910,12 +941,15 @@ export const AgentsSidebar: FC = (props) => {
chatErrorReasons,
isArchiving,
archivingChatId,
+ isRegeneratingTitle,
+ regeneratingTitleChatId,
toggleExpanded,
onArchiveAgent,
onUnarchiveAgent,
onArchiveAndDeleteWorkspace,
onPinAgent,
onUnpinAgent,
+ onRegenerateTitle,
};
const subNavTitle = "Settings";