---
title: "Key Concepts"
description: "How agent-native apps work: actions first, SQL database, app-agent loop, optional UI, polling sync, external-agent entry points, context awareness, and portability."
---

# Key Concepts

How agent-native apps work under the hood — the principles and the architecture. This page is the contract; for the vision and the case for building this way, see [What Is Agent-Native?](/docs/what-is-agent-native).

## The architecture {#the-architecture}

Every agent-native app is three things working together:

- **Agent** — Autonomous AI that reads data, writes data, runs actions, and modifies code. Customizable with skills and instructions.
- **Application** — The product surface around the agent. This may start as chat, add native inline results, grow into a small control plane, or become a full React UI with dashboards, flows, and visualizations.
- **Computer** — Database, browser, code execution. Agents work directly with SQL and built-in tools; MCP servers are optional add-ons, not the foundation.

<Diagram id="doc-block-t1f7pj" title="Agent, application, and computer" summary="Three layers working together over one shared SQL store. The agent and the application both read and write the same data.">

```html
<div class="diagram-arch">
  <div class="diagram-row">
    <div class="diagram-card">
      <span class="diagram-pill accent">Agent</span
      ><small class="diagram-muted"
        >reads + writes data, runs actions, modifies code</small
      >
    </div>
    <div class="diagram-card">
      <span class="diagram-pill">Application</span
      ><small class="diagram-muted"
        >chat, inline results, control plane, or full React UI</small
      >
    </div>
  </div>
  <div class="diagram-arrow diagram-muted" aria-hidden="true">
    &darr;&nbsp;&uarr;
  </div>
  <div class="diagram-box" data-rough>
    Computer<br /><small class="diagram-muted"
      >SQL database · browser · code execution</small
    >
  </div>
</div>
```

```css
.diagram-arch {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.diagram-arch .diagram-row {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  justify-content: center;
}
.diagram-arch .diagram-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px 16px;
  min-width: 220px;
}
.diagram-arch .diagram-arrow {
  font-size: 20px;
  line-height: 1;
}
.diagram-arch .diagram-box {
  text-align: center;
  padding: 12px 18px;
}
```

</Diagram>

Automation-first apps can run the same production app-agent loop from the folder with `pnpm agent`, while UI apps mount the embedded agent panel and run locally with `pnpm dev`. In the cloud, Builder.io provides a managed frame — the environment that hosts the agent next to your app — with collaboration, visual editing, and managed infrastructure for teams.

## Agent building blocks {#agent-building-blocks}

Every agent-native app has the same agent building blocks, regardless of whether
the product surface is chat-first, automation-first, or a full UI:

<FileTree
  id="doc-block-1ae57y4"
  title="Guidance and behavior"
  entries={[
    {
      path: "AGENTS.md",
      note: "always-on instructions: purpose, core rules, state keys, action index, skills index",
    },
    {
      path: ".agents/skills/<name>/SKILL.md",
      note: "reusable behavior: workflow steps, policies, examples, references, and do/don't lists",
    },
    {
      path: "actions/<name>.ts",
      note: "executable capability: typed operation exposed to the agent, UI, CLI, HTTP, MCP, A2A, jobs, and webhooks",
    },
  ]}
/>

| Building block   | Use it for                                                                                          | Loaded when                                           |
| ---------------- | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| **Instructions** | Stable guidance the agent should carry into every task: what the app is, invariants, tone, indexes  | Every turn                                            |
| **Skills**       | Reusable behavior: how to follow a workflow, apply a policy, inspect evidence, or verify an output  | On demand when the skill description matches the task |
| **Actions**      | Real operations: read or write data, call APIs, send messages, run approvals, produce typed results | Listed as tools every turn; executed only when called |

Skills and actions work together. A skill teaches the agent how to do a class of
work; an action is the code path it can call while doing that work. For example,
a `customer-research` skill might tell the agent which sources to inspect and
how to summarize evidence, while `search-crm` and `create-brief` actions fetch
and write the actual data.

Six rules govern the architecture:

1. **Data lives in SQL** — all app state lives in the database via Drizzle ORM
2. **All AI goes through the agent** — no inline LLM calls
3. **Actions for agent operations** — complex work runs as actions
4. **Live sync keeps the UI in sync** — database changes stream over SSE with polling as the universal fallback
5. **The agent can modify code** — the app evolves as you use it
6. **Application state in SQL** — ephemeral UI state lives in the database, readable by both agent and UI

## The four-area checklist {#four-area-checklist}

Every user-facing feature should update all applicable areas. Skipping an applicable area breaks the agent-native contract; forcing a screen onto an automation that no human needs to browse is also a smell.

| Area             | Description                                                    |
| ---------------- | -------------------------------------------------------------- |
| **1. UI**        | Page, component, or dialog the user interacts with             |
| **2. Action**    | Agent-callable action in actions/ for the same operation       |
| **3. Skills**    | Update AGENTS.md and/or create a skill documenting the pattern |
| **4. App-State** | Navigation state, view-screen data, and navigate commands      |

A feature with only UI is invisible to the agent. A full UI 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. An automation-first operation can legitimately start with action + instructions and add chat, UI, or app-state later when humans need to browse, approve, configure, or share it.

## Data in SQL {#data-in-sql}

All application state lives in a SQL database via Drizzle ORM. Schemas are provider-agnostic; the supported databases, `DATABASE_URL` config, and portability rules live in [Database](/docs/database).

Core SQL stores are auto-created and available in every template:

- `application_state` — ephemeral UI state (navigation, drafts, selections)
- `settings` — persistent key-value config
- `oauth_tokens` — OAuth credentials
- `sessions` — auth sessions

<DataModel
  id="doc-block-4w0fu8"
  title="Core SQL stores"
  summary={
    "Auto-created in every template — the agent and UI both read and write these."
  }
  entities={[
    {
      id: "application_state",
      name: "application_state",
      note: "Ephemeral UI state the agent reads for context",
      fields: [
        {
          name: "key",
          type: "text",
          pk: true,
          note: "e.g. 'navigation'",
        },
        {
          name: "value",
          type: "json",
          note: "view, selection, drafts",
        },
      ],
    },
    {
      id: "settings",
      name: "settings",
      note: "Persistent key-value config",
      fields: [
        {
          name: "key",
          type: "text",
          pk: true,
        },
        {
          name: "value",
          type: "json",
        },
      ],
    },
    {
      id: "oauth_tokens",
      name: "oauth_tokens",
      note: "OAuth credentials",
      fields: [
        {
          name: "provider",
          type: "text",
          pk: true,
        },
        {
          name: "token",
          type: "text",
        },
      ],
    },
    {
      id: "sessions",
      name: "sessions",
      note: "Auth sessions",
      fields: [
        {
          name: "id",
          type: "text",
          pk: true,
        },
        {
          name: "userId",
          type: "text",
        },
      ],
    },
  ]}
/>

```ts
// Drizzle schema for domain data
import { table, text, integer } from "@agent-native/core/db/schema";

export const forms = table("forms", {
  id: text("id").primaryKey(),
  title: text("title").notNull(),
  schema: text("schema").notNull(), // JSON
  ownerEmail: text("owner_email"),
  createdAt: integer("created_at").notNull(),
});
```

```bash
# Core actions for quick database inspection
pnpm action db-schema                                       # show all tables
pnpm action db-query --sql "SELECT * FROM forms"
```

The production agent chat plugin defaults raw SQL tools to read-only
(`databaseTools: "read"`), so agents inspect app-owned data with `db-schema` /
`db-query` and perform writes through typed app actions. Set
`databaseTools: "write"` (or `true`) only for deliberate maintenance surfaces
that should also expose scoped `db-exec` / `db-patch`; set
`databaseTools: "off"` / `false` to require typed app actions for all data
access.

## Agent chat bridge {#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.

```ts
// In a React component — delegate AI work to the agent
import { sendToAgentChat } from "@agent-native/core/client/agent-chat";
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.
- **External execution.** Because everything goes through the agent and actions, any app can be driven from Slack, Telegram, scheduled jobs, scripts, or another agent via [A2A](/docs/a2a-protocol).

## Actions system {#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 `defineAction()`:

```ts filename="actions/fetch-data.ts"
import { defineAction } from "@agent-native/core/action";
import { z } from "zod";

export default defineAction({
  description: "Fetch data from a source API.",
  schema: z.object({
    source: z.string().describe("Data source key, e.g. 'signups'"),
  }),
  run: async ({ source }) => {
    const res = await fetch(`https://api.example.com/${source}`);
    return await res.json();
  },
});
```

One `defineAction()` call gives you:

- **Agent tool** — the agent sees it with the zod-derived JSON Schema and can call it.
- **Frontend hook** — `useActionMutation("fetch-data")` with full TypeScript inference.
- **Framework transport** — auto-mounted behind the client hooks.
- **CLI** — `pnpm action fetch-data --source=signups` for scripting and agent dev loops.
- **MCP tool / A2A tool** — when MCP server or A2A is enabled, the same action shows up there too.

Same logic, one definition, wired to every consumer automatically. See [Actions](/docs/actions) for the full reference.

## Live sync {#polling-sync}

Database changes are synced to the UI through `useDbSync()`. Same-process writes stream over `/_agent-native/events`; `/_agent-native/poll` remains the cross-process and serverless fallback. When the agent writes to the database (application state, settings, or domain data), a version counter increments and the client invalidates the relevant React Query caches.

```ts
// Client: subscribe to agent/UI data changes once near the app shell
import { useDbSync } from "@agent-native/core/client/hooks";
useDbSync({ queryClient });
```

The flow is:

1. Agent runs an action that writes to the database
2. The server emits a change event with a source such as `"action"` or `"settings"`
3. `useDbSync` receives it over SSE or the polling fallback
4. `useActionQuery` hooks and source-versioned `useQuery` hooks refetch
5. Components render the new data without a page reload

<Diagram id="doc-block-87e1c3" title="Live sync flow" summary={"An agent write becomes a UI render with no manual refresh — SSE first, polling as the universal fallback."}>

```html
<div class="diagram-sync">
  <div class="diagram-node">
    Agent action<br /><small class="diagram-muted">writes to DB</small>
  </div>
  <div class="diagram-arrow diagram-muted" aria-hidden="true">&rarr;</div>
  <div class="diagram-node">
    Change event<br /><small class="diagram-muted"
      >source: action / settings</small
    >
  </div>
  <div class="diagram-arrow diagram-muted" aria-hidden="true">&rarr;</div>
  <div class="diagram-panel center">
    <span class="diagram-pill accent">useDbSync</span
    ><small class="diagram-muted">SSE &middot; poll fallback</small>
  </div>
  <div class="diagram-arrow diagram-muted" aria-hidden="true">&rarr;</div>
  <div class="diagram-node">
    Query refetch<br /><small class="diagram-muted">render, no reload</small>
  </div>
</div>
```

```css
.diagram-sync {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.diagram-sync .diagram-arrow {
  font-size: 22px;
  line-height: 1;
}
.diagram-sync .center {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 10px 14px;
}
```

</Diagram>

This works in all deployment environments — including serverless and edge — because it uses the database, not in-memory state or file system watchers.

## Frames {#frames}

A _frame_ is the environment that hosts the agent next to your app — locally that's the embedded panel; in the cloud it's Builder.io's managed surface. See [Frames](/docs/frames).

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.

- **Embedded Agent Panel** — Chat and optional CLI terminal built into every app. Supports Claude Code, Codex, Gemini, OpenCode, and Builder.io. Runs locally. Free and open source.
- **Cloud** — Deploy to any cloud with real-time collaboration, visual editing, roles and permissions. Best for teams.

## Context awareness {#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.

For example, when you open an email thread the UI upserts a row like:

```json
{ "key": "navigation", "value": { "view": "thread", "threadId": "th_abc123" } }
```

The UI writes this on route change; the agent reads it (via `view-screen`) before taking any action, so it always knows which thread — or chart, or slide — you're focused on.

See [Context Awareness](/docs/context-awareness) for the full pattern: navigation state, view-screen, navigate commands, and jitter prevention.

## One action, many surfaces {#protocols}

Implement a domain operation once as an action; the framework exposes it to every consumer. The same `defineAction()` becomes an agent tool, a typesafe UI hook, an HTTP endpoint, a CLI command, an MCP tool, and an A2A tool, with optional `link`, `mcpApp`, native-widget metadata, or Generative UI wrappers added only when a surface needs richer interaction. Skills and instructions cover behavior.

For the full protocol/surface matrix (MCP server and OAuth, MCP Apps, A2A, deep links, native chat widgets, Generative UI, AgentChatRuntime connectors, Agent Web, and the adapter horizon for ACP and A2UI), and for choosing a product shape — chat, inline UI, full app pages, embedded sidecar, automation, or external-agent access — see [Agent Surfaces](/docs/agent-surfaces).

## Agent modifies code {#agent-modifies-code}

This is a feature, not a bug. 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:

1. Start from a template (e.g. the analytics template)
2. Customize it by asking the agent
3. "Add a new chart type for cohort analysis" — the agent builds it
4. "Connect to our Stripe account" — the agent writes the integration
5. Your app keeps improving without manual development

## Portable by default {#hosting-agnostic}

Two architectural rules keep apps portable across databases and hosts:

- **Database-agnostic.** Write schemas with `@agent-native/core/db/schema` and reads/writes with Drizzle's portable query DSL so the same code runs on any supported provider. Use raw SQL only for additive migrations or one-off maintenance, kept parameterized and dialect-agnostic. See [Database](/docs/database).
- **Hosting-agnostic.** The server runs on Nitro and compiles to any deployment target. Never use Node-specific APIs (`fs`, `child_process`, `path`) in server routes or plugins, and never assume a persistent server process — serverless and edge are stateless, so keep all state in SQL. See [Deployment](/docs/deployment).

## Agent Resources {#workspace}

Every user gets a personal set of **agent resources** — instructions, skills, memory, custom sub-agents, scheduled jobs, and connected MCP servers — all stored in SQL rather than files. That makes Claude-Code-level customization viable inside multi-tenant SaaS without spinning up a container per user. See [Agent Resources](/docs/agent-resources).

## Related building blocks {#building-blocks}

These sit on top of the same contract and have their own deep dives:

- **[Dispatch](/docs/dispatch)** — the workspace control plane: shared inbox, secrets vault, scheduled jobs, and an orchestrator that delegates to specialist apps over A2A.
- **[Extensions](/docs/extensions)** — sandboxed Alpine.js mini-apps the agent creates at runtime, no source changes or migrations.
- **[A2A Protocol](/docs/a2a-protocol)** — how apps in the same workspace discover and call each other over JSON-RPC.

## What you get for free {#what-you-get-for-free}

Adopting the framework is valuable mostly because of what you stop having to build. The moment your app follows the six rules, you inherit:

- **One action = every surface.** Every action defined with `defineAction()` is simultaneously an agent tool, a typesafe frontend hook (`useActionQuery` / `useActionMutation`), a framework-owned HTTP transport, a CLI command, an MCP tool for external clients, and an A2A tool for other agent-native apps. Optional `link` and `mcpApp` metadata add deep links and MCP Apps UI without a second implementation.
- **A full set of agent resources per user.** Skills, shared `LEARNINGS.md`, personal `memory/MEMORY.md`, `AGENTS.md`, custom sub-agents, scheduled jobs, connected MCP servers — all SQL-backed, no dev-box required. See [Agent Resources](/docs/agent-resources).
- **Drop-in React components.** `<AgentPanel />` and `<AgentSidebar />` render chat + resources anywhere in your app. See [Drop-in Agent](/docs/drop-in-agent).
- **BYO agent chat runtimes.** The same chat UI can sit on top of OpenAI Agents, OpenAI Responses, Claude Agent SDK, Vercel AI SDK, AG-UI, or your own normalized HTTP stream. See [Native Chat UI](/docs/native-chat-ui#byo-agent-runtimes).
- **Live sync between agent and UI.** Same-process writes stream immediately over `/_agent-native/events`; a lightweight poll keeps serverless, cron, and cross-process writes convergent. Mutating actions invalidate action-backed queries automatically, so agent-created records appear without a manual refresh. See [Live Sync](#polling-sync) below.
- **Auth, orgs, RBAC.** Better Auth with orgs/members/roles is wired in for every template. See [Authentication](/docs/authentication). Building for more than one user? Start with [Organizations, Teams & Permissions](/docs/organizations-teams-permissions).
- **Context awareness.** The agent always knows what the user is looking at through the `navigation` app-state key. See [Context Awareness](/docs/context-awareness).
- **MCP client + server, both directions.** The app ingests MCP servers (local, remote, hub-shared) _and_ exposes its own actions as an MCP server. See [MCP Clients](/docs/mcp-clients) and [MCP Protocol](/docs/mcp-protocol).
- **Inter-app delegation.** Agents in different apps talk over [A2A](/docs/a2a-protocol). Same-origin deploys skip JWT; cross-origin uses a shared `A2A_SECRET`.
- **Sub-agent teams.** Spawn a sub-agent with its own thread and tools, surfaced as a chip inline in chat. See [Agent Teams](/docs/agent-teams).
- **Portability.** Any Drizzle-supported SQL database, any Nitro-compatible host (Node, Workers, Netlify, Vercel, Deno, Lambda, Bun).

That's the "and everything else" you'd otherwise be gluing together yourself.

## What's next {#deep-dives}

- [**What Is Agent-Native?**](/docs/what-is-agent-native) — the vision and philosophy behind these rules
- [**Context Awareness**](/docs/context-awareness) — navigation state, view-screen, and navigate commands in depth
- [**Skills Guide**](/docs/skills-guide) — framework skills, domain skills, and creating custom skills
- [**Native Chat UI**](/docs/native-chat-ui) — action-declared tables, charts, and BYO runtime posture
- [**Agent Surfaces**](/docs/agent-surfaces) — chat, native inline UI, full app pages, embedded sidecar, automation, and external-agent paths
- [**A2A Protocol**](/docs/a2a-protocol) — agent-to-agent communication
