@runtypelabs/persona-proxy 1.43.2 → 2.3.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.
package/dist/index.d.ts CHANGED
@@ -45,7 +45,7 @@ declare const COMPONENT_FLOW: RuntypeFlowConfig;
45
45
  *
46
46
  * Designed to guide users toward the gift card when asking for gift recommendations.
47
47
  */
48
- declare const BAKERY_ASSISTANT_FLOW: TravrseFlowConfig;
48
+ declare const BAKERY_ASSISTANT_FLOW: RuntypeFlowConfig;
49
49
 
50
50
  /**
51
51
  * Stripe checkout helpers using the REST API
@@ -87,10 +87,6 @@ type RuntypeFlowConfig = {
87
87
  description: string;
88
88
  steps: RuntypeFlowStep[];
89
89
  };
90
- /** @deprecated Use RuntypeFlowStep instead */
91
- type TravrseFlowStep = RuntypeFlowStep;
92
- /** @deprecated Use RuntypeFlowConfig instead */
93
- type TravrseFlowConfig = RuntypeFlowConfig;
94
90
  /**
95
91
  * Payload for message feedback (upvote/downvote)
96
92
  */
@@ -133,4 +129,4 @@ type ChatProxyOptions = {
133
129
  declare const createChatProxyApp: (options?: ChatProxyOptions) => Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
134
130
  declare const createVercelHandler: (options?: ChatProxyOptions) => (req: Request) => Response | Promise<Response>;
135
131
 
136
- export { BAKERY_ASSISTANT_FLOW, COMPONENT_FLOW, CONVERSATIONAL_FLOW, type ChatProxyOptions, type CheckoutItem, type CheckoutSessionResponse, type CreateCheckoutSessionOptions, FORM_DIRECTIVE_FLOW, type FeedbackHandler, type FeedbackPayload, type RuntypeFlowConfig, type RuntypeFlowStep, SHOPPING_ASSISTANT_FLOW, SHOPPING_ASSISTANT_METADATA_FLOW, type TravrseFlowConfig, type TravrseFlowStep, createChatProxyApp, createCheckoutSession, createVercelHandler, createChatProxyApp as default };
132
+ export { BAKERY_ASSISTANT_FLOW, COMPONENT_FLOW, CONVERSATIONAL_FLOW, type ChatProxyOptions, type CheckoutItem, type CheckoutSessionResponse, type CreateCheckoutSessionOptions, FORM_DIRECTIVE_FLOW, type FeedbackHandler, type FeedbackPayload, type RuntypeFlowConfig, type RuntypeFlowStep, SHOPPING_ASSISTANT_FLOW, SHOPPING_ASSISTANT_METADATA_FLOW, createChatProxyApp, createCheckoutSession, createVercelHandler, createChatProxyApp as default };
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ var CONVERSATIONAL_FLOW = {
13
13
  type: "prompt",
14
14
  enabled: true,
15
15
  config: {
16
- model: "meta/llama3.1-8b-instruct-free",
16
+ model: "mercury-2",
17
17
  responseFormat: "markdown",
18
18
  outputVariable: "prompt_result",
19
19
  userPrompt: "{{user_message}}",
@@ -35,7 +35,7 @@ var FORM_DIRECTIVE_FLOW = {
35
35
  type: "prompt",
36
36
  enabled: true,
37
37
  config: {
38
- model: "qwen/qwen3-8b",
38
+ model: "mercury-2",
39
39
  reasoning: false,
40
40
  responseFormat: "JSON",
41
41
  outputVariable: "prompt_result",
@@ -95,7 +95,7 @@ var SHOPPING_ASSISTANT_FLOW = {
95
95
  type: "prompt",
96
96
  enabled: true,
97
97
  config: {
98
- model: "qwen/qwen3-8b",
98
+ model: "mercury-2",
99
99
  reasoning: false,
100
100
  responseFormat: "JSON",
101
101
  outputVariable: "prompt_result",
@@ -168,7 +168,7 @@ var SHOPPING_ASSISTANT_METADATA_FLOW = {
168
168
  type: "prompt",
169
169
  enabled: true,
170
170
  config: {
171
- model: "qwen/qwen3-8b",
171
+ model: "mercury-2",
172
172
  reasoning: false,
173
173
  responseFormat: "JSON",
174
174
  outputVariable: "prompt_result",
@@ -252,7 +252,7 @@ var COMPONENT_FLOW = {
252
252
  type: "prompt",
253
253
  enabled: true,
254
254
  config: {
255
- model: "qwen/qwen3-8b",
255
+ model: "mercury-2",
256
256
  reasoning: false,
257
257
  responseFormat: "JSON",
258
258
  outputVariable: "prompt_result",
@@ -306,96 +306,108 @@ var BAKERY_ASSISTANT_FLOW = {
306
306
  type: "prompt",
307
307
  enabled: true,
308
308
  config: {
309
- model: "qwen/qwen3-8b",
309
+ model: "mercury-2",
310
310
  reasoning: false,
311
311
  responseFormat: "JSON",
312
312
  outputVariable: "prompt_result",
313
313
  userPrompt: "{{user_message}}",
314
314
  systemPrompt: `You are a helpful shopping assistant for Flour & Stone, a premium artisan bakery known for traditional bread-making and exceptional pastries.
315
315
 
316
- Brand voice: Warm, knowledgeable, passionate about craft baking. Use phrases like "fresh from the oven", "handcrafted with care", "artisan tradition".
316
+ Brand 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.
317
317
 
318
- CRITICAL: You have access to the current page context through metadata. ALWAYS check this before responding:
319
- - current_page: The path of the current page (e.g., "/bakery-goods.html")
320
- - page_elements: Array of elements on the page - USE THIS to see what products are visible
321
- - cart: Current cart contents (if any items)
322
- - recent_order: Recent order info (if completed)
318
+ ## Live context (request inputs \u2014 substituted each turn)
323
319
 
324
- ALWAYS check current_page FIRST before deciding on an action. Never navigate if already on the target page.
320
+ The widget sends **only** these keys as dispatch **inputs** (nothing extra on the record for this demo).
325
321
 
326
- DISCOVERING PRODUCTS:
327
- Look at page_elements to find products. Products have:
328
- - data-product attribute with the product ID
329
- - Price shown in the element text
330
- - "Add to Cart" buttons
322
+ **Orientation**
323
+ - Path: {{current_page}} (compare before nav_then_click; e.g. /bakery-goods.html)
324
+ - Full URL: {{page_url}}
325
+ - Title: {{page_title}}
331
326
 
332
- You must respond with JSON in one of these formats:
327
+ **Page DOM**
328
+ - 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.
329
+ - page_context: Same slice formatted for the LLM (structured card summaries when matched, then groups by interactivity).
333
330
 
334
- 1. Simple message (use for conversation, questions, product info):
335
- {"action": "message", "text": "Your response text here"}
331
+ {{page_elements}}
336
332
 
337
- 2. Navigate to another page (ONLY if current_page is different from target):
338
- {"action": "nav_then_click", "page": "/bakery-goods.html", "on_load_text": "Message after navigation"}
333
+ {{page_context}}
339
334
 
340
- 3. Add item to cart (use when user confirms they want to add):
341
- {"action": "add_to_cart", "text": "Your message", "item": {"id": "product-id", "name": "Product Name", "price": 1200}}
335
+ **Cart (for checkout \u2014 mirror cart.items when user pays)**
336
+ {{cart}}
342
337
 
343
- 4. Checkout (when user wants to pay):
344
- {"action": "checkout", "text": "Your message", "items": [{"name": "Product Name", "price": 1200, "quantity": 1}]}
338
+ **Recent order (if any)**
339
+ {{recent_order}}
345
340
 
346
- 5. Scroll to product and add to cart (when ON goods page and product may be below fold):
347
- {"action": "scroll_then_add", "text": "Your message", "item": {"id": "product-id", "name": "Product Name", "price": 1200}}
341
+ If {{current_page}} already equals the page you would navigate to, use {"action":"message",...} instead of nav_then_click.
348
342
 
349
- CRITICAL RULES:
350
- - ALWAYS check current_page in the metadata before responding
351
- - Prices are in cents (1200 = $12.00)
352
- - Always respond with valid JSON only
343
+ ## Discovering products
353
344
 
354
- DECISION TREE:
355
- 1. If user wants to see/buy products AND current_page is NOT "/bakery-goods.html" \u2192 nav_then_click
356
- 2. If user wants to add to cart AND current_page IS "/bakery-goods.html" \u2192 scroll_then_add (scrolls to product and adds)
357
- 3. If user asks about gifts AND current_page is NOT "/bakery-goods.html" \u2192 nav_then_click to /bakery-goods.html
358
- 4. If user asks about gifts AND current_page IS "/bakery-goods.html" \u2192 scroll_then_add with gift card
359
- 5. If user says "yes" to adding something AND current_page IS "/bakery-goods.html" \u2192 scroll_then_add
360
- 6. If user confirms checkout (says "yes", "checkout", "pay", etc. after being asked about checkout) AND cart has items \u2192 checkout action
345
+ Use {{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.
361
346
 
362
- PRODUCT INFO (use when adding to cart):
363
- - Sourdough Loaf: id="sourdough-loaf", price=1200
364
- - Croissant Box (6): id="croissant-box", price=2400
365
- - Cinnamon Rolls (4): id="cinnamon-rolls", price=1800
366
- - Baguette Trio: id="baguette-trio", price=900
367
- - Almond Tart: id="almond-tart", price=800
368
- - Fruit Danish: id="fruit-danish", price=600
369
- - $50 Gift Card: id="gift-card-50", price=5000
370
- - $25 Gift Card: id="gift-card-25", price=2500
347
+ ## Output: one JSON object only
371
348
 
372
- EXAMPLES:
349
+ No markdown fences, no commentary before/after. Valid JSON only.
350
+
351
+ ### 1. message
352
+ {"action": "message", "text": "..."}
353
+ Use 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).
354
+
355
+ ### 2. nav_then_click
356
+ {"action": "nav_then_click", "page": "/bakery-goods.html", "on_load_text": "..."}
357
+ Use root-relative paths starting with /. Only when current_page is different from the target. This **only** changes pages \u2014 it does **not** open Stripe or payment.
358
+
359
+ ### 3. add_to_cart
360
+ {"action": "add_to_cart", "text": "...", "item": {"id": "product-id", "name": "Product Name", "price": 1200}}
361
+ Use when adding from context without scrolling (optional; on goods page prefer scroll_then_add).
362
+
363
+ ### 4. scroll_then_add (preferred on /bakery-goods.html)
364
+ {"action": "scroll_then_add", "text": "...", "item": {"id": "...", "name": "...", "price": 1200}}
365
+ Scrolls the product into view then adds one unit (cart merges duplicate ids into quantity).
373
366
 
374
- User on /bakery-locations.html asks "I'm looking for a gift":
375
- {"action": "nav_then_click", "page": "/bakery-goods.html", "on_load_text": "Here are our goods! We have wonderful gift options - scroll down to find our $50 Gift Card, perfect for any occasion. Would you like me to add one to your cart?"}
367
+ ### 5. checkout \u2192 Stripe (this demo)
368
+ {"action": "checkout", "text": "Brief message", "items": [{"name": "...", "price": 1200, "quantity": 2}, ...]}
369
+ **Only** this action starts hosted checkout (Stripe). **Never** use nav_then_click to a "/checkout" URL for payment here.
370
+ Requirements: 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 \u2014 do not checkout.
376
371
 
377
- User on /bakery-goods.html asks "where is the gift card?" or "yes" to adding gift card:
378
- {"action": "scroll_then_add", "text": "Here's our $50 Gift Card! I've added it to your cart. Would you like to checkout now?", "item": {"id": "gift-card-50", "name": "$50 Gift Card", "price": 5000}}
372
+ ### 6. message_and_click (rare)
373
+ If page_elements show a specific button selector and scroll_then_add is wrong, you may use message_and_click with a CSS selector \u2014 prefer scroll_then_add on bakery-goods.html.
379
374
 
380
- User on /bakery.html says "yes" to seeing products:
381
- {"action": "nav_then_click", "page": "/bakery-goods.html", "on_load_text": "Here are all our handcrafted goods! What would you like to add to your cart?"}
375
+ ## Rules
382
376
 
383
- User on /bakery-goods.html says "yes" to adding sourdough:
384
- {"action": "scroll_then_add", "text": "Great choice! I've added the Sourdough Loaf to your cart. Would you like to checkout, or add something else?", "item": {"id": "sourdough-loaf", "name": "Sourdough Loaf", "price": 1200}}
377
+ - Prices in JSON are always **integer cents** (1200 = $12.00).
378
+ - After adding to cart, invite checkout or more shopping.
379
+ - On checkout confirmation ("yes", "checkout", "pay", "proceed", etc.), build **items** from **cart.items** (all rows, correct quantity). Do not drop lines or invent prices.
385
380
 
386
- User on /bakery-goods.html asks "add the croissants":
387
- {"action": "scroll_then_add", "text": "Excellent! I've added the Croissant Box to your cart. Ready to checkout?", "item": {"id": "croissant-box", "name": "Croissant Box (6)", "price": 2400}}
381
+ ## Product catalog (ids and cent prices)
388
382
 
389
- User says "yes" or "yes!" after being asked "Would you like to checkout now?" (cart has $50 Gift Card):
390
- {"action": "checkout", "text": "Perfect! Creating your checkout now...", "items": [{"name": "$50 Gift Card", "price": 5000, "quantity": 1}]}
383
+ - Sourdough Loaf: sourdough-loaf, 1200
384
+ - Croissant Box (6): croissant-box, 2400
385
+ - Cinnamon Rolls (4): cinnamon-rolls, 1800
386
+ - Baguette Trio: baguette-trio, 900
387
+ - Almond Tart: almond-tart, 800
388
+ - Fruit Danish: fruit-danish, 600
389
+ - $50 Gift Card: gift-card-50, 5000
390
+ - $25 Gift Card: gift-card-25, 2500
391
391
 
392
- User says "checkout" or "I want to pay" (cart has Sourdough Loaf):
393
- {"action": "checkout", "text": "Great! Taking you to checkout now...", "items": [{"name": "Sourdough Loaf", "price": 1200, "quantity": 1}]}
392
+ ## Examples
394
393
 
395
- IMPORTANT RULES:
396
- - After ANY add to cart action, ALWAYS ask if they want to checkout
397
- - When user CONFIRMS checkout (yes, sure, checkout, pay, etc.), use the checkout action with items from the cart metadata
398
- - The checkout text should be brief like "Creating your checkout now..." - do NOT ask more questions`,
394
+ Gift seeker on /bakery-locations.html:
395
+ {"action": "nav_then_click", "page": "/bakery-goods.html", "on_load_text": "Here are our goods! You'll find our gift cards below \u2014 $50 is our most popular. Want me to add one?"}
396
+
397
+ On /bakery-goods.html, user wants $50 gift card:
398
+ {"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}}
399
+
400
+ User on /bakery.html agrees to see products:
401
+ {"action": "nav_then_click", "page": "/bakery-goods.html", "on_load_text": "Here are our handcrafted goods \u2014 what sounds good today?"}
402
+
403
+ On /bakery-goods.html, add sourdough:
404
+ {"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}}
405
+
406
+ Cart has one $50 gift card; user says yes to checkout:
407
+ {"action": "checkout", "text": "Opening secure checkout...", "items": [{"name": "$50 Gift Card", "price": 5000, "quantity": 1}]}
408
+
409
+ Cart has sourdough (qty 1) and croissant box (qty 1); user says "pay":
410
+ {"action": "checkout", "text": "Taking you to checkout...", "items": [{"name": "Sourdough Loaf", "price": 1200, "quantity": 1}, {"name": "Croissant Box (6)", "price": 2400, "quantity": 1}]}`,
399
411
  previousMessages: "{{messages}}"
400
412
  }
401
413
  }
@@ -486,8 +498,7 @@ var DEFAULT_FLOW = {
486
498
  type: "prompt",
487
499
  enabled: true,
488
500
  config: {
489
- model: "gemini-2.5-flash",
490
- // model: "gpt-4o",
501
+ model: "mercury-2",
491
502
  responseFormat: "markdown",
492
503
  outputVariable: "prompt_result",
493
504
  userPrompt: "{{user_message}}",
@@ -503,9 +514,8 @@ var DEFAULT_FLOW = {
503
514
  ]
504
515
  };
505
516
  var withCors = (allowedOrigins) => async (c, next) => {
506
- var _a;
507
517
  const origin = c.req.header("origin");
508
- const isDevelopment = process.env.NODE_ENV === "development" || !process.env.NODE_ENV;
518
+ const isDevelopment = process.env.NODE_ENV === "development";
509
519
  let corsOrigin;
510
520
  if (!allowedOrigins || allowedOrigins.length === 0) {
511
521
  corsOrigin = origin || "*";
@@ -522,7 +532,7 @@ var withCors = (allowedOrigins) => async (c, next) => {
522
532
  }
523
533
  const headers = {
524
534
  "Access-Control-Allow-Origin": corsOrigin,
525
- "Access-Control-Allow-Headers": (_a = c.req.header("access-control-request-headers")) != null ? _a : "Content-Type, Authorization",
535
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
526
536
  "Access-Control-Allow-Methods": "POST, OPTIONS",
527
537
  Vary: "Origin"
528
538
  };
@@ -559,7 +569,7 @@ var createChatProxyApp = (options = {}) => {
559
569
  return c.json({ error: "Missing messageId" }, 400);
560
570
  }
561
571
  payload.timestamp = (_a2 = payload.timestamp) != null ? _a2 : (/* @__PURE__ */ new Date()).toISOString();
562
- const isDevelopment = process.env.NODE_ENV === "development" || !process.env.NODE_ENV;
572
+ const isDevelopment = process.env.NODE_ENV === "development";
563
573
  if (isDevelopment) {
564
574
  console.log("\n=== Feedback Received ===");
565
575
  console.log("Type:", payload.type);
@@ -590,8 +600,8 @@ var createChatProxyApp = (options = {}) => {
590
600
  });
591
601
  });
592
602
  app.post(path, async (c) => {
593
- var _a2, _b2, _c2, _d, _e, _f;
594
- const apiKey = (_b2 = (_a2 = options.apiKey) != null ? _a2 : process.env.RUNTYPE_API_KEY) != null ? _b2 : process.env.TRAVRSE_API_KEY;
603
+ var _a2, _b2, _c2, _d, _e;
604
+ const apiKey = (_a2 = options.apiKey) != null ? _a2 : process.env.RUNTYPE_API_KEY;
595
605
  if (!apiKey) {
596
606
  return c.json(
597
607
  { error: "Missing API key. Set RUNTYPE_API_KEY." },
@@ -607,13 +617,13 @@ var createChatProxyApp = (options = {}) => {
607
617
  400
608
618
  );
609
619
  }
610
- const isDevelopment = process.env.NODE_ENV === "development" || !process.env.NODE_ENV;
620
+ const isDevelopment = process.env.NODE_ENV === "development";
611
621
  const isAgentMode = !!clientPayload.agent;
612
622
  let runtypePayload;
613
623
  if (isAgentMode) {
614
624
  runtypePayload = clientPayload;
615
625
  } else {
616
- const messages = (_c2 = clientPayload.messages) != null ? _c2 : [];
626
+ const messages = (_b2 = clientPayload.messages) != null ? _b2 : [];
617
627
  const sortedMessages = [...messages].sort((a, b) => {
618
628
  const timeA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
619
629
  const timeB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
@@ -623,8 +633,8 @@ var createChatProxyApp = (options = {}) => {
623
633
  role: message.role,
624
634
  content: message.content
625
635
  }));
626
- const flowId = (_d = clientPayload.flowId) != null ? _d : options.flowId;
627
- const flowConfig = (_e = options.flowConfig) != null ? _e : DEFAULT_FLOW;
636
+ const flowId = (_c2 = clientPayload.flowId) != null ? _c2 : options.flowId;
637
+ const flowConfig = (_d = options.flowConfig) != null ? _d : DEFAULT_FLOW;
628
638
  runtypePayload = {
629
639
  record: {
630
640
  name: "Streaming Chat Widget",
@@ -639,6 +649,10 @@ var createChatProxyApp = (options = {}) => {
639
649
  autoAppendMetadata: false
640
650
  }
641
651
  };
652
+ const clientInputs = clientPayload.inputs;
653
+ if (clientInputs && typeof clientInputs === "object" && !Array.isArray(clientInputs)) {
654
+ runtypePayload.inputs = clientInputs;
655
+ }
642
656
  if (flowId) {
643
657
  runtypePayload.flow = { id: flowId };
644
658
  } else {
@@ -678,7 +692,7 @@ var createChatProxyApp = (options = {}) => {
678
692
  return new Response(response.body, {
679
693
  status: response.status,
680
694
  headers: {
681
- "Content-Type": (_f = response.headers.get("content-type")) != null ? _f : "application/json",
695
+ "Content-Type": (_e = response.headers.get("content-type")) != null ? _e : "application/json",
682
696
  "Cache-Control": "no-store"
683
697
  }
684
698
  });
package/dist/index.js.map CHANGED
@@ -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/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\n/** @deprecated Use RuntypeFlowStep instead */\nexport type TravrseFlowStep = RuntypeFlowStep;\n/** @deprecated Use RuntypeFlowConfig instead */\nexport type TravrseFlowConfig = RuntypeFlowConfig;\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 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 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: \"gemini-2.5-flash\",\n // model: \"gpt-4o\",\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) =>\n async (c: Context, next: () => Promise<void>) => {\n const origin = c.req.header(\"origin\");\n const isDevelopment = process.env.NODE_ENV === \"development\" || !process.env.NODE_ENV;\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 {\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\":\n c.req.header(\"access-control-request-headers\") ??\n \"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 app.use(\"*\", withCors(options.allowedOrigins));\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 =\n process.env.NODE_ENV === \"development\" || !process.env.NODE_ENV;\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(\n \"Content Preview:\",\n payload.content?.substring(0, 100) ?? \"(none)\"\n );\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 ?? process.env.RUNTYPE_API_KEY ?? process.env.TRAVRSE_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 = process.env.NODE_ENV === \"development\" || !process.env.NODE_ENV;\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 if (flowId) {\n runtypePayload.flow = { id: flowId };\n } else {\n runtypePayload.flow = flowConfig;\n }\n }\n\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 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\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: \"meta/llama3.1-8b-instruct-free\",\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: \"qwen/qwen3-8b\",\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\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\": \"Company\", \"type\": \"text\"}, {\"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: \"qwen/qwen3-8b\",\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: \"qwen/qwen3-8b\",\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: \"qwen/qwen3-8b\",\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 { TravrseFlowConfig } 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: TravrseFlowConfig = {\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: \"qwen/qwen3-8b\",\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\".\n\nCRITICAL: You have access to the current page context through metadata. ALWAYS check this before responding:\n- current_page: The path of the current page (e.g., \"/bakery-goods.html\")\n- page_elements: Array of elements on the page - USE THIS to see what products are visible\n- cart: Current cart contents (if any items)\n- recent_order: Recent order info (if completed)\n\nALWAYS check current_page FIRST before deciding on an action. Never navigate if already on the target page.\n\nDISCOVERING PRODUCTS:\nLook at page_elements to find products. Products have:\n- data-product attribute with the product ID\n- Price shown in the element text\n- \"Add to Cart\" buttons\n\nYou must respond with JSON in one of these formats:\n\n1. Simple message (use for conversation, questions, product info):\n{\"action\": \"message\", \"text\": \"Your response text here\"}\n\n2. Navigate to another page (ONLY if current_page is different from target):\n{\"action\": \"nav_then_click\", \"page\": \"/bakery-goods.html\", \"on_load_text\": \"Message after navigation\"}\n\n3. Add item to cart (use when user confirms they want to add):\n{\"action\": \"add_to_cart\", \"text\": \"Your message\", \"item\": {\"id\": \"product-id\", \"name\": \"Product Name\", \"price\": 1200}}\n\n4. Checkout (when user wants to pay):\n{\"action\": \"checkout\", \"text\": \"Your message\", \"items\": [{\"name\": \"Product Name\", \"price\": 1200, \"quantity\": 1}]}\n\n5. Scroll to product and add to cart (when ON goods page and product may be below fold):\n{\"action\": \"scroll_then_add\", \"text\": \"Your message\", \"item\": {\"id\": \"product-id\", \"name\": \"Product Name\", \"price\": 1200}}\n\nCRITICAL RULES:\n- ALWAYS check current_page in the metadata before responding\n- Prices are in cents (1200 = $12.00)\n- Always respond with valid JSON only\n\nDECISION TREE:\n1. If user wants to see/buy products AND current_page is NOT \"/bakery-goods.html\" → nav_then_click\n2. If user wants to add to cart AND current_page IS \"/bakery-goods.html\" → scroll_then_add (scrolls to product and adds)\n3. If user asks about gifts AND current_page is NOT \"/bakery-goods.html\" → nav_then_click to /bakery-goods.html\n4. If user asks about gifts AND current_page IS \"/bakery-goods.html\" → scroll_then_add with gift card\n5. If user says \"yes\" to adding something AND current_page IS \"/bakery-goods.html\" → scroll_then_add\n6. If user confirms checkout (says \"yes\", \"checkout\", \"pay\", etc. after being asked about checkout) AND cart has items → checkout action\n\nPRODUCT INFO (use when adding to cart):\n- Sourdough Loaf: id=\"sourdough-loaf\", price=1200\n- Croissant Box (6): id=\"croissant-box\", price=2400\n- Cinnamon Rolls (4): id=\"cinnamon-rolls\", price=1800\n- Baguette Trio: id=\"baguette-trio\", price=900\n- Almond Tart: id=\"almond-tart\", price=800\n- Fruit Danish: id=\"fruit-danish\", price=600\n- $50 Gift Card: id=\"gift-card-50\", price=5000\n- $25 Gift Card: id=\"gift-card-25\", price=2500\n\nEXAMPLES:\n\nUser on /bakery-locations.html asks \"I'm looking for a gift\":\n{\"action\": \"nav_then_click\", \"page\": \"/bakery-goods.html\", \"on_load_text\": \"Here are our goods! We have wonderful gift options - scroll down to find our $50 Gift Card, perfect for any occasion. Would you like me to add one to your cart?\"}\n\nUser on /bakery-goods.html asks \"where is the gift card?\" or \"yes\" to adding gift card:\n{\"action\": \"scroll_then_add\", \"text\": \"Here's our $50 Gift Card! I've added it to your cart. Would you like to checkout now?\", \"item\": {\"id\": \"gift-card-50\", \"name\": \"$50 Gift Card\", \"price\": 5000}}\n\nUser on /bakery.html says \"yes\" to seeing products:\n{\"action\": \"nav_then_click\", \"page\": \"/bakery-goods.html\", \"on_load_text\": \"Here are all our handcrafted goods! What would you like to add to your cart?\"}\n\nUser on /bakery-goods.html says \"yes\" to adding sourdough:\n{\"action\": \"scroll_then_add\", \"text\": \"Great choice! I've added the Sourdough Loaf to your cart. Would you like to checkout, or add something else?\", \"item\": {\"id\": \"sourdough-loaf\", \"name\": \"Sourdough Loaf\", \"price\": 1200}}\n\nUser on /bakery-goods.html asks \"add the croissants\":\n{\"action\": \"scroll_then_add\", \"text\": \"Excellent! I've added the Croissant Box to your cart. Ready to checkout?\", \"item\": {\"id\": \"croissant-box\", \"name\": \"Croissant Box (6)\", \"price\": 2400}}\n\nUser says \"yes\" or \"yes!\" after being asked \"Would you like to checkout now?\" (cart has $50 Gift Card):\n{\"action\": \"checkout\", \"text\": \"Perfect! Creating your checkout now...\", \"items\": [{\"name\": \"$50 Gift Card\", \"price\": 5000, \"quantity\": 1}]}\n\nUser says \"checkout\" or \"I want to pay\" (cart has Sourdough Loaf):\n{\"action\": \"checkout\", \"text\": \"Great! Taking you to checkout now...\", \"items\": [{\"name\": \"Sourdough Loaf\", \"price\": 1200, \"quantity\": 1}]}\n\nIMPORTANT RULES:\n- After ANY add to cart action, ALWAYS ask if they want to checkout\n- When user CONFIRMS checkout (yes, sure, checkout, pay, etc.), use the checkout action with items from the cart metadata\n- The checkout text should be brief like \"Creating your checkout now...\" - do NOT ask more questions`,\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\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\nexport interface CheckoutSessionResponse {\n success: boolean;\n checkoutUrl?: string;\n sessionId?: string;\n error?: string;\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 } = options;\n\n try {\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 }\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 // 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 \"Authorization\": `Bearer ${secretKey}`,\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: params,\n });\n\n if (!stripeResponse.ok) {\n const errorData = await stripeResponse.text();\n console.error(\"Stripe API error:\", errorData);\n return {\n success: false,\n error: \"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/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\n\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 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 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: \"mercury-2\",\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) =>\n async (c: Context, next: () => Promise<void>) => {\n const origin = c.req.header(\"origin\");\n const isDevelopment = process.env.NODE_ENV === \"development\";\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 {\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 app.use(\"*\", withCors(options.allowedOrigins));\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 =\n process.env.NODE_ENV === \"development\";\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(\n \"Content Preview:\",\n payload.content?.substring(0, 100) ?? \"(none)\"\n );\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 ?? process.env.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 = process.env.NODE_ENV === \"development\";\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\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 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\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: \"mercury-2\",\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: \"mercury-2\",\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\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\": \"Company\", \"type\": \"text\"}, {\"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: \"mercury-2\",\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: \"mercury-2\",\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: \"mercury-2\",\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: \"mercury-2\",\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","/**\n * Stripe checkout helpers using the REST API\n * This approach works on all platforms including Cloudflare Workers, Vercel Edge, etc.\n */\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\nexport interface CheckoutSessionResponse {\n success: boolean;\n checkoutUrl?: string;\n sessionId?: string;\n error?: string;\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 } = options;\n\n try {\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 }\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 // 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 \"Authorization\": `Bearer ${secretKey}`,\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: params,\n });\n\n if (!stripeResponse.ok) {\n const errorData = await stripeResponse.text();\n console.error(\"Stripe API error:\", errorData);\n return {\n success: false,\n error: \"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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runtypelabs/persona-proxy",
3
- "version": "1.43.2",
3
+ "version": "2.3.0",
4
4
  "description": "Proxy server for @runtypelabs/persona widget - handles flow configuration and forwards requests to AI backends.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -28,11 +28,13 @@
28
28
  "eslint-config-prettier": "^9.1.0",
29
29
  "rimraf": "^5.0.5",
30
30
  "tsup": "^8.0.1",
31
- "typescript": "^5.4.5"
31
+ "typescript": "^5.4.5",
32
+ "vitest": "^4.1.0"
32
33
  },
33
34
  "engines": {
34
35
  "node": ">=18.17.0"
35
36
  },
37
+ "author": "Runtype",
36
38
  "license": "MIT",
37
39
  "keywords": [
38
40
  "ai",