Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ name: Release
# detection and keeps those aggregates 90 days; batches the old admin ingest has processed stay miscounted.
# After it: check the dist-tags (a prerelease lands on the tag named after it and `latest` stays), that the
# "Verify the registry signature and provenance" step passed, and that jsDelivr serves @<version>,
# /dist/cdn/worker.js and /dist/sinks/webhook.js; then set telemetry-worker RUNTIME_ON_NPM to it and deploy the worker.
# /dist/cdn/worker.js and /dist/sinks/webhook.js; then set telemetry-worker RUNTIME_ON_NPM (wrangler.toml) and
# src/pages.ts SNIPPET_RUNTIME to it and deploy the worker. The install lines name the newest release (AGENTS.md
# conventions): a stable release publishes to `latest` below; also run `npm dist-tag add @genclass/runtime@<version>
# beta`, so the `@beta` lines never resolve to an older prerelease, and drop `@beta` from the install lines.
# The registry's top-level `readme` (`npm view @genclass/runtime readme`) is emptied by every publish to a tag other
# than `latest` and filled again by the next `latest` publish, cut to its first 65,536 characters; npmjs.com and
# jsDelivr show each version's full README either way (checked 2026-10-17). Nothing in package.json or this workflow
Expand Down
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ Azure, `git push`, `npm publish`.

- Modules open with a `//` header stating role and invariants; JSDoc on exports; named exports; relative imports
with `.js`; 2 spaces, double quotes. Never throw into the host app: hooks and callbacks run in `try/catch`.
- Install lines name the newest runtime line, never npm's bare `latest`: while it is a prerelease, every command a user
or agent copies says `@genclass/runtime@beta` (`npm install`, `npx ... init`), script tags pin the exact version,
templates and peer ranges resolve to it, the stable line is mentioned only as the exception; at a stable release,
move `latest` and `beta` to it and drop `@beta` everywhere in one change.
- Tests: one `<area>.test.ts` per area; `test/helpers.ts` → `setup()` gives a fake clock, server and scripted decider
(default `mode: "guard"`; pass `mode: undefined` for observe). Name the CONTRACT section in the `describe`.
- Done means: `tsc` clean, `tsup` builds, both vitest steps pass with new tests, no review test edited, no
Expand Down
4 changes: 2 additions & 2 deletions PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ came from, devices, countries, new or returning, entry and exit pages and funnel
(time, route or function name, diagnosis, action). Dashboards do not show the situation text. Data from browsers that opted out or
send Global Privacy Control never reaches a dashboard.

When a developer creates a token with `npx @genclass/runtime init`, after approving its changes, the tool sends us
When a developer creates a token with `npx @genclass/runtime@beta init`, after approving its changes, the tool sends us
the name in their project's `package.json`. We keep one record per token: the token, that name, when it was created,
when data first and last arrived, a one-way hash of the dashboard link's secret, and the account it belongs to, if
any. These records are kept until the owner
Expand All @@ -85,7 +85,7 @@ asks us to delete them (contact below, with the token); the dashboard data itsel
## Accounts on genclass.dev

An account is optional: GenClass, its diagnostics and the private dashboard links work without one. A developer who
creates one (at genclass.dev/signup, or with `npx @genclass/runtime login`, runtime 0.3.0-beta.0 and later) can keep
creates one (at genclass.dev/signup, or with `npx @genclass/runtime@beta login`, runtime 0.3.0-beta.0 and later) can keep
their apps in one place. An account stores:

- **An email address:** as Google returned it when you sign in with Google, or as you typed it (trimmed and
Expand Down
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ One line, first in `<head>` or first in your entry file:
import "@genclass/runtime/auto"; // after npm install @genclass/runtime@beta
```

Pin the version in the script tag, as above: the unversioned URL may serve an older release from the CDN cache.
While `0.3.0` is in beta, `@beta` is part of every npm and npx command here: without it npm installs the stable
`0.2.x`, whose CLI has no `login`, `connect`, `whoami`, `logout` or `--sink` and whose runtime ignores a token.
Use the newest line, `0.3.0-beta.1` (npm dist-tag `beta`): every npm and npx command here names `@beta`, and the
script tag pins the version (the unversioned URL serves npm's `latest`, possibly an older release from the CDN cache).
A bare `npm install @genclass/runtime` or `npx @genclass/runtime init` gets npm's `latest`, the older `0.2.x` line,
which has no token, dashboard, interaction analytics, visual checks or agent link, whose CLI has no `login`,
`connect`, `dashboard` or `--sink`, and whose runtime ignores `token` and `analytics: true` without a word. Keep
`0.2.x` only for an app that cannot take a prerelease.

That covers the whole app: network, user actions, errors, timing, and the app's state (React component state,
Redux / Redux Toolkit and Zustand `devtools` stores are discovered automatically; discovered React and Zustand state
Expand All @@ -35,8 +38,9 @@ generic facts about each write, request and response, and asks a small GenClass
The model runs in the browser (WebGPU or WASM, in a Web Worker). It answers two questions: what is happening, and
which available action is best. There is no list of known bugs in the code.

**Status: `@genclass/runtime@0.3.0-beta.1` (npm `beta` dist-tag: `npm install @genclass/runtime@beta`; the stable
line is `0.2.x`) with model `@genclass/runtime-model@0.2.0`.** Observe by default; `guard` is
**Status: `@genclass/runtime@0.3.0-beta.1`, the newest line (npm `beta` dist-tag: `npm install @genclass/runtime@beta`;
npm's `latest` is still the older `0.2.x`, the exception for apps that cannot take a prerelease) with model
`@genclass/runtime-model@0.2.0`.** Observe by default; `guard` is
opt-in; `heal` is experimental. Measured false-intervention and recall rates are under
[Measured behaviour](#measured-behaviour). AI coding agents: start at [AGENTS.md](AGENTS.md).

Expand Down
4 changes: 2 additions & 2 deletions demos/src/site/landing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { icon } from "./icons.ts";
applyTheme();
const root = siteRoot();

const INSTALL = `<span class="p">$ </span>npm install @genclass/runtime
const INSTALL = `<span class="p">$ </span>npm install @genclass/runtime@beta

${highlight(`import { GenClass } from "@genclass/runtime";
GenClass.init();`)}`;
Expand All @@ -23,7 +23,7 @@ function hero(): HTMLElement {
const copy = h("button", { class: "btn btn-xs copy-btn", type: "button", html: `${icon("copy")}Copy` });
copy.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(`npm install @genclass/runtime\n\nimport { GenClass } from "@genclass/runtime";\nGenClass.init();\n`);
await navigator.clipboard.writeText(`npm install @genclass/runtime@beta\n\nimport { GenClass } from "@genclass/runtime";\nGenClass.init();\n`);
toast("Copied");
} catch {
toast("Clipboard is not available here");
Expand Down
2 changes: 1 addition & 1 deletion docs/adoption/listings/svelte.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Longer description, if the form has one:
> EventSource and user input in the browser and reports stale responses, duplicate requests, retries and failure
> storms; it starts in observe mode, which never changes a request. Its model (about 13 MB, cached) is downloaded from
> jsDelivr on a browser's first visit. The add-on writes GenClass's telemetry opt-out unless you choose to send
> anonymous diagnostics. `npx @genclass/runtime remove` undoes it.
> anonymous diagnostics. `npx @genclass/runtime@beta remove` undoes it.

## The GenClass README

Expand Down
2 changes: 1 addition & 1 deletion docs/adoption/listings/sveltesociety.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Testing; getting there means being noticed, not applying.
## What the entry needs

GenClass has no Svelte-specific package: the runtime is framework-agnostic, and the SvelteKit path is one import in
`src/hooks.client.ts` ([templates/sveltekit](../../../templates/sveltekit/README.md); `npx @genclass/runtime init`
`src/hooks.client.ts` ([templates/sveltekit](../../../templates/sveltekit/README.md); `npx @genclass/runtime@beta init`
writes it). The entry should therefore describe `@genclass/runtime` as it behaves in a SvelteKit app, which is what the
template's README already does honestly: it works from the network and user input (stale typeahead responses, duplicate
submits, failure storms, retries, stalls); Svelte runes and stores are not discovered automatically, so decisions that
Expand Down
2 changes: 1 addition & 1 deletion docs/adoption/listings/vue-and-angular.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ tool that finds leaked RxJS subscriptions in Angular apps.` A bullet, the name l
detects and highlights Angular components that are re-rendering", and `rxjs-leak-finder`). "Runtime" is for things
that run Angular itself (dynamic compile, platforms); "Performance" and "Networking" are stretches.

**Before submitting.** The Angular path is one import in `src/main.ts` (`templates/angular`, `npx @genclass/runtime
**Before submitting.** The Angular path is one import in `src/main.ts` (`templates/angular`, `npx @genclass/runtime@beta
init`), covered by the compat matrix (Angular 22 with SSR, `HttpClient` on fetch and on XHR). Angular signals are not
discovered automatically; the description must not imply they are.

Expand Down
29 changes: 28 additions & 1 deletion docs/runtime/ANALYTICS.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ Interaction counters get their own per-batch budget (200 counters), so they neve
(1 year on Enterprise), hourly rows 48 hours.

**Dashboard.** A "How visitors use this app" section on the app dashboard (`src/pages.ts`, every plan, the same range
picker), shown once the app has sent `interactions` events, using the page's existing table and card styles:
picker), filled once the app has sent `interactions` events (before that, the setup below, with the runtime they
need: section 4.1), using the page's existing table and card styles:

- page views, average engaged time per view, clicks, rage clicks, dead clicks, with the change against the previous
period, and page views on the timeline;
Expand Down Expand Up @@ -215,6 +216,32 @@ GenClass.init({ token: "gc_…", analytics: true });
`stopped`).
- `telemetry.sample` below 1 samples analytics with the page load; the dashboard does not scale counts up.

### 4.1 Which runtime: loud, never silent

Interaction analytics first shipped in `@genclass/runtime` **0.3.0-beta.1**, on npm's `beta` dist-tag (install it with
`npm install @genclass/runtime@beta`, `npx @genclass/runtime@beta init`, or a script tag pinned to
`@0.3.0-beta.1`). npm's `latest` is still `0.2.2` until 0.3.0 is stable, and `0.2.x` and `0.3.0-beta.0` have no
analytics: they ignore `analytics: true` without a word (reproduced on 0.2.2 in jsdom: no warning, no
`runtime.analytics`, nothing sent; a TypeScript app gets a type error for the unknown option). Those releases cannot
change, so every path that the newest line controls says it instead:

- **The runtime.** The script tag loads `dist/analytics/index.js` by URL from its base, so a self-hosted `dist/` can
lack it or hold files of two releases (an app's bundler ships the chunk with the runtime it came with, so the ESM
entries cannot mix them). The tag's loader (`src/cdn/analytics-loader.ts`) warns when the chunk does not load,
once per load: `[GenClass] analytics: <url> (@genclass/runtime <version>) did not load; nothing is recorded. Serve
every file of this version's dist/.`, and `runtime.analytics` reports `{ enabled: false, reason: "error" }`. It
passes the tag's version to the chunk, and a chunk of another release warns once per page with both versions and
the install line, and still records. Both live in the script-tag build and the lazy chunk: the ESM first load does
not grow for them.
- **The CLI.** `init` prints its version and the dist-tag it came from, says when npm has a newer release, and
upgrades an older `@genclass/runtime` in the project instead of keeping it (packages/runtime/README.md, "Which
version").
- **`genclass_setup`** (`@genclass/mcp`) prints `npm install @genclass/runtime@beta` for a project on 0.2.x, on a range
without a version (`*`, `latest`) or with an older installed copy.
- **The dashboard.** Under the copy-prompt button, the Visitors view of an app without interaction data says that
analytics need 0.3.0-beta.1 or later with the install line and names the app's older runtime versions from its own
telemetry; the Connect panel says the same.

## 5. Consent and opt-outs

- Every telemetry opt-out also turns analytics off: `telemetry: false`, `?genclass=no-telemetry`, `?genclass=off`,
Expand Down
13 changes: 9 additions & 4 deletions docs/runtime/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ hosts (`0.2.1` sends from them too, by mistake; fixed in `0.2.2`), outside a bro
[packages/runtime/TELEMETRY.md](../../packages/runtime/TELEMETRY.md).

**App token and dashboard** (0.3.0: `0.2.x` ignores `token`). `token: "gc_..."` (from
`npx @genclass/runtime init` or https://genclass.dev/start) is sent with each telemetry batch so the app's diagnostics
`npx @genclass/runtime@beta init` or https://genclass.dev/start) is sent with each telemetry batch so the app's diagnostics
show up on its private dashboard (`https://genclass.dev/dashboard/<secret>`; free on every plan, and
`npx @genclass/runtime dashboard` prints the link for the app in the current folder); with a token, decision events also
`npx @genclass/runtime@beta dashboard` prints the link for the app in the current folder); with a token, decision events also
carry `fn` (see "Protected functions"). Public; malformed values log one warning and are ignored; it does nothing
while telemetry is off (on a local host the console says so once). `rt.telemetry.token` reports it while telemetry
is on. `token` and `scope` are fixed when GenClass starts: on a runtime already started by `/auto` or the script tag,
Expand All @@ -78,6 +78,11 @@ whether the browser visited before. Every telemetry opt-out and Global Privacy C
event log: nothing reaches a situation, a decision or the model. `rt.analytics.start()` / `stop()` turn them on and
off later (consent; `stop()` drops the counts not sent yet and removes the visit entry). Also `data-analytics="on"`, `analytics=on` in the meta
tag, `window.GENCLASS_CONFIG.analytics`. Schema: [TELEMETRY.md](../../packages/runtime/TELEMETRY.md#interaction-analytics).
With the script tag, a chunk that does not load warns once per load (`[GenClass] analytics: <url> (@genclass/runtime
<version>) did not load; nothing is recorded. ...`, `reason: "error"`), and a chunk of another release than the tag
warns once with both versions. `0.2.x` (npm's `latest` until 0.3.0)
and `0.3.0-beta.0` have no analytics and ignore the option silently: install `@genclass/runtime@beta`
([ANALYTICS.md](ANALYTICS.md) section 4.1).

## Options

Expand Down Expand Up @@ -211,7 +216,7 @@ the model's recent median latency, 150 to 800 ms), then proceed unchanged; nothi
expected to answer within that budget. Serving your page with `Cross-Origin-Opener-Policy: same-origin` and
`Cross-Origin-Embedder-Policy: require-corp` enables WASM threads (about 3× faster without WebGPU).

Self-hosting the model: `npx @genclass/runtime fetch-model public/genclass-model` then
Self-hosting the model: `npx @genclass/runtime@beta fetch-model public/genclass-model` then
`GenClass.init({ model: { baseUrl: "/genclass-model/" } })`.

## State
Expand Down Expand Up @@ -670,7 +675,7 @@ never caps its `summary` record.
Sinks by name, with no code, in the zero-code entries (`/auto`, the script tag): `window.GENCLASS_CONFIG = { sinks:
["webhook:/genclass"] }`, `<meta name="genclass" content="sink=webhook:/genclass+console-json">` (`+` joins names,
since commas separate the meta keys), `data-sink="webhook:/genclass,console-json"` on the script tag, or
`npx @genclass/runtime init --sink webhook:/genclass` (the names are `console-json` and `webhook:<url>`;
`npx @genclass/runtime@beta init --sink webhook:/genclass` (the names are `console-json` and `webhook:<url>`;
`console-json:noevidence` makes its adapter with `evidence: false`). The names `sentry`, `posthog` and `datadog` (any
case, with or without an option) are left out at init with one console warning for them all, `Ignored sink <names>:
Sentry, PostHog and Datadog forwarding is not in the free runtime: https://genclass.dev/pricing`, and nothing is loaded or sent for them; `init --sink sentry` is a usage error that says
Expand Down
2 changes: 1 addition & 1 deletion docs/runtime/THREAT-MODEL.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ gate, or run hostile code.
([decide/policy.ts](../../packages/runtime/src/decide/policy.ts) `gate`). In `observe` (the default) nothing it says
changes anything (`test/invariants/observe.test.ts`).
- Inference runs in a module Web Worker; the ONNX graph has no network or DOM access.
- Self-hosting: `npx @genclass/runtime fetch-model <dir>` plus `model: { baseUrl, ortWasmPaths }` and a
- Self-hosting: `npx @genclass/runtime@beta fetch-model <dir>` plus `model: { baseUrl, ortWasmPaths }` and a
Content-Security-Policy limiting `connect-src`, `script-src` and `worker-src` to your origin
([README](../../packages/runtime/README.md#content-security-policy-and-self-hosting)). The script-tag build pins
every URL it loads later to its own version ([cdn/config.ts](../../packages/runtime/src/cdn/config.ts)).
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/astro/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
(`@genclass/runtime/auto`, or `/auto/guard` for `mode: "guard"`); `token` and `telemetry` are written to
`window.GENCLASS_CONFIG` by a virtual module imported before the runtime. The devtools overlay loads under
`astro dev` only (`devtools: "dev"`, the default), also in builds with `"always"`, never with `"never"`. Nothing runs
on the server. `astro` (4 or later; tested with 7.4) and `@genclass/runtime` (0.2.0 or later; `token` needs 0.3.0)
are peer dependencies. Keywords `astro-integration` and `withastro` for the integrations library and `astro add`.
on the server. `astro` (4 or later; tested with 7.4) and `@genclass/runtime` (`>=0.3.0-beta.1`, the newest
line: `npm install @genclass/runtime@beta`; npm's `latest`, 0.2.x, does not satisfy it) are peer dependencies. Keywords `astro-integration` and `withastro` for the integrations library and `astro add`.
- The runtime modules the script imports are added to Vite's `optimizeDeps.include`: the injected script is a virtual
module Vite's dependency scan does not see, and without this `astro dev` discovered them on the first page request
and re-optimized with a reload (a "504 Outdated Optimize Dep" console error on a cold start).
5 changes: 3 additions & 2 deletions packages/astro/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ export default defineConfig({
That is all the setup. The integration injects one script into every page, bundled by Vite with the page's other
scripts, whose first line is the runtime's `import "@genclass/runtime/auto"`: GenClass is installed when the page's
scripts run, before your islands hydrate and before their first request. On the server (SSR, endpoints, the build)
nothing is imported and nothing runs. The `@beta` tag gets `@genclass/runtime` 0.3.0, which the `token` option needs;
the stable `0.2.x` works with every other option.
nothing is imported and nothing runs. The `@beta` tag gets the newest runtime line, `@genclass/runtime`
0.3.0-beta.1 or later, which the peer range asks for (the `token` option, the dashboard and interaction analytics need
it); a bare `npm install @genclass/runtime` gets npm's `latest`, the older `0.2.x`, which does not satisfy it.

With options:

Expand Down
2 changes: 1 addition & 1 deletion packages/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"test": "vitest run"
},
"peerDependencies": {
"@genclass/runtime": ">=0.2.0 || ^0.3.0-beta.0",
"@genclass/runtime": ">=0.3.0-beta.1",
"astro": ">=4.0.0"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface GenClassOptions {
*/
mode?: "observe" | "guard";
/**
* The app's dashboard token (`gc_...`) from genclass.dev or `npx @genclass/runtime login`. Needs `@genclass/runtime`
* The app's dashboard token (`gc_...`) from genclass.dev or `npx @genclass/runtime@beta login`. Needs `@genclass/runtime`
* 0.3.0 or later; 0.2.x ignores it.
*/
token?: string;
Expand Down
Loading
Loading