@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
@@ -24,6 +24,37 @@ function llmProviderFromEngine(engineName, model) {
24
24
  function costUsdFromCenticents(value) {
25
25
  return Math.round((value / 10_000) * 1_000_000) / 1_000_000;
26
26
  }
27
+ /**
28
+ * Wall-clock time covered by these intervals, counting overlap once.
29
+ *
30
+ * Tools run concurrently, so summing durations reports more elapsed time than
31
+ * actually passed — enough to drive a derived remainder to zero on a parallel
32
+ * fan-out.
33
+ */
34
+ function coveredDurationMs(intervals) {
35
+ if (intervals.length === 0)
36
+ return 0;
37
+ const sorted = [...intervals].sort((a, b) => a.start - b.start);
38
+ let covered = 0;
39
+ let { start: openStart, end: openEnd } = sorted[0];
40
+ for (const { start, end } of sorted.slice(1)) {
41
+ if (start > openEnd) {
42
+ covered += openEnd - openStart;
43
+ openStart = start;
44
+ openEnd = end;
45
+ }
46
+ else if (end > openEnd) {
47
+ openEnd = end;
48
+ }
49
+ }
50
+ return covered + (openEnd - openStart);
51
+ }
52
+ function spanIntervals(spans) {
53
+ return spans.map((s) => ({
54
+ start: s.createdAt,
55
+ end: s.createdAt + Math.max(0, s.durationMs),
56
+ }));
57
+ }
27
58
  /**
28
59
  * Project run metadata onto flat PostHog trace properties.
29
60
  *
@@ -144,7 +175,7 @@ function emitLlmGenerationTrackingEvent(args) {
144
175
  $ai_provider: provider,
145
176
  $ai_input_tokens: args.inputTokens,
146
177
  $ai_output_tokens: args.outputTokens,
147
- $ai_latency: Math.round((args.durationMs / 1000) * 1000) / 1000,
178
+ $ai_latency: Math.round(args.llmDurationMs) / 1000,
148
179
  $ai_is_error: args.status === "error",
149
180
  $ai_error: args.status === "error"
150
181
  ? toAiErrorDetail(error, {
@@ -153,16 +184,24 @@ function emitLlmGenerationTrackingEvent(args) {
153
184
  retryable: terminalRetryable,
154
185
  })
155
186
  : undefined,
187
+ // Every engine here streams (`messages.stream`, `streamText`, gateway SSE),
188
+ // which is also what makes `$ai_time_to_first_token` meaningful.
189
+ $ai_stream: true,
156
190
  $ai_cache_read_input_tokens: args.cacheReadTokens,
157
191
  $ai_cache_creation_input_tokens: args.cacheWriteTokens,
158
- $ai_request_count: 1,
192
+ $ai_request_count: args.llmCallCount,
159
193
  $ai_total_cost_usd: costUsd,
160
194
  $ai_input: args.aiInput,
161
195
  $ai_output_choices: args.aiOutputChoices,
162
196
  $ai_tools: args.aiTools,
163
- $ai_input_truncated: args.aiInputTruncated || undefined,
164
- $ai_output_truncated: args.aiOutputTruncated || undefined,
165
- $ai_time_to_first_token: args.firstTokenMs,
197
+ input_truncated: args.aiInputTruncated || undefined,
198
+ output_truncated: args.aiOutputTruncated || undefined,
199
+ latency_source: args.llmDurationMeasured ? "measured" : "derived",
200
+ // Seconds, per PostHog's schema — `time_to_first_token_ms` above is the
201
+ // millisecond field this framework's own dashboards read.
202
+ $ai_time_to_first_token: args.firstTokenMs === undefined
203
+ ? undefined
204
+ : Math.round(args.firstTokenMs) / 1000,
166
205
  $session_id: args.browserSessionId,
167
206
  };
168
207
  if (args.experimentAssignments?.length) {
@@ -188,6 +227,7 @@ function emitLlmGenerationTrackingEvent(args) {
188
227
  .then(({ track }) => {
189
228
  track("$ai_generation", properties, {
190
229
  userId: args.userId ?? undefined,
230
+ occurredAt: args.createdAt,
191
231
  });
192
232
  })
193
233
  .catch(() => { });
@@ -212,6 +252,10 @@ function emitLlmGenerationTrackingEvent(args) {
212
252
  */
213
253
  function buildGenerationContent(args) {
214
254
  const { config } = args;
255
+ // `$ai_input` is the conversation, not the system prompt. PostHog accepts a
256
+ // `system` role, but the prompt is app configuration rather than content and
257
+ // is near-identical on every run — shipping it would repeat kilobytes on each
258
+ // generation for no analytical gain.
215
259
  const input = config.capturePrompts
216
260
  ? boundAiContent(redactSensitiveFields(args.messages))
217
261
  : undefined;
@@ -364,6 +408,13 @@ export async function instrumentAgentLoop(opts) {
364
408
  let toolCallCount = 0;
365
409
  let successfulTools = 0;
366
410
  let failedTools = 0;
411
+ // One `model_stream` start/end bracket is emitted per LLM round-trip, and it
412
+ // closes before any tool of that turn is started — so these intervals ARE the
413
+ // model's wall clock, not an estimate of it. Recording them is what lets the
414
+ // generation report a measured `$ai_latency` instead of backing tool time out
415
+ // of the run duration.
416
+ const modelStreamIntervals = [];
417
+ let modelStreamOpenedAt = null;
367
418
  // Track in-flight OTel tool spans so they're all ended even if the loop
368
419
  // throws before a matching `tool_done` arrives.
369
420
  const openOtelToolSpans = new Set();
@@ -447,6 +498,20 @@ export async function instrumentAgentLoop(opts) {
447
498
  runStatus = "error";
448
499
  errorMessage = "Missing API key";
449
500
  }
501
+ if (event.type === "model_stream") {
502
+ // The emitter brackets these itself, so a repeated start or an
503
+ // unmatched end is a no-op here rather than a fabricated interval.
504
+ if (event.status === "start") {
505
+ modelStreamOpenedAt ??= Date.now();
506
+ }
507
+ else if (modelStreamOpenedAt !== null) {
508
+ modelStreamIntervals.push({
509
+ start: modelStreamOpenedAt,
510
+ end: Date.now(),
511
+ });
512
+ modelStreamOpenedAt = null;
513
+ }
514
+ }
450
515
  if (event.type === "tool_start") {
451
516
  const counter = toolInvocationCounter++;
452
517
  const sid = spanId();
@@ -563,6 +628,25 @@ export async function instrumentAgentLoop(opts) {
563
628
  else if (pending) {
564
629
  pending.endResult = otelEndResult;
565
630
  }
631
+ const spanMetadataFields = {};
632
+ if (config.captureToolArgs && pending) {
633
+ // Strip Authorization/api-key/token-shaped values before persisting
634
+ // (M14 in the MCP/A2A audit). Tool-runtime execution still sees the
635
+ // unredacted input — only the long-lived span row is sanitized.
636
+ spanMetadataFields.input = redactSensitiveFields(pending.input);
637
+ }
638
+ // A failed tool's content reaches the span through `errorMessage`; a
639
+ // successful one had nowhere to go, so every healthy tool span shipped
640
+ // an input and no output — indistinguishable from a tool that returned
641
+ // nothing. Same redaction and truncation as the error path.
642
+ if (!isError &&
643
+ config.captureToolResults &&
644
+ typeof event.result === "string") {
645
+ spanMetadataFields.output = truncateToolErrorMessage(redactToolErrorMessage(event.result));
646
+ }
647
+ const spanMetadata = Object.keys(spanMetadataFields).length
648
+ ? spanMetadataFields
649
+ : null;
566
650
  const span = {
567
651
  id: pending?.spanId ?? spanId(),
568
652
  runId,
@@ -579,16 +663,11 @@ export async function instrumentAgentLoop(opts) {
579
663
  durationMs: pending ? Math.max(0, finishedAt - pending.startMs) : 0,
580
664
  status: isError ? "error" : "success",
581
665
  errorMessage: isError ? event.result : null,
582
- metadata: config.captureToolArgs && pending
583
- ? // Strip Authorization/api-key/token-shaped values before
584
- // persisting (M14 in the MCP/A2A audit). Tool-runtime
585
- // execution still sees the unredacted input — only the
586
- // long-lived span row is sanitized.
587
- {
588
- input: redactSensitiveFields(pending.input),
589
- }
590
- : null,
591
- createdAt: Date.now(),
666
+ metadata: spanMetadata,
667
+ // The span's start, not its completion: `durationMs` is measured from
668
+ // here, so stamping the end instead places the tool after the run
669
+ // ended in any timeline that plots start + duration.
670
+ createdAt: pending?.startMs ?? finishedAt,
592
671
  };
593
672
  spans.push(span);
594
673
  }
@@ -629,6 +708,19 @@ export async function instrumentAgentLoop(opts) {
629
708
  try {
630
709
  const runEnd = Date.now();
631
710
  const totalDurationMs = runEnd - runStart;
711
+ // The loop threw or was killed mid-stream, so no `end` ever arrived. The
712
+ // model was still running when the run stopped, so the interval closes at
713
+ // the run's end rather than being dropped.
714
+ if (modelStreamOpenedAt !== null) {
715
+ modelStreamIntervals.push({ start: modelStreamOpenedAt, end: runEnd });
716
+ modelStreamOpenedAt = null;
717
+ }
718
+ // Undefined means the engine never bracketed its model calls, NOT that
719
+ // the model took no time — the two must stay distinguishable, because
720
+ // only the first may fall back to backing tool time out of the run.
721
+ const measuredModelDurationMs = modelStreamIntervals.length
722
+ ? coveredDurationMs(modelStreamIntervals)
723
+ : undefined;
632
724
  if (pendingTools.size > 0) {
633
725
  if (runStatus === "success") {
634
726
  runStatus = "error";
@@ -681,7 +773,7 @@ export async function instrumentAgentLoop(opts) {
681
773
  status: "error",
682
774
  errorMessage: interruptedMessage,
683
775
  metadata: null,
684
- createdAt: runEnd,
776
+ createdAt: pending.startMs,
685
777
  });
686
778
  }
687
779
  pendingTools.clear();
@@ -712,6 +804,15 @@ export async function instrumentAgentLoop(opts) {
712
804
  message: `Agent run was cut off before finishing (${cutOffReason}).`,
713
805
  }
714
806
  : undefined);
807
+ const collectedToolSpans = spans.filter((s) => s.spanType === "tool_call");
808
+ // Resolved before the generation event, not just before the span events:
809
+ // the generation's latency is the run minus the tool time PostHog will
810
+ // actually see, so it has to be computed against the same set. Tools
811
+ // dropped by `captureLlmSpans` or the per-run cap have no sibling span to
812
+ // hold their time, and subtracting them would lose it from the trace.
813
+ const emittedToolSpans = (config.captureLlmSpans ? collectedToolSpans : []).slice(0, MAX_AI_SPANS_PER_RUN);
814
+ const droppedToolSpans = (config.captureLlmSpans ? collectedToolSpans.length : 0) -
815
+ emittedToolSpans.length;
715
816
  let llmCallCount = 0;
716
817
  if (usage || runStatus === "error") {
717
818
  llmCallCount =
@@ -736,12 +837,23 @@ export async function instrumentAgentLoop(opts) {
736
837
  ? Math.max(0, usage.firstEngineEventAtMs - runStart)
737
838
  : undefined;
738
839
  const llmSpanId = spanId();
840
+ const generationToolSpans = collectedToolSpans;
841
+ // Measured model time when the engine bracketed its round-trips.
842
+ //
843
+ // The fallback backs tool time out of the run instead, which is an
844
+ // estimate and behaves like one: it has to net out overlapping tools,
845
+ // skip tools PostHog will not receive, and clamp at zero. Engines that
846
+ // report `model_stream` need none of that, so `latency_source` records
847
+ // which of the two a given `$ai_latency` came from.
848
+ const llmDurationMs = measuredModelDurationMs ??
849
+ Math.max(0, totalDurationMs -
850
+ coveredDurationMs(spanIntervals(emittedToolSpans)));
739
851
  const generationContent = buildGenerationContent({
740
852
  config,
741
853
  messages: loopOpts.messages,
742
854
  tools: loopOpts.tools,
743
855
  assistantText: assistantTextParts.join(""),
744
- toolSpans: spans.filter((s) => s.spanType === "tool_call"),
856
+ toolSpans: generationToolSpans,
745
857
  });
746
858
  const llmSpan = {
747
859
  id: llmSpanId,
@@ -785,6 +897,9 @@ export async function instrumentAgentLoop(opts) {
785
897
  : undefined,
786
898
  costCentsX100: usageReported ? costCentsX100 : undefined,
787
899
  durationMs: totalDurationMs,
900
+ llmDurationMs,
901
+ llmDurationMeasured: measuredModelDurationMs !== undefined,
902
+ llmCallCount,
788
903
  firstTokenMs,
789
904
  status: runStatus,
790
905
  errorMessage,
@@ -844,11 +959,22 @@ export async function instrumentAgentLoop(opts) {
844
959
  const provider = llmProviderFromEngine(typeof loopOpts.engine?.name === "string"
845
960
  ? loopOpts.engine.name
846
961
  : undefined, usage?.model ?? loopOpts.model);
847
- const toolSpans = config.captureLlmSpans
848
- ? spans.filter((s) => s.spanType === "tool_call")
849
- : [];
850
- const emittedToolSpans = toolSpans.slice(0, MAX_AI_SPANS_PER_RUN);
851
- const droppedToolSpans = toolSpans.length - emittedToolSpans.length;
962
+ // PostHog reads a trace's input/output state ONLY from the `$ai_trace`
963
+ // event — never from its children — so a trace whose detail view should
964
+ // show what the run was asked and what it answered has to carry them
965
+ // here. Gated on `capturePrompts` and omitted, not emptied, when off.
966
+ const traceInputState = config.capturePrompts
967
+ ? redactSensitiveFields(loopOpts.messages)
968
+ : undefined;
969
+ const traceAssistantText = assistantTextParts.join("");
970
+ const traceOutputState = config.capturePrompts && traceAssistantText
971
+ ? [
972
+ {
973
+ role: "assistant",
974
+ content: redactToolErrorMessage(traceAssistantText),
975
+ },
976
+ ]
977
+ : undefined;
852
978
  emitAiTraceEvent({
853
979
  runId,
854
980
  threadId,
@@ -856,7 +982,7 @@ export async function instrumentAgentLoop(opts) {
856
982
  spanName,
857
983
  model: usage?.model ?? loopOpts.model,
858
984
  provider,
859
- latencySeconds: Math.round(totalDurationMs) / 1000,
985
+ durationMs: totalDurationMs,
860
986
  isError: runStatus === "error",
861
987
  error: aiError,
862
988
  inputTokens: usage?.usageReported ? usage.inputTokens : undefined,
@@ -866,6 +992,8 @@ export async function instrumentAgentLoop(opts) {
866
992
  : undefined,
867
993
  createdAt: runStart,
868
994
  browserSessionId,
995
+ inputState: traceInputState,
996
+ outputState: traceOutputState,
869
997
  extraProperties: {
870
998
  ...trackingIdentityProperties(),
871
999
  source: "agent_observability",
@@ -879,8 +1007,8 @@ export async function instrumentAgentLoop(opts) {
879
1007
  // A truncated run must not read as a complete one.
880
1008
  ...(droppedToolSpans > 0
881
1009
  ? {
882
- $ai_spans_dropped: droppedToolSpans,
883
- $ai_spans_emitted: emittedToolSpans.length,
1010
+ spans_dropped: droppedToolSpans,
1011
+ spans_emitted: emittedToolSpans.length,
884
1012
  }
885
1013
  : {}),
886
1014
  },
@@ -911,10 +1039,12 @@ export async function instrumentAgentLoop(opts) {
911
1039
  : undefined,
912
1040
  createdAt: span.createdAt,
913
1041
  browserSessionId,
914
- // `metadata.input` is already redacted and only present when
915
- // `captureToolArgs` is on; absent stays absent.
1042
+ // `metadata.input` / `metadata.output` are already redacted and
1043
+ // only present when `captureToolArgs` / `captureToolResults` are
1044
+ // on; absent stays absent.
916
1045
  inputState: span.metadata?.input,
917
- outputState: toolErrorMessage,
1046
+ outputState: toolErrorMessage ??
1047
+ span.metadata?.output,
918
1048
  extraProperties: {
919
1049
  ...trackingIdentityProperties(),
920
1050
  source: "agent_observability",
@@ -292,8 +292,8 @@ const PROFILES = {
292
292
  label: "Gmail",
293
293
  required: true,
294
294
  builderIncluded: false,
295
- keySummary: "Google OAuth client ID and secret",
296
- why: "Mail needs OAuth access to read, send, and organize the mailbox.",
295
+ keySummary: "Connect Gmail with OAuth",
296
+ why: "Mail uses the workspace's managed Google connection; no key is pasted here.",
297
297
  },
298
298
  {
299
299
  id: "attachment-storage",
@@ -13,6 +13,7 @@ import { deleteCookie, defineEventHandler, getCookie, getMethod, getQuery, setRe
13
13
  import { appStateGet, appStatePut } from "../application-state/store.js";
14
14
  import { getOrgContext } from "../org/context.js";
15
15
  import { getSession } from "../server/auth.js";
16
+ import { CredentialStoreUnavailableError } from "../server/credential-provider.js";
16
17
  import { awaitBootstrap, getH3App, markDefaultPluginProvided, } from "../server/framework-request-handler.js";
17
18
  import { runWithRequestContext } from "../server/request-context.js";
18
19
  import { FIRST_RUN_ONBOARDING_COMPLETED_KEY, FIRST_RUN_ONBOARDING_COOKIE, FIRST_RUN_ONBOARDING_ELIGIBLE_KEY, } from "../shared/first-run-onboarding.js";
@@ -57,7 +58,11 @@ async function serializeSteps(context, options = {}) {
57
58
  // chain of credential/settings reads — walking them one at a time made this
58
59
  // route cost the SUM of every step's round trips against a remote database
59
60
  // instead of the slowest one. `Promise.all` preserves `steps` order.
60
- return Promise.all(steps.map(async (step) => {
61
+ const serialized = await Promise.all(steps.map(async (step) => {
62
+ if (!options.preview && step.isAvailable) {
63
+ if (!(await step.isAvailable(context)))
64
+ return null;
65
+ }
61
66
  let complete = false;
62
67
  if (!options.preview) {
63
68
  try {
@@ -80,6 +85,7 @@ async function serializeSteps(context, options = {}) {
80
85
  methods: step.methods,
81
86
  };
82
87
  }));
88
+ return serialized.filter((step) => step !== null);
83
89
  }
84
90
  function withOnboardingRequestContext(context, fn) {
85
91
  return runWithRequestContext({
@@ -180,7 +186,9 @@ export function createOnboardingPlugin(options = {}) {
180
186
  };
181
187
  });
182
188
  }
183
- catch {
189
+ catch (error) {
190
+ if (error instanceof CredentialStoreUnavailableError)
191
+ throw error;
184
192
  return { dismissed: false, allComplete: false };
185
193
  }
186
194
  }));
@@ -66,6 +66,8 @@ export interface OnboardingStep {
66
66
  /** Required steps block onboarding dismissal when incomplete. */
67
67
  required?: boolean;
68
68
  methods: OnboardingMethod[];
69
+ /** Hide the step when its backing capability is not configured. */
70
+ isAvailable?: (context?: OnboardingResolveContext) => boolean | Promise<boolean>;
69
71
  /** Resolver — called on every `GET /_agent-native/onboarding/steps` request. */
70
72
  isComplete: (context?: OnboardingResolveContext) => boolean | Promise<boolean>;
71
73
  }
@@ -93,8 +93,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
93
93
  label?: undefined;
94
94
  message?: undefined;
95
95
  } | {
96
- id?: undefined;
97
96
  count?: undefined;
97
+ id?: undefined;
98
98
  deleted?: undefined;
99
99
  providers?: undefined;
100
100
  found: boolean;
@@ -311,9 +311,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
311
311
  notes?: string | undefined;
312
312
  scope?: "org" | "user" | undefined;
313
313
  }, {
314
+ id?: undefined;
314
315
  message?: undefined;
315
316
  deleted?: undefined;
316
- id?: undefined;
317
317
  provider?: undefined;
318
318
  providers: {
319
319
  id: string;
@@ -329,10 +329,10 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
329
329
  registered?: undefined;
330
330
  label?: undefined;
331
331
  } | {
332
+ id?: undefined;
332
333
  message?: undefined;
333
334
  deleted?: undefined;
334
335
  count?: undefined;
335
- id?: undefined;
336
336
  providers?: undefined;
337
337
  found: boolean;
338
338
  provider: import("../custom-registry.js").CustomProviderConfig;
@@ -436,6 +436,7 @@ const PROVIDER_CONFIGS = {
436
436
  type: "oauth-bearer",
437
437
  oauthProvider: "google",
438
438
  tokenLabel: "Google OAuth token",
439
+ workspaceProvider: "gmail",
439
440
  },
440
441
  credentialKeys: ["GOOGLE_OAUTH_ACCOUNT"],
441
442
  docsUrls: ["https://developers.google.com/gmail/api/reference/rest"],
@@ -587,6 +588,7 @@ const PROVIDER_CONFIGS = {
587
588
  type: "oauth-bearer",
588
589
  oauthProvider: "google",
589
590
  tokenLabel: "Google OAuth token",
591
+ workspaceProvider: "google_calendar",
590
592
  },
591
593
  credentialKeys: ["GOOGLE_OAUTH_ACCOUNT"],
592
594
  docsUrls: ["https://developers.google.com/calendar/api/v3/reference"],
@@ -359,15 +359,19 @@ function physicalWorkspaceResourceId(id) {
359
359
  : null;
360
360
  }
361
361
  function rowToGrantedWorkspaceResource(row) {
362
+ const contentLoaded = Object.prototype.hasOwnProperty.call(row, "content");
362
363
  const content = String(row.content ?? "");
363
364
  const path = String(row.path ?? "");
365
+ const size = contentLoaded
366
+ ? Buffer.byteLength(content, "utf8")
367
+ : Number(row.content_size ?? 0);
364
368
  return {
365
369
  id: syntheticWorkspaceResourceId(String(row.id)),
366
370
  path,
367
371
  owner: WORKSPACE_OWNER,
368
372
  content,
369
373
  mimeType: workspaceResourceMimeType(path),
370
- size: Buffer.byteLength(content, "utf8"),
374
+ size: Number.isFinite(size) ? size : 0,
371
375
  createdAt: Number(row.created_at ?? Date.now()),
372
376
  updatedAt: Number(row.updated_at ?? Date.now()),
373
377
  createdBy: "system",
@@ -480,7 +484,7 @@ function mergeResourceMetas(primary, inherited) {
480
484
  }
481
485
  return merged;
482
486
  }
483
- async function selectGrantedWorkspaceResourceRows(input) {
487
+ async function selectGrantedWorkspaceResourceRows(input, options = {}) {
484
488
  const appId = currentWorkspaceAppId(input.workspaceAppId);
485
489
  const { userEmail, orgId } = requestScopedResourceIdentity(input);
486
490
  if (!appId || (!userEmail && !orgId))
@@ -507,6 +511,9 @@ async function selectGrantedWorkspaceResourceRows(input) {
507
511
  conditions.push("wr.owner_email = ?", "wr.org_id IS NULL", "wg.owner_email = ?", "wg.org_id IS NULL");
508
512
  args.push(userEmail, userEmail);
509
513
  }
514
+ const contentSelect = options.includeContent === false
515
+ ? `${isPostgres() ? "octet_length(wr.content)" : "length(CAST(wr.content AS BLOB))"} AS content_size`
516
+ : "wr.content";
510
517
  const client = getDbExec();
511
518
  const { rows } = await client.execute({
512
519
  sql: `
@@ -516,7 +523,7 @@ async function selectGrantedWorkspaceResourceRows(input) {
516
523
  wr.name,
517
524
  wr.description,
518
525
  wr.path,
519
- wr.content,
526
+ ${contentSelect},
520
527
  wr.created_at,
521
528
  wr.updated_at,
522
529
  wg.id AS grant_id,
@@ -532,7 +539,9 @@ async function selectGrantedWorkspaceResourceRows(input) {
532
539
  }
533
540
  async function grantedWorkspaceResources(input) {
534
541
  try {
535
- const rows = await selectGrantedWorkspaceResourceRows(input);
542
+ const rows = await selectGrantedWorkspaceResourceRows(input, {
543
+ includeContent: false,
544
+ });
536
545
  return rows.map(rowToGrantedWorkspaceResource);
537
546
  }
538
547
  catch {
@@ -70,8 +70,29 @@ export async function run(args = {}) {
70
70
  const currentModel = currentEntry && !envUnavailable
71
71
  ? normalizeModelForEngine(currentEntry, currentModelCandidate ?? currentEntry.defaultModel, { preserveCustomModels })
72
72
  : (currentModelCandidate ?? DEFAULT_MODEL);
73
- const result = {
74
- engines: engines.map((e) => ({
73
+ // Readiness has to be resolved here: `requiredEnvVars` alone cannot see
74
+ // vault-stored keys or the deploy-injected Builder gateway lane, so a client
75
+ // that re-derives it from env keys marks working engines unconfigured.
76
+ const engineEntries = await Promise.all(engines.map(async (e) => {
77
+ // Resolved per engine, not across the set: one provider whose credential
78
+ // store is momentarily unreadable must not reject the whole listing. The
79
+ // chat refresh catches that rejection and renders an empty catalog, so a
80
+ // single unrelated provider would make every engine unselectable — the
81
+ // exact symptom this readiness plumbing exists to fix.
82
+ //
83
+ // A read error is its own state, left as `configured: undefined` so the
84
+ // client falls back to its env heuristic. Folding it into `false` would
85
+ // claim the engine needs an API key when nobody actually knows.
86
+ let configured;
87
+ let configuredError;
88
+ try {
89
+ configured = await isStoredEngineUsableForRequest({ engine: e.name, model: e.defaultModel }, e);
90
+ }
91
+ catch (error) {
92
+ configuredError =
93
+ error instanceof Error ? error.message : String(error);
94
+ }
95
+ return {
75
96
  name: e.name,
76
97
  label: e.label,
77
98
  description: e.description,
@@ -81,7 +102,12 @@ export async function run(args = {}) {
81
102
  requiredEnvVars: e.requiredEnvVars,
82
103
  installPackage: e.installPackage,
83
104
  packageInstalled: isAgentEnginePackageInstalled(e),
84
- })),
105
+ configured,
106
+ configuredError,
107
+ };
108
+ }));
109
+ const result = {
110
+ engines: engineEntries,
85
111
  current: envUnavailable
86
112
  ? null
87
113
  : {
@@ -27,7 +27,7 @@ export function registerFrameworkSecrets() {
27
27
  id: "google_drive",
28
28
  credentialPrefix: "GOOGLE",
29
29
  oauthProvider: "google",
30
- label: "Google Drive",
30
+ label: "Google Workspace",
31
31
  docsUrl: "https://developers.google.com/identity/protocols/oauth2/web-server",
32
32
  },
33
33
  {
@@ -163,6 +163,11 @@ function preserveActionFlags(entry) {
163
163
  const out = {};
164
164
  if (typeof entry.agentTool === "boolean")
165
165
  out.agentTool = entry.agentTool;
166
+ if (typeof entry.mcpTool === "boolean")
167
+ out.mcpTool = entry.mcpTool;
168
+ if (typeof entry.deferLoading === "boolean") {
169
+ out.deferLoading = entry.deferLoading;
170
+ }
166
171
  if (typeof entry.requiresAuth === "boolean") {
167
172
  out.requiresAuth = entry.requiresAuth;
168
173
  }
@@ -13,6 +13,18 @@ export declare function filterReadOnlyActions(actions: Record<string, ActionEntr
13
13
  * list (in-app assistant, MCP, A2A, job/trigger runners) or actions prompt.
14
14
  * Default-allow: only an explicit `false` is excluded. */
15
15
  export declare function filterAgentTools(actions: Record<string, ActionEntry>): Record<string, ActionEntry>;
16
+ /**
17
+ * The MCP-only actions: `agentTool: false` paired with an explicit
18
+ * `mcpTool: true`.
19
+ *
20
+ * `filterAgentTools` drops these, which is right for every in-app surface and
21
+ * wrong for the two external ones. Rather than have the MCP and A2A mounts
22
+ * read the unfiltered registry — and re-derive which agent-hidden actions are
23
+ * safe to serve — the plugin re-merges exactly this set into those two mounts.
24
+ * A caller that wants "everything external" merges this with the agent surface;
25
+ * nobody has to remember which raw fields make an action MCP-only.
26
+ */
27
+ export declare function filterMcpOnlyActions(actions: Record<string, ActionEntry>): Record<string, ActionEntry>;
16
28
  export declare function filterPublicAgentActions(actions: Record<string, ActionEntry>): Record<string, ActionEntry>;
17
29
  /**
18
30
  * True when an action takes a free-form query or program as input — raw SQL, a
@@ -137,15 +149,27 @@ export declare function createA2AEngineToolSurface(availableTools: EngineTool[],
137
149
  export declare function isSelectedA2AReceiver(selectedReceiverApp: string | undefined, appId: string | undefined): boolean;
138
150
  export declare function buildSelectedA2AReceiverContext(appId: string): string;
139
151
  /**
140
- * The first-request tool catalog: an explicit `initialToolNames` verbatim, or
141
- * the app's OWN actions by default.
152
+ * The first-request tool catalog, in precedence order:
153
+ *
154
+ * 1. An explicit `initialToolNames`, plus every action that declares
155
+ * `deferLoading: false`. The configured list stays authoritative — an
156
+ * action it names is never dropped by a `deferLoading: true` — so a
157
+ * template can annotate its actions before deleting the array, not after.
158
+ * 2. Otherwise, the actions that declared `deferLoading: false`, if any did.
159
+ * 3. Otherwise, the app's OWN actions, minus any marked `deferLoading: true`.
160
+ *
161
+ * Step 2 is what makes `deferLoading` a replacement for the array rather than
162
+ * a second copy of it: opting a few starter actions out of deferral on an app
163
+ * with no configured list defers everything else, instead of leaving the whole
164
+ * registry in and the annotation decorative.
142
165
  *
143
166
  * "Its own actions" excludes the framework kits. They arrive in this same
144
167
  * registry through `autoDiscoverActions` -> `mergeCoreSharingActions`, so the
145
168
  * plain `Object.keys` default promoted ~45 sharing/review/history/flag schemas
146
169
  * into every app's first request whether or not the app had those surfaces. They
147
170
  * remain in `availableTools` and are still found by `tool-search`; an app that
148
- * wants one on turn one names it in `initialToolNames`.
171
+ * wants one on turn one sets `deferLoading: false` or names it in
172
+ * `initialToolNames`.
149
173
  */
150
174
  export declare function resolveInitialToolNames(templateActions: Record<string, ActionEntry>, configured?: string[]): string[];
151
175
  export {};