Skip to content

Upstream sync 6 (final): merge cloudflare-os up to 8060cd9f - #43

Merged
allanrwakatungu merged 29 commits into
mainfrom
upstream-sync-6
Oct 18, 2026
Merged

allanrwakatungu merged 29 commits into
mainfrom
upstream-sync-6

Conversation

@allanrwakatungu

Copy link
Copy Markdown

Step 6, the last of the upstream sync. It merges the remaining upstream first-parent commits cloudflare#241–cloudflare#268, through 8060cd9f on 2026-10-17, and stacks on #42, which is already merged. That's 31 conflicted files. After this the fork is 0 commits behind upstream main.

What upstream brings:

  • Uploaded files become ordinary workspace files in a chat's scratch worktree (Treat all uploaded files as normal workspace files cloudflare/cloudflare-os#770).
  • Approved agent turns resume durably.
  • Resource catalogs stay fixed in the system prompt, and the agent is told when one changes.
  • Chat bindings can export an app as a file.
  • Users can mute a workspace's notifications.
  • Gatekeeper user-selection tickets.
  • A creation's approver can configure it.
  • Native-app sign-in fixes.
  • Drive Office files are converted on connect.
  • The tests move to Vitest 5.

Resolutions worth reviewing

  • Office attachments ⚠️ The fork converted Office/OpenDocument uploads to Markdown at upload, in a code path that Treat all uploaded files as normal workspace files cloudflare/cloudflare-os#770 removed. The conversion now runs at the new ingest step:
    • The original stays byte-for-byte in the scratch worktree.
    • A best-effort Markdown rendition sits beside it (report.docx.md), recorded as ChatAttachmentRef.renditionPath.
    • The agent's attachment note points it at the rendition. The rendition is not inlined the way the old converted text was, so the agent reads it with readFile.
    • New unit tests cover the rendition and the failed-conversion path.
  • Agent turns and the alarm ⚠️
    • Upstream's durable resume and ownership checks are in, plus the fork's supersession check.
    • The alarm keeps the fork's 10-minute hold and hand-over inside upstream's retrying, resuming alarm.
    • Upstream's inline teardown replaces the fork's #tearDownAgentTurn; it was the same steps.
    • Applying an action, or a configured creation, keeps the fork's timeout.
  • Catalogs. Upstream's per-turn catalogs with change notices. The fork's per-chat skill and Knowledge prompt-context snapshots sit beside them, frozen per chat as before.
  • User DO. The fork's single alarm also sweeps upstream's picker selections.
  • Pinning moves to AuthenticatedApi.setWorkspacePinned, as upstream did.
  • Frontend.
    • Lucide icons, including BellOff for mutes.
    • App/template wording.
    • The fork's failureTitle toasts, with upstream's "Could not complete…" wording.

Tests

  • Skipped (all set up per-user models, which the fork doesn't have):
    • durable-decision-resume, 34 tests;
    • the new create-resource tests;
    • the export-through-binding test.
  • Dropped: upstream's new prompt-cache tests that add models.
  • Adapted: frontend tests follow the fork's labels and mocks ("Create App", useServerConfig, async logout).
  • Main follow-up: a mocked AI Gateway in the integration harness, to bring the skipped and dropped scripted-model tests back. More of upstream's coverage now depends on it.

Verification (local)

  • Typecheck is clean, and configs pass configs:check.
  • Unit: 48/48 non-frontend tasks, plus 849/849 frontend jsdom tests.
  • Not run locally: the browser tests, because Chromium here lacks libasound2.
  • Integration: 116 passed, 26 skipped.
  • Lint: no new errors (main 280 → 280). Warnings rose from 324 to 342, in upstream's code.

After merging

The sync is complete. Next is bumping the cloudflare-os submodule in tyms-os, then a careful deploy, starting with one tenant, because of the new Durable Object migrations:

  • GitHub v2.
  • Google v7–v9.
  • New gatekeepers (GitLab, web search, X).

🤖 Generated with Claude Code

deregtd and others added 29 commits October 14, 2026 06:53
…e#731)

* feat: let a creation's approver configure it

* refactor: share one disposeConfiguratorFrame

* feat: host the creation configurator in the approval dialog

* test: approve creations with and without a configuration
… when one changes (cloudflare#753)

* Keep resource catalogs fixed in the system prompt, and tell the agent when one changes

The system prompt lists each always-available resource with its catalog,
such as the skills in the Context Library. The backend rebuilt that list
on every turn, so a skill added or edited while a chat was open changed
the system prompt. The next request then missed the prompt cache from the
start, and on Claude models that drop thinking blocks after a prefix
change, the chat lost its earlier reasoning.

The chat now saves the list its first turn shows, and each compaction
checkpoint saves its own. Before each model request, the agent compares
the live catalogs with the ones it last saw and records a catalogChange
message for each one that differs. History replay shows that message as a
synthetic observeUserChanges call that lists the entries added and the
ones no longer listed. A catalog that fails to load is not a change, so a
failed load no longer costs two cache misses.

* Sign Gemini's unsigned function calls, and share the synthetic observation builder

Gemini 3 rejects a request whose current turn holds a function call
without a thought signature. The catalog note's synthetic
observeUserChanges call lands after the user's message and has none, so
the first Gemini request after a catalog change failed. The model
handle's onPayload chain now gives each unsigned Gemini function call
skip_thought_signature_validator, the value Google documents for a call
the model didn't make. pi can't send it, because it passes on only base64
signatures. Callback turns with pending user edits, which already sent an
unsigned call there, get the same fix.

The user's edits, reverts and catalog changes now build their synthetic
call through one function, makeReplayObservation.
…loudflare#767)

* fix(native): relay authentication popups through the system browser

* fix: harden native auth handoff recovery and billing refresh

* fix: refresh global billing picker after native auth

* fix: allow cancelling native connector authentication

* fix: preserve billing choices on focus and settle native expiry
…dflare#766)

A chat fixed its always-available bindings on its first turn, so a
gatekeeper the user turned on afterwards, such as Web Search, reached
only chats started after that. The rule dates from numbered env[]
capsules, where a new one would shift the numbers. Bindings have names
now.

On each turn, a chat that isn't spawned now takes in each
always-available gatekeeper the workspace gained. It names it from the
gatekeeper's suggested binding name, made unique in the chat's scope.
The system prompt keeps the list the chat started with, and the
catalogChange note tells the agent about the new binding.

The evals can now add Agent Skills to the user's Skills library before
a turn. Change Calendar's third turn gets its new rules that way.
…ch (cloudflare#795)

PUBSUB_TOPIC becomes PUBSUB_CHAT_TOPIC (Workspace Events subscriptions)
and PUBSUB_GMAIL_TOPIC (users.watch), so a deployment can deliver each
through the topic that grants its publisher, chat-api-push@ or
gmail-api-push@. Setting both to one topic is the old single-topic
setup; a deployment upgrading sets both to its PUBSUB_TOPIC.

The push endpoint, its service account and its routing by payload
shape are unchanged: each topic gets its own push subscription to the
same `/pubsub`. Each kind of hook is refused when its topic is unset.

The README also names the push-authentication IAM Google requires:
actAs on the push service account for whoever creates a subscription,
and getOpenIdToken on it for the Pub/Sub service agent, which its
default role already grants; Token Creator on the account only if not.
Bumps the github-actions group with 2 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact) and [actions/download-artifact](https://github.com/actions/download-artifact).


Updates `actions/upload-artifact` from 7.0.1 to 7.0.2
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@043fb46...cf430e0)

Updates `actions/download-artifact` from 8.0.1 to 8.0.2
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](actions/download-artifact@3e5f45b...9000827)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: actions/download-artifact
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Let users mute a workspace's finished-task notifications

Kernel half. Each user's `notificationMute` lives on the workspace's
record in their User DO: `{ until: Date }` or `{ indefinite: true }`,
mutually exclusive, set via `AuthenticatedApi.setWorkspaceNotificationMute`
(null unmutes). While it lasts, `publishNotification` drops the
workspace's `taskCompleted` notifications before any tab or device sees
them. Permission requests always come through, since the task is stuck
until the user answers. An expired mute is simply ignored, so no timer.

* Add Mute / Unmute to workspace menus

Workspace menus (sidebar row, /workspaces list, the editor's phone menu)
offer "Mute notifications" for 1 or 3 hours, until local midnight, 1 or 3
days or until turned back on, and "Unmute notifications" while muted. The
bell-slash shows on muted workspaces, titled with when the mute ends.
)

* Trace model inference and change persistence without changing scheduling

* Keep agent progress correlation across retry and compaction passes

* Use runtime spans for persistence and reuse existing inference tracing
* Fix blueprint gatekeeper connection setup

A blueprint binding was marked ready whenever exactly one connected
account matched the vendor and resource type, and its suggested resource
was seeded into the configurator unverified. That marked a Gmail-only
Google account ready for a Calendar binding it had no grant for, and
seeded the author's calendar into a recipient's form even though the
recipient could not use it.

Gatekeeper bindings now always go through configuration. Eligibility
requires the resource grant, and an account missing it can expand the
grant in place; new connections request only the binding's resource
types. The configurator is seeded only from a previously saved
assignment, and switching accounts clears it. Saved assignments become
unusable as soon as their account disconnects, expires, or loses the
grant, so Create cannot submit a stale account.

* Verify Calendar write access before seeding a suggested calendar

The Calendar configurator seeded any calendar ID it found in a suggested
resource URL and reported the form ready, so a blueprint author's own
calendar looked selectable to a recipient who cannot write it. The
configurator now asks the gatekeeper whether the connected account can
write that exact calendar and leaves the field unselected when it cannot.
A 404 means the calendar is not on the account's list; other failures are
rethrown so a transient error is not mistaken for missing access.

* Recognize limited Calendar writers as writable

Google reports a calendar shared with edit rights but no private-event
visibility as writerWithoutPrivateAccess. The picker and the suggested-
calendar check treated it as read-only, so such a calendar could not be
chosen at all. It now counts as writable for choosing a calendar.

Observer admission is deliberately unchanged: a collaborator must still be
a full writer or owner, because the workspace may already hold private
event details read through the owner's account. The two predicates live
side by side in calendar-api.ts so the difference is explicit.

* Keep a requested Calendar availability mode when the calendar lookup fails

The configurator runtime discards every seeded value when
initialValuesFromResourceUrl throws. Checking a suggested calendar now
makes an API call for every calendar ID, so a transient failure there
silently turned a requested all-visible connection into a this-calendar
one. The availability mode is read from the URL alone, so it is now kept,
and a failed lookup only leaves the calendar for the user to choose.

* Prefer a blueprint binding account that already holds the grant

The connection dialog selected the first account with live credentials,
so a Gmail-only Google account listed ahead of one with Calendar access
was chosen and asked to grant access it did not need. It now prefers an
account that can configure the binding as is.

The dialog also matched the blueprint's gatekeeper name case-sensitively
while the rest of the page did not. It now resolves the vendor once,
case-insensitively, and uses its id for accounts and connecting.

* Publish blueprint accounts only once their subscription has replayed

A restarted accounts subscription replays each account before ready(),
and the page published the list after every replayed account. Until the
replay finished, saved connections for accounts not yet replayed read as
unavailable, and Create reopened the configurator instead of creating the
Gadget. The list is now published from ready() onward, so a restart keeps
the previous list until the new one is complete.

---------

Co-authored-by: Nathan Disidore <nathan@cloudflare.com>
* Reject malformed agent edits before buffering and trace persistence

* Keep blueprint failure expectation with early transform validation

* Leave replica-only permission import with the replica PR

* Keep recoverable file validation separate from replica synchronization and timings

* Preserve read knowledge until agent edits buffer successfully
…ntials (cloudflare#769)

* Extract scoped native Git bootstrap for GitHub and GitLab

* Unify Git pulls and native bootstrap around raw pack streams

* Preserve Git fetch errors across Workers RPC

* Clarify Git pull authority and consumer documentation

* Cover overseer Git pull transfer validation

* Cancel unclaimed Git pull streams on disposal
…ept Google Chat's copied links (cloudflare#758)

* Match a trailing slash followed by a query in matchesResourceUrlPattern

The trailing-slash retry toggled the slash at the end of the whole URL, so a URL with a query or
fragment, like https://github.com/o/r/?tab=readme, never tried /o/r. Toggle it on the parsed path.

* Accept Google Chat links that carry Chat's cls view parameter

Chat's own "Copy link" adds ?cls=..., which names no resource, so a pasted link was refused. Ignore
cls and keep refusing any other query parameter or fragment; describe() still reports the
canonical URL.

* Match pasted resource URLs the way the backend does

The composer's resource picker had its own segment matcher that read a query string as part of the
path, so a Google Chat link ending in ?cls=7 was offered a thread completion appended after the
query, lowercased, and its account row showed a "Grant access" that did nothing until the
placeholder was filled.

- Decide full matches with matchesResourceUrlPattern, the URLPattern test the backend applies, and
  keep the segment logic only for completing half-typed paths. Completions keep the pattern's case,
  go before any query or fragment the URL already has, and offer a pattern's own query (NetSuite's
  ?id=:customerId) only to a URL without one.
- Send the URL exactly as pasted: gatekeepers such as Confluence, Slack and Calendar read its query.
- While the URL has a placeholder, account rows name it ("Fill in :threadId first") instead of
  offering access, and the overlay shows that Esc dismisses it.
- When the gatekeeper refuses a URL, the toast carries its message.

* Complete resource URLs from URLPattern's parsed path

Completions split the raw pattern string, so Tab could fill in pattern
syntax ({/:rest}*, regex groups, a stray "?") that left the user stuck
on a placeholder. Read the host and path from URLPattern instead and
complete only up to the first optional segment.

Drop query completion: it only served hand-typed NetSuite URLs, and its
"=:" placeholder rule blocked real links such as ?q=:open.

Reserve the overlay footer's height in the list budget, so short
windows no longer push the panel past the top.

* Resolve Google Chat message links to their thread

A message's Copy link is /room/{space}/{thread}/{message}, which matched no resource pattern, so
pasting one offered nothing. Let the thread pattern take an optional message id
({/:messageId}?), as GitLab's and X's patterns take a trailing tab, and have parseChatUrl ignore
it, as the thread configurator already did.

The pattern names the resource type in stored state, so read a grant recorded under the old
pattern under the new one. Still keyed on the old string: an admin's disabled-resource entry for
Chat threads, and the typeUrlPattern of existing thread bindings and blueprints exported from them.

* Complete a pattern's choice between words to its first word

A parameter such as :kind(room|dm) lost its regex like any other, so the picker completed
chat.google.com/ to /:kind/:spaceId, a placeholder asking the user to spell the choice, and
treated a half-typed "ro" as its value. Complete it to its first word instead. A full link with
another word, such as /dm/..., still matches through URLPattern.

* Resolve Google Chat direct message links

Chat links to a direct message under /dm/ rather than /room/, so pasting one, or a link to a
message in one, matched no resource and parseChatUrl refused it, though the thread configurator
and the agent's space lookup already accepted it. Let the conversation and thread patterns take
either word (:kind(room|dm)) and parse /dm/ as /room/; the configurators still mint /room/.

The space configurator now prefills from such a link, and from one carrying Chat's cls parameter,
which it refused before.

The conversation pattern is renamed like the thread pattern, so a grant recorded under it is read
under the new name. As with threads, an admin's disabled-resource entry and the typeUrlPattern of
existing conversation bindings and blueprints exported from them still name the old pattern.

* Complete every word of a pattern's choice, and a bare scheme in plain placeholders

A choice such as :kind(room|dm) completed only to its first word, so a half-typed /dm/ path got no
suggestion until the link was whole. Keep each required segment as the words it accepts: any of
them matches, and a completion spells the first.

A scheme still being typed completed to the raw pattern, so Tab could insert regexes and optional
groups (x.com/:username([A-Za-z0-9_]{1,15}), Chat's :kind(room|dm) and {/:messageId}?) that no
filled-in URL could match. Complete it from the same required segments as the path.

* Parse a resource URL once in matchesResourceUrlPattern

Toggle the trailing slash on the parsed URL itself and test the URL as given, rather than keeping
the parsed URL alongside a copy of it.

* Refresh an account's stored description when nothing is left to grant

The Workshop judges an account's grants from the description stored at its last connect or
reconnect. After a gatekeeper renames a resource's urlPattern (Google's Chat conversation and
thread patterns, on this branch), that copy names the old pattern, so the account shows "Grant
access" for a resource it holds; asking the gatekeeper finds nothing to grant, and the button did
nothing until a full reconnect. ensureAccountResources now refreshes the stored description when
the gatekeeper returns no flow, which every caller reaches only after the stored copy showed a
grant missing. It re-reads the record after describe(), so a disconnect while describe() ran is
not undone.

The refresh is best-effort: a caller such as the observer setup reads null as access confirmed and
continues, which a failed describe() must not turn into an error.

* Toggle the configurator prefill's trailing slash on the path

The fallback that seeds configurator values from a resource URL toggled the trailing slash on the
whole URL, so .../folders/FOLDER123/?usp=sharing became ...?usp=sharing/ and seeded nothing, though
matchesResourceUrlPattern now accepts that URL and routes it to the configurator. Toggle it on the
parsed path, as matchesResourceUrlPattern does.

* Complete a resource URL's path in its own case

The picker lowercased every typed segment, but URLPattern matches paths case-sensitively, so typing
RO against :kind(room|dm), or i against a literal Issues, completed to a URL that matched nothing.
Lowercase only the host, as URLPattern does the pattern's, and compare path segments exactly.

* List a pasted URL's full matches in the order the gatekeeper declared them

Sorting full matches by pattern string put the broadest first, so a GitLab merge request link was offered as a GitLab Project before the Merge Request row.

* Complete a resource pattern's required query

A pattern that requires a query, such as NetSuite's custjob.nl?id=:customerId, completed to its path alone, which can never match. Completions now end with the pattern's query, its values left for the user to type.

* Explain why ensureAccountResources re-reads the account's description

* Note which saved Chat resource names the rename doesn't translate

* Complete to a pattern's query only a URL the rest of the pattern matches

A URL with every required path segment was offered the pattern's query even with segments after them or the wrong scheme, so the completed URL still matched nothing. The query is now offered only when the URL matches the pattern with any query in its place.

* refactor: simplify the logic
)

* Search through the deployment's AI Gateway in Web Search

The Web Search gatekeeper always searched through the account's "default"
AI Gateway, so a deployment's own gateway, with its stored provider keys,
limits and logs, never saw the searches. It now reads the gateway from
CF_AI_GATEWAY, the same var and value as the backend's. The deploy service
sets it to the instance's gateway; anywhere else it stays "default".

* Keep CF_AI_GATEWAY out of the Web Search config

Previews upload a backend secret named CF_AI_GATEWAY, and
staging-config.test.ts fails on any config that declares a var under a
secret's name. The gatekeeper now reads the var when the deploy service
sets it and falls back to "default" in code, like the backend, which
also leaves it undeclared.
…loudflare#754)

* Type-check in the Lint job instead of before the tests

`pnpm build` ran before `pnpm test` in the Build and test job, adding
about a minute to the longest job. The test tasks declare the codegen
they read, so they do not need it. It now runs in the Lint job, which
is also a required check, and runs even when a lint step fails so one
run still reports both kinds of error. Lint gets a 10-minute timeout,
since it now runs the build.

* Run the test suites in parallel jobs

`Build and test` ran every package's tests in one job on a 4 vCPU
runner, so the workshop-backend suite shared the CPU with every other
suite and set the pace. Now workshop-backend, workshop-frontend and
integration-tests each run in their own job, and the other packages
share a fourth. Each job runs the same `test` task as `pnpm test`,
selected with `vp run --filter`.

A job that tests one package runs one suite at a time, so it gives
that suite 3 vitest workers instead of the 2 that concurrency.ts
budgets for 4 concurrent suites.

main's ruleset requires a check named `Build and test`, so a job with
that name waits for the four and passes only if all of them pass.

No job restores the dependency cache any more: restoring it took
longer than downloading the packages (setup p50 29 s against 20 s).
* Bump the editor-codemirror group with 4 updates

Bumps the editor-codemirror group with 4 updates: [@codemirror/language](https://github.com/codemirror/language), [@codemirror/view](https://github.com/codemirror/view), [@lezer/highlight](https://github.com/lezer-parser/highlight) and [@codemirror/legacy-modes](https://github.com/codemirror/legacy-modes).


Updates `@codemirror/language` from 6.12.4 to 6.13.1
- [Changelog](https://github.com/codemirror/language/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/language/commits)

Updates `@codemirror/view` from 6.43.13 to 6.43.14
- [Changelog](https://github.com/codemirror/view/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/view/commits)

Updates `@lezer/highlight` from 1.2.4 to 1.2.5
- [Changelog](https://github.com/lezer-parser/highlight/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lezer-parser/highlight/commits)

Updates `@codemirror/legacy-modes` from 6.5.4 to 6.5.5
- [Changelog](https://github.com/codemirror/legacy-modes/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/legacy-modes/commits)

---
updated-dependencies:
- dependency-name: "@codemirror/language"
  dependency-version: 6.13.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: editor-codemirror
- dependency-name: "@codemirror/view"
  dependency-version: 6.43.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: editor-codemirror
- dependency-name: "@lezer/highlight"
  dependency-version: 1.2.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: editor-codemirror
- dependency-name: "@codemirror/legacy-modes"
  dependency-version: 6.5.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: editor-codemirror
...

Signed-off-by: dependabot[bot] <support@github.com>

* Deduplicate @lezer/common and @lezer/lr in the lockfile

The bump moved @codemirror/language and @lezer/highlight onto
@lezer/common 1.5.3 while @codemirror/lang-markdown and @lezer/markdown
stayed on 1.5.2. NodeProp ids are allocated per module instance, so
lang-markdown's headingProp collided with highlight's rule prop and
highlighting `# Heading` threw "tags is not iterable", disabling syntax
highlighting in the Workshop and Context Library editors.

Resolve every consumer to @lezer/common 1.5.3 and @lezer/lr 1.4.11.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Nathan Disidore <nathan@cloudflare.com>
Bumps the remaining-npm group with 2 updates: [isomorphic-git](https://github.com/isomorphic-git/isomorphic-git) and [motion](https://github.com/motiondivision/motion).


Updates `isomorphic-git` from 1.42.2 to 1.43.1
- [Release notes](https://github.com/isomorphic-git/isomorphic-git/releases)
- [Commits](isomorphic-git/isomorphic-git@v1.42.2...v1.43.1)

Updates `motion` from 13.4.3 to 13.5.1
- [Changelog](https://github.com/motiondivision/motion/blob/main/CHANGELOG.md)
- [Commits](motiondivision/motion@v13.4.3...v13.5.1)

---
updated-dependencies:
- dependency-name: isomorphic-git
  dependency-version: 1.43.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: remaining-npm
- dependency-name: motion
  dependency-version: 13.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: remaining-npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Let a resource configurator ask for the access its next step needs

A configurator runs in a sandboxed iframe that cannot open pop-ups, so
it has no way to ask for an OAuth scope it lacks. ResourceConfiguratorHost
gains ensureResources(resourceUrlPatterns): Workshop resolves true when
the account the configurator was opened for already holds grants
covering them, and otherwise opens the provider's consent pop-up and
resolves false, leaving the configurator to re-check through its own
gatekeeper capability. It must be called from a click in the frame so
the browser allows the pop-up.

configurator-ui exposes the call on the render context, and declares a
Button and a Notice for the step that uses it. The Slack and Cloudflare
configurator tests build a render context by hand, so they gain the
member.
* Keep Google Chat link URLs in messages

* Re-read Chat's stored markup when an edit's response omits it
* Await the share-link rejection assertions that never ran

Four tests built an expect(...).rejects chain without awaiting it, so the
assertion settled after the test had already passed. Vitest 5 fails such
floating assertions; await them so they actually check something.

* Move the test toolchain to Vitest 5 and @cloudflare/vitest-plugin

@vitest/browser-playwright 5 needs Vitest 5, and @cloudflare/vitest-pool-workers
never supported it: the package was renamed @cloudflare/vitest-plugin, which
added Vitest 5 in 1.4.0. Rename it everywhere and move vitest to ^5.0.5.

vite-plus goes to ^1.2.0, which bundles Vitest 5.0.4 rather than 1.0.0's
5.0.1. 1.3.0 bundles 5.0.5 and would remove the last duplicate copy, but is
still inside the release-age window.

The plugin keeps running on the current Wrangler/Miniflare stack: the
cloudflare-toolchain bumps (cloudflare#785) make google-hooks-upgrade flaky (ECONNRESET,
3-5 failures in 8 runs against none on main) and are left for their own change.

vitest-evals 0.17 still declares vitest <5 but works unchanged on it (harness,
judges, reporter and JSON output checked), so a peerDependencyRules entry
allows it until a release widens the range.

Supersedes cloudflare#791 and cloudflare#786.

* Run vp's vitest tasks without Vitest 5's filesystem module cache

Vitest 5 turns fsModuleCache on by default, and every package's vitest shares
one cache under the workspace root's node_modules. vp runs those tasks in
parallel, so when one run invalidates the cache it deletes the directory while
another is writing into it, and that run fails to start with ENOTEMPTY. vp
already caches the whole task, so the builder appends --no-fsModuleCache to
each vitest command, as it already pins the reporter.

* Turn off Vitest 5's filesystem module cache for the backend's Workers suite

vp's tasks already run without it, but a direct vitest run in the package
still uses it, and under the Workers pool every run after the first fails:
cached .txt modules come back with root-relative ids the textModules plugin
cannot read, and the pool's WebSocket rejects the larger cached modules
(1009, Message is too large).
cloudflare#797)

* fix: resume decisions through existing durable agent records

* fix: recover explicit compaction across durable object resets

* fix: close action cursor before adopting legacy agent turns

* fix: bound durable resume retries and preserve alarm delivery

* fix: clarify resume authority and avoid shared fixture conflicts

* fix: notify callers when recovering logged agent outcomes
* Project text attachments into independent chat worktrees

* Support byte-preserving attachment worktrees

* Preserve binary attachments and opaque file state in Workshop

* Simplify blob attachments and reuse chat scratch worktrees

* Preserve local blob reads and guard model image dimensions

* Migrate legacy attachments and harden blob-backed worktree reads

* Store attachments and binary writes as ordinary Git blobs

* Address attachment review nits and rename races

* Harden attachment migration and many-image replay

* Use scratch worktree naming throughout attachment ingress
…flare#811)

Reviews of gatekeepers tend to stop at general correctness. Add a review checklist to the write-gatekeeper skill covering the API's shape, common user stories, and whether bindings and sessions partition authority along the lines users grant, and point REVIEW.md at it.
Brings in the rest of upstream's first-parent history, cloudflare#241-cloudflare#268 (through
"Ask gatekeeper reviewers to pressure-test the agent-facing API",
2026-10-17), which finishes the sync: uploaded files as ordinary workspace
files in a chat's scratch worktree, durable resumption of approved agent
turns, catalogs fixed in the system prompt with change notices, gadget
exports on chat bindings, workspace notification mutes, gatekeeper
user-selection tickets, a creation's approver configuring it, native-app
sign-in fixes, Drive Office conversion on connect, and Vitest 5.

Resolutions that keep Tyms behaviour:
- Attachments: upstream's scratch-worktree ingress, plus the fork's Office
  conversion moved to it. An Office/OpenDocument upload stays byte-for-byte
  and gets a best-effort Markdown rendition beside it (report.docx.md),
  recorded as ChatAttachmentRef.renditionPath; the agent is told to read it.
- Agent turns: upstream's ownership checks and durable resume, plus the
  fork's supersession check; the alarm keeps the fork's 10-minute hold and
  hand-over inside upstream's retrying, resuming alarm. Applying an action or
  a configured creation keeps the fork's apply deadline. Upstream's inline
  teardown replaces the fork's #tearDownAgentTurn.
- Catalogs: upstream's per-turn catalogs with change notices; the fork's
  per-chat skill and Knowledge prompt-context snapshots are kept beside them.
- User DO: the fork's single alarm (#armAlarm) also sweeps upstream's
  picker selections.
- Pinning moves to AuthenticatedApi.setWorkspacePinned, as upstream did.
- Frontend: lucide icons (BellOff for mutes), App/template wording, and the
  fork's failureTitle toasts with upstream's "Could not complete" wording.

Tests: durable-decision-resume is skipped (it sets up per-user models), as
are the per-user-model integration tests upstream added; new tests cover
the Office rendition and its failure path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown


Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA


2 out of 7 committers have signed the CLA.
✅ (ndisidore)[https://github.com/ndisidore]
✅ (maxwellpeterson)[https://github.com/maxwellpeterson]
❌ @deregtd
❌ @AshishKumar4
❌ @dcartertwo
❌ @Maximo-Guk
❌ @allanrwakatungu
You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

@allanrwakatungu
allanrwakatungu merged commit 52f6ca6 into main Oct 18, 2026
14 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants