diff --git a/crates/zclaw-kernel/src/kernel/messaging.rs b/crates/zclaw-kernel/src/kernel/messaging.rs index 33e91ad..20e4a1a 100644 --- a/crates/zclaw-kernel/src/kernel/messaging.rs +++ b/crates/zclaw-kernel/src/kernel/messaging.rs @@ -4,12 +4,13 @@ use tokio::sync::mpsc; use zclaw_types::{AgentId, Result}; /// Chat mode configuration passed from the frontend. -/// Controls thinking, reasoning, and plan mode behavior. +/// Controls thinking, reasoning, plan mode, and sub-agent behavior. #[derive(Debug, Clone)] pub struct ChatModeConfig { pub thinking_enabled: Option, pub reasoning_effort: Option, pub plan_mode: Option, + pub subagent_enabled: Option, } use zclaw_runtime::{AgentLoop, tool::builtin::PathValidator}; @@ -45,7 +46,8 @@ impl Kernel { let model = self.config.model().to_string(); // Create agent loop with model configuration - let tools = self.create_tool_registry(); + let subagent_enabled = chat_mode.as_ref().and_then(|m| m.subagent_enabled).unwrap_or(false); + let tools = self.create_tool_registry(subagent_enabled); let mut loop_runner = AgentLoop::new( *agent_id, self.driver.clone(), @@ -92,7 +94,10 @@ impl Kernel { } // Build system prompt with skill information injected - let system_prompt = self.build_system_prompt_with_skills(agent_config.system_prompt.as_ref()).await; + let system_prompt = self.build_system_prompt_with_skills( + agent_config.system_prompt.as_ref(), + subagent_enabled, + ).await; let loop_runner = loop_runner.with_system_prompt(&system_prompt); // Run the loop @@ -147,7 +152,8 @@ impl Kernel { let model = self.config.model().to_string(); // Create agent loop with model configuration - let tools = self.create_tool_registry(); + let subagent_enabled = chat_mode.as_ref().and_then(|m| m.subagent_enabled).unwrap_or(false); + let tools = self.create_tool_registry(subagent_enabled); let mut loop_runner = AgentLoop::new( *agent_id, self.driver.clone(), @@ -197,7 +203,10 @@ impl Kernel { // Use external prompt if provided, otherwise build default let system_prompt = match system_prompt_override { Some(prompt) => prompt, - None => self.build_system_prompt_with_skills(agent_config.system_prompt.as_ref()).await, + None => self.build_system_prompt_with_skills( + agent_config.system_prompt.as_ref(), + subagent_enabled, + ).await, }; let loop_runner = loop_runner.with_system_prompt(&system_prompt); @@ -206,8 +215,13 @@ impl Kernel { loop_runner.run_streaming(session_id, message).await } - /// Build a system prompt with skill information injected - pub(super) async fn build_system_prompt_with_skills(&self, base_prompt: Option<&String>) -> String { + /// Build a system prompt with skill information injected. + /// When `subagent_enabled` is true, adds sub-agent delegation instructions. + pub(super) async fn build_system_prompt_with_skills( + &self, + base_prompt: Option<&String>, + subagent_enabled: bool, + ) -> String { // Get skill list asynchronously let skills = self.skills.list().await; @@ -215,7 +229,8 @@ impl Kernel { .map(|p| p.clone()) .unwrap_or_else(|| "You are a helpful AI assistant.".to_string()); - // Inject skill information with categories + // Inject skill metadata only (progressive loading pattern from DeerFlow). + // Full skill content is loaded on-demand via `load_skill_content` tool. if !skills.is_empty() { prompt.push_str("\n\n## Available Skills\n\n"); prompt.push_str("You have access to specialized skills. Analyze user intent and autonomously call `execute_skill` with the appropriate skill_id.\n\n"); @@ -245,6 +260,21 @@ impl Kernel { prompt.push_str("User: \"分析腾讯财报\" → Intent: Financial analysis → Call: execute_skill(\"finance-tracker\", {...})\n"); } + // Sub-agent delegation instructions (Ultra mode only) + if subagent_enabled { + prompt.push_str("\n\n## Sub-Agent Delegation\n\n"); + prompt.push_str("You can delegate complex sub-tasks to sub-agents using the `task` tool. This enables parallel execution of independent work.\n\n"); + prompt.push_str("### When to use sub-agents:\n"); + prompt.push_str("- Complex tasks that can be decomposed into independent parallel sub-tasks\n"); + prompt.push_str("- Research tasks requiring multiple independent searches\n"); + prompt.push_str("- Tasks requiring different expertise areas simultaneously\n\n"); + prompt.push_str("### Guidelines:\n"); + prompt.push_str("- Break complex work into clear, self-contained sub-tasks\n"); + prompt.push_str("- Each sub-task should have a clear objective and expected output\n"); + prompt.push_str("- Synthesize sub-agent results into a coherent final response\n"); + prompt.push_str("- Maximum 3 concurrent sub-agents — batch if more are needed\n"); + } + prompt } diff --git a/crates/zclaw-kernel/src/kernel/mod.rs b/crates/zclaw-kernel/src/kernel/mod.rs index ed2dc61..f875403 100644 --- a/crates/zclaw-kernel/src/kernel/mod.rs +++ b/crates/zclaw-kernel/src/kernel/mod.rs @@ -162,18 +162,22 @@ impl Kernel { }) } - /// Create a tool registry with built-in tools - pub(crate) fn create_tool_registry(&self) -> ToolRegistry { + /// Create a tool registry with built-in tools. + /// When `subagent_enabled` is false, TaskTool is excluded to prevent + /// the LLM from attempting sub-agent delegation in non-Ultra modes. + pub(crate) fn create_tool_registry(&self, subagent_enabled: bool) -> ToolRegistry { let mut tools = ToolRegistry::new(); zclaw_runtime::tool::builtin::register_builtin_tools(&mut tools); - // Register TaskTool with driver and memory for sub-agent delegation - let task_tool = zclaw_runtime::tool::builtin::TaskTool::new( - self.driver.clone(), - self.memory.clone(), - self.config.model(), - ); - tools.register(Box::new(task_tool)); + // Register TaskTool only when sub-agent mode is enabled (Ultra mode) + if subagent_enabled { + let task_tool = zclaw_runtime::tool::builtin::TaskTool::new( + self.driver.clone(), + self.memory.clone(), + self.config.model(), + ); + tools.register(Box::new(task_tool)); + } tools } diff --git a/desktop/src-tauri/src/kernel_commands/chat.rs b/desktop/src-tauri/src/kernel_commands/chat.rs index 45584c8..94e2cd7 100644 --- a/desktop/src-tauri/src/kernel_commands/chat.rs +++ b/desktop/src-tauri/src/kernel_commands/chat.rs @@ -60,6 +60,9 @@ pub struct StreamChatRequest { /// Enable plan mode #[serde(default)] pub plan_mode: Option, + /// Enable sub-agent delegation (Ultra mode only) + #[serde(default)] + pub subagent_enabled: Option, } // --------------------------------------------------------------------------- @@ -216,6 +219,7 @@ pub async fn agent_chat_stream( thinking_enabled: request.thinking_enabled, reasoning_effort: request.reasoning_effort.clone(), plan_mode: request.plan_mode, + subagent_enabled: request.subagent_enabled, }; let rx = kernel.send_message_stream_with_prompt( diff --git a/desktop/src/App.tsx b/desktop/src/App.tsx index df3340a..d86614f 100644 --- a/desktop/src/App.tsx +++ b/desktop/src/App.tsx @@ -1,12 +1,9 @@ import { useState, useEffect, useCallback, useRef } from 'react'; -import { motion, AnimatePresence } from 'framer-motion'; import './index.css'; -import { Sidebar, MainViewType } from './components/Sidebar'; +import { Sidebar } from './components/Sidebar'; import { ChatArea } from './components/ChatArea'; import { RightPanel } from './components/RightPanel'; import { SettingsLayout } from './components/Settings/SettingsLayout'; -import { AutomationPanel } from './components/Automation'; -import { SkillMarket } from './components/SkillMarket'; import { AgentOnboardingWizard } from './components/AgentOnboardingWizard'; import { HandApprovalModal } from './components/HandApprovalModal'; import { TopBar } from './components/TopBar'; @@ -17,7 +14,7 @@ import { useHandStore, type HandRun } from './store/handStore'; import { useChatStore } from './store/chatStore'; import { initializeStores } from './store'; import { getStoredGatewayToken } from './lib/gateway-client'; -import { pageVariants, defaultTransition, fadeInVariants } from './lib/animations'; + import { Loader2 } from 'lucide-react'; import { isTauriRuntime, getLocalGatewayStatus, startLocalGateway } from './lib/tauri-gateway'; import { LoginPage } from './components/LoginPage'; @@ -49,7 +46,6 @@ function BootstrapScreen({ status }: { status: string }) { function App() { const [view, setView] = useState('main'); - const [mainContentView, setMainContentView] = useState('chat'); const [bootstrapping, setBootstrapping] = useState(true); const [bootstrapStatus, setBootstrapStatus] = useState('Initializing...'); const [showOnboarding, setShowOnboarding] = useState(false); @@ -379,11 +375,6 @@ function App() { } }; - // 处理主视图切换 - const handleMainViewChange = (view: MainViewType) => { - setMainContentView(view); - }; - // 登录门禁 — 必须登录才能使用 if (isRestoring) { return ; @@ -457,7 +448,6 @@ function App() { {/* 左侧边栏 */} setView('settings')} - onMainViewChange={handleMainViewChange} /> {/* 主内容区 */} @@ -468,62 +458,8 @@ function App() { onOpenDetail={() => setShowDetailDrawer(true)} /> - {/* 内容区域 */} - - - {mainContentView === 'automation' ? ( - -
- - 自动化 -
- -
- ) : mainContentView === 'skills' ? ( - -
- - 技能市场 -
-
- -
-
- ) : ( - - )} -
-
+ {/* 聊天区域 */} + {/* 详情抽屉 - 按需显示 */} diff --git a/desktop/src/components/Sidebar.tsx b/desktop/src/components/Sidebar.tsx index 2baa0f2..3f8d3aa 100644 --- a/desktop/src/components/Sidebar.tsx +++ b/desktop/src/components/Sidebar.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { - SquarePen, MessageSquare, Bot, Search, X, Settings, Zap, Sparkles + SquarePen, MessageSquare, Bot, Search, X, Settings } from 'lucide-react'; import { ConversationList } from './ConversationList'; import { CloneManager } from './CloneManager'; @@ -15,7 +15,7 @@ const sidebarTabVariants = { exit: { opacity: 0, transition: { duration: 0.1 } }, }; -export type MainViewType = 'chat' | 'automation' | 'skills'; +export type MainViewType = 'chat'; interface SidebarProps { onOpenSettings?: () => void; @@ -89,23 +89,6 @@ export function Sidebar({ 智能体 - {/* Divider between primary nav and secondary tools */} -
- - -
{/* Divider */} diff --git a/desktop/src/lib/gateway-client.ts b/desktop/src/lib/gateway-client.ts index 372e47b..d811d16 100644 --- a/desktop/src/lib/gateway-client.ts +++ b/desktop/src/lib/gateway-client.ts @@ -476,6 +476,7 @@ export class GatewayClient { thinking_enabled?: boolean; reasoning_effort?: string; plan_mode?: boolean; + subagent_enabled?: boolean; } ): Promise<{ runId: string }> { const agentId = opts?.agentId || this.defaultAgentId; @@ -489,6 +490,7 @@ export class GatewayClient { thinking_enabled: opts?.thinking_enabled, reasoning_effort: opts?.reasoning_effort, plan_mode: opts?.plan_mode, + subagent_enabled: opts?.subagent_enabled, }; this.fetchDefaultAgentId().then(() => { const resolvedAgentId = this.defaultAgentId; @@ -529,6 +531,7 @@ export class GatewayClient { thinking_enabled?: boolean; reasoning_effort?: string; plan_mode?: boolean; + subagent_enabled?: boolean; } ): void { // Close existing connection if any @@ -570,6 +573,9 @@ export class GatewayClient { if (chatModeOpts?.plan_mode !== undefined) { chatRequest.plan_mode = chatModeOpts.plan_mode; } + if (chatModeOpts?.subagent_enabled !== undefined) { + chatRequest.subagent_enabled = chatModeOpts.subagent_enabled; + } this.zclawWs?.send(JSON.stringify(chatRequest)); }; diff --git a/desktop/src/lib/kernel-chat.ts b/desktop/src/lib/kernel-chat.ts index 4251c2e..abc99fc 100644 --- a/desktop/src/lib/kernel-chat.ts +++ b/desktop/src/lib/kernel-chat.ts @@ -59,6 +59,7 @@ export function installChatMethods(ClientClass: { prototype: KernelClient }): vo thinking_enabled?: boolean; reasoning_effort?: string; plan_mode?: boolean; + subagent_enabled?: boolean; } ): Promise<{ runId: string }> { const runId = crypto.randomUUID(); @@ -185,6 +186,7 @@ export function installChatMethods(ClientClass: { prototype: KernelClient }): vo thinkingEnabled: opts?.thinking_enabled, reasoningEffort: opts?.reasoning_effort, planMode: opts?.plan_mode, + subagentEnabled: opts?.subagent_enabled, }, }); } catch (err: unknown) { diff --git a/desktop/src/lib/kernel-client.ts b/desktop/src/lib/kernel-client.ts index 17c892a..ac3a000 100644 --- a/desktop/src/lib/kernel-client.ts +++ b/desktop/src/lib/kernel-client.ts @@ -403,7 +403,7 @@ export interface KernelClient { // Chat (kernel-chat.ts) chat(message: string, opts?: { sessionKey?: string; agentId?: string }): Promise<{ runId: string; sessionId?: string; response?: string }>; - chatStream(message: string, callbacks: import('./kernel-types').StreamCallbacks, opts?: { sessionKey?: string; agentId?: string; thinking_enabled?: boolean; reasoning_effort?: string; plan_mode?: boolean }): Promise<{ runId: string }>; + chatStream(message: string, callbacks: import('./kernel-types').StreamCallbacks, opts?: { sessionKey?: string; agentId?: string; thinking_enabled?: boolean; reasoning_effort?: string; plan_mode?: boolean; subagent_enabled?: boolean }): Promise<{ runId: string }>; cancelStream(sessionId: string): Promise; fetchDefaultAgentId(): Promise; setDefaultAgentId(agentId: string): void; diff --git a/desktop/src/lib/saas-relay-client.ts b/desktop/src/lib/saas-relay-client.ts index 0eb4eb9..1c0df57 100644 --- a/desktop/src/lib/saas-relay-client.ts +++ b/desktop/src/lib/saas-relay-client.ts @@ -108,6 +108,7 @@ export function createSaaSRelayGatewayClient( thinking_enabled?: boolean; reasoning_effort?: string; plan_mode?: boolean; + subagent_enabled?: boolean; }, ): Promise<{ runId: string }> { const runId = `run_${Date.now()}`; diff --git a/desktop/src/store/chat/streamStore.ts b/desktop/src/store/chat/streamStore.ts index 5794c52..a5b4bcd 100644 --- a/desktop/src/store/chat/streamStore.ts +++ b/desktop/src/store/chat/streamStore.ts @@ -425,6 +425,7 @@ export const useStreamStore = create()( thinking_enabled: get().getChatModeConfig().thinking_enabled, reasoning_effort: get().getChatModeConfig().reasoning_effort, plan_mode: get().getChatModeConfig().plan_mode, + subagent_enabled: get().getChatModeConfig().subagent_enabled, } );