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