agents 0.3.10 → 0.4.1

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.
Files changed (67) hide show
  1. package/README.md +2 -2
  2. package/dist/{index-N6791tVt.d.ts → agent-DY6QmSI_.d.ts} +3 -25
  3. package/dist/ai-types.js +1 -1
  4. package/dist/client-connection-CGMuV62J.js +472 -0
  5. package/dist/client-connection-CGMuV62J.js.map +1 -0
  6. package/dist/client-storage-Cvy5r9FG.d.ts +355 -0
  7. package/dist/client.d.ts +11 -7
  8. package/dist/client.js +6 -2
  9. package/dist/client.js.map +1 -1
  10. package/dist/email.d.ts +146 -16
  11. package/dist/email.js +222 -2
  12. package/dist/email.js.map +1 -0
  13. package/dist/index.d.ts +142 -41
  14. package/dist/index.js +2326 -6
  15. package/dist/index.js.map +1 -0
  16. package/dist/internal_context.d.ts +33 -6
  17. package/dist/internal_context.js +11 -2
  18. package/dist/internal_context.js.map +1 -0
  19. package/dist/mcp/client.d.ts +516 -2
  20. package/dist/mcp/client.js +662 -3
  21. package/dist/mcp/client.js.map +1 -0
  22. package/dist/mcp/do-oauth-client-provider.d.ts +61 -2
  23. package/dist/mcp/do-oauth-client-provider.js +154 -2
  24. package/dist/mcp/do-oauth-client-provider.js.map +1 -0
  25. package/dist/mcp/index.d.ts +3 -5
  26. package/dist/mcp/index.js +8 -7
  27. package/dist/mcp/index.js.map +1 -1
  28. package/dist/mcp/x402.d.ts +34 -14
  29. package/dist/mcp/x402.js +128 -66
  30. package/dist/mcp/x402.js.map +1 -1
  31. package/dist/{mcp-BwPscEiF.d.ts → mcp-Dw5vDrY8.d.ts} +1 -1
  32. package/dist/observability/index.d.ts +23 -2
  33. package/dist/observability/index.js +25 -6
  34. package/dist/observability/index.js.map +1 -0
  35. package/dist/react.d.ts +10 -10
  36. package/dist/react.js +6 -2
  37. package/dist/react.js.map +1 -1
  38. package/dist/types.d.ts +14 -1
  39. package/dist/types.js +16 -2
  40. package/dist/types.js.map +1 -0
  41. package/dist/utils.js +15 -2
  42. package/dist/utils.js.map +1 -0
  43. package/dist/workflow-types.d.ts +235 -23
  44. package/dist/workflows.d.ts +22 -24
  45. package/dist/workflows.js +2 -5
  46. package/dist/workflows.js.map +1 -1
  47. package/package.json +24 -28
  48. package/dist/client-CtC9E06G.js +0 -1122
  49. package/dist/client-CtC9E06G.js.map +0 -1
  50. package/dist/client-DV1CZKqa.d.ts +0 -969
  51. package/dist/do-oauth-client-provider-BqnOQzjy.d.ts +0 -70
  52. package/dist/do-oauth-client-provider-DDg8QrEA.js +0 -155
  53. package/dist/do-oauth-client-provider-DDg8QrEA.js.map +0 -1
  54. package/dist/email-8ljcpvwV.d.ts +0 -157
  55. package/dist/email-XHsSYsTO.js +0 -223
  56. package/dist/email-XHsSYsTO.js.map +0 -1
  57. package/dist/internal_context-CEu5ji80.d.ts +0 -29
  58. package/dist/internal_context-D9eKFth1.js +0 -8
  59. package/dist/internal_context-D9eKFth1.js.map +0 -1
  60. package/dist/src-i_UcyBYf.js +0 -2147
  61. package/dist/src-i_UcyBYf.js.map +0 -1
  62. package/dist/types-BITaDFf-.js +0 -16
  63. package/dist/types-BITaDFf-.js.map +0 -1
  64. package/dist/types-DSSHBW6w.d.ts +0 -14
  65. package/dist/utils-B49TmLCI.js +0 -16
  66. package/dist/utils-B49TmLCI.js.map +0 -1
  67. package/dist/workflow-types-Z_Oem1FJ.d.ts +0 -260
package/dist/email.js CHANGED
@@ -1,3 +1,223 @@
1
- import { a as createSecureReplyEmailResolver, i as createHeaderBasedEmailResolver, n as createAddressBasedEmailResolver, o as isAutoReplyEmail, r as createCatchAllEmailResolver, s as signAgentHeaders, t as DEFAULT_MAX_AGE_SECONDS } from "./email-XHsSYsTO.js";
1
+ //#region src/email.ts
2
+ /**
3
+ * Check if an email appears to be an auto-reply based on standard headers.
4
+ * Checks for Auto-Submitted (RFC 3834), X-Auto-Response-Suppress, and Precedence headers.
5
+ *
6
+ * @param headers - Headers array from postal-mime Email.headers or similar format
7
+ * @returns true if email appears to be an auto-reply
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * if (isAutoReplyEmail(parsed.headers)) {
12
+ * // Skip processing auto-replies
13
+ * return;
14
+ * }
15
+ * ```
16
+ */
17
+ function isAutoReplyEmail(headers) {
18
+ return headers.some((h) => {
19
+ const key = h.key.toLowerCase();
20
+ const value = h.value.toLowerCase();
21
+ if (key === "auto-submitted") return value !== "no";
22
+ if (key === "x-auto-response-suppress") return true;
23
+ if (key === "precedence") return value === "bulk" || value === "junk" || value === "list";
24
+ return false;
25
+ });
26
+ }
27
+ /** Default signature expiration: 30 days in seconds */
28
+ const DEFAULT_MAX_AGE_SECONDS = 720 * 60 * 60;
29
+ /** Maximum allowed clock skew for future timestamps: 5 minutes */
30
+ const MAX_CLOCK_SKEW_SECONDS = 300;
31
+ /**
32
+ * Compute HMAC-SHA256 signature for agent routing headers
33
+ * @param secret - Secret key for HMAC
34
+ * @param agentName - Name of the agent
35
+ * @param agentId - ID of the agent instance
36
+ * @param timestamp - Unix timestamp in seconds
37
+ * @returns Base64-encoded HMAC signature
38
+ */
39
+ async function computeAgentSignature(secret, agentName, agentId, timestamp) {
40
+ const encoder = new TextEncoder();
41
+ const key = await crypto.subtle.importKey("raw", encoder.encode(secret), {
42
+ name: "HMAC",
43
+ hash: "SHA-256"
44
+ }, false, ["sign"]);
45
+ const data = encoder.encode(`${agentName}:${agentId}:${timestamp}`);
46
+ const signature = await crypto.subtle.sign("HMAC", key, data);
47
+ return btoa(String.fromCharCode(...new Uint8Array(signature)));
48
+ }
49
+ /**
50
+ * Verify HMAC-SHA256 signature for agent routing headers
51
+ * @param secret - Secret key for HMAC
52
+ * @param agentName - Name of the agent
53
+ * @param agentId - ID of the agent instance
54
+ * @param signature - Base64-encoded signature to verify
55
+ * @param timestamp - Unix timestamp in seconds when signature was created
56
+ * @param maxAgeSeconds - Maximum age of signature in seconds (default: 30 days)
57
+ * @returns Verification result with reason if invalid
58
+ */
59
+ async function verifyAgentSignature(secret, agentName, agentId, signature, timestamp, maxAgeSeconds = DEFAULT_MAX_AGE_SECONDS) {
60
+ try {
61
+ const timestampNum = Number.parseInt(timestamp, 10);
62
+ if (Number.isNaN(timestampNum)) return {
63
+ valid: false,
64
+ reason: "malformed_timestamp"
65
+ };
66
+ const now = Math.floor(Date.now() / 1e3);
67
+ if (timestampNum > now + MAX_CLOCK_SKEW_SECONDS) return {
68
+ valid: false,
69
+ reason: "invalid"
70
+ };
71
+ if (now - timestampNum > maxAgeSeconds) return {
72
+ valid: false,
73
+ reason: "expired"
74
+ };
75
+ const expected = await computeAgentSignature(secret, agentName, agentId, timestamp);
76
+ if (expected.length !== signature.length) return {
77
+ valid: false,
78
+ reason: "invalid"
79
+ };
80
+ let result = 0;
81
+ for (let i = 0; i < expected.length; i++) result |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
82
+ if (result !== 0) return {
83
+ valid: false,
84
+ reason: "invalid"
85
+ };
86
+ return { valid: true };
87
+ } catch (error) {
88
+ console.warn("[agents] Signature verification error:", error);
89
+ return {
90
+ valid: false,
91
+ reason: "invalid"
92
+ };
93
+ }
94
+ }
95
+ /**
96
+ * Sign agent routing headers for secure reply flows.
97
+ * Use this when sending outbound emails to ensure replies can be securely routed back.
98
+ *
99
+ * @param secret - Secret key for HMAC signing (store in environment variables)
100
+ * @param agentName - Name of the agent
101
+ * @param agentId - ID of the agent instance
102
+ * @returns Headers object with X-Agent-Name, X-Agent-ID, X-Agent-Sig, and X-Agent-Sig-Ts
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * const headers = await signAgentHeaders(env.EMAIL_SECRET, "MyAgent", this.name);
107
+ * // Use these headers when sending outbound emails
108
+ * ```
109
+ */
110
+ async function signAgentHeaders(secret, agentName, agentId) {
111
+ if (!secret) throw new Error("secret is required for signing agent headers");
112
+ if (!agentName) throw new Error("agentName is required for signing agent headers");
113
+ if (!agentId) throw new Error("agentId is required for signing agent headers");
114
+ if (agentName.includes(":")) throw new Error("agentName cannot contain colons");
115
+ if (agentId.includes(":")) throw new Error("agentId cannot contain colons");
116
+ const timestamp = Math.floor(Date.now() / 1e3).toString();
117
+ const signature = await computeAgentSignature(secret, agentName, agentId, timestamp);
118
+ return {
119
+ "X-Agent-Name": agentName,
120
+ "X-Agent-ID": agentId,
121
+ "X-Agent-Sig": signature,
122
+ "X-Agent-Sig-Ts": timestamp
123
+ };
124
+ }
125
+ /**
126
+ * @deprecated REMOVED due to security vulnerability (IDOR via spoofed headers).
127
+ * @throws Always throws an error with migration guidance.
128
+ */
129
+ function createHeaderBasedEmailResolver() {
130
+ throw new Error("createHeaderBasedEmailResolver has been removed due to a security vulnerability. It trusted attacker-controlled email headers for routing, enabling IDOR attacks.\n\nMigration options:\n - For inbound mail: use createAddressBasedEmailResolver(agentName)\n - For reply flows: use createSecureReplyEmailResolver(secret) with signed headers\n\nSee https://github.com/cloudflare/agents/blob/main/docs/email.md for details.");
131
+ }
132
+ /**
133
+ * Create a resolver for routing email replies with signature verification.
134
+ * This resolver verifies that replies contain a valid HMAC signature, preventing
135
+ * attackers from routing emails to arbitrary agent instances.
136
+ *
137
+ * @param secret - Secret key for HMAC verification (must match the key used with signAgentHeaders)
138
+ * @param options - Optional configuration for signature verification
139
+ * @returns A function that resolves the agent to route the email to, or null if signature is invalid
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * // In your email handler
144
+ * const secureResolver = createSecureReplyEmailResolver(env.EMAIL_SECRET, {
145
+ * maxAge: 7 * 24 * 60 * 60, // 7 days
146
+ * onInvalidSignature: (email, reason) => {
147
+ * console.warn(`Invalid signature from ${email.from}: ${reason}`);
148
+ * }
149
+ * });
150
+ * const addressResolver = createAddressBasedEmailResolver("MyAgent");
151
+ *
152
+ * await routeAgentEmail(email, env, {
153
+ * resolver: async (email, env) => {
154
+ * // Try secure reply routing first
155
+ * const replyRouting = await secureResolver(email, env);
156
+ * if (replyRouting) return replyRouting;
157
+ * // Fall back to address-based routing
158
+ * return addressResolver(email, env);
159
+ * }
160
+ * });
161
+ * ```
162
+ */
163
+ function createSecureReplyEmailResolver(secret, options) {
164
+ if (!secret) throw new Error("secret is required for createSecureReplyEmailResolver");
165
+ const maxAge = options?.maxAge ?? DEFAULT_MAX_AGE_SECONDS;
166
+ const onInvalidSignature = options?.onInvalidSignature;
167
+ return async (email, _env) => {
168
+ const agentName = email.headers.get("x-agent-name");
169
+ const agentId = email.headers.get("x-agent-id");
170
+ const signature = email.headers.get("x-agent-sig");
171
+ const timestamp = email.headers.get("x-agent-sig-ts");
172
+ if (!agentName || !agentId || !signature || !timestamp) {
173
+ onInvalidSignature?.(email, "missing_headers");
174
+ return null;
175
+ }
176
+ const result = await verifyAgentSignature(secret, agentName, agentId, signature, timestamp, maxAge);
177
+ if (!result.valid) {
178
+ onInvalidSignature?.(email, result.reason);
179
+ return null;
180
+ }
181
+ return {
182
+ agentName,
183
+ agentId,
184
+ _secureRouted: true
185
+ };
186
+ };
187
+ }
188
+ /**
189
+ * Create a resolver that uses the email address to determine the agent to route the email to
190
+ * @param defaultAgentName The default agent name to use if the email address does not contain a sub-address
191
+ * @returns A function that resolves the agent to route the email to
192
+ */
193
+ function createAddressBasedEmailResolver(defaultAgentName) {
194
+ return async (email, _env) => {
195
+ const emailMatch = email.to.match(/^([^+@]{1,64})(?:\+([^@]{1,64}))?@(.{1,253})$/);
196
+ if (!emailMatch) return null;
197
+ const [, localPart, subAddress] = emailMatch;
198
+ if (subAddress) return {
199
+ agentName: localPart,
200
+ agentId: subAddress
201
+ };
202
+ return {
203
+ agentName: defaultAgentName,
204
+ agentId: localPart
205
+ };
206
+ };
207
+ }
208
+ /**
209
+ * Create a resolver that uses the agentName and agentId to determine the agent to route the email to
210
+ * @param agentName The name of the agent to route the email to
211
+ * @param agentId The id of the agent to route the email to
212
+ * @returns A function that resolves the agent to route the email to
213
+ */
214
+ function createCatchAllEmailResolver(agentName, agentId) {
215
+ return async () => ({
216
+ agentName,
217
+ agentId
218
+ });
219
+ }
2
220
 
3
- export { DEFAULT_MAX_AGE_SECONDS, createAddressBasedEmailResolver, createCatchAllEmailResolver, createHeaderBasedEmailResolver, createSecureReplyEmailResolver, isAutoReplyEmail, signAgentHeaders };
221
+ //#endregion
222
+ export { DEFAULT_MAX_AGE_SECONDS, createAddressBasedEmailResolver, createCatchAllEmailResolver, createHeaderBasedEmailResolver, createSecureReplyEmailResolver, isAutoReplyEmail, signAgentHeaders };
223
+ //# sourceMappingURL=email.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.js","names":[],"sources":["../src/email.ts"],"sourcesContent":["/**\n * Email routing types, resolvers, and signing utilities for Agents\n */\n\n// Re-export AgentEmail type\nexport type { AgentEmail } from \"./internal_context\";\n\n// ============================================================================\n// Email header utilities\n// ============================================================================\n\n/**\n * Header object as returned by postal-mime and similar email parsing libraries.\n * Each header has a lowercase key and a string value.\n */\nexport type EmailHeader = {\n /** Lowercase header name (e.g., \"content-type\", \"x-custom-header\") */\n key: string;\n /** Header value */\n value: string;\n};\n\n/**\n * Check if an email appears to be an auto-reply based on standard headers.\n * Checks for Auto-Submitted (RFC 3834), X-Auto-Response-Suppress, and Precedence headers.\n *\n * @param headers - Headers array from postal-mime Email.headers or similar format\n * @returns true if email appears to be an auto-reply\n *\n * @example\n * ```typescript\n * if (isAutoReplyEmail(parsed.headers)) {\n * // Skip processing auto-replies\n * return;\n * }\n * ```\n */\nexport function isAutoReplyEmail(headers: EmailHeader[]): boolean {\n return headers.some((h) => {\n const key = h.key.toLowerCase();\n const value = h.value.toLowerCase();\n\n // RFC 3834: Auto-Submitted header\n // \"no\" means normal (human-sent) email, anything else indicates auto-reply\n if (key === \"auto-submitted\") {\n return value !== \"no\";\n }\n\n // X-Auto-Response-Suppress: any value indicates sender doesn't want auto-replies\n if (key === \"x-auto-response-suppress\") {\n return true;\n }\n\n // Precedence: only bulk/junk/list indicate automated/mass mail\n if (key === \"precedence\") {\n return value === \"bulk\" || value === \"junk\" || value === \"list\";\n }\n\n return false;\n });\n}\n\n// ============================================================================\n// Signing utilities\n// ============================================================================\n\n/** Default signature expiration: 30 days in seconds */\nexport const DEFAULT_MAX_AGE_SECONDS = 30 * 24 * 60 * 60;\n\n/** Maximum allowed clock skew for future timestamps: 5 minutes */\nconst MAX_CLOCK_SKEW_SECONDS = 5 * 60;\n\n/**\n * Compute HMAC-SHA256 signature for agent routing headers\n * @param secret - Secret key for HMAC\n * @param agentName - Name of the agent\n * @param agentId - ID of the agent instance\n * @param timestamp - Unix timestamp in seconds\n * @returns Base64-encoded HMAC signature\n */\nasync function computeAgentSignature(\n secret: string,\n agentName: string,\n agentId: string,\n timestamp: string\n): Promise<string> {\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\n \"raw\",\n encoder.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"]\n );\n const data = encoder.encode(`${agentName}:${agentId}:${timestamp}`);\n const signature = await crypto.subtle.sign(\"HMAC\", key, data);\n return btoa(String.fromCharCode(...new Uint8Array(signature)));\n}\n\n/**\n * Result of signature verification\n */\ntype SignatureVerificationResult =\n | { valid: true }\n | { valid: false; reason: \"expired\" | \"invalid\" | \"malformed_timestamp\" };\n\n/**\n * Verify HMAC-SHA256 signature for agent routing headers\n * @param secret - Secret key for HMAC\n * @param agentName - Name of the agent\n * @param agentId - ID of the agent instance\n * @param signature - Base64-encoded signature to verify\n * @param timestamp - Unix timestamp in seconds when signature was created\n * @param maxAgeSeconds - Maximum age of signature in seconds (default: 30 days)\n * @returns Verification result with reason if invalid\n */\nasync function verifyAgentSignature(\n secret: string,\n agentName: string,\n agentId: string,\n signature: string,\n timestamp: string,\n maxAgeSeconds: number = DEFAULT_MAX_AGE_SECONDS\n): Promise<SignatureVerificationResult> {\n try {\n // Validate timestamp format\n const timestampNum = Number.parseInt(timestamp, 10);\n if (Number.isNaN(timestampNum)) {\n return { valid: false, reason: \"malformed_timestamp\" };\n }\n\n // Check timestamp validity\n const now = Math.floor(Date.now() / 1000);\n\n // Reject timestamps too far in the future (prevents extending signature validity)\n if (timestampNum > now + MAX_CLOCK_SKEW_SECONDS) {\n return { valid: false, reason: \"invalid\" };\n }\n\n // Check if signature has expired\n if (now - timestampNum > maxAgeSeconds) {\n return { valid: false, reason: \"expired\" };\n }\n\n const expected = await computeAgentSignature(\n secret,\n agentName,\n agentId,\n timestamp\n );\n // Constant-time comparison to prevent timing attacks\n if (expected.length !== signature.length) {\n return { valid: false, reason: \"invalid\" };\n }\n let result = 0;\n for (let i = 0; i < expected.length; i++) {\n result |= expected.charCodeAt(i) ^ signature.charCodeAt(i);\n }\n if (result !== 0) {\n return { valid: false, reason: \"invalid\" };\n }\n return { valid: true };\n } catch (error) {\n console.warn(\"[agents] Signature verification error:\", error);\n return { valid: false, reason: \"invalid\" };\n }\n}\n\n/**\n * Sign agent routing headers for secure reply flows.\n * Use this when sending outbound emails to ensure replies can be securely routed back.\n *\n * @param secret - Secret key for HMAC signing (store in environment variables)\n * @param agentName - Name of the agent\n * @param agentId - ID of the agent instance\n * @returns Headers object with X-Agent-Name, X-Agent-ID, X-Agent-Sig, and X-Agent-Sig-Ts\n *\n * @example\n * ```typescript\n * const headers = await signAgentHeaders(env.EMAIL_SECRET, \"MyAgent\", this.name);\n * // Use these headers when sending outbound emails\n * ```\n */\nexport async function signAgentHeaders(\n secret: string,\n agentName: string,\n agentId: string\n): Promise<Record<string, string>> {\n if (!secret) {\n throw new Error(\"secret is required for signing agent headers\");\n }\n if (!agentName) {\n throw new Error(\"agentName is required for signing agent headers\");\n }\n if (!agentId) {\n throw new Error(\"agentId is required for signing agent headers\");\n }\n // Reject colons to prevent signature confusion attacks\n // (signature payload uses colon as delimiter: \"agentName:agentId:timestamp\")\n if (agentName.includes(\":\")) {\n throw new Error(\"agentName cannot contain colons\");\n }\n if (agentId.includes(\":\")) {\n throw new Error(\"agentId cannot contain colons\");\n }\n\n const timestamp = Math.floor(Date.now() / 1000).toString();\n const signature = await computeAgentSignature(\n secret,\n agentName,\n agentId,\n timestamp\n );\n return {\n \"X-Agent-Name\": agentName,\n \"X-Agent-ID\": agentId,\n \"X-Agent-Sig\": signature,\n \"X-Agent-Sig-Ts\": timestamp\n };\n}\n\n// ============================================================================\n// Email routing types and resolvers\n// ============================================================================\n\nexport type EmailResolverResult = {\n agentName: string;\n agentId: string;\n /** @internal Indicates this resolver requires secure reply signing */\n _secureRouted?: boolean;\n} | null;\n\nexport type EmailResolver<Env> = (\n email: ForwardableEmailMessage,\n env: Env\n) => Promise<EmailResolverResult>;\n\n/**\n * Reason for signature verification failure\n */\nexport type SignatureFailureReason =\n | \"missing_headers\"\n | \"expired\"\n | \"invalid\"\n | \"malformed_timestamp\";\n\n/**\n * Options for createSecureReplyEmailResolver\n */\nexport type SecureReplyResolverOptions = {\n /**\n * Maximum age of signature in seconds.\n * Signatures older than this will be rejected.\n * Default: 30 days (2592000 seconds)\n */\n maxAge?: number;\n /**\n * Callback invoked when signature verification fails.\n * Useful for logging and debugging.\n */\n onInvalidSignature?: (\n email: ForwardableEmailMessage,\n reason: SignatureFailureReason\n ) => void;\n};\n\n/**\n * @deprecated REMOVED due to security vulnerability (IDOR via spoofed headers).\n * @throws Always throws an error with migration guidance.\n */\nexport function createHeaderBasedEmailResolver<Env>(): EmailResolver<Env> {\n throw new Error(\n \"createHeaderBasedEmailResolver has been removed due to a security vulnerability. \" +\n \"It trusted attacker-controlled email headers for routing, enabling IDOR attacks.\\n\\n\" +\n \"Migration options:\\n\" +\n \" - For inbound mail: use createAddressBasedEmailResolver(agentName)\\n\" +\n \" - For reply flows: use createSecureReplyEmailResolver(secret) with signed headers\\n\\n\" +\n \"See https://github.com/cloudflare/agents/blob/main/docs/email.md for details.\"\n );\n}\n\n/**\n * Create a resolver for routing email replies with signature verification.\n * This resolver verifies that replies contain a valid HMAC signature, preventing\n * attackers from routing emails to arbitrary agent instances.\n *\n * @param secret - Secret key for HMAC verification (must match the key used with signAgentHeaders)\n * @param options - Optional configuration for signature verification\n * @returns A function that resolves the agent to route the email to, or null if signature is invalid\n *\n * @example\n * ```typescript\n * // In your email handler\n * const secureResolver = createSecureReplyEmailResolver(env.EMAIL_SECRET, {\n * maxAge: 7 * 24 * 60 * 60, // 7 days\n * onInvalidSignature: (email, reason) => {\n * console.warn(`Invalid signature from ${email.from}: ${reason}`);\n * }\n * });\n * const addressResolver = createAddressBasedEmailResolver(\"MyAgent\");\n *\n * await routeAgentEmail(email, env, {\n * resolver: async (email, env) => {\n * // Try secure reply routing first\n * const replyRouting = await secureResolver(email, env);\n * if (replyRouting) return replyRouting;\n * // Fall back to address-based routing\n * return addressResolver(email, env);\n * }\n * });\n * ```\n */\nexport function createSecureReplyEmailResolver<Env>(\n secret: string,\n options?: SecureReplyResolverOptions\n): EmailResolver<Env> {\n if (!secret) {\n throw new Error(\"secret is required for createSecureReplyEmailResolver\");\n }\n\n const maxAge = options?.maxAge ?? DEFAULT_MAX_AGE_SECONDS;\n const onInvalidSignature = options?.onInvalidSignature;\n\n return async (email: ForwardableEmailMessage, _env: Env) => {\n const agentName = email.headers.get(\"x-agent-name\");\n const agentId = email.headers.get(\"x-agent-id\");\n const signature = email.headers.get(\"x-agent-sig\");\n const timestamp = email.headers.get(\"x-agent-sig-ts\");\n\n if (!agentName || !agentId || !signature || !timestamp) {\n onInvalidSignature?.(email, \"missing_headers\");\n return null;\n }\n\n const result = await verifyAgentSignature(\n secret,\n agentName,\n agentId,\n signature,\n timestamp,\n maxAge\n );\n\n if (!result.valid) {\n onInvalidSignature?.(email, result.reason);\n return null;\n }\n\n return { agentName, agentId, _secureRouted: true };\n };\n}\n\n/**\n * Create a resolver that uses the email address to determine the agent to route the email to\n * @param defaultAgentName The default agent name to use if the email address does not contain a sub-address\n * @returns A function that resolves the agent to route the email to\n */\nexport function createAddressBasedEmailResolver<Env>(\n defaultAgentName: string\n): EmailResolver<Env> {\n return async (email: ForwardableEmailMessage, _env: Env) => {\n // Length limits per RFC 5321: local part max 64 chars, domain max 253 chars\n const emailMatch = email.to.match(\n /^([^+@]{1,64})(?:\\+([^@]{1,64}))?@(.{1,253})$/\n );\n if (!emailMatch) {\n return null;\n }\n\n const [, localPart, subAddress] = emailMatch;\n\n if (subAddress) {\n return {\n agentName: localPart,\n agentId: subAddress\n };\n }\n\n // Option 2: Use defaultAgentName namespace, localPart as agentId\n // Common for catch-all email routing to a single EmailAgent namespace\n return {\n agentName: defaultAgentName,\n agentId: localPart\n };\n };\n}\n\n/**\n * Create a resolver that uses the agentName and agentId to determine the agent to route the email to\n * @param agentName The name of the agent to route the email to\n * @param agentId The id of the agent to route the email to\n * @returns A function that resolves the agent to route the email to\n */\nexport function createCatchAllEmailResolver<Env>(\n agentName: string,\n agentId: string\n): EmailResolver<Env> {\n return async () => ({ agentName, agentId });\n}\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,27 +1,32 @@
1
- import { n as AgentEmail } from "./internal_context-CEu5ji80.js";
2
1
  import {
3
- l as createHeaderBasedEmailResolver,
4
- r as EmailResolver
5
- } from "./email-8ljcpvwV.js";
2
+ AgentEmail,
3
+ __DO_NOT_USE_WILL_BREAK__agentContext
4
+ } from "./internal_context.js";
5
+ import { EmailResolver, createHeaderBasedEmailResolver } from "./email.js";
6
6
  import {
7
- d as MCPConnectionState,
8
- g as TransportType,
9
- t as MCPClientManager
10
- } from "./client-DV1CZKqa.js";
7
+ l as TransportType,
8
+ r as MCPConnectionState
9
+ } from "./client-storage-Cvy5r9FG.js";
11
10
  import {
12
- _ as WorkflowPage,
13
- g as WorkflowInfo,
14
- h as WorkflowEventPayload,
15
- l as WorkflowCallback,
16
- s as RunWorkflowOptions,
17
- y as WorkflowQueryCriteria
18
- } from "./workflow-types-Z_Oem1FJ.js";
19
- import { t as Observability } from "./index-N6791tVt.js";
20
- import { t as MessageType } from "./types-DSSHBW6w.js";
11
+ AgentMcpOAuthProvider,
12
+ AgentsOAuthProvider
13
+ } from "./mcp/do-oauth-client-provider.js";
14
+ import { MCPClientManager } from "./mcp/client.js";
15
+ import {
16
+ RunWorkflowOptions,
17
+ WorkflowCallback,
18
+ WorkflowEventPayload,
19
+ WorkflowInfo,
20
+ WorkflowPage,
21
+ WorkflowQueryCriteria
22
+ } from "./workflow-types.js";
23
+ import { Observability } from "./observability/index.js";
24
+ import { MessageType } from "./types.js";
21
25
  import {
22
26
  Connection,
23
27
  Connection as Connection$1,
24
28
  ConnectionContext,
29
+ ConnectionContext as ConnectionContext$1,
25
30
  PartyServerOptions,
26
31
  Server,
27
32
  WSMessage
@@ -77,7 +82,7 @@ type RPCResponse = {
77
82
  * Metadata for a callable method
78
83
  */
79
84
  type CallableMetadata = {
80
- /** Optional description of what the method does */ description?: string; /** Whether the method supports streaming responses */
85
+ /** Optional description of what the method does */ description?: string /** Whether the method supports streaming responses */;
81
86
  streaming?: boolean;
82
87
  };
83
88
  /**
@@ -96,7 +101,7 @@ declare function callable(
96
101
  metadata?: CallableMetadata
97
102
  ): <This, Args extends unknown[], Return>(
98
103
  target: (this: This, ...args: Args) => Return,
99
- context: ClassMethodDecoratorContext
104
+ _context: ClassMethodDecoratorContext
100
105
  ) => (this: This, ...args: Args) => Return;
101
106
  /**
102
107
  * Decorator that marks a method as callable by clients
@@ -107,7 +112,7 @@ declare const unstable_callable: (
107
112
  metadata?: CallableMetadata
108
113
  ) => <This, Args extends unknown[], Return>(
109
114
  target: (this: This, ...args: Args) => Return,
110
- context: ClassMethodDecoratorContext
115
+ _context: ClassMethodDecoratorContext
111
116
  ) => (this: This, ...args: Args) => Return;
112
117
  type QueueItem<T = string> = {
113
118
  id: string;
@@ -120,27 +125,27 @@ type QueueItem<T = string> = {
120
125
  * @template T Type of the payload data
121
126
  */
122
127
  type Schedule<T = string> = {
123
- /** Unique identifier for the schedule */ id: string; /** Name of the method to be called */
124
- callback: string; /** Data to be passed to the callback */
128
+ /** Unique identifier for the schedule */ id: string /** Name of the method to be called */;
129
+ callback: string /** Data to be passed to the callback */;
125
130
  payload: T;
126
131
  } & (
127
132
  | {
128
- /** Type of schedule for one-time execution at a specific time */ type: "scheduled"; /** Timestamp when the task should execute */
133
+ /** Type of schedule for one-time execution at a specific time */ type: "scheduled" /** Timestamp when the task should execute */;
129
134
  time: number;
130
135
  }
131
136
  | {
132
- /** Type of schedule for delayed execution */ type: "delayed"; /** Timestamp when the task should execute */
133
- time: number; /** Number of seconds to delay execution */
137
+ /** Type of schedule for delayed execution */ type: "delayed" /** Timestamp when the task should execute */;
138
+ time: number /** Number of seconds to delay execution */;
134
139
  delayInSeconds: number;
135
140
  }
136
141
  | {
137
- /** Type of schedule for recurring execution based on cron expression */ type: "cron"; /** Timestamp for the next execution */
138
- time: number; /** Cron expression defining the schedule */
142
+ /** Type of schedule for recurring execution based on cron expression */ type: "cron" /** Timestamp for the next execution */;
143
+ time: number /** Cron expression defining the schedule */;
139
144
  cron: string;
140
145
  }
141
146
  | {
142
- /** Type of schedule for recurring execution at fixed intervals */ type: "interval"; /** Timestamp for the next execution */
143
- time: number; /** Number of seconds between executions */
147
+ /** Type of schedule for recurring execution at fixed intervals */ type: "interval" /** Timestamp for the next execution */;
148
+ time: number /** Number of seconds between executions */;
144
149
  intervalSeconds: number;
145
150
  }
146
151
  );
@@ -178,11 +183,19 @@ type MCPServer = {
178
183
  * Options for adding an MCP server
179
184
  */
180
185
  type AddMcpServerOptions = {
181
- /** OAuth callback host (auto-derived from request if omitted) */ callbackHost?: string; /** Agents routing prefix (default: "agents") */
182
- agentsPrefix?: string; /** MCP client options */
183
- client?: ConstructorParameters<typeof Client>[1]; /** Transport options */
186
+ /** OAuth callback host (auto-derived from request if omitted) */ callbackHost?: string;
187
+ /**
188
+ * Custom callback URL path bypasses the default `/agents/{class}/{name}/callback` construction.
189
+ * Required when `sendIdentityOnConnect` is `false` to prevent leaking the instance name.
190
+ * When set, the callback URL becomes `{callbackHost}/{callbackPath}`.
191
+ * The developer must route this path to the agent instance via `getAgentByName`.
192
+ * Should be a plain path (e.g., `/mcp-callback`) — do not include query strings or fragments.
193
+ */
194
+ callbackPath?: string /** Agents routing prefix (default: "agents") */;
195
+ agentsPrefix?: string /** MCP client options */;
196
+ client?: ConstructorParameters<typeof Client>[1] /** Transport options */;
184
197
  transport?: {
185
- /** Custom headers for authentication (e.g., bearer tokens, CF Access) */ headers?: HeadersInit; /** Transport type: "sse", "streamable-http", or "auto" (default) */
198
+ /** Custom headers for authentication (e.g., bearer tokens, CF Access) */ headers?: HeadersInit /** Transport type: "sse", "streamable-http", or "auto" (default) */;
186
199
  type?: TransportType;
187
200
  };
188
201
  };
@@ -191,7 +204,7 @@ type AddMcpServerOptions = {
191
204
  * Child classes can override specific options without spreading.
192
205
  */
193
206
  declare const DEFAULT_AGENT_STATIC_OPTIONS: {
194
- /** Whether the Agent should hibernate when inactive */ hibernate: boolean; /** Whether to send identity (name, agent) to clients on connect */
207
+ /** Whether the Agent should hibernate when inactive */ hibernate: boolean /** Whether to send identity (name, agent) to clients on connect */;
195
208
  sendIdentityOnConnect: boolean;
196
209
  /**
197
210
  * Timeout in seconds before a running interval schedule is considered "hung"
@@ -242,6 +255,19 @@ declare class Agent<
242
255
  private _state;
243
256
  private _disposables;
244
257
  private _destroyed;
258
+ /**
259
+ * Stores raw state accessors for wrapped connections.
260
+ * Used by setConnectionReadonly/isConnectionReadonly to read/write the
261
+ * _cf_readonly flag without going through the user-facing state/setState.
262
+ */
263
+ private _rawStateAccessors;
264
+ /**
265
+ * Cached persistence-hook dispatch mode, computed once in the constructor.
266
+ * - "new" → call onStateChanged
267
+ * - "old" → call onStateUpdate (deprecated)
268
+ * - "none" → neither hook is overridden, skip entirely
269
+ */
270
+ private _persistenceHookMode;
245
271
  private _ParentClass;
246
272
  readonly mcp: MCPClientManager;
247
273
  /**
@@ -290,8 +316,39 @@ declare class Agent<
290
316
  /**
291
317
  * Update the Agent's state
292
318
  * @param state New state to set
319
+ * @throws Error if called from a readonly connection context
293
320
  */
294
321
  setState(state: State): void;
322
+ /**
323
+ * Wraps connection.state and connection.setState so that the internal
324
+ * _cf_readonly flag is hidden from user code and cannot be accidentally
325
+ * overwritten. Must be called before any user code sees the connection.
326
+ *
327
+ * Idempotent — safe to call multiple times on the same connection.
328
+ */
329
+ private _ensureConnectionWrapped;
330
+ /**
331
+ * Mark a connection as readonly or readwrite
332
+ * @param connection The connection to mark
333
+ * @param readonly Whether the connection should be readonly (default: true)
334
+ */
335
+ setConnectionReadonly(connection: Connection$1, readonly?: boolean): void;
336
+ /**
337
+ * Check if a connection is marked as readonly
338
+ * @param connection The connection to check
339
+ * @returns True if the connection is readonly
340
+ */
341
+ isConnectionReadonly(connection: Connection$1): boolean;
342
+ /**
343
+ * Override this method to determine if a connection should be readonly on connect
344
+ * @param _connection The connection that is being established
345
+ * @param _ctx Connection context
346
+ * @returns True if the connection should be readonly
347
+ */
348
+ shouldConnectionBeReadonly(
349
+ _connection: Connection$1,
350
+ _ctx: ConnectionContext$1
351
+ ): boolean;
295
352
  /**
296
353
  * Called before the Agent's state is persisted and broadcast.
297
354
  * Override to validate or reject an update by throwing an error.
@@ -300,7 +357,25 @@ declare class Agent<
300
357
  */
301
358
  validateStateChange(nextState: State, source: Connection$1 | "server"): void;
302
359
  /**
303
- * Called when the Agent's state is updated
360
+ * Called after the Agent's state has been persisted and broadcast to all clients.
361
+ * This is a notification hook — errors here are routed to onError and do not
362
+ * affect state persistence or client broadcasts.
363
+ *
364
+ * @param state Updated state
365
+ * @param source Source of the state update ("server" or a client connection)
366
+ */
367
+ onStateChanged(
368
+ state: State | undefined,
369
+ source: Connection$1 | "server"
370
+ ): void;
371
+ /**
372
+ * @deprecated Renamed to `onStateChanged` — the behavior is identical.
373
+ * `onStateUpdate` will be removed in the next major version.
374
+ *
375
+ * Called after the Agent's state has been persisted and broadcast to all clients.
376
+ * This is a server-side notification hook. For the client-side state callback,
377
+ * see the `onStateUpdate` option in `useAgent` / `AgentClient`.
378
+ *
304
379
  * @param state Updated state
305
380
  * @param source Source of the state update ("server" or a client connection)
306
381
  */
@@ -308,6 +383,11 @@ declare class Agent<
308
383
  state: State | undefined,
309
384
  source: Connection$1 | "server"
310
385
  ): void;
386
+ /**
387
+ * Dispatch to the appropriate persistence hook based on the mode
388
+ * cached in the constructor. No prototype walks at call time.
389
+ */
390
+ private _callStatePersistenceHook;
311
391
  /**
312
392
  * Called when the Agent receives an email via routeAgentEmail()
313
393
  * Override this method to handle incoming emails
@@ -883,6 +963,29 @@ declare class Agent<
883
963
  >;
884
964
  removeMcpServer(id: string): Promise<void>;
885
965
  getMcpServers(): MCPServersState;
966
+ /**
967
+ * Create the OAuth provider used when connecting to MCP servers that require authentication.
968
+ *
969
+ * Override this method in a subclass to supply a custom OAuth provider implementation,
970
+ * for example to use pre-registered client credentials, mTLS-based authentication,
971
+ * or any other OAuth flow beyond dynamic client registration.
972
+ *
973
+ * @example
974
+ * // Custom OAuth provider
975
+ * class MyAgent extends Agent {
976
+ * createMcpOAuthProvider(callbackUrl: string): AgentMcpOAuthProvider {
977
+ * return new MyCustomOAuthProvider(
978
+ * this.ctx.storage,
979
+ * this.name,
980
+ * callbackUrl
981
+ * );
982
+ * }
983
+ * }
984
+ *
985
+ * @param callbackUrl The OAuth callback URL for the authorization flow
986
+ * @returns An {@link AgentMcpOAuthProvider} instance used by {@link addMcpServer}
987
+ */
988
+ createMcpOAuthProvider(callbackUrl: string): AgentMcpOAuthProvider;
886
989
  private broadcastMcpServers;
887
990
  /**
888
991
  * Handle MCP OAuth callback request if it's an OAuth callback.
@@ -920,12 +1023,7 @@ type AgentContext = DurableObjectState;
920
1023
  /**
921
1024
  * Configuration options for Agent routing
922
1025
  */
923
- type AgentOptions<Env> = PartyServerOptions<Env> & {
924
- /**
925
- * Whether to enable CORS for the Agent
926
- */
927
- cors?: boolean | HeadersInit | undefined;
928
- };
1026
+ type AgentOptions<Env> = PartyServerOptions<Env>;
929
1027
  /**
930
1028
  * Route a request to the appropriate Agent
931
1029
  * @param request Request to route
@@ -1017,9 +1115,11 @@ export {
1017
1115
  AddMcpServerOptions,
1018
1116
  Agent,
1019
1117
  AgentContext,
1118
+ type AgentMcpOAuthProvider,
1020
1119
  AgentNamespace,
1021
1120
  AgentOptions,
1022
1121
  AgentStaticOptions,
1122
+ type AgentsOAuthProvider,
1023
1123
  CallableMetadata,
1024
1124
  type Connection,
1025
1125
  type ConnectionContext,
@@ -1037,6 +1137,7 @@ export {
1037
1137
  StreamingResponse,
1038
1138
  type TransportType,
1039
1139
  type WSMessage,
1140
+ __DO_NOT_USE_WILL_BREAK__agentContext,
1040
1141
  callable,
1041
1142
  createHeaderBasedEmailResolver,
1042
1143
  getAgentByName,