@agent-native/core 0.169.1 → 0.171.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 (463) 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/auth.d.ts +1 -0
  294. package/dist/app-config/auth.js +7 -0
  295. package/dist/app-config/configuration-error.d.ts +11 -0
  296. package/dist/app-config/configuration-error.js +14 -0
  297. package/dist/app-config/index.d.ts +1 -0
  298. package/dist/app-config/index.js +1 -0
  299. package/dist/app-config/integrations.d.ts +1 -0
  300. package/dist/app-config/integrations.js +10 -0
  301. package/dist/app-config/plugins.d.ts +34 -0
  302. package/dist/app-config/plugins.js +39 -0
  303. package/dist/app-config/run-lifecycle-invariants.d.ts +18 -9
  304. package/dist/app-config/run-lifecycle-invariants.js +18 -25
  305. package/dist/app-config/schema.d.ts +17 -0
  306. package/dist/app-config/schema.js +2 -0
  307. package/dist/chat-threads/schema-migrations.js +20 -0
  308. package/dist/chat-threads/store.js +21 -4
  309. package/dist/cli/native-dependencies.d.ts +4 -0
  310. package/dist/cli/native-dependencies.js +15 -0
  311. package/dist/client/EnvironmentBadge.d.ts +1 -1
  312. package/dist/client/EnvironmentBadge.js +40 -5
  313. package/dist/client/MultiTabAssistantChat.d.ts +1 -1
  314. package/dist/client/MultiTabAssistantChat.js +35 -14
  315. package/dist/client/agent-chat/index.d.ts +1 -1
  316. package/dist/client/agent-chat/index.js +1 -1
  317. package/dist/client/agent-page/use-jobs.d.ts +2 -1
  318. package/dist/client/agent-page/use-jobs.js +3 -5
  319. package/dist/client/agent-sidebar-state.js +3 -1
  320. package/dist/client/blocks/library/code-tabs.js +5 -0
  321. package/dist/client/blocks/library/code.js +1 -0
  322. package/dist/client/chat-first/apps-rail.js +2 -2
  323. package/dist/client/chat-model-groups.d.ts +12 -0
  324. package/dist/client/chat-model-groups.js +3 -2
  325. package/dist/client/integrations/GoogleProductLogo.d.ts +5 -0
  326. package/dist/client/integrations/GoogleProductLogo.js +45 -0
  327. package/dist/client/integrations/index.d.ts +2 -0
  328. package/dist/client/integrations/index.js +2 -0
  329. package/dist/client/integrations/workspace-provider-oauth.d.ts +8 -0
  330. package/dist/client/integrations/workspace-provider-oauth.js +13 -0
  331. package/dist/client/resources/mcp-integration-catalog.js +0 -29
  332. package/dist/client/settings/SettingsTabsPage.js +10 -6
  333. package/dist/client/sharing/ShareButton.js +11 -2
  334. package/dist/client/use-chat-models.d.ts +6 -0
  335. package/dist/client/use-chat-models.js +10 -0
  336. package/dist/client/use-db-sync.js +27 -9
  337. package/dist/collab/routes.d.ts +1 -1
  338. package/dist/collab/struct-routes.d.ts +1 -1
  339. package/dist/connections/catalog.d.ts +111 -9
  340. package/dist/connections/catalog.js +86 -30
  341. package/dist/db/create-get-db.js +18 -0
  342. package/dist/db/ddl-guard.d.ts +0 -4
  343. package/dist/db/ddl-guard.js +46 -0
  344. package/dist/deploy/build.d.ts +19 -0
  345. package/dist/deploy/build.js +190 -16
  346. package/dist/deploy/function-bundle.d.ts +45 -0
  347. package/dist/deploy/function-bundle.js +337 -0
  348. package/dist/deploy/workspace-deploy.js +19 -1
  349. package/dist/eject/workspace-connections.js +4 -0
  350. package/dist/email-catalog/registry.d.ts +4 -0
  351. package/dist/email-catalog/registry.js +79 -14
  352. package/dist/extensions/content-patch.js +14 -2
  353. package/dist/integrations/plugin.d.ts +10 -0
  354. package/dist/integrations/plugin.js +40 -6
  355. package/dist/jobs/actions/run-automation-now.d.ts +2 -1
  356. package/dist/jobs/actions/run-automation-now.js +11 -5
  357. package/dist/jobs/frontmatter.js +53 -2
  358. package/dist/jobs/run-now.d.ts +11 -1
  359. package/dist/jobs/run-now.js +42 -7
  360. package/dist/jobs/scheduler.d.ts +1 -0
  361. package/dist/jobs/scheduler.js +2 -1
  362. package/dist/localization/core-messages/ar-SA.js +3 -0
  363. package/dist/localization/core-messages/de-DE.js +3 -0
  364. package/dist/localization/core-messages/en-US.d.ts +3 -0
  365. package/dist/localization/core-messages/en-US.js +3 -0
  366. package/dist/localization/core-messages/es-ES.js +3 -0
  367. package/dist/localization/core-messages/fr-FR.js +3 -0
  368. package/dist/localization/core-messages/hi-IN.js +3 -0
  369. package/dist/localization/core-messages/ja-JP.js +3 -0
  370. package/dist/localization/core-messages/ko-KR.js +3 -0
  371. package/dist/localization/core-messages/pt-BR.js +3 -0
  372. package/dist/localization/core-messages/zh-CN.js +3 -0
  373. package/dist/localization/core-messages/zh-TW.js +3 -0
  374. package/dist/localization/core-messages.d.ts +3 -0
  375. package/dist/localization/default-messages.d.ts +2 -5
  376. package/dist/localization/default-messages.js +2 -5
  377. package/dist/mcp/build-server.d.ts +9 -0
  378. package/dist/mcp/build-server.js +37 -8
  379. package/dist/mcp-client/oauth-client.d.ts +7 -0
  380. package/dist/mcp-client/oauth-client.js +12 -1
  381. package/dist/oauth-tokens/index.d.ts +1 -1
  382. package/dist/oauth-tokens/index.js +1 -1
  383. package/dist/oauth-tokens/lifecycle.d.ts +11 -0
  384. package/dist/oauth-tokens/lifecycle.js +17 -0
  385. package/dist/oauth-tokens/store.js +7 -0
  386. package/dist/observability/posthog-ai.d.ts +15 -1
  387. package/dist/observability/posthog-ai.js +24 -12
  388. package/dist/observability/traces.js +158 -28
  389. package/dist/onboarding/app-profile.js +2 -2
  390. package/dist/onboarding/plugin.js +10 -2
  391. package/dist/onboarding/types.d.ts +2 -0
  392. package/dist/provider-api/actions/custom-provider-registration.d.ts +1 -1
  393. package/dist/provider-api/actions/provider-api.d.ts +2 -2
  394. package/dist/provider-api/index.js +2 -0
  395. package/dist/resources/handlers.d.ts +2 -2
  396. package/dist/resources/store.js +13 -4
  397. package/dist/scripts/agent-engines/list-agent-engines.js +29 -3
  398. package/dist/secrets/register-framework-secrets.js +1 -1
  399. package/dist/server/action-discovery.js +5 -0
  400. package/dist/server/agent-chat/action-filters-a2a.d.ts +27 -3
  401. package/dist/server/agent-chat/action-filters-a2a.js +48 -9
  402. package/dist/server/agent-chat-plugin.js +36 -9
  403. package/dist/server/agent-discovery.js +43 -20
  404. package/dist/server/auth.js +18 -16
  405. package/dist/server/beta-opt-out-html.js +45 -4
  406. package/dist/server/better-auth-instance.d.ts +2 -1
  407. package/dist/server/better-auth-instance.js +37 -7
  408. package/dist/server/builder-design-systems.d.ts +4 -1
  409. package/dist/server/builder-design-systems.js +87 -6
  410. package/dist/server/builder-oauth.d.ts +1 -7
  411. package/dist/server/builder-oauth.js +80 -163
  412. package/dist/server/core-routes-plugin.d.ts +29 -0
  413. package/dist/server/core-routes-plugin.js +100 -20
  414. package/dist/server/default-plugins.d.ts +18 -0
  415. package/dist/server/default-plugins.js +22 -0
  416. package/dist/server/edge.d.ts +1 -0
  417. package/dist/server/edge.js +1 -0
  418. package/dist/server/framework-request-handler.js +31 -3
  419. package/dist/server/google-oauth.d.ts +3 -0
  420. package/dist/server/google-oauth.js +3 -0
  421. package/dist/server/index.d.ts +4 -4
  422. package/dist/server/index.js +3 -3
  423. package/dist/server/onboarding-html.js +37 -3
  424. package/dist/server/realtime-token.d.ts +1 -1
  425. package/dist/server/workspace-provider-oauth.d.ts +6 -1
  426. package/dist/server/workspace-provider-oauth.js +72 -20
  427. package/dist/shared/agent-sidebar-url.d.ts +1 -0
  428. package/dist/shared/agent-sidebar-url.js +1 -0
  429. package/dist/shared/environment-lanes.d.ts +2 -0
  430. package/dist/shared/environment-lanes.js +3 -1
  431. package/dist/shared/index.d.ts +3 -1
  432. package/dist/shared/index.js +3 -1
  433. package/dist/shared/surface-visibility.d.ts +31 -0
  434. package/dist/shared/surface-visibility.js +57 -0
  435. package/dist/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
  436. package/dist/templates/chat/app/routes.ts +3 -1
  437. package/dist/templates/chat/vitest.config.ts +8 -0
  438. package/dist/templates/default/.agents/skills/secrets/SKILL.md +11 -4
  439. package/dist/templates/default/app/routes.ts +3 -1
  440. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +11 -4
  441. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +11 -4
  442. package/dist/tracking/registry.d.ts +11 -0
  443. package/dist/tracking/registry.js +5 -2
  444. package/dist/triggers/actions.js +10 -3
  445. package/dist/usage/store.js +23 -3
  446. package/dist/user-profile/actions/get-auth-methods.js +18 -5
  447. package/dist/vite/client.js +33 -7
  448. package/docs/content/actions-access-control.mdx +43 -4
  449. package/docs/content/actions-defining.mdx +16 -0
  450. package/docs/content/authentication.mdx +40 -21
  451. package/docs/content/environment-variables.mdx +1 -0
  452. package/docs/content/template-clips-capture-everywhere.mdx +29 -30
  453. package/docs/content/template-clips-developers.mdx +4 -4
  454. package/docs/content/template-clips.mdx +3 -1
  455. package/package.json +2 -2
  456. package/src/eject/workspace-connections.ts +4 -0
  457. package/src/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
  458. package/src/templates/chat/app/routes.ts +3 -1
  459. package/src/templates/chat/vitest.config.ts +8 -0
  460. package/src/templates/default/.agents/skills/secrets/SKILL.md +11 -4
  461. package/src/templates/default/app/routes.ts +3 -1
  462. package/src/templates/headless/.agents/skills/secrets/SKILL.md +11 -4
  463. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +11 -4
@@ -5,13 +5,14 @@ import {
5
5
  listOAuthAccountsByOwner,
6
6
  } from "@agent-native/core/oauth-tokens";
7
7
  import {
8
- isOAuthConnected,
9
8
  getOAuthAccounts,
9
+ getCredentialContext,
10
10
  getRequestOrgId,
11
11
  resolveSecret,
12
12
  runWithRequestContext,
13
13
  resolveGoogleProviderCredentialCandidatesWithReader,
14
14
  } from "@agent-native/core/server";
15
+ import { resolveWorkspaceConnectionForApp } from "@agent-native/core/workspace-connections";
15
16
 
16
17
  import type {
17
18
  CalendarEvent,
@@ -31,11 +32,37 @@ import {
31
32
  calendarUpdateEvent,
32
33
  peopleGetProfile,
33
34
  } from "./google-api.js";
35
+ import { getCalendarProviderApiRuntime } from "./provider-api.js";
34
36
  import {
35
37
  alignSeriesRecurrenceToStart,
36
38
  shiftSeriesDateValue,
37
39
  } from "./series-recurrence.js";
38
40
 
41
+ type ManagedCalendarClient = {
42
+ email: string;
43
+ accessToken: string;
44
+ };
45
+
46
+ async function resolveManagedCalendarClient(): Promise<ManagedCalendarClient | null> {
47
+ if (!getCredentialContext()) return null;
48
+ const connection = await resolveWorkspaceConnectionForApp({
49
+ appId: "calendar",
50
+ provider: "google_calendar",
51
+ requireConnected: true,
52
+ });
53
+ if (!connection.available) return null;
54
+ const credential =
55
+ await getCalendarProviderApiRuntime().resolveOAuthAccessToken({
56
+ provider: "google_calendar",
57
+ });
58
+ if (!credential.accountId) {
59
+ throw new Error(
60
+ "The connected Google Calendar workspace account has no account id.",
61
+ );
62
+ }
63
+ return { email: credential.accountId, accessToken: credential.accessToken };
64
+ }
65
+
39
66
  const SCOPES = [
40
67
  "https://www.googleapis.com/auth/calendar.readonly",
41
68
  "https://www.googleapis.com/auth/calendar.events",
@@ -55,6 +82,16 @@ interface GoogleTokens {
55
82
  photoUrl?: string;
56
83
  }
57
84
 
85
+ const CALENDAR_SCOPE_PREFIX = "https://www.googleapis.com/auth/calendar.";
86
+
87
+ function hasCalendarScope(tokens: Record<string, unknown>): boolean {
88
+ const scope = tokens.scope;
89
+ if (typeof scope !== "string" || !scope.trim()) return true;
90
+ return scope
91
+ .split(/[\s,]+/)
92
+ .some((value) => value.startsWith(CALENDAR_SCOPE_PREFIX));
93
+ }
94
+
58
95
  function optionalString(value: unknown): string | undefined {
59
96
  return typeof value === "string" && value.length > 0 ? value : undefined;
60
97
  }
@@ -562,8 +599,10 @@ export async function getClient(
562
599
  email: string | undefined,
563
600
  ): Promise<{ accessToken: string } | null> {
564
601
  if (!email) return null;
565
- const accounts = await listOAuthAccountsByOwner("google", email);
566
- if (accounts.length === 0) return null;
602
+ const accounts = (await listOAuthAccountsByOwner("google", email)).filter(
603
+ (account) => hasCalendarScope(account.tokens),
604
+ );
605
+ if (accounts.length === 0) return resolveManagedCalendarClient();
567
606
 
568
607
  const account = accounts.find((a) => a.accountId === email) ?? accounts[0];
569
608
 
@@ -584,7 +623,9 @@ export interface GoogleAccountSelection {
584
623
  export async function getDefaultAccountSelection(
585
624
  ownerEmail: string,
586
625
  ): Promise<GoogleAccountSelection> {
587
- const accounts = await listOAuthAccountsByOwner("google", ownerEmail);
626
+ const accounts = (
627
+ await listOAuthAccountsByOwner("google", ownerEmail)
628
+ ).filter((account) => hasCalendarScope(account.tokens));
588
629
  const account =
589
630
  accounts.find(
590
631
  (candidate) =>
@@ -592,6 +633,10 @@ export async function getDefaultAccountSelection(
592
633
  ownerEmail.trim().toLowerCase(),
593
634
  ) ?? accounts[0];
594
635
  if (!account) {
636
+ const managed = await resolveManagedCalendarClient();
637
+ if (managed) {
638
+ return { ownerEmail, accountEmail: managed.email };
639
+ }
595
640
  throw new Error(
596
641
  "Google Calendar not connected. Connect via Settings first.",
597
642
  );
@@ -605,12 +650,21 @@ export async function getClientForAccount({
605
650
  accountEmail,
606
651
  }: GoogleAccountSelection): Promise<{ accessToken: string }> {
607
652
  const normalizedAccountEmail = accountEmail.trim().toLowerCase();
608
- const accounts = await listOAuthAccountsByOwner("google", ownerEmail);
653
+ const accounts = (
654
+ await listOAuthAccountsByOwner("google", ownerEmail)
655
+ ).filter((account) => hasCalendarScope(account.tokens));
609
656
  const account = accounts.find(
610
657
  (candidate) =>
611
658
  candidate.accountId.trim().toLowerCase() === normalizedAccountEmail,
612
659
  );
613
660
  if (!account) {
661
+ const managed = await resolveManagedCalendarClient();
662
+ if (
663
+ managed &&
664
+ managed.email.trim().toLowerCase() === normalizedAccountEmail
665
+ ) {
666
+ return { accessToken: managed.accessToken };
667
+ }
614
668
  throw new Error(
615
669
  `Google Calendar account not connected for this user: ${accountEmail}`,
616
670
  );
@@ -654,7 +708,9 @@ export async function getClientsWithErrors(forEmail?: string): Promise<{
654
708
  errors: Array<{ email: string; error: string }>;
655
709
  }> {
656
710
  if (!forEmail) return { clients: [], errors: [] };
657
- const accounts = await listOAuthAccountsByOwner("google", forEmail);
711
+ const accounts = (await listOAuthAccountsByOwner("google", forEmail)).filter(
712
+ (account) => hasCalendarScope(account.tokens),
713
+ );
658
714
 
659
715
  const clients: Array<{ email: string; accessToken: string }> = [];
660
716
  const errors: Array<{ email: string; error: string }> = [];
@@ -682,6 +738,18 @@ export async function getClientsWithErrors(forEmail?: string): Promise<{
682
738
  }
683
739
  }
684
740
 
741
+ if (clients.length === 0) {
742
+ try {
743
+ const managed = await resolveManagedCalendarClient();
744
+ if (managed) clients.push(managed);
745
+ } catch (err: any) {
746
+ errors.push({
747
+ email: "workspace",
748
+ error: err?.message || "Workspace Google Calendar connection failed",
749
+ });
750
+ }
751
+ }
752
+
685
753
  return { clients, errors };
686
754
  }
687
755
 
@@ -695,7 +763,9 @@ export async function getOwnedAccountEmails(
695
763
  forEmail?: string,
696
764
  ): Promise<string[]> {
697
765
  if (!forEmail) return [];
698
- const accounts = await listOAuthAccountsByOwner("google", forEmail);
766
+ const accounts = (await listOAuthAccountsByOwner("google", forEmail)).filter(
767
+ (account) => hasCalendarScope(account.tokens),
768
+ );
699
769
  return accounts.map((account) => account.accountId);
700
770
  }
701
771
 
@@ -716,7 +786,47 @@ export async function getClientsForAccountsWithErrors(
716
786
  resolvedAccounts: [],
717
787
  };
718
788
  }
719
- const accounts = await listOAuthAccountsByOwner("google", forEmail);
789
+ const accounts = (await listOAuthAccountsByOwner("google", forEmail)).filter(
790
+ (account) => hasCalendarScope(account.tokens),
791
+ );
792
+ if (accounts.length === 0) {
793
+ const managed = await resolveManagedCalendarClient();
794
+ if (!managed) {
795
+ if (accountEmails?.length) {
796
+ throw new Error(
797
+ `Google Calendar account not connected for this user: ${accountEmails.join(", ")}`,
798
+ );
799
+ }
800
+ return {
801
+ clients: [],
802
+ errors: [],
803
+ requestedAccounts: [],
804
+ resolvedAccounts: [],
805
+ };
806
+ }
807
+ const requestedAccounts = Array.from(
808
+ new Set(
809
+ (accountEmails ?? [managed.email])
810
+ .map((email) => email.trim().toLowerCase())
811
+ .filter(Boolean),
812
+ ),
813
+ );
814
+ if (
815
+ requestedAccounts.some(
816
+ (email) => email !== managed.email.trim().toLowerCase(),
817
+ )
818
+ ) {
819
+ throw new Error(
820
+ `Google Calendar account not connected for this user: ${requestedAccounts.join(", ")}`,
821
+ );
822
+ }
823
+ return {
824
+ clients: [managed],
825
+ errors: [],
826
+ requestedAccounts,
827
+ resolvedAccounts: [managed.email],
828
+ };
829
+ }
720
830
  const byNormalized = new Map(
721
831
  accounts.map((account) => [
722
832
  account.accountId.trim().toLowerCase(),
@@ -765,15 +875,22 @@ export async function getClientsForAccountsWithErrors(
765
875
  }
766
876
 
767
877
  export async function isConnected(forEmail?: string): Promise<boolean> {
768
- return isOAuthConnected("google", forEmail ?? "");
878
+ if (!forEmail) return false;
879
+ const accounts = await listOAuthAccountsByOwner("google", forEmail);
880
+ if (accounts.some((account) => hasCalendarScope(account.tokens))) return true;
881
+ return Boolean(await resolveManagedCalendarClient());
769
882
  }
770
883
 
771
884
  export async function getConnectedAccounts(
772
885
  forEmail?: string,
773
886
  ): Promise<string[]> {
774
887
  if (!forEmail) return [];
775
- const accounts = await listOAuthAccountsByOwner("google", forEmail);
776
- return accounts.map((a) => a.accountId);
888
+ const accounts = (await listOAuthAccountsByOwner("google", forEmail)).filter(
889
+ (account) => hasCalendarScope(account.tokens),
890
+ );
891
+ if (accounts.length > 0) return accounts.map((a) => a.accountId);
892
+ const managed = await resolveManagedCalendarClient();
893
+ return managed ? [managed.email] : [];
777
894
  }
778
895
 
779
896
  export async function getPrimaryAccountPhotoUrl(
@@ -781,7 +898,9 @@ export async function getPrimaryAccountPhotoUrl(
781
898
  ): Promise<string | undefined> {
782
899
  if (!forEmail) return undefined;
783
900
  const fallbackUserImage = await getBetterAuthUserImage(forEmail);
784
- const accounts = await listOAuthAccountsByOwner("google", forEmail);
901
+ const accounts = (await listOAuthAccountsByOwner("google", forEmail)).filter(
902
+ (account) => hasCalendarScope(account.tokens),
903
+ );
785
904
  const account = accounts.find((a) => a.accountId === forEmail) ?? accounts[0];
786
905
  if (!account) return fallbackUserImage;
787
906
 
@@ -805,16 +924,22 @@ export async function getAuthStatus(
805
924
  forEmail?: string,
806
925
  orgId?: string,
807
926
  ): Promise<GoogleAuthStatus> {
808
- const oauthAccounts = await getOAuthAccounts("google", forEmail);
927
+ const oauthAccounts = (await getOAuthAccounts("google", forEmail)).filter(
928
+ (account) => hasCalendarScope(account.tokens),
929
+ );
809
930
 
810
931
  if (oauthAccounts.length === 0) {
811
- return { connected: false, accounts: [] };
932
+ const managed = await resolveManagedCalendarClient();
933
+ return managed
934
+ ? { connected: true, accounts: [{ email: managed.email, shared: true }] }
935
+ : { connected: false, accounts: [] };
812
936
  }
813
937
 
814
938
  const result: Array<{
815
939
  email: string;
816
940
  expiresAt?: string;
817
941
  photoUrl?: string;
942
+ shared?: boolean;
818
943
  }> = [];
819
944
  for (const account of oauthAccounts) {
820
945
  const tokens = account.tokens as unknown as GoogleTokens;
@@ -0,0 +1,85 @@
1
+ import { listOAuthAccountsByOwner } from "@agent-native/core/oauth-tokens";
2
+ import { registerOnboardingStep } from "@agent-native/core/onboarding";
3
+ import { hasWorkspaceProviderOAuthCredentials } from "@agent-native/core/server";
4
+ import { resolveWorkspaceConnectionForApp } from "@agent-native/core/workspace-connections";
5
+
6
+ const CALENDAR_SCOPES = [
7
+ "https://www.googleapis.com/auth/calendar.readonly",
8
+ "https://www.googleapis.com/auth/calendar.events",
9
+ ] as const;
10
+
11
+ function hasRequiredScope(tokens: unknown): boolean {
12
+ if (!tokens || typeof tokens !== "object" || Array.isArray(tokens)) {
13
+ return false;
14
+ }
15
+ const scope = (tokens as { scope?: unknown }).scope;
16
+ if (typeof scope !== "string" || !scope.trim()) return true;
17
+ const granted = new Set(scope.split(/[\s,]+/).filter(Boolean));
18
+ return CALENDAR_SCOPES.some((requiredScope) => granted.has(requiredScope));
19
+ }
20
+
21
+ registerOnboardingStep({
22
+ id: "google-calendar",
23
+ order: 100,
24
+ required: false,
25
+ title: "Connect Google Calendar",
26
+ description: "Manage events and availability with your Google account.",
27
+ isAvailable: () => hasWorkspaceProviderOAuthCredentials("google_calendar"),
28
+ methods: [
29
+ {
30
+ id: "oauth",
31
+ kind: "link",
32
+ primary: true,
33
+ label: "Connect Google Calendar for me",
34
+ description:
35
+ "One-click Google sign-in using the workspace's managed OAuth connection. Only you can use this connection.",
36
+ payload: {
37
+ url: "/_agent-native/connections/oauth/google_calendar/start?scope=user&appId=calendar&return=/settings",
38
+ external: false,
39
+ },
40
+ },
41
+ {
42
+ id: "oauth-workspace",
43
+ kind: "link",
44
+ label: "Connect Google Calendar for my workspace",
45
+ description:
46
+ "Workspace admins can connect once and make this Calendar connection available to selected apps or everyone in the workspace.",
47
+ payload: {
48
+ url: "/_agent-native/connections/oauth/google_calendar/start?scope=organization&appId=calendar&return=/settings",
49
+ external: false,
50
+ },
51
+ },
52
+ {
53
+ id: "agent-task",
54
+ kind: "agent-task",
55
+ badge: "beta",
56
+ label: "Have the agent connect it",
57
+ payload: {
58
+ prompt:
59
+ "Help me connect Google Calendar through the managed Google OAuth connection. Start the one-click Calendar connection flow, explain the Google permission screen, and confirm whether I want the connection available only to me or to the workspace. Do not ask me to create Google Cloud credentials or paste keys.",
60
+ },
61
+ },
62
+ ],
63
+ isComplete: async (context) => {
64
+ if (!context?.userEmail) return false;
65
+ try {
66
+ const accounts = await listOAuthAccountsByOwner(
67
+ "google",
68
+ context.userEmail,
69
+ );
70
+ if (accounts.some((account) => hasRequiredScope(account.tokens))) {
71
+ return true;
72
+ }
73
+ return (
74
+ await resolveWorkspaceConnectionForApp({
75
+ appId: "calendar",
76
+ provider: "google_calendar",
77
+ requireConnected: true,
78
+ })
79
+ ).available;
80
+ } catch {
81
+ // coercion-ok: a failed lookup must leave onboarding incomplete, never completed.
82
+ return false;
83
+ }
84
+ },
85
+ });
@@ -1,5 +1,7 @@
1
1
  import { registerEvent } from "@agent-native/core/event-bus";
2
2
  import { getOrgContext } from "@agent-native/core/org";
3
+
4
+ import "../onboarding.js";
3
5
  import {
4
6
  createAgentChatPlugin,
5
7
  loadActionsFromStaticRegistry,
@@ -310,13 +310,21 @@ export interface BookingLink {
310
310
  isActive: boolean;
311
311
  /** Sharing visibility: private (default), org, or public */
312
312
  visibility?: "private" | "org" | "public";
313
+ /** Effective management role for the current caller, when loaded from a list. */
314
+ accessRole?: "owner" | "admin" | "editor" | "commenter" | "viewer";
313
315
  createdAt: string;
314
316
  updatedAt: string;
315
317
  }
316
318
 
317
319
  export interface GoogleAuthStatus {
320
+ configured?: boolean;
318
321
  connected: boolean;
319
- accounts: Array<{ email: string; expiresAt?: string; photoUrl?: string }>;
322
+ accounts: Array<{
323
+ email: string;
324
+ expiresAt?: string;
325
+ photoUrl?: string;
326
+ shared?: boolean;
327
+ }>;
320
328
  }
321
329
 
322
330
  export interface ExternalCalendar {
@@ -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
 
@@ -48,7 +48,7 @@ function safeParseArray<T>(
48
48
 
49
49
  export default defineAction({
50
50
  description:
51
- "Get a meeting by id with its participants, action items, and a reference to its recording (if any). Returns null if the user lacks access.",
51
+ 'Get a meeting by id with its participants, action items, and a reference to its recording (if any). When no meeting is returned, `meeting` is null and `reason` is "unavailable" — it does not distinguish missing from inaccessible. A read failure throws instead.',
52
52
  schema: z.object({
53
53
  id: z.string().describe("Meeting id"),
54
54
  }),
@@ -59,12 +59,17 @@ export default defineAction({
59
59
  const materialized = await materializeCalendarMeetingFromVirtualId(
60
60
  args.id,
61
61
  );
62
- if (!materialized?.meeting?.id) return { meeting: null };
62
+ if (!materialized?.meeting?.id)
63
+ return { meeting: null, reason: "unavailable" as const };
63
64
  meetingId = materialized.meeting.id;
64
65
  }
65
66
 
67
+ // "cannot see it" and "does not exist" deliberately share one reason: a
68
+ // caller that could tell them apart could probe ids to learn which
69
+ // meetings exist. The distinction callers actually need is this vs a
70
+ // thrown read failure, which stays separate.
66
71
  const access = await resolveAccess("meeting", meetingId);
67
- if (!access) return { meeting: null };
72
+ if (!access) return { meeting: null, reason: "unavailable" as const };
68
73
 
69
74
  const db = getDb();
70
75
  const [row] = await db
@@ -77,7 +82,7 @@ export default defineAction({
77
82
  ),
78
83
  )
79
84
  .limit(1);
80
- if (!row) return { meeting: null };
85
+ if (!row) return { meeting: null, reason: "unavailable" as const };
81
86
 
82
87
  // Server-side JSON parse — clients see structured arrays, not raw TEXT.
83
88
  const bullets = safeParseArray<Bullet>(
@@ -188,9 +188,19 @@ export default defineAction({
188
188
  // Meeting recordings are transcript-only (no playable media) and live on
189
189
  // the /meetings surface, so keep them out of clip library views. The link
190
190
  // is meetings.recordingId (no meetingId column on recordings), so exclude
191
- // any recording referenced by a meeting. The subquery filters out NULLs
192
- // so NOT IN doesn't collapse to an empty result under SQL NULL semantics.
193
- const meetingRecordingIds = db
191
+ // any recording referenced by a meeting. Keep the exclusion database-side
192
+ // as a NOT IN (SELECT ...) subquery instead of pulling every meeting's
193
+ // recording id into memory — this table only grows. `await db` with no
194
+ // chain resolves the lazy proxy from create-get-db.ts to the real db
195
+ // instance without issuing a query; only *then* build the subquery off
196
+ // that resolved instance. Building it off `db` directly and handing the
197
+ // still-unresolved chain straight to notInArray() is the cold-start bug
198
+ // this used to have — drizzle reads `.getSQL()` on it synchronously, and
199
+ // the proxy throws rather than silently building wrong SQL. The subquery
200
+ // filters out NULLs so NOT IN doesn't collapse to an empty result under
201
+ // SQL NULL semantics.
202
+ const resolvedDb = await db;
203
+ const meetingRecordingIds = resolvedDb
194
204
  .select({ id: schema.meetings.recordingId })
195
205
  .from(schema.meetings)
196
206
  .where(isNotNull(schema.meetings.recordingId));
@@ -200,6 +210,9 @@ export default defineAction({
200
210
  // Lifecycle view filters
201
211
  if (args.view === "trash") {
202
212
  whereClauses.push(isNotNull(schema.recordings.trashedAt));
213
+ if (orgId) {
214
+ whereClauses.push(eq(schema.recordings.organizationId, orgId));
215
+ }
203
216
  } else {
204
217
  whereClauses.push(isNull(schema.recordings.trashedAt));
205
218
  if (args.view === "archive") {
@@ -38,6 +38,7 @@ const MEETING_COLUMNS = {
38
38
  source: schema.meetings.source,
39
39
  platform: schema.meetings.platform,
40
40
  trashedAt: schema.meetings.trashedAt,
41
+ ownerEmail: schema.meetings.ownerEmail,
41
42
  } as const;
42
43
 
43
44
  type MeetingRow = Pick<
@@ -210,7 +210,14 @@ async function fetchLibrary({
210
210
  conditions.push(eq(schema.recordings.organizationId, organizationId));
211
211
  }
212
212
  }
213
- const meetingRecordingIds = db
213
+ // Meeting recordings are transcript-only and live on /meetings, so keep them
214
+ // out of clip library views. `await db` with no chain resolves the lazy proxy
215
+ // from create-get-db.ts to the real instance without issuing a query; the
216
+ // subquery must be built off *that*. Building it off `db` hands an unresolved
217
+ // chain to notInArray(), which reads `.getSQL()` synchronously on a cold-start
218
+ // proxy. The subquery filters NULLs so NOT IN doesn't collapse to empty.
219
+ const resolvedDb = await db;
220
+ const meetingRecordingIds = resolvedDb
214
221
  .select({ id: schema.meetings.recordingId })
215
222
  .from(schema.meetings)
216
223
  .where(isNotNull(schema.meetings.recordingId));
@@ -26,6 +26,7 @@ export interface MeetingHistoryItem {
26
26
  actualEnd?: string | null;
27
27
  createdAt?: string | null;
28
28
  participants?: AttendeeStackParticipant[];
29
+ ownerEmail?: string | null;
29
30
  }
30
31
 
31
32
  function formatTime(iso?: string | null): string {
@@ -56,6 +57,28 @@ export function formatParticipantNames(
56
57
  return `${names.slice(0, 2).join(", ")} & ${names.length - 2} others`;
57
58
  }
58
59
 
60
+ /**
61
+ * A meeting's owner — who actually recorded it in Clips — isn't necessarily
62
+ * on the attendee list (an ad-hoc note has none at all), and two attendees on
63
+ * the same call can each hold their own copy. Unlike the attendee subtitle,
64
+ * this is shown unconditionally, including the viewer's own meetings, so
65
+ * ownership is never ambiguous once a meeting is shared.
66
+ */
67
+ export function formatOwnerHint(
68
+ ownerEmail: string | null | undefined,
69
+ viewerEmail: string | null | undefined,
70
+ t: ReturnType<typeof useT>,
71
+ ): string {
72
+ const owner = ownerEmail?.trim();
73
+ if (!owner) return "";
74
+ const viewer = viewerEmail?.trim().toLowerCase();
75
+ const name =
76
+ viewer && owner.toLowerCase() === viewer
77
+ ? t("meetingDetail.me")
78
+ : owner.replace(/@.*$/, "");
79
+ return t("meetingDetail.recordedBy", { name });
80
+ }
81
+
59
82
  export function MeetingHistoryRow({
60
83
  meeting,
61
84
  snippet,
@@ -66,11 +89,17 @@ export function MeetingHistoryRow({
66
89
  const t = useT();
67
90
  const { session } = useSession();
68
91
  const participants = meeting.participants ?? [];
69
- const subtitle =
92
+ const ownerHint = formatOwnerHint(meeting.ownerEmail, session?.email, t);
93
+ const primaryText =
70
94
  snippet?.trim() || formatParticipantNames(participants, session?.email);
95
+ const subtitle = [primaryText, ownerHint].filter(Boolean).join(" · ");
71
96
  const time = formatTime(
72
97
  meeting.actualStart ?? meeting.scheduledStart ?? meeting.createdAt,
73
98
  );
99
+ const soloOwnerAvatar: AttendeeStackParticipant[] =
100
+ participants.length === 0 && ownerHint && meeting.ownerEmail
101
+ ? [{ email: meeting.ownerEmail }]
102
+ : [];
74
103
 
75
104
  return (
76
105
  <NavLink
@@ -79,6 +108,8 @@ export function MeetingHistoryRow({
79
108
  >
80
109
  {participants.length > 0 ? (
81
110
  <AttendeeStack participants={participants} size="md" max={2} />
111
+ ) : soloOwnerAvatar.length > 0 ? (
112
+ <AttendeeStack participants={soloOwnerAvatar} size="md" max={1} />
82
113
  ) : (
83
114
  <span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground">
84
115
  <IconFileText className="h-3.5 w-3.5" />
@@ -94,7 +94,7 @@ export function RecordingOptionsMenu({
94
94
  <Button
95
95
  variant="ghost"
96
96
  size="icon"
97
- className="-mx-1.5 h-auto w-auto shrink-0 px-0.5 py-1.5"
97
+ className="h-auto w-auto shrink-0 px-0.5 py-1.5"
98
98
  aria-label={t("deleteRecordingMenu.clipOptions")}
99
99
  >
100
100
  <IconDotsVertical className="h-4 w-4" />