@copilotkit/web-inspector 1.61.1 → 1.61.2
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/index.cjs +492 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +18 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +493 -77
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +598 -112
- package/dist/index.umd.js.map +1 -1
- package/dist/lib/context-helpers.cjs.map +1 -1
- package/dist/lib/context-helpers.mjs.map +1 -1
- package/dist/lib/persistence.cjs.map +1 -1
- package/dist/lib/persistence.mjs.map +1 -1
- package/dist/lib/telemetry.cjs +78 -12
- package/dist/lib/telemetry.cjs.map +1 -1
- package/dist/lib/telemetry.mjs +72 -13
- package/dist/lib/telemetry.mjs.map +1 -1
- package/dist/lib/types.d.cts +8 -0
- package/dist/lib/types.d.cts.map +1 -0
- package/dist/lib/types.d.mts +8 -0
- package/dist/lib/types.d.mts.map +1 -0
- package/dist/package.cjs +12 -0
- package/dist/package.cjs.map +1 -0
- package/dist/package.mjs +6 -0
- package/dist/package.mjs.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/web-inspector.spec.ts +215 -1
- package/src/index.ts +616 -108
- package/src/lib/__tests__/telemetry.test.ts +150 -10
- package/src/lib/context-helpers.ts +1 -1
- package/src/lib/persistence.ts +1 -1
- package/src/lib/telemetry.ts +162 -18
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WebInspectorElement, ɵCpkThreadDetails } from "../index";
|
|
1
|
+
import { WebInspectorElement, ɵCpkThreadDetails } from "../index.js";
|
|
2
2
|
import type { CopilotKitCore } from "@copilotkit/core";
|
|
3
3
|
import { CopilotKitCoreRuntimeConnectionStatus } from "@copilotkit/core";
|
|
4
4
|
import type { CopilotKitCoreSubscriber } from "@copilotkit/core";
|
|
@@ -321,6 +321,7 @@ type ThreadDetailsInternals = {
|
|
|
321
321
|
_loadingState: boolean;
|
|
322
322
|
_loadingEvents: boolean;
|
|
323
323
|
_panelTplCache: Map<string, { key: readonly unknown[]; tpl: unknown }>;
|
|
324
|
+
fetchMessages: (threadId: string) => Promise<void>;
|
|
324
325
|
fetchEvents: (threadId: string) => Promise<void>;
|
|
325
326
|
fetchState: (threadId: string) => Promise<void>;
|
|
326
327
|
renderConversation: () => unknown;
|
|
@@ -406,6 +407,43 @@ describe("ɵCpkThreadDetails caching", () => {
|
|
|
406
407
|
}
|
|
407
408
|
});
|
|
408
409
|
|
|
410
|
+
it("joins thread inspection URLs without double slashes when runtimeUrl has a trailing slash", async () => {
|
|
411
|
+
const fetchSpy = vi
|
|
412
|
+
.spyOn(globalThis, "fetch")
|
|
413
|
+
.mockImplementation(async (input) => {
|
|
414
|
+
const url = String(input);
|
|
415
|
+
if (url.endsWith("/messages")) {
|
|
416
|
+
return new Response(JSON.stringify({ messages: [] }), {
|
|
417
|
+
status: 200,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
if (url.endsWith("/events")) {
|
|
421
|
+
return new Response(JSON.stringify({ events: [] }), { status: 200 });
|
|
422
|
+
}
|
|
423
|
+
return new Response(JSON.stringify({ state: null }), { status: 200 });
|
|
424
|
+
});
|
|
425
|
+
try {
|
|
426
|
+
const { el, internals } = createThreadDetails();
|
|
427
|
+
internals.runtimeUrl = "http://localhost:4000/api/";
|
|
428
|
+
internals.threadInspectionAvailable = true;
|
|
429
|
+
internals.threadId = "thread one";
|
|
430
|
+
await el.updateComplete;
|
|
431
|
+
fetchSpy.mockClear();
|
|
432
|
+
|
|
433
|
+
await internals.fetchMessages("thread one");
|
|
434
|
+
await internals.fetchEvents("thread one");
|
|
435
|
+
await internals.fetchState("thread one");
|
|
436
|
+
|
|
437
|
+
expect(fetchSpy.mock.calls.map((call) => String(call[0]))).toEqual([
|
|
438
|
+
"http://localhost:4000/api/threads/thread%20one/messages",
|
|
439
|
+
"http://localhost:4000/api/threads/thread%20one/events",
|
|
440
|
+
"http://localhost:4000/api/threads/thread%20one/state",
|
|
441
|
+
]);
|
|
442
|
+
} finally {
|
|
443
|
+
fetchSpy.mockRestore();
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
|
|
409
447
|
it("conversation cache invalidates when _conversation is reassigned", async () => {
|
|
410
448
|
const { el, internals } = createThreadDetails();
|
|
411
449
|
await settleThread(el, internals, "t1");
|
|
@@ -616,6 +654,7 @@ type HeaderMockCore = {
|
|
|
616
654
|
agents: Record<string, AbstractAgent>;
|
|
617
655
|
context: Record<string, unknown>;
|
|
618
656
|
properties: Record<string, unknown>;
|
|
657
|
+
telemetryDisabled: boolean;
|
|
619
658
|
runtimeConnectionStatus: CopilotKitCoreRuntimeConnectionStatus;
|
|
620
659
|
runtimeUrl: string;
|
|
621
660
|
headers: Record<string, string>;
|
|
@@ -637,12 +676,15 @@ type HeaderMockCore = {
|
|
|
637
676
|
function createHeaderMockCore(
|
|
638
677
|
agents: Record<string, AbstractAgent>,
|
|
639
678
|
headers: Record<string, string>,
|
|
679
|
+
endpointOverrides: Partial<HeaderMockCore["threadEndpoints"]> = {},
|
|
680
|
+
telemetryDisabled = true,
|
|
640
681
|
) {
|
|
641
682
|
const subscribers = new Set<CopilotKitCoreSubscriber>();
|
|
642
683
|
const core: HeaderMockCore = {
|
|
643
684
|
agents,
|
|
644
685
|
context: {},
|
|
645
686
|
properties: {},
|
|
687
|
+
telemetryDisabled,
|
|
646
688
|
runtimeConnectionStatus: CopilotKitCoreRuntimeConnectionStatus.Connected,
|
|
647
689
|
runtimeUrl: "http://localhost/api",
|
|
648
690
|
headers,
|
|
@@ -651,6 +693,7 @@ function createHeaderMockCore(
|
|
|
651
693
|
inspect: true,
|
|
652
694
|
mutations: true,
|
|
653
695
|
realtimeMetadata: true,
|
|
696
|
+
...endpointOverrides,
|
|
654
697
|
},
|
|
655
698
|
subscribe(subscriber: CopilotKitCoreSubscriber) {
|
|
656
699
|
subscribers.add(subscriber);
|
|
@@ -693,6 +736,21 @@ describe("WebInspectorElement owned thread store headers (#5581)", () => {
|
|
|
693
736
|
fetchMock.mock.calls.filter((call) =>
|
|
694
737
|
String(call[0]).includes("/threads?"),
|
|
695
738
|
);
|
|
739
|
+
const telemetryPosts = () =>
|
|
740
|
+
fetchMock.mock.calls
|
|
741
|
+
.filter(
|
|
742
|
+
(call) =>
|
|
743
|
+
String(call[0]) === "https://telemetry.copilotkit.ai/ingest" &&
|
|
744
|
+
(call[1] as RequestInit | undefined)?.method === "POST",
|
|
745
|
+
)
|
|
746
|
+
.map((call) => {
|
|
747
|
+
const body =
|
|
748
|
+
((call[1] as RequestInit | undefined)?.body as string) ?? "{}";
|
|
749
|
+
return JSON.parse(body) as {
|
|
750
|
+
event: string;
|
|
751
|
+
properties: Record<string, unknown>;
|
|
752
|
+
};
|
|
753
|
+
});
|
|
696
754
|
|
|
697
755
|
beforeEach(() => {
|
|
698
756
|
document.body.innerHTML = "";
|
|
@@ -758,4 +816,160 @@ describe("WebInspectorElement owned thread store headers (#5581)", () => {
|
|
|
758
816
|
"X-CSRF": "2",
|
|
759
817
|
});
|
|
760
818
|
});
|
|
819
|
+
|
|
820
|
+
it("shows the locked Intelligence state when thread listing is unavailable without fetching threads", async () => {
|
|
821
|
+
const { agent } = createMockAgent("alpha");
|
|
822
|
+
const harness = createHeaderMockCore(
|
|
823
|
+
{ alpha: agent },
|
|
824
|
+
{ "X-CSRF": "1" },
|
|
825
|
+
{ list: false },
|
|
826
|
+
true,
|
|
827
|
+
);
|
|
828
|
+
|
|
829
|
+
const inspector = new WebInspectorElement();
|
|
830
|
+
document.body.appendChild(inspector);
|
|
831
|
+
inspector.core = harness.core as unknown as WebInspectorElement["core"];
|
|
832
|
+
harness.emitAgentsChanged();
|
|
833
|
+
|
|
834
|
+
const internals = inspector as unknown as {
|
|
835
|
+
isOpen: boolean;
|
|
836
|
+
handleMenuSelect: (key: "threads") => void;
|
|
837
|
+
};
|
|
838
|
+
internals.isOpen = true;
|
|
839
|
+
internals.handleMenuSelect("threads");
|
|
840
|
+
await inspector.updateComplete;
|
|
841
|
+
|
|
842
|
+
const text = inspector.shadowRoot?.textContent ?? "";
|
|
843
|
+
expect(text).toMatch(/Enable Intelligence to inspect Threads\./);
|
|
844
|
+
expect(text).toContain("Talk to an Engineer");
|
|
845
|
+
expect(text).toContain("Sign up for Intelligence");
|
|
846
|
+
const ctaLabels = Array.from(
|
|
847
|
+
inspector.shadowRoot?.querySelectorAll<HTMLAnchorElement>("a") ?? [],
|
|
848
|
+
).map((anchor) => anchor.textContent?.trim());
|
|
849
|
+
expect(ctaLabels).toEqual([
|
|
850
|
+
"Talk to an Engineer",
|
|
851
|
+
"Sign up for Intelligence",
|
|
852
|
+
]);
|
|
853
|
+
expect(text).not.toContain("No threads yet");
|
|
854
|
+
expect(
|
|
855
|
+
fetchMock.mock.calls.some((call) => String(call[0]).includes("/threads")),
|
|
856
|
+
).toBe(false);
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
it("adds ref and posthog distinct ID attribution to locked-state CTAs", async () => {
|
|
860
|
+
const { agent } = createMockAgent("alpha");
|
|
861
|
+
const harness = createHeaderMockCore(
|
|
862
|
+
{ alpha: agent },
|
|
863
|
+
{},
|
|
864
|
+
{ list: false },
|
|
865
|
+
false,
|
|
866
|
+
);
|
|
867
|
+
|
|
868
|
+
const inspector = new WebInspectorElement();
|
|
869
|
+
document.body.appendChild(inspector);
|
|
870
|
+
inspector.core = harness.core as unknown as WebInspectorElement["core"];
|
|
871
|
+
harness.emitAgentsChanged();
|
|
872
|
+
|
|
873
|
+
const internals = inspector as unknown as {
|
|
874
|
+
isOpen: boolean;
|
|
875
|
+
handleMenuSelect: (key: "threads") => void;
|
|
876
|
+
};
|
|
877
|
+
internals.isOpen = true;
|
|
878
|
+
internals.handleMenuSelect("threads");
|
|
879
|
+
await inspector.updateComplete;
|
|
880
|
+
|
|
881
|
+
const signup = inspector.shadowRoot?.querySelector<HTMLAnchorElement>(
|
|
882
|
+
'a[href^="https://go.copilotkit.ai/intelligence-signup"]',
|
|
883
|
+
);
|
|
884
|
+
const engineer = inspector.shadowRoot?.querySelector<HTMLAnchorElement>(
|
|
885
|
+
'a[href^="https://www.copilotkit.ai/talk-to-an-engineer"]',
|
|
886
|
+
);
|
|
887
|
+
|
|
888
|
+
expect(signup).not.toBeNull();
|
|
889
|
+
expect(engineer).not.toBeNull();
|
|
890
|
+
|
|
891
|
+
const signupUrl = new URL(signup!.href);
|
|
892
|
+
expect(signupUrl.searchParams.get("ref")).toBe("cpk-inspector");
|
|
893
|
+
const distinctId = signupUrl.searchParams.get("posthog_distinct_id");
|
|
894
|
+
expect(distinctId).toMatch(/^[0-9a-f-]{36}$/);
|
|
895
|
+
|
|
896
|
+
const engineerUrl = new URL(engineer!.href);
|
|
897
|
+
expect(engineerUrl.origin).toBe("https://www.copilotkit.ai");
|
|
898
|
+
expect(engineerUrl.pathname).toBe("/talk-to-an-engineer");
|
|
899
|
+
expect(engineerUrl.searchParams.get("ref")).toBe("cpk-inspector-threads");
|
|
900
|
+
expect(engineerUrl.searchParams.get("posthog_distinct_id")).toBe(
|
|
901
|
+
distinctId,
|
|
902
|
+
);
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
it("tracks Threads tab clicks through the rendered inspector menu", async () => {
|
|
906
|
+
const { agent } = createMockAgent("alpha");
|
|
907
|
+
const harness = createHeaderMockCore(
|
|
908
|
+
{ alpha: agent },
|
|
909
|
+
{},
|
|
910
|
+
{ list: false },
|
|
911
|
+
false,
|
|
912
|
+
);
|
|
913
|
+
|
|
914
|
+
const inspector = new WebInspectorElement();
|
|
915
|
+
document.body.appendChild(inspector);
|
|
916
|
+
inspector.core = harness.core as unknown as WebInspectorElement["core"];
|
|
917
|
+
harness.emitAgentsChanged();
|
|
918
|
+
|
|
919
|
+
const internals = inspector as unknown as { isOpen: boolean };
|
|
920
|
+
internals.isOpen = true;
|
|
921
|
+
inspector.requestUpdate();
|
|
922
|
+
await inspector.updateComplete;
|
|
923
|
+
|
|
924
|
+
const threadsButton = Array.from(
|
|
925
|
+
inspector.shadowRoot?.querySelectorAll<HTMLButtonElement>("button") ?? [],
|
|
926
|
+
).find((button) => button.textContent?.trim() === "Threads");
|
|
927
|
+
expect(threadsButton, "Threads menu button should render").toBeDefined();
|
|
928
|
+
|
|
929
|
+
threadsButton!.click();
|
|
930
|
+
await inspector.updateComplete;
|
|
931
|
+
await Promise.resolve();
|
|
932
|
+
|
|
933
|
+
const threadsTabClick = telemetryPosts().find(
|
|
934
|
+
(post) => post.event === "oss.inspector.threads_tab_clicked",
|
|
935
|
+
);
|
|
936
|
+
expect(threadsTabClick).toBeDefined();
|
|
937
|
+
expect(threadsTabClick!.properties).toMatchObject({
|
|
938
|
+
intelligence_status: "intelligence_not_enabled",
|
|
939
|
+
thread_service_status: "unavailable",
|
|
940
|
+
telemetry_disabled: false,
|
|
941
|
+
});
|
|
942
|
+
expect(threadsTabClick!.properties.distinct_id).toMatch(/^[0-9a-f-]{36}$/);
|
|
943
|
+
if (threadsTabClick!.properties.posthog_distinct_id !== undefined) {
|
|
944
|
+
expect(threadsTabClick!.properties.posthog_distinct_id).toBe(
|
|
945
|
+
threadsTabClick!.properties.distinct_id,
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
it("keeps the enabled empty Threads state when thread listing is available", async () => {
|
|
951
|
+
const { agent } = createMockAgent("alpha");
|
|
952
|
+
const harness = createHeaderMockCore({ alpha: agent }, {}, {}, true);
|
|
953
|
+
|
|
954
|
+
const inspector = new WebInspectorElement();
|
|
955
|
+
document.body.appendChild(inspector);
|
|
956
|
+
inspector.core = harness.core as unknown as WebInspectorElement["core"];
|
|
957
|
+
harness.emitAgentsChanged();
|
|
958
|
+
|
|
959
|
+
const internals = inspector as unknown as {
|
|
960
|
+
isOpen: boolean;
|
|
961
|
+
handleMenuSelect: (key: "threads") => void;
|
|
962
|
+
};
|
|
963
|
+
internals.isOpen = true;
|
|
964
|
+
internals.handleMenuSelect("threads");
|
|
965
|
+
await inspector.updateComplete;
|
|
966
|
+
|
|
967
|
+
expect(inspector.shadowRoot?.textContent ?? "").toContain("No threads yet");
|
|
968
|
+
const engineer = inspector.shadowRoot?.querySelector<HTMLAnchorElement>(
|
|
969
|
+
'a[href^="https://www.copilotkit.ai/talk-to-an-engineer"]',
|
|
970
|
+
);
|
|
971
|
+
expect(engineer?.href).toBe(
|
|
972
|
+
"https://www.copilotkit.ai/talk-to-an-engineer?ref=cpk-inspector-threads",
|
|
973
|
+
);
|
|
974
|
+
});
|
|
761
975
|
});
|