import { useId, useState } from "react"; import { useMutation, useQuery } from "@tanstack/react-query"; import type { ExecutionBlocker } from "@paperclipai/shared"; import { issuesApi } from "../api/issues"; import { queryKeys } from "../lib/queryKeys"; import { Button } from "./ui/button"; import { Checkbox } from "./ui/checkbox"; import { Label } from "./ui/label"; import { Textarea } from "./ui/textarea"; import { NativeSelect } from "./ui/select"; export function WorkspaceReconstructionRecovery({ issueId, blocker, canManage, onRecorded }: { issueId: string; blocker: ExecutionBlocker; canManage: boolean; onRecorded: () => void; }) { const id = useId(); const [selected, setSelected] = useState([]); const [missingWork, setMissingWork] = useState(""); const [outcomeEvidence, setOutcomeEvidence] = useState(""); const [actionOutcome, setActionOutcome] = useState<"completed" | "not_performed" | "mixed">("mixed"); const [acknowledged, setAcknowledged] = useState(false); const files = useQuery({ queryKey: queryKeys.issues.attachments(issueId), queryFn: () => issuesApi.listAttachments(issueId), enabled: canManage }); const currentSelection = selected.filter(id => files.data?.some(file => file.id === id)); const submit = useMutation({ mutationFn: () => issuesApi.reconstructWorkspace(issueId, { actionId: blocker.recoveryActionId!, runId: blocker.runId!, expectedStatusVersion: blocker.workspaceRecoveryStatusVersion!, attachmentIds: currentSelection, missingWork: missingWork.trim(), actionOutcome, outcomeEvidence: outcomeEvidence.trim(), acknowledgeLostWork: true, }), onSuccess: onRecorded, }); return
Continue from saved sources

The original workspace was destroyed before its edits were saved. Its last run will remain failed.

{canManage ?

Select task attachments to rebuild from and record what remains missing. Paperclip starts a fresh session through its normal execution checks. Existing work limits still apply.

Saved source attachments {files.data?.map(file =>
setSelected(current => checked === true ? [...current, file.id] : current.filter(value => value !== file.id))} />
)} {files.isPending &&

Loading saved attachments…

} {files.data?.length === 0 &&

Attach the surviving source files to this task first.

}