From 18f34fb35f65e9c9bb477016f3c981ef5b5be9cd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2026 11:53:49 +0000 Subject: [PATCH 1/2] chore(release): bump HyperDX app/package versions --- .../alert-configured-channels-direct.md | 5 - .changeset/alert-detail-ux.md | 15 --- .../alert-per-target-notification-timings.md | 11 --- .changeset/alerts-multi-target-display.md | 7 -- .changeset/alerts-row-indicator-alignment.md | 7 -- .changeset/alerts-summary-ux.md | 12 --- .changeset/confirm-close-filter-editor.md | 5 - .changeset/dashboard-filter-value-format.md | 7 -- .changeset/detached-inline-alerts-backend.md | 6 -- .changeset/early-tables-try.md | 6 -- .changeset/enable-dashboard-variables.md | 5 - .../expand-variables-in-drilldown-links.md | 5 - .changeset/fair-walls-listen.md | 6 -- .changeset/fix-common-utils-dts-aliases.md | 5 - .changeset/fix-mcp-non-string-enum.md | 5 - .changeset/fix-rrweb-chunk-ordering.md | 5 - .../lucene-variable-field-distribution.md | 6 -- .changeset/metrics-explorer-modal.md | 11 --- .changeset/ninety-rockets-rest.md | 6 -- .changeset/popular-badgers-exist.md | 6 -- .changeset/promql-dashboard-variables.md | 6 -- .../refactor-dashboard-filters-modal.md | 5 - .changeset/release-markers-query-error.md | 5 - .changeset/reveal-snippet-component.md | 5 - .changeset/reverse-span-links.md | 5 - .changeset/virtualize-alerts-list.md | 5 - .changeset/whats-new-inline.md | 16 ---- .env | 4 +- packages/api/CHANGELOG.md | 29 ++++++ packages/api/package.json | 4 +- packages/app/CHANGELOG.md | 92 +++++++++++++++++++ packages/app/package.json | 4 +- packages/cli/package.json | 2 +- packages/common-utils/CHANGELOG.md | 22 +++++ packages/common-utils/package.json | 2 +- packages/hdx-eval/CHANGELOG.md | 6 ++ packages/hdx-eval/package.json | 2 +- packages/otel-collector/CHANGELOG.md | 2 + packages/otel-collector/package.json | 2 +- yarn.lock | 8 +- 40 files changed, 165 insertions(+), 202 deletions(-) delete mode 100644 .changeset/alert-configured-channels-direct.md delete mode 100644 .changeset/alert-detail-ux.md delete mode 100644 .changeset/alert-per-target-notification-timings.md delete mode 100644 .changeset/alerts-multi-target-display.md delete mode 100644 .changeset/alerts-row-indicator-alignment.md delete mode 100644 .changeset/alerts-summary-ux.md delete mode 100644 .changeset/confirm-close-filter-editor.md delete mode 100644 .changeset/dashboard-filter-value-format.md delete mode 100644 .changeset/detached-inline-alerts-backend.md delete mode 100644 .changeset/early-tables-try.md delete mode 100644 .changeset/enable-dashboard-variables.md delete mode 100644 .changeset/expand-variables-in-drilldown-links.md delete mode 100644 .changeset/fair-walls-listen.md delete mode 100644 .changeset/fix-common-utils-dts-aliases.md delete mode 100644 .changeset/fix-mcp-non-string-enum.md delete mode 100644 .changeset/fix-rrweb-chunk-ordering.md delete mode 100644 .changeset/lucene-variable-field-distribution.md delete mode 100644 .changeset/metrics-explorer-modal.md delete mode 100644 .changeset/ninety-rockets-rest.md delete mode 100644 .changeset/popular-badgers-exist.md delete mode 100644 .changeset/promql-dashboard-variables.md delete mode 100644 .changeset/refactor-dashboard-filters-modal.md delete mode 100644 .changeset/release-markers-query-error.md delete mode 100644 .changeset/reveal-snippet-component.md delete mode 100644 .changeset/reverse-span-links.md delete mode 100644 .changeset/virtualize-alerts-list.md delete mode 100644 .changeset/whats-new-inline.md diff --git a/.changeset/alert-configured-channels-direct.md b/.changeset/alert-configured-channels-direct.md deleted file mode 100644 index d7ab6e6ef6..0000000000 --- a/.changeset/alert-configured-channels-direct.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperdx/api': patch ---- - -Build alert notifications for an alert's configured channels directly, instead of encoding them as `@webhook-` mention strings and parsing them back out. That round-trip carried only `type` and `webhookId`, and it appended the channels *after* whatever the user wrote in the message body — so a body containing `MAX_NOTIFICATIONS_PER_EVENT` mentions consumed every slot of the per-event cap and the alert's own configured channel, the one target it was set up to notify, was silently never reached. Configured channels are now queued first and are exempt from that cap, which only ever meant to bound ad hoc mentions; `channels` is already bounded by `MAX_ALERT_CHANNELS`. Mentions written into the message body are unchanged, still capped, and still deduplicated against the configured channels so naming one twice notifies it once. This also removes the lossiness that prevented a channel from carrying any field beyond its webhook id through to delivery, which downstream forks with richer channel types (e.g. an `email` channel, or a Slack-app channel that also needs a Slack channel id) could not work around. `getDefaultExternalActions` is removed, as nothing needs the mention-string form of a configured channel any more. diff --git a/.changeset/alert-detail-ux.md b/.changeset/alert-detail-ux.md deleted file mode 100644 index 44b19b6f4b..0000000000 --- a/.changeset/alert-detail-ux.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@hyperdx/app': minor ---- - -feat(alerts): tidy the alert detail header and its properties block - -Edit, Delete and Terraform export move behind the same overflow menu the -alerts list uses, so the header no longer spreads four buttons across the top -and both surfaces offer the same actions. The link to what the alert watches -becomes an icon beside the alert name, where it reads as part of the alert's -identity rather than another action. - -The properties block splits configuration from provenance: the creator now -sits with the created and updated timestamps in a dimmed line beneath, instead -of competing with the alert's settings. diff --git a/.changeset/alert-per-target-notification-timings.md b/.changeset/alert-per-target-notification-timings.md deleted file mode 100644 index d685639604..0000000000 --- a/.changeset/alert-per-target-notification-timings.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@hyperdx/api': minor -'@hyperdx/app': minor -'@hyperdx/common-utils': minor ---- - -Record and show which notification target an evaluation's delivery time went to. `webhookDurationMs` was a single number covering the whole delivery, and because targets are dispatched concurrently the slowest one sets it — so a multi-target alert reported a figure with no way to tell which webhook was responsible, or that the other targets were fine. - -Each dispatch is now timed individually and aggregated per target across the evaluation, since a grouped alert notifies the same target once per firing group and again on resolve. One entry per distinct target carries its webhook id, display name, summed duration, how many dispatches it took, and how many failed. The evaluation history's "Notification duration" cell expands in place to show the breakdown. - -Stored per evaluation rather than per dispatch: a 50-group alert notifying 10 targets would otherwise write 500 entries onto every history row. The array is capped at `ALERT_NOTIFICATION_TARGETS_LIMIT` and sorted slowest-first, so the cap drops the least interesting rows. Records written before this change keep rendering their total with nothing to expand. diff --git a/.changeset/alerts-multi-target-display.md b/.changeset/alerts-multi-target-display.md deleted file mode 100644 index f7e3a54ec6..0000000000 --- a/.changeset/alerts-multi-target-display.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@hyperdx/app': patch ---- - -Show every notification target an alert is configured with. The alerts page rows and the alert detail header only ever rendered the legacy singular `channel`, so an alert notifying three webhooks read as if it notified one, and the label was the generic "Webhook" rather than the webhook's name. Both surfaces now resolve all of an alert's channels: the detail page names each target with its service icon (Slack, incident.io, generic), keeping the first two inline and collapsing the rest into a `+N more` tooltip, while the alerts-page rows show the icons only with the names on hover, since spelling out up to ten names wrapped the row into an unreadable block. The hover-only names are also placed in the accessibility tree rather than left to an `aria-label` on a role-less wrapper. - -The evaluation history's "Webhook Duration" column is renamed "Notification duration" and gains a tooltip. The value was always the wall time of the whole delivery, which fans out to every target concurrently, so a single slow webhook sets the figure — but the singular heading read as one webhook's latency. Per-target attribution is not available yet; nothing records it. The remaining column headings are corrected to sentence case. diff --git a/.changeset/alerts-row-indicator-alignment.md b/.changeset/alerts-row-indicator-alignment.md deleted file mode 100644 index 79cde463d2..0000000000 --- a/.changeset/alerts-row-indicator-alignment.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@hyperdx/app': patch ---- - -Give every alerts-page row the same trailing controls. The row's Terraform import button, source link, and acknowledgement button were each independently conditional — import needs a saved-search alert *and* the export feature, and `AckAlert` renders nothing for an OK alert that has never been acknowledged — so the flex row collapsed differently per alert and no two rows lined up. The conditional actions move into an overflow menu that always renders, alongside a new "Delete alert" item, and the acknowledgement button gets a reserved slot so its absence no longer shifts everything to its left. - -The Terraform snippet building is extracted into a `useTerraformSnippets` hook so the row menu can present the same snippets in a modal without duplicating it, or moving `ResourceTerraformPopover` off the two other pages that use it. Snippets are still built lazily on open, which is what keeps `window.location.origin` out of the render path and the ClickStack static export building. diff --git a/.changeset/alerts-summary-ux.md b/.changeset/alerts-summary-ux.md deleted file mode 100644 index 70523d348d..0000000000 --- a/.changeset/alerts-summary-ux.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@hyperdx/app': minor ---- - -feat(alerts): edit from the alerts list, filter by alert source, and label the source icons - -The alerts page row menu now opens the alert editor directly, so changing a -threshold no longer means navigating to the alert first. The source icon on -each row gets a tooltip and accessible label naming what it watches ("Saved -search" / "Dashboard tile"), and a new filter narrows the list by that source -— free-text search matches it too, so typing "tile" works without touching the -dropdown. Team settings tabs gain icons. diff --git a/.changeset/confirm-close-filter-editor.md b/.changeset/confirm-close-filter-editor.md deleted file mode 100644 index 6f4ff017af..0000000000 --- a/.changeset/confirm-close-filter-editor.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperdx/app': patch ---- - -fix: Confirm before discarding unsaved changes when closing the dashboard filter editor diff --git a/.changeset/dashboard-filter-value-format.md b/.changeset/dashboard-filter-value-format.md deleted file mode 100644 index f7f04b8d50..0000000000 --- a/.changeset/dashboard-filter-value-format.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@hyperdx/common-utils': patch -'@hyperdx/api': patch -'@hyperdx/app': patch ---- - -feat: Persist variable-keyed dashboard filter value state diff --git a/.changeset/detached-inline-alerts-backend.md b/.changeset/detached-inline-alerts-backend.md deleted file mode 100644 index bf53dd7a04..0000000000 --- a/.changeset/detached-inline-alerts-backend.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperdx/common-utils': minor -'@hyperdx/api': minor ---- - -Add a new `inline` alert source that persists its own chart config directly on the alert, so alerts no longer require a saved search (logs) or a dashboard tile (metrics). The config is the same shape a dashboard tile stores — builder configs on log/trace/metric sources plus raw SQL (Line/Stacked Bar/Number display types); PromQL is rejected. The internal alerts API accepts and returns the new source, and the check-alerts task evaluates inline alerts through the same code path as tile alerts (including group-by and multi-window behavior). Notifications for inline alerts link to the chart explorer seeded with the alert's config over the alerting window, and default their title to the config's name. Backend only — the creation/edit UI and external API v2 support land separately. diff --git a/.changeset/early-tables-try.md b/.changeset/early-tables-try.md deleted file mode 100644 index 04c19de395..0000000000 --- a/.changeset/early-tables-try.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@hyperdx/common-utils": patch -"@hyperdx/app": patch ---- - -feat: Add completions for PromQL variables diff --git a/.changeset/enable-dashboard-variables.md b/.changeset/enable-dashboard-variables.md deleted file mode 100644 index eddcc8634d..0000000000 --- a/.changeset/enable-dashboard-variables.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperdx/app': patch ---- - -feat: Enable dashboard variables for everyone by removing the feature toggle diff --git a/.changeset/expand-variables-in-drilldown-links.md b/.changeset/expand-variables-in-drilldown-links.md deleted file mode 100644 index 77714bdb61..0000000000 --- a/.changeset/expand-variables-in-drilldown-links.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperdx/app': patch ---- - -fix: Expand variables prior to navigating to search page via drill-down diff --git a/.changeset/fair-walls-listen.md b/.changeset/fair-walls-listen.md deleted file mode 100644 index 6684aefac0..0000000000 --- a/.changeset/fair-walls-listen.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@hyperdx/hdx-eval": patch -"@hyperdx/api": patch ---- - -feat(mcp): improve metric discovery, add quiet-saturation eval scenario diff --git a/.changeset/fix-common-utils-dts-aliases.md b/.changeset/fix-common-utils-dts-aliases.md deleted file mode 100644 index 98e1d76115..0000000000 --- a/.changeset/fix-common-utils-dts-aliases.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperdx/common-utils': patch ---- - -fix: Fix `@/*` aliases leaking into the local type declarations diff --git a/.changeset/fix-mcp-non-string-enum.md b/.changeset/fix-mcp-non-string-enum.md deleted file mode 100644 index 6c308cc752..0000000000 --- a/.changeset/fix-mcp-non-string-enum.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperdx/api': patch ---- - -Advertise the MCP quantile `level` field as a string enum so Gemini-backed clients can use the server at all. `z.union([z.literal(0.5), ...])` renders as `{ "type": "number", "enum": [0.5, 0.9, 0.95, 0.99] }`, and Gemini's function declarations only accept `enum` alongside `type: "string"` — so a client that forwards MCP tool schemas to the provider had its entire tool list rejected because of this one field, surfacing as a generic "trouble connecting to the model provider" error that named neither the tool nor the property. Affected `clickstack_timeseries`, `clickstack_table`, `clickstack_save_dashboard` and `clickstack_patch_dashboard`. Only the advertised wire type changes: numeric input is still accepted for callers working from a cached schema, the value is coerced back to a number before any consumer sees it, and out-of-set values are still rejected. The external REST API's own `level` contract is untouched. A new test asserts that no advertised tool schema carries a non-string `enum` or an array-form `items`, complementing the draft-2020-12 metaschema check. diff --git a/.changeset/fix-rrweb-chunk-ordering.md b/.changeset/fix-rrweb-chunk-ordering.md deleted file mode 100644 index f9e5418f12..0000000000 --- a/.changeset/fix-rrweb-chunk-ordering.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperdx/app': patch ---- - -Fix session replays rendering empty, unstyled, or freezing mid-session when a recorded rrweb event exceeds the recorder's ~950KB chunk size. All chunks of a split event share one timestamp, and the replay query ordered by timestamp alone, so ClickHouse could return chunks in arbitrary order — the scrambled reassembly failed to parse and the event (often the full DOM snapshot carrying all inlined CSS) was silently dropped. The replay stream is now ordered deterministically (`rr-web.offset` and `rr-web.chunk` tiebreaks), chunks are reassembled by explicit chunk index per event, and dropped events are reported in the console and flagged with a warning indicator in the player instead of being swallowed. Existing recordings are replayed correctly without re-ingestion. Replaced replay streams are now also cancelled instead of streaming to completion in the background, and the player imports `Replayer` from `@rrweb/replay` (the replay-only package rrweb recommends over the deprecated combined `rrweb` package). diff --git a/.changeset/lucene-variable-field-distribution.md b/.changeset/lucene-variable-field-distribution.md deleted file mode 100644 index 568c498dc7..0000000000 --- a/.changeset/lucene-variable-field-distribution.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperdx/app': patch -'@hyperdx/common-utils': patch ---- - -feat: Distribute exact-match lucene variable references diff --git a/.changeset/metrics-explorer-modal.md b/.changeset/metrics-explorer-modal.md deleted file mode 100644 index 5606aef3cd..0000000000 --- a/.changeset/metrics-explorer-modal.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@hyperdx/app': minor ---- - -Add a metrics explorer to the chart editor, so you no longer have to already know a metric's name to chart it. A browse control beside the metric select opens a modal with a prefix hierarchy over the metric namespace — `system` → `cpu` → `utilization` — plus search across every name and description the source is reporting. Each row carries the metric's kind and its description, and the detail pane shows the unit (rendered from its UCUM code), reporting services, and tag keys drilling into their values. Previously the picker was a flat 3,000-entry dropdown and that metadata only appeared after you had already committed to a metric. - -Names are split per metric: on `.` when the name has one (OpenTelemetry), otherwise on `_` (Prometheus exporters). Deciding per name rather than per source matters in practice — a real deployment carries thousands of underscore-style collector self-telemetry names alongside dozens of dotted application metrics, and a single source-wide separator flattened whichever family was outnumbered. Single-child chains collapse so the tree does not become a corridor, and the unfiltered tree is never truncated, so no namespace can go missing. - -While browsing a metric's tags you can stage filters and group-bys the same way the chart editor's inline attribute panel allows; they are shown as removable chips and applied together with the metric. Applying also sets an aggregation appropriate to the kind — average for a gauge, sum for a counter, p95 for a histogram — instead of inheriting whatever the previous series used. Both replace rather than merge, since they were written against the newly chosen metric: staged filters replace the series condition, and staged group-bys replace the chart's. - -The chart editor's inline attribute panel now also shows the metric's kind. Only chartable kinds are listed (gauge, sum, histogram, exponential histogram); `summary` is omitted because the query renderer cannot translate it. The browser is a self-contained component, so the modal is one shell around it rather than the only possible home. diff --git a/.changeset/ninety-rockets-rest.md b/.changeset/ninety-rockets-rest.md deleted file mode 100644 index b94f63242c..0000000000 --- a/.changeset/ninety-rockets-rest.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@hyperdx/common-utils": patch -"@hyperdx/app": patch ---- - -feat: Show warnings for invalid promql variable usage diff --git a/.changeset/popular-badgers-exist.md b/.changeset/popular-badgers-exist.md deleted file mode 100644 index e30dec696d..0000000000 --- a/.changeset/popular-badgers-exist.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@hyperdx/common-utils": patch -"@hyperdx/app": patch ---- - -feat: Add generated PromQL preview diff --git a/.changeset/promql-dashboard-variables.md b/.changeset/promql-dashboard-variables.md deleted file mode 100644 index 44c4244e05..0000000000 --- a/.changeset/promql-dashboard-variables.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperdx/common-utils': patch -'@hyperdx/app': patch ---- - -feat: Support variable substitution in PromQL charts diff --git a/.changeset/refactor-dashboard-filters-modal.md b/.changeset/refactor-dashboard-filters-modal.md deleted file mode 100644 index 73b32e87dd..0000000000 --- a/.changeset/refactor-dashboard-filters-modal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperdx/app': patch ---- - -refactor: Split `DashboardFiltersModal` into smaller components diff --git a/.changeset/release-markers-query-error.md b/.changeset/release-markers-query-error.md deleted file mode 100644 index 721ca6d37c..0000000000 --- a/.changeset/release-markers-query-error.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperdx/app': patch ---- - -Release markers now show a distinct "couldn't load release markers" notification when the underlying query fails (e.g. a source's version expression references a column, such as `ResourceAttributes`, that the table doesn't have), instead of silently rendering no markers indistinguishable from "no releases found in this time range." diff --git a/.changeset/reveal-snippet-component.md b/.changeset/reveal-snippet-component.md deleted file mode 100644 index 02f6d65b24..0000000000 --- a/.changeset/reveal-snippet-component.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperdx/app': patch ---- - -feat(app): mask secrets in API key and MCP install snippets with a shared reveal-to-copy component diff --git a/.changeset/reverse-span-links.md b/.changeset/reverse-span-links.md deleted file mode 100644 index 6b942228ff..0000000000 --- a/.changeset/reverse-span-links.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperdx/app': minor ---- - -Show reverse span links and resolved span-link details in the span detail Overview panel. diff --git a/.changeset/virtualize-alerts-list.md b/.changeset/virtualize-alerts-list.md deleted file mode 100644 index 0d1132a231..0000000000 --- a/.changeset/virtualize-alerts-list.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperdx/app': patch ---- - -perf: Virtualize the alerts page list diff --git a/.changeset/whats-new-inline.md b/.changeset/whats-new-inline.md deleted file mode 100644 index 09125051eb..0000000000 --- a/.changeset/whats-new-inline.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'@hyperdx/app': minor ---- - -feat: rebuild the Help menu's "What's new" around the release notes. Replaces -the full-changelog modal with an inline section, a "View all releases" drawer, -and a sparkle on the Help icon when the running version hasn't been acknowledged -in this browser. - -Everything shown now comes from the root CHANGELOG.md, the release-level summary -written during each release: its headline and opening paragraph lead the -release, breaking changes and new features are listed individually and badged -apart, and the remaining sections are summed up as counts linking to that -release's section of the changelog. Nothing is hand-authored in the app. The -whole changelog is no longer shipped as a fetched asset either — next.config.mjs -parses it at build time and emits a small public/whats-new.json instead. diff --git a/.env b/.env index cdd88e4a54..b4e07529eb 100644 --- a/.env +++ b/.env @@ -8,8 +8,8 @@ NEXT_ALL_IN_ONE_IMAGE_NAME_DOCKERHUB=clickhouse/clickstack-all-in-one ALL_IN_ONE_IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx-all-in-one NEXT_OTEL_COLLECTOR_IMAGE_NAME_DOCKERHUB=clickhouse/clickstack-otel-collector OTEL_COLLECTOR_IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx-otel-collector -CODE_VERSION=2.36.0 -IMAGE_VERSION_SUB_TAG=.36.0 +CODE_VERSION=2.37.0 +IMAGE_VERSION_SUB_TAG=.37.0 IMAGE_VERSION=2 IMAGE_NIGHTLY_TAG=2-nightly IMAGE_LATEST_TAG=latest diff --git a/packages/api/CHANGELOG.md b/packages/api/CHANGELOG.md index 57d5357649..75f907e29d 100644 --- a/packages/api/CHANGELOG.md +++ b/packages/api/CHANGELOG.md @@ -1,5 +1,34 @@ # @hyperdx/api +## 2.37.0 + +### Minor Changes + +- 0558f77e: Record and show which notification target an evaluation's delivery time went to. `webhookDurationMs` was a single number covering the whole delivery, and because targets are dispatched concurrently the slowest one sets it — so a multi-target alert reported a figure with no way to tell which webhook was responsible, or that the other targets were fine. + + Each dispatch is now timed individually and aggregated per target across the evaluation, since a grouped alert notifies the same target once per firing group and again on resolve. One entry per distinct target carries its webhook id, display name, summed duration, how many dispatches it took, and how many failed. The evaluation history's "Notification duration" cell expands in place to show the breakdown. + + Stored per evaluation rather than per dispatch: a 50-group alert notifying 10 targets would otherwise write 500 entries onto every history row. The array is capped at `ALERT_NOTIFICATION_TARGETS_LIMIT` and sorted slowest-first, so the cap drops the least interesting rows. Records written before this change keep rendering their total with nothing to expand. + +- df4a7a55: Add a new `inline` alert source that persists its own chart config directly on the alert, so alerts no longer require a saved search (logs) or a dashboard tile (metrics). The config is the same shape a dashboard tile stores — builder configs on log/trace/metric sources plus raw SQL (Line/Stacked Bar/Number display types); PromQL is rejected. The internal alerts API accepts and returns the new source, and the check-alerts task evaluates inline alerts through the same code path as tile alerts (including group-by and multi-window behavior). Notifications for inline alerts link to the chart explorer seeded with the alert's config over the alerting window, and default their title to the config's name. Backend only — the creation/edit UI and external API v2 support land separately. + +### Patch Changes + +- 3c81bb96: Build alert notifications for an alert's configured channels directly, instead of encoding them as `@webhook-` mention strings and parsing them back out. That round-trip carried only `type` and `webhookId`, and it appended the channels _after_ whatever the user wrote in the message body — so a body containing `MAX_NOTIFICATIONS_PER_EVENT` mentions consumed every slot of the per-event cap and the alert's own configured channel, the one target it was set up to notify, was silently never reached. Configured channels are now queued first and are exempt from that cap, which only ever meant to bound ad hoc mentions; `channels` is already bounded by `MAX_ALERT_CHANNELS`. Mentions written into the message body are unchanged, still capped, and still deduplicated against the configured channels so naming one twice notifies it once. This also removes the lossiness that prevented a channel from carrying any field beyond its webhook id through to delivery, which downstream forks with richer channel types (e.g. an `email` channel, or a Slack-app channel that also needs a Slack channel id) could not work around. `getDefaultExternalActions` is removed, as nothing needs the mention-string form of a configured channel any more. +- f11038ef: feat: Persist variable-keyed dashboard filter value state +- 892cc653: feat(mcp): improve metric discovery, add quiet-saturation eval scenario +- b52a6fa8: Advertise the MCP quantile `level` field as a string enum so Gemini-backed clients can use the server at all. `z.union([z.literal(0.5), ...])` renders as `{ "type": "number", "enum": [0.5, 0.9, 0.95, 0.99] }`, and Gemini's function declarations only accept `enum` alongside `type: "string"` — so a client that forwards MCP tool schemas to the provider had its entire tool list rejected because of this one field, surfacing as a generic "trouble connecting to the model provider" error that named neither the tool nor the property. Affected `clickstack_timeseries`, `clickstack_table`, `clickstack_save_dashboard` and `clickstack_patch_dashboard`. Only the advertised wire type changes: numeric input is still accepted for callers working from a cached schema, the value is coerced back to a number before any consumer sees it, and out-of-set values are still rejected. The external REST API's own `level` contract is untouched. A new test asserts that no advertised tool schema carries a non-string `enum` or an array-form `items`, complementing the draft-2020-12 metaschema check. +- Updated dependencies [0558f77e] +- Updated dependencies [f11038ef] +- Updated dependencies [df4a7a55] +- Updated dependencies [f9f7d5bc] +- Updated dependencies [82852c3a] +- Updated dependencies [de9038e7] +- Updated dependencies [7662fae8] +- Updated dependencies [93b51b13] +- Updated dependencies [64326d09] + - @hyperdx/common-utils@0.28.0 + ## 2.36.0 ### Minor Changes diff --git a/packages/api/package.json b/packages/api/package.json index 3aec8e9202..df39b905ee 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@hyperdx/api", - "version": "2.36.0", + "version": "2.37.0", "license": "MIT", "private": true, "engines": { @@ -21,7 +21,7 @@ "@braintree/sanitize-url": "^7.1.1", "@clickhouse/client-common": "1.23.0-head.fae5998.1", "@esm2cjs/p-queue": "^7.3.0", - "@hyperdx/common-utils": "^0.27.0", + "@hyperdx/common-utils": "^0.28.0", "@hyperdx/node-opentelemetry": "^0.9.0", "@hyperdx/passport-local-mongoose": "^9.0.1", "@modelcontextprotocol/sdk": "^1.27.1", diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 2b9e80263f..6d062630cf 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,97 @@ # @hyperdx/app +## 2.37.0 + +### Minor Changes + +- db6ee45f: feat(alerts): tidy the alert detail header and its properties block + + Edit, Delete and Terraform export move behind the same overflow menu the + alerts list uses, so the header no longer spreads four buttons across the top + and both surfaces offer the same actions. The link to what the alert watches + becomes an icon beside the alert name, where it reads as part of the alert's + identity rather than another action. + + The properties block splits configuration from provenance: the creator now + sits with the created and updated timestamps in a dimmed line beneath, instead + of competing with the alert's settings. + +- 0558f77e: Record and show which notification target an evaluation's delivery time went to. `webhookDurationMs` was a single number covering the whole delivery, and because targets are dispatched concurrently the slowest one sets it — so a multi-target alert reported a figure with no way to tell which webhook was responsible, or that the other targets were fine. + + Each dispatch is now timed individually and aggregated per target across the evaluation, since a grouped alert notifies the same target once per firing group and again on resolve. One entry per distinct target carries its webhook id, display name, summed duration, how many dispatches it took, and how many failed. The evaluation history's "Notification duration" cell expands in place to show the breakdown. + + Stored per evaluation rather than per dispatch: a 50-group alert notifying 10 targets would otherwise write 500 entries onto every history row. The array is capped at `ALERT_NOTIFICATION_TARGETS_LIMIT` and sorted slowest-first, so the cap drops the least interesting rows. Records written before this change keep rendering their total with nothing to expand. + +- db6ee45f: feat(alerts): edit from the alerts list, filter by alert source, and label the source icons + + The alerts page row menu now opens the alert editor directly, so changing a + threshold no longer means navigating to the alert first. The source icon on + each row gets a tooltip and accessible label naming what it watches ("Saved + search" / "Dashboard tile"), and a new filter narrows the list by that source + — free-text search matches it too, so typing "tile" works without touching the + dropdown. Team settings tabs gain icons. + +- 8f3126f0: Add a metrics explorer to the chart editor, so you no longer have to already know a metric's name to chart it. A browse control beside the metric select opens a modal with a prefix hierarchy over the metric namespace — `system` → `cpu` → `utilization` — plus search across every name and description the source is reporting. Each row carries the metric's kind and its description, and the detail pane shows the unit (rendered from its UCUM code), reporting services, and tag keys drilling into their values. Previously the picker was a flat 3,000-entry dropdown and that metadata only appeared after you had already committed to a metric. + + Names are split per metric: on `.` when the name has one (OpenTelemetry), otherwise on `_` (Prometheus exporters). Deciding per name rather than per source matters in practice — a real deployment carries thousands of underscore-style collector self-telemetry names alongside dozens of dotted application metrics, and a single source-wide separator flattened whichever family was outnumbered. Single-child chains collapse so the tree does not become a corridor, and the unfiltered tree is never truncated, so no namespace can go missing. + + While browsing a metric's tags you can stage filters and group-bys the same way the chart editor's inline attribute panel allows; they are shown as removable chips and applied together with the metric. Applying also sets an aggregation appropriate to the kind — average for a gauge, sum for a counter, p95 for a histogram — instead of inheriting whatever the previous series used. Both replace rather than merge, since they were written against the newly chosen metric: staged filters replace the series condition, and staged group-bys replace the chart's. + + The chart editor's inline attribute panel now also shows the metric's kind. Only chartable kinds are listed (gauge, sum, histogram, exponential histogram); `summary` is omitted because the query renderer cannot translate it. The browser is a self-contained component, so the modal is one shell around it rather than the only possible home. + +- 6b7ca4ab: Show reverse span links and resolved span-link details in the span detail Overview panel. +- e0d29328: feat: rebuild the Help menu's "What's new" around the release notes. Replaces + the full-changelog modal with an inline section, a "View all releases" drawer, + and a sparkle on the Help icon when the running version hasn't been acknowledged + in this browser. + + Everything shown now comes from the root CHANGELOG.md, the release-level summary + written during each release: its headline and opening paragraph lead the + release, breaking changes and new features are listed individually and badged + apart, and the remaining sections are summed up as counts linking to that + release's section of the changelog. Nothing is hand-authored in the app. The + whole changelog is no longer shipped as a fetched asset either — next.config.mjs + parses it at build time and emits a small public/whats-new.json instead. + +### Patch Changes + +- cb48c46a: Show every notification target an alert is configured with. The alerts page rows and the alert detail header only ever rendered the legacy singular `channel`, so an alert notifying three webhooks read as if it notified one, and the label was the generic "Webhook" rather than the webhook's name. Both surfaces now resolve all of an alert's channels: the detail page names each target with its service icon (Slack, incident.io, generic), keeping the first two inline and collapsing the rest into a `+N more` tooltip, while the alerts-page rows show the icons only with the names on hover, since spelling out up to ten names wrapped the row into an unreadable block. The hover-only names are also placed in the accessibility tree rather than left to an `aria-label` on a role-less wrapper. + + The evaluation history's "Webhook Duration" column is renamed "Notification duration" and gains a tooltip. The value was always the wall time of the whole delivery, which fans out to every target concurrently, so a single slow webhook sets the figure — but the singular heading read as one webhook's latency. Per-target attribution is not available yet; nothing records it. The remaining column headings are corrected to sentence case. + +- cf8e7e72: Give every alerts-page row the same trailing controls. The row's Terraform import button, source link, and acknowledgement button were each independently conditional — import needs a saved-search alert _and_ the export feature, and `AckAlert` renders nothing for an OK alert that has never been acknowledged — so the flex row collapsed differently per alert and no two rows lined up. The conditional actions move into an overflow menu that always renders, alongside a new "Delete alert" item, and the acknowledgement button gets a reserved slot so its absence no longer shifts everything to its left. + + The Terraform snippet building is extracted into a `useTerraformSnippets` hook so the row menu can present the same snippets in a modal without duplicating it, or moving `ResourceTerraformPopover` off the two other pages that use it. Snippets are still built lazily on open, which is what keeps `window.location.origin` out of the render path and the ClickStack static export building. + +- bb320db6: fix: Confirm before discarding unsaved changes when closing the dashboard filter editor +- f11038ef: feat: Persist variable-keyed dashboard filter value state +- f9f7d5bc: feat: Add completions for PromQL variables +- 82180780: feat: Enable dashboard variables for everyone by removing the feature toggle +- 2ba1b25b: fix: Expand variables prior to navigating to search page via drill-down +- 9155b436: Fix session replays rendering empty, unstyled, or freezing mid-session when a recorded rrweb event exceeds the recorder's ~950KB chunk size. All chunks of a split event share one timestamp, and the replay query ordered by timestamp alone, so ClickHouse could return chunks in arbitrary order — the scrambled reassembly failed to parse and the event (often the full DOM snapshot carrying all inlined CSS) was silently dropped. The replay stream is now ordered deterministically (`rr-web.offset` and `rr-web.chunk` tiebreaks), chunks are reassembled by explicit chunk index per event, and dropped events are reported in the console and flagged with a warning indicator in the player instead of being swallowed. Existing recordings are replayed correctly without re-ingestion. Replaced replay streams are now also cancelled instead of streaming to completion in the background, and the player imports `Replayer` from `@rrweb/replay` (the replay-only package rrweb recommends over the deprecated combined `rrweb` package). +- de9038e7: feat: Distribute exact-match lucene variable references +- 7662fae8: feat: Show warnings for invalid promql variable usage +- 93b51b13: feat: Add generated PromQL preview +- 64326d09: feat: Support variable substitution in PromQL charts +- 7f3878bc: refactor: Split `DashboardFiltersModal` into smaller components +- 210a3fb7: Release markers now show a distinct "couldn't load release markers" notification when the underlying query fails (e.g. a source's version expression references a column, such as `ResourceAttributes`, that the table doesn't have), instead of silently rendering no markers indistinguishable from "no releases found in this time range." +- e995c393: feat(app): mask secrets in API key and MCP install snippets with a shared reveal-to-copy component +- 057a6845: perf: Virtualize the alerts page list +- Updated dependencies [3c81bb96] +- Updated dependencies [0558f77e] +- Updated dependencies [f11038ef] +- Updated dependencies [df4a7a55] +- Updated dependencies [f9f7d5bc] +- Updated dependencies [892cc653] +- Updated dependencies [82852c3a] +- Updated dependencies [b52a6fa8] +- Updated dependencies [de9038e7] +- Updated dependencies [7662fae8] +- Updated dependencies [93b51b13] +- Updated dependencies [64326d09] + - @hyperdx/api@2.37.0 + - @hyperdx/common-utils@0.28.0 + ## 2.36.0 ### Minor Changes diff --git a/packages/app/package.json b/packages/app/package.json index a8213500d4..8900ff4c55 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@hyperdx/app", - "version": "2.36.0", + "version": "2.37.0", "private": true, "license": "MIT", "engines": { @@ -38,7 +38,7 @@ "@hookform/resolvers": "^3.9.0", "@hyperdx/api": "workspace:*", "@hyperdx/browser": "^0.22.1", - "@hyperdx/common-utils": "^0.27.0", + "@hyperdx/common-utils": "^0.28.0", "@hyperdx/node-opentelemetry": "^0.9.0", "@mantine/core": "^9.0.0", "@mantine/dates": "^9.0.0", diff --git a/packages/cli/package.json b/packages/cli/package.json index c2e16dcf9f..68ca52c4f6 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -41,7 +41,7 @@ }, "devDependencies": { "@clickhouse/client": "1.23.0-head.fae5998.1", - "@hyperdx/common-utils": "^0.27.0", + "@hyperdx/common-utils": "^0.28.0", "@jest/globals": "^30.2.0", "@types/crypto-js": "^4.2.2", "@types/jest": "^29.5.14", diff --git a/packages/common-utils/CHANGELOG.md b/packages/common-utils/CHANGELOG.md index be43917526..bf51612cf1 100644 --- a/packages/common-utils/CHANGELOG.md +++ b/packages/common-utils/CHANGELOG.md @@ -1,5 +1,27 @@ # @hyperdx/common-utils +## 0.28.0 + +### Minor Changes + +- 0558f77e: Record and show which notification target an evaluation's delivery time went to. `webhookDurationMs` was a single number covering the whole delivery, and because targets are dispatched concurrently the slowest one sets it — so a multi-target alert reported a figure with no way to tell which webhook was responsible, or that the other targets were fine. + + Each dispatch is now timed individually and aggregated per target across the evaluation, since a grouped alert notifies the same target once per firing group and again on resolve. One entry per distinct target carries its webhook id, display name, summed duration, how many dispatches it took, and how many failed. The evaluation history's "Notification duration" cell expands in place to show the breakdown. + + Stored per evaluation rather than per dispatch: a 50-group alert notifying 10 targets would otherwise write 500 entries onto every history row. The array is capped at `ALERT_NOTIFICATION_TARGETS_LIMIT` and sorted slowest-first, so the cap drops the least interesting rows. Records written before this change keep rendering their total with nothing to expand. + +- df4a7a55: Add a new `inline` alert source that persists its own chart config directly on the alert, so alerts no longer require a saved search (logs) or a dashboard tile (metrics). The config is the same shape a dashboard tile stores — builder configs on log/trace/metric sources plus raw SQL (Line/Stacked Bar/Number display types); PromQL is rejected. The internal alerts API accepts and returns the new source, and the check-alerts task evaluates inline alerts through the same code path as tile alerts (including group-by and multi-window behavior). Notifications for inline alerts link to the chart explorer seeded with the alert's config over the alerting window, and default their title to the config's name. Backend only — the creation/edit UI and external API v2 support land separately. + +### Patch Changes + +- f11038ef: feat: Persist variable-keyed dashboard filter value state +- f9f7d5bc: feat: Add completions for PromQL variables +- 82852c3a: fix: Fix `@/*` aliases leaking into the local type declarations +- de9038e7: feat: Distribute exact-match lucene variable references +- 7662fae8: feat: Show warnings for invalid promql variable usage +- 93b51b13: feat: Add generated PromQL preview +- 64326d09: feat: Support variable substitution in PromQL charts + ## 0.27.0 ### Minor Changes diff --git a/packages/common-utils/package.json b/packages/common-utils/package.json index 38d5235880..e3c8686ac1 100644 --- a/packages/common-utils/package.json +++ b/packages/common-utils/package.json @@ -1,7 +1,7 @@ { "name": "@hyperdx/common-utils", "description": "Common utilities for HyperDX application", - "version": "0.27.0", + "version": "0.28.0", "license": "MIT", "private": true, "files": [ diff --git a/packages/hdx-eval/CHANGELOG.md b/packages/hdx-eval/CHANGELOG.md index ac1873a49c..e7038d2880 100644 --- a/packages/hdx-eval/CHANGELOG.md +++ b/packages/hdx-eval/CHANGELOG.md @@ -1,5 +1,11 @@ # @hyperdx/hdx-eval +## 0.3.3 + +### Patch Changes + +- 892cc653: feat(mcp): improve metric discovery, add quiet-saturation eval scenario + ## 0.3.2 ### Patch Changes diff --git a/packages/hdx-eval/package.json b/packages/hdx-eval/package.json index 288b808253..9553a7dffc 100644 --- a/packages/hdx-eval/package.json +++ b/packages/hdx-eval/package.json @@ -1,6 +1,6 @@ { "name": "@hyperdx/hdx-eval", - "version": "0.3.2", + "version": "0.3.3", "license": "MIT", "private": true, "engines": { diff --git a/packages/otel-collector/CHANGELOG.md b/packages/otel-collector/CHANGELOG.md index 17cbaf30d0..3339aeeac2 100644 --- a/packages/otel-collector/CHANGELOG.md +++ b/packages/otel-collector/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperdx/otel-collector +## 2.37.0 + ## 2.36.0 ### Minor Changes diff --git a/packages/otel-collector/package.json b/packages/otel-collector/package.json index fda9885322..51cbf10891 100644 --- a/packages/otel-collector/package.json +++ b/packages/otel-collector/package.json @@ -1,7 +1,7 @@ { "name": "@hyperdx/otel-collector", "description": "HyperDX OpenTelemetry Collector configuration and Docker image", - "version": "2.36.0", + "version": "2.37.0", "license": "MIT", "private": true } diff --git a/yarn.lock b/yarn.lock index 59d9215c1e..db1bc4224c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4616,7 +4616,7 @@ __metadata: "@braintree/sanitize-url": "npm:^7.1.1" "@clickhouse/client-common": "npm:1.23.0-head.fae5998.1" "@esm2cjs/p-queue": "npm:^7.3.0" - "@hyperdx/common-utils": "npm:^0.27.0" + "@hyperdx/common-utils": "npm:^0.28.0" "@hyperdx/node-opentelemetry": "npm:^0.9.0" "@hyperdx/passport-local-mongoose": "npm:^9.0.1" "@modelcontextprotocol/sdk": "npm:^1.27.1" @@ -4708,7 +4708,7 @@ __metadata: "@hookform/resolvers": "npm:^3.9.0" "@hyperdx/api": "workspace:*" "@hyperdx/browser": "npm:^0.22.1" - "@hyperdx/common-utils": "npm:^0.27.0" + "@hyperdx/common-utils": "npm:^0.28.0" "@hyperdx/node-opentelemetry": "npm:^0.9.0" "@mantine/core": "npm:^9.0.0" "@mantine/dates": "npm:^9.0.0" @@ -4835,7 +4835,7 @@ __metadata: resolution: "@hyperdx/cli@workspace:packages/cli" dependencies: "@clickhouse/client": "npm:1.23.0-head.fae5998.1" - "@hyperdx/common-utils": "npm:^0.27.0" + "@hyperdx/common-utils": "npm:^0.28.0" "@jest/globals": "npm:^30.2.0" "@types/crypto-js": "npm:^4.2.2" "@types/jest": "npm:^29.5.14" @@ -4862,7 +4862,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperdx/common-utils@npm:^0.27.0, @hyperdx/common-utils@workspace:packages/common-utils": +"@hyperdx/common-utils@npm:^0.28.0, @hyperdx/common-utils@workspace:packages/common-utils": version: 0.0.0-use.local resolution: "@hyperdx/common-utils@workspace:packages/common-utils" dependencies: From 7aeaf1f746dc320a994f8a237b248ae2cb6c77ed Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 28 Aug 2026 11:56:14 +0000 Subject: [PATCH 2/2] chore(release): update root CHANGELOG.md --- CHANGELOG.md | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 488b4c04a8..78cfe08142 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,190 @@ PR — keep the `hyperdx-release-notes` comment marker intact when editing so yo edits survive regeneration. Per-package detail lives in each `packages/*/CHANGELOG.md`. +## v2.37.0 — 2026-08-28 + + + +**Metrics explorer and in-app release notes** + +You no longer have to know a metric's name to chart it: the chart editor gains a +metrics explorer that browses your metric namespace as a tree, searches every +name and description the source reports, and shows a metric's kind, unit, +reporting services and tag values before you commit to it. The Help menu's +"What's new" is rebuilt around these release notes, so each release's highlights +are readable in the app itself. Multi-webhook alerts are now honoured end to +end — every configured channel is notified, every target is shown on the alerts +pages, and delivery time is attributed to the target it went to — and session +replays no longer break when a recording contains a very large event. + +### ✨ New Features + +- **Metrics explorer in the chart editor**: a browse control beside the metric + select opens a modal with a prefix hierarchy over your metric namespace + (`system` → `cpu` → `utilization`) plus search across every metric name and + description. Each row shows the metric's kind and description, and the detail + pane gives the unit, reporting services and tag keys you can drill into, + replacing a flat dropdown of thousands of names that only revealed that + metadata after you had chosen. Names split on `.` when they have one and on + `_` otherwise, decided per metric, so dotted OpenTelemetry names and + underscore-style exporter names both nest sensibly. While browsing a metric's + tags you can stage filters and group-bys as removable chips and apply them + with the metric, and applying sets an aggregation suited to the kind — average + for a gauge, sum for a counter, p95 for a histogram (#3000, thanks + @MikeShi42!). +- **"What's new" lives in the app**: the Help menu's full-changelog modal is + replaced by an inline section, a "View all releases" drawer, and a sparkle on + the Help icon when the running version has not been acknowledged in this + browser. Everything comes from the release notes themselves — headline and + summary lead each release, breaking changes and new features are listed and + badged apart, and the remaining sections are summarised as counts. The + changelog is parsed at build time rather than shipped as a fetched asset + (#2993, thanks @jordan-simonovski!). +- **Alerts that carry their own chart, without a saved search or dashboard + tile**: a new `inline` alert source persists a chart config directly on the + alert, so alerting on a query no longer means saving a search or building a + tile first. Builder configs on log, trace and metric sources are supported + alongside raw SQL on Line, Stacked Bar and Number displays (PromQL is + rejected), inline alerts evaluate through the same path as tile alerts — + group-by and multi-window behaviour included — and their notifications link + to the chart explorer seeded with the alert's own config over the alerting + window. This release lands the backend only; the creation and edit UI and + external API v2 support follow separately (#3010). +- **Span links read both ways in the span detail**: the Overview panel now shows + reverse span links — the spans that link to the one you are looking at — as + well as the links the span declares itself, and resolves each link's details + instead of leaving you with bare ids (#3011, thanks @karl-power!). +- **Dashboard variables are available to everyone**: the feature toggle that + gated dashboard variables is gone, so every deployment gets variables — and + the filter, Lucene and PromQL work below — without turning a flag on first + (#3009, thanks @pulpdrew!). +- **Dashboard filter values persist per variable**: filter value state is now + stored keyed by its variable, so a dashboard keeps the selections you made + (#2963, thanks @pulpdrew!). +- **Exact-match Lucene variable references are distributed**: a Lucene search + that matches a field exactly against a dashboard variable now expands across + the variable's values (#2987, thanks @pulpdrew!). +- **Dashboard variables work in PromQL charts**: a PromQL query on a dashboard + tile now has its variable references substituted before it runs, so PromQL + charts respond to the dashboard's variables like the rest of your tiles, and + the PromQL editor completes the variables available to it as you type. A + variable used somewhere PromQL cannot take one is now called out with a + warning instead of leaving you to work out why the chart is empty, and a + preview shows the generated PromQL the chart will run (#2994, #2995, #2997, + #2998, thanks @pulpdrew!). + +### 🔧 Improvements + +- **Notification duration is attributed to each target**: the figure was a + single number covering the whole delivery, and because an alert's targets are + notified concurrently the slowest one set it — so a multi-target alert gave + you a number with no way to tell which webhook was responsible, or that the + others were fine. Each dispatch is now timed on its own and aggregated per + target across the evaluation, so the evaluation history's "Notification + duration" cell expands in place to show each target's name, its summed + duration, how many dispatches it took and how many of them failed. Evaluations + recorded before this release keep showing their total with nothing to expand + (#3003, thanks @jordan-simonovski!). +- **Alert actions are the same on both alerts surfaces, and an alert's source + is legible**: the alerts page row menu now opens the alert editor directly, so + changing a threshold no longer means navigating to the alert first, and a new + filter narrows the list by what an alert watches — free-text search matches it + too, so typing "tile" works without touching the dropdown. Each row's source + icon gains a tooltip and accessible label naming it ("Saved search" / + "Dashboard tile"). On the alert detail page, Edit, Delete and Terraform export + move behind the same overflow menu the list uses instead of spreading four + buttons across the header, the link to what the alert watches becomes an icon + beside the alert's name, and the properties block keeps the creator and the + created and updated timestamps in a dimmed line beneath the alert's settings + rather than competing with them. Team settings tabs gain icons (#3015, thanks + @jordan-simonovski!). +- **Every alerts-page row has the same trailing controls**: the Terraform + import, source link and acknowledgement actions were each conditional, so no + two rows lined up. The conditional actions move into an overflow menu that + always renders — with a new "Delete alert" item alongside them — and the + acknowledgement button gets a reserved slot, so its absence no longer shifts + the row (#3002, thanks @jordan-simonovski!). +- **The alerts page stays responsive with a long list of alerts**: the list is + virtualised, so only the rows on screen are rendered and scrolling no longer + slows down on a team with hundreds of alerts (#3012, thanks @pulpdrew!). +- **API keys and MCP install snippets stay masked until revealed**: both now + hide their secret behind a shared reveal-to-copy control, so you can open + those pages without the key on screen (#2988). +- **Closing the dashboard filter editor confirms first**: you are asked before + unsaved changes to a dashboard filter are discarded, so a stray click no + longer loses the edits you were part-way through (#3005, thanks @pulpdrew!). +- **Better metric discovery over MCP**: the MCP server's metric discovery is + improved, so an agent working with your metrics finds the right one more + reliably (#2861, thanks @karl-power!). + +### 🐛 Bug Fixes + +- **Alerts reliably notify their configured channels**: configured channels were + encoded as `@webhook-` mention strings and appended after whatever you + wrote in the alert message, so a body containing enough mentions consumed the + entire per-event notification cap and the alert's own channel — the one target + it was set up to notify — was silently never reached. Configured channels are + now built directly, queued first, and exempt from a cap that only ever meant + to bound ad hoc mentions. Mentions in the message body are unchanged, still + capped, and still deduplicated against the configured channels, so naming one + twice notifies it once (#2984, thanks @jordan-simonovski!). +- **The alerts pages show every notification target**: rows and the alert detail + header only rendered the legacy singular channel, so an alert notifying three + webhooks read as if it notified one, labelled a generic "Webhook" rather than + the webhook's name. The detail page now names each target with its service + icon, keeping the first two inline and collapsing the rest into a `+N more` + tooltip, while rows show the icons with names on hover. The evaluation + history's "Webhook Duration" column is renamed "Notification duration" with a + tooltip, since the figure was always the wall time of a delivery that fans out + to every target at once (#3001, thanks @jordan-simonovski!). +- **Session replays survive very large recorded events**: an rrweb event over + the recorder's ~950KB chunk size is split into chunks that all share one + timestamp, and the replay query ordered by timestamp alone, so ClickHouse + could return them scrambled and the event — often the full DOM snapshot with + all its inlined CSS — was silently dropped, leaving replays empty, unstyled or + frozen mid-session. The stream is now ordered deterministically, chunks are + reassembled by explicit index, and any event that still cannot be rebuilt is + flagged in the player instead of swallowed. Existing recordings play back + correctly without re-ingestion (#2956). +- **Gemini-backed MCP clients can connect again**: the quantile `level` field + was advertised as a numeric enum, and Gemini's function declarations accept + `enum` only alongside `type: "string"` — so a client forwarding tool schemas + to the provider had its whole tool list rejected over this one field, with a + generic "trouble connecting to the model provider" error that named neither + the tool nor the property. `level` is now advertised as a string enum on + `clickstack_timeseries`, `clickstack_table`, `clickstack_save_dashboard` and + `clickstack_patch_dashboard`; numeric input is still accepted and + out-of-set values are still rejected, and the external REST API's own contract + is untouched (#2971, thanks @RIP21!). +- **Drilling down to Search carries the variable's value, not its name**: + dashboard variables are now expanded before a drill-down opens the search + page, so the search runs against the values you were looking at instead of the + unresolved variable references (#3008, thanks @pulpdrew!). +- **A failed release-marker query says so**: when the query behind release + markers fails — for example because a source's version expression references a + column such as `ResourceAttributes` that the table does not have — you now get + a distinct "couldn't load release markers" notification, instead of an empty + chart indistinguishable from "no releases in this time range" (#3007, thanks + @teeohhem!). + +### 📦 Build / Packaging + +- **`@hyperdx/common-utils` ships usable type declarations**: internal `@/*` + path aliases no longer leak into the published `.d.ts` files, where they could + not be resolved by consumers (#2969, thanks @pulpdrew!). + + + +### 📦 Package changelogs + +- `@hyperdx/api` 2.36.0 → 2.37.0 — [changelog](https://github.com/hyperdxio/hyperdx/blob/main/packages/api/CHANGELOG.md#2370) +- `@hyperdx/app` 2.36.0 → 2.37.0 — [changelog](https://github.com/hyperdxio/hyperdx/blob/main/packages/app/CHANGELOG.md#2370) +- `@hyperdx/common-utils` 0.27.0 → 0.28.0 — [changelog](https://github.com/hyperdxio/hyperdx/blob/main/packages/common-utils/CHANGELOG.md#0280) +- `@hyperdx/hdx-eval` 0.3.2 → 0.3.3 — [changelog](https://github.com/hyperdxio/hyperdx/blob/main/packages/hdx-eval/CHANGELOG.md#033) +- `@hyperdx/otel-collector` 2.36.0 → 2.37.0 — [changelog](https://github.com/hyperdxio/hyperdx/blob/main/packages/otel-collector/CHANGELOG.md#2370) + + + ## v2.36.0 — 2026-08-21