---
title: "Agent Surfaces"
description: "Choose how an agentic app grows from chat to inline UI, durable app pages, embedded sidecars, automation, and external agent access."
search: "agentic app rich chat native chat UI full app automation headless BYO agent runtime AgentChatRuntime embed actions MCP A2A HTTP CLI"
---

# Agent Surfaces

A **surface** is the way users (or other systems) interact with your app: a chat window, a dashboard page, a background job, an API call from another agent. Agent-Native lets you mix and match these without rebuilding your core logic, because every surface runs the same underlying actions. If you're new to Agent-Native, read [Key Concepts](/docs/key-concepts) first.

## How surfaces relate to each other

The four main product shapes sit on a spectrum from most interactive to fully headless. What makes them composable is that they share the same actions and SQL-backed app state. Use the framework agent loop when a surface needs an agent. Adding a new surface doesn't mean rewriting what's underneath — you're adding a new way to reach the same operations.

<Diagram id="doc-block-hvdxgh" title="The surface spectrum" summary={"One action surface, four product shapes, each adding UI without changing the operation underneath."}>

```html
<div class="diagram-spectrum">
  <div class="diagram-card">
    <strong>Chat</strong
    ><small class="diagram-muted">composer, transcript, tool calls</small>
  </div>
  <div class="diagram-arrow diagram-muted" aria-hidden="true">&rarr;</div>
  <div class="diagram-card">
    <strong>Inline UI</strong
    ><small class="diagram-muted">tables, charts, cards</small>
  </div>
  <div class="diagram-arrow diagram-muted" aria-hidden="true">&rarr;</div>
  <div class="diagram-card">
    <strong>App page</strong
    ><small class="diagram-muted">durable screens, SQL data</small>
  </div>
  <div class="diagram-arrow diagram-muted" aria-hidden="true">&rarr;</div>
  <div class="diagram-card accent-card">
    <span class="diagram-pill accent">headless</span><strong>Automation</strong
    ><small class="diagram-muted">jobs, scripts, external agents</small>
  </div>
</div>
<div class="diagram-base" data-rough>
  <span class="diagram-muted"
    >same actions · SQL-backed state · agent loop where used</span
  >
</div>
```

```css
.diagram-spectrum {
  display: flex;
  align-items: stretch;
  gap: 10px;
  flex-wrap: wrap;
}
.diagram-spectrum .diagram-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px 16px;
  min-width: 150px;
  flex: 1;
}
.diagram-spectrum .diagram-arrow {
  align-self: center;
  font-size: 22px;
  line-height: 1;
}
.diagram-base {
  margin-top: 12px;
  padding: 10px 14px;
  text-align: center;
}
```

</Diagram>

## Choose a starting point

Chat is the most common entry point. Apps typically grow inline UI as output gets richer, then add full app pages when users need persistent objects to browse and share. The same actions power the buttons, scheduled jobs, and external agents that come later. Use the Embedded sidecar when adding an agent to a product you already own, or Automation-first for work that runs without a browser. Here's the full picture:

| Surface                                         | Use it when                                                                    | Start with                                                            |
| ----------------------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
| **[Rich chat](#rich-chat)**                     | Users talk to the agent, see tool calls, and keep a thread history.            | [Chat template](/docs/template-chat), `<AgentChatSurface>`            |
| **[Native inline UI](#native-inline-ui)**       | Action results should render as tables, charts, cards, or approvals in chat.   | [Native Chat UI](/docs/native-chat-ui), `chatUI.renderer`             |
| **[Generated inline UI](#generated-inline-ui)** | The agent should create temporary or reusable controls inside chat on the fly. | [Generative UI](/docs/generative-ui), `render-inline-extension`       |
| **[Full application](#full-application)**       | Users need durable screens, shared data, navigation, and collaboration.        | Templates, actions, SQL state, context awareness                      |
| **[Embedded sidecar](#embedded-sidecar)**       | You already have a SaaS app and want an agent beside it with page context.     | `createAgentNativeEmbeddedPlugin()`, `AgentNativeEmbedded`            |
| **[Automation-first](#headless)**               | Jobs, scripts, or other agents call the work directly with no browser UI.      | `agent-native create --headless`, `defineAction`, HTTP, CLI, MCP, A2A |
| **[Rich chat on your agent](#byo-agent)**       | You built the agent elsewhere and want Agent-Native's chat UI around it.       | `AgentChatRuntime`, `<AssistantChat runtime={runtime}>`               |

## Rich chat on Agent-Native {#rich-chat}

Use the built-in chat when the user should talk to the agent, see tool calls,
approve work, inspect native results, and keep a durable thread history.

For a full app starting point, use the [Chat template](/docs/template-chat):

```bash
npx --yes @agent-native/core@latest create my-chat-app --template chat
```

The simplest full-page chat:

```tsx
import { AgentChatSurface } from "@agent-native/toolkit/app/chat";

export default function ChatRoute() {
  return <AgentChatSurface mode="page" className="h-screen" />;
}
```

When an app has both a full-page chat tab and an `AgentSidebar`, use the same
`storageKey` on both surfaces, enable `chatViewTransition`, and install the
chat-home handoff helpers in the layout. Ordinary in-app links out of the chat
page can then morph the full chat into the sidebar while keeping the active
thread:

```tsx
import { AgentChatSurface, AgentSidebar } from "@agent-native/toolkit/app/chat";
import {
  useAgentChatHomeHandoff,
  useAgentChatHomeHandoffLinks,
} from "@agent-native/core/client/agent-chat";
import { useLocation } from "react-router";

function ChatRoute() {
  return (
    <AgentChatSurface mode="page" storageKey="my-app" chatViewTransition />
  );
}

function AppLayout({ children }: { children: React.ReactNode }) {
  const location = useLocation();
  const handoffActive = useAgentChatHomeHandoff({
    storageKey: "my-app",
    activePath: location.pathname,
    enabled: location.pathname !== "/chat",
  });
  useAgentChatHomeHandoffLinks({ storageKey: "my-app", chatPath: "/chat" });

  return (
    <AgentSidebar
      storageKey="my-app"
      chatViewTransition
      openOnChatRunning={handoffActive}
    >
      {children}
    </AgentSidebar>
  );
}
```

The simplest embedded chat with your own chrome:

```tsx
import { AssistantChat } from "@agent-native/toolkit/app/chat";

export function ProjectChat({ threadId }: { threadId: string }) {
  return <AssistantChat threadId={threadId} />;
}
```

Actions can return explicit native widget results so chat output is not just
text. Tables, charts, and typed product cards render as first-party React
components in the chat, without iframes. See [Native Chat UI](/docs/native-chat-ui).
When the agent needs arbitrary generated controls instead of a predefined
React widget, use [Generative UI](/docs/generative-ui): it renders sandboxed
Alpine/Tailwind UI inline, can read app state and slot context, and can send
selected values back to chat.

### Request-scoped action surfaces

Use `resolveActionSurface` when the selected agent or thread must expose only a
server-authorized subset of native actions. The callback runs for every
interactive chat request after `prepareRequest`. Returned names form a hard
allowlist for that request: omitted actions are absent from provider schemas,
the execution registry, plan-mode preloading, and `tool-search` discovery.

```ts
createAgentChatPlugin({
  actions,
  nativeActionsInDev: true,
  resolveActionSurface: async ({ threadId, availableActionNames }) => ({
    allowedActionNames: await loadAllowedActions(
      threadId,
      availableActionNames,
    ),
  }),
});
```

When the same resolver also handles unrestricted chat, return
`{ mode: "default" }`. This preserves the normal `initialToolNames` filtering
and `tool-search` discovery instead of loading every available schema on the
first model request. The explicit mode is preserved across durable
continuations.

An empty list exposes no native actions. Unknown names fail the request instead
of widening access. Every allowed action is loaded directly on the first model
request; include `tool-search` explicitly only when discovery inside the
already-authorized catalog is wanted. The callback scopes interactive agent
chat only—it does not change HTTP, MCP, A2A, job, or trigger exposure. Default
framework guidance and spawned sub-agents inherit the same surface. Trusted
shell tools are automatically reduced to the request-filtered sandbox because
an unrestricted shell cannot enforce a hard action boundary.

### Request-scoped additional actions

Use `resolveAdditionalActions` when the actions a caller can use are only known
at request time, such as tools a user registers while the server runs. The
callback runs once per authenticated interactive chat run (production and dev),
before `tool-search` and `resolveActionSurface`, so both see the returned
entries. They join that caller's registry for that run only. A2A tasks and MCP
`ask_app` calls receive them only when you list those surfaces in
`additionalActionSurfaces`; `surface` tells the callback which one is asking.

```ts
createAgentChatPlugin({
  actions,
  additionalActionSurfaces: ["a2a"],
  resolveAdditionalActions: async ({ surface, ownerEmail, orgId }) =>
    loadUserTools(ownerEmail, orgId, {
      includeEditorTools: surface === "chat",
    }),
});
```

Treat returned entries as untrusted input. The framework ignores an entry, and
logs a warning once, when its name is not 1–64 letters, digits, `_`, or `-`;
when the name belongs to any template, framework, dev, or MCP action, including
actions hidden from the agent; when it is an `mcp__` name the caller cannot
see; or when its tool definition is unusable. Descriptions lose hidden control
characters and are capped. `parameters` must be a bounded schema of plain JSON
data with an `object` root, using only common validation and annotation
keywords. Unknown keywords, `pattern`, `patternProperties`, `uniqueItems`,
`$async`, `$id`, anchors, and any `$ref` that is recursive or points outside
the schema are refused. The model sees the checked copy of the schema, and a
call whose arguments are too large to validate against it is refused. Flags such as
`readOnly` and `access` are your app's own declarations, which the framework
trusts as it trusts `run`: set them from what your code does, never from
metadata a user registered.

Each entry is bound to the caller it was resolved for. Every call, whichever
dispatcher makes it, refuses to run for a different user or organization,
enforces the entry's `access` policy, and validates the arguments against its
schema without type coercion.

Names are pinned only when a hosted chat turn continues as a durable background
run. The callback runs again, and the turn keeps its original names for these
entries and the caller's MCP tools alike: new entries stay out, a name that
disappeared returns an error when called, and a name that remains runs its
current definition. Every other request resolves fresh, including each
continuation on a local sidecar, and a dropped stream that reattaches to its run
resolves nothing.

Put the binding's identity in the name, such as a window id or scope
(`notes_window_42_insert`), so a rebind reads as one tool leaving and another
appearing. "Always allow" approvals follow the tool name, so they carry over
when the same name later means something new.

Set `revision` on an entry to stop that: one-time approvals and "Always allow"
record the revision they were given for, and a different revision asks the user
again. A custom "Always allow" handler should pass the approval's `askId` to
`set-tool-approval-policy` as `approvalId`; without it, the grant covers only
entries that have no revision.

A call to a tool the conversation used or loaded earlier, but that this run no
longer has, fails with `Error: Tool no longer available` instead of
`Unknown tool`, and a run that starts after loaded tools went away tells the
model which ones. If the user had approved that call, the run stops rather than
letting the model continue without it. For chat, the callback also receives
`threadId`, `requestedTurnId`, and `internalContinuation`, so an app that
wants one tool set for a whole turn can keep its own snapshot.

These entries are native tools for the agent loop only. Like the caller's MCP
tools, they aren't reachable from `run-code` or `tool-orchestration`.

`event` is set for `chat` and `a2a`; `ask_app` calls have none. The callback
is not called for anonymous requests, scheduled jobs, automations, or agent-team
sub-agents. An error the callback throws fails the request.

## Native inline UI {#native-inline-ui}

Use this when your actions return structured data — a list of records, a chart dataset, a status summary — that should render as a real UI component inside the chat thread rather than a plain text description. You define a `chatUI` renderer on the action, and Agent-Native renders it as a first-party React component: no iframes, no separate rendering path.

This is the right choice when the output has a clear, reusable shape that you'd design once and use across many agent responses. For controls the agent needs to create dynamically at runtime, see [Generated inline UI](#generated-inline-ui) instead.

See [Native Chat UI](/docs/native-chat-ui) for the full renderer API, widget library, and BYO agent runtime integration.

## Generated inline UI {#generated-inline-ui}

Use this when the agent needs to create a control that doesn't exist yet as a pre-built widget — a custom form, a picker built around the current context, a one-off calculator. Unlike native widgets, generated UI is composed by the agent at runtime from Alpine.js and Tailwind, runs sandboxed in an iframe, and can send selected values back into the chat thread.

Generated UI can be transient (rendered once and discarded) or saved as a reusable extension that persists for the user.

See [Generative UI](/docs/generative-ui) for the full API, sandbox constraints, and extension persistence model.

## Full application {#full-application}

Use the full app path when users need durable objects and workflows: forms,
dashboards, calendars, inboxes, editors, documents, assets, or reports.

Full apps add product UI around the same action and agent contract:

<Cards>

### SQL state

App data, navigation, settings, and chat history are durable in SQL. The agent reads and writes the same rows the UI does.

### Context awareness

The agent knows the current route, selection, and focused object, so "edit this" always means the right thing.

### Live sync

Agent changes update the UI through live sync, and UI changes update the agent's context. The framework uses SSE with polling fallback, so users do not need to refresh manually.

### Deep links

Action results can open the right app view directly: a chart links to the dashboard, a draft links to the inbox.

### Native chat widgets

Tables, charts, cards, approvals, and typed results render as first-party React components inline in chat.

### Generative UI and extensions

The agent can create inline controls on the fly, and save reusable mini-apps when a workflow needs to persist.

</Cards>

Start from the [Chat template](/docs/template-chat) when you want a minimal app
around your actions, or from a domain [template](/docs/cloneable-saas) when you
want a complete product shape.

### Full-page Manage Agent {#agent-page}

Every Agent-Native app eventually needs a place where users can configure their agent: set standing instructions, review what it's done, connect MCP servers, manage automations, and control access. Building that UI from scratch is a lot of work. Agent-Native ships a pre-built full-page component, `AgentTabsPage`, that covers all of it across twelve tabs.

First-party templates don't mount it: Settings covers the same ground (Agent › Model, Instructions, Memory, Skills, Files, and Sub-agents, plus the app's Automations and MCP server pages), `/agent` redirects into Settings, and `agentPageHref="/settings/agent"` on `AgentSidebar` opens Settings › Agent › Model. Mount `AgentTabsPage` on its own route when an app wants the whole agent surface on one page.

```tsx filename="app/routes/agent.tsx"
import { AgentTabsPage } from "@agent-native/toolkit/app/agent-page";
export default function AgentRoute() {
  return <AgentTabsPage />;
}
```

The shared page currently provides twelve tabs across two groups:

| Group     | Tab               | Shows                                                                                                                                                                                                                                                   |
| --------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Resources | **Files**         | The existing `ResourcesPanel` for personal or organization files                                                                                                                                                                                        |
| Resources | **Instructions**  | Always-on AGENTS.md-style rules                                                                                                                                                                                                                         |
| Resources | **Agents**        | Custom sub-agent profiles                                                                                                                                                                                                                               |
| Resources | **Memory**        | Long-term recall notes                                                                                                                                                                                                                                  |
| Resources | **Skills**        | Reusable workflows                                                                                                                                                                                                                                      |
| Resources | **Learnings**     | Corrections and patterns captured over time                                                                                                                                                                                                             |
| Resources | **Remote agents** | A2A connections to other agent-native apps (replaces what the Connections tab used to show)                                                                                                                                                             |
| Agent     | **Snapshots**     | A scope preview, token budget, ordered system sections grouped by provenance/governance/source, and the latest live-thread snapshot. Renamed from "Context"; old `#context` links redirect here.                                                        |
| Agent     | **Connections**   | MCP server management only                                                                                                                                                                                                                              |
| Agent     | **Automations**   | Personal and organization Scheduled/Event automations with pause/resume, details, and delete flows. The stable compatibility URL remains `/agent#jobs`.                                                                                                 |
| Agent     | **Settings**      | Agent model, API keys, limits, voice, and automation settings                                                                                                                                                                                           |
| Agent     | **MCP**           | The app MCP URL, an A2A agent card when available, and shared setup guides for Claude, ChatGPT, Cursor, Claude Code, Codex, and other clients. Find it in **Settings → MCP server** or use `/mcp/connect` for the full connect flow and token fallback. |

The page shows personal (`user`-scope) data only. There is no org-level toggle today. It is a thin shell over existing components and access checks, not a new admin console: Connections describes what the app can call; MCP describes how external clients connect to it.

Not yet included:

- Organization-scoped view
- Grants and scope editing
- Revocation UI
- Per-iteration provenance history

## Embedded sidecar {#embedded-sidecar}

Use the embedded sidecar when the main product already exists and you want an
agent beside it.

The server plugin mounts Agent-Native routes into your host app and resolves
host identity server-side:

```ts
import { createAgentNativeEmbeddedPlugin } from "@agent-native/core/server";

export default createAgentNativeEmbeddedPlugin({
  databaseUrl: process.env.AGENT_NATIVE_DATABASE_URL,
  auth: getHostSession,
  actions: hostActions,
});
```

The React sidecar passes page context and host commands:

```tsx
import { AgentNativeEmbedded } from "@agent-native/core/client/host";
export function AppShell({ children }) {
  return (
    <AgentNativeEmbedded
      getContext={() => ({
        route: { pathname: window.location.pathname },
        selection: { text: window.getSelection()?.toString() || undefined },
      })}
      onNavigate={(payload) =>
        router.navigate((payload as { path: string }).path)
      }
      onRefresh={() => queryClient.invalidateQueries()}
    >
      {children}
    </AgentNativeEmbedded>
  );
}
```

### How it connects

The two pieces work as a bridge: the host app passes page context (current route, selected text, focused object) into `AgentNativeEmbedded`, and the agent sends commands back out through `onNavigate` and `onRefresh`. The server plugin handles identity — it resolves the host session so the agent acts as the right user without a separate login. Nothing in the host app needs to change; the plugin attaches Agent-Native's routes alongside your existing ones.

<Diagram id="doc-block-27ug5y" title="How the sidecar bridges to a host app" summary={"The plugin mounts Agent-Native routes server-side; the React sidecar streams page context in and host commands out."}>

```html
<div class="diagram-sidecar">
  <div class="diagram-panel">
    <strong>Host app</strong
    ><small class="diagram-muted">your existing SaaS</small>
    <div class="diagram-node">
      getContext()<br /><small class="diagram-muted">route · selection</small>
    </div>
    <div class="diagram-node">
      onNavigate / onRefresh<br /><small class="diagram-muted"
        >host commands</small
      >
    </div>
  </div>
  <div class="diagram-col-arrows">
    <div class="diagram-arrow diagram-muted" aria-hidden="true">&rarr;</div>
    <div class="diagram-arrow diagram-muted" aria-hidden="true">&larr;</div>
  </div>
  <div class="diagram-panel accent-panel">
    <span class="diagram-pill accent">AgentNativeEmbedded</span
    ><small class="diagram-muted">agent + resources</small>
    <div class="diagram-box" data-rough>
      Agent-Native routes<br /><small class="diagram-muted"
        >mounted by the server plugin</small
      >
    </div>
  </div>
</div>
```

```css
.diagram-sidecar {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.diagram-sidecar .diagram-panel {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px;
  min-width: 200px;
}
.diagram-sidecar .diagram-col-arrows {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.diagram-sidecar .diagram-arrow {
  font-size: 22px;
  line-height: 1;
}
```

</Diagram>

See [Embedding SDK](/docs/embedding-sdk) for host auth, database isolation,
iframe/picker mode, and lower-level bridge APIs.

## Automation-first app {#headless}

Use the automation-first path when no one needs a custom browser screen while
the work runs: scheduled jobs, integrations, backend workflows, CLI loops,
another agent, or an existing product calling into Agent-Native.

This is the shape to reach for when automation is the product surface. You send a request from the terminal, Slack, email, a scheduled job, another agent, or Chat ("summarize my unread emails," "post the daily metrics to Slack," "find the candidates who replied last week") and the agent acts and returns the result wherever it belongs. It is still a real app, not a stateless prompt:
actions, auth sessions, app state, thread/run history, settings, credentials,
and share records all live in SQL.

Pick this pattern when:

- **The work happens in the background.** Most of the value is created while the user isn't looking: triage agents, daily-report agents, on-call responders.
- **The output leaves the app.** The agent posts to Slack, sends email, or updates a third-party system; there's nothing to browse in-app.
- **The domain is one-shot.** Research bot, summary generator, report writer with no persistent object that needs a list view.
- **You're prototyping an automation.** Ship the operation now; add chat or app pages when users need to inspect and steer it.

If your product is built around persistent objects users browse, pivot, and share (emails, events, documents, charts), pick a [full application](#full-application) or a [template](/docs/cloneable-saas) instead; those add a full UI _plus_ the agent.

### What ships in the box {#in-the-box}

An automation-first app skips dashboard work, and it is channel-agnostic from day one. The same agent runs from the web, Slack, Telegram, email, and other agents because everything goes through the same actions. The trade-off is there is no "browse-everything-at-a-glance" view; if users need that, start from [Chat](/docs/template-chat) or add a small status page or list view.

When you add the built-in Chat shell, the framework provides five management surfaces you don't have to build: **Chat** (the main input), **Resources** (skills, memory, instructions, sub-agents, and connected MCP servers), **Automations**, **Thread history**, and **Settings**. Those are usually enough: talk to it, see what it's done, configure how it behaves. Reach for
[Chat](/docs/template-chat) when you're ready to add that browser UI, or the
[Dispatch template](/docs/template-dispatch) for a workspace-style starting
point with Slack/Telegram, scheduled jobs, and shared secrets out of the box.

The smallest no-browser local path is a scaffold plus one action:

```bash
npx --yes @agent-native/core@latest create my-agent --headless
cd my-agent
pnpm install
```

Then define the durable operation:

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

// One action powers every app surface: UI, agent, HTTP, MCP, A2A, and CLI.
export default defineAction({
  description: "Summarize this week's submissions.",
  readOnly: true,
  schema: z.object({ formId: z.string() }),
  run: async ({ formId }) => {
    return { formId, summary: "34 submissions, up 18% from last week." };
  },
});
```

One action is then callable as:

- **HTTP:** `POST /_agent-native/actions/summarize-week`
- **CLI:** `pnpm action summarize-week --formId form_123`
- **App-agent CLI:** `pnpm agent "Summarize form_123"`
- **MCP:** from Claude, ChatGPT, Codex, Cursor, OpenCode, Copilot, and other MCP hosts
- **A2A:** from another agent-native app or agent peer
- **UI:** through `useActionQuery`, `useActionMutation`, or `callAction`
- **Agent tool:** from the built-in chat loop

<Endpoint id="doc-block-kdx289" title="Calling an action over HTTP" method="POST" path="/_agent-native/actions/summarize-week" summary="Invoke any action by name over HTTP" request={{
  "contentType": "application/json",
  "example": "{ \"formId\": \"form_123\" }"
}} responses={[
  {
    "status": "200",
    "description": "The action's return value as JSON",
    "example": "{ \"formId\": \"form_123\", \"summary\": \"34 submissions, up 18% from last week.\" }"
  },
  {
    "status": "400",
    "description": "Input failed schema validation"
  }
]}>

Every `defineAction` is auto-mounted at `/_agent-native/actions/<name>`. The JSON body is validated against the action's zod schema before `run` executes. To call it from an external system with a long-lived bearer token, see [HTTP API](/docs/http-api).

</Endpoint>

This is not a no-database or stateless mode. The app-agent loop stores sessions,
threads, runs, settings, credentials, application state, and share records in
SQL. Local development uses PGlite; hosted automation-first apps should use a
persistent PostgreSQL database.

If you need the whole agent loop headlessly from the project folder, use:

```bash
pnpm agent "Summarize this week's forms."
```

If another app or script needs to call the whole agent, use
`agentNative.invoke("analytics", "...")` or the `agent-native invoke` CLI. That
keeps cross-app work on the A2A path while local work stays on actions.

Workers, jobs, integration webhooks, and custom hosts can drive the agent loop
directly through the server API. This is lower-level than actions — you provide
the engine, model, messages, tools, actions, an event sink, and an abort signal
yourself:

```ts
import { runAgentLoop } from "@agent-native/core/server";

await runAgentLoop({
  engine,
  model,
  systemPrompt,
  tools,
  actions,
  messages,
  send,
  signal,
});
```

For most apps, scheduled prompts and integration webhooks already call this loop
for you. Reach for it directly only when building a custom no-browser host, eval
runner, or server-side orchestration surface. See [Server: Production agent handler](/docs/server-plugins#agent-chat-plugin) for the full signature.

### Running against a folder {#folder-loop}

If your goal is "run an agent against this folder," start with the app-agent
loop in that folder: scaffold the automation-first app, add actions/instructions, run
`pnpm agent "..."`. That keeps the work inside the same action/runtime/state
contract the app will use in production.

External coding harnesses are a separate product surface for embedding Claude
Code, Codex, Pi, Cursor, Mastra, or similar runtimes inside an Agent-Native app.
Use them when you are building a coding-agent product, not as the default way to
start a local agent-native workflow.

### Cloud repo access {#cloud-repo-access}

For cloud automation-first apps that need repository access, use the GitHub connector
plus token CRUD model: list repositories, search files, read files, create or
edit files, delete files, and revoke access through provider-scoped
credentials. In local development, set the target repository explicitly:

```bash
GITHUB_REPOSITORY=owner/repo pnpm agent "Read README.md and suggest the next action."
```

Do not treat a VM clone or long-lived sandbox checkout as the primary cloud
repo-access model. Sandboxes still matter for isolated code execution, but
repository access should be explicit, permissioned, auditable, and revocable
through the connector layer.

### Sharing sessions and runs {#sharing-runs}

Automation-first sessions and runs are durable objects. Shareability should be
phased: read/share links first, so teammates can inspect sanitized prompts,
outputs, and run status; permissioned writable collaboration later, so
continuing a run, approving actions, editing schedules, or changing
configuration goes through explicit access checks.

## Rich chat on your agent {#byo-agent}

Use this path when your agent is already built with another framework or
runtime and you want Agent-Native's chat UI around it. `AgentChatRuntime` is the
boundary: your runtime streams normalized events, and Agent-Native renders the
composer, transcript, tool calls, approvals, native widgets, and app layout.

```tsx
import { AssistantChat } from "@agent-native/toolkit/app/chat";
import { createHttpAgentChatRuntime } from "@agent-native/core/client/agent-chat";

const runtime = createHttpAgentChatRuntime({
  endpoint: "/api/support-agent/chat",
});

export function SupportAgentChat() {
  return <AssistantChat runtime={runtime} threadId="support" />;
}
```

Ready-made runtime helpers exist for OpenAI Agents, OpenAI Responses, the Claude
Agent SDK, the Vercel AI SDK, and AG-UI, plus the normalized HTTP runtime above
for any other agent (Mastra, Flue, Eve, LangGraph, or a custom service). ACP is
not the end-user app chat or A2A transport, and Agent-Native does not currently
claim A2UI support. ACP is supported in one specific place: driving a local
coding agent (Gemini CLI, Claude Code, …) through the
[harness layer](/docs/harness-agents#acp), not as the chat runtime here.

[Native Chat UI: BYO agent runtimes](/docs/native-chat-ui#byo-agent-runtimes)
is the canonical home for the event shapes, the runtime helpers, and `chatUI`
tool-result metadata. Start there when wiring an external agent into the chat.

## What's next {#related-docs}

<Cards>

### [Actions](/docs/actions-overview)

Define the operation once. Every surface above calls the same one.

### [Native Chat UI](/docs/native-chat-ui)

Render typed action results as tables, charts, and cards directly in chat.

### [Generative UI](/docs/generative-ui)

Generate transient or persisted sandboxed UI inline in chat.

### [Automation-First Apps](/docs/pure-agent-apps)

The full no-browser pattern for jobs, queues, scripts, and external agents.

### [External Agents](/docs/external-agents)

Connect MCP-compatible hosts to your app as a tool server.

### [A2A Protocol](/docs/a2a-protocol)

Call agents from other agent-native apps over the A2A standard.

</Cards>
