@firtoz/chat-agent 1.0.1 → 2.1.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.
@@ -0,0 +1,253 @@
1
+ import { Agent, AgentContext, Connection, ConnectionContext } from 'agents';
2
+ import { ChatMessage, ToolDefinition } from './chat-messages.js';
3
+ export { AssistantMessage, ClientMessage, SendMessagePayload, SendMessageTrigger, ServerMessage, TokenUsage, ToolApprovalRequestMessage, ToolApprovalResponsePayload, ToolCall, ToolMessage, ToolNeedsApprovalFn, UserMessage, defineTool } from './chat-messages.js';
4
+ import 'zod/v4';
5
+
6
+ /**
7
+ * ChatAgentBase - Abstract base class for AI chat agents
8
+ *
9
+ * Features:
10
+ * - DB-agnostic SQLite persistence in Durable Objects
11
+ * - Resumable streaming with chunk buffering (like @cloudflare/ai-chat)
12
+ * - Broadcast of stream and history updates to all WebSocket connections (multi-tab)
13
+ * - Serialized chat turns + batched client tool auto-continue
14
+ * - OpenRouter via Cloudflare AI Gateway
15
+ *
16
+ * Subclasses must implement database operations via abstract methods.
17
+ *
18
+ * @template Env - Environment bindings type (must include OPENROUTER_API_KEY, optionally AI Gateway config)
19
+ */
20
+ declare abstract class ChatAgentBase<Env extends Cloudflare.Env & {
21
+ OPENROUTER_API_KEY: string;
22
+ } = Cloudflare.Env & {
23
+ OPENROUTER_API_KEY: string;
24
+ }> extends Agent<Env> {
25
+ /** In-memory cache of messages */
26
+ messages: ChatMessage[];
27
+ /**
28
+ * When set, oldest messages are deleted from storage after each save so only the last N remain.
29
+ * Does not change what is sent to the model unless you prune separately.
30
+ */
31
+ protected maxPersistedMessages?: number;
32
+ /** Map of message IDs to AbortControllers for request cancellation */
33
+ private _abortControllers;
34
+ /** Currently active stream ID */
35
+ private _activeStreamId;
36
+ /** Message ID being streamed */
37
+ private _activeMessageId;
38
+ /** True only while the OpenRouter async iterator for the active stream is running */
39
+ private _openRouterStreamLive;
40
+ /** Current chunk index for active stream */
41
+ private _streamChunkIndex;
42
+ /** Buffer for chunks pending write */
43
+ private _chunkBuffer;
44
+ /** Lock for flush operations */
45
+ private _isFlushingChunks;
46
+ /** Last cleanup timestamp */
47
+ private _lastCleanupTime;
48
+ /** Client-registered tools (tools defined at runtime from frontend) */
49
+ private _clientTools;
50
+ /** FIFO serialization of chat turns (user sends, tool continuations, etc.) */
51
+ private _turnTail;
52
+ /** Bumped by {@link resetTurnState} to ignore stale async work */
53
+ private _turnGeneration;
54
+ /** Connection id that last queued an auto-continue after client tools (for multi-tab hints) */
55
+ private _continuationOriginConnectionId;
56
+ private _pendingClientToolAutoContinue;
57
+ private _clientToolAutoContinueFlushScheduled;
58
+ /** Human-in-the-loop: server tools awaiting `toolApprovalResponse` (not queued — avoids deadlock). */
59
+ private _pendingToolApprovals;
60
+ constructor(ctx: AgentContext, env: Env);
61
+ /**
62
+ * Initialize database and run migrations
63
+ * Called once during constructor
64
+ */
65
+ protected abstract dbInitialize(): void;
66
+ /**
67
+ * Load all messages from database
68
+ * @returns Array of chat messages ordered by createdAt
69
+ */
70
+ protected abstract dbLoadMessages(): ChatMessage[];
71
+ /**
72
+ * Save or update a message in database
73
+ * @param msg - The message to save
74
+ */
75
+ protected abstract dbSaveMessage(msg: ChatMessage): void;
76
+ /**
77
+ * Clear all data (messages, streams, chunks)
78
+ */
79
+ protected abstract dbClearAll(): void;
80
+ /**
81
+ * Find an active streaming session
82
+ * @returns Stream info or null if none active
83
+ */
84
+ protected abstract dbFindActiveStream(): {
85
+ id: string;
86
+ messageId: string;
87
+ createdAt: Date;
88
+ } | null;
89
+ /**
90
+ * Delete a stream and all its chunks
91
+ * @param streamId - The stream to delete
92
+ */
93
+ protected abstract dbDeleteStreamWithChunks(streamId: string): void;
94
+ /**
95
+ * Create a new stream metadata entry
96
+ * @param streamId - Unique stream identifier
97
+ * @param messageId - Associated message ID
98
+ */
99
+ protected abstract dbInsertStreamMetadata(streamId: string, messageId: string): void;
100
+ /**
101
+ * Update stream status to completed or error
102
+ * @param streamId - The stream to update
103
+ * @param status - New status
104
+ */
105
+ protected abstract dbUpdateStreamStatus(streamId: string, status: "completed" | "error"): void;
106
+ /**
107
+ * Delete old completed streams older than cutoff
108
+ * @param cutoffMs - Timestamp in milliseconds
109
+ */
110
+ protected abstract dbDeleteOldCompletedStreams(cutoffMs: number): void;
111
+ /**
112
+ * Find the maximum chunk index for a stream
113
+ * @param streamId - The stream to query
114
+ * @returns Max chunk index or null if no chunks
115
+ */
116
+ protected abstract dbFindMaxChunkIndex(streamId: string): number | null;
117
+ /**
118
+ * Insert multiple stream chunks
119
+ * @param chunks - Array of chunks to insert
120
+ */
121
+ protected abstract dbInsertChunks(chunks: Array<{
122
+ id: string;
123
+ streamId: string;
124
+ content: string;
125
+ chunkIndex: number;
126
+ }>): void;
127
+ /**
128
+ * Get all chunks for a stream, ordered by index
129
+ * @param streamId - The stream to query
130
+ * @returns Array of chunk content strings
131
+ */
132
+ protected abstract dbGetChunks(streamId: string): string[];
133
+ /**
134
+ * Delete all chunks for a stream
135
+ * @param streamId - The stream to clean up
136
+ */
137
+ protected abstract dbDeleteChunks(streamId: string): void;
138
+ /**
139
+ * Whether stream metadata or chunks exist for this stream id
140
+ */
141
+ protected abstract dbIsStreamKnown(streamId: string): boolean;
142
+ /**
143
+ * Delete oldest messages until at most `maxMessages` rows remain
144
+ */
145
+ protected abstract dbTrimMessagesToMax(maxMessages: number): void;
146
+ /**
147
+ * Replace all chat messages (used for client sync / regenerate). Implementations should delete existing rows then insert.
148
+ */
149
+ protected abstract dbReplaceAllMessages(messages: ChatMessage[]): void;
150
+ /**
151
+ * Transform a message immediately before it is written to storage.
152
+ * Default: return the message unchanged.
153
+ */
154
+ protected sanitizeMessageForPersistence(msg: ChatMessage): ChatMessage;
155
+ /**
156
+ * Resolves after all queued turns have finished and no OpenRouter stream is active.
157
+ */
158
+ protected waitUntilStable(): Promise<void>;
159
+ /**
160
+ * Abort in-flight generation, clear pending client tool auto-continue batch, and invalidate queued async work.
161
+ * Call from custom clear handlers; {@link clearHistory} path also resets state.
162
+ */
163
+ protected resetTurnState(): void;
164
+ /**
165
+ * True when the last assistant message still has tool calls without matching tool role replies.
166
+ */
167
+ protected hasPendingInteraction(): boolean;
168
+ private _persistMessage;
169
+ private _maybeTruncateToolMessageContent;
170
+ private _clearMessages;
171
+ /**
172
+ * Restore active stream state if the agent was restarted during streaming.
173
+ * Called during construction to recover any interrupted streams.
174
+ */
175
+ private _restoreActiveStream;
176
+ /**
177
+ * Notify a connection about an active stream that can be resumed.
178
+ */
179
+ private _notifyStreamResuming;
180
+ private _storeChunk;
181
+ private _flushChunkBuffer;
182
+ private _startStream;
183
+ /**
184
+ * Complete stream with a full message (supports tool calls)
185
+ */
186
+ private _completeStreamWithMessage;
187
+ private _markStreamError;
188
+ /**
189
+ * Clean up old completed streams periodically.
190
+ */
191
+ private _maybeCleanupOldStreams;
192
+ private _getStreamChunks;
193
+ /**
194
+ * Finalize a stream that has buffered chunks but no live OpenRouter reader (e.g. after DO restart).
195
+ */
196
+ private _finalizeOrphanedStreamFromChunks;
197
+ private _getAbortSignal;
198
+ private _cancelRequest;
199
+ private _removeAbortController;
200
+ private _broadcast;
201
+ private _sendTo;
202
+ private _enqueueTurn;
203
+ private _resolveToolApproval;
204
+ private _mergeProviderMetadata;
205
+ private _replaceMessagesFromClient;
206
+ private _getOpenRouter;
207
+ onConnect(connection: Connection, _ctx: ConnectionContext): Promise<void>;
208
+ onClose(connection: Connection, _code: number, _reason: string, _wasClean: boolean): Promise<void>;
209
+ onMessage(connection: Connection, message: string): Promise<void>;
210
+ /**
211
+ * Get the system prompt for the AI
212
+ * Override this method to customize the AI's behavior
213
+ */
214
+ protected getSystemPrompt(): string;
215
+ /**
216
+ * Get the AI model to use
217
+ * Override this method to use a different model
218
+ */
219
+ protected getModel(): string;
220
+ /**
221
+ * Get available tools for the AI
222
+ * Override this method to provide custom tools
223
+ */
224
+ protected getTools(): ToolDefinition[];
225
+ private _handleSendMessagePayload;
226
+ private _handleToolResultMessage;
227
+ private _scheduleClientToolAutoContinueFlush;
228
+ private _applyClientToolResult;
229
+ /**
230
+ * Generate AI response (can be called for initial message or after tool results)
231
+ */
232
+ private _generateAIResponse;
233
+ /**
234
+ * Build API messages from our message history
235
+ */
236
+ private _buildApiMessages;
237
+ /**
238
+ * Build a map of tool definitions by name for quick lookup
239
+ */
240
+ private _getToolsMap;
241
+ /**
242
+ * Register tools from the client at runtime
243
+ */
244
+ private _registerClientTools;
245
+ /**
246
+ * Execute server-side tools and continue the conversation
247
+ */
248
+ private _executeServerSideTools;
249
+ private _handleResumeStream;
250
+ destroy(): Promise<void>;
251
+ }
252
+
253
+ export { ChatAgentBase, ChatMessage, ToolDefinition };
@@ -0,0 +1,4 @@
1
+ export { ChatAgentBase } from './chunk-G5P5JXRF.js';
2
+ export { defineTool } from './chunk-OEX3D4WL.js';
3
+ //# sourceMappingURL=chat-agent-base.js.map
4
+ //# sourceMappingURL=chat-agent-base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chat-agent-base.js"}