diff --git a/site/src/pages/AgentsPage/components/AgentChatInput.stories.tsx b/site/src/pages/AgentsPage/components/AgentChatInput.stories.tsx
index b033162163..80b58f30c9 100644
--- a/site/src/pages/AgentsPage/components/AgentChatInput.stories.tsx
+++ b/site/src/pages/AgentsPage/components/AgentChatInput.stories.tsx
@@ -653,6 +653,39 @@ export const PlanningIndicator: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(canvas.getByText("Planning")).toBeVisible();
+ expect(
+ canvas.getByRole("button", { name: "Disable plan mode" }),
+ ).toBeVisible();
+ },
+};
+
+export const DisablePlanModeFromBadge: Story = {
+ args: {
+ planModeEnabled: true,
+ onPlanModeToggle: fn(),
+ },
+ play: async ({ args, canvasElement }) => {
+ const canvas = within(canvasElement);
+ const dismiss = canvas.getByRole("button", {
+ name: "Disable plan mode",
+ });
+ await userEvent.click(dismiss);
+ expect(args.onPlanModeToggle).toHaveBeenCalledTimes(1);
+ expect(args.onPlanModeToggle).toHaveBeenCalledWith(false);
+ },
+};
+
+export const PlanningIndicatorWithoutToggle: Story = {
+ args: {
+ planModeEnabled: true,
+ onPlanModeToggle: undefined,
+ },
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement);
+ expect(canvas.getByText("Planning")).toBeVisible();
+ expect(
+ canvas.queryByRole("button", { name: "Disable plan mode" }),
+ ).not.toBeInTheDocument();
},
};
diff --git a/site/src/pages/AgentsPage/components/AgentChatInput.tsx b/site/src/pages/AgentsPage/components/AgentChatInput.tsx
index 3d18cb923f..4694aa21d7 100644
--- a/site/src/pages/AgentsPage/components/AgentChatInput.tsx
+++ b/site/src/pages/AgentsPage/components/AgentChatInput.tsx
@@ -171,6 +171,26 @@ type ToolBadgeData =
| ({ kind: "attached-workspace" } & AttachedWorkspaceInfo)
| { kind: "mcp"; server: TypesGen.MCPServerConfig };
+// Small `X` button rendered inside pill-style badges (attached
+// workspace, MCP server, planning indicator) to dismiss or disable
+// the badge without opening the `+` menu. Callers pass the action
+// handler and a descriptive aria-label.
+const BadgeDismissButton: FC<{
+ onClick: () => void;
+ ariaLabel: string;
+ isDisabled?: boolean;
+}> = ({ onClick, ariaLabel, isDisabled = false }) => (
+
+);
+
const ToolBadge: FC<{
badge: ToolBadgeData;
onRemoveWorkspace?: () => void;
@@ -210,14 +230,10 @@ const ToolBadge: FC<{
{badge.name}
{onRemoveWorkspace && (
-
+ ariaLabel={`Remove workspace ${badge.name}`}
+ />
)}
);
@@ -237,14 +253,10 @@ const ToolBadge: FC<{
)}
{badge.server.display_name}
{!isForceOn && onRemoveMcp && (
-
+ ariaLabel={`Remove ${badge.server.display_name}`}
+ />
)}
);
@@ -445,6 +457,8 @@ export const AgentChatInput: FC = ({
setPlusMenuOpen(false);
};
+ const handleDisablePlanMode = () => onPlanModeToggle?.(false);
+
const fileInputRef = useRef(null);
const [composerElement, setComposerElement] = useState(
null,
@@ -1065,6 +1079,13 @@ export const AgentChatInput: FC = ({
Planning
+ {onPlanModeToggle && (
+
+ )}
)}{" "}
{/* Badge row — all badges and the pill always