Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions sdk/python/agentfield/harness/_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ def _resolve_options(
"codex_bin",
"gemini_bin",
"opencode_bin",
"opencode_server",
"schema_max_retries",
]:
val = getattr(config, field_name, None)
Expand Down Expand Up @@ -146,17 +145,7 @@ async def run(
resolved_cwd = str(options.get("cwd", "."))
provider_instance = self._build_provider(str(resolved_provider), options)

# When project_dir is set (opencode provider), place the output file
# inside project_dir so the coding agent's Write tool can reach it.
# Use a unique subdir to avoid collisions from parallel calls.
project_dir = options.get("project_dir")
output_dir = resolved_cwd
_temp_output_dir: Optional[str] = None
if isinstance(project_dir, str) and project_dir:
import tempfile as _tempfile

_temp_output_dir = _tempfile.mkdtemp(prefix=".secaf-out-", dir=project_dir)
output_dir = _temp_output_dir

effective_prompt = prompt
if schema is not None:
Expand Down Expand Up @@ -195,10 +184,6 @@ async def run(
finally:
if schema is not None:
cleanup_temp_files(output_dir)
if _temp_output_dir:
import shutil as _shutil

_shutil.rmtree(_temp_output_dir, ignore_errors=True)

def _build_provider(
self, provider_name: str, options: Dict[str, Any]
Expand Down
1 change: 0 additions & 1 deletion sdk/python/agentfield/harness/providers/_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,5 @@ def build_provider(config: "HarnessConfig") -> "HarnessProvider":

return OpenCodeProvider(
bin_path=getattr(config, "opencode_bin", "opencode"),
server_url=getattr(config, "opencode_server", None),
)
raise NotImplementedError(f"Provider {provider_name!r} is not yet implemented.")
7 changes: 1 addition & 6 deletions sdk/python/agentfield/harness/providers/opencode.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,8 @@ class OpenCodeProvider:
_MAX_CONCURRENT: ClassVar[int] = int(os.environ.get("OPENCODE_MAX_CONCURRENT", "3"))
_concurrency_sem: ClassVar[Optional[asyncio.Semaphore]] = None

def __init__(
self,
bin_path: str = "opencode",
server_url: Optional[str] = None,
):
def __init__(self, bin_path: str = "opencode"):
self._bin = bin_path
self._explicit_server = server_url or os.environ.get("OPENCODE_SERVER")

@classmethod
def _get_semaphore(cls) -> asyncio.Semaphore:
Expand Down
9 changes: 0 additions & 9 deletions sdk/python/agentfield/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,15 +326,6 @@ class HarnessConfig(BaseModel):
opencode_bin: str = Field(
default="opencode", description="Path to opencode binary."
)
opencode_server: Optional[str] = Field(
default=None,
description=(
"URL of a running ``opencode serve`` instance "
'(e.g. "http://127.0.0.1:4096"). When set, the opencode provider '
"uses ``--attach`` mode which avoids the standalone session bug. "
"Falls back to OPENCODE_SERVER env var."
),
)


class AIConfig(BaseModel):
Expand Down
37 changes: 29 additions & 8 deletions sdk/python/tests/test_harness_provider_opencode.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ async def fake_run_cli(cmd, *, env=None, cwd=None, timeout=None):

provider = OpenCodeProvider(
bin_path="/usr/local/bin/opencode",
server_url="http://127.0.0.1:9999",
)
raw = await provider.execute(
"hello",
Expand Down Expand Up @@ -65,7 +64,6 @@ async def fake_run_cli(*_args, **_kwargs):

provider = OpenCodeProvider(
bin_path="opencode-missing",
server_url="http://127.0.0.1:9999",
)
raw = await provider.execute("hello", {})

Expand All @@ -84,7 +82,7 @@ async def fake_run_cli(*_args, **_kwargs):

monkeypatch.setattr("agentfield.harness.providers.opencode.run_cli", fake_run_cli)

provider = OpenCodeProvider(server_url="http://127.0.0.1:9999")
provider = OpenCodeProvider()
raw = await provider.execute("hello", {})

assert raw.is_error is True
Expand All @@ -97,13 +95,11 @@ def test_factory_builds_opencode_provider_with_config_bin() -> None:
HarnessConfig(
provider="opencode",
opencode_bin="/opt/opencode",
opencode_server="http://127.0.0.1:4096",
)
)

assert isinstance(provider, OpenCodeProvider)
assert provider._bin == "/opt/opencode"
assert provider._explicit_server == "http://127.0.0.1:4096"


@pytest.mark.asyncio
Expand All @@ -117,7 +113,7 @@ async def fake_run_cli(cmd, *, env=None, cwd=None, timeout=None):

monkeypatch.setattr("agentfield.harness.providers.opencode.run_cli", fake_run_cli)

provider = OpenCodeProvider(server_url="http://127.0.0.1:9999")
provider = OpenCodeProvider()
raw = await provider.execute("hello", {"model": "openai/gpt-5"})

assert captured["cmd"] == [
Expand All @@ -143,7 +139,7 @@ async def fake_run_cli(cmd, *, env=None, cwd=None, timeout=None):
with patch(
"agentfield.harness.providers.opencode.estimate_cli_cost", return_value=0.0035
):
provider = OpenCodeProvider(server_url="http://127.0.0.1:9999")
provider = OpenCodeProvider()
raw = await provider.execute("hello", {"model": "openai/gpt-4o"})

assert raw.metrics.total_cost_usd == 0.0035
Expand All @@ -160,8 +156,33 @@ async def fake_run_cli(cmd, *, env=None, cwd=None, timeout=None):

monkeypatch.setattr("agentfield.harness.providers.opencode.run_cli", fake_run_cli)

provider = OpenCodeProvider(server_url="http://127.0.0.1:9999")
provider = OpenCodeProvider()
raw = await provider.execute("hello", {})

# No model → estimate_cli_cost gets empty string → returns None
assert raw.metrics.total_cost_usd is None


@pytest.mark.asyncio
async def test_opencode_command_does_not_use_attach_pattern(
monkeypatch: pytest.MonkeyPatch,
):
"""Verify the provider uses direct CLI pattern, NOT serve+attach workaround."""
captured_cmd = None

async def capture_cmd(cmd, *, env=None, cwd=None, timeout=None):
nonlocal captured_cmd
captured_cmd = cmd
return "result", "", 0

monkeypatch.setattr("agentfield.harness.providers.opencode.run_cli", capture_cmd)

provider = OpenCodeProvider(bin_path="opencode")
await provider.execute("test prompt", {"model": "gpt-4"})

cmd_str = " ".join(captured_cmd)
assert "--attach" not in cmd_str
assert "http://" not in cmd_str
assert "127.0.0.1" not in cmd_str
assert "localhost" not in cmd_str
assert "opencode run" in cmd_str
Loading