@agent-native/toolkit 0.221.0-same-host-mcp-20261027-20261027081850 → 0.221.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 (259) hide show
  1. package/agent-native.eject.json +16 -1
  2. package/dist/app/agentkit/react/components.d.ts +5 -3
  3. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  4. package/dist/app/agentkit/react/components.js +103 -42
  5. package/dist/app/agentkit/react/components.js.map +1 -1
  6. package/dist/app/agentkit/react/styles.css +93 -65
  7. package/dist/app/agentkit/react/voice-transcripts.d.ts +22 -1
  8. package/dist/app/agentkit/react/voice-transcripts.d.ts.map +1 -1
  9. package/dist/app/agentkit/react/voice-transcripts.js +209 -8
  10. package/dist/app/agentkit/react/voice-transcripts.js.map +1 -1
  11. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  12. package/dist/app/chat/AgentKitAssistantChat.js +55 -40
  13. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  14. package/dist/app/chat/agentkit-chat/guided-questions.d.ts +1 -0
  15. package/dist/app/chat/agentkit-chat/guided-questions.d.ts.map +1 -1
  16. package/dist/app/chat/agentkit-chat/guided-questions.js +18 -6
  17. package/dist/app/chat/agentkit-chat/guided-questions.js.map +1 -1
  18. package/dist/app/chat/agentkit-chat/index.d.ts +1 -1
  19. package/dist/app/chat/agentkit-chat/index.d.ts.map +1 -1
  20. package/dist/app/chat/agentkit-chat/index.js +1 -1
  21. package/dist/app/chat/agentkit-chat/index.js.map +1 -1
  22. package/dist/app/chat/chat/run-recovery.d.ts.map +1 -1
  23. package/dist/app/chat/chat/run-recovery.js +5 -1
  24. package/dist/app/chat/chat/run-recovery.js.map +1 -1
  25. package/dist/app/chat/composer/runtime-adapters.d.ts.map +1 -1
  26. package/dist/app/chat/composer/runtime-adapters.js +2 -0
  27. package/dist/app/chat/composer/runtime-adapters.js.map +1 -1
  28. package/dist/app/chat/reply-connection-suggestion.d.ts +29 -0
  29. package/dist/app/chat/reply-connection-suggestion.d.ts.map +1 -0
  30. package/dist/app/chat/reply-connection-suggestion.js +42 -0
  31. package/dist/app/chat/reply-connection-suggestion.js.map +1 -0
  32. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  33. package/dist/app/i18n/catalogs/ar-SA.js +7 -0
  34. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  35. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  36. package/dist/app/i18n/catalogs/de-DE.js +7 -0
  37. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  38. package/dist/app/i18n/catalogs/en-US.d.ts +7 -0
  39. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  40. package/dist/app/i18n/catalogs/en-US.js +7 -0
  41. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  42. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  43. package/dist/app/i18n/catalogs/es-ES.js +7 -0
  44. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  45. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  46. package/dist/app/i18n/catalogs/fr-FR.js +7 -0
  47. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  48. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  49. package/dist/app/i18n/catalogs/hi-IN.js +7 -0
  50. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  51. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  52. package/dist/app/i18n/catalogs/ja-JP.js +7 -0
  53. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  54. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  55. package/dist/app/i18n/catalogs/ko-KR.js +7 -0
  56. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  57. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  58. package/dist/app/i18n/catalogs/pt-BR.js +7 -0
  59. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  60. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  61. package/dist/app/i18n/catalogs/zh-CN.js +7 -0
  62. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  63. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  64. package/dist/app/i18n/catalogs/zh-TW.js +7 -0
  65. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  66. package/dist/app/mcp-approval/McpApprovalReview.d.ts +25 -0
  67. package/dist/app/mcp-approval/McpApprovalReview.d.ts.map +1 -0
  68. package/dist/app/mcp-approval/McpApprovalReview.js +342 -0
  69. package/dist/app/mcp-approval/McpApprovalReview.js.map +1 -0
  70. package/dist/app/onboarding/FirstRunOnboarding.d.ts.map +1 -1
  71. package/dist/app/onboarding/FirstRunOnboarding.js +299 -71
  72. package/dist/app/onboarding/FirstRunOnboarding.js.map +1 -1
  73. package/dist/app/onboarding/GettingStartedCard.js +1 -1
  74. package/dist/app/onboarding/GettingStartedCard.js.map +1 -1
  75. package/dist/app/onboarding/first-run-builder-credits-flag.d.ts +2 -0
  76. package/dist/app/onboarding/first-run-builder-credits-flag.d.ts.map +1 -0
  77. package/dist/app/onboarding/first-run-builder-credits-flag.js +8 -0
  78. package/dist/app/onboarding/first-run-builder-credits-flag.js.map +1 -0
  79. package/dist/app/providers/AppProviders.d.ts.map +1 -1
  80. package/dist/app/providers/AppProviders.js +14 -1
  81. package/dist/app/providers/AppProviders.js.map +1 -1
  82. package/dist/app/resources/McpConnectionSuggestion.d.ts +2 -4
  83. package/dist/app/resources/McpConnectionSuggestion.d.ts.map +1 -1
  84. package/dist/app/resources/McpConnectionSuggestion.js +59 -36
  85. package/dist/app/resources/McpConnectionSuggestion.js.map +1 -1
  86. package/dist/app/settings/account/PreferencesSettings.d.ts.map +1 -1
  87. package/dist/app/settings/account/PreferencesSettings.js +14 -1
  88. package/dist/app/settings/account/PreferencesSettings.js.map +1 -1
  89. package/dist/app/settings/account/ThemeFamilySettings.js +2 -2
  90. package/dist/app/settings/account/ThemeFamilySettings.js.map +1 -1
  91. package/dist/app/settings/account/search-entries.d.ts +1 -0
  92. package/dist/app/settings/account/search-entries.d.ts.map +1 -1
  93. package/dist/app/settings/account/search-entries.js +5 -0
  94. package/dist/app/settings/account/search-entries.js.map +1 -1
  95. package/dist/app/settings/shell/SettingsShell.d.ts.map +1 -1
  96. package/dist/app/settings/shell/SettingsShell.js +26 -2
  97. package/dist/app/settings/shell/SettingsShell.js.map +1 -1
  98. package/dist/app/shared/AppShellSkeleton.d.ts.map +1 -1
  99. package/dist/app/shared/AppShellSkeleton.js +4 -10
  100. package/dist/app/shared/AppShellSkeleton.js.map +1 -1
  101. package/dist/app/shared/EnvironmentBadge.d.ts +2 -1
  102. package/dist/app/shared/EnvironmentBadge.d.ts.map +1 -1
  103. package/dist/app/shared/EnvironmentBadge.js +2 -3
  104. package/dist/app/shared/EnvironmentBadge.js.map +1 -1
  105. package/dist/app/shared/is-builder-io-employee.d.ts +8 -0
  106. package/dist/app/shared/is-builder-io-employee.d.ts.map +1 -0
  107. package/dist/app/shared/is-builder-io-employee.js +10 -0
  108. package/dist/app/shared/is-builder-io-employee.js.map +1 -0
  109. package/dist/app/styles/agent-conversation.css +30 -17
  110. package/dist/app/styles/theme-families.css +22 -5
  111. package/dist/app/styles/tokens/agent-kit.css +3 -3
  112. package/dist/composer/PromptComposer.d.ts +15 -3
  113. package/dist/composer/PromptComposer.d.ts.map +1 -1
  114. package/dist/composer/PromptComposer.js +74 -16
  115. package/dist/composer/PromptComposer.js.map +1 -1
  116. package/dist/composer/RealtimeVoiceMode.d.ts +7 -0
  117. package/dist/composer/RealtimeVoiceMode.d.ts.map +1 -1
  118. package/dist/composer/RealtimeVoiceMode.js +9 -2
  119. package/dist/composer/RealtimeVoiceMode.js.map +1 -1
  120. package/dist/composer/TiptapComposer.d.ts +7 -0
  121. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  122. package/dist/composer/TiptapComposer.js +12 -4
  123. package/dist/composer/TiptapComposer.js.map +1 -1
  124. package/dist/composer/VoiceButton.d.ts +2 -0
  125. package/dist/composer/VoiceButton.d.ts.map +1 -1
  126. package/dist/composer/VoiceButton.js +5 -4
  127. package/dist/composer/VoiceButton.js.map +1 -1
  128. package/dist/composer/index.d.ts +2 -2
  129. package/dist/composer/index.d.ts.map +1 -1
  130. package/dist/composer/index.js +1 -1
  131. package/dist/composer/index.js.map +1 -1
  132. package/dist/composer/prompt-attachments.d.ts +20 -0
  133. package/dist/composer/prompt-attachments.d.ts.map +1 -1
  134. package/dist/composer/prompt-attachments.js +188 -11
  135. package/dist/composer/prompt-attachments.js.map +1 -1
  136. package/dist/composer/realtime-voice-transcript.d.ts +27 -0
  137. package/dist/composer/realtime-voice-transcript.d.ts.map +1 -1
  138. package/dist/composer/realtime-voice-transcript.js +34 -1
  139. package/dist/composer/realtime-voice-transcript.js.map +1 -1
  140. package/dist/composer/realtime-voice-turn-metrics.d.ts +43 -0
  141. package/dist/composer/realtime-voice-turn-metrics.d.ts.map +1 -0
  142. package/dist/composer/realtime-voice-turn-metrics.js +132 -0
  143. package/dist/composer/realtime-voice-turn-metrics.js.map +1 -0
  144. package/dist/composer/runtime-adapters.d.ts +4 -1
  145. package/dist/composer/runtime-adapters.d.ts.map +1 -1
  146. package/dist/composer/runtime-adapters.js +2 -1
  147. package/dist/composer/runtime-adapters.js.map +1 -1
  148. package/dist/composer/useRealtimeVoiceMode.d.ts +61 -9
  149. package/dist/composer/useRealtimeVoiceMode.d.ts.map +1 -1
  150. package/dist/composer/useRealtimeVoiceMode.js +536 -99
  151. package/dist/composer/useRealtimeVoiceMode.js.map +1 -1
  152. package/dist/composer/voice-chat-icon.d.ts +7 -0
  153. package/dist/composer/voice-chat-icon.d.ts.map +1 -0
  154. package/dist/composer/voice-chat-icon.js +14 -0
  155. package/dist/composer/voice-chat-icon.js.map +1 -0
  156. package/dist/editor/DragHandle.d.ts +12 -1
  157. package/dist/editor/DragHandle.d.ts.map +1 -1
  158. package/dist/editor/DragHandle.js +165 -41
  159. package/dist/editor/DragHandle.js.map +1 -1
  160. package/dist/editor/RegistryBlockNode.d.ts +20 -0
  161. package/dist/editor/RegistryBlockNode.d.ts.map +1 -1
  162. package/dist/editor/RegistryBlockNode.js +58 -13
  163. package/dist/editor/RegistryBlockNode.js.map +1 -1
  164. package/dist/editor/index.d.ts +2 -2
  165. package/dist/editor/index.d.ts.map +1 -1
  166. package/dist/editor/index.js +1 -1
  167. package/dist/editor/index.js.map +1 -1
  168. package/dist/styles.css +2 -0
  169. package/dist/ui/dropdown-menu.d.ts.map +1 -1
  170. package/dist/ui/dropdown-menu.js +5 -1
  171. package/dist/ui/dropdown-menu.js.map +1 -1
  172. package/package.json +14 -4
  173. package/src/app/agentkit/react/components.regenerate-readiness.spec.tsx +3 -1
  174. package/src/app/agentkit/react/components.spec.tsx +24 -4
  175. package/src/app/agentkit/react/components.tsx +174 -44
  176. package/src/app/agentkit/react/composer-context.spec.tsx +3 -0
  177. package/src/app/agentkit/react/disclosures.spec.tsx +124 -0
  178. package/src/app/agentkit/react/lifecycle.spec.tsx +8 -2
  179. package/src/app/agentkit/react/message-actions.spec.tsx +161 -5
  180. package/src/app/agentkit/react/saved-reply-reload.spec.tsx +206 -0
  181. package/src/app/agentkit/react/styles.css +93 -65
  182. package/src/app/agentkit/react/voice-transcripts.spec.tsx +239 -4
  183. package/src/app/agentkit/react/voice-transcripts.ts +245 -8
  184. package/src/app/chat/AgentKitAssistantChat.spec.tsx +59 -11
  185. package/src/app/chat/AgentKitAssistantChat.tsx +74 -62
  186. package/src/app/chat/agentkit-chat/guided-answers-context.spec.ts +9 -0
  187. package/src/app/chat/agentkit-chat/guided-questions.tsx +17 -7
  188. package/src/app/chat/agentkit-chat/index.ts +1 -0
  189. package/src/app/chat/chat/run-recovery.spec.tsx +45 -0
  190. package/src/app/chat/chat/run-recovery.tsx +6 -1
  191. package/src/app/chat/composer/runtime-adapters.tsx +2 -0
  192. package/src/app/chat/reply-connection-suggestion.spec.ts +121 -0
  193. package/src/app/chat/reply-connection-suggestion.ts +76 -0
  194. package/src/app/i18n/catalogs/ar-SA.ts +10 -0
  195. package/src/app/i18n/catalogs/de-DE.ts +10 -0
  196. package/src/app/i18n/catalogs/en-US.ts +10 -0
  197. package/src/app/i18n/catalogs/es-ES.ts +10 -0
  198. package/src/app/i18n/catalogs/fr-FR.ts +10 -0
  199. package/src/app/i18n/catalogs/hi-IN.ts +10 -0
  200. package/src/app/i18n/catalogs/ja-JP.ts +10 -0
  201. package/src/app/i18n/catalogs/ko-KR.ts +10 -0
  202. package/src/app/i18n/catalogs/pt-BR.ts +10 -0
  203. package/src/app/i18n/catalogs/zh-CN.ts +10 -0
  204. package/src/app/i18n/catalogs/zh-TW.ts +10 -0
  205. package/src/app/mcp-approval/McpApprovalReview.spec.tsx +687 -0
  206. package/src/app/mcp-approval/McpApprovalReview.tsx +913 -0
  207. package/src/app/onboarding/FirstRunOnboarding.spec.tsx +736 -26
  208. package/src/app/onboarding/FirstRunOnboarding.tsx +473 -145
  209. package/src/app/onboarding/GettingStartedCard.tsx +1 -1
  210. package/src/app/onboarding/first-run-builder-credits-flag.spec.ts +13 -0
  211. package/src/app/onboarding/first-run-builder-credits-flag.ts +9 -0
  212. package/src/app/providers/AppProviders.spec.tsx +109 -0
  213. package/src/app/providers/AppProviders.tsx +53 -1
  214. package/src/app/resources/McpConnectionSuggestion.spec.ts +77 -12
  215. package/src/app/resources/McpConnectionSuggestion.tsx +69 -51
  216. package/src/app/settings/VoiceChatSettings.spec.tsx +6 -1
  217. package/src/app/settings/account/PreferencesSettings.spec.tsx +73 -0
  218. package/src/app/settings/account/PreferencesSettings.tsx +24 -7
  219. package/src/app/settings/account/ThemeFamilySettings.tsx +2 -2
  220. package/src/app/settings/account/search-entries.spec.ts +14 -0
  221. package/src/app/settings/account/search-entries.ts +9 -0
  222. package/src/app/settings/shell/SettingsShell.tsx +29 -2
  223. package/src/app/shared/AppShellSkeleton.spec.tsx +2 -2
  224. package/src/app/shared/AppShellSkeleton.tsx +3 -16
  225. package/src/app/shared/EnvironmentBadge.tsx +3 -4
  226. package/src/app/shared/is-builder-io-employee.ts +17 -0
  227. package/src/app/styles/agent-conversation.css +30 -17
  228. package/src/app/styles/theme-families.css +22 -5
  229. package/src/app/styles/theme-families.spec.ts +73 -0
  230. package/src/app/styles/tokens/agent-kit.css +3 -3
  231. package/src/composer/AgentSuggestionBar.overflow.spec.tsx +1 -1
  232. package/src/composer/AttachmentChip.spec.tsx +87 -0
  233. package/src/composer/PromptComposer.context.spec.tsx +8 -4
  234. package/src/composer/PromptComposer.setup-card.spec.tsx +170 -0
  235. package/src/composer/PromptComposer.tsx +125 -14
  236. package/src/composer/RealtimeVoiceMode.spec.tsx +48 -0
  237. package/src/composer/RealtimeVoiceMode.tsx +45 -2
  238. package/src/composer/TiptapComposer.tsx +25 -0
  239. package/src/composer/TiptapComposer.voice-toolbar.spec.tsx +4 -1
  240. package/src/composer/VoiceButton.tsx +8 -3
  241. package/src/composer/index.ts +4 -0
  242. package/src/composer/prompt-attachments.spec.ts +160 -8
  243. package/src/composer/prompt-attachments.ts +221 -12
  244. package/src/composer/realtime-voice-transcript.spec.ts +36 -0
  245. package/src/composer/realtime-voice-transcript.ts +56 -1
  246. package/src/composer/realtime-voice-turn-metrics.spec.ts +175 -0
  247. package/src/composer/realtime-voice-turn-metrics.ts +192 -0
  248. package/src/composer/runtime-adapters.tsx +5 -1
  249. package/src/composer/useRealtimeVoiceMode.session.spec.tsx +487 -2
  250. package/src/composer/useRealtimeVoiceMode.spec.ts +267 -65
  251. package/src/composer/useRealtimeVoiceMode.tsx +716 -139
  252. package/src/composer/voice-chat-icon.tsx +19 -0
  253. package/src/editor/DragHandle.spec.ts +217 -0
  254. package/src/editor/DragHandle.ts +214 -55
  255. package/src/editor/RegistryBlockNode.spec.ts +172 -2
  256. package/src/editor/RegistryBlockNode.tsx +91 -13
  257. package/src/editor/index.ts +4 -0
  258. package/src/styles.css +2 -0
  259. package/src/ui/dropdown-menu.tsx +4 -0
@@ -88,14 +88,24 @@ describe("Realtime voice client transport", () => {
88
88
  intelligence: "deep",
89
89
  voice: "cedar",
90
90
  }),
91
- ).toEqual({ language: "en", intelligence: "deep", voice: "cedar" });
91
+ ).toEqual({
92
+ language: "en",
93
+ intelligence: "deep",
94
+ voice: "cedar",
95
+ intelligenceChosen: true,
96
+ });
92
97
  expect(
93
98
  normalizeRealtimeVoicePreferences({
94
99
  language: "xx",
95
100
  intelligence: "maximum",
96
101
  voice: "unknown",
97
102
  }),
98
- ).toEqual({ language: "auto", intelligence: "instant", voice: "marin" });
103
+ ).toEqual({
104
+ language: "auto",
105
+ intelligence: "instant",
106
+ voice: "marin",
107
+ intelligenceChosen: false,
108
+ });
99
109
  expect(resolveRealtimeVoiceLanguage("auto", ["en-US", "fr"])).toBe("en");
100
110
  expect(resolveRealtimeVoiceLanguage("auto", ["nl-NL"])).toBe("en");
101
111
  });
@@ -137,12 +147,44 @@ describe("Realtime voice client transport", () => {
137
147
  type: "session.update",
138
148
  session: {
139
149
  delegation: {
140
- responses: { reasoning: { effort: "medium" } },
150
+ responses: { reasoning: { effort: "high" } },
141
151
  },
142
152
  },
143
153
  });
144
154
  });
145
155
 
156
+ it("gives each intelligence level its own GPT-Live reasoning effort", () => {
157
+ expect(realtimeVoiceReasoningEffort("instant", "live")).toBe("low");
158
+ expect(realtimeVoiceReasoningEffort("balanced", "live")).toBe("medium");
159
+ expect(realtimeVoiceReasoningEffort("deep", "live")).toBe("high");
160
+ });
161
+
162
+ it("treats an old save's default level as not picked, and keeps a new pick and the app's default", () => {
163
+ expect(
164
+ normalizeRealtimeVoicePreferences({
165
+ language: "en",
166
+ intelligence: "instant",
167
+ voice: "cedar",
168
+ }).intelligenceChosen,
169
+ ).toBe(false);
170
+ expect(
171
+ normalizeRealtimeVoicePreferences({
172
+ language: "en",
173
+ intelligence: "instant",
174
+ voice: "cedar",
175
+ intelligenceChosen: true,
176
+ }),
177
+ ).toMatchObject({ intelligence: "instant", intelligenceChosen: true });
178
+ expect(
179
+ normalizeRealtimeVoicePreferences({
180
+ language: "en",
181
+ intelligence: "balanced",
182
+ voice: "cedar",
183
+ intelligenceChosen: false,
184
+ }),
185
+ ).toMatchObject({ intelligence: "balanced", intelligenceChosen: false });
186
+ });
187
+
146
188
  it("maps instant intelligence to a supported GPT-Live reasoning effort", () => {
147
189
  expect(
148
190
  createRealtimeVoicePreferenceUpdate(
@@ -341,6 +383,7 @@ describe("Realtime voice client transport", () => {
341
383
  language: "auto",
342
384
  intelligence: "instant",
343
385
  voice: "marin",
386
+ intelligenceChosen: true,
344
387
  },
345
388
  browserLanguages: ["en-US"],
346
389
  }),
@@ -373,6 +416,61 @@ describe("Realtime voice client transport", () => {
373
416
  );
374
417
  });
375
418
 
419
+ it("lets the app default apply until the user picks a level", async () => {
420
+ const fetchMock = vi.fn(
421
+ async () =>
422
+ new Response("answer-sdp", {
423
+ status: 200,
424
+ headers: {
425
+ "X-Agent-Native-Realtime-Protocol": "live",
426
+ "X-Agent-Native-Realtime-Intelligence": "balanced",
427
+ "X-Agent-Native-Realtime-Priority-Tools":
428
+ "edit-design, bad name,update-slide",
429
+ },
430
+ }),
431
+ );
432
+ vi.stubGlobal("fetch", fetchMock);
433
+
434
+ await expect(
435
+ createRealtimeVoiceSessionWithCapability("offer-sdp", {
436
+ preferences: {
437
+ language: "en",
438
+ intelligence: "instant",
439
+ voice: "marin",
440
+ intelligenceChosen: false,
441
+ },
442
+ }),
443
+ ).resolves.toEqual({
444
+ sdp: "answer-sdp",
445
+ protocol: "live",
446
+ intelligence: "balanced",
447
+ priorityToolNames: ["edit-design", "update-slide"],
448
+ });
449
+ const [, init] = fetchMock.mock.calls[0] as unknown as [
450
+ string,
451
+ RequestInit,
452
+ ];
453
+ expect(init.headers).not.toHaveProperty(
454
+ "X-Agent-Native-Realtime-Intelligence",
455
+ );
456
+ });
457
+
458
+ it("sends the level of preferences that do not say whether it was chosen", async () => {
459
+ const fetchMock = vi.fn(async () => new Response("answer-sdp"));
460
+ vi.stubGlobal("fetch", fetchMock);
461
+
462
+ await createRealtimeVoiceSessionWithCapability("offer-sdp", {
463
+ preferences: { language: "en", intelligence: "deep", voice: "marin" },
464
+ });
465
+ const [, init] = fetchMock.mock.calls[0] as unknown as [
466
+ string,
467
+ RequestInit,
468
+ ];
469
+ expect(init.headers).toMatchObject({
470
+ "X-Agent-Native-Realtime-Intelligence": "deep",
471
+ });
472
+ });
473
+
376
474
  it("sends function calls to the authenticated Agent-Native tool bridge", async () => {
377
475
  const signal = new AbortController().signal;
378
476
  const fetchMock = vi.fn(async () =>
@@ -475,6 +573,66 @@ describe("Realtime voice dynamic tool manifests", () => {
475
573
  expect(names).not.toContain("old-26");
476
574
  });
477
575
 
576
+ it("keeps the app's edit tools when a discovery fills the manifest", () => {
577
+ const current = [
578
+ ...Array.from({ length: 31 }, (_, index) => realtimeTool(`old-${index}`)),
579
+ realtimeTool("edit-design"),
580
+ ];
581
+ const discoveries = Array.from({ length: 8 }, (_, index) =>
582
+ realtimeTool(`found-${index}`),
583
+ );
584
+
585
+ expect(
586
+ mergeRealtimeVoiceToolManifest(current, discoveries).map(
587
+ (tool) => tool.name,
588
+ ),
589
+ ).not.toContain("edit-design");
590
+ const merged = mergeRealtimeVoiceToolManifest(
591
+ current,
592
+ discoveries,
593
+ "live",
594
+ ["edit-design"],
595
+ ).map((tool) => tool.name);
596
+ expect(merged[0]).toBe("edit-design");
597
+ expect(merged).toContain("found-7");
598
+ });
599
+
600
+ it("keeps the session's app edit tools through a coordinator expansion", () => {
601
+ const sent: Record<string, unknown>[] = [];
602
+ const coordinator = createRealtimeVoiceToolManifestCoordinator((event) =>
603
+ sent.push(event),
604
+ );
605
+ coordinator.setProtocol("live");
606
+ coordinator.setPriorityToolNames(["update-slide"]);
607
+ coordinator.setSessionTools([
608
+ ...Array.from({ length: 31 }, (_, index) => realtimeTool(`old-${index}`)),
609
+ realtimeTool("update-slide"),
610
+ ]);
611
+ coordinator.enqueue({
612
+ callId: "call-search",
613
+ status: "completed",
614
+ output: "{}",
615
+ expandedTools: [realtimeTool("web-search")],
616
+ });
617
+
618
+ const update = sent[0] as {
619
+ session: { delegation: { responses: { tools: { name: string }[] } } };
620
+ };
621
+ const names = update.session.delegation.responses.tools.map(
622
+ (tool) => tool.name,
623
+ );
624
+ expect(names.slice(0, 2)).toEqual(["web-search", "update-slide"]);
625
+ expect(names).toHaveLength(32);
626
+ // A strict schema would make the delegated model fill every optional field.
627
+ expect(
628
+ new Set(
629
+ update.session.delegation.responses.tools.map(
630
+ (tool) => (tool as { strict?: unknown }).strict,
631
+ ),
632
+ ),
633
+ ).toEqual(new Set([false]));
634
+ });
635
+
478
636
  it("packs dynamic schemas within the realtime session byte budget", () => {
479
637
  const largeTool = (name: string): RealtimeVoiceFunctionTool => ({
480
638
  ...realtimeTool(name),
@@ -1153,79 +1311,120 @@ describe("Realtime voice startup and transcript ordering", () => {
1153
1311
  ]);
1154
1312
  });
1155
1313
 
1156
- it("keeps overlapping GPT-Live transcript roles independent", () => {
1314
+ // GPT-Live transcript deltas carry an id and their place on the session
1315
+ // timeline; the SDK's grouper turns them into speaker turns.
1316
+ let liveEventId = 0;
1317
+ const liveDelta = (
1318
+ role: "user" | "assistant",
1319
+ delta: string,
1320
+ start_ms: number,
1321
+ end_ms: number,
1322
+ ) => ({
1323
+ type:
1324
+ role === "user"
1325
+ ? "session.input_transcript.delta"
1326
+ : "session.output_transcript.delta",
1327
+ event_id: `live-event-${++liveEventId}`,
1328
+ delta,
1329
+ start_ms,
1330
+ end_ms,
1331
+ });
1332
+
1333
+ it("orders GPT-Live turns by speaker", () => {
1157
1334
  const published: Array<{ role: string; text: string }> = [];
1158
1335
  const sequencer = createRealtimeVoiceTranscriptSequencer((transcript) => {
1159
1336
  published.push(transcript);
1160
1337
  });
1161
1338
 
1162
- sequencer.handle({
1163
- type: "session.input_transcript.delta",
1164
- delta: "Can you help ",
1165
- start_ms: 0,
1166
- end_ms: 400,
1167
- });
1168
- sequencer.handle({
1169
- type: "session.input_transcript.delta",
1170
- delta: "me?",
1171
- start_ms: 400,
1172
- end_ms: 500,
1173
- });
1174
- sequencer.handle({
1175
- type: "session.output_transcript.delta",
1176
- delta: "Absolutely.",
1177
- start_ms: 300,
1178
- end_ms: 900,
1179
- });
1180
- expect(published).toEqual([]);
1181
-
1182
- sequencer.handle({
1183
- type: "response.event",
1184
- event: { type: "response.completed" },
1185
- });
1186
- expect(published).toEqual([]);
1339
+ sequencer.handle(liveDelta("user", "Can you help ", 0, 400));
1340
+ sequencer.handle(liveDelta("user", "me?", 400, 500));
1341
+ sequencer.handle(liveDelta("assistant", "Absolutely.", 1_100, 1_900));
1342
+ sequencer.handle(liveDelta("user", "Next question.", 3_000, 3_400));
1343
+ sequencer.handle(liveDelta("assistant", "Next answer.", 4_000, 4_500));
1344
+ sequencer.handle({ type: "session.closed" });
1187
1345
 
1188
- sequencer.handle({
1189
- type: "session.input_transcript.delta",
1190
- delta: "Next question.",
1191
- start_ms: 2_000,
1192
- end_ms: 2_200,
1193
- });
1194
- sequencer.handle({
1195
- type: "session.output_transcript.delta",
1196
- delta: "Next answer.",
1197
- start_ms: 2_300,
1198
- end_ms: 2_500,
1199
- });
1200
1346
  expect(published).toEqual([
1201
1347
  { role: "user", text: "Can you help me?" },
1202
1348
  { role: "assistant", text: "Absolutely." },
1349
+ { role: "user", text: "Next question." },
1350
+ { role: "assistant", text: "Next answer." },
1203
1351
  ]);
1352
+ });
1353
+
1354
+ it("keeps a GPT-Live reply spoken with pauses as one message", () => {
1355
+ const published: Array<{ role: string; text: string }> = [];
1356
+ const sequencer = createRealtimeVoiceTranscriptSequencer((transcript) => {
1357
+ published.push(transcript);
1358
+ });
1204
1359
 
1360
+ sequencer.handle(
1361
+ liveDelta("assistant", "I found a solid overview. ", 0, 1_000),
1362
+ );
1363
+ sequencer.handle(
1364
+ liveDelta(
1365
+ "assistant",
1366
+ "They explain the meaning of life, ",
1367
+ 2_000,
1368
+ 3_000,
1369
+ ),
1370
+ );
1371
+ sequencer.handle(
1372
+ liveDelta("assistant", "and meaning in life.", 4_500, 5_500),
1373
+ );
1205
1374
  sequencer.handle({ type: "session.closed" });
1375
+
1206
1376
  expect(published).toEqual([
1207
- { role: "user", text: "Can you help me?" },
1208
- { role: "assistant", text: "Absolutely." },
1209
- { role: "user", text: "Next question." },
1210
- { role: "assistant", text: "Next answer." },
1377
+ {
1378
+ role: "assistant",
1379
+ text: "I found a solid overview. They explain the meaning of life, and meaning in life.",
1380
+ },
1211
1381
  ]);
1212
1382
  });
1213
1383
 
1214
- it("discards unconfirmed GPT-Live transcript buffers on reset", () => {
1384
+ it("starts a new GPT-Live message after the assistant goes quiet", () => {
1215
1385
  const published: Array<{ role: string; text: string }> = [];
1216
1386
  const sequencer = createRealtimeVoiceTranscriptSequencer((transcript) => {
1217
1387
  published.push(transcript);
1218
1388
  });
1219
1389
 
1220
- sequencer.handle({
1221
- type: "session.output_transcript.delta",
1222
- delta: "Possibly incomplete.",
1390
+ sequencer.handle(liveDelta("assistant", "On it, looking it ", 0, 400));
1391
+ sequencer.handle(liveDelta("assistant", "up.", 400, 600));
1392
+ // A tool runs in the silence before the answer.
1393
+ sequencer.handle(
1394
+ liveDelta("assistant", "Here is what I found.", 6_000, 7_000),
1395
+ );
1396
+ sequencer.handle({ type: "session.closed" });
1397
+
1398
+ expect(published).toEqual([
1399
+ { role: "assistant", text: "On it, looking it up." },
1400
+ { role: "assistant", text: "Here is what I found." },
1401
+ ]);
1402
+ });
1403
+
1404
+ it("discards unconfirmed GPT-Live transcripts on reset", () => {
1405
+ const published: Array<{ role: string; text: string }> = [];
1406
+ const sequencer = createRealtimeVoiceTranscriptSequencer((transcript) => {
1407
+ published.push(transcript);
1223
1408
  });
1409
+
1410
+ sequencer.handle(liveDelta("assistant", "Possibly incomplete.", 0, 500));
1224
1411
  sequencer.reset();
1225
1412
  sequencer.flush();
1226
1413
 
1227
1414
  expect(published).toEqual([]);
1228
1415
  });
1416
+
1417
+ it("rejects a GPT-Live transcript delta without its timing", () => {
1418
+ const sequencer = createRealtimeVoiceTranscriptSequencer(() => undefined);
1419
+
1420
+ expect(() =>
1421
+ sequencer.handle({
1422
+ type: "session.output_transcript.delta",
1423
+ event_id: "untimed",
1424
+ delta: "Hello",
1425
+ }),
1426
+ ).toThrow(/timing/);
1427
+ });
1229
1428
  });
1230
1429
 
1231
1430
  describe("Realtime voice transcript streaming", () => {
@@ -1310,28 +1509,31 @@ describe("Realtime voice transcript streaming", () => {
1310
1509
  ]);
1311
1510
  });
1312
1511
 
1313
- it("streams GPT-Live turns under a stable live id", () => {
1512
+ it("streams GPT-Live turns under the id their final transcript keeps", () => {
1314
1513
  const { sequencer, streamed, published } = streamingSequencer();
1315
- sequencer.handle({
1316
- type: "session.input_transcript.delta",
1317
- delta: "Hello ",
1318
- start_ms: 0,
1319
- end_ms: 200,
1320
- });
1321
- sequencer.handle({
1322
- type: "session.input_transcript.delta",
1323
- delta: "there",
1324
- start_ms: 200,
1325
- end_ms: 400,
1326
- });
1514
+ for (const [index, [delta, start_ms, end_ms]] of (
1515
+ [
1516
+ ["Hello ", 0, 200],
1517
+ ["there", 200, 400],
1518
+ ] as const
1519
+ ).entries()) {
1520
+ sequencer.handle({
1521
+ type: "session.input_transcript.delta",
1522
+ event_id: `stream-${index}`,
1523
+ delta,
1524
+ start_ms,
1525
+ end_ms,
1526
+ });
1527
+ }
1327
1528
  sequencer.handle({ type: "session.closed" });
1328
1529
 
1530
+ const [id] = published.map(({ providerId }) => providerId);
1329
1531
  expect(streamed).toEqual([
1330
- { role: "user", text: "Hello", providerId: "live-1" },
1331
- { role: "user", text: "Hello there", providerId: "live-1" },
1532
+ { role: "user", text: "Hello", providerId: id },
1533
+ { role: "user", text: "Hello there", providerId: id },
1332
1534
  ]);
1333
1535
  expect(published).toEqual([
1334
- { role: "user", text: "Hello there", providerId: "live-1" },
1536
+ { role: "user", text: "Hello there", providerId: id },
1335
1537
  ]);
1336
1538
  });
1337
1539
  });