agents 0.0.0-22d140b → 0.0.0-23db655
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +255 -27
- package/dist/_esm-LV5FJ3HK.js +3922 -0
- package/dist/_esm-LV5FJ3HK.js.map +1 -0
- package/dist/ai-chat-agent.d.ts +11 -7
- package/dist/ai-chat-agent.js +169 -47
- package/dist/ai-chat-agent.js.map +1 -1
- package/dist/ai-chat-v5-migration.d.ts +152 -0
- package/dist/ai-chat-v5-migration.js +20 -0
- package/dist/ai-chat-v5-migration.js.map +1 -0
- package/dist/ai-react.d.ts +67 -70
- package/dist/ai-react.js +178 -37
- package/dist/ai-react.js.map +1 -1
- package/dist/ai-types.d.ts +37 -19
- package/dist/ai-types.js +7 -0
- package/dist/ccip-CMBYN64O.js +15 -0
- package/dist/ccip-CMBYN64O.js.map +1 -0
- package/dist/chunk-5Y6BEZDY.js +276 -0
- package/dist/chunk-5Y6BEZDY.js.map +1 -0
- package/dist/chunk-BER7KXUJ.js +18 -0
- package/dist/chunk-BER7KXUJ.js.map +1 -0
- package/dist/{chunk-4CIGD73X.js → chunk-HS7VEROK.js} +610 -101
- package/dist/chunk-HS7VEROK.js.map +1 -0
- package/dist/chunk-JJBFIGUC.js +5202 -0
- package/dist/chunk-JJBFIGUC.js.map +1 -0
- package/dist/{chunk-767EASBA.js → chunk-LL2AFX7V.js} +5 -2
- package/dist/chunk-LL2AFX7V.js.map +1 -0
- package/dist/chunk-PR4QN5HX.js +43 -0
- package/dist/chunk-PR4QN5HX.js.map +1 -0
- package/dist/{chunk-NKZZ66QY.js → chunk-QEVM4BVL.js} +5 -5
- package/dist/chunk-QEVM4BVL.js.map +1 -0
- package/dist/{chunk-E3LCYPCB.js → chunk-SKACXF37.js} +164 -21
- package/dist/chunk-SKACXF37.js.map +1 -0
- package/dist/chunk-TYAY6AU6.js +159 -0
- package/dist/chunk-TYAY6AU6.js.map +1 -0
- package/dist/chunk-UJVEAURM.js +150 -0
- package/dist/chunk-UJVEAURM.js.map +1 -0
- package/dist/client-CvaJdLQA.d.ts +5015 -0
- package/dist/client.d.ts +2 -2
- package/dist/client.js +3 -1
- package/dist/index.d.ts +172 -20
- package/dist/index.js +14 -4
- package/dist/mcp/client.d.ts +9 -1053
- package/dist/mcp/client.js +2 -1
- package/dist/mcp/do-oauth-client-provider.d.ts +1 -0
- package/dist/mcp/do-oauth-client-provider.js +2 -1
- package/dist/mcp/index.d.ts +64 -56
- package/dist/mcp/index.js +953 -637
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/x402.d.ts +31 -0
- package/dist/mcp/x402.js +3195 -0
- package/dist/mcp/x402.js.map +1 -0
- package/dist/observability/index.d.ts +46 -0
- package/dist/observability/index.js +12 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/react.d.ts +9 -5
- package/dist/react.js +8 -5
- package/dist/react.js.map +1 -1
- package/dist/schedule.d.ts +79 -5
- package/dist/schedule.js +17 -2
- package/dist/schedule.js.map +1 -1
- package/dist/secp256k1-M22GZP2U.js +2193 -0
- package/dist/secp256k1-M22GZP2U.js.map +1 -0
- package/package.json +30 -8
- package/src/index.ts +836 -140
- package/dist/chunk-4CIGD73X.js.map +0 -1
- package/dist/chunk-767EASBA.js.map +0 -1
- package/dist/chunk-E3LCYPCB.js.map +0 -1
- package/dist/chunk-NKZZ66QY.js.map +0 -1
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";
|
|
@@ -6,23 +7,26 @@ import type {
|
|
|
6
7
|
Prompt,
|
|
7
8
|
Resource,
|
|
8
9
|
ServerCapabilities,
|
|
9
|
-
Tool
|
|
10
|
+
Tool
|
|
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
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";
|
|
28
|
+
import { genericObservability, type Observability } from "./observability";
|
|
29
|
+
import { MessageType } from "./ai-types";
|
|
26
30
|
|
|
27
31
|
export type { Connection, ConnectionContext, WSMessage } from "partyserver";
|
|
28
32
|
|
|
@@ -40,7 +44,7 @@ export type RPCRequest = {
|
|
|
40
44
|
* State update message from client
|
|
41
45
|
*/
|
|
42
46
|
export type StateUpdateMessage = {
|
|
43
|
-
type:
|
|
47
|
+
type: MessageType.CF_AGENT_STATE;
|
|
44
48
|
state: unknown;
|
|
45
49
|
};
|
|
46
50
|
|
|
@@ -48,7 +52,7 @@ export type StateUpdateMessage = {
|
|
|
48
52
|
* RPC response message to client
|
|
49
53
|
*/
|
|
50
54
|
export type RPCResponse = {
|
|
51
|
-
type:
|
|
55
|
+
type: MessageType.RPC;
|
|
52
56
|
id: string;
|
|
53
57
|
} & (
|
|
54
58
|
| {
|
|
@@ -75,7 +79,7 @@ function isRPCRequest(msg: unknown): msg is RPCRequest {
|
|
|
75
79
|
typeof msg === "object" &&
|
|
76
80
|
msg !== null &&
|
|
77
81
|
"type" in msg &&
|
|
78
|
-
msg.type ===
|
|
82
|
+
msg.type === MessageType.RPC &&
|
|
79
83
|
"id" in msg &&
|
|
80
84
|
typeof msg.id === "string" &&
|
|
81
85
|
"method" in msg &&
|
|
@@ -93,7 +97,7 @@ function isStateUpdateMessage(msg: unknown): msg is StateUpdateMessage {
|
|
|
93
97
|
typeof msg === "object" &&
|
|
94
98
|
msg !== null &&
|
|
95
99
|
"type" in msg &&
|
|
96
|
-
msg.type ===
|
|
100
|
+
msg.type === MessageType.CF_AGENT_STATE &&
|
|
97
101
|
"state" in msg
|
|
98
102
|
);
|
|
99
103
|
}
|
|
@@ -114,7 +118,7 @@ const callableMetadata = new Map<Function, CallableMetadata>();
|
|
|
114
118
|
* Decorator that marks a method as callable by clients
|
|
115
119
|
* @param metadata Optional metadata about the callable method
|
|
116
120
|
*/
|
|
117
|
-
export function
|
|
121
|
+
export function callable(metadata: CallableMetadata = {}) {
|
|
118
122
|
return function callableDecorator<This, Args extends unknown[], Return>(
|
|
119
123
|
target: (this: This, ...args: Args) => Return,
|
|
120
124
|
// biome-ignore lint/correctness/noUnusedFunctionParameters: later
|
|
@@ -128,6 +132,30 @@ export function unstable_callable(metadata: CallableMetadata = {}) {
|
|
|
128
132
|
};
|
|
129
133
|
}
|
|
130
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
|
+
|
|
131
159
|
/**
|
|
132
160
|
* Represents a scheduled task within an Agent
|
|
133
161
|
* @template T Type of the payload data
|
|
@@ -173,7 +201,7 @@ function getNextCronTime(cron: string) {
|
|
|
173
201
|
* MCP Server state update message from server -> Client
|
|
174
202
|
*/
|
|
175
203
|
export type MCPServerMessage = {
|
|
176
|
-
type:
|
|
204
|
+
type: MessageType.CF_AGENT_MCP_SERVERS;
|
|
177
205
|
mcp: MCPServersState;
|
|
178
206
|
};
|
|
179
207
|
|
|
@@ -217,23 +245,26 @@ const STATE_WAS_CHANGED = "cf_state_was_changed";
|
|
|
217
245
|
const DEFAULT_STATE = {} as unknown;
|
|
218
246
|
|
|
219
247
|
const agentContext = new AsyncLocalStorage<{
|
|
220
|
-
agent: Agent<unknown>;
|
|
248
|
+
agent: Agent<unknown, unknown>;
|
|
221
249
|
connection: Connection | undefined;
|
|
222
250
|
request: Request | undefined;
|
|
251
|
+
email: AgentEmail | undefined;
|
|
223
252
|
}>();
|
|
224
253
|
|
|
225
254
|
export function getCurrentAgent<
|
|
226
|
-
T extends Agent<unknown, unknown> = Agent<unknown, unknown
|
|
255
|
+
T extends Agent<unknown, unknown> = Agent<unknown, unknown>
|
|
227
256
|
>(): {
|
|
228
257
|
agent: T | undefined;
|
|
229
258
|
connection: Connection | undefined;
|
|
230
|
-
request: Request
|
|
259
|
+
request: Request | undefined;
|
|
260
|
+
email: AgentEmail | undefined;
|
|
231
261
|
} {
|
|
232
262
|
const store = agentContext.getStore() as
|
|
233
263
|
| {
|
|
234
264
|
agent: T;
|
|
235
265
|
connection: Connection | undefined;
|
|
236
|
-
request: Request
|
|
266
|
+
request: Request | undefined;
|
|
267
|
+
email: AgentEmail | undefined;
|
|
237
268
|
}
|
|
238
269
|
| undefined;
|
|
239
270
|
if (!store) {
|
|
@@ -241,17 +272,47 @@ export function getCurrentAgent<
|
|
|
241
272
|
agent: undefined,
|
|
242
273
|
connection: undefined,
|
|
243
274
|
request: undefined,
|
|
275
|
+
email: undefined
|
|
244
276
|
};
|
|
245
277
|
}
|
|
246
278
|
return store;
|
|
247
279
|
}
|
|
248
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
|
+
|
|
249
306
|
/**
|
|
250
307
|
* Base class for creating Agent implementations
|
|
251
308
|
* @template Env Environment type containing bindings
|
|
252
309
|
* @template State State type to store within the Agent
|
|
253
310
|
*/
|
|
254
|
-
export class Agent<
|
|
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> {
|
|
255
316
|
private _state = DEFAULT_STATE as State;
|
|
256
317
|
|
|
257
318
|
private _ParentClass: typeof Agent<Env, State> =
|
|
@@ -313,9 +374,14 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
313
374
|
*/
|
|
314
375
|
static options = {
|
|
315
376
|
/** Whether the Agent should hibernate when inactive */
|
|
316
|
-
hibernate: true
|
|
377
|
+
hibernate: true // default to hibernate
|
|
317
378
|
};
|
|
318
379
|
|
|
380
|
+
/**
|
|
381
|
+
* The observability implementation to use for the Agent
|
|
382
|
+
*/
|
|
383
|
+
observability?: Observability = genericObservability;
|
|
384
|
+
|
|
319
385
|
/**
|
|
320
386
|
* Execute SQL queries against the Agent's database
|
|
321
387
|
* @template T Type of the returned rows
|
|
@@ -345,6 +411,12 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
345
411
|
constructor(ctx: AgentContext, env: Env) {
|
|
346
412
|
super(ctx, env);
|
|
347
413
|
|
|
414
|
+
if (!wrappedClasses.has(this.constructor)) {
|
|
415
|
+
// Auto-wrap custom methods with agent context
|
|
416
|
+
this._autoWrapCustomMethods();
|
|
417
|
+
wrappedClasses.add(this.constructor);
|
|
418
|
+
}
|
|
419
|
+
|
|
348
420
|
this.sql`
|
|
349
421
|
CREATE TABLE IF NOT EXISTS cf_agents_state (
|
|
350
422
|
id TEXT PRIMARY KEY NOT NULL,
|
|
@@ -352,6 +424,15 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
352
424
|
)
|
|
353
425
|
`;
|
|
354
426
|
|
|
427
|
+
this.sql`
|
|
428
|
+
CREATE TABLE IF NOT EXISTS cf_agents_queues (
|
|
429
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
430
|
+
payload TEXT,
|
|
431
|
+
callback TEXT,
|
|
432
|
+
created_at INTEGER DEFAULT (unixepoch())
|
|
433
|
+
)
|
|
434
|
+
`;
|
|
435
|
+
|
|
355
436
|
void this.ctx.blockConcurrencyWhile(async () => {
|
|
356
437
|
return this._tryCatch(async () => {
|
|
357
438
|
// Create alarms table if it doesn't exist
|
|
@@ -388,7 +469,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
388
469
|
const _onRequest = this.onRequest.bind(this);
|
|
389
470
|
this.onRequest = (request: Request) => {
|
|
390
471
|
return agentContext.run(
|
|
391
|
-
{ agent: this, connection: undefined, request },
|
|
472
|
+
{ agent: this, connection: undefined, request, email: undefined },
|
|
392
473
|
async () => {
|
|
393
474
|
if (this.mcp.isCallbackRequest(request)) {
|
|
394
475
|
await this.mcp.handleCallbackRequest(request);
|
|
@@ -397,14 +478,14 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
397
478
|
this.broadcast(
|
|
398
479
|
JSON.stringify({
|
|
399
480
|
mcp: this.getMcpServers(),
|
|
400
|
-
type:
|
|
481
|
+
type: MessageType.CF_AGENT_MCP_SERVERS
|
|
401
482
|
})
|
|
402
483
|
);
|
|
403
484
|
|
|
404
485
|
// We probably should let the user configure this response/redirect, but this is fine for now.
|
|
405
486
|
return new Response("<script>window.close();</script>", {
|
|
406
487
|
headers: { "content-type": "text/html" },
|
|
407
|
-
status: 200
|
|
488
|
+
status: 200
|
|
408
489
|
});
|
|
409
490
|
}
|
|
410
491
|
|
|
@@ -416,7 +497,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
416
497
|
const _onMessage = this.onMessage.bind(this);
|
|
417
498
|
this.onMessage = async (connection: Connection, message: WSMessage) => {
|
|
418
499
|
return agentContext.run(
|
|
419
|
-
{ agent: this, connection, request: undefined },
|
|
500
|
+
{ agent: this, connection, request: undefined, email: undefined },
|
|
420
501
|
async () => {
|
|
421
502
|
if (typeof message !== "string") {
|
|
422
503
|
return this._tryCatch(() => _onMessage(connection, message));
|
|
@@ -460,12 +541,27 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
460
541
|
|
|
461
542
|
// For regular methods, execute and send response
|
|
462
543
|
const result = await methodFn.apply(this, args);
|
|
544
|
+
|
|
545
|
+
this.observability?.emit(
|
|
546
|
+
{
|
|
547
|
+
displayMessage: `RPC call to ${method}`,
|
|
548
|
+
id: nanoid(),
|
|
549
|
+
payload: {
|
|
550
|
+
method,
|
|
551
|
+
streaming: metadata?.streaming
|
|
552
|
+
},
|
|
553
|
+
timestamp: Date.now(),
|
|
554
|
+
type: "rpc"
|
|
555
|
+
},
|
|
556
|
+
this.ctx
|
|
557
|
+
);
|
|
558
|
+
|
|
463
559
|
const response: RPCResponse = {
|
|
464
560
|
done: true,
|
|
465
561
|
id,
|
|
466
562
|
result,
|
|
467
563
|
success: true,
|
|
468
|
-
type:
|
|
564
|
+
type: MessageType.RPC
|
|
469
565
|
};
|
|
470
566
|
connection.send(JSON.stringify(response));
|
|
471
567
|
} catch (e) {
|
|
@@ -475,7 +571,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
475
571
|
e instanceof Error ? e.message : "Unknown error occurred",
|
|
476
572
|
id: parsed.id,
|
|
477
573
|
success: false,
|
|
478
|
-
type:
|
|
574
|
+
type: MessageType.RPC
|
|
479
575
|
};
|
|
480
576
|
connection.send(JSON.stringify(response));
|
|
481
577
|
console.error("RPC error:", e);
|
|
@@ -493,65 +589,104 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
493
589
|
// TODO: This is a hack to ensure the state is sent after the connection is established
|
|
494
590
|
// must fix this
|
|
495
591
|
return agentContext.run(
|
|
496
|
-
{ agent: this, connection, request: ctx.request },
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
if (this.state) {
|
|
500
|
-
connection.send(
|
|
501
|
-
JSON.stringify({
|
|
502
|
-
state: this.state,
|
|
503
|
-
type: "cf_agent_state",
|
|
504
|
-
})
|
|
505
|
-
);
|
|
506
|
-
}
|
|
507
|
-
|
|
592
|
+
{ agent: this, connection, request: ctx.request, email: undefined },
|
|
593
|
+
() => {
|
|
594
|
+
if (this.state) {
|
|
508
595
|
connection.send(
|
|
509
596
|
JSON.stringify({
|
|
510
|
-
|
|
511
|
-
type:
|
|
597
|
+
state: this.state,
|
|
598
|
+
type: MessageType.CF_AGENT_STATE
|
|
512
599
|
})
|
|
513
600
|
);
|
|
601
|
+
}
|
|
514
602
|
|
|
515
|
-
|
|
516
|
-
|
|
603
|
+
connection.send(
|
|
604
|
+
JSON.stringify({
|
|
605
|
+
mcp: this.getMcpServers(),
|
|
606
|
+
type: MessageType.CF_AGENT_MCP_SERVERS
|
|
607
|
+
})
|
|
608
|
+
);
|
|
609
|
+
|
|
610
|
+
this.observability?.emit(
|
|
611
|
+
{
|
|
612
|
+
displayMessage: "Connection established",
|
|
613
|
+
id: nanoid(),
|
|
614
|
+
payload: {
|
|
615
|
+
connectionId: connection.id
|
|
616
|
+
},
|
|
617
|
+
timestamp: Date.now(),
|
|
618
|
+
type: "connect"
|
|
619
|
+
},
|
|
620
|
+
this.ctx
|
|
621
|
+
);
|
|
622
|
+
return this._tryCatch(() => _onConnect(connection, ctx));
|
|
517
623
|
}
|
|
518
624
|
);
|
|
519
625
|
};
|
|
520
626
|
|
|
521
627
|
const _onStart = this.onStart.bind(this);
|
|
522
|
-
this.onStart = async () => {
|
|
628
|
+
this.onStart = async (props?: Props) => {
|
|
523
629
|
return agentContext.run(
|
|
524
|
-
{
|
|
630
|
+
{
|
|
631
|
+
agent: this,
|
|
632
|
+
connection: undefined,
|
|
633
|
+
request: undefined,
|
|
634
|
+
email: undefined
|
|
635
|
+
},
|
|
525
636
|
async () => {
|
|
526
|
-
|
|
637
|
+
await this._tryCatch(() => {
|
|
638
|
+
const servers = this.sql<MCPServerRow>`
|
|
527
639
|
SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
|
|
528
640
|
`;
|
|
529
641
|
|
|
530
|
-
// from DO storage, reconnect to all servers not currently in the oauth flow using our saved auth information
|
|
531
|
-
Promise.allSettled(
|
|
532
|
-
servers.map((server) => {
|
|
533
|
-
return this._connectToMcpServerInternal(
|
|
534
|
-
server.name,
|
|
535
|
-
server.server_url,
|
|
536
|
-
server.callback_url,
|
|
537
|
-
server.server_options
|
|
538
|
-
? JSON.parse(server.server_options)
|
|
539
|
-
: undefined,
|
|
540
|
-
{
|
|
541
|
-
id: server.id,
|
|
542
|
-
oauthClientId: server.client_id ?? undefined,
|
|
543
|
-
}
|
|
544
|
-
);
|
|
545
|
-
})
|
|
546
|
-
).then((_results) => {
|
|
547
642
|
this.broadcast(
|
|
548
643
|
JSON.stringify({
|
|
549
644
|
mcp: this.getMcpServers(),
|
|
550
|
-
type:
|
|
645
|
+
type: MessageType.CF_AGENT_MCP_SERVERS
|
|
551
646
|
})
|
|
552
647
|
);
|
|
648
|
+
|
|
649
|
+
// from DO storage, reconnect to all servers not currently in the oauth flow using our saved auth information
|
|
650
|
+
if (servers && Array.isArray(servers) && servers.length > 0) {
|
|
651
|
+
servers.forEach((server) => {
|
|
652
|
+
this._connectToMcpServerInternal(
|
|
653
|
+
server.name,
|
|
654
|
+
server.server_url,
|
|
655
|
+
server.callback_url,
|
|
656
|
+
server.server_options
|
|
657
|
+
? JSON.parse(server.server_options)
|
|
658
|
+
: undefined,
|
|
659
|
+
{
|
|
660
|
+
id: server.id,
|
|
661
|
+
oauthClientId: server.client_id ?? undefined
|
|
662
|
+
}
|
|
663
|
+
)
|
|
664
|
+
.then(() => {
|
|
665
|
+
// Broadcast updated MCP servers state after each server connects
|
|
666
|
+
this.broadcast(
|
|
667
|
+
JSON.stringify({
|
|
668
|
+
mcp: this.getMcpServers(),
|
|
669
|
+
type: MessageType.CF_AGENT_MCP_SERVERS
|
|
670
|
+
})
|
|
671
|
+
);
|
|
672
|
+
})
|
|
673
|
+
.catch((error) => {
|
|
674
|
+
console.error(
|
|
675
|
+
`Error connecting to MCP server: ${server.name} (${server.server_url})`,
|
|
676
|
+
error
|
|
677
|
+
);
|
|
678
|
+
// Still broadcast even if connection fails, so clients know about the failure
|
|
679
|
+
this.broadcast(
|
|
680
|
+
JSON.stringify({
|
|
681
|
+
mcp: this.getMcpServers(),
|
|
682
|
+
type: MessageType.CF_AGENT_MCP_SERVERS
|
|
683
|
+
})
|
|
684
|
+
);
|
|
685
|
+
});
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
return _onStart(props);
|
|
553
689
|
});
|
|
554
|
-
await this._tryCatch(() => _onStart());
|
|
555
690
|
}
|
|
556
691
|
);
|
|
557
692
|
};
|
|
@@ -573,15 +708,25 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
573
708
|
this.broadcast(
|
|
574
709
|
JSON.stringify({
|
|
575
710
|
state: state,
|
|
576
|
-
type:
|
|
711
|
+
type: MessageType.CF_AGENT_STATE
|
|
577
712
|
}),
|
|
578
713
|
source !== "server" ? [source.id] : []
|
|
579
714
|
);
|
|
580
715
|
return this._tryCatch(() => {
|
|
581
|
-
const { connection, request } = agentContext.getStore() || {};
|
|
716
|
+
const { connection, request, email } = agentContext.getStore() || {};
|
|
582
717
|
return agentContext.run(
|
|
583
|
-
{ agent: this, connection, request },
|
|
718
|
+
{ agent: this, connection, request, email },
|
|
584
719
|
async () => {
|
|
720
|
+
this.observability?.emit(
|
|
721
|
+
{
|
|
722
|
+
displayMessage: "State updated",
|
|
723
|
+
id: nanoid(),
|
|
724
|
+
payload: {},
|
|
725
|
+
timestamp: Date.now(),
|
|
726
|
+
type: "state:update"
|
|
727
|
+
},
|
|
728
|
+
this.ctx
|
|
729
|
+
);
|
|
585
730
|
return this.onStateUpdate(state, source);
|
|
586
731
|
}
|
|
587
732
|
);
|
|
@@ -607,19 +752,83 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
607
752
|
}
|
|
608
753
|
|
|
609
754
|
/**
|
|
610
|
-
* Called when the Agent receives an email
|
|
755
|
+
* Called when the Agent receives an email via routeAgentEmail()
|
|
756
|
+
* Override this method to handle incoming emails
|
|
611
757
|
* @param email Email message to process
|
|
612
758
|
*/
|
|
613
|
-
|
|
614
|
-
|
|
759
|
+
async _onEmail(email: AgentEmail) {
|
|
760
|
+
// nb: we use this roundabout way of getting to onEmail
|
|
761
|
+
// because of https://github.com/cloudflare/workerd/issues/4499
|
|
615
762
|
return agentContext.run(
|
|
616
|
-
{ agent: this, connection: undefined, request: undefined },
|
|
763
|
+
{ agent: this, connection: undefined, request: undefined, email: email },
|
|
617
764
|
async () => {
|
|
618
|
-
|
|
765
|
+
if ("onEmail" in this && typeof this.onEmail === "function") {
|
|
766
|
+
return this._tryCatch(() =>
|
|
767
|
+
(this.onEmail as (email: AgentEmail) => Promise<void>)(email)
|
|
768
|
+
);
|
|
769
|
+
} else {
|
|
770
|
+
console.log("Received email from:", email.from, "to:", email.to);
|
|
771
|
+
console.log("Subject:", email.headers.get("subject"));
|
|
772
|
+
console.log(
|
|
773
|
+
"Implement onEmail(email: AgentEmail): Promise<void> in your agent to process emails"
|
|
774
|
+
);
|
|
775
|
+
}
|
|
619
776
|
}
|
|
620
777
|
);
|
|
621
778
|
}
|
|
622
779
|
|
|
780
|
+
/**
|
|
781
|
+
* Reply to an email
|
|
782
|
+
* @param email The email to reply to
|
|
783
|
+
* @param options Options for the reply
|
|
784
|
+
* @returns void
|
|
785
|
+
*/
|
|
786
|
+
async replyToEmail(
|
|
787
|
+
email: AgentEmail,
|
|
788
|
+
options: {
|
|
789
|
+
fromName: string;
|
|
790
|
+
subject?: string | undefined;
|
|
791
|
+
body: string;
|
|
792
|
+
contentType?: string;
|
|
793
|
+
headers?: Record<string, string>;
|
|
794
|
+
}
|
|
795
|
+
): Promise<void> {
|
|
796
|
+
return this._tryCatch(async () => {
|
|
797
|
+
const agentName = camelCaseToKebabCase(this._ParentClass.name);
|
|
798
|
+
const agentId = this.name;
|
|
799
|
+
|
|
800
|
+
const { createMimeMessage } = await import("mimetext");
|
|
801
|
+
const msg = createMimeMessage();
|
|
802
|
+
msg.setSender({ addr: email.to, name: options.fromName });
|
|
803
|
+
msg.setRecipient(email.from);
|
|
804
|
+
msg.setSubject(
|
|
805
|
+
options.subject || `Re: ${email.headers.get("subject")}` || "No subject"
|
|
806
|
+
);
|
|
807
|
+
msg.addMessage({
|
|
808
|
+
contentType: options.contentType || "text/plain",
|
|
809
|
+
data: options.body
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
const domain = email.from.split("@")[1];
|
|
813
|
+
const messageId = `<${agentId}@${domain}>`;
|
|
814
|
+
msg.setHeader("In-Reply-To", email.headers.get("Message-ID")!);
|
|
815
|
+
msg.setHeader("Message-ID", messageId);
|
|
816
|
+
msg.setHeader("X-Agent-Name", agentName);
|
|
817
|
+
msg.setHeader("X-Agent-ID", agentId);
|
|
818
|
+
|
|
819
|
+
if (options.headers) {
|
|
820
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
821
|
+
msg.setHeader(key, value);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
await email.reply({
|
|
825
|
+
from: email.to,
|
|
826
|
+
raw: msg.asRaw(),
|
|
827
|
+
to: email.from
|
|
828
|
+
});
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
|
|
623
832
|
private async _tryCatch<T>(fn: () => T | Promise<T>) {
|
|
624
833
|
try {
|
|
625
834
|
return await fn();
|
|
@@ -628,6 +837,68 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
628
837
|
}
|
|
629
838
|
}
|
|
630
839
|
|
|
840
|
+
/**
|
|
841
|
+
* Automatically wrap custom methods with agent context
|
|
842
|
+
* This ensures getCurrentAgent() works in all custom methods without decorators
|
|
843
|
+
*/
|
|
844
|
+
private _autoWrapCustomMethods() {
|
|
845
|
+
// Collect all methods from base prototypes (Agent and Server)
|
|
846
|
+
const basePrototypes = [Agent.prototype, Server.prototype];
|
|
847
|
+
const baseMethods = new Set<string>();
|
|
848
|
+
for (const baseProto of basePrototypes) {
|
|
849
|
+
let proto = baseProto;
|
|
850
|
+
while (proto && proto !== Object.prototype) {
|
|
851
|
+
const methodNames = Object.getOwnPropertyNames(proto);
|
|
852
|
+
for (const methodName of methodNames) {
|
|
853
|
+
baseMethods.add(methodName);
|
|
854
|
+
}
|
|
855
|
+
proto = Object.getPrototypeOf(proto);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
// Get all methods from the current instance's prototype chain
|
|
859
|
+
let proto = Object.getPrototypeOf(this);
|
|
860
|
+
let depth = 0;
|
|
861
|
+
while (proto && proto !== Object.prototype && depth < 10) {
|
|
862
|
+
const methodNames = Object.getOwnPropertyNames(proto);
|
|
863
|
+
for (const methodName of methodNames) {
|
|
864
|
+
const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
|
|
865
|
+
|
|
866
|
+
// Skip if it's a private method, a base method, a getter, or not a function,
|
|
867
|
+
if (
|
|
868
|
+
baseMethods.has(methodName) ||
|
|
869
|
+
methodName.startsWith("_") ||
|
|
870
|
+
!descriptor ||
|
|
871
|
+
!!descriptor.get ||
|
|
872
|
+
typeof descriptor.value !== "function"
|
|
873
|
+
) {
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// Now, methodName is confirmed to be a custom method/function
|
|
878
|
+
// Wrap the custom method with context
|
|
879
|
+
const wrappedFunction = withAgentContext(
|
|
880
|
+
// biome-ignore lint/suspicious/noExplicitAny: I can't typescript
|
|
881
|
+
this[methodName as keyof this] as (...args: any[]) => any
|
|
882
|
+
// biome-ignore lint/suspicious/noExplicitAny: I can't typescript
|
|
883
|
+
) as any;
|
|
884
|
+
|
|
885
|
+
// if the method is callable, copy the metadata from the original method
|
|
886
|
+
if (this._isCallable(methodName)) {
|
|
887
|
+
callableMetadata.set(
|
|
888
|
+
wrappedFunction,
|
|
889
|
+
callableMetadata.get(this[methodName as keyof this] as Function)!
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// set the wrapped function on the prototype
|
|
894
|
+
this.constructor.prototype[methodName as keyof this] = wrappedFunction;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
proto = Object.getPrototypeOf(proto);
|
|
898
|
+
depth++;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
631
902
|
override onError(
|
|
632
903
|
connection: Connection,
|
|
633
904
|
error: unknown
|
|
@@ -662,6 +933,131 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
662
933
|
throw new Error("Not implemented");
|
|
663
934
|
}
|
|
664
935
|
|
|
936
|
+
/**
|
|
937
|
+
* Queue a task to be executed in the future
|
|
938
|
+
* @param payload Payload to pass to the callback
|
|
939
|
+
* @param callback Name of the method to call
|
|
940
|
+
* @returns The ID of the queued task
|
|
941
|
+
*/
|
|
942
|
+
async queue<T = unknown>(callback: keyof this, payload: T): Promise<string> {
|
|
943
|
+
const id = nanoid(9);
|
|
944
|
+
if (typeof callback !== "string") {
|
|
945
|
+
throw new Error("Callback must be a string");
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
if (typeof this[callback] !== "function") {
|
|
949
|
+
throw new Error(`this.${callback} is not a function`);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
this.sql`
|
|
953
|
+
INSERT OR REPLACE INTO cf_agents_queues (id, payload, callback)
|
|
954
|
+
VALUES (${id}, ${JSON.stringify(payload)}, ${callback})
|
|
955
|
+
`;
|
|
956
|
+
|
|
957
|
+
void this._flushQueue().catch((e) => {
|
|
958
|
+
console.error("Error flushing queue:", e);
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
return id;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
private _flushingQueue = false;
|
|
965
|
+
|
|
966
|
+
private async _flushQueue() {
|
|
967
|
+
if (this._flushingQueue) {
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
this._flushingQueue = true;
|
|
971
|
+
while (true) {
|
|
972
|
+
const result = this.sql<QueueItem<string>>`
|
|
973
|
+
SELECT * FROM cf_agents_queues
|
|
974
|
+
ORDER BY created_at ASC
|
|
975
|
+
`;
|
|
976
|
+
|
|
977
|
+
if (!result || result.length === 0) {
|
|
978
|
+
break;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
for (const row of result || []) {
|
|
982
|
+
const callback = this[row.callback as keyof Agent<Env>];
|
|
983
|
+
if (!callback) {
|
|
984
|
+
console.error(`callback ${row.callback} not found`);
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
const { connection, request, email } = agentContext.getStore() || {};
|
|
988
|
+
await agentContext.run(
|
|
989
|
+
{
|
|
990
|
+
agent: this,
|
|
991
|
+
connection,
|
|
992
|
+
request,
|
|
993
|
+
email
|
|
994
|
+
},
|
|
995
|
+
async () => {
|
|
996
|
+
// TODO: add retries and backoff
|
|
997
|
+
await (
|
|
998
|
+
callback as (
|
|
999
|
+
payload: unknown,
|
|
1000
|
+
queueItem: QueueItem<string>
|
|
1001
|
+
) => Promise<void>
|
|
1002
|
+
).bind(this)(JSON.parse(row.payload as string), row);
|
|
1003
|
+
await this.dequeue(row.id);
|
|
1004
|
+
}
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
this._flushingQueue = false;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* Dequeue a task by ID
|
|
1013
|
+
* @param id ID of the task to dequeue
|
|
1014
|
+
*/
|
|
1015
|
+
async dequeue(id: string) {
|
|
1016
|
+
this.sql`DELETE FROM cf_agents_queues WHERE id = ${id}`;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
/**
|
|
1020
|
+
* Dequeue all tasks
|
|
1021
|
+
*/
|
|
1022
|
+
async dequeueAll() {
|
|
1023
|
+
this.sql`DELETE FROM cf_agents_queues`;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* Dequeue all tasks by callback
|
|
1028
|
+
* @param callback Name of the callback to dequeue
|
|
1029
|
+
*/
|
|
1030
|
+
async dequeueAllByCallback(callback: string) {
|
|
1031
|
+
this.sql`DELETE FROM cf_agents_queues WHERE callback = ${callback}`;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* Get a queued task by ID
|
|
1036
|
+
* @param id ID of the task to get
|
|
1037
|
+
* @returns The task or undefined if not found
|
|
1038
|
+
*/
|
|
1039
|
+
async getQueue(id: string): Promise<QueueItem<string> | undefined> {
|
|
1040
|
+
const result = this.sql<QueueItem<string>>`
|
|
1041
|
+
SELECT * FROM cf_agents_queues WHERE id = ${id}
|
|
1042
|
+
`;
|
|
1043
|
+
return result
|
|
1044
|
+
? { ...result[0], payload: JSON.parse(result[0].payload) }
|
|
1045
|
+
: undefined;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* Get all queues by key and value
|
|
1050
|
+
* @param key Key to filter by
|
|
1051
|
+
* @param value Value to filter by
|
|
1052
|
+
* @returns Array of matching QueueItem objects
|
|
1053
|
+
*/
|
|
1054
|
+
async getQueues(key: string, value: string): Promise<QueueItem<string>[]> {
|
|
1055
|
+
const result = this.sql<QueueItem<string>>`
|
|
1056
|
+
SELECT * FROM cf_agents_queues
|
|
1057
|
+
`;
|
|
1058
|
+
return result.filter((row) => JSON.parse(row.payload)[key] === value);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
665
1061
|
/**
|
|
666
1062
|
* Schedule a task to be executed in the future
|
|
667
1063
|
* @template T Type of the payload data
|
|
@@ -677,6 +1073,21 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
677
1073
|
): Promise<Schedule<T>> {
|
|
678
1074
|
const id = nanoid(9);
|
|
679
1075
|
|
|
1076
|
+
const emitScheduleCreate = (schedule: Schedule<T>) =>
|
|
1077
|
+
this.observability?.emit(
|
|
1078
|
+
{
|
|
1079
|
+
displayMessage: `Schedule ${schedule.id} created`,
|
|
1080
|
+
id: nanoid(),
|
|
1081
|
+
payload: {
|
|
1082
|
+
callback: callback as string,
|
|
1083
|
+
id: id
|
|
1084
|
+
},
|
|
1085
|
+
timestamp: Date.now(),
|
|
1086
|
+
type: "schedule:create"
|
|
1087
|
+
},
|
|
1088
|
+
this.ctx
|
|
1089
|
+
);
|
|
1090
|
+
|
|
680
1091
|
if (typeof callback !== "string") {
|
|
681
1092
|
throw new Error("Callback must be a string");
|
|
682
1093
|
}
|
|
@@ -696,13 +1107,17 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
696
1107
|
|
|
697
1108
|
await this._scheduleNextAlarm();
|
|
698
1109
|
|
|
699
|
-
|
|
1110
|
+
const schedule: Schedule<T> = {
|
|
700
1111
|
callback: callback,
|
|
701
1112
|
id,
|
|
702
1113
|
payload: payload as T,
|
|
703
1114
|
time: timestamp,
|
|
704
|
-
type: "scheduled"
|
|
1115
|
+
type: "scheduled"
|
|
705
1116
|
};
|
|
1117
|
+
|
|
1118
|
+
emitScheduleCreate(schedule);
|
|
1119
|
+
|
|
1120
|
+
return schedule;
|
|
706
1121
|
}
|
|
707
1122
|
if (typeof when === "number") {
|
|
708
1123
|
const time = new Date(Date.now() + when * 1000);
|
|
@@ -717,14 +1132,18 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
717
1132
|
|
|
718
1133
|
await this._scheduleNextAlarm();
|
|
719
1134
|
|
|
720
|
-
|
|
1135
|
+
const schedule: Schedule<T> = {
|
|
721
1136
|
callback: callback,
|
|
722
1137
|
delayInSeconds: when,
|
|
723
1138
|
id,
|
|
724
1139
|
payload: payload as T,
|
|
725
1140
|
time: timestamp,
|
|
726
|
-
type: "delayed"
|
|
1141
|
+
type: "delayed"
|
|
727
1142
|
};
|
|
1143
|
+
|
|
1144
|
+
emitScheduleCreate(schedule);
|
|
1145
|
+
|
|
1146
|
+
return schedule;
|
|
728
1147
|
}
|
|
729
1148
|
if (typeof when === "string") {
|
|
730
1149
|
const nextExecutionTime = getNextCronTime(when);
|
|
@@ -739,14 +1158,18 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
739
1158
|
|
|
740
1159
|
await this._scheduleNextAlarm();
|
|
741
1160
|
|
|
742
|
-
|
|
1161
|
+
const schedule: Schedule<T> = {
|
|
743
1162
|
callback: callback,
|
|
744
1163
|
cron: when,
|
|
745
1164
|
id,
|
|
746
1165
|
payload: payload as T,
|
|
747
1166
|
time: timestamp,
|
|
748
|
-
type: "cron"
|
|
1167
|
+
type: "cron"
|
|
749
1168
|
};
|
|
1169
|
+
|
|
1170
|
+
emitScheduleCreate(schedule);
|
|
1171
|
+
|
|
1172
|
+
return schedule;
|
|
750
1173
|
}
|
|
751
1174
|
throw new Error("Invalid schedule type");
|
|
752
1175
|
}
|
|
@@ -810,7 +1233,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
810
1233
|
.toArray()
|
|
811
1234
|
.map((row) => ({
|
|
812
1235
|
...row,
|
|
813
|
-
payload: JSON.parse(row.payload as string) as T
|
|
1236
|
+
payload: JSON.parse(row.payload as string) as T
|
|
814
1237
|
})) as Schedule<T>[];
|
|
815
1238
|
|
|
816
1239
|
return result;
|
|
@@ -822,6 +1245,22 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
822
1245
|
* @returns true if the task was cancelled, false otherwise
|
|
823
1246
|
*/
|
|
824
1247
|
async cancelSchedule(id: string): Promise<boolean> {
|
|
1248
|
+
const schedule = await this.getSchedule(id);
|
|
1249
|
+
if (schedule) {
|
|
1250
|
+
this.observability?.emit(
|
|
1251
|
+
{
|
|
1252
|
+
displayMessage: `Schedule ${id} cancelled`,
|
|
1253
|
+
id: nanoid(),
|
|
1254
|
+
payload: {
|
|
1255
|
+
callback: schedule.callback,
|
|
1256
|
+
id: schedule.id
|
|
1257
|
+
},
|
|
1258
|
+
timestamp: Date.now(),
|
|
1259
|
+
type: "schedule:cancel"
|
|
1260
|
+
},
|
|
1261
|
+
this.ctx
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
825
1264
|
this.sql`DELETE FROM cf_agents_schedules WHERE id = ${id}`;
|
|
826
1265
|
|
|
827
1266
|
await this._scheduleNextAlarm();
|
|
@@ -831,9 +1270,9 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
831
1270
|
private async _scheduleNextAlarm() {
|
|
832
1271
|
// Find the next schedule that needs to be executed
|
|
833
1272
|
const result = this.sql`
|
|
834
|
-
SELECT time FROM cf_agents_schedules
|
|
1273
|
+
SELECT time FROM cf_agents_schedules
|
|
835
1274
|
WHERE time > ${Math.floor(Date.now() / 1000)}
|
|
836
|
-
ORDER BY time ASC
|
|
1275
|
+
ORDER BY time ASC
|
|
837
1276
|
LIMIT 1
|
|
838
1277
|
`;
|
|
839
1278
|
if (!result) return;
|
|
@@ -860,40 +1299,61 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
860
1299
|
SELECT * FROM cf_agents_schedules WHERE time <= ${now}
|
|
861
1300
|
`;
|
|
862
1301
|
|
|
863
|
-
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
await agentContext.run(
|
|
870
|
-
{ agent: this, connection: undefined, request: undefined },
|
|
871
|
-
async () => {
|
|
872
|
-
try {
|
|
873
|
-
await (
|
|
874
|
-
callback as (
|
|
875
|
-
payload: unknown,
|
|
876
|
-
schedule: Schedule<unknown>
|
|
877
|
-
) => Promise<void>
|
|
878
|
-
).bind(this)(JSON.parse(row.payload as string), row);
|
|
879
|
-
} catch (e) {
|
|
880
|
-
console.error(`error executing callback "${row.callback}"`, e);
|
|
881
|
-
}
|
|
1302
|
+
if (result && Array.isArray(result)) {
|
|
1303
|
+
for (const row of result) {
|
|
1304
|
+
const callback = this[row.callback as keyof Agent<Env>];
|
|
1305
|
+
if (!callback) {
|
|
1306
|
+
console.error(`callback ${row.callback} not found`);
|
|
1307
|
+
continue;
|
|
882
1308
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
1309
|
+
await agentContext.run(
|
|
1310
|
+
{
|
|
1311
|
+
agent: this,
|
|
1312
|
+
connection: undefined,
|
|
1313
|
+
request: undefined,
|
|
1314
|
+
email: undefined
|
|
1315
|
+
},
|
|
1316
|
+
async () => {
|
|
1317
|
+
try {
|
|
1318
|
+
this.observability?.emit(
|
|
1319
|
+
{
|
|
1320
|
+
displayMessage: `Schedule ${row.id} executed`,
|
|
1321
|
+
id: nanoid(),
|
|
1322
|
+
payload: {
|
|
1323
|
+
callback: row.callback,
|
|
1324
|
+
id: row.id
|
|
1325
|
+
},
|
|
1326
|
+
timestamp: Date.now(),
|
|
1327
|
+
type: "schedule:execute"
|
|
1328
|
+
},
|
|
1329
|
+
this.ctx
|
|
1330
|
+
);
|
|
888
1331
|
|
|
889
|
-
|
|
1332
|
+
await (
|
|
1333
|
+
callback as (
|
|
1334
|
+
payload: unknown,
|
|
1335
|
+
schedule: Schedule<unknown>
|
|
1336
|
+
) => Promise<void>
|
|
1337
|
+
).bind(this)(JSON.parse(row.payload as string), row);
|
|
1338
|
+
} catch (e) {
|
|
1339
|
+
console.error(`error executing callback "${row.callback}"`, e);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
);
|
|
1343
|
+
if (row.type === "cron") {
|
|
1344
|
+
// Update next execution time for cron schedules
|
|
1345
|
+
const nextExecutionTime = getNextCronTime(row.cron);
|
|
1346
|
+
const nextTimestamp = Math.floor(nextExecutionTime.getTime() / 1000);
|
|
1347
|
+
|
|
1348
|
+
this.sql`
|
|
890
1349
|
UPDATE cf_agents_schedules SET time = ${nextTimestamp} WHERE id = ${row.id}
|
|
891
1350
|
`;
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
1351
|
+
} else {
|
|
1352
|
+
// Delete one-time schedules after execution
|
|
1353
|
+
this.sql`
|
|
895
1354
|
DELETE FROM cf_agents_schedules WHERE id = ${row.id}
|
|
896
1355
|
`;
|
|
1356
|
+
}
|
|
897
1357
|
}
|
|
898
1358
|
}
|
|
899
1359
|
|
|
@@ -909,11 +1369,23 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
909
1369
|
this.sql`DROP TABLE IF EXISTS cf_agents_state`;
|
|
910
1370
|
this.sql`DROP TABLE IF EXISTS cf_agents_schedules`;
|
|
911
1371
|
this.sql`DROP TABLE IF EXISTS cf_agents_mcp_servers`;
|
|
1372
|
+
this.sql`DROP TABLE IF EXISTS cf_agents_queues`;
|
|
912
1373
|
|
|
913
1374
|
// delete all alarms
|
|
914
1375
|
await this.ctx.storage.deleteAlarm();
|
|
915
1376
|
await this.ctx.storage.deleteAll();
|
|
916
1377
|
this.ctx.abort("destroyed"); // enforce that the agent is evicted
|
|
1378
|
+
|
|
1379
|
+
this.observability?.emit(
|
|
1380
|
+
{
|
|
1381
|
+
displayMessage: "Agent destroyed",
|
|
1382
|
+
id: nanoid(),
|
|
1383
|
+
payload: {},
|
|
1384
|
+
timestamp: Date.now(),
|
|
1385
|
+
type: "destroy"
|
|
1386
|
+
},
|
|
1387
|
+
this.ctx
|
|
1388
|
+
);
|
|
917
1389
|
}
|
|
918
1390
|
|
|
919
1391
|
/**
|
|
@@ -970,7 +1442,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
970
1442
|
this.broadcast(
|
|
971
1443
|
JSON.stringify({
|
|
972
1444
|
mcp: this.getMcpServers(),
|
|
973
|
-
type:
|
|
1445
|
+
type: MessageType.CF_AGENT_MCP_SERVERS
|
|
974
1446
|
})
|
|
975
1447
|
);
|
|
976
1448
|
|
|
@@ -1026,12 +1498,12 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1026
1498
|
fetch: (url, init) =>
|
|
1027
1499
|
fetch(url, {
|
|
1028
1500
|
...init,
|
|
1029
|
-
headers: options?.transport?.headers
|
|
1030
|
-
})
|
|
1501
|
+
headers: options?.transport?.headers
|
|
1502
|
+
})
|
|
1031
1503
|
},
|
|
1032
1504
|
requestInit: {
|
|
1033
|
-
headers: options?.transport?.headers
|
|
1034
|
-
}
|
|
1505
|
+
headers: options?.transport?.headers
|
|
1506
|
+
}
|
|
1035
1507
|
};
|
|
1036
1508
|
}
|
|
1037
1509
|
|
|
@@ -1040,14 +1512,14 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1040
1512
|
reconnect,
|
|
1041
1513
|
transport: {
|
|
1042
1514
|
...headerTransportOpts,
|
|
1043
|
-
authProvider
|
|
1044
|
-
}
|
|
1515
|
+
authProvider
|
|
1516
|
+
}
|
|
1045
1517
|
});
|
|
1046
1518
|
|
|
1047
1519
|
return {
|
|
1048
1520
|
authUrl,
|
|
1049
1521
|
clientId,
|
|
1050
|
-
id
|
|
1522
|
+
id
|
|
1051
1523
|
};
|
|
1052
1524
|
}
|
|
1053
1525
|
|
|
@@ -1059,7 +1531,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1059
1531
|
this.broadcast(
|
|
1060
1532
|
JSON.stringify({
|
|
1061
1533
|
mcp: this.getMcpServers(),
|
|
1062
|
-
type:
|
|
1534
|
+
type: MessageType.CF_AGENT_MCP_SERVERS
|
|
1063
1535
|
})
|
|
1064
1536
|
);
|
|
1065
1537
|
}
|
|
@@ -1069,30 +1541,35 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1069
1541
|
prompts: this.mcp.listPrompts(),
|
|
1070
1542
|
resources: this.mcp.listResources(),
|
|
1071
1543
|
servers: {},
|
|
1072
|
-
tools: this.mcp.listTools()
|
|
1544
|
+
tools: this.mcp.listTools()
|
|
1073
1545
|
};
|
|
1074
1546
|
|
|
1075
1547
|
const servers = this.sql<MCPServerRow>`
|
|
1076
1548
|
SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
|
|
1077
1549
|
`;
|
|
1078
1550
|
|
|
1079
|
-
|
|
1080
|
-
const
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1551
|
+
if (servers && Array.isArray(servers) && servers.length > 0) {
|
|
1552
|
+
for (const server of servers) {
|
|
1553
|
+
const serverConn = this.mcp.mcpConnections[server.id];
|
|
1554
|
+
mcpState.servers[server.id] = {
|
|
1555
|
+
auth_url: server.auth_url,
|
|
1556
|
+
capabilities: serverConn?.serverCapabilities ?? null,
|
|
1557
|
+
instructions: serverConn?.instructions ?? null,
|
|
1558
|
+
name: server.name,
|
|
1559
|
+
server_url: server.server_url,
|
|
1560
|
+
// mark as "authenticating" because the server isn't automatically connected, so it's pending authenticating
|
|
1561
|
+
state: serverConn?.connectionState ?? "authenticating"
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1090
1564
|
}
|
|
1091
1565
|
|
|
1092
1566
|
return mcpState;
|
|
1093
1567
|
}
|
|
1094
1568
|
}
|
|
1095
1569
|
|
|
1570
|
+
// A set of classes that have been wrapped with agent context
|
|
1571
|
+
const wrappedClasses = new Set<typeof Agent.prototype.constructor>();
|
|
1572
|
+
|
|
1096
1573
|
/**
|
|
1097
1574
|
* Namespace for creating Agent instances
|
|
1098
1575
|
* @template Agentic Type of the Agent class
|
|
@@ -1133,14 +1610,14 @@ export async function routeAgentRequest<Env>(
|
|
|
1133
1610
|
"Access-Control-Allow-Credentials": "true",
|
|
1134
1611
|
"Access-Control-Allow-Methods": "GET, POST, HEAD, OPTIONS",
|
|
1135
1612
|
"Access-Control-Allow-Origin": "*",
|
|
1136
|
-
"Access-Control-Max-Age": "86400"
|
|
1613
|
+
"Access-Control-Max-Age": "86400"
|
|
1137
1614
|
}
|
|
1138
1615
|
: options?.cors;
|
|
1139
1616
|
|
|
1140
1617
|
if (request.method === "OPTIONS") {
|
|
1141
1618
|
if (corsHeaders) {
|
|
1142
1619
|
return new Response(null, {
|
|
1143
|
-
headers: corsHeaders
|
|
1620
|
+
headers: corsHeaders
|
|
1144
1621
|
});
|
|
1145
1622
|
}
|
|
1146
1623
|
console.warn(
|
|
@@ -1153,7 +1630,7 @@ export async function routeAgentRequest<Env>(
|
|
|
1153
1630
|
env as Record<string, unknown>,
|
|
1154
1631
|
{
|
|
1155
1632
|
prefix: "agents",
|
|
1156
|
-
...(options as PartyServerOptions<Record<string, unknown>>)
|
|
1633
|
+
...(options as PartyServerOptions<Record<string, unknown>>)
|
|
1157
1634
|
}
|
|
1158
1635
|
);
|
|
1159
1636
|
|
|
@@ -1166,24 +1643,238 @@ export async function routeAgentRequest<Env>(
|
|
|
1166
1643
|
response = new Response(response.body, {
|
|
1167
1644
|
headers: {
|
|
1168
1645
|
...response.headers,
|
|
1169
|
-
...corsHeaders
|
|
1170
|
-
}
|
|
1646
|
+
...corsHeaders
|
|
1647
|
+
}
|
|
1171
1648
|
});
|
|
1172
1649
|
}
|
|
1173
1650
|
return response;
|
|
1174
1651
|
}
|
|
1175
1652
|
|
|
1653
|
+
export type EmailResolver<Env> = (
|
|
1654
|
+
email: ForwardableEmailMessage,
|
|
1655
|
+
env: Env
|
|
1656
|
+
) => Promise<{
|
|
1657
|
+
agentName: string;
|
|
1658
|
+
agentId: string;
|
|
1659
|
+
} | null>;
|
|
1660
|
+
|
|
1661
|
+
/**
|
|
1662
|
+
* Create a resolver that uses the message-id header to determine the agent to route the email to
|
|
1663
|
+
* @returns A function that resolves the agent to route the email to
|
|
1664
|
+
*/
|
|
1665
|
+
export function createHeaderBasedEmailResolver<Env>(): EmailResolver<Env> {
|
|
1666
|
+
return async (email: ForwardableEmailMessage, _env: Env) => {
|
|
1667
|
+
const messageId = email.headers.get("message-id");
|
|
1668
|
+
if (messageId) {
|
|
1669
|
+
const messageIdMatch = messageId.match(/<([^@]+)@([^>]+)>/);
|
|
1670
|
+
if (messageIdMatch) {
|
|
1671
|
+
const [, agentId, domain] = messageIdMatch;
|
|
1672
|
+
const agentName = domain.split(".")[0];
|
|
1673
|
+
return { agentName, agentId };
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
const references = email.headers.get("references");
|
|
1678
|
+
if (references) {
|
|
1679
|
+
const referencesMatch = references.match(
|
|
1680
|
+
/<([A-Za-z0-9+/]{43}=)@([^>]+)>/
|
|
1681
|
+
);
|
|
1682
|
+
if (referencesMatch) {
|
|
1683
|
+
const [, base64Id, domain] = referencesMatch;
|
|
1684
|
+
const agentId = Buffer.from(base64Id, "base64").toString("hex");
|
|
1685
|
+
const agentName = domain.split(".")[0];
|
|
1686
|
+
return { agentName, agentId };
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
const agentName = email.headers.get("x-agent-name");
|
|
1691
|
+
const agentId = email.headers.get("x-agent-id");
|
|
1692
|
+
if (agentName && agentId) {
|
|
1693
|
+
return { agentName, agentId };
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
return null;
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
/**
|
|
1701
|
+
* Create a resolver that uses the email address to determine the agent to route the email to
|
|
1702
|
+
* @param defaultAgentName The default agent name to use if the email address does not contain a sub-address
|
|
1703
|
+
* @returns A function that resolves the agent to route the email to
|
|
1704
|
+
*/
|
|
1705
|
+
export function createAddressBasedEmailResolver<Env>(
|
|
1706
|
+
defaultAgentName: string
|
|
1707
|
+
): EmailResolver<Env> {
|
|
1708
|
+
return async (email: ForwardableEmailMessage, _env: Env) => {
|
|
1709
|
+
const emailMatch = email.to.match(/^([^+@]+)(?:\+([^@]+))?@(.+)$/);
|
|
1710
|
+
if (!emailMatch) {
|
|
1711
|
+
return null;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
const [, localPart, subAddress] = emailMatch;
|
|
1715
|
+
|
|
1716
|
+
if (subAddress) {
|
|
1717
|
+
return {
|
|
1718
|
+
agentName: localPart,
|
|
1719
|
+
agentId: subAddress
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
// Option 2: Use defaultAgentName namespace, localPart as agentId
|
|
1724
|
+
// Common for catch-all email routing to a single EmailAgent namespace
|
|
1725
|
+
return {
|
|
1726
|
+
agentName: defaultAgentName,
|
|
1727
|
+
agentId: localPart
|
|
1728
|
+
};
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
/**
|
|
1733
|
+
* Create a resolver that uses the agentName and agentId to determine the agent to route the email to
|
|
1734
|
+
* @param agentName The name of the agent to route the email to
|
|
1735
|
+
* @param agentId The id of the agent to route the email to
|
|
1736
|
+
* @returns A function that resolves the agent to route the email to
|
|
1737
|
+
*/
|
|
1738
|
+
export function createCatchAllEmailResolver<Env>(
|
|
1739
|
+
agentName: string,
|
|
1740
|
+
agentId: string
|
|
1741
|
+
): EmailResolver<Env> {
|
|
1742
|
+
return async () => ({ agentName, agentId });
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
export type EmailRoutingOptions<Env> = AgentOptions<Env> & {
|
|
1746
|
+
resolver: EmailResolver<Env>;
|
|
1747
|
+
};
|
|
1748
|
+
|
|
1749
|
+
// Cache the agent namespace map for email routing
|
|
1750
|
+
// This maps both kebab-case and original names to namespaces
|
|
1751
|
+
const agentMapCache = new WeakMap<
|
|
1752
|
+
Record<string, unknown>,
|
|
1753
|
+
Record<string, unknown>
|
|
1754
|
+
>();
|
|
1755
|
+
|
|
1176
1756
|
/**
|
|
1177
1757
|
* Route an email to the appropriate Agent
|
|
1178
|
-
* @param email
|
|
1179
|
-
* @param env
|
|
1180
|
-
* @param options
|
|
1758
|
+
* @param email The email to route
|
|
1759
|
+
* @param env The environment containing the Agent bindings
|
|
1760
|
+
* @param options The options for routing the email
|
|
1761
|
+
* @returns A promise that resolves when the email has been routed
|
|
1181
1762
|
*/
|
|
1182
1763
|
export async function routeAgentEmail<Env>(
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
): Promise<void> {
|
|
1764
|
+
email: ForwardableEmailMessage,
|
|
1765
|
+
env: Env,
|
|
1766
|
+
options: EmailRoutingOptions<Env>
|
|
1767
|
+
): Promise<void> {
|
|
1768
|
+
const routingInfo = await options.resolver(email, env);
|
|
1769
|
+
|
|
1770
|
+
if (!routingInfo) {
|
|
1771
|
+
console.warn("No routing information found for email, dropping message");
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
// Build a map that includes both original names and kebab-case versions
|
|
1776
|
+
if (!agentMapCache.has(env as Record<string, unknown>)) {
|
|
1777
|
+
const map: Record<string, unknown> = {};
|
|
1778
|
+
for (const [key, value] of Object.entries(env as Record<string, unknown>)) {
|
|
1779
|
+
if (
|
|
1780
|
+
value &&
|
|
1781
|
+
typeof value === "object" &&
|
|
1782
|
+
"idFromName" in value &&
|
|
1783
|
+
typeof value.idFromName === "function"
|
|
1784
|
+
) {
|
|
1785
|
+
// Add both the original name and kebab-case version
|
|
1786
|
+
map[key] = value;
|
|
1787
|
+
map[camelCaseToKebabCase(key)] = value;
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
agentMapCache.set(env as Record<string, unknown>, map);
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
const agentMap = agentMapCache.get(env as Record<string, unknown>)!;
|
|
1794
|
+
const namespace = agentMap[routingInfo.agentName];
|
|
1795
|
+
|
|
1796
|
+
if (!namespace) {
|
|
1797
|
+
// Provide helpful error message listing available agents
|
|
1798
|
+
const availableAgents = Object.keys(agentMap)
|
|
1799
|
+
.filter((key) => !key.includes("-")) // Show only original names, not kebab-case duplicates
|
|
1800
|
+
.join(", ");
|
|
1801
|
+
throw new Error(
|
|
1802
|
+
`Agent namespace '${routingInfo.agentName}' not found in environment. Available agents: ${availableAgents}`
|
|
1803
|
+
);
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
const agent = await getAgentByName(
|
|
1807
|
+
namespace as unknown as AgentNamespace<Agent<Env>>,
|
|
1808
|
+
routingInfo.agentId
|
|
1809
|
+
);
|
|
1810
|
+
|
|
1811
|
+
// let's make a serialisable version of the email
|
|
1812
|
+
const serialisableEmail: AgentEmail = {
|
|
1813
|
+
getRaw: async () => {
|
|
1814
|
+
const reader = email.raw.getReader();
|
|
1815
|
+
const chunks: Uint8Array[] = [];
|
|
1816
|
+
|
|
1817
|
+
let done = false;
|
|
1818
|
+
while (!done) {
|
|
1819
|
+
const { value, done: readerDone } = await reader.read();
|
|
1820
|
+
done = readerDone;
|
|
1821
|
+
if (value) {
|
|
1822
|
+
chunks.push(value);
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
1827
|
+
const combined = new Uint8Array(totalLength);
|
|
1828
|
+
let offset = 0;
|
|
1829
|
+
for (const chunk of chunks) {
|
|
1830
|
+
combined.set(chunk, offset);
|
|
1831
|
+
offset += chunk.length;
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
return combined;
|
|
1835
|
+
},
|
|
1836
|
+
headers: email.headers,
|
|
1837
|
+
rawSize: email.rawSize,
|
|
1838
|
+
setReject: (reason: string) => {
|
|
1839
|
+
email.setReject(reason);
|
|
1840
|
+
},
|
|
1841
|
+
forward: (rcptTo: string, headers?: Headers) => {
|
|
1842
|
+
return email.forward(rcptTo, headers);
|
|
1843
|
+
},
|
|
1844
|
+
reply: (options: { from: string; to: string; raw: string }) => {
|
|
1845
|
+
return email.reply(
|
|
1846
|
+
new EmailMessage(options.from, options.to, options.raw)
|
|
1847
|
+
);
|
|
1848
|
+
},
|
|
1849
|
+
from: email.from,
|
|
1850
|
+
to: email.to
|
|
1851
|
+
};
|
|
1852
|
+
|
|
1853
|
+
await agent._onEmail(serialisableEmail);
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
export type AgentEmail = {
|
|
1857
|
+
from: string;
|
|
1858
|
+
to: string;
|
|
1859
|
+
getRaw: () => Promise<Uint8Array>;
|
|
1860
|
+
headers: Headers;
|
|
1861
|
+
rawSize: number;
|
|
1862
|
+
setReject: (reason: string) => void;
|
|
1863
|
+
forward: (rcptTo: string, headers?: Headers) => Promise<void>;
|
|
1864
|
+
reply: (options: { from: string; to: string; raw: string }) => Promise<void>;
|
|
1865
|
+
};
|
|
1866
|
+
|
|
1867
|
+
export type EmailSendOptions = {
|
|
1868
|
+
to: string;
|
|
1869
|
+
subject: string;
|
|
1870
|
+
body: string;
|
|
1871
|
+
contentType?: string;
|
|
1872
|
+
headers?: Record<string, string>;
|
|
1873
|
+
includeRoutingHeaders?: boolean;
|
|
1874
|
+
agentName?: string;
|
|
1875
|
+
agentId?: string;
|
|
1876
|
+
domain?: string;
|
|
1877
|
+
};
|
|
1187
1878
|
|
|
1188
1879
|
/**
|
|
1189
1880
|
* Get or create an Agent by name
|
|
@@ -1194,12 +1885,17 @@ export async function routeAgentEmail<Env>(
|
|
|
1194
1885
|
* @param options Options for Agent creation
|
|
1195
1886
|
* @returns Promise resolving to an Agent instance stub
|
|
1196
1887
|
*/
|
|
1197
|
-
export async function getAgentByName<
|
|
1888
|
+
export async function getAgentByName<
|
|
1889
|
+
Env,
|
|
1890
|
+
T extends Agent<Env>,
|
|
1891
|
+
Props extends Record<string, unknown> = Record<string, unknown>
|
|
1892
|
+
>(
|
|
1198
1893
|
namespace: AgentNamespace<T>,
|
|
1199
1894
|
name: string,
|
|
1200
1895
|
options?: {
|
|
1201
1896
|
jurisdiction?: DurableObjectJurisdiction;
|
|
1202
1897
|
locationHint?: DurableObjectLocationHint;
|
|
1898
|
+
props?: Props;
|
|
1203
1899
|
}
|
|
1204
1900
|
) {
|
|
1205
1901
|
return getServerByName<Env, T>(namespace, name, options);
|
|
@@ -1231,7 +1927,7 @@ export class StreamingResponse {
|
|
|
1231
1927
|
id: this._id,
|
|
1232
1928
|
result: chunk,
|
|
1233
1929
|
success: true,
|
|
1234
|
-
type:
|
|
1930
|
+
type: MessageType.RPC
|
|
1235
1931
|
};
|
|
1236
1932
|
this._connection.send(JSON.stringify(response));
|
|
1237
1933
|
}
|
|
@@ -1250,7 +1946,7 @@ export class StreamingResponse {
|
|
|
1250
1946
|
id: this._id,
|
|
1251
1947
|
result: finalChunk,
|
|
1252
1948
|
success: true,
|
|
1253
|
-
type:
|
|
1949
|
+
type: MessageType.RPC
|
|
1254
1950
|
};
|
|
1255
1951
|
this._connection.send(JSON.stringify(response));
|
|
1256
1952
|
}
|