@agent-native/core 0.169.1 → 0.170.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 (459) hide show
  1. package/agent-native.eject.json +4 -1
  2. package/bin/agent-native.js +23 -5
  3. package/corpus/README.md +1 -1
  4. package/corpus/templates/analytics/.agents/skills/secrets/SKILL.md +11 -4
  5. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
  6. package/corpus/templates/analytics/app/i18n-data.ts +28 -0
  7. package/corpus/templates/analytics/app/pages/Agents.tsx +25 -1
  8. package/corpus/templates/analytics/app/routes/$.tsx +10 -0
  9. package/corpus/templates/analytics/app/routes.ts +3 -1
  10. package/corpus/templates/analytics/changelog/2026-08-22-fixed-the-agent-native-templates-first-party-dashboard-panel.md +6 -0
  11. package/corpus/templates/analytics/changelog/2026-08-22-org-admin-panels-no-longer-report-a-database-error-as-a-per.md +9 -0
  12. package/corpus/templates/analytics/changelog/2026-08-22-slack-requests-report-invalid-inputs.md +6 -0
  13. package/corpus/templates/analytics/changelog/2026-08-22-unknown-analytics-links-now-return-a-real-not-found-status.md +6 -0
  14. package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +2 -1
  15. package/corpus/templates/analytics/docs/schemas/tracked-events.md +2 -0
  16. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +29 -7
  17. package/corpus/templates/analytics/server/handlers/slack.ts +59 -5
  18. package/corpus/templates/analytics/server/lib/db-admin-connections.ts +11 -6
  19. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +132 -2
  20. package/corpus/templates/analytics/server/lib/slack.ts +4 -2
  21. package/corpus/templates/analytics/vitest.config.ts +6 -1
  22. package/corpus/templates/assets/.agents/skills/secrets/SKILL.md +11 -4
  23. package/corpus/templates/assets/app/routes/library.tsx +5 -3
  24. package/corpus/templates/assets/app/routes.ts +3 -1
  25. package/corpus/templates/assets/changelog/2026-08-22-library-page-loads-again-instead-of-crashing-on-open.md +6 -0
  26. package/corpus/templates/assets/vitest.config.ts +6 -1
  27. package/corpus/templates/brain/.agents/skills/secrets/SKILL.md +11 -4
  28. package/corpus/templates/brain/app/components/ui/pointer-lock.ts +1 -0
  29. package/corpus/templates/brain/app/routes/sources.tsx +8 -3
  30. package/corpus/templates/brain/changelog/2026-08-22-tuning-or-adding-a-source-no-longer-leaves-the-sources-page.md +6 -0
  31. package/corpus/templates/brain/vitest.config.ts +1 -0
  32. package/corpus/templates/calendar/.agents/skills/secrets/SKILL.md +11 -4
  33. package/corpus/templates/calendar/actions/list-booking-links.ts +83 -4
  34. package/corpus/templates/calendar/actions/update-booking-link.ts +4 -3
  35. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +11 -3
  36. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +1 -1
  37. package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +103 -44
  38. package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +28 -24
  39. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +9 -2
  40. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +47 -58
  41. package/corpus/templates/calendar/app/hooks/use-booking-links.ts +5 -1
  42. package/corpus/templates/calendar/app/lib/event-layout.ts +52 -26
  43. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +126 -98
  44. package/corpus/templates/calendar/app/pages/CalendarView.tsx +1 -0
  45. package/corpus/templates/calendar/app/pages/Settings.tsx +88 -81
  46. package/corpus/templates/calendar/app/routes.ts +3 -1
  47. package/corpus/templates/calendar/changelog/2026-08-22-booking-link-visibility-respects-shared-access.md +6 -0
  48. package/corpus/templates/calendar/changelog/2026-08-22-clicking-an-out-of-office-event-s-marker-no-longer-creates-a.md +6 -0
  49. package/corpus/templates/calendar/changelog/2026-08-22-event-detail-views-now-show-your-calendar-timezone.md +6 -0
  50. package/corpus/templates/calendar/changelog/2026-08-22-google-calendar-can-now-connect-with-the-shared-google-oauth.md +6 -0
  51. package/corpus/templates/calendar/changelog/2026-08-22-overlapping-events-now-show-each-others-boundaries.md +6 -0
  52. package/corpus/templates/calendar/changelog/2026-08-22-public-booking-pages-now-offer-time-slots-from-the-host-s-we.md +6 -0
  53. package/corpus/templates/calendar/server/handlers/bookings.ts +5 -7
  54. package/corpus/templates/calendar/server/handlers/google-auth.ts +10 -2
  55. package/corpus/templates/calendar/server/lib/google-calendar.ts +139 -14
  56. package/corpus/templates/calendar/server/onboarding.ts +85 -0
  57. package/corpus/templates/calendar/server/plugins/agent-chat.ts +2 -0
  58. package/corpus/templates/calendar/shared/api.ts +9 -1
  59. package/corpus/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
  60. package/corpus/templates/chat/app/routes.ts +3 -1
  61. package/corpus/templates/chat/vitest.config.ts +8 -0
  62. package/corpus/templates/clips/.agents/skills/secrets/SKILL.md +11 -4
  63. package/corpus/templates/clips/actions/get-meeting.ts +9 -4
  64. package/corpus/templates/clips/actions/list-recordings.ts +16 -3
  65. package/corpus/templates/clips/actions/search-meetings.ts +1 -0
  66. package/corpus/templates/clips/actions/view-screen.ts +8 -1
  67. package/corpus/templates/clips/app/components/meetings/meeting-history-row.tsx +32 -1
  68. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +1 -1
  69. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +29 -1
  70. package/corpus/templates/clips/app/i18n/en-US.ts +5 -0
  71. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +33 -4
  72. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +1 -0
  73. package/corpus/templates/clips/app/routes/access-request.approve.tsx +16 -9
  74. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +17 -7
  75. package/corpus/templates/clips/app/routes/share.$shareId.tsx +17 -7
  76. package/corpus/templates/clips/changelog/2026-08-21-camera-bubble-black-circle.md +6 -0
  77. package/corpus/templates/clips/changelog/2026-08-22-screen-recordings-respect-the-microphone-toggle.md +6 -0
  78. package/corpus/templates/clips/changelog/2026-08-22-the-recordings-library-no-longer-fails-right-after-server-start.md +6 -0
  79. package/corpus/templates/clips/changelog/2026-08-23-the-agent-can-see-your-library-right-after-server-start.md +6 -0
  80. package/corpus/templates/clips/chrome-extension/src/background.ts +1 -2
  81. package/corpus/templates/clips/chrome-extension/src/native-transcription.ts +39 -7
  82. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +21 -25
  83. package/corpus/templates/clips/desktop/src/app.tsx +53 -24
  84. package/corpus/templates/clips/desktop/src/lib/bubble-playback.ts +68 -0
  85. package/corpus/templates/clips/desktop/src/lib/recorder.ts +3 -0
  86. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +72 -14
  87. package/corpus/templates/clips/desktop/src/lib/updater.ts +8 -3
  88. package/corpus/templates/clips/desktop/src/overlays/bubble.tsx +162 -20
  89. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +43 -6
  90. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +150 -13
  91. package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +12 -0
  92. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +28 -3
  93. package/corpus/templates/clips/server/lib/emails.ts +17 -33
  94. package/corpus/templates/content/.agents/skills/secrets/SKILL.md +11 -4
  95. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +140 -15
  96. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +12 -14
  97. package/corpus/templates/content/app/components/ui/pointer-lock.ts +1 -0
  98. package/corpus/templates/content/app/hooks/use-content-database.ts +18 -2
  99. package/corpus/templates/content/app/hooks/use-local-storage.ts +38 -22
  100. package/corpus/templates/content/app/i18n-data.ts +25 -0
  101. package/corpus/templates/content/app/routes.ts +3 -1
  102. package/corpus/templates/content/changelog/2026-08-22-code-blocks-inserted-from-the-slash-menu-no-longer-get-stuck.md +6 -0
  103. package/corpus/templates/content/changelog/2026-08-22-the-sidebar-page-tree-no-longer-flashes-empty-after-creating-a-page.md +6 -0
  104. package/corpus/templates/content/docs/solutions/2026-08-21-content-mermaid-rendering-shape.md +233 -0
  105. package/corpus/templates/content/server/plugins/agent-chat.ts +9 -2
  106. package/corpus/templates/content/vitest.config.ts +7 -1
  107. package/corpus/templates/crm/.agents/skills/secrets/SKILL.md +11 -4
  108. package/corpus/templates/design/.agents/skills/secrets/SKILL.md +11 -4
  109. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +1383 -88
  110. package/corpus/templates/design/actions/generate-design.ts +98 -47
  111. package/corpus/templates/design/actions/get-design-system.ts +6 -1
  112. package/corpus/templates/design/actions/refresh-design-system-with-builder.ts +146 -0
  113. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +70 -26
  114. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +284 -136
  115. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2057 -141
  116. package/corpus/templates/design/app/components/design/editor/DesignBottomToolbar.tsx +6 -2
  117. package/corpus/templates/design/app/components/design/editor/DesignWorkspaceRail.tsx +4 -4
  118. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +10 -1
  119. package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +19 -2
  120. package/corpus/templates/design/app/components/design/multi-screen/types.ts +9 -2
  121. package/corpus/templates/design/app/components/layout/Header.tsx +15 -0
  122. package/corpus/templates/design/app/components/layout/Layout.tsx +7 -2
  123. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +11 -4
  124. package/corpus/templates/design/app/i18n-data.ts +1 -0
  125. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +12 -0
  126. package/corpus/templates/design/app/lib/design-system-data.ts +68 -0
  127. package/corpus/templates/design/app/pages/DesignEditor.tsx +77 -35
  128. package/corpus/templates/design/app/pages/DesignSystems.tsx +136 -42
  129. package/corpus/templates/design/app/pages/Index.tsx +2 -0
  130. package/corpus/templates/design/app/pages/Templates.tsx +0 -10
  131. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +10 -0
  132. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +50 -0
  133. package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +1 -1
  134. package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +10 -8
  135. package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +6 -6
  136. package/corpus/templates/design/app/pages/design-editor/commands/apply-pending-visual-styles-with-agent.ts +1 -1
  137. package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +18 -7
  138. package/corpus/templates/design/app/pages/design-editor/commands/escape-hotkey.ts +4 -90
  139. package/corpus/templates/design/app/pages/design-editor/commands/layer-marquee-selection-change.ts +2 -1
  140. package/corpus/templates/design/app/pages/design-editor/commands/layer-selection-change.ts +4 -3
  141. package/corpus/templates/design/app/pages/design-editor/commands/screen-element-select.ts +12 -2
  142. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-style-change.ts +9 -2
  143. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-move-semantic-handoff.ts +1 -1
  144. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-semantic-handoff.ts +1 -1
  145. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-state-semantic-handoff.ts +1 -1
  146. package/corpus/templates/design/app/pages/design-editor/commands/start-sidebar-resize.ts +1 -1
  147. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +11 -1
  148. package/corpus/templates/design/app/pages/design-editor/effects/adopt-db-file-content.ts +11 -19
  149. package/corpus/templates/design/app/pages/design-editor/effects/observe-collab-text.ts +6 -6
  150. package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +1 -1
  151. package/corpus/templates/design/app/pages/design-editor/effects/seed-collab-content.ts +12 -15
  152. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +5 -59
  153. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +8 -0
  154. package/corpus/templates/design/app/pages/design-editor/types.ts +3 -3
  155. package/corpus/templates/design/app/routes.ts +3 -1
  156. package/corpus/templates/design/changelog/2026-08-20-canvas-edits-patch-the-frame-instead-of-rebuilding-it.md +6 -0
  157. package/corpus/templates/design/changelog/2026-08-21-builder-design-system-imports-reconcile-after-indexing.md +6 -0
  158. package/corpus/templates/design/changelog/2026-08-21-draw-mode-needs-shift-y.md +6 -0
  159. package/corpus/templates/design/changelog/2026-08-21-eyedropper-shortcut.md +6 -0
  160. package/corpus/templates/design/changelog/2026-08-21-figma-style-alignment-guides-and-snapping.md +6 -0
  161. package/corpus/templates/design/changelog/2026-08-21-group-transforms-keep-the-selection.md +6 -0
  162. package/corpus/templates/design/changelog/2026-08-21-inspector-fields-keep-focus-on-enter.md +6 -0
  163. package/corpus/templates/design/changelog/2026-08-21-pen-alt-keeps-the-incoming-handle.md +6 -0
  164. package/corpus/templates/design/changelog/2026-08-21-pen-on-the-board-creates-one-vector.md +6 -0
  165. package/corpus/templates/design/changelog/2026-08-21-scale-a-multi-selection-from-the-group-handles.md +6 -0
  166. package/corpus/templates/design/changelog/2026-08-21-scale-resizes-text-with-the-object.md +6 -0
  167. package/corpus/templates/design/changelog/2026-08-21-selecting-another-object-keeps-the-scale-tool-armed.md +6 -0
  168. package/corpus/templates/design/changelog/2026-08-21-the-shape-tool-remembers-the-shape-you-picked.md +6 -0
  169. package/corpus/templates/design/changelog/2026-08-21-vectors-drawn-in-a-frame-land-where-you-drew-them.md +6 -0
  170. package/corpus/templates/design/changelog/2026-08-22-multi-screen-generation-no-longer-loses-a-saved-screens-layo.md +6 -0
  171. package/corpus/templates/design/changelog/2026-08-22-rename-and-search-fields-are-labeled-for-screen-readers.md +6 -0
  172. package/corpus/templates/design/package.json +2 -0
  173. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +311 -0
  174. package/corpus/templates/design/shared/canvas-math.ts +612 -206
  175. package/corpus/templates/design/shared/pen-path.ts +37 -0
  176. package/corpus/templates/design/vitest.config.ts +7 -1
  177. package/corpus/templates/dispatch/.agents/skills/secrets/SKILL.md +11 -4
  178. package/corpus/templates/dispatch/actions/list-workspace-connections.ts +34 -3
  179. package/corpus/templates/dispatch/app/routes/integrations.tsx +55 -21
  180. package/corpus/templates/dispatch/app/routes.ts +3 -1
  181. package/corpus/templates/factory/.agents/skills/secrets/SKILL.md +11 -4
  182. package/corpus/templates/factory/.env.example +2 -7
  183. package/corpus/templates/factory/AGENTS.md +3 -0
  184. package/corpus/templates/factory/README.md +5 -3
  185. package/corpus/templates/factory/actions/create-factory.ts +16 -3
  186. package/corpus/templates/factory/actions/get-slack-feedback-context.ts +2 -0
  187. package/corpus/templates/factory/actions/get-triage-item.ts +2 -0
  188. package/corpus/templates/factory/actions/list-factory-audit.ts +116 -36
  189. package/corpus/templates/factory/actions/list-factory-automations.ts +2 -1
  190. package/corpus/templates/factory/actions/list-triage-items.ts +22 -19
  191. package/corpus/templates/factory/actions/run-factory-automation.ts +7 -3
  192. package/corpus/templates/factory/actions/save-factory-automation.ts +5 -2
  193. package/corpus/templates/factory/actions/save-triage-config.ts +2 -8
  194. package/corpus/templates/factory/actions/start-builder-for-item.ts +21 -0
  195. package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +229 -297
  196. package/corpus/templates/factory/app/components/factory/FactorySettingsView.tsx +397 -247
  197. package/corpus/templates/factory/app/components/factory/FactorySourceSettingsGroup.tsx +33 -0
  198. package/corpus/templates/factory/app/components/factory/NewFactoryForm.tsx +202 -162
  199. package/corpus/templates/factory/app/i18n/en-US.ts +32 -4
  200. package/corpus/templates/factory/app/routes/factory.tsx +84 -11
  201. package/corpus/templates/factory/app/routes.ts +3 -1
  202. package/corpus/templates/factory/changelog/2026-08-21-activity-runs-use-automation-display-names.md +6 -0
  203. package/corpus/templates/factory/changelog/2026-08-21-create-and-settings-source-cards.md +6 -0
  204. package/corpus/templates/factory/changelog/2026-08-21-keep-automations-visible-while-running.md +6 -0
  205. package/corpus/templates/factory/changelog/2026-08-21-nested-factory-automations-can-poll.md +6 -0
  206. package/corpus/templates/factory/changelog/2026-08-21-settings-sticky-unsaved-bar.md +6 -0
  207. package/corpus/templates/factory/server/connectors/credentials.ts +10 -5
  208. package/corpus/templates/factory/server/lib/factory-audit-report.ts +412 -0
  209. package/corpus/templates/factory/server/lib/factory-config-reconcile.ts +321 -0
  210. package/corpus/templates/factory/server/lib/factory-scope.ts +25 -2
  211. package/corpus/templates/factory/server/lib/require-factory-automation.ts +13 -4
  212. package/corpus/templates/factory/server/plugins/factory-migrations.ts +148 -4
  213. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +4 -6
  214. package/corpus/templates/factory/vitest.config.ts +17 -2
  215. package/corpus/templates/forms/.agents/skills/secrets/SKILL.md +11 -4
  216. package/corpus/templates/forms/app/routes/_app.settings.tsx +4 -1
  217. package/corpus/templates/forms/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
  218. package/corpus/templates/forms/server/lib/public-form-ssr.ts +11 -1
  219. package/corpus/templates/forms/vitest.config.ts +1 -0
  220. package/corpus/templates/macros/.agents/skills/secrets/SKILL.md +11 -4
  221. package/corpus/templates/macros/app/routes.ts +3 -1
  222. package/corpus/templates/macros/vitest.config.ts +6 -1
  223. package/corpus/templates/mail/.agents/skills/secrets/SKILL.md +11 -4
  224. package/corpus/templates/mail/actions/manage-gmail-filters.ts +10 -2
  225. package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +100 -37
  226. package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +14 -2
  227. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +66 -36
  228. package/corpus/templates/mail/app/hooks/use-google-auth.ts +2 -0
  229. package/corpus/templates/mail/app/lib/google-oauth-setup.ts +17 -0
  230. package/corpus/templates/mail/app/pages/InboxPage.tsx +69 -9
  231. package/corpus/templates/mail/app/routes/$.tsx +9 -0
  232. package/corpus/templates/mail/app/routes/$view.$threadId.tsx +23 -1
  233. package/corpus/templates/mail/app/routes/$view.tsx +47 -1
  234. package/corpus/templates/mail/app/routes.ts +3 -1
  235. package/corpus/templates/mail/changelog/2026-08-22-gmail-can-now-connect-with-the-shared-google-oauth.md +6 -0
  236. package/corpus/templates/mail/changelog/2026-08-22-gmail-filters-now-shows-the-real-error.md +6 -0
  237. package/corpus/templates/mail/changelog/2026-08-22-unknown-mail-urls-now-show-the-404-page.md +6 -0
  238. package/corpus/templates/mail/server/handlers/google-auth.ts +11 -2
  239. package/corpus/templates/mail/server/lib/google-auth.ts +92 -12
  240. package/corpus/templates/mail/server/onboarding.ts +66 -18
  241. package/corpus/templates/mail/vitest.config.ts +6 -1
  242. package/corpus/templates/plan/.agents/skills/secrets/SKILL.md +11 -4
  243. package/corpus/templates/plan/app/routes/settings.tsx +4 -1
  244. package/corpus/templates/plan/app/routes.ts +3 -1
  245. package/corpus/templates/plan/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
  246. package/corpus/templates/plan/netlify.toml +1 -1
  247. package/corpus/templates/slides/.agents/skills/secrets/SKILL.md +11 -4
  248. package/corpus/templates/slides/actions/patch-deck.ts +79 -10
  249. package/corpus/templates/slides/actions/update-slide.ts +11 -1
  250. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +14 -4
  251. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +10 -52
  252. package/corpus/templates/slides/app/components/editor/GoogleDocImportHint.tsx +8 -53
  253. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +9 -75
  254. package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +8 -60
  255. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +10 -2
  256. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +19 -7
  257. package/corpus/templates/slides/app/lib/sanitize-slide-html.ts +49 -36
  258. package/corpus/templates/slides/app/routes/access-request.approve.tsx +16 -9
  259. package/corpus/templates/slides/app/routes.ts +3 -1
  260. package/corpus/templates/slides/changelog/2026-08-22-already-uploaded-chat-images-are-no-longer-dropped.md +6 -0
  261. package/corpus/templates/slides/changelog/2026-08-22-google-drive-and-slides-can-now-connect-with-the-shared-google-oauth.md +6 -0
  262. package/corpus/templates/slides/changelog/2026-08-22-multi-slide-repairs-now-report-slides-that-still-overflow.md +6 -0
  263. package/corpus/templates/slides/changelog/2026-08-22-update-slide-now-reports-which-batched-edit-was-skipped.md +6 -0
  264. package/corpus/templates/slides/server/handlers/google-docs-auth.ts +52 -1
  265. package/corpus/templates/slides/server/lib/chat-attachments.ts +29 -3
  266. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +66 -16
  267. package/corpus/templates/slides/server/lib/slide-content-patch.ts +13 -2
  268. package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -2
  269. package/corpus/templates/slides/tsconfig.json +5 -0
  270. package/corpus/templates/slides/vitest.config.ts +6 -1
  271. package/corpus/templates/tasks/.agents/skills/secrets/SKILL.md +11 -4
  272. package/corpus/templates/tasks/app/routes/settings.tsx +4 -1
  273. package/corpus/templates/tasks/app/routes.ts +3 -1
  274. package/corpus/templates/tasks/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
  275. package/corpus/templates/tasks/vitest.config.ts +7 -1
  276. package/dist/action.d.ts +91 -0
  277. package/dist/action.js +50 -0
  278. package/dist/agent/abort-reasons.d.ts +38 -0
  279. package/dist/agent/abort-reasons.js +49 -0
  280. package/dist/agent/engine/ai-sdk-engine.js +8 -5
  281. package/dist/agent/engine/anthropic-engine.js +7 -5
  282. package/dist/agent/engine/first-event-timeout.d.ts +42 -17
  283. package/dist/agent/engine/first-event-timeout.js +67 -25
  284. package/dist/agent/production-agent.d.ts +23 -0
  285. package/dist/agent/production-agent.js +107 -102
  286. package/dist/agent/run-loop-with-resume.d.ts +2 -28
  287. package/dist/agent/run-loop-with-resume.js +25 -40
  288. package/dist/agent/run-manager.js +5 -4
  289. package/dist/agent/run-store.d.ts +55 -0
  290. package/dist/agent/run-store.js +124 -1
  291. package/dist/agent/tool-search.js +2 -1
  292. package/dist/agent/types.d.ts +9 -4
  293. package/dist/app-config/configuration-error.d.ts +11 -0
  294. package/dist/app-config/configuration-error.js +14 -0
  295. package/dist/app-config/index.d.ts +1 -0
  296. package/dist/app-config/index.js +1 -0
  297. package/dist/app-config/integrations.d.ts +1 -0
  298. package/dist/app-config/integrations.js +10 -0
  299. package/dist/app-config/plugins.d.ts +34 -0
  300. package/dist/app-config/plugins.js +39 -0
  301. package/dist/app-config/run-lifecycle-invariants.d.ts +18 -9
  302. package/dist/app-config/run-lifecycle-invariants.js +18 -25
  303. package/dist/app-config/schema.d.ts +16 -0
  304. package/dist/app-config/schema.js +2 -0
  305. package/dist/chat-threads/schema-migrations.js +20 -0
  306. package/dist/chat-threads/store.js +21 -4
  307. package/dist/cli/native-dependencies.d.ts +4 -0
  308. package/dist/cli/native-dependencies.js +15 -0
  309. package/dist/client/EnvironmentBadge.d.ts +1 -1
  310. package/dist/client/EnvironmentBadge.js +40 -5
  311. package/dist/client/MultiTabAssistantChat.d.ts +1 -1
  312. package/dist/client/MultiTabAssistantChat.js +35 -14
  313. package/dist/client/agent-chat/index.d.ts +1 -1
  314. package/dist/client/agent-chat/index.js +1 -1
  315. package/dist/client/agent-page/use-jobs.d.ts +2 -1
  316. package/dist/client/agent-page/use-jobs.js +3 -5
  317. package/dist/client/agent-sidebar-state.js +3 -1
  318. package/dist/client/blocks/library/code-tabs.js +5 -0
  319. package/dist/client/blocks/library/code.js +1 -0
  320. package/dist/client/chat-first/apps-rail.js +2 -2
  321. package/dist/client/chat-model-groups.d.ts +12 -0
  322. package/dist/client/chat-model-groups.js +3 -2
  323. package/dist/client/integrations/GoogleProductLogo.d.ts +5 -0
  324. package/dist/client/integrations/GoogleProductLogo.js +45 -0
  325. package/dist/client/integrations/index.d.ts +2 -0
  326. package/dist/client/integrations/index.js +2 -0
  327. package/dist/client/integrations/workspace-provider-oauth.d.ts +8 -0
  328. package/dist/client/integrations/workspace-provider-oauth.js +13 -0
  329. package/dist/client/resources/mcp-integration-catalog.js +0 -29
  330. package/dist/client/settings/SettingsTabsPage.js +10 -6
  331. package/dist/client/sharing/ShareButton.js +11 -2
  332. package/dist/client/use-chat-models.d.ts +6 -0
  333. package/dist/client/use-chat-models.js +10 -0
  334. package/dist/client/use-db-sync.js +27 -9
  335. package/dist/connections/catalog.d.ts +111 -9
  336. package/dist/connections/catalog.js +86 -30
  337. package/dist/db/create-get-db.js +18 -0
  338. package/dist/db/ddl-guard.d.ts +0 -4
  339. package/dist/db/ddl-guard.js +46 -0
  340. package/dist/deploy/build.d.ts +19 -0
  341. package/dist/deploy/build.js +190 -16
  342. package/dist/deploy/function-bundle.d.ts +45 -0
  343. package/dist/deploy/function-bundle.js +337 -0
  344. package/dist/deploy/workspace-deploy.js +19 -1
  345. package/dist/eject/workspace-connections.js +4 -0
  346. package/dist/email-catalog/registry.d.ts +4 -0
  347. package/dist/email-catalog/registry.js +79 -14
  348. package/dist/extensions/content-patch.js +14 -2
  349. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  350. package/dist/integrations/plugin.d.ts +10 -0
  351. package/dist/integrations/plugin.js +40 -6
  352. package/dist/jobs/actions/run-automation-now.d.ts +2 -1
  353. package/dist/jobs/actions/run-automation-now.js +11 -5
  354. package/dist/jobs/frontmatter.js +53 -2
  355. package/dist/jobs/run-now.d.ts +11 -1
  356. package/dist/jobs/run-now.js +42 -7
  357. package/dist/jobs/scheduler.d.ts +1 -0
  358. package/dist/jobs/scheduler.js +2 -1
  359. package/dist/localization/core-messages/ar-SA.js +3 -0
  360. package/dist/localization/core-messages/de-DE.js +3 -0
  361. package/dist/localization/core-messages/en-US.d.ts +3 -0
  362. package/dist/localization/core-messages/en-US.js +3 -0
  363. package/dist/localization/core-messages/es-ES.js +3 -0
  364. package/dist/localization/core-messages/fr-FR.js +3 -0
  365. package/dist/localization/core-messages/hi-IN.js +3 -0
  366. package/dist/localization/core-messages/ja-JP.js +3 -0
  367. package/dist/localization/core-messages/ko-KR.js +3 -0
  368. package/dist/localization/core-messages/pt-BR.js +3 -0
  369. package/dist/localization/core-messages/zh-CN.js +3 -0
  370. package/dist/localization/core-messages/zh-TW.js +3 -0
  371. package/dist/localization/core-messages.d.ts +3 -0
  372. package/dist/localization/default-messages.d.ts +2 -5
  373. package/dist/localization/default-messages.js +2 -5
  374. package/dist/mcp/build-server.d.ts +9 -0
  375. package/dist/mcp/build-server.js +37 -8
  376. package/dist/mcp-client/oauth-client.d.ts +7 -0
  377. package/dist/mcp-client/oauth-client.js +12 -1
  378. package/dist/oauth-tokens/index.d.ts +1 -1
  379. package/dist/oauth-tokens/index.js +1 -1
  380. package/dist/oauth-tokens/lifecycle.d.ts +11 -0
  381. package/dist/oauth-tokens/lifecycle.js +17 -0
  382. package/dist/oauth-tokens/store.js +7 -0
  383. package/dist/observability/posthog-ai.d.ts +15 -1
  384. package/dist/observability/posthog-ai.js +24 -12
  385. package/dist/observability/routes.d.ts +3 -3
  386. package/dist/observability/traces.js +158 -28
  387. package/dist/onboarding/app-profile.js +2 -2
  388. package/dist/onboarding/plugin.js +10 -2
  389. package/dist/onboarding/types.d.ts +2 -0
  390. package/dist/provider-api/actions/custom-provider-registration.d.ts +1 -1
  391. package/dist/provider-api/actions/provider-api.d.ts +2 -2
  392. package/dist/provider-api/index.js +2 -0
  393. package/dist/resources/store.js +13 -4
  394. package/dist/scripts/agent-engines/list-agent-engines.js +29 -3
  395. package/dist/secrets/register-framework-secrets.js +1 -1
  396. package/dist/server/action-discovery.js +5 -0
  397. package/dist/server/agent-chat/action-filters-a2a.d.ts +27 -3
  398. package/dist/server/agent-chat/action-filters-a2a.js +48 -9
  399. package/dist/server/agent-chat-plugin.js +36 -9
  400. package/dist/server/agent-discovery.js +43 -20
  401. package/dist/server/auth.js +18 -16
  402. package/dist/server/beta-opt-out-html.js +45 -4
  403. package/dist/server/better-auth-instance.d.ts +2 -1
  404. package/dist/server/better-auth-instance.js +23 -7
  405. package/dist/server/builder-design-systems.d.ts +4 -1
  406. package/dist/server/builder-design-systems.js +87 -6
  407. package/dist/server/builder-oauth.d.ts +1 -7
  408. package/dist/server/builder-oauth.js +80 -163
  409. package/dist/server/core-routes-plugin.d.ts +29 -0
  410. package/dist/server/core-routes-plugin.js +100 -20
  411. package/dist/server/default-plugins.d.ts +18 -0
  412. package/dist/server/default-plugins.js +22 -0
  413. package/dist/server/edge.d.ts +1 -0
  414. package/dist/server/edge.js +1 -0
  415. package/dist/server/framework-request-handler.js +31 -3
  416. package/dist/server/google-oauth.d.ts +3 -0
  417. package/dist/server/google-oauth.js +3 -0
  418. package/dist/server/index.d.ts +4 -4
  419. package/dist/server/index.js +3 -3
  420. package/dist/server/onboarding-html.js +37 -3
  421. package/dist/server/realtime-token.d.ts +1 -1
  422. package/dist/server/transcribe-voice.d.ts +1 -1
  423. package/dist/server/workspace-provider-oauth.d.ts +6 -1
  424. package/dist/server/workspace-provider-oauth.js +72 -20
  425. package/dist/shared/agent-sidebar-url.d.ts +1 -0
  426. package/dist/shared/agent-sidebar-url.js +1 -0
  427. package/dist/shared/environment-lanes.d.ts +2 -0
  428. package/dist/shared/environment-lanes.js +3 -1
  429. package/dist/shared/index.d.ts +3 -1
  430. package/dist/shared/index.js +3 -1
  431. package/dist/shared/surface-visibility.d.ts +31 -0
  432. package/dist/shared/surface-visibility.js +57 -0
  433. package/dist/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
  434. package/dist/templates/chat/app/routes.ts +3 -1
  435. package/dist/templates/chat/vitest.config.ts +8 -0
  436. package/dist/templates/default/.agents/skills/secrets/SKILL.md +11 -4
  437. package/dist/templates/default/app/routes.ts +3 -1
  438. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +11 -4
  439. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +11 -4
  440. package/dist/tracking/registry.d.ts +11 -0
  441. package/dist/tracking/registry.js +5 -2
  442. package/dist/triggers/actions.js +10 -3
  443. package/dist/usage/store.js +23 -3
  444. package/dist/user-profile/actions/get-auth-methods.js +18 -5
  445. package/dist/vite/client.js +24 -5
  446. package/docs/content/actions-access-control.mdx +43 -4
  447. package/docs/content/actions-defining.mdx +16 -0
  448. package/docs/content/template-clips-capture-everywhere.mdx +29 -30
  449. package/docs/content/template-clips-developers.mdx +4 -4
  450. package/docs/content/template-clips.mdx +3 -1
  451. package/package.json +2 -2
  452. package/src/eject/workspace-connections.ts +4 -0
  453. package/src/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
  454. package/src/templates/chat/app/routes.ts +3 -1
  455. package/src/templates/chat/vitest.config.ts +8 -0
  456. package/src/templates/default/.agents/skills/secrets/SKILL.md +11 -4
  457. package/src/templates/default/app/routes.ts +3 -1
  458. package/src/templates/headless/.agents/skills/secrets/SKILL.md +11 -4
  459. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +11 -4
@@ -87,3 +87,340 @@ function copyTree(src, dest, placeFile, ancestorRealPaths = new Set()) {
87
87
  }
88
88
  }
89
89
  }
90
+ /** Nitro's SSR page/asset route entries. Only the `/*` server function uses these. */
91
+ const SSR_ENTRY_FILES = [
92
+ "_...page_.get.mjs",
93
+ "_...page_.head.mjs",
94
+ "_...asset_.get.mjs",
95
+ ];
96
+ // Rolldown emits `import(`./x.mjs`)` with BACKTICKS; a quote-only pattern
97
+ // under-reports the graph by tens of MB and would delete reachable chunks.
98
+ const SPECIFIER = /(?:from|import|require)\s*\(?\s*(["'`])([^"'`]*)\1/g;
99
+ // `import(`${pkg}/server`)` is a BARE specifier and can never name a local
100
+ // chunk. Only a specifier that is relative BEFORE the interpolation is
101
+ // genuinely unresolvable, and that is when we must not guess.
102
+ const RELATIVE_INTERPOLATED = /import\s*\(\s*`\s*[./][^`]*\$\{/;
103
+ /**
104
+ * Every file reachable from `main.mjs`, treating `removed` as already deleted.
105
+ * Returns null when the graph cannot be resolved statically, so the caller
106
+ * prunes nothing rather than deleting a chunk something still imports.
107
+ */
108
+ function reachableFiles(dir, removed) {
109
+ const seen = new Set();
110
+ const visit = (file) => {
111
+ if (seen.has(file))
112
+ return true;
113
+ seen.add(file);
114
+ let src;
115
+ try {
116
+ src = fs.readFileSync(file, "utf-8");
117
+ }
118
+ catch {
119
+ return true;
120
+ }
121
+ if (RELATIVE_INTERPOLATED.test(src))
122
+ return false;
123
+ SPECIFIER.lastIndex = 0;
124
+ for (let m; (m = SPECIFIER.exec(src));) {
125
+ const spec = m[2];
126
+ if (!spec.startsWith("."))
127
+ continue;
128
+ const base = path.resolve(path.dirname(file), spec);
129
+ const hit = [
130
+ base,
131
+ `${base}.mjs`,
132
+ `${base}.js`,
133
+ path.join(base, "index.mjs"),
134
+ ].find((c) => fs.existsSync(c) && fs.statSync(c).isFile());
135
+ // Docs prose inside the bundle contains path-shaped strings that are not
136
+ // imports; a specifier resolving to nothing is not an edge.
137
+ if (!hit)
138
+ continue;
139
+ if (removed.has(path.relative(dir, hit)))
140
+ continue;
141
+ if (!visit(hit))
142
+ return false;
143
+ }
144
+ return true;
145
+ };
146
+ return visit(path.join(dir, "main.mjs")) ? seen : null;
147
+ }
148
+ /**
149
+ * Drop the SSR page/asset island from a clone that can never route to it.
150
+ *
151
+ * The background and integration-recovery entries overwrite `url.pathname`
152
+ * unconditionally before delegating to `main.mjs`, so those clones cannot reach
153
+ * the page or asset handlers — yet each shipped a full copy of that island, and
154
+ * Netlify zips and uploads every function separately. Deleting from the clone is
155
+ * safe under hard links: only an in-place WRITE would reach the source bundle.
156
+ */
157
+ export function pruneSsrIslandFromRewritingClone(dest, entryText) {
158
+ if (!/^\s*url\.pathname\s*=/m.test(entryText)) {
159
+ throw new Error("[deploy] SSR prune requires a clone entry that rewrites url.pathname unconditionally");
160
+ }
161
+ const full = reachableFiles(dest, new Set());
162
+ const lean = reachableFiles(dest, new Set(SSR_ENTRY_FILES));
163
+ if (!full || !lean) {
164
+ console.warn("[deploy] SSR prune skipped: unresolvable relative dynamic import in the clone.");
165
+ return 0;
166
+ }
167
+ let bytes = 0;
168
+ for (const file of full) {
169
+ if (lean.has(file))
170
+ continue;
171
+ try {
172
+ bytes += fs.statSync(file).size;
173
+ fs.rmSync(file, { force: true });
174
+ }
175
+ catch {
176
+ // coercion-ok: a file already gone is the goal state, and its bytes were
177
+ // read before the unlink, so the reported total stays honest.
178
+ }
179
+ }
180
+ for (const name of SSR_ENTRY_FILES) {
181
+ fs.rmSync(path.join(dest, name), { force: true });
182
+ }
183
+ return bytes;
184
+ }
185
+ /**
186
+ * Read a package's manifest, or null when it has none. Shared with build.ts's
187
+ * copyRuntimePackageTree, which is why this lives here rather than there:
188
+ * build.ts already imports from this file, and the reverse import would be
189
+ * circular.
190
+ */
191
+ export function readPackageManifest(packageDir) {
192
+ const packageJsonPath = path.join(packageDir, "package.json");
193
+ if (!fs.existsSync(packageJsonPath))
194
+ return null;
195
+ const manifest = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
196
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
197
+ return null;
198
+ }
199
+ return manifest;
200
+ }
201
+ /**
202
+ * Every installed package name directly under `nodeModulesDir`, scoped packages
203
+ * expanded to `@scope/name`.
204
+ *
205
+ * Dot-prefixed entries are npm/pnpm bookkeeping, not packages — `.bin` holds
206
+ * executable links and has no manifest, so treating it as a package makes the
207
+ * closure walk throw on a perfectly normal bundle.
208
+ */
209
+ function listTopLevelPackageNames(nodeModulesDir) {
210
+ if (!fs.existsSync(nodeModulesDir))
211
+ return [];
212
+ const names = [];
213
+ for (const entry of fs.readdirSync(nodeModulesDir, { withFileTypes: true })) {
214
+ if (!entry.isDirectory())
215
+ continue;
216
+ if (entry.name.startsWith("."))
217
+ continue;
218
+ if (!entry.name.startsWith("@")) {
219
+ names.push(entry.name);
220
+ continue;
221
+ }
222
+ const scopeDir = path.join(nodeModulesDir, entry.name);
223
+ for (const scoped of fs.readdirSync(scopeDir, { withFileTypes: true })) {
224
+ if (scoped.isDirectory())
225
+ names.push(`${entry.name}/${scoped.name}`);
226
+ }
227
+ }
228
+ return names;
229
+ }
230
+ /**
231
+ * Every package name reachable from `roots` by walking the given manifest
232
+ * fields — the same edge copyRuntimePackageTree (build.ts) follows to build the
233
+ * browser runtime tree in the first place, so a package that tree copied in is
234
+ * exactly a package this walk can find again.
235
+ *
236
+ * The two callers want deliberately different widths. "What might be dead"
237
+ * walks `dependencies` only, so a dev/optional/peer listing cannot pull an
238
+ * unrelated package into the browser's closure. "What must live" also walks
239
+ * `optionalDependencies`: an installed optional dependency is one a surviving
240
+ * package may still require at runtime, and deleting it is unrecoverable
241
+ * whereas keeping it costs bytes. Err narrow when deciding what to delete and
242
+ * wide when deciding what to spare.
243
+ *
244
+ * A dependency name with no directory here was never installed — the same
245
+ * BARE_RUNTIME_ONLY_PACKAGES / SERVERLESS_FUNCTION_PACKAGE_DENYLIST exclusions
246
+ * upstream leave real, expected gaps in a package's own "dependencies" field,
247
+ * and a gap is not a resolution failure. A directory that DOES exist without a
248
+ * readable package.json is one: the closure can no longer be trusted, so that
249
+ * throws instead of guessing which way is safe.
250
+ */
251
+ function reachablePackageNames(nodeModulesDir, roots, fields = ["dependencies"]) {
252
+ const seen = new Set();
253
+ const visit = (packageName) => {
254
+ if (seen.has(packageName))
255
+ return;
256
+ seen.add(packageName);
257
+ const packageDir = path.join(nodeModulesDir, ...packageName.split("/"));
258
+ if (!fs.existsSync(packageDir))
259
+ return;
260
+ const manifest = readPackageManifest(packageDir);
261
+ if (!manifest) {
262
+ throw new Error(`[deploy] ${packageDir} has no readable package.json; cannot compute the runtime dependency closure it belongs to.`);
263
+ }
264
+ for (const field of fields) {
265
+ const dependencies = manifest[field];
266
+ if (!dependencies || typeof dependencies !== "object")
267
+ continue;
268
+ for (const dependencyName of Object.keys(dependencies)) {
269
+ visit(dependencyName);
270
+ }
271
+ }
272
+ };
273
+ for (const root of roots)
274
+ visit(root);
275
+ return seen;
276
+ }
277
+ /**
278
+ * Drop a now-empty `@scope` directory. Never remove a scope that still holds a
279
+ * package: an unrelated `@sparticuz/*` some other dependency needs lives in the
280
+ * same scope as the browser runtime, and the closure walk cannot protect what a
281
+ * scope-wide delete takes.
282
+ */
283
+ function removeScopeIfEmpty(nodeModulesDir, packageDir) {
284
+ const scopeDir = path.dirname(packageDir);
285
+ if (scopeDir === nodeModulesDir)
286
+ return;
287
+ if (fs.readdirSync(scopeDir).length > 0)
288
+ return;
289
+ fs.rmSync(scopeDir, { recursive: true, force: true });
290
+ }
291
+ /**
292
+ * The browser runtime copied into serverless functions, by exact package name.
293
+ *
294
+ * Exact names, never the `@sparticuz` scope: another dependency can install an
295
+ * unrelated package into the same scope, and a scope-wide delete takes it with
296
+ * no way for the closure walk to prove it is still needed. build.ts imports
297
+ * this rather than declaring its own copy, so the list that is copied in and
298
+ * the list that is pruned out cannot drift apart.
299
+ */
300
+ export const SERVERLESS_BROWSER_RUNTIME_PACKAGES = [
301
+ // chromium-min, not chromium: the full package embeds a 66MB browser in every
302
+ // emitted function, paid on every cold start to serve a fallback most requests
303
+ // never take. The min package is 46KB and fetches the same pinned pack on
304
+ // first launch. See chromiumPackUrl() in creative-context's rendered-page.
305
+ // guard:allow-serverless-function-payload — -66.4MB per function, replaces "@sparticuz/chromium"
306
+ "@sparticuz/chromium-min",
307
+ "playwright-core",
308
+ ];
309
+ /**
310
+ * Any path whose handler can reach an agent turn, directly or transitively.
311
+ * `_process-run` is the obvious one, but the integration sweep and the
312
+ * recurring-jobs sweep both resume tasks that run a full agent turn with the
313
+ * app's whole tool surface, so a path-shape check alone is not enough.
314
+ */
315
+ const AGENT_CAPABLE_PATH = /_process-run|_process-task|\/integrations\/|recurring-jobs/;
316
+ function dirSize(dir) {
317
+ let total = 0;
318
+ const stack = [dir];
319
+ while (stack.length > 0) {
320
+ const cur = stack.pop();
321
+ let entries;
322
+ try {
323
+ entries = fs.readdirSync(cur, { withFileTypes: true });
324
+ }
325
+ catch {
326
+ continue;
327
+ }
328
+ for (const entry of entries) {
329
+ const full = path.join(cur, entry.name);
330
+ if (entry.isDirectory())
331
+ stack.push(full);
332
+ else {
333
+ try {
334
+ total += fs.statSync(full).size;
335
+ }
336
+ catch {
337
+ // coercion-ok: an unreadable entry contributes no measurable bytes and
338
+ // must not abort a size report.
339
+ }
340
+ }
341
+ }
342
+ }
343
+ return total;
344
+ }
345
+ /**
346
+ * Drop the serverless browser runtime — and everything installed only because
347
+ * the runtime needed it — from a clone that can never run an agent turn.
348
+ *
349
+ * Playwright and @sparticuz/chromium are reached only through NON-LITERAL
350
+ * dynamic imports in creative-context's rendered-page connector, so no static
351
+ * walk can prove them dead. The caller asserts it instead, and this refuses the
352
+ * prune when the entry rewrites to a route that could reach an agent — a
353
+ * scheduled report sweep can drop the runtime, an agent worker cannot.
354
+ *
355
+ * Deleting just the two known directories used to leave their whole
356
+ * dependency closure behind — packages like tar-fs and pump that exist in
357
+ * `node_modules` for no other reason than @sparticuz/chromium-min or
358
+ * playwright-core needing them. Those are orphaned the instant the runtime is
359
+ * gone, so this walks the closure and removes it too, keeping only what a
360
+ * still-present, unrelated package also depends on.
361
+ */
362
+ export function pruneBrowserRuntimeFromNonAgentClone(dest, entryText) {
363
+ if (!/^\s*url\.pathname\s*=/m.test(entryText)) {
364
+ throw new Error("[deploy] browser-runtime prune requires a clone entry that rewrites url.pathname unconditionally");
365
+ }
366
+ if (AGENT_CAPABLE_PATH.test(entryText)) {
367
+ throw new Error("[deploy] refusing to prune the browser runtime from a clone whose entry names an agent-capable route");
368
+ }
369
+ const nodeModulesDir = path.join(dest, "node_modules");
370
+ const browserRoots = SERVERLESS_BROWSER_RUNTIME_PACKAGES.filter((name) => fs.existsSync(path.join(nodeModulesDir, ...name.split("/"))));
371
+ let bytes = 0;
372
+ if (browserRoots.length > 0) {
373
+ // Everything reachable from the runtime being deleted — e.g.
374
+ // @sparticuz/chromium-min's tar-fs, and everything tar-fs needs. A member
375
+ // of this set is dead UNLESS some other, unrelated top-level package also
376
+ // depends on it — `stillNeeded` proves that by walking every package that
377
+ // ISN'T itself downstream of the runtime, so it can never trivially
378
+ // "prove itself" reachable.
379
+ const browserClosure = reachablePackageNames(nodeModulesDir, browserRoots);
380
+ // The function's own manifest is a liveness root too. Its dependencies are
381
+ // what the emitted bundle traced directly, so a package that is BOTH in the
382
+ // browser closure and imported by the surviving server has no other package
383
+ // vouching for it and would be deleted out from under a live import. The
384
+ // browser roots are excluded — they are declared there as well, and keeping
385
+ // them would make the whole closure look alive.
386
+ const ownManifest = readPackageManifest(dest);
387
+ const ownDependencies = ["dependencies", "optionalDependencies"]
388
+ .flatMap((field) => Object.keys(ownManifest?.[field] ?? {}))
389
+ .filter((name) => !browserRoots.includes(name));
390
+ const otherRoots = [
391
+ ...listTopLevelPackageNames(nodeModulesDir).filter((name) => !browserClosure.has(name)),
392
+ ...ownDependencies,
393
+ ];
394
+ const stillNeeded = reachablePackageNames(nodeModulesDir, otherRoots, [
395
+ "dependencies",
396
+ "optionalDependencies",
397
+ ]);
398
+ for (const packageName of browserClosure) {
399
+ // The roots are deleted below, after their closure.
400
+ if (browserRoots.includes(packageName))
401
+ continue;
402
+ if (stillNeeded.has(packageName))
403
+ continue;
404
+ const packageDir = path.join(nodeModulesDir, ...packageName.split("/"));
405
+ if (!fs.existsSync(packageDir))
406
+ continue;
407
+ bytes += dirSize(packageDir);
408
+ fs.rmSync(packageDir, { recursive: true, force: true });
409
+ removeScopeIfEmpty(nodeModulesDir, packageDir);
410
+ }
411
+ }
412
+ // Delete the roots themselves by package name, never by scope directory: an
413
+ // unrelated @sparticuz/* package that some other dependency still needs would
414
+ // otherwise go with the scope, and `stillNeeded` has no way to protect it.
415
+ // The scope is removed afterwards only once nothing is left in it.
416
+ for (const name of browserRoots) {
417
+ const dir = path.join(nodeModulesDir, ...name.split("/"));
418
+ if (!fs.existsSync(dir))
419
+ continue;
420
+ bytes += dirSize(dir);
421
+ // Hard links: deleting the clone's link never touches the source bundle.
422
+ fs.rmSync(dir, { recursive: true, force: true });
423
+ removeScopeIfEmpty(nodeModulesDir, dir);
424
+ }
425
+ return bytes;
426
+ }
@@ -24,7 +24,7 @@ import { findWorkspaceRoot } from "../scripts/utils.js";
24
24
  import { DEFAULT_WORKSPACE_APP_AUDIENCE, normalizeWorkspaceAppAudience, normalizeWorkspaceAppPathList, workspaceAppAudienceFromPackageJson, workspaceAppRouteAccessFromPackageJson, } from "../shared/workspace-app-audience.js";
25
25
  import { DISPATCH_WORKSPACE_ROOT_REDIRECTS } from "../shared/workspace-app-id.js";
26
26
  import { assertEmittedBackgroundFunctionOnDisk, isRecurringJobsDeployEnabled, } from "./build.js";
27
- import { cloneServerBundleForFunction } from "./function-bundle.js";
27
+ import { cloneServerBundleForFunction, pruneSsrIslandFromRewritingClone, } from "./function-bundle.js";
28
28
  import { collectImmutableAssetPaths, IMMUTABLE_ASSET_CACHE_HEADERS, } from "./immutable-assets.js";
29
29
  const NETLIFY_WORKSPACE_STATIC_DIR = "_workspace_static";
30
30
  const DEFAULT_HOSTED_FEEDBACK_URL = "https://forms.agent-native.com/f/agent-native-feedback/_16ewV";
@@ -753,6 +753,15 @@ export const config = {
753
753
  // is the function entrypoint, mirroring patchNetlifyFunctionEntry.
754
754
  fs.rmSync(path.join(dest, "server.mjs"), { force: true });
755
755
  fs.writeFileSync(path.join(dest, `${backgroundName}.mjs`), server);
756
+ {
757
+ // The clone rewrites url.pathname unconditionally, so it can never
758
+ // route to the SSR page/asset handlers it inherited. Netlify zips and
759
+ // uploads every function separately, so that island is paid for twice.
760
+ const freed = pruneSsrIslandFromRewritingClone(dest, server);
761
+ if (freed > 0) {
762
+ console.log(`[deploy] Pruned ${(freed / 1024 / 1024).toFixed(1)}MB of unroutable SSR modules from ${path.basename(dest)}.`);
763
+ }
764
+ }
756
765
  assertEmittedBackgroundFunctionOnDisk(dest, backgroundName);
757
766
  console.log(`[workspace-deploy] Emitted durable-background function "${backgroundName}" ` +
758
767
  `for app "${app}" with config { background:true } and NO custom path — ` +
@@ -848,6 +857,15 @@ export const config = {
848
857
  };
849
858
  `;
850
859
  fs.writeFileSync(path.join(dest, `${functionName}.mjs`), entry);
860
+ {
861
+ // The clone rewrites url.pathname unconditionally, so it can never route to
862
+ // the SSR page/asset handlers it inherited. Netlify zips and uploads every
863
+ // function separately, so that island is paid for on every deploy.
864
+ const freed = pruneSsrIslandFromRewritingClone(dest, entry);
865
+ if (freed > 0) {
866
+ console.log(`[deploy] Pruned ${(freed / 1024 / 1024).toFixed(1)}MB of unroutable SSR modules from ${path.basename(dest)}.`);
867
+ }
868
+ }
851
869
  }
852
870
  function patchNetlifyFunctionEntry(functionDir, app, workspaceApps, staticDir) {
853
871
  const serverPath = path.join(functionDir, "server.mjs");
@@ -6,7 +6,11 @@ export const WORKSPACE_CONNECTION_PROVIDERS = [
6
6
  "figma",
7
7
  "notion",
8
8
  "gmail",
9
+ "google_calendar",
9
10
  "google_drive",
11
+ "google_docs",
12
+ "google_sheets",
13
+ "google_slides",
10
14
  "hubspot",
11
15
  "salesforce",
12
16
  "jira",
@@ -65,6 +65,10 @@ export type RegisteredTransactionalEmail = TransactionalEmailDefinition & {
65
65
  * export it and reuse `id` when sending.
66
66
  */
67
67
  export declare function defineTransactionalEmail(definition: TransactionalEmailDefinition): RegisteredTransactionalEmail;
68
+ /** Register a catalog as one atomic operation, allowing safe HMR refreshes. */
69
+ export declare function defineTransactionalEmails(definitions: readonly TransactionalEmailDefinition[]): RegisteredTransactionalEmail[];
70
+ /** Replace one app-owned catalog snapshot after validating the full replacement. */
71
+ export declare function replaceTransactionalEmails(ownerApp: string, idPrefix: string, definitions: readonly TransactionalEmailDefinition[]): RegisteredTransactionalEmail[];
68
72
  /** Every registered email, sorted by app then name. */
69
73
  export declare function listTransactionalEmails(): RegisteredTransactionalEmail[];
70
74
  export declare function getTransactionalEmail(id: string): RegisteredTransactionalEmail | undefined;
@@ -23,27 +23,93 @@
23
23
  */
24
24
  import { getAppSlug } from "../server/app-name.js";
25
25
  const registry = new Map();
26
- /** Source definitions, so re-registering the same one is a no-op rather than a clash. */
27
- const sources = new Map();
26
+ function resolveDefinition(definition) {
27
+ return {
28
+ ...definition,
29
+ app: definition.app ?? getAppSlug() ?? "unknown",
30
+ };
31
+ }
32
+ function assertCanRegister(resolved, existing) {
33
+ if (existing &&
34
+ (existing.app !== resolved.app ||
35
+ existing.name !== resolved.name ||
36
+ existing.trigger !== resolved.trigger ||
37
+ existing.recipient !== resolved.recipient ||
38
+ existing.recipientLabel !== resolved.recipientLabel ||
39
+ existing.sender !== resolved.sender ||
40
+ existing.senderLabel !== resolved.senderLabel)) {
41
+ // Two emails sharing an id would silently merge their metrics and make the
42
+ // catalog claim one exists when the other actually sent.
43
+ throw new Error(`Duplicate transactional email id "${resolved.id}". Ids must be unique across the app.`);
44
+ }
45
+ }
28
46
  /**
29
47
  * Register a transactional email. Returns the definition so the call site can
30
48
  * export it and reuse `id` when sending.
31
49
  */
32
50
  export function defineTransactionalEmail(definition) {
33
- const existing = sources.get(definition.id);
34
- if (existing && existing !== definition) {
35
- // Two emails sharing an id would silently merge their metrics and make the
36
- // catalog claim one exists when the other actually sent.
37
- throw new Error(`Duplicate transactional email id "${definition.id}". Ids must be unique across the app.`);
38
- }
39
- const resolved = {
40
- ...definition,
41
- app: definition.app ?? getAppSlug() ?? "unknown",
42
- };
51
+ const resolved = resolveDefinition(definition);
52
+ // HMR recreates preview functions, so stable catalog metadata is the collision boundary.
53
+ assertCanRegister(resolved, registry.get(resolved.id));
43
54
  registry.set(definition.id, resolved);
44
- sources.set(definition.id, definition);
45
55
  return resolved;
46
56
  }
57
+ function resolveDefinitions(definitions) {
58
+ const resolved = definitions.map(resolveDefinition);
59
+ const seen = new Set();
60
+ for (const definition of resolved) {
61
+ if (seen.has(definition.id)) {
62
+ throw new Error(`Duplicate transactional email id "${definition.id}". Ids must be unique across the app.`);
63
+ }
64
+ seen.add(definition.id);
65
+ }
66
+ return resolved;
67
+ }
68
+ function validateDefinitions(definitions) {
69
+ const resolved = resolveDefinitions(definitions);
70
+ for (const definition of resolved) {
71
+ assertCanRegister(definition, registry.get(definition.id));
72
+ }
73
+ return resolved;
74
+ }
75
+ function commitDefinitions(definitions) {
76
+ for (const definition of definitions) {
77
+ registry.set(definition.id, definition);
78
+ }
79
+ return [...definitions];
80
+ }
81
+ /** Register a catalog as one atomic operation, allowing safe HMR refreshes. */
82
+ export function defineTransactionalEmails(definitions) {
83
+ return commitDefinitions(validateDefinitions(definitions));
84
+ }
85
+ /** Replace one app-owned catalog snapshot after validating the full replacement. */
86
+ export function replaceTransactionalEmails(ownerApp, idPrefix, definitions) {
87
+ const runtimeApp = getAppSlug();
88
+ if (!ownerApp || ownerApp.includes(".") || idPrefix !== `${ownerApp}.`) {
89
+ throw new Error("Transactional email replacement requires an owner app and its exact namespace prefix.");
90
+ }
91
+ const resolved = resolveDefinitions(definitions);
92
+ if (resolved.some(({ id, app }) => app !== ownerApp || !id.startsWith(idPrefix))) {
93
+ throw new Error(`Transactional email replacement must contain only ${ownerApp} email definitions in the "${idPrefix}" scope.`);
94
+ }
95
+ for (const [id, existing] of registry) {
96
+ if (id.startsWith(idPrefix) && existing.app !== ownerApp) {
97
+ throw new Error(`Transactional email replacement cannot modify "${id}" owned by "${existing.app}".`);
98
+ }
99
+ }
100
+ const hasExplicitOwner = resolved.length > 0 && resolved.every(({ app }) => app === ownerApp);
101
+ if ((runtimeApp && runtimeApp !== ownerApp) ||
102
+ (!runtimeApp && !hasExplicitOwner)) {
103
+ throw new Error("Transactional email replacement requires a recognized runtime owner or a non-empty snapshot with explicit owner metadata.");
104
+ }
105
+ const nextIds = new Set(resolved.map(({ id }) => id));
106
+ for (const id of registry.keys()) {
107
+ if (id.startsWith(idPrefix) && !nextIds.has(id)) {
108
+ registry.delete(id);
109
+ }
110
+ }
111
+ return commitDefinitions(resolved);
112
+ }
47
113
  /** Every registered email, sorted by app then name. */
48
114
  export function listTransactionalEmails() {
49
115
  return [...registry.values()].sort((a, b) => a.app.localeCompare(b.app) || a.name.localeCompare(b.name));
@@ -66,5 +132,4 @@ export function renderTransactionalEmailPreview(id) {
66
132
  /** Test seam — drops all registrations. */
67
133
  export function resetTransactionalEmailRegistry() {
68
134
  registry.clear();
69
- sources.clear();
70
135
  }
@@ -46,10 +46,22 @@ async function applyExtensionContentUpdateUnchecked(currentContent, opts) {
46
46
  }
47
47
  export async function formatExtensionHtml(content) {
48
48
  try {
49
- const prettier = await import("prettier");
50
- const formatted = await prettier.format(content, {
49
+ // prettier's main entry `import()`s all 13 parser plugins, so a bundler
50
+ // inlines ~3.5MB of flow/typescript/yaml/markdown parsers just to format
51
+ // HTML. Load the standalone core plus only the plugins the HTML printer
52
+ // reaches, which still formats embedded <style> (postcss) and <script>
53
+ // (babel + estree).
54
+ const [{ format }, ...plugins] = await Promise.all([
55
+ import("prettier/standalone"),
56
+ import("prettier/plugins/html"),
57
+ import("prettier/plugins/postcss"),
58
+ import("prettier/plugins/babel"),
59
+ import("prettier/plugins/estree"),
60
+ ]);
61
+ const formatted = await format(content, {
51
62
  parser: "html",
52
63
  htmlWhitespaceSensitivity: "ignore",
64
+ plugins,
53
65
  });
54
66
  return typeof formatted === "string" ? formatted : content;
55
67
  }
@@ -17,11 +17,11 @@ declare const _default: import("../../action.js").ActionDefinition<{
17
17
  id?: undefined;
18
18
  provider?: undefined;
19
19
  } | {
20
- error?: undefined;
21
20
  configured?: undefined;
22
21
  connectPath?: undefined;
23
22
  url: string;
24
23
  id: string;
25
24
  provider: string;
25
+ error?: undefined;
26
26
  }>;
27
27
  export default _default;
@@ -31,6 +31,16 @@ export declare const BUILT_IN_INTEGRATION_ADAPTER_FACTORIES: readonly [{
31
31
  }];
32
32
  export declare const BUILT_IN_INTEGRATION_ADAPTER_IDS: readonly ("discord" | "email" | "google-docs" | "microsoft-teams" | "slack" | "telegram" | "whatsapp")[];
33
33
  export declare function createBuiltInIntegrationAdapters(): PlatformAdapter[];
34
+ /**
35
+ * Narrow the adapter set to `integrations.platforms`, when a deployment
36
+ * declares one.
37
+ *
38
+ * A configured name that matches no adapter throws instead of being ignored:
39
+ * the whole point of the allow-list is that the operator knows which platforms
40
+ * are live, and silently mounting a set nobody named — or mounting nothing
41
+ * because of a typo — is the failure this switch is supposed to prevent.
42
+ */
43
+ export declare function applyConfiguredPlatformAllowList(adapters: PlatformAdapter[]): PlatformAdapter[];
34
44
  type RemoteCodeCommandEnvelope = {
35
45
  kind?: unknown;
36
46
  ownerEmail?: unknown;