@copilotkit/vue 1.69.2 → 1.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PARITY.md +1 -1
- package/README.md +7 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +14 -14
- package/dist/use-render-activity-message-h2NHCkBt.cjs +85 -0
- package/dist/use-render-activity-message-h2NHCkBt.cjs.map +1 -0
- package/dist/{use-render-activity-message-Db2rOZkW.js → use-render-activity-message-ytoSQi6V.js} +3120 -3082
- package/dist/use-render-activity-message-ytoSQi6V.js.map +1 -0
- package/dist/v2/components/CopilotKitInspector.vue.d.ts +2 -0
- package/dist/v2/components/CopilotKitInspector.vue.d.ts.map +1 -1
- package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts +40 -16
- package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts.map +1 -1
- package/dist/v2/index.cjs +1 -1
- package/dist/v2/index.mjs +1 -1
- package/dist/v2/providers/CopilotKitProvider.types.d.ts +10 -9
- package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -1
- package/dist/v2/providers/CopilotKitProvider.vue.d.ts +3 -3
- package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
- package/dist/v2/providers/keys.d.ts +10 -0
- package/dist/v2/providers/keys.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/v2/__tests__/mocks/web-inspector.ts +32 -1
- package/src/v2/components/CopilotKitInspector.vue +41 -3
- package/src/v2/components/__tests__/CopilotKitInspector.test.ts +31 -6
- package/src/v2/hooks/__tests__/use-threads.test.ts +24 -1
- package/src/v2/hooks/use-threads.ts +1 -1
- package/src/v2/providers/CopilotKitProvider.types.ts +10 -9
- package/src/v2/providers/CopilotKitProvider.vue +94 -37
- package/src/v2/providers/__tests__/CopilotKitProvider.managedEntitlement.test.ts +290 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.test.ts +148 -37
- package/src/v2/providers/keys.ts +15 -0
- package/dist/use-render-activity-message-CIlNtfvE.cjs +0 -85
- package/dist/use-render-activity-message-CIlNtfvE.cjs.map +0 -1
- package/dist/use-render-activity-message-Db2rOZkW.js.map +0 -1
|
@@ -15,8 +15,11 @@ import type {
|
|
|
15
15
|
CopilotKitCoreSubscriber,
|
|
16
16
|
FrontendTool,
|
|
17
17
|
} from "@copilotkit/core";
|
|
18
|
-
import { schemaToJsonSchema } from "@copilotkit/shared";
|
|
19
|
-
import type {
|
|
18
|
+
import { schemaToJsonSchema, shouldEnableInspector } from "@copilotkit/shared";
|
|
19
|
+
import type {
|
|
20
|
+
RuntimeEntitlementResponse,
|
|
21
|
+
RuntimeLicenseStatus,
|
|
22
|
+
} from "@copilotkit/shared";
|
|
20
23
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
21
24
|
import { CopilotKitCoreVue } from "../lib/vue-core";
|
|
22
25
|
import { createA2UIMessageRenderer } from "../components/A2UIMessageRenderer";
|
|
@@ -34,7 +37,8 @@ import {
|
|
|
34
37
|
MCPAppsActivityRenderer,
|
|
35
38
|
MCPAppsActivityType,
|
|
36
39
|
} from "../components/MCPAppsActivityRenderer";
|
|
37
|
-
import { CopilotKitKey, SandboxFunctionsKey } from "./keys";
|
|
40
|
+
import { CopilotKitKey, InspectorKey, SandboxFunctionsKey } from "./keys";
|
|
41
|
+
import type { VueInspectorOpenRequest } from "./keys";
|
|
38
42
|
import {
|
|
39
43
|
LicenseContextKey,
|
|
40
44
|
createLicenseContextValue,
|
|
@@ -47,13 +51,11 @@ import type {
|
|
|
47
51
|
SandboxFunction,
|
|
48
52
|
VueActivityMessageRenderer,
|
|
49
53
|
VueFrontendTool,
|
|
50
|
-
VueHumanInTheLoop,
|
|
51
54
|
VueToolCallRenderer,
|
|
52
55
|
} from "../types";
|
|
53
56
|
|
|
54
57
|
const HEADER_NAME = "X-CopilotCloud-Public-Api-Key";
|
|
55
58
|
const COPILOT_CLOUD_CHAT_URL = "https://api.cloud.copilotkit.ai/copilotkit/v1";
|
|
56
|
-
|
|
57
59
|
// Canonical A2UI viewer theme default (matches @copilotkit/a2ui-renderer).
|
|
58
60
|
// Defined locally to avoid pulling React dependencies from a2ui-renderer.
|
|
59
61
|
const viewerTheme: Record<string, unknown> = {};
|
|
@@ -103,32 +105,34 @@ const props = withDefaults(defineProps<CopilotKitProviderProps>(), {
|
|
|
103
105
|
renderCustomMessages: () => [],
|
|
104
106
|
renderActivityMessages: () => [],
|
|
105
107
|
openGenerativeUI: undefined,
|
|
106
|
-
showDevConsole: false,
|
|
107
108
|
useSingleEndpoint: undefined,
|
|
108
109
|
a2ui: undefined,
|
|
110
|
+
enableInspector: undefined,
|
|
109
111
|
});
|
|
110
112
|
|
|
111
113
|
const shouldRenderInspector = ref(false);
|
|
112
114
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const localhostHosts = new Set(["localhost", "127.0.0.1"]);
|
|
124
|
-
shouldRenderInspector.value = localhostHosts.has(window.location.hostname);
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
shouldRenderInspector.value = false;
|
|
128
|
-
};
|
|
115
|
+
function updateInspectorVisibility(): void {
|
|
116
|
+
shouldRenderInspector.value = shouldEnableInspector({
|
|
117
|
+
enableInspector: props.enableInspector,
|
|
118
|
+
isBrowser: true,
|
|
119
|
+
isDevelopment: process.env.NODE_ENV === "development",
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
onMounted(updateInspectorVisibility);
|
|
124
|
+
watch(() => props.enableInspector, updateInspectorVisibility);
|
|
129
125
|
|
|
130
|
-
|
|
131
|
-
|
|
126
|
+
const inspectorOpenRequest = ref<VueInspectorOpenRequest | null>(null);
|
|
127
|
+
const isInspectorEnabled = computed(() => shouldRenderInspector.value);
|
|
128
|
+
|
|
129
|
+
function openInspector(request: VueInspectorOpenRequest) {
|
|
130
|
+
inspectorOpenRequest.value = { ...request };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
provide(InspectorKey, {
|
|
134
|
+
isInspectorEnabled,
|
|
135
|
+
openInspector,
|
|
132
136
|
});
|
|
133
137
|
|
|
134
138
|
const initialFrontendTools = props.frontendTools;
|
|
@@ -292,6 +296,10 @@ const allRenderCustomMessages = computed(
|
|
|
292
296
|
const runtimeA2UIEnabled = ref(false);
|
|
293
297
|
const runtimeOpenGenerativeUIEnabled = ref(false);
|
|
294
298
|
const runtimeLicenseStatus = ref<RuntimeLicenseStatus | undefined>(undefined);
|
|
299
|
+
const runtimeEntitlements = ref<RuntimeEntitlementResponse | undefined>(
|
|
300
|
+
undefined,
|
|
301
|
+
);
|
|
302
|
+
const runtimeEntitlementRetryPending = ref(false);
|
|
295
303
|
const openGenerativeUIActive = computed(
|
|
296
304
|
() => runtimeOpenGenerativeUIEnabled.value || !!props.openGenerativeUI,
|
|
297
305
|
);
|
|
@@ -458,15 +466,14 @@ watch(
|
|
|
458
466
|
runtimeA2UIEnabled.value = core.a2uiEnabled;
|
|
459
467
|
runtimeOpenGenerativeUIEnabled.value = core.openGenerativeUIEnabled;
|
|
460
468
|
runtimeLicenseStatus.value = core.licenseStatus;
|
|
469
|
+
runtimeEntitlements.value = core.runtimeEntitlements;
|
|
470
|
+
runtimeEntitlementRetryPending.value =
|
|
471
|
+
core.runtimeEntitlementRetryPending;
|
|
461
472
|
triggerRef(copilotkit);
|
|
462
473
|
},
|
|
463
474
|
});
|
|
464
475
|
const sub5 = core.subscribe({
|
|
465
|
-
onError: (event
|
|
466
|
-
error: Error;
|
|
467
|
-
code: CopilotKitCoreErrorCode;
|
|
468
|
-
context: Record<string, any>;
|
|
469
|
-
}) => {
|
|
476
|
+
onError: (event) => {
|
|
470
477
|
void props.onError?.({
|
|
471
478
|
error: event.error,
|
|
472
479
|
code: event.code,
|
|
@@ -555,6 +562,9 @@ onMounted(() => {
|
|
|
555
562
|
runtimeOpenGenerativeUIEnabled.value =
|
|
556
563
|
copilotkit.value.openGenerativeUIEnabled;
|
|
557
564
|
runtimeLicenseStatus.value = copilotkit.value.licenseStatus;
|
|
565
|
+
runtimeEntitlements.value = copilotkit.value.runtimeEntitlements;
|
|
566
|
+
runtimeEntitlementRetryPending.value =
|
|
567
|
+
copilotkit.value.runtimeEntitlementRetryPending;
|
|
558
568
|
didMountRef.value = true;
|
|
559
569
|
});
|
|
560
570
|
|
|
@@ -631,23 +641,70 @@ provide(CopilotKitKey, {
|
|
|
631
641
|
});
|
|
632
642
|
provide(SandboxFunctionsKey, sandboxFunctions);
|
|
633
643
|
|
|
634
|
-
// License context — driven by
|
|
635
|
-
const
|
|
636
|
-
|
|
644
|
+
// License context — driven by structured and legacy Runtime authority.
|
|
645
|
+
const retryableRuntimeEntitlementFailure = computed(
|
|
646
|
+
() =>
|
|
647
|
+
runtimeEntitlements.value?.status !== "ready" &&
|
|
648
|
+
runtimeEntitlements.value?.error.retryable === true,
|
|
637
649
|
);
|
|
650
|
+
const hasNonReadyRuntimeEntitlement = computed(
|
|
651
|
+
() =>
|
|
652
|
+
runtimeEntitlements.value !== undefined &&
|
|
653
|
+
runtimeEntitlements.value.status !== "ready",
|
|
654
|
+
);
|
|
655
|
+
const hasLegacyRuntimeEntitlementFallback = computed(
|
|
656
|
+
() =>
|
|
657
|
+
runtimeLicenseStatus.value === "valid" ||
|
|
658
|
+
runtimeLicenseStatus.value === "expiring",
|
|
659
|
+
);
|
|
660
|
+
const runtimeEntitlementRetryInProgress = computed(
|
|
661
|
+
() =>
|
|
662
|
+
retryableRuntimeEntitlementFailure.value &&
|
|
663
|
+
runtimeEntitlementRetryPending.value &&
|
|
664
|
+
!hasLegacyRuntimeEntitlementFallback.value,
|
|
665
|
+
);
|
|
666
|
+
const runtimeEntitlementFailureSettled = computed(
|
|
667
|
+
() =>
|
|
668
|
+
hasNonReadyRuntimeEntitlement.value &&
|
|
669
|
+
!runtimeEntitlementRetryInProgress.value &&
|
|
670
|
+
!hasLegacyRuntimeEntitlementFallback.value,
|
|
671
|
+
);
|
|
672
|
+
const licenseContextValue = computed<LicenseContextValue>(() => {
|
|
673
|
+
const runtimeLicenseContext = createLicenseContextValue(
|
|
674
|
+
runtimeEntitlementRetryInProgress.value
|
|
675
|
+
? undefined
|
|
676
|
+
: runtimeLicenseStatus.value,
|
|
677
|
+
runtimeEntitlements.value,
|
|
678
|
+
);
|
|
679
|
+
if (!runtimeEntitlementFailureSettled.value) {
|
|
680
|
+
return runtimeLicenseContext;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
return {
|
|
684
|
+
...runtimeLicenseContext,
|
|
685
|
+
checkFeature: () => false,
|
|
686
|
+
getLimit: () => null,
|
|
687
|
+
};
|
|
688
|
+
});
|
|
638
689
|
provide(LicenseContextKey, licenseContextValue);
|
|
639
690
|
|
|
691
|
+
const runtimeLicenseWarningStatus = computed(() =>
|
|
692
|
+
runtimeEntitlementRetryInProgress.value
|
|
693
|
+
? undefined
|
|
694
|
+
: (licenseContextValue.value.status ?? undefined),
|
|
695
|
+
);
|
|
640
696
|
const showNoLicenseBanner = computed(
|
|
641
|
-
() =>
|
|
697
|
+
() =>
|
|
698
|
+
runtimeLicenseWarningStatus.value === "none" && !resolvedPublicKey.value,
|
|
642
699
|
);
|
|
643
700
|
const showExpiredBanner = computed(
|
|
644
|
-
() =>
|
|
701
|
+
() => runtimeLicenseWarningStatus.value === "expired",
|
|
645
702
|
);
|
|
646
703
|
const showInvalidBanner = computed(
|
|
647
|
-
() =>
|
|
704
|
+
() => runtimeLicenseWarningStatus.value === "invalid",
|
|
648
705
|
);
|
|
649
706
|
const showExpiringBanner = computed(
|
|
650
|
-
() =>
|
|
707
|
+
() => runtimeLicenseWarningStatus.value === "expiring",
|
|
651
708
|
);
|
|
652
709
|
</script>
|
|
653
710
|
|
|
@@ -656,7 +713,7 @@ const showExpiringBanner = computed(
|
|
|
656
713
|
<CopilotKitInspector
|
|
657
714
|
v-if="shouldRenderInspector"
|
|
658
715
|
:core="copilotkit"
|
|
659
|
-
:
|
|
716
|
+
:open-request="inspectorOpenRequest"
|
|
660
717
|
/>
|
|
661
718
|
<!-- License warnings — driven by server-reported status -->
|
|
662
719
|
<LicenseWarningBanner v-if="showNoLicenseBanner" type="no_license" />
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { cleanup, render, waitFor } from "@testing-library/vue";
|
|
2
|
+
import type {
|
|
3
|
+
RuntimeEntitlementResponse,
|
|
4
|
+
RuntimeInfo,
|
|
5
|
+
} from "@copilotkit/shared";
|
|
6
|
+
import { defineComponent, h, nextTick } from "vue";
|
|
7
|
+
import { expect, test, vi } from "vitest";
|
|
8
|
+
import CopilotKitProvider from "../CopilotKitProvider.vue";
|
|
9
|
+
import { useLicenseContext } from "../useLicenseContext";
|
|
10
|
+
|
|
11
|
+
const AuthorityProbe = defineComponent({
|
|
12
|
+
setup() {
|
|
13
|
+
const license = useLicenseContext();
|
|
14
|
+
return () =>
|
|
15
|
+
h(
|
|
16
|
+
"output",
|
|
17
|
+
{ "data-testid": "runtime-license-authority" },
|
|
18
|
+
[
|
|
19
|
+
`status:${license.value.status ?? "null"}`,
|
|
20
|
+
`chat:${license.value.checkFeature("chat")}`,
|
|
21
|
+
`sidebar:${license.value.checkFeature("sidebar")}`,
|
|
22
|
+
`popup:${license.value.checkFeature("popup")}`,
|
|
23
|
+
`threads:${license.value.checkFeature("threads")}`,
|
|
24
|
+
].join(" "),
|
|
25
|
+
);
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
function managedRuntimeInfo(
|
|
30
|
+
active: boolean,
|
|
31
|
+
licenseStatus: RuntimeInfo["licenseStatus"],
|
|
32
|
+
): RuntimeInfo {
|
|
33
|
+
return {
|
|
34
|
+
version: "1.0.0",
|
|
35
|
+
agents: {},
|
|
36
|
+
audioFileTranscriptionEnabled: false,
|
|
37
|
+
mode: "intelligence",
|
|
38
|
+
licenseStatus,
|
|
39
|
+
runtimeEntitlements: {
|
|
40
|
+
status: "ready",
|
|
41
|
+
entitlement: {
|
|
42
|
+
active,
|
|
43
|
+
source: "managedOrgSubscription",
|
|
44
|
+
// Mirrors the active/inactive free-plan payload emitted by Intelligence.
|
|
45
|
+
...(active
|
|
46
|
+
? {
|
|
47
|
+
planCode: "free",
|
|
48
|
+
entitlementSource: "clerk_free_default",
|
|
49
|
+
features: {
|
|
50
|
+
"sdk.angular": false,
|
|
51
|
+
deployment_via_helm_chart: false,
|
|
52
|
+
analytics: true,
|
|
53
|
+
self_learning: true,
|
|
54
|
+
msteams: false,
|
|
55
|
+
memory: false,
|
|
56
|
+
managed_channels: true,
|
|
57
|
+
"managed_channels.slack": true,
|
|
58
|
+
"managed_channels.teams": true,
|
|
59
|
+
},
|
|
60
|
+
limits: {
|
|
61
|
+
"threads.retention_hours": 72,
|
|
62
|
+
"threads.max_count": 200,
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
: { features: {}, limits: {} }),
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Build Runtime info mirroring the active self-hosted producer payload. */
|
|
72
|
+
function selfHostedRuntimeInfo(
|
|
73
|
+
licenseStatus: RuntimeInfo["licenseStatus"],
|
|
74
|
+
): RuntimeInfo {
|
|
75
|
+
return {
|
|
76
|
+
...managedRuntimeInfo(true, licenseStatus),
|
|
77
|
+
runtimeEntitlements: {
|
|
78
|
+
status: "ready",
|
|
79
|
+
entitlement: {
|
|
80
|
+
active: true,
|
|
81
|
+
source: "selfHostedDeploymentLicense",
|
|
82
|
+
features: { deployment_via_helm_chart: true, msteams: true },
|
|
83
|
+
limits: {
|
|
84
|
+
"threads.retention_hours": 336,
|
|
85
|
+
"threads.max_count": 25_000,
|
|
86
|
+
},
|
|
87
|
+
planCode: "team_self_hosted",
|
|
88
|
+
entitlementSource: "enterprise_override",
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function retryableRuntimeInfo(
|
|
95
|
+
licenseStatus: RuntimeInfo["licenseStatus"] = "unknown",
|
|
96
|
+
): RuntimeInfo {
|
|
97
|
+
const runtimeEntitlements: RuntimeEntitlementResponse = {
|
|
98
|
+
status: "unavailable",
|
|
99
|
+
error: {
|
|
100
|
+
code: "runtime_entitlements_unavailable",
|
|
101
|
+
message: "Runtime entitlement lookup failed",
|
|
102
|
+
retryable: true,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
return {
|
|
106
|
+
...managedRuntimeInfo(true, licenseStatus),
|
|
107
|
+
runtimeEntitlements,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
interface RuntimeAuthoritySetup {
|
|
112
|
+
dispose: () => void;
|
|
113
|
+
fetchMock: ReturnType<typeof vi.fn<typeof globalThis.fetch>>;
|
|
114
|
+
readAuthority: () => string;
|
|
115
|
+
view: ReturnType<typeof render>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Render the provider against ordered `/info` responses.
|
|
120
|
+
*/
|
|
121
|
+
function setupRuntimeAuthority(
|
|
122
|
+
...runtimeInfoResponses: [RuntimeInfo, ...RuntimeInfo[]]
|
|
123
|
+
): RuntimeAuthoritySetup {
|
|
124
|
+
const fetchMock = vi.fn<typeof globalThis.fetch>();
|
|
125
|
+
for (const runtimeInfo of runtimeInfoResponses) {
|
|
126
|
+
fetchMock.mockResolvedValueOnce(
|
|
127
|
+
new Response(JSON.stringify(runtimeInfo), {
|
|
128
|
+
status: 200,
|
|
129
|
+
headers: { "content-type": "application/json" },
|
|
130
|
+
}),
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
134
|
+
|
|
135
|
+
const view = render(CopilotKitProvider, {
|
|
136
|
+
props: { runtimeUrl: "/api" },
|
|
137
|
+
slots: { default: () => h(AuthorityProbe) },
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
dispose() {
|
|
142
|
+
view.unmount();
|
|
143
|
+
cleanup();
|
|
144
|
+
vi.unstubAllGlobals();
|
|
145
|
+
},
|
|
146
|
+
fetchMock,
|
|
147
|
+
readAuthority: () =>
|
|
148
|
+
view.getByTestId("runtime-license-authority").textContent ?? "",
|
|
149
|
+
view,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function flushPromiseUpdates(): Promise<void> {
|
|
154
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
155
|
+
await nextTick();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
test("a ready managed grant drives every Vue feature gate and overrides a stale invalid token", async () => {
|
|
159
|
+
const { dispose, readAuthority, view } = setupRuntimeAuthority(
|
|
160
|
+
managedRuntimeInfo(true, "invalid"),
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
await waitFor(() => {
|
|
165
|
+
expect(readAuthority()).toBe(
|
|
166
|
+
"status:valid chat:true sidebar:true popup:true threads:true",
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
expect(view.queryByText(/Invalid CopilotKit license token/i)).toBeNull();
|
|
170
|
+
} finally {
|
|
171
|
+
dispose();
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("a ready managed denial drives every Vue feature gate", async () => {
|
|
176
|
+
const { dispose, readAuthority } = setupRuntimeAuthority(
|
|
177
|
+
managedRuntimeInfo(false, "none"),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
await waitFor(() => {
|
|
182
|
+
expect(readAuthority()).toBe(
|
|
183
|
+
"status:none chat:false sidebar:false popup:false threads:false",
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
} finally {
|
|
187
|
+
dispose();
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("an active self-hosted producer entitlement drives every Vue feature gate", async () => {
|
|
192
|
+
const { dispose, readAuthority } = setupRuntimeAuthority(
|
|
193
|
+
selfHostedRuntimeInfo("invalid"),
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
await waitFor(() => {
|
|
198
|
+
expect(readAuthority()).toBe(
|
|
199
|
+
"status:valid chat:true sidebar:true popup:true threads:true",
|
|
200
|
+
);
|
|
201
|
+
});
|
|
202
|
+
} finally {
|
|
203
|
+
dispose();
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("a retryable managed lookup keeps every Vue gate pending and hides stale warnings until recovery", async () => {
|
|
208
|
+
vi.useFakeTimers();
|
|
209
|
+
const { dispose, fetchMock, readAuthority, view } = setupRuntimeAuthority(
|
|
210
|
+
retryableRuntimeInfo("invalid"),
|
|
211
|
+
managedRuntimeInfo(true, "invalid"),
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
await flushPromiseUpdates();
|
|
216
|
+
|
|
217
|
+
expect(fetchMock).toHaveBeenCalledOnce();
|
|
218
|
+
expect(readAuthority()).toBe(
|
|
219
|
+
"status:null chat:true sidebar:true popup:true threads:true",
|
|
220
|
+
);
|
|
221
|
+
expect(view.queryByText(/Invalid CopilotKit license token/i)).toBeNull();
|
|
222
|
+
|
|
223
|
+
await vi.advanceTimersByTimeAsync(5_000);
|
|
224
|
+
await nextTick();
|
|
225
|
+
|
|
226
|
+
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
227
|
+
expect(readAuthority()).toBe(
|
|
228
|
+
"status:valid chat:true sidebar:true popup:true threads:true",
|
|
229
|
+
);
|
|
230
|
+
} finally {
|
|
231
|
+
dispose();
|
|
232
|
+
vi.useRealTimers();
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("a persistent retryable lookup denies every Vue gate after Core's bounded retry settles", async () => {
|
|
237
|
+
vi.useFakeTimers();
|
|
238
|
+
const { dispose, fetchMock, readAuthority } = setupRuntimeAuthority(
|
|
239
|
+
retryableRuntimeInfo(),
|
|
240
|
+
retryableRuntimeInfo(),
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
await flushPromiseUpdates();
|
|
245
|
+
|
|
246
|
+
expect(readAuthority()).toBe(
|
|
247
|
+
"status:null chat:true sidebar:true popup:true threads:true",
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
await vi.advanceTimersByTimeAsync(5_000);
|
|
251
|
+
await nextTick();
|
|
252
|
+
|
|
253
|
+
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
254
|
+
expect(readAuthority()).toBe(
|
|
255
|
+
"status:unknown chat:false sidebar:false popup:false threads:false",
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
await vi.advanceTimersByTimeAsync(30_000);
|
|
259
|
+
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
260
|
+
} finally {
|
|
261
|
+
dispose();
|
|
262
|
+
vi.useRealTimers();
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("an inactive self-hosted entitlement keeps Vue's valid legacy fallback", async () => {
|
|
267
|
+
const runtimeInfo: RuntimeInfo = {
|
|
268
|
+
...managedRuntimeInfo(false, "valid"),
|
|
269
|
+
runtimeEntitlements: {
|
|
270
|
+
status: "ready",
|
|
271
|
+
entitlement: {
|
|
272
|
+
active: false,
|
|
273
|
+
source: "selfHostedDeploymentLicense",
|
|
274
|
+
features: {},
|
|
275
|
+
limits: {},
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
const { dispose, readAuthority } = setupRuntimeAuthority(runtimeInfo);
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
await waitFor(() => {
|
|
283
|
+
expect(readAuthority()).toBe(
|
|
284
|
+
"status:valid chat:true sidebar:true popup:true threads:true",
|
|
285
|
+
);
|
|
286
|
+
});
|
|
287
|
+
} finally {
|
|
288
|
+
dispose();
|
|
289
|
+
}
|
|
290
|
+
});
|