agents 0.12.0 → 0.12.2
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 +10 -0
- package/dist/{agent-tool-types-tBGRsPm0.d.ts → agent-tool-types-DSteYkkS.d.ts} +125 -4
- package/dist/agent-tool-types.d.ts +1 -1
- package/dist/{agent-tools-CIO14miM.d.ts → agent-tools-eGTCdVZX.d.ts} +2 -2
- package/dist/agent-tools.d.ts +1 -1
- package/dist/chat/index.d.ts +3 -2
- package/dist/chat/index.js +31 -0
- package/dist/chat/index.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +426 -4
- package/dist/index.js.map +1 -1
- package/dist/mcp/client.d.ts +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +9 -0
- package/dist/react.js.map +1 -1
- package/dist/sub-routing.d.ts +1 -1
- package/dist/sub-routing.js +7 -1
- package/dist/sub-routing.js.map +1 -1
- package/dist/workflows.d.ts +1 -1
- package/package.json +6 -6
package/dist/mcp/client.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as MCPClientManagerOptions, Q as MCPClientManager, at as MCPServerFilter, ct as getNamespacedData, et as MCPClientOAuthCallbackConfig, it as MCPOAuthCallbackResult, nt as MCPConnectionResult, ot as MCPServerOptions, rt as MCPDiscoverResult, st as RegisterServerOptions, tt as MCPClientOAuthResult } from "../agent-tool-types-
|
|
1
|
+
import { $ as MCPClientManagerOptions, Q as MCPClientManager, at as MCPServerFilter, ct as getNamespacedData, et as MCPClientOAuthCallbackConfig, it as MCPOAuthCallbackResult, nt as MCPConnectionResult, ot as MCPServerOptions, rt as MCPDiscoverResult, st as RegisterServerOptions, tt as MCPClientOAuthResult } from "../agent-tool-types-DSteYkkS.js";
|
|
2
2
|
export { MCPClientManager, MCPClientManagerOptions, MCPClientOAuthCallbackConfig, MCPClientOAuthResult, MCPConnectionResult, MCPDiscoverResult, MCPOAuthCallbackResult, MCPServerFilter, MCPServerOptions, RegisterServerOptions, getNamespacedData };
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Ct as TransportState, Dt as StreamableHTTPEdgeClientTransport, Et as SSEEdgeClientTransport, Ot as McpClientOptions, St as getMcpAuthContext, Tt as WorkerTransportOptions, _t as McpAgent, bt as experimental_createMcpHandler, dt as RPCServerTransport, et as MCPClientOAuthCallbackConfig, ft as RPCServerTransportOptions, gt as ElicitResult, ht as ElicitRequestSchema, lt as RPCClientTransport, mt as ElicitRequest, nt as MCPConnectionResult, ot as MCPServerOptions, pt as RPC_DO_PREFIX, rt as MCPDiscoverResult, tt as MCPClientOAuthResult, ut as RPCClientTransportOptions, vt as CreateMcpHandlerOptions, wt as WorkerTransport, xt as McpAuthContext, yt as createMcpHandler } from "../agent-tool-types-
|
|
1
|
+
import { Ct as TransportState, Dt as StreamableHTTPEdgeClientTransport, Et as SSEEdgeClientTransport, Ot as McpClientOptions, St as getMcpAuthContext, Tt as WorkerTransportOptions, _t as McpAgent, bt as experimental_createMcpHandler, dt as RPCServerTransport, et as MCPClientOAuthCallbackConfig, ft as RPCServerTransportOptions, gt as ElicitResult, ht as ElicitRequestSchema, lt as RPCClientTransport, mt as ElicitRequest, nt as MCPConnectionResult, ot as MCPServerOptions, pt as RPC_DO_PREFIX, rt as MCPDiscoverResult, tt as MCPClientOAuthResult, ut as RPCClientTransportOptions, vt as CreateMcpHandlerOptions, wt as WorkerTransport, xt as McpAuthContext, yt as createMcpHandler } from "../agent-tool-types-DSteYkkS.js";
|
|
2
2
|
export { CreateMcpHandlerOptions, ElicitRequest, ElicitRequestSchema, ElicitResult, MCPClientOAuthCallbackConfig, MCPClientOAuthResult, MCPConnectionResult, MCPDiscoverResult, MCPServerOptions, McpAgent, McpAuthContext, McpClientOptions, RPCClientTransport, RPCClientTransportOptions, RPCServerTransport, RPCServerTransportOptions, RPC_DO_PREFIX, SSEEdgeClientTransport, StreamableHTTPEdgeClientTransport, TransportState, WorkerTransport, WorkerTransportOptions, createMcpHandler, experimental_createMcpHandler, getMcpAuthContext };
|
package/dist/react.d.ts
CHANGED
package/dist/react.js
CHANGED
|
@@ -160,6 +160,7 @@ function useAgent(options) {
|
|
|
160
160
|
};
|
|
161
161
|
};
|
|
162
162
|
if (!readyRef.current) resetReady();
|
|
163
|
+
const mutableAgentRef = useRef(null);
|
|
163
164
|
const combinedPath = useMemo(() => buildSubPath(subChain, userPath), [subChain, userPath]);
|
|
164
165
|
const socketOptions = options.basePath ? {
|
|
165
166
|
basePath: options.basePath,
|
|
@@ -191,6 +192,12 @@ function useAgent(options) {
|
|
|
191
192
|
const oldAgent = previousIdentityRef.current.agent;
|
|
192
193
|
const newName = parsedMessage.name;
|
|
193
194
|
const newAgent = parsedMessage.agent;
|
|
195
|
+
const currentAgent = mutableAgentRef.current;
|
|
196
|
+
if (currentAgent) {
|
|
197
|
+
currentAgent.name = newName;
|
|
198
|
+
currentAgent.agent = newAgent;
|
|
199
|
+
currentAgent.identified = true;
|
|
200
|
+
}
|
|
194
201
|
setIdentity({
|
|
195
202
|
name: newName,
|
|
196
203
|
agent: newAgent,
|
|
@@ -253,6 +260,7 @@ function useAgent(options) {
|
|
|
253
260
|
},
|
|
254
261
|
onClose: (event) => {
|
|
255
262
|
resetReady();
|
|
263
|
+
if (mutableAgentRef.current) mutableAgentRef.current.identified = false;
|
|
256
264
|
setIdentity((prev) => ({
|
|
257
265
|
...prev,
|
|
258
266
|
identified: false
|
|
@@ -301,6 +309,7 @@ function useAgent(options) {
|
|
|
301
309
|
agent.identified = identity.identified;
|
|
302
310
|
agent.ready = readyRef.current.promise;
|
|
303
311
|
agent.state = agentState;
|
|
312
|
+
mutableAgentRef.current = agent;
|
|
304
313
|
agent.stub = useMemo(() => createStubProxy(call), [call]);
|
|
305
314
|
agent.getHttpUrl = () => {
|
|
306
315
|
return (agent._url || agent._pkurl || "").replace("ws://", "http://").replace("wss://", "https://");
|
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 { MCPServersState, RPCRequest, RPCResponse } from \"./\";\nimport type {\n AgentPromiseReturnType,\n AgentStub,\n OptionalAgentMethods,\n RequiredAgentMethods,\n StreamOptions,\n UntypedAgentStub\n} from \"./client\";\nimport { createStubProxy } from \"./client\";\nimport { camelCaseToKebabCase } from \"./utils\";\nimport { MessageType } from \"./types\";\nimport {\n applyAgentToolEvent,\n createAgentToolEventState,\n type AgentToolEventMessage,\n type AgentToolEventState,\n type AgentToolRunState\n} from \"./chat/agent-tools\";\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 subChainOrDeps: ReadonlyArray<{ agent: string; name: string }> | unknown[],\n deps?: unknown[]\n): string {\n // Backwards-compatible overload: if called with 3 args, the third\n // argument is `deps` and `subChain` defaults to empty. With 4 args,\n // the third is the sub-chain. This keeps existing callers (and\n // the `_testUtils` surface) working while letting new callers\n // include the nested chain in the cache key.\n //\n // Empty sub-chain must produce the same key as the old 3-arg\n // form, so nested-addressing code can opt-in without invalidating\n // existing caches.\n if (deps === undefined) {\n return JSON.stringify([\n agentNamespace,\n name || \"default\",\n ...(subChainOrDeps as unknown[])\n ]);\n }\n const subChain = subChainOrDeps as ReadonlyArray<{\n agent: string;\n name: string;\n }>;\n if (subChain.length === 0) {\n return JSON.stringify([agentNamespace, name || \"default\", ...deps]);\n }\n return JSON.stringify([\n agentNamespace,\n name || \"default\",\n subChain.map((s) => [s.agent, s.name]),\n ...deps\n ]);\n}\n\n/** Build a URL path tail `/sub/{agent-kebab}/{name}/...` from a sub chain. */\nfunction buildSubPath(\n subChain: ReadonlyArray<{ agent: string; name: string }>,\n extraPath?: string\n): string {\n if (subChain.length === 0) return extraPath ?? \"\";\n const parts = subChain.flatMap((step) => [\n \"sub\",\n camelCaseToKebabCase(step.agent),\n encodeURIComponent(step.name)\n ]);\n const combined = parts.join(\"/\");\n if (extraPath) {\n const trimmed = extraPath.startsWith(\"/\") ? extraPath.slice(1) : extraPath;\n return `${combined}/${trimmed}`;\n }\n return combined;\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// 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 * Connect to a sub-agent (facet) via its parent. Flat array,\n * root-first. Each step addresses one parent↔child hop.\n *\n * The hook's returned `.agent` / `.name` report the **leaf**\n * identity (the deepest entry in `sub`), so downstream hooks\n * like `useAgentChat` see the child they actually talk to.\n * `.path` exposes the full chain for observability, deep links,\n * and reconnect keying.\n *\n * @example\n * ```ts\n * // Two-level nesting: Inbox (Alice) → Chat (abc)\n * useAgent({\n * agent: \"inbox\", name: userId,\n * sub: [{ agent: \"chat\", name: chatId }]\n * });\n *\n * // Three-level: tenant → inbox → chat\n * useAgent({\n * agent: \"tenant\", name: tenantId,\n * sub: [\n * { agent: \"inbox\", name: userId },\n * { agent: \"chat\", name: chatId }\n * ]\n * });\n * ```\n *\n * @experimental The API surface may change before stabilizing.\n */\n sub?: ReadonlyArray<{ agent: string; name: string }>;\n};\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\n/**\n * React hook for connecting to an Agent\n */\nexport function useAgent<State = unknown>(\n options: UseAgentOptions<State>\n): Omit<PartySocket, \"path\"> & {\n agent: string;\n name: string;\n /** Full root-first address chain, including leaf. Single entry when `sub` isn't set. */\n path: ReadonlyArray<{ agent: string; name: string }>;\n identified: boolean;\n ready: Promise<void>;\n state: State | undefined;\n setState: (state: State) => void;\n call: UntypedAgentMethodCall;\n stub: UntypedAgentStub;\n getHttpUrl: () => string;\n};\nexport function useAgent<\n AgentT extends {\n get state(): State;\n },\n State\n>(\n options: UseAgentOptions<State>\n): Omit<PartySocket, \"path\"> & {\n agent: string;\n name: string;\n path: ReadonlyArray<{ agent: string; name: string }>;\n identified: boolean;\n ready: Promise<void>;\n state: State | undefined;\n setState: (state: State) => void;\n call: AgentMethodCall<AgentT>;\n stub: AgentStub<AgentT>;\n getHttpUrl: () => string;\n};\nexport function useAgent<State>(options: UseAgentOptions<unknown>): Omit<\n PartySocket,\n \"path\"\n> & {\n agent: string;\n name: string;\n path: ReadonlyArray<{ agent: string; name: string }>;\n identified: boolean;\n ready: Promise<void>;\n state: State | undefined;\n setState: (state: State) => void;\n call: UntypedAgentMethodCall | AgentMethodCall<unknown>;\n stub: UntypedAgentStub;\n getHttpUrl: () => string;\n} {\n const agentNamespace = camelCaseToKebabCase(options.agent);\n // NOTE: `path` is destructured out (as `userPath`) so it does NOT\n // end up in `restOptions`. Spreading `restOptions` after the\n // computed `path: combinedPath` would otherwise let the user's raw\n // `path` overwrite the combined sub-agent URL, dropping every\n // `/sub/{child}/{name}` segment on the way to the socket.\n const {\n query,\n queryDeps,\n cacheTtl,\n sub: subOption,\n path: userPath,\n ...restOptions\n } = options;\n\n const subChain = useMemo(\n () => (subOption ?? []).map((s) => ({ agent: s.agent, name: s.name })),\n // Stable serialization — deep changes re-memoize.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [JSON.stringify(subOption ?? [])]\n );\n\n // The \"leaf\" is the deepest entry in the chain; it's what\n // downstream code (useAgentChat etc.) should see as the\n // authoritative identity.\n const leafAgent =\n subChain.length > 0 ? subChain[subChain.length - 1].agent : options.agent;\n const leafName =\n subChain.length > 0\n ? subChain[subChain.length - 1].name\n : options.name || \"default\";\n\n // Full root-first chain, including the leaf. Exposed as `.path`\n // and used for cache keying so nested sessions with the same leaf\n // name don't collide.\n const fullPath = useMemo(\n () => [\n { agent: options.agent, name: options.name || \"default\" },\n ...subChain\n ],\n [options.agent, options.name, subChain]\n );\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 () =>\n createCacheKey(agentNamespace, options.name, subChain, queryDeps || []),\n [agentNamespace, options.name, subChain, 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 // Re-run when cache is invalidated after TTL expiry\n void cacheInvalidatedAt;\n\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 // Track agent state for reactivity — updated on server broadcasts and client setState\n const [agentState, setAgentState] = useState<State | undefined>(undefined);\n\n // Store identity in React state for reactivity. Seed with the\n // leaf's address — what the server will echo back in\n // `cf_agent_identity`.\n const [identity, setIdentity] = useState({\n name: leafName,\n agent: camelCaseToKebabCase(leafAgent),\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 // Combine the sub-agent chain with the user-provided `path`.\n // Order matters: `/sub/{child}/{name}/...` comes before `path` so\n // the server sees the hierarchy it expects.\n const combinedPath = useMemo(\n () => buildSubPath(subChain, userPath),\n [subChain, userPath]\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: combinedPath || undefined,\n query: resolvedQuery,\n ...restOptions\n }\n : {\n party: agentNamespace,\n prefix: \"agents\",\n room: options.name || \"default\",\n path: combinedPath || undefined,\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 setAgentState(parsedMessage.state as 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 state: State | undefined;\n setState: (state: State) => void;\n call: UntypedAgentMethodCall;\n stub: UntypedAgentStub;\n getHttpUrl: () => string;\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 = (newState: State) => {\n agent.send(\n JSON.stringify({ state: newState, type: MessageType.CF_AGENT_STATE })\n );\n setAgentState(newState);\n options.onStateUpdate?.(newState, \"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 // Full root-first chain including the leaf. Computed from the\n // user-provided options — the server doesn't need to echo it\n // back because the client already knows. Write past the\n // PartySocket `.path: string` shape via an unknown cast — the\n // overload signatures expose this as `ReadonlyArray<...>`.\n (\n agent as unknown as { path: ReadonlyArray<{ agent: string; name: string }> }\n ).path = fullPath;\n agent.identified = identity.identified;\n agent.ready = readyRef.current!.promise;\n agent.state = agentState;\n // Memoize stub so it's referentially stable across renders\n // (call is already stable via useCallback)\n const stub = useMemo(() => createStubProxy(call), [call]);\n agent.stub = stub;\n agent.getHttpUrl = () => {\n // TODO: upstream to partysocket — expose an HTTP URL property\n // @ts-expect-error accessing protected PartySocket internals\n const wsUrl: string = (agent._url as string | null) || agent._pkurl || \"\";\n return wsUrl.replace(\"ws://\", \"http://\").replace(\"wss://\", \"https://\");\n };\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 // The overload signatures return `Omit<PartySocket, \"path\"> & { path: ... }`,\n // but `agent` is inferred as the raw PartySocket. Cast to satisfy\n // the overload contract — the runtime override of `agent.path`\n // above ensures the shape matches.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return agent as any;\n}\n\ntype AgentToolEventAgent = Pick<\n PartySocket,\n \"addEventListener\" | \"removeEventListener\"\n>;\n\nfunction agentToolDedupeKey(message: AgentToolEventMessage): string {\n return [\n message.parentToolCallId ?? \"\",\n message.event.runId,\n String(message.sequence)\n ].join(\"\\0\");\n}\n\nexport function useAgentToolEvents(options: { agent: AgentToolEventAgent }): {\n runsById: Record<string, AgentToolRunState>;\n runsByToolCallId: Record<string, AgentToolRunState[]>;\n unboundRuns: AgentToolRunState[];\n getRunsForToolCall(toolCallId: string): AgentToolRunState[];\n resetLocalState(): void;\n} {\n const { agent } = options;\n const [state, setState] = useState<AgentToolEventState>(() =>\n createAgentToolEventState()\n );\n const seenRef = useRef(new Set<string>());\n\n useEffect(() => {\n const onMessage = (event: MessageEvent) => {\n if (typeof event.data !== \"string\") return;\n let message: AgentToolEventMessage;\n try {\n message = JSON.parse(event.data) as AgentToolEventMessage;\n } catch {\n return;\n }\n if (message.type !== \"agent-tool-event\") return;\n const key = agentToolDedupeKey(message);\n if (seenRef.current.has(key)) return;\n seenRef.current.add(key);\n setState((prev) => applyAgentToolEvent(prev, message));\n };\n\n agent.addEventListener(\"message\", onMessage);\n return () => agent.removeEventListener(\"message\", onMessage);\n }, [agent]);\n\n const resetLocalState = useCallback(() => {\n seenRef.current.clear();\n setState(createAgentToolEventState());\n }, []);\n\n const getRunsForToolCall = useCallback(\n (toolCallId: string) => state.runsByToolCallId[toolCallId] ?? [],\n [state.runsByToolCallId]\n );\n\n return {\n ...state,\n getRunsForToolCall,\n resetLocalState\n };\n}\n"],"mappings":";;;;;;;AA8BA,MAAM,6BAAa,IAAI,KAAyB;AAEhD,SAAS,eACP,gBACA,MACA,gBACA,MACQ;AAUR,KAAI,SAAS,KAAA,EACX,QAAO,KAAK,UAAU;EACpB;EACA,QAAQ;EACR,GAAI;EACL,CAAC;CAEJ,MAAM,WAAW;AAIjB,KAAI,SAAS,WAAW,EACtB,QAAO,KAAK,UAAU;EAAC;EAAgB,QAAQ;EAAW,GAAG;EAAK,CAAC;AAErE,QAAO,KAAK,UAAU;EACpB;EACA,QAAQ;EACR,SAAS,KAAK,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC;EACtC,GAAG;EACJ,CAAC;;;AAIJ,SAAS,aACP,UACA,WACQ;AACR,KAAI,SAAS,WAAW,EAAG,QAAO,aAAa;CAM/C,MAAM,WALQ,SAAS,SAAS,SAAS;EACvC;EACA,qBAAqB,KAAK,MAAM;EAChC,mBAAmB,KAAK,KAAK;EAC9B,CACqB,CAAC,KAAK,IAAI;AAChC,KAAI,UAEF,QAAO,GAAG,SAAS,GADH,UAAU,WAAW,IAAI,GAAG,UAAU,MAAM,EAAE,GAAG;AAGnE,QAAO;;AAGT,SAAS,cAAc,KAAqC;CAC1D,MAAM,QAAQ,WAAW,IAAI,IAAI;AACjC,KAAI,CAAC,MAAO,QAAO,KAAA;AAEnB,KAAI,KAAK,KAAK,IAAI,MAAM,WAAW;AACjC,aAAW,OAAO,IAAI;AACtB;;AAGF,QAAO;;AAGT,SAAS,cACP,KACA,SACA,UACY;CACZ,MAAM,QAAoB;EACxB;EACA,WAAW,KAAK,KAAK,GAAG;EACzB;AACD,YAAW,IAAI,KAAK,MAAM;AAC1B,QAAO;;AAGT,SAAS,iBAAiB,KAAmB;AAC3C,YAAW,OAAO,IAAI;;AAIxB,MAAa,aAAa;CACxB;CACA;CACA;CACA;CACA,kBAAkB,WAAW,OAAO;CACpC;CACA;CACD;AAiKD,SAAgB,SAAgB,SAc9B;CACA,MAAM,iBAAiB,qBAAqB,QAAQ,MAAM;CAM1D,MAAM,EACJ,OACA,WACA,UACA,KAAK,WACL,MAAM,UACN,GAAG,gBACD;CAEJ,MAAM,WAAW,eACR,aAAa,EAAE,EAAE,KAAK,OAAO;EAAE,OAAO,EAAE;EAAO,MAAM,EAAE;EAAM,EAAE,EAGtE,CAAC,KAAK,UAAU,aAAa,EAAE,CAAC,CAAC,CAClC;CAKD,MAAM,YACJ,SAAS,SAAS,IAAI,SAAS,SAAS,SAAS,GAAG,QAAQ,QAAQ;CACtE,MAAM,WACJ,SAAS,SAAS,IACd,SAAS,SAAS,SAAS,GAAG,OAC9B,QAAQ,QAAQ;CAKtB,MAAM,WAAW,cACT,CACJ;EAAE,OAAO,QAAQ;EAAO,MAAM,QAAQ,QAAQ;EAAW,EACzD,GAAG,SACJ,EACD;EAAC,QAAQ;EAAO,QAAQ;EAAM;EAAS,CACxC;CAGD,MAAM,kBAAkB,uBACtB,IAAI,KAOD,CACJ;CAED,MAAM,WAAW,cAEb,eAAe,gBAAgB,QAAQ,MAAM,UAAU,aAAa,EAAE,CAAC,EACzE;EAAC;EAAgB,QAAQ;EAAM;EAAU;EAAU,CACpD;CAOD,MAAM,cAAc,OAAO,SAAS;AACpC,aAAY,UAAU;CAEtB,MAAM,MAAM,YAAY,MAAS;CAGjC,MAAM,CAAC,oBAAoB,yBAAyB,SAAiB,EAAE;CAGvE,MAAM,eAAe,SAAS,OAAO,UAAU;CAC/C,MAAM,CAAC,sBAAsB,2BAA2B,SAAS,MAAM;CAGvE,MAAM,eAAe,cAAc;AAIjC,MAAI,CAAC,SAAS,OAAO,UAAU,WAC7B,QAAO;EAIT,MAAM,SAAS,cAAc,SAAS;AACtC,MAAI,OACF,QAAO,OAAO;EAIhB,MAAM,UAAU,OAAO,CAAC,OAAO,UAAU;AACvC,WAAQ,MACN,sCAAsC,QAAQ,MAAM,KACpD,MACD;AACD,oBAAiB,SAAS;AAC1B,SAAM;IACN;AAGF,gBAAc,UAAU,SAAS,IAAI;AAErC,SAAO;IACN;EAAC;EAAU;EAAO,QAAQ;EAAO;EAAK;EAAmB,CAAC;AAG7D,iBAAgB;AACd,MAAI,CAAC,gBAAgB,OAAO,EAAG;EAE/B,MAAM,QAAQ,cAAc,SAAS;AACrC,MAAI,CAAC,MAAO;EAEZ,MAAM,kBAAkB,MAAM,YAAY,KAAK,KAAK;EAGpD,MAAM,QAAQ,iBACN;AACJ,oBAAiB,SAAS;AAC1B,yBAAsB,KAAK,KAAK,CAAC;KAEnC,KAAK,IAAI,GAAG,gBAAgB,CAC7B;AAED,eAAa,aAAa,MAAM;IAC/B;EAAC;EAAU;EAAc;EAAI,CAAC;CAEjC,IAAI;AAEJ,KAAI,MACF,KAAI,OAAO,UAAU,YAAY;EAE/B,MAAM,cAAc,IAAI,aAAc;AAGtC,MAAI,aAAa;AACf,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,YAAY,CACpD,KACE,UAAU,QACV,UAAU,KAAA,KACV,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU,UAEjB,SAAQ,KACN,+BAA+B,IAAI,yHAEpC;AAGL,mBAAgB;;OAIlB,iBAAgB;AAKpB,iBAAgB;AACd,MAAI,wBAAwB,kBAAkB,KAAA,EAC5C,yBAAwB,MAAM;IAE/B,CAAC,sBAAsB,cAAc,CAAC;CAGzC,MAAM,CAAC,YAAY,iBAAiB,SAA4B,KAAA,EAAU;CAK1E,MAAM,CAAC,UAAU,eAAe,SAAS;EACvC,MAAM;EACN,OAAO,qBAAqB,UAAU;EACtC,YAAY;EACb,CAAC;CAGF,MAAM,sBAAsB,OAGzB;EAAE,MAAM;EAAM,OAAO;EAAM,CAAC;CAG/B,MAAM,WAAW,OAEf,KAAA,EAAU;CAEZ,MAAM,mBAAmB;EACvB,IAAI;AAIJ,WAAS,UAAU;GAAE,SAAA,IAHD,SAAe,MAAM;AACvC,cAAU;KAEgB;GAAW;GAAU;;AAGnD,KAAI,CAAC,SAAS,QACZ,aAAY;CAMd,MAAM,eAAe,cACb,aAAa,UAAU,SAAS,EACtC,CAAC,UAAU,SAAS,CACrB;CAGD,MAAM,gBAAgB,QAAQ,WAC1B;EACE,UAAU,QAAQ;EAClB,MAAM,gBAAgB,KAAA;EACtB,OAAO;EACP,GAAG;EACJ,GACD;EACE,OAAO;EACP,QAAQ;EACR,MAAM,QAAQ,QAAQ;EACtB,MAAM,gBAAgB,KAAA;EACtB,OAAO;EACP,GAAG;EACJ;CAEL,MAAM,gBAAgB,CAAC,yBAAyB,YAAY,WAAW;CAEvE,MAAM,QAAQ,eAAe;EAC3B,GAAG;EACH,SAAS;EACT,YAAY,YAAY;AACtB,OAAI,OAAO,QAAQ,SAAS,UAAU;IACpC,IAAI;AACJ,QAAI;AACF,qBAAgB,KAAK,MAAM,QAAQ,KAAK;aACjC,QAAQ;AAGf,YAAO,QAAQ,YAAY,QAAQ;;AAErC,QAAI,cAAc,SAAA,qBAAwC;KACxD,MAAM,UAAU,oBAAoB,QAAQ;KAC5C,MAAM,WAAW,oBAAoB,QAAQ;KAC7C,MAAM,UAAU,cAAc;KAC9B,MAAM,WAAW,cAAc;AAG/B,iBAAY;MAAE,MAAM;MAAS,OAAO;MAAU,YAAY;MAAM,CAAC;AAGjE,cAAS,SAAS,SAAS;AAG3B,SACE,YAAY,QACZ,aAAa,SACZ,YAAY,WAAW,aAAa,UAErC,KAAI,QAAQ,iBACV,SAAQ,iBAAiB,SAAS,SAAS,UAAU,SAAS;UACzD;MACL,MAAM,eAAe,aAAa;MAClC,MAAM,cAAc,YAAY;MAChC,IAAI,oBAAoB;AACxB,UAAI,gBAAgB,YAClB,qBAAoB,UAAU,SAAS,OAAO,SAAS,eAAe,QAAQ,OAAO,QAAQ;eACpF,aACT,qBAAoB,UAAU,SAAS,OAAO,SAAS;UAEvD,qBAAoB,aAAa,QAAQ,OAAO,QAAQ;AAE1D,cAAQ,KACN,2CAA2C,kBAAkB,wPAK9D;;AAKL,yBAAoB,UAAU;MAAE,MAAM;MAAS,OAAO;MAAU;AAGhE,aAAQ,aAAa,SAAS,SAAS;AACvC;;AAEF,QAAI,cAAc,SAAA,kBAAqC;AACrD,mBAAc,cAAc,MAAe;AAC3C,aAAQ,gBAAgB,cAAc,OAAgB,SAAS;AAC/D;;AAEF,QAAI,cAAc,SAAA,wBAA2C;AAC3D,aAAQ,qBAAqB,cAAc,MAAgB;AAC3D;;AAEF,QAAI,cAAc,SAAA,wBAA2C;AAC3D,aAAQ,cAAc,cAAc,IAAuB;AAC3D;;AAEF,QAAI,cAAc,SAAA,OAA0B;KAC1C,MAAM,WAAW;KACjB,MAAM,UAAU,gBAAgB,QAAQ,IAAI,SAAS,GAAG;AACxD,SAAI,CAAC,QAAS;AAEd,SAAI,CAAC,SAAS,SAAS;AACrB,cAAQ,OAAO,IAAI,MAAM,SAAS,MAAM,CAAC;AACzC,sBAAgB,QAAQ,OAAO,SAAS,GAAG;AAC3C,cAAQ,QAAQ,UAAU,SAAS,MAAM;AACzC;;AAIF,SAAI,UAAU,SACZ,KAAI,SAAS,MAAM;AACjB,cAAQ,QAAQ,SAAS,OAAO;AAChC,sBAAgB,QAAQ,OAAO,SAAS,GAAG;AAC3C,cAAQ,QAAQ,SAAS,SAAS,OAAO;WAEzC,SAAQ,QAAQ,UAAU,SAAS,OAAO;UAEvC;AAEL,cAAQ,QAAQ,SAAS,OAAO;AAChC,sBAAgB,QAAQ,OAAO,SAAS,GAAG;;AAE7C;;;AAGJ,WAAQ,YAAY,QAAQ;;EAE9B,UAAU,UAAsB;AAE9B,eAAY;AACZ,gBAAa,UAAU;IAAE,GAAG;IAAM,YAAY;IAAO,EAAE;AAGvD,OAAI,aACF,yBAAwB,KAAK;AAI/B,oBAAiB,YAAY,QAAQ;AACrC,yBAAsB,KAAK,KAAK,CAAC;GAGjC,MAAM,wBAAQ,IAAI,MAAM,oBAAoB;AAC5C,QAAK,MAAM,WAAW,gBAAgB,QAAQ,QAAQ,EAAE;AACtD,YAAQ,OAAO,MAAM;AACrB,YAAQ,QAAQ,UAAU,oBAAoB;;AAEhD,mBAAgB,QAAQ,OAAO;AAG/B,WAAQ,UAAU,MAAM;;EAE3B,CAAC;CAYF,MAAM,OAAO,aAET,QACA,OAAkB,EAAE,EACpB,kBACe;AACf,SAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,KAAK,OAAO,YAAY;AAC9B,mBAAgB,QAAQ,IAAI,IAAI;IAC9B;IACS;IACT,QAAQ;IACT,CAAC;GAEF,MAAM,UAAsB;IAC1B;IACA;IACA;IACA,MAAA;IACD;AAED,SAAM,KAAK,KAAK,UAAU,QAAQ,CAAC;IACnC;IAEJ,CAAC,MAAM,CACR;AAED,OAAM,YAAY,aAAoB;AACpC,QAAM,KACJ,KAAK,UAAU;GAAE,OAAO;GAAU,MAAA;GAAkC,CAAC,CACtE;AACD,gBAAc,SAAS;AACvB,UAAQ,gBAAgB,UAAU,SAAS;;AAG7C,OAAM,OAAO;AAEb,OAAM,QAAQ,SAAS;AACvB,OAAM,OAAO,SAAS;AAOpB,OACA,OAAO;AACT,OAAM,aAAa,SAAS;AAC5B,OAAM,QAAQ,SAAS,QAAS;AAChC,OAAM,QAAQ;AAId,OAAM,OADO,cAAc,gBAAgB,KAAK,EAAE,CAAC,KAAK,CACvC;AACjB,OAAM,mBAAmB;AAIvB,UADuB,MAAM,QAA0B,MAAM,UAAU,IAC1D,QAAQ,SAAS,UAAU,CAAC,QAAQ,UAAU,WAAW;;AAIxE,KAAI,SAAS,UAAU,SAAS,MAAM,aAAa,CACjD,SAAQ,KACN,iBACE,SAAS,QACT,iDACA,SAAS,MACZ;AAQH,QAAO;;AAQT,SAAS,mBAAmB,SAAwC;AAClE,QAAO;EACL,QAAQ,oBAAoB;EAC5B,QAAQ,MAAM;EACd,OAAO,QAAQ,SAAS;EACzB,CAAC,KAAK,KAAK;;AAGd,SAAgB,mBAAmB,SAMjC;CACA,MAAM,EAAE,UAAU;CAClB,MAAM,CAAC,OAAO,YAAY,eACxB,2BAA2B,CAC5B;CACD,MAAM,UAAU,uBAAO,IAAI,KAAa,CAAC;AAEzC,iBAAgB;EACd,MAAM,aAAa,UAAwB;AACzC,OAAI,OAAO,MAAM,SAAS,SAAU;GACpC,IAAI;AACJ,OAAI;AACF,cAAU,KAAK,MAAM,MAAM,KAAK;WAC1B;AACN;;AAEF,OAAI,QAAQ,SAAS,mBAAoB;GACzC,MAAM,MAAM,mBAAmB,QAAQ;AACvC,OAAI,QAAQ,QAAQ,IAAI,IAAI,CAAE;AAC9B,WAAQ,QAAQ,IAAI,IAAI;AACxB,aAAU,SAAS,oBAAoB,MAAM,QAAQ,CAAC;;AAGxD,QAAM,iBAAiB,WAAW,UAAU;AAC5C,eAAa,MAAM,oBAAoB,WAAW,UAAU;IAC3D,CAAC,MAAM,CAAC;CAEX,MAAM,kBAAkB,kBAAkB;AACxC,UAAQ,QAAQ,OAAO;AACvB,WAAS,2BAA2B,CAAC;IACpC,EAAE,CAAC;CAEN,MAAM,qBAAqB,aACxB,eAAuB,MAAM,iBAAiB,eAAe,EAAE,EAChE,CAAC,MAAM,iBAAiB,CACzB;AAED,QAAO;EACL,GAAG;EACH;EACA;EACD"}
|
|
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 { MCPServersState, RPCRequest, RPCResponse } from \"./\";\nimport type {\n AgentPromiseReturnType,\n AgentStub,\n OptionalAgentMethods,\n RequiredAgentMethods,\n StreamOptions,\n UntypedAgentStub\n} from \"./client\";\nimport { createStubProxy } from \"./client\";\nimport { camelCaseToKebabCase } from \"./utils\";\nimport { MessageType } from \"./types\";\nimport {\n applyAgentToolEvent,\n createAgentToolEventState,\n type AgentToolEventMessage,\n type AgentToolEventState,\n type AgentToolRunState\n} from \"./chat/agent-tools\";\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 subChainOrDeps: ReadonlyArray<{ agent: string; name: string }> | unknown[],\n deps?: unknown[]\n): string {\n // Backwards-compatible overload: if called with 3 args, the third\n // argument is `deps` and `subChain` defaults to empty. With 4 args,\n // the third is the sub-chain. This keeps existing callers (and\n // the `_testUtils` surface) working while letting new callers\n // include the nested chain in the cache key.\n //\n // Empty sub-chain must produce the same key as the old 3-arg\n // form, so nested-addressing code can opt-in without invalidating\n // existing caches.\n if (deps === undefined) {\n return JSON.stringify([\n agentNamespace,\n name || \"default\",\n ...(subChainOrDeps as unknown[])\n ]);\n }\n const subChain = subChainOrDeps as ReadonlyArray<{\n agent: string;\n name: string;\n }>;\n if (subChain.length === 0) {\n return JSON.stringify([agentNamespace, name || \"default\", ...deps]);\n }\n return JSON.stringify([\n agentNamespace,\n name || \"default\",\n subChain.map((s) => [s.agent, s.name]),\n ...deps\n ]);\n}\n\n/** Build a URL path tail `/sub/{agent-kebab}/{name}/...` from a sub chain. */\nfunction buildSubPath(\n subChain: ReadonlyArray<{ agent: string; name: string }>,\n extraPath?: string\n): string {\n if (subChain.length === 0) return extraPath ?? \"\";\n const parts = subChain.flatMap((step) => [\n \"sub\",\n camelCaseToKebabCase(step.agent),\n encodeURIComponent(step.name)\n ]);\n const combined = parts.join(\"/\");\n if (extraPath) {\n const trimmed = extraPath.startsWith(\"/\") ? extraPath.slice(1) : extraPath;\n return `${combined}/${trimmed}`;\n }\n return combined;\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// 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 * Connect to a sub-agent (facet) via its parent. Flat array,\n * root-first. Each step addresses one parent↔child hop.\n *\n * The hook's returned `.agent` / `.name` report the **leaf**\n * identity (the deepest entry in `sub`), so downstream hooks\n * like `useAgentChat` see the child they actually talk to.\n * `.path` exposes the full chain for observability, deep links,\n * and reconnect keying.\n *\n * @example\n * ```ts\n * // Two-level nesting: Inbox (Alice) → Chat (abc)\n * useAgent({\n * agent: \"inbox\", name: userId,\n * sub: [{ agent: \"chat\", name: chatId }]\n * });\n *\n * // Three-level: tenant → inbox → chat\n * useAgent({\n * agent: \"tenant\", name: tenantId,\n * sub: [\n * { agent: \"inbox\", name: userId },\n * { agent: \"chat\", name: chatId }\n * ]\n * });\n * ```\n *\n * @experimental The API surface may change before stabilizing.\n */\n sub?: ReadonlyArray<{ agent: string; name: string }>;\n};\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\n/**\n * React hook for connecting to an Agent\n */\nexport function useAgent<State = unknown>(\n options: UseAgentOptions<State>\n): Omit<PartySocket, \"path\"> & {\n agent: string;\n name: string;\n /** Full root-first address chain, including leaf. Single entry when `sub` isn't set. */\n path: ReadonlyArray<{ agent: string; name: string }>;\n identified: boolean;\n ready: Promise<void>;\n state: State | undefined;\n setState: (state: State) => void;\n call: UntypedAgentMethodCall;\n stub: UntypedAgentStub;\n getHttpUrl: () => string;\n};\nexport function useAgent<\n AgentT extends {\n get state(): State;\n },\n State\n>(\n options: UseAgentOptions<State>\n): Omit<PartySocket, \"path\"> & {\n agent: string;\n name: string;\n path: ReadonlyArray<{ agent: string; name: string }>;\n identified: boolean;\n ready: Promise<void>;\n state: State | undefined;\n setState: (state: State) => void;\n call: AgentMethodCall<AgentT>;\n stub: AgentStub<AgentT>;\n getHttpUrl: () => string;\n};\nexport function useAgent<State>(options: UseAgentOptions<unknown>): Omit<\n PartySocket,\n \"path\"\n> & {\n agent: string;\n name: string;\n path: ReadonlyArray<{ agent: string; name: string }>;\n identified: boolean;\n ready: Promise<void>;\n state: State | undefined;\n setState: (state: State) => void;\n call: UntypedAgentMethodCall | AgentMethodCall<unknown>;\n stub: UntypedAgentStub;\n getHttpUrl: () => string;\n} {\n const agentNamespace = camelCaseToKebabCase(options.agent);\n // NOTE: `path` is destructured out (as `userPath`) so it does NOT\n // end up in `restOptions`. Spreading `restOptions` after the\n // computed `path: combinedPath` would otherwise let the user's raw\n // `path` overwrite the combined sub-agent URL, dropping every\n // `/sub/{child}/{name}` segment on the way to the socket.\n const {\n query,\n queryDeps,\n cacheTtl,\n sub: subOption,\n path: userPath,\n ...restOptions\n } = options;\n\n const subChain = useMemo(\n () => (subOption ?? []).map((s) => ({ agent: s.agent, name: s.name })),\n // Stable serialization — deep changes re-memoize.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [JSON.stringify(subOption ?? [])]\n );\n\n // The \"leaf\" is the deepest entry in the chain; it's what\n // downstream code (useAgentChat etc.) should see as the\n // authoritative identity.\n const leafAgent =\n subChain.length > 0 ? subChain[subChain.length - 1].agent : options.agent;\n const leafName =\n subChain.length > 0\n ? subChain[subChain.length - 1].name\n : options.name || \"default\";\n\n // Full root-first chain, including the leaf. Exposed as `.path`\n // and used for cache keying so nested sessions with the same leaf\n // name don't collide.\n const fullPath = useMemo(\n () => [\n { agent: options.agent, name: options.name || \"default\" },\n ...subChain\n ],\n [options.agent, options.name, subChain]\n );\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 () =>\n createCacheKey(agentNamespace, options.name, subChain, queryDeps || []),\n [agentNamespace, options.name, subChain, 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 // Re-run when cache is invalidated after TTL expiry\n void cacheInvalidatedAt;\n\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 // Track agent state for reactivity — updated on server broadcasts and client setState\n const [agentState, setAgentState] = useState<State | undefined>(undefined);\n\n // Store identity in React state for reactivity. Seed with the\n // leaf's address — what the server will echo back in\n // `cf_agent_identity`.\n const [identity, setIdentity] = useState({\n name: leafName,\n agent: camelCaseToKebabCase(leafAgent),\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 const mutableAgentRef = useRef<{\n agent: string;\n name: string;\n identified: boolean;\n } | null>(null);\n\n // Combine the sub-agent chain with the user-provided `path`.\n // Order matters: `/sub/{child}/{name}/...` comes before `path` so\n // the server sees the hierarchy it expects.\n const combinedPath = useMemo(\n () => buildSubPath(subChain, userPath),\n [subChain, userPath]\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: combinedPath || undefined,\n query: resolvedQuery,\n ...restOptions\n }\n : {\n party: agentNamespace,\n prefix: \"agents\",\n room: options.name || \"default\",\n path: combinedPath || undefined,\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 const currentAgent = mutableAgentRef.current;\n if (currentAgent) {\n currentAgent.name = newName;\n currentAgent.agent = newAgent;\n currentAgent.identified = true;\n }\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 setAgentState(parsedMessage.state as 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 if (mutableAgentRef.current) {\n mutableAgentRef.current.identified = false;\n }\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 state: State | undefined;\n setState: (state: State) => void;\n call: UntypedAgentMethodCall;\n stub: UntypedAgentStub;\n getHttpUrl: () => string;\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 = (newState: State) => {\n agent.send(\n JSON.stringify({ state: newState, type: MessageType.CF_AGENT_STATE })\n );\n setAgentState(newState);\n options.onStateUpdate?.(newState, \"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 // Full root-first chain including the leaf. Computed from the\n // user-provided options — the server doesn't need to echo it\n // back because the client already knows. Write past the\n // PartySocket `.path: string` shape via an unknown cast — the\n // overload signatures expose this as `ReadonlyArray<...>`.\n (\n agent as unknown as { path: ReadonlyArray<{ agent: string; name: string }> }\n ).path = fullPath;\n agent.identified = identity.identified;\n agent.ready = readyRef.current!.promise;\n agent.state = agentState;\n mutableAgentRef.current = agent;\n // Memoize stub so it's referentially stable across renders\n // (call is already stable via useCallback)\n const stub = useMemo(() => createStubProxy(call), [call]);\n agent.stub = stub;\n agent.getHttpUrl = () => {\n // TODO: upstream to partysocket — expose an HTTP URL property\n // @ts-expect-error accessing protected PartySocket internals\n const wsUrl: string = (agent._url as string | null) || agent._pkurl || \"\";\n return wsUrl.replace(\"ws://\", \"http://\").replace(\"wss://\", \"https://\");\n };\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 // The overload signatures return `Omit<PartySocket, \"path\"> & { path: ... }`,\n // but `agent` is inferred as the raw PartySocket. Cast to satisfy\n // the overload contract — the runtime override of `agent.path`\n // above ensures the shape matches.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return agent as any;\n}\n\ntype AgentToolEventAgent = Pick<\n PartySocket,\n \"addEventListener\" | \"removeEventListener\"\n>;\n\nfunction agentToolDedupeKey(message: AgentToolEventMessage): string {\n return [\n message.parentToolCallId ?? \"\",\n message.event.runId,\n String(message.sequence)\n ].join(\"\\0\");\n}\n\nexport function useAgentToolEvents(options: { agent: AgentToolEventAgent }): {\n runsById: Record<string, AgentToolRunState>;\n runsByToolCallId: Record<string, AgentToolRunState[]>;\n unboundRuns: AgentToolRunState[];\n getRunsForToolCall(toolCallId: string): AgentToolRunState[];\n resetLocalState(): void;\n} {\n const { agent } = options;\n const [state, setState] = useState<AgentToolEventState>(() =>\n createAgentToolEventState()\n );\n const seenRef = useRef(new Set<string>());\n\n useEffect(() => {\n const onMessage = (event: MessageEvent) => {\n if (typeof event.data !== \"string\") return;\n let message: AgentToolEventMessage;\n try {\n message = JSON.parse(event.data) as AgentToolEventMessage;\n } catch {\n return;\n }\n if (message.type !== \"agent-tool-event\") return;\n const key = agentToolDedupeKey(message);\n if (seenRef.current.has(key)) return;\n seenRef.current.add(key);\n setState((prev) => applyAgentToolEvent(prev, message));\n };\n\n agent.addEventListener(\"message\", onMessage);\n return () => agent.removeEventListener(\"message\", onMessage);\n }, [agent]);\n\n const resetLocalState = useCallback(() => {\n seenRef.current.clear();\n setState(createAgentToolEventState());\n }, []);\n\n const getRunsForToolCall = useCallback(\n (toolCallId: string) => state.runsByToolCallId[toolCallId] ?? [],\n [state.runsByToolCallId]\n );\n\n return {\n ...state,\n getRunsForToolCall,\n resetLocalState\n };\n}\n"],"mappings":"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"}
|
package/dist/sub-routing.d.ts
CHANGED
package/dist/sub-routing.js
CHANGED
|
@@ -105,7 +105,13 @@ function resolveClassName(segment, knownClasses) {
|
|
|
105
105
|
*/
|
|
106
106
|
async function routeSubAgentRequest(req, parent, options) {
|
|
107
107
|
if (!parseSubAgentPath(options?.fromPath ? `http://placeholder${options.fromPath.startsWith("/") ? "" : "/"}${options.fromPath}` : req.url)) return new Response("Sub-agent path not found in request URL", { status: 400 });
|
|
108
|
-
const
|
|
108
|
+
const forwardUrl = options?.fromPath !== void 0 ? rewritePathname(req.url, options.fromPath) : req.url;
|
|
109
|
+
const forwardInit = {
|
|
110
|
+
method: req.method,
|
|
111
|
+
headers: new Headers(req.headers)
|
|
112
|
+
};
|
|
113
|
+
if (req.body && req.method !== "GET" && req.method !== "HEAD") forwardInit.body = await req.arrayBuffer();
|
|
114
|
+
const forwardReq = new Request(forwardUrl, forwardInit);
|
|
109
115
|
return parent.fetch(forwardReq);
|
|
110
116
|
}
|
|
111
117
|
/**
|
package/dist/sub-routing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sub-routing.js","names":[],"sources":["../src/sub-routing.ts"],"sourcesContent":["/**\n * Sub-agent routing primitives — external addressability for facets.\n *\n * The public surface:\n * - `routeSubAgentRequest(req, parent, options?)` — the sub-agent\n * analog of `routeAgentRequest`. Use in custom fetch handlers.\n * - `getSubAgentByName(parent, Cls, name)` — the sub-agent analog\n * of `getAgentByName`. Returns a typed RPC stub that proxies\n * method calls through the parent. No `.fetch()` support —\n * external HTTP/WS routing goes through `routeSubAgentRequest`.\n *\n * Internal:\n * - `parseSubAgentPath(url)` — URL → `{ childClass, childName, remainingPath }`.\n * - `forwardToFacet(req, parent, match)` — resolves `ctx.facets.get(...)`\n * on the parent and returns `facetStub.fetch(rewrittenReq)`.\n *\n * @experimental The API surface may change before stabilizing.\n */\n\nimport { camelCaseToKebabCase, isInternalJsStubProp } from \"./utils\";\nimport type { Agent, SubAgentClass, SubAgentStub } from \"./index\";\n\n/**\n * URL segment marking a parent↔child boundary.\n *\n * Exposed as a constant so callers can build URLs symbolically, but\n * not configurable — the routing layer matches on the literal `sub`\n * token everywhere (parent fetch, client, helpers).\n */\nexport const SUB_PREFIX = \"sub\";\n\nexport interface SubAgentPathMatch {\n /** CamelCase class name of the child, as it appears in `ctx.exports`. */\n childClass: string;\n /** URL-decoded child name. */\n childName: string;\n /**\n * Request path to forward to the child, with the\n * `/sub/{class}/{name}` segment stripped. Always begins with `/`;\n * may itself contain further `/sub/...` markers when a\n * recursively nested sub-agent is being routed.\n */\n remainingPath: string;\n}\n\n/**\n * Parse a URL and extract the first `/sub/{class}/{name}` segment,\n * if any. Recursive nesting is handled naturally: callers parse one\n * level at a time; the child then parses its own URL (which still\n * contains any deeper `/sub/...` markers).\n *\n * Names are URL-decoded. Classes are kebab-to-CamelCase converted\n * via a best-effort match against a provided lookup — pass\n * `ctx.exports` keys to get exact CamelCase; pass `undefined` for\n * a tolerant conversion without validation.\n *\n * Returns `null` when the URL doesn't contain the marker at a\n * recognized position, or when the marker has no following\n * class+name pair.\n */\nexport function parseSubAgentPath(\n url: string,\n options?: {\n /** CamelCase class names to match against (usually `ctx.exports` keys). */\n knownClasses?: readonly string[];\n }\n): SubAgentPathMatch | null {\n const pathname = new URL(url).pathname;\n const parts = pathname.split(\"/\").filter(Boolean);\n\n // Walk every occurrence of the `sub` segment — a plain\n // `indexOf(SUB_PREFIX)` would mis-match when the literal token\n // appears earlier in the URL (parent instance name == \"sub\", a\n // `basePath` segment that happens to be \"sub\", etc). We return\n // the first position where `parts[i+1]` resolves to a valid\n // class, which pins the real parent↔child boundary.\n for (let i = 0; i < parts.length; i++) {\n if (parts[i] !== SUB_PREFIX) continue;\n if (i + 2 >= parts.length) continue;\n\n const classSegment = parts[i + 1];\n const nameSegment = parts[i + 2];\n\n const childClass = resolveClassName(classSegment, options?.knownClasses);\n if (!childClass) continue;\n\n let childName: string;\n try {\n childName = decodeURIComponent(nameSegment);\n } catch {\n continue;\n }\n\n const remainingParts = parts.slice(i + 3);\n const remainingPath =\n remainingParts.length > 0 ? \"/\" + remainingParts.join(\"/\") : \"/\";\n\n return { childClass, childName, remainingPath };\n }\n\n return null;\n}\n\n/**\n * Best-effort kebab-to-CamelCase match. If `knownClasses` is\n * provided, returns the matching CamelCase entry (or null if no\n * match). If not, performs a naive kebab→CamelCase conversion.\n */\nfunction resolveClassName(\n segment: string,\n knownClasses?: readonly string[]\n): string | null {\n if (knownClasses) {\n const match = knownClasses.find(\n (name) => camelCaseToKebabCase(name) === segment\n );\n return match ?? null;\n }\n return segment\n .split(\"-\")\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join(\"\");\n}\n\n// ── routeSubAgentRequest ───────────────────────────────────────────\n\n/**\n * Minimal parent-side shape that `routeSubAgentRequest` relies on:\n * something fetchable (a DO stub, a sub-agent stub, etc.).\n *\n * @internal\n */\ninterface FetchableParent {\n fetch(req: Request): Promise<Response>;\n}\n\n/**\n * Route a request into a sub-agent via its parent DO.\n *\n * Use this in a custom fetch handler when your URL shape doesn't\n * match the `/agents/{class}/{name}` default — you identify and\n * fetch the parent yourself, then let this helper parse the\n * `/sub/{child}/...` tail and forward it.\n *\n * Runs `onBeforeSubAgent` on the parent DO (authorization / request\n * mutation / short-circuit response).\n *\n * For the default `/agents/...` URL shape, use `routeAgentRequest`\n * instead — it handles the parent lookup and this dispatch in one\n * call.\n *\n * @example\n * ```ts\n * export default {\n * async fetch(req, env) {\n * const { parentName, rest } = myCustomParse(req.url);\n * const parent = await getAgentByName(env.Inbox, parentName);\n * return routeSubAgentRequest(req, parent, { fromPath: rest });\n * }\n * };\n * ```\n *\n * @experimental The API surface may change before stabilizing.\n */\nexport async function routeSubAgentRequest(\n req: Request,\n parent: unknown,\n options?: {\n /**\n * Path to route on. Defaults to `req.url`'s pathname. Useful\n * when your outer URL is custom (e.g. `/api/v1/...`) and you\n * want to route the sub-agent tail without rewriting the\n * Request first.\n */\n fromPath?: string;\n }\n): Promise<Response> {\n // We don't know the parent's ctx.exports from here, so parse with\n // a permissive resolver. If the class doesn't exist, the parent's\n // bridge will 404. This lets us keep the helper self-contained.\n const pathForParsing = options?.fromPath\n ? `http://placeholder${options.fromPath.startsWith(\"/\") ? \"\" : \"/\"}${options.fromPath}`\n : req.url;\n\n const match = parseSubAgentPath(pathForParsing);\n if (!match) {\n return new Response(\"Sub-agent path not found in request URL\", {\n status: 400\n });\n }\n\n // Hand the request to the parent so `onBeforeSubAgent` fires in\n // the parent's isolate. The parent's `fetch` handler recognizes\n // the marker internally — we preserve the original request URL\n // (possibly rewritten by `fromPath`) so the parent's parse sees\n // the same match.\n //\n // Key subtlety: when rewriting the pathname via `fromPath`, we\n // mutate the *original* URL's pathname instead of constructing a\n // new URL from scratch. `new URL(\"/path\", baseWithQuery)` discards\n // the base's search; we want the caller's query params (e.g. auth\n // tokens, PartySocket's `_pk=...` handshake key) to survive.\n // Mirrors how `_cf_forwardToFacet` rewrites only pathname when\n // handing off to the child facet. If `fromPath` itself contains a\n // `?query` segment, that overrides the original.\n const forwardReq =\n options?.fromPath !== undefined\n ? new Request(rewritePathname(req.url, options.fromPath), req)\n : req;\n\n return (parent as FetchableParent).fetch(forwardReq);\n}\n\n/**\n * Replace a URL's pathname (and optionally its search) while\n * preserving every other component. Matches how `_cf_forwardToFacet`\n * forwards requests — pathname is the only thing that changes by\n * default; if the replacement path carries its own query string,\n * that wins.\n */\nfunction rewritePathname(url: string, fromPath: string): string {\n const normalized = fromPath.startsWith(\"/\") ? fromPath : `/${fromPath}`;\n const queryIdx = normalized.indexOf(\"?\");\n const pathOnly = queryIdx >= 0 ? normalized.slice(0, queryIdx) : normalized;\n const querySuffix = queryIdx >= 0 ? normalized.slice(queryIdx) : \"\";\n\n const rewritten = new URL(url);\n rewritten.pathname = pathOnly;\n if (querySuffix) {\n rewritten.search = querySuffix; // URL setter keeps the `?` prefix\n }\n return rewritten.toString();\n}\n\n// ── getSubAgentByName ──────────────────────────────────────────────\n\n/**\n * Parent-side RPC bridge shape that `getSubAgentByName` relies on.\n *\n * @internal\n */\ninterface SubAgentInvokeEndpoint {\n _cf_invokeSubAgent(\n className: string,\n name: string,\n method: string,\n args: unknown[]\n ): Promise<unknown>;\n}\n\n/**\n * Get a typed RPC stub for a sub-agent from outside the parent DO.\n *\n * The returned stub proxies method calls through the parent via a\n * stateless per-call bridge (caller → parent → facet), so each\n * method invocation costs one extra RPC hop. Works across parent\n * hibernation — no cached references to go stale.\n *\n * Limitations:\n * - RPC methods only. `.fetch()` is not supported (will throw).\n * Use `routeSubAgentRequest` for external HTTP/WS.\n * - Arguments and return values must be structured-cloneable,\n * same as any DO RPC call.\n * - Does not run `onBeforeSubAgent` on the parent — analogous to\n * `getAgentByName` not running `onBeforeConnect`. The caller is\n * assumed to have performed whatever access checks are needed.\n *\n * @example\n * ```ts\n * const inbox = await getAgentByName(env.MyInbox, userId);\n * const chat = await getSubAgentByName(inbox, MyChat, chatId);\n * await chat.addMessage({ role: \"user\", content: \"hi\" });\n * ```\n *\n * @experimental The API surface may change before stabilizing.\n */\nexport async function getSubAgentByName<T extends Agent>(\n parent: unknown,\n cls: SubAgentClass<T>,\n name: string\n): Promise<SubAgentStub<T>> {\n if (name.includes(\"\\0\")) {\n throw new Error(\n `Sub-agent name contains null character (\\\\0), which is reserved.`\n );\n }\n\n const bridge = parent as SubAgentInvokeEndpoint;\n const className = cls?.name;\n if (!className) {\n throw new Error(\n `getSubAgentByName: could not determine class name from ${cls}. ` +\n `Ensure you are passing the class constructor (e.g. getSubAgentByName(parent, MyChat, name)), not a string or undefined.`\n );\n }\n\n return new Proxy(\n {},\n {\n get(_target, prop) {\n // JS / runtime / test-framework probes (thenable check,\n // serialization, inspection, matcher duck-typing) must NOT\n // dispatch an RPC — returning `undefined` is the contract\n // the inner `createStubProxy` uses for `useAgent` stubs.\n // Without this guard, `JSON.stringify(stub)`, `console.log`,\n // Vitest matchers, and `await stub` would all trigger bogus\n // `_cf_invokeSubAgent` calls that fail with \"Method not found\".\n if (isInternalJsStubProp(prop)) return undefined;\n if (typeof prop !== \"string\") return undefined;\n // `.fetch` gets a dedicated error so users who try to use\n // the stub for HTTP/WS get a helpful pointer.\n if (prop === \"fetch\") {\n return () => {\n throw new Error(\n `getSubAgentByName returns an RPC-only stub — .fetch() is ` +\n `not supported. Use routeSubAgentRequest() or the ` +\n `/agents/{parent}/{name}/sub/{child}/{name} URL for ` +\n `external HTTP/WS routing.`\n );\n };\n }\n return async (...args: unknown[]) =>\n bridge._cf_invokeSubAgent(className, name, prop, args);\n }\n }\n ) as SubAgentStub<T>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAa,aAAa;;;;;;;;;;;;;;;;AA+B1B,SAAgB,kBACd,KACA,SAI0B;CAE1B,MAAM,QADW,IAAI,IAAI,IAAI,CAAC,SACP,MAAM,IAAI,CAAC,OAAO,QAAQ;AAQjD,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,MAAI,MAAM,OAAA,MAAmB;AAC7B,MAAI,IAAI,KAAK,MAAM,OAAQ;EAE3B,MAAM,eAAe,MAAM,IAAI;EAC/B,MAAM,cAAc,MAAM,IAAI;EAE9B,MAAM,aAAa,iBAAiB,cAAc,SAAS,aAAa;AACxE,MAAI,CAAC,WAAY;EAEjB,IAAI;AACJ,MAAI;AACF,eAAY,mBAAmB,YAAY;UACrC;AACN;;EAGF,MAAM,iBAAiB,MAAM,MAAM,IAAI,EAAE;EACzC,MAAM,gBACJ,eAAe,SAAS,IAAI,MAAM,eAAe,KAAK,IAAI,GAAG;AAE/D,SAAO;GAAE;GAAY;GAAW;GAAe;;AAGjD,QAAO;;;;;;;AAQT,SAAS,iBACP,SACA,cACe;AACf,KAAI,aAIF,QAHc,aAAa,MACxB,SAAS,qBAAqB,KAAK,KAAK,QAE/B,IAAI;AAElB,QAAO,QACJ,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,OAAO,EAAE,CAAC,aAAa,GAAG,EAAE,MAAM,EAAE,CAAC,CAClD,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2Cb,eAAsB,qBACpB,KACA,QACA,SASmB;AASnB,KAAI,CADU,kBAJS,SAAS,WAC5B,qBAAqB,QAAQ,SAAS,WAAW,IAAI,GAAG,KAAK,MAAM,QAAQ,aAC3E,IAAI,IAGE,CACR,QAAO,IAAI,SAAS,2CAA2C,EAC7D,QAAQ,KACT,CAAC;CAiBJ,MAAM,aACJ,SAAS,aAAa,KAAA,IAClB,IAAI,QAAQ,gBAAgB,IAAI,KAAK,QAAQ,SAAS,EAAE,IAAI,GAC5D;AAEN,QAAQ,OAA2B,MAAM,WAAW;;;;;;;;;AAUtD,SAAS,gBAAgB,KAAa,UAA0B;CAC9D,MAAM,aAAa,SAAS,WAAW,IAAI,GAAG,WAAW,IAAI;CAC7D,MAAM,WAAW,WAAW,QAAQ,IAAI;CACxC,MAAM,WAAW,YAAY,IAAI,WAAW,MAAM,GAAG,SAAS,GAAG;CACjE,MAAM,cAAc,YAAY,IAAI,WAAW,MAAM,SAAS,GAAG;CAEjE,MAAM,YAAY,IAAI,IAAI,IAAI;AAC9B,WAAU,WAAW;AACrB,KAAI,YACF,WAAU,SAAS;AAErB,QAAO,UAAU,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6C7B,eAAsB,kBACpB,QACA,KACA,MAC0B;AAC1B,KAAI,KAAK,SAAS,KAAK,CACrB,OAAM,IAAI,MACR,mEACD;CAGH,MAAM,SAAS;CACf,MAAM,YAAY,KAAK;AACvB,KAAI,CAAC,UACH,OAAM,IAAI,MACR,0DAA0D,IAAI,2HAE/D;AAGH,QAAO,IAAI,MACT,EAAE,EACF,EACE,IAAI,SAAS,MAAM;AAQjB,MAAI,qBAAqB,KAAK,CAAE,QAAO,KAAA;AACvC,MAAI,OAAO,SAAS,SAAU,QAAO,KAAA;AAGrC,MAAI,SAAS,QACX,cAAa;AACX,SAAM,IAAI,MACR,yLAID;;AAGL,SAAO,OAAO,GAAG,SACf,OAAO,mBAAmB,WAAW,MAAM,MAAM,KAAK;IAE3D,CACF"}
|
|
1
|
+
{"version":3,"file":"sub-routing.js","names":[],"sources":["../src/sub-routing.ts"],"sourcesContent":["/**\n * Sub-agent routing primitives — external addressability for facets.\n *\n * The public surface:\n * - `routeSubAgentRequest(req, parent, options?)` — the sub-agent\n * analog of `routeAgentRequest`. Use in custom fetch handlers.\n * - `getSubAgentByName(parent, Cls, name)` — the sub-agent analog\n * of `getAgentByName`. Returns a typed RPC stub that proxies\n * method calls through the parent. No `.fetch()` support —\n * external HTTP/WS routing goes through `routeSubAgentRequest`.\n *\n * Internal:\n * - `parseSubAgentPath(url)` — URL → `{ childClass, childName, remainingPath }`.\n * - `forwardToFacet(req, parent, match)` — resolves `ctx.facets.get(...)`\n * on the parent and returns `facetStub.fetch(rewrittenReq)`.\n *\n * @experimental The API surface may change before stabilizing.\n */\n\nimport { camelCaseToKebabCase, isInternalJsStubProp } from \"./utils\";\nimport type { Agent, SubAgentClass, SubAgentStub } from \"./index\";\n\n/**\n * URL segment marking a parent↔child boundary.\n *\n * Exposed as a constant so callers can build URLs symbolically, but\n * not configurable — the routing layer matches on the literal `sub`\n * token everywhere (parent fetch, client, helpers).\n */\nexport const SUB_PREFIX = \"sub\";\n\nexport interface SubAgentPathMatch {\n /** CamelCase class name of the child, as it appears in `ctx.exports`. */\n childClass: string;\n /** URL-decoded child name. */\n childName: string;\n /**\n * Request path to forward to the child, with the\n * `/sub/{class}/{name}` segment stripped. Always begins with `/`;\n * may itself contain further `/sub/...` markers when a\n * recursively nested sub-agent is being routed.\n */\n remainingPath: string;\n}\n\n/**\n * Parse a URL and extract the first `/sub/{class}/{name}` segment,\n * if any. Recursive nesting is handled naturally: callers parse one\n * level at a time; the child then parses its own URL (which still\n * contains any deeper `/sub/...` markers).\n *\n * Names are URL-decoded. Classes are kebab-to-CamelCase converted\n * via a best-effort match against a provided lookup — pass\n * `ctx.exports` keys to get exact CamelCase; pass `undefined` for\n * a tolerant conversion without validation.\n *\n * Returns `null` when the URL doesn't contain the marker at a\n * recognized position, or when the marker has no following\n * class+name pair.\n */\nexport function parseSubAgentPath(\n url: string,\n options?: {\n /** CamelCase class names to match against (usually `ctx.exports` keys). */\n knownClasses?: readonly string[];\n }\n): SubAgentPathMatch | null {\n const pathname = new URL(url).pathname;\n const parts = pathname.split(\"/\").filter(Boolean);\n\n // Walk every occurrence of the `sub` segment — a plain\n // `indexOf(SUB_PREFIX)` would mis-match when the literal token\n // appears earlier in the URL (parent instance name == \"sub\", a\n // `basePath` segment that happens to be \"sub\", etc). We return\n // the first position where `parts[i+1]` resolves to a valid\n // class, which pins the real parent↔child boundary.\n for (let i = 0; i < parts.length; i++) {\n if (parts[i] !== SUB_PREFIX) continue;\n if (i + 2 >= parts.length) continue;\n\n const classSegment = parts[i + 1];\n const nameSegment = parts[i + 2];\n\n const childClass = resolveClassName(classSegment, options?.knownClasses);\n if (!childClass) continue;\n\n let childName: string;\n try {\n childName = decodeURIComponent(nameSegment);\n } catch {\n continue;\n }\n\n const remainingParts = parts.slice(i + 3);\n const remainingPath =\n remainingParts.length > 0 ? \"/\" + remainingParts.join(\"/\") : \"/\";\n\n return { childClass, childName, remainingPath };\n }\n\n return null;\n}\n\n/**\n * Best-effort kebab-to-CamelCase match. If `knownClasses` is\n * provided, returns the matching CamelCase entry (or null if no\n * match). If not, performs a naive kebab→CamelCase conversion.\n */\nfunction resolveClassName(\n segment: string,\n knownClasses?: readonly string[]\n): string | null {\n if (knownClasses) {\n const match = knownClasses.find(\n (name) => camelCaseToKebabCase(name) === segment\n );\n return match ?? null;\n }\n return segment\n .split(\"-\")\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join(\"\");\n}\n\n// ── routeSubAgentRequest ───────────────────────────────────────────\n\n/**\n * Minimal parent-side shape that `routeSubAgentRequest` relies on:\n * something fetchable (a DO stub, a sub-agent stub, etc.).\n *\n * @internal\n */\ninterface FetchableParent {\n fetch(req: Request): Promise<Response>;\n}\n\n/**\n * Route a request into a sub-agent via its parent DO.\n *\n * Use this in a custom fetch handler when your URL shape doesn't\n * match the `/agents/{class}/{name}` default — you identify and\n * fetch the parent yourself, then let this helper parse the\n * `/sub/{child}/...` tail and forward it.\n *\n * Runs `onBeforeSubAgent` on the parent DO (authorization / request\n * mutation / short-circuit response).\n *\n * For the default `/agents/...` URL shape, use `routeAgentRequest`\n * instead — it handles the parent lookup and this dispatch in one\n * call.\n *\n * @example\n * ```ts\n * export default {\n * async fetch(req, env) {\n * const { parentName, rest } = myCustomParse(req.url);\n * const parent = await getAgentByName(env.Inbox, parentName);\n * return routeSubAgentRequest(req, parent, { fromPath: rest });\n * }\n * };\n * ```\n *\n * @experimental The API surface may change before stabilizing.\n */\nexport async function routeSubAgentRequest(\n req: Request,\n parent: unknown,\n options?: {\n /**\n * Path to route on. Defaults to `req.url`'s pathname. Useful\n * when your outer URL is custom (e.g. `/api/v1/...`) and you\n * want to route the sub-agent tail without rewriting the\n * Request first.\n */\n fromPath?: string;\n }\n): Promise<Response> {\n // We don't know the parent's ctx.exports from here, so parse with\n // a permissive resolver. If the class doesn't exist, the parent's\n // bridge will 404. This lets us keep the helper self-contained.\n const pathForParsing = options?.fromPath\n ? `http://placeholder${options.fromPath.startsWith(\"/\") ? \"\" : \"/\"}${options.fromPath}`\n : req.url;\n\n const match = parseSubAgentPath(pathForParsing);\n if (!match) {\n return new Response(\"Sub-agent path not found in request URL\", {\n status: 400\n });\n }\n\n // Hand the request to the parent so `onBeforeSubAgent` fires in\n // the parent's isolate. The parent's `fetch` handler recognizes\n // the marker internally — we preserve the original request URL\n // (possibly rewritten by `fromPath`) so the parent's parse sees\n // the same match.\n //\n // Key subtlety: when rewriting the pathname via `fromPath`, we\n // mutate the *original* URL's pathname instead of constructing a\n // new URL from scratch. `new URL(\"/path\", baseWithQuery)` discards\n // the base's search; we want the caller's query params (e.g. auth\n // tokens, PartySocket's `_pk=...` handshake key) to survive.\n // Mirrors how `_cf_forwardToFacet` rewrites only pathname when\n // handing off to the child facet. If `fromPath` itself contains a\n // `?query` segment, that overrides the original.\n const forwardUrl =\n options?.fromPath !== undefined\n ? rewritePathname(req.url, options.fromPath)\n : req.url;\n const forwardInit: RequestInit = {\n method: req.method,\n headers: new Headers(req.headers)\n };\n if (req.body && req.method !== \"GET\" && req.method !== \"HEAD\") {\n forwardInit.body = await req.arrayBuffer();\n }\n const forwardReq = new Request(forwardUrl, forwardInit);\n\n return (parent as FetchableParent).fetch(forwardReq);\n}\n\n/**\n * Replace a URL's pathname (and optionally its search) while\n * preserving every other component. Matches how `_cf_forwardToFacet`\n * forwards requests — pathname is the only thing that changes by\n * default; if the replacement path carries its own query string,\n * that wins.\n */\nfunction rewritePathname(url: string, fromPath: string): string {\n const normalized = fromPath.startsWith(\"/\") ? fromPath : `/${fromPath}`;\n const queryIdx = normalized.indexOf(\"?\");\n const pathOnly = queryIdx >= 0 ? normalized.slice(0, queryIdx) : normalized;\n const querySuffix = queryIdx >= 0 ? normalized.slice(queryIdx) : \"\";\n\n const rewritten = new URL(url);\n rewritten.pathname = pathOnly;\n if (querySuffix) {\n rewritten.search = querySuffix; // URL setter keeps the `?` prefix\n }\n return rewritten.toString();\n}\n\n// ── getSubAgentByName ──────────────────────────────────────────────\n\n/**\n * Parent-side RPC bridge shape that `getSubAgentByName` relies on.\n *\n * @internal\n */\ninterface SubAgentInvokeEndpoint {\n _cf_invokeSubAgent(\n className: string,\n name: string,\n method: string,\n args: unknown[]\n ): Promise<unknown>;\n}\n\n/**\n * Get a typed RPC stub for a sub-agent from outside the parent DO.\n *\n * The returned stub proxies method calls through the parent via a\n * stateless per-call bridge (caller → parent → facet), so each\n * method invocation costs one extra RPC hop. Works across parent\n * hibernation — no cached references to go stale.\n *\n * Limitations:\n * - RPC methods only. `.fetch()` is not supported (will throw).\n * Use `routeSubAgentRequest` for external HTTP/WS.\n * - Arguments and return values must be structured-cloneable,\n * same as any DO RPC call.\n * - Does not run `onBeforeSubAgent` on the parent — analogous to\n * `getAgentByName` not running `onBeforeConnect`. The caller is\n * assumed to have performed whatever access checks are needed.\n *\n * @example\n * ```ts\n * const inbox = await getAgentByName(env.MyInbox, userId);\n * const chat = await getSubAgentByName(inbox, MyChat, chatId);\n * await chat.addMessage({ role: \"user\", content: \"hi\" });\n * ```\n *\n * @experimental The API surface may change before stabilizing.\n */\nexport async function getSubAgentByName<T extends Agent>(\n parent: unknown,\n cls: SubAgentClass<T>,\n name: string\n): Promise<SubAgentStub<T>> {\n if (name.includes(\"\\0\")) {\n throw new Error(\n `Sub-agent name contains null character (\\\\0), which is reserved.`\n );\n }\n\n const bridge = parent as SubAgentInvokeEndpoint;\n const className = cls?.name;\n if (!className) {\n throw new Error(\n `getSubAgentByName: could not determine class name from ${cls}. ` +\n `Ensure you are passing the class constructor (e.g. getSubAgentByName(parent, MyChat, name)), not a string or undefined.`\n );\n }\n\n return new Proxy(\n {},\n {\n get(_target, prop) {\n // JS / runtime / test-framework probes (thenable check,\n // serialization, inspection, matcher duck-typing) must NOT\n // dispatch an RPC — returning `undefined` is the contract\n // the inner `createStubProxy` uses for `useAgent` stubs.\n // Without this guard, `JSON.stringify(stub)`, `console.log`,\n // Vitest matchers, and `await stub` would all trigger bogus\n // `_cf_invokeSubAgent` calls that fail with \"Method not found\".\n if (isInternalJsStubProp(prop)) return undefined;\n if (typeof prop !== \"string\") return undefined;\n // `.fetch` gets a dedicated error so users who try to use\n // the stub for HTTP/WS get a helpful pointer.\n if (prop === \"fetch\") {\n return () => {\n throw new Error(\n `getSubAgentByName returns an RPC-only stub — .fetch() is ` +\n `not supported. Use routeSubAgentRequest() or the ` +\n `/agents/{parent}/{name}/sub/{child}/{name} URL for ` +\n `external HTTP/WS routing.`\n );\n };\n }\n return async (...args: unknown[]) =>\n bridge._cf_invokeSubAgent(className, name, prop, args);\n }\n }\n ) as SubAgentStub<T>;\n}\n"],"mappings":"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"}
|
package/dist/workflows.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"durable objects"
|
|
10
10
|
],
|
|
11
11
|
"type": "module",
|
|
12
|
-
"version": "0.12.
|
|
12
|
+
"version": "0.12.2",
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"repository": {
|
|
15
15
|
"directory": "packages/agents",
|
|
@@ -30,26 +30,26 @@
|
|
|
30
30
|
"@rolldown/plugin-babel": "^0.2.3",
|
|
31
31
|
"cron-schedule": "^6.0.0",
|
|
32
32
|
"mimetext": "^3.0.28",
|
|
33
|
-
"nanoid": "^5.1.
|
|
33
|
+
"nanoid": "^5.1.11",
|
|
34
34
|
"partyserver": "^0.5.5",
|
|
35
35
|
"partysocket": "1.1.18",
|
|
36
36
|
"yargs": "^18.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@ai-sdk/react": "^3.0.
|
|
39
|
+
"@ai-sdk/react": "^3.0.176",
|
|
40
40
|
"@tanstack/ai": "^0.14.0",
|
|
41
41
|
"@types/react": "^19.2.14",
|
|
42
42
|
"@types/yargs": "^17.0.35",
|
|
43
43
|
"@vitest/browser-playwright": "^4.1.5",
|
|
44
44
|
"@x402/core": "^2.11.0",
|
|
45
45
|
"@x402/evm": "^2.11.0",
|
|
46
|
-
"ai": "^6.0.
|
|
46
|
+
"ai": "^6.0.174",
|
|
47
47
|
"react": "^19.2.5",
|
|
48
48
|
"vitest-browser-react": "^2.2.0",
|
|
49
|
-
"zod": "^4.4.
|
|
49
|
+
"zod": "^4.4.2"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"@cloudflare/ai-chat": ">=0.
|
|
52
|
+
"@cloudflare/ai-chat": ">=0.6.1 <1.0.0",
|
|
53
53
|
"@cloudflare/codemode": ">=0.3.4 <1.0.0",
|
|
54
54
|
"@tanstack/ai": ">=0.10.2 <1.0.0",
|
|
55
55
|
"@x402/core": "^2.0.0",
|