@agent-native/core 0.56.1 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +9 -7
  2. package/dist/cli/plan-local.d.ts.map +1 -1
  3. package/dist/cli/plan-local.js +66 -10
  4. package/dist/cli/plan-local.js.map +1 -1
  5. package/dist/cli/skills.d.ts +2 -2
  6. package/dist/cli/skills.d.ts.map +1 -1
  7. package/dist/cli/skills.js +13 -5
  8. package/dist/cli/skills.js.map +1 -1
  9. package/dist/client/AssistantChat.d.ts +8 -0
  10. package/dist/client/AssistantChat.d.ts.map +1 -1
  11. package/dist/client/AssistantChat.js +24 -4
  12. package/dist/client/AssistantChat.js.map +1 -1
  13. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  14. package/dist/client/agent-chat-adapter.js +39 -4
  15. package/dist/client/agent-chat-adapter.js.map +1 -1
  16. package/dist/client/chat/connectors.d.ts +19 -0
  17. package/dist/client/chat/connectors.d.ts.map +1 -0
  18. package/dist/client/chat/connectors.js +992 -0
  19. package/dist/client/chat/connectors.js.map +1 -0
  20. package/dist/client/chat/index.d.ts +2 -1
  21. package/dist/client/chat/index.d.ts.map +1 -1
  22. package/dist/client/chat/index.js +2 -0
  23. package/dist/client/chat/index.js.map +1 -1
  24. package/dist/client/chat/runtime.d.ts +93 -0
  25. package/dist/client/chat/runtime.d.ts.map +1 -1
  26. package/dist/client/chat/runtime.js +934 -1
  27. package/dist/client/chat/runtime.js.map +1 -1
  28. package/dist/client/index.d.ts +2 -1
  29. package/dist/client/index.d.ts.map +1 -1
  30. package/dist/client/index.js +2 -0
  31. package/dist/client/index.js.map +1 -1
  32. package/dist/mcp/build-server.d.ts.map +1 -1
  33. package/dist/mcp/build-server.js +48 -3
  34. package/dist/mcp/build-server.js.map +1 -1
  35. package/docs/content/actions.md +5 -1
  36. package/docs/content/agent-surfaces.md +273 -0
  37. package/docs/content/components.md +46 -17
  38. package/docs/content/drop-in-agent.md +10 -5
  39. package/docs/content/embedding-sdk.md +4 -0
  40. package/docs/content/external-agents.md +1 -0
  41. package/docs/content/getting-started.md +2 -0
  42. package/docs/content/harness-agents.md +232 -0
  43. package/docs/content/key-concepts.md +24 -22
  44. package/docs/content/mcp-apps.md +1 -1
  45. package/docs/content/native-chat-ui.md +101 -22
  46. package/docs/content/plan-plugin.md +27 -1
  47. package/docs/content/pure-agent-apps.md +2 -1
  48. package/docs/content/using-your-agent.md +1 -0
  49. package/docs/content/what-is-agent-native.md +3 -2
  50. package/package.json +1 -1
@@ -0,0 +1,273 @@
1
+ ---
2
+ title: "Agent Surfaces"
3
+ description: "Use Agent-Native headlessly, as rich chat, inside an existing app, or as a full agent-native application."
4
+ search: "headless agent rich chat full app BYO agent runtime AgentChatRuntime embed actions MCP A2A HTTP CLI"
5
+ ---
6
+
7
+ # Agent Surfaces
8
+
9
+ Agent-Native is deliberately composable. You can use the agent without much UI,
10
+ use the UI without the built-in agent runtime, or use both together as a full
11
+ application.
12
+
13
+ The useful way to choose is not by protocol first. Choose the product surface
14
+ you want, then use the matching primitive.
15
+
16
+ | Surface | Use it when | Start with |
17
+ | ----------------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
18
+ | **Headless agent/actions** | Code, jobs, scripts, another app, or another agent should call the work directly. | `defineAction`, HTTP, CLI, MCP, A2A |
19
+ | **Rich chat on Agent-Native** | You want a standalone or embedded chat backed by the built-in agent loop. | `<AgentChatSurface>`, `<AssistantChat>`, `createAgentChatPlugin()` |
20
+ | **Rich chat on your agent** | You built the agent elsewhere and want Agent-Native's composer, transcript, tool cards, and native widgets. | `AgentChatRuntime`, `<AssistantChat runtime={runtime}>` |
21
+ | **Embedded sidecar** | You already have a SaaS app and want an agent beside it with page context and host commands. | `createAgentNativeEmbeddedPlugin()`, `AgentNativeEmbedded` |
22
+ | **Full application** | Humans and agents should share durable screens, data, navigation, and collaboration. | Templates, actions, SQL state, context awareness |
23
+
24
+ Those are stages, not separate products. A workflow can start as a headless
25
+ action, appear in chat as a table or chart, and later become a full screen in an
26
+ app without changing the operation the agent calls.
27
+
28
+ ## Headless agent/actions {#headless}
29
+
30
+ Use the headless path when no one needs to stare at a custom app screen while
31
+ the work runs: scheduled jobs, integrations, backend workflows, CLI loops,
32
+ another agent, or an existing product calling into Agent-Native.
33
+
34
+ Most headless integrations should start with actions:
35
+
36
+ ```ts
37
+ // actions/summarize-week.ts
38
+ import { defineAction } from "@agent-native/core";
39
+ import { z } from "zod";
40
+
41
+ export default defineAction({
42
+ description: "Summarize this week's submissions.",
43
+ readOnly: true,
44
+ schema: z.object({ formId: z.string() }),
45
+ run: async ({ formId }) => {
46
+ return { formId, summary: "34 submissions, up 18% from last week." };
47
+ },
48
+ });
49
+ ```
50
+
51
+ One action is then callable as:
52
+
53
+ - **HTTP** — `POST /_agent-native/actions/summarize-week`
54
+ - **CLI** — `pnpm action summarize-week --formId form_123`
55
+ - **MCP** — from Claude, ChatGPT, Codex, Cursor, OpenCode, Copilot, and other MCP hosts
56
+ - **A2A** — from another agent-native app or agent peer
57
+ - **UI** — through `useActionQuery`, `useActionMutation`, or `callAction`
58
+ - **Agent tool** — from the built-in chat loop
59
+
60
+ If you need the whole agent loop headlessly, use the server API from a worker,
61
+ job, integration webhook, or custom host. This is lower-level than actions: you
62
+ provide the engine, model, messages, actions, and event sink yourself.
63
+
64
+ ```ts
65
+ import {
66
+ actionsToEngineTools,
67
+ resolveEngine,
68
+ runAgentLoop,
69
+ } from "@agent-native/core/server";
70
+
71
+ const engine = await resolveEngine({ engineOption: undefined });
72
+ const model = engine.defaultModel;
73
+ const controller = new AbortController();
74
+
75
+ await runAgentLoop({
76
+ engine,
77
+ model,
78
+ systemPrompt: "You are the reporting agent for this workspace.",
79
+ actions,
80
+ tools: actionsToEngineTools(actions),
81
+ messages: [
82
+ {
83
+ role: "user",
84
+ content: [{ type: "text", text: "Summarize this week's forms." }],
85
+ },
86
+ ],
87
+ send: (event) => {
88
+ // Persist, log, stream, or translate AgentChatEvent objects.
89
+ },
90
+ signal: controller.signal,
91
+ ownerEmail: user.email,
92
+ orgId: user.orgId,
93
+ });
94
+ ```
95
+
96
+ For most apps, scheduled prompts and integration webhooks already call this loop
97
+ for you. Reach for direct `runAgentLoop()` when you are building a custom
98
+ headless host, eval runner, or server-side orchestration surface.
99
+
100
+ ## Rich chat on Agent-Native {#rich-chat}
101
+
102
+ Use the built-in chat when the user should talk to the agent, see tool calls,
103
+ approve work, inspect native results, and keep a durable thread history.
104
+
105
+ The simplest full-page chat:
106
+
107
+ ```tsx
108
+ import { AgentChatSurface } from "@agent-native/core/client/chat";
109
+
110
+ export default function ChatRoute() {
111
+ return <AgentChatSurface mode="page" className="h-screen" />;
112
+ }
113
+ ```
114
+
115
+ The simplest embedded chat with your own chrome:
116
+
117
+ ```tsx
118
+ import { AssistantChat } from "@agent-native/core/client/chat";
119
+
120
+ export function ProjectChat({ threadId }: { threadId: string }) {
121
+ return <AssistantChat threadId={threadId} />;
122
+ }
123
+ ```
124
+
125
+ Actions can return explicit native widget results so chat output is not just
126
+ text. Tables, charts, and typed product cards render as first-party React
127
+ components in the chat, without iframes. See [Native Chat UI](/docs/native-chat-ui).
128
+
129
+ ## Rich chat on your agent {#byo-agent}
130
+
131
+ Use this path when your agent is already built with another framework or
132
+ runtime and you want Agent-Native's chat UI around it.
133
+
134
+ `AgentChatRuntime` is the boundary. Your runtime streams normalized events;
135
+ Agent-Native renders the composer, transcript, tool calls, approvals, native
136
+ widgets, and app layout.
137
+
138
+ ```tsx
139
+ import {
140
+ AssistantChat,
141
+ createHttpAgentChatRuntime,
142
+ } from "@agent-native/core/client/chat";
143
+
144
+ const runtime = createHttpAgentChatRuntime({
145
+ id: "external:support-agent",
146
+ label: "Support agent",
147
+ endpoint: "/api/support-agent/chat",
148
+ headers: async () => ({ Authorization: `Bearer ${await getToken()}` }),
149
+ });
150
+
151
+ export function SupportAgentChat() {
152
+ return <AssistantChat runtime={runtime} threadId="support" />;
153
+ }
154
+ ```
155
+
156
+ Use `createOpenAIAgentsChatRuntime()`,
157
+ `createOpenAIResponsesChatRuntime()`, `createClaudeAgentChatRuntime()`,
158
+ `createVercelAiChatRuntime()`, or `createAgUiChatRuntime()` when your endpoint
159
+ already streams one of those event shapes. Use `createHttpAgentChatRuntime()`
160
+ when your agent streams Agent-Native's normalized event shape directly:
161
+
162
+ ```ts
163
+ import { createOpenAIAgentsChatRuntime } from "@agent-native/core/client/chat";
164
+
165
+ const runtime = createOpenAIAgentsChatRuntime({
166
+ endpoint: "/api/openai-agent/chat",
167
+ });
168
+ ```
169
+
170
+ The endpoint can stream SSE or NDJSON events:
171
+
172
+ ```txt
173
+ data: {"type":"message-delta","messageId":"m1","delta":{"type":"text","text":"I found 34 submissions."}}
174
+ data: {"type":"tool-start","toolCall":{"id":"t1","name":"query","input":{"formId":"form_123"}}}
175
+ data: {"type":"tool-done","toolCallId":"t1","toolName":"query","status":"completed","resultText":"34 rows"}
176
+ data: {"type":"done","reason":"complete"}
177
+ ```
178
+
179
+ For a trivial integration, returning `{ "text": "..." }` also works. For richer
180
+ integrations, stream `message-*`, `tool-*`, `approval-request`, `status`,
181
+ `artifact`, `file`, `usage`, `error`, and `done` events. Tool results can carry
182
+ `chatUI` metadata so the same native table/chart/card renderers work with your
183
+ agent too.
184
+
185
+ This is the right place to adapt the OpenAI Agents SDK, Claude Agent SDK, Vercel
186
+ AI SDK, Mastra, Flue, Eve, LangGraph, a custom service, or an AG-UI-compatible
187
+ event stream. Do not use ACP as the default end-user app chat protocol; ACP is
188
+ better framed as coding-agent/editor interoperability. Agent-Native does not
189
+ currently claim A2UI support.
190
+
191
+ ## Embedded sidecar {#embedded-sidecar}
192
+
193
+ Use the embedded sidecar when the main product already exists and you want an
194
+ agent beside it.
195
+
196
+ The server plugin mounts Agent-Native routes into your host app and resolves
197
+ host identity server-side:
198
+
199
+ ```ts
200
+ import { createAgentNativeEmbeddedPlugin } from "@agent-native/core/server";
201
+
202
+ export default createAgentNativeEmbeddedPlugin({
203
+ databaseUrl: process.env.AGENT_NATIVE_DATABASE_URL,
204
+ auth: getHostSession,
205
+ actions: hostActions,
206
+ });
207
+ ```
208
+
209
+ The React sidecar passes page context and host commands:
210
+
211
+ ```tsx
212
+ import { AgentNativeEmbedded } from "@agent-native/core/client";
213
+
214
+ export function AppShell({ children }) {
215
+ return (
216
+ <AgentNativeEmbedded
217
+ getContext={() => ({
218
+ route: { pathname: window.location.pathname },
219
+ selection: { text: window.getSelection()?.toString() || undefined },
220
+ })}
221
+ onNavigate={(payload) =>
222
+ router.navigate((payload as { path: string }).path)
223
+ }
224
+ onRefresh={() => queryClient.invalidateQueries()}
225
+ >
226
+ {children}
227
+ </AgentNativeEmbedded>
228
+ );
229
+ }
230
+ ```
231
+
232
+ See [Embedding SDK](/docs/embedding-sdk) for host auth, database isolation,
233
+ iframe/picker mode, and lower-level bridge APIs.
234
+
235
+ ## Full application {#full-application}
236
+
237
+ Use the full app path when users need durable objects and workflows: forms,
238
+ dashboards, calendars, inboxes, editors, documents, assets, or reports.
239
+
240
+ Full apps add product UI around the same action and agent contract:
241
+
242
+ - **SQL state** — app data, navigation, settings, and chat history are durable.
243
+ - **Context awareness** — the agent knows the current route, selection, and focused object.
244
+ - **Live sync** — agent changes update the UI, and UI changes update the agent's context.
245
+ - **Deep links** — action results can open the right app view.
246
+ - **Native chat widgets** — tables, charts, cards, approvals, and typed results appear inline.
247
+
248
+ Start from a [template](/docs/cloneable-saas) when you want a complete app, or
249
+ from [Starter](/docs/template-starter) when you want only the framework wiring.
250
+
251
+ ## How to choose {#how-to-choose}
252
+
253
+ | If you are thinking... | Choose |
254
+ | --------------------------------------------------------------- | ------------------------- |
255
+ | "I just need a callable tool or workflow." | Headless action |
256
+ | "I want the framework's agent, but chat should be the main UI." | Rich chat on Agent-Native |
257
+ | "I already have an agent; I need a polished chat UI for it." | Rich chat on your agent |
258
+ | "I already have a SaaS app; add an agent beside it." | Embedded sidecar |
259
+ | "The agent and UI should evolve together as the product." | Full application |
260
+
261
+ Keep the contract small: define durable operations as actions, return explicit
262
+ widget results when chat needs rich UI, and add full screens only when users
263
+ need to browse, compare, configure, or collaborate over persistent objects.
264
+
265
+ ## Related docs {#related-docs}
266
+
267
+ - [Actions](/docs/actions) — define the headless operation once.
268
+ - [Native Chat UI](/docs/native-chat-ui) — render typed action results in chat.
269
+ - [Drop-in Agent](/docs/drop-in-agent) — mount chat, sidebar, or panel surfaces.
270
+ - [Component API](/docs/components) — lower-level React chat/composer pieces.
271
+ - [Embedding SDK](/docs/embedding-sdk) — add Agent-Native to an existing app.
272
+ - [External Agents](/docs/external-agents) — connect MCP-compatible hosts to an app.
273
+ - [A2A Protocol](/docs/a2a-protocol) — call agents from other agents.
@@ -27,23 +27,31 @@ so bundlers choose the browser-safe entry.
27
27
 
28
28
  ## Agent And Chat UI {#agent-chat-ui}
29
29
 
30
- | API | Import path | Use when |
31
- | -------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------ |
32
- | `<AgentSidebar>` | `@agent-native/core/client` or `/client/chat` | You want the complete sidebar around your app. |
33
- | `<AgentToggleButton>` | `@agent-native/core/client` or `/client/chat` | You render your own header button for the sidebar. |
34
- | `<AgentPanel>` | `@agent-native/core/client` or `/client/chat` | You want the full panel in your own layout, route, dialog, or side column. |
35
- | `<AgentChatSurface>` | `@agent-native/core/client` or `/client/chat` | You want chat in panel or page mode without the sidebar wrapper. |
36
- | `<AssistantChat>` | `@agent-native/core/client` or `/client/chat` | You want to own surrounding chrome while keeping the standard conversation and composer runtime. |
37
- | `<MultiTabAssistantChat>` | `@agent-native/core/client` or `/client/chat` | You want the framework's thread tabs without `AgentPanel` chrome. |
38
- | `createAgentChatAdapter()` | `@agent-native/core/client` or `/client/chat` | You are adapting a BYO assistant-ui transport into the Agent-Native chat runtime. |
39
- | `useChatThreads()` | `@agent-native/core/client` or `/client/chat` | You need a custom thread list, history picker, or scoped chat UI. |
40
- | `sendToAgentChat()` | `@agent-native/core/client` or `/client/chat` | A product action should hand work to the agent chat. |
41
-
42
- In docs, `AgentChatRuntime` means this standard runtime posture: assistant-ui
43
- thread state, Agent-Native streaming, run recovery, attachments, model
44
- selection, approvals, native tool widgets, and SQL-backed sync. It is not a
45
- separate protocol replacement. BYO agents should adapt into this runtime with
46
- `createAgentChatAdapter()` when they want the normal app chat experience.
30
+ | API | Import path | Use when |
31
+ | ------------------------------------ | --------------------------------------------- | ------------------------------------------------------------------------------------------------ |
32
+ | `<AgentSidebar>` | `@agent-native/core/client` or `/client/chat` | You want the complete sidebar around your app. |
33
+ | `<AgentToggleButton>` | `@agent-native/core/client` or `/client/chat` | You render your own header button for the sidebar. |
34
+ | `<AgentPanel>` | `@agent-native/core/client` or `/client/chat` | You want the full panel in your own layout, route, dialog, or side column. |
35
+ | `<AgentChatSurface>` | `@agent-native/core/client` or `/client/chat` | You want chat in panel or page mode without the sidebar wrapper. |
36
+ | `<AssistantChat>` | `@agent-native/core/client` or `/client/chat` | You want to own surrounding chrome while keeping the standard conversation and composer runtime. |
37
+ | `<MultiTabAssistantChat>` | `@agent-native/core/client` or `/client/chat` | You want the framework's thread tabs without `AgentPanel` chrome. |
38
+ | `createHttpAgentChatRuntime()` | `@agent-native/core/client` or `/client/chat` | You have a BYO agent endpoint that streams normalized chat events. |
39
+ | `createOpenAIAgentsChatRuntime()` | `@agent-native/core/client` or `/client/chat` | You have an OpenAI Agents SDK stream and want the standard chat UI around it. |
40
+ | `createOpenAIResponsesChatRuntime()` | `@agent-native/core/client` or `/client/chat` | You have an OpenAI Responses event stream and want it normalized into the chat UI. |
41
+ | `createAgUiChatRuntime()` | `@agent-native/core/client` or `/client/chat` | You have an AG-UI event stream and want it normalized into the chat UI. |
42
+ | `createClaudeAgentChatRuntime()` | `@agent-native/core/client` or `/client/chat` | You have a Claude Agent SDK stream and want it normalized into the chat UI. |
43
+ | `createVercelAiChatRuntime()` | `@agent-native/core/client` or `/client/chat` | You have a Vercel AI SDK stream and want it normalized into the chat UI. |
44
+ | `createAgentChatRuntimeAdapter()` | `@agent-native/core/client` or `/client/chat` | You need to adapt an `AgentChatRuntime` into assistant-ui yourself. |
45
+ | `createAgentChatAdapter()` | `@agent-native/core/client` or `/client/chat` | You need the built-in Agent-Native SSE transport as a low-level assistant-ui adapter. |
46
+ | `useChatThreads()` | `@agent-native/core/client` or `/client/chat` | You need a custom thread list, history picker, or scoped chat UI. |
47
+ | `sendToAgentChat()` | `@agent-native/core/client` or `/client/chat` | A product action should hand work to the agent chat. |
48
+
49
+ `AgentChatRuntime` is the BYO-agent contract for the standard chat shell. Pass
50
+ `runtime` to `<AssistantChat>` when an external agent should power the
51
+ conversation while Agent-Native keeps the composer, transcript, tool cards, and
52
+ native widget rendering. If you are choosing between headless actions, rich
53
+ chat, embedded sidecar, and full app shapes, see
54
+ [Agent Surfaces](/docs/agent-surfaces).
47
55
 
48
56
  The shortest custom route is still a pre-wired surface:
49
57
 
@@ -77,6 +85,27 @@ function CustomChat({ projectSlug }: { projectSlug: string }) {
77
85
  }
78
86
  ```
79
87
 
88
+ For a bring-your-own agent endpoint:
89
+
90
+ ```tsx
91
+ import {
92
+ AssistantChat,
93
+ createOpenAIAgentsChatRuntime,
94
+ } from "@agent-native/core/client/chat";
95
+
96
+ const runtime = createOpenAIAgentsChatRuntime({
97
+ endpoint: "/api/my-agent/chat",
98
+ label: "My agent",
99
+ });
100
+
101
+ export function MyAgentChat() {
102
+ return <AssistantChat runtime={runtime} />;
103
+ }
104
+ ```
105
+
106
+ Use `createHttpAgentChatRuntime()` instead when your endpoint already emits
107
+ Agent-Native normalized events.
108
+
80
109
  ## Chat Field And Composer {#composer}
81
110
 
82
111
  Use `@agent-native/core/client/composer` when you need to place the same chat
@@ -184,20 +184,25 @@ framework own the agent runtime:
184
184
  - **`@agent-native/core/client/conversation`** — use this for transcript
185
185
  rendering without the full chat runtime.
186
186
  - **`createAgentChatAdapter()`** — use this only when building a custom
187
- assistant-ui transport for the standard Agent-Native chat runtime. It
187
+ assistant-ui transport for the built-in Agent-Native chat endpoint. It
188
188
  connects to the same `/_agent-native/agent-chat` stream and preserves
189
189
  run-manager recovery, attachments, model selection, native widgets, and
190
190
  thread metadata.
191
+ - **`createHttpAgentChatRuntime()`** — use this when a BYO agent exposes a
192
+ POST endpoint that streams `AgentChatRuntime` events. Pass the runtime to
193
+ `<AssistantChat runtime={runtime} />`.
191
194
 
192
195
  Avoid posting directly to `/_agent-native/agent-chat` from product UI. If a
193
196
  lower-level helper is missing for a real custom surface, add that named helper
194
197
  first so client code does not learn a second, ad hoc transport.
195
198
 
196
199
  For BYO agent runtimes, keep actions and SQL-backed app state as the contract.
197
- Adapt the runtime into `<AssistantChat createAdapter={...} />` when you want
198
- Agent-Native chat behavior, or use `PromptComposer` by itself only when the
199
- external runtime owns the transcript and loop. See
200
- [Native Chat UI](/docs/native-chat-ui#byo-agent-runtimes).
200
+ Adapt the runtime into `<AssistantChat runtime={...} />` when you want
201
+ Agent-Native chat behavior. Use `<AssistantChat createAdapter={...} />` only
202
+ for lower-level assistant-ui transports, or `PromptComposer` by itself only
203
+ when the external runtime owns the transcript and loop. See
204
+ [Native Chat UI](/docs/native-chat-ui#byo-agent-runtimes) and
205
+ [Agent Surfaces](/docs/agent-surfaces#byo-agent).
201
206
 
202
207
  ### Build your own sidebar from pieces {#build-your-own-sidebar}
203
208
 
@@ -29,6 +29,10 @@ pnpm add @agent-native/core
29
29
 
30
30
  ## Choosing a mode {#choosing-a-mode}
31
31
 
32
+ This page is for embedding Agent-Native into an existing product. If you are
33
+ still deciding between headless actions, rich chat, an embedded sidecar, or a
34
+ full app, start with [Agent Surfaces](/docs/agent-surfaces).
35
+
32
36
  | Mode | Use it when | Package |
33
37
  | ------------------------------------ | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
34
38
  | **EmbeddedApp picker** | Launching a full Agent-Native app as a focused iframe (asset picker, form builder, approval panel). | `@agent-native/embedding` |
@@ -13,6 +13,7 @@ The external-agent bridge closes the loop. First you connect your own agent to a
13
13
  ## Which agent path do you need? {#which-agent-path}
14
14
 
15
15
  - **External MCP host:** use this page when Claude, ChatGPT, Codex, Cursor, OpenCode, GitHub Copilot / VS Code, or another MCP-compatible host should call your hosted agent-native app.
16
+ - **Your own runtime behind Agent-Native chat:** see [Agent Surfaces](/docs/agent-surfaces#byo-agent) and [Native Chat UI](/docs/native-chat-ui#byo-agent-runtimes) when an agent built with another framework should power `<AssistantChat runtime={...}>`.
16
17
  - **Your app consuming MCP tools:** see [MCP Clients](/docs/mcp-clients) when an agent-native app needs to call tools exposed by another MCP server.
17
18
  - **Another app or agent via A2A:** use [Agent Mentions](/docs/agent-mentions) and [A2A](/docs/a2a-protocol) when agent-native apps should discover and delegate to each other.
18
19
  - **Local custom sub-agents:** use [Workspace](/docs/workspace) when you want custom agent profiles inside the agent-native workspace itself.
@@ -13,6 +13,7 @@ Start with the path that matches what you want to do next:
13
13
 
14
14
  - **Use a starter app.** Browse the [template gallery](/templates). Hosted apps already include sign-in, data, and the agent sidebar. No install required.
15
15
  - **Build or customize your own app.** Continue with [Create a local app](#create-your-app). You'll scaffold a template, run it locally, then change the code, brand, data model, and integrations.
16
+ - **Choose headless, chat, embedded, or full app.** Use [Agent Surfaces](/docs/agent-surfaces) when you know the workflow but not how much UI belongs around it.
16
17
  - **Add agent-native skills to a code tool.** Jump to [Try it with a skill](#try-with-a-skill) to add Plans or PR Recaps to Claude Code, Codex, or Cursor without scaffolding an app.
17
18
  - **Connect an external agent to an app.** Use [External Agents](/docs/external-agents) to connect Claude, ChatGPT, Codex, Cursor, OpenCode, GitHub Copilot / VS Code, or another MCP host to an existing app.
18
19
 
@@ -122,6 +123,7 @@ Once your app is running, the usual next step is small and concrete:
122
123
  Useful follow-up docs:
123
124
 
124
125
  - [Key Concepts](/docs/key-concepts) for the architecture: SQL, actions, polling sync, and context awareness
126
+ - [Agent Surfaces](/docs/agent-surfaces) for choosing headless, rich chat, embedded sidecar, or full app
125
127
  - [Workspace](/docs/workspace) for instructions, skills, memory, and per-user MCP connections
126
128
  - [Messaging](/docs/messaging) for Slack, email, Telegram, and other ways to reach the agent
127
129
  - [FAQ](/docs/faq) for setup and product questions
@@ -0,0 +1,232 @@
1
+ ---
2
+ title: "Harness Agents"
3
+ description: "Run Claude Code, Codex, Pi, and other full coding harnesses as embedded agents inside Agent-Native, with their own loop, sandbox, native tools, and resumable SQL-backed sessions."
4
+ search: "harness agents AgentHarness ai-sdk HarnessAgent Claude Code Codex Pi Cursor Mastra embedded coding agent resolveAgentHarness startAgentHarnessRun resumable session sandbox host tools"
5
+ ---
6
+
7
+ # Harness Agents
8
+
9
+ A harness agent is a full agent runtime — Claude Code, Codex, Pi, and similar —
10
+ that owns its own loop, workspace, native file tools, session state, compaction,
11
+ approval model, and sandbox behavior. Agent-Native runs these through the
12
+ **`AgentHarness`** substrate in `@agent-native/core/agent/harness`, streams their
13
+ events into the normal transcript, and persists their native session so a thread
14
+ can pause and resume.
15
+
16
+ This is different from the built-in chat agent and from bringing your own chat
17
+ runtime. The built-in agent and `AgentEngine` are for one model round trip
18
+ beneath `runAgentLoop`. A harness is not an `AgentEngine` provider — it runs its
19
+ own loop end to end, so Agent-Native drives it as a session, not as a single
20
+ model call.
21
+
22
+ | You want to… | Use |
23
+ | -------------------------------------------------------------------------- | ------------------------------------------------------------- |
24
+ | Run Claude Code / Codex / Pi **as the agent**, with their own loop + tools | **Harness agents** (this page) |
25
+ | Put an agent you built elsewhere behind Agent-Native's **chat UI** | [`AgentChatRuntime`](/docs/native-chat-ui#byo-agent-runtimes) |
26
+ | Let an external MCP host (Claude Code, Cursor, …) **call into your app** | [External Agents](/docs/external-agents) |
27
+ | Render a Claude-Code/Codex-style **coding workspace UI** | [Agent-Native Code UI](/docs/code-agents-ui) |
28
+ | Spawn background / sub-agent runs and teams | [Custom Agents & Teams](/docs/agent-teams) |
29
+
30
+ ## Built-in harnesses {#built-in}
31
+
32
+ `registerBuiltinAgentHarnesses()` registers three adapters backed by the AI SDK
33
+ `HarnessAgent`:
34
+
35
+ | Name | Runtime | Sandbox | Approvals |
36
+ | ---------------------------- | ----------- | ------- | --------- |
37
+ | `ai-sdk-harness:claude-code` | Claude Code | yes | yes |
38
+ | `ai-sdk-harness:codex` | Codex | yes | no |
39
+ | `ai-sdk-harness:pi` | Pi | no | yes |
40
+
41
+ Their runtime packages are **optional peer dependencies** and load lazily, so an
42
+ app that never uses a harness does not pay for it. Each adapter carries an
43
+ `installPackage` hint (for example `@ai-sdk/harness@canary
44
+ @ai-sdk/harness-codex@canary`); `resolveAgentHarness` throws a clear install
45
+ error if the packages are missing, and `isAgentHarnessPackageInstalled(entry)`
46
+ lets you check first.
47
+
48
+ ## Register and resolve {#register-resolve}
49
+
50
+ ```ts
51
+ import {
52
+ registerBuiltinAgentHarnesses,
53
+ resolveAgentHarness,
54
+ } from "@agent-native/core/agent/harness";
55
+
56
+ registerBuiltinAgentHarnesses();
57
+ const adapter = resolveAgentHarness("ai-sdk-harness:codex");
58
+ ```
59
+
60
+ `resolveAgentHarness(name, config?)` returns an `AgentHarnessAdapter`. The
61
+ optional `config` is forwarded to the adapter factory — for the AI SDK adapters
62
+ that maps to `AiSdkHarnessAdapterOptions` (`label`, `description`,
63
+ `permissionMode`, `harnessOptions`, `agentOptions`). Use `listAgentHarnesses()`
64
+ to enumerate what is registered for a picker.
65
+
66
+ ## Run a turn {#run-a-turn}
67
+
68
+ `startAgentHarnessRun` bridges a harness session into the shared run-manager
69
+ lifecycle. It creates (or reuses) the native session, persists it, streams the
70
+ turn, translates each harness event into transcript events, and detaches the
71
+ resumable state when the turn completes.
72
+
73
+ ```ts
74
+ import { startAgentHarnessRun } from "@agent-native/core/agent/harness";
75
+
76
+ const run = startAgentHarnessRun({
77
+ runId,
78
+ threadId,
79
+ adapter,
80
+ input: { prompt },
81
+ createSession: {
82
+ sessionId,
83
+ resumeState, // opaque value from a previous turn, if resuming
84
+ instructions,
85
+ sandbox, // required for sandboxed harnesses — see Sandbox Adapters
86
+ permissionMode: "allow-reads",
87
+ tools, // a narrow, intentional set of host tools (see below)
88
+ },
89
+ ownerEmail,
90
+ orgId,
91
+ });
92
+ ```
93
+
94
+ `startAgentHarnessRun` returns the `ActiveRun` from the run-manager, so the turn
95
+ shows up through the existing run routes, transcript, and cancellation just like
96
+ any other agent run. Pass an already-created `session` instead of `createSession`
97
+ to continue a session you are holding in memory.
98
+
99
+ ## Sessions and resume {#sessions}
100
+
101
+ A harness owns long-lived native session state. Agent-Native persists it in SQL
102
+ so a thread can survive across turns, processes, and deploys. The `resumeState`
103
+ is **opaque** — Agent-Native stores it and hands it back, but never inspects or
104
+ interprets it.
105
+
106
+ ```ts
107
+ import {
108
+ getLatestAgentHarnessSessionForThread,
109
+ listAgentHarnessSessions,
110
+ } from "@agent-native/core/agent/harness";
111
+
112
+ const last = await getLatestAgentHarnessSessionForThread(threadId);
113
+ // Feed last?.resumeState into createSession.resumeState on the next turn.
114
+ ```
115
+
116
+ The store also exposes `saveAgentHarnessSession`, `updateAgentHarnessSession`,
117
+ `getAgentHarnessSession`, `getAgentHarnessSessionByRunId`,
118
+ `markAgentHarnessSessionStopped`, and `ensureAgentHarnessSessionTables`.
119
+ `startAgentHarnessRun` calls the save/update/stop paths for you; reach for them
120
+ directly only in a custom host.
121
+
122
+ ## Host tools and permissions {#host-tools}
123
+
124
+ A harness brings its own native tools (read, edit, write, shell, and so on), so
125
+ you do **not** re-expose file editing as host tools. Pass only a **narrow,
126
+ intentional set** of Agent-Native actions through `createSession.tools` when you
127
+ want the harness to reach specific app operations — and keep `defineAction`
128
+ auth, request context, timeouts, truncation, and read-only metadata intact when
129
+ you do.
130
+
131
+ `permissionMode` gates what the harness may do without approval:
132
+
133
+ | Mode | Meaning |
134
+ | ------------- | -------------------------------------------------- |
135
+ | `allow-reads` | Default. Reads run; edits and risky actions prompt |
136
+ | `allow-edits` | Reads and edits run; other risky actions prompt |
137
+ | `allow-all` | No approval gating |
138
+
139
+ When a harness pauses for approval it emits an `approval-request` event and the
140
+ session is marked `idle` with the pending approval recorded, so the UI can
141
+ surface it and resume on the user's decision. See
142
+ [Human Approval](/docs/human-approval) for the approval surface.
143
+
144
+ ## Events {#events}
145
+
146
+ A harness session streams `AgentHarnessEvent` values, which Agent-Native
147
+ translates to the standard `AgentChatEvent` stream with
148
+ `agentHarnessEventToAgentChatEvents`. The event union covers `text-delta`,
149
+ `thinking-delta`, `activity`, `tool-start`, `tool-done` (which can carry an
150
+ `mcpApp` payload for native widgets), `approval-request`, `file-change`,
151
+ `compaction`, `usage`, `error`, and `done`. Because tool results flow through the
152
+ same translation, action-declared native widgets still render — see
153
+ [Native Chat UI](/docs/native-chat-ui).
154
+
155
+ ## Background runs and the UI {#background-runs}
156
+
157
+ Harness runs project into the shared `BackgroundAgentRun` shape with
158
+ `createAgentHarnessBackgroundAgentController()` and are available through the
159
+ existing run routes as `goalId=agent-harness`. That means a long-running Claude
160
+ Code or Codex session appears in the same background-run and transcript surfaces
161
+ as Agent Teams and other adapters, with `listAgentHarnessBackgroundRuns`,
162
+ `listAgentHarnessBackgroundTranscriptEvents`, `getAgentHarnessBackgroundRun`, and
163
+ `stopAgentHarnessBackgroundRun` available for custom hosts.
164
+
165
+ ## Custom adapters {#custom-adapters}
166
+
167
+ To wrap a runtime that is not one of the built-ins, implement
168
+ `AgentHarnessAdapter` and register it. The adapter declares its capabilities and
169
+ creates sessions; a session exposes `streamTurn` and optional `continueTurn`,
170
+ `approve`, `detach`, `stop`, and `destroy`.
171
+
172
+ ```ts
173
+ import {
174
+ registerAgentHarness,
175
+ type AgentHarnessAdapter,
176
+ } from "@agent-native/core/agent/harness";
177
+
178
+ const myHarness: AgentHarnessAdapter = {
179
+ name: "acme:my-coder",
180
+ label: "Acme Coder",
181
+ description: "Runs the Acme coding agent.",
182
+ installPackage: "@acme/coder",
183
+ capabilities: {
184
+ sandbox: true,
185
+ resumable: true,
186
+ approvals: true,
187
+ hostTools: true,
188
+ fileEvents: true,
189
+ },
190
+ async createSession(opts) {
191
+ // Build your native session and adapt it to AgentHarnessSession.
192
+ return createAcmeSession(opts);
193
+ },
194
+ };
195
+
196
+ registerAgentHarness({
197
+ name: myHarness.name,
198
+ label: myHarness.label,
199
+ description: myHarness.description,
200
+ installPackage: myHarness.installPackage,
201
+ capabilities: myHarness.capabilities,
202
+ create: () => myHarness,
203
+ });
204
+ ```
205
+
206
+ Keep the runtime package optional with a dynamic import in `createSession` and an
207
+ `installPackage` hint. For bridge-backed coding harnesses, require a real
208
+ sandbox/workspace provider rather than running an arbitrary coding agent in the
209
+ host process — see [Sandbox Adapters](/docs/sandbox-adapters). The AI SDK adapter
210
+ (`createAiSdkHarnessAdapter`, backed by `HarnessAgent` from `@ai-sdk/harness`) is
211
+ one implementation of this contract, not the public abstraction.
212
+
213
+ ## Don't {#donts}
214
+
215
+ - Don't add Claude Code, Codex, Cursor, Mastra, or Pi as an `AgentEngine`. They
216
+ own their loop; running one under `AgentEngine.stream()` double-runs the loop
217
+ and loses session lifecycle semantics.
218
+ - Don't replay full Agent-Native chat history into a harness each turn. Resume
219
+ the harness session with its `resumeState` instead.
220
+ - Don't store `resumeState` in `application_state`. It belongs in the harness
221
+ session SQL table.
222
+ - Don't expose every app action to every harness session by default. Hand it a
223
+ small, intentional tool set.
224
+
225
+ ## Related docs {#related-docs}
226
+
227
+ - [Native Chat UI](/docs/native-chat-ui) — put your own agent behind the chat UI with `AgentChatRuntime`.
228
+ - [Agent Surfaces](/docs/agent-surfaces) — choose headless, chat, sidecar, or full-app.
229
+ - [Agent-Native Code UI](/docs/code-agents-ui) — the reusable coding workspace surface.
230
+ - [Custom Agents & Teams](/docs/agent-teams) — background runs and sub-agent delegation.
231
+ - [Sandbox Adapters](/docs/sandbox-adapters) — pluggable execution backends for coding harnesses.
232
+ - [Human Approval](/docs/human-approval) — the approval surface harness runs use.