@copilotkit/web-inspector 1.56.5 → 1.57.0-canary.1778078321

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 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,7 @@ 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.liveMessageVersion = /* @__PURE__ */ new Map();
70
1825
  this.agentStates = /* @__PURE__ */ new Map();
71
1826
  this.flattenedEvents = [];
72
1827
  this.eventCounter = 0;
@@ -83,6 +1838,17 @@ var WebInspectorElement = class extends lit.LitElement {
83
1838
  this.draggedDuringInteraction = false;
84
1839
  this.ignoreNextButtonClick = false;
85
1840
  this.selectedMenu = "ag-ui-events";
1841
+ this.selectedThreadId = null;
1842
+ this.threadListWidth = 290;
1843
+ this.threadDividerResizing = false;
1844
+ this.threadDividerPointerId = -1;
1845
+ this.threadDividerStartX = 0;
1846
+ this.threadDividerStartWidth = 0;
1847
+ this._threads = [];
1848
+ this._threadStoreSubscriptions = /* @__PURE__ */ new Map();
1849
+ this._threadsByAgent = /* @__PURE__ */ new Map();
1850
+ this._threadsErrorByAgent = /* @__PURE__ */ new Map();
1851
+ this._ownedThreadStores = /* @__PURE__ */ new Map();
86
1852
  this.contextMenuOpen = false;
87
1853
  this.dockMode = "floating";
88
1854
  this.previousBodyMargins = null;
@@ -95,15 +1861,20 @@ var WebInspectorElement = class extends lit.LitElement {
95
1861
  this.toolSignature = "";
96
1862
  this.eventFilterText = "";
97
1863
  this.eventTypeFilter = "all";
98
- this.announcementMarkdown = null;
1864
+ this.evtColWidths = [
1865
+ 100,
1866
+ 80,
1867
+ 150
1868
+ ];
1869
+ this._evtColResize = null;
99
1870
  this.announcementHtml = null;
100
1871
  this.announcementTimestamp = null;
101
1872
  this.announcementPreviewText = null;
102
1873
  this.hasUnseenAnnouncement = false;
103
1874
  this.announcementLoaded = false;
104
- this.announcementLoadError = null;
105
1875
  this.announcementPromise = null;
106
1876
  this.showAnnouncementPreview = true;
1877
+ this.announcementExpanded = false;
107
1878
  this.contextState = {
108
1879
  button: {
109
1880
  position: {
@@ -144,28 +1915,7 @@ var WebInspectorElement = class extends lit.LitElement {
144
1915
  this.resizeStart = null;
145
1916
  this.resizeInitialSize = null;
146
1917
  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
- ];
1918
+ 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
1919
  this.handlePointerDown = (event) => {
170
1920
  if (this.dockMode !== "floating" && this.isOpen) return;
171
1921
  const target = event.currentTarget;
@@ -330,6 +2080,26 @@ var WebInspectorElement = class extends lit.LitElement {
330
2080
  this.expandedTools = /* @__PURE__ */ new Set();
331
2081
  this.expandedContextItems = /* @__PURE__ */ new Set();
332
2082
  this.copiedContextItems = /* @__PURE__ */ new Set();
2083
+ this.handleThreadDividerPointerDown = (event) => {
2084
+ this.threadDividerResizing = true;
2085
+ this.threadDividerPointerId = event.pointerId;
2086
+ this.threadDividerStartX = event.clientX;
2087
+ this.threadDividerStartWidth = this.threadListWidth;
2088
+ event.currentTarget.setPointerCapture(event.pointerId);
2089
+ event.preventDefault();
2090
+ };
2091
+ this.handleThreadDividerPointerMove = (event) => {
2092
+ if (!this.threadDividerResizing || this.threadDividerPointerId !== event.pointerId) return;
2093
+ const delta = event.clientX - this.threadDividerStartX;
2094
+ this.threadListWidth = Math.max(180, Math.min(480, this.threadDividerStartWidth + delta));
2095
+ this.requestUpdate();
2096
+ };
2097
+ this.handleThreadDividerPointerUp = (event) => {
2098
+ if (this.threadDividerPointerId !== event.pointerId) return;
2099
+ const target = event.currentTarget;
2100
+ if (target.hasPointerCapture(this.threadDividerPointerId)) target.releasePointerCapture(this.threadDividerPointerId);
2101
+ this.threadDividerResizing = false;
2102
+ };
333
2103
  this.handleClearEvents = () => {
334
2104
  if (this.selectedContext === "all-agents") {
335
2105
  this.agentEvents.clear();
@@ -354,6 +2124,31 @@ var WebInspectorElement = class extends lit.LitElement {
354
2124
  this.handleDismissAnnouncement = () => {
355
2125
  this.markAnnouncementSeen();
356
2126
  };
2127
+ this.copyResetTimeouts = /* @__PURE__ */ new WeakMap();
2128
+ this.handleAnnouncementContentClick = (event) => {
2129
+ const button = (event.target instanceof HTMLElement ? event.target : null)?.closest(".announcement-code__copy");
2130
+ if (!(button instanceof HTMLButtonElement)) return;
2131
+ event.preventDefault();
2132
+ event.stopPropagation();
2133
+ const encoded = button.getAttribute("data-copy") ?? "";
2134
+ const code = this.decodeBase64(encoded);
2135
+ if (!code) return;
2136
+ const showCopied = () => {
2137
+ const existing = this.copyResetTimeouts.get(button);
2138
+ if (existing !== void 0) window.clearTimeout(existing);
2139
+ button.setAttribute("data-copied", "true");
2140
+ button.setAttribute("aria-label", "Code copied");
2141
+ button.textContent = "Copied";
2142
+ const id = window.setTimeout(() => {
2143
+ button.removeAttribute("data-copied");
2144
+ button.setAttribute("aria-label", "Copy code");
2145
+ button.textContent = "Copy";
2146
+ this.copyResetTimeouts.delete(button);
2147
+ }, 1500);
2148
+ this.copyResetTimeouts.set(button, id);
2149
+ };
2150
+ if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) navigator.clipboard.writeText(code).then(showCopied, () => {});
2151
+ };
357
2152
  }
358
2153
  static {
359
2154
  this.properties = {
@@ -375,6 +2170,106 @@ var WebInspectorElement = class extends lit.LitElement {
375
2170
  this.requestUpdate("core", oldValue);
376
2171
  if (this._core) this.attachToCore(this._core);
377
2172
  }
2173
+ get menuItems() {
2174
+ return [
2175
+ {
2176
+ key: "ag-ui-events",
2177
+ label: "AG-UI Events",
2178
+ icon: "Zap"
2179
+ },
2180
+ {
2181
+ key: "agents",
2182
+ label: "Agent",
2183
+ icon: "Bot"
2184
+ },
2185
+ ...(this._core?.tools?.length ?? 0) > 0 ? [{
2186
+ key: "frontend-tools",
2187
+ label: "Frontend Tools",
2188
+ icon: "Hammer"
2189
+ }] : [],
2190
+ {
2191
+ key: "agent-context",
2192
+ label: "Context",
2193
+ icon: "FileText"
2194
+ },
2195
+ {
2196
+ key: "threads",
2197
+ label: "Threads",
2198
+ icon: "MessageSquare"
2199
+ }
2200
+ ];
2201
+ }
2202
+ subscribeToThreadStore(agentId, store) {
2203
+ if (this._threadStoreSubscriptions.has(agentId)) return;
2204
+ const threadsSub = store.select(_copilotkit_core.ɵselectThreads).subscribe((threads) => {
2205
+ this._threadsByAgent.set(agentId, threads);
2206
+ this._threads = Array.from(this._threadsByAgent.values()).flat();
2207
+ this.autoSelectLatestThread();
2208
+ this.requestUpdate();
2209
+ });
2210
+ const errorSub = store.select(_copilotkit_core.ɵselectThreadsError).subscribe((error) => {
2211
+ if (error) this._threadsErrorByAgent.set(agentId, error);
2212
+ else this._threadsErrorByAgent.delete(agentId);
2213
+ this.requestUpdate();
2214
+ });
2215
+ this._threadStoreSubscriptions.set(agentId, () => {
2216
+ threadsSub.unsubscribe();
2217
+ errorSub.unsubscribe();
2218
+ });
2219
+ const initialState = store.getState();
2220
+ this._threadsByAgent.set(agentId, (0, _copilotkit_core.ɵselectThreads)(initialState));
2221
+ const initialError = (0, _copilotkit_core.ɵselectThreadsError)(initialState);
2222
+ if (initialError) this._threadsErrorByAgent.set(agentId, initialError);
2223
+ else this._threadsErrorByAgent.delete(agentId);
2224
+ this._threads = Array.from(this._threadsByAgent.values()).flat();
2225
+ this.autoSelectLatestThread();
2226
+ }
2227
+ autoSelectLatestThread() {
2228
+ if (this._threads.length === 0) return;
2229
+ if (!(this.selectedThreadId != null && this._threads.some((t) => t.id === this.selectedThreadId))) this.selectedThreadId = this._threads[0].id;
2230
+ }
2231
+ teardownThreadStoreSubscriptions() {
2232
+ for (const unsub of this._threadStoreSubscriptions.values()) unsub();
2233
+ this._threadStoreSubscriptions.clear();
2234
+ this._threadsByAgent.clear();
2235
+ this._threadsErrorByAgent.clear();
2236
+ this._threads = [];
2237
+ }
2238
+ ensureOwnedThreadStore(agentId) {
2239
+ if (this._ownedThreadStores.has(agentId)) return;
2240
+ if (this.core?.getThreadStore(agentId)) return;
2241
+ const core = this.core;
2242
+ if (!core?.runtimeUrl) return;
2243
+ const store = (0, _copilotkit_core.ɵcreateThreadStore)({ fetch: globalThis.fetch });
2244
+ store.start();
2245
+ store.setContext({
2246
+ runtimeUrl: core.runtimeUrl,
2247
+ headers: {},
2248
+ agentId
2249
+ });
2250
+ this._ownedThreadStores.set(agentId, store);
2251
+ this.subscribeToThreadStore(agentId, store);
2252
+ core.registerThreadStore(agentId, store);
2253
+ }
2254
+ refreshOwnedThreadStore(agentId) {
2255
+ const store = this._ownedThreadStores.get(agentId);
2256
+ if (!store) return;
2257
+ store.refresh();
2258
+ }
2259
+ removeOwnedThreadStore(agentId) {
2260
+ const store = this._ownedThreadStores.get(agentId);
2261
+ if (!store) return;
2262
+ store.stop();
2263
+ this.core?.unregisterThreadStore(agentId);
2264
+ this._ownedThreadStores.delete(agentId);
2265
+ }
2266
+ teardownOwnedThreadStores() {
2267
+ for (const [agentId, store] of this._ownedThreadStores) {
2268
+ store.stop();
2269
+ this.core?.unregisterThreadStore(agentId);
2270
+ }
2271
+ this._ownedThreadStores.clear();
2272
+ }
378
2273
  attachToCore(core) {
379
2274
  this.runtimeStatus = core.runtimeConnectionStatus;
380
2275
  this.coreProperties = core.properties;
@@ -382,6 +2277,11 @@ var WebInspectorElement = class extends lit.LitElement {
382
2277
  this.coreSubscriber = {
383
2278
  onRuntimeConnectionStatusChanged: ({ status }) => {
384
2279
  this.runtimeStatus = status;
2280
+ if (status === "connected") for (const agentId of this._ownedThreadStores.keys()) this.refreshOwnedThreadStore(agentId);
2281
+ else {
2282
+ this._threadsByAgent.clear();
2283
+ this._threads = [];
2284
+ }
385
2285
  this.requestUpdate();
386
2286
  },
387
2287
  onPropertiesChanged: ({ properties }) => {
@@ -398,16 +2298,30 @@ var WebInspectorElement = class extends lit.LitElement {
398
2298
  onAgentsChanged: ({ agents }) => {
399
2299
  this.processAgentsChanged(agents);
400
2300
  },
401
- onAgentRunStarted: ({ agent }) => {
402
- if (agent?.agentId) this.subscribeToAgent(agent);
2301
+ onContextChanged: ({ context }) => {
2302
+ this.contextStore = this.normalizeContextStore(context);
2303
+ this.requestUpdate();
2304
+ },
2305
+ onThreadStoreRegistered: ({ agentId, store }) => {
2306
+ this.subscribeToThreadStore(agentId, store);
2307
+ this.requestUpdate();
403
2308
  },
404
- onContextChanged: ({ context }) => {
405
- this.contextStore = this.normalizeContextStore(context);
2309
+ onThreadStoreUnregistered: ({ agentId }) => {
2310
+ const unsub = this._threadStoreSubscriptions.get(agentId);
2311
+ if (unsub) {
2312
+ unsub();
2313
+ this._threadStoreSubscriptions.delete(agentId);
2314
+ }
2315
+ this._threadsByAgent.delete(agentId);
2316
+ this._threadsErrorByAgent.delete(agentId);
2317
+ this._threads = Array.from(this._threadsByAgent.values()).flat();
406
2318
  this.requestUpdate();
407
2319
  }
408
2320
  };
409
2321
  this.coreUnsubscribe = core.subscribe(this.coreSubscriber).unsubscribe;
410
2322
  this.processAgentsChanged(core.agents);
2323
+ const threadStores = typeof core.getThreadStores === "function" ? core.getThreadStores() : {};
2324
+ for (const [agentId, store] of Object.entries(threadStores)) this.subscribeToThreadStore(agentId, store);
411
2325
  if (core.context) this.contextStore = this.normalizeContextStore(core.context);
412
2326
  }
413
2327
  detachFromCore() {
@@ -422,6 +2336,8 @@ var WebInspectorElement = class extends lit.LitElement {
422
2336
  this.cachedTools = [];
423
2337
  this.toolSignature = "";
424
2338
  this.teardownAgentSubscriptions();
2339
+ this.teardownThreadStoreSubscriptions();
2340
+ this.teardownOwnedThreadStores();
425
2341
  }
426
2342
  teardownAgentSubscriptions() {
427
2343
  for (const unsubscribe of this.agentSubscriptions.values()) unsubscribe();
@@ -438,6 +2354,7 @@ var WebInspectorElement = class extends lit.LitElement {
438
2354
  if (!agent?.agentId) continue;
439
2355
  seenAgentIds.add(agent.agentId);
440
2356
  this.subscribeToAgent(agent);
2357
+ this.ensureOwnedThreadStore(agent.agentId);
441
2358
  }
442
2359
  for (const agentId of Array.from(this.agentSubscriptions.keys())) if (!seenAgentIds.has(agentId)) {
443
2360
  this.unsubscribeFromAgent(agentId);
@@ -496,6 +2413,7 @@ var WebInspectorElement = class extends lit.LitElement {
496
2413
  event,
497
2414
  result
498
2415
  });
2416
+ this.refreshOwnedThreadStore(agentId);
499
2417
  },
500
2418
  onRunErrorEvent: ({ event }) => {
501
2419
  this.recordAgentEvent(agentId, "RUN_ERROR", event);
@@ -583,6 +2501,14 @@ var WebInspectorElement = class extends lit.LitElement {
583
2501
  },
584
2502
  onReasoningEncryptedValueEvent: ({ event }) => {
585
2503
  this.recordAgentEvent(agentId, "REASONING_ENCRYPTED_VALUE", event);
2504
+ },
2505
+ onActivitySnapshotEvent: ({ event }) => {
2506
+ this.recordAgentEvent(agentId, "ACTIVITY_SNAPSHOT", event);
2507
+ this.syncAgentMessages(agent);
2508
+ },
2509
+ onActivityDeltaEvent: ({ event }) => {
2510
+ this.recordAgentEvent(agentId, "ACTIVITY_DELTA", event);
2511
+ this.syncAgentMessages(agent);
586
2512
  }
587
2513
  });
588
2514
  this.agentSubscriptions.set(agentId, unsubscribe);
@@ -597,6 +2523,15 @@ var WebInspectorElement = class extends lit.LitElement {
597
2523
  this.agentSubscriptions.delete(agentId);
598
2524
  }
599
2525
  }
2526
+ mapMessagesToConversation(messages) {
2527
+ if (!messages) return null;
2528
+ return messages.filter((m) => m.role === "user" || m.role === "assistant" || m.role === "activity").map((m, i) => ({
2529
+ id: m.id ?? `msg-${i}`,
2530
+ type: m.role === "user" ? "user" : m.role === "activity" ? "generative-ui" : "assistant",
2531
+ content: m.role === "activity" ? m.activityType ?? "unknown" : m.contentText,
2532
+ createdAt: ""
2533
+ }));
2534
+ }
600
2535
  recordAgentEvent(agentId, type, payload) {
601
2536
  const eventId = `${agentId}:${++this.eventCounter}`;
602
2537
  const normalizedPayload = this.normalizeEventPayload(type, payload);
@@ -619,6 +2554,8 @@ var WebInspectorElement = class extends lit.LitElement {
619
2554
  const messages = this.normalizeAgentMessages(agent.messages);
620
2555
  if (messages) this.agentMessages.set(agent.agentId, messages);
621
2556
  else this.agentMessages.delete(agent.agentId);
2557
+ const runThreadId = agent.threadId;
2558
+ if (runThreadId) this.liveMessageVersion.set(runThreadId, (this.liveMessageVersion.get(runThreadId) ?? 0) + 1);
622
2559
  this.requestUpdate();
623
2560
  } catch (error) {
624
2561
  console.error(`[CopilotKit Inspector] Failed to sync messages for agent "${agent.agentId}":`, error);
@@ -646,18 +2583,23 @@ var WebInspectorElement = class extends lit.LitElement {
646
2583
  if (this.contextOptions.length !== nextOptions.length || this.contextOptions.some((option, index) => option.key !== nextOptions[index]?.key)) this.contextOptions = nextOptions;
647
2584
  const pendingContext = this.pendingSelectedContext;
648
2585
  if (pendingContext) {
649
- if (pendingContext === "all-agents" || agentIds.has(pendingContext)) {
2586
+ if ((pendingContext === "all-agents" || agentIds.has(pendingContext)) && (pendingContext === "all-agents" || agentIds.size === 1)) {
650
2587
  if (this.selectedContext !== pendingContext) {
651
2588
  this.selectedContext = pendingContext;
652
2589
  this.expandedRows.clear();
653
2590
  }
654
2591
  this.pendingSelectedContext = null;
655
- } else if (agentIds.size > 0) this.pendingSelectedContext = null;
2592
+ } else if (agentIds.size > 0) {
2593
+ if (this.selectedContext !== "all-agents") {
2594
+ this.selectedContext = "all-agents";
2595
+ this.expandedRows.clear();
2596
+ }
2597
+ this.pendingSelectedContext = null;
2598
+ }
656
2599
  }
657
2600
  if (!nextOptions.some((option) => option.key === this.selectedContext) && this.pendingSelectedContext === null) {
658
2601
  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];
2602
+ if (agentIds.size === 1) nextSelected = Array.from(agentIds)[0];
661
2603
  if (this.selectedContext !== nextSelected) {
662
2604
  this.selectedContext = nextSelected;
663
2605
  this.expandedRows.clear();
@@ -832,6 +2774,7 @@ ${argsString}</pre
832
2774
  z-index: 2147483646;
833
2775
  display: block;
834
2776
  will-change: transform;
2777
+ font-family: "Plus Jakarta Sans", system-ui, sans-serif;
835
2778
  }
836
2779
 
837
2780
  :host([data-transitioning="true"]) {
@@ -887,13 +2830,14 @@ ${argsString}</pre
887
2830
  left: 50%;
888
2831
  transform: translateX(-50%) translateY(-4px);
889
2832
  white-space: nowrap;
890
- background: rgba(17, 24, 39, 0.95);
2833
+ background: rgba(1, 5, 7, 0.95);
891
2834
  color: white;
892
2835
  padding: 4px 8px;
893
2836
  border-radius: 6px;
894
2837
  font-size: 10px;
2838
+ font-family: "Plus Jakarta Sans", system-ui, sans-serif;
895
2839
  line-height: 1.2;
896
- box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
2840
+ box-shadow: 0 4px 10px rgba(1, 5, 7, 0.18);
897
2841
  opacity: 0;
898
2842
  pointer-events: none;
899
2843
  transition:
@@ -914,18 +2858,19 @@ ${argsString}</pre
914
2858
  min-width: 300px;
915
2859
  max-width: 300px;
916
2860
  background: white;
917
- color: #111827;
2861
+ color: #010507;
918
2862
  font-size: 13px;
2863
+ font-family: "Plus Jakarta Sans", system-ui, sans-serif;
919
2864
  line-height: 1.4;
920
2865
  border-radius: 12px;
921
- box-shadow: 0 12px 28px rgba(15, 23, 42, 0.22);
2866
+ box-shadow: 0 12px 28px rgba(1, 5, 7, 0.12);
922
2867
  padding: 10px 12px;
923
2868
  display: inline-flex;
924
2869
  align-items: flex-start;
925
2870
  gap: 8px;
926
2871
  z-index: 4500;
927
2872
  animation: fade-slide-in 160ms ease;
928
- border: 1px solid rgba(148, 163, 184, 0.35);
2873
+ border: 1px solid rgba(219, 219, 229, 0.4);
929
2874
  white-space: normal;
930
2875
  word-break: break-word;
931
2876
  text-align: left;
@@ -946,7 +2891,7 @@ ${argsString}</pre
946
2891
  width: 10px;
947
2892
  height: 10px;
948
2893
  background: white;
949
- border: 1px solid rgba(148, 163, 184, 0.35);
2894
+ border: 1px solid rgba(219, 219, 229, 0.4);
950
2895
  transform: rotate(45deg);
951
2896
  top: 50%;
952
2897
  margin-top: -5px;
@@ -955,81 +2900,564 @@ ${argsString}</pre
955
2900
 
956
2901
  .announcement-preview[data-side="left"] .announcement-preview__arrow {
957
2902
  right: -5px;
958
- box-shadow: 6px -6px 10px rgba(15, 23, 42, 0.12);
2903
+ box-shadow: 6px -6px 10px rgba(1, 5, 7, 0.08);
959
2904
  }
960
2905
 
961
2906
  .announcement-preview[data-side="right"] .announcement-preview__arrow {
962
2907
  left: -5px;
963
- box-shadow: -6px 6px 10px rgba(15, 23, 42, 0.12);
2908
+ box-shadow: -6px 6px 10px rgba(1, 5, 7, 0.08);
964
2909
  }
965
2910
 
966
2911
  .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);
2912
+ background: none;
2913
+ border: none;
2914
+ cursor: pointer;
2915
+ color: #838389;
2916
+ width: 28px;
2917
+ height: 28px;
2918
+ display: flex;
2919
+ align-items: center;
2920
+ justify-content: center;
2921
+ border-radius: 6px;
2922
+ padding: 0;
973
2923
  transition:
974
2924
  background 120ms ease,
975
2925
  color 120ms ease;
976
2926
  }
977
2927
 
978
2928
  .announcement-dismiss:hover {
979
- background: rgba(241, 245, 249, 1);
980
- color: #111827;
2929
+ background: rgba(0, 0, 0, 0.06);
2930
+ color: #010507;
2931
+ }
2932
+
2933
+ /* ── Agent tab section cards ─────────────────────────────────────── */
2934
+ .cpk-section-card {
2935
+ border-radius: 8px;
2936
+ background: #ffffff;
2937
+ overflow: hidden;
2938
+ }
2939
+
2940
+ /* ── Agent icon bubble ───────────────────────────────────────────── */
2941
+ .cpk-agent-icon {
2942
+ background-color: #f0f0f4 !important;
2943
+ color: #57575b !important;
2944
+ }
2945
+
2946
+ /* ── Agent stat cards ────────────────────────────────────────────── */
2947
+ .cpk-stat-card {
2948
+ background-color: #ffffff !important;
2949
+ border: 1px solid #dbdbe5 !important;
2950
+ }
2951
+ button.cpk-stat-card:hover {
2952
+ background-color: #f7f7f9 !important;
2953
+ }
2954
+
2955
+ /* ── Circle chevron (Frontend Tools + Context) ──────────────────── */
2956
+ .cpk-chevron-circle {
2957
+ display: inline-flex;
2958
+ align-items: center;
2959
+ justify-content: center;
2960
+ width: 24px;
2961
+ height: 24px;
2962
+ border-radius: 50%;
2963
+ background-color: #f0f0f4;
2964
+ color: #838389;
2965
+ flex-shrink: 0;
2966
+ transition: transform 0.2s;
2967
+ }
2968
+ .cpk-chevron-circle svg {
2969
+ width: 14px !important;
2970
+ height: 14px !important;
2971
+ }
2972
+ .cpk-chevron-circle--open {
2973
+ transform: rotate(180deg);
2974
+ }
2975
+
2976
+ /* ── Inline copy button ─────────────────────────────────────────── */
2977
+ .cpk-copy-btn {
2978
+ font-size: 10px;
2979
+ font-weight: 500;
2980
+ color: #57575b;
2981
+ background: #ffffff;
2982
+ border: 1px solid #dbdbe5;
2983
+ cursor: pointer;
2984
+ padding: 2px 8px;
2985
+ border-radius: 4px;
2986
+ flex-shrink: 0;
2987
+ transition:
2988
+ background-color 0.15s,
2989
+ border-color 0.15s;
2990
+ }
2991
+ .cpk-copy-btn:hover {
2992
+ background-color: #f0f0f4;
2993
+ border-color: #afafb7;
2994
+ }
2995
+
2996
+ .cpk-section-header {
2997
+ background: #e8edf5;
2998
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
2999
+ padding: 10px 16px;
3000
+ }
3001
+ .cpk-section-header h4 {
3002
+ font-size: 11px;
3003
+ font-weight: 600;
3004
+ color: #181c1f;
3005
+ margin: 0;
3006
+ }
3007
+
3008
+ /* Inputs/selects inside the lavender header need an explicit white bg */
3009
+ .cpk-section-header input,
3010
+ .cpk-section-header select {
3011
+ background-color: #ffffff !important;
3012
+ box-shadow: none !important;
3013
+ }
3014
+ .cpk-section-header select {
3015
+ padding-right: 24px !important;
3016
+ }
3017
+ /* Events table column headers */
3018
+ table thead th {
3019
+ font-weight: 600 !important;
981
3020
  }
982
3021
 
983
3022
  .announcement-content {
984
- color: #111827;
985
- font-size: 14px;
986
- line-height: 1.6;
3023
+ color: #1f2230;
3024
+ font-size: 13px;
3025
+ font-family: "Plus Jakarta Sans", system-ui, sans-serif;
3026
+ line-height: 1.55;
987
3027
  }
988
3028
 
989
3029
  .announcement-content h1,
990
3030
  .announcement-content h2,
991
3031
  .announcement-content h3 {
3032
+ color: #010507;
992
3033
  font-weight: 700;
993
- margin: 0.4rem 0 0.2rem;
3034
+ line-height: 1.3;
3035
+ margin: 0.9rem 0 0.4rem;
3036
+ }
3037
+ .announcement-content > h1:first-child,
3038
+ .announcement-content > h2:first-child,
3039
+ .announcement-content > h3:first-child {
3040
+ margin-top: 0;
994
3041
  }
995
3042
 
996
3043
  .announcement-content h1 {
997
- font-size: 1.1rem;
3044
+ font-size: 1.15rem;
3045
+ letter-spacing: -0.01em;
998
3046
  }
999
-
1000
3047
  .announcement-content h2 {
1001
3048
  font-size: 1rem;
1002
3049
  }
1003
-
1004
3050
  .announcement-content h3 {
1005
- font-size: 0.95rem;
3051
+ font-size: 0.9rem;
3052
+ text-transform: none;
1006
3053
  }
1007
3054
 
1008
3055
  .announcement-content p {
1009
- margin: 0.25rem 0;
3056
+ margin: 0.45rem 0;
3057
+ }
3058
+
3059
+ .announcement-content strong {
3060
+ color: #010507;
3061
+ font-weight: 700;
1010
3062
  }
1011
3063
 
1012
3064
  .announcement-content ul {
1013
3065
  list-style: disc;
1014
3066
  padding-left: 1.25rem;
1015
- margin: 0.3rem 0;
3067
+ margin: 0.45rem 0;
1016
3068
  }
1017
3069
 
1018
3070
  .announcement-content ol {
1019
3071
  list-style: decimal;
1020
3072
  padding-left: 1.25rem;
1021
- margin: 0.3rem 0;
3073
+ margin: 0.45rem 0;
3074
+ }
3075
+
3076
+ .announcement-content li + li {
3077
+ margin-top: 0.15rem;
1022
3078
  }
1023
3079
 
1024
3080
  .announcement-content a {
1025
- color: #0f766e;
3081
+ color: #757cf2;
1026
3082
  text-decoration: underline;
1027
3083
  }
3084
+
3085
+ .announcement-content :not(pre) > code {
3086
+ background: #f3f3f7;
3087
+ border: 1px solid #e4e4ec;
3088
+ border-radius: 4px;
3089
+ padding: 1px 5px;
3090
+ font-size: 0.85em;
3091
+ color: #4a3a8a;
3092
+ }
3093
+
3094
+ .announcement-code {
3095
+ position: relative;
3096
+ margin: 0.6rem 0;
3097
+ }
3098
+
3099
+ .announcement-code pre {
3100
+ background: #0f1117;
3101
+ color: #e6e8f2;
3102
+ border-radius: 8px;
3103
+ padding: 10px 12px;
3104
+ overflow-x: auto;
3105
+ font-size: 12px;
3106
+ line-height: 1.5;
3107
+ white-space: pre;
3108
+ }
3109
+
3110
+ .announcement-code pre code::after {
3111
+ content: "";
3112
+ display: inline-block;
3113
+ width: 80px;
3114
+ }
3115
+
3116
+ .announcement-code__copy-shield {
3117
+ position: absolute;
3118
+ top: 4px;
3119
+ right: 4px;
3120
+ padding: 4px 4px 4px 24px;
3121
+ border-top-right-radius: 8px;
3122
+ background: linear-gradient(
3123
+ to right,
3124
+ rgba(15, 17, 23, 0) 0%,
3125
+ rgba(15, 17, 23, 0.95) 40%,
3126
+ #0f1117 100%
3127
+ );
3128
+ pointer-events: none;
3129
+ }
3130
+
3131
+ .announcement-code pre code {
3132
+ background: transparent;
3133
+ border: none;
3134
+ padding: 0;
3135
+ color: inherit;
3136
+ font-size: inherit;
3137
+ }
3138
+
3139
+ .announcement-code pre::-webkit-scrollbar {
3140
+ height: 6px;
3141
+ }
3142
+ .announcement-code pre::-webkit-scrollbar-track {
3143
+ background: transparent;
3144
+ }
3145
+ .announcement-code pre::-webkit-scrollbar-thumb {
3146
+ background: rgba(255, 255, 255, 0.2);
3147
+ border-radius: 3px;
3148
+ }
3149
+
3150
+ .announcement-code__copy {
3151
+ position: relative;
3152
+ pointer-events: auto;
3153
+ padding: 3px 8px;
3154
+ font-family: "Plus Jakarta Sans", system-ui, sans-serif;
3155
+ font-size: 11px;
3156
+ font-weight: 600;
3157
+ color: #e6e8f2;
3158
+ background: #1f222d;
3159
+ border: 1px solid rgba(255, 255, 255, 0.15);
3160
+ border-radius: 5px;
3161
+ cursor: pointer;
3162
+ transition:
3163
+ background 0.12s ease,
3164
+ color 0.12s ease;
3165
+ }
3166
+ .announcement-code__copy:hover {
3167
+ background: #2a2e3c;
3168
+ }
3169
+ .announcement-code__copy[data-copied="true"] {
3170
+ background: #eee6fe;
3171
+ color: #6430ab;
3172
+ border-color: transparent;
3173
+ }
3174
+
3175
+ .announcement-body {
3176
+ position: relative;
3177
+ overflow: hidden;
3178
+ transition: max-height 0.25s ease;
3179
+ }
3180
+ .announcement-body--collapsed {
3181
+ max-height: 72px;
3182
+ }
3183
+ .announcement-body--expanded {
3184
+ max-height: 2000px;
3185
+ }
3186
+ .announcement-fade {
3187
+ position: absolute;
3188
+ bottom: 0;
3189
+ left: 0;
3190
+ right: 0;
3191
+ height: 48px;
3192
+ background: linear-gradient(to bottom, transparent, #ffffff);
3193
+ pointer-events: none;
3194
+ }
3195
+ .announcement-toggle {
3196
+ display: block;
3197
+ width: 100%;
3198
+ margin-top: 6px;
3199
+ padding: 0;
3200
+ background: none;
3201
+ border: none;
3202
+ font-family: "Plus Jakarta Sans", system-ui, sans-serif;
3203
+ font-size: 12px;
3204
+ font-weight: 500;
3205
+ color: #757cf2;
3206
+ cursor: pointer;
3207
+ text-align: center;
3208
+ }
3209
+ .announcement-toggle:hover {
3210
+ color: #6430ab;
3211
+ }
3212
+
3213
+ /* ── Brand typography ────────────────────────────────────────── */
3214
+ /* Override Tailwind font-mono stack → Spline Sans Mono */
3215
+ .font-mono,
3216
+ pre,
3217
+ code {
3218
+ font-family: "Spline Sans Mono", ui-monospace, "Cascadia Code", monospace;
3219
+ }
3220
+
3221
+ /* ── Floating button ─────────────────────────────────────────── */
3222
+ .console-button {
3223
+ background-color: rgba(1, 5, 7, 0.95) !important;
3224
+ border-color: rgba(190, 194, 255, 0.25) !important;
3225
+ box-shadow:
3226
+ 0 0 0 1px rgba(190, 194, 255, 0.15),
3227
+ 0 4px 14px rgba(1, 5, 7, 0.28) !important;
3228
+ }
3229
+ .console-button:hover {
3230
+ background-color: rgba(1, 5, 7, 1) !important;
3231
+ border-color: rgba(190, 194, 255, 0.45) !important;
3232
+ }
3233
+ .console-button:focus-visible {
3234
+ outline-color: #bec2ff !important;
3235
+ }
3236
+
3237
+ /* ── Inspector window ────────────────────────────────────────── */
3238
+ .inspector-window {
3239
+ border-color: #dbdbe5 !important;
3240
+ box-shadow:
3241
+ 0 8px 32px rgba(1, 5, 7, 0.1),
3242
+ 0 2px 8px rgba(1, 5, 7, 0.06) !important;
3243
+ }
3244
+
3245
+ /* ── Header drag area ────────────────────────────────────────── */
3246
+ .drag-handle {
3247
+ border-bottom-color: #dbdbe5 !important;
3248
+ /* Subtle pale lavender gradient — brand "light, spacious" surface */
3249
+ background: linear-gradient(180deg, #f4f4fd 0%, #ffffff 100%) !important;
3250
+ }
3251
+
3252
+ /* Tab strip row: soft off-white, separated from content */
3253
+ .drag-handle > div:last-child {
3254
+ border-top-color: #e2e2ea !important;
3255
+ background-color: #fafafc !important;
3256
+ }
3257
+
3258
+ /* ── Tab buttons ─────────────────────────────────────────────── */
3259
+ /*
3260
+ * Named classes owned by this component — no Tailwind conflict.
3261
+ * Active: brand surface/surfaceContainerActive (lilac tint) +
3262
+ * border/borderActionEnabled underline.
3263
+ * Dark fill is for primary action buttons only, not nav tabs.
3264
+ */
3265
+ .cpk-tab-active {
3266
+ background-color: rgba(190, 194, 255, 0.18);
3267
+ color: #010507;
3268
+ font-weight: 600;
3269
+ }
3270
+ .cpk-tab-active .cpk-tab-icon {
3271
+ color: #757cf2;
3272
+ }
3273
+ .cpk-tab-inactive {
3274
+ background-color: transparent;
3275
+ color: #2b2b2b;
3276
+ }
3277
+ .cpk-tab-inactive .cpk-tab-icon {
3278
+ color: #838389;
3279
+ }
3280
+ .cpk-tab-inactive:hover {
3281
+ background-color: rgba(190, 194, 255, 0.08);
3282
+ color: #010507;
3283
+ cursor: pointer;
3284
+ }
3285
+ .cpk-tab-active {
3286
+ cursor: pointer;
3287
+ }
3288
+
3289
+ /* ── Header control buttons (dock, close) — first row only ───── */
3290
+ .drag-handle > div:first-child button {
3291
+ color: #838389 !important;
3292
+ }
3293
+ .drag-handle > div:first-child button:hover {
3294
+ background-color: #f0f0f4 !important;
3295
+ color: #57575b !important;
3296
+ }
3297
+ .drag-handle > div:first-child button:focus-visible {
3298
+ outline-color: #bec2ff !important;
3299
+ }
3300
+
3301
+ /* ── Agent/context dropdown ──────────────────────────────────── */
3302
+ [data-context-dropdown-root="true"] > button {
3303
+ border-color: #dbdbe5 !important;
3304
+ color: #010507 !important;
3305
+ }
3306
+ [data-context-dropdown-root="true"] > button:hover {
3307
+ border-color: #bec2ff !important;
3308
+ background-color: #f7f7f9 !important;
3309
+ }
3310
+ [data-context-dropdown-root="true"] > button > span:last-child {
3311
+ color: #838389 !important;
3312
+ }
3313
+ [data-context-dropdown-root="true"] > div {
3314
+ border-color: #dbdbe5 !important;
3315
+ box-shadow: 0 4px 12px rgba(1, 5, 7, 0.08) !important;
3316
+ }
3317
+ [data-context-dropdown-root="true"] > div button:hover,
3318
+ [data-context-dropdown-root="true"] > div button:focus {
3319
+ background-color: #f7f7f9 !important;
3320
+ }
3321
+
3322
+ /* ── Status bar (bottom chrome) ──────────────────────────────── */
3323
+ .inspector-window > div > div:last-child {
3324
+ border-top-color: #dbdbe5 !important;
3325
+ background-color: #f7f7f9 !important;
3326
+ }
3327
+
3328
+ /* ── Resize handle ───────────────────────────────────────────── */
3329
+ .resize-handle {
3330
+ color: #838389 !important;
3331
+ }
3332
+ .resize-handle:hover {
3333
+ color: #57575b !important;
3334
+ }
3335
+
3336
+ /* ── AG-UI Events tab ────────────────────────────────────────── */
3337
+ /* Row hover: replace blue tint with brand lilac */
3338
+ tr:hover td {
3339
+ background-color: rgba(190, 194, 255, 0.08) !important;
3340
+ }
3341
+ /* Reset/dark action button */
3342
+ button[class*="bg-gray-900"] {
3343
+ background-color: #010507 !important;
3344
+ }
3345
+ button[class*="bg-gray-800"] {
3346
+ background-color: #2b2b2b !important;
3347
+ }
3348
+ /* Copy "copied" state: generic green → brand mint */
3349
+ button[class*="bg-green-100"] {
3350
+ background-color: rgba(133, 236, 206, 0.2) !important;
3351
+ color: #189370 !important;
3352
+ }
3353
+
3354
+ /* ── Agents tab ──────────────────────────────────────────────── */
3355
+ /* Agent icon bubble: blue → lilac */
3356
+ span[class*="bg-blue-100"]:not([class*="text-blue-800"]) {
3357
+ background-color: rgba(190, 194, 255, 0.15) !important;
3358
+ }
3359
+ span[class*="text-blue-600"] {
3360
+ color: #757cf2 !important;
3361
+ }
3362
+ /* Running badge: emerald → mint */
3363
+ span[class*="bg-emerald-50"] {
3364
+ background-color: rgba(133, 236, 206, 0.15) !important;
3365
+ }
3366
+ span[class*="text-emerald-700"] {
3367
+ color: #189370 !important;
3368
+ }
3369
+ /* Running status dot */
3370
+ span[class*="bg-emerald-500"] {
3371
+ background-color: #85ecce !important;
3372
+ }
3373
+ /* Idle dot */
3374
+ span[class*="bg-gray-400"] {
3375
+ background-color: #afafb7 !important;
3376
+ }
3377
+ /* User role badge (blue → lilac) */
3378
+ span[class*="bg-blue-100"][class*="text-blue-800"] {
3379
+ background-color: rgba(190, 194, 255, 0.22) !important;
3380
+ border: 1px solid rgba(190, 194, 255, 0.45) !important;
3381
+ color: #57575b !important;
3382
+ }
3383
+ /* Assistant role badge (green → mint) */
3384
+ span[class*="bg-green-100"][class*="text-green-800"] {
3385
+ background-color: rgba(133, 236, 206, 0.18) !important;
3386
+ border: 1px solid rgba(133, 236, 206, 0.4) !important;
3387
+ color: #189370 !important;
3388
+ }
3389
+ /* Tool role badge (amber → orange brand) */
3390
+ span[class*="bg-amber-100"][class*="text-amber-800"] {
3391
+ background-color: rgba(255, 172, 77, 0.15) !important;
3392
+ color: #57575b !important;
3393
+ }
3394
+
3395
+ /* ── Frontend Tools tab ──────────────────────────────────────── */
3396
+ /* Handler badge (blue → lilac) */
3397
+ span[class*="bg-blue-50"][class*="text-blue-700"] {
3398
+ background-color: rgba(190, 194, 255, 0.12) !important;
3399
+ border-color: rgba(190, 194, 255, 0.3) !important;
3400
+ color: #010507 !important;
3401
+ }
3402
+ /* Renderer badge (purple → lilac-adjacent) */
3403
+ span[class*="bg-purple-50"][class*="text-purple-700"] {
3404
+ background-color: rgba(190, 194, 255, 0.12) !important;
3405
+ border-color: rgba(190, 194, 255, 0.3) !important;
3406
+ color: #57575b !important;
3407
+ }
3408
+ /* Required badge (rose → brand red) */
3409
+ span[class*="bg-rose-50"][class*="text-rose-700"] {
3410
+ background-color: rgba(250, 95, 103, 0.1) !important;
3411
+ border-color: rgba(250, 95, 103, 0.25) !important;
3412
+ color: #fa5f67 !important;
3413
+ }
3414
+ /* Code/default value blocks */
3415
+ code[class*="bg-gray-100"],
3416
+ span[class*="bg-gray-100"] {
3417
+ background-color: #f0f0f4 !important;
3418
+ }
3419
+
3420
+ /* ── Connected status bar: match threads header mint (#5BE4BB) ──── */
3421
+ /* Outer strip bg + top border + text when connected badge is present */
3422
+ .inspector-window
3423
+ > div
3424
+ > div:last-child
3425
+ > div:last-child:has(div[class*="bg-emerald-50"]) {
3426
+ background-color: rgba(91, 228, 187, 0.08) !important;
3427
+ border-top-color: rgba(91, 228, 187, 0.3) !important;
3428
+ color: #189370 !important;
3429
+ }
3430
+ /* Inner badge — slightly more opaque on the mint bg */
3431
+ div[class*="bg-emerald-50"][class*="border-emerald-200"] {
3432
+ background-color: rgba(91, 228, 187, 0.12) !important;
3433
+ border-color: rgba(91, 228, 187, 0.4) !important;
3434
+ color: #189370 !important;
3435
+ }
3436
+ /* Icon bubble inside connected badge → mint tint */
3437
+ div[class*="bg-emerald-50"] span[class*="bg-white"] {
3438
+ background-color: rgba(91, 228, 187, 0.3) !important;
3439
+ }
3440
+
3441
+ /* ── Announcement panel ──────────────────────────────────────── */
3442
+ div[class*="border-slate-200"][class*="bg-white"] {
3443
+ border-color: #dbdbe5 !important;
3444
+ }
3445
+ /* Announcement icon bubble: black → brand light lavender + lilac icon */
3446
+ span[class*="bg-slate-900"],
3447
+ div[class*="bg-slate-900"] {
3448
+ background-color: #eee6fe !important;
3449
+ color: #757cf2 !important;
3450
+ }
3451
+ span[class*="text-slate-800"],
3452
+ div[class*="text-slate-800"] {
3453
+ color: #010507 !important;
3454
+ }
1028
3455
  `];
1029
3456
  }
1030
3457
  connectedCallback() {
1031
3458
  super.connectedCallback();
1032
3459
  if (typeof window !== "undefined") {
3460
+ this.ensureBrandFonts();
1033
3461
  window.addEventListener("resize", this.handleResize);
1034
3462
  window.addEventListener("pointerdown", this.handleGlobalPointerDown);
1035
3463
  this.hydrateStateFromStorageEarly();
@@ -1037,6 +3465,15 @@ ${argsString}</pre
1037
3465
  this.ensureAnnouncementLoading();
1038
3466
  }
1039
3467
  }
3468
+ ensureBrandFonts() {
3469
+ const FONT_LINK_ID = "cpk-inspector-brand-fonts";
3470
+ if (document.getElementById(FONT_LINK_ID)) return;
3471
+ const link = document.createElement("link");
3472
+ link.id = FONT_LINK_ID;
3473
+ link.rel = "stylesheet";
3474
+ link.href = "https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600&family=Spline+Sans+Mono:wght@600&display=swap";
3475
+ document.head.appendChild(link);
3476
+ }
1040
3477
  disconnectedCallback() {
1041
3478
  super.disconnectedCallback();
1042
3479
  if (typeof window !== "undefined") {
@@ -1114,7 +3551,7 @@ ${argsString}</pre
1114
3551
  "focus-visible:outline",
1115
3552
  "focus-visible:outline-2",
1116
3553
  "focus-visible:outline-offset-2",
1117
- "focus-visible:outline-rose-500",
3554
+ "focus-visible:outline-[#BEC2FF]",
1118
3555
  "touch-none",
1119
3556
  "select-none",
1120
3557
  this.isDragging ? "cursor-grabbing" : "cursor-grab"
@@ -1222,14 +3659,12 @@ ${argsString}</pre
1222
3659
  return lit.html`
1223
3660
  <button
1224
3661
  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(" ")}
3662
+ 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
3663
  aria-pressed=${isSelected}
1227
3664
  @click=${() => this.handleMenuSelect(key)}
1228
3665
  >
1229
- <span
1230
- class="text-gray-400 ${isSelected ? "text-white" : ""}"
1231
- >
1232
- ${this.renderIcon(icon)}
3666
+ <span class="cpk-tab-icon">
3667
+ ${key in this.customTabIcons ? (0, lit_directives_unsafe_html_js.unsafeHTML)(this.customTabIcons[key]) : this.renderIcon(icon)}
1233
3668
  </span>
1234
3669
  <span>${label}</span>
1235
3670
  </button>
@@ -1238,8 +3673,8 @@ ${argsString}</pre
1238
3673
  </div>
1239
3674
  </div>
1240
3675
  <div class="flex flex-1 flex-col overflow-hidden">
1241
- <div class="flex-1 overflow-auto">
1242
- ${this.renderAnnouncementPanel()}
3676
+ <div id="cpk-main-scroll" class="flex-1 overflow-auto">
3677
+ ${this.renderAnnouncementBanner()}
1243
3678
  ${this.renderCoreWarningBanner()} ${this.renderMainContent()}
1244
3679
  <slot></slot>
1245
3680
  </div>
@@ -1683,7 +4118,8 @@ ${argsString}</pre
1683
4118
  role,
1684
4119
  contentText,
1685
4120
  contentRaw: raw.content !== void 0 ? this.sanitizeForLogging(raw.content) : void 0,
1686
- toolCalls
4121
+ toolCalls,
4122
+ activityType: typeof raw.activityType === "string" ? raw.activityType : void 0
1687
4123
  };
1688
4124
  }
1689
4125
  normalizeAgentMessages(messages) {
@@ -1702,9 +4138,6 @@ ${argsString}</pre
1702
4138
  } else normalized[key] = { value: entry };
1703
4139
  return normalized;
1704
4140
  }
1705
- getSelectedMenu() {
1706
- return this.menuItems.find((item) => item.key === this.selectedMenu) ?? this.menuItems[0];
1707
- }
1708
4141
  renderCoreWarningBanner() {
1709
4142
  if (this._core) return lit.nothing;
1710
4143
  return lit.html`
@@ -1761,27 +4194,99 @@ ${argsString}</pre
1761
4194
  if (this.selectedMenu === "agents") return this.renderAgentsView();
1762
4195
  if (this.selectedMenu === "frontend-tools") return this.renderToolsView();
1763
4196
  if (this.selectedMenu === "agent-context") return this.renderContextView();
4197
+ if (this.selectedMenu === "threads") return this.renderThreadsView();
1764
4198
  return lit.nothing;
1765
4199
  }
4200
+ renderThreadsView() {
4201
+ const displayThreads = this.selectedContext === "all-agents" ? this._threads : this._threadsByAgent.get(this.selectedContext) ?? [];
4202
+ let threadsErrorMessage = null;
4203
+ if (this.selectedContext === "all-agents") threadsErrorMessage = this._threadsErrorByAgent.values().next().value?.message ?? null;
4204
+ else threadsErrorMessage = this._threadsErrorByAgent.get(this.selectedContext)?.message ?? null;
4205
+ const selectedThread = this.selectedThreadId != null ? displayThreads.find((t) => t.id === this.selectedThreadId) ?? null : null;
4206
+ return lit.html`
4207
+ <div style="display:flex;height:100%;overflow:hidden;">
4208
+ <!-- Left sidebar: thread list -->
4209
+ <div
4210
+ style="width:${this.threadListWidth}px;flex-shrink:0;overflow:hidden;display:flex;flex-direction:column;border-right:1px solid #DBDBE5;"
4211
+ >
4212
+ <cpk-thread-list
4213
+ style="height:100%;"
4214
+ .threads=${displayThreads}
4215
+ .selectedThreadId=${this.selectedThreadId}
4216
+ .errorMessage=${threadsErrorMessage}
4217
+ @threadSelected=${(e) => {
4218
+ this.selectedThreadId = e.detail;
4219
+ this.requestUpdate();
4220
+ }}
4221
+ ></cpk-thread-list>
4222
+ </div>
4223
+
4224
+ <!-- Resize divider -->
4225
+ <div
4226
+ style="width:4px;flex-shrink:0;cursor:col-resize;background:transparent;position:relative;z-index:1;"
4227
+ @pointerdown=${this.handleThreadDividerPointerDown}
4228
+ @pointermove=${this.handleThreadDividerPointerMove}
4229
+ @pointerup=${this.handleThreadDividerPointerUp}
4230
+ @pointercancel=${this.handleThreadDividerPointerUp}
4231
+ ></div>
4232
+
4233
+ <!-- Center + right: thread details or empty state -->
4234
+ <div style="flex:1;min-width:0;overflow:hidden;display:flex;">
4235
+ ${this.selectedThreadId ? lit.html`<cpk-thread-details
4236
+ style="flex:1;min-width:0;"
4237
+ .threadId=${this.selectedThreadId}
4238
+ .thread=${selectedThread}
4239
+ .runtimeUrl=${this._core?.runtimeUrl ?? ""}
4240
+ .headers=${this._core?.headers ?? {}}
4241
+ .liveMessageVersion=${this.selectedThreadId ? this.liveMessageVersion.get(this.selectedThreadId) ?? 0 : 0}
4242
+ .agentStateInput=${selectedThread ? this.getLatestStateForAgent(selectedThread.agentId) : null}
4243
+ .agentEventsInput=${selectedThread ? this.agentEvents.get(selectedThread.agentId) ?? [] : []}
4244
+ ></cpk-thread-details>` : lit.html`
4245
+ <div
4246
+ style="
4247
+ flex: 1;
4248
+ display: flex;
4249
+ flex-direction: column;
4250
+ align-items: center;
4251
+ justify-content: center;
4252
+ gap: 8px;
4253
+ color: #838389;
4254
+ "
4255
+ >
4256
+ <svg
4257
+ width="32"
4258
+ height="32"
4259
+ viewBox="0 0 24 24"
4260
+ fill="none"
4261
+ stroke="#c0c0c8"
4262
+ stroke-width="1.5"
4263
+ stroke-linecap="round"
4264
+ stroke-linejoin="round"
4265
+ >
4266
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
4267
+ </svg>
4268
+ <span style="font-size: 13px">${displayThreads.length === 0 ? "No threads yet" : "Select a thread to inspect"}</span>
4269
+ </div>
4270
+ `}
4271
+ </div>
4272
+ </div>
4273
+ `;
4274
+ }
1766
4275
  renderEventsTable() {
1767
4276
  const events = this.getEventsForSelectedContext();
1768
4277
  const filteredEvents = this.filterEvents(events);
1769
4278
  const selectedLabel = this.selectedContext === "all-agents" ? "all agents" : `agent ${this.selectedContext}`;
1770
4279
  if (events.length === 0) return lit.html`
1771
4280
  <div
1772
- class="flex h-full items-center justify-center px-4 py-8 text-center"
4281
+ class="flex h-full flex-col items-center justify-center gap-2 px-4 py-10 text-center"
1773
4282
  >
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>
4283
+ <div class="text-gray-300 [&>svg]:!h-8 [&>svg]:!w-8">
4284
+ ${this.renderIcon("Zap")}
1784
4285
  </div>
4286
+ <span class="text-sm text-gray-600">No events yet</span>
4287
+ <span class="max-w-[240px] text-xs leading-snug text-gray-400"
4288
+ >Events are recorded live. Run the agent to see them here.</span
4289
+ >
1785
4290
  </div>
1786
4291
  `;
1787
4292
  if (filteredEvents.length === 0) return lit.html`
@@ -1878,23 +4383,32 @@ ${argsString}</pre
1878
4383
  </div>
1879
4384
  <div class="relative h-full w-full overflow-y-auto overflow-x-hidden">
1880
4385
  <table class="w-full table-fixed border-collapse text-xs box-border">
4386
+ <colgroup>
4387
+ <col style="width:${this.evtColWidths[0]}px">
4388
+ <col style="width:${this.evtColWidths[1]}px">
4389
+ <col style="width:${this.evtColWidths[2]}px">
4390
+ <col>
4391
+ </colgroup>
1881
4392
  <thead class="sticky top-0 z-10">
1882
4393
  <tr class="bg-white">
4394
+ ${[
4395
+ "Agent",
4396
+ "Time",
4397
+ "Event Type"
4398
+ ].map((label, col) => lit.html`
1883
4399
  <th
1884
4400
  class="border-b border-gray-200 bg-white px-3 py-2 text-left font-medium text-gray-900"
4401
+ style="position:relative;overflow:hidden;"
1885
4402
  >
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>
4403
+ ${label}
4404
+ <div
4405
+ style="position:absolute;top:0;right:0;width:5px;height:100%;cursor:col-resize;user-select:none;background:transparent;"
4406
+ @pointerdown=${(e) => this._onEvtColResizeStart(e, col)}
4407
+ @pointermove=${(e) => this._onEvtColResizeMove(e)}
4408
+ @pointerup=${() => this._onEvtColResizeEnd()}
4409
+ @pointercancel=${() => this._onEvtColResizeEnd()}
4410
+ ></div>
4411
+ </th>`)}
1898
4412
  <th
1899
4413
  class="border-b border-gray-200 bg-white px-3 py-2 text-left font-medium text-gray-900"
1900
4414
  >
@@ -1982,6 +4496,25 @@ ${prettyEvent}</pre
1982
4496
  this.eventTypeFilter = "all";
1983
4497
  this.requestUpdate();
1984
4498
  }
4499
+ _onEvtColResizeStart(e, col) {
4500
+ e.preventDefault();
4501
+ e.stopPropagation();
4502
+ e.currentTarget.setPointerCapture(e.pointerId);
4503
+ this._evtColResize = {
4504
+ col,
4505
+ startX: e.clientX,
4506
+ startW: this.evtColWidths[col] ?? 0
4507
+ };
4508
+ }
4509
+ _onEvtColResizeMove(e) {
4510
+ if (!this._evtColResize) return;
4511
+ const { col, startX, startW } = this._evtColResize;
4512
+ this.evtColWidths = this.evtColWidths.map((w, i) => i === col ? Math.max(40, startW + (e.clientX - startX)) : w);
4513
+ this.requestUpdate();
4514
+ }
4515
+ _onEvtColResizeEnd() {
4516
+ this._evtColResize = null;
4517
+ }
1985
4518
  exportEvents(events) {
1986
4519
  try {
1987
4520
  const payload = JSON.stringify(events, null, 2);
@@ -2026,7 +4559,7 @@ ${prettyEvent}</pre
2026
4559
  <div class="flex items-start justify-between mb-4">
2027
4560
  <div class="flex items-center gap-3">
2028
4561
  <div
2029
- class="flex h-10 w-10 items-center justify-center rounded-lg bg-blue-100 text-blue-600"
4562
+ class="flex h-10 w-10 items-center justify-center rounded-lg bg-blue-100 text-blue-600 cpk-agent-icon"
2030
4563
  >
2031
4564
  ${this.renderIcon("Bot")}
2032
4565
  </div>
@@ -2054,7 +4587,7 @@ ${prettyEvent}</pre
2054
4587
  <div class="grid grid-cols-2 gap-4 md:grid-cols-4">
2055
4588
  <button
2056
4589
  type="button"
2057
- class="rounded-md bg-gray-50 px-3 py-2 text-left transition hover:bg-gray-100 cursor-pointer overflow-hidden"
4590
+ 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
4591
  @click=${() => this.handleMenuSelect("ag-ui-events")}
2059
4592
  title="View all events in AG-UI Events"
2060
4593
  >
@@ -2065,7 +4598,9 @@ ${prettyEvent}</pre
2065
4598
  ${stats.totalEvents}
2066
4599
  </div>
2067
4600
  </button>
2068
- <div class="rounded-md bg-gray-50 px-3 py-2 overflow-hidden">
4601
+ <div
4602
+ class="rounded-md bg-gray-50 px-3 py-2 overflow-hidden cpk-stat-card"
4603
+ >
2069
4604
  <div class="truncate whitespace-nowrap text-xs text-gray-600">
2070
4605
  Messages
2071
4606
  </div>
@@ -2073,7 +4608,9 @@ ${prettyEvent}</pre
2073
4608
  ${stats.messages}
2074
4609
  </div>
2075
4610
  </div>
2076
- <div class="rounded-md bg-gray-50 px-3 py-2 overflow-hidden">
4611
+ <div
4612
+ class="rounded-md bg-gray-50 px-3 py-2 overflow-hidden cpk-stat-card"
4613
+ >
2077
4614
  <div class="truncate whitespace-nowrap text-xs text-gray-600">
2078
4615
  Tool Calls
2079
4616
  </div>
@@ -2081,7 +4618,9 @@ ${prettyEvent}</pre
2081
4618
  ${stats.toolCalls}
2082
4619
  </div>
2083
4620
  </div>
2084
- <div class="rounded-md bg-gray-50 px-3 py-2 overflow-hidden">
4621
+ <div
4622
+ class="rounded-md bg-gray-50 px-3 py-2 overflow-hidden cpk-stat-card"
4623
+ >
2085
4624
  <div class="truncate whitespace-nowrap text-xs text-gray-600">
2086
4625
  Errors
2087
4626
  </div>
@@ -2093,9 +4632,9 @@ ${prettyEvent}</pre
2093
4632
  </div>
2094
4633
 
2095
4634
  <!-- 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>
4635
+ <div class="cpk-section-card">
4636
+ <div class="cpk-section-header">
4637
+ <h4>Current State</h4>
2099
4638
  </div>
2100
4639
  <div class="overflow-auto p-4">
2101
4640
  ${this.hasRenderableState(state) ? lit.html`
@@ -2104,7 +4643,7 @@ ${prettyEvent}</pre
2104
4643
  ><code>${this.formatStateForDisplay(state)}</code></pre>
2105
4644
  ` : lit.html`
2106
4645
  <div
2107
- class="flex h-40 items-center justify-center text-xs text-gray-500"
4646
+ class="flex h-12 items-center justify-center text-xs text-gray-500"
2108
4647
  >
2109
4648
  <div class="flex items-center gap-2 text-gray-500">
2110
4649
  <span class="text-lg text-gray-400"
@@ -2118,30 +4657,18 @@ ${prettyEvent}</pre
2118
4657
  </div>
2119
4658
 
2120
4659
  <!-- 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>
4660
+ <div class="cpk-section-card">
4661
+ <div class="cpk-section-header">
4662
+ <h4>Current Messages</h4>
2126
4663
  </div>
2127
4664
  <div class="overflow-auto">
2128
4665
  ${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">
4666
+ <div class="w-full text-xs">
4667
+ <div class="flex bg-gray-50">
4668
+ <div class="w-40 shrink-0 px-4 py-2 font-medium text-gray-700">Role</div>
4669
+ <div class="flex-1 px-4 py-2 font-medium text-gray-700">Content</div>
4670
+ </div>
4671
+ <div class="divide-y divide-gray-200">
2145
4672
  ${messages.map((msg) => {
2146
4673
  const role = msg.role || "unknown";
2147
4674
  const roleColors = {
@@ -2156,34 +4683,32 @@ ${prettyEvent}</pre
2156
4683
  const hasContent = rawContent.trim().length > 0;
2157
4684
  const contentFallback = toolCalls.length > 0 ? "Invoked tool call" : "—";
2158
4685
  return lit.html`
2159
- <tr>
2160
- <td class="px-4 py-2 align-top">
4686
+ <div class="flex items-start">
4687
+ <div class="w-40 shrink-0 px-4 py-2">
2161
4688
  <span
2162
4689
  class="inline-flex rounded px-2 py-0.5 text-[10px] font-medium ${roleColors[role] || roleColors.unknown}"
2163
4690
  >
2164
4691
  ${role}
2165
4692
  </span>
2166
- </td>
2167
- <td class="px-4 py-2">
4693
+ </div>
4694
+ <div class="flex-1 px-4 py-2">
2168
4695
  ${hasContent ? lit.html`<div
2169
- class="max-w-2xl whitespace-pre-wrap break-words text-gray-700"
4696
+ class="whitespace-pre-line break-words text-gray-700"
2170
4697
  >
2171
4698
  ${rawContent}
2172
- </div>` : lit.html`<div
2173
- class="text-xs italic text-gray-400"
2174
- >
4699
+ </div>` : lit.html`<div class="italic text-gray-400">
2175
4700
  ${contentFallback}
2176
4701
  </div>`}
2177
4702
  ${role === "assistant" && toolCalls.length > 0 ? this.renderToolCallDetails(toolCalls) : lit.nothing}
2178
- </td>
2179
- </tr>
4703
+ </div>
4704
+ </div>
2180
4705
  `;
2181
4706
  })}
2182
- </tbody>
2183
- </table>
4707
+ </div>
4708
+ </div>
2184
4709
  ` : lit.html`
2185
4710
  <div
2186
- class="flex h-40 items-center justify-center text-xs text-gray-500"
4711
+ class="flex h-12 items-center justify-center text-xs text-gray-500"
2187
4712
  >
2188
4713
  <div class="flex items-center gap-2 text-gray-500">
2189
4714
  <span class="text-lg text-gray-400"
@@ -2244,14 +4769,29 @@ ${prettyEvent}</pre
2244
4769
  }
2245
4770
  handleMenuSelect(key) {
2246
4771
  if (!this.menuItems.some((item) => item.key === key)) return;
4772
+ const previousMenu = this.selectedMenu;
2247
4773
  this.selectedMenu = key;
2248
4774
  if (key === "agents" && this.selectedContext === "all-agents") {
2249
4775
  const agentOptions = this.contextOptions.filter((opt) => opt.key !== "all-agents");
2250
4776
  if (agentOptions.length > 0) {
2251
- const defaultAgent = agentOptions.find((opt) => opt.key === "default");
2252
- this.selectedContext = defaultAgent ? defaultAgent.key : agentOptions[0].key;
4777
+ const mostRecent = agentOptions.reduce((best, opt) => {
4778
+ const ts = this.getAgentStats(opt.key).lastActivity ?? -1;
4779
+ return best === null || ts > best.ts ? {
4780
+ key: opt.key,
4781
+ ts
4782
+ } : best;
4783
+ }, null);
4784
+ this.selectedContext = mostRecent ? mostRecent.key : agentOptions[0].key;
2253
4785
  }
2254
4786
  }
4787
+ if (previousMenu === "agents" && key !== "agents") {
4788
+ if (this.contextOptions.filter((opt) => opt.key !== "all-agents").length > 1) this.selectedContext = "all-agents";
4789
+ }
4790
+ if (key === "threads") this.autoSelectLatestThread();
4791
+ if (key === "ag-ui-events" || key === "agents") requestAnimationFrame(() => {
4792
+ const scroller = this.shadowRoot?.getElementById("cpk-main-scroll");
4793
+ if (scroller) scroller.scrollTop = 0;
4794
+ });
2255
4795
  this.contextMenuOpen = false;
2256
4796
  this.persistState();
2257
4797
  this.requestUpdate();
@@ -2639,9 +5179,19 @@ ${prettyEvent}</pre
2639
5179
  <div class="mb-3">
2640
5180
  <h5 class="mb-1 text-xs font-semibold text-gray-700">ID</h5>
2641
5181
  <code
2642
- class="block rounded bg-white border border-gray-200 px-2 py-1 text-[10px] font-mono text-gray-600"
5182
+ class="font-mono text-xs font-medium text-gray-800 flex-1 truncate min-w-0"
2643
5183
  >${id}</code
2644
5184
  >
5185
+ <button
5186
+ type="button"
5187
+ class="cpk-copy-btn"
5188
+ @click=${(e) => {
5189
+ e.stopPropagation();
5190
+ this.copyContextValue(id, `${id}:id`);
5191
+ }}
5192
+ >
5193
+ ${this.copiedContextItems.has(`${id}:id`) ? "✓" : "Copy"}
5194
+ </button>
2645
5195
  </div>
2646
5196
  ${hasValue ? lit.html`
2647
5197
  <div class="mb-2 flex items-center justify-between gap-2">
@@ -2649,8 +5199,8 @@ ${prettyEvent}</pre
2649
5199
  Value
2650
5200
  </h5>
2651
5201
  <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
5202
  type="button"
5203
+ class="cpk-copy-btn"
2654
5204
  @click=${(e) => {
2655
5205
  e.stopPropagation();
2656
5206
  this.copyContextValue(context.value, id);
@@ -2659,13 +5209,6 @@ ${prettyEvent}</pre
2659
5209
  ${this.copiedContextItems.has(id) ? "Copied" : "Copy JSON"}
2660
5210
  </button>
2661
5211
  </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
5212
  ` : lit.html`
2670
5213
  <div class="flex items-center justify-center py-4 text-xs text-gray-500">
2671
5214
  <span>No value available</span>
@@ -2678,7 +5221,7 @@ ${prettyEvent}</pre
2678
5221
  }
2679
5222
  getContextValuePreview(value) {
2680
5223
  if (value === void 0 || value === null) return "—";
2681
- if (typeof value === "string") return value.length > 50 ? `${value.substring(0, 50)}...` : value;
5224
+ if (typeof value === "string") return value.length > 50 ? `${value.slice(0, 50)}...` : value;
2682
5225
  if (typeof value === "number" || typeof value === "boolean") return String(value);
2683
5226
  if (Array.isArray(value)) return `Array(${value.length})`;
2684
5227
  if (typeof value === "object") {
@@ -2728,50 +5271,25 @@ ${prettyEvent}</pre
2728
5271
  else this.expandedRows.add(eventId);
2729
5272
  this.requestUpdate();
2730
5273
  }
2731
- renderAnnouncementPanel() {
2732
- if (!this.isOpen) return lit.nothing;
2733
- this.ensureAnnouncementLoading();
5274
+ renderAnnouncementBanner() {
2734
5275
  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)]"
5276
+ if (!this.announcementLoaded && !this.announcementHtml) return lit.html`<div
5277
+ class="flex items-center gap-2 px-4 py-3 text-sm font-semibold text-slate-800"
2749
5278
  >
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>
5279
+ <span
5280
+ class="inline-flex h-6 w-6 items-center justify-center rounded-md bg-slate-900 text-white shadow-sm"
5281
+ >
5282
+ ${this.renderIcon("Megaphone")}
5283
+ </span>
5284
+ <span>Loading latest announcement…</span>
2762
5285
  </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
- >
5286
+ if (!this.announcementHtml) return lit.nothing;
5287
+ return lit.html`<div class="mx-4 mt-3 mb-3 rounded-xl border border-slate-200 bg-white px-4 py-3">
2770
5288
  <div
2771
- class="mb-3 flex items-center gap-2 text-sm font-semibold text-slate-900"
5289
+ class="mb-2 flex items-center gap-2 text-xs font-semibold text-slate-900"
2772
5290
  >
2773
5291
  <span
2774
- class="inline-flex h-7 w-7 items-center justify-center rounded-md bg-slate-900 text-white shadow-sm"
5292
+ class="inline-flex h-5 w-5 items-center justify-center rounded-md bg-slate-900 text-white shadow-sm"
2775
5293
  >
2776
5294
  ${this.renderIcon("Megaphone")}
2777
5295
  </span>
@@ -2782,12 +5300,30 @@ ${this.announcementMarkdown}</pre
2782
5300
  @click=${this.handleDismissAnnouncement}
2783
5301
  aria-label="Dismiss announcement"
2784
5302
  >
2785
- Dismiss
5303
+ ${this.renderIcon("X")}
2786
5304
  </button>
2787
5305
  </div>
2788
- <div class="announcement-content text-sm leading-relaxed text-gray-900">
2789
- ${content}
5306
+ <div class="announcement-body ${this.announcementExpanded ? "announcement-body--expanded" : "announcement-body--collapsed"}">
5307
+ <div
5308
+ class="announcement-content"
5309
+ @click=${this.handleAnnouncementContentClick}
5310
+ >
5311
+ ${(0, lit_directives_unsafe_html_js.unsafeHTML)(this.announcementHtml)}
5312
+ </div>
5313
+ ${!this.announcementExpanded ? lit.html`
5314
+ <div class="announcement-fade"></div>
5315
+ ` : lit.nothing}
2790
5316
  </div>
5317
+ <button
5318
+ class="announcement-toggle"
5319
+ type="button"
5320
+ @click=${() => {
5321
+ this.announcementExpanded = !this.announcementExpanded;
5322
+ this.requestUpdate();
5323
+ }}
5324
+ >
5325
+ ${this.announcementExpanded ? "Show less ↑" : "Show more ↓"}
5326
+ </button>
2791
5327
  </div>`;
2792
5328
  }
2793
5329
  ensureAnnouncementLoading() {
@@ -2822,14 +5358,13 @@ ${this.announcementMarkdown}</pre
2822
5358
  const storedTimestamp = this.loadStoredAnnouncementTimestamp();
2823
5359
  this.announcementTimestamp = timestamp;
2824
5360
  this.announcementPreviewText = previewText ?? "";
2825
- this.announcementMarkdown = markdown;
2826
5361
  this.hasUnseenAnnouncement = (!storedTimestamp || storedTimestamp !== timestamp) && !!this.announcementPreviewText;
2827
5362
  this.showAnnouncementPreview = this.hasUnseenAnnouncement;
2828
5363
  this.announcementHtml = await this.convertMarkdownToHtml(markdown);
2829
5364
  this.announcementLoaded = true;
2830
5365
  this.requestUpdate();
2831
5366
  } catch (error) {
2832
- this.announcementLoadError = error;
5367
+ console.warn("[CopilotKit Inspector] Failed to load announcement", error);
2833
5368
  this.announcementLoaded = true;
2834
5369
  this.requestUpdate();
2835
5370
  }
@@ -2839,7 +5374,28 @@ ${this.announcementMarkdown}</pre
2839
5374
  renderer.link = (href, title, text) => {
2840
5375
  return `<a href="${this.escapeHtmlAttr(this.appendRefParam(href ?? ""))}" target="_blank" rel="noopener"${title ? ` title="${this.escapeHtmlAttr(title)}"` : ""}>${text}</a>`;
2841
5376
  };
2842
- return marked.marked.parse(markdown, { renderer });
5377
+ renderer.code = (code, lang) => {
5378
+ const safeLang = (lang ?? "").replace(/[^a-z0-9-]/gi, "");
5379
+ return `<div class="announcement-code"><pre><code${safeLang ? ` class="language-${safeLang}"` : ""}>${escapeHtml(code)}</code></pre><div class="announcement-code__copy-shield"><button type="button" class="announcement-code__copy" data-copy="${this.encodeBase64(code)}" aria-label="Copy code">Copy</button></div></div>`;
5380
+ };
5381
+ return marked.marked.parse(markdown, {
5382
+ renderer,
5383
+ async: false
5384
+ });
5385
+ }
5386
+ encodeBase64(value) {
5387
+ if (typeof window === "undefined" || typeof window.btoa !== "function") return "";
5388
+ const bytes = new TextEncoder().encode(value);
5389
+ let binary = "";
5390
+ for (const b of bytes) binary += String.fromCharCode(b);
5391
+ return window.btoa(binary);
5392
+ }
5393
+ decodeBase64(value) {
5394
+ if (typeof window === "undefined" || typeof window.atob !== "function") return "";
5395
+ const decoded = window.atob(value);
5396
+ const bytes = new Uint8Array(decoded.length);
5397
+ for (let i = 0; i < decoded.length; i++) bytes[i] = decoded.charCodeAt(i);
5398
+ return new TextDecoder().decode(bytes);
2843
5399
  }
2844
5400
  appendRefParam(href) {
2845
5401
  try {
@@ -2851,7 +5407,7 @@ ${this.announcementMarkdown}</pre
2851
5407
  }
2852
5408
  }
2853
5409
  escapeHtmlAttr(value) {
2854
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
5410
+ return escapeHtml(value).replace(/"/g, "&quot;").replace(/'/g, "&#39;");
2855
5411
  }
2856
5412
  loadStoredAnnouncementTimestamp() {
2857
5413
  if (typeof window === "undefined" || !window.localStorage) return null;
@@ -2892,4 +5448,5 @@ defineWebInspector();
2892
5448
  exports.WEB_INSPECTOR_TAG = WEB_INSPECTOR_TAG;
2893
5449
  exports.WebInspectorElement = WebInspectorElement;
2894
5450
  exports.defineWebInspector = defineWebInspector;
5451
+ exports.ɵCpkThreadDetails = ɵCpkThreadDetails;
2895
5452
  //# sourceMappingURL=index.cjs.map