From f6f2f0b241f167cd481fd9457d538544fe4afc2e Mon Sep 17 00:00:00 2001 From: scuffi Date: Fri, 23 Oct 2026 10:53:08 +0100 Subject: [PATCH 1/3] [Agents] Add changelog for resumable Computer JavaScript runs --- ...2026-10-23-computer-durable-javascript.mdx | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/content/changelog/agents/2026-10-23-computer-durable-javascript.mdx 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..533aede574b --- /dev/null +++ b/src/content/changelog/agents/2026-10-23-computer-durable-javascript.mdx @@ -0,0 +1,89 @@ +--- +title: "@cloudflare/computer resumes JavaScript runs after deploys and pauses them for approvals" +description: Isolate JavaScript runs in @cloudflare/computer can now survive deploys and crashes, and pause until someone approves an action. +products: + - agents + - workers +date: 2026-10-23 +--- + +import { TypeScriptExample } from "~/components"; + +[`@cloudflare/computer`](https://github.com/cloudflare/computer) v0.7.0 can resume isolate JavaScript runs after a deploy or crash, and lets a run pause until someone answers, for example to approve an action. Both features are experimental. + +Until now, a deploy or a Durable Object restart ended every run in flight. Long agent tasks, and tasks that need a person to sign off, had to fit between deploys. + +## Resume interrupted runs + +Set `onInterrupt: "replay"` on `WorkerJavaScriptBackend`, or on a single run. The code the agent runs does not need to change. + + + +```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", + }), + ], + }); +} +``` + + + +The backend writes every filesystem call, host module call, and `fetch` response to a journal in SQLite. After a restart, the run starts again from the top. Calls it already made are answered from the journal, in their original order, and its output is not repeated. Once the run catches up, it continues live. Clocks, timers, and random values repeat on replay, so the run takes the same path. + +A call that was still running at the restart runs again. Each host module call gets a `context.callId` that stays the same across that rerun, so you can use it as an idempotency key. To fail the run instead, wrap the function in `unsafeToRepeat()`. + +Nothing wakes a Durable Object after a deploy. Interrupted runs resume on the next runtime call, or when your alarm calls `workspace.runtime.recover()`. For code that is safe to repeat, `onInterrupt: "restart"` starts the run again from the beginning instead. + +## Wait for an answer + +A host module function can call `ctx.interrupt()` to pause a replayable run until someone answers. The code in the isolate calls the function as usual and gets its result. + + + +```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 }); + }, + }, + }, +}); +``` + + + +A waiting run holds no isolate, and time spent waiting does not count toward its timeout. The wait also survives deploys. List the waiting calls with `workspace.runtime.pending()`, and answer one with `respond()`: + + + +```ts +for (const call of await workspace.runtime.pending()) { + await workspace.runtime.respond(call.id, call.callId, { approved: true }); +} +``` + + + +Once every waiting call in the run has an answer, the run replays to the call, and `ctx.interrupt()` returns the answer. To notify someone when a call starts waiting, give the backend an `onWait` function. + +For setup, current limitations, and how replay works, refer to [Resume runs after a restart](/agents/tools/filesystem/#resume-runs-after-a-restart). From dfecb7866488cc3a10eb70f30e05a8c096a21596 Mon Sep 17 00:00:00 2001 From: scuffi Date: Fri, 23 Oct 2026 11:09:45 +0100 Subject: [PATCH 2/3] [Agents] Drop journal details from the Computer changelog --- .../agents/2026-10-23-computer-durable-javascript.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index 533aede574b..dc98149aed0 100644 --- a/src/content/changelog/agents/2026-10-23-computer-durable-javascript.mdx +++ b/src/content/changelog/agents/2026-10-23-computer-durable-javascript.mdx @@ -39,7 +39,7 @@ export class MyAgent extends DurableObject { -The backend writes every filesystem call, host module call, and `fetch` response to a journal in SQLite. After a restart, the run starts again from the top. Calls it already made are answered from the journal, in their original order, and its output is not repeated. Once the run catches up, it continues live. Clocks, timers, and random values repeat on replay, so the run takes the same path. +After a restart, the run carries on from where it was, without repeating calls it already made or printing its output twice. A call that was still running at the restart runs again. Each host module call gets a `context.callId` that stays the same across that rerun, so you can use it as an idempotency key. To fail the run instead, wrap the function in `unsafeToRepeat()`. @@ -84,6 +84,6 @@ for (const call of await workspace.runtime.pending()) { -Once every waiting call in the run has an answer, the run replays to the call, and `ctx.interrupt()` returns the answer. To notify someone when a call starts waiting, give the backend an `onWait` function. +Once every waiting call in the run has an answer, the run continues, and `ctx.interrupt()` returns the answer. To notify someone when a call starts waiting, give the backend an `onWait` function. For setup, current limitations, and how replay works, refer to [Resume runs after a restart](/agents/tools/filesystem/#resume-runs-after-a-restart). From 7fbea76cfbaf4e0a424ed3cecdd8247b1af4925c Mon Sep 17 00:00:00 2001 From: scuffi Date: Fri, 23 Oct 2026 15:12:47 +0100 Subject: [PATCH 3/3] [Agents] Tighten the Computer changelog wording --- ...2026-10-23-computer-durable-javascript.mdx | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) 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 index dc98149aed0..4732e472ac9 100644 --- a/src/content/changelog/agents/2026-10-23-computer-durable-javascript.mdx +++ b/src/content/changelog/agents/2026-10-23-computer-durable-javascript.mdx @@ -1,6 +1,6 @@ --- -title: "@cloudflare/computer resumes JavaScript runs after deploys and pauses them for approvals" -description: Isolate JavaScript runs in @cloudflare/computer can now survive deploys and crashes, and pause until someone approves an action. +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 @@ -9,13 +9,11 @@ date: 2026-10-23 import { TypeScriptExample } from "~/components"; -[`@cloudflare/computer`](https://github.com/cloudflare/computer) v0.7.0 can resume isolate JavaScript runs after a deploy or crash, and lets a run pause until someone answers, for example to approve an action. Both features are experimental. - -Until now, a deploy or a Durable Object restart ended every run in flight. Long agent tasks, and tasks that need a person to sign off, had to fit between deploys. +[`@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. The code the agent runs does not need to change. +Set `onInterrupt: "replay"` on `WorkerJavaScriptBackend`, or on a single run. @@ -41,13 +39,11 @@ export class MyAgent extends DurableObject { After a restart, the run carries on from where it was, without repeating calls it already made or printing its output twice. -A call that was still running at the restart runs again. Each host module call gets a `context.callId` that stays the same across that rerun, so you can use it as an idempotency key. To fail the run instead, wrap the function in `unsafeToRepeat()`. - -Nothing wakes a Durable Object after a deploy. Interrupted runs resume on the next runtime call, or when your alarm calls `workspace.runtime.recover()`. For code that is safe to repeat, `onInterrupt: "restart"` starts the run again from the beginning instead. +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 an answer +## Wait for approvals -A host module function can call `ctx.interrupt()` to pause a replayable run until someone answers. The code in the isolate calls the function as usual and gets its result. +A host module function can call `ctx.interrupt()` to pause a replay-able run until it receives an answer. @@ -72,7 +68,7 @@ new WorkerJavaScriptBackend({ -A waiting run holds no isolate, and time spent waiting does not count toward its timeout. The wait also survives deploys. List the waiting calls with `workspace.runtime.pending()`, and answer one with `respond()`: +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()`: @@ -84,6 +80,6 @@ for (const call of await workspace.runtime.pending()) { -Once every waiting call in the run has an answer, the run continues, and `ctx.interrupt()` returns the answer. To notify someone when a call starts waiting, give the backend an `onWait` function. +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).