@agent-native/core 0.63.0 → 0.63.1

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 (94) hide show
  1. package/dist/cli/code-agent-executor.js +1 -1
  2. package/dist/cli/code-agent-executor.js.map +1 -1
  3. package/dist/cli/create.js +1 -1
  4. package/dist/cli/create.js.map +1 -1
  5. package/dist/client/NewWorkspaceAppFlow.js +1 -1
  6. package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
  7. package/dist/client/chat/index.d.ts +2 -1
  8. package/dist/client/chat/index.d.ts.map +1 -1
  9. package/dist/client/chat/index.js +2 -1
  10. package/dist/client/chat/index.js.map +1 -1
  11. package/dist/client/chat-view-transition.d.ts +17 -0
  12. package/dist/client/chat-view-transition.d.ts.map +1 -1
  13. package/dist/client/chat-view-transition.js +46 -0
  14. package/dist/client/chat-view-transition.js.map +1 -1
  15. package/dist/client/index.d.ts +2 -1
  16. package/dist/client/index.d.ts.map +1 -1
  17. package/dist/client/index.js +2 -1
  18. package/dist/client/index.js.map +1 -1
  19. package/dist/client/use-agent-chat-home-handoff.d.ts +27 -0
  20. package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -0
  21. package/dist/client/use-agent-chat-home-handoff.js +120 -0
  22. package/dist/client/use-agent-chat-home-handoff.js.map +1 -0
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +3 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/styles/agent-native.css +2 -6
  28. package/dist/templates/workspace-root/README.md +4 -4
  29. package/docs/content/actions.md +32 -42
  30. package/docs/content/agent-surfaces.md +105 -84
  31. package/docs/content/agent-teams.md +2 -14
  32. package/docs/content/agent-web-surfaces.md +4 -4
  33. package/docs/content/authentication.md +40 -24
  34. package/docs/content/automations.md +18 -36
  35. package/docs/content/blueprint-installer.md +3 -0
  36. package/docs/content/cli-adapters.md +24 -168
  37. package/docs/content/client.md +11 -77
  38. package/docs/content/cloneable-saas.md +1 -1
  39. package/docs/content/code-agents-ui.md +43 -0
  40. package/docs/content/components.md +10 -23
  41. package/docs/content/context-awareness.md +3 -3
  42. package/docs/content/creating-templates.md +20 -18
  43. package/docs/content/database.md +1 -1
  44. package/docs/content/deployment.md +5 -37
  45. package/docs/content/dispatch.md +17 -28
  46. package/docs/content/drop-in-agent.md +24 -111
  47. package/docs/content/durable-resume.md +4 -0
  48. package/docs/content/embedding-sdk.md +141 -135
  49. package/docs/content/evals.md +3 -3
  50. package/docs/content/extensions.md +1 -1
  51. package/docs/content/external-agents.md +34 -60
  52. package/docs/content/faq.md +5 -5
  53. package/docs/content/frames.md +13 -4
  54. package/docs/content/getting-started.md +96 -142
  55. package/docs/content/harness-agents.md +24 -7
  56. package/docs/content/human-approval.md +1 -1
  57. package/docs/content/key-concepts.md +14 -99
  58. package/docs/content/local-file-mode.md +2 -2
  59. package/docs/content/mcp-apps.md +9 -2
  60. package/docs/content/mcp-clients.md +8 -3
  61. package/docs/content/mcp-protocol.md +11 -29
  62. package/docs/content/messaging.md +1 -1
  63. package/docs/content/migration-workbench.md +14 -175
  64. package/docs/content/multi-app-workspace.md +1 -1
  65. package/docs/content/multi-tenancy.md +18 -47
  66. package/docs/content/native-chat-ui.md +15 -12
  67. package/docs/content/observability.md +16 -4
  68. package/docs/content/observational-memory.md +1 -1
  69. package/docs/content/pure-agent-apps.md +17 -124
  70. package/docs/content/real-time-collaboration.md +14 -14
  71. package/docs/content/routing.md +71 -0
  72. package/docs/content/sandbox-adapters.md +78 -4
  73. package/docs/content/security.md +59 -39
  74. package/docs/content/server.md +16 -8
  75. package/docs/content/sharing.md +1 -6
  76. package/docs/content/skills-guide.md +3 -1
  77. package/docs/content/template-analytics.md +1 -1
  78. package/docs/content/template-assets.md +12 -3
  79. package/docs/content/template-brain.md +64 -72
  80. package/docs/content/template-chat.md +32 -4
  81. package/docs/content/template-clips.md +35 -4
  82. package/docs/content/template-design.md +19 -3
  83. package/docs/content/template-dispatch.md +9 -0
  84. package/docs/content/template-forms.md +15 -10
  85. package/docs/content/template-plan.md +7 -0
  86. package/docs/content/template-slides.md +14 -14
  87. package/docs/content/template-videos.md +10 -12
  88. package/docs/content/tracking.md +66 -55
  89. package/docs/content/using-your-agent.md +6 -16
  90. package/docs/content/what-is-agent-native.md +5 -11
  91. package/docs/content/workspace-management.md +2 -2
  92. package/docs/content/workspace.md +20 -160
  93. package/package.json +1 -1
  94. package/src/templates/workspace-root/README.md +4 -4
@@ -19,6 +19,10 @@ One definition, seven consumers. This is rung 3 of the [ladder](/docs/what-is-ag
19
19
  If you are deciding whether to expose an operation headlessly, in chat, in an
20
20
  embedded sidecar, or as a full app screen, see [Agent Surfaces](/docs/agent-surfaces).
21
21
 
22
+ If the UI and agent both need to do something, reach for an action — not a custom
23
+ route. For when a route-shaped protocol _is_ the right call, see [Prefer Actions
24
+ For App Operations](/docs/server#actions-first).
25
+
22
26
  ## Start with one action {#hello-action}
23
27
 
24
28
  The primitive-first on-ramp is one action, not a template. In a headless
@@ -154,9 +158,22 @@ Every action the agent can see is a tool in the model's context window, and a lo
154
158
 
155
159
  A repo-level advisory helper, `node scripts/audit-template-actions.mjs [template ...]` (alias `pnpm actions:audit`), statically scans a template's `actions/` and flags likely UI-dead actions and redundant per-field clusters. It is advisory only (always exits 0, never fails CI) and uses conservative heuristics, so review its suggestions rather than treating them as errors.
156
160
 
157
- ### Agent tool exposure {#agent-tool}
161
+ ### Exposure flags {#exposure-flags}
162
+
163
+ Four flags control _who_ can invoke an action. All default to the permissive value, so you only set one to tighten a specific surface. This table is the glanceable summary; the subsections add the one detail each needs.
164
+
165
+ | Flag | Default | Restrictive value → who can still call | Typical use |
166
+ | --------------- | ------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------- |
167
+ | `agentTool` | `true` | `false` → UI, HTTP, CLI only — **hidden from the model**, MCP, and A2A | UI-only / programmatic actions that shouldn't spend a tool slot |
168
+ | `toolCallable` | `true` | `false` → everything **except** the sandboxed extension iframe bridge (403) | Auth-adjacent ops (delete account, change org membership/roles) |
169
+ | `publicAgent` | off (private) | `{ expose: true }` → adds the action to **public** MCP/A2A/OpenAPI surfaces | Safe read/ingest tools reachable without authentication |
170
+ | `needsApproval` | `false` | `true` → the agent **pauses**; a human must approve the specific call | Consequential side effects (send email, charge a card, delete) |
158
171
 
159
- By default every action is exposed to the agent — the in-app assistant plus the app's MCP / A2A tool surfaces as a callable tool. For an action that only the frontend (or an HTTP / cron caller) needs, set `agentTool: false` to keep it behind the framework's auth + action surface while removing it from every agent tool list:
172
+ These are independent: `agentTool` controls the model's view, `toolCallable` controls only the extension iframe, `publicAgent` adds an opt-in public surface (public web routes never imply public tool exposure), and `needsApproval` gates execution after the call is made see [Human-in-the-loop approval](#needs-approval) below.
173
+
174
+ #### `agentTool` — hide from the model {#agent-tool}
175
+
176
+ By default every action is a callable agent tool. Set `agentTool: false` to keep it behind the framework's auth + action surface while removing it from every agent tool list — it stays callable from the UI (`useActionMutation` / `callAction`), CLI, and `/_agent-native/actions/<name>`:
160
177
 
161
178
  ```ts
162
179
  export default defineAction({
@@ -170,24 +187,11 @@ export default defineAction({
170
187
  });
171
188
  ```
172
189
 
173
- | Value | Behavior |
174
- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
175
- | `true` | Allow (same as undefined). Useful for documenting intent. |
176
- | `false` | **Hidden from the model entirely** — not in the agent's tool list, MCP, or A2A. Still callable from the UI (`useActionMutation` / `callAction`), CLI, and `/_agent-native/actions/<name>`. |
177
- | `undefined` | **Default-allow.** The action is a normal agent tool. |
178
-
179
- `agentTool: false` is **not** the same as [`toolCallable: false`](#tool-callable):
190
+ Reach for it when you add a UI-only or purely programmatic action, or when the UI stops using an action you'd otherwise leave exposed to the model.
180
191
 
181
- - **`agentTool: false`** removes the action from the **model's** view. The model can no longer see or call it; the UI and HTTP can.
182
- - **`toolCallable: false`** only blocks the sandboxed **extension iframe bridge** (`appAction(...)`). The action stays fully visible to the model, UI, CLI, MCP, and A2A. It exists for high-blast-radius operations (account/org/auth changes), not for trimming the tool list.
192
+ #### `toolCallable` block the extension iframe {#tool-callable}
183
193
 
184
- Reach for `agentTool: false` when you find yourself adding a UI-only or purely programmatic action, or when the UI stops using an action you'd otherwise leave exposed to the model.
185
-
186
- ### Extension callability {#tool-callable}
187
-
188
- Extensions (Alpine.js mini-apps that run inside sandboxed iframes — see [Extensions](/docs/extensions)) call actions via `appAction(name, params)`. Because a shared extension's HTML/JS executes inside the _viewer's_ session, an action invoked from an extension runs with the viewer's permissions, secrets, and SQL scope. For high-blast-radius operations, that is too much trust to grant by default.
189
-
190
- Use the `toolCallable` flag to control this (the flag name is kept for backward compatibility — it gates extension iframe callability):
194
+ Extensions ([Alpine.js mini-apps in sandboxed iframes](/docs/extensions)) call actions via `appAction(name, params)`, running with the _viewer's_ permissions, secrets, and SQL scope. For high-blast-radius operations that is too much trust by default. Set `toolCallable: false` to make the extension bridge return 403 while keeping the action callable from the UI, agent, CLI, MCP, and A2A:
191
195
 
192
196
  ```ts
193
197
  export default defineAction({
@@ -200,20 +204,7 @@ export default defineAction({
200
204
  });
201
205
  ```
202
206
 
203
- | Value | Behavior |
204
- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
205
- | `true` | Allow (same as undefined). Useful for documentation of intent. |
206
- | `false` | Explicit deny. The extension bridge returns 403; the action is still callable normally from the UI, agent, CLI, MCP, and A2A. |
207
- | `undefined` | **Default-allow.** Extensions are intra-org and typically authored by trusted teammates, so the default trusts the org-level access controls. Set `false` only for genuinely auth-adjacent operations (account deletion, org membership changes). |
208
-
209
- Enforcement: the parent host tags every outbound action call from an extension iframe with the header `X-Agent-Native-Tool-Bridge: 1`. The action route layer reads this header and applies the rule above. Regular UI/agent/CLI/A2A calls do not carry the header and are unaffected. The header is set by the React host; the iframe's user-authored content cannot spoof it because the bridge sanitizes iframe-supplied headers.
210
-
211
- Set `toolCallable: false` for actions that:
212
-
213
- - delete or transfer ownership of any account/org,
214
- - change auth state (sign-out-all sessions, rotate tokens),
215
- - modify org membership (invite/remove members, change roles),
216
- - change resource visibility or grant share access (the framework's built-in `share-resource`, `unshare-resource`, and `set-resource-visibility` are already opted out).
207
+ Use it for actions that delete or transfer accounts/orgs, change auth state, modify org membership, or grant share access. The framework's built-in `share-resource`, `unshare-resource`, and `set-resource-visibility` are already opted out. Enforcement is by an unspoofable host-set header on iframe calls; regular UI/agent/CLI/MCP/A2A calls are unaffected — see [Security](/docs/security) for details.
217
208
 
218
209
  ### Run context (second argument) {#run-context}
219
210
 
@@ -307,10 +298,10 @@ export default defineAction({
307
298
  });
308
299
  ```
309
300
 
310
- `needsApproval` accepts a boolean or a predicate `(args, ctx) => boolean | Promise<boolean>` to gate conditionally (e.g. only external recipients, only above a threshold). The predicate **fails closed**: a throw is treated as "approval required". When the gate is truthy and the call isn't yet approved, the loop emits an `approval_required` event and stops the turn the side effect never happens and the action runs only once a human approves via the chat UI's Approve affordance.
301
+ `needsApproval` also accepts a predicate `(args, ctx) => boolean | Promise<boolean>` to gate conditionally (e.g. only external recipients, only above a threshold); it **fails closed**, so a throw counts as "approval required". When the gate is truthy and unapproved, the loop stops the turn and the side effect never fires until a human approves in the chat UI.
311
302
 
312
303
  > [!WARNING]
313
- > Keep approvals rare. Each gated action is a hard stop in the agent loop. The default is **off**, and almost every action should leave it off. See [Human-in-the-Loop Approvals](/docs/human-approval) for the full flow.
304
+ > Keep approvals rare. Each gated action is a hard stop in the agent loop. The default is **off**, and almost every action should leave it off. See [Human-in-the-Loop Approvals](/docs/human-approval) for the predicate API, the `approval_required` event, and the full flow.
314
305
 
315
306
  ## Calling it from the UI {#ui}
316
307
 
@@ -394,12 +385,11 @@ export default defineAction({
394
385
  ```
395
386
 
396
387
  The built-in discriminants are `"data-table"`, `"data-chart"`, and
397
- `"data-insights"`. Their server-safe builders and schemas are exported from
398
- `@agent-native/core/data-widgets`, and native renderer ids are exported from
399
- `@agent-native/core`. See [Native Chat UI](/docs/native-chat-ui) for the full
400
- result contract and BYO runtime guidance, or [Agent Surfaces](/docs/agent-surfaces)
401
- for how this same action can stay headless, render in chat, or grow into a full
402
- screen.
388
+ `"data-insights"`, with server-safe builders and schemas in
389
+ `@agent-native/core/data-widgets`. See [Native Chat UI](/docs/native-chat-ui)
390
+ for the full result contract and BYO runtime guidance, or
391
+ [Agent Surfaces](/docs/agent-surfaces) for how the same action can stay
392
+ headless, render in chat, or grow into a full screen.
403
393
 
404
394
  ## Calling it from the CLI {#cli}
405
395
 
@@ -417,9 +407,9 @@ If your app is an [A2A](/docs/a2a-protocol) peer, other agent-native apps discov
417
407
 
418
408
  ## Exposing it over MCP {#mcp}
419
409
 
420
- With MCP enabled, your actions show up in the framework's MCP server at `/_agent-native/mcp`. Every caller gets a compact catalog by default — code/stdio developer clients, the local CLI proxy, and chat-style app hosts (OAuth MCP Apps callers and generic authenticated remote HTTP/static-token callers) alike — containing app-facing builtins (`open_app`, `list_apps`, `ask_app`, and app-only embed helpers) plus the template-declared app actions; action-specific MCP App resources stay out of that catalog unless an action explicitly sets `mcpApp.compactCatalog: true`. `tool-search` is always present (call it with no query for the full tool menu, or with a query for ranked matches), so any tool stays reachable on demand. The full action surface is served only on explicit opt-in (`--full-catalog` token or `AGENT_NATIVE_MCP_FULL_CATALOG=1`). `publicAgent.expose` is still the opt-in for safe read/ingest tools outside that compact app catalog. See [MCP Protocol](/docs/mcp-protocol).
410
+ With MCP enabled, your actions show up in the framework's MCP server at `/_agent-native/mcp`. Every caller gets a compact catalog by default — app-facing builtins plus the template-declared app actions and `tool-search` is always present so any other tool stays reachable on demand. The full action surface is served only on explicit opt-in (`--full-catalog` token or `AGENT_NATIVE_MCP_FULL_CATALOG=1`), and `publicAgent.expose` opts a safe read/ingest tool onto the public surface. See [MCP Protocol](/docs/mcp-protocol) for catalog tiers, auth, and the `mcpApp` resource details.
421
411
 
422
- For UI-capable MCP hosts, an action can also declare an optional MCP Apps resource via the `mcpApp` field (and a matching `link`) so capable hosts render the result inline. The pattern mirrors the focused link we already return for external agents: the action exposes the operation, `link` points at the route with the right URL or deep-link params, and the embed helper uses that same target as the inline app. When an action's `link` and `mcpApp` should point at the same route, use `embedRoute()` to build both from one pure path builder.
412
+ For UI-capable MCP hosts, an action can declare an optional MCP Apps resource via the `mcpApp` field (plus a matching `link`) so capable hosts render the result inline. When `link` and `mcpApp` should point at the same route, `embedRoute()` builds both from one pure path builder:
423
413
 
424
414
  ```ts
425
415
  import { embedRoute } from "@agent-native/core";
@@ -15,23 +15,60 @@ you want, then use the matching primitive.
15
15
 
16
16
  | Surface | Use it when | Start with |
17
17
  | ----------------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
18
- | **Headless agent/actions** | Code, jobs, scripts, another app, or another agent should call the work directly. | `agent-native create --headless`, `defineAction`, `agent-native agent`, HTTP, CLI, MCP, A2A |
18
+ | **Headless agent** | Code, jobs, scripts, another app, or another agent should call the work directly. | `agent-native create --headless`, `defineAction`, `agent-native agent`, HTTP, CLI, MCP, A2A |
19
19
  | **Rich chat on Agent-Native** | You want a standalone or embedded chat backed by the built-in agent loop. | [Chat template](/docs/template-chat), `<AgentChatSurface>`, `<AssistantChat>` |
20
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
21
  | **Embedded sidecar** | You already have a SaaS app and want an agent beside it with page context and host commands. | `createAgentNativeEmbeddedPlugin()`, `AgentNativeEmbedded` |
22
22
  | **Full application** | Humans and agents should share durable screens, data, navigation, and collaboration. | Templates, actions, SQL state, context awareness |
23
23
 
24
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.
25
+ agent with one action, appear in chat as a table or chart, and later become a
26
+ full screen in an app without changing the operation the agent calls.
27
27
 
28
- ## Headless agent/actions {#headless}
28
+ ## Headless agent {#headless}
29
29
 
30
30
  Use the headless path when no one needs to stare at a custom app screen while
31
31
  the work runs: scheduled jobs, integrations, backend workflows, CLI loops,
32
32
  another agent, or an existing product calling into Agent-Native.
33
33
 
34
- The smallest local path is a headless scaffold plus one action:
34
+ This is also the shape to reach for when **the agent _is_ the product** — the
35
+ app-agent loop is the front door, not a dashboard. You send a request from the
36
+ terminal, Slack, email, a scheduled job, another agent, or Chat — "summarize my
37
+ unread emails," "post the daily metrics to Slack," "find the candidates who
38
+ replied last week" — and the agent acts and returns the result wherever it
39
+ belongs. It is still a real app, not a stateless prompt: actions, auth sessions,
40
+ app state, thread/run history, settings, credentials, and share records all live
41
+ in SQL.
42
+
43
+ Pick this pattern when:
44
+
45
+ - **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.
46
+ - **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.
47
+ - **The domain is one-shot.** Research bot, summary generator, report writer — no persistent object that needs a list view.
48
+ - **You're prototyping.** Ship the agent now; add richer UI later if users want one.
49
+
50
+ If your product is built around persistent objects users browse, pivot, and
51
+ share — emails, events, documents, charts — pick a [full application](#full-application)
52
+ or a [template](/docs/cloneable-saas) instead; those add a full UI _plus_ the agent.
53
+
54
+ ### What ships in the box {#in-the-box}
55
+
56
+ A headless app skips weeks of dashboard work, and it's channel-agnostic from day
57
+ one — the same agent runs from the web, Slack, Telegram, email, and other agents
58
+ because everything goes through the agent, not the UI. The trade-off is there's
59
+ no "browse-everything-at-a-glance" view; if users need that, mix patterns and
60
+ add a small status page or list view.
61
+
62
+ When you add the built-in Chat shell, the framework provides five management
63
+ surfaces you don't have to build: **Chat** (the main input), **Workspace**
64
+ (skills, memory, instructions, sub-agents, connected MCP servers, scheduled
65
+ jobs), **Job history**, **Thread history**, and **Settings**. Those are usually
66
+ enough — talk to it, see what it's done, configure how it behaves. Reach for
67
+ [Chat](/docs/template-chat) when you're ready to add that browser UI, or the
68
+ [Dispatch template](/docs/template-dispatch) for a workspace-style starting
69
+ point with Slack/Telegram, scheduled jobs, and shared secrets out of the box.
70
+
71
+ The smallest local path is a headless agent scaffold plus one action:
35
72
 
36
73
  ```bash
37
74
  npx @agent-native/core@latest create my-agent --headless
@@ -81,45 +118,20 @@ If another app or script needs to call the whole agent, use
81
118
  `agentNative.invoke("analytics", "...")` or the `agent-native invoke` CLI. That
82
119
  keeps cross-app work on the A2A path while local work stays on actions.
83
120
 
84
- Workers, jobs, integration webhooks, and custom hosts can use the server API
85
- directly. This is lower-level than actions: you provide the engine, model,
86
- messages, actions, and event sink yourself.
121
+ Workers, jobs, integration webhooks, and custom hosts can drive the agent loop
122
+ directly through the server API. This is lower-level than actions you provide
123
+ the engine, model, messages, actions, and event sink yourself:
87
124
 
88
125
  ```ts
89
- import {
90
- actionsToEngineTools,
91
- resolveEngine,
92
- runAgentLoop,
93
- } from "@agent-native/core/server";
94
-
95
- const engine = await resolveEngine({ engineOption: undefined });
96
- const model = engine.defaultModel;
97
- const controller = new AbortController();
98
-
99
- await runAgentLoop({
100
- engine,
101
- model,
102
- systemPrompt: "You are the reporting agent for this workspace.",
103
- actions,
104
- tools: actionsToEngineTools(actions),
105
- messages: [
106
- {
107
- role: "user",
108
- content: [{ type: "text", text: "Summarize this week's forms." }],
109
- },
110
- ],
111
- send: (event) => {
112
- // Persist, log, stream, or translate AgentChatEvent objects.
113
- },
114
- signal: controller.signal,
115
- ownerEmail: user.email,
116
- orgId: user.orgId,
117
- });
126
+ import { runAgentLoop } from "@agent-native/core/server";
127
+
128
+ await runAgentLoop({ engine, model, systemPrompt, actions, messages, send });
118
129
  ```
119
130
 
120
131
  For most apps, scheduled prompts and integration webhooks already call this loop
121
- for you. Reach for direct `runAgentLoop()` when you are building a custom
122
- headless host, eval runner, or server-side orchestration surface.
132
+ for you. Reach for it directly only when building a custom headless host, eval
133
+ runner, or server-side orchestration surface — see [Server — Production agent
134
+ handler](/docs/server#agent-handler) for the full signature.
123
135
 
124
136
  ### Running against a folder {#folder-loop}
125
137
 
@@ -178,6 +190,48 @@ export default function ChatRoute() {
178
190
  }
179
191
  ```
180
192
 
193
+ When an app has both a full-page chat tab and an `AgentSidebar`, use the same
194
+ `storageKey` on both surfaces, enable `chatViewTransition`, and install the
195
+ chat-home handoff helpers in the layout. Ordinary in-app links out of the chat
196
+ page can then morph the full chat into the sidebar while keeping the active
197
+ thread:
198
+
199
+ ```tsx
200
+ import {
201
+ AgentChatSurface,
202
+ AgentSidebar,
203
+ useAgentChatHomeHandoff,
204
+ useAgentChatHomeHandoffLinks,
205
+ } from "@agent-native/core/client/chat";
206
+ import { useLocation } from "react-router";
207
+
208
+ function ChatRoute() {
209
+ return (
210
+ <AgentChatSurface mode="page" storageKey="my-app" chatViewTransition />
211
+ );
212
+ }
213
+
214
+ function AppLayout({ children }: { children: React.ReactNode }) {
215
+ const location = useLocation();
216
+ const handoffActive = useAgentChatHomeHandoff({
217
+ storageKey: "my-app",
218
+ activePath: location.pathname,
219
+ enabled: location.pathname !== "/chat",
220
+ });
221
+ useAgentChatHomeHandoffLinks({ storageKey: "my-app", chatPath: "/chat" });
222
+
223
+ return (
224
+ <AgentSidebar
225
+ storageKey="my-app"
226
+ chatViewTransition
227
+ openOnChatRunning={handoffActive}
228
+ >
229
+ {children}
230
+ </AgentSidebar>
231
+ );
232
+ }
233
+ ```
234
+
181
235
  The simplest embedded chat with your own chrome:
182
236
 
183
237
  ```tsx
@@ -195,14 +249,9 @@ components in the chat, without iframes. See [Native Chat UI](/docs/native-chat-
195
249
  ## Rich chat on your agent {#byo-agent}
196
250
 
197
251
  Use this path when your agent is already built with another framework or
198
- runtime and you want Agent-Native's chat UI around it.
199
-
200
- The [Chat template](/docs/template-chat) is still useful here: keep its app
201
- shell and thread UI, then swap the runtime behind the chat plugin or route.
202
-
203
- `AgentChatRuntime` is the boundary. Your runtime streams normalized events;
204
- Agent-Native renders the composer, transcript, tool calls, approvals, native
205
- widgets, and app layout.
252
+ runtime and you want Agent-Native's chat UI around it. `AgentChatRuntime` is the
253
+ boundary: your runtime streams normalized events, and Agent-Native renders the
254
+ composer, transcript, tool calls, approvals, native widgets, and app layout.
206
255
 
207
256
  ```tsx
208
257
  import {
@@ -211,10 +260,7 @@ import {
211
260
  } from "@agent-native/core/client/chat";
212
261
 
213
262
  const runtime = createHttpAgentChatRuntime({
214
- id: "external:support-agent",
215
- label: "Support agent",
216
263
  endpoint: "/api/support-agent/chat",
217
- headers: async () => ({ Authorization: `Bearer ${await getToken()}` }),
218
264
  });
219
265
 
220
266
  export function SupportAgentChat() {
@@ -222,40 +268,15 @@ export function SupportAgentChat() {
222
268
  }
223
269
  ```
224
270
 
225
- Use `createOpenAIAgentsChatRuntime()`,
226
- `createOpenAIResponsesChatRuntime()`, `createClaudeAgentChatRuntime()`,
227
- `createVercelAiChatRuntime()`, or `createAgUiChatRuntime()` when your endpoint
228
- already streams one of those event shapes. Use `createHttpAgentChatRuntime()`
229
- when your agent streams Agent-Native's normalized event shape directly:
230
-
231
- ```ts
232
- import { createOpenAIAgentsChatRuntime } from "@agent-native/core/client/chat";
233
-
234
- const runtime = createOpenAIAgentsChatRuntime({
235
- endpoint: "/api/openai-agent/chat",
236
- });
237
- ```
238
-
239
- The endpoint can stream SSE or NDJSON events:
240
-
241
- ```txt
242
- data: {"type":"message-delta","messageId":"m1","delta":{"type":"text","text":"I found 34 submissions."}}
243
- data: {"type":"tool-start","toolCall":{"id":"t1","name":"query","input":{"formId":"form_123"}}}
244
- data: {"type":"tool-done","toolCallId":"t1","toolName":"query","status":"completed","resultText":"34 rows"}
245
- data: {"type":"done","reason":"complete"}
246
- ```
247
-
248
- For a trivial integration, returning `{ "text": "..." }` also works. For richer
249
- integrations, stream `message-*`, `tool-*`, `approval-request`, `status`,
250
- `artifact`, `file`, `usage`, `error`, and `done` events. Tool results can carry
251
- `chatUI` metadata so the same native table/chart/card renderers work with your
252
- agent too.
271
+ Ready-made runtime helpers exist for OpenAI Agents, OpenAI Responses, the Claude
272
+ Agent SDK, the Vercel AI SDK, and AG-UI, plus the normalized HTTP runtime above
273
+ for any other agent (Mastra, Flue, Eve, LangGraph, or a custom service). ACP is
274
+ not the default end-user app chat protocol, and Agent-Native does not currently
275
+ claim A2UI support.
253
276
 
254
- This is the right place to adapt the OpenAI Agents SDK, Claude Agent SDK, Vercel
255
- AI SDK, Mastra, Flue, Eve, LangGraph, a custom service, or an AG-UI-compatible
256
- event stream. Do not use ACP as the default end-user app chat protocol; ACP is
257
- better framed as coding-agent/editor interoperability. Agent-Native does not
258
- currently claim A2UI support.
277
+ [Native Chat UI BYO agent runtimes](/docs/native-chat-ui#byo-agent-runtimes)
278
+ is the canonical home for the event shapes, the runtime helpers, and `chatUI`
279
+ tool-result metadata. Start there when wiring an external agent into the chat.
259
280
 
260
281
  ## Embedded sidecar {#embedded-sidecar}
261
282
 
@@ -322,7 +343,7 @@ want a complete product shape.
322
343
 
323
344
  | If you are thinking... | Choose |
324
345
  | --------------------------------------------------------------- | ------------------------- |
325
- | "I just need a callable tool or workflow." | Headless action |
346
+ | "I just need a callable tool or workflow." | Headless agent |
326
347
  | "I want the framework's agent, but chat should be the main UI." | Rich chat on Agent-Native |
327
348
  | "I already have an agent; I need a polished chat UI for it." | Rich chat on your agent |
328
349
  | "I already have a SaaS app; add an agent beside it." | Embedded sidecar |
@@ -127,19 +127,7 @@ interface AgentTask {
127
127
 
128
128
  ## Custom agent profiles {#profiles}
129
129
 
130
- A custom agent is a Markdown file in the workspace. Minimal example:
131
-
132
- ```markdown
133
- ---
134
- name: Code Review
135
- description: Reviews TypeScript PRs for correctness and type safety.
136
- model: inherit
137
- ---
138
-
139
- You are a meticulous code reviewer. Be terse and concrete — cite file:line wherever you can.
140
- ```
141
-
142
- Store at `agents/code-review.md` in the workspace. It appears in the `@mention` dropdown and is available to the main agent as a delegation target. See [Workspace — Custom Agents](/docs/workspace#custom-agents) for the full format including `tools`, `delegate-default`, and model overrides.
130
+ Sub-agents map to custom agent profiles Markdown files at `agents/<slug>.md` in the workspace that appear in the `@mention` dropdown and serve as delegation targets. [Workspace — Custom Agents](/docs/workspace#custom-agents) owns the full format (frontmatter, `tools`, `delegate-default`, model overrides).
143
131
 
144
132
  ## Delegation depth guard {#depth-guard}
145
133
 
@@ -147,7 +135,7 @@ Sub-agents can spawn sub-agents, which is a runaway/cost risk: an unbounded chai
147
135
 
148
136
  The top-level chat is depth `0`. A sub-agent it spawns is depth `1`; that sub-agent may spawn once more (depth `2`); a spawn that would create a depth-`3` sub-agent is **refused**. The default cap is **2**.
149
137
 
150
- ```txt
138
+ ```text
151
139
  depth 0 top-level chat (may spawn)
152
140
  depth 1 sub-agent (may spawn)
153
141
  depth 2 sub-agent's sub-agent (at the cap — may NOT spawn)
@@ -1,11 +1,11 @@
1
1
  ---
2
- title: "Agent Web Surfaces"
3
- description: "Make public routes crawlable, readable, citable, and optionally callable by agents."
2
+ title: "Public Agent Web"
3
+ description: "Make public routes crawlable, readable, citable, and optionally callable by agents — robots.txt, llms.txt, markdown mirrors, JSON-LD, and a public MCP surface."
4
4
  ---
5
5
 
6
- # Agent Web Surfaces
6
+ # Public Agent Web
7
7
 
8
- Agent Web surfaces make public Agent-Native routes easy for agents to crawl, read, cite, and call. The goal is not to make every app endpoint public. The goal is to publish a clean public surface for pages that are already public, while keeping private data and tool access behind explicit controls.
8
+ The public agent web makes public Agent-Native routes easy for agents to crawl, read, cite, and call. The goal is not to make every app endpoint public. The goal is to publish a clean public surface for pages that are already public, while keeping private data and tool access behind explicit controls.
9
9
 
10
10
  The docs site is the reference implementation. Today it ships:
11
11
 
@@ -9,13 +9,25 @@ Agent-native apps use [Better Auth](https://better-auth.com) for authentication
9
9
 
10
10
  ## Overview {#overview}
11
11
 
12
- Auth is configured automatically via `autoMountAuth(app)` in the auth server plugin. The behavior depends on your environment:
12
+ Auth is configured automatically via `autoMountAuth(app)` in the auth server plugin. There are three modes:
13
13
 
14
14
  - **Default:** Better Auth with email/password + social providers. Onboarding page shown on first visit.
15
15
  - **Remote MCP OAuth:** Standard OAuth 2.1 for MCP hosts such as Claude Code and ChatGPT connectors.
16
16
  - **Custom:** Bring your own auth via `getSession` callback.
17
17
 
18
- Local development uses the same Better Auth flow as production — there is no dev auth bypass, and `getSession()` never falls back to a `local@localhost` sentinel. The first time you load a template the framework auto-creates a throwaway dev account and signs you in, so you are not stuck at a login wall (disable with `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT=1` to use the normal signup page). Set `AUTH_DISABLED=true` (or `AUTH_DISABLED=1`) to skip login/signup entirely and run every request as a shared user — for local dev, cloud previews, and internal demos only, not production with real users. `AUTH_MODE=local` only affects CLI/agent identity resolution (which signed-in dev user `pnpm action` runs as) — it is not a browser login bypass. Email verification is skipped by default in development (and when no email provider is configured), so signup is just an email + password.
18
+ The browser flow is the same Better Auth flow everywhere — there is **no dev auth bypass**, and `getSession()` never falls back to a `local@localhost` sentinel. What changes between environments is signup friction, not the login wall:
19
+
20
+ | Environment | First-load behavior | Email verification |
21
+ | ---------------- | ----------------------------------------------------------------------------- | ----------------------------------------------- |
22
+ | **Local dev** | Auto-creates a throwaway dev account and signs you in (no login wall) | Skipped by default (and when no email provider) |
23
+ | **QA / preview** | Normal signup, but verification can be skipped so testers don't wait on email | Skip with `AUTH_SKIP_EMAIL_VERIFICATION=1` |
24
+ | **Production** | Normal Better Auth signup/login | Required (when an email provider is configured) |
25
+
26
+ A few flags tune this; full details are in the [Environment Variables](#environment-variables) table:
27
+
28
+ - `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT=1` — use the normal signup page in local dev instead of the auto dev account.
29
+ - `AUTH_DISABLED=true` — skip login/signup entirely and run every request as one shared user (local dev / previews / demos only, never production with real users).
30
+ - `AUTH_MODE=local` — affects only CLI/agent identity (which dev user `pnpm action` runs as); it is **not** a browser login bypass.
19
31
 
20
32
  ## Better Auth (Default) {#better-auth}
21
33
 
@@ -36,24 +48,23 @@ Better Auth routes are mounted at `/_agent-native/auth/ba/*`. The framework also
36
48
 
37
49
  ## Cookie Realms {#cookie-realms}
38
50
 
39
- Standalone apps keep their framework session cookie isolated by app when an
40
- app slug is available (`APP_NAME`, or the package name in local dev). Better
41
- Auth keeps its production standalone cookie prefix stable as `an` so existing
42
- sessions are not renamed casually.
43
-
44
- Workspace mode (`AGENT_NATIVE_WORKSPACE=1`) uses one shared session realm
45
- because workspace apps share an origin and database. Custom same-database
46
- subdomain deployments can opt into shared cookies with `COOKIE_DOMAIN`.
47
-
48
- First-party hosted apps at `*.agent-native.com` are different: each app has its
49
- own auth database, so `COOKIE_DOMAIN=.agent-native.com` is ignored by default
50
- and the app uses an isolated cookie namespace instead. Cross-app sign-in for
51
- those apps should go through [Cross-App SSO](/docs/cross-app-sso). First-party
52
- deploys must provide `APP_NAME` or a derivable app URL (`APP_URL`, `URL`,
53
- `DEPLOY_PRIME_URL`, or `DEPLOY_URL`); otherwise startup fails instead of
54
- falling back to the shared `an_session` name. If a first-party deployment
55
- intentionally shares one auth database across subdomains, set
56
- `AGENT_NATIVE_SHARE_COOKIE_DOMAIN=1` alongside `COOKIE_DOMAIN`.
51
+ The session cookie's realm follows the deployment shape, so apps that share a
52
+ database/origin share sign-in and apps that don't stay isolated:
53
+
54
+ | Deployment shape | Cookie realm |
55
+ | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
56
+ | Standalone app | Isolated per app by slug (`APP_NAME`, or package name in local dev); stable `an` prefix in production |
57
+ | Workspace mode (`AGENT_NATIVE_WORKSPACE=1`) | One shared realm — workspace apps share an origin and database |
58
+ | Custom same-database subdomains | Opt into shared cookies with `COOKIE_DOMAIN` |
59
+ | First-party hosted (`*.agent-native.com`) | Isolated namespace per app (each has its own auth database); `COOKIE_DOMAIN=.agent-native.com` is ignored by default |
60
+
61
+ First-party hosted apps each have their own auth database, so cross-app sign-in
62
+ goes through [Cross-App SSO](/docs/cross-app-sso) rather than a shared cookie.
63
+ These deploys must provide `APP_NAME` or a derivable app URL (`APP_URL`, `URL`,
64
+ `DEPLOY_PRIME_URL`, or `DEPLOY_URL`); otherwise startup fails instead of falling
65
+ back to the shared `an_session` name. To intentionally share one auth database
66
+ across subdomains, set `AGENT_NATIVE_SHARE_COOKIE_DOMAIN=1` alongside
67
+ `COOKIE_DOMAIN`.
57
68
 
58
69
  ## QA Accounts {#qa-accounts}
59
70
 
@@ -136,7 +147,7 @@ Access tokens are signed with `A2A_SECRET` when set, otherwise `BETTER_AUTH_SECR
136
147
 
137
148
  Pass a custom `getSession` callback to use any auth provider (Clerk, Auth0, Firebase, etc.):
138
149
 
139
- ```typescript
150
+ ```ts
140
151
  // server/plugins/auth.ts
141
152
  import { createAuthPlugin } from "@agent-native/core/server";
142
153
 
@@ -191,7 +202,7 @@ unless the app explicitly adds those prefixes to
191
202
 
192
203
  The session object returned by `getSession(event)` has this shape:
193
204
 
194
- ```typescript
205
+ ```ts
195
206
  interface AuthSession {
196
207
  email: string; // User's email (primary identifier)
197
208
  userId?: string; // Better Auth user ID
@@ -205,7 +216,7 @@ interface AuthSession {
205
216
 
206
217
  On the client, use the `useSession()` hook:
207
218
 
208
- ```typescript
219
+ ```ts
209
220
  import { useSession } from "@agent-native/core/client";
210
221
 
211
222
  function MyComponent() {
@@ -253,7 +264,7 @@ Both flows (the explicit `/_agent-native/sign-in` entrypoint and the bookmarked-
253
264
 
254
265
  If your template wraps `/_agent-native/google/auth-url` directly (e.g. mail and calendar templates do, to widen scopes), accept a `?return=<path>` query and forward it via the options-object form of `encodeOAuthState`:
255
266
 
256
- ```typescript
267
+ ```ts
257
268
  const returnUrl = getQuery(event).return;
258
269
  const state = encodeOAuthState({
259
270
  redirectUri,
@@ -272,6 +283,11 @@ The default `/_agent-native/google/auth-url` route does this automatically — o
272
283
  | `AUTH_SKIP_EMAIL_VERIFICATION` | Set to `1` in QA/preview environments to let email/password signups proceed without verification; local dev/test skips by default |
273
284
  | `AUTH_DISABLED` | Set to `true` or `1` to skip login/signup; all requests run as one shared user (local dev/preview only — not for production with real users) |
274
285
  | `AGENT_NATIVE_DISABLE_AUTO_DEV_ACCOUNT` | Set to `1` to disable localhost auto-sign-in on a fresh dev database |
286
+ | `AUTH_MODE` | `local` resolves CLI/agent identity only (which dev user `pnpm action` runs as); never a browser login bypass |
287
+ | `COOKIE_DOMAIN` | Opt into shared session cookies across same-database subdomains (see [Cookie Realms](#cookie-realms)) |
288
+ | `AGENT_NATIVE_WORKSPACE` | `1` runs in workspace mode — one shared session realm across workspace apps |
289
+ | `AGENT_NATIVE_SHARE_COOKIE_DOMAIN` | Set with `COOKIE_DOMAIN` to share one auth database across first-party subdomains |
290
+ | `OAUTH_STATE_SECRET` | Dedicated HMAC key for OAuth state envelopes (see [Security — OAuth State Signing](/docs/security#oauth-state)) |
275
291
  | `GOOGLE_CLIENT_ID` | Enable Google OAuth |
276
292
  | `GOOGLE_CLIENT_SECRET` | Google OAuth secret |
277
293
  | `GITHUB_CLIENT_ID` | Enable GitHub OAuth |