diff --git a/README.md b/README.md
index 006af101c5..59d3d1b916 100644
--- a/README.md
+++ b/README.md
@@ -1464,6 +1464,16 @@ Use it as-is. Or tear it apart and make it yours.
## Core Features
+### Workspace
+
+The workspace keeps a collapsed icon sidebar visible on narrow screens. Its
+toggle opens the full navigation drawer; closing the drawer returns to the icon
+bar without changing your saved desktop sidebar preference. Navigating to another
+page also closes the mobile drawer and preserves the destination page's focus.
+Dismissing the drawer returns keyboard focus to the control that opened it.
+The Agents gallery header wraps to the available width so its import and create
+actions remain visible beside the compact sidebar on narrow screens.
+
### Skills & Tools
Open **Capability Center** from the workspace sidebar to manage **Plugins**
diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md
index 42060d835d..0b58946a46 100644
--- a/frontend/AGENTS.md
+++ b/frontend/AGENTS.md
@@ -8,12 +8,9 @@ DeerFlow's Next.js 16 frontend provides thread-based AI conversations with strea
**Stack**: Next.js 16, React 19, TypeScript 5.8, Tailwind CSS 4, pnpm 10.26.2. Requires Node.js 22+ and pnpm 10.26.2+.
-### Core dependencies
-
-- **LangGraph SDK** (`@langchain/langgraph-sdk` ^1.5.3) — Agent orchestration and streaming
-- **LangChain Core** (`@langchain/core` ^1.1.15) — Fundamental AI building blocks
-- **TanStack Query** (`@tanstack/react-query` ^5.90.17) — Server state management
-- **UI**: Shadcn UI, MagicUI, React Bits, and Vercel AI SDK elements (generated from registries — see Code Style)
+Core dependencies: `@langchain/langgraph-sdk` ^1.5.3 for orchestration/streaming,
+`@langchain/core` ^1.1.15, and `@tanstack/react-query` ^5.90.17 for server state.
+Registry UI: Shadcn, MagicUI, React Bits, Vercel AI SDK (see Code Style).
`pnpm-workspace.yaml` overrides vulnerable `@xmldom/xmldom` 0.9.x releases to
0.9.12 for GHSA-965w-775f-mr7g. Nextra pulls it in through MathJax and
@@ -23,18 +20,12 @@ upstream dependency chain resolves a patched version without it; regenerate
## Commands
-| Command | Purpose |
-| ---------------- | --------------------------------------------- |
-| `pnpm dev` | Start the development server with Webpack |
-| `pnpm build` | Production build |
-| `pnpm check` | Lint + type check (run before committing) |
-| `pnpm lint` | ESLint only |
-| `pnpm lint:fix` | ESLint with auto-fix |
-| `pnpm format` | Prettier check (`pnpm format:write` to apply) |
-| `pnpm test` | Run unit tests with Rstest |
-| `pnpm test:e2e` | Run E2E tests with Playwright (Chromium) |
-| `pnpm typecheck` | TypeScript type check (`tsc --noEmit`) |
-| `pnpm start` | Start production server |
+Run from `frontend/`; host pnpm calls use `../scripts/pnpm.py`.
+
+- `pnpm dev`: Webpack development server; `pnpm build` / `pnpm start`: production build/server.
+- `pnpm check`: ESLint + TypeScript (required before committing); `pnpm lint` / `pnpm typecheck`: individual checks; `pnpm lint:fix`: ESLint fixes.
+- `pnpm format` / `pnpm format:write`: Prettier check/write.
+- `pnpm test`: Rstest unit tests; `pnpm test:e2e`: Playwright Chromium E2E tests.
Rstest unit tests under `tests/unit/` mirror `src/` (e.g., `tests/unit/core/api/stream-mode.test.ts` tests `src/core/api/stream-mode.ts`). Import source modules via `@/`.
@@ -169,6 +160,15 @@ DOM regression: `new-agent-save.dom.test.tsx`.
- **Path alias**: `@/*` maps to `src/*`.
- **Components**: `ui/` and `ai-elements/` are generated from registries (Shadcn, MagicUI, React Bits, Vercel AI SDK) — don't manually edit these.
+Sidebar customization: `components/workspace/workspace-sidebar-frame.tsx`.
+Rail/drawer mount one tree; `useWorkspaceSidebarExpanded` preserves desktop
+`SidebarProvider.open`. Keep polling/delete-dialog provider outside that tree.
+Reuse stock `Sidebar`; close before link navigation and on pathname changes,
+preserving page focus. Dismissal restores the opener or remounted rail trigger.
+`WorkspaceSidebarTrigger` owns surface icons/ARIA; keep generated UI unchanged.
+Test frame DOM behavior and `tests/e2e/sidebar.spec.ts`.
+Agents headers/actions wrap with the rail; sidebar E2E tests cover bounds.
+
Scheduled-task list search filters the current authorized query result by title or
prompt, composing with status/type filters and thread scope. Selection must derive
from the filtered list so hidden tasks cannot remain actionable. Keep literal
@@ -234,7 +234,8 @@ round-trip validation must still reject newly entered skipped times.
## Contributing
-Features follow `src/` ownership, include TypeScript types/error handling, unit tests (`tests/unit/`, `pnpm test`) and E2E (`tests/e2e/`, `pnpm test:e2e`). Run `pnpm check` before committing; update AGENTS.md when architecture, commands or conventions change.
+Follow `src/` ownership, add types/error handling and unit/E2E coverage, run
+`pnpm check`, and update this guide for architecture, command, or convention changes.
Route asset budgets are enforced with `pnpm perf:check`. The command measures
`/login` from a normal production build, then builds in static-demo mode for the
diff --git a/frontend/src/app/workspace/agents/[agent_name]/chats/[thread_id]/page.tsx b/frontend/src/app/workspace/agents/[agent_name]/chats/[thread_id]/page.tsx
index 21f2b54ba4..49134d7aa4 100644
--- a/frontend/src/app/workspace/agents/[agent_name]/chats/[thread_id]/page.tsx
+++ b/frontend/src/app/workspace/agents/[agent_name]/chats/[thread_id]/page.tsx
@@ -7,7 +7,6 @@ import { toast } from "sonner";
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
import { Button } from "@/components/ui/button";
-import { SidebarTrigger } from "@/components/ui/sidebar";
import { AgentWelcome } from "@/components/workspace/agent-welcome";
import { ArtifactTrigger } from "@/components/workspace/artifacts";
import { BrowserTrigger } from "@/components/workspace/browser-view";
@@ -43,6 +42,7 @@ import { TodoList } from "@/components/workspace/todo-list";
import { TokenUsageIndicator } from "@/components/workspace/token-usage-indicator";
import { Tooltip } from "@/components/workspace/tooltip";
import { useActiveGoal } from "@/components/workspace/use-active-goal";
+import { WorkspaceSidebarTrigger } from "@/components/workspace/workspace-sidebar-trigger";
import { useAgent } from "@/core/agents";
import { useAuth } from "@/core/auth/AuthProvider";
import { hasPermission, PERMISSIONS } from "@/core/auth/permissions";
@@ -491,7 +491,7 @@ export default function AgentChatPage() {
: "bg-background/80 z-30 shadow-xs backdrop-blur",
)}
>
-
+
{/* Agent badge */}
@@ -51,7 +51,7 @@ export function CapabilityCenter() {
return (
-
+ {t.breadcrumb.workspace}/{t.capabilities.title}
diff --git a/frontend/src/components/workspace/channels/workspace-channels-list.tsx b/frontend/src/components/workspace/channels/workspace-channels-list.tsx
index f470b5449b..e75b8e38fb 100644
--- a/frontend/src/components/workspace/channels/workspace-channels-list.tsx
+++ b/frontend/src/components/workspace/channels/workspace-channels-list.tsx
@@ -10,7 +10,6 @@ import {
SidebarGroupLabel,
SidebarMenu,
SidebarMenuItem,
- useSidebar,
} from "@/components/ui/sidebar";
import { Skeleton } from "@/components/ui/skeleton";
import {
@@ -33,6 +32,8 @@ import type { ChannelProvider } from "@/core/channels/types";
import { useI18n } from "@/core/i18n/hooks";
import { cn } from "@/lib/utils";
+import { useWorkspaceSidebarExpanded } from "../workspace-sidebar-frame";
+
import { ChannelProviderIcon } from "./channel-provider-icon";
import { ChannelRuntimeConfigDialog } from "./channel-runtime-config-dialog";
import { ChannelScheduledUpdates } from "./channel-scheduled-updates";
@@ -54,7 +55,7 @@ function getProviderUnavailableReason(
}
export function WorkspaceChannelsList() {
- const { open: isSidebarOpen } = useSidebar();
+ const isSidebarOpen = useWorkspaceSidebarExpanded();
const { t } = useI18n();
const { enabled, providers, isLoading, error } = useChannelProviders();
const connectMutation = useConnectChannelProvider();
diff --git a/frontend/src/components/workspace/chats/chat-page.tsx b/frontend/src/components/workspace/chats/chat-page.tsx
index c839c2a5ed..bfed5e25db 100644
--- a/frontend/src/components/workspace/chats/chat-page.tsx
+++ b/frontend/src/components/workspace/chats/chat-page.tsx
@@ -8,7 +8,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import { type PromptInputMessage } from "@/components/ai-elements/prompt-input";
-import { SidebarTrigger } from "@/components/ui/sidebar";
import { ArtifactTrigger } from "@/components/workspace/artifacts";
import { BrowserTrigger } from "@/components/workspace/browser-view";
import { ContextUsageBadge } from "@/components/workspace/context-usage-badge";
@@ -39,6 +38,7 @@ import { TodoList } from "@/components/workspace/todo-list";
import { TokenUsageIndicator } from "@/components/workspace/token-usage-indicator";
import { useActiveGoal } from "@/components/workspace/use-active-goal";
import { Welcome } from "@/components/workspace/welcome";
+import { WorkspaceSidebarTrigger } from "@/components/workspace/workspace-sidebar-trigger";
import { useAuth } from "@/core/auth/AuthProvider";
import { hasPermission, PERMISSIONS } from "@/core/auth/permissions";
import {
@@ -599,7 +599,7 @@ export default function ChatPage() {
: "bg-background/80 z-30 shadow-xs backdrop-blur",
)}
>
- {!isMock && }
+ {!isMock && }
-
+ {t.breadcrumb.workspace}/{title}
diff --git a/frontend/src/components/workspace/workspace-container.tsx b/frontend/src/components/workspace/workspace-container.tsx
index 5f9a9173ee..143781df00 100644
--- a/frontend/src/components/workspace/workspace-container.tsx
+++ b/frontend/src/components/workspace/workspace-container.tsx
@@ -12,12 +12,12 @@ import {
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
-import { SidebarTrigger } from "@/components/ui/sidebar";
import { useI18n } from "@/core/i18n/hooks";
import { cn } from "@/lib/utils";
import { GithubIcon } from "./github-icon";
import { Tooltip } from "./tooltip";
+import { WorkspaceSidebarTrigger } from "./workspace-sidebar-trigger";
// Workspace sections that have an index route (/workspace//page.tsx)
// and can therefore be linked to from the breadcrumb.
const LINKABLE_SECTIONS: Record = {
@@ -60,7 +60,7 @@ export function WorkspaceHeader({
{...props}
>