fix(opencode-native): accept 1.18.x — bump version gate to <1.19.0 (#4725)
The upper bound was pinned at <1.18.0 (added in #1550 with 'refuse 1.18+ until validated'). OpenCode 1.18.x has since shipped 17 releases, making the gate reject every current upstream install. The 1.17.x-shaped assumptions in the forwarder are already forward-compatible: - part-based message events (message.updated / message.part.updated) are unchanged in 1.18.x - both permission.asked and permission.v2.asked are already handled Changes: - OPENCODE_MAX_VERSION_EXCLUSIVE: 1.18.0 -> 1.19.0 - npm install pin: opencode-ai@~1.17.7 -> opencode-ai@~1.18.0 - update tests and comments to match the new range Fixes #4670 Signed-off-by: Tomu Hirata <tomu.hirata@gmail.com>
This commit is contained in:
@@ -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,
|
||||
),
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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=""
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user