@agent-native/core 0.92.0 → 0.92.2

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 (375) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +20 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/application-state/store.ts +12 -1
  5. package/corpus/core/src/cli/sync-builder-starter-manifest.ts +29 -1
  6. package/corpus/core/src/client/AssistantChat.tsx +27 -14
  7. package/corpus/core/src/client/PoweredByBadge.tsx +1 -8
  8. package/corpus/core/src/client/agent-chat-adapter.ts +37 -10
  9. package/corpus/core/src/client/chat/widgets/DataTableWidget.tsx +52 -1
  10. package/corpus/core/src/client/db-admin/TableEditor.tsx +49 -0
  11. package/corpus/core/src/client/db-admin/export-utils.ts +13 -4
  12. package/corpus/core/src/client/db-admin/useAgentSync.ts +72 -0
  13. package/corpus/core/src/client/error-capture.ts +69 -7
  14. package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +5 -1
  15. package/corpus/core/src/client/extensions/ExtensionViewer.tsx +53 -8
  16. package/corpus/core/src/client/extensions/InlineExtensionFrame.tsx +5 -1
  17. package/corpus/core/src/client/extensions/portable-extension.ts +1 -0
  18. package/corpus/core/src/client/org/OrgSwitcher.tsx +12 -9
  19. package/corpus/core/src/client/org/TeamPage.tsx +77 -71
  20. package/corpus/core/src/client/settings/SettingsPanel.tsx +205 -52
  21. package/corpus/core/src/client/settings/SettingsSection.tsx +8 -1
  22. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +25 -9
  23. package/corpus/core/src/db-admin/agent-tools.ts +8 -2
  24. package/corpus/core/src/db-admin/operations.ts +116 -4
  25. package/corpus/core/src/db-admin/routes.ts +1 -0
  26. package/corpus/core/src/db-admin/types.ts +4 -0
  27. package/corpus/core/src/extensions/html-shell.ts +5 -4
  28. package/corpus/core/src/extensions/routes.ts +10 -0
  29. package/corpus/core/src/navigation/index.ts +1 -1
  30. package/corpus/core/src/notifications/channels.ts +1 -22
  31. package/corpus/core/src/resources/handlers.ts +10 -8
  32. package/corpus/core/src/server/poll.ts +293 -8
  33. package/corpus/core/src/styles/agent-native.css +27 -0
  34. package/corpus/core/src/templates/default/.agents/skills/actions/SKILL.md +5 -0
  35. package/corpus/core/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
  36. package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  37. package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +7 -0
  38. package/corpus/core/src/templates/default/.agents/skills/storing-data/SKILL.md +5 -1
  39. package/corpus/core/src/templates/default/AGENTS.md +4 -2
  40. package/corpus/core/src/templates/headless/AGENTS.md +4 -0
  41. package/corpus/core/src/templates/workspace-core/.agents/skills/actions/SKILL.md +5 -0
  42. package/corpus/core/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
  43. package/corpus/core/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +4 -0
  44. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -0
  45. package/corpus/core/src/templates/workspace-core/.agents/skills/performance/SKILL.md +4 -0
  46. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  47. package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +7 -0
  48. package/corpus/core/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +5 -1
  49. package/corpus/core/src/templates/workspace-core/AGENTS.md +7 -1
  50. package/corpus/core/src/templates/workspace-root/AGENTS.md +7 -1
  51. package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +5 -0
  52. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +10 -0
  53. package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +1 -1
  54. package/corpus/templates/analytics/.agents/skills/security/SKILL.md +7 -0
  55. package/corpus/templates/analytics/.agents/skills/storing-data/SKILL.md +5 -1
  56. package/corpus/templates/analytics/AGENTS.md +33 -14
  57. package/corpus/templates/analytics/actions/capture-test-error.ts +4 -0
  58. package/corpus/templates/analytics/actions/get-analytics-alert-rule-defaults.ts +23 -0
  59. package/corpus/templates/analytics/actions/get-error-issue.ts +1 -1
  60. package/corpus/templates/analytics/actions/list-analysis-revisions.ts +40 -0
  61. package/corpus/templates/analytics/actions/list-dashboard-revisions.ts +40 -0
  62. package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +299 -0
  63. package/corpus/templates/analytics/actions/navigate.ts +4 -2
  64. package/corpus/templates/analytics/actions/restore-analysis-revision.ts +43 -0
  65. package/corpus/templates/analytics/actions/restore-dashboard-revision.ts +68 -0
  66. package/corpus/templates/analytics/actions/save-analytics-alert-rule.ts +14 -0
  67. package/corpus/templates/analytics/actions/save-monitor.ts +1 -1
  68. package/corpus/templates/analytics/actions/view-screen.ts +24 -0
  69. package/corpus/templates/analytics/app/components/analysis/AnalysisHistoryPanel.tsx +181 -0
  70. package/corpus/templates/analytics/app/components/dashboard/DashboardHistoryPanel.tsx +184 -0
  71. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +22 -16
  72. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -1
  73. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +27 -21
  74. package/corpus/templates/analytics/app/hooks/use-analysis-revisions.ts +43 -0
  75. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +110 -0
  76. package/corpus/templates/analytics/app/hooks/use-dashboard-revisions.ts +43 -0
  77. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +8 -3
  78. package/corpus/templates/analytics/app/i18n/zh-TW.ts +64 -2
  79. package/corpus/templates/analytics/app/i18n-data.ts +783 -3
  80. package/corpus/templates/analytics/app/pages/Agents.tsx +359 -19
  81. package/corpus/templates/analytics/app/pages/Settings.tsx +3 -2
  82. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +34 -1
  83. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +28 -0
  84. package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +18 -1
  85. package/corpus/templates/analytics/app/pages/monitoring/ErrorsPanel.tsx +7 -10
  86. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueDetail.tsx +479 -23
  87. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueList.tsx +0 -9
  88. package/corpus/templates/analytics/app/pages/monitoring/errors/i18n.ts +11 -0
  89. package/corpus/templates/analytics/app/pages/monitoring/errors/types.ts +9 -1
  90. package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorFormPage.tsx +6 -3
  91. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +26 -30
  92. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +9 -99
  93. package/corpus/templates/analytics/app/pages/settings/AlertRulesSettingsCard.tsx +262 -104
  94. package/corpus/templates/analytics/app/routes/team.tsx +1 -1
  95. package/corpus/templates/analytics/changelog/2026-07-08-admins-can-audit-dashboard-usage-ownership-and-cleanup-signa.md +6 -0
  96. package/corpus/templates/analytics/changelog/2026-07-08-agent-monitoring-stays-available-to-non-admins.md +6 -0
  97. package/corpus/templates/analytics/changelog/2026-07-08-alert-emails-now-show-the-message-without-raw-metadata-json.md +6 -0
  98. package/corpus/templates/analytics/changelog/2026-07-08-alert-rules-are-easier-to-scan-expand-and-edit-from-settings.md +6 -0
  99. package/corpus/templates/analytics/changelog/2026-07-08-analytics-alert-forms-remember-last-email-recipients.md +6 -0
  100. package/corpus/templates/analytics/changelog/2026-07-08-analytics-alert-rules-can-now-use-per-rule-slack-and-webhook.md +6 -0
  101. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-history-and-chat-context.md +5 -0
  102. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-usage-now-counts-explorer-dashboard-traffic.md +6 -0
  103. package/corpus/templates/analytics/changelog/2026-07-08-demo-mode-session-emails-are-filtered-and-anonymized.md +6 -0
  104. package/corpus/templates/analytics/changelog/2026-07-08-error-issue-detail-adds-stack-traces-and-frequency-bars.md +6 -0
  105. package/corpus/templates/analytics/changelog/2026-07-08-error-issue-detail-now-shows-the-latest-occurrence-message-a.md +6 -0
  106. package/corpus/templates/analytics/changelog/2026-07-08-monitoring-errors-only-show-test-action-when-empty.md +6 -0
  107. package/corpus/templates/analytics/changelog/2026-07-08-production-session-replay-now-requires-private-blob-storage-.md +6 -0
  108. package/corpus/templates/analytics/changelog/2026-07-08-shared-extension-links-now-show-a-clear-message-when-you-do-.md +6 -0
  109. package/corpus/templates/analytics/changelog/2026-07-08-team-settings-show-access-controls-next-to-member-lists.md +6 -0
  110. package/corpus/templates/analytics/changelog/2026-07-08-uptime-checks-no-longer-count-ssrf-setup-time-against-the-re.md +6 -0
  111. package/corpus/templates/analytics/changelog/2026-07-08-uptime-checks-reuse-one-keep-alive-connection-so-recorded-la.md +6 -0
  112. package/corpus/templates/analytics/changelog/2026-07-08-uptime-monitors-now-confirm-transient-timeout-failures-befor.md +6 -0
  113. package/corpus/templates/analytics/changelog/2026-07-08-uptime-monitors-now-use-a-10-second-request-timeout-by-defau.md +6 -0
  114. package/corpus/templates/analytics/docs/uptime-monitoring.md +30 -18
  115. package/corpus/templates/analytics/server/db/schema-monitoring.ts +1 -1
  116. package/corpus/templates/analytics/server/db/schema.ts +56 -0
  117. package/corpus/templates/analytics/server/lib/analytics-alerts.ts +92 -0
  118. package/corpus/templates/analytics/server/lib/dashboards-store.ts +334 -5
  119. package/corpus/templates/analytics/server/lib/error-capture.ts +231 -36
  120. package/corpus/templates/analytics/server/lib/session-replay.ts +114 -8
  121. package/corpus/templates/analytics/server/lib/uptime-monitors.ts +244 -33
  122. package/corpus/templates/analytics/server/plugins/db.ts +112 -2
  123. package/corpus/templates/analytics/server/routes/api/dashboard-agent-context.json.get.ts +1 -0
  124. package/corpus/templates/assets/.agents/skills/actions/SKILL.md +5 -0
  125. package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +1 -1
  126. package/corpus/templates/assets/.agents/skills/security/SKILL.md +7 -0
  127. package/corpus/templates/assets/.agents/skills/storing-data/SKILL.md +5 -1
  128. package/corpus/templates/assets/AGENTS.md +4 -0
  129. package/corpus/templates/assets/app/routes/settings.tsx +1 -1
  130. package/corpus/templates/assets/app/routes/team.tsx +1 -1
  131. package/corpus/templates/brain/.agents/skills/actions/SKILL.md +5 -0
  132. package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +5 -0
  133. package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +1 -1
  134. package/corpus/templates/brain/.agents/skills/security/SKILL.md +7 -0
  135. package/corpus/templates/brain/AGENTS.md +4 -0
  136. package/corpus/templates/brain/app/routes/team.tsx +1 -1
  137. package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +5 -0
  138. package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +1 -1
  139. package/corpus/templates/calendar/.agents/skills/security/SKILL.md +7 -0
  140. package/corpus/templates/calendar/.agents/skills/storing-data/SKILL.md +5 -1
  141. package/corpus/templates/calendar/AGENTS.md +4 -0
  142. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +11 -2
  143. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +21 -8
  144. package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +7 -1
  145. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -2
  146. package/corpus/templates/calendar/app/lib/popover-click-guard.ts +48 -4
  147. package/corpus/templates/calendar/app/pages/Settings.tsx +1 -1
  148. package/corpus/templates/calendar/app/routes/_app.team.tsx +1 -1
  149. package/corpus/templates/calendar/changelog/2026-07-08-clicking-empty-calendar-space-closes-an-open-event-popover-b.md +6 -0
  150. package/corpus/templates/chat/.agents/skills/actions/SKILL.md +5 -0
  151. package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +5 -0
  152. package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
  153. package/corpus/templates/chat/.agents/skills/security/SKILL.md +7 -0
  154. package/corpus/templates/chat/AGENTS.md +4 -0
  155. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  156. package/corpus/templates/chat/app/routes/settings.tsx +2 -2
  157. package/corpus/templates/chat/app/routes/team.tsx +1 -1
  158. package/corpus/templates/clips/.agents/skills/actions/SKILL.md +5 -0
  159. package/corpus/templates/clips/.agents/skills/security/SKILL.md +7 -0
  160. package/corpus/templates/clips/AGENTS.md +4 -0
  161. package/corpus/templates/clips/actions/finalize-recording.ts +9 -3
  162. package/corpus/templates/clips/actions/set-thumbnail.ts +11 -2
  163. package/corpus/templates/clips/app/components/meetings/canvas-editor.tsx +1 -7
  164. package/corpus/templates/clips/app/components/player/player-controls.tsx +21 -13
  165. package/corpus/templates/clips/app/components/player/video-player.tsx +30 -75
  166. package/corpus/templates/clips/app/i18n/ar-SA.ts +6 -5
  167. package/corpus/templates/clips/app/i18n/de-DE.ts +6 -6
  168. package/corpus/templates/clips/app/i18n/en-US.ts +6 -5
  169. package/corpus/templates/clips/app/i18n/es-ES.ts +6 -5
  170. package/corpus/templates/clips/app/i18n/fr-FR.ts +6 -6
  171. package/corpus/templates/clips/app/i18n/hi-IN.ts +6 -5
  172. package/corpus/templates/clips/app/i18n/ja-JP.ts +6 -5
  173. package/corpus/templates/clips/app/i18n/ko-KR.ts +6 -5
  174. package/corpus/templates/clips/app/i18n/pt-BR.ts +6 -5
  175. package/corpus/templates/clips/app/i18n/zh-CN.ts +6 -5
  176. package/corpus/templates/clips/app/i18n/zh-TW.ts +6 -5
  177. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +51 -118
  178. package/corpus/templates/clips/changelog/2026-07-08-hosted-clip-uploads-now-clear-failed-buffered-chunks.md +6 -0
  179. package/corpus/templates/clips/changelog/2026-07-08-hosted-recording-uploads-now-avoid-sql-chunk-storage.md +6 -0
  180. package/corpus/templates/clips/changelog/2026-07-08-meeting-recaps-now-keep-summaries-action-items-and-transcrip.md +6 -0
  181. package/corpus/templates/clips/changelog/2026-07-08-video-playback-controls-now-match-loom-style-5-second-skips-.md +6 -0
  182. package/corpus/templates/clips/server/lib/recording-upload-state.ts +63 -0
  183. package/corpus/templates/clips/server/lib/video-storage.ts +4 -0
  184. package/corpus/templates/clips/server/routes/api/recordings/[recordingId]/thumbnail.post.ts +19 -13
  185. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +40 -1
  186. package/corpus/templates/content/.agents/skills/actions/SKILL.md +5 -0
  187. package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +1 -1
  188. package/corpus/templates/content/.agents/skills/security/SKILL.md +7 -0
  189. package/corpus/templates/content/.agents/skills/storing-data/SKILL.md +5 -1
  190. package/corpus/templates/content/AGENTS.md +4 -0
  191. package/corpus/templates/content/app/routes/_app.settings.tsx +2 -2
  192. package/corpus/templates/content/app/routes/_app.team.tsx +1 -1
  193. package/corpus/templates/design/.agents/skills/actions/SKILL.md +5 -0
  194. package/corpus/templates/design/.agents/skills/security/SKILL.md +7 -0
  195. package/corpus/templates/design/AGENTS.md +4 -0
  196. package/corpus/templates/design/actions/take-design-screenshot.ts +12 -9
  197. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +57 -13
  198. package/corpus/templates/design/app/routes/team.tsx +1 -1
  199. package/corpus/templates/design/changelog/2026-07-08-local-images-added-to-designs-now-upload-to-file-storage-ins.md +6 -0
  200. package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +5 -0
  201. package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +7 -0
  202. package/corpus/templates/dispatch/AGENTS.md +4 -0
  203. package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +1 -1
  204. package/corpus/templates/dispatch/app/routes/settings.tsx +2 -2
  205. package/corpus/templates/dispatch/app/routes/team.tsx +1 -1
  206. package/corpus/templates/forms/.agents/skills/actions/SKILL.md +5 -0
  207. package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +1 -1
  208. package/corpus/templates/forms/.agents/skills/security/SKILL.md +7 -0
  209. package/corpus/templates/forms/.agents/skills/storing-data/SKILL.md +5 -1
  210. package/corpus/templates/forms/AGENTS.md +4 -0
  211. package/corpus/templates/forms/app/routes/_app.settings.tsx +2 -2
  212. package/corpus/templates/forms/app/routes/_app.team.tsx +1 -1
  213. package/corpus/templates/forms/shared/navigation.ts +1 -1
  214. package/corpus/templates/macros/.agents/skills/security/SKILL.md +7 -0
  215. package/corpus/templates/macros/AGENTS.md +4 -0
  216. package/corpus/templates/macros/app/routes/settings.tsx +2 -2
  217. package/corpus/templates/macros/app/routes/team.tsx +1 -1
  218. package/corpus/templates/mail/.agents/skills/actions/SKILL.md +5 -0
  219. package/corpus/templates/mail/.agents/skills/email-drafts/SKILL.md +1 -1
  220. package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +1 -1
  221. package/corpus/templates/mail/.agents/skills/security/SKILL.md +7 -0
  222. package/corpus/templates/mail/.agents/skills/storing-data/SKILL.md +5 -1
  223. package/corpus/templates/mail/AGENTS.md +4 -0
  224. package/corpus/templates/mail/changelog/2026-07-08-mail-attachments-in-hosted-environments-now-require-file-sto.md +6 -0
  225. package/corpus/templates/mail/server/handlers/media.ts +26 -1
  226. package/corpus/templates/mail/server/lib/outgoing-email.ts +24 -1
  227. package/corpus/templates/mail/server/lib/upload-store.ts +2 -1
  228. package/corpus/templates/plan/.agents/skills/actions/SKILL.md +5 -0
  229. package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +5 -0
  230. package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +1 -1
  231. package/corpus/templates/plan/.agents/skills/security/SKILL.md +7 -0
  232. package/corpus/templates/plan/AGENTS.md +4 -0
  233. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +1 -1
  234. package/corpus/templates/plan/app/routes/settings.tsx +2 -2
  235. package/corpus/templates/plan/app/routes/team.tsx +1 -1
  236. package/corpus/templates/plan/changelog/2026-07-08-hosted-plan-images-now-require-connected-storage.md +6 -0
  237. package/corpus/templates/plan/server/lib/plan-assets.ts +33 -4
  238. package/corpus/templates/slides/.agents/skills/actions/SKILL.md +5 -0
  239. package/corpus/templates/slides/.agents/skills/security/SKILL.md +7 -0
  240. package/corpus/templates/slides/AGENTS.md +4 -0
  241. package/corpus/templates/slides/actions/generate-image-api.ts +15 -3
  242. package/corpus/templates/slides/app/routes/settings.tsx +2 -2
  243. package/corpus/templates/slides/app/routes/team.tsx +1 -1
  244. package/corpus/templates/slides/changelog/2026-07-08-generated-slide-images-now-return-hosted-file-storage-urls-i.md +6 -0
  245. package/corpus/templates/slides/shared/api.ts +1 -1
  246. package/dist/application-state/store.d.ts.map +1 -1
  247. package/dist/application-state/store.js +8 -2
  248. package/dist/application-state/store.js.map +1 -1
  249. package/dist/cli/sync-builder-starter-manifest.d.ts.map +1 -1
  250. package/dist/cli/sync-builder-starter-manifest.js +24 -1
  251. package/dist/cli/sync-builder-starter-manifest.js.map +1 -1
  252. package/dist/client/AssistantChat.d.ts.map +1 -1
  253. package/dist/client/AssistantChat.js +24 -13
  254. package/dist/client/AssistantChat.js.map +1 -1
  255. package/dist/client/PoweredByBadge.d.ts +1 -1
  256. package/dist/client/PoweredByBadge.d.ts.map +1 -1
  257. package/dist/client/PoweredByBadge.js +3 -9
  258. package/dist/client/PoweredByBadge.js.map +1 -1
  259. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  260. package/dist/client/agent-chat-adapter.js +28 -9
  261. package/dist/client/agent-chat-adapter.js.map +1 -1
  262. package/dist/client/chat/widgets/DataTableWidget.d.ts.map +1 -1
  263. package/dist/client/chat/widgets/DataTableWidget.js +19 -2
  264. package/dist/client/chat/widgets/DataTableWidget.js.map +1 -1
  265. package/dist/client/db-admin/TableEditor.d.ts.map +1 -1
  266. package/dist/client/db-admin/TableEditor.js +19 -2
  267. package/dist/client/db-admin/TableEditor.js.map +1 -1
  268. package/dist/client/db-admin/export-utils.d.ts +2 -0
  269. package/dist/client/db-admin/export-utils.d.ts.map +1 -1
  270. package/dist/client/db-admin/export-utils.js +11 -3
  271. package/dist/client/db-admin/export-utils.js.map +1 -1
  272. package/dist/client/db-admin/useAgentSync.d.ts.map +1 -1
  273. package/dist/client/db-admin/useAgentSync.js +63 -0
  274. package/dist/client/db-admin/useAgentSync.js.map +1 -1
  275. package/dist/client/error-capture.d.ts.map +1 -1
  276. package/dist/client/error-capture.js +41 -7
  277. package/dist/client/error-capture.js.map +1 -1
  278. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  279. package/dist/client/extensions/EmbeddedExtension.js +3 -1
  280. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  281. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
  282. package/dist/client/extensions/ExtensionViewer.js +32 -5
  283. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  284. package/dist/client/extensions/InlineExtensionFrame.d.ts.map +1 -1
  285. package/dist/client/extensions/InlineExtensionFrame.js +3 -1
  286. package/dist/client/extensions/InlineExtensionFrame.js.map +1 -1
  287. package/dist/client/extensions/portable-extension.d.ts.map +1 -1
  288. package/dist/client/extensions/portable-extension.js +1 -0
  289. package/dist/client/extensions/portable-extension.js.map +1 -1
  290. package/dist/client/org/OrgSwitcher.d.ts +1 -1
  291. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  292. package/dist/client/org/OrgSwitcher.js +11 -6
  293. package/dist/client/org/OrgSwitcher.js.map +1 -1
  294. package/dist/client/org/TeamPage.d.ts.map +1 -1
  295. package/dist/client/org/TeamPage.js +5 -5
  296. package/dist/client/org/TeamPage.js.map +1 -1
  297. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  298. package/dist/client/settings/SettingsPanel.js +37 -14
  299. package/dist/client/settings/SettingsPanel.js.map +1 -1
  300. package/dist/client/settings/SettingsSection.js +1 -1
  301. package/dist/client/settings/SettingsSection.js.map +1 -1
  302. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  303. package/dist/client/settings/SettingsTabsPage.js +21 -10
  304. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  305. package/dist/collab/routes.d.ts +1 -1
  306. package/dist/db-admin/agent-tools.d.ts.map +1 -1
  307. package/dist/db-admin/agent-tools.js +7 -2
  308. package/dist/db-admin/agent-tools.js.map +1 -1
  309. package/dist/db-admin/operations.d.ts.map +1 -1
  310. package/dist/db-admin/operations.js +90 -4
  311. package/dist/db-admin/operations.js.map +1 -1
  312. package/dist/db-admin/routes.d.ts.map +1 -1
  313. package/dist/db-admin/routes.js +1 -0
  314. package/dist/db-admin/routes.js.map +1 -1
  315. package/dist/db-admin/types.d.ts +4 -0
  316. package/dist/db-admin/types.d.ts.map +1 -1
  317. package/dist/db-admin/types.js.map +1 -1
  318. package/dist/extensions/html-shell.d.ts +5 -4
  319. package/dist/extensions/html-shell.d.ts.map +1 -1
  320. package/dist/extensions/html-shell.js.map +1 -1
  321. package/dist/extensions/routes.d.ts.map +1 -1
  322. package/dist/extensions/routes.js +8 -0
  323. package/dist/extensions/routes.js.map +1 -1
  324. package/dist/navigation/index.d.ts +1 -1
  325. package/dist/navigation/index.d.ts.map +1 -1
  326. package/dist/navigation/index.js +1 -1
  327. package/dist/navigation/index.js.map +1 -1
  328. package/dist/notifications/channels.js +1 -17
  329. package/dist/notifications/channels.js.map +1 -1
  330. package/dist/notifications/routes.d.ts +1 -1
  331. package/dist/resources/handlers.d.ts +5 -0
  332. package/dist/resources/handlers.d.ts.map +1 -1
  333. package/dist/resources/handlers.js +9 -8
  334. package/dist/resources/handlers.js.map +1 -1
  335. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  336. package/dist/server/poll.d.ts +12 -4
  337. package/dist/server/poll.d.ts.map +1 -1
  338. package/dist/server/poll.js +236 -9
  339. package/dist/server/poll.js.map +1 -1
  340. package/dist/styles/agent-native.css +27 -0
  341. package/dist/templates/default/.agents/skills/actions/SKILL.md +5 -0
  342. package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
  343. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  344. package/dist/templates/default/.agents/skills/security/SKILL.md +7 -0
  345. package/dist/templates/default/.agents/skills/storing-data/SKILL.md +5 -1
  346. package/dist/templates/default/AGENTS.md +4 -2
  347. package/dist/templates/headless/AGENTS.md +4 -0
  348. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +5 -0
  349. package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
  350. package/dist/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +4 -0
  351. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -0
  352. package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +4 -0
  353. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  354. package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +7 -0
  355. package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +5 -1
  356. package/dist/templates/workspace-core/AGENTS.md +7 -1
  357. package/dist/templates/workspace-root/AGENTS.md +7 -1
  358. package/package.json +1 -1
  359. package/src/templates/default/.agents/skills/actions/SKILL.md +5 -0
  360. package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
  361. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  362. package/src/templates/default/.agents/skills/security/SKILL.md +7 -0
  363. package/src/templates/default/.agents/skills/storing-data/SKILL.md +5 -1
  364. package/src/templates/default/AGENTS.md +4 -2
  365. package/src/templates/headless/AGENTS.md +4 -0
  366. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +5 -0
  367. package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
  368. package/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +4 -0
  369. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -0
  370. package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +4 -0
  371. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  372. package/src/templates/workspace-core/.agents/skills/security/SKILL.md +7 -0
  373. package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +5 -1
  374. package/src/templates/workspace-core/AGENTS.md +7 -1
  375. package/src/templates/workspace-root/AGENTS.md +7 -1
@@ -27,7 +27,7 @@ import {
27
27
  * - `o:<orgId>:sql-dashboard-{id}` → kind='sql', owner=caller, visibility='org'
28
28
  * - `adhoc-analysis-{id}` → owner=caller, legacy visibility from its source key
29
29
  */
30
- import { and, eq, isNotNull, isNull } from "drizzle-orm";
30
+ import { and, desc, eq, isNotNull, isNull } from "drizzle-orm";
31
31
 
32
32
  import { getDb, schema } from "../db/index.js";
33
33
 
@@ -44,6 +44,7 @@ export interface DashboardRecord {
44
44
  visibility: "private" | "org" | "public";
45
45
  createdAt: string;
46
46
  updatedAt: string;
47
+ updatedBy: string | null;
47
48
  /** ISO timestamp set when the dashboard is archived. Null = active. */
48
49
  archivedAt: string | null;
49
50
  /** ISO timestamp set when the dashboard is hidden from default navigation. */
@@ -55,6 +56,16 @@ export interface DashboardRecord {
55
56
  canManage?: boolean;
56
57
  }
57
58
 
59
+ export interface DashboardRevisionRecord {
60
+ id: string;
61
+ dashboardId: string;
62
+ kind: DashboardKind;
63
+ title: string;
64
+ config: Record<string, unknown>;
65
+ createdAt: string;
66
+ createdBy: string | null;
67
+ }
68
+
58
69
  export type DashboardArchiveFilter = "active" | "archived" | "all";
59
70
  export type DashboardHiddenFilter = "visible" | "hidden" | "all";
60
71
 
@@ -82,6 +93,20 @@ export interface AnalysisRecord {
82
93
  canManage?: boolean;
83
94
  }
84
95
 
96
+ export interface AnalysisRevisionRecord {
97
+ id: string;
98
+ analysisId: string;
99
+ name: string;
100
+ description: string;
101
+ question: string;
102
+ instructions: string;
103
+ dataSources: string[];
104
+ resultMarkdown: string;
105
+ resultData: Record<string, unknown> | null;
106
+ createdAt: string;
107
+ createdBy: string | null;
108
+ }
109
+
85
110
  interface AccessCtx {
86
111
  email: string;
87
112
  orgId: string | null;
@@ -90,6 +115,8 @@ interface AccessCtx {
90
115
  const SQL_PREFIX = "sql-dashboard-";
91
116
  const EXPLORER_PREFIX = "dashboard-";
92
117
  const ANALYSIS_PREFIX = "adhoc-analysis-";
118
+ const DASHBOARD_REVISION_LIMIT = 50;
119
+ const ANALYSIS_REVISION_LIMIT = 30;
93
120
 
94
121
  function nowIso(): string {
95
122
  return new Date().toISOString();
@@ -157,6 +184,7 @@ function rowToDashboard(row: any, role?: AccessRole): DashboardRecord {
157
184
  visibility: row.visibility,
158
185
  createdAt: row.createdAt,
159
186
  updatedAt: row.updatedAt,
187
+ updatedBy: row.updatedBy ?? null,
160
188
  archivedAt: row.archivedAt ?? null,
161
189
  hiddenAt: row.hiddenAt ?? null,
162
190
  hiddenBy: row.hiddenBy ?? null,
@@ -164,6 +192,19 @@ function rowToDashboard(row: any, role?: AccessRole): DashboardRecord {
164
192
  };
165
193
  }
166
194
 
195
+ function rowToDashboardRevision(row: any): DashboardRevisionRecord {
196
+ return {
197
+ id: row.id,
198
+ dashboardId: row.dashboardId,
199
+ kind: row.kind,
200
+ title: row.title,
201
+ config:
202
+ typeof row.config === "string" ? JSON.parse(row.config) : row.config,
203
+ createdAt: row.createdAt,
204
+ createdBy: row.createdBy ?? null,
205
+ };
206
+ }
207
+
167
208
  function configFromSettings(data: Record<string, unknown>): {
168
209
  title: string;
169
210
  config: Record<string, unknown>;
@@ -208,6 +249,7 @@ async function migrateDashboardFromSettings(
208
249
  visibility,
209
250
  createdAt,
210
251
  updatedAt,
252
+ updatedBy: ownerEmail,
211
253
  })
212
254
  .onConflictDoNothing();
213
255
  // guard:allow-unscoped — read-after-write of the row just inserted above
@@ -384,6 +426,42 @@ export async function listDashboards(
384
426
  return out;
385
427
  }
386
428
 
429
+ async function pruneDashboardRevisions(
430
+ db: any,
431
+ dashboardId: string,
432
+ ): Promise<void> {
433
+ const rows = await db
434
+ .select({ id: schema.dashboardRevisions.id })
435
+ .from(schema.dashboardRevisions)
436
+ .where(eq(schema.dashboardRevisions.dashboardId, dashboardId))
437
+ .orderBy(desc(schema.dashboardRevisions.createdAt));
438
+ const stale = rows.slice(DASHBOARD_REVISION_LIMIT);
439
+ for (const row of stale) {
440
+ await db
441
+ .delete(schema.dashboardRevisions)
442
+ .where(eq(schema.dashboardRevisions.id, row.id));
443
+ }
444
+ }
445
+
446
+ async function snapshotDashboardRevision(
447
+ db: any,
448
+ dashboard: DashboardRecord,
449
+ ctx: AccessCtx,
450
+ ): Promise<void> {
451
+ await db.insert(schema.dashboardRevisions).values({
452
+ id: `dashrev-${Date.now()}-${nanoidFallback()}`,
453
+ dashboardId: dashboard.id,
454
+ kind: dashboard.kind,
455
+ title: dashboard.title,
456
+ config: JSON.stringify(dashboard.config),
457
+ createdAt: nowIso(),
458
+ createdBy: ctx.email,
459
+ ownerEmail: dashboard.ownerEmail,
460
+ orgId: dashboard.orgId,
461
+ });
462
+ await pruneDashboardRevisions(db, dashboard.id);
463
+ }
464
+
387
465
  /**
388
466
  * Upsert a dashboard. On create, caller becomes owner and visibility defaults
389
467
  * to `private`; users explicitly promote useful dashboards to org/public via
@@ -399,18 +477,25 @@ export async function upsertDashboard(
399
477
  const existing = await getDashboard(id, ctx);
400
478
  const db = getDb() as any;
401
479
  const { title, config } = configFromSettings(body);
480
+ const configJson = JSON.stringify(config);
402
481
  if (existing) {
403
482
  await assertAccess("dashboard", id, "editor", {
404
483
  userEmail: ctx.email,
405
484
  orgId: ctx.orgId ?? undefined,
406
485
  });
486
+ const changed =
487
+ existing.kind !== kind ||
488
+ existing.title !== title ||
489
+ JSON.stringify(existing.config) !== configJson;
490
+ if (changed) await snapshotDashboardRevision(db, existing, ctx);
407
491
  await db
408
492
  .update(schema.dashboards)
409
493
  .set({
410
494
  kind,
411
495
  title,
412
- config: JSON.stringify(config),
496
+ config: configJson,
413
497
  updatedAt: nowIso(),
498
+ updatedBy: ctx.email,
414
499
  })
415
500
  .where(eq(schema.dashboards.id, id));
416
501
  } else {
@@ -422,6 +507,7 @@ export async function upsertDashboard(
422
507
  ownerEmail: ctx.email,
423
508
  orgId: ctx.orgId,
424
509
  visibility: "private",
510
+ updatedBy: ctx.email,
425
511
  });
426
512
  }
427
513
  const [row] = await db
@@ -442,6 +528,77 @@ export async function upsertDashboard(
442
528
  return dashboard;
443
529
  }
444
530
 
531
+ export async function listDashboardRevisions(
532
+ dashboardId: string,
533
+ ctx: AccessCtx,
534
+ ): Promise<DashboardRevisionRecord[]> {
535
+ const existing = await getDashboard(dashboardId, ctx);
536
+ if (!existing) return [];
537
+ await assertAccess("dashboard", dashboardId, "viewer", {
538
+ userEmail: ctx.email,
539
+ orgId: ctx.orgId ?? undefined,
540
+ });
541
+ const db = getDb() as any;
542
+ const rows = await db
543
+ .select()
544
+ .from(schema.dashboardRevisions)
545
+ .where(eq(schema.dashboardRevisions.dashboardId, dashboardId))
546
+ .orderBy(desc(schema.dashboardRevisions.createdAt))
547
+ .limit(DASHBOARD_REVISION_LIMIT);
548
+ return rows.map(rowToDashboardRevision);
549
+ }
550
+
551
+ export async function restoreDashboardRevision(
552
+ dashboardId: string,
553
+ revisionId: string,
554
+ ctx: AccessCtx,
555
+ ): Promise<DashboardRecord | null> {
556
+ const existing = await getDashboard(dashboardId, ctx);
557
+ if (!existing) return null;
558
+ await assertAccess("dashboard", dashboardId, "editor", {
559
+ userEmail: ctx.email,
560
+ orgId: ctx.orgId ?? undefined,
561
+ });
562
+ const db = getDb() as any;
563
+ const [revisionRow] = await db
564
+ .select()
565
+ .from(schema.dashboardRevisions)
566
+ .where(
567
+ and(
568
+ eq(schema.dashboardRevisions.id, revisionId),
569
+ eq(schema.dashboardRevisions.dashboardId, dashboardId),
570
+ ),
571
+ )
572
+ .limit(1);
573
+ if (!revisionRow) return null;
574
+ const revision = rowToDashboardRevision(revisionRow);
575
+ await snapshotDashboardRevision(db, existing, ctx);
576
+ await db
577
+ .update(schema.dashboards)
578
+ .set({
579
+ kind: revision.kind,
580
+ title: revision.title,
581
+ config: JSON.stringify(revision.config),
582
+ updatedAt: nowIso(),
583
+ updatedBy: ctx.email,
584
+ })
585
+ .where(eq(schema.dashboards.id, dashboardId));
586
+ const [row] = await db
587
+ .select()
588
+ .from(schema.dashboards)
589
+ .where(eq(schema.dashboards.id, dashboardId));
590
+ const dashboard = rowToDashboard(row);
591
+ recordScopedChange(
592
+ "dashboards",
593
+ "change",
594
+ dashboard.id,
595
+ dashboard.ownerEmail,
596
+ dashboard.orgId,
597
+ dashboard.visibility,
598
+ );
599
+ return dashboard;
600
+ }
601
+
445
602
  /**
446
603
  * Archive a dashboard (soft-delete). Requires editor. The row stays in the
447
604
  * dashboards table with `archived_at` set, so it disappears from the default
@@ -462,7 +619,7 @@ export async function archiveDashboard(
462
619
  const now = nowIso();
463
620
  await db
464
621
  .update(schema.dashboards)
465
- .set({ archivedAt: now, updatedAt: now })
622
+ .set({ archivedAt: now, updatedAt: now, updatedBy: ctx.email })
466
623
  .where(eq(schema.dashboards.id, id));
467
624
  const [row] = await db
468
625
  .select()
@@ -495,7 +652,7 @@ export async function unarchiveDashboard(
495
652
  const db = getDb() as any;
496
653
  await db
497
654
  .update(schema.dashboards)
498
- .set({ archivedAt: null, updatedAt: nowIso() })
655
+ .set({ archivedAt: null, updatedAt: nowIso(), updatedBy: ctx.email })
499
656
  .where(eq(schema.dashboards.id, id));
500
657
  const [row] = await db
501
658
  .select()
@@ -533,7 +690,12 @@ export async function hideDashboard(
533
690
  const now = nowIso();
534
691
  await db
535
692
  .update(schema.dashboards)
536
- .set({ hiddenAt: now, hiddenBy: ctx.email, updatedAt: now })
693
+ .set({
694
+ hiddenAt: now,
695
+ hiddenBy: ctx.email,
696
+ updatedAt: now,
697
+ updatedBy: ctx.email,
698
+ })
537
699
  .where(eq(schema.dashboards.id, id));
538
700
  const [row] = await db
539
701
  .select()
@@ -572,6 +734,7 @@ export async function unhideDashboard(
572
734
  hiddenAt: null,
573
735
  hiddenBy: null,
574
736
  updatedAt: now,
737
+ updatedBy: ctx.email,
575
738
  };
576
739
  if (!existing.ownerEmail) {
577
740
  patch.ownerEmail = ctx.email;
@@ -609,6 +772,9 @@ export async function removeDashboard(
609
772
  });
610
773
  const db = getDb() as any;
611
774
  await db.delete(schema.dashboards).where(eq(schema.dashboards.id, id));
775
+ await db
776
+ .delete(schema.dashboardRevisions)
777
+ .where(eq(schema.dashboardRevisions.dashboardId, id));
612
778
  await db
613
779
  .delete(schema.dashboardShares)
614
780
  .where(eq(schema.dashboardShares.resourceId, id));
@@ -658,6 +824,22 @@ function rowToAnalysis(row: any, role?: AccessRole): AnalysisRecord {
658
824
  };
659
825
  }
660
826
 
827
+ function rowToAnalysisRevision(row: any): AnalysisRevisionRecord {
828
+ return {
829
+ id: row.id,
830
+ analysisId: row.analysisId,
831
+ name: row.name,
832
+ description: row.description,
833
+ question: row.question,
834
+ instructions: row.instructions,
835
+ dataSources: safeJsonParse(row.dataSources, []),
836
+ resultMarkdown: row.resultMarkdown,
837
+ resultData: row.resultData ? safeJsonParse(row.resultData, null) : null,
838
+ createdAt: row.createdAt,
839
+ createdBy: row.createdBy ?? null,
840
+ };
841
+ }
842
+
661
843
  function safeJsonParse<T>(s: unknown, fallback: T): T {
662
844
  if (typeof s !== "string") return fallback;
663
845
  try {
@@ -880,6 +1062,48 @@ export async function listAnalyses(
880
1062
  return out;
881
1063
  }
882
1064
 
1065
+ async function pruneAnalysisRevisions(
1066
+ db: any,
1067
+ analysisId: string,
1068
+ ): Promise<void> {
1069
+ const rows = await db
1070
+ .select({ id: schema.analysisRevisions.id })
1071
+ .from(schema.analysisRevisions)
1072
+ .where(eq(schema.analysisRevisions.analysisId, analysisId))
1073
+ .orderBy(desc(schema.analysisRevisions.createdAt));
1074
+ const stale = rows.slice(ANALYSIS_REVISION_LIMIT);
1075
+ for (const row of stale) {
1076
+ await db
1077
+ .delete(schema.analysisRevisions)
1078
+ .where(eq(schema.analysisRevisions.id, row.id));
1079
+ }
1080
+ }
1081
+
1082
+ async function snapshotAnalysisRevision(
1083
+ db: any,
1084
+ analysis: AnalysisRecord,
1085
+ ctx: AccessCtx,
1086
+ ): Promise<void> {
1087
+ await db.insert(schema.analysisRevisions).values({
1088
+ id: `analysisrev-${Date.now()}-${nanoidFallback()}`,
1089
+ analysisId: analysis.id,
1090
+ name: analysis.name,
1091
+ description: analysis.description,
1092
+ question: analysis.question,
1093
+ instructions: analysis.instructions,
1094
+ dataSources: JSON.stringify(analysis.dataSources),
1095
+ resultMarkdown: analysis.resultMarkdown,
1096
+ resultData: analysis.resultData
1097
+ ? JSON.stringify(analysis.resultData)
1098
+ : null,
1099
+ createdAt: nowIso(),
1100
+ createdBy: ctx.email,
1101
+ ownerEmail: analysis.ownerEmail,
1102
+ orgId: analysis.orgId,
1103
+ });
1104
+ await pruneAnalysisRevisions(db, analysis.id);
1105
+ }
1106
+
883
1107
  export async function upsertAnalysis(
884
1108
  id: string,
885
1109
  body: {
@@ -913,6 +1137,32 @@ export async function upsertAnalysis(
913
1137
  patch.resultData = body.resultData
914
1138
  ? JSON.stringify(body.resultData)
915
1139
  : null;
1140
+ const next = {
1141
+ name: (patch.name as string | undefined) ?? existing.name,
1142
+ description:
1143
+ (patch.description as string | undefined) ?? existing.description,
1144
+ question: (patch.question as string | undefined) ?? existing.question,
1145
+ instructions:
1146
+ (patch.instructions as string | undefined) ?? existing.instructions,
1147
+ dataSources:
1148
+ body.dataSources !== undefined
1149
+ ? body.dataSources
1150
+ : existing.dataSources,
1151
+ resultMarkdown:
1152
+ (patch.resultMarkdown as string | undefined) ?? existing.resultMarkdown,
1153
+ resultData:
1154
+ body.resultData !== undefined ? body.resultData : existing.resultData,
1155
+ };
1156
+ const changed =
1157
+ next.name !== existing.name ||
1158
+ next.description !== existing.description ||
1159
+ next.question !== existing.question ||
1160
+ next.instructions !== existing.instructions ||
1161
+ JSON.stringify(next.dataSources) !==
1162
+ JSON.stringify(existing.dataSources) ||
1163
+ next.resultMarkdown !== existing.resultMarkdown ||
1164
+ JSON.stringify(next.resultData) !== JSON.stringify(existing.resultData);
1165
+ if (changed) await snapshotAnalysisRevision(db, existing, ctx);
916
1166
  await db
917
1167
  .update(schema.analyses)
918
1168
  .set(patch)
@@ -944,6 +1194,82 @@ export async function upsertAnalysis(
944
1194
  return rowToAnalysis(row);
945
1195
  }
946
1196
 
1197
+ export async function listAnalysisRevisions(
1198
+ analysisId: string,
1199
+ ctx: AccessCtx,
1200
+ ): Promise<AnalysisRevisionRecord[]> {
1201
+ const existing = await getAnalysis(analysisId, ctx);
1202
+ if (!existing) return [];
1203
+ await assertAccess("analysis", analysisId, "viewer", {
1204
+ userEmail: ctx.email,
1205
+ orgId: ctx.orgId ?? undefined,
1206
+ });
1207
+ const db = getDb() as any;
1208
+ const rows = await db
1209
+ .select()
1210
+ .from(schema.analysisRevisions)
1211
+ .where(eq(schema.analysisRevisions.analysisId, analysisId))
1212
+ .orderBy(desc(schema.analysisRevisions.createdAt))
1213
+ .limit(ANALYSIS_REVISION_LIMIT);
1214
+ return rows.map(rowToAnalysisRevision);
1215
+ }
1216
+
1217
+ export async function restoreAnalysisRevision(
1218
+ analysisId: string,
1219
+ revisionId: string,
1220
+ ctx: AccessCtx,
1221
+ ): Promise<AnalysisRecord | null> {
1222
+ const existing = await getAnalysis(analysisId, ctx);
1223
+ if (!existing) return null;
1224
+ await assertAccess("analysis", analysisId, "editor", {
1225
+ userEmail: ctx.email,
1226
+ orgId: ctx.orgId ?? undefined,
1227
+ });
1228
+ const db = getDb() as any;
1229
+ const [revisionRow] = await db
1230
+ .select()
1231
+ .from(schema.analysisRevisions)
1232
+ .where(
1233
+ and(
1234
+ eq(schema.analysisRevisions.id, revisionId),
1235
+ eq(schema.analysisRevisions.analysisId, analysisId),
1236
+ ),
1237
+ )
1238
+ .limit(1);
1239
+ if (!revisionRow) return null;
1240
+ const revision = rowToAnalysisRevision(revisionRow);
1241
+ await snapshotAnalysisRevision(db, existing, ctx);
1242
+ await db
1243
+ .update(schema.analyses)
1244
+ .set({
1245
+ name: revision.name,
1246
+ description: revision.description,
1247
+ question: revision.question,
1248
+ instructions: revision.instructions,
1249
+ dataSources: JSON.stringify(revision.dataSources),
1250
+ resultMarkdown: revision.resultMarkdown,
1251
+ resultData: revision.resultData
1252
+ ? JSON.stringify(revision.resultData)
1253
+ : null,
1254
+ updatedAt: nowIso(),
1255
+ })
1256
+ .where(eq(schema.analyses.id, analysisId));
1257
+ const [row] = await db
1258
+ .select()
1259
+ .from(schema.analyses)
1260
+ .where(eq(schema.analyses.id, analysisId));
1261
+ const analysis = rowToAnalysis(row);
1262
+ recordScopedChange(
1263
+ "analyses",
1264
+ "change",
1265
+ analysis.id,
1266
+ analysis.ownerEmail,
1267
+ analysis.orgId,
1268
+ analysis.visibility,
1269
+ );
1270
+ return analysis;
1271
+ }
1272
+
947
1273
  export async function removeAnalysis(
948
1274
  id: string,
949
1275
  ctx: AccessCtx,
@@ -956,6 +1282,9 @@ export async function removeAnalysis(
956
1282
  });
957
1283
  const db = getDb() as any;
958
1284
  await db.delete(schema.analyses).where(eq(schema.analyses.id, id));
1285
+ await db
1286
+ .delete(schema.analysisRevisions)
1287
+ .where(eq(schema.analysisRevisions.analysisId, id));
959
1288
  await db
960
1289
  .delete(schema.analysisShares)
961
1290
  .where(eq(schema.analysisShares.resourceId, id));