npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@firtoz/chat-agent

v2.1.1

Published

Wire protocol, tools, and ChatAgentBase for Cloudflare Durable Objects with OpenRouter (use @firtoz/chat-agent-drizzle or @firtoz/chat-agent-sql for persistence)

Readme

@firtoz/chat-agent

npm version npm downloads license

TypeScript Cloudflare OpenRouter

Wire protocol (Zod 4), defineTool, and ChatAgentBase for Durable Objects + OpenRouter — streaming chat, tools, and multi-tab sync; plug in Drizzle or raw SQL via sibling packages.

Persistence is separate: install one of:

  • @firtoz/chat-agent-drizzle — Drizzle ORM (recommended)
  • @firtoz/chat-agent-sql — raw this.sql

Upgrading from v1

v1 shipped DrizzleChatAgent, SqlChatAgent, and ChatAgent (alias) from this package. v2 keeps only protocol + base here.

| v1 | v2 | |----|-----| | import { DrizzleChatAgent, defineTool } from "@firtoz/chat-agent" | import { defineTool } from "@firtoz/chat-agent" and import { DrizzleChatAgent } from "@firtoz/chat-agent-drizzle" | | import { SqlChatAgent } from "@firtoz/chat-agent" | import { SqlChatAgent } from "@firtoz/chat-agent-sql" | | import { … } from "@firtoz/chat-agent/db/schema" | import { … } from "@firtoz/chat-agent-drizzle/db/schema" | | import { ChatAgent } from "@firtoz/chat-agent" (Drizzle alias) | import { DrizzleChatAgent } from "@firtoz/chat-agent-drizzle" (or a local type ChatAgent = DrizzleChatAgent) |

Add bun add @firtoz/chat-agent-drizzle (and drizzle-orm) or bun add @firtoz/chat-agent-sql as needed.

Overview

  • ChatAgentBase — Abstract class with chat + streaming + tools + multi-tab broadcast logic
  • Concrete agentsDrizzleChatAgent (@firtoz/chat-agent-drizzle), SqlChatAgent (@firtoz/chat-agent-sql)

Shared behavior (all implementations):

  • OpenRouter API integration (simpler than AI SDK)
  • Resumable streaming with chunk buffering
  • Multi-tab sync: messageStart, chunks, messageEnd, messageUpdated, streamResume, and post-mutation history are broadcast to every WebSocket on the same Durable Object (merge by message id / streamId on the client)
  • Serialized chat turns with batched toolResult + autoContinue
  • Server-side and client-side tools
  • Cloudflare AI Gateway support
  • Optional maxPersistedMessages and sanitizeMessageForPersistence()
  • waitUntilStable(), resetTurnState(), hasPendingInteraction() (subclasses)
  • Tool approval (needsApprovaltoolApprovalRequest / toolApprovalResponse)
  • Regenerate / client sync (sendMessage with trigger, optional messages)
  • Provider metadata on tool calls for upstream round-trips
  • Wire schemas use Zod 4 (zod/v4)

Long-running streams (Durable Object keep-alive)

Streaming uses Partyserver’s experimental_waitUntil. Enable enable_ctx_exports in wrangler.jsonc (required for experimental_waitUntil on Server / Agent).

Installation

bun add @firtoz/chat-agent @openrouter/sdk agents

For a runnable Durable Object agent, add a persistence package:

# Drizzle (recommended)
bun add @firtoz/chat-agent-drizzle drizzle-orm
bun add -d drizzle-kit

# Or raw SQL only
bun add @firtoz/chat-agent-sql

Quick start (Drizzle)

import { defineTool, type ToolDefinition } from "@firtoz/chat-agent";
import { DrizzleChatAgent } from "@firtoz/chat-agent-drizzle";

interface Env {
  OPENROUTER_API_KEY: string;
}

class MyAgent extends DrizzleChatAgent<Env> {
  protected override getSystemPrompt(): string {
    return "You are a helpful assistant.";
  }

  protected override getModel(): string {
    return "anthropic/claude-sonnet-4.5";
  }

  protected override getTools(): ToolDefinition[] {
    return [
      defineTool({
        name: "get_time",
        description: "Get current time",
        parameters: { type: "object", properties: {} },
        execute: async () => ({ time: new Date().toISOString() }),
      }),
    ];
  }
}

export { MyAgent };

See @firtoz/chat-agent-drizzle for Wrangler rules, migrations, and drizzle.config.ts.

Quick start (SQL)

import { defineTool } from "@firtoz/chat-agent";
import { SqlChatAgent } from "@firtoz/chat-agent-sql";

class MyAgent extends SqlChatAgent<Env> {
  // Same overrides as Drizzle; tables are created in dbInitialize()
}

ChatAgentBase (abstract)

Subclasses must implement the database interface (see Drizzle/SQL packages for examples).

Abstract methods

protected abstract dbInitialize(): void;
protected abstract dbLoadMessages(): ChatMessage[];
protected abstract dbSaveMessage(msg: ChatMessage): void;
protected abstract dbClearAll(): void;
protected abstract dbFindActiveStream(): {
  id: string;
  messageId: string;
  createdAt: Date;
} | null;
protected abstract dbDeleteStreamWithChunks(streamId: string): void;
protected abstract dbInsertStreamMetadata(streamId: string, messageId: string): void;
protected abstract dbUpdateStreamStatus(
  streamId: string,
  status: "completed" | "error",
): void;
protected abstract dbDeleteOldCompletedStreams(cutoffMs: number): void;
protected abstract dbFindMaxChunkIndex(streamId: string): number | null;
protected abstract dbInsertChunks(
  chunks: Array<{
    id: string;
    streamId: string;
    content: string;
    chunkIndex: number;
  }>,
): void;
protected abstract dbGetChunks(streamId: string): string[];
protected abstract dbDeleteChunks(streamId: string): void;
protected abstract dbIsStreamKnown(streamId: string): boolean;
protected abstract dbReplaceAllMessages(messages: ChatMessage[]): void;
protected abstract dbTrimMessagesToMax(maxMessages: number): void;

Common overrides

protected getSystemPrompt(): string { /* … */ }
protected getModel(): string { /* … */ }
protected getTools(): ToolDefinition[] { /* … */ }

Tools

Server-side

defineTool with execute runs on the server; results are merged into the conversation.

Client-side

Omit execute; tool calls are sent to the client over the WebSocket.

Environment variables

OPENROUTER_API_KEY=sk-or-...
# Optional: Cloudflare AI Gateway
CLOUDFLARE_ACCOUNT_ID=…
AI_GATEWAY_NAME=…
AI_GATEWAY_TOKEN=…

Types (excerpt)

UserMessage, AssistantMessage, ToolMessage, ToolCall, ClientMessage, ServerMessage, etc. are exported from this package—import types from @firtoz/chat-agent only.

Drizzle vs SQL

| | Drizzle | SQL | |---|--------|-----| | Package | @firtoz/chat-agent-drizzle | @firtoz/chat-agent-sql | | Type safety | Full schema + query builder | Template SQL | | Setup | Migrations + Wrangler .sql rules | Auto-creates tables | | Best for | New projects | Prototypes / raw SQL |

License

MIT