agents 0.0.0-ac0e999 → 0.0.0-ac74811

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.
Files changed (45) hide show
  1. package/dist/ai-chat-agent.d.ts +13 -12
  2. package/dist/ai-chat-agent.js +94 -57
  3. package/dist/ai-chat-agent.js.map +1 -1
  4. package/dist/ai-react.d.ts +9 -8
  5. package/dist/ai-react.js +27 -29
  6. package/dist/ai-react.js.map +1 -1
  7. package/dist/{chunk-D6UOOELW.js → chunk-767EASBA.js} +15 -15
  8. package/dist/chunk-767EASBA.js.map +1 -0
  9. package/dist/{chunk-25YDMV4H.js → chunk-E3LCYPCB.js} +34 -29
  10. package/dist/chunk-E3LCYPCB.js.map +1 -0
  11. package/dist/{chunk-DMJ7L3FI.js → chunk-JFRK72K3.js} +305 -168
  12. package/dist/chunk-JFRK72K3.js.map +1 -0
  13. package/dist/{chunk-ZKIVUOTQ.js → chunk-NKZZ66QY.js} +14 -21
  14. package/dist/chunk-NKZZ66QY.js.map +1 -0
  15. package/dist/client.d.ts +7 -1
  16. package/dist/client.js +1 -2
  17. package/dist/index-CITGJflw.d.ts +486 -0
  18. package/dist/index.d.ts +25 -369
  19. package/dist/index.js +4 -5
  20. package/dist/mcp/client.d.ts +287 -15
  21. package/dist/mcp/client.js +1 -2
  22. package/dist/mcp/do-oauth-client-provider.d.ts +3 -3
  23. package/dist/mcp/do-oauth-client-provider.js +1 -2
  24. package/dist/mcp/index.d.ts +12 -12
  25. package/dist/mcp/index.js +124 -121
  26. package/dist/mcp/index.js.map +1 -1
  27. package/dist/observability/index.d.ts +12 -0
  28. package/dist/observability/index.js +10 -0
  29. package/dist/react.d.ts +76 -10
  30. package/dist/react.js +16 -8
  31. package/dist/react.js.map +1 -1
  32. package/dist/schedule.d.ts +6 -6
  33. package/dist/schedule.js +4 -6
  34. package/dist/schedule.js.map +1 -1
  35. package/dist/serializable.d.ts +32 -0
  36. package/dist/serializable.js +1 -0
  37. package/dist/serializable.js.map +1 -0
  38. package/package.json +75 -71
  39. package/src/index.ts +253 -131
  40. package/dist/chunk-25YDMV4H.js.map +0 -1
  41. package/dist/chunk-D6UOOELW.js.map +0 -1
  42. package/dist/chunk-DMJ7L3FI.js.map +0 -1
  43. package/dist/chunk-NOUFNU2O.js +0 -12
  44. package/dist/chunk-ZKIVUOTQ.js.map +0 -1
  45. /package/dist/{chunk-NOUFNU2O.js.map → observability/index.js.map} +0 -0
@@ -1,15 +1,15 @@
1
- import { Agent, AgentContext } from "./index.js";
2
1
  import { Message, StreamTextOnFinishCallback, ToolSet } from "ai";
2
+ import { A as Agent, a as AgentContext } from "./index-CITGJflw.js";
3
3
  import { Connection, WSMessage } from "partyserver";
4
+ import "@modelcontextprotocol/sdk/client/index.js";
5
+ import "@modelcontextprotocol/sdk/types.js";
4
6
  import "./mcp/client.js";
5
7
  import "zod";
6
- import "@modelcontextprotocol/sdk/types.js";
7
- import "@modelcontextprotocol/sdk/client/index.js";
8
8
  import "@modelcontextprotocol/sdk/client/sse.js";
9
+ import "@modelcontextprotocol/sdk/shared/protocol.js";
9
10
  import "./mcp/do-oauth-client-provider.js";
10
11
  import "@modelcontextprotocol/sdk/client/auth.js";
11
12
  import "@modelcontextprotocol/sdk/shared/auth.js";
12
- import "@modelcontextprotocol/sdk/shared/protocol.js";
13
13
 
14
14
  /**
15
15
  * Extension of Agent with built-in chat capabilities
@@ -23,14 +23,14 @@ declare class AIChatAgent<Env = unknown, State = unknown> extends Agent<
23
23
  * Map of message `id`s to `AbortController`s
24
24
  * useful to propagate request cancellation signals for any external calls made by the agent
25
25
  */
26
- private chatMessageAbortControllers;
26
+ private _chatMessageAbortControllers;
27
27
  /** Array of chat messages for the current conversation */
28
28
  messages: Message[];
29
29
  constructor(ctx: AgentContext, env: Env);
30
- private broadcastChatMessage;
30
+ private _broadcastChatMessage;
31
31
  onMessage(connection: Connection, message: WSMessage): Promise<void>;
32
32
  onRequest(request: Request): Promise<Response>;
33
- private tryCatchChat;
33
+ private _tryCatchChat;
34
34
  /**
35
35
  * Handle incoming chat messages and generate a response
36
36
  * @param onFinish Callback to be called when the response is finished
@@ -52,26 +52,27 @@ declare class AIChatAgent<Env = unknown, State = unknown> extends Agent<
52
52
  messages: Message[],
53
53
  excludeBroadcastIds?: string[]
54
54
  ): Promise<void>;
55
- private reply;
55
+ private _messagesNotAlreadyInAgent;
56
+ private _reply;
56
57
  /**
57
58
  * For the given message id, look up its associated AbortController
58
59
  * If the AbortController does not exist, create and store one in memory
59
60
  *
60
61
  * returns the AbortSignal associated with the AbortController
61
62
  */
62
- private getAbortSignal;
63
+ private _getAbortSignal;
63
64
  /**
64
65
  * Remove an abort controller from the cache of pending message responses
65
66
  */
66
- private removeAbortController;
67
+ private _removeAbortController;
67
68
  /**
68
69
  * Propagate an abort signal for any requests associated with the given message id
69
70
  */
70
- private cancelChatRequest;
71
+ private _cancelChatRequest;
71
72
  /**
72
73
  * Abort all pending requests and clear the cache of AbortControllers
73
74
  */
74
- private destroyAbortControllers;
75
+ private _destroyAbortControllers;
75
76
  /**
76
77
  * When the DO is destroyed, cancel all pending requests
77
78
  */
@@ -1,10 +1,9 @@
1
1
  import {
2
2
  Agent
3
- } from "./chunk-DMJ7L3FI.js";
4
- import "./chunk-D6UOOELW.js";
5
- import "./chunk-ZKIVUOTQ.js";
6
- import "./chunk-25YDMV4H.js";
7
- import "./chunk-NOUFNU2O.js";
3
+ } from "./chunk-JFRK72K3.js";
4
+ import "./chunk-E3LCYPCB.js";
5
+ import "./chunk-767EASBA.js";
6
+ import "./chunk-NKZZ66QY.js";
8
7
 
9
8
  // src/ai-chat-agent.ts
10
9
  import { appendResponseMessages } from "ai";
@@ -20,9 +19,9 @@ var AIChatAgent = class extends Agent {
20
19
  this.messages = (this.sql`select * from cf_ai_chat_agent_messages` || []).map((row) => {
21
20
  return JSON.parse(row.message);
22
21
  });
23
- this.chatMessageAbortControllers = /* @__PURE__ */ new Map();
22
+ this._chatMessageAbortControllers = /* @__PURE__ */ new Map();
24
23
  }
25
- broadcastChatMessage(message, exclude) {
24
+ _broadcastChatMessage(message, exclude) {
26
25
  this.broadcast(JSON.stringify(message), exclude);
27
26
  }
28
27
  async onMessage(connection, message) {
@@ -30,59 +29,93 @@ var AIChatAgent = class extends Agent {
30
29
  let data;
31
30
  try {
32
31
  data = JSON.parse(message);
33
- } catch (error) {
32
+ } catch (_error) {
34
33
  return;
35
34
  }
36
35
  if (data.type === "cf_agent_use_chat_request" && data.init.method === "POST") {
37
36
  const {
38
- method,
39
- keepalive,
40
- headers,
41
- body,
37
+ // method,
38
+ // keepalive,
39
+ // headers,
40
+ body
42
41
  // we're reading this
43
- redirect,
44
- integrity,
45
- credentials,
46
- mode,
47
- referrer,
48
- referrerPolicy,
49
- window
42
+ //
43
+ // // these might not exist?
50
44
  // dispatcher,
51
45
  // duplex
52
46
  } = data.init;
53
47
  const { messages } = JSON.parse(body);
54
- this.broadcastChatMessage(
48
+ this._broadcastChatMessage(
55
49
  {
56
- type: "cf_agent_chat_messages",
57
- messages
50
+ messages,
51
+ type: "cf_agent_chat_messages"
58
52
  },
59
53
  [connection.id]
60
54
  );
55
+ const incomingMessages = this._messagesNotAlreadyInAgent(messages);
61
56
  await this.persistMessages(messages, [connection.id]);
57
+ this.observability?.emit(
58
+ {
59
+ displayMessage: "Chat message request",
60
+ id: data.id,
61
+ payload: {
62
+ message: incomingMessages
63
+ },
64
+ timestamp: Date.now(),
65
+ type: "message:request"
66
+ },
67
+ this.ctx
68
+ );
62
69
  const chatMessageId = data.id;
63
- const abortSignal = this.getAbortSignal(chatMessageId);
64
- return this.tryCatchChat(async () => {
70
+ const abortSignal = this._getAbortSignal(chatMessageId);
71
+ return this._tryCatchChat(async () => {
65
72
  const response = await this.onChatMessage(
66
73
  async ({ response: response2 }) => {
67
74
  const finalMessages = appendResponseMessages({
68
75
  messages,
69
76
  responseMessages: response2.messages
70
77
  });
78
+ const outgoingMessages = this._messagesNotAlreadyInAgent(finalMessages);
71
79
  await this.persistMessages(finalMessages, [connection.id]);
72
- this.removeAbortController(chatMessageId);
80
+ this._removeAbortController(chatMessageId);
81
+ this.observability?.emit(
82
+ {
83
+ displayMessage: "Chat message response",
84
+ id: data.id,
85
+ payload: {
86
+ message: outgoingMessages
87
+ },
88
+ timestamp: Date.now(),
89
+ type: "message:response"
90
+ },
91
+ this.ctx
92
+ );
73
93
  },
74
94
  abortSignal ? { abortSignal } : void 0
75
95
  );
76
96
  if (response) {
77
- await this.reply(data.id, response);
97
+ await this._reply(data.id, response);
98
+ } else {
99
+ console.warn(
100
+ `[AIChatAgent] onChatMessage returned no response for chatMessageId: ${chatMessageId}`
101
+ );
102
+ this._broadcastChatMessage(
103
+ {
104
+ body: "No response was generated by the agent.",
105
+ done: true,
106
+ id: data.id,
107
+ type: "cf_agent_use_chat_response"
108
+ },
109
+ [connection.id]
110
+ );
78
111
  }
79
112
  });
80
113
  }
81
114
  if (data.type === "cf_agent_chat_clear") {
82
- this.destroyAbortControllers();
115
+ this._destroyAbortControllers();
83
116
  this.sql`delete from cf_ai_chat_agent_messages`;
84
117
  this.messages = [];
85
- this.broadcastChatMessage(
118
+ this._broadcastChatMessage(
86
119
  {
87
120
  type: "cf_agent_chat_clear"
88
121
  },
@@ -91,12 +124,12 @@ var AIChatAgent = class extends Agent {
91
124
  } else if (data.type === "cf_agent_chat_messages") {
92
125
  await this.persistMessages(data.messages, [connection.id]);
93
126
  } else if (data.type === "cf_agent_chat_request_cancel") {
94
- this.cancelChatRequest(data.id);
127
+ this._cancelChatRequest(data.id);
95
128
  }
96
129
  }
97
130
  }
98
131
  async onRequest(request) {
99
- return this.tryCatchChat(() => {
132
+ return this._tryCatchChat(() => {
100
133
  const url = new URL(request.url);
101
134
  if (url.pathname.endsWith("/get-messages")) {
102
135
  const messages = (this.sql`select * from cf_ai_chat_agent_messages` || []).map((row) => {
@@ -107,7 +140,7 @@ var AIChatAgent = class extends Agent {
107
140
  return super.onRequest(request);
108
141
  });
109
142
  }
110
- async tryCatchChat(fn) {
143
+ async _tryCatchChat(fn) {
111
144
  try {
112
145
  return await fn();
113
146
  } catch (e) {
@@ -151,30 +184,34 @@ var AIChatAgent = class extends Agent {
151
184
  this.sql`insert into cf_ai_chat_agent_messages (id, message) values (${message.id},${JSON.stringify(message)})`;
152
185
  }
153
186
  this.messages = messages;
154
- this.broadcastChatMessage(
187
+ this._broadcastChatMessage(
155
188
  {
156
- type: "cf_agent_chat_messages",
157
- messages
189
+ messages,
190
+ type: "cf_agent_chat_messages"
158
191
  },
159
192
  excludeBroadcastIds
160
193
  );
161
194
  }
162
- async reply(id, response) {
163
- return this.tryCatchChat(async () => {
195
+ _messagesNotAlreadyInAgent(messages) {
196
+ const existingIds = new Set(this.messages.map((message) => message.id));
197
+ return messages.filter((message) => !existingIds.has(message.id));
198
+ }
199
+ async _reply(id, response) {
200
+ return this._tryCatchChat(async () => {
164
201
  for await (const chunk of response.body) {
165
202
  const body = decoder.decode(chunk);
166
- this.broadcastChatMessage({
167
- id,
168
- type: "cf_agent_use_chat_response",
203
+ this._broadcastChatMessage({
169
204
  body,
170
- done: false
205
+ done: false,
206
+ id,
207
+ type: "cf_agent_use_chat_response"
171
208
  });
172
209
  }
173
- this.broadcastChatMessage({
174
- id,
175
- type: "cf_agent_use_chat_response",
210
+ this._broadcastChatMessage({
176
211
  body: "",
177
- done: true
212
+ done: true,
213
+ id,
214
+ type: "cf_agent_use_chat_response"
178
215
  });
179
216
  });
180
217
  }
@@ -184,44 +221,44 @@ var AIChatAgent = class extends Agent {
184
221
  *
185
222
  * returns the AbortSignal associated with the AbortController
186
223
  */
187
- getAbortSignal(id) {
224
+ _getAbortSignal(id) {
188
225
  if (typeof id !== "string") {
189
226
  return void 0;
190
227
  }
191
- if (!this.chatMessageAbortControllers.has(id)) {
192
- this.chatMessageAbortControllers.set(id, new AbortController());
228
+ if (!this._chatMessageAbortControllers.has(id)) {
229
+ this._chatMessageAbortControllers.set(id, new AbortController());
193
230
  }
194
- return this.chatMessageAbortControllers.get(id)?.signal;
231
+ return this._chatMessageAbortControllers.get(id)?.signal;
195
232
  }
196
233
  /**
197
234
  * Remove an abort controller from the cache of pending message responses
198
235
  */
199
- removeAbortController(id) {
200
- this.chatMessageAbortControllers.delete(id);
236
+ _removeAbortController(id) {
237
+ this._chatMessageAbortControllers.delete(id);
201
238
  }
202
239
  /**
203
240
  * Propagate an abort signal for any requests associated with the given message id
204
241
  */
205
- cancelChatRequest(id) {
206
- if (this.chatMessageAbortControllers.has(id)) {
207
- const abortController = this.chatMessageAbortControllers.get(id);
242
+ _cancelChatRequest(id) {
243
+ if (this._chatMessageAbortControllers.has(id)) {
244
+ const abortController = this._chatMessageAbortControllers.get(id);
208
245
  abortController?.abort();
209
246
  }
210
247
  }
211
248
  /**
212
249
  * Abort all pending requests and clear the cache of AbortControllers
213
250
  */
214
- destroyAbortControllers() {
215
- for (const controller of this.chatMessageAbortControllers.values()) {
251
+ _destroyAbortControllers() {
252
+ for (const controller of this._chatMessageAbortControllers.values()) {
216
253
  controller?.abort();
217
254
  }
218
- this.chatMessageAbortControllers.clear();
255
+ this._chatMessageAbortControllers.clear();
219
256
  }
220
257
  /**
221
258
  * When the DO is destroyed, cancel all pending requests
222
259
  */
223
260
  async destroy() {
224
- this.destroyAbortControllers();
261
+ this._destroyAbortControllers();
225
262
  await super.destroy();
226
263
  }
227
264
  };
@@ -1 +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\";\n\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 /**\n * Map of message `id`s to `AbortController`s\n * useful to propagate request cancellation signals for any external calls made by the agent\n */\n private chatMessageAbortControllers: Map<string, AbortController>;\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 this.chatMessageAbortControllers = new Map();\n }\n\n private broadcastChatMessage(message: OutgoingMessage, exclude?: string[]) {\n this.broadcast(JSON.stringify(message), exclude);\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\n const chatMessageId = data.id;\n const abortSignal = this.getAbortSignal(chatMessageId);\n\n return this.tryCatchChat(async () => {\n const response = await this.onChatMessage(\n async ({ response }) => {\n const finalMessages = appendResponseMessages({\n messages,\n responseMessages: response.messages,\n });\n\n await this.persistMessages(finalMessages, [connection.id]);\n this.removeAbortController(chatMessageId);\n },\n abortSignal ? { abortSignal } : undefined\n );\n\n if (response) {\n await this.reply(data.id, response);\n }\n });\n }\n if (data.type === \"cf_agent_chat_clear\") {\n this.destroyAbortControllers();\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 } else if (data.type === \"cf_agent_chat_request_cancel\") {\n // propagate an abort signal for the associated request\n this.cancelChatRequest(data.id);\n }\n }\n }\n\n override async onRequest(request: Request): Promise<Response> {\n return this.tryCatchChat(() => {\n const url = new URL(request.url);\n if (url.pathname.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 Response.json(messages);\n }\n return super.onRequest(request);\n });\n }\n\n private async tryCatchChat<T>(fn: () => T | Promise<T>) {\n try {\n return await fn();\n } catch (e) {\n throw this.onError(e);\n }\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 * @param options.signal A signal to pass to any child requests which can be used to cancel them\n * @returns Response to send to the client or undefined\n */\n async onChatMessage(\n onFinish: StreamTextOnFinishCallback<ToolSet>,\n options?: { abortSignal: AbortSignal | undefined }\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 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 return this.tryCatchChat(async () => {\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 this.broadcastChatMessage({\n id,\n type: \"cf_agent_use_chat_response\",\n body,\n done: false,\n });\n }\n\n this.broadcastChatMessage({\n id,\n type: \"cf_agent_use_chat_response\",\n body: \"\",\n done: true,\n });\n });\n }\n\n /**\n * For the given message id, look up its associated AbortController\n * If the AbortController does not exist, create and store one in memory\n *\n * returns the AbortSignal associated with the AbortController\n */\n private getAbortSignal(id: string): AbortSignal | undefined {\n // Defensive check, since we're coercing message types at the moment\n if (typeof id !== \"string\") {\n return undefined;\n }\n\n if (!this.chatMessageAbortControllers.has(id)) {\n this.chatMessageAbortControllers.set(id, new AbortController());\n }\n\n return this.chatMessageAbortControllers.get(id)?.signal;\n }\n\n /**\n * Remove an abort controller from the cache of pending message responses\n */\n private removeAbortController(id: string) {\n this.chatMessageAbortControllers.delete(id);\n }\n\n /**\n * Propagate an abort signal for any requests associated with the given message id\n */\n private cancelChatRequest(id: string) {\n if (this.chatMessageAbortControllers.has(id)) {\n const abortController = this.chatMessageAbortControllers.get(id);\n abortController?.abort();\n }\n }\n\n /**\n * Abort all pending requests and clear the cache of AbortControllers\n */\n private destroyAbortControllers() {\n for (const controller of this.chatMessageAbortControllers.values()) {\n controller?.abort();\n }\n this.chatMessageAbortControllers.clear();\n }\n\n /**\n * When the DO is destroyed, cancel all pending requests\n */\n async destroy() {\n this.destroyAbortControllers();\n await super.destroy();\n }\n}\n"],"mappings":";;;;;;;;;AAMA,SAAS,8BAA8B;AAGvC,IAAM,UAAU,IAAI,YAAY;AAMzB,IAAM,cAAN,cAA0D,MAG/D;AAAA,EAQA,YAAY,KAAmB,KAAU;AACvC,UAAM,KAAK,GAAG;AACd,SAAK;AAAA;AAAA;AAAA;AAAA;AAKL,SAAK,YACH,KAAK,gDAAgD,CAAC,GACtD,IAAI,CAAC,QAAQ;AACb,aAAO,KAAK,MAAM,IAAI,OAAiB;AAAA,IACzC,CAAC;AAED,SAAK,8BAA8B,oBAAI,IAAI;AAAA,EAC7C;AAAA,EAEQ,qBAAqB,SAA0B,SAAoB;AACzE,SAAK,UAAU,KAAK,UAAU,OAAO,GAAG,OAAO;AAAA,EACjD;AAAA,EAEA,MAAe,UAAU,YAAwB,SAAoB;AACnE,QAAI,OAAO,YAAY,UAAU;AAC/B,UAAI;AACJ,UAAI;AACF,eAAO,KAAK,MAAM,OAAO;AAAA,MAC3B,SAAS,OAAO;AAGd;AAAA,MACF;AACA,UACE,KAAK,SAAS,+BACd,KAAK,KAAK,WAAW,QACrB;AACA,cAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA;AAAA,QAGF,IAAI,KAAK;AACT,cAAM,EAAE,SAAS,IAAI,KAAK,MAAM,IAAc;AAC9C,aAAK;AAAA,UACH;AAAA,YACE,MAAM;AAAA,YACN;AAAA,UACF;AAAA,UACA,CAAC,WAAW,EAAE;AAAA,QAChB;AACA,cAAM,KAAK,gBAAgB,UAAU,CAAC,WAAW,EAAE,CAAC;AAEpD,cAAM,gBAAgB,KAAK;AAC3B,cAAM,cAAc,KAAK,eAAe,aAAa;AAErD,eAAO,KAAK,aAAa,YAAY;AACnC,gBAAM,WAAW,MAAM,KAAK;AAAA,YAC1B,OAAO,EAAE,UAAAA,UAAS,MAAM;AACtB,oBAAM,gBAAgB,uBAAuB;AAAA,gBAC3C;AAAA,gBACA,kBAAkBA,UAAS;AAAA,cAC7B,CAAC;AAED,oBAAM,KAAK,gBAAgB,eAAe,CAAC,WAAW,EAAE,CAAC;AACzD,mBAAK,sBAAsB,aAAa;AAAA,YAC1C;AAAA,YACA,cAAc,EAAE,YAAY,IAAI;AAAA,UAClC;AAEA,cAAI,UAAU;AACZ,kBAAM,KAAK,MAAM,KAAK,IAAI,QAAQ;AAAA,UACpC;AAAA,QACF,CAAC;AAAA,MACH;AACA,UAAI,KAAK,SAAS,uBAAuB;AACvC,aAAK,wBAAwB;AAC7B,aAAK;AACL,aAAK,WAAW,CAAC;AACjB,aAAK;AAAA,UACH;AAAA,YACE,MAAM;AAAA,UACR;AAAA,UACA,CAAC,WAAW,EAAE;AAAA,QAChB;AAAA,MACF,WAAW,KAAK,SAAS,0BAA0B;AAEjD,cAAM,KAAK,gBAAgB,KAAK,UAAU,CAAC,WAAW,EAAE,CAAC;AAAA,MAC3D,WAAW,KAAK,SAAS,gCAAgC;AAEvD,aAAK,kBAAkB,KAAK,EAAE;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAe,UAAU,SAAqC;AAC5D,WAAO,KAAK,aAAa,MAAM;AAC7B,YAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,UAAI,IAAI,SAAS,SAAS,eAAe,GAAG;AAC1C,cAAM,YACJ,KAAK,gDAAgD,CAAC,GACtD,IAAI,CAAC,QAAQ;AACb,iBAAO,KAAK,MAAM,IAAI,OAAiB;AAAA,QACzC,CAAC;AACD,eAAO,SAAS,KAAK,QAAQ;AAAA,MAC/B;AACA,aAAO,MAAM,UAAU,OAAO;AAAA,IAChC,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,aAAgB,IAA0B;AACtD,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,SAAS,GAAG;AACV,YAAM,KAAK,QAAQ,CAAC;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,cACJ,UACA,SAC+B;AAC/B,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAAa,UAAyB;AAC1C,UAAM,KAAK,gBAAgB,QAAQ;AACnC,UAAM,WAAW,MAAM,KAAK,cAAc,OAAO,EAAE,UAAAA,UAAS,MAAM;AAChE,YAAM,gBAAgB,uBAAuB;AAAA,QAC3C;AAAA,QACA,kBAAkBA,UAAS;AAAA,MAC7B,CAAC;AAED,YAAM,KAAK,gBAAgB,eAAe,CAAC,CAAC;AAAA,IAC9C,CAAC;AACD,QAAI,UAAU;AAGZ,uBAAiB,SAAS,SAAS,MAAO;AACxC,gBAAQ,OAAO,KAAK;AAAA,MACtB;AACA,eAAS,MAAM,OAAO;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,MAAM,gBACJ,UACA,sBAAgC,CAAC,GACjC;AACA,SAAK;AACL,eAAW,WAAW,UAAU;AAC9B,WAAK,kEACH,QAAQ,EACV,IAAI,KAAK,UAAU,OAAO,CAAC;AAAA,IAC7B;AACA,SAAK,WAAW;AAChB,SAAK;AAAA,MACH;AAAA,QACE,MAAM;AAAA,QACN;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,MAAM,IAAY,UAAoB;AAElD,WAAO,KAAK,aAAa,YAAY;AAEnC,uBAAiB,SAAS,SAAS,MAAO;AACxC,cAAM,OAAO,QAAQ,OAAO,KAAK;AAEjC,aAAK,qBAAqB;AAAA,UACxB;AAAA,UACA,MAAM;AAAA,UACN;AAAA,UACA,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAEA,WAAK,qBAAqB;AAAA,QACxB;AAAA,QACA,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,MACR,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,eAAe,IAAqC;AAE1D,QAAI,OAAO,OAAO,UAAU;AAC1B,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,KAAK,4BAA4B,IAAI,EAAE,GAAG;AAC7C,WAAK,4BAA4B,IAAI,IAAI,IAAI,gBAAgB,CAAC;AAAA,IAChE;AAEA,WAAO,KAAK,4BAA4B,IAAI,EAAE,GAAG;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAAsB,IAAY;AACxC,SAAK,4BAA4B,OAAO,EAAE;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,IAAY;AACpC,QAAI,KAAK,4BAA4B,IAAI,EAAE,GAAG;AAC5C,YAAM,kBAAkB,KAAK,4BAA4B,IAAI,EAAE;AAC/D,uBAAiB,MAAM;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,0BAA0B;AAChC,eAAW,cAAc,KAAK,4BAA4B,OAAO,GAAG;AAClE,kBAAY,MAAM;AAAA,IACpB;AACA,SAAK,4BAA4B,MAAM;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU;AACd,SAAK,wBAAwB;AAC7B,UAAM,MAAM,QAAQ;AAAA,EACtB;AACF;","names":["response"]}
1
+ {"version":3,"sources":["../src/ai-chat-agent.ts"],"sourcesContent":["import type {\n Message as ChatMessage,\n StreamTextOnFinishCallback,\n ToolSet,\n} from \"ai\";\nimport { appendResponseMessages } from \"ai\";\nimport { Agent, type AgentContext, type Connection, type WSMessage } from \"./\";\nimport type { IncomingMessage, OutgoingMessage } from \"./ai-types\";\n\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 /**\n * Map of message `id`s to `AbortController`s\n * useful to propagate request cancellation signals for any external calls made by the agent\n */\n private _chatMessageAbortControllers: Map<string, AbortController>;\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 this._chatMessageAbortControllers = new Map();\n }\n\n private _broadcastChatMessage(message: OutgoingMessage, exclude?: string[]) {\n this.broadcast(JSON.stringify(message), exclude);\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 //\n // // these might not exist?\n // dispatcher,\n // duplex\n } = data.init;\n const { messages } = JSON.parse(body as string);\n this._broadcastChatMessage(\n {\n messages,\n type: \"cf_agent_chat_messages\",\n },\n [connection.id]\n );\n\n const incomingMessages = this._messagesNotAlreadyInAgent(messages);\n await this.persistMessages(messages, [connection.id]);\n\n this.observability?.emit(\n {\n displayMessage: \"Chat message request\",\n id: data.id,\n payload: {\n message: incomingMessages,\n },\n timestamp: Date.now(),\n type: \"message:request\",\n },\n this.ctx\n );\n\n const chatMessageId = data.id;\n const abortSignal = this._getAbortSignal(chatMessageId);\n\n return this._tryCatchChat(async () => {\n const response = await this.onChatMessage(\n async ({ response }) => {\n const finalMessages = appendResponseMessages({\n messages,\n responseMessages: response.messages,\n });\n\n const outgoingMessages =\n this._messagesNotAlreadyInAgent(finalMessages);\n await this.persistMessages(finalMessages, [connection.id]);\n this._removeAbortController(chatMessageId);\n\n this.observability?.emit(\n {\n displayMessage: \"Chat message response\",\n id: data.id,\n payload: {\n message: outgoingMessages,\n },\n timestamp: Date.now(),\n type: \"message:response\",\n },\n this.ctx\n );\n },\n abortSignal ? { abortSignal } : undefined\n );\n\n if (response) {\n await this._reply(data.id, response);\n } else {\n // Log a warning for observability\n console.warn(\n `[AIChatAgent] onChatMessage returned no response for chatMessageId: ${chatMessageId}`\n );\n // Send a fallback message to the client\n this._broadcastChatMessage(\n {\n body: \"No response was generated by the agent.\",\n done: true,\n id: data.id,\n type: \"cf_agent_use_chat_response\",\n },\n [connection.id]\n );\n }\n });\n }\n if (data.type === \"cf_agent_chat_clear\") {\n this._destroyAbortControllers();\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 } else if (data.type === \"cf_agent_chat_request_cancel\") {\n // propagate an abort signal for the associated request\n this._cancelChatRequest(data.id);\n }\n }\n }\n\n override async onRequest(request: Request): Promise<Response> {\n return this._tryCatchChat(() => {\n const url = new URL(request.url);\n if (url.pathname.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 Response.json(messages);\n }\n return super.onRequest(request);\n });\n }\n\n private async _tryCatchChat<T>(fn: () => T | Promise<T>) {\n try {\n return await fn();\n } catch (e) {\n throw this.onError(e);\n }\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 * @param options.signal A signal to pass to any child requests which can be used to cancel them\n * @returns Response to send to the client or undefined\n */\n async onChatMessage(\n // biome-ignore lint/correctness/noUnusedFunctionParameters: overridden later\n onFinish: StreamTextOnFinishCallback<ToolSet>,\n // biome-ignore lint/correctness/noUnusedFunctionParameters: overridden later\n options?: { abortSignal: AbortSignal | undefined }\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 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 messages: messages,\n type: \"cf_agent_chat_messages\",\n },\n excludeBroadcastIds\n );\n }\n\n private _messagesNotAlreadyInAgent(messages: ChatMessage[]) {\n const existingIds = new Set(this.messages.map((message) => message.id));\n return messages.filter((message) => !existingIds.has(message.id));\n }\n\n private async _reply(id: string, response: Response) {\n // now take chunks out from dataStreamResponse and send them to the client\n return this._tryCatchChat(async () => {\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 this._broadcastChatMessage({\n body,\n done: false,\n id,\n type: \"cf_agent_use_chat_response\",\n });\n }\n\n this._broadcastChatMessage({\n body: \"\",\n done: true,\n id,\n type: \"cf_agent_use_chat_response\",\n });\n });\n }\n\n /**\n * For the given message id, look up its associated AbortController\n * If the AbortController does not exist, create and store one in memory\n *\n * returns the AbortSignal associated with the AbortController\n */\n private _getAbortSignal(id: string): AbortSignal | undefined {\n // Defensive check, since we're coercing message types at the moment\n if (typeof id !== \"string\") {\n return undefined;\n }\n\n if (!this._chatMessageAbortControllers.has(id)) {\n this._chatMessageAbortControllers.set(id, new AbortController());\n }\n\n return this._chatMessageAbortControllers.get(id)?.signal;\n }\n\n /**\n * Remove an abort controller from the cache of pending message responses\n */\n private _removeAbortController(id: string) {\n this._chatMessageAbortControllers.delete(id);\n }\n\n /**\n * Propagate an abort signal for any requests associated with the given message id\n */\n private _cancelChatRequest(id: string) {\n if (this._chatMessageAbortControllers.has(id)) {\n const abortController = this._chatMessageAbortControllers.get(id);\n abortController?.abort();\n }\n }\n\n /**\n * Abort all pending requests and clear the cache of AbortControllers\n */\n private _destroyAbortControllers() {\n for (const controller of this._chatMessageAbortControllers.values()) {\n controller?.abort();\n }\n this._chatMessageAbortControllers.clear();\n }\n\n /**\n * When the DO is destroyed, cancel all pending requests\n */\n async destroy() {\n this._destroyAbortControllers();\n await super.destroy();\n }\n}\n"],"mappings":"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","names":["response"]}
@@ -4,18 +4,19 @@ import { useChat } from "@ai-sdk/react";
4
4
  import { useAgent } from "./react.js";
5
5
  import "partysocket";
6
6
  import "partysocket/react";
7
- import "./index.js";
7
+ import "./index-CITGJflw.js";
8
+ import "@modelcontextprotocol/sdk/client/index.js";
9
+ import "@modelcontextprotocol/sdk/types.js";
8
10
  import "partyserver";
9
11
  import "./mcp/client.js";
10
12
  import "zod";
11
- import "@modelcontextprotocol/sdk/types.js";
12
- import "@modelcontextprotocol/sdk/client/index.js";
13
13
  import "@modelcontextprotocol/sdk/client/sse.js";
14
+ import "@modelcontextprotocol/sdk/shared/protocol.js";
14
15
  import "./mcp/do-oauth-client-provider.js";
15
16
  import "@modelcontextprotocol/sdk/client/auth.js";
16
17
  import "@modelcontextprotocol/sdk/shared/auth.js";
17
- import "@modelcontextprotocol/sdk/shared/protocol.js";
18
18
  import "./client.js";
19
+ import "./serializable.js";
19
20
 
20
21
  type GetInitialMessagesOptions = {
21
22
  agent: string;
@@ -44,15 +45,15 @@ type UseAgentChatOptions<State> = Omit<
44
45
  declare function useAgentChat<State = unknown>(
45
46
  options: UseAgentChatOptions<State>
46
47
  ): {
48
+ /**
49
+ * Clear chat history on both client and Agent
50
+ */
51
+ clearHistory: () => void;
47
52
  /**
48
53
  * Set the chat messages and synchronize with the Agent
49
54
  * @param messages New messages to set
50
55
  */
51
56
  setMessages: (messages: Message[]) => void;
52
- /**
53
- * Clear chat history on both client and Agent
54
- */
55
- clearHistory: () => void;
56
57
  messages: ai.UIMessage[];
57
58
  error: undefined | Error;
58
59
  append: (
package/dist/ai-react.js CHANGED
@@ -1,9 +1,7 @@
1
- import "./chunk-NOUFNU2O.js";
2
-
3
1
  // src/ai-react.tsx
4
2
  import { useChat } from "@ai-sdk/react";
5
- import { use, useEffect } from "react";
6
3
  import { nanoid } from "nanoid";
4
+ import { use, useEffect } from "react";
7
5
  var requestCache = /* @__PURE__ */ new Map();
8
6
  function useAgentChat(options) {
9
7
  const { agent, getInitialMessages, ...rest } = options;
@@ -19,8 +17,8 @@ function useAgentChat(options) {
19
17
  const getMessagesUrl = new URL(url);
20
18
  getMessagesUrl.pathname += "/get-messages";
21
19
  const response = await fetch(getMessagesUrl.toString(), {
22
- headers: options.headers,
23
- credentials: options.credentials
20
+ credentials: options.credentials,
21
+ headers: options.headers
24
22
  });
25
23
  return response.json();
26
24
  }
@@ -71,8 +69,8 @@ function useAgentChat(options) {
71
69
  signal?.addEventListener("abort", () => {
72
70
  agent.send(
73
71
  JSON.stringify({
74
- type: "cf_agent_chat_request_cancel",
75
- id
72
+ id,
73
+ type: "cf_agent_chat_request_cancel"
76
74
  })
77
75
  );
78
76
  abortController.abort();
@@ -84,7 +82,7 @@ function useAgentChat(options) {
84
82
  let data;
85
83
  try {
86
84
  data = JSON.parse(event.data);
87
- } catch (error) {
85
+ } catch (_error) {
88
86
  return;
89
87
  }
90
88
  if (data.type === "cf_agent_use_chat_response") {
@@ -107,32 +105,32 @@ function useAgentChat(options) {
107
105
  });
108
106
  agent.send(
109
107
  JSON.stringify({
110
- type: "cf_agent_use_chat_request",
111
108
  id,
112
- url: request.toString(),
113
109
  init: {
114
- method,
115
- keepalive,
116
- headers,
117
110
  body,
118
- redirect,
119
- integrity,
120
111
  credentials,
112
+ headers,
113
+ integrity,
114
+ keepalive,
115
+ method,
121
116
  mode,
117
+ redirect,
122
118
  referrer,
123
119
  referrerPolicy,
124
120
  window
125
121
  // dispatcher,
126
122
  // duplex
127
- }
123
+ },
124
+ type: "cf_agent_use_chat_request",
125
+ url: request.toString()
128
126
  })
129
127
  );
130
128
  return new Response(stream);
131
129
  }
132
130
  const useChatHelpers = useChat({
131
+ fetch: aiFetch,
133
132
  initialMessages,
134
133
  sendExtraMessageFields: true,
135
- fetch: aiFetch,
136
134
  ...rest
137
135
  });
138
136
  useEffect(() => {
@@ -143,7 +141,7 @@ function useAgentChat(options) {
143
141
  let data;
144
142
  try {
145
143
  data = JSON.parse(event.data);
146
- } catch (error) {
144
+ } catch (_error) {
147
145
  return;
148
146
  }
149
147
  if (data.type === "cf_agent_chat_clear") {
@@ -157,7 +155,7 @@ function useAgentChat(options) {
157
155
  let data;
158
156
  try {
159
157
  data = JSON.parse(event.data);
160
- } catch (error) {
158
+ } catch (_error) {
161
159
  return;
162
160
  }
163
161
  if (data.type === "cf_agent_chat_messages") {
@@ -174,26 +172,26 @@ function useAgentChat(options) {
174
172
  return {
175
173
  ...useChatHelpers,
176
174
  /**
177
- * Set the chat messages and synchronize with the Agent
178
- * @param messages New messages to set
175
+ * Clear chat history on both client and Agent
179
176
  */
180
- setMessages: (messages) => {
181
- useChatHelpers.setMessages(messages);
177
+ clearHistory: () => {
178
+ useChatHelpers.setMessages([]);
182
179
  agent.send(
183
180
  JSON.stringify({
184
- type: "cf_agent_chat_messages",
185
- messages
181
+ type: "cf_agent_chat_clear"
186
182
  })
187
183
  );
188
184
  },
189
185
  /**
190
- * Clear chat history on both client and Agent
186
+ * Set the chat messages and synchronize with the Agent
187
+ * @param messages New messages to set
191
188
  */
192
- clearHistory: () => {
193
- useChatHelpers.setMessages([]);
189
+ setMessages: (messages) => {
190
+ useChatHelpers.setMessages(messages);
194
191
  agent.send(
195
192
  JSON.stringify({
196
- type: "cf_agent_chat_clear"
193
+ messages,
194
+ type: "cf_agent_chat_messages"
197
195
  })
198
196
  );
199
197
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ai-react.tsx"],"sourcesContent":["import { useChat } from \"@ai-sdk/react\";\nimport type { Message } from \"ai\";\nimport { use, useEffect } from \"react\";\nimport type { OutgoingMessage } from \"./ai-types\";\nimport type { useAgent } from \"./react\";\nimport { nanoid } from \"nanoid\";\n\ntype GetInitialMessagesOptions = {\n agent: string;\n name: string;\n url: string;\n};\n\n/**\n * Options for the useAgentChat hook\n */\ntype UseAgentChatOptions<State> = Omit<\n Parameters<typeof useChat>[0] & {\n /** Agent connection from useAgent */\n agent: ReturnType<typeof useAgent<State>>;\n getInitialMessages?:\n | undefined\n | null\n // | (() => Message[])\n | ((options: GetInitialMessagesOptions) => Promise<Message[]>);\n },\n \"fetch\"\n>;\n\nconst requestCache = new Map<string, Promise<Message[]>>();\n\n/**\n * React hook for building AI chat interfaces using an Agent\n * @param options Chat options including the agent connection\n * @returns Chat interface controls and state with added clearHistory method\n */\nexport function useAgentChat<State = unknown>(\n options: UseAgentChatOptions<State>\n) {\n const { agent, getInitialMessages, ...rest } = options;\n\n const agentUrl = new URL(\n `${// @ts-expect-error we're using a protected _url property that includes query params\n ((agent._url as string | null) || agent._pkurl)\n ?.replace(\"ws://\", \"http://\")\n .replace(\"wss://\", \"https://\")}`\n );\n\n // delete the _pk query param\n agentUrl.searchParams.delete(\"_pk\");\n const agentUrlString = agentUrl.toString();\n\n async function defaultGetInitialMessagesFetch({\n url,\n }: GetInitialMessagesOptions) {\n const getMessagesUrl = new URL(url);\n getMessagesUrl.pathname += \"/get-messages\";\n const response = await fetch(getMessagesUrl.toString(), {\n headers: options.headers,\n credentials: options.credentials,\n });\n return response.json<Message[]>();\n }\n\n const getInitialMessagesFetch =\n getInitialMessages || defaultGetInitialMessagesFetch;\n\n function doGetInitialMessages(\n getInitialMessagesOptions: GetInitialMessagesOptions\n ) {\n if (requestCache.has(agentUrlString)) {\n return requestCache.get(agentUrlString)!;\n }\n const promise = getInitialMessagesFetch(getInitialMessagesOptions);\n // immediately cache the promise so that we don't\n // create multiple requests for the same agent during multiple\n // concurrent renders\n requestCache.set(agentUrlString, promise);\n return promise;\n }\n\n const initialMessagesPromise =\n getInitialMessages === null\n ? null\n : doGetInitialMessages({\n agent: agent.agent,\n name: agent.name,\n url: agentUrlString,\n });\n const initialMessages = initialMessagesPromise\n ? use(initialMessagesPromise)\n : (rest.initialMessages ?? []);\n\n // manages adding and removing the promise from the cache\n useEffect(() => {\n if (!initialMessagesPromise) {\n return;\n }\n // this effect is responsible for removing the promise from the cache\n // when the component unmounts or the promise changes,\n // but that means it also must add the promise to the cache\n // so that multiple arbitrary effect runs produce the expected state\n // when resolved.\n requestCache.set(agentUrlString, initialMessagesPromise!);\n return () => {\n if (requestCache.get(agentUrlString) === initialMessagesPromise) {\n requestCache.delete(agentUrlString);\n }\n };\n }, [agentUrlString, initialMessagesPromise]);\n\n async function aiFetch(\n request: RequestInfo | URL,\n options: RequestInit = {}\n ) {\n // we're going to use a websocket to do the actual \"fetching\"\n // but still satisfy the type signature of the fetch function\n // so we'll return a promise that resolves to a response\n\n const {\n method,\n keepalive,\n headers,\n body,\n redirect,\n integrity,\n signal,\n credentials,\n mode,\n referrer,\n referrerPolicy,\n window,\n // dispatcher, duplex\n } = options;\n const id = nanoid(8);\n const abortController = new AbortController();\n\n signal?.addEventListener(\"abort\", () => {\n // Propagate request cancellation to the Agent\n // We need to communciate cancellation as a websocket message, instead of a request signal\n agent.send(\n JSON.stringify({\n type: \"cf_agent_chat_request_cancel\",\n id,\n })\n );\n\n // NOTE - If we wanted to, we could preserve the \"interrupted\" message here, with the code below\n // However, I think it might be the responsibility of the library user to implement that behavior manually?\n // Reasoning: This code could be subject to collisions, as it \"force saves\" the messages we have locally\n //\n // agent.send(JSON.stringify({\n // type: \"cf_agent_chat_messages\",\n // messages: ... /* some way of getting current messages ref? */\n // }))\n\n abortController.abort();\n // Make sure to also close the stream (cf. https://github.com/cloudflare/agents-starter/issues/69)\n controller.close();\n });\n\n agent.addEventListener(\n \"message\",\n (event) => {\n let data: OutgoingMessage;\n try {\n data = JSON.parse(event.data) as OutgoingMessage;\n } catch (error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (data.type === \"cf_agent_use_chat_response\") {\n if (data.id === id) {\n controller.enqueue(new TextEncoder().encode(data.body));\n if (data.done) {\n controller.close();\n abortController.abort();\n }\n }\n }\n },\n { signal: abortController.signal }\n );\n\n let controller: ReadableStreamDefaultController;\n\n const stream = new ReadableStream({\n start(c) {\n controller = c;\n },\n });\n\n agent.send(\n JSON.stringify({\n type: \"cf_agent_use_chat_request\",\n id,\n url: request.toString(),\n init: {\n method,\n keepalive,\n headers,\n body,\n redirect,\n integrity,\n credentials,\n mode,\n referrer,\n referrerPolicy,\n window,\n // dispatcher,\n // duplex\n },\n })\n );\n\n return new Response(stream);\n }\n const useChatHelpers = useChat({\n initialMessages,\n sendExtraMessageFields: true,\n fetch: aiFetch,\n ...rest,\n });\n\n useEffect(() => {\n function onClearHistory(event: MessageEvent) {\n if (typeof event.data !== \"string\") {\n return;\n }\n let data: OutgoingMessage;\n try {\n data = JSON.parse(event.data) as OutgoingMessage;\n } catch (error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (data.type === \"cf_agent_chat_clear\") {\n useChatHelpers.setMessages([]);\n }\n }\n\n function onMessages(event: MessageEvent) {\n if (typeof event.data !== \"string\") {\n return;\n }\n let data: OutgoingMessage;\n try {\n data = JSON.parse(event.data) as OutgoingMessage;\n } catch (error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (data.type === \"cf_agent_chat_messages\") {\n useChatHelpers.setMessages(data.messages);\n }\n }\n\n agent.addEventListener(\"message\", onClearHistory);\n agent.addEventListener(\"message\", onMessages);\n\n return () => {\n agent.removeEventListener(\"message\", onClearHistory);\n agent.removeEventListener(\"message\", onMessages);\n };\n }, [agent, useChatHelpers.setMessages]);\n\n return {\n ...useChatHelpers,\n /**\n * Set the chat messages and synchronize with the Agent\n * @param messages New messages to set\n */\n setMessages: (messages: Message[]) => {\n useChatHelpers.setMessages(messages);\n agent.send(\n JSON.stringify({\n type: \"cf_agent_chat_messages\",\n messages,\n })\n );\n },\n /**\n * Clear chat history on both client and Agent\n */\n clearHistory: () => {\n useChatHelpers.setMessages([]);\n agent.send(\n JSON.stringify({\n type: \"cf_agent_chat_clear\",\n })\n );\n },\n };\n}\n"],"mappings":";;;AAAA,SAAS,eAAe;AAExB,SAAS,KAAK,iBAAiB;AAG/B,SAAS,cAAc;AAwBvB,IAAM,eAAe,oBAAI,IAAgC;AAOlD,SAAS,aACd,SACA;AACA,QAAM,EAAE,OAAO,oBAAoB,GAAG,KAAK,IAAI;AAE/C,QAAM,WAAW,IAAI;AAAA,IACnB;AAAA,KACE,MAAM,QAA0B,MAAM,SACpC,QAAQ,SAAS,SAAS,EAC3B,QAAQ,UAAU,UAAU,CAAC;AAAA,EAClC;AAGA,WAAS,aAAa,OAAO,KAAK;AAClC,QAAM,iBAAiB,SAAS,SAAS;AAEzC,iBAAe,+BAA+B;AAAA,IAC5C;AAAA,EACF,GAA8B;AAC5B,UAAM,iBAAiB,IAAI,IAAI,GAAG;AAClC,mBAAe,YAAY;AAC3B,UAAM,WAAW,MAAM,MAAM,eAAe,SAAS,GAAG;AAAA,MACtD,SAAS,QAAQ;AAAA,MACjB,aAAa,QAAQ;AAAA,IACvB,CAAC;AACD,WAAO,SAAS,KAAgB;AAAA,EAClC;AAEA,QAAM,0BACJ,sBAAsB;AAExB,WAAS,qBACP,2BACA;AACA,QAAI,aAAa,IAAI,cAAc,GAAG;AACpC,aAAO,aAAa,IAAI,cAAc;AAAA,IACxC;AACA,UAAM,UAAU,wBAAwB,yBAAyB;AAIjE,iBAAa,IAAI,gBAAgB,OAAO;AACxC,WAAO;AAAA,EACT;AAEA,QAAM,yBACJ,uBAAuB,OACnB,OACA,qBAAqB;AAAA,IACnB,OAAO,MAAM;AAAA,IACb,MAAM,MAAM;AAAA,IACZ,KAAK;AAAA,EACP,CAAC;AACP,QAAM,kBAAkB,yBACpB,IAAI,sBAAsB,IACzB,KAAK,mBAAmB,CAAC;AAG9B,YAAU,MAAM;AACd,QAAI,CAAC,wBAAwB;AAC3B;AAAA,IACF;AAMA,iBAAa,IAAI,gBAAgB,sBAAuB;AACxD,WAAO,MAAM;AACX,UAAI,aAAa,IAAI,cAAc,MAAM,wBAAwB;AAC/D,qBAAa,OAAO,cAAc;AAAA,MACpC;AAAA,IACF;AAAA,EACF,GAAG,CAAC,gBAAgB,sBAAsB,CAAC;AAE3C,iBAAe,QACb,SACAA,WAAuB,CAAC,GACxB;AAKA,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,IAEF,IAAIA;AACJ,UAAM,KAAK,OAAO,CAAC;AACnB,UAAM,kBAAkB,IAAI,gBAAgB;AAE5C,YAAQ,iBAAiB,SAAS,MAAM;AAGtC,YAAM;AAAA,QACJ,KAAK,UAAU;AAAA,UACb,MAAM;AAAA,UACN;AAAA,QACF,CAAC;AAAA,MACH;AAWA,sBAAgB,MAAM;AAEtB,iBAAW,MAAM;AAAA,IACnB,CAAC;AAED,UAAM;AAAA,MACJ;AAAA,MACA,CAAC,UAAU;AACT,YAAI;AACJ,YAAI;AACF,iBAAO,KAAK,MAAM,MAAM,IAAI;AAAA,QAC9B,SAAS,OAAO;AAGd;AAAA,QACF;AACA,YAAI,KAAK,SAAS,8BAA8B;AAC9C,cAAI,KAAK,OAAO,IAAI;AAClB,uBAAW,QAAQ,IAAI,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;AACtD,gBAAI,KAAK,MAAM;AACb,yBAAW,MAAM;AACjB,8BAAgB,MAAM;AAAA,YACxB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,EAAE,QAAQ,gBAAgB,OAAO;AAAA,IACnC;AAEA,QAAI;AAEJ,UAAM,SAAS,IAAI,eAAe;AAAA,MAChC,MAAM,GAAG;AACP,qBAAa;AAAA,MACf;AAAA,IACF,CAAC;AAED,UAAM;AAAA,MACJ,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,QACN;AAAA,QACA,KAAK,QAAQ,SAAS;AAAA,QACtB,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA;AAAA,QAGF;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO,IAAI,SAAS,MAAM;AAAA,EAC5B;AACA,QAAM,iBAAiB,QAAQ;AAAA,IAC7B;AAAA,IACA,wBAAwB;AAAA,IACxB,OAAO;AAAA,IACP,GAAG;AAAA,EACL,CAAC;AAED,YAAU,MAAM;AACd,aAAS,eAAe,OAAqB;AAC3C,UAAI,OAAO,MAAM,SAAS,UAAU;AAClC;AAAA,MACF;AACA,UAAI;AACJ,UAAI;AACF,eAAO,KAAK,MAAM,MAAM,IAAI;AAAA,MAC9B,SAAS,OAAO;AAGd;AAAA,MACF;AACA,UAAI,KAAK,SAAS,uBAAuB;AACvC,uBAAe,YAAY,CAAC,CAAC;AAAA,MAC/B;AAAA,IACF;AAEA,aAAS,WAAW,OAAqB;AACvC,UAAI,OAAO,MAAM,SAAS,UAAU;AAClC;AAAA,MACF;AACA,UAAI;AACJ,UAAI;AACF,eAAO,KAAK,MAAM,MAAM,IAAI;AAAA,MAC9B,SAAS,OAAO;AAGd;AAAA,MACF;AACA,UAAI,KAAK,SAAS,0BAA0B;AAC1C,uBAAe,YAAY,KAAK,QAAQ;AAAA,MAC1C;AAAA,IACF;AAEA,UAAM,iBAAiB,WAAW,cAAc;AAChD,UAAM,iBAAiB,WAAW,UAAU;AAE5C,WAAO,MAAM;AACX,YAAM,oBAAoB,WAAW,cAAc;AACnD,YAAM,oBAAoB,WAAW,UAAU;AAAA,IACjD;AAAA,EACF,GAAG,CAAC,OAAO,eAAe,WAAW,CAAC;AAEtC,SAAO;AAAA,IACL,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,IAKH,aAAa,CAAC,aAAwB;AACpC,qBAAe,YAAY,QAAQ;AACnC,YAAM;AAAA,QACJ,KAAK,UAAU;AAAA,UACb,MAAM;AAAA,UACN;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA;AAAA;AAAA;AAAA,IAIA,cAAc,MAAM;AAClB,qBAAe,YAAY,CAAC,CAAC;AAC7B,YAAM;AAAA,QACJ,KAAK,UAAU;AAAA,UACb,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;","names":["options"]}
1
+ {"version":3,"sources":["../src/ai-react.tsx"],"sourcesContent":["import { useChat } from \"@ai-sdk/react\";\nimport type { Message } from \"ai\";\nimport { nanoid } from \"nanoid\";\nimport { use, useEffect } from \"react\";\nimport type { OutgoingMessage } from \"./ai-types\";\nimport type { useAgent } from \"./react\";\n\ntype GetInitialMessagesOptions = {\n agent: string;\n name: string;\n url: string;\n};\n\n/**\n * Options for the useAgentChat hook\n */\ntype UseAgentChatOptions<State> = Omit<\n Parameters<typeof useChat>[0] & {\n /** Agent connection from useAgent */\n agent: ReturnType<typeof useAgent<State>>;\n getInitialMessages?:\n | undefined\n | null\n // | (() => Message[])\n | ((options: GetInitialMessagesOptions) => Promise<Message[]>);\n },\n \"fetch\"\n>;\n\nconst requestCache = new Map<string, Promise<Message[]>>();\n\n/**\n * React hook for building AI chat interfaces using an Agent\n * @param options Chat options including the agent connection\n * @returns Chat interface controls and state with added clearHistory method\n */\nexport function useAgentChat<State = unknown>(\n options: UseAgentChatOptions<State>\n) {\n const { agent, getInitialMessages, ...rest } = options;\n\n const agentUrl = new URL(\n `${// @ts-expect-error we're using a protected _url property that includes query params\n ((agent._url as string | null) || agent._pkurl)\n ?.replace(\"ws://\", \"http://\")\n .replace(\"wss://\", \"https://\")}`\n );\n\n // delete the _pk query param\n agentUrl.searchParams.delete(\"_pk\");\n const agentUrlString = agentUrl.toString();\n\n async function defaultGetInitialMessagesFetch({\n url,\n }: GetInitialMessagesOptions) {\n const getMessagesUrl = new URL(url);\n getMessagesUrl.pathname += \"/get-messages\";\n const response = await fetch(getMessagesUrl.toString(), {\n credentials: options.credentials,\n headers: options.headers,\n });\n return response.json<Message[]>();\n }\n\n const getInitialMessagesFetch =\n getInitialMessages || defaultGetInitialMessagesFetch;\n\n function doGetInitialMessages(\n getInitialMessagesOptions: GetInitialMessagesOptions\n ) {\n if (requestCache.has(agentUrlString)) {\n return requestCache.get(agentUrlString)!;\n }\n const promise = getInitialMessagesFetch(getInitialMessagesOptions);\n // immediately cache the promise so that we don't\n // create multiple requests for the same agent during multiple\n // concurrent renders\n requestCache.set(agentUrlString, promise);\n return promise;\n }\n\n const initialMessagesPromise =\n getInitialMessages === null\n ? null\n : doGetInitialMessages({\n agent: agent.agent,\n name: agent.name,\n url: agentUrlString,\n });\n const initialMessages = initialMessagesPromise\n ? use(initialMessagesPromise)\n : (rest.initialMessages ?? []);\n\n // manages adding and removing the promise from the cache\n useEffect(() => {\n if (!initialMessagesPromise) {\n return;\n }\n // this effect is responsible for removing the promise from the cache\n // when the component unmounts or the promise changes,\n // but that means it also must add the promise to the cache\n // so that multiple arbitrary effect runs produce the expected state\n // when resolved.\n requestCache.set(agentUrlString, initialMessagesPromise!);\n return () => {\n if (requestCache.get(agentUrlString) === initialMessagesPromise) {\n requestCache.delete(agentUrlString);\n }\n };\n }, [agentUrlString, initialMessagesPromise]);\n\n async function aiFetch(\n request: RequestInfo | URL,\n options: RequestInit = {}\n ) {\n // we're going to use a websocket to do the actual \"fetching\"\n // but still satisfy the type signature of the fetch function\n // so we'll return a promise that resolves to a response\n\n const {\n method,\n keepalive,\n headers,\n body,\n redirect,\n integrity,\n signal,\n credentials,\n mode,\n referrer,\n referrerPolicy,\n window,\n // dispatcher, duplex\n } = options;\n const id = nanoid(8);\n const abortController = new AbortController();\n\n signal?.addEventListener(\"abort\", () => {\n // Propagate request cancellation to the Agent\n // We need to communciate cancellation as a websocket message, instead of a request signal\n agent.send(\n JSON.stringify({\n id,\n type: \"cf_agent_chat_request_cancel\",\n })\n );\n\n // NOTE - If we wanted to, we could preserve the \"interrupted\" message here, with the code below\n // However, I think it might be the responsibility of the library user to implement that behavior manually?\n // Reasoning: This code could be subject to collisions, as it \"force saves\" the messages we have locally\n //\n // agent.send(JSON.stringify({\n // type: \"cf_agent_chat_messages\",\n // messages: ... /* some way of getting current messages ref? */\n // }))\n\n abortController.abort();\n // Make sure to also close the stream (cf. https://github.com/cloudflare/agents-starter/issues/69)\n controller.close();\n });\n\n agent.addEventListener(\n \"message\",\n (event) => {\n let data: OutgoingMessage;\n try {\n data = JSON.parse(event.data) as OutgoingMessage;\n } catch (_error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (data.type === \"cf_agent_use_chat_response\") {\n if (data.id === id) {\n controller.enqueue(new TextEncoder().encode(data.body));\n if (data.done) {\n controller.close();\n abortController.abort();\n }\n }\n }\n },\n { signal: abortController.signal }\n );\n\n let controller: ReadableStreamDefaultController;\n\n const stream = new ReadableStream({\n start(c) {\n controller = c;\n },\n });\n\n agent.send(\n JSON.stringify({\n id,\n init: {\n body,\n credentials,\n headers,\n integrity,\n keepalive,\n method,\n mode,\n redirect,\n referrer,\n referrerPolicy,\n window,\n // dispatcher,\n // duplex\n },\n type: \"cf_agent_use_chat_request\",\n url: request.toString(),\n })\n );\n\n return new Response(stream);\n }\n const useChatHelpers = useChat({\n fetch: aiFetch,\n initialMessages,\n sendExtraMessageFields: true,\n ...rest,\n });\n\n useEffect(() => {\n function onClearHistory(event: MessageEvent) {\n if (typeof event.data !== \"string\") {\n return;\n }\n let data: OutgoingMessage;\n try {\n data = JSON.parse(event.data) as OutgoingMessage;\n } catch (_error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (data.type === \"cf_agent_chat_clear\") {\n useChatHelpers.setMessages([]);\n }\n }\n\n function onMessages(event: MessageEvent) {\n if (typeof event.data !== \"string\") {\n return;\n }\n let data: OutgoingMessage;\n try {\n data = JSON.parse(event.data) as OutgoingMessage;\n } catch (_error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (data.type === \"cf_agent_chat_messages\") {\n useChatHelpers.setMessages(data.messages);\n }\n }\n\n agent.addEventListener(\"message\", onClearHistory);\n agent.addEventListener(\"message\", onMessages);\n\n return () => {\n agent.removeEventListener(\"message\", onClearHistory);\n agent.removeEventListener(\"message\", onMessages);\n };\n }, [agent, useChatHelpers.setMessages]);\n\n return {\n ...useChatHelpers,\n /**\n * Clear chat history on both client and Agent\n */\n clearHistory: () => {\n useChatHelpers.setMessages([]);\n agent.send(\n JSON.stringify({\n type: \"cf_agent_chat_clear\",\n })\n );\n },\n /**\n * Set the chat messages and synchronize with the Agent\n * @param messages New messages to set\n */\n setMessages: (messages: Message[]) => {\n useChatHelpers.setMessages(messages);\n agent.send(\n JSON.stringify({\n messages,\n type: \"cf_agent_chat_messages\",\n })\n );\n },\n };\n}\n"],"mappings":"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","names":["options"]}