@agent-native/core 0.164.4 → 0.164.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +84 -0
  3. package/corpus/templates/calendar/AGENTS.md +5 -0
  4. package/corpus/templates/calendar/actions/delete-event.ts +1 -1
  5. package/corpus/templates/calendar/actions/delete-events.ts +508 -0
  6. package/corpus/templates/calendar/actions/event-action-helpers.ts +1 -1
  7. package/corpus/templates/calendar/actions/list-events.ts +53 -0
  8. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +36 -47
  9. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +3 -0
  10. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +18 -1
  11. package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +256 -38
  12. package/corpus/templates/calendar/app/hooks/use-google-auth.ts +43 -3
  13. package/corpus/templates/calendar/app/i18n-data.ts +120 -0
  14. package/corpus/templates/calendar/app/lib/event-form-utils.ts +113 -0
  15. package/corpus/templates/calendar/changelog/2026-08-18-the-agent-can-now-remove-many-meetings-at-once-such-as-every.md +6 -0
  16. package/corpus/templates/calendar/server/handlers/google-auth.ts +119 -3
  17. package/corpus/templates/calendar/server/lib/event-weekday.ts +112 -0
  18. package/corpus/templates/calendar/server/routes/[base]/_agent-native/google/add-account/auth-url.post.ts +1 -0
  19. package/corpus/templates/calendar/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
  20. package/corpus/templates/calendar/server/routes/_agent-native/google/add-account/auth-url.post.ts +1 -0
  21. package/corpus/templates/calendar/server/routes/_agent-native/google/auth-url.post.ts +1 -0
  22. package/corpus/templates/clips/app/components/player/video-player.tsx +12 -0
  23. package/corpus/templates/clips/app/routes/_app.dictate.tsx +18 -4
  24. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +114 -20
  25. package/corpus/templates/clips/app/routes/share.$shareId.tsx +83 -78
  26. package/corpus/templates/clips/changelog/2026-08-19-clips-nightly-builds-are-available-for-trying-the-latest-upd.md +6 -0
  27. package/corpus/templates/clips/changelog/2026-08-19-social-preview-video-frames.md +1 -0
  28. package/corpus/templates/clips/desktop/README.md +19 -4
  29. package/corpus/templates/clips/desktop/src/app.tsx +78 -16
  30. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -2
  31. package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +1 -0
  32. package/corpus/templates/clips/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
  33. package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.get.ts +41 -3
  34. package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.post.ts +1 -0
  35. package/corpus/templates/clips/server/routes/_agent-native/google/callback.get.ts +22 -1
  36. package/corpus/templates/clips/server/routes/api/clips-updater.json.get.ts +41 -22
  37. package/corpus/templates/factory/AGENTS.md +3 -2
  38. package/corpus/templates/factory/actions/get-factory-graph-version.ts +69 -0
  39. package/corpus/templates/factory/actions/list-factory-automations.ts +4 -0
  40. package/corpus/templates/factory/actions/list-factory-graph-versions.ts +82 -0
  41. package/corpus/templates/factory/actions/restore-factory-graph-version.ts +163 -0
  42. package/corpus/templates/factory/actions/save-factory-graph.ts +93 -60
  43. package/corpus/templates/factory/actions/start-builder-for-item.ts +19 -17
  44. package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +100 -36
  45. package/corpus/templates/factory/app/components/factory/FactoryHistoryView.tsx +709 -0
  46. package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +62 -2
  47. package/corpus/templates/factory/app/components/ui/alert-dialog.tsx +1 -0
  48. package/corpus/templates/factory/app/i18n/en-US.ts +47 -0
  49. package/corpus/templates/factory/app/routes/factory.tsx +324 -61
  50. package/corpus/templates/factory/server/connectors/slack.ts +44 -2
  51. package/corpus/templates/factory/server/plugins/agent-chat.ts +12 -8
  52. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +13 -0
  53. package/corpus/templates/factory/server/triage/slack-client.ts +5 -0
  54. package/corpus/templates/factory/server/triage/slack-poller.ts +72 -4
  55. package/corpus/templates/mail/actions/view-screen.ts +1 -1
  56. package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +100 -18
  57. package/corpus/templates/mail/server/handlers/google-auth.ts +125 -24
  58. package/corpus/templates/mail/server/lib/gmail-query.ts +5 -2
  59. package/corpus/templates/mail/server/routes/[base]/_agent-native/google/add-account/auth-url.post.ts +1 -0
  60. package/corpus/templates/mail/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
  61. package/corpus/templates/mail/server/routes/_agent-native/google/add-account/auth-url.post.ts +1 -0
  62. package/corpus/templates/mail/server/routes/_agent-native/google/auth-url.post.ts +1 -0
  63. package/corpus/templates/plan/app/pages/PlansPage.tsx +0 -12
  64. package/corpus/templates/plan/changelog/2026-08-19-plan-prompts-now-guide-you-to-connect-ai-before-sending-and-.md +6 -0
  65. package/corpus/templates/slides/actions/approve-deck-access-request.ts +234 -0
  66. package/corpus/templates/slides/actions/get-deck-access-status.ts +35 -2
  67. package/corpus/templates/slides/actions/request-deck-access.ts +263 -64
  68. package/corpus/templates/slides/app/components/editor/MissingDeckAccessPane.tsx +196 -0
  69. package/corpus/templates/slides/app/components/editor/RequestDeckAccessDialog.tsx +101 -0
  70. package/corpus/templates/slides/app/hooks/use-deck-access.ts +7 -4
  71. package/corpus/templates/slides/app/i18n/en-US.ts +29 -6
  72. package/corpus/templates/slides/app/pages/DeckEditor.tsx +147 -169
  73. package/corpus/templates/slides/app/routes/access-request.approve.tsx +250 -0
  74. package/corpus/templates/slides/server/db/schema.ts +8 -0
  75. package/corpus/templates/slides/server/lib/access-request-email.ts +36 -0
  76. package/corpus/templates/slides/server/lib/emails.ts +26 -0
  77. package/corpus/templates/slides/server/plugins/agent-chat.ts +7 -0
  78. package/corpus/templates/slides/server/plugins/db.ts +50 -0
  79. package/corpus/templates/slides/shared/deck-access.ts +31 -0
  80. package/dist/a2a/task-store.d.ts +1 -0
  81. package/dist/a2a/task-store.js +1 -1
  82. package/dist/agent/observational-memory/store.d.ts +1 -0
  83. package/dist/agent/observational-memory/store.js +1 -1
  84. package/dist/agent/run-store.d.ts +1 -0
  85. package/dist/agent/run-store.js +1 -1
  86. package/dist/agent/tool-approval-store.d.ts +1 -0
  87. package/dist/agent/tool-approval-store.js +1 -1
  88. package/dist/application-state/store.d.ts +1 -0
  89. package/dist/application-state/store.js +1 -1
  90. package/dist/browser-sessions/store.d.ts +1 -0
  91. package/dist/browser-sessions/store.js +1 -1
  92. package/dist/checkpoints/store.d.ts +1 -0
  93. package/dist/checkpoints/store.js +1 -1
  94. package/dist/cli/code-agent-schedules.d.ts +11 -0
  95. package/dist/cli/code-agent-schedules.js +44 -5
  96. package/dist/client/AgentPanel.d.ts +5 -1
  97. package/dist/client/AgentPanel.js +2 -2
  98. package/dist/client/AssistantChat.d.ts +5 -1
  99. package/dist/client/AssistantChat.js +40 -6
  100. package/dist/client/EnvironmentBadge.d.ts +7 -13
  101. package/dist/client/EnvironmentBadge.js +43 -92
  102. package/dist/client/agent-chat/index.d.ts +1 -0
  103. package/dist/client/agent-chat/index.js +1 -0
  104. package/dist/client/agent-chat-adapter.js +76 -50
  105. package/dist/client/app-providers.js +4 -4
  106. package/dist/client/chat/message-components.d.ts +4 -0
  107. package/dist/client/chat/message-components.js +28 -1
  108. package/dist/client/chat/run-recovery.js +26 -0
  109. package/dist/client/chat-first/apps-rail.d.ts +1 -0
  110. package/dist/client/chat-first/apps-rail.js +28 -2
  111. package/dist/client/sse-event-processor.js +13 -8
  112. package/dist/client/use-active-agent-chat-run.d.ts +2 -0
  113. package/dist/client/use-active-agent-chat-run.js +22 -0
  114. package/dist/coding-tools/sandbox/executions-store.d.ts +1 -0
  115. package/dist/coding-tools/sandbox/executions-store.js +1 -1
  116. package/dist/collab/awareness-store.d.ts +1 -0
  117. package/dist/collab/awareness-store.js +1 -1
  118. package/dist/collab/routes.d.ts +1 -1
  119. package/dist/collab/storage.d.ts +1 -0
  120. package/dist/collab/storage.js +1 -1
  121. package/dist/collab/struct-routes.d.ts +1 -1
  122. package/dist/db/client.js +3 -3
  123. package/dist/db/ddl-guard.js +8 -0
  124. package/dist/db/migration-runtime.d.ts +23 -0
  125. package/dist/db/migration-runtime.js +41 -0
  126. package/dist/db/migrations.d.ts +1 -2
  127. package/dist/db/migrations.js +2 -24
  128. package/dist/email-catalog/log.d.ts +1 -0
  129. package/dist/email-catalog/log.js +1 -1
  130. package/dist/guards/index.d.ts +1 -0
  131. package/dist/guards/index.js +1 -0
  132. package/dist/guards/release-schema-complete.d.ts +22 -0
  133. package/dist/guards/release-schema-complete.js +144 -0
  134. package/dist/integrations/a2a-continuations-store.d.ts +1 -0
  135. package/dist/integrations/a2a-continuations-store.js +1 -1
  136. package/dist/integrations/awaiting-input-store.d.ts +1 -0
  137. package/dist/integrations/awaiting-input-store.js +1 -1
  138. package/dist/integrations/config-store.d.ts +1 -0
  139. package/dist/integrations/config-store.js +1 -1
  140. package/dist/integrations/controls-store.d.ts +1 -0
  141. package/dist/integrations/controls-store.js +1 -1
  142. package/dist/integrations/identity-links-store.d.ts +1 -0
  143. package/dist/integrations/identity-links-store.js +1 -1
  144. package/dist/integrations/installations-store.d.ts +1 -0
  145. package/dist/integrations/installations-store.js +1 -1
  146. package/dist/integrations/remote-commands-store.d.ts +1 -0
  147. package/dist/integrations/remote-commands-store.js +1 -1
  148. package/dist/integrations/remote-devices-store.d.ts +1 -0
  149. package/dist/integrations/remote-devices-store.js +1 -1
  150. package/dist/integrations/remote-push-store.d.ts +1 -0
  151. package/dist/integrations/remote-push-store.js +1 -1
  152. package/dist/integrations/remote-run-events-store.d.ts +1 -0
  153. package/dist/integrations/remote-run-events-store.js +1 -1
  154. package/dist/integrations/scope-store.d.ts +1 -0
  155. package/dist/integrations/scope-store.js +1 -1
  156. package/dist/integrations/thread-mapping-store.d.ts +1 -0
  157. package/dist/integrations/thread-mapping-store.js +1 -1
  158. package/dist/integrations/usage-budget-store.d.ts +1 -0
  159. package/dist/integrations/usage-budget-store.js +1 -1
  160. package/dist/jobs/background-automation-runner.js +111 -9
  161. package/dist/jobs/run-history.d.ts +1 -0
  162. package/dist/jobs/run-history.js +1 -1
  163. package/dist/jobs/scheduler-health.d.ts +1 -0
  164. package/dist/jobs/scheduler-health.js +1 -1
  165. package/dist/mcp/approval-store.d.ts +1 -0
  166. package/dist/mcp/approval-store.js +1 -1
  167. package/dist/mcp/connect-store.d.ts +1 -0
  168. package/dist/mcp/connect-store.js +1 -1
  169. package/dist/mcp/oauth-store.d.ts +1 -0
  170. package/dist/mcp/oauth-store.js +1 -1
  171. package/dist/notifications/store.d.ts +1 -0
  172. package/dist/notifications/store.js +1 -1
  173. package/dist/oauth-tokens/store.d.ts +1 -0
  174. package/dist/oauth-tokens/store.js +1 -1
  175. package/dist/observability/routes.d.ts +5 -5
  176. package/dist/progress/routes.d.ts +1 -1
  177. package/dist/progress/store.d.ts +1 -0
  178. package/dist/progress/store.js +1 -1
  179. package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
  180. package/dist/provider-api/actions/provider-api.d.ts +4 -4
  181. package/dist/provider-api/corpus-jobs-store.d.ts +1 -0
  182. package/dist/provider-api/corpus-jobs-store.js +1 -1
  183. package/dist/provider-api/custom-registry.d.ts +1 -0
  184. package/dist/provider-api/custom-registry.js +1 -1
  185. package/dist/provider-api/quota-governor.d.ts +1 -0
  186. package/dist/provider-api/quota-governor.js +1 -1
  187. package/dist/provider-api/staged-datasets-store.d.ts +1 -0
  188. package/dist/provider-api/staged-datasets-store.js +1 -1
  189. package/dist/resources/handlers.d.ts +3 -3
  190. package/dist/resources/store.d.ts +1 -0
  191. package/dist/resources/store.js +1 -1
  192. package/dist/scripts/dev/index.js +15 -1
  193. package/dist/search/index.js +3 -0
  194. package/dist/secrets/routes.d.ts +9 -9
  195. package/dist/secrets/storage.d.ts +1 -0
  196. package/dist/secrets/storage.js +1 -1
  197. package/dist/server/agent-chat-plugin.js +38 -9
  198. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  199. package/dist/server/agent-teams-run-queue.d.ts +1 -0
  200. package/dist/server/agent-teams-run-queue.js +1 -1
  201. package/dist/server/auth-marketing.js +3 -1
  202. package/dist/server/auth.d.ts +18 -1
  203. package/dist/server/auth.js +370 -154
  204. package/dist/server/embed-session.d.ts +1 -0
  205. package/dist/server/embed-session.js +1 -1
  206. package/dist/server/google-auth-plugin.js +3 -1
  207. package/dist/server/google-oauth.d.ts +10 -0
  208. package/dist/server/google-oauth.js +25 -1
  209. package/dist/server/identity-sso-store.d.ts +1 -0
  210. package/dist/server/identity-sso-store.js +1 -1
  211. package/dist/server/index.d.ts +1 -1
  212. package/dist/server/index.js +1 -1
  213. package/dist/server/onboarding-html.js +200 -31
  214. package/dist/server/poll.d.ts +2 -0
  215. package/dist/server/poll.js +19 -1
  216. package/dist/server/realtime-token.d.ts +1 -1
  217. package/dist/server/release-migrations.js +5 -0
  218. package/dist/server/release-schema.d.ts +46 -0
  219. package/dist/server/release-schema.js +307 -0
  220. package/dist/settings/store.d.ts +1 -0
  221. package/dist/settings/store.js +1 -1
  222. package/dist/shared/environment-lanes.d.ts +28 -0
  223. package/dist/shared/environment-lanes.js +61 -0
  224. package/dist/sharing/actions/share-resource.js +22 -2
  225. package/dist/styles/agent-native.css +17 -15
  226. package/dist/triggers/routes.d.ts +11 -0
  227. package/dist/triggers/routes.js +10 -0
  228. package/dist/usage/alerts-store.d.ts +1 -0
  229. package/dist/usage/alerts-store.js +1 -1
  230. package/dist/usage/store.d.ts +1 -0
  231. package/dist/usage/store.js +1 -1
  232. package/docs/content/actions-access-control.mdx +136 -2
  233. package/docs/content/actions-agent-tools.mdx +101 -0
  234. package/docs/content/actions-defining.mdx +2 -2
  235. package/docs/content/audit-log.mdx +1 -1
  236. package/docs/content/authentication.mdx +1 -1
  237. package/docs/content/cloudflare.mdx +43 -0
  238. package/docs/content/creating-templates.mdx +1 -1
  239. package/docs/content/deploy-an-app.mdx +109 -0
  240. package/docs/content/deployment-environment-variables.mdx +101 -0
  241. package/docs/content/deployment.mdx +125 -792
  242. package/docs/content/docs-components.mdx +2 -2
  243. package/docs/content/environment-variables.mdx +41 -41
  244. package/docs/content/file-uploads.mdx +74 -40
  245. package/docs/content/harness-agents.mdx +2 -2
  246. package/docs/content/internationalization.mdx +61 -65
  247. package/docs/content/key-concepts.mdx +3 -3
  248. package/docs/content/multi-app-workspace.mdx +2 -2
  249. package/docs/content/netlify.mdx +47 -0
  250. package/docs/content/node-docker.mdx +138 -0
  251. package/docs/content/organizations-teams-permissions.mdx +5 -5
  252. package/docs/content/other-platforms.mdx +27 -0
  253. package/docs/content/ssr-caching.mdx +45 -0
  254. package/docs/content/template-assets-developers.mdx +2 -2
  255. package/docs/content/template-chat-developers.mdx +2 -2
  256. package/docs/content/template-content-developers.mdx +2 -2
  257. package/docs/content/{local-file-mode.mdx → template-content-local-files.mdx} +2 -1
  258. package/docs/content/template-content-sync.mdx +2 -2
  259. package/docs/content/toolkit-agent-ux.mdx +1 -1
  260. package/docs/content/toolkit-resources.mdx +2 -2
  261. package/docs/content/updating-ui-in-production.mdx +35 -0
  262. package/docs/content/vercel.mdx +38 -0
  263. package/docs/content/what-is-agent-native.mdx +1 -1
  264. package/docs/content/workspace-deployment.mdx +350 -0
  265. package/package.json +2 -2
  266. package/docs/content/agents.mdx +0 -9
  267. package/docs/content/database.mdx +0 -317
  268. package/docs/content/human-approval.mdx +0 -156
@@ -83,7 +83,141 @@ Use it only for safe, read-only, or ingest-only actions, the kind you'd let a st
83
83
 
84
84
  A handful of actions are too consequential to let the agent run on its own, like sending an email, charging a card, or deleting an account. Set `needsApproval: true` (or a predicate `(args, ctx) => boolean | Promise<boolean>`) on `defineAction()`, and the loop pauses before `run()`, requiring a human to approve that specific call. The default is **off**, and almost every action should stay that way.
85
85
 
86
- See [Human-in-the-Loop Approvals](/docs/human-approval) for the predicate API, the `approval_required` event, and the full end-to-end flow.
86
+ <Callout tone="warning">
87
+ Keep approvals rare. Every gated action is a hard stop in the agent loop. It
88
+ interrupts the run and demands a human round-trip. Use `needsApproval` only
89
+ for genuinely high-consequence, hard-to-undo, outward-facing operations. If
90
+ you find yourself gating reads or routine writes, you're holding it wrong. The
91
+ default is **off**, and almost every action should leave it off.
92
+ </Callout>
93
+
94
+ <AnnotatedCode
95
+ id="doc-block-13p4g9y"
96
+ title="Gating the one consequential action"
97
+ filename="actions/send-email.ts"
98
+ language="ts"
99
+ code={
100
+ 'export default defineAction({\n description: "Send an email via Gmail.",\n schema: z.object({\n to: z.string(),\n subject: z.string(),\n body: z.string(),\n }),\n // Sending is outward-facing and hard to undo, so the agent can never send\n // without a human approving the specific call. Drafting and queueing are\n // not affected. Only the real send is gated.\n needsApproval: true,\n run: async (args) => {\n /* ...actually send... */\n },\n});'
101
+ }
102
+ annotations={[
103
+ {
104
+ lines: "10",
105
+ label: "The whole gate",
106
+ note: "One flag. When it is truthy and the call has not been approved, the loop stops before `run`. The model never reaches the side effect on its own.",
107
+ },
108
+ {
109
+ lines: "11-13",
110
+ label: "run() is untouched",
111
+ note: "The handler stays the same. Approval is enforced by the loop around it, not by anything inside `run`.",
112
+ },
113
+ ]}
114
+ />
115
+
116
+ - **`needsApproval: true`** always requires approval.
117
+ - **`needsApproval: (args, ctx) => boolean | Promise<boolean>`** requires approval only when the predicate returns true. Use this to gate conditionally, for example only for external recipients or only above a dollar threshold:
118
+
119
+ ```ts
120
+ needsApproval: (args) => !args.to.endsWith("@your-company.com"),
121
+ ```
122
+
123
+ Keep the predicate pure and fast. If the predicate throws an error, the framework treats the call the same as if the predicate had returned true, so the action pauses for approval instead of running. This means a broken predicate can never silently let a high-consequence action run without a human approving it first.
124
+
125
+ When `needsApproval` is omitted, behavior is byte-for-byte unchanged. There is no extra cost on the common path.
126
+
127
+ This works the same way for legacy `parameters`-style actions and for schema-based actions. It also works the same way for the in-app agent, sub-agents, A2A, and MCP callers, because every agent surface routes through the same loop.
128
+
129
+ ### How the loop pauses {#loop}
130
+
131
+ When the agent calls a gated action and this specific call has **not** already been approved, the loop does **not** execute `run()`. Instead it:
132
+
133
+ 1. Resolves the gate. For a predicate, the loop calls `needsApproval(input, ctx)`. If that call throws an error, the loop treats it the same as a `true` result, so the action requires approval.
134
+ 2. Emits a `tool_start` event (so the UI shows the call) followed immediately by an **`approval_required`** event, then stops the turn. The action's side effect never happens.
135
+
136
+ The `approval_required` event carries everything the client needs to render an affordance:
137
+
138
+ | Field | Type | Notes |
139
+ | ------------- | -------- | ------------------------------------------------------------------- |
140
+ | `tool` | `string` | The action name the agent tried to call. |
141
+ | `input` | object | The arguments the agent passed. |
142
+ | `approvalKey` | `string` | **Stable key** the client echoes back to approve _this exact call_. |
143
+ | `toolCallId` | `string` | The model-side tool-call id, when available. |
144
+
145
+ The `approvalKey` is derived deterministically from the tool name plus its input, so the same logical call always produces the same key. The model never sees or sets it. It exists purely as a handshake between the framework and the human's Approve affordance.
146
+
147
+ The paused tool returns a result telling the model the turn is paused and not to retry, so the model does not spin.
148
+
149
+ ### How the human approves {#approve}
150
+
151
+ On `approval_required`, the chat UI renders an **Approve / Deny** affordance on the paused tool call. This is wired automatically in `AssistantChat`. You do not need to build it per template.
152
+
153
+ <WireframeBlock id="doc-block-humanapprovalwf1">
154
+ <Screen
155
+ surface="popover"
156
+ html={
157
+ "<div style='display:flex;flex-direction:column;box-sizing:border-box;padding:16px;gap:12px'><div class='wf-card' style='display:flex;flex-direction:column;gap:8px'><div style='display:flex;align-items:center;gap:8px'><span class='wf-pill warn'>Needs approval</span><strong>send-email</strong></div><div style='display:flex;flex-direction:column;gap:2px'><span class='wf-muted'>to: alex@acme.com</span><span class='wf-muted'>subject: Following up on the proposal</span></div><div style='display:flex;gap:8px'><button>Deny</button><button class='primary'>Approve</button></div></div></div>"
158
+ }
159
+ />
160
+ </WireframeBlock>
161
+
162
+ - **Approve** re-issues the turn as an ordinary continuation message that carries the call's key in `approvedToolCalls: [approvalKey]`. On the re-issued turn, the gate sees the key in the approved set and lets that specific call run normally.
163
+ - **Deny** dismisses the affordance locally. Nothing is re-issued, so the action never runs.
164
+
165
+ `approvedToolCalls` is a field on the chat request (`AgentChatRequest.approvedToolCalls`). Keys that are not present in it stay paused. Approving one call never approves any other call. Because the key is content-addressed, an approval only authorizes that exact call with those exact arguments. If the model later proposes a different send, that becomes a new key and needs its own fresh approval.
166
+
167
+ ### End-to-end {#flow}
168
+
169
+ <Diagram id="doc-block-2nor1g" title="The approval interrupt" summary={"A gated call pauses the turn before run() fires. Approval re-issues the turn carrying the call's key. Only then does the side effect happen."}>
170
+
171
+ ```html
172
+ <div class="diagram-approve">
173
+ <div class="diagram-box" data-rough>Agent calls send-email</div>
174
+ <div class="diagram-arrow diagram-muted" aria-hidden="true">&darr;</div>
175
+ <div class="diagram-panel warn" data-rough>
176
+ <strong>Gate truthy, call not yet approved</strong
177
+ ><small class="diagram-muted"
178
+ >loop emits tool_start + approval_required { tool, input, approvalKey
179
+ }</small
180
+ ><span class="diagram-pill warn">run() has not executed</span>
181
+ </div>
182
+ <div class="diagram-arrow diagram-muted" aria-hidden="true">&darr;</div>
183
+ <div class="diagram-box" data-rough>
184
+ Human clicks Approve in chat<br /><small class="diagram-muted"
185
+ >client re-issues the turn with approvedToolCalls: [approvalKey]</small
186
+ >
187
+ </div>
188
+ <div class="diagram-arrow diagram-muted" aria-hidden="true">&darr;</div>
189
+ <div class="diagram-panel ok" data-rough>
190
+ <span class="diagram-pill ok"
191
+ >Gate sees the key &rarr; run() executes &rarr; email sends</span
192
+ >
193
+ </div>
194
+ </div>
195
+ ```
196
+
197
+ ```css
198
+ .diagram-approve {
199
+ display: flex;
200
+ flex-direction: column;
201
+ align-items: center;
202
+ gap: 8px;
203
+ }
204
+ .diagram-approve .diagram-panel {
205
+ display: flex;
206
+ flex-direction: column;
207
+ gap: 6px;
208
+ align-items: center;
209
+ padding: 12px 16px;
210
+ text-align: center;
211
+ }
212
+ .diagram-approve .diagram-arrow {
213
+ font-size: 22px;
214
+ line-height: 1;
215
+ }
216
+ ```
217
+
218
+ </Diagram>
219
+
220
+ The Mail template's `send-email` action is the canonical use of this gate in the framework, and it is intentionally rare. It sets `needsApproval: true` so the agent can draft and queue emails freely. It can never actually send a message without a human approving that specific send.
87
221
 
88
222
  ## Restrict callers by role {#authorize}
89
223
 
@@ -220,7 +354,7 @@ A few other options. `audit: { onRead: true }` audits a sensitive read, like a s
220
354
  - [**Overview**](/docs/actions-overview) — what an action is and the hello-world walkthrough
221
355
  - [**Defining Actions**](/docs/actions-defining) — `defineAction()` anatomy, schema, and HTTP config
222
356
  - [**Run Context**](/docs/actions-run-context) — the `ctx` argument these checks read
223
- - [**Human-in-the-Loop Approvals**](/docs/human-approval) — the `needsApproval` gate in depth
357
+ - [**Mail template**](/docs/template-mail) — `send-email` is the reference example for `needsApproval`
224
358
  - [**Audit Log**](/docs/audit-log) — the automatic who-changed-what trail
225
359
  - [**Security**](/docs/security) — `accessFilter`/`assertAccess` and the extension iframe trust boundary
226
360
  - [**Sharing**](/docs/sharing) — the framework sharing system these helpers back
@@ -0,0 +1,101 @@
1
+ ---
2
+ title: "Production Agent Access"
3
+ description: "Control which framework tools and code-execution capabilities a deployed production agent has, through frameworkTools and codeExecution."
4
+ ---
5
+
6
+ # Production Agent Access
7
+
8
+ ## Production Agent Tools {#production-agent-tools}
9
+
10
+ Production agents get the app's registered actions plus the framework's own
11
+ tools. `frameworkTools` selects which of those framework tools this app exposes.
12
+ Every group defaults to today's behavior, so omitting the option changes
13
+ nothing:
14
+
15
+ ```ts filename="server/plugins/agent-chat.ts"
16
+ export default createAgentChatPlugin({
17
+ frameworkTools: {
18
+ database: "read", // "write" | "read" | "off", default "read"
19
+ extensions: true, // default false
20
+ sharing: false, // this app has no share surface
21
+ review: false,
22
+ },
23
+ });
24
+ ```
25
+
26
+ ```ts filename="server/plugins/core-routes.ts"
27
+ export default createCoreRoutesPlugin({
28
+ extensionTools: true,
29
+ });
30
+ ```
31
+
32
+ `database` and `extensions` accept the values the old top-level flags did:
33
+
34
+ - `database: "read"`. Default. Registers only `db-schema` and `db-query`.
35
+ Agents inspect data with SQL but must use typed app actions for writes.
36
+ - `database: "write"` or `true`. Additionally registers `db-exec` and
37
+ `db-patch` for deliberate raw SQL maintenance. Writes are scoped to the
38
+ current user/org and schema changes are blocked.
39
+ - `database: "off"` or `false`. Removes raw database tools from the
40
+ agent surface so the app's actions are the only data access path.
41
+ - `extensions: true`. Opts the app into framework extension-management
42
+ actions and prompt guidance (`create-extension`, `update-extension`, etc.).
43
+ Set `extensionTools: true` on the core-routes plugin as well to allow
44
+ authenticated REST creation. Extensions are disabled by default.
45
+
46
+ The remaining groups are booleans, all on by default: `sharing`, `review`,
47
+ `history`, `featureFlags`, `localization`, `audit`, `contextXray`,
48
+ `userProfile`, `automation`, `docs`, `resources`, `web`, `workspaceApps`,
49
+ `chat`, and `email`. Use `frameworkTools: "minimal"` to turn every group off at
50
+ once for a voice-first or single-purpose app, or
51
+ `frameworkTools: { preset: "minimal", resources: true }` to keep just one. An
52
+ explicit group key always wins over the preset.
53
+
54
+ Switching a group off removes it from the **agent** surfaces only: interactive
55
+ chat, MCP, A2A, and background runs. The matching HTTP action routes stay
56
+ mounted, because the UI reaches them through client hooks. Disabling `sharing`
57
+ must not 404 a share dialog that is still on screen. Weigh that parity cost
58
+ before using it: the framework's contract is that anything the UI can do the
59
+ agent can do, so `sharing: false` alongside a visible share button is a
60
+ deliberate break, and only makes sense when the app has no such surface at all.
61
+
62
+ Framework tools are no longer promoted into the first model request by default,
63
+ independent of this option. They stay in the searchable registry and load
64
+ through `tool-search`, or immediately if the app names them in
65
+ `initialToolNames`.
66
+
67
+ <Callout tone="warning">
68
+ The top-level `databaseTools` and `extensionTools` options are deprecated in
69
+ favor of `frameworkTools.database` and `frameworkTools.extensions`. They are
70
+ still honored, but setting a top-level flag and its `frameworkTools`
71
+ equivalent to conflicting values throws at plugin startup rather than booting
72
+ with a tool surface nobody chose.
73
+ </Callout>
74
+
75
+ ## Production Code Execution {#production-code-execution}
76
+
77
+ By default, production agents run without code-execution tools. They can call app actions, database tools, MCP tools, browser/session tools, and other registered framework tools, but they do not get shell or filesystem access.
78
+
79
+ Node-compatible deployments can opt into production code execution through the agent chat plugin or an environment override:
80
+
81
+ ```ts filename="server/plugins/agent-chat.ts"
82
+ export default createAgentChatPlugin({
83
+ codeExecution: { production: "sandboxed" },
84
+ });
85
+ ```
86
+
87
+ The available modes are:
88
+
89
+ - `off`. The default. No code-execution tools are registered in production.
90
+ - `sandboxed`. Registers `run-code`, an isolated Node.js JavaScript runner with a scrubbed environment, a fresh temp directory, output/time limits, and a localhost bridge to allowlisted registered tools such as `provider-api-request`, `provider-api-docs`, `provider-api-catalog`, `web-request`, and the Resources-backed workspace file bridge used by `workspaceRead` / `workspaceWrite`.
91
+ - `trusted`. Registers `run-code` plus the full coding tool registry (`bash`, `read`, `edit`, `write`). Use this only for single-tenant or operator-controlled deployments where full shell access to the host is intentional.
92
+
93
+ Set `AGENT_PROD_CODE_EXECUTION=sandboxed` or `AGENT_PROD_CODE_EXECUTION=trusted` to override the plugin option for a specific deployment without a code change. `AGENT_PROD_CODE_EXECUTION=off` forces code execution off even when the plugin option enables it.
94
+
95
+ The `run-code` sandbox is process-level isolation, not an OS container. It strips app secrets from the child process environment and uses the Node permission model when available, but outbound network is not blocked by Node itself. Authenticated calls should go through the bridge helpers the tool exposes.
96
+
97
+ ## What's next
98
+
99
+ - [**Actions: Access & Authorization**](/docs/actions-access-control) — the per-action access-control axes this page's tool-level gating complements
100
+ - [**Updating UI in Production**](/docs/updating-ui-in-production) — what a deployed agent can and can't edit with code execution left off
101
+ - [**Deployment: Environment Variables**](/docs/deployment-environment-variables) — `AGENT_PROD_CODE_EXECUTION` and other production env vars
@@ -29,7 +29,7 @@ Every action starts with a call to `defineAction()`. It bundles a description fo
29
29
  {
30
30
  lines: "12-16",
31
31
  label: "One implementation",
32
- note: "The `run` body is the single source of truth. The UI button and the agent tool both execute exactly this. `getDb()` and `schema` come from your app's `server/db/index.ts`. See [Database](/docs/database).",
32
+ note: "The `run` body is the single source of truth. The UI button and the agent tool both execute exactly this. `getDb()` and `schema` come from your app's `server/db/index.ts`. See [Database](/docs/server-database).",
33
33
  },
34
34
  ]}
35
35
  />
@@ -271,5 +271,5 @@ The repo includes an advisory helper, `node scripts/audit-template-actions.mjs [
271
271
  - [**Access & Authorization**](/docs/actions-access-control) — `agentTool`, `toolCallable`, `authorize`, and `ctx` scoping
272
272
  - [**Run Context**](/docs/actions-run-context) — the full `ctx` field table
273
273
  - [**Other Surfaces**](/docs/actions-other-surfaces) — native chat UI, CLI, A2A, MCP, and standard actions
274
- - [**Database**](/docs/database) — `getDb()`/`schema` used inside `run()`
274
+ - [**Database**](/docs/server-database) — `getDb()`/`schema` used inside `run()`
275
275
  - [**Data & Sync**](/docs/client-data) — calling this action from the UI
@@ -183,7 +183,7 @@ Configure via environment:
183
183
  ## What's next
184
184
 
185
185
  - [**Actions**](/docs/actions-overview) — the `defineAction` seam where capture happens
186
- - [**Human-in-the-Loop Approvals**](/docs/human-approval) — gated actions, recorded as `denied`
186
+ - [**Human-in-the-Loop Approvals**](/docs/actions-access-control#needs-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")
189
189
  - [**Toolkit piece: History Kit**](/docs/toolkit-history) — the version-history timeline and undo/redo UI built on this trail
@@ -456,7 +456,7 @@ function SignInCta() {
456
456
 
457
457
  Normal app pages use one impersonal, public-cacheable SSR shell for every visitor. `AppProviders` checks the session on the client; on a private path such as `/dashboard`, it redirects an anonymous visitor to `/sign-in?return=%2Fdashboard` before mounting private app UI. After sign-in, the visitor returns to `/dashboard`. Pass `isPublicPath` for public/SEO routes, or `sessionBypass` for a surface that authenticates through another scoped mechanism such as an MCP embed token.
458
458
 
459
- How long that shell is cached is a deployment-wide setting — see [SSR Caching](/docs/deployment#ssr-caching) for `AGENT_NATIVE_SSR_CACHE`. Shortening or disabling the cache changes duration only; SSR still renders the anonymous branch with cookies stripped.
459
+ How long that shell is cached is a deployment-wide setting — see [SSR Caching](/docs/ssr-caching) for `AGENT_NATIVE_SSR_CACHE`. Shortening or disabling the cache changes duration only; SSR still renders the anonymous branch with cookies stripped.
460
460
 
461
461
  ### Behind the scenes: Google OAuth
462
462
 
@@ -0,0 +1,43 @@
1
+ ---
2
+ title: "Cloudflare"
3
+ description: "Deploy an agent-native app to Cloudflare Pages or Cloudflare Workers with the matching Nitro preset."
4
+ ---
5
+
6
+ # Cloudflare
7
+
8
+ ## Cloudflare Pages {#cloudflare-pages}
9
+
10
+ ```ts filename="vite.config.ts"
11
+ export default defineConfig({
12
+ plugins: [agentNative({ nitro: { preset: "cloudflare_pages" } })],
13
+ });
14
+ ```
15
+
16
+ ## Cloudflare Workers {#cloudflare-workers}
17
+
18
+ Use Nitro's native module output for a Cloudflare Worker. This keeps SSR
19
+ enabled and emits the Worker entry point plus static assets under
20
+ `.output/server` and `.output/public`:
21
+
22
+ ```ts filename="vite.config.ts"
23
+ export default defineConfig({
24
+ plugins: [agentNative({ nitro: { preset: "cloudflare_module" } })],
25
+ });
26
+ ```
27
+
28
+ Build and deploy the generated Worker with Wrangler:
29
+
30
+ ```bash
31
+ npx @agent-native/core@latest build
32
+ npx wrangler deploy --config .output/server/wrangler.json
33
+ ```
34
+
35
+ You can also select the preset explicitly with
36
+ `NITRO_PRESET=cloudflare_module`. Configure a durable external SQL database
37
+ before deploying. A Worker filesystem is not a persistent application store.
38
+
39
+ ## What's next
40
+
41
+ - [**Deployment**](/docs/deployment) — pick a persistent database and a preset before your first production deploy
42
+ - [**Workspace Deployment**](/docs/workspace-deployment) — Cloudflare Pages is the default preset for the unified `deploy` command
43
+ - [**Node.js & Docker**](/docs/node-docker), [**Vercel**](/docs/vercel), [**Netlify**](/docs/netlify), and [**Other Platforms**](/docs/other-platforms) — the other Nitro presets
@@ -238,7 +238,7 @@ export default runMigrations(
238
238
  );
239
239
  ```
240
240
 
241
- Use the [Database](/docs/database) and [Security](/docs/security) docs before adding schemas that hold user or org data.
241
+ Use the [Database](/docs/server-database) and [Security](/docs/security) docs before adding schemas that hold user or org data.
242
242
 
243
243
  ## Define Operations As Actions {#actions}
244
244
 
@@ -0,0 +1,109 @@
1
+ ---
2
+ title: "Deploy an app"
3
+ description: "Build and deploy one Agent-Native app to Node.js, Vercel, Netlify, Cloudflare, AWS, or Deno."
4
+ search: "deploy app standalone build Nitro preset Node.js Vercel Netlify Cloudflare AWS Deno"
5
+ ---
6
+
7
+ # Deploy an app
8
+
9
+ Use this path for a standalone app, or for one app from a workspace that you
10
+ want to run on its own domain. For several apps on one origin, use [Deploy a
11
+ workspace](/docs/workspace-deployment) instead.
12
+
13
+ <Steps>
14
+
15
+ ### Create or open the app
16
+
17
+ Create a standalone Chat app, or work from an existing app:
18
+
19
+ ```bash
20
+ npx @agent-native/core@latest create my-app --standalone --template chat
21
+ cd my-app
22
+ pnpm install
23
+ ```
24
+
25
+ ### Choose a deployment target
26
+
27
+ The default target is Node.js. For another host, set the Nitro preset in
28
+ `vite.config.ts` or set `NITRO_PRESET` at build time.
29
+
30
+ ```ts filename="vite.config.ts"
31
+ import { agentNative } from "@agent-native/core/vite";
32
+ import { defineConfig } from "vite";
33
+
34
+ export default defineConfig({
35
+ plugins: [agentNative({ nitro: { preset: "vercel" } })],
36
+ });
37
+ ```
38
+
39
+ See the target page for the provider command and runtime details:
40
+
41
+ - [**Node.js & Docker**](/docs/node-docker)
42
+ - [**Vercel**](/docs/vercel)
43
+ - [**Netlify**](/docs/netlify)
44
+ - [**Cloudflare**](/docs/cloudflare)
45
+ - [**AWS Lambda and Deno Deploy**](/docs/other-platforms)
46
+
47
+ ### Build the app
48
+
49
+ Build the client and server output:
50
+
51
+ ```bash
52
+ npx @agent-native/core@latest build
53
+ ```
54
+
55
+ The default Node.js output runs with:
56
+
57
+ ```bash
58
+ node .output/server/index.mjs
59
+ ```
60
+
61
+ For a local production-shaped check with Postgres and Docker, follow the
62
+ [Node.js & Docker quickstart](/docs/node-docker#self-host-quickstart).
63
+
64
+ ### Configure the deployment
65
+
66
+ Set `DATABASE_URL` to a persistent SQL database before the first production
67
+ deploy. A standalone app using framework auth also needs a stable
68
+ `BETTER_AUTH_SECRET`. Add the provider and integration variables your app uses.
69
+
70
+ Use the [deployment environment reference](/docs/deployment-environment-variables)
71
+ for production secrets, auth, webhooks, workspace inheritance, and runtime
72
+ flags.
73
+
74
+ ### Deploy and verify
75
+
76
+ Publish the output using your provider's normal deploy command. Then verify the
77
+ public URL, sign-in flow, database reads and writes, and any OAuth callbacks or
78
+ webhooks your app uses.
79
+
80
+ </Steps>
81
+
82
+ <Callout tone="warning">
83
+
84
+ Do not use the local SQLite file as production storage. Do not reuse the sample
85
+ auth secret from a local Docker setup. Keep secrets in the deployment
86
+ provider's environment or managed secret store.
87
+
88
+ </Callout>
89
+
90
+ ## Deploying one app from a workspace
91
+
92
+ Each workspace app is independently deployable. Run the build from the app
93
+ directory:
94
+
95
+ ```bash
96
+ cd apps/<name>
97
+ npx @agent-native/core@latest build
98
+ ```
99
+
100
+ Give that app its own public origin and configure its database and auth URL for
101
+ that origin. If the apps should share one origin, shared login, and same-origin
102
+ app to app communication, use [Workspace Deployment](/docs/workspace-deployment).
103
+
104
+ ## What's next
105
+
106
+ - [**Deployment**](/docs/deployment) - the database prerequisite and target overview
107
+ - [**Workspace Deployment**](/docs/workspace-deployment) - one origin for many apps
108
+ - [**Database**](/docs/server-database#production) - production SQL adapters and connections
109
+ - [**Security**](/docs/security) - the production security checklist
@@ -0,0 +1,101 @@
1
+ ---
2
+ title: "Deployment: Environment Variables"
3
+ description: "The production environment variable reference for build, runtime, secrets, and hosting behavior."
4
+ ---
5
+
6
+ # Deployment: Environment Variables
7
+
8
+ For the repository-wide reference, including template, provider, local, and CI
9
+ variables, see [Environment Variables](/docs/environment-variables). The tables
10
+ below remain the authoritative deployment guide for production values and
11
+ hosting behavior.
12
+
13
+ ## Build / Runtime {#env-runtime}
14
+
15
+ | Variable | Description |
16
+ | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17
+ | `PORT` | Server port (Node.js only) |
18
+ | `NITRO_PRESET` | Override build preset at build time |
19
+ | `APP_BASE_PATH` | Mount the app under a prefix (e.g. `/mail`). Set automatically by `npx @agent-native/core@latest deploy`. Leave unset for standalone. |
20
+ | `APP_URL` | Optional canonical public origin. Checked ahead of `BETTER_AUTH_URL` when resolving the A2A JWT audience, the OAuth public origin, self-dispatch webhook targets, and generated resource links. Set it explicitly on preview/staging deploys where the request host is unreliable. |
21
+ | `AGENT_PROD_CODE_EXECUTION` | Optional production code-execution mode: `off` (default), `sandboxed`, or `trusted`. See [Production Code Execution](/docs/actions-agent-tools#production-code-execution). |
22
+ | `AGENT_NATIVE_SSR_CACHE` | Deployment-wide SSR shell cache policy: unset/`on` keeps the default hard-cache, `off` sends `no-store`, or a duration such as `30s` / `5m` sets a shorter freshness. See [SSR Caching](/docs/ssr-caching). |
23
+ | `AGENT_NATIVE_ENABLE_KEEP_WARM` | Opt into the Netlify keep-warm scheduled function. Unset by default. See [Keep-warm](/docs/netlify#netlify-keep-warm). |
24
+ | `AGENT_NATIVE_DISABLE_KEEP_WARM` | Compatibility kill switch for the Netlify keep-warm scheduled function. See [Keep-warm](/docs/netlify#netlify-keep-warm). |
25
+ | `AGENT_NATIVE_KEEP_WARM_SCHEDULE` | Override the keep-warm cadence with a 5-field cron expression (default `* * * * *` when enabled). An unparseable value fails the build. See [Keep-warm](/docs/netlify#netlify-keep-warm). |
26
+ | `AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND` | Drop only the background-function warm and keep the server warm. See [Keep-warm](/docs/netlify#netlify-keep-warm). |
27
+ | `AGENT_NATIVE_DISABLE_INPROCESS_SWEEPS` | Turn off the in-process backstop sweep timers (automation redispatch, agent-teams reconciliation, sandbox-execution recovery). On serverless these run per warm container, so their query rate scales with instance count. Set it **only** where a durable scheduler already drives the same recovery, or queued work will not be retried. |
28
+ | `AGENT_NATIVE_SKIP_ENSURE_TABLES` | Skip on-demand schema DDL on deployments the automatic serverless skip does not cover. Requires that your schema is already migrated. See [On-demand schema DDL](/docs/deployment#skip-ensure-tables). |
29
+ | `AGENT_NATIVE_BUILDER_RELAY_SECRET` | Dedicated 32+ char shared HMAC secret for preview-safe Builder authorization relay. Set the same value on the approved corporate callback deployment and preview deployments only when authorization must land in an isolated preview database. |
30
+ | `AGENT_NATIVE_BUILDER_RELAY_TARGET_ORIGINS` | Corporate callback-only, comma-separated allowlist of exact trusted preview origins (for example, `https://0123456789abcdef01234567--content.netlify.app`). Set it only on the corporate callback deployment. Netlify origins must use an immutable, deploy-specific 24-hex permalink. Mutable `deploy-preview-*` aliases, wildcards, and domain suffixes are rejected. |
31
+
32
+ Database connection variables (`DATABASE_URL`, `DATABASE_AUTH_TOKEN`, per-app `<APP_NAME>_DATABASE_URL`) live in [Database](/docs/server-database#production).
33
+
34
+ ## Schema setup and cold starts {#skip-ensure-tables}
35
+
36
+ On the first database touch, each data store checks whether its tables, columns,
37
+ and indexes exist, then creates only what is missing. Managed serverless
38
+ deployments skip this boot-time check in production. Other deployments can make
39
+ the same tradeoff explicit with `AGENT_NATIVE_SKIP_ENSURE_TABLES=1` after the
40
+ schema has been migrated.
41
+
42
+ Use the flag only when your deployment owns schema migrations. If a required
43
+ table is missing, the first real query fails loudly instead of repairing the
44
+ schema at runtime.
45
+
46
+ The check is batched into one introspection pass per database. This keeps the
47
+ default safe for long-lived Node servers while limiting cold-start work on
48
+ platforms that do not skip it automatically.
49
+
50
+ ## Production-sensitive configuration {#env-required-prod}
51
+
52
+ These settings cover production-sensitive behavior. A standalone app using the
53
+ framework auth needs a stable `BETTER_AUTH_SECRET` and a persistent database.
54
+ The public auth URL is inferred from the request, known template, or hosting
55
+ platform when it is unset. Workspace and A2A settings are conditional on those
56
+ features. Missing values either fail-closed or fall back to weaker behavior
57
+ with a loud warning.
58
+
59
+ | Variable | Description |
60
+ | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
61
+ | `BETTER_AUTH_SECRET` | 32+ char random string. Signs session cookies and is the fallback for `OAUTH_STATE_SECRET` and app-local encrypted values. It is also a legacy read candidate for existing shared vault ciphertext. Hard-required: the framework throws on startup if missing in production. |
62
+ | `BETTER_AUTH_URL` | Optional public origin override for this app (e.g. `https://mail.example.com`). The framework infers it from request, known-template, and hosting-platform context when unset. |
63
+ | `ANTHROPIC_API_KEY` | API key for the embedded production agent. **In multi-tenant deploys**, the framework refuses to fall back to this when the user has no per-user key. Bring-your-own-key is required. Single-tenant self-hosted installs use it as a global key. |
64
+ | `OAUTH_STATE_SECRET` | Dedicated HMAC key for OAuth state envelopes (Google, Atlassian, Zoom). Falls back to `BETTER_AUTH_SECRET` when unset, but a dedicated value is recommended so rotating one doesn't invalidate the other. Generate via `openssl rand -hex 32`. |
65
+ | `A2A_SECRET` | Shared HMAC for inter-app A2A JSON-RPC and signed background handoffs. Required for hosted workspace deploys and A2A features. Without it, those endpoints fail closed in production. When no dedicated workspace vault key is set, shared vault ciphertext uses a purpose-derived key from this value, so rotating it also rotates the vault key. |
66
+ | `WORKSPACE_SECRETS_ENCRYPTION_KEY` | Preferred stable AES-256-GCM key for the shared encrypted-at-rest secrets vault. Set the same value in every app that reads the vault. It is separate from app-local OAuth/credential encryption, so A2A trust can rotate without stranding vault rows. Existing A2A-, Better Auth-, and app-key ciphertext is migrated on successful reads. |
67
+ | `WORKSPACE_SECRETS_ENCRYPTION_KEY_PREVIOUS` | Optional previous workspace vault key used only during a rotation window. Deploy it alongside the new `WORKSPACE_SECRETS_ENCRYPTION_KEY`, let successful reads re-encrypt rows, then remove it after migration. |
68
+ | `SECRETS_ENCRYPTION_KEY` | Legacy combined key for app-local encryption and the shared vault. It remains supported, but new multi-app deployments should use `WORKSPACE_SECRETS_ENCRYPTION_KEY` for the shared vault so changing workspace encryption does not change app-local OAuth ciphertext. |
69
+
70
+ ## Auth & Identity {#env-auth}
71
+
72
+ OAuth provider credentials (Google, GitHub), static MCP bearer fallbacks (`ACCESS_TOKEN` / `ACCESS_TOKENS`), and email-verification toggles are documented in [Authentication](/docs/authentication). Set them there per the auth mode you choose.
73
+
74
+ ## Inbound Webhooks {#env-webhooks}
75
+
76
+ Each messaging integration requires its own signing secret in production (handlers fail-closed on forged requests when the secret is missing). The per-integration variables are listed in [Messaging](/docs/messaging) and [Security](/docs/security). For local development only, `AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS=1` opts back into "warn and accept". Never set it in prod.
77
+
78
+ ## Security Configuration (Opt-in) {#security-config}
79
+
80
+ Defaults are strict. A handful of opt-in flags relax behavior (debug stack traces, unverified webhooks, workspace-scoped key fallback, the MCP hub multi-org switch, runtime env-var writes). They are documented with their security trade-offs in [Security](/docs/security). Don't set them unless you specifically want the relaxed path.
81
+
82
+ ## Workspace .env Inheritance {#env-inheritance}
83
+
84
+ Inside a workspace, the root `.env` is loaded into every app automatically, so shared keys like `ANTHROPIC_API_KEY`, `A2A_SECRET`, `BETTER_AUTH_SECRET`, and `OAUTH_STATE_SECRET` only need to be set once. Per-app `apps/<name>/.env` wins on conflict.
85
+
86
+ ## Generating Strong Secrets {#env-generate-secrets}
87
+
88
+ For any secret marked "32+ char random" (`BETTER_AUTH_SECRET`, `OAUTH_STATE_SECRET`, `A2A_SECRET`, `WORKSPACE_SECRETS_ENCRYPTION_KEY`, `SECRETS_ENCRYPTION_KEY`, `AGENT_NATIVE_BUILDER_RELAY_SECRET`), generate fresh values with:
89
+
90
+ ```bash
91
+ openssl rand -hex 32
92
+ ```
93
+
94
+ Rotate signing/session keys by replacing the env var on every instance and redeploying. Sessions and OAuth state envelopes signed under the old key become invalid, so users may need to sign in again. Rotate `WORKSPACE_SECRETS_ENCRYPTION_KEY` with the `..._PREVIOUS` window described above so encrypted vault rows remain readable while they migrate.
95
+
96
+ ## What's next
97
+
98
+ - [**Deployment**](/docs/deployment) — pick a persistent database and a preset before your first production deploy
99
+ - [**Environment Variables**](/docs/environment-variables) — the repository-wide reference, including template, provider, local, and CI variables
100
+ - [**Security**](/docs/security) — the production checklist and opt-in flags referenced throughout this page
101
+ - [**Authentication**](/docs/authentication) — OAuth credentials and env vars for the auth mode you choose