@copilotkit/web-inspector 1.56.4 → 1.56.5-canary.1777664617

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
@@ -5,9 +5,9 @@ const require_inspector_logo_icon = require('./assets/inspector-logo-icon.cjs');
5
5
  const require_context_helpers = require('./lib/context-helpers.cjs');
6
6
  const require_persistence = require('./lib/persistence.cjs');
7
7
  let lit = require("lit");
8
+ let marked = require("marked");
8
9
  let lit_directives_style_map_js = require("lit/directives/style-map.js");
9
10
  let lit_directives_unsafe_html_js = require("lit/directives/unsafe-html.js");
10
- let marked = require("marked");
11
11
  let lucide = require("lucide");
12
12
  let _copilotkit_core = require("@copilotkit/core");
13
13
 
@@ -27,7 +27,7 @@ const DEFAULT_BUTTON_SIZE = {
27
27
  };
28
28
  const DEFAULT_WINDOW_SIZE = {
29
29
  width: 840,
30
- height: 560
30
+ height: 700
31
31
  };
32
32
  const DOCKED_LEFT_WIDTH = 500;
33
33
  const MAX_AGENT_EVENTS = 200;
@@ -53,11 +53,1765 @@ const AGENT_EVENT_TYPES = [
53
53
  "REASONING_MESSAGE_CONTENT",
54
54
  "REASONING_MESSAGE_END",
55
55
  "REASONING_END",
56
- "REASONING_ENCRYPTED_VALUE"
56
+ "REASONING_ENCRYPTED_VALUE",
57
+ "ACTIVITY_SNAPSHOT",
58
+ "ACTIVITY_DELTA"
57
59
  ];
58
- var WebInspectorElement = class extends lit.LitElement {
60
+ function escapeHtml(s) {
61
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
62
+ }
63
+ const highlightedJsonCache = /* @__PURE__ */ new WeakMap();
64
+ function highlightedJson(obj) {
65
+ if (typeof obj === "object" && obj !== null) {
66
+ const cached = highlightedJsonCache.get(obj);
67
+ if (cached !== void 0) return cached;
68
+ }
69
+ const colors = {
70
+ key: "#5558B2",
71
+ str: "#189370",
72
+ num: "#996300",
73
+ bool: "#c0333a",
74
+ nil: "#838389"
75
+ };
76
+ const json = JSON.stringify(obj, null, 2);
77
+ if (!json) return "";
78
+ const parts = [];
79
+ let lastIndex = 0;
80
+ const re = /("(?:\\u[a-fA-F0-9]{4}|\\[^u]|[^\\"])*"(?:\s*:)?|\b(?:true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)/g;
81
+ let match;
82
+ while ((match = re.exec(json)) !== null) {
83
+ parts.push(escapeHtml(json.slice(lastIndex, match.index)));
84
+ const m = match[0];
85
+ let color = colors.num;
86
+ if (m.startsWith("\"")) color = m.trimEnd().endsWith(":") ? colors.key : colors.str;
87
+ else if (m === "true" || m === "false") color = colors.bool;
88
+ else if (m === "null") color = colors.nil;
89
+ parts.push(`<span style="color:${color}">${escapeHtml(m)}</span>`);
90
+ lastIndex = match.index + m.length;
91
+ }
92
+ parts.push(escapeHtml(json.slice(lastIndex)));
93
+ const result = parts.join("");
94
+ if (typeof obj === "object" && obj !== null) highlightedJsonCache.set(obj, result);
95
+ return result;
96
+ }
97
+ function eventColors(type) {
98
+ if (type.startsWith("TEXT_MESSAGE")) return {
99
+ bg: "#EEE6FE",
100
+ fg: "#57575B"
101
+ };
102
+ if (type.startsWith("TOOL_CALL")) return {
103
+ bg: "rgba(133,236,206,0.15)",
104
+ fg: "#189370"
105
+ };
106
+ if (type.startsWith("STATE")) return {
107
+ bg: "rgba(190,194,255,0.102)",
108
+ fg: "#5558B2"
109
+ };
110
+ if (type.startsWith("RUN_") || type.startsWith("STEP_")) return {
111
+ bg: "rgba(255,172,77,0.2)",
112
+ fg: "#996300"
113
+ };
114
+ if (type === "ERROR") return {
115
+ bg: "rgba(250,95,103,0.13)",
116
+ fg: "#c0333a"
117
+ };
118
+ return {
119
+ bg: "#F7F7F9",
120
+ fg: "#838389"
121
+ };
122
+ }
123
+ function formatTimestamp(ts) {
124
+ const date = typeof ts === "number" ? new Date(ts) : new Date(ts);
125
+ if (Number.isNaN(date.getTime())) return "";
126
+ const ms = date.getMilliseconds().toString().padStart(3, "0");
127
+ return date.toLocaleTimeString("en-US", {
128
+ hour: "2-digit",
129
+ minute: "2-digit",
130
+ second: "2-digit",
131
+ hour12: false
132
+ }) + "." + ms;
133
+ }
134
+ var CpkThreadList = class extends lit.LitElement {
59
135
  constructor(..._args) {
60
136
  super(..._args);
137
+ this.threads = [];
138
+ this.selectedThreadId = null;
139
+ this.errorMessage = null;
140
+ this._query = "";
141
+ this.onSearchInput = (event) => {
142
+ this._query = event.target.value;
143
+ };
144
+ }
145
+ static {
146
+ this.properties = {
147
+ threads: { attribute: false },
148
+ selectedThreadId: { attribute: false },
149
+ errorMessage: { attribute: false },
150
+ _query: { state: true }
151
+ };
152
+ }
153
+ static {
154
+ this.styles = lit.css`
155
+ @import url("https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600&family=Spline+Sans+Mono:wght@400;500&display=swap");
156
+
157
+ :host {
158
+ display: flex;
159
+ flex-direction: column;
160
+ height: 100%;
161
+ overflow: hidden;
162
+ }
163
+
164
+ .cpk-tl {
165
+ font-family: "Plus Jakarta Sans", sans-serif;
166
+ display: flex;
167
+ flex-direction: column;
168
+ height: 100%;
169
+ overflow: hidden;
170
+ background: #f7f7f9;
171
+ }
172
+
173
+ /* ── Search ── */
174
+ .cpk-tl__search {
175
+ padding: 10px 12px;
176
+ border-bottom: 1px solid #dbdbe5;
177
+ flex-shrink: 0;
178
+ }
179
+
180
+ .cpk-tl__search-input {
181
+ width: 100%;
182
+ box-sizing: border-box;
183
+ font-family: "Plus Jakarta Sans", sans-serif;
184
+ font-size: 12px;
185
+ padding: 7px 10px;
186
+ border-radius: 6px;
187
+ border: 1px solid #dbdbe5;
188
+ background: #ffffff;
189
+ color: #010507;
190
+ outline: none;
191
+ transition: border-color 0.15s;
192
+ }
193
+
194
+ .cpk-tl__search-input:focus {
195
+ border-color: #bec2ff;
196
+ }
197
+
198
+ /* ── List ── */
199
+ .cpk-tl__list {
200
+ flex: 1;
201
+ overflow-y: auto;
202
+ }
203
+
204
+ /* ── Thread item ── */
205
+ .cpk-tl__item {
206
+ padding: 11px 13px;
207
+ cursor: pointer;
208
+ border-bottom: 1px solid #e9e9ef;
209
+ border-left: 3px solid transparent;
210
+ transition: background 0.1s;
211
+ }
212
+
213
+ .cpk-tl__item:hover {
214
+ background: #ffffff;
215
+ }
216
+
217
+ .cpk-tl__item--active {
218
+ background: #bec2ff1a;
219
+ border-left-color: #bec2ff;
220
+ }
221
+
222
+ .cpk-tl__item--active:hover {
223
+ background: #bec2ff33;
224
+ }
225
+
226
+ .cpk-tl__row1 {
227
+ display: flex;
228
+ align-items: center;
229
+ gap: 8px;
230
+ margin-bottom: 3px;
231
+ }
232
+
233
+ .cpk-tl__name {
234
+ font-size: 12px;
235
+ font-weight: 500;
236
+ color: #010507;
237
+ flex: 1;
238
+ overflow: hidden;
239
+ text-overflow: ellipsis;
240
+ white-space: nowrap;
241
+ }
242
+
243
+ .cpk-tl__name--unnamed {
244
+ color: #838389;
245
+ font-style: italic;
246
+ font-weight: 400;
247
+ }
248
+
249
+ .cpk-tl__time {
250
+ font-family: "Spline Sans Mono", monospace;
251
+ font-size: 10px;
252
+ color: #838389;
253
+ flex-shrink: 0;
254
+ }
255
+
256
+ .cpk-tl__meta {
257
+ display: flex;
258
+ gap: 6px;
259
+ align-items: center;
260
+ flex-wrap: wrap;
261
+ }
262
+
263
+ .cpk-tl__pill {
264
+ font-family: "Spline Sans Mono", monospace;
265
+ font-size: 9px;
266
+ padding: 1px 7px;
267
+ border-radius: 4px;
268
+ text-transform: uppercase;
269
+ font-weight: 500;
270
+ white-space: nowrap;
271
+ background: #eee6fe;
272
+ color: #57575b;
273
+ }
274
+
275
+ /* ── Empty state ── */
276
+ .cpk-tl__empty {
277
+ padding: 32px 16px;
278
+ text-align: center;
279
+ color: #838389;
280
+ font-size: 12px;
281
+ display: flex;
282
+ flex-direction: column;
283
+ align-items: center;
284
+ gap: 8px;
285
+ }
286
+
287
+ .cpk-tl__empty-icon {
288
+ color: #c0c0c8;
289
+ }
290
+ `;
291
+ }
292
+ relativeTime(dateStr) {
293
+ const date = new Date(dateStr);
294
+ const diffMs = Date.now() - date.getTime();
295
+ const diffSec = Math.floor(diffMs / 1e3);
296
+ if (diffSec < 60) return `${diffSec}s ago`;
297
+ const diffMin = Math.floor(diffSec / 60);
298
+ if (diffMin < 60) return `${diffMin}m ago`;
299
+ const diffH = Math.floor(diffMin / 60);
300
+ if (diffH < 24) return `${diffH}h ago`;
301
+ return `${Math.floor(diffH / 24)}d ago`;
302
+ }
303
+ get filtered() {
304
+ const q = this._query.toLowerCase();
305
+ if (!q) return this.threads;
306
+ return this.threads.filter((t) => (t.name?.toLowerCase().includes(q) ?? false) || t.agentId.toLowerCase().includes(q) || t.id.toLowerCase().includes(q));
307
+ }
308
+ onThreadClick(threadId) {
309
+ this.dispatchEvent(new CustomEvent("threadSelected", {
310
+ detail: threadId,
311
+ bubbles: true,
312
+ composed: true
313
+ }));
314
+ }
315
+ render() {
316
+ const filtered = this.filtered;
317
+ return lit.html`
318
+ <div class="cpk-tl">
319
+ <!-- Search -->
320
+ <div class="cpk-tl__search">
321
+ <input
322
+ type="text"
323
+ placeholder="Search threads…"
324
+ .value=${this._query}
325
+ @input=${this.onSearchInput}
326
+ class="cpk-tl__search-input"
327
+ />
328
+ </div>
329
+
330
+ <!-- Thread list -->
331
+ <div class="cpk-tl__list">
332
+ ${filtered.map((thread) => lit.html`
333
+ <div
334
+ class="cpk-tl__item ${this.selectedThreadId === thread.id ? "cpk-tl__item--active" : ""}"
335
+ @click=${() => this.onThreadClick(thread.id)}
336
+ >
337
+ <div class="cpk-tl__row1">
338
+ <span
339
+ class="cpk-tl__name ${!thread.name ? "cpk-tl__name--unnamed" : ""}"
340
+ >${thread.name ?? "Untitled"}</span
341
+ >
342
+ <span class="cpk-tl__time"
343
+ >${this.relativeTime(thread.updatedAt)}</span
344
+ >
345
+ </div>
346
+ <div class="cpk-tl__meta">
347
+ <span class="cpk-tl__pill">${thread.agentId}</span>
348
+ </div>
349
+ </div>
350
+ `)}
351
+ ${filtered.length === 0 ? lit.html`
352
+ <div class="cpk-tl__empty">
353
+ ${this.errorMessage ? lit.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"
364
+ >
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>
374
+ </div>
375
+ ` : this.threads.length === 0 ? lit.html`
376
+ <svg
377
+ width="24"
378
+ height="24"
379
+ viewBox="0 0 24 24"
380
+ fill="none"
381
+ stroke="currentColor"
382
+ stroke-width="1.5"
383
+ stroke-linecap="round"
384
+ stroke-linejoin="round"
385
+ class="cpk-tl__empty-icon"
386
+ >
387
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
388
+ </svg>
389
+ No threads yet
390
+ ` : lit.html`
391
+ No threads match your search.
392
+ `}
393
+ </div>
394
+ ` : lit.nothing}
395
+ </div>
396
+ </div>
397
+ `;
398
+ }
399
+ };
400
+ var ɵCpkThreadDetails = class ɵCpkThreadDetails extends lit.LitElement {
401
+ constructor(..._args2) {
402
+ super(..._args2);
403
+ this.threadId = null;
404
+ this.thread = null;
405
+ this.runtimeUrl = "";
406
+ this.headers = {};
407
+ this.agentStateInput = null;
408
+ this.agentEventsInput = [];
409
+ this.liveMessageVersion = 0;
410
+ this._tab = "conversation";
411
+ this._conversation = [];
412
+ this._fetchedEvents = null;
413
+ this._fetchedState = null;
414
+ this._loadingMessages = false;
415
+ this._loadingEvents = false;
416
+ this._loadingState = false;
417
+ this._messagesError = null;
418
+ this._eventsError = null;
419
+ this._stateError = null;
420
+ this._expandedTools = /* @__PURE__ */ new Set();
421
+ this._expandedMessages = /* @__PURE__ */ new Set();
422
+ this._showDetailPanel = false;
423
+ this._detailPanelWidth = 250;
424
+ this._eventsNotAvailable = false;
425
+ this._stateNotAvailable = false;
426
+ this._panelInitializing = false;
427
+ this._activatedTabs = new Set(["conversation"]);
428
+ this._panelTplCache = /* @__PURE__ */ new Map();
429
+ this._eventsFetched = false;
430
+ this._stateFetched = false;
431
+ this._lastFetchedThreadId = null;
432
+ this._lastSeenLiveMessageVersion = 0;
433
+ this._messagesAbort = null;
434
+ this._eventsAbort = null;
435
+ this._stateAbort = null;
436
+ this._dividerResizing = false;
437
+ this._dividerPointerId = -1;
438
+ this._dividerStartX = 0;
439
+ this._dividerStartWidth = 0;
440
+ this.onDetailDividerDown = (event) => {
441
+ this._dividerResizing = true;
442
+ this._dividerPointerId = event.pointerId;
443
+ this._dividerStartX = event.clientX;
444
+ this._dividerStartWidth = this._detailPanelWidth;
445
+ event.currentTarget.setPointerCapture(event.pointerId);
446
+ event.preventDefault();
447
+ };
448
+ this.onDetailDividerMove = (event) => {
449
+ if (!this._dividerResizing || this._dividerPointerId !== event.pointerId) return;
450
+ const delta = this._dividerStartX - event.clientX;
451
+ this._detailPanelWidth = Math.max(160, Math.min(400, this._dividerStartWidth + delta));
452
+ };
453
+ this.onDetailDividerUp = (event) => {
454
+ if (this._dividerPointerId !== event.pointerId) return;
455
+ const target = event.currentTarget;
456
+ if (target.hasPointerCapture(this._dividerPointerId)) target.releasePointerCapture(this._dividerPointerId);
457
+ this._dividerResizing = false;
458
+ };
459
+ }
460
+ static {
461
+ this.properties = {
462
+ threadId: { attribute: false },
463
+ thread: { attribute: false },
464
+ runtimeUrl: { attribute: false },
465
+ headers: { attribute: false },
466
+ agentStateInput: { attribute: false },
467
+ agentEventsInput: { attribute: false },
468
+ liveMessageVersion: { attribute: false },
469
+ _tab: { state: true },
470
+ _conversation: { state: true },
471
+ _fetchedEvents: { state: true },
472
+ _fetchedState: { state: true },
473
+ _loadingMessages: { state: true },
474
+ _loadingEvents: { state: true },
475
+ _loadingState: { state: true },
476
+ _messagesError: { state: true },
477
+ _eventsError: { state: true },
478
+ _stateError: { state: true },
479
+ _expandedTools: { state: true },
480
+ _expandedMessages: { state: true },
481
+ _showDetailPanel: { state: true },
482
+ _detailPanelWidth: { state: true },
483
+ _eventsNotAvailable: { state: true },
484
+ _stateNotAvailable: { state: true },
485
+ _panelInitializing: { state: true },
486
+ _activatedTabs: { state: true }
487
+ };
488
+ }
489
+ static {
490
+ this.COLLAPSE_THRESHOLD = 800;
491
+ }
492
+ static {
493
+ this.TAB_LIST = [
494
+ {
495
+ id: "conversation",
496
+ label: "Conversation"
497
+ },
498
+ {
499
+ id: "agent-state",
500
+ label: "Agent State"
501
+ },
502
+ {
503
+ id: "ag-ui-events",
504
+ label: "AG-UI Events"
505
+ }
506
+ ];
507
+ }
508
+ renderTabContent(id) {
509
+ if (id === "conversation") return this.renderConversation();
510
+ if (id === "agent-state") return this.renderState();
511
+ return this.renderEvents();
512
+ }
513
+ activateTab(id) {
514
+ if (this._tab === id) return;
515
+ const isFirstActivation = !this._activatedTabs.has(id);
516
+ this._tab = id;
517
+ if (isFirstActivation) {
518
+ this._panelInitializing = true;
519
+ requestAnimationFrame(() => {
520
+ this._activatedTabs = new Set([...this._activatedTabs, id]);
521
+ this._panelInitializing = false;
522
+ });
523
+ }
524
+ this.maybeFetchTabData(id);
525
+ }
526
+ maybeFetchTabData(id) {
527
+ if (!this.threadId) return;
528
+ if (id === "ag-ui-events" && !this._eventsFetched) {
529
+ this._eventsFetched = true;
530
+ this.fetchEvents(this.threadId);
531
+ } else if (id === "agent-state" && !this._stateFetched) {
532
+ this._stateFetched = true;
533
+ this.fetchState(this.threadId);
534
+ }
535
+ }
536
+ static {
537
+ this.styles = lit.css`
538
+ @import url("https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600&family=Spline+Sans+Mono:wght@400;500&display=swap");
539
+
540
+ /* ── Root ────────────────────────────────────────────────────────── */
541
+ :host {
542
+ display: flex;
543
+ flex-direction: row;
544
+ overflow: hidden;
545
+ }
546
+
547
+ .cpk-td {
548
+ font-family: "Plus Jakarta Sans", sans-serif;
549
+ font-size: 13px;
550
+ display: flex;
551
+ flex-direction: row;
552
+ width: 100%;
553
+ height: 100%;
554
+ overflow: hidden;
555
+ background: #ffffff;
556
+ }
557
+
558
+ /* ── Left area ───────────────────────────────────────────────────── */
559
+ .cpk-td__left {
560
+ flex: 1;
561
+ min-width: 0;
562
+ display: flex;
563
+ flex-direction: column;
564
+ overflow: hidden;
565
+ }
566
+
567
+ /* ── Tab bar header ──────────────────────────────────────────────── */
568
+ .cpk-td__tabs-header {
569
+ /* No top/right padding so tabs and toggle sit flush against the
570
+ top and right edges of the inspector. */
571
+ padding: 0 0 0 12px;
572
+ border-bottom: 1px solid #dbdbe5;
573
+ flex-shrink: 0;
574
+ display: flex;
575
+ align-items: stretch;
576
+ }
577
+
578
+ .cpk-td__tab-group {
579
+ display: flex;
580
+ gap: 0;
581
+ margin-bottom: -1px;
582
+ /* Allow the tab list to shrink rather than pushing the panel-toggle
583
+ button past the right edge of the inspector when horizontal space
584
+ gets tight (the drawer being open eats noticeably into width). */
585
+ min-width: 0;
586
+ flex-shrink: 1;
587
+ overflow: hidden;
588
+ }
589
+
590
+ .cpk-td__tab {
591
+ font-family: "Plus Jakarta Sans", sans-serif;
592
+ font-size: 11px;
593
+ font-weight: 500;
594
+ padding: 10px 12px;
595
+ border: none;
596
+ border-bottom: 2px solid transparent;
597
+ cursor: pointer;
598
+ background: transparent;
599
+ color: #838389;
600
+ transition:
601
+ color 0.12s,
602
+ border-color 0.12s;
603
+ white-space: nowrap;
604
+ }
605
+
606
+ .cpk-td__tab:hover {
607
+ color: #010507;
608
+ }
609
+
610
+ .cpk-td__tab--active {
611
+ color: #010507;
612
+ border-bottom-color: #bec2ff;
613
+ }
614
+
615
+ /* Toggle is a separate control, not a tab — so it does NOT use the
616
+ tabs' bottom-border active indicator. Instead, a subtle filled
617
+ state communicates "the drawer is open," and a vertical separator
618
+ on the left visually divorces it from the tab group. */
619
+ .cpk-td__panel-toggle {
620
+ margin-left: auto;
621
+ align-self: stretch;
622
+ display: flex;
623
+ align-items: center;
624
+ justify-content: center;
625
+ padding: 0 12px;
626
+ border: none;
627
+ border-left: 1px solid #dbdbe5;
628
+ background: transparent;
629
+ color: #838389;
630
+ cursor: pointer;
631
+ flex-shrink: 0;
632
+ transition:
633
+ color 0.12s,
634
+ background 0.12s;
635
+ }
636
+ .cpk-td__panel-toggle:hover {
637
+ color: #010507;
638
+ background: #f4f4f9;
639
+ }
640
+ .cpk-td__panel-toggle--active {
641
+ color: #5558b2;
642
+ background: #eee6fe;
643
+ }
644
+ .cpk-td__panel-toggle--active:hover {
645
+ background: #e4d8fc;
646
+ }
647
+
648
+ /* ── Scrollable content ──────────────────────────────────────────── */
649
+ .cpk-td__content {
650
+ flex: 1;
651
+ overflow-y: auto;
652
+ padding: 16px;
653
+ display: flex;
654
+ flex-direction: column;
655
+ gap: 8px;
656
+ }
657
+
658
+ /* Pin direct children so expanded tool bodies don't get flex-shrunk. */
659
+ .cpk-td__content > * {
660
+ flex-shrink: 0;
661
+ }
662
+
663
+ /*
664
+ * Each tab's content is wrapped in this panel so the keep-mounted
665
+ * inactive panels can be hidden via display:none without disturbing
666
+ * the gap between visible siblings. The flex column + gap gives each
667
+ * conversation item / event row breathing room (the cpk-td__content
668
+ * rule above no longer reaches them now that they are nested inside
669
+ * the per-panel wrapper).
670
+ */
671
+ .cpk-td__panel {
672
+ display: flex;
673
+ flex-direction: column;
674
+ gap: 12px;
675
+ }
676
+ .cpk-td__panel > * {
677
+ flex-shrink: 0;
678
+ }
679
+
680
+ /* ── Empty state ─────────────────────────────────────────────────── */
681
+ .cpk-td__empty-state {
682
+ flex: 1;
683
+ display: flex;
684
+ flex-direction: column;
685
+ align-items: center;
686
+ justify-content: center;
687
+ gap: 8px;
688
+ color: #838389;
689
+ font-size: 13px;
690
+ padding: 40px 0;
691
+ }
692
+
693
+ .cpk-td__empty-hint {
694
+ font-size: 11px;
695
+ color: #838389;
696
+ text-align: center;
697
+ max-width: 220px;
698
+ line-height: 1.5;
699
+ }
700
+
701
+ /* ── Status messages ─────────────────────────────────────────────── */
702
+ .cpk-td__status {
703
+ padding: 16px;
704
+ font-size: 12px;
705
+ color: #838389;
706
+ text-align: center;
707
+ }
708
+
709
+ .cpk-td__status--error {
710
+ color: #c0333a;
711
+ }
712
+
713
+ /* ── Conversation bubbles ────────────────────────────────────────── */
714
+ .cpk-td__bubble {
715
+ display: flex;
716
+ margin-bottom: 2px;
717
+ }
718
+
719
+ .cpk-td__bubble--user {
720
+ justify-content: flex-end;
721
+ }
722
+
723
+ .cpk-td__bubble--assistant {
724
+ justify-content: flex-start;
725
+ }
726
+
727
+ .cpk-td__bubble-inner {
728
+ padding: 9px 14px;
729
+ max-width: 75%;
730
+ font-size: 13px;
731
+ line-height: 1.55;
732
+ }
733
+
734
+ .cpk-td__bubble-inner--user {
735
+ background: #eee6fe;
736
+ color: #57575b;
737
+ border-radius: 10px 10px 3px 10px;
738
+ }
739
+
740
+ .cpk-td__show-more {
741
+ display: inline-block;
742
+ margin-top: 4px;
743
+ font-size: 11px;
744
+ font-weight: 500;
745
+ color: #57575b;
746
+ cursor: pointer;
747
+ text-decoration: underline;
748
+ text-underline-offset: 2px;
749
+ }
750
+
751
+ .cpk-td__bubble-inner--assistant {
752
+ background: #f7f7f9;
753
+ color: #010507;
754
+ border-radius: 10px 10px 10px 3px;
755
+ border: 1px solid #e9e9ef;
756
+ }
757
+
758
+ /* ── Tool call blocks ────────────────────────────────────────────── */
759
+ .cpk-td__tool-block {
760
+ border: 1px solid #e9e9ef;
761
+ border-radius: 6px;
762
+ overflow: hidden;
763
+ }
764
+
765
+ .cpk-td__tool-header {
766
+ display: flex;
767
+ align-items: center;
768
+ gap: 6px;
769
+ padding: 6px 10px;
770
+ background: rgba(133, 236, 206, 0.15);
771
+ cursor: pointer;
772
+ font-size: 11px;
773
+ user-select: none;
774
+ }
775
+
776
+ .cpk-td__tool-header:hover {
777
+ background: rgba(133, 236, 206, 0.22);
778
+ }
779
+
780
+ .cpk-td__tool-name {
781
+ font-family: "Spline Sans Mono", monospace;
782
+ font-size: 10px;
783
+ font-weight: 500;
784
+ color: #189370;
785
+ text-transform: uppercase;
786
+ flex: 1;
787
+ }
788
+
789
+ .cpk-td__tool-status {
790
+ font-family: "Spline Sans Mono", monospace;
791
+ font-size: 9px;
792
+ text-transform: uppercase;
793
+ color: #189370;
794
+ }
795
+
796
+ .cpk-td__tool-status--pending {
797
+ color: #996300;
798
+ }
799
+
800
+ .cpk-td__tool-chevron {
801
+ color: #838389;
802
+ font-size: 10px;
803
+ }
804
+
805
+ .cpk-td__tool-body {
806
+ padding: 8px 10px;
807
+ border-top: 1px solid #e9e9ef;
808
+ background: #ffffff;
809
+ }
810
+
811
+ .cpk-td__tool-section-label {
812
+ font-family: "Spline Sans Mono", monospace;
813
+ font-size: 9px;
814
+ font-weight: 500;
815
+ color: #838389;
816
+ text-transform: uppercase;
817
+ margin-bottom: 4px;
818
+ letter-spacing: 0.3px;
819
+ }
820
+
821
+ .cpk-td__tool-pre {
822
+ margin: 0;
823
+ font-family: "Spline Sans Mono", monospace;
824
+ font-size: 10px;
825
+ background: #f7f7f9;
826
+ padding: 6px 8px;
827
+ border-radius: 4px;
828
+ overflow-x: auto;
829
+ white-space: pre-wrap;
830
+ word-break: break-all;
831
+ color: #010507;
832
+ line-height: 1.6;
833
+ }
834
+
835
+ /* ── Tool call group ─────────────────────────────────────────────── */
836
+ .cpk-td__tool-group {
837
+ border: 1px solid #e9e9ef;
838
+ border-radius: 6px;
839
+ overflow: hidden;
840
+ }
841
+
842
+ .cpk-td__tool-group-header {
843
+ padding: 5px 10px;
844
+ background: rgba(133, 236, 206, 0.15);
845
+ font-family: "Spline Sans Mono", monospace;
846
+ font-size: 10px;
847
+ color: #189370;
848
+ text-transform: uppercase;
849
+ font-weight: 500;
850
+ border-bottom: 1px solid #e9e9ef;
851
+ }
852
+
853
+ .cpk-td__tool-group .cpk-td__tool-block {
854
+ border: none;
855
+ border-bottom: 1px solid #e9e9ef;
856
+ border-radius: 0;
857
+ }
858
+
859
+ .cpk-td__tool-group .cpk-td__tool-block:last-child {
860
+ border-bottom: none;
861
+ }
862
+
863
+ /* ── Inline chips (reasoning / state update) ─────────────────────── */
864
+ .cpk-td__inline-chip {
865
+ display: flex;
866
+ align-items: center;
867
+ gap: 8px;
868
+ padding: 5px 0;
869
+ color: #838389;
870
+ font-family: "Spline Sans Mono", monospace;
871
+ font-size: 9px;
872
+ text-transform: uppercase;
873
+ }
874
+
875
+ .cpk-td__inline-chip::before,
876
+ .cpk-td__inline-chip::after {
877
+ content: "";
878
+ flex: 1;
879
+ height: 1px;
880
+ background: #e9e9ef;
881
+ }
882
+
883
+ /* ── Generative UI ──────────────────────────────────────────────── */
884
+ @keyframes cpk-genui-enter {
885
+ from {
886
+ opacity: 0;
887
+ transform: translateY(8px);
888
+ }
889
+ to {
890
+ opacity: 1;
891
+ transform: translateY(0);
892
+ }
893
+ }
894
+
895
+ .cpk-td__genui {
896
+ display: flex;
897
+ flex-direction: column;
898
+ gap: 6px;
899
+ padding: 4px 16px 8px;
900
+ animation: cpk-genui-enter 0.25s cubic-bezier(0.16, 1, 0.3, 1) both;
901
+ }
902
+
903
+ .cpk-td__genui-badge {
904
+ display: inline-flex;
905
+ align-items: center;
906
+ gap: 4px;
907
+ padding: 2px 8px;
908
+ border-radius: 4px;
909
+ background: #eee6fe;
910
+ color: #57575b;
911
+ font-size: 10px;
912
+ font-weight: 600;
913
+ align-self: flex-start;
914
+ }
915
+
916
+ .cpk-td__genui-card {
917
+ overflow: hidden;
918
+ border-radius: 12px;
919
+ border: 1px solid #e2e8f0;
920
+ background: #fff;
921
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.08);
922
+ }
923
+
924
+ .cpk-td__genui-placeholder {
925
+ padding: 8px 12px;
926
+ border-radius: 8px;
927
+ border: 1px solid #ede9fe;
928
+ background: #f5f3ff;
929
+ color: #7c3aed;
930
+ font-size: 11px;
931
+ }
932
+
933
+ /* ── AG-UI Events ────────────────────────────────────────────────── */
934
+ .cpk-td__event {
935
+ flex-shrink: 0;
936
+ border: 1px solid #e9e9ef;
937
+ border-radius: 6px;
938
+ overflow: hidden;
939
+ /*
940
+ * content-visibility: auto lets the browser skip layout + paint for
941
+ * off-screen events while keeping them in the DOM (so scroll size
942
+ * stays correct). Without this, switching back to AG-UI Events on a
943
+ * thread with hundreds of events triggers a full layout pass over
944
+ * every event row, which on Martha's intelligence-backed example
945
+ * shows up as a multi-second freeze each time the panel becomes
946
+ * visible. The intrinsic-size hint avoids the visible jump as the
947
+ * browser swaps in real heights when items scroll into view.
948
+ */
949
+ content-visibility: auto;
950
+ contain-intrinsic-size: 0 80px;
951
+ }
952
+
953
+ .cpk-td__event-header {
954
+ display: flex;
955
+ justify-content: space-between;
956
+ align-items: center;
957
+ padding: 5px 10px;
958
+ }
959
+
960
+ .cpk-td__event-type {
961
+ font-family: "Spline Sans Mono", monospace;
962
+ font-size: 9px;
963
+ font-weight: 500;
964
+ text-transform: uppercase;
965
+ }
966
+
967
+ .cpk-td__event-time {
968
+ font-family: "Spline Sans Mono", monospace;
969
+ font-size: 9px;
970
+ color: #838389;
971
+ }
972
+
973
+ .cpk-td__event-payload {
974
+ margin: 0;
975
+ font-family: "Spline Sans Mono", monospace;
976
+ font-size: 10px;
977
+ line-height: 1.6;
978
+ white-space: pre-wrap;
979
+ word-break: break-all;
980
+ color: #57575b;
981
+ padding: 8px 10px;
982
+ border-top: 1px solid #e9e9ef;
983
+ }
984
+
985
+ /* ── JSON block (agent state) ────────────────────────────────────── */
986
+ .cpk-td__json-block {
987
+ margin: 0;
988
+ font-family: "Spline Sans Mono", monospace;
989
+ font-size: 11px;
990
+ line-height: 1.8;
991
+ white-space: pre-wrap;
992
+ word-break: break-all;
993
+ color: #57575b;
994
+ }
995
+
996
+ /* ── Resize divider ──────────────────────────────────────────────── */
997
+ /* Floats over the drawer's left edge so the toggle and the drawer
998
+ touch directly without a 4px flex-gap between them. The hit zone
999
+ is wider than its visual hint to make it easy to grab. */
1000
+ .cpk-td__detail-divider {
1001
+ position: absolute;
1002
+ top: 0;
1003
+ bottom: 0;
1004
+ left: -3px;
1005
+ width: 7px;
1006
+ cursor: col-resize;
1007
+ background: transparent;
1008
+ z-index: 5;
1009
+ }
1010
+
1011
+ .cpk-td__detail-divider:hover {
1012
+ background: rgba(190, 194, 255, 0.3);
1013
+ }
1014
+
1015
+ /* ── Right detail panel ──────────────────────────────────────────── */
1016
+ .cpk-td__detail {
1017
+ flex-shrink: 0;
1018
+ overflow: hidden;
1019
+ background: #f7f7f9;
1020
+ display: flex;
1021
+ flex-direction: column;
1022
+ gap: 0;
1023
+ padding: 0;
1024
+ box-sizing: border-box;
1025
+ position: relative;
1026
+ /* Slide open/closed via width + padding transition. When closed,
1027
+ width and padding are 0 so the drawer fully collapses. */
1028
+ transition:
1029
+ width 220ms cubic-bezier(0.4, 0, 0.2, 1),
1030
+ padding 220ms cubic-bezier(0.4, 0, 0.2, 1);
1031
+ }
1032
+
1033
+ .cpk-td__detail[data-open="true"] {
1034
+ overflow-y: auto;
1035
+ padding: 16px;
1036
+ }
1037
+
1038
+ .cpk-tdp__section-title {
1039
+ font-family: "Spline Sans Mono", monospace;
1040
+ font-size: 10px;
1041
+ font-weight: 500;
1042
+ color: #838389;
1043
+ text-transform: uppercase;
1044
+ letter-spacing: 0.6px;
1045
+ margin-bottom: 8px;
1046
+ }
1047
+
1048
+ .cpk-tdp__divider {
1049
+ height: 1px;
1050
+ background: #dbdbe5;
1051
+ margin: 14px 0;
1052
+ }
1053
+
1054
+ .cpk-tdp__row {
1055
+ display: flex;
1056
+ justify-content: space-between;
1057
+ align-items: flex-start;
1058
+ padding: 3px 0;
1059
+ gap: 8px;
1060
+ }
1061
+
1062
+ .cpk-tdp__label {
1063
+ color: #838389;
1064
+ font-size: 11px;
1065
+ white-space: nowrap;
1066
+ flex-shrink: 0;
1067
+ }
1068
+
1069
+ .cpk-tdp__value {
1070
+ color: #010507;
1071
+ font-family: "Spline Sans Mono", monospace;
1072
+ font-size: 11px;
1073
+ text-align: right;
1074
+ min-width: 0;
1075
+ }
1076
+
1077
+ .cpk-tdp__value--truncate {
1078
+ overflow: hidden;
1079
+ text-overflow: ellipsis;
1080
+ white-space: nowrap;
1081
+ max-width: 130px;
1082
+ }
1083
+
1084
+ .cpk-tdp__value--wrap {
1085
+ white-space: normal;
1086
+ word-break: break-all;
1087
+ text-align: right;
1088
+ }
1089
+ `;
1090
+ }
1091
+ updated(_changed) {
1092
+ if (this.threadId !== this._lastFetchedThreadId) {
1093
+ this._lastFetchedThreadId = this.threadId;
1094
+ this._lastSeenLiveMessageVersion = this.liveMessageVersion;
1095
+ this._tab = "conversation";
1096
+ this._activatedTabs = new Set(["conversation"]);
1097
+ this._panelTplCache = /* @__PURE__ */ new Map();
1098
+ this._expandedTools = /* @__PURE__ */ new Set();
1099
+ this._expandedMessages = /* @__PURE__ */ new Set();
1100
+ this._messagesAbort?.abort();
1101
+ this._messagesAbort = null;
1102
+ this._eventsAbort?.abort();
1103
+ this._eventsAbort = null;
1104
+ this._stateAbort?.abort();
1105
+ this._stateAbort = null;
1106
+ this._eventsFetched = false;
1107
+ this._stateFetched = false;
1108
+ this._fetchedEvents = null;
1109
+ this._fetchedState = null;
1110
+ if (this.threadId) this.fetchMessages(this.threadId);
1111
+ else this._conversation = [];
1112
+ } else if (this.threadId && this.liveMessageVersion !== this._lastSeenLiveMessageVersion) {
1113
+ this._lastSeenLiveMessageVersion = this.liveMessageVersion;
1114
+ this._messagesAbort?.abort();
1115
+ this._messagesAbort = null;
1116
+ this.fetchMessages(this.threadId, true);
1117
+ }
1118
+ }
1119
+ /**
1120
+ * Fetch the canonical conversation for `threadId` from the runtime.
1121
+ *
1122
+ * `silent` is true for live re-fetches triggered by `liveMessageVersion`
1123
+ * bumps during streaming. In that mode we never toggle the loading state
1124
+ * (which would flash "Loading messages…" between every message) and we
1125
+ * keep the previous conversation on transient errors instead of blanking
1126
+ * it. Initial threadId-change fetches use the default (`silent=false`)
1127
+ * so users see an explicit loading indicator on first load.
1128
+ */
1129
+ async fetchMessages(threadId, silent = false) {
1130
+ if (!this.runtimeUrl) {
1131
+ if (!silent) this._conversation = [];
1132
+ return;
1133
+ }
1134
+ const controller = new AbortController();
1135
+ this._messagesAbort = controller;
1136
+ if (!silent) {
1137
+ this._loadingMessages = true;
1138
+ this._messagesError = null;
1139
+ }
1140
+ try {
1141
+ const res = await fetch(`${this.runtimeUrl}/threads/${encodeURIComponent(threadId)}/messages`, {
1142
+ headers: { ...this.headers },
1143
+ signal: controller.signal
1144
+ });
1145
+ if (controller.signal.aborted || this.threadId !== threadId) return;
1146
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
1147
+ const data = await res.json();
1148
+ if (controller.signal.aborted || this.threadId !== threadId) return;
1149
+ this._conversation = this.mapMessages(data.messages);
1150
+ } catch (err) {
1151
+ if (err instanceof Error && err.name === "AbortError") return;
1152
+ if (!silent) {
1153
+ this._messagesError = err instanceof Error ? err.message : "Failed to load messages";
1154
+ this._conversation = [];
1155
+ }
1156
+ } finally {
1157
+ if (!silent && !controller.signal.aborted) this._loadingMessages = false;
1158
+ }
1159
+ }
1160
+ async fetchEvents(threadId) {
1161
+ this._eventsNotAvailable = false;
1162
+ if (!this.runtimeUrl) {
1163
+ this._fetchedEvents = null;
1164
+ return;
1165
+ }
1166
+ const controller = new AbortController();
1167
+ this._eventsAbort = controller;
1168
+ this._loadingEvents = true;
1169
+ this._eventsError = null;
1170
+ try {
1171
+ const res = await fetch(`${this.runtimeUrl}/threads/${encodeURIComponent(threadId)}/events`, {
1172
+ headers: { ...this.headers },
1173
+ signal: controller.signal
1174
+ });
1175
+ if (controller.signal.aborted || this.threadId !== threadId) return;
1176
+ if (res.status === 501) {
1177
+ this._eventsNotAvailable = true;
1178
+ this._fetchedEvents = null;
1179
+ return;
1180
+ }
1181
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
1182
+ const data = await res.json();
1183
+ if (controller.signal.aborted || this.threadId !== threadId) return;
1184
+ this._fetchedEvents = this.mapApiEvents(data.events);
1185
+ } catch (err) {
1186
+ if (err instanceof Error && err.name === "AbortError") return;
1187
+ if (this.threadId !== threadId) return;
1188
+ this._eventsError = err instanceof Error ? err.message : "Failed to load events";
1189
+ this._fetchedEvents = [];
1190
+ } finally {
1191
+ if (!controller.signal.aborted && this.threadId === threadId) this._loadingEvents = false;
1192
+ }
1193
+ }
1194
+ async fetchState(threadId) {
1195
+ this._stateNotAvailable = false;
1196
+ if (!this.runtimeUrl) {
1197
+ this._fetchedState = null;
1198
+ return;
1199
+ }
1200
+ const controller = new AbortController();
1201
+ this._stateAbort = controller;
1202
+ this._loadingState = true;
1203
+ this._stateError = null;
1204
+ try {
1205
+ const res = await fetch(`${this.runtimeUrl}/threads/${encodeURIComponent(threadId)}/state`, {
1206
+ headers: { ...this.headers },
1207
+ signal: controller.signal
1208
+ });
1209
+ if (controller.signal.aborted || this.threadId !== threadId) return;
1210
+ if (res.status === 501) {
1211
+ this._stateNotAvailable = true;
1212
+ this._fetchedState = null;
1213
+ return;
1214
+ }
1215
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
1216
+ const data = await res.json();
1217
+ if (controller.signal.aborted || this.threadId !== threadId) return;
1218
+ this._fetchedState = data.state ?? null;
1219
+ } catch (err) {
1220
+ if (err instanceof Error && err.name === "AbortError") return;
1221
+ if (this.threadId !== threadId) return;
1222
+ this._stateError = err instanceof Error ? err.message : "Failed to load state";
1223
+ this._fetchedState = null;
1224
+ } finally {
1225
+ if (!controller.signal.aborted && this.threadId === threadId) this._loadingState = false;
1226
+ }
1227
+ }
1228
+ mapMessages(messages) {
1229
+ const items = [];
1230
+ const toolCallMap = /* @__PURE__ */ new Map();
1231
+ for (const msg of messages) if (msg.role === "user" && msg.content) items.push({
1232
+ id: msg.id,
1233
+ type: "user",
1234
+ content: msg.content,
1235
+ createdAt: ""
1236
+ });
1237
+ else if (msg.role === "assistant") {
1238
+ if (msg.toolCalls?.length) for (const tc of msg.toolCalls) {
1239
+ let args = {};
1240
+ try {
1241
+ args = JSON.parse(tc.args);
1242
+ } catch (err) {
1243
+ console.error("[CopilotKit Inspector] Failed to parse tool-call arguments", {
1244
+ toolCallId: tc.id,
1245
+ raw: tc.args,
1246
+ error: err
1247
+ });
1248
+ args = {
1249
+ __parseError: true,
1250
+ __raw: tc.args
1251
+ };
1252
+ }
1253
+ const item = {
1254
+ id: tc.id,
1255
+ type: "tool_call",
1256
+ toolName: tc.name,
1257
+ toolCallId: tc.id,
1258
+ arguments: args,
1259
+ result: null,
1260
+ createdAt: ""
1261
+ };
1262
+ toolCallMap.set(tc.id, item);
1263
+ items.push(item);
1264
+ }
1265
+ if (msg.content) items.push({
1266
+ id: msg.id,
1267
+ type: "assistant",
1268
+ content: msg.content,
1269
+ createdAt: ""
1270
+ });
1271
+ } else if (msg.role === "activity") items.push({
1272
+ id: msg.id,
1273
+ type: "generative-ui",
1274
+ activityType: msg.activityType ?? "unknown",
1275
+ createdAt: ""
1276
+ });
1277
+ else if (msg.role === "tool" && msg.toolCallId) {
1278
+ const tc = toolCallMap.get(msg.toolCallId);
1279
+ if (tc) try {
1280
+ tc.result = JSON.parse(msg.content ?? "{}");
1281
+ } catch (err) {
1282
+ console.error("[CopilotKit Inspector] Failed to parse tool-call result content", {
1283
+ toolCallId: msg.toolCallId,
1284
+ raw: msg.content,
1285
+ error: err
1286
+ });
1287
+ tc.result = {
1288
+ __parseError: true,
1289
+ __raw: msg.content ?? null
1290
+ };
1291
+ }
1292
+ }
1293
+ return items;
1294
+ }
1295
+ mapApiEvents(events) {
1296
+ return events.map((event) => {
1297
+ const { type, timestamp, ...rest } = event;
1298
+ return {
1299
+ type: typeof type === "string" ? type : "UNKNOWN",
1300
+ timestamp: typeof timestamp === "string" || typeof timestamp === "number" ? timestamp : Date.now(),
1301
+ payload: rest
1302
+ };
1303
+ });
1304
+ }
1305
+ get renderItems() {
1306
+ const items = this._conversation;
1307
+ const result = [];
1308
+ const seen = /* @__PURE__ */ new Set();
1309
+ for (const item of items) {
1310
+ if (item.type === "agent_responded") continue;
1311
+ if (item.type !== "tool_call" || !item.groupId) {
1312
+ result.push(item);
1313
+ continue;
1314
+ }
1315
+ if (seen.has(item.groupId)) continue;
1316
+ seen.add(item.groupId);
1317
+ const group = {
1318
+ type: "tool_call_group",
1319
+ id: item.groupId,
1320
+ items: items.filter((i) => i.type === "tool_call" && i.groupId === item.groupId)
1321
+ };
1322
+ result.push(group);
1323
+ }
1324
+ return result;
1325
+ }
1326
+ get activityCounts() {
1327
+ let messages = 0;
1328
+ let toolCalls = 0;
1329
+ let generativeUi = 0;
1330
+ for (const item of this._conversation) {
1331
+ if (item.type === "user" || item.type === "assistant") messages++;
1332
+ if (item.type === "tool_call") toolCalls++;
1333
+ if (item.type === "generative-ui") generativeUi++;
1334
+ }
1335
+ return {
1336
+ messages,
1337
+ toolCalls,
1338
+ generativeUi
1339
+ };
1340
+ }
1341
+ get duration() {
1342
+ const t = this.thread;
1343
+ if (!t?.createdAt || !t?.updatedAt) return "—";
1344
+ const ms = new Date(t.updatedAt).getTime() - new Date(t.createdAt).getTime();
1345
+ if (ms < 0) return "—";
1346
+ if (ms < 1e3) return `${ms}ms`;
1347
+ const s = Math.floor(ms / 1e3);
1348
+ if (s < 60) return `${s}s`;
1349
+ return `${Math.floor(s / 60)}m ${s % 60}s`;
1350
+ }
1351
+ toggleToolExpand(id) {
1352
+ const next = new Set(this._expandedTools);
1353
+ if (next.has(id)) next.delete(id);
1354
+ else next.add(id);
1355
+ this._expandedTools = next;
1356
+ }
1357
+ toggleMessageExpand(id) {
1358
+ const next = new Set(this._expandedMessages);
1359
+ if (next.has(id)) next.delete(id);
1360
+ else next.add(id);
1361
+ this._expandedMessages = next;
1362
+ }
1363
+ get activeEvents() {
1364
+ if (this._eventsNotAvailable) return [];
1365
+ return this._fetchedEvents ?? this.agentEventsInput ?? [];
1366
+ }
1367
+ get activeState() {
1368
+ if (this._stateNotAvailable) return null;
1369
+ return this._fetchedState ?? this.agentStateInput ?? null;
1370
+ }
1371
+ hasRenderableState() {
1372
+ const s = this.activeState;
1373
+ return !!s && typeof s === "object" && Object.keys(s).length > 0;
1374
+ }
1375
+ shortId(id) {
1376
+ if (!id) return "—";
1377
+ return id.length > 20 ? id.slice(0, 8) + "…" : id;
1378
+ }
1379
+ fmtTime(dateStr) {
1380
+ if (!dateStr) return "—";
1381
+ const d = new Date(dateStr);
1382
+ if (Number.isNaN(d.getTime())) return "—";
1383
+ return d.toLocaleTimeString("en-US", {
1384
+ hour: "2-digit",
1385
+ minute: "2-digit",
1386
+ second: "2-digit",
1387
+ hour12: false
1388
+ });
1389
+ }
1390
+ render() {
1391
+ return lit.html`
1392
+ <div class="cpk-td">
1393
+ <!-- ── Left area: tabs + content ─────────────────────────────────── -->
1394
+ <div class="cpk-td__left">
1395
+ <!-- Tab bar -->
1396
+ <div class="cpk-td__tabs-header">
1397
+ <div class="cpk-td__tab-group" role="tablist">
1398
+ ${ɵCpkThreadDetails.TAB_LIST.map((tab) => lit.html`
1399
+ <button
1400
+ role="tab"
1401
+ class="cpk-td__tab ${this._tab === tab.id ? "cpk-td__tab--active" : ""}"
1402
+ @click=${() => this.activateTab(tab.id)}
1403
+ >
1404
+ ${tab.label}
1405
+ </button>
1406
+ `)}
1407
+ </div>
1408
+ ${this.renderPanelToggle()}
1409
+ </div>
1410
+
1411
+ <!-- Scrollable content -->
1412
+ <div class="cpk-td__content">
1413
+ ${this._panelInitializing ? lit.html`
1414
+ <div class="cpk-td__status">Loading…</div>
1415
+ ` : lit.nothing}
1416
+ ${ɵCpkThreadDetails.TAB_LIST.map((tab) => this._activatedTabs.has(tab.id) ? lit.html`<div
1417
+ class="cpk-td__panel"
1418
+ style=${this._tab === tab.id && !this._panelInitializing ? "" : "display:none"}
1419
+ >
1420
+ ${this.renderTabContent(tab.id)}
1421
+ </div>` : lit.nothing)}
1422
+ </div>
1423
+ </div>
1424
+
1425
+ <!--
1426
+ Drawer always rendered so width animates between 0 and its
1427
+ target. Divider lives INSIDE the drawer and is absolutely
1428
+ positioned over its left edge so the toggle (rightmost of the
1429
+ tab row) and the drawer touch with no flex-gap between them.
1430
+ -->
1431
+ <div
1432
+ class="cpk-td__detail"
1433
+ data-open=${this._showDetailPanel ? "true" : "false"}
1434
+ style="width:${this._showDetailPanel ? this._detailPanelWidth : 0}px"
1435
+ aria-hidden=${this._showDetailPanel ? "false" : "true"}
1436
+ >
1437
+ ${this._showDetailPanel ? lit.html`
1438
+ <div
1439
+ class="cpk-td__detail-divider"
1440
+ @pointerdown=${this.onDetailDividerDown}
1441
+ @pointermove=${this.onDetailDividerMove}
1442
+ @pointerup=${this.onDetailDividerUp}
1443
+ @pointercancel=${this.onDetailDividerUp}
1444
+ ></div>
1445
+ ` : lit.nothing}
1446
+ ${this.renderDetailPanel()}
1447
+ </div>
1448
+ </div>
1449
+ `;
1450
+ }
1451
+ renderConversation() {
1452
+ if (this._loadingMessages) return lit.html`
1453
+ <div class="cpk-td__status">Loading messages…</div>
1454
+ `;
1455
+ if (this._messagesError) return lit.html`<div class="cpk-td__status cpk-td__status--error">
1456
+ ${this._messagesError}
1457
+ </div>`;
1458
+ if (this._conversation.length === 0) return lit.html`
1459
+ <div class="cpk-td__empty-state">
1460
+ <svg
1461
+ width="28"
1462
+ height="28"
1463
+ viewBox="0 0 24 24"
1464
+ fill="none"
1465
+ stroke="currentColor"
1466
+ stroke-width="1.5"
1467
+ stroke-linecap="round"
1468
+ stroke-linejoin="round"
1469
+ >
1470
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
1471
+ </svg>
1472
+ <span>No messages yet</span>
1473
+ </div>
1474
+ `;
1475
+ return this.cachedPanelTpl("conversation", [
1476
+ this._conversation,
1477
+ this._expandedTools,
1478
+ this._expandedMessages
1479
+ ], () => {
1480
+ return lit.html`${this.renderItems.map((item) => this.renderRenderItem(item))}`;
1481
+ });
1482
+ }
1483
+ /**
1484
+ * Memoize the rendered TemplateResult for `slot` keyed by tuple
1485
+ * element-wise reference equality. The hot path for tab switches: when
1486
+ * the underlying data hasn't changed, return the previously built
1487
+ * TemplateResult so Lit's diff short-circuits. Each panel's `key` is
1488
+ * the tuple of inputs the template reads — pass everything the template
1489
+ * depends on, or the cache will return stale output when those inputs
1490
+ * change without the listed key flipping.
1491
+ */
1492
+ cachedPanelTpl(slot, key, build) {
1493
+ const cached = this._panelTplCache.get(slot);
1494
+ if (cached && cached.key.length === key.length && cached.key.every((v, i) => v === key[i])) return cached.tpl;
1495
+ const tpl = build();
1496
+ this._panelTplCache.set(slot, {
1497
+ key,
1498
+ tpl
1499
+ });
1500
+ return tpl;
1501
+ }
1502
+ renderRenderItem(item) {
1503
+ switch (item.type) {
1504
+ case "user":
1505
+ case "assistant": return this.renderBubble(item);
1506
+ case "tool_call": return this.renderToolBlock(item);
1507
+ case "tool_call_group": return this.renderToolGroup(item);
1508
+ case "reasoning": return lit.html`<div class="cpk-td__inline-chip">
1509
+ <span>Reasoned for ${item.duration}</span>
1510
+ </div>`;
1511
+ case "state_update": return lit.html`
1512
+ <div class="cpk-td__inline-chip">
1513
+ <span>Updated agent state</span>
1514
+ </div>
1515
+ `;
1516
+ case "generative-ui": return this.renderGenerativeUI(item);
1517
+ case "agent_responded": return lit.nothing;
1518
+ }
1519
+ }
1520
+ renderBubble(item) {
1521
+ const isUser = item.type === "user";
1522
+ const threshold = ɵCpkThreadDetails.COLLAPSE_THRESHOLD;
1523
+ const expanded = this._expandedMessages.has(item.id);
1524
+ const tooLong = item.content.length > threshold;
1525
+ const shown = tooLong && !expanded ? item.content.slice(0, threshold) + "…" : item.content;
1526
+ return lit.html`
1527
+ <div
1528
+ class="cpk-td__bubble ${isUser ? "cpk-td__bubble--user" : "cpk-td__bubble--assistant"}"
1529
+ >
1530
+ <div
1531
+ class="cpk-td__bubble-inner ${isUser ? "cpk-td__bubble-inner--user" : "cpk-td__bubble-inner--assistant"}"
1532
+ >
1533
+ ${shown}
1534
+ ${tooLong ? lit.html`<span
1535
+ class="cpk-td__show-more"
1536
+ @click=${() => this.toggleMessageExpand(item.id)}
1537
+ >${expanded ? "Show less" : "Show more"}</span
1538
+ >` : lit.nothing}
1539
+ </div>
1540
+ </div>
1541
+ `;
1542
+ }
1543
+ renderToolBlock(item) {
1544
+ const expanded = this._expandedTools.has(item.id);
1545
+ return lit.html`
1546
+ <div class="cpk-td__tool-block">
1547
+ <div
1548
+ class="cpk-td__tool-header"
1549
+ @click=${() => this.toggleToolExpand(item.id)}
1550
+ >
1551
+ <svg width="10" height="10" viewBox="0 0 10 10" fill="none">
1552
+ <path
1553
+ d="M1 9C1 9 2 7 5 7C8 7 9 9 9 9M5 1C5 1 7 2.5 7 4.5C7 6.5 5 7 5 7C5 7 3 6.5 3 4.5C3 2.5 5 1 5 1Z"
1554
+ stroke="#189370"
1555
+ stroke-width="1.2"
1556
+ stroke-linecap="round"
1557
+ stroke-linejoin="round"
1558
+ />
1559
+ </svg>
1560
+ <span class="cpk-td__tool-name">${item.toolName}</span>
1561
+ ${item.result || Object.keys(item.arguments).length > 0 ? lit.html`
1562
+ <span class="cpk-td__tool-status">DONE</span>
1563
+ ` : lit.html`
1564
+ <span class="cpk-td__tool-status cpk-td__tool-status--pending">PENDING</span>
1565
+ `}
1566
+ <span class="cpk-td__tool-chevron">${expanded ? "▾" : "▸"}</span>
1567
+ </div>
1568
+ ${expanded ? lit.html`
1569
+ <div class="cpk-td__tool-body">
1570
+ <div class="cpk-td__tool-section-label">Arguments</div>
1571
+ <pre class="cpk-td__tool-pre">
1572
+ ${(0, lit_directives_unsafe_html_js.unsafeHTML)(highlightedJson(item.arguments))}</pre
1573
+ >
1574
+ ${item.result ? lit.html`
1575
+ <div
1576
+ class="cpk-td__tool-section-label"
1577
+ style="margin-top:8px"
1578
+ >
1579
+ Result
1580
+ </div>
1581
+ <pre class="cpk-td__tool-pre">
1582
+ ${(0, lit_directives_unsafe_html_js.unsafeHTML)(highlightedJson(item.result))}</pre
1583
+ >
1584
+ ` : lit.nothing}
1585
+ </div>
1586
+ ` : lit.nothing}
1587
+ </div>
1588
+ `;
1589
+ }
1590
+ renderToolGroup(group) {
1591
+ return lit.html`
1592
+ <div class="cpk-td__tool-group">
1593
+ <div class="cpk-td__tool-group-header">
1594
+ ${group.items.length} tool call${group.items.length !== 1 ? "s" : ""}
1595
+ </div>
1596
+ ${group.items.map((tc) => this.renderToolBlock(tc))}
1597
+ </div>
1598
+ `;
1599
+ }
1600
+ renderGenerativeUI(item) {
1601
+ return lit.html`
1602
+ <div class="cpk-td__genui">
1603
+ <div class="cpk-td__genui-badge">
1604
+ <svg width="9" height="9" viewBox="0 0 24 24" fill="currentColor">
1605
+ <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" />
1606
+ </svg>
1607
+ Generative UI
1608
+ </div>
1609
+ <div class="cpk-td__genui-placeholder">
1610
+ ${item.activityType} — rendered in chat
1611
+ </div>
1612
+ </div>
1613
+ `;
1614
+ }
1615
+ renderState() {
1616
+ if (this._loadingState) return lit.html`
1617
+ <div class="cpk-td__status">Loading state…</div>
1618
+ `;
1619
+ if (this._stateError) return lit.html`<div class="cpk-td__status cpk-td__status--error">
1620
+ ${this._stateError}
1621
+ </div>`;
1622
+ if (this._stateNotAvailable) return lit.html`
1623
+ <div class="cpk-td__empty-state">
1624
+ <svg
1625
+ width="28"
1626
+ height="28"
1627
+ viewBox="0 0 24 24"
1628
+ fill="none"
1629
+ stroke="currentColor"
1630
+ stroke-width="1.5"
1631
+ stroke-linecap="round"
1632
+ stroke-linejoin="round"
1633
+ >
1634
+ <ellipse cx="12" cy="5" rx="9" ry="3" />
1635
+ <path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
1636
+ <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
1637
+ </svg>
1638
+ <span>State history not available</span>
1639
+ <span class="cpk-td__empty-hint"
1640
+ >This runtime doesn't yet expose per-thread agent state. Available when
1641
+ running against the in-memory runner.</span
1642
+ >
1643
+ </div>
1644
+ `;
1645
+ if (!this.hasRenderableState()) return lit.html`
1646
+ <div class="cpk-td__empty-state">
1647
+ <svg
1648
+ width="28"
1649
+ height="28"
1650
+ viewBox="0 0 24 24"
1651
+ fill="none"
1652
+ stroke="currentColor"
1653
+ stroke-width="1.5"
1654
+ stroke-linecap="round"
1655
+ stroke-linejoin="round"
1656
+ >
1657
+ <ellipse cx="12" cy="5" rx="9" ry="3" />
1658
+ <path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
1659
+ <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
1660
+ </svg>
1661
+ <span>No state captured</span>
1662
+ <span class="cpk-td__empty-hint"
1663
+ >Emitted live from STATE_SNAPSHOT events.</span
1664
+ >
1665
+ </div>
1666
+ `;
1667
+ const stateValue = this.activeState;
1668
+ return this.cachedPanelTpl("agent-state", [stateValue], () => {
1669
+ return lit.html`<pre class="cpk-td__json-block">
1670
+ ${(0, lit_directives_unsafe_html_js.unsafeHTML)(highlightedJson(stateValue))}</pre
1671
+ >`;
1672
+ });
1673
+ }
1674
+ renderEvents() {
1675
+ if (this._loadingEvents) return lit.html`
1676
+ <div class="cpk-td__status">Loading events…</div>
1677
+ `;
1678
+ if (this._eventsError) return lit.html`<div class="cpk-td__status cpk-td__status--error">
1679
+ ${this._eventsError}
1680
+ </div>`;
1681
+ if (this._eventsNotAvailable) return lit.html`
1682
+ <div class="cpk-td__empty-state">
1683
+ <span>Event history not available</span>
1684
+ <span class="cpk-td__empty-hint"
1685
+ >This runtime doesn't yet expose per-thread AG-UI events. Available when
1686
+ running against the in-memory runner.</span
1687
+ >
1688
+ </div>
1689
+ `;
1690
+ const events = this.activeEvents;
1691
+ if (events.length === 0) return lit.html`
1692
+ <div class="cpk-td__empty-state">
1693
+ <span>No events captured</span>
1694
+ <span class="cpk-td__empty-hint"
1695
+ >Events are recorded live. Run the agent to see them here.</span
1696
+ >
1697
+ </div>
1698
+ `;
1699
+ return this.cachedPanelTpl("ag-ui-events", [events], () => {
1700
+ return lit.html`${events.map((event) => {
1701
+ const { bg, fg } = eventColors(event.type);
1702
+ return lit.html`
1703
+ <div class="cpk-td__event">
1704
+ <div class="cpk-td__event-header" style="background:${bg}">
1705
+ <span class="cpk-td__event-type" style="color:${fg}"
1706
+ >${event.type}</span
1707
+ >
1708
+ <span class="cpk-td__event-time"
1709
+ >${formatTimestamp(event.timestamp)}</span
1710
+ >
1711
+ </div>
1712
+ <pre class="cpk-td__event-payload">
1713
+ ${(0, lit_directives_unsafe_html_js.unsafeHTML)(highlightedJson(event.payload))}</pre
1714
+ >
1715
+ </div>
1716
+ `;
1717
+ })}`;
1718
+ });
1719
+ }
1720
+ renderPanelToggle() {
1721
+ return lit.html`
1722
+ <button
1723
+ class="cpk-td__panel-toggle ${this._showDetailPanel ? "cpk-td__panel-toggle--active" : ""}"
1724
+ @click=${() => {
1725
+ this._showDetailPanel = !this._showDetailPanel;
1726
+ }}
1727
+ title="Toggle thread details"
1728
+ type="button"
1729
+ >
1730
+ <svg
1731
+ width="14"
1732
+ height="14"
1733
+ viewBox="0 0 24 24"
1734
+ fill="none"
1735
+ stroke="currentColor"
1736
+ stroke-width="2"
1737
+ stroke-linecap="round"
1738
+ stroke-linejoin="round"
1739
+ >
1740
+ <rect x="3" y="3" width="18" height="18" rx="2" />
1741
+ <line x1="15" y1="3" x2="15" y2="21" />
1742
+ </svg>
1743
+ </button>
1744
+ `;
1745
+ }
1746
+ renderDetailPanel() {
1747
+ const counts = this.activityCounts;
1748
+ return lit.html`
1749
+ <!-- Thread -->
1750
+ <div class="cpk-tdp__section-title">Thread</div>
1751
+ <div class="cpk-tdp__row">
1752
+ <span class="cpk-tdp__label">ID</span>
1753
+ <span class="cpk-tdp__value cpk-tdp__value--wrap"
1754
+ >${this.shortId(this.thread?.id)}</span
1755
+ >
1756
+ </div>
1757
+ <div class="cpk-tdp__row">
1758
+ <span class="cpk-tdp__label">Name</span>
1759
+ <span class="cpk-tdp__value">${this.thread?.name ?? "—"}</span>
1760
+ </div>
1761
+ <div class="cpk-tdp__row">
1762
+ <span class="cpk-tdp__label">Agent</span>
1763
+ <span class="cpk-tdp__value cpk-tdp__value--truncate"
1764
+ >${this.thread?.agentId ?? "—"}</span
1765
+ >
1766
+ </div>
1767
+ <div class="cpk-tdp__row">
1768
+ <span class="cpk-tdp__label">Created by</span>
1769
+ <span class="cpk-tdp__value cpk-tdp__value--truncate"
1770
+ >${this.thread?.createdById ?? "—"}</span
1771
+ >
1772
+ </div>
1773
+
1774
+ <div class="cpk-tdp__divider"></div>
1775
+
1776
+ <!-- Timestamps -->
1777
+ <div class="cpk-tdp__section-title">Timestamps</div>
1778
+ <div class="cpk-tdp__row">
1779
+ <span class="cpk-tdp__label">Created</span>
1780
+ <span class="cpk-tdp__value">${this.fmtTime(this.thread?.createdAt)}</span>
1781
+ </div>
1782
+ <div class="cpk-tdp__row">
1783
+ <span class="cpk-tdp__label">Updated</span>
1784
+ <span class="cpk-tdp__value">${this.fmtTime(this.thread?.updatedAt)}</span>
1785
+ </div>
1786
+ <div class="cpk-tdp__row">
1787
+ <span class="cpk-tdp__label">Duration</span>
1788
+ <span class="cpk-tdp__value">${this.duration}</span>
1789
+ </div>
1790
+
1791
+ <div class="cpk-tdp__divider"></div>
1792
+
1793
+ <!-- Activity -->
1794
+ <div class="cpk-tdp__section-title">Activity</div>
1795
+ <div class="cpk-tdp__row">
1796
+ <span class="cpk-tdp__label">Messages</span>
1797
+ <span class="cpk-tdp__value">${counts.messages}</span>
1798
+ </div>
1799
+ <div class="cpk-tdp__row">
1800
+ <span class="cpk-tdp__label">Tool calls</span>
1801
+ <span class="cpk-tdp__value">${counts.toolCalls}</span>
1802
+ </div>
1803
+ <div class="cpk-tdp__row">
1804
+ <span class="cpk-tdp__label">AG-UI events</span>
1805
+ <span class="cpk-tdp__value">${this.activeEvents.length}</span>
1806
+ </div>
1807
+ `;
1808
+ }
1809
+ };
1810
+ if (!customElements.get("cpk-thread-list")) customElements.define("cpk-thread-list", CpkThreadList);
1811
+ if (!customElements.get("cpk-thread-details")) customElements.define("cpk-thread-details", ɵCpkThreadDetails);
1812
+ var WebInspectorElement = class WebInspectorElement extends lit.LitElement {
1813
+ constructor(..._args3) {
1814
+ super(..._args3);
61
1815
  this._core = null;
62
1816
  this.coreSubscriber = null;
63
1817
  this.coreUnsubscribe = null;
@@ -67,6 +1821,8 @@ var WebInspectorElement = class extends lit.LitElement {
67
1821
  this.agentSubscriptions = /* @__PURE__ */ new Map();
68
1822
  this.agentEvents = /* @__PURE__ */ new Map();
69
1823
  this.agentMessages = /* @__PURE__ */ new Map();
1824
+ this.agentRunThreadId = /* @__PURE__ */ new Map();
1825
+ this.liveMessageVersion = /* @__PURE__ */ new Map();
70
1826
  this.agentStates = /* @__PURE__ */ new Map();
71
1827
  this.flattenedEvents = [];
72
1828
  this.eventCounter = 0;
@@ -83,6 +1839,17 @@ var WebInspectorElement = class extends lit.LitElement {
83
1839
  this.draggedDuringInteraction = false;
84
1840
  this.ignoreNextButtonClick = false;
85
1841
  this.selectedMenu = "ag-ui-events";
1842
+ this.selectedThreadId = null;
1843
+ this.threadListWidth = 290;
1844
+ this.threadDividerResizing = false;
1845
+ this.threadDividerPointerId = -1;
1846
+ this.threadDividerStartX = 0;
1847
+ this.threadDividerStartWidth = 0;
1848
+ this._threads = [];
1849
+ this._threadStoreSubscriptions = /* @__PURE__ */ new Map();
1850
+ this._threadsByAgent = /* @__PURE__ */ new Map();
1851
+ this._threadsErrorByAgent = /* @__PURE__ */ new Map();
1852
+ this._ownedThreadStores = /* @__PURE__ */ new Map();
86
1853
  this.contextMenuOpen = false;
87
1854
  this.dockMode = "floating";
88
1855
  this.previousBodyMargins = null;
@@ -95,15 +1862,26 @@ var WebInspectorElement = class extends lit.LitElement {
95
1862
  this.toolSignature = "";
96
1863
  this.eventFilterText = "";
97
1864
  this.eventTypeFilter = "all";
98
- this.announcementMarkdown = null;
1865
+ this.evtColWidths = [
1866
+ 100,
1867
+ 80,
1868
+ 150
1869
+ ];
1870
+ this._evtColResize = null;
1871
+ this._threadsUnlocked = false;
1872
+ this._threadsUnlocking = false;
1873
+ this._threadsGateError = null;
1874
+ this._threadsGateCodeInvalid = false;
1875
+ this._threadsGateInvalidTimer = null;
1876
+ this._threadsUnlockingTimer = null;
99
1877
  this.announcementHtml = null;
100
1878
  this.announcementTimestamp = null;
101
1879
  this.announcementPreviewText = null;
102
1880
  this.hasUnseenAnnouncement = false;
103
1881
  this.announcementLoaded = false;
104
- this.announcementLoadError = null;
105
1882
  this.announcementPromise = null;
106
1883
  this.showAnnouncementPreview = true;
1884
+ this.announcementExpanded = false;
107
1885
  this.contextState = {
108
1886
  button: {
109
1887
  position: {
@@ -144,28 +1922,7 @@ var WebInspectorElement = class extends lit.LitElement {
144
1922
  this.resizeStart = null;
145
1923
  this.resizeInitialSize = null;
146
1924
  this.isResizing = false;
147
- this.menuItems = [
148
- {
149
- key: "ag-ui-events",
150
- label: "AG-UI Events",
151
- icon: "Zap"
152
- },
153
- {
154
- key: "agents",
155
- label: "Agent",
156
- icon: "Bot"
157
- },
158
- {
159
- key: "frontend-tools",
160
- label: "Frontend Tools",
161
- icon: "Hammer"
162
- },
163
- {
164
- key: "agent-context",
165
- label: "Context",
166
- icon: "FileText"
167
- }
168
- ];
1925
+ this.customTabIcons = { threads: `<svg class="h-3.5 w-3.5" width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.04167 15C8.29167 15 7.65972 14.7431 7.14583 14.2292C6.63194 13.7153 6.375 13.0972 6.375 12.375C6.375 11.3194 6.80208 10.3646 7.65625 9.51042C8.51042 8.65625 9.57639 8.125 10.8542 7.91667C10.8125 7.41667 10.6875 7.03819 10.4792 6.78125C10.2708 6.52431 9.98611 6.39583 9.625 6.39583C9.20833 6.39583 8.75694 6.56944 8.27083 6.91667C7.78472 7.26389 7.20833 7.83333 6.54167 8.625C5.45833 9.91667 4.66319 10.7569 4.15625 11.1458C3.64931 11.5347 3.10417 11.7292 2.52083 11.7292C1.8125 11.7292 1.21528 11.4653 0.729167 10.9375C0.243056 10.4097 0 9.77083 0 9.02083C0 8.27083 0.163194 7.50347 0.489583 6.71875C0.815972 5.93403 1.36806 4.99306 2.14583 3.89583C2.40972 3.53472 2.60417 3.22917 2.72917 2.97917C2.85417 2.72917 2.91667 2.52778 2.91667 2.375C2.91667 2.27778 2.89931 2.20486 2.86458 2.15625C2.82986 2.10764 2.77778 2.08333 2.70833 2.08333C2.56944 2.08333 2.39583 2.17014 2.1875 2.34375C1.97917 2.51736 1.73611 2.78472 1.45833 3.14583L0 1.66667C0.444444 1.125 0.895833 0.711806 1.35417 0.427083C1.8125 0.142361 2.26389 0 2.70833 0C3.34722 0 3.88889 0.222222 4.33333 0.666667C4.77778 1.11111 5 1.66667 5 2.33333C5 2.73611 4.89583 3.18056 4.6875 3.66667C4.47917 4.15278 4.13194 4.73611 3.64583 5.41667C3.11806 6.16667 2.72569 6.82639 2.46875 7.39583C2.21181 7.96528 2.08333 8.46528 2.08333 8.89583C2.08333 9.13194 2.12153 9.31597 2.19792 9.44792C2.27431 9.57986 2.38194 9.64583 2.52083 9.64583C2.65972 9.64583 2.78125 9.60764 2.88542 9.53125C2.98958 9.45486 3.18056 9.27083 3.45833 8.97917C3.63889 8.78472 3.85417 8.54514 4.10417 8.26042C4.35417 7.97569 4.65972 7.625 5.02083 7.20833C5.89583 6.16667 6.6875 5.42361 7.39583 4.97917C8.10417 4.53472 8.84722 4.3125 9.625 4.3125C10.5556 4.3125 11.3194 4.625 11.9167 5.25C12.5139 5.875 12.8542 6.72917 12.9375 7.8125H15V9.89583H12.9375C12.8264 11.4514 12.4201 12.691 11.7188 13.6146C11.0174 14.5382 10.125 15 9.04167 15ZM9.08333 12.9167C9.52778 12.9167 9.90278 12.6632 10.2083 12.1562C10.5139 11.6493 10.7222 10.9444 10.8333 10.0417C10.1944 10.1944 9.63889 10.4965 9.16667 10.9479C8.69444 11.3993 8.45833 11.8472 8.45833 12.2917C8.45833 12.4861 8.51389 12.6389 8.625 12.75C8.73611 12.8611 8.88889 12.9167 9.08333 12.9167Z" fill="currentColor"/></svg>` };
169
1926
  this.handlePointerDown = (event) => {
170
1927
  if (this.dockMode !== "floating" && this.isOpen) return;
171
1928
  const target = event.currentTarget;
@@ -330,6 +2087,26 @@ var WebInspectorElement = class extends lit.LitElement {
330
2087
  this.expandedTools = /* @__PURE__ */ new Set();
331
2088
  this.expandedContextItems = /* @__PURE__ */ new Set();
332
2089
  this.copiedContextItems = /* @__PURE__ */ new Set();
2090
+ this.handleThreadDividerPointerDown = (event) => {
2091
+ this.threadDividerResizing = true;
2092
+ this.threadDividerPointerId = event.pointerId;
2093
+ this.threadDividerStartX = event.clientX;
2094
+ this.threadDividerStartWidth = this.threadListWidth;
2095
+ event.currentTarget.setPointerCapture(event.pointerId);
2096
+ event.preventDefault();
2097
+ };
2098
+ this.handleThreadDividerPointerMove = (event) => {
2099
+ if (!this.threadDividerResizing || this.threadDividerPointerId !== event.pointerId) return;
2100
+ const delta = event.clientX - this.threadDividerStartX;
2101
+ this.threadListWidth = Math.max(180, Math.min(480, this.threadDividerStartWidth + delta));
2102
+ this.requestUpdate();
2103
+ };
2104
+ this.handleThreadDividerPointerUp = (event) => {
2105
+ if (this.threadDividerPointerId !== event.pointerId) return;
2106
+ const target = event.currentTarget;
2107
+ if (target.hasPointerCapture(this.threadDividerPointerId)) target.releasePointerCapture(this.threadDividerPointerId);
2108
+ this.threadDividerResizing = false;
2109
+ };
333
2110
  this.handleClearEvents = () => {
334
2111
  if (this.selectedContext === "all-agents") {
335
2112
  this.agentEvents.clear();
@@ -375,6 +2152,106 @@ var WebInspectorElement = class extends lit.LitElement {
375
2152
  this.requestUpdate("core", oldValue);
376
2153
  if (this._core) this.attachToCore(this._core);
377
2154
  }
2155
+ get menuItems() {
2156
+ return [
2157
+ {
2158
+ key: "ag-ui-events",
2159
+ label: "AG-UI Events",
2160
+ icon: "Zap"
2161
+ },
2162
+ {
2163
+ key: "agents",
2164
+ label: "Agent",
2165
+ icon: "Bot"
2166
+ },
2167
+ ...(this._core?.tools?.length ?? 0) > 0 ? [{
2168
+ key: "frontend-tools",
2169
+ label: "Frontend Tools",
2170
+ icon: "Hammer"
2171
+ }] : [],
2172
+ {
2173
+ key: "agent-context",
2174
+ label: "Context",
2175
+ icon: "FileText"
2176
+ },
2177
+ {
2178
+ key: "threads",
2179
+ label: "Threads",
2180
+ icon: "MessageSquare"
2181
+ }
2182
+ ];
2183
+ }
2184
+ subscribeToThreadStore(agentId, store) {
2185
+ if (this._threadStoreSubscriptions.has(agentId)) return;
2186
+ const threadsSub = store.select(_copilotkit_core.ɵselectThreads).subscribe((threads) => {
2187
+ this._threadsByAgent.set(agentId, threads);
2188
+ this._threads = Array.from(this._threadsByAgent.values()).flat();
2189
+ this.autoSelectLatestThread();
2190
+ this.requestUpdate();
2191
+ });
2192
+ const errorSub = store.select(_copilotkit_core.ɵselectThreadsError).subscribe((error) => {
2193
+ if (error) this._threadsErrorByAgent.set(agentId, error);
2194
+ else this._threadsErrorByAgent.delete(agentId);
2195
+ this.requestUpdate();
2196
+ });
2197
+ this._threadStoreSubscriptions.set(agentId, () => {
2198
+ threadsSub.unsubscribe();
2199
+ errorSub.unsubscribe();
2200
+ });
2201
+ const initialState = store.getState();
2202
+ this._threadsByAgent.set(agentId, (0, _copilotkit_core.ɵselectThreads)(initialState));
2203
+ const initialError = (0, _copilotkit_core.ɵselectThreadsError)(initialState);
2204
+ if (initialError) this._threadsErrorByAgent.set(agentId, initialError);
2205
+ else this._threadsErrorByAgent.delete(agentId);
2206
+ this._threads = Array.from(this._threadsByAgent.values()).flat();
2207
+ this.autoSelectLatestThread();
2208
+ }
2209
+ autoSelectLatestThread() {
2210
+ if (this._threads.length === 0) return;
2211
+ if (!(this.selectedThreadId != null && this._threads.some((t) => t.id === this.selectedThreadId))) this.selectedThreadId = this._threads[0].id;
2212
+ }
2213
+ teardownThreadStoreSubscriptions() {
2214
+ for (const unsub of this._threadStoreSubscriptions.values()) unsub();
2215
+ this._threadStoreSubscriptions.clear();
2216
+ this._threadsByAgent.clear();
2217
+ this._threadsErrorByAgent.clear();
2218
+ this._threads = [];
2219
+ }
2220
+ ensureOwnedThreadStore(agentId) {
2221
+ if (this._ownedThreadStores.has(agentId)) return;
2222
+ if (this.core?.getThreadStore(agentId)) return;
2223
+ const core = this.core;
2224
+ if (!core?.runtimeUrl) return;
2225
+ const store = (0, _copilotkit_core.ɵcreateThreadStore)({ fetch: globalThis.fetch });
2226
+ store.start();
2227
+ store.setContext({
2228
+ runtimeUrl: core.runtimeUrl,
2229
+ headers: {},
2230
+ agentId
2231
+ });
2232
+ this._ownedThreadStores.set(agentId, store);
2233
+ this.subscribeToThreadStore(agentId, store);
2234
+ core.registerThreadStore(agentId, store);
2235
+ }
2236
+ refreshOwnedThreadStore(agentId) {
2237
+ const store = this._ownedThreadStores.get(agentId);
2238
+ if (!store) return;
2239
+ store.refresh();
2240
+ }
2241
+ removeOwnedThreadStore(agentId) {
2242
+ const store = this._ownedThreadStores.get(agentId);
2243
+ if (!store) return;
2244
+ store.stop();
2245
+ this.core?.unregisterThreadStore(agentId);
2246
+ this._ownedThreadStores.delete(agentId);
2247
+ }
2248
+ teardownOwnedThreadStores() {
2249
+ for (const [agentId, store] of this._ownedThreadStores) {
2250
+ store.stop();
2251
+ this.core?.unregisterThreadStore(agentId);
2252
+ }
2253
+ this._ownedThreadStores.clear();
2254
+ }
378
2255
  attachToCore(core) {
379
2256
  this.runtimeStatus = core.runtimeConnectionStatus;
380
2257
  this.coreProperties = core.properties;
@@ -382,6 +2259,11 @@ var WebInspectorElement = class extends lit.LitElement {
382
2259
  this.coreSubscriber = {
383
2260
  onRuntimeConnectionStatusChanged: ({ status }) => {
384
2261
  this.runtimeStatus = status;
2262
+ if (status === "connected") for (const agentId of this._ownedThreadStores.keys()) this.refreshOwnedThreadStore(agentId);
2263
+ else {
2264
+ this._threadsByAgent.clear();
2265
+ this._threads = [];
2266
+ }
385
2267
  this.requestUpdate();
386
2268
  },
387
2269
  onPropertiesChanged: ({ properties }) => {
@@ -398,16 +2280,36 @@ var WebInspectorElement = class extends lit.LitElement {
398
2280
  onAgentsChanged: ({ agents }) => {
399
2281
  this.processAgentsChanged(agents);
400
2282
  },
401
- onAgentRunStarted: ({ agent }) => {
402
- if (agent?.agentId) this.subscribeToAgent(agent);
403
- },
404
2283
  onContextChanged: ({ context }) => {
405
2284
  this.contextStore = this.normalizeContextStore(context);
406
2285
  this.requestUpdate();
2286
+ },
2287
+ onThreadStoreRegistered: ({ agentId, store }) => {
2288
+ this.subscribeToThreadStore(agentId, store);
2289
+ this.requestUpdate();
2290
+ },
2291
+ onThreadStoreUnregistered: ({ agentId }) => {
2292
+ const unsub = this._threadStoreSubscriptions.get(agentId);
2293
+ if (unsub) {
2294
+ unsub();
2295
+ this._threadStoreSubscriptions.delete(agentId);
2296
+ }
2297
+ this._threadsByAgent.delete(agentId);
2298
+ this._threadsErrorByAgent.delete(agentId);
2299
+ this._threads = Array.from(this._threadsByAgent.values()).flat();
2300
+ this.requestUpdate();
2301
+ },
2302
+ onAgentRunStarted: ({ agent }) => {
2303
+ this.subscribeToAgent(agent);
2304
+ const runThreadId = agent.threadId;
2305
+ if (agent.agentId && runThreadId) this.agentRunThreadId.set(agent.agentId, runThreadId);
2306
+ this.requestUpdate();
407
2307
  }
408
2308
  };
409
2309
  this.coreUnsubscribe = core.subscribe(this.coreSubscriber).unsubscribe;
410
2310
  this.processAgentsChanged(core.agents);
2311
+ const threadStores = typeof core.getThreadStores === "function" ? core.getThreadStores() : {};
2312
+ for (const [agentId, store] of Object.entries(threadStores)) this.subscribeToThreadStore(agentId, store);
411
2313
  if (core.context) this.contextStore = this.normalizeContextStore(core.context);
412
2314
  }
413
2315
  detachFromCore() {
@@ -422,6 +2324,8 @@ var WebInspectorElement = class extends lit.LitElement {
422
2324
  this.cachedTools = [];
423
2325
  this.toolSignature = "";
424
2326
  this.teardownAgentSubscriptions();
2327
+ this.teardownThreadStoreSubscriptions();
2328
+ this.teardownOwnedThreadStores();
425
2329
  }
426
2330
  teardownAgentSubscriptions() {
427
2331
  for (const unsubscribe of this.agentSubscriptions.values()) unsubscribe();
@@ -438,6 +2342,7 @@ var WebInspectorElement = class extends lit.LitElement {
438
2342
  if (!agent?.agentId) continue;
439
2343
  seenAgentIds.add(agent.agentId);
440
2344
  this.subscribeToAgent(agent);
2345
+ this.ensureOwnedThreadStore(agent.agentId);
441
2346
  }
442
2347
  for (const agentId of Array.from(this.agentSubscriptions.keys())) if (!seenAgentIds.has(agentId)) {
443
2348
  this.unsubscribeFromAgent(agentId);
@@ -496,6 +2401,7 @@ var WebInspectorElement = class extends lit.LitElement {
496
2401
  event,
497
2402
  result
498
2403
  });
2404
+ this.refreshOwnedThreadStore(agentId);
499
2405
  },
500
2406
  onRunErrorEvent: ({ event }) => {
501
2407
  this.recordAgentEvent(agentId, "RUN_ERROR", event);
@@ -583,6 +2489,14 @@ var WebInspectorElement = class extends lit.LitElement {
583
2489
  },
584
2490
  onReasoningEncryptedValueEvent: ({ event }) => {
585
2491
  this.recordAgentEvent(agentId, "REASONING_ENCRYPTED_VALUE", event);
2492
+ },
2493
+ onActivitySnapshotEvent: ({ event }) => {
2494
+ this.recordAgentEvent(agentId, "ACTIVITY_SNAPSHOT", event);
2495
+ this.syncAgentMessages(agent);
2496
+ },
2497
+ onActivityDeltaEvent: ({ event }) => {
2498
+ this.recordAgentEvent(agentId, "ACTIVITY_DELTA", event);
2499
+ this.syncAgentMessages(agent);
586
2500
  }
587
2501
  });
588
2502
  this.agentSubscriptions.set(agentId, unsubscribe);
@@ -597,6 +2511,15 @@ var WebInspectorElement = class extends lit.LitElement {
597
2511
  this.agentSubscriptions.delete(agentId);
598
2512
  }
599
2513
  }
2514
+ mapMessagesToConversation(messages) {
2515
+ if (!messages) return null;
2516
+ return messages.filter((m) => m.role === "user" || m.role === "assistant" || m.role === "activity").map((m, i) => ({
2517
+ id: m.id ?? `msg-${i}`,
2518
+ type: m.role === "user" ? "user" : m.role === "activity" ? "generative-ui" : "assistant",
2519
+ content: m.role === "activity" ? m.activityType ?? "unknown" : m.contentText,
2520
+ createdAt: ""
2521
+ }));
2522
+ }
600
2523
  recordAgentEvent(agentId, type, payload) {
601
2524
  const eventId = `${agentId}:${++this.eventCounter}`;
602
2525
  const normalizedPayload = this.normalizeEventPayload(type, payload);
@@ -619,6 +2542,8 @@ var WebInspectorElement = class extends lit.LitElement {
619
2542
  const messages = this.normalizeAgentMessages(agent.messages);
620
2543
  if (messages) this.agentMessages.set(agent.agentId, messages);
621
2544
  else this.agentMessages.delete(agent.agentId);
2545
+ const runThreadId = this.agentRunThreadId.get(agent.agentId);
2546
+ if (runThreadId) this.liveMessageVersion.set(runThreadId, (this.liveMessageVersion.get(runThreadId) ?? 0) + 1);
622
2547
  this.requestUpdate();
623
2548
  } catch (error) {
624
2549
  console.error(`[CopilotKit Inspector] Failed to sync messages for agent "${agent.agentId}":`, error);
@@ -646,18 +2571,23 @@ var WebInspectorElement = class extends lit.LitElement {
646
2571
  if (this.contextOptions.length !== nextOptions.length || this.contextOptions.some((option, index) => option.key !== nextOptions[index]?.key)) this.contextOptions = nextOptions;
647
2572
  const pendingContext = this.pendingSelectedContext;
648
2573
  if (pendingContext) {
649
- if (pendingContext === "all-agents" || agentIds.has(pendingContext)) {
2574
+ if ((pendingContext === "all-agents" || agentIds.has(pendingContext)) && (pendingContext === "all-agents" || agentIds.size === 1)) {
650
2575
  if (this.selectedContext !== pendingContext) {
651
2576
  this.selectedContext = pendingContext;
652
2577
  this.expandedRows.clear();
653
2578
  }
654
2579
  this.pendingSelectedContext = null;
655
- } else if (agentIds.size > 0) this.pendingSelectedContext = null;
2580
+ } else if (agentIds.size > 0) {
2581
+ if (this.selectedContext !== "all-agents") {
2582
+ this.selectedContext = "all-agents";
2583
+ this.expandedRows.clear();
2584
+ }
2585
+ this.pendingSelectedContext = null;
2586
+ }
656
2587
  }
657
2588
  if (!nextOptions.some((option) => option.key === this.selectedContext) && this.pendingSelectedContext === null) {
658
2589
  let nextSelected = "all-agents";
659
- if (agentIds.has("default")) nextSelected = "default";
660
- else if (agentIds.size > 0) nextSelected = Array.from(agentIds).sort((a, b) => a.localeCompare(b))[0];
2590
+ if (agentIds.size === 1) nextSelected = Array.from(agentIds)[0];
661
2591
  if (this.selectedContext !== nextSelected) {
662
2592
  this.selectedContext = nextSelected;
663
2593
  this.expandedRows.clear();
@@ -832,6 +2762,7 @@ ${argsString}</pre
832
2762
  z-index: 2147483646;
833
2763
  display: block;
834
2764
  will-change: transform;
2765
+ font-family: "Plus Jakarta Sans", system-ui, sans-serif;
835
2766
  }
836
2767
 
837
2768
  :host([data-transitioning="true"]) {
@@ -887,13 +2818,14 @@ ${argsString}</pre
887
2818
  left: 50%;
888
2819
  transform: translateX(-50%) translateY(-4px);
889
2820
  white-space: nowrap;
890
- background: rgba(17, 24, 39, 0.95);
2821
+ background: rgba(1, 5, 7, 0.95);
891
2822
  color: white;
892
2823
  padding: 4px 8px;
893
2824
  border-radius: 6px;
894
2825
  font-size: 10px;
2826
+ font-family: "Plus Jakarta Sans", system-ui, sans-serif;
895
2827
  line-height: 1.2;
896
- box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
2828
+ box-shadow: 0 4px 10px rgba(1, 5, 7, 0.18);
897
2829
  opacity: 0;
898
2830
  pointer-events: none;
899
2831
  transition:
@@ -914,18 +2846,19 @@ ${argsString}</pre
914
2846
  min-width: 300px;
915
2847
  max-width: 300px;
916
2848
  background: white;
917
- color: #111827;
2849
+ color: #010507;
918
2850
  font-size: 13px;
2851
+ font-family: "Plus Jakarta Sans", system-ui, sans-serif;
919
2852
  line-height: 1.4;
920
2853
  border-radius: 12px;
921
- box-shadow: 0 12px 28px rgba(15, 23, 42, 0.22);
2854
+ box-shadow: 0 12px 28px rgba(1, 5, 7, 0.12);
922
2855
  padding: 10px 12px;
923
2856
  display: inline-flex;
924
2857
  align-items: flex-start;
925
2858
  gap: 8px;
926
2859
  z-index: 4500;
927
2860
  animation: fade-slide-in 160ms ease;
928
- border: 1px solid rgba(148, 163, 184, 0.35);
2861
+ border: 1px solid rgba(219, 219, 229, 0.4);
929
2862
  white-space: normal;
930
2863
  word-break: break-word;
931
2864
  text-align: left;
@@ -946,7 +2879,7 @@ ${argsString}</pre
946
2879
  width: 10px;
947
2880
  height: 10px;
948
2881
  background: white;
949
- border: 1px solid rgba(148, 163, 184, 0.35);
2882
+ border: 1px solid rgba(219, 219, 229, 0.4);
950
2883
  transform: rotate(45deg);
951
2884
  top: 50%;
952
2885
  margin-top: -5px;
@@ -955,35 +2888,130 @@ ${argsString}</pre
955
2888
 
956
2889
  .announcement-preview[data-side="left"] .announcement-preview__arrow {
957
2890
  right: -5px;
958
- box-shadow: 6px -6px 10px rgba(15, 23, 42, 0.12);
2891
+ box-shadow: 6px -6px 10px rgba(1, 5, 7, 0.08);
959
2892
  }
960
2893
 
961
2894
  .announcement-preview[data-side="right"] .announcement-preview__arrow {
962
2895
  left: -5px;
963
- box-shadow: -6px 6px 10px rgba(15, 23, 42, 0.12);
2896
+ box-shadow: -6px 6px 10px rgba(1, 5, 7, 0.08);
964
2897
  }
965
2898
 
966
2899
  .announcement-dismiss {
967
- color: #6b7280;
968
- font-size: 12px;
969
- padding: 2px 8px;
970
- border-radius: 8px;
971
- border: 1px solid rgba(148, 163, 184, 0.5);
972
- background: rgba(248, 250, 252, 0.9);
2900
+ background: none;
2901
+ border: none;
2902
+ cursor: pointer;
2903
+ color: #838389;
2904
+ width: 28px;
2905
+ height: 28px;
2906
+ display: flex;
2907
+ align-items: center;
2908
+ justify-content: center;
2909
+ border-radius: 6px;
2910
+ padding: 0;
973
2911
  transition:
974
2912
  background 120ms ease,
975
2913
  color 120ms ease;
976
2914
  }
977
2915
 
978
- .announcement-dismiss:hover {
979
- background: rgba(241, 245, 249, 1);
980
- color: #111827;
2916
+ .announcement-dismiss:hover {
2917
+ background: rgba(0, 0, 0, 0.06);
2918
+ color: #010507;
2919
+ }
2920
+
2921
+ /* ── Agent tab section cards ─────────────────────────────────────── */
2922
+ .cpk-section-card {
2923
+ border-radius: 8px;
2924
+ background: #ffffff;
2925
+ overflow: hidden;
2926
+ }
2927
+
2928
+ /* ── Agent icon bubble ───────────────────────────────────────────── */
2929
+ .cpk-agent-icon {
2930
+ background-color: #f0f0f4 !important;
2931
+ color: #57575b !important;
2932
+ }
2933
+
2934
+ /* ── Agent stat cards ────────────────────────────────────────────── */
2935
+ .cpk-stat-card {
2936
+ background-color: #ffffff !important;
2937
+ border: 1px solid #dbdbe5 !important;
2938
+ }
2939
+ button.cpk-stat-card:hover {
2940
+ background-color: #f7f7f9 !important;
2941
+ }
2942
+
2943
+ /* ── Circle chevron (Frontend Tools + Context) ──────────────────── */
2944
+ .cpk-chevron-circle {
2945
+ display: inline-flex;
2946
+ align-items: center;
2947
+ justify-content: center;
2948
+ width: 24px;
2949
+ height: 24px;
2950
+ border-radius: 50%;
2951
+ background-color: #f0f0f4;
2952
+ color: #838389;
2953
+ flex-shrink: 0;
2954
+ transition: transform 0.2s;
2955
+ }
2956
+ .cpk-chevron-circle svg {
2957
+ width: 14px !important;
2958
+ height: 14px !important;
2959
+ }
2960
+ .cpk-chevron-circle--open {
2961
+ transform: rotate(180deg);
2962
+ }
2963
+
2964
+ /* ── Inline copy button ─────────────────────────────────────────── */
2965
+ .cpk-copy-btn {
2966
+ font-size: 10px;
2967
+ font-weight: 500;
2968
+ color: #57575b;
2969
+ background: #ffffff;
2970
+ border: 1px solid #dbdbe5;
2971
+ cursor: pointer;
2972
+ padding: 2px 8px;
2973
+ border-radius: 4px;
2974
+ flex-shrink: 0;
2975
+ transition:
2976
+ background-color 0.15s,
2977
+ border-color 0.15s;
2978
+ }
2979
+ .cpk-copy-btn:hover {
2980
+ background-color: #f0f0f4;
2981
+ border-color: #afafb7;
2982
+ }
2983
+
2984
+ .cpk-section-header {
2985
+ background: #e8edf5;
2986
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
2987
+ padding: 10px 16px;
2988
+ }
2989
+ .cpk-section-header h4 {
2990
+ font-size: 11px;
2991
+ font-weight: 600;
2992
+ color: #181c1f;
2993
+ margin: 0;
2994
+ }
2995
+
2996
+ /* Inputs/selects inside the lavender header need an explicit white bg */
2997
+ .cpk-section-header input,
2998
+ .cpk-section-header select {
2999
+ background-color: #ffffff !important;
3000
+ box-shadow: none !important;
3001
+ }
3002
+ .cpk-section-header select {
3003
+ padding-right: 24px !important;
3004
+ }
3005
+ /* Events table column headers */
3006
+ table thead th {
3007
+ font-weight: 600 !important;
981
3008
  }
982
3009
 
983
3010
  .announcement-content {
984
- color: #111827;
985
- font-size: 14px;
986
- line-height: 1.6;
3011
+ color: #010507;
3012
+ font-size: 12px;
3013
+ font-family: "Plus Jakarta Sans", system-ui, sans-serif;
3014
+ line-height: 1.5;
987
3015
  }
988
3016
 
989
3017
  .announcement-content h1,
@@ -994,42 +3022,322 @@ ${argsString}</pre
994
3022
  }
995
3023
 
996
3024
  .announcement-content h1 {
997
- font-size: 1.1rem;
3025
+ font-size: 0.75rem;
998
3026
  }
999
-
1000
3027
  .announcement-content h2 {
1001
- font-size: 1rem;
3028
+ font-size: 0.8rem;
1002
3029
  }
1003
-
1004
3030
  .announcement-content h3 {
1005
- font-size: 0.95rem;
3031
+ font-size: 0.75rem;
1006
3032
  }
1007
3033
 
1008
3034
  .announcement-content p {
1009
- margin: 0.25rem 0;
3035
+ margin: 0.2rem 0;
1010
3036
  }
1011
3037
 
1012
3038
  .announcement-content ul {
1013
3039
  list-style: disc;
1014
3040
  padding-left: 1.25rem;
1015
- margin: 0.3rem 0;
3041
+ margin: 0.2rem 0;
1016
3042
  }
1017
3043
 
1018
3044
  .announcement-content ol {
1019
3045
  list-style: decimal;
1020
3046
  padding-left: 1.25rem;
1021
- margin: 0.3rem 0;
3047
+ margin: 0.2rem 0;
1022
3048
  }
1023
3049
 
1024
3050
  .announcement-content a {
1025
- color: #0f766e;
3051
+ color: #757cf2;
1026
3052
  text-decoration: underline;
1027
3053
  }
3054
+
3055
+ .announcement-body {
3056
+ position: relative;
3057
+ overflow: hidden;
3058
+ transition: max-height 0.25s ease;
3059
+ }
3060
+ .announcement-body--collapsed {
3061
+ max-height: 72px;
3062
+ }
3063
+ .announcement-body--expanded {
3064
+ max-height: 2000px;
3065
+ }
3066
+ .announcement-fade {
3067
+ position: absolute;
3068
+ bottom: 0;
3069
+ left: 0;
3070
+ right: 0;
3071
+ height: 48px;
3072
+ background: linear-gradient(to bottom, transparent, #ffffff);
3073
+ pointer-events: none;
3074
+ }
3075
+ .announcement-toggle {
3076
+ display: block;
3077
+ width: 100%;
3078
+ margin-top: 6px;
3079
+ padding: 0;
3080
+ background: none;
3081
+ border: none;
3082
+ font-family: "Plus Jakarta Sans", system-ui, sans-serif;
3083
+ font-size: 12px;
3084
+ font-weight: 500;
3085
+ color: #757cf2;
3086
+ cursor: pointer;
3087
+ text-align: center;
3088
+ }
3089
+ .announcement-toggle:hover {
3090
+ color: #6430ab;
3091
+ }
3092
+
3093
+ /* ── Brand typography ────────────────────────────────────────── */
3094
+ /* Override Tailwind font-mono stack → Spline Sans Mono */
3095
+ .font-mono,
3096
+ pre,
3097
+ code {
3098
+ font-family: "Spline Sans Mono", ui-monospace, "Cascadia Code", monospace;
3099
+ }
3100
+
3101
+ /* ── Floating button ─────────────────────────────────────────── */
3102
+ .console-button {
3103
+ background-color: rgba(1, 5, 7, 0.95) !important;
3104
+ border-color: rgba(190, 194, 255, 0.25) !important;
3105
+ box-shadow:
3106
+ 0 0 0 1px rgba(190, 194, 255, 0.15),
3107
+ 0 4px 14px rgba(1, 5, 7, 0.28) !important;
3108
+ }
3109
+ .console-button:hover {
3110
+ background-color: rgba(1, 5, 7, 1) !important;
3111
+ border-color: rgba(190, 194, 255, 0.45) !important;
3112
+ }
3113
+ .console-button:focus-visible {
3114
+ outline-color: #bec2ff !important;
3115
+ }
3116
+
3117
+ /* ── Inspector window ────────────────────────────────────────── */
3118
+ .inspector-window {
3119
+ border-color: #dbdbe5 !important;
3120
+ box-shadow:
3121
+ 0 8px 32px rgba(1, 5, 7, 0.1),
3122
+ 0 2px 8px rgba(1, 5, 7, 0.06) !important;
3123
+ }
3124
+
3125
+ /* ── Header drag area ────────────────────────────────────────── */
3126
+ .drag-handle {
3127
+ border-bottom-color: #dbdbe5 !important;
3128
+ /* Subtle pale lavender gradient — brand "light, spacious" surface */
3129
+ background: linear-gradient(180deg, #f4f4fd 0%, #ffffff 100%) !important;
3130
+ }
3131
+
3132
+ /* Tab strip row: soft off-white, separated from content */
3133
+ .drag-handle > div:last-child {
3134
+ border-top-color: #e2e2ea !important;
3135
+ background-color: #fafafc !important;
3136
+ }
3137
+
3138
+ /* ── Tab buttons ─────────────────────────────────────────────── */
3139
+ /*
3140
+ * Named classes owned by this component — no Tailwind conflict.
3141
+ * Active: brand surface/surfaceContainerActive (lilac tint) +
3142
+ * border/borderActionEnabled underline.
3143
+ * Dark fill is for primary action buttons only, not nav tabs.
3144
+ */
3145
+ .cpk-tab-active {
3146
+ background-color: rgba(190, 194, 255, 0.18);
3147
+ color: #010507;
3148
+ font-weight: 600;
3149
+ }
3150
+ .cpk-tab-active .cpk-tab-icon {
3151
+ color: #757cf2;
3152
+ }
3153
+ .cpk-tab-inactive {
3154
+ background-color: transparent;
3155
+ color: #2b2b2b;
3156
+ }
3157
+ .cpk-tab-inactive .cpk-tab-icon {
3158
+ color: #838389;
3159
+ }
3160
+ .cpk-tab-inactive:hover {
3161
+ background-color: rgba(190, 194, 255, 0.08);
3162
+ color: #010507;
3163
+ cursor: pointer;
3164
+ }
3165
+ .cpk-tab-active {
3166
+ cursor: pointer;
3167
+ }
3168
+
3169
+ /* ── Header control buttons (dock, close) — first row only ───── */
3170
+ .drag-handle > div:first-child button {
3171
+ color: #838389 !important;
3172
+ }
3173
+ .drag-handle > div:first-child button:hover {
3174
+ background-color: #f0f0f4 !important;
3175
+ color: #57575b !important;
3176
+ }
3177
+ .drag-handle > div:first-child button:focus-visible {
3178
+ outline-color: #bec2ff !important;
3179
+ }
3180
+
3181
+ /* ── Agent/context dropdown ──────────────────────────────────── */
3182
+ [data-context-dropdown-root="true"] > button {
3183
+ border-color: #dbdbe5 !important;
3184
+ color: #010507 !important;
3185
+ }
3186
+ [data-context-dropdown-root="true"] > button:hover {
3187
+ border-color: #bec2ff !important;
3188
+ background-color: #f7f7f9 !important;
3189
+ }
3190
+ [data-context-dropdown-root="true"] > button > span:last-child {
3191
+ color: #838389 !important;
3192
+ }
3193
+ [data-context-dropdown-root="true"] > div {
3194
+ border-color: #dbdbe5 !important;
3195
+ box-shadow: 0 4px 12px rgba(1, 5, 7, 0.08) !important;
3196
+ }
3197
+ [data-context-dropdown-root="true"] > div button:hover,
3198
+ [data-context-dropdown-root="true"] > div button:focus {
3199
+ background-color: #f7f7f9 !important;
3200
+ }
3201
+
3202
+ /* ── Status bar (bottom chrome) ──────────────────────────────── */
3203
+ .inspector-window > div > div:last-child {
3204
+ border-top-color: #dbdbe5 !important;
3205
+ background-color: #f7f7f9 !important;
3206
+ }
3207
+
3208
+ /* ── Resize handle ───────────────────────────────────────────── */
3209
+ .resize-handle {
3210
+ color: #838389 !important;
3211
+ }
3212
+ .resize-handle:hover {
3213
+ color: #57575b !important;
3214
+ }
3215
+
3216
+ /* ── AG-UI Events tab ────────────────────────────────────────── */
3217
+ /* Row hover: replace blue tint with brand lilac */
3218
+ tr:hover td {
3219
+ background-color: rgba(190, 194, 255, 0.08) !important;
3220
+ }
3221
+ /* Reset/dark action button */
3222
+ button[class*="bg-gray-900"] {
3223
+ background-color: #010507 !important;
3224
+ }
3225
+ button[class*="bg-gray-800"] {
3226
+ background-color: #2b2b2b !important;
3227
+ }
3228
+ /* Copy "copied" state: generic green → brand mint */
3229
+ button[class*="bg-green-100"] {
3230
+ background-color: rgba(133, 236, 206, 0.2) !important;
3231
+ color: #189370 !important;
3232
+ }
3233
+
3234
+ /* ── Agents tab ──────────────────────────────────────────────── */
3235
+ /* Agent icon bubble: blue → lilac */
3236
+ span[class*="bg-blue-100"]:not([class*="text-blue-800"]) {
3237
+ background-color: rgba(190, 194, 255, 0.15) !important;
3238
+ }
3239
+ span[class*="text-blue-600"] {
3240
+ color: #757cf2 !important;
3241
+ }
3242
+ /* Running badge: emerald → mint */
3243
+ span[class*="bg-emerald-50"] {
3244
+ background-color: rgba(133, 236, 206, 0.15) !important;
3245
+ }
3246
+ span[class*="text-emerald-700"] {
3247
+ color: #189370 !important;
3248
+ }
3249
+ /* Running status dot */
3250
+ span[class*="bg-emerald-500"] {
3251
+ background-color: #85ecce !important;
3252
+ }
3253
+ /* Idle dot */
3254
+ span[class*="bg-gray-400"] {
3255
+ background-color: #afafb7 !important;
3256
+ }
3257
+ /* User role badge (blue → lilac) */
3258
+ span[class*="bg-blue-100"][class*="text-blue-800"] {
3259
+ background-color: rgba(190, 194, 255, 0.22) !important;
3260
+ border: 1px solid rgba(190, 194, 255, 0.45) !important;
3261
+ color: #57575b !important;
3262
+ }
3263
+ /* Assistant role badge (green → mint) */
3264
+ span[class*="bg-green-100"][class*="text-green-800"] {
3265
+ background-color: rgba(133, 236, 206, 0.18) !important;
3266
+ border: 1px solid rgba(133, 236, 206, 0.4) !important;
3267
+ color: #189370 !important;
3268
+ }
3269
+ /* Tool role badge (amber → orange brand) */
3270
+ span[class*="bg-amber-100"][class*="text-amber-800"] {
3271
+ background-color: rgba(255, 172, 77, 0.15) !important;
3272
+ color: #57575b !important;
3273
+ }
3274
+
3275
+ /* ── Frontend Tools tab ──────────────────────────────────────── */
3276
+ /* Handler badge (blue → lilac) */
3277
+ span[class*="bg-blue-50"][class*="text-blue-700"] {
3278
+ background-color: rgba(190, 194, 255, 0.12) !important;
3279
+ border-color: rgba(190, 194, 255, 0.3) !important;
3280
+ color: #010507 !important;
3281
+ }
3282
+ /* Renderer badge (purple → lilac-adjacent) */
3283
+ span[class*="bg-purple-50"][class*="text-purple-700"] {
3284
+ background-color: rgba(190, 194, 255, 0.12) !important;
3285
+ border-color: rgba(190, 194, 255, 0.3) !important;
3286
+ color: #57575b !important;
3287
+ }
3288
+ /* Required badge (rose → brand red) */
3289
+ span[class*="bg-rose-50"][class*="text-rose-700"] {
3290
+ background-color: rgba(250, 95, 103, 0.1) !important;
3291
+ border-color: rgba(250, 95, 103, 0.25) !important;
3292
+ color: #fa5f67 !important;
3293
+ }
3294
+ /* Code/default value blocks */
3295
+ code[class*="bg-gray-100"],
3296
+ span[class*="bg-gray-100"] {
3297
+ background-color: #f0f0f4 !important;
3298
+ }
3299
+
3300
+ /* ── Connected status bar: match threads header mint (#5BE4BB) ──── */
3301
+ /* Outer strip bg + top border + text when connected badge is present */
3302
+ .inspector-window
3303
+ > div
3304
+ > div:last-child
3305
+ > div:last-child:has(div[class*="bg-emerald-50"]) {
3306
+ background-color: rgba(91, 228, 187, 0.08) !important;
3307
+ border-top-color: rgba(91, 228, 187, 0.3) !important;
3308
+ color: #189370 !important;
3309
+ }
3310
+ /* Inner badge — slightly more opaque on the mint bg */
3311
+ div[class*="bg-emerald-50"][class*="border-emerald-200"] {
3312
+ background-color: rgba(91, 228, 187, 0.12) !important;
3313
+ border-color: rgba(91, 228, 187, 0.4) !important;
3314
+ color: #189370 !important;
3315
+ }
3316
+ /* Icon bubble inside connected badge → mint tint */
3317
+ div[class*="bg-emerald-50"] span[class*="bg-white"] {
3318
+ background-color: rgba(91, 228, 187, 0.3) !important;
3319
+ }
3320
+
3321
+ /* ── Announcement panel ──────────────────────────────────────── */
3322
+ div[class*="border-slate-200"][class*="bg-white"] {
3323
+ border-color: #dbdbe5 !important;
3324
+ }
3325
+ /* Announcement icon bubble: black → brand light lavender + lilac icon */
3326
+ span[class*="bg-slate-900"],
3327
+ div[class*="bg-slate-900"] {
3328
+ background-color: #eee6fe !important;
3329
+ color: #757cf2 !important;
3330
+ }
3331
+ span[class*="text-slate-800"],
3332
+ div[class*="text-slate-800"] {
3333
+ color: #010507 !important;
3334
+ }
1028
3335
  `];
1029
3336
  }
1030
3337
  connectedCallback() {
1031
3338
  super.connectedCallback();
1032
3339
  if (typeof window !== "undefined") {
3340
+ this.ensureBrandFonts();
1033
3341
  window.addEventListener("resize", this.handleResize);
1034
3342
  window.addEventListener("pointerdown", this.handleGlobalPointerDown);
1035
3343
  this.hydrateStateFromStorageEarly();
@@ -1037,6 +3345,15 @@ ${argsString}</pre
1037
3345
  this.ensureAnnouncementLoading();
1038
3346
  }
1039
3347
  }
3348
+ ensureBrandFonts() {
3349
+ const FONT_LINK_ID = "cpk-inspector-brand-fonts";
3350
+ if (document.getElementById(FONT_LINK_ID)) return;
3351
+ const link = document.createElement("link");
3352
+ link.id = FONT_LINK_ID;
3353
+ link.rel = "stylesheet";
3354
+ link.href = "https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600&family=Spline+Sans+Mono:wght@600&display=swap";
3355
+ document.head.appendChild(link);
3356
+ }
1040
3357
  disconnectedCallback() {
1041
3358
  super.disconnectedCallback();
1042
3359
  if (typeof window !== "undefined") {
@@ -1114,7 +3431,7 @@ ${argsString}</pre
1114
3431
  "focus-visible:outline",
1115
3432
  "focus-visible:outline-2",
1116
3433
  "focus-visible:outline-offset-2",
1117
- "focus-visible:outline-rose-500",
3434
+ "focus-visible:outline-[#BEC2FF]",
1118
3435
  "touch-none",
1119
3436
  "select-none",
1120
3437
  this.isDragging ? "cursor-grabbing" : "cursor-grab"
@@ -1214,6 +3531,7 @@ ${argsString}</pre
1214
3531
  </div>
1215
3532
  </div>
1216
3533
  </div>
3534
+ ${this.renderAnnouncementBanner()}
1217
3535
  <div
1218
3536
  class="flex flex-wrap items-center gap-2 border-t border-gray-100 px-3 py-2 text-xs"
1219
3537
  >
@@ -1222,14 +3540,12 @@ ${argsString}</pre
1222
3540
  return lit.html`
1223
3541
  <button
1224
3542
  type="button"
1225
- class=${["inline-flex items-center gap-2 rounded-md px-3 py-2 transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-300", isSelected ? "bg-gray-900 text-white shadow-sm" : "text-gray-600 hover:bg-gray-100 hover:text-gray-900"].join(" ")}
3543
+ class=${["inline-flex items-center gap-2 rounded-md px-3 py-2 transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-300", isSelected ? "cpk-tab-active" : "cpk-tab-inactive"].join(" ")}
1226
3544
  aria-pressed=${isSelected}
1227
3545
  @click=${() => this.handleMenuSelect(key)}
1228
3546
  >
1229
- <span
1230
- class="text-gray-400 ${isSelected ? "text-white" : ""}"
1231
- >
1232
- ${this.renderIcon(icon)}
3547
+ <span class="cpk-tab-icon">
3548
+ ${key in this.customTabIcons ? (0, lit_directives_unsafe_html_js.unsafeHTML)(this.customTabIcons[key]) : this.renderIcon(icon)}
1233
3549
  </span>
1234
3550
  <span>${label}</span>
1235
3551
  </button>
@@ -1238,8 +3554,7 @@ ${argsString}</pre
1238
3554
  </div>
1239
3555
  </div>
1240
3556
  <div class="flex flex-1 flex-col overflow-hidden">
1241
- <div class="flex-1 overflow-auto">
1242
- ${this.renderAnnouncementPanel()}
3557
+ <div id="cpk-main-scroll" class="flex-1 overflow-auto">
1243
3558
  ${this.renderCoreWarningBanner()} ${this.renderMainContent()}
1244
3559
  <slot></slot>
1245
3560
  </div>
@@ -1287,6 +3602,7 @@ ${argsString}</pre
1287
3602
  }
1288
3603
  hydrateStateFromStorageEarly() {
1289
3604
  if (typeof document === "undefined" || typeof window === "undefined") return;
3605
+ if (document.cookie.includes("cpk_threads_access=1")) this._threadsUnlocked = true;
1290
3606
  const persisted = require_persistence.loadInspectorState(INSPECTOR_STORAGE_KEY);
1291
3607
  if (!persisted) return;
1292
3608
  if (typeof persisted.isOpen === "boolean") this.isOpen = persisted.isOpen;
@@ -1683,7 +3999,8 @@ ${argsString}</pre
1683
3999
  role,
1684
4000
  contentText,
1685
4001
  contentRaw: raw.content !== void 0 ? this.sanitizeForLogging(raw.content) : void 0,
1686
- toolCalls
4002
+ toolCalls,
4003
+ activityType: typeof raw.activityType === "string" ? raw.activityType : void 0
1687
4004
  };
1688
4005
  }
1689
4006
  normalizeAgentMessages(messages) {
@@ -1702,9 +4019,6 @@ ${argsString}</pre
1702
4019
  } else normalized[key] = { value: entry };
1703
4020
  return normalized;
1704
4021
  }
1705
- getSelectedMenu() {
1706
- return this.menuItems.find((item) => item.key === this.selectedMenu) ?? this.menuItems[0];
1707
- }
1708
4022
  renderCoreWarningBanner() {
1709
4023
  if (this._core) return lit.nothing;
1710
4024
  return lit.html`
@@ -1761,27 +4075,456 @@ ${argsString}</pre
1761
4075
  if (this.selectedMenu === "agents") return this.renderAgentsView();
1762
4076
  if (this.selectedMenu === "frontend-tools") return this.renderToolsView();
1763
4077
  if (this.selectedMenu === "agent-context") return this.renderContextView();
4078
+ if (this.selectedMenu === "threads") return this.renderThreadsView();
1764
4079
  return lit.nothing;
1765
4080
  }
4081
+ renderThreadsGate() {
4082
+ return lit.html`
4083
+ <div style="
4084
+ position:relative;
4085
+ display:flex;
4086
+ flex-direction:column;
4087
+ align-items:center;
4088
+ justify-content:center;
4089
+ padding:40px 24px;
4090
+ min-height:100%;
4091
+ text-align:center;
4092
+ background:linear-gradient(135deg,#f5f4ff 0%,#ede9fe 100%);
4093
+ overflow:hidden;
4094
+ ">
4095
+ <!-- Blurred ellipses from Figma/storybook -->
4096
+ <div style="position:absolute;width:570px;height:570px;border-radius:50%;top:-80px;left:-120px;opacity:0.25;background:#757CF2;filter:blur(120px);pointer-events:none;"></div>
4097
+ <div style="position:absolute;width:570px;height:570px;border-radius:50%;bottom:-100px;right:-80px;opacity:0.2;background:#FFAC4D;filter:blur(120px);pointer-events:none;"></div>
4098
+ <div style="position:absolute;width:400px;height:400px;border-radius:50%;bottom:20px;left:-60px;opacity:0.15;background:#FFAC4D;filter:blur(100px);pointer-events:none;"></div>
4099
+
4100
+ ${this._threadsUnlocking ? this._renderUnlockingCard() : this._renderEarlyAccessCard()}
4101
+ </div>
4102
+ `;
4103
+ }
4104
+ static {
4105
+ this.THREADS_REQUEST_URL = "https://r3x69.share-na2.hsforms.com/2uiZg8EkiT7a_KykeXV1ajQ";
4106
+ }
4107
+ _renderEarlyAccessCard() {
4108
+ const invalid = this._threadsGateCodeInvalid;
4109
+ return lit.html`
4110
+ <div
4111
+ style="
4112
+ position:relative;
4113
+ z-index:1;
4114
+ background:#ffffff;
4115
+ border:1px solid #E5E5EA;
4116
+ border-radius:20px;
4117
+ box-shadow:0 16px 48px rgba(1,5,7,0.12),0 2px 6px rgba(1,5,7,0.05);
4118
+ padding:28px;
4119
+ width:400px;
4120
+ max-width:100%;
4121
+ display:flex;
4122
+ flex-direction:column;
4123
+ gap:18px;
4124
+ text-align:left;
4125
+ font-family:'Plus Jakarta Sans', system-ui, sans-serif;
4126
+ "
4127
+ >
4128
+ <!-- Kicker pill -->
4129
+ <div>
4130
+ <span
4131
+ style="
4132
+ display:inline-flex;
4133
+ align-items:center;
4134
+ gap:4px;
4135
+ padding:4px 10px;
4136
+ border-radius:999px;
4137
+ background:#F3F3FC;
4138
+ color:#757CF2;
4139
+ font-family:'Spline Sans Mono', ui-monospace, monospace;
4140
+ font-size:10px;
4141
+ font-weight:500;
4142
+ letter-spacing:0.08em;
4143
+ text-transform:uppercase;
4144
+ "
4145
+ >Early Access</span
4146
+ >
4147
+ </div>
4148
+
4149
+ <!-- Title + description -->
4150
+ <div style="display:flex;flex-direction:column;gap:8px;">
4151
+ <h2
4152
+ style="
4153
+ font-family:'Plus Jakarta Sans', system-ui, sans-serif;
4154
+ font-size:24px;
4155
+ font-weight:700;
4156
+ color:#010507;
4157
+ line-height:1.2;
4158
+ letter-spacing:-0.015em;
4159
+ margin:0;
4160
+ "
4161
+ >
4162
+ <span
4163
+ style="
4164
+ background:linear-gradient(90deg, #757CF2 0%, #5AE4BB 100%);
4165
+ -webkit-background-clip:text;
4166
+ background-clip:text;
4167
+ color:transparent;
4168
+ -webkit-text-fill-color:transparent;
4169
+ "
4170
+ >Threads</span
4171
+ >
4172
+ are in private beta
4173
+ </h2>
4174
+ <p
4175
+ style="
4176
+ font-size:14px;
4177
+ font-weight:500;
4178
+ color:#5C5C66;
4179
+ line-height:1.55;
4180
+ margin:0;
4181
+ "
4182
+ >
4183
+ Spin up separate conversations with your agent, one per task, bug,
4184
+ or feature, and jump back into any of them without losing context.
4185
+ </p>
4186
+ </div>
4187
+
4188
+ <!-- Bullets -->
4189
+ <div
4190
+ style="display:flex;flex-direction:column;gap:8px;padding:4px 0;"
4191
+ >
4192
+ ${[
4193
+ "One agent, many conversations",
4194
+ "Persistent history across sessions",
4195
+ "Jump between threads in a click"
4196
+ ].map((label) => lit.html`
4197
+ <div style="display:flex;align-items:center;gap:10px;">
4198
+ <svg
4199
+ width="14"
4200
+ height="14"
4201
+ viewBox="0 0 24 24"
4202
+ fill="none"
4203
+ stroke="#010507"
4204
+ stroke-width="2.5"
4205
+ stroke-linecap="round"
4206
+ stroke-linejoin="round"
4207
+ style="flex-shrink:0;"
4208
+ >
4209
+ <polyline points="20 6 9 17 4 12"></polyline>
4210
+ </svg>
4211
+ <span style="font-size:13px;font-weight:500;color:#010507;"
4212
+ >${label}</span
4213
+ >
4214
+ </div>
4215
+ `)}
4216
+ </div>
4217
+
4218
+ <!-- Primary CTA: dark MonoPillButton with adjacent arrow circle -->
4219
+ <div>
4220
+ <a
4221
+ href=${WebInspectorElement.THREADS_REQUEST_URL}
4222
+ target="_blank"
4223
+ rel="noopener noreferrer"
4224
+ style="
4225
+ display:inline-flex;
4226
+ align-items:center;
4227
+ gap:8px;
4228
+ text-decoration:none;
4229
+ cursor:pointer;
4230
+ "
4231
+ >
4232
+ <span
4233
+ style="
4234
+ display:inline-flex;
4235
+ align-items:center;
4236
+ justify-content:center;
4237
+ background:#010507;
4238
+ color:#ffffff;
4239
+ font-family:'Spline Sans Mono', ui-monospace, monospace;
4240
+ font-size:13px;
4241
+ font-weight:500;
4242
+ letter-spacing:0.06em;
4243
+ text-transform:uppercase;
4244
+ padding:14px 22px;
4245
+ border-radius:999px;
4246
+ box-shadow:0 4px 12px rgba(1,5,7,0.18);
4247
+ "
4248
+ >Request Early Access</span
4249
+ >
4250
+ <span
4251
+ style="
4252
+ display:inline-flex;
4253
+ align-items:center;
4254
+ justify-content:center;
4255
+ width:36px;
4256
+ height:36px;
4257
+ border-radius:999px;
4258
+ background:#010507;
4259
+ color:#ffffff;
4260
+ box-shadow:0 4px 12px rgba(1,5,7,0.18);
4261
+ "
4262
+ >
4263
+ <svg
4264
+ width="14"
4265
+ height="14"
4266
+ viewBox="0 0 24 24"
4267
+ fill="none"
4268
+ stroke="currentColor"
4269
+ stroke-width="2"
4270
+ stroke-linecap="round"
4271
+ stroke-linejoin="round"
4272
+ >
4273
+ <line x1="5" y1="12" x2="19" y2="12"></line>
4274
+ <polyline points="12 5 19 12 12 19"></polyline>
4275
+ </svg>
4276
+ </span>
4277
+ </a>
4278
+ </div>
4279
+
4280
+ <!-- Divider + invite-code section -->
4281
+ <div
4282
+ style="
4283
+ display:flex;
4284
+ flex-direction:column;
4285
+ gap:8px;
4286
+ padding-top:14px;
4287
+ border-top:1px dashed #E5E5EA;
4288
+ "
4289
+ >
4290
+ <span style="font-size:12px;font-weight:500;color:#8A8A94;"
4291
+ >Have an invite code?</span
4292
+ >
4293
+ <div style="display:flex;gap:8px;">
4294
+ <div
4295
+ style="
4296
+ flex:1;
4297
+ background:#ffffff;
4298
+ border:1px solid ${invalid ? "#FA5F67" : "#E5E5EA"};
4299
+ border-radius:10px;
4300
+ padding:2px 4px 2px 12px;
4301
+ transition:border-color 150ms ease;
4302
+ "
4303
+ >
4304
+ <input
4305
+ id="cpk-gate-input"
4306
+ type="text"
4307
+ placeholder="Enter access code"
4308
+ style="
4309
+ width:100%;
4310
+ padding:10px 0;
4311
+ font-family:'Plus Jakarta Sans', system-ui, sans-serif;
4312
+ font-size:13px;
4313
+ font-weight:500;
4314
+ color:#010507;
4315
+ background:transparent;
4316
+ border:none;
4317
+ outline:none;
4318
+ "
4319
+ @keydown=${(e) => {
4320
+ if (e.key === "Enter") this._submitThreadsCode(e.currentTarget.value);
4321
+ }}
4322
+ />
4323
+ </div>
4324
+ <button
4325
+ style="
4326
+ background:#010507;
4327
+ color:#ffffff;
4328
+ border:none;
4329
+ border-radius:10px;
4330
+ padding:0 16px;
4331
+ font-family:'Spline Sans Mono', ui-monospace, monospace;
4332
+ font-size:11px;
4333
+ font-weight:500;
4334
+ letter-spacing:0.06em;
4335
+ text-transform:uppercase;
4336
+ cursor:pointer;
4337
+ white-space:nowrap;
4338
+ "
4339
+ @click=${() => {
4340
+ const input = this.shadowRoot?.getElementById("cpk-gate-input");
4341
+ if (input) this._submitThreadsCode(input.value);
4342
+ }}
4343
+ >
4344
+ Unlock
4345
+ </button>
4346
+ </div>
4347
+ ${invalid ? lit.html`
4348
+ <div style="font-size: 11px; font-weight: 500; color: #fa5f67">
4349
+ That code isn't valid. Double-check your invite email.
4350
+ </div>
4351
+ ` : lit.nothing}
4352
+ </div>
4353
+ </div>
4354
+ `;
4355
+ }
4356
+ _renderUnlockingCard() {
4357
+ return lit.html`
4358
+ <div
4359
+ style="
4360
+ position: relative;
4361
+ z-index: 1;
4362
+ background: #ffffff;
4363
+ border: 1px solid #e5e5ea;
4364
+ border-radius: 20px;
4365
+ box-shadow:
4366
+ 0 16px 48px rgba(1, 5, 7, 0.12),
4367
+ 0 2px 6px rgba(1, 5, 7, 0.05);
4368
+ padding: 32px;
4369
+ width: 340px;
4370
+ max-width: 100%;
4371
+ display: flex;
4372
+ flex-direction: column;
4373
+ align-items: center;
4374
+ gap: 16px;
4375
+ text-align: center;
4376
+ font-family: &quot;Plus Jakarta Sans&quot;, system-ui, sans-serif;
4377
+ "
4378
+ >
4379
+ <div
4380
+ style="
4381
+ width: 56px;
4382
+ height: 56px;
4383
+ border-radius: 999px;
4384
+ background: linear-gradient(135deg, #bec2ff 0%, #85ecce 100%);
4385
+ display: flex;
4386
+ align-items: center;
4387
+ justify-content: center;
4388
+ "
4389
+ >
4390
+ <svg
4391
+ width="24"
4392
+ height="24"
4393
+ viewBox="0 0 24 24"
4394
+ fill="none"
4395
+ stroke="#010507"
4396
+ stroke-width="2.5"
4397
+ stroke-linecap="round"
4398
+ stroke-linejoin="round"
4399
+ >
4400
+ <polyline points="20 6 9 17 4 12"></polyline>
4401
+ </svg>
4402
+ </div>
4403
+ <div style="font-size: 18px; font-weight: 700; color: #010507">
4404
+ Welcome to Threads
4405
+ </div>
4406
+ <div style="font-size: 13px; color: #5c5c66; line-height: 1.5">
4407
+ Loading your conversations…
4408
+ </div>
4409
+ </div>
4410
+ `;
4411
+ }
4412
+ _submitThreadsCode(value) {
4413
+ if (value.trim().toLowerCase() === "earlyaccess") {
4414
+ document.cookie = "cpk_threads_access=1; path=/; max-age=31536000; SameSite=Lax";
4415
+ this._threadsGateError = null;
4416
+ this._threadsGateCodeInvalid = false;
4417
+ this._threadsUnlocking = true;
4418
+ if (this._threadsUnlockingTimer !== null) clearTimeout(this._threadsUnlockingTimer);
4419
+ this._threadsUnlockingTimer = setTimeout(() => {
4420
+ this._threadsUnlocking = false;
4421
+ this._threadsUnlocked = true;
4422
+ this._threadsUnlockingTimer = null;
4423
+ this.requestUpdate();
4424
+ }, 2e3);
4425
+ } else {
4426
+ this._threadsGateCodeInvalid = true;
4427
+ this._threadsGateError = null;
4428
+ if (this._threadsGateInvalidTimer !== null) clearTimeout(this._threadsGateInvalidTimer);
4429
+ this._threadsGateInvalidTimer = setTimeout(() => {
4430
+ this._threadsGateCodeInvalid = false;
4431
+ this._threadsGateInvalidTimer = null;
4432
+ this.requestUpdate();
4433
+ }, 1600);
4434
+ }
4435
+ this.requestUpdate();
4436
+ }
4437
+ renderThreadsView() {
4438
+ if (!this._threadsUnlocked) return this.renderThreadsGate();
4439
+ const displayThreads = this.selectedContext === "all-agents" ? this._threads : this._threadsByAgent.get(this.selectedContext) ?? [];
4440
+ let threadsErrorMessage = null;
4441
+ if (this.selectedContext === "all-agents") threadsErrorMessage = this._threadsErrorByAgent.values().next().value?.message ?? null;
4442
+ else threadsErrorMessage = this._threadsErrorByAgent.get(this.selectedContext)?.message ?? null;
4443
+ const selectedThread = this.selectedThreadId != null ? displayThreads.find((t) => t.id === this.selectedThreadId) ?? null : null;
4444
+ return lit.html`
4445
+ <div style="display:flex;height:100%;overflow:hidden;">
4446
+ <!-- Left sidebar: thread list -->
4447
+ <div
4448
+ style="width:${this.threadListWidth}px;flex-shrink:0;overflow:hidden;display:flex;flex-direction:column;border-right:1px solid #DBDBE5;"
4449
+ >
4450
+ <cpk-thread-list
4451
+ style="height:100%;"
4452
+ .threads=${displayThreads}
4453
+ .selectedThreadId=${this.selectedThreadId}
4454
+ .errorMessage=${threadsErrorMessage}
4455
+ @threadSelected=${(e) => {
4456
+ this.selectedThreadId = e.detail;
4457
+ this.requestUpdate();
4458
+ }}
4459
+ ></cpk-thread-list>
4460
+ </div>
4461
+
4462
+ <!-- Resize divider -->
4463
+ <div
4464
+ style="width:4px;flex-shrink:0;cursor:col-resize;background:transparent;position:relative;z-index:1;"
4465
+ @pointerdown=${this.handleThreadDividerPointerDown}
4466
+ @pointermove=${this.handleThreadDividerPointerMove}
4467
+ @pointerup=${this.handleThreadDividerPointerUp}
4468
+ @pointercancel=${this.handleThreadDividerPointerUp}
4469
+ ></div>
4470
+
4471
+ <!-- Center + right: thread details or empty state -->
4472
+ <div style="flex:1;min-width:0;overflow:hidden;display:flex;">
4473
+ ${this.selectedThreadId ? lit.html`<cpk-thread-details
4474
+ style="flex:1;min-width:0;"
4475
+ .threadId=${this.selectedThreadId}
4476
+ .thread=${selectedThread}
4477
+ .runtimeUrl=${this._core?.runtimeUrl ?? ""}
4478
+ .headers=${this._core?.headers ?? {}}
4479
+ .liveMessageVersion=${this.selectedThreadId ? this.liveMessageVersion.get(this.selectedThreadId) ?? 0 : 0}
4480
+ .agentStateInput=${selectedThread ? this.getLatestStateForAgent(selectedThread.agentId) : null}
4481
+ .agentEventsInput=${selectedThread ? this.agentEvents.get(selectedThread.agentId) ?? [] : []}
4482
+ ></cpk-thread-details>` : lit.html`
4483
+ <div
4484
+ style="
4485
+ flex: 1;
4486
+ display: flex;
4487
+ flex-direction: column;
4488
+ align-items: center;
4489
+ justify-content: center;
4490
+ gap: 8px;
4491
+ color: #838389;
4492
+ "
4493
+ >
4494
+ <svg
4495
+ width="32"
4496
+ height="32"
4497
+ viewBox="0 0 24 24"
4498
+ fill="none"
4499
+ stroke="#c0c0c8"
4500
+ stroke-width="1.5"
4501
+ stroke-linecap="round"
4502
+ stroke-linejoin="round"
4503
+ >
4504
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
4505
+ </svg>
4506
+ <span style="font-size: 13px">${displayThreads.length === 0 ? "No threads yet" : "Select a thread to inspect"}</span>
4507
+ </div>
4508
+ `}
4509
+ </div>
4510
+ </div>
4511
+ `;
4512
+ }
1766
4513
  renderEventsTable() {
1767
4514
  const events = this.getEventsForSelectedContext();
1768
4515
  const filteredEvents = this.filterEvents(events);
1769
4516
  const selectedLabel = this.selectedContext === "all-agents" ? "all agents" : `agent ${this.selectedContext}`;
1770
4517
  if (events.length === 0) return lit.html`
1771
4518
  <div
1772
- class="flex h-full items-center justify-center px-4 py-8 text-center"
4519
+ class="flex h-full flex-col items-center justify-center gap-2 px-4 py-10 text-center"
1773
4520
  >
1774
- <div class="max-w-md">
1775
- <div
1776
- class="mb-3 flex justify-center text-gray-300 [&>svg]:!h-8 [&>svg]:!w-8"
1777
- >
1778
- ${this.renderIcon("Zap")}
1779
- </div>
1780
- <p class="text-sm text-gray-600">No events yet</p>
1781
- <p class="mt-2 text-xs text-gray-500">
1782
- Trigger an agent run to see live activity.
1783
- </p>
4521
+ <div class="text-gray-300 [&>svg]:!h-8 [&>svg]:!w-8">
4522
+ ${this.renderIcon("Zap")}
1784
4523
  </div>
4524
+ <span class="text-sm text-gray-600">No events yet</span>
4525
+ <span class="max-w-[240px] text-xs leading-snug text-gray-400"
4526
+ >Events are recorded live. Run the agent to see them here.</span
4527
+ >
1785
4528
  </div>
1786
4529
  `;
1787
4530
  if (filteredEvents.length === 0) return lit.html`
@@ -1878,23 +4621,32 @@ ${argsString}</pre
1878
4621
  </div>
1879
4622
  <div class="relative h-full w-full overflow-y-auto overflow-x-hidden">
1880
4623
  <table class="w-full table-fixed border-collapse text-xs box-border">
4624
+ <colgroup>
4625
+ <col style="width:${this.evtColWidths[0]}px">
4626
+ <col style="width:${this.evtColWidths[1]}px">
4627
+ <col style="width:${this.evtColWidths[2]}px">
4628
+ <col>
4629
+ </colgroup>
1881
4630
  <thead class="sticky top-0 z-10">
1882
4631
  <tr class="bg-white">
4632
+ ${[
4633
+ "Agent",
4634
+ "Time",
4635
+ "Event Type"
4636
+ ].map((label, col) => lit.html`
1883
4637
  <th
1884
4638
  class="border-b border-gray-200 bg-white px-3 py-2 text-left font-medium text-gray-900"
4639
+ style="position:relative;overflow:hidden;"
1885
4640
  >
1886
- Agent
1887
- </th>
1888
- <th
1889
- class="border-b border-gray-200 bg-white px-3 py-2 text-left font-medium text-gray-900"
1890
- >
1891
- Time
1892
- </th>
1893
- <th
1894
- class="border-b border-gray-200 bg-white px-3 py-2 text-left font-medium text-gray-900"
1895
- >
1896
- Event Type
1897
- </th>
4641
+ ${label}
4642
+ <div
4643
+ style="position:absolute;top:0;right:0;width:5px;height:100%;cursor:col-resize;user-select:none;background:transparent;"
4644
+ @pointerdown=${(e) => this._onEvtColResizeStart(e, col)}
4645
+ @pointermove=${(e) => this._onEvtColResizeMove(e)}
4646
+ @pointerup=${() => this._onEvtColResizeEnd()}
4647
+ @pointercancel=${() => this._onEvtColResizeEnd()}
4648
+ ></div>
4649
+ </th>`)}
1898
4650
  <th
1899
4651
  class="border-b border-gray-200 bg-white px-3 py-2 text-left font-medium text-gray-900"
1900
4652
  >
@@ -1982,6 +4734,25 @@ ${prettyEvent}</pre
1982
4734
  this.eventTypeFilter = "all";
1983
4735
  this.requestUpdate();
1984
4736
  }
4737
+ _onEvtColResizeStart(e, col) {
4738
+ e.preventDefault();
4739
+ e.stopPropagation();
4740
+ e.currentTarget.setPointerCapture(e.pointerId);
4741
+ this._evtColResize = {
4742
+ col,
4743
+ startX: e.clientX,
4744
+ startW: this.evtColWidths[col] ?? 0
4745
+ };
4746
+ }
4747
+ _onEvtColResizeMove(e) {
4748
+ if (!this._evtColResize) return;
4749
+ const { col, startX, startW } = this._evtColResize;
4750
+ this.evtColWidths = this.evtColWidths.map((w, i) => i === col ? Math.max(40, startW + (e.clientX - startX)) : w);
4751
+ this.requestUpdate();
4752
+ }
4753
+ _onEvtColResizeEnd() {
4754
+ this._evtColResize = null;
4755
+ }
1985
4756
  exportEvents(events) {
1986
4757
  try {
1987
4758
  const payload = JSON.stringify(events, null, 2);
@@ -2026,7 +4797,7 @@ ${prettyEvent}</pre
2026
4797
  <div class="flex items-start justify-between mb-4">
2027
4798
  <div class="flex items-center gap-3">
2028
4799
  <div
2029
- class="flex h-10 w-10 items-center justify-center rounded-lg bg-blue-100 text-blue-600"
4800
+ class="flex h-10 w-10 items-center justify-center rounded-lg bg-blue-100 text-blue-600 cpk-agent-icon"
2030
4801
  >
2031
4802
  ${this.renderIcon("Bot")}
2032
4803
  </div>
@@ -2054,7 +4825,7 @@ ${prettyEvent}</pre
2054
4825
  <div class="grid grid-cols-2 gap-4 md:grid-cols-4">
2055
4826
  <button
2056
4827
  type="button"
2057
- class="rounded-md bg-gray-50 px-3 py-2 text-left transition hover:bg-gray-100 cursor-pointer overflow-hidden"
4828
+ class="rounded-md bg-gray-50 px-3 py-2 text-left transition hover:bg-gray-100 cursor-pointer overflow-hidden cpk-stat-card"
2058
4829
  @click=${() => this.handleMenuSelect("ag-ui-events")}
2059
4830
  title="View all events in AG-UI Events"
2060
4831
  >
@@ -2065,7 +4836,9 @@ ${prettyEvent}</pre
2065
4836
  ${stats.totalEvents}
2066
4837
  </div>
2067
4838
  </button>
2068
- <div class="rounded-md bg-gray-50 px-3 py-2 overflow-hidden">
4839
+ <div
4840
+ class="rounded-md bg-gray-50 px-3 py-2 overflow-hidden cpk-stat-card"
4841
+ >
2069
4842
  <div class="truncate whitespace-nowrap text-xs text-gray-600">
2070
4843
  Messages
2071
4844
  </div>
@@ -2073,7 +4846,9 @@ ${prettyEvent}</pre
2073
4846
  ${stats.messages}
2074
4847
  </div>
2075
4848
  </div>
2076
- <div class="rounded-md bg-gray-50 px-3 py-2 overflow-hidden">
4849
+ <div
4850
+ class="rounded-md bg-gray-50 px-3 py-2 overflow-hidden cpk-stat-card"
4851
+ >
2077
4852
  <div class="truncate whitespace-nowrap text-xs text-gray-600">
2078
4853
  Tool Calls
2079
4854
  </div>
@@ -2081,7 +4856,9 @@ ${prettyEvent}</pre
2081
4856
  ${stats.toolCalls}
2082
4857
  </div>
2083
4858
  </div>
2084
- <div class="rounded-md bg-gray-50 px-3 py-2 overflow-hidden">
4859
+ <div
4860
+ class="rounded-md bg-gray-50 px-3 py-2 overflow-hidden cpk-stat-card"
4861
+ >
2085
4862
  <div class="truncate whitespace-nowrap text-xs text-gray-600">
2086
4863
  Errors
2087
4864
  </div>
@@ -2093,9 +4870,9 @@ ${prettyEvent}</pre
2093
4870
  </div>
2094
4871
 
2095
4872
  <!-- Current State Section -->
2096
- <div class="rounded-lg border border-gray-200 bg-white">
2097
- <div class="border-b border-gray-200 px-4 py-3">
2098
- <h4 class="text-sm font-semibold text-gray-900">Current State</h4>
4873
+ <div class="cpk-section-card">
4874
+ <div class="cpk-section-header">
4875
+ <h4>Current State</h4>
2099
4876
  </div>
2100
4877
  <div class="overflow-auto p-4">
2101
4878
  ${this.hasRenderableState(state) ? lit.html`
@@ -2104,7 +4881,7 @@ ${prettyEvent}</pre
2104
4881
  ><code>${this.formatStateForDisplay(state)}</code></pre>
2105
4882
  ` : lit.html`
2106
4883
  <div
2107
- class="flex h-40 items-center justify-center text-xs text-gray-500"
4884
+ class="flex h-12 items-center justify-center text-xs text-gray-500"
2108
4885
  >
2109
4886
  <div class="flex items-center gap-2 text-gray-500">
2110
4887
  <span class="text-lg text-gray-400"
@@ -2118,30 +4895,18 @@ ${prettyEvent}</pre
2118
4895
  </div>
2119
4896
 
2120
4897
  <!-- Current Messages Section -->
2121
- <div class="rounded-lg border border-gray-200 bg-white">
2122
- <div class="border-b border-gray-200 px-4 py-3">
2123
- <h4 class="text-sm font-semibold text-gray-900">
2124
- Current Messages
2125
- </h4>
4898
+ <div class="cpk-section-card">
4899
+ <div class="cpk-section-header">
4900
+ <h4>Current Messages</h4>
2126
4901
  </div>
2127
4902
  <div class="overflow-auto">
2128
4903
  ${messages && messages.length > 0 ? lit.html`
2129
- <table class="w-full text-xs">
2130
- <thead class="bg-gray-50">
2131
- <tr>
2132
- <th
2133
- class="px-4 py-2 text-left font-medium text-gray-700"
2134
- >
2135
- Role
2136
- </th>
2137
- <th
2138
- class="px-4 py-2 text-left font-medium text-gray-700"
2139
- >
2140
- Content
2141
- </th>
2142
- </tr>
2143
- </thead>
2144
- <tbody class="divide-y divide-gray-200">
4904
+ <div class="w-full text-xs">
4905
+ <div class="flex bg-gray-50">
4906
+ <div class="w-40 shrink-0 px-4 py-2 font-medium text-gray-700">Role</div>
4907
+ <div class="flex-1 px-4 py-2 font-medium text-gray-700">Content</div>
4908
+ </div>
4909
+ <div class="divide-y divide-gray-200">
2145
4910
  ${messages.map((msg) => {
2146
4911
  const role = msg.role || "unknown";
2147
4912
  const roleColors = {
@@ -2156,34 +4921,32 @@ ${prettyEvent}</pre
2156
4921
  const hasContent = rawContent.trim().length > 0;
2157
4922
  const contentFallback = toolCalls.length > 0 ? "Invoked tool call" : "—";
2158
4923
  return lit.html`
2159
- <tr>
2160
- <td class="px-4 py-2 align-top">
4924
+ <div class="flex items-start">
4925
+ <div class="w-40 shrink-0 px-4 py-2">
2161
4926
  <span
2162
4927
  class="inline-flex rounded px-2 py-0.5 text-[10px] font-medium ${roleColors[role] || roleColors.unknown}"
2163
4928
  >
2164
4929
  ${role}
2165
4930
  </span>
2166
- </td>
2167
- <td class="px-4 py-2">
4931
+ </div>
4932
+ <div class="flex-1 px-4 py-2">
2168
4933
  ${hasContent ? lit.html`<div
2169
- class="max-w-2xl whitespace-pre-wrap break-words text-gray-700"
4934
+ class="whitespace-pre-line break-words text-gray-700"
2170
4935
  >
2171
4936
  ${rawContent}
2172
- </div>` : lit.html`<div
2173
- class="text-xs italic text-gray-400"
2174
- >
4937
+ </div>` : lit.html`<div class="italic text-gray-400">
2175
4938
  ${contentFallback}
2176
4939
  </div>`}
2177
4940
  ${role === "assistant" && toolCalls.length > 0 ? this.renderToolCallDetails(toolCalls) : lit.nothing}
2178
- </td>
2179
- </tr>
4941
+ </div>
4942
+ </div>
2180
4943
  `;
2181
4944
  })}
2182
- </tbody>
2183
- </table>
4945
+ </div>
4946
+ </div>
2184
4947
  ` : lit.html`
2185
4948
  <div
2186
- class="flex h-40 items-center justify-center text-xs text-gray-500"
4949
+ class="flex h-12 items-center justify-center text-xs text-gray-500"
2187
4950
  >
2188
4951
  <div class="flex items-center gap-2 text-gray-500">
2189
4952
  <span class="text-lg text-gray-400"
@@ -2244,14 +5007,29 @@ ${prettyEvent}</pre
2244
5007
  }
2245
5008
  handleMenuSelect(key) {
2246
5009
  if (!this.menuItems.some((item) => item.key === key)) return;
5010
+ const previousMenu = this.selectedMenu;
2247
5011
  this.selectedMenu = key;
2248
5012
  if (key === "agents" && this.selectedContext === "all-agents") {
2249
5013
  const agentOptions = this.contextOptions.filter((opt) => opt.key !== "all-agents");
2250
5014
  if (agentOptions.length > 0) {
2251
- const defaultAgent = agentOptions.find((opt) => opt.key === "default");
2252
- this.selectedContext = defaultAgent ? defaultAgent.key : agentOptions[0].key;
5015
+ const mostRecent = agentOptions.reduce((best, opt) => {
5016
+ const ts = this.getAgentStats(opt.key).lastActivity ?? -1;
5017
+ return best === null || ts > best.ts ? {
5018
+ key: opt.key,
5019
+ ts
5020
+ } : best;
5021
+ }, null);
5022
+ this.selectedContext = mostRecent ? mostRecent.key : agentOptions[0].key;
2253
5023
  }
2254
5024
  }
5025
+ if (previousMenu === "agents" && key !== "agents") {
5026
+ if (this.contextOptions.filter((opt) => opt.key !== "all-agents").length > 1) this.selectedContext = "all-agents";
5027
+ }
5028
+ if (key === "threads") this.autoSelectLatestThread();
5029
+ if (key === "ag-ui-events" || key === "agents") requestAnimationFrame(() => {
5030
+ const scroller = this.shadowRoot?.getElementById("cpk-main-scroll");
5031
+ if (scroller) scroller.scrollTop = 0;
5032
+ });
2255
5033
  this.contextMenuOpen = false;
2256
5034
  this.persistState();
2257
5035
  this.requestUpdate();
@@ -2639,9 +5417,19 @@ ${prettyEvent}</pre
2639
5417
  <div class="mb-3">
2640
5418
  <h5 class="mb-1 text-xs font-semibold text-gray-700">ID</h5>
2641
5419
  <code
2642
- class="block rounded bg-white border border-gray-200 px-2 py-1 text-[10px] font-mono text-gray-600"
5420
+ class="font-mono text-xs font-medium text-gray-800 flex-1 truncate min-w-0"
2643
5421
  >${id}</code
2644
5422
  >
5423
+ <button
5424
+ type="button"
5425
+ class="cpk-copy-btn"
5426
+ @click=${(e) => {
5427
+ e.stopPropagation();
5428
+ this.copyContextValue(id, `${id}:id`);
5429
+ }}
5430
+ >
5431
+ ${this.copiedContextItems.has(`${id}:id`) ? "✓" : "Copy"}
5432
+ </button>
2645
5433
  </div>
2646
5434
  ${hasValue ? lit.html`
2647
5435
  <div class="mb-2 flex items-center justify-between gap-2">
@@ -2649,8 +5437,8 @@ ${prettyEvent}</pre
2649
5437
  Value
2650
5438
  </h5>
2651
5439
  <button
2652
- class="flex items-center gap-1 rounded-md border border-gray-200 bg-white px-2 py-1 text-[10px] font-medium text-gray-700 transition hover:bg-gray-50"
2653
5440
  type="button"
5441
+ class="cpk-copy-btn"
2654
5442
  @click=${(e) => {
2655
5443
  e.stopPropagation();
2656
5444
  this.copyContextValue(context.value, id);
@@ -2659,13 +5447,6 @@ ${prettyEvent}</pre
2659
5447
  ${this.copiedContextItems.has(id) ? "Copied" : "Copy JSON"}
2660
5448
  </button>
2661
5449
  </div>
2662
- <div
2663
- class="rounded-md border border-gray-200 bg-white p-3"
2664
- >
2665
- <pre
2666
- class="overflow-auto text-xs text-gray-800 max-h-96"
2667
- ><code>${this.formatContextValue(context.value)}</code></pre>
2668
- </div>
2669
5450
  ` : lit.html`
2670
5451
  <div class="flex items-center justify-center py-4 text-xs text-gray-500">
2671
5452
  <span>No value available</span>
@@ -2678,7 +5459,7 @@ ${prettyEvent}</pre
2678
5459
  }
2679
5460
  getContextValuePreview(value) {
2680
5461
  if (value === void 0 || value === null) return "—";
2681
- if (typeof value === "string") return value.length > 50 ? `${value.substring(0, 50)}...` : value;
5462
+ if (typeof value === "string") return value.length > 50 ? `${value.slice(0, 50)}...` : value;
2682
5463
  if (typeof value === "number" || typeof value === "boolean") return String(value);
2683
5464
  if (Array.isArray(value)) return `Array(${value.length})`;
2684
5465
  if (typeof value === "object") {
@@ -2728,50 +5509,25 @@ ${prettyEvent}</pre
2728
5509
  else this.expandedRows.add(eventId);
2729
5510
  this.requestUpdate();
2730
5511
  }
2731
- renderAnnouncementPanel() {
2732
- if (!this.isOpen) return lit.nothing;
2733
- this.ensureAnnouncementLoading();
5512
+ renderAnnouncementBanner() {
2734
5513
  if (!this.hasUnseenAnnouncement) return lit.nothing;
2735
- if (!this.announcementLoaded && !this.announcementMarkdown) return lit.html`<div
2736
- class="mx-4 my-3 rounded-xl border border-slate-200 bg-white px-4 py-3 text-sm text-slate-800 shadow-[0_12px_30px_rgba(15,23,42,0.12)]"
2737
- >
2738
- <div class="flex items-center gap-2 font-semibold">
2739
- <span
2740
- class="inline-flex h-6 w-6 items-center justify-center rounded-md bg-slate-900 text-white shadow-sm"
2741
- >
2742
- ${this.renderIcon("Megaphone")}
2743
- </span>
2744
- <span>Loading latest announcement…</span>
2745
- </div>
2746
- </div>`;
2747
- if (this.announcementLoadError) return lit.html`<div
2748
- class="mx-4 my-3 rounded-xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-900 shadow-[0_12px_30px_rgba(15,23,42,0.12)]"
5514
+ if (!this.announcementLoaded && !this.announcementHtml) return lit.html`<div
5515
+ class="flex items-center gap-2 px-4 py-3 text-sm font-semibold text-slate-800"
2749
5516
  >
2750
- <div class="flex items-center gap-2 font-semibold">
2751
- <span
2752
- class="inline-flex h-6 w-6 items-center justify-center rounded-md bg-rose-600 text-white shadow-sm"
2753
- >
2754
- ${this.renderIcon("Megaphone")}
2755
- </span>
2756
- <span>Announcement unavailable</span>
2757
- </div>
2758
- <p class="mt-2 text-xs text-rose-800">
2759
- We couldn’t load the latest notice. Please try opening the inspector
2760
- again.
2761
- </p>
5517
+ <span
5518
+ class="inline-flex h-6 w-6 items-center justify-center rounded-md bg-slate-900 text-white shadow-sm"
5519
+ >
5520
+ ${this.renderIcon("Megaphone")}
5521
+ </span>
5522
+ <span>Loading latest announcement…</span>
2762
5523
  </div>`;
2763
- if (!this.announcementMarkdown) return lit.nothing;
2764
- const content = this.announcementHtml ? (0, lit_directives_unsafe_html_js.unsafeHTML)(this.announcementHtml) : lit.html`<pre class="whitespace-pre-wrap text-sm text-gray-900">
2765
- ${this.announcementMarkdown}</pre
2766
- >`;
2767
- return lit.html`<div
2768
- class="mx-4 my-3 rounded-xl border border-slate-200 bg-white px-4 py-4 shadow-[0_12px_30px_rgba(15,23,42,0.12)]"
2769
- >
5524
+ if (!this.announcementHtml) return lit.nothing;
5525
+ return lit.html`<div class="mx-4 mb-3 rounded-xl border border-slate-200 bg-white px-4 py-3">
2770
5526
  <div
2771
- class="mb-3 flex items-center gap-2 text-sm font-semibold text-slate-900"
5527
+ class="mb-2 flex items-center gap-2 text-xs font-semibold text-slate-900"
2772
5528
  >
2773
5529
  <span
2774
- class="inline-flex h-7 w-7 items-center justify-center rounded-md bg-slate-900 text-white shadow-sm"
5530
+ class="inline-flex h-5 w-5 items-center justify-center rounded-md bg-slate-900 text-white shadow-sm"
2775
5531
  >
2776
5532
  ${this.renderIcon("Megaphone")}
2777
5533
  </span>
@@ -2782,12 +5538,27 @@ ${this.announcementMarkdown}</pre
2782
5538
  @click=${this.handleDismissAnnouncement}
2783
5539
  aria-label="Dismiss announcement"
2784
5540
  >
2785
- Dismiss
5541
+ ${this.renderIcon("X")}
2786
5542
  </button>
2787
5543
  </div>
2788
- <div class="announcement-content text-sm leading-relaxed text-gray-900">
2789
- ${content}
5544
+ <div class="announcement-body ${this.announcementExpanded ? "announcement-body--expanded" : "announcement-body--collapsed"}">
5545
+ <div class="announcement-content">
5546
+ ${(0, lit_directives_unsafe_html_js.unsafeHTML)(this.announcementHtml)}
5547
+ </div>
5548
+ ${!this.announcementExpanded ? lit.html`
5549
+ <div class="announcement-fade"></div>
5550
+ ` : lit.nothing}
2790
5551
  </div>
5552
+ <button
5553
+ class="announcement-toggle"
5554
+ type="button"
5555
+ @click=${() => {
5556
+ this.announcementExpanded = !this.announcementExpanded;
5557
+ this.requestUpdate();
5558
+ }}
5559
+ >
5560
+ ${this.announcementExpanded ? "Show less ↑" : "Show more ↓"}
5561
+ </button>
2791
5562
  </div>`;
2792
5563
  }
2793
5564
  ensureAnnouncementLoading() {
@@ -2822,14 +5593,13 @@ ${this.announcementMarkdown}</pre
2822
5593
  const storedTimestamp = this.loadStoredAnnouncementTimestamp();
2823
5594
  this.announcementTimestamp = timestamp;
2824
5595
  this.announcementPreviewText = previewText ?? "";
2825
- this.announcementMarkdown = markdown;
2826
5596
  this.hasUnseenAnnouncement = (!storedTimestamp || storedTimestamp !== timestamp) && !!this.announcementPreviewText;
2827
5597
  this.showAnnouncementPreview = this.hasUnseenAnnouncement;
2828
5598
  this.announcementHtml = await this.convertMarkdownToHtml(markdown);
2829
5599
  this.announcementLoaded = true;
2830
5600
  this.requestUpdate();
2831
5601
  } catch (error) {
2832
- this.announcementLoadError = error;
5602
+ console.warn("[CopilotKit Inspector] Failed to load announcement", error);
2833
5603
  this.announcementLoaded = true;
2834
5604
  this.requestUpdate();
2835
5605
  }
@@ -2892,4 +5662,5 @@ defineWebInspector();
2892
5662
  exports.WEB_INSPECTOR_TAG = WEB_INSPECTOR_TAG;
2893
5663
  exports.WebInspectorElement = WebInspectorElement;
2894
5664
  exports.defineWebInspector = defineWebInspector;
5665
+ exports.ɵCpkThreadDetails = ɵCpkThreadDetails;
2895
5666
  //# sourceMappingURL=index.cjs.map