GNL, hand-rolling it, and the agent frameworks
Three ways to build the same thing: write the durability yourself, adopt a full agent framework and add durability on top of it, or start from a durability layer that sits on the Vercel AI SDK agent loop you already run. GNL is the third — and on that core it also carries RAG, workflows and dynamic agent networks, each one journal-backed and replayable rather than a separate subsystem. No product is named on this page. Which framework suits you depends on your team and your problem, and a comparison written by one of the candidates is not where that gets settled — go read theirs. What is worth writing down is the axis that actually differs, and what GNL deliberately does not do.
GNL is built on the Vercel AI SDK without changing its loop: a full framework whose every part stands on one durable core. Take all of it, or only the core.
Feature matrix
We line up all three approaches side by side on the axes where GNL's core distinction shows most clearly — durability, exactly-once, and deterministic replay. Parity on the orchestration surface (RAG/workflow/network) is covered separately in the "closed gaps" table below.
| Axis | Hand-rolled | Agent frameworks | GNL |
|---|---|---|---|
| Durability (crash → resume where it left off) | Built by hand — checkpoint/state management is on you | Usually orchestration-first (workflow/RAG/memory); durability added on top when it is offered at all | Core guarantee — every step is written to the journal; under synchronous replication 30/30 acknowledged writes survived a live failover, backed by 10,900+ tests |
| The same side effect never happening twice | You design the idempotency keys — and you have to notice when the model re-plans the same work | Rarely a core guarantee — and anything keyed on toolCallId is blind to the re-planned call | Default: keyed by toolCallId, proven with multi-worker CAS race tests. Opt into `idempotency: 'args'` and it keys on the ARGUMENTS instead — the tool runs once even when the model re-plans it under a brand-new toolCallId (the dominant real-world duplicate case) |
| A repeat of the same work in different words (where a hash is blind) | Nothing — “create product ABC” written two ways is two jobs | Nothing — we found no declared core guarantee on this axis | A candidate is found; the DECISION comes from comparing deterministic fields, and an unclear case becomes a question for a human. A score never decides — the order is deterministic > human gate > probabilistic. Off entirely unless both declarations are made (opt-in) |
| Deterministic replay / time-travel | Usually absent — manual logging/debugging | Varies — depends on that framework's own tooling | Timeline + time-travel replay in Studio |
| Edge-native footprint | Depends on your own dependencies | Full framework (workflow/RAG/memory) — larger surface area | 57.4 KiB gzip core, no cold-start cost |
| BYO-DB (your data stays with you) | Whatever you write to is what you get — it stays with you | Unspecified; depends on the framework integration | SQLite/Postgres journal — data always stays with you |
| Telemetry / phone-home | None (it's your own code) | Unspecified | None — as a principle. The B/C plans only send a signed AGGREGATE usage check-in |
| Governance (approval queue, audit, retention/GDPR, policy, eval gate) | Built by hand | Unspecified | Ships out of the box with Studio |
“Varies” means the axis is not a stated core guarantee across the frameworks we looked at — it differs by product and by version. Check the documentation of the one you are weighing rather than trusting a table we wrote.
The orchestration surface
What people expect from a full agent framework, carried on the same journal — so each of these is exactly-once and replayable rather than a subsystem with its own rules. Every row maps to a named API in the source.
| Capability | GNL API | Note |
|---|---|---|
| Dynamic multi-agent routing (Supervisor/.network()) | createGnl networks: + runNetwork | Router decisions are frozen into the journal via CAS → the router is never called again on resume; capped by maxIterations plus a mandatory final step. |
| Built-in scorer library | @gnldev/evals — 8 scorers | faithfulness, hallucination, answerRelevancy, toxicity, bias, completeness, contextPrecision, toneConsistency — all wrap llmJudge, journal-memoized. |
| Dataset & experiment management | createDatasetsManager | Content-hashed version history, idempotent experiment recording, compare (aggregate deltas + regression/improvement). |
| GraphRAG (indirect-relevance retrieval) | @gnldev/rag — GraphRag | Threshold/hops/decay over a similarity graph; a VectorStore drop-in → exactly-once inside createRagTool. |
| Chunking + persistent vector DB | chunkText/chunkDocuments + PostgresVectorStore | recursive/markdown/character strategy, deterministic id#i; pgvector (HNSW/ivfflat), Qdrant, Chroma and Weaviate share the same interface as InMemory and pass one vector kit. |
| Semantic tool search | @gnldev/processors — toolSearch | Embedding-based topK tool selection; the selection is journaled → embedding never runs again on resume (always/minScore). |
| Named SSO providers | createAuth0Sso, createWorkOsSso | Auth0 (OIDC code + JWKS RS256) and WorkOS; centralized signature verification, CSRF state hook. |
| Named observability presets | @gnldev/otel — otlpPresets | Langfuse/LangSmith/Braintrust/Honeycomb/Datadog/Collector in a single line; plus live mode (@gnldev/otel/live). |
Evidence — not a claim, a passing test
GNL's exactly-once/deterministic-replay claim isn't just talk — it was tested against real infrastructure.
Live failover test — 30/30 writes survived
In a real Postgres primary-SIGKILL → promote scenario with synchronous replication, all 30/30 acknowledged CAS writes survived and exactly-once held; atomicity on the new primary was proven with 10 rounds of race testing (test/failover-real.test.ts).
Multi-worker CAS race tests
CAS does not assume a single connection; 13 scenarios verify it stays atomic when multiple instances try to write to the same run (test/multi-worker.test.ts). A split-brain edge case that was found was CLOSED with putIfMatch CAS — verified against a real Postgres takeover race.
10,900+ tests / 1,144 files green
10,959 tests pass across 1,144 files, none failing (measured 2026-11-07 in the public repository at commit 8fab1865, version 0.17.0, the tree you can clone; the v0.17.0 release gate got the same count). 259 tests and 43 files are skipped by default because they need a server the suite cannot assume. The real-database gate, check:real with --failover, is measured apart and not added to that count, since the two runs can share tests: at commit be402682, 3,249 pass in its shared run and 53 in its run of the tests that terminate backends, and its failover leg passes 2 of 2. Its legs that need a real model provider were not run (no key). The live failover scenario is a reproducible experiment (docker compose + GNL_FAILOVER=1). Journal growth has been characterized, and rolloverRun solves compaction for long-lived runs.
Causal proof for the taint guard — 2/5 → 0/5
Same model, same prompt-injection scenario: with the guard off, the attacker's transfer succeeds in 2 of 5 attempts; with the guard on (block or taintGuardian) it never succeeds (gnl-eval, measured 2026-08-29).
Verified across 8 different runtimes
The same HTTP/Studio handler was actually mounted and kept working on Hono, node:http, Express, Fastify, Koa, Nest, Bun, and Deno (server-matrix, measured 2026-08-29).
Near-linear scaling under load
Under PM2 cluster mode with real Postgres and SSE, throughput went from 24.5 turns/s on 1 worker to 87.3 turns/s on 4 (3.38x) and 135.8 turns/s on 8; 600 concurrent booking attempts produced exactly 200 reservations and 200 ledger rows, no more (performance-test, measured 2026-08-29).
BYO-DB: SQLite to Postgres in one line
The same agent code, installed from a real npm package (not a symlink), ran against SQLite and Postgres storage with only a single line changed (trial-sqlite/trial-pg, measured 2026-08-29).
What the frameworks do that GNL does not
Honesty cuts both ways. If you need any of these, a full agent framework is the better tool and you should use one — these are out of scope here, or simply not built yet:
Voice (TTS/STT)
Speech input/output. Several frameworks have it built in; GNL leaves it out of scope.
Channels (Slack/WhatsApp)
Ready-made Slack/WhatsApp-style channel integrations. GNL deliberately skips these — it is a layer under your application, and your application already owns its channels.
No-code / visual agent editor
A visual, drag-and-drop agent builder. GNL is code-first and offers none.
~16 storage adapters
Breadth of storage backends. GNL has 3 (InMemory/SQLite/Postgres); some frameworks offer many more. The guarantee is the same on all three, but the range of choices is narrower.
ClickHouse observability adapter
Native adapters for observability backends like ClickHouse. GNL goes through OTLP/OTel with named presets, which reaches most of them, but there is no direct adapter.
What GNL is NOT
Setting expectations is part of positioning — areas GNL does not cover:
Not a replacement for the AI SDK
GNL is built on the Vercel AI SDK's agent loop. It does not replace generateText/streamText or change them — your models and tools stay as they are.
Not a no-code / visual builder
GNL is code-first. Durable RAG (@gnldev/rag), workflows, agent networks and Slack/Telegram channels ship out of the box — but there is no no-code agent editor and no voice.
Not a telemetry/analytics platform
No phone-home; as a principle, no run data or content is ever sent to GNL.
Not mandatory hosting
The journal stays in your own database — GNL never hosts your data.
Take all of it, or only the core
Every part — server, Studio, workflows, RAG, evals, auth, channels — stands on the exactly-once, deterministically replayable core and carries its guarantees. Use the whole framework, or put only the core into an agent you already have.
Design partners
Using the AI SDK in a real project? Let's shape GNL around it.
We're working closely with a small number of teams who put GNL into a real project.
- Direct access to the person who writes the code
- One-on-one help with the integration
- A say in the roadmap: what you need moves up
We ask one thing: use it for real and tell us what breaks.
The form opens here and runs on Google Forms; we don't keep personal data on our own servers.