---
name: ash-add-next
description: Add Next.js to an existing Ash agent project. Use when turning an Ash-only app into a combined Ash + Next.js app.
doc:
  title: Add Next.js frontend to Ash
  description: Add a Next.js frontend to an existing Ash agent project.
---

Convert an Ash-only project into a combined Next.js app plus Ash agent. Preserve existing `agent/` files unless the user asks to change them.

## Target

```txt
agent/
  agent.ts
  channels/
    ash.ts
  instructions.md
app/
  globals.css
  layout.tsx
  page.tsx
next.config.ts
next-env.d.ts
package.json
tsconfig.json
```

## Steps

1. Install Next dependencies:

```bash
pnpm add next react react-dom
pnpm add -D typescript @types/react @types/react-dom
```

Align versions with the installed `experimental-ash` if peer warnings appear.

2. Add minimal App Router files:

```tsx
// app/layout.tsx
import type { Metadata } from "next";
import "./globals.css";

export const metadata: Metadata = {
  title: "Ash + Next.js",
  description: "Next.js app with an Ash agent",
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}
```

```tsx
// app/page.tsx
export default function Page() {
  return <main style={{ padding: 24 }}>Ash + Next.js</main>;
}
```

Create `app/globals.css` if imported. Add `next-env.d.ts` with the standard Next references.

3. Add `withAsh()`:

```ts
// next.config.ts
import type { NextConfig } from "next";
import { withAsh } from "experimental-ash/next";

const nextConfig: NextConfig = {};

export default withAsh(nextConfig);
```

4. Choose how frontend users authenticate to the Ash channel.

Inspect `agent/channels/ash.ts` first and preserve any existing auth policy. A channel configured
only with `localDev()` and `vercelOidc()` does not have end-user authentication. If the project does
not already have end-user authentication, ask the user which production access model they want
before adding one:

- **Clerk or Auth.js** for a normal signed-in web application.
- **Existing cookie/session auth** when another application auth system is already planned.
- **Bearer/JWT auth** when a separate client or identity provider supplies access tokens.
- **HTTP Basic** for a small private or internal frontend where a shared credential is acceptable.
- **Local-only for now** when they are only prototyping. Keep `localDev()` and `vercelOidc()`, clearly
  state that browser users cannot access the deployed agent, and do not describe production setup as
  complete.

Do not choose Clerk, Auth.js, or another provider on the user's behalf when starting from an
Ash-only project. Once the user chooses, install and configure that provider using its established
Next.js pattern, then connect the resulting request identity to `agent/channels/ash.ts`.

For cookie or session authentication, use this channel shape:

```ts
// agent/channels/ash.ts
import { ashChannel } from "experimental-ash/channels/ash";
import { type AuthFn, localDev, vercelOidc } from "experimental-ash/channels/auth";

const authenticateUser: AuthFn = async (request) => {
  // Implement this with the selected Clerk, Auth.js, or application session API.
  const user = await authenticate(request);
  if (!user) return null;

  return {
    attributes: {},
    authenticator: "app",
    issuer: "nextjs",
    principalId: user.id,
    principalType: "user",
  };
};

export default ashChannel({
  auth: [localDev(), vercelOidc(), authenticateUser],
});
```

Same-origin cookie sessions need no client credential plumbing. For bearer/JWT or custom-header
auth, also configure the generated frontend with `useAshAgent({ auth })` or
`useAshAgent({ headers })`. For HTTP Basic, use the Ash channel auth helper and keep the credential
outside source control.

5. Make Next own the main scripts:

```json
{
  "scripts": {
    "dev": "next dev",
    "dev:ash": "ash dev",
    "build": "next build",
    "build:ash": "ash build",
    "info:ash": "ash info",
    "typecheck": "tsc --noEmit"
  }
}
```

6. Use a Next-compatible `tsconfig.json`, keep `.ash/**/*.d.ts` in `include`, and preserve Ash aliases:

```json
{
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "plugins": [{ "name": "next" }],
    "paths": {
      "@/*": ["./*"],
      "#*": ["./agent/*"],
      "#evals/*": ["./evals/*"]
    }
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    ".ash/**/*.d.ts",
    ".next/types/**/*.ts",
    ".next/dev/types/**/*.ts"
  ],
  "exclude": ["node_modules"]
}
```

Merge matching `package.json` `imports` when Ash files use `#...`.

7. Ignore generated artifacts if needed: `.next`, `.vercel`, `.ash`, `.output`, `*.tsbuildinfo`.

## Verify

```bash
pnpm install
pnpm dev
curl http://localhost:3000/ash/v1/health
```

Also verify the chosen authentication path. A signed-in or correctly credentialed request should
reach the Ash session API, while an unauthenticated production-style request should return `401`.
