Ship It
Purpose and Scope
The Ship It tutorial step moves the analytics assistant from a local terminal workflow into a browser-facing application that a team can use. Earlier tutorial steps establish the agent, data access, analysis behavior, memory, and spend approval. This page focuses on the final production-shaped wiring: adding a web dashboard, connecting that dashboard to the built-in eve HTTP channel, replacing the scaffold's deliberately closed placeholder auth, and preparing the project for deployment on Vercel. The reader problem is practical: the agent already works in the TUI, but it still needs a user interface and real request-to-user authorization before it can safely leave local development.
Sources: docs/tutorial/ship-it.mdx
In eve terms, a channel is an integration surface that receives user or system input and routes it into an agent session. The tutorial uses the built-in eve channel at agent/channels/eve.ts for a first-party HTTP chat surface. The browser side is driven by useEveAgent from eve/react, which owns session creation, message sending, streamed response state, and human-in-the-loop prompts. Shipping is therefore not a rewrite of the agent; it is a focused integration pass that puts a frontend on top of the same agent channel and replaces development-only authorization with application authorization.
Sources: docs/tutorial/ship-it.mdx
Relevant Source Files
docs/tutorial/ship-it.mdx— The tutorial source for the shipping step, including the web channel scaffold command, generated Next.js wiring, minimal React chat component, and the production auth replacement narrative.
Tutorial Flow
Start from the analytics-assistant/ directory created earlier in the Build an Agent tutorial. The first action is to add a web channel scaffold because the original tutorial project was created without a web frontend. The command is intentionally channel-oriented rather than framework-oriented: eve channels add web adds the web app pieces while keeping them wired to the existing eve channel. After the command runs, install the newly added dependencies so the generated Next.js files and chat components resolve during development and build.
Sources: docs/tutorial/ship-it.mdx
npx eve channels add web
npm installThe generated frontend includes a Next.js config, an app/page.tsx route, and components under app/_components/. The important integration point in the generated next.config.ts is withEve from eve/next. Wrapping the user config with withEve lets the Next.js app serve the eve routes automatically, so the dashboard can talk to the agent without hand-writing route handlers for sessions and streams. This keeps the tutorial aligned with eve's filesystem-first approach: the authored agent remains in agent/, while the frontend framework receives the adapter wiring it needs.
Sources: docs/tutorial/ship-it.mdx
import type { NextConfig } from "next";
import { withEve } from "eve/next";
const nextConfig: NextConfig = {};
export default withEve(nextConfig);Dashboard with useEveAgent
The dashboard itself uses useEveAgent from eve/react. In the tutorial's minimal component, the hook returns an agent object whose status indicates whether a turn has been submitted or is currently streaming, and whose data.messages collection represents the chat transcript. The submit handler reads a form field named q, trims it, and calls agent.send({ message }) when there is real input. That is enough to create or continue the browser session and send the user's natural-language question to the backend agent.
Sources: docs/tutorial/ship-it.mdx
"use client";
import { useEveAgent } from "eve/react";
export function AgentChat() {
const agent = useEveAgent();
const isBusy = agent.status === "submitted" || agent.status === "streaming";
return (
<form
onSubmit={(event) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
const message = String(data.get("q") ?? "").trim();
if (message) void agent.send({ message });
}}
>
{agent.data.messages.map((message) => (
<article key={message.id}>
<header>{message.role}</header>
{message.parts.map((part, index) =>
part.type === "text" ? <p key={index}>{part.text}</p> : null,
)}
</article>
))}
<input name="q" disabled={isBusy} placeholder="Ask about the data…" />
<button type="submit" disabled={isBusy}>
Ask
</button>
</form>
);
}The component intentionally renders only text parts from each message. That keeps the first web dashboard easy to audit while still exercising the important runtime path: user input enters the browser hook, the hook talks to the built-in eve HTTP channel, streamed agent output updates the message list, and the form disables while the turn is busy. The tutorial notes that agent.data.messages and agent.status cover most chat UIs, which makes them the first two properties to understand before adding richer rendering, attachments, or custom reducers.
Sources: docs/tutorial/ship-it.mdx
The generated page already imports and renders the AgentChat export, so the dashboard does not require additional route composition. This matters because the tutorial is teaching the smallest production-shaped path, not a bespoke frontend architecture. If the scaffolded file remains in place, replacing the component implementation is sufficient: app/page.tsx mounts AgentChat, AgentChat uses useEveAgent, and withEve wires the backend routes the hook needs. The result is a working web dashboard on top of the existing agent channel.
Sources: docs/tutorial/ship-it.mdx
import { AgentChat } from "@/app/_components/agent-chat";
export default function Page() {
return <AgentChat />;
}Human-in-the-loop and Spend Approval
The shipping step also preserves the approval workflow created earlier in the tutorial. The source explicitly calls out HITL, short for human-in-the-loop, and connects it to the spend approval from the Guard the Spend step. In a browser dashboard, this means the same session stream can surface prompts that require a person to approve or deny an action before the agent continues. The minimal code sample does not render those controls, but the tutorial establishes that the hook exposes the relevant prompts so the dashboard can grow into a complete approval UI.
Sources: docs/tutorial/ship-it.mdx
This is an important production boundary because spend controls are only useful when the deployed surface respects them. A TUI demonstration proves that the agent can ask for approval; a dashboard must also give authenticated users a way to answer. When extending the minimal component, keep the message rendering and input-response path tied to the same session rather than building a separate approval side channel. That keeps approval decisions associated with the correct run and lets the eve client state reflect whether the agent is waiting, streaming, ready, or in error.
Sources: docs/tutorial/ship-it.mdx
Replace placeholderAuth
Before deployment, replace placeholderAuth(). The tutorial is explicit that the scaffolded channel fails closed: it rejects production traffic so an unauthenticated app cannot accidentally go live. Treat this as a safety feature, not a nuisance. The web UI should only be usable after a request has been mapped to an application user, and that mapping belongs in a single auth module. The tutorial suggests creating agent/lib/auth.ts and using that module to integrate a real provider such as a cookie session, Auth.js, or Clerk.
Sources: docs/tutorial/ship-it.mdx
export interface AppUser {
id: string;
team: string;
}
// Replace with your real session/provider lookup.
export async function authenticate(_request: Request): Promise<AppUser | null> {
return { id: "demo-user", team: "growth" };
}The stubbed authenticate function returns a fixed user only so the page compiles and the end-to-end flow can be exercised. For a real dashboard, the function should inspect the incoming Request, verify the session or identity provider state, and return null when the request is not authenticated. The source also indicates that the existing agent/channels/eve.ts came from a development-oriented state, including a devTeam entry and placeholderAuth(). The production edit is to point the channel at the application auth and list it before catch-all helpers.
Sources: docs/tutorial/ship-it.mdx
Compact Reference
| Item | Concrete value from the tutorial | Why it matters |
|---|---|---|
| Add web app | npx eve channels add web | Scaffolds the Next.js web frontend wired to the existing eve channel. |
| Install dependencies | npm install | Installs packages added by the scaffold. |
| Next.js integration | withEve(nextConfig) from eve/next | Wires eve routes automatically for the frontend app. |
| Browser hook | useEveAgent() from eve/react | Handles session creation, streaming state, message sending, and HITL prompts. |
| Existing channel | agent/channels/eve.ts | The built-in eve HTTP channel used by the dashboard. |
| Chat component | app/_components/agent-chat.tsx | The scaffolded component replaced by the minimal tutorial implementation. |
| Page mount | app/page.tsx | Imports and renders AgentChat. |
| Auth module | agent/lib/auth.ts | Central place to turn a request into an app user. |
| Safety placeholder | placeholderAuth() | Fails closed until replaced with real production auth. |
Deployment Readiness and Next Steps
After these edits, the project has the three pieces the tutorial names at the beginning of the step: a React UI, channel authorization, and a Vercel-oriented deployment target. The supplied source excerpt emphasizes the first two because they are the parts that change the application shape before deployment. Verify the dashboard locally, confirm that unauthenticated requests are rejected by your real auth, and make sure approval prompts from spend-controlled tools can be answered from the web UI before treating the app as production-ready.
Sources: docs/tutorial/ship-it.mdx
Read this page together with the broader Deployment guide when you need the production checklist for build output, environment variables, model credentials, sandbox backend, and host verification. For frontend details beyond the minimal tutorial component, continue to the Frontend Overview and Messages and Streaming pages. For authorization design, continue to Auth and Route Protection. For the tutorial sequence, this page closes the loop: the analytics assistant began as a local agent, gained data and safety controls, and is now shaped as an authenticated web dashboard.