@agent-native/toolkit 0.228.0-nightly-20261111212500 → 0.228.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 (361) hide show
  1. package/agent-native.eject.json +2 -0
  2. package/dist/app/agentkit/react/approval-card.d.ts.map +1 -1
  3. package/dist/app/agentkit/react/approval-card.js +5 -3
  4. package/dist/app/agentkit/react/approval-card.js.map +1 -1
  5. package/dist/app/agentkit/react/code-block.d.ts +32 -0
  6. package/dist/app/agentkit/react/code-block.d.ts.map +1 -0
  7. package/dist/app/agentkit/react/code-block.js +125 -0
  8. package/dist/app/agentkit/react/code-block.js.map +1 -0
  9. package/dist/app/agentkit/react/components.d.ts +33 -14
  10. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  11. package/dist/app/agentkit/react/components.js +843 -168
  12. package/dist/app/agentkit/react/components.js.map +1 -1
  13. package/dist/app/agentkit/react/context-usage.d.ts +45 -0
  14. package/dist/app/agentkit/react/context-usage.d.ts.map +1 -0
  15. package/dist/app/agentkit/react/context-usage.js +93 -0
  16. package/dist/app/agentkit/react/context-usage.js.map +1 -0
  17. package/dist/app/agentkit/react/context.d.ts +42 -3
  18. package/dist/app/agentkit/react/context.d.ts.map +1 -1
  19. package/dist/app/agentkit/react/context.js +46 -1
  20. package/dist/app/agentkit/react/context.js.map +1 -1
  21. package/dist/app/agentkit/react/disclosure-content.d.ts +12 -0
  22. package/dist/app/agentkit/react/disclosure-content.d.ts.map +1 -0
  23. package/dist/app/agentkit/react/disclosure-content.js +33 -0
  24. package/dist/app/agentkit/react/disclosure-content.js.map +1 -0
  25. package/dist/app/agentkit/react/duration.d.ts +6 -0
  26. package/dist/app/agentkit/react/duration.d.ts.map +1 -0
  27. package/dist/app/agentkit/react/duration.js +21 -0
  28. package/dist/app/agentkit/react/duration.js.map +1 -0
  29. package/dist/app/agentkit/react/file-change.d.ts +33 -0
  30. package/dist/app/agentkit/react/file-change.d.ts.map +1 -0
  31. package/dist/app/agentkit/react/file-change.js +83 -0
  32. package/dist/app/agentkit/react/file-change.js.map +1 -0
  33. package/dist/app/agentkit/react/file-diff.d.ts +17 -0
  34. package/dist/app/agentkit/react/file-diff.d.ts.map +1 -0
  35. package/dist/app/agentkit/react/file-diff.js +94 -0
  36. package/dist/app/agentkit/react/file-diff.js.map +1 -0
  37. package/dist/app/agentkit/react/index.d.ts +7 -0
  38. package/dist/app/agentkit/react/index.d.ts.map +1 -1
  39. package/dist/app/agentkit/react/index.js +7 -0
  40. package/dist/app/agentkit/react/index.js.map +1 -1
  41. package/dist/app/agentkit/react/question-card.d.ts +67 -0
  42. package/dist/app/agentkit/react/question-card.d.ts.map +1 -0
  43. package/dist/app/agentkit/react/question-card.js +198 -0
  44. package/dist/app/agentkit/react/question-card.js.map +1 -0
  45. package/dist/app/agentkit/react/root.d.ts +3 -2
  46. package/dist/app/agentkit/react/root.d.ts.map +1 -1
  47. package/dist/app/agentkit/react/root.js +2 -2
  48. package/dist/app/agentkit/react/root.js.map +1 -1
  49. package/dist/app/agentkit/react/roving-focus.d.ts +4 -0
  50. package/dist/app/agentkit/react/roving-focus.d.ts.map +1 -0
  51. package/dist/app/agentkit/react/roving-focus.js +75 -0
  52. package/dist/app/agentkit/react/roving-focus.js.map +1 -0
  53. package/dist/app/agentkit/react/stack-trace.d.ts +34 -0
  54. package/dist/app/agentkit/react/stack-trace.d.ts.map +1 -0
  55. package/dist/app/agentkit/react/stack-trace.js +121 -0
  56. package/dist/app/agentkit/react/stack-trace.js.map +1 -0
  57. package/dist/app/agentkit/react/stop-button.d.ts.map +1 -1
  58. package/dist/app/agentkit/react/stop-button.js +22 -6
  59. package/dist/app/agentkit/react/stop-button.js.map +1 -1
  60. package/dist/app/agentkit/react/styles.css +1306 -395
  61. package/dist/app/agentkit/react/terminal.d.ts +28 -0
  62. package/dist/app/agentkit/react/terminal.d.ts.map +1 -0
  63. package/dist/app/agentkit/react/terminal.js +84 -0
  64. package/dist/app/agentkit/react/terminal.js.map +1 -0
  65. package/dist/app/agentkit/react/trace.d.ts +31 -0
  66. package/dist/app/agentkit/react/trace.d.ts.map +1 -0
  67. package/dist/app/agentkit/react/trace.js +73 -0
  68. package/dist/app/agentkit/react/trace.js.map +1 -0
  69. package/dist/app/blocks/library/HighlightedCode.d.ts +2 -1
  70. package/dist/app/blocks/library/HighlightedCode.d.ts.map +1 -1
  71. package/dist/app/blocks/library/HighlightedCode.js +8 -111
  72. package/dist/app/blocks/library/HighlightedCode.js.map +1 -1
  73. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  74. package/dist/app/chat/AgentKitAssistantChat.js +23 -245
  75. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  76. package/dist/app/chat/agentkit-chat/guided-questions.d.ts +15 -1
  77. package/dist/app/chat/agentkit-chat/guided-questions.d.ts.map +1 -1
  78. package/dist/app/chat/agentkit-chat/guided-questions.js +142 -2
  79. package/dist/app/chat/agentkit-chat/guided-questions.js.map +1 -1
  80. package/dist/app/chat/agentkit-chat/history.js +1 -1
  81. package/dist/app/chat/agentkit-chat/history.js.map +1 -1
  82. package/dist/app/chat/agentkit-chat/index.d.ts +1 -1
  83. package/dist/app/chat/agentkit-chat/index.d.ts.map +1 -1
  84. package/dist/app/chat/agentkit-chat/index.js +1 -1
  85. package/dist/app/chat/agentkit-chat/index.js.map +1 -1
  86. package/dist/app/chat/agentkit-chat/labels.d.ts +5 -0
  87. package/dist/app/chat/agentkit-chat/labels.d.ts.map +1 -0
  88. package/dist/app/chat/agentkit-chat/labels.js +245 -0
  89. package/dist/app/chat/agentkit-chat/labels.js.map +1 -0
  90. package/dist/app/chat/agentkit-chat/questions.d.ts +1 -1
  91. package/dist/app/chat/agentkit-chat/questions.d.ts.map +1 -1
  92. package/dist/app/chat/agentkit-chat/questions.js +1 -1
  93. package/dist/app/chat/agentkit-chat/questions.js.map +1 -1
  94. package/dist/app/chat/agentkit-chat/root.d.ts.map +1 -1
  95. package/dist/app/chat/agentkit-chat/root.js +5 -64
  96. package/dist/app/chat/agentkit-chat/root.js.map +1 -1
  97. package/dist/app/chat/agentkit-chat/work-step-labels.d.ts +2 -0
  98. package/dist/app/chat/agentkit-chat/work-step-labels.d.ts.map +1 -1
  99. package/dist/app/chat/agentkit-chat/work-step-labels.js +31 -0
  100. package/dist/app/chat/agentkit-chat/work-step-labels.js.map +1 -1
  101. package/dist/app/chat/chat/markdown-renderer.d.ts +2 -12
  102. package/dist/app/chat/chat/markdown-renderer.d.ts.map +1 -1
  103. package/dist/app/chat/chat/markdown-renderer.js +2 -41
  104. package/dist/app/chat/chat/markdown-renderer.js.map +1 -1
  105. package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
  106. package/dist/app/chat/chat/tool-call-display.js +2 -2
  107. package/dist/app/chat/chat/tool-call-display.js.map +1 -1
  108. package/dist/app/chat/conversation/AgentConversation.d.ts.map +1 -1
  109. package/dist/app/chat/conversation/AgentConversation.js +2 -37
  110. package/dist/app/chat/conversation/AgentConversation.js.map +1 -1
  111. package/dist/app/chat/tool-cells/BashCell.d.ts +3 -1
  112. package/dist/app/chat/tool-cells/BashCell.d.ts.map +1 -1
  113. package/dist/app/chat/tool-cells/BashCell.js +4 -40
  114. package/dist/app/chat/tool-cells/BashCell.js.map +1 -1
  115. package/dist/app/chat/tool-cells/EditCell.d.ts.map +1 -1
  116. package/dist/app/chat/tool-cells/EditCell.js +5 -108
  117. package/dist/app/chat/tool-cells/EditCell.js.map +1 -1
  118. package/dist/app/chat/tool-cells/FilesChangedSummary.d.ts.map +1 -1
  119. package/dist/app/chat/tool-cells/FilesChangedSummary.js +23 -81
  120. package/dist/app/chat/tool-cells/FilesChangedSummary.js.map +1 -1
  121. package/dist/app/chat/tool-cells/WriteCell.d.ts.map +1 -1
  122. package/dist/app/chat/tool-cells/WriteCell.js +5 -24
  123. package/dist/app/chat/tool-cells/WriteCell.js.map +1 -1
  124. package/dist/app/code-highlighter.d.ts +11 -0
  125. package/dist/app/code-highlighter.d.ts.map +1 -0
  126. package/dist/app/code-highlighter.js +122 -0
  127. package/dist/app/code-highlighter.js.map +1 -0
  128. package/dist/app/context-xray/ThreadContextRing.js +3 -3
  129. package/dist/app/context-xray/ThreadContextRing.js.map +1 -1
  130. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  131. package/dist/app/i18n/catalogs/ar-SA.js +22 -0
  132. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  133. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  134. package/dist/app/i18n/catalogs/de-DE.js +22 -0
  135. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  136. package/dist/app/i18n/catalogs/en-US.d.ts +22 -0
  137. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  138. package/dist/app/i18n/catalogs/en-US.js +22 -0
  139. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  140. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  141. package/dist/app/i18n/catalogs/es-ES.js +22 -0
  142. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  143. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  144. package/dist/app/i18n/catalogs/fr-FR.js +22 -0
  145. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  146. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  147. package/dist/app/i18n/catalogs/hi-IN.js +22 -0
  148. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  149. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  150. package/dist/app/i18n/catalogs/ja-JP.js +22 -0
  151. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  152. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  153. package/dist/app/i18n/catalogs/ko-KR.js +22 -0
  154. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  155. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  156. package/dist/app/i18n/catalogs/pt-BR.js +22 -0
  157. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  158. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  159. package/dist/app/i18n/catalogs/zh-CN.js +22 -0
  160. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  161. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  162. package/dist/app/i18n/catalogs/zh-TW.js +22 -0
  163. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  164. package/dist/app/onboarding/first-run-first-paint.d.ts +10 -0
  165. package/dist/app/onboarding/first-run-first-paint.d.ts.map +1 -0
  166. package/dist/app/onboarding/first-run-first-paint.js +22 -0
  167. package/dist/app/onboarding/first-run-first-paint.js.map +1 -0
  168. package/dist/app/onboarding/first-run-startup-gate.d.ts +3 -1
  169. package/dist/app/onboarding/first-run-startup-gate.d.ts.map +1 -1
  170. package/dist/app/onboarding/first-run-startup-gate.js +30 -6
  171. package/dist/app/onboarding/first-run-startup-gate.js.map +1 -1
  172. package/dist/app/providers/AppProviders.d.ts.map +1 -1
  173. package/dist/app/providers/AppProviders.js +9 -2
  174. package/dist/app/providers/AppProviders.js.map +1 -1
  175. package/dist/app/providers/BuilderSignupActivation.d.ts +2 -1
  176. package/dist/app/providers/BuilderSignupActivation.d.ts.map +1 -1
  177. package/dist/app/providers/BuilderSignupActivation.js +6 -3
  178. package/dist/app/providers/BuilderSignupActivation.js.map +1 -1
  179. package/dist/app/shared/AppShellSkeleton.d.ts.map +1 -1
  180. package/dist/app/shared/AppShellSkeleton.js +11 -1
  181. package/dist/app/shared/AppShellSkeleton.js.map +1 -1
  182. package/dist/app/styles/agent-native.css +2 -2
  183. package/dist/app/styles/tokens/agent-kit.css +20 -3
  184. package/dist/composer/AgentSuggestionBar.d.ts.map +1 -1
  185. package/dist/composer/AgentSuggestionBar.js +2 -2
  186. package/dist/composer/AgentSuggestionBar.js.map +1 -1
  187. package/dist/composer/ComposerContextMenu.js +4 -4
  188. package/dist/composer/ComposerContextMenu.js.map +1 -1
  189. package/dist/composer/ComposerContextPicker.d.ts.map +1 -1
  190. package/dist/composer/ComposerContextPicker.js +2 -2
  191. package/dist/composer/ComposerContextPicker.js.map +1 -1
  192. package/dist/composer/ComposerContextPickerDialog.d.ts.map +1 -1
  193. package/dist/composer/ComposerContextPickerDialog.js +2 -2
  194. package/dist/composer/ComposerContextPickerDialog.js.map +1 -1
  195. package/dist/composer/ComposerModePicker.d.ts.map +1 -1
  196. package/dist/composer/ComposerModePicker.js +2 -1
  197. package/dist/composer/ComposerModePicker.js.map +1 -1
  198. package/dist/composer/ComposerPlusMenu.js +1 -1
  199. package/dist/composer/ComposerPlusMenu.js.map +1 -1
  200. package/dist/composer/MentionChip.js +1 -1
  201. package/dist/composer/MentionChip.js.map +1 -1
  202. package/dist/composer/MentionPopover.d.ts +3 -0
  203. package/dist/composer/MentionPopover.d.ts.map +1 -1
  204. package/dist/composer/MentionPopover.js +34 -15
  205. package/dist/composer/MentionPopover.js.map +1 -1
  206. package/dist/composer/MessageQueueDrawer.d.ts.map +1 -1
  207. package/dist/composer/MessageQueueDrawer.js +14 -9
  208. package/dist/composer/MessageQueueDrawer.js.map +1 -1
  209. package/dist/composer/PastedTextChip.d.ts.map +1 -1
  210. package/dist/composer/PastedTextChip.js +14 -18
  211. package/dist/composer/PastedTextChip.js.map +1 -1
  212. package/dist/composer/PromptBar.js +1 -1
  213. package/dist/composer/PromptBar.js.map +1 -1
  214. package/dist/composer/PromptComposer.d.ts.map +1 -1
  215. package/dist/composer/PromptComposer.js +11 -9
  216. package/dist/composer/PromptComposer.js.map +1 -1
  217. package/dist/composer/RealtimeVoiceMode.js +2 -2
  218. package/dist/composer/RealtimeVoiceMode.js.map +1 -1
  219. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  220. package/dist/composer/TiptapComposer.js +52 -89
  221. package/dist/composer/TiptapComposer.js.map +1 -1
  222. package/dist/composer/VoiceButton.d.ts +3 -1
  223. package/dist/composer/VoiceButton.d.ts.map +1 -1
  224. package/dist/composer/VoiceButton.js +10 -8
  225. package/dist/composer/VoiceButton.js.map +1 -1
  226. package/dist/composer/composer-notice.js +3 -3
  227. package/dist/composer/composer-notice.js.map +1 -1
  228. package/dist/composer/extensions/FileReference.js +1 -1
  229. package/dist/composer/extensions/FileReference.js.map +1 -1
  230. package/dist/composer/extensions/SkillReference.js +1 -1
  231. package/dist/composer/extensions/SkillReference.js.map +1 -1
  232. package/dist/composer/popover-surface.d.ts +1 -1
  233. package/dist/composer/popover-surface.d.ts.map +1 -1
  234. package/dist/composer/popover-surface.js +1 -1
  235. package/dist/composer/popover-surface.js.map +1 -1
  236. package/dist/context-ui/ContextUsageRing.d.ts +1 -45
  237. package/dist/context-ui/ContextUsageRing.d.ts.map +1 -1
  238. package/dist/context-ui/ContextUsageRing.js +2 -87
  239. package/dist/context-ui/ContextUsageRing.js.map +1 -1
  240. package/dist/context-ui/index.d.ts +2 -2
  241. package/dist/context-ui/index.d.ts.map +1 -1
  242. package/dist/context-ui/index.js +2 -2
  243. package/dist/context-ui/index.js.map +1 -1
  244. package/package.json +4 -4
  245. package/src/app/agentkit/react/approval-card.tsx +14 -2
  246. package/src/app/agentkit/react/code-block.spec.tsx +154 -0
  247. package/src/app/agentkit/react/code-block.tsx +317 -0
  248. package/src/app/agentkit/react/components.interaction.spec.tsx +7 -0
  249. package/src/app/agentkit/react/components.papercuts.spec.tsx +143 -2
  250. package/src/app/agentkit/react/components.spec.tsx +40 -47
  251. package/src/app/agentkit/react/components.tsx +1533 -413
  252. package/src/app/agentkit/react/composer-context.spec.tsx +35 -0
  253. package/src/app/agentkit/react/context-usage.tsx +353 -0
  254. package/src/app/agentkit/react/context.tsx +93 -1
  255. package/src/app/agentkit/react/disclosure-content.ts +42 -0
  256. package/src/app/agentkit/react/disclosures.spec.tsx +62 -4
  257. package/src/app/agentkit/react/duration.ts +22 -0
  258. package/src/app/agentkit/react/file-change.spec.tsx +124 -0
  259. package/src/app/agentkit/react/file-change.tsx +228 -0
  260. package/src/app/agentkit/react/file-diff.ts +110 -0
  261. package/src/app/agentkit/react/index.ts +7 -0
  262. package/src/app/agentkit/react/lifecycle.spec.tsx +565 -20
  263. package/src/app/agentkit/react/markdown.spec.tsx +18 -8
  264. package/src/app/agentkit/react/message-actions.spec.tsx +55 -1
  265. package/src/app/agentkit/react/notices.spec.tsx +4 -4
  266. package/src/app/agentkit/react/optimistic-send.spec.tsx +43 -0
  267. package/src/app/agentkit/react/question-card.spec.tsx +276 -0
  268. package/src/app/agentkit/react/question-card.tsx +533 -0
  269. package/src/app/agentkit/react/root.tsx +4 -0
  270. package/src/app/agentkit/react/roving-focus.spec.tsx +114 -0
  271. package/src/app/agentkit/react/roving-focus.ts +82 -0
  272. package/src/app/agentkit/react/stack-trace.spec.tsx +177 -0
  273. package/src/app/agentkit/react/stack-trace.tsx +281 -0
  274. package/src/app/agentkit/react/stop-button.spec.tsx +40 -0
  275. package/src/app/agentkit/react/stop-button.tsx +22 -5
  276. package/src/app/agentkit/react/styles.css +1306 -395
  277. package/src/app/agentkit/react/subagent-step.spec.tsx +554 -0
  278. package/src/app/agentkit/react/terminal.spec.tsx +228 -0
  279. package/src/app/agentkit/react/terminal.tsx +228 -0
  280. package/src/app/agentkit/react/trace.spec.tsx +120 -0
  281. package/src/app/agentkit/react/trace.tsx +239 -0
  282. package/src/app/blocks/library/HighlightedCode.tsx +14 -126
  283. package/src/app/chat/AgentKitAssistantChat.spec.tsx +28 -12
  284. package/src/app/chat/AgentKitAssistantChat.tsx +27 -253
  285. package/src/app/chat/agentkit-chat/approval-card.spec.tsx +1 -0
  286. package/src/app/chat/agentkit-chat/guided-questions.flow.spec.tsx +381 -0
  287. package/src/app/chat/agentkit-chat/guided-questions.tsx +236 -3
  288. package/src/app/chat/agentkit-chat/history.tsx +1 -1
  289. package/src/app/chat/agentkit-chat/index.ts +2 -0
  290. package/src/app/chat/agentkit-chat/labels.ts +259 -0
  291. package/src/app/chat/agentkit-chat/questions.ts +2 -0
  292. package/src/app/chat/agentkit-chat/root.spec.tsx +18 -0
  293. package/src/app/chat/agentkit-chat/root.tsx +5 -64
  294. package/src/app/chat/agentkit-chat/work-step-labels.ts +42 -0
  295. package/src/app/chat/chat/markdown-renderer.tsx +2 -59
  296. package/src/app/chat/chat/tool-call-display.spec.tsx +26 -14
  297. package/src/app/chat/chat/tool-call-display.tsx +2 -1
  298. package/src/app/chat/conversation/AgentConversation.tsx +2 -50
  299. package/src/app/chat/tool-cells/BashCell.tsx +14 -136
  300. package/src/app/chat/tool-cells/EditCell.tsx +11 -248
  301. package/src/app/chat/tool-cells/FilesChangedSummary.spec.tsx +62 -5
  302. package/src/app/chat/tool-cells/FilesChangedSummary.tsx +31 -162
  303. package/src/app/chat/tool-cells/WriteCell.tsx +11 -121
  304. package/src/app/code-highlighter.ts +131 -0
  305. package/src/app/context-xray/ThreadContextRing.tsx +7 -7
  306. package/src/app/i18n/catalogs/ar-SA.ts +22 -0
  307. package/src/app/i18n/catalogs/de-DE.ts +22 -0
  308. package/src/app/i18n/catalogs/en-US.ts +22 -0
  309. package/src/app/i18n/catalogs/es-ES.ts +22 -0
  310. package/src/app/i18n/catalogs/fr-FR.ts +22 -0
  311. package/src/app/i18n/catalogs/hi-IN.ts +22 -0
  312. package/src/app/i18n/catalogs/ja-JP.ts +22 -0
  313. package/src/app/i18n/catalogs/ko-KR.ts +22 -0
  314. package/src/app/i18n/catalogs/pt-BR.ts +22 -0
  315. package/src/app/i18n/catalogs/zh-CN.ts +22 -0
  316. package/src/app/i18n/catalogs/zh-TW.ts +22 -0
  317. package/src/app/onboarding/first-run-first-paint.spec.ts +39 -0
  318. package/src/app/onboarding/first-run-first-paint.ts +24 -0
  319. package/src/app/onboarding/first-run-startup-gate.spec.tsx +125 -13
  320. package/src/app/onboarding/first-run-startup-gate.tsx +48 -5
  321. package/src/app/providers/AppProviders.tsx +18 -0
  322. package/src/app/providers/BuilderSignupActivation.tsx +8 -2
  323. package/src/app/shared/AppShellSkeleton.spec.tsx +60 -1
  324. package/src/app/shared/AppShellSkeleton.tsx +31 -1
  325. package/src/app/styles/agent-native.css +2 -2
  326. package/src/app/styles/agent-native.spec.ts +2 -2
  327. package/src/app/styles/tokens/agent-kit.css +20 -3
  328. package/src/composer/AgentSuggestionBar.spec.tsx +3 -1
  329. package/src/composer/AgentSuggestionBar.tsx +5 -2
  330. package/src/composer/ComposerContextMenu.tsx +4 -4
  331. package/src/composer/ComposerContextPicker.tsx +8 -2
  332. package/src/composer/ComposerContextPickerDialog.tsx +5 -2
  333. package/src/composer/ComposerModePicker.tsx +2 -1
  334. package/src/composer/ComposerPlusMenu.tsx +1 -1
  335. package/src/composer/MentionChip.tsx +1 -1
  336. package/src/composer/MentionPopover.spec.tsx +108 -2
  337. package/src/composer/MentionPopover.tsx +93 -17
  338. package/src/composer/MessageQueueDrawer.spec.tsx +5 -5
  339. package/src/composer/MessageQueueDrawer.tsx +19 -12
  340. package/src/composer/PastedTextChip.spec.tsx +36 -0
  341. package/src/composer/PastedTextChip.tsx +52 -58
  342. package/src/composer/PromptBar.tsx +1 -1
  343. package/src/composer/PromptComposer.context.spec.tsx +2 -2
  344. package/src/composer/PromptComposer.submission.spec.tsx +27 -0
  345. package/src/composer/PromptComposer.tsx +17 -9
  346. package/src/composer/RealtimeVoiceMode.tsx +2 -2
  347. package/src/composer/TiptapComposer.model-picker.spec.tsx +23 -82
  348. package/src/composer/TiptapComposer.tsx +68 -145
  349. package/src/composer/VoiceButton.tsx +12 -7
  350. package/src/composer/composer-notice.tsx +3 -3
  351. package/src/composer/extensions/FileReference.tsx +1 -1
  352. package/src/composer/extensions/SkillReference.tsx +1 -1
  353. package/src/composer/popover-surface.ts +1 -1
  354. package/src/context-ui/ContextUsageRing.spec.tsx +33 -8
  355. package/src/context-ui/ContextUsageRing.tsx +13 -348
  356. package/src/context-ui/index.ts +7 -1
  357. package/dist/composer/ModelPickerParts.d.ts +0 -20
  358. package/dist/composer/ModelPickerParts.d.ts.map +0 -1
  359. package/dist/composer/ModelPickerParts.js +0 -77
  360. package/dist/composer/ModelPickerParts.js.map +0 -1
  361. package/src/composer/ModelPickerParts.tsx +0 -188
@@ -0,0 +1,154 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import { act } from "react";
4
+ import { createRoot, type Root } from "react-dom/client";
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
+
7
+ import { loadCodeHighlighter } from "../../code-highlighter.js";
8
+ import {
9
+ CodeBlock,
10
+ CodeBlockContent,
11
+ CodeBlockCopyButton,
12
+ CodeBlockLine,
13
+ } from "./code-block.js";
14
+
15
+ let root: Root;
16
+ let container: HTMLDivElement;
17
+
18
+ beforeEach(() => {
19
+ // Real throttle timers let a highlight pass land early under load.
20
+ vi.useFakeTimers();
21
+ vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
22
+ container = document.createElement("div");
23
+ document.body.append(container);
24
+ root = createRoot(container);
25
+ });
26
+
27
+ afterEach(() => {
28
+ act(() => root.unmount());
29
+ container.remove();
30
+ vi.unstubAllGlobals();
31
+ vi.useRealTimers();
32
+ });
33
+
34
+ async function render(node: React.ReactNode) {
35
+ await act(async () => root.render(node));
36
+ }
37
+
38
+ async function settle() {
39
+ await act(async () => {
40
+ vi.advanceTimersByTime(100);
41
+ await loadCodeHighlighter();
42
+ await Promise.resolve();
43
+ });
44
+ }
45
+
46
+ function lineText() {
47
+ return Array.from(
48
+ container.querySelectorAll(".agentkit-code-line"),
49
+ (line) => line.textContent,
50
+ );
51
+ }
52
+
53
+ describe("CodeBlock", () => {
54
+ it("renders plain lines first, then highlighted tokens with the same text", async () => {
55
+ const code = "const ready = true;\nexport { ready };\n";
56
+ await render(
57
+ <CodeBlock>
58
+ <CodeBlockContent language="ts">{code}</CodeBlockContent>
59
+ </CodeBlock>,
60
+ );
61
+ expect(container.querySelector(".agentkit-code-token")).toBeNull();
62
+ expect(lineText()).toEqual(["const ready = true;", "export { ready };"]);
63
+
64
+ await settle();
65
+ expect(container.querySelector(".agentkit-code-token")).not.toBeNull();
66
+ expect(lineText()).toEqual(["const ready = true;", "export { ready };"]);
67
+ expect(container.querySelector("code")?.textContent).toBe(
68
+ "const ready = true;\nexport { ready };",
69
+ );
70
+ });
71
+
72
+ it("keeps finished lines highlighted while a new line streams in", async () => {
73
+ const view = (code: string) => (
74
+ <CodeBlockContent language="ts">{code}</CodeBlockContent>
75
+ );
76
+ await render(view("const a = 1;"));
77
+ await settle();
78
+ await render(view("const a = 1;\nconst b"));
79
+ const [first, second] = Array.from(
80
+ container.querySelectorAll(".agentkit-code-line"),
81
+ );
82
+ expect(first?.querySelector(".agentkit-code-token")).not.toBeNull();
83
+ expect(second?.querySelector(".agentkit-code-token")).toBeNull();
84
+ expect(second?.textContent).toBe("const b");
85
+ });
86
+
87
+ it("colors terminal output and never shows its escape codes", async () => {
88
+ await render(
89
+ <CodeBlockContent language="ansi">
90
+ {"\u001b[31mfailed\u001b[0m 2 tests\nok"}
91
+ </CodeBlockContent>,
92
+ );
93
+ expect(lineText()).toEqual(["failed 2 tests", "ok"]);
94
+ await settle();
95
+ expect(lineText()).toEqual(["failed 2 tests", "ok"]);
96
+ const failed = container.querySelector<HTMLElement>(".agentkit-code-token");
97
+ expect(failed?.textContent).toBe("failed");
98
+ expect(failed?.style.getPropertyValue("--shiki-light")).not.toBe("");
99
+ });
100
+
101
+ it("renders an unknown language as plain text", async () => {
102
+ await render(
103
+ <CodeBlockContent language="cobol">{"DISPLAY 'HI'."}</CodeBlockContent>,
104
+ );
105
+ await settle();
106
+ expect(container.querySelector(".agentkit-code-token")).toBeNull();
107
+ expect(lineText()).toEqual(["DISPLAY 'HI'."]);
108
+ });
109
+
110
+ it("keeps diff markers and line numbers out of the text", async () => {
111
+ await render(
112
+ <CodeBlockContent lineNumbers startLine={10}>
113
+ <CodeBlockLine variant="removed">old</CodeBlockLine>
114
+ <CodeBlockLine variant="added">new</CodeBlockLine>
115
+ </CodeBlockContent>,
116
+ );
117
+ const pre = container.querySelector("pre")!;
118
+ expect(pre.dataset.lineNumbers).toBe("true");
119
+ expect(pre.style.getPropertyValue("--agentkit-code-start-line")).toBe("9");
120
+ expect(pre.getAttribute("dir")).toBe("ltr");
121
+ // The visible +/− is generated content; screen readers hear the word.
122
+ expect(pre.textContent).toBe("Removed old\nAdded new");
123
+ expect(
124
+ Array.from(pre.querySelectorAll(".agentkit-code-line-change"), (label) =>
125
+ label.classList.contains("agentkit-visually-hidden"),
126
+ ),
127
+ ).toEqual([true, true]);
128
+ expect(
129
+ Array.from(
130
+ pre.querySelectorAll<HTMLElement>(".agentkit-code-line"),
131
+ (line) => line.dataset.variant,
132
+ ),
133
+ ).toEqual(["removed", "added"]);
134
+ });
135
+
136
+ it("copies its value outside an AgentKit provider", async () => {
137
+ const writeText = vi.fn(async () => undefined);
138
+ vi.stubGlobal("navigator", { ...navigator, clipboard: { writeText } });
139
+ await render(<CodeBlockCopyButton value="pnpm build" />);
140
+ const button = container.querySelector("button")!;
141
+ expect(button.getAttribute("aria-label")).toBe("Copy code");
142
+ await act(async () => {
143
+ button.click();
144
+ await Promise.resolve();
145
+ await Promise.resolve();
146
+ });
147
+ expect(writeText).toHaveBeenCalledWith("pnpm build");
148
+ expect(button.getAttribute("aria-label")).toBe("Copied");
149
+ // Said once in a polite region; a renamed focused button is often silent.
150
+ expect(container.querySelector('[aria-live="polite"]')?.textContent).toBe(
151
+ "Copied",
152
+ );
153
+ });
154
+ });
@@ -0,0 +1,317 @@
1
+ import { IconCheck, IconCopy } from "@tabler/icons-react";
2
+ import {
3
+ Children,
4
+ useEffect,
5
+ useRef,
6
+ useState,
7
+ type ComponentProps,
8
+ type CSSProperties,
9
+ type ReactNode,
10
+ } from "react";
11
+ import type { ThemedToken } from "shiki/core";
12
+
13
+ import { writeClipboardText } from "../../../agentkit.js";
14
+ import { IconButton } from "../../../design-system/index.js";
15
+ import {
16
+ CODE_THEMES,
17
+ loadCodeHighlighter,
18
+ resolveCodeLanguage,
19
+ } from "../../code-highlighter.js";
20
+ import { useAgentKitLabels } from "./context.js";
21
+
22
+ export type CodeBlockLineVariant =
23
+ | "default"
24
+ | "added"
25
+ | "removed"
26
+ | "highlighted";
27
+
28
+ function classes(...names: Array<string | false | null | undefined>): string {
29
+ return names.filter(Boolean).join(" ");
30
+ }
31
+
32
+ export function CodeBlock({
33
+ className,
34
+ size = "default",
35
+ variant = "default",
36
+ ...props
37
+ }: ComponentProps<"div"> & {
38
+ size?: "default" | "sm";
39
+ variant?: "default" | "destructive";
40
+ }) {
41
+ return (
42
+ <div
43
+ data-slot="code-block"
44
+ data-size={size}
45
+ data-variant={variant}
46
+ className={classes("agentkit-code-block", className)}
47
+ {...props}
48
+ />
49
+ );
50
+ }
51
+
52
+ export function CodeBlockHeader({
53
+ className,
54
+ ...props
55
+ }: ComponentProps<"div">) {
56
+ return (
57
+ <div
58
+ data-slot="code-block-header"
59
+ className={classes("agentkit-code-block-header", className)}
60
+ {...props}
61
+ />
62
+ );
63
+ }
64
+
65
+ /** A filename, language, or command. Truncates. */
66
+ export function CodeBlockTitle({
67
+ className,
68
+ ...props
69
+ }: ComponentProps<"span">) {
70
+ return (
71
+ <span
72
+ data-slot="code-block-title"
73
+ className={classes("agentkit-code-block-title", className)}
74
+ {...props}
75
+ />
76
+ );
77
+ }
78
+
79
+ /** Outside a header, floats over the content and shows on hover or focus. */
80
+ export function CodeBlockActions({
81
+ className,
82
+ ...props
83
+ }: ComponentProps<"div">) {
84
+ return (
85
+ <div
86
+ data-slot="code-block-actions"
87
+ className={classes("agentkit-code-block-actions", className)}
88
+ {...props}
89
+ />
90
+ );
91
+ }
92
+
93
+ export function CodeBlockCopyButton({
94
+ value,
95
+ label,
96
+ copiedLabel,
97
+ }: {
98
+ value: string;
99
+ label?: string;
100
+ copiedLabel?: string;
101
+ }) {
102
+ const labels = useAgentKitLabels();
103
+ const [copied, setCopied] = useState(false);
104
+ const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
105
+ useEffect(
106
+ () => () => {
107
+ if (timer.current) clearTimeout(timer.current);
108
+ },
109
+ [],
110
+ );
111
+ const copy = async () => {
112
+ if (!(await writeClipboardText(value))) return;
113
+ setCopied(true);
114
+ if (timer.current) clearTimeout(timer.current);
115
+ timer.current = setTimeout(() => setCopied(false), 2_000);
116
+ };
117
+ const copiedText = copiedLabel ?? labels.codeCopied;
118
+ return (
119
+ <>
120
+ <IconButton
121
+ label={copied ? copiedText : (label ?? labels.copyCode)}
122
+ icon={
123
+ copied ? (
124
+ <IconCheck aria-hidden="true" size={16} />
125
+ ) : (
126
+ <IconCopy aria-hidden="true" size={16} />
127
+ )
128
+ }
129
+ size="compact"
130
+ className="size-6 rounded-[var(--agentkit-code-action-radius,var(--agentkit-radius-xs))]"
131
+ onPress={() => void copy()}
132
+ />
133
+ {/* A changed name on the focused button is not reliably read. */}
134
+ <span className="agentkit-visually-hidden" aria-live="polite">
135
+ {copied ? copiedText : ""}
136
+ </span>
137
+ </>
138
+ );
139
+ }
140
+
141
+ /** Diff variants carry a +/− marker, not color alone. */
142
+ export function CodeBlockLine({
143
+ className,
144
+ variant = "default",
145
+ children,
146
+ ...props
147
+ }: ComponentProps<"span"> & { variant?: CodeBlockLineVariant }) {
148
+ const labels = useAgentKitLabels();
149
+ // Screen readers hear this word, not the silent CSS +/−; it stays out of selections.
150
+ const change =
151
+ variant === "added"
152
+ ? labels.workStepLineAdded
153
+ : variant === "removed"
154
+ ? labels.workStepLineRemoved
155
+ : undefined;
156
+ return (
157
+ <span
158
+ data-slot="code-block-line"
159
+ data-variant={variant === "default" ? undefined : variant}
160
+ className={classes("agentkit-code-line", className)}
161
+ {...props}
162
+ >
163
+ {change ? (
164
+ <span className="agentkit-visually-hidden agentkit-code-line-change">
165
+ {`${change} `}
166
+ </span>
167
+ ) : null}
168
+ {children}
169
+ </span>
170
+ );
171
+ }
172
+
173
+ /** Line numbers stay out of copied text and the accessibility tree. */
174
+ export function CodeBlockContent({
175
+ children,
176
+ language,
177
+ wrap = false,
178
+ lineNumbers = false,
179
+ startLine = 1,
180
+ tabIndex = 0,
181
+ className,
182
+ style,
183
+ ...props
184
+ }: Omit<ComponentProps<"pre">, "children"> & {
185
+ children: string | ReactNode;
186
+ language?: string | undefined;
187
+ wrap?: boolean;
188
+ lineNumbers?: boolean;
189
+ startLine?: number;
190
+ }) {
191
+ const source = typeof children === "string" ? children : "";
192
+ const highlighted = useHighlightedCode(
193
+ source,
194
+ typeof children === "string" ? language : undefined,
195
+ );
196
+ const lines =
197
+ typeof children === "string"
198
+ ? highlighted.map((line, index) => (
199
+ <CodeBlockLine key={index}>{line}</CodeBlockLine>
200
+ ))
201
+ : Children.toArray(children);
202
+ return (
203
+ <pre
204
+ data-slot="code-block-content"
205
+ data-wrap={wrap ? "true" : undefined}
206
+ data-line-numbers={lineNumbers ? "true" : undefined}
207
+ dir="ltr"
208
+ tabIndex={tabIndex}
209
+ className={classes("agentkit-code-block-content", className)}
210
+ style={
211
+ lineNumbers && startLine !== 1
212
+ ? ({
213
+ ...style,
214
+ "--agentkit-code-start-line": startLine - 1,
215
+ } as CSSProperties)
216
+ : style
217
+ }
218
+ {...props}
219
+ >
220
+ <code
221
+ className={language ? `language-${language}` : undefined}
222
+ data-language={language}
223
+ >
224
+ {lines.flatMap((line, index) => (index ? ["\n", line] : [line]))}
225
+ </code>
226
+ </pre>
227
+ );
228
+ }
229
+
230
+ export function CodeBlockFooter({
231
+ className,
232
+ ...props
233
+ }: ComponentProps<"div">) {
234
+ return (
235
+ <div
236
+ data-slot="code-block-footer"
237
+ className={classes("agentkit-code-block-footer", className)}
238
+ {...props}
239
+ />
240
+ );
241
+ }
242
+
243
+ function codeLines(code: string): string[] {
244
+ return code.replace(/\n$/, "").split("\n");
245
+ }
246
+
247
+ // An `ansi` pass drops these from token text, so the plain line must too.
248
+ const ANSI_SEQUENCE = /\u001b\[[0-9;]*m/g;
249
+
250
+ function visibleLine(line: string, language: string | undefined): string {
251
+ return language === "ansi" ? line.replace(ANSI_SEQUENCE, "") : line;
252
+ }
253
+
254
+ function tokenText(tokens: ThemedToken[]): string {
255
+ return tokens.map((token) => token.content).join("");
256
+ }
257
+
258
+ interface HighlightPass {
259
+ language: string;
260
+ lines: ThemedToken[][];
261
+ }
262
+
263
+ const HIGHLIGHT_INTERVAL_MS = 80;
264
+
265
+ /** A line keeps its last tokens until its text changes, so streaming never flashes it plain. */
266
+ export function useHighlightedCode(
267
+ code: string,
268
+ language: string | undefined,
269
+ ): ReactNode[] {
270
+ const [pass, setPass] = useState<HighlightPass | null>(null);
271
+ const lastRun = useRef(0);
272
+ useEffect(() => {
273
+ if (!language) return;
274
+ let cancelled = false;
275
+ const wait = Math.max(
276
+ 0,
277
+ HIGHLIGHT_INTERVAL_MS - (Date.now() - lastRun.current),
278
+ );
279
+ const timer = setTimeout(() => {
280
+ lastRun.current = Date.now();
281
+ void loadCodeHighlighter().then(
282
+ (highlighter) => {
283
+ if (cancelled) return;
284
+ const lang = resolveCodeLanguage(highlighter, language);
285
+ if (!lang) return setPass(null);
286
+ const { tokens } = highlighter.codeToTokens(code.replace(/\n$/, ""), {
287
+ lang,
288
+ themes: CODE_THEMES,
289
+ defaultColor: false,
290
+ });
291
+ setPass({ language, lines: tokens });
292
+ },
293
+ // Highlighting is decoration: the plain lines already show the code.
294
+ () => undefined,
295
+ );
296
+ }, wait);
297
+ return () => {
298
+ cancelled = true;
299
+ clearTimeout(timer);
300
+ };
301
+ }, [code, language]);
302
+ const usable = pass && pass.language === language ? pass.lines : undefined;
303
+ return codeLines(code).map((source, index) => {
304
+ const line = visibleLine(source, language);
305
+ const tokens = usable?.[index];
306
+ if (!tokens || tokenText(tokens) !== line) return line;
307
+ return tokens.map((token, tokenIndex) => (
308
+ <span
309
+ key={tokenIndex}
310
+ className="agentkit-code-token"
311
+ style={token.htmlStyle as CSSProperties | undefined}
312
+ >
313
+ {token.content}
314
+ </span>
315
+ ));
316
+ });
317
+ }
@@ -183,6 +183,13 @@ describe("AgentActivityItem replay privacy", () => {
183
183
  ?.querySelector('a[href^="https://example.test/report"]')
184
184
  ?.closest(`[${SESSION_REPLAY_BLOCK_ATTRIBUTE}]`) !== null,
185
185
  ).toBe(status === "failed");
186
+
187
+ // Closing swaps in a fresh, blocked node rather than re-blocking the
188
+ // node replay recorded while it was open.
189
+ await act(async () => disclosure.click());
190
+ const closed = container.querySelector(".agentkit-activity-summary");
191
+ expect(summary?.isConnected).toBe(false);
192
+ expect(closed?.hasAttribute(SESSION_REPLAY_BLOCK_ATTRIBUTE)).toBe(true);
186
193
  } finally {
187
194
  await act(async () => root.unmount());
188
195
  await client.shutdown();
@@ -13,7 +13,7 @@ import type {
13
13
  AgentEvent,
14
14
  AgentTask,
15
15
  } from "@agent-native/agentkit/protocol";
16
- import { act, createElement, type ReactNode } from "react";
16
+ import { act, createElement, useEffect, useRef, type ReactNode } from "react";
17
17
  import { createRoot, type Root } from "react-dom/client";
18
18
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
19
19
 
@@ -62,7 +62,7 @@ import {
62
62
  AgentTaskItem,
63
63
  useAgentKitFollowTranscript,
64
64
  } from "./components.js";
65
- import { AgentKitProvider } from "./context.js";
65
+ import { AgentKitProvider, useAgentKit } from "./context.js";
66
66
 
67
67
  let container: HTMLDivElement;
68
68
  let root: Root;
@@ -160,6 +160,17 @@ function runningThread(threadId: string, runId: string): AgentThreadState {
160
160
  };
161
161
  }
162
162
 
163
+ // Registered after the chat's own composer, so a focus request lands here.
164
+ function ComposerFocusTarget() {
165
+ const { registerComposerFocus, threadId } = useAgentKit();
166
+ const target = useRef<HTMLButtonElement>(null);
167
+ useEffect(
168
+ () => registerComposerFocus(threadId, () => target.current?.focus()),
169
+ [registerComposerFocus, threadId],
170
+ );
171
+ return <button ref={target}>Composer focus target</button>;
172
+ }
173
+
163
174
  describe("AgentKitChat papercuts", () => {
164
175
  it("offers a default Stop that cancels once however often it is clicked", async () => {
165
176
  const thread = runningThread("thread-stop", "run-stop");
@@ -195,6 +206,37 @@ describe("AgentKitChat papercuts", () => {
195
206
  expect(stop?.disabled).toBe(false);
196
207
  });
197
208
 
209
+ it("offers a skip link past the transcript to the composer", async () => {
210
+ const thread = runningThread("thread-skip", "run-skip");
211
+ const store = observableController(snapshotOf(thread));
212
+ await render(
213
+ <AgentKitProvider controller={store.controller} threadId={thread.id}>
214
+ <AgentKitChat
215
+ composerProps={{
216
+ autoFocus: false,
217
+ modelStatusChecksEnabled: false,
218
+ voiceEnabled: false,
219
+ }}
220
+ />
221
+ <ComposerFocusTarget />
222
+ </AgentKitProvider>,
223
+ );
224
+ const skip = container.querySelector<HTMLButtonElement>(
225
+ '[data-agentkit-skip-link="composer"]',
226
+ );
227
+ const transcript = container.querySelector(".agentkit-transcript");
228
+ expect(skip?.textContent).toBe("Skip to message box");
229
+ expect(
230
+ transcript && skip
231
+ ? skip.compareDocumentPosition(transcript) &
232
+ Node.DOCUMENT_POSITION_FOLLOWING
233
+ : 0,
234
+ ).toBeTruthy();
235
+
236
+ await act(async () => skip?.click());
237
+ expect(document.activeElement?.textContent).toBe("Composer focus target");
238
+ });
239
+
198
240
  it("announces a finished response once in the shared status region", async () => {
199
241
  const thread = runningThread("thread-announce", "run-announce");
200
242
  const store = observableController(snapshotOf(thread));
@@ -232,6 +274,105 @@ describe("AgentKitChat papercuts", () => {
232
274
  ).toHaveLength(1);
233
275
  });
234
276
 
277
+ it.each([
278
+ ["awaiting_approval", "Waiting for approval"],
279
+ ["awaiting_input", "Waiting for input"],
280
+ ["cancelled", "Stopped"],
281
+ ] as const)("announces a run that goes %s", async (status, announcement) => {
282
+ const thread = runningThread("thread-pause", "run-pause");
283
+ const store = observableController(snapshotOf(thread));
284
+ await render(
285
+ <AgentKitProvider controller={store.controller} threadId={thread.id}>
286
+ <AgentKitChat composer={false} />
287
+ </AgentKitProvider>,
288
+ );
289
+
290
+ await act(async () => {
291
+ store.update(
292
+ snapshotOf(
293
+ {
294
+ ...thread,
295
+ runs: {
296
+ "run-pause": { ...thread.runs["run-pause"]!, status },
297
+ },
298
+ },
299
+ 1,
300
+ ),
301
+ );
302
+ await new Promise((resolve) => setTimeout(resolve, 0));
303
+ });
304
+ expect(
305
+ container.querySelector("[data-agentkit-live-status]")?.textContent,
306
+ ).toBe(announcement);
307
+ });
308
+
309
+ it.each([
310
+ ["with an error, which its own alert reads", true, ""],
311
+ ["without an error", false, "Response failed"],
312
+ ] as const)(
313
+ "announces a failed run once: %s",
314
+ async (_case, withError, announcement) => {
315
+ const thread = runningThread("thread-fail", "run-fail");
316
+ const store = observableController(snapshotOf(thread));
317
+ await render(
318
+ <AgentKitProvider controller={store.controller} threadId={thread.id}>
319
+ <AgentKitChat composer={false} />
320
+ </AgentKitProvider>,
321
+ );
322
+ await act(async () => {
323
+ store.update(
324
+ snapshotOf(
325
+ {
326
+ ...thread,
327
+ activeRunIds: [],
328
+ runs: {
329
+ "run-fail": {
330
+ ...thread.runs["run-fail"]!,
331
+ status: "failed",
332
+ ...(withError
333
+ ? {
334
+ error: {
335
+ code: "provider_error",
336
+ message: "The model provider returned an error.",
337
+ },
338
+ }
339
+ : {}),
340
+ },
341
+ },
342
+ },
343
+ 1,
344
+ ),
345
+ );
346
+ await new Promise((resolve) => setTimeout(resolve, 0));
347
+ });
348
+ expect(
349
+ container.querySelector("[data-agentkit-live-status]")?.textContent,
350
+ ).toBe(announcement);
351
+ expect(container.querySelectorAll('[role="alert"]')).toHaveLength(
352
+ withError ? 1 : 0,
353
+ );
354
+ },
355
+ );
356
+
357
+ it("gives every turn a heading for heading navigation", async () => {
358
+ const thread = runningThread("thread-headings", "run-headings");
359
+ const store = observableController(snapshotOf(thread));
360
+ await render(
361
+ <AgentKitProvider controller={store.controller} threadId={thread.id}>
362
+ <AgentKitChat composer={false} />
363
+ </AgentKitProvider>,
364
+ );
365
+ const articles = [
366
+ ...container.querySelectorAll("article.agentkit-message"),
367
+ ];
368
+ expect(articles.length).toBeGreaterThan(0);
369
+ expect(
370
+ articles.map(
371
+ (article) => article.querySelector(":scope > h2")?.textContent,
372
+ ),
373
+ ).toEqual(articles.map((article) => article.getAttribute("aria-label")));
374
+ });
375
+
235
376
  it("marks a stopped run under its turn", async () => {
236
377
  const thread: AgentThreadState = {
237
378
  ...createAgentThreadState("thread-cancelled"),