agents 0.0.0-ac0e999 → 0.0.0-ac74811
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/dist/ai-chat-agent.d.ts +13 -12
- package/dist/ai-chat-agent.js +94 -57
- package/dist/ai-chat-agent.js.map +1 -1
- package/dist/ai-react.d.ts +9 -8
- package/dist/ai-react.js +27 -29
- package/dist/ai-react.js.map +1 -1
- package/dist/{chunk-D6UOOELW.js → chunk-767EASBA.js} +15 -15
- package/dist/chunk-767EASBA.js.map +1 -0
- package/dist/{chunk-25YDMV4H.js → chunk-E3LCYPCB.js} +34 -29
- package/dist/chunk-E3LCYPCB.js.map +1 -0
- package/dist/{chunk-DMJ7L3FI.js → chunk-JFRK72K3.js} +305 -168
- package/dist/chunk-JFRK72K3.js.map +1 -0
- package/dist/{chunk-ZKIVUOTQ.js → chunk-NKZZ66QY.js} +14 -21
- package/dist/chunk-NKZZ66QY.js.map +1 -0
- package/dist/client.d.ts +7 -1
- package/dist/client.js +1 -2
- package/dist/index-CITGJflw.d.ts +486 -0
- package/dist/index.d.ts +25 -369
- package/dist/index.js +4 -5
- package/dist/mcp/client.d.ts +287 -15
- package/dist/mcp/client.js +1 -2
- package/dist/mcp/do-oauth-client-provider.d.ts +3 -3
- package/dist/mcp/do-oauth-client-provider.js +1 -2
- package/dist/mcp/index.d.ts +12 -12
- package/dist/mcp/index.js +124 -121
- package/dist/mcp/index.js.map +1 -1
- package/dist/observability/index.d.ts +12 -0
- package/dist/observability/index.js +10 -0
- package/dist/react.d.ts +76 -10
- package/dist/react.js +16 -8
- package/dist/react.js.map +1 -1
- package/dist/schedule.d.ts +6 -6
- package/dist/schedule.js +4 -6
- package/dist/schedule.js.map +1 -1
- package/dist/serializable.d.ts +32 -0
- package/dist/serializable.js +1 -0
- package/dist/serializable.js.map +1 -0
- package/package.json +75 -71
- package/src/index.ts +253 -131
- package/dist/chunk-25YDMV4H.js.map +0 -1
- package/dist/chunk-D6UOOELW.js.map +0 -1
- package/dist/chunk-DMJ7L3FI.js.map +0 -1
- package/dist/chunk-NOUFNU2O.js +0 -12
- package/dist/chunk-ZKIVUOTQ.js.map +0 -1
- /package/dist/{chunk-NOUFNU2O.js.map → observability/index.js.map} +0 -0
package/src/index.ts
CHANGED
|
@@ -1,34 +1,31 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3
|
+
import type { SSEClientTransportOptions } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
Prompt,
|
|
7
|
+
Resource,
|
|
8
|
+
ServerCapabilities,
|
|
9
|
+
Tool,
|
|
10
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
11
|
+
import { parseCronExpression } from "cron-schedule";
|
|
12
|
+
import { nanoid } from "nanoid";
|
|
1
13
|
import {
|
|
2
|
-
Server,
|
|
3
|
-
routePartykitRequest,
|
|
4
|
-
type PartyServerOptions,
|
|
5
|
-
getServerByName,
|
|
6
14
|
type Connection,
|
|
7
15
|
type ConnectionContext,
|
|
16
|
+
getServerByName,
|
|
17
|
+
type PartyServerOptions,
|
|
18
|
+
routePartykitRequest,
|
|
19
|
+
Server,
|
|
8
20
|
type WSMessage,
|
|
9
21
|
} from "partyserver";
|
|
10
|
-
|
|
11
|
-
import { parseCronExpression } from "cron-schedule";
|
|
12
|
-
import { nanoid } from "nanoid";
|
|
13
|
-
|
|
14
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
15
|
-
import { MCPClientManager } from "./mcp/client";
|
|
16
|
-
import {
|
|
17
|
-
DurableObjectOAuthClientProvider,
|
|
18
|
-
type AgentsOAuthProvider,
|
|
19
|
-
} from "./mcp/do-oauth-client-provider";
|
|
20
|
-
import type {
|
|
21
|
-
Tool,
|
|
22
|
-
Resource,
|
|
23
|
-
Prompt,
|
|
24
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
25
|
-
|
|
26
|
-
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
27
|
-
import type { SSEClientTransportOptions } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
28
|
-
|
|
29
22
|
import { camelCaseToKebabCase } from "./client";
|
|
23
|
+
import { MCPClientManager } from "./mcp/client";
|
|
24
|
+
// import type { MCPClientConnection } from "./mcp/client-connection";
|
|
25
|
+
import { DurableObjectOAuthClientProvider } from "./mcp/do-oauth-client-provider";
|
|
26
|
+
import { genericObservability, type Observability } from "./observability";
|
|
30
27
|
|
|
31
|
-
export type { Connection,
|
|
28
|
+
export type { Connection, ConnectionContext, WSMessage } from "partyserver";
|
|
32
29
|
|
|
33
30
|
/**
|
|
34
31
|
* RPC request message from client
|
|
@@ -112,7 +109,6 @@ export type CallableMetadata = {
|
|
|
112
109
|
streaming?: boolean;
|
|
113
110
|
};
|
|
114
111
|
|
|
115
|
-
// biome-ignore lint/complexity/noBannedTypes: <explanation>
|
|
116
112
|
const callableMetadata = new Map<Function, CallableMetadata>();
|
|
117
113
|
|
|
118
114
|
/**
|
|
@@ -122,6 +118,7 @@ const callableMetadata = new Map<Function, CallableMetadata>();
|
|
|
122
118
|
export function unstable_callable(metadata: CallableMetadata = {}) {
|
|
123
119
|
return function callableDecorator<This, Args extends unknown[], Return>(
|
|
124
120
|
target: (this: This, ...args: Args) => Return,
|
|
121
|
+
// biome-ignore lint/correctness/noUnusedFunctionParameters: later
|
|
125
122
|
context: ClassMethodDecoratorContext
|
|
126
123
|
) {
|
|
127
124
|
if (!callableMetadata.has(target)) {
|
|
@@ -194,7 +191,12 @@ export type MCPServer = {
|
|
|
194
191
|
name: string;
|
|
195
192
|
server_url: string;
|
|
196
193
|
auth_url: string | null;
|
|
194
|
+
// This state is specifically about the temporary process of getting a token (if needed).
|
|
195
|
+
// Scope outside of that can't be relied upon because when the DO sleeps, there's no way
|
|
196
|
+
// to communicate a change to a non-ready state.
|
|
197
197
|
state: "authenticating" | "connecting" | "ready" | "discovering" | "failed";
|
|
198
|
+
instructions: string | null;
|
|
199
|
+
capabilities: ServerCapabilities | null;
|
|
198
200
|
};
|
|
199
201
|
|
|
200
202
|
/**
|
|
@@ -253,10 +255,10 @@ export function getCurrentAgent<
|
|
|
253
255
|
export class Agent<Env, State = unknown> extends Server<Env> {
|
|
254
256
|
private _state = DEFAULT_STATE as State;
|
|
255
257
|
|
|
256
|
-
private
|
|
258
|
+
private _ParentClass: typeof Agent<Env, State> =
|
|
257
259
|
Object.getPrototypeOf(this).constructor;
|
|
258
260
|
|
|
259
|
-
mcp: MCPClientManager = new MCPClientManager(this.
|
|
261
|
+
mcp: MCPClientManager = new MCPClientManager(this._ParentClass.name, "0.0.1");
|
|
260
262
|
|
|
261
263
|
/**
|
|
262
264
|
* Initial state for the Agent
|
|
@@ -315,6 +317,11 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
315
317
|
hibernate: true, // default to hibernate
|
|
316
318
|
};
|
|
317
319
|
|
|
320
|
+
/**
|
|
321
|
+
* The observability implementation to use for the Agent
|
|
322
|
+
*/
|
|
323
|
+
observability?: Observability = genericObservability;
|
|
324
|
+
|
|
318
325
|
/**
|
|
319
326
|
* Execute SQL queries against the Agent's database
|
|
320
327
|
* @template T Type of the returned rows
|
|
@@ -352,7 +359,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
352
359
|
`;
|
|
353
360
|
|
|
354
361
|
void this.ctx.blockConcurrencyWhile(async () => {
|
|
355
|
-
return this.
|
|
362
|
+
return this._tryCatch(async () => {
|
|
356
363
|
// Create alarms table if it doesn't exist
|
|
357
364
|
this.sql`
|
|
358
365
|
CREATE TABLE IF NOT EXISTS cf_agents_schedules (
|
|
@@ -395,19 +402,19 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
395
402
|
// after the MCP connection handshake, we can send updated mcp state
|
|
396
403
|
this.broadcast(
|
|
397
404
|
JSON.stringify({
|
|
405
|
+
mcp: this.getMcpServers(),
|
|
398
406
|
type: "cf_agent_mcp_servers",
|
|
399
|
-
mcp: this.#getMcpServerStateInternal(),
|
|
400
407
|
})
|
|
401
408
|
);
|
|
402
409
|
|
|
403
410
|
// We probably should let the user configure this response/redirect, but this is fine for now.
|
|
404
411
|
return new Response("<script>window.close();</script>", {
|
|
405
|
-
status: 200,
|
|
406
412
|
headers: { "content-type": "text/html" },
|
|
413
|
+
status: 200,
|
|
407
414
|
});
|
|
408
415
|
}
|
|
409
416
|
|
|
410
|
-
return this.
|
|
417
|
+
return this._tryCatch(() => _onRequest(request));
|
|
411
418
|
}
|
|
412
419
|
);
|
|
413
420
|
};
|
|
@@ -418,19 +425,19 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
418
425
|
{ agent: this, connection, request: undefined },
|
|
419
426
|
async () => {
|
|
420
427
|
if (typeof message !== "string") {
|
|
421
|
-
return this.
|
|
428
|
+
return this._tryCatch(() => _onMessage(connection, message));
|
|
422
429
|
}
|
|
423
430
|
|
|
424
431
|
let parsed: unknown;
|
|
425
432
|
try {
|
|
426
433
|
parsed = JSON.parse(message);
|
|
427
|
-
} catch (
|
|
434
|
+
} catch (_e) {
|
|
428
435
|
// silently fail and let the onMessage handler handle it
|
|
429
|
-
return this.
|
|
436
|
+
return this._tryCatch(() => _onMessage(connection, message));
|
|
430
437
|
}
|
|
431
438
|
|
|
432
439
|
if (isStateUpdateMessage(parsed)) {
|
|
433
|
-
this.
|
|
440
|
+
this._setStateInternal(parsed.state as State, connection);
|
|
434
441
|
return;
|
|
435
442
|
}
|
|
436
443
|
|
|
@@ -444,11 +451,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
444
451
|
throw new Error(`Method ${method} does not exist`);
|
|
445
452
|
}
|
|
446
453
|
|
|
447
|
-
if (!this.
|
|
454
|
+
if (!this._isCallable(method)) {
|
|
448
455
|
throw new Error(`Method ${method} is not callable`);
|
|
449
456
|
}
|
|
450
457
|
|
|
451
|
-
// biome-ignore lint/complexity/noBannedTypes: <explanation>
|
|
452
458
|
const metadata = callableMetadata.get(methodFn as Function);
|
|
453
459
|
|
|
454
460
|
// For streaming methods, pass a StreamingResponse object
|
|
@@ -460,22 +466,39 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
460
466
|
|
|
461
467
|
// For regular methods, execute and send response
|
|
462
468
|
const result = await methodFn.apply(this, args);
|
|
469
|
+
|
|
470
|
+
this.observability?.emit(
|
|
471
|
+
{
|
|
472
|
+
displayMessage: `RPC call to ${method}`,
|
|
473
|
+
id: nanoid(),
|
|
474
|
+
payload: {
|
|
475
|
+
args,
|
|
476
|
+
method,
|
|
477
|
+
streaming: metadata?.streaming,
|
|
478
|
+
success: true,
|
|
479
|
+
},
|
|
480
|
+
timestamp: Date.now(),
|
|
481
|
+
type: "rpc",
|
|
482
|
+
},
|
|
483
|
+
this.ctx
|
|
484
|
+
);
|
|
485
|
+
|
|
463
486
|
const response: RPCResponse = {
|
|
464
|
-
|
|
487
|
+
done: true,
|
|
465
488
|
id,
|
|
466
|
-
success: true,
|
|
467
489
|
result,
|
|
468
|
-
|
|
490
|
+
success: true,
|
|
491
|
+
type: "rpc",
|
|
469
492
|
};
|
|
470
493
|
connection.send(JSON.stringify(response));
|
|
471
494
|
} catch (e) {
|
|
472
495
|
// Send error response
|
|
473
496
|
const response: RPCResponse = {
|
|
474
|
-
type: "rpc",
|
|
475
|
-
id: parsed.id,
|
|
476
|
-
success: false,
|
|
477
497
|
error:
|
|
478
498
|
e instanceof Error ? e.message : "Unknown error occurred",
|
|
499
|
+
id: parsed.id,
|
|
500
|
+
success: false,
|
|
501
|
+
type: "rpc",
|
|
479
502
|
};
|
|
480
503
|
connection.send(JSON.stringify(response));
|
|
481
504
|
console.error("RPC error:", e);
|
|
@@ -483,7 +506,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
483
506
|
return;
|
|
484
507
|
}
|
|
485
508
|
|
|
486
|
-
return this.
|
|
509
|
+
return this._tryCatch(() => _onMessage(connection, message));
|
|
487
510
|
}
|
|
488
511
|
);
|
|
489
512
|
};
|
|
@@ -499,20 +522,32 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
499
522
|
if (this.state) {
|
|
500
523
|
connection.send(
|
|
501
524
|
JSON.stringify({
|
|
502
|
-
type: "cf_agent_state",
|
|
503
525
|
state: this.state,
|
|
526
|
+
type: "cf_agent_state",
|
|
504
527
|
})
|
|
505
528
|
);
|
|
506
529
|
}
|
|
507
530
|
|
|
508
531
|
connection.send(
|
|
509
532
|
JSON.stringify({
|
|
533
|
+
mcp: this.getMcpServers(),
|
|
510
534
|
type: "cf_agent_mcp_servers",
|
|
511
|
-
mcp: this.#getMcpServerStateInternal(),
|
|
512
535
|
})
|
|
513
536
|
);
|
|
514
537
|
|
|
515
|
-
|
|
538
|
+
this.observability?.emit(
|
|
539
|
+
{
|
|
540
|
+
displayMessage: "Connection established",
|
|
541
|
+
id: nanoid(),
|
|
542
|
+
payload: {
|
|
543
|
+
connectionId: connection.id,
|
|
544
|
+
},
|
|
545
|
+
timestamp: Date.now(),
|
|
546
|
+
type: "connect",
|
|
547
|
+
},
|
|
548
|
+
this.ctx
|
|
549
|
+
);
|
|
550
|
+
return this._tryCatch(() => _onConnect(connection, ctx));
|
|
516
551
|
}, 20);
|
|
517
552
|
}
|
|
518
553
|
);
|
|
@@ -527,10 +562,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
527
562
|
SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
|
|
528
563
|
`;
|
|
529
564
|
|
|
530
|
-
// from DO storage, reconnect to all servers using our saved auth information
|
|
531
|
-
|
|
565
|
+
// from DO storage, reconnect to all servers not currently in the oauth flow using our saved auth information
|
|
566
|
+
Promise.allSettled(
|
|
532
567
|
servers.map((server) => {
|
|
533
|
-
return this
|
|
568
|
+
return this._connectToMcpServerInternal(
|
|
534
569
|
server.name,
|
|
535
570
|
server.server_url,
|
|
536
571
|
server.callback_url,
|
|
@@ -543,25 +578,25 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
543
578
|
}
|
|
544
579
|
);
|
|
545
580
|
})
|
|
546
|
-
)
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
);
|
|
554
|
-
|
|
555
|
-
await this.tryCatch(() => _onStart());
|
|
581
|
+
).then((_results) => {
|
|
582
|
+
this.broadcast(
|
|
583
|
+
JSON.stringify({
|
|
584
|
+
mcp: this.getMcpServers(),
|
|
585
|
+
type: "cf_agent_mcp_servers",
|
|
586
|
+
})
|
|
587
|
+
);
|
|
588
|
+
});
|
|
589
|
+
await this._tryCatch(() => _onStart());
|
|
556
590
|
}
|
|
557
591
|
);
|
|
558
592
|
};
|
|
559
593
|
}
|
|
560
594
|
|
|
561
|
-
private
|
|
595
|
+
private _setStateInternal(
|
|
562
596
|
state: State,
|
|
563
597
|
source: Connection | "server" = "server"
|
|
564
598
|
) {
|
|
599
|
+
const previousState = this._state;
|
|
565
600
|
this._state = state;
|
|
566
601
|
this.sql`
|
|
567
602
|
INSERT OR REPLACE INTO cf_agents_state (id, state)
|
|
@@ -573,16 +608,29 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
573
608
|
`;
|
|
574
609
|
this.broadcast(
|
|
575
610
|
JSON.stringify({
|
|
576
|
-
type: "cf_agent_state",
|
|
577
611
|
state: state,
|
|
612
|
+
type: "cf_agent_state",
|
|
578
613
|
}),
|
|
579
614
|
source !== "server" ? [source.id] : []
|
|
580
615
|
);
|
|
581
|
-
return this.
|
|
616
|
+
return this._tryCatch(() => {
|
|
582
617
|
const { connection, request } = agentContext.getStore() || {};
|
|
583
618
|
return agentContext.run(
|
|
584
619
|
{ agent: this, connection, request },
|
|
585
620
|
async () => {
|
|
621
|
+
this.observability?.emit(
|
|
622
|
+
{
|
|
623
|
+
displayMessage: "State updated",
|
|
624
|
+
id: nanoid(),
|
|
625
|
+
payload: {
|
|
626
|
+
previousState,
|
|
627
|
+
state,
|
|
628
|
+
},
|
|
629
|
+
timestamp: Date.now(),
|
|
630
|
+
type: "state:update",
|
|
631
|
+
},
|
|
632
|
+
this.ctx
|
|
633
|
+
);
|
|
586
634
|
return this.onStateUpdate(state, source);
|
|
587
635
|
}
|
|
588
636
|
);
|
|
@@ -594,7 +642,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
594
642
|
* @param state New state to set
|
|
595
643
|
*/
|
|
596
644
|
setState(state: State) {
|
|
597
|
-
this.
|
|
645
|
+
this._setStateInternal(state, "server");
|
|
598
646
|
}
|
|
599
647
|
|
|
600
648
|
/**
|
|
@@ -602,6 +650,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
602
650
|
* @param state Updated state
|
|
603
651
|
* @param source Source of the state update ("server" or a client connection)
|
|
604
652
|
*/
|
|
653
|
+
// biome-ignore lint/correctness/noUnusedFunctionParameters: overridden later
|
|
605
654
|
onStateUpdate(state: State | undefined, source: Connection | "server") {
|
|
606
655
|
// override this to handle state updates
|
|
607
656
|
}
|
|
@@ -610,6 +659,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
610
659
|
* Called when the Agent receives an email
|
|
611
660
|
* @param email Email message to process
|
|
612
661
|
*/
|
|
662
|
+
// biome-ignore lint/correctness/noUnusedFunctionParameters: overridden later
|
|
613
663
|
onEmail(email: ForwardableEmailMessage) {
|
|
614
664
|
return agentContext.run(
|
|
615
665
|
{ agent: this, connection: undefined, request: undefined },
|
|
@@ -619,7 +669,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
619
669
|
);
|
|
620
670
|
}
|
|
621
671
|
|
|
622
|
-
private async
|
|
672
|
+
private async _tryCatch<T>(fn: () => T | Promise<T>) {
|
|
623
673
|
try {
|
|
624
674
|
return await fn();
|
|
625
675
|
} catch (e) {
|
|
@@ -676,6 +726,18 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
676
726
|
): Promise<Schedule<T>> {
|
|
677
727
|
const id = nanoid(9);
|
|
678
728
|
|
|
729
|
+
const emitScheduleCreate = (schedule: Schedule<T>) =>
|
|
730
|
+
this.observability?.emit(
|
|
731
|
+
{
|
|
732
|
+
displayMessage: `Schedule ${schedule.id} created`,
|
|
733
|
+
id: nanoid(),
|
|
734
|
+
payload: schedule,
|
|
735
|
+
timestamp: Date.now(),
|
|
736
|
+
type: "schedule:create",
|
|
737
|
+
},
|
|
738
|
+
this.ctx
|
|
739
|
+
);
|
|
740
|
+
|
|
679
741
|
if (typeof callback !== "string") {
|
|
680
742
|
throw new Error("Callback must be a string");
|
|
681
743
|
}
|
|
@@ -693,15 +755,19 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
693
755
|
)}, 'scheduled', ${timestamp})
|
|
694
756
|
`;
|
|
695
757
|
|
|
696
|
-
await this.
|
|
758
|
+
await this._scheduleNextAlarm();
|
|
697
759
|
|
|
698
|
-
|
|
699
|
-
id,
|
|
760
|
+
const schedule: Schedule<T> = {
|
|
700
761
|
callback: callback,
|
|
762
|
+
id,
|
|
701
763
|
payload: payload as T,
|
|
702
764
|
time: timestamp,
|
|
703
765
|
type: "scheduled",
|
|
704
766
|
};
|
|
767
|
+
|
|
768
|
+
emitScheduleCreate(schedule);
|
|
769
|
+
|
|
770
|
+
return schedule;
|
|
705
771
|
}
|
|
706
772
|
if (typeof when === "number") {
|
|
707
773
|
const time = new Date(Date.now() + when * 1000);
|
|
@@ -714,16 +780,20 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
714
780
|
)}, 'delayed', ${when}, ${timestamp})
|
|
715
781
|
`;
|
|
716
782
|
|
|
717
|
-
await this.
|
|
783
|
+
await this._scheduleNextAlarm();
|
|
718
784
|
|
|
719
|
-
|
|
720
|
-
id,
|
|
785
|
+
const schedule: Schedule<T> = {
|
|
721
786
|
callback: callback,
|
|
722
|
-
payload: payload as T,
|
|
723
787
|
delayInSeconds: when,
|
|
788
|
+
id,
|
|
789
|
+
payload: payload as T,
|
|
724
790
|
time: timestamp,
|
|
725
791
|
type: "delayed",
|
|
726
792
|
};
|
|
793
|
+
|
|
794
|
+
emitScheduleCreate(schedule);
|
|
795
|
+
|
|
796
|
+
return schedule;
|
|
727
797
|
}
|
|
728
798
|
if (typeof when === "string") {
|
|
729
799
|
const nextExecutionTime = getNextCronTime(when);
|
|
@@ -736,16 +806,20 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
736
806
|
)}, 'cron', ${when}, ${timestamp})
|
|
737
807
|
`;
|
|
738
808
|
|
|
739
|
-
await this.
|
|
809
|
+
await this._scheduleNextAlarm();
|
|
740
810
|
|
|
741
|
-
|
|
742
|
-
id,
|
|
811
|
+
const schedule: Schedule<T> = {
|
|
743
812
|
callback: callback,
|
|
744
|
-
payload: payload as T,
|
|
745
813
|
cron: when,
|
|
814
|
+
id,
|
|
815
|
+
payload: payload as T,
|
|
746
816
|
time: timestamp,
|
|
747
817
|
type: "cron",
|
|
748
818
|
};
|
|
819
|
+
|
|
820
|
+
emitScheduleCreate(schedule);
|
|
821
|
+
|
|
822
|
+
return schedule;
|
|
749
823
|
}
|
|
750
824
|
throw new Error("Invalid schedule type");
|
|
751
825
|
}
|
|
@@ -821,13 +895,26 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
821
895
|
* @returns true if the task was cancelled, false otherwise
|
|
822
896
|
*/
|
|
823
897
|
async cancelSchedule(id: string): Promise<boolean> {
|
|
898
|
+
const schedule = await this.getSchedule(id);
|
|
899
|
+
if (schedule) {
|
|
900
|
+
this.observability?.emit(
|
|
901
|
+
{
|
|
902
|
+
displayMessage: `Schedule ${id} cancelled`,
|
|
903
|
+
id: nanoid(),
|
|
904
|
+
payload: schedule,
|
|
905
|
+
timestamp: Date.now(),
|
|
906
|
+
type: "schedule:cancel",
|
|
907
|
+
},
|
|
908
|
+
this.ctx
|
|
909
|
+
);
|
|
910
|
+
}
|
|
824
911
|
this.sql`DELETE FROM cf_agents_schedules WHERE id = ${id}`;
|
|
825
912
|
|
|
826
|
-
await this.
|
|
913
|
+
await this._scheduleNextAlarm();
|
|
827
914
|
return true;
|
|
828
915
|
}
|
|
829
916
|
|
|
830
|
-
private async
|
|
917
|
+
private async _scheduleNextAlarm() {
|
|
831
918
|
// Find the next schedule that needs to be executed
|
|
832
919
|
const result = this.sql`
|
|
833
920
|
SELECT time FROM cf_agents_schedules
|
|
@@ -869,6 +956,17 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
869
956
|
{ agent: this, connection: undefined, request: undefined },
|
|
870
957
|
async () => {
|
|
871
958
|
try {
|
|
959
|
+
this.observability?.emit(
|
|
960
|
+
{
|
|
961
|
+
displayMessage: `Schedule ${row.id} executed`,
|
|
962
|
+
id: nanoid(),
|
|
963
|
+
payload: row,
|
|
964
|
+
timestamp: Date.now(),
|
|
965
|
+
type: "schedule:execute",
|
|
966
|
+
},
|
|
967
|
+
this.ctx
|
|
968
|
+
);
|
|
969
|
+
|
|
872
970
|
await (
|
|
873
971
|
callback as (
|
|
874
972
|
payload: unknown,
|
|
@@ -897,7 +995,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
897
995
|
}
|
|
898
996
|
|
|
899
997
|
// Schedule the next alarm
|
|
900
|
-
await this.
|
|
998
|
+
await this._scheduleNextAlarm();
|
|
901
999
|
};
|
|
902
1000
|
|
|
903
1001
|
/**
|
|
@@ -912,10 +1010,25 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
912
1010
|
// delete all alarms
|
|
913
1011
|
await this.ctx.storage.deleteAlarm();
|
|
914
1012
|
await this.ctx.storage.deleteAll();
|
|
1013
|
+
this.ctx.abort("destroyed"); // enforce that the agent is evicted
|
|
1014
|
+
|
|
1015
|
+
this.observability?.emit(
|
|
1016
|
+
{
|
|
1017
|
+
displayMessage: "Agent destroyed",
|
|
1018
|
+
id: nanoid(),
|
|
1019
|
+
payload: {},
|
|
1020
|
+
timestamp: Date.now(),
|
|
1021
|
+
type: "destroy",
|
|
1022
|
+
},
|
|
1023
|
+
this.ctx
|
|
1024
|
+
);
|
|
915
1025
|
}
|
|
916
1026
|
|
|
917
|
-
|
|
918
|
-
|
|
1027
|
+
/**
|
|
1028
|
+
* Get all methods marked as callable on this Agent
|
|
1029
|
+
* @returns A map of method names to their metadata
|
|
1030
|
+
*/
|
|
1031
|
+
private _isCallable(method: string): boolean {
|
|
919
1032
|
return callableMetadata.has(this[method as keyof this] as Function);
|
|
920
1033
|
}
|
|
921
1034
|
|
|
@@ -940,27 +1053,40 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
940
1053
|
};
|
|
941
1054
|
}
|
|
942
1055
|
): Promise<{ id: string; authUrl: string | undefined }> {
|
|
943
|
-
const callbackUrl = `${callbackHost}/${agentsPrefix}/${camelCaseToKebabCase(this.
|
|
1056
|
+
const callbackUrl = `${callbackHost}/${agentsPrefix}/${camelCaseToKebabCase(this._ParentClass.name)}/${this.name}/callback`;
|
|
944
1057
|
|
|
945
|
-
const result = await this
|
|
1058
|
+
const result = await this._connectToMcpServerInternal(
|
|
946
1059
|
serverName,
|
|
947
1060
|
url,
|
|
948
1061
|
callbackUrl,
|
|
949
1062
|
options
|
|
950
1063
|
);
|
|
1064
|
+
this.sql`
|
|
1065
|
+
INSERT
|
|
1066
|
+
OR REPLACE INTO cf_agents_mcp_servers (id, name, server_url, client_id, auth_url, callback_url, server_options)
|
|
1067
|
+
VALUES (
|
|
1068
|
+
${result.id},
|
|
1069
|
+
${serverName},
|
|
1070
|
+
${url},
|
|
1071
|
+
${result.clientId ?? null},
|
|
1072
|
+
${result.authUrl ?? null},
|
|
1073
|
+
${callbackUrl},
|
|
1074
|
+
${options ? JSON.stringify(options) : null}
|
|
1075
|
+
);
|
|
1076
|
+
`;
|
|
951
1077
|
|
|
952
1078
|
this.broadcast(
|
|
953
1079
|
JSON.stringify({
|
|
1080
|
+
mcp: this.getMcpServers(),
|
|
954
1081
|
type: "cf_agent_mcp_servers",
|
|
955
|
-
mcp: this.#getMcpServerStateInternal(),
|
|
956
1082
|
})
|
|
957
1083
|
);
|
|
958
1084
|
|
|
959
1085
|
return result;
|
|
960
1086
|
}
|
|
961
1087
|
|
|
962
|
-
async
|
|
963
|
-
|
|
1088
|
+
async _connectToMcpServerInternal(
|
|
1089
|
+
_serverName: string,
|
|
964
1090
|
url: string,
|
|
965
1091
|
callbackUrl: string,
|
|
966
1092
|
// it's important that any options here are serializable because we put them into our sqlite DB for reconnection purposes
|
|
@@ -981,7 +1107,11 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
981
1107
|
id: string;
|
|
982
1108
|
oauthClientId?: string;
|
|
983
1109
|
}
|
|
984
|
-
): Promise<{
|
|
1110
|
+
): Promise<{
|
|
1111
|
+
id: string;
|
|
1112
|
+
authUrl: string | undefined;
|
|
1113
|
+
clientId: string | undefined;
|
|
1114
|
+
}> {
|
|
985
1115
|
const authProvider = new DurableObjectOAuthClientProvider(
|
|
986
1116
|
this.ctx.storage,
|
|
987
1117
|
this.name,
|
|
@@ -1014,30 +1144,18 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1014
1144
|
}
|
|
1015
1145
|
|
|
1016
1146
|
const { id, authUrl, clientId } = await this.mcp.connect(url, {
|
|
1147
|
+
client: options?.client,
|
|
1017
1148
|
reconnect,
|
|
1018
1149
|
transport: {
|
|
1019
1150
|
...headerTransportOpts,
|
|
1020
1151
|
authProvider,
|
|
1021
1152
|
},
|
|
1022
|
-
client: options?.client,
|
|
1023
1153
|
});
|
|
1024
1154
|
|
|
1025
|
-
this.sql`
|
|
1026
|
-
INSERT OR REPLACE INTO cf_agents_mcp_servers (id, name, server_url, client_id, auth_url, callback_url, server_options)
|
|
1027
|
-
VALUES (
|
|
1028
|
-
${id},
|
|
1029
|
-
${serverName},
|
|
1030
|
-
${url},
|
|
1031
|
-
${clientId ?? null},
|
|
1032
|
-
${authUrl ?? null},
|
|
1033
|
-
${callbackUrl},
|
|
1034
|
-
${options ? JSON.stringify(options) : null}
|
|
1035
|
-
);
|
|
1036
|
-
`;
|
|
1037
|
-
|
|
1038
1155
|
return {
|
|
1039
|
-
id,
|
|
1040
1156
|
authUrl,
|
|
1157
|
+
clientId,
|
|
1158
|
+
id,
|
|
1041
1159
|
};
|
|
1042
1160
|
}
|
|
1043
1161
|
|
|
@@ -1048,18 +1166,18 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1048
1166
|
`;
|
|
1049
1167
|
this.broadcast(
|
|
1050
1168
|
JSON.stringify({
|
|
1169
|
+
mcp: this.getMcpServers(),
|
|
1051
1170
|
type: "cf_agent_mcp_servers",
|
|
1052
|
-
mcp: this.#getMcpServerStateInternal(),
|
|
1053
1171
|
})
|
|
1054
1172
|
);
|
|
1055
1173
|
}
|
|
1056
1174
|
|
|
1057
|
-
|
|
1175
|
+
getMcpServers(): MCPServersState {
|
|
1058
1176
|
const mcpState: MCPServersState = {
|
|
1059
|
-
servers: {},
|
|
1060
|
-
tools: this.mcp.listTools(),
|
|
1061
1177
|
prompts: this.mcp.listPrompts(),
|
|
1062
1178
|
resources: this.mcp.listResources(),
|
|
1179
|
+
servers: {},
|
|
1180
|
+
tools: this.mcp.listTools(),
|
|
1063
1181
|
};
|
|
1064
1182
|
|
|
1065
1183
|
const servers = this.sql<MCPServerRow>`
|
|
@@ -1067,11 +1185,15 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
1067
1185
|
`;
|
|
1068
1186
|
|
|
1069
1187
|
for (const server of servers) {
|
|
1188
|
+
const serverConn = this.mcp.mcpConnections[server.id];
|
|
1070
1189
|
mcpState.servers[server.id] = {
|
|
1190
|
+
auth_url: server.auth_url,
|
|
1191
|
+
capabilities: serverConn?.serverCapabilities ?? null,
|
|
1192
|
+
instructions: serverConn?.instructions ?? null,
|
|
1071
1193
|
name: server.name,
|
|
1072
1194
|
server_url: server.server_url,
|
|
1073
|
-
|
|
1074
|
-
state:
|
|
1195
|
+
// mark as "authenticating" because the server isn't automatically connected, so it's pending authenticating
|
|
1196
|
+
state: serverConn?.connectionState ?? "authenticating",
|
|
1075
1197
|
};
|
|
1076
1198
|
}
|
|
1077
1199
|
|
|
@@ -1116,9 +1238,9 @@ export async function routeAgentRequest<Env>(
|
|
|
1116
1238
|
const corsHeaders =
|
|
1117
1239
|
options?.cors === true
|
|
1118
1240
|
? {
|
|
1119
|
-
"Access-Control-Allow-Origin": "*",
|
|
1120
|
-
"Access-Control-Allow-Methods": "GET, POST, HEAD, OPTIONS",
|
|
1121
1241
|
"Access-Control-Allow-Credentials": "true",
|
|
1242
|
+
"Access-Control-Allow-Methods": "GET, POST, HEAD, OPTIONS",
|
|
1243
|
+
"Access-Control-Allow-Origin": "*",
|
|
1122
1244
|
"Access-Control-Max-Age": "86400",
|
|
1123
1245
|
}
|
|
1124
1246
|
: options?.cors;
|
|
@@ -1166,9 +1288,9 @@ export async function routeAgentRequest<Env>(
|
|
|
1166
1288
|
* @param options Routing options
|
|
1167
1289
|
*/
|
|
1168
1290
|
export async function routeAgentEmail<Env>(
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1291
|
+
_email: ForwardableEmailMessage,
|
|
1292
|
+
_env: Env,
|
|
1293
|
+
_options?: AgentOptions<Env>
|
|
1172
1294
|
): Promise<void> {}
|
|
1173
1295
|
|
|
1174
1296
|
/**
|
|
@@ -1195,13 +1317,13 @@ export async function getAgentByName<Env, T extends Agent<Env>>(
|
|
|
1195
1317
|
* A wrapper for streaming responses in callable methods
|
|
1196
1318
|
*/
|
|
1197
1319
|
export class StreamingResponse {
|
|
1198
|
-
private
|
|
1199
|
-
private
|
|
1200
|
-
private
|
|
1320
|
+
private _connection: Connection;
|
|
1321
|
+
private _id: string;
|
|
1322
|
+
private _closed = false;
|
|
1201
1323
|
|
|
1202
1324
|
constructor(connection: Connection, id: string) {
|
|
1203
|
-
this.
|
|
1204
|
-
this.
|
|
1325
|
+
this._connection = connection;
|
|
1326
|
+
this._id = id;
|
|
1205
1327
|
}
|
|
1206
1328
|
|
|
1207
1329
|
/**
|
|
@@ -1209,17 +1331,17 @@ export class StreamingResponse {
|
|
|
1209
1331
|
* @param chunk The data to send
|
|
1210
1332
|
*/
|
|
1211
1333
|
send(chunk: unknown) {
|
|
1212
|
-
if (this.
|
|
1334
|
+
if (this._closed) {
|
|
1213
1335
|
throw new Error("StreamingResponse is already closed");
|
|
1214
1336
|
}
|
|
1215
1337
|
const response: RPCResponse = {
|
|
1216
|
-
type: "rpc",
|
|
1217
|
-
id: this.id,
|
|
1218
|
-
success: true,
|
|
1219
|
-
result: chunk,
|
|
1220
1338
|
done: false,
|
|
1339
|
+
id: this._id,
|
|
1340
|
+
result: chunk,
|
|
1341
|
+
success: true,
|
|
1342
|
+
type: "rpc",
|
|
1221
1343
|
};
|
|
1222
|
-
this.
|
|
1344
|
+
this._connection.send(JSON.stringify(response));
|
|
1223
1345
|
}
|
|
1224
1346
|
|
|
1225
1347
|
/**
|
|
@@ -1227,17 +1349,17 @@ export class StreamingResponse {
|
|
|
1227
1349
|
* @param finalChunk Optional final chunk of data to send
|
|
1228
1350
|
*/
|
|
1229
1351
|
end(finalChunk?: unknown) {
|
|
1230
|
-
if (this.
|
|
1352
|
+
if (this._closed) {
|
|
1231
1353
|
throw new Error("StreamingResponse is already closed");
|
|
1232
1354
|
}
|
|
1233
|
-
this.
|
|
1355
|
+
this._closed = true;
|
|
1234
1356
|
const response: RPCResponse = {
|
|
1235
|
-
type: "rpc",
|
|
1236
|
-
id: this.id,
|
|
1237
|
-
success: true,
|
|
1238
|
-
result: finalChunk,
|
|
1239
1357
|
done: true,
|
|
1358
|
+
id: this._id,
|
|
1359
|
+
result: finalChunk,
|
|
1360
|
+
success: true,
|
|
1361
|
+
type: "rpc",
|
|
1240
1362
|
};
|
|
1241
|
-
this.
|
|
1363
|
+
this._connection.send(JSON.stringify(response));
|
|
1242
1364
|
}
|
|
1243
1365
|
}
|