agents 0.16.1 → 0.17.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 +11 -8
- package/dist/{agent-tool-types-NofdbL9X.d.ts → agent-tool-types-Cd1TZPfB.d.ts} +595 -137
- package/dist/agent-tool-types.d.ts +34 -18
- package/dist/agent-tool-types.js +20 -1
- package/dist/agent-tool-types.js.map +1 -0
- package/dist/agent-tools-BXlsuX0d.js +304 -0
- package/dist/agent-tools-BXlsuX0d.js.map +1 -0
- package/dist/agent-tools-_E8wxUIK.d.ts +119 -0
- package/dist/agent-tools.d.ts +24 -18
- package/dist/agent-tools.js.map +1 -1
- package/dist/ai-chat-agent.d.ts +1 -1
- package/dist/ai-chat-agent.js +1 -2
- package/dist/ai-chat-agent.js.map +1 -1
- package/dist/ai-chat-v5-migration.d.ts +1 -1
- package/dist/ai-chat-v5-migration.js +1 -2
- package/dist/ai-chat-v5-migration.js.map +1 -1
- package/dist/ai-react.d.ts +1 -1
- package/dist/ai-react.js +1 -2
- package/dist/ai-react.js.map +1 -1
- package/dist/ai-types.d.ts +1 -7
- package/dist/ai-types.js +2 -8
- package/dist/ai-types.js.map +1 -1
- package/dist/browser/ai.js +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/chat/index.d.ts +1923 -76
- package/dist/chat/index.js +1784 -278
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react.d.ts +602 -0
- package/dist/chat/react.js +1506 -0
- package/dist/chat/react.js.map +1 -0
- package/dist/chat-sdk/index.d.ts +4 -4
- package/dist/{classPrivateFieldGet2-CZ7QjTXN.js → classPrivateFieldGet2-DZBYAB34.js} +5 -5
- package/dist/{classPrivateMethodInitSpec-D-0__zd9.js → classPrivateMethodInitSpec-qMjJ6sHQ.js} +2 -2
- package/dist/{client-FUizKzj2.js → client-BZ-B3NhC.js} +19 -3
- package/dist/client-BZ-B3NhC.js.map +1 -0
- package/dist/client-tools-aIBO0Fk7.d.ts +53 -0
- package/dist/client.d.ts +76 -57
- package/dist/client.js +33 -5
- package/dist/client.js.map +1 -1
- package/dist/{compaction-helpers-DVcu5lPN.d.ts → compaction-helpers-wUz6M3us.d.ts} +20 -1
- package/dist/{connector-CrKhowfD.js → connector-CdldGF3h.js} +5 -5
- package/dist/{connector-CrKhowfD.js.map → connector-CdldGF3h.js.map} +1 -1
- package/dist/email.js +1 -1
- package/dist/email.js.map +1 -1
- package/dist/experimental/memory/session/index.d.ts +1 -1
- package/dist/experimental/memory/session/index.js +20 -2
- package/dist/experimental/memory/session/index.js.map +1 -1
- package/dist/experimental/memory/utils/index.d.ts +1 -1
- package/dist/{index-B7IbEeze.d.ts → index-CcbnKkNh.d.ts} +188 -14
- package/dist/index.d.ts +91 -71
- package/dist/index.js +562 -24
- package/dist/index.js.map +1 -1
- package/dist/mcp/client.d.ts +18 -14
- package/dist/mcp/client.js +1 -1
- package/dist/mcp/index.d.ts +30 -30
- package/dist/mcp/index.js +7 -7
- package/dist/mcp/index.js.map +1 -1
- package/dist/{agent-tools-3zLG7MgA.js → message-builder-BymO4N_D.js} +45 -126
- package/dist/message-builder-BymO4N_D.js.map +1 -0
- package/dist/observability/index.d.ts +1 -1
- package/dist/observability/index.js +4 -2
- package/dist/observability/index.js.map +1 -1
- package/dist/react.d.ts +123 -110
- package/dist/react.js +44 -11
- package/dist/react.js.map +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/skills/compile.js +1 -1
- package/dist/skills/compile.js.map +1 -1
- package/dist/skills/index.js +5 -5
- package/dist/skills/index.js.map +1 -1
- package/dist/sub-routing.d.ts +6 -6
- package/dist/utils.js +1 -1
- package/dist/utils.js.map +1 -1
- package/dist/vite.js +5 -5
- package/dist/vite.js.map +1 -1
- package/dist/wire-types-nflOzNuU.js +240 -0
- package/dist/wire-types-nflOzNuU.js.map +1 -0
- package/dist/{workflow-types-SrZK_o9p.d.ts → workflow-types-Baz_PO5v.d.ts} +42 -22
- package/dist/workflow-types.d.ts +25 -21
- package/dist/workflow-types.js.map +1 -1
- package/dist/workflows.d.ts +22 -21
- package/dist/workflows.js +31 -7
- package/dist/workflows.js.map +1 -1
- package/docs/adding-to-existing-project.md +450 -0
- package/docs/agent-class.md +503 -0
- package/docs/agent-tools.md +552 -0
- package/docs/browse-the-web.md +430 -0
- package/docs/callable-methods.md +627 -0
- package/docs/chat-agents.md +1687 -0
- package/docs/chat-sdk.md +181 -0
- package/docs/client-sdk.md +520 -0
- package/docs/client-tools-continuation.md +177 -0
- package/docs/codemode.md +440 -0
- package/docs/configuration.md +775 -0
- package/docs/cross-domain-authentication.md +171 -0
- package/docs/durable-execution.md +537 -0
- package/docs/email.md +663 -0
- package/docs/get-current-agent.md +204 -0
- package/docs/getting-started.md +305 -0
- package/docs/http-websockets.md +668 -0
- package/docs/human-in-the-loop.md +661 -0
- package/docs/index.md +151 -0
- package/docs/long-running-agents.md +730 -0
- package/docs/mcp-client.md +620 -0
- package/docs/mcp-servers.md +526 -0
- package/docs/mcp-transports.md +308 -0
- package/docs/migration-to-ai-sdk-v5.md +96 -0
- package/docs/migration-to-ai-sdk-v6.md +163 -0
- package/docs/observability.md +261 -0
- package/docs/push-notifications.md +367 -0
- package/docs/queue.md +329 -0
- package/docs/readonly-connections.md +278 -0
- package/docs/resumable-streaming.md +127 -0
- package/docs/retries.md +444 -0
- package/docs/routing.md +749 -0
- package/docs/scheduling.md +898 -0
- package/docs/securing-mcp-servers.md +359 -0
- package/docs/server-driven-messages.md +477 -0
- package/docs/sessions.md +1024 -0
- package/docs/state.md +512 -0
- package/docs/sub-agents.md +389 -0
- package/docs/webhooks.md +604 -0
- package/docs/workflows.md +877 -0
- package/package.json +47 -15
- package/dist/agent-tools-3zLG7MgA.js.map +0 -1
- package/dist/agent-tools-DLquv-dp.d.ts +0 -14
- package/dist/client-FUizKzj2.js.map +0 -1
package/dist/workflows.d.ts
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { O as Agent } from "./agent-tool-types-Cd1TZPfB.js";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
b as
|
|
7
|
-
c as
|
|
8
|
-
d as
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
r as AgentWorkflowParams,
|
|
18
|
-
s as RunWorkflowOptions,
|
|
3
|
+
C as WorkflowStatus,
|
|
4
|
+
S as WorkflowRejectedError,
|
|
5
|
+
_ as WorkflowEventPayload,
|
|
6
|
+
b as WorkflowProgressCallback,
|
|
7
|
+
c as DefaultProgress,
|
|
8
|
+
d as WorkflowCallback,
|
|
9
|
+
g as WorkflowEventCallback,
|
|
10
|
+
h as WorkflowErrorCallback,
|
|
11
|
+
i as AgentWorkflowParams,
|
|
12
|
+
l as RunWorkflowOptions,
|
|
13
|
+
m as WorkflowCompleteCallback,
|
|
14
|
+
o as AgentWorkflowStep,
|
|
15
|
+
p as WorkflowCallbackType,
|
|
16
|
+
s as ApprovalEventPayload,
|
|
19
17
|
t as AgentWorkflowEvent,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
u as WaitForApprovalOptions,
|
|
19
|
+
v as WorkflowInfo,
|
|
20
|
+
w as WorkflowTrackingRow,
|
|
21
|
+
x as WorkflowQueryCriteria,
|
|
22
|
+
y as WorkflowPage
|
|
23
|
+
} from "./workflow-types-Baz_PO5v.js";
|
|
24
24
|
import { WorkflowEntrypoint, WorkflowEvent } from "cloudflare:workers";
|
|
25
25
|
|
|
26
26
|
//#region src/workflows.d.ts
|
|
@@ -68,6 +68,7 @@ declare class AgentWorkflow<
|
|
|
68
68
|
* Called automatically before run() executes.
|
|
69
69
|
*/
|
|
70
70
|
private _initAgent;
|
|
71
|
+
private _initFacetAgent;
|
|
71
72
|
/**
|
|
72
73
|
* Call user workflow code and report unhandled errors to the Agent.
|
|
73
74
|
*/
|
package/dist/workflows.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isInternalJsStubProp } from "./utils.js";
|
|
1
2
|
import { getAgentByName } from "./index.js";
|
|
2
3
|
import { WorkflowRejectedError } from "./workflow-types.js";
|
|
3
4
|
import { WorkflowEntrypoint } from "cloudflare:workers";
|
|
@@ -59,8 +60,8 @@ var AgentWorkflow = class extends WorkflowEntrypoint {
|
|
|
59
60
|
const originalRun = proto.run;
|
|
60
61
|
proto.run = async function(event, step) {
|
|
61
62
|
if (!this.__agentInitCalled) {
|
|
62
|
-
const { __agentName, __agentBinding, __workflowName, ...userParams } = event.payload;
|
|
63
|
-
await this._initAgent(__agentName, __agentBinding, __workflowName, event.instanceId);
|
|
63
|
+
const { __agentName, __agentBinding, __workflowName, __agentOrigin, ...userParams } = event.payload;
|
|
64
|
+
await this._initAgent(__agentName, __agentBinding, __workflowName, __agentOrigin, event.instanceId);
|
|
64
65
|
this.__agentInitCalled = true;
|
|
65
66
|
try {
|
|
66
67
|
const cleanedEvent = {
|
|
@@ -82,14 +83,37 @@ var AgentWorkflow = class extends WorkflowEntrypoint {
|
|
|
82
83
|
* Initialize the Agent stub from workflow params.
|
|
83
84
|
* Called automatically before run() executes.
|
|
84
85
|
*/
|
|
85
|
-
async _initAgent(agentName, agentBinding, workflowName, instanceId) {
|
|
86
|
-
if (!
|
|
86
|
+
async _initAgent(agentName, agentBinding, workflowName, agentOrigin, instanceId) {
|
|
87
|
+
if (!workflowName || !agentOrigin && (!agentName || !agentBinding)) throw new Error("AgentWorkflow requires __agentName, __agentBinding, and __workflowName in params. Use agent.runWorkflow() to start workflows with proper agent context.");
|
|
87
88
|
this._workflowId = instanceId;
|
|
88
89
|
this._workflowName = workflowName;
|
|
89
90
|
this._errorReported = false;
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
|
|
91
|
+
if (agentOrigin && agentOrigin.version !== 1) throw new Error(`AgentWorkflow received an unsupported origin version (${agentOrigin.version}). Upgrade the "agents" package running this Workflow to match the Agent that started it.`);
|
|
92
|
+
if (agentOrigin?.kind === "facet") {
|
|
93
|
+
this._agent = await this._initFacetAgent(agentOrigin);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const resolvedAgentName = agentOrigin?.kind === "agent" ? agentOrigin.name : agentName;
|
|
97
|
+
const resolvedAgentBinding = agentOrigin?.kind === "agent" ? agentOrigin.binding : agentBinding;
|
|
98
|
+
if (!resolvedAgentName || !resolvedAgentBinding) throw new Error("AgentWorkflow requires a valid Agent origin. Use agent.runWorkflow() to start workflows with proper agent context.");
|
|
99
|
+
const namespace = this.env[resolvedAgentBinding];
|
|
100
|
+
if (!namespace) throw new Error(`Agent binding '${resolvedAgentBinding}' not found in environment`);
|
|
101
|
+
this._agent = await getAgentByName(namespace, resolvedAgentName);
|
|
102
|
+
}
|
|
103
|
+
async _initFacetAgent(origin) {
|
|
104
|
+
const root = origin.path[0];
|
|
105
|
+
if (!root) throw new Error("AgentWorkflow facet origin requires a non-empty path");
|
|
106
|
+
const namespace = this.env[origin.rootBinding];
|
|
107
|
+
if (!namespace) throw new Error(`Agent binding '${origin.rootBinding}' not found in environment`);
|
|
108
|
+
const rootAgent = await getAgentByName(namespace, root.name);
|
|
109
|
+
return new Proxy({}, { get(_target, prop) {
|
|
110
|
+
if (isInternalJsStubProp(prop)) return void 0;
|
|
111
|
+
if (typeof prop !== "string") return void 0;
|
|
112
|
+
if (prop === "fetch") return () => {
|
|
113
|
+
throw new Error("AgentWorkflow.agent for sub-agent origins is an RPC-only stub — .fetch() is not supported. Use routeSubAgentRequest() or the /agents/{parent}/{name}/sub/{child}/{name} URL for external HTTP/WS routing.");
|
|
114
|
+
};
|
|
115
|
+
return async (...args) => rootAgent._cf_invokeAgentPath(origin.path, prop, args);
|
|
116
|
+
} });
|
|
93
117
|
}
|
|
94
118
|
/**
|
|
95
119
|
* Call user workflow code and report unhandled errors to the Agent.
|
package/dist/workflows.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflows.js","names":[],"sources":["../src/workflows.ts"],"sourcesContent":["/**\n * AgentWorkflow - Base class for Workflows that integrate with Agents\n *\n * Extends Cloudflare's WorkflowEntrypoint to provide seamless access to\n * the Agent that started the workflow, enabling bidirectional communication.\n *\n * @example\n * ```typescript\n * import { AgentWorkflow } from 'agents/workflows';\n * import type { MyAgent } from './agent';\n *\n * type TaskParams = { taskId: string; data: string };\n *\n * export class ProcessingWorkflow extends AgentWorkflow<MyAgent, TaskParams> {\n * async run(event: AgentWorkflowEvent<TaskParams>, step: WorkflowStep) {\n * // Access the originating Agent via typed RPC\n * await this.agent.updateTaskStatus(event.payload.taskId, 'processing');\n *\n * const result = await step.do('process', async () => {\n * // ... processing logic\n * return { processed: true };\n * });\n *\n * // Report progress to Agent (typed)\n * await this.reportProgress({ step: 'process', status: 'complete', percent: 0.5 });\n *\n * // Broadcast to connected clients\n * await this.broadcastToClients({ type: 'progress', data: result });\n *\n * return result;\n * }\n * }\n * ```\n */\n\nimport { WorkflowEntrypoint } from \"cloudflare:workers\";\nimport type {\n WorkflowEvent,\n WorkflowStep,\n WorkflowStepEvent\n} from \"cloudflare:workers\";\nimport { getAgentByName, type Agent } from \"./index\";\nimport type {\n AgentWorkflowParams,\n AgentWorkflowStep,\n WorkflowCallback,\n DefaultProgress,\n WaitForApprovalOptions\n} from \"./workflow-types\";\nimport { WorkflowRejectedError } from \"./workflow-types\";\n\n/**\n * WeakSet to track which prototypes have been wrapped.\n * This prevents re-wrapping on subsequent instantiations of the same class.\n */\nconst wrappedPrototypes = new WeakSet<object>();\n\n/**\n * Base class for Workflows that need access to their originating Agent.\n *\n * @template AgentType - The Agent class type (for typed RPC access)\n * @template Params - User-defined params passed to the workflow (optional)\n * @template ProgressType - Type for progress reporting (defaults to DefaultProgress)\n * @template Env - Environment type (defaults to Cloudflare.Env)\n */\nexport class AgentWorkflow<\n AgentType extends Agent = Agent,\n Params = unknown,\n ProgressType = DefaultProgress,\n Env extends Cloudflare.Env = Cloudflare.Env\n> extends WorkflowEntrypoint<Env, AgentWorkflowParams<Params>> {\n /**\n * The Agent stub - initialized before run() is called.\n * Use this.agent to access the Agent's RPC methods.\n */\n private _agent?: DurableObjectStub<AgentType>;\n\n /**\n * Workflow instance ID\n */\n private _workflowId!: string;\n\n /**\n * Workflow binding name (for callbacks)\n */\n private _workflowName!: string;\n\n /**\n * Instance-level guard to prevent double initialization.\n * Used when a subclass calls super.run() after its own run() was wrapped.\n */\n private __agentInitCalled = false;\n\n /**\n * Guard to prevent double error notification.\n * Set to true when reportError() is called explicitly, so the automatic\n * error catch in the run() wrapper doesn't send a duplicate notification.\n */\n private _errorReported = false;\n\n constructor(ctx: ExecutionContext, env: Env) {\n super(ctx, env);\n\n const proto = Object.getPrototypeOf(this);\n\n // Only wrap if:\n // 1. This prototype defines its own run method (hasOwnProperty)\n // 2. It hasn't been wrapped yet (WeakSet check)\n // This prevents double-wrapping inherited methods and ensures each subclass\n // that defines run() gets wrapped exactly once.\n if (Object.hasOwn(proto, \"run\") && !wrappedPrototypes.has(proto)) {\n const originalRun = proto.run as (\n event: WorkflowEvent<Params>,\n step: AgentWorkflowStep\n ) => Promise<unknown>;\n\n // Replace the prototype's run method with a wrapper that initializes\n // the agent before calling the user's implementation\n proto.run = async function (\n this: AgentWorkflow<AgentType, Params, ProgressType, Env>,\n event: WorkflowEvent<AgentWorkflowParams<Params>>,\n step: WorkflowStep\n ) {\n // Instance-level guard: only init once per instance\n // (prevents double init if super.run() is called from a subclass)\n if (!this.__agentInitCalled) {\n const { __agentName, __agentBinding, __workflowName, ...userParams } =\n event.payload;\n\n // Initialize agent connection\n await this._initAgent(\n __agentName,\n __agentBinding,\n __workflowName,\n event.instanceId\n );\n this.__agentInitCalled = true;\n\n try {\n // Pass cleaned event and wrapped step to user's implementation\n const cleanedEvent = {\n ...event,\n payload: userParams as Params\n } as WorkflowEvent<Params>;\n\n const wrappedStep = this.extendStep(\n this._wrapStep(step),\n cleanedEvent\n );\n\n return await this._runWithErrorReporting(\n originalRun,\n cleanedEvent,\n wrappedStep\n );\n } finally {\n this._disposeAgent();\n }\n }\n\n // If already initialized (e.g., called via super.run()),\n // just call the original with the event as-is.\n return await this._runWithErrorReporting(\n originalRun,\n event as WorkflowEvent<Params>,\n step as AgentWorkflowStep\n );\n };\n\n wrappedPrototypes.add(proto);\n }\n }\n\n /**\n * Initialize the Agent stub from workflow params.\n * Called automatically before run() executes.\n */\n private async _initAgent(\n agentName: string | undefined,\n agentBinding: string | undefined,\n workflowName: string | undefined,\n instanceId: string\n ): Promise<void> {\n if (!agentName || !agentBinding || !workflowName) {\n throw new Error(\n \"AgentWorkflow requires __agentName, __agentBinding, and __workflowName in params. \" +\n \"Use agent.runWorkflow() to start workflows with proper agent context.\"\n );\n }\n\n this._workflowId = instanceId;\n this._workflowName = workflowName;\n this._errorReported = false;\n\n // Get the Agent namespace from env\n const namespace = (this.env as Record<string, unknown>)[\n agentBinding\n ] as DurableObjectNamespace<AgentType>;\n\n if (!namespace) {\n throw new Error(\n `Agent binding '${agentBinding}' not found in environment`\n );\n }\n\n // Get the Agent stub by name\n this._agent = await getAgentByName<Cloudflare.Env, AgentType>(\n namespace,\n agentName\n );\n }\n\n /**\n * Call user workflow code and report unhandled errors to the Agent.\n */\n private async _runWithErrorReporting(\n originalRun: (\n event: WorkflowEvent<Params>,\n step: AgentWorkflowStep\n ) => Promise<unknown>,\n event: WorkflowEvent<Params>,\n step: AgentWorkflowStep\n ): Promise<unknown> {\n try {\n return await originalRun.call(this, event, step);\n } catch (err) {\n await this._autoReportError(err);\n throw err;\n }\n }\n\n /**\n * Dispose the Agent stub owned by this workflow run.\n */\n private _disposeAgent(): void {\n const agent = this._agent;\n this._agent = undefined;\n this.__agentInitCalled = false;\n disposeIfPresent(agent);\n }\n\n /**\n * Wrap WorkflowStep with durable Agent communication methods.\n * Methods added to the wrapped step are idempotent and won't repeat on retry.\n *\n * Note: We add methods directly to the step object to preserve instanceof checks\n * that Cloudflare's runtime may perform on the WorkflowStep class.\n */\n private _wrapStep(step: WorkflowStep): AgentWorkflowStep {\n let stepCounter = 0;\n\n // Cast step to our extended type and add methods directly\n // This preserves the original object identity and instanceof relationship\n const wrappedStep = step as AgentWorkflowStep;\n\n // Add durable Agent methods directly to the step object\n wrappedStep.reportComplete = async <T>(result?: T): Promise<void> => {\n await step.do(`__agent_reportComplete_${stepCounter++}`, async () => {\n await this.notifyAgent({\n workflowName: this._workflowName,\n workflowId: this._workflowId,\n type: \"complete\",\n result,\n timestamp: Date.now()\n });\n });\n };\n\n wrappedStep.reportError = async (error: Error | string): Promise<void> => {\n const errorMessage = error instanceof Error ? error.message : error;\n this._errorReported = true;\n await step.do(`__agent_reportError_${stepCounter++}`, async () => {\n await this.notifyAgent({\n workflowName: this._workflowName,\n workflowId: this._workflowId,\n type: \"error\",\n error: errorMessage,\n timestamp: Date.now()\n });\n });\n };\n\n wrappedStep.sendEvent = async <T>(event: T): Promise<void> => {\n await step.do(`__agent_sendEvent_${stepCounter++}`, async () => {\n await this.notifyAgent({\n workflowName: this._workflowName,\n workflowId: this._workflowId,\n type: \"event\",\n event,\n timestamp: Date.now()\n });\n });\n };\n\n wrappedStep.updateAgentState = async (state: unknown): Promise<void> => {\n await step.do(`__agent_updateState_${stepCounter++}`, async () => {\n await this.agent._workflow_updateState(\"set\", state);\n });\n };\n\n wrappedStep.mergeAgentState = async (\n partialState: Record<string, unknown>\n ): Promise<void> => {\n await step.do(`__agent_mergeState_${stepCounter++}`, async () => {\n await this.agent._workflow_updateState(\"merge\", partialState);\n });\n };\n\n wrappedStep.resetAgentState = async (): Promise<void> => {\n await step.do(`__agent_resetState_${stepCounter++}`, async () => {\n await this.agent._workflow_updateState(\"reset\");\n });\n };\n\n return wrappedStep;\n }\n\n /**\n * Extend the Agent-aware workflow step before user code receives it.\n *\n * Subclasses can override this to add framework-specific step helpers while\n * preserving the underlying WorkflowStep object identity.\n */\n protected extendStep(\n step: AgentWorkflowStep,\n _event: WorkflowEvent<Params>\n ): AgentWorkflowStep {\n return step;\n }\n\n /**\n * Get the Agent stub for RPC calls.\n * Provides typed access to the Agent's methods.\n *\n * @example\n * ```typescript\n * // Call any public method on the Agent\n * await this.agent.updateStatus('processing');\n * const data = await this.agent.getData();\n * ```\n */\n get agent(): DurableObjectStub<AgentType> {\n if (!this._agent) {\n throw new Error(\n \"Agent not initialized. Ensure you're accessing this.agent inside run().\"\n );\n }\n return this._agent;\n }\n\n /**\n * Get the workflow instance ID\n */\n get workflowId(): string {\n return this._workflowId;\n }\n\n /**\n * Get the workflow binding name\n */\n get workflowName(): string {\n return this._workflowName;\n }\n\n /**\n * Automatically report an unhandled error to the Agent.\n * Skipped if reportError() was already called (prevents double notification).\n * Best-effort: notification failures are swallowed so the original error propagates.\n *\n * @param err - The caught error\n */\n private async _autoReportError(err: unknown): Promise<void> {\n if (this._errorReported) {\n return;\n }\n this._errorReported = true;\n const errorMessage = err instanceof Error ? err.message : String(err);\n try {\n await this.notifyAgent({\n workflowName: this._workflowName,\n workflowId: this._workflowId,\n type: \"error\",\n error: errorMessage,\n timestamp: Date.now()\n });\n } catch (_notifyErr) {\n // Best-effort: don't mask the original error\n }\n }\n\n /**\n * Send a notification to the Agent via RPC.\n *\n * @param callback - Callback payload to send\n */\n protected async notifyAgent(callback: WorkflowCallback): Promise<void> {\n await this.agent._workflow_handleCallback(callback);\n }\n\n /**\n * Report progress to the Agent with typed progress data.\n * Triggers onWorkflowProgress() on the Agent.\n *\n * @param progress - Typed progress data\n *\n * @example\n * ```typescript\n * // Using default progress type\n * await this.reportProgress({ step: 'fetch', status: 'running' });\n * await this.reportProgress({ step: 'fetch', status: 'complete', percent: 0.5 });\n *\n * // With custom progress type\n * await this.reportProgress({ stage: 'extract', recordsProcessed: 100 });\n * ```\n */\n protected async reportProgress(progress: ProgressType): Promise<void> {\n await this.notifyAgent({\n workflowName: this._workflowName,\n workflowId: this._workflowId,\n type: \"progress\",\n progress: progress as DefaultProgress,\n timestamp: Date.now()\n });\n }\n\n /**\n * Broadcast a message to all connected WebSocket clients via the Agent.\n * This is non-durable and may repeat on workflow retry.\n *\n * @param message - Message to broadcast (will be JSON-stringified)\n */\n protected broadcastToClients(message: unknown): void {\n this.agent._workflow_broadcast(message);\n }\n\n /**\n * Wait for approval from the Agent.\n * Handles rejection by reporting error (durably) and throwing WorkflowRejectedError.\n *\n * @param step - AgentWorkflowStep object\n * @param options - Wait options (timeout, eventType, stepName)\n * @returns Approval payload (throws WorkflowRejectedError if rejected)\n *\n * @example\n * ```typescript\n * const approval = await this.waitForApproval(step, { timeout: '7 days' });\n * // approval contains the payload from approveWorkflow()\n * ```\n */\n protected async waitForApproval<T = unknown>(\n step: AgentWorkflowStep,\n options?: WaitForApprovalOptions\n ): Promise<T> {\n const stepName = options?.stepName ?? \"wait-for-approval\";\n const eventType = options?.eventType ?? \"approval\";\n const timeout = options?.timeout;\n\n // Wait for the approval event\n // Note: Call reportProgress() before this method if you want to update progress\n const event = (await step.waitForEvent(stepName, {\n type: eventType,\n timeout\n })) as WorkflowStepEvent<{\n approved: boolean;\n reason?: string;\n metadata?: T;\n }>;\n\n try {\n const payload = event.payload;\n\n // Check if rejected\n if (!payload.approved) {\n const reason = payload.reason;\n await step.reportError(reason ?? \"Workflow rejected\");\n throw new WorkflowRejectedError(reason, this._workflowId);\n }\n\n // Return the approval metadata as the result\n return payload.metadata as T;\n } finally {\n disposeIfPresent(event);\n }\n }\n}\n\ntype DisposableResource = {\n [Symbol.dispose](): void;\n};\n\nfunction isDisposableResource(value: unknown): value is DisposableResource {\n return (\n !!value &&\n typeof value === \"object\" &&\n Symbol.dispose in value &&\n typeof value[Symbol.dispose] === \"function\"\n );\n}\n\nfunction disposeIfPresent(value: unknown): void {\n if (isDisposableResource(value)) {\n value[Symbol.dispose]();\n }\n}\n\n// Re-export types for convenience\nexport type {\n AgentWorkflowEvent,\n AgentWorkflowStep,\n WorkflowCallback,\n WorkflowCallbackType,\n WorkflowProgressCallback,\n WorkflowCompleteCallback,\n WorkflowErrorCallback,\n WorkflowEventCallback,\n DefaultProgress,\n WaitForApprovalOptions,\n ApprovalEventPayload,\n WorkflowStatus,\n WorkflowTrackingRow,\n RunWorkflowOptions,\n WorkflowEventPayload,\n WorkflowInfo,\n WorkflowQueryCriteria,\n WorkflowPage\n} from \"./workflow-types\";\n\nexport { WorkflowRejectedError } from \"./workflow-types\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"workflows.js","names":[],"sources":["../src/workflows.ts"],"sourcesContent":["/**\n * AgentWorkflow - Base class for Workflows that integrate with Agents\n *\n * Extends Cloudflare's WorkflowEntrypoint to provide seamless access to\n * the Agent that started the workflow, enabling bidirectional communication.\n *\n * @example\n * ```typescript\n * import { AgentWorkflow } from 'agents/workflows';\n * import type { MyAgent } from './agent';\n *\n * type TaskParams = { taskId: string; data: string };\n *\n * export class ProcessingWorkflow extends AgentWorkflow<MyAgent, TaskParams> {\n * async run(event: AgentWorkflowEvent<TaskParams>, step: WorkflowStep) {\n * // Access the originating Agent via typed RPC\n * await this.agent.updateTaskStatus(event.payload.taskId, 'processing');\n *\n * const result = await step.do('process', async () => {\n * // ... processing logic\n * return { processed: true };\n * });\n *\n * // Report progress to Agent (typed)\n * await this.reportProgress({ step: 'process', status: 'complete', percent: 0.5 });\n *\n * // Broadcast to connected clients\n * await this.broadcastToClients({ type: 'progress', data: result });\n *\n * return result;\n * }\n * }\n * ```\n */\n\nimport { WorkflowEntrypoint } from \"cloudflare:workers\";\nimport type {\n WorkflowEvent,\n WorkflowStep,\n WorkflowStepEvent\n} from \"cloudflare:workers\";\nimport { getAgentByName, type Agent } from \"./index\";\nimport type {\n AgentWorkflowParams,\n AgentWorkflowStep,\n WorkflowCallback,\n DefaultProgress,\n WaitForApprovalOptions\n} from \"./workflow-types\";\nimport { WorkflowRejectedError } from \"./workflow-types\";\nimport type {\n AgentWorkflowOrigin,\n AgentWorkflowPathStep\n} from \"./workflow-types\";\nimport { isInternalJsStubProp } from \"./utils\";\n\ntype AgentPathInvoker = {\n _cf_invokeAgentPath(\n path: ReadonlyArray<AgentWorkflowPathStep>,\n method: string,\n args: unknown[]\n ): Promise<unknown>;\n};\n\n/**\n * WeakSet to track which prototypes have been wrapped.\n * This prevents re-wrapping on subsequent instantiations of the same class.\n */\nconst wrappedPrototypes = new WeakSet<object>();\n\n/**\n * Base class for Workflows that need access to their originating Agent.\n *\n * @template AgentType - The Agent class type (for typed RPC access)\n * @template Params - User-defined params passed to the workflow (optional)\n * @template ProgressType - Type for progress reporting (defaults to DefaultProgress)\n * @template Env - Environment type (defaults to Cloudflare.Env)\n */\nexport class AgentWorkflow<\n AgentType extends Agent = Agent,\n Params = unknown,\n ProgressType = DefaultProgress,\n Env extends Cloudflare.Env = Cloudflare.Env\n> extends WorkflowEntrypoint<Env, AgentWorkflowParams<Params>> {\n /**\n * The Agent stub - initialized before run() is called.\n * Use this.agent to access the Agent's RPC methods.\n */\n private _agent?: DurableObjectStub<AgentType>;\n\n /**\n * Workflow instance ID\n */\n private _workflowId!: string;\n\n /**\n * Workflow binding name (for callbacks)\n */\n private _workflowName!: string;\n\n /**\n * Instance-level guard to prevent double initialization.\n * Used when a subclass calls super.run() after its own run() was wrapped.\n */\n private __agentInitCalled = false;\n\n /**\n * Guard to prevent double error notification.\n * Set to true when reportError() is called explicitly, so the automatic\n * error catch in the run() wrapper doesn't send a duplicate notification.\n */\n private _errorReported = false;\n\n constructor(ctx: ExecutionContext, env: Env) {\n super(ctx, env);\n\n const proto = Object.getPrototypeOf(this);\n\n // Only wrap if:\n // 1. This prototype defines its own run method (hasOwnProperty)\n // 2. It hasn't been wrapped yet (WeakSet check)\n // This prevents double-wrapping inherited methods and ensures each subclass\n // that defines run() gets wrapped exactly once.\n if (Object.hasOwn(proto, \"run\") && !wrappedPrototypes.has(proto)) {\n const originalRun = proto.run as (\n event: WorkflowEvent<Params>,\n step: AgentWorkflowStep\n ) => Promise<unknown>;\n\n // Replace the prototype's run method with a wrapper that initializes\n // the agent before calling the user's implementation\n proto.run = async function (\n this: AgentWorkflow<AgentType, Params, ProgressType, Env>,\n event: WorkflowEvent<AgentWorkflowParams<Params>>,\n step: WorkflowStep\n ) {\n // Instance-level guard: only init once per instance\n // (prevents double init if super.run() is called from a subclass)\n if (!this.__agentInitCalled) {\n const {\n __agentName,\n __agentBinding,\n __workflowName,\n __agentOrigin,\n ...userParams\n } = event.payload;\n\n // Initialize agent connection\n await this._initAgent(\n __agentName,\n __agentBinding,\n __workflowName,\n __agentOrigin,\n event.instanceId\n );\n this.__agentInitCalled = true;\n\n try {\n // Pass cleaned event and wrapped step to user's implementation\n const cleanedEvent = {\n ...event,\n payload: userParams as Params\n } as WorkflowEvent<Params>;\n\n const wrappedStep = this.extendStep(\n this._wrapStep(step),\n cleanedEvent\n );\n\n return await this._runWithErrorReporting(\n originalRun,\n cleanedEvent,\n wrappedStep\n );\n } finally {\n this._disposeAgent();\n }\n }\n\n // If already initialized (e.g., called via super.run()),\n // just call the original with the event as-is.\n return await this._runWithErrorReporting(\n originalRun,\n event as WorkflowEvent<Params>,\n step as AgentWorkflowStep\n );\n };\n\n wrappedPrototypes.add(proto);\n }\n }\n\n /**\n * Initialize the Agent stub from workflow params.\n * Called automatically before run() executes.\n */\n private async _initAgent(\n agentName: string | undefined,\n agentBinding: string | undefined,\n workflowName: string | undefined,\n agentOrigin: AgentWorkflowOrigin | undefined,\n instanceId: string\n ): Promise<void> {\n if (!workflowName || (!agentOrigin && (!agentName || !agentBinding))) {\n throw new Error(\n \"AgentWorkflow requires __agentName, __agentBinding, and __workflowName in params. \" +\n \"Use agent.runWorkflow() to start workflows with proper agent context.\"\n );\n }\n\n this._workflowId = instanceId;\n this._workflowName = workflowName;\n this._errorReported = false;\n\n // The origin payload is durably persisted in workflow params, so a workflow\n // started by an older SDK can resume against newer code (and vice versa).\n // Reject origin versions this build does not understand rather than\n // silently misreading a future shape.\n if (agentOrigin && agentOrigin.version !== 1) {\n throw new Error(\n `AgentWorkflow received an unsupported origin version (${\n (agentOrigin as { version?: unknown }).version\n }). Upgrade the \"agents\" package running this Workflow to match the Agent that started it.`\n );\n }\n\n if (agentOrigin?.kind === \"facet\") {\n this._agent = await this._initFacetAgent(agentOrigin);\n return;\n }\n\n const resolvedAgentName =\n agentOrigin?.kind === \"agent\" ? agentOrigin.name : agentName;\n const resolvedAgentBinding =\n agentOrigin?.kind === \"agent\" ? agentOrigin.binding : agentBinding;\n\n if (!resolvedAgentName || !resolvedAgentBinding) {\n throw new Error(\n \"AgentWorkflow requires a valid Agent origin. Use agent.runWorkflow() to start workflows with proper agent context.\"\n );\n }\n\n // Get the Agent namespace from env\n const namespace = (this.env as Record<string, unknown>)[\n resolvedAgentBinding\n ] as DurableObjectNamespace<AgentType>;\n\n if (!namespace) {\n throw new Error(\n `Agent binding '${resolvedAgentBinding}' not found in environment`\n );\n }\n\n // Get the Agent stub by name\n this._agent = await getAgentByName<Cloudflare.Env, AgentType>(\n namespace,\n resolvedAgentName\n );\n }\n\n private async _initFacetAgent(\n origin: Extract<AgentWorkflowOrigin, { kind: \"facet\" }>\n ): Promise<DurableObjectStub<AgentType>> {\n const root = origin.path[0];\n if (!root) {\n throw new Error(\"AgentWorkflow facet origin requires a non-empty path\");\n }\n\n const namespace = (this.env as Record<string, unknown>)[\n origin.rootBinding\n ] as DurableObjectNamespace<Agent> | undefined;\n\n if (!namespace) {\n throw new Error(\n `Agent binding '${origin.rootBinding}' not found in environment`\n );\n }\n\n const rootAgent = (await getAgentByName<Cloudflare.Env, Agent>(\n namespace,\n root.name\n )) as unknown as AgentPathInvoker;\n\n return new Proxy(\n {},\n {\n get(_target, prop) {\n if (isInternalJsStubProp(prop)) return undefined;\n if (typeof prop !== \"string\") return undefined;\n if (prop === \"fetch\") {\n return () => {\n throw new Error(\n \"AgentWorkflow.agent for sub-agent origins is an RPC-only stub — .fetch() is not supported. Use routeSubAgentRequest() or the /agents/{parent}/{name}/sub/{child}/{name} URL for external HTTP/WS routing.\"\n );\n };\n }\n return async (...args: unknown[]) =>\n rootAgent._cf_invokeAgentPath(origin.path, prop, args);\n }\n }\n ) as DurableObjectStub<AgentType>;\n }\n\n /**\n * Call user workflow code and report unhandled errors to the Agent.\n */\n private async _runWithErrorReporting(\n originalRun: (\n event: WorkflowEvent<Params>,\n step: AgentWorkflowStep\n ) => Promise<unknown>,\n event: WorkflowEvent<Params>,\n step: AgentWorkflowStep\n ): Promise<unknown> {\n try {\n return await originalRun.call(this, event, step);\n } catch (err) {\n await this._autoReportError(err);\n throw err;\n }\n }\n\n /**\n * Dispose the Agent stub owned by this workflow run.\n */\n private _disposeAgent(): void {\n const agent = this._agent;\n this._agent = undefined;\n this.__agentInitCalled = false;\n disposeIfPresent(agent);\n }\n\n /**\n * Wrap WorkflowStep with durable Agent communication methods.\n * Methods added to the wrapped step are idempotent and won't repeat on retry.\n *\n * Note: We add methods directly to the step object to preserve instanceof checks\n * that Cloudflare's runtime may perform on the WorkflowStep class.\n */\n private _wrapStep(step: WorkflowStep): AgentWorkflowStep {\n let stepCounter = 0;\n\n // Cast step to our extended type and add methods directly\n // This preserves the original object identity and instanceof relationship\n const wrappedStep = step as AgentWorkflowStep;\n\n // Add durable Agent methods directly to the step object\n wrappedStep.reportComplete = async <T>(result?: T): Promise<void> => {\n await step.do(`__agent_reportComplete_${stepCounter++}`, async () => {\n await this.notifyAgent({\n workflowName: this._workflowName,\n workflowId: this._workflowId,\n type: \"complete\",\n result,\n timestamp: Date.now()\n });\n });\n };\n\n wrappedStep.reportError = async (error: Error | string): Promise<void> => {\n const errorMessage = error instanceof Error ? error.message : error;\n this._errorReported = true;\n await step.do(`__agent_reportError_${stepCounter++}`, async () => {\n await this.notifyAgent({\n workflowName: this._workflowName,\n workflowId: this._workflowId,\n type: \"error\",\n error: errorMessage,\n timestamp: Date.now()\n });\n });\n };\n\n wrappedStep.sendEvent = async <T>(event: T): Promise<void> => {\n await step.do(`__agent_sendEvent_${stepCounter++}`, async () => {\n await this.notifyAgent({\n workflowName: this._workflowName,\n workflowId: this._workflowId,\n type: \"event\",\n event,\n timestamp: Date.now()\n });\n });\n };\n\n wrappedStep.updateAgentState = async (state: unknown): Promise<void> => {\n await step.do(`__agent_updateState_${stepCounter++}`, async () => {\n await this.agent._workflow_updateState(\"set\", state);\n });\n };\n\n wrappedStep.mergeAgentState = async (\n partialState: Record<string, unknown>\n ): Promise<void> => {\n await step.do(`__agent_mergeState_${stepCounter++}`, async () => {\n await this.agent._workflow_updateState(\"merge\", partialState);\n });\n };\n\n wrappedStep.resetAgentState = async (): Promise<void> => {\n await step.do(`__agent_resetState_${stepCounter++}`, async () => {\n await this.agent._workflow_updateState(\"reset\");\n });\n };\n\n return wrappedStep;\n }\n\n /**\n * Extend the Agent-aware workflow step before user code receives it.\n *\n * Subclasses can override this to add framework-specific step helpers while\n * preserving the underlying WorkflowStep object identity.\n */\n protected extendStep(\n step: AgentWorkflowStep,\n _event: WorkflowEvent<Params>\n ): AgentWorkflowStep {\n return step;\n }\n\n /**\n * Get the Agent stub for RPC calls.\n * Provides typed access to the Agent's methods.\n *\n * @example\n * ```typescript\n * // Call any public method on the Agent\n * await this.agent.updateStatus('processing');\n * const data = await this.agent.getData();\n * ```\n */\n get agent(): DurableObjectStub<AgentType> {\n if (!this._agent) {\n throw new Error(\n \"Agent not initialized. Ensure you're accessing this.agent inside run().\"\n );\n }\n return this._agent;\n }\n\n /**\n * Get the workflow instance ID\n */\n get workflowId(): string {\n return this._workflowId;\n }\n\n /**\n * Get the workflow binding name\n */\n get workflowName(): string {\n return this._workflowName;\n }\n\n /**\n * Automatically report an unhandled error to the Agent.\n * Skipped if reportError() was already called (prevents double notification).\n * Best-effort: notification failures are swallowed so the original error propagates.\n *\n * @param err - The caught error\n */\n private async _autoReportError(err: unknown): Promise<void> {\n if (this._errorReported) {\n return;\n }\n this._errorReported = true;\n const errorMessage = err instanceof Error ? err.message : String(err);\n try {\n await this.notifyAgent({\n workflowName: this._workflowName,\n workflowId: this._workflowId,\n type: \"error\",\n error: errorMessage,\n timestamp: Date.now()\n });\n } catch (_notifyErr) {\n // Best-effort: don't mask the original error\n }\n }\n\n /**\n * Send a notification to the Agent via RPC.\n *\n * @param callback - Callback payload to send\n */\n protected async notifyAgent(callback: WorkflowCallback): Promise<void> {\n await this.agent._workflow_handleCallback(callback);\n }\n\n /**\n * Report progress to the Agent with typed progress data.\n * Triggers onWorkflowProgress() on the Agent.\n *\n * @param progress - Typed progress data\n *\n * @example\n * ```typescript\n * // Using default progress type\n * await this.reportProgress({ step: 'fetch', status: 'running' });\n * await this.reportProgress({ step: 'fetch', status: 'complete', percent: 0.5 });\n *\n * // With custom progress type\n * await this.reportProgress({ stage: 'extract', recordsProcessed: 100 });\n * ```\n */\n protected async reportProgress(progress: ProgressType): Promise<void> {\n await this.notifyAgent({\n workflowName: this._workflowName,\n workflowId: this._workflowId,\n type: \"progress\",\n progress: progress as DefaultProgress,\n timestamp: Date.now()\n });\n }\n\n /**\n * Broadcast a message to all connected WebSocket clients via the Agent.\n * This is non-durable and may repeat on workflow retry.\n *\n * @param message - Message to broadcast (will be JSON-stringified)\n */\n protected broadcastToClients(message: unknown): void {\n this.agent._workflow_broadcast(message);\n }\n\n /**\n * Wait for approval from the Agent.\n * Handles rejection by reporting error (durably) and throwing WorkflowRejectedError.\n *\n * @param step - AgentWorkflowStep object\n * @param options - Wait options (timeout, eventType, stepName)\n * @returns Approval payload (throws WorkflowRejectedError if rejected)\n *\n * @example\n * ```typescript\n * const approval = await this.waitForApproval(step, { timeout: '7 days' });\n * // approval contains the payload from approveWorkflow()\n * ```\n */\n protected async waitForApproval<T = unknown>(\n step: AgentWorkflowStep,\n options?: WaitForApprovalOptions\n ): Promise<T> {\n const stepName = options?.stepName ?? \"wait-for-approval\";\n const eventType = options?.eventType ?? \"approval\";\n const timeout = options?.timeout;\n\n // Wait for the approval event\n // Note: Call reportProgress() before this method if you want to update progress\n const event = (await step.waitForEvent(stepName, {\n type: eventType,\n timeout\n })) as WorkflowStepEvent<{\n approved: boolean;\n reason?: string;\n metadata?: T;\n }>;\n\n try {\n const payload = event.payload;\n\n // Check if rejected\n if (!payload.approved) {\n const reason = payload.reason;\n await step.reportError(reason ?? \"Workflow rejected\");\n throw new WorkflowRejectedError(reason, this._workflowId);\n }\n\n // Return the approval metadata as the result\n return payload.metadata as T;\n } finally {\n disposeIfPresent(event);\n }\n }\n}\n\ntype DisposableResource = {\n [Symbol.dispose](): void;\n};\n\nfunction isDisposableResource(value: unknown): value is DisposableResource {\n return (\n !!value &&\n typeof value === \"object\" &&\n Symbol.dispose in value &&\n typeof value[Symbol.dispose] === \"function\"\n );\n}\n\nfunction disposeIfPresent(value: unknown): void {\n if (isDisposableResource(value)) {\n value[Symbol.dispose]();\n }\n}\n\n// Re-export types for convenience\nexport type {\n AgentWorkflowEvent,\n AgentWorkflowStep,\n WorkflowCallback,\n WorkflowCallbackType,\n WorkflowProgressCallback,\n WorkflowCompleteCallback,\n WorkflowErrorCallback,\n WorkflowEventCallback,\n DefaultProgress,\n WaitForApprovalOptions,\n ApprovalEventPayload,\n WorkflowStatus,\n WorkflowTrackingRow,\n RunWorkflowOptions,\n WorkflowEventPayload,\n WorkflowInfo,\n WorkflowQueryCriteria,\n WorkflowPage\n} from \"./workflow-types\";\n\nexport { WorkflowRejectedError } from \"./workflow-types\";\n"],"mappings":"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"}
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
# Adding Agents to an Existing Project
|
|
2
|
+
|
|
3
|
+
This guide shows how to add agents to an existing Cloudflare Workers project. If you're starting fresh, see [Getting Started](./getting-started.md) instead.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- An existing Cloudflare Workers project with `wrangler.jsonc`
|
|
10
|
+
- Node.js 18+
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Install the Package
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install agents
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
For React applications, no additional packages are needed—React bindings are included.
|
|
21
|
+
|
|
22
|
+
For Hono applications:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install agents hono-agents
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 2. Create an Agent
|
|
31
|
+
|
|
32
|
+
Create a new file for your agent (e.g., `src/agents/counter.ts`):
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { Agent } from "agents";
|
|
36
|
+
|
|
37
|
+
type CounterState = {
|
|
38
|
+
count: number;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export class Counter extends Agent<Env, CounterState> {
|
|
42
|
+
initialState: CounterState = { count: 0 };
|
|
43
|
+
|
|
44
|
+
increment() {
|
|
45
|
+
this.setState({ count: this.state.count + 1 });
|
|
46
|
+
return this.state.count;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
decrement() {
|
|
50
|
+
this.setState({ count: this.state.count - 1 });
|
|
51
|
+
return this.state.count;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 3. Update wrangler.jsonc
|
|
59
|
+
|
|
60
|
+
Add the Durable Object binding and migration:
|
|
61
|
+
|
|
62
|
+
```jsonc
|
|
63
|
+
{
|
|
64
|
+
"name": "my-existing-project",
|
|
65
|
+
"main": "src/index.ts",
|
|
66
|
+
"compatibility_date": "2025-01-01",
|
|
67
|
+
"compatibility_flags": ["nodejs_compat"], // Required for agents
|
|
68
|
+
|
|
69
|
+
// Add this section
|
|
70
|
+
"durable_objects": {
|
|
71
|
+
"bindings": [
|
|
72
|
+
{
|
|
73
|
+
"name": "Counter",
|
|
74
|
+
"class_name": "Counter"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// Add this section
|
|
80
|
+
"migrations": [
|
|
81
|
+
{
|
|
82
|
+
"tag": "v1",
|
|
83
|
+
"new_sqlite_classes": ["Counter"]
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Key points:**
|
|
90
|
+
|
|
91
|
+
- `name` in bindings becomes the property on `env` (e.g., `env.Counter`)
|
|
92
|
+
- `class_name` must match your exported class name exactly
|
|
93
|
+
- `new_sqlite_classes` enables SQLite storage for state persistence
|
|
94
|
+
- The `nodejs_compat` flag is required for the agents package
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 4. Configure TypeScript and Vite
|
|
99
|
+
|
|
100
|
+
If you use `@callable()` decorators, you need two build configurations.
|
|
101
|
+
|
|
102
|
+
**tsconfig.json** — extend `agents/tsconfig` (or add `"target": "ES2021"` manually):
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"extends": "agents/tsconfig"
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
If you have an existing `tsconfig.json` with custom settings, you can extend and override:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"extends": "agents/tsconfig",
|
|
115
|
+
"compilerOptions": {
|
|
116
|
+
"paths": { "~/*": ["./src/*"] }
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**vite.config.ts** — add the `agents()` plugin (handles TC39 decorator transforms for Vite 8):
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import agents from "agents/vite";
|
|
125
|
+
|
|
126
|
+
export default defineConfig({
|
|
127
|
+
plugins: [
|
|
128
|
+
agents()
|
|
129
|
+
// ... your existing plugins
|
|
130
|
+
]
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
If your project does not use Vite, the `tsconfig.json` change alone is sufficient — your bundler must support TC39 decorators (stage 3, version `2023-11`).
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 5. Export the Agent Class
|
|
139
|
+
|
|
140
|
+
Your agent class must be exported from your main entry point. Update your `src/index.ts`:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// Export the agent class (required for Durable Objects)
|
|
144
|
+
export { Counter } from "./agents/counter";
|
|
145
|
+
|
|
146
|
+
// Your existing exports...
|
|
147
|
+
export default {
|
|
148
|
+
// ...
|
|
149
|
+
};
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 6. Wire Up Routing
|
|
155
|
+
|
|
156
|
+
Choose the approach that matches your project structure:
|
|
157
|
+
|
|
158
|
+
### Plain Workers (fetch handler)
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { routeAgentRequest } from "agents";
|
|
162
|
+
export { Counter } from "./agents/counter";
|
|
163
|
+
|
|
164
|
+
export default {
|
|
165
|
+
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
|
|
166
|
+
// Try agent routing first
|
|
167
|
+
const agentResponse = await routeAgentRequest(request, env);
|
|
168
|
+
if (agentResponse) return agentResponse;
|
|
169
|
+
|
|
170
|
+
// Your existing routing logic
|
|
171
|
+
const url = new URL(request.url);
|
|
172
|
+
if (url.pathname === "/api/hello") {
|
|
173
|
+
return Response.json({ message: "Hello!" });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return new Response("Not found", { status: 404 });
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Hono
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { Hono } from "hono";
|
|
185
|
+
import { agentsMiddleware } from "hono-agents";
|
|
186
|
+
export { Counter } from "./agents/counter";
|
|
187
|
+
|
|
188
|
+
const app = new Hono<{ Bindings: Env }>();
|
|
189
|
+
|
|
190
|
+
// Add agents middleware - handles WebSocket upgrades and agent HTTP requests
|
|
191
|
+
app.use("*", agentsMiddleware());
|
|
192
|
+
|
|
193
|
+
// Your existing routes continue to work
|
|
194
|
+
app.get("/api/hello", (c) => c.json({ message: "Hello!" }));
|
|
195
|
+
|
|
196
|
+
export default app;
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### With Static Assets
|
|
200
|
+
|
|
201
|
+
If you're serving static assets alongside agents:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import { routeAgentRequest } from "agents";
|
|
205
|
+
export { Counter } from "./agents/counter";
|
|
206
|
+
|
|
207
|
+
export default {
|
|
208
|
+
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
|
|
209
|
+
// Try agent routing first
|
|
210
|
+
const agentResponse = await routeAgentRequest(request, env);
|
|
211
|
+
if (agentResponse) return agentResponse;
|
|
212
|
+
|
|
213
|
+
// Fall back to static assets
|
|
214
|
+
return env.ASSETS.fetch(request);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Make sure your `wrangler.jsonc` has the assets binding:
|
|
220
|
+
|
|
221
|
+
```jsonc
|
|
222
|
+
{
|
|
223
|
+
"assets": {
|
|
224
|
+
"binding": "ASSETS"
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## 7. Add TypeScript Types
|
|
232
|
+
|
|
233
|
+
Update your `Env` type to include the agent namespace. Create or update `env.d.ts`:
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
import type { Counter } from "./agents/counter";
|
|
237
|
+
|
|
238
|
+
interface Env {
|
|
239
|
+
// Your existing bindings
|
|
240
|
+
MY_KV: KVNamespace;
|
|
241
|
+
MY_DB: D1Database;
|
|
242
|
+
|
|
243
|
+
// Add agent bindings
|
|
244
|
+
Counter: DurableObjectNamespace<Counter>;
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## 8. Connect from the Frontend
|
|
251
|
+
|
|
252
|
+
### React
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
import { useAgent } from "agents/react";
|
|
256
|
+
|
|
257
|
+
type CounterState = { count: number };
|
|
258
|
+
|
|
259
|
+
function CounterWidget() {
|
|
260
|
+
const agent = useAgent<CounterState>({
|
|
261
|
+
agent: "Counter"
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<div>
|
|
266
|
+
<span>{agent.state?.count ?? 0}</span>
|
|
267
|
+
<button onClick={() => agent.stub.increment()}>+</button>
|
|
268
|
+
<button onClick={() => agent.stub.decrement()}>-</button>
|
|
269
|
+
</div>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Vanilla JavaScript
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
import { AgentClient } from "agents/client";
|
|
278
|
+
|
|
279
|
+
const agent = new AgentClient({
|
|
280
|
+
agent: "Counter",
|
|
281
|
+
name: "user-123", // Optional: unique instance name
|
|
282
|
+
host: window.location.host,
|
|
283
|
+
onStateUpdate: (state) => {
|
|
284
|
+
// Update the DOM when state changes
|
|
285
|
+
document.getElementById("count").textContent = String(state.count);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// Call methods — agent.state is also readable directly
|
|
290
|
+
document.getElementById("increment").onclick = () => agent.call("increment");
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Adding Multiple Agents
|
|
296
|
+
|
|
297
|
+
Add more agents by extending the configuration:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
// src/agents/chat.ts
|
|
301
|
+
export class Chat extends Agent<Env, ChatState> {
|
|
302
|
+
// ...
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/agents/scheduler.ts
|
|
306
|
+
export class Scheduler extends Agent<Env> {
|
|
307
|
+
// ...
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Update `wrangler.jsonc`:
|
|
312
|
+
|
|
313
|
+
```jsonc
|
|
314
|
+
{
|
|
315
|
+
"durable_objects": {
|
|
316
|
+
"bindings": [
|
|
317
|
+
{ "name": "Counter", "class_name": "Counter" },
|
|
318
|
+
{ "name": "Chat", "class_name": "Chat" },
|
|
319
|
+
{ "name": "Scheduler", "class_name": "Scheduler" }
|
|
320
|
+
]
|
|
321
|
+
},
|
|
322
|
+
"migrations": [
|
|
323
|
+
{
|
|
324
|
+
"tag": "v1",
|
|
325
|
+
"new_sqlite_classes": ["Counter", "Chat", "Scheduler"]
|
|
326
|
+
}
|
|
327
|
+
]
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Export all agents from your entry point:
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
export { Counter } from "./agents/counter";
|
|
335
|
+
export { Chat } from "./agents/chat";
|
|
336
|
+
export { Scheduler } from "./agents/scheduler";
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## Common Integration Patterns
|
|
342
|
+
|
|
343
|
+
### Agents Behind Authentication
|
|
344
|
+
|
|
345
|
+
Check auth before routing to agents:
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
export default {
|
|
349
|
+
async fetch(request: Request, env: Env) {
|
|
350
|
+
// Check auth for agent routes
|
|
351
|
+
if (request.url.includes("/agents/")) {
|
|
352
|
+
const authResult = await checkAuth(request, env);
|
|
353
|
+
if (!authResult.valid) {
|
|
354
|
+
return new Response("Unauthorized", { status: 401 });
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const agentResponse = await routeAgentRequest(request, env);
|
|
359
|
+
if (agentResponse) return agentResponse;
|
|
360
|
+
|
|
361
|
+
// ... rest of routing
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Custom Agent Path Prefix
|
|
367
|
+
|
|
368
|
+
By default, agents are routed at `/agents/{agent-name}/{instance-name}`. You can customize this:
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
import { routeAgentRequest } from "agents";
|
|
372
|
+
|
|
373
|
+
const agentResponse = await routeAgentRequest(request, env, {
|
|
374
|
+
prefix: "/api/agents" // Now routes at /api/agents/{agent-name}/{instance-name}
|
|
375
|
+
});
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Accessing Agents from Server Code
|
|
379
|
+
|
|
380
|
+
You can interact with agents directly from your Worker code:
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
import { getAgentByName } from "agents";
|
|
384
|
+
|
|
385
|
+
export default {
|
|
386
|
+
async fetch(request: Request, env: Env) {
|
|
387
|
+
if (request.url.endsWith("/api/increment")) {
|
|
388
|
+
// Get a specific agent instance
|
|
389
|
+
const counter = await getAgentByName(env.Counter, "shared-counter");
|
|
390
|
+
const newCount = await counter.increment();
|
|
391
|
+
return Response.json({ count: newCount });
|
|
392
|
+
}
|
|
393
|
+
// ...
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## Troubleshooting
|
|
401
|
+
|
|
402
|
+
### "Agent not found" or 404 errors
|
|
403
|
+
|
|
404
|
+
1. **Check the export** - Agent class must be exported from your main entry point
|
|
405
|
+
2. **Check the binding** - `class_name` in `wrangler.jsonc` must match the exported class name exactly
|
|
406
|
+
3. **Check the route** - Default route is `/agents/{agent-name}/{instance-name}`
|
|
407
|
+
|
|
408
|
+
### "No such Durable Object class" error
|
|
409
|
+
|
|
410
|
+
Add the migration to `wrangler.jsonc`:
|
|
411
|
+
|
|
412
|
+
```jsonc
|
|
413
|
+
"migrations": [
|
|
414
|
+
{
|
|
415
|
+
"tag": "v1",
|
|
416
|
+
"new_sqlite_classes": ["YourAgentClass"]
|
|
417
|
+
}
|
|
418
|
+
]
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### WebSocket connection fails
|
|
422
|
+
|
|
423
|
+
Ensure your routing passes the response through unchanged:
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
// ✅ Correct - return the response directly
|
|
427
|
+
const agentResponse = await routeAgentRequest(request, env);
|
|
428
|
+
if (agentResponse) return agentResponse;
|
|
429
|
+
|
|
430
|
+
// ❌ Wrong - don't wrap or modify the response
|
|
431
|
+
const agentResponse = await routeAgentRequest(request, env);
|
|
432
|
+
if (agentResponse) return new Response(agentResponse.body); // Breaks WebSocket
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### State not persisting
|
|
436
|
+
|
|
437
|
+
Check that:
|
|
438
|
+
|
|
439
|
+
1. You're using `this.setState()`, not mutating `this.state` directly
|
|
440
|
+
2. The agent class is in `new_sqlite_classes` in migrations
|
|
441
|
+
3. You're connecting to the same agent instance name
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## Next Steps
|
|
446
|
+
|
|
447
|
+
- [State Management](./state.md) - Deep dive into agent state
|
|
448
|
+
- [Scheduling](./scheduling.md) - Background tasks and cron jobs
|
|
449
|
+
- [Agent Class](./agent-class.md) - Full lifecycle and methods
|
|
450
|
+
- [Client SDK](./client-sdk.md) - Complete client API reference
|