@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
@@ -9,16 +9,22 @@ import {
9
9
  IconExternalLink,
10
10
  IconSearch,
11
11
  } from "@tabler/icons-react";
12
- import { useEffect, useMemo, useRef } from "react";
12
+ import { useEffect, useRef } from "react";
13
13
  import { useSearchParams } from "react-router";
14
14
 
15
15
  import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
16
16
 
17
+ type FactoryAuditCounts = {
18
+ newlyObserved: number;
19
+ scanned: number;
20
+ investigated: number;
21
+ held: number;
22
+ dispatched: number;
23
+ failed: number;
24
+ };
25
+
17
26
  type FactoryAuditEvent = {
18
27
  id: string;
19
- automationRunId: string | null;
20
- automationThreadId: string | null;
21
- automationName: string | null;
22
28
  itemId: string | null;
23
29
  source: string | null;
24
30
  sourceUrl: string | null;
@@ -30,16 +36,45 @@ type FactoryAuditEvent = {
30
36
  createdAt: string;
31
37
  };
32
38
 
39
+ type FactoryAuditItem = {
40
+ itemId: string;
41
+ source: string | null;
42
+ sourceUrl: string | null;
43
+ title: string;
44
+ outcome: "held" | "dispatched" | "failed" | "inspected";
45
+ status: string;
46
+ rationale: string | null;
47
+ dispatchError: string | null;
48
+ clearBug: boolean | null;
49
+ productUx: boolean | null;
50
+ ownerArea: string | null;
51
+ guards: string | null;
52
+ events: FactoryAuditEvent[];
53
+ };
54
+
55
+ type FactoryAuditTraceStep = {
56
+ id: string;
57
+ action: string;
58
+ summary: string;
59
+ status: string;
60
+ createdAt: string;
61
+ count: number;
62
+ purpose: string | null;
63
+ };
64
+
33
65
  type FactoryAuditRun = {
34
66
  id: string;
35
67
  automation: string;
68
+ displayName: string | null;
36
69
  runId: string | null;
37
70
  threadId: string | null;
38
71
  status: string;
39
72
  startedAt: number;
40
73
  finishedAt: number | null;
41
74
  error: string | null;
42
- events: FactoryAuditEvent[];
75
+ counts: FactoryAuditCounts;
76
+ items: FactoryAuditItem[];
77
+ trace: FactoryAuditTraceStep[];
43
78
  };
44
79
 
45
80
  type FactoryAuditResponse = {
@@ -47,21 +82,6 @@ type FactoryAuditResponse = {
47
82
  count: number;
48
83
  };
49
84
 
50
- type AuditItemGroup = {
51
- key: string;
52
- events: FactoryAuditEvent[];
53
- itemId: string | null;
54
- source: string | null;
55
- sourceUrl: string | null;
56
- title: string;
57
- outcome: string;
58
- status: string;
59
- latestAt: string;
60
- checks: number;
61
- decision: FactoryAuditEvent | null;
62
- externalAction: FactoryAuditEvent | null;
63
- };
64
-
65
85
  export function FactoryAuditView({
66
86
  factoryId,
67
87
  refreshToken = 0,
@@ -76,7 +96,13 @@ export function FactoryAuditView({
76
96
  const auditQuery = useActionQuery<FactoryAuditResponse>(
77
97
  "list-factory-audit",
78
98
  { factoryId, limit: 30 },
79
- { staleTime: 5_000 },
99
+ {
100
+ staleTime: 5_000,
101
+ refetchInterval: (query) =>
102
+ query.state.data?.runs.some((run) => run.status === "running")
103
+ ? 1_000
104
+ : false,
105
+ },
80
106
  );
81
107
  const refetchAudit = auditQuery.refetch;
82
108
  const runs = auditQuery.data?.runs ?? [];
@@ -140,14 +166,9 @@ export function FactoryAuditView({
140
166
  <div className="grid gap-4 lg:grid-cols-[minmax(240px,.4fr)_minmax(0,1fr)]">
141
167
  <Card>
142
168
  <CardHeader className="px-4 py-3">
143
- <div className="flex items-center justify-between gap-3">
144
- <CardTitle className="text-sm">
145
- {t("factoryRoute.auditRuns")}
146
- </CardTitle>
147
- <span className="text-xs text-muted-foreground">
148
- {runs.length}
149
- </span>
150
- </div>
169
+ <CardTitle className="text-sm">
170
+ {t("factoryRoute.auditRuns")}
171
+ </CardTitle>
151
172
  </CardHeader>
152
173
  <CardContent className="p-0">
153
174
  <div className="grid gap-1.5 p-2">
@@ -167,17 +188,16 @@ export function FactoryAuditView({
167
188
  >
168
189
  <div className="flex items-center justify-between gap-3">
169
190
  <span className="min-w-0 truncate text-sm font-medium">
170
- {formatAutomationName(run.automation)}
191
+ {automationLabel(run)}
171
192
  </span>
172
- <AuditStatus status={run.status} />
193
+ <AuditStatus status={runHeadlineStatus(run)} />
173
194
  </div>
174
195
  <div className="mt-1 flex items-center justify-between gap-2 text-xs text-muted-foreground">
175
196
  <span className="shrink-0">
176
197
  {formatAuditAge(run.startedAt)}
177
198
  </span>
178
- <span className="shrink-0">
179
- {run.events.length}{" "}
180
- {formatAuditCountLabel(run.events.length)}
199
+ <span className="min-w-0 truncate">
200
+ {formatRunHeadline(run.counts, t)}
181
201
  </span>
182
202
  </div>
183
203
  </button>
@@ -192,16 +212,19 @@ export function FactoryAuditView({
192
212
  <div className="flex flex-wrap items-start justify-between gap-3">
193
213
  <div className="min-w-0">
194
214
  <CardTitle className="truncate text-base">
195
- {selectedRun &&
196
- formatAutomationName(selectedRun.automation)}
215
+ {selectedRun && automationLabel(selectedRun)}
197
216
  </CardTitle>
198
217
  {selectedRun && (
199
218
  <p className="mt-1 truncate text-xs text-muted-foreground">
200
219
  {formatAuditAge(selectedRun.startedAt)}
220
+ <span aria-hidden="true"> · </span>
221
+ {formatRunHeadline(selectedRun.counts, t)}
201
222
  </p>
202
223
  )}
203
224
  </div>
204
- {selectedRun && <AuditStatus status={selectedRun.status} />}
225
+ {selectedRun && (
226
+ <AuditStatus status={runHeadlineStatus(selectedRun)} />
227
+ )}
205
228
  </div>
206
229
  </CardHeader>
207
230
  <CardContent className="pt-4">
@@ -224,35 +247,17 @@ function AuditRunDetail({
224
247
  factoryId: string;
225
248
  }) {
226
249
  const t = useT();
227
- const groups = useMemo(() => groupAuditEvents(run.events), [run.events]);
228
- const itemCount = new Set(
229
- run.events
230
- .map((event) => event.itemId)
231
- .filter((itemId): itemId is string => Boolean(itemId)),
232
- ).size;
233
- const decisionCount = run.events.filter(
234
- (event) => event.kind === "decision",
235
- ).length;
236
- const actionCount = run.events.filter(
237
- (event) => event.kind === "external_action",
238
- ).length;
250
+ const items = run.items ?? [];
251
+ const trace = run.trace ?? [];
252
+ const failedItems = items.filter((item) => item.outcome === "failed");
239
253
 
240
254
  return (
241
255
  <>
242
256
  <div className="flex flex-wrap items-center gap-x-4 gap-y-2 text-xs text-muted-foreground">
243
- <span>
244
- {itemCount} {itemCount === 1 ? "item" : "items"}
245
- </span>
246
- <span>
247
- {decisionCount} {decisionCount === 1 ? "decision" : "decisions"}
248
- </span>
249
- <span>
250
- {actionCount} {actionCount === 1 ? "action" : "actions"}
251
- </span>
252
257
  {run.threadId && (
253
258
  <a
254
259
  href={`/chat/${encodeURIComponent(run.threadId)}`}
255
- className="ml-auto inline-flex items-center text-primary hover:underline"
260
+ className="inline-flex items-center text-primary hover:underline"
256
261
  onClick={(event) => {
257
262
  if (
258
263
  event.metaKey ||
@@ -272,167 +277,194 @@ function AuditRunDetail({
272
277
  )}
273
278
  </div>
274
279
 
275
- {run.error && (
280
+ {(run.error || failedItems.length > 0) && (
276
281
  <div className="mt-4 rounded-md bg-destructive/5 p-3 text-sm">
277
282
  <p className="font-medium text-destructive">
278
283
  {t("factoryRoute.auditRunError")}
279
284
  </p>
280
- <p className="mt-1 break-words text-muted-foreground">{run.error}</p>
285
+ {run.error ? (
286
+ <p className="mt-1 break-words text-muted-foreground">
287
+ {run.error}
288
+ </p>
289
+ ) : null}
290
+ {failedItems.map((item) => (
291
+ <p
292
+ key={item.itemId}
293
+ className="mt-1 break-words text-muted-foreground"
294
+ >
295
+ {item.title}
296
+ {item.dispatchError ? ` — ${item.dispatchError}` : ""}
297
+ </p>
298
+ ))}
281
299
  </div>
282
300
  )}
283
301
 
284
- <div className="mt-5">
285
- <div className="mb-2 flex items-center justify-between gap-3">
286
- <h3 className="text-sm font-medium">Activity</h3>
287
- <span className="text-xs text-muted-foreground">
288
- {groups.length} {groups.length === 1 ? "thing" : "things"}
289
- </span>
290
- </div>
291
- {groups.length === 0 ? (
302
+ <div className="mt-5 grid gap-1.5">
303
+ {items.length === 0 ? (
292
304
  <p className="rounded-md bg-muted/20 p-4 text-sm text-muted-foreground">
293
305
  {t("factoryRoute.auditNoEvents")}
294
306
  </p>
295
307
  ) : (
296
- <div className="grid gap-1.5">
297
- {groups.map((group) => (
298
- <AuditItemRow
299
- key={group.key}
300
- group={group}
301
- factoryId={factoryId}
302
- />
303
- ))}
304
- </div>
308
+ items.map((item) => (
309
+ <AuditItemRow key={item.itemId} item={item} factoryId={factoryId} />
310
+ ))
305
311
  )}
306
312
  </div>
313
+
314
+ {trace.length > 0 && (
315
+ <details className="group mt-5 overflow-hidden rounded-lg border border-border bg-muted/20">
316
+ <summary className="flex cursor-pointer list-none items-center justify-between gap-3 rounded-lg px-3 py-3 text-sm hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring [&::-webkit-details-marker]:hidden">
317
+ <span>{t("factoryRoute.auditTrace")}</span>
318
+ <IconChevronDown className="size-4 shrink-0 text-muted-foreground transition-transform duration-200 ease-[var(--ease-collapse)] group-open:rotate-180 motion-reduce:transition-none" />
319
+ </summary>
320
+ <div className="space-y-2 px-4 pb-4">
321
+ {trace.map((step) => (
322
+ <div
323
+ key={step.id}
324
+ className="flex items-center justify-between gap-3 text-xs text-muted-foreground"
325
+ >
326
+ <span className="min-w-0 truncate">{step.summary}</span>
327
+ <time className="shrink-0">
328
+ {formatAuditAge(step.createdAt)}
329
+ </time>
330
+ </div>
331
+ ))}
332
+ </div>
333
+ </details>
334
+ )}
307
335
  </>
308
336
  );
309
337
  }
310
338
 
311
339
  function AuditItemRow({
312
- group,
340
+ item,
313
341
  factoryId,
314
342
  }: {
315
- group: AuditItemGroup;
343
+ item: FactoryAuditItem;
316
344
  factoryId: string;
317
345
  }) {
318
346
  const t = useT();
319
- const decision = group.decision;
320
- const rationale = decision?.summary ?? group.externalAction?.summary ?? null;
321
- const sourceLink = resolveAuditSourceLink(group);
347
+ const sourceLink = resolveAuditSourceLink(item);
322
348
 
323
349
  return (
324
350
  <details className="group overflow-hidden rounded-lg border border-border bg-muted/20">
325
351
  <summary className="flex cursor-pointer list-none items-center gap-3 rounded-lg px-3 py-3 hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring [&::-webkit-details-marker]:hidden">
326
- <AuditSourceIcon source={group.source} />
352
+ <AuditSourceIcon source={item.source} />
327
353
  <div className="min-w-0 flex-1">
328
- <p className="truncate text-sm font-medium">{group.title}</p>
354
+ <p className="truncate text-sm font-medium">{item.title}</p>
329
355
  <p className="mt-0.5 truncate text-xs text-muted-foreground">
330
- {group.outcome}
331
- <span aria-hidden="true"> · </span>
332
- {group.checks} {formatAuditCountLabel(group.checks)}
356
+ {formatItemOutcome(item.outcome, t)}
333
357
  </p>
334
358
  </div>
335
359
  <span className="hidden shrink-0 text-[11px] text-muted-foreground sm:block">
336
- {formatAuditSource(group.source)}
360
+ {formatAuditSource(item.source)}
337
361
  </span>
338
- <AuditStatus status={group.status} compact />
362
+ <AuditStatus status={item.status} compact />
339
363
  <IconChevronDown className="size-4 shrink-0 text-muted-foreground transition-transform duration-200 ease-[var(--ease-collapse)] group-open:rotate-180 motion-reduce:transition-none" />
340
364
  </summary>
341
365
  <div className="bg-muted/20 px-4 pb-4 pt-3">
342
366
  <div className="grid gap-3 sm:grid-cols-[minmax(0,1fr)_auto]">
343
367
  <div>
344
368
  <p className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
345
- {rationale ? "Why" : "What happened"}
369
+ {item.rationale || item.dispatchError
370
+ ? t("factoryRoute.auditWhy")
371
+ : t("factoryRoute.auditWhatHappened")}
346
372
  </p>
347
373
  <p className="mt-1 max-w-[72ch] text-sm leading-6">
348
- {rationale ??
349
- "The source was inspected, but no action was taken."}
374
+ {item.dispatchError ??
375
+ item.rationale ??
376
+ t("factoryRoute.auditInspectedOnly")}
350
377
  </p>
351
378
  </div>
352
- <AuditDecisionFacts event={decision} />
379
+ <AuditDecisionFacts item={item} />
353
380
  </div>
354
381
 
355
- <div className="mt-4 rounded-md bg-background/40 p-3">
356
- <p className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
357
- Trace
358
- </p>
359
- <div className="mt-2 space-y-2">
360
- {group.events.map((event) => (
361
- <div
362
- key={event.id}
363
- className="flex items-center justify-between gap-3 text-xs text-muted-foreground"
364
- >
365
- <span className="flex min-w-0 items-center gap-2">
366
- <AuditStatus status={event.status} compact />
367
- <span className="truncate">
368
- {formatAuditAction(event.action)}
382
+ {item.events.length > 0 && (
383
+ <div className="mt-4 rounded-md bg-background/40 p-3">
384
+ <p className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
385
+ {t("factoryRoute.auditTrace")}
386
+ </p>
387
+ <div className="mt-2 space-y-2">
388
+ {item.events.map((entry) => (
389
+ <div
390
+ key={entry.id}
391
+ className="flex items-center justify-between gap-3 text-xs text-muted-foreground"
392
+ >
393
+ <span className="flex min-w-0 items-center gap-2">
394
+ <AuditStatus status={entry.status} compact />
395
+ <span className="truncate">
396
+ {formatAuditAction(entry.action)}
397
+ </span>
369
398
  </span>
370
- </span>
371
- <time className="shrink-0">
372
- {formatAuditAge(event.createdAt)}
373
- </time>
374
- </div>
375
- ))}
376
- </div>
377
- </div>
378
-
379
- {(group.itemId || sourceLink) && (
380
- <div className="mt-4 flex flex-wrap items-center gap-x-4 gap-y-2 bg-background/40 px-3 py-3 text-xs">
381
- {group.itemId && (
382
- <a
383
- href={`/factory?factoryId=${encodeURIComponent(factoryId)}&tab=inbox&itemId=${encodeURIComponent(group.itemId)}`}
384
- className="text-primary hover:underline"
385
- >
386
- {t("factoryRoute.auditOpenItem")}
387
- </a>
388
- )}
389
- {sourceLink && (
390
- <a
391
- href={sourceLink}
392
- target="_blank"
393
- rel="noreferrer"
394
- className="inline-flex items-center gap-1 text-primary hover:underline"
395
- >
396
- {t("factoryRoute.auditOpenSource")}
397
- <IconExternalLink className="size-3" />
398
- </a>
399
- )}
399
+ <time className="shrink-0">
400
+ {formatAuditAge(entry.createdAt)}
401
+ </time>
402
+ </div>
403
+ ))}
404
+ </div>
400
405
  </div>
401
406
  )}
407
+
408
+ <div className="mt-4 flex flex-wrap items-center gap-x-4 gap-y-2 bg-background/40 px-3 py-3 text-xs">
409
+ <a
410
+ href={`/factory?factoryId=${encodeURIComponent(factoryId)}&tab=inbox&itemId=${encodeURIComponent(item.itemId)}`}
411
+ className="text-primary hover:underline"
412
+ >
413
+ {t("factoryRoute.auditOpenItem")}
414
+ </a>
415
+ {sourceLink && (
416
+ <a
417
+ href={sourceLink}
418
+ target="_blank"
419
+ rel="noreferrer"
420
+ className="inline-flex items-center gap-1 text-primary hover:underline"
421
+ >
422
+ {t("factoryRoute.auditOpenSource")}
423
+ <IconExternalLink className="size-3" />
424
+ </a>
425
+ )}
426
+ </div>
402
427
  </div>
403
428
  </details>
404
429
  );
405
430
  }
406
431
 
407
- function AuditDecisionFacts({ event }: { event: FactoryAuditEvent | null }) {
432
+ function AuditDecisionFacts({ item }: { item: FactoryAuditItem }) {
408
433
  const t = useT();
409
- if (!event) return null;
410
- const clearBug = readBooleanDetail(event.details, "clearBug");
411
- const productUx = readBooleanDetail(event.details, "productUxImplications");
412
- const ownerArea = readStringDetail(event.details, "ownerOwnedArea");
413
- const guards = readGuardSummary(event.details.guardResults);
414
- if (clearBug === null && productUx === null && !ownerArea && !guards)
434
+ if (
435
+ item.clearBug === null &&
436
+ item.productUx === null &&
437
+ !item.ownerArea &&
438
+ !item.guards
439
+ ) {
415
440
  return null;
441
+ }
416
442
 
417
443
  return (
418
444
  <div className="flex flex-wrap content-start gap-x-3 gap-y-1 text-xs text-muted-foreground sm:max-w-[260px] sm:justify-end">
419
- {clearBug !== null && (
445
+ {item.clearBug !== null && (
420
446
  <AuditFact
421
447
  label={t("factoryRoute.auditClearBug")}
422
- value={yesNo(clearBug)}
448
+ value={yesNo(item.clearBug)}
423
449
  />
424
450
  )}
425
- {productUx !== null && (
451
+ {item.productUx !== null && (
426
452
  <AuditFact
427
453
  label={t("factoryRoute.auditUxImpact")}
428
- value={yesNo(productUx)}
454
+ value={yesNo(item.productUx)}
429
455
  />
430
456
  )}
431
- {ownerArea && (
432
- <AuditFact label={t("factoryRoute.auditOwnerArea")} value={ownerArea} />
457
+ {item.ownerArea && (
458
+ <AuditFact
459
+ label={t("factoryRoute.auditOwnerArea")}
460
+ value={item.ownerArea}
461
+ />
433
462
  )}
434
- {guards && (
435
- <AuditFact label={t("factoryRoute.auditGuardsLabel")} value={guards} />
463
+ {item.guards && (
464
+ <AuditFact
465
+ label={t("factoryRoute.auditGuardsLabel")}
466
+ value={item.guards}
467
+ />
436
468
  )}
437
469
  </div>
438
470
  );
@@ -502,117 +534,51 @@ function AuditSkeleton({ rows }: { rows: number }) {
502
534
  );
503
535
  }
504
536
 
505
- function groupAuditEvents(events: FactoryAuditEvent[]): AuditItemGroup[] {
506
- const grouped = new Map<string, FactoryAuditEvent[]>();
507
- for (const event of events) {
508
- const key = event.itemId ?? `event:${event.id}`;
509
- const current = grouped.get(key) ?? [];
510
- current.push(event);
511
- grouped.set(key, current);
537
+ function runHeadlineStatus(run: FactoryAuditRun): string {
538
+ if (run.status === "error" || (run.counts?.failed ?? 0) > 0) return "error";
539
+ if (run.status === "running") return "running";
540
+ if ((run.counts?.held ?? 0) > 0 && (run.counts?.dispatched ?? 0) === 0) {
541
+ return "skipped";
512
542
  }
513
-
514
- return [...grouped.entries()]
515
- .map(([key, groupEvents]) => {
516
- const decision =
517
- groupEvents.find((event) => event.kind === "decision") ?? null;
518
- const externalAction =
519
- groupEvents.find((event) => event.kind === "external_action") ?? null;
520
- const titleEvent =
521
- groupEvents.find((event) => event.action === "get-triage-item") ??
522
- groupEvents.find(
523
- (event) => event.action === "get-slack-feedback-context",
524
- ) ??
525
- groupEvents.find((event) => event.action === "list-triage-items") ??
526
- groupEvents.find((event) => event.kind === "observed") ??
527
- groupEvents[0];
528
- const latestAt = groupEvents.reduce(
529
- (latest, event) =>
530
- new Date(event.createdAt).getTime() > new Date(latest).getTime()
531
- ? event.createdAt
532
- : latest,
533
- groupEvents[0].createdAt,
534
- );
535
-
536
- return {
537
- key,
538
- events: groupEvents,
539
- itemId: titleEvent.itemId,
540
- source: titleEvent.source,
541
- sourceUrl: titleEvent.sourceUrl,
542
- title: formatAuditSubject(titleEvent),
543
- outcome: formatAuditOutcome(groupEvents, decision, externalAction),
544
- status: formatGroupStatus(groupEvents, decision, externalAction),
545
- latestAt,
546
- checks: groupEvents.filter(
547
- (event) => event.kind === "read" || event.kind === "observed",
548
- ).length,
549
- decision,
550
- externalAction,
551
- };
552
- })
553
- .sort(
554
- (left, right) =>
555
- new Date(right.latestAt).getTime() - new Date(left.latestAt).getTime(),
556
- );
543
+ return run.status;
557
544
  }
558
545
 
559
- function formatAuditSubject(event: FactoryAuditEvent): string {
560
- if (event.action === "get-slack-feedback-context") {
561
- const messageCount = readNumberDetail(event.details, "messageCount");
562
- return `Slack thread${messageCount === null ? "" : ` · ${messageCount} ${messageCount === 1 ? "message" : "messages"}`}`;
563
- }
564
- const summary = event.summary.trim();
565
- const subject = summary.replace(/^(Inspected|Read)\s+/i, "");
566
- return truncateAuditText(subject || formatAuditAction(event.action), 110);
567
- }
568
-
569
- function formatAuditOutcome(
570
- events: FactoryAuditEvent[],
571
- decision: FactoryAuditEvent | null,
572
- externalAction: FactoryAuditEvent | null,
546
+ function formatRunHeadline(
547
+ counts: FactoryAuditCounts | undefined,
548
+ t: ReturnType<typeof useT>,
573
549
  ): string {
574
- if (externalAction) {
575
- const provider = readStringDetail(externalAction.details, "provider");
576
- if (provider === "bot-tag") return "Builder tagged in Slack";
577
- if (provider === "builder-http") return "Builder fix submitted";
578
- return "Action taken";
579
- }
580
- if (decision) {
581
- const ownerArea = readStringDetail(decision.details, "ownerOwnedArea");
582
- const productUx = readBooleanDetail(
583
- decision.details,
584
- "productUxImplications",
550
+ if (!counts) return "";
551
+ const parts: string[] = [];
552
+ if (counts.newlyObserved > 0) {
553
+ parts.push(
554
+ t("factoryRoute.auditObserved", { count: counts.newlyObserved }),
585
555
  );
586
- if (decision.status === "skipped" || ownerArea || productUx === true) {
587
- return "Held for review";
588
- }
589
- if (readBooleanDetail(decision.details, "clearBug") === true) {
590
- return "Builder fix selected";
591
- }
592
- return "Decision recorded";
556
+ } else {
557
+ parts.push(t("factoryRoute.auditNoNew"));
593
558
  }
594
- if (
595
- events.some(
596
- (event) =>
597
- event.kind === "observed" && /no new|no result/i.test(event.summary),
598
- )
599
- ) {
600
- return "No new items";
559
+ if (counts.scanned > 0 && counts.scanned !== counts.newlyObserved) {
560
+ parts.push(t("factoryRoute.auditScanned", { count: counts.scanned }));
561
+ }
562
+ if (counts.failed > 0) {
563
+ parts.push(t("factoryRoute.auditFailed", { count: counts.failed }));
564
+ }
565
+ if (counts.dispatched > 0) {
566
+ parts.push(t("factoryRoute.auditDispatched", { count: counts.dispatched }));
601
567
  }
602
- return "Inspected only";
568
+ if (counts.held > 0) {
569
+ parts.push(t("factoryRoute.auditHeld", { count: counts.held }));
570
+ }
571
+ return parts.join(" · ");
603
572
  }
604
573
 
605
- function formatGroupStatus(
606
- events: FactoryAuditEvent[],
607
- decision: FactoryAuditEvent | null,
608
- externalAction: FactoryAuditEvent | null,
574
+ function formatItemOutcome(
575
+ outcome: FactoryAuditItem["outcome"],
576
+ t: ReturnType<typeof useT>,
609
577
  ): string {
610
- if (events.some((event) => event.status === "error")) return "error";
611
- if (externalAction) return externalAction.status;
612
- if (decision) return decision.status;
613
- return events.some((event) => event.status === "running")
614
- ? "running"
615
- : "success";
578
+ if (outcome === "failed") return t("factoryRoute.auditOutcomeFailed");
579
+ if (outcome === "dispatched") return t("factoryRoute.auditOutcomeDispatched");
580
+ if (outcome === "held") return t("factoryRoute.auditOutcomeHeld");
581
+ return t("factoryRoute.auditOutcomeInspected");
616
582
  }
617
583
 
618
584
  function formatAuditSource(source: string | null): string {
@@ -623,14 +589,9 @@ function formatAuditSource(source: string | null): string {
623
589
  return source ? formatAuditLabel(source) : "Factory";
624
590
  }
625
591
 
626
- function resolveAuditSourceLink(group: AuditItemGroup): string | null {
627
- const storedUrl =
628
- group.sourceUrl ??
629
- group.events.find((event) => event.sourceUrl)?.sourceUrl ??
630
- null;
631
- if (storedUrl) return storedUrl;
632
-
633
- for (const event of group.events) {
592
+ function resolveAuditSourceLink(item: FactoryAuditItem): string | null {
593
+ if (item.sourceUrl) return item.sourceUrl;
594
+ for (const event of item.events) {
634
595
  const channelId = readStringDetail(event.details, "channelId");
635
596
  const threadTs = readStringDetail(event.details, "threadTs");
636
597
  if (channelId && threadTs) return slackThreadUrl(channelId, threadTs);
@@ -643,6 +604,12 @@ function slackThreadUrl(channelId: string, threadTs: string): string {
643
604
  return `https://slack.com/archives/${encodeURIComponent(channelId)}/p${compactTs}?thread_ts=${encodeURIComponent(threadTs)}`;
644
605
  }
645
606
 
607
+ function automationLabel(run: FactoryAuditRun): string {
608
+ if (run.displayName) return run.displayName;
609
+ const segments = run.automation.split("/");
610
+ return formatAutomationName(segments[segments.length - 1] || run.automation);
611
+ }
612
+
646
613
  function formatAutomationName(value: string): string {
647
614
  const words = value
648
615
  .replace(/^factory-/, "")
@@ -665,10 +632,6 @@ function formatAuditAction(value: string): string {
665
632
  return formatAuditLabel(value).replace(/^Poll /, "Check ");
666
633
  }
667
634
 
668
- function formatAuditCountLabel(value: number): string {
669
- return value === 1 ? "check" : "checks";
670
- }
671
-
672
635
  function formatAuditAge(value: string | number) {
673
636
  const date = new Date(value);
674
637
  if (Number.isNaN(date.getTime())) return String(value);
@@ -695,24 +658,6 @@ function formatAuditLabel(value: string): string {
695
658
  .replace(/\b\w/g, (letter) => letter.toUpperCase());
696
659
  }
697
660
 
698
- function truncateAuditText(value: string, maxLength: number): string {
699
- return value.length > maxLength ? `${value.slice(0, maxLength - 1)}…` : value;
700
- }
701
-
702
- function readBooleanDetail(
703
- details: Record<string, unknown>,
704
- key: string,
705
- ): boolean | null {
706
- return typeof details[key] === "boolean" ? details[key] : null;
707
- }
708
-
709
- function readNumberDetail(
710
- details: Record<string, unknown>,
711
- key: string,
712
- ): number | null {
713
- return typeof details[key] === "number" ? details[key] : null;
714
- }
715
-
716
661
  function readStringDetail(
717
662
  details: Record<string, unknown>,
718
663
  key: string,
@@ -720,19 +665,6 @@ function readStringDetail(
720
665
  return typeof details[key] === "string" && details[key] ? details[key] : null;
721
666
  }
722
667
 
723
- function readGuardSummary(value: unknown): string | null {
724
- if (!Array.isArray(value) || value.length === 0) return null;
725
- const passed = value.filter(
726
- (guard): guard is { passed: boolean } =>
727
- typeof guard === "object" &&
728
- guard !== null &&
729
- "passed" in guard &&
730
- typeof guard.passed === "boolean" &&
731
- guard.passed,
732
- ).length;
733
- return `${passed}/${value.length} passed`;
734
- }
735
-
736
668
  function yesNo(value: boolean): string {
737
669
  return value ? "yes" : "no";
738
670
  }