import { useCallback } from 'react'; import { createAgentLocalSkill, deleteAgentLocalSkill, disableAgentSkill, enableAgentSkill, fetchAgentProfile, fetchAgentSkillDetail, fetchAgentSkills, fetchAgentWorkspaceFile, fetchCurrentRuntime, updateAgentLocalSkill, updateAgentWorkspaceFile, uploadAgentSkillZip } from '../services/runtimeApi'; import { useAgentStore } from '../store/agentStore'; /** * Custom hook for agent operation callbacks. * Takes clientRef, uses agentStore. */ export function useAgentDataRequests(clientRef) { const { selectedSkillAgentId, setSelectedSkillAgentId, setAgentProfilesByAgent, setIsAgentSkillsLoading, setAgentSkillsFeedback, setAgentSkillsSavingKey, setSkillDetailLoadingKey, setAgentSkillsByAgent, setSkillDetailsByName, localSkillDraftsByKey, selectedWorkspaceFile, setWorkspaceFilesByAgent, setWorkspaceDraftContent, workspaceDraftContent, setWorkspaceFileFeedback, setWorkspaceFileSavingKey, setIsWorkspaceFileLoading } = useAgentStore(); const resolveRunId = useCallback(async () => { const runtime = await fetchCurrentRuntime(); const runId = runtime?.run_id; if (!runId) { throw new Error('未检测到正在运行的任务'); } return runId; }, []); const requestAgentSkills = useCallback((agentId) => { const normalized = typeof agentId === 'string' ? agentId.trim() : ''; if (!normalized) return false; setIsAgentSkillsLoading(true); setAgentSkillsFeedback(null); void resolveRunId() .then((runId) => fetchAgentSkills(runId, normalized)) .then((payload) => { setAgentSkillsByAgent((prev) => ({ ...prev, [normalized]: Array.isArray(payload?.skills) ? payload.skills : [] })); setIsAgentSkillsLoading(false); }) .catch(() => { if (!clientRef.current) { setIsAgentSkillsLoading(false); return; } console.debug('REST agent skills request failed, falling back to websocket compatibility path'); const success = clientRef.current.send({ type: 'get_agent_skills', agent_id: normalized }); if (!success) { setIsAgentSkillsLoading(false); } }); return true; }, [clientRef, resolveRunId, setAgentSkillsByAgent, setIsAgentSkillsLoading, setAgentSkillsFeedback]); const requestAgentProfile = useCallback((agentId) => { const normalized = typeof agentId === 'string' ? agentId.trim() : ''; if (!normalized) return false; void resolveRunId() .then((runId) => fetchAgentProfile(runId, normalized)) .then((payload) => { setAgentProfilesByAgent((prev) => ({ ...prev, [normalized]: payload?.profile && typeof payload.profile === 'object' ? payload.profile : {} })); }) .catch(() => { if (clientRef.current) { console.debug('REST agent profile request failed, falling back to websocket compatibility path'); clientRef.current.send({ type: 'get_agent_profile', agent_id: normalized }); } }); return true; }, [clientRef, resolveRunId, setAgentProfilesByAgent]); const requestSkillDetail = useCallback((skillName) => { const normalized = typeof skillName === 'string' ? skillName.trim() : ''; if (!normalized) return false; const detailKey = `${selectedSkillAgentId}:${normalized}`; setSkillDetailLoadingKey(detailKey); void resolveRunId() .then((runId) => fetchAgentSkillDetail(runId, selectedSkillAgentId, normalized)) .then((payload) => { setSkillDetailsByName((prev) => ({ ...prev, [detailKey]: payload?.skill || null })); useAgentStore.getState().setLocalSkillDraftsByKey((prev) => ({ ...prev, [detailKey]: typeof payload?.skill?.content === 'string' ? payload.skill.content : '' })); setSkillDetailLoadingKey(null); }) .catch(() => { if (!clientRef.current) { setSkillDetailLoadingKey(null); return; } console.debug('REST skill detail request failed, falling back to websocket compatibility path'); const success = clientRef.current.send({ type: 'get_skill_detail', agent_id: selectedSkillAgentId, skill_name: normalized }); if (!success) { setSkillDetailLoadingKey(null); } }); return true; }, [clientRef, resolveRunId, selectedSkillAgentId, setSkillDetailLoadingKey, setSkillDetailsByName]); const handleCreateLocalSkill = useCallback((skillName) => { const normalized = typeof skillName === 'string' ? skillName.trim() : ''; if (!normalized) { setAgentSkillsFeedback({ type: 'error', text: '技能名称不能为空' }); return; } setAgentSkillsSavingKey(`${selectedSkillAgentId}:${normalized}:create`); setAgentSkillsFeedback(null); void resolveRunId() .then((runId) => createAgentLocalSkill(runId, selectedSkillAgentId, normalized)) .then(() => { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'success', text: `已创建本地技能 ${normalized}` }); requestAgentSkills(selectedSkillAgentId); requestSkillDetail(normalized); }) .catch(() => { if (!clientRef.current) { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'error', text: '连接未就绪,稍后重试' }); return; } console.debug('REST local skill create failed, falling back to websocket compatibility path'); const success = clientRef.current.send({ type: 'create_agent_local_skill', agent_id: selectedSkillAgentId, skill_name: normalized }); if (!success) { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'error', text: '发送失败,请检查连接状态' }); } }); }, [clientRef, requestAgentSkills, requestSkillDetail, resolveRunId, selectedSkillAgentId, setAgentSkillsFeedback, setAgentSkillsSavingKey]); const handleLocalSkillDraftChange = useCallback((skillName, content) => { const detailKey = `${selectedSkillAgentId}:${skillName}`; useAgentStore.getState().setLocalSkillDraftsByKey((prev) => ({ ...prev, [detailKey]: content })); }, [selectedSkillAgentId]); const handleLocalSkillSave = useCallback((skillName) => { const detailKey = `${selectedSkillAgentId}:${skillName}`; const content = localSkillDraftsByKey[detailKey]; if (typeof content !== 'string') return; setAgentSkillsSavingKey(`${selectedSkillAgentId}:${skillName}:content`); setAgentSkillsFeedback(null); void resolveRunId() .then((runId) => updateAgentLocalSkill(runId, selectedSkillAgentId, skillName, content)) .then(() => { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'success', text: `${selectedSkillAgentId} 的本地技能 ${skillName} 已保存` }); requestSkillDetail(skillName); }) .catch(() => { if (!clientRef.current) { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'error', text: '连接未就绪,稍后重试' }); return; } console.debug('REST local skill save failed, falling back to websocket compatibility path'); const success = clientRef.current.send({ type: 'update_agent_local_skill', agent_id: selectedSkillAgentId, skill_name: skillName, content }); if (!success) { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'error', text: '发送失败,请检查连接状态' }); } }); }, [clientRef, localSkillDraftsByKey, requestSkillDetail, resolveRunId, selectedSkillAgentId, setAgentSkillsFeedback, setAgentSkillsSavingKey]); const handleLocalSkillDelete = useCallback((skillName) => { setAgentSkillsSavingKey(`${selectedSkillAgentId}:${skillName}:delete`); setAgentSkillsFeedback(null); void resolveRunId() .then((runId) => deleteAgentLocalSkill(runId, selectedSkillAgentId, skillName)) .then(() => { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'success', text: `${selectedSkillAgentId} 的本地技能 ${skillName} 已删除` }); requestAgentSkills(selectedSkillAgentId); }) .catch(() => { if (!clientRef.current) { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'error', text: '连接未就绪,稍后重试' }); return; } console.debug('REST local skill delete failed, falling back to websocket compatibility path'); const success = clientRef.current.send({ type: 'delete_agent_local_skill', agent_id: selectedSkillAgentId, skill_name: skillName }); if (!success) { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'error', text: '发送失败,请检查连接状态' }); } }); }, [clientRef, requestAgentSkills, resolveRunId, selectedSkillAgentId, setAgentSkillsFeedback, setAgentSkillsSavingKey]); const handleRemoveSharedSkill = useCallback((skillName) => { setAgentSkillsSavingKey(`${selectedSkillAgentId}:${skillName}:remove`); setAgentSkillsFeedback(null); void resolveRunId() .then((runId) => disableAgentSkill(runId, selectedSkillAgentId, skillName)) .then(() => { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'success', text: `${selectedSkillAgentId} 已移除共享技能 ${skillName}` }); requestAgentSkills(selectedSkillAgentId); }) .catch(() => { if (!clientRef.current) { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'error', text: '连接未就绪,稍后重试' }); return; } console.debug('REST shared skill remove failed, falling back to websocket compatibility path'); const success = clientRef.current.send({ type: 'remove_agent_skill', agent_id: selectedSkillAgentId, skill_name: skillName }); if (!success) { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'error', text: '发送失败,请检查连接状态' }); } }); }, [clientRef, requestAgentSkills, resolveRunId, selectedSkillAgentId, setAgentSkillsFeedback, setAgentSkillsSavingKey]); const handleAgentSkillToggle = useCallback((skillName, enabled) => { const agentId = selectedSkillAgentId; setAgentSkillsSavingKey(`${agentId}:${skillName}`); setAgentSkillsFeedback(null); void resolveRunId() .then((runId) => enabled ? enableAgentSkill(runId, agentId, skillName) : disableAgentSkill(runId, agentId, skillName)) .then(() => { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'success', text: `${agentId} ${enabled ? '已启用' : '已禁用'} ${skillName}` }); requestAgentSkills(agentId); }) .catch(() => { if (!clientRef.current) { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'error', text: '连接未就绪,稍后重试' }); return; } console.debug('REST skill toggle failed, falling back to websocket compatibility path'); const success = clientRef.current.send({ type: 'update_agent_skill', agent_id: agentId, skill_name: skillName, enabled }); if (!success) { setAgentSkillsSavingKey(null); setAgentSkillsFeedback({ type: 'error', text: '发送失败,请检查连接状态' }); } }); }, [clientRef, requestAgentSkills, resolveRunId, selectedSkillAgentId, setAgentSkillsFeedback, setAgentSkillsSavingKey]); const handleSkillAgentChange = useCallback((agentId) => { setSelectedSkillAgentId(agentId); requestAgentProfile(agentId); requestAgentSkills(agentId); requestWorkspaceFile(agentId, selectedWorkspaceFile); }, [requestAgentProfile, requestAgentSkills, setSelectedSkillAgentId, selectedWorkspaceFile]); const requestWorkspaceFile = useCallback((agentId, filename) => { const normalizedAgentId = typeof agentId === 'string' ? agentId.trim() : ''; const normalizedFilename = typeof filename === 'string' ? filename.trim() : ''; if (!normalizedAgentId || !normalizedFilename) return false; setIsWorkspaceFileLoading(true); setWorkspaceFileFeedback(null); void resolveRunId() .then((runId) => fetchAgentWorkspaceFile(runId, normalizedAgentId, normalizedFilename)) .then((payload) => { setWorkspaceFilesByAgent((prev) => ({ ...prev, [normalizedAgentId]: { ...(prev[normalizedAgentId] || {}), [normalizedFilename]: typeof payload?.content === 'string' ? payload.content : '' } })); setWorkspaceDraftContent(typeof payload?.content === 'string' ? payload.content : ''); setIsWorkspaceFileLoading(false); }) .catch(() => { if (!clientRef.current) { setIsWorkspaceFileLoading(false); return; } console.debug('REST workspace file read failed, falling back to websocket compatibility path'); const success = clientRef.current.send({ type: 'get_agent_workspace_file', agent_id: normalizedAgentId, filename: normalizedFilename }); if (!success) { setIsWorkspaceFileLoading(false); } }); return true; }, [clientRef, resolveRunId, setIsWorkspaceFileLoading, setWorkspaceDraftContent, setWorkspaceFileFeedback, setWorkspaceFilesByAgent]); const handleWorkspaceFileChange = useCallback((filename) => { useAgentStore.getState().setSelectedWorkspaceFile(filename); requestWorkspaceFile(selectedSkillAgentId, filename); }, [requestWorkspaceFile, selectedSkillAgentId]); const handleWorkspaceFileSave = useCallback(() => { const key = `${selectedSkillAgentId}:${selectedWorkspaceFile}`; setWorkspaceFileSavingKey(key); setWorkspaceFileFeedback(null); void resolveRunId() .then((runId) => updateAgentWorkspaceFile(runId, selectedSkillAgentId, selectedWorkspaceFile, workspaceDraftContent)) .then((payload) => { setWorkspaceFileSavingKey(null); setWorkspaceFileFeedback({ type: 'success', text: `${selectedSkillAgentId} 的 ${selectedWorkspaceFile} 已保存` }); setWorkspaceFilesByAgent((prev) => ({ ...prev, [selectedSkillAgentId]: { ...(prev[selectedSkillAgentId] || {}), [selectedWorkspaceFile]: typeof payload?.content === 'string' ? payload.content : workspaceDraftContent } })); }) .catch(() => { if (!clientRef.current) { setWorkspaceFileSavingKey(null); setWorkspaceFileFeedback({ type: 'error', text: '连接未就绪,稍后重试' }); return; } console.debug('REST workspace file save failed, falling back to websocket compatibility path'); const success = clientRef.current.send({ type: 'update_agent_workspace_file', agent_id: selectedSkillAgentId, filename: selectedWorkspaceFile, content: workspaceDraftContent }); if (!success) { setWorkspaceFileSavingKey(null); setWorkspaceFileFeedback({ type: 'error', text: '发送失败,请检查连接状态' }); } }); }, [clientRef, resolveRunId, selectedSkillAgentId, selectedWorkspaceFile, setWorkspaceFileFeedback, setWorkspaceFileSavingKey, setWorkspaceFilesByAgent, workspaceDraftContent]); const handleUploadExternalSkill = useCallback(async (file) => { if (!(file instanceof File)) { setAgentSkillsFeedback({ type: 'error', text: '请选择 zip 文件后再上传' }); return; } if (!selectedSkillAgentId) { setAgentSkillsFeedback({ type: 'error', text: '未选择目标 Agent' }); return; } setAgentSkillsSavingKey(`${selectedSkillAgentId}:__upload__`); setAgentSkillsFeedback(null); try { const result = await uploadAgentSkillZip({ agentId: selectedSkillAgentId, file, activate: true }); setAgentSkillsFeedback({ type: 'success', text: `已上传并安装技能 ${result.skill_name || ''}`.trim() }); requestAgentSkills(selectedSkillAgentId); } catch (error) { setAgentSkillsFeedback({ type: 'error', text: `上传失败: ${error.message || '未知错误'}` }); } finally { setAgentSkillsSavingKey(null); } }, [requestAgentSkills, selectedSkillAgentId, setAgentSkillsFeedback, setAgentSkillsSavingKey]); return { requestAgentSkills, requestAgentProfile, requestSkillDetail, handleCreateLocalSkill, handleLocalSkillDraftChange, handleLocalSkillSave, handleLocalSkillDelete, handleRemoveSharedSkill, handleAgentSkillToggle, handleSkillAgentChange, requestWorkspaceFile, handleWorkspaceFileChange, handleWorkspaceFileSave, handleUploadExternalSkill }; }