@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.
- package/agent-native.eject.json +16 -1
- package/dist/app/agentkit/react/components.d.ts +5 -3
- package/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +103 -42
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/agentkit/react/styles.css +93 -65
- package/dist/app/agentkit/react/voice-transcripts.d.ts +22 -1
- package/dist/app/agentkit/react/voice-transcripts.d.ts.map +1 -1
- package/dist/app/agentkit/react/voice-transcripts.js +209 -8
- package/dist/app/agentkit/react/voice-transcripts.js.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +55 -40
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/agentkit-chat/guided-questions.d.ts +1 -0
- package/dist/app/chat/agentkit-chat/guided-questions.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/guided-questions.js +18 -6
- package/dist/app/chat/agentkit-chat/guided-questions.js.map +1 -1
- package/dist/app/chat/agentkit-chat/index.d.ts +1 -1
- package/dist/app/chat/agentkit-chat/index.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/index.js +1 -1
- package/dist/app/chat/agentkit-chat/index.js.map +1 -1
- package/dist/app/chat/chat/run-recovery.d.ts.map +1 -1
- package/dist/app/chat/chat/run-recovery.js +5 -1
- package/dist/app/chat/chat/run-recovery.js.map +1 -1
- package/dist/app/chat/composer/runtime-adapters.d.ts.map +1 -1
- package/dist/app/chat/composer/runtime-adapters.js +2 -0
- package/dist/app/chat/composer/runtime-adapters.js.map +1 -1
- package/dist/app/chat/reply-connection-suggestion.d.ts +29 -0
- package/dist/app/chat/reply-connection-suggestion.d.ts.map +1 -0
- package/dist/app/chat/reply-connection-suggestion.js +42 -0
- package/dist/app/chat/reply-connection-suggestion.js.map +1 -0
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +7 -0
- package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.js +7 -0
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +7 -0
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +7 -0
- package/dist/app/i18n/catalogs/en-US.js.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.js +7 -0
- package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.js +7 -0
- package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.js +7 -0
- package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.js +7 -0
- package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.js +7 -0
- package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.js +7 -0
- package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.js +7 -0
- package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.js +7 -0
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/mcp-approval/McpApprovalReview.d.ts +25 -0
- package/dist/app/mcp-approval/McpApprovalReview.d.ts.map +1 -0
- package/dist/app/mcp-approval/McpApprovalReview.js +342 -0
- package/dist/app/mcp-approval/McpApprovalReview.js.map +1 -0
- package/dist/app/onboarding/FirstRunOnboarding.d.ts.map +1 -1
- package/dist/app/onboarding/FirstRunOnboarding.js +299 -71
- package/dist/app/onboarding/FirstRunOnboarding.js.map +1 -1
- package/dist/app/onboarding/GettingStartedCard.js +1 -1
- package/dist/app/onboarding/GettingStartedCard.js.map +1 -1
- package/dist/app/onboarding/first-run-builder-credits-flag.d.ts +2 -0
- package/dist/app/onboarding/first-run-builder-credits-flag.d.ts.map +1 -0
- package/dist/app/onboarding/first-run-builder-credits-flag.js +8 -0
- package/dist/app/onboarding/first-run-builder-credits-flag.js.map +1 -0
- package/dist/app/providers/AppProviders.d.ts.map +1 -1
- package/dist/app/providers/AppProviders.js +14 -1
- package/dist/app/providers/AppProviders.js.map +1 -1
- package/dist/app/resources/McpConnectionSuggestion.d.ts +2 -4
- package/dist/app/resources/McpConnectionSuggestion.d.ts.map +1 -1
- package/dist/app/resources/McpConnectionSuggestion.js +59 -36
- package/dist/app/resources/McpConnectionSuggestion.js.map +1 -1
- package/dist/app/settings/account/PreferencesSettings.d.ts.map +1 -1
- package/dist/app/settings/account/PreferencesSettings.js +14 -1
- package/dist/app/settings/account/PreferencesSettings.js.map +1 -1
- package/dist/app/settings/account/ThemeFamilySettings.js +2 -2
- package/dist/app/settings/account/ThemeFamilySettings.js.map +1 -1
- package/dist/app/settings/account/search-entries.d.ts +1 -0
- package/dist/app/settings/account/search-entries.d.ts.map +1 -1
- package/dist/app/settings/account/search-entries.js +5 -0
- package/dist/app/settings/account/search-entries.js.map +1 -1
- package/dist/app/settings/shell/SettingsShell.d.ts.map +1 -1
- package/dist/app/settings/shell/SettingsShell.js +26 -2
- package/dist/app/settings/shell/SettingsShell.js.map +1 -1
- package/dist/app/shared/AppShellSkeleton.d.ts.map +1 -1
- package/dist/app/shared/AppShellSkeleton.js +4 -10
- package/dist/app/shared/AppShellSkeleton.js.map +1 -1
- package/dist/app/shared/EnvironmentBadge.d.ts +2 -1
- package/dist/app/shared/EnvironmentBadge.d.ts.map +1 -1
- package/dist/app/shared/EnvironmentBadge.js +2 -3
- package/dist/app/shared/EnvironmentBadge.js.map +1 -1
- package/dist/app/shared/is-builder-io-employee.d.ts +8 -0
- package/dist/app/shared/is-builder-io-employee.d.ts.map +1 -0
- package/dist/app/shared/is-builder-io-employee.js +10 -0
- package/dist/app/shared/is-builder-io-employee.js.map +1 -0
- package/dist/app/styles/agent-conversation.css +30 -17
- package/dist/app/styles/theme-families.css +22 -5
- package/dist/app/styles/tokens/agent-kit.css +3 -3
- package/dist/composer/PromptComposer.d.ts +15 -3
- package/dist/composer/PromptComposer.d.ts.map +1 -1
- package/dist/composer/PromptComposer.js +74 -16
- package/dist/composer/PromptComposer.js.map +1 -1
- package/dist/composer/RealtimeVoiceMode.d.ts +7 -0
- package/dist/composer/RealtimeVoiceMode.d.ts.map +1 -1
- package/dist/composer/RealtimeVoiceMode.js +9 -2
- package/dist/composer/RealtimeVoiceMode.js.map +1 -1
- package/dist/composer/TiptapComposer.d.ts +7 -0
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +12 -4
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/dist/composer/VoiceButton.d.ts +2 -0
- package/dist/composer/VoiceButton.d.ts.map +1 -1
- package/dist/composer/VoiceButton.js +5 -4
- package/dist/composer/VoiceButton.js.map +1 -1
- package/dist/composer/index.d.ts +2 -2
- package/dist/composer/index.d.ts.map +1 -1
- package/dist/composer/index.js +1 -1
- package/dist/composer/index.js.map +1 -1
- package/dist/composer/prompt-attachments.d.ts +20 -0
- package/dist/composer/prompt-attachments.d.ts.map +1 -1
- package/dist/composer/prompt-attachments.js +188 -11
- package/dist/composer/prompt-attachments.js.map +1 -1
- package/dist/composer/realtime-voice-transcript.d.ts +27 -0
- package/dist/composer/realtime-voice-transcript.d.ts.map +1 -1
- package/dist/composer/realtime-voice-transcript.js +34 -1
- package/dist/composer/realtime-voice-transcript.js.map +1 -1
- package/dist/composer/realtime-voice-turn-metrics.d.ts +43 -0
- package/dist/composer/realtime-voice-turn-metrics.d.ts.map +1 -0
- package/dist/composer/realtime-voice-turn-metrics.js +132 -0
- package/dist/composer/realtime-voice-turn-metrics.js.map +1 -0
- package/dist/composer/runtime-adapters.d.ts +4 -1
- package/dist/composer/runtime-adapters.d.ts.map +1 -1
- package/dist/composer/runtime-adapters.js +2 -1
- package/dist/composer/runtime-adapters.js.map +1 -1
- package/dist/composer/useRealtimeVoiceMode.d.ts +61 -9
- package/dist/composer/useRealtimeVoiceMode.d.ts.map +1 -1
- package/dist/composer/useRealtimeVoiceMode.js +536 -99
- package/dist/composer/useRealtimeVoiceMode.js.map +1 -1
- package/dist/composer/voice-chat-icon.d.ts +7 -0
- package/dist/composer/voice-chat-icon.d.ts.map +1 -0
- package/dist/composer/voice-chat-icon.js +14 -0
- package/dist/composer/voice-chat-icon.js.map +1 -0
- package/dist/editor/DragHandle.d.ts +12 -1
- package/dist/editor/DragHandle.d.ts.map +1 -1
- package/dist/editor/DragHandle.js +165 -41
- package/dist/editor/DragHandle.js.map +1 -1
- package/dist/editor/RegistryBlockNode.d.ts +20 -0
- package/dist/editor/RegistryBlockNode.d.ts.map +1 -1
- package/dist/editor/RegistryBlockNode.js +58 -13
- package/dist/editor/RegistryBlockNode.js.map +1 -1
- package/dist/editor/index.d.ts +2 -2
- package/dist/editor/index.d.ts.map +1 -1
- package/dist/editor/index.js +1 -1
- package/dist/editor/index.js.map +1 -1
- package/dist/styles.css +2 -0
- package/dist/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/ui/dropdown-menu.js +5 -1
- package/dist/ui/dropdown-menu.js.map +1 -1
- package/package.json +14 -4
- package/src/app/agentkit/react/components.regenerate-readiness.spec.tsx +3 -1
- package/src/app/agentkit/react/components.spec.tsx +24 -4
- package/src/app/agentkit/react/components.tsx +174 -44
- package/src/app/agentkit/react/composer-context.spec.tsx +3 -0
- package/src/app/agentkit/react/disclosures.spec.tsx +124 -0
- package/src/app/agentkit/react/lifecycle.spec.tsx +8 -2
- package/src/app/agentkit/react/message-actions.spec.tsx +161 -5
- package/src/app/agentkit/react/saved-reply-reload.spec.tsx +206 -0
- package/src/app/agentkit/react/styles.css +93 -65
- package/src/app/agentkit/react/voice-transcripts.spec.tsx +239 -4
- package/src/app/agentkit/react/voice-transcripts.ts +245 -8
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +59 -11
- package/src/app/chat/AgentKitAssistantChat.tsx +74 -62
- package/src/app/chat/agentkit-chat/guided-answers-context.spec.ts +9 -0
- package/src/app/chat/agentkit-chat/guided-questions.tsx +17 -7
- package/src/app/chat/agentkit-chat/index.ts +1 -0
- package/src/app/chat/chat/run-recovery.spec.tsx +45 -0
- package/src/app/chat/chat/run-recovery.tsx +6 -1
- package/src/app/chat/composer/runtime-adapters.tsx +2 -0
- package/src/app/chat/reply-connection-suggestion.spec.ts +121 -0
- package/src/app/chat/reply-connection-suggestion.ts +76 -0
- package/src/app/i18n/catalogs/ar-SA.ts +10 -0
- package/src/app/i18n/catalogs/de-DE.ts +10 -0
- package/src/app/i18n/catalogs/en-US.ts +10 -0
- package/src/app/i18n/catalogs/es-ES.ts +10 -0
- package/src/app/i18n/catalogs/fr-FR.ts +10 -0
- package/src/app/i18n/catalogs/hi-IN.ts +10 -0
- package/src/app/i18n/catalogs/ja-JP.ts +10 -0
- package/src/app/i18n/catalogs/ko-KR.ts +10 -0
- package/src/app/i18n/catalogs/pt-BR.ts +10 -0
- package/src/app/i18n/catalogs/zh-CN.ts +10 -0
- package/src/app/i18n/catalogs/zh-TW.ts +10 -0
- package/src/app/mcp-approval/McpApprovalReview.spec.tsx +687 -0
- package/src/app/mcp-approval/McpApprovalReview.tsx +913 -0
- package/src/app/onboarding/FirstRunOnboarding.spec.tsx +736 -26
- package/src/app/onboarding/FirstRunOnboarding.tsx +473 -145
- package/src/app/onboarding/GettingStartedCard.tsx +1 -1
- package/src/app/onboarding/first-run-builder-credits-flag.spec.ts +13 -0
- package/src/app/onboarding/first-run-builder-credits-flag.ts +9 -0
- package/src/app/providers/AppProviders.spec.tsx +109 -0
- package/src/app/providers/AppProviders.tsx +53 -1
- package/src/app/resources/McpConnectionSuggestion.spec.ts +77 -12
- package/src/app/resources/McpConnectionSuggestion.tsx +69 -51
- package/src/app/settings/VoiceChatSettings.spec.tsx +6 -1
- package/src/app/settings/account/PreferencesSettings.spec.tsx +73 -0
- package/src/app/settings/account/PreferencesSettings.tsx +24 -7
- package/src/app/settings/account/ThemeFamilySettings.tsx +2 -2
- package/src/app/settings/account/search-entries.spec.ts +14 -0
- package/src/app/settings/account/search-entries.ts +9 -0
- package/src/app/settings/shell/SettingsShell.tsx +29 -2
- package/src/app/shared/AppShellSkeleton.spec.tsx +2 -2
- package/src/app/shared/AppShellSkeleton.tsx +3 -16
- package/src/app/shared/EnvironmentBadge.tsx +3 -4
- package/src/app/shared/is-builder-io-employee.ts +17 -0
- package/src/app/styles/agent-conversation.css +30 -17
- package/src/app/styles/theme-families.css +22 -5
- package/src/app/styles/theme-families.spec.ts +73 -0
- package/src/app/styles/tokens/agent-kit.css +3 -3
- package/src/composer/AgentSuggestionBar.overflow.spec.tsx +1 -1
- package/src/composer/AttachmentChip.spec.tsx +87 -0
- package/src/composer/PromptComposer.context.spec.tsx +8 -4
- package/src/composer/PromptComposer.setup-card.spec.tsx +170 -0
- package/src/composer/PromptComposer.tsx +125 -14
- package/src/composer/RealtimeVoiceMode.spec.tsx +48 -0
- package/src/composer/RealtimeVoiceMode.tsx +45 -2
- package/src/composer/TiptapComposer.tsx +25 -0
- package/src/composer/TiptapComposer.voice-toolbar.spec.tsx +4 -1
- package/src/composer/VoiceButton.tsx +8 -3
- package/src/composer/index.ts +4 -0
- package/src/composer/prompt-attachments.spec.ts +160 -8
- package/src/composer/prompt-attachments.ts +221 -12
- package/src/composer/realtime-voice-transcript.spec.ts +36 -0
- package/src/composer/realtime-voice-transcript.ts +56 -1
- package/src/composer/realtime-voice-turn-metrics.spec.ts +175 -0
- package/src/composer/realtime-voice-turn-metrics.ts +192 -0
- package/src/composer/runtime-adapters.tsx +5 -1
- package/src/composer/useRealtimeVoiceMode.session.spec.tsx +487 -2
- package/src/composer/useRealtimeVoiceMode.spec.ts +267 -65
- package/src/composer/useRealtimeVoiceMode.tsx +716 -139
- package/src/composer/voice-chat-icon.tsx +19 -0
- package/src/editor/DragHandle.spec.ts +217 -0
- package/src/editor/DragHandle.ts +214 -55
- package/src/editor/RegistryBlockNode.spec.ts +172 -2
- package/src/editor/RegistryBlockNode.tsx +91 -13
- package/src/editor/index.ts +4 -0
- package/src/styles.css +2 -0
- package/src/ui/dropdown-menu.tsx +4 -0
|
@@ -237,6 +237,11 @@ import {
|
|
|
237
237
|
isWaitingOnConnectionRequest,
|
|
238
238
|
reconcileSettledRun,
|
|
239
239
|
} from "./reconcile-settled-run.js";
|
|
240
|
+
import {
|
|
241
|
+
pickReplyConnectionSuggestion,
|
|
242
|
+
latestReplyContext,
|
|
243
|
+
type LatestReplyContext,
|
|
244
|
+
} from "./reply-connection-suggestion.js";
|
|
240
245
|
import { RunStuckBanner } from "./RunStuckBanner.js";
|
|
241
246
|
import { SESSION_REPLAY_MASK_PROPS } from "./session-replay-privacy.js";
|
|
242
247
|
import { ThinkingDisplayProvider } from "./thinking-display.js";
|
|
@@ -1455,6 +1460,8 @@ interface AgentKitSurfaceContextValue {
|
|
|
1455
1460
|
hasRenderedMessages: boolean;
|
|
1456
1461
|
canChat: boolean;
|
|
1457
1462
|
setupMissing: boolean;
|
|
1463
|
+
/** Only the server's readiness answer, never the model list's early guess. */
|
|
1464
|
+
setupConfirmedMissing: boolean;
|
|
1458
1465
|
providerStatus: AgentEngineConfiguredState;
|
|
1459
1466
|
modelListUnavailable: boolean;
|
|
1460
1467
|
retryProviderStatus: () => void;
|
|
@@ -1476,6 +1483,7 @@ interface AgentKitSurfaceContextValue {
|
|
|
1476
1483
|
authSessionAvailable: boolean;
|
|
1477
1484
|
setupBouncePulse: number;
|
|
1478
1485
|
bounceSetupCard: () => void;
|
|
1486
|
+
onComposerSetupRequiredChange: (required: boolean) => void;
|
|
1479
1487
|
isSubmissionInFlight: boolean;
|
|
1480
1488
|
composerSubmissionPending: boolean;
|
|
1481
1489
|
onComposerSubmissionPendingChange: (pending: boolean) => void;
|
|
@@ -2341,8 +2349,22 @@ const AgentKitAssistantChatBody = forwardRef<
|
|
|
2341
2349
|
props.showModelSelector !== false && props.modelListError === true;
|
|
2342
2350
|
const effectiveReadiness = readiness.state;
|
|
2343
2351
|
const canChat = !providerChecksEnabled || effectiveReadiness === "configured";
|
|
2344
|
-
const
|
|
2352
|
+
const [composerSetupRequired, setComposerSetupRequired] = useState(false);
|
|
2353
|
+
const setupConfirmedMissing =
|
|
2345
2354
|
providerChecksEnabled && effectiveReadiness === "missing";
|
|
2355
|
+
// The composer's model list knows nothing is connected well before the
|
|
2356
|
+
// readiness check answers; an answer of "configured" still wins. Only this
|
|
2357
|
+
// chat's own card can show on that guess: a host-owned card waits for the
|
|
2358
|
+
// server, and acting on the guess without it closes the prompt with nothing
|
|
2359
|
+
// on screen to explain why.
|
|
2360
|
+
const ownsSetupCard =
|
|
2361
|
+
props.showMissingApiKeySetup !== false && props.setupCardOwner !== "host";
|
|
2362
|
+
const setupMissing =
|
|
2363
|
+
setupConfirmedMissing ||
|
|
2364
|
+
(providerChecksEnabled &&
|
|
2365
|
+
ownsSetupCard &&
|
|
2366
|
+
effectiveReadiness !== "configured" &&
|
|
2367
|
+
composerSetupRequired);
|
|
2346
2368
|
const providerStatus: AgentEngineConfiguredState = setupMissing
|
|
2347
2369
|
? "missing"
|
|
2348
2370
|
: modelListUnavailable
|
|
@@ -4947,6 +4969,7 @@ const AgentKitAssistantChatBody = forwardRef<
|
|
|
4947
4969
|
hasRenderedMessages,
|
|
4948
4970
|
canChat,
|
|
4949
4971
|
setupMissing,
|
|
4972
|
+
setupConfirmedMissing,
|
|
4950
4973
|
providerStatus,
|
|
4951
4974
|
modelListUnavailable,
|
|
4952
4975
|
retryProviderStatus,
|
|
@@ -4971,6 +4994,7 @@ const AgentKitAssistantChatBody = forwardRef<
|
|
|
4971
4994
|
authSessionAvailable,
|
|
4972
4995
|
setupBouncePulse,
|
|
4973
4996
|
bounceSetupCard,
|
|
4997
|
+
onComposerSetupRequiredChange: setComposerSetupRequired,
|
|
4974
4998
|
isSubmissionInFlight,
|
|
4975
4999
|
composerSubmissionPending,
|
|
4976
5000
|
onComposerSubmissionPendingChange: setComposerSubmissionPending,
|
|
@@ -5685,6 +5709,7 @@ function AgentKitComposerSurface({
|
|
|
5685
5709
|
showSuggestions,
|
|
5686
5710
|
setupBouncePulse,
|
|
5687
5711
|
bounceSetupCard,
|
|
5712
|
+
onComposerSetupRequiredChange,
|
|
5688
5713
|
contextItems,
|
|
5689
5714
|
providerContextItems,
|
|
5690
5715
|
selectionLength,
|
|
@@ -5723,6 +5748,7 @@ function AgentKitComposerSurface({
|
|
|
5723
5748
|
hasRenderedMessages: boolean;
|
|
5724
5749
|
setupBouncePulse: number;
|
|
5725
5750
|
bounceSetupCard: () => void;
|
|
5751
|
+
onComposerSetupRequiredChange: (required: boolean) => void;
|
|
5726
5752
|
contextItems: AgentChatContextItem[];
|
|
5727
5753
|
providerContextItems: { current: readonly AgentChatContextItem[] };
|
|
5728
5754
|
selectionLength: number | null;
|
|
@@ -5939,12 +5965,6 @@ function AgentKitComposerSurface({
|
|
|
5939
5965
|
},
|
|
5940
5966
|
onError: (error) => setComposerError(error.message),
|
|
5941
5967
|
});
|
|
5942
|
-
const integration = findMcpConnectionSuggestionIntegration({
|
|
5943
|
-
text,
|
|
5944
|
-
variant: "composer",
|
|
5945
|
-
requestedByUser: true,
|
|
5946
|
-
excludedIntegrationIds: props.mcpSuggestionExcludedIntegrationIds,
|
|
5947
|
-
});
|
|
5948
5968
|
const showHomeIntro =
|
|
5949
5969
|
props.homeIntroSlot &&
|
|
5950
5970
|
!hasRenderedMessages &&
|
|
@@ -6039,15 +6059,6 @@ function AgentKitComposerSurface({
|
|
|
6039
6059
|
onRetry={modelListUnavailable ? retryModelList : retryProviderStatus}
|
|
6040
6060
|
/>
|
|
6041
6061
|
) : null}
|
|
6042
|
-
{integration ? (
|
|
6043
|
-
<McpConnectionSuggestion
|
|
6044
|
-
text={text}
|
|
6045
|
-
variant="composer"
|
|
6046
|
-
requestedByUser
|
|
6047
|
-
excludedIntegrationIds={props.mcpSuggestionExcludedIntegrationIds}
|
|
6048
|
-
integrationId={integration.id}
|
|
6049
|
-
/>
|
|
6050
|
-
) : null}
|
|
6051
6062
|
{selectionLength !== null && selectionLength > 0 ? (
|
|
6052
6063
|
<AgentKitSelectionPill
|
|
6053
6064
|
length={selectionLength}
|
|
@@ -6107,6 +6118,7 @@ function AgentKitComposerSurface({
|
|
|
6107
6118
|
initialTextKey={`${props.tabId ?? threadId}:${prefillRevision}`}
|
|
6108
6119
|
showMissingApiKeySetup={false}
|
|
6109
6120
|
setupCardVisible={composerShowsSetupCard({ setupMissing, props })}
|
|
6121
|
+
onModelConnectionRequiredChange={onComposerSetupRequiredChange}
|
|
6110
6122
|
onTextChange={onTextChange}
|
|
6111
6123
|
announcePendingSubmission={!isThinkingVisibleInTranscript}
|
|
6112
6124
|
onSubmissionPendingChange={onComposerSubmissionPendingChange}
|
|
@@ -6308,68 +6320,66 @@ function AgentKitUserMessage(props: AgentKitRenderProps<AgentMessage>) {
|
|
|
6308
6320
|
return <AgentMessageView {...props} value={visible} />;
|
|
6309
6321
|
}
|
|
6310
6322
|
|
|
6311
|
-
interface AgentKitSuggestionContext {
|
|
6312
|
-
latestUser?: AgentMessage | undefined;
|
|
6323
|
+
interface AgentKitSuggestionContext extends LatestReplyContext {
|
|
6313
6324
|
runsActive: boolean;
|
|
6314
6325
|
}
|
|
6315
6326
|
|
|
6316
|
-
const NO_SUGGESTION_CONTEXT: AgentKitSuggestionContext = {
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
const index = messages.findIndex((message) => message.id === messageId);
|
|
6323
|
-
return messages
|
|
6324
|
-
.slice(0, Math.max(0, index))
|
|
6325
|
-
.findLast((message) => message.role === "user");
|
|
6326
|
-
}
|
|
6327
|
+
const NO_SUGGESTION_CONTEXT: AgentKitSuggestionContext = {
|
|
6328
|
+
isLatest: false,
|
|
6329
|
+
userText: "",
|
|
6330
|
+
agentRequestedProviders: "",
|
|
6331
|
+
runsActive: true,
|
|
6332
|
+
};
|
|
6327
6333
|
|
|
6328
6334
|
function AgentKitMessageSupplement(props: AgentKitRenderProps<AgentMessage>) {
|
|
6329
6335
|
const value = props.value;
|
|
6330
6336
|
const t = useT();
|
|
6331
6337
|
const surface = useAgentKitSurface();
|
|
6332
|
-
const
|
|
6333
|
-
value.role === "assistant" && value.status === "complete";
|
|
6338
|
+
const isAssistant = value.role === "assistant";
|
|
6334
6339
|
// Every row renders this, so it reads only what it shows; a whole-thread
|
|
6335
6340
|
// subscription would re-render every row on every streamed delta.
|
|
6336
|
-
const
|
|
6337
|
-
(thread): AgentKitSuggestionContext =>
|
|
6338
|
-
|
|
6339
|
-
?
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
: NO_SUGGESTION_CONTEXT
|
|
6341
|
+
const suggestion = useAgentThreadSelector(
|
|
6342
|
+
(thread): AgentKitSuggestionContext => {
|
|
6343
|
+
const context = isAssistant
|
|
6344
|
+
? latestReplyContext(thread, value.id)
|
|
6345
|
+
: undefined;
|
|
6346
|
+
return context?.isLatest
|
|
6347
|
+
? { ...context, runsActive: hasActiveAgentRuns(thread) }
|
|
6348
|
+
: NO_SUGGESTION_CONTEXT;
|
|
6349
|
+
},
|
|
6344
6350
|
(previous, next) =>
|
|
6345
|
-
previous.
|
|
6351
|
+
previous.isLatest === next.isLatest &&
|
|
6352
|
+
previous.userText === next.userText &&
|
|
6353
|
+
previous.agentRequestedProviders === next.agentRequestedProviders &&
|
|
6346
6354
|
previous.runsActive === next.runsActive,
|
|
6347
6355
|
props.threadId,
|
|
6348
6356
|
);
|
|
6349
|
-
const contextText =
|
|
6350
|
-
latestUser?.parts
|
|
6351
|
-
.filter((part) => part.type === "text")
|
|
6352
|
-
.map((part) =>
|
|
6353
|
-
part.type === "text"
|
|
6354
|
-
? splitAgentChatContextFromMessage(part.text).message
|
|
6355
|
-
: "",
|
|
6356
|
-
)
|
|
6357
|
-
.join("\n") ?? "";
|
|
6358
6357
|
const text = value.parts
|
|
6359
6358
|
.filter((part) => part.type === "text")
|
|
6360
6359
|
.map((part) => (part.type === "text" ? part.text : ""))
|
|
6361
6360
|
.join("\n");
|
|
6362
|
-
const
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6361
|
+
const excludedIntegrationIds =
|
|
6362
|
+
surface.props.mcpSuggestionExcludedIntegrationIds;
|
|
6363
|
+
const replyFinished = value.status === "complete" && !suggestion.runsActive;
|
|
6364
|
+
// The streaming row re-renders on every delta; its ask is read once done.
|
|
6365
|
+
const replyText = replyFinished ? text : "";
|
|
6366
|
+
const integration = useMemo(
|
|
6367
|
+
() =>
|
|
6368
|
+
isAssistant
|
|
6369
|
+
? pickReplyConnectionSuggestion({
|
|
6370
|
+
context: suggestion,
|
|
6371
|
+
replyText,
|
|
6372
|
+
replyFinished,
|
|
6373
|
+
find: (message) =>
|
|
6374
|
+
findMcpConnectionSuggestionIntegration({
|
|
6375
|
+
text: message,
|
|
6376
|
+
variant: "response",
|
|
6377
|
+
...(excludedIntegrationIds ? { excludedIntegrationIds } : {}),
|
|
6378
|
+
}),
|
|
6379
|
+
})
|
|
6380
|
+
: null,
|
|
6381
|
+
[excludedIntegrationIds, isAssistant, replyFinished, replyText, suggestion],
|
|
6382
|
+
);
|
|
6373
6383
|
const runWarning = asRecord(asRecord(value.metadata?.custom)?.runWarning);
|
|
6374
6384
|
const missingFinalResponse =
|
|
6375
6385
|
runWarning?.errorCode === "final_response_missing_after_tool";
|
|
@@ -6401,7 +6411,7 @@ function AgentKitMessageSupplement(props: AgentKitRenderProps<AgentMessage>) {
|
|
|
6401
6411
|
{integration ? (
|
|
6402
6412
|
<McpConnectionSuggestion
|
|
6403
6413
|
text={text}
|
|
6404
|
-
contextText={
|
|
6414
|
+
contextText={suggestion.userText}
|
|
6405
6415
|
variant="response"
|
|
6406
6416
|
requestedByAgent
|
|
6407
6417
|
excludedIntegrationIds={
|
|
@@ -6886,7 +6896,9 @@ function useResumeAfterAiSetup(
|
|
|
6886
6896
|
): () => void {
|
|
6887
6897
|
const surface = useAgentKitSurface();
|
|
6888
6898
|
const sawSetupMissingRef = useRef(false);
|
|
6889
|
-
|
|
6899
|
+
// A resend fires without a fresh user action, so only the server's answer
|
|
6900
|
+
// arms it; the model list's early guess can be briefly wrong.
|
|
6901
|
+
if (surface.setupConfirmedMissing) sawSetupMissingRef.current = true;
|
|
6890
6902
|
const latestRef = useRef({ resendKey, enabled, resend });
|
|
6891
6903
|
latestRef.current = { resendKey, enabled, resend };
|
|
6892
6904
|
const resumed = surface.resumedAfterSetup;
|
|
@@ -58,6 +58,15 @@ describe("formatGuidedAnswersForAgent", () => {
|
|
|
58
58
|
);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
it("keeps skipped questions in the order the user saw them", () => {
|
|
62
|
+
expect(
|
|
63
|
+
formatGuidedAnswersForAgent({ q2: "No" }, [
|
|
64
|
+
{ id: "q1", type: "text-options", question: "Framework?" },
|
|
65
|
+
{ id: "q2", type: "text-options", question: "Auth required?" },
|
|
66
|
+
]),
|
|
67
|
+
).toBe("Q: Framework?\nA: no answer\nQ: Auth required?\nA: No");
|
|
68
|
+
});
|
|
69
|
+
|
|
61
70
|
it("keeps meaningful ids as labels when no question is supplied", () => {
|
|
62
71
|
expect(
|
|
63
72
|
formatGuidedAnswersForAgent({
|
|
@@ -161,18 +161,28 @@ export function formatGuidedAnswersForAgent(
|
|
|
161
161
|
.filter((question) => question.question?.trim())
|
|
162
162
|
.map((question) => [question.id, question.question.trim()] as const),
|
|
163
163
|
);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
164
|
+
const normalized = normalizeGuidedAnswers(answers);
|
|
165
|
+
const declaredIds = new Set((questions ?? []).map((question) => question.id));
|
|
166
|
+
// Declared question order keeps the summary in the order the user saw; skipped questions stay visible so the agent can state its assumption.
|
|
167
|
+
const ids = [...new Set([...declaredIds, ...Object.keys(normalized)])];
|
|
168
|
+
return ids
|
|
169
|
+
.flatMap((id) => {
|
|
170
|
+
const value = Object.hasOwn(normalized, id) ? normalized[id] : undefined;
|
|
168
171
|
const question = questionTextById.get(id);
|
|
169
|
-
|
|
172
|
+
if (!hasGuidedAnswer(value)) {
|
|
173
|
+
if (!declaredIds.has(id)) return [];
|
|
174
|
+
return [question ? `Q: ${question}\nA: no answer` : `${id}: no answer`];
|
|
175
|
+
}
|
|
176
|
+
const answer = Array.isArray(value) ? value.join(", ") : String(value);
|
|
177
|
+
return [question ? `Q: ${question}\nA: ${answer}` : `${id}: ${answer}`];
|
|
170
178
|
})
|
|
171
179
|
.join("\n");
|
|
172
180
|
}
|
|
173
181
|
|
|
174
|
-
const SETTLED_ANSWERS_INSTRUCTION =
|
|
175
|
-
"Treat every question below as settled: do not ask it again, and do not ask for a confirmation of it. Continue the work these answers were blocking."
|
|
182
|
+
export const SETTLED_ANSWERS_INSTRUCTION = [
|
|
183
|
+
"Treat every question below as settled: do not ask it again, and do not ask for a confirmation of it. Continue the work these answers were blocking.",
|
|
184
|
+
"Open your reply with a short summary: one line per question pairing the question with the user's answer in plain words. For a question marked no answer, say so and state the assumption you will use. Then close with one line saying what you will do next.",
|
|
185
|
+
].join(" ");
|
|
176
186
|
|
|
177
187
|
function defaultGuidedSubmitContext(formattedAnswers: string): string {
|
|
178
188
|
return [
|
|
@@ -1247,6 +1247,51 @@ describe("run recovery surfaces", () => {
|
|
|
1247
1247
|
);
|
|
1248
1248
|
});
|
|
1249
1249
|
|
|
1250
|
+
it("hides a failed Builder status read on the card but keeps connect errors", async () => {
|
|
1251
|
+
const renderCard = () => (
|
|
1252
|
+
<AgentNativeI18nProvider
|
|
1253
|
+
initialLocale="en-US"
|
|
1254
|
+
initialPreference="en-US"
|
|
1255
|
+
persistPreference={false}
|
|
1256
|
+
>
|
|
1257
|
+
<BuilderSetupCard />
|
|
1258
|
+
</AgentNativeI18nProvider>
|
|
1259
|
+
);
|
|
1260
|
+
builderFlowMock.state.statusUnavailable = true;
|
|
1261
|
+
builderFlowMock.state.errorKind = "status-read";
|
|
1262
|
+
builderFlowMock.state.error = "Connection status is unavailable.";
|
|
1263
|
+
await act(async () => {
|
|
1264
|
+
root.render(renderCard());
|
|
1265
|
+
});
|
|
1266
|
+
expect(container.textContent).not.toContain(
|
|
1267
|
+
"Connection status is unavailable.",
|
|
1268
|
+
);
|
|
1269
|
+
|
|
1270
|
+
builderFlowMock.state.statusUnavailable = false;
|
|
1271
|
+
builderFlowMock.state.errorKind = "connection";
|
|
1272
|
+
builderFlowMock.state.error = "Builder.io sign-in was cancelled.";
|
|
1273
|
+
await act(async () => {
|
|
1274
|
+
root.render(renderCard());
|
|
1275
|
+
});
|
|
1276
|
+
expect(container.textContent).toContain(
|
|
1277
|
+
"Builder.io sign-in was cancelled.",
|
|
1278
|
+
);
|
|
1279
|
+
|
|
1280
|
+
builderFlowMock.state.statusUnavailable = true;
|
|
1281
|
+
builderFlowMock.state.errorKind = "status-read";
|
|
1282
|
+
builderFlowMock.state.error = "Connection status is unavailable.";
|
|
1283
|
+
builderFlowMock.state.terminalError = "Builder.io sign-in was cancelled.";
|
|
1284
|
+
await act(async () => {
|
|
1285
|
+
root.render(renderCard());
|
|
1286
|
+
});
|
|
1287
|
+
expect(container.textContent).toContain(
|
|
1288
|
+
"Builder.io sign-in was cancelled.",
|
|
1289
|
+
);
|
|
1290
|
+
expect(container.textContent).not.toContain(
|
|
1291
|
+
"Connection status is unavailable.",
|
|
1292
|
+
);
|
|
1293
|
+
});
|
|
1294
|
+
|
|
1250
1295
|
it("records bounded Builder failure outcomes after a setup attempt", async () => {
|
|
1251
1296
|
const renderCard = () => (
|
|
1252
1297
|
<AgentNativeI18nProvider
|
|
@@ -343,7 +343,12 @@ export function BuilderConnectCta({
|
|
|
343
343
|
trackingSource: "assistant_chat_builder_cta",
|
|
344
344
|
onConnected: handleConnected,
|
|
345
345
|
});
|
|
346
|
-
const { configured, orgName, connecting
|
|
346
|
+
const { configured, orgName, connecting } = flow;
|
|
347
|
+
// A failed status read changes nothing here: the card only shows while AI
|
|
348
|
+
// is missing, and connecting re-reads status. Its retry lives in Settings.
|
|
349
|
+
// A newer status read still must not hide the connect failure before it.
|
|
350
|
+
const error =
|
|
351
|
+
flow.errorKind === "status-read" ? flow.terminalError : flow.error;
|
|
347
352
|
useEffect(() => {
|
|
348
353
|
const attempt = connectAttemptRef.current;
|
|
349
354
|
if (!attempt) return;
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "@agent-native/core/client/agent-chat";
|
|
13
13
|
import { useVoiceProviderStatus } from "@agent-native/core/client/agent-chat";
|
|
14
14
|
import { SIDEBAR_STATE_CHANGE_EVENT } from "@agent-native/core/client/agent-chat";
|
|
15
|
+
import { trackEvent } from "@agent-native/core/client/analytics";
|
|
15
16
|
import {
|
|
16
17
|
appPath,
|
|
17
18
|
frameworkRoutePrefix,
|
|
@@ -103,6 +104,7 @@ export const coreComposerAdapters: CoreComposerRuntimeAdapters = {
|
|
|
103
104
|
}),
|
|
104
105
|
subscribeSidebarState,
|
|
105
106
|
applyContextReplacements: applyVoiceContextReplacements,
|
|
107
|
+
trackEvent,
|
|
106
108
|
},
|
|
107
109
|
};
|
|
108
110
|
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentConnectionRequest,
|
|
3
|
+
AgentMessage,
|
|
4
|
+
} from "@agent-native/agentkit/protocol";
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
latestReplyContext,
|
|
9
|
+
pickReplyConnectionSuggestion,
|
|
10
|
+
type LatestReplyContext,
|
|
11
|
+
} from "./reply-connection-suggestion.js";
|
|
12
|
+
|
|
13
|
+
function message(id: string, role: "user" | "assistant", text: string) {
|
|
14
|
+
return {
|
|
15
|
+
id,
|
|
16
|
+
role,
|
|
17
|
+
status: "complete",
|
|
18
|
+
parts: [{ type: "text", text }],
|
|
19
|
+
} as unknown as AgentMessage;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const slack = { id: "slack", provider: "slack", name: "Slack" };
|
|
23
|
+
const notion = { id: "notion", provider: "notion", name: "Notion" };
|
|
24
|
+
const find = (text: string) =>
|
|
25
|
+
/Slack/.test(text) ? slack : /Notion/.test(text) ? notion : null;
|
|
26
|
+
|
|
27
|
+
const latest: LatestReplyContext = {
|
|
28
|
+
isLatest: true,
|
|
29
|
+
userText: "Connect Slack",
|
|
30
|
+
agentRequestedProviders: "",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
describe("latestReplyContext", () => {
|
|
34
|
+
const messages = [
|
|
35
|
+
message("u1", "user", "Connect Slack"),
|
|
36
|
+
message("a1", "assistant", "Working on it."),
|
|
37
|
+
message("u2", "user", "Connect Notion"),
|
|
38
|
+
message("a2", "assistant", "Still working."),
|
|
39
|
+
message("a3", "assistant", "Done."),
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
it("offers a connection only on the thread's newest message", () => {
|
|
43
|
+
const thread = { messages, connectionRequests: {} };
|
|
44
|
+
expect(latestReplyContext(thread, "a1").isLatest).toBe(false);
|
|
45
|
+
expect(latestReplyContext(thread, "a2").isLatest).toBe(false);
|
|
46
|
+
expect(latestReplyContext(thread, "a3")).toMatchObject({
|
|
47
|
+
isLatest: true,
|
|
48
|
+
userText: "Connect Notion",
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("lists every provider the agent asked for, settled or not", () => {
|
|
53
|
+
const request = (
|
|
54
|
+
provider: string,
|
|
55
|
+
status: AgentConnectionRequest["status"],
|
|
56
|
+
) => ({ id: provider, provider, reason: "missing", status }) as const;
|
|
57
|
+
const context = latestReplyContext(
|
|
58
|
+
{
|
|
59
|
+
messages,
|
|
60
|
+
connectionRequests: {
|
|
61
|
+
one: request("Slack", "requested"),
|
|
62
|
+
two: request("notion", "connected"),
|
|
63
|
+
three: request("linear", "declined"),
|
|
64
|
+
} as Record<string, AgentConnectionRequest>,
|
|
65
|
+
},
|
|
66
|
+
"a3",
|
|
67
|
+
);
|
|
68
|
+
expect(context.agentRequestedProviders).toBe("linear\nnotion\nslack");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("pickReplyConnectionSuggestion", () => {
|
|
73
|
+
const pick = (
|
|
74
|
+
context: LatestReplyContext,
|
|
75
|
+
replyText: string,
|
|
76
|
+
replyFinished: boolean,
|
|
77
|
+
) =>
|
|
78
|
+
pickReplyConnectionSuggestion({ context, replyText, replyFinished, find });
|
|
79
|
+
|
|
80
|
+
it("offers nothing until the reply finishes", () => {
|
|
81
|
+
expect(pick(latest, "Connect Notion there.", false)).toBeNull();
|
|
82
|
+
expect(pick(latest, "", true)).toBe(slack);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("never offers anything on an older reply", () => {
|
|
86
|
+
expect(
|
|
87
|
+
pick({ ...latest, isLatest: false }, "Connect Notion.", true),
|
|
88
|
+
).toBeNull();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("prefers the finished reply's ask over the sent request", () => {
|
|
92
|
+
expect(pick(latest, "Connect Notion to file it.", true)).toBe(notion);
|
|
93
|
+
expect(pick(latest, "Posted it.", true)).toBe(slack);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("matches the agent's request by brand name, not only catalog id", () => {
|
|
97
|
+
const jira = { id: "atlassian", provider: "atlassian", name: "Jira" };
|
|
98
|
+
expect(
|
|
99
|
+
pickReplyConnectionSuggestion({
|
|
100
|
+
context: {
|
|
101
|
+
...latest,
|
|
102
|
+
userText: "Connect Jira",
|
|
103
|
+
agentRequestedProviders: "jira",
|
|
104
|
+
},
|
|
105
|
+
replyText: "",
|
|
106
|
+
replyFinished: true,
|
|
107
|
+
find: () => jira,
|
|
108
|
+
}),
|
|
109
|
+
).toBeNull();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("never duplicates the agent's own connection request card", () => {
|
|
113
|
+
expect(
|
|
114
|
+
pick(
|
|
115
|
+
{ ...latest, agentRequestedProviders: "slack" },
|
|
116
|
+
"Connect Slack.",
|
|
117
|
+
true,
|
|
118
|
+
),
|
|
119
|
+
).toBeNull();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { AgentThreadState } from "@agent-native/agentkit";
|
|
2
|
+
import { splitAgentChatContextFromMessage } from "@agent-native/core/shared";
|
|
3
|
+
|
|
4
|
+
export interface LatestReplyContext {
|
|
5
|
+
/** Only the thread's newest message offers a connection. */
|
|
6
|
+
isLatest: boolean;
|
|
7
|
+
/** The user message it replies to; a connect started here resumes it. */
|
|
8
|
+
userText: string;
|
|
9
|
+
/** Providers the agent has asked to connect in this thread, one per line. */
|
|
10
|
+
agentRequestedProviders: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Primitives only: a thread selector compares this on every streamed delta.
|
|
15
|
+
*/
|
|
16
|
+
export function latestReplyContext(
|
|
17
|
+
thread: Pick<AgentThreadState, "messages" | "connectionRequests">,
|
|
18
|
+
messageId: string,
|
|
19
|
+
): LatestReplyContext {
|
|
20
|
+
if (thread.messages.at(-1)?.id !== messageId) {
|
|
21
|
+
return { isLatest: false, userText: "", agentRequestedProviders: "" };
|
|
22
|
+
}
|
|
23
|
+
const user = thread.messages.findLast((message) => message.role === "user");
|
|
24
|
+
const userText = (user?.parts ?? [])
|
|
25
|
+
.flatMap((part) =>
|
|
26
|
+
part.type === "text"
|
|
27
|
+
? [splitAgentChatContextFromMessage(part.text).message]
|
|
28
|
+
: [],
|
|
29
|
+
)
|
|
30
|
+
.join("\n");
|
|
31
|
+
// Settled requests count too: one connected through a workspace connection
|
|
32
|
+
// or declined must not come back as an MCP card.
|
|
33
|
+
const agentRequestedProviders = [
|
|
34
|
+
...new Set(
|
|
35
|
+
Object.values(thread.connectionRequests).map((request) =>
|
|
36
|
+
request.provider.trim().toLowerCase(),
|
|
37
|
+
),
|
|
38
|
+
),
|
|
39
|
+
]
|
|
40
|
+
.sort()
|
|
41
|
+
.join("\n");
|
|
42
|
+
return { isLatest: true, userText, agentRequestedProviders };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* One card, on the newest reply once it finishes: the reply's own ask, else
|
|
47
|
+
* what the user asked to connect. Never mid-run: a connect resumes the user's
|
|
48
|
+
* prompt, which would send it twice. Never beside the agent's own request card.
|
|
49
|
+
*/
|
|
50
|
+
export function pickReplyConnectionSuggestion<
|
|
51
|
+
T extends { id: string; provider: string; name: string },
|
|
52
|
+
>({
|
|
53
|
+
context,
|
|
54
|
+
replyText,
|
|
55
|
+
replyFinished,
|
|
56
|
+
find,
|
|
57
|
+
}: {
|
|
58
|
+
context: LatestReplyContext;
|
|
59
|
+
replyText: string;
|
|
60
|
+
replyFinished: boolean;
|
|
61
|
+
find: (text: string) => T | null;
|
|
62
|
+
}): T | null {
|
|
63
|
+
if (!context.isLatest || !replyFinished) return null;
|
|
64
|
+
const agentRequested = new Set(
|
|
65
|
+
context.agentRequestedProviders.split("\n").filter(Boolean),
|
|
66
|
+
);
|
|
67
|
+
const offered = (integration: T | null) =>
|
|
68
|
+
integration &&
|
|
69
|
+
!agentRequested.has(integration.id.toLowerCase()) &&
|
|
70
|
+
!agentRequested.has(integration.provider.toLowerCase()) &&
|
|
71
|
+
// A Jira request names the brand; the catalog id is "atlassian".
|
|
72
|
+
!agentRequested.has(integration.name.toLowerCase())
|
|
73
|
+
? integration
|
|
74
|
+
: null;
|
|
75
|
+
return offered(find(replyText)) ?? offered(find(context.userText));
|
|
76
|
+
}
|
|
@@ -130,6 +130,8 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
130
130
|
"onboarding.roleOther": "أخرى",
|
|
131
131
|
"onboarding.roleOtherInputLabel": "صف دورك",
|
|
132
132
|
"onboarding.skipForNow": "تخطي الآن",
|
|
133
|
+
"onboarding.builderProvisionFailedDetail":
|
|
134
|
+
"تعذّر إنشاء حساب Builder.io الخاص بك. حاول مرة أخرى أو سجّل الدخول بحساب موجود.",
|
|
133
135
|
"onboarding.saveRoleError": "تعذر حفظ دورك.",
|
|
134
136
|
"onboarding.builderCreateAccount": "استخدم Builder.io",
|
|
135
137
|
"onboarding.builderSignInWithAccount": "سجّل الدخول بحساب Builder.io",
|
|
@@ -186,6 +188,7 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
186
188
|
"onboarding.creditsSkipGoBack": "رجوع",
|
|
187
189
|
"onboarding.creditsSkipAnyway": "تخطي على أي حال",
|
|
188
190
|
"onboarding.creditsUseCustomKeys": "استخدام مفاتيحك الخاصة",
|
|
191
|
+
"onboarding.creditsUseOwnApiKeys": "استخدم مفاتيح API الخاصة بي",
|
|
189
192
|
"onboarding.creditsAboutLabel": "حول الأرصدة المجانية",
|
|
190
193
|
"onboarding.creditsActivating": "جارٍ تفعيل الأرصدة المجانية",
|
|
191
194
|
"onboarding.creditsActivatingTooltip":
|
|
@@ -411,6 +414,9 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
411
414
|
"تعذّرت إضافة الملف الذي تم إفلاته. جرّب تنسيقًا آخر.",
|
|
412
415
|
"composer.openDesktop": "افتح تطبيق سطح المكتب لاستخدام هذه المحادثة.",
|
|
413
416
|
"composer.removeAttachment": "إزالة {{name}}",
|
|
417
|
+
"composer.attachmentReadFailed": "تعذّرت قراءة هذا الملف",
|
|
418
|
+
"composer.attachmentTooLarge": "حجم الملف أكبر من 8 ميغابايت",
|
|
419
|
+
"composer.attachmentNotUtf8": "الملف ليس نصًا بترميز UTF-8",
|
|
414
420
|
"composer.scrollToBottom": "التمرير إلى الأسفل",
|
|
415
421
|
"composer.suggestedPrompts": "اقتراحات المطالبات",
|
|
416
422
|
"composer.stopResponse": "إيقاف الرد",
|
|
@@ -533,6 +539,10 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
533
539
|
"composer.skill.uploadFile": "رفع ملف مهارة",
|
|
534
540
|
"composer.upload": "رفع",
|
|
535
541
|
"composer.uploadFailed": "تعذّر رفع الملف المحدد.",
|
|
542
|
+
"composer.imageTooLargeToAttach":
|
|
543
|
+
'"{{name}}" بحجم {{size}} ميغابايت، ولا يمكن إرفاق الصور التي يزيد حجمها على {{limit}} ميغابايت. يُرجى استخدام صورة أصغر.',
|
|
544
|
+
"composer.imageTooManyPixelsToAttach":
|
|
545
|
+
'"{{name}}" بدقة {{megapixels}} ميغابكسل، ولا يمكن إرفاق الصور التي تزيد على {{limit}} ميغابكسل. يُرجى استخدام صورة أصغر.',
|
|
536
546
|
"composer.fileTooLarge":
|
|
537
547
|
"هذا الملف أكبر من حد الرفع البالغ {{size}} ميغابايت.",
|
|
538
548
|
"composer.sessionExpired":
|
|
@@ -136,6 +136,8 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
136
136
|
"onboarding.roleOther": "Andere",
|
|
137
137
|
"onboarding.roleOtherInputLabel": "Beschreibe deine Rolle",
|
|
138
138
|
"onboarding.skipForNow": "Vorerst überspringen",
|
|
139
|
+
"onboarding.builderProvisionFailedDetail":
|
|
140
|
+
"Dein Builder.io-Konto konnte nicht erstellt werden. Versuche es erneut oder melde dich mit einem bestehenden Konto an.",
|
|
139
141
|
"onboarding.saveRoleError": "Deine Rolle konnte nicht gespeichert werden.",
|
|
140
142
|
"onboarding.builderCreateAccount": "Builder.io verwenden",
|
|
141
143
|
"onboarding.builderSignInWithAccount": "Mit einem Builder.io-Konto anmelden",
|
|
@@ -198,6 +200,7 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
198
200
|
"onboarding.creditsSkipGoBack": "Zurück",
|
|
199
201
|
"onboarding.creditsSkipAnyway": "Trotzdem überspringen",
|
|
200
202
|
"onboarding.creditsUseCustomKeys": "Eigene Schlüssel verwenden",
|
|
203
|
+
"onboarding.creditsUseOwnApiKeys": "Meine eigenen API-Schlüssel verwenden",
|
|
201
204
|
"onboarding.creditsAboutLabel": "Über kostenlose Credits",
|
|
202
205
|
"onboarding.creditsActivating": "Gratis-Credits werden aktiviert",
|
|
203
206
|
"onboarding.creditsActivatingTooltip":
|
|
@@ -438,6 +441,9 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
438
441
|
"Die abgelegte Datei konnte nicht hinzugefügt werden. Versuche ein anderes Format.",
|
|
439
442
|
"composer.openDesktop": "Öffne Desktop, um diesen Chat zu verwenden.",
|
|
440
443
|
"composer.removeAttachment": "{{name}} entfernen",
|
|
444
|
+
"composer.attachmentReadFailed": "Datei konnte nicht gelesen werden",
|
|
445
|
+
"composer.attachmentTooLarge": "Datei ist größer als 8 MB",
|
|
446
|
+
"composer.attachmentNotUtf8": "Datei ist kein UTF-8-Text",
|
|
441
447
|
"composer.scrollToBottom": "Nach unten scrollen",
|
|
442
448
|
"composer.suggestedPrompts": "Vorgeschlagene Prompts",
|
|
443
449
|
"composer.stopResponse": "Antwort stoppen",
|
|
@@ -1257,6 +1263,10 @@ const messages: ToolkitAgentChatTranslation = {
|
|
|
1257
1263
|
"composer.upload": "Hochladen",
|
|
1258
1264
|
"composer.uploadFailed":
|
|
1259
1265
|
"Die ausgewählte Datei konnte nicht hochgeladen werden.",
|
|
1266
|
+
"composer.imageTooLargeToAttach":
|
|
1267
|
+
'"{{name}}" ist {{size}} MB groß - Bilder über {{limit}} MB können nicht angehängt werden. Bitte verwende ein kleineres Bild.',
|
|
1268
|
+
"composer.imageTooManyPixelsToAttach":
|
|
1269
|
+
'"{{name}}" hat {{megapixels}} Megapixel - Bilder über {{limit}} Megapixel können nicht angehängt werden. Bitte verwende ein kleineres Bild.',
|
|
1260
1270
|
"composer.fileTooLarge":
|
|
1261
1271
|
"Diese Datei ist größer als das Upload-Limit von {{size}} MB.",
|
|
1262
1272
|
"composer.sessionExpired":
|