Back to README | See also: Environment, Customization
This document describes the technical architecture of Personal Agent Template — a durable personal AI assistant built with Eve, Nuxt 4, and Better Auth.
The app runs as two cooperating services on Vercel:
flowchart TB
subgraph surfaces [User surfaces]
web[Web chat — Nuxt]
slack[Slack DMs and mentions]
imessage[iMessage — Photon]
end
subgraph eve [Eve agent — agent/]
channels[Channels: eve · slack · photon]
tools[Tools: weather · github]
skills[Skills · memory · connections: Linear MCP]
end
subgraph nuxt [Nuxt app — app/ + server/]
api["/api/* — public API"]
internal["/api/internal — agent-only"]
auth[Better Auth]
db[(NuxtHub PostgreSQL — Drizzle)]
end
connect[Vercel Connect — GitHub · Linear · Slack · Photon]
surfaces --> eve
eve -->|"HTTP + Bearer INTERNAL_API_SECRET"| nuxt
api --> db
internal --> db
auth --> db
nuxt --> connect
| Vercel service | Entry | Role |
|---|---|---|
web |
/ |
Nuxt UI + Nitro API |
eve |
/eve/v1/* |
Eve agent runtime (service generated by eve/nuxt) |
The eve/nuxt module generates the Vercel service and its /eve/v1/* route at build time — vercel.json does not declare them.
personal-agent-template/
├── agent/ # Eve agent
│ ├── agent.ts # Model and agent config
│ ├── channels/ # eve (web), slack, photon (iMessage)
│ ├── tools/ # weather
│ ├── extensions/ # mounted eve extensions (github)
│ ├── memory/ # memory slots (profile)
│ ├── skills/ # e.g. daily-summary.md
│ ├── connections/ # Linear MCP
│ ├── lib/ # profile-internal, slack-internal, phone-internal, internal-api
│ └── instructions.ts # system instructions
├── app/ # Nuxt frontend
│ ├── pages/ # chat, settings, login
│ ├── components/ # chat UI, profile, integrations
│ └── composables/ # useProfile, useConnectors, chat providers
├── server/ # Nitro API
│ ├── api/ # Public + internal routes
│ ├── db/ # Drizzle schema + migrations
│ └── utils/ # profile, auth, connectors, threads
├── shared/ # Cross-layer types and helpers
│ ├── agent.ts # Branding metadata
│ └── types/ # profile, thread, connector
└── docs/ # Documentation
- User opens
/chat/[id]— Nuxt loads thread via/api/threads - Chat streams through Eve's Nuxt module (
eve/nuxt) - Tool calls render in
MessageContentEve.vue - Approvals and questions render through
AgentInputRequest.vue
- Eve resolves the
profileslot's scope to the authenticated principal (agent/memory/profile.ts) fileMemory()reads that principal's document from private Vercel Blob storage- The indexed entries are recalled on
turn.startedand again after compaction
Records arrive as untrusted user-role messages, never as system instructions.
The agent maintains them with profile__save_memory and profile__remove_memory.
- Slack events hit Eve's slack channel (
agent/channels/slack.ts) - Linked users map Slack ID → app user via
slack_linkstable - Unlinked users get instructions to generate a link code in the web app
- Link flow: web generates code → user DMs
link <code>→ agent consumes via internal API
- User connects Linear in Settings → Integrations
- Vercel Connect provisions MCP credentials
- Eve connection (
agent/connections/linear.ts) exposes Linear tools to the agent
Routes under /api/internal/* require:
Authorization: Bearer <INTERNAL_API_SECRET>
Validated in server/utils/internal-api.ts.
| Route | Purpose |
|---|---|
GET /api/internal/profile |
Caller identity for the session instructions |
GET /api/internal/phone/link |
Resolve phone number → app user |
GET /api/internal/slack/link/member |
Resolve Slack user → app user |
POST /api/internal/slack/link/consume |
Consume link code |
Agent-side clients live in agent/lib/*-internal.ts.
PostgreSQL via NuxtHub, postgres-js driver. Schema in server/db/schema/.
Key tables:
| Table | Purpose |
|---|---|
user / session / account |
Better Auth |
threads |
Chat threads |
user_profile |
Name, timezone, phone |
phone_links |
Phone ↔ app user mapping, used by the iMessage channel |
slack_links |
Slack ↔ app user mapping |
slack_link_codes |
Temporary link codes |
Migrations: pnpm db:generate → pnpm db:migrate.
server/db/schema/auth.ts is generated by the Better Auth CLI, not hand-written. db:generate regenerates it before diffing the schema, so the adapter's expected tables and the migration cannot drift. Run pnpm auth:schema on its own after changing the Better Auth config.
- Provider — Eve's
fileMemory(), one bounded document per scope - Scope —
byPrincipal: one document per authenticated caller - Storage — private Vercel Blob,
eve/memory/file/<scope>/MEMORY.md - Tools —
profile__save_memory,profile__remove_memory - Bounds — 4,000 recalled characters, 2 KB per entry, 64 KB per document
Better Auth with email/password. Config: server/utils/auth.ts, route: server/api/auth/[...all].ts.
Global middleware: app/middleware/auth.global.ts.
For channels, tools, connections, and deployment details, read Eve guides in node_modules/eve/dist/docs/public/.