@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
@@ -0,0 +1,246 @@
1
+ import type {
2
+ AgentEvent,
3
+ AgentMessage,
4
+ AgentMessagePart,
5
+ AgentWidget,
6
+ } from "@agent-native/agentkit/protocol";
7
+
8
+ export type AgentMessageContentItem =
9
+ | { key: string; type: "part"; part: AgentMessagePart }
10
+ | { key: string; type: "widget"; widget: AgentWidget };
11
+
12
+ export interface AgentMessageContentOrderInput {
13
+ message: AgentMessage;
14
+ parts: AgentMessagePart[];
15
+ widgets: AgentWidget[];
16
+ events: AgentEvent[];
17
+ defaultOrder: AgentMessageContentItem[];
18
+ }
19
+
20
+ export type AgentMessageContentOrderResolver = (
21
+ input: AgentMessageContentOrderInput,
22
+ ) => AgentMessageContentItem[];
23
+
24
+ type TextContentOrderItem = Extract<
25
+ AgentMessageContentItem,
26
+ { type: "part" }
27
+ > & {
28
+ part: Extract<AgentMessagePart, { type: "text" }>;
29
+ };
30
+
31
+ function isTextContentOrderItem(
32
+ item: AgentMessageContentItem | null | undefined,
33
+ ): item is TextContentOrderItem {
34
+ return (
35
+ item?.type === "part" &&
36
+ typeof item.key === "string" &&
37
+ item.part?.type === "text" &&
38
+ typeof item.part.text === "string"
39
+ );
40
+ }
41
+
42
+ function resolvedItemsPreserveContent(
43
+ defaultOrder: AgentMessageContentItem[],
44
+ resolvedOrder: AgentMessageContentItem[],
45
+ ): AgentMessageContentItem[] | undefined {
46
+ if (!Array.isArray(resolvedOrder)) return undefined;
47
+
48
+ const defaultText = defaultOrder
49
+ .filter(isTextContentOrderItem)
50
+ .map((item) => item.part.text)
51
+ .join("");
52
+ const resolvedText = resolvedOrder
53
+ .filter(isTextContentOrderItem)
54
+ .map((item) => item.part.text)
55
+ .join("");
56
+ if (defaultText !== resolvedText) return undefined;
57
+
58
+ const expectedItems = new Map<string, AgentMessageContentItem[]>();
59
+ for (const item of defaultOrder) {
60
+ if (item.type === "part" && item.part.type === "text") continue;
61
+ const identity = `${item.type}:${item.key}`;
62
+ const matchingItems = expectedItems.get(identity) ?? [];
63
+ matchingItems.push(item);
64
+ expectedItems.set(identity, matchingItems);
65
+ }
66
+
67
+ const preservedItems: AgentMessageContentItem[] = [];
68
+ for (const item of resolvedOrder) {
69
+ if (!item || typeof item !== "object") return undefined;
70
+ if (item.type === "part" && item.part?.type === "text") {
71
+ if (typeof item.key !== "string" || typeof item.part.text !== "string") {
72
+ return undefined;
73
+ }
74
+ preservedItems.push(item);
75
+ continue;
76
+ }
77
+
78
+ if (
79
+ (item.type !== "part" && item.type !== "widget") ||
80
+ typeof item.key !== "string"
81
+ ) {
82
+ return undefined;
83
+ }
84
+
85
+ const identity = `${item.type}:${item.key}`;
86
+ const matchingItems = expectedItems.get(identity);
87
+ const original = matchingItems?.shift();
88
+ if (!original) return undefined;
89
+ preservedItems.push(original);
90
+ }
91
+
92
+ if (
93
+ [...expectedItems.values()].some((matchingItems) => matchingItems.length)
94
+ ) {
95
+ return undefined;
96
+ }
97
+ return preservedItems;
98
+ }
99
+
100
+ /** Uses the stream timeline only when it reconstructs the saved text exactly. */
101
+ export function orderMessageContent(
102
+ message: AgentMessage,
103
+ visibleParts: AgentMessagePart[],
104
+ attachedWidgets: AgentWidget[],
105
+ events: AgentEvent[],
106
+ resolveOrder?: AgentMessageContentOrderResolver,
107
+ ): AgentMessageContentItem[] {
108
+ const fallback = [
109
+ ...visibleParts.map((part, index) => ({
110
+ key: `${message.id}-${index}`,
111
+ type: "part" as const,
112
+ part,
113
+ })),
114
+ ...attachedWidgets.map((widget) => ({
115
+ key: widget.id,
116
+ type: "widget" as const,
117
+ widget,
118
+ })),
119
+ ];
120
+ const resolve = (defaultOrder: AgentMessageContentItem[]) => {
121
+ const resolvedOrder = resolveOrder?.({
122
+ message,
123
+ parts: visibleParts,
124
+ widgets: attachedWidgets,
125
+ events,
126
+ defaultOrder,
127
+ });
128
+ if (resolvedOrder == null) return defaultOrder;
129
+ return (
130
+ resolvedItemsPreserveContent(defaultOrder, resolvedOrder) ?? defaultOrder
131
+ );
132
+ };
133
+
134
+ if (
135
+ attachedWidgets.length === 0 ||
136
+ message.parts.length !== 1 ||
137
+ message.parts[0]?.type !== "text" ||
138
+ visibleParts.length !== 1 ||
139
+ visibleParts[0]?.type !== "text" ||
140
+ visibleParts[0].text !== message.parts[0].text
141
+ ) {
142
+ return resolve(fallback);
143
+ }
144
+
145
+ const widgetIds = new Set(attachedWidgets.map((widget) => widget.id));
146
+ const widgetOffsets = new Map<string, number>();
147
+ const widgetEventOrder = new Map<string, number>();
148
+ let reconstructedText = "";
149
+ let textOffset = 0;
150
+
151
+ for (const [eventIndex, event] of events.entries()) {
152
+ if (event.type === "message.created" && event.message.id === message.id) {
153
+ const createdParts = event.message.parts;
154
+ if (
155
+ createdParts.length > 1 ||
156
+ createdParts.some((part) => part.type !== "text")
157
+ ) {
158
+ return resolve(fallback);
159
+ }
160
+ reconstructedText =
161
+ createdParts[0]?.type === "text" ? createdParts[0].text : "";
162
+ textOffset = reconstructedText.length;
163
+ continue;
164
+ }
165
+
166
+ if (event.type === "message.delta" && event.messageId === message.id) {
167
+ reconstructedText += event.text;
168
+ textOffset += event.text.length;
169
+ continue;
170
+ }
171
+
172
+ if (
173
+ (event.type === "widget.created" || event.type === "widget.updated") &&
174
+ event.messageId === message.id &&
175
+ widgetIds.has(event.widget.id) &&
176
+ !widgetOffsets.has(event.widget.id)
177
+ ) {
178
+ widgetOffsets.set(event.widget.id, textOffset);
179
+ widgetEventOrder.set(event.widget.id, eventIndex);
180
+ }
181
+ }
182
+
183
+ const fullText = message.parts[0].text;
184
+ if (
185
+ reconstructedText !== fullText ||
186
+ widgetOffsets.size !== attachedWidgets.length ||
187
+ [...widgetOffsets.values()].some((offset) => {
188
+ if (offset === 0 || offset === fullText.length) return false;
189
+ const preceding = fullText.charCodeAt(offset - 1);
190
+ const following = fullText.charCodeAt(offset);
191
+ return (
192
+ preceding >= 0xd800 &&
193
+ preceding <= 0xdbff &&
194
+ following >= 0xdc00 &&
195
+ following <= 0xdfff
196
+ );
197
+ })
198
+ ) {
199
+ return resolve(fallback);
200
+ }
201
+
202
+ const widgetsAtOffset = new Map<number, AgentWidget[]>();
203
+ for (const widget of attachedWidgets) {
204
+ const offset = widgetOffsets.get(widget.id)!;
205
+ const widgets = widgetsAtOffset.get(offset) ?? [];
206
+ widgets.push(widget);
207
+ widgetsAtOffset.set(offset, widgets);
208
+ }
209
+ for (const widgets of widgetsAtOffset.values()) {
210
+ widgets.sort(
211
+ (first, second) =>
212
+ widgetEventOrder.get(first.id)! - widgetEventOrder.get(second.id)!,
213
+ );
214
+ }
215
+
216
+ const insertions = [...widgetsAtOffset.entries()].sort(
217
+ ([first], [second]) => first - second,
218
+ );
219
+ const part = visibleParts[0];
220
+ if (part.type !== "text") return resolve(fallback);
221
+
222
+ const ordered: AgentMessageContentItem[] = [];
223
+ let cursor = 0;
224
+ for (const [offset, widgets] of insertions) {
225
+ if (offset > cursor) {
226
+ ordered.push({
227
+ key: `${message.id}-text-${cursor}`,
228
+ type: "part",
229
+ part: { ...part, text: part.text.slice(cursor, offset) },
230
+ });
231
+ }
232
+ for (const widget of widgets) {
233
+ ordered.push({ key: widget.id, type: "widget", widget });
234
+ }
235
+ cursor = offset;
236
+ }
237
+ if (cursor < part.text.length) {
238
+ ordered.push({
239
+ key: `${message.id}-text-${cursor}`,
240
+ type: "part",
241
+ part: { ...part, text: part.text.slice(cursor) },
242
+ });
243
+ }
244
+
245
+ return resolve(ordered);
246
+ }
@@ -30,6 +30,8 @@ import {
30
30
  type AgentKitRunUsageLoader,
31
31
  type AgentKitSlots,
32
32
  } from "./context.js";
33
+ import type { AgentMessageContentOrderResolver } from "./message-content-order.js";
34
+ import type { AgentChatWorkedForRowLayout } from "./transcript-layout.js";
33
35
 
34
36
  const mountedManagedClients = new WeakMap<AgentKitClient, number>();
35
37
 
@@ -84,6 +86,9 @@ export interface AgentKitRootBaseProps {
84
86
  request: AgentConnectionRequest,
85
87
  ) => Promise<AgentConnectionResponse>;
86
88
  onRenderError?: (failure: AgentKitRenderFailure) => void;
89
+ resolveMessageContentOrder?: AgentMessageContentOrderResolver;
90
+ workedForRowLayout?: AgentChatWorkedForRowLayout;
91
+ formatWorkedForDuration?: (durationMs: number) => string;
87
92
  onClientEffect?: (effect: {
88
93
  type: "client.effect" | "client.deeplink";
89
94
  name: string;
@@ -113,6 +118,9 @@ export function AgentKitRoot({
113
118
  loadRunUsage,
114
119
  onConnectionRequest,
115
120
  onRenderError,
121
+ resolveMessageContentOrder,
122
+ workedForRowLayout,
123
+ formatWorkedForDuration,
116
124
  onClientEffect,
117
125
  load = "auto",
118
126
  onLoadError,
@@ -290,6 +298,9 @@ export function AgentKitRoot({
290
298
  loadRunUsage={loadRunUsage}
291
299
  onConnectionRequest={onConnectionRequest}
292
300
  onRenderError={onRenderError}
301
+ resolveMessageContentOrder={resolveMessageContentOrder}
302
+ workedForRowLayout={workedForRowLayout}
303
+ formatWorkedForDuration={formatWorkedForDuration}
293
304
  onClientEffect={onClientEffect}
294
305
  >
295
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;
@@ -1216,7 +1253,7 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
1216
1253
  inline-size: fit-content;
1217
1254
  max-inline-size: 100%;
1218
1255
  align-items: center;
1219
- gap: 0.375rem;
1256
+ gap: 1rem;
1220
1257
  }
1221
1258
 
1222
1259
  .agentkit-activities {
@@ -1418,7 +1455,7 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
1418
1455
  }
1419
1456
 
1420
1457
  .agentkit-activities-summary > .agentkit-summary-chevron {
1421
- opacity: 0;
1458
+ opacity: 1;
1422
1459
  }
1423
1460
 
1424
1461
  .agentkit-activities[data-expandable="true"]
@@ -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(
@@ -1054,6 +1054,27 @@ describe("AgentKitAssistantChat host behavior", () => {
1054
1054
  expect(chatMocks.composerProps.isReferenceTarget).toBe(true);
1055
1055
  });
1056
1056
 
1057
+ it("passes the worked-for row layout into the shared AgentKit root", async () => {
1058
+ const layout = {
1059
+ chevronSize: 12,
1060
+ iconGap: 8,
1061
+ fontSize: 13,
1062
+ lineHeight: 16,
1063
+ paddingBlock: 2,
1064
+ paddingInline: 4,
1065
+ };
1066
+ const formatWorkedForDuration = (durationMs: number) =>
1067
+ `${Math.round(durationMs / 1000)} seconds`;
1068
+ await mount(
1069
+ baseProps({ workedForRowLayout: layout, formatWorkedForDuration }),
1070
+ );
1071
+
1072
+ expect(chatMocks.rootProps.workedForRowLayout).toEqual(layout);
1073
+ expect(chatMocks.rootProps.formatWorkedForDuration).toBe(
1074
+ formatWorkedForDuration,
1075
+ );
1076
+ });
1077
+
1057
1078
  it("reports whether a composer can hold a prefill with its current context", async () => {
1058
1079
  const ref = createRef<AssistantChatHandle>();
1059
1080
  await mount(baseProps(), ref);
@@ -3271,6 +3292,24 @@ describe("AgentKitAssistantChat host behavior", () => {
3271
3292
  ).not.toBeNull();
3272
3293
  });
3273
3294
 
3295
+ it("forwards transcript layout options to the shared chat component", async () => {
3296
+ const transcriptLayout = {
3297
+ topInset: 5,
3298
+ messageLaneWidth: 730,
3299
+ messageLaneOffsetInline: -33,
3300
+ messageGap: 12,
3301
+ messageLineHeight: 20,
3302
+ fontFamily: "IBM Plex Sans",
3303
+ userBubbleLineHeight: 18,
3304
+ userBubbleMaxWidth: 600,
3305
+ userBubbleOffsetInline: 10,
3306
+ userBubblePadding: { block: 8, inline: 13 },
3307
+ };
3308
+ await mount(baseProps({ transcriptLayout }));
3309
+
3310
+ expect(chatMocks.chatProps.transcriptLayout).toBe(transcriptLayout);
3311
+ });
3312
+
3274
3313
  it("anchors the composer at the bottom while restoring a persisted thread", async () => {
3275
3314
  await mount(
3276
3315
  baseProps({
@@ -3348,6 +3387,25 @@ describe("AgentKitAssistantChat host behavior", () => {
3348
3387
  expect(container.querySelector(".agentkit-home-intro")).toBeNull();
3349
3388
  });
3350
3389
 
3390
+ it("renders the shared context banner slot alongside the existing composer slot", async () => {
3391
+ await mount(
3392
+ baseProps({
3393
+ composerSlot: <div data-testid="legacy-composer-slot">Notice</div>,
3394
+ composerContextBannerSlot: (
3395
+ <div data-testid="composer-context-banner-slot">Add context</div>
3396
+ ),
3397
+ }),
3398
+ );
3399
+
3400
+ const composer = container.querySelector(".agentkit-host-composer")!;
3401
+ expect(
3402
+ composer.querySelector('[data-testid="legacy-composer-slot"]'),
3403
+ ).not.toBeNull();
3404
+ expect(
3405
+ composer.querySelector('[data-testid="composer-context-banner-slot"]'),
3406
+ ).not.toBeNull();
3407
+ });
3408
+
3351
3409
  it("puts the beginning revert before messages inside the scrollable transcript", async () => {
3352
3410
  chatMocks.thread.messages = [
3353
3411
  { id: "first-message", role: "user", parts: [] },