Key Concepts
How agent-native apps work under the hood — the principles, the architecture, and why they're built this way.
Why agent-native
Teams today have four options for AI-powered work, and none of them are ideal:
- Chat apps (Claude Projects, ChatGPT) — accessible but not built for structured workflows. No persistent UI, no dashboards, no team collaboration.
- Raw agent interfaces (Claude Code, Cursor) — powerful but inaccessible to non-devs. No guardrails, no onboarding, no structured UI.
- Custom AI apps — limited. The AI can't see what you see, can't react to what you click, and can't update the app itself. No conversation history, no rollback, no skills.
- Existing SaaS (Amplitude, HubSpot, Google Slides) — bolting AI onto architectures that weren't designed for it. You can feel the seams.
Agent-native apps solve this by making the agent and the UI equal citizens of the same system. Think of it as Claude Code, but with buttons and visual interfaces. The agent can do anything the UI can do (via natural language), and the UI can trigger anything the agent can do (via buttons).
See What Is Agent-Native? for the full vision and philosophy.
The architecture
Every agent-native app is three things working together:
Autonomous AI that reads data, writes data, runs actions, and modifies code. Customizable with skills and instructions.
Full React UI with dashboards, flows, and visualizations. Guided experiences your team can use.
Database, browser, code execution. Agents work directly with SQL and tools — no MCPs needed.
Every app includes an embedded agent panel with chat and optional CLI terminal. Locally, you run pnpm dev and the agent is right there. In the cloud, Builder.io provides a managed harness with collaboration, visual editing, and managed infrastructure for teams.
Six rules govern the architecture:
- Data lives in SQL — all app state lives in the database via Drizzle ORM
- All AI goes through the agent — no inline LLM calls
- Actions for agent operations — complex work runs as actions
- Polling keeps the UI in sync — database changes sync via lightweight polling
- The agent can modify code — the app evolves as you use it
- Application state in SQL — ephemeral UI state lives in the database, readable by both agent and UI
The four-area checklist
Every new feature must update all four areas. Skipping any one breaks the agent-native contract.
Page, component, or dialog the user interacts with
Agent-callable action in actions/ for the same operation
Update AGENTS.md and/or create a skill documenting the pattern
Navigation state, view-screen data, and navigate commands
A feature with only UI is invisible to the agent. A feature with only actions is invisible to the user. A feature without app-state means the agent is blind to what the user is doing.
Data in SQL
All application state lives in a SQL database via Drizzle ORM. The framework supports multiple databases — SQLite, Postgres (Neon, Supabase), Turso, Cloudflare D1. Users configure DATABASE_URL to choose their database.
Core SQL stores are auto-created and available in every template:
application_state— ephemeral UI state (navigation, drafts, selections)settings— persistent key-value configoauth_tokens— OAuth credentialssessions— auth sessions
// Drizzle schema for domain data
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
export const forms = sqliteTable("forms", {
id: text("id").primaryKey(),
title: text("title").notNull(),
schema: text("schema").notNull(), // JSON
ownerEmail: text("owner_email"),
createdAt: integer("created_at").notNull(),
});
// Core actions for quick database access
pnpm action db-schema # show all tables
pnpm action db-query --sql "SELECT * FROM forms"
pnpm action db-exec --sql "INSERT INTO forms ..."Agent chat bridge
The UI never calls an LLM directly. When a user clicks "Generate chart" or "Write summary", the UI sends a message to the agent via postMessage. The agent does the work — with full conversation history, skills, instructions, and the ability to iterate.
// In a React component — delegate AI work to the agent
import { sendToAgentChat } from "@agent-native/core";
sendToAgentChat({
message: "Generate a chart showing signups by source",
context: "Dashboard ID: main, date range: last 30 days",
submit: true,
});Why not call an LLM inline?
- AI is non-deterministic. You need conversation flow to give feedback and iterate — not one-shot buttons.
- Context matters. The agent has your full codebase, instructions, skills, and history. An inline call has none of that.
- The agent can do more. It can run actions, browse the web, modify code, and chain multiple steps together.
- Headless execution. Because everything goes through the agent, any app can be driven entirely from Slack, Telegram, or another agent via A2A.
Actions system
When the agent needs to do something complex — call an API, process data, query the database — it runs an action. Actions are TypeScript files in actions/ that export a default async function:
// actions/fetch-data.ts
import { parseArgs } from "@agent-native/core";
export default async function fetchData(args: string[]) {
const { source } = parseArgs(args);
const res = await fetch(`https://api.example.com/${source}`);
const data = await res.json();
console.log(JSON.stringify(data, null, 2));
}# Agent runs actions via CLI
pnpm action fetch-data --source=signupsThis means anything the UI can do, the agent can do — and vice versa. The UI calls POST /api/fetch-data, the agent calls pnpm action fetch-data. Same logic, same results, different entry points.
Polling sync
Database changes are synced to the UI via lightweight polling. When the agent writes to the database (application state, settings, or domain data), a version counter increments. The client useDbSync() hook (formerly useFileWatcher) polls /_agent-native/poll every 2 seconds and invalidates React Query caches when changes are detected.
// Client: invalidate caches on database changes
import { useDbSync } from "@agent-native/core";
useDbSync({
queryClient,
queryKeys: ["app-state", "settings", "forms"],
});The flow is:
- Agent runs an action that writes to the database
- Version counter increments
useDbSyncdetects the new version on next poll- React Query caches are invalidated
- Components re-fetch and render the new data
This works in all deployment environments — including serverless and edge — because it uses the database, not in-memory state or file system watchers.
Harnesses
Agent-native apps include an embedded agent panel that provides the AI agent alongside the app UI. This is what makes the architecture work: the agent needs a computer (database, browser, code execution), and the app needs the agent for AI work.
Chat and optional CLI terminal built into every app. Supports Claude Code, Codex, Gemini, OpenCode, and Builder.io. Runs locally. Free and open source.
Deploy to any cloud with real-time collaboration, visual editing, roles and permissions. Best for teams.
Context awareness
The agent always knows what the user is looking at. The UI writes a navigation key to application-state on every route change. The agent reads it via the view-screen action before acting.
See Context Awareness for the full pattern: navigation state, view-screen, navigate commands, and jitter prevention.
APIs & CLIs, not MCPs
Agent-native apps can work with MCP servers, but the architecture leans heavily on something more standard: regular APIs and CLIs accessed through code execution. Agents are great at writing code that calls fetch() or runs a CLI command — no special protocol needed.
- No wrapper layer. Call APIs directly with
fetch()or use official SDKs. - Any CLI works.
ffmpeg,gh,aws,gcloud— if it runs in a terminal, the agent can use it. - Code is the protocol. TypeScript actions are more expressive than any tool schema.
- MCP is additive. Use MCP servers alongside actions if you want, but they're not required.
Agent modifies code
This is a feature, not a bug. Because every agent-native app is single-tenant — your team's own fork — the agent can safely edit the app's source code: components, routes, styles, actions.
There's no shared codebase to break. You own the app, and the agent evolves it for you over time:
- Fork a template (e.g. the analytics template)
- Customize it by asking the agent
- "Add a new chart type for cohort analysis" — the agent builds it
- "Connect to our Stripe account" — the agent writes the integration
- Your app keeps improving without manual development
Database agnostic
The framework supports every Drizzle-supported database. Never write SQL that only works on one dialect.
- SQLite — local dev fallback when
DATABASE_URLis unset - Neon Postgres — common in both dev and production
- Turso (libSQL) — edge-friendly SQLite-compatible
- Supabase Postgres
- Cloudflare D1
- Plain Postgres
Use the framework helpers for dialect-agnostic SQL:
import { getDbExec, isPostgres, intType } from "@agent-native/core/db/client";
// getDbExec() auto-converts ? params to $1 for Postgres
const client = getDbExec();
await client.execute({
sql: "SELECT * FROM forms WHERE owner_email = ?",
args: [email],
});
// Branch when syntax differs
const upsert = isPostgres()
? "INSERT INTO settings (key, value) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET value = $2"
: "INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)";Hosting agnostic
The server runs on Nitro, which compiles to any deployment target:
- Node.js — local dev, traditional servers
- Cloudflare Workers/Pages
- Netlify Functions/Edge
- Vercel Serverless/Edge
- Deno Deploy
- AWS Lambda
- Bun
Never use Node-specific APIs (fs, child_process, path) in server routes or plugins. These don't exist in Workers/edge environments. Actions in actions/ run in Node.js and can use Node APIs freely.
Never assume a persistent server process. Serverless and edge environments are stateless — no in-memory caches, no long-lived connections. Use the SQL database for all state.
Deep dives
For detailed guidance on specific patterns:
- What Is Agent-Native? — the vision and philosophy
- Context Awareness — navigation state, view-screen, navigate commands
- Skills Guide — framework skills, domain skills, creating custom skills
- A2A Protocol — agent-to-agent communication