Commit Graph

3515 Commits

Author SHA1 Message Date
Konstantin Wohlwend
882236c7ef Better LMDB KV store logging 2026-07-03 11:09:42 -07:00
Konstantin Wohlwend
b9694ea031 Much faster backfill script 2026-07-03 10:34:20 -07:00
Konstantin Wohlwend
b6444e0164 More and better Bulldozer logs 2026-07-03 10:31:41 -07:00
nams1570
08d0528f2e feat: customizable batch size + better logging on backfill 2026-07-03 10:09:02 -07:00
nams1570
f9b457e062 feat: expose gc on bs start
this should allow gc() calls to clean up
2026-07-03 10:07:05 -07:00
Konstantin Wohlwend
121793595d Decrease Bulldozer Payments init batch size to 50 2026-07-03 09:43:53 -07:00
Konstantin Wohlwend
d568752a3d getDebugInfo() for Bulldozer
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
2026-07-02 17:04:15 -07:00
Konstantin Wohlwend
c21fa2ee88 Improve info logging for globalThis object 2026-07-02 16:52:27 -07:00
Konstantin Wohlwend
f312200801 Better debug logging for Bulldozer globalThis 2026-07-02 16:49:11 -07:00
Konstantin Wohlwend
784454522d Add bulldozerDb as a global on globalThis 2026-07-02 16:33:32 -07:00
Konstantin Wohlwend
e9d4a09750 Bulldozer now GC's more aggressively 2026-07-02 15:59:19 -07:00
Konstantin Wohlwend
1ae0611248 Decrease Bulldozer payments init batch size to 100 from 500, add timings 2026-07-02 15:55:38 -07:00
Armaan Jain
72026e61e2
Payments checkout page error display (#1704)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/hexclave/hexclave/blob/dev/CONTRIBUTING.md

-->

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Checkout now shows inline errors for validation, purchase setup, Stripe
confirmation, and test‑mode bypass, with safer response parsing, inline
alerts, and loading states to prevent silent failures. Free checkouts
hide Stripe Elements and redirect via /purchase/return?free=1; added
tests and refreshed OpenAPI for /emails/send-email to allow user_ids,
all_users, or emails with recipients accepting email and not requiring
user_id; docs also add a `hexclaveServerApp.queryAnalytics` tip.

<sup>Written for commit 137a8c596b.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1704?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Improved checkout handling for free purchases and test-mode bypasses,
with clearer inline alerts and smoother redirects.
* Expanded email-sending API docs to cover sending to selected users,
all users, or arbitrary email addresses.

* **Bug Fixes**
* Checkout errors now display more reliably in the page instead of
relying on generic popups.
* Better handling of payment confirmation and missing payment details to
reduce failed checkout edge cases.

* **Documentation**
* Added guidance for advanced query access and updated Hexclave reminder
text.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-03 00:15:43 +05:30
Armaan Jain
9b0361a1d7
Swap Do Later and Connect button positions in payments onboarding (#1714)
## Summary

Swaps the positions of "Do Later" and "Connect" buttons in the payments
setup onboarding step for US users. "Connect" is now `primaryAction`
(filled, left) and "Do Later" is `secondaryAction` (outline, right).

For non-US users (where "Connect" isn't shown), "Do Later" remains the
filled `primaryAction` — preserving the original visual hierarchy.

Link to Devin session:
https://app.devin.ai/sessions/a5ff1d4533b54cbc9e1ed06b08334604
Requested by: @Developing-Gamer

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved the payment setup step’s action button behavior for a more
consistent experience.
* For US-based setup, **Connect** is now the main action (with matching
loading state).
  * For non-US countries, the main action is omitted.
* **Do Later** is available as the secondary action (with matching
loading state).
* Action buttons are disabled during saving or when a payment setup
action is in progress.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: armaan <armaan@stack-auth.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-03 00:14:55 +05:30
github-actions[bot]
976aaffd56 chore: update package versions 2026-07-02 17:12:34 +00:00
Aman Ganapathy
ec35b067eb
[Feat] [Refactor]: Implement Bulldozer JS (#1700) 2026-07-02 10:09:46 -07:00
github-actions[bot]
484850e8d2 chore: update package versions
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
Publish RDE dashboard release / publish-dashboard (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
Publish npm packages / publish (push) Has been cancelled
Publish Swift SDK to prerelease repo / publish (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
2026-07-02 16:45:16 +00:00
aadesh18
d5d2f27a96
Move queryAnalytics to server app (#1687)
Moves `queryAnalytics` from the admin app to the server app so backend
code can call it without needing an over-privileged admin key. All
existing dashboard pages that ran queries internally are updated to use
`useServerApp()` instead of `useAdminApp()`.

## What changed

- **Backend**: New `POST /api/v1/analytics/query` and `POST
/api/v1/analytics/query/timing` endpoints on the **server** app
(previously admin-only)
- **Dashboard**: `queryAnalytics` calls in Queries, Query Analytics,
Session Replays, and Sign-up Rules pages switched from `adminApp` →
`serverApp`
- **Docs**: Added SDK + REST API usage example under the Queries section
of the Analytics guide
- **REST API reference**: New `Run analytics query` and `Get analytics
query timing` endpoints visible under Server API > Analytics

## Docs — Analytics › Queries section

New paragraph + code snippet showing
`hexclaveServerApp.queryAnalytics()` from backend code.

| | Light | Dark |
|---|---|---|
| **Before** |
![](https://gist.githubusercontent.com/aadesh18/e7950872b14557cfac823a99cb90ea9f/raw/analytics-before-light-queries.png)
|
![](https://gist.githubusercontent.com/aadesh18/e7950872b14557cfac823a99cb90ea9f/raw/analytics-before-dark-queries.png)
|
| **After** |
![](https://gist.githubusercontent.com/aadesh18/e7950872b14557cfac823a99cb90ea9f/raw/analytics-after-light-queries.png)
|
![](https://gist.githubusercontent.com/aadesh18/e7950872b14557cfac823a99cb90ea9f/raw/analytics-after-dark-queries.png)
|

<details>
<summary>Full-page overview (both themes)</summary>

| Light | Dark |
|---|---|
|
![](https://gist.githubusercontent.com/aadesh18/e7950872b14557cfac823a99cb90ea9f/raw/analytics-after-light.png)
|
![](https://gist.githubusercontent.com/aadesh18/e7950872b14557cfac823a99cb90ea9f/raw/analytics-after-dark.png)
|

</details>

## REST API reference — new Server API endpoints

`POST /analytics/query` and `POST /analytics/query/timing` now appear
under **Server API > Analytics** in the Mintlify reference.

| Light | Dark |
|---|---|
|
![](https://gist.githubusercontent.com/aadesh18/e7950872b14557cfac823a99cb90ea9f/raw/mintlify-run-analytics-query-light.png)
|
![](https://gist.githubusercontent.com/aadesh18/e7950872b14557cfac823a99cb90ea9f/raw/mintlify-run-analytics-query-dark.png)
|


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added public server-access analytics query support, including a query
timing endpoint for performance details.
  * Enriched API documentation for analytics queries and results.
* **Bug Fixes**
* Switched analytics querying across the dashboard and E2E tests to use
the correct public route and server access flow.
* Increased the long-request warning threshold for the analytics query
endpoint.
* **Documentation**
* Updated OpenAPI specs and the analytics guide with REST endpoint
details and examples.
* **Breaking Changes**
* Removed analytics query support from the admin client-side flow; use
the server-access method instead.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-02 09:42:10 -07:00
Konstantin Wohlwend
c5c853ccff Cleaner SMTP connections 2026-07-01 17:09:18 -07:00
Konstantin Wohlwend
82517ee69d Move dbSync to environment config 2026-07-01 12:44:33 -07:00
Konsti Wohlwend
1cf93a5f76
feat: add recent triggers across all rules card to Sign-up Rules page (#1703) 2026-07-01 11:59:38 -07:00
Konstantin Wohlwend
91d7362617 Add comment explaining how to use queryAnalytics 2026-07-01 11:57:28 -07:00
Konsti Wohlwend
56b7acea93
feat: add AI tool to read the branch config object (#1702) 2026-07-01 11:51:34 -07:00
github-actions[bot]
e883f112fb chore: update package versions 2026-07-01 18:33:02 +00:00
Armaan Jain
db94ec2d94
More consistent dashboard (#1613)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/hexclave/hexclave/blob/dev/CONTRIBUTING.md

-->

## Summary

This PR tightens dashboard consistency across the project list, team
invite flow, hosted auth previews, and shared overlay surfaces. It also
includes smaller visual fixes for charts/tooltips, globe sizing, sticky
data-grid surfaces, and dashboard/hosted component light-mode parity.

**Base:** `dev@2c5b2d02c` -> **Head:**
`more-consistent-dashboard@b1eae206`
**Capture notes:** screenshots were captured locally at `1440x1000`
after signing in through GitHub -> `admin@example.com` on the mock OAuth
server. Each page/state was allowed to settle for 5 seconds immediately
before capture. Dark mode is used by default; light mode is included
only for the light-specific overlay/dialog surfaces changed in this PR.

## Visual Comparison

### Hosted Auth Preview - Dark Mode

| Before | After |
| --- | --- |
| ![Before hosted auth preview
dark](https://gist.githubusercontent.com/Developing-Gamer/7f29365532f54161bc5b4531f7a71b1b/raw/before-auth-methods-preview-dark.png)
| ![After hosted auth preview
dark](https://gist.githubusercontent.com/Developing-Gamer/7f29365532f54161bc5b4531f7a71b1b/raw/after-auth-methods-preview-dark.png)
|

### Team Invite Dialog - Light-Specific Surface

| Before | After |
| --- | --- |
| ![Before team invite dialog
light](https://gist.githubusercontent.com/Developing-Gamer/7f29365532f54161bc5b4531f7a71b1b/raw/before-projects-invite-dialog-light.png)
| ![After team invite dialog
light](https://gist.githubusercontent.com/Developing-Gamer/7f29365532f54161bc5b4531f7a71b1b/raw/after-projects-invite-dialog-light.png)
|

## What's Changed

- Reworks the team invite dialog with clearer loading/retry states, seat
information, capacity warnings, pending invites, revoke actions, and
refresh behavior.
- Adds a dashboard-local hosted auth preview used by Auth Methods and
onboarding-style flows, showing sign-in/sign-up methods with project
display names and disabled sign-up behavior.
- Standardizes overlay and popover surfaces across dashboard, hosted
components, and shared UI primitives for better light/dark consistency.
- Adjusts chart, tooltip, globe, sidebar drawer, data-grid, and
email-log presentation details to reduce clipping, bleed, and stacking
issues.

## Notes For Reviewers

- The visual comparison focuses on places where this PR has visible UI
differences: project cards/actions, the team invite modal, overlay menu
styling, and the hosted auth preview.
- Lower-level primitive background/ring changes are represented through
the menu/dialog screenshots rather than separate screenshots for every
consuming component.

## Test Plan

- Captured the before screenshots on `dev@2c5b2d02c` and the after
screenshots on `more-consistent-dashboard@b1eae206`.
- Waited 5 seconds on each page/state before capturing screenshots.
- Uploaded exactly 10 screenshots to a fresh gist and embedded them
above.
- Not run in this pass: code tests, because this pass only updates the
PR description/screenshots.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added a live hosted authentication preview for project setup and auth
methods, including sign-in/sign-up states and multiple login options.

* **Bug Fixes**
* Improved invite dialog behavior so seat limits, plan upgrades, loading
states, retries, and invitation refreshes stay in sync.
* Fixed chart tooltip and globe display behavior so overlays, sizing,
and labels render more reliably.

* **Style**
* Refreshed dialogs, menus, tooltips, charts, and sidebars with improved
light/dark theme styling and spacing.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: armaan <armaan@stack-auth.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-07-01 09:24:08 -07:00
Konsti Wohlwend
244fc3ab7b
feat: add navigation funnel graph page (#1701) 2026-06-30 23:04:22 -07:00
github-actions[bot]
c9612539c7 chore: update package versions 2026-06-30 23:15:23 +00:00
BilalG1
526ce69ab9
feat(cli): fetch RDE dashboard from GitHub Releases instead of bundling (#1682)
## Summary

The RDE dashboard was bundled into the `@hexclave/cli` npm tarball
(~165MB). That made the `npx @latest` auto-update re-exec pull a huge
tarball, which tripped download firewalls (Socket Firewall
false-positives on Replit → `Lock compromised`).

This decouples the dashboard from the CLI: it's published as a **GitHub
Release artifact** (a zip + `manifest.json` on a floating
`dashboard-latest` release) and fetched at runtime, cached on disk. The
dashboard now rolls forward **independently of the CLI version**
(assuming newest dashboards stay compatible with older CLIs — no compat
gate yet, by design for now).

## How it works

- **Runtime** (`packages/cli/src/lib/dashboard-release.ts`): on
`hexclave dev`, fetch `manifest.json` (`{version, sha256, url}`) from a
stable, no-API GitHub URL → cache under
`<dirname(devEnvStatePath())>/dashboards/<version>/` → streamed
download, **sha256 verify**, extract via the existing `extract-zip` dep,
atomic publish. Offline → fall back to the newest cached build.
- **Wiring** (`dev.ts`): `startDashboardIfNeeded` resolves/launches the
cached release; the restart decision now compares the **dashboard
release** version (not the CLI version). `HEXCLAVE_DASHBOARD_DIR` runs a
local build with zero network; a custom dev dashboard command bypasses
releases entirely.
- **No more npx self-update**: the `npx @latest` re-exec in `hexclave
dev` is removed entirely (along with `self-update.ts`, the
`--no-auto-update` flag, and the binName plumbing that only built the
npx call). It existed to keep the bundled dashboard fresh; now the
dashboard self-updates from GitHub, so the re-exec only added an npx
download on every run — the firewall surface. Users update the CLI via
npm/npx themselves.
- **Publishing** (`scripts/package-dashboard-release.mjs` +
`.github/workflows/dashboard-release.yaml`): build standalone → zip →
write manifest → publish immutable `dashboard-v<version>` release +
clobber the `dashboard-latest` manifest. **Fails loudly** if an existing
tag's asset sha differs from a fresh build (dashboard changed without a
version bump → would otherwise advertise a hash the served zip doesn't
match).
- **Build**: CLI build is now `tsdown`-only (no bundled dashboard);
`turbo.json` drops the dashboard standalone build from the CLI build
graph.

## Testing

- **Unit**: 135 CLI tests pass (manifest parsing incl. path-safe version
validation, URL resolution, version picking, dir override).
- **End-to-end (no GitHub)**: a localhost HTTP server + fixture zip
exercised the full fetch path — download, sha256 verify, cache-hit,
roll-forward, **corrupt-sha rejection**, offline→cache fallback (9/9).
- **Real dashboard, no GitHub**: built the standalone, served the real
76MB zip over `python -m http.server`, and confirmed `hexclave dev`
downloads → verifies → extracts → **boots** the dashboard and registers
an RDE session against the hosted backend. Also validated
`HEXCLAVE_DASHBOARD_DIR` boot.

## Review

Three review agents (runtime correctness, packaging/workflow, code
quality) ran; valid findings fixed: path-traversal hardening on
`version`, a concurrent cache rm/rename race, the workflow
sha-mismatch/atomicity gap, plus `errorMessage` dedup and added tests.

## Notes / follow-ups

- No CLI↔dashboard compatibility gate yet (assumes always-compatible);
easy to add later via a min-CLI field in the manifest. This matters more
now that the CLI no longer auto-updates.
- The publish workflow has not run yet, so the first push to `main` will
create the `dashboard-latest` release.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added automated, immutable versioned releases for the standalone
dashboard plus a continuously updated “latest” manifest.
* CLI now fetches, verifies, caches, and supports offline fallback for
the dashboard runtime.
* **Bug Fixes**
* Improved dashboard restart behavior to update only when a strictly
newer published version is available.
* **Chores**
* Removed the CLI’s automatic update/re-launch behavior for more
predictable startup.
* **Tests**
* Added unit tests for dashboard manifest validation and latest-version
selection.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-30 16:12:13 -07:00
Konsti Wohlwend
6a979e8b3e
chore: remove unused dependencies from package.json files (#1698) 2026-06-30 15:45:25 -07:00
Konsti Wohlwend
d472283283
Use tsdown -W --watch (workspace mode) for dev builds (#1689) 2026-06-30 15:40:08 -07:00
github-actions[bot]
31b32735bf chore: update package versions 2026-06-30 20:44:13 +00:00
BilalG1
2f0d9bc6cb
fix(session-replay): gzip replay batches so large rrweb snapshots aren't dropped (#1675)
## Problem

Session replay batches are uploaded as **raw, uncompressed JSON**. A
single large rrweb full snapshot (>900 KB) is dropped client-side in
`SessionRecorder._flush`, and any batch near the server's 1 MB body
limit is rejected. Meanwhile the **analytics events** path right next
door already gzips on the wire (`encodeAnalyticsBody` + a server-side
gunzip with zip-bomb guards) — replays just never got the same
treatment.

Notably, `dev` was already **red** on the SDK test `sends a single
oversized event alone without dropping it` (the code dropped at 900 KB
while the test expected the event to be sent). This change makes that
test pass.

## What this does

Ports the proven analytics compression pattern to session replays:

- **`packages/shared` (client transport):** generalize
`encodeAnalyticsBody` → `encodeGzipJsonBody` and wire it into
`sendSessionReplayBatch`. Replays now gzip via native
`CompressionStream` (`application/octet-stream`), with the same
fallbacks: keepalive flushes send plain JSON (so the request survives
page teardown), and a missing/throwing `CompressionStream` (Safari <
16.4) degrades to plain JSON.
- **`packages/template` (SDK recorder):** replace the 900 KB hard-drop
with `MAX_SINGLE_EVENT_BYTES = 8 MB`, kept in sync with the server's
decompressed cap. The wire is gzipped downstream, so an oversized single
event is now sent compressed instead of discarded; only an event that
could never fit *decompressed* is dropped.
- **`apps/backend` (route):** add `maybeDecodeBinaryBody` (gunzip with a
1 MB compressed cap + 8 MB `maxOutputLength` zip-bomb guard) via a
`.transform` on the batch route body schema. Plain-JSON bodies pass
through untouched.

## Why gzip / CompressionStream

Benchmarked on a real rrweb capture (Puppeteer, rrweb 1.1.3): a **1.09
MiB full snapshot → 134 KiB gzip (~8×) in ~6.6 ms**. Native
`CompressionStream` adds **0 bundle bytes** and was faster than fflate.
This mirrors what PostHog (gzip, native `CompressionStream` + fflate
fallback) and Sentry (fflate deflate in a worker) both ship — both rely
on compression, not splitting, as the primary mechanism for large
events.

## Tests

- **Client SDK** (`client-interface.test.ts`): gzip+octet-stream on
non-keepalive (round-trip + size assert), plain JSON on keepalive, and
both `CompressionStream`-unavailable fallbacks.
- **SDK recorder** (`session-replay.test.ts`): existing oversized-event
test now passes; new test that a >8 MB event is dropped while a
following normal event still sends.
- **Backend e2e** (`session-replays.test.ts`): gzipped happy path
(`compressed < 1MB < raw`), invalid gzip → 400, oversized compressed →
413, zip-bomb → 400. Verified passing against a live local backend.

## Verification notes

- `@hexclave/shared` + `@hexclave/template` typecheck pass; touched
files lint clean; SDK unit tests pass.
- Two **pre-existing, unrelated** local failures (each confirmed by
stashing this change and re-running on clean `dev`): a backend typecheck
error in `oauth/ssrf-protection.test.ts` (`NODE_ENV` read-only), and 3
session-replay **quota** e2e tests failing in the payments
`setSessionReplayItemQuantity` setup helper.

## Follow-up (out of scope)

The keepalive/unload path still sends uncompressed (64 KB browser
keepalive cap), so a very large final batch on page exit can still be
lost — not addressed here.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Gzip session replay batches so large rrweb snapshots are sent instead of
dropped. Keepalive flushes cap single-event size to avoid 413s on page
unload, and limits now include a 1 KiB envelope margin to match server
caps.

- **Bug Fixes**
- Client (`@hexclave/shared`): generalized `encodeAnalyticsBody` →
`encodeGzipJsonBody`; gzip via native `CompressionStream` for
`/session-replays/batch`; keepalive and missing/throwing
`CompressionStream` fall back to JSON.
- SDK recorder (`@hexclave/template`): normal flushes allow a single
event up to `MAX_SINGLE_EVENT_BYTES = 8 MiB - 1 KiB`; keepalive flushes
cap single-event size at `MAX_BATCH_UNCOMPRESSED_BYTES = 900 KB`; send
oversized events alone and rely on gzip; drop only above those
thresholds; log a distinct 413 warning with the count of buffered events
dropped and stop the loop.
- Backend: gunzip `application/octet-stream` bodies with caps (`1 MB`
compressed, `8 MiB` decompressed); invalid gzip → 400; oversized
compressed → 413; plain JSON passes through.
- Tests: added client encoding tests for session replays; SDK tests for
>8 MiB drop, keepalive drop, and 413 warning; backend e2e for gzip,
invalid gzip, oversized compressed, and zip-bomb. The oversized-event
SDK test now passes.

- **Refactors**
  - Hoisted shared test helpers and trimmed comments.

<sup>Written for commit 855cdb052e.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1675?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Session replay batch uploads can now be automatically gzipped (when
supported) to reduce request size; analytics batch uploads use the same
behavior.
* **Bug Fixes**
* Improved handling of gzipped upload bodies, with stricter validation
and safer size/decompression limits to reject invalid payloads and “zip
bomb” scenarios.
* Updated session replay buffering so oversized events are dropped only
when they exceed decompression constraints, while allowing later events
to upload.
* **Tests**
* Added client, server, and end-to-end coverage for gzip/keepalive
behavior and limit enforcement.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-30 13:13:11 -07:00
Konstantin Wohlwend
36c2b49e23 Fix slop description 2026-06-30 11:56:02 -07:00
Konstantin Wohlwend
7588445efa Update AI setup prompt 2026-06-30 11:36:13 -07:00
github-actions[bot]
9b52f76520 chore: update package versions 2026-06-30 18:27:55 +00:00
BilalG1
bdb511f6a9
Make analytics include_all_branches a no-op with TODO (#1678)
## What

The `/internal/analytics/query` route accepts an `include_all_branches`
flag (defaults to `false`). Previously, passing `true` threw a
`HexclaveAssertionError`. Since the flag is reachable from user input,
that assertion surfaced as a 500 / error-tracking noise rather than a
clean response.

This makes the flag a **no-op** for now and documents the intended
behavior with a TODO.

## Behavior

- Regardless of `include_all_branches`, queries remain scoped to the
current branch via the ClickHouse row policy that filters on
`SQL_branch_id` (set from `auth.tenancy.branchId`).
- Callers passing `include_all_branches=true` will still only receive
data for the **current branch** until cross-branch filtering is
implemented.

## Note / follow-up

Because the flag is silently ignored, a caller asking for "all branches"
gets single-branch results with no signal. The TODO tracks implementing
real cross-branch querying. Flagging in case we'd rather surface a
proper client-facing error in the interim instead of a silent no-op.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Made the `include_all_branches` flag on `/internal/analytics/query` a
no-op so `true` no longer throws and avoids 500s from user input.
Branching isn’t implemented yet, so queries always scope to the current
branch via the ClickHouse `SQL_branch_id` row policy; TODO added for
real cross-branch querying.

<sup>Written for commit a07e1237e9.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1678?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Analytics requests now accept the “include all branches” option
without failing.
* Branch-scoped analytics queries continue to run as before, while the
option is treated as a no-op for now.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-30 11:16:37 -07:00
BilalG1
81433e5181
feat(emails): arbitrary-recipient send + Emailable on managed domains (#1681)
## Summary

Two related email changes:

### 1. Send to arbitrary email addresses
The `sendEmail` SDK function and `POST /api/v1/emails/send-email` now
accept a third recipient selector, **`emails: string[]`**, mutually
exclusive (XOR) with `user_ids` and `all_users`. These recipients don't
have to belong to a user — they're mapped to the existing internal
`custom-emails` recipient type (no associated user object, can't
unsubscribe, so intended for transactional mail).

- Response `results` items are now `{ user_id?, email? }`; the `emails`
path returns `{ email }`.
- Validation message updated to `Exactly one of user_ids, all_users, or
emails must be provided`.
- Recipient addresses are validated with the shared `emailSchema`.

### 2. Run Emailable on managed custom domains
`LowLevelEmailConfig.type` is widened `'shared' | 'standard'` →
`'shared' | 'managed' | 'standard'`, and `getEmailConfig` returns
`'managed'` for the managed-Resend branch. The Emailable deliverability
check now runs for **shared** and **managed**, and is skipped for
**`standard`** (a customer's own SMTP server or Resend API key).

**Why:** managed domains send through our single Resend *account*
(per-domain scoped keys, but shared account → account-level bounce
penalties are collateral across managed customers), so we own that
reputation. With a custom SMTP server or the customer's own Resend key,
the customer owns their deliverability, so we don't second-guess their
recipients or spend Emailable checks on their volume. The shared
dev-email wrapper remains `shared`-only.

## Tests
- `send-email.test.ts`: added arbitrary-recipient send
(single/multiple), empty-array, and validation cases (both-selectors,
no-selector, invalid email); updated the exactly-one-of snapshot.
**24/24 pass.**
- New `emails/deliverability-gating.test.ts`: shared & managed skip an
undeliverable address (`LIKELY_NOT_DELIVERABLE`), custom SMTP still
sends. **3/3 pass.**

> Note: `email-queue.test.ts` has 2 pre-existing snapshot failures (a
`margin:0rem`→`margin:0` CSS normalization in rendered email HTML) that
are unrelated to this change — confirmed by reproducing them on `dev`
with these changes stashed.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Adds support for sending emails to arbitrary addresses and enables
Emailable checks for managed domains to protect shared sending
reputation. The API/SDK now accept `emails: string[]` as a recipient
selector, and deliverability checks run on shared and managed sending.

- New Features
- API/SDK: `emails: string[]` recipient selector (XOR with
`user_ids`/`all_users`) in `sendEmail` and POST
/api/v1/emails/send-email. Results return `{ email }` for this path;
addresses validated via `emailSchema`. Recipients map to
transactional-only “custom-emails” (no user, no unsubscribe).
- Deliverability: email config adds `'managed'`; `getEmailConfig`
returns it for managed Resend domains. Emailable check runs for `shared`
and `managed`; skipped for `standard` (custom SMTP or customer Resend
key).

<sup>Written for commit e48cf2ddda.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1681?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Emails can now be sent to arbitrary email addresses, not just existing
users.
* Email sending APIs and client options now accept an email list as a
recipient choice.

* **Bug Fixes**
* Improved recipient validation so exactly one target type must be
chosen.
* Fixed response results to consistently show either a user ID or an
email address.
* Corrected deliverability checks for managed email setups and updated
skip behavior for undeliverable addresses.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-30 11:16:25 -07:00
BilalG1
4cd766e406
fix(backend): use structured output for QA reviewer instead of JSON.parse (#1688)
## Problem

The QA reviewer (`apps/backend/src/lib/ai/qa-reviewer.ts`) extracted the
model's JSON verdict from free-form text with a greedy `/\{[\s\S]*\}/`
regex followed by `JSON.parse`. Whenever Haiku wrapped its JSON in prose
or tool-call text, the captured slice was malformed and `JSON.parse`
threw:

```
SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)
```

This is Sentry issue **STACK-BACKEND-18F** — **218 occurrences in the
last 30 days** (262 all-time), still ongoing. It's caught (background QA
task, 0 users impacted) but noisy, and on failure the review is silently
marked failed.

### Root cause
The brace-matching heuristic assumes the first `{` in the response
starts the JSON object and the last `}` ends it. Any stray brace in
surrounding prose (a preamble like `"Here's my review {note}: {...}"`,
unquoted/single-quoted keys, or `<function_calls>` tool text) breaks the
extraction. The reviewer output is non-deterministic, so a low steady
fraction of reviews fail.

## Fix

Switch to schema-enforced **structured output** via `generateText`'s
`output: Output.object({ schema })` (AI SDK v6), which keeps the
existing tool-calling loop while constraining the final result to a
validated object. The verdict is read directly off `result.output`,
removing the regex, `JSON.parse`, and manual `typeof` shape validation.

- Net **−12 lines**.
- Trimmed the now-redundant "respond with ONLY valid JSON … {inline
schema}" block from the system prompt (the schema enforces structure);
kept the semantic guidance (flag types, scoring, `needsHumanReview`
rule).
- Kept the existing `overallScore` clamp.

## Verification

- **Typecheck** (`tsc --noEmit`): clean.
- **Lint** (`eslint`): clean.
- **Live end-to-end**: ran the real Haiku-4.5 reviewer against the exact
prompt/input that failed **11/15** on the old path → **15/15** return a
validated object on the new path, zero parse failures.

## Follow-ups (out of scope, noted during investigation)
- These Sentry events leak a live access token + publishable key into
the request-context payload; the OpenRouter key sits in plaintext
(commented) in `apps/backend/.env.local`. Worth rotating/scrubbing.
- Consider capturing `result.text` on the failure path so any residual
structured-output failures are inspectable (currently discarded).

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Replaced regex/JSON.parse extraction in the QA reviewer with
schema-enforced structured output using `ai`’s `generateText` +
`Output.object`. Also defaulted `improvementSuggestions` to an empty
string to avoid false failures when the model omits suggestions.

- **Bug Fixes**
  - Added a `zod` verdict schema and read result from `result.output`.
  - Removed greedy JSON extraction and manual shape checks.
- Defaulted `improvementSuggestions` to "" to tolerate omission and
prevent false `needsHumanReview` verdicts.
- Slimmed prompt by dropping “respond with ONLY JSON” block; kept review
guidance.
  - Kept `overallScore` clamp; tool-calling loop unchanged.
- Verification: typecheck/lint clean; 15/15 successful runs on a
previously failing case.

<sup>Written for commit 72ae71017f.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1688?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* QA reviews now use structured output, making review results more
reliable and consistently formatted.
* Review flags now include clearer details such as type, severity, and
explanation.

* **Bug Fixes**
* Improved handling of review output by removing fragile text parsing
and manual JSON extraction.
  * Added safer defaults for missing review suggestions.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-30 11:15:46 -07:00
Konstantin Wohlwend
e60d880eae Update AGENTS.md 2026-06-30 11:06:32 -07:00
github-actions[bot]
ea2915faa8 chore: update package versions 2026-06-30 18:04:45 +00:00
Konstantin Wohlwend
86ef03cad9 Base64 self-hosted project key initialization 2026-06-30 10:58:40 -07:00
Konsti Wohlwend
09c8ef1946
Keep local emulator removal in a new migration, not edits to applied ones (#1694) 2026-06-30 10:43:10 -07:00
Mantra
09bb1a8974
chore: use glm-5.2:nitro for unauthenticated AI tiers; document CLI exec & config sync (#1693)
## Summary
- Standardize the unauthenticated `smart` and `smartest` model tiers in
`apps/backend/src/lib/ai/models.ts` on `z-ai/glm-5.2:nitro` (previously
a mix of `z-ai/glm-5.2` and `z-ai/glm-5:nitro`).
- Document the `npx @hexclave/cli exec <javascript>` command (runs JS
with a pre-configured `hexclaveServerApp`) in the agent reminders.
- Note that the config file is automatically synced when using the local
dashboard/dev environment via `npx @hexclave/cli dev --config-file
<path>`.

These reminder updates are reflected across `docs-mintlify/llms.txt`,
`docs-mintlify/snippets/hexclave-agent-reminders.jsx`,
`docs-mintlify/snippets/home-prompt-island.jsx`, and
`docs-mintlify/guides/getting-started/setup.mdx`.

## Test plan
- [ ] Verify unauthenticated AI requests resolve to `z-ai/glm-5.2:nitro`
for smart/smartest tiers.
- [ ] Confirm docs render the new agent-reminder bullets correctly.

Made with [Cursor](https://cursor.com)

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Standardized unauthenticated `smart` and `smartest` tiers to
`z-ai/glm-5.2:nitro` for both slow and fast paths to ensure consistent
model selection. Updated docs to include `npx @hexclave/cli exec
<javascript>` (preloads `hexclaveServerApp`) and to note config
auto-sync when using `npx @hexclave/cli dev --config-file <path>`.

<sup>Written for commit 3363c8d9f3.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1693?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Updated unauthenticated AI model selection to use a newer variant for
faster and more reliable responses in supported smart modes.

* **Documentation**
* Added clearer CLI guidance for running JavaScript with a
pre-configured app context.
* Expanded reminder text to better explain local config syncing during
development and dashboard runs.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 10:39:27 -07:00
Konsti Wohlwend
d6d9d0d934
Remove local emulator support (#1692) 2026-06-30 10:27:01 -07:00
github-actions[bot]
d962e9843a chore: update package versions 2026-06-30 17:18:40 +00:00
Konstantin Wohlwend
d31d29c112 Clean up AI tools 2026-06-30 10:08:08 -07:00
Mantra
61698f6ecf
feat(mcp): switch ask_hexclave model from gemini to glm 5.2 (#1677)
## Summary

Switches the `ask_hexclave` MCP tool's model from Gemini 3.5 Flash to
GLM 5.2 by changing the speed parameter from `"fast"` to `"slow"` in the
query to the AI backend.

The model selection matrix maps `smart`/`slow`/`unauthenticated` →
`z-ai/glm-5.2` (previously `smart`/`fast`/`unauthenticated` →
`google/gemini-3.5-flash`).

Link to Devin session:
https://app.devin.ai/sessions/f710490916754099916f9899c2700a5c
Requested by: @mantrakp04

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Switched the `ask_hexclave` MCP tool from Gemini to GLM by updating the
model selection matrix. Unauthenticated smart/fast now routes to
`z-ai/glm-5:nitro`, unauthenticated smartest/fast to
`z-ai/glm-5.2:nitro`, and `dumb/slow/authenticated` uses
`z-ai/glm-4.5-air` (removed `:free`).

<sup>Written for commit 6a51ba6358.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1677?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Adjusted AI request behavior to favor more deliberate responses, which
may improve result quality and consistency.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: mantra <mantra@stack-auth.com>
2026-06-30 10:00:58 -07:00
BilalG1
7f1d563753
fix(backend): use paid z-ai/glm-4.5-air slug instead of retired :free variant (#1685)
## Problem

Sentry is reporting an `AI_APICallError` on the AI proxy path:

> This model is unavailable for free. The paid version is available now
- use this slug instead: `z-ai/glm-4.5-air`

OpenRouter discontinued the **free** tier for GLM 4.5 Air. The model
selection matrix still pinned the `:free` variant for the **dumb / slow
/ authenticated** path, so every request routed there fails.

## Fix

Drop the `:free` suffix in `apps/backend/src/lib/ai/models.ts` so it
uses the paid slug OpenRouter recommends:

```diff
-      authenticated: { modelId: "z-ai/glm-4.5-air:free" },
+      authenticated: { modelId: "z-ai/glm-4.5-air" },
```

`ALLOWED_MODEL_IDS` is derived from the matrix, so the new slug is
picked up automatically.

## Notes

- This was the only `:free` slug in the matrix; nothing else is exposed
to the same break.
- This moves a previously-free path onto paid inference (cheap model,
but no longer $0).

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Switch the dumb/slow/authenticated path to the paid `z-ai/glm-4.5-air`
slug, replacing the retired `:free` variant to stop “This model is
unavailable for free” errors. Restores successful requests after
OpenRouter ended the free tier.

- **Bug Fixes**
- Updated model matrix in apps/backend/src/lib/ai/models.ts to use
`z-ai/glm-4.5-air`; `ALLOWED_MODEL_IDS` picks up the new slug
automatically.

<sup>Written for commit c354b5fcf2.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1685?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Updated the model selection for authenticated requests in one
quality/speed setting, so the app now uses the correct model ID and
accepts the updated option in allowed model lists.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-30 09:47:53 -07:00
Konstantin Wohlwend
b049306b6e Make hexclave exec support local dashboard
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
2026-06-29 18:45:11 -07:00
Konsti Wohlwend
287d96602a
feat: add Project ID column to platform analytics leaderboard (#1690) 2026-06-29 18:44:13 -07:00