@agent-native/toolkit 0.205.0 → 0.206.0-nightly-20261009011246
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/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +17 -9
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/auth/AuthPage.d.ts.map +1 -1
- package/dist/app/auth/AuthPage.js +11 -0
- package/dist/app/auth/AuthPage.js.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.d.ts +2 -0
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +24 -14
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/AgentPanel.d.ts.map +1 -1
- package/dist/app/chat/AgentPanel.js +3 -2
- package/dist/app/chat/AgentPanel.js.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.d.ts +4 -1
- package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.js +61 -3
- package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
- package/dist/app/chat/agentkit-chat/approval-card.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/approval-card.js +2 -1
- package/dist/app/chat/agentkit-chat/approval-card.js.map +1 -1
- package/dist/app/chat/chat/run-recovery.d.ts.map +1 -1
- package/dist/app/chat/chat/run-recovery.js +74 -8
- package/dist/app/chat/chat/run-recovery.js.map +1 -1
- package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
- package/dist/app/chat/chat/tool-call-display.js +6 -4
- package/dist/app/chat/chat/tool-call-display.js.map +1 -1
- package/dist/app/chat/chat-first/ChatFirstAgentActivityPanel.d.ts.map +1 -1
- package/dist/app/chat/chat-first/ChatFirstAgentActivityPanel.js +2 -1
- package/dist/app/chat/chat-first/ChatFirstAgentActivityPanel.js.map +1 -1
- package/dist/app/chat/conversation/AgentConversation.d.ts.map +1 -1
- package/dist/app/chat/conversation/AgentConversation.js +10 -7
- package/dist/app/chat/conversation/AgentConversation.js.map +1 -1
- package/dist/app/chat/fallback-chat-title.d.ts.map +1 -1
- package/dist/app/chat/fallback-chat-title.js +2 -4
- package/dist/app/chat/fallback-chat-title.js.map +1 -1
- package/dist/app/chat/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/app/chat/mcp-apps/McpAppRenderer.js +3 -2
- package/dist/app/chat/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/app/chat/session-replay-privacy.d.ts +7 -0
- package/dist/app/chat/session-replay-privacy.d.ts.map +1 -0
- package/dist/app/chat/session-replay-privacy.js +6 -0
- package/dist/app/chat/session-replay-privacy.js.map +1 -0
- package/dist/app/extensions/ExtensionViewer.d.ts +4 -1
- package/dist/app/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/app/extensions/ExtensionViewer.js +3 -3
- package/dist/app/extensions/ExtensionViewer.js.map +1 -1
- package/dist/app/extensions/ExtensionViewerPage.d.ts +4 -1
- package/dist/app/extensions/ExtensionViewerPage.d.ts.map +1 -1
- package/dist/app/extensions/ExtensionViewerPage.js +2 -2
- package/dist/app/extensions/ExtensionViewerPage.js.map +1 -1
- package/dist/app/onboarding/FirstRunOnboarding.d.ts.map +1 -1
- package/dist/app/onboarding/FirstRunOnboarding.js +53 -12
- package/dist/app/onboarding/FirstRunOnboarding.js.map +1 -1
- package/dist/app/onboarding/OnboardingPanel.d.ts.map +1 -1
- package/dist/app/onboarding/OnboardingPanel.js +14 -4
- package/dist/app/onboarding/OnboardingPanel.js.map +1 -1
- package/dist/app/resources/ResourceEditor.d.ts +4 -1
- package/dist/app/resources/ResourceEditor.d.ts.map +1 -1
- package/dist/app/resources/ResourceEditor.js +18 -7
- package/dist/app/resources/ResourceEditor.js.map +1 -1
- package/dist/app/resources/ResourcesPanel.d.ts +4 -1
- package/dist/app/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/app/resources/ResourcesPanel.js +18 -18
- package/dist/app/resources/ResourcesPanel.js.map +1 -1
- package/dist/app/resources/custom-agent-model-options.d.ts +16 -0
- package/dist/app/resources/custom-agent-model-options.d.ts.map +1 -0
- package/dist/app/resources/custom-agent-model-options.js +53 -0
- package/dist/app/resources/custom-agent-model-options.js.map +1 -0
- package/dist/app/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/app/settings/SettingsPanel.js +34 -3
- package/dist/app/settings/SettingsPanel.js.map +1 -1
- package/dist/app/settings/SettingsTabsPage.d.ts.map +1 -1
- package/dist/app/settings/SettingsTabsPage.js +2 -0
- package/dist/app/settings/SettingsTabsPage.js.map +1 -1
- package/dist/app/settings/api-keys/ApiKeyDialogs.d.ts.map +1 -1
- package/dist/app/settings/api-keys/ApiKeyDialogs.js +49 -9
- package/dist/app/settings/api-keys/ApiKeyDialogs.js.map +1 -1
- package/dist/app/settings/api-keys/ApiKeysSettingsPage.js +2 -1
- package/dist/app/settings/api-keys/ApiKeysSettingsPage.js.map +1 -1
- package/dist/app/settings/model/ModelSettingsPage.d.ts.map +1 -1
- package/dist/app/settings/model/ModelSettingsPage.js +43 -9
- package/dist/app/settings/model/ModelSettingsPage.js.map +1 -1
- package/dist/app/settings/model/ProviderDialog.d.ts +4 -0
- package/dist/app/settings/model/ProviderDialog.d.ts.map +1 -1
- package/dist/app/settings/model/ProviderDialog.js +107 -11
- package/dist/app/settings/model/ProviderDialog.js.map +1 -1
- package/dist/app/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/app/settings/useBuilderStatus.js +13 -10
- package/dist/app/settings/useBuilderStatus.js.map +1 -1
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +51 -13
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/package.json +4 -4
- package/src/app/agentkit/react/components.interaction.spec.tsx +229 -1
- package/src/app/agentkit/react/components.spec.tsx +42 -1
- package/src/app/agentkit/react/components.tsx +46 -12
- package/src/app/auth/AuthPage.tsx +13 -0
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +116 -5
- package/src/app/chat/AgentKitAssistantChat.tsx +41 -20
- package/src/app/chat/AgentPanel.tsx +5 -1
- package/src/app/chat/MultiTabAssistantChat.spec.tsx +152 -7
- package/src/app/chat/MultiTabAssistantChat.tsx +71 -4
- package/src/app/chat/agentkit-chat/approval-card.spec.tsx +17 -0
- package/src/app/chat/agentkit-chat/approval-card.tsx +2 -0
- package/src/app/chat/chat/run-recovery.spec.tsx +354 -9
- package/src/app/chat/chat/run-recovery.tsx +178 -10
- package/src/app/chat/chat/tool-call-display.spec.tsx +77 -0
- package/src/app/chat/chat/tool-call-display.tsx +23 -6
- package/src/app/chat/chat-first/ChatFirstAgentActivityPanel.tsx +2 -1
- package/src/app/chat/conversation/AgentConversation.spec.tsx +190 -1
- package/src/app/chat/conversation/AgentConversation.tsx +32 -8
- package/src/app/chat/fallback-chat-title.spec.ts +44 -0
- package/src/app/chat/fallback-chat-title.ts +3 -4
- package/src/app/chat/mcp-apps/McpAppRenderer.spec.ts +24 -0
- package/src/app/chat/mcp-apps/McpAppRenderer.tsx +10 -2
- package/src/app/chat/session-replay-privacy.spec.ts +14 -0
- package/src/app/chat/session-replay-privacy.ts +5 -0
- package/src/app/extensions/ExtensionViewer.tsx +15 -3
- package/src/app/extensions/ExtensionViewerPage.tsx +7 -3
- package/src/app/onboarding/FirstRunOnboarding.spec.tsx +252 -2
- package/src/app/onboarding/FirstRunOnboarding.tsx +56 -9
- package/src/app/onboarding/OnboardingPanel.save-scope.spec.tsx +1 -0
- package/src/app/onboarding/OnboardingPanel.tsx +15 -3
- package/src/app/resources/ResourceEditor.spec.tsx +240 -5
- package/src/app/resources/ResourceEditor.tsx +57 -10
- package/src/app/resources/ResourcesPanel.spec.ts +86 -4
- package/src/app/resources/ResourcesPanel.tsx +49 -16
- package/src/app/resources/custom-agent-model-options.ts +92 -0
- package/src/app/settings/SettingsPanel.model-defaults.spec.tsx +26 -13
- package/src/app/settings/SettingsPanel.tsx +35 -3
- package/src/app/settings/SettingsTabsPage.spec.tsx +97 -0
- package/src/app/settings/SettingsTabsPage.tsx +2 -0
- package/src/app/settings/api-keys/ApiKeyDialogs.tsx +84 -22
- package/src/app/settings/api-keys/ApiKeysSettingsPage.spec.tsx +229 -1
- package/src/app/settings/api-keys/ApiKeysSettingsPage.tsx +3 -2
- package/src/app/settings/model/ModelSettingsPage.spec.tsx +99 -9
- package/src/app/settings/model/ModelSettingsPage.tsx +79 -11
- package/src/app/settings/model/ProviderDialog.spec.tsx +192 -1
- package/src/app/settings/model/ProviderDialog.tsx +225 -29
- package/src/app/settings/useBuilderStatus.spec.tsx +49 -0
- package/src/app/settings/useBuilderStatus.ts +16 -10
- package/src/composer/TiptapComposer.spec.ts +97 -2
- package/src/composer/TiptapComposer.tsx +67 -13
|
@@ -8,8 +8,17 @@ import {
|
|
|
8
8
|
getAgentProviderOption,
|
|
9
9
|
type AgentProviderId,
|
|
10
10
|
} from "@agent-native/core/client/agent-provider-catalog";
|
|
11
|
+
import { injectedAgentNativeAppId } from "@agent-native/core/client/app-config";
|
|
11
12
|
import { callAction, useActionQuery } from "@agent-native/core/client/hooks";
|
|
12
13
|
import { useFormatters, useT } from "@agent-native/core/client/i18n";
|
|
14
|
+
import {
|
|
15
|
+
requestCustomKeyOnboardingAbandonment,
|
|
16
|
+
setCustomKeyOnboardingSetupKind,
|
|
17
|
+
trackOnboardingEvent,
|
|
18
|
+
trackCustomKeyOnboardingOutcome,
|
|
19
|
+
withCustomKeyOnboardingCredentialSave,
|
|
20
|
+
withCustomKeyOnboardingLocalEndpointSave,
|
|
21
|
+
} from "@agent-native/core/client/onboarding/use-onboarding";
|
|
13
22
|
import { useOrg } from "@agent-native/core/client/org";
|
|
14
23
|
import { Alert, AlertDescription } from "@agent-native/toolkit/ui/alert";
|
|
15
24
|
import { Button } from "@agent-native/toolkit/ui/button";
|
|
@@ -40,9 +49,17 @@ import {
|
|
|
40
49
|
IconServer,
|
|
41
50
|
} from "@tabler/icons-react";
|
|
42
51
|
import { useQueryClient } from "@tanstack/react-query";
|
|
43
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
useCallback,
|
|
54
|
+
useEffect,
|
|
55
|
+
useId,
|
|
56
|
+
useMemo,
|
|
57
|
+
useRef,
|
|
58
|
+
useState,
|
|
59
|
+
} from "react";
|
|
44
60
|
|
|
45
61
|
import { BrandLogo } from "../infra/logos.js";
|
|
62
|
+
import { currentTemplateId } from "../shell/app-identity.js";
|
|
46
63
|
import { WhoField } from "../WhoField.js";
|
|
47
64
|
import {
|
|
48
65
|
addDialogChoices,
|
|
@@ -82,10 +99,73 @@ const PROVIDER_LOGO_IDS: Record<AgentProviderId, string | null> = {
|
|
|
82
99
|
*/
|
|
83
100
|
export type ProviderDialogMode = "add" | "manage" | "add-from-service";
|
|
84
101
|
|
|
102
|
+
type ProviderSetupTrackingFlow = "chat_setup" | "settings";
|
|
103
|
+
type ProviderSetupEventName =
|
|
104
|
+
| "integration_key_entry_started"
|
|
105
|
+
| "integration_key_validation_outcome"
|
|
106
|
+
| "integration_key_save_outcome";
|
|
107
|
+
type ProviderSetupOutcome =
|
|
108
|
+
| "started"
|
|
109
|
+
| "accepted"
|
|
110
|
+
| "rejected"
|
|
111
|
+
| "missing_key"
|
|
112
|
+
| "invalid_endpoint"
|
|
113
|
+
| "unreachable"
|
|
114
|
+
| "provider_error"
|
|
115
|
+
| "error"
|
|
116
|
+
| "saved"
|
|
117
|
+
| "failed";
|
|
118
|
+
|
|
119
|
+
function setupTelemetryAppName(): string {
|
|
120
|
+
const appId = injectedAgentNativeAppId() ?? currentTemplateId();
|
|
121
|
+
return appId && /^[a-z0-9][a-z0-9-]{0,63}$/.test(appId)
|
|
122
|
+
? appId.replace(/^agent-native-/, "") || "framework"
|
|
123
|
+
: "framework";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function trackProviderSetupEvent(
|
|
127
|
+
flow: ProviderSetupTrackingFlow,
|
|
128
|
+
eventName: ProviderSetupEventName,
|
|
129
|
+
action: "enter" | "validate" | "save",
|
|
130
|
+
outcome: ProviderSetupOutcome,
|
|
131
|
+
): void {
|
|
132
|
+
trackOnboardingEvent(eventName, {
|
|
133
|
+
flow,
|
|
134
|
+
app_name: setupTelemetryAppName(),
|
|
135
|
+
step_id: "connect_ai",
|
|
136
|
+
method_id: "custom_keys",
|
|
137
|
+
action,
|
|
138
|
+
outcome,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function providerCheckOutcome(
|
|
143
|
+
result: ProviderModelsCheck,
|
|
144
|
+
): ProviderSetupOutcome {
|
|
145
|
+
if (result.ok) return "accepted";
|
|
146
|
+
switch (result.code) {
|
|
147
|
+
case "rejected":
|
|
148
|
+
case "wrong-provider":
|
|
149
|
+
return "rejected";
|
|
150
|
+
case "missing-key":
|
|
151
|
+
return "missing_key";
|
|
152
|
+
case "invalid-endpoint":
|
|
153
|
+
return "invalid_endpoint";
|
|
154
|
+
case "unreachable":
|
|
155
|
+
return "unreachable";
|
|
156
|
+
case "provider-error":
|
|
157
|
+
return "provider_error";
|
|
158
|
+
default:
|
|
159
|
+
return "error";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
85
163
|
export interface ProviderDialogProps {
|
|
86
164
|
open: boolean;
|
|
87
165
|
onOpenChange: (open: boolean) => void;
|
|
88
166
|
mode: ProviderDialogMode;
|
|
167
|
+
/** Preserve the chat setup handoff through the first provider dialog. */
|
|
168
|
+
trackingFlow?: ProviderSetupTrackingFlow;
|
|
89
169
|
/** Required for `manage` and `add-from-service`; the first choice for `add`. */
|
|
90
170
|
provider?: AgentProviderId;
|
|
91
171
|
/** `manage`: which saved key, the personal (`user`) or organization (`org`) one. */
|
|
@@ -134,14 +214,58 @@ function unique(models: readonly string[]): string[] {
|
|
|
134
214
|
* the checked models in one step.
|
|
135
215
|
*/
|
|
136
216
|
export function ProviderDialog(props: ProviderDialogProps) {
|
|
217
|
+
const savePending = useRef(false);
|
|
218
|
+
const selectedProvider = useRef<AgentProviderId>(
|
|
219
|
+
props.provider ?? props.providers?.[0] ?? "anthropic",
|
|
220
|
+
);
|
|
221
|
+
const reportSelectedProvider = useCallback((provider: AgentProviderId) => {
|
|
222
|
+
selectedProvider.current = provider;
|
|
223
|
+
setCustomKeyOnboardingSetupKind(
|
|
224
|
+
provider === "ollama" ? "local_endpoint" : "credential",
|
|
225
|
+
);
|
|
226
|
+
}, []);
|
|
227
|
+
const dismiss = () => {
|
|
228
|
+
if (selectedProvider.current === "ollama" && !savePending.current) {
|
|
229
|
+
trackCustomKeyOnboardingOutcome("local_endpoint_skipped");
|
|
230
|
+
} else if (savePending.current) {
|
|
231
|
+
requestCustomKeyOnboardingAbandonment();
|
|
232
|
+
} else {
|
|
233
|
+
trackCustomKeyOnboardingOutcome("credential_skipped");
|
|
234
|
+
}
|
|
235
|
+
props.onOpenChange(false);
|
|
236
|
+
};
|
|
237
|
+
|
|
137
238
|
return (
|
|
138
|
-
<Dialog
|
|
139
|
-
{props.open
|
|
239
|
+
<Dialog
|
|
240
|
+
open={props.open}
|
|
241
|
+
onOpenChange={(open) => {
|
|
242
|
+
if (open) props.onOpenChange(true);
|
|
243
|
+
else dismiss();
|
|
244
|
+
}}
|
|
245
|
+
>
|
|
246
|
+
{props.open ? (
|
|
247
|
+
<ProviderDialogContent
|
|
248
|
+
{...props}
|
|
249
|
+
onDismiss={dismiss}
|
|
250
|
+
onProviderChange={reportSelectedProvider}
|
|
251
|
+
onSavingChange={(saving) => {
|
|
252
|
+
savePending.current = saving;
|
|
253
|
+
}}
|
|
254
|
+
/>
|
|
255
|
+
) : null}
|
|
140
256
|
</Dialog>
|
|
141
257
|
);
|
|
142
258
|
}
|
|
143
259
|
|
|
144
|
-
|
|
260
|
+
interface ProviderDialogInternalProps {
|
|
261
|
+
onDismiss: () => void;
|
|
262
|
+
onProviderChange: (provider: AgentProviderId) => void;
|
|
263
|
+
onSavingChange: (saving: boolean) => void;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function ProviderDialogContent(
|
|
267
|
+
props: ProviderDialogProps & ProviderDialogInternalProps,
|
|
268
|
+
) {
|
|
145
269
|
const t = useT();
|
|
146
270
|
const listing = useActionQuery<ModelProvidersListing>(
|
|
147
271
|
"list-model-providers" as never,
|
|
@@ -209,7 +333,7 @@ function ProviderDialogContent(props: ProviderDialogProps) {
|
|
|
209
333
|
);
|
|
210
334
|
}
|
|
211
335
|
|
|
212
|
-
interface FormProps extends ProviderDialogProps {
|
|
336
|
+
interface FormProps extends ProviderDialogProps, ProviderDialogInternalProps {
|
|
213
337
|
title: string;
|
|
214
338
|
listing: ModelProvidersListing;
|
|
215
339
|
models: ProviderModelsRead;
|
|
@@ -217,11 +341,15 @@ interface FormProps extends ProviderDialogProps {
|
|
|
217
341
|
|
|
218
342
|
function ProviderDialogForm({
|
|
219
343
|
mode,
|
|
344
|
+
trackingFlow = "settings",
|
|
220
345
|
provider: requestedProvider,
|
|
221
346
|
scope: requestedScope,
|
|
222
347
|
serviceLabel,
|
|
223
348
|
providers: providerChoices,
|
|
224
349
|
onOpenChange,
|
|
350
|
+
onDismiss,
|
|
351
|
+
onProviderChange,
|
|
352
|
+
onSavingChange,
|
|
225
353
|
onSaved,
|
|
226
354
|
onRemoved,
|
|
227
355
|
title,
|
|
@@ -286,6 +414,7 @@ function ProviderDialogForm({
|
|
|
286
414
|
);
|
|
287
415
|
const [keyValue, setKeyValue] = useState("");
|
|
288
416
|
const [keyError, setKeyError] = useState(false);
|
|
417
|
+
const keyEntryTrackedRef = useRef(false);
|
|
289
418
|
const savedEndpoint = isOpenAi ? (existing?.endpoint ?? "") : "";
|
|
290
419
|
const [endpointOpen, setEndpointOpen] = useState(!!savedEndpoint);
|
|
291
420
|
const [endpoint, setEndpoint] = useState(savedEndpoint);
|
|
@@ -313,9 +442,22 @@ function ProviderDialogForm({
|
|
|
313
442
|
const requestRef = useRef(0);
|
|
314
443
|
const fromService = mode === "add-from-service";
|
|
315
444
|
|
|
445
|
+
useEffect(() => {
|
|
446
|
+
onProviderChange(provider);
|
|
447
|
+
}, [onProviderChange, provider]);
|
|
448
|
+
|
|
316
449
|
// A pasted key (or Ollama endpoint) is checked as it's entered, by asking
|
|
317
450
|
// the provider which models it reaches.
|
|
318
451
|
useEffect(() => {
|
|
452
|
+
if (keyValue.trim() && !keyEntryTrackedRef.current) {
|
|
453
|
+
keyEntryTrackedRef.current = true;
|
|
454
|
+
trackProviderSetupEvent(
|
|
455
|
+
trackingFlow,
|
|
456
|
+
"integration_key_entry_started",
|
|
457
|
+
"enter",
|
|
458
|
+
"started",
|
|
459
|
+
);
|
|
460
|
+
}
|
|
319
461
|
if (!replacing) return;
|
|
320
462
|
const value = keyValue.trim();
|
|
321
463
|
const request = ++requestRef.current;
|
|
@@ -336,7 +478,16 @@ function ProviderDialogForm({
|
|
|
336
478
|
.then((result) => {
|
|
337
479
|
if (request !== requestRef.current) return;
|
|
338
480
|
setCheck(toCheckState(result));
|
|
481
|
+
trackProviderSetupEvent(
|
|
482
|
+
trackingFlow,
|
|
483
|
+
"integration_key_validation_outcome",
|
|
484
|
+
"validate",
|
|
485
|
+
providerCheckOutcome(result),
|
|
486
|
+
);
|
|
339
487
|
if (!result.ok) return;
|
|
488
|
+
if (provider !== "ollama") {
|
|
489
|
+
trackCustomKeyOnboardingOutcome("credential_validated");
|
|
490
|
+
}
|
|
340
491
|
setChecked((previous) => {
|
|
341
492
|
const kept = previous.filter((model) =>
|
|
342
493
|
result.models.includes(model),
|
|
@@ -351,6 +502,12 @@ function ProviderDialogForm({
|
|
|
351
502
|
state: "error",
|
|
352
503
|
message: err instanceof Error ? err.message : String(err),
|
|
353
504
|
});
|
|
505
|
+
trackProviderSetupEvent(
|
|
506
|
+
trackingFlow,
|
|
507
|
+
"integration_key_validation_outcome",
|
|
508
|
+
"validate",
|
|
509
|
+
"error",
|
|
510
|
+
);
|
|
354
511
|
});
|
|
355
512
|
}, CHECK_DEBOUNCE_MS);
|
|
356
513
|
return () => window.clearTimeout(timer);
|
|
@@ -362,6 +519,7 @@ function ProviderDialogForm({
|
|
|
362
519
|
replacing,
|
|
363
520
|
models,
|
|
364
521
|
fromService,
|
|
522
|
+
trackingFlow,
|
|
365
523
|
]);
|
|
366
524
|
|
|
367
525
|
// Models checked when the dialog opened stay listed after a check, so one
|
|
@@ -392,6 +550,7 @@ function ProviderDialogForm({
|
|
|
392
550
|
addChoices.find((choice) => choice.provider === next)?.replaces ?? null;
|
|
393
551
|
const nextEndpoint = next === "openai" ? (target?.endpoint ?? "") : "";
|
|
394
552
|
setProvider(next);
|
|
553
|
+
onProviderChange(next);
|
|
395
554
|
setKeyValue((value) => (keepValue ? value : ""));
|
|
396
555
|
setKeyError(false);
|
|
397
556
|
setChecked(
|
|
@@ -411,10 +570,22 @@ function ProviderDialogForm({
|
|
|
411
570
|
if (result.ok) {
|
|
412
571
|
setCheck(toCheckState(result));
|
|
413
572
|
setRecheck({ checkedAt: result.checkedAt });
|
|
573
|
+
trackProviderSetupEvent(
|
|
574
|
+
trackingFlow,
|
|
575
|
+
"integration_key_validation_outcome",
|
|
576
|
+
"validate",
|
|
577
|
+
providerCheckOutcome(result),
|
|
578
|
+
);
|
|
414
579
|
return;
|
|
415
580
|
}
|
|
416
581
|
setRecheck("idle");
|
|
417
582
|
setCheck(toCheckState(result));
|
|
583
|
+
trackProviderSetupEvent(
|
|
584
|
+
trackingFlow,
|
|
585
|
+
"integration_key_validation_outcome",
|
|
586
|
+
"validate",
|
|
587
|
+
providerCheckOutcome(result),
|
|
588
|
+
);
|
|
418
589
|
if (result.code === "rejected" || result.code === "wrong-provider") {
|
|
419
590
|
setSavedRejected(true);
|
|
420
591
|
setReplacing(true);
|
|
@@ -425,6 +596,12 @@ function ProviderDialogForm({
|
|
|
425
596
|
.catch((err: unknown) => {
|
|
426
597
|
setRecheck("idle");
|
|
427
598
|
setError(err instanceof Error ? err.message : String(err));
|
|
599
|
+
trackProviderSetupEvent(
|
|
600
|
+
trackingFlow,
|
|
601
|
+
"integration_key_validation_outcome",
|
|
602
|
+
"validate",
|
|
603
|
+
"error",
|
|
604
|
+
);
|
|
428
605
|
});
|
|
429
606
|
};
|
|
430
607
|
|
|
@@ -447,26 +624,39 @@ function ProviderDialogForm({
|
|
|
447
624
|
mode !== "manage" || !sameModels(checked, initialSelection ?? []);
|
|
448
625
|
|
|
449
626
|
setSaving(true);
|
|
450
|
-
|
|
627
|
+
onSavingChange(true);
|
|
628
|
+
let settingsSaved = false;
|
|
451
629
|
try {
|
|
452
630
|
if (replacing) {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
631
|
+
const saveProviderSettings = () =>
|
|
632
|
+
saveAgentEngineProviderSettings({
|
|
633
|
+
provider,
|
|
634
|
+
...(isOllama ? { baseUrl: value } : { apiKey: value }),
|
|
635
|
+
...(gateway ? { baseUrl: gateway } : {}),
|
|
636
|
+
...(isOpenAi && !gateway && existing?.endpoint
|
|
637
|
+
? { clearBaseUrl: true }
|
|
638
|
+
: {}),
|
|
639
|
+
scope,
|
|
640
|
+
});
|
|
641
|
+
if (isOllama) {
|
|
642
|
+
await withCustomKeyOnboardingLocalEndpointSave(saveProviderSettings);
|
|
643
|
+
} else {
|
|
644
|
+
await withCustomKeyOnboardingCredentialSave(saveProviderSettings);
|
|
645
|
+
}
|
|
646
|
+
settingsSaved = true;
|
|
647
|
+
trackProviderSetupEvent(
|
|
648
|
+
trackingFlow,
|
|
649
|
+
"integration_key_save_outcome",
|
|
650
|
+
"save",
|
|
651
|
+
"saved",
|
|
652
|
+
);
|
|
463
653
|
} else if (endpointChanged) {
|
|
464
654
|
await saveAgentEngineProviderSettings({
|
|
465
655
|
provider,
|
|
466
656
|
...(gateway ? { baseUrl: gateway } : { clearBaseUrl: true }),
|
|
467
657
|
scope,
|
|
468
658
|
});
|
|
469
|
-
|
|
659
|
+
settingsSaved = true;
|
|
470
660
|
}
|
|
471
661
|
if (modelsChanged) {
|
|
472
662
|
await callAction(
|
|
@@ -484,12 +674,23 @@ function ProviderDialogForm({
|
|
|
484
674
|
onOpenChange(false);
|
|
485
675
|
} catch (err) {
|
|
486
676
|
const message = err instanceof Error ? err.message : String(err);
|
|
487
|
-
|
|
488
|
-
|
|
677
|
+
if (replacing && !settingsSaved) {
|
|
678
|
+
trackProviderSetupEvent(
|
|
679
|
+
trackingFlow,
|
|
680
|
+
"integration_key_save_outcome",
|
|
681
|
+
"save",
|
|
682
|
+
"failed",
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
setError(
|
|
686
|
+
settingsSaved ? t(`${K}modelsSaveFailed`, { message }) : message,
|
|
687
|
+
);
|
|
688
|
+
if (settingsSaved) {
|
|
489
689
|
void queryClient.invalidateQueries({ queryKey: ["action"] });
|
|
490
690
|
}
|
|
491
691
|
} finally {
|
|
492
692
|
setSaving(false);
|
|
693
|
+
onSavingChange(false);
|
|
493
694
|
}
|
|
494
695
|
};
|
|
495
696
|
|
|
@@ -508,11 +709,7 @@ function ProviderDialogForm({
|
|
|
508
709
|
{t(`${K}restricted`)}
|
|
509
710
|
</p>
|
|
510
711
|
<DialogFooter>
|
|
511
|
-
<Button
|
|
512
|
-
type="button"
|
|
513
|
-
variant="secondary"
|
|
514
|
-
onClick={() => onOpenChange(false)}
|
|
515
|
-
>
|
|
712
|
+
<Button type="button" variant="secondary" onClick={onDismiss}>
|
|
516
713
|
{t(`${K}cancel`)}
|
|
517
714
|
</Button>
|
|
518
715
|
</DialogFooter>
|
|
@@ -607,6 +804,9 @@ function ProviderDialogForm({
|
|
|
607
804
|
onChange={(event) => {
|
|
608
805
|
setKeyValue(event.target.value);
|
|
609
806
|
setKeyError(false);
|
|
807
|
+
if (event.target.value.trim() && !isOllama) {
|
|
808
|
+
trackCustomKeyOnboardingOutcome("credential_entry_started");
|
|
809
|
+
}
|
|
610
810
|
}}
|
|
611
811
|
/>
|
|
612
812
|
) : (
|
|
@@ -762,11 +962,7 @@ function ProviderDialogForm({
|
|
|
762
962
|
{t(`${K}removeProvider`)}
|
|
763
963
|
</Button>
|
|
764
964
|
) : null}
|
|
765
|
-
<Button
|
|
766
|
-
type="button"
|
|
767
|
-
variant="secondary"
|
|
768
|
-
onClick={() => onOpenChange(false)}
|
|
769
|
-
>
|
|
965
|
+
<Button type="button" variant="secondary" onClick={onDismiss}>
|
|
770
966
|
{t(`${K}cancel`)}
|
|
771
967
|
</Button>
|
|
772
968
|
<Button type="submit" disabled={saving || !ready}>
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
isBuilderConnectComplete,
|
|
13
13
|
useBuilderStatus,
|
|
14
14
|
useBuilderConnectFlow,
|
|
15
|
+
requestBuilderAccountActivation,
|
|
15
16
|
withBuilderConnectTrackingParams,
|
|
16
17
|
type BuilderConnectionScope,
|
|
17
18
|
} from "./useBuilderStatus.js";
|
|
@@ -292,6 +293,7 @@ describe("useBuilderStatus", () => {
|
|
|
292
293
|
afterEach(() => {
|
|
293
294
|
act(() => root.unmount());
|
|
294
295
|
container.remove();
|
|
296
|
+
Reflect.deleteProperty(window.location, "hostname");
|
|
295
297
|
vi.unstubAllGlobals();
|
|
296
298
|
});
|
|
297
299
|
|
|
@@ -498,6 +500,7 @@ describe("useBuilderConnectFlow", () => {
|
|
|
498
500
|
afterEach(() => {
|
|
499
501
|
act(() => root.unmount());
|
|
500
502
|
container.remove();
|
|
503
|
+
Reflect.deleteProperty(window.location, "hostname");
|
|
501
504
|
vi.useRealTimers();
|
|
502
505
|
vi.unstubAllGlobals();
|
|
503
506
|
});
|
|
@@ -776,6 +779,12 @@ describe("useBuilderConnectFlow", () => {
|
|
|
776
779
|
|
|
777
780
|
for (const host of ["desktop", "embedded", "browser"] as const) {
|
|
778
781
|
it(`creates the account with one request and no popup (${host})`, async () => {
|
|
782
|
+
Object.defineProperty(window.location, "hostname", {
|
|
783
|
+
configurable: true,
|
|
784
|
+
value: "design.custom-domain.com",
|
|
785
|
+
});
|
|
786
|
+
vi.stubGlobal("__AGENT_NATIVE_APP_ID__", "agent-native-clips");
|
|
787
|
+
vi.stubGlobal("__AGENT_NATIVE_TEMPLATE__", "clips");
|
|
779
788
|
setUserAgent(
|
|
780
789
|
host === "desktop"
|
|
781
790
|
? "Mozilla/5.0 AgentNativeDesktop/1.0"
|
|
@@ -809,11 +818,48 @@ describe("useBuilderConnectFlow", () => {
|
|
|
809
818
|
expect(posts[0]!.url.searchParams.get("agentNativeFlow")).toBe(
|
|
810
819
|
"connect_llm",
|
|
811
820
|
);
|
|
821
|
+
expect(posts[0]!.url.searchParams.get("agentNativeApp")).toBe(
|
|
822
|
+
"agent-native-clips",
|
|
823
|
+
);
|
|
824
|
+
expect(posts[0]!.url.searchParams.get("agentNativeTemplate")).toBe(
|
|
825
|
+
"clips",
|
|
826
|
+
);
|
|
812
827
|
expect(container.textContent).toContain("configured idle resolved");
|
|
813
828
|
expect(onConnected).toHaveBeenCalledOnce();
|
|
814
829
|
});
|
|
815
830
|
}
|
|
816
831
|
|
|
832
|
+
it.each([
|
|
833
|
+
"localhost",
|
|
834
|
+
"127.0.0.1",
|
|
835
|
+
"www.agent-native.com",
|
|
836
|
+
"design.custom-domain.com",
|
|
837
|
+
])("does not infer signup attribution from %s", async (hostname) => {
|
|
838
|
+
Object.defineProperty(window.location, "hostname", {
|
|
839
|
+
configurable: true,
|
|
840
|
+
value: hostname,
|
|
841
|
+
});
|
|
842
|
+
const posts = mockActivation(() => activationResponse(200, { ok: true }));
|
|
843
|
+
|
|
844
|
+
await requestBuilderAccountActivation({ provisioningToken });
|
|
845
|
+
|
|
846
|
+
expect(posts).toHaveLength(1);
|
|
847
|
+
expect(posts[0]!.url.searchParams.get("agentNativeApp")).toBeNull();
|
|
848
|
+
expect(posts[0]!.url.searchParams.get("agentNativeTemplate")).toBeNull();
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
it("does not infer a template from the injected app ID", async () => {
|
|
852
|
+
vi.stubGlobal("__AGENT_NATIVE_APP_ID__", "private-workspace");
|
|
853
|
+
const posts = mockActivation(() => activationResponse(200, { ok: true }));
|
|
854
|
+
|
|
855
|
+
await requestBuilderAccountActivation({ provisioningToken });
|
|
856
|
+
|
|
857
|
+
expect(posts[0]!.url.searchParams.get("agentNativeApp")).toBe(
|
|
858
|
+
"private-workspace",
|
|
859
|
+
);
|
|
860
|
+
expect(posts[0]!.url.searchParams.get("agentNativeTemplate")).toBeNull();
|
|
861
|
+
});
|
|
862
|
+
|
|
817
863
|
it("activates an account for the organization's connection", async () => {
|
|
818
864
|
const posts = mockActivation(() =>
|
|
819
865
|
activationResponse(200, { ok: true, scope: "org" }),
|
|
@@ -2604,6 +2650,9 @@ describe("useBuilderConnectFlow", () => {
|
|
|
2604
2650
|
|
|
2605
2651
|
expect(container.textContent).toContain("not-configured idle");
|
|
2606
2652
|
expect(container.textContent).toContain("Didn't hear back from Builder");
|
|
2653
|
+
expect(
|
|
2654
|
+
container.querySelector('[data-testid="error-kind"]')?.textContent,
|
|
2655
|
+
).toBe("connection");
|
|
2607
2656
|
});
|
|
2608
2657
|
|
|
2609
2658
|
it("clears a stale status-read error after a readable incomplete callback status", async () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { trackEvent } from "@agent-native/core/client/analytics";
|
|
2
2
|
import { agentNativePath } from "@agent-native/core/client/api-path";
|
|
3
|
+
import { injectedAgentNativeAppId } from "@agent-native/core/client/app-config";
|
|
3
4
|
import { getCallbackOrigin } from "@agent-native/core/client/frame";
|
|
4
5
|
import { scheduleAfterPaint } from "@agent-native/core/client/hooks";
|
|
5
6
|
import { usePollLoop } from "@agent-native/core/client/hooks";
|
|
@@ -297,6 +298,7 @@ const BUILDER_AGENT_NATIVE_FLOW_PARAM = "agentNativeFlow";
|
|
|
297
298
|
const BUILDER_AGENT_NATIVE_CONNECT_SOURCE_PARAM = "agentNativeConnectSource";
|
|
298
299
|
const BUILDER_AGENT_NATIVE_APP_PARAM = "agentNativeApp";
|
|
299
300
|
const BUILDER_AGENT_NATIVE_TEMPLATE_PARAM = "agentNativeTemplate";
|
|
301
|
+
declare const __AGENT_NATIVE_TEMPLATE__: string | undefined;
|
|
300
302
|
const BUILDER_SIGNUP_SOURCE = "agent-native";
|
|
301
303
|
const STATUS_CONNECT_URL_TTL_MS = 9 * 60 * 1000;
|
|
302
304
|
|
|
@@ -354,18 +356,16 @@ function inferBuilderConnectTrackingIdentity(options: {
|
|
|
354
356
|
const app =
|
|
355
357
|
normalizeTrackingSlug(options.app) ??
|
|
356
358
|
normalizeTrackingSlug(env.VITE_AGENT_NATIVE_APP) ??
|
|
357
|
-
(
|
|
358
|
-
? normalizeTrackingSlug(window.location.hostname.split(".")[0])
|
|
359
|
-
: null);
|
|
359
|
+
normalizeTrackingSlug(injectedAgentNativeAppId());
|
|
360
360
|
const template =
|
|
361
361
|
normalizeTrackingSlug(options.template) ??
|
|
362
362
|
normalizeTrackingSlug(env.VITE_AGENT_NATIVE_TEMPLATE) ??
|
|
363
363
|
normalizeTrackingSlug(env.VITE_APP_TEMPLATE) ??
|
|
364
|
-
(
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
364
|
+
normalizeTrackingSlug(
|
|
365
|
+
typeof __AGENT_NATIVE_TEMPLATE__ === "string"
|
|
366
|
+
? __AGENT_NATIVE_TEMPLATE__
|
|
367
|
+
: undefined,
|
|
368
|
+
);
|
|
369
369
|
|
|
370
370
|
return { app, template };
|
|
371
371
|
}
|
|
@@ -722,9 +722,15 @@ export async function requestBuilderAccountActivation({
|
|
|
722
722
|
signal?: AbortSignal;
|
|
723
723
|
}): Promise<BuilderAccountActivationResult> {
|
|
724
724
|
const origin = getCallbackOrigin() || window.location.origin;
|
|
725
|
+
const { app, template } = inferBuilderConnectTrackingIdentity({});
|
|
725
726
|
const url = withBuilderConnectTrackingParams(
|
|
726
727
|
new URL(agentNativePath("/_agent-native/builder/provision"), origin).href,
|
|
727
|
-
{
|
|
728
|
+
{
|
|
729
|
+
source,
|
|
730
|
+
flow,
|
|
731
|
+
app: app ?? undefined,
|
|
732
|
+
template: template ?? undefined,
|
|
733
|
+
},
|
|
728
734
|
);
|
|
729
735
|
let response: Response;
|
|
730
736
|
try {
|
|
@@ -1812,7 +1818,7 @@ export function useBuilderConnectFlow(
|
|
|
1812
1818
|
if (s) {
|
|
1813
1819
|
setError(
|
|
1814
1820
|
"Didn't hear back from Builder in 5 minutes. Allow popups and try again.",
|
|
1815
|
-
"
|
|
1821
|
+
"connection",
|
|
1816
1822
|
);
|
|
1817
1823
|
}
|
|
1818
1824
|
}
|