GNL
Compare

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.

Your applicationYour agent logic, prompts, tool definitionsVercel AI SDKgenerateText / streamText / tool-call loop — unchangedGNLjournal · never silently twice · deterministic replay

GNL doesn't replace the Vercel AI SDK — it sits underneath as a thin durability strip.

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.

AxisHand-rolledAgent frameworksGNL
Durability (crash → resume where it left off)Built by hand — checkpoint/state management is on youUsually orchestration-first (workflow/RAG/memory); durability added on top when it is offered at allCore guarantee — every step is written to the journal; under synchronous replication 30/30 acknowledged writes survived a live failover, backed by 4,900+ tests
The same side effect never happening twiceYou design the idempotency keys — and you have to notice when the model re-plans the same workRarely a core guarantee — and anything keyed on toolCallId is blind to the re-planned callDefault: 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 jobsNothing — we found no declared core guarantee on this axisA 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-travelUsually absent — manual logging/debuggingVaries — depends on that framework's own toolingTimeline + time-travel replay in Studio
Edge-native footprintDepends on your own dependenciesFull framework (workflow/RAG/memory) — larger surface area32.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 youUnspecified; depends on the framework integrationSQLite/Postgres/Redis journal — data always stays with you
Telemetry / phone-homeNone (it's your own code)UnspecifiedNone — 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 handUnspecifiedShips 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.

CapabilityGNL APINote
Dynamic multi-agent routing (Supervisor/.network())createGnl networks: + runNetworkRouter 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 scorersfaithfulness, hallucination, answerRelevancy, toxicity, bias, completeness, contextPrecision, toneConsistency — all wrap llmJudge, journal-memoized.
Dataset & experiment managementcreateDatasetsManagerContent-hashed version history, idempotent experiment recording, compare (aggregate deltas + regression/improvement).
GraphRAG (indirect-relevance retrieval)@gnldev/rag — GraphRagThreshold/hops/decay over a similarity graph; a VectorStore drop-in → exactly-once inside createRagTool.
Chunking + persistent vector DBchunkText/chunkDocuments + PostgresVectorStorerecursive/markdown/character strategy, deterministic id#i; pgvector (HNSW/ivfflat) shares the same interface as InMemory.
Semantic tool search@gnldev/processors — toolSearchEmbedding-based topK tool selection; the selection is journaled → embedding never runs again on resume (always/minScore).
Named SSO providerscreateAuth0Sso, createWorkOsSsoAuth0 (OIDC code + JWKS RS256) and WorkOS; centralized signature verification, CSRF state hook.
Named observability presets@gnldev/otel — otlpPresetsLangfuse/LangSmith/Braintrust/Honeycomb/Datadog/Collector in a single line; plus live mode (@gnldev/otel/live).
Redis storage adapter + distributed pubsub@gnldev/durable/redis — RedisStorageAtomic CAS via SET NX, native TTL cache; two-worker fan-out with @gnldev/events (tested against a real Redis integration).

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 PG+Redis takeover race.

4,900+ tests / 548 files green

5,004 tests pass across 548 files (measured 2026-09-14), including real Postgres and Redis integrations; every package typechecks clean. 65 tests and 5 files are skipped by default because they need an environment the suite cannot assume — among them the live failover scenario, which is a separate reproducible experiment (docker compose + GNL_FAILOVER=1) rather than part of the green count. Journal growth has been characterized, and rolloverRun solves compaction for long-lived runs.

Causal proof for the taint guard — 2/5 → 0/N

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 4 (InMemory/SQLite/Postgres/Redis); some frameworks offer many more. The guarantee is the same on all four, 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 framework replacement

It wraps the Vercel AI SDK's agent loop; it does not replace it or change generateText/streamText.

Not a no-code / visual builder

GNL is a code-first layer. Durable RAG (@gnldev/rag), workflows, and agent networks now ship out of the box — but it provides no no-code agent editor, voice, or ready-made chat channels.

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.

A layer underneath, not a replacement

GNL's distinguishing value is the exactly-once, deterministically replayable core. The orchestration surface on top of it exists so those guarantees reach the whole application — not as a claim to have replaced anything you are already using happily.

Don't replace your stack — add durability on top of it.

Read the docs