agents 0.0.0-5460fd0 → 0.0.0-569680f

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.
@@ -1,5 +1,5 @@
1
1
  import { Message, StreamTextOnFinishCallback, ToolSet } from "ai";
2
- import { Agent, AgentContext } from "./index.js";
2
+ import { A as Agent, a as AgentContext } from "./index-aBwVVXj7.js";
3
3
  import { Connection, WSMessage } from "partyserver";
4
4
  import "@modelcontextprotocol/sdk/client/index.js";
5
5
  import "@modelcontextprotocol/sdk/types.js";
@@ -52,6 +52,7 @@ declare class AIChatAgent<Env = unknown, State = unknown> extends Agent<
52
52
  messages: Message[],
53
53
  excludeBroadcastIds?: string[]
54
54
  ): Promise<void>;
55
+ private _messagesNotAlreadyInAgent;
55
56
  private _reply;
56
57
  /**
57
58
  * For the given message id, look up its associated AbortController
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Agent
3
- } from "./chunk-CGWTDCBQ.js";
4
- import "./chunk-E3LCYPCB.js";
3
+ } from "./chunk-RX76B6DL.js";
4
+ import "./chunk-TN7QOY4S.js";
5
5
  import "./chunk-767EASBA.js";
6
6
  import "./chunk-NKZZ66QY.js";
7
7
 
@@ -52,7 +52,20 @@ var AIChatAgent = class extends Agent {
52
52
  },
53
53
  [connection.id]
54
54
  );
55
+ const incomingMessages = this._messagesNotAlreadyInAgent(messages);
55
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
+ );
56
69
  const chatMessageId = data.id;
57
70
  const abortSignal = this._getAbortSignal(chatMessageId);
58
71
  return this._tryCatchChat(async () => {
@@ -62,8 +75,21 @@ var AIChatAgent = class extends Agent {
62
75
  messages,
63
76
  responseMessages: response2.messages
64
77
  });
78
+ const outgoingMessages = this._messagesNotAlreadyInAgent(finalMessages);
65
79
  await this.persistMessages(finalMessages, [connection.id]);
66
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
+ );
67
93
  },
68
94
  abortSignal ? { abortSignal } : void 0
69
95
  );
@@ -166,6 +192,10 @@ var AIChatAgent = class extends Agent {
166
192
  excludeBroadcastIds
167
193
  );
168
194
  }
195
+ _messagesNotAlreadyInAgent(messages) {
196
+ const existingIds = new Set(this.messages.map((message) => message.id));
197
+ return messages.filter((message) => !existingIds.has(message.id));
198
+ }
169
199
  async _reply(id, response) {
170
200
  return this._tryCatchChat(async () => {
171
201
  for await (const chunk of response.body) {
@@ -1 +1 @@
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 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 } 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 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"]}
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,7 +4,7 @@ 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-aBwVVXj7.js";
8
8
  import "@modelcontextprotocol/sdk/client/index.js";
9
9
  import "@modelcontextprotocol/sdk/types.js";
10
10
  import "partyserver";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MCPClientManager
3
- } from "./chunk-E3LCYPCB.js";
3
+ } from "./chunk-TN7QOY4S.js";
4
4
  import {
5
5
  DurableObjectOAuthClientProvider
6
6
  } from "./chunk-767EASBA.js";
@@ -12,10 +12,11 @@ import {
12
12
  import { AsyncLocalStorage } from "async_hooks";
13
13
  import { parseCronExpression } from "cron-schedule";
14
14
  import { nanoid } from "nanoid";
15
+ import { EmailMessage } from "cloudflare:email";
15
16
  import {
17
+ Server,
16
18
  getServerByName,
17
- routePartykitRequest,
18
- Server
19
+ routePartykitRequest
19
20
  } from "partyserver";
20
21
  function isRPCRequest(msg) {
21
22
  return typeof msg === "object" && msg !== null && "type" in msg && msg.type === "rpc" && "id" in msg && typeof msg.id === "string" && "method" in msg && typeof msg.method === "string" && "args" in msg && Array.isArray(msg.args);
@@ -62,6 +63,10 @@ var Agent = class extends Server {
62
63
  * Override to provide default state values
63
64
  */
64
65
  this.initialState = DEFAULT_STATE;
66
+ /**
67
+ * The observability implementation to use for the Agent
68
+ */
69
+ this.observability = genericObservability;
65
70
  /**
66
71
  * Method called when an alarm fires.
67
72
  * Executes any scheduled tasks that are due.
@@ -85,6 +90,16 @@ var Agent = class extends Server {
85
90
  { agent: this, connection: void 0, request: void 0 },
86
91
  async () => {
87
92
  try {
93
+ this.observability?.emit(
94
+ {
95
+ displayMessage: `Schedule ${row.id} executed`,
96
+ id: nanoid(),
97
+ payload: row,
98
+ timestamp: Date.now(),
99
+ type: "schedule:execute"
100
+ },
101
+ this.ctx
102
+ );
88
103
  await callback.bind(this)(JSON.parse(row.payload), row);
89
104
  } catch (e) {
90
105
  console.error(`error executing callback "${row.callback}"`, e);
@@ -196,6 +211,21 @@ var Agent = class extends Server {
196
211
  return;
197
212
  }
198
213
  const result = await methodFn.apply(this, args);
214
+ this.observability?.emit(
215
+ {
216
+ displayMessage: `RPC call to ${method}`,
217
+ id: nanoid(),
218
+ payload: {
219
+ args,
220
+ method,
221
+ streaming: metadata?.streaming,
222
+ success: true
223
+ },
224
+ timestamp: Date.now(),
225
+ type: "rpc"
226
+ },
227
+ this.ctx
228
+ );
199
229
  const response = {
200
230
  done: true,
201
231
  id,
@@ -240,6 +270,18 @@ var Agent = class extends Server {
240
270
  type: "cf_agent_mcp_servers"
241
271
  })
242
272
  );
273
+ this.observability?.emit(
274
+ {
275
+ displayMessage: "Connection established",
276
+ id: nanoid(),
277
+ payload: {
278
+ connectionId: connection.id
279
+ },
280
+ timestamp: Date.now(),
281
+ type: "connect"
282
+ },
283
+ this.ctx
284
+ );
243
285
  return this._tryCatch(() => _onConnect(connection, ctx2));
244
286
  }, 20);
245
287
  }
@@ -269,8 +311,8 @@ var Agent = class extends Server {
269
311
  ).then((_results) => {
270
312
  this.broadcast(
271
313
  JSON.stringify({
272
- type: "cf_agent_mcp_servers",
273
- mcp: this.getMcpServers()
314
+ mcp: this.getMcpServers(),
315
+ type: "cf_agent_mcp_servers"
274
316
  })
275
317
  );
276
318
  });
@@ -325,6 +367,7 @@ var Agent = class extends Server {
325
367
  }
326
368
  }
327
369
  _setStateInternal(state, source = "server") {
370
+ const previousState = this._state;
328
371
  this._state = state;
329
372
  this.sql`
330
373
  INSERT OR REPLACE INTO cf_agents_state (id, state)
@@ -346,6 +389,19 @@ var Agent = class extends Server {
346
389
  return agentContext.run(
347
390
  { agent: this, connection, request },
348
391
  async () => {
392
+ this.observability?.emit(
393
+ {
394
+ displayMessage: "State updated",
395
+ id: nanoid(),
396
+ payload: {
397
+ previousState,
398
+ state
399
+ },
400
+ timestamp: Date.now(),
401
+ type: "state:update"
402
+ },
403
+ this.ctx
404
+ );
349
405
  return this.onStateUpdate(state, source);
350
406
  }
351
407
  );
@@ -368,17 +424,29 @@ var Agent = class extends Server {
368
424
  }
369
425
  /**
370
426
  * Called when the Agent receives an email
427
+ * Override this method to handle incoming emails
371
428
  * @param email Email message to process
372
429
  */
373
- // biome-ignore lint/correctness/noUnusedFunctionParameters: overridden later
374
- onEmail(email) {
430
+ async onEmail(email) {
375
431
  return agentContext.run(
376
432
  { agent: this, connection: void 0, request: void 0 },
377
433
  async () => {
378
- console.error("onEmail not implemented");
434
+ console.log("Received email from:", email.from, "to:", email.to);
435
+ console.log("Subject:", email.headers.get("subject"));
436
+ console.log("Override onEmail() in your agent to process emails");
379
437
  }
380
438
  );
381
439
  }
440
+ async sendEmail(emailBinding, from, fromName, options) {
441
+ const agentName = camelCaseToKebabCase(this._ParentClass.name);
442
+ const agentId = this.name;
443
+ return sendEmailWithRouting(emailBinding, from, fromName, {
444
+ ...options,
445
+ agentName,
446
+ agentId,
447
+ includeRoutingHeaders: true
448
+ });
449
+ }
382
450
  async _tryCatch(fn) {
383
451
  try {
384
452
  return await fn();
@@ -421,6 +489,16 @@ var Agent = class extends Server {
421
489
  */
422
490
  async schedule(when, callback, payload) {
423
491
  const id = nanoid(9);
492
+ const emitScheduleCreate = (schedule) => this.observability?.emit(
493
+ {
494
+ displayMessage: `Schedule ${schedule.id} created`,
495
+ id: nanoid(),
496
+ payload: schedule,
497
+ timestamp: Date.now(),
498
+ type: "schedule:create"
499
+ },
500
+ this.ctx
501
+ );
424
502
  if (typeof callback !== "string") {
425
503
  throw new Error("Callback must be a string");
426
504
  }
@@ -436,13 +514,15 @@ var Agent = class extends Server {
436
514
  )}, 'scheduled', ${timestamp})
437
515
  `;
438
516
  await this._scheduleNextAlarm();
439
- return {
517
+ const schedule = {
440
518
  callback,
441
519
  id,
442
520
  payload,
443
521
  time: timestamp,
444
522
  type: "scheduled"
445
523
  };
524
+ emitScheduleCreate(schedule);
525
+ return schedule;
446
526
  }
447
527
  if (typeof when === "number") {
448
528
  const time = new Date(Date.now() + when * 1e3);
@@ -454,7 +534,7 @@ var Agent = class extends Server {
454
534
  )}, 'delayed', ${when}, ${timestamp})
455
535
  `;
456
536
  await this._scheduleNextAlarm();
457
- return {
537
+ const schedule = {
458
538
  callback,
459
539
  delayInSeconds: when,
460
540
  id,
@@ -462,6 +542,8 @@ var Agent = class extends Server {
462
542
  time: timestamp,
463
543
  type: "delayed"
464
544
  };
545
+ emitScheduleCreate(schedule);
546
+ return schedule;
465
547
  }
466
548
  if (typeof when === "string") {
467
549
  const nextExecutionTime = getNextCronTime(when);
@@ -473,7 +555,7 @@ var Agent = class extends Server {
473
555
  )}, 'cron', ${when}, ${timestamp})
474
556
  `;
475
557
  await this._scheduleNextAlarm();
476
- return {
558
+ const schedule = {
477
559
  callback,
478
560
  cron: when,
479
561
  id,
@@ -481,6 +563,8 @@ var Agent = class extends Server {
481
563
  time: timestamp,
482
564
  type: "cron"
483
565
  };
566
+ emitScheduleCreate(schedule);
567
+ return schedule;
484
568
  }
485
569
  throw new Error("Invalid schedule type");
486
570
  }
@@ -538,6 +622,19 @@ var Agent = class extends Server {
538
622
  * @returns true if the task was cancelled, false otherwise
539
623
  */
540
624
  async cancelSchedule(id) {
625
+ const schedule = await this.getSchedule(id);
626
+ if (schedule) {
627
+ this.observability?.emit(
628
+ {
629
+ displayMessage: `Schedule ${id} cancelled`,
630
+ id: nanoid(),
631
+ payload: schedule,
632
+ timestamp: Date.now(),
633
+ type: "schedule:cancel"
634
+ },
635
+ this.ctx
636
+ );
637
+ }
541
638
  this.sql`DELETE FROM cf_agents_schedules WHERE id = ${id}`;
542
639
  await this._scheduleNextAlarm();
543
640
  return true;
@@ -565,6 +662,16 @@ var Agent = class extends Server {
565
662
  await this.ctx.storage.deleteAlarm();
566
663
  await this.ctx.storage.deleteAll();
567
664
  this.ctx.abort("destroyed");
665
+ this.observability?.emit(
666
+ {
667
+ displayMessage: "Agent destroyed",
668
+ id: nanoid(),
669
+ payload: {},
670
+ timestamp: Date.now(),
671
+ type: "destroy"
672
+ },
673
+ this.ctx
674
+ );
568
675
  }
569
676
  /**
570
677
  * Get all methods marked as callable on this Agent
@@ -731,7 +838,105 @@ async function routeAgentRequest(request, env, options) {
731
838
  }
732
839
  return response;
733
840
  }
734
- async function routeAgentEmail(_email, _env, _options) {
841
+ function createHeaderBasedResolver() {
842
+ return async (email, _env) => {
843
+ const messageId = email.headers.get("message-id");
844
+ if (messageId) {
845
+ const messageIdMatch = messageId.match(/<([^@]+)@([^>]+)>/);
846
+ if (messageIdMatch) {
847
+ const [, agentId2, domain] = messageIdMatch;
848
+ const agentName2 = domain.split(".")[0];
849
+ return { agentName: agentName2, agentId: agentId2 };
850
+ }
851
+ }
852
+ const references = email.headers.get("references");
853
+ if (references) {
854
+ const referencesMatch = references.match(
855
+ /<([A-Za-z0-9+/]{43}=)@([^>]+)>/
856
+ );
857
+ if (referencesMatch) {
858
+ const [, base64Id, domain] = referencesMatch;
859
+ const agentId2 = Buffer.from(base64Id, "base64").toString("hex");
860
+ const agentName2 = domain.split(".")[0];
861
+ return { agentName: agentName2, agentId: agentId2 };
862
+ }
863
+ }
864
+ const agentName = email.headers.get("x-agent-name");
865
+ const agentId = email.headers.get("x-agent-id");
866
+ if (agentName && agentId) {
867
+ return { agentName, agentId };
868
+ }
869
+ return null;
870
+ };
871
+ }
872
+ function createAddressBasedResolver(defaultAgentName) {
873
+ return async (email, _env) => {
874
+ const emailMatch = email.to.match(/^([^+@]+)(?:\+([^@]+))?@(.+)$/);
875
+ if (!emailMatch) {
876
+ return null;
877
+ }
878
+ const [, localPart, subAddress] = emailMatch;
879
+ if (subAddress) {
880
+ return {
881
+ agentName: localPart,
882
+ agentId: subAddress
883
+ };
884
+ }
885
+ return {
886
+ agentName: defaultAgentName,
887
+ agentId: localPart
888
+ };
889
+ };
890
+ }
891
+ function createCatchAllResolver(agentName, agentId) {
892
+ return async () => ({ agentName, agentId });
893
+ }
894
+ async function routeAgentEmail(email, env, options) {
895
+ const routingInfo = await options.resolver(email, env);
896
+ if (!routingInfo) {
897
+ console.warn("No routing information found for email, dropping message");
898
+ return;
899
+ }
900
+ const namespaceBinding = env[routingInfo.agentName];
901
+ if (!namespaceBinding) {
902
+ console.error(
903
+ `Agent namespace '${routingInfo.agentName}' not found in environment`
904
+ );
905
+ return;
906
+ }
907
+ if (typeof namespaceBinding !== "object" || !("idFromName" in namespaceBinding) || typeof namespaceBinding.idFromName !== "function") {
908
+ console.error(
909
+ `Environment binding '${routingInfo.agentName}' is not an AgentNamespace (found: ${typeof namespaceBinding})`
910
+ );
911
+ return;
912
+ }
913
+ const namespace = namespaceBinding;
914
+ const agent = await getAgentByName(namespace, routingInfo.agentId);
915
+ await agent.onEmail(email);
916
+ }
917
+ async function sendEmailWithRouting(emailBinding, from, fromName, options) {
918
+ const { createMimeMessage } = await import("mimetext");
919
+ const msg = createMimeMessage();
920
+ msg.setSender({ addr: from, name: fromName });
921
+ msg.setRecipient(options.to);
922
+ msg.setSubject(options.subject);
923
+ msg.addMessage({
924
+ contentType: options.contentType || "text/plain",
925
+ data: options.body
926
+ });
927
+ if (options.includeRoutingHeaders && options.agentName && options.agentId) {
928
+ const domain = options.domain || from.split("@")[1];
929
+ const messageId = `<${options.agentId}@${domain}>`;
930
+ msg.setHeader("Message-ID", messageId);
931
+ msg.setHeader("X-Agent-Name", options.agentName);
932
+ msg.setHeader("X-Agent-ID", options.agentId);
933
+ }
934
+ if (options.headers) {
935
+ for (const [key, value] of Object.entries(options.headers)) {
936
+ msg.setHeader(key, value);
937
+ }
938
+ }
939
+ await emailBinding.send(new EmailMessage(from, options.to, msg.asRaw()));
735
940
  }
736
941
  async function getAgentByName(namespace, name, options) {
737
942
  return getServerByName(namespace, name, options);
@@ -779,13 +984,42 @@ var StreamingResponse = class {
779
984
  }
780
985
  };
781
986
 
987
+ // src/observability/index.ts
988
+ var genericObservability = {
989
+ emit(event) {
990
+ if (isLocalMode()) {
991
+ console.log(event.displayMessage);
992
+ return;
993
+ }
994
+ console.log(event);
995
+ }
996
+ };
997
+ var localMode = false;
998
+ function isLocalMode() {
999
+ if (localMode) {
1000
+ return true;
1001
+ }
1002
+ const { request } = getCurrentAgent();
1003
+ if (!request) {
1004
+ return false;
1005
+ }
1006
+ const url = new URL(request.url);
1007
+ localMode = url.hostname === "localhost";
1008
+ return localMode;
1009
+ }
1010
+
782
1011
  export {
1012
+ genericObservability,
783
1013
  unstable_callable,
784
1014
  getCurrentAgent,
785
1015
  Agent,
786
1016
  routeAgentRequest,
1017
+ createHeaderBasedResolver,
1018
+ createAddressBasedResolver,
1019
+ createCatchAllResolver,
787
1020
  routeAgentEmail,
1021
+ sendEmailWithRouting,
788
1022
  getAgentByName,
789
1023
  StreamingResponse
790
1024
  };
791
- //# sourceMappingURL=chunk-CGWTDCBQ.js.map
1025
+ //# sourceMappingURL=chunk-RX76B6DL.js.map