@agent-native/core 0.149.6 → 0.151.0

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 (177) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  3. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +83 -45
  4. package/corpus/templates/analytics/changelog/2026-08-11-analytics-dashboards-keep-the-selected-tab-when-reopened.md +6 -0
  5. package/corpus/templates/assets/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  6. package/corpus/templates/brain/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  7. package/corpus/templates/calendar/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  8. package/corpus/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  9. package/corpus/templates/clips/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  10. package/corpus/templates/clips/changelog/2026-08-11-clips-recovers-from-transient-updates-and-avoids-macos-priva.md +6 -0
  11. package/corpus/templates/clips/desktop/src/app.tsx +32 -176
  12. package/corpus/templates/clips/desktop/src/lib/updater.ts +52 -37
  13. package/corpus/templates/clips/desktop/src/styles.css +1 -166
  14. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +2 -0
  15. package/corpus/templates/clips/desktop/src-tauri/Info.plist +4 -0
  16. package/corpus/templates/clips/desktop/src-tauri/src/eventkit.rs +28 -1
  17. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +2 -0
  18. package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +48 -1
  19. package/corpus/templates/clips/desktop/src-tauri/src/permission_status.rs +3 -0
  20. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +5 -0
  21. package/corpus/templates/clips/server/plugins/auth.ts +3 -0
  22. package/corpus/templates/content/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  23. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +2 -1
  24. package/corpus/templates/design/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  25. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +5 -1
  26. package/corpus/templates/design/changelog/2026-08-11-design-agent-prompts-copy-successfully-in-the-desktop-app.md +6 -0
  27. package/corpus/templates/dispatch/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  28. package/corpus/templates/dispatch/app/global.css +2 -2
  29. package/corpus/templates/dispatch/changelog/2026-08-10-desktop-workspace-sign-in.md +6 -0
  30. package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-no-longer-shows-the-generic-chat-starter-among-defa.md +6 -0
  31. package/corpus/templates/dispatch/changelog/2026-08-11-integrations-and-scheduled-work-open-as-first-class-electron-pages.md +6 -0
  32. package/corpus/templates/dispatch/changelog/2026-08-11-sidebar-text-is-slightly-easier-to-read-in-light-and-dark-mo.md +6 -0
  33. package/corpus/templates/dispatch/server/lib/identity-sso.ts +57 -31
  34. package/corpus/templates/dispatch/server/plugins/feature-flags.ts +5 -0
  35. package/corpus/templates/dispatch/server/plugins/identity-sso.ts +82 -33
  36. package/corpus/templates/dispatch/server/plugins/org-apps-directory.ts +7 -15
  37. package/corpus/templates/dispatch/server/plugins/setup-dispatch.ts +9 -1
  38. package/corpus/templates/dispatch/shared/feature-flags.ts +15 -0
  39. package/corpus/templates/factory/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  40. package/corpus/templates/forms/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  41. package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +8 -7
  42. package/corpus/templates/forms/app/hooks/use-forms.ts +8 -1
  43. package/corpus/templates/forms/changelog/2026-08-11-fixed-public-forms-so-conditional-questions-stay-hidden-unti.md +6 -0
  44. package/corpus/templates/forms/server/handlers/submissions.ts +2 -1
  45. package/corpus/templates/forms/server/lib/public-form-ssr.ts +37 -2
  46. package/corpus/templates/forms/shared/page-url.ts +54 -0
  47. package/corpus/templates/forms/shared/types.ts +4 -4
  48. package/corpus/templates/mail/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  49. package/corpus/templates/plan/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  50. package/corpus/templates/slides/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  51. package/corpus/templates/slides/actions/add-slide.ts +0 -27
  52. package/corpus/templates/slides/actions/import-file.ts +28 -9
  53. package/corpus/templates/slides/actions/view-screen.ts +4 -4
  54. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +8 -1
  55. package/corpus/templates/slides/app/components/editor/AiEditingMarker.tsx +25 -0
  56. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +43 -48
  57. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +9 -6
  58. package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -9
  59. package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +2 -0
  60. package/corpus/templates/slides/app/components/editor/ImageOverlay.tsx +0 -26
  61. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +904 -217
  62. package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +1 -0
  63. package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +11 -0
  64. package/corpus/templates/slides/app/components/editor/inline-edit-session.ts +16 -0
  65. package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +22 -6
  66. package/corpus/templates/slides/app/components/editor/style-clipboard.ts +61 -0
  67. package/corpus/templates/slides/app/components/layout/Layout.tsx +26 -2
  68. package/corpus/templates/slides/app/components/ui/context-menu.tsx +1 -0
  69. package/corpus/templates/slides/app/context/DeckContext.tsx +75 -25
  70. package/corpus/templates/slides/app/global.css +1 -1
  71. package/corpus/templates/slides/app/i18n/en-US.ts +6 -1
  72. package/corpus/templates/slides/app/lib/create-deck-generation.ts +1 -1
  73. package/corpus/templates/slides/app/lib/image-drop-to-agent.ts +12 -12
  74. package/corpus/templates/slides/app/lib/slide-agent-context.ts +52 -0
  75. package/corpus/templates/slides/app/lib/slide-image-replacement.ts +61 -0
  76. package/corpus/templates/slides/app/pages/DeckEditor.tsx +55 -116
  77. package/corpus/templates/slides/app/pages/Index.tsx +28 -3
  78. package/corpus/templates/slides/changelog/2026-08-11-agent-chat-now-follows-the-current-slide-and-selection.md +6 -0
  79. package/corpus/templates/slides/changelog/2026-08-11-deck-titles-now-use-slide-content-instead-of-placeholder-filenames.md +6 -0
  80. package/corpus/templates/slides/changelog/2026-08-11-history-now-opens-from-the-deck-overflow-menu.md +6 -0
  81. package/corpus/templates/slides/changelog/2026-08-11-images-can-be-dropped-directly-onto-slides-and-resized-or-re.md +6 -0
  82. package/corpus/templates/slides/changelog/2026-08-11-new-decks-now-transition-directly-to-a-full-page-editor-load.md +6 -0
  83. package/corpus/templates/slides/changelog/2026-08-11-selected-slide-layers-can-be-nudged-precisely-with-arrow-key.md +6 -0
  84. package/corpus/templates/slides/changelog/2026-08-11-slides-can-copy-and-paste-element-styles-with-cmd-option-c-v.md +6 -0
  85. package/corpus/templates/slides/changelog/2026-08-11-slides-shows-one-compact-ai-marker-and-keeps-your-current-sl.md +6 -0
  86. package/corpus/templates/slides/changelog/2026-08-11-text-edits-preserve-slide-layout-when-no-changes-are-made.md +6 -0
  87. package/corpus/templates/slides/changelog/2026-08-11-the-style-toolbar-now-uses-the-full-available-width.md +6 -0
  88. package/corpus/templates/slides/changelog/2026-08-11-undo-and-redo-now-move-one-visible-slide-state-at-a-time.md +6 -0
  89. package/corpus/templates/slides/server/lib/chat-attachments.ts +6 -2
  90. package/corpus/templates/slides/shared/deck-title.ts +56 -0
  91. package/corpus/templates/tasks/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  92. package/dist/agent/run-store.js +6 -1
  93. package/dist/agent/thread-data-builder.js +19 -44
  94. package/dist/agent/types.d.ts +13 -0
  95. package/dist/catalog.json +1 -0
  96. package/dist/cli/create.js +1 -1
  97. package/dist/client/AssistantChat.js +58 -10
  98. package/dist/client/FileStorageSetupCard.d.ts +6 -0
  99. package/dist/client/FileStorageSetupCard.js +18 -0
  100. package/dist/client/NewWorkspaceAppFlow.js +10 -0
  101. package/dist/client/agent-chat-adapter.js +87 -11
  102. package/dist/client/agent-chat.d.ts +1 -1
  103. package/dist/client/agent-chat.js +8 -3
  104. package/dist/client/chat/attachment-adapters.js +10 -0
  105. package/dist/client/chat/message-components.d.ts +4 -1
  106. package/dist/client/chat/message-components.js +37 -13
  107. package/dist/client/chat/tool-call-display.js +14 -0
  108. package/dist/client/chat-first/apps-rail.d.ts +1 -1
  109. package/dist/client/chat-first/apps-rail.js +6 -6
  110. package/dist/client/chat-first/copy.js +1 -0
  111. package/dist/client/chat-first/types.d.ts +1 -0
  112. package/dist/client/extensions/EmbeddedExtension.js +1 -1
  113. package/dist/client/extensions/ExtensionViewer.js +1 -1
  114. package/dist/client/extensions/InlineExtensionFrame.js +1 -1
  115. package/dist/client/onboarding/FirstRunOnboarding.js +17 -2
  116. package/dist/client/settings/FileStorageSettingsForm.d.ts +2 -0
  117. package/dist/client/settings/FileStorageSettingsForm.js +113 -0
  118. package/dist/client/settings/SettingsPanel.js +2 -1
  119. package/dist/client/sse-event-processor.js +21 -0
  120. package/dist/collab/awareness.d.ts +2 -2
  121. package/dist/collab/struct-routes.d.ts +1 -1
  122. package/dist/extensions/actions.js +2 -2
  123. package/dist/extensions/html-shell.js +3 -1
  124. package/dist/file-upload/pre-upload-attachments.d.ts +15 -10
  125. package/dist/file-upload/pre-upload-attachments.js +68 -24
  126. package/dist/file-upload/registry.d.ts +4 -5
  127. package/dist/file-upload/registry.js +7 -14
  128. package/dist/file-upload/s3.d.ts +9 -0
  129. package/dist/file-upload/s3.js +168 -0
  130. package/dist/file-upload/types.d.ts +3 -3
  131. package/dist/file-upload/types.js +2 -2
  132. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  133. package/dist/notifications/routes.d.ts +6 -6
  134. package/dist/observability/routes.d.ts +3 -3
  135. package/dist/onboarding/app-profile.js +23 -1
  136. package/dist/onboarding/default-steps.js +69 -0
  137. package/dist/onboarding/types.d.ts +2 -0
  138. package/dist/progress/routes.d.ts +1 -1
  139. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  140. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  141. package/dist/secrets/routes.d.ts +3 -3
  142. package/dist/server/agent-chat/browser-team-tools.js +20 -0
  143. package/dist/server/agent-chat-plugin.js +12 -3
  144. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  145. package/dist/server/auth.js +45 -21
  146. package/dist/server/better-auth-instance.d.ts +2 -0
  147. package/dist/server/better-auth-instance.js +2 -2
  148. package/dist/server/core-routes-plugin.d.ts +1 -0
  149. package/dist/server/core-routes-plugin.js +26 -16
  150. package/dist/server/identity-sso-store.d.ts +3 -0
  151. package/dist/server/identity-sso-store.js +45 -0
  152. package/dist/server/identity-sso.d.ts +20 -10
  153. package/dist/server/identity-sso.js +79 -31
  154. package/dist/server/index.d.ts +1 -1
  155. package/dist/server/index.js +1 -1
  156. package/dist/server/realtime-token.d.ts +1 -1
  157. package/dist/server/transcribe-voice.d.ts +1 -1
  158. package/dist/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  159. package/dist/templates/default/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  160. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +24 -1
  161. package/dist/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  162. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -2
  163. package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +1 -1
  164. package/dist/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +3 -1
  165. package/docs/content/authentication.mdx +27 -0
  166. package/docs/content/cross-app-sso.mdx +38 -8
  167. package/docs/content/extensions.mdx +15 -15
  168. package/docs/content/file-uploads.mdx +10 -12
  169. package/docs/content/generative-ui.mdx +6 -1
  170. package/package.json +2 -2
  171. package/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  172. package/src/templates/default/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  173. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +24 -1
  174. package/src/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  175. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -2
  176. package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +1 -1
  177. package/src/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +3 -1
@@ -35,6 +35,11 @@ cookie**. Each separately hosted app still creates and owns its own local
35
35
  session. Dispatch proves the user's identity at the app's front door, and the
36
36
  app then continues with its normal local auth and access checks.
37
37
 
38
+ The packaged Desktop Canary is deliberately more explicit: it never intercepts
39
+ anonymous app sign-in navigation. The user chooses workspace sign-in in Desktop
40
+ Settings for the active registered app; ordinary app sign-in remains the
41
+ default path when that action is not chosen or the flag is unavailable.
42
+
38
43
  For a product walkthrough, record this exact sequence: sign in once in
39
44
  Dispatch, open two other hosted apps, show that neither asks for credentials,
40
45
  then sign out of one app and show that the other app's session is independent.
@@ -140,7 +145,7 @@ The flow is a standard authorize → signed-token → callback redirect, with em
140
145
  "in": "query",
141
146
  "type": "string",
142
147
  "required": true,
143
- "description": "App callback URL. Validated against a strict allowlist (`*.agent-native.com` or localhost by default)."
148
+ "description": "App callback URL. Canonical apps must match an exact registered app ID, origin, and callback path; localhost is allowed for development."
144
149
  },
145
150
  {
146
151
  "name": "state",
@@ -156,7 +161,7 @@ The flow is a standard authorize → signed-token → callback redirect, with em
156
161
  },
157
162
  {
158
163
  "status": "400",
159
- "description": "`redirect_uri` failed allowlist validation (cross-origin, scheme-relative `//host`, or unlisted suffix)."
164
+ "description": "`app` and `redirect_uri` failed allowlist validation (mismatched app, cross-origin, scheme-relative `//host`, or unlisted host)."
160
165
  }
161
166
  ]}>
162
167
 
@@ -183,7 +188,7 @@ The flow is a standard authorize → signed-token → callback redirect, with em
183
188
 
184
189
  ### Opting in {#opt-in}
185
190
 
186
- An app participates **only** when this environment variable is set on its deployment:
191
+ An app's ordinary web sign-in participates **only** when this environment variable is set on its deployment:
187
192
 
188
193
  ```bash
189
194
  AGENT_NATIVE_IDENTITY_HUB_URL=https://dispatch.agent-native.com
@@ -197,10 +202,10 @@ AGENT_NATIVE_IDENTITY_HUB_URL=https://dispatch.agent-native.com
197
202
  The whole model rests on a few deliberately small guarantees:
198
203
 
199
204
  - **Short-lived signed token.** The identity assertion is an `A2A_SECRET`-signed JWT with a **≤ 2-minute** expiry and `scope: "identity"`. It authorizes a single sign-in and cannot be replayed for long or repurposed for API/A2A access.
200
- - **Strict `redirect_uri` allowlist.** Dispatch only ever redirects to `*.agent-native.com` or localhost by default. Arbitrary, scheme-relative (`//host`), and cross-origin redirect targets are rejected, so the authority can't be turned into an open-redirect or token-exfiltration oracle.
205
+ - **Strict app-bound `redirect_uri` allowlist.** Every canonical app ID maps to one exact production origin and callback path; localhost remains available for development. Unknown subdomains, mismatched app IDs, arbitrary paths, scheme-relative (`//host`), and cross-origin targets are rejected, so the authority can't become a token-exfiltration oracle.
201
206
  - **Email-only join from a verified token.** The _only_ thing that crosses the trust boundary is the verified email in a signed token. The app does not accept a user id, role, org membership, or any privileged state from the wire — it derives everything locally from the matched account.
202
207
  - **Additive-only identity writes.** Linking either reuses an existing same-email account untouched or inserts a new one. No update, rename, repoint, or delete of identity rows ever happens on this path.
203
- - **Off by default.** With `AGENT_NATIVE_IDENTITY_HUB_URL` unset the entire feature is inert.
208
+ - **Off by default.** With `AGENT_NATIVE_IDENTITY_HUB_URL` unset, ordinary browser federation is inert. The separately gated packaged Desktop path is described below.
204
209
 
205
210
  <Callout id="doc-block-18qhl3m" tone="success">
206
211
 
@@ -265,6 +270,31 @@ The just-in-time link is a single decision keyed entirely on the verified email:
265
270
 
266
271
  </Diagram>
267
272
 
273
+ ## Packaged Desktop workspace {#desktop-workspace}
274
+
275
+ The signed Agent Native Desktop SSO Canary uses this same federation flow as the
276
+ ordinary sign-in front door for canonical first-party apps. A dedicated
277
+ Electron partition holds the Dispatch authority session; each target app still
278
+ mints its own local session, and Desktop transfers only that app's allowlisted
279
+ session cookie into its isolated `persist:app-<id>` partition. Custom,
280
+ user-added, and third-party apps are excluded.
281
+
282
+ Dispatch registers the default-off `desktop.workspace-sso` rollout flag. The
283
+ flag controls whether Desktop may begin the ceremony; it does not authorize a
284
+ user or weaken nonce, signature, origin, callback, app-binding, cookie,
285
+ revocation, or credential-custody checks. With the flag Off or unavailable,
286
+ Desktop leaves ordinary per-app sign-in, sign-out, and Settings unchanged.
287
+ Canonical hosted apps recognize the packaged Desktop request without requiring
288
+ `AGENT_NATIVE_IDENTITY_HUB_URL` on every deployment; ordinary web browsers and
289
+ self-hosted apps still require that explicit configuration.
290
+ Stable Desktop builds do not initialize the broker until a later release is
291
+ separately approved.
292
+ The Canary user-agent marker is an availability hint, not remote attestation;
293
+ the server cannot infer code signature from HTTP. No authentication or
294
+ authorization decision trusts it. Supervised acceptance therefore binds the
295
+ tested client through the signed artifact's exact commit, manifest, hashes, and
296
+ notarization evidence.
297
+
268
298
  ## Self-hosting {#self-hosting}
269
299
 
270
300
  Any Dispatch deployment can serve as the identity hub — you are not limited to `dispatch.agent-native.com`. Set `AGENT_NATIVE_IDENTITY_HUB_URL` on each client app to point at your Dispatch instance:
@@ -275,7 +305,7 @@ AGENT_NATIVE_IDENTITY_HUB_URL=https://dispatch.yourcompany.com
275
305
 
276
306
  **Redirect allowlist.** The hub (Dispatch) validates `redirect_uri` on the authorize endpoint before issuing a token. The allowlist is configured in `templates/dispatch/server/lib/identity-sso.ts`:
277
307
 
278
- - **Default:** `*.agent-native.com` and localhost only (the `DEFAULT_ALLOWED_HOST_SUFFIXES` constant).
308
+ - **Default:** exact registered Agent Native app ID/origin pairs and localhost only (`CANONICAL_IDENTITY_SSO_APP_ORIGINS`).
279
309
  - **Extending it:** set the `IDENTITY_SSO_ALLOWED_HOST_SUFFIXES` environment variable on the Dispatch deployment with a comma-separated list of additional host suffixes:
280
310
 
281
311
  ```bash
@@ -283,11 +313,11 @@ AGENT_NATIVE_IDENTITY_HUB_URL=https://dispatch.yourcompany.com
283
313
  IDENTITY_SSO_ALLOWED_HOST_SUFFIXES=".yourcompany.com,.staging.yourcompany.com"
284
314
  ```
285
315
 
286
- Each entry is normalized to a dot-prefixed suffix (`.yourcompany.com`), so a suffix check is both sufficient and the least footgun-prone — no per-app list to keep in sync. Entries that would match everything (empty or just `.`) are filtered out.
316
+ Each entry is normalized to a dot-prefixed suffix (`.yourcompany.com`). Entries that would match everything (empty or just `.`) are filtered out. The request must still provide a valid app ID and the exact `/_agent-native/identity/callback` path.
287
317
 
288
318
  - **Localhost** is always allowed for local development of client-side apps regardless of `IDENTITY_SSO_ALLOWED_HOST_SUFFIXES`.
289
319
 
290
- Without `IDENTITY_SSO_ALLOWED_HOST_SUFFIXES`, a self-hosted Dispatch can only issue tokens to apps on `*.agent-native.com`. Set the env var on your Dispatch deployment to unlock other domains.
320
+ Without `IDENTITY_SSO_ALLOWED_HOST_SUFFIXES`, a self-hosted Dispatch can only issue tokens to the exact registered Agent Native app origins. Set the env var on your Dispatch deployment to unlock other domains.
291
321
 
292
322
  ## Canary rollout runbook {#canary-rollout}
293
323
 
@@ -268,27 +268,27 @@ Everything from here through [Local File Extensions](#local-file-extensions) is
268
268
 
269
269
  Inside the iframe sandbox, every extension has these helpers on `window`:
270
270
 
271
- | Helper | Purpose | Example |
272
- | ------------------------------------------------ | --------------------------------------------------------- | --------------------------------------------------------- |
273
- | `appAction(name, params)` | Call any of the host template's actions, including writes | `appAction('list-emails', { view: 'inbox' })` |
274
- | `appFetch(path, options)` | Call allowed framework endpoints under `/_agent-native/*` | `appFetch('/_agent-native/application-state/navigation')` |
275
- | `dbQuery(sql, args)` | Read from SQL (auto-scoped to the user; never for writes) | `dbQuery('SELECT id, name FROM tools')` |
276
- | `dbExec(sql, args)` | Legacy trusted SQL writes for editor/admin extensions | Prefer `appAction` for app data writes |
277
- | `extensionFetch(url, options)` | Hit external APIs through a secure proxy with secrets | `extensionFetch('https://api.github.com/user')` |
278
- | `extensionData.set(collection, id, data, opts?)` | Persist data per-extension (user / org scoping) | `extensionData.set('notes', id, { text: '...' })` |
279
- | `extensionData.list(collection, opts?)` | List persisted items | `extensionData.list('notes', { scope: 'all' })` |
280
- | `extensionData.get(collection, id, opts?)` | Get a single item | `extensionData.get('notes', 'note-1')` |
281
- | `extensionData.remove(collection, id, opts?)` | Delete a persisted item | `extensionData.remove('notes', 'note-1')` |
282
- | `window.slotContext` | Read the page/chat context passed into this slot | `window.slotContext?.contactEmail` |
283
- | `agentNative.ui.output(value, opts?)` | Record passive inline UI output in application state | `agentNative.ui.output({ threshold })` |
284
- | `agentNative.chat.send(message, opts?)` | Send a prompt or selected value back to the agent chat | `agentNative.chat.send('Use Q2', { context: { q: 2 } })` |
271
+ | Helper | Purpose | Example |
272
+ | ------------------------------------------------ | --------------------------------------------------------- | ---------------------------------------------------------------------- |
273
+ | `appAction(name, params)` | Call any of the host template's actions, including writes | `appAction('list-emails', { view: 'inbox' })` |
274
+ | `appFetch(path, options)` | Call allowed framework endpoints under `/_agent-native/*` | `appFetch('/_agent-native/application-state/navigation')` |
275
+ | `dbQuery(sql, args)` | Read from SQL (auto-scoped to the user; never for writes) | `dbQuery('SELECT id, name FROM tools')` |
276
+ | `dbExec(sql, args)` | Legacy trusted SQL writes for editor/admin extensions | Prefer `appAction` for app data writes |
277
+ | `extensionFetch(url, options)` | Hit external APIs through a secure proxy with secrets | `extensionFetch('https://api.github.com/user')` |
278
+ | `extensionData.set(collection, id, data, opts?)` | Persist data per-extension (user / org scoping) | `extensionData.set('notes', id, { text: '...' })` |
279
+ | `extensionData.list(collection, opts?)` | List persisted items | `extensionData.list('notes', { scope: 'all' })` |
280
+ | `extensionData.get(collection, id, opts?)` | Get a single item | `extensionData.get('notes', 'note-1')` |
281
+ | `extensionData.remove(collection, id, opts?)` | Delete a persisted item | `extensionData.remove('notes', 'note-1')` |
282
+ | `window.slotContext` | Read the page/chat context passed into this slot | `window.slotContext?.contactEmail` |
283
+ | `agentNative.ui.output(value, opts?)` | Record passive inline UI output in application state | `agentNative.ui.output({ threshold })` |
284
+ | `agentNative.chat.send(message, opts?)` | Send a prompt or selected value back to the agent chat | `agentNative.chat.send('Use Q2', { submit: true, context: { q: 2 } })` |
285
285
 
286
286
  Three rules of thumb:
287
287
 
288
288
  - **Prefer `appAction` over raw SQL.** Actions are the template's official surface — they handle access control, scoping, and validation for you. `dbQuery` is read-only; `dbExec` remains for legacy trusted editor/admin extensions, but new app data writes should go through actions.
289
289
  - **Use `appAction` for template data.** Extension `appFetch` is limited to framework `/_agent-native/*` endpoints; template `/api/*` routes are blocked by the iframe bridge.
290
290
  - **Prefer `extensionData` over making new tables.** Each extension gets its own isolated key-value store. No schema, no migration. Set `{ scope: 'org' }` to share with the user's org, `'user'` (default) for private.
291
- - **Use `agentNative.ui.output` for inline control values the agent should read later.** It writes `inline-ui:<extensionId>:output` to application state; `agentNative.chat.send` is for visible submit/apply messages.
291
+ - **Use `agentNative.ui.output` for inline control values the agent should read later.** It writes `inline-ui:<extensionId>:output` to application state; `agentNative.chat.send` is draft-only unless `{ submit: true }` is passed for a user-triggered submit/apply message. Never call it from polling, refresh, or error handlers.
292
292
 
293
293
  ```html
294
294
  <script>
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: "File Uploads"
3
- description: "Configure file upload storage — SQL fallback for dev, Builder.io or custom providers for production."
3
+ description: "Configure durable file upload storage — Builder.io or custom object storage for production."
4
4
  ---
5
5
 
6
6
  # File Uploads
@@ -15,9 +15,9 @@ The provider resolution order is:
15
15
 
16
16
  1. **User-registered providers** — custom providers registered via `registerFileUploadProvider()`
17
17
  2. **Builder.io provider** — built-in, activates automatically when Builder.io is connected
18
- 3. **SQL fallback** — stores files as base64 in the database (fine for dev, not for production)
18
+ 3. **No provider** — the upload is rejected and the chat renders a storage setup CTA; binary data is never persisted in SQL
19
19
 
20
- <Diagram id="doc-block-hq5uvx" title="Provider resolution order" summary="uploadFile() picks the first configured provider in order. The SQL fallback always exists so uploads work with zero setup.">
20
+ <Diagram id="doc-block-hq5uvx" title="Provider resolution order" summary="uploadFile() picks the first configured provider in order. Without one, the upload fails closed so binary data is never persisted in SQL.">
21
21
 
22
22
  ```html
23
23
  <div class="diagram-upload">
@@ -47,8 +47,8 @@ The provider resolution order is:
47
47
  <div class="diagram-step">
48
48
  <span class="diagram-pill warn">3</span>
49
49
  <div class="diagram-node">
50
- SQL fallback<br /><small class="diagram-muted"
51
- >base64 in DB — dev only</small
50
+ No provider<br /><small class="diagram-muted"
51
+ >setup CTA — no binary persistence</small
52
52
  >
53
53
  </div>
54
54
  </div>
@@ -94,15 +94,13 @@ Dispatches to the first configured provider in resolution order. Check the activ
94
94
 
95
95
  </Endpoint>
96
96
 
97
- ## Default: SQL Fallback {#sql-fallback}
97
+ ## Storage required for durable uploads {#storage-required}
98
98
 
99
- When no provider is configured, files are stored as base64 data in the SQL database via the resources system. This works out of the box for local development but is not recommended for production — large files bloat the database and there's no CDN.
100
-
101
- A one-time warning is logged when the fallback is used.
99
+ When no provider is configured, `uploadFile()` returns `null` and the multipart route returns `503`. Chat attachments remain available only for the current request, the agent receives a storage setup hint, and the transcript stores a setup marker instead of the binary payload. This fail-closed behavior keeps SQL small and makes the missing configuration visible.
102
100
 
103
101
  ## Builder.io Hosting {#builder-hosting}
104
102
 
105
- When your app is connected to Builder.io, file uploads are automatically routed to Builder's asset hosting. Builder.io's free tier includes this managed storage. Files are served from a CDN with no configuration needed. This is the recommended production setup.
103
+ When your app is connected to Builder.io, file uploads are automatically routed to Builder's asset hosting. Builder.io's free tier includes this managed storage. Files are served from a CDN with no configuration needed, and the durable URL is stored in the chat thread so later turns can reuse it. This is the recommended production setup.
106
104
 
107
105
  ## Custom Providers {#custom-providers}
108
106
 
@@ -159,7 +157,7 @@ interface FileUploadInput {
159
157
  data: Uint8Array | Buffer; // File contents
160
158
  filename?: string; // Original filename
161
159
  mimeType?: string; // MIME type, e.g. "image/png"
162
- ownerEmail?: string; // For per-user scoping in fallback
160
+ ownerEmail?: string; // For request-scoped provider credentials
163
161
  }
164
162
 
165
163
  interface FileUploadResult {
@@ -183,7 +181,7 @@ const result = await uploadFile({
183
181
  if (result) {
184
182
  // Provider handled it — result.url is the public URL
185
183
  } else {
186
- // No provider configured — handle SQL fallback yourself, or skip
184
+ // No provider configured — show the storage setup CTA, or skip
187
185
  }
188
186
  ```
189
187
 
@@ -222,12 +222,17 @@ Generated UI can send a visible prompt or result back into the current app chat:
222
222
  `agentNative.chat.send(message, options)` is also available as
223
223
  `sendToAgentChat(message, options)`.
224
224
 
225
+ For generated extensions, chat messages are draft-only unless `submit: true` is
226
+ passed explicitly. Use that opt-in only from a user-triggered Apply or Submit
227
+ action. Do not call either helper from polling, refresh, or error handlers; use
228
+ `agentNative.ui.output` or application state for passive results.
229
+
225
230
  Options:
226
231
 
227
232
  | Option | Default | Effect |
228
233
  | ------------- | ------- | ------------------------------------------------ |
229
234
  | `context` | none | hidden context appended to the submitted message |
230
- | `submit` | `true` | `false` pre-fills/reviews instead of auto-submit |
235
+ | `submit` | `false` | `true` auto-submits; `false` pre-fills/reviews |
231
236
  | `openSidebar` | `true` | opens the agent sidebar when the message is sent |
232
237
 
233
238
  This is the same app chat bridge used by normal UI components, so generated
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.149.6",
3
+ "version": "0.151.0",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -414,7 +414,7 @@
414
414
  "yjs": "^13.6.31",
415
415
  "zod": "^4.3.6",
416
416
  "@agent-native/recap-cli": "0.5.4",
417
- "@agent-native/toolkit": "^0.13.9"
417
+ "@agent-native/toolkit": "^0.13.10"
418
418
  },
419
419
  "devDependencies": {
420
420
  "@ai-sdk/anthropic": "^3.0.71",
@@ -49,6 +49,34 @@ sendToAgentChat({
49
49
  });
50
50
  ```
51
51
 
52
+ ### Keep user text separate from injected context
53
+
54
+ Treat the visible `message` as the user's request or the shortest clear
55
+ description of an app-initiated operation. Put context the user did not type —
56
+ IDs, URLs, filenames, current selection or screen state, serialized records,
57
+ upload instructions, and bounded source excerpts — in the `context` field.
58
+ `context` is model input carried through the agent turn; it is stripped from
59
+ the rendered user message, so do not concatenate it into `message` with labels,
60
+ blank-line sections, or `<context>` tags yourself.
61
+
62
+ Use the related surface for each kind of supporting input:
63
+
64
+ | Surface | Use for |
65
+ | --- | --- |
66
+ | `message` | User-authored intent or a concise app action description |
67
+ | `context` | Derived metadata and bounded text needed to carry out that intent |
68
+ | `setAgentChatContextItem` | Context staged for a later user-submitted prompt; keep it keyed so updates replace stale context |
69
+ | `images`, `referenceImagePaths`, attachments | Binary or visual inputs; describe only the handling instructions in `context` |
70
+
71
+ If an app builds a prompt from a form, selection, upload, or editor state, keep
72
+ the visible message short and pass the assembled details as `context`. Preserve
73
+ the user's actual freeform text in `message` when it is the request; do not
74
+ restate it as metadata in `context` unless the agent needs a structured copy.
75
+
76
+ This boundary applies to auto-submitted turns and prefills. A hidden context
77
+ field is not a license to send unbounded records or secrets: cap excerpts,
78
+ prefer stable IDs and URLs, and use an action or resource lookup for full data.
79
+
52
80
  **From the UI, in the background:**
53
81
 
54
82
  ```ts
@@ -49,6 +49,34 @@ sendToAgentChat({
49
49
  });
50
50
  ```
51
51
 
52
+ ### Keep user text separate from injected context
53
+
54
+ Treat the visible `message` as the user's request or the shortest clear
55
+ description of an app-initiated operation. Put context the user did not type —
56
+ IDs, URLs, filenames, current selection or screen state, serialized records,
57
+ upload instructions, and bounded source excerpts — in the `context` field.
58
+ `context` is model input carried through the agent turn; it is stripped from
59
+ the rendered user message, so do not concatenate it into `message` with labels,
60
+ blank-line sections, or `<context>` tags yourself.
61
+
62
+ Use the related surface for each kind of supporting input:
63
+
64
+ | Surface | Use for |
65
+ | --- | --- |
66
+ | `message` | User-authored intent or a concise app action description |
67
+ | `context` | Derived metadata and bounded text needed to carry out that intent |
68
+ | `setAgentChatContextItem` | Context staged for a later user-submitted prompt; keep it keyed so updates replace stale context |
69
+ | `images`, `referenceImagePaths`, attachments | Binary or visual inputs; describe only the handling instructions in `context` |
70
+
71
+ If an app builds a prompt from a form, selection, upload, or editor state, keep
72
+ the visible message short and pass the assembled details as `context`. Preserve
73
+ the user's actual freeform text in `message` when it is the request; do not
74
+ restate it as metadata in `context` unless the agent needs a structured copy.
75
+
76
+ This boundary applies to auto-submitted turns and prefills. A hidden context
77
+ field is not a license to send unbounded records or secrets: cap excerpts,
78
+ prefer stable IDs and URLs, and use an action or resource lookup for full data.
79
+
52
80
  **From the UI, in the background:**
53
81
 
54
82
  ```ts
@@ -168,10 +168,33 @@ Set `A2A_SECRET` (same value) on all apps that must verify each other's identity
168
168
  Each hosted `*.agent-native.com` app has its **own user store**, so "sign in once" is identity federation, not a shared cookie. **Dispatch is the identity authority.**
169
169
 
170
170
  - **Opt-in per app via one env var:** set `AGENT_NATIVE_IDENTITY_HUB_URL=https://dispatch.agent-native.com` and the app shows a "Sign in with Agent-Native" option. **Unset = zero behavior change** — the whole path is dormant. Reversible at any time.
171
- - **Flow:** app → `GET <hub>/_agent-native/identity/authorize?app=&redirect_uri=&state=` → user logs in at Dispatch → 302 back with a short-lived (`≤5min`) `A2A_SECRET`-signed identity JWT (`sub`/`email`/`name`/`org_domain`/`scope:"identity"`). Strict `redirect_uri` allowlist (`*.agent-native.com` + localhost). App verifies the token, **JIT-links strictly by verified email** (existing same-email user → reused unchanged; new email → created), then mints a normal local session.
171
+ - **Flow:** app → `GET <hub>/_agent-native/identity/authorize?app=&redirect_uri=&state=` → user logs in at Dispatch → 302 back with a short-lived (`≤5min`) `A2A_SECRET`-signed identity JWT (`sub`/`email`/`name`/`org_domain`/`scope:"identity"`). Canonical clients require an exact registered app ID, origin, and callback path; localhost remains available for development. App verifies the token, **JIT-links strictly by verified email** (existing same-email user → reused unchanged; new email → created), then mints a normal local session.
172
172
  - **Invariant (do not break):** identity rows are only ever **added** — never modified, renamed, or deleted. Enabling SSO logs users out, but they always log back into the **same email-matched account with data intact**. Email is the only thing that crosses the trust boundary; the app never trusts a user id, role, or org from the wire.
173
173
  - **Canary rollout:** deploy with the env unset everywhere (no-op) → set it on **one** app (mail) only → verify (logout → SSO → Dispatch → back to the same pre-existing account, data intact, direct logins still work) → expand app-by-app → rollback = unset the env on that app's deploy (instant, no data change).
174
174
 
175
+ ### Packaged Desktop workspace sign-in
176
+
177
+ For canonical first-party hosted apps, the packaged Desktop SSO Canary may compose the same
178
+ federation into one workspace sign-in. Dispatch owns the default-off
179
+ `desktop.workspace-sso` availability flag; every app still owns its local
180
+ session. The flag is never an auth or authorization boundary. Keep nonce,
181
+ signature, exact origin/callback, authenticated-session, app-binding, cookie
182
+ allowlist, revocation, and credential-custody checks unconditional. When the
183
+ flag is Off or unreadable, Desktop must leave ordinary per-app sign-in, sign-out,
184
+ and Settings unchanged. Never extend the broker to custom or third-party apps,
185
+ and never expose cookies, identity tokens, or provider credentials through IPC.
186
+ Canonical hosted apps recognize packaged Desktop requests without per-app
187
+ identity-hub environment configuration. Ordinary browsers and self-hosted apps
188
+ still require `AGENT_NATIVE_IDENTITY_HUB_URL`.
189
+ Stable Desktop builds must not initialize the broker until a later release is
190
+ separately approved.
191
+ Treat the Canary user-agent marker only as an availability hint, never as
192
+ remote attestation or an authentication boundary. Bind supervised acceptance
193
+ to exact signed-artifact provenance.
194
+ The Canary must not intercept anonymous app sign-in navigation. The user
195
+ explicitly chooses workspace sign-in in Desktop Settings for the active
196
+ registered app; ordinary app sign-in remains the default path.
197
+
175
198
  Full runbook + flow detail: [Cross-App SSO doc](/docs/cross-app-sso).
176
199
 
177
200
  ## Builder Browser Access
@@ -49,6 +49,34 @@ sendToAgentChat({
49
49
  });
50
50
  ```
51
51
 
52
+ ### Keep user text separate from injected context
53
+
54
+ Treat the visible `message` as the user's request or the shortest clear
55
+ description of an app-initiated operation. Put context the user did not type —
56
+ IDs, URLs, filenames, current selection or screen state, serialized records,
57
+ upload instructions, and bounded source excerpts — in the `context` field.
58
+ `context` is model input carried through the agent turn; it is stripped from
59
+ the rendered user message, so do not concatenate it into `message` with labels,
60
+ blank-line sections, or `<context>` tags yourself.
61
+
62
+ Use the related surface for each kind of supporting input:
63
+
64
+ | Surface | Use for |
65
+ | --- | --- |
66
+ | `message` | User-authored intent or a concise app action description |
67
+ | `context` | Derived metadata and bounded text needed to carry out that intent |
68
+ | `setAgentChatContextItem` | Context staged for a later user-submitted prompt; keep it keyed so updates replace stale context |
69
+ | `images`, `referenceImagePaths`, attachments | Binary or visual inputs; describe only the handling instructions in `context` |
70
+
71
+ If an app builds a prompt from a form, selection, upload, or editor state, keep
72
+ the visible message short and pass the assembled details as `context`. Preserve
73
+ the user's actual freeform text in `message` when it is the request; do not
74
+ restate it as metadata in `context` unless the agent needs a structured copy.
75
+
76
+ This boundary applies to auto-submitted turns and prefills. A hidden context
77
+ field is not a license to send unbounded records or secrets: cap excerpts,
78
+ prefer stable IDs and URLs, and use an action or resource lookup for full data.
79
+
52
80
  **From the UI, in the background:**
53
81
 
54
82
  ```ts
@@ -129,8 +129,12 @@ Extensions have full access to app data via helpers injected into the iframe
129
129
  in chat, record passive control/selection output at
130
130
  `inline-ui:<extensionId>:output` in application state so the agent can read it
131
131
  later with `readAppState`.
132
- - `agentNative.chat.send(message, opts?)` — send a visible prompt or selected
133
- value back into the current agent chat.
132
+ - `agentNative.chat.send(message, opts?)` - send a visible prompt or selected
133
+ value back into the current agent chat. The extension bridge keeps messages
134
+ draft-only unless `opts.submit === true`; pass `{ submit: true }` only from a
135
+ user-triggered Apply or Submit action. Never call it from polling, refresh, or
136
+ error handlers; use application state or `agentNative.ui.output` for passive
137
+ results.
134
138
 
135
139
  For transient inline generative UI, `extensionData` is host-browser
136
140
  `localStorage`: the agent cannot read it, it does not sync across devices, it
@@ -137,7 +137,7 @@ await dbExec("UPDATE notes SET title = 'Updated Title' WHERE id = 'abc'");
137
137
  | `extensionData.get(collection, id, opts?)` | Get a single item by id | `extensionData.get('todos', 'todo-1')` |
138
138
  | `extensionData.remove(collection, id, opts?)` | Delete an item | `extensionData.remove('todos', 'todo-1')` |
139
139
  | `agentNative.ui.output(value, opts?)` | Record passive inline UI output in application state | `agentNative.ui.output({ threshold })` |
140
- | `agentNative.chat.send(message, opts?)` | Send a visible prompt or selected value back to chat | `agentNative.chat.send('Use Q2', { context: { q: 2 } })` |
140
+ | `agentNative.chat.send(message, opts?)` | Send a visible prompt or selected value back to chat | `agentNative.chat.send('Use Q2', { submit: true, context: { q: 2 } })` |
141
141
 
142
142
  ## Persisting Custom Data
143
143
 
@@ -76,7 +76,9 @@ The stored payload includes `value`, `updatedAt`, `extensionId`, and
76
76
 
77
77
  Use `agentNative.chat.send(message, opts?)` or `sendToAgentChat(message, opts?)`
78
78
  for a visible Apply/Submit button that should send a prompt or selected value
79
- into the chat.
79
+ into the chat. Pass `{ submit: true }` explicitly for that user-triggered
80
+ action; extension messages are draft-only otherwise. Never call these helpers
81
+ from polling, refresh, or error handlers.
80
82
 
81
83
  ## App Data And State
82
84