@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.mjs CHANGED
@@ -3,7 +3,7 @@ import inspector_logo_default from "./assets/inspector-logo.mjs";
3
3
  import inspector_logo_icon_default from "./assets/inspector-logo-icon.mjs";
4
4
  import { applyAnchorPosition, centerContext, clampSize, constrainToViewport, keepPositionWithinViewport, updateAnchorFromPosition, updateSizeFromElement } from "./lib/context-helpers.mjs";
5
5
  import { isValidAnchor, isValidDockMode, isValidPosition, isValidSize, loadInspectorState, saveInspectorState } from "./lib/persistence.mjs";
6
- import { TELEMETRY_DOCS_URL, ensureTelemetryDistinctId, getTelemetryDistinctIdForUrl, maybeShowDisclosure, trackBannerClicked, trackBannerViewed, trackThreadsTabClicked } from "./lib/telemetry.mjs";
6
+ import { TELEMETRY_DOCS_URL, ensureTelemetryDistinctId, getRuntimeUrlType, getTelemetryDistinctIdForUrl, maybeShowDisclosure, trackBannerClicked, trackBannerViewed, trackTalkToEngineerClicked, trackThreadsEmptyEnabledViewed, trackThreadsEnabledViewed, trackThreadsIntelligenceSignupClicked, trackThreadsLockedViewed, trackThreadsTabClicked, trackThreadsTalkToEngineerClicked } from "./lib/telemetry.mjs";
7
7
  import { LitElement, css, html, nothing, unsafeCSS } from "lit";
8
8
  import { marked } from "marked";
9
9
  import { styleMap } from "lit/directives/style-map.js";
@@ -32,6 +32,8 @@ const DEFAULT_WINDOW_SIZE = {
32
32
  const DOCKED_LEFT_WIDTH = 500;
33
33
  const MAX_AGENT_EVENTS = 200;
34
34
  const MAX_TOTAL_EVENTS = 500;
35
+ const INTELLIGENCE_SIGNUP_URL = "https://go.copilotkit.ai/intelligence-signup";
36
+ const TALK_TO_ENGINEER_URL = "https://www.copilotkit.ai/talk-to-an-engineer";
35
37
  const AGENT_EVENT_TYPES = [
36
38
  "RUN_STARTED",
37
39
  "RUN_FINISHED",
@@ -107,14 +109,14 @@ function eventColors(type) {
107
109
  bg: "rgba(190,194,255,0.102)",
108
110
  fg: "#5558B2"
109
111
  };
112
+ if (type === "RUN_ERROR" || type === "ERROR") return {
113
+ bg: "rgba(250,95,103,0.13)",
114
+ fg: "#c0333a"
115
+ };
110
116
  if (type.startsWith("RUN_") || type.startsWith("STEP_")) return {
111
117
  bg: "rgba(255,172,77,0.2)",
112
118
  fg: "#996300"
113
119
  };
114
- if (type === "ERROR") return {
115
- bg: "rgba(250,95,103,0.13)",
116
- fg: "#c0333a"
117
- };
118
120
  return {
119
121
  bg: "#F7F7F9",
120
122
  fg: "#838389"
@@ -1140,7 +1142,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1140
1142
  this._messagesError = null;
1141
1143
  }
1142
1144
  try {
1143
- const res = await fetch(`${this.runtimeUrl}/threads/${encodeURIComponent(threadId)}/messages`, {
1145
+ const res = await fetch(this.getThreadInspectionUrl(threadId, "messages"), {
1144
1146
  headers: { ...this.headers },
1145
1147
  signal: controller.signal
1146
1148
  });
@@ -1170,7 +1172,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1170
1172
  this._loadingEvents = true;
1171
1173
  this._eventsError = null;
1172
1174
  try {
1173
- const res = await fetch(`${this.runtimeUrl}/threads/${encodeURIComponent(threadId)}/events`, {
1175
+ const res = await fetch(this.getThreadInspectionUrl(threadId, "events"), {
1174
1176
  headers: { ...this.headers },
1175
1177
  signal: controller.signal
1176
1178
  });
@@ -1204,7 +1206,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1204
1206
  this._loadingState = true;
1205
1207
  this._stateError = null;
1206
1208
  try {
1207
- const res = await fetch(`${this.runtimeUrl}/threads/${encodeURIComponent(threadId)}/state`, {
1209
+ const res = await fetch(this.getThreadInspectionUrl(threadId, "state"), {
1208
1210
  headers: { ...this.headers },
1209
1211
  signal: controller.signal
1210
1212
  });
@@ -1227,6 +1229,9 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1227
1229
  if (!controller.signal.aborted && this.threadId === threadId) this._loadingState = false;
1228
1230
  }
1229
1231
  }
1232
+ getThreadInspectionUrl(threadId, resource) {
1233
+ return `${this.runtimeUrl.replace(/\/+$/, "")}/threads/${encodeURIComponent(threadId)}/${resource}`;
1234
+ }
1230
1235
  mapMessages(messages) {
1231
1236
  const items = [];
1232
1237
  const toolCallMap = /* @__PURE__ */ new Map();
@@ -1881,6 +1886,7 @@ var WebInspectorElement = class extends LitElement {
1881
1886
  this.viewedBannerTimestamps = /* @__PURE__ */ new Set();
1882
1887
  this.pendingBannerViewed = null;
1883
1888
  this.clickedBannerIds = /* @__PURE__ */ new Set();
1889
+ this.viewedThreadsTelemetryStates = /* @__PURE__ */ new Set();
1884
1890
  this.contextState = {
1885
1891
  button: {
1886
1892
  position: {
@@ -2086,6 +2092,27 @@ var WebInspectorElement = class extends LitElement {
2086
2092
  this.expandedTools = /* @__PURE__ */ new Set();
2087
2093
  this.expandedContextItems = /* @__PURE__ */ new Set();
2088
2094
  this.copiedContextItems = /* @__PURE__ */ new Set();
2095
+ this.handleTalkToEngineerClick = () => {
2096
+ if (this.core?.telemetryDisabled) return;
2097
+ trackTalkToEngineerClicked(this.getThreadsTelemetryProps({
2098
+ cta: "talk_to_engineer",
2099
+ cta_surface: "threads_header"
2100
+ }, { includeUrlAttribution: true }));
2101
+ };
2102
+ this.handleThreadsIntelligenceSignupClick = () => {
2103
+ if (this.core?.telemetryDisabled) return;
2104
+ trackThreadsIntelligenceSignupClicked(this.getThreadsTelemetryProps({
2105
+ cta: "signup",
2106
+ cta_surface: "threads_locked"
2107
+ }, { includeUrlAttribution: true }));
2108
+ };
2109
+ this.handleThreadsTalkToEngineerClick = () => {
2110
+ if (this.core?.telemetryDisabled) return;
2111
+ trackThreadsTalkToEngineerClicked(this.getThreadsTelemetryProps({
2112
+ cta: "talk_to_engineer",
2113
+ cta_surface: "threads_locked"
2114
+ }, { includeUrlAttribution: true }));
2115
+ };
2089
2116
  this.handleThreadDividerPointerDown = (event) => {
2090
2117
  this.threadDividerResizing = true;
2091
2118
  this.threadDividerPointerId = event.pointerId;
@@ -2213,7 +2240,36 @@ var WebInspectorElement = class extends LitElement {
2213
2240
  }
2214
2241
  ];
2215
2242
  }
2243
+ getThreadServiceStatus() {
2244
+ if (!this._core) return "unknown";
2245
+ if (!this._core.threadEndpoints) return "unknown";
2246
+ return this._core.threadEndpoints?.list === false ? "unavailable" : "available";
2247
+ }
2248
+ areThreadEndpointsAvailable() {
2249
+ return this.getThreadServiceStatus() !== "unavailable";
2250
+ }
2251
+ getThreadsTelemetryProps(extra = {}, options = {}) {
2252
+ const distinctId = options.includeUrlAttribution && !this.core?.telemetryDisabled ? getTelemetryDistinctIdForUrl() : null;
2253
+ const threadServiceStatus = this.getThreadServiceStatus();
2254
+ return {
2255
+ posthog_distinct_id: distinctId ?? void 0,
2256
+ intelligence_status: threadServiceStatus === "available" ? "intelligence_enabled" : threadServiceStatus === "unavailable" ? "intelligence_not_enabled" : "unknown",
2257
+ thread_service_status: threadServiceStatus,
2258
+ license_status: this.core?.licenseStatus ?? void 0,
2259
+ runtime_mode: this.core?.runtimeMode ?? void 0,
2260
+ runtime_url_type: getRuntimeUrlType(this.core?.runtimeUrl),
2261
+ telemetry_disabled: this.core?.telemetryDisabled ?? false,
2262
+ ...extra
2263
+ };
2264
+ }
2265
+ getIntelligenceSignupUrl() {
2266
+ return this.appendRefParam(INTELLIGENCE_SIGNUP_URL, "cpk-inspector");
2267
+ }
2268
+ getTalkToEngineerUrl() {
2269
+ return this.appendRefParam(TALK_TO_ENGINEER_URL, "cpk-inspector-threads");
2270
+ }
2216
2271
  subscribeToThreadStore(agentId, store) {
2272
+ if (!this.areThreadEndpointsAvailable()) return;
2217
2273
  if (this._threadStoreSubscriptions.has(agentId)) return;
2218
2274
  const threadsSub = store.select(ɵselectThreads).subscribe((threads) => {
2219
2275
  this._threadsByAgent.set(agentId, threads);
@@ -2254,7 +2310,7 @@ var WebInspectorElement = class extends LitElement {
2254
2310
  if (this.core?.getThreadStore(agentId)) return;
2255
2311
  const core = this.core;
2256
2312
  if (!core?.runtimeUrl) return;
2257
- if (core.threadEndpoints?.list === false) return;
2313
+ if (!this.areThreadEndpointsAvailable()) return;
2258
2314
  const store = ɵcreateThreadStore({ fetch: globalThis.fetch });
2259
2315
  store.start();
2260
2316
  store.setContext({
@@ -2278,6 +2334,7 @@ var WebInspectorElement = class extends LitElement {
2278
2334
  for (const [agentId, store] of this._ownedThreadStores) store.setContext({
2279
2335
  runtimeUrl: core.runtimeUrl,
2280
2336
  headers: { ...headers },
2337
+ wsUrl: core.intelligence?.wsUrl,
2281
2338
  agentId
2282
2339
  });
2283
2340
  }
@@ -2308,7 +2365,7 @@ var WebInspectorElement = class extends LitElement {
2308
2365
  maybeShowDisclosure();
2309
2366
  }
2310
2367
  this.flushPendingBannerViewed();
2311
- if (core.threadEndpoints?.list !== false) for (const agentId of this._ownedThreadStores.keys()) this.refreshOwnedThreadStore(agentId);
2368
+ if (this.areThreadEndpointsAvailable()) for (const agentId of this._ownedThreadStores.keys()) this.refreshOwnedThreadStore(agentId);
2312
2369
  else this.teardownOwnedThreadStores();
2313
2370
  } else {
2314
2371
  this._threadsByAgent.clear();
@@ -2355,6 +2412,13 @@ var WebInspectorElement = class extends LitElement {
2355
2412
  };
2356
2413
  this.coreUnsubscribe = core.subscribe(this.coreSubscriber).unsubscribe;
2357
2414
  this.processAgentsChanged(core.agents);
2415
+ if (core.runtimeConnectionStatus === "connected") {
2416
+ if (!core.telemetryDisabled) {
2417
+ ensureTelemetryDistinctId();
2418
+ maybeShowDisclosure();
2419
+ }
2420
+ this.flushPendingBannerViewed();
2421
+ }
2358
2422
  const threadStores = typeof core.getThreadStores === "function" ? core.getThreadStores() : {};
2359
2423
  for (const [agentId, store] of Object.entries(threadStores)) this.subscribeToThreadStore(agentId, store);
2360
2424
  if (core.context) this.contextStore = this.normalizeContextStore(core.context);
@@ -2443,10 +2507,10 @@ var WebInspectorElement = class extends LitElement {
2443
2507
  onRunStartedEvent: ({ event }) => {
2444
2508
  this.recordAgentEvent(agentId, "RUN_STARTED", event);
2445
2509
  },
2446
- onRunFinishedEvent: ({ event, result }) => {
2510
+ onRunFinishedEvent: (params) => {
2447
2511
  this.recordAgentEvent(agentId, "RUN_FINISHED", {
2448
- event,
2449
- result
2512
+ event: params.event,
2513
+ result: "result" in params ? params.result : void 0
2450
2514
  });
2451
2515
  this.refreshOwnedThreadStore(agentId);
2452
2516
  },
@@ -2764,13 +2828,13 @@ ${argsString}</pre
2764
2828
  }
2765
2829
  getEventBadgeClasses(type) {
2766
2830
  const base = "font-mono text-[10px] font-medium inline-flex items-center rounded-sm px-1.5 py-0.5 border";
2831
+ if (type === "RUN_ERROR") return `${base} bg-rose-50 text-rose-700 border-rose-200`;
2767
2832
  if (type.startsWith("RUN_")) return `${base} bg-blue-50 text-blue-700 border-blue-200`;
2768
2833
  if (type.startsWith("TEXT_MESSAGE")) return `${base} bg-emerald-50 text-emerald-700 border-emerald-200`;
2769
2834
  if (type.startsWith("TOOL_CALL")) return `${base} bg-amber-50 text-amber-700 border-amber-200`;
2770
2835
  if (type.startsWith("REASONING")) return `${base} bg-fuchsia-50 text-fuchsia-700 border-fuchsia-200`;
2771
2836
  if (type.startsWith("STATE")) return `${base} bg-violet-50 text-violet-700 border-violet-200`;
2772
2837
  if (type.startsWith("MESSAGES")) return `${base} bg-sky-50 text-sky-700 border-sky-200`;
2773
- if (type === "RUN_ERROR") return `${base} bg-rose-50 text-rose-700 border-rose-200`;
2774
2838
  return `${base} bg-gray-100 text-gray-600 border-gray-200`;
2775
2839
  }
2776
2840
  stringifyPayload(payload, pretty) {
@@ -4327,78 +4391,399 @@ ${argsString}</pre
4327
4391
  cta_label: this.announcementCtaLabel ?? void 0
4328
4392
  });
4329
4393
  }
4394
+ trackThreadsViewStateOnce(state, threadCount) {
4395
+ if (this.core?.telemetryDisabled) return;
4396
+ const key = `${state}:${this.getThreadServiceStatus()}`;
4397
+ if (this.viewedThreadsTelemetryStates.has(key)) return;
4398
+ this.viewedThreadsTelemetryStates.add(key);
4399
+ const props = this.getThreadsTelemetryProps({ thread_count: threadCount });
4400
+ if (state === "locked") trackThreadsLockedViewed(props);
4401
+ else if (state === "empty_enabled") trackThreadsEmptyEnabledViewed(props);
4402
+ else trackThreadsEnabledViewed(props);
4403
+ }
4404
+ renderThreadsLockedBackgroundMockup() {
4405
+ return html`
4406
+ <div
4407
+ aria-hidden="true"
4408
+ style="
4409
+ position: absolute;
4410
+ inset: 0;
4411
+ display: grid;
4412
+ grid-template-columns: minmax(180px, 28%) 1fr;
4413
+ overflow: hidden;
4414
+ opacity: 0.58;
4415
+ pointer-events: none;
4416
+ "
4417
+ >
4418
+ <div
4419
+ style="
4420
+ display: flex;
4421
+ flex-direction: column;
4422
+ gap: 12px;
4423
+ padding: 28px 24px;
4424
+ border-right: 1px solid #dbdbe5;
4425
+ background: #fafafa;
4426
+ "
4427
+ >
4428
+ ${[
4429
+ {
4430
+ width: 74,
4431
+ accent: true
4432
+ },
4433
+ { width: 92 },
4434
+ { width: 68 },
4435
+ { width: 84 },
4436
+ { width: 58 },
4437
+ { width: 76 }
4438
+ ].map((row) => html`
4439
+ <div
4440
+ style="
4441
+ padding: 12px;
4442
+ border-radius: 8px;
4443
+ background: ${row.accent ? "#eee6fe" : "#ffffff"};
4444
+ box-shadow: inset 0 0 0 1px #eeeef4;
4445
+ "
4446
+ >
4447
+ <div
4448
+ style="
4449
+ height: 8px;
4450
+ width: ${row.width}%;
4451
+ border-radius: 99px;
4452
+ background: ${row.accent ? "#a984f5" : "#d7d7df"};
4453
+ "
4454
+ ></div>
4455
+ <div
4456
+ style="
4457
+ height: 6px;
4458
+ width: 88%;
4459
+ margin-top: 10px;
4460
+ border-radius: 99px;
4461
+ background: #e3e3eb;
4462
+ "
4463
+ ></div>
4464
+ <div
4465
+ style="
4466
+ height: 6px;
4467
+ width: 62%;
4468
+ margin-top: 7px;
4469
+ border-radius: 99px;
4470
+ background: #e8e8ef;
4471
+ "
4472
+ ></div>
4473
+ </div>
4474
+ `)}
4475
+ </div>
4476
+ <div
4477
+ style="
4478
+ min-width: 0;
4479
+ padding: 42px 48px;
4480
+ background: #ffffff;
4481
+ "
4482
+ >
4483
+ <div
4484
+ style="
4485
+ height: 10px;
4486
+ width: 180px;
4487
+ border-radius: 99px;
4488
+ background: #d7d7df;
4489
+ "
4490
+ ></div>
4491
+ <div
4492
+ style="
4493
+ height: 8px;
4494
+ width: min(520px, 58%);
4495
+ margin-top: 28px;
4496
+ border-radius: 99px;
4497
+ background: #e3e3eb;
4498
+ "
4499
+ ></div>
4500
+ <div
4501
+ style="
4502
+ height: 8px;
4503
+ width: min(430px, 48%);
4504
+ margin-top: 12px;
4505
+ border-radius: 99px;
4506
+ background: #e8e8ef;
4507
+ "
4508
+ ></div>
4509
+ <div
4510
+ style="
4511
+ display: grid;
4512
+ grid-template-columns: repeat(2, minmax(0, 1fr));
4513
+ gap: 16px;
4514
+ max-width: 620px;
4515
+ margin-top: 30px;
4516
+ "
4517
+ >
4518
+ <div
4519
+ style="
4520
+ height: 116px;
4521
+ border-radius: 8px;
4522
+ background: #f5f5f8;
4523
+ box-shadow: inset 0 0 0 1px #eeeef4;
4524
+ "
4525
+ ></div>
4526
+ <div
4527
+ style="
4528
+ height: 116px;
4529
+ border-radius: 8px;
4530
+ background: #f5f5f8;
4531
+ box-shadow: inset 0 0 0 1px #eeeef4;
4532
+ "
4533
+ ></div>
4534
+ </div>
4535
+ <div
4536
+ style="
4537
+ height: 10px;
4538
+ width: min(680px, 74%);
4539
+ margin-top: 34px;
4540
+ border-radius: 99px;
4541
+ background: #e3e3eb;
4542
+ "
4543
+ ></div>
4544
+ <div
4545
+ style="
4546
+ height: 10px;
4547
+ width: min(560px, 60%);
4548
+ margin-top: 14px;
4549
+ border-radius: 99px;
4550
+ background: #e8e8ef;
4551
+ "
4552
+ ></div>
4553
+ </div>
4554
+ </div>
4555
+ `;
4556
+ }
4557
+ renderThreadsLockedView() {
4558
+ this.trackThreadsViewStateOnce("locked", 0);
4559
+ return html`
4560
+ <div
4561
+ style="
4562
+ position: relative;
4563
+ height: 100%;
4564
+ display: flex;
4565
+ align-items: center;
4566
+ justify-content: center;
4567
+ padding: 32px;
4568
+ overflow: hidden;
4569
+ background: #ffffff;
4570
+ "
4571
+ >
4572
+ ${this.renderThreadsLockedBackgroundMockup()}
4573
+ <div
4574
+ aria-hidden="true"
4575
+ style="
4576
+ position: absolute;
4577
+ inset: 0;
4578
+ pointer-events: none;
4579
+ background:
4580
+ radial-gradient(circle at center, rgba(255,255,255,0.9) 0, rgba(255,255,255,0.78) 24%, rgba(255,255,255,0.34) 48%, rgba(255,255,255,0.56) 100%);
4581
+ "
4582
+ ></div>
4583
+ <div
4584
+ style="
4585
+ position: relative;
4586
+ z-index: 1;
4587
+ max-width: 440px;
4588
+ text-align: center;
4589
+ color: #57575b;
4590
+ "
4591
+ >
4592
+ <div
4593
+ aria-hidden="true"
4594
+ style="
4595
+ margin: 0 auto 18px;
4596
+ display: flex;
4597
+ justify-content: center;
4598
+ "
4599
+ >
4600
+ <div
4601
+ style="
4602
+ display: flex;
4603
+ height: 44px;
4604
+ width: 44px;
4605
+ align-items: center;
4606
+ justify-content: center;
4607
+ border: 1px solid #dfd6fb;
4608
+ border-radius: 8px;
4609
+ background: #eee6fe;
4610
+ color: #57575b;
4611
+ box-shadow: 0 8px 18px rgba(87, 87, 91, 0.14);
4612
+ "
4613
+ >
4614
+ ${this.renderIcon("Lock")}
4615
+ </div>
4616
+ </div>
4617
+ <h2
4618
+ style="
4619
+ margin: 0 0 8px;
4620
+ font-size: 16px;
4621
+ line-height: 1.35;
4622
+ font-weight: 600;
4623
+ color: #010507;
4624
+ "
4625
+ >
4626
+ Enable Intelligence to inspect Threads.
4627
+ </h2>
4628
+ <p
4629
+ style="
4630
+ margin: 0 auto 18px;
4631
+ max-width: 380px;
4632
+ font-size: 13px;
4633
+ line-height: 1.55;
4634
+ color: #57575b;
4635
+ "
4636
+ >
4637
+ Persist conversations and inspect saved thread history from the
4638
+ Inspector.
4639
+ </p>
4640
+ <div
4641
+ style="
4642
+ display: flex;
4643
+ flex-wrap: wrap;
4644
+ justify-content: center;
4645
+ gap: 8px;
4646
+ "
4647
+ >
4648
+ <a
4649
+ href=${this.getTalkToEngineerUrl()}
4650
+ target="_blank"
4651
+ rel="noopener"
4652
+ style="
4653
+ display: inline-flex;
4654
+ min-height: 34px;
4655
+ align-items: center;
4656
+ justify-content: center;
4657
+ gap: 6px;
4658
+ border-radius: 6px;
4659
+ background: #010507;
4660
+ padding: 8px 12px;
4661
+ font-size: 12px;
4662
+ font-weight: 600;
4663
+ color: #ffffff;
4664
+ text-decoration: none;
4665
+ "
4666
+ @click=${this.handleThreadsTalkToEngineerClick}
4667
+ >
4668
+ Talk to an Engineer
4669
+ </a>
4670
+ <a
4671
+ href=${this.getIntelligenceSignupUrl()}
4672
+ target="_blank"
4673
+ rel="noopener"
4674
+ style="
4675
+ display: inline-flex;
4676
+ min-height: 34px;
4677
+ align-items: center;
4678
+ justify-content: center;
4679
+ gap: 6px;
4680
+ border-radius: 6px;
4681
+ border: 1px solid #dbdbe5;
4682
+ background: #ffffff;
4683
+ padding: 8px 12px;
4684
+ font-size: 12px;
4685
+ font-weight: 600;
4686
+ color: #57575b;
4687
+ text-decoration: none;
4688
+ "
4689
+ @click=${this.handleThreadsIntelligenceSignupClick}
4690
+ >
4691
+ Sign up for Intelligence
4692
+ </a>
4693
+ </div>
4694
+ </div>
4695
+ </div>
4696
+ `;
4697
+ }
4330
4698
  renderThreadsView() {
4699
+ if (!this.areThreadEndpointsAvailable()) return this.renderThreadsLockedView();
4331
4700
  const displayThreads = this.selectedContext === "all-agents" ? this._threads : this._threadsByAgent.get(this.selectedContext) ?? [];
4332
4701
  let threadsErrorMessage = null;
4333
4702
  if (this.selectedContext === "all-agents") threadsErrorMessage = this._threadsErrorByAgent.values().next().value?.message ?? null;
4334
4703
  else threadsErrorMessage = this._threadsErrorByAgent.get(this.selectedContext)?.message ?? null;
4335
4704
  const selectedThread = this.selectedThreadId != null ? displayThreads.find((t) => t.id === this.selectedThreadId) ?? null : null;
4705
+ if (!threadsErrorMessage) this.trackThreadsViewStateOnce(displayThreads.length === 0 ? "empty_enabled" : "enabled", displayThreads.length);
4336
4706
  return html`
4337
- <div style="display:flex;height:100%;overflow:hidden;">
4338
- <!-- Left sidebar: thread list -->
4707
+ <div style="display:flex;height:100%;overflow:hidden;flex-direction:column;">
4339
4708
  <div
4340
- style="width:${this.threadListWidth}px;flex-shrink:0;overflow:hidden;display:flex;flex-direction:column;border-right:1px solid #DBDBE5;"
4709
+ style="display:flex;align-items:center;justify-content:flex-end;border-bottom:1px solid #DBDBE5;background:#ffffff;padding:8px 12px;flex-shrink:0;"
4341
4710
  >
4342
- <cpk-thread-list
4343
- style="height:100%;"
4344
- .threads=${displayThreads}
4345
- .selectedThreadId=${this.selectedThreadId}
4346
- .errorMessage=${threadsErrorMessage}
4347
- @threadSelected=${(e) => {
4711
+ <a
4712
+ href=${this.getTalkToEngineerUrl()}
4713
+ target="_blank"
4714
+ rel="noopener"
4715
+ style="display:inline-flex;align-items:center;gap:6px;border-radius:6px;border:1px solid #dbdbe5;background:#ffffff;padding:7px 10px;font-size:12px;font-weight:600;color:#57575b;text-decoration:none;"
4716
+ @click=${this.handleTalkToEngineerClick}
4717
+ >
4718
+ Talk to an Engineer
4719
+ </a>
4720
+ </div>
4721
+ <div style="display:flex;min-height:0;flex:1;overflow:hidden;">
4722
+ <!-- Left sidebar: thread list -->
4723
+ <div
4724
+ style="width:${this.threadListWidth}px;flex-shrink:0;overflow:hidden;display:flex;flex-direction:column;border-right:1px solid #DBDBE5;"
4725
+ >
4726
+ <cpk-thread-list
4727
+ style="height:100%;"
4728
+ .threads=${displayThreads}
4729
+ .selectedThreadId=${this.selectedThreadId}
4730
+ .errorMessage=${threadsErrorMessage}
4731
+ @threadSelected=${(e) => {
4348
4732
  this.selectedThreadId = e.detail;
4349
4733
  this.requestUpdate();
4350
4734
  }}
4351
- ></cpk-thread-list>
4352
- </div>
4353
-
4354
- <!-- Resize divider -->
4355
- <div
4356
- style="width:4px;flex-shrink:0;cursor:col-resize;background:transparent;position:relative;z-index:1;"
4357
- @pointerdown=${this.handleThreadDividerPointerDown}
4358
- @pointermove=${this.handleThreadDividerPointerMove}
4359
- @pointerup=${this.handleThreadDividerPointerUp}
4360
- @pointercancel=${this.handleThreadDividerPointerUp}
4361
- ></div>
4735
+ ></cpk-thread-list>
4736
+ </div>
4362
4737
 
4363
- <!-- Center + right: thread details or empty state -->
4364
- <div style="flex:1;min-width:0;overflow:hidden;display:flex;">
4365
- ${this.selectedThreadId ? html`<cpk-thread-details
4366
- style="flex:1;min-width:0;"
4367
- .threadId=${this.selectedThreadId}
4368
- .thread=${selectedThread}
4369
- .runtimeUrl=${this._core?.runtimeUrl ?? ""}
4370
- .headers=${this._core?.headers ?? {}}
4371
- .threadInspectionAvailable=${this._core?.threadEndpoints?.inspect !== false}
4372
- .liveMessageVersion=${this.selectedThreadId ? this.liveMessageVersion.get(this.selectedThreadId) ?? 0 : 0}
4373
- .agentStateInput=${selectedThread ? this.getLatestStateForAgent(selectedThread.agentId) : null}
4374
- .agentEventsInput=${selectedThread ? this.agentEvents.get(selectedThread.agentId) ?? [] : []}
4375
- ></cpk-thread-details>` : html`
4376
- <div
4377
- style="
4378
- flex: 1;
4379
- display: flex;
4380
- flex-direction: column;
4381
- align-items: center;
4382
- justify-content: center;
4383
- gap: 8px;
4384
- color: #838389;
4385
- "
4386
- >
4387
- <svg
4388
- width="32"
4389
- height="32"
4390
- viewBox="0 0 24 24"
4391
- fill="none"
4392
- stroke="#c0c0c8"
4393
- stroke-width="1.5"
4394
- stroke-linecap="round"
4395
- stroke-linejoin="round"
4738
+ <!-- Resize divider -->
4739
+ <div
4740
+ style="width:4px;flex-shrink:0;cursor:col-resize;background:transparent;position:relative;z-index:1;"
4741
+ @pointerdown=${this.handleThreadDividerPointerDown}
4742
+ @pointermove=${this.handleThreadDividerPointerMove}
4743
+ @pointerup=${this.handleThreadDividerPointerUp}
4744
+ @pointercancel=${this.handleThreadDividerPointerUp}
4745
+ ></div>
4746
+
4747
+ <!-- Center + right: thread details or empty state -->
4748
+ <div style="flex:1;min-width:0;overflow:hidden;display:flex;">
4749
+ ${selectedThread ? html`<cpk-thread-details
4750
+ style="flex:1;min-width:0;"
4751
+ .threadId=${selectedThread.id}
4752
+ .thread=${selectedThread}
4753
+ .runtimeUrl=${this._core?.runtimeUrl ?? ""}
4754
+ .headers=${this._core?.headers ?? {}}
4755
+ .threadInspectionAvailable=${this._core?.threadEndpoints?.inspect !== false}
4756
+ .liveMessageVersion=${this.liveMessageVersion.get(selectedThread.id) ?? 0}
4757
+ .agentStateInput=${this.getLatestStateForAgent(selectedThread.agentId)}
4758
+ .agentEventsInput=${this.agentEvents.get(selectedThread.agentId) ?? []}
4759
+ ></cpk-thread-details>` : html`
4760
+ <div
4761
+ style="
4762
+ flex: 1;
4763
+ display: flex;
4764
+ flex-direction: column;
4765
+ align-items: center;
4766
+ justify-content: center;
4767
+ gap: 8px;
4768
+ color: #838389;
4769
+ "
4396
4770
  >
4397
- <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
4398
- </svg>
4399
- <span style="font-size: 13px">${displayThreads.length === 0 ? "No threads yet" : "Select a thread to inspect"}</span>
4400
- </div>
4401
- `}
4771
+ <svg
4772
+ width="32"
4773
+ height="32"
4774
+ viewBox="0 0 24 24"
4775
+ fill="none"
4776
+ stroke="#c0c0c8"
4777
+ stroke-width="1.5"
4778
+ stroke-linecap="round"
4779
+ stroke-linejoin="round"
4780
+ >
4781
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
4782
+ </svg>
4783
+ <span style="font-size: 13px">${displayThreads.length === 0 ? "No threads yet" : "Select a thread to inspect"}</span>
4784
+ </div>
4785
+ `}
4786
+ </div>
4402
4787
  </div>
4403
4788
  </div>
4404
4789
  `;
@@ -4919,7 +5304,7 @@ ${prettyEvent}</pre
4919
5304
  if (this.contextOptions.filter((opt) => opt.key !== "all-agents").length > 1) this.selectedContext = "all-agents";
4920
5305
  }
4921
5306
  if (key === "threads") {
4922
- if (this.selectedMenu !== "threads" && !this.core?.telemetryDisabled) trackThreadsTabClicked();
5307
+ if (previousMenu !== "threads" && !this.core?.telemetryDisabled) trackThreadsTabClicked(this.getThreadsTelemetryProps());
4923
5308
  this.autoSelectLatestThread();
4924
5309
  }
4925
5310
  if (key === "ag-ui-events" || key === "agents") requestAnimationFrame(() => {
@@ -5343,6 +5728,22 @@ ${prettyEvent}</pre
5343
5728
  ${this.copiedContextItems.has(id) ? "Copied" : "Copy JSON"}
5344
5729
  </button>
5345
5730
  </div>
5731
+ <pre
5732
+ style="
5733
+ margin: 0;
5734
+ max-height: 180px;
5735
+ overflow: auto;
5736
+ white-space: pre-wrap;
5737
+ word-break: break-word;
5738
+ border-radius: 6px;
5739
+ border: 1px solid #eeeef4;
5740
+ background: #f7f7f9;
5741
+ padding: 10px;
5742
+ font-size: 11px;
5743
+ line-height: 1.5;
5744
+ color: #2d2d30;
5745
+ "
5746
+ >${this.formatContextValue(context.value)}</pre>
5346
5747
  ` : html`
5347
5748
  <div class="flex items-center justify-center py-4 text-xs text-gray-500">
5348
5749
  <span>No value available</span>
@@ -5533,8 +5934,9 @@ ${prettyEvent}</pre
5533
5934
  async convertMarkdownToHtml(markdown) {
5534
5935
  const renderer = new marked.Renderer();
5535
5936
  renderer.link = (href, title, text) => {
5536
- return `<a href="${this.escapeHtmlAttr(this.appendRefParam(href ?? ""))}" target="_blank" rel="noopener"${title ? ` title="${this.escapeHtmlAttr(title)}"` : ""}>${text}</a>`;
5937
+ return `<a href="${this.escapeHtmlAttr(this.isSafeAnnouncementHref(href ?? "") ? this.appendRefParam(href ?? "") : "#")}" target="_blank" rel="noopener"${title ? ` title="${this.escapeHtmlAttr(title)}"` : ""}>${text}</a>`;
5537
5938
  };
5939
+ renderer.html = (html) => escapeHtml(html);
5538
5940
  renderer.code = (code, lang) => {
5539
5941
  const safeLang = (lang ?? "").replace(/[^a-z0-9-]/gi, "");
5540
5942
  return `<div class="announcement-code"><pre><code${safeLang ? ` class="language-${safeLang}"` : ""}>${escapeHtml(code)}</code></pre><div class="announcement-code__copy-shield"><button type="button" class="announcement-code__copy" data-copy="${this.encodeBase64(code)}" aria-label="Copy code">Copy</button></div></div>`;
@@ -5544,6 +5946,14 @@ ${prettyEvent}</pre
5544
5946
  async: false
5545
5947
  });
5546
5948
  }
5949
+ isSafeAnnouncementHref(href) {
5950
+ try {
5951
+ const url = new URL(href, typeof window !== "undefined" ? window.location.href : "https://copilotkit.ai");
5952
+ return url.protocol === "http:" || url.protocol === "https:" || url.protocol === "mailto:";
5953
+ } catch {
5954
+ return false;
5955
+ }
5956
+ }
5547
5957
  encodeBase64(value) {
5548
5958
  if (typeof window === "undefined" || typeof window.btoa !== "function") return "";
5549
5959
  const bytes = new TextEncoder().encode(value);
@@ -5558,19 +5968,25 @@ ${prettyEvent}</pre
5558
5968
  for (let i = 0; i < decoded.length; i++) bytes[i] = decoded.charCodeAt(i);
5559
5969
  return new TextDecoder().decode(bytes);
5560
5970
  }
5561
- appendRefParam(href) {
5971
+ appendRefParam(href, ref = "cpk-inspector") {
5562
5972
  try {
5973
+ const isRootRelative = href.startsWith("/") && !href.startsWith("//");
5563
5974
  const url = new URL(href, typeof window !== "undefined" ? window.location.href : "https://copilotkit.ai");
5564
- if (!url.searchParams.has("ref")) url.searchParams.append("ref", "cpk-inspector");
5565
- if (!url.searchParams.has("posthog_distinct_id") && !this.core?.telemetryDisabled) {
5975
+ if (!url.searchParams.has("ref")) url.searchParams.append("ref", ref);
5976
+ if (!url.searchParams.has("posthog_distinct_id") && !this.core?.telemetryDisabled && this.isCopilotKitDestination(url)) {
5566
5977
  const distinctId = getTelemetryDistinctIdForUrl();
5567
5978
  if (distinctId) url.searchParams.append("posthog_distinct_id", distinctId);
5568
5979
  }
5980
+ if (isRootRelative) return `${url.pathname}${url.search}${url.hash}`;
5569
5981
  return url.toString();
5570
5982
  } catch {
5571
5983
  return href;
5572
5984
  }
5573
5985
  }
5986
+ isCopilotKitDestination(url) {
5987
+ const hostname = url.hostname.toLowerCase();
5988
+ return hostname === "copilotkit.ai" || hostname.endsWith(".copilotkit.ai");
5989
+ }
5574
5990
  escapeHtmlAttr(value) {
5575
5991
  return escapeHtml(value).replace(/"/g, "&quot;").replace(/'/g, "&#39;");
5576
5992
  }