@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
@@ -52,6 +52,43 @@ export function createSmoothNode(
52
52
  };
53
53
  }
54
54
 
55
+ /**
56
+ * Per-drag memory for Figma's Alt-breaks-the-handle-pair gesture on a new pen
57
+ * anchor. Owned by the drag state, not by a single move event.
58
+ */
59
+ export interface PenCuspLatch {
60
+ broken: boolean;
61
+ handleIn?: PenPoint;
62
+ }
63
+
64
+ export function createPenCuspLatch(): PenCuspLatch {
65
+ return { broken: false };
66
+ }
67
+
68
+ /**
69
+ * Builds the anchor a new-anchor drag is currently describing.
70
+ *
71
+ * Alt is a one-way latch for the life of the drag: releasing it does not
72
+ * re-mirror the pair, and breaking it freezes `handleIn` at the tangent it
73
+ * had when Alt went down rather than deleting it, so the segment already
74
+ * drawn keeps its curve. Alt held from the first tick has no earlier tangent
75
+ * to keep, leaving the incoming side a corner.
76
+ */
77
+ export function createPenDragNode(
78
+ anchor: PenPoint,
79
+ handleOut: PenPoint,
80
+ latch: PenCuspLatch,
81
+ altKey: boolean,
82
+ ): PenNode {
83
+ if (altKey) latch.broken = true;
84
+ if (!latch.broken) latch.handleIn = mirrorPoint(anchor, handleOut);
85
+ return {
86
+ point: { ...anchor },
87
+ handleIn: latch.handleIn ? { ...latch.handleIn } : undefined,
88
+ handleOut: { ...handleOut },
89
+ };
90
+ }
91
+
55
92
  export function appendPenNode(path: PenPath | null, node: PenNode): PenPath {
56
93
  return {
57
94
  nodes: [...(path?.nodes ?? []), clonePenNode(node)],
@@ -15,7 +15,13 @@ export default mergeConfig(
15
15
  test: {
16
16
  include: ["**/*.{test,spec}.?(c|m)[jt]s?(x)"],
17
17
  // e2e/ holds Playwright (@playwright/test) specs; run via `pnpm e2e`, not vitest.
18
- exclude: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/e2e/**"],
18
+ exclude: [
19
+ "**/node_modules/**",
20
+ "**/.git/**",
21
+ "**/dist/**",
22
+ "**/.react-router/**",
23
+ "**/e2e/**",
24
+ ],
19
25
  },
20
26
  }),
21
27
  );
@@ -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,10 @@ import {
8
8
  isProviderApiId,
9
9
  listProviderApiCatalog,
10
10
  } from "@agent-native/core/provider-api";
11
+ import {
12
+ hasWorkspaceProviderOAuthCredentials,
13
+ isGoogleWorkspaceOAuthProvider,
14
+ } from "@agent-native/core/server";
11
15
  import {
12
16
  getWorkspaceConnectionAppAccess,
13
17
  listWorkspaceConnectionGrants,
@@ -46,6 +50,8 @@ type GrantSummary = {
46
50
  lastUsedAt?: string | null;
47
51
  };
48
52
 
53
+ type GoogleOAuthAvailability = "configured" | "unconfigured";
54
+
49
55
  function unique(values: string[]) {
50
56
  return Array.from(
51
57
  new Set(values.map((value) => value.trim()).filter(Boolean)),
@@ -117,21 +123,40 @@ export default defineAction({
117
123
  }),
118
124
  http: { method: "GET" },
119
125
  run: async (args) => {
120
- const providers = listWorkspaceConnectionProviders({
126
+ const catalogProviders = listWorkspaceConnectionProviders({
121
127
  capability: args.capability as WorkspaceConnectionCapability | undefined,
122
128
  templateUse: args.templateUse as
123
129
  | WorkspaceConnectionTemplateUse
124
130
  | undefined,
125
131
  });
126
- const connections = await listWorkspaceConnections({
132
+ const googleOAuthAvailability: GoogleOAuthAvailability =
133
+ (await hasWorkspaceProviderOAuthCredentials("gmail"))
134
+ ? "configured"
135
+ : "unconfigured";
136
+ const googleOAuthConfigured = googleOAuthAvailability === "configured";
137
+ const providers = catalogProviders.filter(
138
+ (provider) =>
139
+ googleOAuthConfigured || !isGoogleWorkspaceOAuthProvider(provider.id),
140
+ );
141
+ const allConnections = await listWorkspaceConnections({
127
142
  provider: args.provider,
128
143
  appId: args.appId,
129
144
  includeDisabled: args.includeDisabled,
130
145
  });
131
- const explicitGrants = await listWorkspaceConnectionGrants({
146
+ const connections = allConnections.filter(
147
+ (connection) =>
148
+ googleOAuthConfigured ||
149
+ !isGoogleWorkspaceOAuthProvider(connection.provider),
150
+ );
151
+ const allExplicitGrants = await listWorkspaceConnectionGrants({
132
152
  provider: args.provider,
133
153
  appId: args.appId,
134
154
  });
155
+ const explicitGrants = allExplicitGrants.filter(
156
+ (grant) =>
157
+ googleOAuthConfigured ||
158
+ !isGoogleWorkspaceOAuthProvider(grant.provider),
159
+ );
135
160
  const grantApps = await listGrantApps();
136
161
  const legacyGrants = connections.flatMap<GrantSummary>((connection) => {
137
162
  if (connection.allowedApps.length === 0) {
@@ -246,6 +271,12 @@ export default defineAction({
246
271
  });
247
272
 
248
273
  return {
274
+ availability: {
275
+ googleOAuth: {
276
+ status: googleOAuthAvailability,
277
+ retryable: false,
278
+ },
279
+ },
249
280
  providers: providersWithReadiness,
250
281
  connections,
251
282
  grants,
@@ -5,8 +5,10 @@ import {
5
5
  } from "@agent-native/core/client/hooks";
6
6
  import { useT } from "@agent-native/core/client/i18n";
7
7
  import {
8
+ GoogleProductLogo,
8
9
  IntegrationConnectionChoice,
9
10
  IntegrationGrid,
11
+ startWorkspaceProviderOAuth as startManagedWorkspaceProviderOAuth,
10
12
  } from "@agent-native/core/client/integrations";
11
13
  import { useAppRoles, useOrgRole } from "@agent-native/core/client/org";
12
14
  import {
@@ -361,7 +363,8 @@ interface SetupWizardFormState {
361
363
 
362
364
  interface ProviderChoice {
363
365
  provider: WorkspaceConnectionProvider;
364
- personalIntegration: DefaultMcpIntegration;
366
+ personalIntegration?: DefaultMcpIntegration;
367
+ personalOAuth?: boolean;
365
368
  }
366
369
 
367
370
  const EMPTY_RESPONSE: WorkspaceConnectionsResponse = {
@@ -433,9 +436,18 @@ const MCP_INTEGRATIONS_BY_ID = new Map(
433
436
  ]),
434
437
  );
435
438
 
436
- const PROVIDER_LOGO_IDS: Record<string, string> = {
437
- google_drive: "google-workspace",
438
- };
439
+ const GOOGLE_PRODUCT_BY_PROVIDER = {
440
+ gmail: "gmail",
441
+ google_calendar: "calendar",
442
+ google_docs: "docs",
443
+ google_drive: "drive",
444
+ google_sheets: "sheets",
445
+ google_slides: "slides",
446
+ } as const;
447
+
448
+ function isGoogleWorkspaceProvider(providerId: string): boolean {
449
+ return providerId in GOOGLE_PRODUCT_BY_PROVIDER;
450
+ }
439
451
 
440
452
  function cx(...classes: Array<string | false | null | undefined>) {
441
453
  return classes.filter(Boolean).join(" ");
@@ -446,9 +458,14 @@ function iconForProvider(providerId: string): IconComponent {
446
458
  }
447
459
 
448
460
  function logoForProvider(providerId: string, providerName: string): ReactNode {
449
- const integration = MCP_INTEGRATIONS_BY_ID.get(
450
- PROVIDER_LOGO_IDS[providerId] ?? providerId,
451
- );
461
+ const googleProduct =
462
+ GOOGLE_PRODUCT_BY_PROVIDER[
463
+ providerId as keyof typeof GOOGLE_PRODUCT_BY_PROVIDER
464
+ ];
465
+ if (googleProduct) {
466
+ return <GoogleProductLogo product={googleProduct} className="size-7" />;
467
+ }
468
+ const integration = MCP_INTEGRATIONS_BY_ID.get(providerId);
452
469
  if (!integration?.logoUrl) {
453
470
  const Icon = iconForProvider(providerId);
454
471
  return <Icon size={18} />;
@@ -766,7 +783,10 @@ function summarizeUserAccess(
766
783
  return `${labels.join(", ")}${suffix}`;
767
784
  }
768
785
 
769
- function startWorkspaceProviderOAuth(provider: WorkspaceConnectionProvider) {
786
+ function startWorkspaceProviderOAuth(
787
+ provider: WorkspaceConnectionProvider,
788
+ scope: "user" | "organization" = "organization",
789
+ ) {
770
790
  const returnPath = `${window.location.pathname}${window.location.search}`;
771
791
  if (provider.id === "slack") {
772
792
  const params = new URLSearchParams({ return: returnPath });
@@ -777,15 +797,11 @@ function startWorkspaceProviderOAuth(provider: WorkspaceConnectionProvider) {
777
797
  );
778
798
  return;
779
799
  }
780
- const params = new URLSearchParams({
800
+ startManagedWorkspaceProviderOAuth(provider.id, {
781
801
  appId: "dispatch",
782
- return: returnPath,
802
+ returnPath,
803
+ scope,
783
804
  });
784
- window.location.assign(
785
- agentNativePath(
786
- `/_agent-native/connections/oauth/${provider.id}/start?${params.toString()}`,
787
- ),
788
- );
789
805
  }
790
806
 
791
807
  function providerHasOAuth(provider: WorkspaceConnectionProvider): boolean {
@@ -820,7 +836,10 @@ function ConnectionRow({
820
836
  canManageGrant: (appId: string) => boolean;
821
837
  }) {
822
838
  const t = useT();
823
- const Icon = iconForProvider(connection.provider);
839
+ const providerLogo = logoForProvider(
840
+ connection.provider,
841
+ provider?.label ?? connection.provider,
842
+ );
824
843
  const missingKeys = missingRequiredCredentialKeys(
825
844
  provider,
826
845
  connection.credentialRefs,
@@ -837,7 +856,7 @@ function ConnectionRow({
837
856
  <div className="flex flex-wrap items-start justify-between gap-3">
838
857
  <div className="flex min-w-0 items-start gap-3">
839
858
  <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-md bg-background/80">
840
- <Icon size={18} className="text-muted-foreground" />
859
+ {providerLogo}
841
860
  </div>
842
861
  <div className="min-w-0">
843
862
  <div className="flex flex-wrap items-center gap-2">
@@ -2787,6 +2806,10 @@ export default function WorkspaceIntegrationsRoute() {
2787
2806
  }
2788
2807
 
2789
2808
  function openProviderConnection(provider: WorkspaceConnectionProvider) {
2809
+ if (isGoogleWorkspaceProvider(provider.id)) {
2810
+ setProviderChoice({ provider, personalOAuth: true });
2811
+ return;
2812
+ }
2790
2813
  const personalIntegration = personalIntegrations.get(provider.id);
2791
2814
  if (personalIntegration) {
2792
2815
  setProviderChoice({ provider, personalIntegration });
@@ -3195,14 +3218,16 @@ export default function WorkspaceIntegrationsRoute() {
3195
3218
  actionLabel: connected
3196
3219
  ? canManageConnection(active[0])
3197
3220
  ? "Manage"
3198
- : personalIntegrations.has(provider.id)
3221
+ : personalIntegrations.has(provider.id) ||
3222
+ isGoogleWorkspaceProvider(provider.id)
3199
3223
  ? "Connect for me"
3200
3224
  : "Admin only"
3201
3225
  : "Connect",
3202
3226
  disabled:
3203
3227
  !connected &&
3204
3228
  !canManageConnections &&
3205
- !personalIntegrations.has(provider.id),
3229
+ !personalIntegrations.has(provider.id) &&
3230
+ !isGoogleWorkspaceProvider(provider.id),
3206
3231
  onAction: () =>
3207
3232
  connected
3208
3233
  ? canManageConnection(active[0])
@@ -3416,14 +3441,23 @@ export default function WorkspaceIntegrationsRoute() {
3416
3441
  )}
3417
3442
  showWorkspaceOption={canManageConnections}
3418
3443
  onPersonal={() => {
3419
- setPersonalIntegrationId(providerChoice.personalIntegration.id);
3444
+ const provider = providerChoice.provider;
3420
3445
  setProviderChoice(null);
3446
+ if (providerChoice.personalOAuth) {
3447
+ startWorkspaceProviderOAuth(provider, "user");
3448
+ return;
3449
+ }
3450
+ if (providerChoice.personalIntegration) {
3451
+ setPersonalIntegrationId(
3452
+ providerChoice.personalIntegration.id,
3453
+ );
3454
+ }
3421
3455
  }}
3422
3456
  onWorkspace={() => {
3423
3457
  const provider = providerChoice.provider;
3424
3458
  setProviderChoice(null);
3425
3459
  if (providerHasOAuth(provider)) {
3426
- startWorkspaceProviderOAuth(provider);
3460
+ startWorkspaceProviderOAuth(provider, "organization");
3427
3461
  } else {
3428
3462
  openSetup(provider);
3429
3463
  }
@@ -15,4 +15,6 @@ import { flatRoutes } from "@react-router/fs-routes";
15
15
  * route here via `app/routes/<name>.tsx`, then register the tab in
16
16
  * `app/dispatch-extensions.tsx`.
17
17
  */
18
- export default flatRoutes() satisfies RouteConfig;
18
+ export default flatRoutes({
19
+ ignoredRouteFiles: ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"],
20
+ }) 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
 
@@ -29,13 +29,8 @@ BUILDER_PROJECT_ID=
29
29
  BUILDER_PRIVATE_KEY=
30
30
  FACTORY_BUILDER_WEBHOOK_SECRET=
31
31
 
32
- # Optional deployment fallbacks. Prefer Dispatch Vault and leave these blank
33
- # when the shared workspace secret rows are available.
34
- SLACK_BOT_TOKEN=
35
- GITHUB_TOKEN=
36
- SENTRY_SERVER_TOKEN=
37
- SENTRY_AUTH_TOKEN=
38
- SENTRY_ORG_SLUG=
32
+ # Provider credentials and account identifiers belong in standard shared
33
+ # integrations and the org/workspace vault. Do not put provider keys in .env.
39
34
 
40
35
  # Skip login/signup (local dev / preview only)
41
36
  # AUTH_DISABLED=true
@@ -31,6 +31,9 @@ decisions, feedback, agent runs, and provider audit records.
31
31
  - Provider credentials belong to Dispatch/shared workspace integrations, never
32
32
  to a Factory or Factory graph. Agents use shared provider APIs and connected
33
33
  MCP tools through the workspace grant boundary.
34
+ - Never put provider keys or account identifiers in `.env`, deployment env, or
35
+ Factory actions/bootstrap. Configure standard integrations and resolve them
36
+ from the multitenant org/workspace vault.
34
37
  - For external integrations, inspect the workspace/provider connection catalog first; reuse its scoped resolver.
35
38
 
36
39
  ## Application state
@@ -37,9 +37,11 @@ If Dispatch synced the vault into a different organization, set
37
37
 
38
38
  Connect providers in Dispatch or in Settings -> Integrations. Factory resolves
39
39
  Slack, GitHub, Sentry, Builder, and other supported provider credentials from
40
- the shared workspace vault and only uses matching deployment env vars as a
41
- last-resort fallback. Factory never stores keys per factory. All apps that read
42
- shared `app_secrets` rows must use the same
40
+ the shared workspace vault. Provider deployment environment variables are not
41
+ supported for Factory. To migrate an existing deployment, add the provider
42
+ connection in the standard workspace integration surface, verify it, and then
43
+ remove the old deployment variables. Factory never stores keys per factory. All
44
+ apps that read shared `app_secrets` rows must use the same
43
45
  `WORKSPACE_SECRETS_ENCRYPTION_KEY` (or the workspace's existing shared
44
46
  encryption fallback). Never copy raw tokens between apps or add a second
45
47
  env-only read in a provider client.
@@ -20,6 +20,7 @@ import {
20
20
  } from "../server/lib/factory-automation-plan.js";
21
21
  import {
22
22
  assertUniqueSlackChannelForFactory,
23
+ builderSlackUserIdSchema,
23
24
  factoryConfigRowId,
24
25
  resolveUniqueFactoryId,
25
26
  } from "../server/lib/factory-scope.js";
@@ -40,8 +41,10 @@ export default defineAction({
40
41
  schema: z.object({
41
42
  name: z.string().trim().min(1).max(120),
42
43
  description: z.string().trim().max(500).optional(),
44
+ slackWorkspace: z.enum(["primary", "secondary"]).optional(),
43
45
  slackChannelId: z.string().trim().max(128).optional(),
44
46
  slackChannelName: z.string().trim().max(200).optional(),
47
+ builderSlackUserId: builderSlackUserIdSchema.optional(),
45
48
  observeSlack: z.boolean().optional(),
46
49
  repository: z.string().trim().max(256).optional(),
47
50
  observeGithub: z.boolean().optional(),
@@ -111,7 +114,16 @@ export default defineAction({
111
114
  orgId,
112
115
  });
113
116
 
114
- if (automationPlan.hasConfig) {
117
+ const shouldWriteConfig =
118
+ automationPlan.hasConfig ||
119
+ Boolean(input.slackChannelId?.trim()) ||
120
+ Boolean(input.slackChannelName?.trim()) ||
121
+ Boolean(input.builderSlackUserId?.trim()) ||
122
+ Boolean(input.slackWorkspace) ||
123
+ Boolean(input.sentryOrgSlug?.trim()) ||
124
+ Boolean(input.sentryProjectSlug?.trim()) ||
125
+ Boolean(input.sentryEnvironment?.trim());
126
+ if (shouldWriteConfig) {
115
127
  const slackChannelId = input.slackChannelId?.trim() || null;
116
128
  await assertUniqueSlackChannelForFactory(
117
129
  tx as unknown as typeof db,
@@ -122,10 +134,11 @@ export default defineAction({
122
134
  await tx.insert(triageConfig).values({
123
135
  id: factoryConfigRowId(orgId, factoryId),
124
136
  factoryId,
125
- slackWorkspace: "primary",
137
+ slackWorkspace:
138
+ input.slackWorkspace === "secondary" ? "secondary" : "primary",
126
139
  slackChannelId,
127
140
  slackChannelName: input.slackChannelName?.trim() || null,
128
- builderSlackUserId: null,
141
+ builderSlackUserId: input.builderSlackUserId?.trim() || null,
129
142
  pollingEnabled: automationPlan.pollingEnabled ? 1 : 0,
130
143
  githubPollingEnabled: automationPlan.githubPollingEnabled ? 1 : 0,
131
144
  sentryPollingEnabled: automationPlan.sentryPollingEnabled ? 1 : 0,
@@ -72,6 +72,8 @@ export default defineAction({
72
72
  threadTs: item.threadTs,
73
73
  coverage: hasMore ? "partial" : "complete",
74
74
  messageCount: messages.length,
75
+ itemTitle: item.title,
76
+ itemSummary: item.summary,
75
77
  },
76
78
  },
77
79
  factoryId,
@@ -95,6 +95,8 @@ export default defineAction({
95
95
  feedbackCount: matchingFeedback.length,
96
96
  runCount: runs.length,
97
97
  coverage: item.coverage,
98
+ itemTitle: item.title,
99
+ itemSummary: item.summary,
98
100
  },
99
101
  },
100
102
  factoryId,
@@ -7,9 +7,17 @@ import { and, desc, eq, inArray } from "drizzle-orm";
7
7
  import { z } from "zod";
8
8
 
9
9
  import { getDb } from "../server/db/index.js";
10
- import { factoryAuditEvents } from "../server/db/schema.js";
10
+ import {
11
+ factoryAuditEvents,
12
+ triageItems,
13
+ triageRuns,
14
+ } from "../server/db/schema.js";
15
+ import { projectFactoryAuditReport } from "../server/lib/factory-audit-report.js";
11
16
  import {
12
17
  factoryIdSchema,
18
+ orgFactoryItemFilter,
19
+ orgFactoryRunFilter,
20
+ readAutomationDisplayName,
13
21
  readAutomationFactoryId,
14
22
  } from "../server/lib/factory-scope.js";
15
23
  import {
@@ -39,23 +47,30 @@ export default defineAction({
39
47
  const factoryDefinitions = definitions.filter(
40
48
  ({ meta, name, resource }) =>
41
49
  meta.domain === "factory" &&
42
- readAutomationFactoryId(meta, resource.content) === factoryId &&
50
+ readAutomationFactoryId(meta, resource.content, resource.path) ===
51
+ factoryId &&
43
52
  (!automation || name === automation),
44
53
  );
45
54
  const runGroups = await Promise.all(
46
- factoryDefinitions.map(async ({ name, resource }) =>
47
- listAutomationRuns({
55
+ factoryDefinitions.map(async ({ name, resource }) => {
56
+ const runs = await listAutomationRuns({
48
57
  owners: [resource.owner],
49
58
  automation: name,
50
59
  appId: "factory",
51
60
  limit,
52
- }),
53
- ),
61
+ });
62
+ // Absent must stay distinguishable from a stored label so the client
63
+ // can derive its own fallback instead of rendering the nested path.
64
+ const displayName = readAutomationDisplayName(resource.content);
65
+ return runs.map((run) => ({ run, displayName }));
66
+ }),
54
67
  );
55
- const runs = runGroups.flat().sort((a, b) => b.startedAt - a.startedAt);
56
- const boundedRuns = runs.slice(0, limit);
57
- const runIds = boundedRuns
58
- .map((run) => run.runId)
68
+ const entries = runGroups
69
+ .flat()
70
+ .sort((a, b) => b.run.startedAt - a.run.startedAt);
71
+ const boundedEntries = entries.slice(0, limit);
72
+ const runIds = boundedEntries
73
+ .map(({ run }) => run.runId)
59
74
  .filter((runId): runId is string => Boolean(runId));
60
75
 
61
76
  const db = getDb();
@@ -80,33 +95,98 @@ export default defineAction({
80
95
  eventsByRun.set(event.automationRunId, current);
81
96
  }
82
97
 
98
+ const itemIds = [
99
+ ...new Set(
100
+ events.flatMap((event) => {
101
+ const ids: string[] = [];
102
+ if (event.itemId) ids.push(event.itemId);
103
+ const details = parseDetails(event.detailsJson);
104
+ const listed = details.itemIds;
105
+ if (Array.isArray(listed)) {
106
+ for (const value of listed) {
107
+ if (typeof value === "string" && value) ids.push(value);
108
+ }
109
+ }
110
+ return ids;
111
+ }),
112
+ ),
113
+ ];
114
+ const itemRows = itemIds.length
115
+ ? await db
116
+ .select({
117
+ id: triageItems.id,
118
+ title: triageItems.title,
119
+ summary: triageItems.summary,
120
+ source: triageItems.source,
121
+ sourceUrl: triageItems.sourceUrl,
122
+ })
123
+ .from(triageItems)
124
+ .where(
125
+ and(
126
+ orgFactoryItemFilter(orgId, factoryId),
127
+ inArray(triageItems.id, itemIds),
128
+ ),
129
+ )
130
+ : [];
131
+ const runRows = itemIds.length
132
+ ? await db
133
+ .select({
134
+ itemId: triageRuns.itemId,
135
+ status: triageRuns.status,
136
+ error: triageRuns.error,
137
+ provider: triageRuns.provider,
138
+ startedAt: triageRuns.startedAt,
139
+ })
140
+ .from(triageRuns)
141
+ .where(
142
+ and(
143
+ orgFactoryRunFilter(orgId, factoryId),
144
+ inArray(triageRuns.itemId, itemIds),
145
+ ),
146
+ )
147
+ : [];
148
+
83
149
  return {
84
- runs: boundedRuns.map((run) => ({
85
- id: run.id,
86
- automation: run.automation,
87
- runId: run.runId,
88
- threadId: run.threadId,
89
- status: run.status,
90
- startedAt: run.startedAt,
91
- finishedAt: run.finishedAt,
92
- error: run.error,
93
- events: (eventsByRun.get(run.runId ?? "") ?? []).map((event) => ({
94
- id: event.id,
95
- automationRunId: event.automationRunId,
96
- automationThreadId: event.automationThreadId,
97
- automationName: event.automationName,
98
- itemId: event.itemId,
99
- source: event.source,
100
- sourceUrl: event.sourceUrl,
101
- action: event.action,
102
- kind: event.kind,
103
- status: event.status,
104
- summary: event.summary,
105
- details: parseDetails(event.detailsJson),
106
- createdAt: event.createdAt,
107
- })),
108
- })),
109
- count: boundedRuns.length,
150
+ runs: boundedEntries.map(({ run, displayName }) => {
151
+ const mappedEvents = (eventsByRun.get(run.runId ?? "") ?? []).map(
152
+ (event) => ({
153
+ id: event.id,
154
+ automationRunId: event.automationRunId,
155
+ automationThreadId: event.automationThreadId,
156
+ automationName: event.automationName,
157
+ itemId: event.itemId,
158
+ source: event.source,
159
+ sourceUrl: event.sourceUrl,
160
+ action: event.action,
161
+ kind: event.kind,
162
+ status: event.status,
163
+ summary: event.summary,
164
+ details: parseDetails(event.detailsJson),
165
+ createdAt: event.createdAt,
166
+ }),
167
+ );
168
+ const report = projectFactoryAuditReport(
169
+ mappedEvents,
170
+ itemRows,
171
+ runRows,
172
+ { startedAt: run.startedAt, finishedAt: run.finishedAt },
173
+ );
174
+ return {
175
+ id: run.id,
176
+ automation: run.automation,
177
+ displayName,
178
+ runId: run.runId,
179
+ threadId: run.threadId,
180
+ status: run.status,
181
+ startedAt: run.startedAt,
182
+ finishedAt: run.finishedAt,
183
+ error: run.error,
184
+ counts: report.counts,
185
+ items: report.items,
186
+ trace: report.trace,
187
+ };
188
+ }),
189
+ count: boundedEntries.length,
110
190
  };
111
191
  },
112
192
  });
@@ -43,7 +43,8 @@ export default defineAction({
43
43
  const scoped = definitions.filter(
44
44
  ({ meta, resource }) =>
45
45
  meta.domain === "factory" &&
46
- readAutomationFactoryId(meta, resource.content) === factoryId,
46
+ readAutomationFactoryId(meta, resource.content, resource.path) ===
47
+ factoryId,
47
48
  );
48
49
  return Promise.all(
49
50
  scoped.map(async ({ resource, name, meta, body, canUpdate }) => {