diff --git a/ui/src/components/EmailMessageCard.tsx b/ui/src/components/EmailMessageCard.tsx index be4a083e15f..b1c374e1ac4 100644 --- a/ui/src/components/EmailMessageCard.tsx +++ b/ui/src/components/EmailMessageCard.tsx @@ -42,7 +42,9 @@ export function EmailMessageCard({ message, publication, issueId, + contextNotice = false, }: { + contextNotice?: boolean; message: EmailMessage; publication?: EmailPublicationSummary; issueId: string; @@ -57,7 +59,7 @@ export function EmailMessageCard({ aria-label={ message.direction === "inbound" ? "Email received" : "Email sent" } - className="space-y-4 rounded-xl border border-border bg-card p-5" + className={contextNotice ? "task-context-notice space-y-3" : "space-y-4 rounded-xl border border-border bg-card p-5"} >
{p.request.subject ?? "Email reply"}
{p.request.to &&To: {p.request.to.join(", ")}
} diff --git a/ui/src/components/ExecutionBlockerNotice.test.tsx b/ui/src/components/ExecutionBlockerNotice.test.tsx index 3960f71070b..980f0a6fdf1 100644 --- a/ui/src/components/ExecutionBlockerNotice.test.tsx +++ b/ui/src/components/ExecutionBlockerNotice.test.tsx @@ -36,8 +36,7 @@ describe("stopped task recovery notice", () => { const notice = container.querySelector('[role="status"][aria-label="Task recovery"]')!; expect(notice.textContent).toContain("Recorded work is preserved"); expect(notice.textContent).toContain("Retry"); - expect(notice.classList.contains("border")).toBe(true); - expect(notice.classList.contains("bg-muted")).toBe(true); + expect(notice.classList.contains("task-context-notice")).toBe(true); expect(notice.querySelector("a")?.getAttribute("href")).toBe("/agents/agent/runs/failed-run"); }); it.each([false, true])("explains cancelled runs and saved input; continue eligibility %s", async canContinue => { diff --git a/ui/src/components/ExecutionBlockerNotice.tsx b/ui/src/components/ExecutionBlockerNotice.tsx index c157a359b5d..0cb1eb0b775 100644 --- a/ui/src/components/ExecutionBlockerNotice.tsx +++ b/ui/src/components/ExecutionBlockerNotice.tsx @@ -4,9 +4,11 @@ import { agentsApi } from "../api/agents"; import { activityApi } from "../api/activity"; import { queryKeys } from "../lib/queryKeys"; import { Button } from "./ui/button"; +import { cn } from "../lib/utils"; import { Link } from "../lib/router"; -export function ExecutionBlockerNotice({ companyId, issueId, blocker, onRetried }: { +export function ExecutionBlockerNotice({ companyId, issueId, blocker, onRetried, className }: { + className?: string; companyId: string; issueId: string; blocker: ExecutionBlocker; @@ -33,7 +35,7 @@ export function ExecutionBlockerNotice({ companyId, issueId, blocker, onRetried }, }); return ( -{modelRejected ? "Model unavailable." : "Recovery needed."}{blocker.runError ? ` ${blocker.runError}` : ""}
{modelRejected &&Choose a supported model or clear the task's model override, then retry.
} diff --git a/ui/src/components/WorkspaceExportRecovery.tsx b/ui/src/components/WorkspaceExportRecovery.tsx index 23ae74319b1..98376816817 100644 --- a/ui/src/components/WorkspaceExportRecovery.tsx +++ b/ui/src/components/WorkspaceExportRecovery.tsx @@ -3,12 +3,14 @@ import { useMutation } from "@tanstack/react-query"; import type { IssueRecoveryAction } from "@paperclipai/shared"; import { isNativeWorkspaceExportRepairCause } from "@paperclipai/shared"; import { issuesApi } from "../api/issues"; +import { cn } from "../lib/utils"; import { Button } from "./ui/button"; import { Label } from "./ui/label"; import { Textarea } from "./ui/textarea"; /** Copyback repair preserves the accepted result and never wakes the provider. */ -export function WorkspaceExportRecovery({ issueId, action, canManage, onQueued }: { +export function WorkspaceExportRecovery({ issueId, action, canManage, onQueued, className }: { + className?: string; issueId: string; action: IssueRecoveryAction | null; canManage: boolean; onQueued: () => void; }) { const noteId = useId(); @@ -23,7 +25,7 @@ export function WorkspaceExportRecovery({ issueId, action, canManage, onQueued } if (!action || !isNativeWorkspaceExportRepairCause(action.cause) || action.ownerType !== "board" || !["active", "escalated"].includes(action.status) || typeof action.evidence.runId !== "string") return null; const queued = queuedActionVersion === String(action.updatedAt) || action.wakePolicy?.kind === "resume_native_run"; - returnWorkspace export needs repair
{queued ?Export is queued for the saved result. The agent will not repeat its work.
: <>{"Automatic workspace export retries stopped. Inspect the export failure, restore provider or destination availability, and preserve the saved files in the retained sandbox. Retry export here when the cause is resolved."}
diff --git a/ui/src/components/chat/ExternallyConnectedTaskBanner.tsx b/ui/src/components/chat/ExternallyConnectedTaskBanner.tsx index 221f28c14aa..bb92647d243 100644 --- a/ui/src/components/chat/ExternallyConnectedTaskBanner.tsx +++ b/ui/src/components/chat/ExternallyConnectedTaskBanner.tsx @@ -17,6 +17,7 @@ import { Checkbox } from "@/components/ui/checkbox"; import { Textarea } from "@/components/ui/textarea"; import { useToast } from "@/context/ToastContext"; import { Link } from "@/lib/router"; +import { cn } from "@/lib/utils"; import { queryKeys } from "@/lib/queryKeys"; import { useChatConnectorsEnabled, chatProviderVisible } from "@/hooks/useChatConnectorsEnabled"; import { issuesApi } from "@/api/issues"; @@ -124,6 +125,7 @@ export function useIssueChatBinding(companyId: string, issueId: string) { } type ConnectedTaskProps = { + className?: string; attachments?: IssueAttachment[]; companyId: string; issueId: string; @@ -148,6 +150,7 @@ export function ExternallyConnectedTaskBanner(props: ConnectedTaskProps) { } function ConnectedTaskComposer({ + className, attachments = [], companyId, issueId, @@ -486,7 +489,7 @@ function ConnectedTaskComposer({ return (Reply sent. Send a message to continue.
)} @@ -7952,11 +7962,6 @@ export function TaskDetailSurface({ conversation, tasksTab }: { tasksTab?: TaskS : undefined } > -