@agent-native/core 0.169.0 → 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 (496) 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/bigquery-table-info.ts +3 -2
  6. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
  7. package/corpus/templates/analytics/actions/search-bigquery-schema.ts +130 -4
  8. package/corpus/templates/analytics/agent-native.config.ts +1 -0
  9. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +36 -23
  10. package/corpus/templates/analytics/app/i18n-data.ts +28 -0
  11. package/corpus/templates/analytics/app/lib/dashboard-visibility.ts +41 -0
  12. package/corpus/templates/analytics/app/pages/Agents.tsx +25 -1
  13. package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +34 -9
  14. package/corpus/templates/analytics/app/routes/$.tsx +10 -0
  15. package/corpus/templates/analytics/app/routes.ts +3 -1
  16. package/corpus/templates/analytics/changelog/2026-08-21-dashboard-filters-keep-other-users-dashboards-out-of-mine.md +6 -0
  17. package/corpus/templates/analytics/changelog/2026-08-22-fixed-the-agent-native-templates-first-party-dashboard-panel.md +6 -0
  18. package/corpus/templates/analytics/changelog/2026-08-22-org-admin-panels-no-longer-report-a-database-error-as-a-per.md +9 -0
  19. package/corpus/templates/analytics/changelog/2026-08-22-slack-requests-report-invalid-inputs.md +6 -0
  20. package/corpus/templates/analytics/changelog/2026-08-22-unknown-analytics-links-now-return-a-real-not-found-status.md +6 -0
  21. package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +2 -1
  22. package/corpus/templates/analytics/docs/schemas/tracked-events.md +2 -0
  23. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +29 -7
  24. package/corpus/templates/analytics/server/handlers/slack.ts +59 -5
  25. package/corpus/templates/analytics/server/lib/db-admin-connections.ts +11 -6
  26. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +132 -2
  27. package/corpus/templates/analytics/server/lib/real-data-actions.ts +14 -3
  28. package/corpus/templates/analytics/server/lib/slack.ts +4 -2
  29. package/corpus/templates/analytics/server/plugins/agent-chat.ts +33 -3
  30. package/corpus/templates/analytics/vitest.config.ts +6 -1
  31. package/corpus/templates/assets/.agents/skills/secrets/SKILL.md +11 -4
  32. package/corpus/templates/assets/app/routes/library.tsx +5 -3
  33. package/corpus/templates/assets/app/routes.ts +3 -1
  34. package/corpus/templates/assets/changelog/2026-08-22-library-page-loads-again-instead-of-crashing-on-open.md +6 -0
  35. package/corpus/templates/assets/vitest.config.ts +6 -1
  36. package/corpus/templates/brain/.agents/skills/secrets/SKILL.md +11 -4
  37. package/corpus/templates/brain/app/components/ui/pointer-lock.ts +1 -0
  38. package/corpus/templates/brain/app/routes/sources.tsx +8 -3
  39. package/corpus/templates/brain/changelog/2026-08-22-tuning-or-adding-a-source-no-longer-leaves-the-sources-page.md +6 -0
  40. package/corpus/templates/brain/server/lib/brain.ts +27 -2
  41. package/corpus/templates/brain/vitest.config.ts +1 -0
  42. package/corpus/templates/calendar/.agents/skills/secrets/SKILL.md +11 -4
  43. package/corpus/templates/calendar/actions/list-booking-links.ts +83 -4
  44. package/corpus/templates/calendar/actions/update-booking-link.ts +4 -3
  45. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +11 -3
  46. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +1 -1
  47. package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +103 -44
  48. package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +28 -24
  49. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +9 -2
  50. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +47 -58
  51. package/corpus/templates/calendar/app/hooks/use-booking-links.ts +5 -1
  52. package/corpus/templates/calendar/app/lib/event-layout.ts +52 -26
  53. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +126 -98
  54. package/corpus/templates/calendar/app/pages/CalendarView.tsx +1 -0
  55. package/corpus/templates/calendar/app/pages/Settings.tsx +88 -81
  56. package/corpus/templates/calendar/app/routes.ts +3 -1
  57. package/corpus/templates/calendar/changelog/2026-08-22-booking-link-visibility-respects-shared-access.md +6 -0
  58. package/corpus/templates/calendar/changelog/2026-08-22-clicking-an-out-of-office-event-s-marker-no-longer-creates-a.md +6 -0
  59. package/corpus/templates/calendar/changelog/2026-08-22-event-detail-views-now-show-your-calendar-timezone.md +6 -0
  60. package/corpus/templates/calendar/changelog/2026-08-22-google-calendar-can-now-connect-with-the-shared-google-oauth.md +6 -0
  61. package/corpus/templates/calendar/changelog/2026-08-22-overlapping-events-now-show-each-others-boundaries.md +6 -0
  62. package/corpus/templates/calendar/changelog/2026-08-22-public-booking-pages-now-offer-time-slots-from-the-host-s-we.md +6 -0
  63. package/corpus/templates/calendar/server/handlers/bookings.ts +5 -7
  64. package/corpus/templates/calendar/server/handlers/google-auth.ts +10 -2
  65. package/corpus/templates/calendar/server/lib/google-calendar.ts +139 -14
  66. package/corpus/templates/calendar/server/onboarding.ts +85 -0
  67. package/corpus/templates/calendar/server/plugins/agent-chat.ts +2 -0
  68. package/corpus/templates/calendar/shared/api.ts +9 -1
  69. package/corpus/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
  70. package/corpus/templates/chat/app/routes.ts +3 -1
  71. package/corpus/templates/chat/vitest.config.ts +8 -0
  72. package/corpus/templates/clips/.agents/skills/secrets/SKILL.md +11 -4
  73. package/corpus/templates/clips/actions/get-meeting.ts +9 -4
  74. package/corpus/templates/clips/actions/list-recordings.ts +16 -3
  75. package/corpus/templates/clips/actions/search-meetings.ts +1 -0
  76. package/corpus/templates/clips/actions/stop-meeting-recording.ts +87 -6
  77. package/corpus/templates/clips/actions/view-screen.ts +8 -1
  78. package/corpus/templates/clips/app/components/library/search-bar.tsx +1 -1
  79. package/corpus/templates/clips/app/components/meetings/meeting-history-row.tsx +32 -1
  80. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +1 -1
  81. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +29 -1
  82. package/corpus/templates/clips/app/i18n/en-US.ts +5 -0
  83. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +33 -4
  84. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +1 -0
  85. package/corpus/templates/clips/app/routes/access-request.approve.tsx +16 -9
  86. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +17 -7
  87. package/corpus/templates/clips/app/routes/share.$shareId.tsx +17 -7
  88. package/corpus/templates/clips/changelog/2026-08-21-camera-bubble-black-circle.md +6 -0
  89. package/corpus/templates/clips/changelog/2026-08-22-screen-recordings-respect-the-microphone-toggle.md +6 -0
  90. package/corpus/templates/clips/changelog/2026-08-22-the-recordings-library-no-longer-fails-right-after-server-start.md +6 -0
  91. package/corpus/templates/clips/changelog/2026-08-23-the-agent-can-see-your-library-right-after-server-start.md +6 -0
  92. package/corpus/templates/clips/chrome-extension/src/background.ts +1 -2
  93. package/corpus/templates/clips/chrome-extension/src/native-transcription.ts +39 -7
  94. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +21 -25
  95. package/corpus/templates/clips/desktop/src/app.tsx +53 -24
  96. package/corpus/templates/clips/desktop/src/lib/bubble-playback.ts +68 -0
  97. package/corpus/templates/clips/desktop/src/lib/recorder.ts +3 -0
  98. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +72 -14
  99. package/corpus/templates/clips/desktop/src/lib/updater.ts +8 -3
  100. package/corpus/templates/clips/desktop/src/overlays/bubble.tsx +162 -20
  101. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +43 -6
  102. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +150 -13
  103. package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +12 -0
  104. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +28 -3
  105. package/corpus/templates/clips/server/lib/emails.ts +17 -33
  106. package/corpus/templates/content/.agents/skills/secrets/SKILL.md +11 -4
  107. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +140 -15
  108. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +12 -14
  109. package/corpus/templates/content/app/components/ui/pointer-lock.ts +1 -0
  110. package/corpus/templates/content/app/hooks/use-content-database.ts +18 -2
  111. package/corpus/templates/content/app/hooks/use-local-storage.ts +38 -22
  112. package/corpus/templates/content/app/i18n-data.ts +25 -0
  113. package/corpus/templates/content/app/routes.ts +3 -1
  114. package/corpus/templates/content/changelog/2026-08-22-code-blocks-inserted-from-the-slash-menu-no-longer-get-stuck.md +6 -0
  115. package/corpus/templates/content/changelog/2026-08-22-the-sidebar-page-tree-no-longer-flashes-empty-after-creating-a-page.md +6 -0
  116. package/corpus/templates/content/docs/solutions/2026-08-21-content-mermaid-rendering-shape.md +233 -0
  117. package/corpus/templates/content/server/plugins/agent-chat.ts +9 -2
  118. package/corpus/templates/content/vitest.config.ts +7 -1
  119. package/corpus/templates/crm/.agents/skills/secrets/SKILL.md +11 -4
  120. package/corpus/templates/design/.agents/skills/secrets/SKILL.md +11 -4
  121. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +1386 -92
  122. package/corpus/templates/design/actions/generate-design.ts +98 -47
  123. package/corpus/templates/design/actions/get-design-system.ts +6 -1
  124. package/corpus/templates/design/actions/refresh-design-system-with-builder.ts +146 -0
  125. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +70 -26
  126. package/corpus/templates/design/app/components/design/EditPanel.tsx +15 -47
  127. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +284 -136
  128. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2060 -144
  129. package/corpus/templates/design/app/components/design/editor/DesignBottomToolbar.tsx +6 -2
  130. package/corpus/templates/design/app/components/design/editor/DesignWorkspaceRail.tsx +4 -4
  131. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +10 -8
  132. package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +19 -2
  133. package/corpus/templates/design/app/components/design/multi-screen/types.ts +9 -2
  134. package/corpus/templates/design/app/components/layout/Header.tsx +15 -0
  135. package/corpus/templates/design/app/components/layout/Layout.tsx +7 -2
  136. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +13 -8
  137. package/corpus/templates/design/app/i18n-data.ts +11 -10
  138. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +12 -0
  139. package/corpus/templates/design/app/lib/design-system-data.ts +68 -0
  140. package/corpus/templates/design/app/pages/DesignEditor.tsx +99 -57
  141. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +39 -8
  142. package/corpus/templates/design/app/pages/DesignSystems.tsx +137 -43
  143. package/corpus/templates/design/app/pages/Index.tsx +3 -1
  144. package/corpus/templates/design/app/pages/Templates.tsx +0 -10
  145. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +10 -0
  146. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +50 -0
  147. package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +1 -1
  148. package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +10 -8
  149. package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +6 -6
  150. package/corpus/templates/design/app/pages/design-editor/commands/apply-pending-visual-styles-with-agent.ts +1 -1
  151. package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +18 -7
  152. package/corpus/templates/design/app/pages/design-editor/commands/escape-hotkey.ts +4 -90
  153. package/corpus/templates/design/app/pages/design-editor/commands/layer-marquee-selection-change.ts +2 -1
  154. package/corpus/templates/design/app/pages/design-editor/commands/layer-selection-change.ts +4 -3
  155. package/corpus/templates/design/app/pages/design-editor/commands/screen-element-select.ts +12 -2
  156. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-style-change.ts +9 -2
  157. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-move-semantic-handoff.ts +1 -1
  158. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-semantic-handoff.ts +1 -1
  159. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-state-semantic-handoff.ts +1 -1
  160. package/corpus/templates/design/app/pages/design-editor/commands/start-sidebar-resize.ts +1 -1
  161. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +11 -1
  162. package/corpus/templates/design/app/pages/design-editor/effects/adopt-db-file-content.ts +11 -19
  163. package/corpus/templates/design/app/pages/design-editor/effects/observe-collab-text.ts +6 -6
  164. package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +1 -1
  165. package/corpus/templates/design/app/pages/design-editor/effects/seed-collab-content.ts +12 -15
  166. package/corpus/templates/design/app/pages/design-editor/preview-navigation.ts +25 -0
  167. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +5 -59
  168. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +8 -0
  169. package/corpus/templates/design/app/pages/design-editor/types.ts +3 -3
  170. package/corpus/templates/design/app/routes.ts +3 -1
  171. package/corpus/templates/design/changelog/2026-08-20-canvas-edits-patch-the-frame-instead-of-rebuilding-it.md +6 -0
  172. package/corpus/templates/design/changelog/2026-08-21-builder-design-system-imports-reconcile-after-indexing.md +6 -0
  173. package/corpus/templates/design/changelog/2026-08-21-draw-mode-needs-shift-y.md +6 -0
  174. package/corpus/templates/design/changelog/2026-08-21-eyedropper-shortcut.md +6 -0
  175. package/corpus/templates/design/changelog/2026-08-21-figma-style-alignment-guides-and-snapping.md +6 -0
  176. package/corpus/templates/design/changelog/2026-08-21-group-transforms-keep-the-selection.md +6 -0
  177. package/corpus/templates/design/changelog/2026-08-21-inspector-fields-keep-focus-on-enter.md +6 -0
  178. package/corpus/templates/design/changelog/2026-08-21-pen-alt-keeps-the-incoming-handle.md +6 -0
  179. package/corpus/templates/design/changelog/2026-08-21-pen-on-the-board-creates-one-vector.md +6 -0
  180. package/corpus/templates/design/changelog/2026-08-21-scale-a-multi-selection-from-the-group-handles.md +6 -0
  181. package/corpus/templates/design/changelog/2026-08-21-scale-resizes-text-with-the-object.md +6 -0
  182. package/corpus/templates/design/changelog/2026-08-21-selecting-another-object-keeps-the-scale-tool-armed.md +6 -0
  183. package/corpus/templates/design/changelog/2026-08-21-the-shape-tool-remembers-the-shape-you-picked.md +6 -0
  184. package/corpus/templates/design/changelog/2026-08-21-vectors-drawn-in-a-frame-land-where-you-drew-them.md +6 -0
  185. package/corpus/templates/design/changelog/2026-08-22-multi-screen-generation-no-longer-loses-a-saved-screens-layo.md +6 -0
  186. package/corpus/templates/design/changelog/2026-08-22-rename-and-search-fields-are-labeled-for-screen-readers.md +6 -0
  187. package/corpus/templates/design/package.json +2 -0
  188. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +311 -0
  189. package/corpus/templates/design/shared/canvas-math.ts +612 -206
  190. package/corpus/templates/design/shared/pen-path.ts +37 -0
  191. package/corpus/templates/design/vitest.config.ts +7 -1
  192. package/corpus/templates/dispatch/.agents/skills/secrets/SKILL.md +11 -4
  193. package/corpus/templates/dispatch/actions/list-workspace-connections.ts +34 -3
  194. package/corpus/templates/dispatch/app/routes/integrations.tsx +55 -21
  195. package/corpus/templates/dispatch/app/routes.ts +3 -1
  196. package/corpus/templates/factory/.agents/skills/secrets/SKILL.md +11 -4
  197. package/corpus/templates/factory/.env.example +2 -7
  198. package/corpus/templates/factory/AGENTS.md +3 -0
  199. package/corpus/templates/factory/README.md +5 -3
  200. package/corpus/templates/factory/actions/create-factory.ts +16 -3
  201. package/corpus/templates/factory/actions/get-slack-feedback-context.ts +2 -0
  202. package/corpus/templates/factory/actions/get-triage-item.ts +2 -0
  203. package/corpus/templates/factory/actions/list-factory-audit.ts +116 -36
  204. package/corpus/templates/factory/actions/list-factory-automations.ts +2 -1
  205. package/corpus/templates/factory/actions/list-triage-items.ts +22 -19
  206. package/corpus/templates/factory/actions/run-factory-automation.ts +7 -3
  207. package/corpus/templates/factory/actions/save-factory-automation.ts +5 -2
  208. package/corpus/templates/factory/actions/save-triage-config.ts +2 -8
  209. package/corpus/templates/factory/actions/start-builder-for-item.ts +21 -0
  210. package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +229 -297
  211. package/corpus/templates/factory/app/components/factory/FactorySettingsView.tsx +397 -247
  212. package/corpus/templates/factory/app/components/factory/FactorySourceSettingsGroup.tsx +33 -0
  213. package/corpus/templates/factory/app/components/factory/NewFactoryForm.tsx +202 -162
  214. package/corpus/templates/factory/app/i18n/en-US.ts +32 -4
  215. package/corpus/templates/factory/app/routes/factory.tsx +84 -11
  216. package/corpus/templates/factory/app/routes.ts +3 -1
  217. package/corpus/templates/factory/changelog/2026-08-21-activity-runs-use-automation-display-names.md +6 -0
  218. package/corpus/templates/factory/changelog/2026-08-21-create-and-settings-source-cards.md +6 -0
  219. package/corpus/templates/factory/changelog/2026-08-21-keep-automations-visible-while-running.md +6 -0
  220. package/corpus/templates/factory/changelog/2026-08-21-nested-factory-automations-can-poll.md +6 -0
  221. package/corpus/templates/factory/changelog/2026-08-21-settings-sticky-unsaved-bar.md +6 -0
  222. package/corpus/templates/factory/server/connectors/credentials.ts +10 -5
  223. package/corpus/templates/factory/server/lib/factory-audit-report.ts +412 -0
  224. package/corpus/templates/factory/server/lib/factory-config-reconcile.ts +321 -0
  225. package/corpus/templates/factory/server/lib/factory-scope.ts +25 -2
  226. package/corpus/templates/factory/server/lib/require-factory-automation.ts +13 -4
  227. package/corpus/templates/factory/server/plugins/factory-migrations.ts +148 -4
  228. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +4 -6
  229. package/corpus/templates/factory/vitest.config.ts +17 -2
  230. package/corpus/templates/forms/.agents/skills/secrets/SKILL.md +11 -4
  231. package/corpus/templates/forms/app/routes/_app.settings.tsx +4 -1
  232. package/corpus/templates/forms/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
  233. package/corpus/templates/forms/server/lib/public-form-ssr.ts +11 -1
  234. package/corpus/templates/forms/vitest.config.ts +1 -0
  235. package/corpus/templates/macros/.agents/skills/secrets/SKILL.md +11 -4
  236. package/corpus/templates/macros/app/routes.ts +3 -1
  237. package/corpus/templates/macros/vitest.config.ts +6 -1
  238. package/corpus/templates/mail/.agents/skills/secrets/SKILL.md +11 -4
  239. package/corpus/templates/mail/actions/manage-gmail-filters.ts +10 -2
  240. package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +100 -37
  241. package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +14 -2
  242. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +66 -36
  243. package/corpus/templates/mail/app/hooks/use-emails.ts +13 -3
  244. package/corpus/templates/mail/app/hooks/use-google-auth.ts +2 -0
  245. package/corpus/templates/mail/app/lib/google-oauth-setup.ts +17 -0
  246. package/corpus/templates/mail/app/pages/InboxPage.tsx +69 -9
  247. package/corpus/templates/mail/app/routes/$.tsx +9 -0
  248. package/corpus/templates/mail/app/routes/$view.$threadId.tsx +23 -1
  249. package/corpus/templates/mail/app/routes/$view.tsx +47 -1
  250. package/corpus/templates/mail/app/routes.ts +3 -1
  251. package/corpus/templates/mail/changelog/2026-08-22-gmail-can-now-connect-with-the-shared-google-oauth.md +6 -0
  252. package/corpus/templates/mail/changelog/2026-08-22-gmail-filters-now-shows-the-real-error.md +6 -0
  253. package/corpus/templates/mail/changelog/2026-08-22-unknown-mail-urls-now-show-the-404-page.md +6 -0
  254. package/corpus/templates/mail/server/handlers/google-auth.ts +11 -2
  255. package/corpus/templates/mail/server/lib/google-auth.ts +92 -12
  256. package/corpus/templates/mail/server/onboarding.ts +66 -18
  257. package/corpus/templates/mail/vitest.config.ts +6 -1
  258. package/corpus/templates/plan/.agents/skills/secrets/SKILL.md +11 -4
  259. package/corpus/templates/plan/app/routes/settings.tsx +4 -1
  260. package/corpus/templates/plan/app/routes.ts +3 -1
  261. package/corpus/templates/plan/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
  262. package/corpus/templates/plan/netlify.toml +1 -1
  263. package/corpus/templates/slides/.agents/skills/secrets/SKILL.md +11 -4
  264. package/corpus/templates/slides/actions/patch-deck.ts +79 -10
  265. package/corpus/templates/slides/actions/update-slide.ts +11 -1
  266. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +14 -4
  267. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +10 -52
  268. package/corpus/templates/slides/app/components/editor/GoogleDocImportHint.tsx +8 -53
  269. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +9 -75
  270. package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +8 -60
  271. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +10 -2
  272. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +19 -7
  273. package/corpus/templates/slides/app/lib/sanitize-slide-html.ts +49 -36
  274. package/corpus/templates/slides/app/routes/access-request.approve.tsx +16 -9
  275. package/corpus/templates/slides/app/routes.ts +3 -1
  276. package/corpus/templates/slides/changelog/2026-08-22-already-uploaded-chat-images-are-no-longer-dropped.md +6 -0
  277. package/corpus/templates/slides/changelog/2026-08-22-google-drive-and-slides-can-now-connect-with-the-shared-google-oauth.md +6 -0
  278. package/corpus/templates/slides/changelog/2026-08-22-multi-slide-repairs-now-report-slides-that-still-overflow.md +6 -0
  279. package/corpus/templates/slides/changelog/2026-08-22-update-slide-now-reports-which-batched-edit-was-skipped.md +6 -0
  280. package/corpus/templates/slides/server/handlers/google-docs-auth.ts +52 -1
  281. package/corpus/templates/slides/server/lib/chat-attachments.ts +29 -3
  282. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +66 -16
  283. package/corpus/templates/slides/server/lib/slide-content-patch.ts +13 -2
  284. package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -2
  285. package/corpus/templates/slides/tsconfig.json +5 -0
  286. package/corpus/templates/slides/vitest.config.ts +6 -1
  287. package/corpus/templates/tasks/.agents/skills/secrets/SKILL.md +11 -4
  288. package/corpus/templates/tasks/app/routes/settings.tsx +4 -1
  289. package/corpus/templates/tasks/app/routes.ts +3 -1
  290. package/corpus/templates/tasks/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
  291. package/corpus/templates/tasks/vitest.config.ts +7 -1
  292. package/dist/action.d.ts +91 -0
  293. package/dist/action.js +50 -0
  294. package/dist/agent/abort-reasons.d.ts +38 -0
  295. package/dist/agent/abort-reasons.js +49 -0
  296. package/dist/agent/engine/ai-sdk-engine.js +8 -5
  297. package/dist/agent/engine/anthropic-engine.js +7 -5
  298. package/dist/agent/engine/credential-errors.d.ts +1 -1
  299. package/dist/agent/engine/credential-errors.js +2 -2
  300. package/dist/agent/engine/first-event-timeout.d.ts +42 -17
  301. package/dist/agent/engine/first-event-timeout.js +67 -25
  302. package/dist/agent/production-agent.d.ts +23 -0
  303. package/dist/agent/production-agent.js +107 -102
  304. package/dist/agent/run-loop-with-resume.d.ts +2 -28
  305. package/dist/agent/run-loop-with-resume.js +25 -40
  306. package/dist/agent/run-manager.js +5 -4
  307. package/dist/agent/run-store.d.ts +55 -0
  308. package/dist/agent/run-store.js +124 -1
  309. package/dist/agent/tool-search.js +2 -1
  310. package/dist/agent/types.d.ts +9 -4
  311. package/dist/app-config/configuration-error.d.ts +11 -0
  312. package/dist/app-config/configuration-error.js +14 -0
  313. package/dist/app-config/index.d.ts +1 -0
  314. package/dist/app-config/index.js +1 -0
  315. package/dist/app-config/integrations.d.ts +1 -0
  316. package/dist/app-config/integrations.js +10 -0
  317. package/dist/app-config/plugins.d.ts +34 -0
  318. package/dist/app-config/plugins.js +39 -0
  319. package/dist/app-config/run-lifecycle-invariants.d.ts +18 -9
  320. package/dist/app-config/run-lifecycle-invariants.js +18 -25
  321. package/dist/app-config/schema.d.ts +16 -0
  322. package/dist/app-config/schema.js +2 -0
  323. package/dist/chat-threads/schema-migrations.js +20 -0
  324. package/dist/chat-threads/store.js +21 -4
  325. package/dist/cli/native-dependencies.d.ts +4 -0
  326. package/dist/cli/native-dependencies.js +15 -0
  327. package/dist/client/AssistantChat.js +47 -15
  328. package/dist/client/ConnectBuilderCard.d.ts +3 -1
  329. package/dist/client/ConnectBuilderCard.js +6 -3
  330. package/dist/client/EnvironmentBadge.d.ts +1 -1
  331. package/dist/client/EnvironmentBadge.js +40 -5
  332. package/dist/client/MultiTabAssistantChat.d.ts +1 -1
  333. package/dist/client/MultiTabAssistantChat.js +35 -14
  334. package/dist/client/agent-chat/index.d.ts +1 -1
  335. package/dist/client/agent-chat/index.js +1 -1
  336. package/dist/client/agent-page/use-jobs.d.ts +2 -1
  337. package/dist/client/agent-page/use-jobs.js +3 -5
  338. package/dist/client/agent-sidebar-state.js +3 -1
  339. package/dist/client/blocks/library/code-tabs.js +5 -0
  340. package/dist/client/blocks/library/code.js +1 -0
  341. package/dist/client/chat/message-components.d.ts +7 -0
  342. package/dist/client/chat/message-components.js +1 -1
  343. package/dist/client/chat/run-recovery.js +5 -2
  344. package/dist/client/chat/tool-call-display.js +9 -3
  345. package/dist/client/chat-first/apps-rail.js +2 -2
  346. package/dist/client/chat-model-groups.d.ts +12 -0
  347. package/dist/client/chat-model-groups.js +3 -2
  348. package/dist/client/error-format.js +12 -0
  349. package/dist/client/integrations/GoogleProductLogo.d.ts +5 -0
  350. package/dist/client/integrations/GoogleProductLogo.js +45 -0
  351. package/dist/client/integrations/index.d.ts +2 -0
  352. package/dist/client/integrations/index.js +2 -0
  353. package/dist/client/integrations/workspace-provider-oauth.d.ts +8 -0
  354. package/dist/client/integrations/workspace-provider-oauth.js +13 -0
  355. package/dist/client/navigation/index.d.ts +1 -0
  356. package/dist/client/navigation/index.js +1 -0
  357. package/dist/client/resources/mcp-integration-catalog.js +0 -29
  358. package/dist/client/route-state.js +6 -0
  359. package/dist/client/settings/SettingsTabsPage.js +10 -6
  360. package/dist/client/sharing/ShareButton.js +11 -2
  361. package/dist/client/use-chat-models.d.ts +6 -0
  362. package/dist/client/use-chat-models.js +10 -0
  363. package/dist/client/use-db-sync.js +27 -9
  364. package/dist/client/workspace-app-navigation.d.ts +10 -0
  365. package/dist/client/workspace-app-navigation.js +38 -0
  366. package/dist/collab/struct-routes.d.ts +1 -1
  367. package/dist/connections/catalog.d.ts +111 -9
  368. package/dist/connections/catalog.js +86 -30
  369. package/dist/db/create-get-db.js +18 -0
  370. package/dist/db/ddl-guard.d.ts +0 -4
  371. package/dist/db/ddl-guard.js +46 -0
  372. package/dist/deploy/build.d.ts +32 -0
  373. package/dist/deploy/build.js +241 -16
  374. package/dist/deploy/function-bundle.d.ts +45 -0
  375. package/dist/deploy/function-bundle.js +337 -0
  376. package/dist/deploy/workspace-deploy.js +19 -1
  377. package/dist/eject/workspace-connections.js +4 -0
  378. package/dist/email-catalog/registry.d.ts +4 -0
  379. package/dist/email-catalog/registry.js +79 -14
  380. package/dist/extensions/content-patch.js +14 -2
  381. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  382. package/dist/integrations/plugin.d.ts +10 -0
  383. package/dist/integrations/plugin.js +40 -6
  384. package/dist/jobs/actions/run-automation-now.d.ts +2 -1
  385. package/dist/jobs/actions/run-automation-now.js +11 -5
  386. package/dist/jobs/frontmatter.js +53 -2
  387. package/dist/jobs/run-now.d.ts +11 -1
  388. package/dist/jobs/run-now.js +42 -7
  389. package/dist/jobs/scheduler.d.ts +1 -0
  390. package/dist/jobs/scheduler.js +2 -1
  391. package/dist/localization/core-messages/ar-SA.js +4 -1
  392. package/dist/localization/core-messages/de-DE.js +4 -1
  393. package/dist/localization/core-messages/en-US.d.ts +4 -1
  394. package/dist/localization/core-messages/en-US.js +4 -1
  395. package/dist/localization/core-messages/es-ES.js +4 -1
  396. package/dist/localization/core-messages/fr-FR.js +4 -1
  397. package/dist/localization/core-messages/hi-IN.js +4 -1
  398. package/dist/localization/core-messages/ja-JP.js +4 -1
  399. package/dist/localization/core-messages/ko-KR.js +4 -1
  400. package/dist/localization/core-messages/pt-BR.js +4 -1
  401. package/dist/localization/core-messages/zh-CN.js +4 -1
  402. package/dist/localization/core-messages/zh-TW.js +4 -1
  403. package/dist/localization/core-messages.d.ts +4 -1
  404. package/dist/localization/default-messages.d.ts +2 -5
  405. package/dist/localization/default-messages.js +2 -5
  406. package/dist/mcp/build-server.d.ts +9 -0
  407. package/dist/mcp/build-server.js +37 -8
  408. package/dist/mcp-client/oauth-client.d.ts +7 -0
  409. package/dist/mcp-client/oauth-client.js +12 -1
  410. package/dist/notifications/routes.d.ts +1 -1
  411. package/dist/oauth-tokens/index.d.ts +1 -1
  412. package/dist/oauth-tokens/index.js +1 -1
  413. package/dist/oauth-tokens/lifecycle.d.ts +11 -0
  414. package/dist/oauth-tokens/lifecycle.js +17 -0
  415. package/dist/oauth-tokens/store.js +7 -0
  416. package/dist/observability/posthog-ai.d.ts +15 -1
  417. package/dist/observability/posthog-ai.js +24 -12
  418. package/dist/observability/traces.js +158 -28
  419. package/dist/onboarding/app-profile.js +2 -2
  420. package/dist/onboarding/plugin.js +10 -2
  421. package/dist/onboarding/types.d.ts +2 -0
  422. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  423. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  424. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  425. package/dist/provider-api/index.js +2 -0
  426. package/dist/resources/handlers.d.ts +1 -1
  427. package/dist/resources/store.js +13 -4
  428. package/dist/scripts/agent-engines/list-agent-engines.js +29 -3
  429. package/dist/scripts/db/check-scoping.js +9 -6
  430. package/dist/secrets/register-framework-secrets.js +1 -1
  431. package/dist/secrets/routes.d.ts +6 -6
  432. package/dist/server/action-discovery.js +5 -0
  433. package/dist/server/agent-chat/action-filters-a2a.d.ts +27 -3
  434. package/dist/server/agent-chat/action-filters-a2a.js +48 -9
  435. package/dist/server/agent-chat-plugin.js +36 -9
  436. package/dist/server/agent-discovery.js +43 -20
  437. package/dist/server/auth.js +18 -16
  438. package/dist/server/beta-opt-out-html.js +45 -4
  439. package/dist/server/better-auth-instance.d.ts +2 -1
  440. package/dist/server/better-auth-instance.js +23 -7
  441. package/dist/server/builder-browser.d.ts +4 -0
  442. package/dist/server/builder-browser.js +23 -1
  443. package/dist/server/builder-design-systems.d.ts +4 -1
  444. package/dist/server/builder-design-systems.js +87 -6
  445. package/dist/server/builder-oauth.d.ts +1 -7
  446. package/dist/server/builder-oauth.js +80 -163
  447. package/dist/server/core-routes-plugin.d.ts +29 -0
  448. package/dist/server/core-routes-plugin.js +113 -21
  449. package/dist/server/default-plugins.d.ts +18 -0
  450. package/dist/server/default-plugins.js +22 -0
  451. package/dist/server/edge.d.ts +1 -0
  452. package/dist/server/edge.js +1 -0
  453. package/dist/server/framework-request-handler.js +31 -3
  454. package/dist/server/google-oauth.d.ts +3 -0
  455. package/dist/server/google-oauth.js +3 -0
  456. package/dist/server/index.d.ts +4 -4
  457. package/dist/server/index.js +3 -3
  458. package/dist/server/onboarding-html.js +37 -3
  459. package/dist/server/workspace-provider-oauth.d.ts +6 -1
  460. package/dist/server/workspace-provider-oauth.js +72 -20
  461. package/dist/shared/agent-sidebar-url.d.ts +1 -0
  462. package/dist/shared/agent-sidebar-url.js +1 -0
  463. package/dist/shared/environment-lanes.d.ts +2 -0
  464. package/dist/shared/environment-lanes.js +3 -1
  465. package/dist/shared/index.d.ts +3 -1
  466. package/dist/shared/index.js +3 -1
  467. package/dist/shared/surface-visibility.d.ts +31 -0
  468. package/dist/shared/surface-visibility.js +57 -0
  469. package/dist/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
  470. package/dist/templates/chat/app/routes.ts +3 -1
  471. package/dist/templates/chat/vitest.config.ts +8 -0
  472. package/dist/templates/default/.agents/skills/secrets/SKILL.md +11 -4
  473. package/dist/templates/default/app/routes.ts +3 -1
  474. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +11 -4
  475. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +11 -4
  476. package/dist/tracking/registry.d.ts +11 -0
  477. package/dist/tracking/registry.js +5 -2
  478. package/dist/triggers/actions.js +10 -3
  479. package/dist/usage/store.js +23 -3
  480. package/dist/user-profile/actions/get-auth-methods.js +18 -5
  481. package/dist/vite/client.js +24 -5
  482. package/docs/content/actions-access-control.mdx +43 -4
  483. package/docs/content/actions-defining.mdx +16 -0
  484. package/docs/content/template-clips-capture-everywhere.mdx +29 -30
  485. package/docs/content/template-clips-developers.mdx +4 -4
  486. package/docs/content/template-clips.mdx +3 -1
  487. package/package.json +3 -3
  488. package/src/eject/workspace-connections.ts +4 -0
  489. package/src/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
  490. package/src/templates/chat/app/routes.ts +3 -1
  491. package/src/templates/chat/vitest.config.ts +8 -0
  492. package/src/templates/default/.agents/skills/secrets/SKILL.md +11 -4
  493. package/src/templates/default/app/routes.ts +3 -1
  494. package/src/templates/headless/.agents/skills/secrets/SKILL.md +11 -4
  495. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +11 -4
  496. /package/corpus/templates/dispatch/app/routes/{apps.$appId.tsx → apps.$appId.$.tsx} +0 -0
@@ -70,8 +70,29 @@ export async function run(args = {}) {
70
70
  const currentModel = currentEntry && !envUnavailable
71
71
  ? normalizeModelForEngine(currentEntry, currentModelCandidate ?? currentEntry.defaultModel, { preserveCustomModels })
72
72
  : (currentModelCandidate ?? DEFAULT_MODEL);
73
- const result = {
74
- engines: engines.map((e) => ({
73
+ // Readiness has to be resolved here: `requiredEnvVars` alone cannot see
74
+ // vault-stored keys or the deploy-injected Builder gateway lane, so a client
75
+ // that re-derives it from env keys marks working engines unconfigured.
76
+ const engineEntries = await Promise.all(engines.map(async (e) => {
77
+ // Resolved per engine, not across the set: one provider whose credential
78
+ // store is momentarily unreadable must not reject the whole listing. The
79
+ // chat refresh catches that rejection and renders an empty catalog, so a
80
+ // single unrelated provider would make every engine unselectable — the
81
+ // exact symptom this readiness plumbing exists to fix.
82
+ //
83
+ // A read error is its own state, left as `configured: undefined` so the
84
+ // client falls back to its env heuristic. Folding it into `false` would
85
+ // claim the engine needs an API key when nobody actually knows.
86
+ let configured;
87
+ let configuredError;
88
+ try {
89
+ configured = await isStoredEngineUsableForRequest({ engine: e.name, model: e.defaultModel }, e);
90
+ }
91
+ catch (error) {
92
+ configuredError =
93
+ error instanceof Error ? error.message : String(error);
94
+ }
95
+ return {
75
96
  name: e.name,
76
97
  label: e.label,
77
98
  description: e.description,
@@ -81,7 +102,12 @@ export async function run(args = {}) {
81
102
  requiredEnvVars: e.requiredEnvVars,
82
103
  installPackage: e.installPackage,
83
104
  packageInstalled: isAgentEnginePackageInstalled(e),
84
- })),
105
+ configured,
106
+ configuredError,
107
+ };
108
+ }));
109
+ const result = {
110
+ engines: engineEntries,
85
111
  current: envUnavailable
86
112
  ? null
87
113
  : {
@@ -12,9 +12,15 @@
12
12
  * pnpm action db-check-scoping [--db path] [--require-org] [--format json]
13
13
  */
14
14
  import path from "path";
15
- import { createClient } from "@libsql/client";
16
- import { getDatabaseUrl, getDatabaseAuthToken } from "../../db/client.js";
15
+ import { getDatabaseUrl } from "../../db/client.js";
17
16
  import { parseArgs } from "../utils.js";
17
+ // The `@libsql/client` node entry pulls the `libsql` native addon into whatever
18
+ // graph imports it, and this maintenance script was its only importer in the
19
+ // server bundle — which is how a 9.3MB Linux SQLite driver reached the docs
20
+ // function, a deployment that runs Postgres. `createSqliteScriptClient` reaches
21
+ // the same two backends through dynamic imports instead, so nothing static
22
+ // depends on the native package.
23
+ import { createSqliteScriptClient } from "./sqlite-client.js";
18
24
  function isPostgresUrl(url) {
19
25
  return url.startsWith("postgres://") || url.startsWith("postgresql://");
20
26
  }
@@ -125,10 +131,7 @@ Options:
125
131
  }
126
132
  }
127
133
  else {
128
- const client = createClient({
129
- url,
130
- authToken: getDatabaseAuthToken(),
131
- });
134
+ const client = await createSqliteScriptClient(url);
132
135
  try {
133
136
  allColumns = await discoverColumnsSqlite(client);
134
137
  }
@@ -27,7 +27,7 @@ export function registerFrameworkSecrets() {
27
27
  id: "google_drive",
28
28
  credentialPrefix: "GOOGLE",
29
29
  oauthProvider: "google",
30
- label: "Google Drive",
30
+ label: "Google Workspace",
31
31
  docsUrl: "https://developers.google.com/identity/protocols/oauth2/web-server",
32
32
  },
33
33
  {
@@ -37,17 +37,17 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
37
37
  ok?: undefined;
38
38
  status?: undefined;
39
39
  } | {
40
- error?: undefined;
41
40
  ok: boolean;
42
41
  status: string;
42
+ error?: undefined;
43
43
  } | {
44
44
  ok?: undefined;
45
45
  error: string;
46
46
  removed?: undefined;
47
47
  } | {
48
- error?: undefined;
49
48
  ok: boolean;
50
49
  removed: boolean;
50
+ error?: undefined;
51
51
  }>>;
52
52
  /**
53
53
  * POST /_agent-native/secrets/:key/test — validate an optional candidate value
@@ -58,13 +58,13 @@ export declare function createTestSecretHandler(): import("h3").EventHandlerWith
58
58
  error: string;
59
59
  note?: undefined;
60
60
  } | {
61
- error?: undefined;
62
61
  ok: boolean;
63
62
  note?: undefined;
64
- } | {
65
63
  error?: undefined;
64
+ } | {
66
65
  ok: boolean;
67
66
  note: string;
67
+ error?: undefined;
68
68
  } | {
69
69
  note?: undefined;
70
70
  ok: boolean;
@@ -95,11 +95,11 @@ export interface AdHocSecretPayload {
95
95
  export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
96
96
  error: string;
97
97
  } | {
98
- error?: undefined;
99
98
  ok: boolean;
100
99
  key: string;
101
- } | {
102
100
  error?: undefined;
101
+ } | {
103
102
  ok: boolean;
104
103
  removed: boolean;
104
+ error?: undefined;
105
105
  }>>;
@@ -163,6 +163,11 @@ function preserveActionFlags(entry) {
163
163
  const out = {};
164
164
  if (typeof entry.agentTool === "boolean")
165
165
  out.agentTool = entry.agentTool;
166
+ if (typeof entry.mcpTool === "boolean")
167
+ out.mcpTool = entry.mcpTool;
168
+ if (typeof entry.deferLoading === "boolean") {
169
+ out.deferLoading = entry.deferLoading;
170
+ }
166
171
  if (typeof entry.requiresAuth === "boolean") {
167
172
  out.requiresAuth = entry.requiresAuth;
168
173
  }
@@ -13,6 +13,18 @@ export declare function filterReadOnlyActions(actions: Record<string, ActionEntr
13
13
  * list (in-app assistant, MCP, A2A, job/trigger runners) or actions prompt.
14
14
  * Default-allow: only an explicit `false` is excluded. */
15
15
  export declare function filterAgentTools(actions: Record<string, ActionEntry>): Record<string, ActionEntry>;
16
+ /**
17
+ * The MCP-only actions: `agentTool: false` paired with an explicit
18
+ * `mcpTool: true`.
19
+ *
20
+ * `filterAgentTools` drops these, which is right for every in-app surface and
21
+ * wrong for the two external ones. Rather than have the MCP and A2A mounts
22
+ * read the unfiltered registry — and re-derive which agent-hidden actions are
23
+ * safe to serve — the plugin re-merges exactly this set into those two mounts.
24
+ * A caller that wants "everything external" merges this with the agent surface;
25
+ * nobody has to remember which raw fields make an action MCP-only.
26
+ */
27
+ export declare function filterMcpOnlyActions(actions: Record<string, ActionEntry>): Record<string, ActionEntry>;
16
28
  export declare function filterPublicAgentActions(actions: Record<string, ActionEntry>): Record<string, ActionEntry>;
17
29
  /**
18
30
  * True when an action takes a free-form query or program as input — raw SQL, a
@@ -137,15 +149,27 @@ export declare function createA2AEngineToolSurface(availableTools: EngineTool[],
137
149
  export declare function isSelectedA2AReceiver(selectedReceiverApp: string | undefined, appId: string | undefined): boolean;
138
150
  export declare function buildSelectedA2AReceiverContext(appId: string): string;
139
151
  /**
140
- * The first-request tool catalog: an explicit `initialToolNames` verbatim, or
141
- * the app's OWN actions by default.
152
+ * The first-request tool catalog, in precedence order:
153
+ *
154
+ * 1. An explicit `initialToolNames`, plus every action that declares
155
+ * `deferLoading: false`. The configured list stays authoritative — an
156
+ * action it names is never dropped by a `deferLoading: true` — so a
157
+ * template can annotate its actions before deleting the array, not after.
158
+ * 2. Otherwise, the actions that declared `deferLoading: false`, if any did.
159
+ * 3. Otherwise, the app's OWN actions, minus any marked `deferLoading: true`.
160
+ *
161
+ * Step 2 is what makes `deferLoading` a replacement for the array rather than
162
+ * a second copy of it: opting a few starter actions out of deferral on an app
163
+ * with no configured list defers everything else, instead of leaving the whole
164
+ * registry in and the annotation decorative.
142
165
  *
143
166
  * "Its own actions" excludes the framework kits. They arrive in this same
144
167
  * registry through `autoDiscoverActions` -> `mergeCoreSharingActions`, so the
145
168
  * plain `Object.keys` default promoted ~45 sharing/review/history/flag schemas
146
169
  * into every app's first request whether or not the app had those surfaces. They
147
170
  * remain in `availableTools` and are still found by `tool-search`; an app that
148
- * wants one on turn one names it in `initialToolNames`.
171
+ * wants one on turn one sets `deferLoading: false` or names it in
172
+ * `initialToolNames`.
149
173
  */
150
174
  export declare function resolveInitialToolNames(templateActions: Record<string, ActionEntry>, configured?: string[]): string[];
151
175
  export {};
@@ -1,6 +1,7 @@
1
1
  import { getHeader } from "h3";
2
2
  import { appendA2AArtifactLinks, extractA2APersistedMutationReceipts, } from "../../a2a/artifact-response.js";
3
3
  import { collectFinalResponseTextFromAgentEvents } from "../../a2a/response-text.js";
4
+ import { isActionExposedToExternalAgents } from "../../action.js";
4
5
  import { resolveMainChatMaxOutputTokens } from "../../agent/engine/output-tokens.js";
5
6
  import { filterInitialEngineTools, resolveAgentRequestReasoningEffort, } from "../../agent/production-agent.js";
6
7
  import { runAgentLoopDirectWithSoftTimeout } from "../../agent/run-loop-with-resume.js";
@@ -25,6 +26,20 @@ export function filterReadOnlyActions(actions) {
25
26
  export function filterAgentTools(actions) {
26
27
  return Object.fromEntries(Object.entries(actions).filter(([, entry]) => entry.agentTool !== false));
27
28
  }
29
+ /**
30
+ * The MCP-only actions: `agentTool: false` paired with an explicit
31
+ * `mcpTool: true`.
32
+ *
33
+ * `filterAgentTools` drops these, which is right for every in-app surface and
34
+ * wrong for the two external ones. Rather than have the MCP and A2A mounts
35
+ * read the unfiltered registry — and re-derive which agent-hidden actions are
36
+ * safe to serve — the plugin re-merges exactly this set into those two mounts.
37
+ * A caller that wants "everything external" merges this with the agent surface;
38
+ * nobody has to remember which raw fields make an action MCP-only.
39
+ */
40
+ export function filterMcpOnlyActions(actions) {
41
+ return Object.fromEntries(Object.entries(actions).filter(([, entry]) => entry.agentTool === false && entry.mcpTool === true));
42
+ }
28
43
  export function filterPublicAgentActions(actions) {
29
44
  return Object.fromEntries(Object.entries(actions).filter(([, entry]) => {
30
45
  const config = entry.publicAgent;
@@ -80,7 +95,10 @@ export function filterDirectA2AActions(actions, options) {
80
95
  const autoReads = options.externalAgents?.authenticatedReads === "auto";
81
96
  return Object.fromEntries(Object.entries(actions).filter(([name, entry]) => {
82
97
  const exposure = entry.publicAgent;
83
- const selected = catalog.has(name) ||
98
+ // `mcpTool: true` is catalog membership declared beside the action; the
99
+ // exposure resolver below is the veto no config list can re-open.
100
+ const selected = entry.mcpTool === true ||
101
+ catalog.has(name) ||
84
102
  (autoReads &&
85
103
  isAuthenticatedReadAction(entry) &&
86
104
  !isAutoReadExcludedActionName(name));
@@ -88,7 +106,7 @@ export function filterDirectA2AActions(actions, options) {
88
106
  return (selected &&
89
107
  rawQueryAllowed &&
90
108
  !denied.has(name) &&
91
- entry.agentTool !== false &&
109
+ isActionExposedToExternalAgents(entry) &&
92
110
  entry.readOnly === true &&
93
111
  exposure?.expose === true &&
94
112
  exposure.readOnly === true &&
@@ -108,7 +126,7 @@ export function filterDelegatedA2ACapabilityActions(actions, options) {
108
126
  return Object.fromEntries(Object.entries(actions).filter(([name, entry]) => {
109
127
  const exposure = entry.publicAgent;
110
128
  return (!denied.has(name) &&
111
- entry.agentTool !== false &&
129
+ isActionExposedToExternalAgents(entry) &&
112
130
  entry.readOnly !== true &&
113
131
  exposure?.expose === true &&
114
132
  exposure.readOnly === false &&
@@ -463,20 +481,41 @@ The caller already selected this app to own the current objective. Start with th
463
481
  </selected-a2a-receiver>`;
464
482
  }
465
483
  /**
466
- * The first-request tool catalog: an explicit `initialToolNames` verbatim, or
467
- * the app's OWN actions by default.
484
+ * The first-request tool catalog, in precedence order:
485
+ *
486
+ * 1. An explicit `initialToolNames`, plus every action that declares
487
+ * `deferLoading: false`. The configured list stays authoritative — an
488
+ * action it names is never dropped by a `deferLoading: true` — so a
489
+ * template can annotate its actions before deleting the array, not after.
490
+ * 2. Otherwise, the actions that declared `deferLoading: false`, if any did.
491
+ * 3. Otherwise, the app's OWN actions, minus any marked `deferLoading: true`.
492
+ *
493
+ * Step 2 is what makes `deferLoading` a replacement for the array rather than
494
+ * a second copy of it: opting a few starter actions out of deferral on an app
495
+ * with no configured list defers everything else, instead of leaving the whole
496
+ * registry in and the annotation decorative.
468
497
  *
469
498
  * "Its own actions" excludes the framework kits. They arrive in this same
470
499
  * registry through `autoDiscoverActions` -> `mergeCoreSharingActions`, so the
471
500
  * plain `Object.keys` default promoted ~45 sharing/review/history/flag schemas
472
501
  * into every app's first request whether or not the app had those surfaces. They
473
502
  * remain in `availableTools` and are still found by `tool-search`; an app that
474
- * wants one on turn one names it in `initialToolNames`.
503
+ * wants one on turn one sets `deferLoading: false` or names it in
504
+ * `initialToolNames`.
475
505
  */
476
506
  export function resolveInitialToolNames(templateActions, configured) {
507
+ const entries = Object.entries(templateActions);
508
+ // A framework-grouped action stays out of the DERIVED set, but an explicit
509
+ // `deferLoading: false` on one is a declaration, not a leak: it is the same
510
+ // opt-in `initialToolNames` already gave apps for these names.
511
+ const eager = entries
512
+ .filter(([, entry]) => entry.deferLoading === false)
513
+ .map(([name]) => name);
477
514
  if (configured)
478
- return configured;
479
- return Object.entries(templateActions)
480
- .filter(([name, entry]) => !isFrameworkGroupedAction(name, entry))
515
+ return [...new Set([...configured, ...eager])];
516
+ if (eager.length > 0)
517
+ return eager;
518
+ return entries
519
+ .filter(([name, entry]) => !isFrameworkGroupedAction(name, entry) && entry.deferLoading !== true)
481
520
  .map(([name]) => name);
482
521
  }
@@ -6,6 +6,7 @@ import { appendA2AArtifactLinks, buildA2ARecoverableArtifactMessage, } from "../
6
6
  import { hasConfiguredA2ASecret, isLoopbackAddress, isTrustedLocalRuntime, } from "../a2a/auth-policy.js";
7
7
  import { sanitizeA2ACorrelationId, sanitizeA2ACorrelationMetadata, } from "../a2a/correlation.js";
8
8
  import { createA2AApproval, updateTaskStatusMessage, } from "../a2a/task-store.js";
9
+ import { clientAbortReason } from "../agent/abort-reasons.js";
9
10
  import { canUpdateAgentAppModelDefaultSettings, normalizeAgentAppModelDefaultAppId, readAgentAppModelDefaultSettings, resetAgentAppModelDefaultSettings, writeAgentAppModelDefaultSettings, } from "../agent/app-model-defaults.js";
10
11
  import { DEFAULT_ANTHROPIC_MODEL } from "../agent/default-model.js";
11
12
  import { AGENT_CHAT_BACKGROUND_RUN_FIELD, AGENT_CHAT_PROCESS_RUN_PATH, backgroundRunMarkerExpectsBackgroundRuntime, isInBackgroundFunctionRuntime, prepareProcessRunRequest, } from "../agent/durable-background.js";
@@ -14,7 +15,6 @@ import { SYSTEM_PROMPT_CACHE_SPLIT } from "../agent/engine/prompt-cache.js";
14
15
  import { PROVIDER_TO_ENV } from "../agent/engine/provider-env-vars.js";
15
16
  import { hostedHarnessSystemPrompt } from "../agent/harness/hosted.js";
16
17
  import { createProductionAgentHandler, actionsToEngineTools, executeAgentToolCall, filterActionsByAllowedNames, normalizeAgentActionSurfaceResolution, readPersistedActionSurface, toolCallCacheKey, getActiveRunForThreadAsync, abortRunDurably, abortTurnDurably, subscribeToRun, } from "../agent/production-agent.js";
17
- import { clientAbortReason } from "../agent/run-loop-with-resume.js";
18
18
  import { callerHasRunAccess, callerHasThreadAccess, } from "../agent/run-ownership.js";
19
19
  import { markTurnAborted, readBackgroundRunClaim } from "../agent/run-store.js";
20
20
  import { buildCurrentTimeUserContext, buildRuntimeContextPrompt, } from "../agent/runtime-context.js";
@@ -33,6 +33,7 @@ import { filterFrameworkToolGroups, resolveFrameworkTools, } from "../framework-
33
33
  import { verifyInternalToken, extractBearerToken, } from "../integrations/internal-token.js";
34
34
  import { RECURRING_JOBS_SWEEP_PATH, RECURRING_JOBS_SWEEP_TOKEN_SUBJECT, } from "../jobs/scheduler-dispatch.js";
35
35
  import { McpClientManager, mcpToolsToActionEntries, syncMcpActionEntries, mountMcpServersRoutes, mountMcpHubRoutes, buildMergedConfig, startMcpConfigRefresh, getHubStatus, isHubServeEnabled, } from "../mcp-client/index.js";
36
+ import { declaredMcpToolNames } from "../mcp/build-server.js";
36
37
  import { setProgressPreListHook } from "../progress/store.js";
37
38
  import { getSkillNameFromPath } from "../resources/metadata.js";
38
39
  import { resourceList, resourceListAccessible, resourceGet, ensurePersonalDefaults, SHARED_OWNER, WORKSPACE_OWNER, } from "../resources/store.js";
@@ -113,7 +114,7 @@ import { buildSystemManifestSections, setContextXraySystemSections, } from "../a
113
114
  // the ones that were already part of the public surface) so
114
115
  // `createAgentChatPlugin` below stays a thinner orchestrator.
115
116
  // ---------------------------------------------------------------------------
116
- import { buildSelectedA2AReceiverContext, createA2AEngineToolSurface, DEFAULT_DELEGATED_MAX_ITERATIONS, DEFAULT_DELEGATED_MAX_RUN_INPUT_TOKENS, DEFAULT_DELEGATED_MAX_TOOL_RESULT_CHARS, filterAgentTools, filterPublicAgentActions, filterDirectA2AActions, filterReadOnlyActions, isSelectedA2AReceiver, resolveInitialToolNames, runA2AAgentLoop, runMCPAgentLoop, assembleA2AFinalResponse, buildPublicAgentA2ASkills, buildAuthenticatedAgentA2ASkills, resolveArtifactBaseUrl, } from "./agent-chat/action-filters-a2a.js";
117
+ import { buildSelectedA2AReceiverContext, createA2AEngineToolSurface, DEFAULT_DELEGATED_MAX_ITERATIONS, DEFAULT_DELEGATED_MAX_RUN_INPUT_TOKENS, DEFAULT_DELEGATED_MAX_TOOL_RESULT_CHARS, filterAgentTools, filterMcpOnlyActions, filterPublicAgentActions, filterDirectA2AActions, filterReadOnlyActions, isSelectedA2AReceiver, resolveInitialToolNames, runA2AAgentLoop, runMCPAgentLoop, assembleA2AFinalResponse, buildPublicAgentA2ASkills, buildAuthenticatedAgentA2ASkills, resolveArtifactBaseUrl, } from "./agent-chat/action-filters-a2a.js";
117
118
  import { createBuilderBrowserTool, createTeamTools, } from "./agent-chat/browser-team-tools.js";
118
119
  import { createDataWidgetActionEntries, createFrameworkContextEntry, createRefreshScreenEntry, createUrlTools, } from "./agent-chat/context-tools.js";
119
120
  import { _agentChatPromptSectionsForTests, buildFrameworkPrompts, buildSchemaBlock, collectFiles, corpusToolNamesTaughtByPrompt, generateActionsPrompt, generateCorpusToolsPrompt, } from "./agent-chat/framework-prompts.js";
@@ -708,6 +709,17 @@ export function createAgentChatPlugin(options) {
708
709
  // whatever `generateCorpusToolsPrompt` teaches by name for this request.
709
710
  const templateInitialToolNames = resolveInitialToolNames(templateScripts, options?.initialToolNames);
710
711
  const discoveredActions = filterFrameworkToolGroups(filterAgentTools(discoveredActionsAll), disabledFrameworkGroups);
712
+ // MCP-only actions: `agentTool: false` with an explicit `mcpTool: true`.
713
+ // `filterAgentTools` above just dropped them — correctly, since the app's
714
+ // own agent must not see them — so the external surfaces below re-merge
715
+ // this set instead of reading the unfiltered registries. Keep it OUT of
716
+ // `allScripts`, the chat/A2A/ask_app loops, and every prompt: an
717
+ // `ask_app` run is the app's own agent, which `agentTool: false` already
718
+ // answered. Only the direct MCP and A2A tool surfaces get these.
719
+ const mcpOnlyActions = filterFrameworkToolGroups(filterMcpOnlyActions({
720
+ ...discoveredActionsAll,
721
+ ...templateScriptsAll,
722
+ }), disabledFrameworkGroups);
711
723
  // Per-request owner is read from the AsyncLocalStorage run context
712
724
  // (populated by prepareRun). Module-scope `let` would race across
713
725
  // concurrent requests on a long-lived Node process — overlapping
@@ -1146,6 +1158,14 @@ export function createAgentChatPlugin(options) {
1146
1158
  ...devRunCodeTool,
1147
1159
  })
1148
1160
  : undefined;
1161
+ // The surface external agents get: everything the app's agent has, plus
1162
+ // the MCP-only actions the agent filter removed. The agent sets win on a
1163
+ // name collision, which by construction cannot happen — an action is in
1164
+ // one set or the other, never both.
1165
+ const externalActions = { ...mcpOnlyActions, ...allScripts };
1166
+ const externalFullActions = mcpFullActions
1167
+ ? { ...mcpOnlyActions, ...mcpFullActions }
1168
+ : undefined;
1149
1169
  const { mountA2A } = await import("../a2a/server.js");
1150
1170
  mountA2A(nitroApp, {
1151
1171
  appId: options?.appId,
@@ -1153,13 +1173,13 @@ export function createAgentChatPlugin(options) {
1153
1173
  ? options.appId.charAt(0).toUpperCase() + options.appId.slice(1)
1154
1174
  : "Agent",
1155
1175
  description: `Agent-native ${options?.appId ?? "app"} agent`,
1156
- skills: buildPublicAgentA2ASkills(allScripts),
1157
- authenticatedSkills: buildAuthenticatedAgentA2ASkills(mcpFullActions ?? allScripts, mcpOptions),
1176
+ skills: buildPublicAgentA2ASkills(externalActions),
1177
+ authenticatedSkills: buildAuthenticatedAgentA2ASkills(externalFullActions ?? externalActions, mcpOptions),
1158
1178
  publicSkillsOnly: true,
1159
1179
  streaming: true,
1160
1180
  durableBackgroundRuns: options?.durableBackgroundRuns,
1161
1181
  executeReadOnlyAction: async ({ action, input, invocationId }) => {
1162
- const actions = filterDirectA2AActions(mcpFullActions ?? allScripts, mcpOptions);
1182
+ const actions = filterDirectA2AActions(externalFullActions ?? externalActions, mcpOptions);
1163
1183
  const entry = actions[action];
1164
1184
  if (!entry) {
1165
1185
  return {
@@ -1188,7 +1208,7 @@ export function createAgentChatPlugin(options) {
1188
1208
  },
1189
1209
  executeApproval: async (approval) => {
1190
1210
  const result = await executeAgentToolCall({
1191
- actions: mcpFullActions ?? allScripts,
1211
+ actions: externalFullActions ?? externalActions,
1192
1212
  name: approval.tool,
1193
1213
  input: approval.input,
1194
1214
  callId: approval.callId,
@@ -1523,7 +1543,14 @@ export function createAgentChatPlugin(options) {
1523
1543
  });
1524
1544
  const a2aToolSurface = createA2AEngineToolSurface(actionsToEngineTools(a2aActions), effectiveInitialToolNames, {
1525
1545
  receiverOwnsObjective,
1526
- localCapabilityNames: mcpOptions.connectorCatalog,
1546
+ // Same curated set the MCP mount serves: config names plus the
1547
+ // actions that declare `mcpTool: true` themselves.
1548
+ localCapabilityNames: [
1549
+ ...new Set([
1550
+ ...(mcpOptions.connectorCatalog ?? []),
1551
+ ...declaredMcpToolNames(a2aActions),
1552
+ ]),
1553
+ ],
1527
1554
  });
1528
1555
  // Precise current time rides the user message (not the cached
1529
1556
  // system-prompt prefix) — same pattern as the interactive handler.
@@ -1848,8 +1875,8 @@ export function createAgentChatPlugin(options) {
1848
1875
  `Agent-native ${options?.appId ?? "app"} agent`,
1849
1876
  websiteUrl: mcpOptions.websiteUrl,
1850
1877
  icons: mcpOptions.icons,
1851
- actions: allScripts,
1852
- productionActions: mcpFullActions,
1878
+ actions: externalActions,
1879
+ productionActions: externalFullActions,
1853
1880
  ...(mcpOptions.catalog ? { catalogMode: mcpOptions.catalog } : {}),
1854
1881
  ...(mcpOptions.builtinCrossAppTools !== undefined
1855
1882
  ? { builtinCrossAppTools: mcpOptions.builtinCrossAppTools }
@@ -40,6 +40,8 @@ export function normalizeAgentId(id) {
40
40
  normalized === "asset") {
41
41
  return "assets";
42
42
  }
43
+ if (normalized === "videos")
44
+ return "clips";
43
45
  return normalized;
44
46
  }
45
47
  const WORKSPACE_APPS_ENV_KEY = "AGENT_NATIVE_WORKSPACE_APPS_JSON";
@@ -67,8 +69,11 @@ export function parseWorkspaceAppMetadataSettings(raw) {
67
69
  ? record.apps
68
70
  : {};
69
71
  const apps = {};
70
- for (const [id, value] of Object.entries(rawApps)) {
71
- if (!id.trim() || !value || typeof value !== "object")
72
+ const canonicalIds = new Set();
73
+ for (const [rawId, value] of Object.entries(rawApps)) {
74
+ const id = rawId.trim();
75
+ const normalizedId = id ? normalizeAgentId(id) : "";
76
+ if (!normalizedId || !value || typeof value !== "object")
72
77
  continue;
73
78
  const item = value;
74
79
  const override = {};
@@ -89,8 +94,14 @@ export function parseWorkspaceAppMetadataSettings(raw) {
89
94
  override.updatedAt = updatedAt;
90
95
  if (updatedBy)
91
96
  override.updatedBy = updatedBy;
92
- if (Object.keys(override).length > 0)
93
- apps[id.trim()] = override;
97
+ if (Object.keys(override).length > 0) {
98
+ const isCanonical = id.toLowerCase() === normalizedId;
99
+ if (isCanonical || !canonicalIds.has(normalizedId)) {
100
+ apps[normalizedId] = override;
101
+ }
102
+ if (isCanonical)
103
+ canonicalIds.add(normalizedId);
104
+ }
94
105
  }
95
106
  return { apps };
96
107
  }
@@ -110,7 +121,7 @@ export async function writeWorkspaceAppMetadataOverride(input) {
110
121
  const key = workspaceAppMetadataSettingsKey();
111
122
  if (!key)
112
123
  throw new Error("no authenticated user");
113
- const appId = input.appId.trim();
124
+ const appId = normalizeAgentId(input.appId);
114
125
  if (!appId)
115
126
  throw new Error("appId is required");
116
127
  const { getSetting, putSetting } = await import("../settings/index.js");
@@ -145,7 +156,7 @@ export async function writeWorkspaceAppMetadataOverride(input) {
145
156
  return current;
146
157
  }
147
158
  export function applyWorkspaceAppMetadataOverride(app, settings) {
148
- const override = settings.apps[app.id];
159
+ const override = settings.apps[normalizeAgentId(app.id)] ?? settings.apps[app.id];
149
160
  if (!override)
150
161
  return app;
151
162
  const name = cleanOptionalText(override.name);
@@ -244,21 +255,20 @@ export async function discoverAgents(selfAppId, options) {
244
255
  continue;
245
256
  const manifestId = normalizeAgentId(manifest.id);
246
257
  // If the resource override carries a localhost URL but we're running
247
- // in production (e.g. a stale dev-time seed got promoted to the prod
258
+ // in a hosted runtime (e.g. a stale dev-time seed got promoted to the prod
248
259
  // DB), fall back to the matching built-in's prod URL instead of
249
260
  // letting the override win — otherwise outbound `call-agent` fetches
250
261
  // from a serverless function would target localhost and fail with
251
262
  // "fetch failed" instantly. The override still wins for non-localhost
252
263
  // URLs (the supported case for self-hosted custom agents).
253
264
  let url = manifest.url;
254
- const isProduction = typeof process !== "undefined" &&
255
- process.env?.NODE_ENV === "production";
256
- if (isProduction &&
257
- typeof url === "string" &&
258
- /^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0)(:|\/|$)/.test(url)) {
265
+ const isHosted = isHostedRuntime();
266
+ if (isHosted && typeof url === "string" && isLoopbackUrl(url)) {
259
267
  const builtin = agentsById.get(manifestId);
260
268
  if (builtin?.url)
261
269
  url = builtin.url;
270
+ else
271
+ continue;
262
272
  }
263
273
  const builtin = agentsById.get(manifestId);
264
274
  if (options?.preferLocalUrls && builtin) {
@@ -327,10 +337,19 @@ function hostnameFromUrlLike(value) {
327
337
  }
328
338
  function isLoopbackUrl(value) {
329
339
  const hostname = hostnameFromUrlLike(value);
330
- return (hostname === "localhost" ||
331
- hostname === "127.0.0.1" ||
332
- hostname === "0.0.0.0" ||
333
- hostname === "::1");
340
+ if (!hostname)
341
+ return false;
342
+ const normalized = hostname.replace(/^\[|\]$/g, "");
343
+ if (normalized === "localhost" ||
344
+ normalized.startsWith("127.") ||
345
+ normalized === "0.0.0.0" ||
346
+ normalized === "::1") {
347
+ return true;
348
+ }
349
+ const mapped = normalized.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);
350
+ if (!mapped)
351
+ return false;
352
+ return Number.parseInt(mapped[1], 16) >>> 8 === 0x7f;
334
353
  }
335
354
  function hasPublicRuntimeUrl() {
336
355
  // Intentionally omit generic `URL` / `DEPLOY_URL` here. Platforms that set
@@ -353,6 +372,8 @@ function hasPublicRuntimeUrl() {
353
372
  });
354
373
  }
355
374
  function isHostedRuntime() {
375
+ if (process.env.NETLIFY_LOCAL === "true")
376
+ return false;
356
377
  return (process.env.NODE_ENV === "production" ||
357
378
  !!process.env.NETLIFY ||
358
379
  !!process.env.AWS_LAMBDA_FUNCTION_NAME ||
@@ -412,7 +433,8 @@ function parseWorkspaceAppsManifest(parsed) {
412
433
  if (!entry || typeof entry !== "object")
413
434
  return null;
414
435
  const e = entry;
415
- const id = typeof e.id === "string" ? e.id.trim() : "";
436
+ const rawId = typeof e.id === "string" ? e.id.trim() : "";
437
+ const id = rawId ? normalizeAgentId(rawId) : "";
416
438
  const pathValue = typeof e.path === "string" ? e.path.trim() : "";
417
439
  if (!id || !pathValue.startsWith("/"))
418
440
  return null;
@@ -498,11 +520,11 @@ function readWorkspaceAppsFromFilesystem() {
498
520
  return null;
499
521
  const routeAccess = workspaceAppRouteAccessFromPackageJson(pkg);
500
522
  return {
501
- id: entry.name,
523
+ id: normalizeAgentId(entry.name),
502
524
  name: pkg.displayName || titleCase(entry.name),
503
525
  description: pkg.description || "",
504
526
  path: `/${entry.name}`,
505
- isDispatch: entry.name === "dispatch",
527
+ isDispatch: normalizeAgentId(entry.name) === "dispatch",
506
528
  audience: workspaceAppAudienceFromPackageJson(pkg) ??
507
529
  DEFAULT_WORKSPACE_APP_AUDIENCE,
508
530
  publicPaths: routeAccess.publicPaths ?? [],
@@ -550,8 +572,9 @@ async function discoverWorkspaceAgents(selfAppId, options) {
550
572
  if (!workspaceApps)
551
573
  return [];
552
574
  const metadataSettings = await readWorkspaceAppMetadataSettings();
575
+ const normalizedSelfAppId = selfAppId ? normalizeAgentId(selfAppId) : "";
553
576
  return workspaceApps
554
- .filter((app) => app.id !== selfAppId)
577
+ .filter((app) => normalizeAgentId(app.id) !== normalizedSelfAppId)
555
578
  .map((app) => {
556
579
  const withOverride = applyWorkspaceAppMetadataOverride(app, metadataSettings);
557
580
  const builtin = BUILTIN_AGENTS.find((agent) => agent.id === withOverride.id);
@@ -4056,6 +4056,24 @@ async function mountBetterAuthRoutes(app, options) {
4056
4056
  return { error: authError.message };
4057
4057
  }
4058
4058
  }));
4059
+ // Better Auth redirects new magic-link users through this small public
4060
+ // callback so first-run onboarding is marked only for newly created users.
4061
+ // Keep this before the generic magic-link handler for runtimes whose
4062
+ // app.use() middleware paths match descendants as prefixes.
4063
+ app.use("/_agent-native/auth/magic-link/new-user", defineEventHandler(async (event) => {
4064
+ if (!isReadMethod(event)) {
4065
+ setResponseStatus(event, 405);
4066
+ return { error: "Method not allowed" };
4067
+ }
4068
+ const query = getQuery(event);
4069
+ const rawReturn = Array.isArray(query.return)
4070
+ ? query.return[0]
4071
+ : query.return;
4072
+ if (await getSession(event)) {
4073
+ setFirstRunOnboardingCookie(event);
4074
+ }
4075
+ return redirectWithStagedCookies(event, safeReturnPath(rawReturn), 302);
4076
+ }));
4059
4077
  // Passwordless login via Better Auth's rate-limited magic-link plugin.
4060
4078
  app.use("/_agent-native/auth/magic-link", defineEventHandler(async (event) => {
4061
4079
  if (getMethod(event) === "GET") {
@@ -4313,22 +4331,6 @@ async function mountBetterAuthRoutes(app, options) {
4313
4331
  headers: { "Content-Type": "text/html; charset=utf-8" },
4314
4332
  });
4315
4333
  }));
4316
- // Better Auth redirects new magic-link users through this small public
4317
- // callback so first-run onboarding is marked only for newly created users.
4318
- app.use("/_agent-native/auth/magic-link/new-user", defineEventHandler(async (event) => {
4319
- if (!isReadMethod(event)) {
4320
- setResponseStatus(event, 405);
4321
- return { error: "Method not allowed" };
4322
- }
4323
- const query = getQuery(event);
4324
- const rawReturn = Array.isArray(query.return)
4325
- ? query.return[0]
4326
- : query.return;
4327
- if (await getSession(event)) {
4328
- setFirstRunOnboardingCookie(event);
4329
- }
4330
- return redirectWithStagedCookies(event, safeReturnPath(rawReturn), 302);
4331
- }));
4332
4334
  // Auth guard — stored both in framework middleware registry AND in
4333
4335
  // _authGuardFn so the server middleware can enforce it on ALL routes.
4334
4336
  const loginHtmlConfig = getOnboardingLoginHtmlConfig(options, authLoginMode);