@@ -26,6 +26,7 @@ import {
2626} from "@/types/agentConfig" ;
2727import AgentImportWizard from "@/components/agent/AgentImportWizard" ;
2828import log from "@/lib/logger" ;
29+ import { useConfirmModal } from "@/hooks/useConfirmModal" ;
2930import { useAuth } from "@/hooks/useAuth" ;
3031
3132import SubAgentPool from "./agent/SubAgentPool" ;
@@ -154,7 +155,9 @@ export default function AgentSetupOrchestrator({
154155 detailReasons . length > 0 ? detailReasons : fallbackReasons ;
155156
156157 const normalizedAvailability =
157- typeof detail ?. is_available === "boolean"
158+ normalizedReasons . length > 0
159+ ? false
160+ : typeof detail ?. is_available === "boolean"
158161 ? detail . is_available
159162 : typeof fallback ?. is_available === "boolean"
160163 ? fallback . is_available
@@ -200,6 +203,7 @@ export default function AgentSetupOrchestrator({
200203
201204 const { t } = useTranslation ( "common" ) ;
202205 const { message } = App . useApp ( ) ;
206+ const { confirm } = useConfirmModal ( ) ;
203207
204208 // Common refresh agent list function, moved to the front to avoid hoisting issues
205209 const refreshAgentList = async ( t : TFunction , clearTools : boolean = true ) => {
@@ -1871,6 +1875,138 @@ export default function AgentSetupOrchestrator({
18711875 }
18721876 } ;
18731877
1878+ // Handle copy agent from list
1879+ const handleCopyAgentFromList = async ( agent : Agent ) => {
1880+ try {
1881+ // Fetch source agent detail before duplicating
1882+ const detailResult = await searchAgentInfo ( Number ( agent . id ) ) ;
1883+ if ( ! detailResult . success || ! detailResult . data ) {
1884+ message . error ( detailResult . message ) ;
1885+ return ;
1886+ }
1887+ const detail = detailResult . data ;
1888+
1889+ // Prepare copy names
1890+ const copyName = `${ detail . name || "agent" } _copy` ;
1891+ const copyDisplayName = `${
1892+ detail . display_name || t ( "agentConfig.agents.defaultDisplayName" )
1893+ } ${ t ( "agent.copySuffix" ) } `;
1894+
1895+ // Gather tool and sub-agent identifiers from the source agent
1896+ const tools = Array . isArray ( detail . tools ) ? detail . tools : [ ] ;
1897+ const unavailableTools = tools . filter (
1898+ ( tool : any ) => tool && tool . is_available === false
1899+ ) ;
1900+ const unavailableToolNames = unavailableTools
1901+ . map (
1902+ ( tool : any ) =>
1903+ tool ?. display_name || tool ?. name || tool ?. tool_name || ""
1904+ )
1905+ . filter ( ( name : string ) => Boolean ( name ) ) ;
1906+
1907+ const enabledToolIds = tools
1908+ . filter ( ( tool : any ) => tool && tool . is_available !== false )
1909+ . map ( ( tool : any ) => Number ( tool . id ) )
1910+ . filter ( ( id : number ) => Number . isFinite ( id ) ) ;
1911+ const subAgentIds = ( Array . isArray ( detail . sub_agent_id_list )
1912+ ? detail . sub_agent_id_list
1913+ : [ ]
1914+ )
1915+ . map ( ( id : any ) => Number ( id ) )
1916+ . filter ( ( id : number ) => Number . isFinite ( id ) ) ;
1917+
1918+ // Create a new agent using the source agent fields
1919+ const createResult = await updateAgent (
1920+ undefined ,
1921+ copyName ,
1922+ detail . description ,
1923+ detail . model ,
1924+ detail . max_step ,
1925+ detail . provide_run_summary ,
1926+ detail . enabled ,
1927+ detail . business_description ,
1928+ detail . duty_prompt ,
1929+ detail . constraint_prompt ,
1930+ detail . few_shots_prompt ,
1931+ copyDisplayName ,
1932+ detail . model_id ?? undefined ,
1933+ detail . business_logic_model_name ?? undefined ,
1934+ detail . business_logic_model_id ?? undefined ,
1935+ enabledToolIds ,
1936+ subAgentIds
1937+ ) ;
1938+ if ( ! createResult . success || ! createResult . data ?. agent_id ) {
1939+ message . error (
1940+ createResult . message ||
1941+ t ( "agentConfig.agents.copyFailed" )
1942+ ) ;
1943+ return ;
1944+ }
1945+ const newAgentId = Number ( createResult . data . agent_id ) ;
1946+ const copiedAgentFallback : Agent = {
1947+ ...detail ,
1948+ id : String ( newAgentId ) ,
1949+ name : copyName ,
1950+ display_name : copyDisplayName ,
1951+ sub_agent_id_list : subAgentIds ,
1952+ } ;
1953+
1954+ // Copy tool configuration to the new agent
1955+ for ( const tool of tools ) {
1956+ if ( ! tool || tool . is_available === false ) {
1957+ continue ;
1958+ }
1959+ const params =
1960+ tool . initParams ?. reduce ( ( acc : Record < string , any > , param : any ) => {
1961+ acc [ param . name ] = param . value ;
1962+ return acc ;
1963+ } , { } ) || { } ;
1964+ try {
1965+ await updateToolConfig ( Number ( tool . id ) , newAgentId , params , true ) ;
1966+ } catch ( error ) {
1967+ log . error ( "Failed to copy tool configuration while duplicating agent:" , error ) ;
1968+ message . error (
1969+ t ( "agentConfig.agents.copyFailed" )
1970+ ) ;
1971+ return ;
1972+ }
1973+ }
1974+
1975+ // Refresh UI state and notify user about copy result
1976+ await refreshAgentList ( t , false ) ;
1977+ message . success ( t ( "agentConfig.agents.copySuccess" ) ) ;
1978+ if ( unavailableTools . length > 0 ) {
1979+ const names =
1980+ unavailableToolNames . join ( ", " ) ||
1981+ unavailableTools
1982+ . map ( ( tool : any ) => Number ( tool ?. id ) )
1983+ . filter ( ( id : number ) => ! Number . isNaN ( id ) )
1984+ . join ( ", " ) ;
1985+ message . warning (
1986+ t ( "agentConfig.agents.copyUnavailableTools" , {
1987+ count : unavailableTools . length ,
1988+ names,
1989+ } )
1990+ ) ;
1991+ }
1992+ // Auto select the newly copied agent for editing
1993+ await handleEditAgent ( copiedAgentFallback , t ) ;
1994+ } catch ( error ) {
1995+ log . error ( "Failed to copy agent:" , error ) ;
1996+ message . error ( t ( "agentConfig.agents.copyFailed" ) ) ;
1997+ }
1998+ } ;
1999+
2000+ const handleCopyAgentWithConfirm = ( agent : Agent ) => {
2001+ confirm ( {
2002+ title : t ( "agentConfig.agents.copyConfirmTitle" ) ,
2003+ content : t ( "agentConfig.agents.copyConfirmContent" , {
2004+ name : agent ?. display_name || agent ?. name || "" ,
2005+ } ) ,
2006+ onConfirm : ( ) => handleCopyAgentFromList ( agent ) ,
2007+ } ) ;
2008+ } ;
2009+
18742010 // Handle delete agent from list
18752011 const handleDeleteAgentFromList = ( agent : Agent ) => {
18762012 setAgentToDelete ( agent ) ;
@@ -1990,6 +2126,7 @@ export default function AgentSetupOrchestrator({
19902126 isGeneratingAgent = { isGeneratingAgent }
19912127 editingAgent = { editingAgent }
19922128 isCreatingNewAgent = { isCreatingNewAgent }
2129+ onCopyAgent = { handleCopyAgentWithConfirm }
19932130 onExportAgent = { handleExportAgentFromList }
19942131 onDeleteAgent = { handleDeleteAgentFromList }
19952132 unsavedAgentId = {
0 commit comments