Every chat completion request re-loads the target conversation's entire
message history from the database inside drain_approved_tool_calls() before
discovering there is nothing to drain: a fresh message always points at a
newly minted assistant message with no stored output, so the full-history
read (one SELECT of every chat_message row plus building the message map,
uncached, on top of the identical read process_chat_payload already did) is
pure overhead on every message.
Queued tool approvals can only ever be acted on by a resume or continue
request, and exactly those requests carry assistant_message_id in their
payload. The drain now returns early when the field is absent, removing one
O(conversation length) query per chat message while resume, continue, reject
and pause flows behave exactly as before, independent of the approval mode.
* fix: surface files the browser cannot read during a knowledge base directory sync
Syncing a local folder into a knowledge base could fail with nothing but "Error accessing directory": no failing file name, no network request, no server log, and no console output either, because production builds strip console.error. On Windows this happens once the absolute path of a file passes the platform limit, at which point the browser refuses to open a file it just listed.
The directory scan now handles that per file. It names the first failing path and how many files are affected, and stops before anything is uploaded. Stopping is the point: a file missing from the manifest is treated as deleted by the sync, so continuing would remove the knowledge base copy of a file that still exists on disk.
Dragging a folder in hit the same failure and reported nothing at all, and the Firefox picker path returned its promise without awaiting it, so a rejection escaped the error handler and surfaced only as an unhandled rejection. Both report through the existing handler now, and production builds keep console.error so the underlying exception stays visible.
* fix: narrow the change to the silent drag-and-drop folder failure
Dropping a folder onto a knowledge base did nothing at all when the browser refused to open one of the files inside it: the rejection escaped the async drop listener, so the user got no toast, no upload and no clue why. The listener now routes that failure through the same error handler the directory picker already uses, so one path and one message cover both ways of adding a folder.
The rest of the branch is reverted. Dropping `console.error` from the esbuild `pure` list un-stripped 597 call sites across 103 files from every production bundle, which is a repo-wide logging policy change that needs its own argument. The picker-side collect-and-count machinery only reworded a toast the existing catch already showed, and the Firefox `return await` fix is a different bug in a different path.
* fix: a rejected ask_user call ending the turn with no reply
The documented behaviour of the built-in ask_user tool is that a call breaking its rules comes back to the model as an error. Instead the reply stopped there: the error was recorded as the tool result, the model was never asked again, and the user was left with a dead chat and no answer.
The rejection is now handed back like any other failed tool result, so the model sees it and can correct itself within the normal tool-call iteration limit. Any ordinary tool the model emitted in the same turn still runs.
A call rejected for arriving alongside other ask_user calls also left those siblings without a result, which the UI shows as a tool call stuck on "Executing..." forever. Every invalid call now gets its own result. Two ask_user calls on their own also reported the wrong reason, saying the call must be made by itself rather than that only one is allowed per turn.
Fixes#29077
* Keep the original ask_user validation order
Restores the pre-existing check order and the unchanged output id fallback, so this change only alters the return shape needed for staging, and trims a comment that narrated the lines below it.
* Correct the ask_user sibling-call error message
* Shorten the ask_user sibling-call error message
* Drop the untrue sibling-call claim from the ask_user error
The ask_user error text told the user and the model "The others ran.", but that sentence is written into the turn output before any sibling tool call has executed, so it can be plainly false. Under a saved chat with tool approval set to ask, the turn pauses right afterwards and the siblings sit at pending/queued, so the user reads "The others ran" directly above the approval prompt for tools that have not run, and reads it again beside the rejection result if they decline. When the model sends two ask_user calls and nothing else, nothing runs at all and the sentence is emitted twice.
The staging helper cannot see what happens to the sibling calls, so it no longer narrates it. The remaining two sentences hold in every flow: ask_user really is dropped from the executed calls whenever this error is set, and calling it on its own is always the right retry.
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.