@agent-native/toolkit 0.213.1 → 0.216.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 (222) hide show
  1. package/agent-native.eject.json +9 -0
  2. package/dist/app/agentkit/react/chat.d.ts.map +1 -1
  3. package/dist/app/agentkit/react/chat.js +2 -2
  4. package/dist/app/agentkit/react/chat.js.map +1 -1
  5. package/dist/app/agentkit/react/components.d.ts +4 -1
  6. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  7. package/dist/app/agentkit/react/components.js +73 -13
  8. package/dist/app/agentkit/react/components.js.map +1 -1
  9. package/dist/app/agentkit/react/context.d.ts +19 -1
  10. package/dist/app/agentkit/react/context.d.ts.map +1 -1
  11. package/dist/app/agentkit/react/context.js +7 -1
  12. package/dist/app/agentkit/react/context.js.map +1 -1
  13. package/dist/app/agentkit/react/index.d.ts +2 -0
  14. package/dist/app/agentkit/react/index.d.ts.map +1 -1
  15. package/dist/app/agentkit/react/index.js +2 -0
  16. package/dist/app/agentkit/react/index.js.map +1 -1
  17. package/dist/app/agentkit/react/message-content-order.d.ts +21 -0
  18. package/dist/app/agentkit/react/message-content-order.d.ts.map +1 -0
  19. package/dist/app/agentkit/react/message-content-order.js +173 -0
  20. package/dist/app/agentkit/react/message-content-order.js.map +1 -0
  21. package/dist/app/agentkit/react/root.d.ts +6 -1
  22. package/dist/app/agentkit/react/root.d.ts.map +1 -1
  23. package/dist/app/agentkit/react/root.js +2 -2
  24. package/dist/app/agentkit/react/root.js.map +1 -1
  25. package/dist/app/agentkit/react/styles.css +45 -8
  26. package/dist/app/agentkit/react/transcript-layout.d.ts +51 -0
  27. package/dist/app/agentkit/react/transcript-layout.d.ts.map +1 -0
  28. package/dist/app/agentkit/react/transcript-layout.js +34 -0
  29. package/dist/app/agentkit/react/transcript-layout.js.map +1 -0
  30. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  31. package/dist/app/chat/AgentKitAssistantChat.js +9 -4
  32. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  33. package/dist/app/chat/AgentPanel.d.ts +16 -2
  34. package/dist/app/chat/AgentPanel.d.ts.map +1 -1
  35. package/dist/app/chat/AgentPanel.js +26 -5
  36. package/dist/app/chat/AgentPanel.js.map +1 -1
  37. package/dist/app/chat/chat/index.d.ts +2 -0
  38. package/dist/app/chat/chat/index.d.ts.map +1 -1
  39. package/dist/app/chat/chat/index.js.map +1 -1
  40. package/dist/app/chat/chat/surface-types.d.ts +15 -0
  41. package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
  42. package/dist/app/chat/chat/surface-types.js.map +1 -1
  43. package/dist/app/chat/chat/tool-call-display.d.ts +7 -2
  44. package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
  45. package/dist/app/chat/chat/tool-call-display.js +10 -9
  46. package/dist/app/chat/chat/tool-call-display.js.map +1 -1
  47. package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts.map +1 -1
  48. package/dist/app/chat/chat/widgets/DataChartRenderer.js +300 -33
  49. package/dist/app/chat/chat/widgets/DataChartRenderer.js.map +1 -1
  50. package/dist/app/chat/chat/widgets/DataChartWidget.d.ts +3 -2
  51. package/dist/app/chat/chat/widgets/DataChartWidget.d.ts.map +1 -1
  52. package/dist/app/chat/chat/widgets/DataChartWidget.js +18 -9
  53. package/dist/app/chat/chat/widgets/DataChartWidget.js.map +1 -1
  54. package/dist/app/chat/chat/widgets/DataInsightsWidget.js +1 -1
  55. package/dist/app/chat/chat/widgets/DataInsightsWidget.js.map +1 -1
  56. package/dist/app/chat/chat/widgets/DataTableWidget.d.ts +3 -2
  57. package/dist/app/chat/chat/widgets/DataTableWidget.d.ts.map +1 -1
  58. package/dist/app/chat/chat/widgets/DataTableWidget.js +10 -66
  59. package/dist/app/chat/chat/widgets/DataTableWidget.js.map +1 -1
  60. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts +11 -0
  61. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts.map +1 -0
  62. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js +133 -0
  63. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js.map +1 -0
  64. package/dist/app/chat/chat/widgets/builtin-tool-renderers.js +2 -2
  65. package/dist/app/chat/chat/widgets/builtin-tool-renderers.js.map +1 -1
  66. package/dist/app/chat/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  67. package/dist/app/chat/mcp-apps/McpAppRenderer.js +6 -5
  68. package/dist/app/chat/mcp-apps/McpAppRenderer.js.map +1 -1
  69. package/dist/app/chat/mcp-apps/mcp-app-theme.d.ts +2 -0
  70. package/dist/app/chat/mcp-apps/mcp-app-theme.d.ts.map +1 -0
  71. package/dist/app/chat/mcp-apps/mcp-app-theme.js +11 -0
  72. package/dist/app/chat/mcp-apps/mcp-app-theme.js.map +1 -0
  73. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  74. package/dist/app/i18n/catalogs/ar-SA.js +9 -0
  75. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  76. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  77. package/dist/app/i18n/catalogs/de-DE.js +9 -0
  78. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  79. package/dist/app/i18n/catalogs/en-US.d.ts +9 -0
  80. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  81. package/dist/app/i18n/catalogs/en-US.js +9 -0
  82. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  83. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  84. package/dist/app/i18n/catalogs/es-ES.js +9 -0
  85. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  86. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  87. package/dist/app/i18n/catalogs/fr-FR.js +9 -0
  88. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  89. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  90. package/dist/app/i18n/catalogs/hi-IN.js +9 -0
  91. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  92. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  93. package/dist/app/i18n/catalogs/ja-JP.js +9 -0
  94. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  95. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  96. package/dist/app/i18n/catalogs/ko-KR.js +9 -0
  97. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  98. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  99. package/dist/app/i18n/catalogs/pt-BR.js +9 -0
  100. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  101. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  102. package/dist/app/i18n/catalogs/zh-CN.js +9 -0
  103. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  104. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  105. package/dist/app/i18n/catalogs/zh-TW.js +9 -0
  106. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  107. package/dist/app/providers/AppProviders.d.ts +2 -1
  108. package/dist/app/providers/AppProviders.d.ts.map +1 -1
  109. package/dist/app/providers/AppProviders.js +4 -3
  110. package/dist/app/providers/AppProviders.js.map +1 -1
  111. package/dist/app/providers/ThemeFamilySync.d.ts +2 -0
  112. package/dist/app/providers/ThemeFamilySync.d.ts.map +1 -0
  113. package/dist/app/providers/ThemeFamilySync.js +6 -0
  114. package/dist/app/providers/ThemeFamilySync.js.map +1 -0
  115. package/dist/app/settings/account/PreferencesSettings.d.ts.map +1 -1
  116. package/dist/app/settings/account/PreferencesSettings.js +2 -1
  117. package/dist/app/settings/account/PreferencesSettings.js.map +1 -1
  118. package/dist/app/settings/account/ThemeFamilySettings.d.ts +2 -0
  119. package/dist/app/settings/account/ThemeFamilySettings.d.ts.map +1 -0
  120. package/dist/app/settings/account/ThemeFamilySettings.js +31 -0
  121. package/dist/app/settings/account/ThemeFamilySettings.js.map +1 -0
  122. package/dist/app/settings/account/search-entries.d.ts.map +1 -1
  123. package/dist/app/settings/account/search-entries.js +6 -0
  124. package/dist/app/settings/account/search-entries.js.map +1 -1
  125. package/dist/app/shared/DefaultSpinner.d.ts.map +1 -1
  126. package/dist/app/shared/DefaultSpinner.js +4 -5
  127. package/dist/app/shared/DefaultSpinner.js.map +1 -1
  128. package/dist/app/shared/ErrorBoundary.d.ts +1 -0
  129. package/dist/app/shared/ErrorBoundary.d.ts.map +1 -1
  130. package/dist/app/shared/ErrorBoundary.js +27 -12
  131. package/dist/app/shared/ErrorBoundary.js.map +1 -1
  132. package/dist/app/shared/PoweredByBadge.d.ts.map +1 -1
  133. package/dist/app/shared/PoweredByBadge.js +8 -49
  134. package/dist/app/shared/PoweredByBadge.js.map +1 -1
  135. package/dist/app/styles/agent-conversation.css +0 -23
  136. package/dist/app/styles/agent-native.css +1 -1
  137. package/dist/app/styles/theme-families.css +66 -0
  138. package/dist/composer/ComposerContextBanner.d.ts +24 -0
  139. package/dist/composer/ComposerContextBanner.d.ts.map +1 -0
  140. package/dist/composer/ComposerContextBanner.js +9 -0
  141. package/dist/composer/ComposerContextBanner.js.map +1 -0
  142. package/dist/composer/ComposerModePicker.d.ts +26 -0
  143. package/dist/composer/ComposerModePicker.d.ts.map +1 -0
  144. package/dist/composer/ComposerModePicker.js +14 -0
  145. package/dist/composer/ComposerModePicker.js.map +1 -0
  146. package/dist/composer/index.d.ts +2 -0
  147. package/dist/composer/index.d.ts.map +1 -1
  148. package/dist/composer/index.js +2 -0
  149. package/dist/composer/index.js.map +1 -1
  150. package/dist/styles.css +1 -0
  151. package/package.json +3 -3
  152. package/src/app/agentkit/react/chat.tsx +2 -0
  153. package/src/app/agentkit/react/components.interaction.spec.tsx +107 -0
  154. package/src/app/agentkit/react/components.spec.tsx +40 -1
  155. package/src/app/agentkit/react/components.tsx +186 -31
  156. package/src/app/agentkit/react/context.tsx +29 -0
  157. package/src/app/agentkit/react/index.ts +2 -0
  158. package/src/app/agentkit/react/lifecycle.spec.tsx +289 -4
  159. package/src/app/agentkit/react/message-content-order.spec.ts +190 -0
  160. package/src/app/agentkit/react/message-content-order.ts +246 -0
  161. package/src/app/agentkit/react/root.tsx +11 -0
  162. package/src/app/agentkit/react/styles.css +45 -8
  163. package/src/app/agentkit/react/transcript-layout.spec.ts +72 -0
  164. package/src/app/agentkit/react/transcript-layout.ts +97 -0
  165. package/src/app/auth/AuthPage.attribution.spec.tsx +6 -0
  166. package/src/app/chat/AgentKitAssistantChat.spec.tsx +58 -0
  167. package/src/app/chat/AgentKitAssistantChat.tsx +15 -1
  168. package/src/app/chat/AgentPanel.header.spec.ts +72 -14
  169. package/src/app/chat/AgentPanel.page-header-share.spec.tsx +101 -0
  170. package/src/app/chat/AgentPanel.tsx +111 -55
  171. package/src/app/chat/chat/index.ts +2 -0
  172. package/src/app/chat/chat/surface-types.ts +18 -0
  173. package/src/app/chat/chat/tool-call-display.spec.tsx +83 -0
  174. package/src/app/chat/chat/tool-call-display.tsx +40 -8
  175. package/src/app/chat/chat/widgets/DataChartRenderer.spec.tsx +849 -0
  176. package/src/app/chat/chat/widgets/DataChartRenderer.tsx +567 -72
  177. package/src/app/chat/chat/widgets/DataChartWidget.spec.tsx +432 -0
  178. package/src/app/chat/chat/widgets/DataChartWidget.ssr.spec.tsx +25 -0
  179. package/src/app/chat/chat/widgets/DataChartWidget.tsx +58 -33
  180. package/src/app/chat/chat/widgets/DataInsightsWidget.tsx +2 -2
  181. package/src/app/chat/chat/widgets/DataTableWidget.spec.tsx +37 -3
  182. package/src/app/chat/chat/widgets/DataTableWidget.tsx +22 -86
  183. package/src/app/chat/chat/widgets/DataWidgetDisplayControls.tsx +321 -0
  184. package/src/app/chat/chat/widgets/builtin-tool-renderers.tsx +5 -5
  185. package/src/app/chat/mcp-apps/McpAppRenderer.tsx +10 -5
  186. package/src/app/chat/mcp-apps/mcp-app-theme.spec.ts +30 -0
  187. package/src/app/chat/mcp-apps/mcp-app-theme.ts +11 -0
  188. package/src/app/i18n/catalogs/ar-SA.ts +10 -0
  189. package/src/app/i18n/catalogs/de-DE.ts +11 -0
  190. package/src/app/i18n/catalogs/en-US.ts +11 -0
  191. package/src/app/i18n/catalogs/es-ES.ts +12 -0
  192. package/src/app/i18n/catalogs/fr-FR.ts +12 -0
  193. package/src/app/i18n/catalogs/hi-IN.ts +10 -0
  194. package/src/app/i18n/catalogs/ja-JP.ts +11 -0
  195. package/src/app/i18n/catalogs/ko-KR.ts +10 -0
  196. package/src/app/i18n/catalogs/pt-BR.ts +11 -0
  197. package/src/app/i18n/catalogs/zh-CN.ts +9 -0
  198. package/src/app/i18n/catalogs/zh-TW.ts +10 -0
  199. package/src/app/providers/AppProviders.spec.tsx +71 -0
  200. package/src/app/providers/AppProviders.tsx +10 -0
  201. package/src/app/providers/ThemeFamilySync.tsx +6 -0
  202. package/src/app/settings/account/PreferencesSettings.spec.tsx +13 -2
  203. package/src/app/settings/account/PreferencesSettings.tsx +8 -0
  204. package/src/app/settings/account/ThemeFamilySettings.tsx +61 -0
  205. package/src/app/settings/account/search-entries.ts +6 -0
  206. package/src/app/shared/DefaultSpinner.spec.tsx +14 -0
  207. package/src/app/shared/DefaultSpinner.tsx +4 -5
  208. package/src/app/shared/ErrorBoundary.spec.tsx +45 -2
  209. package/src/app/shared/ErrorBoundary.tsx +37 -12
  210. package/src/app/shared/PoweredByBadge.spec.tsx +16 -0
  211. package/src/app/shared/PoweredByBadge.tsx +8 -50
  212. package/src/app/styles/agent-conversation.css +0 -23
  213. package/src/app/styles/agent-native.css +1 -1
  214. package/src/app/styles/agent-native.spec.ts +18 -0
  215. package/src/app/styles/theme-families.css +66 -0
  216. package/src/app/styles/theme-families.spec.ts +29 -0
  217. package/src/composer/ComposerContextBanner.spec.tsx +100 -0
  218. package/src/composer/ComposerContextBanner.tsx +77 -0
  219. package/src/composer/ComposerModePicker.spec.tsx +101 -0
  220. package/src/composer/ComposerModePicker.tsx +98 -0
  221. package/src/composer/index.ts +9 -0
  222. package/src/styles.css +1 -0
@@ -157,6 +157,211 @@ afterEach(async () => {
157
157
  });
158
158
 
159
159
  describe("AgentChat lifecycle", () => {
160
+ it("renders host-ordered text around an attached widget", async () => {
161
+ const threadId = "thread-host-content-order";
162
+ const message = {
163
+ id: "assistant-host-content-order",
164
+ role: "assistant" as const,
165
+ status: "complete" as const,
166
+ parts: [{ type: "text" as const, text: "Intro. Summary." }],
167
+ };
168
+ const widget = {
169
+ id: "chart-host-content-order",
170
+ kind: "test-chart",
171
+ data: {},
172
+ };
173
+ const thread = {
174
+ ...createAgentThreadState(threadId),
175
+ messages: [message],
176
+ widgets: { [widget.id]: widget },
177
+ widgetMessageIds: { [widget.id]: message.id },
178
+ };
179
+ const observable = observableController({
180
+ connection: "connected",
181
+ capabilities: {},
182
+ capabilitiesStatus: "ready",
183
+ threads: { [threadId]: thread },
184
+ revision: 0,
185
+ });
186
+ const tree = mount();
187
+
188
+ const renderChat = (enablePostWidgetText: boolean) => (
189
+ <AgentKitProvider
190
+ controller={observable.controller}
191
+ threadId={threadId}
192
+ resolveMessageContentOrder={({ parts, widgets }) => {
193
+ const part = parts[0];
194
+ const chart = widgets[0];
195
+ if (part?.type !== "text" || !chart) return [];
196
+ return [
197
+ {
198
+ key: "intro",
199
+ type: "part",
200
+ part: { ...part, text: "Intro. " },
201
+ },
202
+ { key: chart.id, type: "widget", widget: chart },
203
+ {
204
+ key: "summary",
205
+ type: "part",
206
+ part: { ...part, text: "Summary." },
207
+ },
208
+ ];
209
+ }}
210
+ registry={{
211
+ widgets: {
212
+ "test-chart": () => <span data-chart-result>Chart</span>,
213
+ },
214
+ }}
215
+ slots={{
216
+ text: ({ value }) => (
217
+ <div className="agentkit-markdown" data-chat-part>
218
+ <p>{value.text}</p>
219
+ </div>
220
+ ),
221
+ }}
222
+ >
223
+ <AgentKitChat
224
+ composer={false}
225
+ transcriptLayout={
226
+ enablePostWidgetText
227
+ ? {
228
+ postWidgetText: {
229
+ tone: "muted",
230
+ paragraphPaddingBlock: 4,
231
+ },
232
+ }
233
+ : undefined
234
+ }
235
+ />
236
+ </AgentKitProvider>
237
+ );
238
+ await tree.render(renderChat(true));
239
+
240
+ const content = tree.container.querySelector(
241
+ `[data-message-id="${message.id}"] .agentkit-message-content`,
242
+ );
243
+ const transcriptContent = tree.container.querySelector(
244
+ ".agentkit-transcript-content",
245
+ );
246
+ const renderedOrder = [...(content?.children ?? [])].map((element) =>
247
+ element.hasAttribute("data-post-widget-text")
248
+ ? element.textContent
249
+ : element.hasAttribute("data-chat-part")
250
+ ? element.textContent
251
+ : element.hasAttribute("data-chart-result")
252
+ ? "chart"
253
+ : "other",
254
+ );
255
+ expect(renderedOrder).toEqual(["Intro. ", "chart", "Summary."]);
256
+ expect(content?.children[0].hasAttribute("data-post-widget-text")).toBe(
257
+ false,
258
+ );
259
+ expect(content?.children[2].hasAttribute("data-post-widget-text")).toBe(
260
+ true,
261
+ );
262
+ expect(
263
+ content?.children[2].querySelector(".agentkit-markdown p"),
264
+ ).not.toBeNull();
265
+ expect(transcriptContent?.getAttribute("style")).toContain(
266
+ "--agentkit-transcript-post-widget-text-color: var(--agentkit-text-muted)",
267
+ );
268
+ expect(transcriptContent?.getAttribute("style")).toContain(
269
+ "--agentkit-transcript-post-widget-paragraph-padding-block: 4px",
270
+ );
271
+
272
+ await tree.render(renderChat(false));
273
+ const defaultContent = tree.container.querySelector(
274
+ `[data-message-id="${message.id}"] .agentkit-message-content`,
275
+ );
276
+ expect(
277
+ [...(defaultContent?.children ?? [])].some((element) =>
278
+ element.hasAttribute("data-post-widget-text"),
279
+ ),
280
+ ).toBe(false);
281
+ await tree.unmount();
282
+ });
283
+
284
+ it("styles assistant text after an embedded widget part", async () => {
285
+ const threadId = "thread-embedded-content-order";
286
+ const widget = {
287
+ id: "chart-embedded-content-order",
288
+ kind: "test-chart",
289
+ data: {},
290
+ };
291
+ const message = {
292
+ id: "assistant-embedded-content-order",
293
+ role: "assistant" as const,
294
+ status: "complete" as const,
295
+ parts: [
296
+ { type: "text" as const, text: "Intro." },
297
+ { type: "widget" as const, widget },
298
+ { type: "text" as const, text: "Summary." },
299
+ ],
300
+ };
301
+ const thread = {
302
+ ...createAgentThreadState(threadId),
303
+ messages: [message],
304
+ };
305
+ const observable = observableController({
306
+ connection: "connected",
307
+ capabilities: {},
308
+ capabilitiesStatus: "ready",
309
+ threads: { [threadId]: thread },
310
+ revision: 0,
311
+ });
312
+ const tree = mount();
313
+
314
+ await tree.render(
315
+ <AgentKitProvider
316
+ controller={observable.controller}
317
+ threadId={threadId}
318
+ registry={{
319
+ widgets: {
320
+ "test-chart": () => <span data-chart-result>Chart</span>,
321
+ },
322
+ }}
323
+ slots={{
324
+ text: ({ value }) => (
325
+ <div data-chat-part>
326
+ <p>{value.text}</p>
327
+ </div>
328
+ ),
329
+ }}
330
+ >
331
+ <AgentKitChat
332
+ composer={false}
333
+ transcriptLayout={{
334
+ postWidgetText: {
335
+ tone: "muted",
336
+ paragraphPaddingBlock: 4,
337
+ },
338
+ }}
339
+ />
340
+ </AgentKitProvider>,
341
+ );
342
+
343
+ const content = tree.container.querySelector(
344
+ `[data-message-id="${message.id}"] .agentkit-message-content`,
345
+ );
346
+ const renderedOrder = [...(content?.children ?? [])].map((element) =>
347
+ element.hasAttribute("data-post-widget-text")
348
+ ? element.textContent
349
+ : element.hasAttribute("data-chat-part")
350
+ ? element.textContent
351
+ : element.hasAttribute("data-chart-result")
352
+ ? "chart"
353
+ : "other",
354
+ );
355
+ expect(renderedOrder).toEqual(["Intro.", "chart", "Summary."]);
356
+ expect(content?.children[0].hasAttribute("data-post-widget-text")).toBe(
357
+ false,
358
+ );
359
+ expect(content?.children[2].hasAttribute("data-post-widget-text")).toBe(
360
+ true,
361
+ );
362
+ await tree.unmount();
363
+ });
364
+
160
365
  it("keeps partial transcript updates out of live announcements", async () => {
161
366
  const threadId = "thread-accessible-transcript";
162
367
  const thread = {
@@ -1609,7 +1814,7 @@ describe("AgentChat lifecycle", () => {
1609
1814
  await tree.unmount();
1610
1815
  });
1611
1816
 
1612
- it("preserves visible reasoning without a Thinking history row", async () => {
1817
+ it("preserves visible reasoning and applies the worked-row layout", async () => {
1613
1818
  const threadId = "thread-reasoning-work";
1614
1819
  const runId = "run-reasoning-work";
1615
1820
  const base = (sequence: number, seconds: number = sequence) => ({
@@ -1694,10 +1899,24 @@ describe("AgentChat lifecycle", () => {
1694
1899
  });
1695
1900
  const observable = observableController(snapshot(0));
1696
1901
  const tree = mount();
1902
+ const workedForRowLayout = {
1903
+ chevronSize: 12,
1904
+ iconGap: 8,
1905
+ marginBlockStart: -12,
1906
+ fontSize: 13,
1907
+ lineHeight: 16,
1908
+ paddingBlock: 2,
1909
+ paddingInline: 4,
1910
+ };
1911
+ const formatWorkedForDuration = (durationMs: number) =>
1912
+ `${Math.round(durationMs / 1000)} seconds`;
1697
1913
  await tree.render(
1698
- <AgentKitProvider
1914
+ <AgentKitRoot
1699
1915
  controller={observable.controller}
1700
1916
  threadId={threadId}
1917
+ load="manual"
1918
+ workedForRowLayout={workedForRowLayout}
1919
+ formatWorkedForDuration={formatWorkedForDuration}
1701
1920
  registry={{
1702
1921
  activities: {
1703
1922
  reasoning: ({ value }) => (
@@ -1714,11 +1933,15 @@ describe("AgentChat lifecycle", () => {
1714
1933
  }}
1715
1934
  >
1716
1935
  <AgentKitChat composer={false} />
1717
- </AgentKitProvider>,
1936
+ </AgentKitRoot>,
1718
1937
  );
1719
1938
  const work = tree.container.querySelector<HTMLDetailsElement>(
1720
1939
  ".agentkit-activities",
1721
1940
  )!;
1941
+ expect(
1942
+ work.querySelector<HTMLElement>(".agentkit-activities-summary")?.style
1943
+ .marginBlockStart,
1944
+ ).toBe("-12px");
1722
1945
  expect(work.open).toBe(false);
1723
1946
  expect(
1724
1947
  tree.container.querySelectorAll(".agentkit-activities"),
@@ -1744,6 +1967,60 @@ describe("AgentChat lifecycle", () => {
1744
1967
  ".agentkit-activities-summary > .agentkit-summary-chevron",
1745
1968
  ),
1746
1969
  ).not.toBeNull();
1970
+ expect(work.querySelector("summary")?.getAttribute("style")).toContain(
1971
+ "gap: 8px",
1972
+ );
1973
+ expect(
1974
+ work.querySelector<HTMLElement>(".agentkit-summary-chevron")?.style.width,
1975
+ ).toBe("12px");
1976
+ await tree.render(
1977
+ <AgentKitRoot
1978
+ controller={observable.controller}
1979
+ threadId={threadId}
1980
+ load="manual"
1981
+ workedForRowLayout={workedForRowLayout}
1982
+ formatWorkedForDuration={formatWorkedForDuration}
1983
+ registry={{
1984
+ activities: {
1985
+ reasoning: ({ value }) => (
1986
+ <span data-protocol-reasoning>{value.label}</span>
1987
+ ),
1988
+ },
1989
+ }}
1990
+ slots={{
1991
+ reasoning: ({ value, active, resetKey }) => (
1992
+ <span data-thought={resetKey} data-active={active}>
1993
+ {value.text}
1994
+ </span>
1995
+ ),
1996
+ workedForSummary: ({ label, open, current, layout }) => (
1997
+ <span
1998
+ data-work-summary
1999
+ data-open={open}
2000
+ data-icon-gap={layout?.iconGap}
2001
+ >
2002
+ <span>{label}</span>
2003
+ <span data-work-chevron aria-hidden="true">
2004
+ ›
2005
+ </span>
2006
+ {current}
2007
+ </span>
2008
+ ),
2009
+ }}
2010
+ >
2011
+ <AgentKitChat composer={false} />
2012
+ </AgentKitRoot>,
2013
+ );
2014
+ expect(work.querySelector("[data-work-summary]")?.textContent).toContain(
2015
+ "Searching documentation",
2016
+ );
2017
+ expect(work.querySelector("[data-work-chevron]")).not.toBeNull();
2018
+ expect(work.querySelector("[data-work-summary]")?.dataset.iconGap).toBe(
2019
+ "8",
2020
+ );
2021
+ expect(
2022
+ work.querySelector("[data-work-summary]")?.getAttribute("data-open"),
2023
+ ).toBe("false");
1747
2024
  thread = reduceAgentEvent(thread, {
1748
2025
  ...base(8, 18),
1749
2026
  type: "message.completed",
@@ -1765,7 +2042,15 @@ describe("AgentChat lifecycle", () => {
1765
2042
  await flush();
1766
2043
  expect(tree.container.querySelector(".agentkit-activities")).toBe(work);
1767
2044
  expect(work.open).toBe(false);
1768
- expect(work.querySelector("summary")?.textContent).toBe("Worked for 18s");
2045
+ expect(work.querySelector("summary")?.textContent).toBe(
2046
+ "Worked for 18 seconds›",
2047
+ );
2048
+ expect(work.querySelector<HTMLElement>("summary")?.style.fontSize).toBe(
2049
+ "13px",
2050
+ );
2051
+ expect(work.querySelector<HTMLElement>("summary")?.style.lineHeight).toBe(
2052
+ "16px",
2053
+ );
1769
2054
  expect(work.querySelector("[data-agentkit-current-activity]")).toBeNull();
1770
2055
  expect(tree.container.querySelectorAll("article")).toHaveLength(1);
1771
2056
  const answer = tree.container.querySelector(
@@ -0,0 +1,190 @@
1
+ import type {
2
+ AgentEvent,
3
+ AgentMessage,
4
+ AgentWidget,
5
+ } from "@agent-native/agentkit/protocol";
6
+ import { describe, expect, it } from "vitest";
7
+
8
+ import {
9
+ orderMessageContent,
10
+ type AgentMessageContentOrderResolver,
11
+ } from "./message-content-order.js";
12
+
13
+ const widget: AgentWidget = {
14
+ id: "chart-1",
15
+ kind: "core.data-chart",
16
+ data: { toolCallId: "tool-1" },
17
+ };
18
+
19
+ function event(
20
+ sequence: number,
21
+ payload: Omit<
22
+ AgentEvent,
23
+ "id" | "threadId" | "runId" | "sequence" | "occurredAt"
24
+ >,
25
+ ): AgentEvent {
26
+ return {
27
+ id: `event-${sequence}`,
28
+ threadId: "thread-1",
29
+ runId: "run-1",
30
+ sequence,
31
+ occurredAt: `2026-10-21T00:00:0${sequence}.000Z`,
32
+ ...payload,
33
+ } as AgentEvent;
34
+ }
35
+
36
+ describe("orderMessageContent", () => {
37
+ it("keeps an attached chart between the streamed introduction and conclusion", () => {
38
+ const message: AgentMessage = {
39
+ id: "assistant-1",
40
+ role: "assistant",
41
+ status: "complete",
42
+ parts: [
43
+ {
44
+ type: "text",
45
+ text: "Intro before chart. Conclusion after chart.",
46
+ format: "markdown",
47
+ },
48
+ ],
49
+ };
50
+ const events: AgentEvent[] = [
51
+ event(1, {
52
+ type: "message.created",
53
+ message: { ...message, status: "streaming", parts: [] },
54
+ }),
55
+ event(2, {
56
+ type: "message.delta",
57
+ messageId: message.id,
58
+ text: "Intro before chart. ",
59
+ format: "markdown",
60
+ }),
61
+ event(3, { type: "widget.created", messageId: message.id, widget }),
62
+ event(4, {
63
+ type: "message.delta",
64
+ messageId: message.id,
65
+ text: "Conclusion after chart.",
66
+ format: "markdown",
67
+ }),
68
+ event(5, { type: "message.completed", message }),
69
+ ];
70
+
71
+ const result = orderMessageContent(
72
+ message,
73
+ message.parts,
74
+ [widget],
75
+ events,
76
+ );
77
+
78
+ expect(result.map((item) => item.type)).toEqual(["part", "widget", "part"]);
79
+ expect(result[0]).toMatchObject({
80
+ type: "part",
81
+ part: { text: "Intro before chart. " },
82
+ });
83
+ expect(result[1]).toMatchObject({ type: "widget", widget });
84
+ expect(result[2]).toMatchObject({
85
+ type: "part",
86
+ part: { text: "Conclusion after chart." },
87
+ });
88
+ });
89
+
90
+ it("keeps legacy append order when streamed text cannot be matched exactly", () => {
91
+ const message: AgentMessage = {
92
+ id: "assistant-2",
93
+ role: "assistant",
94
+ parts: [{ type: "text", text: "Final answer" }],
95
+ };
96
+
97
+ const result = orderMessageContent(message, message.parts, [widget], []);
98
+
99
+ expect(result.map((item) => item.type)).toEqual(["part", "widget"]);
100
+ });
101
+
102
+ it("lets a host split text around a widget when stream order is unavailable", () => {
103
+ const message: AgentMessage = {
104
+ id: "assistant-3",
105
+ role: "assistant",
106
+ parts: [{ type: "text", text: "Intro. Summary." }],
107
+ };
108
+ const textPart = message.parts[0];
109
+ if (textPart?.type !== "text") throw new Error("Expected text part");
110
+
111
+ const result = orderMessageContent(
112
+ message,
113
+ message.parts,
114
+ [widget],
115
+ [],
116
+ ({ parts, widgets }) => {
117
+ const part = parts[0];
118
+ const chart = widgets[0];
119
+ if (part?.type !== "text" || !chart) return [];
120
+ return [
121
+ {
122
+ key: "intro",
123
+ type: "part",
124
+ part: { ...part, text: "Intro. " },
125
+ },
126
+ { key: chart.id, type: "widget", widget: chart },
127
+ {
128
+ key: "summary",
129
+ type: "part",
130
+ part: { ...part, text: "Summary." },
131
+ },
132
+ ];
133
+ },
134
+ );
135
+
136
+ expect(result.map((item) => item.type)).toEqual(["part", "widget", "part"]);
137
+ expect(result[0]).toMatchObject({ part: { text: "Intro. " } });
138
+ expect(result[1]).toMatchObject({ type: "widget", widget });
139
+ expect(result[2]).toMatchObject({ part: { text: "Summary." } });
140
+ expect(textPart.text).toBe("Intro. Summary.");
141
+ });
142
+
143
+ it("falls back when the host ordering result drops all visible content", () => {
144
+ const message: AgentMessage = {
145
+ id: "assistant-4",
146
+ role: "assistant",
147
+ parts: [{ type: "text", text: "A saved answer" }],
148
+ };
149
+
150
+ const result = orderMessageContent(
151
+ message,
152
+ message.parts,
153
+ [widget],
154
+ [],
155
+ () => [],
156
+ );
157
+
158
+ expect(result).toEqual([
159
+ {
160
+ key: "assistant-4-0",
161
+ type: "part",
162
+ part: message.parts[0],
163
+ },
164
+ { key: widget.id, type: "widget", widget },
165
+ ]);
166
+ });
167
+
168
+ it("falls back when the host ordering result loses text or a widget", () => {
169
+ const message: AgentMessage = {
170
+ id: "assistant-5",
171
+ role: "assistant",
172
+ parts: [{ type: "text", text: "Visible answer" }],
173
+ };
174
+ const incompleteOrders: AgentMessageContentOrderResolver[] = [
175
+ () => [{ key: widget.id, type: "widget" as const, widget }],
176
+ ({ defaultOrder }) => defaultOrder.slice(0, 1),
177
+ ];
178
+
179
+ for (const resolveOrder of incompleteOrders) {
180
+ const result = orderMessageContent(
181
+ message,
182
+ message.parts,
183
+ [widget],
184
+ [],
185
+ resolveOrder,
186
+ );
187
+ expect(result.map((item) => item.type)).toEqual(["part", "widget"]);
188
+ }
189
+ });
190
+ });