Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # frontend-web/src/components/workspace/workspace-nav-chat-list.tsx # frontend-web/src/pages/WorkspaceRoutes.tsx # offline-backend-20260512/backend/app/gateway/app.py # offline-backend-20260512/backend/app/gateway/deps.py # offline-backend-20260512/backend/pyproject.toml
1142 lines
35 KiB
TypeScript
1142 lines
35 KiB
TypeScript
import { useEffect, useState, type CSSProperties, type ReactNode } from "react";
|
||
import {
|
||
Navigate,
|
||
Route,
|
||
Routes,
|
||
useLocation,
|
||
useParams,
|
||
} from "react-router-dom";
|
||
|
||
import { getStoredAuth } from "@/core/auth";
|
||
import { getAPIClient } from "@/core/api";
|
||
import { isSentimentAgentVisible } from "@/core/sentiment-agent/config";
|
||
import {
|
||
resolveChatsIframeEntryRedirect,
|
||
resolveStandardChatFallback,
|
||
} from "@/core/page-layout/chats-iframe-route";
|
||
import { isEmbeddedHost } from "@/core/embed/frame-context";
|
||
|
||
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
|
||
import { PromptInputProvider } from "@/components/ai-elements/prompt-input";
|
||
import {
|
||
CompactShortcutButtons,
|
||
LayoutModeSwitcher,
|
||
ThemeSwitcher,
|
||
} from "@/components/workspace/appearance-controls";
|
||
import { WorkspaceSidebar } from "@/components/workspace/workspace-sidebar";
|
||
import { UserIdentityBadge } from "@/components/workspace/user-identity-badge";
|
||
import { CommandPalette } from "@/components/workspace/command-palette";
|
||
import { Toaster } from "sonner";
|
||
import { SubtasksProvider } from "@/core/tasks/context";
|
||
import { ArtifactsProvider } from "@/components/workspace/artifacts";
|
||
import { DocumentRewritePresentationProvider } from "@/core/document-rewrite/context";
|
||
|
||
import { RouteBaseProvider } from "@/core/route-base";
|
||
import { agentRefOfThread } from "@/core/threads/utils";
|
||
|
||
import ChatsPage from "./ChatsPage";
|
||
import ChatPage from "./ChatPage";
|
||
import ArtifactLibraryPage from "./ArtifactLibraryPage";
|
||
import IframeChatPage from "./IframeChatPage";
|
||
import AgentsPage from "./AgentsPage";
|
||
import AgentChatPage from "./AgentChatPage";
|
||
import SentimentAgentChatPage from "./SentimentAgentChatPage";
|
||
import NewAgentPage from "./NewAgentPage";
|
||
import AdminAgentsPage from "./AdminAgentsPage";
|
||
import AdminScheduledTasksPage from "./AdminScheduledTasksPage";
|
||
import AdminSkillsPage from "./AdminSkillsPage";
|
||
import ScheduledTaskRunDetailPage from "./ScheduledTaskRunDetailPage";
|
||
import ScheduledTasksPage from "./ScheduledTasksPage";
|
||
import LlmWikiKnowledgePage from "./LlmWikiKnowledgePage";
|
||
import AssistantKnowledgePage from "./AssistantKnowledgePage";
|
||
import WeKnoraWikiManagementPage from "@/strategy-components/components/resource-management/llmwiki/WeKnoraWikiManagementPage";
|
||
import WeKnoraWikiPageView from "@/strategy-components/components/resource-management/llmwiki/WeKnoraWikiPageView";
|
||
import WeKnoraPackageTransfersPage from "@/strategy-components/components/resource-management/llmwiki/WeKnoraPackageTransfersPage";
|
||
import MarkdownDocxEditorPage from "./MarkdownDocxEditorPage";
|
||
import LlmMetricsPage from "./LlmMetricsPage";
|
||
import CheckpointMigrationPage from "./CheckpointMigrationPage";
|
||
import MemoryPage from "./MemoryPage";
|
||
import PromptPrefixSettingsPage from "./PromptPrefixSettingsPage";
|
||
import PrivateSquareSettingsPage from "./PrivateSquareSettingsPage";
|
||
import ModelConcurrencySettingsPage from "./ModelConcurrencySettingsPage";
|
||
import ModelManagementPage from "./ModelManagementPage";
|
||
import PublishSquaresSettingsPage from "./PublishSquaresSettingsPage";
|
||
import SkillCompressionSettingsPage from "./SkillCompressionSettingsPage";
|
||
import SkillEvolutionPage from "./SkillEvolutionPage";
|
||
import SkillsPage from "./SkillsPage";
|
||
import UsersPage from "./UsersPage";
|
||
import AdminUserActivityPage from "./AdminUserActivityPage";
|
||
import AdminMemoryPage from "./AdminMemoryPage";
|
||
import AdminUserHubPage from "./AdminUserHubPage";
|
||
import TagsPage from "./TagsPage";
|
||
import PositionsPage from "./PositionsPage";
|
||
import PositionRolesPage from "./PositionRolesPage";
|
||
import PluginManagementPage from "./PluginManagementPage";
|
||
import RecommendedQuestionsPage from "./RecommendedQuestionsPage";
|
||
import AIWritingPage from "@/open-canvas/pages/AIWritingPage";
|
||
import DeepResearchPage from "./DeepResearchPage";
|
||
import ReportStructureCreatePage from "./deep-research/ReportStructureCreatePage";
|
||
import EnterpriseResearchPage from "./EnterpriseResearchPage";
|
||
import StudioNotebooksPage from "./StudioNotebooksPage";
|
||
import StudioNotebookDetailPage from "./StudioNotebookDetailPage";
|
||
import RoundtablePlanningPage from "@/roundtable-planning/pages/RoundtablePlanningPage";
|
||
import PositionRoundtablePage from "@/position-roundtable/pages/PositionRoundtablePage";
|
||
import EightBfRoundtablePage from "@/eightbf-roundtable/pages/EightBfRoundtablePage";
|
||
import BusinessChainEditorPage from "@/roundtable-planning/pages/BusinessChainEditorPage";
|
||
import BusinessChainListPage from "@/roundtable-planning/pages/BusinessChainListPage";
|
||
import DispatchChainPreviewPage from "@/roundtable-planning/pages/DispatchChainPreviewPage";
|
||
import "@/roundtable-planning/styles/roundtable-planning.css";
|
||
import ReportCollaborationPage, {
|
||
ReportCollaborationSessionPage,
|
||
} from "@/report-collaboration/pages/ReportCollaborationPage";
|
||
import "@/report-collaboration/styles/report-collaboration.css";
|
||
import { cn } from "@/lib/utils";
|
||
|
||
function NotebookDetailPageWithKey() {
|
||
const { notebookId } = useParams<{ notebookId: string }>();
|
||
return <StudioNotebookDetailPage key={notebookId} />;
|
||
}
|
||
|
||
// 恢复某条 bootstrap 会话时按 thread_id 重挂,切换会话不串台。
|
||
function NewAgentPageWithKey() {
|
||
const { thread_id } = useParams<{ thread_id: string }>();
|
||
return <NewAgentPage key={thread_id} />;
|
||
}
|
||
|
||
// 报告协作会话页:按 sessionId 重挂(旧 SSE/状态不串台)。
|
||
function ReportCollaborationSessionRoute({
|
||
layoutProps,
|
||
}: {
|
||
layoutProps: {
|
||
pageSidebarCollapsed: boolean;
|
||
pageSidebarHidden: boolean;
|
||
workspaceSidebarHidden: boolean;
|
||
headerHidden: boolean;
|
||
compactActionsHidden: boolean;
|
||
compactWorkspaceSidebar: boolean;
|
||
pageSidebarDark: boolean;
|
||
forceWorkspaceSidebarCollapsed: boolean;
|
||
};
|
||
}) {
|
||
const { sessionId } = useParams<{ sessionId: string }>();
|
||
return (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<ReportCollaborationSessionPage key={sessionId} sessionId={sessionId ?? ""} />
|
||
</WorkspaceLayout>
|
||
);
|
||
}
|
||
|
||
function shouldShowCompactTopActions(pathname: string): boolean {
|
||
return (
|
||
pathname === "/page/workspace/agents" ||
|
||
pathname === "/page/workspace/skills" ||
|
||
pathname === "/page/workspace/memory" ||
|
||
pathname === "/page/workspace/library" ||
|
||
pathname === "/page/workspace/knowledge" ||
|
||
pathname.startsWith("/page/workspace/knowledge/")
|
||
);
|
||
}
|
||
|
||
function CompactWorkspaceTopActions() {
|
||
return (
|
||
<div className="z-[60] flex h-12 shrink-0 items-center justify-end gap-2 border-b border-border/70 bg-card/90 px-4 shadow-sm backdrop-blur-md">
|
||
<CompactShortcutButtons tone="content" />
|
||
<ThemeSwitcher tone="content" />
|
||
<LayoutModeSwitcher tone="content" />
|
||
<div className="bg-border/70 mx-1 h-6 w-px shrink-0" aria-hidden />
|
||
<UserIdentityBadge />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function CompactAppearanceActions() {
|
||
return (
|
||
<>
|
||
<CompactShortcutButtons tone="content" />
|
||
<ThemeSwitcher tone="content" />
|
||
<LayoutModeSwitcher tone="content" />
|
||
</>
|
||
);
|
||
}
|
||
|
||
export function WorkspaceLayout({
|
||
children,
|
||
pageSidebarCollapsed,
|
||
pageSidebarHidden = false,
|
||
workspaceSidebarHidden = false,
|
||
headerHidden = false,
|
||
compactActionsHidden = false,
|
||
compactWorkspaceSidebar = false,
|
||
defaultHistorySidebarOpen,
|
||
wrapperOverflowHidden = false,
|
||
insetOverflowHidden = false,
|
||
pageSidebarDark = false,
|
||
forceWorkspaceSidebarCollapsed = false,
|
||
}: {
|
||
children: ReactNode;
|
||
pageSidebarCollapsed: boolean;
|
||
pageSidebarHidden?: boolean;
|
||
workspaceSidebarHidden?: boolean;
|
||
headerHidden?: boolean;
|
||
compactActionsHidden?: boolean;
|
||
compactWorkspaceSidebar?: boolean;
|
||
/** 全量模式下通用问答等页:聊天记录侧栏默认展开;未指定时紧凑模式仍默认收起。 */
|
||
defaultHistorySidebarOpen?: boolean;
|
||
wrapperOverflowHidden?: boolean;
|
||
insetOverflowHidden?: boolean;
|
||
pageSidebarDark?: boolean;
|
||
/** Deployment-level compact mode: keep only the icon menu rail. */
|
||
forceWorkspaceSidebarCollapsed?: boolean;
|
||
}) {
|
||
// 全量模式下通用问答(/page/workspace/chats*)的历史侧栏默认展开;其余紧凑页
|
||
// (如智能体对话,为聊天区更宽而默认收起)维持原默认。
|
||
const pathname = useLocation().pathname;
|
||
const isGeneralQa = pathname.startsWith("/page/workspace/chats");
|
||
const showCompactTopActions =
|
||
headerHidden &&
|
||
!compactActionsHidden &&
|
||
shouldShowCompactTopActions(pathname);
|
||
|
||
const pageSidebarWidth = pageSidebarHidden
|
||
? "0rem"
|
||
: pageSidebarDark
|
||
? pageSidebarCollapsed
|
||
? "0rem"
|
||
: "118px"
|
||
: pageSidebarCollapsed
|
||
? "4rem"
|
||
: "16rem";
|
||
|
||
return (
|
||
<SidebarProvider
|
||
className={[
|
||
"relative h-full w-full overflow-x-hidden",
|
||
"[&_[data-slot=sidebar-container]]:absolute",
|
||
"[&_[data-slot=sidebar-container]]:left-0",
|
||
"[&_[data-slot=sidebar-container]]:top-0",
|
||
].join(" ")}
|
||
style={
|
||
{
|
||
"--page-sidebar-width": pageSidebarWidth,
|
||
"--header-height": headerHidden ? "0rem" : "4rem",
|
||
} as CSSProperties
|
||
}
|
||
// Agent and general-chat routes share this layout component. Keep their
|
||
// sidebar state separate: otherwise React preserves the collapsed agent
|
||
// state when a history item switches to `/chats/:thread_id`, while the
|
||
// new page's header button is meant to control GeneralQaSidebar.
|
||
key={`${String(compactWorkspaceSidebar)}:${isGeneralQa ? "general" : "workspace"}`}
|
||
data-page-sidebar-collapsed={pageSidebarCollapsed}
|
||
defaultOpen={
|
||
forceWorkspaceSidebarCollapsed
|
||
? false
|
||
: (defaultHistorySidebarOpen ??
|
||
(isGeneralQa || !compactWorkspaceSidebar))
|
||
}
|
||
open={forceWorkspaceSidebarCollapsed ? false : undefined}
|
||
onOpenChange={
|
||
forceWorkspaceSidebarCollapsed ? () => undefined : undefined
|
||
}
|
||
disableToggle={forceWorkspaceSidebarCollapsed}
|
||
>
|
||
{!workspaceSidebarHidden && (
|
||
<WorkspaceSidebar
|
||
compactChrome={compactWorkspaceSidebar}
|
||
forceCollapsed={forceWorkspaceSidebarCollapsed}
|
||
/>
|
||
)}
|
||
<SidebarInset
|
||
className={cn("min-w-0", insetOverflowHidden && "overflow-hidden")}
|
||
>
|
||
{showCompactTopActions ? (
|
||
<>
|
||
<CompactWorkspaceTopActions />
|
||
<div className="min-h-0 flex-1">{children}</div>
|
||
</>
|
||
) : (
|
||
children
|
||
)}
|
||
</SidebarInset>
|
||
<CommandPalette />
|
||
<Toaster position="top-center" />
|
||
</SidebarProvider>
|
||
);
|
||
}
|
||
|
||
export function ChatRuntime({ children }: { children: ReactNode }) {
|
||
return (
|
||
<SubtasksProvider>
|
||
<DocumentRewritePresentationProvider>
|
||
<ArtifactsProvider>
|
||
<PromptInputProvider>{children}</PromptInputProvider>
|
||
</ArtifactsProvider>
|
||
</DocumentRewritePresentationProvider>
|
||
</SubtasksProvider>
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 通用问答新对话内容区:默认渲染 ChatPage;当 URL 带 ``?agent=<id>``(点击左栏
|
||
* 「常用智能体」时由侧边栏写入)时,**在右侧问答区原地嵌入**该智能体的对话页
|
||
* (AgentChatPage),路由仍停留在 /chats/new,不跳转出去、不更换左侧栏。
|
||
* key 绑定 location.key —— 每次点击(即便同一 agent)都重开一个干净的会话。
|
||
*/
|
||
function NewChatContent() {
|
||
const location = useLocation();
|
||
const agentId = new URLSearchParams(location.search).get("agent");
|
||
if (agentId) {
|
||
return (
|
||
<AgentChatPage
|
||
key={`${agentId}:${location.key}`}
|
||
embeddedAgentId={agentId}
|
||
/>
|
||
);
|
||
}
|
||
return <ChatPage />;
|
||
}
|
||
|
||
function NewChatEntryRoute({
|
||
layoutProps,
|
||
}: {
|
||
layoutProps: {
|
||
pageSidebarCollapsed: boolean;
|
||
pageSidebarHidden: boolean;
|
||
workspaceSidebarHidden: boolean;
|
||
headerHidden: boolean;
|
||
compactWorkspaceSidebar: boolean;
|
||
compactActionsHidden: boolean;
|
||
pageSidebarDark: boolean;
|
||
forceWorkspaceSidebarCollapsed: boolean;
|
||
};
|
||
}) {
|
||
const location = useLocation();
|
||
const iframeEntryTarget = resolveChatsIframeEntryRedirect(
|
||
location.pathname,
|
||
location.search,
|
||
isEmbeddedHost(),
|
||
);
|
||
|
||
if (iframeEntryTarget) {
|
||
return <Navigate to={iframeEntryTarget} replace />;
|
||
}
|
||
|
||
// A bare external iframe historically opens the dedicated AgentSelector
|
||
// page. `resolveChatsIframeEntryRedirect` records that inferred context as
|
||
// `from=iframe`; explicit full-chat/presentation parameters stay here.
|
||
return (
|
||
<WorkspaceLayout {...layoutProps} defaultHistorySidebarOpen>
|
||
<ChatRuntime>
|
||
<NewChatContent />
|
||
</ChatRuntime>
|
||
</WorkspaceLayout>
|
||
);
|
||
}
|
||
|
||
function ChatsIndexEntryRoute({
|
||
layoutProps,
|
||
compactChrome,
|
||
}: {
|
||
layoutProps: {
|
||
pageSidebarCollapsed: boolean;
|
||
pageSidebarHidden: boolean;
|
||
workspaceSidebarHidden: boolean;
|
||
headerHidden: boolean;
|
||
compactWorkspaceSidebar: boolean;
|
||
compactActionsHidden: boolean;
|
||
pageSidebarDark: boolean;
|
||
forceWorkspaceSidebarCollapsed: boolean;
|
||
};
|
||
compactChrome: boolean;
|
||
}) {
|
||
const location = useLocation();
|
||
const iframeEntryTarget = resolveChatsIframeEntryRedirect(
|
||
location.pathname,
|
||
location.search,
|
||
isEmbeddedHost(),
|
||
);
|
||
|
||
if (iframeEntryTarget) {
|
||
return <Navigate to={iframeEntryTarget} replace />;
|
||
}
|
||
|
||
return (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<ChatsPage compactChrome={compactChrome} />
|
||
</WorkspaceLayout>
|
||
);
|
||
}
|
||
|
||
function ChatThreadEntryRoute({
|
||
layoutProps,
|
||
}: {
|
||
layoutProps: {
|
||
pageSidebarCollapsed: boolean;
|
||
pageSidebarHidden: boolean;
|
||
workspaceSidebarHidden: boolean;
|
||
headerHidden: boolean;
|
||
compactWorkspaceSidebar: boolean;
|
||
compactActionsHidden: boolean;
|
||
pageSidebarDark: boolean;
|
||
forceWorkspaceSidebarCollapsed: boolean;
|
||
};
|
||
}) {
|
||
const location = useLocation();
|
||
const iframeEntryTarget = resolveChatsIframeEntryRedirect(
|
||
location.pathname,
|
||
location.search,
|
||
isEmbeddedHost(),
|
||
);
|
||
|
||
if (iframeEntryTarget) {
|
||
return <Navigate to={iframeEntryTarget} replace />;
|
||
}
|
||
|
||
return (
|
||
<WorkspaceLayout {...layoutProps} defaultHistorySidebarOpen>
|
||
<ChatRuntime>
|
||
<AgentAwareChatThreadContent />
|
||
</ChatRuntime>
|
||
</WorkspaceLayout>
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Older bookmarks and a few historic list entries use the generic
|
||
* ``/chats/:thread_id`` path even for an agent-owned conversation. Recover the
|
||
* owning agent from the persisted thread record before rendering so those
|
||
* links keep the agent workspace (agent list and detail panel) instead of
|
||
* falling back to the general Q&A page. Task workspace threads intentionally
|
||
* remain on their explicit task routes because their surrounding layout is
|
||
* part of the task context.
|
||
*/
|
||
function AgentAwareChatThreadContent() {
|
||
const { thread_id: threadId } = useParams<{ thread_id: string }>();
|
||
const location = useLocation();
|
||
const [agentId, setAgentId] = useState<string | null>(null);
|
||
const [resolved, setResolved] = useState(false);
|
||
|
||
useEffect(() => {
|
||
let cancelled = false;
|
||
setAgentId(null);
|
||
setResolved(false);
|
||
|
||
if (!threadId) {
|
||
setResolved(true);
|
||
return () => {
|
||
cancelled = true;
|
||
};
|
||
}
|
||
|
||
void getAPIClient()
|
||
.threads.get(threadId)
|
||
.then((thread) => {
|
||
if (cancelled) return;
|
||
const record = thread as {
|
||
context?: { agent_id?: string; agent_name?: string } | null;
|
||
metadata?: Record<string, unknown> | null;
|
||
};
|
||
// Task-bound threads need their task host to preserve the purpose gate,
|
||
// action partitioning, and other task-specific controls.
|
||
const isTaskThread = typeof record.metadata?.taskId === "string";
|
||
setAgentId(isTaskThread ? null : (agentRefOfThread(record) ?? null));
|
||
})
|
||
.catch(() => {
|
||
if (!cancelled) setAgentId(null);
|
||
})
|
||
.finally(() => {
|
||
if (!cancelled) setResolved(true);
|
||
});
|
||
|
||
return () => {
|
||
cancelled = true;
|
||
};
|
||
}, [threadId]);
|
||
|
||
if (!resolved) {
|
||
return <div className="size-full bg-background" aria-busy="true" />;
|
||
}
|
||
|
||
if (threadId && agentId) {
|
||
return (
|
||
<Navigate
|
||
replace
|
||
to={`/page/workspace/agents/${encodeURIComponent(agentId)}/chats/${threadId}${location.search}`}
|
||
/>
|
||
);
|
||
}
|
||
|
||
return <ChatPage />;
|
||
}
|
||
|
||
/**
|
||
* `chats_iframe` is reserved for the Brainy postMessage protocol. `from=iframe`
|
||
* and `chatMode=iframe` opt into this page; ordinary iframe/Wujie embedding
|
||
* remains on the normal full chat workspace.
|
||
*/
|
||
function IframeChatEntryRoute({
|
||
layoutProps,
|
||
hasThreadParam,
|
||
}: {
|
||
layoutProps: {
|
||
pageSidebarCollapsed: boolean;
|
||
pageSidebarHidden: boolean;
|
||
workspaceSidebarHidden: boolean;
|
||
headerHidden: boolean;
|
||
compactWorkspaceSidebar: boolean;
|
||
compactActionsHidden: boolean;
|
||
pageSidebarDark: boolean;
|
||
forceWorkspaceSidebarCollapsed: boolean;
|
||
};
|
||
hasThreadParam: boolean;
|
||
}) {
|
||
const location = useLocation();
|
||
const standardChatTarget = resolveStandardChatFallback(
|
||
location.pathname,
|
||
location.search,
|
||
);
|
||
|
||
if (standardChatTarget) {
|
||
return <Navigate to={standardChatTarget} replace />;
|
||
}
|
||
|
||
return (
|
||
<WorkspaceLayout
|
||
{...layoutProps}
|
||
pageSidebarHidden
|
||
workspaceSidebarHidden
|
||
wrapperOverflowHidden={hasThreadParam}
|
||
insetOverflowHidden={hasThreadParam}
|
||
>
|
||
<ChatRuntime>
|
||
<IframeChatPage />
|
||
</ChatRuntime>
|
||
</WorkspaceLayout>
|
||
);
|
||
}
|
||
|
||
export default function WorkspaceRoutes({
|
||
pageSidebarCollapsed = false,
|
||
pageSidebarHidden = false,
|
||
workspaceSidebarHidden = false,
|
||
headerHidden = false,
|
||
compactActionsHidden = false,
|
||
compactWorkspaceSidebar = false,
|
||
pageSidebarDark = false,
|
||
forceWorkspaceSidebarCollapsed = false,
|
||
}: {
|
||
pageSidebarCollapsed?: boolean;
|
||
pageSidebarHidden?: boolean;
|
||
workspaceSidebarHidden?: boolean;
|
||
headerHidden?: boolean;
|
||
compactActionsHidden?: boolean;
|
||
compactWorkspaceSidebar?: boolean;
|
||
pageSidebarDark?: boolean;
|
||
forceWorkspaceSidebarCollapsed?: boolean;
|
||
}) {
|
||
const layoutProps = {
|
||
pageSidebarCollapsed,
|
||
pageSidebarHidden,
|
||
workspaceSidebarHidden,
|
||
headerHidden,
|
||
compactActionsHidden,
|
||
compactWorkspaceSidebar,
|
||
pageSidebarDark,
|
||
forceWorkspaceSidebarCollapsed,
|
||
};
|
||
|
||
return (
|
||
<RouteBaseProvider value="/page/workspace">
|
||
<Routes>
|
||
<Route index element={<Navigate to="chats/new" replace />} />
|
||
<Route
|
||
path="chats"
|
||
element={
|
||
<ChatsIndexEntryRoute
|
||
layoutProps={layoutProps}
|
||
compactChrome={headerHidden && !compactActionsHidden}
|
||
/>
|
||
}
|
||
/>
|
||
<Route
|
||
path="chats/new"
|
||
element={<NewChatEntryRoute layoutProps={layoutProps} />}
|
||
/>
|
||
<Route
|
||
path="chats/:thread_id"
|
||
element={<ChatThreadEntryRoute layoutProps={layoutProps} />}
|
||
/>
|
||
<Route
|
||
path="library"
|
||
element={
|
||
<WorkspaceLayout
|
||
{...layoutProps}
|
||
workspaceSidebarHidden={
|
||
layoutProps.workspaceSidebarHidden ||
|
||
!layoutProps.pageSidebarHidden
|
||
}
|
||
>
|
||
<ArtifactLibraryPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="chats_iframe/new"
|
||
element={
|
||
<IframeChatEntryRoute
|
||
layoutProps={layoutProps}
|
||
hasThreadParam={false}
|
||
/>
|
||
}
|
||
/>
|
||
<Route
|
||
path="chats_iframe/:thread_id"
|
||
element={
|
||
<IframeChatEntryRoute layoutProps={layoutProps} hasThreadParam />
|
||
}
|
||
/>
|
||
<Route
|
||
path="agents"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<AgentsPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="agents/new"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<NewAgentPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
{/* 从「常用对话」点击智能体创建(bootstrap)会话回到创建页并恢复该会话。
|
||
key 绑定 thread_id —— 切换不同 bootstrap 会话时重挂一个干净的页面。 */}
|
||
<Route
|
||
path="agents/new/:thread_id"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<NewAgentPageWithKey />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
{/* 舆情分析虚拟智能体:静态段优先于 :agent_id 命中,走 Gateway 转发
|
||
外部 AG-UI 网关的专用对话页(不在后端 /api/agents 落库)。 */}
|
||
<Route
|
||
path="agents/sentiment-analysis/chats"
|
||
element={
|
||
isSentimentAgentVisible() ? (
|
||
<Navigate
|
||
to="/page/workspace/agents/sentiment-analysis/chats/new"
|
||
replace
|
||
/>
|
||
) : (
|
||
<Navigate to="/page/workspace/agents" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="agents/sentiment-analysis/chats/:thread_id"
|
||
element={
|
||
isSentimentAgentVisible() ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<SentimentAgentChatPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/agents" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="agents/:agent_id/chats"
|
||
element={<Navigate to="/page/workspace/agents" replace />}
|
||
/>
|
||
<Route
|
||
path="agents/:agent_id/chats/:thread_id"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<ChatRuntime>
|
||
<AgentChatPage
|
||
showAgentList
|
||
compactAppearanceActions={
|
||
headerHidden && !compactActionsHidden ? (
|
||
<CompactAppearanceActions />
|
||
) : undefined
|
||
}
|
||
/>
|
||
</ChatRuntime>
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="skills"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<SkillsPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="skills/evolution"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<SkillEvolutionPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="memory"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<MemoryPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="ai-writing/*"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<AIWritingPage
|
||
hideAdvancedFields
|
||
compactChrome={headerHidden && !compactActionsHidden}
|
||
/>
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="deep-research"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<ChatRuntime>
|
||
<DeepResearchPage />
|
||
</ChatRuntime>
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="deep-research/structures/new"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<ChatRuntime>
|
||
<ReportStructureCreatePage />
|
||
</ChatRuntime>
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="enterprise-research"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<EnterpriseResearchPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="studio/notebooks"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<StudioNotebooksPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="studio/notebooks/:notebookId"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<ChatRuntime>
|
||
<NotebookDetailPageWithKey />
|
||
</ChatRuntime>
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="roundtable"
|
||
element={
|
||
<Navigate to="/page/workspace/roundtable/planning" replace />
|
||
}
|
||
/>
|
||
<Route
|
||
path="roundtable/planning"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<ChatRuntime>
|
||
<RoundtablePlanningPage
|
||
compactChrome={headerHidden && !compactActionsHidden}
|
||
/>
|
||
</ChatRuntime>
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="roundtable/8bf"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<ChatRuntime>
|
||
<EightBfRoundtablePage
|
||
compactChrome={headerHidden && !compactActionsHidden}
|
||
/>
|
||
</ChatRuntime>
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="roundtable/position"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<ChatRuntime>
|
||
<PositionRoundtablePage
|
||
compactChrome={headerHidden && !compactActionsHidden}
|
||
/>
|
||
</ChatRuntime>
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="roundtable/chains"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<BusinessChainEditorPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="business-chains"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<BusinessChainListPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
{/* AgentScope 报告协作工作台:左对话(复用 MessageList)/右画布的独立新页面,
|
||
不与旧多智能体会商共享状态。key 绑定 sessionId——切换会话重挂干净的页面。 */}
|
||
<Route
|
||
path="report-collaboration"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<ReportCollaborationPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="report-collaboration/:sessionId"
|
||
element={<ReportCollaborationSessionRoute layoutProps={layoutProps} />}
|
||
/>
|
||
{/* Phase 7 临时预览入口:派活链路流程图弹窗(假数据自测,接通后台后可删) */}
|
||
<Route
|
||
path="roundtable/chain-preview"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<DispatchChainPreviewPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="knowledge"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<LlmWikiKnowledgePage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="knowledge/graph"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<LlmWikiKnowledgePage mode="graph" />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="knowledge/assistant"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<AssistantKnowledgePage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="knowledge/:noteId/wiki/view"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<WeKnoraWikiPageView />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="knowledge/:noteId/wiki"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<WeKnoraWikiManagementPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="knowledge/:noteId/packages"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<WeKnoraPackageTransfersPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="knowledge/markdown-editor"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<MarkdownDocxEditorPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="knowledge/:noteId"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<LlmWikiKnowledgePage mode="detail" />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="scheduled"
|
||
element={<Navigate to="/page/workspace/scheduled/tasks" replace />}
|
||
/>
|
||
<Route
|
||
path="scheduled/tasks"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<ScheduledTasksPage
|
||
compactChrome={headerHidden && !compactActionsHidden}
|
||
/>
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="scheduled/runs/:run_id"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<ScheduledTaskRunDetailPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
{/* Unified admin user hub */}
|
||
<Route
|
||
path="admin"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps} pageSidebarHidden>
|
||
<AdminUserHubPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
{/* Legacy redirects */}
|
||
<Route
|
||
path="admin/users"
|
||
element={<Navigate to="/page/workspace/admin" replace />}
|
||
/>
|
||
<Route
|
||
path="admin/user-activity"
|
||
element={<Navigate to="/page/workspace/admin" replace />}
|
||
/>
|
||
<Route
|
||
path="admin/memory"
|
||
element={<Navigate to="/page/workspace/admin" replace />}
|
||
/>
|
||
<Route
|
||
path="admin/scheduled-tasks"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<AdminScheduledTasksPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="admin/agents"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<AdminAgentsPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="admin/skills"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<AdminSkillsPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="admin/tags"
|
||
element={
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<TagsPage />
|
||
</WorkspaceLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="admin/plugins"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<PluginManagementPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="admin/positions"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<PositionsPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="admin/position-roles"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<PositionRolesPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="settings/recommended-questions"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<RecommendedQuestionsPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="settings/prompt-prefix"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<PromptPrefixSettingsPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="settings/private-square"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<PrivateSquareSettingsPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="admin/models"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<ModelManagementPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="settings/model-concurrency"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<ModelConcurrencySettingsPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="settings/publish-squares"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<PublishSquaresSettingsPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="settings/skill-compression"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<SkillCompressionSettingsPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="settings/llm-metrics"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<LlmMetricsPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
<Route
|
||
path="settings/checkpoint-migration"
|
||
element={
|
||
getStoredAuth()?.system_role === "admin" ? (
|
||
<WorkspaceLayout {...layoutProps}>
|
||
<CheckpointMigrationPage />
|
||
</WorkspaceLayout>
|
||
) : (
|
||
<Navigate to="/page/workspace/chats/new" replace />
|
||
)
|
||
}
|
||
/>
|
||
</Routes>
|
||
</RouteBaseProvider>
|
||
);
|
||
}
|
||
|
||
// Suppress unused import warnings for pages referenced only in some branches
|
||
void UsersPage;
|
||
void AdminUserActivityPage;
|
||
void AdminMemoryPage;
|