A Web Dashboard
You've talked to the dispatcher in a terminal and over curl. Neither is something you'd hand a customer. The front door most people expect is a web page: a text box, a conversation, a send button.
The browser chat uses the same HTTP session API you drove by hand in 1.3. The generated web channel gives those routes a customer-facing interface while leaving the dispatcher unchanged. Two pieces handle the integration: withEve wires the agent's routes into a Next.js app, and useEveAgent drives them from React.
In this lesson, you'll inspect the generated chat UI and give it the shop's identity. The next two lessons add Slack and real authentication.
Outcome
The dispatcher answers in a browser dashboard, approvals included, and the page wears the Spoke & Mirror name instead of the scaffold's default.
Hands-on exercise
Add the web channel. One command scaffolds the whole front end:
npx eve channels add webThat command generates a Next.js app around your agent, including a next.config.ts that mounts the routes and an app/ directory with the React chat. Run git status afterward to see what it added. File paths can shift between eve versions, so use your generated tree as the source of truth.
How the door is wired. The generated next.config.ts wraps your config with withEve:
import type { NextConfig } from "next";
import { withEve } from "eve/next";
const nextConfig: NextConfig = {};
export default withEve(nextConfig);withEve mounts the agent's /eve/v1/* routes on the Next.js app's origin. The browser can reach the agent without crossing a CORS boundary or reading an environment variable. In development, npm run dev starts the eve runtime alongside next dev and forwards those routes.
Now, how the browser drives it. Open the generated chat component. It leans on one hook:
const agent = useEveAgent();That single call opens a durable session, sends turns, streams replies, and hands you render-ready state. The component reads from it:
agent.data.messages, the conversation (anEveMessage[]following the AI SDKUIMessageconvention), mapped to message bubbles.agent.status,"ready" | "submitted" | "streaming" | "error", used to disable the composer while the agent is working.agent.send({ message }), to send a turn. The samesendcarries your answer back when the agent stops for an approval.
The approval gate you built in 3.2 shows up here automatically. When book_repair parks on a big booking, the message renders approve/deny controls, and clicking one sends your answer back through the hook. You wrote zero approval-UI code; useEveAgent surfaces the parked request and the generated component renders it.
Make it the shop's. The generated app names the agent after your project. Give it the shop's identity instead. Open the generated chat component, app/_components/agent-chat.tsx in this eve version, and rename the agent (the constant the scaffold uses for the display name):
const AGENT_NAME = "Spoke & Mirror";Then set the page metadata in the app's layout.tsx:
export const metadata: Metadata = {
title: "Spoke & Mirror Dispatcher",
description: "Book bike repairs at Spoke & Mirror Cyclery.",
};Try It
Boot the dashboard:
npm run devOpen http://localhost:3000. You'll see the Spoke & Mirror chat: a header with the shop's name and a message box at the bottom. Click into the box, type a real customer question, and press Enter:
my rear brake feels spongy on the way down the hillThe reply streams into the page in the front-desk voice you wrote in 1.1. The agent is now answering in a browser without changes to any tool.
Now run it through the rest of its paces:
- Ask "what's a brake bleed cost?" The
lookup_servicetool runs and returns the same catalog price as the TUI. - Ask it to book a flat repair. The booking goes straight through.
- Ask for the Full Overhaul. The approval gate from 3.2 appears as approve / deny buttons in the chat. Click approve to resume the turn and complete the booking.
Only the surface changed. The dispatcher, tools, and approval logic stayed in place.
Blank page or a connection error in the console? Make sure you ran npm run dev; npx eve dev opens the TUI and does not serve the web app. If the chat loads but every message errors, the agent could not reach a model. Use the credential checks from 1.1: eve link or AI_GATEWAY_API_KEY.
Done-When
npm run devserves the dashboard atlocalhost:3000.- The dispatcher answers in the browser and calls tools (you see real prices/slots).
- An expensive booking renders approve/deny controls, and approving completes it.
- The page and chat header show "Spoke & Mirror", not the scaffold default.
Solution
eve channels add web did the wiring; withEve and useEveAgent came with it. Your only edits are the shop's identity, the display name in the chat component:
const AGENT_NAME = "Spoke & Mirror";and the page metadata in layout.tsx:
export const metadata: Metadata = {
title: "Spoke & Mirror Dispatcher",
description: "Book bike repairs at Spoke & Mirror Cyclery.",
};The generated channel gives you a working Next.js interface. The dispatcher's tools, state, skill, and approval logic remain unchanged. Next, you'll expose it through Slack.
Was this helpful?