@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
@@ -0,0 +1,321 @@
1
+ import { DEFAULT_FACTORY_ID } from "../factory-graph/store.js";
2
+ import { factoryConfigRowId } from "./factory-scope.js";
3
+
4
+ export type FactoryConfigSqlRow = {
5
+ id: string;
6
+ org_id: string | null;
7
+ factory_id: string | null;
8
+ slack_workspace: string | null;
9
+ slack_channel_id: string | null;
10
+ slack_channel_name: string | null;
11
+ builder_slack_user_id: string | null;
12
+ polling_enabled: number | null;
13
+ last_slack_ts: string | null;
14
+ slack_history_cursor: string | null;
15
+ repository: string | null;
16
+ github_polling_enabled: number | null;
17
+ sentry_polling_enabled: number | null;
18
+ sentry_org_slug: string | null;
19
+ sentry_project_slug: string | null;
20
+ sentry_environment: string | null;
21
+ last_sentry_seen_at: string | null;
22
+ automation_failure_alerts_enabled: number | null;
23
+ automation_failure_alert_email: string | null;
24
+ last_automation_failure_alert_key: string | null;
25
+ last_automation_failure_alert_at: string | null;
26
+ owner_email: string | null;
27
+ created_at: string | null;
28
+ updated_at: string | null;
29
+ };
30
+
31
+ export type FactoryConfigReconcilePlan = {
32
+ fromId: string;
33
+ row: FactoryConfigSqlRow;
34
+ deleteIds: string[];
35
+ };
36
+
37
+ function rowText(row: Record<string, unknown>, key: string): string | null {
38
+ return asText(row[key]);
39
+ }
40
+
41
+ function rowFlag(row: Record<string, unknown>, key: string): number | null {
42
+ const value = row[key];
43
+ if (value === null || value === undefined || value === "") return null;
44
+ return Number(value) === 1 ? 1 : 0;
45
+ }
46
+
47
+ export function factoryConfigSqlRowFromQuery(
48
+ row: Record<string, unknown>,
49
+ ): FactoryConfigSqlRow | null {
50
+ const id = rowText(row, "id");
51
+ if (!id) return null;
52
+ return {
53
+ id,
54
+ org_id: rowText(row, "org_id"),
55
+ factory_id: rowText(row, "factory_id"),
56
+ slack_workspace: rowText(row, "slack_workspace"),
57
+ slack_channel_id: rowText(row, "slack_channel_id"),
58
+ slack_channel_name: rowText(row, "slack_channel_name"),
59
+ builder_slack_user_id: rowText(row, "builder_slack_user_id"),
60
+ polling_enabled: rowFlag(row, "polling_enabled"),
61
+ last_slack_ts: rowText(row, "last_slack_ts"),
62
+ slack_history_cursor: rowText(row, "slack_history_cursor"),
63
+ repository: rowText(row, "repository"),
64
+ github_polling_enabled: rowFlag(row, "github_polling_enabled"),
65
+ sentry_polling_enabled: rowFlag(row, "sentry_polling_enabled"),
66
+ sentry_org_slug: rowText(row, "sentry_org_slug"),
67
+ sentry_project_slug: rowText(row, "sentry_project_slug"),
68
+ sentry_environment: rowText(row, "sentry_environment"),
69
+ last_sentry_seen_at: rowText(row, "last_sentry_seen_at"),
70
+ automation_failure_alerts_enabled: rowFlag(
71
+ row,
72
+ "automation_failure_alerts_enabled",
73
+ ),
74
+ automation_failure_alert_email: rowText(
75
+ row,
76
+ "automation_failure_alert_email",
77
+ ),
78
+ last_automation_failure_alert_key: rowText(
79
+ row,
80
+ "last_automation_failure_alert_key",
81
+ ),
82
+ last_automation_failure_alert_at: rowText(
83
+ row,
84
+ "last_automation_failure_alert_at",
85
+ ),
86
+ owner_email: rowText(row, "owner_email"),
87
+ created_at: rowText(row, "created_at"),
88
+ updated_at: rowText(row, "updated_at"),
89
+ };
90
+ }
91
+
92
+ function asText(value: unknown): string | null {
93
+ if (typeof value === "string") {
94
+ const text = value.trim();
95
+ return text.length > 0 ? text : null;
96
+ }
97
+ if (typeof value === "number" && Number.isFinite(value)) return String(value);
98
+ return null;
99
+ }
100
+
101
+ function asFlag(value: unknown): number {
102
+ return Number(value) === 1 ? 1 : 0;
103
+ }
104
+
105
+ function pickText(
106
+ keep: string | null | undefined,
107
+ other: string | null | undefined,
108
+ ): string | null {
109
+ return asText(keep) ?? asText(other);
110
+ }
111
+
112
+ function pickLater(
113
+ keep: string | null | undefined,
114
+ other: string | null | undefined,
115
+ ): string | null {
116
+ const left = asText(keep);
117
+ const right = asText(other);
118
+ if (!left) return right;
119
+ if (!right) return left;
120
+ const leftNum = Number(left);
121
+ const rightNum = Number(right);
122
+ if (Number.isFinite(leftNum) && Number.isFinite(rightNum)) {
123
+ return leftNum >= rightNum ? left : right;
124
+ }
125
+ return left >= right ? left : right;
126
+ }
127
+
128
+ export function isUniqueConstraintError(error: unknown): boolean {
129
+ const message = error instanceof Error ? error.message : String(error);
130
+ return /unique constraint failed|duplicate key|primary key/i.test(message);
131
+ }
132
+
133
+ export function mergeFactoryConfigRows(
134
+ keep: FactoryConfigSqlRow,
135
+ other: FactoryConfigSqlRow,
136
+ ): FactoryConfigSqlRow {
137
+ const slackIdentity = mergeSlackIdentity(keep, other);
138
+ return {
139
+ ...keep,
140
+ slack_workspace: slackIdentity.slack_workspace,
141
+ slack_channel_id: slackIdentity.slack_channel_id,
142
+ slack_channel_name: slackIdentity.slack_channel_name,
143
+ builder_slack_user_id: pickText(
144
+ keep.builder_slack_user_id,
145
+ other.builder_slack_user_id,
146
+ ),
147
+ polling_enabled: Math.max(
148
+ asFlag(keep.polling_enabled),
149
+ asFlag(other.polling_enabled),
150
+ ),
151
+ last_slack_ts: slackIdentity.last_slack_ts,
152
+ slack_history_cursor: slackIdentity.slack_history_cursor,
153
+ repository: pickText(keep.repository, other.repository),
154
+ github_polling_enabled: Math.max(
155
+ asFlag(keep.github_polling_enabled),
156
+ asFlag(other.github_polling_enabled),
157
+ ),
158
+ sentry_polling_enabled: Math.max(
159
+ asFlag(keep.sentry_polling_enabled),
160
+ asFlag(other.sentry_polling_enabled),
161
+ ),
162
+ sentry_org_slug: pickText(keep.sentry_org_slug, other.sentry_org_slug),
163
+ sentry_project_slug: pickText(
164
+ keep.sentry_project_slug,
165
+ other.sentry_project_slug,
166
+ ),
167
+ sentry_environment: pickText(
168
+ keep.sentry_environment,
169
+ other.sentry_environment,
170
+ ),
171
+ last_sentry_seen_at: pickLater(
172
+ keep.last_sentry_seen_at,
173
+ other.last_sentry_seen_at,
174
+ ),
175
+ automation_failure_alerts_enabled: Math.max(
176
+ asFlag(keep.automation_failure_alerts_enabled),
177
+ asFlag(other.automation_failure_alerts_enabled),
178
+ ),
179
+ automation_failure_alert_email: pickText(
180
+ keep.automation_failure_alert_email,
181
+ other.automation_failure_alert_email,
182
+ ),
183
+ last_automation_failure_alert_key: pickText(
184
+ keep.last_automation_failure_alert_key,
185
+ other.last_automation_failure_alert_key,
186
+ ),
187
+ last_automation_failure_alert_at: pickLater(
188
+ keep.last_automation_failure_alert_at,
189
+ other.last_automation_failure_alert_at,
190
+ ),
191
+ owner_email: pickText(keep.owner_email, other.owner_email),
192
+ created_at: pickText(keep.created_at, other.created_at) ?? keep.created_at,
193
+ updated_at: pickLater(keep.updated_at, other.updated_at),
194
+ org_id: keep.org_id || other.org_id,
195
+ };
196
+ }
197
+
198
+ export function planDefaultFactoryConfigReconciliation(
199
+ rows: FactoryConfigSqlRow[],
200
+ defaultFactoryId = DEFAULT_FACTORY_ID,
201
+ ): FactoryConfigReconcilePlan[] {
202
+ const byOrg = new Map<string, FactoryConfigSqlRow[]>();
203
+ for (const row of rows) {
204
+ const orgId = asText(row.org_id);
205
+ const id = asText(row.id);
206
+ if (!orgId || !id) continue;
207
+ const group = byOrg.get(orgId) ?? [];
208
+ group.push({ ...row, id, org_id: orgId });
209
+ byOrg.set(orgId, group);
210
+ }
211
+
212
+ const plans: FactoryConfigReconcilePlan[] = [];
213
+ for (const [orgId, group] of byOrg) {
214
+ const canonicalId = factoryConfigRowId(orgId, defaultFactoryId);
215
+ const keepSource = group.find((row) => row.id === canonicalId) ?? group[0]!;
216
+ let merged: FactoryConfigSqlRow = {
217
+ ...keepSource,
218
+ id: canonicalId,
219
+ org_id: orgId,
220
+ factory_id: defaultFactoryId,
221
+ };
222
+ const deleteIds: string[] = [];
223
+ for (const row of group) {
224
+ if (row.id === keepSource.id) continue;
225
+ merged = mergeFactoryConfigRows(merged, row);
226
+ deleteIds.push(row.id);
227
+ }
228
+ plans.push({
229
+ fromId: keepSource.id,
230
+ row: merged,
231
+ deleteIds,
232
+ });
233
+ }
234
+ return plans;
235
+ }
236
+
237
+ export type SlackChannelConflictClear = {
238
+ id: string;
239
+ org_id: string;
240
+ };
241
+
242
+ function mergeSlackIdentity(
243
+ keep: FactoryConfigSqlRow,
244
+ other: FactoryConfigSqlRow,
245
+ ): Pick<
246
+ FactoryConfigSqlRow,
247
+ | "slack_workspace"
248
+ | "slack_channel_id"
249
+ | "slack_channel_name"
250
+ | "last_slack_ts"
251
+ | "slack_history_cursor"
252
+ > {
253
+ const keepChannel = asText(keep.slack_channel_id);
254
+ const otherChannel = asText(other.slack_channel_id);
255
+ if (!otherChannel || keepChannel === otherChannel) {
256
+ return {
257
+ slack_workspace: pickText(keep.slack_workspace, other.slack_workspace),
258
+ slack_channel_id: keepChannel ?? otherChannel,
259
+ slack_channel_name: pickText(
260
+ keep.slack_channel_name,
261
+ other.slack_channel_name,
262
+ ),
263
+ last_slack_ts: pickLater(keep.last_slack_ts, other.last_slack_ts),
264
+ slack_history_cursor: pickText(
265
+ keep.slack_history_cursor,
266
+ other.slack_history_cursor,
267
+ ),
268
+ };
269
+ }
270
+ if (!keepChannel) {
271
+ return {
272
+ slack_workspace: other.slack_workspace,
273
+ slack_channel_id: other.slack_channel_id,
274
+ slack_channel_name: other.slack_channel_name,
275
+ last_slack_ts: other.last_slack_ts,
276
+ slack_history_cursor: other.slack_history_cursor,
277
+ };
278
+ }
279
+ return {
280
+ slack_workspace: keep.slack_workspace,
281
+ slack_channel_id: keep.slack_channel_id,
282
+ slack_channel_name: keep.slack_channel_name,
283
+ last_slack_ts: keep.last_slack_ts,
284
+ slack_history_cursor: keep.slack_history_cursor,
285
+ };
286
+ }
287
+
288
+ export function planSlackChannelConflictClears(
289
+ rows: FactoryConfigSqlRow[],
290
+ ): SlackChannelConflictClear[] {
291
+ const groups = new Map<string, FactoryConfigSqlRow[]>();
292
+ for (const row of rows) {
293
+ const orgId = asText(row.org_id);
294
+ const channelId = asText(row.slack_channel_id);
295
+ const id = asText(row.id);
296
+ if (!orgId || !channelId || !id) continue;
297
+ const key = `${orgId}\0${channelId}`;
298
+ const group = groups.get(key) ?? [];
299
+ group.push({ ...row, id, org_id: orgId });
300
+ groups.set(key, group);
301
+ }
302
+
303
+ const clears: SlackChannelConflictClear[] = [];
304
+ for (const group of groups.values()) {
305
+ if (group.length < 2) continue;
306
+ const ranked = [...group].sort((left, right) => {
307
+ const polling =
308
+ asFlag(right.polling_enabled) - asFlag(left.polling_enabled);
309
+ if (polling !== 0) return polling;
310
+ const updated = (right.updated_at ?? "").localeCompare(
311
+ left.updated_at ?? "",
312
+ );
313
+ if (updated !== 0) return updated;
314
+ return left.id.localeCompare(right.id);
315
+ });
316
+ for (const row of ranked.slice(1)) {
317
+ clears.push({ id: row.id, org_id: row.org_id! });
318
+ }
319
+ }
320
+ return clears;
321
+ }
@@ -23,6 +23,14 @@ export const factoryIdSchema = z
23
23
  .max(120)
24
24
  .regex(/^[a-z0-9][a-z0-9-]*$/);
25
25
 
26
+ export const builderSlackUserIdSchema = z
27
+ .string()
28
+ .trim()
29
+ .max(32)
30
+ .refine((value) => value === "" || /^[UW][A-Z0-9]+$/i.test(value), {
31
+ message: "Builder Slack member id must look like U01234567.",
32
+ });
33
+
26
34
  export function factoryConfigRowId(orgId: string, factoryId: string): string {
27
35
  return `${orgId}:${factoryId}`;
28
36
  }
@@ -222,7 +230,13 @@ function readFrontmatterFactoryId(content: string): string | undefined {
222
230
  return value.replace(/^(\"|')|((\"|')$)/g, "");
223
231
  }
224
232
 
225
- export function readAutomationFactoryId(meta: object, content: string): string {
233
+ export function readAutomationFactoryId(
234
+ meta: object,
235
+ content: string,
236
+ path: string,
237
+ ): string {
238
+ const fromPath = readFactoryIdFromAutomationPath(path);
239
+ if (fromPath) return fromPath;
226
240
  const fromContent = readFrontmatterFactoryId(content);
227
241
  return resolveAutomationFactoryId(
228
242
  fromContent ? { ...meta, factoryId: fromContent } : meta,
@@ -247,6 +261,15 @@ export function isLegacyFactoryAutomationPath(path: string): boolean {
247
261
  return /^jobs\/factory-[^/]+\.md$/.test(path);
248
262
  }
249
263
 
264
+ /** Scheduler trigger names keep the nested path; role allowlists use the leaf. */
265
+ export function factoryAutomationLeafName(nameOrPath: string): string {
266
+ const withoutJobsPrefix = nameOrPath
267
+ .replace(/^jobs\//, "")
268
+ .replace(/\.md$/, "");
269
+ const slash = withoutJobsPrefix.lastIndexOf("/");
270
+ return slash === -1 ? withoutJobsPrefix : withoutJobsPrefix.slice(slash + 1);
271
+ }
272
+
250
273
  export function readFactoryIdFromAutomationPath(path: string): string | null {
251
274
  const match = path.match(/^jobs\/factories\/([^/]+)\/factory-[^/]+\.md$/);
252
275
  return match?.[1] ?? null;
@@ -294,7 +317,7 @@ export async function assertUniqueSlackChannelForFactory(
294
317
  eq(triageConfig.slackChannelId, normalized),
295
318
  ),
296
319
  )
297
- ).find((row) => row.factoryId && row.factoryId !== factoryId);
320
+ ).find((row) => row.factoryId !== factoryId);
298
321
  if (conflict) {
299
322
  throw new Error(
300
323
  "That Slack channel is already used by another Factory in this workspace.",
@@ -1,7 +1,10 @@
1
1
  import type { ActionRunContext } from "@agent-native/core/action";
2
2
  import { listAutomationDefinitions } from "@agent-native/core/triggers";
3
3
 
4
- import { readAutomationFactoryId } from "./factory-scope.js";
4
+ import {
5
+ factoryAutomationLeafName,
6
+ readAutomationFactoryId,
7
+ } from "./factory-scope.js";
5
8
  import type { WorkspaceMemberIdentity } from "./require-workspace-member.js";
6
9
 
7
10
  const FACTORY_AUTOMATION_NAMES = {
@@ -39,7 +42,10 @@ export async function requireFactoryAutomation(
39
42
  }
40
43
  const lineage = context.automation;
41
44
  const expectedNames = FACTORY_AUTOMATION_NAMES[role];
42
- if (!lineage || !expectedNames.has(lineage.triggerName)) {
45
+ if (
46
+ !lineage ||
47
+ !expectedNames.has(factoryAutomationLeafName(lineage.triggerName))
48
+ ) {
43
49
  throw new Error(
44
50
  "The action was not invoked by a governed Factory automation.",
45
51
  );
@@ -71,8 +77,11 @@ export async function requireFactoryAutomation(
71
77
  }
72
78
  if (
73
79
  expectedFactoryId &&
74
- readAutomationFactoryId(definition.meta, definition.resource.content) !==
75
- expectedFactoryId
80
+ readAutomationFactoryId(
81
+ definition.meta,
82
+ definition.resource.content,
83
+ definition.resource.path,
84
+ ) !== expectedFactoryId
76
85
  ) {
77
86
  throw new Error(
78
87
  "The action was not invoked by a governed Factory automation.",
@@ -1,6 +1,128 @@
1
- import { runMigrations } from "@agent-native/core/db";
1
+ import { runMigrations, type DbExec } from "@agent-native/core/db";
2
2
  import { defineNitroPlugin } from "@agent-native/core/server";
3
3
 
4
+ import {
5
+ factoryConfigSqlRowFromQuery,
6
+ isUniqueConstraintError,
7
+ planDefaultFactoryConfigReconciliation,
8
+ planSlackChannelConflictClears,
9
+ type FactoryConfigSqlRow,
10
+ } from "../lib/factory-config-reconcile.js";
11
+
12
+ async function writeReconciledFactoryConfig(
13
+ exec: DbExec,
14
+ plan: {
15
+ fromId: string;
16
+ row: FactoryConfigSqlRow;
17
+ deleteIds: string[];
18
+ },
19
+ ): Promise<void> {
20
+ const { row } = plan;
21
+ await exec.execute({
22
+ sql: `UPDATE factory_config SET
23
+ id = ?,
24
+ factory_id = ?,
25
+ slack_workspace = ?,
26
+ slack_channel_id = ?,
27
+ slack_channel_name = ?,
28
+ builder_slack_user_id = ?,
29
+ polling_enabled = ?,
30
+ last_slack_ts = ?,
31
+ slack_history_cursor = ?,
32
+ repository = ?,
33
+ github_polling_enabled = ?,
34
+ sentry_polling_enabled = ?,
35
+ sentry_org_slug = ?,
36
+ sentry_project_slug = ?,
37
+ sentry_environment = ?,
38
+ last_sentry_seen_at = ?,
39
+ automation_failure_alerts_enabled = ?,
40
+ automation_failure_alert_email = ?,
41
+ last_automation_failure_alert_key = ?,
42
+ last_automation_failure_alert_at = ?,
43
+ owner_email = ?,
44
+ created_at = ?,
45
+ updated_at = ?
46
+ WHERE id = ? AND org_id = ?`,
47
+ args: [
48
+ row.id,
49
+ row.factory_id,
50
+ row.slack_workspace ?? "primary",
51
+ row.slack_channel_id,
52
+ row.slack_channel_name,
53
+ row.builder_slack_user_id,
54
+ row.polling_enabled ?? 0,
55
+ row.last_slack_ts,
56
+ row.slack_history_cursor,
57
+ row.repository,
58
+ row.github_polling_enabled ?? 0,
59
+ row.sentry_polling_enabled ?? 0,
60
+ row.sentry_org_slug,
61
+ row.sentry_project_slug,
62
+ row.sentry_environment,
63
+ row.last_sentry_seen_at,
64
+ row.automation_failure_alerts_enabled ?? 1,
65
+ row.automation_failure_alert_email,
66
+ row.last_automation_failure_alert_key,
67
+ row.last_automation_failure_alert_at,
68
+ row.owner_email ?? "",
69
+ row.created_at ?? new Date().toISOString(),
70
+ row.updated_at ?? new Date().toISOString(),
71
+ plan.fromId,
72
+ row.org_id,
73
+ ],
74
+ });
75
+ for (const deleteId of plan.deleteIds) {
76
+ await exec.execute({
77
+ sql: `DELETE FROM factory_config WHERE id = ? AND org_id = ?`,
78
+ args: [deleteId, row.org_id],
79
+ });
80
+ }
81
+ }
82
+
83
+ async function reconcileDefaultFactoryConfigRows(): Promise<void> {
84
+ const { getDbExec } = await import("@agent-native/core/db");
85
+ const exec = getDbExec();
86
+ const defaultFactoryId = "product-feedback";
87
+ const configRows = await exec.execute({
88
+ sql: `SELECT * FROM factory_config
89
+ WHERE factory_id IS NULL OR factory_id = '' OR factory_id = ?`,
90
+ args: [defaultFactoryId],
91
+ });
92
+ const rows = (configRows.rows ?? [])
93
+ .map((row) => factoryConfigSqlRowFromQuery(row as Record<string, unknown>))
94
+ .filter((row): row is FactoryConfigSqlRow => row !== null);
95
+ for (const plan of planDefaultFactoryConfigReconciliation(
96
+ rows,
97
+ defaultFactoryId,
98
+ )) {
99
+ await writeReconciledFactoryConfig(exec, plan);
100
+ }
101
+ }
102
+
103
+ async function clearDuplicateSlackChannelAssignments(): Promise<void> {
104
+ const { getDbExec } = await import("@agent-native/core/db");
105
+ const exec = getDbExec();
106
+ const configRows = await exec.execute({
107
+ sql: `SELECT * FROM factory_config`,
108
+ args: [],
109
+ });
110
+ const rows = (configRows.rows ?? [])
111
+ .map((row) => factoryConfigSqlRowFromQuery(row as Record<string, unknown>))
112
+ .filter((row): row is FactoryConfigSqlRow => row !== null);
113
+ for (const clear of planSlackChannelConflictClears(rows)) {
114
+ await exec.execute({
115
+ sql: `UPDATE factory_config SET
116
+ slack_channel_id = NULL,
117
+ slack_channel_name = NULL,
118
+ last_slack_ts = NULL,
119
+ slack_history_cursor = NULL
120
+ WHERE id = ? AND org_id = ?`,
121
+ args: [clear.id, clear.org_id],
122
+ });
123
+ }
124
+ }
125
+
4
126
  const migrations = [
5
127
  {
6
128
  version: 1,
@@ -339,10 +461,20 @@ const migrations = [
339
461
  const id = String(row.id ?? "");
340
462
  if (!orgId || id.includes(":")) continue;
341
463
  const nextId = `${orgId}:${defaultFactoryId}`;
342
- await exec.execute({
343
- sql: `UPDATE factory_config SET id = ? WHERE id = ? AND org_id = ?`,
344
- args: [nextId, id, orgId],
464
+ const existing = await exec.execute({
465
+ sql: `SELECT id FROM factory_config WHERE id = ? AND org_id = ?`,
466
+ args: [nextId, orgId],
345
467
  });
468
+ if ((existing.rows?.length ?? 0) > 0) continue;
469
+ try {
470
+ await exec.execute({
471
+ sql: `UPDATE factory_config SET id = ? WHERE id = ? AND org_id = ?`,
472
+ args: [nextId, id, orgId],
473
+ });
474
+ } catch (error) {
475
+ if (isUniqueConstraintError(error)) continue;
476
+ throw error;
477
+ }
346
478
  }
347
479
  },
348
480
  },
@@ -368,12 +500,24 @@ const migrations = [
368
500
  {
369
501
  version: 24,
370
502
  name: "factory-config-org-slack-channel-unique",
503
+ run: async () => {
504
+ await reconcileDefaultFactoryConfigRows();
505
+ await clearDuplicateSlackChannelAssignments();
506
+ },
371
507
  sql: `
372
508
  CREATE UNIQUE INDEX IF NOT EXISTS factory_config_org_slack_channel_idx
373
509
  ON factory_config (org_id, slack_channel_id)
374
510
  WHERE slack_channel_id IS NOT NULL AND slack_channel_id != '';
375
511
  `,
376
512
  },
513
+ {
514
+ version: 25,
515
+ name: "factory-config-reconcile-legacy-rows",
516
+ sql: {},
517
+ run: async () => {
518
+ await reconcileDefaultFactoryConfigRows();
519
+ },
520
+ },
377
521
  ];
378
522
 
379
523
  export const runFactoryMigrations = runMigrations(migrations, {
@@ -39,8 +39,6 @@ import {
39
39
  } from "../triage/review-skill-alignment.js";
40
40
 
41
41
  const LEGACY_JOB_PATH = "jobs/factory-observation-scheduler.md";
42
- const DEFAULT_SLACK_CHANNEL_ID = "C0ATH3CCZT4";
43
- const DEFAULT_SLACK_CHANNEL_NAME = "product-agent-native-feedback";
44
42
  const FAILURE_ALERT_COOLDOWN_MS = 15 * 60_000;
45
43
 
46
44
  type AutomationRunFinishedEvent = {
@@ -627,6 +625,7 @@ export async function syncFactoryAutomationEnabledStates(
627
625
  readAutomationFactoryId(
628
626
  definition.meta,
629
627
  definition.resource.content,
628
+ definition.resource.path,
630
629
  ) === factoryId,
631
630
  )
632
631
  .map(async (definition) => {
@@ -687,16 +686,15 @@ async function ensureDefaultTriageConfig(
687
686
  }
688
687
  const now = new Date().toISOString();
689
688
  const repository = defaultRepository();
690
- const pollingEnabled = 1;
691
689
  const githubPollingEnabled = defaultGithubPollingEnabled() ? 1 : 0;
692
690
  await db.insert(triageConfig).values({
693
691
  id: factoryConfigRowId(orgId, factoryId),
694
692
  factoryId,
695
693
  slackWorkspace: "primary",
696
- slackChannelId: DEFAULT_SLACK_CHANNEL_ID,
697
- slackChannelName: DEFAULT_SLACK_CHANNEL_NAME,
694
+ slackChannelId: null,
695
+ slackChannelName: null,
698
696
  builderSlackUserId: null,
699
- pollingEnabled,
697
+ pollingEnabled: 0,
700
698
  githubPollingEnabled,
701
699
  sentryPollingEnabled: 0,
702
700
  lastSlackTs: null,
@@ -1,9 +1,24 @@
1
1
  import baseConfig from "@agent-native/core/vitest-config";
2
- import { mergeConfig } from "vitest/config";
2
+ import { defineConfig, mergeConfig } from "vitest/config";
3
3
 
4
4
  import viteConfig from "./vite.config";
5
5
 
6
6
  // Kept separate from vite.config.ts so the production config never imports
7
7
  // vitest: `agent-native build` loads vite.config.ts in installs where vitest
8
8
  // is absent.
9
- export default mergeConfig(viteConfig, baseConfig);
9
+ export default mergeConfig(
10
+ viteConfig,
11
+ mergeConfig(
12
+ baseConfig,
13
+ defineConfig({
14
+ test: {
15
+ exclude: [
16
+ "**/node_modules/**",
17
+ "**/.git/**",
18
+ "**/dist/**",
19
+ "**/.react-router/**",
20
+ ],
21
+ },
22
+ }),
23
+ ),
24
+ );
@@ -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
 
@@ -22,7 +22,10 @@ export function meta() {
22
22
 
23
23
  export default function SettingsRoute() {
24
24
  const t = useT();
25
- const agentSettingsTabs = useAgentSettingsTabs();
25
+ // /extensions redirects here and the agent's own navigate instructions list
26
+ // "extensions" as a workspace view, so the settings tab must exist too —
27
+ // otherwise /settings/extensions silently falls back to General.
28
+ const agentSettingsTabs = useAgentSettingsTabs({ extensionTools: true });
26
29
  useSetPageTitle(t("settings.title"));
27
30
 
28
31
  const generalSearchEntries = useMemo<SettingsSearchEntry[]>(
@@ -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.