@agent-native/core 0.63.0 → 0.63.2

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 (134) hide show
  1. package/dist/agent/harness/ai-sdk-adapter.d.ts +44 -0
  2. package/dist/agent/harness/ai-sdk-adapter.d.ts.map +1 -1
  3. package/dist/agent/harness/ai-sdk-adapter.js +120 -1
  4. package/dist/agent/harness/ai-sdk-adapter.js.map +1 -1
  5. package/dist/agent/harness/index.d.ts +1 -1
  6. package/dist/agent/harness/index.d.ts.map +1 -1
  7. package/dist/agent/harness/index.js.map +1 -1
  8. package/dist/cli/code-agent-executor.js +1 -1
  9. package/dist/cli/code-agent-executor.js.map +1 -1
  10. package/dist/cli/create.js +1 -1
  11. package/dist/cli/create.js.map +1 -1
  12. package/dist/client/NewWorkspaceAppFlow.js +1 -1
  13. package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
  14. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
  15. package/dist/client/blocks/library/AnnotatedCodeBlock.js +29 -10
  16. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
  17. package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
  18. package/dist/client/blocks/library/DiffBlock.js +48 -20
  19. package/dist/client/blocks/library/DiffBlock.js.map +1 -1
  20. package/dist/client/blocks/library/diagram.d.ts.map +1 -1
  21. package/dist/client/blocks/library/diagram.js +14 -3
  22. package/dist/client/blocks/library/diagram.js.map +1 -1
  23. package/dist/client/blocks/library/wireframe.d.ts.map +1 -1
  24. package/dist/client/blocks/library/wireframe.js +14 -3
  25. package/dist/client/blocks/library/wireframe.js.map +1 -1
  26. package/dist/client/blocks/types.d.ts +5 -0
  27. package/dist/client/blocks/types.d.ts.map +1 -1
  28. package/dist/client/blocks/types.js.map +1 -1
  29. package/dist/client/chat/index.d.ts +2 -1
  30. package/dist/client/chat/index.d.ts.map +1 -1
  31. package/dist/client/chat/index.js +2 -1
  32. package/dist/client/chat/index.js.map +1 -1
  33. package/dist/client/chat-view-transition.d.ts +17 -0
  34. package/dist/client/chat-view-transition.d.ts.map +1 -1
  35. package/dist/client/chat-view-transition.js +46 -0
  36. package/dist/client/chat-view-transition.js.map +1 -1
  37. package/dist/client/index.d.ts +2 -1
  38. package/dist/client/index.d.ts.map +1 -1
  39. package/dist/client/index.js +2 -1
  40. package/dist/client/index.js.map +1 -1
  41. package/dist/client/use-agent-chat-home-handoff.d.ts +27 -0
  42. package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -0
  43. package/dist/client/use-agent-chat-home-handoff.js +120 -0
  44. package/dist/client/use-agent-chat-home-handoff.js.map +1 -0
  45. package/dist/index.d.ts +1 -1
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +3 -1
  48. package/dist/index.js.map +1 -1
  49. package/dist/server/action-discovery.d.ts.map +1 -1
  50. package/dist/server/action-discovery.js +24 -2
  51. package/dist/server/action-discovery.js.map +1 -1
  52. package/dist/server/deep-link.d.ts +2 -2
  53. package/dist/server/deep-link.d.ts.map +1 -1
  54. package/dist/server/deep-link.js +2 -2
  55. package/dist/server/deep-link.js.map +1 -1
  56. package/dist/styles/agent-native.css +2 -6
  57. package/dist/tailwind.preset.d.ts.map +1 -1
  58. package/dist/tailwind.preset.js +8 -1
  59. package/dist/tailwind.preset.js.map +1 -1
  60. package/dist/templates/default/package.json +1 -0
  61. package/dist/templates/headless/AGENTS.md +3 -0
  62. package/dist/templates/headless/DEVELOPING.md +4 -0
  63. package/dist/templates/headless/actions/run.ts +6 -0
  64. package/dist/templates/workspace-root/README.md +4 -4
  65. package/docs/content/actions.md +32 -42
  66. package/docs/content/agent-surfaces.md +105 -84
  67. package/docs/content/agent-teams.md +2 -14
  68. package/docs/content/agent-web-surfaces.md +4 -4
  69. package/docs/content/authentication.md +40 -24
  70. package/docs/content/automations.md +18 -36
  71. package/docs/content/blueprint-installer.md +3 -0
  72. package/docs/content/cli-adapters.md +24 -168
  73. package/docs/content/client.md +11 -77
  74. package/docs/content/cloneable-saas.md +1 -1
  75. package/docs/content/code-agents-ui.md +44 -0
  76. package/docs/content/components.md +10 -23
  77. package/docs/content/context-awareness.md +3 -3
  78. package/docs/content/creating-templates.md +20 -18
  79. package/docs/content/database.md +1 -1
  80. package/docs/content/deployment.md +5 -37
  81. package/docs/content/dispatch.md +17 -28
  82. package/docs/content/drop-in-agent.md +24 -111
  83. package/docs/content/durable-resume.md +4 -0
  84. package/docs/content/embedding-sdk.md +141 -135
  85. package/docs/content/evals.md +3 -3
  86. package/docs/content/extensions.md +1 -1
  87. package/docs/content/external-agents.md +35 -61
  88. package/docs/content/faq.md +5 -5
  89. package/docs/content/frames.md +13 -4
  90. package/docs/content/getting-started.md +96 -142
  91. package/docs/content/harness-agents.md +53 -9
  92. package/docs/content/human-approval.md +1 -1
  93. package/docs/content/key-concepts.md +14 -99
  94. package/docs/content/local-file-mode.md +2 -2
  95. package/docs/content/mcp-apps.md +9 -2
  96. package/docs/content/mcp-clients.md +8 -3
  97. package/docs/content/mcp-protocol.md +11 -29
  98. package/docs/content/messaging.md +1 -1
  99. package/docs/content/migration-workbench.md +14 -175
  100. package/docs/content/multi-app-workspace.md +1 -1
  101. package/docs/content/multi-tenancy.md +18 -47
  102. package/docs/content/native-chat-ui.md +15 -12
  103. package/docs/content/observability.md +16 -4
  104. package/docs/content/observational-memory.md +1 -1
  105. package/docs/content/pure-agent-apps.md +17 -124
  106. package/docs/content/real-time-collaboration.md +14 -14
  107. package/docs/content/routing.md +71 -0
  108. package/docs/content/sandbox-adapters.md +78 -4
  109. package/docs/content/security.md +59 -39
  110. package/docs/content/server.md +16 -8
  111. package/docs/content/sharing.md +1 -6
  112. package/docs/content/skills-guide.md +3 -1
  113. package/docs/content/template-analytics.md +1 -1
  114. package/docs/content/template-assets.md +12 -3
  115. package/docs/content/template-brain.md +64 -72
  116. package/docs/content/template-chat.md +32 -4
  117. package/docs/content/template-clips.md +35 -4
  118. package/docs/content/template-design.md +19 -3
  119. package/docs/content/template-dispatch.md +9 -0
  120. package/docs/content/template-forms.md +15 -10
  121. package/docs/content/template-plan.md +13 -5
  122. package/docs/content/template-slides.md +14 -14
  123. package/docs/content/template-videos.md +10 -12
  124. package/docs/content/tracking.md +66 -55
  125. package/docs/content/using-your-agent.md +6 -16
  126. package/docs/content/what-is-agent-native.md +5 -11
  127. package/docs/content/workspace-management.md +2 -2
  128. package/docs/content/workspace.md +20 -160
  129. package/package.json +6 -2
  130. package/src/templates/default/package.json +1 -0
  131. package/src/templates/headless/AGENTS.md +3 -0
  132. package/src/templates/headless/DEVELOPING.md +4 -0
  133. package/src/templates/headless/actions/run.ts +6 -0
  134. package/src/templates/workspace-root/README.md +4 -4
@@ -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 |
@@ -7,7 +7,7 @@ description: "Event-triggered and scheduled automations with natural-language co
7
7
 
8
8
  An **automation** is a rule: _when X happens, do Y_ — described in natural language. The agent executes the instructions, so automations have access to every action, tool, and MCP server the agent can use in an interactive chat.
9
9
 
10
- Automations extend [recurring jobs](/docs/recurring-jobs) with **event triggers**, **natural-language conditions**, and **outbound HTTP** via the `web-request` tool. They live as markdown resources under `jobs/` the same storage and format as recurring jobs, with extra frontmatter fields.
10
+ Automations extend [recurring jobs](/docs/recurring-jobs) with **event triggers**, **natural-language conditions**, and **outbound HTTP** via the `web-request` tool. They use the same `jobs/<name>.md` file format, storage, and "create three ways" workflow as recurring jobs see [Recurring Jobs](/docs/recurring-jobs#job-file) for the shared format. This page covers only what's new for event-driven automations.
11
11
 
12
12
  ## Two trigger types {#trigger-types}
13
13
 
@@ -30,17 +30,10 @@ The agent discovers available events, confirms the plan, and writes the automati
30
30
 
31
31
  Automations appear in the settings panel. Users can view, enable/disable, and delete them there.
32
32
 
33
- ### Via the API
33
+ The third path — writing the `jobs/<name>.md` file by hand via `resourcePut` — works exactly as it does for [recurring jobs](/docs/recurring-jobs#creating). For an event-driven automation you add the event-trigger frontmatter below to that same file. An event-triggered job sets `schedule: ""` and supplies `triggerType: event`, an `event` name, and an optional `condition`:
34
34
 
35
- Write a `jobs/<name>.md` resource directly:
36
-
37
- ```ts
38
- import { resourcePut } from "@agent-native/core/resources";
39
-
40
- await resourcePut(
41
- ownerEmail,
42
- "jobs/slack-on-builder-booking.md",
43
- `---
35
+ ```yaml
36
+ ---
44
37
  schedule: ""
45
38
  enabled: true
46
39
  triggerType: event
@@ -48,36 +41,25 @@ event: calendar.booking.created
48
41
  condition: "attendee email ends with @builder.io"
49
42
  mode: agentic
50
43
  domain: calendar
51
- createdBy: steve@builder.io
52
44
  runAs: creator
53
45
  ---
54
-
55
46
  Send a Slack message to #sales with the booking details.
56
- Use the web-request tool to POST to \${keys.SLACK_WEBHOOK}.`,
57
- );
47
+ Use the web-request tool to POST to ${keys.SLACK_WEBHOOK}.
58
48
  ```
59
49
 
60
- ## The markdown format {#format}
61
-
62
- Each automation is a markdown file with YAML frontmatter and a body containing natural-language instructions.
63
-
64
- ### Frontmatter fields {#frontmatter}
65
-
66
- | Field | Type | Default | Description |
67
- | ------------- | ------------------------------------------------------ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
68
- | `schedule` | cron expression | `""` | Cron expression (required for schedule triggers) |
69
- | `enabled` | boolean | `true` | Whether the automation is active |
70
- | `triggerType` | `"schedule"` \| `"event"` | `"schedule"` | How the automation fires |
71
- | `event` | string | _(optional)_ | Event name to subscribe to (event triggers only) |
72
- | `condition` | string | _(optional)_ | Natural-language condition evaluated before dispatch |
73
- | `mode` | `"agentic"` \| `"deterministic"` | `"agentic"` | Full agent loop. (`"deterministic"` is reserved but not yet implemented — automations that set it are skipped. Use `"agentic"` for all current automations.) |
74
- | `domain` | string | _(optional)_ | Grouping tag (mail, calendar, clips, etc.) |
75
- | `createdBy` | email | _(auto)_ | Owner email |
76
- | `orgId` | string | _(auto)_ | Org scope; inherited from the creator's active org |
77
- | `runAs` | `"creator"` \| `"shared"` | `"creator"` | Whose API key and permissions to use |
78
- | `lastRun` | ISO timestamp | _(managed)_ | Written by the dispatcher after each run |
79
- | `lastStatus` | `"success"` \| `"error"` \| `"running"` \| `"skipped"` | _(managed)_ | Latest outcome |
80
- | `lastError` | string | _(managed)_ | Error message if the last run failed |
50
+ ## Automation frontmatter {#frontmatter}
51
+
52
+ Automations share every field in the [recurring-jobs frontmatter table](/docs/recurring-jobs#frontmatter). These additional fields control event triggers, conditions, and the execution mode:
53
+
54
+ | Field | Type | Default | Description |
55
+ | ------------- | -------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
56
+ | `triggerType` | `"schedule"` \| `"event"` | `"schedule"` | How the automation fires |
57
+ | `event` | string | _(optional)_ | Event name to subscribe to (event triggers only) |
58
+ | `condition` | string | _(optional)_ | Natural-language condition evaluated before dispatch |
59
+ | `mode` | `"agentic"` \| `"deterministic"` | `"agentic"` | Full agent loop. (`"deterministic"` is reserved but not yet implemented — automations that set it are skipped. Use `"agentic"` for all current automations.) |
60
+ | `domain` | string | _(optional)_ | Grouping tag (mail, calendar, clips, etc.) |
61
+
62
+ For an event trigger, `schedule` is `""` (empty); for a schedule trigger it carries the cron expression. The dispatcher also writes the same managed `lastRun` / `lastStatus` / `lastError` fields the scheduler does, plus a `"skipped"` status when a condition evaluates to false.
81
63
 
82
64
  ## The event bus {#event-bus}
83
65
 
@@ -5,6 +5,9 @@ description: "agent-native add prints a curated Markdown integration recipe to s
5
5
 
6
6
  # Blueprint Installer
7
7
 
8
+ > **Who is this for:** host authors and integrators adding a provider, channel,
9
+ > sandbox backend, or action to a repo by piping a recipe into their coding agent.
10
+
8
11
  `agent-native add` is **not** a dumb scaffolder that writes files for you. It emits a curated Markdown _integration blueprint_ to stdout. You pipe that blueprint into your own coding agent (Claude Code, Codex, …), which applies the changes against the live repo with full context.
9
12
 
10
13
  This fits the agent-applies-changes, filesystem-first house style: the framework supplies the recipe (the canonical files to touch, the rules to honor, the verification step), and the coding agent does the editing.
@@ -1,21 +1,16 @@
1
1
  ---
2
2
  title: "CLI Adapters"
3
- description: "Connect any CLI tool to your agent-native app with a standard adapter interface for discovery and execution."
3
+ description: "Give the agent structured access to any CLI tool (gh, ffmpeg, stripe) through a standard adapter interface one of the two adapter seams covered in the Adapters guide."
4
4
  ---
5
5
 
6
6
  # CLI Adapters
7
7
 
8
- Give your agent structured access to any CLI tool with discovery, execution, and consistent output handling.
8
+ > **Where this fits:** CLI adapters are one of two adapter seams in the
9
+ > framework. The canonical guide is [Adapters](/docs/sandbox-adapters), which
10
+ > covers both this seam and the `run-code` sandbox seam — including the shared
11
+ > edge/serverless constraint. This page is the quick reference for the CLI side.
9
12
 
10
- ## Overview {#overview}
11
-
12
- Agents are great at running CLI commands. But without structure, every script reinvents how to invoke a CLI, check if it's installed, and parse its output.
13
-
14
- CLI adapters solve this with a small interface — similar to file sync adapters but for command-line tools. Each adapter wraps a single CLI (`gh`, `ffmpeg`, `stripe`, `aws`) and provides:
15
-
16
- - **Discovery** — the agent can list what CLIs are available and what they do
17
- - **Availability checks** — is the CLI installed?
18
- - **Consistent execution** — stdout, stderr, and exit code in a standard format
13
+ A CLI adapter wraps a single command-line tool (`gh`, `ffmpeg`, `stripe`, `aws`) so the agent can discover it, check whether it's installed, and run it with a consistent stdout/stderr/exit-code result. Without this seam, every script reinvents how to invoke a CLI and parse its output.
19
14
 
20
15
  ## The interface {#the-interface}
21
16
 
@@ -40,7 +35,7 @@ interface CliResult {
40
35
 
41
36
  ## ShellCliAdapter {#shell-adapter}
42
37
 
43
- For most CLIs, you don't need a custom class. `ShellCliAdapter` wraps any CLI binary with sensible defaults:
38
+ For most CLIs you don't need a custom class `ShellCliAdapter` wraps any binary with sensible defaults:
44
39
 
45
40
  ```ts
46
41
  import { ShellCliAdapter } from "@agent-native/core/adapters/cli";
@@ -54,177 +49,37 @@ const ffmpeg = new ShellCliAdapter({
54
49
  command: "ffmpeg",
55
50
  description: "Audio/video processing and transcoding",
56
51
  timeoutMs: 120_000, // 2 min for long encodes
57
- });
58
-
59
- const stripe = new ShellCliAdapter({
60
- command: "stripe",
61
- description: "Stripe CLI — manage payments, webhooks, and customers",
62
52
  env: { STRIPE_API_KEY: process.env.STRIPE_SECRET_KEY! },
63
53
  });
64
54
  ```
65
55
 
66
- ### Options {#shell-adapter-options}
56
+ Options: `command` (required), `description` (required), `name` (defaults to `command`), `env` (merged with `process.env`), `cwd` (defaults to `process.cwd()`), and `timeoutMs` (default `30000`).
67
57
 
68
- | Option | Type | Description |
69
- | ------------- | ------ | --------------------------------------------------- |
70
- | `command` | string | Binary name or path (required) |
71
- | `description` | string | What the CLI does — shown to the agent (required) |
72
- | `name` | string | Display name (defaults to `command`) |
73
- | `env` | Record | Extra environment variables merged with process.env |
74
- | `cwd` | string | Working directory (defaults to process.cwd()) |
75
- | `timeoutMs` | number | Execution timeout (default: 30000) |
58
+ For custom auth, output parsing, or pre/post processing, implement `CliAdapter` directly instead of using `ShellCliAdapter`.
76
59
 
77
60
  ## Registry {#registry}
78
61
 
79
- The `CliRegistry` collects adapters so the agent can discover what's available at runtime:
62
+ `CliRegistry` collects adapters so the agent can discover what's available at runtime:
80
63
 
81
64
  ```ts
82
65
  import { CliRegistry, ShellCliAdapter } from "@agent-native/core/adapters/cli";
83
66
 
84
67
  const cliRegistry = new CliRegistry();
85
-
86
68
  cliRegistry.register(
87
- new ShellCliAdapter({
88
- command: "gh",
89
- description: "GitHub CLI — manage repos, PRs, issues, and releases",
90
- }),
69
+ new ShellCliAdapter({ command: "gh", description: "GitHub CLI" }),
91
70
  );
92
71
 
93
- cliRegistry.register(
94
- new ShellCliAdapter({
95
- command: "ffmpeg",
96
- description: "Audio/video processing and transcoding",
97
- }),
98
- );
99
-
100
- // List all registered CLIs
101
- cliRegistry.list();
102
- // → [{ name: "gh", ... }, { name: "ffmpeg", ... }]
103
-
104
- // List only installed CLIs
105
- await cliRegistry.listAvailable();
106
- // → [{ name: "gh", ... }] (if ffmpeg isn't installed)
72
+ cliRegistry.list(); // all registered
73
+ await cliRegistry.listAvailable(); // only installed
74
+ await cliRegistry.describe(); // [{ name, description, available }] for discovery
107
75
 
108
- // Get a full summary for agent discovery
109
- await cliRegistry.describe();
110
- // → [{ name: "gh", description: "...", available: true },
111
- // { name: "ffmpeg", description: "...", available: false }]
112
-
113
- // Execute a command
114
76
  const gh = cliRegistry.get("gh");
115
77
  const result = await gh?.execute(["pr", "list", "--json", "title,url"]);
116
- console.log(result?.stdout);
117
- ```
118
-
119
- ## Custom adapters {#custom-adapter}
120
-
121
- When you need more than `ShellCliAdapter` provides — custom auth, output parsing, or pre/post processing — implement `CliAdapter` directly:
122
-
123
- ```ts
124
- import type { CliAdapter, CliResult } from "@agent-native/core/adapters/cli";
125
- import { execFile } from "node:child_process";
126
-
127
- export class DockerAdapter implements CliAdapter {
128
- name = "docker";
129
- description =
130
- "Docker container management — build, run, and manage containers";
131
-
132
- async isAvailable(): Promise<boolean> {
133
- try {
134
- const result = await this.execute([
135
- "info",
136
- "--format",
137
- "{{.ServerVersion}}",
138
- ]);
139
- return result.exitCode === 0;
140
- } catch {
141
- return false;
142
- }
143
- }
144
-
145
- async execute(args: string[]): Promise<CliResult> {
146
- return new Promise((resolve) => {
147
- execFile(
148
- "docker",
149
- args,
150
- {
151
- timeout: 60_000,
152
- maxBuffer: 10 * 1024 * 1024,
153
- encoding: "utf-8",
154
- },
155
- (error, stdout, stderr) => {
156
- resolve({
157
- stdout: stdout ?? "",
158
- stderr: stderr ?? "",
159
- exitCode: (error as any)?.code ?? 0,
160
- });
161
- },
162
- );
163
- });
164
- }
165
- }
166
- ```
167
-
168
- > [!WARNING]
169
- > **Edge and Serverless Compatibility:**
170
- > CLI adapters (both `ShellCliAdapter` and custom adapters using `node:child_process`) rely on Node.js-specific system bindings (`child_process.execFile` or `child_process.spawn`).
171
- > These APIs **do not exist** on edge/worker runtimes (e.g., Cloudflare Workers, Netlify Edge Functions). If you deploy your server routes to these edge presets, executing CLI adapters will throw runtime exceptions. Always ensure CLI adapter endpoints and tasks run in standard Node.js environments (like traditional server containers or serverless Node functions).
172
-
173
- ## Server route {#server-route}
174
-
175
- Expose the registry to the UI via an API route so actions and components can discover and invoke CLIs:
176
-
177
- `createServer()` returns an H3 `{ app, router }`. Mount routes on `router` with H3 handlers (`defineEventHandler`, `readBody`, `getRouterParam`):
178
-
179
- ```ts
180
- // server/index.ts
181
- import { createServer } from "@agent-native/core";
182
- import { CliRegistry, ShellCliAdapter } from "@agent-native/core/adapters/cli";
183
- import {
184
- defineEventHandler,
185
- readBody,
186
- getRouterParam,
187
- setResponseStatus,
188
- } from "h3";
189
-
190
- const { router } = createServer();
191
- const cliRegistry = new CliRegistry();
192
-
193
- cliRegistry.register(
194
- new ShellCliAdapter({
195
- command: "gh",
196
- description: "GitHub CLI",
197
- }),
198
- );
199
-
200
- // Discovery endpoint — agent can query this
201
- router.get(
202
- "/api/cli",
203
- defineEventHandler(async () => {
204
- return await cliRegistry.describe();
205
- }),
206
- );
207
-
208
- // Execution endpoint
209
- router.post(
210
- "/api/cli/:name",
211
- defineEventHandler(async (event) => {
212
- const name = getRouterParam(event, "name");
213
- const adapter = name ? cliRegistry.get(name) : undefined;
214
- if (!adapter) {
215
- setResponseStatus(event, 404);
216
- return { error: "CLI not found" };
217
- }
218
-
219
- const { args } = await readBody(event);
220
- return await adapter.execute(args ?? []);
221
- }),
222
- );
223
78
  ```
224
79
 
225
80
  ## Using from actions {#from-actions}
226
81
 
227
- Actions can use CLI adapters directly for structured access:
82
+ Wrap a CLI call in `defineAction` to expose it on the action surface — `defineAction` is required when the code runs inside the server action surface; use an adapter directly in a `scripts/` file otherwise. Never call `process.exit` in an action; throw an error instead.
228
83
 
229
84
  ```ts
230
85
  // actions/list-prs.ts
@@ -232,10 +87,7 @@ import { defineAction } from "@agent-native/core/action";
232
87
  import { ShellCliAdapter } from "@agent-native/core/adapters/cli";
233
88
  import { z } from "zod";
234
89
 
235
- const gh = new ShellCliAdapter({
236
- command: "gh",
237
- description: "GitHub CLI",
238
- });
90
+ const gh = new ShellCliAdapter({ command: "gh", description: "GitHub CLI" });
239
91
 
240
92
  export default defineAction({
241
93
  description: "List open pull requests via the GitHub CLI.",
@@ -244,7 +96,6 @@ export default defineAction({
244
96
  if (!(await gh.isAvailable())) {
245
97
  throw new Error("GitHub CLI not installed. Run: brew install gh");
246
98
  }
247
-
248
99
  const result = await gh.execute([
249
100
  "pr",
250
101
  "list",
@@ -253,14 +104,19 @@ export default defineAction({
253
104
  "--limit",
254
105
  "10",
255
106
  ]);
256
-
257
107
  if (result.exitCode !== 0) {
258
108
  throw new Error(result.stderr || "gh pr list failed");
259
109
  }
260
-
261
110
  return JSON.parse(result.stdout);
262
111
  },
263
112
  });
264
113
  ```
265
114
 
266
- Or use CLI adapters directly in a script under `scripts/` — adapters are useful when you want discovery, availability checks, and consistent error handling, but `defineAction` is required when the code runs inside the server action surface. Never call `process.exit` in an action; throw an error instead.
115
+ ## Edge and serverless {#edge-serverless}
116
+
117
+ CLI adapters use `node:child_process`, which does not exist on edge/worker runtimes (Cloudflare Workers, Netlify Edge Functions). Run CLI adapter endpoints and tasks in a standard Node.js environment. This constraint is shared with the sandbox seam — see the full discussion in [Adapters](/docs/sandbox-adapters#edge-serverless).
118
+
119
+ ## What's next
120
+
121
+ - [**Adapters**](/docs/sandbox-adapters) — the canonical guide to both adapter seams.
122
+ - [**Actions**](/docs/actions) — the action surface CLI adapters are usually wrapped in.
@@ -9,21 +9,7 @@ description: "React hooks and utilities for agent-native apps: sendToAgentChat,
9
9
 
10
10
  These client/React APIs are exported from both `@agent-native/core` and `@agent-native/core/client`. Import them from `@agent-native/core/client` (the browser entry) for clarity and correct bundling, since the bare `@agent-native/core` root resolves to the Node build by default.
11
11
 
12
- ## File-Based Routing {#file-based-routing}
13
-
14
- Agent-native apps use **React Router v7** with file-based routing via `flatRoutes()` from `@react-router/fs-routes`. Every file in `app/routes/` becomes a URL. Templates use the dot-notation convention — dots separate URL segments inside a single filename.
15
-
16
- ### File → URL mapping
17
-
18
- | File | URL | Notes |
19
- | --------------------- | ------------------ | -------------------------------------- |
20
- | `_index.tsx` | `/` | Index route |
21
- | `settings.tsx` | `/settings` | Simple page |
22
- | `inbox.$threadId.tsx` | `/inbox/:threadId` | Dot = `/`, `$` = dynamic param |
23
- | `_app.tsx` | (no URL segment) | Pathless layout — prefix with `_` |
24
- | `inbox/route.tsx` | `/inbox` | Folder form — `route.tsx` is the index |
25
-
26
- Prefix a segment with `$` for a dynamic param. Prefix with `_` to make it a pathless layout route (no URL segment). Templates use `flatRoutes()` — the dot-notation file above is primary; the nested-folder form `inbox/route.tsx` also works.
12
+ For file-based routing — adding pages, dynamic params, and navigation — see [Routing](/docs/routing).
27
13
 
28
14
  ## Fetching and Mutating Data {#fetching-mutating}
29
15
 
@@ -47,70 +33,9 @@ mutate({ name: "Alice", company: "Acme" });
47
33
  await callAction("archive-lead", { leadId });
48
34
  ```
49
35
 
50
- ---
51
-
52
- ### Adding a new page
53
-
54
- Create the file and export a default component:
55
-
56
- ```tsx
57
- // app/routes/settings.tsx
58
- export function meta() {
59
- return [{ title: "Settings" }];
60
- }
61
-
62
- export default function SettingsPage() {
63
- return <div>Settings</div>;
64
- }
65
- ```
66
-
67
- That's it — React Router picks it up automatically, no registration needed.
68
-
69
- ### Dynamic params
70
-
71
- ```tsx
72
- // app/routes/inbox/$threadId.tsx
73
- import { useParams } from "react-router";
74
-
75
- export default function ThreadPage() {
76
- const { threadId } = useParams();
77
- return <div>Thread: {threadId}</div>;
78
- }
79
- ```
80
-
81
- ### Navigation
82
-
83
- Use `<Link>` for client-side navigation and `useNavigate()` for programmatic navigation:
84
-
85
- ```tsx
86
- import { Link, useNavigate } from "react-router";
87
-
88
- // In JSX
89
- <Link to="/settings">Settings</Link>;
90
-
91
- // Programmatic
92
- const navigate = useNavigate();
93
- navigate(`/inbox/${threadId}`);
94
- ```
95
-
96
- ---
97
-
98
36
  ## sendToAgentChat(opts) {#sendtoagentchat}
99
37
 
100
- Send a message to the agent chat via postMessage. Used to delegate AI tasks from UI interactions.
101
-
102
- When the app route is running inside an MCP App embed created with `embedApp()`,
103
- auto-submitted messages (`submit` omitted or `true`) are forwarded to the MCP
104
- App host bridge, which asks the containing host to add hidden context and send
105
- the visible user turn. `context` is sent as model context before the visible
106
- message, so it stays model-visible without being posted as user-facing chat or
107
- concatenated into the host's visible prompt.
108
- `submit: false` keeps the local prefill/review behavior because MCP Apps do not
109
- define a standard draft-prefill API.
110
-
111
- Internally this is the submitted-chat path sometimes surfaced as
112
- `agentNative.submitChat`; app code should call `sendToAgentChat()` rather than
113
- posting that event directly.
38
+ Send a message to the agent chat via postMessage the common way to delegate an AI task from a UI interaction. Pass `context` for hidden model context and `submit: true` to send immediately, or `submit: false` to prefill a draft the user reviews first.
114
39
 
115
40
  ```ts
116
41
  import { sendToAgentChat } from "@agent-native/core/client";
@@ -130,6 +55,15 @@ sendToAgentChat({
130
55
  });
131
56
  ```
132
57
 
58
+ Inside an MCP App embed created with `embedApp()`, auto-submitted messages
59
+ (`submit` omitted or `true`) are forwarded to the MCP App host bridge, which
60
+ asks the containing host to add hidden context and send the visible user turn.
61
+ `context` stays model-visible without being posted as user-facing chat.
62
+ `submit: false` keeps the local prefill/review behavior because MCP Apps do not
63
+ define a standard draft-prefill API. Internally this is the submitted-chat path
64
+ sometimes surfaced as `agentNative.submitChat`; app code should call
65
+ `sendToAgentChat()` rather than posting that event directly.
66
+
133
67
  ### Silent background sends {#background-send}
134
68
 
135
69
  Use `background: true` when a UI action should kick off real agent work without
@@ -77,7 +77,7 @@ Not ready to scaffold? You can add agent-native superpowers to a coding agent yo
77
77
 
78
78
  ## Building on this
79
79
 
80
- - [**Getting Started**](/docs/getting-started) — create a minimal chat app or headless action
80
+ - [**Getting Started**](/docs/getting-started) — create a minimal chat app or headless agent
81
81
  - [**Messaging the agent**](/docs/messaging) — how users (and you) talk to the agent that ships with each template
82
82
  - [**Multi-App Workspace**](/docs/multi-app-workspace) — bundle several templates into one workspace that shares auth, brand, and agent
83
83
  - [**Dispatch**](/docs/template-dispatch) — the workspace control plane template