@agent-native/core 0.22.4 → 0.22.7

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 (179) hide show
  1. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  2. package/dist/agent/engine/builder-engine.js +10 -9
  3. package/dist/agent/engine/builder-engine.js.map +1 -1
  4. package/dist/agent/engine/builder-gateway-headers.d.ts +10 -0
  5. package/dist/agent/engine/builder-gateway-headers.d.ts.map +1 -0
  6. package/dist/agent/engine/builder-gateway-headers.js +44 -0
  7. package/dist/agent/engine/builder-gateway-headers.js.map +1 -0
  8. package/dist/agent/engine/index.d.ts +1 -1
  9. package/dist/agent/engine/index.d.ts.map +1 -1
  10. package/dist/agent/engine/index.js +1 -1
  11. package/dist/agent/engine/index.js.map +1 -1
  12. package/dist/agent/engine/registry.d.ts +1 -0
  13. package/dist/agent/engine/registry.d.ts.map +1 -1
  14. package/dist/agent/engine/registry.js +60 -1
  15. package/dist/agent/engine/registry.js.map +1 -1
  16. package/dist/agent/engine/translate-ai-sdk.d.ts.map +1 -1
  17. package/dist/agent/engine/translate-ai-sdk.js +3 -2
  18. package/dist/agent/engine/translate-ai-sdk.js.map +1 -1
  19. package/dist/agent/engine/translate-anthropic.d.ts +36 -2
  20. package/dist/agent/engine/translate-anthropic.d.ts.map +1 -1
  21. package/dist/agent/engine/translate-anthropic.js +159 -6
  22. package/dist/agent/engine/translate-anthropic.js.map +1 -1
  23. package/dist/agent/engine/types.d.ts +4 -2
  24. package/dist/agent/engine/types.d.ts.map +1 -1
  25. package/dist/agent/engine/types.js.map +1 -1
  26. package/dist/agent/production-agent.d.ts.map +1 -1
  27. package/dist/agent/production-agent.js +69 -9
  28. package/dist/agent/production-agent.js.map +1 -1
  29. package/dist/agent/types.d.ts +2 -0
  30. package/dist/agent/types.d.ts.map +1 -1
  31. package/dist/agent/types.js.map +1 -1
  32. package/dist/cli/connect.d.ts +1 -1
  33. package/dist/cli/connect.d.ts.map +1 -1
  34. package/dist/cli/connect.js +5 -2
  35. package/dist/cli/connect.js.map +1 -1
  36. package/dist/cli/create.d.ts.map +1 -1
  37. package/dist/cli/create.js +48 -6
  38. package/dist/cli/create.js.map +1 -1
  39. package/dist/client/AssistantChat.d.ts.map +1 -1
  40. package/dist/client/AssistantChat.js +7 -1
  41. package/dist/client/AssistantChat.js.map +1 -1
  42. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  43. package/dist/client/MultiTabAssistantChat.js +4 -3
  44. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  45. package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -1
  46. package/dist/client/NewWorkspaceAppFlow.js +1 -0
  47. package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
  48. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  49. package/dist/client/agent-chat-adapter.js +11 -5
  50. package/dist/client/agent-chat-adapter.js.map +1 -1
  51. package/dist/client/api-path.d.ts.map +1 -1
  52. package/dist/client/api-path.js +2 -0
  53. package/dist/client/api-path.js.map +1 -1
  54. package/dist/client/embed-auth.d.ts +4 -0
  55. package/dist/client/embed-auth.d.ts.map +1 -0
  56. package/dist/client/embed-auth.js +102 -0
  57. package/dist/client/embed-auth.js.map +1 -0
  58. package/dist/client/index.d.ts +1 -0
  59. package/dist/client/index.d.ts.map +1 -1
  60. package/dist/client/index.js +1 -0
  61. package/dist/client/index.js.map +1 -1
  62. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  63. package/dist/client/settings/SettingsPanel.js +7 -3
  64. package/dist/client/settings/SettingsPanel.js.map +1 -1
  65. package/dist/client/use-action.d.ts.map +1 -1
  66. package/dist/client/use-action.js +2 -0
  67. package/dist/client/use-action.js.map +1 -1
  68. package/dist/client/use-chat-models.d.ts.map +1 -1
  69. package/dist/client/use-chat-models.js +4 -3
  70. package/dist/client/use-chat-models.js.map +1 -1
  71. package/dist/client/use-chat-threads.d.ts.map +1 -1
  72. package/dist/client/use-chat-threads.js +7 -7
  73. package/dist/client/use-chat-threads.js.map +1 -1
  74. package/dist/client/use-chat-threads.spec.js +70 -0
  75. package/dist/client/use-chat-threads.spec.js.map +1 -1
  76. package/dist/client/use-db-sync.d.ts.map +1 -1
  77. package/dist/client/use-db-sync.js +4 -0
  78. package/dist/client/use-db-sync.js.map +1 -1
  79. package/dist/deploy/route-discovery.js +1 -1
  80. package/dist/deploy/route-discovery.js.map +1 -1
  81. package/dist/index.d.ts +2 -0
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +2 -0
  84. package/dist/index.js.map +1 -1
  85. package/dist/mcp/build-server.d.ts.map +1 -1
  86. package/dist/mcp/build-server.js +49 -21
  87. package/dist/mcp/build-server.js.map +1 -1
  88. package/dist/mcp/builtin-tools.d.ts +1 -0
  89. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  90. package/dist/mcp/builtin-tools.js +147 -8
  91. package/dist/mcp/builtin-tools.js.map +1 -1
  92. package/dist/mcp/connect-route.d.ts.map +1 -1
  93. package/dist/mcp/connect-route.js +79 -51
  94. package/dist/mcp/connect-route.js.map +1 -1
  95. package/dist/mcp/embed-app.d.ts +14 -0
  96. package/dist/mcp/embed-app.d.ts.map +1 -0
  97. package/dist/mcp/embed-app.js +191 -0
  98. package/dist/mcp/embed-app.js.map +1 -0
  99. package/dist/mcp/index.d.ts +1 -0
  100. package/dist/mcp/index.d.ts.map +1 -1
  101. package/dist/mcp/index.js +1 -0
  102. package/dist/mcp/index.js.map +1 -1
  103. package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
  104. package/dist/scripts/agent-engines/list-agent-engines.js +2 -1
  105. package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
  106. package/dist/scripts/agent-engines/manage-agent-engine.d.ts.map +1 -1
  107. package/dist/scripts/agent-engines/manage-agent-engine.js +4 -1
  108. package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
  109. package/dist/scripts/agent-engines/set-agent-engine.d.ts.map +1 -1
  110. package/dist/scripts/agent-engines/set-agent-engine.js +4 -1
  111. package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
  112. package/dist/server/action-discovery.d.ts.map +1 -1
  113. package/dist/server/action-discovery.js +10 -1
  114. package/dist/server/action-discovery.js.map +1 -1
  115. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  116. package/dist/server/agent-chat-plugin.js +9 -1
  117. package/dist/server/agent-chat-plugin.js.map +1 -1
  118. package/dist/server/auth.d.ts +7 -6
  119. package/dist/server/auth.d.ts.map +1 -1
  120. package/dist/server/auth.js +64 -15
  121. package/dist/server/auth.js.map +1 -1
  122. package/dist/server/core-routes-plugin.d.ts +2 -0
  123. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  124. package/dist/server/core-routes-plugin.js +7 -0
  125. package/dist/server/core-routes-plugin.js.map +1 -1
  126. package/dist/server/credential-provider.d.ts +6 -4
  127. package/dist/server/credential-provider.d.ts.map +1 -1
  128. package/dist/server/credential-provider.js +91 -12
  129. package/dist/server/credential-provider.js.map +1 -1
  130. package/dist/server/embed-route.d.ts +8 -0
  131. package/dist/server/embed-route.d.ts.map +1 -0
  132. package/dist/server/embed-route.js +71 -0
  133. package/dist/server/embed-route.js.map +1 -0
  134. package/dist/server/embed-session.d.ts +65 -0
  135. package/dist/server/embed-session.d.ts.map +1 -0
  136. package/dist/server/embed-session.js +433 -0
  137. package/dist/server/embed-session.js.map +1 -0
  138. package/dist/server/index.d.ts +2 -0
  139. package/dist/server/index.d.ts.map +1 -1
  140. package/dist/server/index.js +2 -0
  141. package/dist/server/index.js.map +1 -1
  142. package/dist/server/open-route.d.ts.map +1 -1
  143. package/dist/server/open-route.js +10 -0
  144. package/dist/server/open-route.js.map +1 -1
  145. package/dist/server/security-headers.d.ts.map +1 -1
  146. package/dist/server/security-headers.js +4 -2
  147. package/dist/server/security-headers.js.map +1 -1
  148. package/dist/shared/embed-auth.d.ts +6 -0
  149. package/dist/shared/embed-auth.d.ts.map +1 -0
  150. package/dist/shared/embed-auth.js +6 -0
  151. package/dist/shared/embed-auth.js.map +1 -0
  152. package/dist/shared/index.d.ts +1 -0
  153. package/dist/shared/index.d.ts.map +1 -1
  154. package/dist/shared/index.js +1 -0
  155. package/dist/shared/index.js.map +1 -1
  156. package/dist/templates/workspace-core/AGENTS.md +14 -5
  157. package/dist/templates/workspace-root/AGENTS.md +5 -0
  158. package/dist/templates/workspace-root/README.md +3 -0
  159. package/dist/vite/action-types-plugin.d.ts.map +1 -1
  160. package/dist/vite/action-types-plugin.js +10 -1
  161. package/dist/vite/action-types-plugin.js.map +1 -1
  162. package/docs/content/a2a-protocol.md +5 -1
  163. package/docs/content/actions.md +19 -4
  164. package/docs/content/cli-adapters.md +5 -0
  165. package/docs/content/client.md +35 -1
  166. package/docs/content/database.md +29 -0
  167. package/docs/content/dispatch.md +7 -1
  168. package/docs/content/external-agents.md +37 -13
  169. package/docs/content/key-concepts.md +3 -3
  170. package/docs/content/messaging.md +1 -1
  171. package/docs/content/onboarding.md +26 -0
  172. package/docs/content/template-content.md +1 -1
  173. package/docs/content/template-dispatch.md +9 -0
  174. package/docs/content/template-starter.md +2 -2
  175. package/package.json +1 -1
  176. package/src/templates/workspace-core/AGENTS.md +14 -5
  177. package/src/templates/workspace-root/AGENTS.md +5 -0
  178. package/src/templates/workspace-root/README.md +3 -0
  179. /package/docs/content/{template-video.md → template-videos.md} +0 -0
@@ -74,6 +74,35 @@ For cases where you need raw SQL outside of Drizzle queries:
74
74
  - `isPostgres()` — runtime dialect check
75
75
  - `intType()` — returns the correct integer type for the current dialect
76
76
 
77
+ ## Migrations and Schema Updates {#migrations}
78
+
79
+ In hosted environments, multiple deployment previews, branches, and the production server share the same underlying database. Therefore, database schema updates must follow strict constraints to avoid data loss and service disruption.
80
+
81
+ ### The "Zero Destructive Changes" Rule
82
+
83
+ All database schema updates must be **strictly additive**.
84
+
85
+ - **Do not drop tables or columns.**
86
+ - **Do not rename tables or columns.** Renaming a column or table looks like a drop + create sequence to Drizzle, which will permanently delete your existing production data.
87
+ - If a column needs to be renamed or replaced, add the new column alongside the old one, update your application code to read from/write to both, migrate the data, and only retire the old column in a later release once no active deployments are referencing it.
88
+
89
+ > [!WARNING]
90
+ > **Never run `drizzle-kit push` against a production database.**
91
+ > Template database schemas only define app-specific domain tables; they do not define central framework tables (`user`, `session`, `application_state`, etc.). If you run `drizzle-kit push` against production, Drizzle will detect these framework tables as "not in schema" and attempt to drop them, causing immediate system-wide failure and data loss.
92
+
93
+ ### Safe Migration Path
94
+
95
+ Instead of pushing directly, schema changes should be applied via SQL migrations executed at application startup. Implement additive migrations within a server plugin (e.g., `server/plugins/db.ts`) by invoking the framework's `runMigrations()` helper:
96
+
97
+ ```ts
98
+ import { runMigrations } from "@agent-native/core/db";
99
+
100
+ export default defineNitroPlugin(async () => {
101
+ // Executes pending SQL migrations safely at startup
102
+ await runMigrations();
103
+ });
104
+ ```
105
+
77
106
  ## Environment Variables {#environment-variables}
78
107
 
79
108
  | Variable | Purpose |
@@ -25,7 +25,7 @@ If you're running a single template standalone, you don't need Dispatch — each
25
25
 
26
26
  ## What Dispatch does {#what-it-does}
27
27
 
28
- Five capabilities, all sitting on top of the same workspace database the other apps use.
28
+ Six capabilities, all sitting on top of the same workspace database the other apps use.
29
29
 
30
30
  ### Central inbox
31
31
 
@@ -45,6 +45,12 @@ Dispatch auto-discovers the other apps in your workspace as A2A peers — no man
45
45
 
46
46
  The behavioral rule lives in the dispatch agent's instructions: domain work belongs to the domain app. Dispatch is the orchestrator, not the specialist.
47
47
 
48
+ ### Unified MCP gateway
49
+
50
+ Dispatch can also be the single MCP URL for external agents. Connect Claude, ChatGPT, Codex, Cursor, or another MCP host to `https://dispatch.agent-native.com/_agent-native/mcp` once, then manage which apps that gateway can reach from Dispatch's **Agents** page. The gateway exposes `list_apps`, `ask_app`, and `open_app`, filtered by the selected app grants, so external agents can route work to Mail, Calendar, Analytics, Brain, and workspace apps without a separate browser authorization for every app.
51
+
52
+ Direct per-app MCP URLs still exist when you intentionally want one isolated app surface. For most workspace use, the Dispatch gateway is the lower-friction path.
53
+
48
54
  ### Workspace resources
49
55
 
50
56
  Skills, guardrail instructions, agent profiles, and reference resources can be authored once in Dispatch and inherited by the rest of the workspace. Resources with **All apps** scope are global: Dispatch stores them once at workspace scope, and every app agent reads them at runtime. They are not copied into each app, and there is no manual workspace-resource sync step. App shared resources and personal resources can override or narrow the workspace defaults locally. Selected resources use explicit per-app grants for app-specific exceptions.
@@ -14,12 +14,21 @@ The external-agent bridge closes the loop. First you connect your own agent to a
14
14
 
15
15
  Add the hosted app as a remote MCP connector in your chat host, sign in, and enable it in a chat.
16
16
 
17
- **Shortcut:** every hosted agent-native app serves a one-page connect helper at `https://<app>/_agent-native/mcp/connect` (for example [mail.agent-native.com/\_agent-native/mcp/connect](https://mail.agent-native.com/_agent-native/mcp/connect), [analytics.agent-native.com/\_agent-native/mcp/connect](https://analytics.agent-native.com/_agent-native/mcp/connect)). It shows the MCP URL with a one-click copy button and a tab strip — Claude · ChatGPT · Cursor · Claude Code · Codex · Other — each with the exact steps or copy-able command for that host. Bookmark it and share with non-developer teammates; everything below is also reachable from that page.
17
+ **Recommended for cross-app work:** connect Dispatch once:
18
+
19
+ ```text
20
+ https://dispatch.agent-native.com/_agent-native/mcp
21
+ ```
22
+
23
+ Dispatch exposes a unified MCP gateway with `list_apps`, `ask_app`, and `open_app`. In Dispatch's **Agents** page, choose whether that gateway can reach all apps or only selected apps. This avoids adding Mail, Calendar, Analytics, Brain, and every workspace app as separate MCP resources.
24
+
25
+ **Shortcut:** every hosted agent-native app serves a one-page connect helper at `https://<app>/_agent-native/mcp/connect` (for example [dispatch.agent-native.com/\_agent-native/mcp/connect](https://dispatch.agent-native.com/_agent-native/mcp/connect), [mail.agent-native.com/\_agent-native/mcp/connect](https://mail.agent-native.com/_agent-native/mcp/connect), [analytics.agent-native.com/\_agent-native/mcp/connect](https://analytics.agent-native.com/_agent-native/mcp/connect), or `https://<your-app>/_agent-native/mcp/connect`). It shows the MCP URL with a one-click copy button and a tab strip — Claude · ChatGPT · Cursor · Claude Code · Codex · Other — each with the exact steps or copy-able command for that host. Bookmark it and share with non-developer teammates; everything below is also reachable from that page.
18
26
 
19
27
  Use the hosted app's MCP URL:
20
28
 
21
29
  | App | Remote MCP URL |
22
30
  | --------- | ------------------------------------------------------ |
31
+ | Dispatch | `https://dispatch.agent-native.com/_agent-native/mcp` |
23
32
  | Mail | `https://mail.agent-native.com/_agent-native/mcp` |
24
33
  | Analytics | `https://analytics.agent-native.com/_agent-native/mcp` |
25
34
  | Any app | `https://<app-host>/_agent-native/mcp` |
@@ -61,6 +70,17 @@ ChatGPT's full MCP connector flow is currently workspace/admin gated. Use ChatGP
61
70
 
62
71
  If the ChatGPT workspace does not expose custom MCP connectors, ask a workspace admin to enable them first.
63
72
 
73
+ #### Recovering "Connector name already exists" {#chatgpt-drafts}
74
+
75
+ ChatGPT creates a **draft** the moment you click **Create app** — even if you closed the OAuth popup before approving the scopes. The draft is not visible under **Enabled apps**, but it still owns the name, so retrying with the same name surfaces a `"Connector name already exists"` toast. Recovery is fully self-service in the ChatGPT UI:
76
+
77
+ 1. Open **Settings → Apps**.
78
+ 2. Scroll past **Enabled apps** and **Advanced settings** to the **Drafts** section (labeled _"Private apps you've created in developer mode"_).
79
+ 3. Click the draft with the conflicting name.
80
+ 4. Either press **Connect** to finish OAuth in place (no rename needed), or open the **⋯** overflow menu and choose **Delete** and re-add via **Advanced settings → Create app**.
81
+
82
+ There is no "Drafts" tab on the **Add more / app directory** dialog, so non-admins sometimes miss the section entirely — it lives further down the same Settings → Apps page that lists enabled apps.
83
+
64
84
  ### Cursor {#cursor}
65
85
 
66
86
  1. Open Cursor → **Settings → MCP**.
@@ -91,13 +111,13 @@ Use this flow for local agent clients on your machine — Claude Code, Claude Co
91
111
  If you have the Agent-Native CLI installed, run:
92
112
 
93
113
  ```bash
94
- agent-native connect https://mail.agent-native.com
114
+ agent-native connect https://dispatch.agent-native.com
95
115
  ```
96
116
 
97
117
  Or run the same command through npm without installing anything globally:
98
118
 
99
119
  ```bash
100
- npx @agent-native/core connect https://mail.agent-native.com
120
+ npx @agent-native/core connect https://dispatch.agent-native.com
101
121
  ```
102
122
 
103
123
  The command asks which local agent clients should receive MCP config. All clients are preselected the first time; after you choose, the selection is saved to `~/.agent-native/connect.json` so the next run can reuse it with Enter, or you can edit the checked items.
@@ -116,7 +136,7 @@ Restart the agent client after connecting so it picks up the new MCP server; OAu
116
136
 
117
137
  Use `--client codex` (or `--client claude-code`, `--client claude-code-cli`, `--client cowork`, `--client all`) to skip the picker for scripts or one-off installs.
118
138
 
119
- Connect every first-party hosted app at once with:
139
+ When you truly need isolated per-app MCP resources, connect every first-party hosted app at once with:
120
140
 
121
141
  ```bash
122
142
  npx @agent-native/core connect --all
@@ -156,7 +176,7 @@ claude mcp add --transport http agent-native-mail \
156
176
  https://mail.agent-native.com/_agent-native/mcp
157
177
  ```
158
178
 
159
- This is the same URL-only entry that `agent-native connect https://mail.agent-native.com --client claude-code` writes for you. Then run `/mcp` in Claude Code and choose **Authenticate**. The client discovers auth from the MCP server's `401 WWW-Authenticate` challenge, fetches `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server`, dynamically registers a public OAuth client, opens the app's authorization page, and stores the resulting token securely. ChatGPT developer-mode connectors use the same server URL:
179
+ This is the same URL-only entry that `agent-native connect https://dispatch.agent-native.com --client claude-code` writes for you. Then run `/mcp` in Claude Code and choose **Authenticate**. The client discovers auth from the MCP server's `401 WWW-Authenticate` challenge, fetches `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server`, dynamically registers a public OAuth client, opens the app's authorization page, and stores the resulting token securely. ChatGPT developer-mode connectors use the same server URL:
160
180
 
161
181
  ```text
162
182
  https://mail.agent-native.com/_agent-native/mcp
@@ -199,13 +219,13 @@ Claude Code and other CLI-first clients still receive the same resources and met
199
219
 
200
220
  On top of the per-action tools the MCP server exposes a stable verb set, so an external agent has a predictable surface without guessing per-app action names:
201
221
 
202
- | Tool | Side effects | Returns |
203
- | ------------------------------------------ | ------------ | ------------------------------------------------------------------------------------ |
204
- | `list_apps` | none | workspace apps + their URLs / running state |
205
- | `open_app({ app, view, params? })` | none | a `buildDeepLink` URL (surfaces as an "Open …" link) |
206
- | `ask_app({ app, message })` | agent loop | routes a natural-language task to that app's in-app agent (delegates to `ask-agent`) |
207
- | `create_workspace_app({ name, template })` | scaffolds | a new app booted via the workspace path, plus its running URL + deep link |
208
- | `list_templates` | none | the allow-listed templates only |
222
+ | Tool | Side effects | Returns |
223
+ | -------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------- |
224
+ | `list_apps` | none | workspace apps + their URLs / running state |
225
+ | `open_app({ app, view?, path?, params?, embed? })` | none | a deep link or same-origin route; `embed: true` renders the full app inline where supported |
226
+ | `ask_app({ app, message })` | agent loop | routes a natural-language task to that app's in-app agent (delegates to `ask-agent`) |
227
+ | `create_workspace_app({ name, template })` | scaffolds | a new app booted via the workspace path, plus its running URL + deep link |
228
+ | `list_templates` | none | the allow-listed templates only |
209
229
 
210
230
  `create_workspace_app` rejects any non-allow-listed template — the public template allow-list in `packages/shared-app-config/templates.ts` is authoritative and CI-guarded; an external agent cannot widen it. A same-named template action overrides a builtin (template-over-core precedence). Disable the whole set with `MCPConfig.builtinCrossAppTools: false`.
211
231
 
@@ -281,7 +301,9 @@ export default defineAction({
281
301
 
282
302
  The MCP server advertises extension `io.modelcontextprotocol/ui`, adds `_meta.ui.resourceUri` plus `_meta["ui/resourceUri"]` to `tools/list`, and exposes the HTML through `resources/list` + `resources/read` using MIME `text/html;profile=mcp-app`. The stdio proxy forwards those resource handlers from the live app, so desktop and CLI clients see the same resources as HTTP clients.
283
303
 
284
- Keep the existing `link` builder even when adding `mcpApp`. CLI-only clients, older hosts, and any host that does not render MCP Apps will ignore the UI metadata and still need the `"Open in … →"` link. Treat `mcpApp.resource.html` like `link`: synchronous, deterministic, and self-contained; declare external origins in `csp`. Use the full app deep link for heavyweight authenticated workflows that do not fit cleanly in an embedded iframe.
304
+ Keep the existing `link` builder even when adding `mcpApp`. CLI-only clients, older hosts, and any host that does not render MCP Apps will ignore the UI metadata and still need the `"Open in … →"` link. Treat `mcpApp.resource.html` like `link`: synchronous, deterministic, and self-contained; declare external origins in `csp`.
305
+
306
+ For heavyweight authenticated workflows, reuse the real React app instead of rebuilding a plain-HTML mini UI. Core exports `embedApp()` from `@agent-native/core/mcp` and `@agent-native/core`; attach it to an action that already has a `link` builder. The embedded MCP App calls the app-only `create_embed_session` helper, exchanges a one-time SQL ticket at `/_agent-native/embed/start`, and loads the target route in an iframe with a short-lived browser session plus a bearer fallback for same-origin fetches. `open_app({ app, path, embed: true })` is the generic escape hatch for routes such as dashboards, filtered inboxes, calendar draft views, and extension pages.
285
307
 
286
308
  ### The `link` contract {#link-contract}
287
309
 
@@ -403,6 +425,8 @@ If `connect dev` cannot infer your local owner identity from an existing connect
403
425
 
404
426
  The standard OAuth path never exposes tokens to MCP Apps: the host stores OAuth access/refresh tokens and mediates tool calls and `resources/read` over the authenticated MCP connection. Embedded iframes receive app data and tool results, not bearer secrets.
405
427
 
428
+ Full-app embeds also avoid handing the MCP bearer token to the browser. The MCP caller mints a one-time embed ticket in SQL; the iframe launch route consumes it and sets a short-lived, iframe-safe browser session cookie. The landing URL carries a temporary `__an_embed_token` query param only long enough for the client to capture it, remove it from the address bar, and attach it to same-origin `fetch` calls when third-party cookies are blocked. Embed sessions are route-scoped; app fetches include the current embedded target, and the server rejects token reuse outside the minted route. Production `X-Frame-Options: DENY` stays in place for normal page loads and is omitted only when that embed session marker is present.
429
+
406
430
  The fallback hosted `connect` flow never copies the deployment's shared secret. Instead:
407
431
 
408
432
  - A logged-in browser session mints a **per-user, scoped, revocable** token — an `A2A_SECRET`-signed JWT carrying the caller's `sub` + `org_domain` and a unique `jti`, so every tool run stays tenant-scoped via `runWithRequestContext`.
@@ -84,9 +84,9 @@ Core SQL stores are auto-created and available in every template:
84
84
 
85
85
  ```ts
86
86
  // Drizzle schema for domain data
87
- import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
87
+ import { table, text, integer } from "@agent-native/core/db/schema";
88
88
 
89
- export const forms = sqliteTable("forms", {
89
+ export const forms = table("forms", {
90
90
  id: text("id").primaryKey(),
91
91
  title: text("title").notNull(),
92
92
  schema: text("schema").notNull(), // JSON
@@ -239,7 +239,7 @@ The framework supports every Drizzle-supported database. Never write SQL that on
239
239
  Use the framework helpers for dialect-agnostic SQL:
240
240
 
241
241
  ```ts
242
- import { getDbExec, isPostgres, intType } from "@agent-native/core/db/client";
242
+ import { getDbExec, isPostgres, intType } from "@agent-native/core/db";
243
243
 
244
244
  // getDbExec() auto-converts ? params to $1 for Postgres
245
245
  const client = getDbExec();
@@ -349,7 +349,7 @@ This means a multi-app workspace fronted by Dispatch is more resilient than a si
349
349
  ### Common pitfalls {#pitfalls}
350
350
 
351
351
  - **Don't double-read the request body.** h3 v2's body stream is consume-once: if you call `readBody(event)` after the framework has already parsed `event.node.req.body` (or vice versa), the second read hangs the request indefinitely. This shows up most often with Resend and SendGrid — both stream the inbound payload and the dangling read never resolves, the platform times out, and the webhook gets retried until it dedups. If you wrap the framework's webhook handler in your own middleware, pass the already-parsed `IncomingMessage` via the `incoming` option rather than letting the handler re-parse.
352
- - **Don't run agent loops inside the webhook handler.** The handler must enqueue and return — the agent loop runs in the processor's fresh execution. Putting it inline guarantees serverless freeze kills the run.
352
+ - **Don't run agent loops inside the webhook handler.** The handler must enqueue and return — the agent loop runs in the processor's fresh execution. Putting it inline guarantees serverless freeze kills the run. Furthermore, public-facing gateway integrations (such as Netlify or Vercel) enforce strict HTTP timeout limits (e.g., Netlify's 10-second request limit). Because agent runs and tools often take longer than this window, trying to run the loop synchronously within the webhook request will cause the gateway to terminate the connection, resulting in aborted execution and dropped replies. The HMAC-signed self-webhook `/process-task` queue pattern is the only way to satisfy gateway limits while executing the full agent loop safely.
353
353
  - **Don't rely on dedup memory across cold starts.** The dedup key lives in the SQL `(platform, external_event_key)` unique index, not an in-process Map. If you replace the queue, keep the SQL-level dedup or duplicate Slack retries will trigger duplicate agent runs.
354
354
  - **Keep the self-webhook URL reachable.** The processor URL is built from `APP_URL` / `URL` / `DEPLOY_URL` / `BETTER_AUTH_URL`, falling back to the inbound request headers. On preview deploys with rewritten hostnames, set one of these explicitly or the dispatch will hit a 404.
355
355
 
@@ -132,6 +132,32 @@ export default defineNitroPlugin(() => {
132
132
  });
133
133
  ```
134
134
 
135
+ ### Checking Workspace Connections in Onboarding
136
+
137
+ When building templates that interact with external services (like Slack, Google Workspace, GitHub, or HubSpot), you should check if the workspace has already connected and granted that provider connection to your application. This prevents users from having to duplicate credentials (like API keys or refresh tokens) in their local environment variables when a central, managed connection exists.
138
+
139
+ You can check connection readiness in your `isComplete` callback using the connection catalog APIs:
140
+
141
+ ```ts
142
+ import { listWorkspaceConnectionProviderCatalogForApp } from "@agent-native/core/workspace-connections";
143
+
144
+ // Inside registerOnboardingStep:
145
+ isComplete: async () => {
146
+ // Check if a managed workspace connection exists and is ready
147
+ const catalog = await listWorkspaceConnectionProviderCatalogForApp("gmail");
148
+ const connection = catalog.find((p) => p.providerId === "google-gmail");
149
+
150
+ if (connection?.status === "ready" && connection.granted) {
151
+ return true;
152
+ }
153
+
154
+ // Fall back to local environment variable check
155
+ return !!process.env.GMAIL_REFRESH_TOKEN;
156
+ };
157
+ ```
158
+
159
+ Refer to the [Workspace Connections](/docs/workspace-connections) documentation for the full list of connection provider catalog methods.
160
+
135
161
  ### Method kinds
136
162
 
137
163
  | Kind | Payload | Use for |
@@ -155,7 +155,7 @@ The four places to look when changing behavior:
155
155
  - **`app/components/editor/`** — the Tiptap editor. Add a new node type under `extensions/` and register it in `DocumentEditor.tsx`. The bubble toolbar, slash menu, and hover previews are all component files you can edit.
156
156
  - **`.agents/skills/`** — guidance the agent reads before acting. If you add a new capability (say, a CMS publishing pipeline), drop a `SKILL.md` in a new skill folder so the agent uses it correctly. Existing skills: `document-editing`, `notion-integration`, `real-time-sync`, `delegate-to-agent`, `storing-data`, `self-modifying-code`, `security`, `frontend-design`, `create-skill`, `capture-learnings`.
157
157
  - **`AGENTS.md`** — the top-level agent guide with the action cheatsheet and common-tasks table. Update it whenever you add a major feature so the agent discovers it without exploring.
158
- - **`server/db/schema.ts`** — data model. Add a column or table here, run `pnpm db:push`, and it's available to every action.
158
+ - **`server/db/schema.ts`** — data model. Add a column or table here. For local development, you can sync your database schema using `pnpm db:push`. In hosted production environments, **never** run `db:push` (doing so will attempt to drop core framework tables not defined in the template's schema); instead, apply schema updates via strictly additive migration scripts executed at startup (see [Database](/docs/database#migrations) for guidelines).
159
159
  - **`shared/notion-markdown.ts`** — markdown-to-Notion-blocks conversion. Extend this if you add new block types that need to round-trip through Notion.
160
160
 
161
161
  The agent can make all of these changes itself — ask it to "add a tags column to documents and expose it in the sidebar" and it will update the schema, migrate, wire the UI, and write the action.
@@ -83,6 +83,15 @@ When Dispatch approval policy is enabled, applying a shared or team-wide dream p
83
83
 
84
84
  Use Dreams when you want to answer questions like "what did agents keep getting wrong this week?", "what should we remember?", or "which repeated lesson deserves a skill?" Inbound Slack, email, Telegram, WhatsApp, and web-derived evidence is treated as untrusted input, so proposals from those sources require review and provenance before they affect shared memory. Workspace-instruction proposals require durable evidence spanning at least two threads or two source apps; eval-only noise, account setup issues, quota limits, and single-app UI wording corrections stay out of global instructions.
85
85
 
86
+ ### Dream Input Validation Boundaries
87
+
88
+ Because evidence is collected from external, untrusted sources (such as chat transcripts, webhooks, and third-party integrations), the Dream processor enforces strict input validation boundaries to prevent prompt injection and payload-size attacks:
89
+
90
+ - **Byte Size Limits:** Individual thread payloads are capped at a maximum of 10KB of text content per message, and candidate scans are truncated if they exceed 100KB in total to prevent context exhaustion.
91
+ - **Sanitization:** All text inputs are sanitized to strip control characters, binary payloads, and non-printable Unicode ranges.
92
+ - **Schema Validation:** Inbound debug data and thread history are parsed against strict Zod schemas before being compiled into LLM prompts. Any candidate structure that fails schema validation is immediately discarded from the processing batch.
93
+ - **Escaping:** All user-provided text chunks are dynamically escaped when formatted into the prompt templates to prevent prompt injections (e.g., trying to hijack the Dream loop to write arbitrary instructions).
94
+
86
95
  In the Dispatch UI, open **Dreams** to run a manual pass, review candidate threads, inspect the report, and open each proposal's review sheet before applying or rejecting it. Use **Settings** to edit the recurring cron schedule, source scope, timeout/concurrency limits, candidate limit, and minimum candidate threshold; use **Ensure schedule** after saving when you want the `jobs/dispatch-dream.md` recurring job materialized from those settings. The review sheet shows approval behavior, the current target content, proposed content, and source evidence. Agents use the same workflow through actions:
87
96
 
88
97
  - `list-dream-candidates` finds recent threads with grounded signals such as explicit user corrections, failed runs, tool errors, feedback, eval failures, and successful checkpointed workflows. Pass `sourceId: "all"` or `sourceIds` to scan multiple thread-debug sources; `sourceTimeoutMs`, `sourceConcurrency`, `sourceStartStaggerMs`, `threadConcurrency`, and `threadTimeoutMs` keep production scans partial and bounded, and the response includes per-source health.
@@ -10,14 +10,14 @@ Starter is the minimum viable agent-native app. You get the six-rules architectu
10
10
  <!-- screenshot:
11
11
  app: starter
12
12
  view: /
13
- shows: Blank-slate app with sidebar (Starter brand, Home / New App / Observability), centered "Blank app" card with Start building prompt button + 3 quick-action tiles (New app / Documentation / Theme), agent chat panel on the right
13
+ shows: Blank-slate app with sidebar (Blank app brand, Home / Observability), centered "Blank app" card with Start building prompt button + quick-action tiles for Documentation and Theme, agent chat panel on the right
14
14
  account: screenshot-account (no domain data needed — starter ships with no seed schema)
15
15
  capture: 1400x800 viewport, cropped 90px from bottom (final 1400x710)
16
16
  -->
17
17
 
18
18
  ![Starter scaffold with the agent sidebar and a clean blank-slate UI](/screenshots/starter.png)
19
19
 
20
- Pick Starter when you're not sure which domain template fits, or when you want to learn the framework by doing there's almost nothing to delete.
20
+ Pick Starter when you're not sure which domain template fits, or when you want to learn the framework by doing. It is scaffolding for your app, not a launcher for more apps, so starter-derived apps should be renamed and reshaped into the actual product.
21
21
 
22
22
  ## What's in it {#whats-in-it}
23
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.22.4",
3
+ "version": "0.22.7",
4
4
  "type": "module",
5
5
  "description": "Framework for agent-native application development — where AI agents and UI share state via files",
6
6
  "license": "MIT",
@@ -55,11 +55,14 @@ available to every workspace app. Only create or request per-app vault grants
55
55
  when Dispatch's vault access setting is switched to manual mode.
56
56
 
57
57
  Workspace apps are discovered from `apps/<app-name>/package.json`. There is no
58
- separate workspace app registry to edit for Dispatch to list the app. Use
59
- relative workspace links like `/<app-name>` and never hardcode `localhost`,
60
- `127.0.0.1`, `8080`, `8100`, or any dev port in app cards, instructions,
61
- redirects, or navigation; the active workspace gateway/browser origin owns the
62
- port. React Router apps must preserve `APP_BASE_PATH` / `VITE_APP_BASE_PATH` in
58
+ separate workspace app registry to edit for Dispatch to list the app. Always
59
+ save a concise, human-readable `description` there; Dispatch lists and A2A
60
+ connected-agent context use the app name plus description so agents know what
61
+ the app does. Use relative workspace links like `/<app-name>` and never
62
+ hardcode `localhost`, `127.0.0.1`, `8080`, `8100`, or any dev port in app
63
+ cards, instructions, redirects, or navigation; the active workspace
64
+ gateway/browser origin owns the port. React Router apps must preserve
65
+ `APP_BASE_PATH` / `VITE_APP_BASE_PATH` in
63
66
  `app/entry.client.tsx` via `appBasePath()` so the app hydrates correctly when
64
67
  mounted at `/<app-name>`. Use the framework/template UI stack for standard UI:
65
68
  shadcn/ui components and `@tabler/icons-react`. Do not add `lucide-react` or
@@ -80,3 +83,9 @@ workspace root. In production, Dispatch posts new-app requests to Builder
80
83
  branch creation; Builder should still scaffold the separate workspace app. The
81
84
  workspace dev gateway (`pnpm dev`) detects new `apps/<app-name>` directories
82
85
  automatically.
86
+
87
+ When using the starter template, treat it as scaffolding only. The finished app
88
+ must be branded as the requested app, with its own home screen, navigation,
89
+ package metadata, manifest, and domain workflow. Do not leave visible
90
+ `Starter`, `Blank app`, `Start building`, or `New app` UI in a starter-derived
91
+ app.
@@ -108,6 +108,11 @@ coding agents can discover the same workspace-wide guidance from the root.
108
108
  should still create the separate workspace app, not patch starter. The local
109
109
  workspace gateway detects new app directories automatically and starts each
110
110
  app server lazily on first visit.
111
+ - When using the starter template, treat it as scaffolding only. The finished
112
+ app must be branded as the requested app, with its own home screen,
113
+ navigation, package metadata, manifest, and domain workflow. Do not leave
114
+ visible `Starter`, `Blank app`, `Start building`, or `New app` UI in a
115
+ starter-derived app.
111
116
 
112
117
  ## Workspace Identity
113
118
 
@@ -106,6 +106,9 @@ pnpm exec agent-native create crm --template=starter
106
106
  The CLI detects the workspace root and scaffolds a minimal app that already
107
107
  depends on `@{{APP_NAME}}/shared`. Edit only the routes you care about;
108
108
  auth, org switching, skills, and instructions come from the shared package.
109
+ Starter is only the source scaffold: the finished app should use its own name,
110
+ home screen, navigation, package metadata, and manifest rather than leaving
111
+ starter or new-app UI in place.
109
112
  If the request starts from Dispatch in production, Dispatch sends it to Builder
110
113
  branch creation; that branch should still add a new `apps/<app-id>` workspace
111
114
  app rather than adding files to `apps/starter`.