@copilotkit/web-inspector 1.61.1 → 1.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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";
@@ -13,6 +13,7 @@ import { CopilotKitCoreRuntimeConnectionStatus, ɵcreateThreadStore, ɵselectThr
13
13
 
14
14
  //#region src/index.ts
15
15
  const WEB_INSPECTOR_TAG = "cpk-web-inspector";
16
+ const THREAD_INSPECTOR_TAG = "cpk-thread-inspector";
16
17
  const EDGE_MARGIN = 16;
17
18
  const DRAG_THRESHOLD = 6;
18
19
  const MIN_WINDOW_WIDTH = 600;
@@ -32,6 +33,8 @@ const DEFAULT_WINDOW_SIZE = {
32
33
  const DOCKED_LEFT_WIDTH = 500;
33
34
  const MAX_AGENT_EVENTS = 200;
34
35
  const MAX_TOTAL_EVENTS = 500;
36
+ const INTELLIGENCE_SIGNUP_URL = "https://go.copilotkit.ai/intelligence-signup";
37
+ const TALK_TO_ENGINEER_URL = "https://www.copilotkit.ai/talk-to-an-engineer";
35
38
  const AGENT_EVENT_TYPES = [
36
39
  "RUN_STARTED",
37
40
  "RUN_FINISHED",
@@ -107,14 +110,14 @@ function eventColors(type) {
107
110
  bg: "rgba(190,194,255,0.102)",
108
111
  fg: "#5558B2"
109
112
  };
113
+ if (type === "RUN_ERROR" || type === "ERROR") return {
114
+ bg: "rgba(250,95,103,0.13)",
115
+ fg: "#c0333a"
116
+ };
110
117
  if (type.startsWith("RUN_") || type.startsWith("STEP_")) return {
111
118
  bg: "rgba(255,172,77,0.2)",
112
119
  fg: "#996300"
113
120
  };
114
- if (type === "ERROR") return {
115
- bg: "rgba(250,95,103,0.13)",
116
- fg: "#c0333a"
117
- };
118
121
  return {
119
122
  bg: "#F7F7F9",
120
123
  fg: "#838389"
@@ -351,28 +354,30 @@ var CpkThreadList = class extends LitElement {
351
354
  ${filtered.length === 0 ? html`
352
355
  <div class="cpk-tl__empty">
353
356
  ${this.errorMessage ? html`
354
- <svg
355
- width="24"
356
- height="24"
357
- viewBox="0 0 24 24"
358
- fill="none"
359
- stroke="currentColor"
360
- stroke-width="1.5"
361
- stroke-linecap="round"
362
- stroke-linejoin="round"
363
- class="cpk-tl__empty-icon"
357
+ <svg
358
+ width="24"
359
+ height="24"
360
+ viewBox="0 0 24 24"
361
+ fill="none"
362
+ stroke="currentColor"
363
+ stroke-width="1.5"
364
+ stroke-linecap="round"
365
+ stroke-linejoin="round"
366
+ class="cpk-tl__empty-icon"
367
+ >
368
+ <circle cx="12" cy="12" r="10" />
369
+ <line x1="12" y1="8" x2="12" y2="12" />
370
+ <line x1="12" y1="16" x2="12.01" y2="16" />
371
+ </svg>
372
+ <div>
373
+ Failed to load threads
374
+ <div
375
+ style="font-size:11px;margin-top:4px;color:#c0333a;"
364
376
  >
365
- <circle cx="12" cy="12" r="10" />
366
- <line x1="12" y1="8" x2="12" y2="12" />
367
- <line x1="12" y1="16" x2="12.01" y2="16" />
368
- </svg>
369
- <div>
370
- Failed to load threads
371
- <div style="font-size:11px;margin-top:4px;color:#c0333a;">
372
- ${this.errorMessage}
373
- </div>
377
+ ${this.errorMessage}
374
378
  </div>
375
- ` : this.threads.length === 0 ? html`
379
+ </div>
380
+ ` : this.threads.length === 0 ? html`
376
381
  <svg
377
382
  width="24"
378
383
  height="24"
@@ -397,10 +402,11 @@ var CpkThreadList = class extends LitElement {
397
402
  `;
398
403
  }
399
404
  };
400
- var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
405
+ var CpkThreadInspector = class CpkThreadInspector extends LitElement {
401
406
  constructor(..._args2) {
402
407
  super(..._args2);
403
408
  this.threadId = null;
409
+ this.provider = null;
404
410
  this.thread = null;
405
411
  this.runtimeUrl = "";
406
412
  this.headers = {};
@@ -408,7 +414,8 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
408
414
  this.agentStateInput = null;
409
415
  this.agentEventsInput = [];
410
416
  this.liveMessageVersion = 0;
411
- this._tab = "conversation";
417
+ this._tab = "timeline";
418
+ this._fetchedMetadata = null;
412
419
  this._conversation = [];
413
420
  this._fetchedEvents = null;
414
421
  this._fetchedState = null;
@@ -425,12 +432,15 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
425
432
  this._eventsNotAvailable = false;
426
433
  this._stateNotAvailable = false;
427
434
  this._panelInitializing = false;
428
- this._activatedTabs = new Set(["conversation"]);
435
+ this._activatedTabs = new Set(["timeline"]);
429
436
  this._panelTplCache = /* @__PURE__ */ new Map();
437
+ this._timelineItemsCache = null;
438
+ this._liveEventsWithSourceIndexCache = null;
430
439
  this._eventsFetched = false;
431
440
  this._stateFetched = false;
432
- this._lastFetchedThreadId = null;
441
+ this._lastLoadKey = null;
433
442
  this._lastSeenLiveMessageVersion = 0;
443
+ this._metadataAbort = null;
434
444
  this._messagesAbort = null;
435
445
  this._eventsAbort = null;
436
446
  this._stateAbort = null;
@@ -461,6 +471,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
461
471
  static {
462
472
  this.properties = {
463
473
  threadId: { attribute: false },
474
+ provider: { attribute: false },
464
475
  thread: { attribute: false },
465
476
  runtimeUrl: { attribute: false },
466
477
  headers: { attribute: false },
@@ -469,6 +480,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
469
480
  agentEventsInput: { attribute: false },
470
481
  liveMessageVersion: { attribute: false },
471
482
  _tab: { state: true },
483
+ _fetchedMetadata: { state: true },
472
484
  _conversation: { state: true },
473
485
  _fetchedEvents: { state: true },
474
486
  _fetchedState: { state: true },
@@ -494,22 +506,52 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
494
506
  static {
495
507
  this.TAB_LIST = [
496
508
  {
497
- id: "conversation",
498
- label: "Conversation"
509
+ id: "timeline",
510
+ label: "Timeline"
499
511
  },
500
512
  {
501
- id: "agent-state",
502
- label: "Agent State"
513
+ id: "raw-events",
514
+ label: "Raw AG-UI Events"
503
515
  },
504
516
  {
505
- id: "ag-ui-events",
506
- label: "AG-UI Events"
517
+ id: "state",
518
+ label: "State"
507
519
  }
508
520
  ];
509
521
  }
522
+ static {
523
+ this.providerIds = /* @__PURE__ */ new WeakMap();
524
+ }
525
+ static {
526
+ this.nextProviderId = 1;
527
+ }
528
+ static providerLoadKey(provider) {
529
+ if (!provider) return "provider:none";
530
+ let id = CpkThreadInspector.providerIds.get(provider);
531
+ if (!id) {
532
+ id = CpkThreadInspector.nextProviderId;
533
+ CpkThreadInspector.nextProviderId += 1;
534
+ CpkThreadInspector.providerIds.set(provider, id);
535
+ }
536
+ return [
537
+ `provider:${id}`,
538
+ provider.getThreadMetadata ? "metadata:1" : "metadata:0",
539
+ provider.getMessages ? "messages:1" : "messages:0",
540
+ provider.getEvents ? "events:1" : "events:0",
541
+ provider.getState ? "state:1" : "state:0"
542
+ ].join("|");
543
+ }
544
+ /**
545
+ * Build a deterministic signature for runtime fetch headers so auth/CSRF
546
+ * changes invalidate cached thread data even when the selected thread is
547
+ * otherwise unchanged.
548
+ */
549
+ static headersLoadKey(headers) {
550
+ return JSON.stringify(Object.entries(headers).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey)));
551
+ }
510
552
  renderTabContent(id) {
511
- if (id === "conversation") return this.renderConversation();
512
- if (id === "agent-state") return this.renderState();
553
+ if (id === "timeline") return this.renderTimeline();
554
+ if (id === "state") return this.renderState();
513
555
  return this.renderEvents();
514
556
  }
515
557
  activateTab(id) {
@@ -527,10 +569,10 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
527
569
  }
528
570
  maybeFetchTabData(id) {
529
571
  if (!this.threadId) return;
530
- if (id === "ag-ui-events" && !this._eventsFetched) {
572
+ if ((id === "timeline" || id === "raw-events") && !this._eventsFetched) {
531
573
  this._eventsFetched = true;
532
574
  this.fetchEvents(this.threadId);
533
- } else if (id === "agent-state" && !this._stateFetched) {
575
+ } else if (id === "state" && !this._stateFetched) {
534
576
  this._stateFetched = true;
535
577
  this.fetchState(this.threadId);
536
578
  }
@@ -662,6 +704,42 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
662
704
  flex-shrink: 0;
663
705
  }
664
706
 
707
+ .cpk-td__metadata-strip {
708
+ display: flex;
709
+ gap: 6px;
710
+ flex-wrap: wrap;
711
+ padding: 10px 16px;
712
+ border-bottom: 1px solid #e9e9ef;
713
+ background: #fbfbfd;
714
+ flex-shrink: 0;
715
+ }
716
+
717
+ .cpk-td__metadata-pill {
718
+ display: inline-flex;
719
+ align-items: center;
720
+ gap: 5px;
721
+ max-width: 220px;
722
+ padding: 3px 7px;
723
+ border: 1px solid #e9e9ef;
724
+ border-radius: 5px;
725
+ background: #ffffff;
726
+ color: #57575b;
727
+ font-family: "Spline Sans Mono", monospace;
728
+ font-size: 10px;
729
+ white-space: nowrap;
730
+ }
731
+
732
+ .cpk-td__metadata-label {
733
+ color: #838389;
734
+ text-transform: uppercase;
735
+ font-size: 9px;
736
+ }
737
+
738
+ .cpk-td__metadata-value {
739
+ overflow: hidden;
740
+ text-overflow: ellipsis;
741
+ }
742
+
665
743
  /*
666
744
  * Each tab's content is wrapped in this panel so the keep-mounted
667
745
  * inactive panels can be hidden via display:none without disturbing
@@ -882,6 +960,81 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
882
960
  background: #e9e9ef;
883
961
  }
884
962
 
963
+ /* ── Interaction timeline ───────────────────────────────────────── */
964
+ .cpk-td__timeline-item {
965
+ border: 1px solid #e9e9ef;
966
+ border-radius: 6px;
967
+ background: #ffffff;
968
+ overflow: hidden;
969
+ }
970
+
971
+ .cpk-td__timeline-item--warning {
972
+ border-color: rgba(250, 95, 103, 0.35);
973
+ background: rgba(250, 95, 103, 0.04);
974
+ }
975
+
976
+ .cpk-td__timeline-header {
977
+ display: flex;
978
+ align-items: center;
979
+ gap: 8px;
980
+ padding: 7px 10px;
981
+ background: #f7f7f9;
982
+ }
983
+
984
+ .cpk-td__timeline-kind {
985
+ font-family: "Spline Sans Mono", monospace;
986
+ font-size: 9px;
987
+ font-weight: 600;
988
+ text-transform: uppercase;
989
+ color: #5558b2;
990
+ }
991
+
992
+ .cpk-td__timeline-title {
993
+ flex: 1;
994
+ min-width: 0;
995
+ font-size: 12px;
996
+ font-weight: 500;
997
+ color: #010507;
998
+ overflow: hidden;
999
+ text-overflow: ellipsis;
1000
+ white-space: nowrap;
1001
+ }
1002
+
1003
+ .cpk-td__timeline-time {
1004
+ font-family: "Spline Sans Mono", monospace;
1005
+ font-size: 9px;
1006
+ color: #838389;
1007
+ flex-shrink: 0;
1008
+ }
1009
+
1010
+ .cpk-td__timeline-body {
1011
+ padding: 9px 10px;
1012
+ font-size: 12px;
1013
+ line-height: 1.55;
1014
+ color: #57575b;
1015
+ white-space: pre-wrap;
1016
+ word-break: break-word;
1017
+ border-top: 1px solid #e9e9ef;
1018
+ }
1019
+
1020
+ .cpk-td__source-link {
1021
+ margin: 0;
1022
+ padding: 0;
1023
+ border: none;
1024
+ background: transparent;
1025
+ color: #5558b2;
1026
+ cursor: pointer;
1027
+ font-family: "Spline Sans Mono", monospace;
1028
+ font-size: 9px;
1029
+ text-decoration: underline;
1030
+ text-underline-offset: 2px;
1031
+ flex-shrink: 0;
1032
+ }
1033
+
1034
+ .cpk-td__source-link:hover {
1035
+ color: #010507;
1036
+ }
1037
+
885
1038
  /* ── Generative UI ──────────────────────────────────────────────── */
886
1039
  @keyframes cpk-genui-enter {
887
1040
  from {
@@ -1091,26 +1244,21 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1091
1244
  `;
1092
1245
  }
1093
1246
  updated(_changed) {
1094
- if (this.threadId !== this._lastFetchedThreadId) {
1095
- this._lastFetchedThreadId = this.threadId;
1247
+ const loadKey = this.currentLoadKey();
1248
+ if (loadKey !== this._lastLoadKey) {
1249
+ this._lastLoadKey = loadKey;
1096
1250
  this._lastSeenLiveMessageVersion = this.liveMessageVersion;
1097
- this._tab = "conversation";
1098
- this._activatedTabs = new Set(["conversation"]);
1099
- this._panelTplCache = /* @__PURE__ */ new Map();
1100
- this._expandedTools = /* @__PURE__ */ new Set();
1101
- this._expandedMessages = /* @__PURE__ */ new Set();
1102
- this._messagesAbort?.abort();
1103
- this._messagesAbort = null;
1104
- this._eventsAbort?.abort();
1105
- this._eventsAbort = null;
1106
- this._stateAbort?.abort();
1107
- this._stateAbort = null;
1108
- this._eventsFetched = false;
1109
- this._stateFetched = false;
1110
- this._fetchedEvents = null;
1111
- this._fetchedState = null;
1112
- if (this.threadId) this.fetchMessages(this.threadId);
1113
- else this._conversation = [];
1251
+ this.resetLoadedThreadData();
1252
+ if (this.threadId) {
1253
+ this.fetchMetadata(this.threadId);
1254
+ if (this.canFetchEvents()) {
1255
+ this._eventsFetched = true;
1256
+ this.fetchEvents(this.threadId);
1257
+ } else this.fetchMessages(this.threadId);
1258
+ } else {
1259
+ this._fetchedMetadata = null;
1260
+ this._conversation = [];
1261
+ }
1114
1262
  } else if (this.threadId && this.liveMessageVersion !== this._lastSeenLiveMessageVersion) {
1115
1263
  this._lastSeenLiveMessageVersion = this.liveMessageVersion;
1116
1264
  this._messagesAbort?.abort();
@@ -1118,6 +1266,70 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1118
1266
  this.fetchMessages(this.threadId, true);
1119
1267
  }
1120
1268
  }
1269
+ canFetchMessages() {
1270
+ return !!this.provider?.getMessages || !!this.runtimeUrl && this.threadInspectionAvailable;
1271
+ }
1272
+ canFetchEvents() {
1273
+ return !!this.provider?.getEvents || !!this.runtimeUrl && this.threadInspectionAvailable;
1274
+ }
1275
+ canFetchState() {
1276
+ return !!this.provider?.getState || !!this.runtimeUrl && this.threadInspectionAvailable;
1277
+ }
1278
+ currentLoadKey() {
1279
+ return [
1280
+ this.threadId ?? "thread:none",
1281
+ CpkThreadInspector.providerLoadKey(this.provider),
1282
+ `runtime:${this.runtimeUrl}`,
1283
+ `headers:${CpkThreadInspector.headersLoadKey(this.headers)}`,
1284
+ `inspect:${this.threadInspectionAvailable ? "1" : "0"}`
1285
+ ].join("||");
1286
+ }
1287
+ resetLoadedThreadData() {
1288
+ this._tab = "timeline";
1289
+ this._activatedTabs = new Set(["timeline"]);
1290
+ this._panelTplCache = /* @__PURE__ */ new Map();
1291
+ this._timelineItemsCache = null;
1292
+ this._liveEventsWithSourceIndexCache = null;
1293
+ this._expandedTools = /* @__PURE__ */ new Set();
1294
+ this._expandedMessages = /* @__PURE__ */ new Set();
1295
+ this._metadataAbort?.abort();
1296
+ this._metadataAbort = null;
1297
+ this._messagesAbort?.abort();
1298
+ this._messagesAbort = null;
1299
+ this._eventsAbort?.abort();
1300
+ this._eventsAbort = null;
1301
+ this._stateAbort?.abort();
1302
+ this._stateAbort = null;
1303
+ this._eventsFetched = false;
1304
+ this._stateFetched = false;
1305
+ this._eventsNotAvailable = false;
1306
+ this._stateNotAvailable = false;
1307
+ this._loadingMessages = false;
1308
+ this._loadingEvents = false;
1309
+ this._loadingState = false;
1310
+ this._messagesError = null;
1311
+ this._eventsError = null;
1312
+ this._stateError = null;
1313
+ this._fetchedMetadata = null;
1314
+ this._conversation = [];
1315
+ this._fetchedEvents = null;
1316
+ this._fetchedState = null;
1317
+ }
1318
+ async fetchMetadata(threadId) {
1319
+ if (!this.provider?.getThreadMetadata) return;
1320
+ this._metadataAbort?.abort();
1321
+ const controller = new AbortController();
1322
+ this._metadataAbort = controller;
1323
+ try {
1324
+ const metadata = await this.provider.getThreadMetadata(threadId, { signal: controller.signal });
1325
+ if (controller.signal.aborted || this.threadId !== threadId) return;
1326
+ this._fetchedMetadata = metadata;
1327
+ } catch (err) {
1328
+ if (err instanceof Error && err.name === "AbortError") return;
1329
+ if (this.threadId !== threadId) return;
1330
+ this._fetchedMetadata = null;
1331
+ }
1332
+ }
1121
1333
  /**
1122
1334
  * Fetch the canonical conversation for `threadId` from the runtime.
1123
1335
  *
@@ -1129,10 +1341,11 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1129
1341
  * so users see an explicit loading indicator on first load.
1130
1342
  */
1131
1343
  async fetchMessages(threadId, silent = false) {
1132
- if (!this.runtimeUrl || !this.threadInspectionAvailable) {
1344
+ if (!this.canFetchMessages()) {
1133
1345
  if (!silent) this._conversation = [];
1134
1346
  return;
1135
1347
  }
1348
+ this._messagesAbort?.abort();
1136
1349
  const controller = new AbortController();
1137
1350
  this._messagesAbort = controller;
1138
1351
  if (!silent) {
@@ -1140,15 +1353,9 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1140
1353
  this._messagesError = null;
1141
1354
  }
1142
1355
  try {
1143
- const res = await fetch(`${this.runtimeUrl}/threads/${encodeURIComponent(threadId)}/messages`, {
1144
- headers: { ...this.headers },
1145
- signal: controller.signal
1146
- });
1147
- if (controller.signal.aborted || this.threadId !== threadId) return;
1148
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
1149
- const data = await res.json();
1356
+ const messages = this.provider?.getMessages ? await this.provider.getMessages(threadId, { signal: controller.signal }) : await this.fetchRuntimeMessages(threadId, controller.signal);
1150
1357
  if (controller.signal.aborted || this.threadId !== threadId) return;
1151
- this._conversation = this.mapMessages(data.messages);
1358
+ this._conversation = this.mapMessages(messages);
1152
1359
  } catch (err) {
1153
1360
  if (err instanceof Error && err.name === "AbortError") return;
1154
1361
  if (!silent) {
@@ -1160,64 +1367,62 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1160
1367
  }
1161
1368
  }
1162
1369
  async fetchEvents(threadId) {
1163
- this._eventsNotAvailable = false;
1164
- if (!this.runtimeUrl || !this.threadInspectionAvailable) {
1370
+ if (!this.canFetchEvents()) {
1165
1371
  this._fetchedEvents = null;
1166
1372
  return;
1167
1373
  }
1374
+ this._eventsAbort?.abort();
1168
1375
  const controller = new AbortController();
1169
1376
  this._eventsAbort = controller;
1170
1377
  this._loadingEvents = true;
1171
1378
  this._eventsError = null;
1172
1379
  try {
1173
- const res = await fetch(`${this.runtimeUrl}/threads/${encodeURIComponent(threadId)}/events`, {
1174
- headers: { ...this.headers },
1175
- signal: controller.signal
1176
- });
1380
+ const result = this.provider?.getEvents ? {
1381
+ status: "available",
1382
+ events: await this.provider.getEvents(threadId, { signal: controller.signal })
1383
+ } : await this.fetchRuntimeEvents(threadId, controller.signal);
1177
1384
  if (controller.signal.aborted || this.threadId !== threadId) return;
1178
- if (res.status === 501) {
1385
+ if (result.status === "not-available") {
1179
1386
  this._eventsNotAvailable = true;
1180
- this._fetchedEvents = null;
1387
+ this._fetchedEvents = [];
1388
+ if (this.canFetchMessages()) this.fetchMessages(threadId);
1181
1389
  return;
1182
1390
  }
1183
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
1184
- const data = await res.json();
1185
- if (controller.signal.aborted || this.threadId !== threadId) return;
1186
- this._fetchedEvents = this.mapApiEvents(data.events);
1391
+ const mappedEvents = this.mapApiEvents(result.events);
1392
+ this._fetchedEvents = mappedEvents;
1393
+ if (mappedEvents.length === 0 && this.canFetchMessages()) this.fetchMessages(threadId);
1187
1394
  } catch (err) {
1188
1395
  if (err instanceof Error && err.name === "AbortError") return;
1189
1396
  if (this.threadId !== threadId) return;
1190
1397
  this._eventsError = err instanceof Error ? err.message : "Failed to load events";
1191
1398
  this._fetchedEvents = [];
1399
+ if (this.canFetchMessages()) this.fetchMessages(threadId);
1192
1400
  } finally {
1193
1401
  if (!controller.signal.aborted && this.threadId === threadId) this._loadingEvents = false;
1194
1402
  }
1195
1403
  }
1196
1404
  async fetchState(threadId) {
1197
- this._stateNotAvailable = false;
1198
- if (!this.runtimeUrl || !this.threadInspectionAvailable) {
1405
+ if (!this.canFetchState()) {
1199
1406
  this._fetchedState = null;
1200
1407
  return;
1201
1408
  }
1409
+ this._stateAbort?.abort();
1202
1410
  const controller = new AbortController();
1203
1411
  this._stateAbort = controller;
1204
1412
  this._loadingState = true;
1205
1413
  this._stateError = null;
1206
1414
  try {
1207
- const res = await fetch(`${this.runtimeUrl}/threads/${encodeURIComponent(threadId)}/state`, {
1208
- headers: { ...this.headers },
1209
- signal: controller.signal
1210
- });
1415
+ const result = this.provider?.getState ? {
1416
+ status: "available",
1417
+ state: await this.provider.getState(threadId, { signal: controller.signal })
1418
+ } : await this.fetchRuntimeState(threadId, controller.signal);
1211
1419
  if (controller.signal.aborted || this.threadId !== threadId) return;
1212
- if (res.status === 501) {
1420
+ if (result.status === "not-available") {
1213
1421
  this._stateNotAvailable = true;
1214
1422
  this._fetchedState = null;
1215
1423
  return;
1216
1424
  }
1217
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
1218
- const data = await res.json();
1219
- if (controller.signal.aborted || this.threadId !== threadId) return;
1220
- this._fetchedState = data.state ?? null;
1425
+ this._fetchedState = result.state ?? null;
1221
1426
  } catch (err) {
1222
1427
  if (err instanceof Error && err.name === "AbortError") return;
1223
1428
  if (this.threadId !== threadId) return;
@@ -1227,6 +1432,41 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1227
1432
  if (!controller.signal.aborted && this.threadId === threadId) this._loadingState = false;
1228
1433
  }
1229
1434
  }
1435
+ async fetchRuntimeMessages(threadId, signal) {
1436
+ const res = await fetch(this.getThreadInspectionUrl(threadId, "messages"), {
1437
+ headers: { ...this.headers },
1438
+ signal
1439
+ });
1440
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
1441
+ return (await res.json()).messages;
1442
+ }
1443
+ async fetchRuntimeEvents(threadId, signal) {
1444
+ const res = await fetch(this.getThreadInspectionUrl(threadId, "events"), {
1445
+ headers: { ...this.headers },
1446
+ signal
1447
+ });
1448
+ if (res.status === 501) return { status: "not-available" };
1449
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
1450
+ return {
1451
+ status: "available",
1452
+ events: (await res.json()).events
1453
+ };
1454
+ }
1455
+ async fetchRuntimeState(threadId, signal) {
1456
+ const res = await fetch(this.getThreadInspectionUrl(threadId, "state"), {
1457
+ headers: { ...this.headers },
1458
+ signal
1459
+ });
1460
+ if (res.status === 501) return { status: "not-available" };
1461
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
1462
+ return {
1463
+ status: "available",
1464
+ state: (await res.json()).state ?? null
1465
+ };
1466
+ }
1467
+ getThreadInspectionUrl(threadId, resource) {
1468
+ return `${this.runtimeUrl.replace(/\/+$/, "")}/threads/${encodeURIComponent(threadId)}/${resource}`;
1469
+ }
1230
1470
  mapMessages(messages) {
1231
1471
  const items = [];
1232
1472
  const toolCallMap = /* @__PURE__ */ new Map();
@@ -1239,7 +1479,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1239
1479
  else if (msg.role === "assistant") {
1240
1480
  if (msg.toolCalls?.length) for (const tc of msg.toolCalls) {
1241
1481
  let args = {};
1242
- try {
1482
+ if (typeof tc.args === "string") try {
1243
1483
  args = JSON.parse(tc.args);
1244
1484
  } catch (err) {
1245
1485
  console.error("[CopilotKit Inspector] Failed to parse tool-call arguments", {
@@ -1252,6 +1492,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1252
1492
  __raw: tc.args
1253
1493
  };
1254
1494
  }
1495
+ else args = tc.args;
1255
1496
  const item = {
1256
1497
  id: tc.id,
1257
1498
  type: "tool_call",
@@ -1295,15 +1536,232 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1295
1536
  return items;
1296
1537
  }
1297
1538
  mapApiEvents(events) {
1298
- return events.map((event) => {
1299
- const { type, timestamp, ...rest } = event;
1539
+ return events.map((event, index) => {
1540
+ const { type, timestamp, payload, ...rest } = event;
1300
1541
  return {
1301
1542
  type: typeof type === "string" ? type : "UNKNOWN",
1302
1543
  timestamp: typeof timestamp === "string" || typeof timestamp === "number" ? timestamp : Date.now(),
1303
- payload: rest
1544
+ payload: payload ?? rest,
1545
+ sourceIndex: index + 1,
1546
+ rawEvent: event
1304
1547
  };
1305
1548
  });
1306
1549
  }
1550
+ get activeTimelineItems() {
1551
+ return this.timelineItemsForEvents(this.activeEvents);
1552
+ }
1553
+ timelineItemsForEvents(events) {
1554
+ if (this._timelineItemsCache?.events === events) return this._timelineItemsCache.items;
1555
+ const items = this.timelineItemsFromEvents(events);
1556
+ this._timelineItemsCache = {
1557
+ events,
1558
+ items
1559
+ };
1560
+ return items;
1561
+ }
1562
+ timelineItemsFromEvents(events) {
1563
+ if (events.length === 0) return [];
1564
+ const items = [];
1565
+ const messageItems = /* @__PURE__ */ new Map();
1566
+ const toolItems = /* @__PURE__ */ new Map();
1567
+ const readString = (payload, keys) => {
1568
+ for (const key of keys) {
1569
+ const value = payload[key];
1570
+ if (typeof value === "string") return value;
1571
+ }
1572
+ return null;
1573
+ };
1574
+ const sourceIndexFor = (event) => event.sourceIndex ?? 0;
1575
+ const appendWarning = (event, title, body, severity = "warning") => {
1576
+ const sourceIndex = sourceIndexFor(event);
1577
+ items.push({
1578
+ id: `warning-${sourceIndex}-${items.length}`,
1579
+ kind: "warning",
1580
+ title,
1581
+ body,
1582
+ timestamp: event.timestamp,
1583
+ sourceIndex,
1584
+ severity
1585
+ });
1586
+ };
1587
+ const ensureMessage = (event, role) => {
1588
+ const sourceIndex = sourceIndexFor(event);
1589
+ const key = readString(event.payload, [
1590
+ "messageId",
1591
+ "message_id",
1592
+ "id"
1593
+ ]) ?? `message-${sourceIndex}`;
1594
+ let item = messageItems.get(key);
1595
+ if (!item) {
1596
+ item = {
1597
+ id: `message-${key}`,
1598
+ kind: "message",
1599
+ title: `${role || "message"} message`,
1600
+ body: "",
1601
+ timestamp: event.timestamp,
1602
+ sourceIndex
1603
+ };
1604
+ messageItems.set(key, item);
1605
+ items.push(item);
1606
+ }
1607
+ return item;
1608
+ };
1609
+ const ensureTool = (event) => {
1610
+ const sourceIndex = sourceIndexFor(event);
1611
+ const key = readString(event.payload, [
1612
+ "toolCallId",
1613
+ "tool_call_id",
1614
+ "id",
1615
+ "callId"
1616
+ ]) ?? `tool-${sourceIndex}`;
1617
+ let item = toolItems.get(key);
1618
+ if (!item) {
1619
+ item = {
1620
+ id: `tool-${key}`,
1621
+ kind: "tool",
1622
+ title: readString(event.payload, [
1623
+ "toolCallName",
1624
+ "toolName",
1625
+ "name",
1626
+ "functionName"
1627
+ ]) ?? "Tool call",
1628
+ body: "",
1629
+ timestamp: event.timestamp,
1630
+ sourceIndex
1631
+ };
1632
+ toolItems.set(key, item);
1633
+ items.push(item);
1634
+ }
1635
+ return item;
1636
+ };
1637
+ for (const event of events) {
1638
+ const { type, payload } = event;
1639
+ const sourceIndex = sourceIndexFor(event);
1640
+ if (type === "UNKNOWN") {
1641
+ appendWarning(event, "Unknown AG-UI event", "The event is missing a string type and could not be normalized.");
1642
+ continue;
1643
+ }
1644
+ if (type === "RUN_STARTED" || type === "STEP_STARTED") {
1645
+ items.push({
1646
+ id: `${type}-${sourceIndex}`,
1647
+ kind: "run",
1648
+ title: type === "RUN_STARTED" ? "Run started" : "Step started",
1649
+ timestamp: event.timestamp,
1650
+ sourceIndex,
1651
+ details: payload
1652
+ });
1653
+ continue;
1654
+ }
1655
+ if (type === "RUN_FINISHED" || type === "STEP_FINISHED") {
1656
+ items.push({
1657
+ id: `${type}-${sourceIndex}`,
1658
+ kind: "run",
1659
+ title: type === "RUN_FINISHED" ? "Run finished" : "Step finished",
1660
+ timestamp: event.timestamp,
1661
+ sourceIndex,
1662
+ details: payload
1663
+ });
1664
+ continue;
1665
+ }
1666
+ if (type === "RUN_ERROR" || type === "ERROR") {
1667
+ items.push({
1668
+ id: `${type}-${sourceIndex}`,
1669
+ kind: "warning",
1670
+ title: "Run error",
1671
+ body: readString(payload, [
1672
+ "message",
1673
+ "error",
1674
+ "description"
1675
+ ]) ?? "",
1676
+ timestamp: event.timestamp,
1677
+ sourceIndex,
1678
+ severity: "error",
1679
+ details: payload
1680
+ });
1681
+ continue;
1682
+ }
1683
+ if (type === "TEXT_MESSAGE_START") {
1684
+ ensureMessage(event, readString(payload, ["role"]) ?? "assistant");
1685
+ continue;
1686
+ }
1687
+ if (type === "TEXT_MESSAGE_CONTENT") {
1688
+ const item = ensureMessage(event, readString(payload, ["role"]) ?? "assistant");
1689
+ item.body = `${item.body ?? ""}${readString(payload, [
1690
+ "delta",
1691
+ "content",
1692
+ "text"
1693
+ ]) ?? ""}`;
1694
+ continue;
1695
+ }
1696
+ if (type === "TEXT_MESSAGE_END") {
1697
+ ensureMessage(event, readString(payload, ["role"]) ?? "assistant");
1698
+ continue;
1699
+ }
1700
+ if (type === "TOOL_CALL_START") {
1701
+ ensureTool(event);
1702
+ continue;
1703
+ }
1704
+ if (type === "TOOL_CALL_ARGS") {
1705
+ const item = ensureTool(event);
1706
+ const chunk = readString(payload, [
1707
+ "args",
1708
+ "arguments",
1709
+ "delta"
1710
+ ]) ?? (typeof payload.args === "object" ? JSON.stringify(payload.args) : null);
1711
+ if (chunk) {
1712
+ item.rawArgs = `${item.rawArgs ?? ""}${chunk}`;
1713
+ item.body = item.rawArgs;
1714
+ }
1715
+ continue;
1716
+ }
1717
+ if (type === "TOOL_CALL_END") {
1718
+ const item = ensureTool(event);
1719
+ if (item.rawArgs) try {
1720
+ JSON.parse(item.rawArgs);
1721
+ } catch {
1722
+ appendWarning(event, "Could not decode tool call arguments", item.rawArgs);
1723
+ }
1724
+ continue;
1725
+ }
1726
+ if (type === "TOOL_CALL_RESULT") {
1727
+ const item = ensureTool(event);
1728
+ const result = readString(payload, [
1729
+ "result",
1730
+ "content",
1731
+ "delta"
1732
+ ]);
1733
+ if (result) {
1734
+ item.body = item.body ? `${item.body}\nResult: ${result}` : `Result: ${result}`;
1735
+ try {
1736
+ JSON.parse(result);
1737
+ } catch {
1738
+ appendWarning(event, "Could not decode tool result", result);
1739
+ }
1740
+ }
1741
+ continue;
1742
+ }
1743
+ if (type.startsWith("STATE_")) {
1744
+ items.push({
1745
+ id: `${type}-${sourceIndex}`,
1746
+ kind: "state",
1747
+ title: type === "STATE_SNAPSHOT" ? "State snapshot captured" : "State delta captured",
1748
+ timestamp: event.timestamp,
1749
+ sourceIndex,
1750
+ details: payload
1751
+ });
1752
+ continue;
1753
+ }
1754
+ items.push({
1755
+ id: `event-${sourceIndex}`,
1756
+ kind: "event",
1757
+ title: type,
1758
+ timestamp: event.timestamp,
1759
+ sourceIndex,
1760
+ details: payload
1761
+ });
1762
+ }
1763
+ return items;
1764
+ }
1307
1765
  get renderItems() {
1308
1766
  const items = this._conversation;
1309
1767
  const result = [];
@@ -1341,7 +1799,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1341
1799
  };
1342
1800
  }
1343
1801
  get duration() {
1344
- const t = this.thread;
1802
+ const t = this.metadata;
1345
1803
  if (!t?.createdAt || !t?.updatedAt) return "—";
1346
1804
  const ms = new Date(t.updatedAt).getTime() - new Date(t.createdAt).getTime();
1347
1805
  if (ms < 0) return "—";
@@ -1364,7 +1822,18 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1364
1822
  }
1365
1823
  get activeEvents() {
1366
1824
  if (this._eventsNotAvailable) return [];
1367
- return this._fetchedEvents ?? this.agentEventsInput ?? [];
1825
+ const events = this._fetchedEvents ?? this.agentEventsInput ?? [];
1826
+ if (events.every((event) => event.sourceIndex != null)) return events;
1827
+ if (this._liveEventsWithSourceIndexCache?.events === events) return this._liveEventsWithSourceIndexCache.indexedEvents;
1828
+ const indexedEvents = events.map((event, index) => event.sourceIndex == null ? {
1829
+ ...event,
1830
+ sourceIndex: index + 1
1831
+ } : event);
1832
+ this._liveEventsWithSourceIndexCache = {
1833
+ events,
1834
+ indexedEvents
1835
+ };
1836
+ return indexedEvents;
1368
1837
  }
1369
1838
  get activeState() {
1370
1839
  if (this._stateNotAvailable) return null;
@@ -1378,6 +1847,9 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1378
1847
  if (!id) return "—";
1379
1848
  return id.length > 20 ? id.slice(0, 8) + "…" : id;
1380
1849
  }
1850
+ get metadata() {
1851
+ return this._fetchedMetadata ?? this.thread ?? null;
1852
+ }
1381
1853
  fmtTime(dateStr) {
1382
1854
  if (!dateStr) return "—";
1383
1855
  const d = new Date(dateStr);
@@ -1397,7 +1869,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1397
1869
  <!-- Tab bar -->
1398
1870
  <div class="cpk-td__tabs-header">
1399
1871
  <div class="cpk-td__tab-group" role="tablist">
1400
- ${ɵCpkThreadDetails.TAB_LIST.map((tab) => html`
1872
+ ${CpkThreadInspector.TAB_LIST.map((tab) => html`
1401
1873
  <button
1402
1874
  role="tab"
1403
1875
  class="cpk-td__tab ${this._tab === tab.id ? "cpk-td__tab--active" : ""}"
@@ -1409,18 +1881,19 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1409
1881
  </div>
1410
1882
  ${this.renderPanelToggle()}
1411
1883
  </div>
1884
+ ${this.renderMetadataStrip()}
1412
1885
 
1413
1886
  <!-- Scrollable content -->
1414
1887
  <div class="cpk-td__content">
1415
1888
  ${this._panelInitializing ? html`
1416
1889
  <div class="cpk-td__status">Loading…</div>
1417
1890
  ` : nothing}
1418
- ${ɵCpkThreadDetails.TAB_LIST.map((tab) => this._activatedTabs.has(tab.id) ? html`<div
1419
- class="cpk-td__panel"
1420
- style=${this._tab === tab.id && !this._panelInitializing ? "" : "display:none"}
1421
- >
1422
- ${this.renderTabContent(tab.id)}
1423
- </div>` : nothing)}
1891
+ ${CpkThreadInspector.TAB_LIST.map((tab) => this._activatedTabs.has(tab.id) ? html`<div
1892
+ class="cpk-td__panel"
1893
+ style=${this._tab === tab.id && !this._panelInitializing ? "" : "display:none"}
1894
+ >
1895
+ ${this.renderTabContent(tab.id)}
1896
+ </div>` : nothing)}
1424
1897
  </div>
1425
1898
  </div>
1426
1899
 
@@ -1448,6 +1921,116 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1448
1921
  ${this.renderDetailPanel()}
1449
1922
  </div>
1450
1923
  </div>
1924
+ `;
1925
+ }
1926
+ renderMetadataStrip() {
1927
+ const metadata = this.metadata;
1928
+ const pills = [
1929
+ {
1930
+ label: "Thread",
1931
+ value: metadata?.id ?? this.threadId
1932
+ },
1933
+ {
1934
+ label: "Agent",
1935
+ value: metadata?.agentId
1936
+ },
1937
+ {
1938
+ label: "End user",
1939
+ value: metadata?.endUserId ?? metadata?.createdById
1940
+ },
1941
+ {
1942
+ label: "Status",
1943
+ value: metadata?.status
1944
+ }
1945
+ ].filter((pill) => pill.value != null && pill.value !== "");
1946
+ if (pills.length === 0) return nothing;
1947
+ return html`
1948
+ <div class="cpk-td__metadata-strip" aria-label="Thread metadata">
1949
+ ${pills.map((pill) => html`
1950
+ <span class="cpk-td__metadata-pill" title=${pill.value ?? ""}>
1951
+ <span class="cpk-td__metadata-label">${pill.label}</span>
1952
+ <span class="cpk-td__metadata-value"
1953
+ >${this.shortId(pill.value)}</span
1954
+ >
1955
+ </span>
1956
+ `)}
1957
+ </div>
1958
+ `;
1959
+ }
1960
+ revealSourceEvent(sourceIndex) {
1961
+ this._activatedTabs = new Set([...this._activatedTabs, "raw-events"]);
1962
+ this._tab = "raw-events";
1963
+ this.requestUpdate();
1964
+ requestAnimationFrame(() => {
1965
+ (this.shadowRoot?.querySelector(`[data-source-index="${sourceIndex}"]`))?.scrollIntoView?.({ block: "center" });
1966
+ });
1967
+ }
1968
+ renderTimeline() {
1969
+ if (this._loadingEvents) return html`
1970
+ <div class="cpk-td__status">Loading timeline…</div>
1971
+ `;
1972
+ if (this._eventsError) return html`<div class="cpk-td__status cpk-td__status--error">
1973
+ ${this._eventsError}
1974
+ </div>`;
1975
+ if (this._eventsNotAvailable) {
1976
+ if (this._conversation.length > 0) return this.renderConversation();
1977
+ if (this._loadingMessages) return this.renderConversation();
1978
+ return html`
1979
+ <div class="cpk-td__empty-state">
1980
+ <span>Timeline event history not available</span>
1981
+ <span class="cpk-td__empty-hint"
1982
+ >This runtime doesn't yet expose per-thread AG-UI events. Check State for
1983
+ the latest snapshot when available.</span
1984
+ >
1985
+ </div>
1986
+ `;
1987
+ }
1988
+ const events = this.activeEvents;
1989
+ const cachedTimeline = this.getCachedPanelTpl("timeline", [events]);
1990
+ if (cachedTimeline) return cachedTimeline;
1991
+ const timelineItems = this.timelineItemsForEvents(events);
1992
+ if (timelineItems.length === 0) {
1993
+ if (this._conversation.length > 0) return this.renderConversation();
1994
+ if (this._loadingMessages) return this.renderConversation();
1995
+ return html`
1996
+ <div class="cpk-td__empty-state">
1997
+ <span>No timeline events captured</span>
1998
+ <span class="cpk-td__empty-hint"
1999
+ >Timeline rows are normalized from AG-UI events. Open Raw AG-UI Events or
2000
+ State to inspect the available thread data.</span
2001
+ >
2002
+ </div>
2003
+ `;
2004
+ }
2005
+ return this.cachedPanelTpl("timeline", [events], () => {
2006
+ return html`${timelineItems.map((item) => this.renderTimelineItem(item))}`;
2007
+ });
2008
+ }
2009
+ renderTimelineItem(item) {
2010
+ return html`
2011
+ <div
2012
+ class="cpk-td__timeline-item ${item.kind === "warning" ? "cpk-td__timeline-item--warning" : ""}"
2013
+ >
2014
+ <div class="cpk-td__timeline-header">
2015
+ <span class="cpk-td__timeline-kind"
2016
+ >${item.severity === "error" ? "error" : item.kind}</span
2017
+ >
2018
+ <span class="cpk-td__timeline-title">${item.title}</span>
2019
+ <button
2020
+ type="button"
2021
+ class="cpk-td__source-link"
2022
+ @click=${() => this.revealSourceEvent(item.sourceIndex)}
2023
+ >
2024
+ Source event #${item.sourceIndex}
2025
+ </button>
2026
+ <span class="cpk-td__timeline-time"
2027
+ >${formatTimestamp(item.timestamp)}</span
2028
+ >
2029
+ </div>
2030
+ ${item.body ? html`<div class="cpk-td__timeline-body">${item.body}</div>` : item.details ? html`<pre class="cpk-td__timeline-body">
2031
+ ${unsafeHTML(highlightedJson(item.details))}</pre
2032
+ >` : nothing}
2033
+ </div>
1451
2034
  `;
1452
2035
  }
1453
2036
  renderConversation() {
@@ -1474,7 +2057,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1474
2057
  <span>No messages yet</span>
1475
2058
  </div>
1476
2059
  `;
1477
- return this.cachedPanelTpl("conversation", [
2060
+ return this.cachedPanelTpl("timeline-fallback", [
1478
2061
  this._conversation,
1479
2062
  this._expandedTools,
1480
2063
  this._expandedMessages
@@ -1492,8 +2075,8 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1492
2075
  * change without the listed key flipping.
1493
2076
  */
1494
2077
  cachedPanelTpl(slot, key, build) {
1495
- const cached = this._panelTplCache.get(slot);
1496
- if (cached && cached.key.length === key.length && cached.key.every((v, i) => v === key[i])) return cached.tpl;
2078
+ const cached = this.getCachedPanelTpl(slot, key);
2079
+ if (cached) return cached;
1497
2080
  const tpl = build();
1498
2081
  this._panelTplCache.set(slot, {
1499
2082
  key,
@@ -1501,6 +2084,11 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1501
2084
  });
1502
2085
  return tpl;
1503
2086
  }
2087
+ getCachedPanelTpl(slot, key) {
2088
+ const cached = this._panelTplCache.get(slot);
2089
+ if (cached && cached.key.length === key.length && cached.key.every((v, i) => v === key[i])) return cached.tpl;
2090
+ return null;
2091
+ }
1504
2092
  renderRenderItem(item) {
1505
2093
  switch (item.type) {
1506
2094
  case "user":
@@ -1521,7 +2109,7 @@ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends LitElement {
1521
2109
  }
1522
2110
  renderBubble(item) {
1523
2111
  const isUser = item.type === "user";
1524
- const threshold = ɵCpkThreadDetails.COLLAPSE_THRESHOLD;
2112
+ const threshold = CpkThreadInspector.COLLAPSE_THRESHOLD;
1525
2113
  const expanded = this._expandedMessages.has(item.id);
1526
2114
  const tooLong = item.content.length > threshold;
1527
2115
  const shown = tooLong && !expanded ? item.content.slice(0, threshold) + "…" : item.content;
@@ -1667,7 +2255,7 @@ ${unsafeHTML(highlightedJson(item.result))}</pre
1667
2255
  </div>
1668
2256
  `;
1669
2257
  const stateValue = this.activeState;
1670
- return this.cachedPanelTpl("agent-state", [stateValue], () => {
2258
+ return this.cachedPanelTpl("state", [stateValue], () => {
1671
2259
  return html`<pre class="cpk-td__json-block">
1672
2260
  ${unsafeHTML(highlightedJson(stateValue))}</pre
1673
2261
  >`;
@@ -1698,11 +2286,11 @@ ${unsafeHTML(highlightedJson(stateValue))}</pre
1698
2286
  >
1699
2287
  </div>
1700
2288
  `;
1701
- return this.cachedPanelTpl("ag-ui-events", [events], () => {
2289
+ return this.cachedPanelTpl("raw-events", [events], () => {
1702
2290
  return html`${events.map((event) => {
1703
2291
  const { bg, fg } = eventColors(event.type);
1704
2292
  return html`
1705
- <div class="cpk-td__event">
2293
+ <div class="cpk-td__event" data-source-index=${event.sourceIndex}>
1706
2294
  <div class="cpk-td__event-header" style="background:${bg}">
1707
2295
  <span class="cpk-td__event-type" style="color:${fg}"
1708
2296
  >${event.type}</span
@@ -1712,7 +2300,7 @@ ${unsafeHTML(highlightedJson(stateValue))}</pre
1712
2300
  >
1713
2301
  </div>
1714
2302
  <pre class="cpk-td__event-payload">
1715
- ${unsafeHTML(highlightedJson(event.payload))}</pre
2303
+ ${unsafeHTML(highlightedJson(event.rawEvent ?? event))}</pre
1716
2304
  >
1717
2305
  </div>
1718
2306
  `;
@@ -1747,29 +2335,42 @@ ${unsafeHTML(highlightedJson(event.payload))}</pre
1747
2335
  }
1748
2336
  renderDetailPanel() {
1749
2337
  const counts = this.activityCounts;
2338
+ const metadata = this.metadata;
1750
2339
  return html`
1751
2340
  <!-- Thread -->
1752
2341
  <div class="cpk-tdp__section-title">Thread</div>
1753
2342
  <div class="cpk-tdp__row">
1754
2343
  <span class="cpk-tdp__label">ID</span>
1755
2344
  <span class="cpk-tdp__value cpk-tdp__value--wrap"
1756
- >${this.shortId(this.thread?.id)}</span
2345
+ >${this.shortId(metadata?.id)}</span
1757
2346
  >
1758
2347
  </div>
1759
2348
  <div class="cpk-tdp__row">
1760
2349
  <span class="cpk-tdp__label">Name</span>
1761
- <span class="cpk-tdp__value">${this.thread?.name ?? "—"}</span>
2350
+ <span class="cpk-tdp__value">${metadata?.name ?? "—"}</span>
1762
2351
  </div>
1763
2352
  <div class="cpk-tdp__row">
1764
2353
  <span class="cpk-tdp__label">Agent</span>
1765
2354
  <span class="cpk-tdp__value cpk-tdp__value--truncate"
1766
- >${this.thread?.agentId ?? "—"}</span
2355
+ >${metadata?.agentId ?? "—"}</span
2356
+ >
2357
+ </div>
2358
+ <div class="cpk-tdp__row">
2359
+ <span class="cpk-tdp__label">End user</span>
2360
+ <span class="cpk-tdp__value cpk-tdp__value--truncate"
2361
+ >${metadata?.endUserId ?? "—"}</span
1767
2362
  >
1768
2363
  </div>
1769
2364
  <div class="cpk-tdp__row">
1770
2365
  <span class="cpk-tdp__label">Created by</span>
1771
2366
  <span class="cpk-tdp__value cpk-tdp__value--truncate"
1772
- >${this.thread?.createdById ?? "—"}</span
2367
+ >${metadata?.createdById ?? "—"}</span
2368
+ >
2369
+ </div>
2370
+ <div class="cpk-tdp__row">
2371
+ <span class="cpk-tdp__label">Status</span>
2372
+ <span class="cpk-tdp__value cpk-tdp__value--truncate"
2373
+ >${metadata?.status ?? "—"}</span
1773
2374
  >
1774
2375
  </div>
1775
2376
 
@@ -1779,11 +2380,11 @@ ${unsafeHTML(highlightedJson(event.payload))}</pre
1779
2380
  <div class="cpk-tdp__section-title">Timestamps</div>
1780
2381
  <div class="cpk-tdp__row">
1781
2382
  <span class="cpk-tdp__label">Created</span>
1782
- <span class="cpk-tdp__value">${this.fmtTime(this.thread?.createdAt)}</span>
2383
+ <span class="cpk-tdp__value">${this.fmtTime(metadata?.createdAt)}</span>
1783
2384
  </div>
1784
2385
  <div class="cpk-tdp__row">
1785
2386
  <span class="cpk-tdp__label">Updated</span>
1786
- <span class="cpk-tdp__value">${this.fmtTime(this.thread?.updatedAt)}</span>
2387
+ <span class="cpk-tdp__value">${this.fmtTime(metadata?.updatedAt)}</span>
1787
2388
  </div>
1788
2389
  <div class="cpk-tdp__row">
1789
2390
  <span class="cpk-tdp__label">Duration</span>
@@ -1809,7 +2410,9 @@ ${unsafeHTML(highlightedJson(event.payload))}</pre
1809
2410
  `;
1810
2411
  }
1811
2412
  };
2413
+ var ɵCpkThreadDetails = class extends CpkThreadInspector {};
1812
2414
  if (!customElements.get("cpk-thread-list")) customElements.define("cpk-thread-list", CpkThreadList);
2415
+ if (!customElements.get(THREAD_INSPECTOR_TAG)) customElements.define(THREAD_INSPECTOR_TAG, CpkThreadInspector);
1813
2416
  if (!customElements.get("cpk-thread-details")) customElements.define("cpk-thread-details", ɵCpkThreadDetails);
1814
2417
  var WebInspectorElement = class extends LitElement {
1815
2418
  constructor(..._args3) {
@@ -1881,6 +2484,7 @@ var WebInspectorElement = class extends LitElement {
1881
2484
  this.viewedBannerTimestamps = /* @__PURE__ */ new Set();
1882
2485
  this.pendingBannerViewed = null;
1883
2486
  this.clickedBannerIds = /* @__PURE__ */ new Set();
2487
+ this.viewedThreadsTelemetryStates = /* @__PURE__ */ new Set();
1884
2488
  this.contextState = {
1885
2489
  button: {
1886
2490
  position: {
@@ -2086,6 +2690,27 @@ var WebInspectorElement = class extends LitElement {
2086
2690
  this.expandedTools = /* @__PURE__ */ new Set();
2087
2691
  this.expandedContextItems = /* @__PURE__ */ new Set();
2088
2692
  this.copiedContextItems = /* @__PURE__ */ new Set();
2693
+ this.handleTalkToEngineerClick = () => {
2694
+ if (this.core?.telemetryDisabled) return;
2695
+ trackTalkToEngineerClicked(this.getThreadsTelemetryProps({
2696
+ cta: "talk_to_engineer",
2697
+ cta_surface: "threads_header"
2698
+ }, { includeUrlAttribution: true }));
2699
+ };
2700
+ this.handleThreadsIntelligenceSignupClick = () => {
2701
+ if (this.core?.telemetryDisabled) return;
2702
+ trackThreadsIntelligenceSignupClicked(this.getThreadsTelemetryProps({
2703
+ cta: "signup",
2704
+ cta_surface: "threads_locked"
2705
+ }, { includeUrlAttribution: true }));
2706
+ };
2707
+ this.handleThreadsTalkToEngineerClick = () => {
2708
+ if (this.core?.telemetryDisabled) return;
2709
+ trackThreadsTalkToEngineerClicked(this.getThreadsTelemetryProps({
2710
+ cta: "talk_to_engineer",
2711
+ cta_surface: "threads_locked"
2712
+ }, { includeUrlAttribution: true }));
2713
+ };
2089
2714
  this.handleThreadDividerPointerDown = (event) => {
2090
2715
  this.threadDividerResizing = true;
2091
2716
  this.threadDividerPointerId = event.pointerId;
@@ -2213,7 +2838,36 @@ var WebInspectorElement = class extends LitElement {
2213
2838
  }
2214
2839
  ];
2215
2840
  }
2841
+ getThreadServiceStatus() {
2842
+ if (!this._core) return "unknown";
2843
+ if (!this._core.threadEndpoints) return "unknown";
2844
+ return this._core.threadEndpoints?.list === false ? "unavailable" : "available";
2845
+ }
2846
+ areThreadEndpointsAvailable() {
2847
+ return this.getThreadServiceStatus() !== "unavailable";
2848
+ }
2849
+ getThreadsTelemetryProps(extra = {}, options = {}) {
2850
+ const distinctId = options.includeUrlAttribution && !this.core?.telemetryDisabled ? getTelemetryDistinctIdForUrl() : null;
2851
+ const threadServiceStatus = this.getThreadServiceStatus();
2852
+ return {
2853
+ posthog_distinct_id: distinctId ?? void 0,
2854
+ intelligence_status: threadServiceStatus === "available" ? "intelligence_enabled" : threadServiceStatus === "unavailable" ? "intelligence_not_enabled" : "unknown",
2855
+ thread_service_status: threadServiceStatus,
2856
+ license_status: this.core?.licenseStatus ?? void 0,
2857
+ runtime_mode: this.core?.runtimeMode ?? void 0,
2858
+ runtime_url_type: getRuntimeUrlType(this.core?.runtimeUrl),
2859
+ telemetry_disabled: this.core?.telemetryDisabled ?? false,
2860
+ ...extra
2861
+ };
2862
+ }
2863
+ getIntelligenceSignupUrl() {
2864
+ return this.appendRefParam(INTELLIGENCE_SIGNUP_URL, "cpk-inspector");
2865
+ }
2866
+ getTalkToEngineerUrl() {
2867
+ return this.appendRefParam(TALK_TO_ENGINEER_URL, "cpk-inspector-threads");
2868
+ }
2216
2869
  subscribeToThreadStore(agentId, store) {
2870
+ if (!this.areThreadEndpointsAvailable()) return;
2217
2871
  if (this._threadStoreSubscriptions.has(agentId)) return;
2218
2872
  const threadsSub = store.select(ɵselectThreads).subscribe((threads) => {
2219
2873
  this._threadsByAgent.set(agentId, threads);
@@ -2254,7 +2908,7 @@ var WebInspectorElement = class extends LitElement {
2254
2908
  if (this.core?.getThreadStore(agentId)) return;
2255
2909
  const core = this.core;
2256
2910
  if (!core?.runtimeUrl) return;
2257
- if (core.threadEndpoints?.list === false) return;
2911
+ if (!this.areThreadEndpointsAvailable()) return;
2258
2912
  const store = ɵcreateThreadStore({ fetch: globalThis.fetch });
2259
2913
  store.start();
2260
2914
  store.setContext({
@@ -2278,6 +2932,7 @@ var WebInspectorElement = class extends LitElement {
2278
2932
  for (const [agentId, store] of this._ownedThreadStores) store.setContext({
2279
2933
  runtimeUrl: core.runtimeUrl,
2280
2934
  headers: { ...headers },
2935
+ wsUrl: core.intelligence?.wsUrl,
2281
2936
  agentId
2282
2937
  });
2283
2938
  }
@@ -2308,7 +2963,7 @@ var WebInspectorElement = class extends LitElement {
2308
2963
  maybeShowDisclosure();
2309
2964
  }
2310
2965
  this.flushPendingBannerViewed();
2311
- if (core.threadEndpoints?.list !== false) for (const agentId of this._ownedThreadStores.keys()) this.refreshOwnedThreadStore(agentId);
2966
+ if (this.areThreadEndpointsAvailable()) for (const agentId of this._ownedThreadStores.keys()) this.refreshOwnedThreadStore(agentId);
2312
2967
  else this.teardownOwnedThreadStores();
2313
2968
  } else {
2314
2969
  this._threadsByAgent.clear();
@@ -2322,6 +2977,7 @@ var WebInspectorElement = class extends LitElement {
2322
2977
  },
2323
2978
  onHeadersChanged: ({ headers }) => {
2324
2979
  this.updateOwnedThreadStoreHeaders(headers);
2980
+ this.requestUpdate();
2325
2981
  },
2326
2982
  onError: ({ code, error }) => {
2327
2983
  this.lastCoreError = {
@@ -2333,6 +2989,9 @@ var WebInspectorElement = class extends LitElement {
2333
2989
  onAgentsChanged: ({ agents }) => {
2334
2990
  this.processAgentsChanged(agents);
2335
2991
  },
2992
+ onAgentRunStarted: ({ agent }) => {
2993
+ if (agent?.agentId) this.subscribeToAgent(agent);
2994
+ },
2336
2995
  onContextChanged: ({ context }) => {
2337
2996
  this.contextStore = this.normalizeContextStore(context);
2338
2997
  this.requestUpdate();
@@ -2355,6 +3014,13 @@ var WebInspectorElement = class extends LitElement {
2355
3014
  };
2356
3015
  this.coreUnsubscribe = core.subscribe(this.coreSubscriber).unsubscribe;
2357
3016
  this.processAgentsChanged(core.agents);
3017
+ if (core.runtimeConnectionStatus === "connected") {
3018
+ if (!core.telemetryDisabled) {
3019
+ ensureTelemetryDistinctId();
3020
+ maybeShowDisclosure();
3021
+ }
3022
+ this.flushPendingBannerViewed();
3023
+ }
2358
3024
  const threadStores = typeof core.getThreadStores === "function" ? core.getThreadStores() : {};
2359
3025
  for (const [agentId, store] of Object.entries(threadStores)) this.subscribeToThreadStore(agentId, store);
2360
3026
  if (core.context) this.contextStore = this.normalizeContextStore(core.context);
@@ -2443,10 +3109,10 @@ var WebInspectorElement = class extends LitElement {
2443
3109
  onRunStartedEvent: ({ event }) => {
2444
3110
  this.recordAgentEvent(agentId, "RUN_STARTED", event);
2445
3111
  },
2446
- onRunFinishedEvent: ({ event, result }) => {
3112
+ onRunFinishedEvent: (params) => {
2447
3113
  this.recordAgentEvent(agentId, "RUN_FINISHED", {
2448
- event,
2449
- result
3114
+ event: params.event,
3115
+ result: "result" in params ? params.result : void 0
2450
3116
  });
2451
3117
  this.refreshOwnedThreadStore(agentId);
2452
3118
  },
@@ -2764,13 +3430,13 @@ ${argsString}</pre
2764
3430
  }
2765
3431
  getEventBadgeClasses(type) {
2766
3432
  const base = "font-mono text-[10px] font-medium inline-flex items-center rounded-sm px-1.5 py-0.5 border";
3433
+ if (type === "RUN_ERROR") return `${base} bg-rose-50 text-rose-700 border-rose-200`;
2767
3434
  if (type.startsWith("RUN_")) return `${base} bg-blue-50 text-blue-700 border-blue-200`;
2768
3435
  if (type.startsWith("TEXT_MESSAGE")) return `${base} bg-emerald-50 text-emerald-700 border-emerald-200`;
2769
3436
  if (type.startsWith("TOOL_CALL")) return `${base} bg-amber-50 text-amber-700 border-amber-200`;
2770
3437
  if (type.startsWith("REASONING")) return `${base} bg-fuchsia-50 text-fuchsia-700 border-fuchsia-200`;
2771
3438
  if (type.startsWith("STATE")) return `${base} bg-violet-50 text-violet-700 border-violet-200`;
2772
3439
  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
3440
  return `${base} bg-gray-100 text-gray-600 border-gray-200`;
2775
3441
  }
2776
3442
  stringifyPayload(payload, pretty) {
@@ -4294,7 +4960,9 @@ ${argsString}</pre
4294
4960
 
4295
4961
  <div class="space-y-2">
4296
4962
  <h3 class="text-sm text-slate-500">Privacy</h3>
4297
- <div class="rounded-lg border border-slate-200 bg-white p-4 space-y-3">
4963
+ <div
4964
+ class="rounded-lg border border-slate-200 bg-white p-4 space-y-3"
4965
+ >
4298
4966
  <p class="text-sm text-gray-600 flex items-start gap-2">
4299
4967
  <span>${optedOut ? "❌" : "✅"}</span>
4300
4968
  <span>
@@ -4306,7 +4974,8 @@ ${argsString}</pre
4306
4974
  href=${TELEMETRY_DOCS_URL}
4307
4975
  target="_blank"
4308
4976
  rel="noopener"
4309
- >Learn more →</a>
4977
+ >Learn more →</a
4978
+ >
4310
4979
  </div>
4311
4980
  </div>
4312
4981
  </div>
@@ -4327,78 +4996,399 @@ ${argsString}</pre
4327
4996
  cta_label: this.announcementCtaLabel ?? void 0
4328
4997
  });
4329
4998
  }
4999
+ trackThreadsViewStateOnce(state, threadCount) {
5000
+ if (this.core?.telemetryDisabled) return;
5001
+ const key = `${state}:${this.getThreadServiceStatus()}`;
5002
+ if (this.viewedThreadsTelemetryStates.has(key)) return;
5003
+ this.viewedThreadsTelemetryStates.add(key);
5004
+ const props = this.getThreadsTelemetryProps({ thread_count: threadCount });
5005
+ if (state === "locked") trackThreadsLockedViewed(props);
5006
+ else if (state === "empty_enabled") trackThreadsEmptyEnabledViewed(props);
5007
+ else trackThreadsEnabledViewed(props);
5008
+ }
5009
+ renderThreadsLockedBackgroundMockup() {
5010
+ return html`
5011
+ <div
5012
+ aria-hidden="true"
5013
+ style="
5014
+ position: absolute;
5015
+ inset: 0;
5016
+ display: grid;
5017
+ grid-template-columns: minmax(180px, 28%) 1fr;
5018
+ overflow: hidden;
5019
+ opacity: 0.58;
5020
+ pointer-events: none;
5021
+ "
5022
+ >
5023
+ <div
5024
+ style="
5025
+ display: flex;
5026
+ flex-direction: column;
5027
+ gap: 12px;
5028
+ padding: 28px 24px;
5029
+ border-right: 1px solid #dbdbe5;
5030
+ background: #fafafa;
5031
+ "
5032
+ >
5033
+ ${[
5034
+ {
5035
+ width: 74,
5036
+ accent: true
5037
+ },
5038
+ { width: 92 },
5039
+ { width: 68 },
5040
+ { width: 84 },
5041
+ { width: 58 },
5042
+ { width: 76 }
5043
+ ].map((row) => html`
5044
+ <div
5045
+ style="
5046
+ padding: 12px;
5047
+ border-radius: 8px;
5048
+ background: ${row.accent ? "#eee6fe" : "#ffffff"};
5049
+ box-shadow: inset 0 0 0 1px #eeeef4;
5050
+ "
5051
+ >
5052
+ <div
5053
+ style="
5054
+ height: 8px;
5055
+ width: ${row.width}%;
5056
+ border-radius: 99px;
5057
+ background: ${row.accent ? "#a984f5" : "#d7d7df"};
5058
+ "
5059
+ ></div>
5060
+ <div
5061
+ style="
5062
+ height: 6px;
5063
+ width: 88%;
5064
+ margin-top: 10px;
5065
+ border-radius: 99px;
5066
+ background: #e3e3eb;
5067
+ "
5068
+ ></div>
5069
+ <div
5070
+ style="
5071
+ height: 6px;
5072
+ width: 62%;
5073
+ margin-top: 7px;
5074
+ border-radius: 99px;
5075
+ background: #e8e8ef;
5076
+ "
5077
+ ></div>
5078
+ </div>
5079
+ `)}
5080
+ </div>
5081
+ <div
5082
+ style="
5083
+ min-width: 0;
5084
+ padding: 42px 48px;
5085
+ background: #ffffff;
5086
+ "
5087
+ >
5088
+ <div
5089
+ style="
5090
+ height: 10px;
5091
+ width: 180px;
5092
+ border-radius: 99px;
5093
+ background: #d7d7df;
5094
+ "
5095
+ ></div>
5096
+ <div
5097
+ style="
5098
+ height: 8px;
5099
+ width: min(520px, 58%);
5100
+ margin-top: 28px;
5101
+ border-radius: 99px;
5102
+ background: #e3e3eb;
5103
+ "
5104
+ ></div>
5105
+ <div
5106
+ style="
5107
+ height: 8px;
5108
+ width: min(430px, 48%);
5109
+ margin-top: 12px;
5110
+ border-radius: 99px;
5111
+ background: #e8e8ef;
5112
+ "
5113
+ ></div>
5114
+ <div
5115
+ style="
5116
+ display: grid;
5117
+ grid-template-columns: repeat(2, minmax(0, 1fr));
5118
+ gap: 16px;
5119
+ max-width: 620px;
5120
+ margin-top: 30px;
5121
+ "
5122
+ >
5123
+ <div
5124
+ style="
5125
+ height: 116px;
5126
+ border-radius: 8px;
5127
+ background: #f5f5f8;
5128
+ box-shadow: inset 0 0 0 1px #eeeef4;
5129
+ "
5130
+ ></div>
5131
+ <div
5132
+ style="
5133
+ height: 116px;
5134
+ border-radius: 8px;
5135
+ background: #f5f5f8;
5136
+ box-shadow: inset 0 0 0 1px #eeeef4;
5137
+ "
5138
+ ></div>
5139
+ </div>
5140
+ <div
5141
+ style="
5142
+ height: 10px;
5143
+ width: min(680px, 74%);
5144
+ margin-top: 34px;
5145
+ border-radius: 99px;
5146
+ background: #e3e3eb;
5147
+ "
5148
+ ></div>
5149
+ <div
5150
+ style="
5151
+ height: 10px;
5152
+ width: min(560px, 60%);
5153
+ margin-top: 14px;
5154
+ border-radius: 99px;
5155
+ background: #e8e8ef;
5156
+ "
5157
+ ></div>
5158
+ </div>
5159
+ </div>
5160
+ `;
5161
+ }
5162
+ renderThreadsLockedView() {
5163
+ this.trackThreadsViewStateOnce("locked", 0);
5164
+ return html`
5165
+ <div
5166
+ style="
5167
+ position: relative;
5168
+ height: 100%;
5169
+ display: flex;
5170
+ align-items: center;
5171
+ justify-content: center;
5172
+ padding: 32px;
5173
+ overflow: hidden;
5174
+ background: #ffffff;
5175
+ "
5176
+ >
5177
+ ${this.renderThreadsLockedBackgroundMockup()}
5178
+ <div
5179
+ aria-hidden="true"
5180
+ style="
5181
+ position: absolute;
5182
+ inset: 0;
5183
+ pointer-events: none;
5184
+ background:
5185
+ 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%);
5186
+ "
5187
+ ></div>
5188
+ <div
5189
+ style="
5190
+ position: relative;
5191
+ z-index: 1;
5192
+ max-width: 440px;
5193
+ text-align: center;
5194
+ color: #57575b;
5195
+ "
5196
+ >
5197
+ <div
5198
+ aria-hidden="true"
5199
+ style="
5200
+ margin: 0 auto 18px;
5201
+ display: flex;
5202
+ justify-content: center;
5203
+ "
5204
+ >
5205
+ <div
5206
+ style="
5207
+ display: flex;
5208
+ height: 44px;
5209
+ width: 44px;
5210
+ align-items: center;
5211
+ justify-content: center;
5212
+ border: 1px solid #dfd6fb;
5213
+ border-radius: 8px;
5214
+ background: #eee6fe;
5215
+ color: #57575b;
5216
+ box-shadow: 0 8px 18px rgba(87, 87, 91, 0.14);
5217
+ "
5218
+ >
5219
+ ${this.renderIcon("Lock")}
5220
+ </div>
5221
+ </div>
5222
+ <h2
5223
+ style="
5224
+ margin: 0 0 8px;
5225
+ font-size: 16px;
5226
+ line-height: 1.35;
5227
+ font-weight: 600;
5228
+ color: #010507;
5229
+ "
5230
+ >
5231
+ Enable Intelligence to inspect Threads.
5232
+ </h2>
5233
+ <p
5234
+ style="
5235
+ margin: 0 auto 18px;
5236
+ max-width: 380px;
5237
+ font-size: 13px;
5238
+ line-height: 1.55;
5239
+ color: #57575b;
5240
+ "
5241
+ >
5242
+ Persist conversations and inspect saved thread history from the
5243
+ Inspector.
5244
+ </p>
5245
+ <div
5246
+ style="
5247
+ display: flex;
5248
+ flex-wrap: wrap;
5249
+ justify-content: center;
5250
+ gap: 8px;
5251
+ "
5252
+ >
5253
+ <a
5254
+ href=${this.getTalkToEngineerUrl()}
5255
+ target="_blank"
5256
+ rel="noopener"
5257
+ style="
5258
+ display: inline-flex;
5259
+ min-height: 34px;
5260
+ align-items: center;
5261
+ justify-content: center;
5262
+ gap: 6px;
5263
+ border-radius: 6px;
5264
+ background: #010507;
5265
+ padding: 8px 12px;
5266
+ font-size: 12px;
5267
+ font-weight: 600;
5268
+ color: #ffffff;
5269
+ text-decoration: none;
5270
+ "
5271
+ @click=${this.handleThreadsTalkToEngineerClick}
5272
+ >
5273
+ Talk to an Engineer
5274
+ </a>
5275
+ <a
5276
+ href=${this.getIntelligenceSignupUrl()}
5277
+ target="_blank"
5278
+ rel="noopener"
5279
+ style="
5280
+ display: inline-flex;
5281
+ min-height: 34px;
5282
+ align-items: center;
5283
+ justify-content: center;
5284
+ gap: 6px;
5285
+ border-radius: 6px;
5286
+ border: 1px solid #dbdbe5;
5287
+ background: #ffffff;
5288
+ padding: 8px 12px;
5289
+ font-size: 12px;
5290
+ font-weight: 600;
5291
+ color: #57575b;
5292
+ text-decoration: none;
5293
+ "
5294
+ @click=${this.handleThreadsIntelligenceSignupClick}
5295
+ >
5296
+ Sign up for Intelligence
5297
+ </a>
5298
+ </div>
5299
+ </div>
5300
+ </div>
5301
+ `;
5302
+ }
4330
5303
  renderThreadsView() {
5304
+ if (!this.areThreadEndpointsAvailable()) return this.renderThreadsLockedView();
4331
5305
  const displayThreads = this.selectedContext === "all-agents" ? this._threads : this._threadsByAgent.get(this.selectedContext) ?? [];
4332
5306
  let threadsErrorMessage = null;
4333
5307
  if (this.selectedContext === "all-agents") threadsErrorMessage = this._threadsErrorByAgent.values().next().value?.message ?? null;
4334
5308
  else threadsErrorMessage = this._threadsErrorByAgent.get(this.selectedContext)?.message ?? null;
4335
5309
  const selectedThread = this.selectedThreadId != null ? displayThreads.find((t) => t.id === this.selectedThreadId) ?? null : null;
5310
+ if (!threadsErrorMessage) this.trackThreadsViewStateOnce(displayThreads.length === 0 ? "empty_enabled" : "enabled", displayThreads.length);
4336
5311
  return html`
4337
- <div style="display:flex;height:100%;overflow:hidden;">
4338
- <!-- Left sidebar: thread list -->
5312
+ <div style="display:flex;height:100%;overflow:hidden;flex-direction:column;">
4339
5313
  <div
4340
- style="width:${this.threadListWidth}px;flex-shrink:0;overflow:hidden;display:flex;flex-direction:column;border-right:1px solid #DBDBE5;"
5314
+ style="display:flex;align-items:center;justify-content:flex-end;border-bottom:1px solid #DBDBE5;background:#ffffff;padding:8px 12px;flex-shrink:0;"
4341
5315
  >
4342
- <cpk-thread-list
4343
- style="height:100%;"
4344
- .threads=${displayThreads}
4345
- .selectedThreadId=${this.selectedThreadId}
4346
- .errorMessage=${threadsErrorMessage}
4347
- @threadSelected=${(e) => {
5316
+ <a
5317
+ href=${this.getTalkToEngineerUrl()}
5318
+ target="_blank"
5319
+ rel="noopener"
5320
+ 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;"
5321
+ @click=${this.handleTalkToEngineerClick}
5322
+ >
5323
+ Talk to an Engineer
5324
+ </a>
5325
+ </div>
5326
+ <div style="display:flex;min-height:0;flex:1;overflow:hidden;">
5327
+ <!-- Left sidebar: thread list -->
5328
+ <div
5329
+ style="width:${this.threadListWidth}px;flex-shrink:0;overflow:hidden;display:flex;flex-direction:column;border-right:1px solid #DBDBE5;"
5330
+ >
5331
+ <cpk-thread-list
5332
+ style="height:100%;"
5333
+ .threads=${displayThreads}
5334
+ .selectedThreadId=${this.selectedThreadId}
5335
+ .errorMessage=${threadsErrorMessage}
5336
+ @threadSelected=${(e) => {
4348
5337
  this.selectedThreadId = e.detail;
4349
5338
  this.requestUpdate();
4350
5339
  }}
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>
5340
+ ></cpk-thread-list>
5341
+ </div>
4362
5342
 
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"
5343
+ <!-- Resize divider -->
5344
+ <div
5345
+ style="width:4px;flex-shrink:0;cursor:col-resize;background:transparent;position:relative;z-index:1;"
5346
+ @pointerdown=${this.handleThreadDividerPointerDown}
5347
+ @pointermove=${this.handleThreadDividerPointerMove}
5348
+ @pointerup=${this.handleThreadDividerPointerUp}
5349
+ @pointercancel=${this.handleThreadDividerPointerUp}
5350
+ ></div>
5351
+
5352
+ <!-- Center + right: thread details or empty state -->
5353
+ <div style="flex:1;min-width:0;overflow:hidden;display:flex;">
5354
+ ${selectedThread ? html`<cpk-thread-details
5355
+ style="flex:1;min-width:0;"
5356
+ .threadId=${selectedThread.id}
5357
+ .thread=${selectedThread}
5358
+ .runtimeUrl=${this._core?.runtimeUrl ?? ""}
5359
+ .headers=${this._core?.headers ?? {}}
5360
+ .threadInspectionAvailable=${this._core?.threadEndpoints?.inspect !== false}
5361
+ .liveMessageVersion=${this.liveMessageVersion.get(selectedThread.id) ?? 0}
5362
+ .agentStateInput=${this.getLatestStateForAgent(selectedThread.agentId)}
5363
+ .agentEventsInput=${this.agentEvents.get(selectedThread.agentId) ?? []}
5364
+ ></cpk-thread-details>` : html`
5365
+ <div
5366
+ style="
5367
+ flex: 1;
5368
+ display: flex;
5369
+ flex-direction: column;
5370
+ align-items: center;
5371
+ justify-content: center;
5372
+ gap: 8px;
5373
+ color: #838389;
5374
+ "
4396
5375
  >
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
- `}
5376
+ <svg
5377
+ width="32"
5378
+ height="32"
5379
+ viewBox="0 0 24 24"
5380
+ fill="none"
5381
+ stroke="#c0c0c8"
5382
+ stroke-width="1.5"
5383
+ stroke-linecap="round"
5384
+ stroke-linejoin="round"
5385
+ >
5386
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
5387
+ </svg>
5388
+ <span style="font-size: 13px">${displayThreads.length === 0 ? "No threads yet" : "Select a thread to inspect"}</span>
5389
+ </div>
5390
+ `}
5391
+ </div>
4402
5392
  </div>
4403
5393
  </div>
4404
5394
  `;
@@ -4515,10 +5505,10 @@ ${argsString}</pre
4515
5505
  <div class="relative h-full w-full overflow-y-auto overflow-x-hidden">
4516
5506
  <table class="w-full table-fixed border-collapse text-xs box-border">
4517
5507
  <colgroup>
4518
- <col style="width:${this.evtColWidths[0]}px">
4519
- <col style="width:${this.evtColWidths[1]}px">
4520
- <col style="width:${this.evtColWidths[2]}px">
4521
- <col>
5508
+ <col style="width:${this.evtColWidths[0]}px" />
5509
+ <col style="width:${this.evtColWidths[1]}px" />
5510
+ <col style="width:${this.evtColWidths[2]}px" />
5511
+ <col />
4522
5512
  </colgroup>
4523
5513
  <thead class="sticky top-0 z-10">
4524
5514
  <tr class="bg-white">
@@ -4526,20 +5516,19 @@ ${argsString}</pre
4526
5516
  "Agent",
4527
5517
  "Time",
4528
5518
  "Event Type"
4529
- ].map((label, col) => html`
4530
- <th
4531
- class="border-b border-gray-200 bg-white px-3 py-2 text-left font-medium text-gray-900"
4532
- style="position:relative;overflow:hidden;"
4533
- >
4534
- ${label}
4535
- <div
4536
- style="position:absolute;top:0;right:0;width:5px;height:100%;cursor:col-resize;user-select:none;background:transparent;"
4537
- @pointerdown=${(e) => this._onEvtColResizeStart(e, col)}
4538
- @pointermove=${(e) => this._onEvtColResizeMove(e)}
4539
- @pointerup=${() => this._onEvtColResizeEnd()}
4540
- @pointercancel=${() => this._onEvtColResizeEnd()}
4541
- ></div>
4542
- </th>`)}
5519
+ ].map((label, col) => html` <th
5520
+ class="border-b border-gray-200 bg-white px-3 py-2 text-left font-medium text-gray-900"
5521
+ style="position:relative;overflow:hidden;"
5522
+ >
5523
+ ${label}
5524
+ <div
5525
+ style="position:absolute;top:0;right:0;width:5px;height:100%;cursor:col-resize;user-select:none;background:transparent;"
5526
+ @pointerdown=${(e) => this._onEvtColResizeStart(e, col)}
5527
+ @pointermove=${(e) => this._onEvtColResizeMove(e)}
5528
+ @pointerup=${() => this._onEvtColResizeEnd()}
5529
+ @pointercancel=${() => this._onEvtColResizeEnd()}
5530
+ ></div>
5531
+ </th>`)}
4543
5532
  <th
4544
5533
  class="border-b border-gray-200 bg-white px-3 py-2 text-left font-medium text-gray-900"
4545
5534
  >
@@ -4796,8 +5785,14 @@ ${prettyEvent}</pre
4796
5785
  ${messages && messages.length > 0 ? html`
4797
5786
  <div class="w-full text-xs">
4798
5787
  <div class="flex bg-gray-50">
4799
- <div class="w-40 shrink-0 px-4 py-2 font-medium text-gray-700">Role</div>
4800
- <div class="flex-1 px-4 py-2 font-medium text-gray-700">Content</div>
5788
+ <div
5789
+ class="w-40 shrink-0 px-4 py-2 font-medium text-gray-700"
5790
+ >
5791
+ Role
5792
+ </div>
5793
+ <div class="flex-1 px-4 py-2 font-medium text-gray-700">
5794
+ Content
5795
+ </div>
4801
5796
  </div>
4802
5797
  <div class="divide-y divide-gray-200">
4803
5798
  ${messages.map((msg) => {
@@ -4919,7 +5914,7 @@ ${prettyEvent}</pre
4919
5914
  if (this.contextOptions.filter((opt) => opt.key !== "all-agents").length > 1) this.selectedContext = "all-agents";
4920
5915
  }
4921
5916
  if (key === "threads") {
4922
- if (this.selectedMenu !== "threads" && !this.core?.telemetryDisabled) trackThreadsTabClicked();
5917
+ if (previousMenu !== "threads" && !this.core?.telemetryDisabled) trackThreadsTabClicked(this.getThreadsTelemetryProps());
4923
5918
  this.autoSelectLatestThread();
4924
5919
  }
4925
5920
  if (key === "ag-ui-events" || key === "agents") requestAnimationFrame(() => {
@@ -5343,6 +6338,22 @@ ${prettyEvent}</pre
5343
6338
  ${this.copiedContextItems.has(id) ? "Copied" : "Copy JSON"}
5344
6339
  </button>
5345
6340
  </div>
6341
+ <pre
6342
+ style="
6343
+ margin: 0;
6344
+ max-height: 180px;
6345
+ overflow: auto;
6346
+ white-space: pre-wrap;
6347
+ word-break: break-word;
6348
+ border-radius: 6px;
6349
+ border: 1px solid #eeeef4;
6350
+ background: #f7f7f9;
6351
+ padding: 10px;
6352
+ font-size: 11px;
6353
+ line-height: 1.5;
6354
+ color: #2d2d30;
6355
+ "
6356
+ >${this.formatContextValue(context.value)}</pre>
5346
6357
  ` : html`
5347
6358
  <div class="flex items-center justify-center py-4 text-xs text-gray-500">
5348
6359
  <span>No value available</span>
@@ -5418,7 +6429,9 @@ ${prettyEvent}</pre
5418
6429
  <span>Loading latest announcement…</span>
5419
6430
  </div>`;
5420
6431
  if (!this.announcementHtml) return nothing;
5421
- return html`<div class="mx-4 mt-3 mb-3 rounded-xl border border-slate-200 bg-white px-4 py-3">
6432
+ return html`<div
6433
+ class="mx-4 mt-3 mb-3 rounded-xl border border-slate-200 bg-white px-4 py-3"
6434
+ >
5422
6435
  <div
5423
6436
  class="mb-2 flex items-center gap-2 text-xs font-semibold text-slate-900"
5424
6437
  >
@@ -5437,7 +6450,9 @@ ${prettyEvent}</pre
5437
6450
  ${this.renderIcon("X")}
5438
6451
  </button>
5439
6452
  </div>
5440
- <div class="announcement-body ${this.announcementExpanded ? "announcement-body--expanded" : "announcement-body--collapsed"}">
6453
+ <div
6454
+ class="announcement-body ${this.announcementExpanded ? "announcement-body--expanded" : "announcement-body--collapsed"}"
6455
+ >
5441
6456
  <div
5442
6457
  class="announcement-content"
5443
6458
  @click=${this.handleAnnouncementContentClick}
@@ -5533,8 +6548,9 @@ ${prettyEvent}</pre
5533
6548
  async convertMarkdownToHtml(markdown) {
5534
6549
  const renderer = new marked.Renderer();
5535
6550
  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>`;
6551
+ return `<a href="${this.escapeHtmlAttr(this.isSafeAnnouncementHref(href ?? "") ? this.appendRefParam(href ?? "") : "#")}" target="_blank" rel="noopener"${title ? ` title="${this.escapeHtmlAttr(title)}"` : ""}>${text}</a>`;
5537
6552
  };
6553
+ renderer.html = (html) => escapeHtml(html);
5538
6554
  renderer.code = (code, lang) => {
5539
6555
  const safeLang = (lang ?? "").replace(/[^a-z0-9-]/gi, "");
5540
6556
  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 +6560,14 @@ ${prettyEvent}</pre
5544
6560
  async: false
5545
6561
  });
5546
6562
  }
6563
+ isSafeAnnouncementHref(href) {
6564
+ try {
6565
+ const url = new URL(href, typeof window !== "undefined" ? window.location.href : "https://copilotkit.ai");
6566
+ return url.protocol === "http:" || url.protocol === "https:" || url.protocol === "mailto:";
6567
+ } catch {
6568
+ return false;
6569
+ }
6570
+ }
5547
6571
  encodeBase64(value) {
5548
6572
  if (typeof window === "undefined" || typeof window.btoa !== "function") return "";
5549
6573
  const bytes = new TextEncoder().encode(value);
@@ -5558,19 +6582,25 @@ ${prettyEvent}</pre
5558
6582
  for (let i = 0; i < decoded.length; i++) bytes[i] = decoded.charCodeAt(i);
5559
6583
  return new TextDecoder().decode(bytes);
5560
6584
  }
5561
- appendRefParam(href) {
6585
+ appendRefParam(href, ref = "cpk-inspector") {
5562
6586
  try {
6587
+ const isRootRelative = href.startsWith("/") && !href.startsWith("//");
5563
6588
  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) {
6589
+ if (!url.searchParams.has("ref")) url.searchParams.append("ref", ref);
6590
+ if (!url.searchParams.has("posthog_distinct_id") && !this.core?.telemetryDisabled && this.isCopilotKitDestination(url)) {
5566
6591
  const distinctId = getTelemetryDistinctIdForUrl();
5567
6592
  if (distinctId) url.searchParams.append("posthog_distinct_id", distinctId);
5568
6593
  }
6594
+ if (isRootRelative) return `${url.pathname}${url.search}${url.hash}`;
5569
6595
  return url.toString();
5570
6596
  } catch {
5571
6597
  return href;
5572
6598
  }
5573
6599
  }
6600
+ isCopilotKitDestination(url) {
6601
+ const hostname = url.hostname.toLowerCase();
6602
+ return hostname === "copilotkit.ai" || hostname.endsWith(".copilotkit.ai");
6603
+ }
5574
6604
  escapeHtmlAttr(value) {
5575
6605
  return escapeHtml(value).replace(/"/g, "&quot;").replace(/'/g, "&#39;");
5576
6606
  }
@@ -5610,5 +6640,5 @@ function defineWebInspector() {
5610
6640
  defineWebInspector();
5611
6641
 
5612
6642
  //#endregion
5613
- export { WEB_INSPECTOR_TAG, WebInspectorElement, defineWebInspector, ɵCpkThreadDetails };
6643
+ export { CpkThreadInspector, THREAD_INSPECTOR_TAG, WEB_INSPECTOR_TAG, WebInspectorElement, defineWebInspector, ɵCpkThreadDetails };
5614
6644
  //# sourceMappingURL=index.mjs.map