@agent-native/toolkit 0.214.0 → 0.217.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 (203) hide show
  1. package/agent-native.eject.json +5 -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 +69 -15
  8. package/dist/app/agentkit/react/components.js.map +1 -1
  9. package/dist/app/agentkit/react/context.d.ts +7 -1
  10. package/dist/app/agentkit/react/context.d.ts.map +1 -1
  11. package/dist/app/agentkit/react/context.js +5 -1
  12. package/dist/app/agentkit/react/context.js.map +1 -1
  13. package/dist/app/agentkit/react/index.d.ts +1 -0
  14. package/dist/app/agentkit/react/index.d.ts.map +1 -1
  15. package/dist/app/agentkit/react/index.js +1 -0
  16. package/dist/app/agentkit/react/index.js.map +1 -1
  17. package/dist/app/agentkit/react/root.d.ts +4 -1
  18. package/dist/app/agentkit/react/root.d.ts.map +1 -1
  19. package/dist/app/agentkit/react/root.js +2 -2
  20. package/dist/app/agentkit/react/root.js.map +1 -1
  21. package/dist/app/agentkit/react/styles.css +43 -6
  22. package/dist/app/agentkit/react/transcript-layout.d.ts +51 -0
  23. package/dist/app/agentkit/react/transcript-layout.d.ts.map +1 -0
  24. package/dist/app/agentkit/react/transcript-layout.js +34 -0
  25. package/dist/app/agentkit/react/transcript-layout.js.map +1 -0
  26. package/dist/app/auth/AuthPage.d.ts.map +1 -1
  27. package/dist/app/auth/AuthPage.js +33 -44
  28. package/dist/app/auth/AuthPage.js.map +1 -1
  29. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  30. package/dist/app/chat/AgentKitAssistantChat.js +3 -3
  31. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  32. package/dist/app/chat/AgentPanel.d.ts +2 -0
  33. package/dist/app/chat/AgentPanel.d.ts.map +1 -1
  34. package/dist/app/chat/AgentPanel.js +6 -2
  35. package/dist/app/chat/AgentPanel.js.map +1 -1
  36. package/dist/app/chat/chat/index.d.ts +2 -0
  37. package/dist/app/chat/chat/index.d.ts.map +1 -1
  38. package/dist/app/chat/chat/index.js.map +1 -1
  39. package/dist/app/chat/chat/surface-types.d.ts +7 -0
  40. package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
  41. package/dist/app/chat/chat/surface-types.js.map +1 -1
  42. package/dist/app/chat/chat/tool-call-display.d.ts +7 -2
  43. package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
  44. package/dist/app/chat/chat/tool-call-display.js +10 -9
  45. package/dist/app/chat/chat/tool-call-display.js.map +1 -1
  46. package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts.map +1 -1
  47. package/dist/app/chat/chat/widgets/DataChartRenderer.js +196 -26
  48. package/dist/app/chat/chat/widgets/DataChartRenderer.js.map +1 -1
  49. package/dist/app/chat/chat/widgets/DataChartWidget.d.ts.map +1 -1
  50. package/dist/app/chat/chat/widgets/DataChartWidget.js +5 -1
  51. package/dist/app/chat/chat/widgets/DataChartWidget.js.map +1 -1
  52. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts +2 -2
  53. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts.map +1 -1
  54. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js +6 -4
  55. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js.map +1 -1
  56. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  57. package/dist/app/i18n/catalogs/ar-SA.js +1 -0
  58. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  59. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  60. package/dist/app/i18n/catalogs/de-DE.js +1 -0
  61. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  62. package/dist/app/i18n/catalogs/en-US.d.ts +1 -0
  63. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  64. package/dist/app/i18n/catalogs/en-US.js +1 -0
  65. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  66. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  67. package/dist/app/i18n/catalogs/es-ES.js +1 -0
  68. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  69. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  70. package/dist/app/i18n/catalogs/fr-FR.js +1 -0
  71. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  72. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  73. package/dist/app/i18n/catalogs/hi-IN.js +1 -0
  74. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  75. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  76. package/dist/app/i18n/catalogs/ja-JP.js +1 -0
  77. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  78. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  79. package/dist/app/i18n/catalogs/ko-KR.js +1 -0
  80. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  81. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  82. package/dist/app/i18n/catalogs/pt-BR.js +1 -0
  83. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  84. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  85. package/dist/app/i18n/catalogs/zh-CN.js +1 -0
  86. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  87. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  88. package/dist/app/i18n/catalogs/zh-TW.js +1 -0
  89. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  90. package/dist/app/onboarding/GettingStartedCard.d.ts +35 -0
  91. package/dist/app/onboarding/GettingStartedCard.d.ts.map +1 -0
  92. package/dist/app/onboarding/GettingStartedCard.js +38 -0
  93. package/dist/app/onboarding/GettingStartedCard.js.map +1 -0
  94. package/dist/app/onboarding/index.d.ts +1 -0
  95. package/dist/app/onboarding/index.d.ts.map +1 -1
  96. package/dist/app/onboarding/index.js +1 -0
  97. package/dist/app/onboarding/index.js.map +1 -1
  98. package/dist/app/providers/AppProviders.d.ts.map +1 -1
  99. package/dist/app/providers/AppProviders.js +3 -1
  100. package/dist/app/providers/AppProviders.js.map +1 -1
  101. package/dist/app/settings/AccountSettingsCard.d.ts.map +1 -1
  102. package/dist/app/settings/AccountSettingsCard.js +2 -1
  103. package/dist/app/settings/AccountSettingsCard.js.map +1 -1
  104. package/dist/app/settings/index.d.ts +1 -0
  105. package/dist/app/settings/index.d.ts.map +1 -1
  106. package/dist/app/settings/index.js +1 -0
  107. package/dist/app/settings/index.js.map +1 -1
  108. package/dist/app/settings/shell/SettingsShell.d.ts +40 -1
  109. package/dist/app/settings/shell/SettingsShell.d.ts.map +1 -1
  110. package/dist/app/settings/shell/SettingsShell.js +87 -20
  111. package/dist/app/settings/shell/SettingsShell.js.map +1 -1
  112. package/dist/app/settings/shell/bridge.d.ts +5 -3
  113. package/dist/app/settings/shell/bridge.d.ts.map +1 -1
  114. package/dist/app/settings/shell/bridge.js +8 -5
  115. package/dist/app/settings/shell/bridge.js.map +1 -1
  116. package/dist/app/settings/shell/index.d.ts +2 -2
  117. package/dist/app/settings/shell/index.d.ts.map +1 -1
  118. package/dist/app/settings/shell/index.js.map +1 -1
  119. package/dist/app/settings/shell/registry.d.ts +9 -1
  120. package/dist/app/settings/shell/registry.d.ts.map +1 -1
  121. package/dist/app/settings/shell/registry.js +16 -0
  122. package/dist/app/settings/shell/registry.js.map +1 -1
  123. package/dist/app/settings/shell/routing.d.ts +5 -4
  124. package/dist/app/settings/shell/routing.d.ts.map +1 -1
  125. package/dist/app/settings/shell/routing.js +38 -10
  126. package/dist/app/settings/shell/routing.js.map +1 -1
  127. package/dist/app/shared/CommandMenu.d.ts +86 -6
  128. package/dist/app/shared/CommandMenu.d.ts.map +1 -1
  129. package/dist/app/shared/CommandMenu.js +303 -41
  130. package/dist/app/shared/CommandMenu.js.map +1 -1
  131. package/dist/app/shared/ConsentBanner.d.ts +9 -0
  132. package/dist/app/shared/ConsentBanner.d.ts.map +1 -0
  133. package/dist/app/shared/ConsentBanner.js +35 -0
  134. package/dist/app/shared/ConsentBanner.js.map +1 -0
  135. package/dist/composer/ComposerContextBanner.d.ts +3 -1
  136. package/dist/composer/ComposerContextBanner.d.ts.map +1 -1
  137. package/dist/composer/ComposerContextBanner.js +2 -2
  138. package/dist/composer/ComposerContextBanner.js.map +1 -1
  139. package/dist/ui/sonner.d.ts +2 -2
  140. package/dist/ui/sonner.d.ts.map +1 -1
  141. package/dist/ui/sonner.js +2 -2
  142. package/dist/ui/sonner.js.map +1 -1
  143. package/package.json +3 -3
  144. package/src/app/agentkit/react/chat.tsx +2 -0
  145. package/src/app/agentkit/react/components.interaction.spec.tsx +107 -0
  146. package/src/app/agentkit/react/components.spec.tsx +28 -1
  147. package/src/app/agentkit/react/components.tsx +153 -17
  148. package/src/app/agentkit/react/context.tsx +12 -0
  149. package/src/app/agentkit/react/index.ts +1 -0
  150. package/src/app/agentkit/react/lifecycle.spec.tsx +186 -16
  151. package/src/app/agentkit/react/root.tsx +7 -0
  152. package/src/app/agentkit/react/styles.css +43 -6
  153. package/src/app/agentkit/react/transcript-layout.spec.ts +72 -0
  154. package/src/app/agentkit/react/transcript-layout.ts +97 -0
  155. package/src/app/auth/AuthPage.attribution.spec.tsx +6 -0
  156. package/src/app/auth/AuthPage.interaction.spec.tsx +74 -0
  157. package/src/app/auth/AuthPage.tsx +32 -39
  158. package/src/app/chat/AgentKitAssistantChat.spec.tsx +39 -0
  159. package/src/app/chat/AgentKitAssistantChat.tsx +5 -0
  160. package/src/app/chat/AgentPanel.page-header-share.spec.tsx +101 -0
  161. package/src/app/chat/AgentPanel.tsx +14 -2
  162. package/src/app/chat/chat/index.ts +2 -0
  163. package/src/app/chat/chat/surface-types.ts +10 -0
  164. package/src/app/chat/chat/tool-call-display.spec.tsx +83 -0
  165. package/src/app/chat/chat/tool-call-display.tsx +40 -8
  166. package/src/app/chat/chat/widgets/DataChartRenderer.spec.tsx +632 -0
  167. package/src/app/chat/chat/widgets/DataChartRenderer.tsx +308 -51
  168. package/src/app/chat/chat/widgets/DataChartWidget.spec.tsx +126 -23
  169. package/src/app/chat/chat/widgets/DataChartWidget.tsx +10 -0
  170. package/src/app/chat/chat/widgets/DataWidgetDisplayControls.tsx +26 -6
  171. package/src/app/i18n/catalogs/ar-SA.ts +1 -0
  172. package/src/app/i18n/catalogs/de-DE.ts +2 -0
  173. package/src/app/i18n/catalogs/en-US.ts +2 -0
  174. package/src/app/i18n/catalogs/es-ES.ts +2 -0
  175. package/src/app/i18n/catalogs/fr-FR.ts +2 -0
  176. package/src/app/i18n/catalogs/hi-IN.ts +1 -0
  177. package/src/app/i18n/catalogs/ja-JP.ts +2 -0
  178. package/src/app/i18n/catalogs/ko-KR.ts +1 -0
  179. package/src/app/i18n/catalogs/pt-BR.ts +2 -0
  180. package/src/app/i18n/catalogs/zh-CN.ts +1 -0
  181. package/src/app/i18n/catalogs/zh-TW.ts +1 -0
  182. package/src/app/onboarding/GettingStartedCard.tsx +203 -0
  183. package/src/app/onboarding/index.ts +1 -0
  184. package/src/app/providers/AppProviders.tsx +7 -1
  185. package/src/app/settings/AccountSettingsCard.spec.tsx +31 -0
  186. package/src/app/settings/AccountSettingsCard.tsx +8 -0
  187. package/src/app/settings/index.ts +1 -0
  188. package/src/app/settings/shell/SettingsShell.spec.tsx +472 -1
  189. package/src/app/settings/shell/SettingsShell.tsx +245 -31
  190. package/src/app/settings/shell/bridge.spec.ts +12 -0
  191. package/src/app/settings/shell/bridge.ts +17 -4
  192. package/src/app/settings/shell/index.ts +6 -1
  193. package/src/app/settings/shell/registry.ts +33 -1
  194. package/src/app/settings/shell/routing.spec.ts +92 -1
  195. package/src/app/settings/shell/routing.ts +52 -9
  196. package/src/app/shared/CommandMenu.spec.tsx +1662 -1
  197. package/src/app/shared/CommandMenu.tsx +763 -164
  198. package/src/app/shared/ConsentBanner.spec.tsx +141 -0
  199. package/src/app/shared/ConsentBanner.tsx +141 -0
  200. package/src/composer/ComposerContextBanner.spec.tsx +31 -5
  201. package/src/composer/ComposerContextBanner.tsx +7 -2
  202. package/src/ui/sonner.spec.ts +10 -0
  203. package/src/ui/sonner.tsx +2 -2
@@ -44,6 +44,7 @@ import {
44
44
  } from "react";
45
45
 
46
46
  import type { AgentMessageContentOrderResolver } from "./message-content-order.js";
47
+ import type { AgentChatWorkedForRowLayout } from "./transcript-layout.js";
47
48
 
48
49
  export interface AgentKitRenderProps<T> {
49
50
  value: T;
@@ -87,6 +88,7 @@ export interface AgentKitWorkedForSummaryRenderProps {
87
88
  expandable: boolean;
88
89
  open: boolean;
89
90
  current?: ReactNode;
91
+ layout?: AgentChatWorkedForRowLayout;
90
92
  }
91
93
 
92
94
  export interface AgentConnectionErrorRenderProps {
@@ -495,6 +497,8 @@ export interface AgentKitProviderProps {
495
497
  /** Receives full renderer failures while the UI shows a safe fallback. */
496
498
  onRenderError?: (failure: AgentKitRenderFailure) => void;
497
499
  resolveMessageContentOrder?: AgentMessageContentOrderResolver;
500
+ workedForRowLayout?: AgentChatWorkedForRowLayout;
501
+ formatWorkedForDuration?: (durationMs: number) => string;
498
502
  onClientEffect?: (effect: {
499
503
  type: "client.effect" | "client.deeplink";
500
504
  name: string;
@@ -525,6 +529,8 @@ export interface AgentKitContextValue {
525
529
  onConnectionRequest?: AgentKitProviderProps["onConnectionRequest"];
526
530
  onRenderError?: (failure: AgentKitRenderFailure) => void;
527
531
  resolveMessageContentOrder?: AgentMessageContentOrderResolver;
532
+ workedForRowLayout?: AgentChatWorkedForRowLayout;
533
+ formatWorkedForDuration?: (durationMs: number) => string;
528
534
  registerComposerFocus: (threadId: ThreadId, focus: () => void) => () => void;
529
535
  requestComposerFocus: (threadId: ThreadId) => void;
530
536
  }
@@ -546,6 +552,8 @@ export function AgentKitProvider({
546
552
  onConnectionRequest,
547
553
  onRenderError,
548
554
  resolveMessageContentOrder,
555
+ workedForRowLayout,
556
+ formatWorkedForDuration,
549
557
  onClientEffect,
550
558
  children,
551
559
  }: AgentKitProviderProps) {
@@ -613,6 +621,8 @@ export function AgentKitProvider({
613
621
  onConnectionRequest,
614
622
  onRenderError,
615
623
  resolveMessageContentOrder,
624
+ workedForRowLayout,
625
+ formatWorkedForDuration,
616
626
  registerComposerFocus,
617
627
  requestComposerFocus,
618
628
  }),
@@ -631,6 +641,8 @@ export function AgentKitProvider({
631
641
  onConnectionRequest,
632
642
  onRenderError,
633
643
  resolveMessageContentOrder,
644
+ workedForRowLayout,
645
+ formatWorkedForDuration,
634
646
  registerComposerFocus,
635
647
  requestComposerFocus,
636
648
  ],
@@ -4,3 +4,4 @@ export * from "./context.js";
4
4
  export * from "./message-content-order.js";
5
5
  export * from "./root.js";
6
6
  export * from "./streaming-text.js";
7
+ export * from "./transcript-layout.js";
@@ -185,7 +185,7 @@ describe("AgentChat lifecycle", () => {
185
185
  });
186
186
  const tree = mount();
187
187
 
188
- await tree.render(
188
+ const renderChat = (enablePostWidgetText: boolean) => (
189
189
  <AgentKitProvider
190
190
  controller={observable.controller}
191
191
  threadId={threadId}
@@ -213,24 +213,152 @@ describe("AgentChat lifecycle", () => {
213
213
  },
214
214
  }}
215
215
  slots={{
216
- text: ({ value }) => <span data-chat-part>{value.text}</span>,
216
+ text: ({ value }) => (
217
+ <div className="agentkit-markdown" data-chat-part>
218
+ <p>{value.text}</p>
219
+ </div>
220
+ ),
217
221
  }}
218
222
  >
219
- <AgentKitChat composer={false} />
220
- </AgentKitProvider>,
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>
221
237
  );
238
+ await tree.render(renderChat(true));
222
239
 
223
240
  const content = tree.container.querySelector(
224
241
  `[data-message-id="${message.id}"] .agentkit-message-content`,
225
242
  );
243
+ const transcriptContent = tree.container.querySelector(
244
+ ".agentkit-transcript-content",
245
+ );
226
246
  const renderedOrder = [...(content?.children ?? [])].map((element) =>
227
- element.hasAttribute("data-chat-part")
247
+ element.hasAttribute("data-post-widget-text")
228
248
  ? element.textContent
229
- : element.hasAttribute("data-chart-result")
230
- ? "chart"
231
- : "other",
249
+ : element.hasAttribute("data-chat-part")
250
+ ? element.textContent
251
+ : element.hasAttribute("data-chart-result")
252
+ ? "chart"
253
+ : "other",
232
254
  );
233
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
+ );
234
362
  await tree.unmount();
235
363
  });
236
364
 
@@ -1686,7 +1814,7 @@ describe("AgentChat lifecycle", () => {
1686
1814
  await tree.unmount();
1687
1815
  });
1688
1816
 
1689
- it("preserves visible reasoning without a Thinking history row", async () => {
1817
+ it("preserves visible reasoning and applies the worked-row layout", async () => {
1690
1818
  const threadId = "thread-reasoning-work";
1691
1819
  const runId = "run-reasoning-work";
1692
1820
  const base = (sequence: number, seconds: number = sequence) => ({
@@ -1771,10 +1899,24 @@ describe("AgentChat lifecycle", () => {
1771
1899
  });
1772
1900
  const observable = observableController(snapshot(0));
1773
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`;
1774
1913
  await tree.render(
1775
- <AgentKitProvider
1914
+ <AgentKitRoot
1776
1915
  controller={observable.controller}
1777
1916
  threadId={threadId}
1917
+ load="manual"
1918
+ workedForRowLayout={workedForRowLayout}
1919
+ formatWorkedForDuration={formatWorkedForDuration}
1778
1920
  registry={{
1779
1921
  activities: {
1780
1922
  reasoning: ({ value }) => (
@@ -1791,11 +1933,15 @@ describe("AgentChat lifecycle", () => {
1791
1933
  }}
1792
1934
  >
1793
1935
  <AgentKitChat composer={false} />
1794
- </AgentKitProvider>,
1936
+ </AgentKitRoot>,
1795
1937
  );
1796
1938
  const work = tree.container.querySelector<HTMLDetailsElement>(
1797
1939
  ".agentkit-activities",
1798
1940
  )!;
1941
+ expect(
1942
+ work.querySelector<HTMLElement>(".agentkit-activities-summary")?.style
1943
+ .marginBlockStart,
1944
+ ).toBe("-12px");
1799
1945
  expect(work.open).toBe(false);
1800
1946
  expect(
1801
1947
  tree.container.querySelectorAll(".agentkit-activities"),
@@ -1821,10 +1967,19 @@ describe("AgentChat lifecycle", () => {
1821
1967
  ".agentkit-activities-summary > .agentkit-summary-chevron",
1822
1968
  ),
1823
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");
1824
1976
  await tree.render(
1825
- <AgentKitProvider
1977
+ <AgentKitRoot
1826
1978
  controller={observable.controller}
1827
1979
  threadId={threadId}
1980
+ load="manual"
1981
+ workedForRowLayout={workedForRowLayout}
1982
+ formatWorkedForDuration={formatWorkedForDuration}
1828
1983
  registry={{
1829
1984
  activities: {
1830
1985
  reasoning: ({ value }) => (
@@ -1838,8 +1993,12 @@ describe("AgentChat lifecycle", () => {
1838
1993
  {value.text}
1839
1994
  </span>
1840
1995
  ),
1841
- workedForSummary: ({ label, open, current }) => (
1842
- <span data-work-summary data-open={open}>
1996
+ workedForSummary: ({ label, open, current, layout }) => (
1997
+ <span
1998
+ data-work-summary
1999
+ data-open={open}
2000
+ data-icon-gap={layout?.iconGap}
2001
+ >
1843
2002
  <span>{label}</span>
1844
2003
  <span data-work-chevron aria-hidden="true">
1845
2004
  ›
@@ -1850,12 +2009,15 @@ describe("AgentChat lifecycle", () => {
1850
2009
  }}
1851
2010
  >
1852
2011
  <AgentKitChat composer={false} />
1853
- </AgentKitProvider>,
2012
+ </AgentKitRoot>,
1854
2013
  );
1855
2014
  expect(work.querySelector("[data-work-summary]")?.textContent).toContain(
1856
2015
  "Searching documentation",
1857
2016
  );
1858
2017
  expect(work.querySelector("[data-work-chevron]")).not.toBeNull();
2018
+ expect(work.querySelector("[data-work-summary]")?.dataset.iconGap).toBe(
2019
+ "8",
2020
+ );
1859
2021
  expect(
1860
2022
  work.querySelector("[data-work-summary]")?.getAttribute("data-open"),
1861
2023
  ).toBe("false");
@@ -1880,7 +2042,15 @@ describe("AgentChat lifecycle", () => {
1880
2042
  await flush();
1881
2043
  expect(tree.container.querySelector(".agentkit-activities")).toBe(work);
1882
2044
  expect(work.open).toBe(false);
1883
- 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
+ );
1884
2054
  expect(work.querySelector("[data-agentkit-current-activity]")).toBeNull();
1885
2055
  expect(tree.container.querySelectorAll("article")).toHaveLength(1);
1886
2056
  const answer = tree.container.querySelector(
@@ -31,6 +31,7 @@ import {
31
31
  type AgentKitSlots,
32
32
  } from "./context.js";
33
33
  import type { AgentMessageContentOrderResolver } from "./message-content-order.js";
34
+ import type { AgentChatWorkedForRowLayout } from "./transcript-layout.js";
34
35
 
35
36
  const mountedManagedClients = new WeakMap<AgentKitClient, number>();
36
37
 
@@ -86,6 +87,8 @@ export interface AgentKitRootBaseProps {
86
87
  ) => Promise<AgentConnectionResponse>;
87
88
  onRenderError?: (failure: AgentKitRenderFailure) => void;
88
89
  resolveMessageContentOrder?: AgentMessageContentOrderResolver;
90
+ workedForRowLayout?: AgentChatWorkedForRowLayout;
91
+ formatWorkedForDuration?: (durationMs: number) => string;
89
92
  onClientEffect?: (effect: {
90
93
  type: "client.effect" | "client.deeplink";
91
94
  name: string;
@@ -116,6 +119,8 @@ export function AgentKitRoot({
116
119
  onConnectionRequest,
117
120
  onRenderError,
118
121
  resolveMessageContentOrder,
122
+ workedForRowLayout,
123
+ formatWorkedForDuration,
119
124
  onClientEffect,
120
125
  load = "auto",
121
126
  onLoadError,
@@ -294,6 +299,8 @@ export function AgentKitRoot({
294
299
  onConnectionRequest={onConnectionRequest}
295
300
  onRenderError={onRenderError}
296
301
  resolveMessageContentOrder={resolveMessageContentOrder}
302
+ workedForRowLayout={workedForRowLayout}
303
+ formatWorkedForDuration={formatWorkedForDuration}
297
304
  onClientEffect={onClientEffect}
298
305
  >
299
306
  {children}
@@ -669,11 +669,18 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
669
669
  }
670
670
 
671
671
  .agentkit-transcript-content {
672
+ position: relative;
672
673
  box-sizing: border-box;
673
- inline-size: min(100%, 47rem);
674
+ inline-size: min(100%, var(--agentkit-transcript-lane-width, 47rem));
674
675
  min-inline-size: 0;
675
676
  margin-inline: auto;
677
+ inset-inline-start: clamp(
678
+ calc((100% - min(100%, var(--agentkit-transcript-lane-width, 47rem))) / -2),
679
+ var(--agentkit-transcript-lane-offset-inline, 0px),
680
+ calc((100% - min(100%, var(--agentkit-transcript-lane-width, 47rem))) / 2)
681
+ );
676
682
  padding: 1.5rem 1rem;
683
+ padding-block-start: var(--agentkit-transcript-top-inset, 1.5rem);
677
684
  }
678
685
 
679
686
  .agentkit-scroll-to-bottom-control {
@@ -710,13 +717,21 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
710
717
  display: flex;
711
718
  flex-direction: column;
712
719
  align-items: flex-start;
713
- margin-block: 0 1.25rem;
720
+ margin-block: 0 var(--agentkit-transcript-message-gap, 1.25rem);
714
721
  font-size: 0.875rem;
715
- line-height: 1.52;
722
+ font-family: var(
723
+ --agentkit-transcript-font-family,
724
+ var(--font-sans, inherit)
725
+ );
726
+ line-height: var(--agentkit-transcript-message-line-height, 1.52);
716
727
  }
717
728
 
718
729
  .agentkit-message[data-role="user"] {
719
730
  align-items: flex-end;
731
+ line-height: var(
732
+ --agentkit-user-bubble-line-height,
733
+ var(--agentkit-transcript-message-line-height, 1.52)
734
+ );
720
735
  }
721
736
 
722
737
  .agentkit-message-content {
@@ -726,8 +741,11 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
726
741
  }
727
742
 
728
743
  .agentkit-message[data-role="user"] .agentkit-message-content {
729
- max-inline-size: min(88%, 38rem);
730
- padding: 0.625rem 0.8125rem;
744
+ position: relative;
745
+ inset-inline-start: var(--agentkit-user-bubble-offset-inline, 0px);
746
+ max-inline-size: min(88%, var(--agentkit-user-bubble-max-width, 38rem));
747
+ padding: var(--agentkit-user-bubble-padding-block, 0.625rem)
748
+ var(--agentkit-user-bubble-padding-inline, 0.8125rem);
731
749
  border-radius: var(--agentkit-radius-inner);
732
750
  background: var(--agentkit-subtle);
733
751
  }
@@ -845,6 +863,25 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
845
863
  background: var(--agentkit-canvas);
846
864
  }
847
865
 
866
+ .agentkit-message-content [data-post-widget-text="true"] {
867
+ display: contents;
868
+ color: var(--agentkit-transcript-post-widget-text-color, inherit);
869
+ }
870
+
871
+ .agentkit-message-content [data-post-widget-text="true"] > p,
872
+ .agentkit-message-content [data-post-widget-text="true"] .agentkit-markdown p {
873
+ padding-block: var(
874
+ --agentkit-transcript-post-widget-paragraph-padding-block,
875
+ 0px
876
+ );
877
+ }
878
+
879
+ .agentkit-message-content
880
+ > [data-post-widget-text="true"]:last-child
881
+ > :last-child {
882
+ margin-block-end: 0;
883
+ }
884
+
848
885
  .agentkit-image-preview img {
849
886
  max-inline-size: 92vw;
850
887
  max-block-size: 90vh;
@@ -2133,6 +2170,6 @@ button.agentkit-object {
2133
2170
  }
2134
2171
 
2135
2172
  .agentkit-message[data-role="user"] .agentkit-message-content {
2136
- max-inline-size: 94%;
2173
+ max-inline-size: min(94%, var(--agentkit-user-bubble-max-width, 38rem));
2137
2174
  }
2138
2175
  }
@@ -0,0 +1,72 @@
1
+ import type { AgentKitController } from "@agent-native/agentkit/client";
2
+ import { createElement } from "react";
3
+ import { renderToStaticMarkup } from "react-dom/server";
4
+ import { describe, expect, it } from "vitest";
5
+
6
+ import { useAgentKit } from "./context.js";
7
+ import { AgentKitRoot } from "./root.js";
8
+ import {
9
+ workedForRowChevronStyle,
10
+ workedForRowSummaryStyle,
11
+ } from "./transcript-layout.js";
12
+
13
+ function WorkedForLayoutProbe() {
14
+ const { workedForRowLayout, formatWorkedForDuration } = useAgentKit();
15
+ return createElement("span", {
16
+ "data-icon-gap": workedForRowLayout?.iconGap,
17
+ "data-duration": formatWorkedForDuration?.(16_000),
18
+ });
19
+ }
20
+
21
+ describe("worked-for row layout", () => {
22
+ it("applies shared dimensions to the summary and leading chevron", () => {
23
+ const layout = {
24
+ chevronSize: 12,
25
+ iconGap: 8,
26
+ fontSize: 13,
27
+ lineHeight: 16,
28
+ paddingBlock: 2,
29
+ paddingInline: 4,
30
+ };
31
+
32
+ expect(workedForRowSummaryStyle(layout)).toEqual({
33
+ gap: "8px",
34
+ fontSize: "13px",
35
+ lineHeight: "16px",
36
+ paddingBlock: "2px",
37
+ paddingInline: "4px",
38
+ fontFamily: "inherit",
39
+ });
40
+ expect(workedForRowChevronStyle(layout)).toEqual({
41
+ width: "12px",
42
+ height: "12px",
43
+ });
44
+ });
45
+
46
+ it("rejects invalid CSS dimensions", () => {
47
+ expect(() => workedForRowSummaryStyle({ fontSize: 0 })).toThrow(
48
+ "workedForRowLayout.fontSize must be a valid CSS length",
49
+ );
50
+ });
51
+
52
+ it("carries the layout from the AgentKit root into shared context", () => {
53
+ const layout = { iconGap: 8 };
54
+ const formatWorkedForDuration = (durationMs: number) =>
55
+ `${Math.round(durationMs / 1000)} seconds`;
56
+ const markup = renderToStaticMarkup(
57
+ createElement(
58
+ AgentKitRoot,
59
+ {
60
+ controller: {} as AgentKitController,
61
+ threadId: "thread",
62
+ workedForRowLayout: layout,
63
+ formatWorkedForDuration,
64
+ },
65
+ createElement(WorkedForLayoutProbe),
66
+ ),
67
+ );
68
+
69
+ expect(markup).toContain('data-icon-gap="8"');
70
+ expect(markup).toContain('data-duration="16 seconds"');
71
+ });
72
+ });
@@ -0,0 +1,97 @@
1
+ import type { CSSProperties } from "react";
2
+
3
+ export interface AgentChatTranscriptLayout {
4
+ /** Top padding above transcript messages, in CSS pixels. */
5
+ topInset?: number;
6
+ /** Maximum centered message lane width, in CSS pixels. */
7
+ messageLaneWidth?: number;
8
+ /** Signed inline offset for the centered message lane, in CSS pixels. */
9
+ messageLaneOffsetInline?: number;
10
+ /** Space after each message, in CSS pixels. */
11
+ messageGap?: number;
12
+ /** Assistant message line height, in CSS pixels. */
13
+ messageLineHeight?: number;
14
+ /** Font family for transcript messages. Omitted uses the shared semantic font. */
15
+ fontFamily?: string;
16
+ /** Optional appearance for assistant text displayed after a widget. */
17
+ postWidgetText?: {
18
+ tone?: "muted";
19
+ paragraphPaddingBlock?: number;
20
+ };
21
+ /** User bubble line height, in CSS pixels. */
22
+ userBubbleLineHeight?: number;
23
+ /** Maximum user bubble width, in CSS pixels. */
24
+ userBubbleMaxWidth?: number;
25
+ /** Signed inline offset for the user bubble, in CSS pixels. */
26
+ userBubbleOffsetInline?: number;
27
+ /** User bubble padding on the block and inline axes, in CSS pixels. */
28
+ userBubblePadding?: { block?: number; inline?: number };
29
+ }
30
+
31
+ /** Optional geometry for the completed-work disclosure row in chat. */
32
+ export interface AgentChatWorkedForRowLayout {
33
+ /** Chevron width and height, in CSS pixels. */
34
+ chevronSize?: number;
35
+ /** Space between the chevron and label, in CSS pixels. */
36
+ iconGap?: number;
37
+ /** Signed margin before the row, in CSS pixels. */
38
+ marginBlockStart?: number;
39
+ /** Label font size, in CSS pixels. */
40
+ fontSize?: number;
41
+ /** Label line height, in CSS pixels. */
42
+ lineHeight?: number;
43
+ /** Padding on the block axis, in CSS pixels. */
44
+ paddingBlock?: number;
45
+ /** Padding on the inline axis, in CSS pixels. */
46
+ paddingInline?: number;
47
+ }
48
+
49
+ function workedForRowLength(
50
+ value: number | undefined,
51
+ name: keyof AgentChatWorkedForRowLayout,
52
+ positive = false,
53
+ ) {
54
+ if (value === undefined) return undefined;
55
+ if (!Number.isFinite(value) || value < 0 || (positive && value === 0)) {
56
+ throw new RangeError(
57
+ `workedForRowLayout.${name} must be a valid CSS length`,
58
+ );
59
+ }
60
+ return `${value}px`;
61
+ }
62
+
63
+ function workedForRowOffset(
64
+ value: number | undefined,
65
+ name: keyof AgentChatWorkedForRowLayout,
66
+ ) {
67
+ if (value === undefined) return undefined;
68
+ if (!Number.isFinite(value)) {
69
+ throw new RangeError(`workedForRowLayout.${name} must be finite`);
70
+ }
71
+ return `${value}px`;
72
+ }
73
+
74
+ export function workedForRowSummaryStyle(
75
+ layout?: AgentChatWorkedForRowLayout,
76
+ ): CSSProperties | undefined {
77
+ if (!layout) return undefined;
78
+ return {
79
+ gap: workedForRowLength(layout.iconGap, "iconGap"),
80
+ marginBlockStart: workedForRowOffset(
81
+ layout.marginBlockStart,
82
+ "marginBlockStart",
83
+ ),
84
+ fontSize: workedForRowLength(layout.fontSize, "fontSize", true),
85
+ lineHeight: workedForRowLength(layout.lineHeight, "lineHeight", true),
86
+ paddingBlock: workedForRowLength(layout.paddingBlock, "paddingBlock"),
87
+ paddingInline: workedForRowLength(layout.paddingInline, "paddingInline"),
88
+ fontFamily: "inherit",
89
+ };
90
+ }
91
+
92
+ export function workedForRowChevronStyle(
93
+ layout?: AgentChatWorkedForRowLayout,
94
+ ): CSSProperties | undefined {
95
+ const size = workedForRowLength(layout?.chevronSize, "chevronSize", true);
96
+ return size ? { width: size, height: size } : undefined;
97
+ }
@@ -129,6 +129,12 @@ async function recordBoth(
129
129
 
130
130
  describe("AuthPage attribution", () => {
131
131
  beforeEach(() => {
132
+ // Attribution storage depends on the visitor's time zone, so pin it.
133
+ const realOptions = new Intl.DateTimeFormat().resolvedOptions();
134
+ vi.spyOn(Intl.DateTimeFormat.prototype, "resolvedOptions").mockReturnValue({
135
+ ...realOptions,
136
+ timeZone: "America/Los_Angeles",
137
+ });
132
138
  vi.useFakeTimers({ toFake: ["Date"] });
133
139
  vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
134
140
  vi.stubGlobal(