Website and docs: genclass.dev · Discord · Docs · Benchmarks · Pricing
A runtime for web apps. It watches the app from the inside and uses a small local model to flag, and optionally prevent, stale responses, races, duplicate requests, inconsistent state and failure storms.
One line, first in <head> or first in your entry file:
<script src="https://cdn.jsdelivr.net/npm/@genclass/runtime@0.3.0-beta.1"></script>import "@genclass/runtime/auto"; // after npm install @genclass/runtime@betaUse 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
is observed only). Observe mode by default: it reports only. npx @genclass/runtime@beta init writes the line for you;
GenClass.init() is the explicit setup (package README).
GenClass Runtime records what the app does: user actions, async operations and their causal chains, network traffic (fetch, XHR, WebSocket, EventSource), store writes with per-field versions, errors and timing. It computes generic facts about each write, request and response, and asks a small GenClass model only about salient situations. 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, 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. AI coding agents: start at AGENTS.md.
Pick one; all of them start the same runtime in observe mode unless you choose otherwise. Full install guide, with
init flags, page configuration and the script-tag attributes: package README.
npx @genclass/runtime@beta init # finds the framework, adds the import, shows the diff first
npm install @genclass/runtime@beta # then: import "@genclass/runtime/auto" (or GenClass.init())To audit any page for visual bugs without adding anything to it: npx @genclass/runtime@beta scan <url> (alias
audit; a score and each problem with its element and a suggested CSS fix; Audit a page).
Coding agents get the same as genclass_scan in @genclass/mcp.
The package is ESM only (no require). Jest users need transformIgnorePatterns that lets
@genclass/runtime be transformed.
- One console line saying anonymous diagnostics are on (
[GenClass] Sends anonymous diagnostics (decisions and counts, no page text) to improve the model. Opt out: …); since0.2.2it does not appear onlocalhostor other local hosts (0.2.1sends and shows it there too, by mistake). [GenClass] Model ready (genclass-runtime-r17, wasm, q8, 0.4s). Mode: observe.once the model has loaded at idle.- Then possibly nothing for a while. Observe mode reports only salient situations (a response landing over newer data, a repeat, a failure streak, a broken relation, an error), and the first response of each kind after a page load is never a delivery decision: the runtime first has to see what that request writes.
- The devtools overlay:
Alt+Shift+Gon localhost (theinitcommand loads it in development only). - The first-visit cost: about 13 MB downloaded from jsDelivr on WASM (model 10 MB plus ONNX Runtime's wasm) or
18 MB on WebGPU, cached afterwards, and 190–280 MB of memory in a Web Worker while the model is loaded. Devices
that report 2 GB of memory or less, and Save-Data connections, do not load the model by default (they observe
without it and say so in one console line; new in
0.3.0:0.2.xloads everywhere and only lazily on Save-Data). For phones and strict Content-Security-Policies see self-hosting andloadIfin the package README.
@genclass/mcp (unreleased) is an MCP server for Claude Code and other coding agents. Your app's development build
connects to it with one line, and the agent gets every click, request, the order the responses arrived and what they
wrote, uncaught errors with the click and request behind them, and the on-device model's detections. It can run your
steps again with the network answering in different orders (and slower) to find what depends on timing, replay the
exact order that broke, and write a Playwright test that fails until the fix is in. Everything stays on your machine.
claude mcp add genclass -- npx -y @genclass/mcp@0.1.0import { GenClass } from "@genclass/runtime";
import { connectAgent } from "@genclass/runtime/agent";
const rt = GenClass.init();
if (import.meta.env.DEV) connectAgent(rt); // development builds onlyOn a real page in headless Chromium, it found a stale-results race in 5 of 6 reordered runs of a page that never showed it on the developer's fast backend, with no false report on the fixed page (AGENT.md, RESULTS.md section 16).
In 0.3.0-beta.0 (@beta on npm): 0.2.x ignores token and has no protect() export (an import of it fails),
and its init creates no token. Details: CHANGELOG, 0.3.0-beta.0.
import { GenClass, protect } from "@genclass/runtime"; // 0.3.0
GenClass.init({ token: "gc_your_app_token", scope: "functions" }); // scope: decide only inside protect()ed functions
// what GenClass finds here (a double submit, overlapping calls, a failure) is reported under "checkout submit"
export const submitOrder = protect("checkout submit", (cart) => fetch("/api/orders", { method: "POST", body: JSON.stringify(cart) }));- Token and dashboard. A token (
gc_plus 22 letters and digits, public) groups your app's diagnostics under a private dashboard link (https://genclass.dev/dashboard/...). The dashboard is free for every app, with or without an account. Get one at genclass.dev/start, or letinitcreate it after you approve its diff. Or keep the dashboards in an optional genclass.dev account:npx @genclass/runtime@beta loginsigns in from the terminal (device flow) and connects the app in the current folder;connect,whoamiandlogoutdo the rest. The dashboard fills from deployed pages with telemetry on. Local and private hosts (localhost,*.local,*.test, private networks) do not send by default;telemetry: trueor any telemetry options object (telemetry: { ... }) is an explicit opt-in that sends from them too, with the token. Data is kept for 90 days. protect(name, fn)returns a function with the same signature (samethis, arguments, return value and errors); decisions about what it causes carryfn: name. Without a runtime it just callsfn. Package README.- More sensitive observe mode. Observe also reports possible detections (below the report gate, at the
eagerprofile's value; oneconsole.debugline each, ending in(possible); yourrt.on("detect")and report function get them only withdetect: "possible";detect: "likely"turns them off), and decides what happens while the model loads or the tab is hidden once it can, instead of skipping it, reporting those at the possible tier at most until their false-report rate is measured. Their telemetrydetectevents carrytier: "possible"; nothing new about the page is sent. Package README.
-
Observe. Fetch, XHR, WebSocket, EventSource, DOM events, errors, navigation, storage, long tasks and timers are instrumented. Async work is linked into causal chains. Stores created through GenClass, or wrapped with its Redux, Zustand, React or generic adapters, get per-field versions and write histories.
-
Learn. The runtime learns per-operation baselines (latency, failure rate, request rate) and which fields each operation usually writes. It also learns cadences (polling, debounced saves) and relations that keep holding (
total == sum(items[*].price × qty),selectedId ∈ items[*].id). -
Triage. Cheap facts are computed for every write and request. Only salient situations reach the model: a response landing over newer data, a repeated request, a failure streak, a broken relation, an error.
-
Decide at the network boundary. Responses and push messages are judged before the app sees them:
deliver(passive);discard(deliver, but drop the writes it makes over newer data);defer(wait for related in-flight work).
Store writes are never held or reordered by default. Salient writes that no delivery decision covered are decided in the background, and can be reverted within 2 s under strict rules.
-
Act by mode.
mode behaviour observe(default)reports only; never holds, delays or changes anything guard(opt-in)discard,defer,coalesce,delay, only when the model's gate for that trigger says acting beats doing nothing by the margin of the chosenaggressivenessprofileheal(experimental)also retry,serve_cached,block,hedge,rollback,resyncand custom actions, at the heal margin of the profileDetections are reported when the top diagnosis is not
expectedwith probability at or above the profile's report threshold (0.95 / 0.90 / 0.70 for cautious / balanced / eager).rt.gates()shows what is in force. -
Explain.
- Every detection and intervention is logged in plain English, with the exact situation text the model read.
- Reversible actions can be undone.
?genclass=offinstalls nothing.
@genclass/runtime@0.3.0-beta.1with the model@genclass/runtime-model@0.2.0. The model and the situation text it reads (situation-v2.3) are the same as in 0.2.x; the numbers below were measured with them.
Runtime: works and is unit-tested (situation format tag
situation-v2.3). Install withnpx @genclass/runtime@beta init, one import (@genclass/runtime/auto) or one script tag.Default mode is
observe: the model diagnoses salient situations and the runtime reports likely problems, without changing what the app does (it never holds or delays a response).guardis opt-in;healis experimental.How eager it acts once you opt in:
aggressiveness: "cautious" | "balanced" (default) | "eager".Model:
genclass-runtime-r172.0.0-rc4t (r17-v2dT, gain gate, 10 MB (10.2 MB q8), about 180 ms per decision on one WASM thread; WASM or WebGPU), loaded from jsDelivr at idle and cached. On held-out data (RESULTS.md §1):
profile guard FIR guard recall (clear / real) heal FIR heal recall (clear / real) report threshold cautious0.005% 2.4% / 1.4% 0.26% 3.7% / 3.8% 0.95 balanced(default)0.13% 7.8% / 7.5% 0.59% 7.1% / 10.4% 0.90 eager0.54% 14.2% / 21.0% 1.84% 17.1% / 24.2% 0.70 Our targets are guard FIR ≤ 0.1% and heal FIR ≤ 0.5%.
cautiousstays under both;balancedis slightly over both (0.13% and 0.59% on simulated apps);eageris well over. FIR on held-out real apps was 0.00% for every profile. Recall is still low: atbalanced, guard acts on under 8% of the cases where acting would help. Installed from the registry into a fresh app, guard mode fixed an out-of-order typeahead in 6 of 6 trials, and clean typing made 0 model calls. See the model card.Options (activation, route scopes, protected endpoints, breaker, shadow, veto, action limits, sinks): OPTIONS-SPEC.md.
Privacy notice: anonymous diagnostics are on by default, without page text, and off on localhost (since
0.2.2;0.2.1sends from localhost too, by mistake); opt out withGenClass.init({ telemetry: false })or?genclass=no-telemetry. See Privacy and telemetry.
- Decisions are local. Situations are built and decided in the browser by the local model; nothing is sent anywhere to make a decision.
- Anonymous diagnostics are on by default with
GenClass.init()in a browser, except on local and private hosts (localhost,*.localhost,*.local,*.test,127.0.0.1,::1, private IP ranges) (0.2.1sends from local hosts too, by mistake; fixed in0.2.2, see the CHANGELOG), in Node/SSR, and increateRuntime(). They go to the GenClass maintainers' collector (telemetry-worker/, a Cloudflare Worker writing to a private R2 bucket) and are used to improve the model. - By default no text from your page is sent. Sent: a random per-page session id, versions, mode, device class, the app's hostname and id-normalised path (from
0.3.0a segment with an@, such as an e-mail address, is:idtoo), and per decision (every detection and every decision whose diagnosis is notexpected, every action and veto; of the routine ones, one in four and at most 200 per page load; counts are exact;0.2.2and earlier send every decision) the trigger, the diagnosis and its confidence, the action and gate, the outcome and latency. The collector stores no IP address or user agent. One console notice per page says it is on. - The measured visual checks send nothing (new in
0.3.0): their findings stay in the page (the console,rt.checks(), your own sinks). - The situation text the model read is sent only if you opt in with
GenClass.init({ telemetry: { include: { situation: true } } }). It is redacted by field meaning, but can still contain route names with query strings, short error messages and short field values; read TELEMETRY.md before enabling it. - Opt out:
GenClass.init({ telemetry: false }),?genclass=no-telemetry,localStorage.setItem("genclass.telemetry", "off"); browsers sending Global Privacy Control are never collected. - Interaction analytics are off unless the app turns them on (
analytics: truewith the app's token, new in0.3.0-beta.1): per page view the route, engaged time, scroll depth, and clicks, rage clicks and dead clicks per element, the element named from markup, never its text. No visitor id, nothing stored on the device, every opt-out above applies, and they never feed a decision. Where analytics need consent (EU, EEA, UK), start them after consent withruntime.analytics.start(). See TELEMETRY.md. - With a token (
0.3.0:tokenoption,data-token,token=in the meta tag), each batch also carries your app's public token, so the same diagnostics also show on your app's private dashboard (whoever holds its link can open it), and decisions aboutprotect()ed functions carry the function's name (fn, the label you chose). Without a token neither is sent; without telemetry, a token does nothing. - Possible detections (
0.3.0, observe): theirdetectevents carrytier: "possible"(a likely detection has notier, as before), and the page-load summary counts them apart from detections (possibleDetections). They say which tier a detection was in; nothing new about the page is sent. - The CLI contacts genclass.dev only for the command you run:
initcreates a dashboard only after you approve its diff (it sends thenamefrompackage.json);loginasks for a sign-in code withclient_name: "genclass-runtime <version>"(shown on the approval page and in your account's session list) and polls until you approve it;whoamiandconnectread your account's apps;connectsends a new app's name, or an existing dashboard's link from.genclass.localto add it to your account;logoutends the CLI session. The account requests carryuser-agent: genclass-runtime-cli/<version>. What an account stores: PRIVACY.md. - Apps that ship GenClass may need to disclose this to their users (GDPR/CCPA). Full schema, storage and guidance: TELEMETRY.md.
- SECURITY.md: how to report a vulnerability, supported versions.
- packages/runtime/INTERCEPTION.md: every browser API GenClass wraps or listens
to, what each mode may change, what it never does, every action's preconditions and undo, and how
disable()and?genclass=offrestore the page (checked against the code by a unit test). - docs/runtime/THREAT-MODEL.md: page scripts, the model supply chain, crafted responses, denial of service, telemetry and state discovery, with mitigations and residual risks.
rt.audit()exports a structured record of every decision and action; payment, checkout and sign-in endpoints should stay observe-only (requests: { protect: protectPreset("payments", "auth") }).
Full guide (modes, triggers, adapters, observability, performance, privacy, known limitations): packages/runtime/README.md. API: docs/runtime/API.md.
All numbers are on held-out data, with each recall reported next to its false-intervention rate (FIR). Source: docs/runtime/RESULTS.md.
| what | result |
|---|---|
Round 1 model, R17 (9.6 MB int8; previous format situation-v1), simulated apps |
diagnosis 90.5%, action 81.9%, guard FIR 0.05%, heal FIR 0.24%, calibration error 0.009. Recall on clear stale/duplicate cases is only 7.7% (precise but timid). |
| Why round 1 was timid (sim/SEPARABILITY.md) | Many clear cases had benign twins with identical visible facts; 24% of clear rows were mislabelled expected; labels assumed knowledge a runtime cannot have. v2 adds measured facts and fixes the labels. |
r17-v2dT (situation-v2; shipped as @genclass/runtime-model@0.2.0) |
Gain gate with three profiles (table above). balanced: guard FIR 0.13%, heal FIR 0.59% on simulated apps (slightly over the 0.1% / 0.5% targets); cautious: 0.005% / 0.26% (under). 0.00% on held-out real apps for every profile. Calibration error not yet measured for this model (r17-v2b: 0.021–0.023). |
r17-v2b (shipped as @genclass/runtime-model@0.1.0) |
Simulated apps: diagnosis 84.2%, action 77.8%. Real apps: diagnosis 83.6%, action 80.0%. Guard FIR 0.01%, heal FIR 0.07%; heal acts on 5.9% of clear simulated cases and 5.7% of actionable real-app cases; guard on 0.6%. |
| Training continues | teacher model, distillation, DAgger rounds; ~10M simulated gold rows, ~50M unlabeled rows, ~0.6M real-app rows |
| Never make a correct app worse (always-passive model, heal vs observe, 66 real apps × 6 seeds) | 0/396 clean runs changed: final page text (inputs and alerts excluded) and server state. Request timing and store contents are not compared, nor is observe mode against no runtime. With chaos: 3/198 changed. |
| Same check on the v1 runtime (store-write holds) | 4/198 clean runs changed; the React/Redux RealWorld app never rendered its home feed |
Demos (model 0.2.0, balanced, 10 chaos + 5 clean seeds per demo and mode) |
The chaos bug rate over the six demos was 72% off, 70% observe, 72% guard and 73% heal (differences under 5 points are noise): nothing is healed at the shipped gates, because candidate gains rarely clear the margins; 0 false interventions on clean runs. |
| path | what |
|---|---|
packages/runtime |
@genclass/runtime, the library. Observers, causality, stores and adapters, learned baselines/relations/profiles, facts, triage, policy gate, actions, model host (Web Worker, ONNX Runtime Web on WebGPU/WASM), devtools overlay. README · CHANGELOG |
packages/mcp |
@genclass/mcp (unreleased), the MCP server for coding agents: pages connect through @genclass/runtime/agent; the agent gets findings in causal order, replays, explore verdicts and generated regression tests (AGENT.md). README |
packages/playwright |
@genclass/playwright (unreleased), GenClass for Playwright: a fixture that puts the runtime on every page under test (observe mode, seeded network chaos with page.route, the model skipped by default) and writes one race file per test, and a reporter that turns those files into one markdown report for the pull request, with the evidence line and what reproduced across seeds. README |
packages/nuxt, packages/astro |
@genclass/nuxt and @genclass/astro (unreleased): the runtime as a Nuxt module and as an Astro integration, one line in the framework's config, with options for the mode, the token, telemetry and the devtools overlay; browser only. Nuxt README · Astro README |
packages/runtime-model |
@genclass/runtime-model, the default model package (data only; files/ is gitignored and filled at release time) and its model card |
sim |
Training-data simulator: random apps run on the real runtime in a deterministic virtual world, labelled by counterfactual outcomes |
realapps |
128 real apps (written for the corpus, plus open-source ones) run with the real runtime in headless Chromium, labelled the same way; the never-worse sweep covered the first 66. README |
training |
Vocabulary pruning, curriculum (curriculum/rt.py mirrors the runtime's renderer), multi-node CPU training on Azure, int8 ONNX export, evaluation (EVAL.md) |
demos |
Six demo apps (typeahead, autosave, checkout, flaky dashboard, live kanban, real-time decisions) with a Service Worker chaos backend and Playwright trials; bench/heal runs them per mode, bench/visual measures the visual checks on them |
compat, templates |
Framework compatibility matrix (7 apps, 15 data layers, every mode; RESULTS.md) and eight starter templates with the one line (React + Vite, Next.js, Vue + Vite, SvelteKit, Astro, Nuxt, React Router, Angular; templates/README.md) |
docs/runtime |
Results · Coding agents · Architecture · Build contract · API · Threat model |
telemetry-worker, site |
The diagnostics collector (Cloudflare Worker) and the genclass.dev site |
research |
Earlier GenClass/Jev research (voice-control Chrome extension, the jev_local encoder and server, classifier benchmarks); the training scripts still use jev_local (README) |
AGENTS.md, CONTRIBUTING.md |
How to work in this repository |
Node ≥ 20. From the repo root:
ONNXRUNTIME_NODE_INSTALL=skip npm ci # the root lockfile is committed; skip onnxruntime-node's CUDA download
npm run typecheck -w @genclass/runtime
npm run build -w @genclass/runtime
cd packages/runtime
NODE_OPTIONS=--expose-gc npx vitest run --exclude "test/browser/**" --exclude test/review-perf.test.ts
NODE_OPTIONS=--expose-gc npx vitest run test/review-perf.test.ts --retry=2 # timing budgets, run aloneThe same steps run in CI (.github/workflows/ci.yml, Node 22) on pushes to main,
runtime, mvp and mvp-v2, on pull requests, and on manual dispatch. 667 unit tests pass in CI (14 skipped: the
model-parity tests, which need GENCLASS_MODEL_DIR), plus the 4 perf tests, which time a 5,000-item store and run
in their own step because they can fail under parallel load.
Releases are published by .github/workflows/release.yml from a v* tag, with npm
provenance.
Not covered by CI:
- Playwright browser tests:
npm run test:browserinpackages/runtime. @genclass/playwright: its typecheck, build, unit tests and the fixture's end-to-end check in Chromium run in their own workflow, .github/workflows/playwright-reporter.yml, on pushes and pull requests that touch the package; it never blocksci.yml.- The npm tarball smoke test:
test/smoke/smoke.sh. - The sim, the real-app corpus, the demos' evaluation, and training.
These are heavier or need Azure; see AGENTS.md before running them.
The training format is tagged. The current tag is situation-v2.3; the shipped model was trained on data from
situation-v2. Any change to packages/runtime/src/situation/* (or other text the model reads) changes the model's
input and means a new tag and regenerated data (training/curriculum/rt.py mirrors the renderer and must change
with it).
Apache-2.0. See NOTICE.