@copilotkit/web-inspector 1.61.2 → 1.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dev/css-raw-import.spec.ts +31 -0
- package/dev/index.html +115 -0
- package/dev/main.ts +165 -0
- package/dev/vite.config.ts +30 -0
- package/dist/index.cjs +752 -136
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -6
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +90 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +751 -137
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +807 -163
- package/dist/index.umd.js.map +1 -1
- package/dist/package.cjs +1 -1
- package/dist/package.mjs +1 -1
- package/package.json +4 -2
- package/src/__tests__/web-inspector.spec.ts +717 -13
- package/src/index.ts +1017 -193
package/src/index.ts
CHANGED
|
@@ -68,6 +68,7 @@ import type { InspectorThreadTelemetryProps } from "./lib/telemetry.js";
|
|
|
68
68
|
export type { Anchor } from "./lib/types.js";
|
|
69
69
|
|
|
70
70
|
export const WEB_INSPECTOR_TAG = "cpk-web-inspector" as const;
|
|
71
|
+
export const THREAD_INSPECTOR_TAG = "cpk-thread-inspector" as const;
|
|
71
72
|
|
|
72
73
|
type LucideIconName = keyof typeof icons;
|
|
73
74
|
|
|
@@ -200,15 +201,62 @@ type InspectorEvent = {
|
|
|
200
201
|
|
|
201
202
|
// ─── Thread details types ────────────────────────────────────────────────────
|
|
202
203
|
|
|
203
|
-
|
|
204
|
+
export type ThreadDebuggerProviderLoadOptions = {
|
|
205
|
+
signal: AbortSignal;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export type ThreadDebuggerToolCall = {
|
|
209
|
+
id: string;
|
|
210
|
+
name: string;
|
|
211
|
+
args: string | Record<string, unknown>;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export type ThreadDebuggerMessage = {
|
|
204
215
|
id: string;
|
|
205
216
|
role: string;
|
|
206
217
|
content?: string;
|
|
207
|
-
toolCalls?:
|
|
218
|
+
toolCalls?: ThreadDebuggerToolCall[];
|
|
208
219
|
toolCallId?: string;
|
|
209
220
|
/** Present when role === "activity" (Generative UI output). */
|
|
210
221
|
activityType?: string;
|
|
211
|
-
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
export type ThreadDebuggerEvent = {
|
|
225
|
+
type: string;
|
|
226
|
+
timestamp: string | number;
|
|
227
|
+
payload?: Record<string, unknown>;
|
|
228
|
+
[key: string]: unknown;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export type ThreadDebuggerMetadata = {
|
|
232
|
+
id: string;
|
|
233
|
+
name?: string | null;
|
|
234
|
+
agentId?: string | null;
|
|
235
|
+
endUserId?: string | null;
|
|
236
|
+
createdById?: string | null;
|
|
237
|
+
status?: string | null;
|
|
238
|
+
createdAt?: string | null;
|
|
239
|
+
updatedAt?: string | null;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export type ThreadDebuggerProvider = {
|
|
243
|
+
getThreadMetadata?: (
|
|
244
|
+
threadId: string,
|
|
245
|
+
options: ThreadDebuggerProviderLoadOptions,
|
|
246
|
+
) => Promise<ThreadDebuggerMetadata | null>;
|
|
247
|
+
getMessages?: (
|
|
248
|
+
threadId: string,
|
|
249
|
+
options: ThreadDebuggerProviderLoadOptions,
|
|
250
|
+
) => Promise<ThreadDebuggerMessage[]>;
|
|
251
|
+
getEvents?: (
|
|
252
|
+
threadId: string,
|
|
253
|
+
options: ThreadDebuggerProviderLoadOptions,
|
|
254
|
+
) => Promise<ThreadDebuggerEvent[]>;
|
|
255
|
+
getState?: (
|
|
256
|
+
threadId: string,
|
|
257
|
+
options: ThreadDebuggerProviderLoadOptions,
|
|
258
|
+
) => Promise<Record<string, unknown> | null>;
|
|
259
|
+
};
|
|
212
260
|
|
|
213
261
|
interface ConversationUser {
|
|
214
262
|
id: string;
|
|
@@ -282,9 +330,39 @@ interface ApiAgentEvent {
|
|
|
282
330
|
type: string;
|
|
283
331
|
timestamp: string | number;
|
|
284
332
|
payload: Record<string, unknown>;
|
|
333
|
+
sourceIndex?: number;
|
|
334
|
+
rawEvent?: ThreadDebuggerEvent;
|
|
285
335
|
}
|
|
286
336
|
|
|
287
|
-
type ThreadDetailsTab = "
|
|
337
|
+
type ThreadDetailsTab = "timeline" | "state" | "raw-events";
|
|
338
|
+
type ThreadDetailsPanelCacheSlot = ThreadDetailsTab | "timeline-fallback";
|
|
339
|
+
|
|
340
|
+
type TimelineItemKind =
|
|
341
|
+
| "message"
|
|
342
|
+
| "tool"
|
|
343
|
+
| "state"
|
|
344
|
+
| "run"
|
|
345
|
+
| "event"
|
|
346
|
+
| "warning";
|
|
347
|
+
|
|
348
|
+
type TimelineItem = {
|
|
349
|
+
id: string;
|
|
350
|
+
kind: TimelineItemKind;
|
|
351
|
+
title: string;
|
|
352
|
+
body?: string;
|
|
353
|
+
timestamp: string | number;
|
|
354
|
+
sourceIndex: number;
|
|
355
|
+
severity?: "warning" | "error";
|
|
356
|
+
details?: Record<string, unknown>;
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
type RuntimeEventsFetchResult =
|
|
360
|
+
| { status: "available"; events: ThreadDebuggerEvent[] }
|
|
361
|
+
| { status: "not-available" };
|
|
362
|
+
|
|
363
|
+
type RuntimeStateFetchResult =
|
|
364
|
+
| { status: "available"; state: Record<string, unknown> | null }
|
|
365
|
+
| { status: "not-available" };
|
|
288
366
|
|
|
289
367
|
// ─── JSON syntax highlighter ─────────────────────────────────────────────────
|
|
290
368
|
// Inline-styled so shadow DOM encapsulation preserves colors when the output
|
|
@@ -618,28 +696,30 @@ class CpkThreadList extends LitElement {
|
|
|
618
696
|
${
|
|
619
697
|
this.errorMessage
|
|
620
698
|
? html`
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
699
|
+
<svg
|
|
700
|
+
width="24"
|
|
701
|
+
height="24"
|
|
702
|
+
viewBox="0 0 24 24"
|
|
703
|
+
fill="none"
|
|
704
|
+
stroke="currentColor"
|
|
705
|
+
stroke-width="1.5"
|
|
706
|
+
stroke-linecap="round"
|
|
707
|
+
stroke-linejoin="round"
|
|
708
|
+
class="cpk-tl__empty-icon"
|
|
709
|
+
>
|
|
710
|
+
<circle cx="12" cy="12" r="10" />
|
|
711
|
+
<line x1="12" y1="8" x2="12" y2="12" />
|
|
712
|
+
<line x1="12" y1="16" x2="12.01" y2="16" />
|
|
713
|
+
</svg>
|
|
714
|
+
<div>
|
|
715
|
+
Failed to load threads
|
|
716
|
+
<div
|
|
717
|
+
style="font-size:11px;margin-top:4px;color:#c0333a;"
|
|
631
718
|
>
|
|
632
|
-
|
|
633
|
-
<line x1="12" y1="8" x2="12" y2="12" />
|
|
634
|
-
<line x1="12" y1="16" x2="12.01" y2="16" />
|
|
635
|
-
</svg>
|
|
636
|
-
<div>
|
|
637
|
-
Failed to load threads
|
|
638
|
-
<div style="font-size:11px;margin-top:4px;color:#c0333a;">
|
|
639
|
-
${this.errorMessage}
|
|
640
|
-
</div>
|
|
719
|
+
${this.errorMessage}
|
|
641
720
|
</div>
|
|
642
|
-
|
|
721
|
+
</div>
|
|
722
|
+
`
|
|
643
723
|
: this.threads.length === 0
|
|
644
724
|
? html`
|
|
645
725
|
<svg
|
|
@@ -671,21 +751,14 @@ class CpkThreadList extends LitElement {
|
|
|
671
751
|
}
|
|
672
752
|
}
|
|
673
753
|
|
|
674
|
-
// ─── cpk-thread-
|
|
675
|
-
// Renders the selected thread's
|
|
676
|
-
//
|
|
677
|
-
//
|
|
678
|
-
|
|
679
|
-
// agent subscriptions; when those are absent we fall back to the per-thread
|
|
680
|
-
// fetched data via `/threads/:id/{events,state}`.
|
|
681
|
-
|
|
682
|
-
// Exported (with the underscore-prefixed name signalling internal/test-only)
|
|
683
|
-
// so unit tests can pin down the per-panel template-cache invariants without
|
|
684
|
-
// reaching through `customElements`. Production consumers continue to use the
|
|
685
|
-
// `cpk-thread-details` custom element registered below.
|
|
686
|
-
export class ɵCpkThreadDetails extends LitElement {
|
|
754
|
+
// ─── cpk-thread-inspector ────────────────────────────────────────────────────
|
|
755
|
+
// Renders the selected thread's read-only timeline, state, raw AG-UI events,
|
|
756
|
+
// and compact technical metadata. External hosts provide a ThreadDebuggerProvider;
|
|
757
|
+
// the legacy CopilotKit Inspector wrapper can still pass runtime URL inputs.
|
|
758
|
+
export class CpkThreadInspector extends LitElement {
|
|
687
759
|
static properties = {
|
|
688
760
|
threadId: { attribute: false },
|
|
761
|
+
provider: { attribute: false },
|
|
689
762
|
thread: { attribute: false },
|
|
690
763
|
runtimeUrl: { attribute: false },
|
|
691
764
|
headers: { attribute: false },
|
|
@@ -694,6 +767,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
694
767
|
agentEventsInput: { attribute: false },
|
|
695
768
|
liveMessageVersion: { attribute: false },
|
|
696
769
|
_tab: { state: true },
|
|
770
|
+
_fetchedMetadata: { state: true },
|
|
697
771
|
_conversation: { state: true },
|
|
698
772
|
_fetchedEvents: { state: true },
|
|
699
773
|
_fetchedState: { state: true },
|
|
@@ -714,7 +788,8 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
714
788
|
};
|
|
715
789
|
|
|
716
790
|
threadId: string | null = null;
|
|
717
|
-
|
|
791
|
+
provider: ThreadDebuggerProvider | null = null;
|
|
792
|
+
thread: ThreadDebuggerMetadata | ɵThread | null = null;
|
|
718
793
|
runtimeUrl = "";
|
|
719
794
|
headers: Record<string, string> = {};
|
|
720
795
|
threadInspectionAvailable = false;
|
|
@@ -728,7 +803,8 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
728
803
|
*/
|
|
729
804
|
liveMessageVersion = 0;
|
|
730
805
|
|
|
731
|
-
private _tab: ThreadDetailsTab = "
|
|
806
|
+
private _tab: ThreadDetailsTab = "timeline";
|
|
807
|
+
private _fetchedMetadata: ThreadDebuggerMetadata | null = null;
|
|
732
808
|
private _conversation: ConversationItem[] = [];
|
|
733
809
|
private _fetchedEvents: ApiAgentEvent[] | null = null;
|
|
734
810
|
private _fetchedState: Record<string, unknown> | null = null;
|
|
@@ -761,9 +837,9 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
761
837
|
* switching back to AG-UI Events on a thread with hundreds of events
|
|
762
838
|
* triggers a multi-second DOM-creation pass each time.
|
|
763
839
|
*
|
|
764
|
-
* Reset to {"
|
|
840
|
+
* Reset to {"timeline"} when the selected thread changes.
|
|
765
841
|
*/
|
|
766
|
-
private _activatedTabs: Set<ThreadDetailsTab> = new Set(["
|
|
842
|
+
private _activatedTabs: Set<ThreadDetailsTab> = new Set(["timeline"]);
|
|
767
843
|
/**
|
|
768
844
|
* Memoized per-panel templates keyed by the inputs they render from.
|
|
769
845
|
* When the underlying data hasn't changed (same `_conversation` /
|
|
@@ -775,9 +851,17 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
775
851
|
* reference; if any element flips, the cache misses and rebuilds.
|
|
776
852
|
*/
|
|
777
853
|
private _panelTplCache: Map<
|
|
778
|
-
|
|
854
|
+
ThreadDetailsPanelCacheSlot,
|
|
779
855
|
{ key: readonly unknown[]; tpl: TemplateResult }
|
|
780
856
|
> = new Map();
|
|
857
|
+
private _timelineItemsCache: {
|
|
858
|
+
events: ApiAgentEvent[];
|
|
859
|
+
items: TimelineItem[];
|
|
860
|
+
} | null = null;
|
|
861
|
+
private _liveEventsWithSourceIndexCache: {
|
|
862
|
+
events: ApiAgentEvent[];
|
|
863
|
+
indexedEvents: ApiAgentEvent[];
|
|
864
|
+
} | null = null;
|
|
781
865
|
/**
|
|
782
866
|
* Tracks whether we've fetched events for the current thread yet. Events
|
|
783
867
|
* fetch lazily on first sub-tab click so a large response's JSON.parse
|
|
@@ -790,8 +874,9 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
790
874
|
* lazy-load reasoning as `_eventsFetched`.
|
|
791
875
|
*/
|
|
792
876
|
private _stateFetched = false;
|
|
793
|
-
private
|
|
877
|
+
private _lastLoadKey: string | null = null;
|
|
794
878
|
private _lastSeenLiveMessageVersion = 0;
|
|
879
|
+
private _metadataAbort: AbortController | null = null;
|
|
795
880
|
private _messagesAbort: AbortController | null = null;
|
|
796
881
|
private _eventsAbort: AbortController | null = null;
|
|
797
882
|
private _stateAbort: AbortController | null = null;
|
|
@@ -801,18 +886,53 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
801
886
|
private _dividerStartWidth = 0;
|
|
802
887
|
|
|
803
888
|
static readonly COLLAPSE_THRESHOLD = 800;
|
|
804
|
-
|
|
889
|
+
static readonly TAB_LIST: ReadonlyArray<{
|
|
805
890
|
id: ThreadDetailsTab;
|
|
806
891
|
label: string;
|
|
807
892
|
}> = [
|
|
808
|
-
{ id: "
|
|
809
|
-
{ id: "
|
|
810
|
-
{ id: "
|
|
893
|
+
{ id: "timeline", label: "Timeline" },
|
|
894
|
+
{ id: "raw-events", label: "Raw AG-UI Events" },
|
|
895
|
+
{ id: "state", label: "State" },
|
|
811
896
|
];
|
|
812
897
|
|
|
898
|
+
private static providerIds = new WeakMap<ThreadDebuggerProvider, number>();
|
|
899
|
+
private static nextProviderId = 1;
|
|
900
|
+
|
|
901
|
+
private static providerLoadKey(
|
|
902
|
+
provider: ThreadDebuggerProvider | null,
|
|
903
|
+
): string {
|
|
904
|
+
if (!provider) return "provider:none";
|
|
905
|
+
let id = CpkThreadInspector.providerIds.get(provider);
|
|
906
|
+
if (!id) {
|
|
907
|
+
id = CpkThreadInspector.nextProviderId;
|
|
908
|
+
CpkThreadInspector.nextProviderId += 1;
|
|
909
|
+
CpkThreadInspector.providerIds.set(provider, id);
|
|
910
|
+
}
|
|
911
|
+
return [
|
|
912
|
+
`provider:${id}`,
|
|
913
|
+
provider.getThreadMetadata ? "metadata:1" : "metadata:0",
|
|
914
|
+
provider.getMessages ? "messages:1" : "messages:0",
|
|
915
|
+
provider.getEvents ? "events:1" : "events:0",
|
|
916
|
+
provider.getState ? "state:1" : "state:0",
|
|
917
|
+
].join("|");
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* Build a deterministic signature for runtime fetch headers so auth/CSRF
|
|
922
|
+
* changes invalidate cached thread data even when the selected thread is
|
|
923
|
+
* otherwise unchanged.
|
|
924
|
+
*/
|
|
925
|
+
private static headersLoadKey(headers: Record<string, string>): string {
|
|
926
|
+
return JSON.stringify(
|
|
927
|
+
Object.entries(headers).sort(([leftKey], [rightKey]) =>
|
|
928
|
+
leftKey.localeCompare(rightKey),
|
|
929
|
+
),
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
|
|
813
933
|
private renderTabContent(id: ThreadDetailsTab): TemplateResult {
|
|
814
|
-
if (id === "
|
|
815
|
-
if (id === "
|
|
934
|
+
if (id === "timeline") return this.renderTimeline();
|
|
935
|
+
if (id === "state") return this.renderState();
|
|
816
936
|
return this.renderEvents();
|
|
817
937
|
}
|
|
818
938
|
|
|
@@ -845,10 +965,10 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
845
965
|
// the entire panel for seconds — including making the tab buttons
|
|
846
966
|
// themselves feel unresponsive.
|
|
847
967
|
if (!this.threadId) return;
|
|
848
|
-
if (id === "
|
|
968
|
+
if ((id === "timeline" || id === "raw-events") && !this._eventsFetched) {
|
|
849
969
|
this._eventsFetched = true;
|
|
850
970
|
void this.fetchEvents(this.threadId);
|
|
851
|
-
} else if (id === "
|
|
971
|
+
} else if (id === "state" && !this._stateFetched) {
|
|
852
972
|
this._stateFetched = true;
|
|
853
973
|
void this.fetchState(this.threadId);
|
|
854
974
|
}
|
|
@@ -980,6 +1100,42 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
980
1100
|
flex-shrink: 0;
|
|
981
1101
|
}
|
|
982
1102
|
|
|
1103
|
+
.cpk-td__metadata-strip {
|
|
1104
|
+
display: flex;
|
|
1105
|
+
gap: 6px;
|
|
1106
|
+
flex-wrap: wrap;
|
|
1107
|
+
padding: 10px 16px;
|
|
1108
|
+
border-bottom: 1px solid #e9e9ef;
|
|
1109
|
+
background: #fbfbfd;
|
|
1110
|
+
flex-shrink: 0;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
.cpk-td__metadata-pill {
|
|
1114
|
+
display: inline-flex;
|
|
1115
|
+
align-items: center;
|
|
1116
|
+
gap: 5px;
|
|
1117
|
+
max-width: 220px;
|
|
1118
|
+
padding: 3px 7px;
|
|
1119
|
+
border: 1px solid #e9e9ef;
|
|
1120
|
+
border-radius: 5px;
|
|
1121
|
+
background: #ffffff;
|
|
1122
|
+
color: #57575b;
|
|
1123
|
+
font-family: "Spline Sans Mono", monospace;
|
|
1124
|
+
font-size: 10px;
|
|
1125
|
+
white-space: nowrap;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
.cpk-td__metadata-label {
|
|
1129
|
+
color: #838389;
|
|
1130
|
+
text-transform: uppercase;
|
|
1131
|
+
font-size: 9px;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
.cpk-td__metadata-value {
|
|
1135
|
+
overflow: hidden;
|
|
1136
|
+
text-overflow: ellipsis;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
983
1139
|
/*
|
|
984
1140
|
* Each tab's content is wrapped in this panel so the keep-mounted
|
|
985
1141
|
* inactive panels can be hidden via display:none without disturbing
|
|
@@ -1200,6 +1356,81 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1200
1356
|
background: #e9e9ef;
|
|
1201
1357
|
}
|
|
1202
1358
|
|
|
1359
|
+
/* ── Interaction timeline ───────────────────────────────────────── */
|
|
1360
|
+
.cpk-td__timeline-item {
|
|
1361
|
+
border: 1px solid #e9e9ef;
|
|
1362
|
+
border-radius: 6px;
|
|
1363
|
+
background: #ffffff;
|
|
1364
|
+
overflow: hidden;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
.cpk-td__timeline-item--warning {
|
|
1368
|
+
border-color: rgba(250, 95, 103, 0.35);
|
|
1369
|
+
background: rgba(250, 95, 103, 0.04);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
.cpk-td__timeline-header {
|
|
1373
|
+
display: flex;
|
|
1374
|
+
align-items: center;
|
|
1375
|
+
gap: 8px;
|
|
1376
|
+
padding: 7px 10px;
|
|
1377
|
+
background: #f7f7f9;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
.cpk-td__timeline-kind {
|
|
1381
|
+
font-family: "Spline Sans Mono", monospace;
|
|
1382
|
+
font-size: 9px;
|
|
1383
|
+
font-weight: 600;
|
|
1384
|
+
text-transform: uppercase;
|
|
1385
|
+
color: #5558b2;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
.cpk-td__timeline-title {
|
|
1389
|
+
flex: 1;
|
|
1390
|
+
min-width: 0;
|
|
1391
|
+
font-size: 12px;
|
|
1392
|
+
font-weight: 500;
|
|
1393
|
+
color: #010507;
|
|
1394
|
+
overflow: hidden;
|
|
1395
|
+
text-overflow: ellipsis;
|
|
1396
|
+
white-space: nowrap;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
.cpk-td__timeline-time {
|
|
1400
|
+
font-family: "Spline Sans Mono", monospace;
|
|
1401
|
+
font-size: 9px;
|
|
1402
|
+
color: #838389;
|
|
1403
|
+
flex-shrink: 0;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
.cpk-td__timeline-body {
|
|
1407
|
+
padding: 9px 10px;
|
|
1408
|
+
font-size: 12px;
|
|
1409
|
+
line-height: 1.55;
|
|
1410
|
+
color: #57575b;
|
|
1411
|
+
white-space: pre-wrap;
|
|
1412
|
+
word-break: break-word;
|
|
1413
|
+
border-top: 1px solid #e9e9ef;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
.cpk-td__source-link {
|
|
1417
|
+
margin: 0;
|
|
1418
|
+
padding: 0;
|
|
1419
|
+
border: none;
|
|
1420
|
+
background: transparent;
|
|
1421
|
+
color: #5558b2;
|
|
1422
|
+
cursor: pointer;
|
|
1423
|
+
font-family: "Spline Sans Mono", monospace;
|
|
1424
|
+
font-size: 9px;
|
|
1425
|
+
text-decoration: underline;
|
|
1426
|
+
text-underline-offset: 2px;
|
|
1427
|
+
flex-shrink: 0;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
.cpk-td__source-link:hover {
|
|
1431
|
+
color: #010507;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1203
1434
|
/* ── Generative UI ──────────────────────────────────────────────── */
|
|
1204
1435
|
@keyframes cpk-genui-enter {
|
|
1205
1436
|
from {
|
|
@@ -1409,36 +1640,27 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1409
1640
|
`;
|
|
1410
1641
|
|
|
1411
1642
|
updated(_changed: Map<string, unknown>): void {
|
|
1412
|
-
|
|
1413
|
-
|
|
1643
|
+
const loadKey = this.currentLoadKey();
|
|
1644
|
+
if (loadKey !== this._lastLoadKey) {
|
|
1645
|
+
this._lastLoadKey = loadKey;
|
|
1414
1646
|
this._lastSeenLiveMessageVersion = this.liveMessageVersion;
|
|
1415
|
-
this.
|
|
1416
|
-
this._activatedTabs = new Set(["conversation"]);
|
|
1417
|
-
this._panelTplCache = new Map();
|
|
1418
|
-
this._expandedTools = new Set();
|
|
1419
|
-
this._expandedMessages = new Set();
|
|
1420
|
-
this._messagesAbort?.abort();
|
|
1421
|
-
this._messagesAbort = null;
|
|
1422
|
-
this._eventsAbort?.abort();
|
|
1423
|
-
this._eventsAbort = null;
|
|
1424
|
-
this._stateAbort?.abort();
|
|
1425
|
-
this._stateAbort = null;
|
|
1426
|
-
// Reset cleared so the next click into events/state triggers a fresh
|
|
1427
|
-
// fetch. Eagerly clear `_fetchedEvents` / `_fetchedState` so the empty
|
|
1428
|
-
// state doesn't briefly show last thread's data.
|
|
1429
|
-
this._eventsFetched = false;
|
|
1430
|
-
this._stateFetched = false;
|
|
1431
|
-
this._fetchedEvents = null;
|
|
1432
|
-
this._fetchedState = null;
|
|
1647
|
+
this.resetLoadedThreadData();
|
|
1433
1648
|
|
|
1434
1649
|
if (this.threadId) {
|
|
1435
|
-
//
|
|
1436
|
-
//
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1650
|
+
// Timeline is the default tab and should be event-derived. Fetch
|
|
1651
|
+
// events eagerly; the raw tab reuses the same response when opened.
|
|
1652
|
+
void this.fetchMetadata(this.threadId);
|
|
1653
|
+
if (this.canFetchEvents()) {
|
|
1654
|
+
this._eventsFetched = true;
|
|
1655
|
+
void this.fetchEvents(this.threadId);
|
|
1656
|
+
} else {
|
|
1657
|
+
// Last-resort compatibility path for consumers that only implement
|
|
1658
|
+
// messages. New integrations should provide events so Timeline can
|
|
1659
|
+
// expose source references and decode warnings.
|
|
1660
|
+
void this.fetchMessages(this.threadId);
|
|
1661
|
+
}
|
|
1441
1662
|
} else {
|
|
1663
|
+
this._fetchedMetadata = null;
|
|
1442
1664
|
this._conversation = [];
|
|
1443
1665
|
}
|
|
1444
1666
|
} else if (
|
|
@@ -1459,6 +1681,90 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1459
1681
|
}
|
|
1460
1682
|
}
|
|
1461
1683
|
|
|
1684
|
+
private canFetchMessages(): boolean {
|
|
1685
|
+
return (
|
|
1686
|
+
!!this.provider?.getMessages ||
|
|
1687
|
+
(!!this.runtimeUrl && this.threadInspectionAvailable)
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
private canFetchEvents(): boolean {
|
|
1692
|
+
return (
|
|
1693
|
+
!!this.provider?.getEvents ||
|
|
1694
|
+
(!!this.runtimeUrl && this.threadInspectionAvailable)
|
|
1695
|
+
);
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
private canFetchState(): boolean {
|
|
1699
|
+
return (
|
|
1700
|
+
!!this.provider?.getState ||
|
|
1701
|
+
(!!this.runtimeUrl && this.threadInspectionAvailable)
|
|
1702
|
+
);
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
private currentLoadKey(): string {
|
|
1706
|
+
return [
|
|
1707
|
+
this.threadId ?? "thread:none",
|
|
1708
|
+
CpkThreadInspector.providerLoadKey(this.provider),
|
|
1709
|
+
`runtime:${this.runtimeUrl}`,
|
|
1710
|
+
`headers:${CpkThreadInspector.headersLoadKey(this.headers)}`,
|
|
1711
|
+
`inspect:${this.threadInspectionAvailable ? "1" : "0"}`,
|
|
1712
|
+
].join("||");
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
private resetLoadedThreadData(): void {
|
|
1716
|
+
this._tab = "timeline";
|
|
1717
|
+
this._activatedTabs = new Set(["timeline"]);
|
|
1718
|
+
this._panelTplCache = new Map();
|
|
1719
|
+
this._timelineItemsCache = null;
|
|
1720
|
+
this._liveEventsWithSourceIndexCache = null;
|
|
1721
|
+
this._expandedTools = new Set();
|
|
1722
|
+
this._expandedMessages = new Set();
|
|
1723
|
+
this._metadataAbort?.abort();
|
|
1724
|
+
this._metadataAbort = null;
|
|
1725
|
+
this._messagesAbort?.abort();
|
|
1726
|
+
this._messagesAbort = null;
|
|
1727
|
+
this._eventsAbort?.abort();
|
|
1728
|
+
this._eventsAbort = null;
|
|
1729
|
+
this._stateAbort?.abort();
|
|
1730
|
+
this._stateAbort = null;
|
|
1731
|
+
// Reset cleared so the next click into events/state triggers a fresh
|
|
1732
|
+
// fetch. Eagerly clear fetched data so a provider/runtime swap cannot
|
|
1733
|
+
// briefly show the old source's values for the same threadId.
|
|
1734
|
+
this._eventsFetched = false;
|
|
1735
|
+
this._stateFetched = false;
|
|
1736
|
+
this._eventsNotAvailable = false;
|
|
1737
|
+
this._stateNotAvailable = false;
|
|
1738
|
+
this._loadingMessages = false;
|
|
1739
|
+
this._loadingEvents = false;
|
|
1740
|
+
this._loadingState = false;
|
|
1741
|
+
this._messagesError = null;
|
|
1742
|
+
this._eventsError = null;
|
|
1743
|
+
this._stateError = null;
|
|
1744
|
+
this._fetchedMetadata = null;
|
|
1745
|
+
this._conversation = [];
|
|
1746
|
+
this._fetchedEvents = null;
|
|
1747
|
+
this._fetchedState = null;
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
private async fetchMetadata(threadId: string): Promise<void> {
|
|
1751
|
+
if (!this.provider?.getThreadMetadata) return;
|
|
1752
|
+
this._metadataAbort?.abort();
|
|
1753
|
+
const controller = new AbortController();
|
|
1754
|
+
this._metadataAbort = controller;
|
|
1755
|
+
try {
|
|
1756
|
+
const metadata = await this.provider.getThreadMetadata(threadId, {
|
|
1757
|
+
signal: controller.signal,
|
|
1758
|
+
});
|
|
1759
|
+
if (controller.signal.aborted || this.threadId !== threadId) return;
|
|
1760
|
+
this._fetchedMetadata = metadata;
|
|
1761
|
+
} catch (err) {
|
|
1762
|
+
if (err instanceof Error && err.name === "AbortError") return;
|
|
1763
|
+
if (this.threadId !== threadId) return;
|
|
1764
|
+
this._fetchedMetadata = null;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1462
1768
|
/**
|
|
1463
1769
|
* Fetch the canonical conversation for `threadId` from the runtime.
|
|
1464
1770
|
*
|
|
@@ -1473,10 +1779,11 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1473
1779
|
threadId: string,
|
|
1474
1780
|
silent: boolean = false,
|
|
1475
1781
|
): Promise<void> {
|
|
1476
|
-
if (!this.
|
|
1782
|
+
if (!this.canFetchMessages()) {
|
|
1477
1783
|
if (!silent) this._conversation = [];
|
|
1478
1784
|
return;
|
|
1479
1785
|
}
|
|
1786
|
+
this._messagesAbort?.abort();
|
|
1480
1787
|
const controller = new AbortController();
|
|
1481
1788
|
this._messagesAbort = controller;
|
|
1482
1789
|
if (!silent) {
|
|
@@ -1484,15 +1791,13 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1484
1791
|
this._messagesError = null;
|
|
1485
1792
|
}
|
|
1486
1793
|
try {
|
|
1487
|
-
const
|
|
1488
|
-
this.
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1493
|
-
const data = (await res.json()) as { messages: ApiThreadMessage[] };
|
|
1794
|
+
const messages = this.provider?.getMessages
|
|
1795
|
+
? await this.provider.getMessages(threadId, {
|
|
1796
|
+
signal: controller.signal,
|
|
1797
|
+
})
|
|
1798
|
+
: await this.fetchRuntimeMessages(threadId, controller.signal);
|
|
1494
1799
|
if (controller.signal.aborted || this.threadId !== threadId) return;
|
|
1495
|
-
this._conversation = this.mapMessages(
|
|
1800
|
+
this._conversation = this.mapMessages(messages);
|
|
1496
1801
|
} catch (err) {
|
|
1497
1802
|
if (err instanceof Error && err.name === "AbortError") return;
|
|
1498
1803
|
if (!silent) {
|
|
@@ -1510,45 +1815,51 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1510
1815
|
}
|
|
1511
1816
|
|
|
1512
1817
|
private async fetchEvents(threadId: string): Promise<void> {
|
|
1513
|
-
this.
|
|
1514
|
-
if (!this.runtimeUrl || !this.threadInspectionAvailable) {
|
|
1818
|
+
if (!this.canFetchEvents()) {
|
|
1515
1819
|
this._fetchedEvents = null;
|
|
1516
1820
|
return;
|
|
1517
1821
|
}
|
|
1822
|
+
this._eventsAbort?.abort();
|
|
1518
1823
|
const controller = new AbortController();
|
|
1519
1824
|
this._eventsAbort = controller;
|
|
1520
1825
|
this._loadingEvents = true;
|
|
1521
1826
|
this._eventsError = null;
|
|
1522
1827
|
try {
|
|
1523
|
-
const
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1828
|
+
const result = this.provider?.getEvents
|
|
1829
|
+
? {
|
|
1830
|
+
status: "available" as const,
|
|
1831
|
+
events: await this.provider.getEvents(threadId, {
|
|
1832
|
+
signal: controller.signal,
|
|
1833
|
+
}),
|
|
1834
|
+
}
|
|
1835
|
+
: await this.fetchRuntimeEvents(threadId, controller.signal);
|
|
1527
1836
|
// Drop results if a newer fetch superseded this one (thread switched
|
|
1528
|
-
// mid-flight). Without this, switching A→B
|
|
1529
|
-
// showing thread A's events when A's request
|
|
1837
|
+
// or provider/runtime changed mid-flight). Without this, switching A→B
|
|
1838
|
+
// can leave thread B's view showing thread A's events when A's request
|
|
1839
|
+
// resolves last.
|
|
1530
1840
|
if (controller.signal.aborted || this.threadId !== threadId) return;
|
|
1531
|
-
if (
|
|
1532
|
-
// Endpoint not supported on this runtime (e.g. Intelligence platform).
|
|
1533
|
-
// Mark unavailable so we don't misleadingly fall back to the parent's
|
|
1534
|
-
// live agent events — those are agent-keyed, not thread-keyed, and
|
|
1535
|
-
// would render identical across every thread on the same agent.
|
|
1841
|
+
if (result.status === "not-available") {
|
|
1536
1842
|
this._eventsNotAvailable = true;
|
|
1537
|
-
this._fetchedEvents =
|
|
1843
|
+
this._fetchedEvents = [];
|
|
1844
|
+
if (this.canFetchMessages()) {
|
|
1845
|
+
void this.fetchMessages(threadId);
|
|
1846
|
+
}
|
|
1538
1847
|
return;
|
|
1539
1848
|
}
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
this._fetchedEvents = this.mapApiEvents(data.events);
|
|
1849
|
+
const mappedEvents = this.mapApiEvents(result.events);
|
|
1850
|
+
this._fetchedEvents = mappedEvents;
|
|
1851
|
+
if (mappedEvents.length === 0 && this.canFetchMessages()) {
|
|
1852
|
+
void this.fetchMessages(threadId);
|
|
1853
|
+
}
|
|
1546
1854
|
} catch (err) {
|
|
1547
1855
|
if (err instanceof Error && err.name === "AbortError") return;
|
|
1548
1856
|
if (this.threadId !== threadId) return;
|
|
1549
1857
|
this._eventsError =
|
|
1550
1858
|
err instanceof Error ? err.message : "Failed to load events";
|
|
1551
1859
|
this._fetchedEvents = [];
|
|
1860
|
+
if (this.canFetchMessages()) {
|
|
1861
|
+
void this.fetchMessages(threadId);
|
|
1862
|
+
}
|
|
1552
1863
|
} finally {
|
|
1553
1864
|
if (!controller.signal.aborted && this.threadId === threadId) {
|
|
1554
1865
|
this._loadingEvents = false;
|
|
@@ -1557,32 +1868,31 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1557
1868
|
}
|
|
1558
1869
|
|
|
1559
1870
|
private async fetchState(threadId: string): Promise<void> {
|
|
1560
|
-
this.
|
|
1561
|
-
if (!this.runtimeUrl || !this.threadInspectionAvailable) {
|
|
1871
|
+
if (!this.canFetchState()) {
|
|
1562
1872
|
this._fetchedState = null;
|
|
1563
1873
|
return;
|
|
1564
1874
|
}
|
|
1875
|
+
this._stateAbort?.abort();
|
|
1565
1876
|
const controller = new AbortController();
|
|
1566
1877
|
this._stateAbort = controller;
|
|
1567
1878
|
this._loadingState = true;
|
|
1568
1879
|
this._stateError = null;
|
|
1569
1880
|
try {
|
|
1570
|
-
const
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1881
|
+
const result = this.provider?.getState
|
|
1882
|
+
? {
|
|
1883
|
+
status: "available" as const,
|
|
1884
|
+
state: await this.provider.getState(threadId, {
|
|
1885
|
+
signal: controller.signal,
|
|
1886
|
+
}),
|
|
1887
|
+
}
|
|
1888
|
+
: await this.fetchRuntimeState(threadId, controller.signal);
|
|
1574
1889
|
if (controller.signal.aborted || this.threadId !== threadId) return;
|
|
1575
|
-
if (
|
|
1890
|
+
if (result.status === "not-available") {
|
|
1576
1891
|
this._stateNotAvailable = true;
|
|
1577
1892
|
this._fetchedState = null;
|
|
1578
1893
|
return;
|
|
1579
1894
|
}
|
|
1580
|
-
|
|
1581
|
-
const data = (await res.json()) as {
|
|
1582
|
-
state: Record<string, unknown> | null;
|
|
1583
|
-
};
|
|
1584
|
-
if (controller.signal.aborted || this.threadId !== threadId) return;
|
|
1585
|
-
this._fetchedState = data.state ?? null;
|
|
1895
|
+
this._fetchedState = result.state ?? null;
|
|
1586
1896
|
} catch (err) {
|
|
1587
1897
|
if (err instanceof Error && err.name === "AbortError") return;
|
|
1588
1898
|
if (this.threadId !== threadId) return;
|
|
@@ -1596,6 +1906,55 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1596
1906
|
}
|
|
1597
1907
|
}
|
|
1598
1908
|
|
|
1909
|
+
private async fetchRuntimeMessages(
|
|
1910
|
+
threadId: string,
|
|
1911
|
+
signal: AbortSignal,
|
|
1912
|
+
): Promise<ThreadDebuggerMessage[]> {
|
|
1913
|
+
const res = await fetch(this.getThreadInspectionUrl(threadId, "messages"), {
|
|
1914
|
+
headers: { ...this.headers },
|
|
1915
|
+
signal,
|
|
1916
|
+
});
|
|
1917
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1918
|
+
const data = (await res.json()) as { messages: ThreadDebuggerMessage[] };
|
|
1919
|
+
return data.messages;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
private async fetchRuntimeEvents(
|
|
1923
|
+
threadId: string,
|
|
1924
|
+
signal: AbortSignal,
|
|
1925
|
+
): Promise<RuntimeEventsFetchResult> {
|
|
1926
|
+
const res = await fetch(this.getThreadInspectionUrl(threadId, "events"), {
|
|
1927
|
+
headers: { ...this.headers },
|
|
1928
|
+
signal,
|
|
1929
|
+
});
|
|
1930
|
+
if (res.status === 501) {
|
|
1931
|
+
return { status: "not-available" };
|
|
1932
|
+
}
|
|
1933
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1934
|
+
const data = (await res.json()) as {
|
|
1935
|
+
events: ThreadDebuggerEvent[];
|
|
1936
|
+
};
|
|
1937
|
+
return { status: "available", events: data.events };
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
private async fetchRuntimeState(
|
|
1941
|
+
threadId: string,
|
|
1942
|
+
signal: AbortSignal,
|
|
1943
|
+
): Promise<RuntimeStateFetchResult> {
|
|
1944
|
+
const res = await fetch(this.getThreadInspectionUrl(threadId, "state"), {
|
|
1945
|
+
headers: { ...this.headers },
|
|
1946
|
+
signal,
|
|
1947
|
+
});
|
|
1948
|
+
if (res.status === 501) {
|
|
1949
|
+
return { status: "not-available" };
|
|
1950
|
+
}
|
|
1951
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1952
|
+
const data = (await res.json()) as {
|
|
1953
|
+
state: Record<string, unknown> | null;
|
|
1954
|
+
};
|
|
1955
|
+
return { status: "available", state: data.state ?? null };
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1599
1958
|
private getThreadInspectionUrl(
|
|
1600
1959
|
threadId: string,
|
|
1601
1960
|
resource: "messages" | "events" | "state",
|
|
@@ -1603,7 +1962,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1603
1962
|
return `${this.runtimeUrl.replace(/\/+$/, "")}/threads/${encodeURIComponent(threadId)}/${resource}`;
|
|
1604
1963
|
}
|
|
1605
1964
|
|
|
1606
|
-
private mapMessages(messages:
|
|
1965
|
+
private mapMessages(messages: ThreadDebuggerMessage[]): ConversationItem[] {
|
|
1607
1966
|
const items: ConversationItem[] = [];
|
|
1608
1967
|
const toolCallMap = new Map<string, ConversationToolCall>();
|
|
1609
1968
|
for (const msg of messages) {
|
|
@@ -1618,19 +1977,20 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1618
1977
|
if (msg.toolCalls?.length) {
|
|
1619
1978
|
for (const tc of msg.toolCalls) {
|
|
1620
1979
|
let args: Record<string, unknown> = {};
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
{
|
|
1632
|
-
|
|
1633
|
-
|
|
1980
|
+
if (typeof tc.args === "string") {
|
|
1981
|
+
try {
|
|
1982
|
+
args = JSON.parse(tc.args) as Record<string, unknown>;
|
|
1983
|
+
} catch (err) {
|
|
1984
|
+
// Inspector is a debugging surface — surface malformed payloads
|
|
1985
|
+
// instead of silently substituting `{}`.
|
|
1986
|
+
console.error(
|
|
1987
|
+
"[CopilotKit Inspector] Failed to parse tool-call arguments",
|
|
1988
|
+
{ toolCallId: tc.id, raw: tc.args, error: err },
|
|
1989
|
+
);
|
|
1990
|
+
args = { __parseError: true, __raw: tc.args };
|
|
1991
|
+
}
|
|
1992
|
+
} else {
|
|
1993
|
+
args = tc.args;
|
|
1634
1994
|
}
|
|
1635
1995
|
const item: ConversationToolCall = {
|
|
1636
1996
|
id: tc.id,
|
|
@@ -1684,22 +2044,284 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1684
2044
|
return items;
|
|
1685
2045
|
}
|
|
1686
2046
|
|
|
1687
|
-
private mapApiEvents(
|
|
1688
|
-
events
|
|
1689
|
-
|
|
1690
|
-
return events.map((event) => {
|
|
1691
|
-
const { type, timestamp, ...rest } = event;
|
|
2047
|
+
private mapApiEvents(events: ThreadDebuggerEvent[]): ApiAgentEvent[] {
|
|
2048
|
+
return events.map((event, index) => {
|
|
2049
|
+
const { type, timestamp, payload, ...rest } = event;
|
|
1692
2050
|
return {
|
|
1693
2051
|
type: typeof type === "string" ? type : "UNKNOWN",
|
|
1694
2052
|
timestamp:
|
|
1695
2053
|
typeof timestamp === "string" || typeof timestamp === "number"
|
|
1696
2054
|
? timestamp
|
|
1697
2055
|
: Date.now(),
|
|
1698
|
-
payload: rest,
|
|
2056
|
+
payload: payload ?? rest,
|
|
2057
|
+
sourceIndex: index + 1,
|
|
2058
|
+
rawEvent: event,
|
|
1699
2059
|
};
|
|
1700
2060
|
});
|
|
1701
2061
|
}
|
|
1702
2062
|
|
|
2063
|
+
private get activeTimelineItems(): TimelineItem[] {
|
|
2064
|
+
return this.timelineItemsForEvents(this.activeEvents);
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
private timelineItemsForEvents(events: ApiAgentEvent[]): TimelineItem[] {
|
|
2068
|
+
if (this._timelineItemsCache?.events === events) {
|
|
2069
|
+
return this._timelineItemsCache.items;
|
|
2070
|
+
}
|
|
2071
|
+
const items = this.timelineItemsFromEvents(events);
|
|
2072
|
+
this._timelineItemsCache = { events, items };
|
|
2073
|
+
return items;
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
private timelineItemsFromEvents(events: ApiAgentEvent[]): TimelineItem[] {
|
|
2077
|
+
if (events.length === 0) return [];
|
|
2078
|
+
|
|
2079
|
+
const items: TimelineItem[] = [];
|
|
2080
|
+
const messageItems = new Map<string, TimelineItem>();
|
|
2081
|
+
const toolItems = new Map<string, TimelineItem & { rawArgs?: string }>();
|
|
2082
|
+
|
|
2083
|
+
const readString = (
|
|
2084
|
+
payload: Record<string, unknown>,
|
|
2085
|
+
keys: string[],
|
|
2086
|
+
): string | null => {
|
|
2087
|
+
for (const key of keys) {
|
|
2088
|
+
const value = payload[key];
|
|
2089
|
+
if (typeof value === "string") return value;
|
|
2090
|
+
}
|
|
2091
|
+
return null;
|
|
2092
|
+
};
|
|
2093
|
+
|
|
2094
|
+
const sourceIndexFor = (event: ApiAgentEvent): number =>
|
|
2095
|
+
event.sourceIndex ?? 0;
|
|
2096
|
+
|
|
2097
|
+
const appendWarning = (
|
|
2098
|
+
event: ApiAgentEvent,
|
|
2099
|
+
title: string,
|
|
2100
|
+
body: string,
|
|
2101
|
+
severity: "warning" | "error" = "warning",
|
|
2102
|
+
): void => {
|
|
2103
|
+
const sourceIndex = sourceIndexFor(event);
|
|
2104
|
+
items.push({
|
|
2105
|
+
id: `warning-${sourceIndex}-${items.length}`,
|
|
2106
|
+
kind: "warning",
|
|
2107
|
+
title,
|
|
2108
|
+
body,
|
|
2109
|
+
timestamp: event.timestamp,
|
|
2110
|
+
sourceIndex,
|
|
2111
|
+
severity,
|
|
2112
|
+
});
|
|
2113
|
+
};
|
|
2114
|
+
|
|
2115
|
+
const ensureMessage = (
|
|
2116
|
+
event: ApiAgentEvent,
|
|
2117
|
+
role: string,
|
|
2118
|
+
): TimelineItem => {
|
|
2119
|
+
const sourceIndex = sourceIndexFor(event);
|
|
2120
|
+
const key =
|
|
2121
|
+
readString(event.payload, ["messageId", "message_id", "id"]) ??
|
|
2122
|
+
`message-${sourceIndex}`;
|
|
2123
|
+
let item = messageItems.get(key);
|
|
2124
|
+
if (!item) {
|
|
2125
|
+
item = {
|
|
2126
|
+
id: `message-${key}`,
|
|
2127
|
+
kind: "message",
|
|
2128
|
+
title: `${role || "message"} message`,
|
|
2129
|
+
body: "",
|
|
2130
|
+
timestamp: event.timestamp,
|
|
2131
|
+
sourceIndex,
|
|
2132
|
+
};
|
|
2133
|
+
messageItems.set(key, item);
|
|
2134
|
+
items.push(item);
|
|
2135
|
+
}
|
|
2136
|
+
return item;
|
|
2137
|
+
};
|
|
2138
|
+
|
|
2139
|
+
const ensureTool = (
|
|
2140
|
+
event: ApiAgentEvent,
|
|
2141
|
+
): TimelineItem & {
|
|
2142
|
+
rawArgs?: string;
|
|
2143
|
+
} => {
|
|
2144
|
+
const sourceIndex = sourceIndexFor(event);
|
|
2145
|
+
const key =
|
|
2146
|
+
readString(event.payload, [
|
|
2147
|
+
"toolCallId",
|
|
2148
|
+
"tool_call_id",
|
|
2149
|
+
"id",
|
|
2150
|
+
"callId",
|
|
2151
|
+
]) ?? `tool-${sourceIndex}`;
|
|
2152
|
+
let item = toolItems.get(key);
|
|
2153
|
+
if (!item) {
|
|
2154
|
+
item = {
|
|
2155
|
+
id: `tool-${key}`,
|
|
2156
|
+
kind: "tool",
|
|
2157
|
+
title:
|
|
2158
|
+
readString(event.payload, [
|
|
2159
|
+
"toolCallName",
|
|
2160
|
+
"toolName",
|
|
2161
|
+
"name",
|
|
2162
|
+
"functionName",
|
|
2163
|
+
]) ?? "Tool call",
|
|
2164
|
+
body: "",
|
|
2165
|
+
timestamp: event.timestamp,
|
|
2166
|
+
sourceIndex,
|
|
2167
|
+
};
|
|
2168
|
+
toolItems.set(key, item);
|
|
2169
|
+
items.push(item);
|
|
2170
|
+
}
|
|
2171
|
+
return item;
|
|
2172
|
+
};
|
|
2173
|
+
|
|
2174
|
+
for (const event of events) {
|
|
2175
|
+
const { type, payload } = event;
|
|
2176
|
+
const sourceIndex = sourceIndexFor(event);
|
|
2177
|
+
|
|
2178
|
+
if (type === "UNKNOWN") {
|
|
2179
|
+
appendWarning(
|
|
2180
|
+
event,
|
|
2181
|
+
"Unknown AG-UI event",
|
|
2182
|
+
"The event is missing a string type and could not be normalized.",
|
|
2183
|
+
);
|
|
2184
|
+
continue;
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
if (type === "RUN_STARTED" || type === "STEP_STARTED") {
|
|
2188
|
+
items.push({
|
|
2189
|
+
id: `${type}-${sourceIndex}`,
|
|
2190
|
+
kind: "run",
|
|
2191
|
+
title: type === "RUN_STARTED" ? "Run started" : "Step started",
|
|
2192
|
+
timestamp: event.timestamp,
|
|
2193
|
+
sourceIndex,
|
|
2194
|
+
details: payload,
|
|
2195
|
+
});
|
|
2196
|
+
continue;
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
if (type === "RUN_FINISHED" || type === "STEP_FINISHED") {
|
|
2200
|
+
items.push({
|
|
2201
|
+
id: `${type}-${sourceIndex}`,
|
|
2202
|
+
kind: "run",
|
|
2203
|
+
title: type === "RUN_FINISHED" ? "Run finished" : "Step finished",
|
|
2204
|
+
timestamp: event.timestamp,
|
|
2205
|
+
sourceIndex,
|
|
2206
|
+
details: payload,
|
|
2207
|
+
});
|
|
2208
|
+
continue;
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
if (type === "RUN_ERROR" || type === "ERROR") {
|
|
2212
|
+
items.push({
|
|
2213
|
+
id: `${type}-${sourceIndex}`,
|
|
2214
|
+
kind: "warning",
|
|
2215
|
+
title: "Run error",
|
|
2216
|
+
body: readString(payload, ["message", "error", "description"]) ?? "",
|
|
2217
|
+
timestamp: event.timestamp,
|
|
2218
|
+
sourceIndex,
|
|
2219
|
+
severity: "error",
|
|
2220
|
+
details: payload,
|
|
2221
|
+
});
|
|
2222
|
+
continue;
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
if (type === "TEXT_MESSAGE_START") {
|
|
2226
|
+
ensureMessage(event, readString(payload, ["role"]) ?? "assistant");
|
|
2227
|
+
continue;
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
if (type === "TEXT_MESSAGE_CONTENT") {
|
|
2231
|
+
const item = ensureMessage(
|
|
2232
|
+
event,
|
|
2233
|
+
readString(payload, ["role"]) ?? "assistant",
|
|
2234
|
+
);
|
|
2235
|
+
item.body = `${item.body ?? ""}${
|
|
2236
|
+
readString(payload, ["delta", "content", "text"]) ?? ""
|
|
2237
|
+
}`;
|
|
2238
|
+
continue;
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
if (type === "TEXT_MESSAGE_END") {
|
|
2242
|
+
ensureMessage(event, readString(payload, ["role"]) ?? "assistant");
|
|
2243
|
+
continue;
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
if (type === "TOOL_CALL_START") {
|
|
2247
|
+
ensureTool(event);
|
|
2248
|
+
continue;
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
if (type === "TOOL_CALL_ARGS") {
|
|
2252
|
+
const item = ensureTool(event);
|
|
2253
|
+
const chunk =
|
|
2254
|
+
readString(payload, ["args", "arguments", "delta"]) ??
|
|
2255
|
+
(typeof payload.args === "object"
|
|
2256
|
+
? JSON.stringify(payload.args)
|
|
2257
|
+
: null);
|
|
2258
|
+
if (chunk) {
|
|
2259
|
+
item.rawArgs = `${item.rawArgs ?? ""}${chunk}`;
|
|
2260
|
+
item.body = item.rawArgs;
|
|
2261
|
+
}
|
|
2262
|
+
continue;
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
if (type === "TOOL_CALL_END") {
|
|
2266
|
+
const item = ensureTool(event);
|
|
2267
|
+
if (item.rawArgs) {
|
|
2268
|
+
try {
|
|
2269
|
+
JSON.parse(item.rawArgs);
|
|
2270
|
+
} catch {
|
|
2271
|
+
appendWarning(
|
|
2272
|
+
event,
|
|
2273
|
+
"Could not decode tool call arguments",
|
|
2274
|
+
item.rawArgs,
|
|
2275
|
+
);
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
continue;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
if (type === "TOOL_CALL_RESULT") {
|
|
2282
|
+
const item = ensureTool(event);
|
|
2283
|
+
const result = readString(payload, ["result", "content", "delta"]);
|
|
2284
|
+
if (result) {
|
|
2285
|
+
item.body = item.body
|
|
2286
|
+
? `${item.body}\nResult: ${result}`
|
|
2287
|
+
: `Result: ${result}`;
|
|
2288
|
+
try {
|
|
2289
|
+
JSON.parse(result);
|
|
2290
|
+
} catch {
|
|
2291
|
+
appendWarning(event, "Could not decode tool result", result);
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
continue;
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
if (type.startsWith("STATE_")) {
|
|
2298
|
+
items.push({
|
|
2299
|
+
id: `${type}-${sourceIndex}`,
|
|
2300
|
+
kind: "state",
|
|
2301
|
+
title:
|
|
2302
|
+
type === "STATE_SNAPSHOT"
|
|
2303
|
+
? "State snapshot captured"
|
|
2304
|
+
: "State delta captured",
|
|
2305
|
+
timestamp: event.timestamp,
|
|
2306
|
+
sourceIndex,
|
|
2307
|
+
details: payload,
|
|
2308
|
+
});
|
|
2309
|
+
continue;
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
items.push({
|
|
2313
|
+
id: `event-${sourceIndex}`,
|
|
2314
|
+
kind: "event",
|
|
2315
|
+
title: type,
|
|
2316
|
+
timestamp: event.timestamp,
|
|
2317
|
+
sourceIndex,
|
|
2318
|
+
details: payload,
|
|
2319
|
+
});
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
return items;
|
|
2323
|
+
}
|
|
2324
|
+
|
|
1703
2325
|
private get renderItems(): RenderItem[] {
|
|
1704
2326
|
const items = this._conversation;
|
|
1705
2327
|
const result: RenderItem[] = [];
|
|
@@ -1742,7 +2364,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1742
2364
|
}
|
|
1743
2365
|
|
|
1744
2366
|
private get duration(): string {
|
|
1745
|
-
const t = this.
|
|
2367
|
+
const t = this.metadata;
|
|
1746
2368
|
if (!t?.createdAt || !t?.updatedAt) return "—";
|
|
1747
2369
|
const ms =
|
|
1748
2370
|
new Date(t.updatedAt).getTime() - new Date(t.createdAt).getTime();
|
|
@@ -1775,7 +2397,16 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1775
2397
|
// threads (those would render identically for every thread on the same
|
|
1776
2398
|
// agent and mislead the reader).
|
|
1777
2399
|
if (this._eventsNotAvailable) return [];
|
|
1778
|
-
|
|
2400
|
+
const events = this._fetchedEvents ?? this.agentEventsInput ?? [];
|
|
2401
|
+
if (events.every((event) => event.sourceIndex != null)) return events;
|
|
2402
|
+
if (this._liveEventsWithSourceIndexCache?.events === events) {
|
|
2403
|
+
return this._liveEventsWithSourceIndexCache.indexedEvents;
|
|
2404
|
+
}
|
|
2405
|
+
const indexedEvents = events.map((event, index) =>
|
|
2406
|
+
event.sourceIndex == null ? { ...event, sourceIndex: index + 1 } : event,
|
|
2407
|
+
);
|
|
2408
|
+
this._liveEventsWithSourceIndexCache = { events, indexedEvents };
|
|
2409
|
+
return indexedEvents;
|
|
1779
2410
|
}
|
|
1780
2411
|
|
|
1781
2412
|
private get activeState(): Record<string, unknown> | null {
|
|
@@ -1793,6 +2424,10 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1793
2424
|
return id.length > 20 ? id.slice(0, 8) + "…" : id;
|
|
1794
2425
|
}
|
|
1795
2426
|
|
|
2427
|
+
private get metadata(): ThreadDebuggerMetadata | null {
|
|
2428
|
+
return this._fetchedMetadata ?? this.thread ?? null;
|
|
2429
|
+
}
|
|
2430
|
+
|
|
1796
2431
|
private fmtTime(dateStr: string | null | undefined): string {
|
|
1797
2432
|
if (!dateStr) return "—";
|
|
1798
2433
|
const d = new Date(dateStr);
|
|
@@ -1841,7 +2476,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1841
2476
|
<!-- Tab bar -->
|
|
1842
2477
|
<div class="cpk-td__tabs-header">
|
|
1843
2478
|
<div class="cpk-td__tab-group" role="tablist">
|
|
1844
|
-
${
|
|
2479
|
+
${CpkThreadInspector.TAB_LIST.map(
|
|
1845
2480
|
(tab) => html`
|
|
1846
2481
|
<button
|
|
1847
2482
|
role="tab"
|
|
@@ -1857,6 +2492,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1857
2492
|
</div>
|
|
1858
2493
|
${this.renderPanelToggle()}
|
|
1859
2494
|
</div>
|
|
2495
|
+
${this.renderMetadataStrip()}
|
|
1860
2496
|
|
|
1861
2497
|
<!-- Scrollable content -->
|
|
1862
2498
|
<div class="cpk-td__content">
|
|
@@ -1867,18 +2503,18 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1867
2503
|
`
|
|
1868
2504
|
: nothing
|
|
1869
2505
|
}
|
|
1870
|
-
${
|
|
2506
|
+
${CpkThreadInspector.TAB_LIST.map((tab) =>
|
|
1871
2507
|
this._activatedTabs.has(tab.id)
|
|
1872
2508
|
? html`<div
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
2509
|
+
class="cpk-td__panel"
|
|
2510
|
+
style=${
|
|
2511
|
+
this._tab === tab.id && !this._panelInitializing
|
|
2512
|
+
? ""
|
|
2513
|
+
: "display:none"
|
|
2514
|
+
}
|
|
2515
|
+
>
|
|
2516
|
+
${this.renderTabContent(tab.id)}
|
|
2517
|
+
</div>`
|
|
1882
2518
|
: nothing,
|
|
1883
2519
|
)}
|
|
1884
2520
|
</div>
|
|
@@ -1915,6 +2551,134 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1915
2551
|
`;
|
|
1916
2552
|
}
|
|
1917
2553
|
|
|
2554
|
+
private renderMetadataStrip() {
|
|
2555
|
+
const metadata = this.metadata;
|
|
2556
|
+
const pills: Array<{ label: string; value: string | null | undefined }> = [
|
|
2557
|
+
{ label: "Thread", value: metadata?.id ?? this.threadId },
|
|
2558
|
+
{ label: "Agent", value: metadata?.agentId },
|
|
2559
|
+
{
|
|
2560
|
+
label: "End user",
|
|
2561
|
+
value: metadata?.endUserId ?? metadata?.createdById,
|
|
2562
|
+
},
|
|
2563
|
+
{ label: "Status", value: metadata?.status },
|
|
2564
|
+
].filter((pill) => pill.value != null && pill.value !== "");
|
|
2565
|
+
|
|
2566
|
+
if (pills.length === 0) return nothing;
|
|
2567
|
+
|
|
2568
|
+
return html`
|
|
2569
|
+
<div class="cpk-td__metadata-strip" aria-label="Thread metadata">
|
|
2570
|
+
${pills.map(
|
|
2571
|
+
(pill) => html`
|
|
2572
|
+
<span class="cpk-td__metadata-pill" title=${pill.value ?? ""}>
|
|
2573
|
+
<span class="cpk-td__metadata-label">${pill.label}</span>
|
|
2574
|
+
<span class="cpk-td__metadata-value"
|
|
2575
|
+
>${this.shortId(pill.value)}</span
|
|
2576
|
+
>
|
|
2577
|
+
</span>
|
|
2578
|
+
`,
|
|
2579
|
+
)}
|
|
2580
|
+
</div>
|
|
2581
|
+
`;
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
private revealSourceEvent(sourceIndex: number): void {
|
|
2585
|
+
this._activatedTabs = new Set([...this._activatedTabs, "raw-events"]);
|
|
2586
|
+
this._tab = "raw-events";
|
|
2587
|
+
this.requestUpdate();
|
|
2588
|
+
requestAnimationFrame(() => {
|
|
2589
|
+
const source = this.shadowRoot?.querySelector<HTMLElement>(
|
|
2590
|
+
`[data-source-index="${sourceIndex}"]`,
|
|
2591
|
+
);
|
|
2592
|
+
source?.scrollIntoView?.({ block: "center" });
|
|
2593
|
+
});
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
private renderTimeline() {
|
|
2597
|
+
if (this._loadingEvents) {
|
|
2598
|
+
return html`
|
|
2599
|
+
<div class="cpk-td__status">Loading timeline…</div>
|
|
2600
|
+
`;
|
|
2601
|
+
}
|
|
2602
|
+
if (this._eventsError) {
|
|
2603
|
+
return html`<div class="cpk-td__status cpk-td__status--error">
|
|
2604
|
+
${this._eventsError}
|
|
2605
|
+
</div>`;
|
|
2606
|
+
}
|
|
2607
|
+
if (this._eventsNotAvailable) {
|
|
2608
|
+
if (this._conversation.length > 0) return this.renderConversation();
|
|
2609
|
+
if (this._loadingMessages) return this.renderConversation();
|
|
2610
|
+
return html`
|
|
2611
|
+
<div class="cpk-td__empty-state">
|
|
2612
|
+
<span>Timeline event history not available</span>
|
|
2613
|
+
<span class="cpk-td__empty-hint"
|
|
2614
|
+
>This runtime doesn't yet expose per-thread AG-UI events. Check State for
|
|
2615
|
+
the latest snapshot when available.</span
|
|
2616
|
+
>
|
|
2617
|
+
</div>
|
|
2618
|
+
`;
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
const events = this.activeEvents;
|
|
2622
|
+
const cachedTimeline = this.getCachedPanelTpl("timeline", [events]);
|
|
2623
|
+
if (cachedTimeline) return cachedTimeline;
|
|
2624
|
+
|
|
2625
|
+
const timelineItems = this.timelineItemsForEvents(events);
|
|
2626
|
+
if (timelineItems.length === 0) {
|
|
2627
|
+
if (this._conversation.length > 0) return this.renderConversation();
|
|
2628
|
+
if (this._loadingMessages) return this.renderConversation();
|
|
2629
|
+
return html`
|
|
2630
|
+
<div class="cpk-td__empty-state">
|
|
2631
|
+
<span>No timeline events captured</span>
|
|
2632
|
+
<span class="cpk-td__empty-hint"
|
|
2633
|
+
>Timeline rows are normalized from AG-UI events. Open Raw AG-UI Events or
|
|
2634
|
+
State to inspect the available thread data.</span
|
|
2635
|
+
>
|
|
2636
|
+
</div>
|
|
2637
|
+
`;
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
return this.cachedPanelTpl("timeline", [events], () => {
|
|
2641
|
+
return html`${timelineItems.map((item) => this.renderTimelineItem(item))}`;
|
|
2642
|
+
});
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
private renderTimelineItem(item: TimelineItem) {
|
|
2646
|
+
const isWarning = item.kind === "warning";
|
|
2647
|
+
return html`
|
|
2648
|
+
<div
|
|
2649
|
+
class="cpk-td__timeline-item ${
|
|
2650
|
+
isWarning ? "cpk-td__timeline-item--warning" : ""
|
|
2651
|
+
}"
|
|
2652
|
+
>
|
|
2653
|
+
<div class="cpk-td__timeline-header">
|
|
2654
|
+
<span class="cpk-td__timeline-kind"
|
|
2655
|
+
>${item.severity === "error" ? "error" : item.kind}</span
|
|
2656
|
+
>
|
|
2657
|
+
<span class="cpk-td__timeline-title">${item.title}</span>
|
|
2658
|
+
<button
|
|
2659
|
+
type="button"
|
|
2660
|
+
class="cpk-td__source-link"
|
|
2661
|
+
@click=${() => this.revealSourceEvent(item.sourceIndex)}
|
|
2662
|
+
>
|
|
2663
|
+
Source event #${item.sourceIndex}
|
|
2664
|
+
</button>
|
|
2665
|
+
<span class="cpk-td__timeline-time"
|
|
2666
|
+
>${formatTimestamp(item.timestamp)}</span
|
|
2667
|
+
>
|
|
2668
|
+
</div>
|
|
2669
|
+
${
|
|
2670
|
+
item.body
|
|
2671
|
+
? html`<div class="cpk-td__timeline-body">${item.body}</div>`
|
|
2672
|
+
: item.details
|
|
2673
|
+
? html`<pre class="cpk-td__timeline-body">
|
|
2674
|
+
${unsafeHTML(highlightedJson(item.details))}</pre
|
|
2675
|
+
>`
|
|
2676
|
+
: nothing
|
|
2677
|
+
}
|
|
2678
|
+
</div>
|
|
2679
|
+
`;
|
|
2680
|
+
}
|
|
2681
|
+
|
|
1918
2682
|
private renderConversation() {
|
|
1919
2683
|
if (this._loadingMessages) {
|
|
1920
2684
|
return html`
|
|
@@ -1951,7 +2715,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1951
2715
|
// `_conversation` — without those keys the cache returns the
|
|
1952
2716
|
// pre-toggle template and the disclosure appears broken.
|
|
1953
2717
|
return this.cachedPanelTpl(
|
|
1954
|
-
"
|
|
2718
|
+
"timeline-fallback",
|
|
1955
2719
|
[this._conversation, this._expandedTools, this._expandedMessages],
|
|
1956
2720
|
() => {
|
|
1957
2721
|
const items = this.renderItems;
|
|
@@ -1970,10 +2734,21 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1970
2734
|
* change without the listed key flipping.
|
|
1971
2735
|
*/
|
|
1972
2736
|
private cachedPanelTpl(
|
|
1973
|
-
slot:
|
|
2737
|
+
slot: ThreadDetailsPanelCacheSlot,
|
|
1974
2738
|
key: readonly unknown[],
|
|
1975
2739
|
build: () => TemplateResult,
|
|
1976
2740
|
): TemplateResult {
|
|
2741
|
+
const cached = this.getCachedPanelTpl(slot, key);
|
|
2742
|
+
if (cached) return cached;
|
|
2743
|
+
const tpl = build();
|
|
2744
|
+
this._panelTplCache.set(slot, { key, tpl });
|
|
2745
|
+
return tpl;
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
private getCachedPanelTpl(
|
|
2749
|
+
slot: ThreadDetailsPanelCacheSlot,
|
|
2750
|
+
key: readonly unknown[],
|
|
2751
|
+
): TemplateResult | null {
|
|
1977
2752
|
const cached = this._panelTplCache.get(slot);
|
|
1978
2753
|
if (
|
|
1979
2754
|
cached &&
|
|
@@ -1982,9 +2757,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1982
2757
|
) {
|
|
1983
2758
|
return cached.tpl;
|
|
1984
2759
|
}
|
|
1985
|
-
|
|
1986
|
-
this._panelTplCache.set(slot, { key, tpl });
|
|
1987
|
-
return tpl;
|
|
2760
|
+
return null;
|
|
1988
2761
|
}
|
|
1989
2762
|
|
|
1990
2763
|
private renderRenderItem(item: RenderItem) {
|
|
@@ -2015,7 +2788,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
2015
2788
|
|
|
2016
2789
|
private renderBubble(item: ConversationUser | ConversationAssistant) {
|
|
2017
2790
|
const isUser = item.type === "user";
|
|
2018
|
-
const threshold =
|
|
2791
|
+
const threshold = CpkThreadInspector.COLLAPSE_THRESHOLD;
|
|
2019
2792
|
const expanded = this._expandedMessages.has(item.id);
|
|
2020
2793
|
const tooLong = item.content.length > threshold;
|
|
2021
2794
|
const shown =
|
|
@@ -2198,7 +2971,7 @@ ${unsafeHTML(highlightedJson(item.result))}</pre
|
|
|
2198
2971
|
`;
|
|
2199
2972
|
}
|
|
2200
2973
|
const stateValue = this.activeState;
|
|
2201
|
-
return this.cachedPanelTpl("
|
|
2974
|
+
return this.cachedPanelTpl("state", [stateValue], () => {
|
|
2202
2975
|
return html`<pre class="cpk-td__json-block">
|
|
2203
2976
|
${unsafeHTML(highlightedJson(stateValue))}</pre
|
|
2204
2977
|
>`;
|
|
@@ -2238,11 +3011,11 @@ ${unsafeHTML(highlightedJson(stateValue))}</pre
|
|
|
2238
3011
|
</div>
|
|
2239
3012
|
`;
|
|
2240
3013
|
}
|
|
2241
|
-
return this.cachedPanelTpl("
|
|
3014
|
+
return this.cachedPanelTpl("raw-events", [events], () => {
|
|
2242
3015
|
return html`${events.map((event) => {
|
|
2243
3016
|
const { bg, fg } = eventColors(event.type);
|
|
2244
3017
|
return html`
|
|
2245
|
-
<div class="cpk-td__event">
|
|
3018
|
+
<div class="cpk-td__event" data-source-index=${event.sourceIndex}>
|
|
2246
3019
|
<div class="cpk-td__event-header" style="background:${bg}">
|
|
2247
3020
|
<span class="cpk-td__event-type" style="color:${fg}"
|
|
2248
3021
|
>${event.type}</span
|
|
@@ -2252,7 +3025,7 @@ ${unsafeHTML(highlightedJson(stateValue))}</pre
|
|
|
2252
3025
|
>
|
|
2253
3026
|
</div>
|
|
2254
3027
|
<pre class="cpk-td__event-payload">
|
|
2255
|
-
${unsafeHTML(highlightedJson(event.
|
|
3028
|
+
${unsafeHTML(highlightedJson(event.rawEvent ?? event))}</pre
|
|
2256
3029
|
>
|
|
2257
3030
|
</div>
|
|
2258
3031
|
`;
|
|
@@ -2291,29 +3064,42 @@ ${unsafeHTML(highlightedJson(event.payload))}</pre
|
|
|
2291
3064
|
|
|
2292
3065
|
private renderDetailPanel() {
|
|
2293
3066
|
const counts = this.activityCounts;
|
|
3067
|
+
const metadata = this.metadata;
|
|
2294
3068
|
return html`
|
|
2295
3069
|
<!-- Thread -->
|
|
2296
3070
|
<div class="cpk-tdp__section-title">Thread</div>
|
|
2297
3071
|
<div class="cpk-tdp__row">
|
|
2298
3072
|
<span class="cpk-tdp__label">ID</span>
|
|
2299
3073
|
<span class="cpk-tdp__value cpk-tdp__value--wrap"
|
|
2300
|
-
>${this.shortId(
|
|
3074
|
+
>${this.shortId(metadata?.id)}</span
|
|
2301
3075
|
>
|
|
2302
3076
|
</div>
|
|
2303
3077
|
<div class="cpk-tdp__row">
|
|
2304
3078
|
<span class="cpk-tdp__label">Name</span>
|
|
2305
|
-
<span class="cpk-tdp__value">${
|
|
3079
|
+
<span class="cpk-tdp__value">${metadata?.name ?? "—"}</span>
|
|
2306
3080
|
</div>
|
|
2307
3081
|
<div class="cpk-tdp__row">
|
|
2308
3082
|
<span class="cpk-tdp__label">Agent</span>
|
|
2309
3083
|
<span class="cpk-tdp__value cpk-tdp__value--truncate"
|
|
2310
|
-
>${
|
|
3084
|
+
>${metadata?.agentId ?? "—"}</span
|
|
3085
|
+
>
|
|
3086
|
+
</div>
|
|
3087
|
+
<div class="cpk-tdp__row">
|
|
3088
|
+
<span class="cpk-tdp__label">End user</span>
|
|
3089
|
+
<span class="cpk-tdp__value cpk-tdp__value--truncate"
|
|
3090
|
+
>${metadata?.endUserId ?? "—"}</span
|
|
2311
3091
|
>
|
|
2312
3092
|
</div>
|
|
2313
3093
|
<div class="cpk-tdp__row">
|
|
2314
3094
|
<span class="cpk-tdp__label">Created by</span>
|
|
2315
3095
|
<span class="cpk-tdp__value cpk-tdp__value--truncate"
|
|
2316
|
-
>${
|
|
3096
|
+
>${metadata?.createdById ?? "—"}</span
|
|
3097
|
+
>
|
|
3098
|
+
</div>
|
|
3099
|
+
<div class="cpk-tdp__row">
|
|
3100
|
+
<span class="cpk-tdp__label">Status</span>
|
|
3101
|
+
<span class="cpk-tdp__value cpk-tdp__value--truncate"
|
|
3102
|
+
>${metadata?.status ?? "—"}</span
|
|
2317
3103
|
>
|
|
2318
3104
|
</div>
|
|
2319
3105
|
|
|
@@ -2323,11 +3109,11 @@ ${unsafeHTML(highlightedJson(event.payload))}</pre
|
|
|
2323
3109
|
<div class="cpk-tdp__section-title">Timestamps</div>
|
|
2324
3110
|
<div class="cpk-tdp__row">
|
|
2325
3111
|
<span class="cpk-tdp__label">Created</span>
|
|
2326
|
-
<span class="cpk-tdp__value">${this.fmtTime(
|
|
3112
|
+
<span class="cpk-tdp__value">${this.fmtTime(metadata?.createdAt)}</span>
|
|
2327
3113
|
</div>
|
|
2328
3114
|
<div class="cpk-tdp__row">
|
|
2329
3115
|
<span class="cpk-tdp__label">Updated</span>
|
|
2330
|
-
<span class="cpk-tdp__value">${this.fmtTime(
|
|
3116
|
+
<span class="cpk-tdp__value">${this.fmtTime(metadata?.updatedAt)}</span>
|
|
2331
3117
|
</div>
|
|
2332
3118
|
<div class="cpk-tdp__row">
|
|
2333
3119
|
<span class="cpk-tdp__label">Duration</span>
|
|
@@ -2354,9 +3140,17 @@ ${unsafeHTML(highlightedJson(event.payload))}</pre
|
|
|
2354
3140
|
}
|
|
2355
3141
|
}
|
|
2356
3142
|
|
|
3143
|
+
// Backwards-compatible internal element name used by the full CopilotKit
|
|
3144
|
+
// Inspector shell. Keep this class thin so the public body remains the single
|
|
3145
|
+
// implementation.
|
|
3146
|
+
export class ɵCpkThreadDetails extends CpkThreadInspector {}
|
|
3147
|
+
|
|
2357
3148
|
if (!customElements.get("cpk-thread-list")) {
|
|
2358
3149
|
customElements.define("cpk-thread-list", CpkThreadList);
|
|
2359
3150
|
}
|
|
3151
|
+
if (!customElements.get(THREAD_INSPECTOR_TAG)) {
|
|
3152
|
+
customElements.define(THREAD_INSPECTOR_TAG, CpkThreadInspector);
|
|
3153
|
+
}
|
|
2360
3154
|
if (!customElements.get("cpk-thread-details")) {
|
|
2361
3155
|
customElements.define("cpk-thread-details", ɵCpkThreadDetails);
|
|
2362
3156
|
}
|
|
@@ -2752,6 +3546,7 @@ export class WebInspectorElement extends LitElement {
|
|
|
2752
3546
|
},
|
|
2753
3547
|
onHeadersChanged: ({ headers }) => {
|
|
2754
3548
|
this.updateOwnedThreadStoreHeaders(headers);
|
|
3549
|
+
this.requestUpdate();
|
|
2755
3550
|
},
|
|
2756
3551
|
onError: ({ code, error }) => {
|
|
2757
3552
|
this.lastCoreError = { code, message: error.message };
|
|
@@ -2760,6 +3555,14 @@ export class WebInspectorElement extends LitElement {
|
|
|
2760
3555
|
onAgentsChanged: ({ agents }) => {
|
|
2761
3556
|
this.processAgentsChanged(agents);
|
|
2762
3557
|
},
|
|
3558
|
+
onAgentRunStarted: ({ agent }) => {
|
|
3559
|
+
// Per-thread clones (from useAgent) are not in the agent registry, so
|
|
3560
|
+
// onAgentsChanged never fires for them. Subscribe to the running
|
|
3561
|
+
// instance here so its AG-UI events reach the event timeline.
|
|
3562
|
+
if (agent?.agentId) {
|
|
3563
|
+
this.subscribeToAgent(agent);
|
|
3564
|
+
}
|
|
3565
|
+
},
|
|
2763
3566
|
onContextChanged: ({ context }) => {
|
|
2764
3567
|
this.contextStore = this.normalizeContextStore(context);
|
|
2765
3568
|
this.requestUpdate();
|
|
@@ -5832,7 +6635,9 @@ ${argsString}</pre
|
|
|
5832
6635
|
|
|
5833
6636
|
<div class="space-y-2">
|
|
5834
6637
|
<h3 class="text-sm text-slate-500">Privacy</h3>
|
|
5835
|
-
<div
|
|
6638
|
+
<div
|
|
6639
|
+
class="rounded-lg border border-slate-200 bg-white p-4 space-y-3"
|
|
6640
|
+
>
|
|
5836
6641
|
<p class="text-sm text-gray-600 flex items-start gap-2">
|
|
5837
6642
|
<span>${optedOut ? "❌" : "✅"}</span>
|
|
5838
6643
|
<span>
|
|
@@ -5848,7 +6653,8 @@ ${argsString}</pre
|
|
|
5848
6653
|
href=${TELEMETRY_DOCS_URL}
|
|
5849
6654
|
target="_blank"
|
|
5850
6655
|
rel="noopener"
|
|
5851
|
-
|
|
6656
|
+
>Learn more →</a
|
|
6657
|
+
>
|
|
5852
6658
|
</div>
|
|
5853
6659
|
</div>
|
|
5854
6660
|
</div>
|
|
@@ -6527,28 +7333,30 @@ ${argsString}</pre
|
|
|
6527
7333
|
<div class="relative h-full w-full overflow-y-auto overflow-x-hidden">
|
|
6528
7334
|
<table class="w-full table-fixed border-collapse text-xs box-border">
|
|
6529
7335
|
<colgroup>
|
|
6530
|
-
<col style="width:${this.evtColWidths[0]}px"
|
|
6531
|
-
<col style="width:${this.evtColWidths[1]}px"
|
|
6532
|
-
<col style="width:${this.evtColWidths[2]}px"
|
|
6533
|
-
<col
|
|
7336
|
+
<col style="width:${this.evtColWidths[0]}px" />
|
|
7337
|
+
<col style="width:${this.evtColWidths[1]}px" />
|
|
7338
|
+
<col style="width:${this.evtColWidths[2]}px" />
|
|
7339
|
+
<col />
|
|
6534
7340
|
</colgroup>
|
|
6535
7341
|
<thead class="sticky top-0 z-10">
|
|
6536
7342
|
<tr class="bg-white">
|
|
6537
7343
|
${["Agent", "Time", "Event Type"].map(
|
|
6538
|
-
(label, col) =>
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
7344
|
+
(label, col) =>
|
|
7345
|
+
html` <th
|
|
7346
|
+
class="border-b border-gray-200 bg-white px-3 py-2 text-left font-medium text-gray-900"
|
|
7347
|
+
style="position:relative;overflow:hidden;"
|
|
7348
|
+
>
|
|
7349
|
+
${label}
|
|
7350
|
+
<div
|
|
7351
|
+
style="position:absolute;top:0;right:0;width:5px;height:100%;cursor:col-resize;user-select:none;background:transparent;"
|
|
7352
|
+
@pointerdown=${(e: PointerEvent) =>
|
|
7353
|
+
this._onEvtColResizeStart(e, col)}
|
|
7354
|
+
@pointermove=${(e: PointerEvent) =>
|
|
7355
|
+
this._onEvtColResizeMove(e)}
|
|
7356
|
+
@pointerup=${() => this._onEvtColResizeEnd()}
|
|
7357
|
+
@pointercancel=${() => this._onEvtColResizeEnd()}
|
|
7358
|
+
></div>
|
|
7359
|
+
</th>`,
|
|
6552
7360
|
)}
|
|
6553
7361
|
<th
|
|
6554
7362
|
class="border-b border-gray-200 bg-white px-3 py-2 text-left font-medium text-gray-900"
|
|
@@ -6880,8 +7688,14 @@ ${prettyEvent}</pre
|
|
|
6880
7688
|
? html`
|
|
6881
7689
|
<div class="w-full text-xs">
|
|
6882
7690
|
<div class="flex bg-gray-50">
|
|
6883
|
-
<div
|
|
6884
|
-
|
|
7691
|
+
<div
|
|
7692
|
+
class="w-40 shrink-0 px-4 py-2 font-medium text-gray-700"
|
|
7693
|
+
>
|
|
7694
|
+
Role
|
|
7695
|
+
</div>
|
|
7696
|
+
<div class="flex-1 px-4 py-2 font-medium text-gray-700">
|
|
7697
|
+
Content
|
|
7698
|
+
</div>
|
|
6885
7699
|
</div>
|
|
6886
7700
|
<div class="divide-y divide-gray-200">
|
|
6887
7701
|
${messages.map((msg) => {
|
|
@@ -6904,7 +7718,9 @@ ${prettyEvent}</pre
|
|
|
6904
7718
|
<div class="flex items-start">
|
|
6905
7719
|
<div class="w-40 shrink-0 px-4 py-2">
|
|
6906
7720
|
<span
|
|
6907
|
-
class="inline-flex rounded px-2 py-0.5 text-[10px] font-medium ${
|
|
7721
|
+
class="inline-flex rounded px-2 py-0.5 text-[10px] font-medium ${
|
|
7722
|
+
roleColors[role] || roleColors.unknown
|
|
7723
|
+
}"
|
|
6908
7724
|
>
|
|
6909
7725
|
${role}
|
|
6910
7726
|
</span>
|
|
@@ -7910,7 +8726,9 @@ ${prettyEvent}</pre
|
|
|
7910
8726
|
return nothing;
|
|
7911
8727
|
}
|
|
7912
8728
|
|
|
7913
|
-
return html`<div
|
|
8729
|
+
return html`<div
|
|
8730
|
+
class="mx-4 mt-3 mb-3 rounded-xl border border-slate-200 bg-white px-4 py-3"
|
|
8731
|
+
>
|
|
7914
8732
|
<div
|
|
7915
8733
|
class="mb-2 flex items-center gap-2 text-xs font-semibold text-slate-900"
|
|
7916
8734
|
>
|
|
@@ -7929,7 +8747,13 @@ ${prettyEvent}</pre
|
|
|
7929
8747
|
${this.renderIcon("X")}
|
|
7930
8748
|
</button>
|
|
7931
8749
|
</div>
|
|
7932
|
-
<div
|
|
8750
|
+
<div
|
|
8751
|
+
class="announcement-body ${
|
|
8752
|
+
this.announcementExpanded
|
|
8753
|
+
? "announcement-body--expanded"
|
|
8754
|
+
: "announcement-body--collapsed"
|
|
8755
|
+
}"
|
|
8756
|
+
>
|
|
7933
8757
|
<div
|
|
7934
8758
|
class="announcement-content"
|
|
7935
8759
|
@click=${this.handleAnnouncementContentClick}
|