agents 0.0.0-74a8c74 → 0.0.0-7b22285

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/mcp/index.js CHANGED
@@ -1,12 +1,17 @@
1
1
  import {
2
2
  Agent
3
- } from "../chunk-SZEXGW6W.js";
3
+ } from "../chunk-XG52S6YY.js";
4
4
  import {
5
- SSEEdgeServerTransport
6
- } from "../chunk-EZ76ZGDB.js";
5
+ __privateAdd,
6
+ __privateGet,
7
+ __privateMethod,
8
+ __privateSet
9
+ } from "../chunk-HMLY7DHA.js";
7
10
 
8
11
  // src/mcp/index.ts
9
12
  import { DurableObject } from "cloudflare:workers";
13
+ import { JSONRPCMessageSchema } from "@modelcontextprotocol/sdk/types.js";
14
+ var MAXIMUM_MESSAGE_SIZE = 4 * 1024 * 1024;
10
15
  function handleCORS(request, corsOptions) {
11
16
  const origin = request.headers.get("Origin") || "*";
12
17
  const corsHeaders = {
@@ -20,68 +25,208 @@ function handleCORS(request, corsOptions) {
20
25
  }
21
26
  return null;
22
27
  }
28
+ var _getWebSocket, _started;
29
+ var McpTransport = class {
30
+ constructor(getWebSocket) {
31
+ __privateAdd(this, _getWebSocket);
32
+ __privateAdd(this, _started, false);
33
+ __privateSet(this, _getWebSocket, getWebSocket);
34
+ }
35
+ async start() {
36
+ if (__privateGet(this, _started)) {
37
+ throw new Error("Transport already started");
38
+ }
39
+ __privateSet(this, _started, true);
40
+ }
41
+ async send(message) {
42
+ if (!__privateGet(this, _started)) {
43
+ throw new Error("Transport not started");
44
+ }
45
+ const websocket = __privateGet(this, _getWebSocket).call(this);
46
+ if (!websocket) {
47
+ throw new Error("WebSocket not connected");
48
+ }
49
+ try {
50
+ websocket.send(JSON.stringify(message));
51
+ } catch (error) {
52
+ this.onerror?.(error);
53
+ throw error;
54
+ }
55
+ }
56
+ async close() {
57
+ this.onclose?.();
58
+ }
59
+ };
60
+ _getWebSocket = new WeakMap();
61
+ _started = new WeakMap();
62
+ var _status, _transport, _connected, _agent, _McpAgent_instances, initialize_fn;
23
63
  var McpAgent = class extends DurableObject {
24
- /**
25
- * Since McpAgent's _aren't_ yet real "Agents" (they route differently, don't support
26
- * websockets, don't support hibernation), let's only expose a couple of the methods
27
- * to the outer class: initialState/state/setState/onStateUpdate/sql
28
- */
29
- #agent;
30
64
  constructor(ctx, env) {
65
+ var _a;
31
66
  super(ctx, env);
67
+ __privateAdd(this, _McpAgent_instances);
68
+ __privateAdd(this, _status, "zero");
69
+ __privateAdd(this, _transport);
70
+ __privateAdd(this, _connected, false);
71
+ /**
72
+ * Since McpAgent's _aren't_ yet real "Agents" (they route differently, don't support
73
+ * websockets, don't support hibernation), let's only expose a couple of the methods
74
+ * to the outer class: initialState/state/setState/onStateUpdate/sql
75
+ */
76
+ __privateAdd(this, _agent);
77
+ this.initRun = false;
32
78
  const self = this;
33
- this.#agent = new class extends Agent {
34
- static options = {
35
- hibernate: false
36
- };
79
+ __privateSet(this, _agent, new (_a = class extends Agent {
37
80
  onStateUpdate(state, source) {
38
81
  return self.onStateUpdate(state, source);
39
82
  }
40
- }(ctx, env);
83
+ }, _a.options = {
84
+ hibernate: true
85
+ }, _a)(ctx, env));
41
86
  }
42
- /**
43
- * Agents API allowlist
44
- */
45
- initialState;
46
87
  get state() {
47
- if (this.initialState) this.#agent.initialState = this.initialState;
48
- return this.#agent.state;
88
+ return __privateGet(this, _agent).state;
49
89
  }
50
90
  sql(strings, ...values) {
51
- return this.#agent.sql(strings, ...values);
91
+ return __privateGet(this, _agent).sql(strings, ...values);
52
92
  }
53
93
  setState(state) {
54
- return this.#agent.setState(state);
94
+ return __privateGet(this, _agent).setState(state);
55
95
  }
56
96
  onStateUpdate(state, source) {
57
97
  }
58
- transport;
59
- props;
60
- initRun = false;
98
+ async onStart() {
99
+ var _a;
100
+ const self = this;
101
+ __privateSet(this, _agent, new (_a = class extends Agent {
102
+ constructor() {
103
+ super(...arguments);
104
+ this.initialState = self.initialState;
105
+ }
106
+ onStateUpdate(state, source) {
107
+ return self.onStateUpdate(state, source);
108
+ }
109
+ }, _a.options = {
110
+ hibernate: true
111
+ }, _a)(this.ctx, this.env));
112
+ this.props = await this.ctx.storage.get("props");
113
+ this.init?.();
114
+ __privateSet(this, _transport, new McpTransport(() => this.getWebSocket()));
115
+ await this.server.connect(__privateGet(this, _transport));
116
+ }
61
117
  async _init(props) {
118
+ await this.ctx.storage.put("props", props);
62
119
  this.props = props;
63
120
  if (!this.initRun) {
64
121
  this.initRun = true;
65
122
  await this.init();
66
123
  }
67
124
  }
68
- async onSSE(path) {
69
- this.transport = new SSEEdgeServerTransport(
70
- `${path}/message`,
71
- this.ctx.id.toString()
72
- );
73
- await this.server.connect(this.transport);
74
- return this.transport.sseResponse;
125
+ // Allow the worker to fetch a websocket connection to the agent
126
+ async fetch(request) {
127
+ if (__privateGet(this, _status) !== "started") {
128
+ await __privateMethod(this, _McpAgent_instances, initialize_fn).call(this);
129
+ }
130
+ if (request.headers.get("Upgrade") !== "websocket") {
131
+ return new Response("Expected WebSocket Upgrade request", {
132
+ status: 400
133
+ });
134
+ }
135
+ const url = new URL(request.url);
136
+ const sessionId = url.searchParams.get("sessionId");
137
+ if (!sessionId) {
138
+ return new Response("Missing sessionId", { status: 400 });
139
+ }
140
+ if (__privateGet(this, _connected)) {
141
+ return new Response("WebSocket already connected", { status: 400 });
142
+ }
143
+ const response = await __privateGet(this, _agent).fetch(request);
144
+ __privateSet(this, _connected, true);
145
+ __privateSet(this, _transport, new McpTransport(() => this.getWebSocket()));
146
+ await this.server.connect(__privateGet(this, _transport));
147
+ return response;
75
148
  }
76
- async onMCPMessage(request) {
77
- return this.transport.handlePostMessage(request);
149
+ getWebSocket() {
150
+ const websockets = this.ctx.getWebSockets();
151
+ if (websockets.length === 0) {
152
+ return null;
153
+ }
154
+ return websockets[0];
155
+ }
156
+ async onMCPMessage(sessionId, request) {
157
+ if (__privateGet(this, _status) !== "started") {
158
+ await __privateMethod(this, _McpAgent_instances, initialize_fn).call(this);
159
+ }
160
+ try {
161
+ const contentType = request.headers.get("content-type") || "";
162
+ if (!contentType.includes("application/json")) {
163
+ return new Response(`Unsupported content-type: ${contentType}`, {
164
+ status: 400
165
+ });
166
+ }
167
+ const contentLength = Number.parseInt(
168
+ request.headers.get("content-length") || "0",
169
+ 10
170
+ );
171
+ if (contentLength > MAXIMUM_MESSAGE_SIZE) {
172
+ return new Response(`Request body too large: ${contentLength} bytes`, {
173
+ status: 400
174
+ });
175
+ }
176
+ const message = await request.json();
177
+ let parsedMessage;
178
+ try {
179
+ parsedMessage = JSONRPCMessageSchema.parse(message);
180
+ } catch (error) {
181
+ __privateGet(this, _transport)?.onerror?.(error);
182
+ throw error;
183
+ }
184
+ __privateGet(this, _transport)?.onmessage?.(parsedMessage);
185
+ return new Response("Accepted", { status: 202 });
186
+ } catch (error) {
187
+ __privateGet(this, _transport)?.onerror?.(error);
188
+ return new Response(String(error), { status: 400 });
189
+ }
190
+ }
191
+ // This is unused since there are no incoming websocket messages
192
+ async webSocketMessage(ws, event) {
193
+ let message;
194
+ try {
195
+ const data = typeof event === "string" ? event : new TextDecoder().decode(event);
196
+ message = JSONRPCMessageSchema.parse(JSON.parse(data));
197
+ } catch (error) {
198
+ __privateGet(this, _transport)?.onerror?.(error);
199
+ return;
200
+ }
201
+ if (__privateGet(this, _status) !== "started") {
202
+ await __privateMethod(this, _McpAgent_instances, initialize_fn).call(this);
203
+ }
204
+ __privateGet(this, _transport)?.onmessage?.(message);
205
+ }
206
+ // WebSocket event handlers for hibernation support
207
+ async webSocketError(ws, error) {
208
+ if (__privateGet(this, _status) !== "started") {
209
+ await __privateMethod(this, _McpAgent_instances, initialize_fn).call(this);
210
+ }
211
+ __privateGet(this, _transport)?.onerror?.(error);
212
+ }
213
+ async webSocketClose(ws, code, reason, wasClean) {
214
+ if (__privateGet(this, _status) !== "started") {
215
+ await __privateMethod(this, _McpAgent_instances, initialize_fn).call(this);
216
+ }
217
+ __privateGet(this, _transport)?.onclose?.();
218
+ __privateSet(this, _connected, false);
78
219
  }
79
220
  static mount(path, {
80
221
  binding = "MCP_OBJECT",
81
222
  corsOptions
82
223
  } = {}) {
83
- const basePattern = new URLPattern({ pathname: path });
84
- const messagePattern = new URLPattern({ pathname: `${path}/message` });
224
+ let pathname = path;
225
+ if (path === "/") {
226
+ pathname = "/*";
227
+ }
228
+ const basePattern = new URLPattern({ pathname });
229
+ const messagePattern = new URLPattern({ pathname: `${pathname}/message` });
85
230
  return {
86
231
  fetch: async (request, env, ctx) => {
87
232
  const corsResponse = handleCORS(request, corsOptions);
@@ -89,39 +234,96 @@ var McpAgent = class extends DurableObject {
89
234
  const url = new URL(request.url);
90
235
  const namespace = env[binding];
91
236
  if (request.method === "GET" && basePattern.test(url)) {
92
- const object = namespace.get(namespace.newUniqueId());
93
- await object._init(ctx.props);
94
- const response = await object.onSSE(path);
95
- const headerObj = {};
96
- response.headers.forEach((value, key) => {
97
- headerObj[key] = value;
237
+ const sessionId = url.searchParams.get("sessionId") || namespace.newUniqueId().toString();
238
+ const { readable, writable } = new TransformStream();
239
+ const writer = writable.getWriter();
240
+ const encoder = new TextEncoder();
241
+ const endpointMessage = `event: endpoint
242
+ data: ${encodeURI(`${pathname}/message`)}?sessionId=${sessionId}
243
+
244
+ `;
245
+ writer.write(encoder.encode(endpointMessage));
246
+ const id = namespace.idFromString(sessionId);
247
+ const doStub = namespace.get(id);
248
+ await doStub._init(ctx.props);
249
+ const upgradeUrl = new URL(request.url);
250
+ upgradeUrl.searchParams.set("sessionId", sessionId);
251
+ const response = await doStub.fetch(
252
+ new Request(upgradeUrl, {
253
+ headers: {
254
+ Upgrade: "websocket",
255
+ // Required by PartyServer
256
+ "x-partykit-room": sessionId
257
+ }
258
+ })
259
+ );
260
+ const ws = response.webSocket;
261
+ if (!ws) {
262
+ console.error("Failed to establish WebSocket connection");
263
+ await writer.close();
264
+ return;
265
+ }
266
+ ws.accept();
267
+ ws.addEventListener("message", async (event) => {
268
+ try {
269
+ const message = JSON.parse(event.data);
270
+ const result = JSONRPCMessageSchema.safeParse(message);
271
+ if (!result.success) {
272
+ return;
273
+ }
274
+ const messageText = `event: message
275
+ data: ${JSON.stringify(result.data)}
276
+
277
+ `;
278
+ await writer.write(encoder.encode(messageText));
279
+ } catch (error) {
280
+ console.error("Error forwarding message to SSE:", error);
281
+ }
98
282
  });
99
- headerObj["Access-Control-Allow-Origin"] = corsOptions?.origin || "*";
100
- return new Response(response.body, {
101
- status: response.status,
102
- statusText: response.statusText,
103
- headers: headerObj
283
+ ws.addEventListener("error", async (error) => {
284
+ try {
285
+ await writer.close();
286
+ } catch (e) {
287
+ }
288
+ });
289
+ ws.addEventListener("close", async () => {
290
+ try {
291
+ await writer.close();
292
+ } catch (error) {
293
+ console.error("Error closing SSE connection:", error);
294
+ }
295
+ });
296
+ return new Response(readable, {
297
+ headers: {
298
+ "Content-Type": "text/event-stream",
299
+ "Cache-Control": "no-cache",
300
+ Connection: "keep-alive",
301
+ "Access-Control-Allow-Origin": corsOptions?.origin || "*"
302
+ }
104
303
  });
105
304
  }
106
305
  if (request.method === "POST" && messagePattern.test(url)) {
107
306
  const sessionId = url.searchParams.get("sessionId");
108
307
  if (!sessionId) {
109
308
  return new Response(
110
- `Missing sessionId. Expected POST to ${path} to initiate new one`,
309
+ `Missing sessionId. Expected POST to ${pathname} to initiate new one`,
111
310
  { status: 400 }
112
311
  );
113
312
  }
114
313
  const object = namespace.get(namespace.idFromString(sessionId));
115
- const response = await object.onMCPMessage(request);
116
- const headerObj = {};
117
- response.headers.forEach((value, key) => {
118
- headerObj[key] = value;
314
+ const response = await object.onMCPMessage(sessionId, request);
315
+ const headers = new Headers();
316
+ response.headers.forEach?.((value, key) => {
317
+ headers.set(key, value);
119
318
  });
120
- headerObj["Access-Control-Allow-Origin"] = corsOptions?.origin || "*";
319
+ headers.set(
320
+ "Access-Control-Allow-Origin",
321
+ corsOptions?.origin || "*"
322
+ );
121
323
  return new Response(response.body, {
122
324
  status: response.status,
123
325
  statusText: response.statusText,
124
- headers: headerObj
326
+ headers
125
327
  });
126
328
  }
127
329
  return new Response("Not Found", { status: 404 });
@@ -129,6 +331,18 @@ var McpAgent = class extends DurableObject {
129
331
  };
130
332
  }
131
333
  };
334
+ _status = new WeakMap();
335
+ _transport = new WeakMap();
336
+ _connected = new WeakMap();
337
+ _agent = new WeakMap();
338
+ _McpAgent_instances = new WeakSet();
339
+ initialize_fn = async function() {
340
+ await this.ctx.blockConcurrencyWhile(async () => {
341
+ __privateSet(this, _status, "starting");
342
+ await this.onStart();
343
+ __privateSet(this, _status, "started");
344
+ });
345
+ };
132
346
  export {
133
347
  McpAgent
134
348
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mcp/index.ts"],"sourcesContent":["import { DurableObject } from \"cloudflare:workers\";\nimport { Agent } from \"../\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { SSEEdgeServerTransport } from \"./sse-edge\";\nimport type { Connection } from \"../\";\n\n// CORS helper function\nfunction handleCORS(\n request: Request,\n corsOptions?: CORSOptions\n): Response | null {\n const origin = request.headers.get(\"Origin\") || \"*\";\n const corsHeaders = {\n \"Access-Control-Allow-Origin\": corsOptions?.origin || origin,\n \"Access-Control-Allow-Methods\":\n corsOptions?.methods || \"GET, POST, OPTIONS\",\n \"Access-Control-Allow-Headers\": corsOptions?.headers || \"Content-Type\",\n \"Access-Control-Max-Age\": (corsOptions?.maxAge || 86400).toString(),\n };\n\n if (request.method === \"OPTIONS\") {\n return new Response(null, { headers: corsHeaders });\n }\n\n return null;\n}\n\ninterface CORSOptions {\n origin?: string;\n methods?: string;\n headers?: string;\n maxAge?: number;\n}\n\nexport abstract class McpAgent<\n Env = unknown,\n State = unknown,\n Props extends Record<string, unknown> = Record<string, unknown>,\n> extends DurableObject<Env> {\n /**\n * Since McpAgent's _aren't_ yet real \"Agents\" (they route differently, don't support\n * websockets, don't support hibernation), let's only expose a couple of the methods\n * to the outer class: initialState/state/setState/onStateUpdate/sql\n */\n readonly #agent: Agent<Env, State>;\n protected constructor(ctx: DurableObjectState, env: Env) {\n super(ctx, env);\n const self = this;\n\n // Since McpAgent's _aren't_ yet real \"Agents\" (they route differently, they don't support\n // websockets, hibernation, scheduling etc), let's only expose a couple of the methods\n // to the outer class for now.\n this.#agent = new (class extends Agent<Env, State> {\n static options = {\n hibernate: false,\n };\n\n onStateUpdate(state: State | undefined, source: Connection | \"server\") {\n return self.onStateUpdate(state, source);\n }\n })(ctx, env);\n }\n\n /**\n * Agents API allowlist\n */\n initialState!: State;\n get state() {\n if (this.initialState) this.#agent.initialState = this.initialState;\n return this.#agent.state;\n }\n sql<T = Record<string, string | number | boolean | null>>(\n strings: TemplateStringsArray,\n ...values: (string | number | boolean | null)[]\n ) {\n return this.#agent.sql<T>(strings, ...values);\n }\n\n setState(state: State) {\n return this.#agent.setState(state);\n }\n onStateUpdate(state: State | undefined, source: Connection | \"server\") {\n // override this to handle state updates\n }\n\n /**\n * McpAgent API\n */\n abstract server: McpServer;\n private transport!: SSEEdgeServerTransport;\n props!: Props;\n initRun = false;\n\n abstract init(): Promise<void>;\n\n async _init(props: Props) {\n this.props = props;\n if (!this.initRun) {\n this.initRun = true;\n await this.init();\n }\n }\n\n async onSSE(path: string): Promise<Response> {\n this.transport = new SSEEdgeServerTransport(\n `${path}/message`,\n this.ctx.id.toString()\n );\n await this.server.connect(this.transport);\n return this.transport.sseResponse;\n }\n\n async onMCPMessage(request: Request): Promise<Response> {\n return this.transport.handlePostMessage(request);\n }\n\n static mount(\n path: string,\n {\n binding = \"MCP_OBJECT\",\n corsOptions,\n }: {\n binding?: string;\n corsOptions?: CORSOptions;\n } = {}\n ) {\n const basePattern = new URLPattern({ pathname: path });\n const messagePattern = new URLPattern({ pathname: `${path}/message` });\n\n return {\n fetch: async (\n request: Request,\n env: Record<string, DurableObjectNamespace<McpAgent>>,\n ctx: ExecutionContext\n ) => {\n // Handle CORS preflight\n const corsResponse = handleCORS(request, corsOptions);\n if (corsResponse) return corsResponse;\n\n const url = new URL(request.url);\n const namespace = env[binding];\n\n if (request.method === \"GET\" && basePattern.test(url)) {\n const object = namespace.get(namespace.newUniqueId());\n // @ts-ignore\n await object._init(ctx.props);\n const response = await object.onSSE(path);\n\n // Convert headers to a plain object\n const headerObj: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headerObj[key] = value;\n });\n headerObj[\"Access-Control-Allow-Origin\"] = corsOptions?.origin || \"*\";\n\n // Clone the response to get a new body stream\n // const clonedResponse = response.clone();\n return new Response(response.body as unknown as BodyInit, {\n status: response.status,\n statusText: response.statusText,\n headers: headerObj,\n });\n }\n\n if (request.method === \"POST\" && messagePattern.test(url)) {\n const sessionId = url.searchParams.get(\"sessionId\");\n if (!sessionId) {\n return new Response(\n `Missing sessionId. Expected POST to ${path} to initiate new one`,\n { status: 400 }\n );\n }\n const object = namespace.get(namespace.idFromString(sessionId));\n const response = await object.onMCPMessage(request);\n\n // Convert headers to a plain object\n const headerObj: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headerObj[key] = value;\n });\n headerObj[\"Access-Control-Allow-Origin\"] = corsOptions?.origin || \"*\";\n\n return new Response(response.body as unknown as BodyInit, {\n status: response.status,\n statusText: response.statusText,\n headers: headerObj,\n });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n },\n };\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/mcp/index.ts"],"sourcesContent":["import { DurableObject } from \"cloudflare:workers\";\nimport { Agent } from \"../\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { Connection } from \"../\";\nimport type { JSONRPCMessage } from \"@modelcontextprotocol/sdk/types.js\";\nimport { JSONRPCMessageSchema } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\n\nconst MAXIMUM_MESSAGE_SIZE = 4 * 1024 * 1024; // 4MB\n\n// CORS helper function\nfunction handleCORS(\n request: Request,\n corsOptions?: CORSOptions\n): Response | null {\n const origin = request.headers.get(\"Origin\") || \"*\";\n const corsHeaders = {\n \"Access-Control-Allow-Origin\": corsOptions?.origin || origin,\n \"Access-Control-Allow-Methods\":\n corsOptions?.methods || \"GET, POST, OPTIONS\",\n \"Access-Control-Allow-Headers\": corsOptions?.headers || \"Content-Type\",\n \"Access-Control-Max-Age\": (corsOptions?.maxAge || 86400).toString(),\n };\n\n if (request.method === \"OPTIONS\") {\n return new Response(null, { headers: corsHeaders });\n }\n\n return null;\n}\n\ninterface CORSOptions {\n origin?: string;\n methods?: string;\n headers?: string;\n maxAge?: number;\n}\n\nclass McpTransport implements Transport {\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n sessionId?: string;\n\n #getWebSocket: () => WebSocket | null;\n #started = false;\n constructor(getWebSocket: () => WebSocket | null) {\n this.#getWebSocket = getWebSocket;\n }\n\n async start() {\n // The transport does not manage the WebSocket connection since it's terminated\n // by the Durable Object in order to allow hibernation. There's nothing to initialize.\n if (this.#started) {\n throw new Error(\"Transport already started\");\n }\n this.#started = true;\n }\n\n async send(message: JSONRPCMessage) {\n if (!this.#started) {\n throw new Error(\"Transport not started\");\n }\n const websocket = this.#getWebSocket();\n if (!websocket) {\n throw new Error(\"WebSocket not connected\");\n }\n try {\n websocket.send(JSON.stringify(message));\n } catch (error) {\n this.onerror?.(error as Error);\n throw error;\n }\n }\n\n async close() {\n // Similar to start, the only thing to do is to pass the event on to the server\n this.onclose?.();\n }\n}\n\nexport abstract class McpAgent<\n Env = unknown,\n State = unknown,\n Props extends Record<string, unknown> = Record<string, unknown>,\n> extends DurableObject<Env> {\n #status: \"zero\" | \"starting\" | \"started\" = \"zero\";\n #transport?: McpTransport;\n #connected = false;\n\n /**\n * Since McpAgent's _aren't_ yet real \"Agents\" (they route differently, don't support\n * websockets, don't support hibernation), let's only expose a couple of the methods\n * to the outer class: initialState/state/setState/onStateUpdate/sql\n */\n #agent: Agent<Env, State>;\n\n protected constructor(ctx: DurableObjectState, env: Env) {\n super(ctx, env);\n const self = this;\n\n this.#agent = new (class extends Agent<Env, State> {\n static options = {\n hibernate: true,\n };\n\n onStateUpdate(state: State | undefined, source: Connection | \"server\") {\n return self.onStateUpdate(state, source);\n }\n })(ctx, env);\n }\n\n /**\n * Agents API allowlist\n */\n initialState!: State;\n get state() {\n return this.#agent.state;\n }\n sql<T = Record<string, string | number | boolean | null>>(\n strings: TemplateStringsArray,\n ...values: (string | number | boolean | null)[]\n ) {\n return this.#agent.sql<T>(strings, ...values);\n }\n\n setState(state: State) {\n return this.#agent.setState(state);\n }\n onStateUpdate(state: State | undefined, source: Connection | \"server\") {\n // override this to handle state updates\n }\n async onStart() {\n const self = this;\n\n this.#agent = new (class extends Agent<Env, State> {\n initialState: State = self.initialState;\n static options = {\n hibernate: true,\n };\n\n onStateUpdate(state: State | undefined, source: Connection | \"server\") {\n return self.onStateUpdate(state, source);\n }\n })(this.ctx, this.env);\n\n this.props = (await this.ctx.storage.get(\"props\")) as Props;\n this.init?.();\n\n // Connect to the MCP server\n this.#transport = new McpTransport(() => this.getWebSocket());\n await this.server.connect(this.#transport);\n }\n\n /**\n * McpAgent API\n */\n abstract server: McpServer;\n props!: Props;\n initRun = false;\n\n abstract init(): Promise<void>;\n\n async _init(props: Props) {\n await this.ctx.storage.put(\"props\", props);\n this.props = props;\n if (!this.initRun) {\n this.initRun = true;\n await this.init();\n }\n }\n\n async #initialize(): Promise<void> {\n await this.ctx.blockConcurrencyWhile(async () => {\n this.#status = \"starting\";\n await this.onStart();\n this.#status = \"started\";\n });\n }\n\n // Allow the worker to fetch a websocket connection to the agent\n async fetch(request: Request): Promise<Response> {\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n\n // Only handle WebSocket upgrade requests\n if (request.headers.get(\"Upgrade\") !== \"websocket\") {\n return new Response(\"Expected WebSocket Upgrade request\", {\n status: 400,\n });\n }\n\n const url = new URL(request.url);\n const sessionId = url.searchParams.get(\"sessionId\");\n if (!sessionId) {\n return new Response(\"Missing sessionId\", { status: 400 });\n }\n\n // For now, each agent can only have one connection\n // If we get an upgrade while already connected, we should error\n if (this.#connected) {\n return new Response(\"WebSocket already connected\", { status: 400 });\n }\n\n // Defer to the Agent's fetch method to handle the WebSocket connection\n // PartyServer does a lot to manage the connections under the hood\n const response = await this.#agent.fetch(request);\n\n this.#connected = true;\n\n // Connect to the MCP server\n this.#transport = new McpTransport(() => this.getWebSocket());\n await this.server.connect(this.#transport);\n\n return response;\n }\n\n getWebSocket() {\n const websockets = this.ctx.getWebSockets();\n if (websockets.length === 0) {\n return null;\n }\n return websockets[0];\n }\n\n async onMCPMessage(sessionId: string, request: Request): Promise<Response> {\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n try {\n const contentType = request.headers.get(\"content-type\") || \"\";\n if (!contentType.includes(\"application/json\")) {\n return new Response(`Unsupported content-type: ${contentType}`, {\n status: 400,\n });\n }\n\n // check if the request body is too large\n const contentLength = Number.parseInt(\n request.headers.get(\"content-length\") || \"0\",\n 10\n );\n if (contentLength > MAXIMUM_MESSAGE_SIZE) {\n return new Response(`Request body too large: ${contentLength} bytes`, {\n status: 400,\n });\n }\n\n // Clone the request before reading the body to avoid stream issues\n const message = await request.json();\n let parsedMessage: JSONRPCMessage;\n try {\n parsedMessage = JSONRPCMessageSchema.parse(message);\n } catch (error) {\n this.#transport?.onerror?.(error as Error);\n throw error;\n }\n\n this.#transport?.onmessage?.(parsedMessage);\n return new Response(\"Accepted\", { status: 202 });\n } catch (error) {\n this.#transport?.onerror?.(error as Error);\n return new Response(String(error), { status: 400 });\n }\n }\n\n // This is unused since there are no incoming websocket messages\n async webSocketMessage(ws: WebSocket, event: ArrayBuffer | string) {\n let message: JSONRPCMessage;\n try {\n // Ensure event is a string\n const data =\n typeof event === \"string\" ? event : new TextDecoder().decode(event);\n message = JSONRPCMessageSchema.parse(JSON.parse(data));\n } catch (error) {\n this.#transport?.onerror?.(error as Error);\n return;\n }\n\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n\n this.#transport?.onmessage?.(message);\n }\n\n // WebSocket event handlers for hibernation support\n async webSocketError(ws: WebSocket, error: unknown): Promise<void> {\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n this.#transport?.onerror?.(error as Error);\n }\n\n async webSocketClose(\n ws: WebSocket,\n code: number,\n reason: string,\n wasClean: boolean\n ): Promise<void> {\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n this.#transport?.onclose?.();\n this.#connected = false;\n }\n\n static mount(\n path: string,\n {\n binding = \"MCP_OBJECT\",\n corsOptions,\n }: {\n binding?: string;\n corsOptions?: CORSOptions;\n } = {}\n ) {\n let pathname = path;\n if (path === \"/\") {\n pathname = \"/*\";\n }\n const basePattern = new URLPattern({ pathname });\n const messagePattern = new URLPattern({ pathname: `${pathname}/message` });\n\n return {\n fetch: async (\n request: Request,\n env: Record<string, DurableObjectNamespace<McpAgent>>,\n ctx: ExecutionContext\n ) => {\n // Handle CORS preflight\n const corsResponse = handleCORS(request, corsOptions);\n if (corsResponse) return corsResponse;\n\n const url = new URL(request.url);\n const namespace = env[binding];\n\n // Handle SSE connections\n if (request.method === \"GET\" && basePattern.test(url)) {\n // Use a session ID if one is passed in, or create a unique\n // session ID for this connection\n const sessionId =\n url.searchParams.get(\"sessionId\") ||\n namespace.newUniqueId().toString();\n\n // Create a Transform Stream for SSE\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n\n // Send the endpoint event\n const endpointMessage = `event: endpoint\\ndata: ${encodeURI(`${pathname}/message`)}?sessionId=${sessionId}\\n\\n`;\n writer.write(encoder.encode(endpointMessage));\n\n // Get the Durable Object\n const id = namespace.idFromString(sessionId);\n const doStub = namespace.get(id);\n\n // Initialize the object\n await doStub._init(ctx.props);\n\n // Connect to the Durable Object via WebSocket\n const upgradeUrl = new URL(request.url);\n upgradeUrl.searchParams.set(\"sessionId\", sessionId);\n const response = await doStub.fetch(\n new Request(upgradeUrl, {\n headers: {\n Upgrade: \"websocket\",\n // Required by PartyServer\n \"x-partykit-room\": sessionId,\n },\n })\n );\n\n // Get the WebSocket\n const ws = response.webSocket;\n if (!ws) {\n console.error(\"Failed to establish WebSocket connection\");\n await writer.close();\n return;\n }\n\n // Accept the WebSocket\n ws.accept();\n\n // Handle messages from the Durable Object\n ws.addEventListener(\"message\", async (event) => {\n try {\n const message = JSON.parse(event.data);\n\n // validate that the message is a valid JSONRPC message\n const result = JSONRPCMessageSchema.safeParse(message);\n if (!result.success) {\n // The message was not a valid JSONRPC message, so we will drop it\n // PartyKit will broadcast state change messages to all connected clients\n // and we need to filter those out so they are not passed to MCP clients\n return;\n }\n\n // Send the message as an SSE event\n const messageText = `event: message\\ndata: ${JSON.stringify(result.data)}\\n\\n`;\n await writer.write(encoder.encode(messageText));\n } catch (error) {\n console.error(\"Error forwarding message to SSE:\", error);\n }\n });\n\n // Handle WebSocket errors\n ws.addEventListener(\"error\", async (error) => {\n try {\n await writer.close();\n } catch (e) {\n // Ignore errors when closing\n }\n });\n\n // Handle WebSocket closure\n ws.addEventListener(\"close\", async () => {\n try {\n await writer.close();\n } catch (error) {\n console.error(\"Error closing SSE connection:\", error);\n }\n });\n\n // Return the SSE response\n return new Response(readable, {\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": corsOptions?.origin || \"*\",\n },\n });\n }\n\n // Handle MCP messages\n if (request.method === \"POST\" && messagePattern.test(url)) {\n const sessionId = url.searchParams.get(\"sessionId\");\n if (!sessionId) {\n return new Response(\n `Missing sessionId. Expected POST to ${pathname} to initiate new one`,\n { status: 400 }\n );\n }\n\n // Get the Durable Object\n const object = namespace.get(namespace.idFromString(sessionId));\n\n // Forward the request to the Durable Object\n const response = await object.onMCPMessage(sessionId, request);\n\n // Add CORS headers\n const headers = new Headers();\n response.headers.forEach?.((value, key) => {\n headers.set(key, value);\n });\n headers.set(\n \"Access-Control-Allow-Origin\",\n corsOptions?.origin || \"*\"\n );\n\n return new Response(response.body as unknown as BodyInit, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n },\n };\n }\n}\n"],"mappings":"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","names":[]}
package/dist/react.js CHANGED
@@ -1,33 +1,27 @@
1
+ import "./chunk-HMLY7DHA.js";
2
+
1
3
  // src/react.tsx
2
4
  import { usePartySocket } from "partysocket/react";
3
5
  import { useCallback, useRef } from "react";
6
+ function camelCaseToKebabCase(str) {
7
+ if (str === str.toUpperCase() && str !== str.toLowerCase()) {
8
+ return str.toLowerCase().replace(/_/g, "-");
9
+ }
10
+ let kebabified = str.replace(
11
+ /[A-Z]/g,
12
+ (letter) => `-${letter.toLowerCase()}`
13
+ );
14
+ kebabified = kebabified.startsWith("-") ? kebabified.slice(1) : kebabified;
15
+ return kebabified.replace(/_/g, "-").replace(/-$/, "");
16
+ }
4
17
  function useAgent(options) {
18
+ const agentNamespace = camelCaseToKebabCase(options.agent);
5
19
  const pendingCallsRef = useRef(
6
20
  /* @__PURE__ */ new Map()
7
21
  );
8
- const call = useCallback(
9
- (method, args = [], streamOptions) => {
10
- return new Promise((resolve, reject) => {
11
- const id = Math.random().toString(36).slice(2);
12
- pendingCallsRef.current.set(id, {
13
- resolve,
14
- reject,
15
- stream: streamOptions
16
- });
17
- const request = {
18
- type: "rpc",
19
- id,
20
- method,
21
- args
22
- };
23
- agent.send(JSON.stringify(request));
24
- });
25
- },
26
- []
27
- );
28
22
  const agent = usePartySocket({
29
23
  prefix: "agents",
30
- party: options.agent,
24
+ party: agentNamespace,
31
25
  room: options.name || "default",
32
26
  ...options,
33
27
  onMessage: (message) => {
@@ -70,23 +64,38 @@ function useAgent(options) {
70
64
  options.onMessage?.(message);
71
65
  }
72
66
  });
67
+ const call = useCallback(
68
+ (method, args = [], streamOptions) => {
69
+ return new Promise((resolve, reject) => {
70
+ const id = Math.random().toString(36).slice(2);
71
+ pendingCallsRef.current.set(id, {
72
+ resolve,
73
+ reject,
74
+ stream: streamOptions
75
+ });
76
+ const request = {
77
+ type: "rpc",
78
+ id,
79
+ method,
80
+ args
81
+ };
82
+ agent.send(JSON.stringify(request));
83
+ });
84
+ },
85
+ [agent]
86
+ );
73
87
  agent.setState = (state) => {
74
88
  agent.send(JSON.stringify({ type: "cf_agent_state", state }));
75
89
  options.onStateUpdate?.(state, "client");
76
90
  };
77
91
  agent.call = call;
78
- agent.agent = options.agent;
92
+ agent.agent = agentNamespace;
79
93
  agent.name = options.name || "default";
80
94
  if (agent.agent !== agent.agent.toLowerCase()) {
81
95
  console.warn(
82
96
  `Agent name: ${agent.agent} should probably be in lowercase. Received: ${agent.agent}`
83
97
  );
84
98
  }
85
- if (agent.name !== agent.name.toLowerCase()) {
86
- console.warn(
87
- `Agent instance name: ${agent.name} should probably be in lowercase. Received: ${agent.name}`
88
- );
89
- }
90
99
  return agent;
91
100
  }
92
101
  export {
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.tsx"],"sourcesContent":["import type { PartySocket } from \"partysocket\";\nimport { usePartySocket } from \"partysocket/react\";\nimport { useCallback, useRef } from \"react\";\nimport type { RPCRequest, RPCResponse } from \"./\";\nimport type { StreamOptions } from \"./client\";\n\n/**\n * Options for the useAgent hook\n * @template State Type of the Agent's state\n */\nexport type UseAgentOptions<State = unknown> = Omit<\n Parameters<typeof usePartySocket>[0],\n \"party\" | \"room\"\n> & {\n /** Name of the agent to connect to */\n agent: string;\n /** Name of the specific Agent instance */\n name?: string;\n /** Called when the Agent's state is updated */\n onStateUpdate?: (state: State, source: \"server\" | \"client\") => void;\n};\n\n/**\n * React hook for connecting to an Agent\n * @template State Type of the Agent's state\n * @param options Connection options\n * @returns WebSocket connection with setState and call methods\n */\nexport function useAgent<State = unknown>(\n options: UseAgentOptions<State>\n): PartySocket & {\n agent: string;\n name: string;\n setState: (state: State) => void;\n call: <T = unknown>(\n method: string,\n args?: unknown[],\n streamOptions?: StreamOptions\n ) => Promise<T>;\n} {\n // Keep track of pending RPC calls\n const pendingCallsRef = useRef(\n new Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n stream?: StreamOptions;\n }\n >()\n );\n\n // Create the call method\n const call = useCallback(\n <T = unknown,>(\n method: string,\n args: unknown[] = [],\n streamOptions?: StreamOptions\n ): Promise<T> => {\n return new Promise((resolve, reject) => {\n const id = Math.random().toString(36).slice(2);\n pendingCallsRef.current.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n stream: streamOptions,\n });\n\n const request: RPCRequest = {\n type: \"rpc\",\n id,\n method,\n args,\n };\n\n agent.send(JSON.stringify(request));\n });\n },\n []\n );\n\n const agent = usePartySocket({\n prefix: \"agents\",\n party: options.agent,\n room: options.name || \"default\",\n ...options,\n onMessage: (message) => {\n if (typeof message.data === \"string\") {\n let parsedMessage: Record<string, unknown>;\n try {\n parsedMessage = JSON.parse(message.data);\n } catch (error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return options.onMessage?.(message);\n }\n if (parsedMessage.type === \"cf_agent_state\") {\n options.onStateUpdate?.(parsedMessage.state as State, \"server\");\n return;\n }\n if (parsedMessage.type === \"rpc\") {\n const response = parsedMessage as RPCResponse;\n const pending = pendingCallsRef.current.get(response.id);\n if (!pending) return;\n\n if (!response.success) {\n pending.reject(new Error(response.error));\n pendingCallsRef.current.delete(response.id);\n pending.stream?.onError?.(response.error);\n return;\n }\n\n // Handle streaming responses\n if (\"done\" in response) {\n if (response.done) {\n pending.resolve(response.result);\n pendingCallsRef.current.delete(response.id);\n pending.stream?.onDone?.(response.result);\n } else {\n pending.stream?.onChunk?.(response.result);\n }\n } else {\n // Non-streaming response\n pending.resolve(response.result);\n pendingCallsRef.current.delete(response.id);\n }\n return;\n }\n }\n options.onMessage?.(message);\n },\n }) as PartySocket & {\n agent: string;\n name: string;\n setState: (state: State) => void;\n call: <T = unknown>(\n method: string,\n args?: unknown[],\n streamOptions?: StreamOptions\n ) => Promise<T>;\n };\n\n agent.setState = (state: State) => {\n agent.send(JSON.stringify({ type: \"cf_agent_state\", state }));\n options.onStateUpdate?.(state, \"client\");\n };\n\n agent.call = call;\n agent.agent = options.agent;\n agent.name = options.name || \"default\";\n\n // warn if agent or name isn't in lowercase\n if (agent.agent !== agent.agent.toLowerCase()) {\n console.warn(\n `Agent name: ${agent.agent} should probably be in lowercase. Received: ${agent.agent}`\n );\n }\n if (agent.name !== agent.name.toLowerCase()) {\n console.warn(\n `Agent instance name: ${agent.name} should probably be in lowercase. Received: ${agent.name}`\n );\n }\n\n return agent;\n}\n"],"mappings":";AACA,SAAS,sBAAsB;AAC/B,SAAS,aAAa,cAAc;AA0B7B,SAAS,SACd,SAUA;AAEA,QAAM,kBAAkB;AAAA,IACtB,oBAAI,IAOF;AAAA,EACJ;AAGA,QAAM,OAAO;AAAA,IACX,CACE,QACA,OAAkB,CAAC,GACnB,kBACe;AACf,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,cAAM,KAAK,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC;AAC7C,wBAAgB,QAAQ,IAAI,IAAI;AAAA,UAC9B;AAAA,UACA;AAAA,UACA,QAAQ;AAAA,QACV,CAAC;AAED,cAAM,UAAsB;AAAA,UAC1B,MAAM;AAAA,UACN;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAEA,cAAM,KAAK,KAAK,UAAU,OAAO,CAAC;AAAA,MACpC,CAAC;AAAA,IACH;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,QAAQ,eAAe;AAAA,IAC3B,QAAQ;AAAA,IACR,OAAO,QAAQ;AAAA,IACf,MAAM,QAAQ,QAAQ;AAAA,IACtB,GAAG;AAAA,IACH,WAAW,CAAC,YAAY;AACtB,UAAI,OAAO,QAAQ,SAAS,UAAU;AACpC,YAAI;AACJ,YAAI;AACF,0BAAgB,KAAK,MAAM,QAAQ,IAAI;AAAA,QACzC,SAAS,OAAO;AAGd,iBAAO,QAAQ,YAAY,OAAO;AAAA,QACpC;AACA,YAAI,cAAc,SAAS,kBAAkB;AAC3C,kBAAQ,gBAAgB,cAAc,OAAgB,QAAQ;AAC9D;AAAA,QACF;AACA,YAAI,cAAc,SAAS,OAAO;AAChC,gBAAM,WAAW;AACjB,gBAAM,UAAU,gBAAgB,QAAQ,IAAI,SAAS,EAAE;AACvD,cAAI,CAAC,QAAS;AAEd,cAAI,CAAC,SAAS,SAAS;AACrB,oBAAQ,OAAO,IAAI,MAAM,SAAS,KAAK,CAAC;AACxC,4BAAgB,QAAQ,OAAO,SAAS,EAAE;AAC1C,oBAAQ,QAAQ,UAAU,SAAS,KAAK;AACxC;AAAA,UACF;AAGA,cAAI,UAAU,UAAU;AACtB,gBAAI,SAAS,MAAM;AACjB,sBAAQ,QAAQ,SAAS,MAAM;AAC/B,8BAAgB,QAAQ,OAAO,SAAS,EAAE;AAC1C,sBAAQ,QAAQ,SAAS,SAAS,MAAM;AAAA,YAC1C,OAAO;AACL,sBAAQ,QAAQ,UAAU,SAAS,MAAM;AAAA,YAC3C;AAAA,UACF,OAAO;AAEL,oBAAQ,QAAQ,SAAS,MAAM;AAC/B,4BAAgB,QAAQ,OAAO,SAAS,EAAE;AAAA,UAC5C;AACA;AAAA,QACF;AAAA,MACF;AACA,cAAQ,YAAY,OAAO;AAAA,IAC7B;AAAA,EACF,CAAC;AAWD,QAAM,WAAW,CAAC,UAAiB;AACjC,UAAM,KAAK,KAAK,UAAU,EAAE,MAAM,kBAAkB,MAAM,CAAC,CAAC;AAC5D,YAAQ,gBAAgB,OAAO,QAAQ;AAAA,EACzC;AAEA,QAAM,OAAO;AACb,QAAM,QAAQ,QAAQ;AACtB,QAAM,OAAO,QAAQ,QAAQ;AAG7B,MAAI,MAAM,UAAU,MAAM,MAAM,YAAY,GAAG;AAC7C,YAAQ;AAAA,MACN,eAAe,MAAM,KAAK,+CAA+C,MAAM,KAAK;AAAA,IACtF;AAAA,EACF;AACA,MAAI,MAAM,SAAS,MAAM,KAAK,YAAY,GAAG;AAC3C,YAAQ;AAAA,MACN,wBAAwB,MAAM,IAAI,+CAA+C,MAAM,IAAI;AAAA,IAC7F;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/react.tsx"],"sourcesContent":["import type { PartySocket } from \"partysocket\";\nimport { usePartySocket } from \"partysocket/react\";\nimport { useCallback, useRef } from \"react\";\nimport type { RPCRequest, RPCResponse } from \"./\";\nimport type { StreamOptions } from \"./client\";\n\n/**\n * Convert a camelCase string to a kebab-case string\n * @param str The string to convert\n * @returns The kebab-case string\n */\nfunction camelCaseToKebabCase(str: string): string {\n // If string is all uppercase, convert to lowercase\n if (str === str.toUpperCase() && str !== str.toLowerCase()) {\n return str.toLowerCase().replace(/_/g, \"-\");\n }\n\n // Otherwise handle camelCase to kebab-case\n let kebabified = str.replace(\n /[A-Z]/g,\n (letter) => `-${letter.toLowerCase()}`\n );\n kebabified = kebabified.startsWith(\"-\") ? kebabified.slice(1) : kebabified;\n // Convert any remaining underscores to hyphens and remove trailing -'s\n return kebabified.replace(/_/g, \"-\").replace(/-$/, \"\");\n}\n\n/**\n * Options for the useAgent hook\n * @template State Type of the Agent's state\n */\nexport type UseAgentOptions<State = unknown> = Omit<\n Parameters<typeof usePartySocket>[0],\n \"party\" | \"room\"\n> & {\n /** Name of the agent to connect to */\n agent: string;\n /** Name of the specific Agent instance */\n name?: string;\n /** Called when the Agent's state is updated */\n onStateUpdate?: (state: State, source: \"server\" | \"client\") => void;\n};\n\n/**\n * React hook for connecting to an Agent\n * @template State Type of the Agent's state\n * @param options Connection options\n * @returns WebSocket connection with setState and call methods\n */\nexport function useAgent<State = unknown>(\n options: UseAgentOptions<State>\n): PartySocket & {\n agent: string;\n name: string;\n setState: (state: State) => void;\n call: <T = unknown>(\n method: string,\n args?: unknown[],\n streamOptions?: StreamOptions\n ) => Promise<T>;\n} {\n const agentNamespace = camelCaseToKebabCase(options.agent);\n // Keep track of pending RPC calls\n const pendingCallsRef = useRef(\n new Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n stream?: StreamOptions;\n }\n >()\n );\n\n // TODO: if options.query is a function, then use\n // \"use()\" to get the value and pass it\n // as a query parameter to usePartySocket\n const agent = usePartySocket({\n prefix: \"agents\",\n party: agentNamespace,\n room: options.name || \"default\",\n ...options,\n onMessage: (message) => {\n if (typeof message.data === \"string\") {\n let parsedMessage: Record<string, unknown>;\n try {\n parsedMessage = JSON.parse(message.data);\n } catch (error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return options.onMessage?.(message);\n }\n if (parsedMessage.type === \"cf_agent_state\") {\n options.onStateUpdate?.(parsedMessage.state as State, \"server\");\n return;\n }\n if (parsedMessage.type === \"rpc\") {\n const response = parsedMessage as RPCResponse;\n const pending = pendingCallsRef.current.get(response.id);\n if (!pending) return;\n\n if (!response.success) {\n pending.reject(new Error(response.error));\n pendingCallsRef.current.delete(response.id);\n pending.stream?.onError?.(response.error);\n return;\n }\n\n // Handle streaming responses\n if (\"done\" in response) {\n if (response.done) {\n pending.resolve(response.result);\n pendingCallsRef.current.delete(response.id);\n pending.stream?.onDone?.(response.result);\n } else {\n pending.stream?.onChunk?.(response.result);\n }\n } else {\n // Non-streaming response\n pending.resolve(response.result);\n pendingCallsRef.current.delete(response.id);\n }\n return;\n }\n }\n options.onMessage?.(message);\n },\n }) as PartySocket & {\n agent: string;\n name: string;\n setState: (state: State) => void;\n call: <T = unknown>(\n method: string,\n args?: unknown[],\n streamOptions?: StreamOptions\n ) => Promise<T>;\n };\n // Create the call method\n const call = useCallback(\n <T = unknown,>(\n method: string,\n args: unknown[] = [],\n streamOptions?: StreamOptions\n ): Promise<T> => {\n return new Promise((resolve, reject) => {\n const id = Math.random().toString(36).slice(2);\n pendingCallsRef.current.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n stream: streamOptions,\n });\n\n const request: RPCRequest = {\n type: \"rpc\",\n id,\n method,\n args,\n };\n\n agent.send(JSON.stringify(request));\n });\n },\n [agent]\n );\n\n agent.setState = (state: State) => {\n agent.send(JSON.stringify({ type: \"cf_agent_state\", state }));\n options.onStateUpdate?.(state, \"client\");\n };\n\n agent.call = call;\n agent.agent = agentNamespace;\n agent.name = options.name || \"default\";\n\n // warn if agent isn't in lowercase\n if (agent.agent !== agent.agent.toLowerCase()) {\n console.warn(\n `Agent name: ${agent.agent} should probably be in lowercase. Received: ${agent.agent}`\n );\n }\n\n return agent;\n}\n"],"mappings":"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","names":[]}
package/dist/schedule.js CHANGED
@@ -1,3 +1,5 @@
1
+ import "./chunk-HMLY7DHA.js";
2
+
1
3
  // src/schedule.ts
2
4
  import { z } from "zod";
3
5
  function unstable_getSchedulePrompt(event) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/schedule.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport type Schedule = z.infer<typeof unstable_scheduleSchema>;\n\nexport function unstable_getSchedulePrompt(event: { date: Date }) {\n return `\n[Schedule Parser Component]\n\nCurrent time: ${event.date.toUTCString()}\n\nThis component parses natural language scheduling requests into a structured format. It extracts:\n1. A clean task description (without timing information)\n2. Scheduling details in one of these formats:\n - scheduled: Specific date/time events\n - delayed: Relative time delays (in seconds)\n - cron: Recurring patterns\n - no-schedule: Tasks without timing\n\nRules:\n- Task descriptions should be clean and focused on the action\n- Use numbers (0-6) for days in cron patterns (0=Sunday)\n- For recurring tasks, use standard cron syntax\n- For relative times, convert to seconds\n- For specific dates, use the current time as reference\n\nExample outputs:\n{\n \"description\": \"meeting with team\",\n \"when\": {\n \"type\": \"scheduled\",\n \"date\": \"tomorrow at 14:00\"\n }\n}\n\n{\n \"description\": \"backup database\",\n \"when\": {\n \"type\": \"cron\",\n \"cron\": \"0 0 * * *\"\n }\n}\n\n{\n \"description\": \"send report\",\n \"when\": {\n \"type\": \"delayed\",\n \"delayInSeconds\": 1800\n }\n}\n\n[End Schedule Parser Component]\n`;\n}\n\nexport const unstable_scheduleSchema = z.object({\n description: z.string().describe(\"A description of the task\"),\n when: z.object({\n type: z\n .enum([\"scheduled\", \"delayed\", \"cron\", \"no-schedule\"])\n .describe(\"The type of scheduling details\"),\n date: z.coerce\n .date()\n .optional()\n .describe(\n \"execute task at the specified date and time (only use if the type is scheduled)\"\n ),\n delayInSeconds: z\n .number()\n .optional()\n .describe(\n \"execute task after a delay in seconds (only use if the type is delayed)\"\n ),\n cron: z\n .string()\n .optional()\n .describe(\n \"execute task on a recurring interval specified as cron syntax (only use if the type is cron)\"\n ),\n }),\n});\n"],"mappings":";AAAA,SAAS,SAAS;AAIX,SAAS,2BAA2B,OAAuB;AAChE,SAAO;AAAA;AAAA;AAAA,gBAGO,MAAM,KAAK,YAAY,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4CxC;AAEO,IAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,aAAa,EAAE,OAAO,EAAE,SAAS,2BAA2B;AAAA,EAC5D,MAAM,EAAE,OAAO;AAAA,IACb,MAAM,EACH,KAAK,CAAC,aAAa,WAAW,QAAQ,aAAa,CAAC,EACpD,SAAS,gCAAgC;AAAA,IAC5C,MAAM,EAAE,OACL,KAAK,EACL,SAAS,EACT;AAAA,MACC;AAAA,IACF;AAAA,IACF,gBAAgB,EACb,OAAO,EACP,SAAS,EACT;AAAA,MACC;AAAA,IACF;AAAA,IACF,MAAM,EACH,OAAO,EACP,SAAS,EACT;AAAA,MACC;AAAA,IACF;AAAA,EACJ,CAAC;AACH,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/schedule.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport type Schedule = z.infer<typeof unstable_scheduleSchema>;\n\nexport function unstable_getSchedulePrompt(event: { date: Date }) {\n return `\n[Schedule Parser Component]\n\nCurrent time: ${event.date.toUTCString()}\n\nThis component parses natural language scheduling requests into a structured format. It extracts:\n1. A clean task description (without timing information)\n2. Scheduling details in one of these formats:\n - scheduled: Specific date/time events\n - delayed: Relative time delays (in seconds)\n - cron: Recurring patterns\n - no-schedule: Tasks without timing\n\nRules:\n- Task descriptions should be clean and focused on the action\n- Use numbers (0-6) for days in cron patterns (0=Sunday)\n- For recurring tasks, use standard cron syntax\n- For relative times, convert to seconds\n- For specific dates, use the current time as reference\n\nExample outputs:\n{\n \"description\": \"meeting with team\",\n \"when\": {\n \"type\": \"scheduled\",\n \"date\": \"tomorrow at 14:00\"\n }\n}\n\n{\n \"description\": \"backup database\",\n \"when\": {\n \"type\": \"cron\",\n \"cron\": \"0 0 * * *\"\n }\n}\n\n{\n \"description\": \"send report\",\n \"when\": {\n \"type\": \"delayed\",\n \"delayInSeconds\": 1800\n }\n}\n\n[End Schedule Parser Component]\n`;\n}\n\nexport const unstable_scheduleSchema = z.object({\n description: z.string().describe(\"A description of the task\"),\n when: z.object({\n type: z\n .enum([\"scheduled\", \"delayed\", \"cron\", \"no-schedule\"])\n .describe(\"The type of scheduling details\"),\n date: z.coerce\n .date()\n .optional()\n .describe(\n \"execute task at the specified date and time (only use if the type is scheduled)\"\n ),\n delayInSeconds: z\n .number()\n .optional()\n .describe(\n \"execute task after a delay in seconds (only use if the type is delayed)\"\n ),\n cron: z\n .string()\n .optional()\n .describe(\n \"execute task on a recurring interval specified as cron syntax (only use if the type is cron)\"\n ),\n }),\n});\n"],"mappings":";;;AAAA,SAAS,SAAS;AAIX,SAAS,2BAA2B,OAAuB;AAChE,SAAO;AAAA;AAAA;AAAA,gBAGO,MAAM,KAAK,YAAY,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4CxC;AAEO,IAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,aAAa,EAAE,OAAO,EAAE,SAAS,2BAA2B;AAAA,EAC5D,MAAM,EAAE,OAAO;AAAA,IACb,MAAM,EACH,KAAK,CAAC,aAAa,WAAW,QAAQ,aAAa,CAAC,EACpD,SAAS,gCAAgC;AAAA,IAC5C,MAAM,EAAE,OACL,KAAK,EACL,SAAS,EACT;AAAA,MACC;AAAA,IACF;AAAA,IACF,gBAAgB,EACb,OAAO,EACP,SAAS,EACT;AAAA,MACC;AAAA,IACF;AAAA,IACF,MAAM,EACH,OAAO,EACP,SAAS,EACT;AAAA,MACC;AAAA,IACF;AAAA,EACJ,CAAC;AACH,CAAC;","names":[]}