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 install

The 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

ItemConcrete value from the tutorialWhy it matters
Add web appnpx eve channels add webScaffolds the Next.js web frontend wired to the existing eve channel.
Install dependenciesnpm installInstalls packages added by the scaffold.
Next.js integrationwithEve(nextConfig) from eve/nextWires eve routes automatically for the frontend app.
Browser hookuseEveAgent() from eve/reactHandles session creation, streaming state, message sending, and HITL prompts.
Existing channelagent/channels/eve.tsThe built-in eve HTTP channel used by the dashboard.
Chat componentapp/_components/agent-chat.tsxThe scaffolded component replaced by the minimal tutorial implementation.
Page mountapp/page.tsxImports and renders AgentChat.
Auth moduleagent/lib/auth.tsCentral place to turn a request into an app user.
Safety placeholderplaceholderAuth()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.