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-28 10:45:27 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/mistral-vibe (Old) and /work/SRC/openSUSE:Factory/.mistral-vibe.new.383539 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "mistral-vibe" Mon Sep 28 10:45:27 2026 rev:22 rq:1380774 version:2.25.8 Changes: -------- --- /work/SRC/openSUSE:Factory/mistral-vibe/mistral-vibe.changes 2026-09-14 16:22:45.215503230 +0200 +++ /work/SRC/openSUSE:Factory/.mistral-vibe.new.383539/mistral-vibe.changes 2026-09-28 10:45:47.118275042 +0200 @@ -1,0 +2,63 @@ +Thu Sep 24 09:35:11 UTC 2026 - Martin Pluskal <[email protected]> + +- Update to 2.25.8 (includes 2.25.7): + * Remote Vibe Code project configuration, session + archiving with unseen indicators, pinned live todo plan + * Plugin MCP servers start so their tools are callable; + sessionless MCP add/remove, caret-aware completion + * /leanstall works on the unified backend without TUI + crashes; clean exit on terminal disconnect + * Hardened Git handling: automatic project context no longer + runs repository-configured clean or process filters, and + repository inspection, worktree setup, fetches and teleport + diffs no longer honour repository-controlled Git + executables, hooks, helpers or configuration. Worktree + operations disable core.hooksPath, so a repository's own + hooks can no longer run before trust validation + (CVE-2026-93993, boo#1282078) + * The Vibe home, prompt history, debug log and trust store are + now created owner-only, and the home plus existing session + logs are tightened at startup; ACP workspace-trust decisions + require a session and cannot target another directory + * Shell approvals no longer widen across programs, + subcommands, redirections or line continuations, and + WebFetch asks again before following a redirect to another + origin + * vibe mcp add --allow-insecure-http and /mcp add + --allow-insecure-http opt into plaintext http:// MCP servers + on non-localhost hosts + * /mcp login now fails loudly when a server never issues an + OAuth challenge, instead of reporting a false success + * Config layers merge key by key, so setting one nested value + no longer discards the rest of that group + * Sessions reopen on their last model and thinking level, + open much faster with many git worktrees, and path + completion no longer inserts stale suggestions + * /teleport accepts Vibe and workspace API keys, including on + free accounts, and says which key to use when it rejects one + * Many more fixes and improvements; see upstream's release + notes for the full list +- Add build_system_hatchling.patch: upstream now builds a PyO3 + extension with maturin, whose v8 crate downloads a toolchain at + build time; keep building the pure-Python tree with hatchling +- Add harness_optional_import.patch: keep the optional Unified + Harness distribution off the legacy app-server import path, + which otherwise fails to import without it +- Add app_server_turn_timeouts.patch: one-second asyncio waits in + the turn tests expire on a loaded build worker +- Add greeting_startup_race.patch: the startup worker raised out + of Textual when the chat widget was not on screen yet +- Refresh the carried patches for 2.25.7 and extend + harness_importorskip.patch to the test modules that now import + the harness at module scope +- Run test_respects_max_entries_to_process_limit again: the + upstream race that made it fail is gone +- Delete vibe/cli-rust and client-e2e after unpacking: the Rust + front-end declares its own "Proprietary" license in Cargo.toml + and neither tree is built or shipped +- Drop python3-sounddevice: upstream switched the audio engine to + miniaudio, which Factory does not carry, so voice recording and + playback are no longer available +- Require python3-GitPython >= 3.1.61 + +------------------------------------------------------------------- Old: ---- mistral-vibe-2.25.4.tar.gz New: ---- app_server_turn_timeouts.patch build_system_hatchling.patch greeting_startup_race.patch harness_optional_import.patch mistral-vibe-2.25.8.tar.gz ----------(New B)---------- New: which otherwise fails to import without it - Add app_server_turn_timeouts.patch: one-second asyncio waits in the turn tests expire on a loaded build worker New: notes for the full list - Add build_system_hatchling.patch: upstream now builds a PyO3 extension with maturin, whose v8 crate downloads a toolchain at New: the turn tests expire on a loaded build worker - Add greeting_startup_race.patch: the startup worker raised out of Textual when the chat widget was not on screen yet New: build time; keep building the pure-Python tree with hatchling - Add harness_optional_import.patch: keep the optional Unified Harness distribution off the legacy app-server import path, ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ mistral-vibe.spec ++++++ --- /var/tmp/diff_new_pack.km2hhE/_old 2026-09-28 10:45:49.564377525 +0200 +++ /var/tmp/diff_new_pack.km2hhE/_new 2026-09-28 10:45:49.568377692 +0200 @@ -28,9 +28,16 @@ %endif %define origname mistral-vibe Name: %{origname}%{psuffix} -Version: 2.25.4 +Version: 2.25.8 Release: 0 Summary: Minimal CLI coding agent by Mistral +# Legal-Review-Notice: vibe/cli-rust is not built and not shipped. It is an +# alternative Rust front-end whose own Cargo.toml declares license = +# "Proprietary", while the repository LICENSE and the [project] metadata both +# say Apache-2.0. Nothing here compiles or installs it: the directory is +# deleted right after the sources are unpacked and the wheel configuration +# excludes it, so every file in the binary packages comes from the +# Apache-2.0 Python tree. License: Apache-2.0 URL: https://github.com/mistralai/mistral-vibe Source0: https://github.com/mistralai/mistral-vibe/archive/refs/tags/v%{version}.tar.gz#/mistral-vibe-%{version}.tar.gz @@ -60,9 +67,22 @@ # invisible to the file fingerprint; hash the contents too Patch7: fingerprint_file_contents.patch # PATCH-FIX-UPSTREAM harness_importorskip.patch [email protected] -# the harness is an optional extra: skip the module when it is missing -# instead of failing collection, same as the other harness test modules +# the harness is an optional extra: skip the modules that import it at module +# scope instead of failing collection, same as upstream's own harness guards Patch8: harness_importorskip.patch +# PATCH-FIX-UPSTREAM harness_optional_import.patch [email protected] +# keep the optional harness distribution off the legacy app-server import path +Patch9: harness_optional_import.patch +# PATCH-FIX-OPENSUSE build_system_hatchling.patch [email protected] +# upstream builds a PyO3 extension with maturin, whose v8 crate downloads a +# toolchain at build time; build the pure-Python tree with hatchling instead +Patch10: build_system_hatchling.patch +# PATCH-FIX-OPENSUSE app_server_turn_timeouts.patch [email protected] +# one-second asyncio waits time out under the parallel run on a busy worker +Patch11: app_server_turn_timeouts.patch +# PATCH-FIX-UPSTREAM greeting_startup_race.patch [email protected] +# the post-ready worker must not raise when the chat widget is not on screen +Patch12: greeting_startup_race.patch BuildRequires: fdupes BuildRequires: python-rpm-macros BuildRequires: python3-base >= 3.12 @@ -73,7 +93,7 @@ BuildRequires: python3-rfc8785 >= 0.1.4 BuildArch: noarch %if !%{with test} -Requires: python3-GitPython >= 3.1.57 +Requires: python3-GitPython >= 3.1.61 Requires: python3-PyJWT >= 2.13.0 Requires: python3-PyYAML >= 6.0.3 Requires: python3-SecretStorage >= 3.5.0 @@ -149,7 +169,6 @@ Requires: python3-setproctitle >= 1.3.7 Requires: python3-six >= 1.17.0 Requires: python3-smmap >= 5.0.3 -Requires: python3-sounddevice >= 0.5.5 Requires: python3-soupsieve >= 2.8.4 Requires: python3-sse-starlette >= 3.4.1 Requires: python3-starlette >= 1.3.1 @@ -180,7 +199,7 @@ %if %{with test} BuildRequires: ca-certificates BuildRequires: ca-certificates-mozilla -BuildRequires: python3-GitPython >= 3.1.57 +BuildRequires: python3-GitPython >= 3.1.61 BuildRequires: python3-Jinja2 BuildRequires: python3-PyJWT >= 2.13.0 BuildRequires: python3-PyYAML >= 6.0.3 @@ -259,7 +278,6 @@ BuildRequires: python3-rpds-py >= 0.30.0 BuildRequires: python3-sentry-sdk >= 2.64.0 BuildRequires: python3-setproctitle >= 1.3.7 -BuildRequires: python3-sounddevice >= 0.5.5 BuildRequires: python3-soupsieve >= 2.8.4 BuildRequires: python3-sse-starlette >= 3.4.1 BuildRequires: python3-starlette >= 1.3.1 @@ -293,6 +311,9 @@ %prep %autosetup -p1 -n %{origname}-%{version} +# not built, not shipped: cli-rust declares its own "Proprietary" license and +# client-e2e is a standalone suite upstream excludes from this project's pytest +rm -rf vibe/cli-rust client-e2e %build %python3_pyproject_wheel @@ -313,10 +334,16 @@ 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" -# -k deselects: the ssl_cert_file test needs network certs; the -# max_entries test races scandir walk order against the processing -# cap and fails with an empty assertion set (upstream 2.25.3 race). -%python3_pytest -m 'not (network or terminal)' -k 'not test_generic_backend_streaming_uses_ssl_cert_file and not test_respects_max_entries_to_process_limit' +# The ssl_cert_file test drives a TLS mock server over a real socket; the +# request never completes here and the test only ends when pytest-timeout +# kills it, so deselect it. +# Twelve tests need the experimental harness (mistralai_vibe_local_harness, +# a Rust extension built from harness/ and not shipped in this wheel): +# ten resolve_agent_types tests import its protocol types at runtime, and +# two expect its stubbed (not absent) import error. Deselect them. +# (test_respects_max_entries_to_process_limit was deselected until 2.25.7 +# for an upstream race; it passes again.) +%python3_pytest -m 'not (network or terminal)' -k 'not test_generic_backend_streaming_uses_ssl_cert_file and not test_a_project_subagent_is_advertised_and_bound and not test_explicit_instructions_win_over_a_named_prompt and not test_a_subagent_with_no_prompt_inherits_the_parents and not test_a_tool_the_profile_disables_is_denied_to_the_child and not test_the_advertised_half_alone_needs_no_tool_catalogue and not test_a_tool_the_profile_disables_is_not_reported_as_downgraded and not test_an_agent_file_that_shadows_explore_is_advertised and not test_a_granted_tool_narrowed_by_an_allowlist_is_capped_at_ask and not test_a_named_prompt_becomes_the_prompt_the_child_runs_on and not test_a_granted_tool_with_no_list_stays_granted and not test_available_experimental_harness_never_opens_a_legacy_runtime and not test_a_host_that_cannot_take_builtin_hooks_falls_back_to_legacy' %endif %if !%{with test} ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.km2hhE/_old 2026-09-28 10:45:49.742384982 +0200 +++ /var/tmp/diff_new_pack.km2hhE/_new 2026-09-28 10:45:49.758385653 +0200 @@ -1,5 +1,5 @@ -mtime: 1789319620 -commit: ef25ca8d7a609d354780161982786da34888317bf5d41e677dea43f769268b97 +mtime: 1790407971 +commit: 75f8f6d271afd980d4963eaada9f0b743c56944bd65cd54f6f7400bcd379e606 url: https://src.opensuse.org/AI/mistral-vibe.git revision: factory ++++++ app_server_turn_timeouts.patch ++++++ From: Martin Pluskal <[email protected]> Subject: give the turn tests room on a loaded build worker The asyncio.wait_for() calls in these two modules allow one second for an event the runtime sets almost immediately. Under the parallel test run that budget measures scheduling latency rather than a real bound, and two tests (the background-title quiescence test here and the stale-turn-control test in backend_contract) fail intermittently with TimeoutError. Nothing in either module asserts that a wait times out, so raise the budget; it stays below the 60 s pytest-timeout cap even when several waits stack up in one test. Index: mistral-vibe-2.25.7/tests/app_server/backend_contract/test_turns.py =================================================================== --- mistral-vibe-2.25.7.orig/tests/app_server/backend_contract/test_turns.py +++ mistral-vibe-2.25.7/tests/app_server/backend_contract/test_turns.py @@ -590,7 +590,7 @@ async def test_interrupt_terminates_an_a turn = asyncio.create_task(consume_turn()) try: - await asyncio.wait_for(started.wait(), timeout=1) + await asyncio.wait_for(started.wait(), timeout=10) await backend_contract_session.interrupt() await turn finally: @@ -715,7 +715,7 @@ async def test_steering_adds_a_public_us turn = asyncio.create_task(consume_turn()) try: - await asyncio.wait_for(started.wait(), timeout=1) + await asyncio.wait_for(started.wait(), timeout=10) active_turn = backend_contract_session.state.latest_turn assert active_turn is not None await backend_contract_session.inject_user_context( @@ -762,7 +762,7 @@ async def test_stale_turn_control_return turn = asyncio.create_task(consume_turn()) try: - await asyncio.wait_for(started.wait(), timeout=1) + await asyncio.wait_for(started.wait(), timeout=10) with pytest.raises(AppServerResponseError) as exc_info: await backend_contract_connection.client.request( "turn/steer", @@ -802,10 +802,10 @@ async def test_reconnect_replays_turn_ou turn = asyncio.create_task(consume_turn()) try: - await asyncio.wait_for(started.wait(), timeout=1) + await asyncio.wait_for(started.wait(), timeout=10) await backend_contract_connection.client.close() release.set() - events = await asyncio.wait_for(turn, timeout=1) + events = await asyncio.wait_for(turn, timeout=10) finally: release.set() if not turn.done(): Index: mistral-vibe-2.25.7/tests/app_server/test_turns.py =================================================================== --- mistral-vibe-2.25.7.orig/tests/app_server/test_turns.py +++ mistral-vibe-2.25.7/tests/app_server/test_turns.py @@ -65,8 +65,8 @@ async def test_background_title_keeps_ru background_events = session.events() try: - during_turn = await asyncio.wait_for(_consume(turn_events), timeout=1) - await asyncio.wait_for(generation_started.wait(), timeout=1) + during_turn = await asyncio.wait_for(_consume(turn_events), timeout=10) + await asyncio.wait_for(generation_started.wait(), timeout=10) pending = [ event @@ -79,7 +79,7 @@ async def test_background_title_keeps_ru finish_generation.set() delivered: list[object] = [] while True: - event = await asyncio.wait_for(background_events.__anext__(), timeout=1) + event = await asyncio.wait_for(background_events.__anext__(), timeout=10) delivered.append(event) if isinstance(event, SessionSnapshot) and event.state.is_quiescent is True: break @@ -127,8 +127,8 @@ async def test_retry_state_survives_unre turns = legacy_backend(server).session.turns try: - await asyncio.wait_for(retry_started.wait(), timeout=1) - retry_snapshot = await asyncio.wait_for(retry_snapshot_task, timeout=1) + await asyncio.wait_for(retry_started.wait(), timeout=10) + retry_snapshot = await asyncio.wait_for(retry_snapshot_task, timeout=10) legacy_retry = await _next_event(events, TurnRetrying) retrying = turns.retrying active_turn = turns.active_turn @@ -185,7 +185,7 @@ async def test_interrupt_clears_retry_st await retry_sink(RetryReason.from_http_status(503)) future = asyncio.run_coroutine_threadsafe(report_retry(), loop) - future.result(timeout=1) + future.result(timeout=10) await asyncio.to_thread(emit_retry) retry_started.set() @@ -203,8 +203,8 @@ async def test_interrupt_clears_retry_st turns = legacy_backend(server).session.turns try: - await asyncio.wait_for(retry_started.wait(), timeout=1) - retry_snapshot = await asyncio.wait_for(retry_snapshot_task, timeout=1) + await asyncio.wait_for(retry_started.wait(), timeout=10) + retry_snapshot = await asyncio.wait_for(retry_snapshot_task, timeout=10) await _next_event(events, TurnRetrying) assert turns.retrying is not None assert retry_snapshot.state.retrying == turns.retrying @@ -251,7 +251,7 @@ async def test_retry_from_interrupted_tu await self.on_retry(RetryReason.from_http_status(503)) future = asyncio.run_coroutine_threadsafe(report_retry(), loop) - future.result(timeout=1) + future.result(timeout=10) loop.call_soon_threadsafe(late_retry_finished.set) self.late_retry_task = asyncio.create_task( @@ -280,23 +280,23 @@ async def test_retry_from_interrupted_tu second_consumer: asyncio.Task[list[object]] | None = None try: - await asyncio.wait_for(first_turn_started.wait(), timeout=1) + await asyncio.wait_for(first_turn_started.wait(), timeout=10) await session.interrupt() - await asyncio.wait_for(first_consumer, timeout=1) + await asyncio.wait_for(first_consumer, timeout=10) assert turns.retrying is None second_events = session.act("second") second_consumer = asyncio.create_task(_consume(second_events)) - await asyncio.wait_for(second_turn_started.wait(), timeout=1) + await asyncio.wait_for(second_turn_started.wait(), timeout=10) release_late_retry.set() - await asyncio.wait_for(late_retry_finished.wait(), timeout=1) + await asyncio.wait_for(late_retry_finished.wait(), timeout=10) assert turns.retrying is None assert session.state.retrying is None finish_second_turn.set() - await asyncio.wait_for(second_consumer, timeout=1) + await asyncio.wait_for(second_consumer, timeout=10) finally: release_late_retry.set() finish_second_turn.set() ++++++ 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-26 09:32:51.000000000 +0200 @@ -0,0 +1,9 @@ +.assets +_build.* +*.obscpio +*.obsinfo +*.osc +.pbuild +_service:* +mistral-vibe-*-build/ +.aider* ++++++ build_system_hatchling.patch ++++++ From: Martin Pluskal <[email protected]> Subject: build the pure-Python distribution with hatchling 2.25.7 switched [build-system] to maturin so that the PyO3 extension mistralai_vibe_local_harness._native is compiled from harness/core. That crate pulls deno_core/deno_ast and pins v8, whose build script either downloads a prebuilt static library or fetches GN, ninja and clang from chromium.googlesource.com -- neither is possible in an offline build, and upstream publishes no sdist to fall back on. Restore the 2.25.4 hatchling configuration so the pure-Python vibe/ package is built; the Unified Harness is optional at runtime (vibe/_experimental_harness.py defaults to the legacy backend and raises ExperimentalHarnessUnavailableError when the distribution is missing), so the CLI is fully functional without it, minus --experimental-harness. Index: mistral-vibe-2.25.7/pyproject.toml =================================================================== --- mistral-vibe-2.25.7.orig/pyproject.toml +++ mistral-vibe-2.25.7/pyproject.toml @@ -141,33 +141,24 @@ Issues = "https://github.com/mistralai/m Documentation = "https://github.com/mistralai/mistral-vibe#readme" [build-system] -requires = ["maturin==1.14.1"] -build-backend = "maturin_backend" -backend-path = ["build_backend"] +requires = ["hatchling==1.31.0", "hatch-vcs==0.5.0", "editables==0.6"] +build-backend = "hatchling.build" + +[tool.hatch.metadata] +allow-direct-references = true [[tool.uv.index]] default = true name = "pypi" url = "https://pypi.org/simple" -[tool.maturin] -manifest-path = ".native-build/harness-core/Cargo.toml" -module-name = "mistralai_vibe_local_harness._native" -python-source = "." -python-packages = ["vibe", "mistralai_vibe_local_harness"] -include = [ - { path = "vibe/_bin/*", format = "wheel" }, - # The runtime is staged during the build and ignored by Git. Include it - # explicitly so Maturin does not filter its Python sources out. - { path = "mistralai_vibe_local_harness/*", format = "wheel" }, - { path = "mistralai_vibe_local_harness/**/*", format = "wheel" }, -] -exclude = [ - "**/__pycache__/**", - "**/*.pyc", - "vibe/cli-rust/**", -] -features = ["extension-module"] +[tool.hatch.build.targets.wheel] +# "packages", not the 2.25.4 "include": an include pattern is gitignore-style +# and "vibe/" would also match harness/.../mistralai_vibe_local_harness/vibe. +packages = ["vibe"] +# vibe/cli-rust is the (unbuilt, separately licensed) Rust CLI source tree; +# upstream's maturin config excludes it from the wheel too. +exclude = ["vibe/cli-rust/**"] [project.scripts] vibe = "vibe.cli.launcher:main" ++++++ fingerprint_file_contents.patch ++++++ --- /var/tmp/diff_new_pack.km2hhE/_old 2026-09-28 10:45:50.291407984 +0200 +++ /var/tmp/diff_new_pack.km2hhE/_new 2026-09-28 10:45:50.306408613 +0200 @@ -9,12 +9,12 @@ 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 +Index: mistral-vibe-2.25.7/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) +--- mistral-vibe-2.25.7.orig/vibe/core/config/fingerprint.py ++++ mistral-vibe-2.25.7/vibe/core/config/fingerprint.py +@@ -29,10 +29,35 @@ def open_fingerprinted_file(path: Path) + yield file, create_file_fingerprint(file) +_FINGERPRINT_CHUNK_SIZE = 1 << 16 ++++++ fix_ui_tests.patch ++++++ --- /var/tmp/diff_new_pack.km2hhE/_old 2026-09-28 10:45:50.349410414 +0200 +++ /var/tmp/diff_new_pack.km2hhE/_new 2026-09-28 10:45:50.356410708 +0200 @@ -7,10 +7,10 @@ tests/test_ui_rewind.py | 2 ++ 6 files changed, 20 insertions(+), 2 deletions(-) -Index: mistral-vibe-2.25.1/tests/app_server/test_vibe_code.py +Index: mistral-vibe-2.25.7/tests/app_server/test_vibe_code.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/app_server/test_vibe_code.py -+++ mistral-vibe-2.25.1/tests/app_server/test_vibe_code.py +--- mistral-vibe-2.25.7.orig/tests/app_server/test_vibe_code.py ++++ mistral-vibe-2.25.7/tests/app_server/test_vibe_code.py @@ -68,7 +68,7 @@ def test_app_server_import_does_not_requ capture_output=True, env=env, @@ -20,10 +20,10 @@ ) assert result.returncode == 0, result.stderr -Index: mistral-vibe-2.25.1/tests/cli/test_ui_config_and_model_picker.py +Index: mistral-vibe-2.25.7/tests/cli/test_ui_config_and_model_picker.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/cli/test_ui_config_and_model_picker.py -+++ mistral-vibe-2.25.1/tests/cli/test_ui_config_and_model_picker.py +--- mistral-vibe-2.25.7.orig/tests/cli/test_ui_config_and_model_picker.py ++++ mistral-vibe-2.25.7/tests/cli/test_ui_config_and_model_picker.py @@ -1,5 +1,6 @@ from __future__ import annotations @@ -31,7 +31,7 @@ from unittest.mock import AsyncMock, patch import pytest -@@ -52,6 +53,15 @@ async def _open_model_picker(pilot, app) +@@ -54,6 +55,15 @@ async def _open_model_picker(pilot, app) return picker @@ -47,7 +47,7 @@ # --- /model command --- -@@ -181,7 +191,7 @@ async def test_model_picker_select_curre +@@ -183,7 +193,7 @@ async def test_model_picker_select_curre await _open_model_picker(pilot, app) await pilot.press("enter") @@ -56,10 +56,10 @@ assert app.config.active_model.alias == "alpha" assert app._current_bottom_app == BottomApp.Input -Index: mistral-vibe-2.25.1/tests/cli/test_ui_session_incremental_renderer.py +Index: mistral-vibe-2.25.7/tests/cli/test_ui_session_incremental_renderer.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/cli/test_ui_session_incremental_renderer.py -+++ mistral-vibe-2.25.1/tests/cli/test_ui_session_incremental_renderer.py +--- mistral-vibe-2.25.7.orig/tests/cli/test_ui_session_incremental_renderer.py ++++ mistral-vibe-2.25.7/tests/cli/test_ui_session_incremental_renderer.py @@ -5,6 +5,8 @@ from pathlib import Path import time @@ -69,10 +69,10 @@ from textual.widgets import Button from tests.conftest import build_test_agent_loop -Index: mistral-vibe-2.25.1/tests/cli/test_ui_skill_dispatch.py +Index: mistral-vibe-2.25.7/tests/cli/test_ui_skill_dispatch.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/cli/test_ui_skill_dispatch.py -+++ mistral-vibe-2.25.1/tests/cli/test_ui_skill_dispatch.py +--- mistral-vibe-2.25.7.orig/tests/cli/test_ui_skill_dispatch.py ++++ mistral-vibe-2.25.7/tests/cli/test_ui_skill_dispatch.py @@ -8,6 +8,8 @@ from weakref import WeakKeyDictionary import pytest @@ -82,27 +82,27 @@ from tests.conftest import ( build_test_agent_loop, build_test_vibe_app, -Index: mistral-vibe-2.25.1/tests/cli/textual_ui/test_app_server_requests.py +Index: mistral-vibe-2.25.7/tests/cli/textual_ui/test_app_server_requests.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/cli/textual_ui/test_app_server_requests.py -+++ mistral-vibe-2.25.1/tests/cli/textual_ui/test_app_server_requests.py -@@ -8,6 +8,8 @@ from unittest.mock import AsyncMock, Mag - +--- mistral-vibe-2.25.7.orig/tests/cli/textual_ui/test_app_server_requests.py ++++ mistral-vibe-2.25.7/tests/cli/textual_ui/test_app_server_requests.py +@@ -10,6 +10,8 @@ from unittest.mock import AsyncMock, Mag import pytest + from textual.worker import WorkerCancelled +pytestmark = pytest.mark.terminal + from tests.conftest import ( build_test_agent_loop, build_test_vibe_app, -Index: mistral-vibe-2.25.1/tests/test_ui_rewind.py +Index: mistral-vibe-2.25.7/tests/test_ui_rewind.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/test_ui_rewind.py -+++ mistral-vibe-2.25.1/tests/test_ui_rewind.py -@@ -4,6 +4,8 @@ from contextlib import suppress - +--- mistral-vibe-2.25.7.orig/tests/test_ui_rewind.py ++++ mistral-vibe-2.25.7/tests/test_ui_rewind.py +@@ -5,6 +5,8 @@ from contextlib import suppress + import pytest - + +pytestmark = pytest.mark.terminal + from tests.conftest import build_test_agent_loop, build_test_vibe_app, wait_until ++++++ greeting_startup_race.patch ++++++ From: Martin Pluskal <[email protected]> Subject: do not crash the post-ready worker when the chat is gone _show_greeting_message() runs inside the non-exclusive worker started by _start_post_ready_startup(), after an await on the account and identity refreshes. When the chat widget is not on the screen by the time that await returns, query_one("#chat") raises NoMatches out of the worker and Textual re-raises it at the end of run_test(), failing whichever UI test happened to be running. The greeting is cosmetic, so skip it; messages.py already guards the same lookup this way. Index: mistral-vibe-2.25.7/vibe/cli/textual_ui/app.py =================================================================== --- mistral-vibe-2.25.7.orig/vibe/cli/textual_ui/app.py +++ mistral-vibe-2.25.7/vibe/cli/textual_ui/app.py @@ -22,6 +22,7 @@ from rich import print as rprint from textual.app import WINDOWS, App, ComposeResult from textual.binding import Binding, BindingType from textual.containers import Horizontal, VerticalGroup, VerticalScroll +from textual.css.query import NoMatches from textual.dom import NoScreen from textual.driver import Driver from textual.events import AppBlur, AppFocus, MouseScrollDown, MouseScrollUp, MouseUp @@ -6267,7 +6268,12 @@ class VibeApp(App): # noqa: PLR0904 if username is None: return greeting_message = GreetingMessage(username) - chat = self._chat_widget + try: + chat = self._chat_widget + except NoMatches: + # Nothing to mount into: this runs in the post-ready worker, which + # can still be waiting on the network when the chat goes away. + return # Mount after banner so it appears below banner and scrolls up with messages await chat.mount(greeting_message, after=self._banner) self._greeting_message = greeting_message ++++++ harness_importorskip.patch ++++++ --- /var/tmp/diff_new_pack.km2hhE/_old 2026-09-28 10:45:50.424413557 +0200 +++ /var/tmp/diff_new_pack.km2hhE/_new 2026-09-28 10:45:50.433413934 +0200 @@ -1,24 +1,99 @@ ---- - tests/app_server/test_unified_harness_backend_adapter.py | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) +From: Martin Pluskal <[email protected]> +Subject: skip the harness-only tests when the harness is absent -Index: mistral-vibe-2.25.1/tests/app_server/test_unified_harness_backend_adapter.py +mistralai-vibe-local-harness is built by the maturin backend this package +does not run, so the distribution is not installed. Guard the test modules +that import it at module scope with pytest.importorskip, the way upstream +already guards its other harness tests, so collection skips them instead of +erroring. + +Index: mistral-vibe-2.25.7/tests/app_server/test_unified_harness_backend_adapter.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/app_server/test_unified_harness_backend_adapter.py -+++ mistral-vibe-2.25.1/tests/app_server/test_unified_harness_backend_adapter.py -@@ -14,10 +14,13 @@ +--- mistral-vibe-2.25.7.orig/tests/app_server/test_unified_harness_backend_adapter.py ++++ mistral-vibe-2.25.7/tests/app_server/test_unified_harness_backend_adapter.py +@@ -17,6 +17,13 @@ from types import SimpleNamespace from typing import TYPE_CHECKING, Any, Literal, cast from unittest.mock import AsyncMock, Mock --import mistralai_vibe_local_harness.session_protocol as harness_session_protocol - import pytest - import tomli_w - -+# mistralai-vibe-local-harness is an optional extra: skip this module when -+# it is missing instead of failing collection, like the other harness tests -+harness_session_protocol = pytest.importorskip("mistralai_vibe_local_harness.session_protocol") ++import pytest ++import tomli_w ++ ++# mistralai-vibe-local-harness is an optional extra openSUSE does not ship: ++# skip instead of failing collection, like upstream's own harness guards. ++pytest.importorskip("mistralai_vibe_local_harness") + + import mistralai_vibe_local_harness.session_protocol as harness_session_protocol + from mistralai_vibe_local_harness.vibe import ( + DeferredTurnPreparationContext as HarnessDeferredTurnPreparationContext, +@@ -25,8 +32,6 @@ from mistralai_vibe_local_harness.vibe i + DeferredTurnStartParams as HarnessDeferredTurnStartParams, + DeferredTurnStartResult as HarnessDeferredTurnStartResult, + ) +-import pytest +-import tomli_w + from tests.conftest import build_test_agent_loop, build_test_vibe_config from tests.mock.utils import mock_llm_chunk - from tests.stubs.app_server import build_test_app_server +Index: mistral-vibe-2.25.7/tests/app_server/test_provided_tools.py +=================================================================== +--- mistral-vibe-2.25.7.orig/tests/app_server/test_provided_tools.py ++++ mistral-vibe-2.25.7/tests/app_server/test_provided_tools.py +@@ -3,6 +3,12 @@ from __future__ import annotations + from collections.abc import Awaitable, Callable + from pathlib import Path + ++import pytest ++ ++# mistralai-vibe-local-harness is an optional extra openSUSE does not ship: ++# skip instead of failing collection, like upstream's own harness guards. ++pytest.importorskip("mistralai_vibe_local_harness") ++ + from mistralai_vibe_local_harness.protocol import ( + JsonObject, + JsonSchema, +@@ -13,7 +19,6 @@ from mistralai_vibe_local_harness.protoc + RustToolFailedEvent, + RustToolSucceededEvent, + ) +-import pytest + + from vibe.app_server._provided_tools import ( + TODO_TOOL_NAME, +Index: mistral-vibe-2.25.7/tests/app_server/test_unified_scratchpad.py +=================================================================== +--- mistral-vibe-2.25.7.orig/tests/app_server/test_unified_scratchpad.py ++++ mistral-vibe-2.25.7/tests/app_server/test_unified_scratchpad.py +@@ -433,6 +433,7 @@ async def test_notes_written_through_the + def test_the_scratchpad_lives_inside_the_harness_session_directory( + tmp_path: Path, + ) -> None: ++ pytest.importorskip("mistralai_vibe_local_harness") + from mistralai_vibe_local_harness.vibe._host import UnifiedHarnessSessionBackendHost + + # Reaching for the private ``_session_root`` on purpose: the scratchpad shares +Index: mistral-vibe-2.25.7/tests/app_server/test_unified_tool_projection.py +=================================================================== +--- mistral-vibe-2.25.7.orig/tests/app_server/test_unified_tool_projection.py ++++ mistral-vibe-2.25.7/tests/app_server/test_unified_tool_projection.py +@@ -4,6 +4,12 @@ import asyncio + from copy import deepcopy + from pathlib import Path + ++import pytest ++ ++# mistralai-vibe-local-harness is an optional extra openSUSE does not ship: ++# skip instead of failing collection, like upstream's own harness guards. ++pytest.importorskip("mistralai_vibe_local_harness") ++ + from mistralai_vibe_local_harness.protocol import ( + JsonObject, + RustEvent, +@@ -11,7 +17,6 @@ from mistralai_vibe_local_harness.protoc + RustProvidedToolCallAction, + RustToolSucceededEvent, + ) +-import pytest + + from vibe.app_server._provided_tools import VIBE_TOOL_GROUP, VibeProvidedTools + from vibe.app_server._unified_scratchpad import SCRATCHPAD_TOOL_NAME ++++++ harness_optional_import.patch ++++++ From: Martin Pluskal <[email protected]> Subject: keep the Harness off the legacy import path _provided_tools imports mistralai_vibe_local_harness.protocol at module scope, so the module-scope import of it here pulled the Harness distribution in for every app-server import -- including the legacy path, which must run without it (the neighbouring imports below say so). vibe_tool_groups() is only ever called from build_unified_session_context(), so import it there. Index: mistral-vibe-2.25.7/vibe/app_server/_runtime.py =================================================================== --- mistral-vibe-2.25.7.orig/vibe/app_server/_runtime.py +++ mistral-vibe-2.25.7/vibe/app_server/_runtime.py @@ -29,7 +29,6 @@ from vibe.app_server._projection import project_skill_summaries, project_unified_agent_summaries, ) -from vibe.app_server._provided_tools import vibe_tool_groups from vibe.app_server._session_backend_port import SessionBackendHost from vibe.app_server._session_backend_services import SessionBackendServices from vibe.app_server._session_model import ( @@ -1331,6 +1330,7 @@ class HarnessProcess: # Imported here rather than at module scope: it depends on the Harness # package, which only exists under the experimental-harness extra. + from vibe.app_server._provided_tools import vibe_tool_groups from vibe.app_server._provider_credentials import ProviderCredentialService from vibe.app_server._unified_harness_backend_adapter import ( CorrelationIdHolder, ++++++ mistral-vibe-2.25.4.tar.gz -> mistral-vibe-2.25.8.tar.gz ++++++ ++++ 469987 lines of diff (skipped) ++++++ no_network.patch ++++++ --- /var/tmp/diff_new_pack.km2hhE/_old 2026-09-28 10:45:52.835514572 +0200 +++ /var/tmp/diff_new_pack.km2hhE/_new 2026-09-28 10:45:52.841514824 +0200 @@ -16,11 +16,11 @@ tests/test_install_script.py | 3 +++ 15 files changed, 36 insertions(+) -Index: mistral-vibe-2.25.0/pyproject.toml +Index: mistral-vibe-2.25.7/pyproject.toml =================================================================== ---- 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 +--- mistral-vibe-2.25.7.orig/pyproject.toml ++++ mistral-vibe-2.25.7/pyproject.toml +@@ -306,6 +306,7 @@ addopts = "-p pytest_vibe -vvvv -q -n au timeout = 10 markers = [ "unified_supported: backend contract behavior implemented by the Unified Harness", @@ -28,10 +28,10 @@ ] filterwarnings = [ # (e2e) streaming mock server is started in a thread; and the cli is spawn in a fork -Index: mistral-vibe-2.25.0/tests/acp/test_acp_entrypoint_smoke.py +Index: mistral-vibe-2.25.7/tests/acp/test_acp_entrypoint_smoke.py =================================================================== ---- 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 +--- mistral-vibe-2.25.7.orig/tests/acp/test_acp_entrypoint_smoke.py ++++ mistral-vibe-2.25.7/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.25.0/tests/app_server/test_transport.py +Index: mistral-vibe-2.25.7/tests/app_server/test_transport.py =================================================================== ---- 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_ +--- mistral-vibe-2.25.7.orig/tests/app_server/test_transport.py ++++ mistral-vibe-2.25.7/tests/app_server/test_transport.py +@@ -281,6 +281,7 @@ async def test_stdio_transport_close_is_ assert [json.loads(line) for line in writer.getvalue().splitlines()] == [{"id": 1}] @@ -53,10 +53,10 @@ @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.25.0/tests/cli/test_audio_config_boundary.py +Index: mistral-vibe-2.25.7/tests/cli/test_audio_config_boundary.py =================================================================== ---- 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 +--- mistral-vibe-2.25.7.orig/tests/cli/test_audio_config_boundary.py ++++ mistral-vibe-2.25.7/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 @@ -81,10 +81,10 @@ @pytest.mark.asyncio async def test_narrator_sync_observes_accepted_config() -> None: narrator = MagicMock() -Index: mistral-vibe-2.25.0/tests/cli/test_log_level_command.py +Index: mistral-vibe-2.25.7/tests/cli/test_log_level_command.py =================================================================== ---- 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 +--- mistral-vibe-2.25.7.orig/tests/cli/test_log_level_command.py ++++ mistral-vibe-2.25.7/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.25.0/tests/cli/test_ui_config_and_model_picker.py +Index: mistral-vibe-2.25.7/tests/cli/test_ui_config_and_model_picker.py =================================================================== ---- 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_ +--- mistral-vibe-2.25.7.orig/tests/cli/test_ui_config_and_model_picker.py ++++ mistral-vibe-2.25.7/tests/cli/test_ui_config_and_model_picker.py +@@ -153,6 +153,7 @@ async def test_model_picker_escape_does_ update_config.assert_not_awaited() @@ -105,10 +105,10 @@ @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.25.0/tests/cli/textual_ui/test_custom_tools_deprecation.py +Index: mistral-vibe-2.25.7/tests/cli/textual_ui/test_custom_tools_deprecation.py =================================================================== ---- 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 +--- mistral-vibe-2.25.7.orig/tests/cli/textual_ui/test_custom_tools_deprecation.py ++++ mistral-vibe-2.25.7/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") @@ -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.25.0/tests/conftest.py +Index: mistral-vibe-2.25.7/tests/conftest.py =================================================================== ---- 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 +--- mistral-vibe-2.25.7.orig/tests/conftest.py ++++ mistral-vibe-2.25.7/tests/conftest.py +@@ -275,6 +275,17 @@ def _mock_api_key(monkeypatch: pytest.Mo @pytest.fixture(autouse=True) @@ -147,22 +147,22 @@ def _mock_platform(monkeypatch: pytest.MonkeyPatch) -> None: """Mock platform to be Linux with /bin/sh shell for consistent test behavior. -Index: mistral-vibe-2.25.0/tests/e2e/test_cli_tui_fresh_install.py +Index: mistral-vibe-2.25.7/tests/e2e/test_cli_tui_fresh_install.py =================================================================== ---- 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, +--- mistral-vibe-2.25.7.orig/tests/e2e/test_cli_tui_fresh_install.py ++++ mistral-vibe-2.25.7/tests/e2e/test_cli_tui_fresh_install.py +@@ -81,6 +81,7 @@ def _install_fresh_wheel(tmp_path: Path, return _venv_executable(venv_path, "vibe") [email protected] - @pytest.mark.timeout(90) + @pytest.mark.timeout(900) def test_fresh_wheel_install_can_spawn_cli_and_complete_happy_path( streaming_mock_server: StreamingMockServer, -Index: mistral-vibe-2.25.0/tests/e2e/test_cli_tui_hooks.py +Index: mistral-vibe-2.25.7/tests/e2e/test_cli_tui_hooks.py =================================================================== ---- 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 +--- mistral-vibe-2.25.7.orig/tests/e2e/test_cli_tui_hooks.py ++++ mistral-vibe-2.25.7/tests/e2e/test_cli_tui_hooks.py @@ -18,6 +18,8 @@ from tests.e2e.common import ( ) from tests.e2e.mock_server import StreamingMockServer @@ -172,10 +172,10 @@ def _setup_recording_hook(vibe_home: Path, invocation_path: Path) -> None: script = vibe_home / "_record_hook.py" -Index: mistral-vibe-2.25.0/tests/e2e/test_cli_tui_onboarding.py +Index: mistral-vibe-2.25.7/tests/e2e/test_cli_tui_onboarding.py =================================================================== ---- 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 +--- mistral-vibe-2.25.7.orig/tests/e2e/test_cli_tui_onboarding.py ++++ mistral-vibe-2.25.7/tests/e2e/test_cli_tui_onboarding.py @@ -7,6 +7,8 @@ import pytest from tests.e2e.common import SpawnedVibeProcessFixture, ansi_tolerant_pattern @@ -185,10 +185,10 @@ @pytest.mark.timeout(15) def test_spawn_cli_shows_onboarding_when_api_key_missing( -Index: mistral-vibe-2.25.0/tests/e2e/test_cli_tui_session_exit.py +Index: mistral-vibe-2.25.7/tests/e2e/test_cli_tui_session_exit.py =================================================================== ---- 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 +--- mistral-vibe-2.25.7.orig/tests/e2e/test_cli_tui_session_exit.py ++++ mistral-vibe-2.25.7/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,10 +198,10 @@ def _usage_by_run_factory( request_index: int, _payload: object -Index: mistral-vibe-2.25.0/tests/e2e/test_cli_tui_streaming.py +Index: mistral-vibe-2.25.7/tests/e2e/test_cli_tui_streaming.py =================================================================== ---- 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 +--- mistral-vibe-2.25.7.orig/tests/e2e/test_cli_tui_streaming.py ++++ mistral-vibe-2.25.7/tests/e2e/test_cli_tui_streaming.py @@ -14,6 +14,8 @@ from tests.e2e.common import ( ) from tests.e2e.mock_server import StreamingMockServer @@ -211,10 +211,10 @@ @pytest.mark.timeout(15) def test_spawn_cli_to_send_and_receive_message( -Index: mistral-vibe-2.25.0/tests/e2e/test_cli_tui_tool_approval.py +Index: mistral-vibe-2.25.7/tests/e2e/test_cli_tui_tool_approval.py =================================================================== ---- 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 +--- mistral-vibe-2.25.7.orig/tests/e2e/test_cli_tui_tool_approval.py ++++ mistral-vibe-2.25.7/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,10 +224,10 @@ PREDICTABLE_OUTPUT = "__E2E_BASH_OK__" TOOL_ARGUMENTS = f'{{"command":"printf \\"{PREDICTABLE_OUTPUT}\\\\n\\""}}' -Index: mistral-vibe-2.25.0/tests/test_install_script.py +Index: mistral-vibe-2.25.7/tests/test_install_script.py =================================================================== ---- mistral-vibe-2.25.0.orig/tests/test_install_script.py -+++ mistral-vibe-2.25.0/tests/test_install_script.py +--- mistral-vibe-2.25.7.orig/tests/test_install_script.py ++++ mistral-vibe-2.25.7/tests/test_install_script.py @@ -7,6 +7,8 @@ import stat import subprocess from textwrap import dedent ++++++ no_terminal.patch ++++++ --- /var/tmp/diff_new_pack.km2hhE/_old 2026-09-28 10:45:52.869515997 +0200 +++ /var/tmp/diff_new_pack.km2hhE/_new 2026-09-28 10:45:52.873516165 +0200 @@ -1,31 +1,8 @@ -# PATCH-FIX-OPENSUSE no_terminal.patch [email protected] -# mark tests requiring a terminal or PTY so they are skipped in OBS builds ---- - pyproject.toml | 1 + - 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 | 3 +++ - tests/cli/test_ui_exit_command.py | 2 ++ - tests/cli/test_ui_theme_picker.py | 2 ++ - tests/cli/test_whoami_command.py | 2 ++ - tests/cli/test_word_selection.py | 2 ++ - tests/cli/textual_ui/test_chat_input_word_drag.py | 2 ++ - tests/cli/textual_ui/test_message_queue_ui.py | 2 ++ - tests/e2e/agent_loop_characterization/test_resume.py | 2 ++ - tests/e2e/agent_loop_characterization/test_subagents.py | 2 ++ - tests/e2e/agent_loop_characterization/test_tool_execution.py | 2 ++ - tests/e2e/agent_loop_characterization/test_tool_permissions.py | 2 ++ - tests/e2e/agent_loop_characterization/test_user_interaction.py | 2 ++ - 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, 39 insertions(+) -Index: mistral-vibe-2.25.1/pyproject.toml -=================================================================== ---- mistral-vibe-2.25.1.orig/pyproject.toml -+++ mistral-vibe-2.25.1/pyproject.toml -@@ -273,6 +273,7 @@ timeout = 10 +Index: mistral-vibe-2.25.8/pyproject.toml +=================================================================== +--- mistral-vibe-2.25.8.orig/pyproject.toml ++++ mistral-vibe-2.25.8/pyproject.toml +@@ -307,6 +307,7 @@ markers = [ "unified_supported: backend contract behavior implemented by the Unified Harness", "network: tests that require network-capable execution environment", @@ -33,11 +10,11 @@ ] filterwarnings = [ # (e2e) streaming mock server is started in a thread; and the cli is spawn in a fork -Index: mistral-vibe-2.25.1/tests/cli/test_switching_mode.py +Index: mistral-vibe-2.25.8/tests/cli/test_switching_mode.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/cli/test_switching_mode.py -+++ mistral-vibe-2.25.1/tests/cli/test_switching_mode.py -@@ -5,6 +5,8 @@ from unittest.mock import patch +--- mistral-vibe-2.25.8.orig/tests/cli/test_switching_mode.py ++++ mistral-vibe-2.25.8/tests/cli/test_switching_mode.py +@@ -5,6 +5,8 @@ import pytest @@ -46,11 +23,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.25.1/tests/cli/test_ui_clipboard_notifications.py +Index: mistral-vibe-2.25.8/tests/cli/test_ui_clipboard_notifications.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/cli/test_ui_clipboard_notifications.py -+++ mistral-vibe-2.25.1/tests/cli/test_ui_clipboard_notifications.py -@@ -3,6 +3,8 @@ from __future__ import annotations +--- mistral-vibe-2.25.8.orig/tests/cli/test_ui_clipboard_notifications.py ++++ mistral-vibe-2.25.8/tests/cli/test_ui_clipboard_notifications.py +@@ -3,6 +3,8 @@ from unittest.mock import patch import pytest @@ -59,11 +36,11 @@ from textual.selection import Selection from textual.widget import Widget -Index: mistral-vibe-2.25.1/tests/cli/test_ui_config_and_model_picker.py +Index: mistral-vibe-2.25.8/tests/cli/test_ui_config_and_model_picker.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/cli/test_ui_config_and_model_picker.py -+++ mistral-vibe-2.25.1/tests/cli/test_ui_config_and_model_picker.py -@@ -3,6 +3,9 @@ from __future__ import annotations +--- mistral-vibe-2.25.8.orig/tests/cli/test_ui_config_and_model_picker.py ++++ mistral-vibe-2.25.8/tests/cli/test_ui_config_and_model_picker.py +@@ -3,6 +3,9 @@ from unittest.mock import AsyncMock, patch import pytest @@ -73,11 +50,11 @@ from textual.widgets import OptionList from tests.conftest import build_test_vibe_app, build_test_vibe_config, wait_until -Index: mistral-vibe-2.25.1/tests/cli/test_ui_config_screen.py +Index: mistral-vibe-2.25.8/tests/cli/test_ui_config_screen.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/cli/test_ui_config_screen.py -+++ mistral-vibe-2.25.1/tests/cli/test_ui_config_screen.py -@@ -4,6 +4,9 @@ from pathlib import Path +--- mistral-vibe-2.25.8.orig/tests/cli/test_ui_config_screen.py ++++ mistral-vibe-2.25.8/tests/cli/test_ui_config_screen.py +@@ -4,6 +4,9 @@ import tomllib import pytest @@ -87,11 +64,11 @@ from textual.pilot import Pilot import tomli_w -Index: mistral-vibe-2.25.1/tests/cli/test_ui_exit_command.py +Index: mistral-vibe-2.25.8/tests/cli/test_ui_exit_command.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/cli/test_ui_exit_command.py -+++ mistral-vibe-2.25.1/tests/cli/test_ui_exit_command.py -@@ -4,6 +4,8 @@ from typing import Any +--- mistral-vibe-2.25.8.orig/tests/cli/test_ui_exit_command.py ++++ mistral-vibe-2.25.8/tests/cli/test_ui_exit_command.py +@@ -4,6 +4,8 @@ import pytest @@ -100,11 +77,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.25.1/tests/cli/test_ui_theme_picker.py +Index: mistral-vibe-2.25.8/tests/cli/test_ui_theme_picker.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/cli/test_ui_theme_picker.py -+++ mistral-vibe-2.25.1/tests/cli/test_ui_theme_picker.py -@@ -7,6 +7,8 @@ from unittest.mock import AsyncMock, Moc +--- mistral-vibe-2.25.8.orig/tests/cli/test_ui_theme_picker.py ++++ mistral-vibe-2.25.8/tests/cli/test_ui_theme_picker.py +@@ -5,6 +5,8 @@ import pytest @@ -113,11 +90,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.25.1/tests/cli/test_whoami_command.py +Index: mistral-vibe-2.25.8/tests/cli/test_whoami_command.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/cli/test_whoami_command.py -+++ mistral-vibe-2.25.1/tests/cli/test_whoami_command.py -@@ -4,6 +4,8 @@ import asyncio +--- mistral-vibe-2.25.8.orig/tests/cli/test_whoami_command.py ++++ mistral-vibe-2.25.8/tests/cli/test_whoami_command.py +@@ -4,6 +4,8 @@ import pytest @@ -126,11 +103,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.25.1/tests/cli/test_word_selection.py +Index: mistral-vibe-2.25.8/tests/cli/test_word_selection.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/cli/test_word_selection.py -+++ mistral-vibe-2.25.1/tests/cli/test_word_selection.py -@@ -3,6 +3,8 @@ from __future__ import annotations +--- mistral-vibe-2.25.8.orig/tests/cli/test_word_selection.py ++++ mistral-vibe-2.25.8/tests/cli/test_word_selection.py +@@ -3,6 +3,8 @@ from typing import Any, ClassVar, cast import pytest @@ -139,10 +116,10 @@ from textual import events from textual.geometry import Offset, Region from textual.screen import Screen -Index: mistral-vibe-2.25.1/tests/cli/textual_ui/test_chat_input_word_drag.py +Index: mistral-vibe-2.25.8/tests/cli/textual_ui/test_chat_input_word_drag.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/cli/textual_ui/test_chat_input_word_drag.py -+++ mistral-vibe-2.25.1/tests/cli/textual_ui/test_chat_input_word_drag.py +--- mistral-vibe-2.25.8.orig/tests/cli/textual_ui/test_chat_input_word_drag.py ++++ mistral-vibe-2.25.8/tests/cli/textual_ui/test_chat_input_word_drag.py @@ -1,6 +1,8 @@ from __future__ import annotations @@ -152,24 +129,24 @@ from textual import events from textual.widgets.text_area import Selection -Index: mistral-vibe-2.25.1/tests/cli/textual_ui/test_message_queue_ui.py +Index: mistral-vibe-2.25.8/tests/cli/textual_ui/test_message_queue_ui.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/cli/textual_ui/test_message_queue_ui.py -+++ mistral-vibe-2.25.1/tests/cli/textual_ui/test_message_queue_ui.py -@@ -4,6 +4,8 @@ from collections.abc import Iterator - +--- mistral-vibe-2.25.8.orig/tests/cli/textual_ui/test_message_queue_ui.py ++++ mistral-vibe-2.25.8/tests/cli/textual_ui/test_message_queue_ui.py +@@ -5,6 +5,8 @@ + import pytest - + +pytestmark = pytest.mark.terminal + from tests.conftest import ( build_test_agent_loop, build_test_vibe_app, - Index: mistral-vibe-2.25.1/tests/e2e/agent_loop_characterization/test_resume.py +Index: mistral-vibe-2.25.8/tests/e2e/agent_loop_characterization/test_resume.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/e2e/agent_loop_characterization/test_resume.py -+++ mistral-vibe-2.25.1/tests/e2e/agent_loop_characterization/test_resume.py -@@ -8,6 +8,8 @@ import time +--- mistral-vibe-2.25.8.orig/tests/e2e/agent_loop_characterization/test_resume.py ++++ mistral-vibe-2.25.8/tests/e2e/agent_loop_characterization/test_resume.py +@@ -8,6 +8,8 @@ import pexpect import pytest @@ -178,11 +155,11 @@ from tests.e2e.agent_loop_characterization.support import ( assert_assistant_tool_call_present, assert_message_content_present, -Index: mistral-vibe-2.25.1/tests/e2e/agent_loop_characterization/test_subagents.py +Index: mistral-vibe-2.25.8/tests/e2e/agent_loop_characterization/test_subagents.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/e2e/agent_loop_characterization/test_subagents.py -+++ mistral-vibe-2.25.1/tests/e2e/agent_loop_characterization/test_subagents.py -@@ -5,6 +5,8 @@ from pathlib import Path +--- mistral-vibe-2.25.8.orig/tests/e2e/agent_loop_characterization/test_subagents.py ++++ mistral-vibe-2.25.8/tests/e2e/agent_loop_characterization/test_subagents.py +@@ -5,6 +5,8 @@ import pexpect import pytest @@ -191,11 +168,11 @@ from tests.e2e.agent_loop_characterization.support import ( assert_message_content_present, assert_tool_result_contains, -Index: mistral-vibe-2.25.1/tests/e2e/agent_loop_characterization/test_tool_execution.py +Index: mistral-vibe-2.25.8/tests/e2e/agent_loop_characterization/test_tool_execution.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/e2e/agent_loop_characterization/test_tool_execution.py -+++ mistral-vibe-2.25.1/tests/e2e/agent_loop_characterization/test_tool_execution.py -@@ -6,6 +6,8 @@ from pathlib import Path +--- mistral-vibe-2.25.8.orig/tests/e2e/agent_loop_characterization/test_tool_execution.py ++++ mistral-vibe-2.25.8/tests/e2e/agent_loop_characterization/test_tool_execution.py +@@ -6,6 +6,8 @@ import pexpect import pytest @@ -204,11 +181,11 @@ from tests.e2e.agent_loop_characterization.support import ( answer_approval, assert_assistant_tool_call_present, -Index: mistral-vibe-2.25.1/tests/e2e/agent_loop_characterization/test_tool_permissions.py +Index: mistral-vibe-2.25.8/tests/e2e/agent_loop_characterization/test_tool_permissions.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/e2e/agent_loop_characterization/test_tool_permissions.py -+++ mistral-vibe-2.25.1/tests/e2e/agent_loop_characterization/test_tool_permissions.py -@@ -6,6 +6,8 @@ from pathlib import Path +--- mistral-vibe-2.25.8.orig/tests/e2e/agent_loop_characterization/test_tool_permissions.py ++++ mistral-vibe-2.25.8/tests/e2e/agent_loop_characterization/test_tool_permissions.py +@@ -6,6 +6,8 @@ import pexpect import pytest @@ -217,11 +194,11 @@ from tests.e2e.agent_loop_characterization.support import ( answer_approval, assert_tool_result_contains, -Index: mistral-vibe-2.25.1/tests/e2e/agent_loop_characterization/test_user_interaction.py +Index: mistral-vibe-2.25.8/tests/e2e/agent_loop_characterization/test_user_interaction.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/e2e/agent_loop_characterization/test_user_interaction.py -+++ mistral-vibe-2.25.1/tests/e2e/agent_loop_characterization/test_user_interaction.py -@@ -7,6 +7,8 @@ import time +--- mistral-vibe-2.25.8.orig/tests/e2e/agent_loop_characterization/test_user_interaction.py ++++ mistral-vibe-2.25.8/tests/e2e/agent_loop_characterization/test_user_interaction.py +@@ -7,6 +7,8 @@ import pexpect import pytest @@ -230,11 +207,11 @@ from tests.e2e.agent_loop_characterization.support import ( APPROVAL_INPUT_GRACE_PERIOD_S, assert_tool_result_contains, -Index: mistral-vibe-2.25.1/tests/onboarding/test_ui_onboarding.py +Index: mistral-vibe-2.25.8/tests/onboarding/test_ui_onboarding.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/onboarding/test_ui_onboarding.py -+++ mistral-vibe-2.25.1/tests/onboarding/test_ui_onboarding.py -@@ -10,6 +10,8 @@ from unittest.mock import MagicMock, pat +--- mistral-vibe-2.25.8.orig/tests/onboarding/test_ui_onboarding.py ++++ mistral-vibe-2.25.8/tests/onboarding/test_ui_onboarding.py +@@ -10,6 +10,8 @@ import keyring from keyring.errors import KeyringError import pytest @@ -243,11 +220,11 @@ from textual.content import Content from textual.events import Resize from textual.geometry import Size -Index: mistral-vibe-2.25.1/tests/tools/test_ui_bash_execution.py +Index: mistral-vibe-2.25.8/tests/tools/test_ui_bash_execution.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/tools/test_ui_bash_execution.py -+++ mistral-vibe-2.25.1/tests/tools/test_ui_bash_execution.py -@@ -5,6 +5,8 @@ import time +--- mistral-vibe-2.25.8.orig/tests/tools/test_ui_bash_execution.py ++++ mistral-vibe-2.25.8/tests/tools/test_ui_bash_execution.py +@@ -5,6 +5,8 @@ from pydantic import JsonValue import pytest @@ -256,11 +233,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.25.1/tests/vscode_extension_promo/test_ui_vscode_extension_promo.py +Index: mistral-vibe-2.25.8/tests/vscode_extension_promo/test_ui_vscode_extension_promo.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/vscode_extension_promo/test_ui_vscode_extension_promo.py -+++ mistral-vibe-2.25.1/tests/vscode_extension_promo/test_ui_vscode_extension_promo.py -@@ -8,6 +8,8 @@ from unittest.mock import patch +--- mistral-vibe-2.25.8.orig/tests/vscode_extension_promo/test_ui_vscode_extension_promo.py ++++ mistral-vibe-2.25.8/tests/vscode_extension_promo/test_ui_vscode_extension_promo.py +@@ -8,6 +8,8 @@ import pytest ++++++ obs-test-synchronization.patch ++++++ --- /var/tmp/diff_new_pack.km2hhE/_old 2026-09-28 10:45:52.892516961 +0200 +++ /var/tmp/diff_new_pack.km2hhE/_new 2026-09-28 10:45:52.896517128 +0200 @@ -1,7 +1,7 @@ -Index: mistral-vibe-2.25.0/tests/cli/test_connector_auth_app.py +Index: mistral-vibe-2.25.7/tests/cli/test_connector_auth_app.py =================================================================== ---- 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 +--- mistral-vibe-2.25.7.orig/tests/cli/test_connector_auth_app.py ++++ mistral-vibe-2.25.7/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.25.0/tests/cli/test_mcp_app.py +Index: mistral-vibe-2.25.7/tests/cli/test_mcp_app.py =================================================================== ---- 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 +--- mistral-vibe-2.25.7.orig/tests/cli/test_mcp_app.py ++++ mistral-vibe-2.25.7/tests/cli/test_mcp_app.py +@@ -466,8 +466,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,10 +28,10 @@ app.run_worker = MagicMock(side_effect=close_worker) -Index: mistral-vibe-2.25.0/tests/update_notifier/test_ui_update_notification.py +Index: mistral-vibe-2.25.7/tests/update_notifier/test_ui_update_notification.py =================================================================== ---- 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 +--- mistral-vibe-2.25.7.orig/tests/update_notifier/test_ui_update_notification.py ++++ mistral-vibe-2.25.7/tests/update_notifier/test_ui_update_notification.py @@ -28,6 +28,15 @@ from vibe.core.config import VibeConfigS TEST_CURRENT_VERSION = "0.1.0" @@ -73,23 +73,14 @@ await _assert_no_notifications(app, pilot, timeout=0.3) assert notifier.fetch_update_calls == 1 -Index: mistral-vibe-2.25.1/tests/autocompletion/test_ui_chat_autocompletion.py +Index: mistral-vibe-2.25.7/tests/autocompletion/test_ui_chat_autocompletion.py =================================================================== ---- mistral-vibe-2.25.1.orig/tests/autocompletion/test_ui_chat_autocompletion.py -+++ mistral-vibe-2.25.1/tests/autocompletion/test_ui_chat_autocompletion.py -@@ -2,6 +2,7 @@ - - from pathlib import Path - -+import asyncio - import pytest - from textual.widgets import Markdown - -@@ -12,6 +13,14 @@ - _CompletionRow, - ) +--- mistral-vibe-2.25.7.orig/tests/autocompletion/test_ui_chat_autocompletion.py ++++ mistral-vibe-2.25.7/tests/autocompletion/test_ui_chat_autocompletion.py +@@ -18,6 +18,15 @@ from vibe.cli.textual_ui.widgets.chat_in from vibe.cli.textual_ui.widgets.chat_input.container import ChatInputContainer -+ + + +async def _wait_until(pilot, predicate, timeout: float = 6.0) -> bool: + deadline = asyncio.get_running_loop().time() + timeout + while asyncio.get_running_loop().time() < deadline: @@ -97,10 +88,12 @@ + return True + await pilot.pause(0.05) + return predicate() - - ++ ++ @pytest.mark.asyncio -@@ -183,6 +192,21 @@ + async def test_popup_appears_with_matching_suggestions(vibe_app: VibeApp) -> None: + async with vibe_app.run_test() as pilot: +@@ -187,6 +196,21 @@ async def test_path_completion_popup_sho await pilot.press(*"@src/core/extra/") @@ -122,20 +115,8 @@ popup_content = popup.content_text assert "src/core/extra/extra_file_1.py" in popup_content assert "src/core/extra/extra_file_10.py" in popup_content -@@ -225,9 +249,8 @@ - - await pilot.press(*"Print @REA") - await pilot.press("tab") -- await pilot.pause(0.2) -- -- assert chat_input.value == "Print @README.md " -+ -+ assert await _wait_until(pilot, lambda: chat_input.value == "Print @README.md ") - assert popup.styles.display == "none" - - -@@ -242,7 +265,7 @@ - await pilot.press(*"Print @src/m") +@@ -287,7 +311,7 @@ async def test_pressing_enter_writes_sel + await wait_for_path_completions(pilot) await pilot.press("enter") - assert chat_input.value == "Print @src/main.py " @@ -143,7 +124,7 @@ assert popup.styles.display == "none" -@@ -317,7 +340,7 @@ +@@ -363,7 +387,7 @@ async def test_does_not_trigger_completi await pilot.press(*message_to_fill_history) await pilot.press("enter") await pilot.press("up", "up") @@ -152,9 +133,12 @@ await pilot.pause(0.2) # ensure popup is hidden - user was navigating history: we don't want to interrupt assert popup.styles.display == "none" -@@ -329,3 +353,3 @@ +@@ -373,6 +397,6 @@ async def test_does_not_trigger_completi + # get back to the message with path completion; ensure again + await pilot.press("up") await pilot.pause(0.1) - assert chat_input.value == "Check @src/main.py" + assert await _wait_until(pilot, lambda: chat_input.value == "Check @src/main.py") await pilot.pause(0.2) + assert popup.styles.display == "none"
