diff --git a/src/content/changelog/agents/2026-10-23-computer-durable-javascript.mdx b/src/content/changelog/agents/2026-10-23-computer-durable-javascript.mdx new file mode 100644 index 00000000000..4732e472ac9 --- /dev/null +++ b/src/content/changelog/agents/2026-10-23-computer-durable-javascript.mdx @@ -0,0 +1,85 @@ +--- +title: "@cloudflare/computer JavaScript runs can now be paused and resumed" +description: Isolate JavaScript runs in @cloudflare/computer can now survive deploys and crashes as well as pausing on approvals or out of band checks. +products: + - agents + - workers +date: 2026-10-23 +--- + +import { TypeScriptExample } from "~/components"; + +[`@cloudflare/computer`](https://github.com/cloudflare/computer) v0.7.0 now supports resuming a JavaScript run after a deploy or crash. It also supports pausing a run on an external action, for example to approve a tool call. + +## Resume interrupted runs + +Set `onInterrupt: "replay"` on `WorkerJavaScriptBackend`, or on a single run. + + + +```ts +import { DurableObject } from "cloudflare:workers"; +import { Workspace } from "@cloudflare/computer"; +import { WorkerJavaScriptBackend } from "@cloudflare/computer/backends/worker-javascript"; + +export class MyAgent extends DurableObject { + workspace = new Workspace({ + storage: this.ctx.storage, + backends: [ + new WorkerJavaScriptBackend({ + loader: this.env.LOADER, + onInterrupt: "replay", + }), + ], + }); +} +``` + + + +After a restart, the run carries on from where it was, without repeating calls it already made or printing its output twice. + +Interrupted runs resume on the next call or by calling `workspace.runtime.recover()`. Passing `onInterrupt: "restart"` starts the run again from the beginning. + +## Wait for approvals + +A host module function can call `ctx.interrupt()` to pause a replay-able run until it receives an answer. + + + +```ts +new WorkerJavaScriptBackend({ + loader: this.env.LOADER, + onInterrupt: "replay", + modules: { + "ws:github": { + postComment: async ([issue, body], ctx) => { + const { approved } = await ctx.interrupt<{ approved: boolean }>({ + issue, + body, + }); + if (!approved) throw new Error("The comment was rejected."); + return github.comment(issue, body, { idempotencyKey: ctx.callId }); + }, + }, + }, +}); +``` + + + +The time spent waiting does not count toward its timeout. The wait also survives deploys. Fetch the pending calls with `workspace.runtime.pending()` and resolve them with `respond()`: + + + +```ts +for (const call of await workspace.runtime.pending()) { + await workspace.runtime.respond(call.id, call.callId, { approved: true }); +} +``` + + + +The run continues once all pending calls are resolved. The `onWait` option can be passed to the backend to listen for and act on pending calls. + +For setup, current limitations, and how replay works, refer to [Resume runs after a restart](/agents/tools/filesystem/#resume-runs-after-a-restart).