Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package mistral-vibe for openSUSE:Factory 
checked in at 2026-09-09 16:19:56
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/mistral-vibe (Old)
 and      /work/SRC/openSUSE:Factory/.mistral-vibe.new.1265 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "mistral-vibe"

Wed Sep  9 16:19:56 2026 rev:17 rq:1376417 version:2.25.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/mistral-vibe/mistral-vibe.changes        
2026-09-04 12:41:31.791943961 +0200
+++ /work/SRC/openSUSE:Factory/.mistral-vibe.new.1265/mistral-vibe.changes      
2026-09-09 16:22:14.740762374 +0200
@@ -1,0 +2,50 @@
+Sun Sep  6 15:49:53 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to 2.25.0:
+  * Add an experimental /skills browser with search and filter;
+    shared skills sync on session start
+  * Add "vibe update" to check for and install a newer release
+  * Expose plugin-declared MCP servers in /mcp, authenticatable
+    via /mcp login, and discover Mistral's built-in web-search
+    connector
+  * Add connectors (Notion, Linear, Slack, GitHub, Gmail, Google
+    Calendar, Drive) to ACP
+  * Run prompt queueing through the app server, with select,
+    edit, remove, pause and resume controls
+  * vibe --yolo now selects the auto-approve profile; /config
+    saves to the user config by default
+  * Approved file operations act on the cleared path, so a
+    symlink can no longer redirect a write outside the project
+  * Fix crashes on a malformed browser_auth_api_base_url port and
+    on a JSON-Patch move in the session event stream
+  * Drop Devstral Small from the default models list; stale
+    sparse config overrides are migrated automatically
+  * Many more fixes and improvements; see upstream's release
+    notes for the full list
+- Rebase no_network.patch, no_terminal.patch, fix_ui_tests.patch
+  and warning_cleanup.patch
+- Rebase obs-test-synchronization.patch and drop its
+  test_queue_selection_ui.py hunks: upstream removed the
+  blocking-bash queue tests they synchronised
+- Add missing runtime dependency on python3-setproctitle, which
+  upstream requires to rename the process to "Vibe CLI"
+- Add fingerprint_file_contents.patch: the config file
+  fingerprint was dev:inode:mtime:size, so rewriting a config
+  file with different data of the same length inside one
+  filesystem timestamp tick left it unchanged and the edit went
+  unnoticed, which lost a concurrent write and kept a stale
+  config loaded.  The contents are now hashed in as well
+  (submitted upstream)
+- Stop exporting PYTHONTRACEMALLOC=20 when running the test
+  suite: recording a 20-frame traceback for every allocation made
+  it about 29 times slower (2h25m to 5min) and turned some 44
+  timing-sensitive tests into spurious timeout failures
+- Cap pytest-xdist at 4 workers: upstream sets "-n auto", so the
+  suite otherwise differs with the builder's CPU count, and
+  many-core workers exhaust the pty pool
+- Move the test suite into a "test" multibuild flavour, so the
+  shipped package no longer waits on it and is not blocked by it.
+  The test flavour carries the test-only BuildRequires, declares
+  no files and produces only a src.rpm
+
+-------------------------------------------------------------------

Old:
----
  mistral-vibe-2.24.5.tar.gz

New:
----
  _multibuild
  fingerprint_file_contents.patch
  mistral-vibe-2.25.0.tar.gz

----------(New B)----------
  New:  upstream requires to rename the process to "Vibe CLI"
- Add fingerprint_file_contents.patch: the config file
  fingerprint was dev:inode:mtime:size, so rewriting a config
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ mistral-vibe.spec ++++++
--- /var/tmp/diff_new_pack.8Qm2cD/_old  2026-09-09 16:22:15.938812297 +0200
+++ /var/tmp/diff_new_pack.8Qm2cD/_new  2026-09-09 16:22:15.940812380 +0200
@@ -15,8 +15,20 @@
 # Please submit bugfixes or comments via https://bugs.opensuse.org/
 #
 
-Name:           mistral-vibe
-Version:        2.24.5
+%global flavor @BUILD_FLAVOR@%{nil}
+%if "%{flavor}" == "test"
+%define psuffix -test
+# the test flavour installs into the buildroot only so that %%check has an
+# importable tree on PYTHONPATH; it declares no %%files and ships nothing
+%define _unpackaged_files_terminate_build 0
+%bcond_without test
+%else
+%define psuffix %{nil}
+%bcond_with test
+%endif
+%define origname mistral-vibe
+Name:           %{origname}%{psuffix}
+Version:        2.25.0
 Release:        0
 Summary:        Minimal CLI coding agent by Mistral
 License:        Apache-2.0
@@ -43,6 +55,10 @@
 # PATCH-FIX-OPENSUSE obs-test-synchronization.patch [email protected]
 # make UI tests robust on slow build workers
 Patch6:         obs-test-synchronization.patch
+# PATCH-FIX-UPSTREAM fingerprint_file_contents.patch 
gh#mistralai/mistral-vibe#1064 [email protected]
+# a same-length config rewrite inside one filesystem timestamp tick was
+# invisible to the file fingerprint; hash the contents too
+Patch7:         fingerprint_file_contents.patch
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 BuildRequires:  python3-base >= 3.12
@@ -51,6 +67,8 @@
 BuildRequires:  python3-hatchling
 BuildRequires:  python3-pip
 BuildRequires:  python3-rfc8785 >= 0.1.4
+BuildArch:      noarch
+%if !%{with test}
 Requires:       python3-GitPython >= 3.1.57
 Requires:       python3-PyJWT >= 2.13.0
 Requires:       python3-PyYAML >= 6.0.3
@@ -124,6 +142,7 @@
 Requires:       python3-rich >= 15.0.0
 Requires:       python3-rpds-py >= 0.30.0
 Requires:       python3-sentry-sdk >= 2.64.0
+Requires:       python3-setproctitle >= 1.3.7
 Requires:       python3-six >= 1.17.0
 Requires:       python3-smmap >= 5.0.3
 Requires:       python3-sounddevice >= 0.5.5
@@ -152,8 +171,9 @@
 Provides:       python313-mistral-vibe = %{version}
 Provides:       python314-mistral-vibe = %{version}
 Obsoletes:      python314-mistral-vibe < %{version}
-BuildArch:      noarch
+%endif
 # SECTION test requirements
+%if %{with test}
 BuildRequires:  ca-certificates
 BuildRequires:  ca-certificates-mozilla
 BuildRequires:  python3-GitPython >= 3.1.57
@@ -257,6 +277,7 @@
 BuildRequires:  python3-zipp >= 3.23.1
 BuildRequires:  python3-zstandard >= 0.25.0
 BuildRequires:  tree-sitter-bash
+%endif
 # /SECTION
 
 %description
@@ -266,7 +287,7 @@
 with your projects through a powerful set of tools.
 
 %prep
-%autosetup -p1
+%autosetup -p1 -n %{origname}-%{version}
 
 %build
 %python3_pyproject_wheel
@@ -275,13 +296,22 @@
 %python3_pyproject_install
 %fdupes %{buildroot}%{python3_sitelib}
 
+%if %{with test}
 %check
-export PYTHONTRACEMALLOC=20
-PYTEST_ADDOPTS="--ignore=tests/audio_player/test_audio_player.py --timeout=60"
+# Do NOT set PYTHONTRACEMALLOC here: recording a 20-frame traceback for every
+# allocation made the suite about 29 times slower (2h25m -> 5min) and turned
+# roughly 44 timing-sensitive tests into spurious timeout failures.
+# Upstream's pyproject.toml sets "-n auto", one xdist worker per CPU; cap it so
+# the suite is the same everywhere instead of varying with the builder's CPU
+# count, and so many-core workers do not exhaust the pty pool.  A later -n
+# overrides the ini value.
+PYTEST_ADDOPTS="--ignore=tests/audio_player/test_audio_player.py --timeout=60 
-n 4"
 export PYTEST_ADDOPTS+=" --ignore=tests/audio_recorder/test_audio_recorder.py"
 export PYTEST_ADDOPTS+=" --ignore=tests/snapshots"
 %python3_pytest -m 'not (network or terminal)' -k 'not 
test_generic_backend_streaming_uses_ssl_cert_file'
+%endif
 
+%if !%{with test}
 %files
 %license LICENSE
 %doc README.md
@@ -290,4 +320,5 @@
 %{_bindir}/vibe-app-server
 %{python3_sitelib}/vibe
 %{python3_sitelib}/mistral_vibe-%{version}.dist-info
+%endif
 

++++++ _multibuild ++++++
<multibuild>
  <flavor>test</flavor>
</multibuild>

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.8Qm2cD/_old  2026-09-09 16:22:16.001814922 +0200
+++ /var/tmp/diff_new_pack.8Qm2cD/_new  2026-09-09 16:22:16.004815047 +0200
@@ -1,5 +1,5 @@
-mtime: 1788328899
-commit: 70cc33cf11450a10c02645526f1a33e70ac551f382496f1923e07e229eb87ff8
+mtime: 1788715116
+commit: c1044986e1fe5d923f2f53f27f3f11e52f0660460b483b82f505a2b9a26b9d6f
 url: https://src.opensuse.org/AI/mistral-vibe.git
 revision: factory
 

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-09-06 19:18:36.000000000 +0200
@@ -0,0 +1,9 @@
+.assets
+_build.*
+*.obscpio
+*.obsinfo
+*.osc
+.pbuild
+_service:*
+mistral-vibe-*-build/
+.aider*

++++++ fingerprint_file_contents.patch ++++++
From: Martin Pluskal <[email protected]>
Subject: fingerprint: detect same-length rewrites within one timestamp tick

create_file_fingerprint() returned dev:ino:mtime_ns:size, so rewriting a config
file with different data of the same length inside one filesystem timestamp
tick left every component unchanged and the change went unnoticed.  st_ctime_ns
does not help: it comes from the same clock and does not tick either (measured:
1959 of 2000 same-length rewrites undetectable, 0 of 2000 with contents hashed).
Contents are read with os.pread() so the caller's file position is untouched and
the O_RDWR temp handle of the atomic-write path can still be fingerprinted.

Index: mistral-vibe-2.25.0/vibe/core/config/fingerprint.py
===================================================================
--- mistral-vibe-2.25.0.orig/vibe/core/config/fingerprint.py
+++ mistral-vibe-2.25.0/vibe/core/config/fingerprint.py
@@ -27,10 +27,35 @@ def capture_stable_file(path: Path) -> I
         raise ConcurrencyConflictError(expected_fp=before, actual_fp=after)
 
 
+_FINGERPRINT_CHUNK_SIZE = 1 << 16
+
+
 def create_file_fingerprint(file: IO) -> str:
-    """Return an opaque token representing the current state of a file."""
-    stat = os.fstat(file.fileno())
-    return f"{stat.st_dev}:{stat.st_ino}:{stat.st_mtime_ns}:{stat.st_size}"
+    """Return an opaque token representing the current state of a file.
+
+    The token covers both the file's identity and its contents. Identity alone
+    is not sufficient: replacing a file's contents with different data of the
+    same length within a single filesystem timestamp tick leaves st_size,
+    st_mtime_ns and st_ctime_ns all unchanged (both timestamps come from the
+    same clock), so a stat-only token cannot observe the change and a config
+    reload silently keeps serving stale values.
+
+    Contents are read with os.pread() so that the caller's file position is
+    left untouched -- capture_stable_file() hands the same open file to its
+    caller -- and so that the O_RDWR handle from tempfile.NamedTemporaryFile()
+    used by the atomic-write path can be fingerprinted as well.
+    """
+    fd = file.fileno()
+    stat = os.fstat(fd)
+    digest = hashlib.sha256()
+    offset = 0
+    while chunk := os.pread(fd, _FINGERPRINT_CHUNK_SIZE, offset):
+        digest.update(chunk)
+        offset += len(chunk)
+    return (
+        f"{stat.st_dev}:{stat.st_ino}:{stat.st_mtime_ns}:{stat.st_size}"
+        f":{digest.hexdigest()}"
+    )
 
 
 def create_dict_fingerprint(source: dict[str, Any]) -> str:

++++++ fix_ui_tests.patch ++++++
--- /var/tmp/diff_new_pack.8Qm2cD/_old  2026-09-09 16:22:16.182822465 +0200
+++ /var/tmp/diff_new_pack.8Qm2cD/_new  2026-09-09 16:22:16.186822631 +0200
@@ -7,11 +7,11 @@
  tests/test_ui_rewind.py                           |    2 ++
  6 files changed, 20 insertions(+), 2 deletions(-)
 
-Index: mistral-vibe-2.24.3/tests/app_server/test_vibe_code.py
+Index: mistral-vibe-2.25.0/tests/app_server/test_vibe_code.py
 ===================================================================
---- mistral-vibe-2.24.3.orig/tests/app_server/test_vibe_code.py        
2026-08-22 13:42:42.936887544 +0200
-+++ mistral-vibe-2.24.3/tests/app_server/test_vibe_code.py     2026-08-22 
13:44:23.769739837 +0200
-@@ -68,7 +68,7 @@
+--- mistral-vibe-2.25.0.orig/tests/app_server/test_vibe_code.py
++++ mistral-vibe-2.25.0/tests/app_server/test_vibe_code.py
+@@ -68,7 +68,7 @@ def test_app_server_import_does_not_requ
          capture_output=True,
          env=env,
          text=True,
@@ -20,10 +20,10 @@
      )
  
      assert result.returncode == 0, result.stderr
-Index: mistral-vibe-2.24.3/tests/cli/test_ui_config_and_model_picker.py
+Index: mistral-vibe-2.25.0/tests/cli/test_ui_config_and_model_picker.py
 ===================================================================
---- mistral-vibe-2.24.3.orig/tests/cli/test_ui_config_and_model_picker.py      
2026-08-22 13:44:10.539665525 +0200
-+++ mistral-vibe-2.24.3/tests/cli/test_ui_config_and_model_picker.py   
2026-08-22 13:44:23.770218219 +0200
+--- mistral-vibe-2.25.0.orig/tests/cli/test_ui_config_and_model_picker.py
++++ mistral-vibe-2.25.0/tests/cli/test_ui_config_and_model_picker.py
 @@ -1,5 +1,6 @@
  from __future__ import annotations
  
@@ -31,8 +31,8 @@
  from unittest.mock import AsyncMock, patch
  
  import pytest
-@@ -41,6 +42,15 @@
-     return build_test_vibe_config(models=_model_configs(), active_model="", 
**kwargs)
+@@ -52,6 +53,15 @@ async def _open_model_picker(pilot, app)
+     return picker
  
  
 +async def _wait_until_drained(pilot, app, timeout: float = 2.0) -> bool:
@@ -47,20 +47,20 @@
  # --- /model command ---
  
  
-@@ -263,7 +273,7 @@
-         # Highlight starts on pinned "alpha" (index 1); move up to Default.
-         await pilot.press("up")
+@@ -181,7 +191,7 @@ async def test_model_picker_select_curre
+         await _open_model_picker(pilot, app)
+ 
          await pilot.press("enter")
 -        await pilot.pause(0.2)
 +        assert await _wait_until_drained(pilot, app)
  
-         assert app.config.active_model_pinned is False
+         assert app.config.active_model.alias == "alpha"
          assert app._current_bottom_app == BottomApp.Input
-Index: mistral-vibe-2.24.3/tests/cli/test_ui_session_incremental_renderer.py
+Index: mistral-vibe-2.25.0/tests/cli/test_ui_session_incremental_renderer.py
 ===================================================================
---- mistral-vibe-2.24.3.orig/tests/cli/test_ui_session_incremental_renderer.py 
2026-08-22 13:42:42.936887544 +0200
-+++ mistral-vibe-2.24.3/tests/cli/test_ui_session_incremental_renderer.py      
2026-08-22 13:44:23.770518335 +0200
-@@ -5,6 +5,8 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_ui_session_incremental_renderer.py
++++ mistral-vibe-2.25.0/tests/cli/test_ui_session_incremental_renderer.py
+@@ -5,6 +5,8 @@ from pathlib import Path
  import time
  
  import pytest
@@ -69,24 +69,24 @@
  from textual.widgets import Button
  
  from tests.conftest import build_test_agent_loop
-Index: mistral-vibe-2.24.3/tests/cli/test_ui_skill_dispatch.py
+Index: mistral-vibe-2.25.0/tests/cli/test_ui_skill_dispatch.py
 ===================================================================
---- mistral-vibe-2.24.3.orig/tests/cli/test_ui_skill_dispatch.py       
2026-08-22 13:42:42.936887544 +0200
-+++ mistral-vibe-2.24.3/tests/cli/test_ui_skill_dispatch.py    2026-08-22 
13:44:23.770763718 +0200
-@@ -7,6 +7,8 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_ui_skill_dispatch.py
++++ mistral-vibe-2.25.0/tests/cli/test_ui_skill_dispatch.py
+@@ -8,6 +8,8 @@ from weakref import WeakKeyDictionary
  
  import pytest
  
 +pytestmark = pytest.mark.terminal
 +
- from tests.conftest import build_test_vibe_app, build_test_vibe_config
- from tests.skills.conftest import create_skill
- from vibe.app_server.models import CompletedEffectState, PublicEffectEntry
-Index: mistral-vibe-2.24.3/tests/cli/textual_ui/test_app_server_requests.py
-===================================================================
---- mistral-vibe-2.24.3.orig/tests/cli/textual_ui/test_app_server_requests.py  
2026-08-22 13:42:42.936887544 +0200
-+++ mistral-vibe-2.24.3/tests/cli/textual_ui/test_app_server_requests.py       
2026-08-22 13:44:23.771035260 +0200
-@@ -8,6 +8,8 @@
+ from tests.conftest import (
+     build_test_agent_loop,
+     build_test_vibe_app,
+Index: mistral-vibe-2.25.0/tests/cli/textual_ui/test_app_server_requests.py
+===================================================================
+--- mistral-vibe-2.25.0.orig/tests/cli/textual_ui/test_app_server_requests.py
++++ mistral-vibe-2.25.0/tests/cli/textual_ui/test_app_server_requests.py
+@@ -8,6 +8,8 @@ from unittest.mock import AsyncMock, Mag
  
  import pytest
  
@@ -95,11 +95,11 @@
  from tests.conftest import (
      build_test_agent_loop,
      build_test_vibe_app,
-Index: mistral-vibe-2.24.3/tests/test_ui_rewind.py
+Index: mistral-vibe-2.25.0/tests/test_ui_rewind.py
 ===================================================================
---- mistral-vibe-2.24.3.orig/tests/test_ui_rewind.py   2026-08-22 
13:42:42.936887544 +0200
-+++ mistral-vibe-2.24.3/tests/test_ui_rewind.py        2026-08-22 
13:44:23.771389237 +0200
-@@ -6,6 +6,8 @@
+--- mistral-vibe-2.25.0.orig/tests/test_ui_rewind.py
++++ mistral-vibe-2.25.0/tests/test_ui_rewind.py
+@@ -6,6 +6,8 @@ import time
  
  import pytest
  

++++++ mistral-vibe-2.24.5.tar.gz -> mistral-vibe-2.25.0.tar.gz ++++++
++++ 63567 lines of diff (skipped)

++++++ no_network.patch ++++++
--- /var/tmp/diff_new_pack.8Qm2cD/_old  2026-09-09 16:22:16.885851760 +0200
+++ /var/tmp/diff_new_pack.8Qm2cD/_new  2026-09-09 16:22:16.889851927 +0200
@@ -16,11 +16,11 @@
  tests/test_install_script.py                          |    3 +++
  15 files changed, 36 insertions(+)
 
-Index: mistral-vibe-2.24.4/pyproject.toml
+Index: mistral-vibe-2.25.0/pyproject.toml
 ===================================================================
---- mistral-vibe-2.24.4.orig/pyproject.toml    2026-08-26 17:18:33.000000000 
+0200
-+++ mistral-vibe-2.24.4/pyproject.toml 2026-08-27 15:09:33.762522932 +0200
-@@ -272,6 +272,7 @@
+--- mistral-vibe-2.25.0.orig/pyproject.toml
++++ mistral-vibe-2.25.0/pyproject.toml
+@@ -272,6 +272,7 @@ addopts = "-p pytest_vibe -vvvv -q -n au
  timeout = 10
  markers = [
      "unified_supported: backend contract behavior implemented by the Unified 
Harness",
@@ -28,11 +28,11 @@
  ]
  filterwarnings = [
      # (e2e) streaming mock server is started in a thread; and the cli is 
spawn in a fork
-Index: mistral-vibe-2.24.4/tests/acp/test_acp_entrypoint_smoke.py
+Index: mistral-vibe-2.25.0/tests/acp/test_acp_entrypoint_smoke.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/acp/test_acp_entrypoint_smoke.py    
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/acp/test_acp_entrypoint_smoke.py 2026-08-27 
09:57:37.157045175 +0200
-@@ -29,6 +29,8 @@
+--- mistral-vibe-2.25.0.orig/tests/acp/test_acp_entrypoint_smoke.py
++++ mistral-vibe-2.25.0/tests/acp/test_acp_entrypoint_smoke.py
+@@ -29,6 +29,8 @@ BROWSER_AUTH_DESCRIPTION = (
      "Sign into Mistral Vibe through your Mistral AI Studio account."
  )
  
@@ -41,11 +41,11 @@
  
  class _AcpSmokeClient(Client):
      def on_connect(self, conn: Any) -> None:
-Index: mistral-vibe-2.24.4/tests/app_server/test_transport.py
+Index: mistral-vibe-2.25.0/tests/app_server/test_transport.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/app_server/test_transport.py        
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/app_server/test_transport.py     2026-08-27 
09:57:37.157476994 +0200
-@@ -239,6 +239,7 @@
+--- mistral-vibe-2.25.0.orig/tests/app_server/test_transport.py
++++ mistral-vibe-2.25.0/tests/app_server/test_transport.py
+@@ -240,6 +240,7 @@ async def test_stdio_transport_close_is_
      assert [json.loads(line) for line in writer.getvalue().splitlines()] == 
[{"id": 1}]
  
  
@@ -53,19 +53,19 @@
  @pytest.mark.asyncio
  async def test_stdio_server_uses_the_same_json_rpc_lifecycle() -> None:
      agent_loop = build_test_agent_loop()
-Index: mistral-vibe-2.24.4/tests/cli/test_audio_config_boundary.py
+Index: mistral-vibe-2.25.0/tests/cli/test_audio_config_boundary.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/cli/test_audio_config_boundary.py   
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/cli/test_audio_config_boundary.py        
2026-08-27 09:57:37.157833971 +0200
-@@ -29,6 +29,7 @@
-     return await _wait_until(pilot, lambda: not app._queue.draining, timeout)
+--- mistral-vibe-2.25.0.orig/tests/cli/test_audio_config_boundary.py
++++ mistral-vibe-2.25.0/tests/cli/test_audio_config_boundary.py
+@@ -9,6 +9,7 @@ from tests.stubs.fake_voice_manager impo
+ from vibe.cli.narrator_manager import NarratorState
  
  
 [email protected]
  @pytest.mark.asyncio
  async def test_voice_mode_is_persisted_before_local_application() -> None:
      voice = FakeVoiceManager(is_voice_ready=False)
-@@ -60,6 +61,7 @@
+@@ -37,6 +38,7 @@ async def test_voice_mode_is_persisted_b
          assert voice.is_enabled is True
  
  
@@ -73,7 +73,7 @@
  @pytest.mark.asyncio
  async def test_local_voice_failure_does_not_undo_accepted_config() -> None:
      voice = FakeVoiceManager(is_voice_ready=False)
-@@ -84,6 +86,7 @@
+@@ -60,6 +62,7 @@ async def test_local_voice_failure_does_
          assert notify.call_args.kwargs["severity"] == "warning"
  
  
@@ -81,11 +81,11 @@
  @pytest.mark.asyncio
  async def test_narrator_sync_observes_accepted_config() -> None:
      narrator = MagicMock()
-Index: mistral-vibe-2.24.4/tests/cli/test_log_level_command.py
+Index: mistral-vibe-2.25.0/tests/cli/test_log_level_command.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/cli/test_log_level_command.py       
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/cli/test_log_level_command.py    2026-08-27 
09:57:37.158153357 +0200
-@@ -199,6 +199,7 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_log_level_command.py
++++ mistral-vibe-2.25.0/tests/cli/test_log_level_command.py
+@@ -199,6 +199,7 @@ async def test_picker_session_cleared_sh
      assert get_session_override() is None
  
  
@@ -93,11 +93,11 @@
  @pytest.mark.asyncio
  async def test_picker_config_write_failure_surfaces_error(monkeypatch) -> 
None:
      # When the inline (idle) config write fails, the picker must surface an
-Index: mistral-vibe-2.24.4/tests/cli/test_ui_config_and_model_picker.py
+Index: mistral-vibe-2.25.0/tests/cli/test_ui_config_and_model_picker.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/cli/test_ui_config_and_model_picker.py      
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/cli/test_ui_config_and_model_picker.py   
2026-08-27 15:09:33.762522932 +0200
-@@ -145,6 +145,7 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_ui_config_and_model_picker.py
++++ mistral-vibe-2.25.0/tests/cli/test_ui_config_and_model_picker.py
+@@ -151,6 +151,7 @@ async def test_model_picker_escape_does_
              update_config.assert_not_awaited()
  
  
@@ -105,11 +105,11 @@
  @pytest.mark.asyncio
  async def test_model_picker_select_model() -> None:
      app = build_test_vibe_app(config=_make_config_with_models())
-Index: 
mistral-vibe-2.24.4/tests/cli/textual_ui/test_custom_tools_deprecation.py
+Index: 
mistral-vibe-2.25.0/tests/cli/textual_ui/test_custom_tools_deprecation.py
 ===================================================================
---- 
mistral-vibe-2.24.4.orig/tests/cli/textual_ui/test_custom_tools_deprecation.py  
   2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/cli/textual_ui/test_custom_tools_deprecation.py  
2026-08-27 09:57:37.158693942 +0200
-@@ -21,6 +21,7 @@
+--- 
mistral-vibe-2.25.0.orig/tests/cli/textual_ui/test_custom_tools_deprecation.py
++++ mistral-vibe-2.25.0/tests/cli/textual_ui/test_custom_tools_deprecation.py
+@@ -21,6 +21,7 @@ async def _wait_for(predicate: Callable[
      pytest.fail("Condition not met within timeout")
  
  
@@ -117,7 +117,7 @@
  @pytest.mark.asyncio
  async def test_startup_warns_when_custom_tools_are_available(tmp_path: Path) 
-> None:
      tools_dir = tmp_path / "tools"
-@@ -69,6 +70,7 @@
+@@ -69,6 +70,7 @@ async def test_startup_does_not_warn_wit
      assert not app.query(CustomToolsDeprecationMessage)
  
  
@@ -125,11 +125,11 @@
  @pytest.mark.asyncio
  async def test_rebuilding_the_transcript_restores_the_custom_tools_warning(
      tmp_path: Path,
-Index: mistral-vibe-2.24.4/tests/conftest.py
+Index: mistral-vibe-2.25.0/tests/conftest.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/conftest.py 2026-08-26 17:18:33.000000000 
+0200
-+++ mistral-vibe-2.24.4/tests/conftest.py      2026-08-27 15:14:18.316888893 
+0200
-@@ -237,6 +237,17 @@
+--- mistral-vibe-2.25.0.orig/tests/conftest.py
++++ mistral-vibe-2.25.0/tests/conftest.py
+@@ -238,6 +238,17 @@ def _mock_api_key(monkeypatch: pytest.Mo
  
  
  @pytest.fixture(autouse=True)
@@ -147,11 +147,11 @@
  def _mock_platform(monkeypatch: pytest.MonkeyPatch) -> None:
      """Mock platform to be Linux with /bin/sh shell for consistent test 
behavior.
  
-Index: mistral-vibe-2.24.4/tests/e2e/test_cli_tui_fresh_install.py
+Index: mistral-vibe-2.25.0/tests/e2e/test_cli_tui_fresh_install.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/e2e/test_cli_tui_fresh_install.py   
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/e2e/test_cli_tui_fresh_install.py        
2026-08-27 09:57:37.159243674 +0200
-@@ -62,6 +62,7 @@
+--- mistral-vibe-2.25.0.orig/tests/e2e/test_cli_tui_fresh_install.py
++++ mistral-vibe-2.25.0/tests/e2e/test_cli_tui_fresh_install.py
+@@ -62,6 +62,7 @@ def _install_fresh_wheel(tmp_path: Path,
      return _venv_executable(venv_path, "vibe")
  
  
@@ -159,11 +159,11 @@
  @pytest.mark.timeout(90)
  def test_fresh_wheel_install_can_spawn_cli_and_complete_happy_path(
      streaming_mock_server: StreamingMockServer,
-Index: mistral-vibe-2.24.4/tests/e2e/test_cli_tui_hooks.py
+Index: mistral-vibe-2.25.0/tests/e2e/test_cli_tui_hooks.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/e2e/test_cli_tui_hooks.py   2026-08-26 
17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/e2e/test_cli_tui_hooks.py        2026-08-27 
09:57:37.159496414 +0200
-@@ -18,6 +18,8 @@
+--- mistral-vibe-2.25.0.orig/tests/e2e/test_cli_tui_hooks.py
++++ mistral-vibe-2.25.0/tests/e2e/test_cli_tui_hooks.py
+@@ -18,6 +18,8 @@ from tests.e2e.common import (
  )
  from tests.e2e.mock_server import StreamingMockServer
  
@@ -172,11 +172,11 @@
  
  def _setup_recording_hook(vibe_home: Path, invocation_path: Path) -> None:
      script = vibe_home / "_record_hook.py"
-Index: mistral-vibe-2.24.4/tests/e2e/test_cli_tui_onboarding.py
+Index: mistral-vibe-2.25.0/tests/e2e/test_cli_tui_onboarding.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/e2e/test_cli_tui_onboarding.py      
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/e2e/test_cli_tui_onboarding.py   2026-08-27 
09:57:37.159696533 +0200
-@@ -7,6 +7,8 @@
+--- mistral-vibe-2.25.0.orig/tests/e2e/test_cli_tui_onboarding.py
++++ mistral-vibe-2.25.0/tests/e2e/test_cli_tui_onboarding.py
+@@ -7,6 +7,8 @@ import pytest
  
  from tests.e2e.common import SpawnedVibeProcessFixture, ansi_tolerant_pattern
  
@@ -185,11 +185,11 @@
  
  @pytest.mark.timeout(15)
  def test_spawn_cli_shows_onboarding_when_api_key_missing(
-Index: mistral-vibe-2.24.4/tests/e2e/test_cli_tui_session_exit.py
+Index: mistral-vibe-2.25.0/tests/e2e/test_cli_tui_session_exit.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/e2e/test_cli_tui_session_exit.py    
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/e2e/test_cli_tui_session_exit.py 2026-08-27 
09:57:37.159895881 +0200
-@@ -24,6 +24,8 @@
+--- mistral-vibe-2.25.0.orig/tests/e2e/test_cli_tui_session_exit.py
++++ mistral-vibe-2.25.0/tests/e2e/test_cli_tui_session_exit.py
+@@ -24,6 +24,8 @@ from tests.e2e.common import (
  from tests.e2e.mock_server import StreamingMockServer
  from vibe.utils.io import read_safe
  
@@ -198,11 +198,11 @@
  
  def _usage_by_run_factory(
      request_index: int, _payload: object
-Index: mistral-vibe-2.24.4/tests/e2e/test_cli_tui_streaming.py
+Index: mistral-vibe-2.25.0/tests/e2e/test_cli_tui_streaming.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/e2e/test_cli_tui_streaming.py       
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/e2e/test_cli_tui_streaming.py    2026-08-27 
09:57:37.160090270 +0200
-@@ -14,6 +14,8 @@
+--- mistral-vibe-2.25.0.orig/tests/e2e/test_cli_tui_streaming.py
++++ mistral-vibe-2.25.0/tests/e2e/test_cli_tui_streaming.py
+@@ -14,6 +14,8 @@ from tests.e2e.common import (
  )
  from tests.e2e.mock_server import StreamingMockServer
  
@@ -211,11 +211,11 @@
  
  @pytest.mark.timeout(15)
  def test_spawn_cli_to_send_and_receive_message(
-Index: mistral-vibe-2.24.4/tests/e2e/test_cli_tui_tool_approval.py
+Index: mistral-vibe-2.25.0/tests/e2e/test_cli_tui_tool_approval.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/e2e/test_cli_tui_tool_approval.py   
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/e2e/test_cli_tui_tool_approval.py        
2026-08-27 09:57:37.160281803 +0200
-@@ -14,6 +14,8 @@
+--- mistral-vibe-2.25.0.orig/tests/e2e/test_cli_tui_tool_approval.py
++++ mistral-vibe-2.25.0/tests/e2e/test_cli_tui_tool_approval.py
+@@ -14,6 +14,8 @@ from tests.e2e.common import (
  )
  from tests.e2e.mock_server import ChatCompletionsRequestPayload, 
StreamingMockServer
  
@@ -224,11 +224,11 @@
  PREDICTABLE_OUTPUT = "__E2E_BASH_OK__"
  TOOL_ARGUMENTS = f'{{"command":"printf \\"{PREDICTABLE_OUTPUT}\\\\n\\""}}'
  
-Index: mistral-vibe-2.24.4/tests/test_install_script.py
+Index: mistral-vibe-2.25.0/tests/test_install_script.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/test_install_script.py      2026-08-26 
17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/test_install_script.py   2026-08-27 
09:57:37.160559720 +0200
-@@ -7,6 +7,8 @@
+--- mistral-vibe-2.25.0.orig/tests/test_install_script.py
++++ mistral-vibe-2.25.0/tests/test_install_script.py
+@@ -7,6 +7,8 @@ import stat
  import subprocess
  from textwrap import dedent
  
@@ -237,7 +237,7 @@
  INSTALL_SCRIPT = Path(__file__).resolve().parents[1] / "scripts" / 
"install.sh"
  
  
-@@ -146,6 +148,7 @@
+@@ -146,6 +148,7 @@ def _run_install_script(
      )
  
  

++++++ no_terminal.patch ++++++
--- /var/tmp/diff_new_pack.8Qm2cD/_old  2026-09-09 16:22:16.919853177 +0200
+++ /var/tmp/diff_new_pack.8Qm2cD/_new  2026-09-09 16:22:16.925853427 +0200
@@ -5,7 +5,7 @@
  tests/cli/test_switching_mode.py                               |    2 ++
  tests/cli/test_ui_clipboard_notifications.py                   |    2 ++
  tests/cli/test_ui_config_and_model_picker.py                   |    3 +++
- tests/cli/test_ui_config_screen.py                             |    2 ++
+ tests/cli/test_ui_config_screen.py                             |    3 +++
  tests/cli/test_ui_exit_command.py                              |    2 ++
  tests/cli/test_ui_theme_picker.py                              |    2 ++
  tests/cli/test_whoami_command.py                               |    2 ++
@@ -20,12 +20,12 @@
  tests/onboarding/test_ui_onboarding.py                         |    2 ++
  tests/tools/test_ui_bash_execution.py                          |    2 ++
  tests/vscode_extension_promo/test_ui_vscode_extension_promo.py |    2 ++
- 19 files changed, 38 insertions(+)
-Index: mistral-vibe-2.24.4/pyproject.toml
+ 19 files changed, 39 insertions(+)
+Index: mistral-vibe-2.25.0/pyproject.toml
 ===================================================================
---- mistral-vibe-2.24.4.orig/pyproject.toml    2026-08-27 09:57:37.156721782 
+0200
-+++ mistral-vibe-2.24.4/pyproject.toml 2026-08-27 09:57:43.320690567 +0200
-@@ -273,6 +273,7 @@
+--- mistral-vibe-2.25.0.orig/pyproject.toml
++++ mistral-vibe-2.25.0/pyproject.toml
+@@ -273,6 +273,7 @@ timeout = 10
  markers = [
      "unified_supported: backend contract behavior implemented by the Unified 
Harness",
      "network: tests that require network-capable execution environment",
@@ -33,11 +33,11 @@
  ]
  filterwarnings = [
      # (e2e) streaming mock server is started in a thread; and the cli is 
spawn in a fork
-Index: mistral-vibe-2.24.4/tests/cli/test_switching_mode.py
+Index: mistral-vibe-2.25.0/tests/cli/test_switching_mode.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/cli/test_switching_mode.py  2026-08-26 
17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/cli/test_switching_mode.py       2026-08-27 
09:57:43.320988152 +0200
-@@ -5,6 +5,8 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_switching_mode.py
++++ mistral-vibe-2.25.0/tests/cli/test_switching_mode.py
+@@ -5,6 +5,8 @@ from unittest.mock import patch
  
  import pytest
  
@@ -46,11 +46,11 @@
  from tests.conftest import build_test_agent_loop, build_test_vibe_app
  from tests.mock.utils import mock_llm_chunk
  from tests.stubs.fake_backend import FakeBackend
-Index: mistral-vibe-2.24.4/tests/cli/test_ui_clipboard_notifications.py
+Index: mistral-vibe-2.25.0/tests/cli/test_ui_clipboard_notifications.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/cli/test_ui_clipboard_notifications.py      
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/cli/test_ui_clipboard_notifications.py   
2026-08-27 09:57:43.321270718 +0200
-@@ -3,6 +3,8 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_ui_clipboard_notifications.py
++++ mistral-vibe-2.25.0/tests/cli/test_ui_clipboard_notifications.py
+@@ -3,6 +3,8 @@ from __future__ import annotations
  from unittest.mock import patch
  
  import pytest
@@ -59,11 +59,11 @@
  from textual.selection import Selection
  from textual.widget import Widget
  
-Index: mistral-vibe-2.24.4/tests/cli/test_ui_config_and_model_picker.py
+Index: mistral-vibe-2.25.0/tests/cli/test_ui_config_and_model_picker.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/cli/test_ui_config_and_model_picker.py      
2026-08-27 09:57:37.158402940 +0200
-+++ mistral-vibe-2.24.4/tests/cli/test_ui_config_and_model_picker.py   
2026-08-27 09:57:43.321469515 +0200
-@@ -3,6 +3,9 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_ui_config_and_model_picker.py
++++ mistral-vibe-2.25.0/tests/cli/test_ui_config_and_model_picker.py
+@@ -3,6 +3,9 @@ from __future__ import annotations
  from unittest.mock import AsyncMock, patch
  
  import pytest
@@ -72,25 +72,26 @@
 +
  from textual.widgets import OptionList
  
- from tests.conftest import build_test_vibe_app, build_test_vibe_config
-Index: mistral-vibe-2.24.4/tests/cli/test_ui_config_screen.py
+ from tests.conftest import build_test_vibe_app, build_test_vibe_config, 
wait_until
+Index: mistral-vibe-2.25.0/tests/cli/test_ui_config_screen.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/cli/test_ui_config_screen.py        
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/cli/test_ui_config_screen.py     2026-08-27 
09:57:43.321715491 +0200
-@@ -4,6 +4,8 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_ui_config_screen.py
++++ mistral-vibe-2.25.0/tests/cli/test_ui_config_screen.py
+@@ -4,6 +4,9 @@ from pathlib import Path
  import tomllib
  
  import pytest
 +
 +pytestmark = pytest.mark.terminal
++
+ from textual.pilot import Pilot
  import tomli_w
  
- from tests.conftest import (
-Index: mistral-vibe-2.24.4/tests/cli/test_ui_exit_command.py
+Index: mistral-vibe-2.25.0/tests/cli/test_ui_exit_command.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/cli/test_ui_exit_command.py 2026-08-26 
17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/cli/test_ui_exit_command.py      2026-08-27 
09:57:43.321933444 +0200
-@@ -4,6 +4,8 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_ui_exit_command.py
++++ mistral-vibe-2.25.0/tests/cli/test_ui_exit_command.py
+@@ -4,6 +4,8 @@ from typing import Any
  
  import pytest
  
@@ -99,11 +100,11 @@
  from tests.conftest import build_test_vibe_app
  from vibe.cli.textual_ui.widgets.chat_input.container import 
ChatInputContainer
  from vibe.cli.textual_ui.widgets.messages import SlashCommandMessage, 
UserMessage
-Index: mistral-vibe-2.24.4/tests/cli/test_ui_theme_picker.py
+Index: mistral-vibe-2.25.0/tests/cli/test_ui_theme_picker.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/cli/test_ui_theme_picker.py 2026-08-26 
17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/cli/test_ui_theme_picker.py      2026-08-27 
09:57:43.322155054 +0200
-@@ -7,6 +7,8 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_ui_theme_picker.py
++++ mistral-vibe-2.25.0/tests/cli/test_ui_theme_picker.py
+@@ -7,6 +7,8 @@ from unittest.mock import AsyncMock, Moc
  
  import pytest
  
@@ -112,11 +113,11 @@
  from tests.conftest import build_test_vibe_app, build_test_vibe_config
  from vibe.cli.textual_ui.app import BottomApp
  from vibe.cli.textual_ui.widgets.theme_picker import ThemePickerApp
-Index: mistral-vibe-2.24.4/tests/cli/test_whoami_command.py
+Index: mistral-vibe-2.25.0/tests/cli/test_whoami_command.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/cli/test_whoami_command.py  2026-08-26 
17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/cli/test_whoami_command.py       2026-08-27 
09:57:43.322376424 +0200
-@@ -4,6 +4,8 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_whoami_command.py
++++ mistral-vibe-2.25.0/tests/cli/test_whoami_command.py
+@@ -4,6 +4,8 @@ import asyncio
  
  import pytest
  
@@ -125,11 +126,11 @@
  from tests.conftest import build_test_vibe_app
  from tests.stubs.fake_account_gateway import FakeAccountGateway
  from tests.stubs.fake_identity_gateway import FakeIdentityGateway
-Index: mistral-vibe-2.24.4/tests/cli/test_word_selection.py
+Index: mistral-vibe-2.25.0/tests/cli/test_word_selection.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/cli/test_word_selection.py  2026-08-26 
17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/cli/test_word_selection.py       2026-08-27 
09:57:43.322622130 +0200
-@@ -3,6 +3,8 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_word_selection.py
++++ mistral-vibe-2.25.0/tests/cli/test_word_selection.py
+@@ -3,6 +3,8 @@ from __future__ import annotations
  from typing import Any, ClassVar, cast
  
  import pytest
@@ -138,10 +139,10 @@
  from textual import events
  from textual.geometry import Offset, Region
  from textual.screen import Screen
-Index: mistral-vibe-2.24.4/tests/cli/textual_ui/test_chat_input_word_drag.py
+Index: mistral-vibe-2.25.0/tests/cli/textual_ui/test_chat_input_word_drag.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/cli/textual_ui/test_chat_input_word_drag.py 
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/cli/textual_ui/test_chat_input_word_drag.py      
2026-08-27 09:57:43.322933020 +0200
+--- mistral-vibe-2.25.0.orig/tests/cli/textual_ui/test_chat_input_word_drag.py
++++ mistral-vibe-2.25.0/tests/cli/textual_ui/test_chat_input_word_drag.py
 @@ -1,6 +1,8 @@
  from __future__ import annotations
  
@@ -151,24 +152,24 @@
  from textual import events
  from textual.widgets.text_area import Selection
  
-Index: mistral-vibe-2.24.4/tests/cli/textual_ui/test_message_queue_ui.py
+Index: mistral-vibe-2.25.0/tests/cli/textual_ui/test_message_queue_ui.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/cli/textual_ui/test_message_queue_ui.py     
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/cli/textual_ui/test_message_queue_ui.py  
2026-08-27 09:57:43.323195167 +0200
-@@ -5,6 +5,8 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/textual_ui/test_message_queue_ui.py
++++ mistral-vibe-2.25.0/tests/cli/textual_ui/test_message_queue_ui.py
+@@ -6,6 +6,8 @@ import time
  
  import pytest
  
 +pytestmark = pytest.mark.terminal
 +
- from tests.conftest import build_test_vibe_app
- from vibe.cli.textual_ui.app import VibeApp
- from vibe.cli.textual_ui.widgets.chat_input.container import 
ChatInputContainer
-Index: mistral-vibe-2.24.4/tests/e2e/agent_loop_characterization/test_resume.py
+ from tests.conftest import build_test_agent_loop, build_test_vibe_app
+ from tests.mock.utils import mock_llm_chunk
+ from tests.stubs.fake_backend import FakeBackend
+Index: mistral-vibe-2.25.0/tests/e2e/agent_loop_characterization/test_resume.py
 ===================================================================
---- 
mistral-vibe-2.24.4.orig/tests/e2e/agent_loop_characterization/test_resume.py   
   2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/e2e/agent_loop_characterization/test_resume.py   
2026-08-27 09:57:43.323455320 +0200
-@@ -8,6 +8,8 @@
+--- 
mistral-vibe-2.25.0.orig/tests/e2e/agent_loop_characterization/test_resume.py
++++ mistral-vibe-2.25.0/tests/e2e/agent_loop_characterization/test_resume.py
+@@ -8,6 +8,8 @@ import time
  import pexpect
  import pytest
  
@@ -177,11 +178,11 @@
  from tests.e2e.agent_loop_characterization.support import (
      assert_assistant_tool_call_present,
      assert_message_content_present,
-Index: 
mistral-vibe-2.24.4/tests/e2e/agent_loop_characterization/test_subagents.py
+Index: 
mistral-vibe-2.25.0/tests/e2e/agent_loop_characterization/test_subagents.py
 ===================================================================
---- 
mistral-vibe-2.24.4.orig/tests/e2e/agent_loop_characterization/test_subagents.py
   2026-08-26 17:18:33.000000000 +0200
-+++ 
mistral-vibe-2.24.4/tests/e2e/agent_loop_characterization/test_subagents.py     
   2026-08-27 09:57:43.323736183 +0200
-@@ -5,6 +5,8 @@
+--- 
mistral-vibe-2.25.0.orig/tests/e2e/agent_loop_characterization/test_subagents.py
++++ mistral-vibe-2.25.0/tests/e2e/agent_loop_characterization/test_subagents.py
+@@ -5,6 +5,8 @@ from pathlib import Path
  import pexpect
  import pytest
  
@@ -190,11 +191,11 @@
  from tests.e2e.agent_loop_characterization.support import (
      assert_message_content_present,
      assert_tool_result_contains,
-Index: 
mistral-vibe-2.24.4/tests/e2e/agent_loop_characterization/test_tool_execution.py
+Index: 
mistral-vibe-2.25.0/tests/e2e/agent_loop_characterization/test_tool_execution.py
 ===================================================================
---- 
mistral-vibe-2.24.4.orig/tests/e2e/agent_loop_characterization/test_tool_execution.py
      2026-08-26 17:18:33.000000000 +0200
-+++ 
mistral-vibe-2.24.4/tests/e2e/agent_loop_characterization/test_tool_execution.py
   2026-08-27 09:57:43.323911806 +0200
-@@ -6,6 +6,8 @@
+--- 
mistral-vibe-2.25.0.orig/tests/e2e/agent_loop_characterization/test_tool_execution.py
++++ 
mistral-vibe-2.25.0/tests/e2e/agent_loop_characterization/test_tool_execution.py
+@@ -6,6 +6,8 @@ from pathlib import Path
  import pexpect
  import pytest
  
@@ -203,11 +204,11 @@
  from tests.e2e.agent_loop_characterization.support import (
      answer_approval,
      assert_assistant_tool_call_present,
-Index: 
mistral-vibe-2.24.4/tests/e2e/agent_loop_characterization/test_tool_permissions.py
+Index: 
mistral-vibe-2.25.0/tests/e2e/agent_loop_characterization/test_tool_permissions.py
 ===================================================================
---- 
mistral-vibe-2.24.4.orig/tests/e2e/agent_loop_characterization/test_tool_permissions.py
    2026-08-26 17:18:33.000000000 +0200
-+++ 
mistral-vibe-2.24.4/tests/e2e/agent_loop_characterization/test_tool_permissions.py
 2026-08-27 09:57:43.324095464 +0200
-@@ -6,6 +6,8 @@
+--- 
mistral-vibe-2.25.0.orig/tests/e2e/agent_loop_characterization/test_tool_permissions.py
++++ 
mistral-vibe-2.25.0/tests/e2e/agent_loop_characterization/test_tool_permissions.py
+@@ -6,6 +6,8 @@ from pathlib import Path
  import pexpect
  import pytest
  
@@ -216,11 +217,11 @@
  from tests.e2e.agent_loop_characterization.support import (
      answer_approval,
      assert_tool_result_contains,
-Index: 
mistral-vibe-2.24.4/tests/e2e/agent_loop_characterization/test_user_interaction.py
+Index: 
mistral-vibe-2.25.0/tests/e2e/agent_loop_characterization/test_user_interaction.py
 ===================================================================
---- 
mistral-vibe-2.24.4.orig/tests/e2e/agent_loop_characterization/test_user_interaction.py
    2026-08-26 17:18:33.000000000 +0200
-+++ 
mistral-vibe-2.24.4/tests/e2e/agent_loop_characterization/test_user_interaction.py
 2026-08-27 09:57:43.324278411 +0200
-@@ -7,6 +7,8 @@
+--- 
mistral-vibe-2.25.0.orig/tests/e2e/agent_loop_characterization/test_user_interaction.py
++++ 
mistral-vibe-2.25.0/tests/e2e/agent_loop_characterization/test_user_interaction.py
+@@ -7,6 +7,8 @@ import time
  import pexpect
  import pytest
  
@@ -229,11 +230,11 @@
  from tests.e2e.agent_loop_characterization.support import (
      APPROVAL_INPUT_GRACE_PERIOD_S,
      assert_tool_result_contains,
-Index: mistral-vibe-2.24.4/tests/onboarding/test_ui_onboarding.py
+Index: mistral-vibe-2.25.0/tests/onboarding/test_ui_onboarding.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/onboarding/test_ui_onboarding.py    
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/onboarding/test_ui_onboarding.py 2026-08-27 
09:57:43.324532673 +0200
-@@ -10,6 +10,8 @@
+--- mistral-vibe-2.25.0.orig/tests/onboarding/test_ui_onboarding.py
++++ mistral-vibe-2.25.0/tests/onboarding/test_ui_onboarding.py
+@@ -10,6 +10,8 @@ from unittest.mock import MagicMock, pat
  import keyring
  from keyring.errors import KeyringError
  import pytest
@@ -242,11 +243,11 @@
  from textual.content import Content
  from textual.events import Resize
  from textual.geometry import Size
-Index: mistral-vibe-2.24.4/tests/tools/test_ui_bash_execution.py
+Index: mistral-vibe-2.25.0/tests/tools/test_ui_bash_execution.py
 ===================================================================
---- mistral-vibe-2.24.4.orig/tests/tools/test_ui_bash_execution.py     
2026-08-26 17:18:33.000000000 +0200
-+++ mistral-vibe-2.24.4/tests/tools/test_ui_bash_execution.py  2026-08-27 
09:57:43.324865304 +0200
-@@ -5,6 +5,8 @@
+--- mistral-vibe-2.25.0.orig/tests/tools/test_ui_bash_execution.py
++++ mistral-vibe-2.25.0/tests/tools/test_ui_bash_execution.py
+@@ -5,6 +5,8 @@ import time
  from pydantic import JsonValue
  import pytest
  
@@ -255,11 +256,11 @@
  from tests.conftest import build_test_agent_loop, build_test_vibe_app
  from tests.mock.utils import mock_llm_chunk
  from tests.stubs.fake_backend import FakeBackend
-Index: 
mistral-vibe-2.24.4/tests/vscode_extension_promo/test_ui_vscode_extension_promo.py
+Index: 
mistral-vibe-2.25.0/tests/vscode_extension_promo/test_ui_vscode_extension_promo.py
 ===================================================================
---- 
mistral-vibe-2.24.4.orig/tests/vscode_extension_promo/test_ui_vscode_extension_promo.py
    2026-08-26 17:18:33.000000000 +0200
-+++ 
mistral-vibe-2.24.4/tests/vscode_extension_promo/test_ui_vscode_extension_promo.py
 2026-08-27 09:57:43.325114777 +0200
-@@ -8,6 +8,8 @@
+--- 
mistral-vibe-2.25.0.orig/tests/vscode_extension_promo/test_ui_vscode_extension_promo.py
++++ 
mistral-vibe-2.25.0/tests/vscode_extension_promo/test_ui_vscode_extension_promo.py
+@@ -8,6 +8,8 @@ from unittest.mock import patch
  
  import pytest
  

++++++ obs-test-synchronization.patch ++++++
--- /var/tmp/diff_new_pack.8Qm2cD/_old  2026-09-09 16:22:16.947854343 +0200
+++ /var/tmp/diff_new_pack.8Qm2cD/_new  2026-09-09 16:22:16.951854510 +0200
@@ -1,8 +1,8 @@
-Index: mistral-vibe-2.24.5/tests/cli/test_connector_auth_app.py
+Index: mistral-vibe-2.25.0/tests/cli/test_connector_auth_app.py
 ===================================================================
---- mistral-vibe-2.24.5.orig/tests/cli/test_connector_auth_app.py      
2026-08-31 08:24:47.483883761 +0200
-+++ mistral-vibe-2.24.5/tests/cli/test_connector_auth_app.py   2026-08-31 
08:24:47.498852360 +0200
-@@ -253,8 +253,8 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_connector_auth_app.py
++++ mistral-vibe-2.25.0/tests/cli/test_connector_auth_app.py
+@@ -253,8 +253,8 @@ class TestCloseAndRefresh:
      async def test_action_refresh_dispatches_worker(self) -> None:
          app = _make_app()
  
@@ -13,11 +13,11 @@
  
          app.run_worker = MagicMock(side_effect=close_worker)
          app.query_one = MagicMock()
-Index: mistral-vibe-2.24.5/tests/cli/test_mcp_app.py
+Index: mistral-vibe-2.25.0/tests/cli/test_mcp_app.py
 ===================================================================
---- mistral-vibe-2.24.5.orig/tests/cli/test_mcp_app.py 2026-08-31 
08:24:45.830139052 +0200
-+++ mistral-vibe-2.24.5/tests/cli/test_mcp_app.py      2026-08-31 
08:24:47.499468230 +0200
-@@ -425,8 +425,8 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_mcp_app.py
++++ mistral-vibe-2.25.0/tests/cli/test_mcp_app.py
+@@ -433,8 +433,8 @@ def test_refresh_index_replaces_state_fr
  def test_start_refresh_dispatches_one_worker() -> None:
      app = MCPApp(_state(), 
refresh_callback=AsyncMock(return_value="Refreshed"))
  
@@ -28,87 +28,11 @@
  
      app.run_worker = MagicMock(side_effect=close_worker)
  
-Index: mistral-vibe-2.24.5/tests/cli/textual_ui/test_queue_selection_ui.py
+Index: mistral-vibe-2.25.0/tests/update_notifier/test_ui_update_notification.py
 ===================================================================
---- mistral-vibe-2.24.5.orig/tests/cli/textual_ui/test_queue_selection_ui.py   
2026-08-31 08:24:45.830139052 +0200
-+++ mistral-vibe-2.24.5/tests/cli/textual_ui/test_queue_selection_ui.py        
2026-09-02 07:54:45.031953978 +0200
-@@ -627,19 +627,18 @@
-     # items so the drain consumes them by running the shell (no LLM turn).
-     async with vibe_app.run_test() as pilot:
-         chat_input = vibe_app.query_one(ChatInputContainer)
--        chat_input.value = "!sleep 2"
-+        chat_input.value = "!sleep 10"
-         await pilot.press("enter")
-         assert await _wait_until(
-             pilot, lambda: vibe_app._bash_task is not None, timeout=2.0
-         )
- 
--        chat_input.value = "!sleep 1"
-+        chat_input.value = "!sleep 10"
-         await pilot.press("enter")
--        await pilot.pause(0.05)
-+        assert await _wait_until(pilot, lambda: len(vibe_app._queue) == 1, 
timeout=6.0)
-         chat_input.value = "!echo newest"
-         await pilot.press("enter")
--        await pilot.pause(0.05)
--        assert len(vibe_app._queue) == 2
-+        assert await _wait_until(pilot, lambda: len(vibe_app._queue) == 2, 
timeout=6.0)
- 
-         body = vibe_app.query_one(ChatInputContainer)._body
-         assert body is not None
-@@ -647,16 +646,15 @@
-         # Highlight the OLDEST item (the one the drain consumes first, FIFO).
-         # newest-first list: cursor 0 = newest, cursor 1 = oldest.
-         await pilot.press("up")
--        await pilot.pause(0.1)
-+        assert await _wait_until(pilot, lambda: body._queue_cursor == 0, 
timeout=6.0)
-         await pilot.press("up")
--        await pilot.pause(0.1)
--        assert body._queue_cursor == 1
-+        assert await _wait_until(pilot, lambda: body._queue_cursor == 1, 
timeout=6.0)
-         assert vibe_app._queue_selected_widget is not None
- 
-         # Blocking bash finishes, drain consumes the highlighted oldest bash
-         # (FIFO) — the highlighted widget is removed but 
_queue_selected_widget
-         # still points at it until the next resync re-posts the scroll.
--        assert await _wait_until(pilot, lambda: len(vibe_app._queue) == 1, 
timeout=6.0)
-+        assert await _wait_until(pilot, lambda: len(vibe_app._queue) == 1, 
timeout=15.0)
-         # Re-sync via navigation hits the clamp branch (highlighted widget 
gone);
-         # the fix re-posts the scroll so _queue_selected_widget re-points at 
the
-         # surviving newest item. Without it, Backspace below no-ops.
-@@ -677,7 +675,7 @@
- ) -> None:
-     async with vibe_app.run_test() as pilot:
-         chat_input = vibe_app.query_one(ChatInputContainer)
--        chat_input.value = "!sleep 2"
-+        chat_input.value = "!sleep 10"
-         await pilot.press("enter")
-         assert await _wait_until(
-             pilot, lambda: vibe_app._bash_task is not None, timeout=2.0
-@@ -693,14 +691,14 @@
- 
-         # Enter edit mode on the bash item while the app is still busy.
-         await pilot.press("up")
--        await pilot.pause(0.1)
-+        assert await _wait_until(pilot, lambda: 
vibe_app._queue_selected_widget is not None, timeout=6.0)
-+        assert await _wait_until(pilot, lambda: body._queue_cursor == 0, 
timeout=6.0)
-         await pilot.press("enter")
--        await pilot.pause(0.1)
--        assert body._queue_in_edit_mode
-+        assert await _wait_until(pilot, lambda: body._queue_in_edit_mode, 
timeout=6.0)
-         assert body._queue_edit_kind == QueuedItemKind.BASH
- 
-         # Wait for the drain to consume the queued bash item.
--        assert await _wait_until(pilot, lambda: len(vibe_app._queue) == 0, 
timeout=6.0)
-+        assert await _wait_until(pilot, lambda: len(vibe_app._queue) == 0, 
timeout=15.0)
- 
-         assert body.input_widget is not None
-         body.input_widget.clear_text()
-Index: mistral-vibe-2.24.5/tests/update_notifier/test_ui_update_notification.py
-===================================================================
---- 
mistral-vibe-2.24.5.orig/tests/update_notifier/test_ui_update_notification.py   
   2026-08-31 08:24:45.830139052 +0200
-+++ mistral-vibe-2.24.5/tests/update_notifier/test_ui_update_notification.py   
2026-08-31 08:25:48.175638232 +0200
-@@ -28,6 +28,15 @@
+--- 
mistral-vibe-2.25.0.orig/tests/update_notifier/test_ui_update_notification.py
++++ mistral-vibe-2.25.0/tests/update_notifier/test_ui_update_notification.py
+@@ -28,6 +28,15 @@ from vibe.core.config import VibeConfigS
  TEST_CURRENT_VERSION = "0.1.0"
  
  
@@ -124,7 +48,7 @@
  @pytest.fixture
  def build_update_test_app(
      vibe_config_with_update_checks_enabled: VibeConfigSchema,
-@@ -79,6 +88,7 @@
+@@ -79,6 +88,7 @@ async def test_ui_writes_pending_update_
      )
  
      async with app.run_test() as pilot:
@@ -132,7 +56,7 @@
          await _assert_no_notifications(app, pilot, timeout=0.3)
  
      assert notifier.fetch_update_calls == 1
-@@ -91,9 +101,15 @@
+@@ -91,9 +101,15 @@ async def test_ui_does_not_notify_when_n
      build_update_test_app: Callable[..., VibeApp],
  ) -> None:
      notifier = FakeUpdateGateway(update=None)

++++++ warning_cleanup.patch ++++++
--- /var/tmp/diff_new_pack.8Qm2cD/_old  2026-09-09 16:22:16.967855177 +0200
+++ /var/tmp/diff_new_pack.8Qm2cD/_new  2026-09-09 16:22:16.971855343 +0200
@@ -6,11 +6,11 @@
  vibe/core/config/event_bus.py                     |    7 +++----
  5 files changed, 10 insertions(+), 12 deletions(-)
 
-Index: mistral-vibe-2.24.5/tests/cli/test_connector_auth_app.py
+Index: mistral-vibe-2.25.0/tests/cli/test_connector_auth_app.py
 ===================================================================
---- mistral-vibe-2.24.5.orig/tests/cli/test_connector_auth_app.py      
2026-08-27 17:26:03.000000000 +0200
-+++ mistral-vibe-2.24.5/tests/cli/test_connector_auth_app.py   2026-08-28 
17:12:00.705219399 +0200
-@@ -263,7 +263,7 @@
+--- mistral-vibe-2.25.0.orig/tests/cli/test_connector_auth_app.py
++++ mistral-vibe-2.25.0/tests/cli/test_connector_auth_app.py
+@@ -263,7 +263,7 @@ class TestCloseAndRefresh:
  
          assert app._status_message == "Refreshing connector..."
          app.run_worker.assert_called_once()
@@ -19,11 +19,11 @@
          assert kwargs["group"] == "connector_refresh"
  
  
-Index: mistral-vibe-2.24.5/vibe/cli/textual_ui/widgets/connector_auth_app.py
+Index: mistral-vibe-2.25.0/vibe/cli/textual_ui/widgets/connector_auth_app.py
 ===================================================================
---- mistral-vibe-2.24.5.orig/vibe/cli/textual_ui/widgets/connector_auth_app.py 
2026-08-27 17:26:03.000000000 +0200
-+++ mistral-vibe-2.24.5/vibe/cli/textual_ui/widgets/connector_auth_app.py      
2026-08-28 17:12:00.705596550 +0200
-@@ -98,7 +98,7 @@
+--- mistral-vibe-2.25.0.orig/vibe/cli/textual_ui/widgets/connector_auth_app.py
++++ mistral-vibe-2.25.0/vibe/cli/textual_ui/widgets/connector_auth_app.py
+@@ -98,7 +98,7 @@ class ConnectorAuthApp(Container):
          self._status_message = "Refreshing connector..."
          self._set_help_text(_HELP)
          self.run_worker(
@@ -32,23 +32,23 @@
          )
  
      # ── workers ──────────────────────────────────────────────────────
-Index: mistral-vibe-2.24.5/vibe/cli/textual_ui/widgets/mcp_app.py
+Index: mistral-vibe-2.25.0/vibe/cli/textual_ui/widgets/mcp_app.py
 ===================================================================
---- mistral-vibe-2.24.5.orig/vibe/cli/textual_ui/widgets/mcp_app.py    
2026-08-27 17:26:03.000000000 +0200
-+++ mistral-vibe-2.24.5/vibe/cli/textual_ui/widgets/mcp_app.py 2026-08-28 
17:12:00.706014339 +0200
-@@ -243,7 +243,7 @@
-         if self._refresh_callback is None or self._refreshing:
-             return
-         self._refreshing = True
--        self.run_worker(self._run_refresh(), exclusive=True, group="refresh")
-+        self.run_worker(self._run_refresh, exclusive=True, group="refresh")
+--- mistral-vibe-2.25.0.orig/vibe/cli/textual_ui/widgets/mcp_app.py
++++ mistral-vibe-2.25.0/vibe/cli/textual_ui/widgets/mcp_app.py
+@@ -248,7 +248,7 @@ class MCPApp(Container):
+         # The callback owns loop-bound app-server state. Its blocking 
discovery
+         # setup is offloaded at the app-server and Runtime I/O boundaries.
+         self.run_worker(
+-            self._run_refresh(), exclusive=True, group="refresh", 
exit_on_error=False
++            self._run_refresh, exclusive=True, group="refresh", 
exit_on_error=False
+         )
  
      async def _run_refresh(self) -> None:
-         if self._refresh_callback is not None:
-Index: mistral-vibe-2.24.5/vibe/cli/textual_ui/widgets/question_app.py
+Index: mistral-vibe-2.25.0/vibe/cli/textual_ui/widgets/question_app.py
 ===================================================================
---- mistral-vibe-2.24.5.orig/vibe/cli/textual_ui/widgets/question_app.py       
2026-08-27 17:26:03.000000000 +0200
-+++ mistral-vibe-2.24.5/vibe/cli/textual_ui/widgets/question_app.py    
2026-08-28 17:12:00.706455069 +0200
+--- mistral-vibe-2.25.0.orig/vibe/cli/textual_ui/widgets/question_app.py
++++ mistral-vibe-2.25.0/vibe/cli/textual_ui/widgets/question_app.py
 @@ -1,6 +1,5 @@
  from __future__ import annotations
  
@@ -56,7 +56,7 @@
  import time
  from typing import TYPE_CHECKING, ClassVar
  
-@@ -424,10 +423,10 @@
+@@ -424,10 +423,10 @@ class QuestionApp(VimNavigationMixin, Co
          else:
              new_idx = next(
                  i
@@ -71,11 +71,11 @@
                  if i not in self.answers
              )
              self._switch_question(new_idx)
-Index: mistral-vibe-2.24.5/vibe/core/config/event_bus.py
+Index: mistral-vibe-2.25.0/vibe/core/config/event_bus.py
 ===================================================================
---- mistral-vibe-2.24.5.orig/vibe/core/config/event_bus.py     2026-08-27 
17:26:03.000000000 +0200
-+++ mistral-vibe-2.24.5/vibe/core/config/event_bus.py  2026-08-28 
17:12:00.706804558 +0200
-@@ -2,8 +2,6 @@
+--- mistral-vibe-2.25.0.orig/vibe/core/config/event_bus.py
++++ mistral-vibe-2.25.0/vibe/core/config/event_bus.py
+@@ -2,8 +2,6 @@ from __future__ import annotations
  
  from collections.abc import Callable
  from dataclasses import dataclass
@@ -84,7 +84,7 @@
  from vibe.core.config.types import ConfigChangeCallback, ConfigChangeEvent
  
  
-@@ -18,7 +16,7 @@
+@@ -18,7 +16,7 @@ class Subscription:
  class EventBus:
      def __init__(self) -> None:
          self._subscribers: dict[int, Subscription] = {}
@@ -93,7 +93,7 @@
  
      def subscribe(
          self, callback: ConfigChangeCallback, *, keys: set[str] | None = None
-@@ -28,7 +26,8 @@
+@@ -28,7 +26,8 @@ class EventBus:
          keys filters delivery to slash-separated config paths (ancestor and
          descendant paths match); None subscribes to every change (wildcard).
          """

Reply via email to