From b1fe444984b584c18e5634c3055399572a6ff698 Mon Sep 17 00:00:00 2001 From: LawyZheng Date: Tue, 4 Nov 2025 22:54:39 +0800 Subject: [PATCH] fix task v1 rerun shell command (#3893) --- .../routes/tasks/create/CreateNewTaskForm.tsx | 6 ++- .../src/routes/tasks/create/SavedTaskForm.tsx | 6 ++- .../src/routes/tasks/detail/TaskDetails.tsx | 6 ++- .../src/util/taskRunPayload.test.ts | 31 +++++++++++++- skyvern-frontend/src/util/taskRunPayload.ts | 41 ++++++++++++++++--- 5 files changed, 80 insertions(+), 10 deletions(-) diff --git a/skyvern-frontend/src/routes/tasks/create/CreateNewTaskForm.tsx b/skyvern-frontend/src/routes/tasks/create/CreateNewTaskForm.tsx index 293c2af7..f7e04587 100644 --- a/skyvern-frontend/src/routes/tasks/create/CreateNewTaskForm.tsx +++ b/skyvern-frontend/src/routes/tasks/create/CreateNewTaskForm.tsx @@ -3,6 +3,7 @@ import { CreateTaskRequest, OrganizationApiResponse, ProxyLocation, + RunEngine, } from "@/api/types"; import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea"; import { Button } from "@/components/ui/button"; @@ -775,7 +776,10 @@ function CreateNewTaskForm({ initialValues }: Props) { return { method: "POST", url: `${runsApiBaseUrl}/run/tasks`, - body: buildTaskRunPayload(createTaskRequestObject(formValues)), + body: buildTaskRunPayload( + createTaskRequestObject(formValues), + RunEngine.SkyvernV1, + ), headers, } satisfies ApiCommandOptions; }} diff --git a/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx b/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx index a4b37ac9..81288815 100644 --- a/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx +++ b/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx @@ -36,6 +36,7 @@ import { CreateTaskRequest, OrganizationApiResponse, ProxyLocation, + RunEngine, } from "@/api/types"; import { ProxySelector } from "@/components/ProxySelector"; import { TestWebhookDialog } from "@/components/TestWebhookDialog"; @@ -786,7 +787,10 @@ function SavedTaskForm({ initialValues }: Props) { return { method: "POST", url: `${runsApiBaseUrl}/run/tasks`, - body: buildTaskRunPayload(createTaskRequestObject(formValues)), + body: buildTaskRunPayload( + createTaskRequestObject(formValues), + RunEngine.SkyvernV1, + ), headers, } satisfies ApiCommandOptions; }} diff --git a/skyvern-frontend/src/routes/tasks/detail/TaskDetails.tsx b/skyvern-frontend/src/routes/tasks/detail/TaskDetails.tsx index cd5eb789..1048a6ca 100644 --- a/skyvern-frontend/src/routes/tasks/detail/TaskDetails.tsx +++ b/skyvern-frontend/src/routes/tasks/detail/TaskDetails.tsx @@ -1,6 +1,7 @@ import { getClient } from "@/api/AxiosClient"; import { useState } from "react"; import { + RunEngine, Status, TaskApiResponse, WorkflowRunStatusApiResponse, @@ -227,7 +228,10 @@ function TaskDetails() { return { method: "POST", url: `${runsApiBaseUrl}/run/tasks`, - body: buildTaskRunPayload(createTaskRequestObject(task)), + body: buildTaskRunPayload( + createTaskRequestObject(task), + RunEngine.SkyvernV1, + ), headers, } satisfies ApiCommandOptions; }} diff --git a/skyvern-frontend/src/util/taskRunPayload.test.ts b/skyvern-frontend/src/util/taskRunPayload.test.ts index 153edd5e..0164d508 100644 --- a/skyvern-frontend/src/util/taskRunPayload.test.ts +++ b/skyvern-frontend/src/util/taskRunPayload.test.ts @@ -9,6 +9,7 @@ describe("buildTaskRunPayload", () => { url: " https://example.com/task ", navigation_goal: "Navigate somewhere", data_extraction_goal: "Collect some data", + navigation_payload: { name: "John", age: 30 }, webhook_callback_url: " https://callback.example.com ", proxy_location: "RESIDENTIAL", extracted_information_schema: { foo: "bar" }, @@ -23,7 +24,8 @@ describe("buildTaskRunPayload", () => { const payload = buildTaskRunPayload(request); expect(payload).toEqual({ - prompt: "Navigate somewhere\n\nCollect some data", + prompt: + 'Navigate somewhere\n\nCollect some data\n\n{"name":"John","age":30}', url: "https://example.com/task", proxy_location: "RESIDENTIAL", data_extraction_schema: { foo: "bar" }, @@ -44,6 +46,7 @@ describe("buildTaskRunPayload", () => { url: " https://fallback.example.com ", navigation_goal: "", data_extraction_goal: null, + navigation_payload: null, webhook_callback_url: " ", proxy_location: null, extracted_information_schema: null, @@ -71,4 +74,30 @@ describe("buildTaskRunPayload", () => { error_code_mapping: undefined, }); }); + + it("includes navigation_payload as string in prompt", () => { + const request: CreateTaskRequest = { + url: "https://example.com", + navigation_goal: "Fill form", + navigation_payload: '{"email": "test@example.com"}', + }; + + const payload = buildTaskRunPayload(request); + + expect(payload.prompt).toBe('Fill form\n\n{"email": "test@example.com"}'); + }); + + it("formats navigation_payload object as JSON in prompt", () => { + const request: CreateTaskRequest = { + url: "https://example.com", + navigation_goal: "Fill form", + navigation_payload: { email: "test@example.com", name: "Test" }, + }; + + const payload = buildTaskRunPayload(request); + + expect(payload.prompt).toBe( + 'Fill form\n\n{"email":"test@example.com","name":"Test"}', + ); + }); }); diff --git a/skyvern-frontend/src/util/taskRunPayload.ts b/skyvern-frontend/src/util/taskRunPayload.ts index c5e6b761..b2980190 100644 --- a/skyvern-frontend/src/util/taskRunPayload.ts +++ b/skyvern-frontend/src/util/taskRunPayload.ts @@ -1,4 +1,4 @@ -import type { CreateTaskRequest, ProxyLocation } from "@/api/types"; +import type { CreateTaskRequest, ProxyLocation, RunEngine } from "@/api/types"; type TaskRunPayload = { prompt: string; @@ -13,6 +13,7 @@ type TaskRunPayload = { include_action_history_in_verification?: boolean | null; max_screenshot_scrolls?: number | null; title?: string | null; + engine?: RunEngine | null; }; // Helper to trim and check for empty strings @@ -21,12 +22,33 @@ const trim = (s: string | null | undefined): string | undefined => { return t && t.length > 0 ? t : undefined; }; -// Build prompt from navigation_goal + data_extraction_goal +// Helper to format navigation_payload as a string +function formatNavigationPayload( + payload: Record | string | null | undefined, +): string | undefined { + if (payload == null) return undefined; + if (typeof payload === "string") { + const trimmed = payload.trim(); + return trimmed.length > 0 ? trimmed : undefined; + } + if (typeof payload === "object" && !Array.isArray(payload)) { + try { + const jsonStr = JSON.stringify(payload); + return jsonStr.length > 0 ? jsonStr : undefined; + } catch { + return undefined; + } + } + return undefined; +} + +// Build prompt from navigation_goal + data_extraction_goal + navigation_payload function buildPrompt(request: CreateTaskRequest): string { const nav = trim(request.navigation_goal); const extract = trim(request.data_extraction_goal); + const payload = formatNavigationPayload(request.navigation_payload); - const parts = [nav, extract].filter(Boolean); + const parts = [nav, extract, payload].filter(Boolean); if (parts.length > 0) return parts.join("\n\n"); // Fallback chain: try title, then goals again, then url, then default @@ -47,14 +69,17 @@ function isValidRecord(val: unknown): val is Record { * Transforms a CreateTaskRequest (old schema) to TaskRunPayload (Runs API v2 schema). * * Key transformations: - * - navigation_goal + data_extraction_goal → prompt (combined) + * - navigation_goal + data_extraction_goal + navigation_payload → prompt (combined) * - extracted_information_schema → data_extraction_schema * - webhook_callback_url → webhook_url * * Note: max_steps is optional and can be added manually to the cURL if needed. */ -function buildTaskRunPayload(request: CreateTaskRequest): TaskRunPayload { - return { +function buildTaskRunPayload( + request: CreateTaskRequest, + engine?: RunEngine | null, +): TaskRunPayload { + const payload: TaskRunPayload = { prompt: buildPrompt(request), url: trim(request.url) ?? null, proxy_location: request.proxy_location ?? null, @@ -73,6 +98,10 @@ function buildTaskRunPayload(request: CreateTaskRequest): TaskRunPayload { ? request.error_code_mapping : undefined, }; + if (engine) { + payload.engine = engine; + } + return payload; } export type { TaskRunPayload };