---
name: ash-add-agent
description: Add an Ash agent to an existing Next.js app. Use when turning a Next app into a combined Next.js + Ash app.
doc:
  title: Add Agent to Next.js
  description: Add Ash to an existing Next.js app while keeping Next.js as the primary web app.
---

Add Ash to an existing Next.js app while keeping Next as the primary web app. Preserve existing Next config, scripts, aliases, and app files unless they conflict with Ash.

## Target

```txt
agent/
  agent.ts
  channels/
    ash.ts
  instructions.md
app/
next.config.ts
package.json
tsconfig.json
```

## Steps

1. Install Ash:

```bash
pnpm add experimental-ash ai zod
```

2. Add the agent root:

```ts
// agent/agent.ts
import { defineAgent } from "experimental-ash";

export default defineAgent({
  model: "openai/gpt-5.4-mini",
});
```

```md
<!-- agent/instructions.md -->

You are a helpful assistant running inside an Ash agent.
```

Use the model requested by the user or the project standard.

3. Add the Ash HTTP channel:

First inspect the existing Next.js app for authentication. Reuse its current provider and session
model instead of introducing a second auth system. Look for Auth.js, Clerk, application session
cookies, bearer-token middleware, or another established request-auth helper.

```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) => {
  // Replace this call with the app's existing Auth.js, Clerk, or session helper.
  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],
});
```

An authored `agent/channels/ash.ts` replaces Ash's framework default `ash` channel. Keep
`localDev()` for localhost and the REPL, keep `vercelOidc()` so Vercel services can reach the
deployed agent, and implement `authenticateUser` with the app's real user auth. If the app already
uses Auth.js, Clerk, or another session provider, wire that existing provider here.

For same-origin cookie auth, the browser already sends the session cookie and the channel should
validate it from `request`. For bearer or custom-header auth, also configure the frontend's
`useAshAgent({ auth })` or `useAshAgent({ headers })` call. Do not report the frontend as
production-ready until unauthenticated requests are rejected.

4. Merge package imports only when agent files use `#...` aliases:

```json
{
  "imports": {
    "#*": "./agent/*",
    "#evals/*": "./evals/*"
  }
}
```

5. Wrap `next.config.ts` with `withAsh()`:

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

const nextConfig: NextConfig = {
  // existing config
};

export default withAsh(nextConfig);
```

If config is already wrapped by other plugins, make `withAsh()` outermost:

```ts
export default withAsh(withSomePlugin(nextConfig));
```

If config is an async function, wrap the function export with `withAsh()` rather than rewriting its internals.

6. Keep Next as the main command and add Ash-specific scripts:

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

Keep an existing `typecheck` unless it excludes `agent/**/*.ts`.

7. Update `tsconfig.json` without clobbering existing aliases:

```json
{
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    ".ash/**/*.d.ts",
    ".next/types/**/*.ts",
    ".next/dev/types/**/*.ts"
  ],
  "compilerOptions": {
    "paths": {
      "@/*": ["./*"],
      "#*": ["./agent/*"],
      "#evals/*": ["./evals/*"]
    }
  }
}
```

Add `paths` only if the project uses those aliases.

## Verify

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

Use `pnpm info:ash` or `pnpm dev:ash` to inspect Ash directly.

Verify the selected auth path as well as the health route. Local development may be accepted by
`localDev()`, but a production-style unauthenticated request must be rejected by the end-user auth
policy.
