@worktango/ai-assistant 0.0.21 → 0.0.24

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 (209) hide show
  1. package/README.md +18 -2
  2. package/dist/{Absenteeism-CedNvSxC.mjs → Absenteeism-BlfJuY7Q.mjs} +1 -1
  3. package/dist/{ActivityFeed-wI9wBx6H.mjs → ActivityFeed-CdbbvPCr.mjs} +1 -1
  4. package/dist/{AddUser-DE-2WrlL.mjs → AddUser-Dm5tN1Cr.mjs} +1 -1
  5. package/dist/{Admin-Cfl1AW_P.mjs → Admin-Cv5LVBbB.mjs} +1 -1
  6. package/dist/{Admin-C15wLDAT.mjs → Admin-GJZ14xCg.mjs} +1 -1
  7. package/dist/{Anniversary-CsvnVDOS.mjs → Anniversary-CxsqeD8j.mjs} +1 -1
  8. package/dist/{Appreciation-CYMmLsU_.mjs → Appreciation-CplBRYpa.mjs} +1 -1
  9. package/dist/{ArrowDown-Dubu_Ec7.mjs → ArrowDown-DCYs9jbi.mjs} +1 -1
  10. package/dist/{ArrowLeft-A1yp5aqr.mjs → ArrowLeft-DlFDpsqu.mjs} +1 -1
  11. package/dist/{ArrowRight-BhAh3I-x.mjs → ArrowRight-CDqrZvCr.mjs} +1 -1
  12. package/dist/{ArrowUp-bVYNtMgx.mjs → ArrowUp-DtWnNyRL.mjs} +1 -1
  13. package/dist/{Attachment-CWcY2CqV.mjs → Attachment-DEzcZHI7.mjs} +1 -1
  14. package/dist/{BeQuickDontHurry-DbSFImq2.mjs → BeQuickDontHurry-DoCKS3D0.mjs} +1 -1
  15. package/dist/{Birthday-C5IrWOxl.mjs → Birthday-COe-42mW.mjs} +1 -1
  16. package/dist/{BrandPromise-C_LmWwe6.mjs → BrandPromise-DT7BpKTq.mjs} +1 -1
  17. package/dist/{BrandVision-DYq6uUoU.mjs → BrandVision-CsF5PHr9.mjs} +1 -1
  18. package/dist/{Calendar-BYVlbm4s.mjs → Calendar-CbVaJVcV.mjs} +1 -1
  19. package/dist/{CaretDn-BeyN8HcH.mjs → CaretDn-DfXkDzAH.mjs} +1 -1
  20. package/dist/{CaretDown-BS3WGRra.mjs → CaretDown-DKwSpoJj.mjs} +1 -1
  21. package/dist/{CaretLeft-BXRjchoM.mjs → CaretLeft-DjIuIw4E.mjs} +1 -1
  22. package/dist/{CaretRight-CTegVEEE.mjs → CaretRight-Blv3CCWa.mjs} +1 -1
  23. package/dist/{CaretUp-B4tN3uVW.mjs → CaretUp-CEfq2pon.mjs} +1 -1
  24. package/dist/{ChallengeAccepted-DzEX4vek.mjs → ChallengeAccepted-BrB08i_g.mjs} +1 -1
  25. package/dist/{CheckIn-BsEJXvpg.mjs → CheckIn-DHloFCTi.mjs} +1 -1
  26. package/dist/{CheckIns-CeDSmtsH.mjs → CheckIns-BokF9D3N.mjs} +1 -1
  27. package/dist/{CheckSeal-BPPpnqgj.mjs → CheckSeal-3RBoC7kt.mjs} +1 -1
  28. package/dist/{Checkmark-DPwoD2cc.mjs → Checkmark-Cvb9TKPn.mjs} +1 -1
  29. package/dist/{Clock-C9YN2UE6.mjs → Clock-CRQ3vRb2.mjs} +1 -1
  30. package/dist/{Close-CuB41vwe.mjs → Close-CgKO-GmV.mjs} +1 -1
  31. package/dist/{CloudDownload-CxbXkl-A.mjs → CloudDownload-h8jFkmQV.mjs} +1 -1
  32. package/dist/{Comment-B70VK5iQ.mjs → Comment-CGL9Fj78.mjs} +1 -1
  33. package/dist/{Comments-B1eVtuj_.mjs → Comments-DnQeuBWu.mjs} +1 -1
  34. package/dist/{Connections-CgHfsXFl.mjs → Connections-GHPqdXk3.mjs} +1 -1
  35. package/dist/{Copy-BT-yQJw7.mjs → Copy-DSXLX28f.mjs} +1 -1
  36. package/dist/{CustomQuestion-DAwKynPQ.mjs → CustomQuestion-CYiCPJX8.mjs} +1 -1
  37. package/dist/{CustomerSuccess-1U9T-T1n.mjs → CustomerSuccess-FdMSyQLg.mjs} +1 -1
  38. package/dist/{DatePicker-BP1Bpv1b.mjs → DatePicker-0RNUxWxN.mjs} +1 -1
  39. package/dist/{Dot-CfD9KJ6k.mjs → Dot-CRWfXlj2.mjs} +1 -1
  40. package/dist/{Download-G0LOA1A3.mjs → Download-Be5-WujX.mjs} +1 -1
  41. package/dist/{Drag-C4fCOqTJ.mjs → Drag-wZ_mXU-D.mjs} +1 -1
  42. package/dist/{Earn-_iBKtfla.mjs → Earn-06RGCgPA.mjs} +1 -1
  43. package/dist/{Email-Cmm1RvmL.mjs → Email-8o3h5VSI.mjs} +1 -1
  44. package/dist/{EmojiDisplay-BZzD6ibf.mjs → EmojiDisplay-D3d36Nrl.mjs} +2 -2
  45. package/dist/{EmojiPicker-DMtVAC8n.mjs → EmojiPicker-BzkYk2Yv.mjs} +2 -2
  46. package/dist/{EnpsSurvey-DEMQkTOy.mjs → EnpsSurvey-wpqDe9Xq.mjs} +1 -1
  47. package/dist/{Expired-BflOG-Fm.mjs → Expired-DbhcQ39l.mjs} +1 -1
  48. package/dist/{Eye-B6MG7oI1.mjs → Eye-Dceyvp5y.mjs} +1 -1
  49. package/dist/{Feedback-qpfEKs95.mjs → Feedback-BQpvKfzY.mjs} +1 -1
  50. package/dist/{Feedback-DBmAcA2m.mjs → Feedback-DTwwTQN3.mjs} +3 -3
  51. package/dist/{Filter-BN9dK5kD.mjs → Filter-CKJYpt6s.mjs} +1 -1
  52. package/dist/{Flag-jKSpbvn6.mjs → Flag-Bex2EvDE.mjs} +1 -1
  53. package/dist/{Giphy-CXDFjiTX.mjs → Giphy-CFMqJSYK.mjs} +1 -1
  54. package/dist/{GoalCount-KUyaRVck.mjs → GoalCount-Bi0NMucg.mjs} +1 -1
  55. package/dist/{GoalPercent-DDsVzhO3.mjs → GoalPercent-B-kxyps2.mjs} +1 -1
  56. package/dist/{GoalTask-TDXXVUhs.mjs → GoalTask-IZwR0ZSf.mjs} +1 -1
  57. package/dist/{GoalTask-C3AKmm5p.mjs → GoalTask-Xjgp_Wo0.mjs} +1 -1
  58. package/dist/{Goals-a3Gd4p1n.mjs → Goals-BBGtKkJ1.mjs} +1 -1
  59. package/dist/{Goals-BnrsLd9r.mjs → Goals-COoucVqq.mjs} +1 -1
  60. package/dist/{Growth-CCeyxRJc.mjs → Growth-D8A13g-Q.mjs} +1 -1
  61. package/dist/{Hamburger-2QlPY7MK.mjs → Hamburger-DInD_hK4.mjs} +1 -1
  62. package/dist/{Help-BcXc8ez6.mjs → Help-BcQznZaK.mjs} +1 -1
  63. package/dist/{HighFive-UG0NAADf.mjs → HighFive-DqVD1Say.mjs} +1 -1
  64. package/dist/{HighFive-DCjp43gs.mjs → HighFive-kxxxx1a1.mjs} +1 -1
  65. package/dist/{Home-yMgsJvNt.mjs → Home-CE6c4US8.mjs} +1 -1
  66. package/dist/{Home-C5WodEqM.mjs → Home-Dzhzxm5B.mjs} +1 -1
  67. package/dist/{HorizontalLine-JD_RqEgw.mjs → HorizontalLine-Dyh1cfN-.mjs} +1 -1
  68. package/dist/{Hourglass-C3_IVNJa.mjs → Hourglass-CZ_gs0L5.mjs} +1 -1
  69. package/dist/{Image-CFKQTicl.mjs → Image-D8RpGjUr.mjs} +1 -1
  70. package/dist/{ImageFile-DfNJ9qhq.mjs → ImageFile-CpJ6rqlm.mjs} +1 -1
  71. package/dist/{InGoodCompany-6xQLozP6.mjs → InGoodCompany-F_ltthqt.mjs} +1 -1
  72. package/dist/{Incentives-DFFQFZih.mjs → Incentives-CFe-oLpH.mjs} +1 -1
  73. package/dist/{Info-DwqI5ZcJ.mjs → Info-DNU4voqh.mjs} +1 -1
  74. package/dist/{Insights-Cy8kUifj.mjs → Insights-C0x9QzfX.mjs} +1 -1
  75. package/dist/{Insights-Dx0mt_sq.mjs → Insights-D9LemXYM.mjs} +1 -1
  76. package/dist/{Integrations-BYYqLmX_.mjs → Integrations-D4MNE3-i.mjs} +1 -1
  77. package/dist/{KazooLogo-I7BBjIWv.mjs → KazooLogo-DaVNTRlw.mjs} +1 -1
  78. package/dist/{KnowYourAudience-CVAchlPu.mjs → KnowYourAudience-D9qjKO2J.mjs} +1 -1
  79. package/dist/{Launch-Da51LF1k.mjs → Launch-xm296JGJ.mjs} +1 -1
  80. package/dist/{Link-Bi1JqO19.mjs → Link-DUJoL5SM.mjs} +1 -1
  81. package/dist/{LinkArrow-a7xg2uw5.mjs → LinkArrow-C53WYuP9.mjs} +1 -1
  82. package/dist/{Loading-C1i-_OMU.mjs → Loading-YoU3Ro5C.mjs} +1 -1
  83. package/dist/{Lock-CU7TWBei.mjs → Lock-Ig2rvmxz.mjs} +1 -1
  84. package/dist/{Management-DcaxdfRe.mjs → Management-CSk1I3Vu.mjs} +1 -1
  85. package/dist/{MeatballMenu-CXyQkQTY.mjs → MeatballMenu-akus2Lt6.mjs} +1 -1
  86. package/dist/{Messages-BaRHnj7R.mjs → Messages-CdHryWCZ.mjs} +1 -1
  87. package/dist/{Mobile-C82mnkS8.mjs → Mobile-DIs7zF33.mjs} +1 -1
  88. package/dist/{Nomination-Clb3B7rU.mjs → Nomination-DtyERYRo.mjs} +1 -1
  89. package/dist/{NominationsAndAwards-LizaS-lj.mjs → NominationsAndAwards-DUlt_REW.mjs} +1 -1
  90. package/dist/{Notification-C-hvbQgF.mjs → Notification-yHY4Q-iO.mjs} +1 -1
  91. package/dist/{Okrs-Dz7iz6Y9.mjs → Okrs-BVR2trfg.mjs} +1 -1
  92. package/dist/{PeerToPeer-BvKH26DO.mjs → PeerToPeer-Dw7054su.mjs} +1 -1
  93. package/dist/{Pencil-CXJlOW6Z.mjs → Pencil--PMTRUju.mjs} +1 -1
  94. package/dist/{People--NThL2WE.mjs → People-01fveD3Z.mjs} +1 -1
  95. package/dist/{PerformanceManagement-Bb1JvSj_.mjs → PerformanceManagement-BO6JMhw0.mjs} +1 -1
  96. package/dist/{Pin-cuzusAoP.mjs → Pin-DrqWw6Ik.mjs} +1 -1
  97. package/dist/PinnedIconAward-CfI7QvmM.mjs +20 -0
  98. package/dist/PinnedIconBrightIdea-C-jLsEme.mjs +20 -0
  99. package/dist/PinnedIconBullseye-DHzPwFNh.mjs +36 -0
  100. package/dist/PinnedIconChampagne-DqhhML6p.mjs +20 -0
  101. package/dist/PinnedIconCheer-Dr-LynHf.mjs +20 -0
  102. package/dist/PinnedIconCrown-BjZF_Us9.mjs +20 -0
  103. package/dist/PinnedIconDiamond-mR8xcC_8.mjs +20 -0
  104. package/dist/PinnedIconHoleInOne-D0YlpLT_.mjs +20 -0
  105. package/dist/PinnedIconMedal-CsbWDOeO.mjs +20 -0
  106. package/dist/PinnedIconMetrics-FZwXRcdT.mjs +20 -0
  107. package/dist/PinnedIconPin-DQYaYwDa.mjs +20 -0
  108. package/dist/PinnedIconRockAndRoll-2pp2LlIt.mjs +23 -0
  109. package/dist/PinnedIconRocket-BIrfZPOc.mjs +20 -0
  110. package/dist/PinnedIconStrength-B2Jlgh2J.mjs +20 -0
  111. package/dist/PinnedIconTeddyBear-CywM8WU1.mjs +43 -0
  112. package/dist/{PlatformLogo-CK09vVsJ.mjs → PlatformLogo-D2gEkUsw.mjs} +1 -1
  113. package/dist/{Plus-DJaLCs4t.mjs → Plus-hKSiCxQa.mjs} +1 -1
  114. package/dist/{Popper-DTEvAyBx.mjs → Popper-D6_fRDiW.mjs} +1 -1
  115. package/dist/{PrivateEye-WzWeEjb1.mjs → PrivateEye-DOcfeBbQ.mjs} +1 -1
  116. package/dist/{PrivatePushPin-DnNGaLNq.mjs → PrivatePushPin-BORYdpxi.mjs} +1 -1
  117. package/dist/{Processing-BDUBy4UH.mjs → Processing-C_zBVZIK.mjs} +1 -1
  118. package/dist/{Productivity-D5BVYtaN.mjs → Productivity-CLljIpDf.mjs} +1 -1
  119. package/dist/{ProgressCircle-CFTLbp-e.mjs → ProgressCircle-B9baqpgb.mjs} +1 -1
  120. package/dist/{PulseSurvey-B1TvHyDt.mjs → PulseSurvey-BQdfWo9A.mjs} +1 -1
  121. package/dist/{PushPin-CvluYJjM.mjs → PushPin-DkTVqfKk.mjs} +1 -1
  122. package/dist/{Recognition-DmfePy_z.mjs → Recognition-D3KBTtXQ.mjs} +1 -1
  123. package/dist/{Recognition-BDpYBUvN.mjs → Recognition-Dz_nZRDX.mjs} +1 -1
  124. package/dist/{Retention-BkJo5gos.mjs → Retention-ChOlEHZk.mjs} +1 -1
  125. package/dist/{Rewards-CtGT7qtN.mjs → Rewards-CVe8-44F.mjs} +1 -1
  126. package/dist/{Rewards-D_TyBfCv.mjs → Rewards-DU-Ho2dk.mjs} +1 -1
  127. package/dist/{RewardsCatalog-CyMbCiki.mjs → RewardsCatalog-BV1YY9Iq.mjs} +1 -1
  128. package/dist/{Roadmap-djDY2KOo.mjs → Roadmap-zfvbEbqx.mjs} +1 -1
  129. package/dist/{Search-N21SDOKm.mjs → Search-BhJp-JJi.mjs} +1 -1
  130. package/dist/{Security-CExbN6aN.mjs → Security-DRtNtKPc.mjs} +1 -1
  131. package/dist/{SendRecognition-gKLXXcO-.mjs → SendRecognition-Byajzteg.mjs} +1 -1
  132. package/dist/{ShareLink-Bn_3CvlX.mjs → ShareLink-D5S-TmIH.mjs} +1 -1
  133. package/dist/{Skill-BMD-jOQ8.mjs → Skill-CimhEzRw.mjs} +1 -1
  134. package/dist/{Skills-3QOhEpbd.mjs → Skills-DaQLD2Bb.mjs} +1 -1
  135. package/dist/{Smiley-BVpBA18A.mjs → Smiley-BmRWMKju.mjs} +1 -1
  136. package/dist/{Stop-C1787JfZ.mjs → Stop-jEo5XX0_.mjs} +1 -1
  137. package/dist/{Survey-CKqWVVEo.mjs → Survey-D7Lx8jsj.mjs} +1 -1
  138. package/dist/{Surveys-DvOszZdj.mjs → Surveys-Bnwc-nKF.mjs} +1 -1
  139. package/dist/{SyncUps-WBKKUyeT.mjs → SyncUps-DRTsoQ40.mjs} +1 -1
  140. package/dist/{Syncs-C_fskEeo.mjs → Syncs-CND3di67.mjs} +1 -1
  141. package/dist/{TalentReview-Dyg4wS7c.mjs → TalentReview-BCPucb1o.mjs} +1 -1
  142. package/dist/{TalentReview-RkspiqWg.mjs → TalentReview-CnEteZI6.mjs} +1 -1
  143. package/dist/{TalentReviewInsights-BnGxBe_H.mjs → TalentReviewInsights-COIEM4Dx.mjs} +1 -1
  144. package/dist/{TaskCompleted-BzlFAImO.mjs → TaskCompleted-CVVIklMl.mjs} +1 -1
  145. package/dist/{Team-Dxwjirjk.mjs → Team-DG9N4ucC.mjs} +1 -1
  146. package/dist/{Teams-2ntLFtej.mjs → Teams-BksxxA72.mjs} +1 -1
  147. package/dist/{TextBold-O-BU1YXO.mjs → TextBold-JCoCqG2E.mjs} +1 -1
  148. package/dist/{TextBulletList-BqZE7Exr.mjs → TextBulletList-wdvz7YF6.mjs} +1 -1
  149. package/dist/{TextInput-CvxuF1db.mjs → TextInput-BOcHdYfi.mjs} +1 -1
  150. package/dist/{TextItalic-DvGZwAEM.mjs → TextItalic-DS-EVK3i.mjs} +1 -1
  151. package/dist/{TextNumberList-BjEHBnDb.mjs → TextNumberList-DN5rq8Yj.mjs} +1 -1
  152. package/dist/{TextUnderline-BztlEu7E.mjs → TextUnderline-BFQg7hSu.mjs} +1 -1
  153. package/dist/{Toggle-BwrTz2fn.mjs → Toggle-CFvYpFN8.mjs} +1 -1
  154. package/dist/{Trash-6WVc1Zo7.mjs → Trash-CS1BAv8d.mjs} +1 -1
  155. package/dist/{TrendDown-C6wcWlmc.mjs → TrendDown-C8RVuMbz.mjs} +1 -1
  156. package/dist/{TrendUp-BWejhArt.mjs → TrendUp-eYJN1A6n.mjs} +1 -1
  157. package/dist/{Unplugged-0SGS9CKk.mjs → Unplugged-j7jw4iIL.mjs} +1 -1
  158. package/dist/{UpDownCaret-ClBkt-ma.mjs → UpDownCaret-DdXKQT69.mjs} +1 -1
  159. package/dist/{VerticalLine-DjZxhqxX.mjs → VerticalLine-DU1-BZfR.mjs} +1 -1
  160. package/dist/{Warning-Bj_gmfqP.mjs → Warning-C1kQzf8A.mjs} +1 -1
  161. package/dist/{WarningNoOutline-DBvoW0_1.mjs → WarningNoOutline-CXC1MVzS.mjs} +1 -1
  162. package/dist/{YesAnd-COl6p1Q_.mjs → YesAnd-C18ssrgG.mjs} +1 -1
  163. package/dist/ai-assistant.css +645 -0
  164. package/dist/{emoji-BYYHYzOu.mjs → emoji-CJuut-ic.mjs} +1 -88
  165. package/dist/react.es.js +5 -0
  166. package/dist/{ai-assistant.umd.js → react.umd.js} +15685 -1695
  167. package/dist/{reactComponent-DigPLrfy.mjs → reactComponent-B4VA_e0q.mjs} +15712 -1646
  168. package/express.js +2 -2
  169. package/package.json +10 -5
  170. package/react.js +1 -1
  171. package/src/backend/express.test.ts +177 -10
  172. package/src/backend/express.ts +93 -12
  173. package/src/frontend/AiAssistant.test.tsx +1 -1
  174. package/src/frontend/AiAssistant.tsx +9 -2
  175. package/src/frontend/components/AiAssistantComponent.scss +24 -0
  176. package/src/frontend/components/AiAssistantComponent.test.tsx +262 -3
  177. package/src/frontend/components/AiAssistantComponent.tsx +238 -10
  178. package/src/frontend/components/messageRenderers/AssistantMessage.test.tsx +154 -0
  179. package/src/frontend/components/messageRenderers/AssistantMessage.tsx +104 -0
  180. package/src/frontend/components/messageRenderers/UserMessage.test.tsx +103 -0
  181. package/src/frontend/components/messageRenderers/UserMessage.tsx +43 -0
  182. package/src/frontend/reactComponent.ts +2 -0
  183. package/src/frontend/tools/generatedSdks.ts +43 -0
  184. package/src/frontend/tools/llm-tools/getCurrentUser.test.ts +100 -0
  185. package/src/frontend/tools/llm-tools/getCurrentUser.ts +158 -0
  186. package/src/frontend/tools/llm-tools/index.ts +6 -0
  187. package/src/frontend/tools/llm-tools/kazooPlatform/getRewardsAndRecognitionCurrentUser.test.ts +76 -0
  188. package/src/frontend/tools/llm-tools/kazooPlatform/getRewardsAndRecognitionCurrentUser.ts +36 -0
  189. package/src/frontend/tools/llm-tools/pulsePlatform/.gitkeep +0 -0
  190. package/src/frontend/tools/llm-tools/shared/.gitkeep +0 -0
  191. package/src/frontend/tools/useAiAssistantSession.test.ts +768 -0
  192. package/src/frontend/tools/useAiAssistantSession.ts +374 -0
  193. package/types.d.ts +67 -1
  194. package/dist/PinnedIconAward-CWgNNAPU.mjs +0 -20
  195. package/dist/PinnedIconBrightIdea-Cj1UUoKb.mjs +0 -20
  196. package/dist/PinnedIconBullseye-SN7Ps5fC.mjs +0 -20
  197. package/dist/PinnedIconChampagne-CMj1TZw7.mjs +0 -20
  198. package/dist/PinnedIconCheer-osAldHSN.mjs +0 -20
  199. package/dist/PinnedIconCrown-u0dSUhVZ.mjs +0 -20
  200. package/dist/PinnedIconDiamond-Byiswgk3.mjs +0 -20
  201. package/dist/PinnedIconHoleInOne-DzSbNQ5B.mjs +0 -20
  202. package/dist/PinnedIconMedal-YQYBs9V1.mjs +0 -20
  203. package/dist/PinnedIconMetrics-D2Li4Bnf.mjs +0 -20
  204. package/dist/PinnedIconPin-Xxp0coP8.mjs +0 -20
  205. package/dist/PinnedIconRockAndRoll-DlbejnZU.mjs +0 -23
  206. package/dist/PinnedIconRocket-B9dqD0uv.mjs +0 -42
  207. package/dist/PinnedIconStrength-BXA19IfC.mjs +0 -20
  208. package/dist/PinnedIconTeddyBear-D2n83xPZ.mjs +0 -20
  209. package/dist/ai-assistant.es.js +0 -4
@@ -0,0 +1,104 @@
1
+ import React from "react";
2
+
3
+ import { micromark } from "micromark";
4
+
5
+ import {
6
+ Flex,
7
+ Spacer,
8
+ UtilityText,
9
+ Text,
10
+ Pill,
11
+ CardPadding,
12
+ Icon,
13
+ } from "@kazoohr/confetti";
14
+
15
+ import { type ChatItemWithTimestamp } from "../../tools/useAiAssistantSession";
16
+
17
+ export function AssistantMessage({
18
+ message,
19
+ }: {
20
+ message: ChatItemWithTimestamp;
21
+ }) {
22
+ const aggregatedToolCalls = message.parts.filter(
23
+ (part) => part.type === "toolCall"
24
+ );
25
+ const aggregatedTextParts = message.parts
26
+ .filter((part) => part.type === "text")
27
+ .map((text) => text.text)
28
+ .join("");
29
+
30
+ const streamingTextResizingContainerRef = React.useRef<HTMLDivElement>(null);
31
+ const streamingTextRef = React.useRef<HTMLDivElement>(null);
32
+
33
+ const componentsToRender: React.ReactNode[] = [];
34
+
35
+ React.useEffect(() => {
36
+ if (streamingTextRef.current && streamingTextResizingContainerRef.current) {
37
+ const textHeight = streamingTextRef.current.clientHeight;
38
+ streamingTextResizingContainerRef.current.style.height = `${textHeight}px`;
39
+ }
40
+ }, [aggregatedTextParts]);
41
+
42
+ if (aggregatedTextParts) {
43
+ componentsToRender.push(
44
+ <div
45
+ ref={streamingTextResizingContainerRef}
46
+ className="streaming-text-resizing-container"
47
+ key="text"
48
+ >
49
+ <div ref={streamingTextRef}>
50
+ <CardPadding>
51
+ <Flex gap="16px" alignItems="center">
52
+ <Icon icon="teams" />
53
+ <Text bold size="small">
54
+ assistant
55
+ </Text>
56
+ <UtilityText size="extraSmall">
57
+ {message.timestamp?.toLocaleString()}
58
+ </UtilityText>
59
+ </Flex>
60
+ <Spacer />
61
+ <div
62
+ className="markdown"
63
+ dangerouslySetInnerHTML={{
64
+ __html: micromark(aggregatedTextParts),
65
+ }}
66
+ />
67
+ </CardPadding>
68
+ </div>
69
+ </div>
70
+ );
71
+ }
72
+
73
+ if (aggregatedToolCalls.length > 0) {
74
+ componentsToRender.push(
75
+ <div key="tools">
76
+ {aggregatedToolCalls.map((toolCall) => {
77
+ return (
78
+ <CardPadding key={toolCall.toolName}>
79
+ <div
80
+ style={{ textAlign: "right" }}
81
+ title={`Tool call: ${toolCall.toolName}`}
82
+ >
83
+ <div style={{ display: "inline-block" }}>
84
+ <Pill
85
+ icon="info"
86
+ size="large"
87
+ text="Fetch data"
88
+ variant="outline"
89
+ />
90
+ </div>
91
+ </div>
92
+ </CardPadding>
93
+ );
94
+ })}
95
+ </div>
96
+ );
97
+ }
98
+
99
+ if (componentsToRender.length === 0) {
100
+ return null;
101
+ }
102
+
103
+ return <>{componentsToRender}</>;
104
+ }
@@ -0,0 +1,103 @@
1
+ import React from "react";
2
+
3
+ import { render, screen } from "@kazoohr/test";
4
+
5
+ import { type ChatItemWithTimestamp } from "../../tools/useAiAssistantSession";
6
+
7
+ import { UserMessage } from "./UserMessage";
8
+
9
+ jest.mock("micromark", () => ({
10
+ micromark: (text: string) => text,
11
+ }));
12
+
13
+ describe("UserMessage", () => {
14
+ const mockTimestamp = new Date("2024-01-01T12:00:00Z");
15
+
16
+ it("renders text message correctly", () => {
17
+ const message: ChatItemWithTimestamp = {
18
+ role: "user",
19
+ timestamp: mockTimestamp,
20
+ parts: [
21
+ {
22
+ type: "text",
23
+ text: "Hello world",
24
+ },
25
+ ],
26
+ };
27
+
28
+ render(<UserMessage message={message} />);
29
+
30
+ expect(screen.getByText("you")).toBeInTheDocument();
31
+ expect(screen.getByText("Hello world")).toBeInTheDocument();
32
+ expect(
33
+ screen.getByText(mockTimestamp.toLocaleString())
34
+ ).toBeInTheDocument();
35
+ });
36
+
37
+ it("renders markdown text correctly", () => {
38
+ const message: ChatItemWithTimestamp = {
39
+ role: "user",
40
+ timestamp: mockTimestamp,
41
+ parts: [
42
+ {
43
+ type: "text",
44
+ text: "# Hello\n\n**Bold text**",
45
+ },
46
+ ],
47
+ };
48
+
49
+ render(<UserMessage message={message} />);
50
+
51
+ const content = document.querySelector(".markdown");
52
+ expect(content).toBeInTheDocument();
53
+ expect(content?.innerHTML).toContain("# Hello\n\n**Bold text**");
54
+ });
55
+
56
+ it("returns null for empty message parts", () => {
57
+ const message: ChatItemWithTimestamp = {
58
+ role: "user",
59
+ timestamp: mockTimestamp,
60
+ parts: [],
61
+ };
62
+
63
+ const { container } = render(<UserMessage message={message} />);
64
+ expect(container.firstChild).toBeNull();
65
+ });
66
+
67
+ it("aggregates multiple text parts", () => {
68
+ const message: ChatItemWithTimestamp = {
69
+ role: "user",
70
+ timestamp: mockTimestamp,
71
+ parts: [
72
+ {
73
+ type: "text",
74
+ text: "Hello",
75
+ },
76
+ {
77
+ type: "text",
78
+ text: " world",
79
+ },
80
+ ],
81
+ };
82
+
83
+ render(<UserMessage message={message} />);
84
+ expect(screen.getByText("Hello world")).toBeInTheDocument();
85
+ });
86
+
87
+ it("ignores non-text parts", () => {
88
+ const message: ChatItemWithTimestamp = {
89
+ role: "user",
90
+ timestamp: mockTimestamp,
91
+ parts: [
92
+ {
93
+ type: "toolCall",
94
+ toolName: "testTool",
95
+ args: { test: true },
96
+ },
97
+ ],
98
+ };
99
+
100
+ const { container } = render(<UserMessage message={message} />);
101
+ expect(container.firstChild).toBeNull();
102
+ });
103
+ });
@@ -0,0 +1,43 @@
1
+ import React from "react";
2
+
3
+ import { micromark } from "micromark";
4
+
5
+ import { Card, Flex, Spacer, UtilityText, Text, Icon } from "@kazoohr/confetti";
6
+
7
+ import { ChatItemWithTimestamp } from "../../tools/useAiAssistantSession";
8
+
9
+ export function UserMessage({ message }: { message: ChatItemWithTimestamp }) {
10
+ const aggregatedTextParts = message.parts
11
+ .filter((part) => part.type === "text")
12
+ .map((text) => text.text)
13
+ .join("");
14
+
15
+ const componentsToRender: React.ReactNode[] = [];
16
+
17
+ if (aggregatedTextParts) {
18
+ componentsToRender.push(
19
+ <Card key="text" noShadow withBorder>
20
+ <Flex gap="16px" alignItems="center">
21
+ <Icon icon="feedback" />
22
+ <Text bold size="small">
23
+ you
24
+ </Text>
25
+ <UtilityText size="extraSmall">
26
+ {message.timestamp?.toLocaleString()}
27
+ </UtilityText>
28
+ </Flex>
29
+ <Spacer />
30
+ <div
31
+ className="markdown"
32
+ dangerouslySetInnerHTML={{ __html: micromark(aggregatedTextParts) }}
33
+ />
34
+ </Card>
35
+ );
36
+ }
37
+
38
+ if (componentsToRender.length === 0) {
39
+ return null;
40
+ }
41
+
42
+ return <>{componentsToRender}</>;
43
+ }
@@ -1,3 +1,5 @@
1
1
  // istanbul ignore file - Main package entrypoint
2
2
 
3
3
  export { AiAssistant } from "./AiAssistant";
4
+
5
+ export * as tools from "./tools/llm-tools";
@@ -0,0 +1,43 @@
1
+ // istanbul ignore file - light wrapper around the generated SDK
2
+
3
+ import { DocumentNode, print } from "@kazoohr/graphql";
4
+
5
+ import { getSdk as getStitchedSdk } from "../../../generated/stitchedSdk";
6
+
7
+ export const stitchedSdk = {
8
+ ...getStitchedSdk((query, variables) => {
9
+ return fetcher({
10
+ url: "/graphql",
11
+ query,
12
+ variables,
13
+ });
14
+ }),
15
+ };
16
+
17
+ export async function fetcher({
18
+ query,
19
+ url,
20
+ variables,
21
+ }: {
22
+ url: string;
23
+ query: DocumentNode;
24
+ variables?: any;
25
+ }): Promise<any> {
26
+ const response = await fetch(url, {
27
+ method: "POST",
28
+ headers: {
29
+ "Content-Type": "application/json",
30
+ Accept: "application/graphql-response+json",
31
+ },
32
+ body: JSON.stringify({
33
+ query: print(query),
34
+ variables,
35
+ }),
36
+ });
37
+
38
+ if (!response.ok) {
39
+ throw new Error("Network response returned non-OK status");
40
+ }
41
+
42
+ return await response.json();
43
+ }
@@ -0,0 +1,100 @@
1
+ import { getCurrentUserTool, resetCurrentUser } from "./getCurrentUser";
2
+
3
+ const mockFetch = jest.fn();
4
+ global.fetch = mockFetch;
5
+
6
+ const mockConsoleLog = jest.fn();
7
+ global.console.log = mockConsoleLog;
8
+
9
+ describe("getCurrentUserTool", () => {
10
+ beforeEach(() => {
11
+ jest.clearAllMocks();
12
+ resetCurrentUser();
13
+ });
14
+
15
+ it("fetches and caches user data", async () => {
16
+ const mockUserData = {
17
+ data: {
18
+ directory_me: {
19
+ id: "123",
20
+ name: "Test User",
21
+ },
22
+ },
23
+ };
24
+
25
+ mockFetch.mockResolvedValueOnce({
26
+ json: () => Promise.resolve(mockUserData),
27
+ });
28
+
29
+ // First call should make the fetch request
30
+ const result1 = await getCurrentUserTool.fulfill({});
31
+ expect(result1).toBe(JSON.stringify(mockUserData));
32
+ expect(mockFetch).toHaveBeenCalledTimes(1);
33
+ expect(mockFetch).toHaveBeenCalledWith("/graphql", {
34
+ method: "POST",
35
+ credentials: "include",
36
+ headers: {
37
+ "Content-Type": "application/json",
38
+ },
39
+ body: expect.any(String),
40
+ });
41
+
42
+ // Second call should use cached value
43
+ const result2 = await getCurrentUserTool.fulfill({});
44
+ expect(result2).toBe(JSON.stringify(mockUserData));
45
+ expect(mockFetch).toHaveBeenCalledTimes(1); // Should not make another fetch
46
+ });
47
+
48
+ it("handles fetch errors", async () => {
49
+ const errorMessage = "Network error";
50
+ const error = new Error(errorMessage);
51
+ mockFetch.mockRejectedValueOnce(error);
52
+
53
+ const result = await getCurrentUserTool.fulfill({});
54
+ expect(result).toBe(JSON.stringify({ error: errorMessage }));
55
+ expect(mockFetch).toHaveBeenCalledTimes(1);
56
+ expect(mockConsoleLog).not.toHaveBeenCalled();
57
+
58
+ // Next call should try fetching again since error cleared the cache
59
+ const mockUserData = { data: { directory_me: { id: "123" } } };
60
+ mockFetch.mockResolvedValueOnce({
61
+ json: () => Promise.resolve(mockUserData),
62
+ });
63
+ const result2 = await getCurrentUserTool.fulfill({});
64
+ expect(result2).toBe(JSON.stringify(mockUserData));
65
+ expect(mockFetch).toHaveBeenCalledTimes(2);
66
+ });
67
+
68
+ it("handles non-Error fetch rejections", async () => {
69
+ mockFetch.mockRejectedValueOnce("Non-error rejection");
70
+
71
+ const result = await getCurrentUserTool.fulfill({});
72
+ expect(result).toBe(JSON.stringify({ error: "Non-error rejection" }));
73
+ expect(mockFetch).toHaveBeenCalledTimes(1);
74
+ });
75
+
76
+ it("handles JSON parse errors", async () => {
77
+ mockFetch.mockResolvedValueOnce({
78
+ json: () => Promise.reject(new Error("Invalid JSON")),
79
+ });
80
+
81
+ const result = await getCurrentUserTool.fulfill({});
82
+ expect(result).toBe(JSON.stringify({ error: "Invalid JSON" }));
83
+ expect(mockFetch).toHaveBeenCalledTimes(1);
84
+ });
85
+
86
+ it("handles non-Error JSON parse errors", async () => {
87
+ mockFetch.mockResolvedValueOnce({
88
+ json: () => Promise.reject("Invalid JSON string"),
89
+ });
90
+
91
+ const result = await getCurrentUserTool.fulfill({});
92
+ expect(result).toBe(JSON.stringify({ error: "Invalid JSON string" }));
93
+ expect(mockFetch).toHaveBeenCalledTimes(1);
94
+ });
95
+
96
+ it("has correct tool metadata", () => {
97
+ expect(getCurrentUserTool.name).toBe("getCurrentUser");
98
+ expect(getCurrentUserTool.description).toBe("Get the current user");
99
+ });
100
+ });
@@ -0,0 +1,158 @@
1
+ import { handleForegroundPromise } from "@kazoohr/helpers";
2
+ import { createTool } from "@kazoohr/llm/createTool";
3
+
4
+ export let currentUser: Promise<string> | undefined = undefined;
5
+
6
+ export function resetCurrentUser() {
7
+ currentUser = undefined;
8
+ }
9
+
10
+ export const getCurrentUserTool = createTool({
11
+ name: "getCurrentUser",
12
+ description: "Get the current user",
13
+ fulfill: async function fulfillGetCurrentUser() {
14
+ if (currentUser) {
15
+ return await currentUser;
16
+ }
17
+ currentUser = new Promise((resolve) => {
18
+ handleForegroundPromise(async (): Promise<unknown> => {
19
+ try {
20
+ // First, introspect `/api` endpoint to get the schema
21
+ const response = await fetch("/graphql", {
22
+ method: "POST",
23
+ credentials: "include",
24
+ headers: {
25
+ "Content-Type": "application/json",
26
+ },
27
+ body: JSON.stringify({
28
+ query: currentUserQuery,
29
+ }),
30
+ }).catch((error: Error | string) => error);
31
+ if (response instanceof Error || typeof response === "string") {
32
+ currentUser = undefined;
33
+ resolve(
34
+ JSON.stringify({
35
+ error: response instanceof Error ? response.message : response,
36
+ })
37
+ );
38
+ return;
39
+ }
40
+ const schema = await response.json();
41
+ resolve(JSON.stringify(schema));
42
+ } catch (error) {
43
+ currentUser = undefined;
44
+ resolve(
45
+ JSON.stringify({
46
+ error: error instanceof Error ? error.message : String(error),
47
+ })
48
+ );
49
+ }
50
+ });
51
+ });
52
+ return currentUser;
53
+ },
54
+ });
55
+
56
+ const currentUserQuery = `
57
+ query GetCurrentUser {
58
+ directory_me {
59
+ avatar
60
+ adminOf {
61
+ departments {
62
+ id
63
+ name
64
+ }
65
+ locations {
66
+ id
67
+ name
68
+ }
69
+ groups {
70
+ id
71
+ name
72
+ }
73
+ }
74
+ birthday {
75
+ month
76
+ day
77
+ }
78
+ companyInfo {
79
+ customEula
80
+ customAcceptTermsMessage
81
+ name
82
+ allowUsersToShareInfo
83
+ allowProfilePictureUploads
84
+ kwProfileSettings {
85
+ canShareBirthday
86
+ canShareAnniversary
87
+ canShareBio
88
+ canChangeAvatar
89
+ canChangePreferredName
90
+ }
91
+ timezone
92
+ }
93
+ location
94
+ department
95
+ departmentInfo {
96
+ id
97
+ name
98
+ }
99
+ firstName
100
+ preferredName
101
+ id
102
+ ids {
103
+ kazooId
104
+ rrId
105
+ yeiImpersonatingUserId
106
+ yeiCompanyId
107
+ }
108
+ email
109
+ image30
110
+ image70
111
+ image160
112
+ avatar
113
+ initials
114
+ title
115
+ jobTitle: title
116
+ name
117
+ roles
118
+ isManager
119
+ startDate
120
+ groupFeatureFlags {
121
+ performance {
122
+ adhocGoal
123
+ brandedEmail
124
+ enablePerform
125
+ goalLibrary
126
+ syncUp
127
+ continuousFeedback
128
+ feedback360
129
+ requestFeedback
130
+ giveFeedback
131
+ recognition2
132
+ enableFeedback
133
+ enableGoals
134
+ enablePoints
135
+ enableLanguages
136
+ enableLocation
137
+ enablePoints
138
+ enablePolling
139
+ feedbackCheckin
140
+ }
141
+ recognition
142
+ }
143
+ permissionsInGroup {
144
+ performance
145
+ }
146
+ points {
147
+ givingBalance
148
+ redemptionBalance
149
+ }
150
+ showTermsConditionsModal
151
+ status
152
+ following
153
+ enabledModules
154
+ source
155
+ lastName
156
+ secondaryManagerOf
157
+ }
158
+ }`;
@@ -0,0 +1,6 @@
1
+ // istanbul ignore file - barrel
2
+ import { getRewardsAndRecognitionCurrentUserTool } from "./kazooPlatform/getRewardsAndRecognitionCurrentUser";
3
+
4
+ export const kazooTools = {
5
+ getRewardsAndRecognitionCurrentUserTool,
6
+ };
@@ -0,0 +1,76 @@
1
+ import { stitchedSdk } from "../../generatedSdks";
2
+
3
+ import {
4
+ getRewardsAndRecognitionCurrentUserTool,
5
+ resetCurrentUser,
6
+ } from "./getRewardsAndRecognitionCurrentUser";
7
+
8
+ jest.mock("../../generatedSdks", () => ({
9
+ stitchedSdk: {
10
+ GetCurrentUser: jest.fn(),
11
+ },
12
+ }));
13
+
14
+ const mockGetCurrentUser = stitchedSdk.GetCurrentUser as jest.Mock;
15
+
16
+ describe("getRewardsAndRecognitionCurrentUserTool", () => {
17
+ beforeEach(() => {
18
+ jest.clearAllMocks();
19
+ resetCurrentUser();
20
+ });
21
+
22
+ it("fetches and caches user data", async () => {
23
+ const mockUserData = {
24
+ data: {
25
+ directory_me: {
26
+ id: "123",
27
+ name: "Test User",
28
+ },
29
+ },
30
+ };
31
+
32
+ mockGetCurrentUser.mockResolvedValueOnce(mockUserData);
33
+
34
+ // First call should make the execute request
35
+ const result1 = await getRewardsAndRecognitionCurrentUserTool.fulfill({});
36
+ expect(result1).toBe(JSON.stringify(mockUserData));
37
+ expect(mockGetCurrentUser).toHaveBeenCalledTimes(1);
38
+
39
+ // Second call should use cached value
40
+ const result2 = await getRewardsAndRecognitionCurrentUserTool.fulfill({});
41
+ expect(result2).toBe(JSON.stringify(mockUserData));
42
+ expect(mockGetCurrentUser).toHaveBeenCalledTimes(1); // Should not make another execute call
43
+ });
44
+
45
+ it("handles execute errors", async () => {
46
+ const errorMessage = "Network error";
47
+ const error = new Error(errorMessage);
48
+ mockGetCurrentUser.mockRejectedValueOnce(error);
49
+
50
+ const result = await getRewardsAndRecognitionCurrentUserTool.fulfill({});
51
+ expect(result).toBe(JSON.stringify({ error: errorMessage }));
52
+ expect(mockGetCurrentUser).toHaveBeenCalledTimes(1);
53
+
54
+ // Next call should try executing again since error cleared the cache
55
+ const mockUserData = { data: { directory_me: { id: "123" } } };
56
+ mockGetCurrentUser.mockResolvedValueOnce(mockUserData);
57
+ const result2 = await getRewardsAndRecognitionCurrentUserTool.fulfill({});
58
+ expect(result2).toBe(JSON.stringify(mockUserData));
59
+ expect(mockGetCurrentUser).toHaveBeenCalledTimes(2);
60
+ });
61
+
62
+ it("handles non-Error execute rejections", async () => {
63
+ mockGetCurrentUser.mockRejectedValueOnce(new Error("Non-error rejection"));
64
+
65
+ const result = await getRewardsAndRecognitionCurrentUserTool.fulfill({});
66
+ expect(result).toBe(JSON.stringify({ error: "Non-error rejection" }));
67
+ expect(mockGetCurrentUser).toHaveBeenCalledTimes(1);
68
+ });
69
+
70
+ it("has correct tool metadata", () => {
71
+ expect(getRewardsAndRecognitionCurrentUserTool.name).toBe("getCurrentUser");
72
+ expect(getRewardsAndRecognitionCurrentUserTool.description).toContain(
73
+ "Get the current user"
74
+ );
75
+ });
76
+ });
@@ -0,0 +1,36 @@
1
+ import { handleForegroundPromise } from "@kazoohr/helpers";
2
+ import { createTool } from "@kazoohr/llm/createTool";
3
+
4
+ import { stitchedSdk } from "../../generatedSdks";
5
+
6
+ export let currentUser: Promise<string> | undefined = undefined;
7
+
8
+ export function resetCurrentUser() {
9
+ currentUser = undefined;
10
+ }
11
+
12
+ export const getRewardsAndRecognitionCurrentUserTool = createTool({
13
+ name: "getCurrentUser",
14
+ description: "Get the current user from the Rewards and Recognition API",
15
+ fulfill: async function fulfillGetCurrentUser() {
16
+ if (currentUser) {
17
+ return await currentUser;
18
+ }
19
+ currentUser = new Promise((resolve) => {
20
+ handleForegroundPromise(async () => {
21
+ try {
22
+ const response = await stitchedSdk.GetCurrentUser();
23
+ resolve(JSON.stringify(response));
24
+ } catch (error) {
25
+ currentUser = undefined;
26
+ resolve(
27
+ JSON.stringify({
28
+ error: error?.message,
29
+ })
30
+ );
31
+ }
32
+ });
33
+ });
34
+ return currentUser;
35
+ },
36
+ });
File without changes