@copilotkit/web-inspector 1.61.1 → 1.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +1241 -209
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +107 -7
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +107 -7
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1241 -211
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1397 -267
- package/dist/index.umd.js.map +1 -1
- package/dist/lib/context-helpers.cjs.map +1 -1
- package/dist/lib/context-helpers.mjs.map +1 -1
- package/dist/lib/persistence.cjs.map +1 -1
- package/dist/lib/persistence.mjs.map +1 -1
- package/dist/lib/telemetry.cjs +78 -12
- package/dist/lib/telemetry.cjs.map +1 -1
- package/dist/lib/telemetry.mjs +72 -13
- package/dist/lib/telemetry.mjs.map +1 -1
- package/dist/lib/types.d.cts +8 -0
- package/dist/lib/types.d.cts.map +1 -0
- package/dist/lib/types.d.mts +8 -0
- package/dist/lib/types.d.mts.map +1 -0
- package/dist/package.cjs +12 -0
- package/dist/package.cjs.map +1 -0
- package/dist/package.mjs +6 -0
- package/dist/package.mjs.map +1 -0
- package/package.json +4 -2
- package/src/__tests__/web-inspector.spec.ts +929 -11
- package/src/index.ts +1650 -318
- package/src/lib/__tests__/telemetry.test.ts +150 -10
- package/src/lib/context-helpers.ts +1 -1
- package/src/lib/persistence.ts +1 -1
- package/src/lib/telemetry.ts +162 -18
package/src/index.ts
CHANGED
|
@@ -28,7 +28,7 @@ import type {
|
|
|
28
28
|
DockMode,
|
|
29
29
|
Position,
|
|
30
30
|
Size,
|
|
31
|
-
} from "./lib/types";
|
|
31
|
+
} from "./lib/types.js";
|
|
32
32
|
import {
|
|
33
33
|
applyAnchorPosition as applyAnchorPositionHelper,
|
|
34
34
|
centerContext as centerContextHelper,
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
updateAnchorFromPosition as updateAnchorFromPositionHelper,
|
|
38
38
|
updateSizeFromElement,
|
|
39
39
|
clampSize as clampSizeToViewport,
|
|
40
|
-
} from "./lib/context-helpers";
|
|
40
|
+
} from "./lib/context-helpers.js";
|
|
41
41
|
import {
|
|
42
42
|
loadInspectorState,
|
|
43
43
|
saveInspectorState,
|
|
@@ -45,19 +45,30 @@ import {
|
|
|
45
45
|
isValidPosition,
|
|
46
46
|
isValidSize,
|
|
47
47
|
isValidDockMode,
|
|
48
|
-
} from "./lib/persistence";
|
|
49
|
-
import type { PersistedState } from "./lib/persistence";
|
|
48
|
+
} from "./lib/persistence.js";
|
|
49
|
+
import type { PersistedState } from "./lib/persistence.js";
|
|
50
50
|
import {
|
|
51
51
|
TELEMETRY_DOCS_URL,
|
|
52
52
|
ensureTelemetryDistinctId,
|
|
53
|
+
getRuntimeUrlType,
|
|
53
54
|
getTelemetryDistinctIdForUrl,
|
|
54
55
|
maybeShowDisclosure,
|
|
55
56
|
trackBannerClicked,
|
|
56
57
|
trackBannerViewed,
|
|
58
|
+
trackTalkToEngineerClicked,
|
|
59
|
+
trackThreadsEmptyEnabledViewed,
|
|
60
|
+
trackThreadsEnabledViewed,
|
|
61
|
+
trackThreadsIntelligenceSignupClicked,
|
|
62
|
+
trackThreadsLockedViewed,
|
|
57
63
|
trackThreadsTabClicked,
|
|
58
|
-
|
|
64
|
+
trackThreadsTalkToEngineerClicked,
|
|
65
|
+
} from "./lib/telemetry.js";
|
|
66
|
+
import type { InspectorThreadTelemetryProps } from "./lib/telemetry.js";
|
|
67
|
+
|
|
68
|
+
export type { Anchor } from "./lib/types.js";
|
|
59
69
|
|
|
60
70
|
export const WEB_INSPECTOR_TAG = "cpk-web-inspector" as const;
|
|
71
|
+
export const THREAD_INSPECTOR_TAG = "cpk-thread-inspector" as const;
|
|
61
72
|
|
|
62
73
|
type LucideIconName = keyof typeof icons;
|
|
63
74
|
|
|
@@ -88,6 +99,10 @@ const DEFAULT_WINDOW_SIZE: Size = { width: 840, height: 700 };
|
|
|
88
99
|
const DOCKED_LEFT_WIDTH = 500; // Sensible width for left dock with collapsed sidebar
|
|
89
100
|
const MAX_AGENT_EVENTS = 200;
|
|
90
101
|
const MAX_TOTAL_EVENTS = 500;
|
|
102
|
+
const INTELLIGENCE_SIGNUP_URL = "https://go.copilotkit.ai/intelligence-signup";
|
|
103
|
+
const TALK_TO_ENGINEER_URL = "https://www.copilotkit.ai/talk-to-an-engineer";
|
|
104
|
+
|
|
105
|
+
type ThreadServiceStatus = "available" | "unavailable" | "unknown" | "error";
|
|
91
106
|
|
|
92
107
|
type InspectorAgentEventType =
|
|
93
108
|
| "RUN_STARTED"
|
|
@@ -186,15 +201,62 @@ type InspectorEvent = {
|
|
|
186
201
|
|
|
187
202
|
// ─── Thread details types ────────────────────────────────────────────────────
|
|
188
203
|
|
|
189
|
-
|
|
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 = {
|
|
190
215
|
id: string;
|
|
191
216
|
role: string;
|
|
192
217
|
content?: string;
|
|
193
|
-
toolCalls?:
|
|
218
|
+
toolCalls?: ThreadDebuggerToolCall[];
|
|
194
219
|
toolCallId?: string;
|
|
195
220
|
/** Present when role === "activity" (Generative UI output). */
|
|
196
221
|
activityType?: string;
|
|
197
|
-
}
|
|
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
|
+
};
|
|
198
260
|
|
|
199
261
|
interface ConversationUser {
|
|
200
262
|
id: string;
|
|
@@ -268,9 +330,39 @@ interface ApiAgentEvent {
|
|
|
268
330
|
type: string;
|
|
269
331
|
timestamp: string | number;
|
|
270
332
|
payload: Record<string, unknown>;
|
|
333
|
+
sourceIndex?: number;
|
|
334
|
+
rawEvent?: ThreadDebuggerEvent;
|
|
271
335
|
}
|
|
272
336
|
|
|
273
|
-
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" };
|
|
274
366
|
|
|
275
367
|
// ─── JSON syntax highlighter ─────────────────────────────────────────────────
|
|
276
368
|
// Inline-styled so shadow DOM encapsulation preserves colors when the output
|
|
@@ -334,9 +426,10 @@ function eventColors(type: string): { bg: string; fg: string } {
|
|
|
334
426
|
return { bg: "rgba(133,236,206,0.15)", fg: "#189370" };
|
|
335
427
|
if (type.startsWith("STATE"))
|
|
336
428
|
return { bg: "rgba(190,194,255,0.102)", fg: "#5558B2" };
|
|
429
|
+
if (type === "RUN_ERROR" || type === "ERROR")
|
|
430
|
+
return { bg: "rgba(250,95,103,0.13)", fg: "#c0333a" };
|
|
337
431
|
if (type.startsWith("RUN_") || type.startsWith("STEP_"))
|
|
338
432
|
return { bg: "rgba(255,172,77,0.2)", fg: "#996300" };
|
|
339
|
-
if (type === "ERROR") return { bg: "rgba(250,95,103,0.13)", fg: "#c0333a" };
|
|
340
433
|
return { bg: "#F7F7F9", fg: "#838389" };
|
|
341
434
|
}
|
|
342
435
|
|
|
@@ -603,28 +696,30 @@ class CpkThreadList extends LitElement {
|
|
|
603
696
|
${
|
|
604
697
|
this.errorMessage
|
|
605
698
|
? html`
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
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;"
|
|
616
718
|
>
|
|
617
|
-
|
|
618
|
-
<line x1="12" y1="8" x2="12" y2="12" />
|
|
619
|
-
<line x1="12" y1="16" x2="12.01" y2="16" />
|
|
620
|
-
</svg>
|
|
621
|
-
<div>
|
|
622
|
-
Failed to load threads
|
|
623
|
-
<div style="font-size:11px;margin-top:4px;color:#c0333a;">
|
|
624
|
-
${this.errorMessage}
|
|
625
|
-
</div>
|
|
719
|
+
${this.errorMessage}
|
|
626
720
|
</div>
|
|
627
|
-
|
|
721
|
+
</div>
|
|
722
|
+
`
|
|
628
723
|
: this.threads.length === 0
|
|
629
724
|
? html`
|
|
630
725
|
<svg
|
|
@@ -656,21 +751,14 @@ class CpkThreadList extends LitElement {
|
|
|
656
751
|
}
|
|
657
752
|
}
|
|
658
753
|
|
|
659
|
-
// ─── cpk-thread-
|
|
660
|
-
// Renders the selected thread's
|
|
661
|
-
//
|
|
662
|
-
//
|
|
663
|
-
|
|
664
|
-
// agent subscriptions; when those are absent we fall back to the per-thread
|
|
665
|
-
// fetched data via `/threads/:id/{events,state}`.
|
|
666
|
-
|
|
667
|
-
// Exported (with the underscore-prefixed name signalling internal/test-only)
|
|
668
|
-
// so unit tests can pin down the per-panel template-cache invariants without
|
|
669
|
-
// reaching through `customElements`. Production consumers continue to use the
|
|
670
|
-
// `cpk-thread-details` custom element registered below.
|
|
671
|
-
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 {
|
|
672
759
|
static properties = {
|
|
673
760
|
threadId: { attribute: false },
|
|
761
|
+
provider: { attribute: false },
|
|
674
762
|
thread: { attribute: false },
|
|
675
763
|
runtimeUrl: { attribute: false },
|
|
676
764
|
headers: { attribute: false },
|
|
@@ -679,6 +767,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
679
767
|
agentEventsInput: { attribute: false },
|
|
680
768
|
liveMessageVersion: { attribute: false },
|
|
681
769
|
_tab: { state: true },
|
|
770
|
+
_fetchedMetadata: { state: true },
|
|
682
771
|
_conversation: { state: true },
|
|
683
772
|
_fetchedEvents: { state: true },
|
|
684
773
|
_fetchedState: { state: true },
|
|
@@ -699,7 +788,8 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
699
788
|
};
|
|
700
789
|
|
|
701
790
|
threadId: string | null = null;
|
|
702
|
-
|
|
791
|
+
provider: ThreadDebuggerProvider | null = null;
|
|
792
|
+
thread: ThreadDebuggerMetadata | ɵThread | null = null;
|
|
703
793
|
runtimeUrl = "";
|
|
704
794
|
headers: Record<string, string> = {};
|
|
705
795
|
threadInspectionAvailable = false;
|
|
@@ -713,7 +803,8 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
713
803
|
*/
|
|
714
804
|
liveMessageVersion = 0;
|
|
715
805
|
|
|
716
|
-
private _tab: ThreadDetailsTab = "
|
|
806
|
+
private _tab: ThreadDetailsTab = "timeline";
|
|
807
|
+
private _fetchedMetadata: ThreadDebuggerMetadata | null = null;
|
|
717
808
|
private _conversation: ConversationItem[] = [];
|
|
718
809
|
private _fetchedEvents: ApiAgentEvent[] | null = null;
|
|
719
810
|
private _fetchedState: Record<string, unknown> | null = null;
|
|
@@ -746,9 +837,9 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
746
837
|
* switching back to AG-UI Events on a thread with hundreds of events
|
|
747
838
|
* triggers a multi-second DOM-creation pass each time.
|
|
748
839
|
*
|
|
749
|
-
* Reset to {"
|
|
840
|
+
* Reset to {"timeline"} when the selected thread changes.
|
|
750
841
|
*/
|
|
751
|
-
private _activatedTabs: Set<ThreadDetailsTab> = new Set(["
|
|
842
|
+
private _activatedTabs: Set<ThreadDetailsTab> = new Set(["timeline"]);
|
|
752
843
|
/**
|
|
753
844
|
* Memoized per-panel templates keyed by the inputs they render from.
|
|
754
845
|
* When the underlying data hasn't changed (same `_conversation` /
|
|
@@ -760,9 +851,17 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
760
851
|
* reference; if any element flips, the cache misses and rebuilds.
|
|
761
852
|
*/
|
|
762
853
|
private _panelTplCache: Map<
|
|
763
|
-
|
|
854
|
+
ThreadDetailsPanelCacheSlot,
|
|
764
855
|
{ key: readonly unknown[]; tpl: TemplateResult }
|
|
765
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;
|
|
766
865
|
/**
|
|
767
866
|
* Tracks whether we've fetched events for the current thread yet. Events
|
|
768
867
|
* fetch lazily on first sub-tab click so a large response's JSON.parse
|
|
@@ -775,8 +874,9 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
775
874
|
* lazy-load reasoning as `_eventsFetched`.
|
|
776
875
|
*/
|
|
777
876
|
private _stateFetched = false;
|
|
778
|
-
private
|
|
877
|
+
private _lastLoadKey: string | null = null;
|
|
779
878
|
private _lastSeenLiveMessageVersion = 0;
|
|
879
|
+
private _metadataAbort: AbortController | null = null;
|
|
780
880
|
private _messagesAbort: AbortController | null = null;
|
|
781
881
|
private _eventsAbort: AbortController | null = null;
|
|
782
882
|
private _stateAbort: AbortController | null = null;
|
|
@@ -786,18 +886,53 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
786
886
|
private _dividerStartWidth = 0;
|
|
787
887
|
|
|
788
888
|
static readonly COLLAPSE_THRESHOLD = 800;
|
|
789
|
-
|
|
889
|
+
static readonly TAB_LIST: ReadonlyArray<{
|
|
790
890
|
id: ThreadDetailsTab;
|
|
791
891
|
label: string;
|
|
792
892
|
}> = [
|
|
793
|
-
{ id: "
|
|
794
|
-
{ id: "
|
|
795
|
-
{ id: "
|
|
893
|
+
{ id: "timeline", label: "Timeline" },
|
|
894
|
+
{ id: "raw-events", label: "Raw AG-UI Events" },
|
|
895
|
+
{ id: "state", label: "State" },
|
|
796
896
|
];
|
|
797
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
|
+
|
|
798
933
|
private renderTabContent(id: ThreadDetailsTab): TemplateResult {
|
|
799
|
-
if (id === "
|
|
800
|
-
if (id === "
|
|
934
|
+
if (id === "timeline") return this.renderTimeline();
|
|
935
|
+
if (id === "state") return this.renderState();
|
|
801
936
|
return this.renderEvents();
|
|
802
937
|
}
|
|
803
938
|
|
|
@@ -830,10 +965,10 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
830
965
|
// the entire panel for seconds — including making the tab buttons
|
|
831
966
|
// themselves feel unresponsive.
|
|
832
967
|
if (!this.threadId) return;
|
|
833
|
-
if (id === "
|
|
968
|
+
if ((id === "timeline" || id === "raw-events") && !this._eventsFetched) {
|
|
834
969
|
this._eventsFetched = true;
|
|
835
970
|
void this.fetchEvents(this.threadId);
|
|
836
|
-
} else if (id === "
|
|
971
|
+
} else if (id === "state" && !this._stateFetched) {
|
|
837
972
|
this._stateFetched = true;
|
|
838
973
|
void this.fetchState(this.threadId);
|
|
839
974
|
}
|
|
@@ -965,6 +1100,42 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
965
1100
|
flex-shrink: 0;
|
|
966
1101
|
}
|
|
967
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
|
+
|
|
968
1139
|
/*
|
|
969
1140
|
* Each tab's content is wrapped in this panel so the keep-mounted
|
|
970
1141
|
* inactive panels can be hidden via display:none without disturbing
|
|
@@ -1185,6 +1356,81 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1185
1356
|
background: #e9e9ef;
|
|
1186
1357
|
}
|
|
1187
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
|
+
|
|
1188
1434
|
/* ── Generative UI ──────────────────────────────────────────────── */
|
|
1189
1435
|
@keyframes cpk-genui-enter {
|
|
1190
1436
|
from {
|
|
@@ -1394,36 +1640,27 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1394
1640
|
`;
|
|
1395
1641
|
|
|
1396
1642
|
updated(_changed: Map<string, unknown>): void {
|
|
1397
|
-
|
|
1398
|
-
|
|
1643
|
+
const loadKey = this.currentLoadKey();
|
|
1644
|
+
if (loadKey !== this._lastLoadKey) {
|
|
1645
|
+
this._lastLoadKey = loadKey;
|
|
1399
1646
|
this._lastSeenLiveMessageVersion = this.liveMessageVersion;
|
|
1400
|
-
this.
|
|
1401
|
-
this._activatedTabs = new Set(["conversation"]);
|
|
1402
|
-
this._panelTplCache = new Map();
|
|
1403
|
-
this._expandedTools = new Set();
|
|
1404
|
-
this._expandedMessages = new Set();
|
|
1405
|
-
this._messagesAbort?.abort();
|
|
1406
|
-
this._messagesAbort = null;
|
|
1407
|
-
this._eventsAbort?.abort();
|
|
1408
|
-
this._eventsAbort = null;
|
|
1409
|
-
this._stateAbort?.abort();
|
|
1410
|
-
this._stateAbort = null;
|
|
1411
|
-
// Reset cleared so the next click into events/state triggers a fresh
|
|
1412
|
-
// fetch. Eagerly clear `_fetchedEvents` / `_fetchedState` so the empty
|
|
1413
|
-
// state doesn't briefly show last thread's data.
|
|
1414
|
-
this._eventsFetched = false;
|
|
1415
|
-
this._stateFetched = false;
|
|
1416
|
-
this._fetchedEvents = null;
|
|
1417
|
-
this._fetchedState = null;
|
|
1647
|
+
this.resetLoadedThreadData();
|
|
1418
1648
|
|
|
1419
1649
|
if (this.threadId) {
|
|
1420
|
-
//
|
|
1421
|
-
//
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
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
|
+
}
|
|
1426
1662
|
} else {
|
|
1663
|
+
this._fetchedMetadata = null;
|
|
1427
1664
|
this._conversation = [];
|
|
1428
1665
|
}
|
|
1429
1666
|
} else if (
|
|
@@ -1444,6 +1681,90 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1444
1681
|
}
|
|
1445
1682
|
}
|
|
1446
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
|
+
|
|
1447
1768
|
/**
|
|
1448
1769
|
* Fetch the canonical conversation for `threadId` from the runtime.
|
|
1449
1770
|
*
|
|
@@ -1458,10 +1779,11 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1458
1779
|
threadId: string,
|
|
1459
1780
|
silent: boolean = false,
|
|
1460
1781
|
): Promise<void> {
|
|
1461
|
-
if (!this.
|
|
1782
|
+
if (!this.canFetchMessages()) {
|
|
1462
1783
|
if (!silent) this._conversation = [];
|
|
1463
1784
|
return;
|
|
1464
1785
|
}
|
|
1786
|
+
this._messagesAbort?.abort();
|
|
1465
1787
|
const controller = new AbortController();
|
|
1466
1788
|
this._messagesAbort = controller;
|
|
1467
1789
|
if (!silent) {
|
|
@@ -1469,15 +1791,13 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1469
1791
|
this._messagesError = null;
|
|
1470
1792
|
}
|
|
1471
1793
|
try {
|
|
1472
|
-
const
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1478
|
-
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);
|
|
1479
1799
|
if (controller.signal.aborted || this.threadId !== threadId) return;
|
|
1480
|
-
this._conversation = this.mapMessages(
|
|
1800
|
+
this._conversation = this.mapMessages(messages);
|
|
1481
1801
|
} catch (err) {
|
|
1482
1802
|
if (err instanceof Error && err.name === "AbortError") return;
|
|
1483
1803
|
if (!silent) {
|
|
@@ -1495,45 +1815,51 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1495
1815
|
}
|
|
1496
1816
|
|
|
1497
1817
|
private async fetchEvents(threadId: string): Promise<void> {
|
|
1498
|
-
this.
|
|
1499
|
-
if (!this.runtimeUrl || !this.threadInspectionAvailable) {
|
|
1818
|
+
if (!this.canFetchEvents()) {
|
|
1500
1819
|
this._fetchedEvents = null;
|
|
1501
1820
|
return;
|
|
1502
1821
|
}
|
|
1822
|
+
this._eventsAbort?.abort();
|
|
1503
1823
|
const controller = new AbortController();
|
|
1504
1824
|
this._eventsAbort = controller;
|
|
1505
1825
|
this._loadingEvents = true;
|
|
1506
1826
|
this._eventsError = null;
|
|
1507
1827
|
try {
|
|
1508
|
-
const
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
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);
|
|
1512
1836
|
// Drop results if a newer fetch superseded this one (thread switched
|
|
1513
|
-
// mid-flight). Without this, switching A→B
|
|
1514
|
-
// 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.
|
|
1515
1840
|
if (controller.signal.aborted || this.threadId !== threadId) return;
|
|
1516
|
-
if (
|
|
1517
|
-
// Endpoint not supported on this runtime (e.g. Intelligence platform).
|
|
1518
|
-
// Mark unavailable so we don't misleadingly fall back to the parent's
|
|
1519
|
-
// live agent events — those are agent-keyed, not thread-keyed, and
|
|
1520
|
-
// would render identical across every thread on the same agent.
|
|
1841
|
+
if (result.status === "not-available") {
|
|
1521
1842
|
this._eventsNotAvailable = true;
|
|
1522
|
-
this._fetchedEvents =
|
|
1843
|
+
this._fetchedEvents = [];
|
|
1844
|
+
if (this.canFetchMessages()) {
|
|
1845
|
+
void this.fetchMessages(threadId);
|
|
1846
|
+
}
|
|
1523
1847
|
return;
|
|
1524
1848
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
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
|
+
}
|
|
1531
1854
|
} catch (err) {
|
|
1532
1855
|
if (err instanceof Error && err.name === "AbortError") return;
|
|
1533
1856
|
if (this.threadId !== threadId) return;
|
|
1534
1857
|
this._eventsError =
|
|
1535
1858
|
err instanceof Error ? err.message : "Failed to load events";
|
|
1536
1859
|
this._fetchedEvents = [];
|
|
1860
|
+
if (this.canFetchMessages()) {
|
|
1861
|
+
void this.fetchMessages(threadId);
|
|
1862
|
+
}
|
|
1537
1863
|
} finally {
|
|
1538
1864
|
if (!controller.signal.aborted && this.threadId === threadId) {
|
|
1539
1865
|
this._loadingEvents = false;
|
|
@@ -1542,32 +1868,31 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1542
1868
|
}
|
|
1543
1869
|
|
|
1544
1870
|
private async fetchState(threadId: string): Promise<void> {
|
|
1545
|
-
this.
|
|
1546
|
-
if (!this.runtimeUrl || !this.threadInspectionAvailable) {
|
|
1871
|
+
if (!this.canFetchState()) {
|
|
1547
1872
|
this._fetchedState = null;
|
|
1548
1873
|
return;
|
|
1549
1874
|
}
|
|
1875
|
+
this._stateAbort?.abort();
|
|
1550
1876
|
const controller = new AbortController();
|
|
1551
1877
|
this._stateAbort = controller;
|
|
1552
1878
|
this._loadingState = true;
|
|
1553
1879
|
this._stateError = null;
|
|
1554
1880
|
try {
|
|
1555
|
-
const
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
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);
|
|
1559
1889
|
if (controller.signal.aborted || this.threadId !== threadId) return;
|
|
1560
|
-
if (
|
|
1890
|
+
if (result.status === "not-available") {
|
|
1561
1891
|
this._stateNotAvailable = true;
|
|
1562
1892
|
this._fetchedState = null;
|
|
1563
1893
|
return;
|
|
1564
1894
|
}
|
|
1565
|
-
|
|
1566
|
-
const data = (await res.json()) as {
|
|
1567
|
-
state: Record<string, unknown> | null;
|
|
1568
|
-
};
|
|
1569
|
-
if (controller.signal.aborted || this.threadId !== threadId) return;
|
|
1570
|
-
this._fetchedState = data.state ?? null;
|
|
1895
|
+
this._fetchedState = result.state ?? null;
|
|
1571
1896
|
} catch (err) {
|
|
1572
1897
|
if (err instanceof Error && err.name === "AbortError") return;
|
|
1573
1898
|
if (this.threadId !== threadId) return;
|
|
@@ -1581,7 +1906,63 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1581
1906
|
}
|
|
1582
1907
|
}
|
|
1583
1908
|
|
|
1584
|
-
private
|
|
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
|
+
|
|
1958
|
+
private getThreadInspectionUrl(
|
|
1959
|
+
threadId: string,
|
|
1960
|
+
resource: "messages" | "events" | "state",
|
|
1961
|
+
): string {
|
|
1962
|
+
return `${this.runtimeUrl.replace(/\/+$/, "")}/threads/${encodeURIComponent(threadId)}/${resource}`;
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
private mapMessages(messages: ThreadDebuggerMessage[]): ConversationItem[] {
|
|
1585
1966
|
const items: ConversationItem[] = [];
|
|
1586
1967
|
const toolCallMap = new Map<string, ConversationToolCall>();
|
|
1587
1968
|
for (const msg of messages) {
|
|
@@ -1596,19 +1977,20 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1596
1977
|
if (msg.toolCalls?.length) {
|
|
1597
1978
|
for (const tc of msg.toolCalls) {
|
|
1598
1979
|
let args: Record<string, unknown> = {};
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
{
|
|
1610
|
-
|
|
1611
|
-
|
|
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;
|
|
1612
1994
|
}
|
|
1613
1995
|
const item: ConversationToolCall = {
|
|
1614
1996
|
id: tc.id,
|
|
@@ -1662,22 +2044,284 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1662
2044
|
return items;
|
|
1663
2045
|
}
|
|
1664
2046
|
|
|
1665
|
-
private mapApiEvents(
|
|
1666
|
-
events
|
|
1667
|
-
|
|
1668
|
-
return events.map((event) => {
|
|
1669
|
-
const { type, timestamp, ...rest } = event;
|
|
2047
|
+
private mapApiEvents(events: ThreadDebuggerEvent[]): ApiAgentEvent[] {
|
|
2048
|
+
return events.map((event, index) => {
|
|
2049
|
+
const { type, timestamp, payload, ...rest } = event;
|
|
1670
2050
|
return {
|
|
1671
2051
|
type: typeof type === "string" ? type : "UNKNOWN",
|
|
1672
2052
|
timestamp:
|
|
1673
2053
|
typeof timestamp === "string" || typeof timestamp === "number"
|
|
1674
2054
|
? timestamp
|
|
1675
2055
|
: Date.now(),
|
|
1676
|
-
payload: rest,
|
|
2056
|
+
payload: payload ?? rest,
|
|
2057
|
+
sourceIndex: index + 1,
|
|
2058
|
+
rawEvent: event,
|
|
1677
2059
|
};
|
|
1678
2060
|
});
|
|
1679
2061
|
}
|
|
1680
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
|
+
|
|
1681
2325
|
private get renderItems(): RenderItem[] {
|
|
1682
2326
|
const items = this._conversation;
|
|
1683
2327
|
const result: RenderItem[] = [];
|
|
@@ -1720,7 +2364,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1720
2364
|
}
|
|
1721
2365
|
|
|
1722
2366
|
private get duration(): string {
|
|
1723
|
-
const t = this.
|
|
2367
|
+
const t = this.metadata;
|
|
1724
2368
|
if (!t?.createdAt || !t?.updatedAt) return "—";
|
|
1725
2369
|
const ms =
|
|
1726
2370
|
new Date(t.updatedAt).getTime() - new Date(t.createdAt).getTime();
|
|
@@ -1753,7 +2397,16 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1753
2397
|
// threads (those would render identically for every thread on the same
|
|
1754
2398
|
// agent and mislead the reader).
|
|
1755
2399
|
if (this._eventsNotAvailable) return [];
|
|
1756
|
-
|
|
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;
|
|
1757
2410
|
}
|
|
1758
2411
|
|
|
1759
2412
|
private get activeState(): Record<string, unknown> | null {
|
|
@@ -1771,6 +2424,10 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1771
2424
|
return id.length > 20 ? id.slice(0, 8) + "…" : id;
|
|
1772
2425
|
}
|
|
1773
2426
|
|
|
2427
|
+
private get metadata(): ThreadDebuggerMetadata | null {
|
|
2428
|
+
return this._fetchedMetadata ?? this.thread ?? null;
|
|
2429
|
+
}
|
|
2430
|
+
|
|
1774
2431
|
private fmtTime(dateStr: string | null | undefined): string {
|
|
1775
2432
|
if (!dateStr) return "—";
|
|
1776
2433
|
const d = new Date(dateStr);
|
|
@@ -1819,7 +2476,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1819
2476
|
<!-- Tab bar -->
|
|
1820
2477
|
<div class="cpk-td__tabs-header">
|
|
1821
2478
|
<div class="cpk-td__tab-group" role="tablist">
|
|
1822
|
-
${
|
|
2479
|
+
${CpkThreadInspector.TAB_LIST.map(
|
|
1823
2480
|
(tab) => html`
|
|
1824
2481
|
<button
|
|
1825
2482
|
role="tab"
|
|
@@ -1835,6 +2492,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1835
2492
|
</div>
|
|
1836
2493
|
${this.renderPanelToggle()}
|
|
1837
2494
|
</div>
|
|
2495
|
+
${this.renderMetadataStrip()}
|
|
1838
2496
|
|
|
1839
2497
|
<!-- Scrollable content -->
|
|
1840
2498
|
<div class="cpk-td__content">
|
|
@@ -1845,50 +2503,178 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1845
2503
|
`
|
|
1846
2504
|
: nothing
|
|
1847
2505
|
}
|
|
1848
|
-
${
|
|
2506
|
+
${CpkThreadInspector.TAB_LIST.map((tab) =>
|
|
1849
2507
|
this._activatedTabs.has(tab.id)
|
|
1850
2508
|
? html`<div
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
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>`
|
|
1860
2518
|
: nothing,
|
|
1861
2519
|
)}
|
|
1862
2520
|
</div>
|
|
1863
2521
|
</div>
|
|
1864
2522
|
|
|
1865
|
-
<!--
|
|
1866
|
-
Drawer always rendered so width animates between 0 and its
|
|
1867
|
-
target. Divider lives INSIDE the drawer and is absolutely
|
|
1868
|
-
positioned over its left edge so the toggle (rightmost of the
|
|
1869
|
-
tab row) and the drawer touch with no flex-gap between them.
|
|
1870
|
-
-->
|
|
1871
|
-
<div
|
|
1872
|
-
class="cpk-td__detail"
|
|
1873
|
-
data-open=${this._showDetailPanel ? "true" : "false"}
|
|
1874
|
-
style="width:${this._showDetailPanel ? this._detailPanelWidth : 0}px"
|
|
1875
|
-
aria-hidden=${this._showDetailPanel ? "false" : "true"}
|
|
1876
|
-
>
|
|
1877
|
-
${
|
|
1878
|
-
this._showDetailPanel
|
|
1879
|
-
? html`
|
|
1880
|
-
<div
|
|
1881
|
-
class="cpk-td__detail-divider"
|
|
1882
|
-
@pointerdown=${this.onDetailDividerDown}
|
|
1883
|
-
@pointermove=${this.onDetailDividerMove}
|
|
1884
|
-
@pointerup=${this.onDetailDividerUp}
|
|
1885
|
-
@pointercancel=${this.onDetailDividerUp}
|
|
1886
|
-
></div>
|
|
1887
|
-
`
|
|
1888
|
-
: nothing
|
|
1889
|
-
}
|
|
1890
|
-
${this.renderDetailPanel()}
|
|
2523
|
+
<!--
|
|
2524
|
+
Drawer always rendered so width animates between 0 and its
|
|
2525
|
+
target. Divider lives INSIDE the drawer and is absolutely
|
|
2526
|
+
positioned over its left edge so the toggle (rightmost of the
|
|
2527
|
+
tab row) and the drawer touch with no flex-gap between them.
|
|
2528
|
+
-->
|
|
2529
|
+
<div
|
|
2530
|
+
class="cpk-td__detail"
|
|
2531
|
+
data-open=${this._showDetailPanel ? "true" : "false"}
|
|
2532
|
+
style="width:${this._showDetailPanel ? this._detailPanelWidth : 0}px"
|
|
2533
|
+
aria-hidden=${this._showDetailPanel ? "false" : "true"}
|
|
2534
|
+
>
|
|
2535
|
+
${
|
|
2536
|
+
this._showDetailPanel
|
|
2537
|
+
? html`
|
|
2538
|
+
<div
|
|
2539
|
+
class="cpk-td__detail-divider"
|
|
2540
|
+
@pointerdown=${this.onDetailDividerDown}
|
|
2541
|
+
@pointermove=${this.onDetailDividerMove}
|
|
2542
|
+
@pointerup=${this.onDetailDividerUp}
|
|
2543
|
+
@pointercancel=${this.onDetailDividerUp}
|
|
2544
|
+
></div>
|
|
2545
|
+
`
|
|
2546
|
+
: nothing
|
|
2547
|
+
}
|
|
2548
|
+
${this.renderDetailPanel()}
|
|
2549
|
+
</div>
|
|
2550
|
+
</div>
|
|
2551
|
+
`;
|
|
2552
|
+
}
|
|
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
|
+
>
|
|
1891
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
|
+
}
|
|
1892
2678
|
</div>
|
|
1893
2679
|
`;
|
|
1894
2680
|
}
|
|
@@ -1929,7 +2715,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1929
2715
|
// `_conversation` — without those keys the cache returns the
|
|
1930
2716
|
// pre-toggle template and the disclosure appears broken.
|
|
1931
2717
|
return this.cachedPanelTpl(
|
|
1932
|
-
"
|
|
2718
|
+
"timeline-fallback",
|
|
1933
2719
|
[this._conversation, this._expandedTools, this._expandedMessages],
|
|
1934
2720
|
() => {
|
|
1935
2721
|
const items = this.renderItems;
|
|
@@ -1948,10 +2734,21 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1948
2734
|
* change without the listed key flipping.
|
|
1949
2735
|
*/
|
|
1950
2736
|
private cachedPanelTpl(
|
|
1951
|
-
slot:
|
|
2737
|
+
slot: ThreadDetailsPanelCacheSlot,
|
|
1952
2738
|
key: readonly unknown[],
|
|
1953
2739
|
build: () => TemplateResult,
|
|
1954
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 {
|
|
1955
2752
|
const cached = this._panelTplCache.get(slot);
|
|
1956
2753
|
if (
|
|
1957
2754
|
cached &&
|
|
@@ -1960,9 +2757,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1960
2757
|
) {
|
|
1961
2758
|
return cached.tpl;
|
|
1962
2759
|
}
|
|
1963
|
-
|
|
1964
|
-
this._panelTplCache.set(slot, { key, tpl });
|
|
1965
|
-
return tpl;
|
|
2760
|
+
return null;
|
|
1966
2761
|
}
|
|
1967
2762
|
|
|
1968
2763
|
private renderRenderItem(item: RenderItem) {
|
|
@@ -1993,7 +2788,7 @@ export class ɵCpkThreadDetails extends LitElement {
|
|
|
1993
2788
|
|
|
1994
2789
|
private renderBubble(item: ConversationUser | ConversationAssistant) {
|
|
1995
2790
|
const isUser = item.type === "user";
|
|
1996
|
-
const threshold =
|
|
2791
|
+
const threshold = CpkThreadInspector.COLLAPSE_THRESHOLD;
|
|
1997
2792
|
const expanded = this._expandedMessages.has(item.id);
|
|
1998
2793
|
const tooLong = item.content.length > threshold;
|
|
1999
2794
|
const shown =
|
|
@@ -2176,7 +2971,7 @@ ${unsafeHTML(highlightedJson(item.result))}</pre
|
|
|
2176
2971
|
`;
|
|
2177
2972
|
}
|
|
2178
2973
|
const stateValue = this.activeState;
|
|
2179
|
-
return this.cachedPanelTpl("
|
|
2974
|
+
return this.cachedPanelTpl("state", [stateValue], () => {
|
|
2180
2975
|
return html`<pre class="cpk-td__json-block">
|
|
2181
2976
|
${unsafeHTML(highlightedJson(stateValue))}</pre
|
|
2182
2977
|
>`;
|
|
@@ -2216,11 +3011,11 @@ ${unsafeHTML(highlightedJson(stateValue))}</pre
|
|
|
2216
3011
|
</div>
|
|
2217
3012
|
`;
|
|
2218
3013
|
}
|
|
2219
|
-
return this.cachedPanelTpl("
|
|
3014
|
+
return this.cachedPanelTpl("raw-events", [events], () => {
|
|
2220
3015
|
return html`${events.map((event) => {
|
|
2221
3016
|
const { bg, fg } = eventColors(event.type);
|
|
2222
3017
|
return html`
|
|
2223
|
-
<div class="cpk-td__event">
|
|
3018
|
+
<div class="cpk-td__event" data-source-index=${event.sourceIndex}>
|
|
2224
3019
|
<div class="cpk-td__event-header" style="background:${bg}">
|
|
2225
3020
|
<span class="cpk-td__event-type" style="color:${fg}"
|
|
2226
3021
|
>${event.type}</span
|
|
@@ -2230,7 +3025,7 @@ ${unsafeHTML(highlightedJson(stateValue))}</pre
|
|
|
2230
3025
|
>
|
|
2231
3026
|
</div>
|
|
2232
3027
|
<pre class="cpk-td__event-payload">
|
|
2233
|
-
${unsafeHTML(highlightedJson(event.
|
|
3028
|
+
${unsafeHTML(highlightedJson(event.rawEvent ?? event))}</pre
|
|
2234
3029
|
>
|
|
2235
3030
|
</div>
|
|
2236
3031
|
`;
|
|
@@ -2269,29 +3064,42 @@ ${unsafeHTML(highlightedJson(event.payload))}</pre
|
|
|
2269
3064
|
|
|
2270
3065
|
private renderDetailPanel() {
|
|
2271
3066
|
const counts = this.activityCounts;
|
|
3067
|
+
const metadata = this.metadata;
|
|
2272
3068
|
return html`
|
|
2273
3069
|
<!-- Thread -->
|
|
2274
3070
|
<div class="cpk-tdp__section-title">Thread</div>
|
|
2275
3071
|
<div class="cpk-tdp__row">
|
|
2276
3072
|
<span class="cpk-tdp__label">ID</span>
|
|
2277
3073
|
<span class="cpk-tdp__value cpk-tdp__value--wrap"
|
|
2278
|
-
>${this.shortId(
|
|
3074
|
+
>${this.shortId(metadata?.id)}</span
|
|
2279
3075
|
>
|
|
2280
3076
|
</div>
|
|
2281
3077
|
<div class="cpk-tdp__row">
|
|
2282
3078
|
<span class="cpk-tdp__label">Name</span>
|
|
2283
|
-
<span class="cpk-tdp__value">${
|
|
3079
|
+
<span class="cpk-tdp__value">${metadata?.name ?? "—"}</span>
|
|
2284
3080
|
</div>
|
|
2285
3081
|
<div class="cpk-tdp__row">
|
|
2286
3082
|
<span class="cpk-tdp__label">Agent</span>
|
|
2287
3083
|
<span class="cpk-tdp__value cpk-tdp__value--truncate"
|
|
2288
|
-
>${
|
|
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
|
|
2289
3091
|
>
|
|
2290
3092
|
</div>
|
|
2291
3093
|
<div class="cpk-tdp__row">
|
|
2292
3094
|
<span class="cpk-tdp__label">Created by</span>
|
|
2293
3095
|
<span class="cpk-tdp__value cpk-tdp__value--truncate"
|
|
2294
|
-
>${
|
|
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
|
|
2295
3103
|
>
|
|
2296
3104
|
</div>
|
|
2297
3105
|
|
|
@@ -2301,11 +3109,11 @@ ${unsafeHTML(highlightedJson(event.payload))}</pre
|
|
|
2301
3109
|
<div class="cpk-tdp__section-title">Timestamps</div>
|
|
2302
3110
|
<div class="cpk-tdp__row">
|
|
2303
3111
|
<span class="cpk-tdp__label">Created</span>
|
|
2304
|
-
<span class="cpk-tdp__value">${this.fmtTime(
|
|
3112
|
+
<span class="cpk-tdp__value">${this.fmtTime(metadata?.createdAt)}</span>
|
|
2305
3113
|
</div>
|
|
2306
3114
|
<div class="cpk-tdp__row">
|
|
2307
3115
|
<span class="cpk-tdp__label">Updated</span>
|
|
2308
|
-
<span class="cpk-tdp__value">${this.fmtTime(
|
|
3116
|
+
<span class="cpk-tdp__value">${this.fmtTime(metadata?.updatedAt)}</span>
|
|
2309
3117
|
</div>
|
|
2310
3118
|
<div class="cpk-tdp__row">
|
|
2311
3119
|
<span class="cpk-tdp__label">Duration</span>
|
|
@@ -2332,9 +3140,17 @@ ${unsafeHTML(highlightedJson(event.payload))}</pre
|
|
|
2332
3140
|
}
|
|
2333
3141
|
}
|
|
2334
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
|
+
|
|
2335
3148
|
if (!customElements.get("cpk-thread-list")) {
|
|
2336
3149
|
customElements.define("cpk-thread-list", CpkThreadList);
|
|
2337
3150
|
}
|
|
3151
|
+
if (!customElements.get(THREAD_INSPECTOR_TAG)) {
|
|
3152
|
+
customElements.define(THREAD_INSPECTOR_TAG, CpkThreadInspector);
|
|
3153
|
+
}
|
|
2338
3154
|
if (!customElements.get("cpk-thread-details")) {
|
|
2339
3155
|
customElements.define("cpk-thread-details", ɵCpkThreadDetails);
|
|
2340
3156
|
}
|
|
@@ -2446,6 +3262,7 @@ export class WebInspectorElement extends LitElement {
|
|
|
2446
3262
|
// `${bannerId}:${cta}`) so copy-button retries and accidental multi-clicks
|
|
2447
3263
|
// don't inflate funnel counts beyond one signal per intent type per banner.
|
|
2448
3264
|
private clickedBannerIds: Set<string> = new Set();
|
|
3265
|
+
private viewedThreadsTelemetryStates: Set<string> = new Set();
|
|
2449
3266
|
|
|
2450
3267
|
get core(): CopilotKitCore | null {
|
|
2451
3268
|
return this._core;
|
|
@@ -2527,7 +3344,54 @@ export class WebInspectorElement extends LitElement {
|
|
|
2527
3344
|
];
|
|
2528
3345
|
}
|
|
2529
3346
|
|
|
3347
|
+
private getThreadServiceStatus(): ThreadServiceStatus {
|
|
3348
|
+
if (!this._core) return "unknown";
|
|
3349
|
+
if (!this._core.threadEndpoints) return "unknown";
|
|
3350
|
+
return this._core.threadEndpoints?.list === false
|
|
3351
|
+
? "unavailable"
|
|
3352
|
+
: "available";
|
|
3353
|
+
}
|
|
3354
|
+
|
|
3355
|
+
private areThreadEndpointsAvailable(): boolean {
|
|
3356
|
+
return this.getThreadServiceStatus() !== "unavailable";
|
|
3357
|
+
}
|
|
3358
|
+
|
|
3359
|
+
private getThreadsTelemetryProps(
|
|
3360
|
+
extra: Partial<InspectorThreadTelemetryProps> = {},
|
|
3361
|
+
options: { includeUrlAttribution?: boolean } = {},
|
|
3362
|
+
): InspectorThreadTelemetryProps {
|
|
3363
|
+
const distinctId =
|
|
3364
|
+
options.includeUrlAttribution && !this.core?.telemetryDisabled
|
|
3365
|
+
? getTelemetryDistinctIdForUrl()
|
|
3366
|
+
: null;
|
|
3367
|
+
const threadServiceStatus = this.getThreadServiceStatus();
|
|
3368
|
+
return {
|
|
3369
|
+
posthog_distinct_id: distinctId ?? undefined,
|
|
3370
|
+
intelligence_status:
|
|
3371
|
+
threadServiceStatus === "available"
|
|
3372
|
+
? "intelligence_enabled"
|
|
3373
|
+
: threadServiceStatus === "unavailable"
|
|
3374
|
+
? "intelligence_not_enabled"
|
|
3375
|
+
: "unknown",
|
|
3376
|
+
thread_service_status: threadServiceStatus,
|
|
3377
|
+
license_status: this.core?.licenseStatus ?? undefined,
|
|
3378
|
+
runtime_mode: this.core?.runtimeMode ?? undefined,
|
|
3379
|
+
runtime_url_type: getRuntimeUrlType(this.core?.runtimeUrl),
|
|
3380
|
+
telemetry_disabled: this.core?.telemetryDisabled ?? false,
|
|
3381
|
+
...extra,
|
|
3382
|
+
};
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3385
|
+
private getIntelligenceSignupUrl(): string {
|
|
3386
|
+
return this.appendRefParam(INTELLIGENCE_SIGNUP_URL, "cpk-inspector");
|
|
3387
|
+
}
|
|
3388
|
+
|
|
3389
|
+
private getTalkToEngineerUrl(): string {
|
|
3390
|
+
return this.appendRefParam(TALK_TO_ENGINEER_URL, "cpk-inspector-threads");
|
|
3391
|
+
}
|
|
3392
|
+
|
|
2530
3393
|
private subscribeToThreadStore(agentId: string, store: ɵThreadStore): void {
|
|
3394
|
+
if (!this.areThreadEndpointsAvailable()) return;
|
|
2531
3395
|
if (this._threadStoreSubscriptions.has(agentId)) return;
|
|
2532
3396
|
const threadsSub = store.select(ɵselectThreads).subscribe((threads) => {
|
|
2533
3397
|
this._threadsByAgent.set(agentId, threads as ɵThread[]);
|
|
@@ -2587,7 +3451,7 @@ export class WebInspectorElement extends LitElement {
|
|
|
2587
3451
|
if (this.core?.getThreadStore(agentId)) return;
|
|
2588
3452
|
const core = this.core;
|
|
2589
3453
|
if (!core?.runtimeUrl) return;
|
|
2590
|
-
if (
|
|
3454
|
+
if (!this.areThreadEndpointsAvailable()) return;
|
|
2591
3455
|
|
|
2592
3456
|
const store = ɵcreateThreadStore({ fetch: globalThis.fetch });
|
|
2593
3457
|
store.start();
|
|
@@ -2626,6 +3490,7 @@ export class WebInspectorElement extends LitElement {
|
|
|
2626
3490
|
store.setContext({
|
|
2627
3491
|
runtimeUrl: core.runtimeUrl,
|
|
2628
3492
|
headers: { ...headers },
|
|
3493
|
+
wsUrl: core.intelligence?.wsUrl,
|
|
2629
3494
|
agentId,
|
|
2630
3495
|
});
|
|
2631
3496
|
}
|
|
@@ -2661,7 +3526,7 @@ export class WebInspectorElement extends LitElement {
|
|
|
2661
3526
|
maybeShowDisclosure();
|
|
2662
3527
|
}
|
|
2663
3528
|
this.flushPendingBannerViewed();
|
|
2664
|
-
if (
|
|
3529
|
+
if (this.areThreadEndpointsAvailable()) {
|
|
2665
3530
|
for (const agentId of this._ownedThreadStores.keys()) {
|
|
2666
3531
|
this.refreshOwnedThreadStore(agentId);
|
|
2667
3532
|
}
|
|
@@ -2681,6 +3546,7 @@ export class WebInspectorElement extends LitElement {
|
|
|
2681
3546
|
},
|
|
2682
3547
|
onHeadersChanged: ({ headers }) => {
|
|
2683
3548
|
this.updateOwnedThreadStoreHeaders(headers);
|
|
3549
|
+
this.requestUpdate();
|
|
2684
3550
|
},
|
|
2685
3551
|
onError: ({ code, error }) => {
|
|
2686
3552
|
this.lastCoreError = { code, message: error.message };
|
|
@@ -2689,6 +3555,14 @@ export class WebInspectorElement extends LitElement {
|
|
|
2689
3555
|
onAgentsChanged: ({ agents }) => {
|
|
2690
3556
|
this.processAgentsChanged(agents);
|
|
2691
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
|
+
},
|
|
2692
3566
|
onContextChanged: ({ context }) => {
|
|
2693
3567
|
this.contextStore = this.normalizeContextStore(context);
|
|
2694
3568
|
this.requestUpdate();
|
|
@@ -2713,6 +3587,14 @@ export class WebInspectorElement extends LitElement {
|
|
|
2713
3587
|
this.coreUnsubscribe = core.subscribe(this.coreSubscriber).unsubscribe;
|
|
2714
3588
|
this.processAgentsChanged(core.agents);
|
|
2715
3589
|
|
|
3590
|
+
if (core.runtimeConnectionStatus === "connected") {
|
|
3591
|
+
if (!core.telemetryDisabled) {
|
|
3592
|
+
ensureTelemetryDistinctId();
|
|
3593
|
+
maybeShowDisclosure();
|
|
3594
|
+
}
|
|
3595
|
+
this.flushPendingBannerViewed();
|
|
3596
|
+
}
|
|
3597
|
+
|
|
2716
3598
|
// Subscribe to any already-registered thread stores. `getThreadStores` was
|
|
2717
3599
|
// added in the same release as this inspector; guard so consumers still on
|
|
2718
3600
|
// an older @copilotkit/core don't throw when assigning `inspector.core`.
|
|
@@ -2859,8 +3741,11 @@ export class WebInspectorElement extends LitElement {
|
|
|
2859
3741
|
onRunStartedEvent: ({ event }) => {
|
|
2860
3742
|
this.recordAgentEvent(agentId, "RUN_STARTED", event);
|
|
2861
3743
|
},
|
|
2862
|
-
onRunFinishedEvent: (
|
|
2863
|
-
this.recordAgentEvent(agentId, "RUN_FINISHED", {
|
|
3744
|
+
onRunFinishedEvent: (params) => {
|
|
3745
|
+
this.recordAgentEvent(agentId, "RUN_FINISHED", {
|
|
3746
|
+
event: params.event,
|
|
3747
|
+
result: "result" in params ? params.result : undefined,
|
|
3748
|
+
});
|
|
2864
3749
|
this.refreshOwnedThreadStore(agentId);
|
|
2865
3750
|
},
|
|
2866
3751
|
onRunErrorEvent: ({ event }) => {
|
|
@@ -3404,6 +4289,10 @@ ${argsString}</pre
|
|
|
3404
4289
|
const base =
|
|
3405
4290
|
"font-mono text-[10px] font-medium inline-flex items-center rounded-sm px-1.5 py-0.5 border";
|
|
3406
4291
|
|
|
4292
|
+
if (type === "RUN_ERROR") {
|
|
4293
|
+
return `${base} bg-rose-50 text-rose-700 border-rose-200`;
|
|
4294
|
+
}
|
|
4295
|
+
|
|
3407
4296
|
if (type.startsWith("RUN_")) {
|
|
3408
4297
|
return `${base} bg-blue-50 text-blue-700 border-blue-200`;
|
|
3409
4298
|
}
|
|
@@ -3428,10 +4317,6 @@ ${argsString}</pre
|
|
|
3428
4317
|
return `${base} bg-sky-50 text-sky-700 border-sky-200`;
|
|
3429
4318
|
}
|
|
3430
4319
|
|
|
3431
|
-
if (type === "RUN_ERROR") {
|
|
3432
|
-
return `${base} bg-rose-50 text-rose-700 border-rose-200`;
|
|
3433
|
-
}
|
|
3434
|
-
|
|
3435
4320
|
return `${base} bg-gray-100 text-gray-600 border-gray-200`;
|
|
3436
4321
|
}
|
|
3437
4322
|
|
|
@@ -5750,7 +6635,9 @@ ${argsString}</pre
|
|
|
5750
6635
|
|
|
5751
6636
|
<div class="space-y-2">
|
|
5752
6637
|
<h3 class="text-sm text-slate-500">Privacy</h3>
|
|
5753
|
-
<div
|
|
6638
|
+
<div
|
|
6639
|
+
class="rounded-lg border border-slate-200 bg-white p-4 space-y-3"
|
|
6640
|
+
>
|
|
5754
6641
|
<p class="text-sm text-gray-600 flex items-start gap-2">
|
|
5755
6642
|
<span>${optedOut ? "❌" : "✅"}</span>
|
|
5756
6643
|
<span>
|
|
@@ -5766,7 +6653,8 @@ ${argsString}</pre
|
|
|
5766
6653
|
href=${TELEMETRY_DOCS_URL}
|
|
5767
6654
|
target="_blank"
|
|
5768
6655
|
rel="noopener"
|
|
5769
|
-
|
|
6656
|
+
>Learn more →</a
|
|
6657
|
+
>
|
|
5770
6658
|
</div>
|
|
5771
6659
|
</div>
|
|
5772
6660
|
</div>
|
|
@@ -5791,6 +6679,45 @@ ${argsString}</pre
|
|
|
5791
6679
|
});
|
|
5792
6680
|
}
|
|
5793
6681
|
|
|
6682
|
+
private handleTalkToEngineerClick = (): void => {
|
|
6683
|
+
if (this.core?.telemetryDisabled) return;
|
|
6684
|
+
trackTalkToEngineerClicked(
|
|
6685
|
+
this.getThreadsTelemetryProps(
|
|
6686
|
+
{
|
|
6687
|
+
cta: "talk_to_engineer",
|
|
6688
|
+
cta_surface: "threads_header",
|
|
6689
|
+
},
|
|
6690
|
+
{ includeUrlAttribution: true },
|
|
6691
|
+
),
|
|
6692
|
+
);
|
|
6693
|
+
};
|
|
6694
|
+
|
|
6695
|
+
private handleThreadsIntelligenceSignupClick = (): void => {
|
|
6696
|
+
if (this.core?.telemetryDisabled) return;
|
|
6697
|
+
trackThreadsIntelligenceSignupClicked(
|
|
6698
|
+
this.getThreadsTelemetryProps(
|
|
6699
|
+
{
|
|
6700
|
+
cta: "signup",
|
|
6701
|
+
cta_surface: "threads_locked",
|
|
6702
|
+
},
|
|
6703
|
+
{ includeUrlAttribution: true },
|
|
6704
|
+
),
|
|
6705
|
+
);
|
|
6706
|
+
};
|
|
6707
|
+
|
|
6708
|
+
private handleThreadsTalkToEngineerClick = (): void => {
|
|
6709
|
+
if (this.core?.telemetryDisabled) return;
|
|
6710
|
+
trackThreadsTalkToEngineerClicked(
|
|
6711
|
+
this.getThreadsTelemetryProps(
|
|
6712
|
+
{
|
|
6713
|
+
cta: "talk_to_engineer",
|
|
6714
|
+
cta_surface: "threads_locked",
|
|
6715
|
+
},
|
|
6716
|
+
{ includeUrlAttribution: true },
|
|
6717
|
+
),
|
|
6718
|
+
);
|
|
6719
|
+
};
|
|
6720
|
+
|
|
5794
6721
|
private handleThreadDividerPointerDown = (event: PointerEvent) => {
|
|
5795
6722
|
this.threadDividerResizing = true;
|
|
5796
6723
|
this.threadDividerPointerId = event.pointerId;
|
|
@@ -5823,7 +6750,326 @@ ${argsString}</pre
|
|
|
5823
6750
|
this.threadDividerResizing = false;
|
|
5824
6751
|
};
|
|
5825
6752
|
|
|
6753
|
+
private trackThreadsViewStateOnce(
|
|
6754
|
+
state: "locked" | "empty_enabled" | "enabled",
|
|
6755
|
+
threadCount: number,
|
|
6756
|
+
): void {
|
|
6757
|
+
if (this.core?.telemetryDisabled) return;
|
|
6758
|
+
const key = `${state}:${this.getThreadServiceStatus()}`;
|
|
6759
|
+
if (this.viewedThreadsTelemetryStates.has(key)) return;
|
|
6760
|
+
this.viewedThreadsTelemetryStates.add(key);
|
|
6761
|
+
const props = this.getThreadsTelemetryProps({ thread_count: threadCount });
|
|
6762
|
+
if (state === "locked") {
|
|
6763
|
+
trackThreadsLockedViewed(props);
|
|
6764
|
+
} else if (state === "empty_enabled") {
|
|
6765
|
+
trackThreadsEmptyEnabledViewed(props);
|
|
6766
|
+
} else {
|
|
6767
|
+
trackThreadsEnabledViewed(props);
|
|
6768
|
+
}
|
|
6769
|
+
}
|
|
6770
|
+
|
|
6771
|
+
private renderThreadsLockedBackgroundMockup() {
|
|
6772
|
+
const threadRows = [
|
|
6773
|
+
{ width: 74, accent: true },
|
|
6774
|
+
{ width: 92 },
|
|
6775
|
+
{ width: 68 },
|
|
6776
|
+
{ width: 84 },
|
|
6777
|
+
{ width: 58 },
|
|
6778
|
+
{ width: 76 },
|
|
6779
|
+
];
|
|
6780
|
+
|
|
6781
|
+
return html`
|
|
6782
|
+
<div
|
|
6783
|
+
aria-hidden="true"
|
|
6784
|
+
style="
|
|
6785
|
+
position: absolute;
|
|
6786
|
+
inset: 0;
|
|
6787
|
+
display: grid;
|
|
6788
|
+
grid-template-columns: minmax(180px, 28%) 1fr;
|
|
6789
|
+
overflow: hidden;
|
|
6790
|
+
opacity: 0.58;
|
|
6791
|
+
pointer-events: none;
|
|
6792
|
+
"
|
|
6793
|
+
>
|
|
6794
|
+
<div
|
|
6795
|
+
style="
|
|
6796
|
+
display: flex;
|
|
6797
|
+
flex-direction: column;
|
|
6798
|
+
gap: 12px;
|
|
6799
|
+
padding: 28px 24px;
|
|
6800
|
+
border-right: 1px solid #dbdbe5;
|
|
6801
|
+
background: #fafafa;
|
|
6802
|
+
"
|
|
6803
|
+
>
|
|
6804
|
+
${threadRows.map(
|
|
6805
|
+
(row) => html`
|
|
6806
|
+
<div
|
|
6807
|
+
style="
|
|
6808
|
+
padding: 12px;
|
|
6809
|
+
border-radius: 8px;
|
|
6810
|
+
background: ${row.accent ? "#eee6fe" : "#ffffff"};
|
|
6811
|
+
box-shadow: inset 0 0 0 1px #eeeef4;
|
|
6812
|
+
"
|
|
6813
|
+
>
|
|
6814
|
+
<div
|
|
6815
|
+
style="
|
|
6816
|
+
height: 8px;
|
|
6817
|
+
width: ${row.width}%;
|
|
6818
|
+
border-radius: 99px;
|
|
6819
|
+
background: ${row.accent ? "#a984f5" : "#d7d7df"};
|
|
6820
|
+
"
|
|
6821
|
+
></div>
|
|
6822
|
+
<div
|
|
6823
|
+
style="
|
|
6824
|
+
height: 6px;
|
|
6825
|
+
width: 88%;
|
|
6826
|
+
margin-top: 10px;
|
|
6827
|
+
border-radius: 99px;
|
|
6828
|
+
background: #e3e3eb;
|
|
6829
|
+
"
|
|
6830
|
+
></div>
|
|
6831
|
+
<div
|
|
6832
|
+
style="
|
|
6833
|
+
height: 6px;
|
|
6834
|
+
width: 62%;
|
|
6835
|
+
margin-top: 7px;
|
|
6836
|
+
border-radius: 99px;
|
|
6837
|
+
background: #e8e8ef;
|
|
6838
|
+
"
|
|
6839
|
+
></div>
|
|
6840
|
+
</div>
|
|
6841
|
+
`,
|
|
6842
|
+
)}
|
|
6843
|
+
</div>
|
|
6844
|
+
<div
|
|
6845
|
+
style="
|
|
6846
|
+
min-width: 0;
|
|
6847
|
+
padding: 42px 48px;
|
|
6848
|
+
background: #ffffff;
|
|
6849
|
+
"
|
|
6850
|
+
>
|
|
6851
|
+
<div
|
|
6852
|
+
style="
|
|
6853
|
+
height: 10px;
|
|
6854
|
+
width: 180px;
|
|
6855
|
+
border-radius: 99px;
|
|
6856
|
+
background: #d7d7df;
|
|
6857
|
+
"
|
|
6858
|
+
></div>
|
|
6859
|
+
<div
|
|
6860
|
+
style="
|
|
6861
|
+
height: 8px;
|
|
6862
|
+
width: min(520px, 58%);
|
|
6863
|
+
margin-top: 28px;
|
|
6864
|
+
border-radius: 99px;
|
|
6865
|
+
background: #e3e3eb;
|
|
6866
|
+
"
|
|
6867
|
+
></div>
|
|
6868
|
+
<div
|
|
6869
|
+
style="
|
|
6870
|
+
height: 8px;
|
|
6871
|
+
width: min(430px, 48%);
|
|
6872
|
+
margin-top: 12px;
|
|
6873
|
+
border-radius: 99px;
|
|
6874
|
+
background: #e8e8ef;
|
|
6875
|
+
"
|
|
6876
|
+
></div>
|
|
6877
|
+
<div
|
|
6878
|
+
style="
|
|
6879
|
+
display: grid;
|
|
6880
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
6881
|
+
gap: 16px;
|
|
6882
|
+
max-width: 620px;
|
|
6883
|
+
margin-top: 30px;
|
|
6884
|
+
"
|
|
6885
|
+
>
|
|
6886
|
+
<div
|
|
6887
|
+
style="
|
|
6888
|
+
height: 116px;
|
|
6889
|
+
border-radius: 8px;
|
|
6890
|
+
background: #f5f5f8;
|
|
6891
|
+
box-shadow: inset 0 0 0 1px #eeeef4;
|
|
6892
|
+
"
|
|
6893
|
+
></div>
|
|
6894
|
+
<div
|
|
6895
|
+
style="
|
|
6896
|
+
height: 116px;
|
|
6897
|
+
border-radius: 8px;
|
|
6898
|
+
background: #f5f5f8;
|
|
6899
|
+
box-shadow: inset 0 0 0 1px #eeeef4;
|
|
6900
|
+
"
|
|
6901
|
+
></div>
|
|
6902
|
+
</div>
|
|
6903
|
+
<div
|
|
6904
|
+
style="
|
|
6905
|
+
height: 10px;
|
|
6906
|
+
width: min(680px, 74%);
|
|
6907
|
+
margin-top: 34px;
|
|
6908
|
+
border-radius: 99px;
|
|
6909
|
+
background: #e3e3eb;
|
|
6910
|
+
"
|
|
6911
|
+
></div>
|
|
6912
|
+
<div
|
|
6913
|
+
style="
|
|
6914
|
+
height: 10px;
|
|
6915
|
+
width: min(560px, 60%);
|
|
6916
|
+
margin-top: 14px;
|
|
6917
|
+
border-radius: 99px;
|
|
6918
|
+
background: #e8e8ef;
|
|
6919
|
+
"
|
|
6920
|
+
></div>
|
|
6921
|
+
</div>
|
|
6922
|
+
</div>
|
|
6923
|
+
`;
|
|
6924
|
+
}
|
|
6925
|
+
|
|
6926
|
+
private renderThreadsLockedView() {
|
|
6927
|
+
this.trackThreadsViewStateOnce("locked", 0);
|
|
6928
|
+
return html`
|
|
6929
|
+
<div
|
|
6930
|
+
style="
|
|
6931
|
+
position: relative;
|
|
6932
|
+
height: 100%;
|
|
6933
|
+
display: flex;
|
|
6934
|
+
align-items: center;
|
|
6935
|
+
justify-content: center;
|
|
6936
|
+
padding: 32px;
|
|
6937
|
+
overflow: hidden;
|
|
6938
|
+
background: #ffffff;
|
|
6939
|
+
"
|
|
6940
|
+
>
|
|
6941
|
+
${this.renderThreadsLockedBackgroundMockup()}
|
|
6942
|
+
<div
|
|
6943
|
+
aria-hidden="true"
|
|
6944
|
+
style="
|
|
6945
|
+
position: absolute;
|
|
6946
|
+
inset: 0;
|
|
6947
|
+
pointer-events: none;
|
|
6948
|
+
background:
|
|
6949
|
+
radial-gradient(circle at center, rgba(255,255,255,0.9) 0, rgba(255,255,255,0.78) 24%, rgba(255,255,255,0.34) 48%, rgba(255,255,255,0.56) 100%);
|
|
6950
|
+
"
|
|
6951
|
+
></div>
|
|
6952
|
+
<div
|
|
6953
|
+
style="
|
|
6954
|
+
position: relative;
|
|
6955
|
+
z-index: 1;
|
|
6956
|
+
max-width: 440px;
|
|
6957
|
+
text-align: center;
|
|
6958
|
+
color: #57575b;
|
|
6959
|
+
"
|
|
6960
|
+
>
|
|
6961
|
+
<div
|
|
6962
|
+
aria-hidden="true"
|
|
6963
|
+
style="
|
|
6964
|
+
margin: 0 auto 18px;
|
|
6965
|
+
display: flex;
|
|
6966
|
+
justify-content: center;
|
|
6967
|
+
"
|
|
6968
|
+
>
|
|
6969
|
+
<div
|
|
6970
|
+
style="
|
|
6971
|
+
display: flex;
|
|
6972
|
+
height: 44px;
|
|
6973
|
+
width: 44px;
|
|
6974
|
+
align-items: center;
|
|
6975
|
+
justify-content: center;
|
|
6976
|
+
border: 1px solid #dfd6fb;
|
|
6977
|
+
border-radius: 8px;
|
|
6978
|
+
background: #eee6fe;
|
|
6979
|
+
color: #57575b;
|
|
6980
|
+
box-shadow: 0 8px 18px rgba(87, 87, 91, 0.14);
|
|
6981
|
+
"
|
|
6982
|
+
>
|
|
6983
|
+
${this.renderIcon("Lock")}
|
|
6984
|
+
</div>
|
|
6985
|
+
</div>
|
|
6986
|
+
<h2
|
|
6987
|
+
style="
|
|
6988
|
+
margin: 0 0 8px;
|
|
6989
|
+
font-size: 16px;
|
|
6990
|
+
line-height: 1.35;
|
|
6991
|
+
font-weight: 600;
|
|
6992
|
+
color: #010507;
|
|
6993
|
+
"
|
|
6994
|
+
>
|
|
6995
|
+
Enable Intelligence to inspect Threads.
|
|
6996
|
+
</h2>
|
|
6997
|
+
<p
|
|
6998
|
+
style="
|
|
6999
|
+
margin: 0 auto 18px;
|
|
7000
|
+
max-width: 380px;
|
|
7001
|
+
font-size: 13px;
|
|
7002
|
+
line-height: 1.55;
|
|
7003
|
+
color: #57575b;
|
|
7004
|
+
"
|
|
7005
|
+
>
|
|
7006
|
+
Persist conversations and inspect saved thread history from the
|
|
7007
|
+
Inspector.
|
|
7008
|
+
</p>
|
|
7009
|
+
<div
|
|
7010
|
+
style="
|
|
7011
|
+
display: flex;
|
|
7012
|
+
flex-wrap: wrap;
|
|
7013
|
+
justify-content: center;
|
|
7014
|
+
gap: 8px;
|
|
7015
|
+
"
|
|
7016
|
+
>
|
|
7017
|
+
<a
|
|
7018
|
+
href=${this.getTalkToEngineerUrl()}
|
|
7019
|
+
target="_blank"
|
|
7020
|
+
rel="noopener"
|
|
7021
|
+
style="
|
|
7022
|
+
display: inline-flex;
|
|
7023
|
+
min-height: 34px;
|
|
7024
|
+
align-items: center;
|
|
7025
|
+
justify-content: center;
|
|
7026
|
+
gap: 6px;
|
|
7027
|
+
border-radius: 6px;
|
|
7028
|
+
background: #010507;
|
|
7029
|
+
padding: 8px 12px;
|
|
7030
|
+
font-size: 12px;
|
|
7031
|
+
font-weight: 600;
|
|
7032
|
+
color: #ffffff;
|
|
7033
|
+
text-decoration: none;
|
|
7034
|
+
"
|
|
7035
|
+
@click=${this.handleThreadsTalkToEngineerClick}
|
|
7036
|
+
>
|
|
7037
|
+
Talk to an Engineer
|
|
7038
|
+
</a>
|
|
7039
|
+
<a
|
|
7040
|
+
href=${this.getIntelligenceSignupUrl()}
|
|
7041
|
+
target="_blank"
|
|
7042
|
+
rel="noopener"
|
|
7043
|
+
style="
|
|
7044
|
+
display: inline-flex;
|
|
7045
|
+
min-height: 34px;
|
|
7046
|
+
align-items: center;
|
|
7047
|
+
justify-content: center;
|
|
7048
|
+
gap: 6px;
|
|
7049
|
+
border-radius: 6px;
|
|
7050
|
+
border: 1px solid #dbdbe5;
|
|
7051
|
+
background: #ffffff;
|
|
7052
|
+
padding: 8px 12px;
|
|
7053
|
+
font-size: 12px;
|
|
7054
|
+
font-weight: 600;
|
|
7055
|
+
color: #57575b;
|
|
7056
|
+
text-decoration: none;
|
|
7057
|
+
"
|
|
7058
|
+
@click=${this.handleThreadsIntelligenceSignupClick}
|
|
7059
|
+
>
|
|
7060
|
+
Sign up for Intelligence
|
|
7061
|
+
</a>
|
|
7062
|
+
</div>
|
|
7063
|
+
</div>
|
|
7064
|
+
</div>
|
|
7065
|
+
`;
|
|
7066
|
+
}
|
|
7067
|
+
|
|
5826
7068
|
private renderThreadsView() {
|
|
7069
|
+
if (!this.areThreadEndpointsAvailable()) {
|
|
7070
|
+
return this.renderThreadsLockedView();
|
|
7071
|
+
}
|
|
7072
|
+
|
|
5827
7073
|
const displayThreads =
|
|
5828
7074
|
this.selectedContext === "all-agents"
|
|
5829
7075
|
? this._threads
|
|
@@ -5847,91 +7093,110 @@ ${argsString}</pre
|
|
|
5847
7093
|
? (displayThreads.find((t) => t.id === this.selectedThreadId) ?? null)
|
|
5848
7094
|
: null;
|
|
5849
7095
|
|
|
7096
|
+
if (!threadsErrorMessage) {
|
|
7097
|
+
this.trackThreadsViewStateOnce(
|
|
7098
|
+
displayThreads.length === 0 ? "empty_enabled" : "enabled",
|
|
7099
|
+
displayThreads.length,
|
|
7100
|
+
);
|
|
7101
|
+
}
|
|
7102
|
+
|
|
5850
7103
|
return html`
|
|
5851
|
-
<div style="display:flex;height:100%;overflow:hidden;">
|
|
5852
|
-
<!-- Left sidebar: thread list -->
|
|
7104
|
+
<div style="display:flex;height:100%;overflow:hidden;flex-direction:column;">
|
|
5853
7105
|
<div
|
|
5854
|
-
style="
|
|
7106
|
+
style="display:flex;align-items:center;justify-content:flex-end;border-bottom:1px solid #DBDBE5;background:#ffffff;padding:8px 12px;flex-shrink:0;"
|
|
5855
7107
|
>
|
|
5856
|
-
<
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
@
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
></cpk-thread-list>
|
|
7108
|
+
<a
|
|
7109
|
+
href=${this.getTalkToEngineerUrl()}
|
|
7110
|
+
target="_blank"
|
|
7111
|
+
rel="noopener"
|
|
7112
|
+
style="display:inline-flex;align-items:center;gap:6px;border-radius:6px;border:1px solid #dbdbe5;background:#ffffff;padding:7px 10px;font-size:12px;font-weight:600;color:#57575b;text-decoration:none;"
|
|
7113
|
+
@click=${this.handleTalkToEngineerClick}
|
|
7114
|
+
>
|
|
7115
|
+
Talk to an Engineer
|
|
7116
|
+
</a>
|
|
5866
7117
|
</div>
|
|
7118
|
+
<div style="display:flex;min-height:0;flex:1;overflow:hidden;">
|
|
7119
|
+
<!-- Left sidebar: thread list -->
|
|
7120
|
+
<div
|
|
7121
|
+
style="width:${this.threadListWidth}px;flex-shrink:0;overflow:hidden;display:flex;flex-direction:column;border-right:1px solid #DBDBE5;"
|
|
7122
|
+
>
|
|
7123
|
+
<cpk-thread-list
|
|
7124
|
+
style="height:100%;"
|
|
7125
|
+
.threads=${displayThreads}
|
|
7126
|
+
.selectedThreadId=${this.selectedThreadId}
|
|
7127
|
+
.errorMessage=${threadsErrorMessage}
|
|
7128
|
+
@threadSelected=${(e: CustomEvent<string>) => {
|
|
7129
|
+
this.selectedThreadId = e.detail;
|
|
7130
|
+
this.requestUpdate();
|
|
7131
|
+
}}
|
|
7132
|
+
></cpk-thread-list>
|
|
7133
|
+
</div>
|
|
5867
7134
|
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
display: flex;
|
|
5912
|
-
flex-direction: column;
|
|
5913
|
-
align-items: center;
|
|
5914
|
-
justify-content: center;
|
|
5915
|
-
gap: 8px;
|
|
5916
|
-
color: #838389;
|
|
5917
|
-
"
|
|
5918
|
-
>
|
|
5919
|
-
<svg
|
|
5920
|
-
width="32"
|
|
5921
|
-
height="32"
|
|
5922
|
-
viewBox="0 0 24 24"
|
|
5923
|
-
fill="none"
|
|
5924
|
-
stroke="#c0c0c8"
|
|
5925
|
-
stroke-width="1.5"
|
|
5926
|
-
stroke-linecap="round"
|
|
5927
|
-
stroke-linejoin="round"
|
|
7135
|
+
<!-- Resize divider -->
|
|
7136
|
+
<div
|
|
7137
|
+
style="width:4px;flex-shrink:0;cursor:col-resize;background:transparent;position:relative;z-index:1;"
|
|
7138
|
+
@pointerdown=${this.handleThreadDividerPointerDown}
|
|
7139
|
+
@pointermove=${this.handleThreadDividerPointerMove}
|
|
7140
|
+
@pointerup=${this.handleThreadDividerPointerUp}
|
|
7141
|
+
@pointercancel=${this.handleThreadDividerPointerUp}
|
|
7142
|
+
></div>
|
|
7143
|
+
|
|
7144
|
+
<!-- Center + right: thread details or empty state -->
|
|
7145
|
+
<div style="flex:1;min-width:0;overflow:hidden;display:flex;">
|
|
7146
|
+
${
|
|
7147
|
+
selectedThread
|
|
7148
|
+
? html`<cpk-thread-details
|
|
7149
|
+
style="flex:1;min-width:0;"
|
|
7150
|
+
.threadId=${selectedThread.id}
|
|
7151
|
+
.thread=${selectedThread}
|
|
7152
|
+
.runtimeUrl=${this._core?.runtimeUrl ?? ""}
|
|
7153
|
+
.headers=${this._core?.headers ?? {}}
|
|
7154
|
+
.threadInspectionAvailable=${
|
|
7155
|
+
this._core?.threadEndpoints?.inspect !== false
|
|
7156
|
+
}
|
|
7157
|
+
.liveMessageVersion=${
|
|
7158
|
+
this.liveMessageVersion.get(selectedThread.id) ?? 0
|
|
7159
|
+
}
|
|
7160
|
+
.agentStateInput=${this.getLatestStateForAgent(
|
|
7161
|
+
selectedThread.agentId,
|
|
7162
|
+
)}
|
|
7163
|
+
.agentEventsInput=${
|
|
7164
|
+
this.agentEvents.get(selectedThread.agentId) ?? []
|
|
7165
|
+
}
|
|
7166
|
+
></cpk-thread-details>`
|
|
7167
|
+
: html`
|
|
7168
|
+
<div
|
|
7169
|
+
style="
|
|
7170
|
+
flex: 1;
|
|
7171
|
+
display: flex;
|
|
7172
|
+
flex-direction: column;
|
|
7173
|
+
align-items: center;
|
|
7174
|
+
justify-content: center;
|
|
7175
|
+
gap: 8px;
|
|
7176
|
+
color: #838389;
|
|
7177
|
+
"
|
|
5928
7178
|
>
|
|
5929
|
-
<
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
7179
|
+
<svg
|
|
7180
|
+
width="32"
|
|
7181
|
+
height="32"
|
|
7182
|
+
viewBox="0 0 24 24"
|
|
7183
|
+
fill="none"
|
|
7184
|
+
stroke="#c0c0c8"
|
|
7185
|
+
stroke-width="1.5"
|
|
7186
|
+
stroke-linecap="round"
|
|
7187
|
+
stroke-linejoin="round"
|
|
7188
|
+
>
|
|
7189
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
|
7190
|
+
</svg>
|
|
7191
|
+
<span style="font-size: 13px">${
|
|
7192
|
+
displayThreads.length === 0
|
|
7193
|
+
? "No threads yet"
|
|
7194
|
+
: "Select a thread to inspect"
|
|
7195
|
+
}</span>
|
|
7196
|
+
</div>
|
|
7197
|
+
`
|
|
7198
|
+
}
|
|
7199
|
+
</div>
|
|
5935
7200
|
</div>
|
|
5936
7201
|
</div>
|
|
5937
7202
|
`;
|
|
@@ -6068,28 +7333,30 @@ ${argsString}</pre
|
|
|
6068
7333
|
<div class="relative h-full w-full overflow-y-auto overflow-x-hidden">
|
|
6069
7334
|
<table class="w-full table-fixed border-collapse text-xs box-border">
|
|
6070
7335
|
<colgroup>
|
|
6071
|
-
<col style="width:${this.evtColWidths[0]}px"
|
|
6072
|
-
<col style="width:${this.evtColWidths[1]}px"
|
|
6073
|
-
<col style="width:${this.evtColWidths[2]}px"
|
|
6074
|
-
<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 />
|
|
6075
7340
|
</colgroup>
|
|
6076
7341
|
<thead class="sticky top-0 z-10">
|
|
6077
7342
|
<tr class="bg-white">
|
|
6078
7343
|
${["Agent", "Time", "Event Type"].map(
|
|
6079
|
-
(label, col) =>
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
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>`,
|
|
6093
7360
|
)}
|
|
6094
7361
|
<th
|
|
6095
7362
|
class="border-b border-gray-200 bg-white px-3 py-2 text-left font-medium text-gray-900"
|
|
@@ -6421,8 +7688,14 @@ ${prettyEvent}</pre
|
|
|
6421
7688
|
? html`
|
|
6422
7689
|
<div class="w-full text-xs">
|
|
6423
7690
|
<div class="flex bg-gray-50">
|
|
6424
|
-
<div
|
|
6425
|
-
|
|
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>
|
|
6426
7699
|
</div>
|
|
6427
7700
|
<div class="divide-y divide-gray-200">
|
|
6428
7701
|
${messages.map((msg) => {
|
|
@@ -6445,7 +7718,9 @@ ${prettyEvent}</pre
|
|
|
6445
7718
|
<div class="flex items-start">
|
|
6446
7719
|
<div class="w-40 shrink-0 px-4 py-2">
|
|
6447
7720
|
<span
|
|
6448
|
-
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
|
+
}"
|
|
6449
7724
|
>
|
|
6450
7725
|
${role}
|
|
6451
7726
|
</span>
|
|
@@ -6603,8 +7878,8 @@ ${prettyEvent}</pre
|
|
|
6603
7878
|
}
|
|
6604
7879
|
|
|
6605
7880
|
if (key === "threads") {
|
|
6606
|
-
if (
|
|
6607
|
-
trackThreadsTabClicked();
|
|
7881
|
+
if (previousMenu !== "threads" && !this.core?.telemetryDisabled) {
|
|
7882
|
+
trackThreadsTabClicked(this.getThreadsTelemetryProps());
|
|
6608
7883
|
}
|
|
6609
7884
|
this.autoSelectLatestThread();
|
|
6610
7885
|
}
|
|
@@ -7284,6 +8559,22 @@ ${prettyEvent}</pre
|
|
|
7284
8559
|
}
|
|
7285
8560
|
</button>
|
|
7286
8561
|
</div>
|
|
8562
|
+
<pre
|
|
8563
|
+
style="
|
|
8564
|
+
margin: 0;
|
|
8565
|
+
max-height: 180px;
|
|
8566
|
+
overflow: auto;
|
|
8567
|
+
white-space: pre-wrap;
|
|
8568
|
+
word-break: break-word;
|
|
8569
|
+
border-radius: 6px;
|
|
8570
|
+
border: 1px solid #eeeef4;
|
|
8571
|
+
background: #f7f7f9;
|
|
8572
|
+
padding: 10px;
|
|
8573
|
+
font-size: 11px;
|
|
8574
|
+
line-height: 1.5;
|
|
8575
|
+
color: #2d2d30;
|
|
8576
|
+
"
|
|
8577
|
+
>${this.formatContextValue(context.value)}</pre>
|
|
7287
8578
|
`
|
|
7288
8579
|
: html`
|
|
7289
8580
|
<div class="flex items-center justify-center py-4 text-xs text-gray-500">
|
|
@@ -7435,7 +8726,9 @@ ${prettyEvent}</pre
|
|
|
7435
8726
|
return nothing;
|
|
7436
8727
|
}
|
|
7437
8728
|
|
|
7438
|
-
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
|
+
>
|
|
7439
8732
|
<div
|
|
7440
8733
|
class="mb-2 flex items-center gap-2 text-xs font-semibold text-slate-900"
|
|
7441
8734
|
>
|
|
@@ -7454,7 +8747,13 @@ ${prettyEvent}</pre
|
|
|
7454
8747
|
${this.renderIcon("X")}
|
|
7455
8748
|
</button>
|
|
7456
8749
|
</div>
|
|
7457
|
-
<div
|
|
8750
|
+
<div
|
|
8751
|
+
class="announcement-body ${
|
|
8752
|
+
this.announcementExpanded
|
|
8753
|
+
? "announcement-body--expanded"
|
|
8754
|
+
: "announcement-body--collapsed"
|
|
8755
|
+
}"
|
|
8756
|
+
>
|
|
7458
8757
|
<div
|
|
7459
8758
|
class="announcement-content"
|
|
7460
8759
|
@click=${this.handleAnnouncementContentClick}
|
|
@@ -7632,10 +8931,15 @@ ${prettyEvent}</pre
|
|
|
7632
8931
|
): Promise<string | null> {
|
|
7633
8932
|
const renderer = new marked.Renderer();
|
|
7634
8933
|
renderer.link = (href, title, text) => {
|
|
7635
|
-
const safeHref = this.escapeHtmlAttr(
|
|
8934
|
+
const safeHref = this.escapeHtmlAttr(
|
|
8935
|
+
this.isSafeAnnouncementHref(href ?? "")
|
|
8936
|
+
? this.appendRefParam(href ?? "")
|
|
8937
|
+
: "#",
|
|
8938
|
+
);
|
|
7636
8939
|
const titleAttr = title ? ` title="${this.escapeHtmlAttr(title)}"` : "";
|
|
7637
8940
|
return `<a href="${safeHref}" target="_blank" rel="noopener"${titleAttr}>${text}</a>`;
|
|
7638
8941
|
};
|
|
8942
|
+
renderer.html = (html) => escapeHtml(html);
|
|
7639
8943
|
renderer.code = (code, lang) => {
|
|
7640
8944
|
const safeLang = (lang ?? "").replace(/[^a-z0-9-]/gi, "");
|
|
7641
8945
|
const langClass = safeLang ? ` class="language-${safeLang}"` : "";
|
|
@@ -7646,6 +8950,24 @@ ${prettyEvent}</pre
|
|
|
7646
8950
|
return marked.parse(markdown, { renderer, async: false });
|
|
7647
8951
|
}
|
|
7648
8952
|
|
|
8953
|
+
private isSafeAnnouncementHref(href: string): boolean {
|
|
8954
|
+
try {
|
|
8955
|
+
const url = new URL(
|
|
8956
|
+
href,
|
|
8957
|
+
typeof window !== "undefined"
|
|
8958
|
+
? window.location.href
|
|
8959
|
+
: "https://copilotkit.ai",
|
|
8960
|
+
);
|
|
8961
|
+
return (
|
|
8962
|
+
url.protocol === "http:" ||
|
|
8963
|
+
url.protocol === "https:" ||
|
|
8964
|
+
url.protocol === "mailto:"
|
|
8965
|
+
);
|
|
8966
|
+
} catch {
|
|
8967
|
+
return false;
|
|
8968
|
+
}
|
|
8969
|
+
}
|
|
8970
|
+
|
|
7649
8971
|
private copyResetTimeouts = new WeakMap<HTMLButtonElement, number>();
|
|
7650
8972
|
|
|
7651
8973
|
private encodeBase64(value: string): string {
|
|
@@ -7710,8 +9032,9 @@ ${prettyEvent}</pre
|
|
|
7710
9032
|
}
|
|
7711
9033
|
};
|
|
7712
9034
|
|
|
7713
|
-
private appendRefParam(href: string): string {
|
|
9035
|
+
private appendRefParam(href: string, ref = "cpk-inspector"): string {
|
|
7714
9036
|
try {
|
|
9037
|
+
const isRootRelative = href.startsWith("/") && !href.startsWith("//");
|
|
7715
9038
|
const url = new URL(
|
|
7716
9039
|
href,
|
|
7717
9040
|
typeof window !== "undefined"
|
|
@@ -7719,7 +9042,7 @@ ${prettyEvent}</pre
|
|
|
7719
9042
|
: "https://copilotkit.ai",
|
|
7720
9043
|
);
|
|
7721
9044
|
if (!url.searchParams.has("ref")) {
|
|
7722
|
-
url.searchParams.append("ref",
|
|
9045
|
+
url.searchParams.append("ref", ref);
|
|
7723
9046
|
}
|
|
7724
9047
|
// Propagate the inspector's anonymous distinct-ID so the website /
|
|
7725
9048
|
// Ops API can call posthog.alias(...) on signup-flow landing and
|
|
@@ -7728,19 +9051,28 @@ ${prettyEvent}</pre
|
|
|
7728
9051
|
// suppresses cross-domain ID leaks too.
|
|
7729
9052
|
if (
|
|
7730
9053
|
!url.searchParams.has("posthog_distinct_id") &&
|
|
7731
|
-
!this.core?.telemetryDisabled
|
|
9054
|
+
!this.core?.telemetryDisabled &&
|
|
9055
|
+
this.isCopilotKitDestination(url)
|
|
7732
9056
|
) {
|
|
7733
9057
|
const distinctId = getTelemetryDistinctIdForUrl();
|
|
7734
9058
|
if (distinctId) {
|
|
7735
9059
|
url.searchParams.append("posthog_distinct_id", distinctId);
|
|
7736
9060
|
}
|
|
7737
9061
|
}
|
|
9062
|
+
if (isRootRelative) {
|
|
9063
|
+
return `${url.pathname}${url.search}${url.hash}`;
|
|
9064
|
+
}
|
|
7738
9065
|
return url.toString();
|
|
7739
9066
|
} catch {
|
|
7740
9067
|
return href;
|
|
7741
9068
|
}
|
|
7742
9069
|
}
|
|
7743
9070
|
|
|
9071
|
+
private isCopilotKitDestination(url: URL): boolean {
|
|
9072
|
+
const hostname = url.hostname.toLowerCase();
|
|
9073
|
+
return hostname === "copilotkit.ai" || hostname.endsWith(".copilotkit.ai");
|
|
9074
|
+
}
|
|
9075
|
+
|
|
7744
9076
|
private escapeHtmlAttr(value: string): string {
|
|
7745
9077
|
return escapeHtml(value).replace(/"/g, """).replace(/'/g, "'");
|
|
7746
9078
|
}
|