@agent-native/core 0.124.2 → 0.124.4

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 (267) hide show
  1. package/README.md +1 -1
  2. package/corpus/README.md +2 -2
  3. package/corpus/core/CHANGELOG.md +16 -0
  4. package/corpus/core/docs/content/cloneable-saas.mdx +9 -9
  5. package/corpus/core/docs/content/creating-templates.mdx +2 -2
  6. package/corpus/core/docs/content/deployment.mdx +23 -0
  7. package/corpus/core/docs/content/faq.mdx +4 -4
  8. package/corpus/core/docs/content/key-concepts.mdx +1 -1
  9. package/corpus/core/docs/content/multi-app-workspace.mdx +1 -1
  10. package/corpus/core/docs/content/syncing-template-changes.mdx +1 -1
  11. package/corpus/core/docs/content/template-analytics-developers.mdx +2 -2
  12. package/corpus/core/docs/content/template-analytics.mdx +2 -2
  13. package/corpus/core/docs/content/template-assets-developers.mdx +3 -3
  14. package/corpus/core/docs/content/template-assets.mdx +2 -2
  15. package/corpus/core/docs/content/template-brain-developers.mdx +2 -2
  16. package/corpus/core/docs/content/template-brain.mdx +1 -1
  17. package/corpus/core/docs/content/template-calendar-developers.mdx +1 -1
  18. package/corpus/core/docs/content/template-calendar.mdx +1 -1
  19. package/corpus/core/docs/content/template-chat.mdx +1 -1
  20. package/corpus/core/docs/content/template-clips-developers.mdx +4 -4
  21. package/corpus/core/docs/content/template-clips.mdx +2 -2
  22. package/corpus/core/docs/content/template-content-developers.mdx +2 -2
  23. package/corpus/core/docs/content/template-content.mdx +1 -1
  24. package/corpus/core/docs/content/template-design-brand-and-figma.mdx +1 -1
  25. package/corpus/core/docs/content/template-design-developers.mdx +2 -2
  26. package/corpus/core/docs/content/template-design.mdx +2 -2
  27. package/corpus/core/docs/content/template-dispatch-developers.mdx +5 -5
  28. package/corpus/core/docs/content/template-forms-developers.mdx +1 -1
  29. package/corpus/core/docs/content/template-forms.mdx +1 -1
  30. package/corpus/core/docs/content/template-mail-developers.mdx +2 -2
  31. package/corpus/core/docs/content/template-mail.mdx +2 -2
  32. package/corpus/core/docs/content/template-plan-developers.mdx +3 -3
  33. package/corpus/core/docs/content/template-plan.mdx +3 -3
  34. package/corpus/core/docs/content/template-slides-developers.mdx +4 -4
  35. package/corpus/core/docs/content/template-slides.mdx +1 -1
  36. package/corpus/core/docs/content/what-is-agent-native.mdx +5 -5
  37. package/corpus/core/package.json +1 -1
  38. package/corpus/core/src/a2a/auth-policy.ts +1 -1
  39. package/corpus/core/src/agent/production-agent.ts +14 -1
  40. package/corpus/core/src/agent/run-manager.ts +6 -13
  41. package/corpus/core/src/cli/index.ts +11 -1
  42. package/corpus/core/src/client/route-chunk-recovery.ts +18 -7
  43. package/corpus/core/src/db/client.ts +7 -6
  44. package/corpus/core/src/db/migrations.ts +2 -1
  45. package/corpus/core/src/deploy/build.ts +290 -4
  46. package/corpus/core/src/deploy/nitro-preset.ts +50 -0
  47. package/corpus/core/src/integrations/pending-tasks-retry-job.ts +2 -1
  48. package/corpus/core/src/scripts/call-agent.ts +4 -2
  49. package/corpus/core/src/server/agent-discovery.ts +1 -0
  50. package/corpus/core/src/server/agent-run-context.ts +13 -3
  51. package/corpus/core/src/server/request-context.ts +2 -0
  52. package/corpus/core/src/shared/cloudflare-globals.d.ts +2 -1
  53. package/corpus/core/src/shared/runtime.ts +2 -0
  54. package/corpus/core/src/shared/streaming-text-smoothing.ts +15 -14
  55. package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +29 -0
  56. package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +6 -6
  57. package/corpus/core/src/templates/default/app/i18n/de-DE.ts +6 -6
  58. package/corpus/core/src/templates/default/app/i18n/en-US.ts +6 -6
  59. package/corpus/core/src/templates/default/app/i18n/es-ES.ts +6 -6
  60. package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +6 -6
  61. package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +6 -6
  62. package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +6 -6
  63. package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +6 -6
  64. package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +6 -6
  65. package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +6 -6
  66. package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +6 -6
  67. package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +29 -0
  68. package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -0
  69. package/corpus/core/src/vite/client.ts +20 -1
  70. package/corpus/templates/analytics/actions/restore-dashboard-revision.ts +9 -2
  71. package/corpus/templates/analytics/app/components/dashboard/DashboardHistoryPanel.tsx +3 -0
  72. package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +1 -1
  73. package/corpus/templates/analytics/app/global.css +10 -9
  74. package/corpus/templates/analytics/app/hooks/use-chart-tooltip-portal.ts +59 -18
  75. package/corpus/templates/analytics/app/hooks/use-dashboard-revisions.ts +14 -2
  76. package/corpus/templates/analytics/app/i18n-data.ts +102 -0
  77. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +232 -2
  78. package/corpus/templates/analytics/changelog/2026-07-27-chart-tooltips-stay-beside-the-cursor-when-they-extend-beyon.md +6 -0
  79. package/corpus/templates/analytics/changelog/2026-07-27-chat-no-longer-claims-a-data-source-is-disconnected-when-it-.md +6 -0
  80. package/corpus/templates/analytics/changelog/2026-07-27-dashboard-changes-can-be-undone-with-cmd-z-and-actions-menu.md +6 -0
  81. package/corpus/templates/analytics/changelog/2026-07-27-dashboard-redo-with-cmd-shift-z-and-actions-menu.md +6 -0
  82. package/corpus/templates/analytics/changelog/2026-07-27-dashboard-skeletons-are-subtler-and-smoother.md +6 -0
  83. package/corpus/templates/analytics/server/lib/dashboards-store.ts +61 -20
  84. package/corpus/templates/analytics/server/lib/real-data-actions.ts +1 -0
  85. package/corpus/templates/analytics/server/plugins/agent-chat.ts +7 -3
  86. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +7 -32
  87. package/corpus/templates/calendar/changelog/2026-07-27-calendar-s-sidebar-now-uses-a-cleaner-layout-with-fewer-util.md +6 -0
  88. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +14 -6
  89. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +32 -37
  90. package/corpus/templates/content/changelog/2026-07-27-content-sidebar-page-lists-now-load-with-quiet-skeletons-and.md +6 -0
  91. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +34 -11
  92. package/corpus/templates/design/.agents/skills/responsive-breakpoints/SKILL.md +8 -0
  93. package/corpus/templates/design/actions/generate-design.ts +259 -36
  94. package/corpus/templates/design/actions/generate-screens.ts +51 -14
  95. package/corpus/templates/design/actions/present-design-variants.ts +6 -3
  96. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +8 -0
  97. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +158 -9
  98. package/corpus/templates/design/app/components/design/design-canvas/content-size-report.ts +124 -0
  99. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +61 -9
  100. package/corpus/templates/design/app/components/layout/Sidebar.tsx +16 -18
  101. package/corpus/templates/design/changelog/2026-07-24-ai-designs-now-default-to-a-full-size-desktop-mobile-pair-fr.md +6 -0
  102. package/corpus/templates/design/changelog/2026-07-24-design-frames-now-fill-the-viewport-height-and-grow-to-fit-t.md +6 -0
  103. package/corpus/templates/design/changelog/2026-07-24-generating-an-additional-screen-now-places-it-beside-the-exi.md +6 -0
  104. package/corpus/templates/design/changelog/2026-07-27-collapsed-sidebar-keeps-the-agent-native-logo.md +6 -0
  105. package/corpus/templates/dispatch/AGENTS.md +7 -2
  106. package/corpus/templates/dispatch/actions/view-screen.ts +3 -2
  107. package/corpus/templates/dispatch/changelog/2026-07-17-curated-template-remixes.md +1 -1
  108. package/corpus/templates/dispatch/changelog/2026-07-27-creating-an-app-from-a-curated-template-now-uses-clearer-mor.md +6 -0
  109. package/corpus/templates/dispatch/changelog/2026-07-27-thread-debug-accepts-request-run-ids.md +6 -0
  110. package/corpus/templates/slides/actions/create-design-system.ts +10 -2
  111. package/corpus/templates/slides/actions/import-file.ts +173 -1
  112. package/corpus/templates/slides/actions/list-slide-comments.ts +16 -1
  113. package/corpus/templates/slides/actions/update-design-system.ts +13 -2
  114. package/corpus/templates/slides/app/components/editor/AssetLibraryPanel.tsx +35 -12
  115. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +36 -0
  116. package/corpus/templates/slides/app/i18n/en-US.ts +3 -0
  117. package/corpus/templates/slides/app/lib/dynamic-import.ts +63 -0
  118. package/corpus/templates/slides/app/lib/export-pdf-client.ts +3 -2
  119. package/corpus/templates/slides/app/lib/export-pptx-client.ts +5 -2
  120. package/corpus/templates/slides/app/pages/DesignSystems.tsx +9 -1
  121. package/corpus/templates/slides/server/db/schema.ts +11 -0
  122. package/corpus/templates/slides/server/handlers/assets.ts +64 -19
  123. package/corpus/templates/slides/server/handlers/import/html-converter.ts +38 -0
  124. package/corpus/templates/slides/server/plugins/db.ts +20 -0
  125. package/corpus/templates/slides/shared/design-system-validation.ts +62 -0
  126. package/dist/a2a/auth-policy.js +1 -1
  127. package/dist/a2a/auth-policy.js.map +1 -1
  128. package/dist/agent/production-agent.d.ts +1 -0
  129. package/dist/agent/production-agent.d.ts.map +1 -1
  130. package/dist/agent/production-agent.js +12 -1
  131. package/dist/agent/production-agent.js.map +1 -1
  132. package/dist/agent/run-manager.d.ts +2 -0
  133. package/dist/agent/run-manager.d.ts.map +1 -1
  134. package/dist/agent/run-manager.js +4 -10
  135. package/dist/agent/run-manager.js.map +1 -1
  136. package/dist/cli/index.js +8 -1
  137. package/dist/cli/index.js.map +1 -1
  138. package/dist/client/route-chunk-recovery.d.ts.map +1 -1
  139. package/dist/client/route-chunk-recovery.js +16 -7
  140. package/dist/client/route-chunk-recovery.js.map +1 -1
  141. package/dist/db/client.d.ts.map +1 -1
  142. package/dist/db/client.js +4 -2
  143. package/dist/db/client.js.map +1 -1
  144. package/dist/db/migrations.d.ts.map +1 -1
  145. package/dist/db/migrations.js +2 -1
  146. package/dist/db/migrations.js.map +1 -1
  147. package/dist/deploy/build.d.ts +25 -0
  148. package/dist/deploy/build.d.ts.map +1 -1
  149. package/dist/deploy/build.js +227 -4
  150. package/dist/deploy/build.js.map +1 -1
  151. package/dist/deploy/nitro-preset.d.ts +8 -0
  152. package/dist/deploy/nitro-preset.d.ts.map +1 -0
  153. package/dist/deploy/nitro-preset.js +36 -0
  154. package/dist/deploy/nitro-preset.js.map +1 -0
  155. package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
  156. package/dist/integrations/pending-tasks-retry-job.js +2 -1
  157. package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
  158. package/dist/notifications/routes.d.ts +3 -3
  159. package/dist/observability/routes.d.ts +3 -3
  160. package/dist/progress/routes.d.ts +1 -1
  161. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  162. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  163. package/dist/resources/handlers.d.ts +1 -1
  164. package/dist/scripts/call-agent.d.ts.map +1 -1
  165. package/dist/scripts/call-agent.js +4 -2
  166. package/dist/scripts/call-agent.js.map +1 -1
  167. package/dist/server/agent-discovery.d.ts.map +1 -1
  168. package/dist/server/agent-discovery.js +1 -0
  169. package/dist/server/agent-discovery.js.map +1 -1
  170. package/dist/server/agent-run-context.d.ts.map +1 -1
  171. package/dist/server/agent-run-context.js +10 -3
  172. package/dist/server/agent-run-context.js.map +1 -1
  173. package/dist/server/request-context.d.ts +2 -0
  174. package/dist/server/request-context.d.ts.map +1 -1
  175. package/dist/server/request-context.js.map +1 -1
  176. package/dist/server/transcribe-voice.d.ts +1 -1
  177. package/dist/shared/runtime.d.ts.map +1 -1
  178. package/dist/shared/runtime.js +2 -0
  179. package/dist/shared/runtime.js.map +1 -1
  180. package/dist/shared/streaming-text-smoothing.d.ts.map +1 -1
  181. package/dist/shared/streaming-text-smoothing.js +15 -8
  182. package/dist/shared/streaming-text-smoothing.js.map +1 -1
  183. package/dist/templates/default/.agents/skills/secrets/SKILL.md +29 -0
  184. package/dist/templates/default/app/i18n/ar-SA.ts +6 -6
  185. package/dist/templates/default/app/i18n/de-DE.ts +6 -6
  186. package/dist/templates/default/app/i18n/en-US.ts +6 -6
  187. package/dist/templates/default/app/i18n/es-ES.ts +6 -6
  188. package/dist/templates/default/app/i18n/fr-FR.ts +6 -6
  189. package/dist/templates/default/app/i18n/hi-IN.ts +6 -6
  190. package/dist/templates/default/app/i18n/ja-JP.ts +6 -6
  191. package/dist/templates/default/app/i18n/ko-KR.ts +6 -6
  192. package/dist/templates/default/app/i18n/pt-BR.ts +6 -6
  193. package/dist/templates/default/app/i18n/zh-CN.ts +6 -6
  194. package/dist/templates/default/app/i18n/zh-TW.ts +6 -6
  195. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +29 -0
  196. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -0
  197. package/dist/vite/client.d.ts +1 -1
  198. package/dist/vite/client.d.ts.map +1 -1
  199. package/dist/vite/client.js +16 -0
  200. package/dist/vite/client.js.map +1 -1
  201. package/docs/content/cloneable-saas.mdx +9 -9
  202. package/docs/content/creating-templates.mdx +2 -2
  203. package/docs/content/deployment.mdx +23 -0
  204. package/docs/content/faq.mdx +4 -4
  205. package/docs/content/key-concepts.mdx +1 -1
  206. package/docs/content/multi-app-workspace.mdx +1 -1
  207. package/docs/content/syncing-template-changes.mdx +1 -1
  208. package/docs/content/template-analytics-developers.mdx +2 -2
  209. package/docs/content/template-analytics.mdx +2 -2
  210. package/docs/content/template-assets-developers.mdx +3 -3
  211. package/docs/content/template-assets.mdx +2 -2
  212. package/docs/content/template-brain-developers.mdx +2 -2
  213. package/docs/content/template-brain.mdx +1 -1
  214. package/docs/content/template-calendar-developers.mdx +1 -1
  215. package/docs/content/template-calendar.mdx +1 -1
  216. package/docs/content/template-chat.mdx +1 -1
  217. package/docs/content/template-clips-developers.mdx +4 -4
  218. package/docs/content/template-clips.mdx +2 -2
  219. package/docs/content/template-content-developers.mdx +2 -2
  220. package/docs/content/template-content.mdx +1 -1
  221. package/docs/content/template-design-brand-and-figma.mdx +1 -1
  222. package/docs/content/template-design-developers.mdx +2 -2
  223. package/docs/content/template-design.mdx +2 -2
  224. package/docs/content/template-dispatch-developers.mdx +5 -5
  225. package/docs/content/template-forms-developers.mdx +1 -1
  226. package/docs/content/template-forms.mdx +1 -1
  227. package/docs/content/template-mail-developers.mdx +2 -2
  228. package/docs/content/template-mail.mdx +2 -2
  229. package/docs/content/template-plan-developers.mdx +3 -3
  230. package/docs/content/template-plan.mdx +3 -3
  231. package/docs/content/template-slides-developers.mdx +4 -4
  232. package/docs/content/template-slides.mdx +1 -1
  233. package/docs/content/what-is-agent-native.mdx +5 -5
  234. package/package.json +1 -1
  235. package/src/a2a/auth-policy.ts +1 -1
  236. package/src/agent/production-agent.ts +14 -1
  237. package/src/agent/run-manager.ts +6 -13
  238. package/src/cli/index.ts +11 -1
  239. package/src/client/route-chunk-recovery.ts +18 -7
  240. package/src/db/client.ts +7 -6
  241. package/src/db/migrations.ts +2 -1
  242. package/src/deploy/build.ts +290 -4
  243. package/src/deploy/nitro-preset.ts +50 -0
  244. package/src/integrations/pending-tasks-retry-job.ts +2 -1
  245. package/src/scripts/call-agent.ts +4 -2
  246. package/src/server/agent-discovery.ts +1 -0
  247. package/src/server/agent-run-context.ts +13 -3
  248. package/src/server/request-context.ts +2 -0
  249. package/src/shared/cloudflare-globals.d.ts +2 -1
  250. package/src/shared/runtime.ts +2 -0
  251. package/src/shared/streaming-text-smoothing.ts +15 -14
  252. package/src/templates/default/.agents/skills/secrets/SKILL.md +29 -0
  253. package/src/templates/default/app/i18n/ar-SA.ts +6 -6
  254. package/src/templates/default/app/i18n/de-DE.ts +6 -6
  255. package/src/templates/default/app/i18n/en-US.ts +6 -6
  256. package/src/templates/default/app/i18n/es-ES.ts +6 -6
  257. package/src/templates/default/app/i18n/fr-FR.ts +6 -6
  258. package/src/templates/default/app/i18n/hi-IN.ts +6 -6
  259. package/src/templates/default/app/i18n/ja-JP.ts +6 -6
  260. package/src/templates/default/app/i18n/ko-KR.ts +6 -6
  261. package/src/templates/default/app/i18n/pt-BR.ts +6 -6
  262. package/src/templates/default/app/i18n/zh-CN.ts +6 -6
  263. package/src/templates/default/app/i18n/zh-TW.ts +6 -6
  264. package/src/templates/headless/.agents/skills/secrets/SKILL.md +29 -0
  265. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -0
  266. package/src/vite/client.ts +20 -1
  267. /package/corpus/templates/slides/server/routes/api/assets/{[filename].delete.ts → [id].delete.ts} +0 -0
@@ -455,6 +455,7 @@ function isHostedRuntime(): boolean {
455
455
  !!process.env.AWS_LAMBDA_FUNCTION_NAME ||
456
456
  !!process.env.VERCEL ||
457
457
  "__cf_env" in globalThis ||
458
+ "__env__" in globalThis ||
458
459
  hasPublicRuntimeUrl()
459
460
  );
460
461
  }
@@ -34,6 +34,13 @@ function eventContext(
34
34
  return event.context;
35
35
  }
36
36
 
37
+ function requestWaitUntil(
38
+ event: H3Event,
39
+ ): ((promise: Promise<unknown>) => void) | undefined {
40
+ const waitUntil = event.req?.waitUntil;
41
+ return typeof waitUntil === "function" ? waitUntil : undefined;
42
+ }
43
+
37
44
  function normalizeId(value: string | null | undefined): string | undefined {
38
45
  return typeof value === "string" && value.trim().length > 0
39
46
  ? value.trim()
@@ -179,14 +186,17 @@ export async function resolveAgentRunRequestContext(options: {
179
186
  }): Promise<RequestContext> {
180
187
  const orgId = await resolveAgentRunOrgId(options);
181
188
  const timezone = readAgentRunTimezone(options.event);
189
+ const waitUntil = requestWaitUntil(options.event);
190
+ const run = {
191
+ ...(options.isBackgroundWorker ? { isBackgroundWorker: true } : {}),
192
+ ...(waitUntil ? { waitUntil } : {}),
193
+ };
182
194
  return {
183
195
  userEmail: options.ownerContext.owner,
184
196
  userName: options.ownerContext.name,
185
197
  orgId,
186
198
  timezone,
187
- ...(options.isBackgroundWorker
188
- ? { run: { isBackgroundWorker: true } }
189
- : {}),
199
+ ...(Object.keys(run).length > 0 ? { run } : {}),
190
200
  };
191
201
  }
192
202
 
@@ -84,6 +84,8 @@ function processEnv(name: string): string | undefined {
84
84
  * the live per-request value via `getRequestRunContext()`.
85
85
  */
86
86
  export interface RequestRunContext {
87
+ /** Request-scoped serverless continuation hook, when the runtime provides one. */
88
+ waitUntil?: (promise: Promise<unknown>) => void;
87
89
  /** Origin of the current request (used by the builder-browser tool). */
88
90
  requestOrigin?: string;
89
91
  /** Stable browser tab id for tab-scoped app-state reads/writes. */
@@ -31,4 +31,5 @@ interface CfEnv {
31
31
  }
32
32
 
33
33
  declare var __cf_env: CfEnv | undefined;
34
- declare var __cf_ctx: { waitUntil(p: Promise<unknown>): void } | undefined;
34
+ /** Nitro's native Cloudflare preset exposes the request environment here. */
35
+ declare var __env__: CfEnv | undefined;
@@ -12,6 +12,7 @@ export function isNodeRuntime(): boolean {
12
12
  typeof process !== "undefined" &&
13
13
  typeof process.versions?.node === "string" &&
14
14
  !("__cf_env" in globalThis) &&
15
+ !("__env__" in globalThis) &&
15
16
  !("Deno" in globalThis)
16
17
  );
17
18
  }
@@ -20,6 +21,7 @@ export function isNodeRuntime(): boolean {
20
21
  export function isCloudflareRuntime(): boolean {
21
22
  return (
22
23
  "__cf_env" in globalThis ||
24
+ "__env__" in globalThis ||
23
25
  (typeof navigator !== "undefined" &&
24
26
  navigator.userAgent === "Cloudflare-Workers")
25
27
  );
@@ -70,25 +70,26 @@ export function splitStreamingTextGraphemes(text: string): string[] {
70
70
  // Incremental path: new text extends the cached text
71
71
  if (_segCache && text.startsWith(_segCache.text)) {
72
72
  const prevLen = _segCache.text.length;
73
- // Re-segment from (prevLen - OVERLAP) to include the overlap so any
74
- // grapheme that was split across the boundary is correctly re-assembled.
75
- const overlapStart = Math.max(0, prevLen - SEGMENTER_OVERLAP);
73
+ const cached = _segCache.graphemes;
74
+ // Walk back over cached graphemes until at least OVERLAP characters have
75
+ // been released, so the re-segmented suffix starts on a known grapheme
76
+ // boundary. Slicing at a fixed character offset instead would cut a
77
+ // cluster (surrogate pair, ZWJ sequence) in half, and segmenting that
78
+ // partial cluster in isolation yields a different grapheme count than the
79
+ // cache holds for the same region — which silently drops characters.
80
+ let stableCount = cached.length;
81
+ let releasedChars = 0;
82
+ while (stableCount > 0 && releasedChars < SEGMENTER_OVERLAP) {
83
+ stableCount--;
84
+ releasedChars += cached[stableCount]!.length;
85
+ }
86
+ const overlapStart = prevLen - releasedChars;
76
87
  const suffix = text.slice(overlapStart);
77
88
  const newGraphemes = Array.from(
78
89
  segmenter.segment(suffix),
79
90
  (entry) => entry.segment,
80
91
  );
81
- // Drop graphemes that correspond to the overlap region from the cache so
82
- // we don't double-count them.
83
- const overlapGraphemes = Array.from(
84
- segmenter.segment(text.slice(overlapStart, prevLen)),
85
- (entry) => entry.segment,
86
- );
87
- const stableGraphemes = _segCache.graphemes.slice(
88
- 0,
89
- _segCache.graphemes.length - overlapGraphemes.length,
90
- );
91
- const merged = stableGraphemes.concat(newGraphemes);
92
+ const merged = cached.slice(0, stableCount).concat(newGraphemes);
92
93
  _segCache = { text, graphemes: merged };
93
94
  return merged;
94
95
  }
@@ -151,6 +151,35 @@ Rules:
151
151
  - **Scope matches the registration.** `scope: "user"` → pass the user email.
152
152
  `scope: "workspace"` → pass the active `orgId` from
153
153
  `getOrgContext(event).orgId`.
154
+ - **One resolver per key, and every runtime path goes through it.** Before
155
+ reading a credential, grep the app for the key name. If a resolver already
156
+ exists (`resolveXConfig`, a connector, a client factory), call it — do not
157
+ read `process.env` for that key a second time somewhere else.
158
+ - **Identity comes from the caller, not the module.** A shared helper under
159
+ `server/lib/` takes the email as a parameter; only an entrypoint (action,
160
+ route, cron) decides whose identity it is. A library that resolves its own
161
+ identity from `process.env.AGENT_USER_EMAIL` authorizes the deployment rather
162
+ than the caller.
163
+
164
+ The failure this prevents is not a leak — it is a split brain. When the config
165
+ check reads the vault and the feature reads `process.env`, the settings UI and
166
+ onboarding checklist report the integration as **configured** while every actual
167
+ call fails with "env var is required". The credential is right there in the
168
+ vault, so the error names the wrong cause and sends everyone hunting for a sync
169
+ or redeploy problem that does not exist. This has now shipped four times in one
170
+ app (BigQuery, Jira, Pylon, Academy).
171
+
172
+ Before finishing any change that touches a credential, run the guard from the
173
+ app directory — it finds this whether the app lives in `templates/` or in a
174
+ workspace repo:
175
+
176
+ ```bash
177
+ npx agent-native doctor --only no-env-credentials
178
+ ```
179
+
180
+ Findings in files that already call `resolveCredential` are usually the
181
+ sanctioned `resolveCredential(key, ctx) ?? process.env.KEY` fallback. The bugs
182
+ are the files with **no** resolver at all.
154
183
 
155
184
  ## HTTP routes
156
185
 
@@ -92,19 +92,19 @@ const messages = {
92
92
  curatedTemplates: "قوالب منسقة",
93
93
  curatedTemplatesDescription:
94
94
  "ابدأ من شكل تطبيق تمت مراجعته، ثم اجعله خاصًا بك.",
95
- remix: "إعادة المزج",
96
- remixTemplate: "إعادة مزج {{name}}",
95
+ remix: "إنشاء من قالب",
96
+ remixTemplate: "إنشاء تطبيق {{name}}",
97
97
  remixAppIdLabel: "معرّف التطبيق الجديد",
98
- remixStarted: "بدأت إعادة مزج {{name}}",
98
+ remixStarted: "بدأ إنشاء {{name}}",
99
99
  viewLiveApp: "عرض التطبيق المباشر",
100
100
  alreadyInWorkspace: "موجود بالفعل في مساحة العمل",
101
101
  remixAppIdDescription:
102
102
  "اختر معرّفًا آمنًا لعنوان URL لتطبيق مساحة العمل الجديد.",
103
103
  integrationSetup: "إعداد التكامل",
104
104
  source: "المصدر",
105
- remixing: "جارٍ إعادة المزج…",
106
- remixSuccess: "بدأت إعادة مزج القالب.",
107
- remixError: "تعذرت إعادة مزج هذا القالب",
105
+ remixing: "جارٍ إنشاء التطبيق…",
106
+ remixSuccess: "بدأ إنشاء التطبيق من القالب.",
107
+ remixError: "تعذر إنشاء تطبيق من هذا القالب",
108
108
  appIdRequired: "معرّف التطبيق مطلوب.",
109
109
  hide: "إخفاء",
110
110
  show: "Show",
@@ -91,19 +91,19 @@ const messages = {
91
91
  curatedTemplates: "Kuratierte Vorlagen",
92
92
  curatedTemplatesDescription:
93
93
  "Starte mit einer geprüften App-Struktur und passe sie an.",
94
- remix: "Remix",
95
- remixTemplate: "{{name}} remixen",
94
+ remix: "Aus Vorlage erstellen",
95
+ remixTemplate: "{{name}}-App erstellen",
96
96
  remixAppIdLabel: "Neue App-ID",
97
- remixStarted: "Remix für {{name}} gestartet",
97
+ remixStarted: "Erstellung von {{name}} gestartet",
98
98
  viewLiveApp: "Live-App öffnen",
99
99
  alreadyInWorkspace: "Bereits im Workspace",
100
100
  remixAppIdDescription:
101
101
  "Wähle eine URL-sichere ID für die neue Workspace-App.",
102
102
  integrationSetup: "Integration einrichten",
103
103
  source: "Quelle",
104
- remixing: "Remix wird erstellt…",
105
- remixSuccess: "Template-Remix gestartet.",
106
- remixError: "Dieses Template konnte nicht geremixt werden",
104
+ remixing: "App wird erstellt…",
105
+ remixSuccess: "Erstellung der App aus der Vorlage gestartet.",
106
+ remixError: "App konnte nicht aus dieser Vorlage erstellt werden",
107
107
  appIdRequired: "App-ID ist erforderlich.",
108
108
  hide: "Ausblenden",
109
109
  show: "Show",
@@ -89,19 +89,19 @@ const messages = {
89
89
  curatedTemplates: "Curated templates",
90
90
  curatedTemplatesDescription:
91
91
  "Start from a reviewed app shape, then make it your own.",
92
- remix: "Remix",
93
- remixTemplate: "Remix {{name}}",
92
+ remix: "Create from template",
93
+ remixTemplate: "Create {{name}} app",
94
94
  remixAppIdLabel: "New app id",
95
- remixStarted: "Started {{name}} remix",
95
+ remixStarted: "Started creating {{name}}",
96
96
  viewLiveApp: "View live app",
97
97
  alreadyInWorkspace: "Already in workspace",
98
98
  remixAppIdDescription:
99
99
  "Choose the URL-safe id for the new workspace app.",
100
100
  integrationSetup: "Integration setup",
101
101
  source: "Source",
102
- remixing: "Remixing…",
103
- remixSuccess: "Template remix started.",
104
- remixError: "Could not remix this template",
102
+ remixing: "Creating app…",
103
+ remixSuccess: "Template app creation started.",
104
+ remixError: "Could not create an app from this template",
105
105
  appIdRequired: "App ID is required.",
106
106
  hide: "Hide",
107
107
  show: "Show",
@@ -91,19 +91,19 @@ const messages = {
91
91
  curatedTemplates: "Plantillas seleccionadas",
92
92
  curatedTemplatesDescription:
93
93
  "Empieza con una estructura de aplicación revisada y hazla tuya.",
94
- remix: "Reutilizar",
95
- remixTemplate: "Reutilizar {{name}}",
94
+ remix: "Crear desde plantilla",
95
+ remixTemplate: "Crear app de {{name}}",
96
96
  remixAppIdLabel: "Nuevo ID de aplicación",
97
- remixStarted: "Se inició la reutilización de {{name}}",
97
+ remixStarted: "Se inició la creación de {{name}}",
98
98
  viewLiveApp: "Ver aplicación en vivo",
99
99
  alreadyInWorkspace: "Ya está en el espacio de trabajo",
100
100
  remixAppIdDescription:
101
101
  "Elige un ID seguro para URL para la nueva aplicación.",
102
102
  integrationSetup: "Configuración de integración",
103
103
  source: "Origen",
104
- remixing: "Reutilizando…",
105
- remixSuccess: "Se inició la reutilización de la plantilla.",
106
- remixError: "No se pudo reutilizar esta plantilla",
104
+ remixing: "Creando app…",
105
+ remixSuccess: "Se inició la creación de la app desde la plantilla.",
106
+ remixError: "No se pudo crear una app desde esta plantilla",
107
107
  appIdRequired: "El ID de aplicación es obligatorio.",
108
108
  hide: "Ocultar",
109
109
  show: "Show",
@@ -93,19 +93,19 @@ const messages = {
93
93
  curatedTemplates: "Modèles sélectionnés",
94
94
  curatedTemplatesDescription:
95
95
  "Partez d’une structure d’application vérifiée et personnalisez-la.",
96
- remix: "Remixer",
97
- remixTemplate: "Remixer {{name}}",
96
+ remix: "Créer à partir du modèle",
97
+ remixTemplate: "Créer l’app {{name}}",
98
98
  remixAppIdLabel: "Nouvel identifiant d’application",
99
- remixStarted: "Remix de {{name}} lancé",
99
+ remixStarted: "Création de {{name}} démarrée",
100
100
  viewLiveApp: "Voir l’application en direct",
101
101
  alreadyInWorkspace: "Déjà dans l’espace de travail",
102
102
  remixAppIdDescription:
103
103
  "Choisissez un identifiant compatible avec l’URL pour la nouvelle application.",
104
104
  integrationSetup: "Configuration de l’intégration",
105
105
  source: "Source",
106
- remixing: "Remix en cours…",
107
- remixSuccess: "Le remix du modèle a démarré.",
108
- remixError: "Impossible de remixer ce modèle",
106
+ remixing: "Création de l’app…",
107
+ remixSuccess: "Création de l’app à partir du modèle démarrée.",
108
+ remixError: "Impossible de créer une app à partir de ce modèle",
109
109
  appIdRequired: "L’identifiant de l’application est requis.",
110
110
  hide: "Masquer",
111
111
  show: "Show",
@@ -87,18 +87,18 @@ const messages = {
87
87
  curatedTemplates: "चयनित टेम्पलेट",
88
88
  curatedTemplatesDescription:
89
89
  "समीक्षित ऐप संरचना से शुरू करें और इसे अपने अनुसार बनाएं।",
90
- remix: "रीमिक्स",
91
- remixTemplate: "{{name}} रीमिक्स करें",
90
+ remix: "टेम्पलेट से बनाएं",
91
+ remixTemplate: "{{name}} ऐप बनाएं",
92
92
  remixAppIdLabel: "नया ऐप आईडी",
93
- remixStarted: "{{name}} रीमिक्स शुरू हुआ",
93
+ remixStarted: "{{name}} बनाना शुरू किया",
94
94
  viewLiveApp: "लाइव ऐप देखें",
95
95
  alreadyInWorkspace: "वर्कस्पेस में पहले से मौजूद",
96
96
  remixAppIdDescription: "नए वर्कस्पेस ऐप के लिए URL-सुरक्षित आईडी चुनें।",
97
97
  integrationSetup: "इंटीग्रेशन सेटअप",
98
98
  source: "स्रोत",
99
- remixing: "रीमिक्स हो रहा है…",
100
- remixSuccess: "टेम्पलेट रीमिक्स शुरू हुआ।",
101
- remixError: "यह टेम्पलेट रीमिक्स नहीं हो सका",
99
+ remixing: "ऐप बनाया जा रहा है…",
100
+ remixSuccess: "टेम्पलेट से ऐप बनाना शुरू किया गया।",
101
+ remixError: "इस टेम्पलेट से ऐप नहीं बना सके",
102
102
  appIdRequired: "ऐप आईडी आवश्यक है।",
103
103
  hide: "छिपाएं",
104
104
  show: "Show",
@@ -89,19 +89,19 @@ const messages = {
89
89
  curatedTemplates: "厳選テンプレート",
90
90
  curatedTemplatesDescription:
91
91
  "レビュー済みのアプリ構成をベースに、自分用にカスタマイズできます。",
92
- remix: "リミックス",
93
- remixTemplate: "{{name}} をリミックス",
92
+ remix: "テンプレートから作成",
93
+ remixTemplate: "{{name}} アプリを作成",
94
94
  remixAppIdLabel: "新しいアプリ ID",
95
- remixStarted: "{{name}} のリミックスを開始しました",
95
+ remixStarted: "{{name}} の作成を開始しました",
96
96
  viewLiveApp: "ライブアプリを見る",
97
97
  alreadyInWorkspace: "ワークスペースに追加済み",
98
98
  remixAppIdDescription:
99
99
  "新しいワークスペースアプリの URL セーフな ID を選択してください。",
100
100
  integrationSetup: "連携の設定",
101
101
  source: "提供元",
102
- remixing: "リミックス中…",
103
- remixSuccess: "テンプレートのリミックスを開始しました。",
104
- remixError: "このテンプレートをリミックスできませんでした",
102
+ remixing: "アプリを作成中…",
103
+ remixSuccess: "テンプレートからのアプリ作成を開始しました。",
104
+ remixError: "このテンプレートからアプリを作成できませんでした",
105
105
  appIdRequired: "アプリ ID は必須です。",
106
106
  hide: "非表示",
107
107
  show: "Show",
@@ -88,18 +88,18 @@ const messages = {
88
88
  curatedTemplates: "엄선된 템플릿",
89
89
  curatedTemplatesDescription:
90
90
  "검토된 앱 구조에서 시작해 나만의 앱으로 만들어 보세요.",
91
- remix: "리믹스",
92
- remixTemplate: "{{name}} 리믹스",
91
+ remix: "템플릿에서 만들기",
92
+ remixTemplate: "{{name}} 앱 만들기",
93
93
  remixAppIdLabel: "새 앱 ID",
94
- remixStarted: "{{name}} 리믹스를 시작했습니다",
94
+ remixStarted: "{{name}} 생성을 시작했습니다",
95
95
  viewLiveApp: "라이브 앱 보기",
96
96
  alreadyInWorkspace: "워크스페이스에 이미 있음",
97
97
  remixAppIdDescription: "새 워크스페이스 앱의 URL 안전 ID를 선택하세요.",
98
98
  integrationSetup: "연동 설정",
99
99
  source: "소스",
100
- remixing: "리믹스 중…",
101
- remixSuccess: "템플릿 리믹스를 시작했습니다.",
102
- remixError: "이 템플릿을 리믹스할 수 없습니다",
100
+ remixing: " 만드는 중…",
101
+ remixSuccess: "템플릿에서 만들기를 시작했습니다.",
102
+ remixError: "이 템플릿에서 앱을 만들 수 없습니다",
103
103
  appIdRequired: "앱 ID가 필요합니다.",
104
104
  hide: "숨기기",
105
105
  show: "Show",
@@ -92,19 +92,19 @@ const messages = {
92
92
  curatedTemplates: "Modelos selecionados",
93
93
  curatedTemplatesDescription:
94
94
  "Comece com uma estrutura de app revisada e personalize-a.",
95
- remix: "Remixar",
96
- remixTemplate: "Remixar {{name}}",
95
+ remix: "Criar a partir do modelo",
96
+ remixTemplate: "Criar app {{name}}",
97
97
  remixAppIdLabel: "Novo ID do app",
98
- remixStarted: "Remix de {{name}} iniciado",
98
+ remixStarted: "Criação de {{name}} iniciada",
99
99
  viewLiveApp: "Ver app ao vivo",
100
100
  alreadyInWorkspace: "Já está no workspace",
101
101
  remixAppIdDescription:
102
102
  "Escolha um ID seguro para URL para o novo app do workspace.",
103
103
  integrationSetup: "Configuração da integração",
104
104
  source: "Origem",
105
- remixing: "Remixando…",
106
- remixSuccess: "Remix do modelo iniciado.",
107
- remixError: "Não foi possível remixar este modelo",
105
+ remixing: "Criando app…",
106
+ remixSuccess: "Criação do app a partir do modelo iniciada.",
107
+ remixError: "Não foi possível criar um app a partir deste modelo",
108
108
  appIdRequired: "O ID do app é obrigatório.",
109
109
  hide: "Ocultar",
110
110
  show: "Show",
@@ -85,18 +85,18 @@ const messages = {
85
85
  curatedTemplates: "精选模板",
86
86
  curatedTemplatesDescription:
87
87
  "从经过审核的应用结构开始,然后打造属于你的应用。",
88
- remix: "再创作",
89
- remixTemplate: "再创作 {{name}}",
88
+ remix: "从模板创建",
89
+ remixTemplate: "创建 {{name}} 应用",
90
90
  remixAppIdLabel: "新应用 ID",
91
- remixStarted: "已开始再创作 {{name}}",
91
+ remixStarted: "已开始创建 {{name}}",
92
92
  viewLiveApp: "查看在线应用",
93
93
  alreadyInWorkspace: "已在工作区中",
94
94
  remixAppIdDescription: "为新的工作区应用选择一个 URL 安全的 ID。",
95
95
  integrationSetup: "集成设置",
96
96
  source: "来源",
97
- remixing: "正在再创作…",
98
- remixSuccess: "已开始再创作模板。",
99
- remixError: "无法再创作此模板",
97
+ remixing: "正在创建应用…",
98
+ remixSuccess: "已开始从模板创建应用。",
99
+ remixError: "无法从此模板创建应用",
100
100
  appIdRequired: "应用 ID 是必填项。",
101
101
  hide: "隐藏",
102
102
  show: "Show",
@@ -85,18 +85,18 @@ const messages = {
85
85
  curatedTemplates: "精選範本",
86
86
  curatedTemplatesDescription:
87
87
  "從經過審核的應用程式結構開始,打造屬於你的應用程式。",
88
- remix: "重新混搭",
89
- remixTemplate: "重新混搭 {{name}}",
88
+ remix: "從範本建立",
89
+ remixTemplate: "建立 {{name}} 應用程式",
90
90
  remixAppIdLabel: "新應用程式 ID",
91
- remixStarted: "已開始重新混搭 {{name}}",
91
+ remixStarted: "已開始建立 {{name}}",
92
92
  viewLiveApp: "查看線上應用程式",
93
93
  alreadyInWorkspace: "已在工作區中",
94
94
  remixAppIdDescription: "為新的工作區應用程式選擇 URL 安全的 ID。",
95
95
  integrationSetup: "整合設定",
96
96
  source: "來源",
97
- remixing: "正在重新混搭…",
98
- remixSuccess: "已開始重新混搭範本。",
99
- remixError: "無法重新混搭此範本",
97
+ remixing: "正在建立應用程式…",
98
+ remixSuccess: "已開始從範本建立應用程式。",
99
+ remixError: "無法從此範本建立應用程式",
100
100
  appIdRequired: "應用程式 ID 為必填項目。",
101
101
  hide: "隱藏",
102
102
  show: "Show",
@@ -151,6 +151,35 @@ Rules:
151
151
  - **Scope matches the registration.** `scope: "user"` → pass the user email.
152
152
  `scope: "workspace"` → pass the active `orgId` from
153
153
  `getOrgContext(event).orgId`.
154
+ - **One resolver per key, and every runtime path goes through it.** Before
155
+ reading a credential, grep the app for the key name. If a resolver already
156
+ exists (`resolveXConfig`, a connector, a client factory), call it — do not
157
+ read `process.env` for that key a second time somewhere else.
158
+ - **Identity comes from the caller, not the module.** A shared helper under
159
+ `server/lib/` takes the email as a parameter; only an entrypoint (action,
160
+ route, cron) decides whose identity it is. A library that resolves its own
161
+ identity from `process.env.AGENT_USER_EMAIL` authorizes the deployment rather
162
+ than the caller.
163
+
164
+ The failure this prevents is not a leak — it is a split brain. When the config
165
+ check reads the vault and the feature reads `process.env`, the settings UI and
166
+ onboarding checklist report the integration as **configured** while every actual
167
+ call fails with "env var is required". The credential is right there in the
168
+ vault, so the error names the wrong cause and sends everyone hunting for a sync
169
+ or redeploy problem that does not exist. This has now shipped four times in one
170
+ app (BigQuery, Jira, Pylon, Academy).
171
+
172
+ Before finishing any change that touches a credential, run the guard from the
173
+ app directory — it finds this whether the app lives in `templates/` or in a
174
+ workspace repo:
175
+
176
+ ```bash
177
+ npx agent-native doctor --only no-env-credentials
178
+ ```
179
+
180
+ Findings in files that already call `resolveCredential` are usually the
181
+ sanctioned `resolveCredential(key, ctx) ?? process.env.KEY` fallback. The bugs
182
+ are the files with **no** resolver at all.
154
183
 
155
184
  ## HTTP routes
156
185
 
@@ -151,6 +151,35 @@ Rules:
151
151
  - **Scope matches the registration.** `scope: "user"` → pass the user email.
152
152
  `scope: "workspace"` → pass the active `orgId` from
153
153
  `getOrgContext(event).orgId`.
154
+ - **One resolver per key, and every runtime path goes through it.** Before
155
+ reading a credential, grep the app for the key name. If a resolver already
156
+ exists (`resolveXConfig`, a connector, a client factory), call it — do not
157
+ read `process.env` for that key a second time somewhere else.
158
+ - **Identity comes from the caller, not the module.** A shared helper under
159
+ `server/lib/` takes the email as a parameter; only an entrypoint (action,
160
+ route, cron) decides whose identity it is. A library that resolves its own
161
+ identity from `process.env.AGENT_USER_EMAIL` authorizes the deployment rather
162
+ than the caller.
163
+
164
+ The failure this prevents is not a leak — it is a split brain. When the config
165
+ check reads the vault and the feature reads `process.env`, the settings UI and
166
+ onboarding checklist report the integration as **configured** while every actual
167
+ call fails with "env var is required". The credential is right there in the
168
+ vault, so the error names the wrong cause and sends everyone hunting for a sync
169
+ or redeploy problem that does not exist. This has now shipped four times in one
170
+ app (BigQuery, Jira, Pylon, Academy).
171
+
172
+ Before finishing any change that touches a credential, run the guard from the
173
+ app directory — it finds this whether the app lives in `templates/` or in a
174
+ workspace repo:
175
+
176
+ ```bash
177
+ npx agent-native doctor --only no-env-credentials
178
+ ```
179
+
180
+ Findings in files that already call `resolveCredential` are usually the
181
+ sanctioned `resolveCredential(key, ctx) ?? process.env.KEY` fallback. The bugs
182
+ are the files with **no** resolver at all.
154
183
 
155
184
  ## HTTP routes
156
185
 
@@ -22,6 +22,7 @@ import {
22
22
  parsePendingEntry,
23
23
  } from "../changelog/parse.js";
24
24
  import { getViteDevRecoveryScript } from "../client/vite-dev-recovery-script.js";
25
+ import { writeAgentNativeNitroPresetMarker } from "../deploy/nitro-preset.js";
25
26
  import { findWorkspaceRoot } from "../scripts/utils.js";
26
27
  import { verifyEmbedSessionToken } from "../server/embed-session.js";
27
28
  import {
@@ -1387,7 +1388,7 @@ function getCoreSourceAliases(
1387
1388
  }
1388
1389
 
1389
1390
  export interface NitroOptions {
1390
- /** Nitro deployment preset (e.g. "node", "vercel", "netlify", "cloudflare_pages"). Default: "node" */
1391
+ /** Nitro deployment preset (e.g. "node", "vercel", "netlify", "cloudflare_pages", "cloudflare_module"). Default: "node" */
1391
1392
  preset?: string;
1392
1393
  /** Source directory for server files. Default: "./server" */
1393
1394
  srcDir?: string;
@@ -2964,6 +2965,22 @@ function forceServeOnly(pluginOrPreset: any): any {
2964
2965
  return { ...pluginOrPreset, apply: "serve" };
2965
2966
  }
2966
2967
 
2968
+ function nitroPresetMarkerPlugin(
2969
+ options: ClientConfigOptions | AgentNativeVitePluginOptions,
2970
+ ): Plugin | null {
2971
+ const preset = options.nitro?.preset;
2972
+ if (typeof preset !== "string" || !preset.trim()) return null;
2973
+
2974
+ return {
2975
+ name: "agent-native-nitro-preset-marker",
2976
+ configResolved(config) {
2977
+ if (config.command === "build") {
2978
+ writeAgentNativeNitroPresetMarker(preset);
2979
+ }
2980
+ },
2981
+ };
2982
+ }
2983
+
2967
2984
  function createAgentNativePlugins(
2968
2985
  options: ClientConfigOptions | AgentNativeVitePluginOptions,
2969
2986
  {
@@ -2981,8 +2998,10 @@ function createAgentNativePlugins(
2981
2998
  const { appBasePath } = getConfiguredAppBasePath();
2982
2999
  const nitroPlugin = createNitroDevPlugin(options, appBasePath);
2983
3000
  const includeNitro = !isBuildCommand(command);
3001
+ const presetMarkerPlugin = nitroPresetMarkerPlugin(options);
2984
3002
 
2985
3003
  return [
3004
+ presetMarkerPlugin,
2986
3005
  // Stub packages from `options.ssrStubs` in the SSR bundle so they
2987
3006
  // don't bloat the edge worker. Opt-in per template — the framework
2988
3007
  // hardcodes nothing (e.g. docs sites legitimately import `shiki` on