agents 0.3.6 → 0.3.7

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 (69) hide show
  1. package/README.md +303 -314
  2. package/dist/ai-types.js +1 -1
  3. package/dist/{client-0lfEZpSQ.js → client-6CeOh-EV.js} +7 -3
  4. package/dist/client-6CeOh-EV.js.map +1 -0
  5. package/dist/{client-Cxno-5sH.d.ts → client-DGpERepg.d.ts} +8 -14
  6. package/dist/client.d.ts +80 -24
  7. package/dist/client.js +184 -2
  8. package/dist/client.js.map +1 -0
  9. package/dist/{do-oauth-client-provider-BH9zFtSy.d.ts → do-oauth-client-provider-BqnOQzjy.d.ts} +1 -1
  10. package/dist/{do-oauth-client-provider-BfPFgQU0.js → do-oauth-client-provider-DDg8QrEA.js} +1 -1
  11. package/dist/{do-oauth-client-provider-BfPFgQU0.js.map → do-oauth-client-provider-DDg8QrEA.js.map} +1 -1
  12. package/dist/email-8ljcpvwV.d.ts +157 -0
  13. package/dist/email-XHsSYsTO.js +223 -0
  14. package/dist/email-XHsSYsTO.js.map +1 -0
  15. package/dist/email.d.ts +30 -0
  16. package/dist/email.js +3 -0
  17. package/dist/{index-B7Ny-XfU.d.ts → index-N6791tVt.d.ts} +18 -3
  18. package/dist/index.d.ts +543 -87
  19. package/dist/index.js +6 -6
  20. package/dist/{internal_context-neg89p5n.d.ts → internal_context-CEu5ji80.d.ts} +8 -3
  21. package/dist/{internal_context-oN047Id3.js → internal_context-D9eKFth1.js} +1 -1
  22. package/dist/internal_context-D9eKFth1.js.map +1 -0
  23. package/dist/internal_context.d.ts +1 -1
  24. package/dist/internal_context.js +1 -1
  25. package/dist/mcp/client.d.ts +1 -1
  26. package/dist/mcp/client.js +2 -2
  27. package/dist/mcp/do-oauth-client-provider.d.ts +1 -1
  28. package/dist/mcp/do-oauth-client-provider.js +1 -1
  29. package/dist/mcp/index.d.ts +4 -2
  30. package/dist/mcp/index.js +35 -35
  31. package/dist/mcp/index.js.map +1 -1
  32. package/dist/mcp/x402.d.ts +0 -1
  33. package/dist/mcp/x402.js.map +1 -1
  34. package/dist/{mcp-AK39tq6H.d.ts → mcp-BwPscEiF.d.ts} +1 -1
  35. package/dist/observability/index.d.ts +1 -1
  36. package/dist/observability/index.js +5 -5
  37. package/dist/react.d.ts +70 -26
  38. package/dist/react.js +83 -21
  39. package/dist/react.js.map +1 -1
  40. package/dist/schedule.d.ts +23 -2
  41. package/dist/schedule.js +23 -1
  42. package/dist/schedule.js.map +1 -1
  43. package/dist/serializable.d.ts +68 -3
  44. package/dist/src-BFP4sOQ4.js +2146 -0
  45. package/dist/src-BFP4sOQ4.js.map +1 -0
  46. package/dist/types-BITaDFf-.js +16 -0
  47. package/dist/{types-4b5tlB0u.js.map → types-BITaDFf-.js.map} +1 -1
  48. package/dist/{types-C5vR2Gzv.d.ts → types-DSSHBW6w.d.ts} +2 -1
  49. package/dist/types.d.ts +1 -1
  50. package/dist/types.js +1 -1
  51. package/dist/utils-B49TmLCI.js +16 -0
  52. package/dist/utils-B49TmLCI.js.map +1 -0
  53. package/dist/utils.d.ts +10 -0
  54. package/dist/utils.js +3 -0
  55. package/dist/workflow-types-Z_Oem1FJ.d.ts +260 -0
  56. package/dist/workflow-types.d.ts +48 -0
  57. package/dist/workflow-types.js +16 -0
  58. package/dist/workflow-types.js.map +1 -0
  59. package/dist/workflows.d.ts +163 -0
  60. package/dist/workflows.js +240 -0
  61. package/dist/workflows.js.map +1 -0
  62. package/package.json +22 -12
  63. package/dist/client-0lfEZpSQ.js.map +0 -1
  64. package/dist/client-CEO0P7vN.js +0 -117
  65. package/dist/client-CEO0P7vN.js.map +0 -1
  66. package/dist/internal_context-oN047Id3.js.map +0 -1
  67. package/dist/src-C_iKczoR.js +0 -1191
  68. package/dist/src-C_iKczoR.js.map +0 -1
  69. package/dist/types-4b5tlB0u.js +0 -15
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":[],"sources":["../src/client.ts"],"sourcesContent":["import {\n type PartyFetchOptions,\n PartySocket,\n type PartySocketOptions\n} from \"partysocket\";\nimport type { RPCRequest, RPCResponse } from \"./\";\nimport type {\n SerializableReturnValue,\n SerializableValue\n} from \"./serializable\";\nimport { MessageType } from \"./types\";\n\n/**\n * Options for creating an AgentClient\n */\nexport type AgentClientOptions<State = unknown> = Omit<\n PartySocketOptions,\n \"party\" | \"room\"\n> & {\n /** Name of the agent to connect to (ignored if basePath is set) */\n agent: string;\n /** Name of the specific Agent instance (ignored if basePath is set) */\n name?: string;\n /**\n * Full URL path - bypasses agent/name URL construction.\n * When set, the client connects to this path directly.\n * Server must handle routing manually (e.g., with getAgentByName + fetch).\n * @example\n * // Client connects to /user, server routes based on session\n * useAgent({ agent: \"UserAgent\", basePath: \"user\" })\n */\n basePath?: string;\n /** Called when the Agent's state is updated */\n onStateUpdate?: (state: State, source: \"server\" | \"client\") => void;\n /**\n * Called when the server sends the agent's identity on connect.\n * Useful when using basePath, as the actual instance name is determined server-side.\n * @param name The actual agent instance name\n * @param agent The agent class name (kebab-case)\n */\n onIdentity?: (name: string, agent: string) => void;\n /**\n * Called when identity changes on reconnect (different instance than before).\n * If not provided and identity changes, a warning will be logged.\n * @param oldName Previous instance name\n * @param newName New instance name\n * @param oldAgent Previous agent class name\n * @param newAgent New agent class name\n */\n onIdentityChange?: (\n oldName: string,\n newName: string,\n oldAgent: string,\n newAgent: string\n ) => void;\n /**\n * Additional path to append to the URL.\n * Works with both standard routing and basePath.\n * @example\n * // With basePath: /user/settings\n * { basePath: \"user\", path: \"settings\" }\n * // Standard: /agents/my-agent/room/settings\n * { agent: \"MyAgent\", name: \"room\", path: \"settings\" }\n */\n path?: string;\n};\n\n/**\n * Options for streaming RPC calls\n */\nexport type StreamOptions = {\n /** Called when a chunk of data is received */\n onChunk?: (chunk: unknown) => void;\n /** Called when the stream ends */\n onDone?: (finalChunk: unknown) => void;\n /** Called when an error occurs */\n onError?: (error: string) => void;\n};\n\n/**\n * Options for RPC calls\n */\nexport type CallOptions = {\n /** Timeout in milliseconds. If the call doesn't complete within this time, it will be rejected. */\n timeout?: number;\n /** Streaming options for handling streaming responses */\n stream?: StreamOptions;\n};\n\n/**\n * Options for the agentFetch function\n */\nexport type AgentClientFetchOptions = Omit<\n PartyFetchOptions,\n \"party\" | \"room\"\n> & {\n /** Name of the agent to connect to (ignored if basePath is set) */\n agent: string;\n /** Name of the specific Agent instance (ignored if basePath is set) */\n name?: string;\n /**\n * Full URL path - bypasses agent/name URL construction.\n * When set, the request is made to this path directly.\n */\n basePath?: string;\n};\n\nimport { camelCaseToKebabCase } from \"./utils\";\n\n/**\n * WebSocket client for connecting to an Agent\n */\nexport class AgentClient<State = unknown> extends PartySocket {\n /**\n * @deprecated Use agentFetch instead\n */\n static fetch(_opts: PartyFetchOptions): Promise<Response> {\n throw new Error(\n \"AgentClient.fetch is not implemented, use agentFetch instead\"\n );\n }\n agent: string;\n name: string;\n\n /**\n * Whether the client has received identity from the server.\n * Becomes true after the first identity message is received.\n * Resets to false on connection close.\n */\n identified = false;\n\n /**\n * Promise that resolves when identity has been received from the server.\n * Useful for waiting before making calls that depend on knowing the instance.\n * Resets on connection close so it can be awaited again after reconnect.\n */\n get ready(): Promise<void> {\n return this._readyPromise;\n }\n\n private options: AgentClientOptions<State>;\n private _pendingCalls = new Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n stream?: StreamOptions;\n type?: unknown;\n }\n >();\n private _readyPromise!: Promise<void>;\n private _resolveReady!: () => void;\n private _previousName: string | null = null;\n private _previousAgent: string | null = null;\n\n private _resetReady() {\n this._readyPromise = new Promise((resolve) => {\n this._resolveReady = resolve;\n });\n }\n\n constructor(options: AgentClientOptions<State>) {\n const agentNamespace = camelCaseToKebabCase(options.agent);\n\n // If basePath is provided, use it directly; otherwise construct from agent/name\n const socketOptions = options.basePath\n ? { basePath: options.basePath, path: options.path, ...options }\n : {\n party: agentNamespace,\n prefix: \"agents\",\n room: options.name || \"default\",\n path: options.path,\n ...options\n };\n\n super(socketOptions);\n this.agent = agentNamespace;\n this.name = options.name || \"default\";\n this.options = options;\n\n // Initialize ready promise\n this._resetReady();\n\n this.addEventListener(\"message\", (event) => {\n if (typeof event.data === \"string\") {\n let parsedMessage: Record<string, unknown>;\n try {\n parsedMessage = JSON.parse(event.data);\n } catch (_error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (parsedMessage.type === MessageType.CF_AGENT_IDENTITY) {\n const oldName = this._previousName;\n const oldAgent = this._previousAgent;\n const newName = parsedMessage.name as string;\n const newAgent = parsedMessage.agent as string;\n\n // Resolve ready/identified\n this.identified = true;\n this._resolveReady();\n\n // Detect identity change on reconnect\n if (\n oldName !== null &&\n oldAgent !== null &&\n (oldName !== newName || oldAgent !== newAgent)\n ) {\n if (this.options.onIdentityChange) {\n this.options.onIdentityChange(\n oldName,\n newName,\n oldAgent,\n newAgent\n );\n } else {\n const agentChanged = oldAgent !== newAgent;\n const nameChanged = oldName !== newName;\n let changeDescription = \"\";\n if (agentChanged && nameChanged) {\n changeDescription = `agent \"${oldAgent}\" → \"${newAgent}\", instance \"${oldName}\" → \"${newName}\"`;\n } else if (agentChanged) {\n changeDescription = `agent \"${oldAgent}\" → \"${newAgent}\"`;\n } else {\n changeDescription = `instance \"${oldName}\" → \"${newName}\"`;\n }\n console.warn(\n `[agents] Identity changed on reconnect: ${changeDescription}. ` +\n \"This can happen with server-side routing (e.g., basePath with getAgentByName) \" +\n \"where the instance is determined by auth/session. \" +\n \"Provide onIdentityChange callback to handle this explicitly, \" +\n \"or ignore if this is expected for your routing pattern.\"\n );\n }\n }\n\n // Always update from server identity (server is authoritative)\n this._previousName = newName;\n this._previousAgent = newAgent;\n this.name = newName;\n this.agent = newAgent;\n\n // Call onIdentity callback\n this.options.onIdentity?.(newName, newAgent);\n return;\n }\n if (parsedMessage.type === MessageType.CF_AGENT_STATE) {\n this.options.onStateUpdate?.(parsedMessage.state as State, \"server\");\n return;\n }\n if (parsedMessage.type === MessageType.RPC) {\n const response = parsedMessage as RPCResponse;\n const pending = this._pendingCalls.get(response.id);\n if (!pending) return;\n\n if (!response.success) {\n pending.reject(new Error(response.error));\n this._pendingCalls.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 this._pendingCalls.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 this._pendingCalls.delete(response.id);\n }\n }\n }\n });\n\n // Clean up pending calls and reset ready state when connection closes\n this.addEventListener(\"close\", () => {\n // Reset ready state for next connection\n this.identified = false;\n this._resetReady();\n\n // Reject all pending calls\n const error = new Error(\"Connection closed\");\n for (const pending of this._pendingCalls.values()) {\n pending.reject(error);\n pending.stream?.onError?.(\"Connection closed\");\n }\n this._pendingCalls.clear();\n });\n }\n\n setState(state: State) {\n this.send(JSON.stringify({ state, type: MessageType.CF_AGENT_STATE }));\n this.options.onStateUpdate?.(state, \"client\");\n }\n\n /**\n * Call a method on the Agent\n * @param method Name of the method to call\n * @param args Arguments to pass to the method\n * @param options Options for the call (timeout, streaming) or legacy StreamOptions\n * @returns Promise that resolves with the method's return value\n */\n call<T extends SerializableReturnValue>(\n method: string,\n args?: SerializableValue[],\n options?: CallOptions | StreamOptions\n ): Promise<T>;\n call<T = unknown>(\n method: string,\n args?: unknown[],\n options?: CallOptions | StreamOptions\n ): Promise<T>;\n async call<T>(\n method: string,\n args: unknown[] = [],\n options?: CallOptions | StreamOptions\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const id = crypto.randomUUID();\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n // Detect legacy format: { onChunk?, onDone?, onError? } vs new format: { timeout?, stream? }\n const isLegacyFormat =\n options &&\n (\"onChunk\" in options || \"onDone\" in options || \"onError\" in options);\n const streamOptions = isLegacyFormat\n ? (options as StreamOptions)\n : (options as CallOptions | undefined)?.stream;\n const timeout = isLegacyFormat\n ? undefined\n : (options as CallOptions | undefined)?.timeout;\n\n // Set up timeout if specified\n if (timeout) {\n timeoutId = setTimeout(() => {\n const pending = this._pendingCalls.get(id);\n this._pendingCalls.delete(id);\n const errorMessage = `RPC call to ${method} timed out after ${timeout}ms`;\n // Call stream onError callback if present (for streaming calls)\n pending?.stream?.onError?.(errorMessage);\n reject(new Error(errorMessage));\n }, timeout);\n }\n\n this._pendingCalls.set(id, {\n reject: (e: Error) => {\n if (timeoutId) clearTimeout(timeoutId);\n reject(e);\n },\n resolve: (value: unknown) => {\n if (timeoutId) clearTimeout(timeoutId);\n resolve(value as T);\n },\n stream: streamOptions,\n type: null as T\n });\n\n const request: RPCRequest = {\n args,\n id,\n method,\n type: MessageType.RPC\n };\n\n this.send(JSON.stringify(request));\n });\n }\n}\n\n/**\n * Make an HTTP request to an Agent\n * @param opts Connection options\n * @param init Request initialization options\n * @returns Promise resolving to a Response\n */\nexport function agentFetch(opts: AgentClientFetchOptions, init?: RequestInit) {\n const agentNamespace = camelCaseToKebabCase(opts.agent);\n\n // If basePath is provided, use it directly; otherwise construct from agent/name\n // When basePath is set, room/party aren't used by PartySocket (basePath replaces the URL)\n if (opts.basePath) {\n return PartySocket.fetch(\n { basePath: opts.basePath, ...opts } as unknown as PartyFetchOptions,\n init\n );\n }\n\n return PartySocket.fetch(\n {\n party: agentNamespace,\n prefix: \"agents\",\n room: opts.name || \"default\",\n ...opts\n },\n init\n );\n}\n"],"mappings":"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"}
@@ -67,4 +67,4 @@ declare class DurableObjectOAuthClientProvider implements AgentsOAuthProvider {
67
67
  }
68
68
  //#endregion
69
69
  export { DurableObjectOAuthClientProvider as n, AgentsOAuthProvider as t };
70
- //# sourceMappingURL=do-oauth-client-provider-BH9zFtSy.d.ts.map
70
+ //# sourceMappingURL=do-oauth-client-provider-BqnOQzjy.d.ts.map
@@ -152,4 +152,4 @@ var DurableObjectOAuthClientProvider = class {
152
152
 
153
153
  //#endregion
154
154
  export { DurableObjectOAuthClientProvider as t };
155
- //# sourceMappingURL=do-oauth-client-provider-BfPFgQU0.js.map
155
+ //# sourceMappingURL=do-oauth-client-provider-DDg8QrEA.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"do-oauth-client-provider-BfPFgQU0.js","names":["storage: DurableObjectStorage","clientName: string","baseRedirectUrl: string","storedState: StoredState","deleteKeys: string[]"],"sources":["../src/mcp/do-oauth-client-provider.ts"],"sourcesContent":["import type { OAuthClientProvider } from \"@modelcontextprotocol/sdk/client/auth.js\";\nimport type {\n OAuthClientInformation,\n OAuthClientInformationFull,\n OAuthClientMetadata,\n OAuthTokens\n} from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport { nanoid } from \"nanoid\";\n\nconst STATE_EXPIRATION_MS = 10 * 60 * 1000; // 10 minutes\n\ninterface StoredState {\n nonce: string;\n serverId: string;\n createdAt: number;\n}\n\n// A slight extension to the standard OAuthClientProvider interface because `redirectToAuthorization` doesn't give us the interface we need\n// This allows us to track authentication for a specific server and associated dynamic client registration\nexport interface AgentsOAuthProvider extends OAuthClientProvider {\n authUrl: string | undefined;\n clientId: string | undefined;\n serverId: string | undefined;\n checkState(\n state: string\n ): Promise<{ valid: boolean; serverId?: string; error?: string }>;\n consumeState(state: string): Promise<void>;\n deleteCodeVerifier(): Promise<void>;\n}\n\nexport class DurableObjectOAuthClientProvider implements AgentsOAuthProvider {\n private _authUrl_: string | undefined;\n private _serverId_: string | undefined;\n private _clientId_: string | undefined;\n\n constructor(\n public storage: DurableObjectStorage,\n public clientName: string,\n public baseRedirectUrl: string\n ) {\n if (!storage) {\n throw new Error(\n \"DurableObjectOAuthClientProvider requires a valid DurableObjectStorage instance\"\n );\n }\n }\n\n get clientMetadata(): OAuthClientMetadata {\n return {\n client_name: this.clientName,\n client_uri: this.clientUri,\n grant_types: [\"authorization_code\", \"refresh_token\"],\n redirect_uris: [this.redirectUrl],\n response_types: [\"code\"],\n token_endpoint_auth_method: \"none\"\n };\n }\n\n get clientUri() {\n return new URL(this.redirectUrl).origin;\n }\n\n get redirectUrl() {\n return this.baseRedirectUrl;\n }\n\n get clientId() {\n if (!this._clientId_) {\n throw new Error(\"Trying to access clientId before it was set\");\n }\n return this._clientId_;\n }\n\n set clientId(clientId_: string) {\n this._clientId_ = clientId_;\n }\n\n get serverId() {\n if (!this._serverId_) {\n throw new Error(\"Trying to access serverId before it was set\");\n }\n return this._serverId_;\n }\n\n set serverId(serverId_: string) {\n this._serverId_ = serverId_;\n }\n\n keyPrefix(clientId: string) {\n return `/${this.clientName}/${this.serverId}/${clientId}`;\n }\n\n clientInfoKey(clientId: string) {\n return `${this.keyPrefix(clientId)}/client_info/`;\n }\n\n async clientInformation(): Promise<OAuthClientInformation | undefined> {\n if (!this._clientId_) {\n return undefined;\n }\n return (\n (await this.storage.get<OAuthClientInformation>(\n this.clientInfoKey(this.clientId)\n )) ?? undefined\n );\n }\n\n async saveClientInformation(\n clientInformation: OAuthClientInformationFull\n ): Promise<void> {\n await this.storage.put(\n this.clientInfoKey(clientInformation.client_id),\n clientInformation\n );\n this.clientId = clientInformation.client_id;\n }\n\n tokenKey(clientId: string) {\n return `${this.keyPrefix(clientId)}/token`;\n }\n\n async tokens(): Promise<OAuthTokens | undefined> {\n if (!this._clientId_) {\n return undefined;\n }\n return (\n (await this.storage.get<OAuthTokens>(this.tokenKey(this.clientId))) ??\n undefined\n );\n }\n\n async saveTokens(tokens: OAuthTokens): Promise<void> {\n await this.storage.put(this.tokenKey(this.clientId), tokens);\n }\n\n get authUrl() {\n return this._authUrl_;\n }\n\n stateKey(nonce: string) {\n return `/${this.clientName}/${this.serverId}/state/${nonce}`;\n }\n\n async state(): Promise<string> {\n const nonce = nanoid();\n const state = `${nonce}.${this.serverId}`;\n const storedState: StoredState = {\n nonce,\n serverId: this.serverId,\n createdAt: Date.now()\n };\n await this.storage.put(this.stateKey(nonce), storedState);\n return state;\n }\n\n async checkState(\n state: string\n ): Promise<{ valid: boolean; serverId?: string; error?: string }> {\n const parts = state.split(\".\");\n if (parts.length !== 2) {\n return { valid: false, error: \"Invalid state format\" };\n }\n\n const [nonce, serverId] = parts;\n const key = this.stateKey(nonce);\n const storedState = await this.storage.get<StoredState>(key);\n\n if (!storedState) {\n return { valid: false, error: \"State not found or already used\" };\n }\n\n if (storedState.serverId !== serverId) {\n await this.storage.delete(key);\n return { valid: false, error: \"State serverId mismatch\" };\n }\n\n const age = Date.now() - storedState.createdAt;\n if (age > STATE_EXPIRATION_MS) {\n await this.storage.delete(key);\n return { valid: false, error: \"State expired\" };\n }\n\n return { valid: true, serverId };\n }\n\n async consumeState(state: string): Promise<void> {\n const parts = state.split(\".\");\n if (parts.length !== 2) {\n // This should never happen since checkState validates format first.\n // Log for debugging but don't throw - state consumption is best-effort.\n console.warn(\n `[OAuth] consumeState called with invalid state format: ${state.substring(0, 20)}...`\n );\n return;\n }\n const [nonce] = parts;\n await this.storage.delete(this.stateKey(nonce));\n }\n\n async redirectToAuthorization(authUrl: URL): Promise<void> {\n this._authUrl_ = authUrl.toString();\n }\n\n async invalidateCredentials(\n scope: \"all\" | \"client\" | \"tokens\" | \"verifier\"\n ): Promise<void> {\n if (!this._clientId_) return;\n\n const deleteKeys: string[] = [];\n\n if (scope === \"all\" || scope === \"client\") {\n deleteKeys.push(this.clientInfoKey(this.clientId));\n }\n if (scope === \"all\" || scope === \"tokens\") {\n deleteKeys.push(this.tokenKey(this.clientId));\n }\n if (scope === \"all\" || scope === \"verifier\") {\n deleteKeys.push(this.codeVerifierKey(this.clientId));\n }\n\n if (deleteKeys.length > 0) {\n await this.storage.delete(deleteKeys);\n }\n }\n\n codeVerifierKey(clientId: string) {\n return `${this.keyPrefix(clientId)}/code_verifier`;\n }\n\n async saveCodeVerifier(verifier: string): Promise<void> {\n const key = this.codeVerifierKey(this.clientId);\n\n // Don't overwrite existing verifier to preserve first PKCE verifier\n const existing = await this.storage.get<string>(key);\n if (existing) {\n return;\n }\n\n await this.storage.put(key, verifier);\n }\n\n async codeVerifier(): Promise<string> {\n const codeVerifier = await this.storage.get<string>(\n this.codeVerifierKey(this.clientId)\n );\n if (!codeVerifier) {\n throw new Error(\"No code verifier found\");\n }\n return codeVerifier;\n }\n\n async deleteCodeVerifier(): Promise<void> {\n await this.storage.delete(this.codeVerifierKey(this.clientId));\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"do-oauth-client-provider-DDg8QrEA.js","names":[],"sources":["../src/mcp/do-oauth-client-provider.ts"],"sourcesContent":["import type { OAuthClientProvider } from \"@modelcontextprotocol/sdk/client/auth.js\";\nimport type {\n OAuthClientInformation,\n OAuthClientInformationFull,\n OAuthClientMetadata,\n OAuthTokens\n} from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport { nanoid } from \"nanoid\";\n\nconst STATE_EXPIRATION_MS = 10 * 60 * 1000; // 10 minutes\n\ninterface StoredState {\n nonce: string;\n serverId: string;\n createdAt: number;\n}\n\n// A slight extension to the standard OAuthClientProvider interface because `redirectToAuthorization` doesn't give us the interface we need\n// This allows us to track authentication for a specific server and associated dynamic client registration\nexport interface AgentsOAuthProvider extends OAuthClientProvider {\n authUrl: string | undefined;\n clientId: string | undefined;\n serverId: string | undefined;\n checkState(\n state: string\n ): Promise<{ valid: boolean; serverId?: string; error?: string }>;\n consumeState(state: string): Promise<void>;\n deleteCodeVerifier(): Promise<void>;\n}\n\nexport class DurableObjectOAuthClientProvider implements AgentsOAuthProvider {\n private _authUrl_: string | undefined;\n private _serverId_: string | undefined;\n private _clientId_: string | undefined;\n\n constructor(\n public storage: DurableObjectStorage,\n public clientName: string,\n public baseRedirectUrl: string\n ) {\n if (!storage) {\n throw new Error(\n \"DurableObjectOAuthClientProvider requires a valid DurableObjectStorage instance\"\n );\n }\n }\n\n get clientMetadata(): OAuthClientMetadata {\n return {\n client_name: this.clientName,\n client_uri: this.clientUri,\n grant_types: [\"authorization_code\", \"refresh_token\"],\n redirect_uris: [this.redirectUrl],\n response_types: [\"code\"],\n token_endpoint_auth_method: \"none\"\n };\n }\n\n get clientUri() {\n return new URL(this.redirectUrl).origin;\n }\n\n get redirectUrl() {\n return this.baseRedirectUrl;\n }\n\n get clientId() {\n if (!this._clientId_) {\n throw new Error(\"Trying to access clientId before it was set\");\n }\n return this._clientId_;\n }\n\n set clientId(clientId_: string) {\n this._clientId_ = clientId_;\n }\n\n get serverId() {\n if (!this._serverId_) {\n throw new Error(\"Trying to access serverId before it was set\");\n }\n return this._serverId_;\n }\n\n set serverId(serverId_: string) {\n this._serverId_ = serverId_;\n }\n\n keyPrefix(clientId: string) {\n return `/${this.clientName}/${this.serverId}/${clientId}`;\n }\n\n clientInfoKey(clientId: string) {\n return `${this.keyPrefix(clientId)}/client_info/`;\n }\n\n async clientInformation(): Promise<OAuthClientInformation | undefined> {\n if (!this._clientId_) {\n return undefined;\n }\n return (\n (await this.storage.get<OAuthClientInformation>(\n this.clientInfoKey(this.clientId)\n )) ?? undefined\n );\n }\n\n async saveClientInformation(\n clientInformation: OAuthClientInformationFull\n ): Promise<void> {\n await this.storage.put(\n this.clientInfoKey(clientInformation.client_id),\n clientInformation\n );\n this.clientId = clientInformation.client_id;\n }\n\n tokenKey(clientId: string) {\n return `${this.keyPrefix(clientId)}/token`;\n }\n\n async tokens(): Promise<OAuthTokens | undefined> {\n if (!this._clientId_) {\n return undefined;\n }\n return (\n (await this.storage.get<OAuthTokens>(this.tokenKey(this.clientId))) ??\n undefined\n );\n }\n\n async saveTokens(tokens: OAuthTokens): Promise<void> {\n await this.storage.put(this.tokenKey(this.clientId), tokens);\n }\n\n get authUrl() {\n return this._authUrl_;\n }\n\n stateKey(nonce: string) {\n return `/${this.clientName}/${this.serverId}/state/${nonce}`;\n }\n\n async state(): Promise<string> {\n const nonce = nanoid();\n const state = `${nonce}.${this.serverId}`;\n const storedState: StoredState = {\n nonce,\n serverId: this.serverId,\n createdAt: Date.now()\n };\n await this.storage.put(this.stateKey(nonce), storedState);\n return state;\n }\n\n async checkState(\n state: string\n ): Promise<{ valid: boolean; serverId?: string; error?: string }> {\n const parts = state.split(\".\");\n if (parts.length !== 2) {\n return { valid: false, error: \"Invalid state format\" };\n }\n\n const [nonce, serverId] = parts;\n const key = this.stateKey(nonce);\n const storedState = await this.storage.get<StoredState>(key);\n\n if (!storedState) {\n return { valid: false, error: \"State not found or already used\" };\n }\n\n if (storedState.serverId !== serverId) {\n await this.storage.delete(key);\n return { valid: false, error: \"State serverId mismatch\" };\n }\n\n const age = Date.now() - storedState.createdAt;\n if (age > STATE_EXPIRATION_MS) {\n await this.storage.delete(key);\n return { valid: false, error: \"State expired\" };\n }\n\n return { valid: true, serverId };\n }\n\n async consumeState(state: string): Promise<void> {\n const parts = state.split(\".\");\n if (parts.length !== 2) {\n // This should never happen since checkState validates format first.\n // Log for debugging but don't throw - state consumption is best-effort.\n console.warn(\n `[OAuth] consumeState called with invalid state format: ${state.substring(0, 20)}...`\n );\n return;\n }\n const [nonce] = parts;\n await this.storage.delete(this.stateKey(nonce));\n }\n\n async redirectToAuthorization(authUrl: URL): Promise<void> {\n this._authUrl_ = authUrl.toString();\n }\n\n async invalidateCredentials(\n scope: \"all\" | \"client\" | \"tokens\" | \"verifier\"\n ): Promise<void> {\n if (!this._clientId_) return;\n\n const deleteKeys: string[] = [];\n\n if (scope === \"all\" || scope === \"client\") {\n deleteKeys.push(this.clientInfoKey(this.clientId));\n }\n if (scope === \"all\" || scope === \"tokens\") {\n deleteKeys.push(this.tokenKey(this.clientId));\n }\n if (scope === \"all\" || scope === \"verifier\") {\n deleteKeys.push(this.codeVerifierKey(this.clientId));\n }\n\n if (deleteKeys.length > 0) {\n await this.storage.delete(deleteKeys);\n }\n }\n\n codeVerifierKey(clientId: string) {\n return `${this.keyPrefix(clientId)}/code_verifier`;\n }\n\n async saveCodeVerifier(verifier: string): Promise<void> {\n const key = this.codeVerifierKey(this.clientId);\n\n // Don't overwrite existing verifier to preserve first PKCE verifier\n const existing = await this.storage.get<string>(key);\n if (existing) {\n return;\n }\n\n await this.storage.put(key, verifier);\n }\n\n async codeVerifier(): Promise<string> {\n const codeVerifier = await this.storage.get<string>(\n this.codeVerifierKey(this.clientId)\n );\n if (!codeVerifier) {\n throw new Error(\"No code verifier found\");\n }\n return codeVerifier;\n }\n\n async deleteCodeVerifier(): Promise<void> {\n await this.storage.delete(this.codeVerifierKey(this.clientId));\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,157 @@
1
+ //#region src/email.d.ts
2
+ /**
3
+ * Header object as returned by postal-mime and similar email parsing libraries.
4
+ * Each header has a lowercase key and a string value.
5
+ */
6
+ type EmailHeader = {
7
+ /** Lowercase header name (e.g., "content-type", "x-custom-header") */ key: string; /** Header value */
8
+ value: string;
9
+ };
10
+ /**
11
+ * Check if an email appears to be an auto-reply based on standard headers.
12
+ * Checks for Auto-Submitted (RFC 3834), X-Auto-Response-Suppress, and Precedence headers.
13
+ *
14
+ * @param headers - Headers array from postal-mime Email.headers or similar format
15
+ * @returns true if email appears to be an auto-reply
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * if (isAutoReplyEmail(parsed.headers)) {
20
+ * // Skip processing auto-replies
21
+ * return;
22
+ * }
23
+ * ```
24
+ */
25
+ declare function isAutoReplyEmail(headers: EmailHeader[]): boolean;
26
+ /** Default signature expiration: 30 days in seconds */
27
+ declare const DEFAULT_MAX_AGE_SECONDS: number;
28
+ /**
29
+ * Sign agent routing headers for secure reply flows.
30
+ * Use this when sending outbound emails to ensure replies can be securely routed back.
31
+ *
32
+ * @param secret - Secret key for HMAC signing (store in environment variables)
33
+ * @param agentName - Name of the agent
34
+ * @param agentId - ID of the agent instance
35
+ * @returns Headers object with X-Agent-Name, X-Agent-ID, X-Agent-Sig, and X-Agent-Sig-Ts
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const headers = await signAgentHeaders(env.EMAIL_SECRET, "MyAgent", this.name);
40
+ * // Use these headers when sending outbound emails
41
+ * ```
42
+ */
43
+ declare function signAgentHeaders(
44
+ secret: string,
45
+ agentName: string,
46
+ agentId: string
47
+ ): Promise<Record<string, string>>;
48
+ type EmailResolverResult = {
49
+ agentName: string;
50
+ agentId: string; /** @internal Indicates this resolver requires secure reply signing */
51
+ _secureRouted?: boolean;
52
+ } | null;
53
+ type EmailResolver<Env> = (
54
+ email: ForwardableEmailMessage,
55
+ env: Env
56
+ ) => Promise<EmailResolverResult>;
57
+ /**
58
+ * Reason for signature verification failure
59
+ */
60
+ type SignatureFailureReason =
61
+ | "missing_headers"
62
+ | "expired"
63
+ | "invalid"
64
+ | "malformed_timestamp";
65
+ /**
66
+ * Options for createSecureReplyEmailResolver
67
+ */
68
+ type SecureReplyResolverOptions = {
69
+ /**
70
+ * Maximum age of signature in seconds.
71
+ * Signatures older than this will be rejected.
72
+ * Default: 30 days (2592000 seconds)
73
+ */
74
+ maxAge?: number;
75
+ /**
76
+ * Callback invoked when signature verification fails.
77
+ * Useful for logging and debugging.
78
+ */
79
+ onInvalidSignature?: (
80
+ email: ForwardableEmailMessage,
81
+ reason: SignatureFailureReason
82
+ ) => void;
83
+ };
84
+ /**
85
+ * @deprecated REMOVED due to security vulnerability (IDOR via spoofed headers).
86
+ * @throws Always throws an error with migration guidance.
87
+ */
88
+ declare function createHeaderBasedEmailResolver<Env>(): EmailResolver<Env>;
89
+ /**
90
+ * Create a resolver for routing email replies with signature verification.
91
+ * This resolver verifies that replies contain a valid HMAC signature, preventing
92
+ * attackers from routing emails to arbitrary agent instances.
93
+ *
94
+ * @param secret - Secret key for HMAC verification (must match the key used with signAgentHeaders)
95
+ * @param options - Optional configuration for signature verification
96
+ * @returns A function that resolves the agent to route the email to, or null if signature is invalid
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * // In your email handler
101
+ * const secureResolver = createSecureReplyEmailResolver(env.EMAIL_SECRET, {
102
+ * maxAge: 7 * 24 * 60 * 60, // 7 days
103
+ * onInvalidSignature: (email, reason) => {
104
+ * console.warn(`Invalid signature from ${email.from}: ${reason}`);
105
+ * }
106
+ * });
107
+ * const addressResolver = createAddressBasedEmailResolver("MyAgent");
108
+ *
109
+ * await routeAgentEmail(email, env, {
110
+ * resolver: async (email, env) => {
111
+ * // Try secure reply routing first
112
+ * const replyRouting = await secureResolver(email, env);
113
+ * if (replyRouting) return replyRouting;
114
+ * // Fall back to address-based routing
115
+ * return addressResolver(email, env);
116
+ * }
117
+ * });
118
+ * ```
119
+ */
120
+ declare function createSecureReplyEmailResolver<Env>(
121
+ secret: string,
122
+ options?: SecureReplyResolverOptions
123
+ ): EmailResolver<Env>;
124
+ /**
125
+ * Create a resolver that uses the email address to determine the agent to route the email to
126
+ * @param defaultAgentName The default agent name to use if the email address does not contain a sub-address
127
+ * @returns A function that resolves the agent to route the email to
128
+ */
129
+ declare function createAddressBasedEmailResolver<Env>(
130
+ defaultAgentName: string
131
+ ): EmailResolver<Env>;
132
+ /**
133
+ * Create a resolver that uses the agentName and agentId to determine the agent to route the email to
134
+ * @param agentName The name of the agent to route the email to
135
+ * @param agentId The id of the agent to route the email to
136
+ * @returns A function that resolves the agent to route the email to
137
+ */
138
+ declare function createCatchAllEmailResolver<Env>(
139
+ agentName: string,
140
+ agentId: string
141
+ ): EmailResolver<Env>;
142
+ //#endregion
143
+ export {
144
+ SecureReplyResolverOptions as a,
145
+ createCatchAllEmailResolver as c,
146
+ isAutoReplyEmail as d,
147
+ signAgentHeaders as f,
148
+ EmailResolverResult as i,
149
+ createHeaderBasedEmailResolver as l,
150
+ EmailHeader as n,
151
+ SignatureFailureReason as o,
152
+ EmailResolver as r,
153
+ createAddressBasedEmailResolver as s,
154
+ DEFAULT_MAX_AGE_SECONDS as t,
155
+ createSecureReplyEmailResolver as u
156
+ };
157
+ //# sourceMappingURL=email-8ljcpvwV.d.ts.map
@@ -0,0 +1,223 @@
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
+ }
220
+
221
+ //#endregion
222
+ export { createSecureReplyEmailResolver as a, createHeaderBasedEmailResolver as i, createAddressBasedEmailResolver as n, isAutoReplyEmail as o, createCatchAllEmailResolver as r, signAgentHeaders as s, DEFAULT_MAX_AGE_SECONDS as t };
223
+ //# sourceMappingURL=email-XHsSYsTO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-XHsSYsTO.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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"}
@@ -0,0 +1,30 @@
1
+ import { n as AgentEmail } from "./internal_context-CEu5ji80.js";
2
+ import {
3
+ a as SecureReplyResolverOptions,
4
+ c as createCatchAllEmailResolver,
5
+ d as isAutoReplyEmail,
6
+ f as signAgentHeaders,
7
+ i as EmailResolverResult,
8
+ l as createHeaderBasedEmailResolver,
9
+ n as EmailHeader,
10
+ o as SignatureFailureReason,
11
+ r as EmailResolver,
12
+ s as createAddressBasedEmailResolver,
13
+ t as DEFAULT_MAX_AGE_SECONDS,
14
+ u as createSecureReplyEmailResolver
15
+ } from "./email-8ljcpvwV.js";
16
+ export {
17
+ AgentEmail,
18
+ DEFAULT_MAX_AGE_SECONDS,
19
+ EmailHeader,
20
+ EmailResolver,
21
+ EmailResolverResult,
22
+ SecureReplyResolverOptions,
23
+ SignatureFailureReason,
24
+ createAddressBasedEmailResolver,
25
+ createCatchAllEmailResolver,
26
+ createHeaderBasedEmailResolver,
27
+ createSecureReplyEmailResolver,
28
+ isAutoReplyEmail,
29
+ signAgentHeaders
30
+ };
package/dist/email.js ADDED
@@ -0,0 +1,3 @@
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";
2
+
3
+ export { DEFAULT_MAX_AGE_SECONDS, createAddressBasedEmailResolver, createCatchAllEmailResolver, createHeaderBasedEmailResolver, createSecureReplyEmailResolver, isAutoReplyEmail, signAgentHeaders };
@@ -1,7 +1,6 @@
1
- import { n as BaseEvent, t as MCPObservabilityEvent } from "./mcp-AK39tq6H.js";
1
+ import { n as BaseEvent, t as MCPObservabilityEvent } from "./mcp-BwPscEiF.js";
2
2
 
3
3
  //#region src/observability/agent.d.ts
4
-
5
4
  /**
6
5
  * Agent-specific observability events
7
6
  * These track the lifecycle and operations of an Agent
@@ -30,6 +29,22 @@ type AgentObservabilityEvent =
30
29
  {
31
30
  connectionId: string;
32
31
  }
32
+ >
33
+ | BaseEvent<
34
+ | "workflow:start"
35
+ | "workflow:event"
36
+ | "workflow:approved"
37
+ | "workflow:rejected"
38
+ | "workflow:terminated"
39
+ | "workflow:paused"
40
+ | "workflow:resumed"
41
+ | "workflow:restarted",
42
+ {
43
+ workflowId: string;
44
+ workflowName?: string;
45
+ eventType?: string;
46
+ reason?: string;
47
+ }
33
48
  >;
34
49
  //#endregion
35
50
  //#region src/observability/index.d.ts
@@ -55,4 +70,4 @@ export {
55
70
  genericObservability as r,
56
71
  Observability as t
57
72
  };
58
- //# sourceMappingURL=index-B7Ny-XfU.d.ts.map
73
+ //# sourceMappingURL=index-N6791tVt.d.ts.map