From acf8a22c0ac662720423d28e0f3c8f74c417567d Mon Sep 17 00:00:00 2001 From: lpf Date: Mon, 9 Mar 2026 11:24:31 +0800 Subject: [PATCH] fix: enforce subagent prompt files and refine webui --- pkg/agent/loop.go | 3 - pkg/agent/runtime_admin.go | 2 - pkg/agent/runtime_admin_test.go | 2 - pkg/agent/subagent_prompt_test.go | 13 +-- pkg/api/server.go | 76 +++++++------- pkg/config/config.go | 14 ++- pkg/tools/subagent.go | 7 -- pkg/tools/subagent_config_manager.go | 5 - pkg/tools/subagent_config_tool.go | 1 - pkg/tools/subagent_config_tool_test.go | 1 - pkg/tools/subagent_profile.go | 8 -- pkg/tools/subagent_profile_test.go | 1 - pkg/tools/subagent_runtime_control_test.go | 1 - webui/src/components/Header.tsx | 3 +- webui/src/components/NavItem.tsx | 22 +++-- webui/src/components/Sidebar.tsx | 29 +++--- webui/src/context/UIContext.tsx | 21 +++- webui/src/i18n/index.ts | 2 + webui/src/index.css | 110 ++++++++++----------- webui/src/pages/Dashboard.tsx | 12 +-- webui/src/pages/Nodes.tsx | 49 ++++++--- webui/src/pages/Skills.tsx | 17 +++- webui/src/pages/SubagentProfiles.tsx | 32 +++--- webui/src/pages/Subagents.tsx | 2 - webui/src/pages/TaskAudit.tsx | 35 +++++-- 25 files changed, 257 insertions(+), 211 deletions(-) diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 80f6cd1..7f4154e 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -464,9 +464,6 @@ func (al *AgentLoop) buildSubagentTaskInput(task *tools.SubagentTask) string { return fmt.Sprintf("Role Profile Policy (%s):\n%s\n\nTask:\n%s", promptFile, promptText, taskText) } } - if prompt := strings.TrimSpace(task.SystemPrompt); prompt != "" { - return fmt.Sprintf("Role Profile Prompt:\n%s\n\nTask:\n%s", prompt, taskText) - } return taskText } diff --git a/pkg/agent/runtime_admin.go b/pkg/agent/runtime_admin.go index 0dff513..5b59e89 100644 --- a/pkg/agent/runtime_admin.go +++ b/pkg/agent/runtime_admin.go @@ -136,7 +136,6 @@ func (al *AgentLoop) HandleSubagentRuntime(ctx context.Context, action string, a "display_name": subcfg.DisplayName, "role": subcfg.Role, "description": subcfg.Description, - "system_prompt": subcfg.SystemPrompt, "system_prompt_file": subcfg.SystemPromptFile, "prompt_file_found": promptFileFound, "memory_namespace": subcfg.MemoryNamespace, @@ -167,7 +166,6 @@ func (al *AgentLoop) HandleSubagentRuntime(ctx context.Context, action string, a "display_name": profile.Name, "role": profile.Role, "description": "Node-registered remote main agent branch", - "system_prompt": profile.SystemPrompt, "system_prompt_file": profile.SystemPromptFile, "prompt_file_found": false, "memory_namespace": profile.MemoryNamespace, diff --git a/pkg/agent/runtime_admin_test.go b/pkg/agent/runtime_admin_test.go index f6edbe5..ae2927d 100644 --- a/pkg/agent/runtime_admin_test.go +++ b/pkg/agent/runtime_admin_test.go @@ -78,7 +78,6 @@ func TestHandleSubagentRuntimeUpsertConfigSubagent(t *testing.T) { "role": "testing", "notify_main_policy": "internal_only", "display_name": "Review Agent", - "system_prompt": "review changes", "system_prompt_file": "agents/reviewer/AGENT.md", "routing_keywords": []interface{}{"review", "regression"}, "tool_allowlist": []interface{}{"shell", "sessions"}, @@ -129,7 +128,6 @@ func TestHandleSubagentRuntimeRegistryAndToggleEnabled(t *testing.T) { Type: "worker", Role: "testing", DisplayName: "Test Agent", - SystemPrompt: "run tests", SystemPromptFile: "agents/tester/AGENT.md", MemoryNamespace: "tester", Tools: config.SubagentToolsConfig{ diff --git a/pkg/agent/subagent_prompt_test.go b/pkg/agent/subagent_prompt_test.go index 3414248..c8d18bd 100644 --- a/pkg/agent/subagent_prompt_test.go +++ b/pkg/agent/subagent_prompt_test.go @@ -20,7 +20,6 @@ func TestBuildSubagentTaskInputPrefersPromptFile(t *testing.T) { loop := &AgentLoop{workspace: workspace} input := loop.buildSubagentTaskInput(&tools.SubagentTask{ Task: "implement login flow", - SystemPrompt: "inline-fallback", SystemPromptFile: "agents/coder/AGENT.md", }) if !strings.Contains(input, "coder-file-policy") { @@ -31,13 +30,15 @@ func TestBuildSubagentTaskInputPrefersPromptFile(t *testing.T) { } } -func TestBuildSubagentTaskInputFallsBackToInlinePrompt(t *testing.T) { +func TestBuildSubagentTaskInputWithoutPromptFileUsesTaskOnly(t *testing.T) { loop := &AgentLoop{workspace: t.TempDir()} input := loop.buildSubagentTaskInput(&tools.SubagentTask{ - Task: "run regression", - SystemPrompt: "test inline prompt", + Task: "run regression", }) - if !strings.Contains(input, "test inline prompt") { - t.Fatalf("expected inline prompt in task input, got: %s", input) + if strings.Contains(input, "test inline prompt") { + t.Fatalf("did not expect inline prompt fallback, got: %s", input) + } + if !strings.Contains(input, "run regression") { + t.Fatalf("expected task input to contain task, got: %s", input) } } diff --git a/pkg/api/server.go b/pkg/api/server.go index a92e80e..c3a14e5 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -4007,19 +4007,19 @@ func (s *Server) handleWebUISubagentProfiles(w http.ResponseWriter, r *http.Requ _ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "deleted": true, "agent_id": agentID}) case http.MethodPost: var body struct { - Action string `json:"action"` - AgentID string `json:"agent_id"` - Name string `json:"name"` - Role string `json:"role"` - SystemPrompt string `json:"system_prompt"` - MemoryNamespace string `json:"memory_namespace"` - Status string `json:"status"` - ToolAllowlist []string `json:"tool_allowlist"` - MaxRetries *int `json:"max_retries"` - RetryBackoffMS *int `json:"retry_backoff_ms"` - TimeoutSec *int `json:"timeout_sec"` - MaxTaskChars *int `json:"max_task_chars"` - MaxResultChars *int `json:"max_result_chars"` + Action string `json:"action"` + AgentID string `json:"agent_id"` + Name string `json:"name"` + Role string `json:"role"` + SystemPromptFile string `json:"system_prompt_file"` + MemoryNamespace string `json:"memory_namespace"` + Status string `json:"status"` + ToolAllowlist []string `json:"tool_allowlist"` + MaxRetries *int `json:"max_retries"` + RetryBackoffMS *int `json:"retry_backoff_ms"` + TimeoutSec *int `json:"timeout_sec"` + MaxTaskChars *int `json:"max_task_chars"` + MaxResultChars *int `json:"max_result_chars"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { http.Error(w, "invalid json", http.StatusBadRequest) @@ -4045,18 +4045,18 @@ func (s *Server) handleWebUISubagentProfiles(w http.ResponseWriter, r *http.Requ return } profile, err := store.Upsert(tools.SubagentProfile{ - AgentID: agentID, - Name: body.Name, - Role: body.Role, - SystemPrompt: body.SystemPrompt, - MemoryNamespace: body.MemoryNamespace, - Status: body.Status, - ToolAllowlist: body.ToolAllowlist, - MaxRetries: derefInt(body.MaxRetries), - RetryBackoff: derefInt(body.RetryBackoffMS), - TimeoutSec: derefInt(body.TimeoutSec), - MaxTaskChars: derefInt(body.MaxTaskChars), - MaxResultChars: derefInt(body.MaxResultChars), + AgentID: agentID, + Name: body.Name, + Role: body.Role, + SystemPromptFile: body.SystemPromptFile, + MemoryNamespace: body.MemoryNamespace, + Status: body.Status, + ToolAllowlist: body.ToolAllowlist, + MaxRetries: derefInt(body.MaxRetries), + RetryBackoff: derefInt(body.RetryBackoffMS), + TimeoutSec: derefInt(body.TimeoutSec), + MaxTaskChars: derefInt(body.MaxTaskChars), + MaxResultChars: derefInt(body.MaxResultChars), }) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) @@ -4076,7 +4076,7 @@ func (s *Server) handleWebUISubagentProfiles(w http.ResponseWriter, r *http.Requ next := *existing next.Name = body.Name next.Role = body.Role - next.SystemPrompt = body.SystemPrompt + next.SystemPromptFile = body.SystemPromptFile next.MemoryNamespace = body.MemoryNamespace if body.Status != "" { next.Status = body.Status @@ -4134,18 +4134,18 @@ func (s *Server) handleWebUISubagentProfiles(w http.ResponseWriter, r *http.Requ _ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "deleted": true, "agent_id": agentID}) case "upsert": profile, err := store.Upsert(tools.SubagentProfile{ - AgentID: agentID, - Name: body.Name, - Role: body.Role, - SystemPrompt: body.SystemPrompt, - MemoryNamespace: body.MemoryNamespace, - Status: body.Status, - ToolAllowlist: body.ToolAllowlist, - MaxRetries: derefInt(body.MaxRetries), - RetryBackoff: derefInt(body.RetryBackoffMS), - TimeoutSec: derefInt(body.TimeoutSec), - MaxTaskChars: derefInt(body.MaxTaskChars), - MaxResultChars: derefInt(body.MaxResultChars), + AgentID: agentID, + Name: body.Name, + Role: body.Role, + SystemPromptFile: body.SystemPromptFile, + MemoryNamespace: body.MemoryNamespace, + Status: body.Status, + ToolAllowlist: body.ToolAllowlist, + MaxRetries: derefInt(body.MaxRetries), + RetryBackoff: derefInt(body.RetryBackoffMS), + TimeoutSec: derefInt(body.TimeoutSec), + MaxTaskChars: derefInt(body.MaxTaskChars), + MaxResultChars: derefInt(body.MaxResultChars), }) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) diff --git a/pkg/config/config.go b/pkg/config/config.go index 0fc50a7..88b8995 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -77,7 +77,6 @@ type SubagentConfig struct { DisplayName string `json:"display_name,omitempty"` Role string `json:"role,omitempty"` Description string `json:"description,omitempty"` - SystemPrompt string `json:"system_prompt,omitempty"` SystemPromptFile string `json:"system_prompt_file,omitempty"` MemoryNamespace string `json:"memory_namespace,omitempty"` AcceptFrom []string `json:"accept_from,omitempty"` @@ -88,6 +87,19 @@ type SubagentConfig struct { Runtime SubagentRuntimeConfig `json:"runtime,omitempty"` } +func (s *SubagentConfig) UnmarshalJSON(data []byte) error { + type alias SubagentConfig + var raw struct { + alias + LegacySystemPrompt string `json:"system_prompt"` + } + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + *s = SubagentConfig(raw.alias) + return nil +} + type SubagentToolsConfig struct { Allowlist []string `json:"allowlist,omitempty"` Denylist []string `json:"denylist,omitempty"` diff --git a/pkg/tools/subagent.go b/pkg/tools/subagent.go index 7353e0f..914db4b 100644 --- a/pkg/tools/subagent.go +++ b/pkg/tools/subagent.go @@ -27,7 +27,6 @@ type SubagentTask struct { NotifyMainPolicy string `json:"notify_main_policy,omitempty"` SessionKey string `json:"session_key"` MemoryNS string `json:"memory_ns"` - SystemPrompt string `json:"system_prompt,omitempty"` SystemPromptFile string `json:"system_prompt_file,omitempty"` ToolAllowlist []string `json:"tool_allowlist,omitempty"` MaxRetries int `json:"max_retries,omitempty"` @@ -168,7 +167,6 @@ func (sm *SubagentManager) spawnTask(ctx context.Context, opts SubagentSpawnOpti agentID = "default" } memoryNS := agentID - systemPrompt := "" systemPromptFile := "" transport := "local" nodeID := "" @@ -207,7 +205,6 @@ func (sm *SubagentManager) spawnTask(ctx context.Context, opts SubagentSpawnOpti nodeID = strings.TrimSpace(profile.NodeID) parentAgentID = strings.TrimSpace(profile.ParentAgentID) notifyMainPolicy = normalizeNotifyMainPolicy(profile.NotifyMainPolicy) - systemPrompt = strings.TrimSpace(profile.SystemPrompt) systemPromptFile = strings.TrimSpace(profile.SystemPromptFile) toolAllowlist = append([]string(nil), profile.ToolAllowlist...) maxRetries = profile.MaxRetries @@ -288,7 +285,6 @@ func (sm *SubagentManager) spawnTask(ctx context.Context, opts SubagentSpawnOpti NotifyMainPolicy: notifyMainPolicy, SessionKey: sessionKey, MemoryNS: memoryNS, - SystemPrompt: systemPrompt, SystemPromptFile: systemPromptFile, ToolAllowlist: toolAllowlist, MaxRetries: maxRetries, @@ -671,9 +667,6 @@ func (sm *SubagentManager) resolveSystemPrompt(task *SubagentTask) string { return systemPrompt + "\n\nSubagent policy (" + promptFile + "):\n" + promptText } } - if rolePrompt := strings.TrimSpace(task.SystemPrompt); rolePrompt != "" { - return systemPrompt + "\n\nRole-specific profile prompt:\n" + rolePrompt - } return systemPrompt } diff --git a/pkg/tools/subagent_config_manager.go b/pkg/tools/subagent_config_manager.go index d761934..2ad197d 100644 --- a/pkg/tools/subagent_config_manager.go +++ b/pkg/tools/subagent_config_manager.go @@ -21,14 +21,12 @@ func DraftConfigSubagent(description, agentIDHint string) map[string]interface{} displayName := inferDraftDisplayName(role, agentID) toolAllowlist := inferDraftToolAllowlist(role) keywords := inferDraftKeywords(role, lower) - systemPrompt := inferDraftSystemPrompt(role, desc) return map[string]interface{}{ "agent_id": agentID, "role": role, "display_name": displayName, "description": desc, "notify_main_policy": "final_only", - "system_prompt": systemPrompt, "system_prompt_file": "agents/" + agentID + "/AGENT.md", "memory_namespace": agentID, "tool_allowlist": toolAllowlist, @@ -87,9 +85,6 @@ func UpsertConfigSubagent(configPath string, args map[string]interface{}) (map[s if v := stringArgFromMap(args, "description"); v != "" { subcfg.Description = v } - if v := stringArgFromMap(args, "system_prompt"); v != "" { - subcfg.SystemPrompt = v - } if v := stringArgFromMap(args, "system_prompt_file"); v != "" { subcfg.SystemPromptFile = v } diff --git a/pkg/tools/subagent_config_tool.go b/pkg/tools/subagent_config_tool.go index 16c72bb..8352941 100644 --- a/pkg/tools/subagent_config_tool.go +++ b/pkg/tools/subagent_config_tool.go @@ -45,7 +45,6 @@ func (t *SubagentConfigTool) Parameters() map[string]interface{} { "parent_agent_id": map[string]interface{}{"type": "string"}, "role": map[string]interface{}{"type": "string"}, "display_name": map[string]interface{}{"type": "string"}, - "system_prompt": map[string]interface{}{"type": "string"}, "system_prompt_file": map[string]interface{}{"type": "string"}, "memory_namespace": map[string]interface{}{"type": "string"}, "type": map[string]interface{}{"type": "string"}, diff --git a/pkg/tools/subagent_config_tool_test.go b/pkg/tools/subagent_config_tool_test.go index 651425f..a0f2cab 100644 --- a/pkg/tools/subagent_config_tool_test.go +++ b/pkg/tools/subagent_config_tool_test.go @@ -35,7 +35,6 @@ func TestSubagentConfigToolUpsert(t *testing.T) { "notify_main_policy": "internal_only", "display_name": "Review Agent", "description": "负责回归与评审", - "system_prompt": "review changes", "system_prompt_file": "agents/reviewer/AGENT.md", "routing_keywords": []interface{}{"review", "regression"}, "tool_allowlist": []interface{}{"shell", "sessions"}, diff --git a/pkg/tools/subagent_profile.go b/pkg/tools/subagent_profile.go index bd1610c..0abe584 100644 --- a/pkg/tools/subagent_profile.go +++ b/pkg/tools/subagent_profile.go @@ -24,7 +24,6 @@ type SubagentProfile struct { ParentAgentID string `json:"parent_agent_id,omitempty"` NotifyMainPolicy string `json:"notify_main_policy,omitempty"` Role string `json:"role,omitempty"` - SystemPrompt string `json:"system_prompt,omitempty"` SystemPromptFile string `json:"system_prompt_file,omitempty"` ToolAllowlist []string `json:"tool_allowlist,omitempty"` MemoryNamespace string `json:"memory_namespace,omitempty"` @@ -191,7 +190,6 @@ func normalizeSubagentProfile(in SubagentProfile) SubagentProfile { p.ParentAgentID = normalizeSubagentIdentifier(p.ParentAgentID) p.NotifyMainPolicy = normalizeNotifyMainPolicy(p.NotifyMainPolicy) p.Role = strings.TrimSpace(p.Role) - p.SystemPrompt = strings.TrimSpace(p.SystemPrompt) p.SystemPromptFile = strings.TrimSpace(p.SystemPromptFile) p.MemoryNamespace = normalizeSubagentIdentifier(p.MemoryNamespace) if p.MemoryNamespace == "" { @@ -409,7 +407,6 @@ func profileFromConfig(agentID string, subcfg config.SubagentConfig) SubagentPro ParentAgentID: strings.TrimSpace(subcfg.ParentAgentID), NotifyMainPolicy: strings.TrimSpace(subcfg.NotifyMainPolicy), Role: strings.TrimSpace(subcfg.Role), - SystemPrompt: strings.TrimSpace(subcfg.SystemPrompt), SystemPromptFile: strings.TrimSpace(subcfg.SystemPromptFile), ToolAllowlist: append([]string(nil), subcfg.Tools.Allowlist...), MemoryNamespace: strings.TrimSpace(subcfg.MemoryNamespace), @@ -554,7 +551,6 @@ func (t *SubagentProfileTool) Parameters() map[string]interface{} { "name": map[string]interface{}{"type": "string"}, "notify_main_policy": map[string]interface{}{"type": "string", "description": "final_only|internal_only|milestone|on_blocked|always"}, "role": map[string]interface{}{"type": "string"}, - "system_prompt": map[string]interface{}{"type": "string"}, "system_prompt_file": map[string]interface{}{"type": "string"}, "memory_namespace": map[string]interface{}{"type": "string"}, "status": map[string]interface{}{"type": "string", "description": "active|disabled"}, @@ -625,7 +621,6 @@ func (t *SubagentProfileTool) Execute(ctx context.Context, args map[string]inter Name: stringArg(args, "name"), NotifyMainPolicy: stringArg(args, "notify_main_policy"), Role: stringArg(args, "role"), - SystemPrompt: stringArg(args, "system_prompt"), SystemPromptFile: stringArg(args, "system_prompt_file"), MemoryNamespace: stringArg(args, "memory_namespace"), Status: stringArg(args, "status"), @@ -662,9 +657,6 @@ func (t *SubagentProfileTool) Execute(ctx context.Context, args map[string]inter if _, ok := args["notify_main_policy"]; ok { next.NotifyMainPolicy = stringArg(args, "notify_main_policy") } - if _, ok := args["system_prompt"]; ok { - next.SystemPrompt = stringArg(args, "system_prompt") - } if _, ok := args["system_prompt_file"]; ok { next.SystemPromptFile = stringArg(args, "system_prompt_file") } diff --git a/pkg/tools/subagent_profile_test.go b/pkg/tools/subagent_profile_test.go index 1597575..f538e97 100644 --- a/pkg/tools/subagent_profile_test.go +++ b/pkg/tools/subagent_profile_test.go @@ -126,7 +126,6 @@ func TestSubagentProfileStoreReadsProfilesFromRuntimeConfig(t *testing.T) { Enabled: true, DisplayName: "Code Agent", Role: "coding", - SystemPrompt: "write code", SystemPromptFile: "agents/coder/AGENT.md", MemoryNamespace: "code-ns", Tools: config.SubagentToolsConfig{ diff --git a/pkg/tools/subagent_runtime_control_test.go b/pkg/tools/subagent_runtime_control_test.go index 9adb7ef..818a0fc 100644 --- a/pkg/tools/subagent_runtime_control_test.go +++ b/pkg/tools/subagent_runtime_control_test.go @@ -719,7 +719,6 @@ func TestSubagentUsesConfiguredSystemPromptFile(t *testing.T) { if _, err := manager.ProfileStore().Upsert(SubagentProfile{ AgentID: "coder", Status: "active", - SystemPrompt: "inline-fallback", SystemPromptFile: "agents/coder/AGENT.md", }); err != nil { t.Fatalf("profile upsert failed: %v", err) diff --git a/webui/src/components/Header.tsx b/webui/src/components/Header.tsx index 3b9948e..1c1d8fc 100644 --- a/webui/src/components/Header.tsx +++ b/webui/src/components/Header.tsx @@ -54,11 +54,10 @@ const Header: React.FC = () => { diff --git a/webui/src/context/UIContext.tsx b/webui/src/context/UIContext.tsx index a530259..ce377e7 100644 --- a/webui/src/context/UIContext.tsx +++ b/webui/src/context/UIContext.tsx @@ -21,12 +21,9 @@ type UIContextType = { }; const UIContext = createContext(undefined); -const THEME_STORAGE_KEY = 'clawgo:webui:theme'; function getInitialTheme(): ThemeMode { if (typeof window === 'undefined') return 'dark'; - const saved = window.localStorage.getItem(THEME_STORAGE_KEY); - if (saved === 'light' || saved === 'dark') return saved; return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } @@ -45,9 +42,25 @@ export const UIProvider: React.FC<{ children: React.ReactNode }> = ({ children } root.classList.remove('theme-light', 'theme-dark'); root.classList.add(theme === 'dark' ? 'theme-dark' : 'theme-light'); root.style.colorScheme = theme; - window.localStorage.setItem(THEME_STORAGE_KEY, theme); }, [theme]); + useEffect(() => { + if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return; + const media = window.matchMedia('(prefers-color-scheme: dark)'); + const applySystemTheme = (event?: MediaQueryList | MediaQueryListEvent) => { + const matches = 'matches' in (event || media) ? (event || media).matches : media.matches; + setTheme(matches ? 'dark' : 'light'); + }; + applySystemTheme(media); + const onChange = (event: MediaQueryListEvent) => applySystemTheme(event); + if (typeof media.addEventListener === 'function') { + media.addEventListener('change', onChange); + return () => media.removeEventListener('change', onChange); + } + media.addListener(onChange); + return () => media.removeListener(onChange); + }, []); + const value = useMemo(() => ({ loading, theme, diff --git a/webui/src/i18n/index.ts b/webui/src/i18n/index.ts index 5d9b321..4a6fe85 100644 --- a/webui/src/i18n/index.ts +++ b/webui/src/i18n/index.ts @@ -108,6 +108,7 @@ const resources = { subagentDeleteConfirmMessage: 'Delete subagent profile "{{id}}" permanently?', sidebarCore: 'Core', sidebarMain: 'Main', + sidebarAgents: 'Agents', sidebarRuntime: 'Runtime', sidebarConfig: 'Configuration', sidebarKnowledge: 'Knowledge', @@ -726,6 +727,7 @@ const resources = { subagentDeleteConfirmMessage: '确认永久删除子代理档案 "{{id}}"?', sidebarCore: '核心', sidebarMain: '主入口', + sidebarAgents: 'Agents', sidebarRuntime: '运行态', sidebarConfig: '配置', sidebarKnowledge: '知识与调试', diff --git a/webui/src/index.css b/webui/src/index.css index 1659476..a11135e 100644 --- a/webui/src/index.css +++ b/webui/src/index.css @@ -78,62 +78,62 @@ html { html.theme-dark { --color-zinc-50: #f8fafc; --color-zinc-100: #e2e8f0; - --color-zinc-200: #cbd5e1; - --color-zinc-300: #94a3b8; - --color-zinc-400: #64748b; - --color-zinc-500: #475569; - --color-zinc-600: #334155; - --color-zinc-700: #1e293b; - --color-zinc-800: #111827; - --color-zinc-900: #0b1220; - --color-zinc-950: #070b13; - --color-indigo-300: #fdba74; - --color-indigo-400: #fb923c; - --color-indigo-500: #f97316; - --color-indigo-600: #ea580c; - --color-indigo-700: #c2410c; - --color-indigo-800: #9a3412; - --app-bg-spot-a: rgb(249 115 22 / 0.02); - --app-bg-spot-b: rgb(96 165 250 / 0.02); - --app-bg-base-top: rgb(3 7 12 / 0.998); - --app-bg-base-bottom: rgb(8 12 18 / 0.995); - --shell-spot-a: rgb(249 115 22 / 0.018); - --shell-spot-b: rgb(148 163 184 / 0.025); - --main-surface-top: rgb(255 255 255 / 0.012); - --main-surface-mid: rgb(255 255 255 / 0.008); - --main-surface-bottom: rgb(255 255 255 / 0.002); - --header-bg-a: rgb(10 14 20 / 0.86); - --header-bg-b: rgb(6 10 15 / 0.82); - --header-overlay-a: rgb(255 255 255 / 0.018); - --header-overlay-b: rgb(255 255 255 / 0.005); - --sidebar-bg-a: rgb(10 14 20 / 0.9); - --sidebar-bg-b: rgb(6 10 15 / 0.88); - --sidebar-overlay-a: rgb(255 255 255 / 0.012); - --sidebar-overlay-b: rgb(255 255 255 / 0.003); - --sidebar-edge: rgb(30 41 59 / 0.58); - --sidebar-section-a: rgb(15 23 42 / 0.52); - --sidebar-section-b: rgb(9 14 23 / 0.34); - --active-bg: rgb(249 115 22 / 0.09); - --active-ring: rgb(249 115 22 / 0.16); - --card-bg-a: rgb(15 23 42 / 0.56); - --card-bg-b: rgb(9 14 23 / 0.44); - --card-topline: rgb(255 255 255 / 0.035); - --card-inner-highlight: rgb(255 255 255 / 0.03); - --card-shadow: rgb(0 0 0 / 0.16); - --card-subtle-a: rgb(17 24 39 / 0.4); - --card-subtle-b: rgb(9 14 23 / 0.24); - --button-start: #fb923c; - --button-end: #ea580c; - --button-shadow: rgb(0 0 0 / 0.14); - --chip-bg: rgb(30 41 59 / 0.82); - --chip-bg-hover: rgb(51 65 85 / 0.92); - --chip-border: rgb(71 85 105 / 0.85); + --color-zinc-200: #d7e1ee; + --color-zinc-300: #b8c6d8; + --color-zinc-400: #90a4bc; + --color-zinc-500: #6f839b; + --color-zinc-600: #516278; + --color-zinc-700: #243244; + --color-zinc-800: #162131; + --color-zinc-900: #101827; + --color-zinc-950: #0d1522; + --color-indigo-300: #f8c58d; + --color-indigo-400: #f1a561; + --color-indigo-500: #e8843a; + --color-indigo-600: #d46a23; + --color-indigo-700: #af4f16; + --color-indigo-800: #8d3f13; + --app-bg-spot-a: rgb(249 115 22 / 0.035); + --app-bg-spot-b: rgb(56 189 248 / 0.05); + --app-bg-base-top: rgb(9 16 28 / 0.995); + --app-bg-base-bottom: rgb(14 22 36 / 0.992); + --shell-spot-a: rgb(249 115 22 / 0.03); + --shell-spot-b: rgb(96 165 250 / 0.04); + --main-surface-top: rgb(255 255 255 / 0.026); + --main-surface-mid: rgb(255 255 255 / 0.016); + --main-surface-bottom: rgb(255 255 255 / 0.008); + --header-bg-a: rgb(14 21 34 / 0.92); + --header-bg-b: rgb(11 18 29 / 0.9); + --header-overlay-a: rgb(255 255 255 / 0.03); + --header-overlay-b: rgb(255 255 255 / 0.01); + --sidebar-bg-a: rgb(13 20 33 / 0.93); + --sidebar-bg-b: rgb(10 16 27 / 0.9); + --sidebar-overlay-a: rgb(255 255 255 / 0.022); + --sidebar-overlay-b: rgb(255 255 255 / 0.008); + --sidebar-edge: rgb(71 85 105 / 0.64); + --sidebar-section-a: rgb(18 30 49 / 0.74); + --sidebar-section-b: rgb(12 20 34 / 0.58); + --active-bg: rgb(232 132 58 / 0.11); + --active-ring: rgb(241 165 97 / 0.22); + --card-bg-a: rgb(17 28 46 / 0.82); + --card-bg-b: rgb(11 20 34 / 0.72); + --card-topline: rgb(255 255 255 / 0.065); + --card-inner-highlight: rgb(255 255 255 / 0.045); + --card-shadow: rgb(0 0 0 / 0.24); + --card-subtle-a: rgb(22 32 50 / 0.62); + --card-subtle-b: rgb(13 21 35 / 0.48); + --button-start: #ee9852; + --button-end: #d96b25; + --button-shadow: rgb(217 107 37 / 0.18); + --chip-bg: rgb(36 49 69 / 0.9); + --chip-bg-hover: rgb(48 63 87 / 0.96); + --chip-border: rgb(93 109 135 / 0.82); --chip-text: rgb(226 232 240 / 0.96); - --chip-active-bg: rgb(124 45 18 / 0.36); - --chip-active-border: rgb(251 146 60 / 0.35); - --chip-active-text: rgb(255 237 213 / 0.96); - --chip-group-bg: rgb(15 23 42 / 0.5); - --chip-group-border: rgb(51 65 85 / 0.7); + --chip-active-bg: rgb(123 58 24 / 0.28); + --chip-active-border: rgb(232 132 58 / 0.28); + --chip-active-text: rgb(255 237 213 / 0.92); + --chip-group-bg: rgb(19 31 49 / 0.68); + --chip-group-border: rgb(71 85 105 / 0.76); --radius-card: 18px; --radius-subtle: 12px; --radius-panel: 16px; diff --git a/webui/src/pages/Dashboard.tsx b/webui/src/pages/Dashboard.tsx index ab0d4d0..c54e4ce 100644 --- a/webui/src/pages/Dashboard.tsx +++ b/webui/src/pages/Dashboard.tsx @@ -147,15 +147,11 @@ const Dashboard: React.FC = () => {
- -
{t('nodeP2P')}
-
-
- {p2pEnabled ? `${p2pConfiguredIce} ICE · ${p2pConfiguredStun} STUN` : t('disabled')} -
-
- {t('dashboardNodeP2PDetail', { transport: p2pTransport, sessions: p2pSessions, retries: p2pRetryCount })} + +
{t('ekgTopProvidersWorkload')}
+
{ekgTopProvider}
+
{t('dashboardWorkloadSnapshot')}
diff --git a/webui/src/pages/Nodes.tsx b/webui/src/pages/Nodes.tsx index 863dcea..9a158be 100644 --- a/webui/src/pages/Nodes.tsx +++ b/webui/src/pages/Nodes.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; +import { Check } from 'lucide-react'; import { useAppContext } from '../context/AppContext'; import { formatLocalDateTime } from '../utils/time'; @@ -247,20 +248,29 @@ const Nodes: React.FC = () => { ); })} @@ -416,10 +426,19 @@ const Nodes: React.FC = () => { ); })} diff --git a/webui/src/pages/Skills.tsx b/webui/src/pages/Skills.tsx index edafee6..b83bfb2 100644 --- a/webui/src/pages/Skills.tsx +++ b/webui/src/pages/Skills.tsx @@ -202,13 +202,13 @@ const Skills: React.FC = () => {
-
+
{t('skillsClawhubStatus')}: {clawhubInstalled ? t('installed') : t('notInstalled')}
{!clawhubInstalled && ( @@ -223,9 +223,16 @@ const Skills: React.FC = () => {
{!clawhubInstalled && ( -
-
{t('skillsClawhubMissingTitle')}
-
{t('skillsInstallPanelHint')}
+
+
+
+ +
+
+
{t('skillsClawhubMissingTitle')}
+
{t('skillsInstallPanelHint')}
+
+
)} diff --git a/webui/src/pages/SubagentProfiles.tsx b/webui/src/pages/SubagentProfiles.tsx index 4a4b283..68499ca 100644 --- a/webui/src/pages/SubagentProfiles.tsx +++ b/webui/src/pages/SubagentProfiles.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { Check } from 'lucide-react'; import { useAppContext } from '../context/AppContext'; import { useUI } from '../context/UIContext'; @@ -8,7 +9,6 @@ type SubagentProfile = { name?: string; notify_main_policy?: string; role?: string; - system_prompt?: string; system_prompt_file?: string; tool_allowlist?: string[]; memory_namespace?: string; @@ -33,7 +33,6 @@ const emptyDraft: SubagentProfile = { name: '', notify_main_policy: 'final_only', role: '', - system_prompt: '', system_prompt_file: '', memory_namespace: '', status: 'active', @@ -81,7 +80,6 @@ const SubagentProfiles: React.FC = () => { name: next.name || '', notify_main_policy: next.notify_main_policy || 'final_only', role: next.role || '', - system_prompt: next.system_prompt || '', system_prompt_file: next.system_prompt_file || '', memory_namespace: next.memory_namespace || '', status: (next.status as string) || 'active', @@ -142,7 +140,6 @@ const SubagentProfiles: React.FC = () => { name: p.name || '', notify_main_policy: p.notify_main_policy || 'final_only', role: p.role || '', - system_prompt: p.system_prompt || '', system_prompt_file: p.system_prompt_file || '', memory_namespace: p.memory_namespace || '', status: (p.status as string) || 'active', @@ -195,7 +192,6 @@ const SubagentProfiles: React.FC = () => { name: draft.name || '', notify_main_policy: draft.notify_main_policy || 'final_only', role: draft.role || '', - system_prompt: draft.system_prompt || '', system_prompt_file: draft.system_prompt_file || '', memory_namespace: draft.memory_namespace || '', status: draft.status || 'active', @@ -295,11 +291,20 @@ const SubagentProfiles: React.FC = () => { ))} @@ -409,15 +414,6 @@ const SubagentProfiles: React.FC = () => {
)}
-
-
System Prompt
-