@copilotkit/web-inspector 1.61.0 → 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 +512 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +23 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +513 -79
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +614 -109
- 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 +399 -1
- package/src/index.ts +649 -109
- 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,17 +1,27 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
import type { MockInstance } from "vitest";
|
|
3
3
|
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
|
|
4
7
|
import {
|
|
5
8
|
TELEMETRY_DOCS_URL,
|
|
6
9
|
TELEMETRY_EVENTS,
|
|
7
10
|
TELEMETRY_INGEST_URL,
|
|
11
|
+
getRuntimeUrlType,
|
|
8
12
|
getTelemetryDistinctIdForUrl,
|
|
9
13
|
maybeShowDisclosure,
|
|
10
14
|
track,
|
|
11
15
|
trackBannerClicked,
|
|
12
16
|
trackBannerViewed,
|
|
17
|
+
trackTalkToEngineerClicked,
|
|
18
|
+
trackThreadsEmptyEnabledViewed,
|
|
19
|
+
trackThreadsEnabledViewed,
|
|
20
|
+
trackThreadsIntelligenceSignupClicked,
|
|
21
|
+
trackThreadsLockedViewed,
|
|
13
22
|
trackThreadsTabClicked,
|
|
14
|
-
|
|
23
|
+
trackThreadsTalkToEngineerClicked,
|
|
24
|
+
} from "../telemetry.js";
|
|
15
25
|
import {
|
|
16
26
|
_resetTelemetryPersistenceForTesting,
|
|
17
27
|
getOrCreateTelemetryDistinctId,
|
|
@@ -19,13 +29,16 @@ import {
|
|
|
19
29
|
isTelemetryOptedOut,
|
|
20
30
|
markTelemetryDisclosureShown,
|
|
21
31
|
setTelemetryOptOut,
|
|
22
|
-
} from "../persistence";
|
|
32
|
+
} from "../persistence.js";
|
|
23
33
|
|
|
24
34
|
// The wrapper short-circuits before any network call when opted out, but
|
|
25
35
|
// for the network-touching cases we mock fetch globally so we can read
|
|
26
36
|
// what would have been sent without making real HTTP requests.
|
|
27
37
|
let fetchMock: MockInstance<typeof fetch>;
|
|
28
38
|
let consoleInfoSpy: MockInstance<typeof console.info>;
|
|
39
|
+
const webInspectorPackage = JSON.parse(
|
|
40
|
+
readFileSync(resolve(process.cwd(), "package.json"), "utf8"),
|
|
41
|
+
) as { version: string };
|
|
29
42
|
|
|
30
43
|
beforeEach(() => {
|
|
31
44
|
// Each test starts from a clean localStorage so distinct-ID + opt-out
|
|
@@ -83,10 +96,12 @@ describe("track()", () => {
|
|
|
83
96
|
// package is top-level object, not a string inside properties
|
|
84
97
|
expect(body.package).toEqual({ name: "@copilotkit/web-inspector" });
|
|
85
98
|
expect(body.properties).not.toHaveProperty("package");
|
|
99
|
+
expect(body.properties).not.toHaveProperty("package_name");
|
|
100
|
+
expect(body.properties).not.toHaveProperty("inspector_distinct_id");
|
|
86
101
|
expect(typeof body.ts).toBe("number");
|
|
87
102
|
});
|
|
88
103
|
|
|
89
|
-
it("
|
|
104
|
+
it("short-circuits when localStorage opt-out is set", async () => {
|
|
90
105
|
setTelemetryOptOut(true);
|
|
91
106
|
expect(isTelemetryOptedOut()).toBe(true);
|
|
92
107
|
|
|
@@ -96,9 +111,20 @@ describe("track()", () => {
|
|
|
96
111
|
});
|
|
97
112
|
await Promise.resolve();
|
|
98
113
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
114
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("swallows unserializable properties before dispatching", async () => {
|
|
118
|
+
const circular: Record<string, unknown> = {};
|
|
119
|
+
circular.self = circular;
|
|
120
|
+
|
|
121
|
+
expect(() => track(TELEMETRY_EVENTS.bannerClicked, circular)).not.toThrow();
|
|
122
|
+
expect(() =>
|
|
123
|
+
track(TELEMETRY_EVENTS.bannerClicked, { value: 1n }),
|
|
124
|
+
).not.toThrow();
|
|
125
|
+
await Promise.resolve();
|
|
126
|
+
|
|
127
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
102
128
|
});
|
|
103
129
|
|
|
104
130
|
it("swallows fetch failures (telemetry is best-effort)", async () => {
|
|
@@ -170,17 +196,131 @@ describe("typed helpers", () => {
|
|
|
170
196
|
expect(body.properties.cta).toBe("body");
|
|
171
197
|
});
|
|
172
198
|
|
|
173
|
-
it("trackThreadsTabClicked sends
|
|
174
|
-
trackThreadsTabClicked(
|
|
199
|
+
it("trackThreadsTabClicked sends thread metadata without content", async () => {
|
|
200
|
+
trackThreadsTabClicked({
|
|
201
|
+
intelligence_status: "intelligence_not_enabled",
|
|
202
|
+
thread_service_status: "unavailable",
|
|
203
|
+
runtime_mode: "sse",
|
|
204
|
+
runtime_url_type: "localhost",
|
|
205
|
+
license_status: "none",
|
|
206
|
+
telemetry_disabled: false,
|
|
207
|
+
});
|
|
175
208
|
await Promise.resolve();
|
|
176
209
|
const [, init] = fetchMock.mock.calls[0]!;
|
|
177
210
|
const body = JSON.parse((init?.body as string) ?? "{}") as {
|
|
178
211
|
event: string;
|
|
179
212
|
properties: Record<string, unknown>;
|
|
213
|
+
package: { name: string; version?: string };
|
|
180
214
|
};
|
|
181
215
|
expect(body.event).toBe(TELEMETRY_EVENTS.threadsTabClicked);
|
|
182
|
-
|
|
183
|
-
|
|
216
|
+
expect(body.properties).toMatchObject({
|
|
217
|
+
intelligence_status: "intelligence_not_enabled",
|
|
218
|
+
thread_service_status: "unavailable",
|
|
219
|
+
runtime_mode: "sse",
|
|
220
|
+
runtime_url_type: "localhost",
|
|
221
|
+
license_status: "none",
|
|
222
|
+
telemetry_disabled: false,
|
|
223
|
+
package_name: "@copilotkit/web-inspector",
|
|
224
|
+
package_version: webInspectorPackage.version,
|
|
225
|
+
});
|
|
226
|
+
expect(body.properties).toHaveProperty("inspector_distinct_id");
|
|
227
|
+
expect(body.properties.inspector_distinct_id).toBe(
|
|
228
|
+
body.properties.distinct_id,
|
|
229
|
+
);
|
|
230
|
+
expect(body.package).toEqual({
|
|
231
|
+
name: "@copilotkit/web-inspector",
|
|
232
|
+
version: webInspectorPackage.version,
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("sends required threads CTA and viewed events", async () => {
|
|
237
|
+
trackThreadsLockedViewed({
|
|
238
|
+
intelligence_status: "intelligence_not_enabled",
|
|
239
|
+
thread_service_status: "unavailable",
|
|
240
|
+
});
|
|
241
|
+
trackThreadsIntelligenceSignupClicked({
|
|
242
|
+
cta: "signup",
|
|
243
|
+
cta_surface: "threads_locked",
|
|
244
|
+
posthog_distinct_id: "abc-123",
|
|
245
|
+
});
|
|
246
|
+
trackThreadsTalkToEngineerClicked({
|
|
247
|
+
cta: "talk_to_engineer",
|
|
248
|
+
cta_surface: "threads_locked",
|
|
249
|
+
posthog_distinct_id: "abc-123",
|
|
250
|
+
});
|
|
251
|
+
trackTalkToEngineerClicked({
|
|
252
|
+
cta: "talk_to_engineer",
|
|
253
|
+
cta_surface: "threads_header",
|
|
254
|
+
posthog_distinct_id: "abc-123",
|
|
255
|
+
});
|
|
256
|
+
trackThreadsEmptyEnabledViewed({
|
|
257
|
+
intelligence_status: "intelligence_enabled",
|
|
258
|
+
thread_service_status: "available",
|
|
259
|
+
thread_count: 0,
|
|
260
|
+
});
|
|
261
|
+
trackThreadsEnabledViewed({
|
|
262
|
+
intelligence_status: "intelligence_enabled",
|
|
263
|
+
thread_service_status: "available",
|
|
264
|
+
thread_count: 2,
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
await Promise.resolve();
|
|
268
|
+
|
|
269
|
+
const payloads = fetchMock.mock.calls.map(([, init]) => {
|
|
270
|
+
return JSON.parse((init?.body as string) ?? "{}") as {
|
|
271
|
+
event: string;
|
|
272
|
+
properties: Record<string, unknown>;
|
|
273
|
+
};
|
|
274
|
+
});
|
|
275
|
+
const events = payloads.map((payload) => payload.event);
|
|
276
|
+
expect(events).toEqual([
|
|
277
|
+
TELEMETRY_EVENTS.threadsLockedViewed,
|
|
278
|
+
TELEMETRY_EVENTS.threadsIntelligenceSignupClicked,
|
|
279
|
+
TELEMETRY_EVENTS.threadsTalkToEngineerClicked,
|
|
280
|
+
TELEMETRY_EVENTS.talkToEngineerClicked,
|
|
281
|
+
TELEMETRY_EVENTS.threadsEmptyEnabledViewed,
|
|
282
|
+
TELEMETRY_EVENTS.threadsEnabledViewed,
|
|
283
|
+
]);
|
|
284
|
+
expect(payloads[0]!.properties).toMatchObject({
|
|
285
|
+
intelligence_status: "intelligence_not_enabled",
|
|
286
|
+
thread_service_status: "unavailable",
|
|
287
|
+
});
|
|
288
|
+
expect(payloads[1]!.properties).toMatchObject({
|
|
289
|
+
cta: "signup",
|
|
290
|
+
cta_surface: "threads_locked",
|
|
291
|
+
posthog_distinct_id: "abc-123",
|
|
292
|
+
});
|
|
293
|
+
expect(payloads[2]!.properties).toMatchObject({
|
|
294
|
+
cta: "talk_to_engineer",
|
|
295
|
+
cta_surface: "threads_locked",
|
|
296
|
+
posthog_distinct_id: "abc-123",
|
|
297
|
+
});
|
|
298
|
+
expect(payloads[3]!.properties).toMatchObject({
|
|
299
|
+
cta: "talk_to_engineer",
|
|
300
|
+
cta_surface: "threads_header",
|
|
301
|
+
posthog_distinct_id: "abc-123",
|
|
302
|
+
});
|
|
303
|
+
expect(payloads[4]!.properties).toMatchObject({
|
|
304
|
+
intelligence_status: "intelligence_enabled",
|
|
305
|
+
thread_service_status: "available",
|
|
306
|
+
thread_count: 0,
|
|
307
|
+
});
|
|
308
|
+
expect(payloads[5]!.properties).toMatchObject({
|
|
309
|
+
intelligence_status: "intelligence_enabled",
|
|
310
|
+
thread_service_status: "available",
|
|
311
|
+
thread_count: 2,
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// ─── Safe URL classification ────────────────────────────────────────────────
|
|
317
|
+
|
|
318
|
+
describe("getRuntimeUrlType()", () => {
|
|
319
|
+
it("classifies runtime URLs without exposing origins", () => {
|
|
320
|
+
expect(getRuntimeUrlType(undefined)).toBe("missing");
|
|
321
|
+
expect(getRuntimeUrlType("/api/copilotkit")).toBe("relative");
|
|
322
|
+
expect(getRuntimeUrlType("http://localhost:4000")).toBe("localhost");
|
|
323
|
+
expect(getRuntimeUrlType("https://example.com/api")).toBe("remote");
|
|
184
324
|
});
|
|
185
325
|
});
|
|
186
326
|
|
package/src/lib/persistence.ts
CHANGED
package/src/lib/telemetry.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
// Inspector-side anonymous telemetry.
|
|
2
|
-
//
|
|
3
|
-
// `oss.inspector.threads_tab_clicked`. POSTs directly from the browser
|
|
1
|
+
// Inspector-side anonymous telemetry. V1 events fire from index.ts for
|
|
2
|
+
// banner and thread-inspection interactions. POSTs directly from the browser
|
|
4
3
|
// to the CopilotKit telemetry sink at `telemetry.copilotkit.ai/ingest`,
|
|
5
4
|
// where a Lambda fan-out forwards events to PostHog / Reo / Scarf.
|
|
6
5
|
//
|
|
@@ -21,16 +20,24 @@ import {
|
|
|
21
20
|
hasTelemetryDisclosureBeenShown,
|
|
22
21
|
isTelemetryOptedOut,
|
|
23
22
|
markTelemetryDisclosureShown,
|
|
24
|
-
} from "./persistence";
|
|
23
|
+
} from "./persistence.js";
|
|
24
|
+
import packageJson from "../../package.json" with { type: "json" };
|
|
25
25
|
|
|
26
26
|
// V1 funnel events. Namespaced `oss.inspector.*` so the lambda's
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
// allowlist entry on the lambda or events will be rejected.
|
|
27
|
+
// owned-prefix gate (oss-path-to-production) can accept them server-side
|
|
28
|
+
// without a per-event sink deploy.
|
|
30
29
|
export const TELEMETRY_EVENTS = {
|
|
31
30
|
bannerViewed: "oss.inspector.banner_viewed",
|
|
32
31
|
bannerClicked: "oss.inspector.banner_clicked",
|
|
33
32
|
threadsTabClicked: "oss.inspector.threads_tab_clicked",
|
|
33
|
+
threadsLockedViewed: "oss.inspector.threads_locked_viewed",
|
|
34
|
+
threadsIntelligenceSignupClicked:
|
|
35
|
+
"oss.inspector.threads_intelligence_signup_clicked",
|
|
36
|
+
threadsTalkToEngineerClicked:
|
|
37
|
+
"oss.inspector.threads_talk_to_engineer_clicked",
|
|
38
|
+
talkToEngineerClicked: "oss.inspector.talk_to_engineer_clicked",
|
|
39
|
+
threadsEmptyEnabledViewed: "oss.inspector.threads_empty_enabled_viewed",
|
|
40
|
+
threadsEnabledViewed: "oss.inspector.threads_enabled_viewed",
|
|
34
41
|
} as const;
|
|
35
42
|
|
|
36
43
|
export type TelemetryEvent =
|
|
@@ -47,11 +54,63 @@ export const TELEMETRY_INGEST_URL = "https://telemetry.copilotkit.ai/ingest";
|
|
|
47
54
|
export const TELEMETRY_DOCS_URL = "https://docs.copilotkit.ai/telemetry";
|
|
48
55
|
|
|
49
56
|
const PACKAGE_NAME = "@copilotkit/web-inspector";
|
|
57
|
+
const PACKAGE_VERSION = packageJson.version;
|
|
50
58
|
|
|
51
59
|
// 3-second cap so a slow gateway can't hang the host app. Matches the
|
|
52
60
|
// runtime's existing scarf-client convention.
|
|
53
61
|
const FETCH_TIMEOUT_MS = 3000;
|
|
54
62
|
|
|
63
|
+
function isThreadsTelemetryEvent(event: TelemetryEvent): boolean {
|
|
64
|
+
return (
|
|
65
|
+
event === TELEMETRY_EVENTS.threadsTabClicked ||
|
|
66
|
+
event === TELEMETRY_EVENTS.threadsLockedViewed ||
|
|
67
|
+
event === TELEMETRY_EVENTS.threadsIntelligenceSignupClicked ||
|
|
68
|
+
event === TELEMETRY_EVENTS.threadsTalkToEngineerClicked ||
|
|
69
|
+
event === TELEMETRY_EVENTS.talkToEngineerClicked ||
|
|
70
|
+
event === TELEMETRY_EVENTS.threadsEmptyEnabledViewed ||
|
|
71
|
+
event === TELEMETRY_EVENTS.threadsEnabledViewed
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type RuntimeUrlType =
|
|
76
|
+
| "missing"
|
|
77
|
+
| "relative"
|
|
78
|
+
| "localhost"
|
|
79
|
+
| "same_origin"
|
|
80
|
+
| "remote"
|
|
81
|
+
| "invalid";
|
|
82
|
+
|
|
83
|
+
export function getRuntimeUrlType(
|
|
84
|
+
runtimeUrl: string | undefined,
|
|
85
|
+
): RuntimeUrlType {
|
|
86
|
+
if (!runtimeUrl) return "missing";
|
|
87
|
+
if (runtimeUrl.startsWith("/") && !runtimeUrl.startsWith("//")) {
|
|
88
|
+
return "relative";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const baseHref =
|
|
93
|
+
typeof window !== "undefined"
|
|
94
|
+
? window.location.href
|
|
95
|
+
: "https://copilotkit.ai";
|
|
96
|
+
const url = new URL(runtimeUrl, baseHref);
|
|
97
|
+
const baseUrl = new URL(baseHref);
|
|
98
|
+
const hostname = url.hostname.toLowerCase();
|
|
99
|
+
|
|
100
|
+
if (
|
|
101
|
+
hostname === "localhost" ||
|
|
102
|
+
hostname === "127.0.0.1" ||
|
|
103
|
+
hostname === "[::1]"
|
|
104
|
+
) {
|
|
105
|
+
return "localhost";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return url.origin === baseUrl.origin ? "same_origin" : "remote";
|
|
109
|
+
} catch {
|
|
110
|
+
return "invalid";
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
55
114
|
/**
|
|
56
115
|
* Fire-and-forget telemetry send. Returns synchronously; the network
|
|
57
116
|
* call is dispatched in the background and any failure is swallowed.
|
|
@@ -64,16 +123,34 @@ export function track(
|
|
|
64
123
|
event: TelemetryEvent,
|
|
65
124
|
properties: Record<string, unknown> = {},
|
|
66
125
|
): void {
|
|
126
|
+
if (isTelemetryOptedOut()) return;
|
|
127
|
+
|
|
67
128
|
const distinctId = getOrCreateTelemetryDistinctId();
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
129
|
+
const threadsProperties = isThreadsTelemetryEvent(event)
|
|
130
|
+
? {
|
|
131
|
+
package_name: PACKAGE_NAME,
|
|
132
|
+
package_version: PACKAGE_VERSION,
|
|
133
|
+
inspector_distinct_id: distinctId,
|
|
134
|
+
}
|
|
135
|
+
: {};
|
|
136
|
+
let body: string;
|
|
137
|
+
try {
|
|
138
|
+
body = JSON.stringify({
|
|
139
|
+
event,
|
|
140
|
+
properties: {
|
|
141
|
+
...properties,
|
|
142
|
+
...threadsProperties,
|
|
143
|
+
distinct_id: distinctId,
|
|
144
|
+
},
|
|
145
|
+
package: {
|
|
146
|
+
name: PACKAGE_NAME,
|
|
147
|
+
...(isThreadsTelemetryEvent(event) ? { version: PACKAGE_VERSION } : {}),
|
|
148
|
+
},
|
|
149
|
+
ts: Math.floor(Date.now() / 1000),
|
|
150
|
+
});
|
|
151
|
+
} catch {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
77
154
|
|
|
78
155
|
void postBestEffort(TELEMETRY_INGEST_URL, body, distinctId);
|
|
79
156
|
}
|
|
@@ -97,8 +174,75 @@ export function trackBannerClicked(props: {
|
|
|
97
174
|
track(TELEMETRY_EVENTS.bannerClicked, props);
|
|
98
175
|
}
|
|
99
176
|
|
|
100
|
-
export
|
|
101
|
-
|
|
177
|
+
export type InspectorThreadTelemetryProps = {
|
|
178
|
+
package_name?: typeof PACKAGE_NAME;
|
|
179
|
+
package_version?: string;
|
|
180
|
+
inspector_distinct_id?: string;
|
|
181
|
+
posthog_distinct_id?: string;
|
|
182
|
+
intelligence_status?:
|
|
183
|
+
| "intelligence_not_enabled"
|
|
184
|
+
| "intelligence_enabled"
|
|
185
|
+
| "unknown";
|
|
186
|
+
thread_service_status?: "unavailable" | "available" | "unknown" | "error";
|
|
187
|
+
license_status?:
|
|
188
|
+
| "valid"
|
|
189
|
+
| "none"
|
|
190
|
+
| "expired"
|
|
191
|
+
| "expiring"
|
|
192
|
+
| "invalid"
|
|
193
|
+
| "unknown";
|
|
194
|
+
runtime_mode?: "sse" | "intelligence";
|
|
195
|
+
runtime_url_type?: RuntimeUrlType;
|
|
196
|
+
cta_surface?:
|
|
197
|
+
| "threads_locked"
|
|
198
|
+
| "threads_header"
|
|
199
|
+
| "threads_empty"
|
|
200
|
+
| "threads_populated";
|
|
201
|
+
cta?: "signup" | "talk_to_engineer";
|
|
202
|
+
telemetry_disabled?: boolean;
|
|
203
|
+
thread_count?: number;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export function trackThreadsTabClicked(
|
|
207
|
+
props: InspectorThreadTelemetryProps = {},
|
|
208
|
+
): void {
|
|
209
|
+
track(TELEMETRY_EVENTS.threadsTabClicked, props);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function trackThreadsLockedViewed(
|
|
213
|
+
props: InspectorThreadTelemetryProps,
|
|
214
|
+
): void {
|
|
215
|
+
track(TELEMETRY_EVENTS.threadsLockedViewed, props);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function trackThreadsIntelligenceSignupClicked(
|
|
219
|
+
props: InspectorThreadTelemetryProps,
|
|
220
|
+
): void {
|
|
221
|
+
track(TELEMETRY_EVENTS.threadsIntelligenceSignupClicked, props);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function trackThreadsTalkToEngineerClicked(
|
|
225
|
+
props: InspectorThreadTelemetryProps,
|
|
226
|
+
): void {
|
|
227
|
+
track(TELEMETRY_EVENTS.threadsTalkToEngineerClicked, props);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function trackTalkToEngineerClicked(
|
|
231
|
+
props: InspectorThreadTelemetryProps,
|
|
232
|
+
): void {
|
|
233
|
+
track(TELEMETRY_EVENTS.talkToEngineerClicked, props);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function trackThreadsEmptyEnabledViewed(
|
|
237
|
+
props: InspectorThreadTelemetryProps,
|
|
238
|
+
): void {
|
|
239
|
+
track(TELEMETRY_EVENTS.threadsEmptyEnabledViewed, props);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function trackThreadsEnabledViewed(
|
|
243
|
+
props: InspectorThreadTelemetryProps,
|
|
244
|
+
): void {
|
|
245
|
+
track(TELEMETRY_EVENTS.threadsEnabledViewed, props);
|
|
102
246
|
}
|
|
103
247
|
|
|
104
248
|
/**
|