@agent-native/toolkit 0.214.0 → 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 (128) hide show
  1. package/dist/app/agentkit/react/chat.d.ts.map +1 -1
  2. package/dist/app/agentkit/react/chat.js +2 -2
  3. package/dist/app/agentkit/react/chat.js.map +1 -1
  4. package/dist/app/agentkit/react/components.d.ts +4 -1
  5. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  6. package/dist/app/agentkit/react/components.js +69 -15
  7. package/dist/app/agentkit/react/components.js.map +1 -1
  8. package/dist/app/agentkit/react/context.d.ts +7 -1
  9. package/dist/app/agentkit/react/context.d.ts.map +1 -1
  10. package/dist/app/agentkit/react/context.js +5 -1
  11. package/dist/app/agentkit/react/context.js.map +1 -1
  12. package/dist/app/agentkit/react/index.d.ts +1 -0
  13. package/dist/app/agentkit/react/index.d.ts.map +1 -1
  14. package/dist/app/agentkit/react/index.js +1 -0
  15. package/dist/app/agentkit/react/index.js.map +1 -1
  16. package/dist/app/agentkit/react/root.d.ts +4 -1
  17. package/dist/app/agentkit/react/root.d.ts.map +1 -1
  18. package/dist/app/agentkit/react/root.js +2 -2
  19. package/dist/app/agentkit/react/root.js.map +1 -1
  20. package/dist/app/agentkit/react/styles.css +43 -6
  21. package/dist/app/agentkit/react/transcript-layout.d.ts +51 -0
  22. package/dist/app/agentkit/react/transcript-layout.d.ts.map +1 -0
  23. package/dist/app/agentkit/react/transcript-layout.js +34 -0
  24. package/dist/app/agentkit/react/transcript-layout.js.map +1 -0
  25. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  26. package/dist/app/chat/AgentKitAssistantChat.js +3 -3
  27. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  28. package/dist/app/chat/AgentPanel.d.ts +2 -0
  29. package/dist/app/chat/AgentPanel.d.ts.map +1 -1
  30. package/dist/app/chat/AgentPanel.js +6 -2
  31. package/dist/app/chat/AgentPanel.js.map +1 -1
  32. package/dist/app/chat/chat/index.d.ts +2 -0
  33. package/dist/app/chat/chat/index.d.ts.map +1 -1
  34. package/dist/app/chat/chat/index.js.map +1 -1
  35. package/dist/app/chat/chat/surface-types.d.ts +7 -0
  36. package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
  37. package/dist/app/chat/chat/surface-types.js.map +1 -1
  38. package/dist/app/chat/chat/tool-call-display.d.ts +7 -2
  39. package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
  40. package/dist/app/chat/chat/tool-call-display.js +10 -9
  41. package/dist/app/chat/chat/tool-call-display.js.map +1 -1
  42. package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts.map +1 -1
  43. package/dist/app/chat/chat/widgets/DataChartRenderer.js +196 -26
  44. package/dist/app/chat/chat/widgets/DataChartRenderer.js.map +1 -1
  45. package/dist/app/chat/chat/widgets/DataChartWidget.d.ts.map +1 -1
  46. package/dist/app/chat/chat/widgets/DataChartWidget.js +5 -1
  47. package/dist/app/chat/chat/widgets/DataChartWidget.js.map +1 -1
  48. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts +2 -2
  49. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts.map +1 -1
  50. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js +6 -4
  51. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js.map +1 -1
  52. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  53. package/dist/app/i18n/catalogs/ar-SA.js +1 -0
  54. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  55. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  56. package/dist/app/i18n/catalogs/de-DE.js +1 -0
  57. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  58. package/dist/app/i18n/catalogs/en-US.d.ts +1 -0
  59. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  60. package/dist/app/i18n/catalogs/en-US.js +1 -0
  61. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  62. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  63. package/dist/app/i18n/catalogs/es-ES.js +1 -0
  64. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  65. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  66. package/dist/app/i18n/catalogs/fr-FR.js +1 -0
  67. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  68. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  69. package/dist/app/i18n/catalogs/hi-IN.js +1 -0
  70. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  71. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  72. package/dist/app/i18n/catalogs/ja-JP.js +1 -0
  73. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  74. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  75. package/dist/app/i18n/catalogs/ko-KR.js +1 -0
  76. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  77. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  78. package/dist/app/i18n/catalogs/pt-BR.js +1 -0
  79. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  80. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  81. package/dist/app/i18n/catalogs/zh-CN.js +1 -0
  82. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  83. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  84. package/dist/app/i18n/catalogs/zh-TW.js +1 -0
  85. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  86. package/dist/composer/ComposerContextBanner.d.ts +3 -1
  87. package/dist/composer/ComposerContextBanner.d.ts.map +1 -1
  88. package/dist/composer/ComposerContextBanner.js +2 -2
  89. package/dist/composer/ComposerContextBanner.js.map +1 -1
  90. package/package.json +3 -3
  91. package/src/app/agentkit/react/chat.tsx +2 -0
  92. package/src/app/agentkit/react/components.interaction.spec.tsx +107 -0
  93. package/src/app/agentkit/react/components.spec.tsx +28 -1
  94. package/src/app/agentkit/react/components.tsx +153 -17
  95. package/src/app/agentkit/react/context.tsx +12 -0
  96. package/src/app/agentkit/react/index.ts +1 -0
  97. package/src/app/agentkit/react/lifecycle.spec.tsx +186 -16
  98. package/src/app/agentkit/react/root.tsx +7 -0
  99. package/src/app/agentkit/react/styles.css +43 -6
  100. package/src/app/agentkit/react/transcript-layout.spec.ts +72 -0
  101. package/src/app/agentkit/react/transcript-layout.ts +97 -0
  102. package/src/app/auth/AuthPage.attribution.spec.tsx +6 -0
  103. package/src/app/chat/AgentKitAssistantChat.spec.tsx +39 -0
  104. package/src/app/chat/AgentKitAssistantChat.tsx +5 -0
  105. package/src/app/chat/AgentPanel.page-header-share.spec.tsx +101 -0
  106. package/src/app/chat/AgentPanel.tsx +14 -2
  107. package/src/app/chat/chat/index.ts +2 -0
  108. package/src/app/chat/chat/surface-types.ts +10 -0
  109. package/src/app/chat/chat/tool-call-display.spec.tsx +83 -0
  110. package/src/app/chat/chat/tool-call-display.tsx +40 -8
  111. package/src/app/chat/chat/widgets/DataChartRenderer.spec.tsx +632 -0
  112. package/src/app/chat/chat/widgets/DataChartRenderer.tsx +308 -51
  113. package/src/app/chat/chat/widgets/DataChartWidget.spec.tsx +126 -23
  114. package/src/app/chat/chat/widgets/DataChartWidget.tsx +10 -0
  115. package/src/app/chat/chat/widgets/DataWidgetDisplayControls.tsx +26 -6
  116. package/src/app/i18n/catalogs/ar-SA.ts +1 -0
  117. package/src/app/i18n/catalogs/de-DE.ts +2 -0
  118. package/src/app/i18n/catalogs/en-US.ts +2 -0
  119. package/src/app/i18n/catalogs/es-ES.ts +2 -0
  120. package/src/app/i18n/catalogs/fr-FR.ts +2 -0
  121. package/src/app/i18n/catalogs/hi-IN.ts +1 -0
  122. package/src/app/i18n/catalogs/ja-JP.ts +2 -0
  123. package/src/app/i18n/catalogs/ko-KR.ts +1 -0
  124. package/src/app/i18n/catalogs/pt-BR.ts +2 -0
  125. package/src/app/i18n/catalogs/zh-CN.ts +1 -0
  126. package/src/app/i18n/catalogs/zh-TW.ts +1 -0
  127. package/src/composer/ComposerContextBanner.spec.tsx +31 -5
  128. package/src/composer/ComposerContextBanner.tsx +7 -2
@@ -40,6 +40,7 @@ import {
40
40
  import {
41
41
  createContext,
42
42
  Component,
43
+ Fragment,
43
44
  memo,
44
45
  useCallback,
45
46
  useContext,
@@ -49,6 +50,7 @@ import {
49
50
  useRef,
50
51
  useState,
51
52
  type ComponentType,
53
+ type CSSProperties,
52
54
  type ErrorInfo,
53
55
  type KeyboardEvent as ReactKeyboardEvent,
54
56
  type PointerEvent as ReactPointerEvent,
@@ -138,6 +140,11 @@ import {
138
140
  } from "./context.js";
139
141
  import { orderMessageContent } from "./message-content-order.js";
140
142
  import { AgentStreamingText } from "./streaming-text.js";
143
+ import {
144
+ workedForRowChevronStyle,
145
+ workedForRowSummaryStyle,
146
+ type AgentChatTranscriptLayout,
147
+ } from "./transcript-layout.js";
141
148
 
142
149
  // AgentKit React installs without @agent-native/core, so it repeats core's
143
150
  // replay markers. Its specs assert core's attribute names.
@@ -1465,7 +1472,8 @@ export function AgentActivityGroup({
1465
1472
  excludeAgentActivities?: boolean;
1466
1473
  isCurrentSegment?: boolean;
1467
1474
  }) {
1468
- const { threadId, slots, registry, labels } = useAgentKit();
1475
+ const { threadId, slots, registry, labels, formatWorkedForDuration } =
1476
+ useAgentKit();
1469
1477
  const thread = useAgentThread();
1470
1478
  const runEvents = runId
1471
1479
  ? thread.events.filter((event) => event.runId === runId)
@@ -1762,6 +1770,7 @@ export function AgentActivityGroup({
1762
1770
  }
1763
1771
  const hasExpandableActivity = displayGroups.length > 0;
1764
1772
  const formatDuration = (ms: number) =>
1773
+ formatWorkedForDuration?.(ms) ??
1765
1774
  formatAgentKitDuration(ms, {
1766
1775
  hour: labels.durationHourShort,
1767
1776
  minute: labels.durationMinuteShort,
@@ -1908,7 +1917,7 @@ function AgentWorkDisclosure({
1908
1917
  current?: ReactNode;
1909
1918
  children: ReactNode;
1910
1919
  }) {
1911
- const { slots } = useAgentKit();
1920
+ const { slots, workedForRowLayout } = useAgentKit();
1912
1921
  const [open, setOpen] = useState(false);
1913
1922
  const Summary = slots.workedForSummary;
1914
1923
  return (
@@ -1921,6 +1930,7 @@ function AgentWorkDisclosure({
1921
1930
  <summary
1922
1931
  className="agentkit-activities-summary"
1923
1932
  aria-disabled={!expandable}
1933
+ style={workedForRowSummaryStyle(workedForRowLayout)}
1924
1934
  onClick={(event) => {
1925
1935
  event.preventDefault();
1926
1936
  if (!expandable) return;
@@ -1934,12 +1944,14 @@ function AgentWorkDisclosure({
1934
1944
  expandable={expandable}
1935
1945
  open={open}
1936
1946
  current={current}
1947
+ layout={workedForRowLayout}
1937
1948
  />
1938
1949
  ) : (
1939
1950
  <>
1940
1951
  <IconChevronRight
1941
1952
  aria-hidden="true"
1942
1953
  className="agentkit-icon agentkit-summary-chevron"
1954
+ style={workedForRowChevronStyle(workedForRowLayout)}
1943
1955
  />
1944
1956
  <span className="agentkit-activities-summary-content">
1945
1957
  {label ? (
@@ -3798,11 +3810,15 @@ export function AgentMessageActions({
3798
3810
  );
3799
3811
  }
3800
3812
 
3813
+ const AgentKitPostWidgetTextContext =
3814
+ createContext<AgentChatTranscriptLayout["postWidgetText"]>(undefined);
3815
+
3801
3816
  export function AgentMessageView({
3802
3817
  value: message,
3803
3818
  threadId,
3804
3819
  }: AgentKitRenderProps<AgentMessage>) {
3805
3820
  const { labels, slots, resolveMessageContentOrder } = useAgentKit();
3821
+ const postWidgetText = useContext(AgentKitPostWidgetTextContext);
3806
3822
  const thread = useAgentThread(threadId);
3807
3823
  const visibleMessage = stripAgentMessageContext(message);
3808
3824
  const Supplement = slots.messageSupplement;
@@ -3843,6 +3859,7 @@ export function AgentMessageView({
3843
3859
  <Supplement value={visibleMessage} threadId={threadId} />
3844
3860
  ) : null;
3845
3861
  }
3862
+ let hasSeenWidget = false;
3846
3863
  return (
3847
3864
  <article
3848
3865
  className="agentkit-message"
@@ -3852,20 +3869,39 @@ export function AgentMessageView({
3852
3869
  aria-busy={message.status === "streaming"}
3853
3870
  >
3854
3871
  <div className="agentkit-message-content">
3855
- {orderedContent.map((item, index) => (
3856
- <AgentMessagePartView
3857
- key={item.key}
3858
- value={
3859
- item.type === "widget"
3860
- ? { type: "widget", widget: item.widget }
3861
- : item.part
3862
- }
3863
- threadId={threadId}
3864
- active={message.status === "streaming"}
3865
- resetKey={`${threadId}:${message.id}:${index}`}
3866
- userMessage={message.role === "user"}
3867
- />
3868
- ))}
3872
+ {orderedContent.map((item, index) => {
3873
+ const isWidget =
3874
+ item.type === "widget" ||
3875
+ (item.type === "part" && item.part.type === "widget");
3876
+ const followsWidget =
3877
+ message.role === "assistant" &&
3878
+ postWidgetText !== undefined &&
3879
+ hasSeenWidget &&
3880
+ item.type === "part" &&
3881
+ item.part.type === "text";
3882
+ if (isWidget) hasSeenWidget = true;
3883
+ const content = (
3884
+ <AgentMessagePartView
3885
+ value={
3886
+ item.type === "widget"
3887
+ ? { type: "widget", widget: item.widget }
3888
+ : item.part
3889
+ }
3890
+ threadId={threadId}
3891
+ active={message.status === "streaming"}
3892
+ resetKey={`${threadId}:${message.id}:${index}`}
3893
+ userMessage={message.role === "user"}
3894
+ />
3895
+ );
3896
+
3897
+ return followsWidget ? (
3898
+ <div key={item.key} data-post-widget-text="true">
3899
+ {content}
3900
+ </div>
3901
+ ) : (
3902
+ <Fragment key={item.key}>{content}</Fragment>
3903
+ );
3904
+ })}
3869
3905
  {attachedAnnotations.length ? (
3870
3906
  <div className="agentkit-annotations">
3871
3907
  {attachedAnnotations.map((annotation) => (
@@ -4800,9 +4836,103 @@ export interface AgentKitChatProps {
4800
4836
  emptyComposerPlacement?: "center" | "bottom";
4801
4837
  /** Follow new output until the user deliberately scrolls away. */
4802
4838
  autoScroll?: boolean;
4839
+ /** Optional transcript geometry. Omitted values keep the shared defaults. */
4840
+ transcriptLayout?: AgentChatTranscriptLayout;
4803
4841
  className?: string;
4804
4842
  }
4805
4843
 
4844
+ type AgentKitTranscriptContentStyle = CSSProperties & {
4845
+ "--agentkit-transcript-top-inset"?: string;
4846
+ "--agentkit-transcript-lane-width"?: string;
4847
+ "--agentkit-transcript-lane-offset-inline"?: string;
4848
+ "--agentkit-transcript-message-gap"?: string;
4849
+ "--agentkit-transcript-message-line-height"?: string;
4850
+ "--agentkit-transcript-font-family"?: string;
4851
+ "--agentkit-transcript-post-widget-text-color"?: string;
4852
+ "--agentkit-transcript-post-widget-paragraph-padding-block"?: string;
4853
+ "--agentkit-user-bubble-line-height"?: string;
4854
+ "--agentkit-user-bubble-max-width"?: string;
4855
+ "--agentkit-user-bubble-offset-inline"?: string;
4856
+ "--agentkit-user-bubble-padding-block"?: string;
4857
+ "--agentkit-user-bubble-padding-inline"?: string;
4858
+ };
4859
+
4860
+ function transcriptLength(value: number | undefined, name: string) {
4861
+ if (value === undefined) return undefined;
4862
+ if (!Number.isFinite(value) || value < 0) {
4863
+ throw new RangeError(
4864
+ `transcriptLayout.${name} must be a non-negative number`,
4865
+ );
4866
+ }
4867
+ return `${value}px`;
4868
+ }
4869
+
4870
+ function transcriptOffset(value: number | undefined, name: string) {
4871
+ if (value === undefined) return undefined;
4872
+ if (!Number.isFinite(value)) {
4873
+ throw new RangeError(`transcriptLayout.${name} must be a finite number`);
4874
+ }
4875
+ return `${value}px`;
4876
+ }
4877
+
4878
+ function transcriptContentStyle(
4879
+ layout?: AgentChatTranscriptLayout,
4880
+ ): AgentKitTranscriptContentStyle | undefined {
4881
+ if (!layout) return undefined;
4882
+ return {
4883
+ "--agentkit-transcript-top-inset": transcriptLength(
4884
+ layout.topInset,
4885
+ "topInset",
4886
+ ),
4887
+ "--agentkit-transcript-lane-width": transcriptLength(
4888
+ layout.messageLaneWidth,
4889
+ "messageLaneWidth",
4890
+ ),
4891
+ "--agentkit-transcript-lane-offset-inline": transcriptOffset(
4892
+ layout.messageLaneOffsetInline,
4893
+ "messageLaneOffsetInline",
4894
+ ),
4895
+ "--agentkit-transcript-message-gap": transcriptLength(
4896
+ layout.messageGap,
4897
+ "messageGap",
4898
+ ),
4899
+ "--agentkit-transcript-message-line-height": transcriptLength(
4900
+ layout.messageLineHeight,
4901
+ "messageLineHeight",
4902
+ ),
4903
+ "--agentkit-transcript-font-family": layout.fontFamily,
4904
+ "--agentkit-transcript-post-widget-text-color":
4905
+ layout.postWidgetText?.tone === "muted"
4906
+ ? "var(--agentkit-text-muted)"
4907
+ : undefined,
4908
+ "--agentkit-transcript-post-widget-paragraph-padding-block":
4909
+ transcriptLength(
4910
+ layout.postWidgetText?.paragraphPaddingBlock,
4911
+ "postWidgetText.paragraphPaddingBlock",
4912
+ ),
4913
+ "--agentkit-user-bubble-line-height": transcriptLength(
4914
+ layout.userBubbleLineHeight,
4915
+ "userBubbleLineHeight",
4916
+ ),
4917
+ "--agentkit-user-bubble-max-width": transcriptLength(
4918
+ layout.userBubbleMaxWidth,
4919
+ "userBubbleMaxWidth",
4920
+ ),
4921
+ "--agentkit-user-bubble-offset-inline": transcriptOffset(
4922
+ layout.userBubbleOffsetInline,
4923
+ "userBubbleOffsetInline",
4924
+ ),
4925
+ "--agentkit-user-bubble-padding-block": transcriptLength(
4926
+ layout.userBubblePadding?.block,
4927
+ "userBubblePadding.block",
4928
+ ),
4929
+ "--agentkit-user-bubble-padding-inline": transcriptLength(
4930
+ layout.userBubblePadding?.inline,
4931
+ "userBubblePadding.inline",
4932
+ ),
4933
+ };
4934
+ }
4935
+
4806
4936
  const AGENTKIT_TRANSCRIPT_BOTTOM_THRESHOLD = 24;
4807
4937
  const AGENTKIT_TRANSCRIPT_SCROLLBAR_IDLE_MS = 900;
4808
4938
  const AGENTKIT_TRANSCRIPT_SCROLL_KEYS = new Set([
@@ -4832,6 +4962,7 @@ export function AgentKitChat({
4832
4962
  hasRenderedMessages = false,
4833
4963
  emptyComposerPlacement = "center",
4834
4964
  autoScroll = true,
4965
+ transcriptLayout,
4835
4966
  className,
4836
4967
  }: AgentKitChatProps) {
4837
4968
  const { threadId, slots, registry, labels, onThreadForked } = useAgentKit();
@@ -5582,12 +5713,17 @@ export function AgentKitChat({
5582
5713
  <div
5583
5714
  ref={transcriptContentRef}
5584
5715
  className="agentkit-transcript-content"
5716
+ style={transcriptContentStyle(transcriptLayout)}
5585
5717
  >
5586
5718
  <AgentKitRegionSlot slot={Transcript} threadId={threadId}>
5587
5719
  {!hasConversation && EmptyState ? (
5588
5720
  <EmptyState threadId={threadId} />
5589
5721
  ) : null}
5590
- {transcriptItems}
5722
+ <AgentKitPostWidgetTextContext.Provider
5723
+ value={transcriptLayout?.postWidgetText}
5724
+ >
5725
+ {transcriptItems}
5726
+ </AgentKitPostWidgetTextContext.Provider>
5591
5727
  {approvalEntries.map(([approvalId, request]) => (
5592
5728
  <AgentKitSurfaceBoundary
5593
5729
  key={`${threadId}:${approvalId}`}
@@ -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}