From 8c19dc1802260a9183c9ea92753172c4c531d3c8 Mon Sep 17 00:00:00 2001 From: Mark Henderson Date: Sat, 7 Mar 2026 12:25:58 -0800 Subject: [PATCH] feat: agent session retry + follow-up UX - retry/route.ts: reset failed/stopped session and re-fire agent runner with optional continueTask follow-up text - build/page.tsx: Retry button and Follow up input appear on failed/stopped sessions so users can continue without losing context or creating a duplicate session; task input hint clarifies each Run = new session Made-with: Cursor --- .../project/[projectId]/build/page.tsx | 74 +++++++++++- .../agent/sessions/[sessionId]/retry/route.ts | 114 ++++++++++++++++++ 2 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 app/api/projects/[projectId]/agent/sessions/[sessionId]/retry/route.ts diff --git a/app/[workspace]/project/[projectId]/build/page.tsx b/app/[workspace]/project/[projectId]/build/page.tsx index 044eff6..4115426 100644 --- a/app/[workspace]/project/[projectId]/build/page.tsx +++ b/app/[workspace]/project/[projectId]/build/page.tsx @@ -278,6 +278,9 @@ function AgentMode({ projectId, appName, appPath }: { projectId: string; appName const [approveMsg, setApproveMsg] = useState(""); const [showApproveInput, setShowApproveInput] = useState(false); const [approveResult, setApproveResult] = useState(null); + const [retrying, setRetrying] = useState(false); + const [followUp, setFollowUp] = useState(""); + const [showFollowUp, setShowFollowUp] = useState(false); const outputRef = useCallback((el: HTMLDivElement | null) => { if (el) el.scrollTop = el.scrollHeight; }, []); @@ -346,6 +349,24 @@ function AgentMode({ projectId, appName, appPath }: { projectId: string; appName setActiveSession(prev => prev ? { ...prev, status: "stopped" } : null); }; + const handleRetry = async (continueTask?: string) => { + if (!activeSessionId) return; + setRetrying(true); + try { + const r = await fetch(`/api/projects/${projectId}/agent/sessions/${activeSessionId}/retry`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ continueTask: continueTask?.trim() || undefined }), + }); + const d = await r.json(); + if (d.sessionId) { + setActiveSession(prev => prev ? { ...prev, status: "running", output: [], error: null } : null); + setShowFollowUp(false); + setFollowUp(""); + } + } finally { setRetrying(false); } + }; + const handleApprove = async () => { if (!activeSessionId || !approveMsg.trim()) return; setApproving(true); @@ -459,6 +480,57 @@ function AgentMode({ projectId, appName, appPath }: { projectId: string; appName )} + {/* Retry / follow-up panel for failed or stopped sessions */} + {["failed", "stopped"].includes(activeSession.status) && ( +
+ {showFollowUp ? ( +
+
+ Add a follow-up instruction (optional) then retry: +
+
+