agents 0.0.0-d7d2876 → 0.0.0-df716f2

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.
@@ -0,0 +1,45 @@
1
+ import { DurableObject } from 'cloudflare:workers';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { Connection } from 'partyserver';
4
+
5
+ interface CORSOptions {
6
+ origin?: string;
7
+ methods?: string;
8
+ headers?: string;
9
+ maxAge?: number;
10
+ }
11
+ declare abstract class McpAgent<Env = unknown, State = unknown, Props extends Record<string, unknown> = Record<string, unknown>> extends DurableObject<Env> {
12
+ #private;
13
+ protected constructor(ctx: DurableObjectState, env: Env);
14
+ /**
15
+ * Agents API allowlist
16
+ */
17
+ initialState: State;
18
+ get state(): State;
19
+ sql<T = Record<string, string | number | boolean | null>>(strings: TemplateStringsArray, ...values: (string | number | boolean | null)[]): T[];
20
+ setState(state: State): void;
21
+ onStateUpdate(state: State | undefined, source: Connection | "server"): void;
22
+ onStart(): Promise<void>;
23
+ /**
24
+ * McpAgent API
25
+ */
26
+ abstract server: McpServer;
27
+ props: Props;
28
+ initRun: boolean;
29
+ abstract init(): Promise<void>;
30
+ _init(props: Props): Promise<void>;
31
+ fetch(request: Request): Promise<Response>;
32
+ getWebSocket(): WebSocket | null;
33
+ onMCPMessage(sessionId: string, request: Request): Promise<Response>;
34
+ webSocketMessage(ws: WebSocket, event: ArrayBuffer | string): Promise<void>;
35
+ webSocketError(ws: WebSocket, error: unknown): Promise<void>;
36
+ webSocketClose(ws: WebSocket, code: number, reason: string, wasClean: boolean): Promise<void>;
37
+ static mount(path: string, { binding, corsOptions, }?: {
38
+ binding?: string;
39
+ corsOptions?: CORSOptions;
40
+ }): {
41
+ fetch: (request: Request, env: Record<string, DurableObjectNamespace<McpAgent>>, ctx: ExecutionContext) => Promise<Response | undefined>;
42
+ };
43
+ }
44
+
45
+ export { McpAgent };
package/dist/react.d.ts CHANGED
@@ -1,21 +1,18 @@
1
- import { PartySocket } from "partysocket";
2
- import { usePartySocket } from "partysocket/react";
3
- import { StreamOptions } from "./client.js";
1
+ import { PartySocket } from 'partysocket';
2
+ import { usePartySocket } from 'partysocket/react';
3
+ import { StreamOptions } from './client.js';
4
4
 
5
5
  /**
6
6
  * Options for the useAgent hook
7
7
  * @template State Type of the Agent's state
8
8
  */
9
- type UseAgentOptions<State = unknown> = Omit<
10
- Parameters<typeof usePartySocket>[0],
11
- "party" | "room"
12
- > & {
13
- /** Name of the agent to connect to */
14
- agent: string;
15
- /** Name of the specific Agent instance */
16
- name?: string;
17
- /** Called when the Agent's state is updated */
18
- onStateUpdate?: (state: State, source: "server" | "client") => void;
9
+ type UseAgentOptions<State = unknown> = Omit<Parameters<typeof usePartySocket>[0], "party" | "room"> & {
10
+ /** Name of the agent to connect to */
11
+ agent: string;
12
+ /** Name of the specific Agent instance */
13
+ name?: string;
14
+ /** Called when the Agent's state is updated */
15
+ onStateUpdate?: (state: State, source: "server" | "client") => void;
19
16
  };
20
17
  /**
21
18
  * React hook for connecting to an Agent
@@ -23,17 +20,11 @@ type UseAgentOptions<State = unknown> = Omit<
23
20
  * @param options Connection options
24
21
  * @returns WebSocket connection with setState and call methods
25
22
  */
26
- declare function useAgent<State = unknown>(
27
- options: UseAgentOptions<State>
28
- ): PartySocket & {
29
- agent: string;
30
- name: string;
31
- setState: (state: State) => void;
32
- call: <T = unknown>(
33
- method: string,
34
- args?: unknown[],
35
- streamOptions?: StreamOptions
36
- ) => Promise<T>;
23
+ declare function useAgent<State = unknown>(options: UseAgentOptions<State>): PartySocket & {
24
+ agent: string;
25
+ name: string;
26
+ setState: (state: State) => void;
27
+ call: <T = unknown>(method: string, args?: unknown[], streamOptions?: StreamOptions) => Promise<T>;
37
28
  };
38
29
 
39
30
  export { type UseAgentOptions, useAgent };
@@ -1,53 +1,43 @@
1
- import { z } from "zod";
1
+ import { z } from 'zod';
2
2
 
3
3
  type Schedule = z.infer<typeof unstable_scheduleSchema>;
4
- declare function unstable_getSchedulePrompt(event: { date: Date }): string;
5
- declare const unstable_scheduleSchema: z.ZodObject<
6
- {
4
+ declare function unstable_getSchedulePrompt(event: {
5
+ date: Date;
6
+ }): string;
7
+ declare const unstable_scheduleSchema: z.ZodObject<{
7
8
  description: z.ZodString;
8
- when: z.ZodObject<
9
- {
9
+ when: z.ZodObject<{
10
10
  type: z.ZodEnum<["scheduled", "delayed", "cron", "no-schedule"]>;
11
11
  date: z.ZodOptional<z.ZodDate>;
12
12
  delayInSeconds: z.ZodOptional<z.ZodNumber>;
13
13
  cron: z.ZodOptional<z.ZodString>;
14
- },
15
- "strip",
16
- z.ZodTypeAny,
17
- {
14
+ }, "strip", z.ZodTypeAny, {
18
15
  type: "scheduled" | "delayed" | "cron" | "no-schedule";
19
16
  cron?: string | undefined;
20
17
  delayInSeconds?: number | undefined;
21
18
  date?: Date | undefined;
22
- },
23
- {
19
+ }, {
24
20
  type: "scheduled" | "delayed" | "cron" | "no-schedule";
25
21
  cron?: string | undefined;
26
22
  delayInSeconds?: number | undefined;
27
23
  date?: Date | undefined;
28
- }
29
- >;
30
- },
31
- "strip",
32
- z.ZodTypeAny,
33
- {
24
+ }>;
25
+ }, "strip", z.ZodTypeAny, {
34
26
  description: string;
35
27
  when: {
36
- type: "scheduled" | "delayed" | "cron" | "no-schedule";
37
- cron?: string | undefined;
38
- delayInSeconds?: number | undefined;
39
- date?: Date | undefined;
28
+ type: "scheduled" | "delayed" | "cron" | "no-schedule";
29
+ cron?: string | undefined;
30
+ delayInSeconds?: number | undefined;
31
+ date?: Date | undefined;
40
32
  };
41
- },
42
- {
33
+ }, {
43
34
  description: string;
44
35
  when: {
45
- type: "scheduled" | "delayed" | "cron" | "no-schedule";
46
- cron?: string | undefined;
47
- delayInSeconds?: number | undefined;
48
- date?: Date | undefined;
36
+ type: "scheduled" | "delayed" | "cron" | "no-schedule";
37
+ cron?: string | undefined;
38
+ delayInSeconds?: number | undefined;
39
+ date?: Date | undefined;
49
40
  };
50
- }
51
- >;
41
+ }>;
52
42
 
53
43
  export { type Schedule, unstable_getSchedulePrompt, unstable_scheduleSchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents",
3
- "version": "0.0.0-d7d2876",
3
+ "version": "0.0.0-df716f2",
4
4
  "main": "src/index.ts",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -42,6 +42,16 @@
42
42
  "types": "./dist/schedule.d.ts",
43
43
  "require": "./dist/schedule.js",
44
44
  "import": "./dist/schedule.js"
45
+ },
46
+ "./mcp": {
47
+ "types": "./dist/mcp/index.d.ts",
48
+ "require": "./dist/mcp/index.js",
49
+ "import": "./dist/mcp/index.js"
50
+ },
51
+ "./mcp/client": {
52
+ "types": "./dist/mcp/client.d.ts",
53
+ "require": "./dist/mcp/client.js",
54
+ "import": "./dist/mcp/client.js"
45
55
  }
46
56
  },
47
57
  "keywords": [],
@@ -58,8 +68,11 @@
58
68
  "description": "A home for your AI agents",
59
69
  "dependencies": {
60
70
  "cron-schedule": "^5.0.4",
61
- "nanoid": "^5.1.4",
62
- "partyserver": "^0.0.65",
63
- "partysocket": "0.0.0-548c226"
71
+ "nanoid": "^5.1.5",
72
+ "partyserver": "^0.0.66",
73
+ "partysocket": "1.1.3"
74
+ },
75
+ "devDependencies": {
76
+ "@modelcontextprotocol/sdk": "^1.8.0"
64
77
  }
65
78
  }
package/src/index.ts CHANGED
@@ -11,6 +11,8 @@ import {
11
11
  import { parseCronExpression } from "cron-schedule";
12
12
  import { nanoid } from "nanoid";
13
13
 
14
+ import { AsyncLocalStorage } from "node:async_hooks";
15
+
14
16
  export type { Connection, WSMessage, ConnectionContext } from "partyserver";
15
17
 
16
18
  import { WorkflowEntrypoint as CFWorkflowEntrypoint } from "cloudflare:workers";
@@ -168,6 +170,12 @@ const STATE_WAS_CHANGED = "cf_state_was_changed";
168
170
 
169
171
  const DEFAULT_STATE = {} as unknown;
170
172
 
173
+ export const unstable_context = new AsyncLocalStorage<{
174
+ agent: Agent<unknown>;
175
+ connection: Connection | undefined;
176
+ request: Request | undefined;
177
+ }>();
178
+
171
179
  /**
172
180
  * Base class for creating Agent implementations
173
181
  * @template Env Environment type containing bindings
@@ -292,89 +300,100 @@ export class Agent<Env, State = unknown> extends Server<Env> {
292
300
 
293
301
  const _onMessage = this.onMessage.bind(this);
294
302
  this.onMessage = async (connection: Connection, message: WSMessage) => {
295
- if (typeof message !== "string") {
296
- return this.#tryCatch(() => _onMessage(connection, message));
297
- }
298
-
299
- let parsed: unknown;
300
- try {
301
- parsed = JSON.parse(message);
302
- } catch (e) {
303
- // silently fail and let the onMessage handler handle it
304
- return this.#tryCatch(() => _onMessage(connection, message));
305
- }
306
-
307
- if (isStateUpdateMessage(parsed)) {
308
- this.#setStateInternal(parsed.state as State, connection);
309
- return;
310
- }
311
-
312
- if (isRPCRequest(parsed)) {
313
- try {
314
- const { id, method, args } = parsed;
315
-
316
- // Check if method exists and is callable
317
- const methodFn = this[method as keyof this];
318
- if (typeof methodFn !== "function") {
319
- throw new Error(`Method ${method} does not exist`);
303
+ return unstable_context.run(
304
+ { agent: this, connection, request: undefined },
305
+ async () => {
306
+ if (typeof message !== "string") {
307
+ return this.#tryCatch(() => _onMessage(connection, message));
320
308
  }
321
309
 
322
- if (!this.#isCallable(method)) {
323
- throw new Error(`Method ${method} is not callable`);
310
+ let parsed: unknown;
311
+ try {
312
+ parsed = JSON.parse(message);
313
+ } catch (e) {
314
+ // silently fail and let the onMessage handler handle it
315
+ return this.#tryCatch(() => _onMessage(connection, message));
324
316
  }
325
317
 
326
- // biome-ignore lint/complexity/noBannedTypes: <explanation>
327
- const metadata = callableMetadata.get(methodFn as Function);
318
+ if (isStateUpdateMessage(parsed)) {
319
+ this.#setStateInternal(parsed.state as State, connection);
320
+ return;
321
+ }
328
322
 
329
- // For streaming methods, pass a StreamingResponse object
330
- if (metadata?.streaming) {
331
- const stream = new StreamingResponse(connection, id);
332
- await methodFn.apply(this, [stream, ...args]);
323
+ if (isRPCRequest(parsed)) {
324
+ try {
325
+ const { id, method, args } = parsed;
326
+
327
+ // Check if method exists and is callable
328
+ const methodFn = this[method as keyof this];
329
+ if (typeof methodFn !== "function") {
330
+ throw new Error(`Method ${method} does not exist`);
331
+ }
332
+
333
+ if (!this.#isCallable(method)) {
334
+ throw new Error(`Method ${method} is not callable`);
335
+ }
336
+
337
+ // biome-ignore lint/complexity/noBannedTypes: <explanation>
338
+ const metadata = callableMetadata.get(methodFn as Function);
339
+
340
+ // For streaming methods, pass a StreamingResponse object
341
+ if (metadata?.streaming) {
342
+ const stream = new StreamingResponse(connection, id);
343
+ await methodFn.apply(this, [stream, ...args]);
344
+ return;
345
+ }
346
+
347
+ // For regular methods, execute and send response
348
+ const result = await methodFn.apply(this, args);
349
+ const response: RPCResponse = {
350
+ type: "rpc",
351
+ id,
352
+ success: true,
353
+ result,
354
+ done: true,
355
+ };
356
+ connection.send(JSON.stringify(response));
357
+ } catch (e) {
358
+ // Send error response
359
+ const response: RPCResponse = {
360
+ type: "rpc",
361
+ id: parsed.id,
362
+ success: false,
363
+ error:
364
+ e instanceof Error ? e.message : "Unknown error occurred",
365
+ };
366
+ connection.send(JSON.stringify(response));
367
+ console.error("RPC error:", e);
368
+ }
333
369
  return;
334
370
  }
335
371
 
336
- // For regular methods, execute and send response
337
- const result = await methodFn.apply(this, args);
338
- const response: RPCResponse = {
339
- type: "rpc",
340
- id,
341
- success: true,
342
- result,
343
- done: true,
344
- };
345
- connection.send(JSON.stringify(response));
346
- } catch (e) {
347
- // Send error response
348
- const response: RPCResponse = {
349
- type: "rpc",
350
- id: parsed.id,
351
- success: false,
352
- error: e instanceof Error ? e.message : "Unknown error occurred",
353
- };
354
- connection.send(JSON.stringify(response));
355
- console.error("RPC error:", e);
372
+ return this.#tryCatch(() => _onMessage(connection, message));
356
373
  }
357
- return;
358
- }
359
-
360
- return this.#tryCatch(() => _onMessage(connection, message));
374
+ );
361
375
  };
362
376
 
363
377
  const _onConnect = this.onConnect.bind(this);
364
378
  this.onConnect = (connection: Connection, ctx: ConnectionContext) => {
365
379
  // TODO: This is a hack to ensure the state is sent after the connection is established
366
380
  // must fix this
367
- setTimeout(() => {
368
- if (this.state) {
369
- connection.send(
370
- JSON.stringify({
371
- type: "cf_agent_state",
372
- state: this.state,
373
- })
374
- );
381
+ return unstable_context.run(
382
+ { agent: this, connection, request: ctx.request },
383
+ async () => {
384
+ setTimeout(() => {
385
+ if (this.state) {
386
+ connection.send(
387
+ JSON.stringify({
388
+ type: "cf_agent_state",
389
+ state: this.state,
390
+ })
391
+ );
392
+ }
393
+ return this.#tryCatch(() => _onConnect(connection, ctx));
394
+ }, 20);
375
395
  }
376
- return this.#tryCatch(() => _onConnect(connection, ctx));
377
- }, 20);
396
+ );
378
397
  };
379
398
  }
380
399
 
@@ -395,7 +414,15 @@ export class Agent<Env, State = unknown> extends Server<Env> {
395
414
  }),
396
415
  source !== "server" ? [source.id] : []
397
416
  );
398
- return this.#tryCatch(() => this.onStateUpdate(state, source));
417
+ return this.#tryCatch(() => {
418
+ const { connection, request } = unstable_context.getStore() || {};
419
+ return unstable_context.run(
420
+ { agent: this, connection, request },
421
+ async () => {
422
+ return this.onStateUpdate(state, source);
423
+ }
424
+ );
425
+ });
399
426
  }
400
427
 
401
428
  /**
@@ -420,7 +447,12 @@ export class Agent<Env, State = unknown> extends Server<Env> {
420
447
  * @param email Email message to process
421
448
  */
422
449
  onEmail(email: ForwardableEmailMessage) {
423
- throw new Error("Not implemented");
450
+ return unstable_context.run(
451
+ { agent: this, connection: undefined, request: undefined },
452
+ async () => {
453
+ console.error("onEmail not implemented");
454
+ }
455
+ );
424
456
  }
425
457
 
426
458
  async #tryCatch<T>(fn: () => T | Promise<T>) {
@@ -580,7 +612,6 @@ export class Agent<Env, State = unknown> extends Server<Env> {
580
612
  */
581
613
  getSchedules<T = string>(
582
614
  criteria: {
583
- description?: string;
584
615
  id?: string;
585
616
  type?: "scheduled" | "delayed" | "cron";
586
617
  timeRange?: { start?: Date; end?: Date };
@@ -594,11 +625,6 @@ export class Agent<Env, State = unknown> extends Server<Env> {
594
625
  params.push(criteria.id);
595
626
  }
596
627
 
597
- if (criteria.description) {
598
- query += " AND description = ?";
599
- params.push(criteria.description);
600
- }
601
-
602
628
  if (criteria.type) {
603
629
  query += " AND type = ?";
604
630
  params.push(criteria.type);
@@ -671,16 +697,21 @@ export class Agent<Env, State = unknown> extends Server<Env> {
671
697
  console.error(`callback ${row.callback} not found`);
672
698
  continue;
673
699
  }
674
- try {
675
- await (
676
- callback as (
677
- payload: unknown,
678
- schedule: Schedule<unknown>
679
- ) => Promise<void>
680
- ).bind(this)(JSON.parse(row.payload as string), row);
681
- } catch (e) {
682
- console.error(`error executing callback "${row.callback}"`, e);
683
- }
700
+ await unstable_context.run(
701
+ { agent: this, connection: undefined, request: undefined },
702
+ async () => {
703
+ try {
704
+ await (
705
+ callback as (
706
+ payload: unknown,
707
+ schedule: Schedule<unknown>
708
+ ) => Promise<void>
709
+ ).bind(this)(JSON.parse(row.payload as string), row);
710
+ } catch (e) {
711
+ console.error(`error executing callback "${row.callback}"`, e);
712
+ }
713
+ }
714
+ );
684
715
  if (row.type === "cron") {
685
716
  // Update next execution time for cron schedules
686
717
  const nextExecutionTime = getNextCronTime(row.cron);
@@ -1,170 +0,0 @@
1
- import {
2
- Agent
3
- } from "./chunk-X6BBKLSC.js";
4
- import {
5
- __privateAdd,
6
- __privateMethod
7
- } from "./chunk-HMLY7DHA.js";
8
-
9
- // src/ai-chat-agent.ts
10
- import { appendResponseMessages } from "ai";
11
- var decoder = new TextDecoder();
12
- var _AIChatAgent_instances, sendChatMessage_fn, broadcastChatMessage_fn, tryCatch_fn, persistMessages_fn, reply_fn;
13
- var AIChatAgent = class extends Agent {
14
- constructor(ctx, env) {
15
- super(ctx, env);
16
- __privateAdd(this, _AIChatAgent_instances);
17
- this.sql`create table if not exists cf_ai_chat_agent_messages (
18
- id text primary key,
19
- message text not null,
20
- created_at datetime default current_timestamp
21
- )`;
22
- this.messages = (this.sql`select * from cf_ai_chat_agent_messages` || []).map((row) => {
23
- return JSON.parse(row.message);
24
- });
25
- }
26
- async onMessage(connection, message) {
27
- if (typeof message === "string") {
28
- let data;
29
- try {
30
- data = JSON.parse(message);
31
- } catch (error) {
32
- return;
33
- }
34
- if (data.type === "cf_agent_use_chat_request" && data.init.method === "POST") {
35
- const {
36
- method,
37
- keepalive,
38
- headers,
39
- body,
40
- // we're reading this
41
- redirect,
42
- integrity,
43
- credentials,
44
- mode,
45
- referrer,
46
- referrerPolicy,
47
- window
48
- // dispatcher,
49
- // duplex
50
- } = data.init;
51
- const { messages } = JSON.parse(body);
52
- __privateMethod(this, _AIChatAgent_instances, broadcastChatMessage_fn).call(this, {
53
- type: "cf_agent_chat_messages",
54
- messages
55
- }, [connection.id]);
56
- await __privateMethod(this, _AIChatAgent_instances, persistMessages_fn).call(this, messages, [connection.id]);
57
- return __privateMethod(this, _AIChatAgent_instances, tryCatch_fn).call(this, async () => {
58
- const response = await this.onChatMessage(async ({ response: response2 }) => {
59
- const finalMessages = appendResponseMessages({
60
- messages,
61
- responseMessages: response2.messages
62
- });
63
- await __privateMethod(this, _AIChatAgent_instances, persistMessages_fn).call(this, finalMessages, [connection.id]);
64
- });
65
- if (response) {
66
- await __privateMethod(this, _AIChatAgent_instances, reply_fn).call(this, data.id, response);
67
- }
68
- });
69
- }
70
- if (data.type === "cf_agent_chat_clear") {
71
- this.sql`delete from cf_ai_chat_agent_messages`;
72
- this.messages = [];
73
- __privateMethod(this, _AIChatAgent_instances, broadcastChatMessage_fn).call(this, {
74
- type: "cf_agent_chat_clear"
75
- }, [connection.id]);
76
- } else if (data.type === "cf_agent_chat_messages") {
77
- await __privateMethod(this, _AIChatAgent_instances, persistMessages_fn).call(this, data.messages, [connection.id]);
78
- }
79
- }
80
- }
81
- async onRequest(request) {
82
- return __privateMethod(this, _AIChatAgent_instances, tryCatch_fn).call(this, () => {
83
- if (request.url.endsWith("/get-messages")) {
84
- const messages = (this.sql`select * from cf_ai_chat_agent_messages` || []).map((row) => {
85
- return JSON.parse(row.message);
86
- });
87
- return Response.json(messages);
88
- }
89
- return super.onRequest(request);
90
- });
91
- }
92
- /**
93
- * Handle incoming chat messages and generate a response
94
- * @param onFinish Callback to be called when the response is finished
95
- * @returns Response to send to the client or undefined
96
- */
97
- async onChatMessage(onFinish) {
98
- throw new Error(
99
- "recieved a chat message, override onChatMessage and return a Response to send to the client"
100
- );
101
- }
102
- /**
103
- * Save messages on the server side and trigger AI response
104
- * @param messages Chat messages to save
105
- */
106
- async saveMessages(messages) {
107
- await __privateMethod(this, _AIChatAgent_instances, persistMessages_fn).call(this, messages);
108
- const response = await this.onChatMessage(async ({ response: response2 }) => {
109
- const finalMessages = appendResponseMessages({
110
- messages,
111
- responseMessages: response2.messages
112
- });
113
- await __privateMethod(this, _AIChatAgent_instances, persistMessages_fn).call(this, finalMessages, []);
114
- });
115
- if (response) {
116
- for await (const chunk of response.body) {
117
- decoder.decode(chunk);
118
- }
119
- response.body?.cancel();
120
- }
121
- }
122
- };
123
- _AIChatAgent_instances = new WeakSet();
124
- sendChatMessage_fn = function(connection, message) {
125
- connection.send(JSON.stringify(message));
126
- };
127
- broadcastChatMessage_fn = function(message, exclude) {
128
- this.broadcast(JSON.stringify(message), exclude);
129
- };
130
- tryCatch_fn = async function(fn) {
131
- try {
132
- return await fn();
133
- } catch (e) {
134
- throw this.onError(e);
135
- }
136
- };
137
- persistMessages_fn = async function(messages, excludeBroadcastIds = []) {
138
- this.sql`delete from cf_ai_chat_agent_messages`;
139
- for (const message of messages) {
140
- this.sql`insert into cf_ai_chat_agent_messages (id, message) values (${message.id},${JSON.stringify(message)})`;
141
- }
142
- this.messages = messages;
143
- __privateMethod(this, _AIChatAgent_instances, broadcastChatMessage_fn).call(this, {
144
- type: "cf_agent_chat_messages",
145
- messages
146
- }, excludeBroadcastIds);
147
- };
148
- reply_fn = async function(id, response) {
149
- return __privateMethod(this, _AIChatAgent_instances, tryCatch_fn).call(this, async () => {
150
- for await (const chunk of response.body) {
151
- const body = decoder.decode(chunk);
152
- __privateMethod(this, _AIChatAgent_instances, broadcastChatMessage_fn).call(this, {
153
- id,
154
- type: "cf_agent_use_chat_response",
155
- body,
156
- done: false
157
- });
158
- }
159
- __privateMethod(this, _AIChatAgent_instances, broadcastChatMessage_fn).call(this, {
160
- id,
161
- type: "cf_agent_use_chat_response",
162
- body: "",
163
- done: true
164
- });
165
- });
166
- };
167
- export {
168
- AIChatAgent
169
- };
170
- //# sourceMappingURL=ai-chat-agent.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/ai-chat-agent.ts"],"sourcesContent":["import { Agent, type AgentContext, type Connection, type WSMessage } from \"./\";\nimport type {\n Message as ChatMessage,\n StreamTextOnFinishCallback,\n ToolSet,\n} from \"ai\";\nimport { appendResponseMessages } from \"ai\";\nimport type { OutgoingMessage, IncomingMessage } from \"./ai-types\";\nconst decoder = new TextDecoder();\n\n/**\n * Extension of Agent with built-in chat capabilities\n * @template Env Environment type containing bindings\n */\nexport class AIChatAgent<Env = unknown, State = unknown> extends Agent<\n Env,\n State\n> {\n /** Array of chat messages for the current conversation */\n messages: ChatMessage[];\n constructor(ctx: AgentContext, env: Env) {\n super(ctx, env);\n this.sql`create table if not exists cf_ai_chat_agent_messages (\n id text primary key,\n message text not null,\n created_at datetime default current_timestamp\n )`;\n this.messages = (\n this.sql`select * from cf_ai_chat_agent_messages` || []\n ).map((row) => {\n return JSON.parse(row.message as string);\n });\n }\n\n #sendChatMessage(connection: Connection, message: OutgoingMessage) {\n connection.send(JSON.stringify(message));\n }\n\n #broadcastChatMessage(message: OutgoingMessage, exclude?: string[]) {\n this.broadcast(JSON.stringify(message), exclude);\n }\n\n override async onMessage(connection: Connection, message: WSMessage) {\n if (typeof message === \"string\") {\n let data: IncomingMessage;\n try {\n data = JSON.parse(message) as IncomingMessage;\n } catch (error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (\n data.type === \"cf_agent_use_chat_request\" &&\n data.init.method === \"POST\"\n ) {\n const {\n method,\n keepalive,\n headers,\n body, // we're reading this\n redirect,\n integrity,\n credentials,\n mode,\n referrer,\n referrerPolicy,\n window,\n // dispatcher,\n // duplex\n } = data.init;\n const { messages } = JSON.parse(body as string);\n this.#broadcastChatMessage(\n {\n type: \"cf_agent_chat_messages\",\n messages,\n },\n [connection.id]\n );\n await this.#persistMessages(messages, [connection.id]);\n return this.#tryCatch(async () => {\n const response = await this.onChatMessage(async ({ response }) => {\n const finalMessages = appendResponseMessages({\n messages,\n responseMessages: response.messages,\n });\n\n await this.#persistMessages(finalMessages, [connection.id]);\n });\n if (response) {\n await this.#reply(data.id, response);\n }\n });\n }\n if (data.type === \"cf_agent_chat_clear\") {\n this.sql`delete from cf_ai_chat_agent_messages`;\n this.messages = [];\n this.#broadcastChatMessage(\n {\n type: \"cf_agent_chat_clear\",\n },\n [connection.id]\n );\n } else if (data.type === \"cf_agent_chat_messages\") {\n // replace the messages with the new ones\n await this.#persistMessages(data.messages, [connection.id]);\n }\n }\n }\n\n override async onRequest(request: Request): Promise<Response> {\n return this.#tryCatch(() => {\n if (request.url.endsWith(\"/get-messages\")) {\n const messages = (\n this.sql`select * from cf_ai_chat_agent_messages` || []\n ).map((row) => {\n return JSON.parse(row.message as string);\n });\n return Response.json(messages);\n }\n return super.onRequest(request);\n });\n }\n\n async #tryCatch<T>(fn: () => T | Promise<T>) {\n try {\n return await fn();\n } catch (e) {\n throw this.onError(e);\n }\n }\n\n /**\n * Handle incoming chat messages and generate a response\n * @param onFinish Callback to be called when the response is finished\n * @returns Response to send to the client or undefined\n */\n async onChatMessage(\n onFinish: StreamTextOnFinishCallback<ToolSet>\n ): Promise<Response | undefined> {\n throw new Error(\n \"recieved a chat message, override onChatMessage and return a Response to send to the client\"\n );\n }\n\n /**\n * Save messages on the server side and trigger AI response\n * @param messages Chat messages to save\n */\n async saveMessages(messages: ChatMessage[]) {\n await this.#persistMessages(messages);\n const response = await this.onChatMessage(async ({ response }) => {\n const finalMessages = appendResponseMessages({\n messages,\n responseMessages: response.messages,\n });\n\n await this.#persistMessages(finalMessages, []);\n });\n if (response) {\n // we're just going to drain the body\n // @ts-ignore TODO: fix this type error\n for await (const chunk of response.body!) {\n decoder.decode(chunk);\n }\n response.body?.cancel();\n }\n }\n\n async #persistMessages(\n messages: ChatMessage[],\n excludeBroadcastIds: string[] = []\n ) {\n this.sql`delete from cf_ai_chat_agent_messages`;\n for (const message of messages) {\n this.sql`insert into cf_ai_chat_agent_messages (id, message) values (${\n message.id\n },${JSON.stringify(message)})`;\n }\n this.messages = messages;\n this.#broadcastChatMessage(\n {\n type: \"cf_agent_chat_messages\",\n messages: messages,\n },\n excludeBroadcastIds\n );\n }\n\n async #reply(id: string, response: Response) {\n // now take chunks out from dataStreamResponse and send them to the client\n return this.#tryCatch(async () => {\n // @ts-expect-error TODO: fix this type error\n for await (const chunk of response.body!) {\n const body = decoder.decode(chunk);\n\n this.#broadcastChatMessage({\n id,\n type: \"cf_agent_use_chat_response\",\n body,\n done: false,\n });\n }\n\n this.#broadcastChatMessage({\n id,\n type: \"cf_agent_use_chat_response\",\n body: \"\",\n done: true,\n });\n });\n }\n}\n"],"mappings":";;;;;;;;;AAMA,SAAS,8BAA8B;AAEvC,IAAM,UAAU,IAAI,YAAY;AARhC;AAcO,IAAM,cAAN,cAA0D,MAG/D;AAAA,EAGA,YAAY,KAAmB,KAAU;AACvC,UAAM,KAAK,GAAG;AAPX;AAQH,SAAK;AAAA;AAAA;AAAA;AAAA;AAKL,SAAK,YACH,KAAK,gDAAgD,CAAC,GACtD,IAAI,CAAC,QAAQ;AACb,aAAO,KAAK,MAAM,IAAI,OAAiB;AAAA,IACzC,CAAC;AAAA,EACH;AAAA,EAUA,MAAe,UAAU,YAAwB,SAAoB;AACnE,QAAI,OAAO,YAAY,UAAU;AAC/B,UAAI;AACJ,UAAI;AACF,eAAO,KAAK,MAAM,OAAO;AAAA,MAC3B,SAAS,OAAO;AAGd;AAAA,MACF;AACA,UACE,KAAK,SAAS,+BACd,KAAK,KAAK,WAAW,QACrB;AACA,cAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA;AAAA,QAGF,IAAI,KAAK;AACT,cAAM,EAAE,SAAS,IAAI,KAAK,MAAM,IAAc;AAC9C,8BAAK,iDAAL,WACE;AAAA,UACE,MAAM;AAAA,UACN;AAAA,QACF,GACA,CAAC,WAAW,EAAE;AAEhB,cAAM,sBAAK,4CAAL,WAAsB,UAAU,CAAC,WAAW,EAAE;AACpD,eAAO,sBAAK,qCAAL,WAAe,YAAY;AAChC,gBAAM,WAAW,MAAM,KAAK,cAAc,OAAO,EAAE,UAAAA,UAAS,MAAM;AAChE,kBAAM,gBAAgB,uBAAuB;AAAA,cAC3C;AAAA,cACA,kBAAkBA,UAAS;AAAA,YAC7B,CAAC;AAED,kBAAM,sBAAK,4CAAL,WAAsB,eAAe,CAAC,WAAW,EAAE;AAAA,UAC3D,CAAC;AACD,cAAI,UAAU;AACZ,kBAAM,sBAAK,kCAAL,WAAY,KAAK,IAAI;AAAA,UAC7B;AAAA,QACF;AAAA,MACF;AACA,UAAI,KAAK,SAAS,uBAAuB;AACvC,aAAK;AACL,aAAK,WAAW,CAAC;AACjB,8BAAK,iDAAL,WACE;AAAA,UACE,MAAM;AAAA,QACR,GACA,CAAC,WAAW,EAAE;AAAA,MAElB,WAAW,KAAK,SAAS,0BAA0B;AAEjD,cAAM,sBAAK,4CAAL,WAAsB,KAAK,UAAU,CAAC,WAAW,EAAE;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAe,UAAU,SAAqC;AAC5D,WAAO,sBAAK,qCAAL,WAAe,MAAM;AAC1B,UAAI,QAAQ,IAAI,SAAS,eAAe,GAAG;AACzC,cAAM,YACJ,KAAK,gDAAgD,CAAC,GACtD,IAAI,CAAC,QAAQ;AACb,iBAAO,KAAK,MAAM,IAAI,OAAiB;AAAA,QACzC,CAAC;AACD,eAAO,SAAS,KAAK,QAAQ;AAAA,MAC/B;AACA,aAAO,MAAM,UAAU,OAAO;AAAA,IAChC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,cACJ,UAC+B;AAC/B,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAAa,UAAyB;AAC1C,UAAM,sBAAK,4CAAL,WAAsB;AAC5B,UAAM,WAAW,MAAM,KAAK,cAAc,OAAO,EAAE,UAAAA,UAAS,MAAM;AAChE,YAAM,gBAAgB,uBAAuB;AAAA,QAC3C;AAAA,QACA,kBAAkBA,UAAS;AAAA,MAC7B,CAAC;AAED,YAAM,sBAAK,4CAAL,WAAsB,eAAe,CAAC;AAAA,IAC9C,CAAC;AACD,QAAI,UAAU;AAGZ,uBAAiB,SAAS,SAAS,MAAO;AACxC,gBAAQ,OAAO,KAAK;AAAA,MACtB;AACA,eAAS,MAAM,OAAO;AAAA,IACxB;AAAA,EACF;AA6CF;AAtMO;AAoBL,qBAAgB,SAAC,YAAwB,SAA0B;AACjE,aAAW,KAAK,KAAK,UAAU,OAAO,CAAC;AACzC;AAEA,0BAAqB,SAAC,SAA0B,SAAoB;AAClE,OAAK,UAAU,KAAK,UAAU,OAAO,GAAG,OAAO;AACjD;AAoFM,cAAY,eAAC,IAA0B;AAC3C,MAAI;AACF,WAAO,MAAM,GAAG;AAAA,EAClB,SAAS,GAAG;AACV,UAAM,KAAK,QAAQ,CAAC;AAAA,EACtB;AACF;AAuCM,qBAAgB,eACpB,UACA,sBAAgC,CAAC,GACjC;AACA,OAAK;AACL,aAAW,WAAW,UAAU;AAC9B,SAAK,kEACH,QAAQ,EACV,IAAI,KAAK,UAAU,OAAO,CAAC;AAAA,EAC7B;AACA,OAAK,WAAW;AAChB,wBAAK,iDAAL,WACE;AAAA,IACE,MAAM;AAAA,IACN;AAAA,EACF,GACA;AAEJ;AAEM,WAAM,eAAC,IAAY,UAAoB;AAE3C,SAAO,sBAAK,qCAAL,WAAe,YAAY;AAEhC,qBAAiB,SAAS,SAAS,MAAO;AACxC,YAAM,OAAO,QAAQ,OAAO,KAAK;AAEjC,4BAAK,iDAAL,WAA2B;AAAA,QACzB;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA,MAAM;AAAA,MACR;AAAA,IACF;AAEA,0BAAK,iDAAL,WAA2B;AAAA,MACzB;AAAA,MACA,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAAA,EACF;AACF;","names":["response"]}