@firtoz/chat-agent 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -349
- package/package.json +13 -19
- package/src/chat-agent-base.ts +731 -303
- package/src/chat-messages.ts +81 -5
- package/src/index.ts +11 -22
- package/src/chat-agent-drizzle.ts +0 -227
- package/src/chat-agent-sql.ts +0 -199
- package/src/db/index.ts +0 -21
- package/src/db/schema.ts +0 -47
package/README.md
CHANGED
|
@@ -1,39 +1,71 @@
|
|
|
1
1
|
# @firtoz/chat-agent
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Wire protocol (Zod 4), `defineTool`, and abstract **`ChatAgentBase`** for Cloudflare Durable Objects with OpenRouter—a simplified alternative to `@cloudflare/ai-chat`.
|
|
4
|
+
|
|
5
|
+
**Persistence is separate:** install one of:
|
|
6
|
+
|
|
7
|
+
- **`@firtoz/chat-agent-drizzle`** — Drizzle ORM (recommended)
|
|
8
|
+
- **`@firtoz/chat-agent-sql`** — raw `this.sql`
|
|
9
|
+
|
|
10
|
+
## Upgrading from v1
|
|
11
|
+
|
|
12
|
+
v1 shipped `DrizzleChatAgent`, `SqlChatAgent`, and `ChatAgent` (alias) from this package. v2 keeps only protocol + base here.
|
|
13
|
+
|
|
14
|
+
| v1 | v2 |
|
|
15
|
+
|----|-----|
|
|
16
|
+
| `import { DrizzleChatAgent, defineTool } from "@firtoz/chat-agent"` | `import { defineTool } from "@firtoz/chat-agent"` and `import { DrizzleChatAgent } from "@firtoz/chat-agent-drizzle"` |
|
|
17
|
+
| `import { SqlChatAgent } from "@firtoz/chat-agent"` | `import { SqlChatAgent } from "@firtoz/chat-agent-sql"` |
|
|
18
|
+
| `import { … } from "@firtoz/chat-agent/db/schema"` | `import { … } from "@firtoz/chat-agent-drizzle/db/schema"` |
|
|
19
|
+
| `import { ChatAgent } from "@firtoz/chat-agent"` (Drizzle alias) | `import { DrizzleChatAgent } from "@firtoz/chat-agent-drizzle"` (or a local `type ChatAgent = DrizzleChatAgent`) |
|
|
20
|
+
|
|
21
|
+
Add `bun add @firtoz/chat-agent-drizzle` (and `drizzle-orm`) or `bun add @firtoz/chat-agent-sql` as needed.
|
|
4
22
|
|
|
5
23
|
## Overview
|
|
6
24
|
|
|
7
|
-
|
|
25
|
+
- **`ChatAgentBase`** — Abstract class with chat + streaming + tools + multi-tab broadcast logic
|
|
26
|
+
- **Concrete agents** — `DrizzleChatAgent` (`@firtoz/chat-agent-drizzle`), `SqlChatAgent` (`@firtoz/chat-agent-sql`)
|
|
8
27
|
|
|
9
|
-
|
|
10
|
-
- **`DrizzleChatAgent`** - Type-safe implementation using Drizzle ORM (recommended)
|
|
11
|
-
- **`SqlChatAgent`** - Raw SQL implementation using `this.sql` template tags
|
|
28
|
+
Shared behavior (all implementations):
|
|
12
29
|
|
|
13
|
-
All implementations share the same API and features:
|
|
14
30
|
- OpenRouter API integration (simpler than AI SDK)
|
|
15
31
|
- Resumable streaming with chunk buffering
|
|
16
|
-
-
|
|
32
|
+
- **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)
|
|
33
|
+
- Serialized chat turns with batched `toolResult` + `autoContinue`
|
|
34
|
+
- Server-side and client-side tools
|
|
17
35
|
- Cloudflare AI Gateway support
|
|
18
|
-
-
|
|
36
|
+
- Optional `maxPersistedMessages` and `sanitizeMessageForPersistence()`
|
|
37
|
+
- `waitUntilStable()`, `resetTurnState()`, `hasPendingInteraction()` (subclasses)
|
|
38
|
+
- Tool approval (`needsApproval` → `toolApprovalRequest` / `toolApprovalResponse`)
|
|
39
|
+
- Regenerate / client sync (`sendMessage` with `trigger`, optional `messages`)
|
|
40
|
+
- **Provider metadata** on tool calls for upstream round-trips
|
|
41
|
+
- Wire schemas use **Zod 4** (`zod/v4`)
|
|
42
|
+
|
|
43
|
+
### Long-running streams (Durable Object keep-alive)
|
|
44
|
+
|
|
45
|
+
Streaming uses Partyserver’s `experimental_waitUntil`. Enable **`enable_ctx_exports`** in `wrangler.jsonc` (required for `experimental_waitUntil` on `Server` / `Agent`).
|
|
19
46
|
|
|
20
47
|
## Installation
|
|
21
48
|
|
|
22
49
|
```bash
|
|
23
50
|
bun add @firtoz/chat-agent @openrouter/sdk agents
|
|
51
|
+
```
|
|
24
52
|
|
|
25
|
-
|
|
26
|
-
|
|
53
|
+
For a runnable Durable Object agent, add a persistence package:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Drizzle (recommended)
|
|
57
|
+
bun add @firtoz/chat-agent-drizzle drizzle-orm
|
|
27
58
|
bun add -d drizzle-kit
|
|
28
|
-
```
|
|
29
59
|
|
|
30
|
-
|
|
60
|
+
# Or raw SQL only
|
|
61
|
+
bun add @firtoz/chat-agent-sql
|
|
62
|
+
```
|
|
31
63
|
|
|
32
|
-
|
|
64
|
+
## Quick start (Drizzle)
|
|
33
65
|
|
|
34
66
|
```typescript
|
|
35
|
-
import {
|
|
36
|
-
import
|
|
67
|
+
import { defineTool, type ToolDefinition } from "@firtoz/chat-agent";
|
|
68
|
+
import { DrizzleChatAgent } from "@firtoz/chat-agent-drizzle";
|
|
37
69
|
|
|
38
70
|
interface Env {
|
|
39
71
|
OPENROUTER_API_KEY: string;
|
|
@@ -48,14 +80,14 @@ class MyAgent extends DrizzleChatAgent<Env> {
|
|
|
48
80
|
return "anthropic/claude-sonnet-4.5";
|
|
49
81
|
}
|
|
50
82
|
|
|
51
|
-
protected override getTools() {
|
|
83
|
+
protected override getTools(): ToolDefinition[] {
|
|
52
84
|
return [
|
|
53
85
|
defineTool({
|
|
54
86
|
name: "get_time",
|
|
55
87
|
description: "Get current time",
|
|
56
88
|
parameters: { type: "object", properties: {} },
|
|
57
|
-
execute: async () => ({ time: new Date().toISOString() })
|
|
58
|
-
})
|
|
89
|
+
execute: async () => ({ time: new Date().toISOString() }),
|
|
90
|
+
}),
|
|
59
91
|
];
|
|
60
92
|
}
|
|
61
93
|
}
|
|
@@ -63,381 +95,99 @@ class MyAgent extends DrizzleChatAgent<Env> {
|
|
|
63
95
|
export { MyAgent };
|
|
64
96
|
```
|
|
65
97
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
import { SqlChatAgent } from "@firtoz/chat-agent";
|
|
70
|
-
|
|
71
|
-
class MyAgent extends SqlChatAgent<Env> {
|
|
72
|
-
// Same API as DrizzleChatAgent
|
|
73
|
-
protected override getSystemPrompt(): string {
|
|
74
|
-
return "You are a helpful assistant.";
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
## Setup Instructions
|
|
80
|
-
|
|
81
|
-
### DrizzleChatAgent Setup
|
|
82
|
-
|
|
83
|
-
#### 1. Add Wrangler Rules
|
|
98
|
+
See **`@firtoz/chat-agent-drizzle`** for Wrangler rules, migrations, and `drizzle.config.ts`.
|
|
84
99
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
```jsonc
|
|
88
|
-
{
|
|
89
|
-
/**
|
|
90
|
-
* Rules to import SQL migration files as text
|
|
91
|
-
* Required for Drizzle ORM migrations in Durable Objects
|
|
92
|
-
* @see https://orm.drizzle.team/docs/connect-cloudflare-do
|
|
93
|
-
*/
|
|
94
|
-
"rules": [
|
|
95
|
-
{
|
|
96
|
-
"type": "Text",
|
|
97
|
-
"globs": ["**/*.sql"],
|
|
98
|
-
"fallthrough": true
|
|
99
|
-
}
|
|
100
|
-
]
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
#### 2. Create Drizzle Config
|
|
105
|
-
|
|
106
|
-
Create `drizzle.config.ts` in your package:
|
|
100
|
+
## Quick start (SQL)
|
|
107
101
|
|
|
108
102
|
```typescript
|
|
109
|
-
import {
|
|
110
|
-
|
|
111
|
-
export default defineConfig({
|
|
112
|
-
schema: "./node_modules/@firtoz/chat-agent/src/db/schema.ts",
|
|
113
|
-
out: "./drizzle",
|
|
114
|
-
dialect: "sqlite",
|
|
115
|
-
driver: "durable-sqlite",
|
|
116
|
-
});
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Or if you're in the chat-agent package itself:
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
import { defineConfig } from "drizzle-kit";
|
|
123
|
-
|
|
124
|
-
export default defineConfig({
|
|
125
|
-
schema: "./src/db/schema.ts",
|
|
126
|
-
out: "./drizzle",
|
|
127
|
-
dialect: "sqlite",
|
|
128
|
-
driver: "durable-sqlite",
|
|
129
|
-
});
|
|
130
|
-
```
|
|
103
|
+
import { defineTool } from "@firtoz/chat-agent";
|
|
104
|
+
import { SqlChatAgent } from "@firtoz/chat-agent-sql";
|
|
131
105
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
```json
|
|
135
|
-
{
|
|
136
|
-
"scripts": {
|
|
137
|
-
"db:generate": "bunx drizzle-kit generate"
|
|
138
|
-
}
|
|
106
|
+
class MyAgent extends SqlChatAgent<Env> {
|
|
107
|
+
// Same overrides as Drizzle; tables are created in dbInitialize()
|
|
139
108
|
}
|
|
140
109
|
```
|
|
141
110
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
bun run db:generate
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
This creates:
|
|
149
|
-
- `drizzle/migrations.js` - Runtime migration imports
|
|
150
|
-
- `drizzle/0000_*.sql` - Migration SQL files
|
|
151
|
-
- `drizzle/meta/` - Journal and snapshots
|
|
111
|
+
## ChatAgentBase (abstract)
|
|
152
112
|
|
|
153
|
-
|
|
113
|
+
Subclasses must implement the database interface (see Drizzle/SQL packages for examples).
|
|
154
114
|
|
|
155
|
-
###
|
|
156
|
-
|
|
157
|
-
No additional setup needed! The SQL version creates tables automatically using `this.sql` template tags in `dbInitialize()`.
|
|
158
|
-
|
|
159
|
-
## ChatAgentBase (Abstract Class)
|
|
160
|
-
|
|
161
|
-
The base class handles all chat logic and defines the abstract database interface that implementations must provide.
|
|
162
|
-
|
|
163
|
-
### Abstract Methods
|
|
164
|
-
|
|
165
|
-
Subclasses must implement these database operations:
|
|
115
|
+
### Abstract methods
|
|
166
116
|
|
|
167
117
|
```typescript
|
|
168
|
-
// Initialization
|
|
169
118
|
protected abstract dbInitialize(): void;
|
|
170
|
-
|
|
171
|
-
// Messages
|
|
172
119
|
protected abstract dbLoadMessages(): ChatMessage[];
|
|
173
120
|
protected abstract dbSaveMessage(msg: ChatMessage): void;
|
|
174
121
|
protected abstract dbClearAll(): void;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
messageId: string;
|
|
180
|
-
createdAt: Date
|
|
122
|
+
protected abstract dbFindActiveStream(): {
|
|
123
|
+
id: string;
|
|
124
|
+
messageId: string;
|
|
125
|
+
createdAt: Date;
|
|
181
126
|
} | null;
|
|
182
127
|
protected abstract dbDeleteStreamWithChunks(streamId: string): void;
|
|
183
128
|
protected abstract dbInsertStreamMetadata(streamId: string, messageId: string): void;
|
|
184
|
-
protected abstract dbUpdateStreamStatus(
|
|
129
|
+
protected abstract dbUpdateStreamStatus(
|
|
130
|
+
streamId: string,
|
|
131
|
+
status: "completed" | "error",
|
|
132
|
+
): void;
|
|
185
133
|
protected abstract dbDeleteOldCompletedStreams(cutoffMs: number): void;
|
|
186
|
-
|
|
187
|
-
// Stream chunks
|
|
188
134
|
protected abstract dbFindMaxChunkIndex(streamId: string): number | null;
|
|
189
|
-
protected abstract dbInsertChunks(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
135
|
+
protected abstract dbInsertChunks(
|
|
136
|
+
chunks: Array<{
|
|
137
|
+
id: string;
|
|
138
|
+
streamId: string;
|
|
139
|
+
content: string;
|
|
140
|
+
chunkIndex: number;
|
|
141
|
+
}>,
|
|
142
|
+
): void;
|
|
195
143
|
protected abstract dbGetChunks(streamId: string): string[];
|
|
196
144
|
protected abstract dbDeleteChunks(streamId: string): void;
|
|
145
|
+
protected abstract dbIsStreamKnown(streamId: string): boolean;
|
|
146
|
+
protected abstract dbReplaceAllMessages(messages: ChatMessage[]): void;
|
|
147
|
+
protected abstract dbTrimMessagesToMax(maxMessages: number): void;
|
|
197
148
|
```
|
|
198
149
|
|
|
199
|
-
###
|
|
200
|
-
|
|
201
|
-
Customize your agent's behavior:
|
|
150
|
+
### Common overrides
|
|
202
151
|
|
|
203
152
|
```typescript
|
|
204
|
-
protected getSystemPrompt(): string {
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
protected getModel(): string {
|
|
209
|
-
// Popular OpenRouter models:
|
|
210
|
-
// - anthropic/claude-opus-4.5 (most capable)
|
|
211
|
-
// - anthropic/claude-sonnet-4.5 (balanced, default)
|
|
212
|
-
// - anthropic/claude-haiku-3.5 (fastest, cheapest)
|
|
213
|
-
return "anthropic/claude-sonnet-4.5";
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
protected getTools(): ToolDefinition[] {
|
|
217
|
-
return [
|
|
218
|
-
// Your tools here
|
|
219
|
-
];
|
|
220
|
-
}
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
## DrizzleChatAgent
|
|
224
|
-
|
|
225
|
-
Type-safe implementation using Drizzle ORM.
|
|
226
|
-
|
|
227
|
-
### Database Schema
|
|
228
|
-
|
|
229
|
-
The package provides three tables:
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
// From @firtoz/chat-agent/db/schema
|
|
233
|
-
|
|
234
|
-
export const messagesTable = sqliteTable("messages", {
|
|
235
|
-
id: text("id").primaryKey(),
|
|
236
|
-
role: text("role", { enum: ["user", "assistant", "tool"] }).notNull(),
|
|
237
|
-
messageJson: text("message_json").notNull(),
|
|
238
|
-
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
export const streamChunksTable = sqliteTable("stream_chunks", {
|
|
242
|
-
id: text("id").primaryKey(),
|
|
243
|
-
streamId: text("stream_id").notNull(),
|
|
244
|
-
content: text("content").notNull(),
|
|
245
|
-
chunkIndex: integer("chunk_index").notNull(),
|
|
246
|
-
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
export const streamMetadataTable = sqliteTable("stream_metadata", {
|
|
250
|
-
id: text("id").primaryKey(),
|
|
251
|
-
messageId: text("message_id").notNull(),
|
|
252
|
-
status: text("status", { enum: ["streaming", "completed", "error"] }).notNull(),
|
|
253
|
-
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
|
|
254
|
-
completedAt: integer("completed_at", { mode: "timestamp_ms" }),
|
|
255
|
-
});
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
### Implementation Details
|
|
259
|
-
|
|
260
|
-
```typescript
|
|
261
|
-
import { DrizzleChatAgent } from "@firtoz/chat-agent";
|
|
262
|
-
|
|
263
|
-
// Automatically:
|
|
264
|
-
// - Creates Drizzle DB instance in dbInitialize()
|
|
265
|
-
// - Runs migrations from drizzle/migrations.js
|
|
266
|
-
// - Uses type-safe query builder for all operations
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
## SqlChatAgent
|
|
270
|
-
|
|
271
|
-
Raw SQL implementation following `@cloudflare/ai-chat` pattern.
|
|
272
|
-
|
|
273
|
-
### Table Structure
|
|
274
|
-
|
|
275
|
-
Creates these tables automatically:
|
|
276
|
-
|
|
277
|
-
```sql
|
|
278
|
-
CREATE TABLE IF NOT EXISTS cf_ai_chat_agent_messages (
|
|
279
|
-
id TEXT PRIMARY KEY,
|
|
280
|
-
message TEXT NOT NULL,
|
|
281
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
282
|
-
);
|
|
283
|
-
|
|
284
|
-
CREATE TABLE IF NOT EXISTS cf_ai_chat_stream_chunks (
|
|
285
|
-
id TEXT PRIMARY KEY,
|
|
286
|
-
stream_id TEXT NOT NULL,
|
|
287
|
-
body TEXT NOT NULL,
|
|
288
|
-
chunk_index INTEGER NOT NULL,
|
|
289
|
-
created_at INTEGER NOT NULL
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
CREATE TABLE IF NOT EXISTS cf_ai_chat_stream_metadata (
|
|
293
|
-
id TEXT PRIMARY KEY,
|
|
294
|
-
request_id TEXT NOT NULL,
|
|
295
|
-
status TEXT NOT NULL,
|
|
296
|
-
created_at INTEGER NOT NULL,
|
|
297
|
-
completed_at INTEGER
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
CREATE INDEX IF NOT EXISTS idx_stream_chunks_stream_id
|
|
301
|
-
ON cf_ai_chat_stream_chunks(stream_id, chunk_index);
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
### Implementation Details
|
|
305
|
-
|
|
306
|
-
```typescript
|
|
307
|
-
import { SqlChatAgent } from "@firtoz/chat-agent";
|
|
308
|
-
|
|
309
|
-
// Uses Agent's built-in this.sql template tag:
|
|
310
|
-
// - this.sql`SELECT * FROM table WHERE id = ${id}`
|
|
311
|
-
// - No additional dependencies
|
|
312
|
-
// - Tables created automatically in dbInitialize()
|
|
153
|
+
protected getSystemPrompt(): string { /* … */ }
|
|
154
|
+
protected getModel(): string { /* … */ }
|
|
155
|
+
protected getTools(): ToolDefinition[] { /* … */ }
|
|
313
156
|
```
|
|
314
157
|
|
|
315
158
|
## Tools
|
|
316
159
|
|
|
317
|
-
### Server-
|
|
160
|
+
### Server-side
|
|
318
161
|
|
|
319
|
-
|
|
162
|
+
`defineTool` with `execute` runs on the server; results are merged into the conversation.
|
|
320
163
|
|
|
321
|
-
|
|
322
|
-
defineTool({
|
|
323
|
-
name: "get_weather",
|
|
324
|
-
description: "Get weather for a location",
|
|
325
|
-
parameters: {
|
|
326
|
-
type: "object",
|
|
327
|
-
properties: {
|
|
328
|
-
location: { type: "string" }
|
|
329
|
-
},
|
|
330
|
-
required: ["location"]
|
|
331
|
-
},
|
|
332
|
-
execute: async (args: { location: string }) => {
|
|
333
|
-
// Runs on server, result automatically added to conversation
|
|
334
|
-
const weather = await fetchWeather(args.location);
|
|
335
|
-
return { temperature: weather.temp, condition: weather.condition };
|
|
336
|
-
}
|
|
337
|
-
})
|
|
338
|
-
```
|
|
164
|
+
### Client-side
|
|
339
165
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
Tools without `execute` are sent to client for execution:
|
|
343
|
-
|
|
344
|
-
```typescript
|
|
345
|
-
defineTool({
|
|
346
|
-
name: "get_user_location",
|
|
347
|
-
description: "Get user's browser location",
|
|
348
|
-
parameters: {
|
|
349
|
-
type: "object",
|
|
350
|
-
properties: {}
|
|
351
|
-
}
|
|
352
|
-
// No execute - client handles this via WebSocket
|
|
353
|
-
})
|
|
354
|
-
```
|
|
166
|
+
Omit `execute`; tool calls are sent to the client over the WebSocket.
|
|
355
167
|
|
|
356
|
-
## Environment
|
|
168
|
+
## Environment variables
|
|
357
169
|
|
|
358
170
|
```env
|
|
359
|
-
# Required
|
|
360
171
|
OPENROUTER_API_KEY=sk-or-...
|
|
361
|
-
|
|
362
172
|
# Optional: Cloudflare AI Gateway
|
|
363
|
-
CLOUDFLARE_ACCOUNT_ID
|
|
364
|
-
AI_GATEWAY_NAME
|
|
365
|
-
AI_GATEWAY_TOKEN
|
|
173
|
+
CLOUDFLARE_ACCOUNT_ID=…
|
|
174
|
+
AI_GATEWAY_NAME=…
|
|
175
|
+
AI_GATEWAY_TOKEN=…
|
|
366
176
|
```
|
|
367
177
|
|
|
368
|
-
##
|
|
178
|
+
## Types (excerpt)
|
|
369
179
|
|
|
370
|
-
|
|
180
|
+
`UserMessage`, `AssistantMessage`, `ToolMessage`, `ToolCall`, `ClientMessage`, `ServerMessage`, etc. are exported from this package—import types from `@firtoz/chat-agent` only.
|
|
371
181
|
|
|
372
|
-
|
|
373
|
-
type UserMessage = {
|
|
374
|
-
id: string;
|
|
375
|
-
role: "user";
|
|
376
|
-
content: string;
|
|
377
|
-
createdAt: number;
|
|
378
|
-
};
|
|
182
|
+
## Drizzle vs SQL
|
|
379
183
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
type ToolMessage = {
|
|
389
|
-
id: string;
|
|
390
|
-
role: "tool";
|
|
391
|
-
toolCallId: string;
|
|
392
|
-
content: string;
|
|
393
|
-
createdAt: number;
|
|
394
|
-
};
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
### ToolDefinition
|
|
398
|
-
|
|
399
|
-
```typescript
|
|
400
|
-
type ToolDefinition = {
|
|
401
|
-
type: "function";
|
|
402
|
-
function: {
|
|
403
|
-
name: string;
|
|
404
|
-
description?: string;
|
|
405
|
-
parameters?: JSONSchema;
|
|
406
|
-
strict?: boolean;
|
|
407
|
-
};
|
|
408
|
-
execute?: (args: any) => unknown | Promise<unknown>;
|
|
409
|
-
};
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
## Features
|
|
413
|
-
|
|
414
|
-
### Implemented
|
|
415
|
-
|
|
416
|
-
- ✅ Message persistence (Drizzle or SQL)
|
|
417
|
-
- ✅ Resumable streaming with chunk buffering
|
|
418
|
-
- ✅ Stream restoration on reconnect
|
|
419
|
-
- ✅ Request cancellation via AbortController
|
|
420
|
-
- ✅ Server-side and client-side tools
|
|
421
|
-
- ✅ Tool result handling with auto-continue
|
|
422
|
-
- ✅ Cloudflare AI Gateway support
|
|
423
|
-
- ✅ DB-agnostic architecture
|
|
424
|
-
|
|
425
|
-
### Comparison: Drizzle vs SQL
|
|
426
|
-
|
|
427
|
-
| Feature | DrizzleChatAgent | SqlChatAgent |
|
|
428
|
-
|---------|-----------------|--------------|
|
|
429
|
-
| Type Safety | ✅ Full type inference | ❌ Template string types only |
|
|
430
|
-
| Setup Complexity | ⚠️ Requires migrations | ✅ Auto-creates tables |
|
|
431
|
-
| Dependencies | Drizzle ORM + drizzle-kit | None (uses Agent.sql) |
|
|
432
|
-
| Query Builder | ✅ Yes | ❌ Raw SQL only |
|
|
433
|
-
| Performance | Same | Same |
|
|
434
|
-
| Wrangler Config | ⚠️ Requires rules for .sql | ✅ No special config |
|
|
435
|
-
| Recommended For | New projects, teams | Quick prototypes, SQL experts |
|
|
184
|
+
| | Drizzle | SQL |
|
|
185
|
+
|---|--------|-----|
|
|
186
|
+
| Package | `@firtoz/chat-agent-drizzle` | `@firtoz/chat-agent-sql` |
|
|
187
|
+
| Type safety | Full schema + query builder | Template SQL |
|
|
188
|
+
| Setup | Migrations + Wrangler `.sql` rules | Auto-creates tables |
|
|
189
|
+
| Best for | New projects | Prototypes / raw SQL |
|
|
436
190
|
|
|
437
191
|
## License
|
|
438
192
|
|
|
439
193
|
MIT
|
|
440
|
-
|
|
441
|
-
## Contributing
|
|
442
|
-
|
|
443
|
-
PRs welcome!
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firtoz/chat-agent",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Wire protocol, tools, and ChatAgentBase for Cloudflare Durable Objects with OpenRouter (use @firtoz/chat-agent-drizzle or @firtoz/chat-agent-sql for persistence)",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"module": "./src/index.ts",
|
|
7
7
|
"types": "./src/index.ts",
|
|
@@ -10,11 +10,6 @@
|
|
|
10
10
|
"types": "./src/index.ts",
|
|
11
11
|
"import": "./src/index.ts",
|
|
12
12
|
"require": "./src/index.ts"
|
|
13
|
-
},
|
|
14
|
-
"./db/schema": {
|
|
15
|
-
"types": "./src/db/schema.ts",
|
|
16
|
-
"import": "./src/db/schema.ts",
|
|
17
|
-
"require": "./src/db/schema.ts"
|
|
18
13
|
}
|
|
19
14
|
},
|
|
20
15
|
"files": [
|
|
@@ -26,7 +21,8 @@
|
|
|
26
21
|
"typecheck": "tsc --noEmit -p ./tsconfig.json",
|
|
27
22
|
"lint": "biome check --write src",
|
|
28
23
|
"lint:ci": "biome ci src",
|
|
29
|
-
"
|
|
24
|
+
"test": "bun test",
|
|
25
|
+
"test:watch": "bun test --watch",
|
|
30
26
|
"format": "biome format src --write"
|
|
31
27
|
},
|
|
32
28
|
"keywords": [
|
|
@@ -37,7 +33,6 @@
|
|
|
37
33
|
"chat",
|
|
38
34
|
"ai",
|
|
39
35
|
"openrouter",
|
|
40
|
-
"drizzle-orm",
|
|
41
36
|
"agents"
|
|
42
37
|
],
|
|
43
38
|
"author": "Firtina Ozbalikchi <firtoz@github.com>",
|
|
@@ -52,13 +47,9 @@
|
|
|
52
47
|
"url": "https://github.com/firtoz/fullstack-toolkit/issues"
|
|
53
48
|
},
|
|
54
49
|
"peerDependencies": {
|
|
55
|
-
"@cloudflare/workers-types": "^4.
|
|
56
|
-
"@openrouter/sdk": "^0.
|
|
57
|
-
"agents": "^0.
|
|
58
|
-
"drizzle-orm": "^0.45.1"
|
|
59
|
-
},
|
|
60
|
-
"optionalDependencies": {
|
|
61
|
-
"zod": "^4.3.5"
|
|
50
|
+
"@cloudflare/workers-types": "^4.20260317.1",
|
|
51
|
+
"@openrouter/sdk": "^0.9.11",
|
|
52
|
+
"agents": "^0.8.1"
|
|
62
53
|
},
|
|
63
54
|
"engines": {
|
|
64
55
|
"node": ">=18.0.0"
|
|
@@ -67,8 +58,11 @@
|
|
|
67
58
|
"access": "public"
|
|
68
59
|
},
|
|
69
60
|
"devDependencies": {
|
|
70
|
-
"bun-types": "^1.3.
|
|
71
|
-
"
|
|
72
|
-
|
|
61
|
+
"bun-types": "^1.3.11",
|
|
62
|
+
"typescript": "^6.0.2"
|
|
63
|
+
},
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"@firtoz/maybe-error": "^1.5.2",
|
|
66
|
+
"zod": "^4.3.6"
|
|
73
67
|
}
|
|
74
68
|
}
|