@copilotkit/web-inspector 1.73.3 → 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 (43) hide show
  1. package/dist/index.cjs +250 -233
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +24 -6
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.mts +24 -6
  6. package/dist/index.d.mts.map +1 -1
  7. package/dist/index.mjs +251 -234
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/index.umd.js +1165 -591
  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/persistence.cjs +124 -48
  24. package/dist/lib/persistence.cjs.map +1 -1
  25. package/dist/lib/persistence.mjs +121 -45
  26. package/dist/lib/persistence.mjs.map +1 -1
  27. package/dist/lib/telemetry.cjs +42 -0
  28. package/dist/lib/telemetry.cjs.map +1 -1
  29. package/dist/lib/telemetry.d.cts.map +1 -1
  30. package/dist/lib/telemetry.d.mts.map +1 -1
  31. package/dist/lib/telemetry.mjs +35 -1
  32. package/dist/lib/telemetry.mjs.map +1 -1
  33. package/dist/package.cjs +1 -1
  34. package/dist/package.mjs +1 -1
  35. package/dist/styles/generated.cjs +1 -1
  36. package/dist/styles/generated.cjs.map +1 -1
  37. package/dist/styles/generated.mjs +1 -1
  38. package/dist/styles/generated.mjs.map +1 -1
  39. package/dist/styles/notification-article.cjs +244 -0
  40. package/dist/styles/notification-article.cjs.map +1 -0
  41. package/dist/styles/notification-article.mjs +244 -0
  42. package/dist/styles/notification-article.mjs.map +1 -0
  43. 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 = {
@@ -5131,11 +5133,13 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
5131
5133
  150
5132
5134
  ];
5133
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;
5134
5141
  this.announcementHtml = null;
5135
- this.announcementMarkdown = null;
5136
- this.announcementTimestamp = null;
5137
- this.announcementPreviewText = null;
5138
- this.announcementCtaLabel = null;
5142
+ this.announcementId = null;
5139
5143
  this.announcementLoaded = false;
5140
5144
  this.announcementPromise = null;
5141
5145
  this.newsSignalArmed = false;
@@ -5172,6 +5176,10 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
5172
5176
  this.launcherHudIntro = false;
5173
5177
  this.launcherHudIntroStartTimer = null;
5174
5178
  this.launcherHudIntroEndTimer = null;
5179
+ this.viewedHudElement = null;
5180
+ this.launcherHudTrigger = "user";
5181
+ this.viewedHudParts = /* @__PURE__ */ new Set();
5182
+ this.pendingHudTelemetry = [];
5175
5183
  this.inspectorDismissedUntil = null;
5176
5184
  this.lastReportedInspectorVisibility = null;
5177
5185
  this.inspectorDismissalTimer = null;
@@ -5460,9 +5468,18 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
5460
5468
  this.closeLauncherHud();
5461
5469
  this.activeRoot.querySelector(".console-button")?.focus();
5462
5470
  };
5463
- this.handleHudActionClick = (event, row) => {
5471
+ this.handleHudActionClick = (event, row, control) => {
5464
5472
  event.preventDefault();
5465
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
+ }));
5466
5483
  this.hudLandingMenu = row === "threads" ? "threads" : row === "learning" ? "memories" : "home";
5467
5484
  this.closeLauncherHud();
5468
5485
  this.openInspector("floating_button");
@@ -5470,24 +5487,47 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
5470
5487
  this.handleHudRowClick = (event, row) => {
5471
5488
  const target = event.target;
5472
5489
  if (target instanceof Element && target.closest(".cpk-launcher-hud__controls, [data-cpk-hud-action]")) return;
5473
- this.handleHudActionClick(event, row);
5490
+ this.handleHudActionClick(event, row, "row");
5474
5491
  };
5475
5492
  this.handleHudNewsClick = (event) => {
5476
5493
  event.preventDefault();
5477
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
+ }
5478
5505
  this.hudLandingMenu = WHATS_NEW_MENU_KEY;
5506
+ if (this.notificationState.activeId) this.readNotification(this.notificationState.activeId);
5479
5507
  this.closeLauncherHud();
5480
5508
  this.openInspector("floating_button");
5481
5509
  };
5482
5510
  this.handleHudNewsDismissClick = (event) => {
5483
5511
  event.preventDefault();
5484
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
+ }
5485
5523
  this.clearNewsSignal();
5486
5524
  this.activeRoot.querySelector(".console-button")?.focus({ preventScroll: true });
5487
5525
  };
5488
5526
  this.handleHudDismissDayClick = (event) => {
5489
5527
  event.preventDefault();
5490
5528
  event.stopPropagation();
5529
+ const trigger = this.launcherHudTrigger;
5530
+ this.queueHudTelemetry(() => trackHudHideClicked({ trigger }));
5491
5531
  this.dismissInspectorFor("day");
5492
5532
  };
5493
5533
  this.handleSidebarToggle = () => {
@@ -6138,6 +6178,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
6138
6178
  static {
6139
6179
  this.properties = {
6140
6180
  core: { attribute: false },
6181
+ notificationContext: { attribute: false },
6141
6182
  autoAttachCore: {
6142
6183
  type: Boolean,
6143
6184
  attribute: "auto-attach-core"
@@ -6573,6 +6614,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
6573
6614
  runtimeLicense = void 0;
6574
6615
  }
6575
6616
  this.inspectorMetadataProjection = projectInspectorMetadata(value, runtimeLicense);
6617
+ this.refreshNotifications();
6576
6618
  }
6577
6619
  attachToCore(core) {
6578
6620
  this.runtimeStatus = core.runtimeConnectionStatus;
@@ -7926,158 +7968,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
7926
7968
  font-weight: 600 !important;
7927
7969
  }
7928
7970
 
7929
- .announcement-content {
7930
- color: #1f2230;
7931
- font-size: 13px;
7932
- font-family: "Plus Jakarta Sans", system-ui, sans-serif;
7933
- line-height: 1.55;
7934
- }
7935
-
7936
- .announcement-content h1,
7937
- .announcement-content h2,
7938
- .announcement-content h3 {
7939
- color: #010507;
7940
- font-weight: 700;
7941
- line-height: 1.3;
7942
- margin: 0.9rem 0 0.4rem;
7943
- }
7944
- .announcement-content > h1:first-child,
7945
- .announcement-content > h2:first-child,
7946
- .announcement-content > h3:first-child {
7947
- margin-top: 0;
7948
- }
7949
-
7950
- .announcement-content h1 {
7951
- font-size: 1.15rem;
7952
- letter-spacing: -0.01em;
7953
- }
7954
- .announcement-content h2 {
7955
- font-size: 1rem;
7956
- }
7957
- .announcement-content h3 {
7958
- font-size: 0.9rem;
7959
- text-transform: none;
7960
- }
7961
-
7962
- .announcement-content p {
7963
- margin: 0.45rem 0;
7964
- }
7965
-
7966
- .announcement-content strong {
7967
- color: #010507;
7968
- font-weight: 700;
7969
- }
7970
-
7971
- .announcement-content ul {
7972
- list-style: disc;
7973
- padding-left: 1.25rem;
7974
- margin: 0.45rem 0;
7975
- }
7976
-
7977
- .announcement-content ol {
7978
- list-style: decimal;
7979
- padding-left: 1.25rem;
7980
- margin: 0.45rem 0;
7981
- }
7982
-
7983
- .announcement-content li + li {
7984
- margin-top: 0.15rem;
7985
- }
7986
-
7987
- .announcement-content a {
7988
- color: #5558b2;
7989
- text-decoration: underline;
7990
- }
7991
-
7992
- .announcement-content :not(pre) > code {
7993
- background: #f3f3f7;
7994
- border: 1px solid #e4e4ec;
7995
- border-radius: 5px;
7996
- padding: 1px 5px;
7997
- font-size: 0.85em;
7998
- color: #4a3a8a;
7999
- }
8000
-
8001
- .announcement-code {
8002
- position: relative;
8003
- margin: 0.6rem 0;
8004
- }
8005
-
8006
- .announcement-code pre {
8007
- background: #0f1117;
8008
- color: #e6e8f2;
8009
- border-radius: 10px;
8010
- padding: 10px 12px;
8011
- overflow-x: auto;
8012
- font-size: 12px;
8013
- line-height: 1.5;
8014
- white-space: pre;
8015
- }
8016
-
8017
- .announcement-code pre code::after {
8018
- content: "";
8019
- display: inline-block;
8020
- width: 80px;
8021
- }
8022
-
8023
- .announcement-code__copy-shield {
8024
- position: absolute;
8025
- top: 4px;
8026
- right: 4px;
8027
- padding: 4px 4px 4px 24px;
8028
- border-top-right-radius: 10px;
8029
- background: linear-gradient(
8030
- to right,
8031
- rgba(15, 17, 23, 0) 0%,
8032
- rgba(15, 17, 23, 0.95) 40%,
8033
- #0f1117 100%
8034
- );
8035
- pointer-events: none;
8036
- }
8037
-
8038
- .announcement-code pre code {
8039
- background: transparent;
8040
- border: none;
8041
- padding: 0;
8042
- color: inherit;
8043
- font-size: inherit;
8044
- }
8045
-
8046
- .announcement-code pre::-webkit-scrollbar {
8047
- height: 6px;
8048
- }
8049
- .announcement-code pre::-webkit-scrollbar-track {
8050
- background: transparent;
8051
- }
8052
- .announcement-code pre::-webkit-scrollbar-thumb {
8053
- background: rgba(255, 255, 255, 0.2);
8054
- border-radius: 4px;
8055
- }
8056
-
8057
- .announcement-code__copy {
8058
- position: relative;
8059
- pointer-events: auto;
8060
- padding: 3px 8px;
8061
- font-family: "Plus Jakarta Sans", system-ui, sans-serif;
8062
- font-size: 11px;
8063
- font-weight: 600;
8064
- color: #e6e8f2;
8065
- background: #1f222d;
8066
- border: 1px solid rgba(255, 255, 255, 0.15);
8067
- border-radius: 6px;
8068
- cursor: pointer;
8069
- transition:
8070
- background 0.12s ease,
8071
- color 0.12s ease;
8072
- }
8073
- .announcement-code__copy:hover {
8074
- background: #2a2e3c;
8075
- }
8076
- .announcement-code__copy[data-copied="true"] {
8077
- background: #eee6fe;
8078
- color: #6430ab;
8079
- border-color: transparent;
8080
- }
7971
+ ${notificationArticleStyles}
8081
7972
 
8082
7973
  /* ── What's new ──────────────────────────────────────────────── */
8083
7974
  .whats-new {
@@ -9605,6 +9496,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9605
9496
  }
9606
9497
  connectedCallback() {
9607
9498
  super.connectedCallback();
9499
+ this.notificationState = loadNotificationState();
9608
9500
  if (typeof window !== "undefined") {
9609
9501
  this.accountCtaMotionPaused = document.visibilityState !== "visible";
9610
9502
  this.threadsExampleOverviewVideoReducedMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
@@ -9719,7 +9611,11 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9719
9611
  if (this.isOpen && this.dockMode === "docked-left") this.setAttribute("data-docked", "true");
9720
9612
  else this.removeAttribute("data-docked");
9721
9613
  }
9722
- updated() {
9614
+ updated(changed) {
9615
+ if (changed.has("notificationContext") || changed.has("core")) {
9616
+ this.ensureAnnouncementLoading();
9617
+ this.refreshNotifications();
9618
+ }
9723
9619
  const visible = !this.isInspectorDismissed;
9724
9620
  if (visible !== this.lastReportedInspectorVisibility) {
9725
9621
  this.lastReportedInspectorVisibility = visible;
@@ -9729,6 +9625,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9729
9625
  this.syncThreadsExampleOverviewVideo();
9730
9626
  this.maybeTrackInspectorMetadataViews();
9731
9627
  this.maybeTrackNewsSignalViewed();
9628
+ this.maybeTrackHudViews();
9732
9629
  this.resolvePillDirection();
9733
9630
  this.maybeTrackErrorSignalViewed();
9734
9631
  this.maybeCompleteWhatsNewView();
@@ -9970,6 +9867,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9970
9867
  this.resolveLauncherHudSide();
9971
9868
  this.launcherHudIntro = true;
9972
9869
  this.launcherHudOpen = true;
9870
+ this.launcherHudTrigger = "intro";
9973
9871
  this.refreshLearningSnapshot({ preserve: true });
9974
9872
  this.requestUpdate();
9975
9873
  this.launcherHudIntroEndTimer = setTimeout(() => {
@@ -10018,6 +9916,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10018
9916
  }
10019
9917
  if (this.launcherHudOpen) return;
10020
9918
  this.launcherHudOpen = true;
9919
+ this.launcherHudTrigger = "user";
10021
9920
  this.refreshLearningSnapshot({ preserve: true });
10022
9921
  this.requestUpdate();
10023
9922
  }
@@ -10029,11 +9928,61 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10029
9928
  }
10030
9929
  if (!this.launcherHudOpen) return;
10031
9930
  this.launcherHudOpen = false;
9931
+ this.viewedHudElement = null;
9932
+ this.viewedHudParts.clear();
10032
9933
  this.requestUpdate();
10033
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
+ }
10034
9983
  getUnreadAnnouncementTitle() {
10035
9984
  if (!this.newsSignalArmed || !this.announcementLoaded) return null;
10036
- 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";
10037
9986
  const titleCharacters = Array.from(title);
10038
9987
  return titleCharacters.length > HUD_ANNOUNCEMENT_TITLE_LIMIT ? `${titleCharacters.slice(0, HUD_ANNOUNCEMENT_TITLE_LIMIT).join("").trimEnd()}...` : title;
10039
9988
  }
@@ -10054,7 +10003,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10054
10003
  class="cpk-launcher-hud__action"
10055
10004
  data-cpk-hud-action
10056
10005
  aria-label=${`Open ${args.label} in Inspector`}
10057
- @click=${(event) => this.handleHudActionClick(event, args.id)}
10006
+ @click=${(event) => this.handleHudActionClick(event, args.id, "action")}
10058
10007
  @pointerdown=${(event) => event.stopPropagation()}
10059
10008
  >
10060
10009
  <span
@@ -10079,7 +10028,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10079
10028
  data-cpk-hud-learn-more=${args.id}
10080
10029
  aria-label=${`Learn more about ${args.label}`}
10081
10030
  aria-describedby=${detailId}
10082
- @click=${(event) => this.handleHudActionClick(event, args.id)}
10031
+ @click=${(event) => this.handleHudActionClick(event, args.id, "learn_more")}
10083
10032
  @pointerdown=${(event) => event.stopPropagation()}
10084
10033
  >
10085
10034
  ${this.renderIcon("CircleHelp")}
@@ -10091,7 +10040,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10091
10040
  data-enabled=${args.connected ? "true" : "false"}
10092
10041
  aria-label=${args.connected ? `${args.label} is enabled` : `Open ${args.label} in Inspector`}
10093
10042
  ?disabled=${args.connected}
10094
- @click=${(event) => this.handleHudActionClick(event, args.id)}
10043
+ @click=${(event) => this.handleHudActionClick(event, args.id, "toggle")}
10095
10044
  @pointerdown=${(event) => event.stopPropagation()}
10096
10045
  >
10097
10046
  <span
@@ -10408,6 +10357,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10408
10357
  }
10409
10358
  getHomeModel() {
10410
10359
  const lastRuntimeEvent = this.flattenedEvents[0];
10360
+ const activeNotice = this.notificationFeed?.notifications.find((notice) => notice.id === this.notificationState.activeId);
10411
10361
  return buildHomeModel({
10412
10362
  intelligenceConnected: Boolean(this._core?.intelligence),
10413
10363
  threadsAvailable: this.areThreadEndpointsAvailable(),
@@ -10426,9 +10376,9 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10426
10376
  suggestionsOn: this._core?.suggestions === true,
10427
10377
  audioOn: this._core?.audioFileTranscriptionEnabled === true,
10428
10378
  websocketUrl: this._core?.intelligence?.wsUrl,
10429
- announcementPreviewText: this.announcementPreviewText ?? void 0,
10430
- announcementMarkdown: this.announcementMarkdown ?? void 0,
10431
- announcementHtml: this.announcementHtml ?? void 0,
10379
+ announcementPreviewText: activeNotice?.title,
10380
+ announcementMarkdown: activeNotice?.body,
10381
+ announcementHtml: activeNotice ? this.notificationDocuments.get(activeNotice.id) : void 0,
10432
10382
  intelligenceSignupUrl: this.getIntelligenceSignupUrl()
10433
10383
  });
10434
10384
  }
@@ -10464,7 +10414,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10464
10414
  data-inspector-whats-new-preview
10465
10415
  aria-label="Open What's New"
10466
10416
  style=${INTERACTIVE_FOCUS_BASE_STYLE}
10467
- @click=${() => this.handleMenuSelect(WHATS_NEW_MENU_KEY)}
10417
+ @click=${() => this.notificationState.activeId ? this.readNotification(this.notificationState.activeId) : this.handleMenuSelect(WHATS_NEW_MENU_KEY)}
10468
10418
  >
10469
10419
  <span class="inspector-whats-new-preview-copy">
10470
10420
  <span class="inspector-whats-new-preview-title">
@@ -10481,47 +10431,89 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10481
10431
  `;
10482
10432
  }
10483
10433
  renderWhatsNewView() {
10484
- const state = this.getWhatsNewState();
10485
- const news = this.getHomeModel().news;
10486
- const updatedAt = this.announcementTimestamp ? new Date(this.announcementTimestamp) : null;
10487
- const updatedLabel = updatedAt && !Number.isNaN(updatedAt.getTime()) ? new Intl.DateTimeFormat(void 0, {
10488
- 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",
10489
10438
  day: "numeric",
10490
10439
  year: "numeric"
10491
- }).format(updatedAt) : null;
10440
+ });
10492
10441
  return html`
10493
10442
  <div
10494
10443
  class="inspector-home inspector-whats-new"
10495
10444
  data-inspector-whats-new
10496
10445
  data-cpk-whats-new
10497
- data-cpk-whats-new-state=${state}
10446
+ data-cpk-whats-new-state=${this.getWhatsNewState()}
10498
10447
  >
10499
- <header class="inspector-whats-new-header">
10500
- <h1 class="inspector-home-title">What's New</h1>
10501
- ${updatedLabel ? html`
10502
- <p class="inspector-whats-new-updated">
10503
- Updated
10504
- <time datetime=${updatedAt?.toISOString()}
10505
- >${updatedLabel}</time
10506
- >
10507
- </p>
10508
- ` : nothing}
10509
- </header>
10510
10448
  <section class="inspector-home-news" aria-label="CopilotKit updates">
10511
- ${news.empty || !news.documentHtml ? html`
10512
- <article class="inspector-whats-new-empty">
10513
- <h2 class="inspector-home-card-title">${news.title}</h2>
10514
- <p class="inspector-home-card-copy">${news.previewText}</p>
10515
- </article>
10516
- ` : 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>
10517
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>
10518
10470
  <div
10519
10471
  class="announcement-content"
10520
10472
  @click=${this.handleAnnouncementContentClick}
10521
10473
  >
10522
- ${unsafeHTML(news.documentHtml)}
10474
+ ${unsafeHTML(this.notificationDocuments.get(selected.id) ?? "")}
10523
10475
  </div>
10524
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>`}
10525
10517
  `}
10526
10518
  </section>
10527
10519
  </div>
@@ -13004,15 +12996,15 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
13004
12996
  }
13005
12997
  trackWhatsNewClickedOnce(opts) {
13006
12998
  if (this.runtimeStatus !== CopilotKitCoreRuntimeConnectionStatus.Connected || this.core?.telemetryDisabled) return;
13007
- const id = this.announcementTimestamp;
12999
+ const id = this.announcementId;
13008
13000
  if (!id) return;
13009
13001
  const key = `${id}:${opts.cta}`;
13010
13002
  if (this.clickedBannerIds.has(key)) return;
13011
13003
  this.clickedBannerIds.add(key);
13012
13004
  trackWhatsNewClicked({
13013
13005
  banner_id: id,
13014
- cta: opts.cta,
13015
- cta_label: this.announcementCtaLabel ?? void 0
13006
+ notification_id: id,
13007
+ cta: opts.cta
13016
13008
  });
13017
13009
  }
13018
13010
  trackThreadsViewStateOnce(state) {
@@ -15524,11 +15516,9 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
15524
15516
  else if (!wasArmed) this.requestUpdate();
15525
15517
  }
15526
15518
  clearNewsSignal() {
15527
- if (!this.newsSignalArmed) return;
15528
- this.newsSignalArmed = false;
15529
- if (this.announcementTimestamp) saveAnnouncementReadTimestamp(this.announcementTimestamp);
15530
- this.retireSignal(NEWS_SIGNAL_ID);
15531
- this.requestUpdate();
15519
+ if (!this.notificationState.activeId) return;
15520
+ this.notificationState = acknowledgeNotification(this.notificationState, this.notificationState.activeId);
15521
+ this.refreshNotifications();
15532
15522
  }
15533
15523
  /**
15534
15524
  * Requests one beat for a signal, running it now or deferring it.
@@ -15561,7 +15551,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
15561
15551
  this.pendingPulseSignal = null;
15562
15552
  this.pulsingSignal = key;
15563
15553
  if (isWiringErrorKey(key)) this.errorBeatSpent = true;
15564
- 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);
15565
15555
  this.beginGestureTail(key);
15566
15556
  this.requestUpdate();
15567
15557
  if (typeof window === "undefined") return;
@@ -15861,14 +15851,14 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
15861
15851
  }
15862
15852
  maybeTrackNewsSignalViewed() {
15863
15853
  if (!this.newsSignalArmed || this.pulsingSignal !== NEWS_SIGNAL_ID || this.isOpen || typeof document === "undefined" || document.visibilityState !== "visible") return;
15864
- const id = this.announcementTimestamp;
15854
+ const id = (this.notificationFeed?.notifications.find((n) => n.id === this.notificationState.activeId))?.id;
15865
15855
  if (!id || this.viewedNewsSignalIds.has(id)) return;
15866
15856
  this.viewedNewsSignalIds.add(id);
15867
15857
  this.pendingNewsSignalViewed = {
15868
15858
  banner_id: id,
15859
+ notification_id: id,
15869
15860
  surface: "launcher",
15870
- presentation: typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ? "reduced_motion" : "animated",
15871
- cta_label: this.announcementCtaLabel ?? void 0
15861
+ presentation: typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ? "reduced_motion" : "animated"
15872
15862
  };
15873
15863
  this.flushPendingWhatsNewTelemetry();
15874
15864
  }
@@ -15886,7 +15876,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
15886
15876
  getVisibleBannerSurface() {
15887
15877
  if (!this.isOpen || this.settingsOpen) return null;
15888
15878
  if (this.selectedMenu !== WHATS_NEW_MENU_KEY) return null;
15889
- return this.announcementHtml ? "whats_new" : null;
15879
+ return this.selectedNotificationId && this.announcementHtml ? "whats_new" : null;
15890
15880
  }
15891
15881
  /**
15892
15882
  * The single condition that retires the news signal: What's new has
@@ -15905,14 +15895,13 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
15905
15895
  maybeCompleteWhatsNewView() {
15906
15896
  if (!this.getVisibleBannerSurface()) return;
15907
15897
  this.maybeTrackWhatsNewViewed();
15908
- this.clearNewsSignal();
15909
15898
  }
15910
15899
  /**
15911
15900
  * Records a `whats_new_viewed` impression for whichever surface is
15912
15901
  * currently visible, once per announcement per surface.
15913
15902
  */
15914
15903
  maybeTrackWhatsNewViewed() {
15915
- const id = this.announcementTimestamp;
15904
+ const id = this.announcementId;
15916
15905
  if (!id) return;
15917
15906
  const surface = this.getVisibleBannerSurface();
15918
15907
  if (!surface) return;
@@ -15922,12 +15911,13 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
15922
15911
  this.viewedBannerSurfaces.add(key);
15923
15912
  this.pendingBannerViewed.push({
15924
15913
  banner_id: id,
15925
- surface,
15926
- cta_label: this.announcementCtaLabel ?? void 0
15914
+ notification_id: id,
15915
+ surface
15927
15916
  });
15928
15917
  this.flushPendingWhatsNewTelemetry();
15929
15918
  }
15930
15919
  flushPendingWhatsNewTelemetry() {
15920
+ this.flushPendingHudTelemetry();
15931
15921
  if (this.pendingBannerViewed.length === 0 && !this.pendingNewsSignalViewed) return;
15932
15922
  if (this.core?.telemetryDisabled) {
15933
15923
  this.pendingBannerViewed = [];
@@ -15945,32 +15935,58 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
15945
15935
  }
15946
15936
  }
15947
15937
  ensureAnnouncementLoading() {
15948
- 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;
15949
15939
  this.announcementPromise = this.fetchAnnouncement();
15950
15940
  }
15951
15941
  async fetchAnnouncement() {
15952
15942
  try {
15953
- const response = await fetch(ANNOUNCEMENT_URL, { cache: "no-cache" });
15954
- if (!response.ok) throw new Error(`Failed to load announcement (${response.status})`);
15955
- const data = await response.json();
15956
- const timestamp = typeof data?.timestamp === "string" ? data.timestamp : null;
15957
- const previewText = typeof data?.previewText === "string" ? data.previewText : null;
15958
- const markdown = typeof data?.announcement === "string" ? data.announcement : null;
15959
- const ctaLabel = typeof data?.cta_label === "string" ? data.cta_label : null;
15960
- if (!timestamp || !markdown) throw new Error("Malformed announcement payload");
15961
- this.announcementTimestamp = timestamp;
15962
- this.announcementPreviewText = previewText ?? "";
15963
- this.announcementMarkdown = markdown;
15964
- this.announcementCtaLabel = ctaLabel;
15965
- this.announcementHtml = await this.convertMarkdownToHtml(markdown);
15966
- this.announcementLoaded = true;
15967
- if (this.announcementHtml && loadAnnouncementReadTimestamp() !== timestamp) this.armNewsSignal({ pulse: loadAnnouncementPulsedTimestamp() !== timestamp });
15968
- this.requestUpdate();
15969
- } catch (error) {
15970
- console.warn("[CopilotKit Inspector] Failed to load announcement", error);
15971
- this.announcementLoaded = true;
15972
- 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);
15973
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);
15974
15990
  }
15975
15991
  async convertMarkdownToHtml(markdown) {
15976
15992
  const renderer = new marked.Renderer();
@@ -16045,8 +16061,9 @@ function defineWebInspector(registry = globalThis.customElements) {
16045
16061
  * auto-attachment first prevents `connectedCallback` from briefly selecting a
16046
16062
  * different global core.
16047
16063
  */
16048
- function configureWebInspectorElement(inspector, core) {
16064
+ function configureWebInspectorElement(inspector, core, notificationContext = { development: false }) {
16049
16065
  inspector.autoAttachCore = false;
16066
+ inspector.notificationContext = notificationContext;
16050
16067
  inspector.core = core;
16051
16068
  return inspector;
16052
16069
  }