Skip to content
PluginStats

@gnldev/chat-adapter weekly downloads statistics

By gnldev (15 listings) · npm · AI SDKs

Vercel AI SDK `useChat` compatibility for @gnldev/durable agents: UI message stream conversion (with sentinel masking), journal→UIMessage history reconstruction, and a Hono chat route.

View on npm Stale · Oct 18, 2026

@gnldev/chat-adapter has 384 recorded weekly downloads on npm.

Weekly downloads
384
Official API · Oct 18, 2026
7-day growth
—
Insufficient history
30-day growth
—
Insufficient history
90-day growth
—
Insufficient history
Rank
—
Among tracked npm listings

About @gnldev/chat-adapter

@gnldev/chat-adapter

Compatibility with the Vercel AI SDK's UI layer: run a durable agent on the server, render it with useChat on the client.

Install

Install: pnpm add @gnldev/chat-adapter — or use it from a repo clone: pnpm install && pnpm -r build.

npm i @gnldev/chat-adapter

On the REST API (recommended)

Mount the chat format on @gnldev/server's REST API. The API's auth decides who the caller is, which organization they are in and whose run this is — the same gates as every REST route — and there is no second identity resolver to write:

import { createRestApi } from '@gnldev/server';
import { chatSurface } from '@gnldev/chat-adapter';
import { roleAuth } from '@gnldev/auth';
import type { CreateGnlConfig } from '@gnldev/durable';

declare const config: CreateGnlConfig;

export const api = createRestApi(config, {
  auth: roleAuth({ endUsers: { secret: process.env.GNL_END_USER_SECRET!, orgId: 'acme' } }),
  surfaces: [chatSurface()], // POST /agents/:name/chat — useChat({ api: '/agents/pay/chat' })
  cors: { origins: ['https://app.example.com'] }, // if the browser calls this API directly
});

A request with no valid credential is refused before the model runs. An end user's turn is filed under that user and their organization, shows up in their GET /runs, and is visible to their organization's staff in Studio. chatSurface({ path }) changes the mount path.

Sending the user's token from useChat. With end users, a request without a token is a 401, so the browser attaches one on every request. headers may be an async function, so each request asks your token route for a current one:

<!-- doccheck: skip — @ai-sdk/react is the browser app's dependency, not this repository's -->
import { useChat } from '@ai-sdk/react';
import { DefaultChatTransport } from 'ai';
import { tokenFrom } from '@gnldev/client';

const getToken = tokenFrom('/gnl-token'); // your app's route (subjectTokenEndpoint in @gnldev/auth)

const chat = useChat({
  transport: new DefaultChatTransport({
    api: 'https://api.example.com/agents/pay/chat',
    headers: async () => ({ authorization: `Bearer ${await getToken()}` }),
  }),
});

Chat route (standalone)

For a host that authenticates the request itself (a session cookie — with SameSite — or a verified token) and tells the route who is calling through identify. In production it refuses to start without identify. Your identify may answer undefined for a visitor with no session: that visitor is served as anonymous, with the checks below.

import { createChatRoute } from '@gnldev/chat-adapter';
import type { Principal } from '@gnldev/auth';

// Your session lookup: who this request is, as YOUR server established it.
declare function userOf(req: Request): Promise<string | undefined>;

app.route('/api', createChatRoute({ gnl }, {
  identify: async (req): Promise<Principal | undefined> => {
    const id = await userOf(req);
    return id ? { kind: 'subject', id, roles: [] } : undefined;
  },
}));
// POST /api/agents/:name/chat — useChat({ api: '/api/agents/pay/chat' }) works unchanged.

The route streams back in the AI SDK's UI-message format, so an existing useChat frontend works unchanged — while the run behind it is journaled, replayable, and its side effects are at-most-once.

You normally do NOT need resolveRunId: the default derivation ${body.id}:${lastMessage.id} gives one durable run PER TURN (a network retry of the same turn replays; a new turn runs fresh). Anti-pattern to avoid: resolveRunId: (_c, body) => body.id — useChat's body.id is stable for the WHOLE conversation, so every later turn would replay turn 1 from the journal forever. What that derived string is — a name the engine hashes into an id, or the id itself — depends on whether the route can name the user; see the idempotency contract.

A request with no identity: what the route refuses

A request carries an identity when identify resolved a caller for it. One that did not — the route has no identify, or identify answered nothing for it (an anonymous visitor; identify: () => undefined for everyone) — has no identity to check, so it is refused what a REST API with no auth refuses — the same two checks, from the same @gnldev/auth functions (doorGate):

  • a request addressed by a name other than localhost, a *.localhost name or an IP address → 403 host_not_allowed. That is what a DNS-rebinding page sends; it is same-origin to the browser, so without this it could replay a run and read the thread back.
  • a state-changing request a page on another site sent (Sec-Fetch-Site: cross-site) → 403 cross_site_refused. A plain <form> on any site could otherwise run the model and write into a conversation.

Nothing changes on this machine: localhost, 127.0.0.1, a LAN IP, a UI on another localhost port (or on 127.0.0.1 calling localhost), curl or a mobile app with no Origin, and an in-process fetch are answered. A name of your own (a tunnel, Codespaces, a Docker service) is one line: createChatRoute(…, { allowedHosts: ['.ngrok-free.app'] }), or GNL_ALLOWED_HOSTS=.ngrok-free.app in the environment. A widget embedded on other sites with no sign-in is one line too: createChatRoute(…, { allowCrossSite: true, allowedHosts: ['chat.example.com'] }) — allowCrossSite lifts the cross-site check only. A request identify resolved a caller for gets neither check.…

Description from the npm listing.

  • ai
  • agent
  • llm
  • typescript
  • ai-sdk
  • durable
  • exactly-once
  • usechat
  • ui-stream

README badge

Show your rank in a README or on your website. The badge updates automatically.

Charts

Images: 30d · 1y · all time · 1600×900 dark

Adoption over time

@gnldev/chat-adapter weekly downloads on npm, last 90 days: 384 weekly downloads as of Oct 18, 2026.@gnldev/chat-adapterWeekly downloads on npm · Last 90 days384downloads / week340360380400420440Jul 20Aug 4Aug 19Sep 3Sep 18Oct 3Oct 18downloads / weekOct 18, 2026 – Oct 18, 2026 · 1 observations · Updated Oct 18, 2026pluginstats.com
@gnldev/chat-adapter weekly downloads on npm, last 90 days: 384 weekly downloads as of Oct 18, 2026.

7/30/90-day growth

@gnldev/chat-adapter weekly downloads growth on npm: 7-day growth unavailable, 30-day growth unavailable, 90-day growth unavailable.@gnldev/chat-adapterWeekly downloads growth on npm384downloads / week now7-day growthinsufficient historyInsufficient history30-day growthinsufficient historyInsufficient history90-day growthinsufficient historyInsufficient historyGrowth vs. nearest observation to each baseline date · Updated Oct 18, 2026pluginstats.com
@gnldev/chat-adapter weekly downloads growth on npm: 7-day growth unavailable, 30-day growth unavailable, 90-day growth unavailable.

Ranking history

@gnldev/chat-adapter has no ranking history on npm yet.@gnldev/chat-adapternpm ranking · by weekly downloads · Last 90 daysNo ranking observations in this periodno observations · 0 observations · Updated Oct 18, 2026pluginstats.com
@gnldev/chat-adapter has no ranking history on npm yet.

Listing placement

All npm2,422 listings · 122 pages · 1,212 shown · page 84, #19 (overall #1,679)
All npm: every listing in weekly downloads order

Listing placement

AI SDKs270 listings · 14 pages · page 10, #20 (overall #200)
AI SDKs: every listing in weekly downloads order

Filled points are observed values; hollow points come from Internet Archive snapshots of the listing page. Dashed segments span periods with no observations — values between points are not interpolated.

Each band is one page of 20. Weekly downloads on a linear scale. Placement is projected from weekly downloads, so it can differ from the marketplace's own sort order. Hover a dot to see the listing.

Recent observations

Full history (1)
Recent weekly downloads observations for @gnldev/chat-adapter
Date (UTC)Weekly downloadsChange vs. previous
384—

Data source

Weekly downloads (trailing 7 complete days) come from the official npm downloads API. Downloads include CI and mirrors and are not equivalent to installs or users. npm publishes no ratings.

Listing: https://www.npmjs.com/package/@gnldev/chat-adapter. First seen Oct 22, 2026; last successful collection Oct 22, 2026. See the methodology.