agents 0.0.2 → 0.0.37

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 CHANGED
@@ -1,32 +1,432 @@
1
- node-user-agents
2
- ================
1
+ ### 🧠 `agents` - A Framework for Digital Intelligence
3
2
 
4
- # agents
3
+ ![agents-header](https://github.com/user-attachments/assets/f6d99eeb-1803-4495-9c5e-3cf07a37b402)
5
4
 
6
- Quick HTTP user agent headers for slightly-immoral-and-very-improper web scraping. It's so stupidly simple that you could probably write this yourself.
5
+ Welcome to a new chapter in software development, where AI agents persist, think, and act with purpose. The `agents` framework creates an environment where artificial intelligence can flourish - maintaining state, engaging in meaningful interactions, and evolving over time.
7
6
 
8
- ##Usage:
7
+ _This project is in active development. Join us in shaping the future of intelligent agents._
9
8
 
10
- ```bash
11
- npm install node-user-agents
9
+ #### The Nature of Agents
10
+
11
+ An AI agent transcends traditional software boundaries. It's an entity that:
12
+
13
+ - **Persistence**: Maintains its state and knowledge across time
14
+ - **Agency**: Acts autonomously within its defined purpose
15
+ - **Connection**: Communicates through multiple channels with both humans and other agents
16
+ - **Growth**: Learns and adapts through its interactions
17
+
18
+ Built on Cloudflare's global network, this framework provides agents with a reliable, distributed foundation where they can operate continuously and effectively.
19
+
20
+ #### 💫 Core Principles
21
+
22
+ 1. **Stateful Existence**: Each agent maintains its own persistent reality
23
+ 2. **Long-lived Presence**: Agents can run for extended periods, resting when idle
24
+ 3. **Natural Communication**: Interact through HTTP, WebSockets, or direct calls
25
+ 4. **Global Distribution**: Leverage Cloudflare's network for worldwide presence
26
+ 5. **Resource Harmony**: Efficient hibernation and awakening as needed
27
+
28
+ ---
29
+
30
+ ### 🌱 Beginning the Journey
31
+
32
+ Start with a complete environment:
33
+
34
+ ```sh
35
+ # Create a new project
36
+ npm create cloudflare@latest -- --template cloudflare/agents-starter
37
+
38
+ # Or enhance an existing one
39
+ npm install agents
40
+ ```
41
+
42
+ ### 📝 Your First Agent
43
+
44
+ Create an agent that bridges thought and action:
45
+
46
+ ```ts
47
+ import { Agent } from "agents";
48
+
49
+ export class IntelligentAgent extends Agent {
50
+ async onRequest(request) {
51
+ // Transform intention into response
52
+ return new Response("Ready to assist.");
53
+ }
54
+ }
55
+ ```
56
+
57
+ ### 🎭 Patterns of Intelligence
58
+
59
+ Agents can manifest various forms of understanding:
60
+
61
+ ```ts
62
+ import { Agent } from "agents";
63
+ import { OpenAI } from "openai";
64
+
65
+ export class AIAgent extends Agent {
66
+ async onRequest(request) {
67
+ // Connect with AI capabilities
68
+ const ai = new OpenAI({
69
+ apiKey: this.env.OPENAI_API_KEY,
70
+ });
71
+
72
+ // Process and understand
73
+ const response = await ai.chat.completions.create({
74
+ model: "gpt-4",
75
+ messages: [{ role: "user", content: await request.text() }],
76
+ });
77
+
78
+ return new Response(response.choices[0].message.content);
79
+ }
80
+
81
+ async processTask(task) {
82
+ await this.understand(task);
83
+ await this.act();
84
+ await this.reflect();
85
+ }
86
+ }
87
+ ```
88
+
89
+ ### 🏰 Creating Space
90
+
91
+ Define your agent's domain:
92
+
93
+ ```jsonc
94
+ {
95
+ "durable_objects": {
96
+ "bindings": [
97
+ {
98
+ "name": "AIAgent",
99
+ "class_name": "AIAgent",
100
+ },
101
+ ],
102
+ },
103
+ "migrations": [
104
+ {
105
+ "tag": "v1",
106
+ // Mandatory for the Agent to store state
107
+ "new_sqlite_classes": ["AIAgent"],
108
+ },
109
+ ],
110
+ }
12
111
  ```
13
112
 
14
- ```JavaScript
15
- var request = require('request')
16
- , agents = require('agents');
113
+ ### 🌐 Lifecycle
17
114
 
18
- request({'uri':'wikipedia.org','headers':{'User-Agent':agents.randomAgentString()}},function(error,response,body){
19
- console.log(body);
115
+ Bring your agent into being:
116
+
117
+ ```ts
118
+ // Create a new instance
119
+ const id = env.AIAgent.newUniqueId();
120
+ const agent = env.AIAgent.get(id);
121
+
122
+ // Initialize with purpose
123
+ await agent.processTask({
124
+ type: "analysis",
125
+ context: "incoming_data",
126
+ parameters: initialConfig,
127
+ });
128
+
129
+ // Or reconnect with an existing one
130
+ const existingAgent = await getAgentByName(env.AIAgent, "data-analyzer");
131
+ ```
132
+
133
+ ### 🔄 Paths of Communication
134
+
135
+ #### HTTP Understanding
136
+
137
+ Process and respond to direct requests:
138
+
139
+ ```ts
140
+ export class APIAgent extends Agent {
141
+ async onRequest(request) {
142
+ const data = await request.json();
143
+
144
+ return Response.json({
145
+ insight: await this.process(data),
146
+ moment: Date.now(),
147
+ });
148
+ }
149
+ }
150
+ ```
151
+
152
+ #### Persistent Connections
153
+
154
+ Maintain ongoing dialogues through WebSocket:
155
+
156
+ ```ts
157
+ export class DialogueAgent extends Agent {
158
+ async onConnect(connection) {
159
+ await this.initiate(connection);
160
+ }
161
+
162
+ async onMessage(connection, message) {
163
+ const understanding = await this.comprehend(message);
164
+ await this.respond(connection, understanding);
165
+ }
166
+ }
167
+ ```
168
+
169
+ #### Client Communion
170
+
171
+ For direct connection to your agent:
172
+
173
+ ```ts
174
+ import { AgentClient } from "agents/client";
175
+
176
+ const connection = new AgentClient({
177
+ agent: "dialogue-agent",
178
+ name: "insight-seeker",
20
179
  });
21
- //See? It's like taking candy from a baby.
180
+
181
+ connection.addEventListener("message", (event) => {
182
+ console.log("Received:", event.data);
183
+ });
184
+
185
+ connection.send(
186
+ JSON.stringify({
187
+ type: "inquiry",
188
+ content: "What patterns do you see?",
189
+ })
190
+ );
191
+ ```
192
+
193
+ #### React Integration
194
+
195
+ For harmonious integration with React:
196
+
197
+ ```tsx
198
+ import { useAgent } from "agents/react";
199
+
200
+ function AgentInterface() {
201
+ const connection = useAgent({
202
+ agent: "dialogue-agent",
203
+ name: "insight-seeker",
204
+ onMessage: (message) => {
205
+ console.log("Understanding received:", message.data);
206
+ },
207
+ onOpen: () => console.log("Connection established"),
208
+ onClose: () => console.log("Connection closed"),
209
+ });
210
+
211
+ const inquire = () => {
212
+ connection.send(
213
+ JSON.stringify({
214
+ type: "inquiry",
215
+ content: "What insights have you gathered?",
216
+ })
217
+ );
218
+ };
219
+
220
+ return (
221
+ <div className="agent-interface">
222
+ <button onClick={inquire}>Seek Understanding</button>
223
+ </div>
224
+ );
225
+ }
226
+ ```
227
+
228
+ ### 🌊 Flow of State
229
+
230
+ Maintain and evolve your agent's understanding:
231
+
232
+ ```ts
233
+ export class ThinkingAgent extends Agent {
234
+ async evolve(newInsight) {
235
+ this.setState({
236
+ ...this.state,
237
+ insights: [...(this.state.insights || []), newInsight],
238
+ understanding: this.state.understanding + 1,
239
+ });
240
+ }
241
+
242
+ onStateUpdate(state, source) {
243
+ console.log("Understanding deepened:", {
244
+ newState: state,
245
+ origin: source,
246
+ });
247
+ }
248
+ }
22
249
  ```
23
- ## Working use cases:
24
250
 
25
- - Scripting HTTP requests to Wikipedia (has terrible bot detection)
26
- - Any other site that isn't as big as Google to be able to invest in super-smart bot-detection systems.
251
+ Connect to your agent's state from React:
252
+
253
+ ```tsx
254
+ import { useState } from "react";
255
+ import { useAgent } from "agents/react";
256
+
257
+ function StateInterface() {
258
+ const [state, setState] = useState({ counter: 0 });
259
+
260
+ const agent = useAgent({
261
+ agent: "thinking-agent",
262
+ onStateUpdate: (newState) => setState(newState),
263
+ });
264
+
265
+ const increment = () => {
266
+ agent.setState({ counter: state.counter + 1 });
267
+ };
268
+
269
+ return (
270
+ <div>
271
+ <div>Count: {state.counter}</div>
272
+ <button onClick={increment}>Increment</button>
273
+ </div>
274
+ );
275
+ }
276
+ ```
277
+
278
+ This creates a synchronized state that automatically updates across all connected clients.
279
+
280
+ ### ⏳ Temporal Patterns
281
+
282
+ Schedule moments of action and reflection:
283
+
284
+ ```ts
285
+ export class TimeAwareAgent extends Agent {
286
+ async initialize() {
287
+ // Quick reflection
288
+ this.schedule(10, "quickInsight", { focus: "patterns" });
289
+
290
+ // Daily synthesis
291
+ this.schedule("0 0 * * *", "dailySynthesis", {
292
+ depth: "comprehensive",
293
+ });
294
+
295
+ // Milestone review
296
+ this.schedule(new Date("2024-12-31"), "yearlyAnalysis");
297
+ }
298
+
299
+ async quickInsight(data) {
300
+ await this.analyze(data.focus);
301
+ }
302
+
303
+ async dailySynthesis(data) {
304
+ await this.synthesize(data.depth);
305
+ }
306
+
307
+ async yearlyAnalysis() {
308
+ await this.analyze();
309
+ }
310
+ }
311
+ ```
312
+
313
+ ### 💬 AI Dialogue
314
+
315
+ Create meaningful conversations with intelligence:
316
+
317
+ ```ts
318
+ import { AIChatAgent } from "agents/ai-chat-agent";
319
+ import { createOpenAI } from "@ai-sdk/openai";
320
+
321
+ export class DialogueAgent extends AIChatAgent {
322
+ async onChatMessage(onFinish) {
323
+ return createDataStreamResponse({
324
+ execute: async (dataStream) => {
325
+ const ai = createOpenAI({
326
+ apiKey: this.env.OPENAI_API_KEY,
327
+ });
328
+
329
+ const stream = streamText({
330
+ model: ai("gpt-4o"),
331
+ messages: this.messages,
332
+ onFinish, // call onFinish so that messages get saved
333
+ });
334
+
335
+ stream.mergeIntoDataStream(dataStream);
336
+ },
337
+ });
338
+ }
339
+ }
340
+ ```
341
+
342
+ #### Creating the Interface
343
+
344
+ Connect with your agent through a React interface:
345
+
346
+ ```tsx
347
+ import { useAgent } from "agents/react";
348
+ import { useAgentChat } from "agents/ai-react";
349
+
350
+ function ChatInterface() {
351
+ // Connect to the agent
352
+ const agent = useAgent({
353
+ agent: "dialogue-agent",
354
+ });
355
+
356
+ // Set up the chat interaction
357
+ const { messages, input, handleInputChange, handleSubmit, clearHistory } =
358
+ useAgentChat({
359
+ agent,
360
+ maxSteps: 5,
361
+ });
362
+
363
+ return (
364
+ <div className="chat-interface">
365
+ {/* Message History */}
366
+ <div className="message-flow">
367
+ {messages.map((message) => (
368
+ <div key={message.id} className="message">
369
+ <div className="role">{message.role}</div>
370
+ <div className="content">{message.content}</div>
371
+ </div>
372
+ ))}
373
+ </div>
374
+
375
+ {/* Input Area */}
376
+ <form onSubmit={handleSubmit} className="input-area">
377
+ <input
378
+ value={input}
379
+ onChange={handleInputChange}
380
+ placeholder="Type your message..."
381
+ className="message-input"
382
+ />
383
+ </form>
384
+
385
+ <button onClick={clearHistory} className="clear-button">
386
+ Clear Chat
387
+ </button>
388
+ </div>
389
+ );
390
+ }
391
+ ```
392
+
393
+ This creates:
394
+
395
+ - Real-time message streaming
396
+ - Simple message history
397
+ - Intuitive input handling
398
+ - Easy conversation reset
399
+
400
+ ### 💬 The Path Forward
401
+
402
+ We're developing new dimensions of agent capability:
403
+
404
+ #### Enhanced Understanding
405
+
406
+ - **WebRTC Perception**: Audio and video communication channels
407
+ - **Email Discourse**: Automated email interaction and response
408
+ - **Deep Memory**: Long-term context and relationship understanding
409
+
410
+ #### Development Insights
411
+
412
+ - **Evaluation Framework**: Understanding agent effectiveness
413
+ - **Clear Sight**: Deep visibility into agent processes
414
+ - **Private Realms**: Complete self-hosting guide
415
+
416
+ These capabilities will expand your agents' potential while maintaining their reliability and purpose.
417
+
418
+ Welcome to the future of intelligent agents. Create something meaningful. 🌟
419
+
420
+ ### Contributing
421
+
422
+ Contributions are welcome, but are especially welcome when:
27
423
 
424
+ - You have opened an issue as a Request for Comment (RFC) to discuss your proposal, show your thinking, and iterate together.
425
+ - Is not "AI slop": LLMs are powerful tools, but contributions entirely authored by vibe coding are unlikely to meet the quality bar, and will be rejected.
426
+ - You're willing to accept feedback and make sure the changes fit the goals of the `agents` sdk. Not everything will, and that's OK.
28
427
 
29
- ## Non-working use cases:
428
+ Small fixes, type bugs, and documentation improvements can be raised directly as PRs.
30
429
 
31
- - Google (based on IP address and no more than 20 requests in a minute)
430
+ ### License
32
431
 
432
+ MIT licensed. See the LICENSE file at the root of this repository for details.
@@ -0,0 +1,38 @@
1
+ import { Agent, AgentContext } from "./index.js";
2
+ import { Message, StreamTextOnFinishCallback, ToolSet } from "ai";
3
+ import { Connection, WSMessage } from "partyserver";
4
+ import "cloudflare:workers";
5
+
6
+ /**
7
+ * Extension of Agent with built-in chat capabilities
8
+ * @template Env Environment type containing bindings
9
+ */
10
+ declare class AIChatAgent<Env = unknown, State = unknown> extends Agent<
11
+ Env,
12
+ State
13
+ > {
14
+ /** Array of chat messages for the current conversation */
15
+ messages: Message[];
16
+ constructor(ctx: AgentContext, env: Env);
17
+ private sendChatMessage;
18
+ private broadcastChatMessage;
19
+ onMessage(connection: Connection, message: WSMessage): Promise<void>;
20
+ onRequest(request: Request): Promise<Response>;
21
+ /**
22
+ * Handle incoming chat messages and generate a response
23
+ * @param onFinish Callback to be called when the response is finished
24
+ * @returns Response to send to the client or undefined
25
+ */
26
+ onChatMessage(
27
+ onFinish: StreamTextOnFinishCallback<ToolSet>
28
+ ): Promise<Response | undefined>;
29
+ /**
30
+ * Save messages on the server side and trigger AI response
31
+ * @param messages Chat messages to save
32
+ */
33
+ saveMessages(messages: Message[]): Promise<void>;
34
+ private persistMessages;
35
+ private reply;
36
+ }
37
+
38
+ export { AIChatAgent };
@@ -0,0 +1,169 @@
1
+ import {
2
+ Agent
3
+ } from "./chunk-PDF5WEP4.js";
4
+ import "./chunk-HMLY7DHA.js";
5
+
6
+ // src/ai-chat-agent.ts
7
+ import { appendResponseMessages } from "ai";
8
+ var decoder = new TextDecoder();
9
+ var AIChatAgent = class extends Agent {
10
+ constructor(ctx, env) {
11
+ super(ctx, env);
12
+ this.sql`create table if not exists cf_ai_chat_agent_messages (
13
+ id text primary key,
14
+ message text not null,
15
+ created_at datetime default current_timestamp
16
+ )`;
17
+ this.messages = (this.sql`select * from cf_ai_chat_agent_messages` || []).map((row) => {
18
+ return JSON.parse(row.message);
19
+ });
20
+ }
21
+ sendChatMessage(connection, message) {
22
+ try {
23
+ connection.send(JSON.stringify(message));
24
+ } catch (e) {
25
+ }
26
+ }
27
+ broadcastChatMessage(message, exclude) {
28
+ try {
29
+ this.broadcast(JSON.stringify(message), exclude);
30
+ } catch (e) {
31
+ }
32
+ }
33
+ async onMessage(connection, message) {
34
+ if (typeof message === "string") {
35
+ let data;
36
+ try {
37
+ data = JSON.parse(message);
38
+ } catch (error) {
39
+ return;
40
+ }
41
+ if (data.type === "cf_agent_use_chat_request" && data.init.method === "POST") {
42
+ const {
43
+ method,
44
+ keepalive,
45
+ headers,
46
+ body,
47
+ // we're reading this
48
+ redirect,
49
+ integrity,
50
+ credentials,
51
+ mode,
52
+ referrer,
53
+ referrerPolicy,
54
+ window
55
+ // dispatcher,
56
+ // duplex
57
+ } = data.init;
58
+ const { messages } = JSON.parse(body);
59
+ this.broadcastChatMessage(
60
+ {
61
+ type: "cf_agent_chat_messages",
62
+ messages
63
+ },
64
+ [connection.id]
65
+ );
66
+ await this.persistMessages(messages, [connection.id]);
67
+ const response = await this.onChatMessage(async ({ response: response2 }) => {
68
+ const finalMessages = appendResponseMessages({
69
+ messages,
70
+ responseMessages: response2.messages
71
+ });
72
+ await this.persistMessages(finalMessages, [connection.id]);
73
+ });
74
+ if (response) {
75
+ await this.reply(data.id, response);
76
+ }
77
+ } else if (data.type === "cf_agent_chat_clear") {
78
+ this.sql`delete from cf_ai_chat_agent_messages`;
79
+ this.messages = [];
80
+ this.broadcastChatMessage(
81
+ {
82
+ type: "cf_agent_chat_clear"
83
+ },
84
+ [connection.id]
85
+ );
86
+ } else if (data.type === "cf_agent_chat_messages") {
87
+ await this.persistMessages(data.messages, [connection.id]);
88
+ }
89
+ }
90
+ }
91
+ async onRequest(request) {
92
+ if (request.url.endsWith("/get-messages")) {
93
+ const messages = (this.sql`select * from cf_ai_chat_agent_messages` || []).map((row) => {
94
+ return JSON.parse(row.message);
95
+ });
96
+ return new Response(JSON.stringify(messages));
97
+ }
98
+ return super.onRequest(request);
99
+ }
100
+ /**
101
+ * Handle incoming chat messages and generate a response
102
+ * @param onFinish Callback to be called when the response is finished
103
+ * @returns Response to send to the client or undefined
104
+ */
105
+ async onChatMessage(onFinish) {
106
+ throw new Error(
107
+ "recieved a chat message, override onChatMessage and return a Response to send to the client"
108
+ );
109
+ }
110
+ /**
111
+ * Save messages on the server side and trigger AI response
112
+ * @param messages Chat messages to save
113
+ */
114
+ async saveMessages(messages) {
115
+ await this.persistMessages(messages);
116
+ const response = await this.onChatMessage(async ({ response: response2 }) => {
117
+ const finalMessages = appendResponseMessages({
118
+ messages,
119
+ responseMessages: response2.messages
120
+ });
121
+ await this.persistMessages(finalMessages, []);
122
+ });
123
+ if (response) {
124
+ for await (const chunk of response.body) {
125
+ decoder.decode(chunk);
126
+ }
127
+ response.body?.cancel();
128
+ }
129
+ }
130
+ async persistMessages(messages, excludeBroadcastIds = []) {
131
+ this.sql`delete from cf_ai_chat_agent_messages`;
132
+ for (const message of messages) {
133
+ this.sql`insert into cf_ai_chat_agent_messages (id, message) values (${message.id},${JSON.stringify(message)})`;
134
+ }
135
+ this.messages = messages;
136
+ this.broadcastChatMessage(
137
+ {
138
+ type: "cf_agent_chat_messages",
139
+ messages
140
+ },
141
+ excludeBroadcastIds
142
+ );
143
+ }
144
+ async reply(id, response) {
145
+ for await (const chunk of response.body) {
146
+ const body = decoder.decode(chunk);
147
+ for (const conn of this.getConnections()) {
148
+ this.sendChatMessage(conn, {
149
+ id,
150
+ type: "cf_agent_use_chat_response",
151
+ body,
152
+ done: false
153
+ });
154
+ }
155
+ }
156
+ for (const conn of this.getConnections()) {
157
+ this.sendChatMessage(conn, {
158
+ id,
159
+ type: "cf_agent_use_chat_response",
160
+ body: "",
161
+ done: true
162
+ });
163
+ }
164
+ }
165
+ };
166
+ export {
167
+ AIChatAgent
168
+ };
169
+ //# sourceMappingURL=ai-chat-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ai-chat-agent.ts"],"sourcesContent":["import { Agent, type AgentContext, type Connection, type WSMessage } from \"./\";\nimport type {\n Message as ChatMessage,\n StreamTextOnFinishCallback,\n ToolSet,\n} from \"ai\";\nimport { appendResponseMessages } from \"ai\";\nimport type { OutgoingMessage, IncomingMessage } from \"./ai-types\";\nconst decoder = new TextDecoder();\n\n/**\n * Extension of Agent with built-in chat capabilities\n * @template Env Environment type containing bindings\n */\nexport class AIChatAgent<Env = unknown, State = unknown> extends Agent<\n Env,\n State\n> {\n /** Array of chat messages for the current conversation */\n messages: ChatMessage[];\n constructor(ctx: AgentContext, env: Env) {\n super(ctx, env);\n this.sql`create table if not exists cf_ai_chat_agent_messages (\n id text primary key,\n message text not null,\n created_at datetime default current_timestamp\n )`;\n this.messages = (\n this.sql`select * from cf_ai_chat_agent_messages` || []\n ).map((row) => {\n return JSON.parse(row.message as string);\n });\n }\n\n private sendChatMessage(connection: Connection, message: OutgoingMessage) {\n try {\n connection.send(JSON.stringify(message));\n } catch (e) {\n // silently ignore\n }\n }\n\n private broadcastChatMessage(message: OutgoingMessage, exclude?: string[]) {\n try {\n this.broadcast(JSON.stringify(message), exclude);\n } catch (e) {\n // silently ignore\n }\n }\n\n override async onMessage(connection: Connection, message: WSMessage) {\n if (typeof message === \"string\") {\n let data: IncomingMessage;\n try {\n data = JSON.parse(message) as IncomingMessage;\n } catch (error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (\n data.type === \"cf_agent_use_chat_request\" &&\n data.init.method === \"POST\"\n ) {\n const {\n method,\n keepalive,\n headers,\n body, // we're reading this\n redirect,\n integrity,\n credentials,\n mode,\n referrer,\n referrerPolicy,\n window,\n // dispatcher,\n // duplex\n } = data.init;\n const { messages } = JSON.parse(body as string);\n this.broadcastChatMessage(\n {\n type: \"cf_agent_chat_messages\",\n messages,\n },\n [connection.id]\n );\n await this.persistMessages(messages, [connection.id]);\n const response = await this.onChatMessage(async ({ response }) => {\n const finalMessages = appendResponseMessages({\n messages,\n responseMessages: response.messages,\n });\n\n await this.persistMessages(finalMessages, [connection.id]);\n });\n if (response) {\n await this.reply(data.id, response);\n }\n } else if (data.type === \"cf_agent_chat_clear\") {\n this.sql`delete from cf_ai_chat_agent_messages`;\n this.messages = [];\n this.broadcastChatMessage(\n {\n type: \"cf_agent_chat_clear\",\n },\n [connection.id]\n );\n } else if (data.type === \"cf_agent_chat_messages\") {\n // replace the messages with the new ones\n await this.persistMessages(data.messages, [connection.id]);\n }\n }\n }\n\n override async onRequest(request: Request): Promise<Response> {\n if (request.url.endsWith(\"/get-messages\")) {\n const messages = (\n this.sql`select * from cf_ai_chat_agent_messages` || []\n ).map((row) => {\n return JSON.parse(row.message as string);\n });\n return new Response(JSON.stringify(messages));\n }\n return super.onRequest(request);\n }\n\n /**\n * Handle incoming chat messages and generate a response\n * @param onFinish Callback to be called when the response is finished\n * @returns Response to send to the client or undefined\n */\n async onChatMessage(\n onFinish: StreamTextOnFinishCallback<ToolSet>\n ): Promise<Response | undefined> {\n throw new Error(\n \"recieved a chat message, override onChatMessage and return a Response to send to the client\"\n );\n }\n\n /**\n * Save messages on the server side and trigger AI response\n * @param messages Chat messages to save\n */\n async saveMessages(messages: ChatMessage[]) {\n await this.persistMessages(messages);\n const response = await this.onChatMessage(async ({ response }) => {\n const finalMessages = appendResponseMessages({\n messages,\n responseMessages: response.messages,\n });\n\n await this.persistMessages(finalMessages, []);\n });\n if (response) {\n // we're just going to drain the body\n // @ts-ignore TODO: fix this type error\n for await (const chunk of response.body!) {\n decoder.decode(chunk);\n }\n response.body?.cancel();\n }\n }\n\n private async persistMessages(\n messages: ChatMessage[],\n excludeBroadcastIds: string[] = []\n ) {\n this.sql`delete from cf_ai_chat_agent_messages`;\n for (const message of messages) {\n this.sql`insert into cf_ai_chat_agent_messages (id, message) values (${\n message.id\n },${JSON.stringify(message)})`;\n }\n this.messages = messages;\n this.broadcastChatMessage(\n {\n type: \"cf_agent_chat_messages\",\n messages: messages,\n },\n excludeBroadcastIds\n );\n }\n\n private async reply(id: string, response: Response) {\n // now take chunks out from dataStreamResponse and send them to the client\n\n // @ts-expect-error TODO: fix this type error\n for await (const chunk of response.body!) {\n const body = decoder.decode(chunk);\n\n for (const conn of this.getConnections()) {\n this.sendChatMessage(conn, {\n id,\n type: \"cf_agent_use_chat_response\",\n body,\n done: false,\n });\n }\n }\n\n for (const conn of this.getConnections()) {\n this.sendChatMessage(conn, {\n id,\n type: \"cf_agent_use_chat_response\",\n body: \"\",\n done: true,\n });\n }\n }\n}\n"],"mappings":"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","names":["response"]}