agents 0.0.0-c5e3a32 → 0.0.0-c6d9bf1
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 +234 -6
- package/dist/_esm-LV5FJ3HK.js +3922 -0
- package/dist/_esm-LV5FJ3HK.js.map +1 -0
- package/dist/ai-chat-agent.d.ts +13 -9
- package/dist/ai-chat-agent.js +444 -60
- 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 +69 -71
- package/dist/ai-react.js +252 -99
- 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-EDUDXISR.js → chunk-254F4GDT.js} +321 -126
- package/dist/chunk-254F4GDT.js.map +1 -0
- package/dist/chunk-3OT2NNEW.js +941 -0
- package/dist/chunk-3OT2NNEW.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-JJBFIGUC.js +5202 -0
- package/dist/chunk-JJBFIGUC.js.map +1 -0
- package/dist/chunk-PR4QN5HX.js +43 -0
- package/dist/chunk-PR4QN5HX.js.map +1 -0
- package/dist/{chunk-KUH345EY.js → chunk-QEVM4BVL.js} +5 -5
- package/dist/chunk-QEVM4BVL.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/{chunk-PVQZBKN7.js → chunk-Z44WASMA.js} +11 -3
- package/dist/chunk-Z44WASMA.js.map +1 -0
- package/dist/client-DVoPb3-C.d.ts +5120 -0
- package/dist/client.js +3 -1
- package/dist/codemode/ai.d.ts +25 -0
- package/dist/codemode/ai.js +5112 -0
- package/dist/codemode/ai.js.map +1 -0
- package/dist/index.d.ts +550 -31
- package/dist/index.js +8 -4
- package/dist/mcp/client.d.ts +10 -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 +58 -63
- package/dist/mcp/index.js +954 -638
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/x402.d.ts +39 -0
- package/dist/mcp/x402.js +3195 -0
- package/dist/mcp/x402.js.map +1 -0
- package/dist/mcp-BH1fJeiU.d.ts +58 -0
- package/dist/observability/index.d.ts +34 -12
- package/dist/observability/index.js +6 -4
- package/dist/react.d.ts +16 -8
- package/dist/react.js +107 -7
- package/dist/react.js.map +1 -1
- package/dist/schedule.d.ts +83 -9
- 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 +32 -9
- package/src/index.ts +453 -154
- package/dist/chunk-EDUDXISR.js.map +0 -1
- package/dist/chunk-KUH345EY.js.map +0 -1
- package/dist/chunk-MW5BQ2FW.js +0 -469
- package/dist/chunk-MW5BQ2FW.js.map +0 -1
- package/dist/chunk-PVQZBKN7.js.map +0 -1
- package/dist/index-DukU3sIa.d.ts +0 -571
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";
|
|
@@ -21,10 +22,13 @@ import {
|
|
|
21
22
|
routePartykitRequest
|
|
22
23
|
} from "partyserver";
|
|
23
24
|
import { camelCaseToKebabCase } from "./client";
|
|
24
|
-
import { MCPClientManager } from "./mcp/client";
|
|
25
|
-
|
|
25
|
+
import { MCPClientManager, type MCPClientOAuthResult } from "./mcp/client";
|
|
26
|
+
import type { MCPConnectionState } from "./mcp/client-connection";
|
|
26
27
|
import { DurableObjectOAuthClientProvider } from "./mcp/do-oauth-client-provider";
|
|
28
|
+
import type { TransportType } from "./mcp/types";
|
|
27
29
|
import { genericObservability, type Observability } from "./observability";
|
|
30
|
+
import { DisposableStore } from "./core/events";
|
|
31
|
+
import { MessageType } from "./ai-types";
|
|
28
32
|
|
|
29
33
|
export type { Connection, ConnectionContext, WSMessage } from "partyserver";
|
|
30
34
|
|
|
@@ -42,7 +46,7 @@ export type RPCRequest = {
|
|
|
42
46
|
* State update message from client
|
|
43
47
|
*/
|
|
44
48
|
export type StateUpdateMessage = {
|
|
45
|
-
type:
|
|
49
|
+
type: MessageType.CF_AGENT_STATE;
|
|
46
50
|
state: unknown;
|
|
47
51
|
};
|
|
48
52
|
|
|
@@ -50,7 +54,7 @@ export type StateUpdateMessage = {
|
|
|
50
54
|
* RPC response message to client
|
|
51
55
|
*/
|
|
52
56
|
export type RPCResponse = {
|
|
53
|
-
type:
|
|
57
|
+
type: MessageType.RPC;
|
|
54
58
|
id: string;
|
|
55
59
|
} & (
|
|
56
60
|
| {
|
|
@@ -77,7 +81,7 @@ function isRPCRequest(msg: unknown): msg is RPCRequest {
|
|
|
77
81
|
typeof msg === "object" &&
|
|
78
82
|
msg !== null &&
|
|
79
83
|
"type" in msg &&
|
|
80
|
-
msg.type ===
|
|
84
|
+
msg.type === MessageType.RPC &&
|
|
81
85
|
"id" in msg &&
|
|
82
86
|
typeof msg.id === "string" &&
|
|
83
87
|
"method" in msg &&
|
|
@@ -95,7 +99,7 @@ function isStateUpdateMessage(msg: unknown): msg is StateUpdateMessage {
|
|
|
95
99
|
typeof msg === "object" &&
|
|
96
100
|
msg !== null &&
|
|
97
101
|
"type" in msg &&
|
|
98
|
-
msg.type ===
|
|
102
|
+
msg.type === MessageType.CF_AGENT_STATE &&
|
|
99
103
|
"state" in msg
|
|
100
104
|
);
|
|
101
105
|
}
|
|
@@ -116,7 +120,7 @@ const callableMetadata = new Map<Function, CallableMetadata>();
|
|
|
116
120
|
* Decorator that marks a method as callable by clients
|
|
117
121
|
* @param metadata Optional metadata about the callable method
|
|
118
122
|
*/
|
|
119
|
-
export function
|
|
123
|
+
export function callable(metadata: CallableMetadata = {}) {
|
|
120
124
|
return function callableDecorator<This, Args extends unknown[], Return>(
|
|
121
125
|
target: (this: This, ...args: Args) => Return,
|
|
122
126
|
// biome-ignore lint/correctness/noUnusedFunctionParameters: later
|
|
@@ -130,6 +134,30 @@ export function unstable_callable(metadata: CallableMetadata = {}) {
|
|
|
130
134
|
};
|
|
131
135
|
}
|
|
132
136
|
|
|
137
|
+
let didWarnAboutUnstableCallable = false;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Decorator that marks a method as callable by clients
|
|
141
|
+
* @deprecated this has been renamed to callable, and unstable_callable will be removed in the next major version
|
|
142
|
+
* @param metadata Optional metadata about the callable method
|
|
143
|
+
*/
|
|
144
|
+
export const unstable_callable = (metadata: CallableMetadata = {}) => {
|
|
145
|
+
if (!didWarnAboutUnstableCallable) {
|
|
146
|
+
didWarnAboutUnstableCallable = true;
|
|
147
|
+
console.warn(
|
|
148
|
+
"unstable_callable is deprecated, use callable instead. unstable_callable will be removed in the next major version."
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
callable(metadata);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export type QueueItem<T = string> = {
|
|
155
|
+
id: string;
|
|
156
|
+
payload: T;
|
|
157
|
+
callback: keyof Agent<unknown>;
|
|
158
|
+
created_at: number;
|
|
159
|
+
};
|
|
160
|
+
|
|
133
161
|
/**
|
|
134
162
|
* Represents a scheduled task within an Agent
|
|
135
163
|
* @template T Type of the payload data
|
|
@@ -171,11 +199,13 @@ function getNextCronTime(cron: string) {
|
|
|
171
199
|
return interval.getNextDate();
|
|
172
200
|
}
|
|
173
201
|
|
|
202
|
+
export type { TransportType } from "./mcp/types";
|
|
203
|
+
|
|
174
204
|
/**
|
|
175
205
|
* MCP Server state update message from server -> Client
|
|
176
206
|
*/
|
|
177
207
|
export type MCPServerMessage = {
|
|
178
|
-
type:
|
|
208
|
+
type: MessageType.CF_AGENT_MCP_SERVERS;
|
|
179
209
|
mcp: MCPServersState;
|
|
180
210
|
};
|
|
181
211
|
|
|
@@ -195,7 +225,7 @@ export type MCPServer = {
|
|
|
195
225
|
// This state is specifically about the temporary process of getting a token (if needed).
|
|
196
226
|
// Scope outside of that can't be relied upon because when the DO sleeps, there's no way
|
|
197
227
|
// to communicate a change to a non-ready state.
|
|
198
|
-
state:
|
|
228
|
+
state: MCPConnectionState;
|
|
199
229
|
instructions: string | null;
|
|
200
230
|
capabilities: ServerCapabilities | null;
|
|
201
231
|
};
|
|
@@ -264,7 +294,13 @@ function withAgentContext<T extends (...args: any[]) => any>(
|
|
|
264
294
|
method: T
|
|
265
295
|
): (this: Agent<unknown, unknown>, ...args: Parameters<T>) => ReturnType<T> {
|
|
266
296
|
return function (...args: Parameters<T>): ReturnType<T> {
|
|
267
|
-
const { connection, request, email } = getCurrentAgent();
|
|
297
|
+
const { connection, request, email, agent } = getCurrentAgent();
|
|
298
|
+
|
|
299
|
+
if (agent === this) {
|
|
300
|
+
// already wrapped, so we can just call the method
|
|
301
|
+
return method.apply(this, args);
|
|
302
|
+
}
|
|
303
|
+
// not wrapped, so we need to wrap it
|
|
268
304
|
return agentContext.run({ agent: this, connection, request, email }, () => {
|
|
269
305
|
return method.apply(this, args);
|
|
270
306
|
});
|
|
@@ -276,13 +312,21 @@ function withAgentContext<T extends (...args: any[]) => any>(
|
|
|
276
312
|
* @template Env Environment type containing bindings
|
|
277
313
|
* @template State State type to store within the Agent
|
|
278
314
|
*/
|
|
279
|
-
export class Agent<
|
|
315
|
+
export class Agent<
|
|
316
|
+
Env = typeof env,
|
|
317
|
+
State = unknown,
|
|
318
|
+
Props extends Record<string, unknown> = Record<string, unknown>
|
|
319
|
+
> extends Server<Env, Props> {
|
|
280
320
|
private _state = DEFAULT_STATE as State;
|
|
321
|
+
private _disposables = new DisposableStore();
|
|
281
322
|
|
|
282
323
|
private _ParentClass: typeof Agent<Env, State> =
|
|
283
324
|
Object.getPrototypeOf(this).constructor;
|
|
284
325
|
|
|
285
|
-
mcp: MCPClientManager = new MCPClientManager(
|
|
326
|
+
readonly mcp: MCPClientManager = new MCPClientManager(
|
|
327
|
+
this._ParentClass.name,
|
|
328
|
+
"0.0.1"
|
|
329
|
+
);
|
|
286
330
|
|
|
287
331
|
/**
|
|
288
332
|
* Initial state for the Agent
|
|
@@ -375,6 +419,26 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
375
419
|
constructor(ctx: AgentContext, env: Env) {
|
|
376
420
|
super(ctx, env);
|
|
377
421
|
|
|
422
|
+
if (!wrappedClasses.has(this.constructor)) {
|
|
423
|
+
// Auto-wrap custom methods with agent context
|
|
424
|
+
this._autoWrapCustomMethods();
|
|
425
|
+
wrappedClasses.add(this.constructor);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Broadcast server state after background connects (for OAuth servers)
|
|
429
|
+
this._disposables.add(
|
|
430
|
+
this.mcp.onConnected(async () => {
|
|
431
|
+
this.broadcastMcpServers();
|
|
432
|
+
})
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
// Emit MCP observability events
|
|
436
|
+
this._disposables.add(
|
|
437
|
+
this.mcp.onObservabilityEvent((event) => {
|
|
438
|
+
this.observability?.emit(event);
|
|
439
|
+
})
|
|
440
|
+
);
|
|
441
|
+
|
|
378
442
|
this.sql`
|
|
379
443
|
CREATE TABLE IF NOT EXISTS cf_agents_state (
|
|
380
444
|
id TEXT PRIMARY KEY NOT NULL,
|
|
@@ -382,8 +446,14 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
382
446
|
)
|
|
383
447
|
`;
|
|
384
448
|
|
|
385
|
-
|
|
386
|
-
|
|
449
|
+
this.sql`
|
|
450
|
+
CREATE TABLE IF NOT EXISTS cf_agents_queues (
|
|
451
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
452
|
+
payload TEXT,
|
|
453
|
+
callback TEXT,
|
|
454
|
+
created_at INTEGER DEFAULT (unixepoch())
|
|
455
|
+
)
|
|
456
|
+
`;
|
|
387
457
|
|
|
388
458
|
void this.ctx.blockConcurrencyWhile(async () => {
|
|
389
459
|
return this._tryCatch(async () => {
|
|
@@ -424,21 +494,24 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
424
494
|
{ agent: this, connection: undefined, request, email: undefined },
|
|
425
495
|
async () => {
|
|
426
496
|
if (this.mcp.isCallbackRequest(request)) {
|
|
427
|
-
await this.mcp.handleCallbackRequest(request);
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
497
|
+
const result = await this.mcp.handleCallbackRequest(request);
|
|
498
|
+
this.broadcastMcpServers();
|
|
499
|
+
|
|
500
|
+
if (result.authSuccess) {
|
|
501
|
+
// Start background connection if auth was successful
|
|
502
|
+
this.mcp
|
|
503
|
+
.establishConnection(result.serverId)
|
|
504
|
+
.catch((error) => {
|
|
505
|
+
console.error("Background connection failed:", error);
|
|
506
|
+
})
|
|
507
|
+
.finally(() => {
|
|
508
|
+
// Broadcast after background connection resolves (success/failure)
|
|
509
|
+
this.broadcastMcpServers();
|
|
510
|
+
});
|
|
511
|
+
}
|
|
436
512
|
|
|
437
|
-
//
|
|
438
|
-
return
|
|
439
|
-
headers: { "content-type": "text/html" },
|
|
440
|
-
status: 200
|
|
441
|
-
});
|
|
513
|
+
// Handle OAuth callback response using MCPClientManager configuration
|
|
514
|
+
return this.handleOAuthCallbackResponse(result, request);
|
|
442
515
|
}
|
|
443
516
|
|
|
444
517
|
return this._tryCatch(() => _onRequest(request));
|
|
@@ -499,10 +572,8 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
499
572
|
displayMessage: `RPC call to ${method}`,
|
|
500
573
|
id: nanoid(),
|
|
501
574
|
payload: {
|
|
502
|
-
args,
|
|
503
575
|
method,
|
|
504
|
-
streaming: metadata?.streaming
|
|
505
|
-
success: true
|
|
576
|
+
streaming: metadata?.streaming
|
|
506
577
|
},
|
|
507
578
|
timestamp: Date.now(),
|
|
508
579
|
type: "rpc"
|
|
@@ -515,7 +586,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
515
586
|
id,
|
|
516
587
|
result,
|
|
517
588
|
success: true,
|
|
518
|
-
type:
|
|
589
|
+
type: MessageType.RPC
|
|
519
590
|
};
|
|
520
591
|
connection.send(JSON.stringify(response));
|
|
521
592
|
} catch (e) {
|
|
@@ -525,7 +596,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
525
596
|
e instanceof Error ? e.message : "Unknown error occurred",
|
|
526
597
|
id: parsed.id,
|
|
527
598
|
success: false,
|
|
528
|
-
type:
|
|
599
|
+
type: MessageType.RPC
|
|
529
600
|
};
|
|
530
601
|
connection.send(JSON.stringify(response));
|
|
531
602
|
console.error("RPC error:", e);
|
|
@@ -544,44 +615,42 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
544
615
|
// must fix this
|
|
545
616
|
return agentContext.run(
|
|
546
617
|
{ agent: this, connection, request: ctx.request, email: undefined },
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
if (this.state) {
|
|
550
|
-
connection.send(
|
|
551
|
-
JSON.stringify({
|
|
552
|
-
state: this.state,
|
|
553
|
-
type: "cf_agent_state"
|
|
554
|
-
})
|
|
555
|
-
);
|
|
556
|
-
}
|
|
557
|
-
|
|
618
|
+
() => {
|
|
619
|
+
if (this.state) {
|
|
558
620
|
connection.send(
|
|
559
621
|
JSON.stringify({
|
|
560
|
-
|
|
561
|
-
type:
|
|
622
|
+
state: this.state,
|
|
623
|
+
type: MessageType.CF_AGENT_STATE
|
|
562
624
|
})
|
|
563
625
|
);
|
|
626
|
+
}
|
|
564
627
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
628
|
+
connection.send(
|
|
629
|
+
JSON.stringify({
|
|
630
|
+
mcp: this.getMcpServers(),
|
|
631
|
+
type: MessageType.CF_AGENT_MCP_SERVERS
|
|
632
|
+
})
|
|
633
|
+
);
|
|
634
|
+
|
|
635
|
+
this.observability?.emit(
|
|
636
|
+
{
|
|
637
|
+
displayMessage: "Connection established",
|
|
638
|
+
id: nanoid(),
|
|
639
|
+
payload: {
|
|
640
|
+
connectionId: connection.id
|
|
574
641
|
},
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
642
|
+
timestamp: Date.now(),
|
|
643
|
+
type: "connect"
|
|
644
|
+
},
|
|
645
|
+
this.ctx
|
|
646
|
+
);
|
|
647
|
+
return this._tryCatch(() => _onConnect(connection, ctx));
|
|
579
648
|
}
|
|
580
649
|
);
|
|
581
650
|
};
|
|
582
651
|
|
|
583
652
|
const _onStart = this.onStart.bind(this);
|
|
584
|
-
this.onStart = async () => {
|
|
653
|
+
this.onStart = async (props?: Props) => {
|
|
585
654
|
return agentContext.run(
|
|
586
655
|
{
|
|
587
656
|
agent: this,
|
|
@@ -590,15 +659,27 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
590
659
|
email: undefined
|
|
591
660
|
},
|
|
592
661
|
async () => {
|
|
593
|
-
|
|
662
|
+
await this._tryCatch(() => {
|
|
663
|
+
const servers = this.sql<MCPServerRow>`
|
|
594
664
|
SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
|
|
595
665
|
`;
|
|
596
666
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
667
|
+
this.broadcastMcpServers();
|
|
668
|
+
|
|
669
|
+
// from DO storage, reconnect to all servers not currently in the oauth flow using our saved auth information
|
|
670
|
+
if (servers && Array.isArray(servers) && servers.length > 0) {
|
|
671
|
+
// Restore callback URLs for OAuth-enabled servers
|
|
672
|
+
servers.forEach((server) => {
|
|
673
|
+
if (server.callback_url) {
|
|
674
|
+
// Register the full redirect URL including serverId to avoid ambiguous matches
|
|
675
|
+
this.mcp.registerCallbackUrl(
|
|
676
|
+
`${server.callback_url}/${server.id}`
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
servers.forEach((server) => {
|
|
682
|
+
this._connectToMcpServerInternal(
|
|
602
683
|
server.name,
|
|
603
684
|
server.server_url,
|
|
604
685
|
server.callback_url,
|
|
@@ -609,18 +690,23 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
609
690
|
id: server.id,
|
|
610
691
|
oauthClientId: server.client_id ?? undefined
|
|
611
692
|
}
|
|
612
|
-
)
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
693
|
+
)
|
|
694
|
+
.then(() => {
|
|
695
|
+
// Broadcast updated MCP servers state after each server connects
|
|
696
|
+
this.broadcastMcpServers();
|
|
697
|
+
})
|
|
698
|
+
.catch((error) => {
|
|
699
|
+
console.error(
|
|
700
|
+
`Error connecting to MCP server: ${server.name} (${server.server_url})`,
|
|
701
|
+
error
|
|
702
|
+
);
|
|
703
|
+
// Still broadcast even if connection fails, so clients know about the failure
|
|
704
|
+
this.broadcastMcpServers();
|
|
705
|
+
});
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
return _onStart(props);
|
|
709
|
+
});
|
|
624
710
|
}
|
|
625
711
|
);
|
|
626
712
|
};
|
|
@@ -630,7 +716,6 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
630
716
|
state: State,
|
|
631
717
|
source: Connection | "server" = "server"
|
|
632
718
|
) {
|
|
633
|
-
const previousState = this._state;
|
|
634
719
|
this._state = state;
|
|
635
720
|
this.sql`
|
|
636
721
|
INSERT OR REPLACE INTO cf_agents_state (id, state)
|
|
@@ -643,7 +728,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
643
728
|
this.broadcast(
|
|
644
729
|
JSON.stringify({
|
|
645
730
|
state: state,
|
|
646
|
-
type:
|
|
731
|
+
type: MessageType.CF_AGENT_STATE
|
|
647
732
|
}),
|
|
648
733
|
source !== "server" ? [source.id] : []
|
|
649
734
|
);
|
|
@@ -656,10 +741,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
656
741
|
{
|
|
657
742
|
displayMessage: "State updated",
|
|
658
743
|
id: nanoid(),
|
|
659
|
-
payload: {
|
|
660
|
-
previousState,
|
|
661
|
-
state
|
|
662
|
-
},
|
|
744
|
+
payload: {},
|
|
663
745
|
timestamp: Date.now(),
|
|
664
746
|
type: "state:update"
|
|
665
747
|
},
|
|
@@ -799,41 +881,37 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
799
881
|
while (proto && proto !== Object.prototype && depth < 10) {
|
|
800
882
|
const methodNames = Object.getOwnPropertyNames(proto);
|
|
801
883
|
for (const methodName of methodNames) {
|
|
802
|
-
|
|
884
|
+
const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
|
|
885
|
+
|
|
886
|
+
// Skip if it's a private method, a base method, a getter, or not a function,
|
|
803
887
|
if (
|
|
804
888
|
baseMethods.has(methodName) ||
|
|
805
889
|
methodName.startsWith("_") ||
|
|
806
|
-
|
|
890
|
+
!descriptor ||
|
|
891
|
+
!!descriptor.get ||
|
|
892
|
+
typeof descriptor.value !== "function"
|
|
807
893
|
) {
|
|
808
894
|
continue;
|
|
809
895
|
}
|
|
810
|
-
// If the method doesn't exist in base prototypes, it's a custom method
|
|
811
|
-
if (!baseMethods.has(methodName)) {
|
|
812
|
-
const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
|
|
813
|
-
if (descriptor && typeof descriptor.value === "function") {
|
|
814
|
-
// Wrap the custom method with context
|
|
815
|
-
|
|
816
|
-
const wrappedFunction = withAgentContext(
|
|
817
|
-
// biome-ignore lint/suspicious/noExplicitAny: I can't typescript
|
|
818
|
-
this[methodName as keyof this] as (...args: any[]) => any
|
|
819
|
-
// biome-ignore lint/suspicious/noExplicitAny: I can't typescript
|
|
820
|
-
) as any;
|
|
821
|
-
|
|
822
|
-
// if the method is callable, copy the metadata from the original method
|
|
823
|
-
if (this._isCallable(methodName)) {
|
|
824
|
-
callableMetadata.set(
|
|
825
|
-
wrappedFunction,
|
|
826
|
-
callableMetadata.get(
|
|
827
|
-
this[methodName as keyof this] as Function
|
|
828
|
-
)!
|
|
829
|
-
);
|
|
830
|
-
}
|
|
831
896
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
897
|
+
// Now, methodName is confirmed to be a custom method/function
|
|
898
|
+
// Wrap the custom method with context
|
|
899
|
+
const wrappedFunction = withAgentContext(
|
|
900
|
+
// biome-ignore lint/suspicious/noExplicitAny: I can't typescript
|
|
901
|
+
this[methodName as keyof this] as (...args: any[]) => any
|
|
902
|
+
// biome-ignore lint/suspicious/noExplicitAny: I can't typescript
|
|
903
|
+
) as any;
|
|
904
|
+
|
|
905
|
+
// if the method is callable, copy the metadata from the original method
|
|
906
|
+
if (this._isCallable(methodName)) {
|
|
907
|
+
callableMetadata.set(
|
|
908
|
+
wrappedFunction,
|
|
909
|
+
callableMetadata.get(this[methodName as keyof this] as Function)!
|
|
910
|
+
);
|
|
836
911
|
}
|
|
912
|
+
|
|
913
|
+
// set the wrapped function on the prototype
|
|
914
|
+
this.constructor.prototype[methodName as keyof this] = wrappedFunction;
|
|
837
915
|
}
|
|
838
916
|
|
|
839
917
|
proto = Object.getPrototypeOf(proto);
|
|
@@ -875,6 +953,131 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
875
953
|
throw new Error("Not implemented");
|
|
876
954
|
}
|
|
877
955
|
|
|
956
|
+
/**
|
|
957
|
+
* Queue a task to be executed in the future
|
|
958
|
+
* @param payload Payload to pass to the callback
|
|
959
|
+
* @param callback Name of the method to call
|
|
960
|
+
* @returns The ID of the queued task
|
|
961
|
+
*/
|
|
962
|
+
async queue<T = unknown>(callback: keyof this, payload: T): Promise<string> {
|
|
963
|
+
const id = nanoid(9);
|
|
964
|
+
if (typeof callback !== "string") {
|
|
965
|
+
throw new Error("Callback must be a string");
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
if (typeof this[callback] !== "function") {
|
|
969
|
+
throw new Error(`this.${callback} is not a function`);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
this.sql`
|
|
973
|
+
INSERT OR REPLACE INTO cf_agents_queues (id, payload, callback)
|
|
974
|
+
VALUES (${id}, ${JSON.stringify(payload)}, ${callback})
|
|
975
|
+
`;
|
|
976
|
+
|
|
977
|
+
void this._flushQueue().catch((e) => {
|
|
978
|
+
console.error("Error flushing queue:", e);
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
return id;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
private _flushingQueue = false;
|
|
985
|
+
|
|
986
|
+
private async _flushQueue() {
|
|
987
|
+
if (this._flushingQueue) {
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
this._flushingQueue = true;
|
|
991
|
+
while (true) {
|
|
992
|
+
const result = this.sql<QueueItem<string>>`
|
|
993
|
+
SELECT * FROM cf_agents_queues
|
|
994
|
+
ORDER BY created_at ASC
|
|
995
|
+
`;
|
|
996
|
+
|
|
997
|
+
if (!result || result.length === 0) {
|
|
998
|
+
break;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
for (const row of result || []) {
|
|
1002
|
+
const callback = this[row.callback as keyof Agent<Env>];
|
|
1003
|
+
if (!callback) {
|
|
1004
|
+
console.error(`callback ${row.callback} not found`);
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
const { connection, request, email } = agentContext.getStore() || {};
|
|
1008
|
+
await agentContext.run(
|
|
1009
|
+
{
|
|
1010
|
+
agent: this,
|
|
1011
|
+
connection,
|
|
1012
|
+
request,
|
|
1013
|
+
email
|
|
1014
|
+
},
|
|
1015
|
+
async () => {
|
|
1016
|
+
// TODO: add retries and backoff
|
|
1017
|
+
await (
|
|
1018
|
+
callback as (
|
|
1019
|
+
payload: unknown,
|
|
1020
|
+
queueItem: QueueItem<string>
|
|
1021
|
+
) => Promise<void>
|
|
1022
|
+
).bind(this)(JSON.parse(row.payload as string), row);
|
|
1023
|
+
await this.dequeue(row.id);
|
|
1024
|
+
}
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
this._flushingQueue = false;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* Dequeue a task by ID
|
|
1033
|
+
* @param id ID of the task to dequeue
|
|
1034
|
+
*/
|
|
1035
|
+
async dequeue(id: string) {
|
|
1036
|
+
this.sql`DELETE FROM cf_agents_queues WHERE id = ${id}`;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* Dequeue all tasks
|
|
1041
|
+
*/
|
|
1042
|
+
async dequeueAll() {
|
|
1043
|
+
this.sql`DELETE FROM cf_agents_queues`;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/**
|
|
1047
|
+
* Dequeue all tasks by callback
|
|
1048
|
+
* @param callback Name of the callback to dequeue
|
|
1049
|
+
*/
|
|
1050
|
+
async dequeueAllByCallback(callback: string) {
|
|
1051
|
+
this.sql`DELETE FROM cf_agents_queues WHERE callback = ${callback}`;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* Get a queued task by ID
|
|
1056
|
+
* @param id ID of the task to get
|
|
1057
|
+
* @returns The task or undefined if not found
|
|
1058
|
+
*/
|
|
1059
|
+
async getQueue(id: string): Promise<QueueItem<string> | undefined> {
|
|
1060
|
+
const result = this.sql<QueueItem<string>>`
|
|
1061
|
+
SELECT * FROM cf_agents_queues WHERE id = ${id}
|
|
1062
|
+
`;
|
|
1063
|
+
return result
|
|
1064
|
+
? { ...result[0], payload: JSON.parse(result[0].payload) }
|
|
1065
|
+
: undefined;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Get all queues by key and value
|
|
1070
|
+
* @param key Key to filter by
|
|
1071
|
+
* @param value Value to filter by
|
|
1072
|
+
* @returns Array of matching QueueItem objects
|
|
1073
|
+
*/
|
|
1074
|
+
async getQueues(key: string, value: string): Promise<QueueItem<string>[]> {
|
|
1075
|
+
const result = this.sql<QueueItem<string>>`
|
|
1076
|
+
SELECT * FROM cf_agents_queues
|
|
1077
|
+
`;
|
|
1078
|
+
return result.filter((row) => JSON.parse(row.payload)[key] === value);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
878
1081
|
/**
|
|
879
1082
|
* Schedule a task to be executed in the future
|
|
880
1083
|
* @template T Type of the payload data
|
|
@@ -895,7 +1098,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
895
1098
|
{
|
|
896
1099
|
displayMessage: `Schedule ${schedule.id} created`,
|
|
897
1100
|
id: nanoid(),
|
|
898
|
-
payload:
|
|
1101
|
+
payload: {
|
|
1102
|
+
callback: callback as string,
|
|
1103
|
+
id: id
|
|
1104
|
+
},
|
|
899
1105
|
timestamp: Date.now(),
|
|
900
1106
|
type: "schedule:create"
|
|
901
1107
|
},
|
|
@@ -1065,7 +1271,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1065
1271
|
{
|
|
1066
1272
|
displayMessage: `Schedule ${id} cancelled`,
|
|
1067
1273
|
id: nanoid(),
|
|
1068
|
-
payload:
|
|
1274
|
+
payload: {
|
|
1275
|
+
callback: schedule.callback,
|
|
1276
|
+
id: schedule.id
|
|
1277
|
+
},
|
|
1069
1278
|
timestamp: Date.now(),
|
|
1070
1279
|
type: "schedule:cancel"
|
|
1071
1280
|
},
|
|
@@ -1081,9 +1290,9 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1081
1290
|
private async _scheduleNextAlarm() {
|
|
1082
1291
|
// Find the next schedule that needs to be executed
|
|
1083
1292
|
const result = this.sql`
|
|
1084
|
-
SELECT time FROM cf_agents_schedules
|
|
1293
|
+
SELECT time FROM cf_agents_schedules
|
|
1085
1294
|
WHERE time > ${Math.floor(Date.now() / 1000)}
|
|
1086
|
-
ORDER BY time ASC
|
|
1295
|
+
ORDER BY time ASC
|
|
1087
1296
|
LIMIT 1
|
|
1088
1297
|
`;
|
|
1089
1298
|
if (!result) return;
|
|
@@ -1130,7 +1339,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1130
1339
|
{
|
|
1131
1340
|
displayMessage: `Schedule ${row.id} executed`,
|
|
1132
1341
|
id: nanoid(),
|
|
1133
|
-
payload:
|
|
1342
|
+
payload: {
|
|
1343
|
+
callback: row.callback,
|
|
1344
|
+
id: row.id
|
|
1345
|
+
},
|
|
1134
1346
|
timestamp: Date.now(),
|
|
1135
1347
|
type: "schedule:execute"
|
|
1136
1348
|
},
|
|
@@ -1177,10 +1389,13 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1177
1389
|
this.sql`DROP TABLE IF EXISTS cf_agents_state`;
|
|
1178
1390
|
this.sql`DROP TABLE IF EXISTS cf_agents_schedules`;
|
|
1179
1391
|
this.sql`DROP TABLE IF EXISTS cf_agents_mcp_servers`;
|
|
1392
|
+
this.sql`DROP TABLE IF EXISTS cf_agents_queues`;
|
|
1180
1393
|
|
|
1181
1394
|
// delete all alarms
|
|
1182
1395
|
await this.ctx.storage.deleteAlarm();
|
|
1183
1396
|
await this.ctx.storage.deleteAll();
|
|
1397
|
+
this._disposables.dispose();
|
|
1398
|
+
await this.mcp.dispose?.();
|
|
1184
1399
|
this.ctx.abort("destroyed"); // enforce that the agent is evicted
|
|
1185
1400
|
|
|
1186
1401
|
this.observability?.emit(
|
|
@@ -1206,25 +1421,42 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1206
1421
|
/**
|
|
1207
1422
|
* Connect to a new MCP Server
|
|
1208
1423
|
*
|
|
1424
|
+
* @param serverName Name of the MCP server
|
|
1209
1425
|
* @param url MCP Server SSE URL
|
|
1210
|
-
* @param callbackHost Base host for the agent, used for the redirect URI.
|
|
1426
|
+
* @param callbackHost Base host for the agent, used for the redirect URI. If not provided, will be derived from the current request.
|
|
1211
1427
|
* @param agentsPrefix agents routing prefix if not using `agents`
|
|
1212
|
-
* @param options MCP client and transport
|
|
1428
|
+
* @param options MCP client and transport options
|
|
1213
1429
|
* @returns authUrl
|
|
1214
1430
|
*/
|
|
1215
1431
|
async addMcpServer(
|
|
1216
1432
|
serverName: string,
|
|
1217
1433
|
url: string,
|
|
1218
|
-
callbackHost
|
|
1434
|
+
callbackHost?: string,
|
|
1219
1435
|
agentsPrefix = "agents",
|
|
1220
1436
|
options?: {
|
|
1221
1437
|
client?: ConstructorParameters<typeof Client>[1];
|
|
1222
1438
|
transport?: {
|
|
1223
|
-
headers
|
|
1439
|
+
headers?: HeadersInit;
|
|
1440
|
+
type?: TransportType;
|
|
1224
1441
|
};
|
|
1225
1442
|
}
|
|
1226
1443
|
): Promise<{ id: string; authUrl: string | undefined }> {
|
|
1227
|
-
|
|
1444
|
+
// If callbackHost is not provided, derive it from the current request
|
|
1445
|
+
let resolvedCallbackHost = callbackHost;
|
|
1446
|
+
if (!resolvedCallbackHost) {
|
|
1447
|
+
const { request } = getCurrentAgent();
|
|
1448
|
+
if (!request) {
|
|
1449
|
+
throw new Error(
|
|
1450
|
+
"callbackHost is required when not called within a request context"
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
// Extract the origin from the request
|
|
1455
|
+
const requestUrl = new URL(request.url);
|
|
1456
|
+
resolvedCallbackHost = `${requestUrl.protocol}//${requestUrl.host}`;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
const callbackUrl = `${resolvedCallbackHost}/${agentsPrefix}/${camelCaseToKebabCase(this._ParentClass.name)}/${this.name}/callback`;
|
|
1228
1460
|
|
|
1229
1461
|
const result = await this._connectToMcpServerInternal(
|
|
1230
1462
|
serverName,
|
|
@@ -1232,6 +1464,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1232
1464
|
callbackUrl,
|
|
1233
1465
|
options
|
|
1234
1466
|
);
|
|
1467
|
+
|
|
1235
1468
|
this.sql`
|
|
1236
1469
|
INSERT
|
|
1237
1470
|
OR REPLACE INTO cf_agents_mcp_servers (id, name, server_url, client_id, auth_url, callback_url, server_options)
|
|
@@ -1246,17 +1479,12 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1246
1479
|
);
|
|
1247
1480
|
`;
|
|
1248
1481
|
|
|
1249
|
-
this.
|
|
1250
|
-
JSON.stringify({
|
|
1251
|
-
mcp: this.getMcpServers(),
|
|
1252
|
-
type: "cf_agent_mcp_servers"
|
|
1253
|
-
})
|
|
1254
|
-
);
|
|
1482
|
+
this.broadcastMcpServers();
|
|
1255
1483
|
|
|
1256
1484
|
return result;
|
|
1257
1485
|
}
|
|
1258
1486
|
|
|
1259
|
-
async _connectToMcpServerInternal(
|
|
1487
|
+
private async _connectToMcpServerInternal(
|
|
1260
1488
|
_serverName: string,
|
|
1261
1489
|
url: string,
|
|
1262
1490
|
callbackUrl: string,
|
|
@@ -1272,6 +1500,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1272
1500
|
*/
|
|
1273
1501
|
transport?: {
|
|
1274
1502
|
headers?: HeadersInit;
|
|
1503
|
+
type?: TransportType;
|
|
1275
1504
|
};
|
|
1276
1505
|
},
|
|
1277
1506
|
reconnect?: {
|
|
@@ -1296,6 +1525,9 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1296
1525
|
}
|
|
1297
1526
|
}
|
|
1298
1527
|
|
|
1528
|
+
// Use the transport type specified in options, or default to "auto"
|
|
1529
|
+
const transportType: TransportType = options?.transport?.type ?? "auto";
|
|
1530
|
+
|
|
1299
1531
|
// allows passing through transport headers if necessary
|
|
1300
1532
|
// this handles some non-standard bearer auth setups (i.e. MCP server behind CF access instead of OAuth)
|
|
1301
1533
|
let headerTransportOpts: SSEClientTransportOptions = {};
|
|
@@ -1319,7 +1551,8 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1319
1551
|
reconnect,
|
|
1320
1552
|
transport: {
|
|
1321
1553
|
...headerTransportOpts,
|
|
1322
|
-
authProvider
|
|
1554
|
+
authProvider,
|
|
1555
|
+
type: transportType
|
|
1323
1556
|
}
|
|
1324
1557
|
});
|
|
1325
1558
|
|
|
@@ -1332,15 +1565,11 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1332
1565
|
|
|
1333
1566
|
async removeMcpServer(id: string) {
|
|
1334
1567
|
this.mcp.closeConnection(id);
|
|
1568
|
+
this.mcp.unregisterCallbackUrl(id);
|
|
1335
1569
|
this.sql`
|
|
1336
1570
|
DELETE FROM cf_agents_mcp_servers WHERE id = ${id};
|
|
1337
1571
|
`;
|
|
1338
|
-
this.
|
|
1339
|
-
JSON.stringify({
|
|
1340
|
-
mcp: this.getMcpServers(),
|
|
1341
|
-
type: "cf_agent_mcp_servers"
|
|
1342
|
-
})
|
|
1343
|
-
);
|
|
1572
|
+
this.broadcastMcpServers();
|
|
1344
1573
|
}
|
|
1345
1574
|
|
|
1346
1575
|
getMcpServers(): MCPServersState {
|
|
@@ -1372,8 +1601,53 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1372
1601
|
|
|
1373
1602
|
return mcpState;
|
|
1374
1603
|
}
|
|
1604
|
+
|
|
1605
|
+
private broadcastMcpServers() {
|
|
1606
|
+
this.broadcast(
|
|
1607
|
+
JSON.stringify({
|
|
1608
|
+
mcp: this.getMcpServers(),
|
|
1609
|
+
type: MessageType.CF_AGENT_MCP_SERVERS
|
|
1610
|
+
})
|
|
1611
|
+
);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
/**
|
|
1615
|
+
* Handle OAuth callback response using MCPClientManager configuration
|
|
1616
|
+
* @param result OAuth callback result
|
|
1617
|
+
* @param request The original request (needed for base URL)
|
|
1618
|
+
* @returns Response for the OAuth callback
|
|
1619
|
+
*/
|
|
1620
|
+
private handleOAuthCallbackResponse(
|
|
1621
|
+
result: MCPClientOAuthResult,
|
|
1622
|
+
request: Request
|
|
1623
|
+
): Response {
|
|
1624
|
+
const config = this.mcp.getOAuthCallbackConfig();
|
|
1625
|
+
|
|
1626
|
+
// Use custom handler if configured
|
|
1627
|
+
if (config?.customHandler) {
|
|
1628
|
+
return config.customHandler(result);
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
// Use redirect URLs if configured
|
|
1632
|
+
if (config?.successRedirect && result.authSuccess) {
|
|
1633
|
+
return Response.redirect(config.successRedirect);
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
if (config?.errorRedirect && !result.authSuccess) {
|
|
1637
|
+
return Response.redirect(
|
|
1638
|
+
`${config.errorRedirect}?error=${encodeURIComponent(result.authError || "Unknown error")}`
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
// Default behavior - redirect to base URL
|
|
1643
|
+
const baseUrl = new URL(request.url).origin;
|
|
1644
|
+
return Response.redirect(baseUrl);
|
|
1645
|
+
}
|
|
1375
1646
|
}
|
|
1376
1647
|
|
|
1648
|
+
// A set of classes that have been wrapped with agent context
|
|
1649
|
+
const wrappedClasses = new Set<typeof Agent.prototype.constructor>();
|
|
1650
|
+
|
|
1377
1651
|
/**
|
|
1378
1652
|
* Namespace for creating Agent instances
|
|
1379
1653
|
* @template Agentic Type of the Agent class
|
|
@@ -1550,6 +1824,13 @@ export type EmailRoutingOptions<Env> = AgentOptions<Env> & {
|
|
|
1550
1824
|
resolver: EmailResolver<Env>;
|
|
1551
1825
|
};
|
|
1552
1826
|
|
|
1827
|
+
// Cache the agent namespace map for email routing
|
|
1828
|
+
// This maps both kebab-case and original names to namespaces
|
|
1829
|
+
const agentMapCache = new WeakMap<
|
|
1830
|
+
Record<string, unknown>,
|
|
1831
|
+
Record<string, unknown>
|
|
1832
|
+
>();
|
|
1833
|
+
|
|
1553
1834
|
/**
|
|
1554
1835
|
* Route an email to the appropriate Agent
|
|
1555
1836
|
* @param email The email to route
|
|
@@ -1569,28 +1850,41 @@ export async function routeAgentEmail<Env>(
|
|
|
1569
1850
|
return;
|
|
1570
1851
|
}
|
|
1571
1852
|
|
|
1572
|
-
|
|
1573
|
-
if (!
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1853
|
+
// Build a map that includes both original names and kebab-case versions
|
|
1854
|
+
if (!agentMapCache.has(env as Record<string, unknown>)) {
|
|
1855
|
+
const map: Record<string, unknown> = {};
|
|
1856
|
+
for (const [key, value] of Object.entries(env as Record<string, unknown>)) {
|
|
1857
|
+
if (
|
|
1858
|
+
value &&
|
|
1859
|
+
typeof value === "object" &&
|
|
1860
|
+
"idFromName" in value &&
|
|
1861
|
+
typeof value.idFromName === "function"
|
|
1862
|
+
) {
|
|
1863
|
+
// Add both the original name and kebab-case version
|
|
1864
|
+
map[key] = value;
|
|
1865
|
+
map[camelCaseToKebabCase(key)] = value;
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
agentMapCache.set(env as Record<string, unknown>, map);
|
|
1577
1869
|
}
|
|
1578
1870
|
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1871
|
+
const agentMap = agentMapCache.get(env as Record<string, unknown>)!;
|
|
1872
|
+
const namespace = agentMap[routingInfo.agentName];
|
|
1873
|
+
|
|
1874
|
+
if (!namespace) {
|
|
1875
|
+
// Provide helpful error message listing available agents
|
|
1876
|
+
const availableAgents = Object.keys(agentMap)
|
|
1877
|
+
.filter((key) => !key.includes("-")) // Show only original names, not kebab-case duplicates
|
|
1878
|
+
.join(", ");
|
|
1585
1879
|
throw new Error(
|
|
1586
|
-
`
|
|
1880
|
+
`Agent namespace '${routingInfo.agentName}' not found in environment. Available agents: ${availableAgents}`
|
|
1587
1881
|
);
|
|
1588
1882
|
}
|
|
1589
1883
|
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1884
|
+
const agent = await getAgentByName(
|
|
1885
|
+
namespace as unknown as AgentNamespace<Agent<Env>>,
|
|
1886
|
+
routingInfo.agentId
|
|
1887
|
+
);
|
|
1594
1888
|
|
|
1595
1889
|
// let's make a serialisable version of the email
|
|
1596
1890
|
const serialisableEmail: AgentEmail = {
|
|
@@ -1669,12 +1963,17 @@ export type EmailSendOptions = {
|
|
|
1669
1963
|
* @param options Options for Agent creation
|
|
1670
1964
|
* @returns Promise resolving to an Agent instance stub
|
|
1671
1965
|
*/
|
|
1672
|
-
export async function getAgentByName<
|
|
1966
|
+
export async function getAgentByName<
|
|
1967
|
+
Env,
|
|
1968
|
+
T extends Agent<Env>,
|
|
1969
|
+
Props extends Record<string, unknown> = Record<string, unknown>
|
|
1970
|
+
>(
|
|
1673
1971
|
namespace: AgentNamespace<T>,
|
|
1674
1972
|
name: string,
|
|
1675
1973
|
options?: {
|
|
1676
1974
|
jurisdiction?: DurableObjectJurisdiction;
|
|
1677
1975
|
locationHint?: DurableObjectLocationHint;
|
|
1976
|
+
props?: Props;
|
|
1678
1977
|
}
|
|
1679
1978
|
) {
|
|
1680
1979
|
return getServerByName<Env, T>(namespace, name, options);
|
|
@@ -1706,7 +2005,7 @@ export class StreamingResponse {
|
|
|
1706
2005
|
id: this._id,
|
|
1707
2006
|
result: chunk,
|
|
1708
2007
|
success: true,
|
|
1709
|
-
type:
|
|
2008
|
+
type: MessageType.RPC
|
|
1710
2009
|
};
|
|
1711
2010
|
this._connection.send(JSON.stringify(response));
|
|
1712
2011
|
}
|
|
@@ -1725,7 +2024,7 @@ export class StreamingResponse {
|
|
|
1725
2024
|
id: this._id,
|
|
1726
2025
|
result: finalChunk,
|
|
1727
2026
|
success: true,
|
|
1728
|
-
type:
|
|
2027
|
+
type: MessageType.RPC
|
|
1729
2028
|
};
|
|
1730
2029
|
this._connection.send(JSON.stringify(response));
|
|
1731
2030
|
}
|