"""ReinOpenWeights — glas-harness adapter around the rein-openweights CLI. Same shape as reins/rein_aharness.py: rein-openweights's `run` command performs an entire bounded agentic session (credential acquisition, OpenRouter tool-calling loop, commit verification) as one opaque unit — no per-tool-call hooks exposed yet. dispatch_tool collapses the whole run into a single call. """ from __future__ import annotations import shutil import subprocess from typing import Any from glas_harness.contract import Rein, SandboxHandle, ToolCall, ToolResult from glas_harness.reins._shared import git_head, write_task_file class ReinOpenWeightsNotInstalled(RuntimeError): pass class ReinOpenWeights(Rein): def __init__(self, cli_bin: str = "rein-openweights", model: str | None = None) -> None: self.cli_bin = cli_bin self.model = model def _bin(self) -> str: resolved = shutil.which(self.cli_bin) if not resolved: raise ReinOpenWeightsNotInstalled( f"'{self.cli_bin}' not found on PATH — install rein-openweights first" ) return resolved def start_session( self, profile: dict[str, Any], inputs: dict[str, str], sandbox: SandboxHandle ) -> dict[str, str]: target_repo = ( inputs.get("target_repo") or sandbox.reachability.get("workspace_dir") or sandbox.reachability.get("remote_dir") ) if not target_repo: raise ValueError("no target_repo resolvable from inputs or sandbox reachability") task_file = inputs.get("task_file") or write_task_file( inputs["title"], inputs["description"], target_repo ) return { "sandbox_id": sandbox.sandbox_id, "task_file": task_file, "target_repo": target_repo, "head_before": git_head(target_repo), } def dispatch_tool(self, session: dict[str, str], tool_call: ToolCall) -> ToolResult: argv = [self._bin(), "run", "--task-file", session["task_file"], "--no-hub"] if self.model: argv += ["--model", self.model] proc = subprocess.run(argv, capture_output=True, text=True) ok = proc.returncode == 0 return ToolResult(ok=ok, output=proc.stdout, error=None if ok else proc.stderr) def end_session(self, session: dict[str, str]) -> dict[str, str]: head_after = git_head(session["target_repo"]) committed = bool(head_after) and head_after != session.get("head_before") return { "commit_sha": head_after or "", "committed": str(committed), }