* fix: resolve mobile chat input freeze caused by autoResize oscillation
The autoResize() function in ChatInput.vue used minHeight + 8 as the
threshold to decide whether to switch from multi-line (textarea) back
to single-line (input). On mobile viewports (max-width: 768px), the
textarea's 2-line scrollHeight is below minHeight + 8 due to smaller
line-height and padding, causing an infinite oscillation loop between
<input> and <textarea> that freezes the browser.
Fix: compute the actual single-line content height from
getComputedStyle (line-height + vertical padding) and use that as the
wrapping threshold instead of the hardcoded minHeight + 8 offset.
Also temporarily set min-height to 0 during scrollHeight measurement
to avoid the CSS min-height: 52px inflating the value.
* fix: align mobile multiline attachment button to the left
The mobile multiline grid used a 2-column layout (1fr auto) with
'left right' areas, causing the left actions area to span the full
remaining width and centering the + button via justify-content: center.
Switch to the same 3-column layout (auto 1fr auto) with 'left . right'
areas used on desktop, so the + button sits at the left edge.
* fix: use !important to override min-height during scrollHeight measurement
el.style.minHeight = '0' cannot override the CSS min-height: 52px
!important rule, leaving scrollHeight inflated and preventing the
textarea from switching back to single-line for short text.
Use setProperty('min-height', '0', 'important') to force the override,
and removeProperty to restore. Also add a fontSize * 1.2 fallback when
getComputedStyle returns 'normal' for line-height, so the threshold is
always computable.
* fix: exit MCP anyio contexts in the lifecycle task on disable
#9070 moved MCP shutdown cleanup into the per-server lifecycle task so
that the anyio cancel scopes entered in connect_to_server() are exited
from the task that entered them. Running the cleanup through
asyncio.shield() defeats that: shield wraps the coroutine in a new
task, so disabling a server still fails with "Attempted to exit cancel
scope in a different task than it was entered in" and leaves the scope
state corrupted -- the failure mode behind the CPU spin reported in
#9068.
Await _terminate_mcp_client() directly instead. The graceful disable
path has no cancellation in flight, and on forced shutdown the
lifecycle task still runs its finally block in-task, so the shield only
served to break task affinity.
Closes#9068
* fix: absorb late cancellations during MCP shutdown cleanup
A cancellation delivered while the lifecycle task is already running its
finally-block cleanup would abort _terminate_mcp_client() halfway,
stranding the runtime entry and the MCP transport. Retry the cleanup in
the same task and uncancel() the absorbed request instead, so a forced
shutdown can no longer skip it.
* fix: guard Task.uncancel() for Python 3.10 runtimes
Task.uncancel() only exists on Python 3.11+. On 3.10 absorbing the
CancelledError is sufficient, so skip the call when unavailable.
* fix(webui): fix plugin activation status sync issue in turn_on_plugin #9149
* fix(webui): sync plugin activation status after enabling
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
ChatService serialized PlatformMessageHistory records via bare model_dump(),
causing created_at/updated_at to be emitted as naive ISO strings without a
timezone suffix. The frontend new Date() parses such strings as local time
per ES2015+, so UTC values were displayed as-is instead of converted.
Add serialize_history_entry helper and apply it to get_session, get_thread,
and update_message. This matches the SSE real-time path and session list
path, ensuring the frontend consistently receives ISO strings with the
+00:00 suffix across all code paths.
* fix: adapt MiMo STT to V2.5 models and reject non-WAV audio
The MiMo-V2 series went offline on 2026-06-30, so the default STT model
mimo-v2-omni fails for every default configuration. Switch the default
to mimo-v2.5-asr, the dedicated speech recognition model whose official
docs use exactly the bare input_audio payload this provider sends.
For non-ASR multimodal models such as mimo-v2.5, the audio understanding
docs require a text instruction alongside the audio, so restore the
system/user transcription prompts for that model family only.
Also validate that the resolved audio payload really is RIFF/WAVE before
calling the API: when a platform voice file (e.g. Tencent SILK from QQ)
slips through the WAV conversion chain unchanged, fail locally with an
actionable error instead of the opaque HTTP 400 from the API.
Fixes#9113
* fix: accept unpadded MiMo wav headers
---------
Co-authored-by: tangtaizong666 <212687958+tangtaizong666@users.noreply.github.com>
* fix(kb): clean up KBMedia records on document delete, fix update_kb_stats
- delete_document_by_id now deletes associated KBMedia records before
removing the KBDocument row, preventing orphaned media data.
- update_kb_stats now passes kb_id filter to vec_db.count_documents,
fixing chunk count that previously counted across all KBs.
- add tests for document deletion cleanup and update_kb_stats behavior.
* fix(kb): merge media and document deletion into single transaction
* fix: skip _unbind_plugin for inactivated plugins in reload()
* fix: only skip _unbind_plugin for deactivated plugins on specified-plugin reload path
Revert the full reload unconditional smd.activated guard added in 555fcfa; it is unnecessary because load() re-adds everything after the clear anyway. Keep the guard only on the specified-plugin reload path.
* test: set tool active=False precondition in turn_on_plugin test; clean up mock_load in unbind test