From db54f77b73da7f067c631dc8233cf8583b441c3e Mon Sep 17 00:00:00 2001 From: Dotta Date: Thu, 5 Mar 2026 07:50:54 -0600 Subject: [PATCH] Add retry button to run detail page for failed/timed_out runs The retry button was only on the Inbox page but missing from the individual run detail view. This adds it next to the status badge, matching the same wakeup pattern used in the Inbox (retry_failed_run with context snapshot). Co-Authored-By: Claude Opus 4.6 --- ui/src/pages/AgentDetail.tsx | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/ui/src/pages/AgentDetail.tsx b/ui/src/pages/AgentDetail.tsx index 9243bc2b..7c8c6b19 100644 --- a/ui/src/pages/AgentDetail.tsx +++ b/ui/src/pages/AgentDetail.tsx @@ -1395,6 +1395,38 @@ function RunDetail({ run, agentRouteId, adapterType }: { run: HeartbeatRun; agen }, }); + const canRetryRun = run.status === "failed" || run.status === "timed_out"; + const retryPayload = useMemo(() => { + const payload: Record = {}; + const context = asRecord(run.contextSnapshot); + if (!context) return payload; + const issueId = asNonEmptyString(context.issueId); + const taskId = asNonEmptyString(context.taskId); + const taskKey = asNonEmptyString(context.taskKey); + if (issueId) payload.issueId = issueId; + if (taskId) payload.taskId = taskId; + if (taskKey) payload.taskKey = taskKey; + return payload; + }, [run.contextSnapshot]); + const retryRun = useMutation({ + mutationFn: async () => { + const result = await agentsApi.wakeup(run.agentId, { + source: "on_demand", + triggerDetail: "manual", + reason: "retry_failed_run", + payload: retryPayload, + }, run.companyId); + if (!("id" in result)) { + throw new Error("Retry was skipped because the agent is not currently invokable."); + } + return result; + }, + onSuccess: (newRun) => { + queryClient.invalidateQueries({ queryKey: queryKeys.heartbeats(run.companyId, run.agentId) }); + navigate(`/agents/${agentRouteId}/runs/${newRun.id}`); + }, + }); + const { data: touchedIssues } = useQuery({ queryKey: queryKeys.runIssues(run.id), queryFn: () => activityApi.issuesForRun(run.id), @@ -1485,12 +1517,29 @@ function RunDetail({ run, agentRouteId, adapterType }: { run: HeartbeatRun; agen {resumeRun.isPending ? "Resuming…" : "Resume"} )} + {canRetryRun && !canResumeLostRun && ( + + )} {resumeRun.isError && (
{resumeRun.error instanceof Error ? resumeRun.error.message : "Failed to resume run"}
)} + {retryRun.isError && ( +
+ {retryRun.error instanceof Error ? retryRun.error.message : "Failed to retry run"} +
+ )} {startTime && (