agents 0.3.9 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{index-N6791tVt.d.ts → agent-DY6QmSI_.d.ts} +3 -25
- package/dist/ai-types.js +1 -1
- package/dist/client-connection-CGMuV62J.js +472 -0
- package/dist/client-connection-CGMuV62J.js.map +1 -0
- package/dist/client-storage-Cvy5r9FG.d.ts +355 -0
- package/dist/client.d.ts +11 -7
- package/dist/client.js +6 -2
- package/dist/client.js.map +1 -1
- package/dist/email.d.ts +146 -16
- package/dist/email.js +222 -2
- package/dist/email.js.map +1 -0
- package/dist/index.d.ts +139 -41
- package/dist/index.js +2317 -6
- package/dist/index.js.map +1 -0
- package/dist/internal_context.d.ts +28 -5
- package/dist/internal_context.js +7 -2
- package/dist/internal_context.js.map +1 -0
- package/dist/mcp/client.d.ts +516 -2
- package/dist/mcp/client.js +662 -3
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/do-oauth-client-provider.d.ts +61 -2
- package/dist/mcp/do-oauth-client-provider.js +154 -2
- package/dist/mcp/do-oauth-client-provider.js.map +1 -0
- package/dist/mcp/index.d.ts +3 -5
- package/dist/mcp/index.js +8 -7
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/x402.d.ts +34 -14
- package/dist/mcp/x402.js +128 -66
- package/dist/mcp/x402.js.map +1 -1
- package/dist/{mcp-BwPscEiF.d.ts → mcp-Dw5vDrY8.d.ts} +1 -1
- package/dist/observability/index.d.ts +23 -2
- package/dist/observability/index.js +25 -6
- package/dist/observability/index.js.map +1 -0
- package/dist/react.d.ts +16 -10
- package/dist/react.js +34 -16
- package/dist/react.js.map +1 -1
- package/dist/types.d.ts +14 -1
- package/dist/types.js +16 -2
- package/dist/types.js.map +1 -0
- package/dist/utils.js +15 -2
- package/dist/utils.js.map +1 -0
- package/dist/workflow-types.d.ts +235 -23
- package/dist/workflows.d.ts +22 -24
- package/dist/workflows.js +2 -5
- package/dist/workflows.js.map +1 -1
- package/package.json +26 -23
- package/dist/client-BA8NJB6l.js +0 -1124
- package/dist/client-BA8NJB6l.js.map +0 -1
- package/dist/client-Ba-xntmv.d.ts +0 -963
- package/dist/do-oauth-client-provider-BqnOQzjy.d.ts +0 -70
- package/dist/do-oauth-client-provider-DDg8QrEA.js +0 -155
- package/dist/do-oauth-client-provider-DDg8QrEA.js.map +0 -1
- package/dist/email-8ljcpvwV.d.ts +0 -157
- package/dist/email-XHsSYsTO.js +0 -223
- package/dist/email-XHsSYsTO.js.map +0 -1
- package/dist/internal_context-CEu5ji80.d.ts +0 -29
- package/dist/internal_context-D9eKFth1.js +0 -8
- package/dist/internal_context-D9eKFth1.js.map +0 -1
- package/dist/src-CqnVUbg1.js +0 -2146
- package/dist/src-CqnVUbg1.js.map +0 -1
- package/dist/types-BITaDFf-.js +0 -16
- package/dist/types-BITaDFf-.js.map +0 -1
- package/dist/types-DSSHBW6w.d.ts +0 -14
- package/dist/utils-B49TmLCI.js +0 -16
- package/dist/utils-B49TmLCI.js.map +0 -1
- package/dist/workflow-types-Z_Oem1FJ.d.ts +0 -260
package/dist/react.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { MessageType } from "./types.js";
|
|
2
|
+
import { camelCaseToKebabCase } from "./utils.js";
|
|
3
3
|
import { usePartySocket } from "partysocket/react";
|
|
4
4
|
import { use, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
5
5
|
|
|
@@ -49,7 +49,8 @@ const _testUtils = {
|
|
|
49
49
|
getCacheEntry,
|
|
50
50
|
deleteCacheEntry,
|
|
51
51
|
clearCache: () => queryCache.clear(),
|
|
52
|
-
createStubProxy
|
|
52
|
+
createStubProxy,
|
|
53
|
+
createCacheKey
|
|
53
54
|
};
|
|
54
55
|
function useAgent(options) {
|
|
55
56
|
const agentNamespace = camelCaseToKebabCase(options.agent);
|
|
@@ -60,8 +61,12 @@ function useAgent(options) {
|
|
|
60
61
|
options.name,
|
|
61
62
|
queryDeps
|
|
62
63
|
]);
|
|
64
|
+
const cacheKeyRef = useRef(cacheKey);
|
|
65
|
+
cacheKeyRef.current = cacheKey;
|
|
63
66
|
const ttl = cacheTtl ?? 300 * 1e3;
|
|
64
67
|
const [cacheInvalidatedAt, setCacheInvalidatedAt] = useState(0);
|
|
68
|
+
const isAsyncQuery = query && typeof query === "function";
|
|
69
|
+
const [awaitingQueryRefresh, setAwaitingQueryRefresh] = useState(false);
|
|
65
70
|
const queryPromise = useMemo(() => {
|
|
66
71
|
if (!query || typeof query !== "function") return null;
|
|
67
72
|
const cached = getCacheEntry(cacheKey);
|
|
@@ -103,6 +108,9 @@ function useAgent(options) {
|
|
|
103
108
|
resolvedQuery = queryResult;
|
|
104
109
|
}
|
|
105
110
|
} else resolvedQuery = query;
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
if (awaitingQueryRefresh && resolvedQuery !== void 0) setAwaitingQueryRefresh(false);
|
|
113
|
+
}, [awaitingQueryRefresh, resolvedQuery]);
|
|
106
114
|
const [identity, setIdentity] = useState({
|
|
107
115
|
name: options.name || "default",
|
|
108
116
|
agent: agentNamespace,
|
|
@@ -123,20 +131,23 @@ function useAgent(options) {
|
|
|
123
131
|
};
|
|
124
132
|
};
|
|
125
133
|
if (!readyRef.current) resetReady();
|
|
134
|
+
const socketOptions = options.basePath ? {
|
|
135
|
+
basePath: options.basePath,
|
|
136
|
+
path: options.path,
|
|
137
|
+
query: resolvedQuery,
|
|
138
|
+
...restOptions
|
|
139
|
+
} : {
|
|
140
|
+
party: agentNamespace,
|
|
141
|
+
prefix: "agents",
|
|
142
|
+
room: options.name || "default",
|
|
143
|
+
path: options.path,
|
|
144
|
+
query: resolvedQuery,
|
|
145
|
+
...restOptions
|
|
146
|
+
};
|
|
147
|
+
const socketEnabled = !awaitingQueryRefresh && (restOptions.enabled ?? true);
|
|
126
148
|
const agent = usePartySocket({
|
|
127
|
-
...
|
|
128
|
-
|
|
129
|
-
path: options.path,
|
|
130
|
-
query: resolvedQuery,
|
|
131
|
-
...restOptions
|
|
132
|
-
} : {
|
|
133
|
-
party: agentNamespace,
|
|
134
|
-
prefix: "agents",
|
|
135
|
-
room: options.name || "default",
|
|
136
|
-
path: options.path,
|
|
137
|
-
query: resolvedQuery,
|
|
138
|
-
...restOptions
|
|
139
|
-
},
|
|
149
|
+
...socketOptions,
|
|
150
|
+
enabled: socketEnabled,
|
|
140
151
|
onMessage: (message) => {
|
|
141
152
|
if (typeof message.data === "string") {
|
|
142
153
|
let parsedMessage;
|
|
@@ -177,6 +188,10 @@ function useAgent(options) {
|
|
|
177
188
|
options.onStateUpdate?.(parsedMessage.state, "server");
|
|
178
189
|
return;
|
|
179
190
|
}
|
|
191
|
+
if (parsedMessage.type === MessageType.CF_AGENT_STATE_ERROR) {
|
|
192
|
+
options.onStateUpdateError?.(parsedMessage.error);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
180
195
|
if (parsedMessage.type === MessageType.CF_AGENT_MCP_SERVERS) {
|
|
181
196
|
options.onMcpUpdate?.(parsedMessage.mcp);
|
|
182
197
|
return;
|
|
@@ -211,6 +226,9 @@ function useAgent(options) {
|
|
|
211
226
|
...prev,
|
|
212
227
|
identified: false
|
|
213
228
|
}));
|
|
229
|
+
if (isAsyncQuery) setAwaitingQueryRefresh(true);
|
|
230
|
+
deleteCacheEntry(cacheKeyRef.current);
|
|
231
|
+
setCacheInvalidatedAt(Date.now());
|
|
214
232
|
const error = /* @__PURE__ */ new Error("Connection closed");
|
|
215
233
|
for (const pending of pendingCallsRef.current.values()) {
|
|
216
234
|
pending.reject(error);
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.js","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import type { PartySocket } from \"partysocket\";\nimport { usePartySocket } from \"partysocket/react\";\nimport { useCallback, useRef, use, useMemo, useState, useEffect } from \"react\";\nimport type { Agent, MCPServersState, RPCRequest, RPCResponse } from \"./\";\nimport type { StreamOptions } from \"./client\";\nimport { camelCaseToKebabCase } from \"./utils\";\nimport type { Method, RPCMethod } from \"./serializable\";\nimport { MessageType } from \"./types\";\n\ntype QueryObject = Record<string, string | null>;\n\ninterface CacheEntry {\n promise: Promise<QueryObject>;\n expiresAt: number;\n}\n\nconst queryCache = new Map<string, CacheEntry>();\n\nfunction createCacheKey(\n agentNamespace: string,\n name: string | undefined,\n deps: unknown[]\n): string {\n return JSON.stringify([agentNamespace, name || \"default\", ...deps]);\n}\n\nfunction getCacheEntry(key: string): CacheEntry | undefined {\n const entry = queryCache.get(key);\n if (!entry) return undefined;\n\n if (Date.now() >= entry.expiresAt) {\n queryCache.delete(key);\n return undefined;\n }\n\n return entry;\n}\n\nfunction setCacheEntry(\n key: string,\n promise: Promise<QueryObject>,\n cacheTtl: number\n): CacheEntry {\n const entry: CacheEntry = {\n promise,\n expiresAt: Date.now() + cacheTtl\n };\n queryCache.set(key, entry);\n return entry;\n}\n\nfunction deleteCacheEntry(key: string): void {\n queryCache.delete(key);\n}\n\n/**\n * Creates a proxy that wraps RPC method calls.\n * Internal JS methods (toJSON, then, etc.) return undefined to avoid\n * triggering RPC calls during serialization (e.g., console.log)\n */\nfunction createStubProxy<T = Record<string, Method>>(\n call: (method: string, args: unknown[]) => unknown\n): T {\n // biome-ignore lint/suspicious/noExplicitAny: proxy needs any for dynamic method access\n return new Proxy<any>(\n {},\n {\n get: (_target, method) => {\n // Skip internal JavaScript methods that shouldn't trigger RPC calls.\n // These are commonly accessed by console.log, JSON.stringify, and other\n // serialization utilities.\n if (\n typeof method === \"symbol\" ||\n method === \"toJSON\" ||\n method === \"then\" ||\n method === \"catch\" ||\n method === \"finally\" ||\n method === \"valueOf\" ||\n method === \"toString\" ||\n method === \"constructor\" ||\n method === \"prototype\" ||\n method === \"$$typeof\" ||\n method === \"@@toStringTag\" ||\n method === \"asymmetricMatch\" ||\n method === \"nodeType\"\n ) {\n return undefined;\n }\n return (...args: unknown[]) => call(method as string, args);\n }\n }\n );\n}\n\n// Export for testing purposes\nexport const _testUtils = {\n queryCache,\n setCacheEntry,\n getCacheEntry,\n deleteCacheEntry,\n clearCache: () => queryCache.clear(),\n createStubProxy\n};\n\n/**\n * Options for the useAgent hook\n * @template State Type of the Agent's state\n */\nexport type UseAgentOptions<State = unknown> = Omit<\n Parameters<typeof usePartySocket>[0],\n \"party\" | \"room\" | \"query\"\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 /** Query parameters - can be static object or async function */\n query?: QueryObject | (() => Promise<QueryObject>);\n /** Dependencies for async query caching */\n queryDeps?: unknown[];\n /** Cache TTL in milliseconds for auth tokens/time-sensitive data */\n cacheTtl?: number;\n /** Called when the Agent's state is updated */\n onStateUpdate?: (state: State, source: \"server\" | \"client\") => void;\n /** Called when MCP server state is updated */\n onMcpUpdate?: (mcpServers: MCPServersState) => 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\ntype AllOptional<T> = T extends [infer A, ...infer R]\n ? undefined extends A\n ? AllOptional<R>\n : false\n : true; // no params means optional by default\n\ntype RPCMethods<T> = {\n [K in keyof T as T[K] extends RPCMethod<T[K]> ? K : never]: RPCMethod<T[K]>;\n};\n\ntype OptionalParametersMethod<T extends RPCMethod> =\n AllOptional<Parameters<T>> extends true ? T : never;\n\n// all methods of the Agent, excluding the ones that are declared in the base Agent class\n// biome-ignore lint: suppressions/parse\ntype AgentMethods<T> = Omit<RPCMethods<T>, keyof Agent<any, any>>;\n\ntype OptionalAgentMethods<T> = {\n [K in keyof AgentMethods<T> as AgentMethods<T>[K] extends OptionalParametersMethod<\n AgentMethods<T>[K]\n >\n ? K\n : never]: OptionalParametersMethod<AgentMethods<T>[K]>;\n};\n\ntype RequiredAgentMethods<T> = Omit<\n AgentMethods<T>,\n keyof OptionalAgentMethods<T>\n>;\n\ntype AgentPromiseReturnType<T, K extends keyof AgentMethods<T>> =\n // biome-ignore lint: suppressions/parse\n ReturnType<AgentMethods<T>[K]> extends Promise<any>\n ? ReturnType<AgentMethods<T>[K]>\n : Promise<ReturnType<AgentMethods<T>[K]>>;\n\ntype OptionalArgsAgentMethodCall<AgentT> = <\n K extends keyof OptionalAgentMethods<AgentT>\n>(\n method: K,\n args?: Parameters<OptionalAgentMethods<AgentT>[K]>,\n streamOptions?: StreamOptions\n) => AgentPromiseReturnType<AgentT, K>;\n\ntype RequiredArgsAgentMethodCall<AgentT> = <\n K extends keyof RequiredAgentMethods<AgentT>\n>(\n method: K,\n args: Parameters<RequiredAgentMethods<AgentT>[K]>,\n streamOptions?: StreamOptions\n) => AgentPromiseReturnType<AgentT, K>;\n\ntype AgentMethodCall<AgentT> = OptionalArgsAgentMethodCall<AgentT> &\n RequiredArgsAgentMethodCall<AgentT>;\n\ntype UntypedAgentMethodCall = <T = unknown>(\n method: string,\n args?: unknown[],\n streamOptions?: StreamOptions\n) => Promise<T>;\n\ntype AgentStub<T> = {\n [K in keyof AgentMethods<T>]: (\n ...args: Parameters<AgentMethods<T>[K]>\n ) => AgentPromiseReturnType<AgentMethods<T>, K>;\n};\n\n// we neet to use Method instead of RPCMethod here for retro-compatibility\ntype UntypedAgentStub = Record<string, Method>;\n\n/**\n * React hook for connecting to an Agent\n */\nexport function useAgent<State = unknown>(\n options: UseAgentOptions<State>\n): PartySocket & {\n agent: string;\n name: string;\n identified: boolean;\n ready: Promise<void>;\n setState: (state: State) => void;\n call: UntypedAgentMethodCall;\n stub: UntypedAgentStub;\n};\nexport function useAgent<\n AgentT extends {\n get state(): State;\n },\n State\n>(\n options: UseAgentOptions<State>\n): PartySocket & {\n agent: string;\n name: string;\n identified: boolean;\n ready: Promise<void>;\n setState: (state: State) => void;\n call: AgentMethodCall<AgentT>;\n stub: AgentStub<AgentT>;\n};\nexport function useAgent<State>(\n options: UseAgentOptions<unknown>\n): PartySocket & {\n agent: string;\n name: string;\n identified: boolean;\n ready: Promise<void>;\n setState: (state: State) => void;\n call: UntypedAgentMethodCall | AgentMethodCall<unknown>;\n stub: UntypedAgentStub;\n} {\n const agentNamespace = camelCaseToKebabCase(options.agent);\n const { query, queryDeps, cacheTtl, ...restOptions } = options;\n\n // Keep track of pending RPC calls\n const pendingCallsRef = useRef(\n new Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n stream?: StreamOptions;\n }\n >()\n );\n\n const cacheKey = useMemo(\n () => createCacheKey(agentNamespace, options.name, queryDeps || []),\n [agentNamespace, options.name, queryDeps]\n );\n\n const ttl = cacheTtl ?? 5 * 60 * 1000;\n\n // Track cache invalidation to force re-render when TTL expires\n const [cacheInvalidatedAt, setCacheInvalidatedAt] = useState<number>(0);\n\n // Get or create the query promise\n // biome-ignore lint/correctness/useExhaustiveDependencies: cacheInvalidatedAt intentionally forces re-evaluation when TTL expires\n const queryPromise = useMemo(() => {\n if (!query || typeof query !== \"function\") {\n return null;\n }\n\n // Always check cache first to deduplicate concurrent requests\n const cached = getCacheEntry(cacheKey);\n if (cached) {\n return cached.promise;\n }\n\n // Create new promise\n const promise = query().catch((error) => {\n console.error(\n `[useAgent] Query failed for agent \"${options.agent}\":`,\n error\n );\n deleteCacheEntry(cacheKey);\n throw error;\n });\n\n // Always cache to deduplicate concurrent requests\n setCacheEntry(cacheKey, promise, ttl);\n\n return promise;\n }, [cacheKey, query, options.agent, ttl, cacheInvalidatedAt]);\n\n // Schedule cache invalidation when TTL expires\n useEffect(() => {\n if (!queryPromise || ttl <= 0) return;\n\n const entry = getCacheEntry(cacheKey);\n if (!entry) return;\n\n const timeUntilExpiry = entry.expiresAt - Date.now();\n\n // Always set a timer (with min 0ms) to ensure cleanup function is returned\n const timer = setTimeout(\n () => {\n deleteCacheEntry(cacheKey);\n setCacheInvalidatedAt(Date.now());\n },\n Math.max(0, timeUntilExpiry)\n );\n\n return () => clearTimeout(timer);\n }, [cacheKey, queryPromise, ttl]);\n\n let resolvedQuery: QueryObject | undefined;\n\n if (query) {\n if (typeof query === \"function\") {\n // Use React's use() to resolve the promise\n const queryResult = use(queryPromise!);\n\n // Check for non-primitive values and warn\n if (queryResult) {\n for (const [key, value] of Object.entries(queryResult)) {\n if (\n value !== null &&\n value !== undefined &&\n typeof value !== \"string\" &&\n typeof value !== \"number\" &&\n typeof value !== \"boolean\"\n ) {\n console.warn(\n `[useAgent] Query parameter \"${key}\" is an object and will be converted to \"[object Object]\". ` +\n \"Query parameters should be string, number, boolean, or null.\"\n );\n }\n }\n resolvedQuery = queryResult;\n }\n } else {\n // Sync query - use directly\n resolvedQuery = query;\n }\n }\n\n // Store identity in React state for reactivity\n const [identity, setIdentity] = useState({\n name: options.name || \"default\",\n agent: agentNamespace,\n identified: false\n });\n\n // Track previous identity for change detection\n const previousIdentityRef = useRef<{\n name: string | null;\n agent: string | null;\n }>({ name: null, agent: null });\n\n // Ready promise - resolves when identity is received, resets on close\n const readyRef = useRef<\n { promise: Promise<void>; resolve: () => void } | undefined\n >(undefined);\n\n const resetReady = () => {\n let resolve: () => void;\n const promise = new Promise<void>((r) => {\n resolve = r;\n });\n readyRef.current = { promise, resolve: resolve! };\n };\n\n if (!readyRef.current) {\n resetReady();\n }\n\n // If basePath is provided, use it directly; otherwise construct from agent/name\n const socketOptions = options.basePath\n ? {\n basePath: options.basePath,\n path: options.path,\n query: resolvedQuery,\n ...restOptions\n }\n : {\n party: agentNamespace,\n prefix: \"agents\",\n room: options.name || \"default\",\n path: options.path,\n query: resolvedQuery,\n ...restOptions\n };\n\n const agent = usePartySocket({\n ...socketOptions,\n onMessage: (message) => {\n if (typeof message.data === \"string\") {\n let parsedMessage: Record<string, unknown>;\n try {\n parsedMessage = JSON.parse(message.data);\n } catch (_error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return options.onMessage?.(message);\n }\n if (parsedMessage.type === MessageType.CF_AGENT_IDENTITY) {\n const oldName = previousIdentityRef.current.name;\n const oldAgent = previousIdentityRef.current.agent;\n const newName = parsedMessage.name as string;\n const newAgent = parsedMessage.agent as string;\n\n // Update reactive state (triggers re-render)\n setIdentity({ name: newName, agent: newAgent, identified: true });\n\n // Resolve ready promise\n readyRef.current?.resolve();\n\n // Detect identity change on reconnect\n if (\n oldName !== null &&\n oldAgent !== null &&\n (oldName !== newName || oldAgent !== newAgent)\n ) {\n if (options.onIdentityChange) {\n options.onIdentityChange(oldName, newName, oldAgent, newAgent);\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 // Track for next change detection\n previousIdentityRef.current = { name: newName, agent: newAgent };\n\n // Call onIdentity callback\n options.onIdentity?.(newName, newAgent);\n return;\n }\n if (parsedMessage.type === MessageType.CF_AGENT_STATE) {\n options.onStateUpdate?.(parsedMessage.state as State, \"server\");\n return;\n }\n if (parsedMessage.type === MessageType.CF_AGENT_MCP_SERVERS) {\n options.onMcpUpdate?.(parsedMessage.mcp as MCPServersState);\n return;\n }\n if (parsedMessage.type === MessageType.RPC) {\n const response = parsedMessage as RPCResponse;\n const pending = pendingCallsRef.current.get(response.id);\n if (!pending) return;\n\n if (!response.success) {\n pending.reject(new Error(response.error));\n pendingCallsRef.current.delete(response.id);\n pending.stream?.onError?.(response.error);\n return;\n }\n\n // Handle streaming responses\n if (\"done\" in response) {\n if (response.done) {\n pending.resolve(response.result);\n pendingCallsRef.current.delete(response.id);\n pending.stream?.onDone?.(response.result);\n } else {\n pending.stream?.onChunk?.(response.result);\n }\n } else {\n // Non-streaming response\n pending.resolve(response.result);\n pendingCallsRef.current.delete(response.id);\n }\n return;\n }\n }\n options.onMessage?.(message);\n },\n onClose: (event: CloseEvent) => {\n // Reset ready state for next connection\n resetReady();\n setIdentity((prev) => ({ ...prev, identified: false }));\n\n // Reject all pending calls (consistent with AgentClient behavior)\n const error = new Error(\"Connection closed\");\n for (const pending of pendingCallsRef.current.values()) {\n pending.reject(error);\n pending.stream?.onError?.(\"Connection closed\");\n }\n pendingCallsRef.current.clear();\n\n // Call user's onClose if provided\n options.onClose?.(event);\n }\n }) as PartySocket & {\n agent: string;\n name: string;\n identified: boolean;\n ready: Promise<void>;\n setState: (state: State) => void;\n call: UntypedAgentMethodCall;\n stub: UntypedAgentStub;\n };\n // Create the call method\n const call = useCallback(\n <T = unknown,>(\n method: string,\n args: unknown[] = [],\n streamOptions?: StreamOptions\n ): Promise<T> => {\n return new Promise((resolve, reject) => {\n const id = crypto.randomUUID();\n pendingCallsRef.current.set(id, {\n reject,\n resolve: resolve as (value: unknown) => void,\n stream: streamOptions\n });\n\n const request: RPCRequest = {\n args,\n id,\n method,\n type: MessageType.RPC\n };\n\n agent.send(JSON.stringify(request));\n });\n },\n [agent]\n );\n\n agent.setState = (state: State) => {\n agent.send(JSON.stringify({ state, type: MessageType.CF_AGENT_STATE }));\n options.onStateUpdate?.(state, \"client\");\n };\n\n agent.call = call;\n // Use reactive identity state (updates on identity message)\n agent.agent = identity.agent;\n agent.name = identity.name;\n agent.identified = identity.identified;\n agent.ready = readyRef.current!.promise;\n agent.stub = createStubProxy(call);\n\n // warn if agent isn't in lowercase\n if (identity.agent !== identity.agent.toLowerCase()) {\n console.warn(\n \"Agent name: \" +\n identity.agent +\n \" should probably be in lowercase. Received: \" +\n identity.agent\n );\n }\n\n return agent;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"react.js","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import type { PartySocket } from \"partysocket\";\nimport { usePartySocket } from \"partysocket/react\";\nimport { useCallback, useRef, use, useMemo, useState, useEffect } from \"react\";\nimport type { Agent, MCPServersState, RPCRequest, RPCResponse } from \"./\";\nimport type { StreamOptions } from \"./client\";\nimport { camelCaseToKebabCase } from \"./utils\";\nimport type { Method, RPCMethod } from \"./serializable\";\nimport { MessageType } from \"./types\";\n\ntype QueryObject = Record<string, string | null>;\n\ninterface CacheEntry {\n promise: Promise<QueryObject>;\n expiresAt: number;\n}\n\nconst queryCache = new Map<string, CacheEntry>();\n\nfunction createCacheKey(\n agentNamespace: string,\n name: string | undefined,\n deps: unknown[]\n): string {\n return JSON.stringify([agentNamespace, name || \"default\", ...deps]);\n}\n\nfunction getCacheEntry(key: string): CacheEntry | undefined {\n const entry = queryCache.get(key);\n if (!entry) return undefined;\n\n if (Date.now() >= entry.expiresAt) {\n queryCache.delete(key);\n return undefined;\n }\n\n return entry;\n}\n\nfunction setCacheEntry(\n key: string,\n promise: Promise<QueryObject>,\n cacheTtl: number\n): CacheEntry {\n const entry: CacheEntry = {\n promise,\n expiresAt: Date.now() + cacheTtl\n };\n queryCache.set(key, entry);\n return entry;\n}\n\nfunction deleteCacheEntry(key: string): void {\n queryCache.delete(key);\n}\n\n/**\n * Creates a proxy that wraps RPC method calls.\n * Internal JS methods (toJSON, then, etc.) return undefined to avoid\n * triggering RPC calls during serialization (e.g., console.log)\n */\nfunction createStubProxy<T = Record<string, Method>>(\n call: (method: string, args: unknown[]) => unknown\n): T {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any -- proxy needs any for dynamic method access\n return new Proxy<any>(\n {},\n {\n get: (_target, method) => {\n // Skip internal JavaScript methods that shouldn't trigger RPC calls.\n // These are commonly accessed by console.log, JSON.stringify, and other\n // serialization utilities.\n if (\n typeof method === \"symbol\" ||\n method === \"toJSON\" ||\n method === \"then\" ||\n method === \"catch\" ||\n method === \"finally\" ||\n method === \"valueOf\" ||\n method === \"toString\" ||\n method === \"constructor\" ||\n method === \"prototype\" ||\n method === \"$$typeof\" ||\n method === \"@@toStringTag\" ||\n method === \"asymmetricMatch\" ||\n method === \"nodeType\"\n ) {\n return undefined;\n }\n return (...args: unknown[]) => call(method as string, args);\n }\n }\n );\n}\n\n// Export for testing purposes\nexport const _testUtils = {\n queryCache,\n setCacheEntry,\n getCacheEntry,\n deleteCacheEntry,\n clearCache: () => queryCache.clear(),\n createStubProxy,\n createCacheKey\n};\n\n/**\n * Options for the useAgent hook\n * @template State Type of the Agent's state\n */\nexport type UseAgentOptions<State = unknown> = Omit<\n Parameters<typeof usePartySocket>[0],\n \"party\" | \"room\" | \"query\"\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 /** Query parameters - can be static object or async function */\n query?: QueryObject | (() => Promise<QueryObject>);\n /** Dependencies for async query caching */\n queryDeps?: unknown[];\n /** Cache TTL in milliseconds for auth tokens/time-sensitive data */\n cacheTtl?: number;\n /** Called when the Agent's state is updated */\n onStateUpdate?: (state: State, source: \"server\" | \"client\") => void;\n /** Called when a state update fails (e.g., connection is readonly) */\n onStateUpdateError?: (error: string) => void;\n /** Called when MCP server state is updated */\n onMcpUpdate?: (mcpServers: MCPServersState) => 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\ntype AllOptional<T> = T extends [infer A, ...infer R]\n ? undefined extends A\n ? AllOptional<R>\n : false\n : true; // no params means optional by default\n\ntype RPCMethods<T> = {\n [K in keyof T as T[K] extends RPCMethod<T[K]> ? K : never]: RPCMethod<T[K]>;\n};\n\ntype OptionalParametersMethod<T extends RPCMethod> =\n AllOptional<Parameters<T>> extends true ? T : never;\n\n// all methods of the Agent, excluding the ones that are declared in the base Agent class\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any -- generic agent type constraint\ntype AgentMethods<T> = Omit<RPCMethods<T>, keyof Agent<any, any>>;\n\ntype OptionalAgentMethods<T> = {\n [K in keyof AgentMethods<T> as AgentMethods<T>[K] extends OptionalParametersMethod<\n AgentMethods<T>[K]\n >\n ? K\n : never]: OptionalParametersMethod<AgentMethods<T>[K]>;\n};\n\ntype RequiredAgentMethods<T> = Omit<\n AgentMethods<T>,\n keyof OptionalAgentMethods<T>\n>;\n\ntype AgentPromiseReturnType<T, K extends keyof AgentMethods<T>> =\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any -- generic promise return type\n ReturnType<AgentMethods<T>[K]> extends Promise<any>\n ? ReturnType<AgentMethods<T>[K]>\n : Promise<ReturnType<AgentMethods<T>[K]>>;\n\ntype OptionalArgsAgentMethodCall<AgentT> = <\n K extends keyof OptionalAgentMethods<AgentT>\n>(\n method: K,\n args?: Parameters<OptionalAgentMethods<AgentT>[K]>,\n streamOptions?: StreamOptions\n) => AgentPromiseReturnType<AgentT, K>;\n\ntype RequiredArgsAgentMethodCall<AgentT> = <\n K extends keyof RequiredAgentMethods<AgentT>\n>(\n method: K,\n args: Parameters<RequiredAgentMethods<AgentT>[K]>,\n streamOptions?: StreamOptions\n) => AgentPromiseReturnType<AgentT, K>;\n\ntype AgentMethodCall<AgentT> = OptionalArgsAgentMethodCall<AgentT> &\n RequiredArgsAgentMethodCall<AgentT>;\n\ntype UntypedAgentMethodCall = <T = unknown>(\n method: string,\n args?: unknown[],\n streamOptions?: StreamOptions\n) => Promise<T>;\n\ntype AgentStub<T> = {\n [K in keyof AgentMethods<T>]: (\n ...args: Parameters<AgentMethods<T>[K]>\n ) => AgentPromiseReturnType<AgentMethods<T>, K>;\n};\n\n// we neet to use Method instead of RPCMethod here for retro-compatibility\ntype UntypedAgentStub = Record<string, Method>;\n\n/**\n * React hook for connecting to an Agent\n */\nexport function useAgent<State = unknown>(\n options: UseAgentOptions<State>\n): PartySocket & {\n agent: string;\n name: string;\n identified: boolean;\n ready: Promise<void>;\n setState: (state: State) => void;\n call: UntypedAgentMethodCall;\n stub: UntypedAgentStub;\n};\nexport function useAgent<\n AgentT extends {\n get state(): State;\n },\n State\n>(\n options: UseAgentOptions<State>\n): PartySocket & {\n agent: string;\n name: string;\n identified: boolean;\n ready: Promise<void>;\n setState: (state: State) => void;\n call: AgentMethodCall<AgentT>;\n stub: AgentStub<AgentT>;\n};\nexport function useAgent<State>(\n options: UseAgentOptions<unknown>\n): PartySocket & {\n agent: string;\n name: string;\n identified: boolean;\n ready: Promise<void>;\n setState: (state: State) => void;\n call: UntypedAgentMethodCall | AgentMethodCall<unknown>;\n stub: UntypedAgentStub;\n} {\n const agentNamespace = camelCaseToKebabCase(options.agent);\n const { query, queryDeps, cacheTtl, ...restOptions } = options;\n\n // Keep track of pending RPC calls\n const pendingCallsRef = useRef(\n new Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n stream?: StreamOptions;\n }\n >()\n );\n\n const cacheKey = useMemo(\n () => createCacheKey(agentNamespace, options.name, queryDeps || []),\n [agentNamespace, options.name, queryDeps]\n );\n\n // Track current cache key in a ref for use in onClose handler.\n // This ensures we invalidate the correct cache entry when the connection closes,\n // even if the component re-renders with different props before onClose fires.\n // We update synchronously during render (not in useEffect) to avoid race\n // conditions where onClose could fire before the effect runs.\n const cacheKeyRef = useRef(cacheKey);\n cacheKeyRef.current = cacheKey;\n\n const ttl = cacheTtl ?? 5 * 60 * 1000;\n\n // Track cache invalidation to force re-render when TTL expires\n const [cacheInvalidatedAt, setCacheInvalidatedAt] = useState<number>(0);\n\n // Disable socket while waiting for async query to refresh after disconnect\n const isAsyncQuery = query && typeof query === \"function\";\n const [awaitingQueryRefresh, setAwaitingQueryRefresh] = useState(false);\n\n // Get or create the query promise\n const queryPromise = useMemo(() => {\n if (!query || typeof query !== \"function\") {\n return null;\n }\n\n // Always check cache first to deduplicate concurrent requests\n const cached = getCacheEntry(cacheKey);\n if (cached) {\n return cached.promise;\n }\n\n // Create new promise\n const promise = query().catch((error) => {\n console.error(\n `[useAgent] Query failed for agent \"${options.agent}\":`,\n error\n );\n deleteCacheEntry(cacheKey);\n throw error;\n });\n\n // Always cache to deduplicate concurrent requests\n setCacheEntry(cacheKey, promise, ttl);\n\n return promise;\n }, [cacheKey, query, options.agent, ttl, cacheInvalidatedAt]);\n\n // Schedule cache invalidation when TTL expires\n useEffect(() => {\n if (!queryPromise || ttl <= 0) return;\n\n const entry = getCacheEntry(cacheKey);\n if (!entry) return;\n\n const timeUntilExpiry = entry.expiresAt - Date.now();\n\n // Always set a timer (with min 0ms) to ensure cleanup function is returned\n const timer = setTimeout(\n () => {\n deleteCacheEntry(cacheKey);\n setCacheInvalidatedAt(Date.now());\n },\n Math.max(0, timeUntilExpiry)\n );\n\n return () => clearTimeout(timer);\n }, [cacheKey, queryPromise, ttl]);\n\n let resolvedQuery: QueryObject | undefined;\n\n if (query) {\n if (typeof query === \"function\") {\n // Use React's use() to resolve the promise\n const queryResult = use(queryPromise!);\n\n // Check for non-primitive values and warn\n if (queryResult) {\n for (const [key, value] of Object.entries(queryResult)) {\n if (\n value !== null &&\n value !== undefined &&\n typeof value !== \"string\" &&\n typeof value !== \"number\" &&\n typeof value !== \"boolean\"\n ) {\n console.warn(\n `[useAgent] Query parameter \"${key}\" is an object and will be converted to \"[object Object]\". ` +\n \"Query parameters should be string, number, boolean, or null.\"\n );\n }\n }\n resolvedQuery = queryResult;\n }\n } else {\n // Sync query - use directly\n resolvedQuery = query;\n }\n }\n\n // Re-enable socket after async query resolves\n useEffect(() => {\n if (awaitingQueryRefresh && resolvedQuery !== undefined) {\n setAwaitingQueryRefresh(false);\n }\n }, [awaitingQueryRefresh, resolvedQuery]);\n\n // Store identity in React state for reactivity\n const [identity, setIdentity] = useState({\n name: options.name || \"default\",\n agent: agentNamespace,\n identified: false\n });\n\n // Track previous identity for change detection\n const previousIdentityRef = useRef<{\n name: string | null;\n agent: string | null;\n }>({ name: null, agent: null });\n\n // Ready promise - resolves when identity is received, resets on close\n const readyRef = useRef<\n { promise: Promise<void>; resolve: () => void } | undefined\n >(undefined);\n\n const resetReady = () => {\n let resolve: () => void;\n const promise = new Promise<void>((r) => {\n resolve = r;\n });\n readyRef.current = { promise, resolve: resolve! };\n };\n\n if (!readyRef.current) {\n resetReady();\n }\n\n // If basePath is provided, use it directly; otherwise construct from agent/name\n const socketOptions = options.basePath\n ? {\n basePath: options.basePath,\n path: options.path,\n query: resolvedQuery,\n ...restOptions\n }\n : {\n party: agentNamespace,\n prefix: \"agents\",\n room: options.name || \"default\",\n path: options.path,\n query: resolvedQuery,\n ...restOptions\n };\n\n const socketEnabled = !awaitingQueryRefresh && (restOptions.enabled ?? true);\n\n const agent = usePartySocket({\n ...socketOptions,\n enabled: socketEnabled,\n onMessage: (message) => {\n if (typeof message.data === \"string\") {\n let parsedMessage: Record<string, unknown>;\n try {\n parsedMessage = JSON.parse(message.data);\n } catch (_error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return options.onMessage?.(message);\n }\n if (parsedMessage.type === MessageType.CF_AGENT_IDENTITY) {\n const oldName = previousIdentityRef.current.name;\n const oldAgent = previousIdentityRef.current.agent;\n const newName = parsedMessage.name as string;\n const newAgent = parsedMessage.agent as string;\n\n // Update reactive state (triggers re-render)\n setIdentity({ name: newName, agent: newAgent, identified: true });\n\n // Resolve ready promise\n readyRef.current?.resolve();\n\n // Detect identity change on reconnect\n if (\n oldName !== null &&\n oldAgent !== null &&\n (oldName !== newName || oldAgent !== newAgent)\n ) {\n if (options.onIdentityChange) {\n options.onIdentityChange(oldName, newName, oldAgent, newAgent);\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 // Track for next change detection\n previousIdentityRef.current = { name: newName, agent: newAgent };\n\n // Call onIdentity callback\n options.onIdentity?.(newName, newAgent);\n return;\n }\n if (parsedMessage.type === MessageType.CF_AGENT_STATE) {\n options.onStateUpdate?.(parsedMessage.state as State, \"server\");\n return;\n }\n if (parsedMessage.type === MessageType.CF_AGENT_STATE_ERROR) {\n options.onStateUpdateError?.(parsedMessage.error as string);\n return;\n }\n if (parsedMessage.type === MessageType.CF_AGENT_MCP_SERVERS) {\n options.onMcpUpdate?.(parsedMessage.mcp as MCPServersState);\n return;\n }\n if (parsedMessage.type === MessageType.RPC) {\n const response = parsedMessage as RPCResponse;\n const pending = pendingCallsRef.current.get(response.id);\n if (!pending) return;\n\n if (!response.success) {\n pending.reject(new Error(response.error));\n pendingCallsRef.current.delete(response.id);\n pending.stream?.onError?.(response.error);\n return;\n }\n\n // Handle streaming responses\n if (\"done\" in response) {\n if (response.done) {\n pending.resolve(response.result);\n pendingCallsRef.current.delete(response.id);\n pending.stream?.onDone?.(response.result);\n } else {\n pending.stream?.onChunk?.(response.result);\n }\n } else {\n // Non-streaming response\n pending.resolve(response.result);\n pendingCallsRef.current.delete(response.id);\n }\n return;\n }\n }\n options.onMessage?.(message);\n },\n onClose: (event: CloseEvent) => {\n // Reset ready state for next connection\n resetReady();\n setIdentity((prev) => ({ ...prev, identified: false }));\n\n // Pause reconnection for async queries until fresh query params are ready\n if (isAsyncQuery) {\n setAwaitingQueryRefresh(true);\n }\n\n // Invalidate cache and trigger re-render to fetch fresh query params\n deleteCacheEntry(cacheKeyRef.current);\n setCacheInvalidatedAt(Date.now());\n\n // Reject all pending calls (consistent with AgentClient behavior)\n const error = new Error(\"Connection closed\");\n for (const pending of pendingCallsRef.current.values()) {\n pending.reject(error);\n pending.stream?.onError?.(\"Connection closed\");\n }\n pendingCallsRef.current.clear();\n\n // Call user's onClose if provided\n options.onClose?.(event);\n }\n }) as PartySocket & {\n agent: string;\n name: string;\n identified: boolean;\n ready: Promise<void>;\n setState: (state: State) => void;\n call: UntypedAgentMethodCall;\n stub: UntypedAgentStub;\n };\n // Create the call method\n const call = useCallback(\n <T = unknown>(\n method: string,\n args: unknown[] = [],\n streamOptions?: StreamOptions\n ): Promise<T> => {\n return new Promise((resolve, reject) => {\n const id = crypto.randomUUID();\n pendingCallsRef.current.set(id, {\n reject,\n resolve: resolve as (value: unknown) => void,\n stream: streamOptions\n });\n\n const request: RPCRequest = {\n args,\n id,\n method,\n type: MessageType.RPC\n };\n\n agent.send(JSON.stringify(request));\n });\n },\n [agent]\n );\n\n agent.setState = (state: State) => {\n agent.send(JSON.stringify({ state, type: MessageType.CF_AGENT_STATE }));\n options.onStateUpdate?.(state, \"client\");\n };\n\n agent.call = call;\n // Use reactive identity state (updates on identity message)\n agent.agent = identity.agent;\n agent.name = identity.name;\n agent.identified = identity.identified;\n agent.ready = readyRef.current!.promise;\n agent.stub = createStubProxy(call);\n\n // warn if agent isn't in lowercase\n if (identity.agent !== identity.agent.toLowerCase()) {\n console.warn(\n \"Agent name: \" +\n identity.agent +\n \" should probably be in lowercase. Received: \" +\n identity.agent\n );\n }\n\n return agent;\n}\n"],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Enum for message types to improve type safety and maintainability
|
|
4
|
+
*/
|
|
5
|
+
declare enum MessageType {
|
|
6
|
+
CF_AGENT_MCP_SERVERS = "cf_agent_mcp_servers",
|
|
7
|
+
CF_MCP_AGENT_EVENT = "cf_mcp_agent_event",
|
|
8
|
+
CF_AGENT_STATE = "cf_agent_state",
|
|
9
|
+
CF_AGENT_STATE_ERROR = "cf_agent_state_error",
|
|
10
|
+
CF_AGENT_IDENTITY = "cf_agent_identity",
|
|
11
|
+
RPC = "rpc"
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
2
14
|
export { MessageType };
|
|
15
|
+
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.js
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/types.ts
|
|
2
|
+
/**
|
|
3
|
+
* Enum for message types to improve type safety and maintainability
|
|
4
|
+
*/
|
|
5
|
+
let MessageType = /* @__PURE__ */ function(MessageType) {
|
|
6
|
+
MessageType["CF_AGENT_MCP_SERVERS"] = "cf_agent_mcp_servers";
|
|
7
|
+
MessageType["CF_MCP_AGENT_EVENT"] = "cf_mcp_agent_event";
|
|
8
|
+
MessageType["CF_AGENT_STATE"] = "cf_agent_state";
|
|
9
|
+
MessageType["CF_AGENT_STATE_ERROR"] = "cf_agent_state_error";
|
|
10
|
+
MessageType["CF_AGENT_IDENTITY"] = "cf_agent_identity";
|
|
11
|
+
MessageType["RPC"] = "rpc";
|
|
12
|
+
return MessageType;
|
|
13
|
+
}({});
|
|
2
14
|
|
|
3
|
-
|
|
15
|
+
//#endregion
|
|
16
|
+
export { MessageType };
|
|
17
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["/**\n * Enum for message types to improve type safety and maintainability\n */\nexport enum MessageType {\n CF_AGENT_MCP_SERVERS = \"cf_agent_mcp_servers\",\n CF_MCP_AGENT_EVENT = \"cf_mcp_agent_event\",\n CF_AGENT_STATE = \"cf_agent_state\",\n CF_AGENT_STATE_ERROR = \"cf_agent_state_error\",\n CF_AGENT_IDENTITY = \"cf_agent_identity\",\n RPC = \"rpc\"\n}\n"],"mappings":";;;;AAGA,IAAY,oDAAL;AACL;AACA;AACA;AACA;AACA;AACA"}
|
package/dist/utils.js
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/utils.ts
|
|
2
|
+
/**
|
|
3
|
+
* Convert a camelCase string to a kebab-case string
|
|
4
|
+
* @param str The string to convert
|
|
5
|
+
* @returns The kebab-case string
|
|
6
|
+
*/
|
|
7
|
+
function camelCaseToKebabCase(str) {
|
|
8
|
+
if (str === str.toUpperCase() && str !== str.toLowerCase()) return str.toLowerCase().replace(/_/g, "-");
|
|
9
|
+
let kebabified = str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
10
|
+
kebabified = kebabified.startsWith("-") ? kebabified.slice(1) : kebabified;
|
|
11
|
+
return kebabified.replace(/_/g, "-").replace(/-$/, "");
|
|
12
|
+
}
|
|
2
13
|
|
|
3
|
-
|
|
14
|
+
//#endregion
|
|
15
|
+
export { camelCaseToKebabCase };
|
|
16
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["/**\n * Convert a camelCase string to a kebab-case string\n * @param str The string to convert\n * @returns The kebab-case string\n */\nexport function camelCaseToKebabCase(str: string): string {\n // If string is all uppercase, convert to lowercase\n if (str === str.toUpperCase() && str !== str.toLowerCase()) {\n return str.toLowerCase().replace(/_/g, \"-\");\n }\n\n // Otherwise handle camelCase to kebab-case\n let kebabified = str.replace(\n /[A-Z]/g,\n (letter) => `-${letter.toLowerCase()}`\n );\n kebabified = kebabified.startsWith(\"-\") ? kebabified.slice(1) : kebabified;\n // Convert any remaining underscores to hyphens and remove trailing -'s\n return kebabified.replace(/_/g, \"-\").replace(/-$/, \"\");\n}\n"],"mappings":";;;;;;AAKA,SAAgB,qBAAqB,KAAqB;AAExD,KAAI,QAAQ,IAAI,aAAa,IAAI,QAAQ,IAAI,aAAa,CACxD,QAAO,IAAI,aAAa,CAAC,QAAQ,MAAM,IAAI;CAI7C,IAAI,aAAa,IAAI,QACnB,WACC,WAAW,IAAI,OAAO,aAAa,GACrC;AACD,cAAa,WAAW,WAAW,IAAI,GAAG,WAAW,MAAM,EAAE,GAAG;AAEhE,QAAO,WAAW,QAAQ,MAAM,IAAI,CAAC,QAAQ,MAAM,GAAG"}
|
package/dist/workflow-types.d.ts
CHANGED
|
@@ -1,27 +1,238 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
2
|
+
WorkflowEvent,
|
|
3
|
+
WorkflowSleepDuration,
|
|
4
|
+
WorkflowStep
|
|
5
|
+
} from "cloudflare:workers";
|
|
6
|
+
|
|
7
|
+
//#region src/workflow-types.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Type alias for WorkflowEvent in AgentWorkflow context.
|
|
10
|
+
* Identical to WorkflowEvent - provided for naming consistency with AgentWorkflowStep.
|
|
11
|
+
*/
|
|
12
|
+
type AgentWorkflowEvent<Params = unknown> = WorkflowEvent<Params>;
|
|
13
|
+
/**
|
|
14
|
+
* Extended WorkflowStep with durable Agent communication methods.
|
|
15
|
+
* All added methods on this interface are durable - they're idempotent and won't
|
|
16
|
+
* repeat on workflow retry.
|
|
17
|
+
*/
|
|
18
|
+
interface AgentWorkflowStep extends WorkflowStep {
|
|
19
|
+
/**
|
|
20
|
+
* Report successful completion to the Agent (durable).
|
|
21
|
+
* Triggers onWorkflowComplete() on the Agent.
|
|
22
|
+
* @param result - Optional result data
|
|
23
|
+
*/
|
|
24
|
+
reportComplete<T = unknown>(result?: T): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Report an error to the Agent (durable).
|
|
27
|
+
* Triggers onWorkflowError() on the Agent.
|
|
28
|
+
* @param error - Error or error message
|
|
29
|
+
*/
|
|
30
|
+
reportError(error: Error | string): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Send a custom event to the Agent (durable).
|
|
33
|
+
* Triggers onWorkflowEvent() on the Agent.
|
|
34
|
+
* @param event - Custom event payload
|
|
35
|
+
*/
|
|
36
|
+
sendEvent<T = unknown>(event: T): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Update the Agent's state entirely (durable).
|
|
39
|
+
* This will replace the Agent's state and broadcast to all connected clients.
|
|
40
|
+
* @param state - New state to set
|
|
41
|
+
*/
|
|
42
|
+
updateAgentState(state: unknown): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Merge partial state into the Agent's existing state (durable).
|
|
45
|
+
* Performs a shallow merge and broadcasts to all connected clients.
|
|
46
|
+
* @param partialState - Partial state to merge
|
|
47
|
+
*/
|
|
48
|
+
mergeAgentState(partialState: Record<string, unknown>): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Reset the Agent's state to its initialState (durable).
|
|
51
|
+
* Broadcasts the reset state to all connected clients.
|
|
52
|
+
*/
|
|
53
|
+
resetAgentState(): Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Internal parameters injected by runWorkflow() to identify the originating Agent
|
|
57
|
+
*/
|
|
58
|
+
type AgentWorkflowInternalParams = {
|
|
59
|
+
/** Name/ID of the Agent that started this workflow */ __agentName: string /** Environment binding name for the Agent's namespace */;
|
|
60
|
+
__agentBinding: string /** Workflow binding name (for callbacks) */;
|
|
61
|
+
__workflowName: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Combined workflow params: user params + internal agent params
|
|
65
|
+
*/
|
|
66
|
+
type AgentWorkflowParams<T = unknown> = T & AgentWorkflowInternalParams;
|
|
67
|
+
/**
|
|
68
|
+
* Workflow callback types for Agent-Workflow communication
|
|
69
|
+
*/
|
|
70
|
+
type WorkflowCallbackType = "progress" | "complete" | "error" | "event";
|
|
71
|
+
/**
|
|
72
|
+
* Base callback structure sent from Workflow to Agent
|
|
73
|
+
*/
|
|
74
|
+
type WorkflowCallbackBase = {
|
|
75
|
+
/** Workflow binding name */ workflowName: string /** ID of the workflow instance */;
|
|
76
|
+
workflowId: string /** Type of callback */;
|
|
77
|
+
type: WorkflowCallbackType /** Timestamp when callback was sent */;
|
|
78
|
+
timestamp: number;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Default progress type - covers common use cases.
|
|
82
|
+
* Developers can define their own progress type for domain-specific needs.
|
|
83
|
+
*/
|
|
84
|
+
type DefaultProgress = {
|
|
85
|
+
/** Current step name */ step?: string /** Step/overall status */;
|
|
86
|
+
status?:
|
|
87
|
+
| "pending"
|
|
88
|
+
| "running"
|
|
89
|
+
| "complete"
|
|
90
|
+
| "error" /** Human-readable message */;
|
|
91
|
+
message?: string /** Progress percentage (0-1) */;
|
|
92
|
+
percent?: number /** Allow additional custom fields */;
|
|
93
|
+
[key: string]: unknown;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Progress callback - reports workflow progress with typed payload
|
|
97
|
+
*/
|
|
98
|
+
type WorkflowProgressCallback<P = DefaultProgress> = WorkflowCallbackBase & {
|
|
99
|
+
type: "progress" /** Typed progress data */;
|
|
100
|
+
progress: P;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Complete callback - workflow finished successfully
|
|
104
|
+
*/
|
|
105
|
+
type WorkflowCompleteCallback = WorkflowCallbackBase & {
|
|
106
|
+
type: "complete" /** Result of the workflow */;
|
|
107
|
+
result?: unknown;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Error callback - workflow encountered an error
|
|
111
|
+
*/
|
|
112
|
+
type WorkflowErrorCallback = WorkflowCallbackBase & {
|
|
113
|
+
type: "error" /** Error message */;
|
|
114
|
+
error: string;
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Event callback - custom event from workflow
|
|
118
|
+
*/
|
|
119
|
+
type WorkflowEventCallback = WorkflowCallbackBase & {
|
|
120
|
+
type: "event" /** Custom event payload */;
|
|
121
|
+
event: unknown;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Union of all callback types
|
|
125
|
+
*/
|
|
126
|
+
type WorkflowCallback<P = DefaultProgress> =
|
|
127
|
+
| WorkflowProgressCallback<P>
|
|
128
|
+
| WorkflowCompleteCallback
|
|
129
|
+
| WorkflowErrorCallback
|
|
130
|
+
| WorkflowEventCallback;
|
|
131
|
+
/**
|
|
132
|
+
* Workflow status values - derived from Cloudflare's InstanceStatus
|
|
133
|
+
*/
|
|
134
|
+
type WorkflowStatus = InstanceStatus["status"];
|
|
135
|
+
/**
|
|
136
|
+
* Row structure for cf_agents_workflows tracking table
|
|
137
|
+
*/
|
|
138
|
+
type WorkflowTrackingRow = {
|
|
139
|
+
/** Internal row ID (UUID) */ id: string /** Cloudflare Workflow instance ID */;
|
|
140
|
+
workflow_id: string /** Workflow binding name */;
|
|
141
|
+
workflow_name: string /** Current workflow status */;
|
|
142
|
+
status: WorkflowStatus /** JSON-serialized metadata for querying */;
|
|
143
|
+
metadata: string | null /** Error name if workflow failed */;
|
|
144
|
+
error_name: string | null /** Error message if workflow failed */;
|
|
145
|
+
error_message: string | null /** Unix timestamp when workflow was created */;
|
|
146
|
+
created_at: number /** Unix timestamp when workflow was last updated */;
|
|
147
|
+
updated_at: number /** Unix timestamp when workflow completed (null if not complete) */;
|
|
148
|
+
completed_at: number | null;
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Options for runWorkflow()
|
|
152
|
+
*/
|
|
153
|
+
type RunWorkflowOptions = {
|
|
154
|
+
/** Custom workflow instance ID (auto-generated if not provided) */ id?: string /** Optional metadata for querying (stored as JSON) */;
|
|
155
|
+
metadata?: Record<
|
|
156
|
+
string,
|
|
157
|
+
unknown
|
|
158
|
+
> /** Agent binding name (auto-detected from class name if not provided) */;
|
|
159
|
+
agentBinding?: string;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Event payload for sendWorkflowEvent()
|
|
163
|
+
*/
|
|
164
|
+
type WorkflowEventPayload = {
|
|
165
|
+
/** Event type name */ type: string /** Event payload data */;
|
|
166
|
+
payload: unknown;
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Parsed workflow tracking info returned by getWorkflow()
|
|
170
|
+
*/
|
|
171
|
+
type WorkflowInfo = {
|
|
172
|
+
/** Internal row ID */ id: string /** Cloudflare Workflow instance ID */;
|
|
173
|
+
workflowId: string /** Workflow binding name */;
|
|
174
|
+
workflowName: string /** Current workflow status */;
|
|
175
|
+
status: WorkflowStatus /** Metadata (parsed from JSON) */;
|
|
176
|
+
metadata: Record<string, unknown> | null /** Error info if workflow failed */;
|
|
177
|
+
error: {
|
|
178
|
+
name: string;
|
|
179
|
+
message: string;
|
|
180
|
+
} | null /** When workflow was created */;
|
|
181
|
+
createdAt: Date /** When workflow was last updated */;
|
|
182
|
+
updatedAt: Date /** When workflow completed (null if not complete) */;
|
|
183
|
+
completedAt: Date | null;
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* Criteria for querying tracked workflows
|
|
187
|
+
*/
|
|
188
|
+
type WorkflowQueryCriteria = {
|
|
189
|
+
/** Filter by status */ status?:
|
|
190
|
+
| WorkflowStatus
|
|
191
|
+
| WorkflowStatus[] /** Filter by workflow binding name */;
|
|
192
|
+
workflowName?: string /** Filter by metadata key-value pairs (exact match) */;
|
|
193
|
+
metadata?: Record<
|
|
194
|
+
string,
|
|
195
|
+
string | number | boolean
|
|
196
|
+
> /** Limit number of results (default 50, max 100) */;
|
|
197
|
+
limit?: number /** Order by created_at */;
|
|
198
|
+
orderBy?:
|
|
199
|
+
| "asc"
|
|
200
|
+
| "desc" /** Cursor for pagination (from previous WorkflowPage.nextCursor) */;
|
|
201
|
+
cursor?: string;
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Paginated result from getWorkflows()
|
|
205
|
+
*/
|
|
206
|
+
type WorkflowPage = {
|
|
207
|
+
/** Workflows for this page */ workflows: WorkflowInfo[] /** Total count of workflows matching the criteria (ignoring pagination) */;
|
|
208
|
+
total: number /** Cursor for next page, or null if no more pages */;
|
|
209
|
+
nextCursor: string | null;
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Standard approval event payload used by approveWorkflow/rejectWorkflow
|
|
213
|
+
*/
|
|
214
|
+
type ApprovalEventPayload = {
|
|
215
|
+
/** Whether the workflow was approved */ approved: boolean /** Optional reason for approval/rejection */;
|
|
216
|
+
reason?: string /** Optional additional metadata */;
|
|
217
|
+
metadata?: Record<string, unknown>;
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* Options for waitForApproval()
|
|
221
|
+
*/
|
|
222
|
+
type WaitForApprovalOptions = {
|
|
223
|
+
/** Step name for waitForEvent (default: "wait-for-approval") */ stepName?: string /** Timeout duration (e.g., "7 days") */;
|
|
224
|
+
timeout?: WorkflowSleepDuration /** Event type to wait for (default: "approval") */;
|
|
225
|
+
eventType?: string;
|
|
226
|
+
};
|
|
227
|
+
/**
|
|
228
|
+
* Error thrown when a workflow is rejected via rejectWorkflow()
|
|
229
|
+
*/
|
|
230
|
+
declare class WorkflowRejectedError extends Error {
|
|
231
|
+
readonly reason?: string | undefined;
|
|
232
|
+
readonly workflowId?: string | undefined;
|
|
233
|
+
constructor(reason?: string | undefined, workflowId?: string | undefined);
|
|
234
|
+
}
|
|
235
|
+
//#endregion
|
|
25
236
|
export {
|
|
26
237
|
AgentWorkflowEvent,
|
|
27
238
|
AgentWorkflowInternalParams,
|
|
@@ -46,3 +257,4 @@ export {
|
|
|
46
257
|
WorkflowStatus,
|
|
47
258
|
WorkflowTrackingRow
|
|
48
259
|
};
|
|
260
|
+
//# sourceMappingURL=workflow-types.d.ts.map
|
package/dist/workflows.d.ts
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import "./email-8ljcpvwV.js";
|
|
3
|
-
import "./client-Ba-xntmv.js";
|
|
1
|
+
import "./client-storage-Cvy5r9FG.js";
|
|
4
2
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} from "./workflow-types
|
|
3
|
+
AgentWorkflowEvent,
|
|
4
|
+
AgentWorkflowParams,
|
|
5
|
+
AgentWorkflowStep,
|
|
6
|
+
ApprovalEventPayload,
|
|
7
|
+
DefaultProgress,
|
|
8
|
+
RunWorkflowOptions,
|
|
9
|
+
WaitForApprovalOptions,
|
|
10
|
+
WorkflowCallback,
|
|
11
|
+
WorkflowCallbackType,
|
|
12
|
+
WorkflowCompleteCallback,
|
|
13
|
+
WorkflowErrorCallback,
|
|
14
|
+
WorkflowEventCallback,
|
|
15
|
+
WorkflowEventPayload,
|
|
16
|
+
WorkflowInfo,
|
|
17
|
+
WorkflowPage,
|
|
18
|
+
WorkflowProgressCallback,
|
|
19
|
+
WorkflowQueryCriteria,
|
|
20
|
+
WorkflowRejectedError,
|
|
21
|
+
WorkflowStatus,
|
|
22
|
+
WorkflowTrackingRow
|
|
23
|
+
} from "./workflow-types.js";
|
|
26
24
|
import { Agent } from "./index.js";
|
|
27
25
|
import { WorkflowEntrypoint } from "cloudflare:workers";
|
|
28
26
|
|
package/dist/workflows.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import "./
|
|
3
|
-
import "./client-BA8NJB6l.js";
|
|
4
|
-
import "./do-oauth-client-provider-DDg8QrEA.js";
|
|
5
|
-
import { o as getAgentByName } from "./src-CqnVUbg1.js";
|
|
1
|
+
import "./client-connection-CGMuV62J.js";
|
|
2
|
+
import { getAgentByName } from "./index.js";
|
|
6
3
|
import { WorkflowRejectedError } from "./workflow-types.js";
|
|
7
4
|
import { WorkflowEntrypoint } from "cloudflare:workers";
|
|
8
5
|
|