@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
@@ -13,7 +13,12 @@ import { decodeOAuthState, encodeOAuthState, getAppUrl, resolveOAuthRedirectUri,
13
13
  import { runWithRequestContext } from "./request-context.js";
14
14
  const SUPPORTED_PROVIDERS = new Set([
15
15
  "figma",
16
+ "gmail",
17
+ "google_calendar",
18
+ "google_docs",
16
19
  "google_drive",
20
+ "google_sheets",
21
+ "google_slides",
17
22
  "github",
18
23
  "hubspot",
19
24
  "salesforce",
@@ -26,7 +31,22 @@ const PROVIDER_REQUEST_TIMEOUT_MS = 10_000;
26
31
  const PROVIDER_RESPONSE_MAX_BYTES = 256 * 1024;
27
32
  const SALESFORCE_PRODUCTION_LOGIN_URL = "https://login.salesforce.com";
28
33
  const SALESFORCE_SANDBOX_LOGIN_URL = "https://test.salesforce.com";
29
- const WORKSPACE_OAUTH_ADMIN_ERROR = "Only organization admins or the relevant app admin can connect shared OAuth accounts.";
34
+ const WORKSPACE_OAUTH_ADMIN_ERROR = "This shared connection requires organization or app-admin access. Personal connections can be connected by any workspace member.";
35
+ export function isWorkspaceProviderOAuthScope(value) {
36
+ return value === "user" || value === "organization" || value === "app";
37
+ }
38
+ export function isGoogleWorkspaceOAuthProvider(provider) {
39
+ return (provider === "gmail" ||
40
+ provider === "google_calendar" ||
41
+ provider === "google_docs" ||
42
+ provider === "google_drive" ||
43
+ provider === "google_sheets" ||
44
+ provider === "google_slides");
45
+ }
46
+ export async function hasWorkspaceProviderOAuthCredentials(provider) {
47
+ const [clientId, clientSecret] = await resolveProviderClientCredentials(provider);
48
+ return Boolean(clientId && clientSecret);
49
+ }
30
50
  export function workspaceProviderOAuthPath(provider, phase) {
31
51
  return `/_agent-native/connections/oauth/${provider}/${phase}`;
32
52
  }
@@ -46,7 +66,14 @@ export async function handleWorkspaceProviderOAuthStart(event, providerId) {
46
66
  return unauthorized(event);
47
67
  const query = getQuery(event);
48
68
  const appId = normalizeAppId(text(query.appId) ?? getAppConfig().app.workspaceId ?? "creative-context");
49
- const orgContext = await requireWorkspaceProviderOAuthAdmin(event, appId);
69
+ const requestedScope = parseWorkspaceProviderOAuthScope(text(query.scope));
70
+ if (!requestedScope) {
71
+ setResponseStatus(event, 400);
72
+ return {
73
+ error: "OAuth connection scope must be user, organization, or app.",
74
+ };
75
+ }
76
+ const orgContext = await requireWorkspaceProviderOAuthAccess(event, appId, requestedScope);
50
77
  if (!orgContext) {
51
78
  return { error: WORKSPACE_OAUTH_ADMIN_ERROR };
52
79
  }
@@ -91,6 +118,7 @@ export async function handleWorkspaceProviderOAuthStart(event, providerId) {
91
118
  owner: session.email,
92
119
  orgId,
93
120
  app: appId,
121
+ scope: orgContext.oauthScope,
94
122
  returnUrl,
95
123
  flowId,
96
124
  });
@@ -102,6 +130,7 @@ export async function handleWorkspaceProviderOAuthStart(event, providerId) {
102
130
  owner: session.email,
103
131
  orgId,
104
132
  appId,
133
+ scope: orgContext.oauthScope,
105
134
  ...(salesforceLoginUrl ? { salesforceLoginUrl } : {}),
106
135
  expiresAt: Date.now() + FLOW_TTL_SECONDS * 1_000,
107
136
  };
@@ -134,7 +163,11 @@ export async function handleWorkspaceProviderOAuthCallback(event, providerId) {
134
163
  if (!session?.email)
135
164
  return unauthorized(event);
136
165
  const flow = readStoredFlow(event, providerId);
137
- const orgContext = await requireWorkspaceProviderOAuthAdmin(event, flow?.appId);
166
+ if (!flow || !isWorkspaceProviderOAuthScope(flow.scope)) {
167
+ setResponseStatus(event, 400);
168
+ return { error: "OAuth state is invalid or expired." };
169
+ }
170
+ const orgContext = await requireWorkspaceProviderOAuthAccess(event, flow.appId, flow.scope);
138
171
  if (!orgContext) {
139
172
  return { error: WORKSPACE_OAUTH_ADMIN_ERROR };
140
173
  }
@@ -204,8 +237,12 @@ export async function handleWorkspaceProviderOAuthCallback(event, providerId) {
204
237
  await saveOAuthTokens(provider.oauth.provider, accountId, tokens, session.email);
205
238
  await setOAuthDisplayName(provider.oauth.provider, accountId, identity.label);
206
239
  const existing = existingConnections.find((connection) => connection.accountId === accountId &&
207
- (orgContext.oauthScope === "organization" ||
208
- connection.allowedApps.some((allowedApp) => allowedApp.toLowerCase() === flow.appId.toLowerCase())));
240
+ (flow.scope === "user"
241
+ ? connection.allowedUsers.some((allowedUser) => allowedUser.toLowerCase() === session.email.toLowerCase())
242
+ : flow.scope === "organization"
243
+ ? connection.allowedUsers.length === 0 &&
244
+ (connection.allowedUserGroups?.length ?? 0) === 0
245
+ : connection.allowedApps.some((allowedApp) => allowedApp.toLowerCase() === flow.appId.toLowerCase())));
209
246
  const scopes = mergeWorkspaceOAuthValues(existing?.scopes ?? [], identity.scopes ?? tokenScopes(tokens, provider.oauth.scopes));
210
247
  const connectionConfig = {
211
248
  credentialMode: "oauth",
@@ -229,8 +266,10 @@ export async function handleWorkspaceProviderOAuthCallback(event, providerId) {
229
266
  accountLabel: identity.label,
230
267
  status: "connected",
231
268
  scopes,
232
- allowedApps: existing?.allowedApps ??
233
- (orgContext.oauthScope === "app" ? [flow.appId] : []),
269
+ allowedApps: existing?.allowedApps ?? (flow.scope === "app" ? [flow.appId] : []),
270
+ allowedUsers: flow.scope === "user"
271
+ ? [session.email]
272
+ : (existing?.allowedUsers ?? []),
234
273
  config: connectionConfig,
235
274
  lastCheckedAt: new Date(),
236
275
  lastError: null,
@@ -267,7 +306,7 @@ export function buildWorkspaceProviderAuthorizationUrl(input) {
267
306
  url.searchParams.set("audience", "api.atlassian.com");
268
307
  url.searchParams.set("prompt", "consent");
269
308
  }
270
- if (input.provider.id === "google_drive") {
309
+ if (isGoogleWorkspaceOAuthProvider(input.provider.id)) {
271
310
  url.searchParams.set("access_type", "offline");
272
311
  url.searchParams.set("include_granted_scopes", "true");
273
312
  url.searchParams.set("prompt", "consent");
@@ -393,7 +432,7 @@ export async function exchangeWorkspaceProviderOAuthCode(input) {
393
432
  ...(input.providerId === "sentry"
394
433
  ? { client_id: input.clientId, client_secret: input.clientSecret }
395
434
  : {}),
396
- ...(input.providerId === "google_drive"
435
+ ...(isGoogleWorkspaceOAuthProvider(input.providerId)
397
436
  ? { client_id: input.clientId, client_secret: input.clientSecret }
398
437
  : {}),
399
438
  };
@@ -566,19 +605,16 @@ async function resolveWorkspaceProviderIdentitySingle(providerId, tokens) {
566
605
  label: text(user?.email) ?? text(user?.name) ?? "Sentry account",
567
606
  };
568
607
  }
569
- if (providerId === "google_drive") {
608
+ if (isGoogleWorkspaceOAuthProvider(providerId)) {
570
609
  const accessToken = text(tokens.access_token);
571
- const { response, body } = await fetchBoundedProviderJson("https://www.googleapis.com/drive/v3/about?fields=user(displayName,emailAddress,permissionId)", { headers: { Authorization: `Bearer ${accessToken}` } }, "Google Drive");
572
- const user = record(body.user);
573
- const accountId = text(user?.permissionId) ?? text(user?.emailAddress);
610
+ const { response, body } = await fetchBoundedProviderJson("https://openidconnect.googleapis.com/v1/userinfo", { headers: { Authorization: `Bearer ${accessToken}` } }, "Google Workspace");
611
+ const accountId = text(body.email) ?? text(body.sub);
574
612
  if (!response.ok || !accountId) {
575
- throw new Error("Google Drive OAuth response did not identify the connected account.");
613
+ throw new Error("Google OAuth response did not identify the connected account.");
576
614
  }
577
615
  return {
578
616
  accountId,
579
- label: text(user?.emailAddress) ??
580
- text(user?.displayName) ??
581
- "Google Drive account",
617
+ label: text(body.email) ?? text(body.name) ?? "Google account",
582
618
  };
583
619
  }
584
620
  const accessToken = text(tokens.access_token);
@@ -615,6 +651,7 @@ export function isWorkspaceProviderOAuthFlowValid(input) {
615
651
  input.state.flowId === input.flow.flowId &&
616
652
  input.state.redirectUri === input.flow.redirectUri &&
617
653
  input.state.owner === input.flow.owner &&
654
+ input.state.scope === input.flow.scope &&
618
655
  input.sessionEmail === input.flow.owner &&
619
656
  input.state.orgId === input.flow.orgId &&
620
657
  input.sessionOrgId === input.flow.orgId &&
@@ -697,7 +734,9 @@ export function mergeWorkspaceOAuthValues(existing, incoming) {
697
734
  return [...new Set([...existing, ...incoming])];
698
735
  }
699
736
  function clientCredentialKeys(provider, field) {
700
- const prefix = provider === "google_drive" ? "GOOGLE" : provider.toUpperCase();
737
+ const prefix = isGoogleWorkspaceOAuthProvider(provider)
738
+ ? "GOOGLE"
739
+ : provider.toUpperCase();
701
740
  const suffix = field === "id" ? "ID" : "SECRET";
702
741
  if (provider === "github") {
703
742
  const integrationPrefix = `GITHUB_INTEGRATION_CLIENT_${suffix}`;
@@ -799,6 +838,11 @@ function normalizeAppId(value) {
799
838
  function text(value) {
800
839
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
801
840
  }
841
+ function parseWorkspaceProviderOAuthScope(value) {
842
+ if (value === undefined)
843
+ return "organization";
844
+ return isWorkspaceProviderOAuthScope(value) ? value : null;
845
+ }
802
846
  function scalarText(value) {
803
847
  if (typeof value === "number" && Number.isFinite(value))
804
848
  return String(value);
@@ -817,15 +861,23 @@ function unauthorized(event) {
817
861
  setResponseStatus(event, 401);
818
862
  return { error: "Authentication required" };
819
863
  }
820
- async function requireWorkspaceProviderOAuthAdmin(event, appId) {
864
+ async function requireWorkspaceProviderOAuthAccess(event, appId, requestedScope = "organization") {
821
865
  const context = await getOrgContext(event).catch(() => null);
822
866
  if (!context || !context.orgId) {
823
867
  setResponseStatus(event, 403);
824
868
  return null;
825
869
  }
826
- if (canConnectWorkspaceProviderOAuth(context.orgId, context.role)) {
870
+ if (requestedScope === "user") {
871
+ return { ...context, oauthScope: "user" };
872
+ }
873
+ if (requestedScope === "organization" &&
874
+ canConnectWorkspaceProviderOAuth(context.orgId, context.role)) {
827
875
  return { ...context, oauthScope: "organization" };
828
876
  }
877
+ if (requestedScope === "app" &&
878
+ canConnectWorkspaceProviderOAuth(context.orgId, context.role)) {
879
+ return { ...context, oauthScope: "app" };
880
+ }
829
881
  if (appId) {
830
882
  const descriptor = getRegisteredAppRoles(appId);
831
883
  if (descriptor?.roles.some((role) => role === "admin")) {
@@ -1,5 +1,6 @@
1
1
  export declare const AGENT_NATIVE_OPEN_PATH = "/_agent-native/open";
2
2
  export declare const AGENT_SIDEBAR_QUERY_PARAM = "agentSidebar";
3
3
  export declare const AGENT_SIDEBAR_QUERY_VALUE_CLOSED = "closed";
4
+ export declare const AGENT_SIDEBAR_QUERY_VALUE_OPEN = "open";
4
5
  export declare function isAgentNativeOpenDeepLink(urlOrPath: string): boolean;
5
6
  export declare function withCollapsedAgentSidebarParam(urlOrPath: string): string;
@@ -1,6 +1,7 @@
1
1
  export const AGENT_NATIVE_OPEN_PATH = "/_agent-native/open";
2
2
  export const AGENT_SIDEBAR_QUERY_PARAM = "agentSidebar";
3
3
  export const AGENT_SIDEBAR_QUERY_VALUE_CLOSED = "closed";
4
+ export const AGENT_SIDEBAR_QUERY_VALUE_OPEN = "open";
4
5
  const RELATIVE_URL_BASE = "http://agent-native.invalid";
5
6
  function hasUrlScheme(urlOrPath) {
6
7
  return /^[a-z][a-z0-9+.-]*:/i.test(urlOrPath);
@@ -1,6 +1,8 @@
1
1
  export declare const BETA_OPT_OUT_QUERY_PARAM = "agentNativeBetaOptOut";
2
2
  export declare const BETA_OPT_OUT_DURATION_MS: number;
3
3
  export declare const BETA_OPT_OUT_STORAGE_KEY = "agent-native:beta-opt-out-until";
4
+ export declare const BETA_FORCE_QUERY_PARAM = "force";
5
+ export declare const BETA_FORCE_SESSION_STORAGE_KEY = "agent-native:force-production";
4
6
  export declare const ENVIRONMENT_BETA_HOSTS: {
5
7
  readonly "agent-workspace.builder.io": "beta.agent-workspace.builder.io";
6
8
  readonly "analytics.agent-native.com": "beta.analytics.agent-native.com";
@@ -1,6 +1,8 @@
1
1
  export const BETA_OPT_OUT_QUERY_PARAM = "agentNativeBetaOptOut";
2
- export const BETA_OPT_OUT_DURATION_MS = 24 * 60 * 60 * 1000;
2
+ export const BETA_OPT_OUT_DURATION_MS = 8 * 60 * 60 * 1000;
3
3
  export const BETA_OPT_OUT_STORAGE_KEY = "agent-native:beta-opt-out-until";
4
+ export const BETA_FORCE_QUERY_PARAM = "force";
5
+ export const BETA_FORCE_SESSION_STORAGE_KEY = "agent-native:force-production";
4
6
  export const ENVIRONMENT_BETA_HOSTS = {
5
7
  "agent-workspace.builder.io": "beta.agent-workspace.builder.io",
6
8
  "analytics.agent-native.com": "beta.analytics.agent-native.com",
@@ -6,13 +6,15 @@ export { truncate } from "./truncate.js";
6
6
  export { isHumanReadableDocumentTitle, normalizeDocumentTitle, } from "./document-title.js";
7
7
  export { injectDocumentMarkup } from "./html-document.js";
8
8
  export { withBuilderUtmTrackingParams } from "./builder-link-tracking.js";
9
+ export { BETA_FORCE_QUERY_PARAM, BETA_FORCE_SESSION_STORAGE_KEY, BETA_OPT_OUT_DURATION_MS, BETA_OPT_OUT_QUERY_PARAM, BETA_OPT_OUT_STORAGE_KEY, ENVIRONMENT_BETA_HOSTS, resolveEnvironmentTargets, type EnvironmentBadgeTargets, } from "./environment-lanes.js";
9
10
  export { SSR_HTML_CONTENT_TYPE, SSR_QUERY_CACHE_KEY_HEADER, type SsrHtmlContentTypeOptions, withSsrHtmlContentType, } from "./cache-control.js";
11
+ export { SURFACE_HIDDEN_FLAG, SURFACE_VISIBILITY_EVENT, addSurfaceVisibilityListener, buildSurfaceVisibilityScript, isHostSurfaceHidden, isSurfaceHidden, } from "./surface-visibility.js";
10
12
  export { AGENT_NATIVE_DOCS_ORIGIN, docsUrl, type DocsUrlOptions, } from "./docs-url.js";
11
13
  export { buildRuntimeConfigPrompt, formatRuntimeConfigReport, getRuntimeConfigReport, parseRuntimeConfigReport, runtimeConfigRequirementsFromSearchParams, type RuntimeConfigEnvironment, type RuntimeConfigIssue, type RuntimeConfigIssueCode, type RuntimeConfigIssueSeverity, type RuntimeConfigPhase, type RuntimeConfigReport, type RuntimeConfigRequirements, } from "./runtime-config.js";
12
14
  export { llmConnectionTrackingProperties, normalizeLlmConnection, type LlmConnectionStatus, } from "./llm-connection.js";
13
15
  export { DISPATCH_WORKSPACE_ROOT_REDIRECTS, RESERVED_WORKSPACE_APP_IDS, assertValidWorkspaceAppId, getWorkspaceAppIdValidationError, isValidWorkspaceAppIdFormat, normalizeWorkspaceAppId, } from "./workspace-app-id.js";
14
16
  export { DEFAULT_WORKSPACE_APP_AUDIENCE, WORKSPACE_APP_AUDIENCES, normalizeWorkspaceAppAudience, normalizeWorkspaceAppPathList, workspaceAppAudienceFromEnv, workspaceAppAudienceFromPackageJson, workspaceAppRouteAccessFromEnv, workspaceAppRouteAccessFromPackageJson, type WorkspaceAppRouteAccess, type WorkspaceAppRouteAccessFromConfig, type WorkspaceAppAudience, } from "./workspace-app-audience.js";
15
- export { AGENT_NATIVE_OPEN_PATH, AGENT_SIDEBAR_QUERY_PARAM, AGENT_SIDEBAR_QUERY_VALUE_CLOSED, isAgentNativeOpenDeepLink, withCollapsedAgentSidebarParam, } from "./agent-sidebar-url.js";
17
+ export { AGENT_NATIVE_OPEN_PATH, AGENT_SIDEBAR_QUERY_PARAM, AGENT_SIDEBAR_QUERY_VALUE_CLOSED, AGENT_SIDEBAR_QUERY_VALUE_OPEN, isAgentNativeOpenDeepLink, withCollapsedAgentSidebarParam, } from "./agent-sidebar-url.js";
16
18
  export { buildChatFirstAppCreationPrompt, titleFromChatFirstAppPrompt, type ChatFirstAppCreationPromptInput, type ChatFirstAppCreationResource, type ChatFirstAppCreationVaultAccessMode, } from "./chat-first-app-creation.js";
17
19
  export { NATIVE_AUTH_COPY, resolveNativeAuthCopy, type NativeAuthCopy, } from "./auth-copy.js";
18
20
  export { createPollEngine, type PollEngineOptions, type PollEngineHandle, } from "./poll-engine.js";
@@ -6,13 +6,15 @@ export { truncate } from "./truncate.js";
6
6
  export { isHumanReadableDocumentTitle, normalizeDocumentTitle, } from "./document-title.js";
7
7
  export { injectDocumentMarkup } from "./html-document.js";
8
8
  export { withBuilderUtmTrackingParams } from "./builder-link-tracking.js";
9
+ export { BETA_FORCE_QUERY_PARAM, BETA_FORCE_SESSION_STORAGE_KEY, BETA_OPT_OUT_DURATION_MS, BETA_OPT_OUT_QUERY_PARAM, BETA_OPT_OUT_STORAGE_KEY, ENVIRONMENT_BETA_HOSTS, resolveEnvironmentTargets, } from "./environment-lanes.js";
9
10
  export { SSR_HTML_CONTENT_TYPE, SSR_QUERY_CACHE_KEY_HEADER, withSsrHtmlContentType, } from "./cache-control.js";
11
+ export { SURFACE_HIDDEN_FLAG, SURFACE_VISIBILITY_EVENT, addSurfaceVisibilityListener, buildSurfaceVisibilityScript, isHostSurfaceHidden, isSurfaceHidden, } from "./surface-visibility.js";
10
12
  export { AGENT_NATIVE_DOCS_ORIGIN, docsUrl, } from "./docs-url.js";
11
13
  export { buildRuntimeConfigPrompt, formatRuntimeConfigReport, getRuntimeConfigReport, parseRuntimeConfigReport, runtimeConfigRequirementsFromSearchParams, } from "./runtime-config.js";
12
14
  export { llmConnectionTrackingProperties, normalizeLlmConnection, } from "./llm-connection.js";
13
15
  export { DISPATCH_WORKSPACE_ROOT_REDIRECTS, RESERVED_WORKSPACE_APP_IDS, assertValidWorkspaceAppId, getWorkspaceAppIdValidationError, isValidWorkspaceAppIdFormat, normalizeWorkspaceAppId, } from "./workspace-app-id.js";
14
16
  export { DEFAULT_WORKSPACE_APP_AUDIENCE, WORKSPACE_APP_AUDIENCES, normalizeWorkspaceAppAudience, normalizeWorkspaceAppPathList, workspaceAppAudienceFromEnv, workspaceAppAudienceFromPackageJson, workspaceAppRouteAccessFromEnv, workspaceAppRouteAccessFromPackageJson, } from "./workspace-app-audience.js";
15
- export { AGENT_NATIVE_OPEN_PATH, AGENT_SIDEBAR_QUERY_PARAM, AGENT_SIDEBAR_QUERY_VALUE_CLOSED, isAgentNativeOpenDeepLink, withCollapsedAgentSidebarParam, } from "./agent-sidebar-url.js";
17
+ export { AGENT_NATIVE_OPEN_PATH, AGENT_SIDEBAR_QUERY_PARAM, AGENT_SIDEBAR_QUERY_VALUE_CLOSED, AGENT_SIDEBAR_QUERY_VALUE_OPEN, isAgentNativeOpenDeepLink, withCollapsedAgentSidebarParam, } from "./agent-sidebar-url.js";
16
18
  export { buildChatFirstAppCreationPrompt, titleFromChatFirstAppPrompt, } from "./chat-first-app-creation.js";
17
19
  export { NATIVE_AUTH_COPY, resolveNativeAuthCopy, } from "./auth-copy.js";
18
20
  export { createPollEngine, } from "./poll-engine.js";
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Host-declared surface visibility.
3
+ *
4
+ * An Electron `<webview>` guest never reports itself hidden: hiding the element
5
+ * or any ancestor leaves `document.visibilityState === "visible"` and never
6
+ * fires `visibilitychange`, so every visibility-based pause in this package is
7
+ * inert inside the desktop shell. A backgrounded app tab there keeps polling at
8
+ * its full foreground cadence and holds its event stream open for as long as
9
+ * the shell is running. The host therefore has to say so explicitly, and this
10
+ * flag is what background work must consult instead of the document alone.
11
+ */
12
+ export declare const SURFACE_HIDDEN_FLAG = "__agentNativeSurfaceHidden";
13
+ /** Dispatched on `window` whenever the host flips the flag. */
14
+ export declare const SURFACE_VISIBILITY_EVENT = "agentnative:surfacevisibilitychange";
15
+ /** Whether an embedding host has declared this surface off screen. */
16
+ export declare function isHostSurfaceHidden(): boolean;
17
+ /**
18
+ * Whether this surface is off screen by either signal. A host that has stashed
19
+ * the surface entirely is a stronger statement than a backgrounded browser tab,
20
+ * so callers that keep working while merely backgrounded should still stop on
21
+ * `isHostSurfaceHidden()`.
22
+ */
23
+ export declare function isSurfaceHidden(): boolean;
24
+ /** Subscribe to both visibility signals. Returns an unsubscribe function. */
25
+ export declare function addSurfaceVisibilityListener(handler: () => void): () => void;
26
+ /**
27
+ * Script a host embedder evaluates inside the guest to declare whether the
28
+ * surface it is hosting is on screen. Kept here so the flag name, the event
29
+ * name, and the write that sets them cannot drift apart across packages.
30
+ */
31
+ export declare function buildSurfaceVisibilityScript(hidden: boolean): string;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Host-declared surface visibility.
3
+ *
4
+ * An Electron `<webview>` guest never reports itself hidden: hiding the element
5
+ * or any ancestor leaves `document.visibilityState === "visible"` and never
6
+ * fires `visibilitychange`, so every visibility-based pause in this package is
7
+ * inert inside the desktop shell. A backgrounded app tab there keeps polling at
8
+ * its full foreground cadence and holds its event stream open for as long as
9
+ * the shell is running. The host therefore has to say so explicitly, and this
10
+ * flag is what background work must consult instead of the document alone.
11
+ */
12
+ export const SURFACE_HIDDEN_FLAG = "__agentNativeSurfaceHidden";
13
+ /** Dispatched on `window` whenever the host flips the flag. */
14
+ export const SURFACE_VISIBILITY_EVENT = "agentnative:surfacevisibilitychange";
15
+ /** Whether an embedding host has declared this surface off screen. */
16
+ export function isHostSurfaceHidden() {
17
+ if (typeof window === "undefined")
18
+ return false;
19
+ return (window[SURFACE_HIDDEN_FLAG] === true);
20
+ }
21
+ /**
22
+ * Whether this surface is off screen by either signal. A host that has stashed
23
+ * the surface entirely is a stronger statement than a backgrounded browser tab,
24
+ * so callers that keep working while merely backgrounded should still stop on
25
+ * `isHostSurfaceHidden()`.
26
+ */
27
+ export function isSurfaceHidden() {
28
+ if (isHostSurfaceHidden())
29
+ return true;
30
+ return (typeof document !== "undefined" && document.visibilityState === "hidden");
31
+ }
32
+ /** Subscribe to both visibility signals. Returns an unsubscribe function. */
33
+ export function addSurfaceVisibilityListener(handler) {
34
+ if (typeof document === "undefined" || typeof window === "undefined") {
35
+ return () => { };
36
+ }
37
+ document.addEventListener("visibilitychange", handler);
38
+ window.addEventListener(SURFACE_VISIBILITY_EVENT, handler);
39
+ return () => {
40
+ document.removeEventListener("visibilitychange", handler);
41
+ window.removeEventListener(SURFACE_VISIBILITY_EVENT, handler);
42
+ };
43
+ }
44
+ /**
45
+ * Script a host embedder evaluates inside the guest to declare whether the
46
+ * surface it is hosting is on screen. Kept here so the flag name, the event
47
+ * name, and the write that sets them cannot drift apart across packages.
48
+ */
49
+ export function buildSurfaceVisibilityScript(hidden) {
50
+ return `(() => {
51
+ const next = ${hidden ? "true" : "false"};
52
+ if (window[${JSON.stringify(SURFACE_HIDDEN_FLAG)}] === next) return next;
53
+ window[${JSON.stringify(SURFACE_HIDDEN_FLAG)}] = next;
54
+ window.dispatchEvent(new Event(${JSON.stringify(SURFACE_VISIBILITY_EVENT)}));
55
+ return next;
56
+ })()`;
57
+ }
@@ -19,10 +19,17 @@ data, and generated extension/app content may mention credential **names** such
19
19
  as `OPENAI_API_KEY`, but must not contain real API keys, tokens, webhook URLs,
20
20
  signing secrets, OAuth refresh tokens, or private Builder/customer data.
21
21
 
22
- Secret values are supplied at runtime through deployment configuration, the
23
- encrypted `app_secrets` vault, `saveCredential` / `resolveCredential`, OAuth, or
24
- `${keys.NAME}` substitution. Examples must use obvious placeholders such as
25
- `<OPENAI_API_KEY>` or `${keys.SLACK_WEBHOOK}`, not real-looking copied values.
22
+ Provider secret values are supplied at runtime through the encrypted
23
+ `app_secrets` vault, `saveCredential` / `resolveCredential`, OAuth, or
24
+ `${keys.NAME}` substitution. Deployment configuration is reserved for
25
+ deploy-level secrets and non-provider configuration. Examples must use obvious
26
+ placeholders such as `<OPENAI_API_KEY>` or `${keys.SLACK_WEBHOOK}`, not
27
+ real-looking copied values.
28
+
29
+ Provider credentials and provider account identifiers are workspace data. Use
30
+ standard workspace connections and org/workspace vault scopes; never put them
31
+ in `.env` or deployment environment variables, and never add a provider-specific
32
+ action or startup bootstrap just to write a credential for one organization.
26
33
 
27
34
  ## Credential Modeling Preflight
28
35
 
@@ -1,4 +1,6 @@
1
1
  import { type RouteConfig } from "@react-router/dev/routes";
2
2
  import { flatRoutes } from "@react-router/fs-routes";
3
3
 
4
- export default flatRoutes() satisfies RouteConfig;
4
+ export default flatRoutes({
5
+ ignoredRouteFiles: ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"],
6
+ }) satisfies RouteConfig;
@@ -11,4 +11,12 @@ export default defineConfig({
11
11
  "@shared": path.resolve(__dirname, "./shared"),
12
12
  },
13
13
  },
14
+ test: {
15
+ exclude: [
16
+ "**/node_modules/**",
17
+ "**/.git/**",
18
+ "**/dist/**",
19
+ "**/.react-router/**",
20
+ ],
21
+ },
14
22
  });
@@ -19,10 +19,17 @@ data, and generated extension/app content may mention credential **names** such
19
19
  as `OPENAI_API_KEY`, but must not contain real API keys, tokens, webhook URLs,
20
20
  signing secrets, OAuth refresh tokens, or private Builder/customer data.
21
21
 
22
- Secret values are supplied at runtime through deployment configuration, the
23
- encrypted `app_secrets` vault, `saveCredential` / `resolveCredential`, OAuth, or
24
- `${keys.NAME}` substitution. Examples must use obvious placeholders such as
25
- `<OPENAI_API_KEY>` or `${keys.SLACK_WEBHOOK}`, not real-looking copied values.
22
+ Provider secret values are supplied at runtime through the encrypted
23
+ `app_secrets` vault, `saveCredential` / `resolveCredential`, OAuth, or
24
+ `${keys.NAME}` substitution. Deployment configuration is reserved for
25
+ deploy-level secrets and non-provider configuration. Examples must use obvious
26
+ placeholders such as `<OPENAI_API_KEY>` or `${keys.SLACK_WEBHOOK}`, not
27
+ real-looking copied values.
28
+
29
+ Provider credentials and provider account identifiers are workspace data. Use
30
+ standard workspace connections and org/workspace vault scopes; never put them
31
+ in `.env` or deployment environment variables, and never add a provider-specific
32
+ action or startup bootstrap just to write a credential for one organization.
26
33
 
27
34
  ## Credential Modeling Preflight
28
35
 
@@ -1,4 +1,6 @@
1
1
  import { type RouteConfig } from "@react-router/dev/routes";
2
2
  import { flatRoutes } from "@react-router/fs-routes";
3
3
 
4
- export default flatRoutes() satisfies RouteConfig;
4
+ export default flatRoutes({
5
+ ignoredRouteFiles: ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"],
6
+ }) satisfies RouteConfig;
@@ -19,10 +19,17 @@ data, and generated extension/app content may mention credential **names** such
19
19
  as `OPENAI_API_KEY`, but must not contain real API keys, tokens, webhook URLs,
20
20
  signing secrets, OAuth refresh tokens, or private Builder/customer data.
21
21
 
22
- Secret values are supplied at runtime through deployment configuration, the
23
- encrypted `app_secrets` vault, `saveCredential` / `resolveCredential`, OAuth, or
24
- `${keys.NAME}` substitution. Examples must use obvious placeholders such as
25
- `<OPENAI_API_KEY>` or `${keys.SLACK_WEBHOOK}`, not real-looking copied values.
22
+ Provider secret values are supplied at runtime through the encrypted
23
+ `app_secrets` vault, `saveCredential` / `resolveCredential`, OAuth, or
24
+ `${keys.NAME}` substitution. Deployment configuration is reserved for
25
+ deploy-level secrets and non-provider configuration. Examples must use obvious
26
+ placeholders such as `<OPENAI_API_KEY>` or `${keys.SLACK_WEBHOOK}`, not
27
+ real-looking copied values.
28
+
29
+ Provider credentials and provider account identifiers are workspace data. Use
30
+ standard workspace connections and org/workspace vault scopes; never put them
31
+ in `.env` or deployment environment variables, and never add a provider-specific
32
+ action or startup bootstrap just to write a credential for one organization.
26
33
 
27
34
  ## Credential Modeling Preflight
28
35
 
@@ -19,10 +19,17 @@ data, and generated extension/app content may mention credential **names** such
19
19
  as `OPENAI_API_KEY`, but must not contain real API keys, tokens, webhook URLs,
20
20
  signing secrets, OAuth refresh tokens, or private Builder/customer data.
21
21
 
22
- Secret values are supplied at runtime through deployment configuration, the
23
- encrypted `app_secrets` vault, `saveCredential` / `resolveCredential`, OAuth, or
24
- `${keys.NAME}` substitution. Examples must use obvious placeholders such as
25
- `<OPENAI_API_KEY>` or `${keys.SLACK_WEBHOOK}`, not real-looking copied values.
22
+ Provider secret values are supplied at runtime through the encrypted
23
+ `app_secrets` vault, `saveCredential` / `resolveCredential`, OAuth, or
24
+ `${keys.NAME}` substitution. Deployment configuration is reserved for
25
+ deploy-level secrets and non-provider configuration. Examples must use obvious
26
+ placeholders such as `<OPENAI_API_KEY>` or `${keys.SLACK_WEBHOOK}`, not
27
+ real-looking copied values.
28
+
29
+ Provider credentials and provider account identifiers are workspace data. Use
30
+ standard workspace connections and org/workspace vault scopes; never put them
31
+ in `.env` or deployment environment variables, and never add a provider-specific
32
+ action or startup bootstrap just to write a credential for one organization.
26
33
 
27
34
  ## Credential Modeling Preflight
28
35
 
@@ -8,6 +8,17 @@ export interface TrackingMeta {
8
8
  anonymousId?: string;
9
9
  /** Overrides the ambient request's browser session. */
10
10
  sessionId?: string;
11
+ /**
12
+ * When the event actually happened, in epoch ms. Defaults to now.
13
+ *
14
+ * Needed by callers that buffer and flush a batch of events at the end of a
15
+ * unit of work — an agent run emits its trace, generation, and tool spans in
16
+ * one burst, and stamping all of them with the flush time collapses a
17
+ * multi-second waterfall into a single instant. PostHog orders an LLM trace
18
+ * tree by event timestamp, so without this the tree renders with a synthetic
19
+ * timeline.
20
+ */
21
+ occurredAt?: number;
11
22
  }
12
23
  /**
13
24
  * Who an event is attributed to. Pass an action's `ctx` straight through —
@@ -42,10 +42,11 @@ function resolveTrackingSource(source) {
42
42
  userId: source.userId,
43
43
  anonymousId: source.anonymousId,
44
44
  sessionId: source.sessionId ?? ambientSessionId,
45
+ occurredAt: source.occurredAt,
45
46
  };
46
47
  }
47
48
  export function track(name, properties, source) {
48
- const { userId, anonymousId, sessionId } = resolveTrackingSource(source);
49
+ const { userId, anonymousId, sessionId, occurredAt } = resolveTrackingSource(source);
49
50
  const clientPlatform = getRequestContext()?.clientPlatform;
50
51
  const trackedProperties = {
51
52
  ...(properties ?? {}),
@@ -57,7 +58,9 @@ export function track(name, properties, source) {
57
58
  const event = {
58
59
  name,
59
60
  properties: trackedProperties,
60
- timestamp: new Date().toISOString(),
61
+ // A caller-supplied `occurredAt` of 0 is not a real event time, so `||`
62
+ // rather than `??` is deliberate here.
63
+ timestamp: new Date(occurredAt || Date.now()).toISOString(),
61
64
  userId,
62
65
  anonymousId,
63
66
  sessionId,
@@ -301,12 +301,15 @@ async function handleRunNow(args, getCurrentUser, appId, context) {
301
301
  return "Error: an automation cannot run another automation.";
302
302
  }
303
303
  try {
304
+ const path = typeof args.path === "string" ? args.path.trim() : "";
305
+ const name = typeof args.name === "string" ? args.name : "";
304
306
  const result = await queueAutomationRunNow({
305
307
  userEmail: getCurrentUser(),
306
308
  orgId: getRequestOrgId(),
307
309
  appId,
308
310
  scope: automationScope(args.scope),
309
- name: typeof args.name === "string" ? args.name : "",
311
+ ...(path ? { path } : { name }),
312
+ requestHeaders: context?.requestHeaders,
310
313
  });
311
314
  return JSON.stringify(result);
312
315
  }
@@ -340,7 +343,7 @@ export function createAutomationToolEntries(getCurrentUser, appId) {
340
343
  - **update**: Update an existing automation's settings without changing its creator (enabled, schedule, timezone, condition, body, policy, model, execution host, MCP allowlist). Required param: name. Use the same scope it was created in.
341
344
  - **delete**: Delete an automation. Always confirm with the user first. Required param: name.
342
345
  - **fire-test**: Fire a test event to validate automations. Emits a test.event.fired event. Optional param: data (JSON string).
343
- - **run-now**: Run one automation immediately using its real actions and side effects. This is an explicit user-authorized run and returns a durable run id; it does not change the automation's next scheduled run. Required params: name; optional scope.`,
346
+ - **run-now**: Run one automation immediately using its real actions and side effects. This is an explicit user-authorized run and returns a durable run id; it does not change the automation's next scheduled run. Required params: name or path (not both); optional scope. Use path for automations nested under jobs/ (for example jobs/factories/<id>/factory-slack-feedback.md); those names contain a slash and cannot round-trip through name.`,
344
347
  parameters: {
345
348
  type: "object",
346
349
  properties: {
@@ -351,7 +354,11 @@ export function createAutomationToolEntries(getCurrentUser, appId) {
351
354
  },
352
355
  name: {
353
356
  type: "string",
354
- description: "Slug name for the automation (lowercase, hyphens). Used by define, update, delete, and run-now.",
357
+ description: "Slug name for the automation (lowercase, hyphens). Used by define, update, delete, and run-now for flat automations. For nested automations, pass path to run-now instead.",
358
+ },
359
+ path: {
360
+ type: "string",
361
+ description: "Full jobs resource path (jobs/...md) for a nested automation. Use with run-now instead of name when the automation name contains a slash.",
355
362
  },
356
363
  scope: {
357
364
  type: "string",
@@ -207,6 +207,17 @@ export async function ensureUsageTable() {
207
207
  // Probe pg_indexes first (no lock) and skip the SHARE-locking CREATE
208
208
  // INDEX when the index is already present.
209
209
  await ensureIndexExists("idx_token_usage_owner_created", `CREATE INDEX IF NOT EXISTS idx_token_usage_owner_created ON token_usage (owner_email, created_at)`);
210
+ // `owner_email` is written as the caller supplied it, so the metrics
211
+ // queries scope with `LOWER(owner_email) IN (…)`. A plain btree cannot
212
+ // serve a function-wrapped predicate: without this expression index the
213
+ // usage panel scans the whole table, which is the highest-row-count one
214
+ // in the system (a row per LLM call, every app and org).
215
+ // NOT built CONCURRENTLY. This runs at release over the pooled Neon
216
+ // endpoint, and a transaction-pooled connection cannot carry
217
+ // `CREATE INDEX CONCURRENTLY` to completion — it returns without
218
+ // creating the index, which then fails the verifying probe and blocks
219
+ // the whole release. The SHARE lock is the cost of a build that lands.
220
+ await ensureIndexExists("idx_token_usage_lower_owner_created", `CREATE INDEX IF NOT EXISTS idx_token_usage_lower_owner_created ON token_usage (LOWER(owner_email), created_at)`);
210
221
  return;
211
222
  }
212
223
  // SQLite (local dev): no lock problem — keep the original behaviour.
@@ -226,10 +237,19 @@ export async function ensureUsageTable() {
226
237
  // the `Date.now()` written per run by recordUsage() overflows int4. Widen
227
238
  // it in place (no-op once done / on fresh BIGINT databases).
228
239
  await widenIntColumnsToBigInt("token_usage", ["created_at"]);
229
- try {
230
- await client.execute(`CREATE INDEX IF NOT EXISTS idx_token_usage_owner_created ON token_usage (owner_email, created_at)`);
240
+ for (const ddl of [
241
+ `CREATE INDEX IF NOT EXISTS idx_token_usage_owner_created ON token_usage (owner_email, created_at)`,
242
+ `CREATE INDEX IF NOT EXISTS idx_token_usage_lower_owner_created ON token_usage (LOWER(owner_email), created_at)`,
243
+ ]) {
244
+ try {
245
+ await client.execute(ddl);
246
+ }
247
+ catch {
248
+ // coercion-ok: index already exists, or this dialect rejected the
249
+ // duplicate. Local-dev SQLite only — the hosted path creates these
250
+ // through the Postgres branch above, which probes before creating.
251
+ }
231
252
  }
232
- catch { }
233
253
  })().catch((err) => {
234
254
  // Retry init on the next call after a failed startup.
235
255
  _initPromise = undefined;