feat: hint that Copilot accepts pasted recorded steps from other agents (#7097)

This commit is contained in:
Cindy Li 2026-07-05 22:51:32 -04:00 committed by GitHub
parent a95bab83a3
commit 121d323ec2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 95 additions and 2 deletions

View file

@ -0,0 +1,61 @@
import { useState } from "react";
import { Cross2Icon } from "@radix-ui/react-icons";
import { cn } from "@/util/utils";
import { usePasteSkillHintStore } from "@/store/usePasteSkillHintStore";
export function PasteRecordedStepsHint({ className }: { className?: string }) {
const dismissed = usePasteSkillHintStore((s) => s.dismissed);
const dismiss = usePasteSkillHintStore((s) => s.dismiss);
const [expanded, setExpanded] = useState(false);
if (dismissed) {
return null;
}
return (
<div
className={cn(
"shrink-0 rounded-lg border border-border bg-slate-elevation2 px-3 py-2 text-xs text-muted-foreground",
className,
)}
>
<div className="flex items-start gap-2">
<span aria-hidden className="leading-5">
💡
</span>
<div className="flex-1">
<span>
Already recorded this with another agent? Paste the steps into
Copilot to build it here.
</span>{" "}
<button
type="button"
onClick={() => setExpanded((prev) => !prev)}
className="font-medium text-foreground underline-offset-2 hover:underline"
>
{expanded ? "Hide" : "How?"}
</button>
</div>
<button
type="button"
aria-label="Dismiss"
onClick={dismiss}
className="text-muted-foreground hover:text-foreground"
>
<Cross2Icon className="h-3.5 w-3.5" />
</button>
</div>
{expanded ? (
<ol className="mt-2 list-decimal space-y-1 pl-8">
<li>
In your agent (e.g. the Claude Chrome extension), open your recorded
workflow and copy its prompt text.
</li>
<li>Paste it into the Copilot chat.</li>
<li>Copilot turns it into a Skyvern agent you can run and edit.</li>
</ol>
) : null}
</div>
);
}

View file

@ -21,6 +21,7 @@ import { stringify as convertToYAML } from "yaml";
import { useWorkflowHasChangesStore } from "@/store/WorkflowHasChangesStore";
import { useCopilotActionStore } from "@/store/useCopilotActionStore";
import { useCopilotHeaderStore } from "@/store/useCopilotHeaderStore";
import { usePasteSkillHintStore } from "@/store/usePasteSkillHintStore";
import { WorkflowCreateYAMLRequest } from "@/routes/workflows/types/workflowYamlTypes";
import { WorkflowApiResponse } from "@/routes/workflows/types/workflowTypes";
import { toast } from "@/components/ui/use-toast";
@ -509,6 +510,7 @@ export function WorkflowCopilotChat({
);
const [autoAccept, setAutoAccept] = useState<boolean>(false);
const [inputValue, setInputValue] = useState("");
const dismissPasteSkillHint = usePasteSkillHintStore((s) => s.dismiss);
const [isLoading, setIsLoading] = useState(false);
const [queuedPrompt, setQueuedPrompt] = useState<QueuedPrompt | null>(null);
const [narrative, setNarrative] =
@ -2382,10 +2384,11 @@ export function WorkflowCopilotChat({
? "Type a message to send next…"
: isWaitingForLiveBrowser
? "Type a prompt to send when ready..."
: "Message Skyvern Copilot…"
: "Message Skyvern Copilot, or paste recorded steps…"
}
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onPaste={() => dismissPasteSkillHint()}
onKeyDown={handleKeyPress}
disabled={inputDisabled}
rows={1}

View file

@ -4,6 +4,9 @@ import { usePostHog } from "posthog-js/react";
import { StreamStatusPanel } from "@/routes/streaming/StreamDiagnostics";
import { PasteRecordedStepsHint } from "@/routes/workflows/copilot/PasteRecordedStepsHint";
import { useRecordingStore } from "@/store/useRecordingStore";
import { HeroRecording } from "./runview/HeroRecording";
import { HeroScreenshot } from "./runview/HeroScreenshot";
import { RunLiveStream } from "./runview/RunLiveStream";
@ -28,6 +31,7 @@ export function BrowserTab() {
liveSurface,
} = useBrowserPaneView();
const postHog = usePostHog();
const isRecording = useRecordingStore((s) => s.isRecording);
const {
workflowRun,
@ -61,7 +65,9 @@ export function BrowserTab() {
const showRunStream = liveSurface === "run" && runId != null;
return (
<div className="flex h-full min-h-0 w-full flex-col p-3">
<div className="flex h-full min-h-0 w-full flex-col gap-3 p-3">
{!isRecording ? <PasteRecordedStepsHint /> : null}
<div className="relative flex min-h-0 flex-1 items-center justify-center overflow-hidden rounded-lg border border-border bg-slate-950">
{view === "live" ? (
showRunStream ? (

View file

@ -0,0 +1,23 @@
import { create } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";
type PasteSkillHintState = {
dismissed: boolean;
dismiss: () => void;
};
export const PASTE_SKILL_HINT_STORAGE_KEY = "skyvern.copilot.pasteSkillHint";
export const usePasteSkillHintStore = create<PasteSkillHintState>()(
persist(
(set) => ({
dismissed: false,
dismiss: () => set({ dismissed: true }),
}),
{
name: PASTE_SKILL_HINT_STORAGE_KEY,
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({ dismissed: state.dismissed }),
},
),
);