@agent-native/core 0.157.4 → 0.157.5
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/docs/content/actions-access-control.mdx +226 -0
- package/docs/content/actions-advanced.mdx +152 -0
- package/docs/content/actions-defining.mdx +275 -0
- package/docs/content/actions-other-surfaces.mdx +167 -0
- package/docs/content/actions-overview.mdx +214 -0
- package/docs/content/actions-run-context.mdx +180 -0
- package/docs/content/agent-surfaces.mdx +1 -1
- package/docs/content/agent-teams.mdx +1 -1
- package/docs/content/agent-web-surfaces.mdx +1 -1
- package/docs/content/audit-log.mdx +1 -1
- package/docs/content/automation-connectors.mdx +1 -1
- package/docs/content/automations.mdx +1 -1
- package/docs/content/blueprint-installer.mdx +1 -1
- package/docs/content/cli-adapters.mdx +1 -1
- package/docs/content/client-agent-chat.mdx +1 -1
- package/docs/content/client-data.mdx +2 -2
- package/docs/content/client-overview.mdx +2 -2
- package/docs/content/components.mdx +1 -1
- package/docs/content/data-programs.mdx +2 -2
- package/docs/content/database.mdx +1 -1
- package/docs/content/deployment.mdx +1 -1
- package/docs/content/docs-components.mdx +14 -3
- package/docs/content/drop-in-agent.mdx +2 -2
- package/docs/content/durable-resume.mdx +1 -1
- package/docs/content/evals.mdx +1 -1
- package/docs/content/extensions.mdx +1 -1
- package/docs/content/external-agents-catalog.mdx +2 -2
- package/docs/content/external-agents.mdx +1 -1
- package/docs/content/generative-ui.mdx +1 -1
- package/docs/content/getting-started.mdx +1 -1
- package/docs/content/http-api.mdx +4 -4
- package/docs/content/human-approval.mdx +1 -1
- package/docs/content/key-concepts.mdx +1 -1
- package/docs/content/mcp-apps.mdx +1 -1
- package/docs/content/mcp-clients.mdx +1 -1
- package/docs/content/mcp-protocol.mdx +1 -1
- package/docs/content/native-chat-ui.mdx +1 -1
- package/docs/content/observability.mdx +1 -1
- package/docs/content/organizations-teams-permissions.mdx +1 -1
- package/docs/content/processors.mdx +1 -1
- package/docs/content/pure-agent-apps.mdx +1 -1
- package/docs/content/recurring-jobs.mdx +1 -1
- package/docs/content/server-database.mdx +1 -1
- package/docs/content/server-overview.mdx +3 -3
- package/docs/content/server-routes.mdx +2 -2
- package/docs/content/template-analytics-developers.mdx +1 -1
- package/docs/content/template-assets-developers.mdx +1 -1
- package/docs/content/template-brain-developers.mdx +1 -1
- package/docs/content/template-calendar-developers.mdx +1 -1
- package/docs/content/template-chat-first-edits.mdx +1 -1
- package/docs/content/template-chat.mdx +1 -1
- package/docs/content/template-content-developers.mdx +1 -1
- package/docs/content/template-forms-developers.mdx +1 -1
- package/docs/content/template-forms.mdx +1 -1
- package/docs/content/template-mail-developers.mdx +2 -2
- package/docs/content/toolkit-org-team.mdx +1 -1
- package/docs/content/tracking.mdx +1 -1
- package/docs/content/what-is-agent-native.mdx +1 -1
- package/package.json +1 -1
- package/docs/content/actions.mdx +0 -750
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Other Surfaces"
|
|
3
|
+
description: "Everything that calls an action beyond the UI hooks: native chat UI widget results, the CLI, A2A, MCP, and the two standard actions every template ships."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Other Surfaces
|
|
7
|
+
|
|
8
|
+
The UI-hook surface is covered on [Data & Sync](/docs/client-data): `useActionQuery()`, `useActionMutation()`, and `callAction()`. This page covers everything else that can call, or be driven by, an action.
|
|
9
|
+
|
|
10
|
+
## Rendering native chat UI {#native-chat-ui}
|
|
11
|
+
|
|
12
|
+
Actions can return structured widget data that the in-app chat renders
|
|
13
|
+
natively. This is the first-party chat path for reusable tables, charts, setup
|
|
14
|
+
summaries, and insight cards. Use [Generative UI](/docs/generative-ui) when the
|
|
15
|
+
agent needs to generate arbitrary interactive controls inside the in-app chat,
|
|
16
|
+
and use [MCP Apps](/docs/mcp-apps) for inline UI in external MCP hosts.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { defineAction } from "@agent-native/core/action";
|
|
20
|
+
import { ACTION_CHAT_UI_DATA_CHART_RENDERER } from "@agent-native/core/action-ui";
|
|
21
|
+
import {
|
|
22
|
+
createDataChartWidgetResult,
|
|
23
|
+
dataChartWidgetResultSchema,
|
|
24
|
+
} from "@agent-native/core/data-widgets";
|
|
25
|
+
|
|
26
|
+
export default defineAction({
|
|
27
|
+
description: "Summarize response trends.",
|
|
28
|
+
readOnly: true,
|
|
29
|
+
outputSchema: dataChartWidgetResultSchema,
|
|
30
|
+
chatUI: { renderer: ACTION_CHAT_UI_DATA_CHART_RENDERER },
|
|
31
|
+
run: async () =>
|
|
32
|
+
createDataChartWidgetResult({
|
|
33
|
+
title: "Response trends",
|
|
34
|
+
chartSeries: {
|
|
35
|
+
type: "line",
|
|
36
|
+
xKey: "day",
|
|
37
|
+
series: [{ key: "responses", label: "Responses" }],
|
|
38
|
+
data: [
|
|
39
|
+
{ day: "Mon", responses: 12 },
|
|
40
|
+
{ day: "Tue", responses: 18 },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The built-in discriminants are `"data-table"`, `"data-chart"`, and
|
|
48
|
+
`"data-insights"`, with server-safe builders and schemas in
|
|
49
|
+
`@agent-native/core/data-widgets`. Use `data-chart` for chart-only answers,
|
|
50
|
+
`data-table` for SQL/list results, and `data-insights` only when a combined
|
|
51
|
+
summary/chart/table card is the right output. See
|
|
52
|
+
[Native Chat UI](/docs/native-chat-ui) for the full result contract, or
|
|
53
|
+
[BYO agent runtimes](/docs/native-chat-ui#byo-agent-runtimes) if you're
|
|
54
|
+
wiring a custom agent runtime instead of the built-in chat loop. See
|
|
55
|
+
[Agent Surfaces](/docs/agent-surfaces) for how the same action can stay
|
|
56
|
+
automation-friendly, render in chat, or grow into a full screen.
|
|
57
|
+
|
|
58
|
+
## Calling it from the CLI {#cli}
|
|
59
|
+
|
|
60
|
+
Every action is runnable via `pnpm action`:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pnpm action reply-to-email '{"emailId":"thread-123","body":"Thanks!"}'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
JSON input is the preferred shape for agents and complex objects. Flags work
|
|
67
|
+
the same way for simple manual runs and existing scripts:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pnpm action reply-to-email --emailId thread-123 --body "Thanks!"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
This is useful for agent-dev loops, scripts, and cron jobs.
|
|
74
|
+
|
|
75
|
+
## Calling it from another agent (A2A) {#a2a}
|
|
76
|
+
|
|
77
|
+
If your app is an [A2A](/docs/a2a-protocol) peer, other agent-native apps discover your actions automatically and can call them by name. Same-origin deploys skip JWT signing. Cross-origin deploys use a shared `A2A_SECRET` instead.
|
|
78
|
+
|
|
79
|
+
## Exposing it over MCP {#mcp}
|
|
80
|
+
|
|
81
|
+
With MCP enabled, your actions show up in the framework's MCP server at `/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`](/docs/actions-access-control#public-agent) 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.
|
|
82
|
+
|
|
83
|
+
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:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { embedRoute } from "@agent-native/core";
|
|
87
|
+
|
|
88
|
+
export default defineAction({
|
|
89
|
+
description: "Create an email draft for review.",
|
|
90
|
+
schema: z.object({ body: z.string() }),
|
|
91
|
+
run: async ({ body }) => ({ body }),
|
|
92
|
+
...embedRoute({
|
|
93
|
+
title: "Review draft",
|
|
94
|
+
openLabel: "Open in Mail",
|
|
95
|
+
path: ({ result }) => ({
|
|
96
|
+
label: "Open draft in Mail",
|
|
97
|
+
url: "/_agent-native/open?app=mail&view=inbox",
|
|
98
|
+
}),
|
|
99
|
+
}),
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Keep `link` as the fallback for CLI and non-UI MCP hosts. It is also the embed's launch target. The embed bridge (the signed embed-start session, transplant vs. controlled-frame rendering, the `ui/*` host bridge, CSP, and height clamping) is owned by [External Agents](/docs/external-agents#mcp-app-bridge).
|
|
104
|
+
|
|
105
|
+
## Standard actions {#standard-actions}
|
|
106
|
+
|
|
107
|
+
Every template should include these two for [context awareness](/docs/context-awareness).
|
|
108
|
+
|
|
109
|
+
### view-screen {#view-screen}
|
|
110
|
+
|
|
111
|
+
Reads the current navigation state, fetches contextual data, and returns a snapshot of what the user sees. The agent calls this when it needs a fresh look at the screen.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
// actions/view-screen.ts
|
|
115
|
+
import { defineAction } from "@agent-native/core/action";
|
|
116
|
+
import { readAppState } from "@agent-native/core/application-state";
|
|
117
|
+
import { z } from "zod";
|
|
118
|
+
|
|
119
|
+
export default defineAction({
|
|
120
|
+
description: "Read the current screen state for context.",
|
|
121
|
+
schema: z.object({}),
|
|
122
|
+
http: { method: "GET" },
|
|
123
|
+
run: async () => {
|
|
124
|
+
const navigation = await readAppState("navigation");
|
|
125
|
+
const screen: Record<string, unknown> = { navigation };
|
|
126
|
+
|
|
127
|
+
if (navigation?.view === "inbox") {
|
|
128
|
+
screen.emailList = await listEmailsForLabel(navigation.label);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return screen;
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### navigate {#navigate}
|
|
137
|
+
|
|
138
|
+
Writes a one-shot navigation command to application state. The UI reads it, navigates, and deletes the entry.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
// actions/navigate.ts
|
|
142
|
+
import { defineAction } from "@agent-native/core/action";
|
|
143
|
+
import { writeAppState } from "@agent-native/core/application-state";
|
|
144
|
+
import { z } from "zod";
|
|
145
|
+
|
|
146
|
+
export default defineAction({
|
|
147
|
+
description: "Navigate the user to a view.",
|
|
148
|
+
schema: z.object({
|
|
149
|
+
view: z.string(),
|
|
150
|
+
threadId: z.string().optional(),
|
|
151
|
+
}),
|
|
152
|
+
run: async (args) => {
|
|
153
|
+
await writeAppState("navigate", args);
|
|
154
|
+
return { ok: true };
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## What's next
|
|
160
|
+
|
|
161
|
+
- [**Data & Sync**](/docs/client-data) — `useActionQuery`, `useActionMutation`, and `callAction` from the UI
|
|
162
|
+
- [**Overview**](/docs/actions-overview) — what an action is and the hello-world walkthrough
|
|
163
|
+
- [**Context Awareness**](/docs/context-awareness) — the `view-screen` + `navigate` pattern in depth
|
|
164
|
+
- [**A2A Protocol**](/docs/a2a-protocol) — how other agents discover and call your actions
|
|
165
|
+
- [**MCP Protocol**](/docs/mcp-protocol) — catalog tiers, auth, and `mcpApp` resources
|
|
166
|
+
- [**Generative UI**](/docs/generative-ui) — rendering transient or persisted generated UI inline in chat
|
|
167
|
+
- [**Native Chat UI**](/docs/native-chat-ui) — the full widget result contract
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Overview"
|
|
3
|
+
description: "Actions are the single source of truth for anything your app does. Define one with defineAction() and it fans out to an agent tool, UI hooks, HTTP, an MCP tool, an A2A tool, and a CLI command."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Overview
|
|
7
|
+
|
|
8
|
+
Actions are the single source of truth for anything your app does. Define one with `defineAction()` and drop it in `actions/`. Every surface below picks it up automatically. You write the logic once. No surface gets its own copy, and none can drift out of sync with the others.
|
|
9
|
+
|
|
10
|
+
<Cards columns={2}>
|
|
11
|
+
|
|
12
|
+
### :robot: [Agent Tool](/docs/agent-surfaces)
|
|
13
|
+
|
|
14
|
+
The agent reads the description and schema as a callable tool. Ask for it in chat, like "reply to this ticket" or "summarize this thread."
|
|
15
|
+
|
|
16
|
+
### :components: [UI Hooks](/docs/client-data)
|
|
17
|
+
|
|
18
|
+
`useActionQuery()`, `useActionMutation()`, and `callAction()` call the same action from React. A click and a chat message run the exact same operation.
|
|
19
|
+
|
|
20
|
+
### :server: [Framework Transport](/docs/http-api)
|
|
21
|
+
|
|
22
|
+
Auto-mounted at `/_agent-native/actions/<name>` on startup. The UI hooks call this route under the hood, and an external HTTP client can call it directly too.
|
|
23
|
+
|
|
24
|
+
### :plug: [MCP Tool](/docs/actions-other-surfaces#mcp)
|
|
25
|
+
|
|
26
|
+
Shows up as a tool to any MCP host: Claude, Claude Desktop, Cursor, Codex. Use it when a different AI client should drive your app.
|
|
27
|
+
|
|
28
|
+
### :network: [A2A Tool](/docs/actions-other-surfaces#a2a)
|
|
29
|
+
|
|
30
|
+
Other agent-native apps discover and call it over A2A. Use it when a second app in your workspace needs this one to act on its behalf.
|
|
31
|
+
|
|
32
|
+
### :terminal: [CLI Command](/docs/actions-other-surfaces#cli)
|
|
33
|
+
|
|
34
|
+
`pnpm action <name>` runs it from a terminal. Use it for scripts, cron jobs, and quick manual tests.
|
|
35
|
+
|
|
36
|
+
</Cards>
|
|
37
|
+
|
|
38
|
+
Most apps build a separate API layer between the frontend and the database, one that only the browser can call. An agent that wants the same capability needs its own, separate integration, written and maintained again. Agent-native apps skip that split. The UI and the agent call the exact same action, so there is only one implementation to write, and only one place for a bug to hide.
|
|
39
|
+
|
|
40
|
+
<Diagram id="doc-block-actions-overview-2" title="Traditional frontend vs. Agent Native" summary="A traditional frontend calls its own API layer, which only it can reach. In Agent Native, app/ and the agent both call the same actions, so every capability built here is agent-callable for free.">
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<div class="diagram-cocmp">
|
|
44
|
+
<div class="cocmp-col">
|
|
45
|
+
<div class="cocmp-label diagram-muted">Traditional</div>
|
|
46
|
+
<div class="diagram-node">app/ (React frontend)</div>
|
|
47
|
+
<div class="cocmp-arrow diagram-muted" aria-hidden="true">↓</div>
|
|
48
|
+
<div class="diagram-panel" data-rough>
|
|
49
|
+
<strong>API layer</strong>
|
|
50
|
+
<small class="diagram-muted">only the frontend calls it</small>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="cocmp-arrow diagram-muted" aria-hidden="true">↓</div>
|
|
53
|
+
<div class="diagram-box" data-rough>Database</div>
|
|
54
|
+
</div>
|
|
55
|
+
<div class="cocmp-vs diagram-muted">vs</div>
|
|
56
|
+
<div class="cocmp-col">
|
|
57
|
+
<div class="cocmp-label diagram-muted">Agent Native</div>
|
|
58
|
+
<div class="cocmp-callers">
|
|
59
|
+
<div class="diagram-node">app/ (React frontend)</div>
|
|
60
|
+
<div class="diagram-node">Agent</div>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="cocmp-arrow diagram-muted" aria-hidden="true">↓</div>
|
|
63
|
+
<div class="diagram-panel" data-rough>
|
|
64
|
+
<span class="diagram-pill accent">Actions</span
|
|
65
|
+
><small class="diagram-muted">equal partners, same operations</small>
|
|
66
|
+
</div>
|
|
67
|
+
<div class="cocmp-arrow diagram-muted" aria-hidden="true">↓</div>
|
|
68
|
+
<div class="diagram-box" data-rough>SQL database</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```css
|
|
74
|
+
.diagram-cocmp {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: flex-start;
|
|
77
|
+
gap: 28px;
|
|
78
|
+
flex-wrap: wrap;
|
|
79
|
+
}
|
|
80
|
+
.cocmp-col {
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-direction: column;
|
|
83
|
+
align-items: center;
|
|
84
|
+
gap: 8px;
|
|
85
|
+
flex: 1;
|
|
86
|
+
min-width: 180px;
|
|
87
|
+
}
|
|
88
|
+
.cocmp-label {
|
|
89
|
+
font-size: 11px;
|
|
90
|
+
text-transform: uppercase;
|
|
91
|
+
letter-spacing: 0.06em;
|
|
92
|
+
font-weight: 600;
|
|
93
|
+
}
|
|
94
|
+
.cocmp-callers {
|
|
95
|
+
display: flex;
|
|
96
|
+
gap: 8px;
|
|
97
|
+
flex-wrap: wrap;
|
|
98
|
+
justify-content: center;
|
|
99
|
+
}
|
|
100
|
+
.cocmp-vs {
|
|
101
|
+
font-size: 13px;
|
|
102
|
+
font-weight: 600;
|
|
103
|
+
padding-top: 52px;
|
|
104
|
+
}
|
|
105
|
+
.diagram-cocmp .diagram-panel {
|
|
106
|
+
display: flex;
|
|
107
|
+
flex-direction: column;
|
|
108
|
+
gap: 6px;
|
|
109
|
+
padding: 12px 16px;
|
|
110
|
+
align-self: stretch;
|
|
111
|
+
align-items: center;
|
|
112
|
+
}
|
|
113
|
+
.cocmp-arrow {
|
|
114
|
+
font-size: 18px;
|
|
115
|
+
line-height: 1;
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
</Diagram>
|
|
120
|
+
|
|
121
|
+
If the UI and the agent both need to do something, reach for an action instead of a custom route. For the rare case where a route-shaped protocol is the right call, visit [Prefer Actions For App Operations](/docs/server-overview#actions-first).
|
|
122
|
+
|
|
123
|
+
## Where Actions Fit {#role}
|
|
124
|
+
|
|
125
|
+
`app/` (visit [Client Overview](/docs/client-overview#role)) and the agent both resolve to the same actions and the same SQL database. There is no separate "app backend" this layer talks to that the agent can't also reach. Building a feature means defining an operation here and getting a UI, an agent tool, and every other surface for it at the same time.
|
|
126
|
+
|
|
127
|
+
`actions/` itself is a flat folder. Each file is one action: a `defineAction()` call exported as the file's default export. The framework scans this folder on startup and mounts every file it finds. There is no registration step and no index file to keep up to date.
|
|
128
|
+
|
|
129
|
+
An action's `run()` body is regular server code, so it commonly reaches into a few other directories:
|
|
130
|
+
|
|
131
|
+
<FileTree
|
|
132
|
+
id="doc-block-actions-overview-filetree"
|
|
133
|
+
entries={[
|
|
134
|
+
{
|
|
135
|
+
path: "actions/reply-to-email.ts",
|
|
136
|
+
note: "One action per file. Exports a defineAction() call, and the framework mounts it automatically.",
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
path: "server/db/schema.ts",
|
|
140
|
+
note: "Drizzle table definitions most actions read and write through.",
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
path: "server/lib/access.ts",
|
|
144
|
+
note: "Shared access guards (accessFilter, assertAccess, authorize) reused across actions.",
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
path: "shared/feature-flags.ts",
|
|
148
|
+
note: "Flag definitions an action and a server plugin both check.",
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
path: "app/components/ReplyButton.tsx",
|
|
152
|
+
note: "Calls the action from the UI with useActionMutation.",
|
|
153
|
+
},
|
|
154
|
+
]}
|
|
155
|
+
/>
|
|
156
|
+
|
|
157
|
+
Nothing outside `actions/` should import an action's `run` function directly. Call it through one of the surfaces above instead: the agent tool, a UI hook, HTTP, MCP, A2A, or the CLI. Every one of those paths runs the same schema validation, access checks, and audit trail.
|
|
158
|
+
|
|
159
|
+
## Start with one action {#hello-action}
|
|
160
|
+
|
|
161
|
+
The first operation in a chat-first app can be tiny. In the Chat template,
|
|
162
|
+
replace `actions/hello.ts` or add a new file beside it:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
// actions/hello.ts
|
|
166
|
+
import { defineAction } from "@agent-native/core/action";
|
|
167
|
+
import { z } from "zod";
|
|
168
|
+
|
|
169
|
+
export default defineAction({
|
|
170
|
+
description: "Say hello from the local agent.",
|
|
171
|
+
schema: z.object({
|
|
172
|
+
name: z.string().default("world"),
|
|
173
|
+
}),
|
|
174
|
+
http: { method: "GET" },
|
|
175
|
+
readOnly: true,
|
|
176
|
+
run: async ({ name }) => {
|
|
177
|
+
return { message: `Hello, ${name}!` };
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Run it from the same folder:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
pnpm action hello '{"name":"Steve"}'
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The CLI accepts a JSON object as the action input, which matches the structured
|
|
189
|
+
tool calls agents already make. Simple flags still work for quick manual runs:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
pnpm action hello --name Steve
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Then run the app-agent loop against the folder:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
npx agent-native agent "Call hello for Steve and explain the result"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
That is the same app-agent loop your scheduled jobs, chat UI, external MCP
|
|
202
|
+
tools, and future screens will use. Chat and domain templates are for adding UI
|
|
203
|
+
around actions, not a required prerequisite for the action itself.
|
|
204
|
+
|
|
205
|
+
## What's next
|
|
206
|
+
|
|
207
|
+
- [**Defining Actions**](/docs/actions-defining) — `defineAction()` anatomy, schema, and HTTP config
|
|
208
|
+
- [**Access & Authorization**](/docs/actions-access-control) — exposure flags, `ctx` scoping, and `authorize`
|
|
209
|
+
- [**Run Context**](/docs/actions-run-context) — the full `ctx` field table
|
|
210
|
+
- [**Other Surfaces**](/docs/actions-other-surfaces) — native chat UI, CLI, A2A, MCP, and standard actions
|
|
211
|
+
- [**Advanced & Legacy**](/docs/actions-advanced) — feature-flag gating and legacy CLI-style actions
|
|
212
|
+
- [**Overview**](/docs/client-overview) — the `app/` side of this same contract
|
|
213
|
+
- [**Data & Sync**](/docs/client-data) — `useActionQuery`, `useActionMutation`, and `callAction` in the UI
|
|
214
|
+
- [**Agent Surfaces**](/docs/agent-surfaces) — how one action grows from chat tool to full screen
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Run Context"
|
|
3
|
+
description: "The ctx second argument passed to run(): the full ActionRunContext signature and the caller union, for tracking, automation attribution, and cross-cutting concerns."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Run Context
|
|
7
|
+
|
|
8
|
+
`run` receives an optional second argument, `ctx`, carrying the resolved request identity and the surface that invoked the action. Read it instead of calling `getRequestUserEmail()` / `getRequestOrgId()` by hand, and pass the whole `ctx` to tracking:
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
export default defineAction({
|
|
12
|
+
description: "Log an audit entry for the current request.",
|
|
13
|
+
schema: z.object({ event: z.string() }),
|
|
14
|
+
run: async (args, ctx) => {
|
|
15
|
+
// ctx is undefined-safe: a 1-arg `run(args)` is still valid.
|
|
16
|
+
const actor = ctx?.userEmail ?? "system";
|
|
17
|
+
if (ctx?.caller === "frontend") {
|
|
18
|
+
// tighter rules for browser-initiated calls, looser for "tool"/"cli"
|
|
19
|
+
}
|
|
20
|
+
await db.insert(audit).values({
|
|
21
|
+
actor,
|
|
22
|
+
orgId: ctx?.orgId ?? null,
|
|
23
|
+
source: ctx?.caller ?? "unknown",
|
|
24
|
+
event: args.event,
|
|
25
|
+
});
|
|
26
|
+
return { ok: true };
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Most actions only ever read `ctx?.userEmail` and `ctx?.orgId`, the [access-control scoping](/docs/actions-access-control#access-control) used in nearly every real action. The rest of this page covers cross-cutting concerns like tracking and automation attribution, which fewer actions need.
|
|
32
|
+
|
|
33
|
+
## The `ctx` argument {#ctx}
|
|
34
|
+
|
|
35
|
+
`ctx` carries two kinds of information: the resolved request identity (`userEmail`, `orgId`) that's present on nearly every call, and metadata about the surface that invoked the action (`caller`, plus surface-specific fields like `threadId` or `automation` that are only populated for certain callers). The full shape, as the second argument to `run()`:
|
|
36
|
+
|
|
37
|
+
<Signature
|
|
38
|
+
id="doc-block-actions-run-context-1"
|
|
39
|
+
name="run"
|
|
40
|
+
params={[
|
|
41
|
+
{
|
|
42
|
+
name: "args",
|
|
43
|
+
type: "TInput",
|
|
44
|
+
description: "The validated input, parsed from `schema`.",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "ctx",
|
|
48
|
+
type: "ActionRunContext",
|
|
49
|
+
optional: true,
|
|
50
|
+
description:
|
|
51
|
+
"The resolved request identity and the surface that invoked the action. Undefined-safe: a 1-argument `run(args)` handler is still valid.",
|
|
52
|
+
fields: [
|
|
53
|
+
{
|
|
54
|
+
name: "userEmail",
|
|
55
|
+
type: "string",
|
|
56
|
+
optional: true,
|
|
57
|
+
description:
|
|
58
|
+
"Resolved request user. **Never defaulted to a dev identity**: `undefined` when the request has no authenticated user. Apply your own fallback if you need one.",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "orgId",
|
|
62
|
+
type: "string | null",
|
|
63
|
+
description:
|
|
64
|
+
"Resolved org id, or `null` when the request has no org.",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "caller",
|
|
68
|
+
type: "ActionCaller",
|
|
69
|
+
description:
|
|
70
|
+
"How the action was invoked. See [the caller union](#caller) below.",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "send",
|
|
74
|
+
type: "(event) => void",
|
|
75
|
+
optional: true,
|
|
76
|
+
description:
|
|
77
|
+
'Emits an SSE event to the client. Only present inside the agent tool loop (`caller: "tool"`). `undefined` elsewhere.',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "attachments",
|
|
81
|
+
type: "AgentChatAttachment[]",
|
|
82
|
+
optional: true,
|
|
83
|
+
description:
|
|
84
|
+
'Files, images, and pasted text blocks submitted with the current agent turn. Populated only when `caller: "tool"`. `undefined` on all other surfaces.',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: "signal",
|
|
88
|
+
type: "AbortSignal",
|
|
89
|
+
optional: true,
|
|
90
|
+
description:
|
|
91
|
+
'Aborts when the run is soft-timed-out, cancelled, or the server shuts down. Populated only when `caller: "tool"`. Long-running actions can observe it to cancel early instead of waiting for the per-tool 60s hard timeout.',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "actionName",
|
|
95
|
+
type: "string",
|
|
96
|
+
optional: true,
|
|
97
|
+
description:
|
|
98
|
+
"The registry key of the action being called (e.g. `delete-recording`), set at each dispatch site so cross-cutting concerns like the audit log can attribute the call. `undefined` for direct programmatic `run()` calls that bypass the dispatcher.",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "threadId",
|
|
102
|
+
type: "string",
|
|
103
|
+
optional: true,
|
|
104
|
+
description:
|
|
105
|
+
'The agent conversation thread that triggered this call. Populated only when `caller: "tool"`, so the audit log can link a mutation back to the exact agent run.',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "runId",
|
|
109
|
+
type: "string",
|
|
110
|
+
optional: true,
|
|
111
|
+
description:
|
|
112
|
+
'The agent run attempt that triggered this call. Populated only when `caller: "tool"`.',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: "turnId",
|
|
116
|
+
type: "string",
|
|
117
|
+
optional: true,
|
|
118
|
+
description:
|
|
119
|
+
'The specific turn within that run that triggered this call. Populated only when `caller: "tool"`.',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: "automation",
|
|
123
|
+
type: "ActionAutomationContext",
|
|
124
|
+
optional: true,
|
|
125
|
+
description:
|
|
126
|
+
'Present only when `caller: "automation"`. Carries `triggerId`, `triggerName`, and an optional `policyId` for the event-triggered automation that dispatched this call.',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "networkProtocol",
|
|
130
|
+
type: '"a2a" | "mcp" | "provider-api"',
|
|
131
|
+
optional: true,
|
|
132
|
+
description:
|
|
133
|
+
"Which delegated-call protocol this request arrived over. Set only for those direct-dispatch paths.",
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "networkId",
|
|
137
|
+
type: "string",
|
|
138
|
+
optional: true,
|
|
139
|
+
description:
|
|
140
|
+
"A correlation id for that delegated call, e.g. the A2A delegation JWT id.",
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: "networkPeer",
|
|
144
|
+
type: "string",
|
|
145
|
+
optional: true,
|
|
146
|
+
description: "The calling peer's identity for that delegated call.",
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
]}
|
|
151
|
+
returns={{
|
|
152
|
+
type: "Promise<TReturn> | TReturn",
|
|
153
|
+
description:
|
|
154
|
+
"The action's result, validated against `outputSchema` if one is set.",
|
|
155
|
+
}}
|
|
156
|
+
/>
|
|
157
|
+
|
|
158
|
+
`run()` stays backward compatible: existing 1-argument handlers and handlers that only destructure `{ send }` continue to work unchanged.
|
|
159
|
+
|
|
160
|
+
### The `caller` union {#caller}
|
|
161
|
+
|
|
162
|
+
`caller` is the union `"tool" | "http" | "frontend" | "cli" | "mcp" | "a2a" | "automation"`:
|
|
163
|
+
|
|
164
|
+
| `caller` | Set when… |
|
|
165
|
+
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
166
|
+
| `"tool"` | The in-app agent loop, a sub-agent or agent team, or natural-language cross-app A2A delegation, which drives the same agent loop, so its tool calls are `"tool"`. |
|
|
167
|
+
| `"frontend"` | A browser call via `useActionMutation()`, `useActionQuery()`, or `callAction()` (tagged with the `X-Agent-Native-Frontend: 1` header). |
|
|
168
|
+
| `"http"` | A bare programmatic `POST` or `GET` to `/_agent-native/actions/<name>` without the frontend marker. |
|
|
169
|
+
| `"cli"` | `pnpm action <name>` (the CLI runner). |
|
|
170
|
+
| `"mcp"` | An external agent over the MCP `tools/call` endpoint. |
|
|
171
|
+
| `"a2a"` | A direct HTTP call to `/_agent-native/actions/<name>` carrying a signed A2A delegation token. This is an explicitly exposed, cross-app dispatch, distinct from the natural-language `"tool"` path above. |
|
|
172
|
+
| `"automation"` | An event-triggered automation dispatched from a stored workspace trigger. `ctx.automation` carries the trigger lineage. See [Automations](/docs/automations). |
|
|
173
|
+
|
|
174
|
+
## What's next
|
|
175
|
+
|
|
176
|
+
- [**Access & Authorization**](/docs/actions-access-control) — the `ctx.userEmail`/`ctx.orgId` scoping pattern this context enables
|
|
177
|
+
- [**Defining Actions**](/docs/actions-defining) — `defineAction()` anatomy and HTTP config
|
|
178
|
+
- [**Other Surfaces**](/docs/actions-other-surfaces) — how CLI, A2A, MCP, and automation callers set `caller`
|
|
179
|
+
- [**Audit Log**](/docs/audit-log) — how `threadId`/`runId`/`turnId` link a mutation back to its agent run
|
|
180
|
+
- [**Automations**](/docs/automations) — the `ctx.automation` trigger lineage
|
|
@@ -591,7 +591,7 @@ tool-result metadata. Start there when wiring an external agent into the chat.
|
|
|
591
591
|
|
|
592
592
|
<Cards>
|
|
593
593
|
|
|
594
|
-
### [Actions](/docs/actions)
|
|
594
|
+
### [Actions](/docs/actions-overview)
|
|
595
595
|
|
|
596
596
|
Define the operation once. Every surface above calls the same one.
|
|
597
597
|
|
|
@@ -328,4 +328,4 @@ When a sub-agent is at or below the cap, the framework injects a line into its r
|
|
|
328
328
|
|
|
329
329
|
- [**Agent Resources — Custom Agents**](/docs/agent-resources#custom-agents) — the profile format
|
|
330
330
|
- [**A2A Protocol**](/docs/a2a-protocol) — when the "sub-agent" lives in a different app entirely
|
|
331
|
-
- [**Actions**](/docs/actions) — the tools a sub-agent calls
|
|
331
|
+
- [**Actions**](/docs/actions-overview) — the tools a sub-agent calls
|
|
@@ -212,6 +212,6 @@ The audit exits non-zero if a required public surface is missing.
|
|
|
212
212
|
|
|
213
213
|
## What's next
|
|
214
214
|
|
|
215
|
-
- [**Actions**](/docs/actions) — how to opt actions into the public agent protocol
|
|
215
|
+
- [**Actions**](/docs/actions-overview) — how to opt actions into the public agent protocol
|
|
216
216
|
- [**MCP Protocol**](/docs/mcp-protocol) — the MCP surface that `publicMcp: true` enables
|
|
217
217
|
- [**Deployment**](/docs/deployment) — where these static files are written during builds
|
|
@@ -182,7 +182,7 @@ Configure via environment:
|
|
|
182
182
|
|
|
183
183
|
## What's next
|
|
184
184
|
|
|
185
|
-
- [**Actions**](/docs/actions) — the `defineAction` seam where capture happens
|
|
185
|
+
- [**Actions**](/docs/actions-overview) — the `defineAction` seam where capture happens
|
|
186
186
|
- [**Human-in-the-Loop Approvals**](/docs/human-approval) — gated actions, recorded as `denied`
|
|
187
187
|
- [**Security & Data Scoping**](/docs/security) — the ownership model audit reads reuse
|
|
188
188
|
- [**Observability**](/docs/observability) — agent-run telemetry (the other "what happened")
|
|
@@ -119,5 +119,5 @@ See [Zapier REST Hook triggers](https://docs.zapier.com/integrations/build/hook-
|
|
|
119
119
|
|
|
120
120
|
- [**Automations**](/docs/automations#web-request) — the ad-hoc `web-request` tool for one-off outbound HTTP, the lighter-weight sibling to a configured connector
|
|
121
121
|
- [**Messaging**](/docs/messaging) — reaching the agent from Slack, email, Telegram, and WhatsApp, the queue-and-processor pattern this page's callbacks reuse
|
|
122
|
-
- [**Actions**](/docs/actions) — `defineAction`, `needsApproval`, and the action surface `createInvokeAutomationWorkflowAction` builds on
|
|
122
|
+
- [**Actions**](/docs/actions-overview) — `defineAction`, `needsApproval`, and the action surface `createInvokeAutomationWorkflowAction` builds on
|
|
123
123
|
- [**Security**](/docs/security) — `${keys.NAME}` secret substitution and the credential-scoping model this runtime relies on
|
|
@@ -450,7 +450,7 @@ for the full `plan.commented` payload reference and all four plan events.
|
|
|
450
450
|
## What's next
|
|
451
451
|
|
|
452
452
|
- [**Recurring Jobs**](/docs/recurring-jobs) — legacy compatibility and advanced scheduled-job fields
|
|
453
|
-
- [**Actions**](/docs/actions) — automations can call any registered action via the agent loop
|
|
453
|
+
- [**Actions**](/docs/actions-overview) — automations can call any registered action via the agent loop
|
|
454
454
|
- [**Workflow Connectors**](/docs/automation-connectors) — pre-configured n8n/Zapier-style workflow invocation, for when ad-hoc `web-request` isn't a strict enough contract
|
|
455
455
|
- [**Security**](/docs/security) — input validation and secret handling
|
|
456
456
|
- [**Events and Automations**](/docs/template-plan-automations) — the Plan event reference and automation recipes
|
|
@@ -124,5 +124,5 @@ A new curated blueprint under an existing kind needs no code — but if you crea
|
|
|
124
124
|
## What's next
|
|
125
125
|
|
|
126
126
|
- [**Sandbox Adapters**](/docs/sandbox-adapters) — the seam the `add sandbox docker` blueprint targets
|
|
127
|
-
- [**Actions**](/docs/actions) — the single source of truth every blueprint builds on
|
|
127
|
+
- [**Actions**](/docs/actions-overview) — the single source of truth every blueprint builds on
|
|
128
128
|
- [**External Agents**](/docs/external-agents) — connecting the coding agent you pipe blueprints into
|
|
@@ -169,4 +169,4 @@ CLI adapters use `node:child_process`, which does not exist on edge/worker runti
|
|
|
169
169
|
## What's next
|
|
170
170
|
|
|
171
171
|
- [**Adapters**](/docs/sandbox-adapters) — the canonical guide to both adapter seams.
|
|
172
|
-
- [**Actions**](/docs/actions) — the action surface CLI adapters are usually wrapped in.
|
|
172
|
+
- [**Actions**](/docs/actions-overview) — the action surface CLI adapters are usually wrapped in.
|
|
@@ -37,7 +37,7 @@ The user experience is as follows:
|
|
|
37
37
|
|
|
38
38
|
1. The user picks **Offer a refund**, which causes `sendDraftRequest()` to send the prompt and ticket context to the agent.
|
|
39
39
|
|
|
40
|
-
1. The agent calls the `draft-ticket-reply` [action](/docs/actions), and the drafted reply appears in the reply composer.
|
|
40
|
+
1. The agent calls the `draft-ticket-reply` [action](/docs/actions-overview), and the drafted reply appears in the reply composer.
|
|
41
41
|
|
|
42
42
|
## askUserQuestion(opts) {#ask-user-question}
|
|
43
43
|
|
|
@@ -5,7 +5,7 @@ description: "The action hooks app/ uses to read, write, and stay live-synced wi
|
|
|
5
5
|
|
|
6
6
|
# Data & Sync
|
|
7
7
|
|
|
8
|
-
The primary way to read and write app data from client application is through [action](/docs/actions) hooks. By embedding requests into actions, both your client and agent can interact with your application in the same way.
|
|
8
|
+
The primary way to read and write app data from client application is through [action](/docs/actions-overview) hooks. By embedding requests into actions, both your client and agent can interact with your application in the same way.
|
|
9
9
|
|
|
10
10
|
In the example below, a ticket is assigned to the current user manually through a dropdown, and then through the chat agent. Both the UI and agent access the same action.
|
|
11
11
|
|
|
@@ -426,7 +426,7 @@ For normal CRUD, prefer `useActionQuery()` and `useActionMutation()`. Mutating a
|
|
|
426
426
|
|
|
427
427
|
- [**Overview**](/docs/client-overview) — the `app/` and `public/` directory layout this page's hooks live in
|
|
428
428
|
- [**Agent Chat**](/docs/client-agent-chat) — sending prompts and staging context for the agent
|
|
429
|
-
- [**Actions**](/docs/actions) — the `defineAction` surface behind every hook on this page
|
|
429
|
+
- [**Actions**](/docs/actions-overview) — the `defineAction` surface behind every hook on this page
|
|
430
430
|
- [**Advanced**](/docs/client-advanced) — direct `application_state` access
|
|
431
431
|
- [**Sync Internals**](/docs/client-sync-internals) — syncing a raw `useQuery` with `useChangeVersion`
|
|
432
432
|
- [**Real-Time Sync**](/docs/key-concepts#polling-sync) — how `useDbSync` keeps the UI current
|