agents 0.0.0-8bf3250 → 0.0.0-8c2713f

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 (39) hide show
  1. package/README.md +128 -22
  2. package/dist/ai-chat-agent.d.ts +6 -3
  3. package/dist/ai-chat-agent.js +34 -4
  4. package/dist/ai-chat-agent.js.map +1 -1
  5. package/dist/ai-react.d.ts +6 -4
  6. package/dist/ai-react.js.map +1 -1
  7. package/dist/{chunk-E3LCYPCB.js → chunk-HY7ZLHJB.js} +146 -17
  8. package/dist/chunk-HY7ZLHJB.js.map +1 -0
  9. package/dist/{chunk-4CIGD73X.js → chunk-JXN5WZFQ.js} +570 -74
  10. package/dist/chunk-JXN5WZFQ.js.map +1 -0
  11. package/dist/{chunk-NKZZ66QY.js → chunk-KUH345EY.js} +1 -1
  12. package/dist/chunk-KUH345EY.js.map +1 -0
  13. package/dist/{chunk-767EASBA.js → chunk-PVQZBKN7.js} +1 -1
  14. package/dist/chunk-PVQZBKN7.js.map +1 -0
  15. package/dist/client-DgyzBU_8.d.ts +4601 -0
  16. package/dist/client.d.ts +2 -2
  17. package/dist/client.js +1 -1
  18. package/dist/index-BCJclX6q.d.ts +615 -0
  19. package/dist/index.d.ts +34 -405
  20. package/dist/index.js +10 -4
  21. package/dist/mcp/client.d.ts +9 -1053
  22. package/dist/mcp/client.js +1 -1
  23. package/dist/mcp/do-oauth-client-provider.js +1 -1
  24. package/dist/mcp/index.d.ts +35 -7
  25. package/dist/mcp/index.js +189 -17
  26. package/dist/mcp/index.js.map +1 -1
  27. package/dist/observability/index.d.ts +14 -0
  28. package/dist/observability/index.js +10 -0
  29. package/dist/observability/index.js.map +1 -0
  30. package/dist/react.d.ts +8 -6
  31. package/dist/react.js.map +1 -1
  32. package/dist/schedule.d.ts +8 -8
  33. package/dist/schedule.js.map +1 -1
  34. package/package.json +10 -4
  35. package/src/index.ts +779 -119
  36. package/dist/chunk-4CIGD73X.js.map +0 -1
  37. package/dist/chunk-767EASBA.js.map +0 -1
  38. package/dist/chunk-E3LCYPCB.js.map +0 -1
  39. package/dist/chunk-NKZZ66QY.js.map +0 -1
@@ -1,21 +1,22 @@
1
1
  import {
2
2
  MCPClientManager
3
- } from "./chunk-E3LCYPCB.js";
3
+ } from "./chunk-HY7ZLHJB.js";
4
4
  import {
5
5
  DurableObjectOAuthClientProvider
6
- } from "./chunk-767EASBA.js";
6
+ } from "./chunk-PVQZBKN7.js";
7
7
  import {
8
8
  camelCaseToKebabCase
9
- } from "./chunk-NKZZ66QY.js";
9
+ } from "./chunk-KUH345EY.js";
10
10
 
11
11
  // src/index.ts
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);
@@ -46,12 +47,21 @@ function getCurrentAgent() {
46
47
  return {
47
48
  agent: void 0,
48
49
  connection: void 0,
49
- request: void 0
50
+ request: void 0,
51
+ email: void 0
50
52
  };
51
53
  }
52
54
  return store;
53
55
  }
54
- var Agent = class extends Server {
56
+ function withAgentContext(method) {
57
+ return function(...args) {
58
+ const { connection, request, email } = getCurrentAgent();
59
+ return agentContext.run({ agent: this, connection, request, email }, () => {
60
+ return method.apply(this, args);
61
+ });
62
+ };
63
+ }
64
+ var _Agent = class _Agent extends Server {
55
65
  constructor(ctx, env) {
56
66
  super(ctx, env);
57
67
  this._state = DEFAULT_STATE;
@@ -62,6 +72,11 @@ var Agent = class extends Server {
62
72
  * Override to provide default state values
63
73
  */
64
74
  this.initialState = DEFAULT_STATE;
75
+ /**
76
+ * The observability implementation to use for the Agent
77
+ */
78
+ this.observability = genericObservability;
79
+ this._flushingQueue = false;
65
80
  /**
66
81
  * Method called when an alarm fires.
67
82
  * Executes any scheduled tasks that are due.
@@ -75,42 +90,68 @@ var Agent = class extends Server {
75
90
  const result = this.sql`
76
91
  SELECT * FROM cf_agents_schedules WHERE time <= ${now}
77
92
  `;
78
- for (const row of result || []) {
79
- const callback = this[row.callback];
80
- if (!callback) {
81
- console.error(`callback ${row.callback} not found`);
82
- continue;
83
- }
84
- await agentContext.run(
85
- { agent: this, connection: void 0, request: void 0 },
86
- async () => {
87
- try {
88
- await callback.bind(this)(JSON.parse(row.payload), row);
89
- } catch (e) {
90
- console.error(`error executing callback "${row.callback}"`, e);
91
- }
93
+ if (result && Array.isArray(result)) {
94
+ for (const row of result) {
95
+ const callback = this[row.callback];
96
+ if (!callback) {
97
+ console.error(`callback ${row.callback} not found`);
98
+ continue;
92
99
  }
93
- );
94
- if (row.type === "cron") {
95
- const nextExecutionTime = getNextCronTime(row.cron);
96
- const nextTimestamp = Math.floor(nextExecutionTime.getTime() / 1e3);
97
- this.sql`
100
+ await agentContext.run(
101
+ {
102
+ agent: this,
103
+ connection: void 0,
104
+ request: void 0,
105
+ email: void 0
106
+ },
107
+ async () => {
108
+ try {
109
+ this.observability?.emit(
110
+ {
111
+ displayMessage: `Schedule ${row.id} executed`,
112
+ id: nanoid(),
113
+ payload: row,
114
+ timestamp: Date.now(),
115
+ type: "schedule:execute"
116
+ },
117
+ this.ctx
118
+ );
119
+ await callback.bind(this)(JSON.parse(row.payload), row);
120
+ } catch (e) {
121
+ console.error(`error executing callback "${row.callback}"`, e);
122
+ }
123
+ }
124
+ );
125
+ if (row.type === "cron") {
126
+ const nextExecutionTime = getNextCronTime(row.cron);
127
+ const nextTimestamp = Math.floor(nextExecutionTime.getTime() / 1e3);
128
+ this.sql`
98
129
  UPDATE cf_agents_schedules SET time = ${nextTimestamp} WHERE id = ${row.id}
99
130
  `;
100
- } else {
101
- this.sql`
131
+ } else {
132
+ this.sql`
102
133
  DELETE FROM cf_agents_schedules WHERE id = ${row.id}
103
134
  `;
135
+ }
104
136
  }
105
137
  }
106
138
  await this._scheduleNextAlarm();
107
139
  };
140
+ this._autoWrapCustomMethods();
108
141
  this.sql`
109
142
  CREATE TABLE IF NOT EXISTS cf_agents_state (
110
143
  id TEXT PRIMARY KEY NOT NULL,
111
144
  state TEXT
112
145
  )
113
146
  `;
147
+ this.sql`
148
+ CREATE TABLE IF NOT EXISTS cf_agents_queues (
149
+ id TEXT PRIMARY KEY NOT NULL,
150
+ payload TEXT,
151
+ callback TEXT,
152
+ created_at INTEGER DEFAULT (unixepoch())
153
+ )
154
+ `;
114
155
  void this.ctx.blockConcurrencyWhile(async () => {
115
156
  return this._tryCatch(async () => {
116
157
  this.sql`
@@ -142,7 +183,7 @@ var Agent = class extends Server {
142
183
  const _onRequest = this.onRequest.bind(this);
143
184
  this.onRequest = (request) => {
144
185
  return agentContext.run(
145
- { agent: this, connection: void 0, request },
186
+ { agent: this, connection: void 0, request, email: void 0 },
146
187
  async () => {
147
188
  if (this.mcp.isCallbackRequest(request)) {
148
189
  await this.mcp.handleCallbackRequest(request);
@@ -164,7 +205,7 @@ var Agent = class extends Server {
164
205
  const _onMessage = this.onMessage.bind(this);
165
206
  this.onMessage = async (connection, message) => {
166
207
  return agentContext.run(
167
- { agent: this, connection, request: void 0 },
208
+ { agent: this, connection, request: void 0, email: void 0 },
168
209
  async () => {
169
210
  if (typeof message !== "string") {
170
211
  return this._tryCatch(() => _onMessage(connection, message));
@@ -196,6 +237,21 @@ var Agent = class extends Server {
196
237
  return;
197
238
  }
198
239
  const result = await methodFn.apply(this, args);
240
+ this.observability?.emit(
241
+ {
242
+ displayMessage: `RPC call to ${method}`,
243
+ id: nanoid(),
244
+ payload: {
245
+ args,
246
+ method,
247
+ streaming: metadata?.streaming,
248
+ success: true
249
+ },
250
+ timestamp: Date.now(),
251
+ type: "rpc"
252
+ },
253
+ this.ctx
254
+ );
199
255
  const response = {
200
256
  done: true,
201
257
  id,
@@ -223,7 +279,7 @@ var Agent = class extends Server {
223
279
  const _onConnect = this.onConnect.bind(this);
224
280
  this.onConnect = (connection, ctx2) => {
225
281
  return agentContext.run(
226
- { agent: this, connection, request: ctx2.request },
282
+ { agent: this, connection, request: ctx2.request, email: void 0 },
227
283
  async () => {
228
284
  setTimeout(() => {
229
285
  if (this.state) {
@@ -240,6 +296,18 @@ var Agent = class extends Server {
240
296
  type: "cf_agent_mcp_servers"
241
297
  })
242
298
  );
299
+ this.observability?.emit(
300
+ {
301
+ displayMessage: "Connection established",
302
+ id: nanoid(),
303
+ payload: {
304
+ connectionId: connection.id
305
+ },
306
+ timestamp: Date.now(),
307
+ type: "connect"
308
+ },
309
+ this.ctx
310
+ );
243
311
  return this._tryCatch(() => _onConnect(connection, ctx2));
244
312
  }, 20);
245
313
  }
@@ -248,33 +316,57 @@ var Agent = class extends Server {
248
316
  const _onStart = this.onStart.bind(this);
249
317
  this.onStart = async () => {
250
318
  return agentContext.run(
251
- { agent: this, connection: void 0, request: void 0 },
319
+ {
320
+ agent: this,
321
+ connection: void 0,
322
+ request: void 0,
323
+ email: void 0
324
+ },
252
325
  async () => {
253
- const servers = this.sql`
326
+ await this._tryCatch(() => {
327
+ const servers = this.sql`
254
328
  SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
255
329
  `;
256
- Promise.allSettled(
257
- servers.map((server) => {
258
- return this._connectToMcpServerInternal(
259
- server.name,
260
- server.server_url,
261
- server.callback_url,
262
- server.server_options ? JSON.parse(server.server_options) : void 0,
263
- {
264
- id: server.id,
265
- oauthClientId: server.client_id ?? void 0
266
- }
267
- );
268
- })
269
- ).then((_results) => {
270
330
  this.broadcast(
271
331
  JSON.stringify({
272
332
  mcp: this.getMcpServers(),
273
333
  type: "cf_agent_mcp_servers"
274
334
  })
275
335
  );
336
+ if (servers && Array.isArray(servers) && servers.length > 0) {
337
+ servers.forEach((server) => {
338
+ this._connectToMcpServerInternal(
339
+ server.name,
340
+ server.server_url,
341
+ server.callback_url,
342
+ server.server_options ? JSON.parse(server.server_options) : void 0,
343
+ {
344
+ id: server.id,
345
+ oauthClientId: server.client_id ?? void 0
346
+ }
347
+ ).then(() => {
348
+ this.broadcast(
349
+ JSON.stringify({
350
+ mcp: this.getMcpServers(),
351
+ type: "cf_agent_mcp_servers"
352
+ })
353
+ );
354
+ }).catch((error) => {
355
+ console.error(
356
+ `Error connecting to MCP server: ${server.name} (${server.server_url})`,
357
+ error
358
+ );
359
+ this.broadcast(
360
+ JSON.stringify({
361
+ mcp: this.getMcpServers(),
362
+ type: "cf_agent_mcp_servers"
363
+ })
364
+ );
365
+ });
366
+ });
367
+ }
368
+ return _onStart();
276
369
  });
277
- await this._tryCatch(() => _onStart());
278
370
  }
279
371
  );
280
372
  };
@@ -325,6 +417,7 @@ var Agent = class extends Server {
325
417
  }
326
418
  }
327
419
  _setStateInternal(state, source = "server") {
420
+ const previousState = this._state;
328
421
  this._state = state;
329
422
  this.sql`
330
423
  INSERT OR REPLACE INTO cf_agents_state (id, state)
@@ -342,10 +435,23 @@ var Agent = class extends Server {
342
435
  source !== "server" ? [source.id] : []
343
436
  );
344
437
  return this._tryCatch(() => {
345
- const { connection, request } = agentContext.getStore() || {};
438
+ const { connection, request, email } = agentContext.getStore() || {};
346
439
  return agentContext.run(
347
- { agent: this, connection, request },
440
+ { agent: this, connection, request, email },
348
441
  async () => {
442
+ this.observability?.emit(
443
+ {
444
+ displayMessage: "State updated",
445
+ id: nanoid(),
446
+ payload: {
447
+ previousState,
448
+ state
449
+ },
450
+ timestamp: Date.now(),
451
+ type: "state:update"
452
+ },
453
+ this.ctx
454
+ );
349
455
  return this.onStateUpdate(state, source);
350
456
  }
351
457
  );
@@ -367,18 +473,67 @@ var Agent = class extends Server {
367
473
  onStateUpdate(state, source) {
368
474
  }
369
475
  /**
370
- * Called when the Agent receives an email
476
+ * Called when the Agent receives an email via routeAgentEmail()
477
+ * Override this method to handle incoming emails
371
478
  * @param email Email message to process
372
479
  */
373
- // biome-ignore lint/correctness/noUnusedFunctionParameters: overridden later
374
- onEmail(email) {
480
+ async _onEmail(email) {
375
481
  return agentContext.run(
376
- { agent: this, connection: void 0, request: void 0 },
482
+ { agent: this, connection: void 0, request: void 0, email },
377
483
  async () => {
378
- console.error("onEmail not implemented");
484
+ if ("onEmail" in this && typeof this.onEmail === "function") {
485
+ return this._tryCatch(
486
+ () => this.onEmail(email)
487
+ );
488
+ } else {
489
+ console.log("Received email from:", email.from, "to:", email.to);
490
+ console.log("Subject:", email.headers.get("subject"));
491
+ console.log(
492
+ "Implement onEmail(email: AgentEmail): Promise<void> in your agent to process emails"
493
+ );
494
+ }
379
495
  }
380
496
  );
381
497
  }
498
+ /**
499
+ * Reply to an email
500
+ * @param email The email to reply to
501
+ * @param options Options for the reply
502
+ * @returns void
503
+ */
504
+ async replyToEmail(email, options) {
505
+ return this._tryCatch(async () => {
506
+ const agentName = camelCaseToKebabCase(this._ParentClass.name);
507
+ const agentId = this.name;
508
+ const { createMimeMessage } = await import("mimetext");
509
+ const msg = createMimeMessage();
510
+ msg.setSender({ addr: email.to, name: options.fromName });
511
+ msg.setRecipient(email.from);
512
+ msg.setSubject(
513
+ options.subject || `Re: ${email.headers.get("subject")}` || "No subject"
514
+ );
515
+ msg.addMessage({
516
+ contentType: options.contentType || "text/plain",
517
+ data: options.body
518
+ });
519
+ const domain = email.from.split("@")[1];
520
+ const messageId = `<${agentId}@${domain}>`;
521
+ msg.setHeader("In-Reply-To", email.headers.get("Message-ID"));
522
+ msg.setHeader("Message-ID", messageId);
523
+ msg.setHeader("X-Agent-Name", agentName);
524
+ msg.setHeader("X-Agent-ID", agentId);
525
+ if (options.headers) {
526
+ for (const [key, value] of Object.entries(options.headers)) {
527
+ msg.setHeader(key, value);
528
+ }
529
+ }
530
+ await email.reply({
531
+ from: email.to,
532
+ raw: msg.asRaw(),
533
+ to: email.from
534
+ });
535
+ });
536
+ }
382
537
  async _tryCatch(fn) {
383
538
  try {
384
539
  return await fn();
@@ -386,6 +541,55 @@ var Agent = class extends Server {
386
541
  throw this.onError(e);
387
542
  }
388
543
  }
544
+ /**
545
+ * Automatically wrap custom methods with agent context
546
+ * This ensures getCurrentAgent() works in all custom methods without decorators
547
+ */
548
+ _autoWrapCustomMethods() {
549
+ const basePrototypes = [_Agent.prototype, Server.prototype];
550
+ const baseMethods = /* @__PURE__ */ new Set();
551
+ for (const baseProto of basePrototypes) {
552
+ let proto2 = baseProto;
553
+ while (proto2 && proto2 !== Object.prototype) {
554
+ const methodNames = Object.getOwnPropertyNames(proto2);
555
+ for (const methodName of methodNames) {
556
+ baseMethods.add(methodName);
557
+ }
558
+ proto2 = Object.getPrototypeOf(proto2);
559
+ }
560
+ }
561
+ let proto = Object.getPrototypeOf(this);
562
+ let depth = 0;
563
+ while (proto && proto !== Object.prototype && depth < 10) {
564
+ const methodNames = Object.getOwnPropertyNames(proto);
565
+ for (const methodName of methodNames) {
566
+ if (baseMethods.has(methodName) || methodName.startsWith("_") || typeof this[methodName] !== "function") {
567
+ continue;
568
+ }
569
+ if (!baseMethods.has(methodName)) {
570
+ const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
571
+ if (descriptor && typeof descriptor.value === "function") {
572
+ const wrappedFunction = withAgentContext(
573
+ // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
574
+ this[methodName]
575
+ // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
576
+ );
577
+ if (this._isCallable(methodName)) {
578
+ callableMetadata.set(
579
+ wrappedFunction,
580
+ callableMetadata.get(
581
+ this[methodName]
582
+ )
583
+ );
584
+ }
585
+ this.constructor.prototype[methodName] = wrappedFunction;
586
+ }
587
+ }
588
+ }
589
+ proto = Object.getPrototypeOf(proto);
590
+ depth++;
591
+ }
592
+ }
389
593
  onError(connectionOrError, error) {
390
594
  let theError;
391
595
  if (connectionOrError && error) {
@@ -411,6 +615,108 @@ var Agent = class extends Server {
411
615
  render() {
412
616
  throw new Error("Not implemented");
413
617
  }
618
+ /**
619
+ * Queue a task to be executed in the future
620
+ * @param payload Payload to pass to the callback
621
+ * @param callback Name of the method to call
622
+ * @returns The ID of the queued task
623
+ */
624
+ async queue(callback, payload) {
625
+ const id = nanoid(9);
626
+ if (typeof callback !== "string") {
627
+ throw new Error("Callback must be a string");
628
+ }
629
+ if (typeof this[callback] !== "function") {
630
+ throw new Error(`this.${callback} is not a function`);
631
+ }
632
+ this.sql`
633
+ INSERT OR REPLACE INTO cf_agents_queues (id, payload, callback)
634
+ VALUES (${id}, ${JSON.stringify(payload)}, ${callback})
635
+ `;
636
+ void this._flushQueue().catch((e) => {
637
+ console.error("Error flushing queue:", e);
638
+ });
639
+ return id;
640
+ }
641
+ async _flushQueue() {
642
+ if (this._flushingQueue) {
643
+ return;
644
+ }
645
+ this._flushingQueue = true;
646
+ while (true) {
647
+ const result = this.sql`
648
+ SELECT * FROM cf_agents_queues
649
+ ORDER BY created_at ASC
650
+ `;
651
+ if (!result || result.length === 0) {
652
+ break;
653
+ }
654
+ for (const row of result || []) {
655
+ const callback = this[row.callback];
656
+ if (!callback) {
657
+ console.error(`callback ${row.callback} not found`);
658
+ continue;
659
+ }
660
+ const { connection, request, email } = agentContext.getStore() || {};
661
+ await agentContext.run(
662
+ {
663
+ agent: this,
664
+ connection,
665
+ request,
666
+ email
667
+ },
668
+ async () => {
669
+ await callback.bind(this)(JSON.parse(row.payload), row);
670
+ await this.dequeue(row.id);
671
+ }
672
+ );
673
+ }
674
+ }
675
+ this._flushingQueue = false;
676
+ }
677
+ /**
678
+ * Dequeue a task by ID
679
+ * @param id ID of the task to dequeue
680
+ */
681
+ async dequeue(id) {
682
+ this.sql`DELETE FROM cf_agents_queues WHERE id = ${id}`;
683
+ }
684
+ /**
685
+ * Dequeue all tasks
686
+ */
687
+ async dequeueAll() {
688
+ this.sql`DELETE FROM cf_agents_queues`;
689
+ }
690
+ /**
691
+ * Dequeue all tasks by callback
692
+ * @param callback Name of the callback to dequeue
693
+ */
694
+ async dequeueAllByCallback(callback) {
695
+ this.sql`DELETE FROM cf_agents_queues WHERE callback = ${callback}`;
696
+ }
697
+ /**
698
+ * Get a queued task by ID
699
+ * @param id ID of the task to get
700
+ * @returns The task or undefined if not found
701
+ */
702
+ async getQueue(id) {
703
+ const result = this.sql`
704
+ SELECT * FROM cf_agents_queues WHERE id = ${id}
705
+ `;
706
+ return result ? { ...result[0], payload: JSON.parse(result[0].payload) } : void 0;
707
+ }
708
+ /**
709
+ * Get all queues by key and value
710
+ * @param key Key to filter by
711
+ * @param value Value to filter by
712
+ * @returns Array of matching QueueItem objects
713
+ */
714
+ async getQueues(key, value) {
715
+ const result = this.sql`
716
+ SELECT * FROM cf_agents_queues
717
+ `;
718
+ return result.filter((row) => JSON.parse(row.payload)[key] === value);
719
+ }
414
720
  /**
415
721
  * Schedule a task to be executed in the future
416
722
  * @template T Type of the payload data
@@ -421,6 +727,16 @@ var Agent = class extends Server {
421
727
  */
422
728
  async schedule(when, callback, payload) {
423
729
  const id = nanoid(9);
730
+ const emitScheduleCreate = (schedule) => this.observability?.emit(
731
+ {
732
+ displayMessage: `Schedule ${schedule.id} created`,
733
+ id: nanoid(),
734
+ payload: schedule,
735
+ timestamp: Date.now(),
736
+ type: "schedule:create"
737
+ },
738
+ this.ctx
739
+ );
424
740
  if (typeof callback !== "string") {
425
741
  throw new Error("Callback must be a string");
426
742
  }
@@ -436,13 +752,15 @@ var Agent = class extends Server {
436
752
  )}, 'scheduled', ${timestamp})
437
753
  `;
438
754
  await this._scheduleNextAlarm();
439
- return {
755
+ const schedule = {
440
756
  callback,
441
757
  id,
442
758
  payload,
443
759
  time: timestamp,
444
760
  type: "scheduled"
445
761
  };
762
+ emitScheduleCreate(schedule);
763
+ return schedule;
446
764
  }
447
765
  if (typeof when === "number") {
448
766
  const time = new Date(Date.now() + when * 1e3);
@@ -454,7 +772,7 @@ var Agent = class extends Server {
454
772
  )}, 'delayed', ${when}, ${timestamp})
455
773
  `;
456
774
  await this._scheduleNextAlarm();
457
- return {
775
+ const schedule = {
458
776
  callback,
459
777
  delayInSeconds: when,
460
778
  id,
@@ -462,6 +780,8 @@ var Agent = class extends Server {
462
780
  time: timestamp,
463
781
  type: "delayed"
464
782
  };
783
+ emitScheduleCreate(schedule);
784
+ return schedule;
465
785
  }
466
786
  if (typeof when === "string") {
467
787
  const nextExecutionTime = getNextCronTime(when);
@@ -473,7 +793,7 @@ var Agent = class extends Server {
473
793
  )}, 'cron', ${when}, ${timestamp})
474
794
  `;
475
795
  await this._scheduleNextAlarm();
476
- return {
796
+ const schedule = {
477
797
  callback,
478
798
  cron: when,
479
799
  id,
@@ -481,6 +801,8 @@ var Agent = class extends Server {
481
801
  time: timestamp,
482
802
  type: "cron"
483
803
  };
804
+ emitScheduleCreate(schedule);
805
+ return schedule;
484
806
  }
485
807
  throw new Error("Invalid schedule type");
486
808
  }
@@ -538,15 +860,28 @@ var Agent = class extends Server {
538
860
  * @returns true if the task was cancelled, false otherwise
539
861
  */
540
862
  async cancelSchedule(id) {
863
+ const schedule = await this.getSchedule(id);
864
+ if (schedule) {
865
+ this.observability?.emit(
866
+ {
867
+ displayMessage: `Schedule ${id} cancelled`,
868
+ id: nanoid(),
869
+ payload: schedule,
870
+ timestamp: Date.now(),
871
+ type: "schedule:cancel"
872
+ },
873
+ this.ctx
874
+ );
875
+ }
541
876
  this.sql`DELETE FROM cf_agents_schedules WHERE id = ${id}`;
542
877
  await this._scheduleNextAlarm();
543
878
  return true;
544
879
  }
545
880
  async _scheduleNextAlarm() {
546
881
  const result = this.sql`
547
- SELECT time FROM cf_agents_schedules
882
+ SELECT time FROM cf_agents_schedules
548
883
  WHERE time > ${Math.floor(Date.now() / 1e3)}
549
- ORDER BY time ASC
884
+ ORDER BY time ASC
550
885
  LIMIT 1
551
886
  `;
552
887
  if (!result) return;
@@ -562,9 +897,20 @@ var Agent = class extends Server {
562
897
  this.sql`DROP TABLE IF EXISTS cf_agents_state`;
563
898
  this.sql`DROP TABLE IF EXISTS cf_agents_schedules`;
564
899
  this.sql`DROP TABLE IF EXISTS cf_agents_mcp_servers`;
900
+ this.sql`DROP TABLE IF EXISTS cf_agents_queues`;
565
901
  await this.ctx.storage.deleteAlarm();
566
902
  await this.ctx.storage.deleteAll();
567
903
  this.ctx.abort("destroyed");
904
+ this.observability?.emit(
905
+ {
906
+ displayMessage: "Agent destroyed",
907
+ id: nanoid(),
908
+ payload: {},
909
+ timestamp: Date.now(),
910
+ type: "destroy"
911
+ },
912
+ this.ctx
913
+ );
568
914
  }
569
915
  /**
570
916
  * Get all methods marked as callable on this Agent
@@ -673,17 +1019,19 @@ var Agent = class extends Server {
673
1019
  const servers = this.sql`
674
1020
  SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
675
1021
  `;
676
- for (const server of servers) {
677
- const serverConn = this.mcp.mcpConnections[server.id];
678
- mcpState.servers[server.id] = {
679
- auth_url: server.auth_url,
680
- capabilities: serverConn?.serverCapabilities ?? null,
681
- instructions: serverConn?.instructions ?? null,
682
- name: server.name,
683
- server_url: server.server_url,
684
- // mark as "authenticating" because the server isn't automatically connected, so it's pending authenticating
685
- state: serverConn?.connectionState ?? "authenticating"
686
- };
1022
+ if (servers && Array.isArray(servers) && servers.length > 0) {
1023
+ for (const server of servers) {
1024
+ const serverConn = this.mcp.mcpConnections[server.id];
1025
+ mcpState.servers[server.id] = {
1026
+ auth_url: server.auth_url,
1027
+ capabilities: serverConn?.serverCapabilities ?? null,
1028
+ instructions: serverConn?.instructions ?? null,
1029
+ name: server.name,
1030
+ server_url: server.server_url,
1031
+ // mark as "authenticating" because the server isn't automatically connected, so it's pending authenticating
1032
+ state: serverConn?.connectionState ?? "authenticating"
1033
+ };
1034
+ }
687
1035
  }
688
1036
  return mcpState;
689
1037
  }
@@ -691,11 +1039,12 @@ var Agent = class extends Server {
691
1039
  /**
692
1040
  * Agent configuration options
693
1041
  */
694
- Agent.options = {
1042
+ _Agent.options = {
695
1043
  /** Whether the Agent should hibernate when inactive */
696
1044
  hibernate: true
697
1045
  // default to hibernate
698
1046
  };
1047
+ var Agent = _Agent;
699
1048
  async function routeAgentRequest(request, env, options) {
700
1049
  const corsHeaders = options?.cors === true ? {
701
1050
  "Access-Control-Allow-Credentials": "true",
@@ -731,7 +1080,126 @@ async function routeAgentRequest(request, env, options) {
731
1080
  }
732
1081
  return response;
733
1082
  }
734
- async function routeAgentEmail(_email, _env, _options) {
1083
+ function createHeaderBasedEmailResolver() {
1084
+ return async (email, _env) => {
1085
+ const messageId = email.headers.get("message-id");
1086
+ if (messageId) {
1087
+ const messageIdMatch = messageId.match(/<([^@]+)@([^>]+)>/);
1088
+ if (messageIdMatch) {
1089
+ const [, agentId2, domain] = messageIdMatch;
1090
+ const agentName2 = domain.split(".")[0];
1091
+ return { agentName: agentName2, agentId: agentId2 };
1092
+ }
1093
+ }
1094
+ const references = email.headers.get("references");
1095
+ if (references) {
1096
+ const referencesMatch = references.match(
1097
+ /<([A-Za-z0-9+/]{43}=)@([^>]+)>/
1098
+ );
1099
+ if (referencesMatch) {
1100
+ const [, base64Id, domain] = referencesMatch;
1101
+ const agentId2 = Buffer.from(base64Id, "base64").toString("hex");
1102
+ const agentName2 = domain.split(".")[0];
1103
+ return { agentName: agentName2, agentId: agentId2 };
1104
+ }
1105
+ }
1106
+ const agentName = email.headers.get("x-agent-name");
1107
+ const agentId = email.headers.get("x-agent-id");
1108
+ if (agentName && agentId) {
1109
+ return { agentName, agentId };
1110
+ }
1111
+ return null;
1112
+ };
1113
+ }
1114
+ function createAddressBasedEmailResolver(defaultAgentName) {
1115
+ return async (email, _env) => {
1116
+ const emailMatch = email.to.match(/^([^+@]+)(?:\+([^@]+))?@(.+)$/);
1117
+ if (!emailMatch) {
1118
+ return null;
1119
+ }
1120
+ const [, localPart, subAddress] = emailMatch;
1121
+ if (subAddress) {
1122
+ return {
1123
+ agentName: localPart,
1124
+ agentId: subAddress
1125
+ };
1126
+ }
1127
+ return {
1128
+ agentName: defaultAgentName,
1129
+ agentId: localPart
1130
+ };
1131
+ };
1132
+ }
1133
+ function createCatchAllEmailResolver(agentName, agentId) {
1134
+ return async () => ({ agentName, agentId });
1135
+ }
1136
+ var agentMapCache = /* @__PURE__ */ new WeakMap();
1137
+ async function routeAgentEmail(email, env, options) {
1138
+ const routingInfo = await options.resolver(email, env);
1139
+ if (!routingInfo) {
1140
+ console.warn("No routing information found for email, dropping message");
1141
+ return;
1142
+ }
1143
+ if (!agentMapCache.has(env)) {
1144
+ const map = {};
1145
+ for (const [key, value] of Object.entries(env)) {
1146
+ if (value && typeof value === "object" && "idFromName" in value && typeof value.idFromName === "function") {
1147
+ map[key] = value;
1148
+ map[camelCaseToKebabCase(key)] = value;
1149
+ }
1150
+ }
1151
+ agentMapCache.set(env, map);
1152
+ }
1153
+ const agentMap = agentMapCache.get(env);
1154
+ const namespace = agentMap[routingInfo.agentName];
1155
+ if (!namespace) {
1156
+ const availableAgents = Object.keys(agentMap).filter((key) => !key.includes("-")).join(", ");
1157
+ throw new Error(
1158
+ `Agent namespace '${routingInfo.agentName}' not found in environment. Available agents: ${availableAgents}`
1159
+ );
1160
+ }
1161
+ const agent = await getAgentByName(
1162
+ namespace,
1163
+ routingInfo.agentId
1164
+ );
1165
+ const serialisableEmail = {
1166
+ getRaw: async () => {
1167
+ const reader = email.raw.getReader();
1168
+ const chunks = [];
1169
+ let done = false;
1170
+ while (!done) {
1171
+ const { value, done: readerDone } = await reader.read();
1172
+ done = readerDone;
1173
+ if (value) {
1174
+ chunks.push(value);
1175
+ }
1176
+ }
1177
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
1178
+ const combined = new Uint8Array(totalLength);
1179
+ let offset = 0;
1180
+ for (const chunk of chunks) {
1181
+ combined.set(chunk, offset);
1182
+ offset += chunk.length;
1183
+ }
1184
+ return combined;
1185
+ },
1186
+ headers: email.headers,
1187
+ rawSize: email.rawSize,
1188
+ setReject: (reason) => {
1189
+ email.setReject(reason);
1190
+ },
1191
+ forward: (rcptTo, headers) => {
1192
+ return email.forward(rcptTo, headers);
1193
+ },
1194
+ reply: (options2) => {
1195
+ return email.reply(
1196
+ new EmailMessage(options2.from, options2.to, options2.raw)
1197
+ );
1198
+ },
1199
+ from: email.from,
1200
+ to: email.to
1201
+ };
1202
+ await agent._onEmail(serialisableEmail);
735
1203
  }
736
1204
  async function getAgentByName(namespace, name, options) {
737
1205
  return getServerByName(namespace, name, options);
@@ -779,13 +1247,41 @@ var StreamingResponse = class {
779
1247
  }
780
1248
  };
781
1249
 
1250
+ // src/observability/index.ts
1251
+ var genericObservability = {
1252
+ emit(event) {
1253
+ if (isLocalMode()) {
1254
+ console.log(event.displayMessage);
1255
+ return;
1256
+ }
1257
+ console.log(event);
1258
+ }
1259
+ };
1260
+ var localMode = false;
1261
+ function isLocalMode() {
1262
+ if (localMode) {
1263
+ return true;
1264
+ }
1265
+ const { request } = getCurrentAgent();
1266
+ if (!request) {
1267
+ return false;
1268
+ }
1269
+ const url = new URL(request.url);
1270
+ localMode = url.hostname === "localhost";
1271
+ return localMode;
1272
+ }
1273
+
782
1274
  export {
1275
+ genericObservability,
783
1276
  unstable_callable,
784
1277
  getCurrentAgent,
785
1278
  Agent,
786
1279
  routeAgentRequest,
1280
+ createHeaderBasedEmailResolver,
1281
+ createAddressBasedEmailResolver,
1282
+ createCatchAllEmailResolver,
787
1283
  routeAgentEmail,
788
1284
  getAgentByName,
789
1285
  StreamingResponse
790
1286
  };
791
- //# sourceMappingURL=chunk-4CIGD73X.js.map
1287
+ //# sourceMappingURL=chunk-JXN5WZFQ.js.map