@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.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"
@@ -404,6 +406,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
404
406
  this.thread = null;
405
407
  this.runtimeUrl = "";
406
408
  this.headers = {};
409
+ this.threadInspectionAvailable = false;
407
410
  this.agentStateInput = null;
408
411
  this.agentEventsInput = [];
409
412
  this.liveMessageVersion = 0;
@@ -463,6 +466,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
463
466
  thread: { attribute: false },
464
467
  runtimeUrl: { attribute: false },
465
468
  headers: { attribute: false },
469
+ threadInspectionAvailable: { attribute: false },
466
470
  agentStateInput: { attribute: false },
467
471
  agentEventsInput: { attribute: false },
468
472
  liveMessageVersion: { attribute: false },
@@ -1127,7 +1131,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1127
1131
  * so users see an explicit loading indicator on first load.
1128
1132
  */
1129
1133
  async fetchMessages(threadId, silent = false) {
1130
- if (!this.runtimeUrl) {
1134
+ if (!this.runtimeUrl || !this.threadInspectionAvailable) {
1131
1135
  if (!silent) this._conversation = [];
1132
1136
  return;
1133
1137
  }
@@ -1138,7 +1142,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1138
1142
  this._messagesError = null;
1139
1143
  }
1140
1144
  try {
1141
- const res = await fetch(`${this.runtimeUrl}/threads/${encodeURIComponent(threadId)}/messages`, {
1145
+ const res = await fetch(this.getThreadInspectionUrl(threadId, "messages"), {
1142
1146
  headers: { ...this.headers },
1143
1147
  signal: controller.signal
1144
1148
  });
@@ -1159,7 +1163,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1159
1163
  }
1160
1164
  async fetchEvents(threadId) {
1161
1165
  this._eventsNotAvailable = false;
1162
- if (!this.runtimeUrl) {
1166
+ if (!this.runtimeUrl || !this.threadInspectionAvailable) {
1163
1167
  this._fetchedEvents = null;
1164
1168
  return;
1165
1169
  }
@@ -1168,7 +1172,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1168
1172
  this._loadingEvents = true;
1169
1173
  this._eventsError = null;
1170
1174
  try {
1171
- const res = await fetch(`${this.runtimeUrl}/threads/${encodeURIComponent(threadId)}/events`, {
1175
+ const res = await fetch(this.getThreadInspectionUrl(threadId, "events"), {
1172
1176
  headers: { ...this.headers },
1173
1177
  signal: controller.signal
1174
1178
  });
@@ -1193,7 +1197,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1193
1197
  }
1194
1198
  async fetchState(threadId) {
1195
1199
  this._stateNotAvailable = false;
1196
- if (!this.runtimeUrl) {
1200
+ if (!this.runtimeUrl || !this.threadInspectionAvailable) {
1197
1201
  this._fetchedState = null;
1198
1202
  return;
1199
1203
  }
@@ -1202,7 +1206,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1202
1206
  this._loadingState = true;
1203
1207
  this._stateError = null;
1204
1208
  try {
1205
- const res = await fetch(`${this.runtimeUrl}/threads/${encodeURIComponent(threadId)}/state`, {
1209
+ const res = await fetch(this.getThreadInspectionUrl(threadId, "state"), {
1206
1210
  headers: { ...this.headers },
1207
1211
  signal: controller.signal
1208
1212
  });
@@ -1225,6 +1229,9 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1225
1229
  if (!controller.signal.aborted && this.threadId === threadId) this._loadingState = false;
1226
1230
  }
1227
1231
  }
1232
+ getThreadInspectionUrl(threadId, resource) {
1233
+ return `${this.runtimeUrl.replace(/\/+$/, "")}/threads/${encodeURIComponent(threadId)}/${resource}`;
1234
+ }
1228
1235
  mapMessages(messages) {
1229
1236
  const items = [];
1230
1237
  const toolCallMap = /* @__PURE__ */ new Map();
@@ -1879,6 +1886,7 @@ var WebInspectorElement = class extends LitElement {
1879
1886
  this.viewedBannerTimestamps = /* @__PURE__ */ new Set();
1880
1887
  this.pendingBannerViewed = null;
1881
1888
  this.clickedBannerIds = /* @__PURE__ */ new Set();
1889
+ this.viewedThreadsTelemetryStates = /* @__PURE__ */ new Set();
1882
1890
  this.contextState = {
1883
1891
  button: {
1884
1892
  position: {
@@ -2084,6 +2092,27 @@ var WebInspectorElement = class extends LitElement {
2084
2092
  this.expandedTools = /* @__PURE__ */ new Set();
2085
2093
  this.expandedContextItems = /* @__PURE__ */ new Set();
2086
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
+ };
2087
2116
  this.handleThreadDividerPointerDown = (event) => {
2088
2117
  this.threadDividerResizing = true;
2089
2118
  this.threadDividerPointerId = event.pointerId;
@@ -2211,7 +2240,36 @@ var WebInspectorElement = class extends LitElement {
2211
2240
  }
2212
2241
  ];
2213
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
+ }
2214
2271
  subscribeToThreadStore(agentId, store) {
2272
+ if (!this.areThreadEndpointsAvailable()) return;
2215
2273
  if (this._threadStoreSubscriptions.has(agentId)) return;
2216
2274
  const threadsSub = store.select(ɵselectThreads).subscribe((threads) => {
2217
2275
  this._threadsByAgent.set(agentId, threads);
@@ -2252,11 +2310,13 @@ var WebInspectorElement = class extends LitElement {
2252
2310
  if (this.core?.getThreadStore(agentId)) return;
2253
2311
  const core = this.core;
2254
2312
  if (!core?.runtimeUrl) return;
2313
+ if (!this.areThreadEndpointsAvailable()) return;
2255
2314
  const store = ɵcreateThreadStore({ fetch: globalThis.fetch });
2256
2315
  store.start();
2257
2316
  store.setContext({
2258
2317
  runtimeUrl: core.runtimeUrl,
2259
- headers: {},
2318
+ headers: { ...core.headers },
2319
+ wsUrl: core.intelligence?.wsUrl,
2260
2320
  agentId
2261
2321
  });
2262
2322
  this._ownedThreadStores.set(agentId, store);
@@ -2268,6 +2328,16 @@ var WebInspectorElement = class extends LitElement {
2268
2328
  if (!store) return;
2269
2329
  store.refresh();
2270
2330
  }
2331
+ updateOwnedThreadStoreHeaders(headers) {
2332
+ const core = this.core;
2333
+ if (!core?.runtimeUrl) return;
2334
+ for (const [agentId, store] of this._ownedThreadStores) store.setContext({
2335
+ runtimeUrl: core.runtimeUrl,
2336
+ headers: { ...headers },
2337
+ wsUrl: core.intelligence?.wsUrl,
2338
+ agentId
2339
+ });
2340
+ }
2271
2341
  removeOwnedThreadStore(agentId) {
2272
2342
  const store = this._ownedThreadStores.get(agentId);
2273
2343
  if (!store) return;
@@ -2295,7 +2365,8 @@ var WebInspectorElement = class extends LitElement {
2295
2365
  maybeShowDisclosure();
2296
2366
  }
2297
2367
  this.flushPendingBannerViewed();
2298
- for (const agentId of this._ownedThreadStores.keys()) this.refreshOwnedThreadStore(agentId);
2368
+ if (this.areThreadEndpointsAvailable()) for (const agentId of this._ownedThreadStores.keys()) this.refreshOwnedThreadStore(agentId);
2369
+ else this.teardownOwnedThreadStores();
2299
2370
  } else {
2300
2371
  this._threadsByAgent.clear();
2301
2372
  this._threads = [];
@@ -2306,6 +2377,9 @@ var WebInspectorElement = class extends LitElement {
2306
2377
  this.coreProperties = properties;
2307
2378
  this.requestUpdate();
2308
2379
  },
2380
+ onHeadersChanged: ({ headers }) => {
2381
+ this.updateOwnedThreadStoreHeaders(headers);
2382
+ },
2309
2383
  onError: ({ code, error }) => {
2310
2384
  this.lastCoreError = {
2311
2385
  code,
@@ -2338,6 +2412,13 @@ var WebInspectorElement = class extends LitElement {
2338
2412
  };
2339
2413
  this.coreUnsubscribe = core.subscribe(this.coreSubscriber).unsubscribe;
2340
2414
  this.processAgentsChanged(core.agents);
2415
+ if (core.runtimeConnectionStatus === "connected") {
2416
+ if (!core.telemetryDisabled) {
2417
+ ensureTelemetryDistinctId();
2418
+ maybeShowDisclosure();
2419
+ }
2420
+ this.flushPendingBannerViewed();
2421
+ }
2341
2422
  const threadStores = typeof core.getThreadStores === "function" ? core.getThreadStores() : {};
2342
2423
  for (const [agentId, store] of Object.entries(threadStores)) this.subscribeToThreadStore(agentId, store);
2343
2424
  if (core.context) this.contextStore = this.normalizeContextStore(core.context);
@@ -2426,10 +2507,10 @@ var WebInspectorElement = class extends LitElement {
2426
2507
  onRunStartedEvent: ({ event }) => {
2427
2508
  this.recordAgentEvent(agentId, "RUN_STARTED", event);
2428
2509
  },
2429
- onRunFinishedEvent: ({ event, result }) => {
2510
+ onRunFinishedEvent: (params) => {
2430
2511
  this.recordAgentEvent(agentId, "RUN_FINISHED", {
2431
- event,
2432
- result
2512
+ event: params.event,
2513
+ result: "result" in params ? params.result : void 0
2433
2514
  });
2434
2515
  this.refreshOwnedThreadStore(agentId);
2435
2516
  },
@@ -2747,13 +2828,13 @@ ${argsString}</pre
2747
2828
  }
2748
2829
  getEventBadgeClasses(type) {
2749
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`;
2750
2832
  if (type.startsWith("RUN_")) return `${base} bg-blue-50 text-blue-700 border-blue-200`;
2751
2833
  if (type.startsWith("TEXT_MESSAGE")) return `${base} bg-emerald-50 text-emerald-700 border-emerald-200`;
2752
2834
  if (type.startsWith("TOOL_CALL")) return `${base} bg-amber-50 text-amber-700 border-amber-200`;
2753
2835
  if (type.startsWith("REASONING")) return `${base} bg-fuchsia-50 text-fuchsia-700 border-fuchsia-200`;
2754
2836
  if (type.startsWith("STATE")) return `${base} bg-violet-50 text-violet-700 border-violet-200`;
2755
2837
  if (type.startsWith("MESSAGES")) return `${base} bg-sky-50 text-sky-700 border-sky-200`;
2756
- if (type === "RUN_ERROR") return `${base} bg-rose-50 text-rose-700 border-rose-200`;
2757
2838
  return `${base} bg-gray-100 text-gray-600 border-gray-200`;
2758
2839
  }
2759
2840
  stringifyPayload(payload, pretty) {
@@ -4310,77 +4391,399 @@ ${argsString}</pre
4310
4391
  cta_label: this.announcementCtaLabel ?? void 0
4311
4392
  });
4312
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
+ }
4313
4698
  renderThreadsView() {
4699
+ if (!this.areThreadEndpointsAvailable()) return this.renderThreadsLockedView();
4314
4700
  const displayThreads = this.selectedContext === "all-agents" ? this._threads : this._threadsByAgent.get(this.selectedContext) ?? [];
4315
4701
  let threadsErrorMessage = null;
4316
4702
  if (this.selectedContext === "all-agents") threadsErrorMessage = this._threadsErrorByAgent.values().next().value?.message ?? null;
4317
4703
  else threadsErrorMessage = this._threadsErrorByAgent.get(this.selectedContext)?.message ?? null;
4318
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);
4319
4706
  return html`
4320
- <div style="display:flex;height:100%;overflow:hidden;">
4321
- <!-- Left sidebar: thread list -->
4707
+ <div style="display:flex;height:100%;overflow:hidden;flex-direction:column;">
4322
4708
  <div
4323
- 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;"
4324
4710
  >
4325
- <cpk-thread-list
4326
- style="height:100%;"
4327
- .threads=${displayThreads}
4328
- .selectedThreadId=${this.selectedThreadId}
4329
- .errorMessage=${threadsErrorMessage}
4330
- @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) => {
4331
4732
  this.selectedThreadId = e.detail;
4332
4733
  this.requestUpdate();
4333
4734
  }}
4334
- ></cpk-thread-list>
4335
- </div>
4336
-
4337
- <!-- Resize divider -->
4338
- <div
4339
- style="width:4px;flex-shrink:0;cursor:col-resize;background:transparent;position:relative;z-index:1;"
4340
- @pointerdown=${this.handleThreadDividerPointerDown}
4341
- @pointermove=${this.handleThreadDividerPointerMove}
4342
- @pointerup=${this.handleThreadDividerPointerUp}
4343
- @pointercancel=${this.handleThreadDividerPointerUp}
4344
- ></div>
4735
+ ></cpk-thread-list>
4736
+ </div>
4345
4737
 
4346
- <!-- Center + right: thread details or empty state -->
4347
- <div style="flex:1;min-width:0;overflow:hidden;display:flex;">
4348
- ${this.selectedThreadId ? html`<cpk-thread-details
4349
- style="flex:1;min-width:0;"
4350
- .threadId=${this.selectedThreadId}
4351
- .thread=${selectedThread}
4352
- .runtimeUrl=${this._core?.runtimeUrl ?? ""}
4353
- .headers=${this._core?.headers ?? {}}
4354
- .liveMessageVersion=${this.selectedThreadId ? this.liveMessageVersion.get(this.selectedThreadId) ?? 0 : 0}
4355
- .agentStateInput=${selectedThread ? this.getLatestStateForAgent(selectedThread.agentId) : null}
4356
- .agentEventsInput=${selectedThread ? this.agentEvents.get(selectedThread.agentId) ?? [] : []}
4357
- ></cpk-thread-details>` : html`
4358
- <div
4359
- style="
4360
- flex: 1;
4361
- display: flex;
4362
- flex-direction: column;
4363
- align-items: center;
4364
- justify-content: center;
4365
- gap: 8px;
4366
- color: #838389;
4367
- "
4368
- >
4369
- <svg
4370
- width="32"
4371
- height="32"
4372
- viewBox="0 0 24 24"
4373
- fill="none"
4374
- stroke="#c0c0c8"
4375
- stroke-width="1.5"
4376
- stroke-linecap="round"
4377
- 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
+ "
4378
4770
  >
4379
- <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
4380
- </svg>
4381
- <span style="font-size: 13px">${displayThreads.length === 0 ? "No threads yet" : "Select a thread to inspect"}</span>
4382
- </div>
4383
- `}
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>
4384
4787
  </div>
4385
4788
  </div>
4386
4789
  `;
@@ -4901,7 +5304,7 @@ ${prettyEvent}</pre
4901
5304
  if (this.contextOptions.filter((opt) => opt.key !== "all-agents").length > 1) this.selectedContext = "all-agents";
4902
5305
  }
4903
5306
  if (key === "threads") {
4904
- if (this.selectedMenu !== "threads" && !this.core?.telemetryDisabled) trackThreadsTabClicked();
5307
+ if (previousMenu !== "threads" && !this.core?.telemetryDisabled) trackThreadsTabClicked(this.getThreadsTelemetryProps());
4905
5308
  this.autoSelectLatestThread();
4906
5309
  }
4907
5310
  if (key === "ag-ui-events" || key === "agents") requestAnimationFrame(() => {
@@ -5325,6 +5728,22 @@ ${prettyEvent}</pre
5325
5728
  ${this.copiedContextItems.has(id) ? "Copied" : "Copy JSON"}
5326
5729
  </button>
5327
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>
5328
5747
  ` : html`
5329
5748
  <div class="flex items-center justify-center py-4 text-xs text-gray-500">
5330
5749
  <span>No value available</span>
@@ -5515,8 +5934,9 @@ ${prettyEvent}</pre
5515
5934
  async convertMarkdownToHtml(markdown) {
5516
5935
  const renderer = new marked.Renderer();
5517
5936
  renderer.link = (href, title, text) => {
5518
- 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>`;
5519
5938
  };
5939
+ renderer.html = (html) => escapeHtml(html);
5520
5940
  renderer.code = (code, lang) => {
5521
5941
  const safeLang = (lang ?? "").replace(/[^a-z0-9-]/gi, "");
5522
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>`;
@@ -5526,6 +5946,14 @@ ${prettyEvent}</pre
5526
5946
  async: false
5527
5947
  });
5528
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
+ }
5529
5957
  encodeBase64(value) {
5530
5958
  if (typeof window === "undefined" || typeof window.btoa !== "function") return "";
5531
5959
  const bytes = new TextEncoder().encode(value);
@@ -5540,19 +5968,25 @@ ${prettyEvent}</pre
5540
5968
  for (let i = 0; i < decoded.length; i++) bytes[i] = decoded.charCodeAt(i);
5541
5969
  return new TextDecoder().decode(bytes);
5542
5970
  }
5543
- appendRefParam(href) {
5971
+ appendRefParam(href, ref = "cpk-inspector") {
5544
5972
  try {
5973
+ const isRootRelative = href.startsWith("/") && !href.startsWith("//");
5545
5974
  const url = new URL(href, typeof window !== "undefined" ? window.location.href : "https://copilotkit.ai");
5546
- if (!url.searchParams.has("ref")) url.searchParams.append("ref", "cpk-inspector");
5547
- 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)) {
5548
5977
  const distinctId = getTelemetryDistinctIdForUrl();
5549
5978
  if (distinctId) url.searchParams.append("posthog_distinct_id", distinctId);
5550
5979
  }
5980
+ if (isRootRelative) return `${url.pathname}${url.search}${url.hash}`;
5551
5981
  return url.toString();
5552
5982
  } catch {
5553
5983
  return href;
5554
5984
  }
5555
5985
  }
5986
+ isCopilotKitDestination(url) {
5987
+ const hostname = url.hostname.toLowerCase();
5988
+ return hostname === "copilotkit.ai" || hostname.endsWith(".copilotkit.ai");
5989
+ }
5556
5990
  escapeHtmlAttr(value) {
5557
5991
  return escapeHtml(value).replace(/"/g, "&quot;").replace(/'/g, "&#39;");
5558
5992
  }