@agent-native/core 0.169.1 → 0.171.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (463) hide show
  1. package/agent-native.eject.json +4 -1
  2. package/bin/agent-native.js +23 -5
  3. package/corpus/README.md +1 -1
  4. package/corpus/templates/analytics/.agents/skills/secrets/SKILL.md +11 -4
  5. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
  6. package/corpus/templates/analytics/app/i18n-data.ts +28 -0
  7. package/corpus/templates/analytics/app/pages/Agents.tsx +25 -1
  8. package/corpus/templates/analytics/app/routes/$.tsx +10 -0
  9. package/corpus/templates/analytics/app/routes.ts +3 -1
  10. package/corpus/templates/analytics/changelog/2026-08-22-fixed-the-agent-native-templates-first-party-dashboard-panel.md +6 -0
  11. package/corpus/templates/analytics/changelog/2026-08-22-org-admin-panels-no-longer-report-a-database-error-as-a-per.md +9 -0
  12. package/corpus/templates/analytics/changelog/2026-08-22-slack-requests-report-invalid-inputs.md +6 -0
  13. package/corpus/templates/analytics/changelog/2026-08-22-unknown-analytics-links-now-return-a-real-not-found-status.md +6 -0
  14. package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +2 -1
  15. package/corpus/templates/analytics/docs/schemas/tracked-events.md +2 -0
  16. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +29 -7
  17. package/corpus/templates/analytics/server/handlers/slack.ts +59 -5
  18. package/corpus/templates/analytics/server/lib/db-admin-connections.ts +11 -6
  19. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +132 -2
  20. package/corpus/templates/analytics/server/lib/slack.ts +4 -2
  21. package/corpus/templates/analytics/vitest.config.ts +6 -1
  22. package/corpus/templates/assets/.agents/skills/secrets/SKILL.md +11 -4
  23. package/corpus/templates/assets/app/routes/library.tsx +5 -3
  24. package/corpus/templates/assets/app/routes.ts +3 -1
  25. package/corpus/templates/assets/changelog/2026-08-22-library-page-loads-again-instead-of-crashing-on-open.md +6 -0
  26. package/corpus/templates/assets/vitest.config.ts +6 -1
  27. package/corpus/templates/brain/.agents/skills/secrets/SKILL.md +11 -4
  28. package/corpus/templates/brain/app/components/ui/pointer-lock.ts +1 -0
  29. package/corpus/templates/brain/app/routes/sources.tsx +8 -3
  30. package/corpus/templates/brain/changelog/2026-08-22-tuning-or-adding-a-source-no-longer-leaves-the-sources-page.md +6 -0
  31. package/corpus/templates/brain/vitest.config.ts +1 -0
  32. package/corpus/templates/calendar/.agents/skills/secrets/SKILL.md +11 -4
  33. package/corpus/templates/calendar/actions/list-booking-links.ts +83 -4
  34. package/corpus/templates/calendar/actions/update-booking-link.ts +4 -3
  35. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +11 -3
  36. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +1 -1
  37. package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +103 -44
  38. package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +28 -24
  39. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +9 -2
  40. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +47 -58
  41. package/corpus/templates/calendar/app/hooks/use-booking-links.ts +5 -1
  42. package/corpus/templates/calendar/app/lib/event-layout.ts +52 -26
  43. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +126 -98
  44. package/corpus/templates/calendar/app/pages/CalendarView.tsx +1 -0
  45. package/corpus/templates/calendar/app/pages/Settings.tsx +88 -81
  46. package/corpus/templates/calendar/app/routes.ts +3 -1
  47. package/corpus/templates/calendar/changelog/2026-08-22-booking-link-visibility-respects-shared-access.md +6 -0
  48. package/corpus/templates/calendar/changelog/2026-08-22-clicking-an-out-of-office-event-s-marker-no-longer-creates-a.md +6 -0
  49. package/corpus/templates/calendar/changelog/2026-08-22-event-detail-views-now-show-your-calendar-timezone.md +6 -0
  50. package/corpus/templates/calendar/changelog/2026-08-22-google-calendar-can-now-connect-with-the-shared-google-oauth.md +6 -0
  51. package/corpus/templates/calendar/changelog/2026-08-22-overlapping-events-now-show-each-others-boundaries.md +6 -0
  52. package/corpus/templates/calendar/changelog/2026-08-22-public-booking-pages-now-offer-time-slots-from-the-host-s-we.md +6 -0
  53. package/corpus/templates/calendar/server/handlers/bookings.ts +5 -7
  54. package/corpus/templates/calendar/server/handlers/google-auth.ts +10 -2
  55. package/corpus/templates/calendar/server/lib/google-calendar.ts +139 -14
  56. package/corpus/templates/calendar/server/onboarding.ts +85 -0
  57. package/corpus/templates/calendar/server/plugins/agent-chat.ts +2 -0
  58. package/corpus/templates/calendar/shared/api.ts +9 -1
  59. package/corpus/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
  60. package/corpus/templates/chat/app/routes.ts +3 -1
  61. package/corpus/templates/chat/vitest.config.ts +8 -0
  62. package/corpus/templates/clips/.agents/skills/secrets/SKILL.md +11 -4
  63. package/corpus/templates/clips/actions/get-meeting.ts +9 -4
  64. package/corpus/templates/clips/actions/list-recordings.ts +16 -3
  65. package/corpus/templates/clips/actions/search-meetings.ts +1 -0
  66. package/corpus/templates/clips/actions/view-screen.ts +8 -1
  67. package/corpus/templates/clips/app/components/meetings/meeting-history-row.tsx +32 -1
  68. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +1 -1
  69. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +29 -1
  70. package/corpus/templates/clips/app/i18n/en-US.ts +5 -0
  71. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +33 -4
  72. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +1 -0
  73. package/corpus/templates/clips/app/routes/access-request.approve.tsx +16 -9
  74. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +17 -7
  75. package/corpus/templates/clips/app/routes/share.$shareId.tsx +17 -7
  76. package/corpus/templates/clips/changelog/2026-08-21-camera-bubble-black-circle.md +6 -0
  77. package/corpus/templates/clips/changelog/2026-08-22-screen-recordings-respect-the-microphone-toggle.md +6 -0
  78. package/corpus/templates/clips/changelog/2026-08-22-the-recordings-library-no-longer-fails-right-after-server-start.md +6 -0
  79. package/corpus/templates/clips/changelog/2026-08-23-the-agent-can-see-your-library-right-after-server-start.md +6 -0
  80. package/corpus/templates/clips/chrome-extension/src/background.ts +1 -2
  81. package/corpus/templates/clips/chrome-extension/src/native-transcription.ts +39 -7
  82. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +21 -25
  83. package/corpus/templates/clips/desktop/src/app.tsx +53 -24
  84. package/corpus/templates/clips/desktop/src/lib/bubble-playback.ts +68 -0
  85. package/corpus/templates/clips/desktop/src/lib/recorder.ts +3 -0
  86. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +72 -14
  87. package/corpus/templates/clips/desktop/src/lib/updater.ts +8 -3
  88. package/corpus/templates/clips/desktop/src/overlays/bubble.tsx +162 -20
  89. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +43 -6
  90. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +150 -13
  91. package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +12 -0
  92. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +28 -3
  93. package/corpus/templates/clips/server/lib/emails.ts +17 -33
  94. package/corpus/templates/content/.agents/skills/secrets/SKILL.md +11 -4
  95. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +140 -15
  96. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +12 -14
  97. package/corpus/templates/content/app/components/ui/pointer-lock.ts +1 -0
  98. package/corpus/templates/content/app/hooks/use-content-database.ts +18 -2
  99. package/corpus/templates/content/app/hooks/use-local-storage.ts +38 -22
  100. package/corpus/templates/content/app/i18n-data.ts +25 -0
  101. package/corpus/templates/content/app/routes.ts +3 -1
  102. package/corpus/templates/content/changelog/2026-08-22-code-blocks-inserted-from-the-slash-menu-no-longer-get-stuck.md +6 -0
  103. package/corpus/templates/content/changelog/2026-08-22-the-sidebar-page-tree-no-longer-flashes-empty-after-creating-a-page.md +6 -0
  104. package/corpus/templates/content/docs/solutions/2026-08-21-content-mermaid-rendering-shape.md +233 -0
  105. package/corpus/templates/content/server/plugins/agent-chat.ts +9 -2
  106. package/corpus/templates/content/vitest.config.ts +7 -1
  107. package/corpus/templates/crm/.agents/skills/secrets/SKILL.md +11 -4
  108. package/corpus/templates/design/.agents/skills/secrets/SKILL.md +11 -4
  109. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +1383 -88
  110. package/corpus/templates/design/actions/generate-design.ts +98 -47
  111. package/corpus/templates/design/actions/get-design-system.ts +6 -1
  112. package/corpus/templates/design/actions/refresh-design-system-with-builder.ts +146 -0
  113. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +70 -26
  114. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +284 -136
  115. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2057 -141
  116. package/corpus/templates/design/app/components/design/editor/DesignBottomToolbar.tsx +6 -2
  117. package/corpus/templates/design/app/components/design/editor/DesignWorkspaceRail.tsx +4 -4
  118. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +10 -1
  119. package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +19 -2
  120. package/corpus/templates/design/app/components/design/multi-screen/types.ts +9 -2
  121. package/corpus/templates/design/app/components/layout/Header.tsx +15 -0
  122. package/corpus/templates/design/app/components/layout/Layout.tsx +7 -2
  123. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +11 -4
  124. package/corpus/templates/design/app/i18n-data.ts +1 -0
  125. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +12 -0
  126. package/corpus/templates/design/app/lib/design-system-data.ts +68 -0
  127. package/corpus/templates/design/app/pages/DesignEditor.tsx +77 -35
  128. package/corpus/templates/design/app/pages/DesignSystems.tsx +136 -42
  129. package/corpus/templates/design/app/pages/Index.tsx +2 -0
  130. package/corpus/templates/design/app/pages/Templates.tsx +0 -10
  131. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +10 -0
  132. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +50 -0
  133. package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +1 -1
  134. package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +10 -8
  135. package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +6 -6
  136. package/corpus/templates/design/app/pages/design-editor/commands/apply-pending-visual-styles-with-agent.ts +1 -1
  137. package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +18 -7
  138. package/corpus/templates/design/app/pages/design-editor/commands/escape-hotkey.ts +4 -90
  139. package/corpus/templates/design/app/pages/design-editor/commands/layer-marquee-selection-change.ts +2 -1
  140. package/corpus/templates/design/app/pages/design-editor/commands/layer-selection-change.ts +4 -3
  141. package/corpus/templates/design/app/pages/design-editor/commands/screen-element-select.ts +12 -2
  142. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-style-change.ts +9 -2
  143. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-move-semantic-handoff.ts +1 -1
  144. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-semantic-handoff.ts +1 -1
  145. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-state-semantic-handoff.ts +1 -1
  146. package/corpus/templates/design/app/pages/design-editor/commands/start-sidebar-resize.ts +1 -1
  147. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +11 -1
  148. package/corpus/templates/design/app/pages/design-editor/effects/adopt-db-file-content.ts +11 -19
  149. package/corpus/templates/design/app/pages/design-editor/effects/observe-collab-text.ts +6 -6
  150. package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +1 -1
  151. package/corpus/templates/design/app/pages/design-editor/effects/seed-collab-content.ts +12 -15
  152. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +5 -59
  153. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +8 -0
  154. package/corpus/templates/design/app/pages/design-editor/types.ts +3 -3
  155. package/corpus/templates/design/app/routes.ts +3 -1
  156. package/corpus/templates/design/changelog/2026-08-20-canvas-edits-patch-the-frame-instead-of-rebuilding-it.md +6 -0
  157. package/corpus/templates/design/changelog/2026-08-21-builder-design-system-imports-reconcile-after-indexing.md +6 -0
  158. package/corpus/templates/design/changelog/2026-08-21-draw-mode-needs-shift-y.md +6 -0
  159. package/corpus/templates/design/changelog/2026-08-21-eyedropper-shortcut.md +6 -0
  160. package/corpus/templates/design/changelog/2026-08-21-figma-style-alignment-guides-and-snapping.md +6 -0
  161. package/corpus/templates/design/changelog/2026-08-21-group-transforms-keep-the-selection.md +6 -0
  162. package/corpus/templates/design/changelog/2026-08-21-inspector-fields-keep-focus-on-enter.md +6 -0
  163. package/corpus/templates/design/changelog/2026-08-21-pen-alt-keeps-the-incoming-handle.md +6 -0
  164. package/corpus/templates/design/changelog/2026-08-21-pen-on-the-board-creates-one-vector.md +6 -0
  165. package/corpus/templates/design/changelog/2026-08-21-scale-a-multi-selection-from-the-group-handles.md +6 -0
  166. package/corpus/templates/design/changelog/2026-08-21-scale-resizes-text-with-the-object.md +6 -0
  167. package/corpus/templates/design/changelog/2026-08-21-selecting-another-object-keeps-the-scale-tool-armed.md +6 -0
  168. package/corpus/templates/design/changelog/2026-08-21-the-shape-tool-remembers-the-shape-you-picked.md +6 -0
  169. package/corpus/templates/design/changelog/2026-08-21-vectors-drawn-in-a-frame-land-where-you-drew-them.md +6 -0
  170. package/corpus/templates/design/changelog/2026-08-22-multi-screen-generation-no-longer-loses-a-saved-screens-layo.md +6 -0
  171. package/corpus/templates/design/changelog/2026-08-22-rename-and-search-fields-are-labeled-for-screen-readers.md +6 -0
  172. package/corpus/templates/design/package.json +2 -0
  173. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +311 -0
  174. package/corpus/templates/design/shared/canvas-math.ts +612 -206
  175. package/corpus/templates/design/shared/pen-path.ts +37 -0
  176. package/corpus/templates/design/vitest.config.ts +7 -1
  177. package/corpus/templates/dispatch/.agents/skills/secrets/SKILL.md +11 -4
  178. package/corpus/templates/dispatch/actions/list-workspace-connections.ts +34 -3
  179. package/corpus/templates/dispatch/app/routes/integrations.tsx +55 -21
  180. package/corpus/templates/dispatch/app/routes.ts +3 -1
  181. package/corpus/templates/factory/.agents/skills/secrets/SKILL.md +11 -4
  182. package/corpus/templates/factory/.env.example +2 -7
  183. package/corpus/templates/factory/AGENTS.md +3 -0
  184. package/corpus/templates/factory/README.md +5 -3
  185. package/corpus/templates/factory/actions/create-factory.ts +16 -3
  186. package/corpus/templates/factory/actions/get-slack-feedback-context.ts +2 -0
  187. package/corpus/templates/factory/actions/get-triage-item.ts +2 -0
  188. package/corpus/templates/factory/actions/list-factory-audit.ts +116 -36
  189. package/corpus/templates/factory/actions/list-factory-automations.ts +2 -1
  190. package/corpus/templates/factory/actions/list-triage-items.ts +22 -19
  191. package/corpus/templates/factory/actions/run-factory-automation.ts +7 -3
  192. package/corpus/templates/factory/actions/save-factory-automation.ts +5 -2
  193. package/corpus/templates/factory/actions/save-triage-config.ts +2 -8
  194. package/corpus/templates/factory/actions/start-builder-for-item.ts +21 -0
  195. package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +229 -297
  196. package/corpus/templates/factory/app/components/factory/FactorySettingsView.tsx +397 -247
  197. package/corpus/templates/factory/app/components/factory/FactorySourceSettingsGroup.tsx +33 -0
  198. package/corpus/templates/factory/app/components/factory/NewFactoryForm.tsx +202 -162
  199. package/corpus/templates/factory/app/i18n/en-US.ts +32 -4
  200. package/corpus/templates/factory/app/routes/factory.tsx +84 -11
  201. package/corpus/templates/factory/app/routes.ts +3 -1
  202. package/corpus/templates/factory/changelog/2026-08-21-activity-runs-use-automation-display-names.md +6 -0
  203. package/corpus/templates/factory/changelog/2026-08-21-create-and-settings-source-cards.md +6 -0
  204. package/corpus/templates/factory/changelog/2026-08-21-keep-automations-visible-while-running.md +6 -0
  205. package/corpus/templates/factory/changelog/2026-08-21-nested-factory-automations-can-poll.md +6 -0
  206. package/corpus/templates/factory/changelog/2026-08-21-settings-sticky-unsaved-bar.md +6 -0
  207. package/corpus/templates/factory/server/connectors/credentials.ts +10 -5
  208. package/corpus/templates/factory/server/lib/factory-audit-report.ts +412 -0
  209. package/corpus/templates/factory/server/lib/factory-config-reconcile.ts +321 -0
  210. package/corpus/templates/factory/server/lib/factory-scope.ts +25 -2
  211. package/corpus/templates/factory/server/lib/require-factory-automation.ts +13 -4
  212. package/corpus/templates/factory/server/plugins/factory-migrations.ts +148 -4
  213. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +4 -6
  214. package/corpus/templates/factory/vitest.config.ts +17 -2
  215. package/corpus/templates/forms/.agents/skills/secrets/SKILL.md +11 -4
  216. package/corpus/templates/forms/app/routes/_app.settings.tsx +4 -1
  217. package/corpus/templates/forms/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
  218. package/corpus/templates/forms/server/lib/public-form-ssr.ts +11 -1
  219. package/corpus/templates/forms/vitest.config.ts +1 -0
  220. package/corpus/templates/macros/.agents/skills/secrets/SKILL.md +11 -4
  221. package/corpus/templates/macros/app/routes.ts +3 -1
  222. package/corpus/templates/macros/vitest.config.ts +6 -1
  223. package/corpus/templates/mail/.agents/skills/secrets/SKILL.md +11 -4
  224. package/corpus/templates/mail/actions/manage-gmail-filters.ts +10 -2
  225. package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +100 -37
  226. package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +14 -2
  227. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +66 -36
  228. package/corpus/templates/mail/app/hooks/use-google-auth.ts +2 -0
  229. package/corpus/templates/mail/app/lib/google-oauth-setup.ts +17 -0
  230. package/corpus/templates/mail/app/pages/InboxPage.tsx +69 -9
  231. package/corpus/templates/mail/app/routes/$.tsx +9 -0
  232. package/corpus/templates/mail/app/routes/$view.$threadId.tsx +23 -1
  233. package/corpus/templates/mail/app/routes/$view.tsx +47 -1
  234. package/corpus/templates/mail/app/routes.ts +3 -1
  235. package/corpus/templates/mail/changelog/2026-08-22-gmail-can-now-connect-with-the-shared-google-oauth.md +6 -0
  236. package/corpus/templates/mail/changelog/2026-08-22-gmail-filters-now-shows-the-real-error.md +6 -0
  237. package/corpus/templates/mail/changelog/2026-08-22-unknown-mail-urls-now-show-the-404-page.md +6 -0
  238. package/corpus/templates/mail/server/handlers/google-auth.ts +11 -2
  239. package/corpus/templates/mail/server/lib/google-auth.ts +92 -12
  240. package/corpus/templates/mail/server/onboarding.ts +66 -18
  241. package/corpus/templates/mail/vitest.config.ts +6 -1
  242. package/corpus/templates/plan/.agents/skills/secrets/SKILL.md +11 -4
  243. package/corpus/templates/plan/app/routes/settings.tsx +4 -1
  244. package/corpus/templates/plan/app/routes.ts +3 -1
  245. package/corpus/templates/plan/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
  246. package/corpus/templates/plan/netlify.toml +1 -1
  247. package/corpus/templates/slides/.agents/skills/secrets/SKILL.md +11 -4
  248. package/corpus/templates/slides/actions/patch-deck.ts +79 -10
  249. package/corpus/templates/slides/actions/update-slide.ts +11 -1
  250. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +14 -4
  251. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +10 -52
  252. package/corpus/templates/slides/app/components/editor/GoogleDocImportHint.tsx +8 -53
  253. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +9 -75
  254. package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +8 -60
  255. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +10 -2
  256. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +19 -7
  257. package/corpus/templates/slides/app/lib/sanitize-slide-html.ts +49 -36
  258. package/corpus/templates/slides/app/routes/access-request.approve.tsx +16 -9
  259. package/corpus/templates/slides/app/routes.ts +3 -1
  260. package/corpus/templates/slides/changelog/2026-08-22-already-uploaded-chat-images-are-no-longer-dropped.md +6 -0
  261. package/corpus/templates/slides/changelog/2026-08-22-google-drive-and-slides-can-now-connect-with-the-shared-google-oauth.md +6 -0
  262. package/corpus/templates/slides/changelog/2026-08-22-multi-slide-repairs-now-report-slides-that-still-overflow.md +6 -0
  263. package/corpus/templates/slides/changelog/2026-08-22-update-slide-now-reports-which-batched-edit-was-skipped.md +6 -0
  264. package/corpus/templates/slides/server/handlers/google-docs-auth.ts +52 -1
  265. package/corpus/templates/slides/server/lib/chat-attachments.ts +29 -3
  266. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +66 -16
  267. package/corpus/templates/slides/server/lib/slide-content-patch.ts +13 -2
  268. package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -2
  269. package/corpus/templates/slides/tsconfig.json +5 -0
  270. package/corpus/templates/slides/vitest.config.ts +6 -1
  271. package/corpus/templates/tasks/.agents/skills/secrets/SKILL.md +11 -4
  272. package/corpus/templates/tasks/app/routes/settings.tsx +4 -1
  273. package/corpus/templates/tasks/app/routes.ts +3 -1
  274. package/corpus/templates/tasks/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
  275. package/corpus/templates/tasks/vitest.config.ts +7 -1
  276. package/dist/action.d.ts +91 -0
  277. package/dist/action.js +50 -0
  278. package/dist/agent/abort-reasons.d.ts +38 -0
  279. package/dist/agent/abort-reasons.js +49 -0
  280. package/dist/agent/engine/ai-sdk-engine.js +8 -5
  281. package/dist/agent/engine/anthropic-engine.js +7 -5
  282. package/dist/agent/engine/first-event-timeout.d.ts +42 -17
  283. package/dist/agent/engine/first-event-timeout.js +67 -25
  284. package/dist/agent/production-agent.d.ts +23 -0
  285. package/dist/agent/production-agent.js +107 -102
  286. package/dist/agent/run-loop-with-resume.d.ts +2 -28
  287. package/dist/agent/run-loop-with-resume.js +25 -40
  288. package/dist/agent/run-manager.js +5 -4
  289. package/dist/agent/run-store.d.ts +55 -0
  290. package/dist/agent/run-store.js +124 -1
  291. package/dist/agent/tool-search.js +2 -1
  292. package/dist/agent/types.d.ts +9 -4
  293. package/dist/app-config/auth.d.ts +1 -0
  294. package/dist/app-config/auth.js +7 -0
  295. package/dist/app-config/configuration-error.d.ts +11 -0
  296. package/dist/app-config/configuration-error.js +14 -0
  297. package/dist/app-config/index.d.ts +1 -0
  298. package/dist/app-config/index.js +1 -0
  299. package/dist/app-config/integrations.d.ts +1 -0
  300. package/dist/app-config/integrations.js +10 -0
  301. package/dist/app-config/plugins.d.ts +34 -0
  302. package/dist/app-config/plugins.js +39 -0
  303. package/dist/app-config/run-lifecycle-invariants.d.ts +18 -9
  304. package/dist/app-config/run-lifecycle-invariants.js +18 -25
  305. package/dist/app-config/schema.d.ts +17 -0
  306. package/dist/app-config/schema.js +2 -0
  307. package/dist/chat-threads/schema-migrations.js +20 -0
  308. package/dist/chat-threads/store.js +21 -4
  309. package/dist/cli/native-dependencies.d.ts +4 -0
  310. package/dist/cli/native-dependencies.js +15 -0
  311. package/dist/client/EnvironmentBadge.d.ts +1 -1
  312. package/dist/client/EnvironmentBadge.js +40 -5
  313. package/dist/client/MultiTabAssistantChat.d.ts +1 -1
  314. package/dist/client/MultiTabAssistantChat.js +35 -14
  315. package/dist/client/agent-chat/index.d.ts +1 -1
  316. package/dist/client/agent-chat/index.js +1 -1
  317. package/dist/client/agent-page/use-jobs.d.ts +2 -1
  318. package/dist/client/agent-page/use-jobs.js +3 -5
  319. package/dist/client/agent-sidebar-state.js +3 -1
  320. package/dist/client/blocks/library/code-tabs.js +5 -0
  321. package/dist/client/blocks/library/code.js +1 -0
  322. package/dist/client/chat-first/apps-rail.js +2 -2
  323. package/dist/client/chat-model-groups.d.ts +12 -0
  324. package/dist/client/chat-model-groups.js +3 -2
  325. package/dist/client/integrations/GoogleProductLogo.d.ts +5 -0
  326. package/dist/client/integrations/GoogleProductLogo.js +45 -0
  327. package/dist/client/integrations/index.d.ts +2 -0
  328. package/dist/client/integrations/index.js +2 -0
  329. package/dist/client/integrations/workspace-provider-oauth.d.ts +8 -0
  330. package/dist/client/integrations/workspace-provider-oauth.js +13 -0
  331. package/dist/client/resources/mcp-integration-catalog.js +0 -29
  332. package/dist/client/settings/SettingsTabsPage.js +10 -6
  333. package/dist/client/sharing/ShareButton.js +11 -2
  334. package/dist/client/use-chat-models.d.ts +6 -0
  335. package/dist/client/use-chat-models.js +10 -0
  336. package/dist/client/use-db-sync.js +27 -9
  337. package/dist/collab/routes.d.ts +1 -1
  338. package/dist/collab/struct-routes.d.ts +1 -1
  339. package/dist/connections/catalog.d.ts +111 -9
  340. package/dist/connections/catalog.js +86 -30
  341. package/dist/db/create-get-db.js +18 -0
  342. package/dist/db/ddl-guard.d.ts +0 -4
  343. package/dist/db/ddl-guard.js +46 -0
  344. package/dist/deploy/build.d.ts +19 -0
  345. package/dist/deploy/build.js +190 -16
  346. package/dist/deploy/function-bundle.d.ts +45 -0
  347. package/dist/deploy/function-bundle.js +337 -0
  348. package/dist/deploy/workspace-deploy.js +19 -1
  349. package/dist/eject/workspace-connections.js +4 -0
  350. package/dist/email-catalog/registry.d.ts +4 -0
  351. package/dist/email-catalog/registry.js +79 -14
  352. package/dist/extensions/content-patch.js +14 -2
  353. package/dist/integrations/plugin.d.ts +10 -0
  354. package/dist/integrations/plugin.js +40 -6
  355. package/dist/jobs/actions/run-automation-now.d.ts +2 -1
  356. package/dist/jobs/actions/run-automation-now.js +11 -5
  357. package/dist/jobs/frontmatter.js +53 -2
  358. package/dist/jobs/run-now.d.ts +11 -1
  359. package/dist/jobs/run-now.js +42 -7
  360. package/dist/jobs/scheduler.d.ts +1 -0
  361. package/dist/jobs/scheduler.js +2 -1
  362. package/dist/localization/core-messages/ar-SA.js +3 -0
  363. package/dist/localization/core-messages/de-DE.js +3 -0
  364. package/dist/localization/core-messages/en-US.d.ts +3 -0
  365. package/dist/localization/core-messages/en-US.js +3 -0
  366. package/dist/localization/core-messages/es-ES.js +3 -0
  367. package/dist/localization/core-messages/fr-FR.js +3 -0
  368. package/dist/localization/core-messages/hi-IN.js +3 -0
  369. package/dist/localization/core-messages/ja-JP.js +3 -0
  370. package/dist/localization/core-messages/ko-KR.js +3 -0
  371. package/dist/localization/core-messages/pt-BR.js +3 -0
  372. package/dist/localization/core-messages/zh-CN.js +3 -0
  373. package/dist/localization/core-messages/zh-TW.js +3 -0
  374. package/dist/localization/core-messages.d.ts +3 -0
  375. package/dist/localization/default-messages.d.ts +2 -5
  376. package/dist/localization/default-messages.js +2 -5
  377. package/dist/mcp/build-server.d.ts +9 -0
  378. package/dist/mcp/build-server.js +37 -8
  379. package/dist/mcp-client/oauth-client.d.ts +7 -0
  380. package/dist/mcp-client/oauth-client.js +12 -1
  381. package/dist/oauth-tokens/index.d.ts +1 -1
  382. package/dist/oauth-tokens/index.js +1 -1
  383. package/dist/oauth-tokens/lifecycle.d.ts +11 -0
  384. package/dist/oauth-tokens/lifecycle.js +17 -0
  385. package/dist/oauth-tokens/store.js +7 -0
  386. package/dist/observability/posthog-ai.d.ts +15 -1
  387. package/dist/observability/posthog-ai.js +24 -12
  388. package/dist/observability/traces.js +158 -28
  389. package/dist/onboarding/app-profile.js +2 -2
  390. package/dist/onboarding/plugin.js +10 -2
  391. package/dist/onboarding/types.d.ts +2 -0
  392. package/dist/provider-api/actions/custom-provider-registration.d.ts +1 -1
  393. package/dist/provider-api/actions/provider-api.d.ts +2 -2
  394. package/dist/provider-api/index.js +2 -0
  395. package/dist/resources/handlers.d.ts +2 -2
  396. package/dist/resources/store.js +13 -4
  397. package/dist/scripts/agent-engines/list-agent-engines.js +29 -3
  398. package/dist/secrets/register-framework-secrets.js +1 -1
  399. package/dist/server/action-discovery.js +5 -0
  400. package/dist/server/agent-chat/action-filters-a2a.d.ts +27 -3
  401. package/dist/server/agent-chat/action-filters-a2a.js +48 -9
  402. package/dist/server/agent-chat-plugin.js +36 -9
  403. package/dist/server/agent-discovery.js +43 -20
  404. package/dist/server/auth.js +18 -16
  405. package/dist/server/beta-opt-out-html.js +45 -4
  406. package/dist/server/better-auth-instance.d.ts +2 -1
  407. package/dist/server/better-auth-instance.js +37 -7
  408. package/dist/server/builder-design-systems.d.ts +4 -1
  409. package/dist/server/builder-design-systems.js +87 -6
  410. package/dist/server/builder-oauth.d.ts +1 -7
  411. package/dist/server/builder-oauth.js +80 -163
  412. package/dist/server/core-routes-plugin.d.ts +29 -0
  413. package/dist/server/core-routes-plugin.js +100 -20
  414. package/dist/server/default-plugins.d.ts +18 -0
  415. package/dist/server/default-plugins.js +22 -0
  416. package/dist/server/edge.d.ts +1 -0
  417. package/dist/server/edge.js +1 -0
  418. package/dist/server/framework-request-handler.js +31 -3
  419. package/dist/server/google-oauth.d.ts +3 -0
  420. package/dist/server/google-oauth.js +3 -0
  421. package/dist/server/index.d.ts +4 -4
  422. package/dist/server/index.js +3 -3
  423. package/dist/server/onboarding-html.js +37 -3
  424. package/dist/server/realtime-token.d.ts +1 -1
  425. package/dist/server/workspace-provider-oauth.d.ts +6 -1
  426. package/dist/server/workspace-provider-oauth.js +72 -20
  427. package/dist/shared/agent-sidebar-url.d.ts +1 -0
  428. package/dist/shared/agent-sidebar-url.js +1 -0
  429. package/dist/shared/environment-lanes.d.ts +2 -0
  430. package/dist/shared/environment-lanes.js +3 -1
  431. package/dist/shared/index.d.ts +3 -1
  432. package/dist/shared/index.js +3 -1
  433. package/dist/shared/surface-visibility.d.ts +31 -0
  434. package/dist/shared/surface-visibility.js +57 -0
  435. package/dist/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
  436. package/dist/templates/chat/app/routes.ts +3 -1
  437. package/dist/templates/chat/vitest.config.ts +8 -0
  438. package/dist/templates/default/.agents/skills/secrets/SKILL.md +11 -4
  439. package/dist/templates/default/app/routes.ts +3 -1
  440. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +11 -4
  441. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +11 -4
  442. package/dist/tracking/registry.d.ts +11 -0
  443. package/dist/tracking/registry.js +5 -2
  444. package/dist/triggers/actions.js +10 -3
  445. package/dist/usage/store.js +23 -3
  446. package/dist/user-profile/actions/get-auth-methods.js +18 -5
  447. package/dist/vite/client.js +33 -7
  448. package/docs/content/actions-access-control.mdx +43 -4
  449. package/docs/content/actions-defining.mdx +16 -0
  450. package/docs/content/authentication.mdx +40 -21
  451. package/docs/content/environment-variables.mdx +1 -0
  452. package/docs/content/template-clips-capture-everywhere.mdx +29 -30
  453. package/docs/content/template-clips-developers.mdx +4 -4
  454. package/docs/content/template-clips.mdx +3 -1
  455. package/package.json +2 -2
  456. package/src/eject/workspace-connections.ts +4 -0
  457. package/src/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
  458. package/src/templates/chat/app/routes.ts +3 -1
  459. package/src/templates/chat/vitest.config.ts +8 -0
  460. package/src/templates/default/.agents/skills/secrets/SKILL.md +11 -4
  461. package/src/templates/default/app/routes.ts +3 -1
  462. package/src/templates/headless/.agents/skills/secrets/SKILL.md +11 -4
  463. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +11 -4
@@ -16,6 +16,7 @@ import {
16
16
  IconPlus,
17
17
  IconRefresh,
18
18
  } from "@tabler/icons-react";
19
+ import { useQueryClient } from "@tanstack/react-query";
19
20
  import { useEffect, useMemo, useRef, useState } from "react";
20
21
  import { Link, Navigate, useSearchParams } from "react-router";
21
22
  import { toast } from "sonner";
@@ -142,6 +143,12 @@ type FactoryAutomation = {
142
143
  pastRuns?: FactoryAutomationRun[] | null;
143
144
  };
144
145
 
146
+ type RunFactoryAutomationResult = {
147
+ queued: true;
148
+ runId: string;
149
+ automationRunId: string;
150
+ };
151
+
145
152
  const DEFAULT_FACTORY_ID = "product-feedback";
146
153
 
147
154
  export function meta() {
@@ -745,9 +752,9 @@ export default function FactoryRoute() {
745
752
  ) : activeTab === "rules" ? (
746
753
  <RulesView factoryId={factoryId} t={t} />
747
754
  ) : activeTab === "settings" ? (
748
- <FactorySettingsView factoryId={factoryId} />
755
+ <FactorySettingsView key={factoryId} factoryId={factoryId} />
749
756
  ) : activeTab === "automations" ? (
750
- <AutomationsView factoryId={factoryId} t={t} />
757
+ <AutomationsView key={factoryId} factoryId={factoryId} t={t} />
751
758
  ) : activeTab === "audit" ? (
752
759
  <FactoryAuditView
753
760
  factoryId={factoryId}
@@ -922,13 +929,22 @@ function AutomationsView({
922
929
  }) {
923
930
  const [searchParams, setSearchParams] = useSearchParams();
924
931
  const [draft, setDraft] = useState<FactoryAutomation | null>(null);
932
+ const [queuedRuns, setQueuedRuns] = useState<Record<string, string>>({});
933
+ const queryClient = useQueryClient();
934
+ useEffect(() => {
935
+ setQueuedRuns({});
936
+ }, [factoryId]);
925
937
  const selectedId = searchParams.get("automationId");
926
938
  const automationsQuery = useActionQuery<FactoryAutomation[]>(
927
939
  "list-factory-automations",
928
940
  { factoryId },
941
+ { refetchInterval: Object.keys(queuedRuns).length > 0 ? 1_000 : false },
929
942
  );
930
943
  const saveMutation = useActionMutation("save-factory-automation");
931
- const runMutation = useActionMutation("run-factory-automation");
944
+ const runMutation = useActionMutation<
945
+ RunFactoryAutomationResult,
946
+ { factoryId: string; automationId: string }
947
+ >("run-factory-automation", { skipActionQueryInvalidation: true });
932
948
  const {
933
949
  availableModels,
934
950
  defaultModel,
@@ -1008,6 +1024,28 @@ function AutomationsView({
1008
1024
  });
1009
1025
  }, [selected, selectedId]);
1010
1026
 
1027
+ useEffect(() => {
1028
+ if (Object.keys(queuedRuns).length === 0 || !response) return;
1029
+ const finishedAutomationIds = Object.entries(queuedRuns).flatMap(
1030
+ ([automationId, runId]) => {
1031
+ const automation = response.find((entry) => entry.id === automationId);
1032
+ const run = automation?.runs?.find((entry) => entry.id === runId);
1033
+ return run && run.status !== "running" ? [automationId] : [];
1034
+ },
1035
+ );
1036
+ if (finishedAutomationIds.length === 0) return;
1037
+ setQueuedRuns((current) => {
1038
+ const next = { ...current };
1039
+ for (const automationId of finishedAutomationIds) {
1040
+ delete next[automationId];
1041
+ }
1042
+ return next;
1043
+ });
1044
+ void queryClient.invalidateQueries({
1045
+ queryKey: ["action", "list-factory-audit"],
1046
+ });
1047
+ }, [queryClient, queuedRuns, response]);
1048
+
1011
1049
  async function saveAutomation() {
1012
1050
  if (!draft) return;
1013
1051
  try {
@@ -1034,8 +1072,33 @@ function AutomationsView({
1034
1072
 
1035
1073
  async function runAutomation() {
1036
1074
  if (!draft) return;
1037
- await runMutation.mutateAsync({ factoryId, automationId: draft.id });
1038
- await automationsQuery.refetch();
1075
+ try {
1076
+ const result = await runMutation.mutateAsync({
1077
+ factoryId,
1078
+ automationId: draft.id,
1079
+ });
1080
+ setQueuedRuns((current) => ({
1081
+ ...current,
1082
+ [draft.id]: result.automationRunId,
1083
+ }));
1084
+ setSearchParams(
1085
+ (current) => {
1086
+ const next = new URLSearchParams(current);
1087
+ next.set("auditRunId", result.automationRunId);
1088
+ return next;
1089
+ },
1090
+ { replace: true },
1091
+ );
1092
+ void queryClient.invalidateQueries({
1093
+ queryKey: ["action", "list-factory-audit"],
1094
+ });
1095
+ } catch (error) {
1096
+ toast.error(
1097
+ error instanceof Error
1098
+ ? error.message
1099
+ : t("factoryRoute.automationRunFailed"),
1100
+ );
1101
+ }
1039
1102
  }
1040
1103
 
1041
1104
  return (
@@ -1067,6 +1130,7 @@ function AutomationsView({
1067
1130
  >
1068
1131
  {automations.map((automation) => {
1069
1132
  const selected = activeAutomationId === automation.id;
1133
+ const running = Boolean(queuedRuns[automation.id]);
1070
1134
  return (
1071
1135
  <button
1072
1136
  key={automation.id}
@@ -1083,15 +1147,20 @@ function AutomationsView({
1083
1147
  onClick={() => selectAutomation(automation.id)}
1084
1148
  >
1085
1149
  <span
1086
- className="block truncate text-sm font-medium"
1150
+ className="block break-words text-sm font-medium"
1087
1151
  title={automation.name}
1088
1152
  >
1089
1153
  {automation.displayName}
1090
1154
  </span>
1091
- <span className="mt-1 block truncate text-xs text-muted-foreground">
1092
- {automation.enabled
1093
- ? t("factoryRoute.automationEnabled")
1094
- : t("factoryRoute.automationDisabled")}
1155
+ <span className="mt-1 flex items-center gap-1.5 truncate text-xs text-muted-foreground">
1156
+ {running && (
1157
+ <IconLoader2 className="size-3 animate-spin motion-reduce:animate-none" />
1158
+ )}
1159
+ {running
1160
+ ? t("factoryRoute.automationRunning")
1161
+ : automation.enabled
1162
+ ? t("factoryRoute.automationEnabled")
1163
+ : t("factoryRoute.automationDisabled")}
1095
1164
  </span>
1096
1165
  </button>
1097
1166
  );
@@ -1126,7 +1195,11 @@ function AutomationsView({
1126
1195
  variant="outline"
1127
1196
  size="sm"
1128
1197
  onClick={() => void runAutomation()}
1129
- disabled={runMutation.isPending || draft.canUpdate === false}
1198
+ disabled={
1199
+ runMutation.isPending ||
1200
+ Boolean(queuedRuns[draft.id]) ||
1201
+ draft.canUpdate === false
1202
+ }
1130
1203
  >
1131
1204
  {runMutation.isPending && (
1132
1205
  <IconLoader2 className="animate-spin" />
@@ -1,4 +1,6 @@
1
1
  import { type RouteConfig } from "@react-router/dev/routes";
2
2
  import { flatRoutes } from "@react-router/fs-routes";
3
3
 
4
- export default flatRoutes() satisfies RouteConfig;
4
+ export default flatRoutes({
5
+ ignoredRouteFiles: ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"],
6
+ }) satisfies RouteConfig;
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-21
4
+ ---
5
+
6
+ Activity run history labels each run with the automation's display name instead of its full resource path.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-21
4
+ ---
5
+
6
+ Create Factory and Settings now share the same labeled source cards, Enable polling toggles, and uncrowded scheduler health rows.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-21
4
+ ---
5
+
6
+ Automations remain visible while running, with live status updates in Automations and Activity.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-21
4
+ ---
5
+
6
+ Factory Slack, GitHub, and Sentry automations now run for nested factories instead of failing before they can poll.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-21
4
+ ---
5
+
6
+ Factory settings now show a sticky Save and Discard bar at the top as soon as a field changes, instead of a save button buried below the page.
@@ -21,6 +21,10 @@ const WORKSPACE_PROVIDER_BY_KEY: Record<string, string> = {
21
21
  SLACK_BOT_TOKEN: "slack",
22
22
  SLACK_BOT_TOKEN_2: "slack",
23
23
  };
24
+ const VAULT_ONLY_KEYS = new Set([
25
+ ...Object.keys(WORKSPACE_PROVIDER_BY_KEY),
26
+ "SENTRY_ORG_SLUG",
27
+ ]);
24
28
 
25
29
  function isMissingTableError(err: unknown): boolean {
26
30
  const message = err instanceof Error ? err.message : String(err);
@@ -145,11 +149,12 @@ export async function resolveConnectorSecret(
145
149
  }
146
150
  }
147
151
 
148
- // Deployment-managed connector configuration is the last resort inside the
149
- // resolver, never a parallel path in a provider client. A designated
150
- // Dispatch vault must win over a stale deployment copy of the same key.
151
- const environmentSecret = process.env[key]?.trim(); // guard:allow-env-credential - deploy-level connector fallback
152
- if (environmentSecret) return environmentSecret;
152
+ // Standard provider keys are org/workspace data, not deployment config.
153
+ // Generic app-owned keys may still use the deployment fallback below.
154
+ if (!VAULT_ONLY_KEYS.has(key)) {
155
+ const environmentSecret = process.env[key]?.trim(); // guard:allow-env-credential - deploy-level connector fallback for generic app-owned configuration
156
+ if (environmentSecret) return environmentSecret;
157
+ }
153
158
 
154
159
  return undefined;
155
160
  }
@@ -0,0 +1,412 @@
1
+ export type FactoryAuditEventRecord = {
2
+ id: string;
3
+ itemId: string | null;
4
+ source: string | null;
5
+ sourceUrl: string | null;
6
+ action: string;
7
+ kind: string;
8
+ status: string;
9
+ summary: string;
10
+ details: Record<string, unknown>;
11
+ createdAt: string;
12
+ };
13
+
14
+ export type FactoryAuditItemSnapshot = {
15
+ id: string;
16
+ title: string;
17
+ summary: string | null;
18
+ source: string | null;
19
+ sourceUrl: string | null;
20
+ };
21
+
22
+ export type FactoryAuditRunSnapshot = {
23
+ itemId: string;
24
+ status: string;
25
+ error: string | null;
26
+ provider: string | null;
27
+ startedAt: string;
28
+ };
29
+
30
+ export type FactoryAuditItemOutcome =
31
+ | "held"
32
+ | "dispatched"
33
+ | "failed"
34
+ | "inspected";
35
+
36
+ export type FactoryAuditReportItem = {
37
+ itemId: string;
38
+ source: string | null;
39
+ sourceUrl: string | null;
40
+ title: string;
41
+ outcome: FactoryAuditItemOutcome;
42
+ status: string;
43
+ rationale: string | null;
44
+ dispatchError: string | null;
45
+ clearBug: boolean | null;
46
+ productUx: boolean | null;
47
+ ownerArea: string | null;
48
+ guards: string | null;
49
+ events: FactoryAuditEventRecord[];
50
+ latestAt: string;
51
+ };
52
+
53
+ export type FactoryAuditTraceStep = {
54
+ id: string;
55
+ action: string;
56
+ summary: string;
57
+ status: string;
58
+ createdAt: string;
59
+ count: number;
60
+ purpose: string | null;
61
+ };
62
+
63
+ export type FactoryAuditCounts = {
64
+ newlyObserved: number;
65
+ scanned: number;
66
+ investigated: number;
67
+ held: number;
68
+ dispatched: number;
69
+ failed: number;
70
+ };
71
+
72
+ export type FactoryAuditReport = {
73
+ counts: FactoryAuditCounts;
74
+ items: FactoryAuditReportItem[];
75
+ trace: FactoryAuditTraceStep[];
76
+ };
77
+
78
+ const INVESTIGATE_ACTIONS = new Set([
79
+ "get-slack-feedback-context",
80
+ "get-triage-item",
81
+ ]);
82
+
83
+ export function projectFactoryAuditReport(
84
+ events: FactoryAuditEventRecord[],
85
+ items: FactoryAuditItemSnapshot[] = [],
86
+ runs: FactoryAuditRunSnapshot[] = [],
87
+ window?: { startedAt: number; finishedAt: number | null },
88
+ ): FactoryAuditReport {
89
+ const itemsById = new Map(items.map((item) => [item.id, item]));
90
+ const latestRunByItem = latestRunsByItem(
91
+ window ? runsInWindow(runs, window) : runs,
92
+ );
93
+ const chronological = [...events].sort(byCreatedAtAsc);
94
+
95
+ const primaryItemIds = new Set<string>();
96
+ for (const event of chronological) {
97
+ if (!event.itemId) continue;
98
+ if (
99
+ INVESTIGATE_ACTIONS.has(event.action) ||
100
+ event.kind === "decision" ||
101
+ event.kind === "external_action"
102
+ ) {
103
+ primaryItemIds.add(event.itemId);
104
+ }
105
+ }
106
+
107
+ const reportItems = [...primaryItemIds]
108
+ .map((itemId) =>
109
+ projectItem(
110
+ itemId,
111
+ chronological.filter(
112
+ (event) => event.itemId === itemId && !isScanEvent(event),
113
+ ),
114
+ itemsById.get(itemId),
115
+ latestRunByItem.get(itemId),
116
+ ),
117
+ )
118
+ .sort(
119
+ (left, right) =>
120
+ new Date(right.latestAt).getTime() - new Date(left.latestAt).getTime(),
121
+ );
122
+
123
+ const counts: FactoryAuditCounts = {
124
+ newlyObserved: uniqueItemIds(
125
+ chronological.filter((event) => isPollEvent(event) && event.itemId),
126
+ ).size,
127
+ scanned: scannedItemCount(chronological),
128
+ investigated: reportItems.length,
129
+ held: reportItems.filter((item) => item.outcome === "held").length,
130
+ dispatched: reportItems.filter((item) => item.outcome === "dispatched")
131
+ .length,
132
+ failed: reportItems.filter((item) => item.outcome === "failed").length,
133
+ };
134
+
135
+ return {
136
+ counts,
137
+ items: reportItems,
138
+ trace: collapseTrace(chronological),
139
+ };
140
+ }
141
+
142
+ function projectItem(
143
+ itemId: string,
144
+ events: FactoryAuditEventRecord[],
145
+ item: FactoryAuditItemSnapshot | undefined,
146
+ run: FactoryAuditRunSnapshot | undefined,
147
+ ): FactoryAuditReportItem {
148
+ const decision = events.find((event) => event.kind === "decision") ?? null;
149
+ const dispatch =
150
+ events.find((event) => event.kind === "external_action") ?? null;
151
+ const latestAt = events.reduce(
152
+ (latest, event) =>
153
+ new Date(event.createdAt).getTime() > new Date(latest).getTime()
154
+ ? event.createdAt
155
+ : latest,
156
+ events[0]?.createdAt ?? new Date(0).toISOString(),
157
+ );
158
+ const dispatchError = readDispatchError(dispatch, run);
159
+ const outcome = itemOutcome(decision, dispatch, dispatchError);
160
+
161
+ return {
162
+ itemId,
163
+ source: item?.source ?? events[0]?.source ?? null,
164
+ sourceUrl:
165
+ item?.sourceUrl ??
166
+ events.find((event) => event.sourceUrl)?.sourceUrl ??
167
+ null,
168
+ title: auditItemSubject(item, events),
169
+ outcome,
170
+ status:
171
+ outcome === "failed"
172
+ ? "error"
173
+ : outcome === "held"
174
+ ? "skipped"
175
+ : "success",
176
+ rationale: decision?.summary ?? dispatch?.summary ?? null,
177
+ dispatchError,
178
+ clearBug: readBooleanDetail(decision?.details, "clearBug"),
179
+ productUx: readBooleanDetail(decision?.details, "productUxImplications"),
180
+ ownerArea: readStringDetail(decision?.details, "ownerOwnedArea"),
181
+ guards: readGuardSummary(decision?.details?.guardResults),
182
+ events,
183
+ latestAt,
184
+ };
185
+ }
186
+
187
+ function itemOutcome(
188
+ decision: FactoryAuditEventRecord | null,
189
+ dispatch: FactoryAuditEventRecord | null,
190
+ dispatchError: string | null,
191
+ ): FactoryAuditItemOutcome {
192
+ if (dispatchError) return "failed";
193
+ if (dispatch?.status === "error") return "failed";
194
+ if (dispatch) return "dispatched";
195
+ if (decision?.status === "skipped") return "held";
196
+ if (decision) return "held";
197
+ return "inspected";
198
+ }
199
+
200
+ function readDispatchError(
201
+ dispatch: FactoryAuditEventRecord | null,
202
+ run: FactoryAuditRunSnapshot | undefined,
203
+ ): string | null {
204
+ if (dispatch?.status === "error") {
205
+ return dispatch.summary.trim() || run?.error || "Dispatch failed.";
206
+ }
207
+ if (run?.status === "failed" && run.error) return run.error;
208
+ return null;
209
+ }
210
+
211
+ export function auditItemSubject(
212
+ item: FactoryAuditItemSnapshot | undefined,
213
+ events: FactoryAuditEventRecord[],
214
+ ): string {
215
+ const observed = events.find(
216
+ (event) =>
217
+ isPollEvent(event) && event.summary.trim() && !isEmptyObservation(event),
218
+ )?.summary;
219
+ const detailSummary = events
220
+ .map((event) => readStringDetail(event.details, "itemSummary"))
221
+ .find((value): value is string => Boolean(value));
222
+ const storedSummary =
223
+ item?.summary?.trim() || detailSummary?.trim() || observed?.trim() || "";
224
+ if (storedSummary) return firstLine(storedSummary, 110);
225
+
226
+ const storedTitle = item?.title?.trim() ?? "";
227
+ if (storedTitle && !isGenericSlackTitle(storedTitle)) return storedTitle;
228
+
229
+ const listed = events.find((event) => event.action === "list-triage-items");
230
+ const listedTitle = listed?.summary.trim() ?? "";
231
+ if (listedTitle && !isGenericSlackTitle(listedTitle)) return listedTitle;
232
+
233
+ return storedTitle || "Item";
234
+ }
235
+
236
+ function collapseTrace(
237
+ events: FactoryAuditEventRecord[],
238
+ ): FactoryAuditTraceStep[] {
239
+ const steps: FactoryAuditTraceStep[] = [];
240
+ const scanGroups = new Map<string, FactoryAuditEventRecord[]>();
241
+
242
+ for (const event of events) {
243
+ if (isScanEvent(event)) {
244
+ const purpose = readStringDetail(event.details, "purpose") ?? "scan";
245
+ const bucket = event.createdAt.slice(0, 19);
246
+ const key = `${purpose}:${bucket}`;
247
+ const current = scanGroups.get(key) ?? [];
248
+ current.push(event);
249
+ scanGroups.set(key, current);
250
+ continue;
251
+ }
252
+ if (isPollEvent(event)) {
253
+ if (event.itemId && !isEmptyObservation(event)) continue;
254
+ steps.push({
255
+ id: event.id,
256
+ action: event.action,
257
+ summary: event.summary,
258
+ status: event.status,
259
+ createdAt: event.createdAt,
260
+ count: event.itemId ? 1 : 0,
261
+ purpose: null,
262
+ });
263
+ }
264
+ }
265
+
266
+ for (const group of scanGroups.values()) {
267
+ const first = group[0]!;
268
+ const itemIds = new Set<string>();
269
+ for (const event of group) {
270
+ for (const itemId of eventItemIds(event)) itemIds.add(itemId);
271
+ }
272
+ const purpose = readStringDetail(first.details, "purpose");
273
+ const count =
274
+ typeof first.details.count === "number"
275
+ ? first.details.count
276
+ : itemIds.size;
277
+ steps.push({
278
+ id: first.id,
279
+ action: first.action,
280
+ summary:
281
+ group.length === 1 && !first.itemId
282
+ ? first.summary
283
+ : `Loaded ${count} item${count === 1 ? "" : "s"}.`,
284
+ status: first.status,
285
+ createdAt: first.createdAt,
286
+ count,
287
+ purpose,
288
+ });
289
+ }
290
+
291
+ return steps.sort(
292
+ (left, right) =>
293
+ new Date(left.createdAt).getTime() - new Date(right.createdAt).getTime(),
294
+ );
295
+ }
296
+
297
+ function scannedItemCount(events: FactoryAuditEventRecord[]): number {
298
+ const ids = new Set<string>();
299
+ for (const event of events) {
300
+ if (!isScanEvent(event)) continue;
301
+ for (const itemId of eventItemIds(event)) ids.add(itemId);
302
+ }
303
+ return ids.size;
304
+ }
305
+
306
+ function eventItemIds(event: FactoryAuditEventRecord): string[] {
307
+ if (event.itemId) return [event.itemId];
308
+ const raw = event.details.itemIds;
309
+ if (!Array.isArray(raw)) return [];
310
+ return raw.filter(
311
+ (value): value is string => typeof value === "string" && value.length > 0,
312
+ );
313
+ }
314
+
315
+ function uniqueItemIds(events: FactoryAuditEventRecord[]): Set<string> {
316
+ const ids = new Set<string>();
317
+ for (const event of events) {
318
+ for (const itemId of eventItemIds(event)) ids.add(itemId);
319
+ }
320
+ return ids;
321
+ }
322
+
323
+ function latestRunsByItem(
324
+ runs: FactoryAuditRunSnapshot[],
325
+ ): Map<string, FactoryAuditRunSnapshot> {
326
+ const latest = new Map<string, FactoryAuditRunSnapshot>();
327
+ for (const run of runs) {
328
+ const current = latest.get(run.itemId);
329
+ if (
330
+ !current ||
331
+ new Date(run.startedAt).getTime() > new Date(current.startedAt).getTime()
332
+ ) {
333
+ latest.set(run.itemId, run);
334
+ }
335
+ }
336
+ return latest;
337
+ }
338
+
339
+ function runsInWindow(
340
+ runs: FactoryAuditRunSnapshot[],
341
+ window: { startedAt: number; finishedAt: number | null },
342
+ ): FactoryAuditRunSnapshot[] {
343
+ const start = window.startedAt - 5_000;
344
+ const end = (window.finishedAt ?? Date.now()) + 5_000;
345
+ return runs.filter((run) => {
346
+ const at = new Date(run.startedAt).getTime();
347
+ return at >= start && at <= end;
348
+ });
349
+ }
350
+
351
+ function isScanEvent(event: FactoryAuditEventRecord): boolean {
352
+ return event.action === "list-triage-items";
353
+ }
354
+
355
+ function isPollEvent(event: FactoryAuditEventRecord): boolean {
356
+ return (
357
+ event.kind === "observed" ||
358
+ event.action.startsWith("poll-") ||
359
+ event.action === "ingest-github-observation"
360
+ );
361
+ }
362
+
363
+ function isEmptyObservation(event: FactoryAuditEventRecord): boolean {
364
+ return /no new|no open|no result/i.test(event.summary);
365
+ }
366
+
367
+ function isGenericSlackTitle(value: string): boolean {
368
+ return /^Slack (user|bot)\b/i.test(value.trim());
369
+ }
370
+
371
+ function firstLine(value: string, maxLength: number): string {
372
+ const line = value.replace(/\s+/g, " ").trim();
373
+ return line.length > maxLength ? `${line.slice(0, maxLength - 1)}…` : line;
374
+ }
375
+
376
+ function byCreatedAtAsc(
377
+ left: FactoryAuditEventRecord,
378
+ right: FactoryAuditEventRecord,
379
+ ): number {
380
+ return (
381
+ new Date(left.createdAt).getTime() - new Date(right.createdAt).getTime()
382
+ );
383
+ }
384
+
385
+ function readBooleanDetail(
386
+ details: Record<string, unknown> | undefined,
387
+ key: string,
388
+ ): boolean | null {
389
+ const value = details?.[key];
390
+ return typeof value === "boolean" ? value : null;
391
+ }
392
+
393
+ function readStringDetail(
394
+ details: Record<string, unknown> | undefined,
395
+ key: string,
396
+ ): string | null {
397
+ const value = details?.[key];
398
+ return typeof value === "string" && value ? value : null;
399
+ }
400
+
401
+ function readGuardSummary(value: unknown): string | null {
402
+ if (!Array.isArray(value) || value.length === 0) return null;
403
+ const passed = value.filter(
404
+ (guard): guard is { passed: boolean } =>
405
+ typeof guard === "object" &&
406
+ guard !== null &&
407
+ "passed" in guard &&
408
+ typeof (guard as { passed: unknown }).passed === "boolean" &&
409
+ (guard as { passed: boolean }).passed,
410
+ ).length;
411
+ return `${passed}/${value.length} passed`;
412
+ }