@agent-native/toolkit 0.201.1 → 0.202.0-nightly-20261006033736

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 (229) hide show
  1. package/agent-native.eject.json +39 -12
  2. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  3. package/dist/app/agentkit/react/components.js +292 -23
  4. package/dist/app/agentkit/react/components.js.map +1 -1
  5. package/dist/app/agentkit/react/context.d.ts +5 -0
  6. package/dist/app/agentkit/react/context.d.ts.map +1 -1
  7. package/dist/app/agentkit/react/context.js +5 -0
  8. package/dist/app/agentkit/react/context.js.map +1 -1
  9. package/dist/app/agentkit/react/styles.css +78 -3
  10. package/dist/app/auth/AuthPage.d.ts.map +1 -1
  11. package/dist/app/auth/AuthPage.js +7 -14
  12. package/dist/app/auth/AuthPage.js.map +1 -1
  13. package/dist/app/auth/server.d.ts +6 -0
  14. package/dist/app/auth/server.d.ts.map +1 -0
  15. package/dist/app/auth/server.js +19 -0
  16. package/dist/app/auth/server.js.map +1 -0
  17. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  18. package/dist/app/chat/AgentKitAssistantChat.js +164 -83
  19. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  20. package/dist/app/chat/AgentSidebar.d.ts +2 -1
  21. package/dist/app/chat/AgentSidebar.d.ts.map +1 -1
  22. package/dist/app/chat/AgentSidebar.js +2 -2
  23. package/dist/app/chat/AgentSidebar.js.map +1 -1
  24. package/dist/app/chat/agentkit-chat/guided-questions.d.ts.map +1 -1
  25. package/dist/app/chat/agentkit-chat/guided-questions.js +13 -3
  26. package/dist/app/chat/agentkit-chat/guided-questions.js.map +1 -1
  27. package/dist/app/chat/chat/surface-types.d.ts +2 -0
  28. package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
  29. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  30. package/dist/app/i18n/catalogs/ar-SA.js +41 -0
  31. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  32. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  33. package/dist/app/i18n/catalogs/de-DE.js +41 -0
  34. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  35. package/dist/app/i18n/catalogs/en-US.d.ts +41 -0
  36. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  37. package/dist/app/i18n/catalogs/en-US.js +41 -0
  38. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  39. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  40. package/dist/app/i18n/catalogs/es-ES.js +41 -0
  41. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  42. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  43. package/dist/app/i18n/catalogs/fr-FR.js +41 -0
  44. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  45. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  46. package/dist/app/i18n/catalogs/hi-IN.js +41 -0
  47. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  48. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  49. package/dist/app/i18n/catalogs/ja-JP.js +41 -0
  50. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  51. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  52. package/dist/app/i18n/catalogs/ko-KR.js +41 -0
  53. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  54. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  55. package/dist/app/i18n/catalogs/pt-BR.js +41 -0
  56. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  57. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  58. package/dist/app/i18n/catalogs/zh-CN.js +41 -0
  59. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  60. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  61. package/dist/app/i18n/catalogs/zh-TW.js +41 -0
  62. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  63. package/dist/app/shared/StarfieldBackground.d.ts +1 -6
  64. package/dist/app/shared/StarfieldBackground.d.ts.map +1 -1
  65. package/dist/app/shared/StarfieldBackground.js +1 -368
  66. package/dist/app/shared/StarfieldBackground.js.map +1 -1
  67. package/dist/app/shared/WaveBackground.d.ts +5 -0
  68. package/dist/app/shared/WaveBackground.d.ts.map +1 -0
  69. package/dist/app/shared/WaveBackground.js +34 -0
  70. package/dist/app/shared/WaveBackground.js.map +1 -0
  71. package/dist/app/shared/WebGlWaveBackground.d.ts +9 -0
  72. package/dist/app/shared/WebGlWaveBackground.d.ts.map +1 -0
  73. package/dist/app/shared/WebGlWaveBackground.js +433 -0
  74. package/dist/app/shared/WebGlWaveBackground.js.map +1 -0
  75. package/dist/app/shared/index.d.ts +1 -0
  76. package/dist/app/shared/index.d.ts.map +1 -1
  77. package/dist/app/shared/index.js +1 -0
  78. package/dist/app/shared/index.js.map +1 -1
  79. package/dist/app/shared/ocean/bloom-blur.wgsl +43 -0
  80. package/dist/app/shared/ocean/bloom-blur.wgsl.js +1 -0
  81. package/dist/app/shared/ocean/bloom-bright.wgsl +21 -0
  82. package/dist/app/shared/ocean/bloom-bright.wgsl.js +1 -0
  83. package/dist/app/shared/ocean/bloom-composite.wgsl +40 -0
  84. package/dist/app/shared/ocean/bloom-composite.wgsl.js +1 -0
  85. package/dist/app/shared/ocean/brand-colors.d.ts +9 -0
  86. package/dist/app/shared/ocean/brand-colors.d.ts.map +1 -0
  87. package/dist/app/shared/ocean/brand-colors.js +45 -0
  88. package/dist/app/shared/ocean/brand-colors.js.map +1 -0
  89. package/dist/app/shared/ocean/camera.d.ts +5 -0
  90. package/dist/app/shared/ocean/camera.d.ts.map +1 -0
  91. package/dist/app/shared/ocean/camera.js +24 -0
  92. package/dist/app/shared/ocean/camera.js.map +1 -0
  93. package/dist/app/shared/ocean/hero-ocean-background.d.ts +8 -0
  94. package/dist/app/shared/ocean/hero-ocean-background.d.ts.map +1 -0
  95. package/dist/app/shared/ocean/hero-ocean-background.js +186 -0
  96. package/dist/app/shared/ocean/hero-ocean-background.js.map +1 -0
  97. package/dist/app/shared/ocean/ifft-stage.wgsl +36 -0
  98. package/dist/app/shared/ocean/ifft-stage.wgsl.js +1 -0
  99. package/dist/app/shared/ocean/initial-spectrum.wgsl +36 -0
  100. package/dist/app/shared/ocean/initial-spectrum.wgsl.js +1 -0
  101. package/dist/app/shared/ocean/noise.wgsl +24 -0
  102. package/dist/app/shared/ocean/noise.wgsl.js +1 -0
  103. package/dist/app/shared/ocean/normal-foam.wgsl +32 -0
  104. package/dist/app/shared/ocean/normal-foam.wgsl.js +1 -0
  105. package/dist/app/shared/ocean/ocean-colors.d.ts +6 -0
  106. package/dist/app/shared/ocean/ocean-colors.d.ts.map +1 -0
  107. package/dist/app/shared/ocean/ocean-colors.js +6 -0
  108. package/dist/app/shared/ocean/ocean-colors.js.map +1 -0
  109. package/dist/app/shared/ocean/ocean-common.wgsl +15 -0
  110. package/dist/app/shared/ocean/ocean-common.wgsl.js +1 -0
  111. package/dist/app/shared/ocean/ocean-graph.d.ts +12 -0
  112. package/dist/app/shared/ocean/ocean-graph.d.ts.map +1 -0
  113. package/dist/app/shared/ocean/ocean-graph.js +16 -0
  114. package/dist/app/shared/ocean/ocean-graph.js.map +1 -0
  115. package/dist/app/shared/ocean/particles.wgsl +139 -0
  116. package/dist/app/shared/ocean/particles.wgsl.js +1 -0
  117. package/dist/app/shared/ocean/present.wgsl +40 -0
  118. package/dist/app/shared/ocean/present.wgsl.js +1 -0
  119. package/dist/app/shared/ocean/renderer.d.ts +66 -0
  120. package/dist/app/shared/ocean/renderer.d.ts.map +1 -0
  121. package/dist/app/shared/ocean/renderer.js +535 -0
  122. package/dist/app/shared/ocean/renderer.js.map +1 -0
  123. package/dist/app/shared/ocean/spectrum.wgsl +35 -0
  124. package/dist/app/shared/ocean/spectrum.wgsl.js +1 -0
  125. package/dist/app/shared/ocean/tuning.d.ts +48 -0
  126. package/dist/app/shared/ocean/tuning.d.ts.map +1 -0
  127. package/dist/app/shared/ocean/tuning.js +74 -0
  128. package/dist/app/shared/ocean/tuning.js.map +1 -0
  129. package/dist/app/shared/ocean/webgpu-support.d.ts +3 -0
  130. package/dist/app/shared/ocean/webgpu-support.d.ts.map +1 -0
  131. package/dist/app/shared/ocean/webgpu-support.js +13 -0
  132. package/dist/app/shared/ocean/webgpu-support.js.map +1 -0
  133. package/dist/app/sharing/AccessRequestApprovalPage.d.ts +23 -0
  134. package/dist/app/sharing/AccessRequestApprovalPage.d.ts.map +1 -0
  135. package/dist/app/sharing/AccessRequestApprovalPage.js +95 -0
  136. package/dist/app/sharing/AccessRequestApprovalPage.js.map +1 -0
  137. package/dist/app/sharing/AccessRequestsSection.d.ts +5 -0
  138. package/dist/app/sharing/AccessRequestsSection.d.ts.map +1 -0
  139. package/dist/app/sharing/AccessRequestsSection.js +47 -0
  140. package/dist/app/sharing/AccessRequestsSection.js.map +1 -0
  141. package/dist/app/sharing/ResourceAccessScreen.d.ts +53 -0
  142. package/dist/app/sharing/ResourceAccessScreen.d.ts.map +1 -0
  143. package/dist/app/sharing/ResourceAccessScreen.js +132 -0
  144. package/dist/app/sharing/ResourceAccessScreen.js.map +1 -0
  145. package/dist/app/sharing/ShareButton.d.ts.map +1 -1
  146. package/dist/app/sharing/ShareButton.js +4 -2
  147. package/dist/app/sharing/ShareButton.js.map +1 -1
  148. package/dist/app/sharing/ShareDialog.d.ts.map +1 -1
  149. package/dist/app/sharing/ShareDialog.js +7 -5
  150. package/dist/app/sharing/ShareDialog.js.map +1 -1
  151. package/dist/app/sharing/index.d.ts +2 -0
  152. package/dist/app/sharing/index.d.ts.map +1 -1
  153. package/dist/app/sharing/index.js +2 -0
  154. package/dist/app/sharing/index.js.map +1 -1
  155. package/dist/app/styles/agent-native.css +21 -1
  156. package/dist/app/styles/tokens/agent-kit.css +3 -1
  157. package/dist/composer/AgentSuggestionBar.d.ts +1 -1
  158. package/dist/composer/AgentSuggestionBar.d.ts.map +1 -1
  159. package/dist/composer/AgentSuggestionBar.js +75 -6
  160. package/dist/composer/AgentSuggestionBar.js.map +1 -1
  161. package/package.json +14 -4
  162. package/src/app/agentkit/react/components.tsx +492 -60
  163. package/src/app/agentkit/react/context.tsx +10 -0
  164. package/src/app/agentkit/react/lifecycle.spec.tsx +487 -1
  165. package/src/app/agentkit/react/styles.css +78 -3
  166. package/src/app/auth/AuthPage.spec.tsx +11 -7
  167. package/src/app/auth/AuthPage.tsx +16 -35
  168. package/src/app/auth/server.spec.ts +68 -0
  169. package/src/app/auth/server.ts +29 -0
  170. package/src/app/chat/AgentKitAssistantChat.spec.tsx +287 -290
  171. package/src/app/chat/AgentKitAssistantChat.tsx +219 -116
  172. package/src/app/chat/AgentSidebar.tsx +3 -0
  173. package/src/app/chat/agentkit-chat/guided-questions.flow.spec.tsx +85 -0
  174. package/src/app/chat/agentkit-chat/guided-questions.tsx +12 -5
  175. package/src/app/chat/chat/surface-types.ts +2 -0
  176. package/src/app/i18n/catalogs/ar-SA.ts +44 -0
  177. package/src/app/i18n/catalogs/de-DE.ts +55 -0
  178. package/src/app/i18n/catalogs/en-US.ts +46 -0
  179. package/src/app/i18n/catalogs/es-ES.ts +53 -0
  180. package/src/app/i18n/catalogs/fr-FR.ts +53 -0
  181. package/src/app/i18n/catalogs/hi-IN.ts +47 -0
  182. package/src/app/i18n/catalogs/ja-JP.ts +54 -0
  183. package/src/app/i18n/catalogs/ko-KR.ts +50 -0
  184. package/src/app/i18n/catalogs/pt-BR.ts +52 -0
  185. package/src/app/i18n/catalogs/zh-CN.ts +42 -0
  186. package/src/app/i18n/catalogs/zh-TW.ts +43 -0
  187. package/src/app/shared/StarfieldBackground.tsx +4 -423
  188. package/src/app/shared/WaveBackground.spec.tsx +104 -0
  189. package/src/app/shared/WaveBackground.tsx +60 -0
  190. package/src/app/shared/WebGlWaveBackground.spec.tsx +106 -0
  191. package/src/app/shared/WebGlWaveBackground.tsx +499 -0
  192. package/src/app/shared/index.ts +1 -0
  193. package/src/app/shared/ocean/bloom-blur.wgsl +43 -0
  194. package/src/app/shared/ocean/bloom-bright.wgsl +21 -0
  195. package/src/app/shared/ocean/bloom-composite.wgsl +40 -0
  196. package/src/app/shared/ocean/brand-colors.ts +49 -0
  197. package/src/app/shared/ocean/camera.ts +28 -0
  198. package/src/app/shared/ocean/hero-ocean-background.spec.tsx +369 -0
  199. package/src/app/shared/ocean/hero-ocean-background.tsx +238 -0
  200. package/src/app/shared/ocean/ifft-stage.wgsl +36 -0
  201. package/src/app/shared/ocean/initial-spectrum.wgsl +36 -0
  202. package/src/app/shared/ocean/noise.wgsl +24 -0
  203. package/src/app/shared/ocean/normal-foam.wgsl +32 -0
  204. package/src/app/shared/ocean/ocean-colors.ts +11 -0
  205. package/src/app/shared/ocean/ocean-common.wgsl +15 -0
  206. package/src/app/shared/ocean/ocean-graph.ts +29 -0
  207. package/src/app/shared/ocean/particles.wgsl +139 -0
  208. package/src/app/shared/ocean/present.wgsl +40 -0
  209. package/src/app/shared/ocean/renderer.spec.ts +100 -0
  210. package/src/app/shared/ocean/renderer.ts +693 -0
  211. package/src/app/shared/ocean/spectrum.wgsl +35 -0
  212. package/src/app/shared/ocean/tuning.ts +131 -0
  213. package/src/app/shared/ocean/webgpu-support.ts +12 -0
  214. package/src/app/sharing/AccessRequestApprovalPage.spec.tsx +276 -0
  215. package/src/app/sharing/AccessRequestApprovalPage.tsx +307 -0
  216. package/src/app/sharing/AccessRequestsSection.tsx +163 -0
  217. package/src/app/sharing/ResourceAccessScreen.spec.tsx +235 -0
  218. package/src/app/sharing/ResourceAccessScreen.tsx +331 -0
  219. package/src/app/sharing/ShareButton.spec.tsx +171 -5
  220. package/src/app/sharing/ShareButton.tsx +15 -2
  221. package/src/app/sharing/ShareDialog.spec.tsx +71 -2
  222. package/src/app/sharing/ShareDialog.tsx +24 -2
  223. package/src/app/sharing/index.ts +2 -0
  224. package/src/app/styles/agent-native.css +21 -1
  225. package/src/app/styles/agent-native.spec.ts +16 -3
  226. package/src/app/styles/tokens/agent-kit.css +3 -1
  227. package/src/composer/AgentSuggestionBar.overflow.spec.tsx +277 -0
  228. package/src/composer/AgentSuggestionBar.tsx +113 -8
  229. package/src/wgsl-env.d.ts +6 -0
@@ -46,6 +46,7 @@ import {
46
46
  } from "@agent-native/core/client/agent-chat";
47
47
  import { dispatchAgentChatRunning } from "@agent-native/core/client/agent-chat";
48
48
  import {
49
+ fetchAgentEngineConfiguredState,
49
50
  useAgentEngineConfigured,
50
51
  type AgentEngineConfiguredState,
51
52
  } from "@agent-native/core/client/agent-chat";
@@ -84,6 +85,7 @@ import {
84
85
  import { cn } from "@agent-native/toolkit/utils";
85
86
  import {
86
87
  IconAlertTriangle,
88
+ IconLoader2,
87
89
  IconMessage,
88
90
  IconPlayerStopFilled,
89
91
  IconQuote,
@@ -728,6 +730,11 @@ export const AgentKitAssistantChat = forwardRef<
728
730
  },
729
731
  agents: t("agentChat.activity.agents"),
730
732
  tasks: t("agentChat.activity.tasks"),
733
+ toolInput: t("agentChat.tool.input"),
734
+ toolResult: t("agentChat.tool.result"),
735
+ activityValueIdentifierHidden: t("agentChat.tool.identifierHidden"),
736
+ activityValueOmitted: t("agentChat.tool.contentOmitted"),
737
+ activityValueCircular: t("agentChat.tool.circularReference"),
731
738
  working: t("agentChat.status.working"),
732
739
  workingFor: t("agentChat.status.workingFor", {
733
740
  duration: "{{duration}}",
@@ -1158,27 +1165,61 @@ const AgentKitAssistantChatBody = forwardRef<
1158
1165
  tabId: props.tabId,
1159
1166
  threadId,
1160
1167
  });
1168
+ const [submissionReadiness, setSubmissionReadiness] =
1169
+ useState<AgentEngineConfiguredState | null>(null);
1170
+ const readinessRequestIdRef = useRef(0);
1171
+ const providerReadinessRequestRef = useRef<{
1172
+ requestId: number;
1173
+ promise: Promise<AgentEngineConfiguredState>;
1174
+ } | null>(null);
1175
+ const latestResolvedReadinessRef = useRef<{
1176
+ requestId: number;
1177
+ state: AgentEngineConfiguredState;
1178
+ } | null>(null);
1179
+ const passiveReadinessRef = useRef(readiness.state);
1180
+ const providerReadinessPassRef = useRef<number | null>(null);
1181
+ const latestSubmissionReadinessRef = useRef(readiness.state);
1182
+ useEffect(() => {
1183
+ if (passiveReadinessRef.current !== readiness.state) {
1184
+ const explicitReadinessPassed =
1185
+ providerReadinessPassRef.current === readinessRequestIdRef.current &&
1186
+ latestResolvedReadinessRef.current?.requestId ===
1187
+ readinessRequestIdRef.current &&
1188
+ latestResolvedReadinessRef.current.state === "configured";
1189
+ if (readiness.state === "unavailable" && explicitReadinessPassed) {
1190
+ return;
1191
+ }
1192
+ passiveReadinessRef.current = readiness.state;
1193
+ readinessRequestIdRef.current += 1;
1194
+ providerReadinessRequestRef.current = null;
1195
+ providerReadinessPassRef.current = null;
1196
+ latestSubmissionReadinessRef.current = readiness.state;
1197
+ setSubmissionReadiness(null);
1198
+ return;
1199
+ }
1200
+ if (submissionReadiness === readiness.state) {
1201
+ latestSubmissionReadinessRef.current = readiness.state;
1202
+ setSubmissionReadiness(null);
1203
+ }
1204
+ }, [readiness.state, submissionReadiness]);
1161
1205
  const modelCatalogPending =
1162
1206
  props.showModelSelector !== false && props.modelListLoading === true;
1163
1207
  const modelListUnavailable =
1164
1208
  props.showModelSelector !== false && props.modelListError === true;
1165
- const canChat = !providerChecksEnabled || readiness.canChat;
1209
+ const effectiveReadiness = submissionReadiness ?? readiness.state;
1210
+ const canChat = !providerChecksEnabled || effectiveReadiness === "configured";
1166
1211
  const setupMissing =
1167
- providerChecksEnabled &&
1168
- readiness.missing &&
1169
- !modelCatalogPending &&
1170
- !modelListUnavailable;
1171
- const providerStatus: AgentEngineConfiguredState = modelListUnavailable
1172
- ? "unavailable"
1173
- : modelCatalogPending
1174
- ? "unknown"
1175
- : providerChecksEnabled
1176
- ? readiness.state
1177
- : "configured";
1178
- const providerSubmissionPending =
1179
- !canChat &&
1180
- !setupMissing &&
1181
- (providerStatus === "unknown" || providerStatus === "unavailable");
1212
+ providerChecksEnabled && effectiveReadiness === "missing";
1213
+ const providerStatus: AgentEngineConfiguredState = setupMissing
1214
+ ? "missing"
1215
+ : modelListUnavailable
1216
+ ? "unavailable"
1217
+ : modelCatalogPending
1218
+ ? "unknown"
1219
+ : providerChecksEnabled
1220
+ ? effectiveReadiness
1221
+ : "configured";
1222
+ const composerPreflightRunActiveRef = useRef<boolean | null>(null);
1182
1223
  const retryProviderStatus = useCallback(() => {
1183
1224
  window.dispatchEvent(new Event("agent-engine:configured-changed"));
1184
1225
  }, []);
@@ -1365,6 +1406,14 @@ const AgentKitAssistantChatBody = forwardRef<
1365
1406
  const bounceSetupCard = useCallback(() => {
1366
1407
  if (setupMissing) setSetupBouncePulse((pulse) => pulse + 1);
1367
1408
  }, [setupMissing]);
1409
+ const reportMissingProvider = useCallback(() => {
1410
+ bounceSetupCard();
1411
+ window.dispatchEvent(
1412
+ new CustomEvent("agent-chat:missing-api-key", {
1413
+ detail: { tabId: props.tabId, threadId },
1414
+ }),
1415
+ );
1416
+ }, [bounceSetupCard, props.tabId, threadId]);
1368
1417
  const lastCustomRunningStateRef = useRef<{
1369
1418
  custom: boolean;
1370
1419
  isRunning: boolean;
@@ -1849,71 +1898,139 @@ const AgentKitAssistantChatBody = forwardRef<
1849
1898
  return () => window.clearInterval(interval);
1850
1899
  }, [isRunning]);
1851
1900
 
1852
- const acquireSubmission = useCallback(async () => {
1853
- if (
1854
- isRestoring ||
1855
- props.composerDisabled ||
1856
- props.composerSubmissionDisabled
1857
- )
1858
- return null;
1859
- if (!canChat) {
1860
- if (setupMissing) {
1861
- bounceSetupCard();
1862
- window.dispatchEvent(
1863
- new CustomEvent("agent-chat:missing-api-key", {
1864
- detail: { tabId: props.tabId, threadId },
1865
- }),
1866
- );
1867
- }
1868
- return null;
1901
+ const refreshProviderReadiness = useCallback(async () => {
1902
+ if (!providerChecksEnabled) return "configured";
1903
+ let request = providerReadinessRequestRef.current;
1904
+ if (!request || request.requestId !== readinessRequestIdRef.current) {
1905
+ const requestId = ++readinessRequestIdRef.current;
1906
+ providerReadinessPassRef.current = null;
1907
+ request = {
1908
+ requestId,
1909
+ promise: fetchAgentEngineConfiguredState(true, { fresh: true }),
1910
+ };
1911
+ providerReadinessRequestRef.current = request;
1869
1912
  }
1870
- if (history) {
1871
- const release = await history.beginSubmission();
1872
- if (!release) return null;
1873
- return release;
1913
+ let currentRequest = request;
1914
+ while (true) {
1915
+ const nextReadiness = await currentRequest.promise;
1916
+ if (currentRequest.requestId === readinessRequestIdRef.current) {
1917
+ if (providerReadinessRequestRef.current === currentRequest) {
1918
+ providerReadinessRequestRef.current = null;
1919
+ }
1920
+ latestResolvedReadinessRef.current = {
1921
+ requestId: currentRequest.requestId,
1922
+ state: nextReadiness,
1923
+ };
1924
+ latestSubmissionReadinessRef.current = nextReadiness;
1925
+ setSubmissionReadiness(nextReadiness);
1926
+ return nextReadiness;
1927
+ }
1928
+ const latestRequest = providerReadinessRequestRef.current;
1929
+ if (
1930
+ latestRequest &&
1931
+ latestRequest.requestId === readinessRequestIdRef.current &&
1932
+ latestRequest.requestId > currentRequest.requestId
1933
+ ) {
1934
+ currentRequest = latestRequest;
1935
+ continue;
1936
+ }
1937
+ const latestResolved = latestResolvedReadinessRef.current;
1938
+ if (latestResolved?.requestId === readinessRequestIdRef.current) {
1939
+ return latestResolved.state;
1940
+ }
1941
+ return latestSubmissionReadinessRef.current;
1874
1942
  }
1875
- return () => undefined;
1876
- }, [
1877
- bounceSetupCard,
1878
- canChat,
1879
- history,
1880
- isRestoring,
1881
- props.composerDisabled,
1882
- props.composerSubmissionDisabled,
1883
- props.tabId,
1884
- setupMissing,
1885
- threadId,
1886
- ]);
1943
+ }, [providerChecksEnabled]);
1944
+
1945
+ const acquireSubmission = useCallback(
1946
+ async (useReadinessPass = false) => {
1947
+ const readinessPass = providerReadinessPassRef.current;
1948
+ const hasReadinessPass =
1949
+ useReadinessPass &&
1950
+ readinessPass !== null &&
1951
+ readinessPass === readinessRequestIdRef.current &&
1952
+ latestResolvedReadinessRef.current?.requestId === readinessPass &&
1953
+ latestResolvedReadinessRef.current.state === "configured";
1954
+ if (useReadinessPass) providerReadinessPassRef.current = null;
1955
+ if (
1956
+ isRestoring ||
1957
+ props.composerDisabled ||
1958
+ props.composerSubmissionDisabled
1959
+ )
1960
+ return null;
1961
+ if (providerChecksEnabled) {
1962
+ const currentReadiness = hasReadinessPass
1963
+ ? "configured"
1964
+ : await refreshProviderReadiness();
1965
+ if (currentReadiness !== "configured") {
1966
+ if (currentReadiness === "missing") reportMissingProvider();
1967
+ return null;
1968
+ }
1969
+ }
1970
+ if (history) {
1971
+ const release = await history.beginSubmission();
1972
+ if (!release) return null;
1973
+ return release;
1974
+ }
1975
+ return () => undefined;
1976
+ },
1977
+ [
1978
+ history,
1979
+ isRestoring,
1980
+ props.composerDisabled,
1981
+ props.composerSubmissionDisabled,
1982
+ providerChecksEnabled,
1983
+ refreshProviderReadiness,
1984
+ reportMissingProvider,
1985
+ ],
1986
+ );
1887
1987
 
1888
1988
  const beforeSubmit = useCallback(async () => {
1989
+ providerReadinessPassRef.current = null;
1990
+ composerPreflightRunActiveRef.current = null;
1889
1991
  if (
1890
1992
  isRestoring ||
1891
1993
  props.composerDisabled ||
1892
1994
  props.composerSubmissionDisabled
1893
1995
  )
1894
1996
  return false;
1895
- if (!canChat) {
1896
- if (setupMissing) {
1897
- bounceSetupCard();
1898
- window.dispatchEvent(
1899
- new CustomEvent("agent-chat:missing-api-key", {
1900
- detail: { tabId: props.tabId, threadId },
1901
- }),
1902
- );
1903
- }
1904
- return providerSubmissionPending;
1997
+ const runWasActive = isThreadRunning();
1998
+ if (!providerChecksEnabled) {
1999
+ composerPreflightRunActiveRef.current = runWasActive;
2000
+ return true;
1905
2001
  }
2002
+ let currentReadiness = await refreshProviderReadiness();
2003
+ let resolvedReadiness = latestResolvedReadinessRef.current;
2004
+ while (
2005
+ currentReadiness === "configured" &&
2006
+ (!resolvedReadiness ||
2007
+ resolvedReadiness.requestId !== readinessRequestIdRef.current ||
2008
+ resolvedReadiness.state !== "configured")
2009
+ ) {
2010
+ currentReadiness = await refreshProviderReadiness();
2011
+ resolvedReadiness = latestResolvedReadinessRef.current;
2012
+ }
2013
+ if (currentReadiness !== "configured") {
2014
+ if (currentReadiness === "missing") reportMissingProvider();
2015
+ return false;
2016
+ }
2017
+ if (
2018
+ !resolvedReadiness ||
2019
+ resolvedReadiness.requestId !== readinessRequestIdRef.current
2020
+ ) {
2021
+ return false;
2022
+ }
2023
+ composerPreflightRunActiveRef.current = runWasActive;
2024
+ providerReadinessPassRef.current = resolvedReadiness.requestId;
1906
2025
  return true;
1907
2026
  }, [
1908
- bounceSetupCard,
1909
- canChat,
1910
2027
  isRestoring,
1911
2028
  props.composerDisabled,
1912
2029
  props.composerSubmissionDisabled,
1913
- props.tabId,
1914
- providerSubmissionPending,
1915
- setupMissing,
1916
- threadId,
2030
+ providerChecksEnabled,
2031
+ isThreadRunning,
2032
+ refreshProviderReadiness,
2033
+ reportMissingProvider,
1917
2034
  ]);
1918
2035
 
1919
2036
  const dispatch = useCallback(
@@ -2120,13 +2237,7 @@ const AgentKitAssistantChatBody = forwardRef<
2120
2237
  );
2121
2238
  const release = await acquireSubmission();
2122
2239
  if (!release) {
2123
- if (
2124
- !setupMissing &&
2125
- (isRestoring ||
2126
- (providerChecksEnabled &&
2127
- (readiness.state === "unknown" ||
2128
- readiness.state === "unavailable")))
2129
- ) {
2240
+ if (isRestoring) {
2130
2241
  try {
2131
2242
  const selectionHydration = pendingSelectionHydrationRef.current;
2132
2243
  await selectionHydration?.promise;
@@ -2218,9 +2329,10 @@ const AgentKitAssistantChatBody = forwardRef<
2218
2329
  throw error;
2219
2330
  }
2220
2331
  }
2221
- const reason = setupMissing
2222
- ? "engine-not-configured"
2223
- : "submission-unavailable";
2332
+ const reason =
2333
+ setupMissing || latestSubmissionReadinessRef.current === "missing"
2334
+ ? "engine-not-configured"
2335
+ : "submission-unavailable";
2224
2336
  reportAgentChatSubmitResult(options.submitMessageId, false, reason);
2225
2337
  return { status: "rejected", reason };
2226
2338
  }
@@ -2260,8 +2372,6 @@ const AgentKitAssistantChatBody = forwardRef<
2260
2372
  isRestoring,
2261
2373
  isThreadRunning,
2262
2374
  props.tabId,
2263
- providerChecksEnabled,
2264
- readiness.state,
2265
2375
  setupMissing,
2266
2376
  t,
2267
2377
  threadId,
@@ -2276,38 +2386,13 @@ const AgentKitAssistantChatBody = forwardRef<
2276
2386
  composerOptions: AgentKitSuggestionSubmitOptions,
2277
2387
  prepare?: () => Promise<PromptComposerSubmitOptions>,
2278
2388
  ) => {
2279
- const runWasActiveAtSubmit = isThreadRunning();
2280
- const release = await acquireSubmission();
2281
- if (!release) {
2282
- if (
2283
- providerSubmissionPending &&
2284
- !props.composerDisabled &&
2285
- !props.composerSubmissionDisabled
2286
- ) {
2287
- try {
2288
- const preparedOptions = prepare ? await prepare() : composerOptions;
2289
- const submittedOptions = captureQueuedRunState(
2290
- preserveQueuedIntent(preparedOptions, composerOptions),
2291
- runWasActiveAtSubmit,
2292
- );
2293
- const result = await submit(
2294
- text,
2295
- files,
2296
- references,
2297
- submittedOptions,
2298
- );
2299
- if (result.status === "rejected") {
2300
- throw new Error(t("agentChat.recovery.deferredSubmissionFailed"));
2301
- }
2302
- } catch (error) {
2303
- dispatchSetupRequiredEvent(error, props.tabId, threadId);
2304
- throw error;
2305
- }
2306
- } else {
2307
- throw new Error(t("agentChat.recovery.deferredSubmissionFailed"));
2308
- }
2309
- return;
2310
- }
2389
+ const runWasActiveAtSubmit = providerReadinessPassRef.current
2390
+ ? (composerPreflightRunActiveRef.current ?? isThreadRunning())
2391
+ : isThreadRunning();
2392
+ composerPreflightRunActiveRef.current = null;
2393
+ const release = await acquireSubmission(true);
2394
+ if (!release)
2395
+ throw new Error(t("agentChat.recovery.deferredSubmissionFailed"));
2311
2396
  try {
2312
2397
  const preparedOptions = prepare ? await prepare() : composerOptions;
2313
2398
  await dispatch(
@@ -2333,8 +2418,6 @@ const AgentKitAssistantChatBody = forwardRef<
2333
2418
  props.composerDisabled,
2334
2419
  props.composerSubmissionDisabled,
2335
2420
  props.tabId,
2336
- providerSubmissionPending,
2337
- submit,
2338
2421
  t,
2339
2422
  threadId,
2340
2423
  ],
@@ -3102,7 +3185,9 @@ function AgentKitTranscript({ children, threadId }: AgentKitRegionRenderProps) {
3102
3185
  ? []
3103
3186
  : getAgentKitThreadHandoffMessages(thread, surface.handoffSnapshot);
3104
3187
  const guided = useGuidedQuestionFlow({
3105
- enabled: surface.props.isActiveComposer !== false,
3188
+ enabled:
3189
+ surface.props.isActiveComposer !== false &&
3190
+ surface.props.showGuidedQuestions !== false,
3106
3191
  stateKey: "guided-questions",
3107
3192
  queryKey: ["guided-questions", "agentkit"],
3108
3193
  browserTabId: surface.props.browserTabId,
@@ -3337,7 +3422,8 @@ function AgentKitTranscript({ children, threadId }: AgentKitRegionRenderProps) {
3337
3422
  </span>
3338
3423
  </div>
3339
3424
  ) : null}
3340
- {guided.questions?.length ? (
3425
+ {surface.props.showGuidedQuestions !== false &&
3426
+ guided.questions?.length ? (
3341
3427
  <div className="px-3 pb-3">
3342
3428
  <GuidedQuestionFlow
3343
3429
  questions={guided.questions}
@@ -3797,8 +3883,9 @@ function AgentKitComposerSurface({
3797
3883
  }
3798
3884
  />
3799
3885
  ) : null}
3800
- {modelListUnavailable ||
3801
- (!canChat && !setupMissing && providerStatus !== "configured") ? (
3886
+ {!setupMissing &&
3887
+ (modelListUnavailable ||
3888
+ (!canChat && providerStatus !== "configured")) ? (
3802
3889
  <GuidedQuestionProviderGate
3803
3890
  providerStatus={providerStatus}
3804
3891
  modelListUnavailable={modelListUnavailable}
@@ -3911,7 +3998,23 @@ function AgentKitComposerSurface({
3911
3998
  {props.composerToolbarSlot}
3912
3999
  </>
3913
4000
  }
3914
- extraActionButton={props.composerExtraActionButton}
4001
+ extraActionButton={
4002
+ <>
4003
+ {props.composerExtraActionButton}
4004
+ {composerSubmissionPending ? (
4005
+ <span
4006
+ className="inline-flex items-center gap-1.5 text-xs text-muted-foreground"
4007
+ role="status"
4008
+ aria-live="polite"
4009
+ aria-label={t("agentChat.common.waiting")}
4010
+ data-testid="provider-preflight-pending"
4011
+ >
4012
+ <IconLoader2 className="size-3.5 animate-spin motion-reduce:animate-none" />
4013
+ <span>{t("agentChat.common.waiting")}</span>
4014
+ </span>
4015
+ ) : null}
4016
+ </>
4017
+ }
3915
4018
  includeDefaultSlashCommands
3916
4019
  includeDefaultSlashSkills
3917
4020
  onSlashCommand={props.onSlashCommand}
@@ -266,6 +266,7 @@ export interface AgentSidebarProps {
266
266
  suppressInlineOpenApp?: AssistantChatProps["suppressInlineOpenApp"];
267
267
  composerPlaceholder?: AssistantChatProps["composerPlaceholder"];
268
268
  showMissingApiKeySetup?: AssistantChatProps["showMissingApiKeySetup"];
269
+ showGuidedQuestions?: AssistantChatProps["showGuidedQuestions"];
269
270
  openOnChatRunning?: boolean;
270
271
  onFullscreenRequest?: (threadId?: string) => void;
271
272
  onOpenSettings?: (section?: string) => void;
@@ -340,6 +341,7 @@ export function AgentSidebar({
340
341
  suppressInlineOpenApp,
341
342
  composerPlaceholder,
342
343
  showMissingApiKeySetup,
344
+ showGuidedQuestions,
343
345
  openOnChatRunning = false,
344
346
  onFullscreenRequest,
345
347
  onOpenSettings,
@@ -1158,6 +1160,7 @@ export function AgentSidebar({
1158
1160
  suppressInlineOpenApp={suppressInlineOpenApp}
1159
1161
  composerPlaceholder={composerPlaceholder}
1160
1162
  showMissingApiKeySetup={showMissingApiKeySetup}
1163
+ showGuidedQuestions={showGuidedQuestions}
1161
1164
  missingApiKeySetupLayout="sidebar"
1162
1165
  defaultMode={defaultMode}
1163
1166
  onCollapse={() => setOpenPersisted(false)}
@@ -235,6 +235,29 @@ describe("useGuidedQuestionFlow scoped reads", () => {
235
235
  expect(result.current().questions?.length).toBe(1);
236
236
  });
237
237
 
238
+ it("hides a thread-scoped question until its owner thread is known", async () => {
239
+ vi.stubGlobal(
240
+ "fetch",
241
+ vi.fn(async (input: RequestInfo | URL) =>
242
+ readResponse(String(input), (key) =>
243
+ key === "guided-questions:tab123"
244
+ ? JSON.stringify({ ...payload, threadId: "chat-a" })
245
+ : "",
246
+ ),
247
+ ),
248
+ );
249
+
250
+ const result = await renderFlow({
251
+ stateKey: "guided-questions",
252
+ queryKey: ["guided-questions"],
253
+ browserTabId: "tab123",
254
+ refetchInterval: false,
255
+ });
256
+
257
+ expect(result.current().questions).toBeNull();
258
+ expect(result.current().payload).toBeNull();
259
+ });
260
+
238
261
  it("renders a payload with no threadId in any chat", async () => {
239
262
  vi.stubGlobal(
240
263
  "fetch",
@@ -401,6 +424,68 @@ describe("useGuidedQuestionFlow scoped reads", () => {
401
424
  expect(deleted).toContain("guided-questions:tab123");
402
425
  });
403
426
 
427
+ it("does not restore an in-flight read after clear", async () => {
428
+ let resolveRead: (() => void) | null = null;
429
+ const fetchMock = vi.fn((input: RequestInfo | URL, init?: RequestInit) => {
430
+ const url = String(input);
431
+ if (init?.method === "DELETE") {
432
+ return Promise.resolve(new Response("", { status: 200 }));
433
+ }
434
+ return new Promise<Response>((resolve) => {
435
+ resolveRead = () =>
436
+ resolve(
437
+ readResponse(url, (key) =>
438
+ key === "guided-questions" ? JSON.stringify(payload) : "",
439
+ ),
440
+ );
441
+ });
442
+ });
443
+ vi.stubGlobal("fetch", fetchMock);
444
+
445
+ const client = new QueryClient({
446
+ defaultOptions: { queries: { retry: false } },
447
+ });
448
+ let latest: HookResult | null = null;
449
+ function Harness() {
450
+ const flow = useGuidedQuestionFlow({
451
+ providerStatusChecksEnabled: false,
452
+ stateKey: "guided-questions",
453
+ queryKey: ["guided-questions"],
454
+ refetchInterval: false,
455
+ });
456
+ latest = flow;
457
+ return flow.questions ? (
458
+ <GuidedQuestionFlow
459
+ questions={flow.questions}
460
+ onSubmit={() => {}}
461
+ onSkip={() => {}}
462
+ />
463
+ ) : null;
464
+ }
465
+
466
+ await act(async () => {
467
+ root.render(
468
+ <QueryClientProvider client={client}>
469
+ <Harness />
470
+ </QueryClientProvider>,
471
+ );
472
+ await Promise.resolve();
473
+ });
474
+ for (let i = 0; i < 20 && !resolveRead; i += 1) await flush();
475
+ expect(resolveRead).toEqual(expect.any(Function));
476
+
477
+ await act(async () => {
478
+ latest!.clear();
479
+ expect(latest!.questions).toBeNull();
480
+ resolveRead!();
481
+ await Promise.resolve();
482
+ });
483
+ await flush();
484
+
485
+ expect(latest!.questions).toBeNull();
486
+ expect(container.textContent).not.toContain("Which range?");
487
+ });
488
+
404
489
  // The agent ids every question it asks `q1`, so an answer that travels as
405
490
  // `q1: 7d` only means something while the turn that asked it survives
406
491
  // history trimming alongside it. When it did not, the agent re-asked the
@@ -1095,10 +1095,12 @@ function payloadBelongsToThread(
1095
1095
  payload: GuidedQuestionPayload,
1096
1096
  threadId: string | undefined,
1097
1097
  ): boolean {
1098
+ const requestedThread = threadId?.trim();
1098
1099
  const asker =
1099
1100
  typeof payload.threadId === "string" ? payload.threadId.trim() : "";
1100
1101
  if (!asker) return true;
1101
- return asker === (threadId ?? "").trim();
1102
+ if (!requestedThread) return false;
1103
+ return asker === requestedThread;
1102
1104
  }
1103
1105
 
1104
1106
  export function useGuidedQuestionFlow({
@@ -1119,6 +1121,7 @@ export function useGuidedQuestionFlow({
1119
1121
  }: UseGuidedQuestionFlowOptions = {}) {
1120
1122
  const queryClient = useQueryClient();
1121
1123
  const [payload, setPayload] = useState<GuidedQuestionPayload | null>(null);
1124
+ const queryGenerationRef = useRef(0);
1122
1125
  const normalizedBrowserTabId = useMemo(
1123
1126
  () => normalizeBrowserTabId(browserTabId),
1124
1127
  [browserTabId],
@@ -1154,6 +1157,7 @@ export function useGuidedQuestionFlow({
1154
1157
  queryKey: resolvedQueryKey,
1155
1158
  enabled,
1156
1159
  queryFn: async () => {
1160
+ const queryGeneration = queryGenerationRef.current;
1157
1161
  const read = async (key: string) => {
1158
1162
  const parsed = await readClientAppState<GuidedQuestionPayload>(
1159
1163
  key,
@@ -1163,10 +1167,11 @@ export function useGuidedQuestionFlow({
1163
1167
  }
1164
1168
  return null;
1165
1169
  };
1166
- return (
1167
- (normalizedBrowserTabId ? await read(scopedKey) : null) ??
1168
- (await read(stateKey))
1169
- );
1170
+ const scopedPayload = normalizedBrowserTabId
1171
+ ? await read(scopedKey)
1172
+ : null;
1173
+ const result = scopedPayload ?? (await read(stateKey));
1174
+ return queryGeneration === queryGenerationRef.current ? result : null;
1170
1175
  },
1171
1176
  refetchInterval: resolvedRefetchInterval,
1172
1177
  structuralSharing: false,
@@ -1221,6 +1226,8 @@ export function useGuidedQuestionFlow({
1221
1226
  const submissionInFlightRef = useRef(false);
1222
1227
 
1223
1228
  const clear = useCallback(() => {
1229
+ // A read already in flight may return the submitted question after clear.
1230
+ queryGenerationRef.current += 1;
1224
1231
  setPayload(null);
1225
1232
  queryClient.setQueryData(resolvedQueryKey, null);
1226
1233
  const del = (key: string) => deleteClientAppState(key).catch(() => {});
@@ -353,6 +353,8 @@ export interface AssistantChatProps {
353
353
  providerStatusChecksEnabled?: boolean;
354
354
  /** Replace the built-in transport with an AgentKit-native BYO transport. */
355
355
  createTransport?: (context: AssistantChatAdapterContext) => AgentTransport;
356
+ /** Hide the default guided-question card when another host owns its flow. */
357
+ showGuidedQuestions?: boolean;
356
358
  /**
357
359
  * Bring-your-own agent runtime. When supplied, AssistantChat keeps the
358
360
  * standard composer/transcript/tool rendering shell but sends turns through