diff --git a/flight-booking-app/app/api/hooks/webhook/[token]/route.ts b/flight-booking-app/app/api/hooks/webhook/[token]/route.ts new file mode 100644 index 0000000..eaa3ca6 --- /dev/null +++ b/flight-booking-app/app/api/hooks/webhook/[token]/route.ts @@ -0,0 +1,43 @@ +import { webhookHook } from '@/workflows/chat/hooks/webhook'; + +export async function POST( + request: Request, + { params }: { params: Promise<{ token: string }> } +) { + const { token } = await params; + + let body: any; + const contentType = request.headers.get('content-type') || ''; + try { + if (contentType.includes('application/json')) { + body = await request.json(); + } else { + body = await request.text(); + } + } catch { + body = null; + } + + await webhookHook.resume(token, { + method: request.method, + body, + }); + + return Response.json({ received: true }); +} + +export async function GET( + request: Request, + { params }: { params: Promise<{ token: string }> } +) { + const { token } = await params; + const url = new URL(request.url); + const body = Object.fromEntries(url.searchParams.entries()); + + await webhookHook.resume(token, { + method: 'GET', + body: Object.keys(body).length > 0 ? body : null, + }); + + return Response.json({ received: true }); +} diff --git a/flight-booking-app/app/page.tsx b/flight-booking-app/app/page.tsx index 16c5d8b..de2e5bb 100644 --- a/flight-booking-app/app/page.tsx +++ b/flight-booking-app/app/page.tsx @@ -18,6 +18,8 @@ import { ToolOutput, } from "@/components/ai-elements/tool"; import { BookingApproval } from "@/components/booking-approval"; +import { WebhookWaiting } from "@/components/webhook-waiting"; +import { SandboxWidget } from "@/components/sandbox-widget"; import { useMultiTurnChat } from "@/hooks/use-multi-turn-chat"; import type { MyMessageMetadata } from "@/schemas/chat"; import ChatInput from "@/components/chat-input"; @@ -36,7 +38,7 @@ const SUGGESTIONS = [ ]; const FULL_EXAMPLE_PROMPT = - "Book me the cheapest flight from San Francisco to Los Angeles for July 27 2025. My name is Pranay Prakash. I like window seats. Don't ask me for approval."; + "Book me the cheapest flight from San Francisco to Los Angeles for July 27 2026. My name is Peter Wielander. I like window seats. Don't ask me for approval."; export default function ChatPage() { const textareaRef = useRef(null); @@ -137,11 +139,49 @@ export default function ChatPage() { const hasText = message.parts.some((part) => part.type === "text"); const isLastMessage = index === messages.length - 1; + // Deduplicate tool calls from doStreamStep retries. + // When a stream step fails and retries, each attempt emits tool-call + // chunks with different toolCallIds. Skip the phantom ones (no output) + // if a later call of the same tool type completed with output. + const supersededToolCallIds = new Set(); + const toolPartsByType = new Map(); + for (const part of message.parts) { + if ("toolCallId" in part && "state" in part) { + const type = (part as any).type as string; + if (!toolPartsByType.has(type)) toolPartsByType.set(type, []); + toolPartsByType.get(type)!.push(part); + } + } + for (const [, parts] of toolPartsByType) { + if (parts.length <= 1) continue; + const hasCompleted = parts.some( + (p: any) => + p.state === "output-available" || p.state === "output-error" + ); + if (hasCompleted) { + for (const p of parts) { + if ( + p.state !== "output-available" && + p.state !== "output-error" + ) { + supersededToolCallIds.add(p.toolCallId); + } + } + } + } + return (
{message.parts.map((part, partIndex) => { + // Skip phantom tool calls from retried stream steps + if ( + "toolCallId" in part && + supersededToolCallIds.has((part as any).toolCallId) + ) { + return null; + } // Render text parts if (part.type === "text") { return ( @@ -158,14 +198,7 @@ export default function ChatPage() { if (data?.type === "user-message") { return null; } - // Render observability events inline - return ( - - ); + return null; } // Render tool parts @@ -175,7 +208,8 @@ export default function ChatPage() { part.type === "tool-getAirportInfo" || part.type === "tool-bookFlight" || part.type === "tool-checkBaggageAllowance" || - part.type === "tool-sleep" + part.type === "tool-sleep" || + part.type === "tool-runCode" ) { if (!("toolCallId" in part) || !("state" in part)) { return null; @@ -221,26 +255,74 @@ export default function ChatPage() { ); } + // Render webhook waiting + if (part.type === "tool-waitForWebhook") { + return ( + + ); + } + return null; })} {/* Loading indicators for assistant messages */} {message.role === "assistant" && isLastMessage && - !hasText && ( - <> - {status === "submitted" && ( - - Sending message... - - )} - {status === "streaming" && ( - - Waiting for response... - - )} - - )} + !hasText && (() => { + const hasSleepActive = message.parts.some( + (part) => + part.type === "tool-sleep" && + "state" in part && + part.state !== "output-available" + ); + const hasApprovalActive = message.parts.some( + (part) => + (part.type === "tool-bookingApproval" || + part.type === "tool-waitForWebhook") && + "state" in part && + part.state !== "output-available" + ); + const hasSandboxActive = message.parts.some( + (part) => + part.type === "tool-runCode" && + "state" in part && + part.state !== "output-available" + ); + return ( + <> + {status === "submitted" && ( + + Sending message... + + )} + {status === "streaming" && !hasSleepActive && !hasApprovalActive && !hasSandboxActive && ( + + Waiting for response... + + )} + {status === "streaming" && hasSleepActive && ( + + Sleeping... + + )} + {status === "streaming" && hasApprovalActive && ( + + Waiting for approval... + + )} + {status === "streaming" && hasSandboxActive && ( + + Running in sandbox... + + )} + + ); + })()}
@@ -294,6 +376,8 @@ export default function ChatPage() { onSendMessage={sendMessage} stop={stop} /> + + ); } @@ -431,6 +515,10 @@ function WorkflowEventBadge({ data, t0 }: { data: any; t0: number | null }) { // Skip rendering agent-step since we now have realtime tool-call events return null; + case "sandbox-event": + // Rendered in the floating SandboxWidget instead + return null; + default: // Render generic data messages if (data?.message) { @@ -661,6 +749,48 @@ function renderToolOutput(part: any) { ); } + case "tool-runCode": { + if (!parsedOutput) return null; + + // Error case: tool caught the error and returned structured data + if (parsedOutput.error) { + return ( +
+
+ Sandbox error during: {parsedOutput.phase || "unknown"} +
+
+                {parsedOutput.message || JSON.stringify(parsedOutput, null, 2)}
+              
+
+ ); + } + + // Success case + const { exitCode, stdout, stderr } = parsedOutput; + return ( +
+ {exitCode === 0 ? ( +
Exited with code 0
+ ) : ( +
Exit code: {exitCode}
+ )} + {stdout && stdout !== "(no output)" && ( +
+
stdout
+
{stdout}
+
+ )} + {stderr && ( +
+
stderr
+
{stderr}
+
+ )} +
+ ); + } + default: return null; } diff --git a/flight-booking-app/components/booking-approval.tsx b/flight-booking-app/components/booking-approval.tsx index ef43bfa..59ba799 100644 --- a/flight-booking-app/components/booking-approval.tsx +++ b/flight-booking-app/components/booking-approval.tsx @@ -20,22 +20,11 @@ export function BookingApproval({ // If we have output, the approval has been processed if (output) { - try { - const json = JSON.parse(output) as { output: { value: string } }; - return ( -
-

{json.output.value}

-
- ); - } catch (error) { - return ( -
-

- Error parsing approval result: {(error as Error).message} -

-
- ); - } + return ( +
+

{output}

+
+ ); } const handleSubmit = async (approved: boolean) => { diff --git a/flight-booking-app/components/sandbox-widget.tsx b/flight-booking-app/components/sandbox-widget.tsx new file mode 100644 index 0000000..1266f84 --- /dev/null +++ b/flight-booking-app/components/sandbox-widget.tsx @@ -0,0 +1,280 @@ +"use client"; + +import { useMemo, useState } from "react"; +import type { UIMessage } from "ai"; +import { + TerminalIcon, + ChevronDownIcon, + ChevronUpIcon, + CircleIcon, + CheckCircleIcon, + XCircleIcon, + LoaderIcon, +} from "lucide-react"; + +interface SandboxEvent { + event: string; + timestamp: number; + sandboxId?: string; + command?: string; + exitCode?: number; + fileCount?: number; + filePaths?: string[]; + phase?: string; + message?: string; + status?: string; +} + +function extractSandboxEvents(messages: UIMessage[]): SandboxEvent[] { + const events: SandboxEvent[] = []; + for (const msg of messages) { + for (const part of msg.parts) { + if ( + part.type === "data-workflow" && + "data" in part && + (part.data as any)?.type === "sandbox-event" + ) { + const { type: _, ...rest } = part.data as any; + events.push(rest as SandboxEvent); + } + } + } + return events; +} + +function getStatusFromEvents(events: SandboxEvent[]): { + status: string; + sandboxId: string | null; + lastCommand: string | null; + lastExitCode: number | null; + hasError: boolean; + lastError: string | null; +} { + let status = "idle"; + let sandboxId: string | null = null; + let lastCommand: string | null = null; + let lastExitCode: number | null = null; + let hasError = false; + let lastError: string | null = null; + + for (const ev of events) { + if (ev.sandboxId) sandboxId = ev.sandboxId; + + switch (ev.event) { + case "creating": + status = "creating"; + break; + case "connecting": + status = "connecting"; + break; + case "ready": + status = "ready"; + break; + case "writing-files": + status = "writing files"; + break; + case "files-written": + status = "ready"; + break; + case "running-command": + status = "running"; + lastCommand = ev.command || null; + lastExitCode = null; + break; + case "command-complete": + status = "ready"; + lastExitCode = ev.exitCode ?? null; + break; + case "error": + hasError = true; + lastError = ev.message || ev.phase || "unknown error"; + status = "error"; + break; + } + } + + return { status, sandboxId, lastCommand, lastExitCode, hasError, lastError }; +} + +const statusConfig: Record< + string, + { icon: React.ReactNode; color: string; label: string } +> = { + idle: { + icon: , + color: "text-muted-foreground", + label: "Idle", + }, + creating: { + icon: , + color: "text-yellow-500", + label: "Creating...", + }, + connecting: { + icon: , + color: "text-yellow-500", + label: "Connecting...", + }, + ready: { + icon: , + color: "text-green-500", + label: "Ready", + }, + "writing files": { + icon: , + color: "text-blue-400", + label: "Writing files...", + }, + running: { + icon: , + color: "text-blue-400", + label: "Running...", + }, + error: { + icon: , + color: "text-red-400", + label: "Error", + }, +}; + +export function SandboxWidget({ messages }: { messages: UIMessage[] }) { + const [expanded, setExpanded] = useState(false); + + const events = useMemo(() => extractSandboxEvents(messages), [messages]); + const { status, sandboxId, lastCommand, lastExitCode, hasError, lastError } = + useMemo(() => getStatusFromEvents(events), [events]); + + // Don't render if no sandbox events yet + if (events.length === 0) return null; + + const cfg = statusConfig[status] || statusConfig.idle; + + return ( +
+
+ {/* Header — always visible */} + + + {/* Expanded content */} + {expanded && ( +
+ {/* Last command */} + {lastCommand && ( +
+
+ Last command +
+ + {lastCommand} + + {lastExitCode !== null && ( + + exit={lastExitCode} + + )} +
+ )} + + {/* Error display */} + {hasError && lastError && ( +
+
+ Last error +
+
+                  {lastError}
+                
+
+ )} + + {/* Event log */} +
+
+ Events ({events.length}) +
+
+ {events + .slice() + .reverse() + .slice(0, 20) + .map((ev, i) => { + const isError = ev.event === "error"; + return ( +
+ + {ev.event} + {ev.command && ( + + {ev.command.slice(0, 30)} + + )} + {ev.filePaths && ( + + {ev.filePaths.join(", ")} + + )} + {ev.exitCode !== undefined && ( + + exit={ev.exitCode} + + )} + {isError && ev.message && ( + + {ev.message.split("\n")[0].slice(0, 50)} + + )} + + + {new Date(ev.timestamp).toLocaleTimeString()} + +
+ ); + })} +
+
+
+ )} +
+
+ ); +} diff --git a/flight-booking-app/components/webhook-waiting.tsx b/flight-booking-app/components/webhook-waiting.tsx new file mode 100644 index 0000000..8a28169 --- /dev/null +++ b/flight-booking-app/components/webhook-waiting.tsx @@ -0,0 +1,117 @@ +"use client"; + +import { useState } from "react"; +import { CheckCircleIcon, CopyIcon, WebhookIcon } from "lucide-react"; + +interface WebhookWaitingProps { + toolCallId: string; + input?: { + description: string; + }; + output?: string; +} + +export function WebhookWaiting({ + toolCallId, + input, + output, +}: WebhookWaitingProps) { + const [copied, setCopied] = useState(false); + + const webhookUrl = + typeof window !== "undefined" + ? `${window.location.origin}/api/hooks/webhook/${encodeURIComponent(toolCallId)}` + : `/api/hooks/webhook/${encodeURIComponent(toolCallId)}`; + + const curlExample = `curl -X POST ${webhookUrl} -H "Content-Type: application/json" -d '{"status": "done"}'`; + + const handleCopy = async (text: string) => { + await navigator.clipboard.writeText(text); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + // Webhook has been called — show the received payload + if (output) { + let parsed: any; + try { + parsed = typeof output === "string" ? JSON.parse(output) : output; + } catch { + parsed = output; + } + + return ( +
+
+ + Webhook received +
+ {input?.description && ( +

{input.description}

+ )} + {parsed?.body && ( +
+            {typeof parsed.body === "string"
+              ? parsed.body
+              : JSON.stringify(parsed.body, null, 2)}
+          
+ )} +
+ ); + } + + // Waiting for webhook call + return ( +
+
+ + Waiting for webhook +
+ + {input?.description && ( +

{input.description}

+ )} + +
+
+ Webhook URL +
+
+ + {webhookUrl} + + +
+
+ +
+
+ Example +
+
 handleCopy(curlExample)}
+          title="Click to copy"
+        >
+          {curlExample}
+        
+
+ +
+ + Waiting for external call... +
+
+ ); +} diff --git a/flight-booking-app/hooks/use-multi-turn-chat.ts b/flight-booking-app/hooks/use-multi-turn-chat.ts index 1cbd7b7..79b57a8 100644 --- a/flight-booking-app/hooks/use-multi-turn-chat.ts +++ b/flight-booking-app/hooks/use-multi-turn-chat.ts @@ -111,13 +111,22 @@ export function useMultiTurnChat< // Track which message content we've seen from stream (to clear pending) const seenFromStreamRef = useRef>(new Set()); - // Initialize from localStorage on mount + // Initialize from URL query param or localStorage on mount useEffect(() => { if (typeof window !== 'undefined') { - const storedRunId = localStorage.getItem(STORAGE_KEY); + const params = new URLSearchParams(window.location.search); + const urlRunId = params.get('run'); + const storedRunId = urlRunId || localStorage.getItem(STORAGE_KEY); if (storedRunId) { setRunId(storedRunId); setShouldResume(true); + localStorage.setItem(STORAGE_KEY, storedRunId); + // Ensure URL reflects the run ID + if (!urlRunId) { + const url = new URL(window.location.href); + url.searchParams.set('run', storedRunId); + window.history.replaceState({}, '', url.toString()); + } } } }, []); @@ -133,6 +142,10 @@ export function useMultiTurnChat< if (workflowRunId) { setRunId(workflowRunId); localStorage.setItem(STORAGE_KEY, workflowRunId); + // Push run ID into URL so the link is shareable/resumable + const url = new URL(window.location.href); + url.searchParams.set('run', workflowRunId); + window.history.replaceState({}, '', url.toString()); } }, onChatEnd: () => { @@ -142,6 +155,10 @@ export function useMultiTurnChat< sentMessagesRef.current.clear(); seenFromStreamRef.current.clear(); setPendingMessage(null); + // Clear run ID from URL + const url = new URL(window.location.href); + url.searchParams.delete('run'); + window.history.replaceState({}, '', url.toString()); }, // Configure reconnection to use the stored workflow run ID prepareReconnectToStreamRequest: ({ api, ...rest }) => { @@ -355,6 +372,10 @@ export function useMultiTurnChat< setPendingMessage(null); setMessages([]); stop(); + // Clear run ID from URL + const url = new URL(window.location.href); + url.searchParams.delete('run'); + window.history.replaceState({}, '', url.toString()); }, [runId, setMessages, stop]); return { diff --git a/flight-booking-app/package.json b/flight-booking-app/package.json index 25d206e..60535ee 100644 --- a/flight-booking-app/package.json +++ b/flight-booking-app/package.json @@ -19,7 +19,7 @@ "@streamdown/math": "^1.0.1", "@streamdown/mermaid": "^1.0.1", "@vercel/otel": "^1.13.0", - "@workflow/ai": "4.0.1-beta.54", + "@workflow/ai": "4.1.0-beta.59", "@workflow/world-postgres": "4.1.0-beta.42", "@xyflow/react": "^12.9.0", "ai": "^6.0.69", @@ -44,7 +44,8 @@ "tokenlens": "^1.3.1", "typescript": "^5.9.3", "use-stick-to-bottom": "^1.1.1", - "workflow": "4.2.0-beta.67", + "workflow": "4.2.0-beta.74", + "@vercel/sandbox": "file:vercel-sandbox-1.9.0.tgz", "zod": "^4.1.12" }, "devDependencies": { diff --git a/flight-booking-app/pnpm-lock.yaml b/flight-booking-app/pnpm-lock.yaml index 55daab9..3393602 100644 --- a/flight-booking-app/pnpm-lock.yaml +++ b/flight-booking-app/pnpm-lock.yaml @@ -39,9 +39,12 @@ importers: '@vercel/otel': specifier: ^1.13.0 version: 1.14.0(@opentelemetry/api-logs@0.57.2)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0)) + '@vercel/sandbox': + specifier: file:vercel-sandbox-1.9.0.tgz + version: file:vercel-sandbox-1.9.0.tgz '@workflow/ai': - specifier: 4.0.1-beta.54 - version: 4.0.1-beta.54(ai@6.0.69(zod@4.1.12))(workflow@4.2.0-beta.67(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.0)(@swc/cli@0.7.10(@swc/core@1.15.3)(chokidar@4.0.3))(@swc/core@1.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-router@7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)) + specifier: 4.1.0-beta.59 + version: 4.1.0-beta.59(@opentelemetry/api@1.9.0)(ai@6.0.69(zod@4.1.12))(workflow@4.2.0-beta.74(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.0)(@swc/cli@0.7.10(@swc/core@1.15.3)(chokidar@4.0.3))(@swc/core@1.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)) '@workflow/world-postgres': specifier: 4.1.0-beta.42 version: 4.1.0-beta.42(@opentelemetry/api@1.9.0)(@types/pg@8.18.0)(pg@8.18.0)(typescript@5.9.3) @@ -115,8 +118,8 @@ importers: specifier: ^1.1.1 version: 1.1.1(react@19.2.3) workflow: - specifier: 4.2.0-beta.67 - version: 4.2.0-beta.67(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.0)(@swc/cli@0.7.10(@swc/core@1.15.3)(chokidar@4.0.3))(@swc/core@1.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-router@7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) + specifier: 4.2.0-beta.74 + version: 4.2.0-beta.74(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.0)(@swc/cli@0.7.10(@swc/core@1.15.3)(chokidar@4.0.3))(@swc/core@1.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) zod: specifier: ^4.1.12 version: 4.1.12 @@ -706,9 +709,6 @@ packages: '@mermaid-js/parser@0.6.3': resolution: {integrity: sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==} - '@mjackson/node-fetch-server@0.2.0': - resolution: {integrity: sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng==} - '@napi-rs/nice-android-arm-eabi@1.1.1': resolution: {integrity: sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==} engines: {node: '>= 10'} @@ -908,8 +908,8 @@ packages: cpu: [x64] os: [win32] - '@nuxt/kit@4.3.1': - resolution: {integrity: sha512-UjBFt72dnpc+83BV3OIbCT0YHLevJtgJCHpxMX0YRKWLDhhbcDdUse87GtsQBrjvOzK7WUNUYLDS/hQLYev5rA==} + '@nuxt/kit@4.4.2': + resolution: {integrity: sha512-5+IPRNX2CjkBhuWUwz0hBuLqiaJPRoKzQ+SvcdrQDbAyE+VDeFt74VpSFr5/R0ujrK4b+XnSHUJWdS72w6hsog==} engines: {node: '>=18.12.0'} '@nuxt/opencollective@0.4.1': @@ -1663,16 +1663,6 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - '@react-router/node@7.13.1': - resolution: {integrity: sha512-IWPPf+Q3nJ6q4bwyTf5leeGUfg8GAxSN1RKj5wp9SK915zKK+1u4TCOfOmr8hmC6IW1fcjKV0WChkM0HkReIiw==} - engines: {node: '>=20.0.0'} - peerDependencies: - react-router: 7.13.1 - typescript: ^5.1.0 - peerDependenciesMeta: - typescript: - optional: true - '@rive-app/react-webgl2@4.26.2': resolution: {integrity: sha512-AwJ7wzx6jzfTD2rrQ8Tt5u5W7b9B4ctnQQnHfIs4ldUM5DV3tE11d81sHOeYNrW9GishgOvU9jsMdHsSnozNzg==} peerDependencies: @@ -2316,24 +2306,36 @@ packages: resolution: {integrity: sha512-ozO0tSBXUYN4gUkK65GbcqgxpC55qaaiY9MzNuXW4cvOSJ5nCkcgO+DQXcfyfL7h+0uIC5HTcP0mPvQ3dW3EhQ==} engines: {node: '>=20.0.0'} - '@workflow/ai@4.0.1-beta.54': - resolution: {integrity: sha512-vdRc6g22Obpugia6hzskGZ8oUMRSU7mjxFCjK/M2yO3GrLZC+eNiMVMgyn1iNRQmwyA/ngVzeMKV6QyIHV2orQ==} + '@vercel/queue@0.1.4': + resolution: {integrity: sha512-wo+jCycmCX078vQSbkX+RcLvySONDCK0f9aQp5UMKQD1+B+xKt3YVbIYbZukvoHQpbm5nnk6If+ADSeK/PmCgQ==} + engines: {node: '>=20.0.0'} + + '@vercel/sandbox@file:vercel-sandbox-1.9.0.tgz': + resolution: {integrity: sha512-/VtPq+1rUoxL7i5D5AQb6G3VvOYOgeKAAPDzzB0Pj0PFmKF22g8Fux77NYlMFvBq0iFOenePUxc9vDde0ykTBQ==, tarball: file:vercel-sandbox-1.9.0.tgz} + version: 1.9.0 + + '@workflow/ai@4.1.0-beta.59': + resolution: {integrity: sha512-Vy11b0fGlbInM1HTnb0iuodEjAW2nMP81KFtUbSfjN20nmlxNADvpOtYkMG8fOk9aSzNGcrzzPMySnze7Xmbag==} peerDependencies: - ai: ^5 || ^6 - workflow: ^4.1.0-beta.62 + '@opentelemetry/api': ^1.0.0 + ai: ^6 + workflow: ^4.2.0-beta.74 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true - '@workflow/astro@4.0.0-beta.41': - resolution: {integrity: sha512-CHpZduft3RFjlpsbAidIhHrGifYtj/y4ueb/+BqWLagt/Vlye9lTn/RPOvfJLcXL/bkn8fo+22giIwZCCGK1/A==} + '@workflow/astro@4.0.0-beta.48': + resolution: {integrity: sha512-BoO5Wc3gjhjKytA/HhgTRmcvmhhoqhLFEAcMPpqzCZKMstGhgNDyVhn00a0PvLASJguTqrmpGYoiaVJhQdUG2w==} - '@workflow/builders@4.0.1-beta.58': - resolution: {integrity: sha512-mGL5UnMuK6IZiVJbS5/hpYnhHs3qjB0Cj05Q/QpekryFEMUKsKKTDr1LLGQ8LiEW/dRO9NZ/Po03+hAqukUj5A==} + '@workflow/builders@4.0.1-beta.65': + resolution: {integrity: sha512-QC70oG32Lg+Lp07v8SCCQPHEqWYPRCQT3m0lCT8i5OV41OkjIbvzggxybdBmgVfXy/8hnecdMfPRXFtdHJwb2w==} - '@workflow/cli@4.2.0-beta.67': - resolution: {integrity: sha512-yQ+OVYwrC3CpmXMOcRHh5QsPP+dXodzSQUFnyBrSRfZaAsPEY7wS+UOOK0j322Z+soapGkuBOS/CA2klCqJLPA==} + '@workflow/cli@4.2.0-beta.74': + resolution: {integrity: sha512-3fqnYLZEEVR5CiBNiH17kcAcPCMHYvODJ15cyebZrxxknnqOgD0LrZ9ShM+TcIWRvioIaLS12lMpH+ujYFgqew==} hasBin: true - '@workflow/core@4.2.0-beta.67': - resolution: {integrity: sha512-Qoyktgenu1YuZUd4Q1GkAWnZ25MrRBXgd4wfLTqtdR40/mm71yKy/eDA7p+AlB3fXWVhQfcQMBf6kgWPrL/DCA==} + '@workflow/core@4.2.0-beta.74': + resolution: {integrity: sha512-tTfhzBYhLDciIh1oCxaMLxcIwBc3GLmNH79IYjbzd1mLm0BMMjsXm3gUlFCGDgdNKOQEBOk8EoL6vI8Fc/kOtg==} peerDependencies: '@opentelemetry/api': '1' peerDependenciesMeta: @@ -2343,8 +2345,11 @@ packages: '@workflow/errors@4.1.0-beta.18': resolution: {integrity: sha512-Ana+xAHp+rKyXe3yues+TOzK+DALLqHTFe7yBEcLjXQZRXof30Wx3BjBaADm2/syIcRpgR3Q2tuASqzrnWmjBg==} - '@workflow/nest@0.0.0-beta.16': - resolution: {integrity: sha512-MNA3uVKVjfgvisO6z8Juet1UH7r+YXLulT3FunP65CeXpIbTyMDPJPL36/ijUH5r8ncn6zPessNmflAycFzUzA==} + '@workflow/errors@4.1.0-beta.19': + resolution: {integrity: sha512-AEZefVGpant/4eUJixnjGlK3LfyNPWp/C6YpmAsJbPPaFHPggKE7WT185BCMtXJMbpVKF/d/rNa7fxZ3J/yYhw==} + + '@workflow/nest@0.0.0-beta.23': + resolution: {integrity: sha512-JCEEXOnz9o6+nnPEFoEfHp+qBHSUgBtOz4M24Mk8AAJ39iQZdR33uuCk+JxslkWGgNDJTEJlCdl/BQDvYR8BAQ==} hasBin: true peerDependencies: '@nestjs/common': '>=10.0.0' @@ -2352,31 +2357,31 @@ packages: '@swc/cli': '>=0.4.0' '@swc/core': '>=1.5.0' - '@workflow/next@4.0.1-beta.63': - resolution: {integrity: sha512-vnOHiOSXuUj9c7VTagN4wj50xVjAkMWwHM+akS0D2zHHVB/Ixbw+pu3CFFk9yUvuaXnZ7jVucDnz1H10d8eh7w==} + '@workflow/next@4.0.1-beta.70': + resolution: {integrity: sha512-tfsvUVaIOZBvJNOh8iPKCKRfkg1ZcFyJNMfNvS++ipOe569R/Mg2TAYFC7PQIG/5vOEXIm/CYg5cJ2+8p31Zxg==} peerDependencies: next: '>13' peerDependenciesMeta: next: optional: true - '@workflow/nitro@4.0.1-beta.62': - resolution: {integrity: sha512-BWY17XcWnfuZnjbHVJc0kNBgoBswz66FX7FcKagVIvpK5OPD8A/8MePY6TRJZvfq0/K4X6kVnEtoLB5DK5KKKg==} + '@workflow/nitro@4.0.1-beta.69': + resolution: {integrity: sha512-ml/k9hR73lV9qVQCKguZk1viXGNcViikTHB8JWjrUPh8sWgsZSQ3qUfaqfag5OtxpbBDAl1peNc4Af6+26RJlg==} - '@workflow/nuxt@4.0.1-beta.51': - resolution: {integrity: sha512-VTaSPh97CRDXdVJPN0IEXawFlvwHPo6GFWLpizIBprA+K/OmTphD3TkLdKx51CsOln78qy8beWfOvg3I7IIIFQ==} + '@workflow/nuxt@4.0.1-beta.58': + resolution: {integrity: sha512-34iaFvRopXoe7qHvu+xuRkmBNPgyl1AWltGfli1rnsfxIj1tvNIzifspNPaoDIaLbuXqAXDChVaULPukGpq91g==} - '@workflow/rollup@4.0.0-beta.24': - resolution: {integrity: sha512-JlxJ3uqXMPpgsPTuJ2Gab+j2DuEzXQmVveRUhz/hSEZqIB8j+EKvcrPYeQQKyA2+z1SVa2c9Duo7oiEg4xWw/A==} + '@workflow/rollup@4.0.0-beta.31': + resolution: {integrity: sha512-U23tqZfZGRSSr2ax5iNHKPlYc1chwRkLVmx70+wWg5CW+Vo8KWAfAcHsukZXWPkg08hBTC7LbilcfBDhHVPI7Q==} '@workflow/serde@4.1.0-beta.2': resolution: {integrity: sha512-8kkeoQKLDaKXefjV5dbhBj2aErfKp1Mc4pb6tj8144cF+Em5SPbyMbyLCHp+BVrFfFVCBluCtMx+jjvaFVZGww==} - '@workflow/sveltekit@4.0.0-beta.56': - resolution: {integrity: sha512-Knv6FnB9ux+fASrkBXSsiawdKUnhsbkoZvWHj93UuJTaEemdefTW+ahVvgYo7vauQkSTai2H3ayzvt59G8dFhg==} + '@workflow/sveltekit@4.0.0-beta.63': + resolution: {integrity: sha512-2g8DTWRmHfHS3wbMeVCVjmxxkE7P/cr3KEStAJTeTyzs9ROHt2oeMWEGmMn9R/moVZUV3vlJSSxuAY1G4rS1ng==} - '@workflow/swc-plugin@4.1.0-beta.18': - resolution: {integrity: sha512-X76FC/YaHbf7wkuv/5f0LS+LHQKNb9uZt8IGKg2B7o0zKteV/1rZXadAyk6IgA/lXv/zHO/6Eki3HOW8sR0WXg==} + '@workflow/swc-plugin@4.1.0-beta.21': + resolution: {integrity: sha512-CcZG8T3oxMNils93OTomXT54pMwqH2IPheiZjg4bEz2aQtIQXGyU3ul0NRCDyLFgQa9tb3mgW18yOsOqajr0Tg==} peerDependencies: '@swc/core': 1.15.3 @@ -2388,11 +2393,11 @@ packages: '@workflow/utils@4.1.0-beta.13': resolution: {integrity: sha512-3vVuXZVfLVeJ78MM6D0gNXg6hMZdDYAzmF92p+HxItI0B2Yk1EuDIIUfBXKWwTOKCCuKF4iroZt2u9BFqrs2AQ==} - '@workflow/vite@4.0.0-beta.17': - resolution: {integrity: sha512-nl/RYigJc+yEVLZdDq/vxGLHyxeMf6I1nKjm10aosxa5Xi9JSIWoiT7FKqOR/+cIAX699oJAlKjwd4gCHwaKlg==} + '@workflow/vite@4.0.0-beta.24': + resolution: {integrity: sha512-dHUaT1J+4N6mbF9ghKZwHthz7Ci1TUzvLMiUMz53MwpIObagSkF1JsbsJXZV3pZXnywNxo4WLzMAKFeruLX+7A==} - '@workflow/web@4.1.0-beta.39': - resolution: {integrity: sha512-nZ2rR0zwJ4zyYRhOiCsmam/mE3REXLlfagYOVCIpjIOftyP8AFAAnpo5mRgcvBa0feL8WFx0JMQ6sA6A+39XNA==} + '@workflow/web@4.1.0-beta.46': + resolution: {integrity: sha512-DCXnXtB2Li7sKBWqZqMfnHj1q/7NC29NjXEsDJMnIqqUVIoxC/H45PDrQKv1h8biBR9yGfLo/8ncJCpldjmEwQ==} '@workflow/world-local@4.1.0-beta.40': resolution: {integrity: sha512-Y+8x3b01wUST7ICsHH79lryZ8cf+UB9pSVJ4orU+p09WzXOrAjKR6mjd+zacdIKPcU0jvw5JDRYYTQgClaqKfw==} @@ -2402,12 +2407,20 @@ packages: '@opentelemetry/api': optional: true + '@workflow/world-local@4.1.0-beta.47': + resolution: {integrity: sha512-hJMbaggGT7UrhZsC3/P31t+HDafzo9kO4cjjKAzbl54YDPseR6j1GGtAkB2qYVKzsfE0L0BUQU0r8wpcpA4tXg==} + peerDependencies: + '@opentelemetry/api': '1' + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@workflow/world-postgres@4.1.0-beta.42': resolution: {integrity: sha512-4OKv5UKyiWMJAG+H4G7ekj/5xhqiOkRkOFcZVOgoOwGKCOHUzJdpV2u5wSX2Al7IfrteX0lmSP0G9VVhU3JNYw==} hasBin: true - '@workflow/world-vercel@4.1.0-beta.41': - resolution: {integrity: sha512-24NyvN/JvkXyDow/FYFHLuwtRSqfemFCTL5vda3952b8z8/7laWPjPsgia98LoWdqIypbNvcPrtRkNi1hd+4AQ==} + '@workflow/world-vercel@4.1.0-beta.45': + resolution: {integrity: sha512-+2u8BtWnOWgdry22zJj9QSgh4s1WrTy9EvAJKx+sFnG24mrVBAW83IvqxpDIXAAd72PTFnEXyGHP4LUIO2KJag==} peerDependencies: '@opentelemetry/api': '1' peerDependenciesMeta: @@ -2419,6 +2432,11 @@ packages: peerDependencies: zod: 4.3.6 + '@workflow/world@4.1.0-beta.14': + resolution: {integrity: sha512-23BC9d7m7DDuGuaLncMHZ/6XFJePsY7Dr2zVecRWpM6NvOmAKV+oiOav/HEBxUCdaccSshSsLRqwdHX9Q9HCZA==} + peerDependencies: + zod: 4.3.6 + '@xhmikosr/archive-type@7.1.0': resolution: {integrity: sha512-xZEpnGplg1sNPyEgFh0zbHxqlw5dtYg6viplmWSxUj12+QjU9SKu3U/2G73a15pEjLaOqTefNSZ1fOPUOT4Xgg==} engines: {node: '>=18'} @@ -2482,6 +2500,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + ai@6.0.69: resolution: {integrity: sha512-zIURMSnNroaVvu47Bm3XhC2y3LRsm8jmkwBgupxF+N7q/s6MpIiv04w1ltlnWqC8+T2PT2rN+f0sUhF+vArkwg==} engines: {node: '>=18'} @@ -2545,6 +2568,9 @@ packages: resolution: {integrity: sha512-V+SsTpDqkrWTimiotsyl33ePSjA5/KrithwupuvJ6ztsqPvGv6ge4OredFhPffVXiLN/QUWvE0XcqJaYgt6fOg==} engines: {node: '>= 14'} + async-retry@1.3.3: + resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} + async-sema@3.1.1: resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} @@ -2821,10 +2847,6 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} - cookie@1.1.1: - resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} - engines: {node: '>=18'} - cose-base@1.0.3: resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} @@ -3711,10 +3733,6 @@ packages: resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} engines: {node: '>=16'} - isbot@5.1.35: - resolution: {integrity: sha512-waFfC72ZNfwLLuJ2iLaoVaqcNo+CAaLR7xCpAn0Y5WfGzkNHv7ZN39Vbi1y+kb+Zs46XHOX3tZNExroFUPX+Kg==} - engines: {node: '>=18'} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -3755,6 +3773,9 @@ packages: jsonfile@6.2.0: resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + jsonlines@0.1.1: + resolution: {integrity: sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==} + katex@0.16.28: resolution: {integrity: sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg==} hasBin: true @@ -4173,6 +4194,9 @@ packages: mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + module-details-from-path@1.0.4: resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} @@ -4535,16 +4559,6 @@ packages: '@types/react': optional: true - react-router@7.13.0: - resolution: {integrity: sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw==} - engines: {node: '>=20.0.0'} - peerDependencies: - react: '>=18' - react-dom: '>=18' - peerDependenciesMeta: - react-dom: - optional: true - react-style-singleton@2.2.3: resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} engines: {node: '>=10'} @@ -4665,6 +4679,10 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} @@ -4726,9 +4744,6 @@ packages: resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} engines: {node: '>= 0.8.0'} - set-cookie-parser@2.7.2: - resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} - setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -5162,8 +5177,8 @@ packages: wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - workflow@4.2.0-beta.67: - resolution: {integrity: sha512-0YYXLY3lqY+FdnAnxdTIKCZm8q0ulfTlCRw/LcnURaLY5nWJ0cAbs7UnssrAs7c5O7l8vo2YVXEBbbEO5bnWkg==} + workflow@4.2.0-beta.74: + resolution: {integrity: sha512-s9RIdUCuVnPZIw/i6cCLQe04UJDAN296NJ/sN+/BRcxVxgLqMm/ybCk0EcrhcxhhwBkP5QHjj50SFlAUT1FdBw==} hasBin: true peerDependencies: '@opentelemetry/api': '1' @@ -5215,6 +5230,9 @@ packages: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} + zod@3.24.4: + resolution: {integrity: sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==} + zod@4.1.11: resolution: {integrity: sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==} @@ -5797,8 +5815,6 @@ snapshots: dependencies: langium: 3.3.1 - '@mjackson/node-fetch-server@0.2.0': {} - '@napi-rs/nice-android-arm-eabi@1.1.1': optional: true @@ -5921,7 +5937,7 @@ snapshots: '@next/swc-win32-x64-msvc@16.0.10': optional: true - '@nuxt/kit@4.3.1': + '@nuxt/kit@4.4.2': dependencies: c12: 3.3.3 consola: 3.4.2 @@ -5932,7 +5948,7 @@ snapshots: ignore: 7.0.5 jiti: 2.6.1 klona: 2.0.6 - mlly: 1.8.0 + mlly: 1.8.2 ohash: 2.0.11 pathe: 2.0.3 pkg-types: 2.3.0 @@ -6773,13 +6789,6 @@ snapshots: '@radix-ui/rect@1.1.1': {} - '@react-router/node@7.13.1(react-router@7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)': - dependencies: - '@mjackson/node-fetch-server': 0.2.0 - react-router: 7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - optionalDependencies: - typescript: 5.9.3 - '@rive-app/react-webgl2@4.26.2(react@19.2.3)': dependencies: '@rive-app/webgl2': 2.34.2 @@ -7535,11 +7544,35 @@ snapshots: '@vercel/oidc': 3.2.0 mixpart: 0.0.5 - '@workflow/ai@4.0.1-beta.54(ai@6.0.69(zod@4.1.12))(workflow@4.2.0-beta.67(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.0)(@swc/cli@0.7.10(@swc/core@1.15.3)(chokidar@4.0.3))(@swc/core@1.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-router@7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3))': + '@vercel/queue@0.1.4': + dependencies: + '@vercel/oidc': 3.2.0 + minimatch: 10.2.4 + mixpart: 0.0.5 + picocolors: 1.1.1 + + '@vercel/sandbox@file:vercel-sandbox-1.9.0.tgz': + dependencies: + '@vercel/oidc': 3.2.0 + '@workflow/serde': 4.1.0-beta.2 + async-retry: 1.3.3 + jsonlines: 0.1.1 + ms: 2.1.3 + picocolors: 1.1.1 + tar-stream: 3.1.7 + undici: 7.22.0 + xdg-app-paths: 5.1.0 + zod: 3.24.4 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + '@workflow/ai@4.1.0-beta.59(@opentelemetry/api@1.9.0)(ai@6.0.69(zod@4.1.12))(workflow@4.2.0-beta.74(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.0)(@swc/cli@0.7.10(@swc/core@1.15.3)(chokidar@4.0.3))(@swc/core@1.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3))': dependencies: '@ai-sdk/provider': 3.0.7 + '@workflow/serde': 4.1.0-beta.2 ai: 6.0.69(zod@4.1.12) - workflow: 4.2.0-beta.67(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.0)(@swc/cli@0.7.10(@swc/core@1.15.3)(chokidar@4.0.3))(@swc/core@1.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-router@7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) + workflow: 4.2.0-beta.74(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.0)(@swc/cli@0.7.10(@swc/core@1.15.3)(chokidar@4.0.3))(@swc/core@1.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) zod: 4.3.6 optionalDependencies: '@ai-sdk/anthropic': 3.0.36(zod@4.3.6) @@ -7547,14 +7580,15 @@ snapshots: '@ai-sdk/google': 3.0.20(zod@4.3.6) '@ai-sdk/openai': 3.0.25(zod@4.3.6) '@ai-sdk/xai': 3.0.46(zod@4.3.6) + '@opentelemetry/api': 1.9.0 - '@workflow/astro@4.0.0-beta.41(@opentelemetry/api@1.9.0)': + '@workflow/astro@4.0.0-beta.48(@opentelemetry/api@1.9.0)': dependencies: '@swc/core': 1.15.3 - '@workflow/builders': 4.0.1-beta.58(@opentelemetry/api@1.9.0) - '@workflow/rollup': 4.0.0-beta.24(@opentelemetry/api@1.9.0) - '@workflow/swc-plugin': 4.1.0-beta.18(@swc/core@1.15.3) - '@workflow/vite': 4.0.0-beta.17(@opentelemetry/api@1.9.0) + '@workflow/builders': 4.0.1-beta.65(@opentelemetry/api@1.9.0) + '@workflow/rollup': 4.0.0-beta.31(@opentelemetry/api@1.9.0) + '@workflow/swc-plugin': 4.1.0-beta.21(@swc/core@1.15.3) + '@workflow/vite': 4.0.0-beta.24(@opentelemetry/api@1.9.0) exsolve: 1.0.8 pathe: 2.0.3 transitivePeerDependencies: @@ -7563,12 +7597,12 @@ snapshots: - aws-crt - supports-color - '@workflow/builders@4.0.1-beta.58(@opentelemetry/api@1.9.0)': + '@workflow/builders@4.0.1-beta.65(@opentelemetry/api@1.9.0)': dependencies: '@swc/core': 1.15.3 - '@workflow/core': 4.2.0-beta.67(@opentelemetry/api@1.9.0) - '@workflow/errors': 4.1.0-beta.18 - '@workflow/swc-plugin': 4.1.0-beta.18(@swc/core@1.15.3) + '@workflow/core': 4.2.0-beta.74(@opentelemetry/api@1.9.0) + '@workflow/errors': 4.1.0-beta.19 + '@workflow/swc-plugin': 4.1.0-beta.21(@swc/core@1.15.3) '@workflow/utils': 4.1.0-beta.13 builtin-modules: 5.0.0 chalk: 5.6.2 @@ -7583,21 +7617,21 @@ snapshots: - aws-crt - supports-color - '@workflow/cli@4.2.0-beta.67(@opentelemetry/api@1.9.0)(react-router@7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)': + '@workflow/cli@4.2.0-beta.74(@opentelemetry/api@1.9.0)': dependencies: '@oclif/core': 4.8.1 '@oclif/plugin-help': 6.2.37 '@swc/core': 1.15.3 '@vercel/cli-auth': 0.0.1 - '@workflow/builders': 4.0.1-beta.58(@opentelemetry/api@1.9.0) - '@workflow/core': 4.2.0-beta.67(@opentelemetry/api@1.9.0) - '@workflow/errors': 4.1.0-beta.18 - '@workflow/swc-plugin': 4.1.0-beta.18(@swc/core@1.15.3) + '@workflow/builders': 4.0.1-beta.65(@opentelemetry/api@1.9.0) + '@workflow/core': 4.2.0-beta.74(@opentelemetry/api@1.9.0) + '@workflow/errors': 4.1.0-beta.19 + '@workflow/swc-plugin': 4.1.0-beta.21(@swc/core@1.15.3) '@workflow/utils': 4.1.0-beta.13 - '@workflow/web': 4.1.0-beta.39(react-router@7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) - '@workflow/world': 4.1.0-beta.11(zod@4.3.6) - '@workflow/world-local': 4.1.0-beta.40(@opentelemetry/api@1.9.0) - '@workflow/world-vercel': 4.1.0-beta.41(@opentelemetry/api@1.9.0) + '@workflow/web': 4.1.0-beta.46 + '@workflow/world': 4.1.0-beta.14(zod@4.3.6) + '@workflow/world-local': 4.1.0-beta.47(@opentelemetry/api@1.9.0) + '@workflow/world-vercel': 4.1.0-beta.45(@opentelemetry/api@1.9.0) boxen: 8.0.1 builtin-modules: 5.0.0 chalk: 5.6.2 @@ -7619,23 +7653,21 @@ snapshots: - '@opentelemetry/api' - '@swc/helpers' - aws-crt - - react-router - supports-color - - typescript - '@workflow/core@4.2.0-beta.67(@opentelemetry/api@1.9.0)': + '@workflow/core@4.2.0-beta.74(@opentelemetry/api@1.9.0)': dependencies: '@aws-sdk/credential-provider-web-identity': 3.972.13 '@jridgewell/trace-mapping': 0.3.31 '@standard-schema/spec': 1.0.0 '@types/ms': 2.1.0 '@vercel/functions': 3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13) - '@workflow/errors': 4.1.0-beta.18 + '@workflow/errors': 4.1.0-beta.19 '@workflow/serde': 4.1.0-beta.2 '@workflow/utils': 4.1.0-beta.13 - '@workflow/world': 4.1.0-beta.11(zod@4.3.6) - '@workflow/world-local': 4.1.0-beta.40(@opentelemetry/api@1.9.0) - '@workflow/world-vercel': 4.1.0-beta.41(@opentelemetry/api@1.9.0) + '@workflow/world': 4.1.0-beta.14(zod@4.3.6) + '@workflow/world-local': 4.1.0-beta.47(@opentelemetry/api@1.9.0) + '@workflow/world-vercel': 4.1.0-beta.45(@opentelemetry/api@1.9.0) debug: 4.4.3(supports-color@8.1.1) devalue: 5.6.3 ms: 2.1.3 @@ -7654,14 +7686,19 @@ snapshots: '@workflow/utils': 4.1.0-beta.13 ms: 2.1.3 - '@workflow/nest@0.0.0-beta.16(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.0)(@swc/cli@0.7.10(@swc/core@1.15.3)(chokidar@4.0.3))(@swc/core@1.15.3)': + '@workflow/errors@4.1.0-beta.19': + dependencies: + '@workflow/utils': 4.1.0-beta.13 + ms: 2.1.3 + + '@workflow/nest@0.0.0-beta.23(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.0)(@swc/cli@0.7.10(@swc/core@1.15.3)(chokidar@4.0.3))(@swc/core@1.15.3)': dependencies: '@nestjs/common': 11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2) '@swc/cli': 0.7.10(@swc/core@1.15.3)(chokidar@4.0.3) '@swc/core': 1.15.3 - '@workflow/builders': 4.0.1-beta.58(@opentelemetry/api@1.9.0) - '@workflow/swc-plugin': 4.1.0-beta.18(@swc/core@1.15.3) + '@workflow/builders': 4.0.1-beta.65(@opentelemetry/api@1.9.0) + '@workflow/swc-plugin': 4.1.0-beta.21(@swc/core@1.15.3) pathe: 2.0.3 transitivePeerDependencies: - '@opentelemetry/api' @@ -7669,12 +7706,12 @@ snapshots: - aws-crt - supports-color - '@workflow/next@4.0.1-beta.63(@opentelemetry/api@1.9.0)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': + '@workflow/next@4.0.1-beta.70(@opentelemetry/api@1.9.0)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': dependencies: '@swc/core': 1.15.3 - '@workflow/builders': 4.0.1-beta.58(@opentelemetry/api@1.9.0) - '@workflow/core': 4.2.0-beta.67(@opentelemetry/api@1.9.0) - '@workflow/swc-plugin': 4.1.0-beta.18(@swc/core@1.15.3) + '@workflow/builders': 4.0.1-beta.65(@opentelemetry/api@1.9.0) + '@workflow/core': 4.2.0-beta.74(@opentelemetry/api@1.9.0) + '@workflow/swc-plugin': 4.1.0-beta.21(@swc/core@1.15.3) semver: 7.7.4 watchpack: 2.5.1 optionalDependencies: @@ -7685,14 +7722,14 @@ snapshots: - aws-crt - supports-color - '@workflow/nitro@4.0.1-beta.62(@opentelemetry/api@1.9.0)': + '@workflow/nitro@4.0.1-beta.69(@opentelemetry/api@1.9.0)': dependencies: '@swc/core': 1.15.3 - '@workflow/builders': 4.0.1-beta.58(@opentelemetry/api@1.9.0) - '@workflow/core': 4.2.0-beta.67(@opentelemetry/api@1.9.0) - '@workflow/rollup': 4.0.0-beta.24(@opentelemetry/api@1.9.0) - '@workflow/swc-plugin': 4.1.0-beta.18(@swc/core@1.15.3) - '@workflow/vite': 4.0.0-beta.17(@opentelemetry/api@1.9.0) + '@workflow/builders': 4.0.1-beta.65(@opentelemetry/api@1.9.0) + '@workflow/core': 4.2.0-beta.74(@opentelemetry/api@1.9.0) + '@workflow/rollup': 4.0.0-beta.31(@opentelemetry/api@1.9.0) + '@workflow/swc-plugin': 4.1.0-beta.21(@swc/core@1.15.3) + '@workflow/vite': 4.0.0-beta.24(@opentelemetry/api@1.9.0) exsolve: 1.0.8 pathe: 2.0.3 transitivePeerDependencies: @@ -7701,10 +7738,10 @@ snapshots: - aws-crt - supports-color - '@workflow/nuxt@4.0.1-beta.51(@opentelemetry/api@1.9.0)': + '@workflow/nuxt@4.0.1-beta.58(@opentelemetry/api@1.9.0)': dependencies: - '@nuxt/kit': 4.3.1 - '@workflow/nitro': 4.0.1-beta.62(@opentelemetry/api@1.9.0) + '@nuxt/kit': 4.4.2 + '@workflow/nitro': 4.0.1-beta.69(@opentelemetry/api@1.9.0) transitivePeerDependencies: - '@opentelemetry/api' - '@swc/helpers' @@ -7712,11 +7749,11 @@ snapshots: - magicast - supports-color - '@workflow/rollup@4.0.0-beta.24(@opentelemetry/api@1.9.0)': + '@workflow/rollup@4.0.0-beta.31(@opentelemetry/api@1.9.0)': dependencies: '@swc/core': 1.15.3 - '@workflow/builders': 4.0.1-beta.58(@opentelemetry/api@1.9.0) - '@workflow/swc-plugin': 4.1.0-beta.18(@swc/core@1.15.3) + '@workflow/builders': 4.0.1-beta.65(@opentelemetry/api@1.9.0) + '@workflow/swc-plugin': 4.1.0-beta.21(@swc/core@1.15.3) exsolve: 1.0.7 transitivePeerDependencies: - '@opentelemetry/api' @@ -7726,13 +7763,13 @@ snapshots: '@workflow/serde@4.1.0-beta.2': {} - '@workflow/sveltekit@4.0.0-beta.56(@opentelemetry/api@1.9.0)': + '@workflow/sveltekit@4.0.0-beta.63(@opentelemetry/api@1.9.0)': dependencies: '@swc/core': 1.15.3 - '@workflow/builders': 4.0.1-beta.58(@opentelemetry/api@1.9.0) - '@workflow/rollup': 4.0.0-beta.24(@opentelemetry/api@1.9.0) - '@workflow/swc-plugin': 4.1.0-beta.18(@swc/core@1.15.3) - '@workflow/vite': 4.0.0-beta.17(@opentelemetry/api@1.9.0) + '@workflow/builders': 4.0.1-beta.65(@opentelemetry/api@1.9.0) + '@workflow/rollup': 4.0.0-beta.31(@opentelemetry/api@1.9.0) + '@workflow/swc-plugin': 4.1.0-beta.21(@swc/core@1.15.3) + '@workflow/vite': 4.0.0-beta.24(@opentelemetry/api@1.9.0) exsolve: 1.0.8 fs-extra: 11.3.3 pathe: 2.0.3 @@ -7742,7 +7779,7 @@ snapshots: - aws-crt - supports-color - '@workflow/swc-plugin@4.1.0-beta.18(@swc/core@1.15.3)': + '@workflow/swc-plugin@4.1.0-beta.21(@swc/core@1.15.3)': dependencies: '@swc/core': 1.15.3 @@ -7754,24 +7791,20 @@ snapshots: dependencies: ms: 2.1.3 - '@workflow/vite@4.0.0-beta.17(@opentelemetry/api@1.9.0)': + '@workflow/vite@4.0.0-beta.24(@opentelemetry/api@1.9.0)': dependencies: - '@workflow/builders': 4.0.1-beta.58(@opentelemetry/api@1.9.0) + '@workflow/builders': 4.0.1-beta.65(@opentelemetry/api@1.9.0) transitivePeerDependencies: - '@opentelemetry/api' - '@swc/helpers' - aws-crt - supports-color - '@workflow/web@4.1.0-beta.39(react-router@7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)': + '@workflow/web@4.1.0-beta.46': dependencies: - '@react-router/node': 7.13.1(react-router@7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) express: 4.22.1 - isbot: 5.1.35 transitivePeerDependencies: - - react-router - supports-color - - typescript '@workflow/world-local@4.1.0-beta.40(@opentelemetry/api@1.9.0)': dependencies: @@ -7786,6 +7819,19 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.0 + '@workflow/world-local@4.1.0-beta.47(@opentelemetry/api@1.9.0)': + dependencies: + '@vercel/queue': 0.1.4 + '@workflow/errors': 4.1.0-beta.19 + '@workflow/utils': 4.1.0-beta.13 + '@workflow/world': 4.1.0-beta.14(zod@4.3.6) + async-sema: 3.1.1 + ulid: 3.0.1 + undici: 7.22.0 + zod: 4.3.6 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@workflow/world-postgres@4.1.0-beta.42(@opentelemetry/api@1.9.0)(@types/pg@8.18.0)(pg@8.18.0)(typescript@5.9.3)': dependencies: '@vercel/queue': 0.1.1 @@ -7832,12 +7878,12 @@ snapshots: - supports-color - typescript - '@workflow/world-vercel@4.1.0-beta.41(@opentelemetry/api@1.9.0)': + '@workflow/world-vercel@4.1.0-beta.45(@opentelemetry/api@1.9.0)': dependencies: '@vercel/oidc': 3.2.0 - '@vercel/queue': 0.1.1 - '@workflow/errors': 4.1.0-beta.18 - '@workflow/world': 4.1.0-beta.11(zod@4.3.6) + '@vercel/queue': 0.1.4 + '@workflow/errors': 4.1.0-beta.19 + '@workflow/world': 4.1.0-beta.14(zod@4.3.6) cbor-x: 1.6.0 undici: 7.22.0 zod: 4.3.6 @@ -7849,6 +7895,11 @@ snapshots: ulid: 3.0.1 zod: 4.3.6 + '@workflow/world@4.1.0-beta.14(zod@4.3.6)': + dependencies: + ulid: 3.0.1 + zod: 4.3.6 + '@xhmikosr/archive-type@7.1.0': dependencies: file-type: 20.5.0 @@ -7972,12 +8023,14 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 - acorn-import-attributes@1.9.5(acorn@8.15.0): + acorn-import-attributes@1.9.5(acorn@8.16.0): dependencies: - acorn: 8.15.0 + acorn: 8.16.0 acorn@8.15.0: {} + acorn@8.16.0: {} + ai@6.0.69(zod@4.1.12): dependencies: '@ai-sdk/gateway': 3.0.32(zod@4.1.12) @@ -8032,6 +8085,10 @@ snapshots: async-listen@3.0.0: {} + async-retry@1.3.3: + dependencies: + retry: 0.13.1 + async-sema@3.1.1: {} async@3.2.6: {} @@ -8303,8 +8360,6 @@ snapshots: cookie@0.7.2: {} - cookie@1.1.1: {} - cose-base@1.0.3: dependencies: layout-base: 1.0.2 @@ -9152,8 +9207,8 @@ snapshots: import-in-the-middle@1.15.0: dependencies: - acorn: 8.15.0 - acorn-import-attributes: 1.9.5(acorn@8.15.0) + acorn: 8.16.0 + acorn-import-attributes: 1.9.5(acorn@8.16.0) cjs-module-lexer: 1.4.3 module-details-from-path: 1.0.4 @@ -9233,8 +9288,6 @@ snapshots: dependencies: is-inside-container: 1.0.0 - isbot@5.1.35: {} - isexe@2.0.0: {} iterare@1.2.1: {} @@ -9267,6 +9320,8 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonlines@0.1.1: {} + katex@0.16.28: dependencies: commander: 8.3.0 @@ -9882,6 +9937,13 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.1 + mlly@1.8.2: + dependencies: + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.3 + module-details-from-path@1.0.4: {} motion-dom@12.23.23: @@ -10287,14 +10349,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 - react-router@7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): - dependencies: - cookie: 1.1.1 - react: 19.2.3 - set-cookie-parser: 2.7.2 - optionalDependencies: - react-dom: 19.2.3(react@19.2.3) - react-style-singleton@2.2.3(@types/react@19.2.7)(react@19.2.3): dependencies: get-nonce: 1.0.1 @@ -10456,6 +10510,8 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 + retry@0.13.1: {} + robust-predicates@3.0.2: {} roughjs@4.6.6: @@ -10525,8 +10581,6 @@ snapshots: transitivePeerDependencies: - supports-color - set-cookie-parser@2.7.2: {} - setprototypeof@1.2.0: {} sharp@0.34.5: @@ -11001,19 +11055,20 @@ snapshots: wordwrap@1.0.0: {} - workflow@4.2.0-beta.67(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.0)(@swc/cli@0.7.10(@swc/core@1.15.3)(chokidar@4.0.3))(@swc/core@1.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-router@7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3): - dependencies: - '@workflow/astro': 4.0.0-beta.41(@opentelemetry/api@1.9.0) - '@workflow/cli': 4.2.0-beta.67(@opentelemetry/api@1.9.0)(react-router@7.13.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) - '@workflow/core': 4.2.0-beta.67(@opentelemetry/api@1.9.0) - '@workflow/errors': 4.1.0-beta.18 - '@workflow/nest': 0.0.0-beta.16(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.0)(@swc/cli@0.7.10(@swc/core@1.15.3)(chokidar@4.0.3))(@swc/core@1.15.3) - '@workflow/next': 4.0.1-beta.63(@opentelemetry/api@1.9.0)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) - '@workflow/nitro': 4.0.1-beta.62(@opentelemetry/api@1.9.0) - '@workflow/nuxt': 4.0.1-beta.51(@opentelemetry/api@1.9.0) - '@workflow/rollup': 4.0.0-beta.24(@opentelemetry/api@1.9.0) - '@workflow/sveltekit': 4.0.0-beta.56(@opentelemetry/api@1.9.0) + workflow@4.2.0-beta.74(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.0)(@swc/cli@0.7.10(@swc/core@1.15.3)(chokidar@4.0.3))(@swc/core@1.15.3)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3): + dependencies: + '@workflow/astro': 4.0.0-beta.48(@opentelemetry/api@1.9.0) + '@workflow/cli': 4.2.0-beta.74(@opentelemetry/api@1.9.0) + '@workflow/core': 4.2.0-beta.74(@opentelemetry/api@1.9.0) + '@workflow/errors': 4.1.0-beta.19 + '@workflow/nest': 0.0.0-beta.23(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.0)(@swc/cli@0.7.10(@swc/core@1.15.3)(chokidar@4.0.3))(@swc/core@1.15.3) + '@workflow/next': 4.0.1-beta.70(@opentelemetry/api@1.9.0)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) + '@workflow/nitro': 4.0.1-beta.69(@opentelemetry/api@1.9.0) + '@workflow/nuxt': 4.0.1-beta.58(@opentelemetry/api@1.9.0) + '@workflow/rollup': 4.0.0-beta.31(@opentelemetry/api@1.9.0) + '@workflow/sveltekit': 4.0.0-beta.63(@opentelemetry/api@1.9.0) '@workflow/typescript-plugin': 4.0.1-beta.5(typescript@5.9.3) + '@workflow/utils': 4.1.0-beta.13 ms: 2.1.3 optionalDependencies: '@opentelemetry/api': 1.9.0 @@ -11026,7 +11081,6 @@ snapshots: - aws-crt - magicast - next - - react-router - supports-color - typescript @@ -11077,6 +11131,8 @@ snapshots: yocto-queue@1.2.2: {} + zod@3.24.4: {} + zod@4.1.11: {} zod@4.1.12: {} diff --git a/flight-booking-app/vercel-sandbox-1.9.0.tgz b/flight-booking-app/vercel-sandbox-1.9.0.tgz new file mode 100644 index 0000000..9502dba Binary files /dev/null and b/flight-booking-app/vercel-sandbox-1.9.0.tgz differ diff --git a/flight-booking-app/workflows/chat/hooks/webhook.ts b/flight-booking-app/workflows/chat/hooks/webhook.ts new file mode 100644 index 0000000..49c564b --- /dev/null +++ b/flight-booking-app/workflows/chat/hooks/webhook.ts @@ -0,0 +1,9 @@ +import { defineHook } from 'workflow'; +import { z } from 'zod'; + +export const webhookHook = defineHook({ + schema: z.object({ + method: z.string(), + body: z.any(), + }), +}); diff --git a/flight-booking-app/workflows/chat/index.ts b/flight-booking-app/workflows/chat/index.ts index 6022bb4..b48773a 100644 --- a/flight-booking-app/workflows/chat/index.ts +++ b/flight-booking-app/workflows/chat/index.ts @@ -5,7 +5,7 @@ import { type ModelMessage, } from 'ai'; import { DurableAgent } from '@workflow/ai/agent'; -import { FLIGHT_ASSISTANT_PROMPT, flightBookingTools } from './steps/tools'; +import { FLIGHT_ASSISTANT_PROMPT, flightBookingTools, createSandboxTools } from './steps/tools'; import { getWritable, getWorkflowMetadata } from 'workflow'; import { chatMessageHook } from './hooks/chat-message'; import { @@ -14,6 +14,7 @@ import { writeStreamClose, writeTurnEnd, } from './steps/writer'; +import { Sandbox } from '@vercel/sandbox'; /** * Multi-turn chat workflow. @@ -62,10 +63,21 @@ export async function chat(initialMessages: UIMessage[], requestReceivedAt: numb } } + // Lazy sandbox creation — only created when the agent first calls runCode + let sandbox: Sandbox | null = null; + const getOrCreateSandbox = async () => { + if (!sandbox) { + sandbox = await Sandbox.create({ runtime: 'node24', timeout: 5 * 60 * 1000 }); + } + return sandbox; + }; + + const sandboxTools = createSandboxTools(getOrCreateSandbox); + const agent = new DurableAgent({ model: 'bedrock/claude-haiku-4-5-20251001-v1', system: FLIGHT_ASSISTANT_PROMPT, - tools: flightBookingTools, + tools: { ...flightBookingTools, ...sandboxTools }, }); // Create a hook that uses the run ID as the token for resumption diff --git a/flight-booking-app/workflows/chat/steps/tools.ts b/flight-booking-app/workflows/chat/steps/tools.ts index 05ca703..9615912 100644 --- a/flight-booking-app/workflows/chat/steps/tools.ts +++ b/flight-booking-app/workflows/chat/steps/tools.ts @@ -1,7 +1,9 @@ import { FatalError, sleep, getWritable } from 'workflow'; import { z } from 'zod'; import { bookingApprovalHook } from '../hooks/approval'; +import { webhookHook } from '../hooks/webhook'; import type { UIMessageChunk } from 'ai'; +import { Sandbox } from '@vercel/sandbox'; /** * Emit a tool-start event for realtime observability. @@ -43,6 +45,138 @@ async function emitToolEnd(toolName: string) { } } +/** + * Emit a sandbox lifecycle event for realtime observability. + * Must be called from within a "use step" context. + */ +async function emitSandboxEvent(event: string, details?: Record) { + const writable = getWritable(); + const writer = writable.getWriter(); + try { + await writer.write({ + type: 'data-workflow', + data: { + type: 'sandbox-event', + event, + ...details, + timestamp: Date.now(), + }, + } as UIMessageChunk); + } finally { + writer.releaseLock(); + } +} + +/** + * Extract a meaningful error message from any thrown value. + * Handles cross-realm errors where instanceof Error fails. + */ +function extractErrorMessage(error: unknown): string { + if (error == null) return 'Unknown error (null)'; + if (typeof error === 'string') return error; + + // Standard Error instances (same realm) + if (error instanceof Error) { + return error.stack || error.message || error.constructor?.name || 'Error'; + } + + // Cross-realm errors or error-like objects + const asAny = error as any; + if (asAny.message) return String(asAny.message); + if (asAny.stack) return String(asAny.stack); + + // Try JSON.stringify with all own property names + try { + const keys = Object.getOwnPropertyNames(asAny); + if (keys.length > 0) { + const obj: Record = {}; + for (const key of keys) obj[key] = asAny[key]; + return JSON.stringify(obj); + } + } catch {} + + const str = String(error); + return str !== '[object Object]' ? str : 'Unknown error (unserializable)'; +} + +/** + * Execute sandbox operations inside a step where getWritable() and Node.js APIs work. + * Takes sandboxId as a plain string so it serializes cleanly across the workflow/step boundary. + */ +async function runInSandboxStep( + sandboxId: string, + files: { path: string; content: string }[] | undefined, + command: string, +): Promise<{ exitCode: number; stdout: string; stderr: string; sandboxId: string } | { error: true; phase: string; message: string; sandboxId: string }> { + 'use step'; + + await emitToolStart('runCode'); + await emitSandboxEvent('connecting', { sandboxId }); + + let sandbox: InstanceType; + try { + sandbox = await Sandbox.get({ sandboxId }); + } catch (err) { + const msg = extractErrorMessage(err); + await emitSandboxEvent('error', { phase: 'connect', message: msg, sandboxId }); + await emitToolEnd('runCode'); + return { error: true, phase: 'sandbox-connect', message: msg, sandboxId }; + } + // Check if the sandbox is still alive + if (sandbox.status !== 'running') { + const msg = `Sandbox is ${sandbox.status}. The session has expired and can no longer execute commands. Please start a new conversation to get a fresh sandbox.`; + await emitSandboxEvent('error', { phase: 'expired', message: msg, sandboxId, status: sandbox.status }); + await emitToolEnd('runCode'); + return { error: true, phase: 'sandbox-expired', message: msg, sandboxId }; + } + + await emitSandboxEvent('ready', { sandboxId, status: sandbox.status }); + + // Write files if provided + if (files && files.length > 0) { + await emitSandboxEvent('writing-files', { + sandboxId, + fileCount: files.length, + filePaths: files.map((f) => f.path), + }); + try { + await sandbox.writeFiles(files); + } catch (err) { + const msg = extractErrorMessage(err); + await emitSandboxEvent('error', { phase: 'write-files', message: msg, sandboxId }); + await emitToolEnd('runCode'); + return { error: true, phase: 'write-files', message: msg, sandboxId }; + } + await emitSandboxEvent('files-written', { sandboxId, fileCount: files.length }); + } + + // Run the command + await emitSandboxEvent('running-command', { sandboxId, command }); + try { + const result = await sandbox.runCommand('sh', ['-c', command]); + const stdout = await result.stdout(); + const stderr = await result.stderr(); + + await emitSandboxEvent('command-complete', { + sandboxId, + exitCode: result.exitCode, + }); + await emitToolEnd('runCode'); + + return { + exitCode: result.exitCode, + stdout: stdout || '(no output)', + stderr: stderr || '', + sandboxId, + }; + } catch (err) { + const msg = extractErrorMessage(err); + await emitSandboxEvent('error', { phase: 'run-command', message: msg, sandboxId }); + await emitToolEnd('runCode'); + return { error: true, phase: 'run-command', message: msg, sandboxId }; + } +} + export const mockAirports: Record< string, { name: string; city: string; timezone: string } @@ -366,6 +500,23 @@ async function executeSleep({ durationMs }: { durationMs: number }) { return { message: `Slept for ${durationMs}ms` }; } +async function executeWaitForWebhook( + { description }: { description: string }, + { toolCallId }: { toolCallId: string } +) { + // No "use step" — hooks are workflow-level primitives + // Use the toolCallId as the hook token so the UI can construct the webhook URL + const hook = webhookHook.create({ token: toolCallId }); + // Workflow pauses here until the webhook endpoint is called + const { method, body } = await hook; + return { + description, + webhookReceived: true, + method, + body, + }; +} + async function executeBookingApproval( { flightNumber, @@ -452,8 +603,77 @@ export const flightBookingTools = { }), execute: executeBookingApproval, }, + waitForWebhook: { + description: + 'Create a webhook URL and pause until it is called by an external system. ' + + 'Only use this tool when the user explicitly asks for a webhook. ' + + 'The webhook URL will be displayed to the user so they can share it with external services.', + inputSchema: z.object({ + description: z + .string() + .describe('What this webhook is waiting for (e.g., "payment confirmation", "deployment complete")'), + }), + execute: executeWaitForWebhook, + }, }; +/** + * Creates sandbox tools that close over a lazily-created Sandbox instance. + * The sandbox persists across tool calls within the same workflow run. + * + * Architecture: The tool execute function runs in workflow context (no "use step") + * so it can call getOrCreateSandbox() which internally uses steps for creation. + * The actual sandbox operations run inside runInSandboxStep() which has "use step", + * enabling getWritable() for event emission and full Node.js APIs. + */ +export function createSandboxTools(getOrCreateSandbox: () => Promise>) { + return { + runCode: { + description: + 'Execute code or shell commands in an isolated cloud sandbox (Linux VM with Node.js). ' + + 'The sandbox persists between calls — installed packages, files, and environment carry over. ' + + 'Write files and run commands to accomplish any coding task.', + inputSchema: z.object({ + files: z + .array( + z.object({ + path: z.string().describe('File path relative to the working directory'), + content: z.string().describe('File content to write'), + }) + ) + .optional() + .describe('Files to write before running the command'), + command: z + .string() + .describe( + 'Shell command to execute (e.g., "node script.js", "npm install lodash && node index.js")' + ), + }), + execute: async ({ files, command }: { files?: { path: string; content: string }[]; command: string }) => { + // Phase 1: Create or retrieve the sandbox at workflow level. + // Sandbox.create() uses "use step" internally so this works from workflow context. + // The sandbox variable persists in the workflow closure across tool calls. + let sandboxId: string; + try { + const sandbox = await getOrCreateSandbox(); + sandboxId = sandbox.sandboxId; + } catch (err) { + // Can't emit events here (no step context), so return structured error + return { + error: true, + phase: 'sandbox-create', + message: extractErrorMessage(err), + sandboxId: '', + }; + } + + // Phase 2: Run the actual work inside a step where getWritable() works + return runInSandboxStep(sandboxId, files, command); + }, + }, + }; +} + // System prompt export const FLIGHT_ASSISTANT_PROMPT = `You are a helpful flight booking assistant. You can help users: - Search for flights between cities @@ -461,6 +681,9 @@ export const FLIGHT_ASSISTANT_PROMPT = `You are a helpful flight booking assista - Get airport information - Book flights - Check baggage allowances +- Run code in an isolated sandbox environment (use the runCode tool for any coding tasks) Be friendly and professional. When searching for flights, always ask for travel dates if not provided. -When booking flights, confirm all details before proceeding.`; +When booking flights, confirm all details before proceeding. +When asked to write or run code, use the runCode tool. The sandbox persists between calls, so you can install packages first and then use them. +When the user explicitly asks for a webhook, use the waitForWebhook tool. A URL will be generated and shown to the user. The workflow will pause until the webhook is called, then you'll receive the payload.`;