@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
@@ -5054,7 +5054,13 @@ export default function SlideEditor({
5054
5054
  (patch: SlideStylePatch) => {
5055
5055
  const editing = editingElRef.current;
5056
5056
  const element = editing ?? resolveSelectedElement();
5057
- if (!element || !selectedElementSelector) return;
5057
+ // Inline edits entered without selectElementForStyling (the text-box
5058
+ // tool) leave selectedElementSelector null, so fall back the same way the
5059
+ // snapshot readers do rather than dropping the write.
5060
+ const selector =
5061
+ selectedElementSelector ??
5062
+ (editing ? getBuilderSelector(editing) : null);
5063
+ if (!element || !selector) return;
5058
5064
 
5059
5065
  const inlinePatch = inlineInspectorStylePatch(patch);
5060
5066
  const inlineKeys = INLINE_INSPECTOR_STYLE_KEYS.filter(
@@ -5117,11 +5123,7 @@ export default function SlideEditor({
5117
5123
  richTextSelectionRef.current,
5118
5124
  )
5119
5125
  : undefined;
5120
- const snapshot = buildStyleSnapshot(
5121
- element,
5122
- selectedElementSelector,
5123
- inlineTextStyle,
5124
- );
5126
+ const snapshot = buildStyleSnapshot(element, selector, inlineTextStyle);
5125
5127
  if (!editing) {
5126
5128
  invalidateSelectionOverlayMeasurement();
5127
5129
  }
@@ -5129,7 +5131,7 @@ export default function SlideEditor({
5129
5131
  if (!editing) {
5130
5132
  syncSelectionToAppState(
5131
5133
  buildSelectionState(getSlideSelectionMode(snapshot), [
5132
- selectionItemForElement(element, selectedElementSelector, snapshot),
5134
+ selectionItemForElement(element, selector, snapshot),
5133
5135
  ]),
5134
5136
  );
5135
5137
  }
@@ -5211,15 +5213,25 @@ export default function SlideEditor({
5211
5213
  converted.focus({ preventScroll: true });
5212
5214
  }
5213
5215
 
5216
+ // Committing while an edit is open re-renders the slide from `content`
5217
+ // and replaces the contentEditable node; stash a draft instead and let
5218
+ // exitInlineEdit flush it.
5219
+ if (editing) {
5220
+ captureInlineEditDraft(slide.id);
5221
+ return;
5222
+ }
5223
+
5214
5224
  const html = readCurrentSlideContentHtml();
5215
5225
  if (html !== null) onUpdateSlideRef.current({ content: html });
5216
5226
  const selector = getBuilderSelector(converted);
5217
5227
  if (selector) selectElementForStyling(converted, selector);
5218
5228
  },
5219
5229
  [
5230
+ captureInlineEditDraft,
5220
5231
  readCurrentSlideContentHtml,
5221
5232
  resolveSelectedElement,
5222
5233
  selectElementForStyling,
5234
+ slide.id,
5223
5235
  ],
5224
5236
  );
5225
5237
 
@@ -300,42 +300,55 @@ function cleanNode(
300
300
  }
301
301
 
302
302
  function sanitizeHtmlString(html: string, scopeSelector?: string): string {
303
- return html
304
- .replace(/<style\b[^>]*>([\s\S]*?)<\/style>/gi, (_match, css) => {
305
- const safeCss = sanitizeStyleSheet(String(css), scopeSelector);
306
- return safeCss
307
- ? `<style>${safeCss.replace(/<\/style/gi, "<\\/style")}</style>`
308
- : "";
309
- })
310
- .replace(
311
- /<(script|iframe|object|embed|form|input|button|select|textarea|meta|base|link|svg|math)\b[\s\S]*?<\/\1>/gi,
312
- "",
313
- )
314
- .replace(
315
- /<(script|iframe|object|embed|form|input|button|select|textarea|meta|base|link|svg|math)\b[^>]*\/?>/gi,
316
- "",
317
- )
318
- .replace(/\s+on[a-z][\w:-]*\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "")
319
- .replace(/\s+srcdoc\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "")
320
- .replace(/\s+srcset\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "")
321
- .replace(
322
- /\s+(href|src|xlink:href)\s*=\s*("([^"]*)"|'([^']*)'|([^\s>]+))/gi,
323
- (match, attr, _raw, dq, sq, bare) => {
324
- const value = dq ?? sq ?? bare ?? "";
325
- const safe = sanitizeSlideUrl(
326
- value,
327
- String(attr).toLowerCase() === "src" ? "image" : "link",
328
- );
329
- return safe ? ` ${attr}="${escapeHtml(safe)}"` : "";
330
- },
331
- )
332
- .replace(
333
- /\s+style\s*=\s*("([^"]*)"|'([^']*)'|([^\s>]+))/gi,
334
- (_match, _raw, dq, sq, bare) => {
335
- const safe = sanitizeStyle(dq ?? sq ?? bare ?? "");
336
- return safe ? ` style="${escapeHtml(safe)}"` : "";
337
- },
338
- );
303
+ return (
304
+ html
305
+ .replace(/<style\b[^>]*>([\s\S]*?)<\/\s*style\s*>/gi, (_match, css) => {
306
+ const safeCss = sanitizeStyleSheet(String(css), scopeSelector);
307
+ return safeCss
308
+ ? `<style>${safeCss.replace(/<\/style/gi, "<\\/style")}</style>`
309
+ : "";
310
+ })
311
+ .replace(
312
+ /<(script|iframe|object|embed|form|input|button|select|textarea|meta|base|link|svg|math)\b[\s\S]*?<\/\s*\1\s*>/gi,
313
+ "",
314
+ )
315
+ // Anything left here is a blocked element that never closed. The opening-tag
316
+ // pass below would strip only its tag and leave the body behind as slide
317
+ // text — which is how a script's JavaScript renders as visible copy on the
318
+ // SSR'd share/present pages, where DOMParser is undefined and this regex
319
+ // twin runs instead of cleanNode(). An unclosed raw-text or embedding
320
+ // element swallows the rest of the document in a real parser, so dropping
321
+ // the remainder is what keeps this path agreeing with the DOM path.
322
+ .replace(
323
+ /<(script|style|textarea|iframe|object|embed|svg|math)\b[\s\S]*$/i,
324
+ "",
325
+ )
326
+ .replace(
327
+ /<(script|iframe|object|embed|form|input|button|select|textarea|meta|base|link|svg|math)\b[^>]*\/?>/gi,
328
+ "",
329
+ )
330
+ .replace(/\s+on[a-z][\w:-]*\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "")
331
+ .replace(/\s+srcdoc\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "")
332
+ .replace(/\s+srcset\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "")
333
+ .replace(
334
+ /\s+(href|src|xlink:href)\s*=\s*("([^"]*)"|'([^']*)'|([^\s>]+))/gi,
335
+ (match, attr, _raw, dq, sq, bare) => {
336
+ const value = dq ?? sq ?? bare ?? "";
337
+ const safe = sanitizeSlideUrl(
338
+ value,
339
+ String(attr).toLowerCase() === "src" ? "image" : "link",
340
+ );
341
+ return safe ? ` ${attr}="${escapeHtml(safe)}"` : "";
342
+ },
343
+ )
344
+ .replace(
345
+ /\s+style\s*=\s*("([^"]*)"|'([^']*)'|([^\s>]+))/gi,
346
+ (_match, _raw, dq, sq, bare) => {
347
+ const safe = sanitizeStyle(dq ?? sq ?? bare ?? "");
348
+ return safe ? ` style="${escapeHtml(safe)}"` : "";
349
+ },
350
+ )
351
+ );
339
352
  }
340
353
 
341
354
  export function sanitizeSlideHtml(
@@ -42,16 +42,23 @@ export default function ApproveDeckAccessRequestRoute() {
42
42
  const deckId = searchParams.get("deckId") ?? "";
43
43
  const approvalTokenFromUrl = searchParams.get("token") ?? "";
44
44
  const approvalTokenStorageKey = deckAccessApprovalSessionKey(deckId);
45
- const [approvalToken, setApprovalToken] = useState(() => {
46
- if (approvalTokenFromUrl) return approvalTokenFromUrl;
47
- if (typeof window === "undefined" || !deckId) return "";
45
+ // Only the URL token is knowable on the server, so reading storage in the
46
+ // initializer makes the first client render disagree with the server's and
47
+ // React re-renders the page from scratch. Adopt the stored token after mount.
48
+ const [approvalToken, setApprovalToken] = useState(
49
+ () => approvalTokenFromUrl ?? "",
50
+ );
51
+
52
+ useEffect(() => {
53
+ if (approvalTokenFromUrl || !deckId) return;
48
54
  try {
49
- return sessionStorage.getItem(approvalTokenStorageKey) ?? "";
50
- } catch {
51
- // coercion-ok: unavailable tab storage is an absent continuation.
52
- return "";
53
- }
54
- });
55
+ const stored = sessionStorage.getItem(approvalTokenStorageKey);
56
+ if (stored) setApprovalToken(stored);
57
+ // Unavailable tab storage is an absent continuation: the page then asks
58
+ // for the token rather than silently continuing without one.
59
+ // coercion-ok: the absent case is visible to the user, not swallowed.
60
+ } catch {}
61
+ }, [approvalTokenFromUrl, deckId, approvalTokenStorageKey]);
55
62
  const [state, setState] = useState<ApprovalState>({ kind: "loading" });
56
63
 
57
64
  useEffect(() => {
@@ -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;
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-22
4
+ ---
5
+
6
+ An image attached to chat that was already uploaded elsewhere is no longer silently dropped — the agent now sees it and can add it to the deck.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-22
4
+ ---
5
+
6
+ Google Drive and Slides can now connect with the shared Google OAuth app in one click
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-22
4
+ ---
5
+
6
+ Fixing several slides at once now tells you when a slide still doesn't fit instead of always reporting success.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-22
4
+ ---
5
+
6
+ Editing a slide with several batched changes now reports which specific change was skipped instead of only one overall success flag.
@@ -2,6 +2,7 @@ import {
2
2
  decodeOAuthState,
3
3
  encodeOAuthState,
4
4
  getAppUrl,
5
+ getCredentialContext,
5
6
  getSession,
6
7
  isElectron,
7
8
  oauthCallbackResponse,
@@ -9,6 +10,7 @@ import {
9
10
  resolveOAuthRedirectUri,
10
11
  safeReturnPath,
11
12
  } from "@agent-native/core/server";
13
+ import { resolveWorkspaceConnectionForApp } from "@agent-native/core/workspace-connections";
12
14
  import {
13
15
  defineEventHandler,
14
16
  getQuery,
@@ -27,8 +29,45 @@ import {
27
29
  isGoogleDocsOAuthConfigured,
28
30
  listGoogleDocsAccounts,
29
31
  } from "../lib/google-docs-oauth.js";
32
+ import { getSlidesProviderApiRuntime } from "../lib/provider-api.js";
33
+ import { withSlidesRequestContext } from "./request-auth-context.js";
30
34
  const OAUTH_STATE_APP_ID = process.env.APP_NAME || "slides";
31
35
 
36
+ async function resolveManagedGoogleDriveAccount() {
37
+ if (!getCredentialContext()) return null;
38
+ const connection = await resolveWorkspaceConnectionForApp({
39
+ appId: "slides",
40
+ provider: "google_drive",
41
+ requireConnected: true,
42
+ });
43
+ if (!connection.available) return null;
44
+ const credential =
45
+ await getSlidesProviderApiRuntime().resolveOAuthAccessToken({
46
+ provider: "google_drive",
47
+ });
48
+ if (!credential.accountId) return null;
49
+ return {
50
+ email: credential.accountId,
51
+ accessToken: credential.accessToken,
52
+ scope: "https://www.googleapis.com/auth/drive.readonly",
53
+ shared: true,
54
+ };
55
+ }
56
+
57
+ async function getAvailableGoogleDocsAccessToken(
58
+ event: H3Event,
59
+ owner: string,
60
+ ) {
61
+ return withSlidesRequestContext(event, async () => {
62
+ const local = await getGoogleDocsAccessToken(owner);
63
+ if (local) return local;
64
+ const managed = await resolveManagedGoogleDriveAccount();
65
+ return managed
66
+ ? { accessToken: managed.accessToken, accountEmail: managed.email }
67
+ : null;
68
+ });
69
+ }
70
+
32
71
  async function requireSessionEmail(event: H3Event): Promise<string | null> {
33
72
  const session = await getSession(event);
34
73
  if (!session?.email) {
@@ -160,6 +199,18 @@ export const getGoogleDocsStatus = defineEventHandler(
160
199
 
161
200
  try {
162
201
  const accounts = await listGoogleDocsAccounts(owner);
202
+ if (accounts.length === 0) {
203
+ const managed = await withSlidesRequestContext(event, () =>
204
+ resolveManagedGoogleDriveAccount(),
205
+ );
206
+ if (managed) {
207
+ accounts.push({
208
+ email: managed.email,
209
+ scope: managed.scope,
210
+ shared: true,
211
+ });
212
+ }
213
+ }
163
214
  const picker = await getGooglePickerConfig(owner);
164
215
  return {
165
216
  configured: await isGoogleDocsOAuthConfigured(owner),
@@ -206,7 +257,7 @@ export const getGoogleDocsPickerToken = defineEventHandler(
206
257
  }
207
258
 
208
259
  try {
209
- const token = await getGoogleDocsAccessToken(owner);
260
+ const token = await getAvailableGoogleDocsAccessToken(event, owner);
210
261
  if (!token) {
211
262
  setResponseStatus(event, 401);
212
263
  return {
@@ -47,7 +47,10 @@ export async function prepareSlidesChatAttachments(args: {
47
47
  path: string;
48
48
  url?: string;
49
49
  type: string;
50
- size: number;
50
+ // Unset (not 0) for an attachment we never downloaded — an already-hosted
51
+ // URL-only attachment has no known byte size, and "0" would misreport it
52
+ // as an empty file instead of an unmeasured one.
53
+ size?: number;
51
54
  }> = [];
52
55
  const failed: Array<{ name: string; reason: string }> = [];
53
56
  const nextAttachments = [...args.attachments];
@@ -55,10 +58,33 @@ export async function prepareSlidesChatAttachments(args: {
55
58
  for (let index = 0; index < args.attachments.length; index++) {
56
59
  const attachment = args.attachments[index];
57
60
  if (!attachment) continue;
61
+ const ext = path.extname(attachment.name).toLowerCase();
62
+
63
+ // An attachment can arrive already durably hosted — a plain `url` with no
64
+ // inline `data` (e.g. `referenceImagePaths`/image content parts wrap an
65
+ // uploaded file as `{ type: "image", url }`, per
66
+ // packages/core/src/client/agent-chat-adapter.ts). There are no bytes to
67
+ // save, but the file IS attached; skipping it here because only `data`
68
+ // was ever recognized as "attached" is what silently drops it and leaves
69
+ // the agent with no signal it exists.
70
+ if (
71
+ typeof attachment.data !== "string" &&
72
+ typeof attachment.url === "string"
73
+ ) {
74
+ if (isSlidesReferenceFileExtension(ext)) {
75
+ uploaded.push({
76
+ originalName: attachment.name,
77
+ path: attachment.url,
78
+ url: attachment.url,
79
+ type: attachment.contentType || "application/octet-stream",
80
+ });
81
+ }
82
+ continue;
83
+ }
84
+
58
85
  const dataUrl = attachmentDataUrl(attachment);
59
86
  if (!dataUrl) continue;
60
87
 
61
- const ext = path.extname(attachment.name).toLowerCase();
62
88
  if (!isSlidesReferenceFileExtension(ext)) continue;
63
89
 
64
90
  const decoded = decodeDataUrl(dataUrl);
@@ -93,7 +119,7 @@ export async function prepareSlidesChatAttachments(args: {
93
119
  const fileList = uploaded
94
120
  .map(
95
121
  (file) =>
96
- `- ${file.originalName} (${file.type}, ${(file.size / 1024).toFixed(1)}KB) at path: ${file.path}${file.url ? `; embeddable URL: ${file.url}` : ""}`,
122
+ `- ${file.originalName} (${file.type}${typeof file.size === "number" ? `, ${(file.size / 1024).toFixed(1)}KB` : ""}) at path: ${file.path}${file.url ? `; embeddable URL: ${file.url}` : ""}`,
97
123
  )
98
124
  .join("\n");
99
125
  const failureList = failed
@@ -14,7 +14,8 @@ const TOKEN_URL = "https://oauth2.googleapis.com/token";
14
14
  const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
15
15
  const USERINFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo";
16
16
 
17
- export const GOOGLE_DOCS_PROVIDER = "google-docs";
17
+ export const GOOGLE_DOCS_PROVIDER = "google";
18
+ const LEGACY_GOOGLE_DOCS_PROVIDER = "google-docs";
18
19
  export const GOOGLE_DRIVE_READONLY_SCOPE =
19
20
  "https://www.googleapis.com/auth/drive.readonly";
20
21
  export const GOOGLE_DOCS_SCOPES = [
@@ -35,6 +36,18 @@ export function hasGoogleDriveExportScope(scope?: string): boolean {
35
36
  );
36
37
  }
37
38
 
39
+ function hasGoogleDriveAccessScope(scope?: string): boolean {
40
+ if (!scope?.trim()) return true;
41
+ const grantedScopes = new Set(
42
+ (scope ?? "").split(/\s+/).filter((value) => value.length > 0),
43
+ );
44
+ return [
45
+ "https://www.googleapis.com/auth/drive",
46
+ "https://www.googleapis.com/auth/drive.file",
47
+ GOOGLE_DRIVE_READONLY_SCOPE,
48
+ ].some((requiredScope) => grantedScopes.has(requiredScope));
49
+ }
50
+
38
51
  interface GoogleDocsTokens {
39
52
  access_token: string;
40
53
  refresh_token?: string;
@@ -130,6 +143,7 @@ export async function getGoogleDocsAuthUrl(
130
143
  }
131
144
 
132
145
  async function refreshGoogleDocsToken(
146
+ provider: string,
133
147
  accountId: string,
134
148
  owner: string,
135
149
  tokens: GoogleDocsTokens,
@@ -143,7 +157,7 @@ async function refreshGoogleDocsToken(
143
157
  }
144
158
 
145
159
  if (!tokens.refresh_token) {
146
- await deleteOAuthTokens(GOOGLE_DOCS_PROVIDER, accountId);
160
+ await deleteOAuthTokens(provider, accountId, owner);
147
161
  throw new Error("Google Docs connection expired. Please reconnect.");
148
162
  }
149
163
 
@@ -190,7 +204,7 @@ async function refreshGoogleDocsToken(
190
204
 
191
205
  if (!data?.access_token) {
192
206
  if (isPermanentGoogleRefreshError(data?.error)) {
193
- await deleteOAuthTokens(GOOGLE_DOCS_PROVIDER, accountId);
207
+ await deleteOAuthTokens(provider, accountId, owner);
194
208
  }
195
209
  throw new Error(
196
210
  data?.error_description ||
@@ -208,7 +222,7 @@ async function refreshGoogleDocsToken(
208
222
  scope: data.scope ?? tokens.scope,
209
223
  };
210
224
  await saveOAuthTokens(
211
- GOOGLE_DOCS_PROVIDER,
225
+ provider,
212
226
  accountId,
213
227
  updated as unknown as Record<string, unknown>,
214
228
  owner,
@@ -286,23 +300,26 @@ export async function listGoogleDocsAccounts(owner: string): Promise<
286
300
  Array<{
287
301
  email: string;
288
302
  scope?: string;
303
+ shared?: boolean;
289
304
  }>
290
305
  > {
291
- const accounts = await listOAuthAccountsByOwner(GOOGLE_DOCS_PROVIDER, owner);
292
- return accounts.map((account) => ({
293
- email: account.accountId,
294
- scope:
295
- typeof account.tokens.scope === "string"
296
- ? account.tokens.scope
297
- : undefined,
298
- }));
306
+ const accounts = await listGoogleProviderAccounts(owner);
307
+ return accounts
308
+ .map((account) => ({
309
+ email: account.accountId,
310
+ scope:
311
+ typeof account.tokens.scope === "string"
312
+ ? account.tokens.scope
313
+ : undefined,
314
+ }))
315
+ .filter((account) => hasGoogleDriveAccessScope(account.scope));
299
316
  }
300
317
 
301
318
  export async function disconnectGoogleDocs(owner: string): Promise<void> {
302
- const accounts = await listOAuthAccountsByOwner(GOOGLE_DOCS_PROVIDER, owner);
319
+ const accounts = await listGoogleProviderAccounts(owner);
303
320
  await Promise.all(
304
321
  accounts.map((account) =>
305
- deleteOAuthTokens(GOOGLE_DOCS_PROVIDER, account.accountId),
322
+ deleteOAuthTokens(account.provider, account.accountId, owner),
306
323
  ),
307
324
  );
308
325
  }
@@ -311,17 +328,50 @@ export async function getGoogleDocsAccessToken(owner: string): Promise<{
311
328
  accessToken: string;
312
329
  accountEmail: string;
313
330
  } | null> {
314
- const accounts = await listOAuthAccountsByOwner(GOOGLE_DOCS_PROVIDER, owner);
331
+ const accounts = await listGoogleProviderAccounts(owner);
315
332
  if (accounts.length === 0) return null;
316
333
 
317
334
  const account = accounts[0];
318
- const stored = await getOAuthTokens(GOOGLE_DOCS_PROVIDER, account.accountId);
335
+ const stored = await getOAuthTokens(
336
+ account.provider,
337
+ account.accountId,
338
+ owner,
339
+ );
319
340
  if (!stored) return null;
320
341
 
321
342
  const accessToken = await refreshGoogleDocsToken(
343
+ account.provider,
322
344
  account.accountId,
323
345
  owner,
324
346
  stored as unknown as GoogleDocsTokens,
325
347
  );
326
348
  return { accessToken, accountEmail: account.accountId };
327
349
  }
350
+
351
+ async function listGoogleProviderAccounts(owner: string): Promise<
352
+ Array<{
353
+ provider: string;
354
+ accountId: string;
355
+ tokens: Record<string, unknown>;
356
+ }>
357
+ > {
358
+ const accounts = await Promise.all(
359
+ [GOOGLE_DOCS_PROVIDER, LEGACY_GOOGLE_DOCS_PROVIDER].map(async (provider) =>
360
+ (await listOAuthAccountsByOwner(provider, owner)).map((account) => ({
361
+ provider,
362
+ accountId: account.accountId,
363
+ tokens: account.tokens,
364
+ })),
365
+ ),
366
+ );
367
+ const seen = new Set<string>();
368
+ return accounts.flat().filter((account) => {
369
+ if (!hasGoogleDriveAccessScope(String(account.tokens.scope ?? ""))) {
370
+ return false;
371
+ }
372
+ const key = account.accountId.toLowerCase();
373
+ if (seen.has(key)) return false;
374
+ seen.add(key);
375
+ return true;
376
+ });
377
+ }
@@ -86,10 +86,21 @@ export async function applySlideContentEdits(
86
86
 
87
87
  export async function formatSlideHtml(content: string): Promise<string> {
88
88
  try {
89
- const prettier = await import("prettier");
90
- return await prettier.format(content, {
89
+ // prettier's main entry `import()`s all 13 parser plugins, so a bundler
90
+ // inlines ~3.5MB of flow/typescript/yaml/markdown parsers just to format
91
+ // HTML. Load the standalone core plus only the plugins the HTML printer
92
+ // reaches, which still formats embedded <style> and <script>.
93
+ const [{ format }, ...plugins] = await Promise.all([
94
+ import("prettier/standalone"),
95
+ import("prettier/plugins/html"),
96
+ import("prettier/plugins/postcss"),
97
+ import("prettier/plugins/babel"),
98
+ import("prettier/plugins/estree"),
99
+ ]);
100
+ return await format(content, {
91
101
  parser: "html",
92
102
  htmlWhitespaceSensitivity: "ignore",
103
+ plugins,
93
104
  });
94
105
  } catch (error) {
95
106
  const message = error instanceof Error ? error.message : String(error);
@@ -100,14 +100,17 @@ When a Google Drive or Google Slides request needs authentication, tell the user
100
100
  search: async (query: string) => {
101
101
  const db = getDb();
102
102
  const access = accessFilter(decks, deckShares);
103
+ // Project only id/title — decks.data is the full deck JSON (every
104
+ // slide) and must not be pulled into this per-keystroke search.
105
+ const mentionColumns = { id: decks.id, title: decks.title };
103
106
  const rows = query
104
107
  ? await db
105
- .select()
108
+ .select(mentionColumns)
106
109
  .from(decks)
107
110
  .where(and(access, like(decks.title, `%${query}%`)))
108
111
  .limit(15)
109
112
  : await db
110
- .select()
113
+ .select(mentionColumns)
111
114
  .from(decks)
112
115
  .where(access)
113
116
  .orderBy(desc(decks.updatedAt))
@@ -2,6 +2,11 @@
2
2
  "extends": "@agent-native/core/tsconfig.base.json",
3
3
  "compilerOptions": {
4
4
  "ignoreDeprecations": "6.0",
5
+ // The shared base targets ES2020, but `new Error(message, { cause })`
6
+ // needs ErrorOptions from lib.es2022.error.d.ts (matches packages/core's
7
+ // own tsconfig.json, which already targets ES2022 for the same reason).
8
+ "target": "ES2022",
9
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
5
10
  "rootDirs": [".", "./.react-router/types"],
6
11
  "paths": {
7
12
  "*": ["./*"],
@@ -16,7 +16,12 @@ export default mergeConfig(
16
16
  },
17
17
  test: {
18
18
  include: ["**/*.{test,spec}.?(c|m)[jt]s?(x)"],
19
- exclude: ["**/node_modules/**", "**/.git/**", "**/dist/**"],
19
+ exclude: [
20
+ "**/node_modules/**",
21
+ "**/.git/**",
22
+ "**/dist/**",
23
+ "**/.react-router/**",
24
+ ],
20
25
  },
21
26
  }),
22
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
 
@@ -15,7 +15,10 @@ export function meta() {
15
15
 
16
16
  export default function SettingsRoute() {
17
17
  const t = useT();
18
- const agentSettingsTabs = useAgentSettingsTabs();
18
+ // Tasks embeds an ExtensionSlot in TaskFieldsSidebar and wires /extensions
19
+ // into agent navigation, so the settings management tab must exist too —
20
+ // otherwise the /settings/extensions destination silently falls back to General.
21
+ const agentSettingsTabs = useAgentSettingsTabs({ extensionTools: true });
19
22
  useSetPageTitle(t("header.pageSettings"));
20
23
 
21
24
  return (
@@ -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;
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-22
4
+ ---
5
+
6
+ The Extensions link now opens the Extensions tab in Settings instead of silently landing on General.