GNL 0.17: early and moving fast — APIs can change between releasesJoin us

GNL
← Blog

A full agent framework, durable from the core

GNL teamarchitecturedurabilityframework

Durability is not a plug-in we added later. It is the core every other GNL package stands on. Use all of it, or only the core — and go from one to the other a step at a time.

In short#

GNL is a full agent framework for TypeScript, built on the Vercel AI SDK: a runtime, an HTTP server, Studio, workflows, RAG, memory, evals, auth and channels. You can use all of it. Or you can put only the core into an agent you already have.

Both are the same product, because everything else is built on that core.

Why the core is durability#

Agents stop in the middle. A process restarts after the refund was issued and before the reply was sent. A queue retries a turn. If durability is a layer added later, it sits outside a loop that does not know what it already did. It can run the loop again, but it cannot tell which tool calls already happened.

In GNL the core is a journal in your own database. Every model step and every tool call is recorded there. Call a run again with the same id, and finished steps replay from the journal: a tool that already ran does not run again, and only what never finished runs.

Every other package writes its work to the same journal, so it gets the same guarantee without building its own. A tool or a step a package adds is journaled like the core's: replayed after a crash, never fired twice. A Slack or Telegram message is run once, and the reply is posted once.

Start with the agent you already have#

The first step does not ask you to move anything. runDurable takes the arguments of generateText, plus two: an id for this piece of work (runId) and a journal in your own database.

agent.ts
import { runDurable, gnlTool } from '@gnldev/durable';
import { SqliteStorage } from '@gnldev/durable/sqlite';
import { openai } from '@ai-sdk/openai';
import { tool } from 'ai';
import { z } from 'zod';

const storage = new SqliteStorage('runs.db');

const refund = gnlTool(
  tool({
    description: 'Refund an order',
    inputSchema: z.object({ orderId: z.string() }),
    execute: async ({ orderId }) => ({ refunded: orderId }),
  }),
  { sideEffect: true, idempotency: 'args' },
);

const res = await runDurable({
  runId: 'refund-order-42',          // the id of THIS work — call again with it after a crash
  journal: storage.runs,
  model: openai('gpt-4o'),
  tools: { refund },
  prompt: 'Refund order 42',
});

Call it again with the same id after a crash. Finished steps replay from the journal, the refund is not issued a second time, and only what never finished runs.

Then add what you need#

Each step keeps what the one before gave you. None of them is a rewrite, and you can stop at any of them.

  1. A registry — createGnl: agents by name, work named by your own id, runs that outlive the call that started them, approvals.
  2. A server — createRestApi: REST, OpenAPI and server-sent events for every agent, with auth, limits and budgets.
  3. Studio — every run on a timeline, time-travel and fork, the approval queue, a playground.
  4. The packages — memory, workflows, RAG, evals, MCP, jobs, channels. Each works with the rest and on its own.

The full walk-through, with code for each step, is in the guide from the core to the whole framework.

What it does not do#

GNL is code-first: there is no visual agent editor and no voice. It does not replace the AI SDK; your models and tools stay as they are. The journal stays in your own database, and no run data is sent to us. The compare page lists these limits, and what other frameworks do that GNL does not.

Tip
Starting from nothing instead? npm create gnl@latest my-agent sets up the registry, the server and Studio in one go.