On Windows hosts the built-in code interpreter fails immediately with "Failed to fetch dynamically imported module: .../pyodide/pyodide.asm.mjs", and the browser console shows the server answered with a MIME type of "text/plain". Code execution is unusable for those users.
Python's mimetypes module reads the Windows registry after loading its own table, so a stray registry entry silently replaces the correct type for an extension and Starlette then labels the file with it. Browsers enforce strict MIME checking for module scripts and streaming WASM compilation, so the pyodide loader gets refused. The same workaround already existed for .js; this extends it to the two other extensions pyodide ships, and moves it out of the frontend-build branch so the unconditionally mounted /static assets are covered as well.
Fixes#29133
* fix: stop labelling a counted schedule as a one-off
The schedule label treated any rule whose text contained COUNT=1 as a single run, so counts such as 10, 12 and 14 were shown as "Once" together with the date of the first run, on the automations list and on the automation page alike. The label now matches a count of exactly one.
This covers the two places that render the label. The schedule editor reads the count the same way and changes separately. Rules that carry a start date still fall through to the raw rule text, exactly as they already did without a count; that parsing gap changes separately too.
Verified in a browser against the same build without these lines: ten ordinary schedules render identically in both places, and a genuine single-run schedule is still labelled as one.
* fix: stop counted schedules being rewritten as one-offs on save
The schedule editor decides that a rule is a one-off by looking for the text COUNT=1 anywhere in it. A rule that runs ten times carries COUNT=10, which contains that text, so opening such an automation shows it as a single run and saving writes a genuine one-off rule back. One open and save is enough to silently turn a ten run schedule into a one run schedule, with whatever date happened to sit in the rule. The check now requires that no further digit follows, the same test the two schedule labels already use.
The same screens also failed to read counted rules at all. Both label helpers and the editor parser split the stored rule on semicolons after stripping the RRULE prefix, so when the rule carries a DTSTART line the first piece is that whole line and the frequency is never found. The automations list then printed the raw rule text where a human label belongs, and the editor fell back to a plain daily schedule, quietly discarding the weekly or monthly settings on the next save. All three sites now drop the DTSTART part before splitting. They split on whitespace, so the newline form and the space separated form are both handled, matching how the backend already strips it.
The Redis-backed model registry skips its write when the content signature matches what is already stored. That skip has never worked across processes. Two of the values it hashes come out of Python sets, and set iteration order varies with each process's hash seed, so every worker computed a different signature for identical content and every worker rewrote the whole registry on every refresh.
Sorting both makes the signature depend on content alone. Measured on a 120 model registry, 522 KiB serialized: a refresh whose content already matches drops from GET, HKEYS, HSET and SET at 5.1 ms to a single GET at 2.2 ms per worker, and the 522 KiB write leaves the wire entirely.
Verified across 12 child processes with 12 distinct hash seeds: 12 different signatures before, 1 after. Filter execution order is unaffected, because the filter pipeline re-sorts by priority and id before running.
New **pt-BR** translations for items introduced in the latest releases, plus a consistency/quality pass across existing strings (grammar, tone, capitalization, pluralization). Placeholders and hotkeys preserved. No logic changes.
A chat could hard-fail in the browser with "TypeError: can't access property content" and stop rendering the assistant message until a reload.
Streamed response items and content parts were placed at the index the provider reports. That index is not bounded by the length of the array the client has built up, so an entry could land past the end and leave a gap behind. Spreading the array on the next event turned that gap into a real empty entry, and the renderer then dereferenced it while looking for message text.
Output items that would land past the end are now appended, since later events locate them by id anyway. Content and summary parts are padded up to the index instead, because a part carries no id and the text streamed for it is addressed by that same index. The renderer and the structured editor now skip an empty entry as well, so chats already saved in the broken state still display and edit.
Fixes#29244
On Redis Cluster deployments the stop button never stopped a running response when the request landed on a different instance than the one streaming it. The pub/sub listener that carries the stop signal between instances never managed to subscribe, so the command was published to a channel nobody was listening on.
The listener subscribes through a cluster client that connects lazily, and redis-py resolves the pub/sub node from a slot cache that is still empty at that point, which fails with a bare KeyError. Awaiting initialize() first fills that cache. It is a no-op on standalone and Sentinel clients, so nothing has to branch on the deployment type, and it stays inside the reconnect loop so a failover refreshes the cache instead of resubscribing against a stale one.
Before 0.11.1 the listener died on that first exception and cross-instance stop never worked at all. The reconnect loop added in 0.11.1 turned it into a startup window plus KeyError retry spam in the logs. Reported upstream as redis/redis-py#4296.
Fixes#19840
Fills the two remaining untranslated strings in the German catalog and corrects a number of existing entries.
The catalog addresses the user formally with "Sie" in over two hundred strings but had drifted to the informal "du" in around thirty, including "Wählen Sie ein Modell" sitting directly alongside "Wähle eine Option". Those now use "Sie", or the infinitive where the surrounding labels already use it. The "Du" chat bubble label and the two model-facing system prompts are deliberately left informal.
"Explored" carried a trailing ": " that the English source and every other translated locale lack. The summary text beside it only renders when there are tool calls or code interpreter runs, so a details group containing only reasoning items rendered a dangling "Untersucht:" in German.
"Persistent" was left in English beside its already translated sibling option "Ephemeral" ("Flüchtig"), leaving a half translated storage dropdown. The calendar tool description promised listing, searching, creating, updating and deleting calendars, when it acts on calendar events.
The remaining changes fix compounds written as two words ("Skill Beschreibung", "Datei upload", "Audio tag"), replace the non-word "managen" with "verwalten", correct a grammatical gender and a plural, and normalize the only two ellipsis characters in the file to the three periods used by every other entry.
Only de-DE is touched. No catalog regeneration, no other locale files.
On a phone the file preview zoom bar shows plus and minus buttons that duplicate pinch-to-zoom and sit on top of an already small preview. They are hidden on coarse pointers now, so touch users get the preview area back and still zoom the way they expect.
The check is the pointer type rather than the viewport width, because what matters is whether the person can pinch, not how narrow their window is. A narrow desktop window keeps the buttons, a tablet does not.
The zoom percentage doubles as the reset control and has no gesture equivalent, so it stays visible, along with all page and slide navigation. Keyboard zoom is unaffected. Word document previews are left alone: they have no pinch support at all, so hiding their buttons would remove zooming entirely.
Fixes#29152
process_pipeline_inlet_filter() and its outlet counterpart construct and tear
down an aiohttp ClientSession, with its own connector and cookie jar, on
every chat completion and every task generation request just to iterate an
empty filter list. On deployments without pipelines, which is the default,
that is wasted setup on every message.
Both functions now return the payload untouched before the session is
created when there is nothing to call. The per-call saving is small, a few
microseconds of object construction per request on the pinned aiohttp; the
point is that requests stop paying setup for a feature that is not
configured.
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args: free_disk:false name:main suffix:]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_CUDA=true
USE_CUDA_VER=cu126
free_disk:true name:cuda126 suffix:-cuda126]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_CUDA=true free_disk:true name:cuda suffix:-cuda]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_OLLAMA=true free_disk:false name:ollama suffix:-ollama]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_SLIM=true free_disk:false name:slim suffix:-slim]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args: free_disk:false name:main suffix:]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_CUDA=true
USE_CUDA_VER=cu126
free_disk:true name:cuda126 suffix:-cuda126]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_CUDA=true free_disk:true name:cuda suffix:-cuda]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_OLLAMA=true free_disk:false name:ollama suffix:-ollama]) (push) Has been cancelled
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_SLIM=true free_disk:false name:slim suffix:-slim]) (push) Has been cancelled
Clicking a pinned model in the sidebar started a new chat with that model but sent the previous model's tool_ids and skill_ids, and they stayed until the page was reloaded. The model dropdown was unaffected, and so was temporary chat.
A pinned entry links to /?model=<id>, which runs the new-chat path. That path applied the model's defaults and then restored the composer draft over the top, and the draft still held the selection from whichever model was active when it was written. The draft save is debounced, so the stale value was reliably the one read back.
The draft is now restored before the defaults are applied, so the model always decides which tools and skills are active while the unsent prompt, files and approval mode are still kept. Starting a new chat with several models selected now clears the selection instead of carrying the draft's over, since there are no per-model defaults to apply in that case.
Co-authored-by: Claude <noreply@anthropic.com>
173 keys in the de-DE catalog still had empty values, so German users saw those strings rendered in English: the whole terminal file browser, the tool-call approval prompts, chat variables, the model manager, calendar navigation, the accessibility labels for zoom, camera and call controls, and several admin settings panels.
Each string was translated against its actual call site rather than in isolation, so the grammatical form fits the widget it renders in (imperatives on buttons, nouns on labels and select options, participles on toasts). Terminology and the formal "Sie" register follow what the catalog already uses elsewhere, and technical literals were left alone on purpose: the CSV header hint mirrors the file that "Download CSV Template" actually produces, and the MIME pattern, the snake_case variable placeholder and the product names stay verbatim.
Only value strings changed; key order and formatting are untouched.
* fix: stop streaming responses breaking on a duplicate output key
With reasoning-capable models the chat froze mid-stream: the first chunk of the answer appeared, nothing followed, and the whole message only showed up once generation finished. The browser console showed a Svelte each_key_duplicate error.
When a stream event addresses an output slot past the end of the array, the missing slots were filled with the event's own item, id included, so a gap of two left two entries claiming the same id. The next chunk for that item was matched by id, landed in the first of the two, and the rendered list ended up with two items sharing a key, which Svelte refuses to update.
Only the addressed slot now takes the event's item, and the slots before it are anonymous placeholders. Replayed the reported event sequence against the real code: keys are unique again and the chunks stay in order instead of being split across the copies.
* fix: stream reasoning deltas when the provider also sends reasoning_details
Providers such as OpenRouter emit reasoning_details alongside the reasoning
text on the same delta. Merging those details cleared the pending event
unconditionally, discarding the response.reasoning_text.delta that had just
been built, so the client received no reasoning until the response completed
and the thinking block only appeared after generation finished.
The event is now only dropped when the details were all there was to report.
Details persistence is unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014uuEg4AXPs9zE3vVUfN1Fj
---------
Co-authored-by: Claude <noreply@anthropic.com>
After a tool call, the model's thinking was streamed into the chat as if it were the main response, and only jumped into the collapsed Thoughts section once the turn finished. Every further tool call repeated it.
Each tool round appended an empty placeholder message item to the output and sent it to the browser, then dropped it again from the copy used to offset the next round's item indices. The browser therefore held one item more than the backend counted, so the first thinking chunk of the next round was written into that leftover message item and rendered as normal text until the finished output replaced it.
The placeholder is removed. It was never needed: a message item is already created when actual content arrives, and dropping it also stops an empty assistant message being sent back to the model on the follow-up request.
Co-authored-by: Claude <noreply@anthropic.com>
Any `<$...>` run in a chat message was treated as an inline skill mention and removed before the request reached the model, so text like `<$(=MonthStart($(vMaxMonthEndINC)))"}, [Registration day] >` silently vanished mid-message and the model only saw the part before it.
The mention regexes accepted any character except `|` and `>` as the skill id, so they matched far more than real mentions. Skill ids are already validated as `[a-z0-9_-]+` when a skill is created, so both regexes now require that charset. Ordinary text passes through untouched while `<$id>`, `<$id|Label>` and `</id|Label>` still resolve and strip as before.
Verified against the reported message (now preserved verbatim) and the three mention forms.
Co-authored-by: Claude <noreply@anthropic.com>