diff --git a/omnigent/onboarding/harness_install.py b/omnigent/onboarding/harness_install.py index 5f84a1f7..035792cc 100644 --- a/omnigent/onboarding/harness_install.py +++ b/omnigent/onboarding/harness_install.py @@ -201,17 +201,17 @@ _HARNESS_INSTALL: dict[str, HarnessInstallSpec] = { # ``pi >= 0.79.0``; older CLIs would prompt mid-session. min_version=_PI_MIN_VERSION, ), - # Pin the install to the supported 1.17.x range: opencode-ai's npm ``latest`` + # Pin the install to the supported 1.18.x range: opencode-ai's npm ``latest`` # is a ``0.0.0-beta-*`` pre-release, so a bare ``opencode-ai`` would install a # version the runtime version-check (``check_opencode_version``, - # >=1.17.7,<1.18.0) then rejects. ``~1.17.7`` mirrors that exact range. + # >=1.17.7,<1.19.0) then rejects. ``~1.18.0`` resolves to the latest 1.18.x. # The same version bounds are enforced in setup via ``min_version`` / # ``max_version_exclusive`` so the install/upgrade prompt fires before # the runtime gate does. OPENCODE_KEY: HarnessInstallSpec( "OpenCode", "opencode", - "opencode-ai@~1.17.7", + "opencode-ai@~1.18.0", min_version=OPENCODE_MIN_VERSION, max_version_exclusive=OPENCODE_MAX_VERSION_EXCLUSIVE, ), diff --git a/omnigent/opencode_native_client.py b/omnigent/opencode_native_client.py index 0e40818c..39e89084 100644 --- a/omnigent/opencode_native_client.py +++ b/omnigent/opencode_native_client.py @@ -1,6 +1,6 @@ """Typed HTTP + SSE client for an ``opencode serve`` native server. -Shaped from the pinned OpenCode OpenAPI (``opencode`` 1.17.x, +Shaped from the pinned OpenCode OpenAPI (``opencode`` 1.17.x–1.18.x, ``packages/sdk/openapi.json``). This is a thin typed wrapper over the v1 REST endpoints the Omnigent OpenCode-native harness needs plus the SSE ``GET /event`` stream — not a full generated SDK. Unknown response fields @@ -29,10 +29,11 @@ from omnigent.json_types import JsonObject as _JsonObject _logger = logging.getLogger(__name__) -# Pinned OpenCode CLI/API version range. The source monorepo reports -# 1.17.7; we accept 1.17.x and refuse 1.18+ until validated. +# Supported OpenCode CLI/API version range. Accepts 1.17.7+ through the +# entire 1.18.x line (validated against 1.18.x event/API shapes); refuses +# 1.19+ until validated against that release. OPENCODE_MIN_VERSION = "1.17.7" -OPENCODE_MAX_VERSION_EXCLUSIVE = "1.18.0" +OPENCODE_MAX_VERSION_EXCLUSIVE = "1.19.0" _DEFAULT_TIMEOUT = httpx.Timeout(30.0, connect=10.0) diff --git a/omnigent/opencode_native_forwarder.py b/omnigent/opencode_native_forwarder.py index 399c5b79..dac93ef0 100644 --- a/omnigent/opencode_native_forwarder.py +++ b/omnigent/opencode_native_forwarder.py @@ -1043,8 +1043,8 @@ def opencode_tool_output_text(state: _JsonMapping) -> str: # resolves the method on the instance. Keys are OpenCode event ``type`` # discriminators (see openapi.json Event* schemas). _HANDLERS: dict[str, Callable[[OpenCodeNativeForwarder, OpenCodeEvent], Awaitable[None]]] = { - # opencode 1.17.x is part-based: text/tool live on message PARTS, lifecycle - # on the message + session. (Verified against a real ``opencode serve``.) + # opencode 1.17.x–1.18.x is part-based: text/tool live on message PARTS, + # lifecycle on the message + session. (Verified against real ``opencode serve``.) "message.updated": OpenCodeNativeForwarder._on_message_updated, "message.part.updated": OpenCodeNativeForwarder._on_part_updated, # NB: ``message.part.delta`` (live token stream) is intentionally NOT @@ -1066,7 +1066,7 @@ _HANDLERS: dict[str, Callable[[OpenCodeNativeForwarder, OpenCodeEvent], Awaitabl "session.compacted": OpenCodeNativeForwarder._on_compaction_ended, # Mirror a TUI model switch back to Omnigent (in-harness session-cmd sync). "session.next.model.switched": OpenCodeNativeForwarder._on_model_switched, - # Permission ask: 1.17.x emits ``permission.asked``; keep the ``v2`` spelling + # Permission ask: pre-1.18 emits ``permission.asked``; keep the ``v2`` spelling # too so a point-release rename still routes through the policy gate. "permission.asked": OpenCodeNativeForwarder._on_permission_asked, "permission.v2.asked": OpenCodeNativeForwarder._on_permission_asked, diff --git a/tests/onboarding/test_harness_install.py b/tests/onboarding/test_harness_install.py index 61b8a69c..7ccac100 100644 --- a/tests/onboarding/test_harness_install.py +++ b/tests/onboarding/test_harness_install.py @@ -1154,7 +1154,7 @@ def test_ui_setup_steps_generic_for_non_installable() -> None: @pytest.mark.parametrize( "key,min_version,max_version_exclusive", [ - (hi.OPENCODE_KEY, "1.17.7", "1.18.0"), + (hi.OPENCODE_KEY, "1.17.7", "1.19.0"), (hi.CURSOR_KEY, "2026.06.02", None), (hi.KIMI_KEY, "0.7.0", None), (ANTHROPIC_FAMILY, "2.1.161", None), @@ -1180,9 +1180,10 @@ def test_versioned_specs_declare_bounds( "version,expected", [ ("1.17.6", False), # below min - ("1.18.0", False), # at max exclusive + ("1.19.0", False), # at max exclusive ("2.0.0", False), # above max ("1.17.8", True), # inside range + ("1.18.16", True), # inside range (1.18.x) ], ) def test_harness_cli_installed_checks_version_for_versioned_specs( @@ -1194,7 +1195,7 @@ def test_harness_cli_installed_checks_version_for_versioned_specs( def _run(argv: list[str], **k: object) -> subprocess.CompletedProcess[str]: if len(argv) >= 2 and argv[1] == "--version": - # OpenCode's supported range is [1.17.7, 1.18.0). + # OpenCode's supported range is [1.17.7, 1.19.0). return subprocess.CompletedProcess( args=argv, returncode=0, stdout=f"{version}\n", stderr="" ) diff --git a/tests/test_opencode_native_app_server.py b/tests/test_opencode_native_app_server.py index 397fef51..47c1ed90 100644 --- a/tests/test_opencode_native_app_server.py +++ b/tests/test_opencode_native_app_server.py @@ -31,9 +31,11 @@ def test_parse_opencode_version() -> None: def test_check_version_in_range() -> None: check_opencode_version("1.17.7") check_opencode_version("1.17.99") + check_opencode_version("1.18.0") + check_opencode_version("1.18.16") -@pytest.mark.parametrize("version", ["1.16.0", "1.18.0", "2.0.0"]) +@pytest.mark.parametrize("version", ["1.16.0", "1.19.0", "2.0.0"]) def test_check_version_out_of_range_raises(version: str) -> None: with pytest.raises(OpenCodeVersionError): check_opencode_version(version) @@ -186,7 +188,7 @@ async def test_start_raises_on_unsupported_version_without_env( monkeypatch: pytest.MonkeyPatch, tmp_path: Path ) -> None: monkeypatch.setattr(appsrv.shutil, "which", lambda name: f"/usr/bin/{name}") - monkeypatch.setattr(appsrv, "resolve_opencode_version", lambda _path: "1.18.0") + monkeypatch.setattr(appsrv, "resolve_opencode_version", lambda _path: "1.19.0") monkeypatch.delenv("OMNIGENT_OPENCODE_SKIP_VERSION_CHECK", raising=False) server = OpenCodeNativeServer( bridge_dir=tmp_path, @@ -214,7 +216,7 @@ async def test_start_skips_version_gate_when_env_set( monkeypatch: pytest.MonkeyPatch, tmp_path: Path ) -> None: monkeypatch.setattr(appsrv.shutil, "which", lambda name: f"/usr/bin/{name}") - monkeypatch.setattr(appsrv, "resolve_opencode_version", lambda _path: "1.18.0") + monkeypatch.setattr(appsrv, "resolve_opencode_version", lambda _path: "1.19.0") monkeypatch.setenv("OMNIGENT_OPENCODE_SKIP_VERSION_CHECK", "1") server = OpenCodeNativeServer( bridge_dir=tmp_path, @@ -235,7 +237,7 @@ async def test_start_skips_version_gate_when_env_set( monkeypatch.setattr(appsrv.subprocess, "Popen", lambda argv, **kwargs: _FakeProc()) monkeypatch.setattr(OpenCodeNativeServer, "_wait_until_ready", fake_wait) await server.start() - assert server.version == "1.18.0" + assert server.version == "1.19.0" assert server.process is not None