@copilotkit/web-inspector 1.62.3 → 1.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import inspector_logo_default from "./assets/inspector-logo.mjs";
3
3
  import inspector_logo_icon_default from "./assets/inspector-logo-icon.mjs";
4
4
  import { applyAnchorPosition, centerContext, clampSize, constrainToViewport, keepPositionWithinViewport, updateAnchorFromPosition, updateSizeFromElement } from "./lib/context-helpers.mjs";
5
5
  import { isValidAnchor, isValidDockMode, isValidPosition, isValidSize, loadInspectorState, saveInspectorState } from "./lib/persistence.mjs";
6
- import { TELEMETRY_DOCS_URL, ensureTelemetryDistinctId, getRuntimeUrlType, getTelemetryDistinctIdForUrl, maybeShowDisclosure, trackBannerClicked, trackBannerViewed, trackMemoriesTabClicked, trackTalkToEngineerClicked, trackThreadsEmptyEnabledViewed, trackThreadsEnabledViewed, trackThreadsIntelligenceSignupClicked, trackThreadsLockedViewed, trackThreadsTabClicked, trackThreadsTalkToEngineerClicked } from "./lib/telemetry.mjs";
6
+ import { TELEMETRY_DOCS_URL, ensureTelemetryDistinctId, getRuntimeUrlType, getTelemetryDistinctIdForUrl, maybeShowDisclosure, trackBannerClicked, trackBannerViewed, trackMemoriesTabClicked, trackTalkToEngineerClicked, trackThreadsEmptyEnabledViewed, trackThreadsEnabledViewed, trackThreadsExampleSelected, trackThreadsExampleTourCompleted, trackThreadsExampleTourDismissed, trackThreadsExampleTourReopened, trackThreadsExampleTourStarted, trackThreadsExampleTourStepViewed, trackThreadsExampleViewed, trackThreadsIntelligenceSignupClicked, trackThreadsLockedViewed, trackThreadsTabClicked, trackThreadsTalkToEngineerClicked } from "./lib/telemetry.mjs";
7
7
  import { LitElement, css, html, nothing, unsafeCSS } from "lit";
8
8
  import { marked } from "marked";
9
9
  import { styleMap } from "lit/directives/style-map.js";
@@ -34,7 +34,13 @@ const DOCKED_LEFT_WIDTH = 500;
34
34
  const MAX_AGENT_EVENTS = 200;
35
35
  const MAX_TOTAL_EVENTS = 500;
36
36
  const INTELLIGENCE_SIGNUP_URL = "https://go.copilotkit.ai/intelligence-signup";
37
+ const THREADS_INTELLIGENCE_SIGNIN_URL = "https://dashboard.operations.copilotkit.ai/sign-in";
37
38
  const TALK_TO_ENGINEER_URL = "https://www.copilotkit.ai/talk-to-an-engineer";
39
+ const THREADS_DOCS_URL = "https://docs.copilotkit.ai/threads";
40
+ const SELF_HOSTED_INTELLIGENCE_URL = "https://docs.copilotkit.ai/premium/self-hosting";
41
+ const THREADS_EXAMPLE_OVERVIEW_VIDEO_URL = "https://cdn.copilotkit.ai/corp-site/videos/copilotkit-generative-ui-agentic-frontend-demo.webm";
42
+ const THREADS_EXAMPLE_TOUR_STORAGE_KEY = "cpk:inspector:threads-example-tour:v1";
43
+ const THREADS_EXAMPLE_AGENT_ID = "threads-feature";
38
44
  const AGENT_EVENT_TYPES = [
39
45
  "RUN_STARTED",
40
46
  "RUN_FINISHED",
@@ -60,6 +66,201 @@ const AGENT_EVENT_TYPES = [
60
66
  "ACTIVITY_SNAPSHOT",
61
67
  "ACTIVITY_DELTA"
62
68
  ];
69
+ const THREADS_EXAMPLE_THREADS = [
70
+ {
71
+ id: "example-realtime-sync",
72
+ name: "Realtime thread sync",
73
+ agentId: THREADS_EXAMPLE_AGENT_ID,
74
+ organizationId: "example-organization",
75
+ createdById: "example-user",
76
+ archived: false,
77
+ createdAt: "2026-07-08T16:00:00.000Z",
78
+ updatedAt: "2026-07-08T16:30:00.000Z",
79
+ isExample: true
80
+ },
81
+ {
82
+ id: "example-manage-history",
83
+ name: "Manage saved conversations",
84
+ agentId: THREADS_EXAMPLE_AGENT_ID,
85
+ organizationId: "example-organization",
86
+ createdById: "example-user",
87
+ archived: false,
88
+ createdAt: "2026-07-07T17:45:00.000Z",
89
+ updatedAt: "2026-07-07T18:15:00.000Z",
90
+ isExample: true
91
+ },
92
+ {
93
+ id: "example-inspect-runs",
94
+ name: "Inspect durable run history",
95
+ agentId: THREADS_EXAMPLE_AGENT_ID,
96
+ organizationId: "example-organization",
97
+ createdById: "example-user",
98
+ archived: false,
99
+ createdAt: "2026-07-06T20:15:00.000Z",
100
+ updatedAt: "2026-07-06T20:45:00.000Z",
101
+ isExample: true
102
+ }
103
+ ];
104
+ const THREADS_EXAMPLE_DETAILS = {
105
+ "example-realtime-sync": {
106
+ messages: [{
107
+ id: "example-sync-user",
108
+ role: "user",
109
+ content: "Resume the checkout support thread from yesterday."
110
+ }, {
111
+ id: "example-sync-assistant",
112
+ role: "assistant",
113
+ content: "I found the saved thread, restored the cart state, and continued from the latest user message."
114
+ }],
115
+ events: [
116
+ {
117
+ type: "RUN_STARTED",
118
+ timestamp: "2026-07-08T16:30:00.000Z",
119
+ payload: {
120
+ threadId: "example-realtime-sync",
121
+ agentId: THREADS_EXAMPLE_AGENT_ID
122
+ }
123
+ },
124
+ {
125
+ type: "MESSAGES_SNAPSHOT",
126
+ timestamp: "2026-07-08T16:30:01.000Z",
127
+ payload: {
128
+ messageCount: 6,
129
+ source: "thread-history"
130
+ }
131
+ },
132
+ {
133
+ type: "STATE_SNAPSHOT",
134
+ timestamp: "2026-07-08T16:30:02.000Z",
135
+ payload: {
136
+ cartId: "cart_demo_42",
137
+ checkoutStep: "shipping",
138
+ resumed: true
139
+ }
140
+ },
141
+ {
142
+ type: "RUN_FINISHED",
143
+ timestamp: "2026-07-08T16:30:04.000Z",
144
+ payload: { status: "completed" }
145
+ }
146
+ ],
147
+ state: {
148
+ cartId: "cart_demo_42",
149
+ checkoutStep: "shipping",
150
+ userIntent: "resume_previous_checkout",
151
+ persistedThread: true
152
+ }
153
+ },
154
+ "example-manage-history": {
155
+ messages: [{
156
+ id: "example-history-user",
157
+ role: "user",
158
+ content: "Rename this saved support conversation for the handoff."
159
+ }, {
160
+ id: "example-history-assistant",
161
+ role: "assistant",
162
+ content: "Renamed the thread and kept the prior messages available for the next session."
163
+ }],
164
+ events: [
165
+ {
166
+ type: "RUN_STARTED",
167
+ timestamp: "2026-07-07T18:15:00.000Z",
168
+ payload: {
169
+ threadId: "example-manage-history",
170
+ agentId: THREADS_EXAMPLE_AGENT_ID
171
+ }
172
+ },
173
+ {
174
+ type: "CUSTOM_EVENT",
175
+ timestamp: "2026-07-07T18:15:01.000Z",
176
+ payload: {
177
+ action: "thread_renamed",
178
+ previousName: "Untitled",
179
+ name: "Billing escalation handoff"
180
+ }
181
+ },
182
+ {
183
+ type: "RUN_FINISHED",
184
+ timestamp: "2026-07-07T18:15:03.000Z",
185
+ payload: { status: "completed" }
186
+ }
187
+ ],
188
+ state: {
189
+ name: "Billing escalation handoff",
190
+ savedMessages: 14,
191
+ lastHandoff: "support-team"
192
+ }
193
+ },
194
+ "example-inspect-runs": {
195
+ messages: [{
196
+ id: "example-inspect-user",
197
+ role: "user",
198
+ content: "Why did the assistant recommend the enterprise plan?"
199
+ }, {
200
+ id: "example-inspect-assistant",
201
+ role: "assistant",
202
+ content: "The recommendation came from the account size, SSO requirement, and audit-log constraint in state."
203
+ }],
204
+ events: [
205
+ {
206
+ type: "RUN_STARTED",
207
+ timestamp: "2026-07-06T20:45:00.000Z",
208
+ payload: {
209
+ threadId: "example-inspect-runs",
210
+ agentId: THREADS_EXAMPLE_AGENT_ID
211
+ }
212
+ },
213
+ {
214
+ type: "TOOL_CALL_START",
215
+ timestamp: "2026-07-06T20:45:01.000Z",
216
+ payload: {
217
+ toolCallId: "call_account_lookup",
218
+ toolName: "lookupAccount"
219
+ }
220
+ },
221
+ {
222
+ type: "TOOL_CALL_RESULT",
223
+ timestamp: "2026-07-06T20:45:02.000Z",
224
+ payload: {
225
+ toolCallId: "call_account_lookup",
226
+ seats: 220,
227
+ requiresSso: true
228
+ }
229
+ },
230
+ {
231
+ type: "RUN_FINISHED",
232
+ timestamp: "2026-07-06T20:45:04.000Z",
233
+ payload: { status: "completed" }
234
+ }
235
+ ],
236
+ state: {
237
+ accountTier: "growth",
238
+ seats: 220,
239
+ requiresSso: true,
240
+ auditLogsRequired: true
241
+ }
242
+ }
243
+ };
244
+ const THREADS_EXAMPLE_TOUR_STEPS = [
245
+ {
246
+ tab: "timeline",
247
+ label: "Timeline",
248
+ title: "Read the run as a story",
249
+ body: "The timeline turns messages, tool calls, state changes, and run markers into a scannable debugging trail."
250
+ },
251
+ {
252
+ tab: "raw-events",
253
+ label: "Raw AG-UI Events",
254
+ title: "Drop into the protocol payloads",
255
+ body: "Raw events show the exact AG-UI stream behind the timeline when you need to verify ordering or payload shape."
256
+ },
257
+ {
258
+ tab: "state",
259
+ label: "State",
260
+ title: "Check the durable state",
261
+ body: "The state tab shows the saved values that make a thread resumable across sessions."
262
+ }
263
+ ];
63
264
  function escapeHtml(s) {
64
265
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
65
266
  }
@@ -275,6 +476,11 @@ var CpkThreadList = class extends LitElement {
275
476
  color: #57575b;
276
477
  }
277
478
 
479
+ .cpk-tl__pill--example {
480
+ background: rgba(133, 236, 206, 0.22);
481
+ color: #189370;
482
+ }
483
+
278
484
  /* ── Empty state ── */
279
485
  .cpk-tl__empty {
280
486
  padding: 32px 16px;
@@ -348,6 +554,9 @@ var CpkThreadList = class extends LitElement {
348
554
  </div>
349
555
  <div class="cpk-tl__meta">
350
556
  <span class="cpk-tl__pill">${thread.agentId}</span>
557
+ ${thread.isExample ? html`
558
+ <span class="cpk-tl__pill cpk-tl__pill--example">Example</span>
559
+ ` : nothing}
351
560
  </div>
352
561
  </div>
353
562
  `)}
@@ -571,6 +780,9 @@ var CpkThreadInspector = class CpkThreadInspector extends LitElement {
571
780
  }
572
781
  this.maybeFetchTabData(id);
573
782
  }
783
+ selectTab(id) {
784
+ this.activateTab(id);
785
+ }
574
786
  maybeFetchTabData(id) {
575
787
  if (!this.threadId) return;
576
788
  if ((id === "timeline" || id === "raw-events") && !this._eventsFetched) {
@@ -3051,6 +3263,18 @@ var WebInspectorElement = class extends LitElement {
3051
3263
  this.pendingBannerViewed = null;
3052
3264
  this.clickedBannerIds = /* @__PURE__ */ new Set();
3053
3265
  this.viewedThreadsTelemetryStates = /* @__PURE__ */ new Set();
3266
+ this.viewedExampleThreadIds = /* @__PURE__ */ new Set();
3267
+ this.selectedExampleThreadIds = /* @__PURE__ */ new Set();
3268
+ this.viewedExampleTourSteps = /* @__PURE__ */ new Set();
3269
+ this.exampleThreadProviders = /* @__PURE__ */ new Map();
3270
+ this.exampleTourDismissed = false;
3271
+ this.exampleTourActive = false;
3272
+ this.exampleTourStep = 0;
3273
+ this.exampleTourAutoShown = false;
3274
+ this.threadsExampleOverviewVideoShouldLoad = false;
3275
+ this.threadsExampleOverviewVideoReady = false;
3276
+ this.threadsExampleOverviewVideoLoadTimer = null;
3277
+ this.threadsExampleOverviewVideoIdleCallbackId = null;
3054
3278
  this.contextState = {
3055
3279
  button: {
3056
3280
  position: {
@@ -3297,6 +3521,10 @@ var WebInspectorElement = class extends LitElement {
3297
3521
  if (target.hasPointerCapture(this.threadDividerPointerId)) target.releasePointerCapture(this.threadDividerPointerId);
3298
3522
  this.threadDividerResizing = false;
3299
3523
  };
3524
+ this.handleThreadsExampleOverviewVideoLoaded = () => {
3525
+ this.threadsExampleOverviewVideoReady = true;
3526
+ this.requestUpdate();
3527
+ };
3300
3528
  this.handleClearEvents = () => {
3301
3529
  if (this.selectedContext === "all-agents") {
3302
3530
  this.agentEvents.clear();
@@ -3417,6 +3645,16 @@ var WebInspectorElement = class extends LitElement {
3417
3645
  areThreadEndpointsAvailable() {
3418
3646
  return this.getThreadServiceStatus() !== "unavailable";
3419
3647
  }
3648
+ getActiveThreadsState() {
3649
+ const displayThreads = this.selectedContext === "all-agents" ? this._threads : this._threadsByAgent.get(this.selectedContext) ?? [];
3650
+ let threadsErrorMessage = null;
3651
+ if (this.selectedContext === "all-agents") threadsErrorMessage = this._threadsErrorByAgent.values().next().value?.message ?? null;
3652
+ else threadsErrorMessage = this._threadsErrorByAgent.get(this.selectedContext)?.message ?? null;
3653
+ return {
3654
+ displayThreads,
3655
+ threadsErrorMessage
3656
+ };
3657
+ }
3420
3658
  getThreadsTelemetryProps(extra = {}, options = {}) {
3421
3659
  const distinctId = options.includeUrlAttribution && !this.core?.telemetryDisabled ? getTelemetryDistinctIdForUrl() : null;
3422
3660
  const threadServiceStatus = this.getThreadServiceStatus();
@@ -3441,9 +3679,21 @@ var WebInspectorElement = class extends LitElement {
3441
3679
  getIntelligenceSignupUrl() {
3442
3680
  return this.appendRefParam(INTELLIGENCE_SIGNUP_URL, "cpk-inspector");
3443
3681
  }
3682
+ getThreadsIntelligenceSignupUrl() {
3683
+ return this.appendRefParam(THREADS_INTELLIGENCE_SIGNIN_URL, "cpk-inspector");
3684
+ }
3444
3685
  getTalkToEngineerUrl() {
3445
3686
  return this.appendRefParam(TALK_TO_ENGINEER_URL, "cpk-inspector-threads");
3446
3687
  }
3688
+ getThreadsTalkToEngineerUrl() {
3689
+ return this.appendRefParam(TALK_TO_ENGINEER_URL, "cpk-inspector-threads");
3690
+ }
3691
+ getThreadsDocsUrl() {
3692
+ return this.appendRefParam(THREADS_DOCS_URL, "cpk-inspector-threads");
3693
+ }
3694
+ getSelfHostedIntelligenceUrl() {
3695
+ return this.appendRefParam(SELF_HOSTED_INTELLIGENCE_URL, "cpk-inspector-threads");
3696
+ }
3447
3697
  subscribeToThreadStore(agentId, store) {
3448
3698
  if (!this.areThreadEndpointsAvailable()) return;
3449
3699
  if (this._threadStoreSubscriptions.has(agentId)) return;
@@ -3472,7 +3722,7 @@ var WebInspectorElement = class extends LitElement {
3472
3722
  }
3473
3723
  autoSelectLatestThread() {
3474
3724
  if (this._threads.length === 0) return;
3475
- if (!(this.selectedThreadId != null && this._threads.some((t) => t.id === this.selectedThreadId))) this.selectedThreadId = this._threads[0].id;
3725
+ if (!(this.selectedThreadId != null && !this.isExampleThreadId(this.selectedThreadId) && this._threads.some((t) => t.id === this.selectedThreadId))) this.selectedThreadId = this._threads[0].id;
3476
3726
  }
3477
3727
  teardownThreadStoreSubscriptions() {
3478
3728
  for (const unsub of this._threadStoreSubscriptions.values()) unsub();
@@ -4667,6 +4917,35 @@ ${argsString}</pre
4667
4917
  .cpk-tab-active {
4668
4918
  cursor: pointer;
4669
4919
  }
4920
+ .cpk-threads-overview-video-frame {
4921
+ position: relative;
4922
+ display: block;
4923
+ width: 100%;
4924
+ max-width: 440px;
4925
+ aspect-ratio: 16 / 9;
4926
+ margin: 0 0 14px;
4927
+ overflow: hidden;
4928
+ border: 1px solid #dbdbe5;
4929
+ border-radius: 8px;
4930
+ background:
4931
+ linear-gradient(
4932
+ 135deg,
4933
+ rgba(190, 194, 255, 0.18),
4934
+ rgba(133, 236, 206, 0.12)
4935
+ ),
4936
+ #ffffff;
4937
+ box-shadow: 0 8px 20px rgba(1, 5, 7, 0.08);
4938
+ }
4939
+ .cpk-threads-overview-video {
4940
+ width: 100%;
4941
+ height: 100%;
4942
+ object-fit: cover;
4943
+ opacity: 0;
4944
+ transition: opacity 220ms ease;
4945
+ }
4946
+ .cpk-threads-overview-video[data-ready="true"] {
4947
+ opacity: 1;
4948
+ }
4670
4949
 
4671
4950
  /* ── Header control buttons (dock, close) — first row only ───── */
4672
4951
  .drag-handle > div:first-child button {
@@ -4843,6 +5122,7 @@ ${argsString}</pre
4843
5122
  window.addEventListener("resize", this.handleResize);
4844
5123
  window.addEventListener("pointerdown", this.handleGlobalPointerDown);
4845
5124
  this.hydrateStateFromStorageEarly();
5125
+ this.exampleTourDismissed = this.readThreadsExampleTourDismissed();
4846
5126
  this.tryAutoAttachCore();
4847
5127
  this.ensureAnnouncementLoading();
4848
5128
  }
@@ -4868,6 +5148,14 @@ ${argsString}</pre
4868
5148
  clearTimeout(this.transitionTimeoutId);
4869
5149
  this.transitionTimeoutId = null;
4870
5150
  }
5151
+ if (this.threadsExampleOverviewVideoLoadTimer !== null) {
5152
+ window.clearTimeout(this.threadsExampleOverviewVideoLoadTimer);
5153
+ this.threadsExampleOverviewVideoLoadTimer = null;
5154
+ }
5155
+ if (this.threadsExampleOverviewVideoIdleCallbackId !== null && typeof window.cancelIdleCallback === "function") {
5156
+ window.cancelIdleCallback(this.threadsExampleOverviewVideoIdleCallbackId);
5157
+ this.threadsExampleOverviewVideoIdleCallbackId = null;
5158
+ }
4871
5159
  this.removeDockStyles(true);
4872
5160
  this.detachFromCore();
4873
5161
  }
@@ -5064,6 +5352,17 @@ ${argsString}</pre
5064
5352
  </button>
5065
5353
  `;
5066
5354
  })}
5355
+ ${this.selectedMenu === "threads" ? html`
5356
+ <a
5357
+ href=${this.getThreadsTalkToEngineerUrl()}
5358
+ target="_blank"
5359
+ rel="noopener"
5360
+ style="margin-left:auto;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;"
5361
+ @click=${this.handleTalkToEngineerClick}
5362
+ >
5363
+ Talk to an Engineer
5364
+ </a>
5365
+ ` : nothing}
5067
5366
  </div>
5068
5367
  </div>
5069
5368
  <div class="flex flex-1 flex-col overflow-hidden">
@@ -5649,6 +5948,372 @@ ${argsString}</pre
5649
5948
  else if (state === "empty_enabled") trackThreadsEmptyEnabledViewed(props);
5650
5949
  else trackThreadsEnabledViewed(props);
5651
5950
  }
5951
+ shouldRenderExampleThreads(displayThreads, threadsErrorMessage) {
5952
+ return this.areThreadEndpointsAvailable() && !threadsErrorMessage && displayThreads.length === 0;
5953
+ }
5954
+ getVisibleThreads(displayThreads, threadsErrorMessage) {
5955
+ return this.shouldRenderExampleThreads(displayThreads, threadsErrorMessage) ? THREADS_EXAMPLE_THREADS : displayThreads;
5956
+ }
5957
+ isExampleThreadId(threadId) {
5958
+ return THREADS_EXAMPLE_THREADS.some((thread) => thread.id === threadId);
5959
+ }
5960
+ getExampleThreadProvider(threadId) {
5961
+ const cached = this.exampleThreadProviders.get(threadId);
5962
+ if (cached) return cached;
5963
+ const thread = THREADS_EXAMPLE_THREADS.find((item) => item.id === threadId);
5964
+ const details = THREADS_EXAMPLE_DETAILS[threadId];
5965
+ const provider = {
5966
+ getThreadMetadata: async () => thread ? {
5967
+ id: thread.id,
5968
+ name: thread.name,
5969
+ agentId: thread.agentId,
5970
+ endUserId: "example-user",
5971
+ status: "completed",
5972
+ updatedAt: thread.updatedAt
5973
+ } : null,
5974
+ getMessages: async () => details?.messages ?? [],
5975
+ getEvents: async () => details?.events ?? [],
5976
+ getState: async () => details?.state ?? null
5977
+ };
5978
+ this.exampleThreadProviders.set(threadId, provider);
5979
+ return provider;
5980
+ }
5981
+ trackThreadsExampleViewedOnce() {
5982
+ if (this.core?.telemetryDisabled) return;
5983
+ for (const thread of THREADS_EXAMPLE_THREADS) {
5984
+ if (this.viewedExampleThreadIds.has(thread.id)) continue;
5985
+ this.viewedExampleThreadIds.add(thread.id);
5986
+ trackThreadsExampleViewed(this.getThreadsTelemetryProps({
5987
+ example_thread_id: thread.id,
5988
+ thread_count: 0
5989
+ }));
5990
+ }
5991
+ }
5992
+ trackThreadsExampleSelectedOnce(threadId) {
5993
+ if (this.core?.telemetryDisabled) return;
5994
+ if (this.selectedExampleThreadIds.has(threadId)) return;
5995
+ this.selectedExampleThreadIds.add(threadId);
5996
+ trackThreadsExampleSelected(this.getThreadsTelemetryProps({
5997
+ example_thread_id: threadId,
5998
+ thread_count: 0
5999
+ }));
6000
+ }
6001
+ getCurrentExampleTourProps() {
6002
+ const step = THREADS_EXAMPLE_TOUR_STEPS[this.exampleTourStep] ?? THREADS_EXAMPLE_TOUR_STEPS[0];
6003
+ return this.getThreadsTelemetryProps({
6004
+ example_thread_id: this.selectedThreadId ?? void 0,
6005
+ thread_count: 0,
6006
+ tour_step: this.exampleTourStep + 1,
6007
+ tour_tab: step?.tab
6008
+ });
6009
+ }
6010
+ trackThreadsExampleTourStepViewedOnce() {
6011
+ if (this.core?.telemetryDisabled || !this.selectedThreadId) return;
6012
+ if (!(THREADS_EXAMPLE_TOUR_STEPS[this.exampleTourStep] ?? THREADS_EXAMPLE_TOUR_STEPS[0])) return;
6013
+ const key = `${this.selectedThreadId}:${this.exampleTourStep}`;
6014
+ if (this.viewedExampleTourSteps.has(key)) return;
6015
+ this.viewedExampleTourSteps.add(key);
6016
+ trackThreadsExampleTourStepViewed(this.getCurrentExampleTourProps());
6017
+ }
6018
+ syncExampleTourTab() {
6019
+ const step = THREADS_EXAMPLE_TOUR_STEPS[this.exampleTourStep] ?? THREADS_EXAMPLE_TOUR_STEPS[0];
6020
+ if (!step) return;
6021
+ this.updateComplete.then(() => {
6022
+ (this.shadowRoot?.querySelector("cpk-thread-details"))?.selectTab?.(step.tab);
6023
+ });
6024
+ }
6025
+ startExampleTour(autoStarted) {
6026
+ if (!this.selectedThreadId) return;
6027
+ this.exampleTourActive = true;
6028
+ this.exampleTourStep = 0;
6029
+ if (autoStarted) {
6030
+ this.exampleTourAutoShown = true;
6031
+ if (!this.core?.telemetryDisabled) trackThreadsExampleTourStarted(this.getCurrentExampleTourProps());
6032
+ } else if (!this.core?.telemetryDisabled) trackThreadsExampleTourReopened(this.getCurrentExampleTourProps());
6033
+ this.trackThreadsExampleTourStepViewedOnce();
6034
+ this.syncExampleTourTab();
6035
+ this.requestUpdate();
6036
+ }
6037
+ setExampleTourStep(nextStep) {
6038
+ this.exampleTourStep = Math.max(0, Math.min(THREADS_EXAMPLE_TOUR_STEPS.length - 1, nextStep));
6039
+ this.trackThreadsExampleTourStepViewedOnce();
6040
+ this.syncExampleTourTab();
6041
+ this.requestUpdate();
6042
+ }
6043
+ dismissExampleTour(method) {
6044
+ if (!this.selectedThreadId) return;
6045
+ const props = {
6046
+ ...this.getCurrentExampleTourProps(),
6047
+ dismiss_method: method
6048
+ };
6049
+ this.exampleTourActive = false;
6050
+ this.exampleTourDismissed = true;
6051
+ this.writeThreadsExampleTourDismissed();
6052
+ if (!this.core?.telemetryDisabled) if (method === "done") trackThreadsExampleTourCompleted(props);
6053
+ else trackThreadsExampleTourDismissed(props);
6054
+ this.requestUpdate();
6055
+ }
6056
+ handleThreadsThreadSelected(threadId, showingExamples) {
6057
+ if (showingExamples && this.selectedThreadId === threadId) {
6058
+ this.selectedThreadId = null;
6059
+ this.exampleTourActive = false;
6060
+ this.requestUpdate();
6061
+ return;
6062
+ }
6063
+ this.selectedThreadId = threadId;
6064
+ if (showingExamples && this.isExampleThreadId(threadId)) {
6065
+ this.trackThreadsExampleSelectedOnce(threadId);
6066
+ if (!this.exampleTourDismissed && !this.exampleTourAutoShown) this.startExampleTour(true);
6067
+ else this.exampleTourActive = false;
6068
+ } else this.exampleTourActive = false;
6069
+ this.requestUpdate();
6070
+ }
6071
+ readThreadsExampleTourDismissed() {
6072
+ if (typeof window === "undefined") return false;
6073
+ try {
6074
+ const raw = window.localStorage.getItem(THREADS_EXAMPLE_TOUR_STORAGE_KEY);
6075
+ if (!raw) return false;
6076
+ return JSON.parse(raw).dismissed === true;
6077
+ } catch {
6078
+ return false;
6079
+ }
6080
+ }
6081
+ writeThreadsExampleTourDismissed() {
6082
+ if (typeof window === "undefined") return;
6083
+ try {
6084
+ window.localStorage.setItem(THREADS_EXAMPLE_TOUR_STORAGE_KEY, JSON.stringify({ dismissed: true }));
6085
+ } catch {}
6086
+ }
6087
+ scheduleThreadsExampleOverviewVideoLoad() {
6088
+ if (this.threadsExampleOverviewVideoShouldLoad || this.threadsExampleOverviewVideoLoadTimer !== null || this.threadsExampleOverviewVideoIdleCallbackId !== null || typeof window === "undefined") return;
6089
+ if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) return;
6090
+ const loadVideo = () => {
6091
+ this.threadsExampleOverviewVideoLoadTimer = null;
6092
+ this.threadsExampleOverviewVideoIdleCallbackId = null;
6093
+ this.threadsExampleOverviewVideoShouldLoad = true;
6094
+ this.requestUpdate();
6095
+ };
6096
+ if (typeof window.requestIdleCallback === "function") {
6097
+ this.threadsExampleOverviewVideoIdleCallbackId = window.requestIdleCallback(loadVideo, { timeout: 1200 });
6098
+ return;
6099
+ }
6100
+ this.threadsExampleOverviewVideoLoadTimer = window.setTimeout(loadVideo, 450);
6101
+ }
6102
+ renderThreadsExampleOverviewVideo() {
6103
+ this.scheduleThreadsExampleOverviewVideoLoad();
6104
+ return html`
6105
+ <div class="cpk-threads-overview-video-frame" aria-hidden="true">
6106
+ ${this.threadsExampleOverviewVideoShouldLoad ? html`
6107
+ <video
6108
+ class="cpk-threads-overview-video"
6109
+ data-ready=${this.threadsExampleOverviewVideoReady}
6110
+ src=${THREADS_EXAMPLE_OVERVIEW_VIDEO_URL}
6111
+ autoplay
6112
+ loop
6113
+ muted
6114
+ playsinline
6115
+ preload="metadata"
6116
+ @loadeddata=${this.handleThreadsExampleOverviewVideoLoaded}
6117
+ ></video>
6118
+ ` : nothing}
6119
+ </div>
6120
+ `;
6121
+ }
6122
+ renderThreadsExampleOverview() {
6123
+ return html`
6124
+ <div
6125
+ style="
6126
+ flex: 1;
6127
+ min-width: 0;
6128
+ display: flex;
6129
+ align-items: center;
6130
+ justify-content: center;
6131
+ padding: 32px;
6132
+ background: #f7f7f9;
6133
+ "
6134
+ >
6135
+ <div style="max-width: 440px; color: #57575b;">
6136
+ <h2
6137
+ style="
6138
+ margin: 0 0 10px;
6139
+ font-size: 20px;
6140
+ line-height: 1.25;
6141
+ font-weight: 600;
6142
+ color: #010507;
6143
+ "
6144
+ >
6145
+ Threads are persistent, inspectable conversations
6146
+ </h2>
6147
+ ${this.renderThreadsExampleOverviewVideo()}
6148
+ <p
6149
+ style="
6150
+ margin: 0 0 16px;
6151
+ font-size: 13px;
6152
+ line-height: 1.55;
6153
+ color: #57575b;
6154
+ "
6155
+ >
6156
+ Take a tour with the example threads in the sidebar. Then, start
6157
+ chatting in your app to create the first real thread.
6158
+ </p>
6159
+ <div style="display:flex;flex-wrap:wrap;gap:8px;">
6160
+ <a
6161
+ href=${this.getThreadsDocsUrl()}
6162
+ target="_blank"
6163
+ rel="noopener"
6164
+ style="
6165
+ display: inline-flex;
6166
+ align-items: center;
6167
+ justify-content: center;
6168
+ gap: 6px;
6169
+ min-height: 34px;
6170
+ border-radius: 6px;
6171
+ background: #010507;
6172
+ padding: 8px 12px;
6173
+ font-size: 12px;
6174
+ font-weight: 600;
6175
+ color: #ffffff;
6176
+ text-decoration: none;
6177
+ "
6178
+ >
6179
+ Learn how Threads work
6180
+ </a>
6181
+ <a
6182
+ href=${this.getSelfHostedIntelligenceUrl()}
6183
+ target="_blank"
6184
+ rel="noopener"
6185
+ style="
6186
+ display: inline-flex;
6187
+ align-items: center;
6188
+ justify-content: center;
6189
+ gap: 6px;
6190
+ min-height: 34px;
6191
+ border-radius: 6px;
6192
+ border: 1px solid #dbdbe5;
6193
+ background: #ffffff;
6194
+ padding: 8px 12px;
6195
+ font-size: 12px;
6196
+ font-weight: 600;
6197
+ color: #010507;
6198
+ text-decoration: none;
6199
+ "
6200
+ >
6201
+ Explore self-hosted Intelligence
6202
+ </a>
6203
+ </div>
6204
+ </div>
6205
+ </div>
6206
+ `;
6207
+ }
6208
+ renderThreadsExampleTour() {
6209
+ if (!this.selectedThreadId || !this.isExampleThreadId(this.selectedThreadId)) return nothing;
6210
+ if (!this.exampleTourActive) return html`
6211
+ <button
6212
+ type="button"
6213
+ style="
6214
+ position: absolute;
6215
+ right: 16px;
6216
+ bottom: 16px;
6217
+ z-index: 2;
6218
+ border: 1px solid #dbdbe5;
6219
+ border-radius: 6px;
6220
+ background: #ffffff;
6221
+ padding: 7px 10px;
6222
+ color: #57575b;
6223
+ font-size: 12px;
6224
+ font-weight: 600;
6225
+ cursor: pointer;
6226
+ box-shadow: 0 8px 18px rgba(1, 5, 7, 0.08);
6227
+ "
6228
+ @click=${() => this.startExampleTour(false)}
6229
+ >
6230
+ Show tour
6231
+ </button>
6232
+ `;
6233
+ const step = THREADS_EXAMPLE_TOUR_STEPS[this.exampleTourStep] ?? THREADS_EXAMPLE_TOUR_STEPS[0];
6234
+ const isFirst = this.exampleTourStep === 0;
6235
+ const isLast = this.exampleTourStep === THREADS_EXAMPLE_TOUR_STEPS.length - 1;
6236
+ return html`
6237
+ <div
6238
+ role="dialog"
6239
+ aria-label="Example thread tour"
6240
+ style="
6241
+ position: absolute;
6242
+ right: 16px;
6243
+ bottom: 16px;
6244
+ z-index: 3;
6245
+ width: min(340px, calc(100% - 32px));
6246
+ border: 1px solid #dbdbe5;
6247
+ border-radius: 8px;
6248
+ background: #ffffff;
6249
+ padding: 14px;
6250
+ box-shadow: 0 16px 36px rgba(1, 5, 7, 0.14);
6251
+ color: #57575b;
6252
+ "
6253
+ >
6254
+ <div
6255
+ style="
6256
+ margin-bottom: 8px;
6257
+ font-family: 'Spline Sans Mono', monospace;
6258
+ font-size: 10px;
6259
+ font-weight: 600;
6260
+ color: #189370;
6261
+ text-transform: uppercase;
6262
+ "
6263
+ >
6264
+ ${this.exampleTourStep + 1}/${THREADS_EXAMPLE_TOUR_STEPS.length}
6265
+ ${step.label}
6266
+ </div>
6267
+ <div
6268
+ style="
6269
+ margin-bottom: 6px;
6270
+ font-size: 14px;
6271
+ line-height: 1.35;
6272
+ font-weight: 600;
6273
+ color: #010507;
6274
+ "
6275
+ >
6276
+ ${step.title}
6277
+ </div>
6278
+ <div style="font-size: 12px; line-height: 1.5; color: #57575b;">
6279
+ ${step.body}
6280
+ </div>
6281
+ <div
6282
+ style="
6283
+ display: flex;
6284
+ justify-content: space-between;
6285
+ gap: 8px;
6286
+ margin-top: 14px;
6287
+ "
6288
+ >
6289
+ <button
6290
+ type="button"
6291
+ style="border:0;background:transparent;color:#838389;font-size:12px;font-weight:600;cursor:pointer;padding:7px 0;"
6292
+ @click=${() => this.dismissExampleTour("skip")}
6293
+ >
6294
+ Skip
6295
+ </button>
6296
+ <div style="display:flex;gap:8px;">
6297
+ <button
6298
+ type="button"
6299
+ style="border:1px solid #dbdbe5;border-radius:6px;background:#ffffff;color:#57575b;font-size:12px;font-weight:600;cursor:pointer;padding:7px 10px;"
6300
+ ?disabled=${isFirst}
6301
+ @click=${() => this.setExampleTourStep(this.exampleTourStep - 1)}
6302
+ >
6303
+ Back
6304
+ </button>
6305
+ <button
6306
+ type="button"
6307
+ style="border:1px solid #010507;border-radius:6px;background:#010507;color:#ffffff;font-size:12px;font-weight:600;cursor:pointer;padding:7px 10px;"
6308
+ @click=${() => isLast ? this.dismissExampleTour("done") : this.setExampleTourStep(this.exampleTourStep + 1)}
6309
+ >
6310
+ ${isLast ? "Done" : "Next"}
6311
+ </button>
6312
+ </div>
6313
+ </div>
6314
+ </div>
6315
+ `;
6316
+ }
5652
6317
  renderThreadsLockedBackgroundMockup() {
5653
6318
  return html`
5654
6319
  <div
@@ -5894,29 +6559,7 @@ ${argsString}</pre
5894
6559
  "
5895
6560
  >
5896
6561
  <a
5897
- href=${this.getTalkToEngineerUrl()}
5898
- target="_blank"
5899
- rel="noopener"
5900
- style="
5901
- display: inline-flex;
5902
- min-height: 34px;
5903
- align-items: center;
5904
- justify-content: center;
5905
- gap: 6px;
5906
- border-radius: 6px;
5907
- background: #010507;
5908
- padding: 8px 12px;
5909
- font-size: 12px;
5910
- font-weight: 600;
5911
- color: #ffffff;
5912
- text-decoration: none;
5913
- "
5914
- @click=${this.handleThreadsTalkToEngineerClick}
5915
- >
5916
- Talk to an Engineer
5917
- </a>
5918
- <a
5919
- href=${this.getIntelligenceSignupUrl()}
6562
+ href=${this.getThreadsIntelligenceSignupUrl()}
5920
6563
  target="_blank"
5921
6564
  rel="noopener"
5922
6565
  style="
@@ -6249,27 +6892,15 @@ ${argsString}</pre
6249
6892
  }
6250
6893
  renderThreadsView() {
6251
6894
  if (!this.areThreadEndpointsAvailable()) return this.renderThreadsLockedView();
6252
- const displayThreads = this.selectedContext === "all-agents" ? this._threads : this._threadsByAgent.get(this.selectedContext) ?? [];
6253
- let threadsErrorMessage = null;
6254
- if (this.selectedContext === "all-agents") threadsErrorMessage = this._threadsErrorByAgent.values().next().value?.message ?? null;
6255
- else threadsErrorMessage = this._threadsErrorByAgent.get(this.selectedContext)?.message ?? null;
6256
- const selectedThread = this.selectedThreadId != null ? displayThreads.find((t) => t.id === this.selectedThreadId) ?? null : null;
6895
+ const { displayThreads, threadsErrorMessage } = this.getActiveThreadsState();
6896
+ const showingExamples = this.shouldRenderExampleThreads(displayThreads, threadsErrorMessage);
6897
+ const visibleThreads = this.getVisibleThreads(displayThreads, threadsErrorMessage);
6898
+ if (showingExamples) this.trackThreadsExampleViewedOnce();
6899
+ const selectedThread = this.selectedThreadId != null ? visibleThreads.find((t) => t.id === this.selectedThreadId) ?? null : null;
6900
+ const selectedThreadIsExample = this.isExampleThreadId(selectedThread?.id);
6257
6901
  if (!threadsErrorMessage) this.trackThreadsViewStateOnce(displayThreads.length === 0 ? "empty_enabled" : "enabled", displayThreads.length);
6258
6902
  return html`
6259
6903
  <div style="display:flex;height:100%;overflow:hidden;flex-direction:column;">
6260
- <div
6261
- style="display:flex;align-items:center;justify-content:flex-end;border-bottom:1px solid #DBDBE5;background:#ffffff;padding:8px 12px;flex-shrink:0;"
6262
- >
6263
- <a
6264
- href=${this.getTalkToEngineerUrl()}
6265
- target="_blank"
6266
- rel="noopener"
6267
- 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;"
6268
- @click=${this.handleTalkToEngineerClick}
6269
- >
6270
- Talk to an Engineer
6271
- </a>
6272
- </div>
6273
6904
  <div style="display:flex;min-height:0;flex:1;overflow:hidden;">
6274
6905
  <!-- Left sidebar: thread list -->
6275
6906
  <div
@@ -6277,12 +6908,11 @@ ${argsString}</pre
6277
6908
  >
6278
6909
  <cpk-thread-list
6279
6910
  style="height:100%;"
6280
- .threads=${displayThreads}
6911
+ .threads=${visibleThreads}
6281
6912
  .selectedThreadId=${this.selectedThreadId}
6282
6913
  .errorMessage=${threadsErrorMessage}
6283
6914
  @threadSelected=${(e) => {
6284
- this.selectedThreadId = e.detail;
6285
- this.requestUpdate();
6915
+ this.handleThreadsThreadSelected(e.detail, showingExamples);
6286
6916
  }}
6287
6917
  ></cpk-thread-list>
6288
6918
  </div>
@@ -6297,18 +6927,20 @@ ${argsString}</pre
6297
6927
  ></div>
6298
6928
 
6299
6929
  <!-- Center + right: thread details or empty state -->
6300
- <div style="flex:1;min-width:0;overflow:hidden;display:flex;">
6930
+ <div style="flex:1;min-width:0;overflow:hidden;display:flex;position:relative;">
6301
6931
  ${selectedThread ? html`<cpk-thread-details
6302
6932
  style="flex:1;min-width:0;"
6303
6933
  .threadId=${selectedThread.id}
6304
6934
  .thread=${selectedThread}
6935
+ .provider=${selectedThreadIsExample ? this.getExampleThreadProvider(selectedThread.id) : null}
6305
6936
  .runtimeUrl=${this._core?.runtimeUrl ?? ""}
6306
6937
  .headers=${this._core?.headers ?? {}}
6307
- .threadInspectionAvailable=${this._core?.threadEndpoints?.inspect !== false}
6938
+ .threadInspectionAvailable=${selectedThreadIsExample || this._core?.threadEndpoints?.inspect !== false}
6308
6939
  .liveMessageVersion=${this.liveMessageVersion.get(selectedThread.id) ?? 0}
6309
6940
  .agentStateInput=${this.getLatestStateForAgent(selectedThread.agentId)}
6310
6941
  .agentEventsInput=${this.agentEvents.get(selectedThread.agentId) ?? []}
6311
- ></cpk-thread-details>` : html`
6942
+ ></cpk-thread-details>
6943
+ ${selectedThreadIsExample ? this.renderThreadsExampleTour() : nothing}` : showingExamples ? this.renderThreadsExampleOverview() : html`
6312
6944
  <div
6313
6945
  style="
6314
6946
  flex: 1;