@agent-native/toolkit 0.198.3-nightly-20260930154911 → 0.198.4
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 +4 -1
- package/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +12 -4
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +43 -21
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/AgentSidebar.d.ts.map +1 -1
- package/dist/app/chat/AgentSidebar.js +3 -1
- package/dist/app/chat/AgentSidebar.js.map +1 -1
- package/dist/app/chat/FileStorageSetupPopover.js +2 -2
- package/dist/app/chat/FileStorageSetupPopover.js.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.js +1 -1
- package/dist/app/chat/agentkit-chat/connections.d.ts +1 -4
- package/dist/app/chat/agentkit-chat/connections.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/connections.js +1 -26
- package/dist/app/chat/agentkit-chat/connections.js.map +1 -1
- package/dist/app/chat/agentkit-chat/history.d.ts +4 -1
- package/dist/app/chat/agentkit-chat/history.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/history.js +17 -5
- package/dist/app/chat/agentkit-chat/history.js.map +1 -1
- package/dist/app/chat/chat/history-types.d.ts +1 -1
- package/dist/app/chat/chat/history-types.d.ts.map +1 -1
- package/dist/app/chat/chat-first/copy.d.ts +1 -2
- package/dist/app/chat/chat-first/copy.d.ts.map +1 -1
- package/dist/app/chat/chat-first/copy.js +1 -121
- package/dist/app/chat/chat-first/copy.js.map +1 -1
- package/dist/app/chat/chat-first-copy.d.ts +3 -0
- package/dist/app/chat/chat-first-copy.d.ts.map +1 -0
- package/dist/app/chat/chat-first-copy.js +122 -0
- package/dist/app/chat/chat-first-copy.js.map +1 -0
- package/dist/app/feature-flags/FeatureFlagsPanel.d.ts.map +1 -1
- package/dist/app/feature-flags/FeatureFlagsPanel.js +1 -0
- package/dist/app/feature-flags/FeatureFlagsPanel.js.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +2 -1
- 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 +2 -1
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +2 -1
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +2 -1
- 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 +2 -1
- 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 +2 -1
- 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 +2 -1
- 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 +2 -1
- 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 +2 -1
- 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 +2 -1
- 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 +2 -1
- 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 +2 -1
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/labs/LabsSettings.d.ts.map +1 -1
- package/dist/app/labs/LabsSettings.js +7 -3
- package/dist/app/labs/LabsSettings.js.map +1 -1
- package/dist/app/org/OrgGeneralSection.d.ts.map +1 -1
- package/dist/app/org/OrgGeneralSection.js +21 -18
- package/dist/app/org/OrgGeneralSection.js.map +1 -1
- package/dist/app/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/app/org/OrgSwitcher.js +3 -2
- package/dist/app/org/OrgSwitcher.js.map +1 -1
- package/dist/app/org/workspace-app-links.d.ts +1 -0
- package/dist/app/org/workspace-app-links.d.ts.map +1 -1
- package/dist/app/org/workspace-app-links.js +3 -0
- package/dist/app/org/workspace-app-links.js.map +1 -1
- package/dist/app/resources/McpAgentKitConnectionRequest.d.ts +7 -2
- package/dist/app/resources/McpAgentKitConnectionRequest.d.ts.map +1 -1
- package/dist/app/resources/McpAgentKitConnectionRequest.js +132 -13
- package/dist/app/resources/McpAgentKitConnectionRequest.js.map +1 -1
- package/dist/icons/ResourceIconPicker.d.ts +6 -1
- package/dist/icons/ResourceIconPicker.d.ts.map +1 -1
- package/dist/icons/ResourceIconPicker.js +45 -6
- package/dist/icons/ResourceIconPicker.js.map +1 -1
- package/package.json +7 -5
- package/src/app/agentkit/react/components.spec.tsx +3 -3
- package/src/app/agentkit/react/components.tsx +22 -4
- package/src/app/agentkit/react/lifecycle.spec.tsx +42 -0
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +114 -11
- package/src/app/chat/AgentKitAssistantChat.tsx +78 -17
- package/src/app/chat/AgentPanel.header.spec.ts +59 -22
- package/src/app/chat/AgentSidebar.spec.tsx +2 -2
- package/src/app/chat/AgentSidebar.tsx +3 -3
- package/src/app/chat/FileStorageSetupPopover.tsx +2 -2
- package/src/app/chat/MultiTabAssistantChat.messageCount.spec.tsx +3 -1
- package/src/app/chat/MultiTabAssistantChat.spec.tsx +8 -4
- package/src/app/chat/MultiTabAssistantChat.tsx +1 -1
- package/src/app/chat/agent-sidebar-url-sync.sessions.spec.tsx +89 -0
- package/src/app/chat/agentkit-chat/action-widget.spec.tsx +5 -17
- package/src/app/chat/agentkit-chat/connections.spec.tsx +137 -0
- package/src/app/chat/agentkit-chat/connections.ts +2 -37
- package/src/app/chat/agentkit-chat/history.tsx +23 -8
- package/src/app/chat/chat/history-types.ts +1 -1
- package/src/app/chat/chat/tool-call-display.spec.tsx +2 -2
- package/src/app/chat/chat/widgets/record-change-widget.spec.tsx +20 -29
- package/src/app/chat/chat-first/copy.ts +1 -133
- package/src/app/chat/chat-first-copy.ts +133 -0
- package/src/app/chat/composer/use-agentkit-capabilities.hardening.spec.tsx +3 -2
- package/src/app/feature-flags/FeatureFlagsPanel.tsx +1 -0
- package/src/app/i18n/catalogs/ar-SA.ts +2 -1
- package/src/app/i18n/catalogs/de-DE.ts +3 -1
- package/src/app/i18n/catalogs/en-US.ts +2 -1
- package/src/app/i18n/catalogs/es-ES.ts +2 -1
- package/src/app/i18n/catalogs/fr-FR.ts +3 -1
- package/src/app/i18n/catalogs/hi-IN.ts +2 -1
- package/src/app/i18n/catalogs/ja-JP.ts +2 -1
- package/src/app/i18n/catalogs/ko-KR.ts +2 -1
- package/src/app/i18n/catalogs/pt-BR.ts +3 -1
- package/src/app/i18n/catalogs/zh-CN.ts +2 -1
- package/src/app/i18n/catalogs/zh-TW.ts +2 -1
- package/src/app/integrations/IntegrationDetailPage.spec.tsx +1 -1
- package/src/app/labs/LabsSettings.tsx +46 -10
- package/src/app/observability/ThumbsFeedback.spec.tsx +15 -0
- package/src/app/onboarding/FirstRunOnboarding.spec.tsx +4 -0
- package/src/app/org/OrgGeneralSection.tsx +32 -22
- package/src/app/org/OrgSwitcher.tsx +3 -2
- package/src/app/org/workspace-app-links.spec.ts +4 -0
- package/src/app/org/workspace-app-links.ts +8 -1
- package/src/app/resources/McpAgentKitConnectionRequest.spec.tsx +371 -2
- package/src/app/resources/McpAgentKitConnectionRequest.tsx +171 -11
- package/src/app/settings/SettingsTabsPage.spec.tsx +4 -2
- package/src/app/settings/shell/SettingsShell.spec.tsx +14 -8
- package/src/app/settings/shell/pages/app-group-pages.spec.tsx +35 -5
- package/src/app/settings/shell/pages/resource-pages.spec.tsx +3 -0
- package/src/app/settings/usage/UsagePage.spec.tsx +3 -3
- package/src/app/shared/lazy-chunk-error-boundary.spec.tsx +2 -0
- package/src/app-shell/sidebar-overflow.spec.tsx +4 -1
- package/src/design-tweaks/visual-style-controls.spec.tsx +38 -0
- package/src/editor/useCollabReconcile.concurrent.spec.ts +2 -1
- package/src/icons/ResourceIconPicker.spec.tsx +270 -0
- package/src/icons/ResourceIconPicker.tsx +72 -4
|
@@ -52,11 +52,14 @@ afterEach(() => {
|
|
|
52
52
|
vi.unstubAllEnvs();
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
+
const catalog = createToolkitI18nCatalog({ messages: {} });
|
|
56
|
+
|
|
55
57
|
describe("ThumbsFeedback localization", () => {
|
|
56
58
|
it("renders localized accessible labels and explanation copy", async () => {
|
|
57
59
|
act(() => {
|
|
58
60
|
root.render(
|
|
59
61
|
<AgentNativeI18nProvider
|
|
62
|
+
catalog={catalog}
|
|
60
63
|
initialLocale="de-DE"
|
|
61
64
|
initialPreference="de-DE"
|
|
62
65
|
persistPreference={false}
|
|
@@ -87,6 +90,7 @@ describe("ThumbsFeedback localization", () => {
|
|
|
87
90
|
act(() => {
|
|
88
91
|
root.render(
|
|
89
92
|
<AgentNativeI18nProvider
|
|
93
|
+
catalog={catalog}
|
|
90
94
|
initialLocale="en-US"
|
|
91
95
|
initialPreference="en-US"
|
|
92
96
|
persistPreference={false}
|
|
@@ -181,6 +185,7 @@ describe("ThumbsFeedback localization", () => {
|
|
|
181
185
|
act(() => {
|
|
182
186
|
root.render(
|
|
183
187
|
<AgentNativeI18nProvider
|
|
188
|
+
catalog={catalog}
|
|
184
189
|
initialLocale="en-US"
|
|
185
190
|
initialPreference="en-US"
|
|
186
191
|
persistPreference={false}
|
|
@@ -295,6 +300,7 @@ describe("ThumbsFeedback localization", () => {
|
|
|
295
300
|
act(() => {
|
|
296
301
|
root.render(
|
|
297
302
|
<AgentNativeI18nProvider
|
|
303
|
+
catalog={catalog}
|
|
298
304
|
initialLocale="en-US"
|
|
299
305
|
initialPreference="en-US"
|
|
300
306
|
persistPreference={false}
|
|
@@ -421,6 +427,7 @@ describe("ThumbsFeedback localization", () => {
|
|
|
421
427
|
act(() => {
|
|
422
428
|
root.render(
|
|
423
429
|
<AgentNativeI18nProvider
|
|
430
|
+
catalog={catalog}
|
|
424
431
|
initialLocale="en-US"
|
|
425
432
|
initialPreference="en-US"
|
|
426
433
|
persistPreference={false}
|
|
@@ -511,6 +518,7 @@ describe("ThumbsFeedback localization", () => {
|
|
|
511
518
|
act(() => {
|
|
512
519
|
root.render(
|
|
513
520
|
<AgentNativeI18nProvider
|
|
521
|
+
catalog={catalog}
|
|
514
522
|
initialLocale="en-US"
|
|
515
523
|
initialPreference="en-US"
|
|
516
524
|
persistPreference={false}
|
|
@@ -558,6 +566,7 @@ describe("ThumbsFeedback localization", () => {
|
|
|
558
566
|
act(() => {
|
|
559
567
|
root.render(
|
|
560
568
|
<AgentNativeI18nProvider
|
|
569
|
+
catalog={catalog}
|
|
561
570
|
initialLocale="en-US"
|
|
562
571
|
initialPreference="en-US"
|
|
563
572
|
persistPreference={false}
|
|
@@ -596,6 +605,7 @@ describe("ThumbsFeedback localization", () => {
|
|
|
596
605
|
act(() => {
|
|
597
606
|
root.render(
|
|
598
607
|
<AgentNativeI18nProvider
|
|
608
|
+
catalog={catalog}
|
|
599
609
|
initialLocale="en-US"
|
|
600
610
|
initialPreference="en-US"
|
|
601
611
|
persistPreference={false}
|
|
@@ -639,6 +649,7 @@ describe("ThumbsFeedback localization", () => {
|
|
|
639
649
|
act(() => {
|
|
640
650
|
root.render(
|
|
641
651
|
<AgentNativeI18nProvider
|
|
652
|
+
catalog={catalog}
|
|
642
653
|
initialLocale="en-US"
|
|
643
654
|
initialPreference="en-US"
|
|
644
655
|
persistPreference={false}
|
|
@@ -674,6 +685,7 @@ describe("ThumbsFeedback localization", () => {
|
|
|
674
685
|
act(() => {
|
|
675
686
|
root.render(
|
|
676
687
|
<AgentNativeI18nProvider
|
|
688
|
+
catalog={catalog}
|
|
677
689
|
initialLocale="en-US"
|
|
678
690
|
initialPreference="en-US"
|
|
679
691
|
persistPreference={false}
|
|
@@ -709,6 +721,7 @@ describe("ThumbsFeedback localization", () => {
|
|
|
709
721
|
act(() => {
|
|
710
722
|
root.render(
|
|
711
723
|
<AgentNativeI18nProvider
|
|
724
|
+
catalog={catalog}
|
|
712
725
|
initialLocale="en-US"
|
|
713
726
|
initialPreference="en-US"
|
|
714
727
|
persistPreference={false}
|
|
@@ -746,6 +759,7 @@ describe("ThumbsFeedback localization", () => {
|
|
|
746
759
|
act(() => {
|
|
747
760
|
root.render(
|
|
748
761
|
<AgentNativeI18nProvider
|
|
762
|
+
catalog={catalog}
|
|
749
763
|
initialLocale="en-US"
|
|
750
764
|
initialPreference="en-US"
|
|
751
765
|
persistPreference={false}
|
|
@@ -797,6 +811,7 @@ describe("ThumbsFeedback localization", () => {
|
|
|
797
811
|
act(() => {
|
|
798
812
|
root.render(
|
|
799
813
|
<AgentNativeI18nProvider
|
|
814
|
+
catalog={catalog}
|
|
800
815
|
initialLocale="en-US"
|
|
801
816
|
initialPreference="en-US"
|
|
802
817
|
persistPreference={false}
|
|
@@ -45,6 +45,10 @@ vi.mock("react-router", async (importOriginal) => {
|
|
|
45
45
|
};
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
+
vi.mock("@agent-native/core/client/feature-flags/use-feature-flag", () => ({
|
|
49
|
+
useFeatureFlagState: () => ({ status: "ready", enabled: true }),
|
|
50
|
+
}));
|
|
51
|
+
|
|
48
52
|
vi.mock("@agent-native/core/client/onboarding/use-onboarding", () => ({
|
|
49
53
|
trackOnboardingEvent: mocks.trackOnboardingEvent,
|
|
50
54
|
useOnboarding: mocks.useOnboarding,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useActionQuery } from "@agent-native/core/client/hooks";
|
|
1
2
|
import { useIconPickerLabels, useT } from "@agent-native/core/client/i18n";
|
|
2
3
|
import {
|
|
3
4
|
useOrg,
|
|
@@ -8,8 +9,11 @@ import {
|
|
|
8
9
|
useSwitchOrg,
|
|
9
10
|
useSetOrgWorkspaceUrl,
|
|
10
11
|
} from "@agent-native/core/client/org";
|
|
11
|
-
import {
|
|
12
|
-
|
|
12
|
+
import {
|
|
13
|
+
uploadWorkspacePrivateIcon,
|
|
14
|
+
workspacePrivateIconLibraryUrl,
|
|
15
|
+
workspacePrivateIconUrl,
|
|
16
|
+
} from "@agent-native/core/client/uploads";
|
|
13
17
|
import type { IconValue } from "@agent-native/core/icons";
|
|
14
18
|
import { docsUrl } from "@agent-native/core/shared/docs-url";
|
|
15
19
|
import { ResourceIcon, ResourceIconPicker } from "@agent-native/toolkit/icons";
|
|
@@ -368,31 +372,37 @@ export function OrgIconControl({
|
|
|
368
372
|
}) {
|
|
369
373
|
const t = useT();
|
|
370
374
|
const iconPickerLabels = useIconPickerLabels();
|
|
371
|
-
const
|
|
372
|
-
const
|
|
373
|
-
|
|
375
|
+
const { data: org } = useOrg();
|
|
376
|
+
const uploadedIcons = useActionQuery<{
|
|
377
|
+
assets: Array<{ id: string; alt?: string }>;
|
|
378
|
+
}>("list-workspace-icons", {}, { enabled: canEdit && !!org?.orgId });
|
|
374
379
|
return canEdit ? (
|
|
375
380
|
<ResourceIconPicker
|
|
376
381
|
value={icon}
|
|
382
|
+
uploadedImages={(uploadedIcons.data?.assets ?? []).map((asset) => ({
|
|
383
|
+
version: 1,
|
|
384
|
+
kind: "image",
|
|
385
|
+
authority: "private-icon",
|
|
386
|
+
assetId: asset.id,
|
|
387
|
+
...(asset.alt ? { alt: asset.alt } : {}),
|
|
388
|
+
}))}
|
|
389
|
+
uploadedImagesError={Boolean(uploadedIcons.error)}
|
|
390
|
+
onUploadedImagesRetry={() => {
|
|
391
|
+
void uploadedIcons.refetch();
|
|
392
|
+
}}
|
|
377
393
|
onValueChange={async (next) => {
|
|
378
394
|
await setVisualIdentity.mutateAsync(next);
|
|
379
395
|
}}
|
|
380
|
-
onUpload={
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
kind: "image",
|
|
387
|
-
authority: "url",
|
|
388
|
-
assetId: uploaded.src,
|
|
389
|
-
alt: uploaded.alt || file.name,
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
: undefined
|
|
393
|
-
}
|
|
396
|
+
onUpload={async (file) => {
|
|
397
|
+
const uploaded = await uploadWorkspacePrivateIcon(file);
|
|
398
|
+
void uploadedIcons.refetch();
|
|
399
|
+
return uploaded;
|
|
400
|
+
}}
|
|
401
|
+
formatUploadError={() => iconPickerLabels.uploadFailed}
|
|
394
402
|
resolveImageUrl={(image) =>
|
|
395
|
-
image.
|
|
403
|
+
image.assetId === (icon?.kind === "image" ? icon.assetId : null)
|
|
404
|
+
? workspacePrivateIconUrl(org?.orgId ?? "", image)
|
|
405
|
+
: workspacePrivateIconLibraryUrl(org?.orgId ?? "", image)
|
|
396
406
|
}
|
|
397
407
|
disabled={setVisualIdentity.isPending}
|
|
398
408
|
labels={{
|
|
@@ -439,7 +449,7 @@ export function OrgIconControl({
|
|
|
439
449
|
value={icon}
|
|
440
450
|
size={16}
|
|
441
451
|
resolveImageUrl={(image) =>
|
|
442
|
-
|
|
452
|
+
workspacePrivateIconUrl(org?.orgId ?? "", image)
|
|
443
453
|
}
|
|
444
454
|
fallback={<IconUsersGroup className="size-4 text-muted-foreground" />}
|
|
445
455
|
/>
|
|
@@ -450,7 +460,7 @@ export function OrgIconControl({
|
|
|
450
460
|
value={icon}
|
|
451
461
|
size={16}
|
|
452
462
|
resolveImageUrl={(image) =>
|
|
453
|
-
|
|
463
|
+
workspacePrivateIconUrl(org?.orgId ?? "", image)
|
|
454
464
|
}
|
|
455
465
|
fallback={<IconUsersGroup className="size-4 text-muted-foreground" />}
|
|
456
466
|
/>
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
useJoinByDomain,
|
|
12
12
|
} from "@agent-native/core/client/org";
|
|
13
13
|
import { signOut } from "@agent-native/core/client/sign-out";
|
|
14
|
+
import { workspacePrivateIconUrl } from "@agent-native/core/client/uploads";
|
|
14
15
|
import { setBrowserDemoModeEnabled } from "@agent-native/core/demo/browser-state";
|
|
15
16
|
import { buildSettingsRoute } from "@agent-native/core/navigation";
|
|
16
17
|
import { shouldOfferWorkspace } from "@agent-native/core/org/workspace-url";
|
|
@@ -503,7 +504,7 @@ export function OrgSwitcher({
|
|
|
503
504
|
value={org.icon}
|
|
504
505
|
size={12}
|
|
505
506
|
resolveImageUrl={(image) =>
|
|
506
|
-
|
|
507
|
+
workspacePrivateIconUrl(org.orgId ?? "", image)
|
|
507
508
|
}
|
|
508
509
|
fallback={<IconBriefcase className="size-3 shrink-0" />}
|
|
509
510
|
/>
|
|
@@ -585,7 +586,7 @@ export function OrgSwitcher({
|
|
|
585
586
|
value={o.icon}
|
|
586
587
|
size={14}
|
|
587
588
|
resolveImageUrl={(image) =>
|
|
588
|
-
|
|
589
|
+
workspacePrivateIconUrl(o.orgId, image)
|
|
589
590
|
}
|
|
590
591
|
fallback={<IconBriefcase className={ITEM_ICON_CLASS} />}
|
|
591
592
|
/>
|
|
@@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest";
|
|
|
3
3
|
import {
|
|
4
4
|
defaultOrgAppLinks,
|
|
5
5
|
dispatchAppsHref,
|
|
6
|
+
dispatchIntegrationsHref,
|
|
6
7
|
isWorkspaceAppEnvironment,
|
|
7
8
|
parseWorkspaceAppLinks,
|
|
8
9
|
visibleOrgAppLinks,
|
|
@@ -66,6 +67,9 @@ describe("org switcher app links", () => {
|
|
|
66
67
|
expect(dispatchAppsHref(apps ?? [])).toBe(
|
|
67
68
|
"http://127.0.0.1:8080/dispatch/apps",
|
|
68
69
|
);
|
|
70
|
+
expect(dispatchIntegrationsHref(apps ?? [])).toBe(
|
|
71
|
+
"http://127.0.0.1:8080/dispatch/integrations",
|
|
72
|
+
);
|
|
69
73
|
});
|
|
70
74
|
|
|
71
75
|
it("uses a published custom home path for workspace app links", () => {
|
|
@@ -266,7 +266,7 @@ export function isWorkspaceAppEnvironment(
|
|
|
266
266
|
|
|
267
267
|
function dispatchPageHref(
|
|
268
268
|
apps: OrgSwitcherAppLink[],
|
|
269
|
-
page: "overview" | "apps" | "vault" | "workspace",
|
|
269
|
+
page: "overview" | "apps" | "vault" | "workspace" | "integrations",
|
|
270
270
|
env: RuntimeEnv,
|
|
271
271
|
): string {
|
|
272
272
|
const dispatch = apps.find((app) => app.isDispatch);
|
|
@@ -303,6 +303,13 @@ export function dispatchResourcesHref(
|
|
|
303
303
|
return dispatchPageHref(apps, "workspace", env);
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
export function dispatchIntegrationsHref(
|
|
307
|
+
apps: OrgSwitcherAppLink[],
|
|
308
|
+
env: RuntimeEnv = runtimeEnv(),
|
|
309
|
+
): string {
|
|
310
|
+
return dispatchPageHref(apps, "integrations", env);
|
|
311
|
+
}
|
|
312
|
+
|
|
306
313
|
export function visibleOrgAppLinks(
|
|
307
314
|
apps: OrgSwitcherAppLink[],
|
|
308
315
|
max = ORG_SWITCHER_MAX_APP_LINKS,
|
|
@@ -1,15 +1,79 @@
|
|
|
1
1
|
// @vitest-environment happy-dom
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { openOAuthPopup } from "@agent-native/core/client/oauth-popup";
|
|
4
|
+
import {
|
|
5
|
+
clearMcpConnectionResume,
|
|
6
|
+
consumeMcpConnectionResume,
|
|
7
|
+
notifyMcpConnectionComplete,
|
|
8
|
+
saveMcpConnectionResume,
|
|
9
|
+
} from "@agent-native/core/client/resources/mcp-connection-resume";
|
|
4
10
|
import React, { act } from "react";
|
|
5
11
|
import { createRoot } from "react-dom/client";
|
|
6
|
-
import { describe, expect, it, vi } from "vitest";
|
|
12
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
7
13
|
|
|
14
|
+
import { dispatchIntegrationsHref } from "../org/workspace-app-links.js";
|
|
8
15
|
import {
|
|
9
16
|
McpAgentKitConnectionRequestCard,
|
|
10
17
|
McpAgentKitConnectionResume,
|
|
11
18
|
} from "./McpAgentKitConnectionRequest.js";
|
|
12
19
|
|
|
20
|
+
const popupState = vi.hoisted(() => ({ popup: null as unknown }));
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
clearMcpConnectionResume();
|
|
24
|
+
window.localStorage.clear();
|
|
25
|
+
vi.unstubAllEnvs();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
vi.mock("@agent-native/core/client/oauth-popup", () => ({
|
|
29
|
+
openOAuthPopup: vi.fn(() => popupState.popup as Window | null),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
vi.mock("../org/workspace-app-links.js", async (importOriginal) => {
|
|
33
|
+
const actual =
|
|
34
|
+
await importOriginal<typeof import("../org/workspace-app-links.js")>();
|
|
35
|
+
return {
|
|
36
|
+
...actual,
|
|
37
|
+
useOrgSwitcherAppLinks: () => ({
|
|
38
|
+
apps: [],
|
|
39
|
+
isWorkspace: false,
|
|
40
|
+
isLoading: false,
|
|
41
|
+
dispatchHref: "",
|
|
42
|
+
dispatchAllAppsHref: "",
|
|
43
|
+
dispatchVaultHref: "",
|
|
44
|
+
dispatchResourcesHref: "",
|
|
45
|
+
}),
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
vi.mock("../agentkit/react/components.js", async () => {
|
|
50
|
+
const { createElement } = await import("react");
|
|
51
|
+
return {
|
|
52
|
+
AgentConnectionRequestCard: ({
|
|
53
|
+
request,
|
|
54
|
+
retry,
|
|
55
|
+
onConnect,
|
|
56
|
+
}: {
|
|
57
|
+
request: { provider: string; status: string };
|
|
58
|
+
retry?: boolean;
|
|
59
|
+
onConnect?: () => void | boolean | Promise<void | boolean>;
|
|
60
|
+
}) => {
|
|
61
|
+
if (request.status === "connected" || request.status === "declined") {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return createElement(
|
|
65
|
+
"button",
|
|
66
|
+
{
|
|
67
|
+
type: "button",
|
|
68
|
+
"data-status": request.status,
|
|
69
|
+
onClick: () => void onConnect?.(),
|
|
70
|
+
},
|
|
71
|
+
`${retry ? "Try again" : "Connect"} ${request.provider}`,
|
|
72
|
+
);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
|
|
13
77
|
describe("McpAgentKitConnectionRequestCard", () => {
|
|
14
78
|
it("keeps an unknown provider request visible without trusting its setup data", () => {
|
|
15
79
|
const container = document.createElement("div");
|
|
@@ -37,6 +101,311 @@ describe("McpAgentKitConnectionRequestCard", () => {
|
|
|
37
101
|
act(() => root.unmount());
|
|
38
102
|
});
|
|
39
103
|
|
|
104
|
+
it("keeps the request alive in a popup and resumes it on OAuth completion", async () => {
|
|
105
|
+
window.sessionStorage.clear();
|
|
106
|
+
vi.stubEnv("VITE_APP_BASE_PATH", "/dispatch");
|
|
107
|
+
window.history.replaceState(
|
|
108
|
+
{},
|
|
109
|
+
"",
|
|
110
|
+
"/dispatch/chat/thread-1?tab=docs#selected",
|
|
111
|
+
);
|
|
112
|
+
vi.mocked(openOAuthPopup).mockClear();
|
|
113
|
+
const locationAssign = vi.fn();
|
|
114
|
+
const popup = {
|
|
115
|
+
closed: false,
|
|
116
|
+
location: { assign: locationAssign },
|
|
117
|
+
close: vi.fn(),
|
|
118
|
+
};
|
|
119
|
+
popupState.popup = popup;
|
|
120
|
+
const onConnected = vi.fn();
|
|
121
|
+
const target = {
|
|
122
|
+
threadId: "thread-1",
|
|
123
|
+
runId: "run-1",
|
|
124
|
+
requestId: "request-1",
|
|
125
|
+
};
|
|
126
|
+
const container = document.createElement("div");
|
|
127
|
+
const root = createRoot(container);
|
|
128
|
+
|
|
129
|
+
await act(async () => {
|
|
130
|
+
root.render(
|
|
131
|
+
<>
|
|
132
|
+
<McpAgentKitConnectionRequestCard
|
|
133
|
+
provider="google_drive"
|
|
134
|
+
reason="connect"
|
|
135
|
+
appId="dispatch"
|
|
136
|
+
source={{
|
|
137
|
+
id: "google_drive",
|
|
138
|
+
kind: "workspace_connection",
|
|
139
|
+
label: "Google Drive",
|
|
140
|
+
}}
|
|
141
|
+
target={target}
|
|
142
|
+
onConnected={onConnected}
|
|
143
|
+
onDeclined={() => undefined}
|
|
144
|
+
/>
|
|
145
|
+
<McpAgentKitConnectionResume
|
|
146
|
+
onResume={() => onConnected()}
|
|
147
|
+
onMessageResume={() => undefined}
|
|
148
|
+
/>
|
|
149
|
+
</>,
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
const button = container.querySelector("button");
|
|
153
|
+
await act(async () => button?.click());
|
|
154
|
+
|
|
155
|
+
expect(openOAuthPopup).toHaveBeenCalledWith({
|
|
156
|
+
features: "width=640,height=760",
|
|
157
|
+
});
|
|
158
|
+
const oauthUrl = new URL(
|
|
159
|
+
locationAssign.mock.calls[0]![0],
|
|
160
|
+
window.location.origin,
|
|
161
|
+
);
|
|
162
|
+
expect(oauthUrl.pathname).toContain(
|
|
163
|
+
"/connections/oauth/google_drive/start",
|
|
164
|
+
);
|
|
165
|
+
expect(oauthUrl.searchParams.get("appId")).toBe("dispatch");
|
|
166
|
+
expect(oauthUrl.searchParams.get("scope")).toBe("user");
|
|
167
|
+
const returnPath = oauthUrl.searchParams.get("return");
|
|
168
|
+
expect(returnPath).toContain(
|
|
169
|
+
"/_agent-native/oauth/popup?complete=workspace-connection&resume=",
|
|
170
|
+
);
|
|
171
|
+
expect(returnPath).not.toContain("#selected");
|
|
172
|
+
expect(consumeMcpConnectionResume()).toBeNull();
|
|
173
|
+
const completionId = new URL(
|
|
174
|
+
returnPath!,
|
|
175
|
+
window.location.origin,
|
|
176
|
+
).searchParams.get("resume");
|
|
177
|
+
expect(completionId).toMatch(
|
|
178
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/,
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
await act(async () => {
|
|
182
|
+
window.dispatchEvent(
|
|
183
|
+
new MessageEvent("message", {
|
|
184
|
+
data: { type: "agent-native:workspace-connection-complete" },
|
|
185
|
+
origin: "https://untrusted.example",
|
|
186
|
+
source: popup as unknown as MessageEventSource,
|
|
187
|
+
}),
|
|
188
|
+
);
|
|
189
|
+
window.dispatchEvent(
|
|
190
|
+
new MessageEvent("message", {
|
|
191
|
+
data: { type: "agent-native:workspace-connection-complete" },
|
|
192
|
+
origin: window.location.origin,
|
|
193
|
+
source: null,
|
|
194
|
+
}),
|
|
195
|
+
);
|
|
196
|
+
await Promise.resolve();
|
|
197
|
+
});
|
|
198
|
+
expect(onConnected).not.toHaveBeenCalled();
|
|
199
|
+
|
|
200
|
+
act(() => root.unmount());
|
|
201
|
+
expect(popup.close).not.toHaveBeenCalled();
|
|
202
|
+
const resumedRoot = createRoot(container);
|
|
203
|
+
await act(async () => {
|
|
204
|
+
notifyMcpConnectionComplete(completionId!);
|
|
205
|
+
resumedRoot.render(
|
|
206
|
+
<McpAgentKitConnectionResume
|
|
207
|
+
onResume={() => onConnected()}
|
|
208
|
+
onMessageResume={() => undefined}
|
|
209
|
+
/>,
|
|
210
|
+
);
|
|
211
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
212
|
+
});
|
|
213
|
+
expect(onConnected).toHaveBeenCalledOnce();
|
|
214
|
+
act(() => resumedRoot.unmount());
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("keeps a granted connection request pending until the user retries", async () => {
|
|
218
|
+
const container = document.createElement("div");
|
|
219
|
+
const root = createRoot(container);
|
|
220
|
+
vi.mocked(openOAuthPopup).mockClear();
|
|
221
|
+
const locationAssign = vi.fn();
|
|
222
|
+
const popup = {
|
|
223
|
+
opener: window,
|
|
224
|
+
location: { assign: locationAssign },
|
|
225
|
+
close: vi.fn(),
|
|
226
|
+
} as unknown as Window;
|
|
227
|
+
const open = vi
|
|
228
|
+
.spyOn(window, "open")
|
|
229
|
+
.mockReturnValueOnce(null)
|
|
230
|
+
.mockReturnValue(popup);
|
|
231
|
+
const onConnected = vi.fn();
|
|
232
|
+
|
|
233
|
+
act(() => {
|
|
234
|
+
root.render(
|
|
235
|
+
<McpAgentKitConnectionRequestCard
|
|
236
|
+
provider="google_drive"
|
|
237
|
+
reason="grant"
|
|
238
|
+
appId="dispatch"
|
|
239
|
+
source={{
|
|
240
|
+
id: "google_drive",
|
|
241
|
+
kind: "workspace_connection",
|
|
242
|
+
label: "Google Drive",
|
|
243
|
+
}}
|
|
244
|
+
target={{
|
|
245
|
+
threadId: "thread-1",
|
|
246
|
+
runId: "run-1",
|
|
247
|
+
requestId: "request-1",
|
|
248
|
+
}}
|
|
249
|
+
onConnected={onConnected}
|
|
250
|
+
onDeclined={() => undefined}
|
|
251
|
+
/>,
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
expect(container.textContent).toContain("Connect google_drive");
|
|
256
|
+
expect(openOAuthPopup).not.toHaveBeenCalled();
|
|
257
|
+
await act(async () => container.querySelector("button")?.click());
|
|
258
|
+
expect(open).toHaveBeenCalledWith("", "_blank");
|
|
259
|
+
expect(onConnected).not.toHaveBeenCalled();
|
|
260
|
+
expect(container.textContent).toContain("Connect google_drive");
|
|
261
|
+
|
|
262
|
+
await act(async () => container.querySelector("button")?.click());
|
|
263
|
+
expect(open).toHaveBeenCalledTimes(2);
|
|
264
|
+
expect(popup.opener).toBeNull();
|
|
265
|
+
expect(locationAssign).toHaveBeenCalledWith(dispatchIntegrationsHref([]));
|
|
266
|
+
expect(container.textContent).toContain("Try again google_drive");
|
|
267
|
+
expect(onConnected).not.toHaveBeenCalled();
|
|
268
|
+
await act(async () => container.querySelector("button")?.click());
|
|
269
|
+
expect(onConnected).toHaveBeenCalledOnce();
|
|
270
|
+
|
|
271
|
+
await act(async () => {
|
|
272
|
+
root.render(
|
|
273
|
+
<McpAgentKitConnectionRequestCard
|
|
274
|
+
provider="google_drive"
|
|
275
|
+
reason="grant"
|
|
276
|
+
status="connected"
|
|
277
|
+
appId="dispatch"
|
|
278
|
+
source={{
|
|
279
|
+
id: "google_drive",
|
|
280
|
+
kind: "workspace_connection",
|
|
281
|
+
label: "Google Drive",
|
|
282
|
+
}}
|
|
283
|
+
target={{
|
|
284
|
+
threadId: "thread-1",
|
|
285
|
+
runId: "run-1",
|
|
286
|
+
requestId: "request-1",
|
|
287
|
+
}}
|
|
288
|
+
onConnected={onConnected}
|
|
289
|
+
onDeclined={() => undefined}
|
|
290
|
+
/>,
|
|
291
|
+
);
|
|
292
|
+
});
|
|
293
|
+
expect(container.querySelector("button")).toBeNull();
|
|
294
|
+
|
|
295
|
+
await act(async () => {
|
|
296
|
+
root.render(
|
|
297
|
+
<McpAgentKitConnectionRequestCard
|
|
298
|
+
provider="google_drive"
|
|
299
|
+
reason="grant"
|
|
300
|
+
status="failed"
|
|
301
|
+
appId="dispatch"
|
|
302
|
+
source={{
|
|
303
|
+
id: "google_drive",
|
|
304
|
+
kind: "workspace_connection",
|
|
305
|
+
label: "Google Drive",
|
|
306
|
+
}}
|
|
307
|
+
target={{
|
|
308
|
+
threadId: "thread-1",
|
|
309
|
+
runId: "run-1",
|
|
310
|
+
requestId: "request-1",
|
|
311
|
+
}}
|
|
312
|
+
onConnected={onConnected}
|
|
313
|
+
onDeclined={() => undefined}
|
|
314
|
+
/>,
|
|
315
|
+
);
|
|
316
|
+
});
|
|
317
|
+
expect(container.querySelector("button")?.getAttribute("data-status")).toBe(
|
|
318
|
+
"failed",
|
|
319
|
+
);
|
|
320
|
+
await act(async () => container.querySelector("button")?.click());
|
|
321
|
+
expect(open).toHaveBeenCalledTimes(3);
|
|
322
|
+
expect(onConnected).toHaveBeenCalledOnce();
|
|
323
|
+
await act(async () => container.querySelector("button")?.click());
|
|
324
|
+
expect(onConnected).toHaveBeenCalledTimes(2);
|
|
325
|
+
act(() => root.unmount());
|
|
326
|
+
open.mockRestore();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it("keeps non-OAuth workspace requests pending until the user retries", async () => {
|
|
330
|
+
const container = document.createElement("div");
|
|
331
|
+
const root = createRoot(container);
|
|
332
|
+
vi.mocked(openOAuthPopup).mockClear();
|
|
333
|
+
const locationAssign = vi.fn();
|
|
334
|
+
const popup = {
|
|
335
|
+
opener: window,
|
|
336
|
+
location: { assign: locationAssign },
|
|
337
|
+
close: vi.fn(),
|
|
338
|
+
} as unknown as Window;
|
|
339
|
+
const open = vi.spyOn(window, "open").mockReturnValue(popup);
|
|
340
|
+
const onConnected = vi.fn();
|
|
341
|
+
|
|
342
|
+
act(() => {
|
|
343
|
+
root.render(
|
|
344
|
+
<McpAgentKitConnectionRequestCard
|
|
345
|
+
provider="slack"
|
|
346
|
+
appId="dispatch"
|
|
347
|
+
source={{
|
|
348
|
+
id: "slack",
|
|
349
|
+
kind: "workspace_connection",
|
|
350
|
+
label: "Slack",
|
|
351
|
+
}}
|
|
352
|
+
target={{
|
|
353
|
+
threadId: "thread-1",
|
|
354
|
+
runId: "run-1",
|
|
355
|
+
requestId: "request-1",
|
|
356
|
+
}}
|
|
357
|
+
onConnected={onConnected}
|
|
358
|
+
onDeclined={() => undefined}
|
|
359
|
+
fallback={<div data-unsupported-provider="">Setup unavailable</div>}
|
|
360
|
+
/>,
|
|
361
|
+
);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
expect(container.textContent).toContain("Connect slack");
|
|
365
|
+
expect(openOAuthPopup).not.toHaveBeenCalled();
|
|
366
|
+
await act(async () => container.querySelector("button")?.click());
|
|
367
|
+
expect(open).toHaveBeenCalledWith("", "_blank");
|
|
368
|
+
expect(popup.opener).toBeNull();
|
|
369
|
+
expect(locationAssign).toHaveBeenCalledWith(dispatchIntegrationsHref([]));
|
|
370
|
+
expect(onConnected).not.toHaveBeenCalled();
|
|
371
|
+
expect(container.textContent).toContain("Try again slack");
|
|
372
|
+
await act(async () => container.querySelector("button")?.click());
|
|
373
|
+
expect(onConnected).toHaveBeenCalledOnce();
|
|
374
|
+
act(() => root.unmount());
|
|
375
|
+
open.mockRestore();
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it("passes terminal request state through to the shared card", () => {
|
|
379
|
+
const container = document.createElement("div");
|
|
380
|
+
const root = createRoot(container);
|
|
381
|
+
|
|
382
|
+
act(() => {
|
|
383
|
+
root.render(
|
|
384
|
+
<McpAgentKitConnectionRequestCard
|
|
385
|
+
provider="google_drive"
|
|
386
|
+
reason="connect"
|
|
387
|
+
status="declined"
|
|
388
|
+
appId="dispatch"
|
|
389
|
+
source={{
|
|
390
|
+
id: "google_drive",
|
|
391
|
+
kind: "workspace_connection",
|
|
392
|
+
label: "Google Drive",
|
|
393
|
+
}}
|
|
394
|
+
target={{
|
|
395
|
+
threadId: "thread-1",
|
|
396
|
+
runId: "run-1",
|
|
397
|
+
requestId: "request-1",
|
|
398
|
+
}}
|
|
399
|
+
onConnected={() => undefined}
|
|
400
|
+
onDeclined={() => undefined}
|
|
401
|
+
/>,
|
|
402
|
+
);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
expect(container.querySelector("button")).toBeNull();
|
|
406
|
+
act(() => root.unmount());
|
|
407
|
+
});
|
|
408
|
+
|
|
40
409
|
it("resumes a prose-inferred connection request through the host thread", async () => {
|
|
41
410
|
window.sessionStorage.clear();
|
|
42
411
|
window.history.replaceState({}, "", "/chat/thread-1");
|