@copilotkit/web-inspector 1.73.2 → 1.74.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.
Files changed (47) hide show
  1. package/dist/index.cjs +363 -271
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +29 -6
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.mts +29 -6
  6. package/dist/index.d.mts.map +1 -1
  7. package/dist/index.mjs +364 -272
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/index.umd.js +1274 -628
  10. package/dist/index.umd.js.map +1 -1
  11. package/dist/lib/notification-loader.cjs +38 -0
  12. package/dist/lib/notification-loader.cjs.map +1 -0
  13. package/dist/lib/notification-loader.mjs +38 -0
  14. package/dist/lib/notification-loader.mjs.map +1 -0
  15. package/dist/lib/notifications.cjs +173 -0
  16. package/dist/lib/notifications.cjs.map +1 -0
  17. package/dist/lib/notifications.d.cts +17 -0
  18. package/dist/lib/notifications.d.cts.map +1 -0
  19. package/dist/lib/notifications.d.mts +17 -0
  20. package/dist/lib/notifications.d.mts.map +1 -0
  21. package/dist/lib/notifications.mjs +168 -0
  22. package/dist/lib/notifications.mjs.map +1 -0
  23. package/dist/lib/onboarding-prompt.cjs +11 -17
  24. package/dist/lib/onboarding-prompt.cjs.map +1 -1
  25. package/dist/lib/onboarding-prompt.mjs +11 -17
  26. package/dist/lib/onboarding-prompt.mjs.map +1 -1
  27. package/dist/lib/persistence.cjs +124 -48
  28. package/dist/lib/persistence.cjs.map +1 -1
  29. package/dist/lib/persistence.mjs +121 -45
  30. package/dist/lib/persistence.mjs.map +1 -1
  31. package/dist/lib/telemetry.cjs +42 -0
  32. package/dist/lib/telemetry.cjs.map +1 -1
  33. package/dist/lib/telemetry.d.cts.map +1 -1
  34. package/dist/lib/telemetry.d.mts.map +1 -1
  35. package/dist/lib/telemetry.mjs +35 -1
  36. package/dist/lib/telemetry.mjs.map +1 -1
  37. package/dist/package.cjs +1 -1
  38. package/dist/package.mjs +1 -1
  39. package/dist/styles/generated.cjs +1 -1
  40. package/dist/styles/generated.cjs.map +1 -1
  41. package/dist/styles/generated.mjs +1 -1
  42. package/dist/styles/generated.mjs.map +1 -1
  43. package/dist/styles/notification-article.cjs +244 -0
  44. package/dist/styles/notification-article.cjs.map +1 -0
  45. package/dist/styles/notification-article.mjs +244 -0
  46. package/dist/styles/notification-article.mjs.map +1 -0
  47. package/package.json +4 -3
package/dist/index.mjs CHANGED
@@ -1,9 +1,12 @@
1
+ import { acknowledgeNotification, compareNotifications, emptyNotificationState, reconcileNotifications } from "./lib/notifications.mjs";
2
+ import { loadNotificationFeed } from "./lib/notification-loader.mjs";
3
+ import { INSPECTOR_DISMISSAL_MAX_DURATION_MS, clearLegacyAnnouncementReadState, hasNotificationPulsed, isValidAnchor, isValidDockMode, isValidPosition, isValidSize, loadInspectorDismissedUntil, loadInspectorState, loadNotificationState, migrateAnnouncementReadState, saveInspectorDismissedUntil, saveInspectorState, saveNotificationPulsedId, saveNotificationState } from "./lib/persistence.mjs";
1
4
  import generated_default from "./styles/generated.mjs";
5
+ import { notificationArticleStyles } from "./styles/notification-article.mjs";
2
6
  import inspector_logo_default from "./assets/inspector-logo.mjs";
3
7
  import inspector_logo_kite_default from "./assets/inspector-logo-kite.mjs";
4
8
  import { deriveLearningViewState } from "./components/learning-view.mjs";
5
9
  import { applyAnchorPosition, centerContext, clampSize, constrainToViewport, keepPositionWithinViewport, updateAnchorFromPosition, updateSizeFromElement } from "./lib/context-helpers.mjs";
6
- import { INSPECTOR_DISMISSAL_MAX_DURATION_MS, clearLegacyAnnouncementReadState, isValidAnchor, isValidDockMode, isValidPosition, isValidSize, loadAnnouncementPulsedTimestamp, loadAnnouncementReadTimestamp, loadInspectorDismissedUntil, loadInspectorState, saveAnnouncementPulsedTimestamp, saveAnnouncementReadTimestamp, saveInspectorDismissedUntil, saveInspectorState } from "./lib/persistence.mjs";
7
10
  import { clearLearningSetupMarker, learningSetupMarkerMatches, readLearningSetupMarker, subscribeToLearningSetupMarker, writeLearningSetupMarker } from "./lib/learning-setup.mjs";
8
11
  import { InspectorLearningUnsupportedError, fetchInspectorLearning } from "./lib/inspector-learning.mjs";
9
12
  import { buildPopOutFeatures, ensureBrandFont, openPopOutWindow } from "./lib/pop-out.mjs";
@@ -11,7 +14,7 @@ import { projectInspectorMetadata } from "./lib/inspector-metadata.mjs";
11
14
  import { buildHomeModel, runtimeConnectionNeedsAttention } from "./lib/home-briefing.mjs";
12
15
  import { INSPECTOR_GROUPS, INSPECTOR_NAV_SECTIONS, getGroupForMenu, isInspectorMenuKey, shouldUseIconRail } from "./lib/inspector-nav.mjs";
13
16
  import { selectVisibleRealThreadId } from "./lib/thread-selection.mjs";
14
- import { TELEMETRY_DOCS_URL, ensureTelemetryDistinctId, getRuntimeUrlType, getTelemetryDistinctIdForUrl, learningCountBucket, learningDurationBucket, maybeShowDisclosure, trackErrorSignalViewed, trackHomeCtaClicked, trackHomeFeaturePromptClicked, trackHomePromptCopied, trackHomeStoryBeatSelected, trackHomeViewed, trackInspectorOpened, trackLearningEvidenceOpened, trackLearningPageChanged, trackLearningPaneViewed, trackLearningSetupPromptClicked, trackLearningSkillToggled, trackLearningSnapshotLoaded, trackLearningWebAppOpened, trackMemoriesTabClicked, trackMetadataActionClicked, trackMetadataModuleViewed, trackTalkToEngineerClicked, trackThreadsEmptyEnabledViewed, trackThreadsEnabledViewed, trackThreadsExampleSelected, trackThreadsExampleTourCompleted, trackThreadsExampleTourDismissed, trackThreadsExampleTourReopened, trackThreadsExampleTourStarted, trackThreadsExampleTourStepViewed, trackThreadsExampleViewed, trackThreadsIntelligenceSignupClicked, trackThreadsLockedViewed, trackThreadsTabClicked, trackThreadsTalkToEngineerClicked, trackThreadsTryFromHereClicked, trackWhatsNewClicked, trackWhatsNewSignalViewed, trackWhatsNewViewed } from "./lib/telemetry.mjs";
17
+ import { TELEMETRY_DOCS_URL, ensureTelemetryDistinctId, getRuntimeUrlType, getTelemetryDistinctIdForUrl, learningCountBucket, learningDurationBucket, maybeShowDisclosure, trackErrorSignalViewed, trackHomeCtaClicked, trackHomeFeaturePromptClicked, trackHomePromptCopied, trackHomeStoryBeatSelected, trackHomeViewed, trackHudFeatureClicked, trackHudFeatureToggleClicked, trackHudFeatureToggleViewed, trackHudHideClicked, trackHudHideViewed, trackHudNotificationClicked, trackHudNotificationViewed, trackHudViewed, trackInspectorOpened, trackLearningEvidenceOpened, trackLearningPageChanged, trackLearningPaneViewed, trackLearningSetupPromptClicked, trackLearningSkillToggled, trackLearningSnapshotLoaded, trackLearningWebAppOpened, trackMemoriesTabClicked, trackMetadataActionClicked, trackMetadataModuleViewed, trackTalkToEngineerClicked, trackThreadsEmptyEnabledViewed, trackThreadsEnabledViewed, trackThreadsExampleSelected, trackThreadsExampleTourCompleted, trackThreadsExampleTourDismissed, trackThreadsExampleTourReopened, trackThreadsExampleTourStarted, trackThreadsExampleTourStepViewed, trackThreadsExampleViewed, trackThreadsIntelligenceSignupClicked, trackThreadsLockedViewed, trackThreadsTabClicked, trackThreadsTalkToEngineerClicked, trackThreadsTryFromHereClicked, trackWhatsNewClicked, trackWhatsNewSignalViewed, trackWhatsNewViewed } from "./lib/telemetry.mjs";
15
18
  import { createFeatureOnboardingPrompt, createOnboardingPrompt, createOnboardingRunId } from "./lib/onboarding-prompt.mjs";
16
19
  import { LitElement, css, html, nothing, render, unsafeCSS } from "lit";
17
20
  import { marked } from "marked";
@@ -264,7 +267,6 @@ const MIN_WINDOW_WIDTH = 880;
264
267
  const MIN_WINDOW_WIDTH_DOCKED_LEFT = 640;
265
268
  const MIN_WINDOW_HEIGHT = 480;
266
269
  const INSPECTOR_STORAGE_KEY = "cpk:inspector:state";
267
- const ANNOUNCEMENT_URL = "https://cdn.copilotkit.ai/announcements.json";
268
270
  const LAUNCHER_MIN_SIZE = 51.84;
269
271
  const LAUNCHER_MAX_SIZE = 62.208;
270
272
  const DEFAULT_BUTTON_SIZE = {
@@ -1263,6 +1265,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
1263
1265
  this._loadingEvents = false;
1264
1266
  this._loadingState = false;
1265
1267
  this._messagesError = null;
1268
+ this._messageRefreshError = null;
1266
1269
  this._eventsError = null;
1267
1270
  this._stateError = null;
1268
1271
  this._expandedTools = /* @__PURE__ */ new Set();
@@ -1355,6 +1358,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
1355
1358
  _loadingEvents: { state: true },
1356
1359
  _loadingState: { state: true },
1357
1360
  _messagesError: { state: true },
1361
+ _messageRefreshError: { state: true },
1358
1362
  _eventsError: { state: true },
1359
1363
  _stateError: { state: true },
1360
1364
  _expandedTools: { state: true },
@@ -1431,7 +1435,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
1431
1435
  </div>`;
1432
1436
  }
1433
1437
  renderTabContent(id) {
1434
- if (id === "timeline") return html`${this._showEventTimeline ? html`<div class="cpk-td__timeline-toolbar">${this.renderTimelineBulkButtons()}</div>${this.renderTimeline()}` : this._conversation.length || this._loadingMessages || this._messagesError ? this.renderConversation() : this.renderTimeline()}`;
1438
+ if (id === "timeline") return html`${this._messageRefreshError ? html`<div class="cpk-td__status cpk-td__status--error" role="status">${this._messageRefreshError}</div>` : nothing}${this._showEventTimeline ? html`<div class="cpk-td__timeline-toolbar">${this.renderTimelineBulkButtons()}</div>${this.renderTimeline()}` : this._conversation.length || this._loadingMessages || this._messagesError ? this.renderConversation() : this.renderTimeline()}`;
1435
1439
  if (id === "state") return this.renderState();
1436
1440
  return this.renderEvents();
1437
1441
  }
@@ -1984,10 +1988,19 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
1984
1988
  background: #ffffff;
1985
1989
  color: #71717a;
1986
1990
  cursor: pointer;
1991
+ width: 100%;
1992
+ border: 0;
1993
+ font-family: inherit;
1994
+ text-align: left;
1987
1995
  font-size: 11px;
1988
1996
  user-select: none;
1989
1997
  }
1990
1998
 
1999
+ .cpk-td__tool-header:focus-visible {
2000
+ outline: 2px solid var(--cpk-primary-color, #7076b3);
2001
+ outline-offset: -2px;
2002
+ }
2003
+
1991
2004
  .cpk-td__tool-header:hover {
1992
2005
  background: #fafafa;
1993
2006
  }
@@ -2824,7 +2837,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
2824
2837
  }
2825
2838
  scrollToFocusedMessage() {
2826
2839
  if (!this.focusMessageId) return;
2827
- const message = Array.from(this.shadowRoot?.querySelectorAll("[data-message-id]") ?? []).find((candidate) => candidate.dataset.messageId === this.focusMessageId);
2840
+ const message = Array.from(this.shadowRoot?.querySelectorAll(`#${this.panelDomId(this._tab)}:not([hidden]) [data-message-id]`) ?? []).find((candidate) => candidate.dataset.messageId === this.focusMessageId);
2828
2841
  if (!message) return;
2829
2842
  message.scrollIntoView?.({ block: "center" });
2830
2843
  this._scrolledFocusRequestId = this.focusRequestId;
@@ -2892,6 +2905,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
2892
2905
  this._loadingEvents = false;
2893
2906
  this._loadingState = false;
2894
2907
  this._messagesError = null;
2908
+ this._messageRefreshError = null;
2895
2909
  this._eventsError = null;
2896
2910
  this._stateError = null;
2897
2911
  this._fetchedMetadata = null;
@@ -2910,7 +2924,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
2910
2924
  this._fetchedMetadata = metadata;
2911
2925
  } catch (err) {
2912
2926
  if (err instanceof Error && err.name === "AbortError") return;
2913
- if (this.threadId !== threadId) return;
2927
+ if (controller.signal.aborted || this.threadId !== threadId) return;
2914
2928
  this._fetchedMetadata = null;
2915
2929
  }
2916
2930
  }
@@ -2935,19 +2949,23 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
2935
2949
  if (!silent) {
2936
2950
  this._loadingMessages = true;
2937
2951
  this._messagesError = null;
2952
+ this._messageRefreshError = null;
2938
2953
  }
2939
2954
  try {
2940
2955
  const messages = this.provider?.getMessages ? await this.provider.getMessages(threadId, { signal: controller.signal }) : await this.fetchRuntimeMessages(threadId, controller.signal);
2941
2956
  if (controller.signal.aborted || this.threadId !== threadId) return;
2942
2957
  this._conversation = this.mapMessages(messages);
2958
+ this._messagesError = null;
2959
+ this._messageRefreshError = null;
2943
2960
  } catch (err) {
2961
+ if (controller.signal.aborted || this.threadId !== threadId) return;
2944
2962
  if (err instanceof Error && err.name === "AbortError") return;
2945
- if (!silent) {
2963
+ if (!silent || this._conversation.length === 0) {
2946
2964
  this._messagesError = err instanceof Error ? err.message : "Failed to load messages";
2947
2965
  this._conversation = [];
2948
- }
2966
+ } else this._messageRefreshError = "Could not refresh messages. Showing the last loaded conversation.";
2949
2967
  } finally {
2950
- if (!silent && !controller.signal.aborted) this._loadingMessages = false;
2968
+ if (!controller.signal.aborted && this.threadId === threadId) this._loadingMessages = false;
2951
2969
  }
2952
2970
  }
2953
2971
  async fetchEvents(threadId) {
@@ -2974,7 +2992,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
2974
2992
  this._fetchedEvents = this.mapApiEvents(result.events);
2975
2993
  } catch (err) {
2976
2994
  if (err instanceof Error && err.name === "AbortError") return;
2977
- if (this.threadId !== threadId) return;
2995
+ if (controller.signal.aborted || this.threadId !== threadId) return;
2978
2996
  this._eventsError = err instanceof Error ? err.message : "Failed to load events";
2979
2997
  this._fetchedEvents = [];
2980
2998
  } finally {
@@ -3005,7 +3023,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
3005
3023
  this._fetchedState = result.state ?? null;
3006
3024
  } catch (err) {
3007
3025
  if (err instanceof Error && err.name === "AbortError") return;
3008
- if (this.threadId !== threadId) return;
3026
+ if (controller.signal.aborted || this.threadId !== threadId) return;
3009
3027
  this._stateError = err instanceof Error ? err.message : "Failed to load state";
3010
3028
  this._fetchedState = null;
3011
3029
  } finally {
@@ -3082,6 +3100,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
3082
3100
  toolCallId: tc.id,
3083
3101
  arguments: args,
3084
3102
  result: null,
3103
+ hasResult: false,
3085
3104
  createdAt: ""
3086
3105
  };
3087
3106
  toolCallMap.set(tc.id, item);
@@ -3102,18 +3121,23 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
3102
3121
  });
3103
3122
  else if (msg.role === "tool" && msg.toolCallId) {
3104
3123
  const tc = toolCallMap.get(msg.toolCallId);
3105
- if (tc) try {
3106
- tc.result = this.parseToolCallContent(msg.content);
3107
- } catch (err) {
3108
- console.error("[CopilotKit Inspector] Failed to parse tool-call result content", {
3109
- toolCallId: msg.toolCallId,
3110
- raw: msg.content,
3111
- error: err
3112
- });
3113
- tc.result = {
3114
- __parseError: true,
3115
- __raw: msg.content ?? null
3116
- };
3124
+ if (tc) {
3125
+ tc.hasResult = true;
3126
+ tc.resultUnreadable = false;
3127
+ try {
3128
+ tc.result = this.parseToolCallContent(msg.content);
3129
+ } catch (err) {
3130
+ console.error("[CopilotKit Inspector] Failed to parse tool-call result content", {
3131
+ toolCallId: msg.toolCallId,
3132
+ raw: msg.content,
3133
+ error: err
3134
+ });
3135
+ tc.resultUnreadable = true;
3136
+ tc.result = {
3137
+ __parseError: true,
3138
+ __raw: msg.content ?? null
3139
+ };
3140
+ }
3117
3141
  }
3118
3142
  }
3119
3143
  return items;
@@ -3869,13 +3893,60 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
3869
3893
  <span>No messages yet</span>
3870
3894
  </div>
3871
3895
  `;
3896
+ const items = this.renderItems;
3897
+ const errors = this.conversationRunErrors(items);
3872
3898
  return this.cachedPanelTpl("timeline-fallback", [
3873
3899
  this._conversation,
3874
3900
  this._expandedTools,
3875
- this._expandedMessages
3901
+ this._expandedMessages,
3902
+ JSON.stringify(errors),
3903
+ this._expandedTimelineDetails
3876
3904
  ], () => {
3877
- return html`${this.renderItems.map((item) => this.renderRenderItem(item))}`;
3905
+ const errorsAfter = /* @__PURE__ */ new Map();
3906
+ for (const { after, item } of errors) {
3907
+ const group = errorsAfter.get(after) ?? [];
3908
+ group.push(item);
3909
+ errorsAfter.set(after, group);
3910
+ }
3911
+ const renderErrors = (after) => errorsAfter.get(after)?.map((item) => this.renderTimelineItem(item));
3912
+ return html`
3913
+ ${renderErrors(-1)}
3914
+ ${items.map((item, index) => html`${this.renderRenderItem(item)}${renderErrors(index)}`)}
3915
+ `;
3916
+ });
3917
+ }
3918
+ conversationRunErrors(items) {
3919
+ const positions = /* @__PURE__ */ new Map();
3920
+ items.forEach((item, index) => {
3921
+ positions.set(item.id, index);
3922
+ if (item.type === "tool_call_group") for (const tool of item.items) positions.set(tool.id, index);
3878
3923
  });
3924
+ const errors = [];
3925
+ let after = -1;
3926
+ let hasAnchor = false;
3927
+ for (const event of this.activeEvents) {
3928
+ const id = (event.type.startsWith("TOOL_CALL") ? [
3929
+ "toolCallId",
3930
+ "tool_call_id",
3931
+ "callId",
3932
+ "id"
3933
+ ] : event.type.startsWith("TEXT_MESSAGE") || event.type.startsWith("ACTIVITY") ? [
3934
+ "messageId",
3935
+ "message_id",
3936
+ "id"
3937
+ ] : []).map((key) => event.payload[key]).find((value) => typeof value === "string");
3938
+ const position = id == null ? void 0 : positions.get(id);
3939
+ if (position !== void 0) {
3940
+ after = Math.max(after, position);
3941
+ hasAnchor = true;
3942
+ }
3943
+ if (event.type === "RUN_ERROR" || event.type === "ERROR") for (const item of this.timelineItemsFromEvents([event])) errors.push({
3944
+ after,
3945
+ item
3946
+ });
3947
+ }
3948
+ if (!hasAnchor) for (const error of errors) error.after = items.length - 1;
3949
+ return errors;
3879
3950
  }
3880
3951
  /**
3881
3952
  * Memoize the rendered TemplateResult for `slot` keyed by tuple
@@ -3949,32 +4020,40 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
3949
4020
  const expanded = this._expandedTools.has(item.id);
3950
4021
  return html`
3951
4022
  <div class="cpk-td__tool-block">
3952
- <div
4023
+ <button
4024
+ type="button"
3953
4025
  class="cpk-td__tool-header"
4026
+ aria-expanded=${expanded}
3954
4027
  @click=${() => this.toggleToolExpand(item.id)}
3955
4028
  >
3956
- <svg width="10" height="10" viewBox="0 0 10 10" fill="none">
4029
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true">
3957
4030
  <path
3958
- d="M1 9C1 9 2 7 5 7C8 7 9 9 9 9M5 1C5 1 7 2.5 7 4.5C7 6.5 5 7 5 7C5 7 3 6.5 3 4.5C3 2.5 5 1 5 1Z"
4031
+ d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94z"
3959
4032
  stroke="currentColor"
3960
- stroke-width="1.2"
4033
+ stroke-width="2"
3961
4034
  stroke-linecap="round"
3962
4035
  stroke-linejoin="round"
3963
4036
  />
3964
4037
  </svg>
3965
4038
  <span class="cpk-td__tool-name">${item.toolName}</span>
3966
- ${item.result || Object.keys(item.arguments).length > 0 ? html`
3967
- <span class="cpk-td__tool-status">Complete</span>
3968
- ` : html`
3969
- <span class="cpk-td__tool-status cpk-td__tool-status--pending">Pending</span>
3970
- `}
4039
+ ${item.resultUnreadable ? html`
4040
+ <span class="cpk-td__tool-status cpk-td__tool-status--pending"
4041
+ >Result unreadable</span
4042
+ >
4043
+ ` : item.hasResult ? html`
4044
+ <span class="cpk-td__tool-status">Result received</span>
4045
+ ` : html`
4046
+ <span class="cpk-td__tool-status cpk-td__tool-status--pending"
4047
+ >No result recorded</span
4048
+ >
4049
+ `}
3971
4050
  <span class="cpk-td__tool-chevron">${expanded ? "▾" : "▸"}</span>
3972
- </div>
4051
+ </button>
3973
4052
  ${expanded ? html`
3974
4053
  <div class="cpk-td__tool-body">
3975
4054
  <div class="cpk-td__tool-section-label">Arguments</div>
3976
4055
  ${renderHighlightedJsonBlock(item.arguments)}
3977
- ${item.result ? html`
4056
+ ${item.hasResult ? html`
3978
4057
  <div
3979
4058
  class="cpk-td__tool-section-label"
3980
4059
  style="margin-top:8px"
@@ -5054,11 +5133,13 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
5054
5133
  150
5055
5134
  ];
5056
5135
  this._evtColResize = null;
5136
+ this.notificationContext = { development: false };
5137
+ this.notificationFeed = null;
5138
+ this.notificationState = emptyNotificationState();
5139
+ this.notificationDocuments = /* @__PURE__ */ new Map();
5140
+ this.selectedNotificationId = null;
5057
5141
  this.announcementHtml = null;
5058
- this.announcementMarkdown = null;
5059
- this.announcementTimestamp = null;
5060
- this.announcementPreviewText = null;
5061
- this.announcementCtaLabel = null;
5142
+ this.announcementId = null;
5062
5143
  this.announcementLoaded = false;
5063
5144
  this.announcementPromise = null;
5064
5145
  this.newsSignalArmed = false;
@@ -5095,6 +5176,10 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
5095
5176
  this.launcherHudIntro = false;
5096
5177
  this.launcherHudIntroStartTimer = null;
5097
5178
  this.launcherHudIntroEndTimer = null;
5179
+ this.viewedHudElement = null;
5180
+ this.launcherHudTrigger = "user";
5181
+ this.viewedHudParts = /* @__PURE__ */ new Set();
5182
+ this.pendingHudTelemetry = [];
5098
5183
  this.inspectorDismissedUntil = null;
5099
5184
  this.lastReportedInspectorVisibility = null;
5100
5185
  this.inspectorDismissalTimer = null;
@@ -5383,9 +5468,18 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
5383
5468
  this.closeLauncherHud();
5384
5469
  this.activeRoot.querySelector(".console-button")?.focus();
5385
5470
  };
5386
- this.handleHudActionClick = (event, row) => {
5471
+ this.handleHudActionClick = (event, row, control) => {
5387
5472
  event.preventDefault();
5388
5473
  event.stopPropagation();
5474
+ const trigger = this.launcherHudTrigger;
5475
+ this.queueHudTelemetry(() => control === "toggle" ? trackHudFeatureToggleClicked({
5476
+ feature: row,
5477
+ trigger
5478
+ }) : trackHudFeatureClicked({
5479
+ feature: row,
5480
+ control,
5481
+ trigger
5482
+ }));
5389
5483
  this.hudLandingMenu = row === "threads" ? "threads" : row === "learning" ? "memories" : "home";
5390
5484
  this.closeLauncherHud();
5391
5485
  this.openInspector("floating_button");
@@ -5393,24 +5487,47 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
5393
5487
  this.handleHudRowClick = (event, row) => {
5394
5488
  const target = event.target;
5395
5489
  if (target instanceof Element && target.closest(".cpk-launcher-hud__controls, [data-cpk-hud-action]")) return;
5396
- this.handleHudActionClick(event, row);
5490
+ this.handleHudActionClick(event, row, "row");
5397
5491
  };
5398
5492
  this.handleHudNewsClick = (event) => {
5399
5493
  event.preventDefault();
5400
5494
  event.stopPropagation();
5495
+ const banner_id = this.announcementId;
5496
+ if (banner_id) {
5497
+ const trigger = this.launcherHudTrigger;
5498
+ this.queueHudTelemetry(() => trackHudNotificationClicked({
5499
+ banner_id,
5500
+ notification_id: banner_id,
5501
+ action: "open",
5502
+ trigger
5503
+ }));
5504
+ }
5401
5505
  this.hudLandingMenu = WHATS_NEW_MENU_KEY;
5506
+ if (this.notificationState.activeId) this.readNotification(this.notificationState.activeId);
5402
5507
  this.closeLauncherHud();
5403
5508
  this.openInspector("floating_button");
5404
5509
  };
5405
5510
  this.handleHudNewsDismissClick = (event) => {
5406
5511
  event.preventDefault();
5407
5512
  event.stopPropagation();
5513
+ const banner_id = this.announcementId;
5514
+ if (banner_id) {
5515
+ const trigger = this.launcherHudTrigger;
5516
+ this.queueHudTelemetry(() => trackHudNotificationClicked({
5517
+ banner_id,
5518
+ notification_id: banner_id,
5519
+ action: "dismiss",
5520
+ trigger
5521
+ }));
5522
+ }
5408
5523
  this.clearNewsSignal();
5409
5524
  this.activeRoot.querySelector(".console-button")?.focus({ preventScroll: true });
5410
5525
  };
5411
5526
  this.handleHudDismissDayClick = (event) => {
5412
5527
  event.preventDefault();
5413
5528
  event.stopPropagation();
5529
+ const trigger = this.launcherHudTrigger;
5530
+ this.queueHudTelemetry(() => trackHudHideClicked({ trigger }));
5414
5531
  this.dismissInspectorFor("day");
5415
5532
  };
5416
5533
  this.handleSidebarToggle = () => {
@@ -6061,6 +6178,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
6061
6178
  static {
6062
6179
  this.properties = {
6063
6180
  core: { attribute: false },
6181
+ notificationContext: { attribute: false },
6064
6182
  autoAttachCore: {
6065
6183
  type: Boolean,
6066
6184
  attribute: "auto-attach-core"
@@ -6496,6 +6614,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
6496
6614
  runtimeLicense = void 0;
6497
6615
  }
6498
6616
  this.inspectorMetadataProjection = projectInspectorMetadata(value, runtimeLicense);
6617
+ this.refreshNotifications();
6499
6618
  }
6500
6619
  attachToCore(core) {
6501
6620
  this.runtimeStatus = core.runtimeConnectionStatus;
@@ -7849,158 +7968,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
7849
7968
  font-weight: 600 !important;
7850
7969
  }
7851
7970
 
7852
- .announcement-content {
7853
- color: #1f2230;
7854
- font-size: 13px;
7855
- font-family: "Plus Jakarta Sans", system-ui, sans-serif;
7856
- line-height: 1.55;
7857
- }
7858
-
7859
- .announcement-content h1,
7860
- .announcement-content h2,
7861
- .announcement-content h3 {
7862
- color: #010507;
7863
- font-weight: 700;
7864
- line-height: 1.3;
7865
- margin: 0.9rem 0 0.4rem;
7866
- }
7867
- .announcement-content > h1:first-child,
7868
- .announcement-content > h2:first-child,
7869
- .announcement-content > h3:first-child {
7870
- margin-top: 0;
7871
- }
7872
-
7873
- .announcement-content h1 {
7874
- font-size: 1.15rem;
7875
- letter-spacing: -0.01em;
7876
- }
7877
- .announcement-content h2 {
7878
- font-size: 1rem;
7879
- }
7880
- .announcement-content h3 {
7881
- font-size: 0.9rem;
7882
- text-transform: none;
7883
- }
7884
-
7885
- .announcement-content p {
7886
- margin: 0.45rem 0;
7887
- }
7888
-
7889
- .announcement-content strong {
7890
- color: #010507;
7891
- font-weight: 700;
7892
- }
7893
-
7894
- .announcement-content ul {
7895
- list-style: disc;
7896
- padding-left: 1.25rem;
7897
- margin: 0.45rem 0;
7898
- }
7899
-
7900
- .announcement-content ol {
7901
- list-style: decimal;
7902
- padding-left: 1.25rem;
7903
- margin: 0.45rem 0;
7904
- }
7905
-
7906
- .announcement-content li + li {
7907
- margin-top: 0.15rem;
7908
- }
7909
-
7910
- .announcement-content a {
7911
- color: #5558b2;
7912
- text-decoration: underline;
7913
- }
7914
-
7915
- .announcement-content :not(pre) > code {
7916
- background: #f3f3f7;
7917
- border: 1px solid #e4e4ec;
7918
- border-radius: 5px;
7919
- padding: 1px 5px;
7920
- font-size: 0.85em;
7921
- color: #4a3a8a;
7922
- }
7923
-
7924
- .announcement-code {
7925
- position: relative;
7926
- margin: 0.6rem 0;
7927
- }
7928
-
7929
- .announcement-code pre {
7930
- background: #0f1117;
7931
- color: #e6e8f2;
7932
- border-radius: 10px;
7933
- padding: 10px 12px;
7934
- overflow-x: auto;
7935
- font-size: 12px;
7936
- line-height: 1.5;
7937
- white-space: pre;
7938
- }
7939
-
7940
- .announcement-code pre code::after {
7941
- content: "";
7942
- display: inline-block;
7943
- width: 80px;
7944
- }
7945
-
7946
- .announcement-code__copy-shield {
7947
- position: absolute;
7948
- top: 4px;
7949
- right: 4px;
7950
- padding: 4px 4px 4px 24px;
7951
- border-top-right-radius: 10px;
7952
- background: linear-gradient(
7953
- to right,
7954
- rgba(15, 17, 23, 0) 0%,
7955
- rgba(15, 17, 23, 0.95) 40%,
7956
- #0f1117 100%
7957
- );
7958
- pointer-events: none;
7959
- }
7960
-
7961
- .announcement-code pre code {
7962
- background: transparent;
7963
- border: none;
7964
- padding: 0;
7965
- color: inherit;
7966
- font-size: inherit;
7967
- }
7968
-
7969
- .announcement-code pre::-webkit-scrollbar {
7970
- height: 6px;
7971
- }
7972
- .announcement-code pre::-webkit-scrollbar-track {
7973
- background: transparent;
7974
- }
7975
- .announcement-code pre::-webkit-scrollbar-thumb {
7976
- background: rgba(255, 255, 255, 0.2);
7977
- border-radius: 4px;
7978
- }
7979
-
7980
- .announcement-code__copy {
7981
- position: relative;
7982
- pointer-events: auto;
7983
- padding: 3px 8px;
7984
- font-family: "Plus Jakarta Sans", system-ui, sans-serif;
7985
- font-size: 11px;
7986
- font-weight: 600;
7987
- color: #e6e8f2;
7988
- background: #1f222d;
7989
- border: 1px solid rgba(255, 255, 255, 0.15);
7990
- border-radius: 6px;
7991
- cursor: pointer;
7992
- transition:
7993
- background 0.12s ease,
7994
- color 0.12s ease;
7995
- }
7996
- .announcement-code__copy:hover {
7997
- background: #2a2e3c;
7998
- }
7999
- .announcement-code__copy[data-copied="true"] {
8000
- background: #eee6fe;
8001
- color: #6430ab;
8002
- border-color: transparent;
8003
- }
7971
+ ${notificationArticleStyles}
8004
7972
 
8005
7973
  /* ── What's new ──────────────────────────────────────────────── */
8006
7974
  .whats-new {
@@ -9528,6 +9496,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9528
9496
  }
9529
9497
  connectedCallback() {
9530
9498
  super.connectedCallback();
9499
+ this.notificationState = loadNotificationState();
9531
9500
  if (typeof window !== "undefined") {
9532
9501
  this.accountCtaMotionPaused = document.visibilityState !== "visible";
9533
9502
  this.threadsExampleOverviewVideoReducedMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
@@ -9642,7 +9611,11 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9642
9611
  if (this.isOpen && this.dockMode === "docked-left") this.setAttribute("data-docked", "true");
9643
9612
  else this.removeAttribute("data-docked");
9644
9613
  }
9645
- updated() {
9614
+ updated(changed) {
9615
+ if (changed.has("notificationContext") || changed.has("core")) {
9616
+ this.ensureAnnouncementLoading();
9617
+ this.refreshNotifications();
9618
+ }
9646
9619
  const visible = !this.isInspectorDismissed;
9647
9620
  if (visible !== this.lastReportedInspectorVisibility) {
9648
9621
  this.lastReportedInspectorVisibility = visible;
@@ -9652,6 +9625,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9652
9625
  this.syncThreadsExampleOverviewVideo();
9653
9626
  this.maybeTrackInspectorMetadataViews();
9654
9627
  this.maybeTrackNewsSignalViewed();
9628
+ this.maybeTrackHudViews();
9655
9629
  this.resolvePillDirection();
9656
9630
  this.maybeTrackErrorSignalViewed();
9657
9631
  this.maybeCompleteWhatsNewView();
@@ -9893,6 +9867,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9893
9867
  this.resolveLauncherHudSide();
9894
9868
  this.launcherHudIntro = true;
9895
9869
  this.launcherHudOpen = true;
9870
+ this.launcherHudTrigger = "intro";
9896
9871
  this.refreshLearningSnapshot({ preserve: true });
9897
9872
  this.requestUpdate();
9898
9873
  this.launcherHudIntroEndTimer = setTimeout(() => {
@@ -9941,6 +9916,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9941
9916
  }
9942
9917
  if (this.launcherHudOpen) return;
9943
9918
  this.launcherHudOpen = true;
9919
+ this.launcherHudTrigger = "user";
9944
9920
  this.refreshLearningSnapshot({ preserve: true });
9945
9921
  this.requestUpdate();
9946
9922
  }
@@ -9952,11 +9928,61 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9952
9928
  }
9953
9929
  if (!this.launcherHudOpen) return;
9954
9930
  this.launcherHudOpen = false;
9931
+ this.viewedHudElement = null;
9932
+ this.viewedHudParts.clear();
9955
9933
  this.requestUpdate();
9956
9934
  }
9935
+ queueHudTelemetry(send) {
9936
+ if (this.core?.telemetryDisabled) return;
9937
+ if (this.runtimeStatus === CopilotKitCoreRuntimeConnectionStatus.Connected) send();
9938
+ else if (this.pendingHudTelemetry.length < MAX_PENDING_BANNER_VIEWED) this.pendingHudTelemetry.push(send);
9939
+ }
9940
+ flushPendingHudTelemetry() {
9941
+ if (this.core?.telemetryDisabled) {
9942
+ this.pendingHudTelemetry = [];
9943
+ return;
9944
+ }
9945
+ if (this.runtimeStatus !== CopilotKitCoreRuntimeConnectionStatus.Connected) return;
9946
+ const queued = this.pendingHudTelemetry;
9947
+ this.pendingHudTelemetry = [];
9948
+ for (const send of queued) send();
9949
+ }
9950
+ maybeTrackHudViews() {
9951
+ const hud = this.activeRoot.querySelector("[data-cpk-launcher-hud]");
9952
+ if (!hud) {
9953
+ this.viewedHudElement = null;
9954
+ this.viewedHudParts.clear();
9955
+ return;
9956
+ }
9957
+ if (document.visibilityState !== "visible") return;
9958
+ if (hud !== this.viewedHudElement) {
9959
+ this.viewedHudElement = hud;
9960
+ this.viewedHudParts.clear();
9961
+ }
9962
+ const once = (key, send) => {
9963
+ if (this.viewedHudParts.has(key)) return;
9964
+ this.viewedHudParts.add(key);
9965
+ this.queueHudTelemetry(send);
9966
+ };
9967
+ const trigger = this.launcherHudTrigger;
9968
+ once("hud", () => trackHudViewed({ trigger }));
9969
+ if (hud.querySelector("[data-cpk-hud-news]") && this.announcementId) {
9970
+ const banner_id = this.announcementId;
9971
+ once(`notification:${banner_id}`, () => trackHudNotificationViewed({
9972
+ banner_id,
9973
+ notification_id: banner_id,
9974
+ trigger
9975
+ }));
9976
+ }
9977
+ for (const feature of ["threads", "learning"]) if (hud.querySelector(`[data-cpk-hud-toggle="${feature}"]`)) once(`toggle:${feature}`, () => trackHudFeatureToggleViewed({
9978
+ feature,
9979
+ trigger
9980
+ }));
9981
+ if (hud.querySelector("[data-cpk-dismiss-inspector=\"day\"]")) once("hide", () => trackHudHideViewed({ trigger }));
9982
+ }
9957
9983
  getUnreadAnnouncementTitle() {
9958
9984
  if (!this.newsSignalArmed || !this.announcementLoaded) return null;
9959
- const title = this.announcementPreviewText?.trim() || "New in CopilotKit";
9985
+ const title = this.notificationFeed?.notifications.find((notice) => notice.id === this.notificationState.activeId)?.title.trim() || "New in CopilotKit";
9960
9986
  const titleCharacters = Array.from(title);
9961
9987
  return titleCharacters.length > HUD_ANNOUNCEMENT_TITLE_LIMIT ? `${titleCharacters.slice(0, HUD_ANNOUNCEMENT_TITLE_LIMIT).join("").trimEnd()}...` : title;
9962
9988
  }
@@ -9977,7 +10003,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9977
10003
  class="cpk-launcher-hud__action"
9978
10004
  data-cpk-hud-action
9979
10005
  aria-label=${`Open ${args.label} in Inspector`}
9980
- @click=${(event) => this.handleHudActionClick(event, args.id)}
10006
+ @click=${(event) => this.handleHudActionClick(event, args.id, "action")}
9981
10007
  @pointerdown=${(event) => event.stopPropagation()}
9982
10008
  >
9983
10009
  <span
@@ -10002,7 +10028,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10002
10028
  data-cpk-hud-learn-more=${args.id}
10003
10029
  aria-label=${`Learn more about ${args.label}`}
10004
10030
  aria-describedby=${detailId}
10005
- @click=${(event) => this.handleHudActionClick(event, args.id)}
10031
+ @click=${(event) => this.handleHudActionClick(event, args.id, "learn_more")}
10006
10032
  @pointerdown=${(event) => event.stopPropagation()}
10007
10033
  >
10008
10034
  ${this.renderIcon("CircleHelp")}
@@ -10014,7 +10040,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10014
10040
  data-enabled=${args.connected ? "true" : "false"}
10015
10041
  aria-label=${args.connected ? `${args.label} is enabled` : `Open ${args.label} in Inspector`}
10016
10042
  ?disabled=${args.connected}
10017
- @click=${(event) => this.handleHudActionClick(event, args.id)}
10043
+ @click=${(event) => this.handleHudActionClick(event, args.id, "toggle")}
10018
10044
  @pointerdown=${(event) => event.stopPropagation()}
10019
10045
  >
10020
10046
  <span
@@ -10331,6 +10357,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10331
10357
  }
10332
10358
  getHomeModel() {
10333
10359
  const lastRuntimeEvent = this.flattenedEvents[0];
10360
+ const activeNotice = this.notificationFeed?.notifications.find((notice) => notice.id === this.notificationState.activeId);
10334
10361
  return buildHomeModel({
10335
10362
  intelligenceConnected: Boolean(this._core?.intelligence),
10336
10363
  threadsAvailable: this.areThreadEndpointsAvailable(),
@@ -10349,25 +10376,23 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10349
10376
  suggestionsOn: this._core?.suggestions === true,
10350
10377
  audioOn: this._core?.audioFileTranscriptionEnabled === true,
10351
10378
  websocketUrl: this._core?.intelligence?.wsUrl,
10352
- announcementPreviewText: this.announcementPreviewText ?? void 0,
10353
- announcementMarkdown: this.announcementMarkdown ?? void 0,
10354
- announcementHtml: this.announcementHtml ?? void 0,
10379
+ announcementPreviewText: activeNotice?.title,
10380
+ announcementMarkdown: activeNotice?.body,
10381
+ announcementHtml: activeNotice ? this.notificationDocuments.get(activeNotice.id) : void 0,
10355
10382
  intelligenceSignupUrl: this.getIntelligenceSignupUrl()
10356
10383
  });
10357
10384
  }
10358
10385
  renderHomeView() {
10359
10386
  const model = this.getHomeModel();
10360
- const connected = model.hero.connection === "connected";
10361
10387
  return html`
10362
10388
  <div
10363
10389
  class="inspector-home"
10364
10390
  data-inspector-home
10365
- data-inspector-home-state=${connected ? "connected" : "disconnected"}
10391
+ data-inspector-home-state=${model.hero.connection === "connected" ? "connected" : "disconnected"}
10366
10392
  >
10367
10393
  ${this.renderHomeWhatsNewPreview(model.news)}
10368
- <header class="inspector-home-heading"><div><h1>${connected ? model.project?.projectName ?? "Your agent workspace" : "Welcome to Intelligence"}</h1><p>${connected ? "Inspect conversations and see what your agent is learning." : "Connect your agent. Intelligence learns from experience."}</p></div></header>
10369
- ${connected ? this.renderHomeSystemHealth(model) : this.renderHomeIntelligenceHud(model)}
10370
- ${connected ? this.renderHomeIntelligenceHud(model) : this.renderHomeSystemHealth(model)}
10394
+ ${this.renderHomeSystemHealth(model)}
10395
+ ${this.renderHomeIntelligenceHud(model)}
10371
10396
  ${this.renderHomeFeatures(model)}
10372
10397
  </div>
10373
10398
  `;
@@ -10389,7 +10414,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10389
10414
  data-inspector-whats-new-preview
10390
10415
  aria-label="Open What's New"
10391
10416
  style=${INTERACTIVE_FOCUS_BASE_STYLE}
10392
- @click=${() => this.handleMenuSelect(WHATS_NEW_MENU_KEY)}
10417
+ @click=${() => this.notificationState.activeId ? this.readNotification(this.notificationState.activeId) : this.handleMenuSelect(WHATS_NEW_MENU_KEY)}
10393
10418
  >
10394
10419
  <span class="inspector-whats-new-preview-copy">
10395
10420
  <span class="inspector-whats-new-preview-title">
@@ -10406,47 +10431,89 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10406
10431
  `;
10407
10432
  }
10408
10433
  renderWhatsNewView() {
10409
- const state = this.getWhatsNewState();
10410
- const news = this.getHomeModel().news;
10411
- const updatedAt = this.announcementTimestamp ? new Date(this.announcementTimestamp) : null;
10412
- const updatedLabel = updatedAt && !Number.isNaN(updatedAt.getTime()) ? new Intl.DateTimeFormat(void 0, {
10413
- month: "long",
10434
+ const notices = this.notificationFeed?.notifications.filter((n) => this.notificationState.eligibleIds.includes(n.id)).sort(compareNotifications) ?? [];
10435
+ const selected = notices.find((n) => n.id === this.selectedNotificationId);
10436
+ const formatDate = (date) => new Date(date).toLocaleDateString(void 0, {
10437
+ month: "short",
10414
10438
  day: "numeric",
10415
10439
  year: "numeric"
10416
- }).format(updatedAt) : null;
10440
+ });
10417
10441
  return html`
10418
10442
  <div
10419
10443
  class="inspector-home inspector-whats-new"
10420
10444
  data-inspector-whats-new
10421
10445
  data-cpk-whats-new
10422
- data-cpk-whats-new-state=${state}
10446
+ data-cpk-whats-new-state=${this.getWhatsNewState()}
10423
10447
  >
10424
- <header class="inspector-whats-new-header">
10425
- <h1 class="inspector-home-title">What's New</h1>
10426
- ${updatedLabel ? html`
10427
- <p class="inspector-whats-new-updated">
10428
- Updated
10429
- <time datetime=${updatedAt?.toISOString()}
10430
- >${updatedLabel}</time
10431
- >
10432
- </p>
10433
- ` : nothing}
10434
- </header>
10435
10448
  <section class="inspector-home-news" aria-label="CopilotKit updates">
10436
- ${news.empty || !news.documentHtml ? html`
10437
- <article class="inspector-whats-new-empty">
10438
- <h2 class="inspector-home-card-title">${news.title}</h2>
10439
- <p class="inspector-home-card-copy">${news.previewText}</p>
10440
- </article>
10441
- ` : html`
10449
+ ${selected ? html`
10450
+ <button
10451
+ type="button"
10452
+ class="inspector-whats-new-back"
10453
+ @click=${() => {
10454
+ this.selectedNotificationId = null;
10455
+ this.requestUpdate();
10456
+ }}
10457
+ >
10458
+ <span aria-hidden="true"
10459
+ >${this.renderIcon("ArrowLeft")}</span
10460
+ >
10461
+ All updates
10462
+ </button>
10442
10463
  <article class="inspector-whats-new-document">
10464
+ <header class="inspector-whats-new-document-header">
10465
+ <h1>${selected.title}</h1>
10466
+ <time datetime=${selected.publishedAt}>
10467
+ ${formatDate(selected.publishedAt)}
10468
+ </time>
10469
+ </header>
10443
10470
  <div
10444
10471
  class="announcement-content"
10445
10472
  @click=${this.handleAnnouncementContentClick}
10446
10473
  >
10447
- ${unsafeHTML(news.documentHtml)}
10474
+ ${unsafeHTML(this.notificationDocuments.get(selected.id) ?? "")}
10448
10475
  </div>
10449
10476
  </article>
10477
+ ` : html`
10478
+ <header class="inspector-whats-new-header">
10479
+ <h1 class="inspector-home-title">What's New</h1>
10480
+ </header>
10481
+ ${notices.length ? html`
10482
+ <ul class="inspector-whats-new-list">
10483
+ ${notices.map((notice) => {
10484
+ const read = this.notificationState.readIds.includes(notice.id);
10485
+ return html`
10486
+ <li>
10487
+ <button
10488
+ type="button"
10489
+ class="cpk-notification-row"
10490
+ data-notification-id=${notice.id}
10491
+ @click=${() => this.readNotification(notice.id)}
10492
+ >
10493
+ <span class="cpk-notification-copy">
10494
+ <strong>${notice.title}</strong>
10495
+ <span class="cpk-notification-meta">
10496
+ <time datetime=${notice.publishedAt}
10497
+ >${formatDate(notice.publishedAt)}</time
10498
+ >
10499
+ ${read ? nothing : html`
10500
+ <span class="cpk-notification-unread">Unread</span>
10501
+ `}
10502
+ </span>
10503
+ </span>
10504
+ <span
10505
+ class="cpk-notification-chevron"
10506
+ aria-hidden="true"
10507
+ >${this.renderIcon("ChevronRight")}</span
10508
+ >
10509
+ </button>
10510
+ </li>
10511
+ `;
10512
+ })}
10513
+ </ul>
10514
+ ` : html`<p class="inspector-whats-new-empty" role="status">
10515
+ ${this.announcementLoaded || !this.notificationContext.development ? "You're all caught up." : "Loading updates…"}
10516
+ </p>`}
10450
10517
  `}
10451
10518
  </section>
10452
10519
  </div>
@@ -12929,15 +12996,15 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
12929
12996
  }
12930
12997
  trackWhatsNewClickedOnce(opts) {
12931
12998
  if (this.runtimeStatus !== CopilotKitCoreRuntimeConnectionStatus.Connected || this.core?.telemetryDisabled) return;
12932
- const id = this.announcementTimestamp;
12999
+ const id = this.announcementId;
12933
13000
  if (!id) return;
12934
13001
  const key = `${id}:${opts.cta}`;
12935
13002
  if (this.clickedBannerIds.has(key)) return;
12936
13003
  this.clickedBannerIds.add(key);
12937
13004
  trackWhatsNewClicked({
12938
13005
  banner_id: id,
12939
- cta: opts.cta,
12940
- cta_label: this.announcementCtaLabel ?? void 0
13006
+ notification_id: id,
13007
+ cta: opts.cta
12941
13008
  });
12942
13009
  }
12943
13010
  trackThreadsViewStateOnce(state) {
@@ -15449,11 +15516,9 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
15449
15516
  else if (!wasArmed) this.requestUpdate();
15450
15517
  }
15451
15518
  clearNewsSignal() {
15452
- if (!this.newsSignalArmed) return;
15453
- this.newsSignalArmed = false;
15454
- if (this.announcementTimestamp) saveAnnouncementReadTimestamp(this.announcementTimestamp);
15455
- this.retireSignal(NEWS_SIGNAL_ID);
15456
- this.requestUpdate();
15519
+ if (!this.notificationState.activeId) return;
15520
+ this.notificationState = acknowledgeNotification(this.notificationState, this.notificationState.activeId);
15521
+ this.refreshNotifications();
15457
15522
  }
15458
15523
  /**
15459
15524
  * Requests one beat for a signal, running it now or deferring it.
@@ -15486,7 +15551,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
15486
15551
  this.pendingPulseSignal = null;
15487
15552
  this.pulsingSignal = key;
15488
15553
  if (isWiringErrorKey(key)) this.errorBeatSpent = true;
15489
- else if (this.announcementTimestamp && key === NEWS_SIGNAL_ID) saveAnnouncementPulsedTimestamp(this.announcementTimestamp);
15554
+ else if (this.notificationState.activeId && key === NEWS_SIGNAL_ID) saveNotificationPulsedId(this.notificationState.activeId);
15490
15555
  this.beginGestureTail(key);
15491
15556
  this.requestUpdate();
15492
15557
  if (typeof window === "undefined") return;
@@ -15786,14 +15851,14 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
15786
15851
  }
15787
15852
  maybeTrackNewsSignalViewed() {
15788
15853
  if (!this.newsSignalArmed || this.pulsingSignal !== NEWS_SIGNAL_ID || this.isOpen || typeof document === "undefined" || document.visibilityState !== "visible") return;
15789
- const id = this.announcementTimestamp;
15854
+ const id = (this.notificationFeed?.notifications.find((n) => n.id === this.notificationState.activeId))?.id;
15790
15855
  if (!id || this.viewedNewsSignalIds.has(id)) return;
15791
15856
  this.viewedNewsSignalIds.add(id);
15792
15857
  this.pendingNewsSignalViewed = {
15793
15858
  banner_id: id,
15859
+ notification_id: id,
15794
15860
  surface: "launcher",
15795
- presentation: typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ? "reduced_motion" : "animated",
15796
- cta_label: this.announcementCtaLabel ?? void 0
15861
+ presentation: typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ? "reduced_motion" : "animated"
15797
15862
  };
15798
15863
  this.flushPendingWhatsNewTelemetry();
15799
15864
  }
@@ -15811,7 +15876,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
15811
15876
  getVisibleBannerSurface() {
15812
15877
  if (!this.isOpen || this.settingsOpen) return null;
15813
15878
  if (this.selectedMenu !== WHATS_NEW_MENU_KEY) return null;
15814
- return this.announcementHtml ? "whats_new" : null;
15879
+ return this.selectedNotificationId && this.announcementHtml ? "whats_new" : null;
15815
15880
  }
15816
15881
  /**
15817
15882
  * The single condition that retires the news signal: What's new has
@@ -15830,14 +15895,13 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
15830
15895
  maybeCompleteWhatsNewView() {
15831
15896
  if (!this.getVisibleBannerSurface()) return;
15832
15897
  this.maybeTrackWhatsNewViewed();
15833
- this.clearNewsSignal();
15834
15898
  }
15835
15899
  /**
15836
15900
  * Records a `whats_new_viewed` impression for whichever surface is
15837
15901
  * currently visible, once per announcement per surface.
15838
15902
  */
15839
15903
  maybeTrackWhatsNewViewed() {
15840
- const id = this.announcementTimestamp;
15904
+ const id = this.announcementId;
15841
15905
  if (!id) return;
15842
15906
  const surface = this.getVisibleBannerSurface();
15843
15907
  if (!surface) return;
@@ -15847,12 +15911,13 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
15847
15911
  this.viewedBannerSurfaces.add(key);
15848
15912
  this.pendingBannerViewed.push({
15849
15913
  banner_id: id,
15850
- surface,
15851
- cta_label: this.announcementCtaLabel ?? void 0
15914
+ notification_id: id,
15915
+ surface
15852
15916
  });
15853
15917
  this.flushPendingWhatsNewTelemetry();
15854
15918
  }
15855
15919
  flushPendingWhatsNewTelemetry() {
15920
+ this.flushPendingHudTelemetry();
15856
15921
  if (this.pendingBannerViewed.length === 0 && !this.pendingNewsSignalViewed) return;
15857
15922
  if (this.core?.telemetryDisabled) {
15858
15923
  this.pendingBannerViewed = [];
@@ -15870,32 +15935,58 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
15870
15935
  }
15871
15936
  }
15872
15937
  ensureAnnouncementLoading() {
15873
- if (this.announcementPromise || typeof window === "undefined" || typeof fetch === "undefined") return;
15938
+ if (this.isInspectorDismissed || !this.notificationContext.development || !this.isConnected || this.announcementPromise || typeof window === "undefined" || typeof fetch === "undefined") return;
15874
15939
  this.announcementPromise = this.fetchAnnouncement();
15875
15940
  }
15876
15941
  async fetchAnnouncement() {
15877
15942
  try {
15878
- const response = await fetch(ANNOUNCEMENT_URL, { cache: "no-cache" });
15879
- if (!response.ok) throw new Error(`Failed to load announcement (${response.status})`);
15880
- const data = await response.json();
15881
- const timestamp = typeof data?.timestamp === "string" ? data.timestamp : null;
15882
- const previewText = typeof data?.previewText === "string" ? data.previewText : null;
15883
- const markdown = typeof data?.announcement === "string" ? data.announcement : null;
15884
- const ctaLabel = typeof data?.cta_label === "string" ? data.cta_label : null;
15885
- if (!timestamp || !markdown) throw new Error("Malformed announcement payload");
15886
- this.announcementTimestamp = timestamp;
15887
- this.announcementPreviewText = previewText ?? "";
15888
- this.announcementMarkdown = markdown;
15889
- this.announcementCtaLabel = ctaLabel;
15890
- this.announcementHtml = await this.convertMarkdownToHtml(markdown);
15891
- this.announcementLoaded = true;
15892
- if (this.announcementHtml && loadAnnouncementReadTimestamp() !== timestamp) this.armNewsSignal({ pulse: loadAnnouncementPulsedTimestamp() !== timestamp });
15893
- this.requestUpdate();
15894
- } catch (error) {
15895
- console.warn("[CopilotKit Inspector] Failed to load announcement", error);
15896
- this.announcementLoaded = true;
15897
- this.requestUpdate();
15943
+ const feed = await loadNotificationFeed(this.notificationContext);
15944
+ if (feed) {
15945
+ const documents = await Promise.all(feed.notifications.map(async (notice) => [notice.id, await this.convertMarkdownToHtml(notice.body) ?? ""]));
15946
+ this.notificationDocuments = new Map(documents);
15947
+ this.notificationFeed = feed;
15948
+ }
15949
+ } catch {}
15950
+ this.announcementLoaded = true;
15951
+ this.refreshNotifications();
15952
+ this.requestUpdate();
15953
+ }
15954
+ /** Resolve only confirmed runtime metadata; missing fields stay unknown. */
15955
+ getNotificationContext() {
15956
+ const base = this.notificationContext;
15957
+ const core = this.core;
15958
+ if (!core || this.runtimeStatus !== CopilotKitCoreRuntimeConnectionStatus.Connected) return base;
15959
+ const mode = core.runtimeMode;
15960
+ const entitlement = core.runtimeEntitlements?.status === "ready" ? core.runtimeEntitlements.entitlement : void 0;
15961
+ return {
15962
+ ...base,
15963
+ intelligence: mode === "intelligence" ? "enabled" : mode === "sse" ? "disabled" : void 0,
15964
+ plan: this.inspectorMetadataProjection.plan?.code ?? entitlement?.planCode,
15965
+ deployment: entitlement?.source === "managedOrgSubscription" ? "managed" : entitlement ? "self-hosted" : void 0
15966
+ };
15967
+ }
15968
+ refreshNotifications() {
15969
+ if (!this.notificationFeed) return;
15970
+ this.notificationState = migrateAnnouncementReadState(this.notificationState, this.notificationFeed);
15971
+ const previousActiveId = this.notificationState.activeId;
15972
+ this.notificationState = reconcileNotifications(this.notificationState, this.notificationFeed, this.getNotificationContext());
15973
+ saveNotificationState(this.notificationState);
15974
+ if (!this.notificationState.eligibleIds.includes(this.selectedNotificationId ?? "")) this.selectedNotificationId = null;
15975
+ const notice = this.notificationFeed.notifications.find((n) => this.notificationState.eligibleIds.includes(n.id) && n.id === (this.selectedNotificationId ?? this.notificationState.activeId)) ?? this.notificationFeed.notifications.filter((n) => this.notificationState.eligibleIds.includes(n.id)).sort(compareNotifications)[0];
15976
+ this.announcementId = notice?.id ?? null;
15977
+ this.announcementHtml = notice ? this.notificationDocuments.get(notice.id) ?? null : null;
15978
+ if (this.notificationState.activeId && this.notificationState.eligibleIds.includes(this.notificationState.activeId)) this.armNewsSignal({ pulse: (previousActiveId !== this.notificationState.activeId || !this.newsSignalArmed) && !hasNotificationPulsed(this.notificationState.activeId, this.notificationFeed.notifications.find((n) => n.id === this.notificationState.activeId).publishedAt) });
15979
+ else {
15980
+ this.newsSignalArmed = false;
15981
+ this.retireSignal(NEWS_SIGNAL_ID);
15898
15982
  }
15983
+ this.requestUpdate();
15984
+ }
15985
+ readNotification(id) {
15986
+ this.selectedNotificationId = id;
15987
+ this.notificationState = acknowledgeNotification(this.notificationState, id);
15988
+ this.refreshNotifications();
15989
+ this.handleMenuSelect(WHATS_NEW_MENU_KEY);
15899
15990
  }
15900
15991
  async convertMarkdownToHtml(markdown) {
15901
15992
  const renderer = new marked.Renderer();
@@ -15970,8 +16061,9 @@ function defineWebInspector(registry = globalThis.customElements) {
15970
16061
  * auto-attachment first prevents `connectedCallback` from briefly selecting a
15971
16062
  * different global core.
15972
16063
  */
15973
- function configureWebInspectorElement(inspector, core) {
16064
+ function configureWebInspectorElement(inspector, core, notificationContext = { development: false }) {
15974
16065
  inspector.autoAttachCore = false;
16066
+ inspector.notificationContext = notificationContext;
15975
16067
  inspector.core = core;
15976
16068
  return inspector;
15977
16069
  }