@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.
- package/dist/agent/harness/ai-sdk-adapter.d.ts +44 -0
- package/dist/agent/harness/ai-sdk-adapter.d.ts.map +1 -1
- package/dist/agent/harness/ai-sdk-adapter.js +120 -1
- package/dist/agent/harness/ai-sdk-adapter.js.map +1 -1
- package/dist/agent/harness/index.d.ts +1 -1
- package/dist/agent/harness/index.d.ts.map +1 -1
- package/dist/agent/harness/index.js.map +1 -1
- package/dist/cli/code-agent-executor.js +1 -1
- package/dist/cli/code-agent-executor.js.map +1 -1
- package/dist/cli/create.js +1 -1
- package/dist/cli/create.js.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.js +1 -1
- package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +29 -10
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
- package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/DiffBlock.js +48 -20
- package/dist/client/blocks/library/DiffBlock.js.map +1 -1
- package/dist/client/blocks/library/diagram.d.ts.map +1 -1
- package/dist/client/blocks/library/diagram.js +14 -3
- package/dist/client/blocks/library/diagram.js.map +1 -1
- package/dist/client/blocks/library/wireframe.d.ts.map +1 -1
- package/dist/client/blocks/library/wireframe.js +14 -3
- package/dist/client/blocks/library/wireframe.js.map +1 -1
- package/dist/client/blocks/types.d.ts +5 -0
- package/dist/client/blocks/types.d.ts.map +1 -1
- package/dist/client/blocks/types.js.map +1 -1
- package/dist/client/chat/index.d.ts +2 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +2 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/chat-view-transition.d.ts +17 -0
- package/dist/client/chat-view-transition.d.ts.map +1 -1
- package/dist/client/chat-view-transition.js +46 -0
- package/dist/client/chat-view-transition.js.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts +27 -0
- package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -0
- package/dist/client/use-agent-chat-home-handoff.js +120 -0
- package/dist/client/use-agent-chat-home-handoff.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +24 -2
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/deep-link.d.ts +2 -2
- package/dist/server/deep-link.d.ts.map +1 -1
- package/dist/server/deep-link.js +2 -2
- package/dist/server/deep-link.js.map +1 -1
- package/dist/styles/agent-native.css +2 -6
- package/dist/tailwind.preset.d.ts.map +1 -1
- package/dist/tailwind.preset.js +8 -1
- package/dist/tailwind.preset.js.map +1 -1
- package/dist/templates/default/package.json +1 -0
- package/dist/templates/headless/AGENTS.md +3 -0
- package/dist/templates/headless/DEVELOPING.md +4 -0
- package/dist/templates/headless/actions/run.ts +6 -0
- package/dist/templates/workspace-root/README.md +4 -4
- package/docs/content/actions.md +32 -42
- package/docs/content/agent-surfaces.md +105 -84
- package/docs/content/agent-teams.md +2 -14
- package/docs/content/agent-web-surfaces.md +4 -4
- package/docs/content/authentication.md +40 -24
- package/docs/content/automations.md +18 -36
- package/docs/content/blueprint-installer.md +3 -0
- package/docs/content/cli-adapters.md +24 -168
- package/docs/content/client.md +11 -77
- package/docs/content/cloneable-saas.md +1 -1
- package/docs/content/code-agents-ui.md +44 -0
- package/docs/content/components.md +10 -23
- package/docs/content/context-awareness.md +3 -3
- package/docs/content/creating-templates.md +20 -18
- package/docs/content/database.md +1 -1
- package/docs/content/deployment.md +5 -37
- package/docs/content/dispatch.md +17 -28
- package/docs/content/drop-in-agent.md +24 -111
- package/docs/content/durable-resume.md +4 -0
- package/docs/content/embedding-sdk.md +141 -135
- package/docs/content/evals.md +3 -3
- package/docs/content/extensions.md +1 -1
- package/docs/content/external-agents.md +35 -61
- package/docs/content/faq.md +5 -5
- package/docs/content/frames.md +13 -4
- package/docs/content/getting-started.md +96 -142
- package/docs/content/harness-agents.md +53 -9
- package/docs/content/human-approval.md +1 -1
- package/docs/content/key-concepts.md +14 -99
- package/docs/content/local-file-mode.md +2 -2
- package/docs/content/mcp-apps.md +9 -2
- package/docs/content/mcp-clients.md +8 -3
- package/docs/content/mcp-protocol.md +11 -29
- package/docs/content/messaging.md +1 -1
- package/docs/content/migration-workbench.md +14 -175
- package/docs/content/multi-app-workspace.md +1 -1
- package/docs/content/multi-tenancy.md +18 -47
- package/docs/content/native-chat-ui.md +15 -12
- package/docs/content/observability.md +16 -4
- package/docs/content/observational-memory.md +1 -1
- package/docs/content/pure-agent-apps.md +17 -124
- package/docs/content/real-time-collaboration.md +14 -14
- package/docs/content/routing.md +71 -0
- package/docs/content/sandbox-adapters.md +78 -4
- package/docs/content/security.md +59 -39
- package/docs/content/server.md +16 -8
- package/docs/content/sharing.md +1 -6
- package/docs/content/skills-guide.md +3 -1
- package/docs/content/template-analytics.md +1 -1
- package/docs/content/template-assets.md +12 -3
- package/docs/content/template-brain.md +64 -72
- package/docs/content/template-chat.md +32 -4
- package/docs/content/template-clips.md +35 -4
- package/docs/content/template-design.md +19 -3
- package/docs/content/template-dispatch.md +9 -0
- package/docs/content/template-forms.md +15 -10
- package/docs/content/template-plan.md +13 -5
- package/docs/content/template-slides.md +14 -14
- package/docs/content/template-videos.md +10 -12
- package/docs/content/tracking.md +66 -55
- package/docs/content/using-your-agent.md +6 -16
- package/docs/content/what-is-agent-native.md +5 -11
- package/docs/content/workspace-management.md +2 -2
- package/docs/content/workspace.md +20 -160
- package/package.json +6 -2
- package/src/templates/default/package.json +1 -0
- package/src/templates/headless/AGENTS.md +3 -0
- package/src/templates/headless/DEVELOPING.md +4 -0
- package/src/templates/headless/actions/run.ts +6 -0
- 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.
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
own auth database, so
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
```
|
|
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
|
-
```
|
|
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
|
-
```
|
|
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
|
-
```
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
57
|
-
);
|
|
47
|
+
Use the web-request tool to POST to ${keys.SLACK_WEBHOOK}.
|
|
58
48
|
```
|
|
59
49
|
|
|
60
|
-
##
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
|
|
72
|
-
|
|
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: "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
package/docs/content/client.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|