@agent-native/toolkit 0.212.1 → 0.214.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 (209) hide show
  1. package/agent-native.eject.json +9 -0
  2. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  3. package/dist/app/agentkit/react/components.js +10 -4
  4. package/dist/app/agentkit/react/components.js.map +1 -1
  5. package/dist/app/agentkit/react/context.d.ts +13 -1
  6. package/dist/app/agentkit/react/context.d.ts.map +1 -1
  7. package/dist/app/agentkit/react/context.js +3 -1
  8. package/dist/app/agentkit/react/context.js.map +1 -1
  9. package/dist/app/agentkit/react/index.d.ts +1 -0
  10. package/dist/app/agentkit/react/index.d.ts.map +1 -1
  11. package/dist/app/agentkit/react/index.js +1 -0
  12. package/dist/app/agentkit/react/index.js.map +1 -1
  13. package/dist/app/agentkit/react/message-content-order.d.ts +21 -0
  14. package/dist/app/agentkit/react/message-content-order.d.ts.map +1 -0
  15. package/dist/app/agentkit/react/message-content-order.js +173 -0
  16. package/dist/app/agentkit/react/message-content-order.js.map +1 -0
  17. package/dist/app/agentkit/react/root.d.ts +3 -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 +2 -2
  22. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  23. package/dist/app/chat/AgentKitAssistantChat.js +7 -2
  24. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  25. package/dist/app/chat/AgentPanel.d.ts +14 -2
  26. package/dist/app/chat/AgentPanel.d.ts.map +1 -1
  27. package/dist/app/chat/AgentPanel.js +22 -5
  28. package/dist/app/chat/AgentPanel.js.map +1 -1
  29. package/dist/app/chat/chat/surface-types.d.ts +8 -0
  30. package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
  31. package/dist/app/chat/chat/surface-types.js.map +1 -1
  32. package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts.map +1 -1
  33. package/dist/app/chat/chat/widgets/DataChartRenderer.js +127 -30
  34. package/dist/app/chat/chat/widgets/DataChartRenderer.js.map +1 -1
  35. package/dist/app/chat/chat/widgets/DataChartWidget.d.ts +3 -2
  36. package/dist/app/chat/chat/widgets/DataChartWidget.d.ts.map +1 -1
  37. package/dist/app/chat/chat/widgets/DataChartWidget.js +14 -9
  38. package/dist/app/chat/chat/widgets/DataChartWidget.js.map +1 -1
  39. package/dist/app/chat/chat/widgets/DataInsightsWidget.js +1 -1
  40. package/dist/app/chat/chat/widgets/DataInsightsWidget.js.map +1 -1
  41. package/dist/app/chat/chat/widgets/DataTableWidget.d.ts +3 -2
  42. package/dist/app/chat/chat/widgets/DataTableWidget.d.ts.map +1 -1
  43. package/dist/app/chat/chat/widgets/DataTableWidget.js +10 -66
  44. package/dist/app/chat/chat/widgets/DataTableWidget.js.map +1 -1
  45. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts +11 -0
  46. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts.map +1 -0
  47. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js +131 -0
  48. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js.map +1 -0
  49. package/dist/app/chat/chat/widgets/builtin-tool-renderers.js +2 -2
  50. package/dist/app/chat/chat/widgets/builtin-tool-renderers.js.map +1 -1
  51. package/dist/app/chat/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  52. package/dist/app/chat/mcp-apps/McpAppRenderer.js +6 -5
  53. package/dist/app/chat/mcp-apps/McpAppRenderer.js.map +1 -1
  54. package/dist/app/chat/mcp-apps/mcp-app-theme.d.ts +2 -0
  55. package/dist/app/chat/mcp-apps/mcp-app-theme.d.ts.map +1 -0
  56. package/dist/app/chat/mcp-apps/mcp-app-theme.js +11 -0
  57. package/dist/app/chat/mcp-apps/mcp-app-theme.js.map +1 -0
  58. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  59. package/dist/app/i18n/catalogs/ar-SA.js +10 -0
  60. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  61. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  62. package/dist/app/i18n/catalogs/de-DE.js +10 -0
  63. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  64. package/dist/app/i18n/catalogs/en-US.d.ts +10 -0
  65. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  66. package/dist/app/i18n/catalogs/en-US.js +10 -0
  67. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  68. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  69. package/dist/app/i18n/catalogs/es-ES.js +10 -0
  70. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  71. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  72. package/dist/app/i18n/catalogs/fr-FR.js +10 -0
  73. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  74. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  75. package/dist/app/i18n/catalogs/hi-IN.js +10 -0
  76. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  77. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  78. package/dist/app/i18n/catalogs/ja-JP.js +10 -0
  79. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  80. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  81. package/dist/app/i18n/catalogs/ko-KR.js +10 -0
  82. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  83. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  84. package/dist/app/i18n/catalogs/pt-BR.js +10 -0
  85. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  86. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  87. package/dist/app/i18n/catalogs/zh-CN.js +10 -0
  88. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  89. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  90. package/dist/app/i18n/catalogs/zh-TW.js +10 -0
  91. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  92. package/dist/app/org/AuthenticationSection.d.ts +3 -0
  93. package/dist/app/org/AuthenticationSection.d.ts.map +1 -1
  94. package/dist/app/org/AuthenticationSection.js +9 -3
  95. package/dist/app/org/AuthenticationSection.js.map +1 -1
  96. package/dist/app/org/pages/OrgAuthenticationPage.d.ts.map +1 -1
  97. package/dist/app/org/pages/OrgAuthenticationPage.js +2 -2
  98. package/dist/app/org/pages/OrgAuthenticationPage.js.map +1 -1
  99. package/dist/app/providers/AppProviders.d.ts +2 -1
  100. package/dist/app/providers/AppProviders.d.ts.map +1 -1
  101. package/dist/app/providers/AppProviders.js +4 -3
  102. package/dist/app/providers/AppProviders.js.map +1 -1
  103. package/dist/app/providers/ThemeFamilySync.d.ts +2 -0
  104. package/dist/app/providers/ThemeFamilySync.d.ts.map +1 -0
  105. package/dist/app/providers/ThemeFamilySync.js +6 -0
  106. package/dist/app/providers/ThemeFamilySync.js.map +1 -0
  107. package/dist/app/settings/account/PreferencesSettings.d.ts.map +1 -1
  108. package/dist/app/settings/account/PreferencesSettings.js +2 -1
  109. package/dist/app/settings/account/PreferencesSettings.js.map +1 -1
  110. package/dist/app/settings/account/ThemeFamilySettings.d.ts +2 -0
  111. package/dist/app/settings/account/ThemeFamilySettings.d.ts.map +1 -0
  112. package/dist/app/settings/account/ThemeFamilySettings.js +31 -0
  113. package/dist/app/settings/account/ThemeFamilySettings.js.map +1 -0
  114. package/dist/app/settings/account/search-entries.d.ts.map +1 -1
  115. package/dist/app/settings/account/search-entries.js +6 -0
  116. package/dist/app/settings/account/search-entries.js.map +1 -1
  117. package/dist/app/shared/DefaultSpinner.d.ts.map +1 -1
  118. package/dist/app/shared/DefaultSpinner.js +4 -5
  119. package/dist/app/shared/DefaultSpinner.js.map +1 -1
  120. package/dist/app/shared/ErrorBoundary.d.ts +1 -0
  121. package/dist/app/shared/ErrorBoundary.d.ts.map +1 -1
  122. package/dist/app/shared/ErrorBoundary.js +27 -12
  123. package/dist/app/shared/ErrorBoundary.js.map +1 -1
  124. package/dist/app/shared/PoweredByBadge.d.ts.map +1 -1
  125. package/dist/app/shared/PoweredByBadge.js +8 -49
  126. package/dist/app/shared/PoweredByBadge.js.map +1 -1
  127. package/dist/app/styles/agent-conversation.css +0 -23
  128. package/dist/app/styles/agent-native.css +1 -1
  129. package/dist/app/styles/theme-families.css +66 -0
  130. package/dist/composer/ComposerContextBanner.d.ts +22 -0
  131. package/dist/composer/ComposerContextBanner.d.ts.map +1 -0
  132. package/dist/composer/ComposerContextBanner.js +9 -0
  133. package/dist/composer/ComposerContextBanner.js.map +1 -0
  134. package/dist/composer/ComposerModePicker.d.ts +26 -0
  135. package/dist/composer/ComposerModePicker.d.ts.map +1 -0
  136. package/dist/composer/ComposerModePicker.js +14 -0
  137. package/dist/composer/ComposerModePicker.js.map +1 -0
  138. package/dist/composer/index.d.ts +2 -0
  139. package/dist/composer/index.d.ts.map +1 -1
  140. package/dist/composer/index.js +2 -0
  141. package/dist/composer/index.js.map +1 -1
  142. package/dist/styles.css +1 -0
  143. package/package.json +3 -3
  144. package/src/app/agentkit/react/components.spec.tsx +12 -0
  145. package/src/app/agentkit/react/components.tsx +41 -22
  146. package/src/app/agentkit/react/context.tsx +17 -0
  147. package/src/app/agentkit/react/index.ts +1 -0
  148. package/src/app/agentkit/react/lifecycle.spec.tsx +116 -1
  149. package/src/app/agentkit/react/message-content-order.spec.ts +190 -0
  150. package/src/app/agentkit/react/message-content-order.ts +246 -0
  151. package/src/app/agentkit/react/root.tsx +4 -0
  152. package/src/app/agentkit/react/styles.css +2 -2
  153. package/src/app/chat/AgentKitAssistantChat.spec.tsx +19 -0
  154. package/src/app/chat/AgentKitAssistantChat.tsx +10 -1
  155. package/src/app/chat/AgentPanel.header.spec.ts +72 -14
  156. package/src/app/chat/AgentPanel.tsx +97 -53
  157. package/src/app/chat/chat/surface-types.ts +8 -0
  158. package/src/app/chat/chat/widgets/DataChartRenderer.spec.tsx +217 -0
  159. package/src/app/chat/chat/widgets/DataChartRenderer.tsx +306 -68
  160. package/src/app/chat/chat/widgets/DataChartWidget.spec.tsx +329 -0
  161. package/src/app/chat/chat/widgets/DataChartWidget.ssr.spec.tsx +25 -0
  162. package/src/app/chat/chat/widgets/DataChartWidget.tsx +48 -33
  163. package/src/app/chat/chat/widgets/DataInsightsWidget.tsx +2 -2
  164. package/src/app/chat/chat/widgets/DataTableWidget.spec.tsx +37 -3
  165. package/src/app/chat/chat/widgets/DataTableWidget.tsx +22 -86
  166. package/src/app/chat/chat/widgets/DataWidgetDisplayControls.tsx +301 -0
  167. package/src/app/chat/chat/widgets/builtin-tool-renderers.tsx +5 -5
  168. package/src/app/chat/mcp-apps/McpAppRenderer.tsx +10 -5
  169. package/src/app/chat/mcp-apps/mcp-app-theme.spec.ts +30 -0
  170. package/src/app/chat/mcp-apps/mcp-app-theme.ts +11 -0
  171. package/src/app/i18n/catalogs/ar-SA.ts +12 -0
  172. package/src/app/i18n/catalogs/de-DE.ts +13 -0
  173. package/src/app/i18n/catalogs/en-US.ts +12 -0
  174. package/src/app/i18n/catalogs/es-ES.ts +14 -0
  175. package/src/app/i18n/catalogs/fr-FR.ts +14 -0
  176. package/src/app/i18n/catalogs/hi-IN.ts +12 -0
  177. package/src/app/i18n/catalogs/ja-JP.ts +12 -0
  178. package/src/app/i18n/catalogs/ko-KR.ts +12 -0
  179. package/src/app/i18n/catalogs/pt-BR.ts +13 -0
  180. package/src/app/i18n/catalogs/zh-CN.ts +11 -0
  181. package/src/app/i18n/catalogs/zh-TW.ts +12 -0
  182. package/src/app/org/AuthenticationSection.spec.tsx +62 -0
  183. package/src/app/org/AuthenticationSection.tsx +47 -1
  184. package/src/app/org/pages/OrgAuthenticationPage.tsx +4 -0
  185. package/src/app/org/pages/OrgSettingsPages.spec.tsx +3 -0
  186. package/src/app/providers/AppProviders.spec.tsx +71 -0
  187. package/src/app/providers/AppProviders.tsx +10 -0
  188. package/src/app/providers/ThemeFamilySync.tsx +6 -0
  189. package/src/app/settings/account/PreferencesSettings.spec.tsx +13 -2
  190. package/src/app/settings/account/PreferencesSettings.tsx +8 -0
  191. package/src/app/settings/account/ThemeFamilySettings.tsx +61 -0
  192. package/src/app/settings/account/search-entries.ts +6 -0
  193. package/src/app/shared/DefaultSpinner.spec.tsx +14 -0
  194. package/src/app/shared/DefaultSpinner.tsx +4 -5
  195. package/src/app/shared/ErrorBoundary.spec.tsx +45 -2
  196. package/src/app/shared/ErrorBoundary.tsx +37 -12
  197. package/src/app/shared/PoweredByBadge.spec.tsx +16 -0
  198. package/src/app/shared/PoweredByBadge.tsx +8 -50
  199. package/src/app/styles/agent-conversation.css +0 -23
  200. package/src/app/styles/agent-native.css +1 -1
  201. package/src/app/styles/agent-native.spec.ts +18 -0
  202. package/src/app/styles/theme-families.css +66 -0
  203. package/src/app/styles/theme-families.spec.ts +29 -0
  204. package/src/composer/ComposerContextBanner.spec.tsx +74 -0
  205. package/src/composer/ComposerContextBanner.tsx +72 -0
  206. package/src/composer/ComposerModePicker.spec.tsx +101 -0
  207. package/src/composer/ComposerModePicker.tsx +98 -0
  208. package/src/composer/index.ts +9 -0
  209. 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,7 @@ import {
30
30
  type AgentKitRunUsageLoader,
31
31
  type AgentKitSlots,
32
32
  } from "./context.js";
33
+ import type { AgentMessageContentOrderResolver } from "./message-content-order.js";
33
34
 
34
35
  const mountedManagedClients = new WeakMap<AgentKitClient, number>();
35
36
 
@@ -84,6 +85,7 @@ export interface AgentKitRootBaseProps {
84
85
  request: AgentConnectionRequest,
85
86
  ) => Promise<AgentConnectionResponse>;
86
87
  onRenderError?: (failure: AgentKitRenderFailure) => void;
88
+ resolveMessageContentOrder?: AgentMessageContentOrderResolver;
87
89
  onClientEffect?: (effect: {
88
90
  type: "client.effect" | "client.deeplink";
89
91
  name: string;
@@ -113,6 +115,7 @@ export function AgentKitRoot({
113
115
  loadRunUsage,
114
116
  onConnectionRequest,
115
117
  onRenderError,
118
+ resolveMessageContentOrder,
116
119
  onClientEffect,
117
120
  load = "auto",
118
121
  onLoadError,
@@ -290,6 +293,7 @@ export function AgentKitRoot({
290
293
  loadRunUsage={loadRunUsage}
291
294
  onConnectionRequest={onConnectionRequest}
292
295
  onRenderError={onRenderError}
296
+ resolveMessageContentOrder={resolveMessageContentOrder}
293
297
  onClientEffect={onClientEffect}
294
298
  >
295
299
  {children}
@@ -1216,7 +1216,7 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
1216
1216
  inline-size: fit-content;
1217
1217
  max-inline-size: 100%;
1218
1218
  align-items: center;
1219
- gap: 0.375rem;
1219
+ gap: 1rem;
1220
1220
  }
1221
1221
 
1222
1222
  .agentkit-activities {
@@ -1418,7 +1418,7 @@ body:has(> [data-agent-native-composer-popover="true"]) > .fixed.inset-0 {
1418
1418
  }
1419
1419
 
1420
1420
  .agentkit-activities-summary > .agentkit-summary-chevron {
1421
- opacity: 0;
1421
+ opacity: 1;
1422
1422
  }
1423
1423
 
1424
1424
  .agentkit-activities[data-expandable="true"]
@@ -3348,6 +3348,25 @@ describe("AgentKitAssistantChat host behavior", () => {
3348
3348
  expect(container.querySelector(".agentkit-home-intro")).toBeNull();
3349
3349
  });
3350
3350
 
3351
+ it("renders the shared context banner slot alongside the existing composer slot", async () => {
3352
+ await mount(
3353
+ baseProps({
3354
+ composerSlot: <div data-testid="legacy-composer-slot">Notice</div>,
3355
+ composerContextBannerSlot: (
3356
+ <div data-testid="composer-context-banner-slot">Add context</div>
3357
+ ),
3358
+ }),
3359
+ );
3360
+
3361
+ const composer = container.querySelector(".agentkit-host-composer")!;
3362
+ expect(
3363
+ composer.querySelector('[data-testid="legacy-composer-slot"]'),
3364
+ ).not.toBeNull();
3365
+ expect(
3366
+ composer.querySelector('[data-testid="composer-context-banner-slot"]'),
3367
+ ).not.toBeNull();
3368
+ });
3369
+
3351
3370
  it("puts the beginning revert before messages inside the scrollable transcript", async () => {
3352
3371
  chatMocks.thread.messages = [
3353
3372
  { id: "first-message", role: "user", parts: [] },
@@ -2093,9 +2093,17 @@ export const AgentKitAssistantChat = forwardRef<
2093
2093
  threadId={threadId}
2094
2094
  load={agentKitLoad}
2095
2095
  onLoadError={onThreadRestoreLoadError}
2096
- slots={agentKitSlots}
2096
+ slots={
2097
+ props.workedForSummarySlot
2098
+ ? {
2099
+ ...agentKitSlots,
2100
+ workedForSummary: props.workedForSummarySlot,
2101
+ }
2102
+ : agentKitSlots
2103
+ }
2097
2104
  registry={agentKitRegistry}
2098
2105
  labels={labels}
2106
+ resolveMessageContentOrder={props.resolveMessageContentOrder}
2099
2107
  branchNavigation={props.branchNavigation}
2100
2108
  loadRunUsage={loadRunUsage}
2101
2109
  buildFeedbackReport={formatFeedbackReport}
@@ -5979,6 +5987,7 @@ function AgentKitComposerSurface({
5979
5987
  })}
5980
5988
  />
5981
5989
  {props.composerSlot}
5990
+ {props.composerContextBannerSlot}
5982
5991
  {showHomeIntro ? (
5983
5992
  <div className="agentkit-home-intro">{props.homeIntroSlot}</div>
5984
5993
  ) : null}
@@ -31,6 +31,8 @@ import {
31
31
  shouldHandleAgentSidebarToggle,
32
32
  shouldHandleAgentPanelChatShortcut,
33
33
  shouldShowAgentPanelFullViewAction,
34
+ resolveAgentPanelPageHeaderActions,
35
+ resolveAgentPanelPageToolbarSlot,
34
36
  shouldShowAgentPanelPageHeader,
35
37
  shouldShowAgentPanelPageNewChatButton,
36
38
  shouldShowAgentPanelChatTabBar,
@@ -350,22 +352,60 @@ describe("AgentPanel header tab visibility", () => {
350
352
  expect(shouldDefaultAgentChatSurfacePageHeader(undefined)).toBe(false);
351
353
  });
352
354
 
353
- it("exposes page header composition without moving it into app chrome", () => {
354
- const source = readSource("src/app/chat/AgentPanel.tsx", "utf8");
355
+ it("keeps share and host toolbar actions when the title menu is hidden", async () => {
356
+ expect(
357
+ resolveAgentPanelPageHeaderActions({
358
+ showPageTitleMenu: false,
359
+ canShareActiveTab: true,
360
+ }),
361
+ ).toEqual({ showPageTitleMenu: false, showShareAction: true });
355
362
 
356
- expect(source).toContain('data-agent-page-chat-header=""');
357
- expect(source).toContain("pageHeaderLeadingSlot");
358
- expect(source).toContain("pageToolbarSlot");
359
- expect(source).toContain("activeTab?.label");
360
- expect(source).toContain('data-agent-page-title-menu=""');
361
- expect(source).toContain("<IconShare3 size={15}");
362
- expect(source.indexOf("<IconShare3 size={15}")).toBeLessThan(
363
- source.indexOf(
364
- "{pageToolbarSlot}",
365
- source.indexOf("triggerContent={<IconShare3"),
366
- ),
363
+ const container = document.createElement("div");
364
+ document.body.append(container);
365
+ const root = createRoot(container);
366
+ const toggleHistory = vi.fn();
367
+ const clearActiveTab = vi.fn();
368
+ const toolbar = resolveAgentPanelPageToolbarSlot(
369
+ (actions) =>
370
+ React.createElement(
371
+ "div",
372
+ null,
373
+ React.createElement(
374
+ "button",
375
+ { onClick: actions.toggleHistory },
376
+ actions.showHistory ? "Hide chats" : "All chats",
377
+ ),
378
+ actions.activeTabMessageCount > 0
379
+ ? React.createElement(
380
+ "button",
381
+ { onClick: actions.clearActiveTab },
382
+ "Clear chat",
383
+ )
384
+ : null,
385
+ ),
386
+ {
387
+ activeTabMessageCount: 2,
388
+ clearActiveTab,
389
+ showHistory: false,
390
+ toggleHistory,
391
+ },
367
392
  );
368
- expect(source).toContain("border-b border-border/70");
393
+
394
+ try {
395
+ await act(async () => root.render(toolbar));
396
+ const buttons = container.querySelectorAll("button");
397
+ expect([...buttons].map((button) => button.textContent)).toEqual([
398
+ "All chats",
399
+ "Clear chat",
400
+ ]);
401
+ await act(async () => buttons[0]?.click());
402
+ await act(async () => buttons[1]?.click());
403
+ expect(toggleHistory).toHaveBeenCalledOnce();
404
+ expect(clearActiveTab).toHaveBeenCalledOnce();
405
+ } finally {
406
+ await act(async () => root.unmount());
407
+ container.remove();
408
+ }
369
409
  });
370
410
 
371
411
  it("normalizes legacy and unknown modes back to chat", () => {
@@ -1193,6 +1233,24 @@ describe("AgentChatSurface chrome defaults", () => {
1193
1233
  expect(surface.props.showHeader).toBe(true);
1194
1234
  expect(surface.props.showTabBar).toBe(true);
1195
1235
  });
1236
+
1237
+ it("allows a page host to hide only the title menu", () => {
1238
+ const surface = AgentChatSurface({
1239
+ mode: "page",
1240
+ showPageTitleMenu: false,
1241
+ });
1242
+ const panel = surface.props.children[1];
1243
+ const headerActions = resolveAgentPanelPageHeaderActions({
1244
+ showPageTitleMenu: false,
1245
+ canShareActiveTab: true,
1246
+ });
1247
+
1248
+ expect(panel.props.showPageTitleMenu).toBe(false);
1249
+ expect(headerActions).toEqual({
1250
+ showPageTitleMenu: false,
1251
+ showShareAction: true,
1252
+ });
1253
+ });
1196
1254
  });
1197
1255
 
1198
1256
  describe("AgentPanel stale lazy chunk recovery", () => {
@@ -441,6 +441,30 @@ export function shouldShowAgentPanelPageHeader(
441
441
  );
442
442
  }
443
443
 
444
+ export function resolveAgentPanelPageHeaderActions({
445
+ showPageTitleMenu = true,
446
+ canShareActiveTab,
447
+ }: {
448
+ showPageTitleMenu?: boolean;
449
+ canShareActiveTab: boolean;
450
+ }) {
451
+ return { showPageTitleMenu, showShareAction: canShareActiveTab };
452
+ }
453
+
454
+ export type AgentPanelPageToolbarActions = Pick<
455
+ MultiTabAssistantChatHeaderCallbackProps,
456
+ "activeTabMessageCount" | "clearActiveTab" | "showHistory" | "toggleHistory"
457
+ >;
458
+
459
+ export function resolveAgentPanelPageToolbarSlot(
460
+ slot:
461
+ | React.ReactNode
462
+ | ((actions: AgentPanelPageToolbarActions) => React.ReactNode),
463
+ actions: AgentPanelPageToolbarActions,
464
+ ) {
465
+ return typeof slot === "function" ? slot(actions) : slot;
466
+ }
467
+
444
468
  export function shouldShowAgentPanelCliTabBar(cliTabs: string[]) {
445
469
  return cliTabs.length > 1;
446
470
  }
@@ -583,8 +607,13 @@ export interface AgentPanelProps extends Omit<
583
607
  showPageNewChatButton?: boolean;
584
608
  showPageHeader?: boolean;
585
609
  showPageHeaderWhenEmpty?: boolean;
610
+ /** Hide the default page title menu while keeping the shared page actions. */
611
+ showPageTitleMenu?: boolean;
586
612
  pageHeaderLeadingSlot?: React.ReactNode;
587
- pageToolbarSlot?: React.ReactNode;
613
+ /** Render host page actions alongside Share, with the active chat controls. */
614
+ pageToolbarSlot?:
615
+ | React.ReactNode
616
+ | ((actions: AgentPanelPageToolbarActions) => React.ReactNode);
588
617
  onPageHeaderVisibilityChange?: (visible: boolean) => void;
589
618
  chatOnly?: boolean;
590
619
  agentPageHref?: string;
@@ -778,6 +807,7 @@ function AgentPanelInner({
778
807
  showPageNewChatButton = false,
779
808
  showPageHeader = false,
780
809
  showPageHeaderWhenEmpty = false,
810
+ showPageTitleMenu = true,
781
811
  pageHeaderLeadingSlot,
782
812
  pageToolbarSlot,
783
813
  onPageHeaderVisibilityChange,
@@ -1694,11 +1724,16 @@ function AgentPanelInner({
1694
1724
  showPageHeaderWhenEmpty,
1695
1725
  activeTabIsPersisted,
1696
1726
  );
1697
- const canShareActiveTab =
1727
+ const canShareActiveTab = Boolean(
1698
1728
  activeTab &&
1699
1729
  (activeTabIsPersisted ||
1700
1730
  activeTabMessageCount > 0 ||
1701
- activeTab.status !== "idle");
1731
+ activeTab.status !== "idle"),
1732
+ );
1733
+ const pageHeaderActions = resolveAgentPanelPageHeaderActions({
1734
+ showPageTitleMenu,
1735
+ canShareActiveTab,
1736
+ });
1702
1737
  const showNewChatAction =
1703
1738
  showPageNewChatButton &&
1704
1739
  shouldShowAgentPanelPageNewChatButton(
@@ -1707,6 +1742,12 @@ function AgentPanelInner({
1707
1742
  activeTabMessageCount,
1708
1743
  activeTabIsPersisted,
1709
1744
  );
1745
+ const toolbarSlot = resolveAgentPanelPageToolbarSlot(pageToolbarSlot, {
1746
+ activeTabMessageCount,
1747
+ clearActiveTab,
1748
+ showHistory,
1749
+ toggleHistory,
1750
+ });
1710
1751
 
1711
1752
  return (
1712
1753
  <>
@@ -1730,62 +1771,64 @@ function AgentPanelInner({
1730
1771
  <h1 className="truncate text-xs font-medium text-foreground">
1731
1772
  {activeTab?.label || t("agentPanel.newChat")}
1732
1773
  </h1>
1733
- <DropdownMenu
1734
- open={headerMenuOpen}
1735
- onOpenChange={setHeaderMenuOpen}
1736
- >
1737
- <DropdownMenuTrigger asChild>
1738
- <button
1739
- type="button"
1740
- data-agent-page-title-menu=""
1741
- aria-label={t("agentPanel.panelOptions")}
1742
- className="flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[state=open]:bg-accent data-[state=open]:text-foreground"
1743
- >
1744
- <IconDotsVertical size={14} />
1745
- </button>
1746
- </DropdownMenuTrigger>
1747
- <DropdownMenuContent
1748
- align="start"
1749
- sideOffset={5}
1750
- className="w-44"
1751
- onCloseAutoFocus={(event) => {
1752
- consumeAgentPanelOverlayFocusRestore(
1753
- pendingHeaderOverlayRef,
1754
- event,
1755
- );
1756
- }}
1774
+ {pageHeaderActions.showPageTitleMenu ? (
1775
+ <DropdownMenu
1776
+ open={headerMenuOpen}
1777
+ onOpenChange={setHeaderMenuOpen}
1757
1778
  >
1758
- {toggleHistory ? (
1759
- <DropdownMenuItem
1760
- onSelect={(event) =>
1761
- deferAgentPanelOverlayOpen(
1762
- event,
1763
- closeHeaderMenuForOverlay,
1764
- toggleHistory,
1765
- pendingHeaderOverlayRef,
1766
- )
1767
- }
1779
+ <DropdownMenuTrigger asChild>
1780
+ <button
1781
+ type="button"
1782
+ data-agent-page-title-menu=""
1783
+ aria-label={t("agentPanel.panelOptions")}
1784
+ className="flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[state=open]:bg-accent data-[state=open]:text-foreground"
1768
1785
  >
1769
- <IconHistory size={14} className="shrink-0" />
1770
- {showHistory
1771
- ? t("agentPanel.hideChats")
1772
- : t("agentPanel.allChats")}
1773
- </DropdownMenuItem>
1774
- ) : null}
1775
- {activeTabMessageCount > 0 ? (
1776
- <DropdownMenuItem onSelect={clearActiveTab}>
1777
- <IconX size={14} className="shrink-0" />
1778
- {t("agentPanel.clearChat")}
1779
- </DropdownMenuItem>
1780
- ) : null}
1781
- </DropdownMenuContent>
1782
- </DropdownMenu>
1786
+ <IconDotsVertical size={14} />
1787
+ </button>
1788
+ </DropdownMenuTrigger>
1789
+ <DropdownMenuContent
1790
+ align="start"
1791
+ sideOffset={5}
1792
+ className="w-44"
1793
+ onCloseAutoFocus={(event) => {
1794
+ consumeAgentPanelOverlayFocusRestore(
1795
+ pendingHeaderOverlayRef,
1796
+ event,
1797
+ );
1798
+ }}
1799
+ >
1800
+ {toggleHistory ? (
1801
+ <DropdownMenuItem
1802
+ onSelect={(event) =>
1803
+ deferAgentPanelOverlayOpen(
1804
+ event,
1805
+ closeHeaderMenuForOverlay,
1806
+ toggleHistory,
1807
+ pendingHeaderOverlayRef,
1808
+ )
1809
+ }
1810
+ >
1811
+ <IconHistory size={14} className="shrink-0" />
1812
+ {showHistory
1813
+ ? t("agentPanel.hideChats")
1814
+ : t("agentPanel.allChats")}
1815
+ </DropdownMenuItem>
1816
+ ) : null}
1817
+ {activeTabMessageCount > 0 ? (
1818
+ <DropdownMenuItem onSelect={clearActiveTab}>
1819
+ <IconX size={14} className="shrink-0" />
1820
+ {t("agentPanel.clearChat")}
1821
+ </DropdownMenuItem>
1822
+ ) : null}
1823
+ </DropdownMenuContent>
1824
+ </DropdownMenu>
1825
+ ) : null}
1783
1826
  {activeTab?.status === "running" ? (
1784
1827
  <span className="size-1.5 shrink-0 rounded-full bg-muted-foreground/55 animate-pulse motion-reduce:animate-none" />
1785
1828
  ) : null}
1786
1829
  </div>
1787
1830
  <div className="flex shrink-0 items-center gap-1">
1788
- {canShareActiveTab ? (
1831
+ {pageHeaderActions.showShareAction && activeTab ? (
1789
1832
  <ShareButton
1790
1833
  resourceType="chat_thread"
1791
1834
  resourceId={activeTab.id}
@@ -1798,7 +1841,7 @@ function AgentPanelInner({
1798
1841
  triggerClassName="size-8 p-0 border-0 bg-transparent shadow-none hover:bg-accent/60"
1799
1842
  />
1800
1843
  ) : null}
1801
- {pageToolbarSlot}
1844
+ {toolbarSlot}
1802
1845
  {showNewChatAction ? (
1803
1846
  <button
1804
1847
  type="button"
@@ -1829,6 +1872,7 @@ function AgentPanelInner({
1829
1872
  pageToolbarSlot,
1830
1873
  pendingHeaderOverlayRef,
1831
1874
  setHeaderMenuOpen,
1875
+ showPageTitleMenu,
1832
1876
  showPageNewChatButton,
1833
1877
  t,
1834
1878
  ],