@runtypelabs/persona-proxy 3.33.0 → 3.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/flows/conversational.ts","../src/flows/scheduling.ts","../src/flows/shopping-assistant.ts","../src/flows/components.ts","../src/flows/bakery-assistant.ts","../src/flows/storefront-assistant.ts","../src/flows/webmcp-storefront.ts","../src/flows/webmcp-calendar.ts","../src/flows/webmcp-slides.ts","../src/flows/webmcp-docked.ts","../src/flows/page-context.ts","../src/flows/theme-assistant.ts","../src/utils/stripe.ts"],"sourcesContent":["import { Hono } from \"hono\";\nimport type { Context } from \"hono\";\nimport { handle } from \"hono/vercel\";\n\nexport type RuntypeFlowStep = {\n id: string;\n name: string;\n type: string;\n enabled: boolean;\n config: Record<string, unknown>;\n};\n\nexport type RuntypeFlowConfig = {\n name: string;\n description: string;\n steps: RuntypeFlowStep[];\n};\n\ntype RuntimeEnv = Record<string, string | undefined>;\n\n/**\n * Payload for message feedback (upvote/downvote)\n */\nexport type FeedbackPayload = {\n type: \"upvote\" | \"downvote\";\n messageId: string;\n content?: string;\n timestamp?: string;\n sessionId?: string;\n metadata?: Record<string, unknown>;\n};\n\n/**\n * Handler function for processing feedback\n */\nexport type FeedbackHandler = (feedback: FeedbackPayload) => Promise<void> | void;\n\nexport type ChatProxyOptions = {\n upstreamUrl?: string;\n apiKey?: string;\n path?: string;\n allowedOrigins?: string[];\n /**\n * Reflect any request origin matching this pattern, in addition to the exact\n * `allowedOrigins` list. Intended for Vercel **preview** deployments, whose\n * URLs are per-branch and dynamic (`*-git-<branch>-<team>.vercel.app`) and so\n * can't be enumerated. Defaults to `https://*.vercel.app`\n * ({@link DEFAULT_PREVIEW_ORIGIN_PATTERN}); pass a custom `RegExp`, set the\n * `PREVIEW_ORIGIN_PATTERN` env var, or pass `false` to disable. Independent of\n * the `VERCEL_ENV === \"preview\"` runtime check, which always reflects the\n * caller's origin when the proxy itself is a preview deployment.\n */\n previewOriginPattern?: RegExp | false;\n flowId?: string;\n flowConfig?: RuntypeFlowConfig;\n /**\n * Path for the feedback endpoint (default: \"/api/feedback\")\n */\n feedbackPath?: string;\n /**\n * Custom handler for processing feedback.\n * Use this to store feedback in a database or send to analytics.\n * \n * @example\n * ```ts\n * onFeedback: async (feedback) => {\n * await db.feedback.create({ data: feedback });\n * }\n * ```\n */\n onFeedback?: FeedbackHandler;\n};\n\nconst DEFAULT_ENDPOINT = \"https://api.runtype.com/v1/dispatch\";\nconst DEFAULT_PATH = \"/api/chat/dispatch\";\n\nconst getRuntimeEnv = (): RuntimeEnv | undefined => {\n const maybeProcess = (\n globalThis as typeof globalThis & { process?: { env?: RuntimeEnv } }\n ).process;\n return maybeProcess?.env;\n};\n\n/** True only when `NODE_ENV` is exactly `\"development\"` (unset = production). Safe when `process` is missing (e.g. some Workers runtimes). */\nconst isDevelopmentRuntime = (): boolean =>\n getRuntimeEnv()?.NODE_ENV === \"development\";\n\n/**\n * True when this proxy is itself running as a Vercel **preview** deployment\n * (`VERCEL_ENV === \"preview\"`). Vercel sets `NODE_ENV=production` for both\n * production and preview, so `isDevelopmentRuntime()` can't distinguish them —\n * `VERCEL_ENV` is the only signal. Preview deployments get per-branch, dynamic\n * URLs (`*-git-<branch>-<team>.vercel.app`) that can't be enumerated in a\n * static `allowedOrigins` list, so for CORS we treat a preview runtime like\n * development and reflect the caller's origin. Safe when `process` is missing.\n */\nconst isVercelPreviewRuntime = (): boolean =>\n getRuntimeEnv()?.VERCEL_ENV === \"preview\";\n\n/**\n * Default origin pattern treated as a Vercel preview/app origin: any\n * `https://<sub>.vercel.app`. When a *production* proxy is called by a static\n * preview site (a different, dynamic `*.vercel.app` origin), the origin won't be\n * in `allowedOrigins`; matching this pattern lets the proxy reflect it so\n * per-branch preview sites work without enumerating their URLs. To allow other\n * preview domains, supply your own pattern via the `previewOriginPattern` option\n * or the `PREVIEW_ORIGIN_PATTERN` env regex; disable with\n * `previewOriginPattern: false`.\n *\n * The `$`-anchored apex prevents look-alikes like `https://x.vercel.app.evil.com`\n * from matching.\n */\nconst DEFAULT_PREVIEW_ORIGIN_PATTERN = /^https:\\/\\/[a-z0-9-]+\\.vercel\\.app$/i;\n\n/**\n * Resolve the preview-origin pattern from options/env. Precedence:\n * explicit `options.previewOriginPattern` (a `RegExp`, or `false` to disable) →\n * `PREVIEW_ORIGIN_PATTERN` env (compiled as a `RegExp`; ignored if invalid) →\n * {@link DEFAULT_PREVIEW_ORIGIN_PATTERN}.\n */\nconst resolvePreviewOriginPattern = (\n option: RegExp | false | undefined\n): RegExp | null => {\n if (option === false) return null;\n if (option instanceof RegExp) return option;\n const envPattern = getRuntimeEnv()?.PREVIEW_ORIGIN_PATTERN;\n if (envPattern) {\n try {\n return new RegExp(envPattern);\n } catch {\n // Invalid env regex — fall back to the default rather than throwing at\n // app-construction time.\n }\n }\n return DEFAULT_PREVIEW_ORIGIN_PATTERN;\n};\n\nconst DEFAULT_FLOW: RuntypeFlowConfig = {\n name: \"Streaming Prompt Flow\",\n description: \"Streaming chat generated by the widget\",\n steps: [\n {\n id: \"widget_prompt\",\n name: \"Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: \"you are a helpful assistant, chatting with a user\",\n // tools: {\n // toolIds: [\n // \"builtin:dalle\"\n // ]\n // },\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n\nconst withCors =\n (allowedOrigins: string[] | undefined, previewOriginPattern: RegExp | null) =>\n async (c: Context, next: () => Promise<void>) => {\n const origin = c.req.header(\"origin\");\n const isDevelopment = isDevelopmentRuntime();\n // A request is preview-allowed when either the proxy itself is a Vercel\n // preview deployment (reflect any caller, like dev) or the caller's origin\n // matches the configured preview pattern (e.g. a `*.vercel.app` preview\n // site calling a production proxy). Both reflect the actual origin.\n const isPreviewOrigin = Boolean(\n origin &&\n (isVercelPreviewRuntime() ||\n (previewOriginPattern !== null && previewOriginPattern.test(origin)))\n );\n\n // Determine the CORS origin to allow\n let corsOrigin: string;\n if (!allowedOrigins || allowedOrigins.length === 0) {\n // No restrictions - allow any origin (or use the request origin)\n corsOrigin = origin || \"*\";\n } else if (allowedOrigins.includes(origin || \"\")) {\n // Origin is in the allowed list\n corsOrigin = origin || \"*\";\n } else if (isDevelopment && origin) {\n // In development, allow the actual origin even if not in the list\n // This helps with local development where ports might vary\n corsOrigin = origin;\n } else if (isPreviewOrigin && origin) {\n // Vercel preview deployment (or a configured preview origin): reflect the\n // dynamic per-branch origin that can't be enumerated in allowedOrigins.\n corsOrigin = origin;\n } else {\n // Production: origin not allowed - reject by not setting CORS headers\n // Return error for preflight, or continue without CORS headers\n if (c.req.method === \"OPTIONS\") {\n return c.json({ error: \"CORS policy violation: origin not allowed\" }, 403);\n }\n // For non-preflight requests, continue but browser will block due to missing CORS headers\n await next();\n return;\n }\n\n const headers: Record<string, string> = {\n \"Access-Control-Allow-Origin\": corsOrigin,\n \"Access-Control-Allow-Headers\": \"Content-Type, Authorization\",\n \"Access-Control-Allow-Methods\": \"POST, OPTIONS\",\n Vary: \"Origin\"\n };\n\n if (c.req.method === \"OPTIONS\") {\n return new Response(null, { status: 204, headers });\n }\n\n await next();\n Object.entries(headers).forEach(([key, value]) =>\n c.header(key, value, { append: false })\n );\n };\n\nexport const createChatProxyApp = (options: ChatProxyOptions = {}) => {\n const app = new Hono();\n const path = options.path ?? DEFAULT_PATH;\n const feedbackPath = options.feedbackPath ?? \"/api/feedback\";\n const upstream = options.upstreamUrl ?? DEFAULT_ENDPOINT;\n\n const previewOriginPattern = resolvePreviewOriginPattern(\n options.previewOriginPattern\n );\n app.use(\"*\", withCors(options.allowedOrigins, previewOriginPattern));\n\n // Feedback endpoint for collecting upvote/downvote data\n app.post(feedbackPath, async (c) => {\n let payload: FeedbackPayload;\n try {\n payload = await c.req.json();\n } catch (error) {\n return c.json({ error: \"Invalid JSON body\" }, 400);\n }\n\n // Validate payload\n if (!payload.type || ![\"upvote\", \"downvote\"].includes(payload.type)) {\n return c.json(\n { error: \"Invalid feedback type. Must be 'upvote' or 'downvote'\" },\n 400\n );\n }\n if (!payload.messageId) {\n return c.json({ error: \"Missing messageId\" }, 400);\n }\n\n // Add timestamp if not provided\n payload.timestamp = payload.timestamp ?? new Date().toISOString();\n\n const isDevelopment = isDevelopmentRuntime();\n\n if (isDevelopment) {\n console.log(\"\\n=== Feedback Received ===\");\n console.log(\"Type:\", payload.type);\n console.log(\"Message ID:\", payload.messageId);\n console.log(\"Content Length:\", payload.content?.length ?? 0);\n console.log(\"Timestamp:\", payload.timestamp);\n console.log(\"=== End Feedback ===\\n\");\n }\n\n // Call custom handler if provided\n if (options.onFeedback) {\n try {\n await options.onFeedback(payload);\n } catch (error) {\n console.error(\"[Feedback] Handler error:\", error);\n return c.json({ error: \"Feedback handler failed\" }, 500);\n }\n }\n\n return c.json({\n success: true,\n message: \"Feedback recorded\",\n feedback: {\n type: payload.type,\n messageId: payload.messageId,\n timestamp: payload.timestamp\n }\n });\n });\n\n // Chat dispatch endpoint\n app.post(path, async (c) => {\n const apiKey = options.apiKey ?? getRuntimeEnv()?.RUNTYPE_API_KEY;\n if (!apiKey) {\n return c.json(\n { error: \"Missing API key. Set RUNTYPE_API_KEY.\" },\n 401\n );\n }\n\n let clientPayload: Record<string, unknown>;\n try {\n clientPayload = await c.req.json();\n } catch (error) {\n return c.json(\n { error: \"Invalid JSON body\", details: error },\n 400\n );\n }\n\n const isDevelopment = isDevelopmentRuntime();\n\n // Detect agent mode: if the payload contains an `agent` field, forward it directly\n const isAgentMode = !!clientPayload.agent;\n\n let runtypePayload: Record<string, unknown>;\n\n if (isAgentMode) {\n // Agent dispatch - forward the payload as-is to the upstream API\n runtypePayload = clientPayload;\n } else {\n // Flow dispatch - build the Runtype flow payload\n const messages = (clientPayload.messages ?? []) as Array<{ role: string; content: string; createdAt?: string }>;\n const sortedMessages = [...messages].sort((a, b) => {\n const timeA = a.createdAt ? new Date(a.createdAt).getTime() : 0;\n const timeB = b.createdAt ? new Date(b.createdAt).getTime() : 0;\n return timeA - timeB;\n });\n const formattedMessages = sortedMessages.map((message) => ({\n role: message.role,\n content: message.content\n }));\n\n const flowId = (clientPayload.flowId as string | undefined) ?? options.flowId;\n const flowConfig = options.flowConfig ?? DEFAULT_FLOW;\n\n runtypePayload = {\n record: {\n name: \"Streaming Chat Widget\",\n type: \"standalone\",\n metadata: (clientPayload.metadata as Record<string, unknown>) || {}\n },\n messages: formattedMessages,\n options: {\n streamResponse: true,\n recordMode: \"virtual\",\n flowMode: flowId ? \"existing\" : \"virtual\",\n autoAppendMetadata: false\n }\n };\n\n const clientInputs = clientPayload.inputs;\n if (clientInputs && typeof clientInputs === \"object\" && !Array.isArray(clientInputs)) {\n runtypePayload.inputs = clientInputs;\n }\n\n if (flowId) {\n runtypePayload.flow = { id: flowId };\n } else {\n runtypePayload.flow = flowConfig;\n }\n\n // WebMCP: forward page-discovered tools so the upstream flow's agent step\n // can call them. The widget snapshots `document.modelContext` per turn and\n // ships them as `clientTools[]`; the flow-dispatch payload is rebuilt from\n // scratch above, so without this they'd be silently dropped and the agent\n // would never see the page tools. (Agent mode forwards the payload as-is,\n // so it already carries `clientTools`.) The matching results come back via\n // the `${path}/resume` endpoint below.\n if (\n Array.isArray(clientPayload.clientTools) &&\n clientPayload.clientTools.length > 0\n ) {\n runtypePayload.clientTools = clientPayload.clientTools;\n }\n }\n\n // Development only: do not log key material or full bodies in production.\n if (isDevelopment) {\n console.log(`\\n=== Runtype Proxy Request (${isAgentMode ? \"agent\" : \"flow\"}) ===`);\n console.log(\"URL:\", upstream);\n console.log(\"API Key Used:\", apiKey ? \"Yes\" : \"No\");\n console.log(\"API Key (first 12 chars):\", apiKey ? apiKey.substring(0, 12) : \"N/A\");\n console.log(\"Request Payload:\", JSON.stringify(runtypePayload, null, 2));\n }\n\n const response = await fetch(upstream, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(runtypePayload)\n });\n\n if (isDevelopment) {\n console.log(\"Response Status:\", response.status);\n console.log(\"Response Status Text:\", response.statusText);\n\n // If there's an error, try to read and log the response body\n if (!response.ok) {\n const clonedResponse = response.clone();\n try {\n const errorBody = await clonedResponse.text();\n console.log(\"Error Response Body:\", errorBody);\n } catch (e) {\n console.log(\"Could not read error response body:\", e);\n }\n }\n console.log(\"=== End Runtype Proxy Request ===\\n\");\n }\n\n return new Response(response.body, {\n status: response.status,\n headers: {\n \"Content-Type\":\n response.headers.get(\"content-type\") ?? \"application/json\",\n \"Cache-Control\": \"no-store\"\n }\n });\n });\n\n // Resume endpoint — forwards client-executed (LOCAL) tool results back to\n // the Runtype upstream so a paused flow execution can continue. Mounted as\n // a child of the dispatch path so the widget can derive its URL by\n // appending \"/resume\" to whatever `apiUrl` it was configured with.\n app.post(`${path}/resume`, async (c) => {\n const apiKey = options.apiKey ?? getRuntimeEnv()?.RUNTYPE_API_KEY;\n if (!apiKey) {\n return c.json(\n { error: \"Missing API key. Set RUNTYPE_API_KEY.\" },\n 401\n );\n }\n\n let body: Record<string, unknown>;\n try {\n body = await c.req.json();\n } catch (error) {\n return c.json(\n { error: \"Invalid JSON body\", details: error },\n 400\n );\n }\n\n const isDevelopment = isDevelopmentRuntime();\n const upstreamResumeUrl = `${upstream.replace(/\\/+$/, '')}/resume`;\n\n if (isDevelopment) {\n console.log(\"\\n=== Runtype Proxy Resume ===\");\n console.log(\"URL:\", upstreamResumeUrl);\n console.log(\n \"executionId:\",\n typeof body.executionId === \"string\" ? body.executionId : \"(missing)\"\n );\n console.log(\n \"toolOutputs keys:\",\n body.toolOutputs && typeof body.toolOutputs === \"object\"\n ? Object.keys(body.toolOutputs)\n : \"(none)\"\n );\n console.log(\"=== End Runtype Proxy Resume ===\\n\");\n }\n\n const response = await fetch(upstreamResumeUrl, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(body)\n });\n\n return new Response(response.body, {\n status: response.status,\n headers: {\n \"Content-Type\":\n response.headers.get(\"content-type\") ?? \"application/json\",\n \"Cache-Control\": \"no-store\"\n }\n });\n });\n\n return app;\n};\n\nexport const createVercelHandler = (options?: ChatProxyOptions) =>\n handle(createChatProxyApp(options));\n\n// Export pre-configured flows\nexport * from \"./flows/index.js\";\n\n// Export utility functions\nexport * from \"./utils/index.js\";\n\nexport default createChatProxyApp;\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Basic conversational assistant flow\n * This is the default flow for simple chat interactions\n */\nexport const CONVERSATIONAL_FLOW: RuntypeFlowConfig = {\n name: \"Streaming Prompt Flow\",\n description: \"Streaming chat generated by the widget\",\n steps: [\n {\n id: \"widget_prompt\",\n name: \"Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: \"you are a helpful assistant, chatting with a user\",\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Dynamic Form flow configuration\n * This flow returns forms as component directives for the widget to render\n */\nexport const FORM_DIRECTIVE_FLOW: RuntypeFlowConfig = {\n name: \"Dynamic Form Flow\",\n description: \"Returns dynamic forms as component directives\",\n steps: [\n {\n id: \"form_prompt\",\n name: \"Form Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful assistant that can have conversations and collect user information via forms.\n\nRESPONSE FORMAT:\nAlways respond with valid JSON. Choose the appropriate format:\n\n1. For CONVERSATIONAL responses or text answers:\n {\"text\": \"Your response here\"}\n\n2. When the user wants to SCHEDULE, BOOK, SIGN UP, or provide DETAILS (show a form):\n {\"component\": \"DynamicForm\", \"props\": {\"title\": \"Form Title\", \"description\": \"Optional description\", \"fields\": [...], \"submit_text\": \"Submit\"}}\n\n3. For BOTH explanation AND form:\n {\"text\": \"Your explanation\", \"component\": \"DynamicForm\", \"props\": {...}}\n\nFORM FIELD FORMAT:\nEach field in the \"fields\" array should have:\n- label (required): Display name for the field\n- name (optional): Field identifier (defaults to lowercase label with underscores)\n- type (optional): \"text\", \"email\", \"tel\", \"date\", \"time\", \"textarea\", \"number\" (defaults to \"text\")\n- placeholder (optional): Placeholder text\n- required (optional): true/false\n- width (optional): \"full\" or \"half\" — pair short related fields side-by-side with \"half\" (e.g. Phone + Company, City + Zip, First + Last name); use \"full\" or omit for everything else (especially textareas, emails, and standalone fields). Two consecutive \"half\" fields render in one row.\n\nEXAMPLES:\n\nUser: \"Schedule a demo for me\"\nResponse: {\"text\": \"I'd be happy to help you schedule a demo! Please fill out the form below:\", \"component\": \"DynamicForm\", \"props\": {\"title\": \"Schedule a Demo\", \"description\": \"Share your details and we'll follow up with a confirmation.\", \"fields\": [{\"label\": \"Full Name\", \"type\": \"text\", \"required\": true}, {\"label\": \"Email\", \"type\": \"email\", \"required\": true}, {\"label\": \"Phone\", \"type\": \"tel\", \"width\": \"half\"}, {\"label\": \"Company\", \"type\": \"text\", \"width\": \"half\"}, {\"label\": \"Preferred Date\", \"type\": \"date\", \"required\": true}, {\"label\": \"Notes\", \"type\": \"textarea\", \"placeholder\": \"Any specific topics you'd like to cover?\"}], \"submit_text\": \"Request Demo\"}}\n\nUser: \"What is AI?\"\nResponse: {\"text\": \"AI (Artificial Intelligence) refers to computer systems designed to perform tasks that typically require human intelligence, such as learning, reasoning, problem-solving, and understanding language.\"}\n\nUser: \"Collect my contact details\"\nResponse: {\"component\": \"DynamicForm\", \"props\": {\"title\": \"Contact Details\", \"fields\": [{\"label\": \"Name\", \"type\": \"text\", \"required\": true}, {\"label\": \"Email\", \"type\": \"email\", \"required\": true}, {\"label\": \"Phone\", \"type\": \"tel\"}], \"submit_text\": \"Save Details\"}}\n\nIMPORTANT:\n- Use {\"text\": \"...\"} for questions, explanations, and general conversation\n- Show a DynamicForm when user wants to provide information, schedule, book, or sign up\n- Create contextually appropriate form fields based on what the user is trying to do\n- Keep forms focused with only the relevant fields needed`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Shopping assistant flow configuration\n * This flow returns JSON actions for page interaction including:\n * - Simple messages\n * - Navigation with messages\n * - Element clicks with messages\n * - Stripe checkout\n */\nexport const SHOPPING_ASSISTANT_FLOW: RuntypeFlowConfig = {\n name: \"Shopping Assistant Flow\",\n description: \"Returns JSON actions for page interaction\",\n steps: [\n {\n id: \"action_prompt\",\n name: \"Action Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful shopping assistant that can interact with web pages.\nYou will receive information about the current page's elements (class names and text content)\nand user messages. You must respond with JSON in one of these formats:\n\n1. Simple message:\n{\n \"action\": \"message\",\n \"text\": \"Your response text here\"\n}\n\n2. Navigate then show message (for navigation to another page):\n{\n \"action\": \"nav_then_click\",\n \"page\": \"http://site.com/page-url\",\n \"on_load_text\": \"Message to show after navigation\"\n}\n\n3. Show message and click an element:\n{\n \"action\": \"message_and_click\",\n \"element\": \".className-of-element\",\n \"text\": \"Your message text\"\n}\n\n4. Create Stripe checkout:\n{\n \"action\": \"checkout\",\n \"text\": \"Your message text\",\n \"items\": [\n {\"name\": \"Product Name\", \"price\": 2999, \"quantity\": 1}\n ]\n}\n\nGuidelines:\n- Use \"message\" for simple conversational responses\n- Use \"nav_then_click\" when you need to navigate to a different page (like a product detail page)\n- Use \"message_and_click\" when you want to click a button or element on the current page\n- Use \"checkout\" when the user wants to proceed to checkout/payment. Include items array with name (string), price (number in cents), and quantity (number)\n- When selecting elements, use the class names provided in the page context\n- Always respond with valid JSON only, no additional text\n- For product searches, format results as markdown links: [Product Name](url)\n- Be helpful and conversational in your messages\n- Product prices: Black Shirt - Medium: $29.99 (2999 cents), Blue Shirt - Large: $34.99 (3499 cents), Red T-Shirt - Small: $19.99 (1999 cents), Jeans - Medium: $49.99 (4999 cents)\n\nExample conversation flow:\n- User: \"I am looking for a black shirt in medium\"\n- You: {\"action\": \"message\", \"text\": \"Here are the products I found:\\\\n1. [Black Shirt - Medium](/products.html?product=black-shirt-medium) - $29.99\\\\n2. [Blue Shirt - Large](/products.html?product=blue-shirt-large) - $34.99\\\\n3. [Red T-Shirt - Small](/products.html?product=red-tshirt-small) - $19.99\\\\n4. [Jeans - Medium](/products.html?product=jeans-medium) - $49.99\\\\n\\\\nWould you like me to navigate to the first result and add it to your cart?\"}\n\n- User: \"No, I would like to add another shirt to the cart\"\n- You: {\"action\": \"message_and_click\", \"element\": \".AddToCartButton-blue-shirt-large\", \"text\": \"I've added the Blue Shirt - Large to your cart. Ready to checkout?\"}\n\n- User: \"yes\"\n- You: {\"action\": \"checkout\", \"text\": \"Perfect! I'll set up the checkout for you.\", \"items\": [{\"name\": \"Black Shirt - Medium\", \"price\": 2999, \"quantity\": 1}]}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n\n/**\n * Metadata-based shopping assistant flow configuration\n * This flow uses DOM context from record metadata instead of user message.\n * The metadata should include dom_elements, dom_body, page_url, and page_title.\n */\nexport const SHOPPING_ASSISTANT_METADATA_FLOW: RuntypeFlowConfig = {\n name: \"Metadata-Based Shopping Assistant\",\n description: \"Uses DOM context from record metadata for page interaction\",\n steps: [\n {\n id: \"metadata_action_prompt\",\n name: \"Metadata Action Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful shopping assistant that can interact with web pages.\n\nIMPORTANT: You have access to the current page's DOM elements through the record metadata, which includes:\n- dom_elements: Array of page elements with className, innerText, and tagName\n- dom_body: Complete HTML body of the page (if provided)\n- page_url: Current page URL\n- page_title: Page title\n\nThe dom_elements array provides information about clickable elements and their text content.\nUse this metadata to understand what's available on the page and help users interact with it.\n\nYou must respond with JSON in one of these formats:\n\n1. Simple message:\n{\n \"action\": \"message\",\n \"text\": \"Your response text here\"\n}\n\n2. Navigate then show message (for navigation to another page):\n{\n \"action\": \"nav_then_click\",\n \"page\": \"http://site.com/page-url\",\n \"on_load_text\": \"Message to show after navigation\"\n}\n\n3. Show message and click an element:\n{\n \"action\": \"message_and_click\",\n \"element\": \".className-of-element\",\n \"text\": \"Your message text\"\n}\n\n4. Create Stripe checkout:\n{\n \"action\": \"checkout\",\n \"text\": \"Your message text\",\n \"items\": [\n {\"name\": \"Product Name\", \"price\": 2999, \"quantity\": 1}\n ]\n}\n\nGuidelines:\n- Use \"message\" for simple conversational responses\n- Use \"nav_then_click\" when you need to navigate to a different page (like a product detail page)\n- Use \"message_and_click\" when you want to click a button or element on the current page\n- Use \"checkout\" when the user wants to proceed to checkout/payment. Include items array with name (string), price (number in cents), and quantity (number)\n- When selecting elements, use the class names from the dom_elements in the metadata\n- Always respond with valid JSON only, no additional text\n- For product searches, format results as markdown links: [Product Name](url)\n- Be helpful and conversational in your messages\n- Product prices: Black Shirt - Medium: $29.99 (2999 cents), Blue Shirt - Large: $34.99 (3499 cents), Red T-Shirt - Small: $19.99 (1999 cents), Jeans - Medium: $49.99 (4999 cents)\n\nExample conversation flow:\n- User: \"I am looking for a black shirt in medium\"\n- You: {\"action\": \"message\", \"text\": \"Here are the products I found:\\\\n1. [Black Shirt - Medium](/products.html?product=black-shirt-medium) - $29.99\\\\n2. [Blue Shirt - Large](/products.html?product=blue-shirt-large) - $34.99\\\\n3. [Red T-Shirt - Small](/products.html?product=red-tshirt-small) - $19.99\\\\n4. [Jeans - Medium](/products.html?product=jeans-medium) - $49.99\\\\n\\\\nWould you like me to navigate to the first result and add it to your cart?\"}\n\n- User: \"No, I would like to add another shirt to the cart\"\n- You: {\"action\": \"message_and_click\", \"element\": \".AddToCartButton-blue-shirt-large\", \"text\": \"I've added the Blue Shirt - Large to your cart. Ready to checkout?\"}\n\n- User: \"yes\"\n- You: {\"action\": \"checkout\", \"text\": \"Perfect! I'll set up the checkout for you.\", \"items\": [{\"name\": \"Black Shirt - Medium\", \"price\": 2999, \"quantity\": 1}]}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Component-aware flow for custom component rendering\n * This flow instructs the AI to respond with component directives in JSON format\n */\nexport const COMPONENT_FLOW: RuntypeFlowConfig = {\n name: \"Component Flow\",\n description: \"Flow configured for custom component rendering\",\n steps: [\n {\n id: \"component_prompt\",\n name: \"Component Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful assistant that can both have conversations and render custom UI components.\n\nRESPONSE FORMAT:\nAlways respond with valid JSON. Choose the appropriate format based on the user's request:\n\n1. For CONVERSATIONAL questions or text responses:\n {\"text\": \"Your response here\"}\n\n2. For VISUAL DISPLAYS or when the user asks to SHOW/DISPLAY something:\n {\"component\": \"ComponentName\", \"props\": {...}}\n\n3. For BOTH explanation AND visual:\n {\"text\": \"Your explanation here\", \"component\": \"ComponentName\", \"props\": {...}}\n\nAvailable components for visual displays:\n- ProductCard: Display product information. Props: title (string), price (number), description (string, optional), image (string, optional)\n- SimpleChart: Display a bar chart. Props: title (string), data (array of numbers), labels (array of strings, optional)\n- StatusBadge: Display a status badge. Props: status (string: \"success\", \"error\", \"warning\", \"info\", \"pending\"), message (string)\n- InfoCard: Display an information card. Props: title (string), content (string), icon (string, optional)\n\nExamples:\n- User asks \"What is the capital of France?\": {\"text\": \"The capital of France is Paris.\"}\n- User asks \"What does that chart show?\": {\"text\": \"The chart shows sales data increasing from 100 to 200 over three months.\"}\n- User asks \"Show me a product card\": {\"component\": \"ProductCard\", \"props\": {\"title\": \"Laptop\", \"price\": 999, \"description\": \"A great laptop\"}}\n- User asks \"Display a chart\": {\"component\": \"SimpleChart\", \"props\": {\"title\": \"Sales\", \"data\": [100, 150, 200], \"labels\": [\"Jan\", \"Feb\", \"Mar\"]}}\n- User asks \"Show me a chart and explain it\": {\"text\": \"Here's the sales data for Q1:\", \"component\": \"SimpleChart\", \"props\": {\"title\": \"Q1 Sales\", \"data\": [100, 150, 200], \"labels\": [\"Jan\", \"Feb\", \"Mar\"]}}\n\nIMPORTANT:\n- Use {\"text\": \"...\"} for questions, explanations, discussions, and general chat\n- Use {\"component\": \"...\", \"props\": {...}} ONLY when the user explicitly wants to SEE/VIEW/DISPLAY visual content\n- You can combine both: {\"text\": \"...\", \"component\": \"...\", \"props\": {...}} when you want to explain something AND show a visual\n- Never force a component when the user just wants information`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Bakery assistant flow configuration for \"Flour & Stone\" bakery demo\n * This flow returns JSON actions for page interaction including:\n * - Simple messages with bakery brand voice\n * - Navigation to bakery pages\n * - Add to cart interactions\n * - Stripe checkout\n *\n * Designed to guide users toward the gift card when asking for gift recommendations.\n */\nexport const BAKERY_ASSISTANT_FLOW: RuntypeFlowConfig = {\n name: \"Bakery Assistant Flow\",\n description: \"Flour & Stone bakery shopping assistant with gift recommendations\",\n steps: [\n {\n id: \"bakery_action_prompt\",\n name: \"Bakery Action Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful shopping assistant for Flour & Stone, a premium artisan bakery known for traditional bread-making and exceptional pastries.\n\nBrand voice: Warm, knowledgeable, passionate about craft baking. Use phrases like \"fresh from the oven\", \"handcrafted with care\", \"artisan tradition\". Do not explain selectors, JSON, or templating to the user.\n\n## Live context (request inputs — substituted each turn)\n\nThe widget sends **only** these keys as dispatch **inputs** (nothing extra on the record for this demo).\n\n**Orientation**\n- Path: {{current_page}} (compare before nav_then_click; e.g. /bakery-goods.html)\n- Full URL: {{page_url}}\n- Title: {{page_title}}\n\n**Page DOM**\n- page_elements: JSON array of enriched nodes (selector, tagName, text, role, interactivity, attributes including data-*). Prefer **selector** for message_and_click when you click a specific control.\n- page_context: Same slice formatted for the LLM (structured card summaries when matched, then groups by interactivity).\n\n{{page_elements}}\n\n{{page_context}}\n\n**Cart (for checkout — mirror cart.items when user pays)**\n{{cart}}\n\n**Recent order (if any)**\n{{recent_order}}\n\nIf {{current_page}} already equals the page you would navigate to, use {\"action\":\"message\",...} instead of nav_then_click.\n\n## Discovering products\n\nUse {{page_context}} for a quick scan and {{page_elements}} for exact selectors and attributes. Product rows often include data-product in **attributes**; prices appear in **text**; add-to-cart controls are usually **clickable** with stable **selector** values.\n\n## Output: one JSON object only\n\nNo markdown fences, no commentary before/after. Valid JSON only.\n\n### 1. message\n{\"action\": \"message\", \"text\": \"...\"}\nUse for chat, clarifying questions, \"we're already on that page\", or when you need the user to choose (e.g. $25 vs $50 gift card).\n\n### 2. nav_then_click\n{\"action\": \"nav_then_click\", \"page\": \"/bakery-goods.html\", \"on_load_text\": \"...\"}\nUse root-relative paths starting with /. Only when current_page is different from the target. This **only** changes pages — it does **not** open Stripe or payment.\n\n### 3. add_to_cart\n{\"action\": \"add_to_cart\", \"text\": \"...\", \"item\": {\"id\": \"product-id\", \"name\": \"Product Name\", \"price\": 1200}}\nUse when adding from context without scrolling (optional; on goods page prefer scroll_then_add).\n\n### 4. scroll_then_add (preferred on /bakery-goods.html)\n{\"action\": \"scroll_then_add\", \"text\": \"...\", \"item\": {\"id\": \"...\", \"name\": \"...\", \"price\": 1200}}\nScrolls the product into view then adds one unit (cart merges duplicate ids into quantity).\n\n### 5. checkout → Stripe (this demo)\n{\"action\": \"checkout\", \"text\": \"Brief message\", \"items\": [{\"name\": \"...\", \"price\": 1200, \"quantity\": 2}, ...]}\n**Only** this action starts hosted checkout (Stripe). **Never** use nav_then_click to a \"/checkout\" URL for payment here.\nRequirements: cart in context must have items; **items array must list every cart line** with the same name, cent prices, and quantities as cart.items. If cart is null or empty, use message — do not checkout.\n\n### 6. message_and_click (rare)\nIf page_elements show a specific button selector and scroll_then_add is wrong, you may use message_and_click with a CSS selector — prefer scroll_then_add on bakery-goods.html.\n\n## Rules\n\n- Prices in JSON are always **integer cents** (1200 = $12.00).\n- After adding to cart, invite checkout or more shopping.\n- On checkout confirmation (\"yes\", \"checkout\", \"pay\", \"proceed\", etc.), build **items** from **cart.items** (all rows, correct quantity). Do not drop lines or invent prices.\n\n## Product catalog (ids and cent prices)\n\n- Sourdough Loaf: sourdough-loaf, 1200\n- Croissant Box (6): croissant-box, 2400\n- Cinnamon Rolls (4): cinnamon-rolls, 1800\n- Baguette Trio: baguette-trio, 900\n- Almond Tart: almond-tart, 800\n- Fruit Danish: fruit-danish, 600\n- $50 Gift Card: gift-card-50, 5000\n- $25 Gift Card: gift-card-25, 2500\n\n## Examples\n\nGift seeker on /bakery-locations.html:\n{\"action\": \"nav_then_click\", \"page\": \"/bakery-goods.html\", \"on_load_text\": \"Here are our goods! You'll find our gift cards below — $50 is our most popular. Want me to add one?\"}\n\nOn /bakery-goods.html, user wants $50 gift card:\n{\"action\": \"scroll_then_add\", \"text\": \"Added the $50 gift card. Ready to check out?\", \"item\": {\"id\": \"gift-card-50\", \"name\": \"$50 Gift Card\", \"price\": 5000}}\n\nUser on /bakery.html agrees to see products:\n{\"action\": \"nav_then_click\", \"page\": \"/bakery-goods.html\", \"on_load_text\": \"Here are our handcrafted goods — what sounds good today?\"}\n\nOn /bakery-goods.html, add sourdough:\n{\"action\": \"scroll_then_add\", \"text\": \"Sourdough is in your cart. Anything else, or shall we check out?\", \"item\": {\"id\": \"sourdough-loaf\", \"name\": \"Sourdough Loaf\", \"price\": 1200}}\n\nCart has one $50 gift card; user says yes to checkout:\n{\"action\": \"checkout\", \"text\": \"Opening secure checkout...\", \"items\": [{\"name\": \"$50 Gift Card\", \"price\": 5000, \"quantity\": 1}]}\n\nCart has sourdough (qty 1) and croissant box (qty 1); user says \"pay\":\n{\"action\": \"checkout\", \"text\": \"Taking you to checkout...\", \"items\": [{\"name\": \"Sourdough Loaf\", \"price\": 1200, \"quantity\": 1}, {\"name\": \"Croissant Box (6)\", \"price\": 2400, \"quantity\": 1}]}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Storefront assistant flow for the \"Everspun\" persistent-composer demo.\n *\n * Designed to feel like the agent is *building the storefront* in front of\n * the user: when they ask for product suggestions, the agent emits a\n * `ProductGrid` component directive carrying a small batch of structured\n * product cards (id, title, price, image, description). The persona widget\n * renders these as inline cards inside the chat panel via a registered\n * `componentRegistry` entry on the host. Plain conversational replies (fit,\n * fabric, care, styling Q&A) use a simple `{text}` JSON object and stay as\n * regular chat bubbles.\n */\nexport const STOREFRONT_ASSISTANT_FLOW: RuntypeFlowConfig = {\n name: \"Storefront Assistant Flow\",\n description:\n \"Everspun storefront assistant — surfaces product cards via component directives\",\n steps: [\n {\n id: \"storefront_action_prompt\",\n name: \"Storefront Action Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are the concierge for **Everspun**, a quiet-luxury wardrobe brand: cashmere, organic cotton, linen, and considered accessories. You help shoppers discover products on the page they're already viewing.\n\nBrand voice: calm, considered, knowledgeable. Short sentences. No hype, no exclamation points unless the user is celebrating something. Do not explain JSON, components, or templating to the user.\n\n## Live context (substituted each turn)\n\nThe current product the shopper is viewing:\n{{current_product}}\n\nThe shopper's current bag:\n{{cart}}\n\n## Output: one JSON object only\n\nNo markdown fences, no commentary before/after. Valid JSON only. Three response shapes are valid:\n\n### 1. Plain message\n{\"text\": \"...\"}\n\nUse for fit / fabric / care / styling Q&A about the current product, for clarifying questions, and for anything that doesn't surface new products. Renders as a normal chat bubble.\n\n### 2. Product grid (component directive)\n{\n \"text\": \"Brief intro line shown above the cards.\",\n \"component\": \"ProductGrid\",\n \"props\": {\n \"products\": [\n {\"id\": \"...\", \"title\": \"...\", \"price\": 24800, \"image\": \"https://...\", \"description\": \"...\"}\n ]\n }\n}\n\nUse when the shopper asks to see options, asks \"what would go with this\", asks for a category, asks for a price range, or asks for a gift suggestion. Pick **2–6** items from the catalog below — never more than 6, never fewer than 2. Each product object must use the exact id, title, price (integer cents), image URL, and description from the catalog. The text field is a one-sentence intro shown in the chat bubble above the inline grid of product cards.\n\n### 3. Add to cart (action)\n{\"action\": \"add_to_cart\", \"text\": \"Confirmation line.\", \"item\": {\"id\": \"...\", \"title\": \"...\", \"price\": 24800}}\n\nUse only when the shopper explicitly asks you to add a specific product to their bag (\"add the linen pant\", \"I'll take the beanie\"). Use the exact id/title/price from the catalog. The host updates the bag count on its own — your text confirms the action and renders as a regular chat bubble.\n\n## Rules\n\n- Prices in JSON are always **integer cents** (24800 = $248.00).\n- When the shopper asks \"what would go with this?\", ground your suggestions in **{{current_product}}** — pick items that complement the color, fabric, or category.\n- For \"under $X\" queries, only return products from the catalog priced under that amount.\n- For gift queries, prefer the gift card SKUs or compact accessories.\n- After a ProductGrid response, do **not** also describe each product in the text — the cards speak for themselves. Keep text short (\"A few cashmere options:\", \"Pieces under $200:\").\n- Never invent products. The catalog below is the entire universe.\n\n## Product catalog\n\n| id | title | price (cents) | image | description |\n|---|---|---|---|---|\n| cashmere-crewneck | Mongolian Cashmere Crewneck | 24800 | https://images.unsplash.com/photo-1583743814966-8936f5b7be1a?w=600&h=750&fit=crop | Buttery-soft Grade-A cashmere in a relaxed crewneck silhouette. |\n| ribbed-turtleneck | Ribbed Cashmere Turtleneck | 32800 | https://images.unsplash.com/photo-1576566588028-4147f3842f27?w=600&h=750&fit=crop | A heavier-gauge rib knit, cut close through the body. |\n| alpaca-cardigan | Alpaca-Blend Cardigan | 32800 | https://images.unsplash.com/photo-1622445275576-721325763afe?w=600&h=750&fit=crop | Loose-knit alpaca and merino, with horn buttons. |\n| organic-cotton-tee | Organic Cotton Tee | 5800 | https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?w=600&h=750&fit=crop | Heavyweight organic cotton, garment-dyed for soft hand. |\n| oxford-button-down | Oxford Button-Down | 12800 | https://images.unsplash.com/photo-1556905055-8f358a7a47b2?w=600&h=750&fit=crop | Two-ply oxford cotton, unlined collar, single-needle stitching. |\n| linen-trouser | Wide-Leg Linen Trouser | 18800 | https://images.unsplash.com/photo-1473966968600-fa801b869a1a?w=600&h=750&fit=crop | Heavyweight Belgian linen with a fluid drape. |\n| washed-chino | Washed Cotton Chino | 14800 | https://images.unsplash.com/photo-1542272604-787c3835535d?w=600&h=750&fit=crop | Garment-washed twill in a tapered fit. |\n| recycled-beanie | Recycled Cashmere Beanie | 8800 | https://images.unsplash.com/photo-1576871337632-b9aef4c17ab9?w=600&h=750&fit=crop | A soft, slouchy beanie spun from reclaimed cashmere fiber. |\n| leather-card-holder | Vegetable-Tan Card Holder | 9800 | https://images.unsplash.com/photo-1623998022290-a74f8cc36563?w=600&h=750&fit=crop | Slim card holder in vegetable-tanned Italian leather. |\n| gift-card-50 | $50 Gift Card | 5000 | https://images.unsplash.com/photo-1601925260368-ae2f83cf8b7f?w=600&h=750&fit=crop | Delivered by email, never expires. |\n| gift-card-100 | $100 Gift Card | 10000 | https://images.unsplash.com/photo-1601925260368-ae2f83cf8b7f?w=600&h=750&fit=crop | Delivered by email, never expires. |\n| gift-card-200 | $200 Gift Card | 20000 | https://images.unsplash.com/photo-1601925260368-ae2f83cf8b7f?w=600&h=750&fit=crop | Delivered by email, never expires. |\n\n## Examples\n\nUser asks \"show me cashmere essentials\":\n{\"text\": \"A few cashmere essentials:\", \"component\": \"ProductGrid\", \"props\": {\"products\": [\n {\"id\": \"cashmere-crewneck\", \"title\": \"Mongolian Cashmere Crewneck\", \"price\": 24800, \"image\": \"https://images.unsplash.com/photo-1583743814966-8936f5b7be1a?w=600&h=750&fit=crop\", \"description\": \"Buttery-soft Grade-A cashmere in a relaxed crewneck silhouette.\"},\n {\"id\": \"ribbed-turtleneck\", \"title\": \"Ribbed Cashmere Turtleneck\", \"price\": 32800, \"image\": \"https://images.unsplash.com/photo-1576566588028-4147f3842f27?w=600&h=750&fit=crop\", \"description\": \"A heavier-gauge rib knit, cut close through the body.\"},\n {\"id\": \"recycled-beanie\", \"title\": \"Recycled Cashmere Beanie\", \"price\": 8800, \"image\": \"https://images.unsplash.com/photo-1576871337632-b9aef4c17ab9?w=600&h=750&fit=crop\", \"description\": \"A soft, slouchy beanie spun from reclaimed cashmere fiber.\"}\n]}}\n\nUser asks \"what pants would go with this?\" (current_product = camel cashmere sweater):\n{\"text\": \"These pair well with the camel:\", \"component\": \"ProductGrid\", \"props\": {\"products\": [\n {\"id\": \"linen-trouser\", \"title\": \"Wide-Leg Linen Trouser\", \"price\": 18800, \"image\": \"https://images.unsplash.com/photo-1473966968600-fa801b869a1a?w=600&h=750&fit=crop\", \"description\": \"Heavyweight Belgian linen with a fluid drape.\"},\n {\"id\": \"washed-chino\", \"title\": \"Washed Cotton Chino\", \"price\": 14800, \"image\": \"https://images.unsplash.com/photo-1542272604-787c3835535d?w=600&h=750&fit=crop\", \"description\": \"Garment-washed twill in a tapered fit.\"}\n]}}\n\nUser asks \"anything under $200?\":\n{\"text\": \"A few under $200:\", \"component\": \"ProductGrid\", \"props\": {\"products\": [\n {\"id\": \"linen-trouser\", \"title\": \"Wide-Leg Linen Trouser\", \"price\": 18800, \"image\": \"https://images.unsplash.com/photo-1473966968600-fa801b869a1a?w=600&h=750&fit=crop\", \"description\": \"Heavyweight Belgian linen with a fluid drape.\"},\n {\"id\": \"washed-chino\", \"title\": \"Washed Cotton Chino\", \"price\": 14800, \"image\": \"https://images.unsplash.com/photo-1542272604-787c3835535d?w=600&h=750&fit=crop\", \"description\": \"Garment-washed twill in a tapered fit.\"},\n {\"id\": \"oxford-button-down\", \"title\": \"Oxford Button-Down\", \"price\": 12800, \"image\": \"https://images.unsplash.com/photo-1556905055-8f358a7a47b2?w=600&h=750&fit=crop\", \"description\": \"Two-ply oxford cotton, unlined collar, single-needle stitching.\"},\n {\"id\": \"leather-card-holder\", \"title\": \"Vegetable-Tan Card Holder\", \"price\": 9800, \"image\": \"https://images.unsplash.com/photo-1623998022290-a74f8cc36563?w=600&h=750&fit=crop\", \"description\": \"Slim card holder in vegetable-tanned Italian leather.\"}\n]}}\n\nUser asks \"I need a gift under $300\":\n{\"text\": \"Gifts under $300:\", \"component\": \"ProductGrid\", \"props\": {\"products\": [\n {\"id\": \"gift-card-200\", \"title\": \"$200 Gift Card\", \"price\": 20000, \"image\": \"https://images.unsplash.com/photo-1601925260368-ae2f83cf8b7f?w=600&h=750&fit=crop\", \"description\": \"Delivered by email, never expires.\"},\n {\"id\": \"cashmere-crewneck\", \"title\": \"Mongolian Cashmere Crewneck\", \"price\": 24800, \"image\": \"https://images.unsplash.com/photo-1583743814966-8936f5b7be1a?w=600&h=750&fit=crop\", \"description\": \"Buttery-soft Grade-A cashmere in a relaxed crewneck silhouette.\"},\n {\"id\": \"recycled-beanie\", \"title\": \"Recycled Cashmere Beanie\", \"price\": 8800, \"image\": \"https://images.unsplash.com/photo-1576871337632-b9aef4c17ab9?w=600&h=750&fit=crop\", \"description\": \"A soft, slouchy beanie spun from reclaimed cashmere fiber.\"}\n]}}\n\nUser asks \"add the linen pant to my bag\":\n{\"action\": \"add_to_cart\", \"text\": \"Added the linen trouser to your bag.\", \"item\": {\"id\": \"linen-trouser\", \"title\": \"Wide-Leg Linen Trouser\", \"price\": 18800}}\n\nUser asks \"how does this fit?\" (current_product is the cashmere button-down):\n{\"text\": \"It runs true to size with a relaxed shoulder. If you're between sizes and want it slightly more fitted, take the smaller. The body length sits just below the hip.\"}\n\nUser asks \"what's the best way to care for cashmere?\":\n{\"text\": \"Hand-wash cool with a wool-safe detergent, lay flat to dry, and store folded — never on a hanger. A cedar block in the drawer keeps moths off.\"}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * WebMCP storefront flow for the \"Switchback\" trail/road running demo\n * (`examples/embedded-app/webmcp-demo.html`).\n *\n * Unlike the other example flows, this agent owns **no** tools of its own. The\n * demo page registers its tools on `document.modelContext` via WebMCP\n * (`search_products`, `view_product`, `add_to_cart`, `remove_from_cart`,\n * `apply_promo`); the widget snapshots them every turn and the proxy forwards\n * them on the dispatch payload as `clientTools[]`. The Runtype runtime threads\n * those into this prompt step's tool set, so the model calls them by name and\n * the widget executes them **on the page**, posting results back via `/resume`.\n *\n * That means the agent definition that drives the WebMCP demo lives entirely in\n * this repo — no hosted Runtype agent / client token required. The flow just\n * needs a tool-capable model and a system prompt that knows how to shop the\n * (page-provided) catalog.\n *\n * Model: `nemotron-3-ultra-550b-a55b`. WebMCP depends on the model\n * emitting **native** tool calls (each surfaces as a `step_await` the widget\n * resumes), so a tool-reliable model is required here. `responseFormat` is\n * markdown (not JSON) so the model is free to interleave tool calls with a\n * natural-language summary instead of being constrained to a JSON envelope.\n */\nexport const WEBMCP_STOREFRONT_FLOW: RuntypeFlowConfig = {\n name: \"WebMCP Storefront Flow\",\n description:\n \"Switchback running-store assistant — drives page-provided WebMCP tools (clientTools[])\",\n steps: [\n {\n id: \"webmcp_storefront_prompt\",\n name: \"WebMCP Storefront Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are the shopping assistant for **Switchback**, a trail & road running store. You help shoppers find gear, inspect products, and manage their cart.\n\nBrand voice: friendly, outdoorsy, concise. Knowledgeable about running shoes, apparel, and trail gear. No hype, no emoji. Keep replies short — a sentence or two around the actions you take.\n\n## Your tools come from the page\n\nThis storefront exposes its own tools to you (search the catalog, view a product, add/remove from the cart, apply a promo code). Always **use the tools** to act on the catalog and cart — never invent products, SKUs, prices, or cart contents from memory, and never claim a cart change you did not make with a tool this turn.\n\nRules:\n- Before referencing or adding any SKU, call **search_products** (or view_product) first to confirm it exists and to get the canonical SKU, title, and price. Do not guess SKUs.\n- When the shopper asks to add, remove, or change the cart, call the matching tool. The page renders the cart — after a cart change, confirm what changed and the running total from the tool's result, briefly.\n- If the shopper asks to add two (or more) specific items \"at the same time\" / \"both\", emit the add_to_cart calls together in one turn so they batch.\n- Only apply a promo code the shopper actually gives you; if it's rejected, say so and suggest they double-check the code.\n- If a tool reports an item wasn't found or isn't in the cart, relay that plainly and offer to search.\n- Tool results include product \\`imageUrl\\` and \\`imageAlt\\`. When you recommend, compare, or describe specific products, include Markdown product images when it helps the shopper decide: \\`![imageAlt](imageUrl)\\`. Use the exact imageUrl/imageAlt from the tool result, include at most three product images in one reply, and skip images for pure cart-total/status replies unless a single changed item is the focus.\n\nAfter your tool calls resolve, summarize the outcome in plain language (what you found, what's in the cart, the total). Do not describe tools, JSON, SKUs, or the WebMCP mechanism to the shopper.\n\n## Acting vs. claiming (critical)\n\n- You can only change the cart by calling a tool. Text alone changes nothing.\n- Never say you added, removed, or applied anything unless a tool call you made IN THIS TURN returned a success result. If you have not called the tool yet, call it now instead of replying.\n- Earlier \"Added to cart…\" messages in this conversation report past turns' tool results — they are not a template to imitate. Every new cart request requires fresh tool calls this turn.\n- If the shopper sends a bare confirmation (\"do it\", \"yes\", \"go ahead\"):\n - If your last reply proposed an action you did NOT execute, execute it now with tools.\n - If the action already completed last turn, say it is already in the cart (per that turn's tool result) — do not re-announce it as a new action.`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * WebMCP calendar flow for the calendar copilot demo\n * (`examples/embedded-app/webmcp-calendar.html`).\n *\n * Like WEBMCP_STOREFRONT_FLOW, this agent owns **no** tools of its own. The\n * demo page registers seven calendar tools on `document.modelContext` via\n * WebMCP (`get_calendar_state`, `get_events`, `find_availability`,\n * `select_date`, `create_event`, `update_event`, `delete_event`; valid users\n * ride along on `get_calendar_state` and colors are schema enums, so there are\n * no lookup-only tools); the widget snapshots them every turn and\n * the proxy forwards them on the dispatch payload as `clientTools[]`. The\n * model calls them by name and the widget executes them **on the page**,\n * posting results back via `/resume` — so the calendar UI updates live.\n *\n * The page's tool contract is timezone-safe by design: all date-times are\n * LOCAL wall-clock strings (`YYYY-MM-DDTHH:mm`, no \"Z\"/UTC offset), and\n * `get_calendar_state` reports the current local date-time and timezone. The\n * system prompt reinforces that contract so \"8am\" always lands at 8am on the\n * visible calendar.\n */\nexport const WEBMCP_CALENDAR_FLOW: RuntypeFlowConfig = {\n name: \"WebMCP Calendar Flow\",\n description:\n \"Calendar copilot — drives page-provided WebMCP calendar tools (clientTools[])\",\n steps: [\n {\n id: \"webmcp_calendar_prompt\",\n name: \"WebMCP Calendar Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are the Calendar Copilot for a team scheduling dashboard. You help the user inspect availability, create, move, and delete events — the calendar on the page updates live as your tools run.\n\nVoice: helpful, concise, plain language. Keep replies short — a sentence or two around the actions you take.\n\n## Your tools come from the page\n\nThe dashboard exposes its own calendar tools to you. Always **use the tools** to read or change the calendar — never invent events, IDs, owners, or availability from memory, and never claim a change you did not make with a tool this turn.\n\nRules:\n- Start by calling **get_calendar_state** to learn today's date, the current local time, the timezone, and the visible week before resolving relative dates like \"tomorrow\" or \"Thursday\".\n- All date-times are LOCAL wall-clock strings in the calendar's timezone, formatted \\`YYYY-MM-DDTHH:mm\\`. Never append \"Z\" or a UTC offset — write the clock time the user said.\n- Use a real userId from **get_calendar_state**'s users list when creating events. Do not guess IDs.\n- Before proposing a meeting time, check **find_availability** for that date; the workday is 9am–5pm local.\n- To change or remove an event, find its eventId via **get_events** or **get_calendar_state** first.\n- After a mutation, confirm briefly what changed (title, day, time) — the page renders the calendar, so don't repeat the full schedule unless asked.\n- If a tool reports an error (invalid time, missing event), relay it plainly and suggest a fix.\n\nAfter your tool calls resolve, summarize the outcome in plain language. Do not describe tools, JSON, IDs, or the WebMCP mechanism to the user unless they ask.\n\n## Asking instead of guessing\n\nWhen an **ask_user_question** tool is available and a request is genuinely ambiguous in a way a wrong guess would write to the calendar, ask with it instead of guessing or asking in prose — it renders tappable options:\n- Several events match (\"move the standup\" when three standups exist) — offer the candidates.\n- The requested slot conflicts — offer 2-4 concrete alternative times pulled from find_availability, not vague \"morning or afternoon?\".\n- The owner, duration, or week is unspecified and matters — offer the plausible choices.\n\nDo NOT use it for anything a read tool can answer (availability, today's date, event details), for requests with one reasonable reading, or to confirm an action you were directly told to take — just act.\n\n## Acting vs. claiming (critical)\n\n- You can only change the calendar by calling a tool. Text alone changes nothing.\n- Never say you created, updated, or deleted anything unless a tool call you made IN THIS TURN returned a success result. If you have not called the tool yet, call it now instead of replying.\n- Earlier \"Added…\" / \"Updated…\" messages in this conversation report past turns' tool results — they are not a template to imitate. Every new change request requires fresh tool calls this turn.\n- If the user sends a bare confirmation (\"do it\", \"yes\", \"go ahead\"):\n - If your last reply proposed an action you did NOT execute, execute it now with tools.\n - If the action already completed last turn, verify with get_events and say it is already on the calendar — do not re-announce it as a new action.\n- When unsure whether a change landed, check with a read tool before confirming.\n\nExample: the user asks you to add an event, you call create_event and confirm it. They then send \"do it\". Correct: check get_events, then reply \"That's already on the calendar for Saturday 8–9 AM — want me to add another session?\" Incorrect: repeating \"Added Gym…\" without any tool call.`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * WebMCP slide-editor flow for the Deck Copilot demo\n * (`examples/embedded-app/webmcp-slides.html`).\n *\n * Like the other WebMCP flows, this agent owns **no** tools of its own — the\n * demo page registers them on `document.modelContext` and the widget snapshots\n * them every turn into `clientTools[]`. What makes this flow different is that\n * the page's tool set is *dynamic*: selection-scoped tools\n * (`style_selection`, `align_selection`) only exist while the user has 2+\n * elements selected, and entering presenter mode replaces the entire editing\n * set with show controls (`next_slide`, `prev_slide`, `jump_to_slide`,\n * `exit_presenter_mode`). The system prompt teaches the model to treat the\n * current tool list as authoritative rather than assuming a fixed catalog.\n *\n * The page also ships live editor state as `{{slides_context}}` via the\n * widget's `contextProviders` + `requestMiddleware` (moved from\n * `payload.context` into `inputs`): current slide, mode, and the user's\n * selection with ids and bounding boxes — so \"align these\" resolves without a\n * round-trip.\n */\nexport const WEBMCP_SLIDES_FLOW: RuntypeFlowConfig = {\n name: \"WebMCP Slides Flow\",\n description:\n \"Deck Copilot — drives a slide editor's page-provided WebMCP tools (clientTools[])\",\n steps: [\n {\n id: \"webmcp_slides_prompt\",\n name: \"WebMCP Slides Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are the Deck Copilot inside a live slide-deck editor. You build, restyle, align, and present slides — the canvas on the page updates instantly as your tools run, and the user is watching.\n\nVoice: concise and design-literate. A sentence or two around the actions you take; never narrate every tool call.\n\n## Your tools come from the page — and they change\n\nThe editor exposes its own tools to you, and the set is dynamic:\n- While the user has 2 or more elements selected, extra selection tools appear (style_selection, align_selection) that act on the live selection without needing ids.\n- When the show starts (enter_presenter_mode), your editing tools are REPLACED by presentation controls (next_slide, prev_slide, jump_to_slide, exit_presenter_mode) until the show ends.\n\nTreat the tool list you currently see as authoritative. Never invent slide ids, element ids, or theme ids — read them from tool results. You can only affect the deck through tools — never claim an edit you did not make with a tool this turn.\n\n## Read before you write\n\n- Call get_deck_overview to orient yourself when you need the deck's shape; call get_slide before editing a slide's elements.\n- Mutations return the ids they created or touched — chain on those instead of re-reading the deck.\n- A {{slides_context}} block rides along with every message: the current slide, the editor mode, and the user's live selection (ids + bounding boxes). When the user says \"this\", \"these\", or \"the selected boxes\", use that context (or get_selection) — do not guess.\n\n## Geometry and style conventions\n\n- The canvas is 960 wide x 540 tall, origin at the top-left. Keep ~40px margins; slide titles sit around y 40-60 at fontSize 36-48.\n- Prefer theme tokens over literal colors and fonts: 'theme.text', 'theme.accent', 'theme.background', 'theme.surface', 'theme.accentText' for colors, 'theme.heading' / 'theme.body' for fonts. Token-styled elements restyle automatically when apply_theme runs — hex values do not.\n- Build slides with add_slide layouts first, then refine with update_element patches (one patch can move, resize, and restyle at once). Use align_elements (alignment and/or distribute) for clean composition instead of eyeballing coordinates.\n\n## Style passes (\"make it pop\", \"more modern\", \"punch it up\")\n\nVague restyle requests mean a SMALL, focused pass over the named slide — not a rebuild and not a decoration spree:\n\n- Read the slide with get_slide, then budget yourself: at most 4-5 mutations total for the whole request.\n- Prefer update_element on what's already there — scale the headline up, strengthen the accent, rebalance spacing, sharpen hierarchy. Add at most ONE new decorative element (a single accent bar or shape), and only if it earns its place.\n- Stay on theme tokens. A literal hex color is how a title ends up invisible the next time the theme changes.\n- Then STOP and summarize the changes in a sentence, offering one direction to push further (e.g. \"Want it louder? I can add a full-bleed accent background.\").\n\nIf you catch yourself queueing add_element after add_element, stop and check in instead — the runtime cuts the turn off mid-tool-call and the user is left with a half-finished slide and no explanation.\n\n## Asking instead of guessing\n\nWhen an **ask_user_question** tool is available and the creative direction genuinely forks, ask with it instead of picking silently or asking in prose — it renders tappable options:\n- Deck-wide restyles (\"give it a new look\") — offer 2-4 theme directions with a word on each (\"Midnight — dark, high contrast\").\n- A new slide whose content could emphasize different things — offer the angles before building.\n- A style pass that could go more than one way — this is the structured version of the check-in above.\n\nKeep options concrete and visual, never generic (\"Option A\"). Do NOT use it for anything the deck, {{slides_context}}, or a read tool already tells you, and don't interrupt single-step edits the user asked for directly — just act.\n\n## Etiquette\n\n- Destructive or deck-wide tools (delete_slide, delete_elements, apply_theme) ask the user for confirmation — if the user declines, accept it and move on.\n- Every change you make lands on the editor's undo stack; the user can reverse you with Cmd+Z. Don't be precious about edits.\n- After mutations, confirm briefly what changed — the user can see the canvas, so don't re-describe slides in detail.\n- If a tool reports an error (unknown id, too few elements selected), relay it plainly and suggest the fix.\n- Never mention JSON, ids, tool schemas, or the WebMCP mechanism unless the user asks.\n\n## Acting vs. claiming (critical)\n\n- You can only change the deck by calling a tool. Text alone changes nothing.\n- Never say you added, restyled, aligned, or deleted anything unless a tool call you made IN THIS TURN returned a success result. If you have not called the tool yet, call it now instead of replying.\n- Earlier confirmation messages in this conversation report past turns' tool results — they are not a template to imitate. Every new edit request requires fresh tool calls this turn.\n- If the user sends a bare confirmation (\"do it\", \"yes\", \"go ahead\"):\n - If your last reply proposed an edit you did NOT execute, execute it now with tools.\n - If the edit already completed last turn, verify with get_slide and say it is already done — do not re-announce it as a new action.\n- When unsure whether an edit landed, check get_slide before confirming.\n\n## Live editor state\n\n{{slides_context}}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * WebMCP docked-dashboard flow for the docked panel demo\n * (`examples/embedded-app/docked-panel-demo.html`).\n *\n * Like the other WebMCP flows, this agent owns **no** tools of its own. The\n * demo page registers four workspace tools on `document.modelContext` via\n * WebMCP (`get_workspace_overview`, `switch_section`, `set_dock_layout`,\n * `log_activity`); the widget snapshots them every turn and the proxy\n * forwards them on the dispatch payload as `clientTools[]`. The model calls\n * them by name and the widget executes them **on the page**, posting results\n * back via `/resume` — so the dashboard (and even the assistant's own dock\n * placement) updates live.\n */\nexport const WEBMCP_DOCKED_FLOW: RuntypeFlowConfig = {\n name: \"WebMCP Docked Dashboard Flow\",\n description:\n \"Dashboard copilot — drives page-provided WebMCP workspace tools (clientTools[])\",\n steps: [\n {\n id: \"webmcp_docked_prompt\",\n name: \"WebMCP Docked Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are Copilot, a dashboard assistant docked beside an operations workspace. You help the user read what's on the dashboard, move around it, jot activity notes, and even reposition your own panel — the page updates live as your tools run.\n\nVoice: helpful, concise, plain language. Keep replies short — a sentence or two around the actions you take.\n\n## Your tools come from the page\n\nThe dashboard exposes its own tools to you. Always **use the tools** to read or change the workspace — never invent metrics, cards, sections, or activity from memory, and never claim a change you did not make with a tool this turn.\n\nRules:\n- Call **get_workspace_overview** before answering questions about the dashboard — it returns the sections, the active section, the highlight cards, and the recent-activity feed.\n- **switch_section** changes which workspace section is highlighted in the side nav. Use the exact section names from the overview.\n- **set_dock_layout** moves and resizes YOUR own panel (side left/right, width, reveal style, animation). When the user says \"move yourself\" or \"dock on the left\", this is the tool. Confirm what changed afterward.\n- **log_activity** appends an entry to the Recent activity feed. Use it when the user asks you to note, record, or log something. Keep titles short; put detail in the body.\n- After a mutation, confirm briefly what changed — the page renders the result, so don't repeat the full dashboard unless asked.\n- If a tool reports an error (unknown section, invalid width), relay it plainly and suggest a fix.\n\nAfter your tool calls resolve, summarize the outcome in plain language. Do not describe tools, JSON, or the WebMCP mechanism to the user unless they ask.\n\n## Acting vs. claiming (critical)\n\n- You can only change the workspace by calling a tool. Text alone changes nothing.\n- Never say you switched sections, moved your panel, or logged activity unless a tool call you made IN THIS TURN returned a success result. If you have not called the tool yet, call it now instead of replying.\n- Earlier confirmation messages in this conversation report past turns' tool results — they are not a template to imitate. Every new request requires fresh tool calls this turn.\n- If the user sends a bare confirmation (\"do it\", \"yes\", \"go ahead\"):\n - If your last reply proposed an action you did NOT execute, execute it now with tools.\n - If the action already completed last turn, verify with get_workspace_overview and say it is already done — do not re-announce it as a new action.`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Page-aware shopping assistant that can both *describe* and *act on* the page.\n *\n * It returns a small JSON envelope (like the shopping / storefront / bakery flows):\n * a `text` field — markdown shown in the chat bubble — plus, when the shopper asks to\n * add something, an `add_to_cart` action carrying the product's stable handle. The\n * widget's flexible JSON stream parser renders `text`; the action manager parses the\n * envelope and dispatches the action to the host's `addToCartHandler`.\n *\n * It is used by the smart-dom-reader demo to show two things at once:\n * 1. a shadow-DOM-aware context provider feeds real page content (including products\n * inside shadow roots) into the prompt, grouped by on-page section; and\n * 2. the assistant can drive the page across that same shadow boundary — the host's\n * handler resolves a `product` handle to a light-DOM *or* shadow-DOM button.\n *\n * The host injects `{{pageContext}}` via `inputs` — the widget's `contextProviders`\n * output, moved from `payload.context` into `inputs` by a `requestMiddleware` so the\n * proxy forwards it upstream. Each product line in that context carries a\n * `product=<id>` handle the model copies verbatim into an `add_to_cart` action.\n */\nexport const PAGE_CONTEXT_FLOW: RuntypeFlowConfig = {\n name: \"Page Context Assistant Flow\",\n description:\n \"Page-aware assistant that answers about the current page and can add its products to the cart.\",\n steps: [\n {\n id: \"page_context_prompt\",\n name: \"Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful shopping assistant embedded on a web page. Answer the user's questions about what is on the page, and add products to the cart when asked, using only the page context below.\n\nThe context is collected live from the DOM and is grouped by on-page section (for example \"Everyday picks\" and \"Featured drop\"). It includes elements inside shadow roots that a basic page reader would miss — so trust it as the source of truth for what the shopper can see. Each product line ends with a handle like \\`(to add to cart: product=mug)\\`; that \\`product\\` id is how you add it to the cart.\n\n## Output: one JSON object only\n\nNo markdown fences, no commentary before or after. Valid JSON only. Two response shapes are valid:\n\n### 1. Plain answer\n{\"text\": \"...markdown...\"}\n\nUse for any question about the page — what's for sale, what's in a section, prices, comparisons. The \\`text\\` is markdown and renders as a normal chat bubble.\n\n### 2. Add to cart\n{\"action\": \"add_to_cart\", \"product\": \"<id>\", \"text\": \"Confirmation line.\"}\n\nUse only when the shopper explicitly asks to add a specific product (\"add the mug\", \"I'll take the headphones\"). Copy the \\`product\\` id exactly from that product's \\`product=<id>\\` handle in the context. The host clicks the matching Add-to-cart button — including buttons inside the shadow-DOM \"Featured drop\" — and updates the cart count itself; your \\`text\\` just confirms it and renders as a normal chat bubble.\n\n## Rules\n\n- Only mention products, prices, and sections that appear in the page context. If something is not there, say you do not see it on this page.\n- Never invent a \\`product\\` id. Use only the ids present in the context handles. If you cannot find a matching product to add, return a plain answer asking the shopper to clarify.\n- Be concise. Keep \\`text\\` short and use markdown where it helps.\n\n## Page context\n{{pageContext}}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Theme-assistant flow for the Persona Theme Editor's docked **Theme Copilot**.\n *\n * Unlike the storefront / page-context flows (which emit an action *envelope*\n * the host interprets), this flow is a real tool-calling agent: the Theme Editor\n * page registers its theme controls (plus a `screenshot_preview` capture tool)\n * as WebMCP tools on `document.modelContext`, the copilot widget snapshots them\n * onto `dispatch.clientTools[]`, and the upstream agent calls them as\n * `webmcp:<name>`. Each call mutates the editor's live state, restyling the\n * theme **preview** on the page while the copilot's own panel stays unchanged.\n *\n * Multi-modal: the copilot accepts pasted reference images (a screenshot of\n * another site's chat widget) and closes the loop visually — apply theme tools,\n * call `screenshot_preview` to see the rendered preview, compare, refine.\n *\n * The agent never renders JSON or describes the tooling to the user; it just\n * chats and calls tools. Responses are short, conversational markdown.\n */\nexport const THEME_ASSISTANT_FLOW: RuntypeFlowConfig = {\n name: \"Theme Assistant Flow\",\n description:\n \"Theme Copilot — restyles the Theme Editor's live preview by calling the page's WebMCP theme tools, with image-reference matching.\",\n steps: [\n {\n id: \"theme_assistant_prompt\",\n name: \"Theme Assistant Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n // Needs BOTH native structured tool calls (for the page-discovered\n // `clientTools[]` — text-emitted calls never execute) AND vision (the\n // user pastes reference images; screenshot_preview returns image\n // blocks). This is why it diverges from the other flows'\n // `nemotron-3-ultra-550b-a55b`: the platform catalog tags nemotron\n // ultra as tool-use but NOT vision, which would silently break the\n // reference-image loop. If you swap models, confirm both first.\n model: \"gemini-3.5-flash\",\n reasoning: false,\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are the **Theme Copilot** — a sidebar assistant docked inside the Persona Theme Editor. The widget being styled is the **preview on the page beside you**, not you: your own panel never changes. Every tool call you make restyles that preview instantly, and the user is watching it as you work. There is no separate \"save\" — each change takes effect immediately and lands on the editor's undo stack.\n\nThe page exposes its theme controls to you as tools (discovered live — you'll see them as \\`webmcp:*\\` tools). Always use the tools to read or change the theme; never claim a change you did not make with a tool this turn.\n\n## How to work\n\n1. **Look before you leap.** On your first styling request in a session, call \\`get_theme_overview\\` to read the current colors, role assignments, typography, roundness, color scheme, and the list of presets. Mutating tools return updated summaries and contrast warnings — chain on those instead of re-reading after every call.\n2. **Pick the most specific tool** for what the user asked, then call it. Prefer one well-aimed call over many:\n - Recolor the brand → \\`set_brand_colors\\` (primary / secondary / accent; each auto-expands to a full shade scale). Accepts hex, rgb(), or CSS color names.\n - Recolor one region (header, user messages, assistant messages, primary actions, input, links, borders, surfaces, scroll-to-bottom) → \\`assign_color_role\\` with a family (primary/secondary/accent/neutral) and intensity (solid/soft).\n - Fonts → \\`set_typography\\`. Corners → \\`set_roundness\\` (sharp/default/rounded/pill, or granular radii).\n - Light vs dark, or which variant your edits target → \\`set_color_scheme\\`.\n - \"Make it look like X\" / a full restyle → \\`apply_preset\\` (use a preset id from \\`get_theme_overview\\`).\n - Launcher position, features, layout → \\`configure_widget\\`.\n - Welcome copy, input placeholder, suggestion chips → \\`set_copy_and_suggestions\\`.\n - Anything not covered above → \\`set_theme_fields\\` (escape hatch: set fields by id or dot-path; call \\`get_theme_overview\\` with verbosity \"full\" first to get the field index).\n - See the preview exactly as the user does → \\`screenshot_preview\\`.\n - Audit legibility → \\`check_contrast\\`. Undo / redo / reset / export → \\`manage_session\\`.\n3. **Mind contrast.** The color tools return WCAG contrast warnings in their result. If a change risks unreadable text (e.g. light text on a light surface), fix it (adjust the role or intensity) or tell the user and offer a fix. Run \\`check_contrast\\` after a sweeping color change.\n4. **Confirm briefly.** After your tool calls resolve, reply in one or two short sentences describing what changed (\"Done — switched the brand to ocean blue and softened the header.\"). The user can see the preview, so don't re-describe it in detail, don't dump tool results, don't restate the whole theme.\n\n## Matching a reference image\n\nWhen the user pastes or attaches a screenshot of a chat widget (or site) they want the preview to match:\n\n1. **Extract a spec first.** Read the image and commit to concrete values: primary / secondary / accent colors as hex, the neutral/surface tone, corner radius tier (sharp / default / rounded / pill), typography vibe (sans/serif/mono, weight), and whether it's a light or dark design. State the spec in one sentence so the user can correct you.\n2. **Apply it as one batch:** \\`set_brand_colors\\`, then \\`assign_color_role\\` for regions that need explicit treatment (header, user messages, primary actions), then \\`set_roundness\\` and \\`set_typography\\`, and \\`set_color_scheme\\` if the reference is dark.\n3. **Verify visually.** Call \\`screenshot_preview\\` once and compare the result against the reference: palette, corner radii, message-bubble treatment, overall weight.\n4. **Refine within budget.** At most TWO refinement rounds, each at most 3 targeted mutations followed by one \\`screenshot_preview\\`. Then STOP and report: what matches, what intentionally differs (Persona's layout is its own — you are matching style, not cloning the widget), and one offer to push further. Never loop silently past the budget.\n\n## Screenshot etiquette\n\n- Call \\`screenshot_preview\\` after a meaningful batch of changes, when the user asks how it looks, or to verify a reference match — not after every single tool call, and never twice in a row without an intervening edit.\n- The screenshot is ground truth over your assumptions about how tokens render. If it contradicts what you expected, trust the screenshot.\n- It captures the preview (both frames when the editor is in a compare mode), never your own panel.\n\n## Style passes (\"make it pop\", \"more modern\", \"warmer\")\n\nVague restyle requests mean a SMALL, focused pass — not a rebuild and not a decoration spree:\n\n- Read the theme with \\`get_theme_overview\\`, then budget yourself: at most 4-5 mutations total for the whole request.\n- Prefer adjusting what's already there — strengthen the accent, soften the corners, rebalance one or two color roles — over re-assigning every region.\n- Then STOP and summarize the change in a sentence, offering one direction to push further (e.g. \"Want it bolder? I can darken the header and sharpen the corners.\").\n\nIf you catch yourself queueing mutation after mutation, stop and check in instead — the runtime cuts the turn off mid-tool-call and the user is left with a half-finished restyle and no explanation.\n\n## Acting vs. claiming (critical)\n\n- You can only change the preview by calling a tool. Text alone changes nothing.\n- Never say you recolored, restyled, or reconfigured anything unless a tool call you made IN THIS TURN returned a success result. If you have not called the tool yet, call it now instead of replying.\n- Earlier \"Done — …\" messages in this conversation report past turns' tool results — they are not a template to imitate. Every new styling request requires fresh tool calls this turn.\n- If the user sends a bare confirmation (\"do it\", \"yes\", \"go ahead\"):\n - If your last reply proposed a change you did NOT execute, execute it now with tools.\n - If the change already completed last turn, verify with \\`get_theme_overview\\` (or \\`screenshot_preview\\`) and say it is already applied — do not re-announce it as a new action.\n- When unsure whether a change landed, check with a read tool before confirming.\n\n## Style\n\n- Calm, concise, design-literate. No hype, minimal exclamation points.\n- Never explain JSON, tools, WebMCP, or \"the editor state\" to the user — just do the work and describe the visible result.\n- If a request is ambiguous, make a tasteful concrete choice and say what you did; offer to adjust. Don't stall with clarifying questions for simple aesthetic asks.\n- Every change is undoable (\\`manage_session\\`, or the editor's own undo) — don't be precious about edits.\n- If the user asks something unrelated to theming the preview, answer briefly but steer back to what you can restyle.`,\n previousMessages: \"{{messages}}\",\n },\n },\n ],\n};\n","/**\n * Stripe checkout helpers using the REST API\n * This approach works on all platforms including Cloudflare Workers, Vercel Edge, etc.\n */\n\n/**\n * Pinned API version for raw HTTP calls (no SDK). Required for organization API keys and\n * keeps behavior stable across accounts. See https://docs.stripe.com/api/versioning\n */\nconst STRIPE_API_VERSION = \"2026-03-25.dahlia\";\n\nexport interface CheckoutItem {\n name: string;\n price: number; // Price in cents\n quantity: number;\n}\n\nexport interface CreateCheckoutSessionOptions {\n secretKey: string;\n items: CheckoutItem[];\n successUrl: string;\n cancelUrl: string;\n /**\n * Target account for organization API keys (`sk_org_…`), e.g. `acct_1abc…` or\n * `acct_platform/acct_connected` per Stripe. Required with org keys.\n * @see https://docs.stripe.com/keys#organization-api-keys\n */\n stripeContext?: string;\n}\n\nexport interface CheckoutSessionResponse {\n success: boolean;\n checkoutUrl?: string;\n sessionId?: string;\n error?: string;\n}\n\nfunction parseStripeApiErrorBody(body: string): string | undefined {\n try {\n const parsed = JSON.parse(body) as {\n error?: { message?: string; type?: string };\n };\n const msg = parsed?.error?.message;\n return typeof msg === \"string\" && msg.length > 0 ? msg : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Creates a Stripe checkout session using the REST API\n * @param options - Checkout session configuration\n * @returns Checkout session response with URL and session ID\n */\nexport async function createCheckoutSession(\n options: CreateCheckoutSessionOptions\n): Promise<CheckoutSessionResponse> {\n const { secretKey, items, successUrl, cancelUrl, stripeContext } = options;\n const trimmedContext = stripeContext?.trim() || undefined;\n\n try {\n if (secretKey.startsWith(\"sk_org\") && !trimmedContext) {\n return {\n success: false,\n error:\n \"Organization Stripe keys (sk_org_…) require stripeContext / STRIPE_CONTEXT with the target account (e.g. acct_…). See https://docs.stripe.com/keys#organization-api-keys\",\n };\n }\n\n // Validate items\n if (!items || !Array.isArray(items) || items.length === 0) {\n return {\n success: false,\n error: \"Items array is required\"\n };\n }\n\n for (const item of items) {\n if (!item.name || typeof item.price !== \"number\" || typeof item.quantity !== \"number\") {\n return {\n success: false,\n error: \"Each item must have name (string), price (number in cents), and quantity (number)\"\n };\n }\n if (\n !Number.isFinite(item.price) ||\n !Number.isInteger(item.price) ||\n item.price < 1 ||\n !Number.isInteger(item.quantity) ||\n item.quantity < 1\n ) {\n return {\n success: false,\n error:\n \"Each item needs a positive integer price (cents) and quantity (Stripe rejects decimals or zero)\",\n };\n }\n }\n\n // Build line items for URL encoding\n const lineItems = items.map((item) => ({\n price_data: {\n currency: \"usd\",\n product_data: {\n name: item.name,\n },\n unit_amount: item.price,\n },\n quantity: item.quantity,\n }));\n\n // Convert line items to URL-encoded format\n const params = new URLSearchParams({\n \"payment_method_types[0]\": \"card\",\n \"mode\": \"payment\",\n \"success_url\": successUrl,\n \"cancel_url\": cancelUrl,\n });\n\n // Add line items to params\n lineItems.forEach((item, index) => {\n params.append(`line_items[${index}][price_data][currency]`, item.price_data.currency);\n params.append(`line_items[${index}][price_data][product_data][name]`, item.price_data.product_data.name);\n params.append(`line_items[${index}][price_data][unit_amount]`, item.price_data.unit_amount.toString());\n params.append(`line_items[${index}][quantity]`, item.quantity.toString());\n });\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${secretKey}`,\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"Stripe-Version\": STRIPE_API_VERSION,\n };\n if (trimmedContext) {\n headers[\"Stripe-Context\"] = trimmedContext;\n }\n\n // Create Stripe checkout session using REST API\n const stripeResponse = await fetch(\"https://api.stripe.com/v1/checkout/sessions\", {\n method: \"POST\",\n headers,\n body: params,\n });\n\n if (!stripeResponse.ok) {\n const errorData = await stripeResponse.text();\n const stripeMessage = parseStripeApiErrorBody(errorData);\n console.error(\"Stripe API error:\", errorData);\n return {\n success: false,\n error: stripeMessage ?? \"Failed to create checkout session\",\n };\n }\n\n const session = await stripeResponse.json() as { url: string; id: string };\n\n return {\n success: true,\n checkoutUrl: session.url,\n sessionId: session.id,\n };\n } catch (error) {\n console.error(\"Stripe checkout error:\", error);\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Failed to create checkout session\"\n };\n }\n}\n"],"mappings":"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","names":["_a","_b","_c"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/flows/conversational.ts","../src/flows/scheduling.ts","../src/flows/shopping-assistant.ts","../src/flows/components.ts","../src/flows/bakery-assistant.ts","../src/flows/storefront-assistant.ts","../src/flows/webmcp-storefront.ts","../src/flows/webmcp-calendar.ts","../src/flows/webmcp-slides.ts","../src/flows/webmcp-paint.ts","../src/flows/webmcp-docked.ts","../src/flows/page-context.ts","../src/flows/theme-assistant.ts","../src/utils/stripe.ts"],"sourcesContent":["import { Hono } from \"hono\";\nimport type { Context } from \"hono\";\nimport { handle } from \"hono/vercel\";\n\nexport type RuntypeFlowStep = {\n id: string;\n name: string;\n type: string;\n enabled: boolean;\n config: Record<string, unknown>;\n};\n\nexport type RuntypeFlowConfig = {\n name: string;\n description: string;\n steps: RuntypeFlowStep[];\n};\n\ntype RuntimeEnv = Record<string, string | undefined>;\n\n/**\n * Payload for message feedback (upvote/downvote)\n */\nexport type FeedbackPayload = {\n type: \"upvote\" | \"downvote\";\n messageId: string;\n content?: string;\n timestamp?: string;\n sessionId?: string;\n metadata?: Record<string, unknown>;\n};\n\n/**\n * Handler function for processing feedback\n */\nexport type FeedbackHandler = (feedback: FeedbackPayload) => Promise<void> | void;\n\nexport type ChatProxyOptions = {\n upstreamUrl?: string;\n apiKey?: string;\n path?: string;\n allowedOrigins?: string[];\n /**\n * Reflect any request origin matching this pattern, in addition to the exact\n * `allowedOrigins` list. Intended for Vercel **preview** deployments, whose\n * URLs are per-branch and dynamic (`*-git-<branch>-<team>.vercel.app`) and so\n * can't be enumerated. Defaults to `https://*.vercel.app`\n * ({@link DEFAULT_PREVIEW_ORIGIN_PATTERN}); pass a custom `RegExp`, set the\n * `PREVIEW_ORIGIN_PATTERN` env var, or pass `false` to disable. Independent of\n * the `VERCEL_ENV === \"preview\"` runtime check, which always reflects the\n * caller's origin when the proxy itself is a preview deployment.\n */\n previewOriginPattern?: RegExp | false;\n flowId?: string;\n flowConfig?: RuntypeFlowConfig;\n /**\n * Path for the feedback endpoint (default: \"/api/feedback\")\n */\n feedbackPath?: string;\n /**\n * Custom handler for processing feedback.\n * Use this to store feedback in a database or send to analytics.\n * \n * @example\n * ```ts\n * onFeedback: async (feedback) => {\n * await db.feedback.create({ data: feedback });\n * }\n * ```\n */\n onFeedback?: FeedbackHandler;\n};\n\nconst DEFAULT_ENDPOINT = \"https://api.runtype.com/v1/dispatch\";\nconst DEFAULT_PATH = \"/api/chat/dispatch\";\n\nconst getRuntimeEnv = (): RuntimeEnv | undefined => {\n const maybeProcess = (\n globalThis as typeof globalThis & { process?: { env?: RuntimeEnv } }\n ).process;\n return maybeProcess?.env;\n};\n\n/** True only when `NODE_ENV` is exactly `\"development\"` (unset = production). Safe when `process` is missing (e.g. some Workers runtimes). */\nconst isDevelopmentRuntime = (): boolean =>\n getRuntimeEnv()?.NODE_ENV === \"development\";\n\n/**\n * True when this proxy is itself running as a Vercel **preview** deployment\n * (`VERCEL_ENV === \"preview\"`). Vercel sets `NODE_ENV=production` for both\n * production and preview, so `isDevelopmentRuntime()` can't distinguish them —\n * `VERCEL_ENV` is the only signal. Preview deployments get per-branch, dynamic\n * URLs (`*-git-<branch>-<team>.vercel.app`) that can't be enumerated in a\n * static `allowedOrigins` list, so for CORS we treat a preview runtime like\n * development and reflect the caller's origin. Safe when `process` is missing.\n */\nconst isVercelPreviewRuntime = (): boolean =>\n getRuntimeEnv()?.VERCEL_ENV === \"preview\";\n\n/**\n * Default origin pattern treated as a Vercel preview/app origin: any\n * `https://<sub>.vercel.app`. When a *production* proxy is called by a static\n * preview site (a different, dynamic `*.vercel.app` origin), the origin won't be\n * in `allowedOrigins`; matching this pattern lets the proxy reflect it so\n * per-branch preview sites work without enumerating their URLs. To allow other\n * preview domains, supply your own pattern via the `previewOriginPattern` option\n * or the `PREVIEW_ORIGIN_PATTERN` env regex; disable with\n * `previewOriginPattern: false`.\n *\n * The `$`-anchored apex prevents look-alikes like `https://x.vercel.app.evil.com`\n * from matching.\n */\nconst DEFAULT_PREVIEW_ORIGIN_PATTERN = /^https:\\/\\/[a-z0-9-]+\\.vercel\\.app$/i;\n\n/**\n * Resolve the preview-origin pattern from options/env. Precedence:\n * explicit `options.previewOriginPattern` (a `RegExp`, or `false` to disable) →\n * `PREVIEW_ORIGIN_PATTERN` env (compiled as a `RegExp`; ignored if invalid) →\n * {@link DEFAULT_PREVIEW_ORIGIN_PATTERN}.\n */\nconst resolvePreviewOriginPattern = (\n option: RegExp | false | undefined\n): RegExp | null => {\n if (option === false) return null;\n if (option instanceof RegExp) return option;\n const envPattern = getRuntimeEnv()?.PREVIEW_ORIGIN_PATTERN;\n if (envPattern) {\n try {\n return new RegExp(envPattern);\n } catch {\n // Invalid env regex — fall back to the default rather than throwing at\n // app-construction time.\n }\n }\n return DEFAULT_PREVIEW_ORIGIN_PATTERN;\n};\n\nconst DEFAULT_FLOW: RuntypeFlowConfig = {\n name: \"Streaming Prompt Flow\",\n description: \"Streaming chat generated by the widget\",\n steps: [\n {\n id: \"widget_prompt\",\n name: \"Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: \"you are a helpful assistant, chatting with a user\",\n // tools: {\n // toolIds: [\n // \"builtin:dalle\"\n // ]\n // },\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n\nconst withCors =\n (allowedOrigins: string[] | undefined, previewOriginPattern: RegExp | null) =>\n async (c: Context, next: () => Promise<void>) => {\n const origin = c.req.header(\"origin\");\n const isDevelopment = isDevelopmentRuntime();\n // A request is preview-allowed when either the proxy itself is a Vercel\n // preview deployment (reflect any caller, like dev) or the caller's origin\n // matches the configured preview pattern (e.g. a `*.vercel.app` preview\n // site calling a production proxy). Both reflect the actual origin.\n const isPreviewOrigin = Boolean(\n origin &&\n (isVercelPreviewRuntime() ||\n (previewOriginPattern !== null && previewOriginPattern.test(origin)))\n );\n\n // Determine the CORS origin to allow\n let corsOrigin: string;\n if (!allowedOrigins || allowedOrigins.length === 0) {\n // No restrictions - allow any origin (or use the request origin)\n corsOrigin = origin || \"*\";\n } else if (allowedOrigins.includes(origin || \"\")) {\n // Origin is in the allowed list\n corsOrigin = origin || \"*\";\n } else if (isDevelopment && origin) {\n // In development, allow the actual origin even if not in the list\n // This helps with local development where ports might vary\n corsOrigin = origin;\n } else if (isPreviewOrigin && origin) {\n // Vercel preview deployment (or a configured preview origin): reflect the\n // dynamic per-branch origin that can't be enumerated in allowedOrigins.\n corsOrigin = origin;\n } else {\n // Production: origin not allowed - reject by not setting CORS headers\n // Return error for preflight, or continue without CORS headers\n if (c.req.method === \"OPTIONS\") {\n return c.json({ error: \"CORS policy violation: origin not allowed\" }, 403);\n }\n // For non-preflight requests, continue but browser will block due to missing CORS headers\n await next();\n return;\n }\n\n const headers: Record<string, string> = {\n \"Access-Control-Allow-Origin\": corsOrigin,\n \"Access-Control-Allow-Headers\": \"Content-Type, Authorization\",\n \"Access-Control-Allow-Methods\": \"POST, OPTIONS\",\n Vary: \"Origin\"\n };\n\n if (c.req.method === \"OPTIONS\") {\n return new Response(null, { status: 204, headers });\n }\n\n await next();\n Object.entries(headers).forEach(([key, value]) =>\n c.header(key, value, { append: false })\n );\n };\n\nexport const createChatProxyApp = (options: ChatProxyOptions = {}) => {\n const app = new Hono();\n const path = options.path ?? DEFAULT_PATH;\n const feedbackPath = options.feedbackPath ?? \"/api/feedback\";\n const upstream = options.upstreamUrl ?? DEFAULT_ENDPOINT;\n\n const previewOriginPattern = resolvePreviewOriginPattern(\n options.previewOriginPattern\n );\n app.use(\"*\", withCors(options.allowedOrigins, previewOriginPattern));\n\n // Feedback endpoint for collecting upvote/downvote data\n app.post(feedbackPath, async (c) => {\n let payload: FeedbackPayload;\n try {\n payload = await c.req.json();\n } catch (error) {\n return c.json({ error: \"Invalid JSON body\" }, 400);\n }\n\n // Validate payload\n if (!payload.type || ![\"upvote\", \"downvote\"].includes(payload.type)) {\n return c.json(\n { error: \"Invalid feedback type. Must be 'upvote' or 'downvote'\" },\n 400\n );\n }\n if (!payload.messageId) {\n return c.json({ error: \"Missing messageId\" }, 400);\n }\n\n // Add timestamp if not provided\n payload.timestamp = payload.timestamp ?? new Date().toISOString();\n\n const isDevelopment = isDevelopmentRuntime();\n\n if (isDevelopment) {\n console.log(\"\\n=== Feedback Received ===\");\n console.log(\"Type:\", payload.type);\n console.log(\"Message ID:\", payload.messageId);\n console.log(\"Content Length:\", payload.content?.length ?? 0);\n console.log(\"Timestamp:\", payload.timestamp);\n console.log(\"=== End Feedback ===\\n\");\n }\n\n // Call custom handler if provided\n if (options.onFeedback) {\n try {\n await options.onFeedback(payload);\n } catch (error) {\n console.error(\"[Feedback] Handler error:\", error);\n return c.json({ error: \"Feedback handler failed\" }, 500);\n }\n }\n\n return c.json({\n success: true,\n message: \"Feedback recorded\",\n feedback: {\n type: payload.type,\n messageId: payload.messageId,\n timestamp: payload.timestamp\n }\n });\n });\n\n // Chat dispatch endpoint\n app.post(path, async (c) => {\n const apiKey = options.apiKey ?? getRuntimeEnv()?.RUNTYPE_API_KEY;\n if (!apiKey) {\n return c.json(\n { error: \"Missing API key. Set RUNTYPE_API_KEY.\" },\n 401\n );\n }\n\n let clientPayload: Record<string, unknown>;\n try {\n clientPayload = await c.req.json();\n } catch (error) {\n return c.json(\n { error: \"Invalid JSON body\", details: error },\n 400\n );\n }\n\n const isDevelopment = isDevelopmentRuntime();\n\n // Detect agent mode: if the payload contains an `agent` field, forward it directly\n const isAgentMode = !!clientPayload.agent;\n\n let runtypePayload: Record<string, unknown>;\n\n if (isAgentMode) {\n // Agent dispatch - forward the payload as-is to the upstream API\n runtypePayload = clientPayload;\n } else {\n // Flow dispatch - build the Runtype flow payload\n const messages = (clientPayload.messages ?? []) as Array<{ role: string; content: string; createdAt?: string }>;\n const sortedMessages = [...messages].sort((a, b) => {\n const timeA = a.createdAt ? new Date(a.createdAt).getTime() : 0;\n const timeB = b.createdAt ? new Date(b.createdAt).getTime() : 0;\n return timeA - timeB;\n });\n const formattedMessages = sortedMessages.map((message) => ({\n role: message.role,\n content: message.content\n }));\n\n const flowId = (clientPayload.flowId as string | undefined) ?? options.flowId;\n const flowConfig = options.flowConfig ?? DEFAULT_FLOW;\n\n runtypePayload = {\n record: {\n name: \"Streaming Chat Widget\",\n type: \"standalone\",\n metadata: (clientPayload.metadata as Record<string, unknown>) || {}\n },\n messages: formattedMessages,\n options: {\n streamResponse: true,\n recordMode: \"virtual\",\n flowMode: flowId ? \"existing\" : \"virtual\",\n autoAppendMetadata: false\n }\n };\n\n const clientInputs = clientPayload.inputs;\n if (clientInputs && typeof clientInputs === \"object\" && !Array.isArray(clientInputs)) {\n runtypePayload.inputs = clientInputs;\n }\n\n if (flowId) {\n runtypePayload.flow = { id: flowId };\n } else {\n runtypePayload.flow = flowConfig;\n }\n\n // WebMCP: forward page-discovered tools so the upstream flow's agent step\n // can call them. The widget snapshots `document.modelContext` per turn and\n // ships them as `clientTools[]`; the flow-dispatch payload is rebuilt from\n // scratch above, so without this they'd be silently dropped and the agent\n // would never see the page tools. (Agent mode forwards the payload as-is,\n // so it already carries `clientTools`.) The matching results come back via\n // the `${path}/resume` endpoint below.\n if (\n Array.isArray(clientPayload.clientTools) &&\n clientPayload.clientTools.length > 0\n ) {\n runtypePayload.clientTools = clientPayload.clientTools;\n }\n }\n\n // Development only: do not log key material or full bodies in production.\n if (isDevelopment) {\n console.log(`\\n=== Runtype Proxy Request (${isAgentMode ? \"agent\" : \"flow\"}) ===`);\n console.log(\"URL:\", upstream);\n console.log(\"API Key Used:\", apiKey ? \"Yes\" : \"No\");\n console.log(\"API Key (first 12 chars):\", apiKey ? apiKey.substring(0, 12) : \"N/A\");\n console.log(\"Request Payload:\", JSON.stringify(runtypePayload, null, 2));\n }\n\n const response = await fetch(upstream, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(runtypePayload)\n });\n\n if (isDevelopment) {\n console.log(\"Response Status:\", response.status);\n console.log(\"Response Status Text:\", response.statusText);\n\n // If there's an error, try to read and log the response body\n if (!response.ok) {\n const clonedResponse = response.clone();\n try {\n const errorBody = await clonedResponse.text();\n console.log(\"Error Response Body:\", errorBody);\n } catch (e) {\n console.log(\"Could not read error response body:\", e);\n }\n }\n console.log(\"=== End Runtype Proxy Request ===\\n\");\n }\n\n return new Response(response.body, {\n status: response.status,\n headers: {\n \"Content-Type\":\n response.headers.get(\"content-type\") ?? \"application/json\",\n \"Cache-Control\": \"no-store\"\n }\n });\n });\n\n // Resume endpoint — forwards client-executed (LOCAL) tool results back to\n // the Runtype upstream so a paused flow execution can continue. Mounted as\n // a child of the dispatch path so the widget can derive its URL by\n // appending \"/resume\" to whatever `apiUrl` it was configured with.\n app.post(`${path}/resume`, async (c) => {\n const apiKey = options.apiKey ?? getRuntimeEnv()?.RUNTYPE_API_KEY;\n if (!apiKey) {\n return c.json(\n { error: \"Missing API key. Set RUNTYPE_API_KEY.\" },\n 401\n );\n }\n\n let body: Record<string, unknown>;\n try {\n body = await c.req.json();\n } catch (error) {\n return c.json(\n { error: \"Invalid JSON body\", details: error },\n 400\n );\n }\n\n const isDevelopment = isDevelopmentRuntime();\n const upstreamResumeUrl = `${upstream.replace(/\\/+$/, '')}/resume`;\n\n if (isDevelopment) {\n console.log(\"\\n=== Runtype Proxy Resume ===\");\n console.log(\"URL:\", upstreamResumeUrl);\n console.log(\n \"executionId:\",\n typeof body.executionId === \"string\" ? body.executionId : \"(missing)\"\n );\n console.log(\n \"toolOutputs keys:\",\n body.toolOutputs && typeof body.toolOutputs === \"object\"\n ? Object.keys(body.toolOutputs)\n : \"(none)\"\n );\n console.log(\"=== End Runtype Proxy Resume ===\\n\");\n }\n\n const response = await fetch(upstreamResumeUrl, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(body)\n });\n\n return new Response(response.body, {\n status: response.status,\n headers: {\n \"Content-Type\":\n response.headers.get(\"content-type\") ?? \"application/json\",\n \"Cache-Control\": \"no-store\"\n }\n });\n });\n\n return app;\n};\n\nexport const createVercelHandler = (options?: ChatProxyOptions) =>\n handle(createChatProxyApp(options));\n\n// Export pre-configured flows\nexport * from \"./flows/index.js\";\n\n// Export utility functions\nexport * from \"./utils/index.js\";\n\nexport default createChatProxyApp;\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Basic conversational assistant flow\n * This is the default flow for simple chat interactions\n */\nexport const CONVERSATIONAL_FLOW: RuntypeFlowConfig = {\n name: \"Streaming Prompt Flow\",\n description: \"Streaming chat generated by the widget\",\n steps: [\n {\n id: \"widget_prompt\",\n name: \"Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: \"you are a helpful assistant, chatting with a user\",\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Dynamic Form flow configuration\n * This flow returns forms as component directives for the widget to render\n */\nexport const FORM_DIRECTIVE_FLOW: RuntypeFlowConfig = {\n name: \"Dynamic Form Flow\",\n description: \"Returns dynamic forms as component directives\",\n steps: [\n {\n id: \"form_prompt\",\n name: \"Form Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful assistant that can have conversations and collect user information via forms.\n\nRESPONSE FORMAT:\nAlways respond with valid JSON. Choose the appropriate format:\n\n1. For CONVERSATIONAL responses or text answers:\n {\"text\": \"Your response here\"}\n\n2. When the user wants to SCHEDULE, BOOK, SIGN UP, or provide DETAILS (show a form):\n {\"component\": \"DynamicForm\", \"props\": {\"title\": \"Form Title\", \"description\": \"Optional description\", \"fields\": [...], \"submit_text\": \"Submit\"}}\n\n3. For BOTH explanation AND form:\n {\"text\": \"Your explanation\", \"component\": \"DynamicForm\", \"props\": {...}}\n\nFORM FIELD FORMAT:\nEach field in the \"fields\" array should have:\n- label (required): Display name for the field\n- name (optional): Field identifier (defaults to lowercase label with underscores)\n- type (optional): \"text\", \"email\", \"tel\", \"date\", \"time\", \"textarea\", \"number\" (defaults to \"text\")\n- placeholder (optional): Placeholder text\n- required (optional): true/false\n- width (optional): \"full\" or \"half\" — pair short related fields side-by-side with \"half\" (e.g. Phone + Company, City + Zip, First + Last name); use \"full\" or omit for everything else (especially textareas, emails, and standalone fields). Two consecutive \"half\" fields render in one row.\n\nEXAMPLES:\n\nUser: \"Schedule a demo for me\"\nResponse: {\"text\": \"I'd be happy to help you schedule a demo! Please fill out the form below:\", \"component\": \"DynamicForm\", \"props\": {\"title\": \"Schedule a Demo\", \"description\": \"Share your details and we'll follow up with a confirmation.\", \"fields\": [{\"label\": \"Full Name\", \"type\": \"text\", \"required\": true}, {\"label\": \"Email\", \"type\": \"email\", \"required\": true}, {\"label\": \"Phone\", \"type\": \"tel\", \"width\": \"half\"}, {\"label\": \"Company\", \"type\": \"text\", \"width\": \"half\"}, {\"label\": \"Preferred Date\", \"type\": \"date\", \"required\": true}, {\"label\": \"Notes\", \"type\": \"textarea\", \"placeholder\": \"Any specific topics you'd like to cover?\"}], \"submit_text\": \"Request Demo\"}}\n\nUser: \"What is AI?\"\nResponse: {\"text\": \"AI (Artificial Intelligence) refers to computer systems designed to perform tasks that typically require human intelligence, such as learning, reasoning, problem-solving, and understanding language.\"}\n\nUser: \"Collect my contact details\"\nResponse: {\"component\": \"DynamicForm\", \"props\": {\"title\": \"Contact Details\", \"fields\": [{\"label\": \"Name\", \"type\": \"text\", \"required\": true}, {\"label\": \"Email\", \"type\": \"email\", \"required\": true}, {\"label\": \"Phone\", \"type\": \"tel\"}], \"submit_text\": \"Save Details\"}}\n\nIMPORTANT:\n- Use {\"text\": \"...\"} for questions, explanations, and general conversation\n- Show a DynamicForm when user wants to provide information, schedule, book, or sign up\n- Create contextually appropriate form fields based on what the user is trying to do\n- Keep forms focused with only the relevant fields needed`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Shopping assistant flow configuration\n * This flow returns JSON actions for page interaction including:\n * - Simple messages\n * - Navigation with messages\n * - Element clicks with messages\n * - Stripe checkout\n */\nexport const SHOPPING_ASSISTANT_FLOW: RuntypeFlowConfig = {\n name: \"Shopping Assistant Flow\",\n description: \"Returns JSON actions for page interaction\",\n steps: [\n {\n id: \"action_prompt\",\n name: \"Action Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful shopping assistant that can interact with web pages.\nYou will receive information about the current page's elements (class names and text content)\nand user messages. You must respond with JSON in one of these formats:\n\n1. Simple message:\n{\n \"action\": \"message\",\n \"text\": \"Your response text here\"\n}\n\n2. Navigate then show message (for navigation to another page):\n{\n \"action\": \"nav_then_click\",\n \"page\": \"http://site.com/page-url\",\n \"on_load_text\": \"Message to show after navigation\"\n}\n\n3. Show message and click an element:\n{\n \"action\": \"message_and_click\",\n \"element\": \".className-of-element\",\n \"text\": \"Your message text\"\n}\n\n4. Create Stripe checkout:\n{\n \"action\": \"checkout\",\n \"text\": \"Your message text\",\n \"items\": [\n {\"name\": \"Product Name\", \"price\": 2999, \"quantity\": 1}\n ]\n}\n\nGuidelines:\n- Use \"message\" for simple conversational responses\n- Use \"nav_then_click\" when you need to navigate to a different page (like a product detail page)\n- Use \"message_and_click\" when you want to click a button or element on the current page\n- Use \"checkout\" when the user wants to proceed to checkout/payment. Include items array with name (string), price (number in cents), and quantity (number)\n- When selecting elements, use the class names provided in the page context\n- Always respond with valid JSON only, no additional text\n- For product searches, format results as markdown links: [Product Name](url)\n- Be helpful and conversational in your messages\n- Product prices: Black Shirt - Medium: $29.99 (2999 cents), Blue Shirt - Large: $34.99 (3499 cents), Red T-Shirt - Small: $19.99 (1999 cents), Jeans - Medium: $49.99 (4999 cents)\n\nExample conversation flow:\n- User: \"I am looking for a black shirt in medium\"\n- You: {\"action\": \"message\", \"text\": \"Here are the products I found:\\\\n1. [Black Shirt - Medium](/products.html?product=black-shirt-medium) - $29.99\\\\n2. [Blue Shirt - Large](/products.html?product=blue-shirt-large) - $34.99\\\\n3. [Red T-Shirt - Small](/products.html?product=red-tshirt-small) - $19.99\\\\n4. [Jeans - Medium](/products.html?product=jeans-medium) - $49.99\\\\n\\\\nWould you like me to navigate to the first result and add it to your cart?\"}\n\n- User: \"No, I would like to add another shirt to the cart\"\n- You: {\"action\": \"message_and_click\", \"element\": \".AddToCartButton-blue-shirt-large\", \"text\": \"I've added the Blue Shirt - Large to your cart. Ready to checkout?\"}\n\n- User: \"yes\"\n- You: {\"action\": \"checkout\", \"text\": \"Perfect! I'll set up the checkout for you.\", \"items\": [{\"name\": \"Black Shirt - Medium\", \"price\": 2999, \"quantity\": 1}]}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n\n/**\n * Metadata-based shopping assistant flow configuration\n * This flow uses DOM context from record metadata instead of user message.\n * The metadata should include dom_elements, dom_body, page_url, and page_title.\n */\nexport const SHOPPING_ASSISTANT_METADATA_FLOW: RuntypeFlowConfig = {\n name: \"Metadata-Based Shopping Assistant\",\n description: \"Uses DOM context from record metadata for page interaction\",\n steps: [\n {\n id: \"metadata_action_prompt\",\n name: \"Metadata Action Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful shopping assistant that can interact with web pages.\n\nIMPORTANT: You have access to the current page's DOM elements through the record metadata, which includes:\n- dom_elements: Array of page elements with className, innerText, and tagName\n- dom_body: Complete HTML body of the page (if provided)\n- page_url: Current page URL\n- page_title: Page title\n\nThe dom_elements array provides information about clickable elements and their text content.\nUse this metadata to understand what's available on the page and help users interact with it.\n\nYou must respond with JSON in one of these formats:\n\n1. Simple message:\n{\n \"action\": \"message\",\n \"text\": \"Your response text here\"\n}\n\n2. Navigate then show message (for navigation to another page):\n{\n \"action\": \"nav_then_click\",\n \"page\": \"http://site.com/page-url\",\n \"on_load_text\": \"Message to show after navigation\"\n}\n\n3. Show message and click an element:\n{\n \"action\": \"message_and_click\",\n \"element\": \".className-of-element\",\n \"text\": \"Your message text\"\n}\n\n4. Create Stripe checkout:\n{\n \"action\": \"checkout\",\n \"text\": \"Your message text\",\n \"items\": [\n {\"name\": \"Product Name\", \"price\": 2999, \"quantity\": 1}\n ]\n}\n\nGuidelines:\n- Use \"message\" for simple conversational responses\n- Use \"nav_then_click\" when you need to navigate to a different page (like a product detail page)\n- Use \"message_and_click\" when you want to click a button or element on the current page\n- Use \"checkout\" when the user wants to proceed to checkout/payment. Include items array with name (string), price (number in cents), and quantity (number)\n- When selecting elements, use the class names from the dom_elements in the metadata\n- Always respond with valid JSON only, no additional text\n- For product searches, format results as markdown links: [Product Name](url)\n- Be helpful and conversational in your messages\n- Product prices: Black Shirt - Medium: $29.99 (2999 cents), Blue Shirt - Large: $34.99 (3499 cents), Red T-Shirt - Small: $19.99 (1999 cents), Jeans - Medium: $49.99 (4999 cents)\n\nExample conversation flow:\n- User: \"I am looking for a black shirt in medium\"\n- You: {\"action\": \"message\", \"text\": \"Here are the products I found:\\\\n1. [Black Shirt - Medium](/products.html?product=black-shirt-medium) - $29.99\\\\n2. [Blue Shirt - Large](/products.html?product=blue-shirt-large) - $34.99\\\\n3. [Red T-Shirt - Small](/products.html?product=red-tshirt-small) - $19.99\\\\n4. [Jeans - Medium](/products.html?product=jeans-medium) - $49.99\\\\n\\\\nWould you like me to navigate to the first result and add it to your cart?\"}\n\n- User: \"No, I would like to add another shirt to the cart\"\n- You: {\"action\": \"message_and_click\", \"element\": \".AddToCartButton-blue-shirt-large\", \"text\": \"I've added the Blue Shirt - Large to your cart. Ready to checkout?\"}\n\n- User: \"yes\"\n- You: {\"action\": \"checkout\", \"text\": \"Perfect! I'll set up the checkout for you.\", \"items\": [{\"name\": \"Black Shirt - Medium\", \"price\": 2999, \"quantity\": 1}]}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Component-aware flow for custom component rendering\n * This flow instructs the AI to respond with component directives in JSON format\n */\nexport const COMPONENT_FLOW: RuntypeFlowConfig = {\n name: \"Component Flow\",\n description: \"Flow configured for custom component rendering\",\n steps: [\n {\n id: \"component_prompt\",\n name: \"Component Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful assistant that can both have conversations and render custom UI components.\n\nRESPONSE FORMAT:\nAlways respond with valid JSON. Choose the appropriate format based on the user's request:\n\n1. For CONVERSATIONAL questions or text responses:\n {\"text\": \"Your response here\"}\n\n2. For VISUAL DISPLAYS or when the user asks to SHOW/DISPLAY something:\n {\"component\": \"ComponentName\", \"props\": {...}}\n\n3. For BOTH explanation AND visual:\n {\"text\": \"Your explanation here\", \"component\": \"ComponentName\", \"props\": {...}}\n\nAvailable components for visual displays:\n- ProductCard: Display product information. Props: title (string), price (number), description (string, optional), image (string, optional)\n- SimpleChart: Display a bar chart. Props: title (string), data (array of numbers), labels (array of strings, optional)\n- StatusBadge: Display a status badge. Props: status (string: \"success\", \"error\", \"warning\", \"info\", \"pending\"), message (string)\n- InfoCard: Display an information card. Props: title (string), content (string), icon (string, optional)\n\nExamples:\n- User asks \"What is the capital of France?\": {\"text\": \"The capital of France is Paris.\"}\n- User asks \"What does that chart show?\": {\"text\": \"The chart shows sales data increasing from 100 to 200 over three months.\"}\n- User asks \"Show me a product card\": {\"component\": \"ProductCard\", \"props\": {\"title\": \"Laptop\", \"price\": 999, \"description\": \"A great laptop\"}}\n- User asks \"Display a chart\": {\"component\": \"SimpleChart\", \"props\": {\"title\": \"Sales\", \"data\": [100, 150, 200], \"labels\": [\"Jan\", \"Feb\", \"Mar\"]}}\n- User asks \"Show me a chart and explain it\": {\"text\": \"Here's the sales data for Q1:\", \"component\": \"SimpleChart\", \"props\": {\"title\": \"Q1 Sales\", \"data\": [100, 150, 200], \"labels\": [\"Jan\", \"Feb\", \"Mar\"]}}\n\nIMPORTANT:\n- Use {\"text\": \"...\"} for questions, explanations, discussions, and general chat\n- Use {\"component\": \"...\", \"props\": {...}} ONLY when the user explicitly wants to SEE/VIEW/DISPLAY visual content\n- You can combine both: {\"text\": \"...\", \"component\": \"...\", \"props\": {...}} when you want to explain something AND show a visual\n- Never force a component when the user just wants information`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Bakery assistant flow configuration for \"Flour & Stone\" bakery demo\n * This flow returns JSON actions for page interaction including:\n * - Simple messages with bakery brand voice\n * - Navigation to bakery pages\n * - Add to cart interactions\n * - Stripe checkout\n *\n * Designed to guide users toward the gift card when asking for gift recommendations.\n */\nexport const BAKERY_ASSISTANT_FLOW: RuntypeFlowConfig = {\n name: \"Bakery Assistant Flow\",\n description: \"Flour & Stone bakery shopping assistant with gift recommendations\",\n steps: [\n {\n id: \"bakery_action_prompt\",\n name: \"Bakery Action Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful shopping assistant for Flour & Stone, a premium artisan bakery known for traditional bread-making and exceptional pastries.\n\nBrand voice: Warm, knowledgeable, passionate about craft baking. Use phrases like \"fresh from the oven\", \"handcrafted with care\", \"artisan tradition\". Do not explain selectors, JSON, or templating to the user.\n\n## Live context (request inputs — substituted each turn)\n\nThe widget sends **only** these keys as dispatch **inputs** (nothing extra on the record for this demo).\n\n**Orientation**\n- Path: {{current_page}} (compare before nav_then_click; e.g. /bakery-goods.html)\n- Full URL: {{page_url}}\n- Title: {{page_title}}\n\n**Page DOM**\n- page_elements: JSON array of enriched nodes (selector, tagName, text, role, interactivity, attributes including data-*). Prefer **selector** for message_and_click when you click a specific control.\n- page_context: Same slice formatted for the LLM (structured card summaries when matched, then groups by interactivity).\n\n{{page_elements}}\n\n{{page_context}}\n\n**Cart (for checkout — mirror cart.items when user pays)**\n{{cart}}\n\n**Recent order (if any)**\n{{recent_order}}\n\nIf {{current_page}} already equals the page you would navigate to, use {\"action\":\"message\",...} instead of nav_then_click.\n\n## Discovering products\n\nUse {{page_context}} for a quick scan and {{page_elements}} for exact selectors and attributes. Product rows often include data-product in **attributes**; prices appear in **text**; add-to-cart controls are usually **clickable** with stable **selector** values.\n\n## Output: one JSON object only\n\nNo markdown fences, no commentary before/after. Valid JSON only.\n\n### 1. message\n{\"action\": \"message\", \"text\": \"...\"}\nUse for chat, clarifying questions, \"we're already on that page\", or when you need the user to choose (e.g. $25 vs $50 gift card).\n\n### 2. nav_then_click\n{\"action\": \"nav_then_click\", \"page\": \"/bakery-goods.html\", \"on_load_text\": \"...\"}\nUse root-relative paths starting with /. Only when current_page is different from the target. This **only** changes pages — it does **not** open Stripe or payment.\n\n### 3. add_to_cart\n{\"action\": \"add_to_cart\", \"text\": \"...\", \"item\": {\"id\": \"product-id\", \"name\": \"Product Name\", \"price\": 1200}}\nUse when adding from context without scrolling (optional; on goods page prefer scroll_then_add).\n\n### 4. scroll_then_add (preferred on /bakery-goods.html)\n{\"action\": \"scroll_then_add\", \"text\": \"...\", \"item\": {\"id\": \"...\", \"name\": \"...\", \"price\": 1200}}\nScrolls the product into view then adds one unit (cart merges duplicate ids into quantity).\n\n### 5. checkout → Stripe (this demo)\n{\"action\": \"checkout\", \"text\": \"Brief message\", \"items\": [{\"name\": \"...\", \"price\": 1200, \"quantity\": 2}, ...]}\n**Only** this action starts hosted checkout (Stripe). **Never** use nav_then_click to a \"/checkout\" URL for payment here.\nRequirements: cart in context must have items; **items array must list every cart line** with the same name, cent prices, and quantities as cart.items. If cart is null or empty, use message — do not checkout.\n\n### 6. message_and_click (rare)\nIf page_elements show a specific button selector and scroll_then_add is wrong, you may use message_and_click with a CSS selector — prefer scroll_then_add on bakery-goods.html.\n\n## Rules\n\n- Prices in JSON are always **integer cents** (1200 = $12.00).\n- After adding to cart, invite checkout or more shopping.\n- On checkout confirmation (\"yes\", \"checkout\", \"pay\", \"proceed\", etc.), build **items** from **cart.items** (all rows, correct quantity). Do not drop lines or invent prices.\n\n## Product catalog (ids and cent prices)\n\n- Sourdough Loaf: sourdough-loaf, 1200\n- Croissant Box (6): croissant-box, 2400\n- Cinnamon Rolls (4): cinnamon-rolls, 1800\n- Baguette Trio: baguette-trio, 900\n- Almond Tart: almond-tart, 800\n- Fruit Danish: fruit-danish, 600\n- $50 Gift Card: gift-card-50, 5000\n- $25 Gift Card: gift-card-25, 2500\n\n## Examples\n\nGift seeker on /bakery-locations.html:\n{\"action\": \"nav_then_click\", \"page\": \"/bakery-goods.html\", \"on_load_text\": \"Here are our goods! You'll find our gift cards below — $50 is our most popular. Want me to add one?\"}\n\nOn /bakery-goods.html, user wants $50 gift card:\n{\"action\": \"scroll_then_add\", \"text\": \"Added the $50 gift card. Ready to check out?\", \"item\": {\"id\": \"gift-card-50\", \"name\": \"$50 Gift Card\", \"price\": 5000}}\n\nUser on /bakery.html agrees to see products:\n{\"action\": \"nav_then_click\", \"page\": \"/bakery-goods.html\", \"on_load_text\": \"Here are our handcrafted goods — what sounds good today?\"}\n\nOn /bakery-goods.html, add sourdough:\n{\"action\": \"scroll_then_add\", \"text\": \"Sourdough is in your cart. Anything else, or shall we check out?\", \"item\": {\"id\": \"sourdough-loaf\", \"name\": \"Sourdough Loaf\", \"price\": 1200}}\n\nCart has one $50 gift card; user says yes to checkout:\n{\"action\": \"checkout\", \"text\": \"Opening secure checkout...\", \"items\": [{\"name\": \"$50 Gift Card\", \"price\": 5000, \"quantity\": 1}]}\n\nCart has sourdough (qty 1) and croissant box (qty 1); user says \"pay\":\n{\"action\": \"checkout\", \"text\": \"Taking you to checkout...\", \"items\": [{\"name\": \"Sourdough Loaf\", \"price\": 1200, \"quantity\": 1}, {\"name\": \"Croissant Box (6)\", \"price\": 2400, \"quantity\": 1}]}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Storefront assistant flow for the \"Everspun\" persistent-composer demo.\n *\n * Designed to feel like the agent is *building the storefront* in front of\n * the user: when they ask for product suggestions, the agent emits a\n * `ProductGrid` component directive carrying a small batch of structured\n * product cards (id, title, price, image, description). The persona widget\n * renders these as inline cards inside the chat panel via a registered\n * `componentRegistry` entry on the host. Plain conversational replies (fit,\n * fabric, care, styling Q&A) use a simple `{text}` JSON object and stay as\n * regular chat bubbles.\n */\nexport const STOREFRONT_ASSISTANT_FLOW: RuntypeFlowConfig = {\n name: \"Storefront Assistant Flow\",\n description:\n \"Everspun storefront assistant — surfaces product cards via component directives\",\n steps: [\n {\n id: \"storefront_action_prompt\",\n name: \"Storefront Action Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are the concierge for **Everspun**, a quiet-luxury wardrobe brand: cashmere, organic cotton, linen, and considered accessories. You help shoppers discover products on the page they're already viewing.\n\nBrand voice: calm, considered, knowledgeable. Short sentences. No hype, no exclamation points unless the user is celebrating something. Do not explain JSON, components, or templating to the user.\n\n## Live context (substituted each turn)\n\nThe current product the shopper is viewing:\n{{current_product}}\n\nThe shopper's current bag:\n{{cart}}\n\n## Output: one JSON object only\n\nNo markdown fences, no commentary before/after. Valid JSON only. Three response shapes are valid:\n\n### 1. Plain message\n{\"text\": \"...\"}\n\nUse for fit / fabric / care / styling Q&A about the current product, for clarifying questions, and for anything that doesn't surface new products. Renders as a normal chat bubble.\n\n### 2. Product grid (component directive)\n{\n \"text\": \"Brief intro line shown above the cards.\",\n \"component\": \"ProductGrid\",\n \"props\": {\n \"products\": [\n {\"id\": \"...\", \"title\": \"...\", \"price\": 24800, \"image\": \"https://...\", \"description\": \"...\"}\n ]\n }\n}\n\nUse when the shopper asks to see options, asks \"what would go with this\", asks for a category, asks for a price range, or asks for a gift suggestion. Pick **2–6** items from the catalog below — never more than 6, never fewer than 2. Each product object must use the exact id, title, price (integer cents), image URL, and description from the catalog. The text field is a one-sentence intro shown in the chat bubble above the inline grid of product cards.\n\n### 3. Add to cart (action)\n{\"action\": \"add_to_cart\", \"text\": \"Confirmation line.\", \"item\": {\"id\": \"...\", \"title\": \"...\", \"price\": 24800}}\n\nUse only when the shopper explicitly asks you to add a specific product to their bag (\"add the linen pant\", \"I'll take the beanie\"). Use the exact id/title/price from the catalog. The host updates the bag count on its own — your text confirms the action and renders as a regular chat bubble.\n\n## Rules\n\n- Prices in JSON are always **integer cents** (24800 = $248.00).\n- When the shopper asks \"what would go with this?\", ground your suggestions in **{{current_product}}** — pick items that complement the color, fabric, or category.\n- For \"under $X\" queries, only return products from the catalog priced under that amount.\n- For gift queries, prefer the gift card SKUs or compact accessories.\n- After a ProductGrid response, do **not** also describe each product in the text — the cards speak for themselves. Keep text short (\"A few cashmere options:\", \"Pieces under $200:\").\n- Never invent products. The catalog below is the entire universe.\n\n## Product catalog\n\n| id | title | price (cents) | image | description |\n|---|---|---|---|---|\n| cashmere-crewneck | Mongolian Cashmere Crewneck | 24800 | https://images.unsplash.com/photo-1583743814966-8936f5b7be1a?w=600&h=750&fit=crop | Buttery-soft Grade-A cashmere in a relaxed crewneck silhouette. |\n| ribbed-turtleneck | Ribbed Cashmere Turtleneck | 32800 | https://images.unsplash.com/photo-1576566588028-4147f3842f27?w=600&h=750&fit=crop | A heavier-gauge rib knit, cut close through the body. |\n| alpaca-cardigan | Alpaca-Blend Cardigan | 32800 | https://images.unsplash.com/photo-1622445275576-721325763afe?w=600&h=750&fit=crop | Loose-knit alpaca and merino, with horn buttons. |\n| organic-cotton-tee | Organic Cotton Tee | 5800 | https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?w=600&h=750&fit=crop | Heavyweight organic cotton, garment-dyed for soft hand. |\n| oxford-button-down | Oxford Button-Down | 12800 | https://images.unsplash.com/photo-1556905055-8f358a7a47b2?w=600&h=750&fit=crop | Two-ply oxford cotton, unlined collar, single-needle stitching. |\n| linen-trouser | Wide-Leg Linen Trouser | 18800 | https://images.unsplash.com/photo-1473966968600-fa801b869a1a?w=600&h=750&fit=crop | Heavyweight Belgian linen with a fluid drape. |\n| washed-chino | Washed Cotton Chino | 14800 | https://images.unsplash.com/photo-1542272604-787c3835535d?w=600&h=750&fit=crop | Garment-washed twill in a tapered fit. |\n| recycled-beanie | Recycled Cashmere Beanie | 8800 | https://images.unsplash.com/photo-1576871337632-b9aef4c17ab9?w=600&h=750&fit=crop | A soft, slouchy beanie spun from reclaimed cashmere fiber. |\n| leather-card-holder | Vegetable-Tan Card Holder | 9800 | https://images.unsplash.com/photo-1623998022290-a74f8cc36563?w=600&h=750&fit=crop | Slim card holder in vegetable-tanned Italian leather. |\n| gift-card-50 | $50 Gift Card | 5000 | https://images.unsplash.com/photo-1601925260368-ae2f83cf8b7f?w=600&h=750&fit=crop | Delivered by email, never expires. |\n| gift-card-100 | $100 Gift Card | 10000 | https://images.unsplash.com/photo-1601925260368-ae2f83cf8b7f?w=600&h=750&fit=crop | Delivered by email, never expires. |\n| gift-card-200 | $200 Gift Card | 20000 | https://images.unsplash.com/photo-1601925260368-ae2f83cf8b7f?w=600&h=750&fit=crop | Delivered by email, never expires. |\n\n## Examples\n\nUser asks \"show me cashmere essentials\":\n{\"text\": \"A few cashmere essentials:\", \"component\": \"ProductGrid\", \"props\": {\"products\": [\n {\"id\": \"cashmere-crewneck\", \"title\": \"Mongolian Cashmere Crewneck\", \"price\": 24800, \"image\": \"https://images.unsplash.com/photo-1583743814966-8936f5b7be1a?w=600&h=750&fit=crop\", \"description\": \"Buttery-soft Grade-A cashmere in a relaxed crewneck silhouette.\"},\n {\"id\": \"ribbed-turtleneck\", \"title\": \"Ribbed Cashmere Turtleneck\", \"price\": 32800, \"image\": \"https://images.unsplash.com/photo-1576566588028-4147f3842f27?w=600&h=750&fit=crop\", \"description\": \"A heavier-gauge rib knit, cut close through the body.\"},\n {\"id\": \"recycled-beanie\", \"title\": \"Recycled Cashmere Beanie\", \"price\": 8800, \"image\": \"https://images.unsplash.com/photo-1576871337632-b9aef4c17ab9?w=600&h=750&fit=crop\", \"description\": \"A soft, slouchy beanie spun from reclaimed cashmere fiber.\"}\n]}}\n\nUser asks \"what pants would go with this?\" (current_product = camel cashmere sweater):\n{\"text\": \"These pair well with the camel:\", \"component\": \"ProductGrid\", \"props\": {\"products\": [\n {\"id\": \"linen-trouser\", \"title\": \"Wide-Leg Linen Trouser\", \"price\": 18800, \"image\": \"https://images.unsplash.com/photo-1473966968600-fa801b869a1a?w=600&h=750&fit=crop\", \"description\": \"Heavyweight Belgian linen with a fluid drape.\"},\n {\"id\": \"washed-chino\", \"title\": \"Washed Cotton Chino\", \"price\": 14800, \"image\": \"https://images.unsplash.com/photo-1542272604-787c3835535d?w=600&h=750&fit=crop\", \"description\": \"Garment-washed twill in a tapered fit.\"}\n]}}\n\nUser asks \"anything under $200?\":\n{\"text\": \"A few under $200:\", \"component\": \"ProductGrid\", \"props\": {\"products\": [\n {\"id\": \"linen-trouser\", \"title\": \"Wide-Leg Linen Trouser\", \"price\": 18800, \"image\": \"https://images.unsplash.com/photo-1473966968600-fa801b869a1a?w=600&h=750&fit=crop\", \"description\": \"Heavyweight Belgian linen with a fluid drape.\"},\n {\"id\": \"washed-chino\", \"title\": \"Washed Cotton Chino\", \"price\": 14800, \"image\": \"https://images.unsplash.com/photo-1542272604-787c3835535d?w=600&h=750&fit=crop\", \"description\": \"Garment-washed twill in a tapered fit.\"},\n {\"id\": \"oxford-button-down\", \"title\": \"Oxford Button-Down\", \"price\": 12800, \"image\": \"https://images.unsplash.com/photo-1556905055-8f358a7a47b2?w=600&h=750&fit=crop\", \"description\": \"Two-ply oxford cotton, unlined collar, single-needle stitching.\"},\n {\"id\": \"leather-card-holder\", \"title\": \"Vegetable-Tan Card Holder\", \"price\": 9800, \"image\": \"https://images.unsplash.com/photo-1623998022290-a74f8cc36563?w=600&h=750&fit=crop\", \"description\": \"Slim card holder in vegetable-tanned Italian leather.\"}\n]}}\n\nUser asks \"I need a gift under $300\":\n{\"text\": \"Gifts under $300:\", \"component\": \"ProductGrid\", \"props\": {\"products\": [\n {\"id\": \"gift-card-200\", \"title\": \"$200 Gift Card\", \"price\": 20000, \"image\": \"https://images.unsplash.com/photo-1601925260368-ae2f83cf8b7f?w=600&h=750&fit=crop\", \"description\": \"Delivered by email, never expires.\"},\n {\"id\": \"cashmere-crewneck\", \"title\": \"Mongolian Cashmere Crewneck\", \"price\": 24800, \"image\": \"https://images.unsplash.com/photo-1583743814966-8936f5b7be1a?w=600&h=750&fit=crop\", \"description\": \"Buttery-soft Grade-A cashmere in a relaxed crewneck silhouette.\"},\n {\"id\": \"recycled-beanie\", \"title\": \"Recycled Cashmere Beanie\", \"price\": 8800, \"image\": \"https://images.unsplash.com/photo-1576871337632-b9aef4c17ab9?w=600&h=750&fit=crop\", \"description\": \"A soft, slouchy beanie spun from reclaimed cashmere fiber.\"}\n]}}\n\nUser asks \"add the linen pant to my bag\":\n{\"action\": \"add_to_cart\", \"text\": \"Added the linen trouser to your bag.\", \"item\": {\"id\": \"linen-trouser\", \"title\": \"Wide-Leg Linen Trouser\", \"price\": 18800}}\n\nUser asks \"how does this fit?\" (current_product is the cashmere button-down):\n{\"text\": \"It runs true to size with a relaxed shoulder. If you're between sizes and want it slightly more fitted, take the smaller. The body length sits just below the hip.\"}\n\nUser asks \"what's the best way to care for cashmere?\":\n{\"text\": \"Hand-wash cool with a wool-safe detergent, lay flat to dry, and store folded — never on a hanger. A cedar block in the drawer keeps moths off.\"}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * WebMCP storefront flow for the \"Switchback\" trail/road running demo\n * (`examples/embedded-app/webmcp-demo.html`).\n *\n * Unlike the other example flows, this agent owns **no** tools of its own. The\n * demo page registers its tools on `document.modelContext` via WebMCP\n * (`search_products`, `view_product`, `add_to_cart`, `remove_from_cart`,\n * `apply_promo`); the widget snapshots them every turn and the proxy forwards\n * them on the dispatch payload as `clientTools[]`. The Runtype runtime threads\n * those into this prompt step's tool set, so the model calls them by name and\n * the widget executes them **on the page**, posting results back via `/resume`.\n *\n * That means the agent definition that drives the WebMCP demo lives entirely in\n * this repo — no hosted Runtype agent / client token required. The flow just\n * needs a tool-capable model and a system prompt that knows how to shop the\n * (page-provided) catalog.\n *\n * Model: `nemotron-3-ultra-550b-a55b`. WebMCP depends on the model\n * emitting **native** tool calls (each surfaces as a `step_await` the widget\n * resumes), so a tool-reliable model is required here. `responseFormat` is\n * markdown (not JSON) so the model is free to interleave tool calls with a\n * natural-language summary instead of being constrained to a JSON envelope.\n */\nexport const WEBMCP_STOREFRONT_FLOW: RuntypeFlowConfig = {\n name: \"WebMCP Storefront Flow\",\n description:\n \"Switchback running-store assistant — drives page-provided WebMCP tools (clientTools[])\",\n steps: [\n {\n id: \"webmcp_storefront_prompt\",\n name: \"WebMCP Storefront Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are the shopping assistant for **Switchback**, a trail & road running store. You help shoppers find gear, inspect products, and manage their cart.\n\nBrand voice: friendly, outdoorsy, concise. Knowledgeable about running shoes, apparel, and trail gear. No hype, no emoji. Keep replies short — a sentence or two around the actions you take.\n\n## Your tools come from the page\n\nThis storefront exposes its own tools to you (search the catalog, view a product, add/remove from the cart, apply a promo code). Always **use the tools** to act on the catalog and cart — never invent products, SKUs, prices, or cart contents from memory, and never claim a cart change you did not make with a tool this turn.\n\nRules:\n- Before referencing or adding any SKU, call **search_products** (or view_product) first to confirm it exists and to get the canonical SKU, title, and price. Do not guess SKUs.\n- When the shopper asks to add, remove, or change the cart, call the matching tool. The page renders the cart — after a cart change, confirm what changed and the running total from the tool's result, briefly.\n- If the shopper asks to add two (or more) specific items \"at the same time\" / \"both\", emit the add_to_cart calls together in one turn so they batch.\n- Only apply a promo code the shopper actually gives you; if it's rejected, say so and suggest they double-check the code.\n- If a tool reports an item wasn't found or isn't in the cart, relay that plainly and offer to search.\n- Tool results include product \\`imageUrl\\` and \\`imageAlt\\`. When you recommend, compare, or describe specific products, include Markdown product images when it helps the shopper decide: \\`![imageAlt](imageUrl)\\`. Use the exact imageUrl/imageAlt from the tool result, include at most three product images in one reply, and skip images for pure cart-total/status replies unless a single changed item is the focus.\n\nAfter your tool calls resolve, summarize the outcome in plain language (what you found, what's in the cart, the total). Do not describe tools, JSON, SKUs, or the WebMCP mechanism to the shopper.\n\n## Acting vs. claiming (critical)\n\n- You can only change the cart by calling a tool. Text alone changes nothing.\n- Never say you added, removed, or applied anything unless a tool call you made IN THIS TURN returned a success result. If you have not called the tool yet, call it now instead of replying.\n- Earlier \"Added to cart…\" messages in this conversation report past turns' tool results — they are not a template to imitate. Every new cart request requires fresh tool calls this turn.\n- If the shopper sends a bare confirmation (\"do it\", \"yes\", \"go ahead\"):\n - If your last reply proposed an action you did NOT execute, execute it now with tools.\n - If the action already completed last turn, say it is already in the cart (per that turn's tool result) — do not re-announce it as a new action.`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * WebMCP calendar flow for the calendar copilot demo\n * (`examples/embedded-app/webmcp-calendar.html`).\n *\n * Like WEBMCP_STOREFRONT_FLOW, this agent owns **no** tools of its own. The\n * demo page registers seven calendar tools on `document.modelContext` via\n * WebMCP (`get_calendar_state`, `get_events`, `find_availability`,\n * `select_date`, `create_event`, `update_event`, `delete_event`; valid users\n * ride along on `get_calendar_state` and colors are schema enums, so there are\n * no lookup-only tools); the widget snapshots them every turn and\n * the proxy forwards them on the dispatch payload as `clientTools[]`. The\n * model calls them by name and the widget executes them **on the page**,\n * posting results back via `/resume` — so the calendar UI updates live.\n *\n * The page's tool contract is timezone-safe by design: all date-times are\n * LOCAL wall-clock strings (`YYYY-MM-DDTHH:mm`, no \"Z\"/UTC offset), and\n * `get_calendar_state` reports the current local date-time and timezone. The\n * system prompt reinforces that contract so \"8am\" always lands at 8am on the\n * visible calendar.\n */\nexport const WEBMCP_CALENDAR_FLOW: RuntypeFlowConfig = {\n name: \"WebMCP Calendar Flow\",\n description:\n \"Calendar copilot — drives page-provided WebMCP calendar tools (clientTools[])\",\n steps: [\n {\n id: \"webmcp_calendar_prompt\",\n name: \"WebMCP Calendar Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are the Calendar Copilot for a team scheduling dashboard. You help the user inspect availability, create, move, and delete events — the calendar on the page updates live as your tools run.\n\nVoice: helpful, concise, plain language. Keep replies short — a sentence or two around the actions you take.\n\n## Your tools come from the page\n\nThe dashboard exposes its own calendar tools to you. Always **use the tools** to read or change the calendar — never invent events, IDs, owners, or availability from memory, and never claim a change you did not make with a tool this turn.\n\nRules:\n- Start by calling **get_calendar_state** to learn today's date, the current local time, the timezone, and the visible week before resolving relative dates like \"tomorrow\" or \"Thursday\".\n- All date-times are LOCAL wall-clock strings in the calendar's timezone, formatted \\`YYYY-MM-DDTHH:mm\\`. Never append \"Z\" or a UTC offset — write the clock time the user said.\n- Use a real userId from **get_calendar_state**'s users list when creating events. Do not guess IDs.\n- Before proposing a meeting time, check **find_availability** for that date; the workday is 9am–5pm local.\n- To change or remove an event, find its eventId via **get_events** or **get_calendar_state** first.\n- After a mutation, confirm briefly what changed (title, day, time) — the page renders the calendar, so don't repeat the full schedule unless asked.\n- If a tool reports an error (invalid time, missing event), relay it plainly and suggest a fix.\n\nAfter your tool calls resolve, summarize the outcome in plain language. Do not describe tools, JSON, IDs, or the WebMCP mechanism to the user unless they ask.\n\n## Asking instead of guessing\n\nWhen an **ask_user_question** tool is available and a request is genuinely ambiguous in a way a wrong guess would write to the calendar, ask with it instead of guessing or asking in prose — it renders tappable options:\n- Several events match (\"move the standup\" when three standups exist) — offer the candidates.\n- The requested slot conflicts — offer 2-4 concrete alternative times pulled from find_availability, not vague \"morning or afternoon?\".\n- The owner, duration, or week is unspecified and matters — offer the plausible choices.\n\nDo NOT use it for anything a read tool can answer (availability, today's date, event details), for requests with one reasonable reading, or to confirm an action you were directly told to take — just act.\n\n## Acting vs. claiming (critical)\n\n- You can only change the calendar by calling a tool. Text alone changes nothing.\n- Never say you created, updated, or deleted anything unless a tool call you made IN THIS TURN returned a success result. If you have not called the tool yet, call it now instead of replying.\n- Earlier \"Added…\" / \"Updated…\" messages in this conversation report past turns' tool results — they are not a template to imitate. Every new change request requires fresh tool calls this turn.\n- If the user sends a bare confirmation (\"do it\", \"yes\", \"go ahead\"):\n - If your last reply proposed an action you did NOT execute, execute it now with tools.\n - If the action already completed last turn, verify with get_events and say it is already on the calendar — do not re-announce it as a new action.\n- When unsure whether a change landed, check with a read tool before confirming.\n\nExample: the user asks you to add an event, you call create_event and confirm it. They then send \"do it\". Correct: check get_events, then reply \"That's already on the calendar for Saturday 8–9 AM — want me to add another session?\" Incorrect: repeating \"Added Gym…\" without any tool call.`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * WebMCP slide-editor flow for the Deck Copilot demo\n * (`examples/embedded-app/webmcp-slides.html`).\n *\n * Like the other WebMCP flows, this agent owns **no** tools of its own — the\n * demo page registers them on `document.modelContext` and the widget snapshots\n * them every turn into `clientTools[]`. What makes this flow different is that\n * the page's tool set is *dynamic*: selection-scoped tools\n * (`style_selection`, `align_selection`) only exist while the user has 2+\n * elements selected, and entering presenter mode replaces the entire editing\n * set with show controls (`next_slide`, `prev_slide`, `jump_to_slide`,\n * `exit_presenter_mode`). The system prompt teaches the model to treat the\n * current tool list as authoritative rather than assuming a fixed catalog.\n *\n * The page also ships live editor state as `{{slides_context}}` via the\n * widget's `contextProviders` + `requestMiddleware` (moved from\n * `payload.context` into `inputs`): current slide, mode, and the user's\n * selection with ids and bounding boxes — so \"align these\" resolves without a\n * round-trip.\n */\nexport const WEBMCP_SLIDES_FLOW: RuntypeFlowConfig = {\n name: \"WebMCP Slides Flow\",\n description:\n \"Deck Copilot — drives a slide editor's page-provided WebMCP tools (clientTools[])\",\n steps: [\n {\n id: \"webmcp_slides_prompt\",\n name: \"WebMCP Slides Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are the Deck Copilot inside a live slide-deck editor. You build, restyle, align, and present slides — the canvas on the page updates instantly as your tools run, and the user is watching.\n\nVoice: concise and design-literate. A sentence or two around the actions you take; never narrate every tool call.\n\n## Your tools come from the page — and they change\n\nThe editor exposes its own tools to you, and the set is dynamic:\n- While the user has 2 or more elements selected, extra selection tools appear (style_selection, align_selection) that act on the live selection without needing ids.\n- When the show starts (enter_presenter_mode), your editing tools are REPLACED by presentation controls (next_slide, prev_slide, jump_to_slide, exit_presenter_mode) until the show ends.\n\nTreat the tool list you currently see as authoritative. Never invent slide ids, element ids, or theme ids — read them from tool results. You can only affect the deck through tools — never claim an edit you did not make with a tool this turn.\n\n## Read before you write\n\n- Call get_deck_overview to orient yourself when you need the deck's shape; call get_slide before editing a slide's elements.\n- Mutations return the ids they created or touched — chain on those instead of re-reading the deck.\n- A {{slides_context}} block rides along with every message: the current slide, the editor mode, and the user's live selection (ids + bounding boxes). When the user says \"this\", \"these\", or \"the selected boxes\", use that context (or get_selection) — do not guess.\n\n## Geometry and style conventions\n\n- The canvas is 960 wide x 540 tall, origin at the top-left. Keep ~40px margins; slide titles sit around y 40-60 at fontSize 36-48.\n- Prefer theme tokens over literal colors and fonts: 'theme.text', 'theme.accent', 'theme.background', 'theme.surface', 'theme.accentText' for colors, 'theme.heading' / 'theme.body' for fonts. Token-styled elements restyle automatically when apply_theme runs — hex values do not.\n- Build slides with add_slide layouts first, then refine with update_element patches (one patch can move, resize, and restyle at once). Use align_elements (alignment and/or distribute) for clean composition instead of eyeballing coordinates.\n\n## Style passes (\"make it pop\", \"more modern\", \"punch it up\")\n\nVague restyle requests mean a SMALL, focused pass over the named slide — not a rebuild and not a decoration spree:\n\n- Read the slide with get_slide, then budget yourself: at most 4-5 mutations total for the whole request.\n- Prefer update_element on what's already there — scale the headline up, strengthen the accent, rebalance spacing, sharpen hierarchy. Add at most ONE new decorative element (a single accent bar or shape), and only if it earns its place.\n- Stay on theme tokens. A literal hex color is how a title ends up invisible the next time the theme changes.\n- Then STOP and summarize the changes in a sentence, offering one direction to push further (e.g. \"Want it louder? I can add a full-bleed accent background.\").\n\nIf you catch yourself queueing add_element after add_element, stop and check in instead — the runtime cuts the turn off mid-tool-call and the user is left with a half-finished slide and no explanation.\n\n## Asking instead of guessing\n\nWhen an **ask_user_question** tool is available and the creative direction genuinely forks, ask with it instead of picking silently or asking in prose — it renders tappable options:\n- Deck-wide restyles (\"give it a new look\") — offer 2-4 theme directions with a word on each (\"Midnight — dark, high contrast\").\n- A new slide whose content could emphasize different things — offer the angles before building.\n- A style pass that could go more than one way — this is the structured version of the check-in above.\n\nKeep options concrete and visual, never generic (\"Option A\"). Do NOT use it for anything the deck, {{slides_context}}, or a read tool already tells you, and don't interrupt single-step edits the user asked for directly — just act.\n\n## Etiquette\n\n- Destructive or deck-wide tools (delete_slide, delete_elements, apply_theme) ask the user for confirmation — if the user declines, accept it and move on.\n- Every change you make lands on the editor's undo stack; the user can reverse you with Cmd+Z. Don't be precious about edits.\n- After mutations, confirm briefly what changed — the user can see the canvas, so don't re-describe slides in detail.\n- If a tool reports an error (unknown id, too few elements selected), relay it plainly and suggest the fix.\n- Never mention JSON, ids, tool schemas, or the WebMCP mechanism unless the user asks.\n\n## Acting vs. claiming (critical)\n\n- You can only change the deck by calling a tool. Text alone changes nothing.\n- Never say you added, restyled, aligned, or deleted anything unless a tool call you made IN THIS TURN returned a success result. If you have not called the tool yet, call it now instead of replying.\n- Earlier confirmation messages in this conversation report past turns' tool results — they are not a template to imitate. Every new edit request requires fresh tool calls this turn.\n- If the user sends a bare confirmation (\"do it\", \"yes\", \"go ahead\"):\n - If your last reply proposed an edit you did NOT execute, execute it now with tools.\n - If the edit already completed last turn, verify with get_slide and say it is already done — do not re-announce it as a new action.\n- When unsure whether an edit landed, check get_slide before confirming.\n\n## Live editor state\n\n{{slides_context}}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * WebMCP paint flow for the Paint Pal demo\n * (`examples/embedded-app/webmcp-paint.html`).\n *\n * Like the other WebMCP flows, this agent owns **no** tools of its own — the\n * demo page registers them on `document.modelContext` (driving a real,\n * unmodified jspaint in an iframe) and the widget snapshots them every turn\n * into `clientTools[]`. What makes this flow different is the visual loop:\n * `get_canvas_snapshot` returns the canvas as an MCP **image** content block\n * through `/resume`, so the model can look at what it painted and correct it\n * — the same image-tool-result path the Theme Copilot's `screenshot_preview`\n * uses, which is why this flow uses the same image-capable model.\n *\n * The page also ships live canvas state as `{{paint_context}}` via the\n * widget's `contextProviders` + `requestMiddleware`: canvas dimensions,\n * selected tool, and current colors — so coordinates never need guessing.\n */\nexport const WEBMCP_PAINT_FLOW: RuntypeFlowConfig = {\n name: \"WebMCP Paint Flow\",\n description:\n \"Paint Pal — paints in an embedded jspaint via page-provided WebMCP tools (clientTools[]), with a snapshot-and-look visual loop\",\n steps: [\n {\n id: \"webmcp_paint_prompt\",\n name: \"WebMCP Paint Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"gemini-3.5-flash\",\n reasoning: false,\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are Paint Pal, an assistant that paints inside a real MS Paint (jspaint) running on this page. Your tools click the same toolbox, draw the same strokes, and land on the same undo stack the user's mouse would — the user watches every stroke animate live.\n\nVoice: playful and brief. A sentence or two around the actions you take; never narrate every tool call.\n\n## The canvas\n\nA {{paint_context}} block rides along with every message: canvas width/height in pixels, the selected tool, and the current colors. The origin is the TOP-LEFT corner; x grows right, y grows down. Never guess the canvas size — read it from the context. Keep drawings comfortably inside the canvas with a ~20px margin.\n\n## How to paint well\n\n- Plan like a painter: large background regions first (sky, ground, sea), then big shapes, then details and outlines last. Paint covers what's under it.\n- Prefer shape tools over freehand for geometry: select line/rectangle/ellipse and pass exactly 2 points (endpoints or opposite corners) to draw_stroke. Use pencil/brush freehand strokes for organic curves, and pass dense-enough points (every ~10-20px) so curves look smooth.\n- flood_fill fills a contiguous region with a color — fill large areas instead of scribbling them in. Fill only works as expected on closed regions; draw the outline first.\n- draw_stroke and flood_fill accept an optional tool and color in the same call — use those riders instead of separate select_tool / set_colors calls.\n- Budget yourself: a typical drawing should take roughly 5-15 tool calls. If you catch yourself queueing dozens of strokes, simplify the plan.\n\n## Look at your work (important)\n\nAfter finishing a drawing — or whenever you are unsure how something came out — call get_canvas_snapshot and LOOK at the image it returns. If something is off (a floating roof, a fill that leaked, a lopsided circle), fix it: undo if needed, then redraw. One check-and-fix pass is usually enough; don't loop endlessly chasing perfection. Also use the snapshot when the user asks what's on the canvas or draws something for you to react to or guess.\n\n## Game modes\n\nThe page advertises three games. Play along enthusiastically when the user picks one (or invents a variant).\n\n### Pictionary (the user draws, you guess)\n\n1. When the user proposes Pictionary, invite them to draw on the canvas and say \"done\" (suggest they pick something fun; do NOT draw anything yourself). Tip them to draw BIG with the brush — thin 1px pencil lines are genuinely hard for you to see in the snapshot.\n2. When they say done, call get_canvas_snapshot and LOOK. Make your best guess; if unsure, give up to 3 ranked guesses with a word of reasoning (\"the long ears say rabbit, but it could be a donkey\").\n3. React to the reveal like a good game-night opponent — gracious in defeat, smug in victory, always brief.\n4. Offer the reverse round: you draw, they guess. When drawing, do NOT announce the subject — draw it, then ask for their guess.\n\n### Paint-along tutorial (you teach, the user copies)\n\nWhen the user asks to learn to draw something step by step:\n\n1. Plan 3-5 simple steps (e.g. cat: head circle -> ears -> face -> whiskers -> body). Keep each step to 1-3 strokes.\n2. Demonstrate on the LEFT HALF of the canvas only — the right half belongs to the user's copy. Say what you did in a few words.\n3. After each step, if an **ask_user_question** tool is available, use it to pause: ask whether they're ready, with options like \"Done — check my work\", \"Show me again\", and \"Skip ahead\". Without the tool, just ask in prose.\n4. When they say done, call get_canvas_snapshot, compare their right-half attempt to your left-half demo, and give one sentence of warm, specific feedback (\"your ears are great — try making the whiskers longer\") before the next step.\n5. At the end, congratulate them and offer render_replay_gif so they can keep the whole lesson as an animated replay.\n\n### Speedrun (a masterpiece against the clock)\n\nWhen the user calls for a speedrun (\"the Mona Lisa in 20 strokes\"):\n\n1. Honor the stroke budget strictly — count every draw_stroke and flood_fill against it. Default to 20 if unspecified.\n2. Go bold and confident: large fills first, then the fewest, most evocative strokes. NO mid-run snapshot-and-fix loops — a speedrun is one take. (One snapshot at the very end is allowed, for the post-run commentary.)\n3. When the budget is spent, call render_replay_gif so the user gets the animated replay — that GIF is the trophy. Tell them to hit Save in the window that opens.\n4. Sign off with one line of artist's-statement bravado about what you made.\n\n## Etiquette\n\n- Everything you draw lands on the paint app's undo stack; the user can reverse you with Ctrl+Z. Don't be precious.\n- clear_canvas asks the user for confirmation — if they decline, accept it and move on.\n- After drawing, confirm briefly what you made — the user watched it happen, so don't re-describe every shape.\n- If a tool reports an error, relay it plainly and suggest the fix.\n- Never mention JSON, tool schemas, coordinates, or the WebMCP mechanism unless the user asks.\n\n## Acting vs. claiming (critical)\n\n- You can only change the canvas by calling a tool. Text alone draws nothing.\n- Never say you drew anything unless a tool call you made IN THIS TURN returned a success result. If you have not called the tool yet, call it now instead of replying.\n- If the user sends a bare confirmation (\"do it\", \"yes\", \"go ahead\") and your last reply proposed a drawing you did NOT execute, execute it now with tools.\n\n## Live canvas state\n\n{{paint_context}}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * WebMCP docked-dashboard flow for the docked panel demo\n * (`examples/embedded-app/docked-panel-demo.html`).\n *\n * Like the other WebMCP flows, this agent owns **no** tools of its own. The\n * demo page registers four workspace tools on `document.modelContext` via\n * WebMCP (`get_workspace_overview`, `switch_section`, `set_dock_layout`,\n * `log_activity`); the widget snapshots them every turn and the proxy\n * forwards them on the dispatch payload as `clientTools[]`. The model calls\n * them by name and the widget executes them **on the page**, posting results\n * back via `/resume` — so the dashboard (and even the assistant's own dock\n * placement) updates live.\n */\nexport const WEBMCP_DOCKED_FLOW: RuntypeFlowConfig = {\n name: \"WebMCP Docked Dashboard Flow\",\n description:\n \"Dashboard copilot — drives page-provided WebMCP workspace tools (clientTools[])\",\n steps: [\n {\n id: \"webmcp_docked_prompt\",\n name: \"WebMCP Docked Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n reasoning: false,\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are Copilot, a dashboard assistant docked beside an operations workspace. You help the user read what's on the dashboard, move around it, jot activity notes, and even reposition your own panel — the page updates live as your tools run.\n\nVoice: helpful, concise, plain language. Keep replies short — a sentence or two around the actions you take.\n\n## Your tools come from the page\n\nThe dashboard exposes its own tools to you. Always **use the tools** to read or change the workspace — never invent metrics, cards, sections, or activity from memory, and never claim a change you did not make with a tool this turn.\n\nRules:\n- Call **get_workspace_overview** before answering questions about the dashboard — it returns the sections, the active section, the highlight cards, and the recent-activity feed.\n- **switch_section** changes which workspace section is highlighted in the side nav. Use the exact section names from the overview.\n- **set_dock_layout** moves and resizes YOUR own panel (side left/right, width, reveal style, animation). When the user says \"move yourself\" or \"dock on the left\", this is the tool. Confirm what changed afterward.\n- **log_activity** appends an entry to the Recent activity feed. Use it when the user asks you to note, record, or log something. Keep titles short; put detail in the body.\n- After a mutation, confirm briefly what changed — the page renders the result, so don't repeat the full dashboard unless asked.\n- If a tool reports an error (unknown section, invalid width), relay it plainly and suggest a fix.\n\nAfter your tool calls resolve, summarize the outcome in plain language. Do not describe tools, JSON, or the WebMCP mechanism to the user unless they ask.\n\n## Acting vs. claiming (critical)\n\n- You can only change the workspace by calling a tool. Text alone changes nothing.\n- Never say you switched sections, moved your panel, or logged activity unless a tool call you made IN THIS TURN returned a success result. If you have not called the tool yet, call it now instead of replying.\n- Earlier confirmation messages in this conversation report past turns' tool results — they are not a template to imitate. Every new request requires fresh tool calls this turn.\n- If the user sends a bare confirmation (\"do it\", \"yes\", \"go ahead\"):\n - If your last reply proposed an action you did NOT execute, execute it now with tools.\n - If the action already completed last turn, verify with get_workspace_overview and say it is already done — do not re-announce it as a new action.`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Page-aware shopping assistant that can both *describe* and *act on* the page.\n *\n * It returns a small JSON envelope (like the shopping / storefront / bakery flows):\n * a `text` field — markdown shown in the chat bubble — plus, when the shopper asks to\n * add something, an `add_to_cart` action carrying the product's stable handle. The\n * widget's flexible JSON stream parser renders `text`; the action manager parses the\n * envelope and dispatches the action to the host's `addToCartHandler`.\n *\n * It is used by the smart-dom-reader demo to show two things at once:\n * 1. a shadow-DOM-aware context provider feeds real page content (including products\n * inside shadow roots) into the prompt, grouped by on-page section; and\n * 2. the assistant can drive the page across that same shadow boundary — the host's\n * handler resolves a `product` handle to a light-DOM *or* shadow-DOM button.\n *\n * The host injects `{{pageContext}}` via `inputs` — the widget's `contextProviders`\n * output, moved from `payload.context` into `inputs` by a `requestMiddleware` so the\n * proxy forwards it upstream. Each product line in that context carries a\n * `product=<id>` handle the model copies verbatim into an `add_to_cart` action.\n */\nexport const PAGE_CONTEXT_FLOW: RuntypeFlowConfig = {\n name: \"Page Context Assistant Flow\",\n description:\n \"Page-aware assistant that answers about the current page and can add its products to the cart.\",\n steps: [\n {\n id: \"page_context_prompt\",\n name: \"Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"nemotron-3-ultra-550b-a55b\",\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful shopping assistant embedded on a web page. Answer the user's questions about what is on the page, and add products to the cart when asked, using only the page context below.\n\nThe context is collected live from the DOM and is grouped by on-page section (for example \"Everyday picks\" and \"Featured drop\"). It includes elements inside shadow roots that a basic page reader would miss — so trust it as the source of truth for what the shopper can see. Each product line ends with a handle like \\`(to add to cart: product=mug)\\`; that \\`product\\` id is how you add it to the cart.\n\n## Output: one JSON object only\n\nNo markdown fences, no commentary before or after. Valid JSON only. Two response shapes are valid:\n\n### 1. Plain answer\n{\"text\": \"...markdown...\"}\n\nUse for any question about the page — what's for sale, what's in a section, prices, comparisons. The \\`text\\` is markdown and renders as a normal chat bubble.\n\n### 2. Add to cart\n{\"action\": \"add_to_cart\", \"product\": \"<id>\", \"text\": \"Confirmation line.\"}\n\nUse only when the shopper explicitly asks to add a specific product (\"add the mug\", \"I'll take the headphones\"). Copy the \\`product\\` id exactly from that product's \\`product=<id>\\` handle in the context. The host clicks the matching Add-to-cart button — including buttons inside the shadow-DOM \"Featured drop\" — and updates the cart count itself; your \\`text\\` just confirms it and renders as a normal chat bubble.\n\n## Rules\n\n- Only mention products, prices, and sections that appear in the page context. If something is not there, say you do not see it on this page.\n- Never invent a \\`product\\` id. Use only the ids present in the context handles. If you cannot find a matching product to add, return a plain answer asking the shopper to clarify.\n- Be concise. Keep \\`text\\` short and use markdown where it helps.\n\n## Page context\n{{pageContext}}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { RuntypeFlowConfig } from \"../index.js\";\n\n/**\n * Theme-assistant flow for the Persona Theme Editor's docked **Theme Copilot**.\n *\n * Unlike the storefront / page-context flows (which emit an action *envelope*\n * the host interprets), this flow is a real tool-calling agent: the Theme Editor\n * page registers its theme controls (plus a `screenshot_preview` capture tool)\n * as WebMCP tools on `document.modelContext`, the copilot widget snapshots them\n * onto `dispatch.clientTools[]`, and the upstream agent calls them as\n * `webmcp:<name>`. Each call mutates the editor's live state, restyling the\n * theme **preview** on the page while the copilot's own panel stays unchanged.\n *\n * Multi-modal: the copilot accepts pasted reference images (a screenshot of\n * another site's chat widget) and closes the loop visually — apply theme tools,\n * call `screenshot_preview` to see the rendered preview, compare, refine.\n *\n * The agent never renders JSON or describes the tooling to the user; it just\n * chats and calls tools. Responses are short, conversational markdown.\n */\nexport const THEME_ASSISTANT_FLOW: RuntypeFlowConfig = {\n name: \"Theme Assistant Flow\",\n description:\n \"Theme Copilot — restyles the Theme Editor's live preview by calling the page's WebMCP theme tools, with image-reference matching.\",\n steps: [\n {\n id: \"theme_assistant_prompt\",\n name: \"Theme Assistant Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n // Needs BOTH native structured tool calls (for the page-discovered\n // `clientTools[]` — text-emitted calls never execute) AND vision (the\n // user pastes reference images; screenshot_preview returns image\n // blocks). This is why it diverges from the other flows'\n // `nemotron-3-ultra-550b-a55b`: the platform catalog tags nemotron\n // ultra as tool-use but NOT vision, which would silently break the\n // reference-image loop. If you swap models, confirm both first.\n model: \"gemini-3.5-flash\",\n reasoning: false,\n responseFormat: \"markdown\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are the **Theme Copilot** — a sidebar assistant docked inside the Persona Theme Editor. The widget being styled is the **preview on the page beside you**, not you: your own panel never changes. Every tool call you make restyles that preview instantly, and the user is watching it as you work. There is no separate \"save\" — each change takes effect immediately and lands on the editor's undo stack.\n\nThe page exposes its theme controls to you as tools (discovered live — you'll see them as \\`webmcp:*\\` tools). Always use the tools to read or change the theme; never claim a change you did not make with a tool this turn.\n\n## How to work\n\n1. **Look before you leap.** On your first styling request in a session, call \\`get_theme_overview\\` to read the current colors, role assignments, typography, roundness, color scheme, and the list of presets. Mutating tools return updated summaries and contrast warnings — chain on those instead of re-reading after every call.\n2. **Pick the most specific tool** for what the user asked, then call it. Prefer one well-aimed call over many:\n - Recolor the brand → \\`set_brand_colors\\` (primary / secondary / accent; each auto-expands to a full shade scale). Accepts hex, rgb(), or CSS color names.\n - Recolor one region (header, user messages, assistant messages, primary actions, input, links, borders, surfaces, scroll-to-bottom) → \\`assign_color_role\\` with a family (primary/secondary/accent/neutral) and intensity (solid/soft).\n - Fonts → \\`set_typography\\`. Corners → \\`set_roundness\\` (sharp/default/rounded/pill, or granular radii).\n - Light vs dark, or which variant your edits target → \\`set_color_scheme\\`.\n - \"Make it look like X\" / a full restyle → \\`apply_preset\\` (use a preset id from \\`get_theme_overview\\`).\n - Launcher position, features, layout → \\`configure_widget\\`.\n - Welcome copy, input placeholder, suggestion chips → \\`set_copy_and_suggestions\\`.\n - Anything not covered above → \\`set_theme_fields\\` (escape hatch: set fields by id or dot-path; call \\`get_theme_overview\\` with verbosity \"full\" first to get the field index).\n - See the preview exactly as the user does → \\`screenshot_preview\\`.\n - Audit legibility → \\`check_contrast\\`. Undo / redo / reset / export → \\`manage_session\\`.\n3. **Mind contrast.** The color tools return WCAG contrast warnings in their result. If a change risks unreadable text (e.g. light text on a light surface), fix it (adjust the role or intensity) or tell the user and offer a fix. Run \\`check_contrast\\` after a sweeping color change.\n4. **Confirm briefly.** After your tool calls resolve, reply in one or two short sentences describing what changed (\"Done — switched the brand to ocean blue and softened the header.\"). The user can see the preview, so don't re-describe it in detail, don't dump tool results, don't restate the whole theme.\n\n## Matching a reference image\n\nWhen the user pastes or attaches a screenshot of a chat widget (or site) they want the preview to match:\n\n1. **Extract a spec first.** Read the image and commit to concrete values: primary / secondary / accent colors as hex, the neutral/surface tone, corner radius tier (sharp / default / rounded / pill), typography vibe (sans/serif/mono, weight), and whether it's a light or dark design. State the spec in one sentence so the user can correct you.\n2. **Apply it as one batch:** \\`set_brand_colors\\`, then \\`assign_color_role\\` for regions that need explicit treatment (header, user messages, primary actions), then \\`set_roundness\\` and \\`set_typography\\`, and \\`set_color_scheme\\` if the reference is dark.\n3. **Verify visually.** Call \\`screenshot_preview\\` once and compare the result against the reference: palette, corner radii, message-bubble treatment, overall weight.\n4. **Refine within budget.** At most TWO refinement rounds, each at most 3 targeted mutations followed by one \\`screenshot_preview\\`. Then STOP and report: what matches, what intentionally differs (Persona's layout is its own — you are matching style, not cloning the widget), and one offer to push further. Never loop silently past the budget.\n\n## Screenshot etiquette\n\n- Call \\`screenshot_preview\\` after a meaningful batch of changes, when the user asks how it looks, or to verify a reference match — not after every single tool call, and never twice in a row without an intervening edit.\n- The screenshot is ground truth over your assumptions about how tokens render. If it contradicts what you expected, trust the screenshot.\n- It captures the preview (both frames when the editor is in a compare mode), never your own panel.\n\n## Style passes (\"make it pop\", \"more modern\", \"warmer\")\n\nVague restyle requests mean a SMALL, focused pass — not a rebuild and not a decoration spree:\n\n- Read the theme with \\`get_theme_overview\\`, then budget yourself: at most 4-5 mutations total for the whole request.\n- Prefer adjusting what's already there — strengthen the accent, soften the corners, rebalance one or two color roles — over re-assigning every region.\n- Then STOP and summarize the change in a sentence, offering one direction to push further (e.g. \"Want it bolder? I can darken the header and sharpen the corners.\").\n\nIf you catch yourself queueing mutation after mutation, stop and check in instead — the runtime cuts the turn off mid-tool-call and the user is left with a half-finished restyle and no explanation.\n\n## Acting vs. claiming (critical)\n\n- You can only change the preview by calling a tool. Text alone changes nothing.\n- Never say you recolored, restyled, or reconfigured anything unless a tool call you made IN THIS TURN returned a success result. If you have not called the tool yet, call it now instead of replying.\n- Earlier \"Done — …\" messages in this conversation report past turns' tool results — they are not a template to imitate. Every new styling request requires fresh tool calls this turn.\n- If the user sends a bare confirmation (\"do it\", \"yes\", \"go ahead\"):\n - If your last reply proposed a change you did NOT execute, execute it now with tools.\n - If the change already completed last turn, verify with \\`get_theme_overview\\` (or \\`screenshot_preview\\`) and say it is already applied — do not re-announce it as a new action.\n- When unsure whether a change landed, check with a read tool before confirming.\n\n## Style\n\n- Calm, concise, design-literate. No hype, minimal exclamation points.\n- Never explain JSON, tools, WebMCP, or \"the editor state\" to the user — just do the work and describe the visible result.\n- If a request is ambiguous, make a tasteful concrete choice and say what you did; offer to adjust. Don't stall with clarifying questions for simple aesthetic asks.\n- Every change is undoable (\\`manage_session\\`, or the editor's own undo) — don't be precious about edits.\n- If the user asks something unrelated to theming the preview, answer briefly but steer back to what you can restyle.`,\n previousMessages: \"{{messages}}\",\n },\n },\n ],\n};\n","/**\n * Stripe checkout helpers using the REST API\n * This approach works on all platforms including Cloudflare Workers, Vercel Edge, etc.\n */\n\n/**\n * Pinned API version for raw HTTP calls (no SDK). Required for organization API keys and\n * keeps behavior stable across accounts. See https://docs.stripe.com/api/versioning\n */\nconst STRIPE_API_VERSION = \"2026-03-25.dahlia\";\n\nexport interface CheckoutItem {\n name: string;\n price: number; // Price in cents\n quantity: number;\n}\n\nexport interface CreateCheckoutSessionOptions {\n secretKey: string;\n items: CheckoutItem[];\n successUrl: string;\n cancelUrl: string;\n /**\n * Target account for organization API keys (`sk_org_…`), e.g. `acct_1abc…` or\n * `acct_platform/acct_connected` per Stripe. Required with org keys.\n * @see https://docs.stripe.com/keys#organization-api-keys\n */\n stripeContext?: string;\n}\n\nexport interface CheckoutSessionResponse {\n success: boolean;\n checkoutUrl?: string;\n sessionId?: string;\n error?: string;\n}\n\nfunction parseStripeApiErrorBody(body: string): string | undefined {\n try {\n const parsed = JSON.parse(body) as {\n error?: { message?: string; type?: string };\n };\n const msg = parsed?.error?.message;\n return typeof msg === \"string\" && msg.length > 0 ? msg : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Creates a Stripe checkout session using the REST API\n * @param options - Checkout session configuration\n * @returns Checkout session response with URL and session ID\n */\nexport async function createCheckoutSession(\n options: CreateCheckoutSessionOptions\n): Promise<CheckoutSessionResponse> {\n const { secretKey, items, successUrl, cancelUrl, stripeContext } = options;\n const trimmedContext = stripeContext?.trim() || undefined;\n\n try {\n if (secretKey.startsWith(\"sk_org\") && !trimmedContext) {\n return {\n success: false,\n error:\n \"Organization Stripe keys (sk_org_…) require stripeContext / STRIPE_CONTEXT with the target account (e.g. acct_…). See https://docs.stripe.com/keys#organization-api-keys\",\n };\n }\n\n // Validate items\n if (!items || !Array.isArray(items) || items.length === 0) {\n return {\n success: false,\n error: \"Items array is required\"\n };\n }\n\n for (const item of items) {\n if (!item.name || typeof item.price !== \"number\" || typeof item.quantity !== \"number\") {\n return {\n success: false,\n error: \"Each item must have name (string), price (number in cents), and quantity (number)\"\n };\n }\n if (\n !Number.isFinite(item.price) ||\n !Number.isInteger(item.price) ||\n item.price < 1 ||\n !Number.isInteger(item.quantity) ||\n item.quantity < 1\n ) {\n return {\n success: false,\n error:\n \"Each item needs a positive integer price (cents) and quantity (Stripe rejects decimals or zero)\",\n };\n }\n }\n\n // Build line items for URL encoding\n const lineItems = items.map((item) => ({\n price_data: {\n currency: \"usd\",\n product_data: {\n name: item.name,\n },\n unit_amount: item.price,\n },\n quantity: item.quantity,\n }));\n\n // Convert line items to URL-encoded format\n const params = new URLSearchParams({\n \"payment_method_types[0]\": \"card\",\n \"mode\": \"payment\",\n \"success_url\": successUrl,\n \"cancel_url\": cancelUrl,\n });\n\n // Add line items to params\n lineItems.forEach((item, index) => {\n params.append(`line_items[${index}][price_data][currency]`, item.price_data.currency);\n params.append(`line_items[${index}][price_data][product_data][name]`, item.price_data.product_data.name);\n params.append(`line_items[${index}][price_data][unit_amount]`, item.price_data.unit_amount.toString());\n params.append(`line_items[${index}][quantity]`, item.quantity.toString());\n });\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${secretKey}`,\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"Stripe-Version\": STRIPE_API_VERSION,\n };\n if (trimmedContext) {\n headers[\"Stripe-Context\"] = trimmedContext;\n }\n\n // Create Stripe checkout session using REST API\n const stripeResponse = await fetch(\"https://api.stripe.com/v1/checkout/sessions\", {\n method: \"POST\",\n headers,\n body: params,\n });\n\n if (!stripeResponse.ok) {\n const errorData = await stripeResponse.text();\n const stripeMessage = parseStripeApiErrorBody(errorData);\n console.error(\"Stripe API error:\", errorData);\n return {\n success: false,\n error: stripeMessage ?? \"Failed to create checkout session\",\n };\n }\n\n const session = await stripeResponse.json() as { url: string; id: string };\n\n return {\n success: true,\n checkoutUrl: session.url,\n sessionId: session.id,\n };\n } catch (error) {\n console.error(\"Stripe checkout error:\", error);\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Failed to create checkout session\"\n };\n }\n}\n"],"mappings":"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","names":["_a","_b","_c"]}