From 5c74e74f2abab9098218846cd684cd47b2dcd01d Mon Sep 17 00:00:00 2001 From: iven Date: Fri, 3 Apr 2026 00:28:58 +0800 Subject: [PATCH] fix(desktop): component cleanup + dead code removal + DeerFlow ai-elements - ChatArea: DeerFlow ai-elements annotations for accessibility - Conversation: remove unused Context, simplify message rendering - Delete dead modules: audit-logger.ts, gateway-reconnect.ts - Replace console.log with structured logger across components - Add idb dependency for IndexedDB persistence - Fix kernel-skills type safety improvements --- desktop/package.json | 1 + desktop/pnpm-lock.yaml | 8 + desktop/src/components/ChatArea.tsx | 34 +++- desktop/src/components/CloneManager.tsx | 4 +- desktop/src/components/ConversationList.tsx | 14 +- .../src/components/IdentityChangeProposal.tsx | 4 +- desktop/src/components/MemoryGraph.tsx | 4 +- desktop/src/components/MemoryPanel.tsx | 4 +- desktop/src/components/RightPanel.tsx | 5 +- desktop/src/components/Settings/General.tsx | 4 +- desktop/src/components/Settings/ModelsAPI.tsx | 5 +- desktop/src/components/ai/Conversation.tsx | 39 +--- desktop/src/components/ai/index.ts | 2 +- desktop/src/lib/audit-logger.ts | 173 ------------------ desktop/src/lib/gateway-reconnect.ts | 80 -------- desktop/src/lib/kernel-skills.ts | 11 +- desktop/src/lib/saas-client.ts | 1 + desktop/src/lib/secure-storage.ts | 22 --- desktop/src/lib/useProposalNotifications.ts | 4 +- 19 files changed, 78 insertions(+), 341 deletions(-) delete mode 100644 desktop/src/lib/audit-logger.ts delete mode 100644 desktop/src/lib/gateway-reconnect.ts diff --git a/desktop/package.json b/desktop/package.json index 8795e02..23e4f3b 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -43,6 +43,7 @@ "clsx": "^2.1.1", "dompurify": "^3.3.3", "framer-motion": "^12.38.0", + "idb": "^8.0.3", "lucide-react": "^0.577.0", "react": "^19.2.4", "react-dom": "^19.2.4", diff --git a/desktop/pnpm-lock.yaml b/desktop/pnpm-lock.yaml index 846c0ee..eed6bc6 100644 --- a/desktop/pnpm-lock.yaml +++ b/desktop/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: framer-motion: specifier: ^12.38.0 version: 12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + idb: + specifier: ^8.0.3 + version: 8.0.3 lucide-react: specifier: ^0.577.0 version: 0.577.0(react@19.2.4) @@ -2075,6 +2078,9 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + idb@8.0.3: + resolution: {integrity: sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -5323,6 +5329,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + idb@8.0.3: {} + ignore@5.3.2: {} ignore@7.0.5: {} diff --git a/desktop/src/components/ChatArea.tsx b/desktop/src/components/ChatArea.tsx index a5d396a..e399411 100644 --- a/desktop/src/components/ChatArea.tsx +++ b/desktop/src/components/ChatArea.tsx @@ -1,11 +1,13 @@ import { useState, useEffect, useRef, useCallback, useMemo, type MutableRefObject, type RefObject, type CSSProperties } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { List, type ListImperativeAPI } from 'react-window'; -import { useChatStore, Message } from '../store/chatStore'; +import { useChatStore, type Message } from '../store/chatStore'; +import { useConversationStore } from '../store/chat/conversationStore'; import { useArtifactStore } from '../store/chat/artifactStore'; import { useConnectionStore } from '../store/connectionStore'; import { useAgentStore } from '../store/agentStore'; import { useConfigStore } from '../store/configStore'; +import { listen, type UnlistenFn } from '@tauri-apps/api/event'; import { Paperclip, SquarePen, ArrowUp, MessageSquare, Download, X, FileText, Image as ImageIcon } from 'lucide-react'; import { Button, EmptyState, MessageListSkeleton, LoadingDots } from './ui'; import { ResizableChatLayout } from './ai/ResizableChatLayout'; @@ -45,11 +47,14 @@ const VIRTUALIZATION_THRESHOLD = 100; export function ChatArea() { const { - messages, currentAgent, isStreaming, isLoading, currentModel, - sendMessage: sendToGateway, setCurrentModel, initStreamListener, + messages, isStreaming, isLoading, + sendMessage: sendToGateway, initStreamListener, newConversation, chatMode, setChatMode, suggestions, totalInputTokens, totalOutputTokens, } = useChatStore(); + const currentAgent = useConversationStore((s) => s.currentAgent); + const currentModel = useConversationStore((s) => s.currentModel); + const setCurrentModel = useConversationStore((s) => s.setCurrentModel); const { artifacts, selectedArtifactId, artifactPanelOpen, selectArtifact, setArtifactPanelOpen, @@ -152,6 +157,29 @@ export function ChatArea() { return unsub; }, []); + // Listen for hand-execution-complete Tauri events + useEffect(() => { + let unlisten: UnlistenFn | undefined; + listen<{ approvalId: string; handId: string; success: boolean; error?: string | null }>( + 'hand-execution-complete', + (event) => { + const { handId, success, error } = event.payload; + useChatStore.getState().addMessage({ + id: crypto.randomUUID(), + role: 'hand', + content: success + ? `Hand ${handId} 执行完成` + : `Hand ${handId} 执行失败: ${error || '未知错误'}`, + timestamp: new Date(), + handName: handId, + handStatus: success ? 'completed' : 'failed', + handResult: event.payload, + }); + }, + ).then((fn) => { unlisten = fn; }); + return () => { unlisten?.(); }; + }, []); + // Auto-scroll to bottom on new messages useEffect(() => { if (scrollRef.current && !useVirtualization) { diff --git a/desktop/src/components/CloneManager.tsx b/desktop/src/components/CloneManager.tsx index b3db54d..d3701af 100644 --- a/desktop/src/components/CloneManager.tsx +++ b/desktop/src/components/CloneManager.tsx @@ -3,6 +3,7 @@ import { useAgentStore } from '../store/agentStore'; import { useConnectionStore } from '../store/connectionStore'; import { useConfigStore } from '../store/configStore'; import { toChatAgent, useChatStore } from '../store/chatStore'; +import { useConversationStore } from '../store/chat/conversationStore'; import { Bot, Plus, X, Globe, Cat, Search, BarChart2, Sparkles } from 'lucide-react'; import { AgentOnboardingWizard } from './AgentOnboardingWizard'; import type { Clone } from '../store/agentStore'; @@ -13,7 +14,8 @@ export function CloneManager() { const deleteClone = useAgentStore((s) => s.deleteClone); const connectionState = useConnectionStore((s) => s.connectionState); const quickConfig = useConfigStore((s) => s.quickConfig); - const { agents, currentAgent, setCurrentAgent } = useChatStore(); + const { agents, currentAgent } = useConversationStore(); + const setCurrentAgent = useChatStore((s) => s.setCurrentAgent); const [showWizard, setShowWizard] = useState(false); const connected = connectionState === 'connected'; diff --git a/desktop/src/components/ConversationList.tsx b/desktop/src/components/ConversationList.tsx index 1401342..b6994ac 100644 --- a/desktop/src/components/ConversationList.tsx +++ b/desktop/src/components/ConversationList.tsx @@ -1,4 +1,5 @@ import { useState, useRef, useEffect } from 'react'; +import { useConversationStore } from '../store/chat/conversationStore'; import { useChatStore } from '../store/chatStore'; import { MessageSquare, Trash2, SquarePen, Download, Check, X } from 'lucide-react'; import { EmptyConversations } from './ui'; @@ -171,15 +172,14 @@ function ConversationItem({ } export function ConversationList() { - const { - conversations, - currentConversationId, - switchConversation, - deleteConversation, - } = useChatStore(); + const conversations = useConversationStore((s) => s.conversations); + const currentConversationId = useConversationStore((s) => s.currentConversationId); + const { switchConversation, deleteConversation } = useChatStore(); + // suppress unused-var lint — these facade actions are needed + void switchConversation; void deleteConversation; const handleRename = (id: string, newTitle: string) => { - useChatStore.setState((state) => ({ + useConversationStore.setState((state) => ({ conversations: state.conversations.map((c) => c.id === id ? { ...c, title: newTitle, updatedAt: new Date() } : c ), diff --git a/desktop/src/components/IdentityChangeProposal.tsx b/desktop/src/components/IdentityChangeProposal.tsx index 9480242..fe466bf 100644 --- a/desktop/src/components/IdentityChangeProposal.tsx +++ b/desktop/src/components/IdentityChangeProposal.tsx @@ -27,7 +27,7 @@ import { type IdentityChangeProposal as Proposal, type IdentitySnapshot, } from '../lib/intelligence-client'; -import { useChatStore } from '../store/chatStore'; +import { useConversationStore } from '../store/chat/conversationStore'; import { Button, Badge } from './ui'; // === Error Parsing Utility === @@ -306,7 +306,7 @@ function HistoryItem({ // === Main Component === export function IdentityChangeProposalPanel() { - const { currentAgent } = useChatStore(); + const currentAgent = useConversationStore((s) => s.currentAgent); const [proposals, setProposals] = useState([]); const [snapshots, setSnapshots] = useState([]); const [loading, setLoading] = useState(true); diff --git a/desktop/src/components/MemoryGraph.tsx b/desktop/src/components/MemoryGraph.tsx index 8deab76..090c2ed 100644 --- a/desktop/src/components/MemoryGraph.tsx +++ b/desktop/src/components/MemoryGraph.tsx @@ -33,7 +33,7 @@ import { type GraphEdge, type MemoryType, } from '../store/memoryGraphStore'; -import { useChatStore } from '../store/chatStore'; +import { useConversationStore } from '../store/chat/conversationStore'; import { cardHover, defaultTransition } from '../lib/animations'; // Mark as intentionally unused for future use @@ -157,7 +157,7 @@ export function MemoryGraph({ className = '' }: MemoryGraphProps) { const [showFilters, setShowFilters] = useState(false); const [searchQuery, setSearchQuery] = useState(''); - const { currentAgent } = useChatStore(); + const currentAgent = useConversationStore((s) => s.currentAgent); const agentId = currentAgent?.id || 'zclaw-main'; const { diff --git a/desktop/src/components/MemoryPanel.tsx b/desktop/src/components/MemoryPanel.tsx index ff4f982..ccad69a 100644 --- a/desktop/src/components/MemoryPanel.tsx +++ b/desktop/src/components/MemoryPanel.tsx @@ -12,7 +12,7 @@ import { type MemoryType, type MemoryStats, } from '../lib/intelligence-client'; -import { useChatStore } from '../store/chatStore'; +import { useConversationStore } from '../store/chat/conversationStore'; const TYPE_LABELS: Record = { fact: { label: '事实', emoji: '📋', color: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300' }, @@ -23,7 +23,7 @@ const TYPE_LABELS: Record s.currentAgent); const agentId = currentAgent?.id || 'zclaw-main'; const [memories, setMemories] = useState([]); diff --git a/desktop/src/components/RightPanel.tsx b/desktop/src/components/RightPanel.tsx index 1bf9236..0460a7f 100644 --- a/desktop/src/components/RightPanel.tsx +++ b/desktop/src/components/RightPanel.tsx @@ -5,6 +5,7 @@ import { useConnectionStore } from '../store/connectionStore'; import { useAgentStore, type PluginStatus } from '../store/agentStore'; import { useConfigStore } from '../store/configStore'; import { toChatAgent, useChatStore, type CodeBlock } from '../store/chatStore'; +import { useConversationStore } from '../store/chat/conversationStore'; import { Wifi, WifiOff, Bot, BarChart3, Plug, RefreshCw, MessageSquare, Cpu, FileText, User, Activity, Brain, @@ -100,7 +101,9 @@ export function RightPanel() { const workspaceInfo = useConfigStore((s) => s.workspaceInfo); const quickConfig = useConfigStore((s) => s.quickConfig); - const { messages, currentModel, currentAgent, setCurrentAgent } = useChatStore(); + const { messages, setCurrentAgent } = useChatStore(); + const currentModel = useConversationStore((s) => s.currentModel); + const currentAgent = useConversationStore((s) => s.currentAgent); const [activeTab, setActiveTab] = useState<'status' | 'files' | 'agent' | 'memory' | 'reflection' | 'autonomy' | 'evolution'>('status'); const [memoryViewMode, setMemoryViewMode] = useState<'list' | 'graph'>('list'); const [isEditingAgent, setIsEditingAgent] = useState(false); diff --git a/desktop/src/components/Settings/General.tsx b/desktop/src/components/Settings/General.tsx index 1635b5e..053e71c 100644 --- a/desktop/src/components/Settings/General.tsx +++ b/desktop/src/components/Settings/General.tsx @@ -1,7 +1,7 @@ import { useState, useEffect } from 'react'; import { useConnectionStore } from '../../store/connectionStore'; import { useConfigStore } from '../../store/configStore'; -import { useChatStore } from '../../store/chatStore'; +import { useConversationStore } from '../../store/chat/conversationStore'; import { getStoredGatewayToken, setStoredGatewayToken } from '../../lib/gateway-client'; import { silentErrorHandler } from '../../lib/error-utils'; @@ -13,7 +13,7 @@ export function General() { const disconnect = useConnectionStore((s) => s.disconnect); const quickConfig = useConfigStore((s) => s.quickConfig); const saveQuickConfig = useConfigStore((s) => s.saveQuickConfig); - const currentModel = useChatStore((s) => s.currentModel); + const currentModel = useConversationStore((s) => s.currentModel); const [theme, setTheme] = useState<'light' | 'dark'>(quickConfig.theme || 'light'); const [autoStart, setAutoStart] = useState(quickConfig.autoStart ?? false); const [showToolCalls, setShowToolCalls] = useState(quickConfig.showToolCalls ?? false); diff --git a/desktop/src/components/Settings/ModelsAPI.tsx b/desktop/src/components/Settings/ModelsAPI.tsx index d70c32b..e4c293db 100644 --- a/desktop/src/components/Settings/ModelsAPI.tsx +++ b/desktop/src/components/Settings/ModelsAPI.tsx @@ -3,7 +3,7 @@ import { invoke } from '@tauri-apps/api/core'; import { getStoredGatewayToken, getStoredGatewayUrl } from '../../lib/gateway-client'; import { useConnectionStore } from '../../store/connectionStore'; import { useConfigStore } from '../../store/configStore'; -import { useChatStore } from '../../store/chatStore'; +import { useConversationStore } from '../../store/chat/conversationStore'; import { silentErrorHandler } from '../../lib/error-utils'; import { secureStorage } from '../../lib/secure-storage'; import { Plus, Pencil, Trash2, Star, Eye, EyeOff, AlertCircle, X, Zap, Check } from 'lucide-react'; @@ -166,7 +166,8 @@ export function ModelsAPI() { const disconnect = useConnectionStore((s) => s.disconnect); const quickConfig = useConfigStore((s) => s.quickConfig); const loadModels = useConfigStore((s) => s.loadModels); - const { currentModel, setCurrentModel } = useChatStore(); + const currentModel = useConversationStore((s) => s.currentModel); + const setCurrentModel = useConversationStore((s) => s.setCurrentModel); const [gatewayUrl, setGatewayUrl] = useState(getStoredGatewayUrl()); const [gatewayToken, setGatewayToken] = useState(quickConfig.gatewayToken || getStoredGatewayToken()); diff --git a/desktop/src/components/ai/Conversation.tsx b/desktop/src/components/ai/Conversation.tsx index 067182d..a6cbe65 100644 --- a/desktop/src/components/ai/Conversation.tsx +++ b/desktop/src/components/ai/Conversation.tsx @@ -1,41 +1,4 @@ -import { useRef, useEffect, useState, createContext, useContext, useMemo, type ReactNode } from 'react'; - -// --------------------------------------------------------------------------- -// ConversationContext — shared state for child ai-elements components -// --------------------------------------------------------------------------- - -interface ConversationContextValue { - isStreaming: boolean; - setIsStreaming: (v: boolean) => void; - messages: unknown[]; - setMessages: (msgs: unknown[]) => void; -} - -const ConversationContext = createContext(null); - -export function useConversationContext() { - const ctx = useContext(ConversationContext); - if (!ctx) { - throw new Error('useConversationContext must be used within ConversationProvider'); - } - return ctx; -} - -export function ConversationProvider({ children }: { children: ReactNode }) { - const [isStreaming, setIsStreaming] = useState(false); - const [messages, setMessages] = useState([]); - - const value = useMemo( - () => ({ isStreaming, setIsStreaming, messages, setMessages }), - [isStreaming, messages], - ); - - return ( - - {children} - - ); -} +import { useRef, useEffect, type ReactNode } from 'react'; // --------------------------------------------------------------------------- // Conversation container with auto-stick-to-bottom scroll behavior diff --git a/desktop/src/components/ai/index.ts b/desktop/src/components/ai/index.ts index 96dcb78..419a2a4 100644 --- a/desktop/src/components/ai/index.ts +++ b/desktop/src/components/ai/index.ts @@ -1,4 +1,4 @@ -export { Conversation, ConversationProvider, useConversationContext } from './Conversation'; +export { Conversation } from './Conversation'; export { ReasoningBlock } from './ReasoningBlock'; export { StreamingText } from './StreamingText'; export { ChatMode, type ChatModeType, type ChatModeConfig, CHAT_MODES } from './ChatMode'; diff --git a/desktop/src/lib/audit-logger.ts b/desktop/src/lib/audit-logger.ts deleted file mode 100644 index 1399b06..0000000 --- a/desktop/src/lib/audit-logger.ts +++ /dev/null @@ -1,173 +0,0 @@ -/** - * audit-logger.ts - 前端审计日志记录工具 - * - * 为 ZCLAW 前端操作提供统一的审计日志记录功能。 - * 记录关键操作(Hand 触发、Agent 创建等)到本地存储。 - * - * @reserved This module is reserved for future audit logging integration. - * It is not currently imported by any component. When audit logging is needed, - * import { logAudit, logAuditSuccess, logAuditFailure } from this module. - */ - -import { createLogger } from './logger'; - -const log = createLogger('AuditLogger'); - -export type AuditAction = - | 'hand.trigger' - | 'hand.approve' - | 'hand.cancel' - | 'agent.create' - | 'agent.update' - | 'agent.delete'; - -export type AuditResult = 'success' | 'failure' | 'pending'; - -export interface FrontendAuditEntry { - id: string; - timestamp: string; - action: AuditAction; - target: string; - result: AuditResult; - actor?: string; - details?: Record; - error?: string; -} - -export interface AuditLogOptions { - action: AuditAction; - target: string; - result: AuditResult; - actor?: string; - details?: Record; - error?: string; -} - -const STORAGE_KEY = 'zclaw-audit-logs'; -const MAX_LOCAL_LOGS = 500; - -function generateId(): string { - if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { - return crypto.randomUUID(); - } - const bytes = crypto.getRandomValues(new Uint8Array(6)); - const suffix = Array.from(bytes).map(b => b.toString(36).padStart(2, '0')).join(''); - return `audit_${Date.now()}_${suffix}`; -} - -function getTimestamp(): string { - return new Date().toISOString(); -} - -function loadLocalLogs(): FrontendAuditEntry[] { - try { - const stored = localStorage.getItem(STORAGE_KEY); - if (!stored) return []; - const logs = JSON.parse(stored) as FrontendAuditEntry[]; - return Array.isArray(logs) ? logs : []; - } catch (e) { - log.debug('Failed to parse audit logs from localStorage', { error: e }); - return []; - } -} - -function saveLocalLogs(logs: FrontendAuditEntry[]): void { - try { - const trimmedLogs = logs.slice(-MAX_LOCAL_LOGS); - localStorage.setItem(STORAGE_KEY, JSON.stringify(trimmedLogs)); - } catch (err) { - console.error('[AuditLogger] Failed to save logs to localStorage:', err); - } -} - -class AuditLogger { - private logs: FrontendAuditEntry[] = []; - private initialized = false; - - constructor() { - this.init(); - } - - private init(): void { - if (this.initialized) return; - this.logs = loadLocalLogs(); - this.initialized = true; - } - - async log(options: AuditLogOptions): Promise { - const entry: FrontendAuditEntry = { - id: generateId(), - timestamp: getTimestamp(), - action: options.action, - target: options.target, - result: options.result, - actor: options.actor, - details: options.details, - error: options.error, - }; - - this.logs.push(entry); - saveLocalLogs(this.logs); - - log.debug(entry.action, entry.target, entry.result, entry.details || ''); - - return entry; - } - - async logSuccess( - action: AuditAction, - target: string, - details?: Record - ): Promise { - return this.log({ action, target, result: 'success', details }); - } - - async logFailure( - action: AuditAction, - target: string, - error: string, - details?: Record - ): Promise { - return this.log({ action, target, result: 'failure', error, details }); - } - - getLogs(): FrontendAuditEntry[] { - return [...this.logs]; - } - - getLogsByAction(action: AuditAction): FrontendAuditEntry[] { - return this.logs.filter(log => log.action === action); - } - - clearLogs(): void { - this.logs = []; - localStorage.removeItem(STORAGE_KEY); - } - - exportLogs(): string { - return JSON.stringify(this.logs, null, 2); - } -} - -export const auditLogger = new AuditLogger(); - -export function logAudit(options: AuditLogOptions): Promise { - return auditLogger.log(options); -} - -export function logAuditSuccess( - action: AuditAction, - target: string, - details?: Record -): Promise { - return auditLogger.logSuccess(action, target, details); -} - -export function logAuditFailure( - action: AuditAction, - target: string, - error: string, - details?: Record -): Promise { - return auditLogger.logFailure(action, target, error, details); -} diff --git a/desktop/src/lib/gateway-reconnect.ts b/desktop/src/lib/gateway-reconnect.ts deleted file mode 100644 index 92c82ee..0000000 --- a/desktop/src/lib/gateway-reconnect.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * gateway-reconnect.ts - Gateway Reconnect Methods - * - * Extracted from gateway-client.ts for modularity. - * Installs reconnect methods onto GatewayClient.prototype via mixin pattern. - */ - -import type { GatewayClient } from './gateway-client'; - -// === Reconnect Constants === - -/** Maximum number of reconnect attempts before giving up */ -export const MAX_RECONNECT_ATTEMPTS = 10; - -// === Mixin Installer === - -/** - * Install reconnect methods onto GatewayClient.prototype. - * - * These methods access instance properties: - * - this.reconnectAttempts: number - * - this.reconnectInterval: number - * - this.reconnectTimer: number | null - * - this.log(level, message): void - * - this.connect(): Promise - * - this.setState(state): void - * - this.emitEvent(event, payload): void - */ -export function installReconnectMethods(ClientClass: { prototype: GatewayClient }): void { - const proto = ClientClass.prototype as any; - - /** - * Schedule a reconnect attempt with exponential backoff. - */ - proto.scheduleReconnect = function (this: GatewayClient): void { - const self = this as any; - if (self.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) { - self.log('error', `Max reconnect attempts (${MAX_RECONNECT_ATTEMPTS}) reached. Please reconnect manually.`); - self.setState('disconnected'); - self.emitEvent('reconnect_failed', { - attempts: self.reconnectAttempts, - maxAttempts: MAX_RECONNECT_ATTEMPTS, - }); - return; - } - - self.reconnectAttempts++; - self.setState('reconnecting'); - const delay = Math.min(self.reconnectInterval * Math.pow(1.5, self.reconnectAttempts - 1), 30000); - - self.log('info', `Scheduling reconnect attempt ${self.reconnectAttempts} in ${delay}ms`); - - // Emit reconnecting event for UI - self.emitEvent('reconnecting', { - attempt: self.reconnectAttempts, - delay, - maxAttempts: MAX_RECONNECT_ATTEMPTS, - }); - - self.reconnectTimer = window.setTimeout(async () => { - try { - await self.connect(); - } catch (e) { - /* close handler will trigger another reconnect */ - self.log('warn', `Reconnect attempt ${self.reconnectAttempts} failed: ${e instanceof Error ? e.message : String(e)}`); - } - }, delay); - }; - - /** - * Cancel a pending reconnect attempt. - */ - proto.cancelReconnect = function (this: GatewayClient): void { - const self = this as any; - if (self.reconnectTimer !== null) { - clearTimeout(self.reconnectTimer); - self.reconnectTimer = null; - } - }; -} diff --git a/desktop/src/lib/kernel-skills.ts b/desktop/src/lib/kernel-skills.ts index 092a6bf..3e27cc0 100644 --- a/desktop/src/lib/kernel-skills.ts +++ b/desktop/src/lib/kernel-skills.ts @@ -6,6 +6,7 @@ import { invoke } from '@tauri-apps/api/core'; import type { KernelClient } from './kernel-client'; +import { useConversationStore } from '../store/chat/conversationStore'; /** Skill shape returned by list/refresh/create/update operations. */ type SkillItem = { @@ -107,12 +108,16 @@ export function installSkillMethods(ClientClass: { prototype: KernelClient }): v error?: string; durationMs?: number; }> { + const convStore = useConversationStore.getState(); + const agent = convStore.currentAgent; + const sessionKey = convStore.sessionKey; + return invoke('skill_execute', { id, context: { - agentId: '', - sessionId: '', - workingDir: '', + agentId: agent?.id || 'zclaw-main', + sessionId: sessionKey || crypto.randomUUID(), + workingDir: null, }, input: input || {}, }); diff --git a/desktop/src/lib/saas-client.ts b/desktop/src/lib/saas-client.ts index 92b69e2..1517127 100644 --- a/desktop/src/lib/saas-client.ts +++ b/desktop/src/lib/saas-client.ts @@ -98,6 +98,7 @@ import type { PromptCheckResult, PromptTemplateInfo, PromptVersionInfo, + PaginatedResponse, AgentTemplateAvailable, AgentTemplateFull, } from './saas-types'; diff --git a/desktop/src/lib/secure-storage.ts b/desktop/src/lib/secure-storage.ts index ff0bd97..25f219a 100644 --- a/desktop/src/lib/secure-storage.ts +++ b/desktop/src/lib/secure-storage.ts @@ -277,28 +277,6 @@ function clearLocalStorageBackup(key: string): void { } } -// Keep synchronous versions for backward compatibility (deprecated) -function writeLocalStorageBackup(key: string, value: string): void { - try { - if (value) { - localStorage.setItem(key, value); - } else { - localStorage.removeItem(key); - } - } catch (e) { - logger.debug('writeLocalStorageBackup failed', { error: e }); - } -} - -function readLocalStorageBackup(key: string): string | null { - try { - return localStorage.getItem(key); - } catch (e) { - logger.debug('readLocalStorageBackup failed', { error: e }); - return null; - } -} - // === Device Keys Secure Storage === /** diff --git a/desktop/src/lib/useProposalNotifications.ts b/desktop/src/lib/useProposalNotifications.ts index d609273..c8acb5b 100644 --- a/desktop/src/lib/useProposalNotifications.ts +++ b/desktop/src/lib/useProposalNotifications.ts @@ -12,7 +12,7 @@ */ import { useEffect, useRef, useCallback } from 'react'; -import { useChatStore } from '../store/chatStore'; +import { useConversationStore } from '../store/chat/conversationStore'; import { intelligenceClient, type IdentityChangeProposal } from './intelligence-client'; import { createLogger } from './logger'; @@ -65,7 +65,7 @@ export function useProposalNotifications(): { pendingCount: number; refresh: () => Promise; } { - const { currentAgent } = useChatStore(); + const currentAgent = useConversationStore((s) => s.currentAgent); const agentId = currentAgent?.id; const pendingCountRef = useRef(0);