@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
|
@@ -1,139 +1,32 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Pure-Agent Apps"
|
|
3
|
-
description: "Apps where the
|
|
3
|
+
description: "Apps where the agent is the whole product: the app-agent loop is the front door, and UI is added only when humans need it."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Pure-Agent Apps
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
A pure-agent app is the minimal end of agent-native: the app-agent loop is the
|
|
9
|
+
product, not a dashboard. You send a request from the terminal, Slack, email, a
|
|
10
|
+
scheduled job, another agent, or Chat — "summarize my unread emails," "post the
|
|
11
|
+
daily metrics to Slack" — and the agent acts and returns the result wherever it
|
|
12
|
+
belongs. It is still a real app: actions, sessions, app state, history,
|
|
13
|
+
settings, credentials, and share records all live in SQL.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
candidates who replied last week" - and the agent goes off and does it. The
|
|
20
|
-
output shows up in the channel that asked, wherever it belongs.
|
|
15
|
+
Reach for this shape when the work runs in the background, the output leaves the
|
|
16
|
+
app, the domain is one-shot, or you're prototyping. The agent still needs a UI —
|
|
17
|
+
not a dashboard, but a place for humans to supervise, configure, and steer it —
|
|
18
|
+
which is why even pure-agent apps usually mount the built-in Chat shell.
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
This is the **headless** product shape. The full decision guide, what ships in
|
|
21
|
+
the box, the scaffold, repo access, and run sharing now live in one place:
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
## What it feels like to use one {#user-experience}
|
|
27
|
-
|
|
28
|
-
Most apps are built around a UI: a database table you browse, a form you fill, a chart you read. The agent is a sidekick.
|
|
29
|
-
|
|
30
|
-
In a pure-agent app, that's flipped. The agent loop is the front door. You type
|
|
31
|
-
or send a request; the agent takes action; you see the result. Chat can be the
|
|
32
|
-
browser front door, but headless apps can also start from CLI, jobs, webhooks,
|
|
33
|
-
or A2A. Everything else - settings, history, what's currently running - is
|
|
34
|
-
available when you need it, but most of the time you do not need a custom
|
|
35
|
-
dashboard.
|
|
36
|
-
|
|
37
|
-
Examples of where this works really well:
|
|
38
|
-
|
|
39
|
-
- **Background workers** — a triage agent that watches your inbox and labels things, a daily-report agent that posts to Slack each morning, an on-call agent that responds to alerts.
|
|
40
|
-
- **One-shot helpers** — "research this company and write a one-pager," "scan my GitHub issues and tell me which ones look stale."
|
|
41
|
-
- **Channel-driven assistants** — agents you mostly talk to from Slack, Telegram, email, or another agent (via [A2A](/docs/a2a-protocol)). The "app" itself is mostly a control panel.
|
|
42
|
-
- **Internal tools** — an agent that knows your runbooks, your APIs, your conventions, and can act on them.
|
|
43
|
-
|
|
44
|
-
The hot take is "agents will replace apps." The honest version is "agents still need a UI — for humans to supervise, configure, and steer them." Pure-agent apps give you that UI without the dashboard sprawl.
|
|
45
|
-
|
|
46
|
-
## When this beats a traditional app {#when}
|
|
47
|
-
|
|
48
|
-
Pick the pure-agent pattern when:
|
|
49
|
-
|
|
50
|
-
- **The work happens in the background.** Most of the value is created while the user isn't looking.
|
|
51
|
-
- **The output leaves the app.** The agent posts to Slack, sends email, updates a third-party system. There's nothing to browse in-app — the value is elsewhere.
|
|
52
|
-
- **The domain is one-shot.** Research bot, summary generator, report writer. There's no persistent object that needs a list view.
|
|
53
|
-
- **You're prototyping.** Ship the agent now; add a richer UI later if it turns out users actually want one.
|
|
54
|
-
|
|
55
|
-
If your product is built around persistent objects users browse, pivot, and share - emails, events, documents, charts - pick a [template](/docs/cloneable-saas) instead. Those have full UIs _plus_ the agent.
|
|
56
|
-
|
|
57
|
-
## What ships in the box when you add Chat {#minimum-ui}
|
|
58
|
-
|
|
59
|
-
When you add the built-in Chat shell, a pure-agent app gets five built-in
|
|
60
|
-
surfaces, all provided by the framework - you don't build them:
|
|
61
|
-
|
|
62
|
-
1. **Chat** — the main input. Users talk to the agent, steer it, queue tasks.
|
|
63
|
-
2. **Workspace** — skills, memory, instructions, custom sub-agents, connected MCP servers, scheduled jobs. Customize the agent's behavior without shipping code.
|
|
64
|
-
3. **Job history** — which scheduled jobs ran, when, whether they succeeded, what they did.
|
|
65
|
-
4. **Thread history** — every past conversation, each preserved with its tool calls and final output.
|
|
66
|
-
5. **Settings** — API keys, connected accounts, onboarding status.
|
|
67
|
-
|
|
68
|
-
Those five are usually enough. No analytics dashboard. No Kanban. No forms. Just: talk to it, see what it's done, configure how it behaves.
|
|
69
|
-
|
|
70
|
-
## Why you'd pick this over "an app with an AI sidebar" {#vs-traditional}
|
|
71
|
-
|
|
72
|
-
Two reasons:
|
|
73
|
-
|
|
74
|
-
1. **You don't have to build the UI.** A pure-agent app skips weeks of dashboard work. The chat handles input; the framework handles supervision and history; the agent handles output.
|
|
75
|
-
2. **It's channel-agnostic from day one.** The same agent that runs in your web UI also runs from Slack, Telegram, email, and other agents — because everything goes through the agent, not the UI. See [Messaging the agent](/docs/messaging) for how that works.
|
|
76
|
-
|
|
77
|
-
The trade-off: pure-agent apps don't give users a "browse-everything-at-a-glance" view. If your users need that, mix patterns: start pure-agent, add a small status page or list view if you discover users want one.
|
|
78
|
-
|
|
79
|
-
## Building one {#building}
|
|
80
|
-
|
|
81
|
-
If you're not a developer, you can usually start with the [Dispatch template](/docs/template-dispatch) — it's a workspace-style pure-agent app with Slack/Telegram, scheduled jobs, and shared secrets out of the box.
|
|
82
|
-
|
|
83
|
-
For developers who want the absolute minimum, start from a headless scaffold:
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
npx @agent-native/core@latest create my-agent --headless
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
This gives you the framework runtime, SQL-backed state, an `actions/` directory, and `pnpm agent` for running the local app-agent loop. Add one useful `defineAction()` and you have a real agent-native app. The same action can later render in chat, appear behind a button, expose an MCP tool, or move into a full UI without changing the core operation.
|
|
90
|
-
|
|
91
|
-
Use [**Chat**](/docs/template-chat) when you are ready to add a browser UI but do not want a domain template. Chat is the add-UI scaffold path, not the required default for a pure-agent app.
|
|
92
|
-
|
|
93
|
-
If you really want _zero_ custom UI except the agent, keep the app route focused on the built-in chat surface. The only thing the user sees is the chat. Everything else - job history, workspace, settings - is one click away in the panel's tabs.
|
|
94
|
-
|
|
95
|
-
### What you still get for free {#still-free}
|
|
96
|
-
|
|
97
|
-
Even with no custom UI, you still inherit every framework benefit:
|
|
98
|
-
|
|
99
|
-
- **Actions** as agent tools, HTTP endpoints, MCP tools, and A2A tools. External agents, Claude Desktop, and your own HTTP clients can drive the agent without going through the chat UI.
|
|
100
|
-
- **Recurring jobs** for scheduled work — "every morning at 7, summarize my unread emails and post to Slack."
|
|
101
|
-
- **The workspace** for per-user customization, skills, memory, MCP connections.
|
|
102
|
-
- **Sub-agent delegation** via [agent teams](/docs/agent-teams).
|
|
103
|
-
- **Portability** — deploys to any serverless host with any supported SQL database.
|
|
104
|
-
- **Multi-tenant by default** — each user gets their own workspace without a dev-box.
|
|
105
|
-
|
|
106
|
-
### Adding a tiny bit of UI {#tiny-ui}
|
|
107
|
-
|
|
108
|
-
Most pure-agent apps eventually want a little custom UI — not a dashboard, but maybe a status page, a job history, or a config screen. The [drop-in agent](/docs/drop-in-agent) components coexist with anything else you render. Add a single `/status` route that lists recent runs; keep everything else in the chat. That's usually enough.
|
|
109
|
-
|
|
110
|
-
Future UI-grafting tooling should use a distinct verb or namespace. `agent-native add` already means integration blueprints such as providers, channels, and sandbox adapters, so it should not also mean "add UI to this headless app."
|
|
111
|
-
|
|
112
|
-
## Repo access for cloud headless {#repo-access}
|
|
113
|
-
|
|
114
|
-
Local headless apps run against the folder on your machine. For cloud headless
|
|
115
|
-
apps that need repository access, use connector-scoped access: a GitHub
|
|
116
|
-
connector and token CRUD that can list repositories, search files, read files,
|
|
117
|
-
create or edit files, and delete files with the user's permission.
|
|
118
|
-
|
|
119
|
-
Do not design this as "clone the user's repo into our VM" or "give the agent a long-lived sandbox copy of the repo" as the primary model. Sandboxes are useful for isolated code execution, but repo access should be a provider integration with explicit tokens, scoped permissions, auditability, and revocation.
|
|
120
|
-
|
|
121
|
-
## Sharing sessions and runs {#sharing-runs}
|
|
122
|
-
|
|
123
|
-
Pure-agent work produces durable sessions and runs. Shareability should roll out in phases:
|
|
124
|
-
|
|
125
|
-
- **First:** read-only share links so a teammate can open a thread or run, inspect the sanitized transcript, outputs, and status, and follow along without taking control.
|
|
126
|
-
- **Later:** permissioned writable collaboration, such as continuing a run, editing schedules, approving actions, or changing configuration with explicit access checks.
|
|
127
|
-
|
|
128
|
-
That staged model keeps the first sharing surface useful without pretending collaborative control is solved before the permission model is ready.
|
|
23
|
+
→ [**Agent Surfaces — Headless agent**](/docs/agent-surfaces#headless)
|
|
129
24
|
|
|
130
25
|
## What's next
|
|
131
26
|
|
|
132
|
-
- [**
|
|
133
|
-
- [**
|
|
27
|
+
- [**Agent Surfaces — Headless**](/docs/agent-surfaces#headless) — the full headless decision guide and APIs
|
|
28
|
+
- [**Getting Started**](/docs/getting-started) — create a chat app or headless agent first
|
|
29
|
+
- [**Dispatch**](/docs/template-dispatch) — the workspace template that's a great pure-agent starting point
|
|
134
30
|
- [**Messaging the agent**](/docs/messaging) — how users talk to the agent across web, Slack, Telegram, email
|
|
135
31
|
- [**Recurring Jobs**](/docs/recurring-jobs) — scheduled prompts the agent runs on its own
|
|
136
|
-
- [**Dispatch**](/docs/template-dispatch) — the workspace template that's a great starting point for pure-agent apps
|
|
137
|
-
- [**Drop-in Agent**](/docs/drop-in-agent) — mounting `<AgentPanel>` fullscreen or in a sidebar
|
|
138
32
|
- [**Actions**](/docs/actions) — the tools your pure-agent will call
|
|
139
|
-
- [**Workspace**](/docs/workspace) — the customization surface for skills, memory, and MCP servers
|
|
@@ -125,7 +125,7 @@ pnpm add @tiptap/extension-collaboration @tiptap/extension-collaboration-caret @
|
|
|
125
125
|
|
|
126
126
|
Prevents Vite from re-bundling TipTap in incompatible ways during dev:
|
|
127
127
|
|
|
128
|
-
```
|
|
128
|
+
```ts
|
|
129
129
|
// vite.config.ts
|
|
130
130
|
export default defineConfig({
|
|
131
131
|
plugins: [reactRouter()],
|
|
@@ -149,7 +149,7 @@ via `registerShareableResource`. Without it, collab push events are delivered
|
|
|
149
149
|
to all authenticated users without document-level scoping, and the server
|
|
150
150
|
logs a one-time warning.
|
|
151
151
|
|
|
152
|
-
```
|
|
152
|
+
```ts
|
|
153
153
|
// server/plugins/collab.ts
|
|
154
154
|
import { createCollabPlugin } from "@agent-native/core/server";
|
|
155
155
|
|
|
@@ -163,7 +163,7 @@ export default createCollabPlugin({
|
|
|
163
163
|
|
|
164
164
|
### 4. Use the client hook
|
|
165
165
|
|
|
166
|
-
```
|
|
166
|
+
```ts
|
|
167
167
|
import {
|
|
168
168
|
useCollaborativeDoc,
|
|
169
169
|
emailToColor,
|
|
@@ -186,7 +186,7 @@ const { ydoc, awareness, isLoading, activeUsers, agentActive, agentPresent } =
|
|
|
186
186
|
|
|
187
187
|
### 5. Add TipTap extensions
|
|
188
188
|
|
|
189
|
-
```
|
|
189
|
+
```ts
|
|
190
190
|
import Collaboration from "@tiptap/extension-collaboration";
|
|
191
191
|
import CollaborationCaret from "@tiptap/extension-collaboration-caret";
|
|
192
192
|
|
|
@@ -208,7 +208,7 @@ const editor = useEditor({
|
|
|
208
208
|
The Collaboration extension does not auto-seed from a `content` prop. If the
|
|
209
209
|
Y.Doc is empty and the document has existing content, seed it:
|
|
210
210
|
|
|
211
|
-
```
|
|
211
|
+
```ts
|
|
212
212
|
useEffect(() => {
|
|
213
213
|
if (!ydoc || !editor || !isLoaded) return;
|
|
214
214
|
const fragment = ydoc.getXmlFragment("default");
|
|
@@ -337,7 +337,7 @@ Awareness state changes now propagate at ~150ms instead of the 2s poll cycle:
|
|
|
337
337
|
|
|
338
338
|
Returns a reactive list of remote participants and a setter for the local presence payload:
|
|
339
339
|
|
|
340
|
-
```
|
|
340
|
+
```ts
|
|
341
341
|
import { usePresence } from "@agent-native/core/client";
|
|
342
342
|
|
|
343
343
|
const { others, setPresence } = usePresence(awareness, ydoc?.clientID);
|
|
@@ -400,7 +400,7 @@ import { RemoteSelectionRings } from "@agent-native/core/client";
|
|
|
400
400
|
|
|
401
401
|
Invoke a callback whenever the followed participant's viewport changes:
|
|
402
402
|
|
|
403
|
-
```
|
|
403
|
+
```ts
|
|
404
404
|
import { useFollowUser } from "@agent-native/core/client";
|
|
405
405
|
|
|
406
406
|
const { isFollowing, stopFollowing } = useFollowUser({
|
|
@@ -435,7 +435,7 @@ The `PresenceBar` component now accepts optional follow-mode props:
|
|
|
435
435
|
|
|
436
436
|
### Normalized coordinate helpers {#norm-coords}
|
|
437
437
|
|
|
438
|
-
```
|
|
438
|
+
```ts
|
|
439
439
|
import { toNormalized, fromNormalized } from "@agent-native/core/client";
|
|
440
440
|
|
|
441
441
|
// In a pointer event handler:
|
|
@@ -454,7 +454,7 @@ const px = fromNormalized(norm, container.getBoundingClientRect());
|
|
|
454
454
|
|
|
455
455
|
Server-side actions call `agentUpdateSelection()` to publish where the agent is working. The design template's `edit-design` and `generate-design` actions call this automatically. Other templates can do the same:
|
|
456
456
|
|
|
457
|
-
```
|
|
457
|
+
```ts
|
|
458
458
|
import {
|
|
459
459
|
agentEnterDocument,
|
|
460
460
|
agentLeaveDocument,
|
|
@@ -511,7 +511,7 @@ plugin:
|
|
|
511
511
|
|
|
512
512
|
### Always set `resourceType`
|
|
513
513
|
|
|
514
|
-
```
|
|
514
|
+
```ts
|
|
515
515
|
createCollabPlugin({
|
|
516
516
|
resourceType: "document", // the name passed to registerShareableResource
|
|
517
517
|
});
|
|
@@ -535,7 +535,7 @@ Write routes (`update`, `text`, `json`, `patch`, `search-replace`) reject
|
|
|
535
535
|
payloads exceeding the configured limit with HTTP 413. The default is 2 MB.
|
|
536
536
|
Override per-plugin:
|
|
537
537
|
|
|
538
|
-
```
|
|
538
|
+
```ts
|
|
539
539
|
createCollabPlugin({
|
|
540
540
|
resourceType: "document",
|
|
541
541
|
maxPayloadBytes: 512 * 1024, // 512 KB
|
|
@@ -561,7 +561,7 @@ applies them atomically, so concurrent edits to different items both survive.
|
|
|
561
561
|
**Slides (`patch-deck`)** — Instead of replacing the entire deck JSON on every
|
|
562
562
|
change, the action accepts per-slide operations:
|
|
563
563
|
|
|
564
|
-
```
|
|
564
|
+
```ts
|
|
565
565
|
// Conceptual patch-deck action shape
|
|
566
566
|
type PatchDeckOp =
|
|
567
567
|
| { type: "patch"; slideId: string; fields: Partial<SlideFields> }
|
|
@@ -593,7 +593,7 @@ The Design template uses `Y.UndoManager` to scope undo/redo to the local
|
|
|
593
593
|
user's own edits. Remote peer edits and agent edits are never undone by a
|
|
594
594
|
user's Cmd+Z.
|
|
595
595
|
|
|
596
|
-
```
|
|
596
|
+
```ts
|
|
597
597
|
import * as Y from "yjs";
|
|
598
598
|
|
|
599
599
|
const LOCAL_EDIT_ORIGIN = "local";
|
|
@@ -660,7 +660,7 @@ layers on top of the same awareness state.
|
|
|
660
660
|
|
|
661
661
|
## Related docs {#related}
|
|
662
662
|
|
|
663
|
-
- [Real-Time Sync](/docs/
|
|
663
|
+
- [Real-Time Sync](/docs/client#usedbsync) — the `useDbSync` + `useChangeVersion`
|
|
664
664
|
system that delivers the `updatedAt` bump driving editor reconciliation.
|
|
665
665
|
- [Security](/docs/security) — `registerShareableResource`, `resolveAccess`,
|
|
666
666
|
and `assertAccess` for the access model referenced by `resourceType`.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Routing"
|
|
3
|
+
description: "File-based routing for agent-native apps with React Router v7 — pages, dynamic params, and navigation."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Routing
|
|
7
|
+
|
|
8
|
+
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.
|
|
9
|
+
|
|
10
|
+
## File-Based Routing {#file-based-routing}
|
|
11
|
+
|
|
12
|
+
### File → URL mapping
|
|
13
|
+
|
|
14
|
+
| File | URL | Notes |
|
|
15
|
+
| --------------------- | ------------------ | -------------------------------------- |
|
|
16
|
+
| `_index.tsx` | `/` | Index route |
|
|
17
|
+
| `settings.tsx` | `/settings` | Simple page |
|
|
18
|
+
| `inbox.$threadId.tsx` | `/inbox/:threadId` | Dot = `/`, `$` = dynamic param |
|
|
19
|
+
| `_app.tsx` | (no URL segment) | Pathless layout — prefix with `_` |
|
|
20
|
+
| `inbox/route.tsx` | `/inbox` | Folder form — `route.tsx` is the index |
|
|
21
|
+
|
|
22
|
+
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.
|
|
23
|
+
|
|
24
|
+
## Adding a new page {#adding-a-page}
|
|
25
|
+
|
|
26
|
+
Create the file and export a default component:
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// app/routes/settings.tsx
|
|
30
|
+
export function meta() {
|
|
31
|
+
return [{ title: "Settings" }];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default function SettingsPage() {
|
|
35
|
+
return <div>Settings</div>;
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
That's it — React Router picks it up automatically, no registration needed.
|
|
40
|
+
|
|
41
|
+
## Dynamic params {#dynamic-params}
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
// app/routes/inbox/$threadId.tsx
|
|
45
|
+
import { useParams } from "react-router";
|
|
46
|
+
|
|
47
|
+
export default function ThreadPage() {
|
|
48
|
+
const { threadId } = useParams();
|
|
49
|
+
return <div>Thread: {threadId}</div>;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Navigation {#navigation}
|
|
54
|
+
|
|
55
|
+
Use `<Link>` for client-side navigation and `useNavigate()` for programmatic navigation:
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { Link, useNavigate } from "react-router";
|
|
59
|
+
|
|
60
|
+
// In JSX
|
|
61
|
+
<Link to="/settings">Settings</Link>;
|
|
62
|
+
|
|
63
|
+
// Programmatic
|
|
64
|
+
const navigate = useNavigate();
|
|
65
|
+
navigate(`/inbox/${threadId}`);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## What's next
|
|
69
|
+
|
|
70
|
+
- [**Client**](/docs/client) — the agent-native browser hooks and utilities
|
|
71
|
+
- [**Server**](/docs/server) — file-based server routes and the `/_agent-native/` namespace
|
|
@@ -1,8 +1,35 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
3
|
-
description: "
|
|
2
|
+
title: "Adapters"
|
|
3
|
+
description: "The framework's two adapter seams: sandbox adapters swap the backend that runs the agent's run-code tool, and CLI adapters give the agent structured access to command-line tools."
|
|
4
|
+
search: "adapters sandbox adapter cli adapter run-code SandboxAdapter CliAdapter ShellCliAdapter durable runner remote sandbox edge serverless child_process"
|
|
4
5
|
---
|
|
5
6
|
|
|
7
|
+
# Adapters
|
|
8
|
+
|
|
9
|
+
> **Who is this for:** host authors extending the runtime. App developers rarely
|
|
10
|
+
> need this — the defaults work out of the box.
|
|
11
|
+
|
|
12
|
+
Agent-Native has two adapter seams that factor a concern out behind a narrow,
|
|
13
|
+
swappable interface:
|
|
14
|
+
|
|
15
|
+
- **Sandbox adapters** swap the backend that runs the agent's `run-code` tool —
|
|
16
|
+
a local child process by default, or a Docker / remote / durable runner.
|
|
17
|
+
- **CLI adapters** give the agent structured access to command-line tools
|
|
18
|
+
(`gh`, `ffmpeg`, `stripe`) with discovery, availability checks, and a
|
|
19
|
+
consistent result shape.
|
|
20
|
+
|
|
21
|
+
Both share one runtime constraint: they rely on Node.js system bindings and do
|
|
22
|
+
not run on edge/worker runtimes — see [Edge and serverless](#edge-serverless).
|
|
23
|
+
|
|
24
|
+
## Which coding doc do I want? {#which-doc}
|
|
25
|
+
|
|
26
|
+
| You want to… | Use |
|
|
27
|
+
| -------------------------------------------------------------------------- | -------------------------------------------- |
|
|
28
|
+
| Swap the backend that runs the agent's **`run-code` tool** | **Sandbox adapters** (this page) |
|
|
29
|
+
| Wrap a CLI tool (`gh`, `ffmpeg`) for the agent to call | **CLI adapters** (this page) |
|
|
30
|
+
| Render a Claude-Code/Codex-style **coding workspace UI** | [Agent-Native Code UI](/docs/code-agents-ui) |
|
|
31
|
+
| Run Claude Code / Codex / Pi **as the agent**, with their own loop + tools | [Harness Agents](/docs/harness-agents) |
|
|
32
|
+
|
|
6
33
|
# Sandbox Adapters
|
|
7
34
|
|
|
8
35
|
The `run-code` tool runs agent-supplied JavaScript in an isolated environment. **Sandbox adapters** factor the _execution_ concern out of that tool so the backend can be swapped — a local child process by default, or a Docker / remote / durable runner — without touching the agent loop, `run-code.ts`, the localhost bridge, the env scrub, or the output formatting.
|
|
@@ -74,13 +101,13 @@ Out of the box, `getSandboxAdapter()` returns `LocalChildProcessAdapter` (`id: "
|
|
|
74
101
|
- Temp files are cleaned up best-effort after the run.
|
|
75
102
|
|
|
76
103
|
> [!WARNING]
|
|
77
|
-
> The default adapter uses `node:child_process`, which does not exist on edge/worker runtimes
|
|
104
|
+
> The default adapter uses `node:child_process`, which does not exist on edge/worker runtimes. Run `run-code` in a standard Node.js environment, or register a remote adapter — see [Edge and serverless](#edge-serverless).
|
|
78
105
|
|
|
79
106
|
## Selecting an adapter {#selection}
|
|
80
107
|
|
|
81
108
|
Resolution order — an explicitly registered adapter wins; otherwise the env var selects a built-in; otherwise the local default is used:
|
|
82
109
|
|
|
83
|
-
```
|
|
110
|
+
```text
|
|
84
111
|
registerSandboxAdapter(adapter) → AGENT_NATIVE_SANDBOX → local default
|
|
85
112
|
```
|
|
86
113
|
|
|
@@ -127,8 +154,55 @@ Register it under a new `AGENT_NATIVE_SANDBOX` value (e.g. `remote`) and/or via
|
|
|
127
154
|
> [!TIP]
|
|
128
155
|
> The `agent-native add sandbox docker` blueprint emits a full, self-contained recipe for implementing a Docker adapter against this seam. See [Blueprint Installer](/docs/blueprint-installer).
|
|
129
156
|
|
|
157
|
+
# CLI Adapters
|
|
158
|
+
|
|
159
|
+
The other adapter seam 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. Every CLI adapter implements `CliAdapter`:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
import type { CliAdapter, CliResult } from "@agent-native/core/adapters/cli";
|
|
163
|
+
|
|
164
|
+
interface CliAdapter {
|
|
165
|
+
name: string; // "gh", "stripe", "ffmpeg"
|
|
166
|
+
description: string; // What the agent sees during discovery
|
|
167
|
+
isAvailable(): Promise<boolean>;
|
|
168
|
+
execute(args: string[]): Promise<CliResult>;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
interface CliResult {
|
|
172
|
+
stdout: string;
|
|
173
|
+
stderr: string;
|
|
174
|
+
exitCode: number;
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
For most CLIs, `ShellCliAdapter` wraps any binary with sensible defaults, and `CliRegistry` collects adapters for runtime discovery:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import { CliRegistry, ShellCliAdapter } from "@agent-native/core/adapters/cli";
|
|
182
|
+
|
|
183
|
+
const cliRegistry = new CliRegistry();
|
|
184
|
+
cliRegistry.register(
|
|
185
|
+
new ShellCliAdapter({
|
|
186
|
+
command: "gh",
|
|
187
|
+
description: "GitHub CLI — manage repos, PRs, issues, and releases",
|
|
188
|
+
}),
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
await cliRegistry.describe(); // [{ name, description, available }] for discovery
|
|
192
|
+
const gh = cliRegistry.get("gh");
|
|
193
|
+
const result = await gh?.execute(["pr", "list", "--json", "title,url"]);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Wrap a CLI call in `defineAction` to expose it on the action surface. See the [CLI Adapters](/docs/cli-adapters) quick reference for `ShellCliAdapter` options, custom adapters, and the action-wrapping pattern.
|
|
197
|
+
|
|
198
|
+
## Edge and serverless {#edge-serverless}
|
|
199
|
+
|
|
200
|
+
> [!WARNING]
|
|
201
|
+
> Both adapter seams rely on Node.js system bindings. The sandbox `LocalChildProcessAdapter` and CLI adapters (`ShellCliAdapter` and custom adapters) use `node:child_process` (`execFile` / `spawn`), which **does not exist** on edge/worker runtimes such as Cloudflare Workers or Netlify Edge Functions. If you deploy server routes to these edge presets, executing these adapters throws a runtime exception. Run adapter endpoints and tasks in a standard Node.js environment (traditional server containers or serverless Node functions) — or, for the sandbox seam, register a remote adapter that ships work out of process.
|
|
202
|
+
|
|
130
203
|
## What's next
|
|
131
204
|
|
|
205
|
+
- [**CLI Adapters**](/docs/cli-adapters) — the quick reference for the CLI seam
|
|
132
206
|
- [**Blueprint Installer**](/docs/blueprint-installer) — `agent-native add sandbox docker` prints a Docker-adapter recipe
|
|
133
207
|
- [**Agent Teams**](/docs/agent-teams) — delegating heavy work to sub-agents
|
|
134
208
|
- [**Security**](/docs/security) — the env scrub and bridge allowlist posture
|
package/docs/content/security.md
CHANGED
|
@@ -7,6 +7,23 @@ description: "Security model for agent-native apps: input validation, SQL inject
|
|
|
7
7
|
|
|
8
8
|
Agent-native apps are designed to be secure by default. The framework provides automatic protections at multiple layers — you get SQL-level data isolation, parameterized queries, input validation, and authentication out of the box.
|
|
9
9
|
|
|
10
|
+
## What you get for free, and what you own {#what-you-own}
|
|
11
|
+
|
|
12
|
+
When you build on the standard patterns, the framework already handles most of the threat surface for you:
|
|
13
|
+
|
|
14
|
+
- **Data isolation** — agent SQL is rewritten so it can only see the current user's (and active org's) rows. See [Data Scoping](#data-scoping).
|
|
15
|
+
- **SQL injection** — `db-query`/`db-exec` and Drizzle always parameterize. See [SQL Injection Prevention](#sql-injection).
|
|
16
|
+
- **XSS** — React auto-escapes, TipTap and `react-markdown` sanitize. See [XSS Prevention](#xss).
|
|
17
|
+
- **Auth & CSRF** — every `defineAction` is auth-guarded; cookies are `httpOnly` + `SameSite=lax`. See [Authentication](#auth).
|
|
18
|
+
- **Secret encryption** — credentials and the vault are encrypted at rest. See [Secrets Management](#secrets).
|
|
19
|
+
|
|
20
|
+
That leaves a small surface you actually have to think about:
|
|
21
|
+
|
|
22
|
+
- **A. Tag your tables for scoping.** Add `owner_email` (and `org_id` for team data) via [`ownableColumns()`](#data-scoping), and route Drizzle reads/writes through the [access guards](#access-guards).
|
|
23
|
+
- **B. Validate and route external input.** Give every action a Zod [`schema:`](#input-validation), and send any server-side fetch of a user/agent URL through the [SSRF guard](#ssrf).
|
|
24
|
+
|
|
25
|
+
Get those two right and the rest is defaults. The [Production Checklist](#production-checklist) is the one-page confirmation before you ship.
|
|
26
|
+
|
|
10
27
|
## Security by Design {#secure-by-design}
|
|
11
28
|
|
|
12
29
|
The framework architecture prevents common vulnerabilities when you use the standard patterns:
|
|
@@ -26,7 +43,7 @@ The framework architecture prevents common vulnerabilities when you use the stan
|
|
|
26
43
|
|
|
27
44
|
Use `defineAction` with a Zod `schema:` for every action. The framework validates input automatically before your code runs:
|
|
28
45
|
|
|
29
|
-
```
|
|
46
|
+
```ts
|
|
30
47
|
import { z } from "zod";
|
|
31
48
|
import { defineAction } from "@agent-native/core/action";
|
|
32
49
|
|
|
@@ -48,7 +65,7 @@ Invalid input returns clear error messages (400 for HTTP, structured error for a
|
|
|
48
65
|
|
|
49
66
|
The framework's `db-query` and `db-exec` tools use parameterized queries. User input is passed as arguments, never interpolated into the SQL string:
|
|
50
67
|
|
|
51
|
-
```
|
|
68
|
+
```ts
|
|
52
69
|
// SAFE — parameterized query (framework default)
|
|
53
70
|
await exec({ sql: "INSERT INTO notes (title) VALUES (?)", args: [title] });
|
|
54
71
|
|
|
@@ -72,7 +89,7 @@ React auto-escapes all JSX expressions. Additional guidelines:
|
|
|
72
89
|
|
|
73
90
|
Any server-side `fetch` of a user- or agent-controlled URL must go through the framework SSRF guard, or it can be pointed at cloud metadata (`169.254.169.254`), `localhost`, or internal services:
|
|
74
91
|
|
|
75
|
-
```
|
|
92
|
+
```ts
|
|
76
93
|
import { ssrfSafeFetch } from "@agent-native/core/extensions/url-safety";
|
|
77
94
|
|
|
78
95
|
const res = await ssrfSafeFetch(userProvidedUrl, {}, { maxRedirects: 3 });
|
|
@@ -98,7 +115,7 @@ The signed-in session carries `email` and (when an org is active) `orgId`. The f
|
|
|
98
115
|
|
|
99
116
|
Every table with user-specific data **must** have an `owner_email` text column. Use the camelCase Drizzle property name — `accessFilter` reads `resourceTable.ownerEmail`:
|
|
100
117
|
|
|
101
|
-
```
|
|
118
|
+
```ts
|
|
102
119
|
import {
|
|
103
120
|
table,
|
|
104
121
|
text,
|
|
@@ -141,7 +158,7 @@ For multi-user apps where teams share data, add an `org_id` column. When both co
|
|
|
141
158
|
|
|
142
159
|
The `ownableColumns()` schema helper adds `owner_email`, `org_id`, and `visibility` in one call, so new tenant-aware tables get the full scoping contract by default:
|
|
143
160
|
|
|
144
|
-
```
|
|
161
|
+
```ts
|
|
145
162
|
import { table, text, ownableColumns } from "@agent-native/core/db/schema";
|
|
146
163
|
|
|
147
164
|
export const projects = table("projects", {
|
|
@@ -212,7 +229,43 @@ Without `A2A_SECRET` in production, every A2A endpoint and the `/_agent-native/i
|
|
|
212
229
|
|
|
213
230
|
Inbound webhook handlers (Resend, SendGrid, Slack, Telegram, WhatsApp, Recall.ai, Deepgram, Zoom, Google Docs Pub/Sub) refuse forged requests by default in production: when the corresponding signing secret env var is missing, the handler returns 401 instead of accepting and dispatching.
|
|
214
231
|
|
|
215
|
-
This was previously a "warn and accept" stance — set the secret you'd otherwise be missing, or opt back into the old behavior with `AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS=1` for local dev only. See [
|
|
232
|
+
This was previously a "warn and accept" stance — set the secret you'd otherwise be missing, or opt back into the old behavior with `AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS=1` for local dev only. See [Messaging](/docs/messaging#env-vars) for the per-integration signing-secret variables.
|
|
233
|
+
|
|
234
|
+
## Production Checklist {#production-checklist}
|
|
235
|
+
|
|
236
|
+
### Auth & secrets
|
|
237
|
+
|
|
238
|
+
- [ ] `BETTER_AUTH_SECRET` set to a random 32+ char string (`openssl rand -hex 32`), unless this is a hosted workspace deploy deriving it from `A2A_SECRET`
|
|
239
|
+
- [ ] `OAUTH_STATE_SECRET` set to a separate random 32+ char string (don't reuse `BETTER_AUTH_SECRET`) — see [OAuth State Signing](#oauth-state)
|
|
240
|
+
- [ ] `A2A_SECRET` set on every app that calls or receives A2A traffic — see [A2A Identity Verification](#a2a-identity)
|
|
241
|
+
- [ ] `SECRETS_ENCRYPTION_KEY` set (or rely on the `BETTER_AUTH_SECRET` fallback) — see [Secrets Management](#secrets)
|
|
242
|
+
- [ ] `AUTH_SKIP_EMAIL_VERIFICATION` is **not** set in production (or set only on QA preview deploys)
|
|
243
|
+
|
|
244
|
+
### Webhook secrets (set the ones for integrations you use)
|
|
245
|
+
|
|
246
|
+
- [ ] Signing secret set for each enabled inbound integration — see [Inbound Webhooks](#webhooks) and [Messaging](/docs/messaging#env-vars) for the per-integration list
|
|
247
|
+
- [ ] `AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS` is **not** set in prod
|
|
248
|
+
|
|
249
|
+
### Schema
|
|
250
|
+
|
|
251
|
+
- [ ] Every user-facing table has `owner_email`, multi-user tables also `org_id` — see [Data Scoping](#data-scoping)
|
|
252
|
+
- [ ] Ownable-table reads/writes go through the [access guards](#access-guards)
|
|
253
|
+
- [ ] All actions use `defineAction` with Zod `schema:` — see [Input Validation](#input-validation)
|
|
254
|
+
- [ ] Server-side fetches of user/agent URLs go through `ssrfSafeFetch` — see [SSRF](#ssrf)
|
|
255
|
+
- [ ] No `dangerouslySetInnerHTML` with user content (or output is run through DOMPurify)
|
|
256
|
+
- [ ] No string-concatenated SQL
|
|
257
|
+
- [ ] `pnpm guards` is clean (`guard-no-unscoped-queries`, `guard-no-env-credentials`, `guard-no-env-mutation`, `guard-no-localhost-fallback`, `guard-no-unscoped-credentials`, `guard-no-drizzle-push`)
|
|
258
|
+
- [ ] Tested with two user accounts to verify data isolation
|
|
259
|
+
|
|
260
|
+
### Misc hardening
|
|
261
|
+
|
|
262
|
+
- [ ] `AGENT_NATIVE_DEBUG_ERRORS` is **not** set in real prod (only on debug previews)
|
|
263
|
+
- [ ] `AGENT_NATIVE_KEYS_WORKSPACE_FALLBACK` is **not** set unless your org actually shares workspace keys — see [Cross-User Tooling Secrets](#tooling-secrets)
|
|
264
|
+
- [ ] In multi-tenant deployments, **users bring their own `ANTHROPIC_API_KEY`** — the framework refuses to fall back to the deploy-level env var
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
The sections below cover niche environment flags you only reach for in specific deployments. Most apps never touch them.
|
|
216
269
|
|
|
217
270
|
## OAuth State Signing {#oauth-state}
|
|
218
271
|
|
|
@@ -237,36 +290,3 @@ AGENT_NATIVE_KEYS_WORKSPACE_FALLBACK=1
|
|
|
237
290
|
```
|
|
238
291
|
|
|
239
292
|
Workspace-scope secret writes still require org owner/admin role regardless of this flag.
|
|
240
|
-
|
|
241
|
-
## Production Checklist {#production-checklist}
|
|
242
|
-
|
|
243
|
-
### Auth & secrets
|
|
244
|
-
|
|
245
|
-
- [ ] `BETTER_AUTH_SECRET` set to a random 32+ char string (`openssl rand -hex 32`), unless this is a hosted workspace deploy deriving it from `A2A_SECRET`
|
|
246
|
-
- [ ] `OAUTH_STATE_SECRET` set to a separate random 32+ char string (don't reuse `BETTER_AUTH_SECRET`), unless this is a hosted workspace deploy deriving it from `A2A_SECRET`
|
|
247
|
-
- [ ] `A2A_SECRET` set on every app that calls or receives A2A traffic
|
|
248
|
-
- [ ] `SECRETS_ENCRYPTION_KEY` set (or rely on the `BETTER_AUTH_SECRET` fallback)
|
|
249
|
-
- [ ] `AUTH_SKIP_EMAIL_VERIFICATION` is **not** set in production (or set only on QA preview deploys)
|
|
250
|
-
|
|
251
|
-
### Webhook secrets (set the ones for integrations you use)
|
|
252
|
-
|
|
253
|
-
- [ ] `EMAIL_INBOUND_WEBHOOK_SECRET` if Resend / SendGrid inbound is enabled
|
|
254
|
-
- [ ] `SLACK_SIGNING_SECRET` if Slack is enabled
|
|
255
|
-
- [ ] `TELEGRAM_WEBHOOK_SECRET` / `WHATSAPP_APP_SECRET` for those integrations
|
|
256
|
-
- [ ] `AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS` is **not** set in prod
|
|
257
|
-
|
|
258
|
-
### Schema
|
|
259
|
-
|
|
260
|
-
- [ ] Every user-facing table has `owner_email`
|
|
261
|
-
- [ ] Multi-user tables also have `org_id`
|
|
262
|
-
- [ ] All actions use `defineAction` with Zod `schema:`
|
|
263
|
-
- [ ] No `dangerouslySetInnerHTML` with user content (or output is run through DOMPurify)
|
|
264
|
-
- [ ] No string-concatenated SQL
|
|
265
|
-
- [ ] `pnpm guards` is clean (`guard-no-unscoped-queries`, `guard-no-env-credentials`, `guard-no-env-mutation`, `guard-no-localhost-fallback`, `guard-no-unscoped-credentials`, `guard-no-drizzle-push`)
|
|
266
|
-
- [ ] Tested with two user accounts to verify data isolation
|
|
267
|
-
|
|
268
|
-
### Misc hardening
|
|
269
|
-
|
|
270
|
-
- [ ] `AGENT_NATIVE_DEBUG_ERRORS` is **not** set in real prod (only on debug previews)
|
|
271
|
-
- [ ] `AGENT_NATIVE_KEYS_WORKSPACE_FALLBACK` is **not** set unless your org actually shares workspace keys
|
|
272
|
-
- [ ] In multi-tenant deployments, **users bring their own `ANTHROPIC_API_KEY`** — the framework refuses to fall back to the deploy-level env var
|