UNPKG

agents

Version:
256 lines (254 loc) 8.41 kB
import "./email-XHsSYsTO.js"; import "./internal_context-D9eKFth1.js"; import "./client-CtC9E06G.js"; import "./do-oauth-client-provider-DDg8QrEA.js"; import { o as getAgentByName } from "./src-i_UcyBYf.js"; import { WorkflowRejectedError } from "./workflow-types.js"; import { WorkflowEntrypoint } from "cloudflare:workers"; //#region src/workflows.ts /** * AgentWorkflow - Base class for Workflows that integrate with Agents * * Extends Cloudflare's WorkflowEntrypoint to provide seamless access to * the Agent that started the workflow, enabling bidirectional communication. * * @example * ```typescript * import { AgentWorkflow } from 'agents/workflows'; * import type { MyAgent } from './agent'; * * type TaskParams = { taskId: string; data: string }; * * export class ProcessingWorkflow extends AgentWorkflow<MyAgent, TaskParams> { * async run(event: AgentWorkflowEvent<TaskParams>, step: WorkflowStep) { * // Access the originating Agent via typed RPC * await this.agent.updateTaskStatus(event.payload.taskId, 'processing'); * * const result = await step.do('process', async () => { * // ... processing logic * return { processed: true }; * }); * * // Report progress to Agent (typed) * await this.reportProgress({ step: 'process', status: 'complete', percent: 0.5 }); * * // Broadcast to connected clients * await this.broadcastToClients({ type: 'progress', data: result }); * * return result; * } * } * ``` */ /** * WeakSet to track which prototypes have been wrapped. * This prevents re-wrapping on subsequent instantiations of the same class. */ const wrappedPrototypes = /* @__PURE__ */ new WeakSet(); /** * Base class for Workflows that need access to their originating Agent. * * @template AgentType - The Agent class type (for typed RPC access) * @template Params - User-defined params passed to the workflow (optional) * @template ProgressType - Type for progress reporting (defaults to DefaultProgress) * @template Env - Environment type (defaults to Cloudflare.Env) */ var AgentWorkflow = class extends WorkflowEntrypoint { constructor(ctx, env) { super(ctx, env); this.__agentInitCalled = false; const proto = Object.getPrototypeOf(this); if (Object.hasOwn(proto, "run") && !wrappedPrototypes.has(proto)) { const originalRun = proto.run; proto.run = async function(event, step) { if (!this.__agentInitCalled) { const { __agentName, __agentBinding, __workflowName, ...userParams } = event.payload; await this._initAgent(__agentName, __agentBinding, __workflowName, event.instanceId); this.__agentInitCalled = true; const cleanedEvent = { ...event, payload: userParams }; const wrappedStep = this._wrapStep(step); return originalRun.call(this, cleanedEvent, wrappedStep); } return originalRun.call(this, event, step); }; wrappedPrototypes.add(proto); } } /** * Initialize the Agent stub from workflow params. * Called automatically before run() executes. */ async _initAgent(agentName, agentBinding, workflowName, instanceId) { if (!agentName || !agentBinding || !workflowName) throw new Error("AgentWorkflow requires __agentName, __agentBinding, and __workflowName in params. Use agent.runWorkflow() to start workflows with proper agent context."); this._workflowId = instanceId; this._workflowName = workflowName; const namespace = this.env[agentBinding]; if (!namespace) throw new Error(`Agent binding '${agentBinding}' not found in environment`); this._agent = await getAgentByName(namespace, agentName); } /** * Wrap WorkflowStep with durable Agent communication methods. * Methods added to the wrapped step are idempotent and won't repeat on retry. * * Note: We add methods directly to the step object to preserve instanceof checks * that Cloudflare's runtime may perform on the WorkflowStep class. */ _wrapStep(step) { let stepCounter = 0; const wrappedStep = step; wrappedStep.reportComplete = async (result) => { await step.do(`__agent_reportComplete_${stepCounter++}`, async () => { await this.notifyAgent({ workflowName: this._workflowName, workflowId: this._workflowId, type: "complete", result, timestamp: Date.now() }); }); }; wrappedStep.reportError = async (error) => { const errorMessage = error instanceof Error ? error.message : error; await step.do(`__agent_reportError_${stepCounter++}`, async () => { await this.notifyAgent({ workflowName: this._workflowName, workflowId: this._workflowId, type: "error", error: errorMessage, timestamp: Date.now() }); }); }; wrappedStep.sendEvent = async (event) => { await step.do(`__agent_sendEvent_${stepCounter++}`, async () => { await this.notifyAgent({ workflowName: this._workflowName, workflowId: this._workflowId, type: "event", event, timestamp: Date.now() }); }); }; wrappedStep.updateAgentState = async (state) => { await step.do(`__agent_updateState_${stepCounter++}`, async () => { this.agent._workflow_updateState("set", state); }); }; wrappedStep.mergeAgentState = async (partialState) => { await step.do(`__agent_mergeState_${stepCounter++}`, async () => { this.agent._workflow_updateState("merge", partialState); }); }; wrappedStep.resetAgentState = async () => { await step.do(`__agent_resetState_${stepCounter++}`, async () => { this.agent._workflow_updateState("reset"); }); }; return wrappedStep; } /** * Get the Agent stub for RPC calls. * Provides typed access to the Agent's methods. * * @example * ```typescript * // Call any public method on the Agent * await this.agent.updateStatus('processing'); * const data = await this.agent.getData(); * ``` */ get agent() { if (!this._agent) throw new Error("Agent not initialized. Ensure you're accessing this.agent inside run()."); return this._agent; } /** * Get the workflow instance ID */ get workflowId() { return this._workflowId; } /** * Get the workflow binding name */ get workflowName() { return this._workflowName; } /** * Send a notification to the Agent via RPC. * * @param callback - Callback payload to send */ async notifyAgent(callback) { await this.agent._workflow_handleCallback(callback); } /** * Report progress to the Agent with typed progress data. * Triggers onWorkflowProgress() on the Agent. * * @param progress - Typed progress data * * @example * ```typescript * // Using default progress type * await this.reportProgress({ step: 'fetch', status: 'running' }); * await this.reportProgress({ step: 'fetch', status: 'complete', percent: 0.5 }); * * // With custom progress type * await this.reportProgress({ stage: 'extract', recordsProcessed: 100 }); * ``` */ async reportProgress(progress) { await this.notifyAgent({ workflowName: this._workflowName, workflowId: this._workflowId, type: "progress", progress, timestamp: Date.now() }); } /** * Broadcast a message to all connected WebSocket clients via the Agent. * This is non-durable and may repeat on workflow retry. * * @param message - Message to broadcast (will be JSON-stringified) */ broadcastToClients(message) { this.agent._workflow_broadcast(message); } /** * Wait for approval from the Agent. * Handles rejection by reporting error (durably) and throwing WorkflowRejectedError. * * @param step - AgentWorkflowStep object * @param options - Wait options (timeout, eventType, stepName) * @returns Approval payload (throws WorkflowRejectedError if rejected) * * @example * ```typescript * const approval = await this.waitForApproval(step, { timeout: '7 days' }); * // approval contains the payload from approveWorkflow() * ``` */ async waitForApproval(step, options) { const stepName = options?.stepName ?? "wait-for-approval"; const eventType = options?.eventType ?? "approval"; const timeout = options?.timeout; const payload = (await step.waitForEvent(stepName, { type: eventType, timeout })).payload; if (!payload.approved) { const reason = payload.reason; await step.reportError(reason ?? "Workflow rejected"); throw new WorkflowRejectedError(reason, this._workflowId); } return payload.metadata; } }; //#endregion export { AgentWorkflow, WorkflowRejectedError }; //# sourceMappingURL=workflows.js.map