@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.
Files changed (35) hide show
  1. package/PARITY.md +1 -1
  2. package/README.md +7 -0
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.mjs +14 -14
  6. package/dist/use-render-activity-message-h2NHCkBt.cjs +85 -0
  7. package/dist/use-render-activity-message-h2NHCkBt.cjs.map +1 -0
  8. package/dist/{use-render-activity-message-Db2rOZkW.js → use-render-activity-message-ytoSQi6V.js} +3120 -3082
  9. package/dist/use-render-activity-message-ytoSQi6V.js.map +1 -0
  10. package/dist/v2/components/CopilotKitInspector.vue.d.ts +2 -0
  11. package/dist/v2/components/CopilotKitInspector.vue.d.ts.map +1 -1
  12. package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts +40 -16
  13. package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts.map +1 -1
  14. package/dist/v2/index.cjs +1 -1
  15. package/dist/v2/index.mjs +1 -1
  16. package/dist/v2/providers/CopilotKitProvider.types.d.ts +10 -9
  17. package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -1
  18. package/dist/v2/providers/CopilotKitProvider.vue.d.ts +3 -3
  19. package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -1
  20. package/dist/v2/providers/keys.d.ts +10 -0
  21. package/dist/v2/providers/keys.d.ts.map +1 -1
  22. package/package.json +7 -7
  23. package/src/v2/__tests__/mocks/web-inspector.ts +32 -1
  24. package/src/v2/components/CopilotKitInspector.vue +41 -3
  25. package/src/v2/components/__tests__/CopilotKitInspector.test.ts +31 -6
  26. package/src/v2/hooks/__tests__/use-threads.test.ts +24 -1
  27. package/src/v2/hooks/use-threads.ts +1 -1
  28. package/src/v2/providers/CopilotKitProvider.types.ts +10 -9
  29. package/src/v2/providers/CopilotKitProvider.vue +94 -37
  30. package/src/v2/providers/__tests__/CopilotKitProvider.managedEntitlement.test.ts +290 -0
  31. package/src/v2/providers/__tests__/CopilotKitProvider.test.ts +148 -37
  32. package/src/v2/providers/keys.ts +15 -0
  33. package/dist/use-render-activity-message-CIlNtfvE.cjs +0 -85
  34. package/dist/use-render-activity-message-CIlNtfvE.cjs.map +0 -1
  35. 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 { RuntimeLicenseStatus } from "@copilotkit/shared";
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
- const updateInspectorVisibility = () => {
114
- if (props.showDevConsole === true) {
115
- shouldRenderInspector.value = true;
116
- return;
117
- }
118
- if (props.showDevConsole === "auto") {
119
- if (typeof window === "undefined") {
120
- shouldRenderInspector.value = false;
121
- return;
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
- watch(() => props.showDevConsole, updateInspectorVisibility, {
131
- immediate: true,
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 server-reported `/info` license status.
635
- const licenseContextValue = computed<LicenseContextValue>(() =>
636
- createLicenseContextValue(runtimeLicenseStatus.value),
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
- () => runtimeLicenseStatus.value === "none" && !resolvedPublicKey.value,
697
+ () =>
698
+ runtimeLicenseWarningStatus.value === "none" && !resolvedPublicKey.value,
642
699
  );
643
700
  const showExpiredBanner = computed(
644
- () => runtimeLicenseStatus.value === "expired",
701
+ () => runtimeLicenseWarningStatus.value === "expired",
645
702
  );
646
703
  const showInvalidBanner = computed(
647
- () => runtimeLicenseStatus.value === "invalid",
704
+ () => runtimeLicenseWarningStatus.value === "invalid",
648
705
  );
649
706
  const showExpiringBanner = computed(
650
- () => runtimeLicenseStatus.value === "expiring",
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
- :default-anchor="props.inspectorDefaultAnchor"
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
+ });