ai-sdk-cloudflare-workflow-harness
v0.1.2
Published
Run an @ai-sdk/harness agent as a durable workflow on Cloudflare Workflows.
Maintainers
Readme
AI SDK - Cloudflare Workflow Harness
This package is experimental.
Drive an AI SDK HarnessAgent turn as a durable workflow on Cloudflare Workflows, dividing it into time slices or semantic agent steps. Each slice is a step.do.
The agent runs its work (Claude Code, Codex) in a sandbox; the workflow orchestrates that turn durably. The turn state machine, step.do checkpointing, resume across slices, and reattachment to a still running sandbox are verified on the live Cloudflare Workflows runtime.
Runtime. The agent's orchestrator runs in the Worker and drives the agent's process in the sandbox, so the orchestrator, its adapter, and its sandbox provider must run in workerd. Today the shipping
@ai-sdk/harness-claude-codeand Codex adapters read their bundled bridge assets from the filesystem and connect over thewspackage, and thelocal,docker, and bridge sandbox providers rely on host process or Node APIs. None of those run in workerd yet, so Claude Code and Codex on Cloudflare Workflows are not yet possible. The durable plumbing is ready for a workerd-compatible agent and sandbox provider.
This is the Cloudflare counterpart to @ai-sdk/workflow-harness. That package runs on Vercel's Workflow DevKit; this one runs on the Cloudflare Workflows engine (WorkflowEntrypoint, step.do) instead. The turn state machine is the same, so the two read alike. There is no dependency on the workflow DevKit.
Each slice runs in its own step.do and returns a serializable state object, which Cloudflare Workflows persists as the durable checkpoint between steps. Time slices let a long turn checkpoint and reattach to the still-running sandbox. Semantic steps persist after each agent step, typically by configuring the agent with stopWhen: isStepCount(1).
This package ships plain helpers; you own the thin WorkflowEntrypoint. Keep the agent, its sandbox provider, and other Node-heavy dependencies out of any code path that has to stay lightweight by importing the agent inside the step body.
Setup
npm i ai-sdk-cloudflare-workflow-harness @ai-sdk/harnessRequires a Workers project with Workflows enabled, and a workerd-compatible HarnessAgent (see the runtime note above). The agent's sandbox provider must also run in workerd; host-process providers like ai-sdk-sandbox-local and the docker CLI provider do not.
Usage
agent.ts:
import { HarnessAgent } from '@ai-sdk/harness/agent';
import { claudeCode } from '@ai-sdk/harness-claude-code';
export function createAgent(env: Env) {
return new HarnessAgent({
harness: claudeCode,
sandbox: createSandbox(env), // your sandbox provider
});
}agent-workflow.ts:
import {
WorkflowEntrypoint,
type WorkflowEvent,
type WorkflowStep,
} from 'cloudflare:workers';
import {
runHarnessWorkflow,
durableObjectResumeStore,
nullWritable,
type HarnessWorkflowInput,
} from 'ai-sdk-cloudflare-workflow-harness';
export class AgentWorkflow extends WorkflowEntrypoint<Env, HarnessWorkflowInput> {
async run(event: WorkflowEvent<HarnessWorkflowInput>, step: WorkflowStep) {
const { createAgent } = await import('./agent');
return runHarnessWorkflow({
agent: createAgent(this.env),
input: event.payload, // { sessionId, prompt, resumeFrom? }
step,
writable: nullWritable(), // or a DO WebSocket / SSE sink
store: durableObjectResumeStore(this.env.SESSIONS.get(id).storage),
});
}
}wrangler.toml:
[[workflows]]
name = "agent"
binding = "AGENT_WORKFLOW"
class_name = "AgentWorkflow"Start a run from a fetch handler with env.AGENT_WORKFLOW.create({ params }), reusing the conversation id as sessionId so each user turn resumes the same warm session.
Driving the loop yourself
runHarnessWorkflow is the loop. To control each step, call the lower-level helpers and continue while the status is ready_for_next_step:
import {
createHarnessWorkflowState,
finalizeHarnessWorkflow,
runHarnessAgentStep,
} from 'ai-sdk-cloudflare-workflow-harness';
let state = createHarnessWorkflowState(event.payload);
let i = 0;
do {
const current = state;
state = await step.do(`agent:${i++}`, () =>
runHarnessAgentStep({ agent, state: current, writable }),
);
} while (state.status === 'ready_for_next_step');
return finalizeHarnessWorkflow(state);Use runHarnessAgentStep for semantic steps (configure the agent with stopWhen: isStepCount(1)) or runHarnessAgentTimeSlice for wall-clock slices.
Resume across turns
Within one run, step.do return values carry state between steps. A ResumeStore is what lets a later run, a new user turn, reattach to the same warm conversation. It is the Cloudflare stand-in for the DevKit's implicit persistence.
import { durableObjectResumeStore, kvResumeStore } from 'ai-sdk-cloudflare-workflow-harness';
const store = durableObjectResumeStore(this.env.SESSIONS.get(id).storage);
// or
const store = kvResumeStore(this.env.SESSIONS_KV);Pass it to runHarnessWorkflow and it loads the coordinate before the turn and saves the next one after. ResumeStore is a three-method interface (load, save, clear); implement it over any backend.
Options
runHarnessWorkflow takes agent, input, step, and writable. The rest are optional:
storepersist and reattach the warm session across turns.timeSliceSecondswall-clock budget per slice. Defaults to 750.destroyOnFinishrelease the sandbox when the turn finishes. Defaults to false, so the next turn resumes.stepConfigoverride thestep.doretry and timeout for each slice.
License
Apache-2.0
Portions derived from @ai-sdk/workflow-harness (Apache-2.0).
