diff --git a/ui/desktop/frontend/src/components/agents/AgentList.tsx b/ui/desktop/frontend/src/components/agents/AgentList.tsx index f4698397..08080604 100644 --- a/ui/desktop/frontend/src/components/agents/AgentList.tsx +++ b/ui/desktop/frontend/src/components/agents/AgentList.tsx @@ -11,7 +11,7 @@ import type { AgentData, AgentInput } from '../../types/agent' export function AgentList() { const { t } = useTranslation(['agents', 'common']) - const { agents, loading, atLimit, createAgent, updateAgent, deleteAgent, resummonAgent, fetchAgents } = useAgentCrud() + const { agents, loading, atLimit, createAgent, updateAgent, deleteAgent, resummonAgent, cancelSummonAgent, fetchAgents } = useAgentCrud() const setStoreAgents = useAgentStore((s) => s.setAgents) const [formOpen, setFormOpen] = useState(false) @@ -140,6 +140,7 @@ export function AgentList() { agentId={summoningAgent.id} agentName={summoningAgent.name} onContinue={handleSummoningComplete} + onCancel={cancelSummonAgent} /> )} diff --git a/ui/desktop/frontend/src/components/onboarding/AgentStep.tsx b/ui/desktop/frontend/src/components/onboarding/AgentStep.tsx index 08b74afb..bb11b974 100644 --- a/ui/desktop/frontend/src/components/onboarding/AgentStep.tsx +++ b/ui/desktop/frontend/src/components/onboarding/AgentStep.tsx @@ -1,6 +1,7 @@ import { useState, useMemo, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { getApiClient } from '../../lib/api' +import { agentService } from '../../services/agent-service' import { SummoningModal } from './SummoningModal' import type { ProviderData } from '../../types/provider' @@ -109,6 +110,7 @@ export function AgentStep({ provider, model, onBack, onComplete }: AgentStepProp agentId={createdAgent.id} agentName={createdAgent.name} onContinue={onComplete} + onCancel={(id) => agentService.cancelSummon(id)} /> ) } diff --git a/ui/desktop/frontend/src/components/onboarding/SummoningModal.tsx b/ui/desktop/frontend/src/components/onboarding/SummoningModal.tsx index 2f19b430..a0eeb01d 100644 --- a/ui/desktop/frontend/src/components/onboarding/SummoningModal.tsx +++ b/ui/desktop/frontend/src/components/onboarding/SummoningModal.tsx @@ -11,14 +11,18 @@ interface SummoningModalProps { agentId: string agentName: string onContinue: () => void + onCancel?: (agentId: string) => Promise } -export function SummoningModal({ agentId, agentName, onContinue }: SummoningModalProps) { +const CANCEL_THRESHOLD_SEC = 60 + +export function SummoningModal({ agentId, agentName, onContinue, onCancel }: SummoningModalProps) { const { t } = useTranslation(['desktop', 'common']) const [generatedFiles, setGeneratedFiles] = useState([]) const [status, setStatus] = useState<'summoning' | 'completed' | 'failed'>('summoning') const [errorMsg, setErrorMsg] = useState('') const [retrying, setRetrying] = useState(false) + const [cancelling, setCancelling] = useState(false) const [elapsed, setElapsed] = useState(0) const timerRef = useRef | null>(null) @@ -84,6 +88,19 @@ export function SummoningModal({ agentId, agentName, onContinue }: SummoningModa } } + const handleCancel = async () => { + if (!onCancel) return + setCancelling(true) + try { + await onCancel(agentId) + // BE emits WS type=failed → listener above switches to failed state + } catch { + // keep modal open + } finally { + setCancelling(false) + } + } + return (
@@ -164,6 +181,19 @@ export function SummoningModal({ agentId, agentName, onContinue }: SummoningModa

)} + {status === 'summoning' && elapsed >= CANCEL_THRESHOLD_SEC && onCancel && ( +
+

{t('desktop:summoning.takingTooLong')}

+ +
+ )} + {status === 'completed' && (