@graph8/sdk 0.362.0 → 0.373.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 (615) hide show
  1. package/README.md +33 -10
  2. package/blocks/_foundation/README.md +78 -1
  3. package/blocks/_foundation/block.json +123 -6
  4. package/blocks/_foundation/components/api-error.tsx +9 -7
  5. package/blocks/_foundation/components/brand-provider.tsx +23 -0
  6. package/blocks/_foundation/components/confirm-spend-dialog.tsx +15 -8
  7. package/blocks/_foundation/components/confirmed-action.tsx +90 -0
  8. package/blocks/_foundation/components/connect-graph8.tsx +280 -0
  9. package/blocks/_foundation/components/empty-state.tsx +5 -2
  10. package/blocks/_foundation/components/list-select.tsx +112 -0
  11. package/blocks/_foundation/components/paging-notices.tsx +55 -0
  12. package/blocks/_foundation/components/screen/detail-panel.tsx +2 -1
  13. package/blocks/_foundation/components/screen/filter-controls.tsx +92 -0
  14. package/blocks/_foundation/components/screen/index.ts +2 -0
  15. package/blocks/_foundation/components/screen/record-link.tsx +76 -0
  16. package/blocks/_foundation/components/screen/split-view.tsx +1 -1
  17. package/blocks/_foundation/components/screen/time-series-chart-recharts.tsx +65 -0
  18. package/blocks/_foundation/components/screen/time-series-chart.tsx +15 -97
  19. package/blocks/_foundation/components/screen/time-series-format.ts +57 -0
  20. package/blocks/_foundation/hooks/use-all-pages.ts +54 -0
  21. package/blocks/_foundation/hooks/use-brand.ts +52 -0
  22. package/blocks/_foundation/hooks/use-g8-mutation.ts +23 -0
  23. package/blocks/_foundation/hooks/use-g8-query.ts +127 -1
  24. package/blocks/_foundation/hooks/use-load-more-list.ts +61 -0
  25. package/blocks/_foundation/hooks/use-paginated.ts +120 -0
  26. package/blocks/_foundation/hooks/use-row-activation.ts +57 -0
  27. package/blocks/_foundation/hooks/use-url-filters.ts +93 -0
  28. package/blocks/_foundation/lib/g8-cache.ts +454 -0
  29. package/blocks/_foundation/lib/g8-client.ts +1 -1
  30. package/blocks/_foundation/lib/paging.ts +57 -0
  31. package/blocks/_foundation/lib/plural.ts +23 -0
  32. package/blocks/_foundation/lib/use-debounced-value.ts +13 -0
  33. package/blocks/_foundation/server/connect-route.ts +15 -0
  34. package/blocks/_foundation/server/connect.ts +127 -0
  35. package/blocks/_foundation/server/local-key.ts +70 -0
  36. package/blocks/_foundation/server/proxy.ts +103 -3
  37. package/blocks/_foundation/server/route.ts +16 -4
  38. package/blocks/agents/README.md +93 -2
  39. package/blocks/agents/block.json +343 -2
  40. package/blocks/agents/components/agent-activity.tsx +112 -0
  41. package/blocks/agents/components/agent-approvals.tsx +135 -0
  42. package/blocks/agents/components/agent-builder.tsx +29 -10
  43. package/blocks/agents/components/agent-conversations.tsx +143 -0
  44. package/blocks/agents/components/agent-knowledge-panel.tsx +4 -3
  45. package/blocks/agents/components/agent-operators.tsx +164 -0
  46. package/blocks/agents/components/agent-run-detail.tsx +119 -0
  47. package/blocks/agents/components/agent-runs.tsx +19 -10
  48. package/blocks/agents/components/agent-skills-panel.tsx +7 -7
  49. package/blocks/agents/components/agent-test-history.tsx +121 -0
  50. package/blocks/agents/components/agents-screen.tsx +226 -0
  51. package/blocks/agents/components/assistant-sessions.tsx +150 -0
  52. package/blocks/agents/components/duplicate-agent-button.tsx +42 -0
  53. package/blocks/agents/components/from-template-button.tsx +65 -0
  54. package/blocks/agents/components/knowledge-browser.tsx +107 -0
  55. package/blocks/agents/components/knowledge-detail.tsx +156 -0
  56. package/blocks/agents/components/knowledge-form.tsx +69 -0
  57. package/blocks/agents/components/name-dialog.tsx +32 -0
  58. package/blocks/agents/components/receptionist-button.tsx +46 -0
  59. package/blocks/agents/components/save-as-template-button.tsx +32 -0
  60. package/blocks/agents/components/skill-editor.tsx +147 -0
  61. package/blocks/agents/components/skill-library.tsx +111 -0
  62. package/blocks/agents/lib/agent-types.ts +7 -1
  63. package/blocks/agents/lib/facts.tsx +13 -0
  64. package/blocks/agents/lib/knowledge-utils.ts +49 -0
  65. package/blocks/agents/lib/load-more.tsx +141 -0
  66. package/blocks/agents/lib/skill-utils.ts +91 -0
  67. package/blocks/agents/recipes/create-agent-and-attach-skill.ts +1 -1
  68. package/blocks/analytics/README.md +13 -0
  69. package/blocks/analytics/block.json +45 -1
  70. package/blocks/analytics/components/analytics-screen.tsx +222 -0
  71. package/blocks/analytics/components/kpi-tiles.tsx +1 -1
  72. package/blocks/analytics/components/leaderboard.tsx +14 -7
  73. package/blocks/analytics/components/report-builder.tsx +38 -11
  74. package/blocks/analytics/components/report-detail.tsx +84 -0
  75. package/blocks/analytics/components/trend-chart.tsx +62 -0
  76. package/blocks/appointments/README.md +6 -0
  77. package/blocks/appointments/block.json +33 -2
  78. package/blocks/appointments/components/availability-editor.tsx +17 -5
  79. package/blocks/appointments/components/booking-page.tsx +2 -2
  80. package/blocks/appointments/components/event-type-editor.tsx +2 -1
  81. package/blocks/appointments/components/event-type-related.tsx +4 -2
  82. package/blocks/appointments/components/meeting-contact-links.tsx +49 -0
  83. package/blocks/appointments/components/meeting-detail.tsx +2 -2
  84. package/blocks/appointments/components/meeting-list.tsx +38 -18
  85. package/blocks/appointments/components/meetings-screen.tsx +7 -4
  86. package/blocks/appointments/components/record-meetings-tab.tsx +92 -0
  87. package/blocks/appointments/components/scheduling-screen.tsx +2 -1
  88. package/blocks/block.schema.json +18 -0
  89. package/blocks/block.types.ts +17 -0
  90. package/blocks/campaigns/README.md +41 -11
  91. package/blocks/campaigns/block.json +105 -42
  92. package/blocks/campaigns/components/campaign-assets.tsx +17 -6
  93. package/blocks/campaigns/components/campaign-launch-step.tsx +114 -26
  94. package/blocks/campaigns/components/campaign-list.tsx +7 -3
  95. package/blocks/campaigns/components/campaign-metrics.tsx +1 -1
  96. package/blocks/campaigns/components/campaign-wizard.tsx +52 -14
  97. package/blocks/campaigns/components/campaigns-screen.tsx +267 -0
  98. package/blocks/campaigns/components/icp-detail.tsx +163 -0
  99. package/blocks/campaigns/components/icp-editor.tsx +22 -17
  100. package/blocks/campaigns/components/persona-detail.tsx +181 -0
  101. package/blocks/campaigns/components/persona-editor.tsx +19 -11
  102. package/blocks/campaigns/lib/campaigns-utils.ts +138 -5
  103. package/blocks/campaigns/recipes/attach-audience-and-launch.ts +16 -3
  104. package/blocks/campaigns/recipes/generate-ideas-and-create-campaign.ts +8 -4
  105. package/blocks/coach/README.md +10 -0
  106. package/blocks/coach/block.json +16 -1
  107. package/blocks/coach/components/call-scorecard.tsx +7 -7
  108. package/blocks/coach/components/coach-screen.tsx +222 -0
  109. package/blocks/company-intelligence/README.md +132 -0
  110. package/blocks/company-intelligence/block.json +318 -0
  111. package/blocks/company-intelligence/components/company-intelligence-screen.tsx +114 -0
  112. package/blocks/company-intelligence/components/company-intelligence-tab.tsx +88 -0
  113. package/blocks/company-intelligence/components/company-profile.tsx +68 -0
  114. package/blocks/company-intelligence/components/intelligence-sources.tsx +63 -0
  115. package/blocks/company-intelligence/components/report-detail.tsx +189 -0
  116. package/blocks/company-intelligence/components/research-reports.tsx +136 -0
  117. package/blocks/company-intelligence/components/research-run.tsx +161 -0
  118. package/blocks/company-intelligence/components/source-detail.tsx +117 -0
  119. package/blocks/company-intelligence/components/source-view.tsx +274 -0
  120. package/blocks/company-intelligence/components/wiki-document-detail.tsx +116 -0
  121. package/blocks/company-intelligence/components/wiki-documents.tsx +66 -0
  122. package/blocks/company-intelligence/hooks/use-intelligence.ts +173 -0
  123. package/blocks/company-intelligence/lib/intelligence-types.ts +230 -0
  124. package/blocks/company-intelligence/lib/safe-markdown.tsx +150 -0
  125. package/blocks/company-intelligence/recipes/read-competitors-and-news.ts +33 -0
  126. package/blocks/company-intelligence/recipes/research-your-website.ts +43 -0
  127. package/blocks/company-intelligence/recipes/write-the-research-reports.ts +54 -0
  128. package/blocks/copilot/README.md +13 -0
  129. package/blocks/copilot/block.json +14 -1
  130. package/blocks/copilot/components/copilot-memory.tsx +6 -2
  131. package/blocks/copilot/components/copilot-panel.tsx +14 -5
  132. package/blocks/copilot/components/copilot-screen.tsx +148 -0
  133. package/blocks/copilot/components/copilot-team-chat.tsx +13 -6
  134. package/blocks/crm/README.md +34 -0
  135. package/blocks/crm/block.json +107 -2
  136. package/blocks/crm/components/companies-screen.tsx +359 -0
  137. package/blocks/crm/components/company-detail.tsx +10 -8
  138. package/blocks/crm/components/company-table.tsx +11 -4
  139. package/blocks/crm/components/contact-detail.tsx +10 -7
  140. package/blocks/crm/components/contact-lookup-link.tsx +44 -0
  141. package/blocks/crm/components/contact-table.tsx +10 -3
  142. package/blocks/crm/components/contacts-screen.tsx +377 -0
  143. package/blocks/crm/components/dedupe-review.tsx +13 -6
  144. package/blocks/crm/components/import-wizard.tsx +7 -9
  145. package/blocks/crm/components/list-select.tsx +20 -0
  146. package/blocks/crm/components/notes-panel.tsx +4 -4
  147. package/blocks/crm/components/record-engagement-tab.tsx +103 -0
  148. package/blocks/crm/components/record-form.tsx +6 -4
  149. package/blocks/crm/components/task-list.tsx +4 -4
  150. package/blocks/crm/components/tasks-screen.tsx +399 -0
  151. package/blocks/crm/hooks/use-all-lists.ts +18 -0
  152. package/blocks/crm/hooks/use-custom-fields.ts +6 -1
  153. package/blocks/crm/hooks/use-page-query.ts +9 -1
  154. package/blocks/crm/lib/crm-types.ts +26 -2
  155. package/blocks/crm/lib/read-lists.ts +16 -0
  156. package/blocks/crm/lib/screen-parts.tsx +105 -0
  157. package/blocks/deals/README.md +24 -6
  158. package/blocks/deals/block.json +160 -5
  159. package/blocks/deals/components/close-deal-dialog.tsx +169 -0
  160. package/blocks/deals/components/deal-contacts-editor.tsx +134 -0
  161. package/blocks/deals/components/deal-detail.tsx +25 -13
  162. package/blocks/deals/components/deal-filters.tsx +65 -0
  163. package/blocks/deals/components/deal-line-items.tsx +120 -0
  164. package/blocks/deals/components/deal-notes-tab.tsx +97 -0
  165. package/blocks/deals/components/deal-related.tsx +31 -10
  166. package/blocks/deals/components/deal-tasks-tab.tsx +88 -0
  167. package/blocks/deals/components/deals-screen.tsx +66 -26
  168. package/blocks/deals/components/forecast-view.tsx +4 -4
  169. package/blocks/deals/components/pipeline-board.tsx +8 -6
  170. package/blocks/deals/components/pipeline-editor.tsx +132 -0
  171. package/blocks/deals/components/record-deals-tab.tsx +62 -0
  172. package/blocks/deals/components/stage-editor.tsx +5 -5
  173. package/blocks/deals/hooks/use-pipeline-deals.ts +21 -4
  174. package/blocks/deals/hooks/use-pipelines.ts +1 -1
  175. package/blocks/deals/lib/deal-extras.ts +156 -0
  176. package/blocks/dialer/README.md +45 -4
  177. package/blocks/dialer/block.json +84 -3
  178. package/blocks/dialer/components/call-contact-button.tsx +269 -0
  179. package/blocks/dialer/components/call-log-screen.tsx +208 -0
  180. package/blocks/dialer/components/call-log.tsx +23 -7
  181. package/blocks/dialer/components/call-queue.tsx +4 -4
  182. package/blocks/dialer/components/dialer-screen.tsx +361 -0
  183. package/blocks/dialer/components/dialer-session-detail.tsx +153 -0
  184. package/blocks/dialer/components/disposition-panel.tsx +1 -1
  185. package/blocks/dialer/components/live-calls-table.tsx +7 -3
  186. package/blocks/dialer/components/record-calls-tab.tsx +101 -0
  187. package/blocks/dialer/components/session-start-form.tsx +61 -12
  188. package/blocks/dialer/components/softphone.tsx +4 -2
  189. package/blocks/enrichment/README.md +29 -7
  190. package/blocks/enrichment/block.json +49 -9
  191. package/blocks/enrichment/components/bulk-enrich-panel.tsx +83 -29
  192. package/blocks/enrichment/components/email-verify-badge.tsx +31 -11
  193. package/blocks/enrichment/components/enrich-button.tsx +32 -9
  194. package/blocks/enrichment/components/enrichment-job-detail.tsx +78 -0
  195. package/blocks/enrichment/components/enrichment-screen.tsx +341 -0
  196. package/blocks/enrichment/components/waterfall-config.tsx +66 -17
  197. package/blocks/enrichment/lib/enrichment-types.ts +39 -0
  198. package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +15 -4
  199. package/blocks/enrichment/recipes/waterfall-pipeline.ts +3 -2
  200. package/blocks/forms/README.md +8 -0
  201. package/blocks/forms/block.json +139 -18
  202. package/blocks/forms/components/form-activity.tsx +71 -0
  203. package/blocks/forms/components/form-backfill.tsx +108 -0
  204. package/blocks/forms/components/form-builder.tsx +4 -2
  205. package/blocks/forms/components/form-embed.tsx +4 -2
  206. package/blocks/forms/components/form-groups.tsx +151 -0
  207. package/blocks/forms/components/form-mapping-editor.tsx +6 -9
  208. package/blocks/forms/components/form-route-history.tsx +72 -0
  209. package/blocks/forms/components/form-saved-views.tsx +81 -0
  210. package/blocks/forms/components/forms-screen.tsx +56 -19
  211. package/blocks/forms/components/more-rows.tsx +30 -0
  212. package/blocks/forms/components/submissions-export.tsx +67 -0
  213. package/blocks/forms/components/submissions-table.tsx +17 -6
  214. package/blocks/forms/lib/forms-extra.ts +67 -0
  215. package/blocks/inbox/README.md +32 -3
  216. package/blocks/inbox/block.json +29 -2
  217. package/blocks/inbox/components/inbox-list.tsx +7 -3
  218. package/blocks/inbox/components/inbox-screen.tsx +294 -27
  219. package/blocks/inbox/components/record-conversations-tab.tsx +154 -0
  220. package/blocks/inbox/components/reply-composer.tsx +65 -9
  221. package/blocks/inbox/components/tag-and-assign.tsx +4 -2
  222. package/blocks/inbox/components/thread-badge.tsx +2 -2
  223. package/blocks/inbox/components/thread-view.tsx +1 -1
  224. package/blocks/inbox/hooks/use-inbox-thread.ts +1 -1
  225. package/blocks/inbox/hooks/use-inbox-threads.ts +1 -0
  226. package/blocks/inbox/lib/inbox-types.ts +47 -1
  227. package/blocks/inbox/recipes/01-triage-and-reply.ts +12 -2
  228. package/blocks/integrations/README.md +63 -1
  229. package/blocks/integrations/block.json +234 -15
  230. package/blocks/integrations/components/ad-accounts.tsx +82 -0
  231. package/blocks/integrations/components/connection-health.tsx +59 -0
  232. package/blocks/integrations/components/crm-detail.tsx +119 -0
  233. package/blocks/integrations/components/crm-records.tsx +103 -0
  234. package/blocks/integrations/components/field-mappings.tsx +61 -0
  235. package/blocks/integrations/components/integration-list.tsx +13 -8
  236. package/blocks/integrations/components/integrations-screen.tsx +152 -0
  237. package/blocks/integrations/components/migration-panel.tsx +133 -0
  238. package/blocks/integrations/components/sugarcrm-connect.tsx +95 -0
  239. package/blocks/integrations/components/sync-status.tsx +11 -9
  240. package/blocks/integrations/components/sync-triggers.tsx +134 -0
  241. package/blocks/integrations/components/webhook-detail.tsx +179 -0
  242. package/blocks/integrations/components/webhook-manager.tsx +43 -25
  243. package/blocks/integrations/components/workspace-tools.tsx +127 -0
  244. package/blocks/integrations/lib/crm-utils.ts +39 -0
  245. package/blocks/integrations/lib/facts.tsx +13 -0
  246. package/blocks/integrations/lib/integrations-utils.ts +11 -1
  247. package/blocks/integrations/lib/load-more.tsx +141 -0
  248. package/blocks/intent/README.md +14 -0
  249. package/blocks/intent/block.json +27 -1
  250. package/blocks/intent/components/account-intent-score.tsx +5 -5
  251. package/blocks/intent/components/intent-feed.tsx +13 -10
  252. package/blocks/intent/components/intent-keyword-manager.tsx +16 -12
  253. package/blocks/intent/components/intent-screen.tsx +265 -0
  254. package/blocks/intent/components/keyword-detail.tsx +124 -0
  255. package/blocks/intent/lib/intent-utils.ts +4 -4
  256. package/blocks/lifecycle/block.json +13 -8
  257. package/blocks/lifecycle/components/account-related.tsx +8 -1
  258. package/blocks/lifecycle/components/churn-list.tsx +5 -2
  259. package/blocks/lifecycle/components/customer-table.tsx +1 -1
  260. package/blocks/lifecycle/components/lifecycle-board.tsx +3 -1
  261. package/blocks/lifecycle/components/lifecycle-screen.tsx +3 -1
  262. package/blocks/lifecycle/lib/lifecycle.ts +1 -1
  263. package/blocks/lists/README.md +20 -1
  264. package/blocks/lists/block.json +69 -3
  265. package/blocks/lists/components/add-company-people-to-list-button.tsx +73 -0
  266. package/blocks/lists/components/add-to-list-button.tsx +20 -9
  267. package/blocks/lists/components/list-picker.tsx +17 -3
  268. package/blocks/lists/components/list-table.tsx +12 -6
  269. package/blocks/lists/components/lists-screen.tsx +346 -0
  270. package/blocks/lists/components/record-lists-tab.tsx +99 -0
  271. package/blocks/lists/components/suppression-manager.tsx +33 -8
  272. package/blocks/lists/components/suppressions-screen.tsx +264 -0
  273. package/blocks/mailboxes/README.md +17 -0
  274. package/blocks/mailboxes/block.json +69 -17
  275. package/blocks/mailboxes/components/connect-mailbox.tsx +32 -21
  276. package/blocks/mailboxes/components/domain-detail.tsx +148 -0
  277. package/blocks/mailboxes/components/domain-purchase.tsx +20 -14
  278. package/blocks/mailboxes/components/mailbox-detail.tsx +157 -0
  279. package/blocks/mailboxes/components/mailbox-list.tsx +8 -3
  280. package/blocks/mailboxes/components/mailboxes-screen.tsx +228 -0
  281. package/blocks/mailboxes/components/warmup-health.tsx +24 -22
  282. package/blocks/mailboxes/lib/mailboxes-utils.ts +26 -5
  283. package/blocks/mailboxes/lib/use-provider-domains.ts +23 -0
  284. package/blocks/newsletter/README.md +12 -0
  285. package/blocks/newsletter/block.json +157 -24
  286. package/blocks/newsletter/components/issue-scheduler.tsx +4 -3
  287. package/blocks/newsletter/components/issue-versions.tsx +93 -0
  288. package/blocks/newsletter/components/more-rows.tsx +30 -0
  289. package/blocks/newsletter/components/newsletter-editor.tsx +1 -1
  290. package/blocks/newsletter/components/newsletter-list-syncs.tsx +121 -0
  291. package/blocks/newsletter/components/newsletter-screen.tsx +39 -14
  292. package/blocks/newsletter/components/newsletter-settings.tsx +211 -0
  293. package/blocks/newsletter/components/newsletter-stats.tsx +2 -2
  294. package/blocks/newsletter/components/newsletter-templates.tsx +133 -0
  295. package/blocks/newsletter/components/subscriber-table.tsx +5 -7
  296. package/blocks/newsletter/components/subscriber-timeline.tsx +65 -0
  297. package/blocks/newsletter/lib/newsletter-extra.ts +60 -0
  298. package/blocks/nurture/block.json +28 -14
  299. package/blocks/nurture/components/nurture-builder.tsx +13 -3
  300. package/blocks/nurture/components/nurture-enroll.tsx +12 -10
  301. package/blocks/nurture/components/nurture-form.tsx +27 -10
  302. package/blocks/nurture/components/nurture-screen.tsx +26 -11
  303. package/blocks/nurture/components/nurture-stats.tsx +5 -4
  304. package/blocks/nurture/lib/nurture-types.ts +1 -1
  305. package/blocks/objects/README.md +18 -1
  306. package/blocks/objects/block.json +42 -21
  307. package/blocks/objects/components/attribute-field-input.tsx +14 -1
  308. package/blocks/objects/components/object-not-found.tsx +3 -1
  309. package/blocks/objects/components/object-record-detail.tsx +10 -2
  310. package/blocks/objects/components/object-schema-editor.tsx +4 -2
  311. package/blocks/objects/components/object-table.tsx +13 -3
  312. package/blocks/objects/components/objects-screen.tsx +147 -0
  313. package/blocks/objects/components/record-roles-panel.tsx +15 -5
  314. package/blocks/objects/lib/object-types.ts +1 -1
  315. package/blocks/pages/README.md +14 -0
  316. package/blocks/pages/block.json +140 -23
  317. package/blocks/pages/components/brand-kit-editor.tsx +5 -2
  318. package/blocks/pages/components/content-library.tsx +9 -4
  319. package/blocks/pages/components/more-rows.tsx +30 -0
  320. package/blocks/pages/components/page-ai-tools.tsx +196 -0
  321. package/blocks/pages/components/page-analytics.tsx +176 -0
  322. package/blocks/pages/components/page-clone.tsx +99 -0
  323. package/blocks/pages/components/page-domains.tsx +167 -0
  324. package/blocks/pages/components/page-editor.tsx +1 -1
  325. package/blocks/pages/components/page-generation-status.tsx +48 -0
  326. package/blocks/pages/components/page-list.tsx +2 -1
  327. package/blocks/pages/components/page-tracking.tsx +137 -0
  328. package/blocks/pages/components/pages-screen.tsx +57 -16
  329. package/blocks/pages/components/seo-report.tsx +6 -1
  330. package/blocks/pages/lib/studio.ts +7 -0
  331. package/blocks/phone-numbers/README.md +74 -0
  332. package/blocks/phone-numbers/block.json +262 -0
  333. package/blocks/phone-numbers/components/a2p-registration.tsx +230 -0
  334. package/blocks/phone-numbers/components/assign-number.tsx +104 -0
  335. package/blocks/phone-numbers/components/number-search.tsx +239 -0
  336. package/blocks/phone-numbers/components/number-settings.tsx +134 -0
  337. package/blocks/phone-numbers/components/number-usage.tsx +97 -0
  338. package/blocks/phone-numbers/components/phone-number-detail.tsx +84 -0
  339. package/blocks/phone-numbers/components/phone-number-list.tsx +110 -0
  340. package/blocks/phone-numbers/components/phone-numbers-screen.tsx +201 -0
  341. package/blocks/phone-numbers/components/register-owned-number.tsx +98 -0
  342. package/blocks/phone-numbers/components/release-number.tsx +126 -0
  343. package/blocks/phone-numbers/hooks/use-phone-numbers.ts +58 -0
  344. package/blocks/phone-numbers/lib/phone-numbers-utils.ts +202 -0
  345. package/blocks/phone-numbers/recipes/01-search-and-buy-number.ts +49 -0
  346. package/blocks/phone-numbers/recipes/02-assign-number-to-rep.ts +25 -0
  347. package/blocks/phone-numbers/recipes/03-audit-numbers.ts +36 -0
  348. package/blocks/phone-numbers/recipes/04-register-text-messaging.ts +39 -0
  349. package/blocks/pickers/README.md +23 -0
  350. package/blocks/pickers/block.json +103 -0
  351. package/blocks/pickers/components/company-picker.tsx +90 -0
  352. package/blocks/pickers/components/contact-picker.tsx +131 -0
  353. package/blocks/pickers/components/member-picker.tsx +99 -0
  354. package/blocks/pickers/components/search-picker.tsx +98 -0
  355. package/blocks/pickers/hooks/use-current-member.ts +104 -0
  356. package/blocks/pickers/hooks/use-picker-pages.ts +73 -0
  357. package/blocks/pickers/lib/use-debounced.ts +13 -0
  358. package/blocks/quotes/README.md +13 -0
  359. package/blocks/quotes/block.json +65 -13
  360. package/blocks/quotes/components/create-quote-from-deal.tsx +48 -0
  361. package/blocks/quotes/components/product-picker.tsx +3 -1
  362. package/blocks/quotes/components/quote-builder.tsx +1 -1
  363. package/blocks/quotes/components/quote-lifecycle-actions.tsx +146 -0
  364. package/blocks/quotes/components/quote-list.tsx +5 -3
  365. package/blocks/quotes/components/quote-related.tsx +8 -4
  366. package/blocks/quotes/components/quote-viewer.tsx +7 -2
  367. package/blocks/quotes/components/record-quotes-tab.tsx +90 -0
  368. package/blocks/radar/README.md +10 -0
  369. package/blocks/radar/block.json +41 -1
  370. package/blocks/radar/components/competitor-battle-card.tsx +1 -1
  371. package/blocks/radar/components/competitor-detail.tsx +117 -0
  372. package/blocks/radar/components/competitor-tracker.tsx +3 -1
  373. package/blocks/radar/components/radar-overview.tsx +4 -4
  374. package/blocks/radar/components/radar-report.tsx +4 -2
  375. package/blocks/radar/components/radar-screen.tsx +214 -0
  376. package/blocks/radar/components/report-detail.tsx +104 -0
  377. package/blocks/search/README.md +10 -0
  378. package/blocks/search/block.json +16 -1
  379. package/blocks/search/components/filter-builder.tsx +3 -3
  380. package/blocks/search/components/prospect-results.tsx +5 -3
  381. package/blocks/search/components/prospect-search-screen.tsx +235 -0
  382. package/blocks/search/components/saved-searches.tsx +4 -1
  383. package/blocks/search/hooks/use-filter-options.ts +20 -6
  384. package/blocks/search/lib/search-types.ts +2 -0
  385. package/blocks/sequencer/README.md +55 -9
  386. package/blocks/sequencer/block.json +126 -19
  387. package/blocks/sequencer/components/add-to-sequence-button.tsx +82 -0
  388. package/blocks/sequencer/components/enroll-dialog.tsx +11 -5
  389. package/blocks/sequencer/components/record-sequences-tab.tsx +126 -0
  390. package/blocks/sequencer/components/sequence-builder.tsx +224 -60
  391. package/blocks/sequencer/components/sequence-detail.tsx +261 -0
  392. package/blocks/sequencer/components/sequence-enrollments.tsx +116 -0
  393. package/blocks/sequencer/components/sequence-list.tsx +34 -9
  394. package/blocks/sequencer/components/sequence-stats.tsx +11 -10
  395. package/blocks/sequencer/components/sequence-steps-panel.tsx +103 -0
  396. package/blocks/sequencer/components/sequences-screen.tsx +162 -0
  397. package/blocks/sequencer/components/step-editor.tsx +14 -12
  398. package/blocks/sequencer/lib/sequencer-utils.ts +133 -10
  399. package/blocks/sequencer/recipes/create-multichannel-sequence.ts +33 -5
  400. package/blocks/sequencer/recipes/launch-and-enroll.ts +19 -2
  401. package/blocks/social-ads/README.md +19 -0
  402. package/blocks/social-ads/block.json +233 -1
  403. package/blocks/social-ads/components/ad-campaign-list.tsx +9 -5
  404. package/blocks/social-ads/components/ads-screen.tsx +152 -0
  405. package/blocks/social-ads/components/audience-sync-detail.tsx +194 -0
  406. package/blocks/social-ads/components/audience-sync.tsx +17 -9
  407. package/blocks/social-ads/components/campaign-ads.tsx +166 -0
  408. package/blocks/social-ads/components/campaign-detail.tsx +138 -0
  409. package/blocks/social-ads/components/campaign-editor.tsx +134 -0
  410. package/blocks/social-ads/components/creative-concepts.tsx +74 -0
  411. package/blocks/social-ads/components/creative-detail.tsx +155 -0
  412. package/blocks/social-ads/components/creative-gallery.tsx +35 -7
  413. package/blocks/social-ads/components/creative-revisions.tsx +57 -0
  414. package/blocks/social-ads/components/creative-tools.tsx +176 -0
  415. package/blocks/social-ads/components/post-composer.tsx +7 -3
  416. package/blocks/social-ads/components/post-detail.tsx +163 -0
  417. package/blocks/social-ads/components/social-ideas.tsx +64 -0
  418. package/blocks/social-ads/components/social-performance.tsx +80 -0
  419. package/blocks/social-ads/components/social-post-list.tsx +11 -3
  420. package/blocks/social-ads/components/social-screen.tsx +187 -0
  421. package/blocks/social-ads/lib/ads-extra.ts +67 -0
  422. package/blocks/surveys/README.md +91 -0
  423. package/blocks/surveys/block.json +299 -0
  424. package/blocks/surveys/components/account-nps.tsx +69 -0
  425. package/blocks/surveys/components/contact-survey-history.tsx +52 -0
  426. package/blocks/surveys/components/org-summary.tsx +82 -0
  427. package/blocks/surveys/components/sending-domains.tsx +113 -0
  428. package/blocks/surveys/components/survey-form.tsx +126 -0
  429. package/blocks/surveys/components/survey-responses.tsx +145 -0
  430. package/blocks/surveys/components/survey-results.tsx +72 -0
  431. package/blocks/surveys/components/survey-send-history.tsx +58 -0
  432. package/blocks/surveys/components/survey-send.tsx +162 -0
  433. package/blocks/surveys/components/survey-triggers.tsx +166 -0
  434. package/blocks/surveys/components/surveys-screen.tsx +234 -0
  435. package/blocks/surveys/lib/surveys-types.ts +228 -0
  436. package/blocks/surveys/recipes/create-an-nps-survey.ts +22 -0
  437. package/blocks/surveys/recipes/send-a-survey-and-follow-it.ts +27 -0
  438. package/blocks/surveys/recipes/survey-score-report.ts +27 -0
  439. package/blocks/visitors/README.md +10 -0
  440. package/blocks/visitors/block.json +25 -1
  441. package/blocks/visitors/components/snippet-install.tsx +21 -10
  442. package/blocks/visitors/components/traffic-chart.tsx +4 -4
  443. package/blocks/visitors/components/visitor-company-card.tsx +26 -16
  444. package/blocks/visitors/components/visitor-detail.tsx +105 -0
  445. package/blocks/visitors/components/visitor-feed.tsx +16 -7
  446. package/blocks/visitors/components/visitors-screen.tsx +166 -0
  447. package/blocks/webchat/README.md +10 -0
  448. package/blocks/webchat/block.json +21 -7
  449. package/blocks/webchat/components/chat-widget-config.tsx +8 -5
  450. package/blocks/webchat/components/conversation-list.tsx +8 -4
  451. package/blocks/webchat/components/webchat-screen.tsx +245 -0
  452. package/blocks/workbench/README.md +11 -0
  453. package/blocks/workbench/block.json +48 -1
  454. package/blocks/workbench/components/column-recipe-editor.tsx +5 -3
  455. package/blocks/workbench/components/run-progress.tsx +1 -1
  456. package/blocks/workbench/components/workbench-grid.tsx +10 -4
  457. package/blocks/workbench/components/workbench-row-detail.tsx +104 -0
  458. package/blocks/workbench/components/workbench-screen.tsx +202 -0
  459. package/blocks/workbench/components/workbench-sources.tsx +194 -0
  460. package/blocks/workbench/components/workbench-table-list.tsx +14 -2
  461. package/blocks/workflows/README.md +13 -0
  462. package/blocks/workflows/block.json +28 -9
  463. package/blocks/workflows/components/run-history.tsx +31 -9
  464. package/blocks/workflows/components/trigger-config.tsx +3 -1
  465. package/blocks/workflows/components/workflow-editor.tsx +4 -2
  466. package/blocks/workflows/components/workflow-list.tsx +13 -6
  467. package/blocks/workflows/components/workflows-screen.tsx +193 -0
  468. package/dist/cli.js +3109 -1977
  469. package/dist/errors.d.mts +34 -0
  470. package/dist/errors.d.ts +34 -0
  471. package/dist/errors.js +46 -0
  472. package/dist/errors.js.map +1 -0
  473. package/dist/errors.mjs +21 -0
  474. package/dist/errors.mjs.map +1 -0
  475. package/dist/index.browser.js +11 -5
  476. package/dist/index.browser.js.map +1 -1
  477. package/dist/index.browser.mjs +11 -5
  478. package/dist/index.browser.mjs.map +1 -1
  479. package/dist/index.d.mts +164 -25
  480. package/dist/index.d.ts +164 -25
  481. package/dist/index.js +11 -5
  482. package/dist/index.js.map +1 -1
  483. package/dist/index.mjs +11 -5
  484. package/dist/index.mjs.map +1 -1
  485. package/dist/react.browser.js +10 -4
  486. package/dist/react.browser.js.map +1 -1
  487. package/dist/react.browser.mjs +10 -4
  488. package/dist/react.browser.mjs.map +1 -1
  489. package/dist/react.d.mts +142 -5
  490. package/dist/react.d.ts +142 -5
  491. package/dist/react.js +10 -4
  492. package/dist/react.js.map +1 -1
  493. package/dist/react.mjs +10 -4
  494. package/dist/react.mjs.map +1 -1
  495. package/dist/types/blocks/add.d.ts +11 -4
  496. package/dist/types/blocks/brand.d.ts +122 -0
  497. package/dist/types/blocks/compose.d.ts +53 -0
  498. package/dist/types/blocks/create.d.ts +27 -20
  499. package/dist/types/blocks/doctor.d.ts +6 -1
  500. package/dist/types/blocks/flows.d.ts +7 -1
  501. package/dist/types/blocks/navfile.d.ts +35 -0
  502. package/dist/types/blocks/orgBrand.d.ts +34 -0
  503. package/dist/types/blocks/paths.d.ts +4 -0
  504. package/dist/types/blocks/prefixes.d.ts +37 -0
  505. package/dist/types/blocks/registry.d.ts +17 -0
  506. package/dist/types/blocks/screens.d.ts +53 -0
  507. package/dist/types/cli.d.ts +5 -0
  508. package/dist/types/errors.d.ts +33 -0
  509. package/dist/types/http.d.ts +2 -18
  510. package/dist/types/version.d.ts +2 -2
  511. package/docs/agents.md +15 -4
  512. package/docs/api/README.md +2 -2
  513. package/docs/api/by-path.md +1 -0
  514. package/docs/api/desk.d.ts +98 -2
  515. package/docs/api/desk.md +123 -1
  516. package/docs/api/inbox.d.ts +10 -2
  517. package/docs/api/inbox.md +10 -2
  518. package/docs/api/seo.d.ts +19 -1
  519. package/docs/api/seo.md +24 -1
  520. package/package.json +16 -2
  521. package/stacks/6sense/pages/companies.tsx.tmpl +21 -0
  522. package/stacks/6sense/pages/contacts.tsx.tmpl +19 -0
  523. package/stacks/6sense/pages/tasks.tsx.tmpl +19 -0
  524. package/stacks/6sense.json +55 -17
  525. package/stacks/apollo/pages/calls.tsx.tmpl +23 -0
  526. package/stacks/apollo/pages/companies.tsx.tmpl +33 -0
  527. package/stacks/apollo/pages/contacts.tsx.tmpl +51 -0
  528. package/stacks/apollo/pages/deals.tsx.tmpl +17 -0
  529. package/stacks/apollo/pages/inbox-contact.tsx.tmpl +13 -0
  530. package/stacks/apollo/pages/lists.tsx.tmpl +12 -0
  531. package/stacks/apollo/pages/meetings.tsx.tmpl +13 -0
  532. package/stacks/apollo/pages/sequences.tsx.tmpl +12 -0
  533. package/stacks/apollo/pages/tasks.tsx.tmpl +20 -0
  534. package/stacks/apollo.json +147 -37
  535. package/stacks/calendly/pages/companies.tsx.tmpl +19 -0
  536. package/stacks/calendly/pages/contacts.tsx.tmpl +21 -0
  537. package/stacks/calendly/pages/meetings.tsx.tmpl +13 -0
  538. package/stacks/calendly/pages/tasks.tsx.tmpl +19 -0
  539. package/stacks/calendly.json +64 -0
  540. package/stacks/clay/pages/companies.tsx.tmpl +31 -0
  541. package/stacks/clay/pages/contacts.tsx.tmpl +31 -0
  542. package/stacks/clay/pages/lists.tsx.tmpl +12 -0
  543. package/stacks/clay/pages/tasks.tsx.tmpl +19 -0
  544. package/stacks/clay.json +73 -27
  545. package/stacks/gong/pages/calls.tsx.tmpl +23 -0
  546. package/stacks/gong/pages/companies.tsx.tmpl +22 -0
  547. package/stacks/gong/pages/contacts.tsx.tmpl +32 -0
  548. package/stacks/gong/pages/lists.tsx.tmpl +12 -0
  549. package/stacks/gong/pages/meetings.tsx.tmpl +13 -0
  550. package/stacks/gong/pages/tasks.tsx.tmpl +19 -0
  551. package/stacks/gong.json +98 -35
  552. package/stacks/hootsuite/pages/companies.tsx.tmpl +22 -0
  553. package/stacks/hootsuite/pages/contacts.tsx.tmpl +28 -0
  554. package/stacks/hootsuite/pages/lists.tsx.tmpl +12 -0
  555. package/stacks/hootsuite/pages/tasks.tsx.tmpl +19 -0
  556. package/stacks/hootsuite.json +104 -0
  557. package/stacks/hubspot/pages/companies.tsx.tmpl +26 -0
  558. package/stacks/hubspot/pages/contacts.tsx.tmpl +45 -0
  559. package/stacks/hubspot/pages/deals.tsx.tmpl +20 -0
  560. package/stacks/hubspot/pages/inbox.tsx.tmpl +103 -0
  561. package/stacks/hubspot/pages/lifecycle.tsx.tmpl +23 -0
  562. package/stacks/hubspot/pages/lists.tsx.tmpl +12 -0
  563. package/stacks/hubspot/pages/meetings.tsx.tmpl +13 -0
  564. package/stacks/hubspot/pages/quotes.tsx.tmpl +11 -0
  565. package/stacks/hubspot/pages/sequences.tsx.tmpl +12 -0
  566. package/stacks/hubspot/pages/tasks.tsx.tmpl +20 -0
  567. package/stacks/hubspot.json +181 -64
  568. package/stacks/intercom/pages/companies.tsx.tmpl +19 -0
  569. package/stacks/intercom/pages/contacts.tsx.tmpl +21 -0
  570. package/stacks/intercom/pages/inbox-contact.tsx.tmpl +13 -0
  571. package/stacks/intercom/pages/tasks.tsx.tmpl +19 -0
  572. package/stacks/intercom.json +52 -20
  573. package/stacks/mailchimp/pages/companies.tsx.tmpl +22 -0
  574. package/stacks/mailchimp/pages/contacts.tsx.tmpl +28 -0
  575. package/stacks/mailchimp/pages/lists.tsx.tmpl +12 -0
  576. package/stacks/mailchimp/pages/tasks.tsx.tmpl +19 -0
  577. package/stacks/mailchimp.json +68 -34
  578. package/stacks/outreach/pages/calls.tsx.tmpl +23 -0
  579. package/stacks/outreach/pages/companies.tsx.tmpl +22 -0
  580. package/stacks/outreach/pages/contacts.tsx.tmpl +46 -0
  581. package/stacks/outreach/pages/inbox-contact.tsx.tmpl +13 -0
  582. package/stacks/outreach/pages/lists.tsx.tmpl +12 -0
  583. package/stacks/outreach/pages/meetings.tsx.tmpl +13 -0
  584. package/stacks/outreach/pages/sequences.tsx.tmpl +12 -0
  585. package/stacks/outreach/pages/tasks.tsx.tmpl +19 -0
  586. package/stacks/outreach.json +106 -25
  587. package/stacks/pipedrive/pages/companies.tsx.tmpl +23 -0
  588. package/stacks/pipedrive/pages/contacts.tsx.tmpl +27 -0
  589. package/stacks/pipedrive/pages/deals.tsx.tmpl +20 -0
  590. package/stacks/pipedrive/pages/inbox.tsx.tmpl +103 -0
  591. package/stacks/pipedrive/pages/meetings.tsx.tmpl +13 -0
  592. package/stacks/pipedrive/pages/quotes.tsx.tmpl +11 -0
  593. package/stacks/pipedrive/pages/tasks.tsx.tmpl +20 -0
  594. package/stacks/pipedrive.json +117 -0
  595. package/stacks/salesforce/pages/companies.tsx.tmpl +26 -0
  596. package/stacks/salesforce/pages/contacts.tsx.tmpl +32 -0
  597. package/stacks/salesforce/pages/deals.tsx.tmpl +20 -0
  598. package/stacks/salesforce/pages/lifecycle.tsx.tmpl +23 -0
  599. package/stacks/salesforce/pages/lists.tsx.tmpl +12 -0
  600. package/stacks/salesforce/pages/quotes.tsx.tmpl +11 -0
  601. package/stacks/salesforce/pages/tasks.tsx.tmpl +20 -0
  602. package/stacks/salesforce.json +85 -31
  603. package/stacks/zoominfo/pages/companies.tsx.tmpl +31 -0
  604. package/stacks/zoominfo/pages/contacts.tsx.tmpl +31 -0
  605. package/stacks/zoominfo/pages/lists.tsx.tmpl +12 -0
  606. package/stacks/zoominfo/pages/tasks.tsx.tmpl +19 -0
  607. package/stacks/zoominfo.json +115 -0
  608. package/styles/styles.css +1 -1
  609. package/templates/next-app/README.md.tmpl +40 -9
  610. package/templates/next-app/app/globals.css.tmpl +349 -64
  611. package/templates/next-app/app/layout.tsx.tmpl +7 -2
  612. package/templates/next-app/components/app-shell.tsx.tmpl +104 -88
  613. package/templates/next-app/components/shell/quick-jump.tsx.tmpl +4 -0
  614. package/templates/next-app/dot-gitignore.tmpl +2 -0
  615. package/templates/next-app/package.json.tmpl +1 -1
@@ -0,0 +1,230 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Shapes and helpers for the company-intelligence components.
5
+ *
6
+ * Reality of this API (read from the handlers, not assumed): intelligence is the research graph8 runs on YOUR OWN website
7
+ * (the workspace's primary website). Every response is an open payload, so each field below is optional and read
8
+ * defensively; a missing value is shown as unknown (a dash), never as a guess and never as 0.
9
+ *
10
+ * SDK operations: none (types and pure helpers).
11
+ */
12
+
13
+ export type Rec = Record<string, unknown>;
14
+
15
+ export const isRec = (v: unknown): v is Rec => v != null && typeof v === "object" && !Array.isArray(v);
16
+ export const asArray = (v: unknown): unknown[] => (Array.isArray(v) ? v : []);
17
+
18
+ /** A non-empty string (numbers are formatted), else null. */
19
+ export function text(v: unknown): string | null {
20
+ if (typeof v === "string") return v.trim() ? v.trim() : null;
21
+ if (typeof v === "number" && Number.isFinite(v)) return String(v);
22
+ return null;
23
+ }
24
+
25
+ export function num(v: unknown): number | null {
26
+ return typeof v === "number" && Number.isFinite(v) ? v : null;
27
+ }
28
+
29
+ /** Read a body that may or may not be wrapped in `{ data }`. */
30
+ export function intelBody<T>(env: unknown): T {
31
+ const e = env as { data?: unknown } | null;
32
+ return (e && typeof e === "object" && "data" in e && e.data != null ? e.data : env) as T;
33
+ }
34
+
35
+ /** The rows of a list payload: a bare array, `{ items }`, or a named key (`documents`, `reports`). */
36
+ export function rowsOf<T = Rec>(payload: unknown, ...keys: string[]): T[] {
37
+ if (Array.isArray(payload)) return payload as T[];
38
+ if (!isRec(payload)) return [];
39
+ for (const k of ["items", ...keys]) if (Array.isArray(payload[k])) return payload[k] as T[];
40
+ return [];
41
+ }
42
+
43
+ /** A host as a person types it: strips the scheme, www., port-less path and query. `https://www.Acme.com/x` -> `acme.com`. */
44
+ export function canonicalDomain(input: string | null | undefined): string {
45
+ return (input ?? "").trim().toLowerCase().replace(/^https?:\/\//, "").replace(/^www\./, "").split(/[/?#]/)[0];
46
+ }
47
+
48
+ /** http(s) URLs only: the API puts web content in these payloads, and a `javascript:` URL would run on click. */
49
+ export function safeHttpUrl(value: unknown): string | null {
50
+ const s = text(value);
51
+ if (!s || !/^https?:\/\/[^\s/]+/i.test(s)) return null;
52
+ try {
53
+ const u = new URL(s);
54
+ return u.protocol === "http:" || u.protocol === "https:" ? u.toString() : null;
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+
60
+ export function humanize(key: string): string {
61
+ const s = key.replace(/[_-]+/g, " ").trim();
62
+ return s ? s.charAt(0).toUpperCase() + s.slice(1) : key;
63
+ }
64
+
65
+ /** A key as a title: `brand_sentiment` -> `Brand Sentiment`, matching the display names the API gives collected sources. */
66
+ export function titleize(key: string): string {
67
+ return key.replace(/[_-]+/g, " ").trim().replace(/\b\w/g, (c) => c.toUpperCase());
68
+ }
69
+
70
+ export function when(v: unknown): string | null {
71
+ const s = text(v);
72
+ if (!s) return null;
73
+ const d = new Date(s);
74
+ return Number.isNaN(d.getTime()) ? s : d.toLocaleString();
75
+ }
76
+
77
+ /** Date part only, in UTC, so the same instant reads the same in every time zone. */
78
+ export function whenDate(v: unknown): string | null {
79
+ const s = text(v);
80
+ if (!s) return null;
81
+ const d = new Date(s);
82
+ return Number.isNaN(d.getTime()) ? s : d.toLocaleDateString(undefined, { timeZone: "UTC" });
83
+ }
84
+
85
+ // ---- research run (intelligence) -------------------------------------------------------------------------------------
86
+
87
+ export interface RunStatus {
88
+ task_id?: string;
89
+ status?: string;
90
+ progress?: number;
91
+ current_step?: string | null;
92
+ steps?: Record<string, string | null>;
93
+ website_url?: string;
94
+ created_at?: string | null;
95
+ completed_at?: string | null;
96
+ error_message?: string | null;
97
+ }
98
+
99
+ export interface LatestTask { task_id?: string; website_url?: string | null; status?: string; created_at?: string | null; completed_at?: string | null }
100
+
101
+ export interface AnalyzeAccepted { task_id?: string; message?: string; website_url?: string; domain_sync_warning?: string | null }
102
+
103
+ export interface IntelDocument {
104
+ id?: string;
105
+ data_type: string;
106
+ display_name?: string | null;
107
+ folder?: string | null;
108
+ website_url?: string | null;
109
+ status?: string | null;
110
+ collected_at?: string | null;
111
+ }
112
+
113
+ /** A run is final once it stops pending or running. */
114
+ export const isRunFinal = (status: string | undefined | null): boolean => status != null && status !== "pending" && status !== "running" && status !== "in_progress";
115
+
116
+ /** The steps of a run, in the order the API lists them, with their state. */
117
+ export function runSteps(run: RunStatus | undefined): Array<{ key: string; label: string; state: string }> {
118
+ return Object.entries(run?.steps ?? {}).map(([key, state]) => ({ key, label: humanize(key), state: state ?? "pending" }));
119
+ }
120
+
121
+ export const stepTone = (state: string): "positive" | "negative" | "info" | "neutral" =>
122
+ state === "completed" ? "positive" : state === "failed" ? "negative" : state === "running" || state === "in_progress" ? "info" : "neutral";
123
+
124
+ /** True when a run finished less than an hour ago: the API answers a repeat request with that run and charges nothing (no `force`). */
125
+ export function finishedWithinHour(task: LatestTask | null | undefined, now = Date.now()): boolean {
126
+ const at = task?.completed_at ? Date.parse(task.completed_at) : NaN;
127
+ return task?.status === "completed" && Number.isFinite(at) && now - at < 3_600_000 && now - at >= 0;
128
+ }
129
+
130
+ // ---- the six research reports ----------------------------------------------------------------------------------------
131
+
132
+ /** The report types the retry operation accepts (its request enum, copied from the contract), by report number 1 to 6. */
133
+ export const REPORT_TYPES = ["review_sentiment", "competitive_teardown", "voice_of_customer", "gtm_channel", "analyst_media", "buyer_psychology"] as const;
134
+ export type ReportType = (typeof REPORT_TYPES)[number];
135
+
136
+ export interface ReportSlot {
137
+ id: string | null;
138
+ report_number: number;
139
+ report_type: string;
140
+ report_title?: string | null;
141
+ status: string;
142
+ has_content?: boolean;
143
+ approved?: boolean;
144
+ created_at?: string | null;
145
+ started_at?: string | null;
146
+ completed_at?: string | null;
147
+ }
148
+
149
+ export interface WorkflowStatus {
150
+ website_url?: string;
151
+ workflow_status?: string;
152
+ reports_completed?: number;
153
+ total_reports?: number;
154
+ current_report?: string | null;
155
+ reports?: Record<string, ReportSlot>;
156
+ started_at?: string | null;
157
+ completed_at?: string | null;
158
+ }
159
+
160
+ /** The six slots in report order. `reports` is keyed `report_1`..`report_6`. */
161
+ export function slotsOf(w: WorkflowStatus | undefined): ReportSlot[] {
162
+ const map = w?.reports ?? {};
163
+ return Object.keys(map).sort((a, b) => Number(a.replace(/\D/g, "")) - Number(b.replace(/\D/g, ""))).map((k) => map[k]);
164
+ }
165
+
166
+ export const isGenerating = (status: string | undefined | null): boolean => status === "generating" || status === "in_progress";
167
+ export const isReportDone = (status: string | undefined | null): boolean => status === "completed" || status === "approved";
168
+
169
+ /** Reports a workflow resume would generate: every slot that is not finished and is not already running. */
170
+ export function incompleteSlots(slots: ReportSlot[]): ReportSlot[] {
171
+ return slots.filter((s) => !isReportDone(s.status) && !isGenerating(s.status));
172
+ }
173
+
174
+ export interface ReportDetailBody {
175
+ id?: string;
176
+ report_title?: string | null;
177
+ report_type?: string;
178
+ report_number?: number;
179
+ website_url?: string;
180
+ status?: string;
181
+ markdown_content?: string | null;
182
+ error_message?: string | null;
183
+ approved?: boolean;
184
+ approved_at?: string | null;
185
+ estimated_cost_usd?: number | null;
186
+ credits_used?: number | null;
187
+ created_at?: string | null;
188
+ updated_at?: string | null;
189
+ completed_at?: string | null;
190
+ }
191
+
192
+ /** What the report used the last time it ran: a real number from the report itself, or null when the API did not say. */
193
+ export function lastRunCredits(r: ReportDetailBody | undefined): number | null {
194
+ const v = num(r?.credits_used) ?? num(r?.estimated_cost_usd);
195
+ return v != null && v > 0 ? v : null;
196
+ }
197
+
198
+ /**
199
+ * The PATCH body for an edited report: only what changed. Clearing the text sends "" (the API only ignores null).
200
+ * Returns null when nothing changed.
201
+ */
202
+ export function reportEdit(original: string | null | undefined, draft: string): { markdown_content: string } | null {
203
+ return (original ?? "") === draft ? null : { markdown_content: draft };
204
+ }
205
+
206
+ export interface ThinkingEvent { type?: string; timestamp?: string; data?: Rec | null }
207
+
208
+ /** One readable line for a recorded reasoning event, from the fields the stream writes (`message`, tool name, error). */
209
+ export function eventLine(e: ThinkingEvent): string {
210
+ const d = isRec(e.data) ? e.data : {};
211
+ return text(d.message) ?? text(d.tool) ?? text(d.tool_name) ?? text(d.error) ?? text(d.status) ?? humanize(e.type ?? "event");
212
+ }
213
+
214
+ // ---- wiki (global context) documents ---------------------------------------------------------------------------------
215
+
216
+ export interface WikiDoc {
217
+ id: string;
218
+ file_type?: string | null;
219
+ display_name?: string | null;
220
+ category?: string | null;
221
+ folder_path?: string | null;
222
+ status?: string | null;
223
+ version?: number | null;
224
+ content?: string | null;
225
+ credits_used?: number | null;
226
+ updated_at?: string | null;
227
+ is_done?: boolean;
228
+ }
229
+
230
+ export const docTitle = (d: Pick<WikiDoc, "display_name" | "file_type" | "id">): string => text(d.display_name) ?? (d.file_type ? humanize(d.file_type) : d.id);
@@ -0,0 +1,150 @@
1
+ "use client";
2
+
3
+ /**
4
+ * SafeMarkdown: renders the markdown the API returns (research reports, wiki documents) without ever turning it into HTML.
5
+ * Every piece of text becomes a React text node, so raw HTML, `<script>`, event handlers and `javascript:` links in a
6
+ * report show up as plain text instead of running. Links are drawn only for http(s) URLs (with `rel="noopener noreferrer"`);
7
+ * images are not drawn, they stay as their text. No `dangerouslySetInnerHTML` anywhere.
8
+ *
9
+ * Supported: headings, paragraphs, bullet and numbered lists, block quotes, fenced code, horizontal rules, pipe tables,
10
+ * and inline bold, italic, code and links. Anything else is a paragraph of plain text.
11
+ *
12
+ * SDK operations: none.
13
+ */
14
+ import type { ReactNode } from "react";
15
+ import { cn } from "@graph8/sdk/ui";
16
+ import { safeHttpUrl } from "./intelligence-types";
17
+
18
+ // Underscore emphasis only at word edges, so snake_case_names are left alone.
19
+ const INLINE = /(\*\*([^*]+)\*\*|(?<!\w)__([^_]+)__(?!\w)|`([^`]+)`|\[([^\]]+)\]\(([^)\s]+)\)|\*([^*\s][^*]*)\*|(?<!\w)_([^_\s][^_]*)_(?!\w))/g;
20
+
21
+ /** Inline markup to React nodes. Unsafe link targets keep their text and lose the link. */
22
+ export function renderInline(source: string, keyPrefix = "i"): ReactNode[] {
23
+ const out: ReactNode[] = [];
24
+ let last = 0;
25
+ let n = 0;
26
+ for (const m of source.matchAll(INLINE)) {
27
+ const at = m.index ?? 0;
28
+ if (at > last) out.push(source.slice(last, at));
29
+ const key = `${keyPrefix}${n++}`;
30
+ if (m[2] ?? m[3]) out.push(<strong key={key}>{m[2] ?? m[3]}</strong>);
31
+ else if (m[4]) out.push(<code key={key} className="rounded bg-muted px-1 py-0.5 text-[0.9em]">{m[4]}</code>);
32
+ else if (m[5]) {
33
+ const href = safeHttpUrl(m[6]);
34
+ out.push(href ? <a key={key} href={href} target="_blank" rel="noopener noreferrer" className="underline underline-offset-2">{m[5]}</a> : <span key={key}>{m[5]}</span>);
35
+ } else out.push(<em key={key}>{m[7] ?? m[8]}</em>);
36
+ last = at + m[0].length;
37
+ }
38
+ if (last < source.length) out.push(source.slice(last));
39
+ return out;
40
+ }
41
+
42
+ const HEADING = /^(#{1,6})\s+(.*)$/;
43
+ const BULLET = /^\s*[-*+]\s+(.*)$/;
44
+ const ORDERED = /^\s*\d+[.)]\s+(.*)$/;
45
+ const RULE = /^\s*([-*_])(\s*\1){2,}\s*$/;
46
+ const TABLE_ROW = /^\s*\|.*\|\s*$/;
47
+ const TABLE_SEP = /^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)*\|?\s*$/;
48
+
49
+ const cells = (line: string): string[] => line.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
50
+
51
+ type Block =
52
+ | { kind: "heading"; level: number; text: string }
53
+ | { kind: "para"; text: string }
54
+ | { kind: "ul" | "ol"; items: string[] }
55
+ | { kind: "quote"; text: string }
56
+ | { kind: "code"; text: string }
57
+ | { kind: "rule" }
58
+ | { kind: "table"; head: string[]; rows: string[][] };
59
+
60
+ /** Split markdown into blocks. Pure, so it is tested without rendering. */
61
+ export function parseBlocks(markdown: string): Block[] {
62
+ const lines = markdown.replace(/\r\n?/g, "\n").split("\n");
63
+ const blocks: Block[] = [];
64
+ let i = 0;
65
+ while (i < lines.length) {
66
+ const line = lines[i];
67
+ if (!line.trim()) { i++; continue; }
68
+ if (/^\s*```/.test(line)) {
69
+ const body: string[] = [];
70
+ for (i++; i < lines.length && !/^\s*```/.test(lines[i]); i++) body.push(lines[i]);
71
+ i++;
72
+ blocks.push({ kind: "code", text: body.join("\n") });
73
+ continue;
74
+ }
75
+ const h = HEADING.exec(line);
76
+ if (h) { blocks.push({ kind: "heading", level: h[1].length, text: h[2].trim() }); i++; continue; }
77
+ if (RULE.test(line)) { blocks.push({ kind: "rule" }); i++; continue; }
78
+ if (TABLE_ROW.test(line) && i + 1 < lines.length && TABLE_SEP.test(lines[i + 1])) {
79
+ const rows: string[][] = [];
80
+ let j = i + 2;
81
+ for (; j < lines.length && TABLE_ROW.test(lines[j]); j++) rows.push(cells(lines[j]));
82
+ blocks.push({ kind: "table", head: cells(line), rows });
83
+ i = j;
84
+ continue;
85
+ }
86
+ if (BULLET.test(line) || ORDERED.test(line)) {
87
+ const ordered = ORDERED.test(line) && !BULLET.test(line);
88
+ const items: string[] = [];
89
+ for (; i < lines.length; i++) {
90
+ const m = (ordered ? ORDERED : BULLET).exec(lines[i]);
91
+ if (!m) break;
92
+ items.push(m[1]);
93
+ }
94
+ blocks.push({ kind: ordered ? "ol" : "ul", items });
95
+ continue;
96
+ }
97
+ if (/^\s*>/.test(line)) {
98
+ const body: string[] = [];
99
+ for (; i < lines.length && /^\s*>/.test(lines[i]); i++) body.push(lines[i].replace(/^\s*>\s?/, ""));
100
+ blocks.push({ kind: "quote", text: body.join(" ") });
101
+ continue;
102
+ }
103
+ const para: string[] = [];
104
+ for (; i < lines.length && lines[i].trim() && !HEADING.test(lines[i]) && !BULLET.test(lines[i]) && !ORDERED.test(lines[i]) && !/^\s*(```|>)/.test(lines[i]); i++) para.push(lines[i].trim());
105
+ blocks.push({ kind: "para", text: para.join(" ") });
106
+ }
107
+ return blocks;
108
+ }
109
+
110
+ const HEADING_CLASS = ["", "text-lg font-semibold", "text-base font-semibold", "text-sm font-semibold", "text-sm font-medium", "text-sm font-medium", "text-sm font-medium"];
111
+
112
+ function BlockView({ block, k }: { block: Block; k: number }): ReactNode {
113
+ switch (block.kind) {
114
+ case "heading": {
115
+ const Tag = `h${Math.min(block.level + 1, 6)}` as "h2";
116
+ return <Tag className={cn("mt-4 text-foreground first:mt-0", HEADING_CLASS[block.level])}>{renderInline(block.text, `h${k}-`)}</Tag>;
117
+ }
118
+ case "ul":
119
+ case "ol": {
120
+ const Tag = block.kind;
121
+ return <Tag className={cn("my-2 space-y-1 pl-5", block.kind === "ul" ? "list-disc" : "list-decimal")}>{block.items.map((t, n) => <li key={n}>{renderInline(t, `l${k}-${n}-`)}</li>)}</Tag>;
122
+ }
123
+ case "quote": return <blockquote className="my-2 border-l-2 border-border pl-3 text-muted-foreground">{renderInline(block.text, `q${k}-`)}</blockquote>;
124
+ case "code": return <pre className="my-2 overflow-x-auto rounded-md bg-muted p-3 text-xs"><code>{block.text}</code></pre>;
125
+ case "rule": return <hr className="my-3 border-border" />;
126
+ case "table":
127
+ return (
128
+ <div className="my-2 overflow-x-auto">
129
+ <table className="w-full border-collapse text-left text-xs">
130
+ <thead><tr>{block.head.map((c, n) => <th key={n} className="border-b border-border px-2 py-1 font-medium">{renderInline(c, `th${k}-${n}-`)}</th>)}</tr></thead>
131
+ <tbody>{block.rows.map((r, ri) => <tr key={ri}>{r.map((c, n) => <td key={n} className="border-b border-border px-2 py-1 align-top">{renderInline(c, `td${k}-${ri}-${n}-`)}</td>)}</tr>)}</tbody>
132
+ </table>
133
+ </div>
134
+ );
135
+ default: return <p className="my-2 leading-6">{renderInline(block.text, `p${k}-`)}</p>;
136
+ }
137
+ }
138
+
139
+ export interface SafeMarkdownProps {
140
+ markdown: string;
141
+ className?: string;
142
+ }
143
+
144
+ export function SafeMarkdown({ markdown, className }: SafeMarkdownProps) {
145
+ return (
146
+ <div data-testid="safe-markdown" className={cn("text-sm text-foreground", className)}>
147
+ {parseBlocks(markdown).map((b, n) => <BlockView key={n} block={b} k={n} />)}
148
+ </div>
149
+ );
150
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @title Read the competitors and the news the research found
3
+ * @when Feeding a briefing, a battle-card draft or a Slack digest. Both reads are free. They describe the competitors of YOUR
4
+ * website (the research is locked to it) and brand news about YOUR company, not about arbitrary companies, and the
5
+ * API has no lookup for another company's profile, technology or org chart. Article text and URLs come from the web: treat
6
+ * them as untrusted text and only open http(s) URLs.
7
+ */
8
+ import { g8 } from "@graph8/sdk";
9
+
10
+ g8.init({ apiKey: process.env.G8_API_KEY! });
11
+
12
+ type Source = { data?: Record<string, unknown> | null; collected_at?: string };
13
+ const entry = (res: unknown): Source => ((res as { data?: Source } | null)?.data ?? (res as Source) ?? {}) as Source;
14
+
15
+ export interface Competitor { name?: string; domain?: string; description?: string; competitive_type?: string; similarity?: string; confidence?: string; reasoning?: string }
16
+ export interface Article { title?: string; url?: string; description?: string; published_at?: string; sentiment?: number | null; source?: { name?: string } }
17
+
18
+ export async function competitors(websiteUrl: string): Promise<Competitor[]> {
19
+ const res = entry(await g8.api.companyIntelligence.getIntelligenceData({ path: { data_type: "competitor_discovery" }, query: { website_url: websiteUrl } }));
20
+ return (Array.isArray(res.data?.competitors) ? res.data?.competitors : []) as Competitor[];
21
+ }
22
+
23
+ /** Only http(s) links, newest first. `sentiment` is -1 to 1 or null (unknown, not neutral). */
24
+ export async function recentNews(websiteUrl: string): Promise<Article[]> {
25
+ const res = entry(await g8.api.companyIntelligence.getIntelligenceData({ path: { data_type: "brand_news" }, query: { website_url: websiteUrl } }));
26
+ const articles = (Array.isArray(res.data?.articles) ? res.data?.articles : []) as Article[];
27
+ return articles.filter((a) => /^https?:\/\//i.test(a.url ?? "")).sort((a, b) => String(b.published_at ?? "").localeCompare(String(a.published_at ?? "")));
28
+ }
29
+
30
+ /** Everything collected, keyed by source type, in one read. */
31
+ export async function everything(websiteUrl: string) {
32
+ return entry(await g8.api.companyIntelligence.getAllIntelligenceData({ path: { website_url: websiteUrl } })) as Record<string, Source>;
33
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @title Research your own website and read the company profile
3
+ * @when Setting up company intelligence for a workspace. The research is locked to ONE website (the workspace's primary
4
+ * website, set by the first run), so this reads it first and never guesses a domain. Starting a run is BILLABLE and the API
5
+ * quotes no price (it is metered by usage): tell a human what will be collected and get a yes first. A run finished less than an
6
+ * hour ago is returned as is (no new work, no charge) unless `force` is true. Order: primary website -> analyze -> poll the
7
+ * run -> read the company profile.
8
+ */
9
+ import { g8 } from "@graph8/sdk";
10
+
11
+ g8.init({ apiKey: process.env.G8_API_KEY! });
12
+
13
+ type Body = Record<string, unknown>;
14
+ const body = (res: unknown): Body => ((res as { data?: Body } | null)?.data ?? (res as Body) ?? {}) as Body;
15
+
16
+ /** The website the research is locked to, or null before the first run. Never invent one. */
17
+ export async function primaryWebsite(): Promise<string | null> {
18
+ const res = body(await g8.api.companyIntelligence.getIntelligencePrimaryWebsite());
19
+ return typeof res.primary_website_url === "string" && res.primary_website_url ? res.primary_website_url : null;
20
+ }
21
+
22
+ /** Billable. Only after a person approved researching `websiteUrl`. With no primary website yet, this one becomes the locked one. */
23
+ export async function startResearch(websiteUrl: string, force = false) {
24
+ const res = body(await g8.api.companyIntelligence.analyzeWebsite({ body: force ? { website_url: websiteUrl, force: true } : { website_url: websiteUrl } }));
25
+ return res.task_id as string;
26
+ }
27
+
28
+ export async function waitForRun(taskId: string) {
29
+ let delay = 3000;
30
+ for (let i = 0; i < 100; i++) {
31
+ const run = body(await g8.api.companyIntelligence.getIntelligenceStatus({ path: { task_id: taskId } }));
32
+ if (run.status !== "pending" && run.status !== "running") return run;
33
+ await new Promise((r) => setTimeout(r, delay));
34
+ delay = Math.min(15000, Math.round(delay * 1.3));
35
+ }
36
+ throw new Error("the research did not finish in time; read the status again later");
37
+ }
38
+
39
+ /** Free. A source that was not collected answers 404: treat that as "not collected", not as an error. */
40
+ export async function companyProfile(websiteUrl: string) {
41
+ const entry = body(await g8.api.companyIntelligence.getIntelligenceData({ path: { data_type: "company_enrichment" }, query: { website_url: websiteUrl } }));
42
+ return (entry.data ?? null) as Record<string, unknown> | null;
43
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @title Write the six research reports, follow them and approve one
3
+ * @when Building market research from your website. Writing a report is the most expensive single action in Studio and
4
+ * the API quotes no price: `generateResearchReport` is one long multi-pass run, `generateAllResearchReports` is six,
5
+ * `resumeResearchWorkflow` writes every report that is not finished. Read the workflow first to count them, tell a human the
6
+ * number, and get a yes. Approving a report indexes it into the knowledge base that campaigns and the copilot read.
7
+ * Order: workflow status -> generate (or retry a failed one) -> poll the workflow -> read the report -> approve.
8
+ */
9
+ import { g8 } from "@graph8/sdk";
10
+
11
+ g8.init({ apiKey: process.env.G8_API_KEY! });
12
+
13
+ type Body = Record<string, unknown>;
14
+ const body = (res: unknown): Body => ((res as { data?: Body } | null)?.data ?? (res as Body) ?? {}) as Body;
15
+
16
+ export interface Slot { id: string | null; report_number: number; report_type: string; status: string; approved?: boolean }
17
+
18
+ /** Free. Always six slots (`report_1`..`report_6`), including the ones not written yet. */
19
+ export async function workflow(websiteUrl: string) {
20
+ const w = body(await g8.api.research.getResearchWorkflowStatus({ query: { website_url: websiteUrl } }));
21
+ const slots = Object.values((w.reports ?? {}) as Record<string, Slot>);
22
+ return { status: w.workflow_status as string, slots, missing: slots.filter((s) => s.status !== "completed" && s.status !== "approved" && s.status !== "generating") };
23
+ }
24
+
25
+ /** Billable. `reportNumber` is 1 to 6. */
26
+ export async function writeReport(websiteUrl: string, reportNumber: number) {
27
+ return g8.api.research.generateResearchReport({ body: { website_url: websiteUrl, report_number: reportNumber } });
28
+ }
29
+
30
+ /** Billable. Re-runs ONE failed report; `reportType` is one of the six types in the workflow slots. */
31
+ export async function retryFailed(websiteUrl: string, reportType: "review_sentiment" | "competitive_teardown" | "voice_of_customer" | "gtm_channel" | "analyst_media" | "buyer_psychology") {
32
+ return g8.api.research.retryResearchReport({ body: { website_url: websiteUrl, report_type: reportType } });
33
+ }
34
+
35
+ export async function waitForReports(websiteUrl: string) {
36
+ let delay = 5000;
37
+ for (let i = 0; i < 200; i++) {
38
+ const w = await workflow(websiteUrl);
39
+ if (!w.slots.some((s) => s.status === "generating")) return w;
40
+ await new Promise((r) => setTimeout(r, delay));
41
+ delay = Math.min(30000, Math.round(delay * 1.3));
42
+ }
43
+ throw new Error("the reports did not finish in time; read the workflow again later");
44
+ }
45
+
46
+ /** Free. The markdown is model output: render it as text, never as HTML. */
47
+ export async function readReport(reportId: string) {
48
+ return body(await g8.api.research.getResearchReport({ path: { report_id: reportId } }));
49
+ }
50
+
51
+ /** Free, but it publishes the report to everything that reads the knowledge base. */
52
+ export async function approve(reportId: string) {
53
+ return g8.api.research.approveResearchReport({ path: { report_id: reportId } });
54
+ }
@@ -4,6 +4,19 @@ Drop an "Ask graph8" side panel into your app. It chats with the copilot (which
4
4
  copy and propose changes), chats in a Work channel where agents answer, and manages what the copilot
5
5
  remembers. Everything is request/response or polled: the API has no streams.
6
6
 
7
+ ## Screens
8
+
9
+ `CopilotScreen` (`components/copilot-screen.tsx`) is the copilot as a full page; mount it as the page.
10
+
11
+ - **Ask**: the conversation with starter questions (`starterPrompts`), an **Ask about** card (whole workspace, one
12
+ campaign or one account; changing it (on Enter or leaving the field) starts a fresh conversation) and a **What it can do** card listing the tools
13
+ and which need approval in graph8. The first message of a session confirms credits. The conversation survives
14
+ switching tabs.
15
+ - **Team**: every Work channel as a list; opening one (`?id=`, full page with `&view=full`) shows the whole thread: details and the polled conversation with a message box and "ask an agent" (billable, confirmed).
16
+ - **Memory**: add and forget (confirmed) what the copilot remembers.
17
+ - Needs `knowledge:run|read|write|delete` and `work:read|write`. `CopilotPanel` gained `hideTabs` and
18
+ `starterPrompts`; `CopilotTeamChat` gained `hideChannelPicker`.
19
+
7
20
  Install: `npx @graph8/sdk add copilot` (installs `foundation` first).
8
21
 
9
22
  ## What you get
@@ -26,6 +26,18 @@
26
26
  "search_copilot_mentions_copilot_mention_search_post"
27
27
  ],
28
28
  "components": [
29
+ {
30
+ "name": "CopilotScreen",
31
+ "file": "components/copilot-screen.tsx",
32
+ "purpose": "Full-page copilot: conversation with starters, ask-about context, tools card, team channel and memory tabs.",
33
+ "operations": [
34
+ "list_copilot_tools_copilot_tools_get",
35
+ "list_work_channels_work_channels_get"
36
+ ],
37
+ "tiers": [
38
+ "read"
39
+ ]
40
+ },
29
41
  {
30
42
  "name": "CopilotPanel",
31
43
  "file": "components/copilot-panel.tsx",
@@ -146,6 +158,7 @@
146
158
  }
147
159
  ],
148
160
  "peerDependencies": {
149
- "react": ">=18"
161
+ "react": ">=18",
162
+ "lucide-react": ">=0.400.0"
150
163
  }
151
164
  }
@@ -16,6 +16,7 @@
16
16
  import { useState } from "react";
17
17
  import { Button, Input, Skeleton } from "@graph8/sdk/ui";
18
18
  import { ApiError } from "@/g8/components/api-error";
19
+ import { FirstNotice } from "@/g8/components/paging-notices";
19
20
  import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
20
21
  import { EmptyState } from "@/g8/components/empty-state";
21
22
  import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
@@ -30,6 +31,8 @@ export interface CopilotMemoryProps {
30
31
  className?: string;
31
32
  }
32
33
 
34
+ /** The memory API has no paging: a full read is announced under the list. */
35
+ const MEMORY_LIMIT = 100;
33
36
  const idOf = (m: MemoryRow): string => m.memory_id ?? m.id ?? "";
34
37
 
35
38
  export function CopilotMemory({ scope = "user", className }: CopilotMemoryProps) {
@@ -37,7 +40,7 @@ export function CopilotMemory({ scope = "user", className }: CopilotMemoryProps)
37
40
  const [forgetting, setForgetting] = useState<MemoryRow | null>(null);
38
41
  const [showTools, setShowTools] = useState(false);
39
42
 
40
- const memories = useG8Query((signal) => g8Get("/copilot/memory", { scope, limit: 100 }, { signal }), [scope]);
43
+ const memories = useG8Query((signal) => g8Get("/copilot/memory", { scope, limit: MEMORY_LIMIT }, { signal }), [scope]);
41
44
  const tools = useG8Query((signal) => g8Get("/copilot/tools", undefined, { signal }), [], { enabled: showTools });
42
45
  const add = useG8Mutation(() => g8Post("/copilot/memory", { content: note.trim(), scope }), { onSuccess: () => { setNote(""); void memories.refetch(); } });
43
46
  const forget = useG8Mutation((id: string) => g8Delete(`/copilot/memory/${id}`), { onSuccess: () => { setForgetting(null); void memories.refetch(); } });
@@ -62,6 +65,7 @@ export function CopilotMemory({ scope = "user", className }: CopilotMemoryProps)
62
65
  </li>
63
66
  ))}
64
67
  </ul>
68
+ <FirstNotice shown={rows.length} limit={MEMORY_LIMIT} noun="memories" />
65
69
  {!memories.loading && !memories.error && rows.length === 0 ? <EmptyState title="Nothing remembered yet" description="Add the rules and facts you want in every answer." /> : null}
66
70
  {forget.error ? <ApiError error={forget.error} title="Could not forget it" /> : null}
67
71
 
@@ -73,7 +77,7 @@ export function CopilotMemory({ scope = "user", className }: CopilotMemoryProps)
73
77
  {toolRows.map((t) => (
74
78
  <li key={t.name} className="flex items-start justify-between gap-3 p-2 text-sm">
75
79
  <span><span className="font-medium">{t.name}</span>{t.description ? <span className="block text-xs text-muted-foreground">{t.description}</span> : null}</span>
76
- {t.requires_approval || t.needs_approval ? <span className="text-xs text-amber-700">needs approval</span> : null}
80
+ {t.requires_approval || t.needs_approval ? <span className="text-xs text-warning-foreground">needs approval</span> : null}
77
81
  </li>
78
82
  ))}
79
83
  </ul>