agents 0.0.0-d40512c → 0.0.0-d72c6a2

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 (50) hide show
  1. package/README.md +234 -6
  2. package/dist/ai-chat-agent.d.ts +12 -9
  3. package/dist/ai-chat-agent.js +150 -59
  4. package/dist/ai-chat-agent.js.map +1 -1
  5. package/dist/ai-chat-v5-migration.d.ts +152 -0
  6. package/dist/ai-chat-v5-migration.js +19 -0
  7. package/dist/ai-chat-v5-migration.js.map +1 -0
  8. package/dist/ai-react.d.ts +62 -71
  9. package/dist/ai-react.js +144 -37
  10. package/dist/ai-react.js.map +1 -1
  11. package/dist/ai-types.d.ts +36 -19
  12. package/dist/ai-types.js +6 -0
  13. package/dist/chunk-AVYJQSLW.js +17 -0
  14. package/dist/chunk-AVYJQSLW.js.map +1 -0
  15. package/dist/{chunk-PVQZBKN7.js → chunk-LL2AFX7V.js} +5 -2
  16. package/dist/chunk-LL2AFX7V.js.map +1 -0
  17. package/dist/{chunk-4RBEYCWK.js → chunk-MH46VMM4.js} +163 -20
  18. package/dist/chunk-MH46VMM4.js.map +1 -0
  19. package/dist/{chunk-KUH345EY.js → chunk-QEVM4BVL.js} +5 -5
  20. package/dist/chunk-QEVM4BVL.js.map +1 -0
  21. package/dist/chunk-UJVEAURM.js +150 -0
  22. package/dist/chunk-UJVEAURM.js.map +1 -0
  23. package/dist/{chunk-LU2RSO54.js → chunk-YDUDMOL6.js} +515 -129
  24. package/dist/chunk-YDUDMOL6.js.map +1 -0
  25. package/dist/client-CvaJdLQA.d.ts +5015 -0
  26. package/dist/client.js +2 -1
  27. package/dist/index.d.ts +557 -24
  28. package/dist/index.js +13 -4
  29. package/dist/mcp/client.d.ts +9 -1053
  30. package/dist/mcp/client.js +1 -1
  31. package/dist/mcp/do-oauth-client-provider.d.ts +1 -0
  32. package/dist/mcp/do-oauth-client-provider.js +1 -1
  33. package/dist/mcp/index.d.ts +68 -55
  34. package/dist/mcp/index.js +854 -608
  35. package/dist/mcp/index.js.map +1 -1
  36. package/dist/observability/index.d.ts +46 -12
  37. package/dist/observability/index.js +5 -4
  38. package/dist/react.d.ts +7 -3
  39. package/dist/react.js +7 -5
  40. package/dist/react.js.map +1 -1
  41. package/dist/schedule.d.ts +83 -9
  42. package/dist/schedule.js +15 -2
  43. package/dist/schedule.js.map +1 -1
  44. package/package.json +19 -8
  45. package/src/index.ts +731 -149
  46. package/dist/chunk-4RBEYCWK.js.map +0 -1
  47. package/dist/chunk-KUH345EY.js.map +0 -1
  48. package/dist/chunk-LU2RSO54.js.map +0 -1
  49. package/dist/chunk-PVQZBKN7.js.map +0 -1
  50. package/dist/index-CITGJflw.d.ts +0 -486
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { env } from "cloudflare:workers";
1
2
  import { AsyncLocalStorage } from "node:async_hooks";
2
3
  import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
3
4
  import type { SSEClientTransportOptions } from "@modelcontextprotocol/sdk/client/sse.js";
@@ -10,20 +11,22 @@ import type {
10
11
  } from "@modelcontextprotocol/sdk/types.js";
11
12
  import { parseCronExpression } from "cron-schedule";
12
13
  import { nanoid } from "nanoid";
14
+ import { EmailMessage } from "cloudflare:email";
13
15
  import {
14
16
  type Connection,
15
17
  type ConnectionContext,
16
- getServerByName,
17
18
  type PartyServerOptions,
18
- routePartykitRequest,
19
19
  Server,
20
- type WSMessage
20
+ type WSMessage,
21
+ getServerByName,
22
+ routePartykitRequest
21
23
  } from "partyserver";
22
24
  import { camelCaseToKebabCase } from "./client";
23
25
  import { MCPClientManager } from "./mcp/client";
24
26
  // import type { MCPClientConnection } from "./mcp/client-connection";
25
27
  import { DurableObjectOAuthClientProvider } from "./mcp/do-oauth-client-provider";
26
28
  import { genericObservability, type Observability } from "./observability";
29
+ import { MessageType } from "./ai-types";
27
30
 
28
31
  export type { Connection, ConnectionContext, WSMessage } from "partyserver";
29
32
 
@@ -41,7 +44,7 @@ export type RPCRequest = {
41
44
  * State update message from client
42
45
  */
43
46
  export type StateUpdateMessage = {
44
- type: "cf_agent_state";
47
+ type: MessageType.CF_AGENT_STATE;
45
48
  state: unknown;
46
49
  };
47
50
 
@@ -49,7 +52,7 @@ export type StateUpdateMessage = {
49
52
  * RPC response message to client
50
53
  */
51
54
  export type RPCResponse = {
52
- type: "rpc";
55
+ type: MessageType.RPC;
53
56
  id: string;
54
57
  } & (
55
58
  | {
@@ -76,7 +79,7 @@ function isRPCRequest(msg: unknown): msg is RPCRequest {
76
79
  typeof msg === "object" &&
77
80
  msg !== null &&
78
81
  "type" in msg &&
79
- msg.type === "rpc" &&
82
+ msg.type === MessageType.RPC &&
80
83
  "id" in msg &&
81
84
  typeof msg.id === "string" &&
82
85
  "method" in msg &&
@@ -94,7 +97,7 @@ function isStateUpdateMessage(msg: unknown): msg is StateUpdateMessage {
94
97
  typeof msg === "object" &&
95
98
  msg !== null &&
96
99
  "type" in msg &&
97
- msg.type === "cf_agent_state" &&
100
+ msg.type === MessageType.CF_AGENT_STATE &&
98
101
  "state" in msg
99
102
  );
100
103
  }
@@ -115,7 +118,7 @@ const callableMetadata = new Map<Function, CallableMetadata>();
115
118
  * Decorator that marks a method as callable by clients
116
119
  * @param metadata Optional metadata about the callable method
117
120
  */
118
- export function unstable_callable(metadata: CallableMetadata = {}) {
121
+ export function callable(metadata: CallableMetadata = {}) {
119
122
  return function callableDecorator<This, Args extends unknown[], Return>(
120
123
  target: (this: This, ...args: Args) => Return,
121
124
  // biome-ignore lint/correctness/noUnusedFunctionParameters: later
@@ -129,6 +132,30 @@ export function unstable_callable(metadata: CallableMetadata = {}) {
129
132
  };
130
133
  }
131
134
 
135
+ let didWarnAboutUnstableCallable = false;
136
+
137
+ /**
138
+ * Decorator that marks a method as callable by clients
139
+ * @deprecated this has been renamed to callable, and unstable_callable will be removed in the next major version
140
+ * @param metadata Optional metadata about the callable method
141
+ */
142
+ export const unstable_callable = (metadata: CallableMetadata = {}) => {
143
+ if (!didWarnAboutUnstableCallable) {
144
+ didWarnAboutUnstableCallable = true;
145
+ console.warn(
146
+ "unstable_callable is deprecated, use callable instead. unstable_callable will be removed in the next major version."
147
+ );
148
+ }
149
+ callable(metadata);
150
+ };
151
+
152
+ export type QueueItem<T = string> = {
153
+ id: string;
154
+ payload: T;
155
+ callback: keyof Agent<unknown>;
156
+ created_at: number;
157
+ };
158
+
132
159
  /**
133
160
  * Represents a scheduled task within an Agent
134
161
  * @template T Type of the payload data
@@ -174,7 +201,7 @@ function getNextCronTime(cron: string) {
174
201
  * MCP Server state update message from server -> Client
175
202
  */
176
203
  export type MCPServerMessage = {
177
- type: "cf_agent_mcp_servers";
204
+ type: MessageType.CF_AGENT_MCP_SERVERS;
178
205
  mcp: MCPServersState;
179
206
  };
180
207
 
@@ -218,9 +245,10 @@ const STATE_WAS_CHANGED = "cf_state_was_changed";
218
245
  const DEFAULT_STATE = {} as unknown;
219
246
 
220
247
  const agentContext = new AsyncLocalStorage<{
221
- agent: Agent<unknown>;
248
+ agent: Agent<unknown, unknown>;
222
249
  connection: Connection | undefined;
223
250
  request: Request | undefined;
251
+ email: AgentEmail | undefined;
224
252
  }>();
225
253
 
226
254
  export function getCurrentAgent<
@@ -228,31 +256,63 @@ export function getCurrentAgent<
228
256
  >(): {
229
257
  agent: T | undefined;
230
258
  connection: Connection | undefined;
231
- request: Request<unknown, CfProperties<unknown>> | undefined;
259
+ request: Request | undefined;
260
+ email: AgentEmail | undefined;
232
261
  } {
233
262
  const store = agentContext.getStore() as
234
263
  | {
235
264
  agent: T;
236
265
  connection: Connection | undefined;
237
- request: Request<unknown, CfProperties<unknown>> | undefined;
266
+ request: Request | undefined;
267
+ email: AgentEmail | undefined;
238
268
  }
239
269
  | undefined;
240
270
  if (!store) {
241
271
  return {
242
272
  agent: undefined,
243
273
  connection: undefined,
244
- request: undefined
274
+ request: undefined,
275
+ email: undefined
245
276
  };
246
277
  }
247
278
  return store;
248
279
  }
249
280
 
281
+ /**
282
+ * Wraps a method to run within the agent context, ensuring getCurrentAgent() works properly
283
+ * @param agent The agent instance
284
+ * @param method The method to wrap
285
+ * @returns A wrapped method that runs within the agent context
286
+ */
287
+
288
+ // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
289
+ function withAgentContext<T extends (...args: any[]) => any>(
290
+ method: T
291
+ ): (this: Agent<unknown, unknown>, ...args: Parameters<T>) => ReturnType<T> {
292
+ return function (...args: Parameters<T>): ReturnType<T> {
293
+ const { connection, request, email, agent } = getCurrentAgent();
294
+
295
+ if (agent === this) {
296
+ // already wrapped, so we can just call the method
297
+ return method.apply(this, args);
298
+ }
299
+ // not wrapped, so we need to wrap it
300
+ return agentContext.run({ agent: this, connection, request, email }, () => {
301
+ return method.apply(this, args);
302
+ });
303
+ };
304
+ }
305
+
250
306
  /**
251
307
  * Base class for creating Agent implementations
252
308
  * @template Env Environment type containing bindings
253
309
  * @template State State type to store within the Agent
254
310
  */
255
- export class Agent<Env, State = unknown> extends Server<Env> {
311
+ export class Agent<
312
+ Env = typeof env,
313
+ State = unknown,
314
+ Props extends Record<string, unknown> = Record<string, unknown>
315
+ > extends Server<Env, Props> {
256
316
  private _state = DEFAULT_STATE as State;
257
317
 
258
318
  private _ParentClass: typeof Agent<Env, State> =
@@ -351,6 +411,9 @@ export class Agent<Env, State = unknown> extends Server<Env> {
351
411
  constructor(ctx: AgentContext, env: Env) {
352
412
  super(ctx, env);
353
413
 
414
+ // Auto-wrap custom methods with agent context
415
+ this._autoWrapCustomMethods();
416
+
354
417
  this.sql`
355
418
  CREATE TABLE IF NOT EXISTS cf_agents_state (
356
419
  id TEXT PRIMARY KEY NOT NULL,
@@ -358,6 +421,15 @@ export class Agent<Env, State = unknown> extends Server<Env> {
358
421
  )
359
422
  `;
360
423
 
424
+ this.sql`
425
+ CREATE TABLE IF NOT EXISTS cf_agents_queues (
426
+ id TEXT PRIMARY KEY NOT NULL,
427
+ payload TEXT,
428
+ callback TEXT,
429
+ created_at INTEGER DEFAULT (unixepoch())
430
+ )
431
+ `;
432
+
361
433
  void this.ctx.blockConcurrencyWhile(async () => {
362
434
  return this._tryCatch(async () => {
363
435
  // Create alarms table if it doesn't exist
@@ -394,7 +466,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
394
466
  const _onRequest = this.onRequest.bind(this);
395
467
  this.onRequest = (request: Request) => {
396
468
  return agentContext.run(
397
- { agent: this, connection: undefined, request },
469
+ { agent: this, connection: undefined, request, email: undefined },
398
470
  async () => {
399
471
  if (this.mcp.isCallbackRequest(request)) {
400
472
  await this.mcp.handleCallbackRequest(request);
@@ -403,7 +475,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
403
475
  this.broadcast(
404
476
  JSON.stringify({
405
477
  mcp: this.getMcpServers(),
406
- type: "cf_agent_mcp_servers"
478
+ type: MessageType.CF_AGENT_MCP_SERVERS
407
479
  })
408
480
  );
409
481
 
@@ -422,7 +494,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
422
494
  const _onMessage = this.onMessage.bind(this);
423
495
  this.onMessage = async (connection: Connection, message: WSMessage) => {
424
496
  return agentContext.run(
425
- { agent: this, connection, request: undefined },
497
+ { agent: this, connection, request: undefined, email: undefined },
426
498
  async () => {
427
499
  if (typeof message !== "string") {
428
500
  return this._tryCatch(() => _onMessage(connection, message));
@@ -472,10 +544,8 @@ export class Agent<Env, State = unknown> extends Server<Env> {
472
544
  displayMessage: `RPC call to ${method}`,
473
545
  id: nanoid(),
474
546
  payload: {
475
- args,
476
547
  method,
477
- streaming: metadata?.streaming,
478
- success: true
548
+ streaming: metadata?.streaming
479
549
  },
480
550
  timestamp: Date.now(),
481
551
  type: "rpc"
@@ -488,7 +558,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
488
558
  id,
489
559
  result,
490
560
  success: true,
491
- type: "rpc"
561
+ type: MessageType.RPC
492
562
  };
493
563
  connection.send(JSON.stringify(response));
494
564
  } catch (e) {
@@ -498,7 +568,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
498
568
  e instanceof Error ? e.message : "Unknown error occurred",
499
569
  id: parsed.id,
500
570
  success: false,
501
- type: "rpc"
571
+ type: MessageType.RPC
502
572
  };
503
573
  connection.send(JSON.stringify(response));
504
574
  console.error("RPC error:", e);
@@ -516,77 +586,104 @@ export class Agent<Env, State = unknown> extends Server<Env> {
516
586
  // TODO: This is a hack to ensure the state is sent after the connection is established
517
587
  // must fix this
518
588
  return agentContext.run(
519
- { agent: this, connection, request: ctx.request },
520
- async () => {
521
- setTimeout(() => {
522
- if (this.state) {
523
- connection.send(
524
- JSON.stringify({
525
- state: this.state,
526
- type: "cf_agent_state"
527
- })
528
- );
529
- }
530
-
589
+ { agent: this, connection, request: ctx.request, email: undefined },
590
+ () => {
591
+ if (this.state) {
531
592
  connection.send(
532
593
  JSON.stringify({
533
- mcp: this.getMcpServers(),
534
- type: "cf_agent_mcp_servers"
594
+ state: this.state,
595
+ type: MessageType.CF_AGENT_STATE
535
596
  })
536
597
  );
598
+ }
537
599
 
538
- this.observability?.emit(
539
- {
540
- displayMessage: "Connection established",
541
- id: nanoid(),
542
- payload: {
543
- connectionId: connection.id
544
- },
545
- timestamp: Date.now(),
546
- type: "connect"
600
+ connection.send(
601
+ JSON.stringify({
602
+ mcp: this.getMcpServers(),
603
+ type: MessageType.CF_AGENT_MCP_SERVERS
604
+ })
605
+ );
606
+
607
+ this.observability?.emit(
608
+ {
609
+ displayMessage: "Connection established",
610
+ id: nanoid(),
611
+ payload: {
612
+ connectionId: connection.id
547
613
  },
548
- this.ctx
549
- );
550
- return this._tryCatch(() => _onConnect(connection, ctx));
551
- }, 20);
614
+ timestamp: Date.now(),
615
+ type: "connect"
616
+ },
617
+ this.ctx
618
+ );
619
+ return this._tryCatch(() => _onConnect(connection, ctx));
552
620
  }
553
621
  );
554
622
  };
555
623
 
556
624
  const _onStart = this.onStart.bind(this);
557
- this.onStart = async () => {
625
+ this.onStart = async (props?: Props) => {
558
626
  return agentContext.run(
559
- { agent: this, connection: undefined, request: undefined },
627
+ {
628
+ agent: this,
629
+ connection: undefined,
630
+ request: undefined,
631
+ email: undefined
632
+ },
560
633
  async () => {
561
- const servers = this.sql<MCPServerRow>`
634
+ await this._tryCatch(() => {
635
+ const servers = this.sql<MCPServerRow>`
562
636
  SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
563
637
  `;
564
638
 
565
- // from DO storage, reconnect to all servers not currently in the oauth flow using our saved auth information
566
- Promise.allSettled(
567
- servers.map((server) => {
568
- return this._connectToMcpServerInternal(
569
- server.name,
570
- server.server_url,
571
- server.callback_url,
572
- server.server_options
573
- ? JSON.parse(server.server_options)
574
- : undefined,
575
- {
576
- id: server.id,
577
- oauthClientId: server.client_id ?? undefined
578
- }
579
- );
580
- })
581
- ).then((_results) => {
582
639
  this.broadcast(
583
640
  JSON.stringify({
584
641
  mcp: this.getMcpServers(),
585
- type: "cf_agent_mcp_servers"
642
+ type: MessageType.CF_AGENT_MCP_SERVERS
586
643
  })
587
644
  );
645
+
646
+ // from DO storage, reconnect to all servers not currently in the oauth flow using our saved auth information
647
+ if (servers && Array.isArray(servers) && servers.length > 0) {
648
+ servers.forEach((server) => {
649
+ this._connectToMcpServerInternal(
650
+ server.name,
651
+ server.server_url,
652
+ server.callback_url,
653
+ server.server_options
654
+ ? JSON.parse(server.server_options)
655
+ : undefined,
656
+ {
657
+ id: server.id,
658
+ oauthClientId: server.client_id ?? undefined
659
+ }
660
+ )
661
+ .then(() => {
662
+ // Broadcast updated MCP servers state after each server connects
663
+ this.broadcast(
664
+ JSON.stringify({
665
+ mcp: this.getMcpServers(),
666
+ type: MessageType.CF_AGENT_MCP_SERVERS
667
+ })
668
+ );
669
+ })
670
+ .catch((error) => {
671
+ console.error(
672
+ `Error connecting to MCP server: ${server.name} (${server.server_url})`,
673
+ error
674
+ );
675
+ // Still broadcast even if connection fails, so clients know about the failure
676
+ this.broadcast(
677
+ JSON.stringify({
678
+ mcp: this.getMcpServers(),
679
+ type: MessageType.CF_AGENT_MCP_SERVERS
680
+ })
681
+ );
682
+ });
683
+ });
684
+ }
685
+ return _onStart(props);
588
686
  });
589
- await this._tryCatch(() => _onStart());
590
687
  }
591
688
  );
592
689
  };
@@ -596,7 +693,6 @@ export class Agent<Env, State = unknown> extends Server<Env> {
596
693
  state: State,
597
694
  source: Connection | "server" = "server"
598
695
  ) {
599
- const previousState = this._state;
600
696
  this._state = state;
601
697
  this.sql`
602
698
  INSERT OR REPLACE INTO cf_agents_state (id, state)
@@ -609,23 +705,20 @@ export class Agent<Env, State = unknown> extends Server<Env> {
609
705
  this.broadcast(
610
706
  JSON.stringify({
611
707
  state: state,
612
- type: "cf_agent_state"
708
+ type: MessageType.CF_AGENT_STATE
613
709
  }),
614
710
  source !== "server" ? [source.id] : []
615
711
  );
616
712
  return this._tryCatch(() => {
617
- const { connection, request } = agentContext.getStore() || {};
713
+ const { connection, request, email } = agentContext.getStore() || {};
618
714
  return agentContext.run(
619
- { agent: this, connection, request },
715
+ { agent: this, connection, request, email },
620
716
  async () => {
621
717
  this.observability?.emit(
622
718
  {
623
719
  displayMessage: "State updated",
624
720
  id: nanoid(),
625
- payload: {
626
- previousState,
627
- state
628
- },
721
+ payload: {},
629
722
  timestamp: Date.now(),
630
723
  type: "state:update"
631
724
  },
@@ -656,19 +749,83 @@ export class Agent<Env, State = unknown> extends Server<Env> {
656
749
  }
657
750
 
658
751
  /**
659
- * Called when the Agent receives an email
752
+ * Called when the Agent receives an email via routeAgentEmail()
753
+ * Override this method to handle incoming emails
660
754
  * @param email Email message to process
661
755
  */
662
- // biome-ignore lint/correctness/noUnusedFunctionParameters: overridden later
663
- onEmail(email: ForwardableEmailMessage) {
756
+ async _onEmail(email: AgentEmail) {
757
+ // nb: we use this roundabout way of getting to onEmail
758
+ // because of https://github.com/cloudflare/workerd/issues/4499
664
759
  return agentContext.run(
665
- { agent: this, connection: undefined, request: undefined },
760
+ { agent: this, connection: undefined, request: undefined, email: email },
666
761
  async () => {
667
- console.error("onEmail not implemented");
762
+ if ("onEmail" in this && typeof this.onEmail === "function") {
763
+ return this._tryCatch(() =>
764
+ (this.onEmail as (email: AgentEmail) => Promise<void>)(email)
765
+ );
766
+ } else {
767
+ console.log("Received email from:", email.from, "to:", email.to);
768
+ console.log("Subject:", email.headers.get("subject"));
769
+ console.log(
770
+ "Implement onEmail(email: AgentEmail): Promise<void> in your agent to process emails"
771
+ );
772
+ }
668
773
  }
669
774
  );
670
775
  }
671
776
 
777
+ /**
778
+ * Reply to an email
779
+ * @param email The email to reply to
780
+ * @param options Options for the reply
781
+ * @returns void
782
+ */
783
+ async replyToEmail(
784
+ email: AgentEmail,
785
+ options: {
786
+ fromName: string;
787
+ subject?: string | undefined;
788
+ body: string;
789
+ contentType?: string;
790
+ headers?: Record<string, string>;
791
+ }
792
+ ): Promise<void> {
793
+ return this._tryCatch(async () => {
794
+ const agentName = camelCaseToKebabCase(this._ParentClass.name);
795
+ const agentId = this.name;
796
+
797
+ const { createMimeMessage } = await import("mimetext");
798
+ const msg = createMimeMessage();
799
+ msg.setSender({ addr: email.to, name: options.fromName });
800
+ msg.setRecipient(email.from);
801
+ msg.setSubject(
802
+ options.subject || `Re: ${email.headers.get("subject")}` || "No subject"
803
+ );
804
+ msg.addMessage({
805
+ contentType: options.contentType || "text/plain",
806
+ data: options.body
807
+ });
808
+
809
+ const domain = email.from.split("@")[1];
810
+ const messageId = `<${agentId}@${domain}>`;
811
+ msg.setHeader("In-Reply-To", email.headers.get("Message-ID")!);
812
+ msg.setHeader("Message-ID", messageId);
813
+ msg.setHeader("X-Agent-Name", agentName);
814
+ msg.setHeader("X-Agent-ID", agentId);
815
+
816
+ if (options.headers) {
817
+ for (const [key, value] of Object.entries(options.headers)) {
818
+ msg.setHeader(key, value);
819
+ }
820
+ }
821
+ await email.reply({
822
+ from: email.to,
823
+ raw: msg.asRaw(),
824
+ to: email.from
825
+ });
826
+ });
827
+ }
828
+
672
829
  private async _tryCatch<T>(fn: () => T | Promise<T>) {
673
830
  try {
674
831
  return await fn();
@@ -677,6 +834,68 @@ export class Agent<Env, State = unknown> extends Server<Env> {
677
834
  }
678
835
  }
679
836
 
837
+ /**
838
+ * Automatically wrap custom methods with agent context
839
+ * This ensures getCurrentAgent() works in all custom methods without decorators
840
+ */
841
+ private _autoWrapCustomMethods() {
842
+ // Collect all methods from base prototypes (Agent and Server)
843
+ const basePrototypes = [Agent.prototype, Server.prototype];
844
+ const baseMethods = new Set<string>();
845
+ for (const baseProto of basePrototypes) {
846
+ let proto = baseProto;
847
+ while (proto && proto !== Object.prototype) {
848
+ const methodNames = Object.getOwnPropertyNames(proto);
849
+ for (const methodName of methodNames) {
850
+ baseMethods.add(methodName);
851
+ }
852
+ proto = Object.getPrototypeOf(proto);
853
+ }
854
+ }
855
+ // Get all methods from the current instance's prototype chain
856
+ let proto = Object.getPrototypeOf(this);
857
+ let depth = 0;
858
+ while (proto && proto !== Object.prototype && depth < 10) {
859
+ const methodNames = Object.getOwnPropertyNames(proto);
860
+ for (const methodName of methodNames) {
861
+ const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
862
+
863
+ // Skip if it's a private method, a base method, a getter, or not a function,
864
+ if (
865
+ baseMethods.has(methodName) ||
866
+ methodName.startsWith("_") ||
867
+ !descriptor ||
868
+ !!descriptor.get ||
869
+ typeof descriptor.value !== "function"
870
+ ) {
871
+ continue;
872
+ }
873
+
874
+ // Now, methodName is confirmed to be a custom method/function
875
+ // Wrap the custom method with context
876
+ const wrappedFunction = withAgentContext(
877
+ // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
878
+ this[methodName as keyof this] as (...args: any[]) => any
879
+ // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
880
+ ) as any;
881
+
882
+ // if the method is callable, copy the metadata from the original method
883
+ if (this._isCallable(methodName)) {
884
+ callableMetadata.set(
885
+ wrappedFunction,
886
+ callableMetadata.get(this[methodName as keyof this] as Function)!
887
+ );
888
+ }
889
+
890
+ // set the wrapped function on the prototype
891
+ this.constructor.prototype[methodName as keyof this] = wrappedFunction;
892
+ }
893
+
894
+ proto = Object.getPrototypeOf(proto);
895
+ depth++;
896
+ }
897
+ }
898
+
680
899
  override onError(
681
900
  connection: Connection,
682
901
  error: unknown
@@ -711,6 +930,131 @@ export class Agent<Env, State = unknown> extends Server<Env> {
711
930
  throw new Error("Not implemented");
712
931
  }
713
932
 
933
+ /**
934
+ * Queue a task to be executed in the future
935
+ * @param payload Payload to pass to the callback
936
+ * @param callback Name of the method to call
937
+ * @returns The ID of the queued task
938
+ */
939
+ async queue<T = unknown>(callback: keyof this, payload: T): Promise<string> {
940
+ const id = nanoid(9);
941
+ if (typeof callback !== "string") {
942
+ throw new Error("Callback must be a string");
943
+ }
944
+
945
+ if (typeof this[callback] !== "function") {
946
+ throw new Error(`this.${callback} is not a function`);
947
+ }
948
+
949
+ this.sql`
950
+ INSERT OR REPLACE INTO cf_agents_queues (id, payload, callback)
951
+ VALUES (${id}, ${JSON.stringify(payload)}, ${callback})
952
+ `;
953
+
954
+ void this._flushQueue().catch((e) => {
955
+ console.error("Error flushing queue:", e);
956
+ });
957
+
958
+ return id;
959
+ }
960
+
961
+ private _flushingQueue = false;
962
+
963
+ private async _flushQueue() {
964
+ if (this._flushingQueue) {
965
+ return;
966
+ }
967
+ this._flushingQueue = true;
968
+ while (true) {
969
+ const result = this.sql<QueueItem<string>>`
970
+ SELECT * FROM cf_agents_queues
971
+ ORDER BY created_at ASC
972
+ `;
973
+
974
+ if (!result || result.length === 0) {
975
+ break;
976
+ }
977
+
978
+ for (const row of result || []) {
979
+ const callback = this[row.callback as keyof Agent<Env>];
980
+ if (!callback) {
981
+ console.error(`callback ${row.callback} not found`);
982
+ continue;
983
+ }
984
+ const { connection, request, email } = agentContext.getStore() || {};
985
+ await agentContext.run(
986
+ {
987
+ agent: this,
988
+ connection,
989
+ request,
990
+ email
991
+ },
992
+ async () => {
993
+ // TODO: add retries and backoff
994
+ await (
995
+ callback as (
996
+ payload: unknown,
997
+ queueItem: QueueItem<string>
998
+ ) => Promise<void>
999
+ ).bind(this)(JSON.parse(row.payload as string), row);
1000
+ await this.dequeue(row.id);
1001
+ }
1002
+ );
1003
+ }
1004
+ }
1005
+ this._flushingQueue = false;
1006
+ }
1007
+
1008
+ /**
1009
+ * Dequeue a task by ID
1010
+ * @param id ID of the task to dequeue
1011
+ */
1012
+ async dequeue(id: string) {
1013
+ this.sql`DELETE FROM cf_agents_queues WHERE id = ${id}`;
1014
+ }
1015
+
1016
+ /**
1017
+ * Dequeue all tasks
1018
+ */
1019
+ async dequeueAll() {
1020
+ this.sql`DELETE FROM cf_agents_queues`;
1021
+ }
1022
+
1023
+ /**
1024
+ * Dequeue all tasks by callback
1025
+ * @param callback Name of the callback to dequeue
1026
+ */
1027
+ async dequeueAllByCallback(callback: string) {
1028
+ this.sql`DELETE FROM cf_agents_queues WHERE callback = ${callback}`;
1029
+ }
1030
+
1031
+ /**
1032
+ * Get a queued task by ID
1033
+ * @param id ID of the task to get
1034
+ * @returns The task or undefined if not found
1035
+ */
1036
+ async getQueue(id: string): Promise<QueueItem<string> | undefined> {
1037
+ const result = this.sql<QueueItem<string>>`
1038
+ SELECT * FROM cf_agents_queues WHERE id = ${id}
1039
+ `;
1040
+ return result
1041
+ ? { ...result[0], payload: JSON.parse(result[0].payload) }
1042
+ : undefined;
1043
+ }
1044
+
1045
+ /**
1046
+ * Get all queues by key and value
1047
+ * @param key Key to filter by
1048
+ * @param value Value to filter by
1049
+ * @returns Array of matching QueueItem objects
1050
+ */
1051
+ async getQueues(key: string, value: string): Promise<QueueItem<string>[]> {
1052
+ const result = this.sql<QueueItem<string>>`
1053
+ SELECT * FROM cf_agents_queues
1054
+ `;
1055
+ return result.filter((row) => JSON.parse(row.payload)[key] === value);
1056
+ }
1057
+
714
1058
  /**
715
1059
  * Schedule a task to be executed in the future
716
1060
  * @template T Type of the payload data
@@ -731,7 +1075,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
731
1075
  {
732
1076
  displayMessage: `Schedule ${schedule.id} created`,
733
1077
  id: nanoid(),
734
- payload: schedule,
1078
+ payload: {
1079
+ callback: callback as string,
1080
+ id: id
1081
+ },
735
1082
  timestamp: Date.now(),
736
1083
  type: "schedule:create"
737
1084
  },
@@ -901,7 +1248,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
901
1248
  {
902
1249
  displayMessage: `Schedule ${id} cancelled`,
903
1250
  id: nanoid(),
904
- payload: schedule,
1251
+ payload: {
1252
+ callback: schedule.callback,
1253
+ id: schedule.id
1254
+ },
905
1255
  timestamp: Date.now(),
906
1256
  type: "schedule:cancel"
907
1257
  },
@@ -917,9 +1267,9 @@ export class Agent<Env, State = unknown> extends Server<Env> {
917
1267
  private async _scheduleNextAlarm() {
918
1268
  // Find the next schedule that needs to be executed
919
1269
  const result = this.sql`
920
- SELECT time FROM cf_agents_schedules
1270
+ SELECT time FROM cf_agents_schedules
921
1271
  WHERE time > ${Math.floor(Date.now() / 1000)}
922
- ORDER BY time ASC
1272
+ ORDER BY time ASC
923
1273
  LIMIT 1
924
1274
  `;
925
1275
  if (!result) return;
@@ -946,51 +1296,61 @@ export class Agent<Env, State = unknown> extends Server<Env> {
946
1296
  SELECT * FROM cf_agents_schedules WHERE time <= ${now}
947
1297
  `;
948
1298
 
949
- for (const row of result || []) {
950
- const callback = this[row.callback as keyof Agent<Env>];
951
- if (!callback) {
952
- console.error(`callback ${row.callback} not found`);
953
- continue;
954
- }
955
- await agentContext.run(
956
- { agent: this, connection: undefined, request: undefined },
957
- async () => {
958
- try {
959
- this.observability?.emit(
960
- {
961
- displayMessage: `Schedule ${row.id} executed`,
962
- id: nanoid(),
963
- payload: row,
964
- timestamp: Date.now(),
965
- type: "schedule:execute"
966
- },
967
- this.ctx
968
- );
1299
+ if (result && Array.isArray(result)) {
1300
+ for (const row of result) {
1301
+ const callback = this[row.callback as keyof Agent<Env>];
1302
+ if (!callback) {
1303
+ console.error(`callback ${row.callback} not found`);
1304
+ continue;
1305
+ }
1306
+ await agentContext.run(
1307
+ {
1308
+ agent: this,
1309
+ connection: undefined,
1310
+ request: undefined,
1311
+ email: undefined
1312
+ },
1313
+ async () => {
1314
+ try {
1315
+ this.observability?.emit(
1316
+ {
1317
+ displayMessage: `Schedule ${row.id} executed`,
1318
+ id: nanoid(),
1319
+ payload: {
1320
+ callback: row.callback,
1321
+ id: row.id
1322
+ },
1323
+ timestamp: Date.now(),
1324
+ type: "schedule:execute"
1325
+ },
1326
+ this.ctx
1327
+ );
969
1328
 
970
- await (
971
- callback as (
972
- payload: unknown,
973
- schedule: Schedule<unknown>
974
- ) => Promise<void>
975
- ).bind(this)(JSON.parse(row.payload as string), row);
976
- } catch (e) {
977
- console.error(`error executing callback "${row.callback}"`, e);
1329
+ await (
1330
+ callback as (
1331
+ payload: unknown,
1332
+ schedule: Schedule<unknown>
1333
+ ) => Promise<void>
1334
+ ).bind(this)(JSON.parse(row.payload as string), row);
1335
+ } catch (e) {
1336
+ console.error(`error executing callback "${row.callback}"`, e);
1337
+ }
978
1338
  }
979
- }
980
- );
981
- if (row.type === "cron") {
982
- // Update next execution time for cron schedules
983
- const nextExecutionTime = getNextCronTime(row.cron);
984
- const nextTimestamp = Math.floor(nextExecutionTime.getTime() / 1000);
1339
+ );
1340
+ if (row.type === "cron") {
1341
+ // Update next execution time for cron schedules
1342
+ const nextExecutionTime = getNextCronTime(row.cron);
1343
+ const nextTimestamp = Math.floor(nextExecutionTime.getTime() / 1000);
985
1344
 
986
- this.sql`
1345
+ this.sql`
987
1346
  UPDATE cf_agents_schedules SET time = ${nextTimestamp} WHERE id = ${row.id}
988
1347
  `;
989
- } else {
990
- // Delete one-time schedules after execution
991
- this.sql`
1348
+ } else {
1349
+ // Delete one-time schedules after execution
1350
+ this.sql`
992
1351
  DELETE FROM cf_agents_schedules WHERE id = ${row.id}
993
1352
  `;
1353
+ }
994
1354
  }
995
1355
  }
996
1356
 
@@ -1006,6 +1366,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1006
1366
  this.sql`DROP TABLE IF EXISTS cf_agents_state`;
1007
1367
  this.sql`DROP TABLE IF EXISTS cf_agents_schedules`;
1008
1368
  this.sql`DROP TABLE IF EXISTS cf_agents_mcp_servers`;
1369
+ this.sql`DROP TABLE IF EXISTS cf_agents_queues`;
1009
1370
 
1010
1371
  // delete all alarms
1011
1372
  await this.ctx.storage.deleteAlarm();
@@ -1078,7 +1439,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1078
1439
  this.broadcast(
1079
1440
  JSON.stringify({
1080
1441
  mcp: this.getMcpServers(),
1081
- type: "cf_agent_mcp_servers"
1442
+ type: MessageType.CF_AGENT_MCP_SERVERS
1082
1443
  })
1083
1444
  );
1084
1445
 
@@ -1167,7 +1528,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1167
1528
  this.broadcast(
1168
1529
  JSON.stringify({
1169
1530
  mcp: this.getMcpServers(),
1170
- type: "cf_agent_mcp_servers"
1531
+ type: MessageType.CF_AGENT_MCP_SERVERS
1171
1532
  })
1172
1533
  );
1173
1534
  }
@@ -1184,17 +1545,19 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1184
1545
  SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
1185
1546
  `;
1186
1547
 
1187
- for (const server of servers) {
1188
- const serverConn = this.mcp.mcpConnections[server.id];
1189
- mcpState.servers[server.id] = {
1190
- auth_url: server.auth_url,
1191
- capabilities: serverConn?.serverCapabilities ?? null,
1192
- instructions: serverConn?.instructions ?? null,
1193
- name: server.name,
1194
- server_url: server.server_url,
1195
- // mark as "authenticating" because the server isn't automatically connected, so it's pending authenticating
1196
- state: serverConn?.connectionState ?? "authenticating"
1197
- };
1548
+ if (servers && Array.isArray(servers) && servers.length > 0) {
1549
+ for (const server of servers) {
1550
+ const serverConn = this.mcp.mcpConnections[server.id];
1551
+ mcpState.servers[server.id] = {
1552
+ auth_url: server.auth_url,
1553
+ capabilities: serverConn?.serverCapabilities ?? null,
1554
+ instructions: serverConn?.instructions ?? null,
1555
+ name: server.name,
1556
+ server_url: server.server_url,
1557
+ // mark as "authenticating" because the server isn't automatically connected, so it's pending authenticating
1558
+ state: serverConn?.connectionState ?? "authenticating"
1559
+ };
1560
+ }
1198
1561
  }
1199
1562
 
1200
1563
  return mcpState;
@@ -1281,17 +1644,231 @@ export async function routeAgentRequest<Env>(
1281
1644
  return response;
1282
1645
  }
1283
1646
 
1647
+ export type EmailResolver<Env> = (
1648
+ email: ForwardableEmailMessage,
1649
+ env: Env
1650
+ ) => Promise<{
1651
+ agentName: string;
1652
+ agentId: string;
1653
+ } | null>;
1654
+
1655
+ /**
1656
+ * Create a resolver that uses the message-id header to determine the agent to route the email to
1657
+ * @returns A function that resolves the agent to route the email to
1658
+ */
1659
+ export function createHeaderBasedEmailResolver<Env>(): EmailResolver<Env> {
1660
+ return async (email: ForwardableEmailMessage, _env: Env) => {
1661
+ const messageId = email.headers.get("message-id");
1662
+ if (messageId) {
1663
+ const messageIdMatch = messageId.match(/<([^@]+)@([^>]+)>/);
1664
+ if (messageIdMatch) {
1665
+ const [, agentId, domain] = messageIdMatch;
1666
+ const agentName = domain.split(".")[0];
1667
+ return { agentName, agentId };
1668
+ }
1669
+ }
1670
+
1671
+ const references = email.headers.get("references");
1672
+ if (references) {
1673
+ const referencesMatch = references.match(
1674
+ /<([A-Za-z0-9+/]{43}=)@([^>]+)>/
1675
+ );
1676
+ if (referencesMatch) {
1677
+ const [, base64Id, domain] = referencesMatch;
1678
+ const agentId = Buffer.from(base64Id, "base64").toString("hex");
1679
+ const agentName = domain.split(".")[0];
1680
+ return { agentName, agentId };
1681
+ }
1682
+ }
1683
+
1684
+ const agentName = email.headers.get("x-agent-name");
1685
+ const agentId = email.headers.get("x-agent-id");
1686
+ if (agentName && agentId) {
1687
+ return { agentName, agentId };
1688
+ }
1689
+
1690
+ return null;
1691
+ };
1692
+ }
1693
+
1694
+ /**
1695
+ * Create a resolver that uses the email address to determine the agent to route the email to
1696
+ * @param defaultAgentName The default agent name to use if the email address does not contain a sub-address
1697
+ * @returns A function that resolves the agent to route the email to
1698
+ */
1699
+ export function createAddressBasedEmailResolver<Env>(
1700
+ defaultAgentName: string
1701
+ ): EmailResolver<Env> {
1702
+ return async (email: ForwardableEmailMessage, _env: Env) => {
1703
+ const emailMatch = email.to.match(/^([^+@]+)(?:\+([^@]+))?@(.+)$/);
1704
+ if (!emailMatch) {
1705
+ return null;
1706
+ }
1707
+
1708
+ const [, localPart, subAddress] = emailMatch;
1709
+
1710
+ if (subAddress) {
1711
+ return {
1712
+ agentName: localPart,
1713
+ agentId: subAddress
1714
+ };
1715
+ }
1716
+
1717
+ // Option 2: Use defaultAgentName namespace, localPart as agentId
1718
+ // Common for catch-all email routing to a single EmailAgent namespace
1719
+ return {
1720
+ agentName: defaultAgentName,
1721
+ agentId: localPart
1722
+ };
1723
+ };
1724
+ }
1725
+
1726
+ /**
1727
+ * Create a resolver that uses the agentName and agentId to determine the agent to route the email to
1728
+ * @param agentName The name of the agent to route the email to
1729
+ * @param agentId The id of the agent to route the email to
1730
+ * @returns A function that resolves the agent to route the email to
1731
+ */
1732
+ export function createCatchAllEmailResolver<Env>(
1733
+ agentName: string,
1734
+ agentId: string
1735
+ ): EmailResolver<Env> {
1736
+ return async () => ({ agentName, agentId });
1737
+ }
1738
+
1739
+ export type EmailRoutingOptions<Env> = AgentOptions<Env> & {
1740
+ resolver: EmailResolver<Env>;
1741
+ };
1742
+
1743
+ // Cache the agent namespace map for email routing
1744
+ // This maps both kebab-case and original names to namespaces
1745
+ const agentMapCache = new WeakMap<
1746
+ Record<string, unknown>,
1747
+ Record<string, unknown>
1748
+ >();
1749
+
1284
1750
  /**
1285
1751
  * Route an email to the appropriate Agent
1286
- * @param email Email message to route
1287
- * @param env Environment containing Agent bindings
1288
- * @param options Routing options
1752
+ * @param email The email to route
1753
+ * @param env The environment containing the Agent bindings
1754
+ * @param options The options for routing the email
1755
+ * @returns A promise that resolves when the email has been routed
1289
1756
  */
1290
1757
  export async function routeAgentEmail<Env>(
1291
- _email: ForwardableEmailMessage,
1292
- _env: Env,
1293
- _options?: AgentOptions<Env>
1294
- ): Promise<void> {}
1758
+ email: ForwardableEmailMessage,
1759
+ env: Env,
1760
+ options: EmailRoutingOptions<Env>
1761
+ ): Promise<void> {
1762
+ const routingInfo = await options.resolver(email, env);
1763
+
1764
+ if (!routingInfo) {
1765
+ console.warn("No routing information found for email, dropping message");
1766
+ return;
1767
+ }
1768
+
1769
+ // Build a map that includes both original names and kebab-case versions
1770
+ if (!agentMapCache.has(env as Record<string, unknown>)) {
1771
+ const map: Record<string, unknown> = {};
1772
+ for (const [key, value] of Object.entries(env as Record<string, unknown>)) {
1773
+ if (
1774
+ value &&
1775
+ typeof value === "object" &&
1776
+ "idFromName" in value &&
1777
+ typeof value.idFromName === "function"
1778
+ ) {
1779
+ // Add both the original name and kebab-case version
1780
+ map[key] = value;
1781
+ map[camelCaseToKebabCase(key)] = value;
1782
+ }
1783
+ }
1784
+ agentMapCache.set(env as Record<string, unknown>, map);
1785
+ }
1786
+
1787
+ const agentMap = agentMapCache.get(env as Record<string, unknown>)!;
1788
+ const namespace = agentMap[routingInfo.agentName];
1789
+
1790
+ if (!namespace) {
1791
+ // Provide helpful error message listing available agents
1792
+ const availableAgents = Object.keys(agentMap)
1793
+ .filter((key) => !key.includes("-")) // Show only original names, not kebab-case duplicates
1794
+ .join(", ");
1795
+ throw new Error(
1796
+ `Agent namespace '${routingInfo.agentName}' not found in environment. Available agents: ${availableAgents}`
1797
+ );
1798
+ }
1799
+
1800
+ const agent = await getAgentByName(
1801
+ namespace as unknown as AgentNamespace<Agent<Env>>,
1802
+ routingInfo.agentId
1803
+ );
1804
+
1805
+ // let's make a serialisable version of the email
1806
+ const serialisableEmail: AgentEmail = {
1807
+ getRaw: async () => {
1808
+ const reader = email.raw.getReader();
1809
+ const chunks: Uint8Array[] = [];
1810
+
1811
+ let done = false;
1812
+ while (!done) {
1813
+ const { value, done: readerDone } = await reader.read();
1814
+ done = readerDone;
1815
+ if (value) {
1816
+ chunks.push(value);
1817
+ }
1818
+ }
1819
+
1820
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
1821
+ const combined = new Uint8Array(totalLength);
1822
+ let offset = 0;
1823
+ for (const chunk of chunks) {
1824
+ combined.set(chunk, offset);
1825
+ offset += chunk.length;
1826
+ }
1827
+
1828
+ return combined;
1829
+ },
1830
+ headers: email.headers,
1831
+ rawSize: email.rawSize,
1832
+ setReject: (reason: string) => {
1833
+ email.setReject(reason);
1834
+ },
1835
+ forward: (rcptTo: string, headers?: Headers) => {
1836
+ return email.forward(rcptTo, headers);
1837
+ },
1838
+ reply: (options: { from: string; to: string; raw: string }) => {
1839
+ return email.reply(
1840
+ new EmailMessage(options.from, options.to, options.raw)
1841
+ );
1842
+ },
1843
+ from: email.from,
1844
+ to: email.to
1845
+ };
1846
+
1847
+ await agent._onEmail(serialisableEmail);
1848
+ }
1849
+
1850
+ export type AgentEmail = {
1851
+ from: string;
1852
+ to: string;
1853
+ getRaw: () => Promise<Uint8Array>;
1854
+ headers: Headers;
1855
+ rawSize: number;
1856
+ setReject: (reason: string) => void;
1857
+ forward: (rcptTo: string, headers?: Headers) => Promise<void>;
1858
+ reply: (options: { from: string; to: string; raw: string }) => Promise<void>;
1859
+ };
1860
+
1861
+ export type EmailSendOptions = {
1862
+ to: string;
1863
+ subject: string;
1864
+ body: string;
1865
+ contentType?: string;
1866
+ headers?: Record<string, string>;
1867
+ includeRoutingHeaders?: boolean;
1868
+ agentName?: string;
1869
+ agentId?: string;
1870
+ domain?: string;
1871
+ };
1295
1872
 
1296
1873
  /**
1297
1874
  * Get or create an Agent by name
@@ -1302,12 +1879,17 @@ export async function routeAgentEmail<Env>(
1302
1879
  * @param options Options for Agent creation
1303
1880
  * @returns Promise resolving to an Agent instance stub
1304
1881
  */
1305
- export async function getAgentByName<Env, T extends Agent<Env>>(
1882
+ export async function getAgentByName<
1883
+ Env,
1884
+ T extends Agent<Env>,
1885
+ Props extends Record<string, unknown> = Record<string, unknown>
1886
+ >(
1306
1887
  namespace: AgentNamespace<T>,
1307
1888
  name: string,
1308
1889
  options?: {
1309
1890
  jurisdiction?: DurableObjectJurisdiction;
1310
1891
  locationHint?: DurableObjectLocationHint;
1892
+ props?: Props;
1311
1893
  }
1312
1894
  ) {
1313
1895
  return getServerByName<Env, T>(namespace, name, options);
@@ -1339,7 +1921,7 @@ export class StreamingResponse {
1339
1921
  id: this._id,
1340
1922
  result: chunk,
1341
1923
  success: true,
1342
- type: "rpc"
1924
+ type: MessageType.RPC
1343
1925
  };
1344
1926
  this._connection.send(JSON.stringify(response));
1345
1927
  }
@@ -1358,7 +1940,7 @@ export class StreamingResponse {
1358
1940
  id: this._id,
1359
1941
  result: finalChunk,
1360
1942
  success: true,
1361
- type: "rpc"
1943
+ type: MessageType.RPC
1362
1944
  };
1363
1945
  this._connection.send(JSON.stringify(response));
1364
1946
  }