@agent-native/core 0.92.11 → 0.93.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 (777) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +45 -0
  3. package/corpus/core/docs/content/automation-connectors.mdx +80 -0
  4. package/corpus/core/docs/content/extensions.mdx +8 -0
  5. package/corpus/core/docs/content/locales/ar-SA/extensions.mdx +8 -0
  6. package/corpus/core/docs/content/locales/ar-SA/messaging.mdx +84 -15
  7. package/corpus/core/docs/content/locales/ar-SA/template-clips.mdx +1 -0
  8. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +1 -0
  9. package/corpus/core/docs/content/locales/de-DE/extensions.mdx +10 -0
  10. package/corpus/core/docs/content/locales/de-DE/messaging.mdx +92 -15
  11. package/corpus/core/docs/content/locales/de-DE/template-clips.mdx +1 -0
  12. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +1 -0
  13. package/corpus/core/docs/content/locales/es-ES/extensions.mdx +10 -0
  14. package/corpus/core/docs/content/locales/es-ES/messaging.mdx +91 -15
  15. package/corpus/core/docs/content/locales/es-ES/template-clips.mdx +1 -0
  16. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +1 -0
  17. package/corpus/core/docs/content/locales/fr-FR/extensions.mdx +10 -0
  18. package/corpus/core/docs/content/locales/fr-FR/messaging.mdx +91 -15
  19. package/corpus/core/docs/content/locales/fr-FR/template-clips.mdx +1 -0
  20. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +1 -0
  21. package/corpus/core/docs/content/locales/hi-IN/extensions.mdx +8 -0
  22. package/corpus/core/docs/content/locales/hi-IN/messaging.mdx +81 -15
  23. package/corpus/core/docs/content/locales/hi-IN/template-clips.mdx +1 -0
  24. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +1 -0
  25. package/corpus/core/docs/content/locales/ja-JP/extensions.mdx +9 -0
  26. package/corpus/core/docs/content/locales/ja-JP/messaging.mdx +81 -15
  27. package/corpus/core/docs/content/locales/ja-JP/template-clips.mdx +1 -0
  28. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +1 -0
  29. package/corpus/core/docs/content/locales/ko-KR/extensions.mdx +8 -0
  30. package/corpus/core/docs/content/locales/ko-KR/messaging.mdx +80 -15
  31. package/corpus/core/docs/content/locales/ko-KR/template-clips.mdx +1 -0
  32. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +1 -0
  33. package/corpus/core/docs/content/locales/pt-BR/extensions.mdx +9 -0
  34. package/corpus/core/docs/content/locales/pt-BR/messaging.mdx +88 -15
  35. package/corpus/core/docs/content/locales/pt-BR/template-clips.mdx +1 -0
  36. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +1 -0
  37. package/corpus/core/docs/content/locales/zh-CN/extensions.mdx +6 -0
  38. package/corpus/core/docs/content/locales/zh-CN/messaging.mdx +78 -15
  39. package/corpus/core/docs/content/locales/zh-CN/template-clips.mdx +1 -0
  40. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +1 -0
  41. package/corpus/core/docs/content/locales/zh-TW/extensions.mdx +6 -0
  42. package/corpus/core/docs/content/locales/zh-TW/messaging.mdx +78 -15
  43. package/corpus/core/docs/content/locales/zh-TW/template-clips.mdx +1 -0
  44. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +1 -0
  45. package/corpus/core/docs/content/messaging.mdx +227 -45
  46. package/corpus/core/docs/content/template-clips.mdx +3 -0
  47. package/corpus/core/docs/content/template-design.mdx +4 -0
  48. package/corpus/core/package.json +5 -1
  49. package/corpus/core/src/a2a/artifact-response.ts +131 -2
  50. package/corpus/core/src/a2a/client.ts +10 -0
  51. package/corpus/core/src/a2a/handlers.ts +40 -76
  52. package/corpus/core/src/a2a/types.ts +2 -0
  53. package/corpus/core/src/agent/durable-background.ts +24 -0
  54. package/corpus/core/src/agent/production-agent.ts +48 -5
  55. package/corpus/core/src/agent/run-manager.ts +171 -20
  56. package/corpus/core/src/agent/run-store.ts +297 -42
  57. package/corpus/core/src/agent/types.ts +23 -0
  58. package/corpus/core/src/audit/record.ts +31 -0
  59. package/corpus/core/src/audit/store.ts +69 -3
  60. package/corpus/core/src/audit/types.ts +13 -0
  61. package/corpus/core/src/automation/index.ts +802 -0
  62. package/corpus/core/src/cli/create.ts +12 -5
  63. package/corpus/core/src/cli/design-connect.ts +596 -36
  64. package/corpus/core/src/client/AgentPanel.tsx +51 -6
  65. package/corpus/core/src/client/AssistantChat.tsx +274 -147
  66. package/corpus/core/src/client/MultiTabAssistantChat.tsx +3 -0
  67. package/corpus/core/src/client/RunStuckBanner.tsx +69 -20
  68. package/corpus/core/src/client/agent-chat-adapter.ts +110 -17
  69. package/corpus/core/src/client/automation.ts +22 -0
  70. package/corpus/core/src/client/chat/run-recovery.tsx +67 -50
  71. package/corpus/core/src/client/composer/TiptapComposer.tsx +8 -0
  72. package/corpus/core/src/client/index.ts +30 -0
  73. package/corpus/core/src/client/integrations/api.ts +295 -0
  74. package/corpus/core/src/client/integrations/index.ts +26 -0
  75. package/corpus/core/src/client/transcription/use-live-transcription.ts +68 -5
  76. package/corpus/core/src/client/use-run-stuck-detection.ts +41 -3
  77. package/corpus/core/src/deploy/build.ts +59 -3
  78. package/corpus/core/src/deploy/workspace-deploy.ts +37 -2
  79. package/corpus/core/src/file-upload/types.ts +2 -0
  80. package/corpus/core/src/index.ts +37 -0
  81. package/corpus/core/src/integrations/a2a-continuations-store.ts +6 -4
  82. package/corpus/core/src/integrations/adapters/discord.ts +362 -0
  83. package/corpus/core/src/integrations/adapters/index.ts +29 -0
  84. package/corpus/core/src/integrations/adapters/microsoft-teams.ts +411 -0
  85. package/corpus/core/src/integrations/adapters/slack.ts +861 -27
  86. package/corpus/core/src/integrations/adapters/telegram.ts +54 -15
  87. package/corpus/core/src/integrations/adapters/whatsapp.ts +41 -17
  88. package/corpus/core/src/integrations/catalog.ts +571 -0
  89. package/corpus/core/src/integrations/controls-store.ts +187 -0
  90. package/corpus/core/src/integrations/index.ts +100 -0
  91. package/corpus/core/src/integrations/installations-store.ts +677 -0
  92. package/corpus/core/src/integrations/integration-memory.ts +187 -0
  93. package/corpus/core/src/integrations/pending-tasks-retry-job.ts +6 -6
  94. package/corpus/core/src/integrations/pending-tasks-store.ts +83 -5
  95. package/corpus/core/src/integrations/plugin.ts +685 -47
  96. package/corpus/core/src/integrations/scope-store.ts +498 -0
  97. package/corpus/core/src/integrations/slack-manifest.ts +78 -0
  98. package/corpus/core/src/integrations/slack-oauth.ts +343 -0
  99. package/corpus/core/src/integrations/types.ts +179 -0
  100. package/corpus/core/src/integrations/usage-budget-store.ts +906 -0
  101. package/corpus/core/src/integrations/webhook-handler.ts +562 -51
  102. package/corpus/core/src/jobs/scheduler.ts +100 -2
  103. package/corpus/core/src/jobs/tools.ts +45 -9
  104. package/corpus/core/src/provider-api/index.ts +72 -1
  105. package/corpus/core/src/resources/handlers.ts +88 -14
  106. package/corpus/core/src/resources/script-helpers.ts +36 -7
  107. package/corpus/core/src/resources/store.ts +59 -52
  108. package/corpus/core/src/scripts/call-agent.ts +107 -1
  109. package/corpus/core/src/server/agent-chat-plugin.ts +294 -119
  110. package/corpus/core/src/server/auth.ts +10 -14
  111. package/corpus/core/src/server/builder-design-systems.ts +16 -1
  112. package/corpus/core/src/server/edge.ts +18 -0
  113. package/corpus/core/src/server/index.ts +26 -0
  114. package/corpus/core/src/server/request-context.ts +18 -0
  115. package/corpus/core/src/server/social-og-image.ts +3 -2
  116. package/corpus/core/src/styles/agent-native.css +62 -44
  117. package/corpus/core/src/templates/default/.agents/skills/capture-learnings/SKILL.md +22 -9
  118. package/corpus/core/src/templates/default/AGENTS.md +7 -3
  119. package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +1 -1
  120. package/corpus/core/src/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +22 -9
  121. package/corpus/core/src/templates/workspace-core/.agents/skills/extension-points/SKILL.md +6 -3
  122. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +25 -0
  123. package/corpus/core/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +15 -0
  124. package/corpus/core/src/templates/workspace-core/AGENTS.md +3 -2
  125. package/corpus/core/src/usage/store.ts +37 -2
  126. package/corpus/templates/analytics/.agents/skills/capture-learnings/SKILL.md +22 -9
  127. package/corpus/templates/analytics/.agents/skills/provider-api/SKILL.md +23 -0
  128. package/corpus/templates/analytics/.builder/skills/provider-api/SKILL.md +15 -0
  129. package/corpus/templates/analytics/actions/save-monitor.ts +25 -2
  130. package/corpus/templates/analytics/app/lib/data-sources.ts +28 -0
  131. package/corpus/templates/analytics/changelog/2026-07-10-agent-created-uptime-monitors-now-return-a-direct-link-to-th.md +6 -0
  132. package/corpus/templates/analytics/changelog/2026-07-10-dashboard-email-reports-still-arrive-when-screenshot-capture-temporarily-fails.md +6 -0
  133. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +3 -7
  134. package/corpus/templates/analytics/server/lib/credential-keys.ts +12 -0
  135. package/corpus/templates/analytics/server/lib/dashboard-report.ts +5 -1
  136. package/corpus/templates/analytics/server/lib/provider-credentials.ts +2 -0
  137. package/corpus/templates/analytics/server/routes/api/test-connection.post.ts +16 -0
  138. package/corpus/templates/assets/.agents/skills/capture-learnings/SKILL.md +22 -9
  139. package/corpus/templates/brain/.agents/skills/capture-learnings/SKILL.md +22 -9
  140. package/corpus/templates/calendar/.agents/skills/capture-learnings/SKILL.md +22 -9
  141. package/corpus/templates/calendar/app/components/calendar/CommandPalette.tsx +98 -108
  142. package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +97 -90
  143. package/corpus/templates/calendar/changelog/2026-07-10-event-guest-details-and-option-menus-now-use-valid-accessibl.md +6 -0
  144. package/corpus/templates/calendar/changelog/2026-07-10-natural-language-event-phrases-stay-available-as-quick-creat.md +6 -0
  145. package/corpus/templates/chat/.agents/skills/capture-learnings/SKILL.md +22 -9
  146. package/corpus/templates/chat/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +6 -0
  147. package/corpus/templates/clips/.agents/skills/capture-learnings/SKILL.md +22 -9
  148. package/corpus/templates/clips/.agents/skills/recording/SKILL.md +17 -1
  149. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +8 -0
  150. package/corpus/templates/clips/AGENTS.md +8 -3
  151. package/corpus/templates/clips/actions/finalize-recording.ts +63 -23
  152. package/corpus/templates/clips/actions/get-recording-player-data.ts +6 -2
  153. package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +72 -9
  154. package/corpus/templates/clips/actions/list-recordings.ts +19 -3
  155. package/corpus/templates/clips/actions/navigate.ts +2 -0
  156. package/corpus/templates/clips/actions/reprocess-recording.ts +20 -6
  157. package/corpus/templates/clips/actions/request-transcript.ts +16 -31
  158. package/corpus/templates/clips/actions/view-screen.ts +57 -5
  159. package/corpus/templates/clips/app/components/layout/Header.tsx +1 -0
  160. package/corpus/templates/clips/app/components/library/empty-state.tsx +2 -0
  161. package/corpus/templates/clips/app/components/library/library-grid.tsx +69 -42
  162. package/corpus/templates/clips/app/components/library/library-layout.tsx +15 -1
  163. package/corpus/templates/clips/app/components/library/recording-card.tsx +81 -68
  164. package/corpus/templates/clips/app/components/library/search-bar.tsx +4 -3
  165. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +9 -3
  166. package/corpus/templates/clips/app/components/player/share-dialog.tsx +1 -1
  167. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +27 -5
  168. package/corpus/templates/clips/app/hooks/use-library.ts +1 -1
  169. package/corpus/templates/clips/app/hooks/use-navigation-state.ts +10 -2
  170. package/corpus/templates/clips/app/hooks/use-player-shortcuts.ts +1 -0
  171. package/corpus/templates/clips/app/i18n/ar-SA.ts +7 -1
  172. package/corpus/templates/clips/app/i18n/de-DE.ts +7 -1
  173. package/corpus/templates/clips/app/i18n/en-US.ts +7 -1
  174. package/corpus/templates/clips/app/i18n/es-ES.ts +7 -1
  175. package/corpus/templates/clips/app/i18n/fr-FR.ts +7 -1
  176. package/corpus/templates/clips/app/i18n/hi-IN.ts +7 -1
  177. package/corpus/templates/clips/app/i18n/ja-JP.ts +7 -1
  178. package/corpus/templates/clips/app/i18n/ko-KR.ts +7 -1
  179. package/corpus/templates/clips/app/i18n/pt-BR.ts +7 -1
  180. package/corpus/templates/clips/app/i18n/zh-CN.ts +8 -1
  181. package/corpus/templates/clips/app/i18n/zh-TW.ts +8 -1
  182. package/corpus/templates/clips/app/lib/recording-visibility.ts +70 -0
  183. package/corpus/templates/clips/app/routes/_app.shared.tsx +20 -0
  184. package/corpus/templates/clips/app/routes/download.tsx +24 -8
  185. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +115 -2
  186. package/corpus/templates/clips/app/routes/record.tsx +62 -0
  187. package/corpus/templates/clips/app/routes/share.$shareId.tsx +25 -16
  188. package/corpus/templates/clips/changelog/2026-07-10-clips-desktop-is-now-available-for-linux-with-appimage-debia.md +6 -0
  189. package/corpus/templates/clips/changelog/2026-07-10-clips-downloads-updates-sooner-in-the-background-and-offers-.md +6 -0
  190. package/corpus/templates/clips/changelog/2026-07-10-clips-shared-with-you-now-appear-in-a-searchable-shared-with.md +6 -0
  191. package/corpus/templates/clips/changelog/2026-07-10-clips-with-missing-mobile-video-frames-can-be-repaired-witho.md +6 -0
  192. package/corpus/templates/clips/changelog/2026-07-10-fixed-copying-agent-responses-and-transcript-text-from-recor.md +6 -0
  193. package/corpus/templates/clips/changelog/2026-07-10-mobile-camera-recordings-pause-while-backgrounded.md +6 -0
  194. package/corpus/templates/clips/changelog/2026-07-10-mp4-downloads-now-show-progress-immediately-after-the-option.md +6 -0
  195. package/corpus/templates/clips/changelog/2026-07-10-recording-pages-now-open-as-soon-as-you-stop-with-a-share-li.md +6 -0
  196. package/corpus/templates/clips/changelog/2026-07-10-recordings-now-seek-smoothly-downloads-use-the-correct-file-.md +6 -0
  197. package/corpus/templates/clips/changelog/2026-07-10-s3-compatible-storage-now-saves-hosted-recordings-without-re.md +6 -0
  198. package/corpus/templates/clips/desktop/README.md +22 -6
  199. package/corpus/templates/clips/desktop/package.json +1 -1
  200. package/corpus/templates/clips/desktop/src/app.tsx +32 -19
  201. package/corpus/templates/clips/desktop/src/components/ReadinessPanel.tsx +15 -9
  202. package/corpus/templates/clips/desktop/src/lib/finalization-guard.ts +22 -0
  203. package/corpus/templates/clips/desktop/src/lib/permissions.ts +3 -1
  204. package/corpus/templates/clips/desktop/src/lib/recorder.ts +97 -120
  205. package/corpus/templates/clips/desktop/src/lib/updater.ts +26 -4
  206. package/corpus/templates/clips/desktop/src/overlays/finalizing.tsx +4 -8
  207. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +1 -1
  208. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +2 -2
  209. package/corpus/templates/clips/desktop/src-tauri/src/permission_status.rs +11 -7
  210. package/corpus/templates/clips/desktop/src-tauri/tauri.linux.conf.json +11 -0
  211. package/corpus/templates/clips/netlify.toml +3 -0
  212. package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +106 -0
  213. package/corpus/templates/clips/server/lib/public-agent-context.ts +9 -1
  214. package/corpus/templates/clips/server/lib/resumable-upload-provider.ts +52 -0
  215. package/corpus/templates/clips/server/lib/s3-upload-provider.ts +433 -85
  216. package/corpus/templates/clips/server/lib/video-remux.ts +114 -0
  217. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +109 -0
  218. package/corpus/templates/clips/server/routes/api/clips-updater.json.get.ts +22 -11
  219. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +4 -2
  220. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +47 -5
  221. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +10 -2
  222. package/corpus/templates/clips/shared/share-loader-response.ts +12 -0
  223. package/corpus/templates/clips/shared/transcript-status.ts +40 -0
  224. package/corpus/templates/content/.agents/skills/capture-learnings/SKILL.md +22 -9
  225. package/corpus/templates/content/.agents/skills/content/SKILL.md +57 -5
  226. package/corpus/templates/content/AGENTS.md +65 -55
  227. package/corpus/templates/content/actions/_builder-cms-read-client.ts +92 -23
  228. package/corpus/templates/content/actions/_database-source-utils.ts +207 -41
  229. package/corpus/templates/content/actions/_property-utils.ts +30 -2
  230. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +316 -121
  231. package/corpus/templates/content/actions/attach-content-database-source.ts +41 -17
  232. package/corpus/templates/content/actions/change-content-database-source-role.ts +46 -8
  233. package/corpus/templates/content/actions/submit-content-database-form.ts +497 -0
  234. package/corpus/templates/content/actions/suggest-source-join-key.ts +8 -1
  235. package/corpus/templates/content/actions/update-content-database-view.ts +8 -1
  236. package/corpus/templates/content/actions/view-screen.ts +4 -1
  237. package/corpus/templates/content/agent-native.app-skill.json +1 -1
  238. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +250 -64
  239. package/corpus/templates/content/app/components/editor/database/FormView.tsx +460 -0
  240. package/corpus/templates/content/app/components/editor/database/navigation-state.ts +3 -0
  241. package/corpus/templates/content/app/components/editor/database/settings.tsx +30 -37
  242. package/corpus/templates/content/app/components/editor/database/shared.tsx +2 -0
  243. package/corpus/templates/content/app/components/editor/database/view-config.ts +29 -1
  244. package/corpus/templates/content/app/components/ui/textarea.tsx +1 -0
  245. package/corpus/templates/content/app/hooks/use-content-database.ts +22 -0
  246. package/corpus/templates/content/app/i18n/zh-TW.ts +21 -0
  247. package/corpus/templates/content/app/i18n-data.ts +225 -0
  248. package/corpus/templates/content/changelog/2026-07-09-builder-source-refreshes-now-keep-mapped-fields-such-as-topi.md +6 -0
  249. package/corpus/templates/content/changelog/2026-07-10-builder-backed-properties-now-load-current-source-values-as-.md +6 -0
  250. package/corpus/templates/content/changelog/2026-07-10-builder-database-refreshes-now-stay-explicit-and-accurate.md +6 -0
  251. package/corpus/templates/content/changelog/2026-07-10-database-form-views-now-collect-ordered-required-answers-and.md +6 -0
  252. package/corpus/templates/content/changelog/2026-07-10-database-tables-now-use-a-quieter-more-compact-layout-with-c.md +6 -0
  253. package/corpus/templates/content/changelog/2026-07-10-slack-intake-routes-to-content-database-forms.md +6 -0
  254. package/corpus/templates/content/parity/matrix.md +1 -0
  255. package/corpus/templates/content/parity/matrix.ts +18 -0
  256. package/corpus/templates/content/shared/api.ts +28 -1
  257. package/corpus/templates/content/shared/database-form.ts +53 -0
  258. package/corpus/templates/design/.agents/skills/capture-learnings/SKILL.md +22 -9
  259. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +26 -0
  260. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +85 -12
  261. package/corpus/templates/design/AGENTS.md +72 -18
  262. package/corpus/templates/design/DESIGN.md +1 -1
  263. package/corpus/templates/design/DEVELOPING.md +1 -1
  264. package/corpus/templates/design/FIGMA_INTEROPERABILITY.md +116 -0
  265. package/corpus/templates/design/actions/create-design-from-template.ts +220 -0
  266. package/corpus/templates/design/actions/create-design-system.ts +92 -16
  267. package/corpus/templates/design/actions/delete-design-template.ts +28 -0
  268. package/corpus/templates/design/actions/delete-file.ts +7 -0
  269. package/corpus/templates/design/actions/edit-design.ts +3 -0
  270. package/corpus/templates/design/actions/export-design-as-figma-svg.ts +137 -5
  271. package/corpus/templates/design/actions/export-pdf.ts +1 -1
  272. package/corpus/templates/design/actions/generate-design.ts +3 -0
  273. package/corpus/templates/design/actions/get-design-snapshot.ts +9 -0
  274. package/corpus/templates/design/actions/get-figma-connection-status.ts +26 -0
  275. package/corpus/templates/design/actions/get-figma-design-context.ts +138 -0
  276. package/corpus/templates/design/actions/get-figma-styles.ts +2 -2
  277. package/corpus/templates/design/actions/import-figma-clipboard.ts +112 -16
  278. package/corpus/templates/design/actions/import-figma-frame.ts +15 -4
  279. package/corpus/templates/design/actions/index-design-system-with-builder.ts +4 -0
  280. package/corpus/templates/design/actions/list-design-templates.ts +118 -0
  281. package/corpus/templates/design/actions/navigate.ts +16 -2
  282. package/corpus/templates/design/actions/provider-api-request.ts +11 -1
  283. package/corpus/templates/design/actions/save-design-as-template.ts +156 -0
  284. package/corpus/templates/design/actions/set-active-breakpoint.ts +13 -4
  285. package/corpus/templates/design/actions/take-design-screenshot.ts +73 -3
  286. package/corpus/templates/design/actions/update-file.ts +39 -22
  287. package/corpus/templates/design/actions/view-screen.ts +36 -1
  288. package/corpus/templates/design/actions/write-local-file.ts +50 -38
  289. package/corpus/templates/design/app/components/design/AutoLayoutSuggestionDialog.tsx +151 -0
  290. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +20 -0
  291. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +251 -22
  292. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +3 -3
  293. package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +384 -9
  294. package/corpus/templates/design/app/components/design/EditPanel.tsx +105 -32
  295. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +58 -28
  296. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +6 -0
  297. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +539 -53
  298. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +40 -0
  299. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +1 -0
  300. package/corpus/templates/design/app/components/design/code-workbench/explorer/ExplorerView.tsx +1 -0
  301. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +5 -1
  302. package/corpus/templates/design/app/components/design/code-workbench/workspace/create-providers.ts +2 -0
  303. package/corpus/templates/design/app/components/design/code-workbench/workspace/localhost-provider.ts +3 -1
  304. package/corpus/templates/design/app/components/design/code-workbench/workspace/types.ts +2 -0
  305. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +19 -0
  306. package/corpus/templates/design/app/components/design/edit-panel/frame-presets-panel.tsx +1 -0
  307. package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +27 -0
  308. package/corpus/templates/design/app/components/design/inspector/InteractionStatePanel.tsx +137 -81
  309. package/corpus/templates/design/app/components/design/inspector/frame-size-presets.ts +25 -5
  310. package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +1 -1
  311. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +220 -3
  312. package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +13 -0
  313. package/corpus/templates/design/app/components/design/multi-screen/types.ts +22 -1
  314. package/corpus/templates/design/app/components/design/types.ts +4 -0
  315. package/corpus/templates/design/app/components/design-system/ProductionDesignSystemShowcase.tsx +208 -0
  316. package/corpus/templates/design/app/components/editor/FigmaLinkComposerBubble.tsx +321 -0
  317. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +24 -4
  318. package/corpus/templates/design/app/components/layout/Layout.tsx +16 -2
  319. package/corpus/templates/design/app/components/layout/Sidebar.tsx +2 -0
  320. package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +72 -0
  321. package/corpus/templates/design/app/global.css +2 -35
  322. package/corpus/templates/design/app/hooks/use-navigation-state.ts +10 -0
  323. package/corpus/templates/design/app/i18n/zh-TW.ts +127 -5
  324. package/corpus/templates/design/app/i18n-breakpoints.ts +209 -0
  325. package/corpus/templates/design/app/i18n-data.ts +1317 -59
  326. package/corpus/templates/design/app/i18n-template-feature.ts +458 -0
  327. package/corpus/templates/design/app/lib/design-clipboard.ts +202 -0
  328. package/corpus/templates/design/app/lib/design-file-upload.ts +87 -0
  329. package/corpus/templates/design/app/lib/design-import.ts +168 -11
  330. package/corpus/templates/design/app/lib/desktop-design-preview.ts +423 -0
  331. package/corpus/templates/design/app/lib/figma-clipboard.ts +74 -8
  332. package/corpus/templates/design/app/lib/figma-connection.ts +161 -0
  333. package/corpus/templates/design/app/lib/figma-svg-copy.ts +814 -22
  334. package/corpus/templates/design/app/lib/figma-url.ts +77 -0
  335. package/corpus/templates/design/app/lib/pending-generation.ts +35 -0
  336. package/corpus/templates/design/app/pages/DesignSystems.tsx +236 -177
  337. package/corpus/templates/design/app/pages/Index.tsx +38 -0
  338. package/corpus/templates/design/app/pages/Templates.tsx +498 -0
  339. package/corpus/templates/design/app/pages/design-editor/auto-layout-suggestion.ts +328 -0
  340. package/corpus/templates/design/app/pages/design-editor/clone-idrefs.ts +113 -0
  341. package/corpus/templates/design/app/pages/design-editor/created-screen-navigation.ts +55 -0
  342. package/corpus/templates/design/app/pages/design-editor/editor-session.ts +32 -0
  343. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +49 -2
  344. package/corpus/templates/design/app/pages/design-editor/export-capture.ts +296 -0
  345. package/corpus/templates/design/app/pages/design-editor/history.ts +101 -2
  346. package/corpus/templates/design/app/pages/design-editor/layout-operations.ts +28 -1
  347. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +34 -0
  348. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +202 -6
  349. package/corpus/templates/design/app/pages/design-editor/pending-structure-verification.ts +92 -0
  350. package/corpus/templates/design/app/pages/design-editor/save-failure.ts +58 -0
  351. package/corpus/templates/design/app/routes/templates.tsx +4 -6
  352. package/corpus/templates/design/changelog/2026-07-10-app-workspaces-now-use-clean-borderless-main-surfaces-throug.md +6 -0
  353. package/corpus/templates/design/changelog/2026-07-10-ask-chat-what-s-in-a-figma-file-or-frame-structure-screensho.md +6 -0
  354. package/corpus/templates/design/changelog/2026-07-10-canvas-edits-now-reject-malformed-html-before-it-can-corrupt.md +6 -0
  355. package/corpus/templates/design/changelog/2026-07-10-complex-designs-now-export-as-valid-secure-svgs-and-crisp-ra.md +6 -0
  356. package/corpus/templates/design/changelog/2026-07-10-complex-figma-masks-arcs-rich-text-transformed-images-and-ad.md +6 -0
  357. package/corpus/templates/design/changelog/2026-07-10-copied-layers-paste-across-designs-and-browser-tabs.md +6 -0
  358. package/corpus/templates/design/changelog/2026-07-10-copy-and-download-editable-figma-svg.md +6 -0
  359. package/corpus/templates/design/changelog/2026-07-10-copying-design-text-now-pastes-readable-text-into-other-apps.md +6 -0
  360. package/corpus/templates/design/changelog/2026-07-10-deleted-screens-can-be-restored-with-undo-and-removed-again-.md +6 -0
  361. package/corpus/templates/design/changelog/2026-07-10-design-changes-now-save-before-switching-apps-and-retry-when.md +6 -0
  362. package/corpus/templates/design/changelog/2026-07-10-design-stays-usable-on-phones-by-keeping-editor-panels-and-f.md +6 -0
  363. package/corpus/templates/design/changelog/2026-07-10-download-all-screens-as-one-multi-page-pdf-from-the-overview.md +6 -0
  364. package/corpus/templates/design/changelog/2026-07-10-duplicated-layers-now-keep-labels-aria-relationships-fragmen.md +6 -0
  365. package/corpus/templates/design/changelog/2026-07-10-fig-import-results-stay-compact.md +6 -0
  366. package/corpus/templates/design/changelog/2026-07-10-figma-imports-now-load-real-fonts-fix-rotated-gradient-dashe.md +6 -0
  367. package/corpus/templates/design/changelog/2026-07-10-figma-links-in-chat-and-the-import-tab-now-offer-secure-one-.md +6 -0
  368. package/corpus/templates/design/changelog/2026-07-10-fixed-cmd-z-not-restoring-content-after-an-agent-driven-desi.md +6 -0
  369. package/corpus/templates/design/changelog/2026-07-10-fixed-download-pdf-being-unreachable-from-the-export-panel-e.md +6 -0
  370. package/corpus/templates/design/changelog/2026-07-10-fixed-dragging-a-layer-out-of-an-auto-layout-row-column-no-l.md +6 -0
  371. package/corpus/templates/design/changelog/2026-07-10-fixed-figma-svg-export-text-wrapping-sizing-and-inner-layer.md +6 -0
  372. package/corpus/templates/design/changelog/2026-07-10-fixed-hiding-or-locking-one-layer-no-longer-hides-or-blocks-.md +6 -0
  373. package/corpus/templates/design/changelog/2026-07-10-fixed-png-screenshots-of-tall-screens-being-cropped-to-one-v.md +6 -0
  374. package/corpus/templates/design/changelog/2026-07-10-fixed-png-svg-exports-capturing-broken-layout-when-webfonts-.md +6 -0
  375. package/corpus/templates/design/changelog/2026-07-10-frame-tool-now-offers-standard-ad-unit-size-presets-medium-r.md +6 -0
  376. package/corpus/templates/design/changelog/2026-07-10-full-view-controls-stay-within-their-frame-instead-of-coveri.md +6 -0
  377. package/corpus/templates/design/changelog/2026-07-10-hover-focus-focus-visible-pressed-and-disabled-states-now-pr.md +6 -0
  378. package/corpus/templates/design/changelog/2026-07-10-layer-drops-now-land-in-the-intended-grid-row-stay-visually-.md +6 -0
  379. package/corpus/templates/design/changelog/2026-07-10-local-code-workspaces-now-keep-unsaved-edits-across-panel-sw.md +6 -0
  380. package/corpus/templates/design/changelog/2026-07-10-local-visual-editing-now-keeps-authenticated-sessions-shared.md +6 -0
  381. package/corpus/templates/design/changelog/2026-07-10-long-running-agent-work-no-longer-shows-a-premature-stuck-wa.md +6 -0
  382. package/corpus/templates/design/changelog/2026-07-10-managed-figma-connections-are-recognized-automatically.md +6 -0
  383. package/corpus/templates/design/changelog/2026-07-10-new-designs-can-open-directly-in-the-editor-without-a-pr.md +6 -0
  384. package/corpus/templates/design/changelog/2026-07-10-new-screens-are-selected-and-revealed-immediately.md +6 -0
  385. package/corpus/templates/design/changelog/2026-07-10-new-text-layers-undo-as-one-safe-creation.md +6 -0
  386. package/corpus/templates/design/changelog/2026-07-10-preview-and-apply-inferred-auto-layout-without-changing-yo.md +5 -0
  387. package/corpus/templates/design/changelog/2026-07-10-print-pdf-exports-and-paper-size-presets-letter-a4-now-match.md +6 -0
  388. package/corpus/templates/design/changelog/2026-07-10-responsive-frame-editing.md +6 -0
  389. package/corpus/templates/design/changelog/2026-07-10-reusable-design-templates.md +6 -0
  390. package/corpus/templates/design/changelog/2026-07-10-screen-overview-frames-now-use-a-clearer-interact-action-wit.md +6 -0
  391. package/corpus/templates/design/changelog/2026-07-10-start-designs-from-source-linked-material-3-carbon-or-primer.md +6 -0
  392. package/corpus/templates/design/changelog/2026-07-10-tweaks-guidance.md +6 -0
  393. package/corpus/templates/design/changelog/2026-07-10-uploaded-fig-files-can-now-become-editable-design-screens-wi.md +6 -0
  394. package/corpus/templates/design/e2e/helpers.ts +3 -3
  395. package/corpus/templates/design/package.json +2 -0
  396. package/corpus/templates/design/scripts/qa-figma-stress-import.ts +96 -0
  397. package/corpus/templates/design/server/db/index.ts +10 -0
  398. package/corpus/templates/design/server/db/schema.ts +44 -0
  399. package/corpus/templates/design/server/handlers/import-design-file.ts +114 -80
  400. package/corpus/templates/design/server/handlers/index-design-system-with-builder.ts +19 -6
  401. package/corpus/templates/design/server/lib/design-export.ts +240 -3
  402. package/corpus/templates/design/server/lib/design-template-data.ts +89 -0
  403. package/corpus/templates/design/server/lib/design-to-figma-svg.ts +250 -26
  404. package/corpus/templates/design/server/lib/fig-file-decoder.ts +839 -0
  405. package/corpus/templates/design/server/lib/fig-file-import.ts +287 -0
  406. package/corpus/templates/design/server/lib/fig-file-limits.ts +3 -0
  407. package/corpus/templates/design/server/lib/fig-file-to-html.ts +2568 -0
  408. package/corpus/templates/design/server/lib/figma-design-context.ts +359 -0
  409. package/corpus/templates/design/server/lib/figma-node-import.ts +478 -22
  410. package/corpus/templates/design/server/lib/figma-node-to-html.ts +554 -29
  411. package/corpus/templates/design/server/lib/local-figma-qa-upload.ts +112 -0
  412. package/corpus/templates/design/server/lib/verify-write-grant.ts +16 -2
  413. package/corpus/templates/design/server/lib/visible-clipboard-html.ts +13 -3
  414. package/corpus/templates/design/server/plugins/agent-chat.ts +7 -0
  415. package/corpus/templates/design/server/plugins/db.ts +42 -0
  416. package/corpus/templates/design/server/plugins/local-figma-qa-upload.ts +7 -0
  417. package/corpus/templates/design/server/register-secrets.ts +8 -8
  418. package/corpus/templates/design/server/routes/api/qa-figma-import-assets/[assetId].get.ts +46 -0
  419. package/corpus/templates/design/server/source-workspace.ts +104 -12
  420. package/corpus/templates/design/shared/code-layer.ts +105 -28
  421. package/corpus/templates/design/shared/design-system-templates.ts +301 -0
  422. package/corpus/templates/design/shared/design-template-presets.ts +195 -0
  423. package/corpus/templates/design/shared/figma-url.ts +8 -0
  424. package/corpus/templates/design/shared/html-integrity.ts +306 -0
  425. package/corpus/templates/design/shared/interaction-states.ts +318 -23
  426. package/corpus/templates/design/shared/locked-layers.ts +151 -0
  427. package/corpus/templates/dispatch/.agents/skills/capture-learnings/SKILL.md +22 -9
  428. package/corpus/templates/dispatch/app/i18n/zh-TW.ts +48 -0
  429. package/corpus/templates/dispatch/app/i18n-data.ts +536 -1
  430. package/corpus/templates/dispatch/changelog/2026-07-10-connect-slack-workspaces-with-oauth-and-manage-channel-ident.md +6 -0
  431. package/corpus/templates/dispatch/changelog/2026-07-10-microsoft-teams-bots-and-discord-slash-commands-can-now-conn.md +6 -0
  432. package/corpus/templates/dispatch/changelog/2026-07-10-slack-and-telegram-now-route-uptime-monitor-requests-to-anal.md +6 -0
  433. package/corpus/templates/forms/.agents/skills/capture-learnings/SKILL.md +22 -9
  434. package/corpus/templates/forms/.agents/skills/form-publishing/SKILL.md +7 -0
  435. package/corpus/templates/forms/AGENTS.md +4 -0
  436. package/corpus/templates/forms/actions/create-form.ts +44 -7
  437. package/corpus/templates/forms/actions/lib/assert-publishable-form.ts +38 -0
  438. package/corpus/templates/forms/actions/patch-form-fields.ts +4 -0
  439. package/corpus/templates/forms/actions/update-form.ts +2 -36
  440. package/corpus/templates/forms/app/i18n/ar-SA.ts +3 -0
  441. package/corpus/templates/forms/app/i18n/de-DE.ts +3 -0
  442. package/corpus/templates/forms/app/i18n/en-US.ts +3 -0
  443. package/corpus/templates/forms/app/i18n/es-ES.ts +3 -0
  444. package/corpus/templates/forms/app/i18n/fr-FR.ts +3 -0
  445. package/corpus/templates/forms/app/i18n/hi-IN.ts +3 -0
  446. package/corpus/templates/forms/app/i18n/ja-JP.ts +3 -0
  447. package/corpus/templates/forms/app/i18n/ko-KR.ts +3 -0
  448. package/corpus/templates/forms/app/i18n/pt-BR.ts +3 -0
  449. package/corpus/templates/forms/app/i18n/zh-CN.ts +2 -0
  450. package/corpus/templates/forms/app/i18n/zh-TW.ts +3 -0
  451. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +17 -0
  452. package/corpus/templates/forms/changelog/2026-07-10-agent-created-published-forms-now-return-the-anonymous-publi.md +6 -0
  453. package/corpus/templates/forms/changelog/2026-07-10-published-forms-can-no-longer-be-edited-into-an-unusable-emp.md +6 -0
  454. package/corpus/templates/forms/server/handlers/submissions.ts +4 -3
  455. package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -0
  456. package/corpus/templates/forms/shared/types.ts +5 -0
  457. package/corpus/templates/mail/.agents/skills/capture-learnings/SKILL.md +22 -9
  458. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +2 -2
  459. package/corpus/templates/mail/changelog/2026-07-10-mail-navigation-now-uses-a-clean-borderless-drawer.md +6 -0
  460. package/corpus/templates/plan/.agents/skills/capture-learnings/SKILL.md +22 -9
  461. package/corpus/templates/plan/app/lib/plan-local-bridge.ts +4 -1
  462. package/corpus/templates/plan/changelog/2026-07-10-local-plans-now-keep-valid-content-visible-when-one-generate.md +6 -0
  463. package/corpus/templates/plan/server/lib/local-plan-files.ts +4 -1
  464. package/corpus/templates/slides/.agents/skills/capture-learnings/SKILL.md +22 -9
  465. package/dist/a2a/artifact-response.d.ts.map +1 -1
  466. package/dist/a2a/artifact-response.js +88 -2
  467. package/dist/a2a/artifact-response.js.map +1 -1
  468. package/dist/a2a/client.d.ts +9 -0
  469. package/dist/a2a/client.d.ts.map +1 -1
  470. package/dist/a2a/client.js +1 -0
  471. package/dist/a2a/client.js.map +1 -1
  472. package/dist/a2a/handlers.d.ts.map +1 -1
  473. package/dist/a2a/handlers.js +24 -69
  474. package/dist/a2a/handlers.js.map +1 -1
  475. package/dist/a2a/types.d.ts +2 -0
  476. package/dist/a2a/types.d.ts.map +1 -1
  477. package/dist/a2a/types.js.map +1 -1
  478. package/dist/agent/durable-background.d.ts +11 -0
  479. package/dist/agent/durable-background.d.ts.map +1 -1
  480. package/dist/agent/durable-background.js +17 -0
  481. package/dist/agent/durable-background.js.map +1 -1
  482. package/dist/agent/production-agent.d.ts +5 -3
  483. package/dist/agent/production-agent.d.ts.map +1 -1
  484. package/dist/agent/production-agent.js +34 -8
  485. package/dist/agent/production-agent.js.map +1 -1
  486. package/dist/agent/run-manager.d.ts +44 -2
  487. package/dist/agent/run-manager.d.ts.map +1 -1
  488. package/dist/agent/run-manager.js +124 -18
  489. package/dist/agent/run-manager.js.map +1 -1
  490. package/dist/agent/run-store.d.ts +109 -0
  491. package/dist/agent/run-store.d.ts.map +1 -1
  492. package/dist/agent/run-store.js +274 -37
  493. package/dist/agent/run-store.js.map +1 -1
  494. package/dist/agent/types.d.ts +22 -0
  495. package/dist/agent/types.d.ts.map +1 -1
  496. package/dist/agent/types.js.map +1 -1
  497. package/dist/audit/record.d.ts.map +1 -1
  498. package/dist/audit/record.js +29 -0
  499. package/dist/audit/record.js.map +1 -1
  500. package/dist/audit/store.d.ts.map +1 -1
  501. package/dist/audit/store.js +62 -3
  502. package/dist/audit/store.js.map +1 -1
  503. package/dist/audit/types.d.ts +13 -0
  504. package/dist/audit/types.d.ts.map +1 -1
  505. package/dist/audit/types.js.map +1 -1
  506. package/dist/automation/index.d.ts +193 -0
  507. package/dist/automation/index.d.ts.map +1 -0
  508. package/dist/automation/index.js +464 -0
  509. package/dist/automation/index.js.map +1 -0
  510. package/dist/cli/create.d.ts.map +1 -1
  511. package/dist/cli/create.js +10 -3
  512. package/dist/cli/create.js.map +1 -1
  513. package/dist/cli/design-connect.d.ts.map +1 -1
  514. package/dist/cli/design-connect.js +492 -35
  515. package/dist/cli/design-connect.js.map +1 -1
  516. package/dist/client/AgentPanel.d.ts +7 -1
  517. package/dist/client/AgentPanel.d.ts.map +1 -1
  518. package/dist/client/AgentPanel.js +32 -8
  519. package/dist/client/AgentPanel.js.map +1 -1
  520. package/dist/client/AssistantChat.d.ts +15 -0
  521. package/dist/client/AssistantChat.d.ts.map +1 -1
  522. package/dist/client/AssistantChat.js +115 -47
  523. package/dist/client/AssistantChat.js.map +1 -1
  524. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  525. package/dist/client/MultiTabAssistantChat.js +1 -1
  526. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  527. package/dist/client/RunStuckBanner.d.ts +14 -1
  528. package/dist/client/RunStuckBanner.d.ts.map +1 -1
  529. package/dist/client/RunStuckBanner.js +35 -7
  530. package/dist/client/RunStuckBanner.js.map +1 -1
  531. package/dist/client/agent-chat-adapter.d.ts +4 -1
  532. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  533. package/dist/client/agent-chat-adapter.js +97 -16
  534. package/dist/client/agent-chat-adapter.js.map +1 -1
  535. package/dist/client/automation.d.ts +14 -0
  536. package/dist/client/automation.d.ts.map +1 -0
  537. package/dist/client/automation.js +9 -0
  538. package/dist/client/automation.js.map +1 -0
  539. package/dist/client/chat/run-recovery.d.ts +4 -0
  540. package/dist/client/chat/run-recovery.d.ts.map +1 -1
  541. package/dist/client/chat/run-recovery.js +12 -10
  542. package/dist/client/chat/run-recovery.js.map +1 -1
  543. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  544. package/dist/client/composer/TiptapComposer.js +8 -0
  545. package/dist/client/composer/TiptapComposer.js.map +1 -1
  546. package/dist/client/index.d.ts +2 -0
  547. package/dist/client/index.d.ts.map +1 -1
  548. package/dist/client/index.js +2 -0
  549. package/dist/client/index.js.map +1 -1
  550. package/dist/client/integrations/api.d.ts +115 -0
  551. package/dist/client/integrations/api.d.ts.map +1 -0
  552. package/dist/client/integrations/api.js +123 -0
  553. package/dist/client/integrations/api.js.map +1 -0
  554. package/dist/client/integrations/index.d.ts +1 -0
  555. package/dist/client/integrations/index.d.ts.map +1 -1
  556. package/dist/client/integrations/index.js +1 -0
  557. package/dist/client/integrations/index.js.map +1 -1
  558. package/dist/client/transcription/use-live-transcription.d.ts.map +1 -1
  559. package/dist/client/transcription/use-live-transcription.js +59 -5
  560. package/dist/client/transcription/use-live-transcription.js.map +1 -1
  561. package/dist/client/use-run-stuck-detection.d.ts +16 -1
  562. package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
  563. package/dist/client/use-run-stuck-detection.js +23 -4
  564. package/dist/client/use-run-stuck-detection.js.map +1 -1
  565. package/dist/collab/awareness.d.ts +2 -2
  566. package/dist/collab/awareness.d.ts.map +1 -1
  567. package/dist/deploy/build.d.ts.map +1 -1
  568. package/dist/deploy/build.js +49 -4
  569. package/dist/deploy/build.js.map +1 -1
  570. package/dist/deploy/workspace-deploy.d.ts.map +1 -1
  571. package/dist/deploy/workspace-deploy.js +31 -2
  572. package/dist/deploy/workspace-deploy.js.map +1 -1
  573. package/dist/file-upload/types.d.ts +2 -0
  574. package/dist/file-upload/types.d.ts.map +1 -1
  575. package/dist/file-upload/types.js.map +1 -1
  576. package/dist/index.d.ts +2 -0
  577. package/dist/index.d.ts.map +1 -1
  578. package/dist/index.js +2 -0
  579. package/dist/index.js.map +1 -1
  580. package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
  581. package/dist/integrations/a2a-continuations-store.js +6 -4
  582. package/dist/integrations/a2a-continuations-store.js.map +1 -1
  583. package/dist/integrations/adapters/discord.d.ts +3 -0
  584. package/dist/integrations/adapters/discord.d.ts.map +1 -0
  585. package/dist/integrations/adapters/discord.js +261 -0
  586. package/dist/integrations/adapters/discord.js.map +1 -0
  587. package/dist/integrations/adapters/index.d.ts +4 -0
  588. package/dist/integrations/adapters/index.d.ts.map +1 -0
  589. package/dist/integrations/adapters/index.js +26 -0
  590. package/dist/integrations/adapters/index.js.map +1 -0
  591. package/dist/integrations/adapters/microsoft-teams.d.ts +5 -0
  592. package/dist/integrations/adapters/microsoft-teams.d.ts.map +1 -0
  593. package/dist/integrations/adapters/microsoft-teams.js +295 -0
  594. package/dist/integrations/adapters/microsoft-teams.js.map +1 -0
  595. package/dist/integrations/adapters/slack.d.ts +8 -2
  596. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  597. package/dist/integrations/adapters/slack.js +736 -27
  598. package/dist/integrations/adapters/slack.js.map +1 -1
  599. package/dist/integrations/adapters/telegram.d.ts +0 -2
  600. package/dist/integrations/adapters/telegram.d.ts.map +1 -1
  601. package/dist/integrations/adapters/telegram.js +49 -15
  602. package/dist/integrations/adapters/telegram.js.map +1 -1
  603. package/dist/integrations/adapters/whatsapp.d.ts +0 -2
  604. package/dist/integrations/adapters/whatsapp.d.ts.map +1 -1
  605. package/dist/integrations/adapters/whatsapp.js +40 -17
  606. package/dist/integrations/adapters/whatsapp.js.map +1 -1
  607. package/dist/integrations/catalog.d.ts +75 -0
  608. package/dist/integrations/catalog.d.ts.map +1 -0
  609. package/dist/integrations/catalog.js +451 -0
  610. package/dist/integrations/catalog.js.map +1 -0
  611. package/dist/integrations/controls-store.d.ts +44 -0
  612. package/dist/integrations/controls-store.d.ts.map +1 -0
  613. package/dist/integrations/controls-store.js +129 -0
  614. package/dist/integrations/controls-store.js.map +1 -0
  615. package/dist/integrations/index.d.ts +13 -1
  616. package/dist/integrations/index.d.ts.map +1 -1
  617. package/dist/integrations/index.js +9 -0
  618. package/dist/integrations/index.js.map +1 -1
  619. package/dist/integrations/installations-store.d.ts +112 -0
  620. package/dist/integrations/installations-store.d.ts.map +1 -0
  621. package/dist/integrations/installations-store.js +493 -0
  622. package/dist/integrations/installations-store.js.map +1 -0
  623. package/dist/integrations/integration-memory.d.ts +24 -0
  624. package/dist/integrations/integration-memory.d.ts.map +1 -0
  625. package/dist/integrations/integration-memory.js +136 -0
  626. package/dist/integrations/integration-memory.js.map +1 -0
  627. package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
  628. package/dist/integrations/pending-tasks-retry-job.js +6 -5
  629. package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
  630. package/dist/integrations/pending-tasks-store.d.ts +17 -0
  631. package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
  632. package/dist/integrations/pending-tasks-store.js +71 -5
  633. package/dist/integrations/pending-tasks-store.js.map +1 -1
  634. package/dist/integrations/plugin.d.ts.map +1 -1
  635. package/dist/integrations/plugin.js +520 -35
  636. package/dist/integrations/plugin.js.map +1 -1
  637. package/dist/integrations/scope-store.d.ts +65 -0
  638. package/dist/integrations/scope-store.d.ts.map +1 -0
  639. package/dist/integrations/scope-store.js +361 -0
  640. package/dist/integrations/scope-store.js.map +1 -0
  641. package/dist/integrations/slack-manifest.d.ts +66 -0
  642. package/dist/integrations/slack-manifest.d.ts.map +1 -0
  643. package/dist/integrations/slack-manifest.js +70 -0
  644. package/dist/integrations/slack-manifest.js.map +1 -0
  645. package/dist/integrations/slack-oauth.d.ts +99 -0
  646. package/dist/integrations/slack-oauth.d.ts.map +1 -0
  647. package/dist/integrations/slack-oauth.js +226 -0
  648. package/dist/integrations/slack-oauth.js.map +1 -0
  649. package/dist/integrations/types.d.ts +144 -0
  650. package/dist/integrations/types.d.ts.map +1 -1
  651. package/dist/integrations/types.js +16 -1
  652. package/dist/integrations/types.js.map +1 -1
  653. package/dist/integrations/usage-budget-store.d.ts +84 -0
  654. package/dist/integrations/usage-budget-store.d.ts.map +1 -0
  655. package/dist/integrations/usage-budget-store.js +650 -0
  656. package/dist/integrations/usage-budget-store.js.map +1 -0
  657. package/dist/integrations/webhook-handler.d.ts +4 -0
  658. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  659. package/dist/integrations/webhook-handler.js +415 -47
  660. package/dist/integrations/webhook-handler.js.map +1 -1
  661. package/dist/jobs/scheduler.d.ts +6 -0
  662. package/dist/jobs/scheduler.d.ts.map +1 -1
  663. package/dist/jobs/scheduler.js +83 -4
  664. package/dist/jobs/scheduler.js.map +1 -1
  665. package/dist/jobs/tools.d.ts.map +1 -1
  666. package/dist/jobs/tools.js +43 -11
  667. package/dist/jobs/tools.js.map +1 -1
  668. package/dist/provider-api/index.d.ts +4 -4
  669. package/dist/provider-api/index.d.ts.map +1 -1
  670. package/dist/provider-api/index.js +72 -1
  671. package/dist/provider-api/index.js.map +1 -1
  672. package/dist/resources/handlers.d.ts.map +1 -1
  673. package/dist/resources/handlers.js +57 -15
  674. package/dist/resources/handlers.js.map +1 -1
  675. package/dist/resources/script-helpers.d.ts.map +1 -1
  676. package/dist/resources/script-helpers.js +34 -8
  677. package/dist/resources/script-helpers.js.map +1 -1
  678. package/dist/resources/store.d.ts +10 -0
  679. package/dist/resources/store.d.ts.map +1 -1
  680. package/dist/resources/store.js +54 -47
  681. package/dist/resources/store.js.map +1 -1
  682. package/dist/scripts/call-agent.d.ts.map +1 -1
  683. package/dist/scripts/call-agent.js +106 -1
  684. package/dist/scripts/call-agent.js.map +1 -1
  685. package/dist/server/agent-chat-plugin.d.ts +15 -4
  686. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  687. package/dist/server/agent-chat-plugin.js +248 -113
  688. package/dist/server/agent-chat-plugin.js.map +1 -1
  689. package/dist/server/auth.d.ts.map +1 -1
  690. package/dist/server/auth.js +10 -14
  691. package/dist/server/auth.js.map +1 -1
  692. package/dist/server/builder-design-systems.d.ts +3 -1
  693. package/dist/server/builder-design-systems.d.ts.map +1 -1
  694. package/dist/server/builder-design-systems.js +9 -2
  695. package/dist/server/builder-design-systems.js.map +1 -1
  696. package/dist/server/edge.d.ts +1 -1
  697. package/dist/server/edge.d.ts.map +1 -1
  698. package/dist/server/edge.js +1 -1
  699. package/dist/server/edge.js.map +1 -1
  700. package/dist/server/index.d.ts +2 -1
  701. package/dist/server/index.d.ts.map +1 -1
  702. package/dist/server/index.js +2 -1
  703. package/dist/server/index.js.map +1 -1
  704. package/dist/server/request-context.d.ts +18 -0
  705. package/dist/server/request-context.d.ts.map +1 -1
  706. package/dist/server/request-context.js.map +1 -1
  707. package/dist/server/social-og-image.d.ts.map +1 -1
  708. package/dist/server/social-og-image.js +3 -2
  709. package/dist/server/social-og-image.js.map +1 -1
  710. package/dist/server/transcribe-voice.d.ts +1 -1
  711. package/dist/styles/agent-native.css +62 -44
  712. package/dist/templates/default/.agents/skills/capture-learnings/SKILL.md +22 -9
  713. package/dist/templates/default/AGENTS.md +7 -3
  714. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +1 -1
  715. package/dist/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +22 -9
  716. package/dist/templates/workspace-core/.agents/skills/extension-points/SKILL.md +6 -3
  717. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +25 -0
  718. package/dist/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +15 -0
  719. package/dist/templates/workspace-core/AGENTS.md +3 -2
  720. package/dist/usage/store.d.ts +7 -0
  721. package/dist/usage/store.d.ts.map +1 -1
  722. package/dist/usage/store.js +24 -3
  723. package/dist/usage/store.js.map +1 -1
  724. package/docs/content/automation-connectors.mdx +80 -0
  725. package/docs/content/extensions.mdx +8 -0
  726. package/docs/content/locales/ar-SA/extensions.mdx +8 -0
  727. package/docs/content/locales/ar-SA/messaging.mdx +84 -15
  728. package/docs/content/locales/ar-SA/template-clips.mdx +1 -0
  729. package/docs/content/locales/ar-SA/template-design.mdx +1 -0
  730. package/docs/content/locales/de-DE/extensions.mdx +10 -0
  731. package/docs/content/locales/de-DE/messaging.mdx +92 -15
  732. package/docs/content/locales/de-DE/template-clips.mdx +1 -0
  733. package/docs/content/locales/de-DE/template-design.mdx +1 -0
  734. package/docs/content/locales/es-ES/extensions.mdx +10 -0
  735. package/docs/content/locales/es-ES/messaging.mdx +91 -15
  736. package/docs/content/locales/es-ES/template-clips.mdx +1 -0
  737. package/docs/content/locales/es-ES/template-design.mdx +1 -0
  738. package/docs/content/locales/fr-FR/extensions.mdx +10 -0
  739. package/docs/content/locales/fr-FR/messaging.mdx +91 -15
  740. package/docs/content/locales/fr-FR/template-clips.mdx +1 -0
  741. package/docs/content/locales/fr-FR/template-design.mdx +1 -0
  742. package/docs/content/locales/hi-IN/extensions.mdx +8 -0
  743. package/docs/content/locales/hi-IN/messaging.mdx +81 -15
  744. package/docs/content/locales/hi-IN/template-clips.mdx +1 -0
  745. package/docs/content/locales/hi-IN/template-design.mdx +1 -0
  746. package/docs/content/locales/ja-JP/extensions.mdx +9 -0
  747. package/docs/content/locales/ja-JP/messaging.mdx +81 -15
  748. package/docs/content/locales/ja-JP/template-clips.mdx +1 -0
  749. package/docs/content/locales/ja-JP/template-design.mdx +1 -0
  750. package/docs/content/locales/ko-KR/extensions.mdx +8 -0
  751. package/docs/content/locales/ko-KR/messaging.mdx +80 -15
  752. package/docs/content/locales/ko-KR/template-clips.mdx +1 -0
  753. package/docs/content/locales/ko-KR/template-design.mdx +1 -0
  754. package/docs/content/locales/pt-BR/extensions.mdx +9 -0
  755. package/docs/content/locales/pt-BR/messaging.mdx +88 -15
  756. package/docs/content/locales/pt-BR/template-clips.mdx +1 -0
  757. package/docs/content/locales/pt-BR/template-design.mdx +1 -0
  758. package/docs/content/locales/zh-CN/extensions.mdx +6 -0
  759. package/docs/content/locales/zh-CN/messaging.mdx +78 -15
  760. package/docs/content/locales/zh-CN/template-clips.mdx +1 -0
  761. package/docs/content/locales/zh-CN/template-design.mdx +1 -0
  762. package/docs/content/locales/zh-TW/extensions.mdx +6 -0
  763. package/docs/content/locales/zh-TW/messaging.mdx +78 -15
  764. package/docs/content/locales/zh-TW/template-clips.mdx +1 -0
  765. package/docs/content/locales/zh-TW/template-design.mdx +1 -0
  766. package/docs/content/messaging.mdx +227 -45
  767. package/docs/content/template-clips.mdx +3 -0
  768. package/docs/content/template-design.mdx +4 -0
  769. package/package.json +5 -1
  770. package/src/templates/default/.agents/skills/capture-learnings/SKILL.md +22 -9
  771. package/src/templates/default/AGENTS.md +7 -3
  772. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +1 -1
  773. package/src/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +22 -9
  774. package/src/templates/workspace-core/.agents/skills/extension-points/SKILL.md +6 -3
  775. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +25 -0
  776. package/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +15 -0
  777. package/src/templates/workspace-core/AGENTS.md +3 -2
@@ -0,0 +1,906 @@
1
+ import { getDbExec, intType, isPostgres, type DbExec } from "../db/client.js";
2
+ import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
3
+ import {
4
+ getIntegrationScope,
5
+ integrationScopeSubjectKey,
6
+ type IntegrationScopeAccess,
7
+ type IntegrationScopeKey,
8
+ } from "./scope-store.js";
9
+
10
+ let initPromise: Promise<void> | undefined;
11
+ let transactionTail: Promise<void> = Promise.resolve();
12
+
13
+ /** All budget values are integer millionths of one billing currency unit. */
14
+ export const INTEGRATION_BUDGET_COST_UNIT = "currency_micros" as const;
15
+
16
+ export type IntegrationBudgetPeriod = "day" | "month";
17
+ export type IntegrationBudgetSubject =
18
+ | { type: "org"; orgId: string }
19
+ | { type: "user"; userEmail: string }
20
+ | { type: "scope"; scope: IntegrationScopeKey };
21
+
22
+ export interface IntegrationUsageBudget {
23
+ id: string;
24
+ subjectType: IntegrationBudgetSubject["type"];
25
+ subjectId: string;
26
+ period: IntegrationBudgetPeriod;
27
+ /** Integer millionths of one billing currency unit. */
28
+ limitMicros: number;
29
+ /** Threshold in basis points: 8,000 means 80%. */
30
+ thresholdBps: number;
31
+ ownerEmail: string;
32
+ orgId: string | null;
33
+ createdAt: number;
34
+ updatedAt: number;
35
+ }
36
+
37
+ export interface IntegrationBudgetSnapshot {
38
+ budget: IntegrationUsageBudget;
39
+ windowStart: number;
40
+ windowEnd: number;
41
+ usedMicros: number;
42
+ reservedMicros: number;
43
+ remainingMicros: number;
44
+ costUnit: typeof INTEGRATION_BUDGET_COST_UNIT;
45
+ }
46
+
47
+ export type IntegrationReservationStatus =
48
+ | "pending"
49
+ | "reserved"
50
+ | "denied"
51
+ | "settled"
52
+ | "released";
53
+
54
+ export interface IntegrationBudgetReservationResult {
55
+ allowed: boolean;
56
+ reservationId: string;
57
+ status: IntegrationReservationStatus;
58
+ estimatedCostMicros: number;
59
+ settledCostMicros: number | null;
60
+ thresholdEventEmitted: boolean;
61
+ snapshot: IntegrationBudgetSnapshot;
62
+ }
63
+
64
+ export interface IntegrationBudgetThresholdEvent {
65
+ id: string;
66
+ budgetId: string;
67
+ windowStart: number;
68
+ thresholdBps: number;
69
+ observedMicros: number;
70
+ createdAt: number;
71
+ }
72
+
73
+ interface ResolvedSubject {
74
+ type: IntegrationBudgetSubject["type"];
75
+ id: string;
76
+ partitionKey: string;
77
+ orgId: string | null;
78
+ }
79
+
80
+ interface ReservationRow {
81
+ id: string;
82
+ reservationKey: string;
83
+ budgetId: string;
84
+ windowStart: number;
85
+ estimatedCostMicros: number;
86
+ settledCostMicros: number | null;
87
+ status: IntegrationReservationStatus;
88
+ }
89
+
90
+ async function ensureTables(): Promise<void> {
91
+ if (!initPromise) {
92
+ initPromise = (async () => {
93
+ const db = getDbExec();
94
+ const budgetsSql = `CREATE TABLE IF NOT EXISTS integration_usage_budgets (
95
+ id TEXT PRIMARY KEY,
96
+ partition_key TEXT NOT NULL,
97
+ subject_type TEXT NOT NULL,
98
+ subject_id TEXT NOT NULL,
99
+ period TEXT NOT NULL,
100
+ limit_micros ${intType()} NOT NULL,
101
+ threshold_bps ${intType()} NOT NULL DEFAULT 8000,
102
+ owner_email TEXT NOT NULL,
103
+ org_id TEXT,
104
+ created_at ${intType()} NOT NULL,
105
+ updated_at ${intType()} NOT NULL
106
+ )`;
107
+ const windowsSql = `CREATE TABLE IF NOT EXISTS integration_usage_budget_windows (
108
+ budget_id TEXT NOT NULL,
109
+ window_start ${intType()} NOT NULL,
110
+ used_micros ${intType()} NOT NULL DEFAULT 0,
111
+ reserved_micros ${intType()} NOT NULL DEFAULT 0,
112
+ updated_at ${intType()} NOT NULL,
113
+ PRIMARY KEY (budget_id, window_start)
114
+ )`;
115
+ const reservationsSql = `CREATE TABLE IF NOT EXISTS integration_usage_reservations (
116
+ id TEXT PRIMARY KEY,
117
+ reservation_key TEXT NOT NULL,
118
+ budget_id TEXT NOT NULL,
119
+ window_start ${intType()} NOT NULL,
120
+ estimated_cost_micros ${intType()} NOT NULL,
121
+ settled_cost_micros ${intType()},
122
+ status TEXT NOT NULL,
123
+ created_at ${intType()} NOT NULL,
124
+ updated_at ${intType()} NOT NULL
125
+ )`;
126
+ const eventsSql = `CREATE TABLE IF NOT EXISTS integration_usage_budget_events (
127
+ id TEXT PRIMARY KEY,
128
+ budget_id TEXT NOT NULL,
129
+ window_start ${intType()} NOT NULL,
130
+ threshold_bps ${intType()} NOT NULL,
131
+ observed_micros ${intType()} NOT NULL,
132
+ created_at ${intType()} NOT NULL
133
+ )`;
134
+ const indexes = [
135
+ {
136
+ name: "idx_integration_budget_subject",
137
+ sql: "CREATE UNIQUE INDEX IF NOT EXISTS idx_integration_budget_subject ON integration_usage_budgets(partition_key, subject_type, subject_id, period)",
138
+ },
139
+ {
140
+ name: "idx_integration_budget_owner",
141
+ sql: "CREATE INDEX IF NOT EXISTS idx_integration_budget_owner ON integration_usage_budgets(owner_email, subject_type)",
142
+ },
143
+ {
144
+ name: "idx_integration_budget_org",
145
+ sql: "CREATE INDEX IF NOT EXISTS idx_integration_budget_org ON integration_usage_budgets(org_id, subject_type)",
146
+ },
147
+ {
148
+ name: "idx_integration_reservation_budget",
149
+ sql: "CREATE INDEX IF NOT EXISTS idx_integration_reservation_budget ON integration_usage_reservations(budget_id, window_start, status)",
150
+ },
151
+ {
152
+ name: "idx_integration_budget_event_window",
153
+ sql: "CREATE UNIQUE INDEX IF NOT EXISTS idx_integration_budget_event_window ON integration_usage_budget_events(budget_id, window_start, threshold_bps)",
154
+ },
155
+ ];
156
+
157
+ if (isPostgres()) {
158
+ await ensureTableExists("integration_usage_budgets", budgetsSql);
159
+ await ensureTableExists("integration_usage_budget_windows", windowsSql);
160
+ await ensureTableExists(
161
+ "integration_usage_reservations",
162
+ reservationsSql,
163
+ );
164
+ await ensureTableExists("integration_usage_budget_events", eventsSql);
165
+ for (const index of indexes) {
166
+ await ensureIndexExists(index.name, index.sql);
167
+ }
168
+ return;
169
+ }
170
+
171
+ await db.execute(budgetsSql);
172
+ await db.execute(windowsSql);
173
+ await db.execute(reservationsSql);
174
+ await db.execute(eventsSql);
175
+ for (const index of indexes) await db.execute(index.sql);
176
+ })().catch((error) => {
177
+ initPromise = undefined;
178
+ throw error;
179
+ });
180
+ }
181
+ return initPromise;
182
+ }
183
+
184
+ function requiredString(value: unknown, name: string, maxLength = 512): string {
185
+ const normalized = String(value ?? "").trim();
186
+ if (!normalized) throw new Error(`${name} is required`);
187
+ if (normalized.length > maxLength) {
188
+ throw new Error(`${name} must be at most ${maxLength} characters`);
189
+ }
190
+ return normalized;
191
+ }
192
+
193
+ function normalizeEmail(value: unknown, name: string): string {
194
+ const normalized = requiredString(value, name, 320).toLowerCase();
195
+ if (!normalized.includes("@")) {
196
+ throw new Error(`${name} must be an email address`);
197
+ }
198
+ return normalized;
199
+ }
200
+
201
+ function normalizeAccess(
202
+ access: IntegrationScopeAccess,
203
+ ): Required<IntegrationScopeAccess> {
204
+ return {
205
+ ownerEmail: normalizeEmail(access.ownerEmail, "ownerEmail"),
206
+ orgId: access.orgId ? requiredString(access.orgId, "orgId") : null,
207
+ };
208
+ }
209
+
210
+ function requireMicros(value: number, name: string, allowZero = false): number {
211
+ if (!Number.isSafeInteger(value) || value < (allowZero ? 0 : 1)) {
212
+ throw new Error(`${name} must be a safe integer in currency micros`);
213
+ }
214
+ return value;
215
+ }
216
+
217
+ function requireThresholdBps(value: number): number {
218
+ if (!Number.isInteger(value) || value < 1 || value > 10_000) {
219
+ throw new Error("thresholdBps must be an integer from 1 to 10000");
220
+ }
221
+ return value;
222
+ }
223
+
224
+ function requirePeriod(
225
+ value: IntegrationBudgetPeriod,
226
+ ): IntegrationBudgetPeriod {
227
+ if (value !== "day" && value !== "month") {
228
+ throw new Error("period must be day or month");
229
+ }
230
+ return value;
231
+ }
232
+
233
+ function authorizationSql(alias = ""): string {
234
+ const prefix = alias ? `${alias}.` : "";
235
+ return `((${prefix}subject_type = 'user' AND ${prefix}owner_email = ?) OR (${prefix}subject_type <> 'user' AND ((${prefix}org_id = ? AND ? IS NOT NULL) OR (${prefix}org_id IS NULL AND ${prefix}owner_email = ?))))`;
236
+ }
237
+
238
+ function authorizationArgs(
239
+ access: Required<IntegrationScopeAccess>,
240
+ ): unknown[] {
241
+ return [access.ownerEmail, access.orgId, access.orgId, access.ownerEmail];
242
+ }
243
+
244
+ async function resolveSubject(
245
+ subject: IntegrationBudgetSubject,
246
+ access: Required<IntegrationScopeAccess>,
247
+ ): Promise<ResolvedSubject> {
248
+ if (subject.type === "org") {
249
+ const orgId = requiredString(subject.orgId, "subject.orgId");
250
+ if (!access.orgId || access.orgId !== orgId) {
251
+ throw new Error("Not authorized to manage a budget for that org");
252
+ }
253
+ return { type: "org", id: orgId, partitionKey: `org:${orgId}`, orgId };
254
+ }
255
+
256
+ if (subject.type === "user") {
257
+ const userEmail = normalizeEmail(subject.userEmail, "subject.userEmail");
258
+ if (userEmail !== access.ownerEmail) {
259
+ throw new Error("Not authorized to manage another user's budget");
260
+ }
261
+ return {
262
+ type: "user",
263
+ id: userEmail,
264
+ partitionKey: access.orgId
265
+ ? `org:${access.orgId}`
266
+ : `owner:${access.ownerEmail}`,
267
+ orgId: access.orgId,
268
+ };
269
+ }
270
+
271
+ const scope = await getIntegrationScope(subject.scope, access);
272
+ if (!scope)
273
+ throw new Error("Integration scope is not available to this caller");
274
+ return {
275
+ type: "scope",
276
+ id: integrationScopeSubjectKey(subject.scope),
277
+ partitionKey: scope.orgId
278
+ ? `org:${scope.orgId}`
279
+ : `owner:${scope.ownerEmail}`,
280
+ orgId: scope.orgId,
281
+ };
282
+ }
283
+
284
+ function stableId(prefix: string, parts: unknown[]): string {
285
+ return `${prefix}:${JSON.stringify(parts)}`;
286
+ }
287
+
288
+ function rowToBudget(row: Record<string, unknown>): IntegrationUsageBudget {
289
+ return {
290
+ id: String(row.id),
291
+ subjectType: row.subject_type as IntegrationBudgetSubject["type"],
292
+ subjectId: String(row.subject_id),
293
+ period: row.period as IntegrationBudgetPeriod,
294
+ limitMicros: Number(row.limit_micros),
295
+ thresholdBps: Number(row.threshold_bps),
296
+ ownerEmail: String(row.owner_email),
297
+ orgId: row.org_id == null ? null : String(row.org_id),
298
+ createdAt: Number(row.created_at),
299
+ updatedAt: Number(row.updated_at),
300
+ };
301
+ }
302
+
303
+ function rowToReservation(row: Record<string, unknown>): ReservationRow {
304
+ return {
305
+ id: String(row.id),
306
+ reservationKey: String(row.reservation_key),
307
+ budgetId: String(row.budget_id),
308
+ windowStart: Number(row.window_start),
309
+ estimatedCostMicros: Number(row.estimated_cost_micros),
310
+ settledCostMicros:
311
+ row.settled_cost_micros == null ? null : Number(row.settled_cost_micros),
312
+ status: row.status as IntegrationReservationStatus,
313
+ };
314
+ }
315
+
316
+ function windowForPeriod(
317
+ period: IntegrationBudgetPeriod,
318
+ timestamp: number,
319
+ ): { start: number; end: number } {
320
+ const date = new Date(timestamp);
321
+ if (!Number.isFinite(date.getTime()))
322
+ throw new Error("Invalid budget timestamp");
323
+ const start =
324
+ period === "day"
325
+ ? Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())
326
+ : Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1);
327
+ const end =
328
+ period === "day"
329
+ ? start + 24 * 60 * 60 * 1000
330
+ : Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 1);
331
+ return { start, end };
332
+ }
333
+
334
+ async function withSerializedTransaction<T>(
335
+ fn: (tx: DbExec) => Promise<T>,
336
+ ): Promise<T> {
337
+ const previous = transactionTail;
338
+ let release!: () => void;
339
+ transactionTail = new Promise<void>((resolve) => {
340
+ release = resolve;
341
+ });
342
+ await previous.catch(() => {});
343
+ const db = getDbExec();
344
+ try {
345
+ if (db.transaction) return await db.transaction(fn);
346
+ await db.execute(isPostgres() ? "BEGIN" : "BEGIN IMMEDIATE");
347
+ try {
348
+ const result = await fn(db);
349
+ await db.execute("COMMIT");
350
+ return result;
351
+ } catch (error) {
352
+ await db.execute("ROLLBACK").catch(() => {});
353
+ throw error;
354
+ }
355
+ } finally {
356
+ release();
357
+ }
358
+ }
359
+
360
+ async function findBudget(
361
+ db: DbExec,
362
+ budgetId: string,
363
+ access: Required<IntegrationScopeAccess>,
364
+ ): Promise<IntegrationUsageBudget | null> {
365
+ const { rows } = await db.execute({
366
+ sql: `SELECT id, subject_type, subject_id, period, limit_micros,
367
+ threshold_bps, owner_email, org_id, created_at, updated_at
368
+ FROM integration_usage_budgets
369
+ WHERE id = ? AND ${authorizationSql()} LIMIT 1`,
370
+ args: [budgetId, ...authorizationArgs(access)],
371
+ });
372
+ return rows.length > 0
373
+ ? rowToBudget(rows[0] as Record<string, unknown>)
374
+ : null;
375
+ }
376
+
377
+ async function ensureWindow(
378
+ db: DbExec,
379
+ budgetId: string,
380
+ windowStart: number,
381
+ now: number,
382
+ ): Promise<void> {
383
+ // SQLite and Postgres both support this exact conflict target. It is used
384
+ // only as the atomic create-if-absent primitive for the counter row.
385
+ await db.execute({
386
+ sql: `INSERT INTO integration_usage_budget_windows
387
+ (budget_id, window_start, used_micros, reserved_micros, updated_at)
388
+ VALUES (?, ?, 0, 0, ?)
389
+ ON CONFLICT (budget_id, window_start) DO NOTHING`,
390
+ args: [budgetId, windowStart, now],
391
+ });
392
+ }
393
+
394
+ async function snapshotForBudget(
395
+ db: DbExec,
396
+ budget: IntegrationUsageBudget,
397
+ timestamp: number,
398
+ ): Promise<IntegrationBudgetSnapshot> {
399
+ const window = windowForPeriod(budget.period, timestamp);
400
+ const { rows } = await db.execute({
401
+ sql: `SELECT used_micros, reserved_micros
402
+ FROM integration_usage_budget_windows
403
+ WHERE budget_id = ? AND window_start = ? LIMIT 1`,
404
+ args: [budget.id, window.start],
405
+ });
406
+ const usedMicros = rows.length > 0 ? Number(rows[0].used_micros) : 0;
407
+ const reservedMicros = rows.length > 0 ? Number(rows[0].reserved_micros) : 0;
408
+ return {
409
+ budget,
410
+ windowStart: window.start,
411
+ windowEnd: window.end,
412
+ usedMicros,
413
+ reservedMicros,
414
+ remainingMicros: Math.max(
415
+ 0,
416
+ budget.limitMicros - usedMicros - reservedMicros,
417
+ ),
418
+ costUnit: INTEGRATION_BUDGET_COST_UNIT,
419
+ };
420
+ }
421
+
422
+ async function emitThresholdEvent(
423
+ db: DbExec,
424
+ budget: IntegrationUsageBudget,
425
+ snapshot: IntegrationBudgetSnapshot,
426
+ now: number,
427
+ ): Promise<boolean> {
428
+ const observedMicros = snapshot.usedMicros + snapshot.reservedMicros;
429
+ if (observedMicros * 10_000 < budget.limitMicros * budget.thresholdBps) {
430
+ return false;
431
+ }
432
+ const eventId = stableId("integration-budget-threshold", [
433
+ budget.id,
434
+ snapshot.windowStart,
435
+ budget.thresholdBps,
436
+ ]);
437
+ const result = await db.execute({
438
+ sql: `INSERT INTO integration_usage_budget_events
439
+ (id, budget_id, window_start, threshold_bps, observed_micros, created_at)
440
+ VALUES (?, ?, ?, ?, ?, ?)
441
+ ON CONFLICT (id) DO NOTHING`,
442
+ args: [
443
+ eventId,
444
+ budget.id,
445
+ snapshot.windowStart,
446
+ budget.thresholdBps,
447
+ observedMicros,
448
+ now,
449
+ ],
450
+ });
451
+ return result.rowsAffected > 0;
452
+ }
453
+
454
+ export async function saveIntegrationUsageBudget(
455
+ input: {
456
+ subject: IntegrationBudgetSubject;
457
+ period: IntegrationBudgetPeriod;
458
+ limitMicros: number;
459
+ thresholdBps?: number;
460
+ },
461
+ accessInput: IntegrationScopeAccess,
462
+ ): Promise<IntegrationUsageBudget> {
463
+ await ensureTables();
464
+ const access = normalizeAccess(accessInput);
465
+ const subject = await resolveSubject(input.subject, access);
466
+ const period = requirePeriod(input.period);
467
+ const limitMicros = requireMicros(input.limitMicros, "limitMicros");
468
+ const thresholdBps = requireThresholdBps(input.thresholdBps ?? 8_000);
469
+ const id = stableId("integration-budget", [
470
+ subject.partitionKey,
471
+ subject.type,
472
+ subject.id,
473
+ period,
474
+ ]);
475
+ const now = Date.now();
476
+
477
+ await withSerializedTransaction(async (tx) => {
478
+ // The stable id is derived entirely from the authorized partition. The
479
+ // conflict update therefore cannot cross an owner/org boundary.
480
+ await tx.execute({
481
+ sql: `INSERT INTO integration_usage_budgets (
482
+ id, partition_key, subject_type, subject_id, period, limit_micros,
483
+ threshold_bps, owner_email, org_id, created_at, updated_at
484
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
485
+ ON CONFLICT (id) DO UPDATE SET
486
+ limit_micros = excluded.limit_micros,
487
+ threshold_bps = excluded.threshold_bps,
488
+ updated_at = excluded.updated_at`,
489
+ args: [
490
+ id,
491
+ subject.partitionKey,
492
+ subject.type,
493
+ subject.id,
494
+ period,
495
+ limitMicros,
496
+ thresholdBps,
497
+ access.ownerEmail,
498
+ subject.orgId,
499
+ now,
500
+ now,
501
+ ],
502
+ });
503
+ });
504
+
505
+ const saved = await getIntegrationUsageBudget(id, access);
506
+ if (!saved) throw new Error("Integration budget write could not be verified");
507
+ return saved;
508
+ }
509
+
510
+ export async function getIntegrationUsageBudget(
511
+ budgetIdInput: string,
512
+ accessInput: IntegrationScopeAccess,
513
+ ): Promise<IntegrationUsageBudget | null> {
514
+ await ensureTables();
515
+ const budgetId = requiredString(budgetIdInput, "budgetId", 2_048);
516
+ return findBudget(getDbExec(), budgetId, normalizeAccess(accessInput));
517
+ }
518
+
519
+ /** List budgets visible in the caller's personal/active-org partition. */
520
+ export async function listIntegrationUsageBudgets(
521
+ accessInput: IntegrationScopeAccess,
522
+ ): Promise<IntegrationUsageBudget[]> {
523
+ await ensureTables();
524
+ const access = normalizeAccess(accessInput);
525
+ const { rows } = await getDbExec().execute({
526
+ sql: `SELECT id, partition_key, subject_type, subject_id, period,
527
+ limit_micros, threshold_bps, owner_email, org_id, created_at, updated_at
528
+ FROM integration_usage_budgets
529
+ WHERE ${authorizationSql()}
530
+ ORDER BY updated_at DESC`,
531
+ args: authorizationArgs(access),
532
+ });
533
+ return rows.map((row) => rowToBudget(row as Record<string, unknown>));
534
+ }
535
+
536
+ export async function getIntegrationBudgetSnapshot(
537
+ budgetIdInput: string,
538
+ accessInput: IntegrationScopeAccess,
539
+ timestamp = Date.now(),
540
+ ): Promise<IntegrationBudgetSnapshot | null> {
541
+ await ensureTables();
542
+ const access = normalizeAccess(accessInput);
543
+ const budget = await findBudget(
544
+ getDbExec(),
545
+ requiredString(budgetIdInput, "budgetId", 2_048),
546
+ access,
547
+ );
548
+ return budget ? snapshotForBudget(getDbExec(), budget, timestamp) : null;
549
+ }
550
+
551
+ export async function reserveIntegrationUsageBudget(
552
+ input: {
553
+ budgetId: string;
554
+ reservationId: string;
555
+ estimatedCostMicros: number;
556
+ timestamp?: number;
557
+ },
558
+ accessInput: IntegrationScopeAccess,
559
+ ): Promise<IntegrationBudgetReservationResult> {
560
+ await ensureTables();
561
+ const access = normalizeAccess(accessInput);
562
+ const budgetId = requiredString(input.budgetId, "budgetId", 2_048);
563
+ const reservationId = requiredString(
564
+ input.reservationId,
565
+ "reservationId",
566
+ 512,
567
+ );
568
+ const estimatedCostMicros = requireMicros(
569
+ input.estimatedCostMicros,
570
+ "estimatedCostMicros",
571
+ );
572
+ const timestamp = input.timestamp ?? Date.now();
573
+
574
+ return withSerializedTransaction(async (tx) => {
575
+ const budget = await findBudget(tx, budgetId, access);
576
+ if (!budget)
577
+ throw new Error("Integration budget is not available to this caller");
578
+ const window = windowForPeriod(budget.period, timestamp);
579
+ const now = Date.now();
580
+ const storageId = stableId("integration-reservation", [
581
+ budget.id,
582
+ reservationId,
583
+ ]);
584
+
585
+ const inserted = await tx.execute({
586
+ sql: `INSERT INTO integration_usage_reservations (
587
+ id, reservation_key, budget_id, window_start,
588
+ estimated_cost_micros, settled_cost_micros, status,
589
+ created_at, updated_at
590
+ ) VALUES (?, ?, ?, ?, ?, NULL, 'pending', ?, ?)
591
+ ON CONFLICT (id) DO NOTHING`,
592
+ args: [
593
+ storageId,
594
+ reservationId,
595
+ budget.id,
596
+ window.start,
597
+ estimatedCostMicros,
598
+ now,
599
+ now,
600
+ ],
601
+ });
602
+
603
+ if (inserted.rowsAffected === 0) {
604
+ const existing = await getReservation(tx, storageId, budget.id);
605
+ if (!existing) {
606
+ throw new Error("Reservation id is not available to this caller");
607
+ }
608
+ if (existing.estimatedCostMicros !== estimatedCostMicros) {
609
+ throw new Error(
610
+ "Reservation id was already used with a different estimate",
611
+ );
612
+ }
613
+ if (existing.windowStart !== window.start) {
614
+ throw new Error(
615
+ "Reservation id was already used in a different budget window",
616
+ );
617
+ }
618
+ const snapshot = await snapshotForBudget(
619
+ tx,
620
+ budget,
621
+ existing.windowStart,
622
+ );
623
+ return {
624
+ allowed:
625
+ existing.status === "reserved" || existing.status === "settled",
626
+ reservationId,
627
+ status: existing.status,
628
+ estimatedCostMicros,
629
+ settledCostMicros: existing.settledCostMicros,
630
+ thresholdEventEmitted: false,
631
+ snapshot,
632
+ };
633
+ }
634
+
635
+ await ensureWindow(tx, budget.id, window.start, now);
636
+ const reserved = await tx.execute({
637
+ sql: `UPDATE integration_usage_budget_windows
638
+ SET reserved_micros = reserved_micros + ?, updated_at = ?
639
+ WHERE budget_id = ? AND window_start = ?
640
+ AND used_micros + reserved_micros + ? <= ?`,
641
+ args: [
642
+ estimatedCostMicros,
643
+ now,
644
+ budget.id,
645
+ window.start,
646
+ estimatedCostMicros,
647
+ budget.limitMicros,
648
+ ],
649
+ });
650
+ const allowed = reserved.rowsAffected > 0;
651
+ const status: IntegrationReservationStatus = allowed
652
+ ? "reserved"
653
+ : "denied";
654
+ await tx.execute({
655
+ sql: `UPDATE integration_usage_reservations
656
+ SET status = ?, updated_at = ? WHERE id = ? AND status = 'pending'`,
657
+ args: [status, now, storageId],
658
+ });
659
+ const snapshot = await snapshotForBudget(tx, budget, timestamp);
660
+ const thresholdEventEmitted = allowed
661
+ ? await emitThresholdEvent(tx, budget, snapshot, now)
662
+ : false;
663
+ return {
664
+ allowed,
665
+ reservationId,
666
+ status,
667
+ estimatedCostMicros,
668
+ settledCostMicros: null,
669
+ thresholdEventEmitted,
670
+ snapshot,
671
+ };
672
+ });
673
+ }
674
+
675
+ async function getReservation(
676
+ db: DbExec,
677
+ storageId: string,
678
+ budgetId: string,
679
+ ): Promise<ReservationRow | null> {
680
+ const { rows } = await db.execute({
681
+ sql: `SELECT id, reservation_key, budget_id, window_start,
682
+ estimated_cost_micros, settled_cost_micros, status
683
+ FROM integration_usage_reservations
684
+ WHERE id = ? AND budget_id = ? LIMIT 1`,
685
+ args: [storageId, budgetId],
686
+ });
687
+ return rows.length > 0
688
+ ? rowToReservation(rows[0] as Record<string, unknown>)
689
+ : null;
690
+ }
691
+
692
+ export async function settleIntegrationUsageBudget(
693
+ input: {
694
+ budgetId: string;
695
+ reservationId: string;
696
+ actualCostMicros: number;
697
+ },
698
+ accessInput: IntegrationScopeAccess,
699
+ ): Promise<IntegrationBudgetReservationResult> {
700
+ await ensureTables();
701
+ const access = normalizeAccess(accessInput);
702
+ const budgetId = requiredString(input.budgetId, "budgetId", 2_048);
703
+ const reservationId = requiredString(
704
+ input.reservationId,
705
+ "reservationId",
706
+ 512,
707
+ );
708
+ const actualCostMicros = requireMicros(
709
+ input.actualCostMicros,
710
+ "actualCostMicros",
711
+ true,
712
+ );
713
+
714
+ return withSerializedTransaction(async (tx) => {
715
+ const budget = await findBudget(tx, budgetId, access);
716
+ if (!budget)
717
+ throw new Error("Integration budget is not available to this caller");
718
+ const storageId = stableId("integration-reservation", [
719
+ budget.id,
720
+ reservationId,
721
+ ]);
722
+ const reservation = await getReservation(tx, storageId, budget.id);
723
+ if (!reservation) {
724
+ throw new Error("Integration budget reservation was not found");
725
+ }
726
+ if (reservation.status === "denied" || reservation.status === "released") {
727
+ throw new Error(`Cannot settle a ${reservation.status} reservation`);
728
+ }
729
+ if (reservation.status === "pending") {
730
+ throw new Error("Cannot settle a pending reservation");
731
+ }
732
+ if (
733
+ reservation.status === "settled" &&
734
+ reservation.settledCostMicros !== actualCostMicros
735
+ ) {
736
+ throw new Error("Reservation was already settled with a different cost");
737
+ }
738
+
739
+ let thresholdEventEmitted = false;
740
+ if (reservation.status === "reserved") {
741
+ const now = Date.now();
742
+ const updated = await tx.execute({
743
+ sql: `UPDATE integration_usage_budget_windows SET
744
+ reserved_micros = reserved_micros - ?,
745
+ used_micros = used_micros + ?,
746
+ updated_at = ?
747
+ WHERE budget_id = ? AND window_start = ?
748
+ AND reserved_micros >= ?`,
749
+ args: [
750
+ reservation.estimatedCostMicros,
751
+ actualCostMicros,
752
+ now,
753
+ budget.id,
754
+ reservation.windowStart,
755
+ reservation.estimatedCostMicros,
756
+ ],
757
+ });
758
+ if (updated.rowsAffected === 0) {
759
+ throw new Error(
760
+ "Integration budget reservation counter is inconsistent",
761
+ );
762
+ }
763
+ await tx.execute({
764
+ sql: `UPDATE integration_usage_reservations SET
765
+ status = 'settled', settled_cost_micros = ?, updated_at = ?
766
+ WHERE id = ? AND status = 'reserved'`,
767
+ args: [actualCostMicros, now, reservation.id],
768
+ });
769
+ const thresholdSnapshot = await snapshotForBudget(
770
+ tx,
771
+ budget,
772
+ reservation.windowStart,
773
+ );
774
+ thresholdEventEmitted = await emitThresholdEvent(
775
+ tx,
776
+ budget,
777
+ thresholdSnapshot,
778
+ now,
779
+ );
780
+ }
781
+
782
+ const snapshot = await snapshotForBudget(
783
+ tx,
784
+ budget,
785
+ reservation.windowStart,
786
+ );
787
+ return {
788
+ allowed: true,
789
+ reservationId,
790
+ status: "settled",
791
+ estimatedCostMicros: reservation.estimatedCostMicros,
792
+ settledCostMicros: actualCostMicros,
793
+ thresholdEventEmitted,
794
+ snapshot,
795
+ };
796
+ });
797
+ }
798
+
799
+ export async function releaseIntegrationUsageBudget(
800
+ input: { budgetId: string; reservationId: string },
801
+ accessInput: IntegrationScopeAccess,
802
+ ): Promise<IntegrationBudgetReservationResult> {
803
+ await ensureTables();
804
+ const access = normalizeAccess(accessInput);
805
+ const budgetId = requiredString(input.budgetId, "budgetId", 2_048);
806
+ const reservationId = requiredString(
807
+ input.reservationId,
808
+ "reservationId",
809
+ 512,
810
+ );
811
+
812
+ return withSerializedTransaction(async (tx) => {
813
+ const budget = await findBudget(tx, budgetId, access);
814
+ if (!budget)
815
+ throw new Error("Integration budget is not available to this caller");
816
+ const storageId = stableId("integration-reservation", [
817
+ budget.id,
818
+ reservationId,
819
+ ]);
820
+ const reservation = await getReservation(tx, storageId, budget.id);
821
+ if (!reservation) {
822
+ throw new Error("Integration budget reservation was not found");
823
+ }
824
+ if (reservation.status === "settled") {
825
+ throw new Error("Cannot release a settled reservation");
826
+ }
827
+ if (reservation.status === "pending") {
828
+ throw new Error("Cannot release a pending reservation");
829
+ }
830
+
831
+ if (reservation.status === "reserved") {
832
+ const now = Date.now();
833
+ const updated = await tx.execute({
834
+ sql: `UPDATE integration_usage_budget_windows SET
835
+ reserved_micros = reserved_micros - ?, updated_at = ?
836
+ WHERE budget_id = ? AND window_start = ?
837
+ AND reserved_micros >= ?`,
838
+ args: [
839
+ reservation.estimatedCostMicros,
840
+ now,
841
+ budget.id,
842
+ reservation.windowStart,
843
+ reservation.estimatedCostMicros,
844
+ ],
845
+ });
846
+ if (updated.rowsAffected === 0) {
847
+ throw new Error(
848
+ "Integration budget reservation counter is inconsistent",
849
+ );
850
+ }
851
+ await tx.execute({
852
+ sql: `UPDATE integration_usage_reservations
853
+ SET status = 'released', updated_at = ?
854
+ WHERE id = ? AND status = 'reserved'`,
855
+ args: [now, reservation.id],
856
+ });
857
+ }
858
+
859
+ const snapshot = await snapshotForBudget(
860
+ tx,
861
+ budget,
862
+ reservation.windowStart,
863
+ );
864
+ return {
865
+ allowed: false,
866
+ reservationId,
867
+ status: reservation.status === "denied" ? "denied" : "released",
868
+ estimatedCostMicros: reservation.estimatedCostMicros,
869
+ settledCostMicros: reservation.settledCostMicros,
870
+ thresholdEventEmitted: false,
871
+ snapshot,
872
+ };
873
+ });
874
+ }
875
+
876
+ export async function listIntegrationBudgetThresholdEvents(
877
+ budgetIdInput: string,
878
+ accessInput: IntegrationScopeAccess,
879
+ ): Promise<IntegrationBudgetThresholdEvent[]> {
880
+ await ensureTables();
881
+ const access = normalizeAccess(accessInput);
882
+ const budgetId = requiredString(budgetIdInput, "budgetId", 2_048);
883
+ const budget = await findBudget(getDbExec(), budgetId, access);
884
+ if (!budget) return [];
885
+ const { rows } = await getDbExec().execute({
886
+ sql: `SELECT id, budget_id, window_start, threshold_bps,
887
+ observed_micros, created_at
888
+ FROM integration_usage_budget_events
889
+ WHERE budget_id = ? ORDER BY window_start DESC, created_at DESC`,
890
+ args: [budget.id],
891
+ });
892
+ return rows.map((row) => ({
893
+ id: String(row.id),
894
+ budgetId: String(row.budget_id),
895
+ windowStart: Number(row.window_start),
896
+ thresholdBps: Number(row.threshold_bps),
897
+ observedMicros: Number(row.observed_micros),
898
+ createdAt: Number(row.created_at),
899
+ }));
900
+ }
901
+
902
+ /** Test-only reset for suites that swap the injected database. */
903
+ export function _resetIntegrationUsageBudgetStoreForTests(): void {
904
+ initPromise = undefined;
905
+ transactionTail = Promise.resolve();
906
+ }