{"id":"src_Q3HlRau9xWBnzIb8lD9g3","slug":"cloudflare-developer-platform","name":"Developer Platform Changelog","type":"feed","url":"https://developers.cloudflare.com/changelog/?area=developer-platform","orgId":"org_fW6EY8PY8Cr42ifo1IUAm","productId":"prod_NroPjNpPM2OEO0xikqyeG","productSlug":"developer-platform","org":{"id":"org_fW6EY8PY8Cr42ifo1IUAm","slug":"cloudflare","name":"Cloudflare"},"isPrimary":false,"isHidden":false,"discovery":"curated","fetchPriority":"normal","metadata":"{\"feedUrl\":\"https://developers.cloudflare.com/changelog/rss/developer-platform.xml\",\"feedType\":\"rss\",\"feedDiscoveredAt\":\"2026-06-19T21:04:40.128Z\",\"noFeedFound\":false,\"feedContentDepth\":\"full\",\"feedEtag\":\"\\\"ebe5fc8c84990a4994b33e34a775485f\\\"\",\"feedContentLength\":\"5391703\",\"enrichment\":{\"consecutiveFailures\":1},\"sourceActor\":{\"nextAlarmAt\":\"2026-11-03T12:37:24.683Z\",\"lastAlarmAt\":\"2026-11-03T08:37:25.531Z\",\"managed\":true}}","notice":null,"kind":null,"stars":null,"starsFetchedAt":null,"releaseCount":438,"releasesLast30Days":52,"avgReleasesPerWeek":12,"latestVersion":null,"latestDate":"2026-11-02T00:00:00.000Z","changelogUrl":null,"hasChangelogFile":false,"lastFetchedAt":"2026-11-03T04:37:39.652Z","lastPolledAt":"2026-11-03T08:40:47.739Z","changeDetectedAt":null,"trackingSince":"2025-11-21T00:00:00.000Z","releases":[{"id":"rel_uGz-W6OtCw05PsDLQ1pOi","version":null,"type":"feature","title":"Workers - Request.signal will be enabled by default in Workers on November 17","summary":"Workers with a compatibility date of 2026-11-17 or later receive client disconnection events through request.signal by default, removing the need for the enable_request_signal flag. Existing Workers keep current behavior until their compatibility date changes; the new date also supports opting out with disable_request_signal, and does not enable request_signal_passthrough.","titleGenerated":"Cloudflare Workers enables request.signal by default from compatibility date 2026-11-17","titleShort":"request.signal now default for 2026-11-17+ dates","breaking":"minor","importance":4,"content":"Starting November 17, 2026, Workers with a [compatibility date](https://developers.cloudflare.com/workers/configuration/compatibility-dates/) of `2026-11-17` or later will receive client disconnection events through `request.signal` by default. Use these events to stop unnecessary work when a client leaves. You will no longer need the `enable_request_signal` compatibility flag for these dates.\n\nWe [introduced this feature in May 2025](https://developers.cloudflare.com/changelog/post/2025-05-22-handle-request-cancellation/). You can log cancellations, clean up resources, or cancel operations that accept an `AbortSignal`.\n\nFor example, this Worker logs a client disconnection and cancels an outgoing request:\n\nsrc/index.jsjs\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-1itgoe\"> default</span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\tasync</span><span class=\"nb-shiki-1t8gfj\"> fetch</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-1jdh33\">request</span><span class=\"nb-shiki-140thh\">) {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\trequest.signal.</span><span class=\"nb-shiki-1t8gfj\">addEventListener</span><span class=\"nb-shiki-140thh\">(</span></span>\n<span class=\"line\"><span class=\"nb-shiki-mdbnqw\">\t\t\t\"abort\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t() </span><span class=\"nb-shiki-1itgoe\">=></span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tconsole.</span><span class=\"nb-shiki-1t8gfj\">log</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">\"Client disconnected\"</span><span class=\"nb-shiki-140thh\">);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t{ once: </span><span class=\"nb-shiki-dzsirb\">true</span><span class=\"nb-shiki-140thh\"> },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\treturn</span><span class=\"nb-shiki-1t8gfj\"> fetch</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">\"https://example.com/slow-response\"</span><span class=\"nb-shiki-140thh\">, {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\tsignal: request.signal,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t});</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">};</span></span>\n```\n\nsrc/index.tsts\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-1itgoe\"> default</span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\tasync</span><span class=\"nb-shiki-1t8gfj\"> fetch</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-1jdh33\">request</span><span class=\"nb-shiki-140thh\">)</span><span class=\"nb-shiki-1itgoe\">:</span><span class=\"nb-shiki-1t8gfj\"> Promise</span><span class=\"nb-shiki-140thh\"><</span><span class=\"nb-shiki-1t8gfj\">Response</span><span class=\"nb-shiki-140thh\">> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\trequest.signal.</span><span class=\"nb-shiki-1t8gfj\">addEventListener</span><span class=\"nb-shiki-140thh\">(</span></span>\n<span class=\"line\"><span class=\"nb-shiki-mdbnqw\">\t\t\t\"abort\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t() </span><span class=\"nb-shiki-1itgoe\">=></span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tconsole.</span><span class=\"nb-shiki-1t8gfj\">log</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">\"Client disconnected\"</span><span class=\"nb-shiki-140thh\">);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t{ once: </span><span class=\"nb-shiki-dzsirb\">true</span><span class=\"nb-shiki-140thh\"> },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\treturn</span><span class=\"nb-shiki-1t8gfj\"> fetch</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">\"https://example.com/slow-response\"</span><span class=\"nb-shiki-140thh\">, {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\tsignal: request.signal,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t});</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">} </span><span class=\"nb-shiki-1itgoe\">satisfies</span><span class=\"nb-shiki-1t8gfj\"> ExportedHandler</span><span class=\"nb-shiki-140thh\">;</span></span>\n```\n\nExisting Workers keep their current behavior until you change their compatibility date or flags. To try this feature before November 17, keep your current date and add `enable_request_signal` to your [compatibility flags](https://developers.cloudflare.com/workers/configuration/compatibility-flags/).\n\nWith a compatibility date of `2026-11-17` or later, add `disable_request_signal` to opt out. This change does not enable `request_signal_passthrough`. Pass the signal explicitly, as the example does.\n\nFor more information, refer to the [Request API](https://developers.cloudflare.com/workers/runtime-apis/request/#properties) and the [Request.signal compatibility flag](https://developers.cloudflare.com/workers/configuration/compatibility-flags/#enable-requestsignal-for-incoming-requests).","publishedAt":"2026-11-02T00:00:00.000Z","fetchedAt":"2026-11-02T20:30:43.948Z","url":"https://developers.cloudflare.com/changelog/post/2026-11-02-request-signal-default/","media":[],"coverageCount":0},{"id":"rel_hHUyuYhFNi1fods1Wt8yL","version":null,"type":"feature","title":"Workers - Workers will use the new module registry by default on November 17","summary":"JavaScript Workers with a compatibility date of 2026-11-17 or later use the new module registry by default and no longer need the new_module_registry flag, while Python Workers stay on the legacy registry. The new registry rejects invalid import attributes and fails require() calls when the module or a dependency uses top-level await; add legacy_module_registry to keep the old behavior.","titleGenerated":"Cloudflare Workers switches to new module registry by default on November 17","titleShort":"New module registry becomes default for Workers; legacy flag available","breaking":"major","importance":4,"content":"Starting November 17, 2026, JavaScript Workers with a [compatibility date](https://developers.cloudflare.com/workers/configuration/compatibility-dates/) of `2026-11-17` or later will use the new module registry by default. You will no longer need the `new_module_registry` flag for these dates. Python Workers keep using the legacy registry.\n\nThe module registry finds and loads JavaScript modules in your Worker. We [announced the new registry in September 2026 ↗︎](https://blog.cloudflare.com/workers-module-registry-nodejs/). It improves compatibility with JavaScript standards and Node.js. In Workers using [Node.js compatibility](https://developers.cloudflare.com/workers/runtime-apis/nodejs/), you can use `require()` to load ES modules. The module and its dependencies must not use top-level `await`.\n\nYou can also use `import.meta` to get information about the current module and find the URL of another module without loading it:\n\nsrc/index.jsjs\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-1itgoe\"> default</span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1t8gfj\">\tfetch</span><span class=\"nb-shiki-140thh\">() {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\treturn</span><span class=\"nb-shiki-140thh\"> Response.</span><span class=\"nb-shiki-1t8gfj\">json</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\tmoduleUrl: </span><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\">.</span><span class=\"nb-shiki-dzsirb\">meta</span><span class=\"nb-shiki-140thh\">.url,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\tisEntrypoint: </span><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\">.</span><span class=\"nb-shiki-dzsirb\">meta</span><span class=\"nb-shiki-140thh\">.main,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\thelperUrl: </span><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\">.</span><span class=\"nb-shiki-dzsirb\">meta</span><span class=\"nb-shiki-140thh\">.</span><span class=\"nb-shiki-1t8gfj\">resolve</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">\"./helper.js\"</span><span class=\"nb-shiki-140thh\">),</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t});</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">};</span></span>\n```\n\nsrc/index.tsts\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-1itgoe\"> default</span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1t8gfj\">\tfetch</span><span class=\"nb-shiki-140thh\">()</span><span class=\"nb-shiki-1itgoe\">:</span><span class=\"nb-shiki-1t8gfj\"> Response</span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\treturn</span><span class=\"nb-shiki-140thh\"> Response.</span><span class=\"nb-shiki-1t8gfj\">json</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\tmoduleUrl: </span><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\">.</span><span class=\"nb-shiki-dzsirb\">meta</span><span class=\"nb-shiki-140thh\">.url,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\tisEntrypoint: </span><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\">.</span><span class=\"nb-shiki-dzsirb\">meta</span><span class=\"nb-shiki-140thh\">.main,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\thelperUrl: </span><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\">.</span><span class=\"nb-shiki-dzsirb\">meta</span><span class=\"nb-shiki-140thh\">.</span><span class=\"nb-shiki-1t8gfj\">resolve</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">\"./helper.js\"</span><span class=\"nb-shiki-140thh\">),</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t});</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">} </span><span class=\"nb-shiki-1itgoe\">satisfies</span><span class=\"nb-shiki-1t8gfj\"> ExportedHandler</span><span class=\"nb-shiki-140thh\">;</span></span>\n```\n\nWhen deployed with `src/index.ts` as the unbundled entrypoint, `isEntrypoint` is `true`. `helperUrl` is a URL within your Worker bundle. Resolving the URL does not check whether the module exists or load it over the network. Build tools may transform `import.meta`. To try this example directly, refer to [unbundled deployments](https://developers.cloudflare.com/workers/wrangler/configuration/#bundling).\n\nExisting Workers keep their current behavior until you change their compatibility date or flags. To try the new registry before November 17, keep your current date and add `new_module_registry` to your [compatibility flags](https://developers.cloudflare.com/workers/configuration/compatibility-flags/).\n\nBefore updating, check your dependencies for invalid import attributes and changes to `require()` behavior. The new registry rejects invalid import attributes. Calls to `require()` also fail if the module or any of its dependencies use top-level `await`. To keep the legacy registry with a compatibility date of `2026-11-17` or later, add `legacy_module_registry`.\n\nFor migration details, refer to the [new module registry compatibility flag](https://developers.cloudflare.com/workers/configuration/compatibility-flags/#new-module-registry), [bundling documentation](https://developers.cloudflare.com/workers/wrangler/bundling/#runtime-module-loading), and [Node.js compatibility documentation](https://developers.cloudflare.com/workers/runtime-apis/nodejs/).","publishedAt":"2026-11-02T00:00:00.000Z","fetchedAt":"2026-11-02T20:30:43.399Z","url":"https://developers.cloudflare.com/changelog/post/2026-11-02-module-registry-default/","media":[],"coverageCount":0},{"id":"rel_GTCl3GqMTfWiGY_CpeNfW","version":null,"type":"feature","title":"AI Search - AI Search billing postponed to December 1, 2026, and default sync interval is now 24 hours","summary":"AI Search billing now begins December 1, 2026 instead of November 1, 2026, with pricing and included usage unchanged. The default sync interval for AI Search instances changes from 6 hours to 24 hours, though any interval from 15 minutes to 24 hours remains selectable via the Sync interval setting or the sync_interval API field.","titleGenerated":"Cloudflare AI Search postpones billing to December 1, 2026 and raises default sync interval to 24 hours","titleShort":"AI Search billing pushed to Dec 1; default sync now 24h","breaking":"major","importance":4,"content":"#### Billing postponed to December 1, 2026\n\n[AI Search](https://developers.cloudflare.com/ai-search/) billing has been postponed by one month and now begins on December 1, 2026, instead of November 1, 2026. You will not be charged for AI Search usage before December 1, 2026. Pricing and included usage are unchanged.\n\nFor rates and included usage, refer to [Limits & pricing](https://developers.cloudflare.com/ai-search/platform/limits-pricing/#pricing).\n\n#### Default sync interval is now 24 hours\n\nThe default sync interval for AI Search instances is changing from 6 hours to 24 hours. You can still choose any supported interval, from 15 minutes to 24 hours, with the **Sync interval** setting in your instance settings or the `sync_interval` field in the API.\n\nFor details, refer to [Sync interval](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/#sync-interval).","publishedAt":"2026-11-02T00:00:00.000Z","fetchedAt":"2026-11-02T20:30:43.399Z","url":"https://developers.cloudflare.com/changelog/post/2026-11-02-billing-postponed-default-sync-interval/","media":[],"coverageCount":0},{"id":"rel_Jsw0YzmXqsbELmCrwJuZR","version":null,"type":"feature","title":"Flagship - Evaluate Flagship flags locally","summary":"Flagship bindings now evaluate flags against a local flag store during local development instead of calling the remote app, applying to wrangler dev, the Vite plugin, Vitest integration, and cf dev. The store starts empty so evaluations return the default value in code, and flags can be managed via --local in wrangler and cf commands, Local Explorer, or pulled from the remote app with wrangler flagship flags pull; set remote: true to keep evaluating remotely.","titleGenerated":"Cloudflare Flagship evaluates flags against a local store during local dev","titleShort":"Flagship flags evaluate locally; remote app now opt-in","breaking":"major","importance":3,"content":"[Flagship](https://developers.cloudflare.com/flagship/) bindings now evaluate flags against a local flag store during local development, instead of calling your remote app. You can develop offline and test flag changes without affecting production. This applies to [`wrangler dev`](https://developers.cloudflare.com/workers/wrangler/commands/general/#dev), the [Cloudflare Vite plugin](https://developers.cloudflare.com/workers/vite-plugin/), the [Vitest integration](https://developers.cloudflare.com/workers/testing/vitest-integration/), and [`cf dev`](https://developers.cloudflare.com/cf/projects/#start-development).\n\nThe local store starts empty, so evaluations return the default value you pass in code. Add and manage local flags in any of these ways:\n\n- **Wrangler** — Add `--local` to `wrangler flagship flags` commands, such as `create`, `update`, and `evaluate`. To copy all flags from your remote app, run `npx wrangler flagship flags pull <APP_ID>`.\n- **Local Explorer** — Create, edit, toggle, and evaluate flags in [Local Explorer](https://developers.cloudflare.com/workers/local-development/local-explorer/) while your dev server runs.\n- **`cf` CLI** — Add `--local` to [`cf flagship` commands](https://developers.cloudflare.com/flagship/configuration/#use-the-cf-cli).\n\nTo keep evaluating against your remote app instead, set [`remote: true`](https://developers.cloudflare.com/flagship/configuration/#use-the-remote-app-instead) on the binding.\n\nThe local flag store requires Wrangler v4.135.0 or `@cloudflare/vite-plugin` v1.56.0 or later. Flagship in Local Explorer requires Wrangler v4.148.0 or `@cloudflare/vite-plugin` v1.63.0 or later.\n\nRefer to [Flagship local development](https://developers.cloudflare.com/flagship/configuration/#local-development) for details.","publishedAt":"2026-11-02T00:00:00.000Z","fetchedAt":"2026-11-02T12:23:56.043Z","url":"https://developers.cloudflare.com/changelog/post/2026-11-02-local-flag-store/","media":[],"coverageCount":0},{"id":"rel_Ip6eLG4AbcHH2Jw1mo343","version":null,"type":"feature","title":"Browser Run - Browser Run now runs Chrome 152","summary":"Browser Run now runs Chrome 152 by default, and the default User-Agent for Quick Actions reports Chrome 152 instead of Chrome 119, so allowlist or User-Agent matching rules may need updating. Also fixes large response headers not loading, makes /links return absolute URLs while links-only crawls follow relative links, preserves code block languages in Markdown output, and removes distrusted root certificates.","titleGenerated":"Cloudflare Browser Run defaults to Chrome 152 with updated User-Agent","titleShort":"Chrome 152 default; User-Agent now reports 152","breaking":"minor","importance":3,"content":"[Browser Run](https://developers.cloudflare.com/browser-run/) now runs Chrome 152 by default. Your sessions get current web platform features, rendering fixes, and security patches. You do not need to change your code.\n\nThis release also includes the following improvements:\n\n- **Updated default User-Agent.** The default User-Agent for [Quick Actions](https://developers.cloudflare.com/browser-run/quick-actions/) now reports Chrome 152 instead of Chrome 119. If you allowlist or match the Browser Run User-Agent, update your rules. You can still set your own value with the `userAgent` parameter. Refer to [Automatic request headers](https://developers.cloudflare.com/browser-run/reference/automatic-request-headers/#user-agent).\n- **Better site compatibility.** Quick Actions requests now look more like requests from a regular Chrome browser, so fewer sites block them.\n- **Large response headers.** Pages that return large response headers now load correctly.\n- **Updated root certificates.** Browser Run removed distrusted root certificates from its trust store.\n- **Absolute links.** [`/links`](https://developers.cloudflare.com/browser-run/quick-actions/links-endpoint/) now returns absolute URLs, and links-only [crawls](https://developers.cloudflare.com/browser-run/quick-actions/crawl-endpoint/) now follow relative links.\n- **Better Markdown output.** [`/markdown`](https://developers.cloudflare.com/browser-run/quick-actions/markdown-endpoint/) and crawl Markdown output now keep code block languages.\n- **Clearer errors.** Quick Actions that time out now return a consistent error. [Outbound Workers](https://developers.cloudflare.com/browser-run/features/outbound-workers/) that throw an exception now return an HTTP error to the browser.\n\nFor more information, refer to the [Browser Run documentation](https://developers.cloudflare.com/browser-run/).","publishedAt":"2026-10-30T00:00:00.000Z","fetchedAt":"2026-10-30T19:29:44.136Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-30-chrome-152/","media":[],"coverageCount":0},{"id":"rel_IFhiof3L-Omd988iUUllb","version":null,"type":"feature","title":"D1 - D1 will enforce a maximum query result size of 50 MB","summary":"Beginning November 4, 2026, D1 queries returning more than 50 MB will fail with a \"query result exceeded the maximum response size limit\" error, a threshold that covers the 99.999th percentile of requests. Import and export via the REST API are exempt; users with large results should select specific columns instead of SELECT * and paginate with LIMIT and OFFSET.","titleGenerated":"Cloudflare D1 to enforce 50 MB maximum query result size on November 4","titleShort":"D1 caps query results at 50 MB starting Nov 4","breaking":"major","importance":4,"content":"Beginning Wednesday, November 4, 2026, D1 will enforce a maximum query result size of 50 MB. Queries that return a result larger than 50 MB will fail and return the following error:\n\n```\n<span class=\"line\"><span class=\"nb-shiki-wvjl67\">D1 query result exceeded the maximum response size limit of 50 MB</span></span>\n```\n\nThe 50 MB limit covers the 99.999th percentile of requests across D1, so most D1 users will not be impacted.\n\n[Import and export](https://developers.cloudflare.com/d1/best-practices/import-export-data/) operations via the [REST API](https://developers.cloudflare.com/api/resources/d1/subresources/database/methods/export/) are not subject to this limit and will continue to work as they do today.\n\nIf your queries return large results, reduce the result size before the enforcement date. For example, select only the columns you need instead of using `SELECT *`, and paginate large result sets with `LIMIT` and `OFFSET`.\n\nFor more information, refer to [D1 limits](https://developers.cloudflare.com/d1/platform/limits/) and the [D1 error list](https://developers.cloudflare.com/d1/observability/debug-d1/#error-list).","publishedAt":"2026-10-30T00:00:00.000Z","fetchedAt":"2026-10-30T19:29:44.136Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-30-query-result-size-limit/","media":[],"coverageCount":0},{"id":"rel_7T_hm065J3B5XEeCFR9J8","version":null,"type":"feature","title":"Durable Objects, Workers - Durable Objects now defer code updates by default","summary":"Deploying new Durable Objects code no longer applies immediately; an active object can keep running its current code for up to 300 seconds while waiting to hibernate, letting in-flight work finish and Hibernation API WebSockets stay connected. The delay is configurable via code_update_strategy in Wrangler with max_delay from 0 to 86400 seconds, or per deployment with --durable-objects-code-update-mode immediate or deferred.","titleGenerated":"Cloudflare Durable Objects now defer code updates by default, up to 5 minutes","titleShort":"Durable Objects defer code updates by default","breaking":"major","importance":4,"content":"Durable Objects can stay active across requests, allowing you to run long-running [agents](https://developers.cloudflare.com/agents/), maintain [long-lived WebSocket connections](https://developers.cloudflare.com/durable-objects/best-practices/websockets/) and coordinate state across many clients. This is possible because each Durable Object is globally unique, and this means it must run only one version of your code at a time. Previously, when you deployed a change to your Durable Objects code, that change would be applied immediately, which could interrupt in-flight work and terminate open connections.\n\nDurable Objects now defer code updates by default. When you deploy, an active Durable Object can keep running its current code for up to 5 minutes (300 seconds) while it waits to hibernate. This gives in-flight work time to finish. WebSocket connections that use the [Hibernation API](https://developers.cloudflare.com/durable-objects/examples/websocket-hibernation-server/) remain connected when an object adopts new code after hibernating.\n\nFor an active Durable Object, a code update follows this flow:\n\n```\nflowchart TD\n    accTitle: Code updates for an active Durable Object\n    accDescr: By default, an active object can keep running its current code until it hibernates or the delay ends. After hibernating, it starts on new code on the next request. If the delay ends first, or immediate mode is selected, it resets and adopts the new code. Deferral is best effort.\n    A[\"You deploy new code\"] -->|\"Default: deferred\"| B[\"The object can keep running<br/>its current code\"]\n    B --> C{\"Hibernates before<br/>the delay ends?\"}\n    C -->|Yes| D[\"Starts on the new code<br/>on the next request\"]\n    C -->|No| E[\"The object resets<br/>and adopts the new code\"]\n    A -.->|\"Override: immediate\"| E\n```\n\nTo change the delay, add `code_update_strategy` to `durable_objects` in your Wrangler configuration:\n\n```\n<span class=\"line\"><span class=\"nb-shiki-140thh\">{</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\"durable_objects\"</span><span class=\"nb-shiki-140thh\">: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\t\"bindings\"</span><span class=\"nb-shiki-140thh\">: [</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t{</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\t\t\t\"name\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"CHAT_ROOM\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\t\t\t\"class_name\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"ChatRoom\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t],</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\t\"code_update_strategy\"</span><span class=\"nb-shiki-140thh\">: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\t\t\"mode\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"deferred\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\t\t\"max_delay\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-dzsirb\">60</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">}</span></span>\n```\n\n```\n<span class=\"line\"><span class=\"nb-shiki-140thh\">[[</span><span class=\"nb-shiki-1t8gfj\">durable_objects</span><span class=\"nb-shiki-140thh\">.</span><span class=\"nb-shiki-1t8gfj\">bindings</span><span class=\"nb-shiki-140thh\">]]</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">name = </span><span class=\"nb-shiki-mdbnqw\">\"CHAT_ROOM\"</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">class_name = </span><span class=\"nb-shiki-mdbnqw\">\"ChatRoom\"</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">[</span><span class=\"nb-shiki-1t8gfj\">durable_objects</span><span class=\"nb-shiki-140thh\">.</span><span class=\"nb-shiki-1t8gfj\">code_update_strategy</span><span class=\"nb-shiki-140thh\">]</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">mode = </span><span class=\"nb-shiki-mdbnqw\">\"deferred\"</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">max_delay = </span><span class=\"nb-shiki-dzsirb\">60</span></span>\n```\n\n`max_delay` is in seconds, from `0` to `86400` (24 hours), with millisecond precision. Omit it to use the default of `300` seconds.\n\nTo set the strategy for a single deployment, pass `--durable-objects-code-update-mode` with `immediate` or `deferred <duration>`. For example, apply an update right away during an incident, or when your deployment tooling calls a Durable Object right after deploying and needs the new code:\n\nif (!customElements.get(\"nb-pm-restore\")) { customElements.define( \"nb-pm-restore\", class extends HTMLElement { connectedCallback() { const card = this.closest(\"\\[data-nb-pm\\]\"); if (!card) return; let saved; try { saved = sessionStorage.getItem(\"ui-pm-tab\"); } catch { return; } if (!saved) return; const tabs = card.querySelectorAll(\"\\[data-nb-pm-tab\\]\"); let idx = -1; tabs.forEach(function (t, i) { if (t.textContent.trim() === saved) idx = i; }); if (idx < 1) return; tabs.forEach(function (t, i) { t.setAttribute(\"aria-selected\", String(i === idx)); }); card.querySelectorAll(\"\\[data-nb-pm-panel\\]\").forEach(function (p, i) { p.hidden = i !== idx; }); } }, ); }\n\nnpmyarnpnpm\n\n```\n<span class=\"text-success\">npx</span><span class=\"text-warning\"> wrangler versions deploy <VERSION_ID>@100% --durable-objects-code-update-mode immediate</span>\n```\n\n```\n<span class=\"text-success\">yarn</span><span class=\"text-warning\"> wrangler versions deploy <VERSION_ID>@100% --durable-objects-code-update-mode immediate</span>\n```\n\n```\n<span class=\"text-success\">pnpm</span><span class=\"text-warning\"> wrangler versions deploy <VERSION_ID>@100% --durable-objects-code-update-mode immediate</span>\n```\n\nA `deferred` code update is best effort and does not guarantee that every operation finishes. The object can still reset before `max_delay` is reached, for example during a Cloudflare runtime update.\n\nFor more information, including how this interacts with gradual deployments and the REST API, refer to [Durable Objects code updates](https://developers.cloudflare.com/durable-objects/deployments/durable-objects-code-updates/).","publishedAt":"2026-10-30T00:00:00.000Z","fetchedAt":"2026-10-30T19:29:44.136Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-30-durable-objects-deferred-code-updates/","media":[],"coverageCount":0},{"id":"rel_tj60AsMf0e-sXDjR3G-bA","version":null,"type":"feature","title":"Workers, BYOIP, Bots - Workers subrequests now preserve context for BYOIP destinations","summary":"Workers subrequests to Cloudflare-proxied BYOIP hostnames are now identified as Cloudflare-to-Cloudflare traffic, preserving the CF-Worker header and cf.worker.upstream_zone field so security rules and Bot Management evaluate the original end-user connection instead of an internal one. Cross-zone subrequests now set CF-Connecting-IP to 2a06:98c0:3600::103, while same-zone subrequests still reflect the client IP.","titleGenerated":"Cloudflare Workers subrequests now preserve context for BYOIP destinations","titleShort":"BYOIP subrequests keep Workers context; false 403s fixed","breaking":"minor","importance":3,"content":"Cloudflare now preserves Workers context when a subrequest targets a Cloudflare-proxied hostname using [Bring Your Own IP (BYOIP)](https://developers.cloudflare.com/byoip/). These requests are identified as Cloudflare-to-Cloudflare traffic instead of external Internet traffic.\n\nPreviously, some BYOIP subrequests could lose Workers context and original client signals. This could cause security rules to behave unexpectedly, Bot Management to evaluate an internal Cloudflare connection instead of the original client, or affected requests to return false `403` responses.\n\nWorkers subrequests to BYOIP destinations now preserve the [`CF-Worker`](https://developers.cloudflare.com/fundamentals/reference/http-headers/#cf-worker) header and [`cf.worker.upstream_zone`](https://developers.cloudflare.com/ruleset-engine/rules-language/fields/reference/cf.worker.upstream_zone/) field. This supports consistent security rule evaluation across same-zone and cross-zone subrequests.\n\n[Bot Management fields](https://developers.cloudflare.com/bots/reference/bot-management-variables/#o2o-and-subrequests) on the destination zone now represent the original end-user connection for affected BYOIP subrequests. Bot scores, verified bot status, and JA3/JA4 fingerprints no longer represent the internal connection between Cloudflare services.\n\nBecause Cloudflare-proxied BYOIP destinations now follow standard Workers subrequest handling, cross-zone requests set [`CF-Connecting-IP`](https://developers.cloudflare.com/fundamentals/reference/http-headers/#cf-connecting-ip-in-worker-subrequests) to the Workers client IP address `2a06:98c0:3600::103` for security reasons. This applies when a Worker in one Cloudflare zone sends a subrequest to a hostname in another Cloudflare zone. Same-zone subrequests continue to reflect the client IP through `CF-Connecting-IP`.\n\nFor more information, refer to [Cloudflare BYOIP](https://developers.cloudflare.com/byoip/), [Workers subrequests](https://developers.cloudflare.com/workers/platform/limits/#subrequests), and [`CF-Connecting-IP` in Worker subrequests](https://developers.cloudflare.com/fundamentals/reference/http-headers/#cf-connecting-ip-in-worker-subrequests).","publishedAt":"2026-10-30T00:00:00.000Z","fetchedAt":"2026-10-30T11:22:56.334Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-30-byoip-workers-subrequest-context/","media":[],"coverageCount":0},{"id":"rel_VGHK0CEm1QdZFmiHveeLE","version":null,"type":"feature","title":"Agents - Agents SDK: Agent Skills are files in your agent's Workspace","summary":"Skills are now loaded from a folder with a SKILL.md written into the agent's Computer Workspace under /workspace/.agents/skills/, with no build step or deploy required, and the agent can write its own skills. Skills can come from a Workspace, an R2 bucket, or code, each validated against the Agent Skills specification. This is a breaking change for code using agents/skills directly: the agents:skills Vite import, agents/skills/compile, SkillRegistry, and Think's skillWorkspace are removed, and runner() moved to agents/skills/runner.","titleGenerated":"Cloudflare Agents SDK turns Agent Skills into workspace files","titleShort":"Agent Skills are now files in the Workspace; agents/skills APIs removed","breaking":"major","importance":4,"content":"[Agent Skills](https://developers.cloudflare.com/agents/skills/) in the [Cloudflare Agents SDK](https://developers.cloudflare.com/agents/) are now files. Write a folder with a `SKILL.md` into your agent's [Computer ↗︎](https://github.com/cloudflare/computer) Workspace, under `/workspace/.agents/skills/`, and the agent can use it on its next turn. You do not need a build step or a deploy, and the agent can write its own skills.\n\nsrc/server.jsjs\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { Think, skills } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"@cloudflare/think\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-1itgoe\"> class</span><span class=\"nb-shiki-1t8gfj\"> MyAgent</span><span class=\"nb-shiki-1itgoe\"> extends</span><span class=\"nb-shiki-1t8gfj\"> Think</span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1t8gfj\">\tgetSkills</span><span class=\"nb-shiki-140thh\">() {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\treturn</span><span class=\"nb-shiki-140thh\"> [</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\tskills.</span><span class=\"nb-shiki-1t8gfj\">workspace</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.workspace), </span><span class=\"nb-shiki-21nrsd\">// /workspace/.agents/skills</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\tskills.</span><span class=\"nb-shiki-1t8gfj\">r2</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.env.</span><span class=\"nb-shiki-dzsirb\">SKILLS_BUCKET</span><span class=\"nb-shiki-140thh\">, { prefix: </span><span class=\"nb-shiki-mdbnqw\">\"skills/\"</span><span class=\"nb-shiki-140thh\"> }),</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t];</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t}</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">}</span></span>\n```\n\nsrc/server.tsts\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { Think, skills } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"@cloudflare/think\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-1itgoe\"> class</span><span class=\"nb-shiki-1t8gfj\"> MyAgent</span><span class=\"nb-shiki-1itgoe\"> extends</span><span class=\"nb-shiki-1t8gfj\"> Think</span><span class=\"nb-shiki-140thh\"><</span><span class=\"nb-shiki-1t8gfj\">Env</span><span class=\"nb-shiki-140thh\">> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1t8gfj\">\tgetSkills</span><span class=\"nb-shiki-140thh\">() {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\treturn</span><span class=\"nb-shiki-140thh\"> [</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\tskills.</span><span class=\"nb-shiki-1t8gfj\">workspace</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.workspace), </span><span class=\"nb-shiki-21nrsd\">// /workspace/.agents/skills</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\tskills.</span><span class=\"nb-shiki-1t8gfj\">r2</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.env.</span><span class=\"nb-shiki-dzsirb\">SKILLS_BUCKET</span><span class=\"nb-shiki-140thh\">, { prefix: </span><span class=\"nb-shiki-mdbnqw\">\"skills/\"</span><span class=\"nb-shiki-140thh\"> }),</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t];</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t}</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">}</span></span>\n```\n\nThis release includes:\n\n- **Three sources.** Load skills from a [Computer Workspace](https://developers.cloudflare.com/agents/skills/sources/computer/), an [R2 bucket](https://developers.cloudflare.com/agents/skills/sources/r2/), or [code](https://developers.cloudflare.com/agents/skills/sources/code/). Think lists every source again before each turn.\n- **The open format.** Every `SKILL.md` is checked against the [Agent Skills specification ↗︎](https://agentskills.io/specification). As the specification's guide for agents recommends, a skill that breaks a rule still loads, with a logged warning. Use `parseSkill()` to check a skill before you write it.\n- **Scripts in the Workspace.** Skill scripts are Workspace files, and the model runs them with the Workspace `exec` tool. Refer to [Execution](https://developers.cloudflare.com/agents/skills/execution/).\n- **Skill tools for any harness.** Use the same `activate_skill` and `read_skill_resource` tools in Think, Pi Durable, or the AI SDK. Refer to [Skills tools](https://developers.cloudflare.com/agents/tools/skills/).\n- **Warm prompt caches.** When a skill changes mid-conversation, Think sends the new catalog as a reminder instead of rebuilding the cached system prompt. An overridden `getSystemPrompt()` now stays at the top of the system prompt, above context blocks.\n\nThis is a breaking change for code that uses `agents/skills` directly. The `agents:skills` Vite import, `agents/skills/compile`, `SkillRegistry`, and Think's `skillWorkspace` are removed, and `runner()` moved to `agents/skills/runner`. Install [Agents SDK v0.32.0 ↗︎](https://github.com/cloudflare/agents/releases/tag/agents%400.32.0) and [Think v0.23.0 ↗︎](https://github.com/cloudflare/agents/releases/tag/%40cloudflare%2Fthink%400.23.0) or later. Refer to the [`computer-skills` ↗︎](https://github.com/cloudflare/agents/tree/main/examples/computer-skills) and [`r2-skills` ↗︎](https://github.com/cloudflare/agents/tree/main/examples/r2-skills) examples.","publishedAt":"2026-10-29T00:00:00.000Z","fetchedAt":"2026-10-29T19:09:26.996Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-29-agent-skills-from-files/","media":[],"coverageCount":0},{"id":"rel_gB56QCURgf9CQ2DjUtMcN","version":null,"type":"feature","title":"Workers VPC - Select a virtual network for VPC Network bindings","summary":"VPC Network bindings can now target a specific virtual network by setting network_id to cf1:vnet:<UUID>, while cf1:network still selects the account's default virtual network. Multiple bindings in one Worker can point at different virtual networks, reaching separate backends that share the same private IP address.","titleGenerated":"Cloudflare Workers VPC lets each Network binding select its own virtual network","titleShort":"VPC bindings target specific virtual networks via network_id","breaking":"none","importance":3,"content":"VPC Network bindings can select a specific virtual network by setting `network_id` to `cf1:vnet:<UUID>`, where the UUID is the ID of the virtual network. Continue to use `cf1:network` to select your account's default virtual network.\n\nMultiple bindings can select different virtual networks. This lets one Worker reach separate backends that use the same private IP address:\n\n```\n<span class=\"line\"><span class=\"nb-shiki-140thh\">{</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">  \"$schema\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"./node_modules/wrangler/config-schema.json\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">  \"vpc_networks\"</span><span class=\"nb-shiki-140thh\">: [</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">    {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">      \"binding\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"PRODUCTION\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">      \"network_id\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"cf1:vnet:<PRODUCTION_VNET_UUID>\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">      \"remote\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-dzsirb\">true</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">    },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">    {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">      \"binding\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"STAGING\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">      \"network_id\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"cf1:vnet:<STAGING_VNET_UUID>\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">      \"remote\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-dzsirb\">true</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">    }</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">  ]</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">}</span></span>\n```\n\n```\n<span class=\"line\"><span class=\"nb-shiki-140thh\">[[</span><span class=\"nb-shiki-1t8gfj\">vpc_networks</span><span class=\"nb-shiki-140thh\">]]</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">binding = </span><span class=\"nb-shiki-mdbnqw\">\"PRODUCTION\"</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">network_id = </span><span class=\"nb-shiki-mdbnqw\">\"cf1:vnet:<PRODUCTION_VNET_UUID>\"</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">remote = </span><span class=\"nb-shiki-dzsirb\">true</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">[[</span><span class=\"nb-shiki-1t8gfj\">vpc_networks</span><span class=\"nb-shiki-140thh\">]]</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">binding = </span><span class=\"nb-shiki-mdbnqw\">\"STAGING\"</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">network_id = </span><span class=\"nb-shiki-mdbnqw\">\"cf1:vnet:<STAGING_VNET_UUID>\"</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">remote = </span><span class=\"nb-shiki-dzsirb\">true</span></span>\n```\n\nFor details, refer to [VPC Networks configuration](https://developers.cloudflare.com/workers-vpc/configuration/vpc-networks/).","publishedAt":"2026-10-28T00:00:00.000Z","fetchedAt":"2026-10-29T15:06:00.123Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-28-virtual-network-bindings/","media":[],"coverageCount":0},{"id":"rel_GR0x-nzyv-oMGjzxT8pU2","version":null,"type":"feature","title":"Workers - Workers Builds now retains builds and build logs for 90 days","summary":"Workers Builds now retains each build and its build logs for 90 days after the build finishes, viewable under View build history in the Deployments tab or via the Workers Builds API. After 90 days the build and logs are removed, but the versions and deployments it created remain in version history for rollbacks; longer-term logs can be downloaded with the Get build logs API endpoint.","titleGenerated":"Cloudflare Workers Builds retains builds and logs for 90 days","titleShort":"Workers Builds keep logs 90 days","breaking":"none","importance":2,"content":"[Workers Builds](https://developers.cloudflare.com/workers/ci-cd/builds/) now retains each build and its build logs for 90 days after the build finishes. You can find them under **View build history** in your Worker's **Deployments** tab, or through the [Workers Builds API](https://developers.cloudflare.com/workers/ci-cd/builds/api-reference/).\n\nAfter 90 days, a build and its logs are removed. The versions and deployments it created stay in your Worker's [version history](https://developers.cloudflare.com/workers/versions-and-deployments/), so you can still [roll back](https://developers.cloudflare.com/workers/versions-and-deployments/rollbacks/) to them.\n\nIf you need a build's logs for longer, you can download them with the [Get build logs](https://developers.cloudflare.com/api/resources/workers_builds/subresources/builds/methods/get_logs/) API endpoint.\n\nFor more information, refer to the [Workers Builds limits documentation](https://developers.cloudflare.com/workers/ci-cd/builds/limits-and-pricing/).","publishedAt":"2026-10-28T00:00:00.000Z","fetchedAt":"2026-10-28T22:52:24.109Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-28-workers-builds-90-day-retention/","media":[],"coverageCount":0},{"id":"rel_gRdi8KEZ12TOKaV6Gmw6O","version":null,"type":"feature","title":"Agents, Browser Run - Agents SDK: Preview dev servers in the browser without public URLs","summary":"Internal hostnames let the persistent browser in the Agents SDK resolve names like preview.internal to your agent instead of the Internet, so a model can check a dev server in its sandbox or another Worker without exposing it publicly. Each host's fetch function runs in your Durable Object with access to its bindings and state, and a target schema lets the model pick what it serves.","titleGenerated":"Cloudflare Agents SDK adds internal hostnames for private dev-server browser previews","titleShort":"Agents SDK browser previews dev servers without public URLs","breaking":"none","importance":3,"content":"Agents that build websites need to check their work in a real browser. Internal hostnames let your agent's browser preview a dev server running in a container without exposing that server to the public Internet.\n\nWith internal hostnames, the [persistent browser](https://developers.cloudflare.com/agents/tools/browser/) in the [Cloudflare Agents SDK](https://developers.cloudflare.com/agents/) can open sites that only your agent serves. Give the browser hostnames such as `preview.internal`, and its requests to them go to your agent instead of the Internet. The model can check a dev server in its sandbox, a folder in its workspace, or another Worker in the same browser it uses for the web, and nothing is exposed publicly.\n\n![An animated diagram of three kinds of hostname: example.com goes out to the Internet; the model points preview.internal at port 5173 with browser.assignHost(), then the browser opens it and your agent serves it from the sandbox; and api.internal goes straight to another Worker, skipping your agent](https://developers.cloudflare.com/cdn-cgi/image/onerror=redirect,width=960,height=524,format=svg/_astro/browser-internal-hostnames.FAVHfafR.svg)\n\nEach internal host has a `fetch` function that runs in your Durable Object, so it can use the object's bindings and state. A host can also send its requests straight to another Worker through a service binding. Add a `target` schema to a host, and the model chooses what it serves, such as which port or which folder.\n\nInternal hostnames are a capability of the browser, independent of your agent harness. Use the built-in adapters for Pi Durable, AI SDK, and TanStack AI, or connect another harness by implementing a tool with [`browser.session()`](https://developers.cloudflare.com/agents/tools/browser/#use-another-harness). No prebuilt adapter is required.\n\nThis example adds a private dev-server preview to a [Pi Durable agent](https://developers.cloudflare.com/agents/tools/browser/#use-it-with-pi-durable). Start with that guide's packages, bindings, and request handler, and install [Agents SDK v0.31.0 ↗︎](https://github.com/cloudflare/agents/releases/tag/agents%400.31.0) or later. The example has three parts:\n\n- **Browser configuration:** `internalHosts` defines how the browser reaches the dev server.\n- **Durable Object wiring:** Lifecycle and `fetchInternalHost()` connect the browser to your application.\n- **Harness integration:** `browserTool` from `agents/browser/pi` registers the tool with Pi.\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { DurableObject } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"cloudflare:workers\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { createModels } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"@earendil-works/pi-ai/models\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { createRegistry, Harness } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"@earendil-works/pi-durable\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { Browser, browserRun } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"agents/browser\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { browserTool } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"agents/browser/pi\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { PiHarness } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"agents/harness/pi\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { Lifecycle } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"agents/lifecycle\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { createAI } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"agents/models/pi-ai\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { z } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"zod\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-21nrsd\">// Your own code: reach a port on your sandbox and return its Response.</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { fetchSandboxPort } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"./hosts\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-21nrsd\">// Browser Run calls back into your Worker through this entrypoint.</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-140thh\"> { InternalHostRouter } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"agents/browser\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-1itgoe\"> class</span><span class=\"nb-shiki-1t8gfj\"> BrowserAssistant</span><span class=\"nb-shiki-1itgoe\"> extends</span><span class=\"nb-shiki-1t8gfj\"> DurableObject</span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1jdh33\">\tai</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1t8gfj\"> createAI</span><span class=\"nb-shiki-140thh\">({ binding: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.env.</span><span class=\"nb-shiki-dzsirb\">AI</span><span class=\"nb-shiki-140thh\"> });</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1jdh33\">\tregistry</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1t8gfj\"> createRegistry</span><span class=\"nb-shiki-140thh\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1jdh33\">\tbrowser</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1itgoe\"> new</span><span class=\"nb-shiki-1t8gfj\"> Browser</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\tprovider: </span><span class=\"nb-shiki-1t8gfj\">browserRun</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.env.</span><span class=\"nb-shiki-dzsirb\">BROWSER</span><span class=\"nb-shiki-140thh\">, {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\tinternalHosts: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\trouter: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.ctx.exports.InternalHostRouter,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\thosts: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-mdbnqw\">\t\t\t\t\t\"preview.internal\"</span><span class=\"nb-shiki-140thh\">: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t\t\tdescription: </span><span class=\"nb-shiki-mdbnqw\">\"Your dev servers\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-21nrsd\">\t\t\t\t\t\t// What the model passes to browser.assignHost() to choose</span></span>\n<span class=\"line\"><span class=\"nb-shiki-21nrsd\">\t\t\t\t\t\t// what this host serves. fetch receives it as its second argument.</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t\t\ttarget: z.</span><span class=\"nb-shiki-1t8gfj\">object</span><span class=\"nb-shiki-140thh\">({ port: z.</span><span class=\"nb-shiki-1t8gfj\">number</span><span class=\"nb-shiki-140thh\">().</span><span class=\"nb-shiki-1t8gfj\">int</span><span class=\"nb-shiki-140thh\">().</span><span class=\"nb-shiki-1t8gfj\">min</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">1024</span><span class=\"nb-shiki-140thh\">) }),</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1t8gfj\">\t\t\t\t\t\tfetch</span><span class=\"nb-shiki-140thh\">: (</span><span class=\"nb-shiki-1jdh33\">request</span><span class=\"nb-shiki-140thh\">, { </span><span class=\"nb-shiki-1jdh33\">port</span><span class=\"nb-shiki-140thh\"> }) </span><span class=\"nb-shiki-1itgoe\">=></span><span class=\"nb-shiki-1t8gfj\"> fetchSandboxPort</span><span class=\"nb-shiki-140thh\">(port, request),</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t}),</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t});</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1jdh33\">\tharness</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1itgoe\"> new</span><span class=\"nb-shiki-1t8gfj\"> PiHarness</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1t8gfj\">\t\tharness</span><span class=\"nb-shiki-140thh\">: ({ </span><span class=\"nb-shiki-1jdh33\">storage</span><span class=\"nb-shiki-140thh\">, </span><span class=\"nb-shiki-1jdh33\">context</span><span class=\"nb-shiki-140thh\">, </span><span class=\"nb-shiki-1jdh33\">onWake</span><span class=\"nb-shiki-140thh\"> }) </span><span class=\"nb-shiki-1itgoe\">=></span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\tconst</span><span class=\"nb-shiki-dzsirb\"> models</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1t8gfj\"> createModels</span><span class=\"nb-shiki-140thh\">();</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\tmodels.</span><span class=\"nb-shiki-1t8gfj\">setProvider</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.ai.provider);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\t\tthis</span><span class=\"nb-shiki-140thh\">.registry.</span><span class=\"nb-shiki-1t8gfj\">install</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tname: </span><span class=\"nb-shiki-mdbnqw\">\"browser\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\ttools: [</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1t8gfj\">\t\t\t\t\tbrowserTool</span><span class=\"nb-shiki-140thh\">({ browser: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.browser, loader: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.env.</span><span class=\"nb-shiki-dzsirb\">LOADER</span><span class=\"nb-shiki-140thh\"> }),</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t],</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t});</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\treturn</span><span class=\"nb-shiki-140thh\"> Harness.</span><span class=\"nb-shiki-1t8gfj\">open</span><span class=\"nb-shiki-140thh\">(</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tstorage,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t{ models, registry: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.registry, onWake },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tcontext,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\tdefaults: { model: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.</span><span class=\"nb-shiki-1t8gfj\">ai</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">\"@cf/zai-org/glm-5.3-flash\"</span><span class=\"nb-shiki-140thh\">) },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t});</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1jdh33\">\tlifecycle</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-140thh\"> Lifecycle.</span><span class=\"nb-shiki-1t8gfj\">install</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">).</span><span class=\"nb-shiki-1t8gfj\">use</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.browser).</span><span class=\"nb-shiki-1t8gfj\">use</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.harness);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-21nrsd\">\t// InternalHostRouter calls this method to reach your Browser.</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1t8gfj\">\tfetchInternalHost</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-1jdh33\">message</span><span class=\"nb-shiki-140thh\">) {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\treturn</span><span class=\"nb-shiki-dzsirb\"> this</span><span class=\"nb-shiki-140thh\">.browser.</span><span class=\"nb-shiki-1t8gfj\">fetchInternalHost</span><span class=\"nb-shiki-140thh\">(message);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\tasync</span><span class=\"nb-shiki-1t8gfj\"> ask</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-1jdh33\">prompt</span><span class=\"nb-shiki-140thh\">) {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\tconst</span><span class=\"nb-shiki-140thh\"> { </span><span class=\"nb-shiki-dzsirb\">text</span><span class=\"nb-shiki-140thh\"> } </span><span class=\"nb-shiki-1itgoe\">=</span><span class=\"nb-shiki-1itgoe\"> await</span><span class=\"nb-shiki-dzsirb\"> this</span><span class=\"nb-shiki-140thh\">.harness.</span><span class=\"nb-shiki-1t8gfj\">prompt</span><span class=\"nb-shiki-140thh\">(prompt);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\treturn</span><span class=\"nb-shiki-140thh\"> text;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t}</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">}</span></span>\n```\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { DurableObject } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"cloudflare:workers\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { createModels } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"@earendil-works/pi-ai/models\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { createRegistry, Harness } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"@earendil-works/pi-durable\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { Browser, browserRun, </span><span class=\"nb-shiki-1itgoe\">type</span><span class=\"nb-shiki-140thh\"> InternalHostMessage } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"agents/browser\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { browserTool } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"agents/browser/pi\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { PiHarness } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"agents/harness/pi\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { Lifecycle } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"agents/lifecycle\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { createAI } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"agents/models/pi-ai\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { z } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"zod\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-21nrsd\">// Your own code: reach a port on your sandbox and return its Response.</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { fetchSandboxPort } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"./hosts\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-21nrsd\">// Browser Run calls back into your Worker through this entrypoint.</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-140thh\"> { InternalHostRouter } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"agents/browser\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-1itgoe\"> class</span><span class=\"nb-shiki-1t8gfj\"> BrowserAssistant</span><span class=\"nb-shiki-1itgoe\"> extends</span><span class=\"nb-shiki-1t8gfj\"> DurableObject</span><span class=\"nb-shiki-140thh\"><</span><span class=\"nb-shiki-1t8gfj\">Env</span><span class=\"nb-shiki-140thh\">> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1jdh33\">\tai</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1t8gfj\"> createAI</span><span class=\"nb-shiki-140thh\">({ binding: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.env.</span><span class=\"nb-shiki-dzsirb\">AI</span><span class=\"nb-shiki-140thh\"> });</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1jdh33\">\tregistry</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1t8gfj\"> createRegistry</span><span class=\"nb-shiki-140thh\">();</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1jdh33\">\tbrowser</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1itgoe\"> new</span><span class=\"nb-shiki-1t8gfj\"> Browser</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\tprovider: </span><span class=\"nb-shiki-1t8gfj\">browserRun</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.env.</span><span class=\"nb-shiki-dzsirb\">BROWSER</span><span class=\"nb-shiki-140thh\">, {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\tinternalHosts: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\trouter: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.ctx.exports.InternalHostRouter,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\thosts: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-mdbnqw\">\t\t\t\t\t\"preview.internal\"</span><span class=\"nb-shiki-140thh\">: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t\t\tdescription: </span><span class=\"nb-shiki-mdbnqw\">\"Your dev servers\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-21nrsd\">\t\t\t\t\t\t// What the model passes to browser.assignHost() to choose</span></span>\n<span class=\"line\"><span class=\"nb-shiki-21nrsd\">\t\t\t\t\t\t// what this host serves. fetch receives it as its second argument.</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t\t\ttarget: z.</span><span class=\"nb-shiki-1t8gfj\">object</span><span class=\"nb-shiki-140thh\">({ port: z.</span><span class=\"nb-shiki-1t8gfj\">number</span><span class=\"nb-shiki-140thh\">().</span><span class=\"nb-shiki-1t8gfj\">int</span><span class=\"nb-shiki-140thh\">().</span><span class=\"nb-shiki-1t8gfj\">min</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">1024</span><span class=\"nb-shiki-140thh\">) }),</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1t8gfj\">\t\t\t\t\t\tfetch</span><span class=\"nb-shiki-140thh\">: (</span><span class=\"nb-shiki-1jdh33\">request</span><span class=\"nb-shiki-140thh\">, { </span><span class=\"nb-shiki-1jdh33\">port</span><span class=\"nb-shiki-140thh\"> }) </span><span class=\"nb-shiki-1itgoe\">=></span><span class=\"nb-shiki-1t8gfj\"> fetchSandboxPort</span><span class=\"nb-shiki-140thh\">(port, request),</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t}),</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t});</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1jdh33\">\tharness</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1itgoe\"> new</span><span class=\"nb-shiki-1t8gfj\"> PiHarness</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1t8gfj\">\t\tharness</span><span class=\"nb-shiki-140thh\">: ({ </span><span class=\"nb-shiki-1jdh33\">storage</span><span class=\"nb-shiki-140thh\">, </span><span class=\"nb-shiki-1jdh33\">context</span><span class=\"nb-shiki-140thh\">, </span><span class=\"nb-shiki-1jdh33\">onWake</span><span class=\"nb-shiki-140thh\"> }) </span><span class=\"nb-shiki-1itgoe\">=></span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\tconst</span><span class=\"nb-shiki-dzsirb\"> models</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1t8gfj\"> createModels</span><span class=\"nb-shiki-140thh\">();</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\tmodels.</span><span class=\"nb-shiki-1t8gfj\">setProvider</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.ai.provider);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\t\tthis</span><span class=\"nb-shiki-140thh\">.registry.</span><span class=\"nb-shiki-1t8gfj\">install</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tname: </span><span class=\"nb-shiki-mdbnqw\">\"browser\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\ttools: [</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1t8gfj\">\t\t\t\t\tbrowserTool</span><span class=\"nb-shiki-140thh\">({ browser: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.browser, loader: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.env.</span><span class=\"nb-shiki-dzsirb\">LOADER</span><span class=\"nb-shiki-140thh\"> }),</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t],</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t});</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\treturn</span><span class=\"nb-shiki-140thh\"> Harness.</span><span class=\"nb-shiki-1t8gfj\">open</span><span class=\"nb-shiki-140thh\">(</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tstorage,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t{ models, registry: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.registry, onWake },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tcontext,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\tdefaults: { model: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.</span><span class=\"nb-shiki-1t8gfj\">ai</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">\"@cf/zai-org/glm-5.3-flash\"</span><span class=\"nb-shiki-140thh\">) },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t});</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1jdh33\">\tlifecycle</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-140thh\"> Lifecycle.</span><span class=\"nb-shiki-1t8gfj\">install</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">).</span><span class=\"nb-shiki-1t8gfj\">use</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.browser).</span><span class=\"nb-shiki-1t8gfj\">use</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.harness);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-21nrsd\">\t// InternalHostRouter calls this method to reach your Browser.</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1t8gfj\">\tfetchInternalHost</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-1jdh33\">message</span><span class=\"nb-shiki-1itgoe\">:</span><span class=\"nb-shiki-1t8gfj\"> InternalHostMessage</span><span class=\"nb-shiki-140thh\">) {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\treturn</span><span class=\"nb-shiki-dzsirb\"> this</span><span class=\"nb-shiki-140thh\">.browser.</span><span class=\"nb-shiki-1t8gfj\">fetchInternalHost</span><span class=\"nb-shiki-140thh\">(message);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\tasync</span><span class=\"nb-shiki-1t8gfj\"> ask</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-1jdh33\">prompt</span><span class=\"nb-shiki-1itgoe\">:</span><span class=\"nb-shiki-dzsirb\"> string</span><span class=\"nb-shiki-140thh\">) {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\tconst</span><span class=\"nb-shiki-140thh\"> { </span><span class=\"nb-shiki-dzsirb\">text</span><span class=\"nb-shiki-140thh\"> } </span><span class=\"nb-shiki-1itgoe\">=</span><span class=\"nb-shiki-1itgoe\"> await</span><span class=\"nb-shiki-dzsirb\"> this</span><span class=\"nb-shiki-140thh\">.harness.</span><span class=\"nb-shiki-1t8gfj\">prompt</span><span class=\"nb-shiki-140thh\">(prompt);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\treturn</span><span class=\"nb-shiki-140thh\"> text;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t}</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">}</span></span>\n```\n\nTo use another harness, keep the browser configuration. Choose a [browser-tool adapter](https://developers.cloudflare.com/agents/tools/browser/#use-another-harness) if one is available, or implement a tool using `browser.session()`.\n\n`browserTool` tells the model about each host, including the schema of its `target`. Because `preview.internal` has a `target` schema, the model can choose which port it serves. To check its dev server, the model calls `browser.assignHost()` with a target that matches the schema, such as `{ port: 5173 }`, and opens the URL it gets back:\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">const</span><span class=\"nb-shiki-140thh\"> { </span><span class=\"nb-shiki-dzsirb\">url</span><span class=\"nb-shiki-140thh\"> } </span><span class=\"nb-shiki-1itgoe\">=</span><span class=\"nb-shiki-1itgoe\"> await</span><span class=\"nb-shiki-140thh\"> browser.</span><span class=\"nb-shiki-1t8gfj\">assignHost</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\thost: </span><span class=\"nb-shiki-mdbnqw\">\"preview.internal\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\ttarget: { port: </span><span class=\"nb-shiki-dzsirb\">5173</span><span class=\"nb-shiki-140thh\"> },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">});</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">await</span><span class=\"nb-shiki-140thh\"> browser.</span><span class=\"nb-shiki-1t8gfj\">cdp</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\tmethod: </span><span class=\"nb-shiki-mdbnqw\">\"Page.navigate\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\tparams: { url },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\tsessionId: </span><span class=\"nb-shiki-mdbnqw\">\"active\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">});</span></span>\n```\n\nThe target is checked against the schema and stored, so it lasts across turns, browser restarts, and redeploys. Each request to `http://preview.internal/` then calls the host's `fetch` function with `{ port: 5173 }`.\n\nThis release also simplifies `browserTool`. It runs the model's code in its own [Dynamic Worker](https://developers.cloudflare.com/dynamic-workers/) and no longer needs a `CodemodeRuntime` export. Refer to the [v0.31.0 upgrade notes ↗︎](https://github.com/cloudflare/agents/releases/tag/agents%400.31.0) if you already use `browserTool`.\n\nFor the complete example, limits, and security guidance, refer to [Internal hostnames](https://developers.cloudflare.com/agents/tools/browser/#internal-hostnames).","publishedAt":"2026-10-28T00:00:00.000Z","fetchedAt":"2026-10-28T14:45:42.051Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-28-browser-internal-hostnames/","media":[{"type":"image","url":"https://developers.cloudflare.com/cdn-cgi/image/onerror=redirect,width=960,height=524,format=svg/_astro/browser-internal-hostnames.FAVHfafR.svg","alt":"An animated diagram of three kinds of hostname: example.com goes out to the Internet; the model points preview.internal at port 5173 with browser.assignHost(), then the browser opens it and your agent serves it from the sandbox; and api.internal goes straight to another Worker, skipping your agent"}],"coverageCount":0},{"id":"rel_FHEtOW5X64H0oNLGh4Rcc","version":null,"type":"feature","title":"Workers, Cloudflare CLI - Serve Workers Static Assets from a URL path prefix","summary":"Workers Static Assets can now serve assets from a URL path prefix without changing their directory structure, configured via assets.base_path in Wrangler or worker.assets.basePath in cloudflare.config.ts. The option requires Wrangler 4.148.0 or later, or cf 1.0.0-beta.13 or later for cloudflare.config.ts.","titleGenerated":"Cloudflare Workers Static Assets can now be served from a URL path prefix","titleShort":"Static Assets can serve from a URL path prefix","breaking":"none","importance":3,"content":"Workers Static Assets can now serve assets from a URL path prefix without changing their directory structure. Set `assets.base_path` in Wrangler, or `worker.assets.basePath` in `cloudflare.config.ts`.\n\nUse [Wrangler 4.148.0 ↗︎](https://github.com/cloudflare/workers-sdk/releases/tag/wrangler%404.148.0) or later. With `cloudflare.config.ts`, use [`cf` 1.0.0-beta.13 ↗︎](https://github.com/cloudflare/cf/releases/tag/cf%401.0.0-beta.13) or later.\n\nFor example, this configuration serves your built assets from `/blog/`:\n\nSelect your preferred CLI\n\nWranglerCF\n\nChanges examples on this page to use the selected CLI.\n\n```\n<span class=\"line\"><span class=\"nb-shiki-140thh\">{</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">  \"$schema\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"./node_modules/wrangler/config-schema.json\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">  \"assets\"</span><span class=\"nb-shiki-140thh\">: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">    \"directory\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"./dist\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">    \"base_path\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"/blog\"</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">  }</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">}</span></span>\n```\n\n```\n<span class=\"line\"><span class=\"nb-shiki-140thh\">[</span><span class=\"nb-shiki-1t8gfj\">assets</span><span class=\"nb-shiki-140thh\">]</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">directory = </span><span class=\"nb-shiki-mdbnqw\">\"./dist\"</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">base_path = </span><span class=\"nb-shiki-mdbnqw\">\"/blog\"</span></span>\n```\n\ncloudflare.config.tsts\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { defineConfig } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"cf/config\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-1itgoe\"> default</span><span class=\"nb-shiki-1t8gfj\"> defineConfig</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">worker: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">  name: </span><span class=\"nb-shiki-mdbnqw\">\"blog\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-21nrsd\">  // Set this to today's date</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">  compatibilityDate: </span><span class=\"nb-shiki-mdbnqw\">\"2026-10-28\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">  assets: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">    basePath: </span><span class=\"nb-shiki-mdbnqw\">\"/blog\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">  },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">});</span></span>\n```\n\nWith Wrangler, `assets.directory` selects the directory to serve. With [`cloudflare.config.ts`](https://developers.cloudflare.com/cf/projects/cloudflare-config/), your build configuration supplies the assets.\n\nTo configure the option, refer to [Serving a subdirectory](https://developers.cloudflare.com/workers/static-assets/routing/advanced/serving-a-subdirectory/).","publishedAt":"2026-10-28T00:00:00.000Z","fetchedAt":"2026-10-28T14:45:42.051Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-28-static-assets-base-path/","media":[],"coverageCount":0},{"id":"rel_Daw7lhb8epm-4xPbVeMAQ","version":null,"type":"feature","title":"Workflows, Workers - Restrict Workflows to a jurisdiction","summary":"Setting jurisdiction on a workflows binding or on a Workflow declared in exports makes its instances run and store data only within that jurisdiction; supported values are eu, us, and fedramp. Jurisdiction is fixed at creation, and wrangler deploy or wrangler versions upload stops before uploading if an existing Workflow has a different jurisdiction, requiring Wrangler 4.152.0 or above.","titleGenerated":"Cloudflare Workflows can now be restricted to EU, US, or FedRAMP jurisdictions","titleShort":"Workflows lock to EU, US, or FedRAMP jurisdiction at creation","breaking":"none","importance":3,"content":"You can now restrict a Workflow to a [jurisdiction](https://developers.cloudflare.com/workflows/reference/data-location/). Its instances only run and store data within that jurisdiction, which helps you comply with data localization regulations such as [GDPR ↗︎](https://gdpr-info.eu/) or [FedRAMP ↗︎](https://blog.cloudflare.com/cloudflare-achieves-fedramp-authorization/).\n\nSet `jurisdiction` on a `workflows` binding, or on a Workflow declared in [`exports`](https://developers.cloudflare.com/workflows/build/workers-api/#declare-workflows-in-exports):\n\n```\n<span class=\"line\"><span class=\"nb-shiki-140thh\">{</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\"workflows\"</span><span class=\"nb-shiki-140thh\">: [</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t{</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\t\t\"name\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"my-workflow\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\t\t\"binding\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"MY_WORKFLOW\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\t\t\"class_name\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"MyWorkflow\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\t\t\"jurisdiction\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"eu\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t],</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">}</span></span>\n```\n\n```\n<span class=\"line\"><span class=\"nb-shiki-140thh\">[[</span><span class=\"nb-shiki-1t8gfj\">workflows</span><span class=\"nb-shiki-140thh\">]]</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">name = </span><span class=\"nb-shiki-mdbnqw\">\"my-workflow\"</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">binding = </span><span class=\"nb-shiki-mdbnqw\">\"MY_WORKFLOW\"</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">class_name = </span><span class=\"nb-shiki-mdbnqw\">\"MyWorkflow\"</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">jurisdiction = </span><span class=\"nb-shiki-mdbnqw\">\"eu\"</span></span>\n```\n\nThe supported jurisdictions are `eu` (the European Union), `us` (the United States), and `fedramp` (FedRAMP-Moderate data centers).\n\nA Workflow's jurisdiction is set when the Workflow is created, and cannot be changed afterwards. If an existing Workflow has a different jurisdiction, `wrangler deploy` and `wrangler versions upload` stop before uploading your Worker.\n\nWorkflow jurisdictions require Wrangler 4.152.0 or above.\n\nFor more information, refer to [Data location](https://developers.cloudflare.com/workflows/reference/data-location/).","publishedAt":"2026-10-27T00:00:00.000Z","fetchedAt":"2026-10-27T18:28:43.007Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-27-workflow-jurisdictions/","media":[],"coverageCount":0},{"id":"rel_Iapk2oxrjRsCEuXr1lzvK","version":null,"type":"feature","title":"Workers - Workers tracing — new spanContext(), setStatus(), and updateName() APIs","summary":"Custom spans in Workers now support three more OpenTelemetry span methods: spanContext() returns traceId, spanId, and traceFlags matching Workers Observability and exported traces, setStatus() sets unset/ok/error with an optional error message, and updateName() renames a span after it starts, including the root span from tracing.getActiveSpan(). Span status appears in traces exported to OpenTelemetry destinations but is not yet shown in the Workers Observability dashboard.","titleGenerated":"Cloudflare Workers tracing adds spanContext, setStatus, and updateName APIs","titleShort":"Custom spans gain spanContext, setStatus, updateName","breaking":"none","importance":3,"content":"[Custom spans](https://developers.cloudflare.com/workers/observability/traces/custom-spans/) in Workers now support three more methods from the OpenTelemetry span API.\n\n- **`span.spanContext()`** returns the span's `traceId`, `spanId`, and `traceFlags`. The IDs match the trace and span IDs in Workers Observability and in exported traces, so you can log them or return them to clients to correlate a request with its trace.\n- **`span.setStatus(status)`** sets the span status to `\"unset\"`, `\"ok\"`, or `\"error\"`, with an optional message for errors. Span status is included in traces exported to [OpenTelemetry destinations](https://developers.cloudflare.com/workers/observability/opentelemetry-export/). The Workers Observability dashboard does not display it yet.\n- **`span.updateName(name)`** renames a span after it starts, such as once you know which route a request matched. You can also rename the invocation's root span returned by `tracing.getActiveSpan()`.\n\nsrc/index.jsjs\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { tracing } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"cloudflare:workers\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-1itgoe\"> default</span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\tasync</span><span class=\"nb-shiki-1t8gfj\"> fetch</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-1jdh33\">request</span><span class=\"nb-shiki-140thh\">, </span><span class=\"nb-shiki-1jdh33\">env</span><span class=\"nb-shiki-140thh\">) {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\tconst</span><span class=\"nb-shiki-dzsirb\"> root</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-140thh\"> tracing.</span><span class=\"nb-shiki-1t8gfj\">getActiveSpan</span><span class=\"nb-shiki-140thh\">();</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\tconst</span><span class=\"nb-shiki-dzsirb\"> route</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-140thh\"> router.</span><span class=\"nb-shiki-1t8gfj\">match</span><span class=\"nb-shiki-140thh\">(request);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\troot?.</span><span class=\"nb-shiki-1t8gfj\">updateName</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">`${</span><span class=\"nb-shiki-140thh\">request</span><span class=\"nb-shiki-mdbnqw\">.</span><span class=\"nb-shiki-140thh\">method</span><span class=\"nb-shiki-mdbnqw\">} ${</span><span class=\"nb-shiki-140thh\">route</span><span class=\"nb-shiki-mdbnqw\">.</span><span class=\"nb-shiki-140thh\">pattern</span><span class=\"nb-shiki-mdbnqw\">}`</span><span class=\"nb-shiki-140thh\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\treturn</span><span class=\"nb-shiki-140thh\"> tracing.</span><span class=\"nb-shiki-1t8gfj\">startActiveSpan</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">\"handle\"</span><span class=\"nb-shiki-140thh\">, </span><span class=\"nb-shiki-1itgoe\">async</span><span class=\"nb-shiki-140thh\"> (</span><span class=\"nb-shiki-1jdh33\">span</span><span class=\"nb-shiki-140thh\">) </span><span class=\"nb-shiki-1itgoe\">=></span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\ttry</span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\t\tconst</span><span class=\"nb-shiki-dzsirb\"> response</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1itgoe\"> await</span><span class=\"nb-shiki-140thh\"> route.</span><span class=\"nb-shiki-1t8gfj\">handler</span><span class=\"nb-shiki-140thh\">(request, env);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tspan.</span><span class=\"nb-shiki-1t8gfj\">setStatus</span><span class=\"nb-shiki-140thh\">({ code: </span><span class=\"nb-shiki-mdbnqw\">\"ok\"</span><span class=\"nb-shiki-140thh\"> });</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\t\treturn</span><span class=\"nb-shiki-140thh\"> response;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t} </span><span class=\"nb-shiki-1itgoe\">catch</span><span class=\"nb-shiki-140thh\"> (err) {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tspan.</span><span class=\"nb-shiki-1t8gfj\">recordException</span><span class=\"nb-shiki-140thh\">(err);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tspan.</span><span class=\"nb-shiki-1t8gfj\">setStatus</span><span class=\"nb-shiki-140thh\">({ code: </span><span class=\"nb-shiki-mdbnqw\">\"error\"</span><span class=\"nb-shiki-140thh\">, message: </span><span class=\"nb-shiki-mdbnqw\">\"Route handler failed\"</span><span class=\"nb-shiki-140thh\"> });</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\t\treturn</span><span class=\"nb-shiki-140thh\"> Response.</span><span class=\"nb-shiki-1t8gfj\">json</span><span class=\"nb-shiki-140thh\">(</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t\t{ error: </span><span class=\"nb-shiki-mdbnqw\">\"Internal error\"</span><span class=\"nb-shiki-140thh\">, traceId: root?.</span><span class=\"nb-shiki-1t8gfj\">spanContext</span><span class=\"nb-shiki-140thh\">().traceId },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t\t{ status: </span><span class=\"nb-shiki-dzsirb\">500</span><span class=\"nb-shiki-140thh\"> },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t} </span><span class=\"nb-shiki-1itgoe\">finally</span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tspan.</span><span class=\"nb-shiki-1t8gfj\">end</span><span class=\"nb-shiki-140thh\">();</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t}</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t});</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">};</span></span>\n```\n\nsrc/index.tsts\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { tracing } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"cloudflare:workers\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-1itgoe\"> default</span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\tasync</span><span class=\"nb-shiki-1t8gfj\"> fetch</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-1jdh33\">request</span><span class=\"nb-shiki-1itgoe\">:</span><span class=\"nb-shiki-1t8gfj\"> Request</span><span class=\"nb-shiki-140thh\">, </span><span class=\"nb-shiki-1jdh33\">env</span><span class=\"nb-shiki-1itgoe\">:</span><span class=\"nb-shiki-1t8gfj\"> Env</span><span class=\"nb-shiki-140thh\">)</span><span class=\"nb-shiki-1itgoe\">:</span><span class=\"nb-shiki-1t8gfj\"> Promise</span><span class=\"nb-shiki-140thh\"><</span><span class=\"nb-shiki-1t8gfj\">Response</span><span class=\"nb-shiki-140thh\">> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\tconst</span><span class=\"nb-shiki-dzsirb\"> root</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-140thh\"> tracing.</span><span class=\"nb-shiki-1t8gfj\">getActiveSpan</span><span class=\"nb-shiki-140thh\">();</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\tconst</span><span class=\"nb-shiki-dzsirb\"> route</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-140thh\"> router.</span><span class=\"nb-shiki-1t8gfj\">match</span><span class=\"nb-shiki-140thh\">(request);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\troot?.</span><span class=\"nb-shiki-1t8gfj\">updateName</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">`${</span><span class=\"nb-shiki-140thh\">request</span><span class=\"nb-shiki-mdbnqw\">.</span><span class=\"nb-shiki-140thh\">method</span><span class=\"nb-shiki-mdbnqw\">} ${</span><span class=\"nb-shiki-140thh\">route</span><span class=\"nb-shiki-mdbnqw\">.</span><span class=\"nb-shiki-140thh\">pattern</span><span class=\"nb-shiki-mdbnqw\">}`</span><span class=\"nb-shiki-140thh\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\treturn</span><span class=\"nb-shiki-140thh\"> tracing.</span><span class=\"nb-shiki-1t8gfj\">startActiveSpan</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">\"handle\"</span><span class=\"nb-shiki-140thh\">, </span><span class=\"nb-shiki-1itgoe\">async</span><span class=\"nb-shiki-140thh\"> (</span><span class=\"nb-shiki-1jdh33\">span</span><span class=\"nb-shiki-140thh\">) </span><span class=\"nb-shiki-1itgoe\">=></span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\ttry</span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\t\tconst</span><span class=\"nb-shiki-dzsirb\"> response</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1itgoe\"> await</span><span class=\"nb-shiki-140thh\"> route.</span><span class=\"nb-shiki-1t8gfj\">handler</span><span class=\"nb-shiki-140thh\">(request, env);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tspan.</span><span class=\"nb-shiki-1t8gfj\">setStatus</span><span class=\"nb-shiki-140thh\">({ code: </span><span class=\"nb-shiki-mdbnqw\">\"ok\"</span><span class=\"nb-shiki-140thh\"> });</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\t\treturn</span><span class=\"nb-shiki-140thh\"> response;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t} </span><span class=\"nb-shiki-1itgoe\">catch</span><span class=\"nb-shiki-140thh\"> (err) {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tspan.</span><span class=\"nb-shiki-1t8gfj\">recordException</span><span class=\"nb-shiki-140thh\">(err </span><span class=\"nb-shiki-1itgoe\">as</span><span class=\"nb-shiki-1t8gfj\"> Error</span><span class=\"nb-shiki-140thh\">);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tspan.</span><span class=\"nb-shiki-1t8gfj\">setStatus</span><span class=\"nb-shiki-140thh\">({ code: </span><span class=\"nb-shiki-mdbnqw\">\"error\"</span><span class=\"nb-shiki-140thh\">, message: </span><span class=\"nb-shiki-mdbnqw\">\"Route handler failed\"</span><span class=\"nb-shiki-140thh\"> });</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\t\treturn</span><span class=\"nb-shiki-140thh\"> Response.</span><span class=\"nb-shiki-1t8gfj\">json</span><span class=\"nb-shiki-140thh\">(</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t\t{ error: </span><span class=\"nb-shiki-mdbnqw\">\"Internal error\"</span><span class=\"nb-shiki-140thh\">, traceId: root?.</span><span class=\"nb-shiki-1t8gfj\">spanContext</span><span class=\"nb-shiki-140thh\">().traceId },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t\t{ status: </span><span class=\"nb-shiki-dzsirb\">500</span><span class=\"nb-shiki-140thh\"> },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t} </span><span class=\"nb-shiki-1itgoe\">finally</span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tspan.</span><span class=\"nb-shiki-1t8gfj\">end</span><span class=\"nb-shiki-140thh\">();</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t}</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t});</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">};</span></span>\n```\n\nFor more details, refer to the [custom spans documentation](https://developers.cloudflare.com/workers/observability/traces/custom-spans/).","publishedAt":"2026-10-26T00:00:00.000Z","fetchedAt":"2026-10-26T22:11:42.401Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-26-span-context-status-update-name/","media":[],"coverageCount":0},{"id":"rel_NETO25vqBQdjIPOhRPT2y","version":null,"type":"feature","title":"Agents, Workers - Cloudflare Computer now helps agents recover from interruptions and wait for approval","summary":"With onInterrupt: \"replay\" set on WorkerJavaScriptBackend, interrupted JavaScript runs again from the beginning, reusing recorded results for completed calls, with recovery triggered on the next workspace.runtime call or via workspace.runtime.recover(). Functions exposed through custom modules can call ctx.interrupt() to pause a run until the application supplies an approval decision.","titleGenerated":"Cloudflare Computer v0.7.0 lets agent code resume after crashes and wait for approvals","titleShort":"Agent runs now resume post-crash; pause at approval gates","breaking":"none","importance":4,"content":"[`@cloudflare/computer` ↗︎](https://github.com/cloudflare/computer) gives your agent a workspace to run code in. From v0.7.0, that code can resume after a deploy or crash, and pause on an external action, for example to approve a tool call.\n\n#### Resume interrupted runs\n\nSet `onInterrupt: \"replay\"` on `WorkerJavaScriptBackend`, or on a single run.\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { DurableObject } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"cloudflare:workers\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { Workspace } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"@cloudflare/computer\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { WorkerJavaScriptBackend } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"@cloudflare/computer/backends/worker-javascript\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-1itgoe\"> class</span><span class=\"nb-shiki-1t8gfj\"> MyAgent</span><span class=\"nb-shiki-1itgoe\"> extends</span><span class=\"nb-shiki-1t8gfj\"> DurableObject</span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1jdh33\">\tworkspace</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1itgoe\"> new</span><span class=\"nb-shiki-1t8gfj\"> Workspace</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\tstorage: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.ctx.storage,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\tbackends: [</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\tnew</span><span class=\"nb-shiki-1t8gfj\"> WorkerJavaScriptBackend</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tloader: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.env.</span><span class=\"nb-shiki-dzsirb\">LOADER</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tonInterrupt: </span><span class=\"nb-shiki-mdbnqw\">\"replay\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t}),</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t],</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t});</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">}</span></span>\n```\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { DurableObject } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"cloudflare:workers\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { Workspace } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"@cloudflare/computer\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { WorkerJavaScriptBackend } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"@cloudflare/computer/backends/worker-javascript\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-1itgoe\"> class</span><span class=\"nb-shiki-1t8gfj\"> MyAgent</span><span class=\"nb-shiki-1itgoe\"> extends</span><span class=\"nb-shiki-1t8gfj\"> DurableObject</span><span class=\"nb-shiki-140thh\"><</span><span class=\"nb-shiki-1t8gfj\">Env</span><span class=\"nb-shiki-140thh\">> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1jdh33\">\tworkspace</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1itgoe\"> new</span><span class=\"nb-shiki-1t8gfj\"> Workspace</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\tstorage: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.ctx.storage,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\tbackends: [</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\tnew</span><span class=\"nb-shiki-1t8gfj\"> WorkerJavaScriptBackend</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tloader: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.env.</span><span class=\"nb-shiki-dzsirb\">LOADER</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\tonInterrupt: </span><span class=\"nb-shiki-mdbnqw\">\"replay\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t}),</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t],</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t});</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">}</span></span>\n```\n\nWhen recovery is triggered, the JavaScript runs again from the beginning, reusing recorded results for completed calls without printing previously recorded output again. If a call's result was not recorded before the interruption, recovery may repeat the call — even if its action already succeeded. For retry handling, including `unsafeToRepeat()` for operations that cannot safely repeat, refer to [Calls that must not run twice](https://developers.cloudflare.com/agents/tools/filesystem/#calls-that-must-not-run-twice).\n\nRecovery starts on the next call to `workspace.runtime`. To recover without waiting for traffic, call `workspace.runtime.recover()` from a [Durable Object alarm](https://developers.cloudflare.com/durable-objects/api/alarms/).\n\n#### Wait for approvals\n\n![A run pauses at an approval gate, then resumes when approved or fails when rejected](https://developers.cloudflare.com/cdn-cgi/image/onerror=redirect,width=920,height=292,format=svg/_astro/computer-approval-flow.CNEabsLO.svg)\n\nExpose your application functions through [custom modules](https://developers.cloudflare.com/agents/tools/filesystem/#give-code-access-to-your-own-functions). A function can call `ctx.interrupt()` to pause the run until your application supplies an approval decision.\n\n#### Require approval before posting a comment\n\nFor example, to provide a GitHub module that allows an agent to post a comment on an issue with approval, an implementation of `postComment` could look like this:\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">new</span><span class=\"nb-shiki-1t8gfj\"> WorkerJavaScriptBackend</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\tloader: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.env.</span><span class=\"nb-shiki-dzsirb\">LOADER</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\tonInterrupt: </span><span class=\"nb-shiki-mdbnqw\">\"replay\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\tmodules: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-21nrsd\">\t\t// The agent's code imports this with `import { postComment } from \"ws:github\"`.</span></span>\n<span class=\"line\"><span class=\"nb-shiki-mdbnqw\">\t\t\"ws:github\"</span><span class=\"nb-shiki-140thh\">: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1t8gfj\">\t\t\tpostComment</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-1itgoe\">async</span><span class=\"nb-shiki-140thh\"> ([</span><span class=\"nb-shiki-1jdh33\">issue</span><span class=\"nb-shiki-140thh\">, </span><span class=\"nb-shiki-1jdh33\">body</span><span class=\"nb-shiki-140thh\">], </span><span class=\"nb-shiki-1jdh33\">ctx</span><span class=\"nb-shiki-140thh\">) </span><span class=\"nb-shiki-1itgoe\">=></span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\t\tconst</span><span class=\"nb-shiki-140thh\"> { </span><span class=\"nb-shiki-dzsirb\">approved</span><span class=\"nb-shiki-140thh\"> } </span><span class=\"nb-shiki-1itgoe\">=</span><span class=\"nb-shiki-1itgoe\"> await</span><span class=\"nb-shiki-140thh\"> ctx.</span><span class=\"nb-shiki-1t8gfj\">interrupt</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t\tissue,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t\tbody,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t});</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\t\tif</span><span class=\"nb-shiki-140thh\"> (</span><span class=\"nb-shiki-1itgoe\">!</span><span class=\"nb-shiki-140thh\">approved) </span><span class=\"nb-shiki-1itgoe\">throw</span><span class=\"nb-shiki-1itgoe\"> new</span><span class=\"nb-shiki-1t8gfj\"> Error</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">\"The comment was rejected.\"</span><span class=\"nb-shiki-140thh\">);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\t\treturn</span><span class=\"nb-shiki-140thh\"> github.</span><span class=\"nb-shiki-1t8gfj\">comment</span><span class=\"nb-shiki-140thh\">(issue, body, { idempotencyKey: ctx.callId });</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">});</span></span>\n```\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">new</span><span class=\"nb-shiki-1t8gfj\"> WorkerJavaScriptBackend</span><span class=\"nb-shiki-140thh\">({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\tloader: </span><span class=\"nb-shiki-dzsirb\">this</span><span class=\"nb-shiki-140thh\">.env.</span><span class=\"nb-shiki-dzsirb\">LOADER</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\tonInterrupt: </span><span class=\"nb-shiki-mdbnqw\">\"replay\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\tmodules: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-21nrsd\">\t\t// The agent's code imports this with `import { postComment } from \"ws:github\"`.</span></span>\n<span class=\"line\"><span class=\"nb-shiki-mdbnqw\">\t\t\"ws:github\"</span><span class=\"nb-shiki-140thh\">: {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1t8gfj\">\t\t\tpostComment</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-1itgoe\">async</span><span class=\"nb-shiki-140thh\"> ([</span><span class=\"nb-shiki-1jdh33\">issue</span><span class=\"nb-shiki-140thh\">, </span><span class=\"nb-shiki-1jdh33\">body</span><span class=\"nb-shiki-140thh\">], </span><span class=\"nb-shiki-1jdh33\">ctx</span><span class=\"nb-shiki-140thh\">) </span><span class=\"nb-shiki-1itgoe\">=></span><span class=\"nb-shiki-140thh\"> {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\t\tconst</span><span class=\"nb-shiki-140thh\"> { </span><span class=\"nb-shiki-dzsirb\">approved</span><span class=\"nb-shiki-140thh\"> } </span><span class=\"nb-shiki-1itgoe\">=</span><span class=\"nb-shiki-1itgoe\"> await</span><span class=\"nb-shiki-140thh\"> ctx.</span><span class=\"nb-shiki-1t8gfj\">interrupt</span><span class=\"nb-shiki-140thh\"><{ </span><span class=\"nb-shiki-1jdh33\">approved</span><span class=\"nb-shiki-1itgoe\">:</span><span class=\"nb-shiki-dzsirb\"> boolean</span><span class=\"nb-shiki-140thh\"> }>({</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t\tissue,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t\tbody,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t\t});</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\t\tif</span><span class=\"nb-shiki-140thh\"> (</span><span class=\"nb-shiki-1itgoe\">!</span><span class=\"nb-shiki-140thh\">approved) </span><span class=\"nb-shiki-1itgoe\">throw</span><span class=\"nb-shiki-1itgoe\"> new</span><span class=\"nb-shiki-1t8gfj\"> Error</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">\"The comment was rejected.\"</span><span class=\"nb-shiki-140thh\">);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\t\t\t\treturn</span><span class=\"nb-shiki-140thh\"> github.</span><span class=\"nb-shiki-1t8gfj\">comment</span><span class=\"nb-shiki-140thh\">(issue, body, { idempotencyKey: ctx.callId });</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\t},</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">});</span></span>\n```\n\n#### Call it from agent code\n\nWhen the agent's code calls `postComment()`, the run pauses at `ctx.interrupt()` until your app responds. The agent's code calls it like any other function:\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">import</span><span class=\"nb-shiki-140thh\"> { postComment } </span><span class=\"nb-shiki-1itgoe\">from</span><span class=\"nb-shiki-mdbnqw\"> \"ws:github\"</span><span class=\"nb-shiki-140thh\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">export</span><span class=\"nb-shiki-1itgoe\"> default</span><span class=\"nb-shiki-1itgoe\"> async</span><span class=\"nb-shiki-1itgoe\"> function</span><span class=\"nb-shiki-140thh\"> ({ </span><span class=\"nb-shiki-1jdh33\">issue</span><span class=\"nb-shiki-140thh\"> }) {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\treturn</span><span class=\"nb-shiki-1t8gfj\"> postComment</span><span class=\"nb-shiki-140thh\">(issue, </span><span class=\"nb-shiki-mdbnqw\">\"Hello world\"</span><span class=\"nb-shiki-140thh\">);</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">}</span></span>\n```\n\n#### Respond to the approval request\n\nThe 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()`:\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">for</span><span class=\"nb-shiki-140thh\"> (</span><span class=\"nb-shiki-1itgoe\">const</span><span class=\"nb-shiki-dzsirb\"> call</span><span class=\"nb-shiki-1itgoe\"> of</span><span class=\"nb-shiki-1itgoe\"> await</span><span class=\"nb-shiki-140thh\"> workspace.runtime.</span><span class=\"nb-shiki-1t8gfj\">pending</span><span class=\"nb-shiki-140thh\">()) {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\tawait</span><span class=\"nb-shiki-140thh\"> workspace.runtime.</span><span class=\"nb-shiki-1t8gfj\">respond</span><span class=\"nb-shiki-140thh\">(call.id, call.callId, { approved: </span><span class=\"nb-shiki-dzsirb\">true</span><span class=\"nb-shiki-140thh\"> });</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">}</span></span>\n```\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">for</span><span class=\"nb-shiki-140thh\"> (</span><span class=\"nb-shiki-1itgoe\">const</span><span class=\"nb-shiki-dzsirb\"> call</span><span class=\"nb-shiki-1itgoe\"> of</span><span class=\"nb-shiki-1itgoe\"> await</span><span class=\"nb-shiki-140thh\"> workspace.runtime.</span><span class=\"nb-shiki-1t8gfj\">pending</span><span class=\"nb-shiki-140thh\">()) {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">\tawait</span><span class=\"nb-shiki-140thh\"> workspace.runtime.</span><span class=\"nb-shiki-1t8gfj\">respond</span><span class=\"nb-shiki-140thh\">(call.id, call.callId, { approved: </span><span class=\"nb-shiki-dzsirb\">true</span><span class=\"nb-shiki-140thh\"> });</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">}</span></span>\n```\n\nThe run continues once all pending calls are resolved.\n\nFor setup, current limitations, and how replay works, refer to [Resume runs after a restart](https://developers.cloudflare.com/agents/tools/filesystem/#resume-runs-after-a-restart).","publishedAt":"2026-10-26T00:00:00.000Z","fetchedAt":"2026-10-26T18:08:19.985Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-26-computer-durable-javascript/","media":[{"type":"image","url":"https://developers.cloudflare.com/cdn-cgi/image/onerror=redirect,width=920,height=292,format=svg/_astro/computer-approval-flow.CNEabsLO.svg","alt":"A run pauses at an approval gate, then resumes when approved or fails when rejected"}],"coverageCount":0},{"id":"rel_GcEa1fpmorupZC7COShO6","version":null,"type":"feature","title":"Workers, Pages - Workers Builds and Pages build images reject compromised eMudhra certificates","summary":"Workers Builds and Pages build images now reject the eight compromised eMudhra intermediate CA certificates when cloning repositories, installing dependencies, or making HTTPS requests, shipping patched CA bundles with Node.js 22.23.4 and 24.22.1, Bun 1.4.4, and pip 26.2.2. Projects setting BUN_VERSION below 1.4.4 or overriding SSL_CERT_FILE, PIP_CERT, or REQUESTS_CA_BUNDLE must verify their own certificates do not trust the compromised intermediates.","titleGenerated":"Cloudflare build images reject compromised eMudhra certificates","titleShort":"Build images reject forged eMudhra certs","breaking":"minor","importance":3,"content":"The private keys of [eight eMudhra intermediate certificate authorities ↗︎](https://blog.cloudflare.com/emudhra-certificate-compromise/) can be recovered, so anyone can forge a certificate for any hostname that chains to them. The Workers Builds and Pages build images now reject these certificates when your build clones your repository, installs dependencies, or makes HTTPS requests.\n\nThe updated images include patched CA certificates, Node.js 22.23.4 and 24.22.1, Bun 1.4.4, and pip 26.2.2. Build commands also run with [environment variables](https://developers.cloudflare.com/workers/ci-cd/builds/build-image/#trusted-certificates) that point older runtimes at the patched CA certificates.\n\n#### What you need to do\n\nMost projects need no changes. Update your project if:\n\n- You set `BUN_VERSION` below `1.4.4`. Older Bun versions trust the compromised certificates.\n- You set your own `SSL_CERT_FILE`, `PIP_CERT`, or `REQUESTS_CA_BUNDLE`, or set `--use-bundled-ca` in `NODE_OPTIONS`. Make sure your CA certificates do not trust the compromised intermediates.\n- Your build installs tools that ship their own CA certificates. Update them to a version that removes the compromised intermediates.\n\nFor details, refer to the build image documentation for [Workers Builds](https://developers.cloudflare.com/workers/ci-cd/builds/build-image/) and [Pages](https://developers.cloudflare.com/pages/configuration/build-image/).","publishedAt":"2026-10-23T00:00:00.000Z","fetchedAt":"2026-10-26T18:08:20.407Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-23-build-images-emudhra-certificates/","media":[],"coverageCount":0},{"id":"rel_fleGqjCti0O0W7rPZEgag","version":null,"type":"feature","title":"Workers - Pin your npm version in Workers Builds with packageManager","summary":"Setting the packageManager field in package.json now controls which npm version Workers Builds installs before dependencies, so lockfiles written by a newer npm install the same way in builds as locally. The field also sets Yarn, pnpm, and Bun versions and requires an exact version rather than a range; engines.npm is not used for npm version selection.","titleGenerated":"Cloudflare Workers Builds lets you pin npm version via packageManager","titleShort":"Workers Builds honors packageManager for npm version","breaking":"none","importance":2,"content":"You can now choose the npm version that [Workers Builds](https://developers.cloudflare.com/workers/ci-cd/builds/) uses by setting the `packageManager` field in your `package.json`. Before the build installs dependencies, it installs the npm version you set, so a lockfile written by a newer npm version installs the same way in your build as it does locally.\n\npackage.jsonjson\n\n```\n<span class=\"line\"><span class=\"nb-shiki-140thh\">{</span></span>\n<span class=\"line\"><span class=\"nb-shiki-dzsirb\">\t\"packageManager\"</span><span class=\"nb-shiki-140thh\">: </span><span class=\"nb-shiki-mdbnqw\">\"npm@11.19.1\"</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">}</span></span>\n```\n\nThe `packageManager` field also sets the version of Yarn, pnpm, and Bun. Use an exact version, such as `npm@11.19.1`, not a range. Workers Builds does not use the `engines.npm` field to choose an npm version.\n\nFor more information, refer to [Package manager versions](https://developers.cloudflare.com/workers/ci-cd/builds/build-image/#package-manager-versions).","publishedAt":"2026-10-23T00:00:00.000Z","fetchedAt":"2026-10-24T01:13:55.896Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-23-workers-builds-pin-npm-version/","media":[],"coverageCount":0},{"id":"rel_5L_1fvwz5S5UXpObqTUlN","version":null,"type":"feature","title":"D1 - D1 database undelete requests limited to the last 7 days","summary":"The D1 undelete API now only accepts requests for databases deleted within the last 7 days; undelete requests for older deletions fail and return an error.","titleGenerated":"Cloudflare D1 undelete API now limited to databases deleted in last 7 days","titleShort":"D1 undelete limited to 7-day window","breaking":"major","importance":3,"content":"The [D1 undelete API](https://developers.cloudflare.com/api/resources/d1/subresources/database/methods/undelete/) now only accepts requests for databases that were deleted within the last 7 days. Requests to undelete a database that was deleted more than 7 days ago will fail and return an error.\n\nIf you delete a D1 database by mistake, send the undelete request as soon as possible, and no later than 7 days after deletion.\n\nFor more information, refer to the [undelete D1 database API reference](https://developers.cloudflare.com/api/resources/d1/subresources/database/methods/undelete/).","publishedAt":"2026-10-23T00:00:00.000Z","fetchedAt":"2026-10-23T17:07:10.632Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-23-undelete-seven-day-window/","media":[],"coverageCount":0},{"id":"rel_KscJLnwH6HKpUZ6HhT8Ok","version":null,"type":"feature","title":"Flagship - Target Flagship feature flags with structured evaluation context","summary":"Flagship evaluation context now accepts nested objects, arrays, and null, so targeting rules can use dot-separated paths like profile.plan and the has/not_has operators for array membership without flattening data. Structured context works through the Workers binding, the evaluation REST API, and the TypeScript, Python, and Go OpenFeature SDKs from version 0.6.0.","titleGenerated":"Cloudflare Flagship supports structured evaluation context for feature flag targeting","titleShort":"Flagship flags can target nested objects and arrays","breaking":"none","importance":3,"content":"[Flagship](https://developers.cloudflare.com/flagship/) supports nested objects, arrays, and `null` in evaluation context. Pass user profiles and tag lists directly, then target nested attributes without flattening your application data.\n\nFor example, pass a profile and tags when evaluating a flag through the Workers binding:\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">const</span><span class=\"nb-shiki-dzsirb\"> enabled</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1itgoe\"> await</span><span class=\"nb-shiki-140thh\"> env.</span><span class=\"nb-shiki-dzsirb\">FLAGS</span><span class=\"nb-shiki-140thh\">.</span><span class=\"nb-shiki-1t8gfj\">getBooleanValue</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">\"new-checkout\"</span><span class=\"nb-shiki-140thh\">, </span><span class=\"nb-shiki-dzsirb\">false</span><span class=\"nb-shiki-140thh\">, {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\ttargetingKey: </span><span class=\"nb-shiki-mdbnqw\">\"user-42\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\tprofile: { plan: </span><span class=\"nb-shiki-mdbnqw\">\"enterprise\"</span><span class=\"nb-shiki-140thh\"> },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\ttags: [</span><span class=\"nb-shiki-mdbnqw\">\"beta\"</span><span class=\"nb-shiki-140thh\">, </span><span class=\"nb-shiki-mdbnqw\">\"early-access\"</span><span class=\"nb-shiki-140thh\">],</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">});</span></span>\n```\n\n```\n<span class=\"line\"><span class=\"nb-shiki-1itgoe\">const</span><span class=\"nb-shiki-dzsirb\"> enabled</span><span class=\"nb-shiki-1itgoe\"> =</span><span class=\"nb-shiki-1itgoe\"> await</span><span class=\"nb-shiki-140thh\"> env.</span><span class=\"nb-shiki-dzsirb\">FLAGS</span><span class=\"nb-shiki-140thh\">.</span><span class=\"nb-shiki-1t8gfj\">getBooleanValue</span><span class=\"nb-shiki-140thh\">(</span><span class=\"nb-shiki-mdbnqw\">\"new-checkout\"</span><span class=\"nb-shiki-140thh\">, </span><span class=\"nb-shiki-dzsirb\">false</span><span class=\"nb-shiki-140thh\">, {</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\ttargetingKey: </span><span class=\"nb-shiki-mdbnqw\">\"user-42\"</span><span class=\"nb-shiki-140thh\">,</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\tprofile: { plan: </span><span class=\"nb-shiki-mdbnqw\">\"enterprise\"</span><span class=\"nb-shiki-140thh\"> },</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">\ttags: [</span><span class=\"nb-shiki-mdbnqw\">\"beta\"</span><span class=\"nb-shiki-140thh\">, </span><span class=\"nb-shiki-mdbnqw\">\"early-access\"</span><span class=\"nb-shiki-140thh\">],</span></span>\n<span class=\"line\"><span class=\"nb-shiki-140thh\">});</span></span>\n```\n\nA targeting rule can combine `profile.plan equals \"enterprise\"` with `tags has \"beta\"` to serve the feature to enterprise users in the beta group. Use dot-separated paths for nested attributes and the `has` and `not_has` operators for array membership. Percentage rollouts can also use a nested identifier, such as `profile.id`, for consistent user assignment.\n\nStructured context works through the Workers binding, the evaluation REST API with a JSON `POST` body, and the TypeScript, Python, and Go OpenFeature SDKs from version `0.6.0`. The SDKs automatically use `POST` when HTTP evaluation requires structured context.\n\nRefer to [targeting rules](https://developers.cloudflare.com/flagship/targeting/), the [evaluation API](https://developers.cloudflare.com/flagship/reference/evaluation-api/), and [SDK context support](https://developers.cloudflare.com/flagship/sdk/#context-support) for usage and supported values.","publishedAt":"2026-10-23T00:00:00.000Z","fetchedAt":"2026-10-23T04:56:50.342Z","url":"https://developers.cloudflare.com/changelog/post/2026-10-23-structured-evaluation-context/","media":[],"coverageCount":0}],"pagination":{"nextCursor":"2026-10-23T00:00:00.000Z|2026-10-23T04:56:50.342Z|rel_KscJLnwH6HKpUZ6HhT8Ok","limit":20},"summaries":{"rolling":null,"monthly":[]}}