@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ProviderKeyPolicyStatus } from "@agent-native/core/agent/actions/manage-provider-key-policy";
|
|
2
2
|
import { CHATGPT_SUBSCRIPTION_ENGINE_NAME } from "@agent-native/core/agent/chatgpt-subscription-contract";
|
|
3
|
-
import {
|
|
3
|
+
import { upgradeModelForProvider } from "@agent-native/core/agent/model-version";
|
|
4
4
|
import {
|
|
5
5
|
setAgentEngineDefaultModel,
|
|
6
6
|
type AgentEngineKeyScope,
|
|
@@ -42,7 +42,16 @@ import {
|
|
|
42
42
|
} from "@agent-native/toolkit/ui/tooltip";
|
|
43
43
|
import { IconCpu, IconLock, IconPlus } from "@tabler/icons-react";
|
|
44
44
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
45
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
useCallback,
|
|
47
|
+
useEffect,
|
|
48
|
+
useId,
|
|
49
|
+
useMemo,
|
|
50
|
+
useRef,
|
|
51
|
+
useState,
|
|
52
|
+
type ReactNode,
|
|
53
|
+
} from "react";
|
|
54
|
+
import { useLocation, useNavigate } from "react-router";
|
|
46
55
|
|
|
47
56
|
import { ErrorRow, SettingsEmpty } from "../../resources/index.js";
|
|
48
57
|
import { DeferredBuilderConnectPopover } from "../deferred-builder-connect-popover.js";
|
|
@@ -90,6 +99,11 @@ type DialogState =
|
|
|
90
99
|
| { mode: "add" }
|
|
91
100
|
| { mode: "manage"; provider: AgentProviderId; scope: AgentEngineKeyScope };
|
|
92
101
|
|
|
102
|
+
type ActiveDialogState = {
|
|
103
|
+
dialog: DialogState;
|
|
104
|
+
trackingFlow: "chat_setup" | "settings";
|
|
105
|
+
};
|
|
106
|
+
|
|
93
107
|
type ChatGPTSubscriptionStatusRead = {
|
|
94
108
|
connected: boolean;
|
|
95
109
|
activeAccountId: string | null;
|
|
@@ -156,6 +170,12 @@ function useChatGPTModels(
|
|
|
156
170
|
*/
|
|
157
171
|
export default function ModelSettingsPage(_props: SettingsPageProps) {
|
|
158
172
|
const t = useT();
|
|
173
|
+
const location = useLocation();
|
|
174
|
+
const navigate = useNavigate();
|
|
175
|
+
const chatSetupTrackingPending = useRef(
|
|
176
|
+
(location.state as { providerSetupTrackingFlow?: unknown } | null)
|
|
177
|
+
?.providerSetupTrackingFlow === "chat_setup",
|
|
178
|
+
);
|
|
159
179
|
const org = useOrg();
|
|
160
180
|
const listing = useActionQuery<ModelProvidersListing>(
|
|
161
181
|
"list-model-providers" as never,
|
|
@@ -168,8 +188,42 @@ export default function ModelSettingsPage(_props: SettingsPageProps) {
|
|
|
168
188
|
trackingSource: "settings_model",
|
|
169
189
|
trackingFlow: "connect_llm",
|
|
170
190
|
});
|
|
171
|
-
const [dialog, setDialog] = useState<
|
|
191
|
+
const [dialog, setDialog] = useState<ActiveDialogState | null>(null);
|
|
172
192
|
const [removing, setRemoving] = useState<ProviderKeyRow | null>(null);
|
|
193
|
+
const openProviderDialog = useCallback(
|
|
194
|
+
(nextDialog: DialogState) => {
|
|
195
|
+
const trackingFlow = chatSetupTrackingPending.current
|
|
196
|
+
? "chat_setup"
|
|
197
|
+
: "settings";
|
|
198
|
+
if (chatSetupTrackingPending.current) {
|
|
199
|
+
chatSetupTrackingPending.current = false;
|
|
200
|
+
const locationState =
|
|
201
|
+
location.state && typeof location.state === "object"
|
|
202
|
+
? Object.fromEntries(
|
|
203
|
+
Object.entries(location.state).filter(
|
|
204
|
+
([key]) => key !== "providerSetupTrackingFlow",
|
|
205
|
+
),
|
|
206
|
+
)
|
|
207
|
+
: null;
|
|
208
|
+
navigate(
|
|
209
|
+
{
|
|
210
|
+
pathname: location.pathname,
|
|
211
|
+
search: location.search,
|
|
212
|
+
hash: location.hash,
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
replace: true,
|
|
216
|
+
state:
|
|
217
|
+
locationState && Object.keys(locationState).length > 0
|
|
218
|
+
? locationState
|
|
219
|
+
: null,
|
|
220
|
+
},
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
setDialog({ dialog: nextDialog, trackingFlow });
|
|
224
|
+
},
|
|
225
|
+
[location.hash, location.pathname, location.search, navigate],
|
|
226
|
+
);
|
|
173
227
|
|
|
174
228
|
const chatgptLab = useLabState("chatgpt-subscription");
|
|
175
229
|
const chatgptStatus = useActionQuery<ChatGPTSubscriptionStatusRead>(
|
|
@@ -212,10 +266,12 @@ export default function ModelSettingsPage(_props: SettingsPageProps) {
|
|
|
212
266
|
() => ({
|
|
213
267
|
action:
|
|
214
268
|
canAdd && !needsProvider ? (
|
|
215
|
-
<AddProviderButton
|
|
269
|
+
<AddProviderButton
|
|
270
|
+
onClick={() => openProviderDialog({ mode: "add" })}
|
|
271
|
+
/>
|
|
216
272
|
) : undefined,
|
|
217
273
|
}),
|
|
218
|
-
[canAdd, needsProvider],
|
|
274
|
+
[canAdd, needsProvider, openProviderDialog],
|
|
219
275
|
);
|
|
220
276
|
useSettingsPageHeader(header);
|
|
221
277
|
|
|
@@ -240,7 +296,11 @@ export default function ModelSettingsPage(_props: SettingsPageProps) {
|
|
|
240
296
|
: { status: "loading" };
|
|
241
297
|
const orgName = org.data?.orgName ?? "";
|
|
242
298
|
const openManage = (row: ProviderKeyRow) =>
|
|
243
|
-
|
|
299
|
+
openProviderDialog({
|
|
300
|
+
mode: "manage",
|
|
301
|
+
provider: row.provider,
|
|
302
|
+
scope: row.key.scope,
|
|
303
|
+
});
|
|
244
304
|
|
|
245
305
|
return (
|
|
246
306
|
<TooltipProvider delayDuration={200}>
|
|
@@ -251,7 +311,7 @@ export default function ModelSettingsPage(_props: SettingsPageProps) {
|
|
|
251
311
|
listing={data}
|
|
252
312
|
canAdd={canAdd}
|
|
253
313
|
builder={builder}
|
|
254
|
-
onAdd={() =>
|
|
314
|
+
onAdd={() => openProviderDialog({ mode: "add" })}
|
|
255
315
|
/>
|
|
256
316
|
</SettingsGroup>
|
|
257
317
|
) : data.hasOrganization ? (
|
|
@@ -295,12 +355,16 @@ export default function ModelSettingsPage(_props: SettingsPageProps) {
|
|
|
295
355
|
</div>
|
|
296
356
|
<ProviderDialog
|
|
297
357
|
open={dialog !== null}
|
|
358
|
+
trackingFlow={dialog?.trackingFlow ?? "settings"}
|
|
298
359
|
onOpenChange={(open) => {
|
|
299
360
|
if (!open) setDialog(null);
|
|
300
361
|
}}
|
|
301
|
-
mode={dialog?.mode ?? "add"}
|
|
302
|
-
{...(dialog?.mode === "manage"
|
|
303
|
-
? {
|
|
362
|
+
mode={dialog?.dialog.mode ?? "add"}
|
|
363
|
+
{...(dialog?.dialog.mode === "manage"
|
|
364
|
+
? {
|
|
365
|
+
provider: dialog.dialog.provider,
|
|
366
|
+
scope: dialog.dialog.scope,
|
|
367
|
+
}
|
|
304
368
|
: {})}
|
|
305
369
|
/>
|
|
306
370
|
{removing ? (
|
|
@@ -802,7 +866,11 @@ function DefaultModelRow({
|
|
|
802
866
|
engine: listing.defaultModel.engine,
|
|
803
867
|
model:
|
|
804
868
|
(!preserveCustomModels &&
|
|
805
|
-
|
|
869
|
+
upgradeModelForProvider(
|
|
870
|
+
model,
|
|
871
|
+
supportedModels,
|
|
872
|
+
listing.defaultModel.engine,
|
|
873
|
+
)) ||
|
|
806
874
|
model,
|
|
807
875
|
};
|
|
808
876
|
})()
|
|
@@ -23,6 +23,12 @@ const keyMock = vi.hoisted(() => ({
|
|
|
23
23
|
deleteAgentEngineProviderSettings: vi.fn(),
|
|
24
24
|
}));
|
|
25
25
|
const callActionMock = vi.hoisted(() => vi.fn());
|
|
26
|
+
const setupTelemetryMock = vi.hoisted(() => vi.fn());
|
|
27
|
+
const onboardingOutcomeMock = vi.hoisted(() => vi.fn());
|
|
28
|
+
const onboardingAbandonmentRequestMock = vi.hoisted(() => vi.fn());
|
|
29
|
+
const onboardingSetupKindMock = vi.hoisted(() => vi.fn());
|
|
30
|
+
const credentialSaveBoundaryMock = vi.hoisted(() => vi.fn());
|
|
31
|
+
const localEndpointSaveBoundaryMock = vi.hoisted(() => vi.fn());
|
|
26
32
|
|
|
27
33
|
vi.mock("@agent-native/core/client/hooks", () => ({
|
|
28
34
|
useActionQuery: (name: string) => ({
|
|
@@ -42,6 +48,29 @@ vi.mock("@agent-native/core/client/hooks", () => ({
|
|
|
42
48
|
|
|
43
49
|
vi.mock("@agent-native/core/client/agent-engine-key", () => keyMock);
|
|
44
50
|
|
|
51
|
+
vi.mock("@agent-native/core/client/onboarding/use-onboarding", () => ({
|
|
52
|
+
trackOnboardingEvent: setupTelemetryMock,
|
|
53
|
+
requestCustomKeyOnboardingAbandonment: onboardingAbandonmentRequestMock,
|
|
54
|
+
setCustomKeyOnboardingSetupKind: onboardingSetupKindMock,
|
|
55
|
+
trackCustomKeyOnboardingOutcome: onboardingOutcomeMock,
|
|
56
|
+
withCustomKeyOnboardingCredentialSave: async (
|
|
57
|
+
save: () => Promise<unknown>,
|
|
58
|
+
) => {
|
|
59
|
+
credentialSaveBoundaryMock();
|
|
60
|
+
const result = await save();
|
|
61
|
+
onboardingOutcomeMock("credential_saved");
|
|
62
|
+
return result;
|
|
63
|
+
},
|
|
64
|
+
withCustomKeyOnboardingLocalEndpointSave: async (
|
|
65
|
+
save: () => Promise<unknown>,
|
|
66
|
+
) => {
|
|
67
|
+
localEndpointSaveBoundaryMock();
|
|
68
|
+
const result = await save();
|
|
69
|
+
onboardingOutcomeMock("local_endpoint_saved");
|
|
70
|
+
return result;
|
|
71
|
+
},
|
|
72
|
+
}));
|
|
73
|
+
|
|
45
74
|
vi.mock("@agent-native/core/client/org", () => ({
|
|
46
75
|
useOrg: () => ({ data: { orgName: "Acme" }, isLoading: false }),
|
|
47
76
|
}));
|
|
@@ -187,6 +216,12 @@ describe("ProviderDialog", () => {
|
|
|
187
216
|
.mockReset()
|
|
188
217
|
.mockResolvedValue(undefined);
|
|
189
218
|
callActionMock.mockReset().mockResolvedValue({});
|
|
219
|
+
setupTelemetryMock.mockReset();
|
|
220
|
+
onboardingOutcomeMock.mockReset();
|
|
221
|
+
onboardingAbandonmentRequestMock.mockReset();
|
|
222
|
+
onboardingSetupKindMock.mockReset();
|
|
223
|
+
credentialSaveBoundaryMock.mockReset();
|
|
224
|
+
localEndpointSaveBoundaryMock.mockReset();
|
|
190
225
|
container = document.createElement("div");
|
|
191
226
|
document.body.appendChild(container);
|
|
192
227
|
root = createRoot(container);
|
|
@@ -275,6 +310,54 @@ describe("ProviderDialog", () => {
|
|
|
275
310
|
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
276
311
|
});
|
|
277
312
|
|
|
313
|
+
it("tracks chat setup key entry and save without recording the key", async () => {
|
|
314
|
+
keyMock.fetchProviderModels.mockResolvedValue({
|
|
315
|
+
ok: true,
|
|
316
|
+
provider: "anthropic",
|
|
317
|
+
models: ["claude-sonnet-5"],
|
|
318
|
+
checkedAt: 1,
|
|
319
|
+
});
|
|
320
|
+
render({ provider: "anthropic", trackingFlow: "chat_setup" });
|
|
321
|
+
|
|
322
|
+
typeInto(inputByLabel("API key"), "sk-ant-test-0000");
|
|
323
|
+
await vi.waitFor(() => {
|
|
324
|
+
expect(setupTelemetryMock).toHaveBeenCalledWith(
|
|
325
|
+
"integration_key_entry_started",
|
|
326
|
+
expect.objectContaining({
|
|
327
|
+
flow: "chat_setup",
|
|
328
|
+
app_name: expect.any(String),
|
|
329
|
+
step_id: "connect_ai",
|
|
330
|
+
method_id: "custom_keys",
|
|
331
|
+
action: "enter",
|
|
332
|
+
outcome: "started",
|
|
333
|
+
}),
|
|
334
|
+
);
|
|
335
|
+
expect(setupTelemetryMock).toHaveBeenCalledWith(
|
|
336
|
+
"integration_key_validation_outcome",
|
|
337
|
+
expect.objectContaining({
|
|
338
|
+
flow: "chat_setup",
|
|
339
|
+
action: "validate",
|
|
340
|
+
outcome: "accepted",
|
|
341
|
+
}),
|
|
342
|
+
);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
await act(async () => button("Add provider").click());
|
|
346
|
+
await vi.waitFor(() => {
|
|
347
|
+
expect(setupTelemetryMock).toHaveBeenCalledWith(
|
|
348
|
+
"integration_key_save_outcome",
|
|
349
|
+
expect.objectContaining({
|
|
350
|
+
flow: "chat_setup",
|
|
351
|
+
action: "save",
|
|
352
|
+
outcome: "saved",
|
|
353
|
+
}),
|
|
354
|
+
);
|
|
355
|
+
});
|
|
356
|
+
expect(JSON.stringify(setupTelemetryMock.mock.calls)).not.toContain(
|
|
357
|
+
"sk-ant-test-0000",
|
|
358
|
+
);
|
|
359
|
+
});
|
|
360
|
+
|
|
278
361
|
it("keeps Add disabled until the key checks out and keeps a failed save open", async () => {
|
|
279
362
|
keyMock.fetchProviderModels.mockResolvedValue({
|
|
280
363
|
ok: true,
|
|
@@ -306,6 +389,49 @@ describe("ProviderDialog", () => {
|
|
|
306
389
|
expect(onOpenChange).not.toHaveBeenCalledWith(false);
|
|
307
390
|
});
|
|
308
391
|
|
|
392
|
+
it("allows leaving a provider dialog while preserving its pending save outcome", async () => {
|
|
393
|
+
let resolveSave!: () => void;
|
|
394
|
+
keyMock.saveAgentEngineProviderSettings.mockReturnValue(
|
|
395
|
+
new Promise<void>((resolve) => {
|
|
396
|
+
resolveSave = resolve;
|
|
397
|
+
}),
|
|
398
|
+
);
|
|
399
|
+
keyMock.fetchProviderModels.mockResolvedValue({
|
|
400
|
+
ok: true,
|
|
401
|
+
provider: "anthropic",
|
|
402
|
+
models: ["claude-sonnet-5"],
|
|
403
|
+
checkedAt: 1,
|
|
404
|
+
});
|
|
405
|
+
const { onOpenChange } = render({ provider: "anthropic" });
|
|
406
|
+
|
|
407
|
+
typeInto(inputByLabel("API key"), "sk-ant-test-0000");
|
|
408
|
+
await vi.waitFor(() => {
|
|
409
|
+
expect(button("Add provider").disabled).toBe(false);
|
|
410
|
+
});
|
|
411
|
+
await act(async () => button("Add provider").click());
|
|
412
|
+
await vi.waitFor(() => {
|
|
413
|
+
expect(keyMock.saveAgentEngineProviderSettings).toHaveBeenCalled();
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
await act(async () => button("Close").click());
|
|
417
|
+
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
418
|
+
expect(onboardingAbandonmentRequestMock).toHaveBeenCalledTimes(1);
|
|
419
|
+
expect(
|
|
420
|
+
onboardingOutcomeMock.mock.calls.map(([outcome]) => outcome),
|
|
421
|
+
).not.toContain("credential_skipped");
|
|
422
|
+
|
|
423
|
+
await act(async () => {
|
|
424
|
+
resolveSave();
|
|
425
|
+
await vi.waitFor(() => expect(onOpenChange).toHaveBeenCalledWith(false));
|
|
426
|
+
});
|
|
427
|
+
const outcomes = onboardingOutcomeMock.mock.calls.map(
|
|
428
|
+
([outcome]) => outcome,
|
|
429
|
+
);
|
|
430
|
+
expect(outcomes).toContain("credential_saved");
|
|
431
|
+
expect(outcomes).not.toContain("credential_skipped");
|
|
432
|
+
expect(credentialSaveBoundaryMock).toHaveBeenCalledTimes(1);
|
|
433
|
+
});
|
|
434
|
+
|
|
309
435
|
it("locks members to a personal key", async () => {
|
|
310
436
|
state.listing = listing({ canManageOrg: false });
|
|
311
437
|
keyMock.fetchProviderModels.mockResolvedValue({
|
|
@@ -387,9 +513,10 @@ describe("ProviderDialog", () => {
|
|
|
387
513
|
models: ["llama3.1:latest"],
|
|
388
514
|
checkedAt: 1,
|
|
389
515
|
});
|
|
390
|
-
render({ provider: "ollama" });
|
|
516
|
+
const { onSaved } = render({ provider: "ollama" });
|
|
391
517
|
expect(document.body.textContent).toContain("No API key required.");
|
|
392
518
|
typeInto(inputByLabel("Endpoint URL"), "http://ollama.internal:11434");
|
|
519
|
+
expect(onboardingOutcomeMock).not.toHaveBeenCalled();
|
|
393
520
|
await vi.waitFor(() => {
|
|
394
521
|
expect(document.body.textContent).toContain("llama3.1:latest");
|
|
395
522
|
});
|
|
@@ -397,6 +524,36 @@ describe("ProviderDialog", () => {
|
|
|
397
524
|
provider: "ollama",
|
|
398
525
|
baseUrl: "http://ollama.internal:11434",
|
|
399
526
|
});
|
|
527
|
+
expect(onboardingOutcomeMock).not.toHaveBeenCalled();
|
|
528
|
+
|
|
529
|
+
await act(async () => button("Add provider").click());
|
|
530
|
+
await vi.waitFor(() => expect(onSaved).toHaveBeenCalled());
|
|
531
|
+
|
|
532
|
+
expect(keyMock.saveAgentEngineProviderSettings).toHaveBeenCalledWith({
|
|
533
|
+
provider: "ollama",
|
|
534
|
+
baseUrl: "http://ollama.internal:11434",
|
|
535
|
+
scope: "org",
|
|
536
|
+
});
|
|
537
|
+
expect(credentialSaveBoundaryMock).not.toHaveBeenCalled();
|
|
538
|
+
expect(localEndpointSaveBoundaryMock).toHaveBeenCalledOnce();
|
|
539
|
+
expect(onboardingOutcomeMock).toHaveBeenCalledWith("local_endpoint_saved");
|
|
540
|
+
expect(onboardingOutcomeMock).not.toHaveBeenCalledWith("credential_saved");
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
it("classifies dismissing Ollama setup separately from skipping credentials", () => {
|
|
544
|
+
render({ provider: "ollama" });
|
|
545
|
+
|
|
546
|
+
expect(onboardingSetupKindMock).toHaveBeenCalledWith("local_endpoint");
|
|
547
|
+
|
|
548
|
+
act(() => button("Cancel").click());
|
|
549
|
+
|
|
550
|
+
expect(onboardingOutcomeMock).toHaveBeenCalledExactlyOnceWith(
|
|
551
|
+
"local_endpoint_skipped",
|
|
552
|
+
);
|
|
553
|
+
expect(onboardingOutcomeMock).not.toHaveBeenCalledWith(
|
|
554
|
+
"credential_skipped",
|
|
555
|
+
);
|
|
556
|
+
expect(onboardingAbandonmentRequestMock).not.toHaveBeenCalled();
|
|
400
557
|
});
|
|
401
558
|
|
|
402
559
|
it("manages a saved key: masked, checked on save, and models only", async () => {
|
|
@@ -442,6 +599,40 @@ describe("ProviderDialog", () => {
|
|
|
442
599
|
});
|
|
443
600
|
});
|
|
444
601
|
|
|
602
|
+
it("does not count an endpoint-only update as a credential save", async () => {
|
|
603
|
+
state.listing = listing(
|
|
604
|
+
{},
|
|
605
|
+
{
|
|
606
|
+
openai: {
|
|
607
|
+
org: {
|
|
608
|
+
scope: "org",
|
|
609
|
+
masked: "••••9f3a",
|
|
610
|
+
updatedAt: 1,
|
|
611
|
+
endpoint: "https://old.example",
|
|
612
|
+
},
|
|
613
|
+
},
|
|
614
|
+
},
|
|
615
|
+
);
|
|
616
|
+
state.models = models({ openai: { org: ["model-a"] } });
|
|
617
|
+
const { onSaved } = render({
|
|
618
|
+
mode: "manage",
|
|
619
|
+
provider: "openai",
|
|
620
|
+
scope: "org",
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
typeInto(inputByLabel("Endpoint URL"), "https://gateway.example");
|
|
624
|
+
await act(async () => button("Save").click());
|
|
625
|
+
await vi.waitFor(() => expect(onSaved).toHaveBeenCalled());
|
|
626
|
+
|
|
627
|
+
expect(keyMock.saveAgentEngineProviderSettings).toHaveBeenCalledWith({
|
|
628
|
+
provider: "openai",
|
|
629
|
+
baseUrl: "https://gateway.example",
|
|
630
|
+
scope: "org",
|
|
631
|
+
});
|
|
632
|
+
expect(credentialSaveBoundaryMock).not.toHaveBeenCalled();
|
|
633
|
+
expect(onboardingOutcomeMock).not.toHaveBeenCalledWith("credential_saved");
|
|
634
|
+
});
|
|
635
|
+
|
|
445
636
|
it("asks for a new key when the saved one was rejected", () => {
|
|
446
637
|
state.listing = listing(
|
|
447
638
|
{},
|