@graph8/sdk 0.308.1 → 0.357.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 (654) hide show
  1. package/README.md +81 -0
  2. package/blocks/README.md +32 -0
  3. package/blocks/_foundation/README.md +78 -0
  4. package/blocks/_foundation/block.json +79 -0
  5. package/blocks/_foundation/components/api-error.tsx +62 -0
  6. package/blocks/_foundation/components/confirm-spend-dialog.tsx +142 -0
  7. package/blocks/_foundation/components/empty-state.tsx +24 -0
  8. package/blocks/_foundation/hooks/use-g8-mutation.ts +88 -0
  9. package/blocks/_foundation/hooks/use-g8-query.ts +87 -0
  10. package/blocks/_foundation/hooks/use-job-poll.ts +130 -0
  11. package/blocks/_foundation/hooks/use-paginated.ts +105 -0
  12. package/blocks/_foundation/lib/g8-client.ts +128 -0
  13. package/blocks/_foundation/recipes/verify-proxy-key.ts +18 -0
  14. package/blocks/_foundation/server/proxy.ts +233 -0
  15. package/blocks/_foundation/server/route.ts +30 -0
  16. package/blocks/agents/README.md +133 -0
  17. package/blocks/agents/block.json +223 -0
  18. package/blocks/agents/components/agent-builder.tsx +199 -0
  19. package/blocks/agents/components/agent-knowledge-panel.tsx +144 -0
  20. package/blocks/agents/components/agent-runs.tsx +175 -0
  21. package/blocks/agents/components/agent-skills-panel.tsx +126 -0
  22. package/blocks/agents/components/agent-test-console.tsx +175 -0
  23. package/blocks/agents/lib/agent-types.ts +114 -0
  24. package/blocks/agents/recipes/create-agent-and-attach-skill.ts +50 -0
  25. package/blocks/agents/recipes/run-and-follow-agent-run.ts +46 -0
  26. package/blocks/agents/recipes/test-agent-before-activating.ts +36 -0
  27. package/blocks/analytics/README.md +73 -0
  28. package/blocks/analytics/block.json +178 -0
  29. package/blocks/analytics/components/funnel-chart.tsx +79 -0
  30. package/blocks/analytics/components/kpi-tiles.tsx +74 -0
  31. package/blocks/analytics/components/leaderboard.tsx +86 -0
  32. package/blocks/analytics/components/report-builder.tsx +200 -0
  33. package/blocks/analytics/hooks/use-report.ts +26 -0
  34. package/blocks/analytics/lib/analytics-types.ts +108 -0
  35. package/blocks/analytics/lib/use-debounced.ts +16 -0
  36. package/blocks/analytics/recipes/01-weekly-outbound-report.ts +32 -0
  37. package/blocks/analytics/recipes/02-revenue-snapshot.ts +22 -0
  38. package/blocks/analytics/recipes/03-saved-report.ts +30 -0
  39. package/blocks/appointments/README.md +80 -0
  40. package/blocks/appointments/block.json +218 -0
  41. package/blocks/appointments/components/availability-editor.tsx +192 -0
  42. package/blocks/appointments/components/booking-page.tsx +154 -0
  43. package/blocks/appointments/components/event-type-editor.tsx +214 -0
  44. package/blocks/appointments/components/meeting-detail.tsx +147 -0
  45. package/blocks/appointments/components/meeting-list.tsx +148 -0
  46. package/blocks/appointments/hooks/use-available-slots.ts +29 -0
  47. package/blocks/appointments/hooks/use-schedules.ts +18 -0
  48. package/blocks/appointments/lib/appointments-types.ts +159 -0
  49. package/blocks/appointments/lib/idempotency.ts +30 -0
  50. package/blocks/appointments/recipes/01-create-event-type-and-hours.ts +27 -0
  51. package/blocks/appointments/recipes/02-find-slots-and-book.ts +34 -0
  52. package/blocks/appointments/recipes/03-bookings-digest.ts +22 -0
  53. package/blocks/appointments/recipes/04-calendar-invite.ts +21 -0
  54. package/blocks/block.schema.json +130 -0
  55. package/blocks/block.types.ts +109 -0
  56. package/blocks/campaigns/README.md +118 -0
  57. package/blocks/campaigns/block.json +361 -0
  58. package/blocks/campaigns/components/campaign-assets.tsx +180 -0
  59. package/blocks/campaigns/components/campaign-audience-step.tsx +91 -0
  60. package/blocks/campaigns/components/campaign-launch-step.tsx +147 -0
  61. package/blocks/campaigns/components/campaign-list.tsx +120 -0
  62. package/blocks/campaigns/components/campaign-metrics.tsx +93 -0
  63. package/blocks/campaigns/components/campaign-select.tsx +52 -0
  64. package/blocks/campaigns/components/campaign-sequence-step.tsx +158 -0
  65. package/blocks/campaigns/components/campaign-wizard.tsx +238 -0
  66. package/blocks/campaigns/components/form-field.tsx +29 -0
  67. package/blocks/campaigns/components/icp-editor.tsx +230 -0
  68. package/blocks/campaigns/components/persona-editor.tsx +210 -0
  69. package/blocks/campaigns/hooks/use-company-profile.ts +34 -0
  70. package/blocks/campaigns/lib/campaigns-utils.ts +155 -0
  71. package/blocks/campaigns/recipes/attach-audience-and-launch.ts +33 -0
  72. package/blocks/campaigns/recipes/campaign-report.ts +22 -0
  73. package/blocks/campaigns/recipes/generate-ideas-and-create-campaign.ts +51 -0
  74. package/blocks/campaigns/recipes/set-up-icp-and-personas.ts +37 -0
  75. package/blocks/coach/README.md +93 -0
  76. package/blocks/coach/block.json +125 -0
  77. package/blocks/coach/components/call-scorecard.tsx +126 -0
  78. package/blocks/coach/components/coaching-feed.tsx +140 -0
  79. package/blocks/coach/lib/coach-types.ts +116 -0
  80. package/blocks/coach/recipes/read-a-scorecard.ts +27 -0
  81. package/blocks/coach/recipes/weekly-coaching-digest.ts +53 -0
  82. package/blocks/copilot/README.md +99 -0
  83. package/blocks/copilot/block.json +151 -0
  84. package/blocks/copilot/components/copilot-memory.tsx +87 -0
  85. package/blocks/copilot/components/copilot-panel.tsx +189 -0
  86. package/blocks/copilot/components/copilot-team-chat.tsx +111 -0
  87. package/blocks/copilot/lib/copilot-types.ts +111 -0
  88. package/blocks/copilot/recipes/ask-the-copilot.ts +35 -0
  89. package/blocks/copilot/recipes/post-in-a-work-channel.ts +33 -0
  90. package/blocks/copilot/recipes/remember-and-recall.ts +19 -0
  91. package/blocks/crm/README.md +122 -0
  92. package/blocks/crm/block.json +378 -0
  93. package/blocks/crm/components/activity-timeline.tsx +99 -0
  94. package/blocks/crm/components/company-detail.tsx +118 -0
  95. package/blocks/crm/components/company-table.tsx +173 -0
  96. package/blocks/crm/components/contact-detail.tsx +135 -0
  97. package/blocks/crm/components/contact-table.tsx +217 -0
  98. package/blocks/crm/components/dedupe-review.tsx +164 -0
  99. package/blocks/crm/components/import-wizard.tsx +193 -0
  100. package/blocks/crm/components/notes-panel.tsx +112 -0
  101. package/blocks/crm/components/record-form.tsx +191 -0
  102. package/blocks/crm/components/task-list.tsx +107 -0
  103. package/blocks/crm/hooks/use-custom-fields.ts +18 -0
  104. package/blocks/crm/hooks/use-debounced.ts +13 -0
  105. package/blocks/crm/hooks/use-page-query.ts +52 -0
  106. package/blocks/crm/lib/crm-types.ts +136 -0
  107. package/blocks/crm/recipes/custom-fields-and-values.ts +28 -0
  108. package/blocks/crm/recipes/dedupe-and-merge.ts +30 -0
  109. package/blocks/crm/recipes/notes-tasks-and-activity.ts +23 -0
  110. package/blocks/crm/recipes/upsert-contact-with-company.ts +33 -0
  111. package/blocks/deals/README.md +78 -0
  112. package/blocks/deals/block.json +198 -0
  113. package/blocks/deals/components/deal-detail.tsx +134 -0
  114. package/blocks/deals/components/forecast-view.tsx +112 -0
  115. package/blocks/deals/components/pipeline-board.tsx +142 -0
  116. package/blocks/deals/components/stage-editor.tsx +96 -0
  117. package/blocks/deals/hooks/use-pipeline-deals.ts +67 -0
  118. package/blocks/deals/hooks/use-pipelines.ts +19 -0
  119. package/blocks/deals/lib/deals-types.ts +128 -0
  120. package/blocks/deals/recipes/01-create-deal-and-advance.ts +32 -0
  121. package/blocks/deals/recipes/02-pipeline-report.ts +35 -0
  122. package/blocks/deals/recipes/03-stale-deals-sweep.ts +20 -0
  123. package/blocks/dialer/README.md +114 -0
  124. package/blocks/dialer/block.json +260 -0
  125. package/blocks/dialer/components/call-log.tsx +78 -0
  126. package/blocks/dialer/components/call-queue.tsx +188 -0
  127. package/blocks/dialer/components/dialer-workspace.tsx +47 -0
  128. package/blocks/dialer/components/disposition-panel.tsx +75 -0
  129. package/blocks/dialer/components/live-calls-table.tsx +107 -0
  130. package/blocks/dialer/components/session-start-form.tsx +111 -0
  131. package/blocks/dialer/components/softphone.tsx +122 -0
  132. package/blocks/dialer/components/transcript-viewer.tsx +82 -0
  133. package/blocks/dialer/hooks/use-dialer-numbers.ts +19 -0
  134. package/blocks/dialer/hooks/use-dialer-session.ts +68 -0
  135. package/blocks/dialer/hooks/use-dispositions.ts +29 -0
  136. package/blocks/dialer/lib/dialer-types.ts +158 -0
  137. package/blocks/dialer/lib/softphone-transport.ts +67 -0
  138. package/blocks/dialer/lib/twilio-transport.ts +74 -0
  139. package/blocks/dialer/recipes/01-create-session-and-dial.ts +39 -0
  140. package/blocks/dialer/recipes/02-disposition-calls.ts +26 -0
  141. package/blocks/dialer/recipes/03-review-a-call.ts +23 -0
  142. package/blocks/enrichment/README.md +105 -0
  143. package/blocks/enrichment/block.json +204 -0
  144. package/blocks/enrichment/components/bulk-enrich-panel.tsx +205 -0
  145. package/blocks/enrichment/components/email-verify-badge.tsx +82 -0
  146. package/blocks/enrichment/components/enrich-button.tsx +136 -0
  147. package/blocks/enrichment/components/waterfall-config.tsx +127 -0
  148. package/blocks/enrichment/hooks/use-providers.ts +18 -0
  149. package/blocks/enrichment/lib/enrichment-types.ts +104 -0
  150. package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +41 -0
  151. package/blocks/enrichment/recipes/lookup-and-verify.ts +25 -0
  152. package/blocks/enrichment/recipes/waterfall-pipeline.ts +26 -0
  153. package/blocks/forms/README.md +112 -0
  154. package/blocks/forms/block.json +200 -0
  155. package/blocks/forms/components/form-builder.tsx +150 -0
  156. package/blocks/forms/components/form-embed.tsx +103 -0
  157. package/blocks/forms/components/form-mapping-editor.tsx +125 -0
  158. package/blocks/forms/components/site-audit-panel.tsx +73 -0
  159. package/blocks/forms/components/submissions-table.tsx +158 -0
  160. package/blocks/forms/lib/form-types.ts +127 -0
  161. package/blocks/forms/recipes/install-snippet-and-form.ts +21 -0
  162. package/blocks/forms/recipes/retry-failed-deliveries.ts +26 -0
  163. package/blocks/forms/recipes/route-form-to-list.ts +44 -0
  164. package/blocks/inbox/README.md +72 -0
  165. package/blocks/inbox/block.json +194 -0
  166. package/blocks/inbox/components/inbox-list.tsx +156 -0
  167. package/blocks/inbox/components/inbox-screen.tsx +49 -0
  168. package/blocks/inbox/components/reply-composer.tsx +183 -0
  169. package/blocks/inbox/components/tag-and-assign.tsx +189 -0
  170. package/blocks/inbox/components/thread-badge.tsx +17 -0
  171. package/blocks/inbox/components/thread-view.tsx +108 -0
  172. package/blocks/inbox/hooks/use-inbox-thread.ts +24 -0
  173. package/blocks/inbox/hooks/use-inbox-threads.ts +40 -0
  174. package/blocks/inbox/lib/idempotency.ts +30 -0
  175. package/blocks/inbox/lib/inbox-types.ts +100 -0
  176. package/blocks/inbox/recipes/01-triage-and-reply.ts +43 -0
  177. package/blocks/inbox/recipes/02-tag-and-assign.ts +31 -0
  178. package/blocks/inbox/recipes/03-hot-contacts-digest.ts +16 -0
  179. package/blocks/integrations/README.md +75 -0
  180. package/blocks/integrations/block.json +172 -0
  181. package/blocks/integrations/components/integration-list.tsx +165 -0
  182. package/blocks/integrations/components/sync-status.tsx +223 -0
  183. package/blocks/integrations/components/webhook-manager.tsx +300 -0
  184. package/blocks/integrations/lib/integrations-utils.ts +62 -0
  185. package/blocks/integrations/recipes/crm-sync-health.ts +19 -0
  186. package/blocks/integrations/recipes/push-contacts-to-crm.ts +20 -0
  187. package/blocks/integrations/recipes/register-webhook.ts +18 -0
  188. package/blocks/integrations/recipes/verify-webhook-route.ts +33 -0
  189. package/blocks/intent/README.md +57 -0
  190. package/blocks/intent/block.json +138 -0
  191. package/blocks/intent/components/account-intent-score.tsx +91 -0
  192. package/blocks/intent/components/intent-feed.tsx +215 -0
  193. package/blocks/intent/components/intent-keyword-manager.tsx +331 -0
  194. package/blocks/intent/hooks/use-account-intent.ts +46 -0
  195. package/blocks/intent/lib/intent-utils.ts +95 -0
  196. package/blocks/intent/recipes/rank-intent-accounts.ts +36 -0
  197. package/blocks/intent/recipes/track-competitor-domain.ts +23 -0
  198. package/blocks/intent/recipes/who-visited-url.ts +18 -0
  199. package/blocks/lifecycle/README.md +79 -0
  200. package/blocks/lifecycle/block.json +198 -0
  201. package/blocks/lifecycle/components/churn-list.tsx +92 -0
  202. package/blocks/lifecycle/components/customer-table.tsx +119 -0
  203. package/blocks/lifecycle/components/lead-queue.tsx +127 -0
  204. package/blocks/lifecycle/components/lifecycle-board.tsx +102 -0
  205. package/blocks/lifecycle/components/stage-move-dialog.tsx +75 -0
  206. package/blocks/lifecycle/hooks/use-debounced.ts +13 -0
  207. package/blocks/lifecycle/hooks/use-lifecycle-stages.ts +27 -0
  208. package/blocks/lifecycle/lib/lifecycle.ts +99 -0
  209. package/blocks/lifecycle/recipes/customer-brief.ts +23 -0
  210. package/blocks/lifecycle/recipes/move-company-stage.ts +17 -0
  211. package/blocks/lifecycle/recipes/read-lifecycle-board.ts +21 -0
  212. package/blocks/lifecycle/recipes/unlock-stage-contacts.ts +25 -0
  213. package/blocks/lists/README.md +96 -0
  214. package/blocks/lists/block.json +166 -0
  215. package/blocks/lists/components/add-to-list-button.tsx +111 -0
  216. package/blocks/lists/components/list-picker.tsx +94 -0
  217. package/blocks/lists/components/list-table.tsx +160 -0
  218. package/blocks/lists/components/suppression-manager.tsx +153 -0
  219. package/blocks/lists/recipes/create-list-and-add-contacts.ts +35 -0
  220. package/blocks/lists/recipes/refresh-and-check-lists.ts +21 -0
  221. package/blocks/lists/recipes/suppress-and-reinstate.ts +30 -0
  222. package/blocks/mailboxes/README.md +71 -0
  223. package/blocks/mailboxes/block.json +187 -0
  224. package/blocks/mailboxes/components/connect-mailbox.tsx +284 -0
  225. package/blocks/mailboxes/components/domain-purchase.tsx +271 -0
  226. package/blocks/mailboxes/components/mailbox-list.tsx +185 -0
  227. package/blocks/mailboxes/components/warmup-health.tsx +232 -0
  228. package/blocks/mailboxes/lib/mailboxes-utils.ts +69 -0
  229. package/blocks/mailboxes/recipes/buy-domain-and-mailboxes.ts +64 -0
  230. package/blocks/mailboxes/recipes/connect-imap-mailbox.ts +38 -0
  231. package/blocks/mailboxes/recipes/is-it-safe-to-send.ts +17 -0
  232. package/blocks/mailboxes/recipes/warmup-report.ts +31 -0
  233. package/blocks/newsletter/README.md +122 -0
  234. package/blocks/newsletter/block.json +224 -0
  235. package/blocks/newsletter/components/issue-form.tsx +124 -0
  236. package/blocks/newsletter/components/issue-scheduler.tsx +152 -0
  237. package/blocks/newsletter/components/newsletter-editor.tsx +131 -0
  238. package/blocks/newsletter/components/newsletter-stats.tsx +93 -0
  239. package/blocks/newsletter/components/subscriber-table.tsx +173 -0
  240. package/blocks/newsletter/lib/newsletter-types.ts +97 -0
  241. package/blocks/newsletter/recipes/create-newsletter-and-draft-issue.ts +27 -0
  242. package/blocks/newsletter/recipes/import-audience-and-send.ts +43 -0
  243. package/blocks/newsletter/recipes/issue-performance-report.ts +26 -0
  244. package/blocks/nurture/README.md +110 -0
  245. package/blocks/nurture/block.json +191 -0
  246. package/blocks/nurture/components/nurture-builder.tsx +114 -0
  247. package/blocks/nurture/components/nurture-enroll.tsx +107 -0
  248. package/blocks/nurture/components/nurture-form.tsx +120 -0
  249. package/blocks/nurture/components/nurture-stats.tsx +81 -0
  250. package/blocks/nurture/lib/nurture-types.ts +109 -0
  251. package/blocks/nurture/recipes/create-a-nurture.ts +34 -0
  252. package/blocks/nurture/recipes/enroll-contacts-and-launch.ts +24 -0
  253. package/blocks/nurture/recipes/nurture-health-check.ts +28 -0
  254. package/blocks/objects/README.md +120 -0
  255. package/blocks/objects/block.json +243 -0
  256. package/blocks/objects/components/attribute-field-input.tsx +82 -0
  257. package/blocks/objects/components/attribute-form-dialog.tsx +159 -0
  258. package/blocks/objects/components/object-not-found.tsx +34 -0
  259. package/blocks/objects/components/object-record-detail.tsx +143 -0
  260. package/blocks/objects/components/object-schema-editor.tsx +197 -0
  261. package/blocks/objects/components/object-table.tsx +130 -0
  262. package/blocks/objects/components/record-form-dialog.tsx +112 -0
  263. package/blocks/objects/components/record-roles-panel.tsx +106 -0
  264. package/blocks/objects/hooks/use-object-schema.ts +36 -0
  265. package/blocks/objects/lib/object-types.ts +214 -0
  266. package/blocks/objects/recipes/define-object-and-add-records.ts +24 -0
  267. package/blocks/objects/recipes/update-audit-and-archive-records.ts +39 -0
  268. package/blocks/objects/recipes/upsert-records-from-your-system.ts +30 -0
  269. package/blocks/pages/README.md +104 -0
  270. package/blocks/pages/block.json +223 -0
  271. package/blocks/pages/components/brand-kit-editor.tsx +152 -0
  272. package/blocks/pages/components/content-library.tsx +163 -0
  273. package/blocks/pages/components/page-editor.tsx +253 -0
  274. package/blocks/pages/components/page-list.tsx +159 -0
  275. package/blocks/pages/components/seo-report.tsx +159 -0
  276. package/blocks/pages/lib/studio.ts +77 -0
  277. package/blocks/pages/recipes/audit-site-seo.ts +28 -0
  278. package/blocks/pages/recipes/create-and-publish-page.ts +26 -0
  279. package/blocks/pages/recipes/edit-page-section.ts +25 -0
  280. package/blocks/pages/recipes/refresh-brand-kit.ts +22 -0
  281. package/blocks/quotes/README.md +79 -0
  282. package/blocks/quotes/block.json +183 -0
  283. package/blocks/quotes/components/product-picker.tsx +80 -0
  284. package/blocks/quotes/components/quote-builder.tsx +195 -0
  285. package/blocks/quotes/components/quote-list.tsx +79 -0
  286. package/blocks/quotes/components/quote-viewer.tsx +135 -0
  287. package/blocks/quotes/components/signature-status.tsx +77 -0
  288. package/blocks/quotes/hooks/use-debounced.ts +13 -0
  289. package/blocks/quotes/lib/quotes.ts +142 -0
  290. package/blocks/quotes/recipes/create-and-send-quote.ts +44 -0
  291. package/blocks/quotes/recipes/revise-sent-quote.ts +18 -0
  292. package/blocks/quotes/recipes/track-quote-signature.ts +22 -0
  293. package/blocks/radar/README.md +106 -0
  294. package/blocks/radar/block.json +189 -0
  295. package/blocks/radar/components/competitor-battle-card.tsx +53 -0
  296. package/blocks/radar/components/competitor-tracker.tsx +158 -0
  297. package/blocks/radar/components/radar-overview.tsx +67 -0
  298. package/blocks/radar/components/radar-report.tsx +86 -0
  299. package/blocks/radar/hooks/use-competitors.ts +23 -0
  300. package/blocks/radar/lib/radar-types.ts +124 -0
  301. package/blocks/radar/recipes/battle-card.ts +28 -0
  302. package/blocks/radar/recipes/deep-dive-report.ts +32 -0
  303. package/blocks/radar/recipes/marketing-snapshot.ts +18 -0
  304. package/blocks/radar/recipes/track-and-scan.ts +39 -0
  305. package/blocks/search/README.md +129 -0
  306. package/blocks/search/block.json +185 -0
  307. package/blocks/search/components/filter-builder.tsx +149 -0
  308. package/blocks/search/components/prospect-results.tsx +175 -0
  309. package/blocks/search/components/save-to-list-dialog.tsx +135 -0
  310. package/blocks/search/components/saved-searches.tsx +95 -0
  311. package/blocks/search/hooks/use-filter-options.ts +41 -0
  312. package/blocks/search/lib/search-types.ts +149 -0
  313. package/blocks/search/recipes/explore-filter-values.ts +27 -0
  314. package/blocks/search/recipes/saved-searches.ts +34 -0
  315. package/blocks/search/recipes/search-and-save-to-list.ts +37 -0
  316. package/blocks/sequencer/README.md +87 -0
  317. package/blocks/sequencer/block.json +214 -0
  318. package/blocks/sequencer/components/enroll-dialog.tsx +93 -0
  319. package/blocks/sequencer/components/sequence-builder.tsx +306 -0
  320. package/blocks/sequencer/components/sequence-list.tsx +200 -0
  321. package/blocks/sequencer/components/sequence-stats.tsx +256 -0
  322. package/blocks/sequencer/components/step-editor.tsx +213 -0
  323. package/blocks/sequencer/lib/sequencer-utils.ts +168 -0
  324. package/blocks/sequencer/recipes/create-multichannel-sequence.ts +51 -0
  325. package/blocks/sequencer/recipes/launch-and-enroll.ts +44 -0
  326. package/blocks/sequencer/recipes/pause-when-unsafe.ts +30 -0
  327. package/blocks/sequencer/recipes/sequence-report.ts +22 -0
  328. package/blocks/social-ads/README.md +128 -0
  329. package/blocks/social-ads/block.json +256 -0
  330. package/blocks/social-ads/components/ad-campaign-list.tsx +136 -0
  331. package/blocks/social-ads/components/audience-sync.tsx +150 -0
  332. package/blocks/social-ads/components/creative-gallery.tsx +121 -0
  333. package/blocks/social-ads/components/post-composer.tsx +122 -0
  334. package/blocks/social-ads/components/social-post-list.tsx +116 -0
  335. package/blocks/social-ads/hooks/use-social-accounts.ts +16 -0
  336. package/blocks/social-ads/lib/social-ads-types.ts +167 -0
  337. package/blocks/social-ads/recipes/creative-to-gallery.ts +41 -0
  338. package/blocks/social-ads/recipes/schedule-post.ts +51 -0
  339. package/blocks/social-ads/recipes/sync-audience-and-launch.ts +41 -0
  340. package/blocks/visitors/README.md +56 -0
  341. package/blocks/visitors/block.json +139 -0
  342. package/blocks/visitors/components/snippet-install.tsx +169 -0
  343. package/blocks/visitors/components/traffic-chart.tsx +156 -0
  344. package/blocks/visitors/components/visitor-company-card.tsx +92 -0
  345. package/blocks/visitors/components/visitor-feed.tsx +157 -0
  346. package/blocks/visitors/lib/visitors-utils.ts +78 -0
  347. package/blocks/visitors/recipes/check-snippet-installed.ts +24 -0
  348. package/blocks/visitors/recipes/recent-company-visits.ts +27 -0
  349. package/blocks/visitors/recipes/traffic-summary.ts +24 -0
  350. package/blocks/webchat/README.md +87 -0
  351. package/blocks/webchat/block.json +134 -0
  352. package/blocks/webchat/components/chat-widget-config.tsx +160 -0
  353. package/blocks/webchat/components/conversation-list.tsx +74 -0
  354. package/blocks/webchat/lib/webchat-types.ts +83 -0
  355. package/blocks/webchat/recipes/create-bot-and-style-widget.ts +24 -0
  356. package/blocks/webchat/recipes/list-chat-visitors.ts +24 -0
  357. package/blocks/workbench/README.md +135 -0
  358. package/blocks/workbench/block.json +259 -0
  359. package/blocks/workbench/components/column-recipe-editor.tsx +183 -0
  360. package/blocks/workbench/components/run-progress.tsx +132 -0
  361. package/blocks/workbench/components/workbench-add-rows.tsx +100 -0
  362. package/blocks/workbench/components/workbench-grid.tsx +159 -0
  363. package/blocks/workbench/components/workbench-row-actions.tsx +102 -0
  364. package/blocks/workbench/components/workbench-table-list.tsx +105 -0
  365. package/blocks/workbench/hooks/use-workbench-table.ts +30 -0
  366. package/blocks/workbench/lib/waterfall-presets.ts +119 -0
  367. package/blocks/workbench/lib/workbench-types.ts +191 -0
  368. package/blocks/workbench/recipes/build-enriched-table.ts +65 -0
  369. package/blocks/workbench/recipes/import-from-search.ts +42 -0
  370. package/blocks/workbench/recipes/scrape-into-table.ts +36 -0
  371. package/blocks/workbench/recipes/waterfall-column.ts +55 -0
  372. package/blocks/workflows/README.md +100 -0
  373. package/blocks/workflows/block.json +188 -0
  374. package/blocks/workflows/components/run-history.tsx +136 -0
  375. package/blocks/workflows/components/trigger-config.tsx +134 -0
  376. package/blocks/workflows/components/workflow-editor.tsx +142 -0
  377. package/blocks/workflows/components/workflow-list.tsx +153 -0
  378. package/blocks/workflows/hooks/use-workflow.ts +51 -0
  379. package/blocks/workflows/lib/graph.ts +187 -0
  380. package/blocks/workflows/recipes/create-workflow.ts +35 -0
  381. package/blocks/workflows/recipes/find-failed-runs.ts +15 -0
  382. package/blocks/workflows/recipes/run-workflow-and-wait.ts +32 -0
  383. package/blocks/workflows/recipes/set-trigger.ts +22 -0
  384. package/dist/cli.js +5330 -29
  385. package/dist/index.browser.js +7740 -0
  386. package/dist/index.browser.js.map +1 -0
  387. package/dist/index.browser.mjs +7717 -0
  388. package/dist/index.browser.mjs.map +1 -0
  389. package/dist/index.d.mts +1440 -21
  390. package/dist/index.d.ts +1440 -21
  391. package/dist/index.js +42 -3
  392. package/dist/index.js.map +1 -1
  393. package/dist/index.mjs +42 -3
  394. package/dist/index.mjs.map +1 -1
  395. package/dist/react.browser.js +7598 -0
  396. package/dist/react.browser.js.map +1 -0
  397. package/dist/react.browser.mjs +7568 -0
  398. package/dist/react.browser.mjs.map +1 -0
  399. package/dist/react.d.mts +1340 -18
  400. package/dist/react.d.ts +1340 -18
  401. package/dist/react.js +41 -2
  402. package/dist/react.js.map +1 -1
  403. package/dist/react.mjs +41 -2
  404. package/dist/react.mjs.map +1 -1
  405. package/dist/types/agentSetup.d.ts +6 -6
  406. package/dist/types/blocks/add.d.ts +53 -0
  407. package/dist/types/blocks/create.d.ts +46 -0
  408. package/dist/types/blocks/doctor.d.ts +26 -0
  409. package/dist/types/blocks/registry.d.ts +134 -0
  410. package/dist/types/cli.d.ts +16 -0
  411. package/dist/types/core.d.ts +1 -1
  412. package/dist/types/crypto-browser.d.ts +2 -0
  413. package/dist/types/crypto-node.d.ts +6 -0
  414. package/dist/types/react.d.ts +1 -1
  415. package/dist/types/version.d.ts +2 -2
  416. package/dist/types/webhooks.d.ts +2 -2
  417. package/dist/ui/index.d.mts +54 -0
  418. package/dist/ui/index.d.ts +54 -0
  419. package/dist/ui/index.js +105 -0
  420. package/dist/ui/index.js.map +1 -0
  421. package/dist/ui/index.mjs +44 -0
  422. package/dist/ui/index.mjs.map +1 -0
  423. package/dist/ui/lib/compose-refs.d.mts +19 -0
  424. package/dist/ui/lib/compose-refs.d.ts +19 -0
  425. package/dist/ui/lib/compose-refs.js +76 -0
  426. package/dist/ui/lib/compose-refs.js.map +1 -0
  427. package/dist/ui/lib/compose-refs.mjs +41 -0
  428. package/dist/ui/lib/compose-refs.mjs.map +1 -0
  429. package/dist/ui/lib/use-mobile.d.mts +4 -0
  430. package/dist/ui/lib/use-mobile.d.ts +4 -0
  431. package/dist/ui/lib/use-mobile.js +57 -0
  432. package/dist/ui/lib/use-mobile.js.map +1 -0
  433. package/dist/ui/lib/use-mobile.mjs +22 -0
  434. package/dist/ui/lib/use-mobile.mjs.map +1 -0
  435. package/dist/ui/lib/utils.d.mts +10 -0
  436. package/dist/ui/lib/utils.d.ts +10 -0
  437. package/dist/ui/lib/utils.js +33 -0
  438. package/dist/ui/lib/utils.js.map +1 -0
  439. package/dist/ui/lib/utils.mjs +9 -0
  440. package/dist/ui/lib/utils.mjs.map +1 -0
  441. package/dist/ui/primitives/alert-dialog.d.mts +22 -0
  442. package/dist/ui/primitives/alert-dialog.d.ts +22 -0
  443. package/dist/ui/primitives/alert-dialog.js +163 -0
  444. package/dist/ui/primitives/alert-dialog.js.map +1 -0
  445. package/dist/ui/primitives/alert-dialog.mjs +119 -0
  446. package/dist/ui/primitives/alert-dialog.mjs.map +1 -0
  447. package/dist/ui/primitives/avatar.d.mts +8 -0
  448. package/dist/ui/primitives/avatar.d.ts +8 -0
  449. package/dist/ui/primitives/avatar.js +91 -0
  450. package/dist/ui/primitives/avatar.js.map +1 -0
  451. package/dist/ui/primitives/avatar.mjs +55 -0
  452. package/dist/ui/primitives/avatar.mjs.map +1 -0
  453. package/dist/ui/primitives/breadcrumb.d.mts +13 -0
  454. package/dist/ui/primitives/breadcrumb.d.ts +13 -0
  455. package/dist/ui/primitives/breadcrumb.js +127 -0
  456. package/dist/ui/primitives/breadcrumb.js.map +1 -0
  457. package/dist/ui/primitives/breadcrumb.mjs +97 -0
  458. package/dist/ui/primitives/breadcrumb.mjs.map +1 -0
  459. package/dist/ui/primitives/button.d.mts +15 -0
  460. package/dist/ui/primitives/button.d.ts +15 -0
  461. package/dist/ui/primitives/button.js +91 -0
  462. package/dist/ui/primitives/button.js.map +1 -0
  463. package/dist/ui/primitives/button.mjs +56 -0
  464. package/dist/ui/primitives/button.mjs.map +1 -0
  465. package/dist/ui/primitives/card.d.mts +10 -0
  466. package/dist/ui/primitives/card.d.ts +10 -0
  467. package/dist/ui/primitives/card.js +101 -0
  468. package/dist/ui/primitives/card.js.map +1 -0
  469. package/dist/ui/primitives/card.mjs +62 -0
  470. package/dist/ui/primitives/card.mjs.map +1 -0
  471. package/dist/ui/primitives/carousel.d.mts +45 -0
  472. package/dist/ui/primitives/carousel.d.ts +45 -0
  473. package/dist/ui/primitives/carousel.js +300 -0
  474. package/dist/ui/primitives/carousel.js.map +1 -0
  475. package/dist/ui/primitives/carousel.mjs +261 -0
  476. package/dist/ui/primitives/carousel.mjs.map +1 -0
  477. package/dist/ui/primitives/chart.d.mts +68 -0
  478. package/dist/ui/primitives/chart.d.ts +68 -0
  479. package/dist/ui/primitives/chart.js +280 -0
  480. package/dist/ui/primitives/chart.js.map +1 -0
  481. package/dist/ui/primitives/chart.mjs +241 -0
  482. package/dist/ui/primitives/chart.mjs.map +1 -0
  483. package/dist/ui/primitives/collapsible.d.mts +8 -0
  484. package/dist/ui/primitives/collapsible.d.ts +8 -0
  485. package/dist/ui/primitives/collapsible.js +72 -0
  486. package/dist/ui/primitives/collapsible.js.map +1 -0
  487. package/dist/ui/primitives/collapsible.mjs +36 -0
  488. package/dist/ui/primitives/collapsible.mjs.map +1 -0
  489. package/dist/ui/primitives/context-menu.d.mts +29 -0
  490. package/dist/ui/primitives/context-menu.d.ts +29 -0
  491. package/dist/ui/primitives/context-menu.js +203 -0
  492. package/dist/ui/primitives/context-menu.js.map +1 -0
  493. package/dist/ui/primitives/context-menu.mjs +155 -0
  494. package/dist/ui/primitives/context-menu.mjs.map +1 -0
  495. package/dist/ui/primitives/dialog.d.mts +26 -0
  496. package/dist/ui/primitives/dialog.d.ts +26 -0
  497. package/dist/ui/primitives/dialog.js +192 -0
  498. package/dist/ui/primitives/dialog.js.map +1 -0
  499. package/dist/ui/primitives/dialog.mjs +148 -0
  500. package/dist/ui/primitives/dialog.mjs.map +1 -0
  501. package/dist/ui/primitives/drawer.d.mts +26 -0
  502. package/dist/ui/primitives/drawer.d.ts +26 -0
  503. package/dist/ui/primitives/drawer.js +146 -0
  504. package/dist/ui/primitives/drawer.js.map +1 -0
  505. package/dist/ui/primitives/drawer.mjs +103 -0
  506. package/dist/ui/primitives/drawer.mjs.map +1 -0
  507. package/dist/ui/primitives/input.d.mts +5 -0
  508. package/dist/ui/primitives/input.d.ts +5 -0
  509. package/dist/ui/primitives/input.js +46 -0
  510. package/dist/ui/primitives/input.js.map +1 -0
  511. package/dist/ui/primitives/input.mjs +22 -0
  512. package/dist/ui/primitives/input.mjs.map +1 -0
  513. package/dist/ui/primitives/item.d.mts +27 -0
  514. package/dist/ui/primitives/item.d.ts +27 -0
  515. package/dist/ui/primitives/item.js +221 -0
  516. package/dist/ui/primitives/item.js.map +1 -0
  517. package/dist/ui/primitives/item.mjs +188 -0
  518. package/dist/ui/primitives/item.mjs.map +1 -0
  519. package/dist/ui/primitives/kbd.d.mts +15 -0
  520. package/dist/ui/primitives/kbd.d.ts +15 -0
  521. package/dist/ui/primitives/kbd.js +45 -0
  522. package/dist/ui/primitives/kbd.js.map +1 -0
  523. package/dist/ui/primitives/kbd.mjs +21 -0
  524. package/dist/ui/primitives/kbd.mjs.map +1 -0
  525. package/dist/ui/primitives/label.d.mts +6 -0
  526. package/dist/ui/primitives/label.d.ts +6 -0
  527. package/dist/ui/primitives/label.js +55 -0
  528. package/dist/ui/primitives/label.js.map +1 -0
  529. package/dist/ui/primitives/label.mjs +21 -0
  530. package/dist/ui/primitives/label.mjs.map +1 -0
  531. package/dist/ui/primitives/resizable.d.mts +10 -0
  532. package/dist/ui/primitives/resizable.d.ts +10 -0
  533. package/dist/ui/primitives/resizable.js +68 -0
  534. package/dist/ui/primitives/resizable.js.map +1 -0
  535. package/dist/ui/primitives/resizable.mjs +42 -0
  536. package/dist/ui/primitives/resizable.mjs.map +1 -0
  537. package/dist/ui/primitives/save-status.d.mts +31 -0
  538. package/dist/ui/primitives/save-status.d.ts +31 -0
  539. package/dist/ui/primitives/save-status.js +150 -0
  540. package/dist/ui/primitives/save-status.js.map +1 -0
  541. package/dist/ui/primitives/save-status.mjs +126 -0
  542. package/dist/ui/primitives/save-status.mjs.map +1 -0
  543. package/dist/ui/primitives/separator.d.mts +6 -0
  544. package/dist/ui/primitives/separator.d.ts +6 -0
  545. package/dist/ui/primitives/separator.js +62 -0
  546. package/dist/ui/primitives/separator.js.map +1 -0
  547. package/dist/ui/primitives/separator.mjs +28 -0
  548. package/dist/ui/primitives/separator.mjs.map +1 -0
  549. package/dist/ui/primitives/sheet.d.mts +16 -0
  550. package/dist/ui/primitives/sheet.d.ts +16 -0
  551. package/dist/ui/primitives/sheet.js +170 -0
  552. package/dist/ui/primitives/sheet.js.map +1 -0
  553. package/dist/ui/primitives/sheet.mjs +129 -0
  554. package/dist/ui/primitives/sheet.mjs.map +1 -0
  555. package/dist/ui/primitives/sidebar.d.mts +74 -0
  556. package/dist/ui/primitives/sidebar.d.ts +74 -0
  557. package/dist/ui/primitives/sidebar.js +697 -0
  558. package/dist/ui/primitives/sidebar.js.map +1 -0
  559. package/dist/ui/primitives/sidebar.mjs +651 -0
  560. package/dist/ui/primitives/sidebar.mjs.map +1 -0
  561. package/dist/ui/primitives/skeleton.d.mts +5 -0
  562. package/dist/ui/primitives/skeleton.d.ts +5 -0
  563. package/dist/ui/primitives/skeleton.js +40 -0
  564. package/dist/ui/primitives/skeleton.js.map +1 -0
  565. package/dist/ui/primitives/skeleton.mjs +16 -0
  566. package/dist/ui/primitives/skeleton.mjs.map +1 -0
  567. package/dist/ui/primitives/slider.d.mts +20 -0
  568. package/dist/ui/primitives/slider.d.ts +20 -0
  569. package/dist/ui/primitives/slider.js +112 -0
  570. package/dist/ui/primitives/slider.js.map +1 -0
  571. package/dist/ui/primitives/slider.mjs +78 -0
  572. package/dist/ui/primitives/slider.mjs.map +1 -0
  573. package/dist/ui/primitives/sortable.d.mts +50 -0
  574. package/dist/ui/primitives/sortable.d.ts +50 -0
  575. package/dist/ui/primitives/sortable.js +456 -0
  576. package/dist/ui/primitives/sortable.js.map +1 -0
  577. package/dist/ui/primitives/sortable.mjs +435 -0
  578. package/dist/ui/primitives/sortable.mjs.map +1 -0
  579. package/dist/ui/primitives/switch.d.mts +6 -0
  580. package/dist/ui/primitives/switch.d.ts +6 -0
  581. package/dist/ui/primitives/switch.js +67 -0
  582. package/dist/ui/primitives/switch.js.map +1 -0
  583. package/dist/ui/primitives/switch.mjs +33 -0
  584. package/dist/ui/primitives/switch.mjs.map +1 -0
  585. package/dist/ui/primitives/table.d.mts +12 -0
  586. package/dist/ui/primitives/table.d.ts +12 -0
  587. package/dist/ui/primitives/table.js +130 -0
  588. package/dist/ui/primitives/table.js.map +1 -0
  589. package/dist/ui/primitives/table.mjs +89 -0
  590. package/dist/ui/primitives/table.mjs.map +1 -0
  591. package/dist/ui/primitives/tabs.d.mts +9 -0
  592. package/dist/ui/primitives/tabs.d.ts +9 -0
  593. package/dist/ui/primitives/tabs.js +86 -0
  594. package/dist/ui/primitives/tabs.js.map +1 -0
  595. package/dist/ui/primitives/tabs.mjs +49 -0
  596. package/dist/ui/primitives/tabs.mjs.map +1 -0
  597. package/dist/ui/primitives/textarea.d.mts +5 -0
  598. package/dist/ui/primitives/textarea.d.ts +5 -0
  599. package/dist/ui/primitives/textarea.js +43 -0
  600. package/dist/ui/primitives/textarea.js.map +1 -0
  601. package/dist/ui/primitives/textarea.mjs +19 -0
  602. package/dist/ui/primitives/textarea.mjs.map +1 -0
  603. package/dist/ui/primitives/tooltip.d.mts +9 -0
  604. package/dist/ui/primitives/tooltip.d.ts +9 -0
  605. package/dist/ui/primitives/tooltip.js +94 -0
  606. package/dist/ui/primitives/tooltip.js.map +1 -0
  607. package/dist/ui/primitives/tooltip.mjs +57 -0
  608. package/dist/ui/primitives/tooltip.mjs.map +1 -0
  609. package/docs/agents.md +72 -29
  610. package/docs/api/README.md +6 -3
  611. package/docs/api/appObjects.d.ts +1 -1
  612. package/docs/api/appObjects.md +1 -1
  613. package/docs/api/by-path.md +29 -0
  614. package/docs/api/objects.d.ts +72 -5
  615. package/docs/api/objects.md +84 -7
  616. package/docs/api/processLists.d.ts +247 -0
  617. package/docs/api/processLists.md +409 -0
  618. package/docs/api/reports.d.ts +476 -0
  619. package/docs/api/reports.md +539 -0
  620. package/docs/api/scheduleBuy.d.ts +12 -8
  621. package/docs/api/scheduleBuy.md +13 -10
  622. package/docs/api/seo.d.ts +406 -3
  623. package/docs/api/seo.md +505 -1
  624. package/docs/api/workspaceBackup.d.ts +324 -0
  625. package/docs/api/workspaceBackup.md +427 -0
  626. package/package.json +174 -5
  627. package/stacks/6sense.json +46 -0
  628. package/stacks/README.md +20 -0
  629. package/stacks/apollo.json +128 -0
  630. package/stacks/clay.json +59 -0
  631. package/stacks/gong.json +72 -0
  632. package/stacks/hubspot.json +162 -0
  633. package/stacks/intercom.json +53 -0
  634. package/stacks/mailchimp.json +71 -0
  635. package/stacks/outreach.json +86 -0
  636. package/stacks/salesforce.json +94 -0
  637. package/stacks/stack.schema.json +32 -0
  638. package/stacks/starter.json +22 -0
  639. package/styles/styles.css +2 -0
  640. package/styles/theme.css +181 -0
  641. package/styles/tokens.css +284 -0
  642. package/templates/next-app/README.md.tmpl +35 -0
  643. package/templates/next-app/_pages/redirect-page.tsx.tmpl +5 -0
  644. package/templates/next-app/_pages/route-page.tsx.tmpl +9 -0
  645. package/templates/next-app/app/globals.css.tmpl +8 -0
  646. package/templates/next-app/app/layout.tsx.tmpl +19 -0
  647. package/templates/next-app/components/app-shell.tsx.tmpl +34 -0
  648. package/templates/next-app/dot-env.local.example.tmpl +6 -0
  649. package/templates/next-app/dot-gitignore.tmpl +7 -0
  650. package/templates/next-app/lib/nav.ts.tmpl +2 -0
  651. package/templates/next-app/next.config.mjs.tmpl +4 -0
  652. package/templates/next-app/package.json.tmpl +25 -0
  653. package/templates/next-app/postcss.config.mjs.tmpl +4 -0
  654. package/templates/next-app/tsconfig.json.tmpl +21 -0
@@ -0,0 +1,214 @@
1
+ {
2
+ "$schema": "../block.schema.json",
3
+ "id": "sequencer",
4
+ "title": "Sequencer",
5
+ "summary": "Build multi-channel outreach sequences (email, LinkedIn, calls), launch and pause them, enroll contacts, and read performance.",
6
+ "features": [
7
+ "multi-channel step builder with send-time rule checks",
8
+ "launch, pause, resume, duplicate and archive with recipient-count confirmation",
9
+ "enroll contacts with deliverability warning",
10
+ "per-step stats, skip reasons and per-contact control"
11
+ ],
12
+ "dependsOn": [
13
+ "lists",
14
+ "mailboxes"
15
+ ],
16
+ "operations": [
17
+ "list_sequences_sequences_get",
18
+ "preview_sequence_sequences__sequence_id__preview_get",
19
+ "run_sequence_sequences__sequence_id__run_post",
20
+ "pause_sequence_sequences__sequence_id__pause_post",
21
+ "resume_sequence_sequences__sequence_id__resume_post",
22
+ "duplicate_sequence_sequences__sequence_id__duplicate_post",
23
+ "archive_sequence_sequences__sequence_id__archive_post",
24
+ "list_mailboxes_mailboxes_get",
25
+ "list_lists_lists_get",
26
+ "create_sequence_sequences_post",
27
+ "list_sequence_steps_sequences__sequence_id__steps_get",
28
+ "get_sequence_sequences__sequence_id__get",
29
+ "add_sequence_steps_sequences__sequence_id__steps_post",
30
+ "update_sequence_step_sequences__sequence_id__steps__step_id__patch",
31
+ "delete_sequence_step_sequences__sequence_id__steps__step_id__delete",
32
+ "get_outreach_connection_integrations_linkedin_outreach_connection_get",
33
+ "get_safe_to_send_deliverability_safe_to_send_get",
34
+ "add_contacts_to_sequence_sequences__sequence_id__contacts_post",
35
+ "get_sequence_stats_sequences__sequence_id__stats_get",
36
+ "get_sequence_analytics_sequences__sequence_id__analytics_get",
37
+ "list_sequence_contacts_sequences__sequence_id__contacts_get",
38
+ "pause_sequence_contact_sequences__sequence_id__contacts__contact_id__pause_post",
39
+ "resume_sequence_contact_sequences__sequence_id__contacts__contact_id__resume_post"
40
+ ],
41
+ "components": [
42
+ {
43
+ "name": "SequenceList",
44
+ "file": "components/sequence-list.tsx",
45
+ "purpose": "All sequences with status; launch (after a copy preview), pause, resume, duplicate and archive, each confirmed with the recipient count.",
46
+ "operations": [
47
+ "list_sequences_sequences_get",
48
+ "preview_sequence_sequences__sequence_id__preview_get",
49
+ "run_sequence_sequences__sequence_id__run_post",
50
+ "pause_sequence_sequences__sequence_id__pause_post",
51
+ "resume_sequence_sequences__sequence_id__resume_post",
52
+ "duplicate_sequence_sequences__sequence_id__duplicate_post",
53
+ "archive_sequence_sequences__sequence_id__archive_post"
54
+ ],
55
+ "tiers": [
56
+ "read",
57
+ "write",
58
+ "external",
59
+ "destructive"
60
+ ]
61
+ },
62
+ {
63
+ "name": "SequenceBuilder",
64
+ "file": "components/sequence-builder.tsx",
65
+ "purpose": "Build a draft with email, LinkedIn, call and call-task steps and a sender, or edit the steps of an existing sequence.",
66
+ "operations": [
67
+ "list_mailboxes_mailboxes_get",
68
+ "list_lists_lists_get",
69
+ "create_sequence_sequences_post",
70
+ "list_sequence_steps_sequences__sequence_id__steps_get",
71
+ "get_sequence_sequences__sequence_id__get",
72
+ "add_sequence_steps_sequences__sequence_id__steps_post",
73
+ "update_sequence_step_sequences__sequence_id__steps__step_id__patch",
74
+ "delete_sequence_step_sequences__sequence_id__steps__step_id__delete"
75
+ ],
76
+ "tiers": [
77
+ "read",
78
+ "write",
79
+ "external",
80
+ "destructive"
81
+ ]
82
+ },
83
+ {
84
+ "name": "StepEditor",
85
+ "file": "components/step-editor.tsx",
86
+ "purpose": "Controlled form for one step with the rules the API only enforces at send time; shows LinkedIn sender readiness.",
87
+ "operations": [
88
+ "get_outreach_connection_integrations_linkedin_outreach_connection_get"
89
+ ],
90
+ "tiers": [
91
+ "read"
92
+ ]
93
+ },
94
+ {
95
+ "name": "EnrollDialog",
96
+ "file": "components/enroll-dialog.tsx",
97
+ "purpose": "Confirm and enroll contact ids into a sequence, showing the recipient count and a deliverability warning.",
98
+ "operations": [
99
+ "get_sequence_sequences__sequence_id__get",
100
+ "get_safe_to_send_deliverability_safe_to_send_get",
101
+ "add_contacts_to_sequence_sequences__sequence_id__contacts_post"
102
+ ],
103
+ "tiers": [
104
+ "read",
105
+ "external"
106
+ ]
107
+ },
108
+ {
109
+ "name": "SequenceStats",
110
+ "file": "components/sequence-stats.tsx",
111
+ "purpose": "Totals, per-step progress, skip reasons and per-contact state with pause and (confirmed) resume.",
112
+ "operations": [
113
+ "get_sequence_stats_sequences__sequence_id__stats_get",
114
+ "get_sequence_analytics_sequences__sequence_id__analytics_get",
115
+ "list_sequence_contacts_sequences__sequence_id__contacts_get",
116
+ "pause_sequence_contact_sequences__sequence_id__contacts__contact_id__pause_post",
117
+ "resume_sequence_contact_sequences__sequence_id__contacts__contact_id__resume_post"
118
+ ],
119
+ "tiers": [
120
+ "read",
121
+ "write",
122
+ "external"
123
+ ]
124
+ }
125
+ ],
126
+ "hooks": [],
127
+ "lib": [
128
+ {
129
+ "file": "lib/sequencer-utils.ts",
130
+ "purpose": "Step kinds and the draft-to-API mapping, send-time rule checks, status helpers and envelope readers."
131
+ }
132
+ ],
133
+ "recipes": [
134
+ {
135
+ "file": "recipes/create-multichannel-sequence.ts",
136
+ "title": "Create a draft sequence with email, LinkedIn and a call",
137
+ "when": "Building outreach; creating the draft contacts nobody."
138
+ },
139
+ {
140
+ "file": "recipes/launch-and-enroll.ts",
141
+ "title": "Check a draft, launch it, and enroll contacts",
142
+ "when": "Starting outreach; reaches real people, needs approval of the recipient count."
143
+ },
144
+ {
145
+ "file": "recipes/sequence-report.ts",
146
+ "title": "Report how a sequence is doing",
147
+ "when": "A daily digest or an agent deciding whether to pause."
148
+ },
149
+ {
150
+ "file": "recipes/pause-when-unsafe.ts",
151
+ "title": "Pause live sequences when deliverability turns bad",
152
+ "when": "A scheduled guard under a standing human instruction."
153
+ }
154
+ ],
155
+ "prerequisites": [
156
+ {
157
+ "id": "sender-mailbox",
158
+ "description": "At least one connected sending mailbox (the mailboxes block). Google and Microsoft mailboxes are connected in the graph8 app.",
159
+ "where": "api",
160
+ "verify": "GET /mailboxes returns a mailbox with connection_status active."
161
+ },
162
+ {
163
+ "id": "audience-list",
164
+ "description": "The contacts to enroll are saved in a list; enrollment needs both the contact ids and their list id.",
165
+ "where": "api",
166
+ "verify": "GET /lists returns the list and it contains the contacts."
167
+ },
168
+ {
169
+ "id": "linkedin-sender",
170
+ "description": "LinkedIn steps need a connected LinkedIn sender account, set up in the graph8 app. A sequence with LinkedIn steps and no sender cannot launch.",
171
+ "where": "app",
172
+ "verify": "GET /integrations/linkedin-outreach/connection returns connected: true."
173
+ }
174
+ ],
175
+ "scopes": [
176
+ "campaigns:read",
177
+ "integrations:read",
178
+ "lists:read",
179
+ "sequences:delete",
180
+ "sequences:read",
181
+ "sequences:run",
182
+ "sequences:write"
183
+ ],
184
+ "defaults": [
185
+ {
186
+ "decision": "Days between steps",
187
+ "default": "2 days (the first step starts on enrollment)",
188
+ "askHuman": false
189
+ },
190
+ {
191
+ "decision": "Stop the sequence when a contact replies",
192
+ "default": "Yes (finish_on_reply)",
193
+ "askHuman": false
194
+ },
195
+ {
196
+ "decision": "Email copy source",
197
+ "default": "The person writes it (MANUAL_TEMPLATE); AI modes are opt-in",
198
+ "askHuman": false
199
+ },
200
+ {
201
+ "decision": "Who to enroll and when to launch",
202
+ "default": "Never launch or enroll without a human approving the recipient count",
203
+ "askHuman": true
204
+ },
205
+ {
206
+ "decision": "Call steps and do-not-call numbers",
207
+ "default": "Phone steps never dial do-not-call numbers",
208
+ "askHuman": false
209
+ }
210
+ ],
211
+ "peerDependencies": {
212
+ "react": ">=18"
213
+ }
214
+ }
@@ -0,0 +1,93 @@
1
+ "use client";
2
+
3
+ /**
4
+ * EnrollDialog: add contacts to a sequence, behind a confirmation that states how many real people will be
5
+ * contacted. Render it when the person clicks "Enroll"; it owns the confirm step and the call.
6
+ *
7
+ * SDK operations, in order:
8
+ * 1. getSequence GET /sequences/{id} (read; only when `listId` is not passed: reads the
9
+ * sequence's associated_list_id, which the enroll call requires)
10
+ * 2. getSafeToSend GET /deliverability/safe-to-send (read; shown as a warning, never blocks)
11
+ * 3. addContactsToSequence POST /sequences/{id}/contacts (external tier: confirmed with the recipient count;
12
+ * sent with an Idempotency-Key)
13
+ *
14
+ * Tiers: step 3 reaches real people. The call is idempotent server-side (contacts already enrolled are
15
+ * skipped, `contacts_affected` counts only the new ones). `contactIds` come from your CRM table
16
+ * (crm block) and `listId` from the lists block; both are required by the API. Search results are
17
+ * masked until saved to a list, so enroll contacts that are already in a list.
18
+ */
19
+
20
+ import { useRef } from "react";
21
+ import { g8Get, g8Post } from "@/g8/lib/g8-client";
22
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
23
+ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
24
+ import { ApiError } from "@/g8/components/api-error";
25
+ import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
26
+ import { newIdempotencyKey, objectOf } from "../lib/sequencer-utils";
27
+
28
+ export interface EnrollDialogProps {
29
+ open: boolean;
30
+ sequenceId: string;
31
+ sequenceName?: string;
32
+ /** Contact ids to enroll (from the crm block's table selection). */
33
+ contactIds: readonly number[];
34
+ /** The list those contacts came from. Default: the sequence's associated list. */
35
+ listId?: number;
36
+ onClose: () => void;
37
+ onEnrolled?: (result: { contactsAffected: number }) => void;
38
+ }
39
+
40
+ function Verdict() {
41
+ const verdict = useG8Query((signal) => g8Get("/deliverability/safe-to-send", undefined, { signal }), []);
42
+ const v = objectOf(verdict.data);
43
+ if ((v.safe_to_send ?? v.safe) !== false) return null;
44
+ return (
45
+ <p role="alert" className="mt-2 rounded bg-amber-50 p-2 text-amber-900">
46
+ Deliverability warning: {typeof v.reason === "string" ? v.reason : "sending is not recommended right now."}
47
+ </p>
48
+ );
49
+ }
50
+
51
+ export function EnrollDialog({ open, sequenceId, sequenceName, contactIds, listId, onClose, onEnrolled }: EnrollDialogProps) {
52
+ const key = useRef<{ sig: string; value: string } | null>(null);
53
+ const sequence = useG8Query((signal) => g8Get(`/sequences/${sequenceId}`, undefined, { signal }), [sequenceId], {
54
+ enabled: open && listId == null,
55
+ });
56
+ const resolvedList = listId ?? (objectOf(sequence.data).associated_list_id as number | undefined);
57
+
58
+ const enroll = useG8Mutation(async () => {
59
+ const body = { contact_ids: [...contactIds], list_id: resolvedList };
60
+ const sig = JSON.stringify([sequenceId, body]);
61
+ if (key.current?.sig !== sig) key.current = { sig, value: newIdempotencyKey() };
62
+ const res = objectOf(await g8Post(`/sequences/${sequenceId}/contacts`, body, { idempotencyKey: key.current.value }));
63
+ onEnrolled?.({ contactsAffected: Number(res.contacts_affected ?? 0) });
64
+ onClose();
65
+ });
66
+
67
+ if (!open) return null;
68
+ const missingList = listId == null && !sequence.loading && resolvedList == null;
69
+ return (
70
+ <ConfirmSpendDialog
71
+ open
72
+ tier="external"
73
+ title={`Enroll ${contactIds.length.toLocaleString()} contact${contactIds.length === 1 ? "" : "s"} in "${sequenceName ?? sequenceId}"`}
74
+ description={
75
+ <div>
76
+ <p>They start receiving this sequence's steps. Contacts already enrolled are skipped.</p>
77
+ {missingList ? (
78
+ <p role="alert" className="mt-2 text-red-700">
79
+ This sequence has no audience list. Pass the list these contacts came from as `listId`.
80
+ </p>
81
+ ) : null}
82
+ {enroll.error ? <ApiError error={enroll.error} onRetry={enroll.reset} className="mt-2" /> : null}
83
+ <Verdict />
84
+ </div>
85
+ }
86
+ recipients={contactIds.length}
87
+ confirmLabel="Enroll contacts"
88
+ confirming={enroll.loading || (listId == null && sequence.loading) || missingList || contactIds.length === 0}
89
+ onCancel={onClose}
90
+ onConfirm={() => void enroll.mutate(undefined)}
91
+ />
92
+ );
93
+ }
@@ -0,0 +1,306 @@
1
+ "use client";
2
+
3
+ /**
4
+ * SequenceBuilder: build a multi-channel outreach sequence (email, LinkedIn, call, call task) and send it
5
+ * to graph8 as a DRAFT, or edit the steps of an existing sequence. Nobody is contacted until the sequence
6
+ * is launched (SequenceList does that).
7
+ *
8
+ * SDK operations, in order:
9
+ * create: 1. listMailboxes GET /mailboxes (read; the sender)
10
+ * 2. listLists GET /lists (read; the audience, becomes associated_list_id)
11
+ * 3. createSequence POST /sequences (external tier: confirmed; steps + sender in ONE call)
12
+ * edit: 1. getSequence / listSequenceSteps GET /sequences/{id}, /sequences/{id}/steps (read)
13
+ * 2. addSequenceSteps POST /sequences/{id}/steps (write; appended steps)
14
+ * 3. updateSequenceStep PATCH /sequences/{id}/steps/{step_id} (external: confirmed; replaces step_data)
15
+ * 4. deleteSequenceStep DELETE /sequences/{id}/steps/{step_id} (destructive: confirmed)
16
+ *
17
+ * Ids passed between steps: the chosen mailbox `id` + `email` become the sequence channel; the list `id` becomes
18
+ * `associated_list_id`; createSequence returns the new sequence `id` (handed to `onSaved`). Changing a LIVE
19
+ * sequence's copy changes what real people receive, which is why the update dialog says so.
20
+ */
21
+
22
+ import { useRef, useState } from "react";
23
+ import { Button, Input, Skeleton } from "@graph8/sdk/ui";
24
+ import { g8Delete, g8Get, g8Patch, g8Post } from "@/g8/lib/g8-client";
25
+ import { useG8Query } from "@/g8/hooks/use-g8-query";
26
+ import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
27
+ import { ApiError } from "@/g8/components/api-error";
28
+ import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
29
+ import type { ConfirmTier } from "@/g8/components/confirm-spend-dialog";
30
+ import { StepEditor } from "./step-editor";
31
+ import {
32
+ channelTypeFor,
33
+ fromApiStep,
34
+ KIND_LABEL,
35
+ KINDS,
36
+ listOf,
37
+ newIdempotencyKey,
38
+ newStep,
39
+ objectOf,
40
+ statusGroup,
41
+ stepIssues,
42
+ toApiStep,
43
+ } from "../lib/sequencer-utils";
44
+ import type { ApiStep, DraftStep, StepKind } from "../lib/sequencer-utils";
45
+
46
+ export interface SequenceBuilderProps {
47
+ /** Edit this sequence's steps. Omit to build a new draft. */
48
+ sequenceId?: string;
49
+ defaultListId?: number;
50
+ /** Optional owner email sent as `user_email` on create. */
51
+ ownerEmail?: string;
52
+ onSaved?: (sequenceId: string) => void;
53
+ className?: string;
54
+ }
55
+
56
+ interface MailboxOption {
57
+ id: string;
58
+ email: string | null;
59
+ provider?: string | null;
60
+ }
61
+ interface ListOption {
62
+ id: number;
63
+ title: string;
64
+ contact_count?: number | null;
65
+ }
66
+ type Pending = { kind: "create" } | { kind: "update" | "delete"; step: DraftStep };
67
+
68
+ function describe(p: Pending, live: boolean): { tier: ConfirmTier; title: string; description: string; label: string } {
69
+ if (p.kind === "create")
70
+ return {
71
+ tier: "external",
72
+ title: "Create this sequence as a draft",
73
+ description: "Saves the steps and sender. Nobody is contacted until you launch it.",
74
+ label: "Create draft",
75
+ };
76
+ if (p.kind === "update")
77
+ return {
78
+ tier: "external",
79
+ title: "Save changes to this step",
80
+ description: live
81
+ ? "This sequence is LIVE: contacts will receive the new version from their next send."
82
+ : "The step's content is replaced with what you see.",
83
+ label: "Save step",
84
+ };
85
+ return {
86
+ tier: "destructive",
87
+ title: "Delete this step",
88
+ description: "Contacts waiting on it move on. This cannot be undone.",
89
+ label: "Delete step",
90
+ };
91
+ }
92
+
93
+ export function SequenceBuilder({ sequenceId, defaultListId, ownerEmail, onSaved, className }: SequenceBuilderProps) {
94
+ const editing = Boolean(sequenceId);
95
+ const [name, setName] = useState("");
96
+ const [mailboxId, setMailboxId] = useState("");
97
+ const [listId, setListId] = useState(defaultListId ? String(defaultListId) : "");
98
+ const [steps, setSteps] = useState<DraftStep[]>([]);
99
+ const [loadedFor, setLoadedFor] = useState<string | null>(null);
100
+ const [pending, setPending] = useState<Pending | null>(null);
101
+ const [saved, setSaved] = useState<string | null>(null);
102
+ // One key per distinct request, so a retry after a timeout cannot create or append twice.
103
+ const keys = useRef<Record<string, { sig: string; value: string }>>({});
104
+ const keyFor = (slot: string, payload: unknown): string => {
105
+ const sig = JSON.stringify(payload);
106
+ if (keys.current[slot]?.sig !== sig) keys.current[slot] = { sig, value: newIdempotencyKey() };
107
+ return keys.current[slot].value;
108
+ };
109
+
110
+ const mailboxes = useG8Query((signal) => g8Get("/mailboxes", { limit: 200 }, { signal }), [], { enabled: !editing });
111
+ const lists = useG8Query((signal) => g8Get("/lists", { limit: 100 }, { signal }), [], { enabled: !editing });
112
+ const existing = useG8Query(
113
+ async (signal) => {
114
+ const [seq, list] = await Promise.all([
115
+ g8Get(`/sequences/${sequenceId}`, undefined, { signal }),
116
+ g8Get(`/sequences/${sequenceId}/steps`, undefined, { signal }),
117
+ ]);
118
+ return { status: String(objectOf(seq).status ?? ""), steps: listOf<ApiStep>(list).map(fromApiStep) };
119
+ },
120
+ [sequenceId],
121
+ { enabled: editing },
122
+ );
123
+ if (editing && existing.data && loadedFor !== sequenceId) {
124
+ setLoadedFor(sequenceId ?? null);
125
+ setSteps(existing.data.steps);
126
+ }
127
+ const live = statusGroup(existing.data?.status) === "live";
128
+
129
+ const run = useG8Mutation(async (p: Pending) => {
130
+ if (p.kind === "create") {
131
+ const mailbox = listOf<MailboxOption>(mailboxes.data).find((m) => m.id === mailboxId);
132
+ const body = {
133
+ name: name.trim(),
134
+ user_email: ownerEmail,
135
+ associated_list_id: listId ? Number(listId) : undefined,
136
+ finish_on_reply: true,
137
+ steps: steps.map((s, i) => toApiStep(s, i + 1)),
138
+ channels: mailbox
139
+ ? [{ channel_id: Number(mailbox.id), channel_value: mailbox.email ?? "", channel_type: channelTypeFor(mailbox.provider) }]
140
+ : [],
141
+ };
142
+ const res = await g8Post<{ id: string }>("/sequences", body, { idempotencyKey: keyFor("create", body) });
143
+ setSaved(`Draft "${body.name}" created. Launch it from the sequence list.`);
144
+ if (res.data?.id) onSaved?.(res.data.id);
145
+ } else if (p.kind === "update" && p.step.id) {
146
+ const api = toApiStep(p.step, 1);
147
+ await g8Patch(`/sequences/${sequenceId}/steps/${p.step.id}`, {
148
+ step_type: api.step_type,
149
+ input_type: api.input_type,
150
+ time_interval: api.time_interval,
151
+ step_data: api.step_data,
152
+ });
153
+ setSteps((all) => all.map((s) => (s.key === p.step.key ? { ...s, dirty: false } : s)));
154
+ } else if (p.kind === "delete" && p.step.id) {
155
+ await g8Delete(`/sequences/${sequenceId}/steps/${p.step.id}`);
156
+ setSteps((all) => all.filter((s) => s.key !== p.step.key));
157
+ }
158
+ });
159
+ const append = useG8Mutation(async () => {
160
+ const fresh = steps.filter((s) => !s.id);
161
+ const offset = steps.length - fresh.length;
162
+ const body = { steps: fresh.map((s, i) => toApiStep(s, offset + i + 1)) };
163
+ await g8Post(`/sequences/${sequenceId}/steps`, body, { idempotencyKey: keyFor("append", body) });
164
+ // The steps are saved: drop the unsaved copies at once so a second click cannot append them again,
165
+ // then reload the real list (with ids) instead of trusting the response shape.
166
+ setSteps((all) => all.filter((s) => s.id));
167
+ const fresh2 = await existing.refetch();
168
+ if (fresh2) setSteps(fresh2.steps);
169
+ onSaved?.(sequenceId!);
170
+ });
171
+
172
+ const add = (kind: StepKind) => setSteps((s) => [...s, { ...newStep(kind), dirty: true }]);
173
+ const move = (i: number, d: -1 | 1) =>
174
+ setSteps((s) => {
175
+ const next = [...s];
176
+ const j = i + d;
177
+ if (j < 0 || j >= s.length || s[i].id || s[j].id) return s;
178
+ [next[i], next[j]] = [next[j], next[i]];
179
+ return next;
180
+ });
181
+ const invalid = steps.some((s) => stepIssues(s).length > 0);
182
+ const canCreate = name.trim() && steps.length > 0 && !invalid && mailboxId;
183
+ const fresh = steps.filter((s) => !s.id);
184
+
185
+ if (editing && existing.error) return <ApiError error={existing.error} onRetry={() => void existing.refetch()} className={className} />;
186
+ if (editing && existing.loading && !existing.data) return <Skeleton className={`h-48 w-full ${className ?? ""}`} />;
187
+ const copy = pending ? describe(pending, live) : null;
188
+
189
+ return (
190
+ <div className={`space-y-4 ${className ?? ""}`}>
191
+ {!editing ? (
192
+ <div className="grid gap-2 sm:grid-cols-3">
193
+ <Input aria-label="Sequence name" placeholder="Q4 outbound" value={name} onChange={(e) => setName(e.target.value)} />
194
+ <select
195
+ aria-label="Send from"
196
+ className="h-9 rounded-md border border-gray-300 px-2 text-sm"
197
+ value={mailboxId}
198
+ onChange={(e) => setMailboxId(e.target.value)}
199
+ >
200
+ <option value="">Send from...</option>
201
+ {listOf<MailboxOption>(mailboxes.data).map((m) => (
202
+ <option key={m.id} value={m.id}>
203
+ {m.email}
204
+ </option>
205
+ ))}
206
+ </select>
207
+ <select
208
+ aria-label="Audience list"
209
+ className="h-9 rounded-md border border-gray-300 px-2 text-sm"
210
+ value={listId}
211
+ onChange={(e) => setListId(e.target.value)}
212
+ >
213
+ <option value="">Audience list (optional)</option>
214
+ {listOf<ListOption>(lists.data).map((l) => (
215
+ <option key={l.id} value={l.id}>
216
+ {l.title}
217
+ {l.contact_count != null ? ` (${l.contact_count})` : ""}
218
+ </option>
219
+ ))}
220
+ </select>
221
+ </div>
222
+ ) : null}
223
+ {mailboxes.error || lists.error ? (
224
+ <ApiError
225
+ error={mailboxes.error ?? lists.error}
226
+ onRetry={() => {
227
+ void mailboxes.refetch();
228
+ void lists.refetch();
229
+ }}
230
+ />
231
+ ) : null}
232
+
233
+ <div className="space-y-3">
234
+ {steps.map((s, i) => (
235
+ <div key={s.key} className="space-y-1">
236
+ <StepEditor
237
+ step={s}
238
+ index={i + 1}
239
+ onChange={(next) => setSteps((all) => all.map((x) => (x.key === s.key ? next : x)))}
240
+ onRemove={() => (s.id ? setPending({ kind: "delete", step: s }) : setSteps((all) => all.filter((x) => x.key !== s.key)))}
241
+ />
242
+ <div className="flex gap-2 text-xs">
243
+ {!s.id ? (
244
+ <>
245
+ <button type="button" className="underline" onClick={() => move(i, -1)}>
246
+ Move up
247
+ </button>
248
+ <button type="button" className="underline" onClick={() => move(i, 1)}>
249
+ Move down
250
+ </button>
251
+ </>
252
+ ) : null}
253
+ {s.id && s.dirty ? (
254
+ <Button size="sm" disabled={stepIssues(s).length > 0} onClick={() => setPending({ kind: "update", step: s })}>
255
+ Save step {i + 1}
256
+ </Button>
257
+ ) : null}
258
+ </div>
259
+ </div>
260
+ ))}
261
+ {steps.length === 0 ? <p className="text-sm text-gray-500">No steps yet. Add the first touch below.</p> : null}
262
+ </div>
263
+
264
+ <div className="flex flex-wrap items-center gap-2">
265
+ {KINDS.map((k) => (
266
+ <Button key={k} size="sm" variant="outline" onClick={() => add(k)}>
267
+ Add {KIND_LABEL[k].toLowerCase()}
268
+ </Button>
269
+ ))}
270
+ </div>
271
+
272
+ {editing ? (
273
+ <Button
274
+ disabled={fresh.length === 0 || fresh.some((s) => stepIssues(s).length > 0) || append.loading}
275
+ onClick={() => void append.mutate(undefined)}
276
+ >
277
+ Add {fresh.length} new step{fresh.length === 1 ? "" : "s"}
278
+ </Button>
279
+ ) : (
280
+ <Button disabled={!canCreate} onClick={() => setPending({ kind: "create" })}>
281
+ Create draft sequence
282
+ </Button>
283
+ )}
284
+ {run.error ? <ApiError error={run.error} onRetry={run.reset} /> : null}
285
+ {append.error ? <ApiError error={append.error} onRetry={append.reset} /> : null}
286
+ {saved ? (
287
+ <p role="status" className="text-sm text-green-700">
288
+ {saved}
289
+ </p>
290
+ ) : null}
291
+
292
+ {pending && copy ? (
293
+ <ConfirmSpendDialog
294
+ open
295
+ tier={copy.tier}
296
+ title={copy.title}
297
+ description={copy.description}
298
+ confirmLabel={copy.label}
299
+ confirming={run.loading}
300
+ onCancel={() => setPending(null)}
301
+ onConfirm={() => void run.mutate(pending).then(() => setPending(null))}
302
+ />
303
+ ) : null}
304
+ </div>
305
+ );
306
+ }