agents 0.0.0-569680f → 0.0.0-57c0db2
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 +12 -9
- package/dist/ai-chat-agent.js +151 -59
- 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 +68 -71
- 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-RX76B6DL.js → chunk-HS7VEROK.js} +454 -179
- 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-TN7QOY4S.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 +557 -31
- package/dist/index.js +14 -12
- 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 +954 -638
- 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 -12
- package/dist/observability/index.js +6 -4
- package/dist/react.d.ts +10 -6
- package/dist/react.js +8 -5
- 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 +26 -10
- package/src/index.ts +651 -236
- package/dist/chunk-767EASBA.js.map +0 -1
- package/dist/chunk-NKZZ66QY.js.map +0 -1
- package/dist/chunk-RX76B6DL.js.map +0 -1
- package/dist/chunk-TN7QOY4S.js.map +0 -1
- package/dist/index-aBwVVXj7.d.ts +0 -529
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,7 +7,7 @@ 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";
|
|
@@ -18,13 +19,14 @@ import {
|
|
|
18
19
|
Server,
|
|
19
20
|
type WSMessage,
|
|
20
21
|
getServerByName,
|
|
21
|
-
routePartykitRequest
|
|
22
|
+
routePartykitRequest
|
|
22
23
|
} from "partyserver";
|
|
23
24
|
import { camelCaseToKebabCase } from "./client";
|
|
24
25
|
import { MCPClientManager } from "./mcp/client";
|
|
25
26
|
// import type { MCPClientConnection } from "./mcp/client-connection";
|
|
26
27
|
import { DurableObjectOAuthClientProvider } from "./mcp/do-oauth-client-provider";
|
|
27
28
|
import { genericObservability, type Observability } from "./observability";
|
|
29
|
+
import { MessageType } from "./ai-types";
|
|
28
30
|
|
|
29
31
|
export type { Connection, ConnectionContext, WSMessage } from "partyserver";
|
|
30
32
|
|
|
@@ -42,7 +44,7 @@ export type RPCRequest = {
|
|
|
42
44
|
* State update message from client
|
|
43
45
|
*/
|
|
44
46
|
export type StateUpdateMessage = {
|
|
45
|
-
type:
|
|
47
|
+
type: MessageType.CF_AGENT_STATE;
|
|
46
48
|
state: unknown;
|
|
47
49
|
};
|
|
48
50
|
|
|
@@ -50,7 +52,7 @@ export type StateUpdateMessage = {
|
|
|
50
52
|
* RPC response message to client
|
|
51
53
|
*/
|
|
52
54
|
export type RPCResponse = {
|
|
53
|
-
type:
|
|
55
|
+
type: MessageType.RPC;
|
|
54
56
|
id: string;
|
|
55
57
|
} & (
|
|
56
58
|
| {
|
|
@@ -77,7 +79,7 @@ function isRPCRequest(msg: unknown): msg is RPCRequest {
|
|
|
77
79
|
typeof msg === "object" &&
|
|
78
80
|
msg !== null &&
|
|
79
81
|
"type" in msg &&
|
|
80
|
-
msg.type ===
|
|
82
|
+
msg.type === MessageType.RPC &&
|
|
81
83
|
"id" in msg &&
|
|
82
84
|
typeof msg.id === "string" &&
|
|
83
85
|
"method" in msg &&
|
|
@@ -95,7 +97,7 @@ function isStateUpdateMessage(msg: unknown): msg is StateUpdateMessage {
|
|
|
95
97
|
typeof msg === "object" &&
|
|
96
98
|
msg !== null &&
|
|
97
99
|
"type" in msg &&
|
|
98
|
-
msg.type ===
|
|
100
|
+
msg.type === MessageType.CF_AGENT_STATE &&
|
|
99
101
|
"state" in msg
|
|
100
102
|
);
|
|
101
103
|
}
|
|
@@ -116,7 +118,7 @@ const callableMetadata = new Map<Function, CallableMetadata>();
|
|
|
116
118
|
* Decorator that marks a method as callable by clients
|
|
117
119
|
* @param metadata Optional metadata about the callable method
|
|
118
120
|
*/
|
|
119
|
-
export function
|
|
121
|
+
export function callable(metadata: CallableMetadata = {}) {
|
|
120
122
|
return function callableDecorator<This, Args extends unknown[], Return>(
|
|
121
123
|
target: (this: This, ...args: Args) => Return,
|
|
122
124
|
// biome-ignore lint/correctness/noUnusedFunctionParameters: later
|
|
@@ -130,6 +132,30 @@ export function unstable_callable(metadata: CallableMetadata = {}) {
|
|
|
130
132
|
};
|
|
131
133
|
}
|
|
132
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
|
+
|
|
133
159
|
/**
|
|
134
160
|
* Represents a scheduled task within an Agent
|
|
135
161
|
* @template T Type of the payload data
|
|
@@ -175,7 +201,7 @@ function getNextCronTime(cron: string) {
|
|
|
175
201
|
* MCP Server state update message from server -> Client
|
|
176
202
|
*/
|
|
177
203
|
export type MCPServerMessage = {
|
|
178
|
-
type:
|
|
204
|
+
type: MessageType.CF_AGENT_MCP_SERVERS;
|
|
179
205
|
mcp: MCPServersState;
|
|
180
206
|
};
|
|
181
207
|
|
|
@@ -219,23 +245,26 @@ const STATE_WAS_CHANGED = "cf_state_was_changed";
|
|
|
219
245
|
const DEFAULT_STATE = {} as unknown;
|
|
220
246
|
|
|
221
247
|
const agentContext = new AsyncLocalStorage<{
|
|
222
|
-
agent: Agent<unknown>;
|
|
248
|
+
agent: Agent<unknown, unknown>;
|
|
223
249
|
connection: Connection | undefined;
|
|
224
250
|
request: Request | undefined;
|
|
251
|
+
email: AgentEmail | undefined;
|
|
225
252
|
}>();
|
|
226
253
|
|
|
227
254
|
export function getCurrentAgent<
|
|
228
|
-
T extends Agent<unknown, unknown> = Agent<unknown, unknown
|
|
255
|
+
T extends Agent<unknown, unknown> = Agent<unknown, unknown>
|
|
229
256
|
>(): {
|
|
230
257
|
agent: T | undefined;
|
|
231
258
|
connection: Connection | undefined;
|
|
232
|
-
request: Request
|
|
259
|
+
request: Request | undefined;
|
|
260
|
+
email: AgentEmail | undefined;
|
|
233
261
|
} {
|
|
234
262
|
const store = agentContext.getStore() as
|
|
235
263
|
| {
|
|
236
264
|
agent: T;
|
|
237
265
|
connection: Connection | undefined;
|
|
238
|
-
request: Request
|
|
266
|
+
request: Request | undefined;
|
|
267
|
+
email: AgentEmail | undefined;
|
|
239
268
|
}
|
|
240
269
|
| undefined;
|
|
241
270
|
if (!store) {
|
|
@@ -243,17 +272,47 @@ export function getCurrentAgent<
|
|
|
243
272
|
agent: undefined,
|
|
244
273
|
connection: undefined,
|
|
245
274
|
request: undefined,
|
|
275
|
+
email: undefined
|
|
246
276
|
};
|
|
247
277
|
}
|
|
248
278
|
return store;
|
|
249
279
|
}
|
|
250
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
|
+
|
|
251
306
|
/**
|
|
252
307
|
* Base class for creating Agent implementations
|
|
253
308
|
* @template Env Environment type containing bindings
|
|
254
309
|
* @template State State type to store within the Agent
|
|
255
310
|
*/
|
|
256
|
-
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> {
|
|
257
316
|
private _state = DEFAULT_STATE as State;
|
|
258
317
|
|
|
259
318
|
private _ParentClass: typeof Agent<Env, State> =
|
|
@@ -315,7 +374,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
315
374
|
*/
|
|
316
375
|
static options = {
|
|
317
376
|
/** Whether the Agent should hibernate when inactive */
|
|
318
|
-
hibernate: true
|
|
377
|
+
hibernate: true // default to hibernate
|
|
319
378
|
};
|
|
320
379
|
|
|
321
380
|
/**
|
|
@@ -352,6 +411,12 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
352
411
|
constructor(ctx: AgentContext, env: Env) {
|
|
353
412
|
super(ctx, env);
|
|
354
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
|
+
|
|
355
420
|
this.sql`
|
|
356
421
|
CREATE TABLE IF NOT EXISTS cf_agents_state (
|
|
357
422
|
id TEXT PRIMARY KEY NOT NULL,
|
|
@@ -359,6 +424,15 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
359
424
|
)
|
|
360
425
|
`;
|
|
361
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
|
+
|
|
362
436
|
void this.ctx.blockConcurrencyWhile(async () => {
|
|
363
437
|
return this._tryCatch(async () => {
|
|
364
438
|
// Create alarms table if it doesn't exist
|
|
@@ -395,7 +469,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
395
469
|
const _onRequest = this.onRequest.bind(this);
|
|
396
470
|
this.onRequest = (request: Request) => {
|
|
397
471
|
return agentContext.run(
|
|
398
|
-
{ agent: this, connection: undefined, request },
|
|
472
|
+
{ agent: this, connection: undefined, request, email: undefined },
|
|
399
473
|
async () => {
|
|
400
474
|
if (this.mcp.isCallbackRequest(request)) {
|
|
401
475
|
await this.mcp.handleCallbackRequest(request);
|
|
@@ -404,14 +478,14 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
404
478
|
this.broadcast(
|
|
405
479
|
JSON.stringify({
|
|
406
480
|
mcp: this.getMcpServers(),
|
|
407
|
-
type:
|
|
481
|
+
type: MessageType.CF_AGENT_MCP_SERVERS
|
|
408
482
|
})
|
|
409
483
|
);
|
|
410
484
|
|
|
411
485
|
// We probably should let the user configure this response/redirect, but this is fine for now.
|
|
412
486
|
return new Response("<script>window.close();</script>", {
|
|
413
487
|
headers: { "content-type": "text/html" },
|
|
414
|
-
status: 200
|
|
488
|
+
status: 200
|
|
415
489
|
});
|
|
416
490
|
}
|
|
417
491
|
|
|
@@ -423,7 +497,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
423
497
|
const _onMessage = this.onMessage.bind(this);
|
|
424
498
|
this.onMessage = async (connection: Connection, message: WSMessage) => {
|
|
425
499
|
return agentContext.run(
|
|
426
|
-
{ agent: this, connection, request: undefined },
|
|
500
|
+
{ agent: this, connection, request: undefined, email: undefined },
|
|
427
501
|
async () => {
|
|
428
502
|
if (typeof message !== "string") {
|
|
429
503
|
return this._tryCatch(() => _onMessage(connection, message));
|
|
@@ -473,13 +547,11 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
473
547
|
displayMessage: `RPC call to ${method}`,
|
|
474
548
|
id: nanoid(),
|
|
475
549
|
payload: {
|
|
476
|
-
args,
|
|
477
550
|
method,
|
|
478
|
-
streaming: metadata?.streaming
|
|
479
|
-
success: true,
|
|
551
|
+
streaming: metadata?.streaming
|
|
480
552
|
},
|
|
481
553
|
timestamp: Date.now(),
|
|
482
|
-
type: "rpc"
|
|
554
|
+
type: "rpc"
|
|
483
555
|
},
|
|
484
556
|
this.ctx
|
|
485
557
|
);
|
|
@@ -489,7 +561,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
489
561
|
id,
|
|
490
562
|
result,
|
|
491
563
|
success: true,
|
|
492
|
-
type:
|
|
564
|
+
type: MessageType.RPC
|
|
493
565
|
};
|
|
494
566
|
connection.send(JSON.stringify(response));
|
|
495
567
|
} catch (e) {
|
|
@@ -499,7 +571,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
499
571
|
e instanceof Error ? e.message : "Unknown error occurred",
|
|
500
572
|
id: parsed.id,
|
|
501
573
|
success: false,
|
|
502
|
-
type:
|
|
574
|
+
type: MessageType.RPC
|
|
503
575
|
};
|
|
504
576
|
connection.send(JSON.stringify(response));
|
|
505
577
|
console.error("RPC error:", e);
|
|
@@ -517,77 +589,104 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
517
589
|
// TODO: This is a hack to ensure the state is sent after the connection is established
|
|
518
590
|
// must fix this
|
|
519
591
|
return agentContext.run(
|
|
520
|
-
{ agent: this, connection, request: ctx.request },
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
if (this.state) {
|
|
524
|
-
connection.send(
|
|
525
|
-
JSON.stringify({
|
|
526
|
-
state: this.state,
|
|
527
|
-
type: "cf_agent_state",
|
|
528
|
-
})
|
|
529
|
-
);
|
|
530
|
-
}
|
|
531
|
-
|
|
592
|
+
{ agent: this, connection, request: ctx.request, email: undefined },
|
|
593
|
+
() => {
|
|
594
|
+
if (this.state) {
|
|
532
595
|
connection.send(
|
|
533
596
|
JSON.stringify({
|
|
534
|
-
|
|
535
|
-
type:
|
|
597
|
+
state: this.state,
|
|
598
|
+
type: MessageType.CF_AGENT_STATE
|
|
536
599
|
})
|
|
537
600
|
);
|
|
601
|
+
}
|
|
538
602
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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
|
|
548
616
|
},
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
617
|
+
timestamp: Date.now(),
|
|
618
|
+
type: "connect"
|
|
619
|
+
},
|
|
620
|
+
this.ctx
|
|
621
|
+
);
|
|
622
|
+
return this._tryCatch(() => _onConnect(connection, ctx));
|
|
553
623
|
}
|
|
554
624
|
);
|
|
555
625
|
};
|
|
556
626
|
|
|
557
627
|
const _onStart = this.onStart.bind(this);
|
|
558
|
-
this.onStart = async () => {
|
|
628
|
+
this.onStart = async (props?: Props) => {
|
|
559
629
|
return agentContext.run(
|
|
560
|
-
{
|
|
630
|
+
{
|
|
631
|
+
agent: this,
|
|
632
|
+
connection: undefined,
|
|
633
|
+
request: undefined,
|
|
634
|
+
email: undefined
|
|
635
|
+
},
|
|
561
636
|
async () => {
|
|
562
|
-
|
|
637
|
+
await this._tryCatch(() => {
|
|
638
|
+
const servers = this.sql<MCPServerRow>`
|
|
563
639
|
SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
|
|
564
640
|
`;
|
|
565
641
|
|
|
566
|
-
// from DO storage, reconnect to all servers not currently in the oauth flow using our saved auth information
|
|
567
|
-
Promise.allSettled(
|
|
568
|
-
servers.map((server) => {
|
|
569
|
-
return this._connectToMcpServerInternal(
|
|
570
|
-
server.name,
|
|
571
|
-
server.server_url,
|
|
572
|
-
server.callback_url,
|
|
573
|
-
server.server_options
|
|
574
|
-
? JSON.parse(server.server_options)
|
|
575
|
-
: undefined,
|
|
576
|
-
{
|
|
577
|
-
id: server.id,
|
|
578
|
-
oauthClientId: server.client_id ?? undefined,
|
|
579
|
-
}
|
|
580
|
-
);
|
|
581
|
-
})
|
|
582
|
-
).then((_results) => {
|
|
583
642
|
this.broadcast(
|
|
584
643
|
JSON.stringify({
|
|
585
644
|
mcp: this.getMcpServers(),
|
|
586
|
-
type:
|
|
645
|
+
type: MessageType.CF_AGENT_MCP_SERVERS
|
|
587
646
|
})
|
|
588
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);
|
|
589
689
|
});
|
|
590
|
-
await this._tryCatch(() => _onStart());
|
|
591
690
|
}
|
|
592
691
|
);
|
|
593
692
|
};
|
|
@@ -597,7 +696,6 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
597
696
|
state: State,
|
|
598
697
|
source: Connection | "server" = "server"
|
|
599
698
|
) {
|
|
600
|
-
const previousState = this._state;
|
|
601
699
|
this._state = state;
|
|
602
700
|
this.sql`
|
|
603
701
|
INSERT OR REPLACE INTO cf_agents_state (id, state)
|
|
@@ -610,25 +708,22 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
610
708
|
this.broadcast(
|
|
611
709
|
JSON.stringify({
|
|
612
710
|
state: state,
|
|
613
|
-
type:
|
|
711
|
+
type: MessageType.CF_AGENT_STATE
|
|
614
712
|
}),
|
|
615
713
|
source !== "server" ? [source.id] : []
|
|
616
714
|
);
|
|
617
715
|
return this._tryCatch(() => {
|
|
618
|
-
const { connection, request } = agentContext.getStore() || {};
|
|
716
|
+
const { connection, request, email } = agentContext.getStore() || {};
|
|
619
717
|
return agentContext.run(
|
|
620
|
-
{ agent: this, connection, request },
|
|
718
|
+
{ agent: this, connection, request, email },
|
|
621
719
|
async () => {
|
|
622
720
|
this.observability?.emit(
|
|
623
721
|
{
|
|
624
722
|
displayMessage: "State updated",
|
|
625
723
|
id: nanoid(),
|
|
626
|
-
payload: {
|
|
627
|
-
previousState,
|
|
628
|
-
state,
|
|
629
|
-
},
|
|
724
|
+
payload: {},
|
|
630
725
|
timestamp: Date.now(),
|
|
631
|
-
type: "state:update"
|
|
726
|
+
type: "state:update"
|
|
632
727
|
},
|
|
633
728
|
this.ctx
|
|
634
729
|
);
|
|
@@ -657,35 +752,80 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
657
752
|
}
|
|
658
753
|
|
|
659
754
|
/**
|
|
660
|
-
* Called when the Agent receives an email
|
|
755
|
+
* Called when the Agent receives an email via routeAgentEmail()
|
|
661
756
|
* Override this method to handle incoming emails
|
|
662
757
|
* @param email Email message to process
|
|
663
758
|
*/
|
|
664
|
-
async
|
|
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
|
|
665
762
|
return agentContext.run(
|
|
666
|
-
{ agent: this, connection: undefined, request: undefined },
|
|
763
|
+
{ agent: this, connection: undefined, request: undefined, email: email },
|
|
667
764
|
async () => {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
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
|
+
}
|
|
671
776
|
}
|
|
672
777
|
);
|
|
673
778
|
}
|
|
674
779
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
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
|
+
}
|
|
680
795
|
): Promise<void> {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
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
|
+
});
|
|
689
829
|
});
|
|
690
830
|
}
|
|
691
831
|
|
|
@@ -697,6 +837,68 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
697
837
|
}
|
|
698
838
|
}
|
|
699
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
|
+
|
|
700
902
|
override onError(
|
|
701
903
|
connection: Connection,
|
|
702
904
|
error: unknown
|
|
@@ -731,6 +933,131 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
731
933
|
throw new Error("Not implemented");
|
|
732
934
|
}
|
|
733
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
|
+
|
|
734
1061
|
/**
|
|
735
1062
|
* Schedule a task to be executed in the future
|
|
736
1063
|
* @template T Type of the payload data
|
|
@@ -751,9 +1078,12 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
751
1078
|
{
|
|
752
1079
|
displayMessage: `Schedule ${schedule.id} created`,
|
|
753
1080
|
id: nanoid(),
|
|
754
|
-
payload:
|
|
1081
|
+
payload: {
|
|
1082
|
+
callback: callback as string,
|
|
1083
|
+
id: id
|
|
1084
|
+
},
|
|
755
1085
|
timestamp: Date.now(),
|
|
756
|
-
type: "schedule:create"
|
|
1086
|
+
type: "schedule:create"
|
|
757
1087
|
},
|
|
758
1088
|
this.ctx
|
|
759
1089
|
);
|
|
@@ -782,7 +1112,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
782
1112
|
id,
|
|
783
1113
|
payload: payload as T,
|
|
784
1114
|
time: timestamp,
|
|
785
|
-
type: "scheduled"
|
|
1115
|
+
type: "scheduled"
|
|
786
1116
|
};
|
|
787
1117
|
|
|
788
1118
|
emitScheduleCreate(schedule);
|
|
@@ -808,7 +1138,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
808
1138
|
id,
|
|
809
1139
|
payload: payload as T,
|
|
810
1140
|
time: timestamp,
|
|
811
|
-
type: "delayed"
|
|
1141
|
+
type: "delayed"
|
|
812
1142
|
};
|
|
813
1143
|
|
|
814
1144
|
emitScheduleCreate(schedule);
|
|
@@ -834,7 +1164,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
834
1164
|
id,
|
|
835
1165
|
payload: payload as T,
|
|
836
1166
|
time: timestamp,
|
|
837
|
-
type: "cron"
|
|
1167
|
+
type: "cron"
|
|
838
1168
|
};
|
|
839
1169
|
|
|
840
1170
|
emitScheduleCreate(schedule);
|
|
@@ -903,7 +1233,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
903
1233
|
.toArray()
|
|
904
1234
|
.map((row) => ({
|
|
905
1235
|
...row,
|
|
906
|
-
payload: JSON.parse(row.payload as string) as T
|
|
1236
|
+
payload: JSON.parse(row.payload as string) as T
|
|
907
1237
|
})) as Schedule<T>[];
|
|
908
1238
|
|
|
909
1239
|
return result;
|
|
@@ -921,9 +1251,12 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
921
1251
|
{
|
|
922
1252
|
displayMessage: `Schedule ${id} cancelled`,
|
|
923
1253
|
id: nanoid(),
|
|
924
|
-
payload:
|
|
1254
|
+
payload: {
|
|
1255
|
+
callback: schedule.callback,
|
|
1256
|
+
id: schedule.id
|
|
1257
|
+
},
|
|
925
1258
|
timestamp: Date.now(),
|
|
926
|
-
type: "schedule:cancel"
|
|
1259
|
+
type: "schedule:cancel"
|
|
927
1260
|
},
|
|
928
1261
|
this.ctx
|
|
929
1262
|
);
|
|
@@ -937,9 +1270,9 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
937
1270
|
private async _scheduleNextAlarm() {
|
|
938
1271
|
// Find the next schedule that needs to be executed
|
|
939
1272
|
const result = this.sql`
|
|
940
|
-
SELECT time FROM cf_agents_schedules
|
|
1273
|
+
SELECT time FROM cf_agents_schedules
|
|
941
1274
|
WHERE time > ${Math.floor(Date.now() / 1000)}
|
|
942
|
-
ORDER BY time ASC
|
|
1275
|
+
ORDER BY time ASC
|
|
943
1276
|
LIMIT 1
|
|
944
1277
|
`;
|
|
945
1278
|
if (!result) return;
|
|
@@ -966,51 +1299,61 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
966
1299
|
SELECT * FROM cf_agents_schedules WHERE time <= ${now}
|
|
967
1300
|
`;
|
|
968
1301
|
|
|
969
|
-
|
|
970
|
-
const
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
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;
|
|
1308
|
+
}
|
|
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
|
+
);
|
|
989
1331
|
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
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
|
+
}
|
|
998
1341
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
const nextTimestamp = Math.floor(nextExecutionTime.getTime() / 1000);
|
|
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);
|
|
1005
1347
|
|
|
1006
|
-
|
|
1348
|
+
this.sql`
|
|
1007
1349
|
UPDATE cf_agents_schedules SET time = ${nextTimestamp} WHERE id = ${row.id}
|
|
1008
1350
|
`;
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1351
|
+
} else {
|
|
1352
|
+
// Delete one-time schedules after execution
|
|
1353
|
+
this.sql`
|
|
1012
1354
|
DELETE FROM cf_agents_schedules WHERE id = ${row.id}
|
|
1013
1355
|
`;
|
|
1356
|
+
}
|
|
1014
1357
|
}
|
|
1015
1358
|
}
|
|
1016
1359
|
|
|
@@ -1026,6 +1369,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1026
1369
|
this.sql`DROP TABLE IF EXISTS cf_agents_state`;
|
|
1027
1370
|
this.sql`DROP TABLE IF EXISTS cf_agents_schedules`;
|
|
1028
1371
|
this.sql`DROP TABLE IF EXISTS cf_agents_mcp_servers`;
|
|
1372
|
+
this.sql`DROP TABLE IF EXISTS cf_agents_queues`;
|
|
1029
1373
|
|
|
1030
1374
|
// delete all alarms
|
|
1031
1375
|
await this.ctx.storage.deleteAlarm();
|
|
@@ -1038,7 +1382,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1038
1382
|
id: nanoid(),
|
|
1039
1383
|
payload: {},
|
|
1040
1384
|
timestamp: Date.now(),
|
|
1041
|
-
type: "destroy"
|
|
1385
|
+
type: "destroy"
|
|
1042
1386
|
},
|
|
1043
1387
|
this.ctx
|
|
1044
1388
|
);
|
|
@@ -1098,7 +1442,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1098
1442
|
this.broadcast(
|
|
1099
1443
|
JSON.stringify({
|
|
1100
1444
|
mcp: this.getMcpServers(),
|
|
1101
|
-
type:
|
|
1445
|
+
type: MessageType.CF_AGENT_MCP_SERVERS
|
|
1102
1446
|
})
|
|
1103
1447
|
);
|
|
1104
1448
|
|
|
@@ -1154,12 +1498,12 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1154
1498
|
fetch: (url, init) =>
|
|
1155
1499
|
fetch(url, {
|
|
1156
1500
|
...init,
|
|
1157
|
-
headers: options?.transport?.headers
|
|
1158
|
-
})
|
|
1501
|
+
headers: options?.transport?.headers
|
|
1502
|
+
})
|
|
1159
1503
|
},
|
|
1160
1504
|
requestInit: {
|
|
1161
|
-
headers: options?.transport?.headers
|
|
1162
|
-
}
|
|
1505
|
+
headers: options?.transport?.headers
|
|
1506
|
+
}
|
|
1163
1507
|
};
|
|
1164
1508
|
}
|
|
1165
1509
|
|
|
@@ -1168,14 +1512,14 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1168
1512
|
reconnect,
|
|
1169
1513
|
transport: {
|
|
1170
1514
|
...headerTransportOpts,
|
|
1171
|
-
authProvider
|
|
1172
|
-
}
|
|
1515
|
+
authProvider
|
|
1516
|
+
}
|
|
1173
1517
|
});
|
|
1174
1518
|
|
|
1175
1519
|
return {
|
|
1176
1520
|
authUrl,
|
|
1177
1521
|
clientId,
|
|
1178
|
-
id
|
|
1522
|
+
id
|
|
1179
1523
|
};
|
|
1180
1524
|
}
|
|
1181
1525
|
|
|
@@ -1187,7 +1531,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1187
1531
|
this.broadcast(
|
|
1188
1532
|
JSON.stringify({
|
|
1189
1533
|
mcp: this.getMcpServers(),
|
|
1190
|
-
type:
|
|
1534
|
+
type: MessageType.CF_AGENT_MCP_SERVERS
|
|
1191
1535
|
})
|
|
1192
1536
|
);
|
|
1193
1537
|
}
|
|
@@ -1197,30 +1541,35 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1197
1541
|
prompts: this.mcp.listPrompts(),
|
|
1198
1542
|
resources: this.mcp.listResources(),
|
|
1199
1543
|
servers: {},
|
|
1200
|
-
tools: this.mcp.listTools()
|
|
1544
|
+
tools: this.mcp.listTools()
|
|
1201
1545
|
};
|
|
1202
1546
|
|
|
1203
1547
|
const servers = this.sql<MCPServerRow>`
|
|
1204
1548
|
SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
|
|
1205
1549
|
`;
|
|
1206
1550
|
|
|
1207
|
-
|
|
1208
|
-
const
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
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
|
+
}
|
|
1218
1564
|
}
|
|
1219
1565
|
|
|
1220
1566
|
return mcpState;
|
|
1221
1567
|
}
|
|
1222
1568
|
}
|
|
1223
1569
|
|
|
1570
|
+
// A set of classes that have been wrapped with agent context
|
|
1571
|
+
const wrappedClasses = new Set<typeof Agent.prototype.constructor>();
|
|
1572
|
+
|
|
1224
1573
|
/**
|
|
1225
1574
|
* Namespace for creating Agent instances
|
|
1226
1575
|
* @template Agentic Type of the Agent class
|
|
@@ -1261,14 +1610,14 @@ export async function routeAgentRequest<Env>(
|
|
|
1261
1610
|
"Access-Control-Allow-Credentials": "true",
|
|
1262
1611
|
"Access-Control-Allow-Methods": "GET, POST, HEAD, OPTIONS",
|
|
1263
1612
|
"Access-Control-Allow-Origin": "*",
|
|
1264
|
-
"Access-Control-Max-Age": "86400"
|
|
1613
|
+
"Access-Control-Max-Age": "86400"
|
|
1265
1614
|
}
|
|
1266
1615
|
: options?.cors;
|
|
1267
1616
|
|
|
1268
1617
|
if (request.method === "OPTIONS") {
|
|
1269
1618
|
if (corsHeaders) {
|
|
1270
1619
|
return new Response(null, {
|
|
1271
|
-
headers: corsHeaders
|
|
1620
|
+
headers: corsHeaders
|
|
1272
1621
|
});
|
|
1273
1622
|
}
|
|
1274
1623
|
console.warn(
|
|
@@ -1281,7 +1630,7 @@ export async function routeAgentRequest<Env>(
|
|
|
1281
1630
|
env as Record<string, unknown>,
|
|
1282
1631
|
{
|
|
1283
1632
|
prefix: "agents",
|
|
1284
|
-
...(options as PartyServerOptions<Record<string, unknown>>)
|
|
1633
|
+
...(options as PartyServerOptions<Record<string, unknown>>)
|
|
1285
1634
|
}
|
|
1286
1635
|
);
|
|
1287
1636
|
|
|
@@ -1294,8 +1643,8 @@ export async function routeAgentRequest<Env>(
|
|
|
1294
1643
|
response = new Response(response.body, {
|
|
1295
1644
|
headers: {
|
|
1296
1645
|
...response.headers,
|
|
1297
|
-
...corsHeaders
|
|
1298
|
-
}
|
|
1646
|
+
...corsHeaders
|
|
1647
|
+
}
|
|
1299
1648
|
});
|
|
1300
1649
|
}
|
|
1301
1650
|
return response;
|
|
@@ -1309,7 +1658,11 @@ export type EmailResolver<Env> = (
|
|
|
1309
1658
|
agentId: string;
|
|
1310
1659
|
} | null>;
|
|
1311
1660
|
|
|
1312
|
-
|
|
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> {
|
|
1313
1666
|
return async (email: ForwardableEmailMessage, _env: Env) => {
|
|
1314
1667
|
const messageId = email.headers.get("message-id");
|
|
1315
1668
|
if (messageId) {
|
|
@@ -1344,7 +1697,12 @@ export function createHeaderBasedResolver<Env>(): EmailResolver<Env> {
|
|
|
1344
1697
|
};
|
|
1345
1698
|
}
|
|
1346
1699
|
|
|
1347
|
-
|
|
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>(
|
|
1348
1706
|
defaultAgentName: string
|
|
1349
1707
|
): EmailResolver<Env> {
|
|
1350
1708
|
return async (email: ForwardableEmailMessage, _env: Env) => {
|
|
@@ -1358,7 +1716,7 @@ export function createAddressBasedResolver<Env>(
|
|
|
1358
1716
|
if (subAddress) {
|
|
1359
1717
|
return {
|
|
1360
1718
|
agentName: localPart,
|
|
1361
|
-
agentId: subAddress
|
|
1719
|
+
agentId: subAddress
|
|
1362
1720
|
};
|
|
1363
1721
|
}
|
|
1364
1722
|
|
|
@@ -1366,12 +1724,18 @@ export function createAddressBasedResolver<Env>(
|
|
|
1366
1724
|
// Common for catch-all email routing to a single EmailAgent namespace
|
|
1367
1725
|
return {
|
|
1368
1726
|
agentName: defaultAgentName,
|
|
1369
|
-
agentId: localPart
|
|
1727
|
+
agentId: localPart
|
|
1370
1728
|
};
|
|
1371
1729
|
};
|
|
1372
1730
|
}
|
|
1373
1731
|
|
|
1374
|
-
|
|
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>(
|
|
1375
1739
|
agentName: string,
|
|
1376
1740
|
agentId: string
|
|
1377
1741
|
): EmailResolver<Env> {
|
|
@@ -1382,6 +1746,20 @@ export type EmailRoutingOptions<Env> = AgentOptions<Env> & {
|
|
|
1382
1746
|
resolver: EmailResolver<Env>;
|
|
1383
1747
|
};
|
|
1384
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
|
+
|
|
1756
|
+
/**
|
|
1757
|
+
* Route an email to the appropriate Agent
|
|
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
|
|
1762
|
+
*/
|
|
1385
1763
|
export async function routeAgentEmail<Env>(
|
|
1386
1764
|
email: ForwardableEmailMessage,
|
|
1387
1765
|
env: Env,
|
|
@@ -1394,33 +1772,98 @@ export async function routeAgentEmail<Env>(
|
|
|
1394
1772
|
return;
|
|
1395
1773
|
}
|
|
1396
1774
|
|
|
1397
|
-
|
|
1398
|
-
if (!
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
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);
|
|
1403
1791
|
}
|
|
1404
1792
|
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
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}`
|
|
1413
1803
|
);
|
|
1414
|
-
return;
|
|
1415
1804
|
}
|
|
1416
1805
|
|
|
1417
|
-
|
|
1418
|
-
|
|
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
|
+
};
|
|
1419
1852
|
|
|
1420
|
-
|
|
1421
|
-
await agent.onEmail(email);
|
|
1853
|
+
await agent._onEmail(serialisableEmail);
|
|
1422
1854
|
}
|
|
1423
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
|
+
|
|
1424
1867
|
export type EmailSendOptions = {
|
|
1425
1868
|
to: string;
|
|
1426
1869
|
subject: string;
|
|
@@ -1433,39 +1876,6 @@ export type EmailSendOptions = {
|
|
|
1433
1876
|
domain?: string;
|
|
1434
1877
|
};
|
|
1435
1878
|
|
|
1436
|
-
export async function sendEmailWithRouting(
|
|
1437
|
-
emailBinding: SendEmail,
|
|
1438
|
-
from: string,
|
|
1439
|
-
fromName: string,
|
|
1440
|
-
options: EmailSendOptions
|
|
1441
|
-
): Promise<void> {
|
|
1442
|
-
const { createMimeMessage } = await import("mimetext");
|
|
1443
|
-
const msg = createMimeMessage();
|
|
1444
|
-
msg.setSender({ addr: from, name: fromName });
|
|
1445
|
-
msg.setRecipient(options.to);
|
|
1446
|
-
msg.setSubject(options.subject);
|
|
1447
|
-
msg.addMessage({
|
|
1448
|
-
contentType: options.contentType || "text/plain",
|
|
1449
|
-
data: options.body,
|
|
1450
|
-
});
|
|
1451
|
-
|
|
1452
|
-
if (options.includeRoutingHeaders && options.agentName && options.agentId) {
|
|
1453
|
-
const domain = options.domain || from.split("@")[1];
|
|
1454
|
-
const messageId = `<${options.agentId}@${domain}>`;
|
|
1455
|
-
msg.setHeader("Message-ID", messageId);
|
|
1456
|
-
msg.setHeader("X-Agent-Name", options.agentName);
|
|
1457
|
-
msg.setHeader("X-Agent-ID", options.agentId);
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
if (options.headers) {
|
|
1461
|
-
for (const [key, value] of Object.entries(options.headers)) {
|
|
1462
|
-
msg.setHeader(key, value);
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1466
|
-
await emailBinding.send(new EmailMessage(from, options.to, msg.asRaw()));
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
1879
|
/**
|
|
1470
1880
|
* Get or create an Agent by name
|
|
1471
1881
|
* @template Env Environment type containing bindings
|
|
@@ -1475,12 +1885,17 @@ export async function sendEmailWithRouting(
|
|
|
1475
1885
|
* @param options Options for Agent creation
|
|
1476
1886
|
* @returns Promise resolving to an Agent instance stub
|
|
1477
1887
|
*/
|
|
1478
|
-
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
|
+
>(
|
|
1479
1893
|
namespace: AgentNamespace<T>,
|
|
1480
1894
|
name: string,
|
|
1481
1895
|
options?: {
|
|
1482
1896
|
jurisdiction?: DurableObjectJurisdiction;
|
|
1483
1897
|
locationHint?: DurableObjectLocationHint;
|
|
1898
|
+
props?: Props;
|
|
1484
1899
|
}
|
|
1485
1900
|
) {
|
|
1486
1901
|
return getServerByName<Env, T>(namespace, name, options);
|
|
@@ -1512,7 +1927,7 @@ export class StreamingResponse {
|
|
|
1512
1927
|
id: this._id,
|
|
1513
1928
|
result: chunk,
|
|
1514
1929
|
success: true,
|
|
1515
|
-
type:
|
|
1930
|
+
type: MessageType.RPC
|
|
1516
1931
|
};
|
|
1517
1932
|
this._connection.send(JSON.stringify(response));
|
|
1518
1933
|
}
|
|
@@ -1531,7 +1946,7 @@ export class StreamingResponse {
|
|
|
1531
1946
|
id: this._id,
|
|
1532
1947
|
result: finalChunk,
|
|
1533
1948
|
success: true,
|
|
1534
|
-
type:
|
|
1949
|
+
type: MessageType.RPC
|
|
1535
1950
|
};
|
|
1536
1951
|
this._connection.send(JSON.stringify(response));
|
|
1537
1952
|
}
|