@assistant-ui/react-devtools 1.2.6 → 1.2.7

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 (533) hide show
  1. package/README.md +26 -8
  2. package/dist/DevToolsModal.d.ts +18 -2
  3. package/dist/DevToolsModal.d.ts.map +1 -1
  4. package/dist/DevToolsModal.js +25 -232
  5. package/dist/DevToolsModal.js.map +1 -1
  6. package/dist/DevToolsModalImpl.d.ts +11 -0
  7. package/dist/DevToolsModalImpl.d.ts.map +1 -0
  8. package/dist/DevToolsModalImpl.js +62 -0
  9. package/dist/DevToolsModalImpl.js.map +1 -0
  10. package/dist/data/createInProcessClient.d.ts +17 -0
  11. package/dist/data/createInProcessClient.d.ts.map +1 -0
  12. package/dist/data/createInProcessClient.js +54 -0
  13. package/dist/data/createInProcessClient.js.map +1 -0
  14. package/dist/data/projectApi.d.ts +22 -0
  15. package/dist/data/projectApi.d.ts.map +1 -0
  16. package/dist/data/projectApi.js +103 -0
  17. package/dist/data/projectApi.js.map +1 -0
  18. package/dist/data/types.d.ts +52 -0
  19. package/dist/data/types.d.ts.map +1 -0
  20. package/dist/data/types.js +10 -0
  21. package/dist/data/types.js.map +1 -0
  22. package/dist/data/useDevToolsClient.d.ts +19 -0
  23. package/dist/data/useDevToolsClient.d.ts.map +1 -0
  24. package/dist/data/useDevToolsClient.js +70 -0
  25. package/dist/data/useDevToolsClient.js.map +1 -0
  26. package/dist/index.d.ts +9 -10
  27. package/dist/index.js +7 -9
  28. package/dist/shell/DevToolsOverlay.d.ts +23 -0
  29. package/dist/shell/DevToolsOverlay.d.ts.map +1 -0
  30. package/dist/shell/DevToolsOverlay.js +106 -0
  31. package/dist/shell/DevToolsOverlay.js.map +1 -0
  32. package/dist/shell/DevToolsPanel.d.ts +23 -0
  33. package/dist/shell/DevToolsPanel.d.ts.map +1 -0
  34. package/dist/shell/DevToolsPanel.js +256 -0
  35. package/dist/shell/DevToolsPanel.js.map +1 -0
  36. package/dist/shell/ShadowRoot.d.ts +24 -0
  37. package/dist/shell/ShadowRoot.d.ts.map +1 -0
  38. package/dist/shell/ShadowRoot.js +107 -0
  39. package/dist/shell/ShadowRoot.js.map +1 -0
  40. package/dist/shell/SplitLayout.d.ts +28 -0
  41. package/dist/shell/SplitLayout.d.ts.map +1 -0
  42. package/dist/shell/SplitLayout.js +67 -0
  43. package/dist/shell/SplitLayout.js.map +1 -0
  44. package/dist/shell/registry.d.ts +41 -0
  45. package/dist/shell/registry.d.ts.map +1 -0
  46. package/dist/shell/registry.js +36 -0
  47. package/dist/shell/registry.js.map +1 -0
  48. package/dist/shell/tabs/ActivityTab.d.ts +13 -0
  49. package/dist/shell/tabs/ActivityTab.d.ts.map +1 -0
  50. package/dist/shell/tabs/ActivityTab.js +259 -0
  51. package/dist/shell/tabs/ActivityTab.js.map +1 -0
  52. package/dist/shell/tabs/ContextTab.d.ts +11 -0
  53. package/dist/shell/tabs/ContextTab.d.ts.map +1 -0
  54. package/dist/shell/tabs/ContextTab.js +210 -0
  55. package/dist/shell/tabs/ContextTab.js.map +1 -0
  56. package/dist/shell/tabs/RawTab.d.ts +11 -0
  57. package/dist/shell/tabs/RawTab.d.ts.map +1 -0
  58. package/dist/shell/tabs/RawTab.js +238 -0
  59. package/dist/shell/tabs/RawTab.js.map +1 -0
  60. package/dist/shell/tabs/ThreadTab.d.ts +12 -0
  61. package/dist/shell/tabs/ThreadTab.d.ts.map +1 -0
  62. package/dist/shell/tabs/ThreadTab.js +426 -0
  63. package/dist/shell/tabs/ThreadTab.js.map +1 -0
  64. package/dist/shell/tabs/navSelection.d.ts +10 -0
  65. package/dist/shell/tabs/navSelection.d.ts.map +1 -0
  66. package/dist/shell/tabs/navSelection.js +37 -0
  67. package/dist/shell/tabs/navSelection.js.map +1 -0
  68. package/dist/styles/panel.generated.d.ts +5 -0
  69. package/dist/styles/panel.generated.d.ts.map +1 -0
  70. package/dist/styles/panel.generated.js +6 -0
  71. package/dist/styles/panel.generated.js.map +1 -0
  72. package/dist/types.d.ts +1 -3
  73. package/dist/types.d.ts.map +1 -1
  74. package/dist/utils/common.d.ts +24 -0
  75. package/dist/utils/common.d.ts.map +1 -0
  76. package/dist/utils/common.js +64 -0
  77. package/dist/utils/common.js.map +1 -0
  78. package/dist/utils/diff.d.ts +16 -0
  79. package/dist/utils/diff.d.ts.map +1 -0
  80. package/dist/utils/diff.js +41 -0
  81. package/dist/utils/diff.js.map +1 -0
  82. package/dist/utils/serialization.d.ts.map +1 -1
  83. package/dist/utils/serialization.js +4 -1
  84. package/dist/utils/serialization.js.map +1 -1
  85. package/dist/views/activity/EventLogRow.d.ts +28 -0
  86. package/dist/views/activity/EventLogRow.d.ts.map +1 -0
  87. package/dist/views/activity/EventLogRow.js +185 -0
  88. package/dist/views/activity/EventLogRow.js.map +1 -0
  89. package/dist/views/activity/EventStreamPane.d.ts +11 -0
  90. package/dist/views/activity/EventStreamPane.d.ts.map +1 -0
  91. package/dist/views/activity/EventStreamPane.js +98 -0
  92. package/dist/views/activity/EventStreamPane.js.map +1 -0
  93. package/dist/views/activity/OrphansPane.d.ts +11 -0
  94. package/dist/views/activity/OrphansPane.d.ts.map +1 -0
  95. package/dist/views/activity/OrphansPane.js +39 -0
  96. package/dist/views/activity/OrphansPane.js.map +1 -0
  97. package/dist/views/activity/RunDetailPane.d.ts +11 -0
  98. package/dist/views/activity/RunDetailPane.d.ts.map +1 -0
  99. package/dist/views/activity/RunDetailPane.js +344 -0
  100. package/dist/views/activity/RunDetailPane.js.map +1 -0
  101. package/dist/views/activity/activityNodes.d.ts +39 -0
  102. package/dist/views/activity/activityNodes.d.ts.map +1 -0
  103. package/dist/views/activity/activityNodes.js +199 -0
  104. package/dist/views/activity/activityNodes.js.map +1 -0
  105. package/dist/views/activity/index.d.ts +6 -0
  106. package/dist/views/activity/index.js +6 -0
  107. package/dist/views/attachments/AttachmentList.d.ts +15 -0
  108. package/dist/views/attachments/AttachmentList.d.ts.map +1 -0
  109. package/dist/views/attachments/AttachmentList.js +279 -0
  110. package/dist/views/attachments/AttachmentList.js.map +1 -0
  111. package/dist/views/attachments/MediaPartView.d.ts +27 -0
  112. package/dist/views/attachments/MediaPartView.d.ts.map +1 -0
  113. package/dist/views/attachments/MediaPartView.js +180 -0
  114. package/dist/views/attachments/MediaPartView.js.map +1 -0
  115. package/dist/views/attachments/formatBytes.d.ts +8 -0
  116. package/dist/views/attachments/formatBytes.d.ts.map +1 -0
  117. package/dist/views/attachments/formatBytes.js +14 -0
  118. package/dist/views/attachments/formatBytes.js.map +1 -0
  119. package/dist/views/attachments/index.d.ts +5 -0
  120. package/dist/views/attachments/index.js +4 -0
  121. package/dist/views/attachments/parseAttachment.d.ts +8 -0
  122. package/dist/views/attachments/parseAttachment.d.ts.map +1 -0
  123. package/dist/views/attachments/parseAttachment.js +58 -0
  124. package/dist/views/attachments/parseAttachment.js.map +1 -0
  125. package/dist/views/attachments/types.d.ts +18 -0
  126. package/dist/views/attachments/types.d.ts.map +1 -0
  127. package/dist/views/attachments/types.js +0 -0
  128. package/dist/views/context/ContextDetail.d.ts +14 -0
  129. package/dist/views/context/ContextDetail.d.ts.map +1 -0
  130. package/dist/views/context/ContextDetail.js +87 -0
  131. package/dist/views/context/ContextDetail.js.map +1 -0
  132. package/dist/views/context/ToolUIsView.d.ts +9 -0
  133. package/dist/views/context/ToolUIsView.d.ts.map +1 -0
  134. package/dist/views/context/ToolUIsView.js +80 -0
  135. package/dist/views/context/ToolUIsView.js.map +1 -0
  136. package/dist/views/context/contextNodes.d.ts +32 -0
  137. package/dist/views/context/contextNodes.d.ts.map +1 -0
  138. package/dist/views/context/contextNodes.js +49 -0
  139. package/dist/views/context/contextNodes.js.map +1 -0
  140. package/dist/views/context/index.d.ts +4 -0
  141. package/dist/views/context/index.js +4 -0
  142. package/dist/views/mcp/McpView.d.ts +11 -0
  143. package/dist/views/mcp/McpView.d.ts.map +1 -0
  144. package/dist/views/mcp/McpView.js +239 -0
  145. package/dist/views/mcp/McpView.js.map +1 -0
  146. package/dist/views/mcp/index.d.ts +2 -0
  147. package/dist/views/mcp/index.js +2 -0
  148. package/dist/views/mcp/parse.d.ts +7 -0
  149. package/dist/views/mcp/parse.d.ts.map +1 -0
  150. package/dist/views/mcp/parse.js +43 -0
  151. package/dist/views/mcp/parse.js.map +1 -0
  152. package/dist/views/mcp/types.d.ts +22 -0
  153. package/dist/views/mcp/types.d.ts.map +1 -0
  154. package/dist/views/mcp/types.js +0 -0
  155. package/dist/views/message/ChainOfThought.d.ts +11 -0
  156. package/dist/views/message/ChainOfThought.d.ts.map +1 -0
  157. package/dist/views/message/ChainOfThought.js +132 -0
  158. package/dist/views/message/ChainOfThought.js.map +1 -0
  159. package/dist/views/message/MessageDetailPane.d.ts +12 -0
  160. package/dist/views/message/MessageDetailPane.d.ts.map +1 -0
  161. package/dist/views/message/MessageDetailPane.js +38 -0
  162. package/dist/views/message/MessageDetailPane.js.map +1 -0
  163. package/dist/views/message/MessageItem.d.ts +13 -0
  164. package/dist/views/message/MessageItem.d.ts.map +1 -0
  165. package/dist/views/message/MessageItem.js +136 -0
  166. package/dist/views/message/MessageItem.js.map +1 -0
  167. package/dist/views/message/MessageList.d.ts +13 -0
  168. package/dist/views/message/MessageList.d.ts.map +1 -0
  169. package/dist/views/message/MessageList.js +95 -0
  170. package/dist/views/message/MessageList.js.map +1 -0
  171. package/dist/views/message/MessageMetrics.d.ts +11 -0
  172. package/dist/views/message/MessageMetrics.d.ts.map +1 -0
  173. package/dist/views/message/MessageMetrics.js +93 -0
  174. package/dist/views/message/MessageMetrics.js.map +1 -0
  175. package/dist/views/message/MessageTimeline.d.ts +16 -0
  176. package/dist/views/message/MessageTimeline.d.ts.map +1 -0
  177. package/dist/views/message/MessageTimeline.js +100 -0
  178. package/dist/views/message/MessageTimeline.js.map +1 -0
  179. package/dist/views/message/PartDisclosure.d.ts +18 -0
  180. package/dist/views/message/PartDisclosure.d.ts.map +1 -0
  181. package/dist/views/message/PartDisclosure.js +79 -0
  182. package/dist/views/message/PartDisclosure.js.map +1 -0
  183. package/dist/views/message/PartView.d.ts +13 -0
  184. package/dist/views/message/PartView.d.ts.map +1 -0
  185. package/dist/views/message/PartView.js +416 -0
  186. package/dist/views/message/PartView.js.map +1 -0
  187. package/dist/views/message/RoleLabel.d.ts +11 -0
  188. package/dist/views/message/RoleLabel.d.ts.map +1 -0
  189. package/dist/views/message/RoleLabel.js +30 -0
  190. package/dist/views/message/RoleLabel.js.map +1 -0
  191. package/dist/views/message/StatusBadge.d.ts +17 -0
  192. package/dist/views/message/StatusBadge.d.ts.map +1 -0
  193. package/dist/views/message/StatusBadge.js +58 -0
  194. package/dist/views/message/StatusBadge.js.map +1 -0
  195. package/dist/views/message/ToolCallView.d.ts +13 -0
  196. package/dist/views/message/ToolCallView.d.ts.map +1 -0
  197. package/dist/views/message/ToolCallView.js +289 -0
  198. package/dist/views/message/ToolCallView.js.map +1 -0
  199. package/dist/views/message/TtftBar.d.ts +16 -0
  200. package/dist/views/message/TtftBar.d.ts.map +1 -0
  201. package/dist/views/message/TtftBar.js +128 -0
  202. package/dist/views/message/TtftBar.js.map +1 -0
  203. package/dist/views/message/index.d.ts +6 -0
  204. package/dist/views/message/index.js +5 -0
  205. package/dist/views/message/messagePlainText.d.ts +8 -0
  206. package/dist/views/message/messagePlainText.d.ts.map +1 -0
  207. package/dist/views/message/messagePlainText.js +39 -0
  208. package/dist/views/message/messagePlainText.js.map +1 -0
  209. package/dist/views/message/parse.d.ts +10 -0
  210. package/dist/views/message/parse.d.ts.map +1 -0
  211. package/dist/views/message/parse.js +235 -0
  212. package/dist/views/message/parse.js.map +1 -0
  213. package/dist/views/message/types.d.ts +114 -0
  214. package/dist/views/message/types.d.ts.map +1 -0
  215. package/dist/views/message/types.js +0 -0
  216. package/dist/views/model-context/ModelContextView.d.ts +26 -0
  217. package/dist/views/model-context/ModelContextView.d.ts.map +1 -0
  218. package/dist/views/model-context/ModelContextView.js +214 -0
  219. package/dist/views/model-context/ModelContextView.js.map +1 -0
  220. package/dist/views/nav.d.ts +19 -0
  221. package/dist/views/nav.d.ts.map +1 -0
  222. package/dist/views/nav.js +8 -0
  223. package/dist/views/nav.js.map +1 -0
  224. package/dist/views/raw/RawDetail.d.ts +15 -0
  225. package/dist/views/raw/RawDetail.d.ts.map +1 -0
  226. package/dist/views/raw/RawDetail.js +79 -0
  227. package/dist/views/raw/RawDetail.js.map +1 -0
  228. package/dist/views/raw/index.d.ts +3 -0
  229. package/dist/views/raw/index.js +3 -0
  230. package/dist/views/raw/rawNodes.d.ts +20 -0
  231. package/dist/views/raw/rawNodes.d.ts.map +1 -0
  232. package/dist/views/raw/rawNodes.js +78 -0
  233. package/dist/views/raw/rawNodes.js.map +1 -0
  234. package/dist/views/raw/statePreview.d.ts +5 -0
  235. package/dist/views/raw/statePreview.d.ts.map +1 -0
  236. package/dist/views/raw/statePreview.js +213 -0
  237. package/dist/views/raw/statePreview.js.map +1 -0
  238. package/dist/views/runs/PayloadDiff.d.ts +11 -0
  239. package/dist/views/runs/PayloadDiff.d.ts.map +1 -0
  240. package/dist/views/runs/PayloadDiff.js +92 -0
  241. package/dist/views/runs/PayloadDiff.js.map +1 -0
  242. package/dist/views/runs/parse.d.ts +8 -0
  243. package/dist/views/runs/parse.d.ts.map +1 -0
  244. package/dist/views/runs/parse.js +78 -0
  245. package/dist/views/runs/parse.js.map +1 -0
  246. package/dist/views/runs/types.d.ts +25 -0
  247. package/dist/views/runs/types.d.ts.map +1 -0
  248. package/dist/views/runs/types.js +0 -0
  249. package/dist/views/scopes/ScopesView.d.ts +9 -0
  250. package/dist/views/scopes/ScopesView.d.ts.map +1 -0
  251. package/dist/views/scopes/ScopesView.js +235 -0
  252. package/dist/views/scopes/ScopesView.js.map +1 -0
  253. package/dist/views/scopes/index.d.ts +2 -0
  254. package/dist/views/scopes/index.js +2 -0
  255. package/dist/views/scopes/parse.d.ts +7 -0
  256. package/dist/views/scopes/parse.d.ts.map +1 -0
  257. package/dist/views/scopes/parse.js +23 -0
  258. package/dist/views/scopes/parse.js.map +1 -0
  259. package/dist/views/scopes/types.d.ts +10 -0
  260. package/dist/views/scopes/types.d.ts.map +1 -0
  261. package/dist/views/scopes/types.js +0 -0
  262. package/dist/views/thread/ComposerAttachments.d.ts +11 -0
  263. package/dist/views/thread/ComposerAttachments.d.ts.map +1 -0
  264. package/dist/views/thread/ComposerAttachments.js +22 -0
  265. package/dist/views/thread/ComposerAttachments.js.map +1 -0
  266. package/dist/views/thread/ComposerFlags.d.ts +11 -0
  267. package/dist/views/thread/ComposerFlags.d.ts.map +1 -0
  268. package/dist/views/thread/ComposerFlags.js +54 -0
  269. package/dist/views/thread/ComposerFlags.js.map +1 -0
  270. package/dist/views/thread/ComposerQueue.d.ts +11 -0
  271. package/dist/views/thread/ComposerQueue.d.ts.map +1 -0
  272. package/dist/views/thread/ComposerQueue.js +55 -0
  273. package/dist/views/thread/ComposerQueue.js.map +1 -0
  274. package/dist/views/thread/ConversationList.d.ts +18 -0
  275. package/dist/views/thread/ConversationList.d.ts.map +1 -0
  276. package/dist/views/thread/ConversationList.js +228 -0
  277. package/dist/views/thread/ConversationList.js.map +1 -0
  278. package/dist/views/thread/ConversationStatusDot.d.ts +29 -0
  279. package/dist/views/thread/ConversationStatusDot.d.ts.map +1 -0
  280. package/dist/views/thread/ConversationStatusDot.js +91 -0
  281. package/dist/views/thread/ConversationStatusDot.js.map +1 -0
  282. package/dist/views/thread/ThreadDetails.d.ts +13 -0
  283. package/dist/views/thread/ThreadDetails.d.ts.map +1 -0
  284. package/dist/views/thread/ThreadDetails.js +170 -0
  285. package/dist/views/thread/ThreadDetails.js.map +1 -0
  286. package/dist/views/thread/Transcript.d.ts +21 -0
  287. package/dist/views/thread/Transcript.d.ts.map +1 -0
  288. package/dist/views/thread/Transcript.js +520 -0
  289. package/dist/views/thread/Transcript.js.map +1 -0
  290. package/dist/views/thread/index.d.ts +10 -0
  291. package/dist/views/thread/index.js +9 -0
  292. package/dist/views/thread/resolve.d.ts +13 -0
  293. package/dist/views/thread/resolve.d.ts.map +1 -0
  294. package/dist/views/thread/resolve.js +29 -0
  295. package/dist/views/thread/resolve.js.map +1 -0
  296. package/dist/views/thread/types.d.ts +50 -0
  297. package/dist/views/thread/types.d.ts.map +1 -0
  298. package/dist/views/thread/types.js +0 -0
  299. package/dist/views/thread/utils.d.ts +10 -0
  300. package/dist/views/thread/utils.d.ts.map +1 -0
  301. package/dist/views/thread/utils.js +77 -0
  302. package/dist/views/thread/utils.js.map +1 -0
  303. package/dist/views/ui/CenteredMessage.d.ts +11 -0
  304. package/dist/views/ui/CenteredMessage.d.ts.map +1 -0
  305. package/dist/views/ui/CenteredMessage.js +21 -0
  306. package/dist/views/ui/CenteredMessage.js.map +1 -0
  307. package/dist/views/ui/Chip.d.ts +13 -0
  308. package/dist/views/ui/Chip.d.ts.map +1 -0
  309. package/dist/views/ui/Chip.js +30 -0
  310. package/dist/views/ui/Chip.js.map +1 -0
  311. package/dist/views/ui/ControlButton.d.ts +10 -0
  312. package/dist/views/ui/ControlButton.d.ts.map +1 -0
  313. package/dist/views/ui/ControlButton.js +39 -0
  314. package/dist/views/ui/ControlButton.js.map +1 -0
  315. package/dist/views/ui/CopyButton.d.ts +19 -0
  316. package/dist/views/ui/CopyButton.d.ts.map +1 -0
  317. package/dist/views/ui/CopyButton.js +125 -0
  318. package/dist/views/ui/CopyButton.js.map +1 -0
  319. package/dist/views/ui/EmptyState.d.ts +11 -0
  320. package/dist/views/ui/EmptyState.d.ts.map +1 -0
  321. package/dist/views/ui/EmptyState.js +21 -0
  322. package/dist/views/ui/EmptyState.js.map +1 -0
  323. package/dist/views/ui/InfoCard.d.ts +22 -0
  324. package/dist/views/ui/InfoCard.d.ts.map +1 -0
  325. package/dist/views/ui/InfoCard.js +44 -0
  326. package/dist/views/ui/InfoCard.js.map +1 -0
  327. package/dist/views/ui/JSONTree.d.ts +16 -0
  328. package/dist/views/ui/JSONTree.d.ts.map +1 -0
  329. package/dist/views/ui/JSONTree.js +394 -0
  330. package/dist/views/ui/JSONTree.js.map +1 -0
  331. package/dist/views/ui/PaneHeader.d.ts +27 -0
  332. package/dist/views/ui/PaneHeader.d.ts.map +1 -0
  333. package/dist/views/ui/PaneHeader.js +71 -0
  334. package/dist/views/ui/PaneHeader.js.map +1 -0
  335. package/dist/views/ui/RailTime.d.ts +12 -0
  336. package/dist/views/ui/RailTime.d.ts.map +1 -0
  337. package/dist/views/ui/RailTime.js +49 -0
  338. package/dist/views/ui/RailTime.js.map +1 -0
  339. package/dist/views/ui/SectionLabel.d.ts +11 -0
  340. package/dist/views/ui/SectionLabel.d.ts.map +1 -0
  341. package/dist/views/ui/SectionLabel.js +21 -0
  342. package/dist/views/ui/SectionLabel.js.map +1 -0
  343. package/dist/views/ui/SelectableRow.d.ts +18 -0
  344. package/dist/views/ui/SelectableRow.d.ts.map +1 -0
  345. package/dist/views/ui/SelectableRow.js +49 -0
  346. package/dist/views/ui/SelectableRow.js.map +1 -0
  347. package/dist/views/ui/Sparkline.d.ts +17 -0
  348. package/dist/views/ui/Sparkline.d.ts.map +1 -0
  349. package/dist/views/ui/Sparkline.js +96 -0
  350. package/dist/views/ui/Sparkline.js.map +1 -0
  351. package/dist/views/ui/SummaryItem.d.ts +23 -0
  352. package/dist/views/ui/SummaryItem.d.ts.map +1 -0
  353. package/dist/views/ui/SummaryItem.js +59 -0
  354. package/dist/views/ui/SummaryItem.js.map +1 -0
  355. package/dist/views/ui/ToneBadge.d.ts +18 -0
  356. package/dist/views/ui/ToneBadge.d.ts.map +1 -0
  357. package/dist/views/ui/ToneBadge.js +34 -0
  358. package/dist/views/ui/ToneBadge.js.map +1 -0
  359. package/dist/views/ui/badgeTone.d.ts +21 -0
  360. package/dist/views/ui/badgeTone.d.ts.map +1 -0
  361. package/dist/views/ui/badgeTone.js +33 -0
  362. package/dist/views/ui/badgeTone.js.map +1 -0
  363. package/dist/views/ui/index.d.ts +16 -0
  364. package/dist/views/ui/index.js +16 -0
  365. package/dist/views/ui/useCopyToClipboard.d.ts +12 -0
  366. package/dist/views/ui/useCopyToClipboard.d.ts.map +1 -0
  367. package/dist/views/ui/useCopyToClipboard.js +44 -0
  368. package/dist/views/ui/useCopyToClipboard.js.map +1 -0
  369. package/package.json +9 -5
  370. package/src/DevToolsModal.tsx +26 -179
  371. package/src/DevToolsModalImpl.tsx +60 -0
  372. package/src/data/createInProcessClient.ts +66 -0
  373. package/src/data/projectApi.test.ts +100 -0
  374. package/src/data/projectApi.ts +169 -0
  375. package/src/data/types.ts +54 -0
  376. package/src/data/useDevToolsClient.ts +59 -0
  377. package/src/index.ts +16 -13
  378. package/src/shell/DevToolsOverlay.tsx +84 -0
  379. package/src/shell/DevToolsPanel.tsx +192 -0
  380. package/src/shell/ShadowRoot.tsx +85 -0
  381. package/src/shell/SplitLayout.tsx +46 -0
  382. package/src/shell/registry.ts +51 -0
  383. package/src/shell/tabs/ActivityTab.test.tsx +34 -0
  384. package/src/shell/tabs/ActivityTab.tsx +161 -0
  385. package/src/shell/tabs/ContextTab.test.tsx +37 -0
  386. package/src/shell/tabs/ContextTab.tsx +157 -0
  387. package/src/shell/tabs/RawTab.test.tsx +43 -0
  388. package/src/shell/tabs/RawTab.tsx +123 -0
  389. package/src/shell/tabs/ThreadTab.tsx +220 -0
  390. package/src/shell/tabs/navSelection.ts +17 -0
  391. package/src/styles/panel.css +153 -0
  392. package/src/styles/panel.generated.ts +2 -0
  393. package/src/types.ts +0 -5
  394. package/src/utils/common.test.ts +30 -0
  395. package/src/utils/common.ts +101 -0
  396. package/src/utils/diff.test.ts +37 -0
  397. package/src/utils/diff.ts +41 -0
  398. package/src/utils/serialization.test.ts +31 -0
  399. package/src/utils/serialization.ts +4 -1
  400. package/src/views/activity/EventLogRow.tsx +108 -0
  401. package/src/views/activity/EventStreamPane.tsx +86 -0
  402. package/src/views/activity/OrphansPane.tsx +23 -0
  403. package/src/views/activity/RunDetailPane.tsx +128 -0
  404. package/src/views/activity/activityNodes.test.ts +24 -0
  405. package/src/views/activity/activityNodes.ts +179 -0
  406. package/src/views/activity/index.ts +10 -0
  407. package/src/views/attachments/AttachmentList.tsx +122 -0
  408. package/src/views/attachments/MediaPartView.tsx +77 -0
  409. package/src/views/attachments/formatBytes.ts +15 -0
  410. package/src/views/attachments/index.ts +4 -0
  411. package/src/views/attachments/parseAttachment.test.ts +37 -0
  412. package/src/views/attachments/parseAttachment.ts +85 -0
  413. package/src/views/attachments/types.ts +15 -0
  414. package/src/views/context/ContextDetail.tsx +37 -0
  415. package/src/views/context/ToolUIsView.tsx +44 -0
  416. package/src/views/context/contextNodes.test.ts +31 -0
  417. package/src/views/context/contextNodes.ts +66 -0
  418. package/src/views/context/index.ts +4 -0
  419. package/src/views/mcp/McpView.tsx +125 -0
  420. package/src/views/mcp/index.ts +1 -0
  421. package/src/views/mcp/parse.test.ts +63 -0
  422. package/src/views/mcp/parse.ts +65 -0
  423. package/src/views/mcp/render.test.tsx +34 -0
  424. package/src/views/mcp/types.ts +20 -0
  425. package/src/views/message/ChainOfThought.tsx +74 -0
  426. package/src/views/message/MessageDetailPane.tsx +26 -0
  427. package/src/views/message/MessageItem.tsx +94 -0
  428. package/src/views/message/MessageList.tsx +48 -0
  429. package/src/views/message/MessageMetrics.tsx +68 -0
  430. package/src/views/message/MessageTimeline.test.tsx +78 -0
  431. package/src/views/message/MessageTimeline.tsx +51 -0
  432. package/src/views/message/PartDisclosure.tsx +32 -0
  433. package/src/views/message/PartView.tsx +197 -0
  434. package/src/views/message/RoleLabel.tsx +22 -0
  435. package/src/views/message/StatusBadge.tsx +28 -0
  436. package/src/views/message/ToolCallView.tsx +151 -0
  437. package/src/views/message/TtftBar.tsx +44 -0
  438. package/src/views/message/index.ts +5 -0
  439. package/src/views/message/messagePlainText.test.ts +48 -0
  440. package/src/views/message/messagePlainText.ts +65 -0
  441. package/src/views/message/parse.test.ts +269 -0
  442. package/src/views/message/parse.ts +282 -0
  443. package/src/views/message/render.test.tsx +128 -0
  444. package/src/views/message/types.ts +137 -0
  445. package/src/views/model-context/ModelContextView.tsx +71 -0
  446. package/src/views/model-context/render.test.tsx +24 -0
  447. package/src/views/nav.ts +18 -0
  448. package/src/views/raw/RawDetail.tsx +39 -0
  449. package/src/views/raw/index.ts +3 -0
  450. package/src/views/raw/rawNodes.test.ts +38 -0
  451. package/src/views/raw/rawNodes.ts +107 -0
  452. package/src/views/raw/statePreview.tsx +188 -0
  453. package/src/views/runs/PayloadDiff.tsx +49 -0
  454. package/src/views/runs/parse.test.ts +152 -0
  455. package/src/views/runs/parse.ts +110 -0
  456. package/src/views/runs/types.ts +23 -0
  457. package/src/views/scopes/ScopesView.tsx +105 -0
  458. package/src/views/scopes/index.ts +1 -0
  459. package/src/views/scopes/parse.test.ts +35 -0
  460. package/src/views/scopes/parse.ts +30 -0
  461. package/src/views/scopes/render.test.tsx +35 -0
  462. package/src/views/scopes/types.ts +6 -0
  463. package/src/views/thread/ComposerAttachments.test.tsx +36 -0
  464. package/src/views/thread/ComposerAttachments.tsx +8 -0
  465. package/src/views/thread/ComposerFlags.test.tsx +23 -0
  466. package/src/views/thread/ComposerFlags.tsx +19 -0
  467. package/src/views/thread/ComposerQueue.test.tsx +20 -0
  468. package/src/views/thread/ComposerQueue.tsx +26 -0
  469. package/src/views/thread/ConversationList.tsx +127 -0
  470. package/src/views/thread/ConversationStatusDot.test.ts +86 -0
  471. package/src/views/thread/ConversationStatusDot.tsx +126 -0
  472. package/src/views/thread/ThreadDetails.tsx +85 -0
  473. package/src/views/thread/Transcript.test.tsx +56 -0
  474. package/src/views/thread/Transcript.tsx +332 -0
  475. package/src/views/thread/index.ts +19 -0
  476. package/src/views/thread/render.test.tsx +41 -0
  477. package/src/views/thread/resolve.ts +36 -0
  478. package/src/views/thread/types.ts +53 -0
  479. package/src/views/thread/utils.test.ts +72 -0
  480. package/src/views/thread/utils.ts +149 -0
  481. package/src/views/ui/CenteredMessage.tsx +7 -0
  482. package/src/views/ui/Chip.tsx +17 -0
  483. package/src/views/ui/ControlButton.tsx +15 -0
  484. package/src/views/ui/CopyButton.tsx +73 -0
  485. package/src/views/ui/EmptyState.tsx +7 -0
  486. package/src/views/ui/InfoCard.tsx +35 -0
  487. package/src/views/ui/JSONTree.test.tsx +29 -0
  488. package/src/views/ui/JSONTree.tsx +240 -0
  489. package/src/views/ui/PaneHeader.tsx +46 -0
  490. package/src/views/ui/RailTime.tsx +27 -0
  491. package/src/views/ui/SectionLabel.tsx +7 -0
  492. package/src/views/ui/SelectableRow.tsx +41 -0
  493. package/src/views/ui/Sparkline.tsx +49 -0
  494. package/src/views/ui/SummaryItem.tsx +25 -0
  495. package/src/views/ui/ToneBadge.tsx +26 -0
  496. package/src/views/ui/badgeTone.ts +40 -0
  497. package/src/views/ui/index.ts +20 -0
  498. package/src/views/ui/useCopyToClipboard.ts +27 -0
  499. package/dist/DevToolsFrame.d.ts +0 -11
  500. package/dist/DevToolsFrame.d.ts.map +0 -1
  501. package/dist/DevToolsFrame.js +0 -66
  502. package/dist/DevToolsFrame.js.map +0 -1
  503. package/dist/DevToolsHost.d.ts +0 -38
  504. package/dist/DevToolsHost.d.ts.map +0 -1
  505. package/dist/DevToolsHost.js +0 -93
  506. package/dist/DevToolsHost.js.map +0 -1
  507. package/dist/ExtensionHost.d.ts +0 -10
  508. package/dist/ExtensionHost.d.ts.map +0 -1
  509. package/dist/ExtensionHost.js +0 -42
  510. package/dist/ExtensionHost.js.map +0 -1
  511. package/dist/FrameClient.d.ts +0 -38
  512. package/dist/FrameClient.d.ts.map +0 -1
  513. package/dist/FrameClient.js +0 -53
  514. package/dist/FrameClient.js.map +0 -1
  515. package/dist/FrameHost.d.ts +0 -11
  516. package/dist/FrameHost.d.ts.map +0 -1
  517. package/dist/FrameHost.js +0 -26
  518. package/dist/FrameHost.js.map +0 -1
  519. package/dist/constants.d.ts +0 -6
  520. package/dist/constants.d.ts.map +0 -1
  521. package/dist/constants.js +0 -7
  522. package/dist/constants.js.map +0 -1
  523. package/dist/styles/DevToolsModal.styles.d.ts +0 -18
  524. package/dist/styles/DevToolsModal.styles.d.ts.map +0 -1
  525. package/dist/styles/DevToolsModal.styles.js +0 -120
  526. package/dist/styles/DevToolsModal.styles.js.map +0 -1
  527. package/src/DevToolsFrame.tsx +0 -55
  528. package/src/DevToolsHost.ts +0 -178
  529. package/src/ExtensionHost.ts +0 -57
  530. package/src/FrameClient.ts +0 -99
  531. package/src/FrameHost.ts +0 -31
  532. package/src/constants.ts +0 -2
  533. package/src/styles/DevToolsModal.styles.ts +0 -137
@@ -0,0 +1,269 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { parseMessage, parsePart, parsePartStatus, partKey } from "./parse";
3
+
4
+ describe("parsePartStatus", () => {
5
+ it("reads type and reason", () => {
6
+ expect(parsePartStatus({ type: "incomplete", reason: "error" })).toEqual({
7
+ type: "incomplete",
8
+ reason: "error",
9
+ });
10
+ });
11
+
12
+ it("returns undefined for non-records and typeless values", () => {
13
+ expect(parsePartStatus(undefined)).toBeUndefined();
14
+ expect(parsePartStatus({ reason: "error" })).toBeUndefined();
15
+ });
16
+ });
17
+
18
+ describe("parsePart", () => {
19
+ it("parses a text part with status", () => {
20
+ const part = parsePart({
21
+ type: "text",
22
+ text: "hello",
23
+ status: { type: "complete" },
24
+ });
25
+ expect(part).toEqual({
26
+ type: "text",
27
+ text: "hello",
28
+ status: { type: "complete" },
29
+ });
30
+ });
31
+
32
+ it("parses a completed tool-call with result", () => {
33
+ const part = parsePart({
34
+ type: "tool-call",
35
+ toolCallId: "call_1",
36
+ toolName: "search",
37
+ args: { query: "weather" },
38
+ argsText: '{"query":"weather"}',
39
+ result: { temperature: 20 },
40
+ status: { type: "complete" },
41
+ });
42
+ expect(part).toMatchObject({
43
+ type: "tool-call",
44
+ toolName: "search",
45
+ toolCallId: "call_1",
46
+ result: { temperature: 20 },
47
+ argsText: '{"query":"weather"}',
48
+ subMessageCount: 0,
49
+ });
50
+ });
51
+
52
+ it("surfaces a pending approval gate", () => {
53
+ const part = parsePart({
54
+ type: "tool-call",
55
+ toolCallId: "call_2",
56
+ toolName: "deleteFile",
57
+ args: {},
58
+ approval: { id: "appr_1" },
59
+ status: { type: "requires-action", reason: "interrupt" },
60
+ });
61
+ expect(part.type).toBe("tool-call");
62
+ if (part.type !== "tool-call") return;
63
+ expect(part.approval).toEqual({ id: "appr_1" });
64
+ expect(part.approval?.approved).toBeUndefined();
65
+ expect(part.status).toEqual({
66
+ type: "requires-action",
67
+ reason: "interrupt",
68
+ });
69
+ });
70
+
71
+ it("surfaces a human interrupt payload and error result", () => {
72
+ const part = parsePart({
73
+ type: "tool-call",
74
+ toolCallId: "call_3",
75
+ toolName: "askUser",
76
+ args: {},
77
+ isError: true,
78
+ result: "boom",
79
+ interrupt: { type: "human", payload: { question: "name?" } },
80
+ });
81
+ if (part.type !== "tool-call") throw new Error("expected tool-call");
82
+ expect(part.isError).toBe(true);
83
+ expect(part.result).toBe("boom");
84
+ expect(part.interrupt).toEqual({
85
+ type: "human",
86
+ payload: { question: "name?" },
87
+ });
88
+ });
89
+
90
+ it("counts nested sub-agent messages", () => {
91
+ const part = parsePart({
92
+ type: "tool-call",
93
+ toolCallId: "call_4",
94
+ toolName: "subAgent",
95
+ args: {},
96
+ messages: [{ role: "assistant" }, { role: "user" }],
97
+ });
98
+ if (part.type !== "tool-call") throw new Error("expected tool-call");
99
+ expect(part.subMessageCount).toBe(2);
100
+ });
101
+
102
+ it("falls back to unknown for unrecognized part types", () => {
103
+ const part = parsePart({ type: "mystery", foo: 1 });
104
+ expect(part.type).toBe("unknown");
105
+ if (part.type !== "unknown") return;
106
+ expect(part.rawType).toBe("mystery");
107
+ });
108
+
109
+ it("drops empty interrupt/approval objects so completed calls do not look pending", () => {
110
+ const part = parsePart({
111
+ type: "tool-call",
112
+ toolCallId: "c0",
113
+ toolName: "done",
114
+ args: {},
115
+ result: {},
116
+ interrupt: {},
117
+ approval: {},
118
+ status: { type: "complete" },
119
+ });
120
+ if (part.type !== "tool-call") throw new Error("expected tool-call");
121
+ expect(part.interrupt).toBeUndefined();
122
+ expect(part.approval).toBeUndefined();
123
+ });
124
+
125
+ it("preserves an empty argsText for finished zero-argument tool calls", () => {
126
+ const part = parsePart({
127
+ type: "tool-call",
128
+ toolCallId: "c5",
129
+ toolName: "noArgs",
130
+ args: {},
131
+ argsText: "",
132
+ status: { type: "complete" },
133
+ });
134
+ if (part.type !== "tool-call") throw new Error("expected tool-call");
135
+ expect(part.argsText).toBe("");
136
+ });
137
+ });
138
+
139
+ describe("partKey", () => {
140
+ it("keys tool-calls by toolCallId and other parts by index", () => {
141
+ expect(
142
+ partKey(
143
+ {
144
+ type: "tool-call",
145
+ toolCallId: "abc",
146
+ toolName: "x",
147
+ args: {},
148
+ subMessageCount: 0,
149
+ },
150
+ 3,
151
+ ),
152
+ ).toBe("tc:abc");
153
+ expect(partKey({ type: "text", text: "hi" }, 3)).toBe("p:3");
154
+ });
155
+ });
156
+
157
+ describe("parseMessage", () => {
158
+ it("parses an enriched assistant message end to end", () => {
159
+ const message = parseMessage(
160
+ {
161
+ id: "msg_1",
162
+ role: "assistant",
163
+ createdAt: "2026-06-08T10:00:00.000Z",
164
+ status: { type: "complete", reason: "stop" },
165
+ branchNumber: 2,
166
+ branchCount: 3,
167
+ index: 1,
168
+ isLast: true,
169
+ parts: [
170
+ { type: "reasoning", text: "thinking", status: { type: "complete" } },
171
+ {
172
+ type: "tool-call",
173
+ toolCallId: "c1",
174
+ toolName: "search",
175
+ args: {},
176
+ result: {},
177
+ status: { type: "complete" },
178
+ },
179
+ { type: "text", text: "done", status: { type: "complete" } },
180
+ ],
181
+ metadata: {
182
+ timing: {
183
+ streamStartTime: 1000,
184
+ firstTokenTime: 1200,
185
+ totalStreamTime: 800,
186
+ tokenCount: 40,
187
+ tokensPerSecond: 50,
188
+ totalChunks: 12,
189
+ toolCallCount: 1,
190
+ },
191
+ steps: [
192
+ { usage: { inputTokens: 100, outputTokens: 20 } },
193
+ { usage: { inputTokens: 30, outputTokens: 10 } },
194
+ ],
195
+ isOptimistic: false,
196
+ custom: {},
197
+ },
198
+ },
199
+ 0,
200
+ );
201
+
202
+ expect(message).not.toBeNull();
203
+ if (!message) return;
204
+ expect(message.id).toBe("msg_1");
205
+ expect(message.role).toBe("assistant");
206
+ expect(message.parts).toHaveLength(3);
207
+ expect(message.parts.map((p) => p.type)).toEqual([
208
+ "reasoning",
209
+ "tool-call",
210
+ "text",
211
+ ]);
212
+ expect(message.status).toEqual({ type: "complete", reason: "stop" });
213
+ expect(message.branchNumber).toBe(2);
214
+ expect(message.branchCount).toBe(3);
215
+ expect(message.index).toBe(1);
216
+ expect(message.timing?.firstTokenTime).toBe(1200);
217
+ expect(message.usage).toEqual({
218
+ inputTokens: 130,
219
+ outputTokens: 30,
220
+ stepCount: 2,
221
+ });
222
+ });
223
+
224
+ it("falls back to content[] when parts[] is absent (raw user message)", () => {
225
+ const message = parseMessage(
226
+ {
227
+ id: "msg_2",
228
+ role: "user",
229
+ createdAt: "2026-06-08T10:01:00.000Z",
230
+ content: [{ type: "text", text: "hi" }],
231
+ attachments: [{ name: "photo.png" }],
232
+ metadata: { custom: {} },
233
+ },
234
+ 0,
235
+ );
236
+ if (!message) return;
237
+ expect(message.parts).toHaveLength(1);
238
+ expect(message.parts[0]?.type).toBe("text");
239
+ expect(message.attachments).toEqual([
240
+ expect.objectContaining({ name: "photo.png" }),
241
+ ]);
242
+ expect(message.status).toBeUndefined();
243
+ expect(message.usage).toBeUndefined();
244
+ });
245
+
246
+ it("captures an incomplete message error", () => {
247
+ const message = parseMessage(
248
+ {
249
+ id: "msg_3",
250
+ role: "assistant",
251
+ status: { type: "incomplete", reason: "error", error: "rate limited" },
252
+ parts: [],
253
+ metadata: { custom: {} },
254
+ },
255
+ 0,
256
+ );
257
+ if (!message) return;
258
+ expect(message.status).toEqual({
259
+ type: "incomplete",
260
+ reason: "error",
261
+ error: "rate limited",
262
+ });
263
+ });
264
+
265
+ it("returns null for non-records", () => {
266
+ expect(parseMessage(null, 0)).toBeNull();
267
+ expect(parseMessage("nope", 0)).toBeNull();
268
+ });
269
+ });
@@ -0,0 +1,282 @@
1
+ import { parseAttachments } from "../attachments/parseAttachment";
2
+ import {
3
+ estimateBase64Bytes,
4
+ isSafeImagePreviewUrl,
5
+ } from "../attachments/formatBytes";
6
+ import { asBool, asNumber, asString, isRecord } from "../../utils/common";
7
+ import type {
8
+ MessagePreview,
9
+ MessageTimingPreview,
10
+ MessageUsagePreview,
11
+ PartPreview,
12
+ PartStatusPreview,
13
+ ToolCallApprovalPreview,
14
+ ToolCallInterruptPreview,
15
+ ToolCallPartPreview,
16
+ } from "./types";
17
+
18
+ export const partKey = (part: PartPreview, index: number) =>
19
+ part.type === "tool-call" && part.toolCallId
20
+ ? `tc:${part.toolCallId}`
21
+ : `p:${index}`;
22
+
23
+ export const parsePartStatus = (
24
+ value: unknown,
25
+ ): PartStatusPreview | undefined => {
26
+ if (!isRecord(value)) return undefined;
27
+ const type = asString(value.type);
28
+ if (!type) return undefined;
29
+ const reason = asString(value.reason);
30
+ return {
31
+ type,
32
+ ...(reason ? { reason } : {}),
33
+ ...("error" in value && value.error !== undefined
34
+ ? { error: value.error }
35
+ : {}),
36
+ };
37
+ };
38
+
39
+ const parseInterrupt = (
40
+ value: unknown,
41
+ ): ToolCallInterruptPreview | undefined => {
42
+ if (!isRecord(value)) return undefined;
43
+ const type = asString(value.type);
44
+ const result: ToolCallInterruptPreview = {
45
+ ...(type ? { type } : {}),
46
+ ...("payload" in value ? { payload: value.payload } : {}),
47
+ };
48
+ return Object.keys(result).length > 0 ? result : undefined;
49
+ };
50
+
51
+ const parseApproval = (value: unknown): ToolCallApprovalPreview | undefined => {
52
+ if (!isRecord(value)) return undefined;
53
+ const id = asString(value.id);
54
+ const approved = asBool(value.approved);
55
+ const reason = asString(value.reason);
56
+ const isAutomatic = asBool(value.isAutomatic);
57
+ const result: ToolCallApprovalPreview = {
58
+ ...(id ? { id } : {}),
59
+ ...(approved !== undefined ? { approved } : {}),
60
+ ...(reason ? { reason } : {}),
61
+ ...(isAutomatic !== undefined ? { isAutomatic } : {}),
62
+ };
63
+ return Object.keys(result).length > 0 ? result : undefined;
64
+ };
65
+
66
+ const parseToolCall = (
67
+ value: Record<string, unknown>,
68
+ base: { status?: PartStatusPreview },
69
+ ): ToolCallPartPreview => {
70
+ const argsText = asString(value.argsText);
71
+ const isError = asBool(value.isError);
72
+ const interrupt = parseInterrupt(value.interrupt);
73
+ const approval = parseApproval(value.approval);
74
+ const subMessageCount = Array.isArray(value.messages)
75
+ ? value.messages.length
76
+ : 0;
77
+
78
+ return {
79
+ type: "tool-call",
80
+ toolCallId: asString(value.toolCallId) ?? "",
81
+ toolName: asString(value.toolName) ?? "(unknown)",
82
+ args: value.args,
83
+ subMessageCount,
84
+ ...(argsText !== undefined ? { argsText } : {}),
85
+ ...("result" in value && value.result !== undefined
86
+ ? { result: value.result }
87
+ : {}),
88
+ ...(isError !== undefined ? { isError } : {}),
89
+ ...(value.artifact !== undefined ? { artifact: value.artifact } : {}),
90
+ ...(value.mcp !== undefined ? { mcp: value.mcp } : {}),
91
+ ...(value.modelContent !== undefined
92
+ ? { modelContent: value.modelContent }
93
+ : {}),
94
+ ...(interrupt ? { interrupt } : {}),
95
+ ...(approval ? { approval } : {}),
96
+ ...base,
97
+ };
98
+ };
99
+
100
+ export const parsePart = (value: unknown): PartPreview => {
101
+ if (!isRecord(value)) {
102
+ return { type: "unknown", rawType: typeof value, raw: value };
103
+ }
104
+
105
+ const status = parsePartStatus(value.status);
106
+ const base = status ? { status } : {};
107
+ const type = asString(value.type);
108
+
109
+ switch (type) {
110
+ case "text":
111
+ return { type: "text", text: asString(value.text) ?? "", ...base };
112
+ case "reasoning":
113
+ return { type: "reasoning", text: asString(value.text) ?? "", ...base };
114
+ case "source": {
115
+ const sourceType = asString(value.sourceType);
116
+ const title = asString(value.title);
117
+ const url = asString(value.url);
118
+ return {
119
+ type: "source",
120
+ ...(sourceType ? { sourceType } : {}),
121
+ ...(title ? { title } : {}),
122
+ ...(url ? { url } : {}),
123
+ ...base,
124
+ };
125
+ }
126
+ case "image": {
127
+ const filename = asString(value.filename);
128
+ const image = asString(value.image);
129
+ const previewUrl =
130
+ image && isSafeImagePreviewUrl(image) ? image : undefined;
131
+ const sizeBytes = previewUrl?.startsWith("data:")
132
+ ? estimateBase64Bytes(previewUrl.split(",", 2)[1] ?? previewUrl)
133
+ : undefined;
134
+ return {
135
+ type: "image",
136
+ ...(filename ? { filename } : {}),
137
+ ...(previewUrl ? { previewUrl } : {}),
138
+ ...(sizeBytes !== undefined ? { sizeBytes } : {}),
139
+ ...base,
140
+ };
141
+ }
142
+ case "file": {
143
+ const filename = asString(value.filename);
144
+ const mimeType = asString(value.mimeType);
145
+ const data = asString(value.data);
146
+ const sizeBytes = data ? estimateBase64Bytes(data) : undefined;
147
+ return {
148
+ type: "file",
149
+ ...(filename ? { filename } : {}),
150
+ ...(mimeType ? { mimeType } : {}),
151
+ ...(sizeBytes !== undefined ? { sizeBytes } : {}),
152
+ ...base,
153
+ };
154
+ }
155
+ case "audio": {
156
+ const audio = isRecord(value.audio) ? value.audio : undefined;
157
+ const format = asString(audio?.format);
158
+ const audioData = asString(audio?.data);
159
+ const sizeBytes = audioData ? estimateBase64Bytes(audioData) : undefined;
160
+ return {
161
+ type: "audio",
162
+ ...(format ? { format } : {}),
163
+ ...(sizeBytes !== undefined ? { sizeBytes } : {}),
164
+ ...base,
165
+ };
166
+ }
167
+ case "data": {
168
+ const name = asString(value.name);
169
+ return {
170
+ type: "data",
171
+ data: value.data,
172
+ ...(name ? { name } : {}),
173
+ ...base,
174
+ };
175
+ }
176
+ case "generative-ui":
177
+ return { type: "generative-ui", spec: value.spec, ...base };
178
+ case "tool-call":
179
+ return parseToolCall(value, base);
180
+ default:
181
+ return {
182
+ type: "unknown",
183
+ rawType: type ?? typeof value,
184
+ raw: value,
185
+ ...base,
186
+ };
187
+ }
188
+ };
189
+
190
+ const parseTiming = (value: unknown): MessageTimingPreview | undefined => {
191
+ if (!isRecord(value)) return undefined;
192
+
193
+ const streamStartTime = asNumber(value.streamStartTime);
194
+ const firstTokenTime = asNumber(value.firstTokenTime);
195
+ const totalStreamTime = asNumber(value.totalStreamTime);
196
+ const tokenCount = asNumber(value.tokenCount);
197
+ const tokensPerSecond = asNumber(value.tokensPerSecond);
198
+ const totalChunks = asNumber(value.totalChunks);
199
+ const toolCallCount = asNumber(value.toolCallCount);
200
+
201
+ const result: MessageTimingPreview = {
202
+ ...(streamStartTime !== undefined ? { streamStartTime } : {}),
203
+ ...(firstTokenTime !== undefined ? { firstTokenTime } : {}),
204
+ ...(totalStreamTime !== undefined ? { totalStreamTime } : {}),
205
+ ...(tokenCount !== undefined ? { tokenCount } : {}),
206
+ ...(tokensPerSecond !== undefined ? { tokensPerSecond } : {}),
207
+ ...(totalChunks !== undefined ? { totalChunks } : {}),
208
+ ...(toolCallCount !== undefined ? { toolCallCount } : {}),
209
+ };
210
+ return Object.keys(result).length > 0 ? result : undefined;
211
+ };
212
+
213
+ const parseUsage = (value: unknown): MessageUsagePreview | undefined => {
214
+ if (!Array.isArray(value) || value.length === 0) return undefined;
215
+
216
+ let inputTokens = 0;
217
+ let outputTokens = 0;
218
+ let sawUsage = false;
219
+
220
+ for (const step of value) {
221
+ if (!isRecord(step) || !isRecord(step.usage)) continue;
222
+ const input = asNumber(step.usage.inputTokens);
223
+ const output = asNumber(step.usage.outputTokens);
224
+ if (input !== undefined) {
225
+ inputTokens += input;
226
+ sawUsage = true;
227
+ }
228
+ if (output !== undefined) {
229
+ outputTokens += output;
230
+ sawUsage = true;
231
+ }
232
+ }
233
+
234
+ if (!sawUsage) return undefined;
235
+ return { inputTokens, outputTokens, stepCount: value.length };
236
+ };
237
+
238
+ export const parseMessage = (
239
+ value: unknown,
240
+ index: number,
241
+ ): MessagePreview | null => {
242
+ if (!isRecord(value)) return null;
243
+
244
+ const id = asString(value.id) ?? `message-${index}`;
245
+ const role = asString(value.role) ?? "unknown";
246
+
247
+ const rawParts = Array.isArray(value.parts)
248
+ ? value.parts
249
+ : Array.isArray(value.content)
250
+ ? value.content
251
+ : [];
252
+ const parts = rawParts.map((part) => parsePart(part));
253
+
254
+ const status = parsePartStatus(value.status);
255
+ const metadata = isRecord(value.metadata) ? value.metadata : undefined;
256
+ const timing = parseTiming(metadata?.timing);
257
+ const usage = parseUsage(metadata?.steps);
258
+ const submittedFeedback = isRecord(metadata?.submittedFeedback)
259
+ ? asString(metadata.submittedFeedback.type)
260
+ : undefined;
261
+ const isOptimistic = asBool(metadata?.isOptimistic);
262
+ const createdAt = asString(value.createdAt);
263
+ const branchNumber = asNumber(value.branchNumber);
264
+ const branchCount = asNumber(value.branchCount);
265
+ const messageIndex = asNumber(value.index);
266
+
267
+ return {
268
+ id,
269
+ role,
270
+ parts,
271
+ attachments: parseAttachments(value.attachments),
272
+ ...(createdAt ? { createdAt } : {}),
273
+ ...(status ? { status } : {}),
274
+ ...(timing ? { timing } : {}),
275
+ ...(usage ? { usage } : {}),
276
+ ...(branchNumber !== undefined ? { branchNumber } : {}),
277
+ ...(branchCount !== undefined ? { branchCount } : {}),
278
+ ...(messageIndex !== undefined ? { index: messageIndex } : {}),
279
+ ...(isOptimistic !== undefined ? { isOptimistic } : {}),
280
+ ...(submittedFeedback ? { submittedFeedback } : {}),
281
+ };
282
+ };
@@ -0,0 +1,128 @@
1
+ import { renderToStaticMarkup } from "react-dom/server";
2
+ import { describe, expect, it } from "vitest";
3
+ import { MessageItem } from "./MessageItem";
4
+ import { MessageList } from "./MessageList";
5
+ import { parseMessage } from "./parse";
6
+ import type { MessagePreview } from "./types";
7
+
8
+ const parsed = (value: unknown): MessagePreview => {
9
+ const message = parseMessage(value, 0);
10
+ if (!message) throw new Error("fixture failed to parse");
11
+ return message;
12
+ };
13
+
14
+ describe("MessageItem rendering", () => {
15
+ it("renders reasoning + tool-call grouped as chain of thought, plus text", () => {
16
+ const message = parsed({
17
+ id: "m1",
18
+ role: "assistant",
19
+ status: { type: "complete", reason: "stop" },
20
+ branchNumber: 1,
21
+ branchCount: 2,
22
+ parts: [
23
+ {
24
+ type: "reasoning",
25
+ text: "let me think",
26
+ status: { type: "complete" },
27
+ },
28
+ {
29
+ type: "tool-call",
30
+ toolCallId: "c1",
31
+ toolName: "getWeather",
32
+ args: { city: "SF" },
33
+ result: { temp: 18 },
34
+ status: { type: "complete" },
35
+ },
36
+ {
37
+ type: "text",
38
+ text: "It is 18 degrees.",
39
+ status: { type: "complete" },
40
+ },
41
+ ],
42
+ metadata: {
43
+ timing: {
44
+ streamStartTime: 0,
45
+ firstTokenTime: 150,
46
+ totalStreamTime: 900,
47
+ },
48
+ steps: [{ usage: { inputTokens: 50, outputTokens: 12 } }],
49
+ custom: {},
50
+ },
51
+ });
52
+
53
+ const html = renderToStaticMarkup(<MessageItem message={message} />);
54
+ expect(html).toContain("getWeather");
55
+ expect(html).toContain("cot");
56
+ expect(html).toContain("It is 18 degrees.");
57
+ expect(html).toContain("branch 1/2");
58
+ expect(html).toContain("TTFT");
59
+ });
60
+
61
+ it("renders interleaved tool and text parts in order", () => {
62
+ const message = parsed({
63
+ id: "m3",
64
+ role: "assistant",
65
+ status: { type: "complete", reason: "stop" },
66
+ parts: [
67
+ {
68
+ type: "tool-call",
69
+ toolCallId: "c1",
70
+ toolName: "search",
71
+ args: { q: "x" },
72
+ status: { type: "complete" },
73
+ },
74
+ { type: "text", text: "Partial update.", status: { type: "complete" } },
75
+ {
76
+ type: "tool-call",
77
+ toolCallId: "c2",
78
+ toolName: "summarize",
79
+ args: {},
80
+ status: { type: "complete" },
81
+ },
82
+ {
83
+ type: "text",
84
+ text: "Done.",
85
+ status: { type: "complete" },
86
+ },
87
+ ],
88
+ metadata: { custom: {} },
89
+ });
90
+
91
+ const html = renderToStaticMarkup(<MessageItem message={message} />);
92
+ expect(html.indexOf("search")).toBeLessThan(
93
+ html.indexOf("Partial update."),
94
+ );
95
+ expect(html.indexOf("Partial update.")).toBeLessThan(
96
+ html.indexOf("summarize"),
97
+ );
98
+ expect(html.indexOf("summarize")).toBeLessThan(html.indexOf("Done."));
99
+ });
100
+
101
+ it("renders a pending approval gate prominently", () => {
102
+ const message = parsed({
103
+ id: "m2",
104
+ role: "assistant",
105
+ status: { type: "requires-action", reason: "tool-calls" },
106
+ parts: [
107
+ {
108
+ type: "tool-call",
109
+ toolCallId: "c2",
110
+ toolName: "deleteAll",
111
+ args: {},
112
+ approval: { id: "appr_9" },
113
+ status: { type: "requires-action", reason: "interrupt" },
114
+ },
115
+ ],
116
+ metadata: { custom: {} },
117
+ });
118
+
119
+ const html = renderToStaticMarkup(<MessageItem message={message} />);
120
+ expect(html).toContain("Awaiting approval");
121
+ expect(html).toContain("appr_9");
122
+ });
123
+
124
+ it("renders an empty message list placeholder", () => {
125
+ const html = renderToStaticMarkup(<MessageList messages={[]} />);
126
+ expect(html).toContain("No messages");
127
+ });
128
+ });