{"$schema":"https://ui.shadcn.com/schema/registry.json","name":"openui-assistant","type":"registry:item","title":"OpenUI Assistant","description":"An Eve agent that streams OpenUI Lang generative UI using the official openuiChatLibrary, with demo weather, stock, and search tools from the openui-chat example.","author":"TommyBez","categories":["general"],"dependencies":["@openuidev/react-lang@^0.2.8","@openuidev/react-ui@^0.12.1","ai@^7.0.38","eve@^0.31.3","just-bash@^3.0.2","zod@^4.4.3"],"meta":{"slug":"openui-assistant","category":"general","createdAt":"2026-06-30T21:52:44.748Z","updatedAt":"2026-08-10T00:00:00.000Z","docs":{"overview":["OpenUI Assistant is an eve agent that answers every chat turn with OpenUI Lang, a structured generative UI language, so your chat streams rendered cards, tables, and follow-up buttons instead of markdown prose. It builds its system prompt at build time by calling openuiChatLibrary.prompt(openuiChatPromptOptions) from @openuidev/react-ui, the same library and prompt options the official openui-chat example app uses, so its output stays compatible with the upstream renderer.","You interact with it through a normal chat session: run eve dev for the terminal UI, or mount the bundled OpenUIEveChat reference component in a Next.js app that uses withEve(), where useEveAgent() streams the reply into the Renderer from @openuidev/react-lang. Every reply is a program whose first statement assigns root, built from chat primitives like Card, CardHeader, TextContent, Table, and FollowUpItem.","Three deterministic demo tools ship with the agent: get_weather, get_stock_price, and search_web. They mirror the sample tools from the openui-chat example and return mock JSON, which makes the agent a self-contained sandbox for evaluating generative UI without wiring real data providers or API keys."],"howItWorks":["At build time, agent/instructions/openui-prompt.ts calls openuiChatLibrary.prompt(openuiChatPromptOptions) and injects the resulting OpenUI system prompt into the agent, alongside instructions.md which enforces the output contract: OpenUI Lang only, no markdown fences, first statement assigns root.","When a message arrives in the terminal UI or your web chat, the agent, running on the openai/gpt-5-mini model, decides whether the request needs get_weather, get_stock_price, or search_web before composing any UI.","The relevant tool runs and returns deterministic mock JSON, for example a weather object with temperature, humidity, wind, and a two-day forecast, or a stock quote with price, change, volume, and day range, and the agent is instructed never to invent tool-backed numbers.","The agent composes a top-down OpenUI Lang program from those tool results, using chat-library components such as Card, Table, ListBlock, and FollowUpBlock, and explicitly avoiding Stack, which is not part of openuiChatLibrary.","On the frontend, the reference component in agent/skills/openui/references/openui-eve-chat.tsx connects useEveAgent() to the @openuidev/react-lang Renderer, which paints live components while the stream is still in flight.","Three evals guard the contract with a 120 second timeout each: openui-format-contract checks that a greeting yields a pure OpenUI welcome card, while stock-quote-exact-price and weather-card-uses-tool-data verify the rendered UI matches the exact tool output."],"useCases":[{"title":"Prototype a generative UI chat product","body":"Use the agent as a working end-to-end reference for streaming structured UI from an eve backend to a React frontend, with the openui skill documenting syntax, library constraints, and a debugging checklist for parse and render failures."},{"title":"Demo dashboards from natural language","body":"Ask for a weather card for Tokyo or an NVDA quote rendered as a dashboard tile, and the agent calls the matching demo tool and streams cards, tables, and follow-up buttons built from that JSON."},{"title":"Test your OpenUI renderer integration","body":"Because the tools are deterministic, the same prompt produces the same data every run, making it a stable fixture for verifying that your Renderer setup, withEve() wiring, and @openuidev packages handle streamed OpenUI Lang correctly."},{"title":"Learn the OpenUI Lang output contract","body":"The instructions, skill references, and eval suite together show how to enforce a strict format contract on a model: root-first programs, no markdown fences, no forbidden components, and tool-backed numbers only."}],"requirements":[{"name":"AI_GATEWAY_API_KEY","body":"Optional model gateway credential for the host eve app; get one from Vercel AI Gateway. Not needed when your deployment already provides credentials through VERCEL_OIDC_TOKEN or another provider. The agent itself requires no agent-specific environment variables."},{"name":"@openuidev/react-ui and @openuidev/react-lang","body":"npm packages (^0.12.1 and ^0.2.8) that provide openuiChatLibrary, the prompt options, and the React Renderer. Both must be installed in the host app for the frontend reference component to work."},{"name":"eve","body":"The eve agent framework (^0.31.3) that runs the agent, tools, skill, and evals; the web integration also relies on withEve() from eve/next and the useEveAgent() hook. Requires Node 24 or newer."}],"faqs":[{"question":"How do I install the agent?","answer":"Run npx shadcn@latest add @evex/openui-assistant in your eve app, then install the registry dependencies (@openuidev/react-lang@^0.2.8, @openuidev/react-ui@^0.12.1, ai@^7.0.38, eve@^0.31.3, just-bash@^3.0.2, zod@^4.4.3) if they are not already present. Start it with pnpm dev or eve dev."},{"question":"Which model does it use, and can I change it?","answer":"agent/agent.ts pins openai/gpt-5-mini via defineAgent. You can swap the model string for any provider your eve gateway supports, then re-run the bundled evals to confirm the new model still honors the OpenUI Lang format contract."},{"question":"Is the web search real?","answer":"No. search_web returns deterministic mock results and is labeled demo data by design; the instructions forbid using it for current or factual research. The weather and stock tools are also deterministic samples mirrored from the openui-chat example."},{"question":"Why does the UI sometimes render as plain text or fail to parse?","answer":"The model broke the contract, usually by adding markdown fences or prose around the program. The openui skill ships a debugging checklist: confirm the reply starts with root =, all identifiers are defined, and no Stack component is used."},{"question":"How do I render the UI in my own Next.js app?","answer":"Wrap your app with withEve() from eve/next, configure the Eve HTTP channel's production route auth, copy the openui-eve-chat.tsx reference into your components folder, and render OpenUIEveChat on a page. The frontend-wiring.md reference covers full setup plus an AgentInterface alternative matching the upstream chat shell."}]}},"files":[{"path":"agent/agent.ts","type":"registry:file","target":"~/agent/agent.ts","content":"import { defineAgent } from \"eve\";\n\nexport default defineAgent({\n  model: \"openai/gpt-5-mini\",\n});\n"},{"path":"agent/instructions/openui-prompt.ts","type":"registry:file","target":"~/agent/instructions/openui-prompt.ts","content":"import {\n  openuiChatLibrary,\n  openuiChatPromptOptions,\n} from \"@openuidev/react-ui/genui-lib\";\nimport { defineInstructions } from \"eve/instructions\";\n\nconst openuiSystemPrompt = createOpenuiSystemPrompt();\n\nexport default defineInstructions({\n  markdown: openuiSystemPrompt,\n});\n\nfunction createOpenuiSystemPrompt(): string {\n  try {\n    return openuiChatLibrary.prompt(openuiChatPromptOptions);\n  } catch (error) {\n    const message = error instanceof Error ? error.message : String(error);\n\n    throw new Error(`Failed to generate OpenUI system prompt: ${message}`);\n  }\n}\n"},{"path":"agent/instructions.md","type":"registry:file","target":"~/agent/instructions.md","content":"# Mission\nYou are a generative UI assistant. Every assistant reply must be valid OpenUI\nLang that renders through the bundled `openuiChatLibrary` component set.\n\n# Non-negotiable output rules\n- Respond with OpenUI Lang only. Do not wrap output in markdown fences or add\n  prose before or after the program.\n- The first statement must assign to `root`.\n- Generate top-down: layout first, then nested components, then leaf data.\n- Use tools before rendering weather or stock tiles. Never invent tool-backed\n  numbers.\n- `search_web` is a deterministic demo tool from the OpenUI example, not a live\n  web search provider. Label its results as demo search results and do not use it\n  for current, time-sensitive, or factual research claims.\n- When a tool fails, render a `Card` with a concise error message and a recovery\n  action button instead of plain text.\n\n# Workflow\n1. Load the `openui` skill when you need component syntax, library constraints,\n   or debugging help for OpenUI Lang output.\n2. Parse the user request and decide whether you need `get_weather`,\n   `get_stock_price`, or `search_web`.\n3. Call the relevant tools, wait for structured JSON results, then compose the\n   UI program from those facts.\n4. Prefer chat-library primitives such as `Card`, `CardHeader`, `TextContent`,\n   `Table`, `ListBlock`, `FollowUpBlock`, and `FollowUpItem`. Do not use\n   `Stack`; it is not part of `openuiChatLibrary`.\n5. For greetings or help requests, render a welcome `Card` with suggested action\n   follow-ups such as weather lookup, stock quote, or demo search.\n\n# Interaction patterns\n- Weather: show location, current conditions, temperature, humidity, wind, and a\n  short forecast list.\n- Stocks: show symbol, price, change, volume, day range, and a clear up/down\n  indicator.\n- Demo search: show the query, clearly mark results as demo data, and include\n  title and snippet fields.\n- Comparisons: use `Table` to place metrics side by side.\n\n# Guardrails\n- Do not expose environment variables or internal tool errors verbatim to users.\n- Do not output HTML, markdown, or JSON when OpenUI Lang is expected.\n- Keep button actions descriptive (`action:check_weather_tokyo`) so the client\n  can map them to follow-up prompts.\n"},{"path":"agent/lib/openui-library.ts","type":"registry:file","target":"~/agent/lib/openui-library.ts","content":"export {\n  openuiChatLibrary,\n  openuiChatPromptOptions,\n} from \"@openuidev/react-ui/genui-lib\";\n"},{"path":"agent/skills/openui/references/frontend-wiring.md","type":"registry:file","target":"~/agent/skills/openui/references/frontend-wiring.md","content":"# Frontend wiring\n\nThese files are reference implementations for a Next.js app that already hosts\nan Eve agent. They mirror the official OpenUI\n[`openui-chat`](https://github.com/thesysdev/openui/tree/main/examples/openui-chat)\nexample, but route turns through Eve's built-in HTTP channel instead of a custom\n`/api/chat` route.\n\n## Required app dependencies\n\n```bash\nnpm install @openuidev/react-lang@^0.2.8 @openuidev/react-ui@^0.12.1 ai@^7.0.38 eve@^0.31.3 just-bash@^3.0.2 zod@^4.4.3\n```\n\n## Next.js setup\n\n1. Wrap `next.config.ts` with `withEve()` from `eve/next`.\n2. Configure the host app's Eve route auth before production; anonymous\n   production sessions are rejected by default. Follow\n   `node_modules/eve/docs/guides/auth-and-route-protection.md`.\n3. Copy `openui-eve-chat.tsx` into your app, for example\n   `app/_components/openui-eve-chat.tsx`.\n4. Render it from `app/page.tsx`.\n\nThe component uses `useEveAgent()` for session streaming and `@openuidev/react-lang`\n`Renderer` with `openuiChatLibrary` to turn assistant text into live UI.\n\n## Accessibility notes\n\n- The send form keeps a persistent label for the message input so keyboard and\n  screen-reader users do not have to rely on placeholder text.\n- The rendered assistant area uses `aria-live=\"polite\"` and `aria-busy` while the\n  stream is active, so updates from `Renderer` are announced without interrupting\n  the current task.\n- If your host app moves focus after submit or renders additional controls from\n  OpenUI actions, keep focus management in the host component so new UI does not\n  unexpectedly steal focus.\n\n## Alternative: AgentInterface\n\nFor the full OpenUI chat chrome (history rail, tool-call cards, theming), follow\nthe upstream `openui-chat` page and point its `ChatLLM.send` handler at Eve's\nsession routes. The Eve + Renderer path in `openui-eve-chat.tsx` is the minimal\nintegration when you already manage chat state with `useEveAgent`.\n\n## Smoke test\n\n1. Start the Eve app with this agent installed.\n2. Open the page that renders `OpenUIEveChat`.\n3. Ask: `What's the weather in Tokyo?`\n4. Confirm the assistant streams OpenUI Lang and the renderer shows cards instead\n   of raw text.\n5. Optional: ask for a demo search (for example `Search for Eve agents`) and\n   confirm results are labeled as demo data, not live web results.\n"},{"path":"agent/skills/openui/references/openui-eve-chat.tsx","type":"registry:file","target":"~/agent/skills/openui/references/openui-eve-chat.tsx","content":"\"use client\";\n\nimport { Renderer } from \"@openuidev/react-lang\";\nimport { openuiChatLibrary } from \"@openuidev/react-ui/genui-lib\";\nimport type { EveMessage } from \"eve/react\";\nimport { useEveAgent } from \"eve/react\";\nimport { useMemo, useState } from \"react\";\n\nfunction getLatestAssistantText(messages: readonly EveMessage[]): string {\n  for (let index = messages.length - 1; index >= 0; index -= 1) {\n    const message = messages[index];\n\n    if (message?.role !== \"assistant\") {\n      continue;\n    }\n\n    return message.parts\n      .filter((part) => part.type === \"text\")\n      .map((part) => part.text)\n      .join(\"\\n\");\n  }\n\n  return \"\";\n}\n\n/**\n * Reference chat surface that streams Eve assistant text into the OpenUI\n * Renderer. Copy this component into a Next.js app that already uses\n * `withEve()` from `eve/next`.\n */\nexport function OpenUIEveChat() {\n  const agent = useEveAgent();\n  const [submitError, setSubmitError] = useState<string | null>(null);\n  const isStreaming = agent.status === \"streaming\";\n  const isBusy =\n    agent.status === \"submitted\" || isStreaming;\n\n  const assistantProgram = useMemo(\n    () => getLatestAssistantText(agent.data.messages),\n    [agent.data.messages],\n  );\n\n  return (\n    <div className=\"mx-auto flex h-dvh max-w-3xl flex-col gap-4 p-4\">\n      <section\n        aria-busy={isBusy}\n        aria-live=\"polite\"\n        className=\"min-h-0 flex-1 overflow-auto rounded-xl border p-4\"\n      >\n        {assistantProgram.length > 0 ? (\n          <Renderer\n            isStreaming={isStreaming}\n            library={openuiChatLibrary}\n            response={assistantProgram}\n          />\n        ) : (\n          <p className=\"text-sm text-muted-foreground\">\n            Ask for weather, a stock quote, or a demo search to see generative UI.\n          </p>\n        )}\n      </section>\n\n      <form\n        className=\"flex gap-2\"\n        onSubmit={async (event) => {\n          event.preventDefault();\n          const formElement = event.currentTarget;\n          const form = new FormData(formElement);\n          const message = String(form.get(\"message\") ?? \"\").trim();\n\n          if (message.length > 0) {\n            setSubmitError(null);\n\n            try {\n              await agent.send(message);\n              formElement.reset();\n            } catch (error) {\n              const errorMessage =\n                error instanceof Error ? error.message : \"Failed to send message.\";\n              setSubmitError(errorMessage);\n            }\n          }\n        }}\n      >\n        <label className=\"sr-only\" htmlFor=\"message\">\n          Message\n        </label>\n        <input\n          aria-describedby={submitError ? \"message-error\" : undefined}\n          className=\"flex-1 rounded-md border px-3 py-2 text-sm\"\n          disabled={isBusy}\n          id=\"message\"\n          name=\"message\"\n          placeholder=\"Show me the weather in Tokyo\"\n        />\n        <button\n          className=\"rounded-md bg-foreground px-4 py-2 text-sm text-background disabled:opacity-50\"\n          disabled={isBusy}\n          type=\"submit\"\n        >\n          Send\n        </button>\n      </form>\n      {submitError ? (\n        <p className=\"text-sm text-destructive\" id=\"message-error\" role=\"alert\">\n          {submitError}\n        </p>\n      ) : null}\n    </div>\n  );\n}\n"},{"path":"agent/skills/openui/references/syntax-examples.md","type":"registry:file","target":"~/agent/skills/openui/references/syntax-examples.md","content":"# OpenUI Lang syntax examples\n\nOne statement per line: `identifier = Expression`. Write top-down: layout, nested\ncomponents, then leaf values. Positional arguments map to Zod prop order. Forward\nreferences are allowed; the renderer shows placeholders until defined.\n\n```text\nroot = Card([header, summary, metricsTable, followups])\nheader = CardHeader(\"Q4 Dashboard\", \"Revenue and user growth\")\nsummary = TextContent(\"Revenue is up while user growth is steady.\", \"default\")\nmetricsTable = Table([metricCol, valueCol, trendCol])\nmetricCol = Col(\"Metric\", [\"Revenue\", \"Users\"])\nvalueCol = Col(\"Value\", [\"$1.2M\", \"450k\"])\ntrendCol = Col(\"Trend\", [\"up\", \"flat\"])\nfollowups = FollowUpBlock([details])\ndetails = FollowUpItem(\"Show the detailed breakdown\")\n```\n"},{"path":"agent/skills/openui/SKILL.md","type":"registry:file","target":"~/agent/skills/openui/SKILL.md","content":"---\nname: openui\ndescription: OpenUI Lang generative UI with openuiChatLibrary. Use when composing layouts, debugging parse/render failures, or extending the chat library.\n---\n\n# OpenUI Lang\n\nEvery assistant reply is an OpenUI Lang **program** whose first statement assigns\n**root**. This agent uses `openuiChatLibrary` from `@openuidev/react-ui/genui-lib`\n(the same library as the official\n[`openui-chat`](https://github.com/thesysdev/openui/tree/main/examples/openui-chat)\nexample). The build-time system prompt comes from\n`openuiChatLibrary.prompt(openuiChatPromptOptions)`.\n\n## Chat library constraints\n\n`openuiChatLibrary` centers on chat primitives: `Card`, `CardHeader`, `TextContent`,\n`Table`, lists, and follow-ups. Do not use `Stack`; it belongs to the broader\nOpenUI library, not this agent's chat library.\n\nFor a layout example, see [syntax-examples](./references/syntax-examples.md).\n\n## Debugging checklist\n\n1. Confirm the reply starts with `root = ...`\n2. Confirm every referenced identifier is defined\n3. Confirm tool-backed values match the latest tool JSON\n4. Remove markdown fences or prose around the program\n5. Re-run with a smaller layout if the stream was truncated\n\n**Done when** each item is confirmed or ruled out and the reply is a valid OpenUI\nLang program starting with `root =`.\n\n## Frontend integration\n\nFor Next.js + Eve wiring (dependencies, `withEve()`, `OpenUIEveChat`, accessibility,\nsmoke tests), see [frontend-wiring](./references/frontend-wiring.md).\n\n## External references\n\n- OpenUI docs: https://www.openui.com/docs/openui-lang/overview\n- Language spec: https://www.openui.com/docs/openui-lang/specification\n- Example app: https://github.com/thesysdev/openui/tree/main/examples/openui-chat\n"},{"path":"agent/tools/get_stock_price.ts","type":"registry:file","target":"~/agent/tools/get_stock_price.ts","content":"import { defineTool } from \"eve/tools\";\nimport { z } from \"zod\";\n\nconst knownPrices: Record<string, number> = {\n  AAPL: 189.84,\n  AMZN: 178.25,\n  GOOGL: 141.8,\n  META: 485.58,\n  MSFT: 378.91,\n  NVDA: 875.28,\n  TSLA: 248.42,\n};\n\nconst getStockPriceInput = z.object({\n  symbol: z\n    .string()\n    .min(1)\n    .describe(\"Ticker symbol, for example AAPL or NVDA\"),\n});\n\nexport type GetStockPriceInput = z.infer<typeof getStockPriceInput>;\n\nexport type GetStockPriceOutput = {\n  change: number;\n  change_percent: number;\n  day_high: number;\n  day_low: number;\n  price: number;\n  symbol: string;\n  volume: string;\n};\n\nfunction resolveBasePrice(symbol: string): number {\n  const known = knownPrices[symbol];\n\n  if (known !== undefined) {\n    return known;\n  }\n\n  let hash = 0;\n\n  for (const character of symbol) {\n    hash = (hash + character.charCodeAt(0)) % 480;\n  }\n\n  return 20 + hash;\n}\n\nexport default defineTool({\n  description:\n    \"Get the latest stock quote for a ticker symbol. Use before rendering price or market cards.\",\n  inputSchema: getStockPriceInput,\n  execute({ symbol }): GetStockPriceOutput {\n    const normalizedSymbol = symbol.trim().toUpperCase();\n    const basePrice = resolveBasePrice(normalizedSymbol);\n    const change = Number.parseFloat(\n      ((normalizedSymbol.length % 8) - 4).toFixed(2),\n    );\n    const price = Number.parseFloat((basePrice + change).toFixed(2));\n    const changePercent = Number.parseFloat(\n      ((change / basePrice) * 100).toFixed(2),\n    );\n    const volumeMillions = 10 + (normalizedSymbol.length % 50);\n\n    return {\n      symbol: normalizedSymbol,\n      price,\n      change,\n      change_percent: changePercent,\n      volume: `${volumeMillions.toFixed(1)}M`,\n      day_high: Number.parseFloat(\n        (price + Math.abs(change) + 1.5).toFixed(2),\n      ),\n      day_low: Number.parseFloat(\n        (price - Math.abs(change) - 1.2).toFixed(2),\n      ),\n    };\n  },\n  toModelOutput(output) {\n    return {\n      type: \"json\",\n      value: output,\n    };\n  },\n});\n"},{"path":"agent/tools/get_weather.ts","type":"registry:file","target":"~/agent/tools/get_weather.ts","content":"import { defineTool } from \"eve/tools\";\nimport { z } from \"zod\";\n\nconst knownTemperaturesCelsius: Record<string, number> = {\n  berlin: 16,\n  london: 14,\n  mumbai: 33,\n  \"new york\": 25,\n  paris: 19,\n  \"san francisco\": 18,\n  sydney: 27,\n  tokyo: 22,\n};\n\nconst weatherConditions = [\n  \"Clear Skies\",\n  \"Cloudy\",\n  \"Light Rain\",\n  \"Partly Cloudy\",\n  \"Sunny\",\n] as const;\n\nconst getWeatherInput = z.object({\n  location: z.string().min(1).describe(\"City name, for example Tokyo\"),\n});\n\nexport type GetWeatherInput = z.infer<typeof getWeatherInput>;\n\nexport type GetWeatherOutput = {\n  condition: string;\n  forecast: Array<{\n    condition: string;\n    day: string;\n    high: number;\n    low: number;\n  }>;\n  humidity_percent: number;\n  location: string;\n  temperature_celsius: number;\n  temperature_fahrenheit: number;\n  wind_speed_kmh: number;\n};\n\nfunction pickCondition(index: number): string {\n  return weatherConditions[index % weatherConditions.length] ?? \"Partly Cloudy\";\n}\n\nfunction resolveTemperatureCelsius(location: string): number {\n  const normalized = location.trim().toLowerCase();\n  const known = knownTemperaturesCelsius[normalized];\n\n  if (known !== undefined) {\n    return known;\n  }\n\n  let hash = 0;\n\n  for (const character of normalized) {\n    hash = (hash + character.charCodeAt(0)) % 30;\n  }\n\n  return 5 + hash;\n}\n\nexport default defineTool({\n  description:\n    \"Get current weather and a short forecast for a city. Use before rendering weather cards or charts.\",\n  inputSchema: getWeatherInput,\n  execute({ location }): GetWeatherOutput {\n    const temperatureCelsius = resolveTemperatureCelsius(location);\n    const condition = pickCondition(location.length);\n\n    return {\n      location,\n      temperature_celsius: temperatureCelsius,\n      temperature_fahrenheit: Math.round(temperatureCelsius * 1.8 + 32),\n      condition,\n      humidity_percent: 40 + (location.length % 40),\n      wind_speed_kmh: 5 + (location.length % 25),\n      forecast: [\n        {\n          day: \"Tomorrow\",\n          high: temperatureCelsius + 2,\n          low: temperatureCelsius - 4,\n          condition: \"Partly Cloudy\",\n        },\n        {\n          day: \"Day After\",\n          high: temperatureCelsius + 1,\n          low: temperatureCelsius - 3,\n          condition: \"Sunny\",\n        },\n      ],\n    };\n  },\n  toModelOutput(output) {\n    return {\n      type: \"json\",\n      value: output,\n    };\n  },\n});\n"},{"path":"agent/tools/search_web.ts","type":"registry:file","target":"~/agent/tools/search_web.ts","content":"import { defineTool } from \"eve/tools\";\nimport { z } from \"zod\";\n\nconst searchWebInput = z.object({\n  query: z.string().min(1).describe(\"Search query\"),\n});\n\nexport type SearchWebInput = z.infer<typeof searchWebInput>;\n\nexport type SearchWebOutput = {\n  kind: \"mock-search-results\";\n  query: string;\n  results: Array<{\n    snippet: string;\n    title: string;\n  }>;\n};\n\nexport default defineTool({\n  description:\n    \"Return deterministic mock search results for OpenUI demo layouts. This does not perform live web search and must be labeled as demo data.\",\n  inputSchema: searchWebInput,\n  execute({ query }): SearchWebOutput {\n    return {\n      kind: \"mock-search-results\",\n      query,\n      results: [\n        {\n          title: `Demo result for \"${query}\"`,\n          snippet: `Example overview card for ${query}. This is mock data for layout testing.`,\n        },\n        {\n          title: `${query} - Demo trend`,\n          snippet: `Example trend snippet for ${query}. Do not treat this as live research.`,\n        },\n        {\n          title: `Understanding ${query}`,\n          snippet: `Example explainer snippet for ${query}, included for demo UI rendering.`,\n        },\n      ],\n    };\n  },\n  toModelOutput(output) {\n    return {\n      type: \"json\",\n      value: output,\n    };\n  },\n});\n"},{"path":"evals/evals.config.ts","type":"registry:file","target":"~/evals/evals.config.ts","content":"import { defineEvalConfig } from \"eve/evals\";\n\nexport default defineEvalConfig({\n  timeoutMs: 120_000,\n});\n"},{"path":"evals/openui-format-contract.eval.ts","type":"registry:file","target":"~/evals/openui-format-contract.eval.ts","content":"import { defineEval } from \"eve/evals\";\nimport { equals, includes } from \"eve/evals/expect\";\n\nexport default defineEval({\n  description:\n    \"Answers a greeting with a pure OpenUI Lang welcome card: assigns root first, uses chat-library components, and emits no markdown fences, prose, or forbidden Stack component.\",\n  async test(t) {\n    await t.send(\"Hi! What can you do?\");\n\n    t.succeeded();\n    t.noFailedActions();\n\n    const reply = t.reply ?? \"\";\n    t.check(reply.trimStart().startsWith(\"root =\"), equals(true).gate());\n    t.check(reply.includes(\"```\"), equals(false).gate());\n    t.check(/\\bStack\\s*\\(/.test(reply), equals(false).gate());\n    t.check(reply, includes(\"Card\").soft());\n    t.check(reply, includes(\"FollowUp\").soft());\n  },\n});\n"},{"path":"evals/stock-quote-exact-price.eval.ts","type":"registry:file","target":"~/evals/stock-quote-exact-price.eval.ts","content":"import { defineEval } from \"eve/evals\";\nimport { equals, includes } from \"eve/evals/expect\";\n\nexport default defineEval({\n  description:\n    \"Calls get_stock_price for NVDA and renders the exact deterministic quote instead of inventing market numbers.\",\n  async test(t) {\n    await t.send(\"Show me the current NVDA stock price as a market card.\");\n\n    t.succeeded();\n    t.noFailedActions();\n    t.calledTool(\"get_stock_price\").gate();\n\n    const reply = t.reply ?? \"\";\n    t.check(reply.trimStart().startsWith(\"root =\"), equals(true).gate());\n    t.check(reply.includes(\"```\"), equals(false).gate());\n    t.check(reply, includes(\"875.28\").gate());\n  },\n});\n"},{"path":"evals/weather-card-uses-tool-data.eval.ts","type":"registry:file","target":"~/evals/weather-card-uses-tool-data.eval.ts","content":"import { defineEval } from \"eve/evals\";\nimport { equals, includes } from \"eve/evals/expect\";\n\nexport default defineEval({\n  description:\n    \"Calls get_weather before rendering a weather card and answers in OpenUI Lang only, with values taken from the deterministic tool output.\",\n  async test(t) {\n    await t.send(\n      \"What's the weather in Tokyo right now? Render it as a weather card.\",\n    );\n\n    t.succeeded();\n    t.noFailedActions();\n    t.calledTool(\"get_weather\").gate();\n\n    const reply = t.reply ?? \"\";\n    t.check(reply.trimStart().startsWith(\"root =\"), equals(true).gate());\n    t.check(reply.includes(\"```\"), equals(false).gate());\n    t.check(reply, includes(\"22\").soft());\n    t.check(reply, includes(\"Clear Skies\").soft());\n  },\n});\n"},{"path":"README.md","type":"registry:file","target":"~/agent/README.md","content":"# OpenUI Assistant\n\nAn Eve agent that responds with **OpenUI Lang** and renders rich generative UI\nthrough the official `openuiChatLibrary`. It follows the\n[`openui-chat`](https://github.com/thesysdev/openui/tree/main/examples/openui-chat)\nreference app: the model streams structured UI instead of markdown, and a React\n`Renderer` turns that stream into live components.\n\n## What it does\n\n1. **Injects the OpenUI system prompt** - `agent/instructions/openui-prompt.ts`\n   calls `openuiChatLibrary.prompt(openuiChatPromptOptions)` at build time, the\n   same library and prompt options used by the GitHub example.\n2. **Answers with OpenUI Lang only** - cards, grids, charts, tables, and buttons\n   instead of plain text replies.\n3. **Calls demo data tools** - `get_weather`, `get_stock_price`, and `search_web`\n   mirror the sample tools from `examples/openui-chat/src/app/api/chat/route.ts`;\n   `search_web` is mock demo data, not live search.\n4. **Ships a frontend reference** -\n   `agent/skills/openui/references/openui-eve-chat.tsx` shows how to connect\n   `useEveAgent()` to `@openuidev/react-lang` `Renderer`.\n\n## Installation\n\n```bash\nnpx shadcn@latest add @evex/openui-assistant\n```\n\nInstall the registry dependencies in your Eve app if they are not already present:\n\n```bash\nnpm install @openuidev/react-lang@^0.2.8 @openuidev/react-ui@^0.12.1 ai@^7.0.38 eve@^0.31.3 just-bash@^3.0.2 zod@^4.4.3\n```\n\n## Configuration\n\nThis agent does not require agent-specific environment variables. The host Eve\napp still needs a model credential (`AI_GATEWAY_API_KEY`, `VERCEL_OIDC_TOKEN`, or\nyour provider's equivalent).\n\nSee `.env.example` for optional notes.\n\n## Usage\n\nAsk for structured UI backed by the demo tools:\n\n```text\nShow me the weather in Tokyo with a short forecast.\n```\n\n```text\nQuote NVDA and render the move as a dashboard card.\n```\n\n```text\nShow demo search results for \"generative UI frameworks\".\n```\n\nThe assistant should return OpenUI Lang starting with `root = ...` and use tool\nresults for factual values. Search results are labeled as demo data because the\nbundled search tool is deterministic and does not call a live provider.\n\n## Web UI setup\n\nTo render generative UI in the browser:\n\n1. Ensure your Next.js app uses `withEve()` from `eve/next`.\n2. Configure production route authentication for the Eve HTTP channel; the\n   framework rejects anonymous production sessions by default.\n3. Copy `agent/skills/openui/references/openui-eve-chat.tsx` into your app\n   components folder.\n4. Render `<OpenUIEveChat />` on a page.\n\nSee `agent/skills/openui/references/frontend-wiring.md` for the full wiring\nguide and an `AgentInterface` alternative that matches the upstream OpenUI chat\nshell. Read `node_modules/eve/docs/guides/auth-and-route-protection.md` for the\nhost app's route-auth policy.\n\n## Smoke test\n\n1. Install the agent into an Eve app and run `pnpm dev` (or `eve dev`).\n2. In the terminal UI or your web chat, send:\n\n   ```text\n   What's the weather in San Francisco?\n   ```\n\n3. Confirm the agent calls `get_weather`, then streams OpenUI Lang with weather\n   cards rather than markdown prose.\n4. If you mounted the frontend reference, confirm `Renderer` paints the UI while\n   the stream is active.\n\n## Troubleshooting\n\n- **Plain text instead of UI** - the model ignored the OpenUI contract. Reload the\n  `openui` skill and retry with an explicit layout request.\n- **Parser errors in the browser** - an assistant reply included markdown fences or\n  prose around the program. The agent should output raw OpenUI Lang only.\n- **Missing tool data** - verify the turn includes a `get_weather`,\n  `get_stock_price`, or `search_web` tool call before the UI references numbers.\n- **Renderer shows nothing** - confirm `@openuidev/react-lang` and\n  `@openuidev/react-ui` are installed in the host app and that you passed\n  `openuiChatLibrary` to `Renderer`.\n\n## Development\n\n```bash\npnpm install\npnpm --dir registry/openui-assistant typecheck\npnpm --dir registry/openui-assistant build\npnpm --dir registry/openui-assistant check\n```\n\nThe package `tsconfig.json` includes the `.tsx` frontend reference under\n`agent/skills/openui/references/`, plus DOM and React types, so `typecheck`\nvalidates the shipped reference component as well as the Eve agent source.\n\nAfter editing `registry.json`, regenerate the embedded catalog:\n\n```bash\npnpm --filter @evex/agent-registry generate\n```\n"},{"path":".env.example","type":"registry:file","target":"~/.env.example","content":"# No environment variables are required for the bundled demo tools.\n\n# Optional: set a model gateway credential in the host Eve app if your deployment\n# does not already provide one through Vercel OIDC or AI Gateway.\n# AI_GATEWAY_API_KEY=\n"}]}