From 1049ff3155179568e5ac51b66c70baba47584baa Mon Sep 17 00:00:00 2001 From: hermes Date: Fri, 16 Oct 2026 07:40:04 +0000 Subject: [PATCH] test(agents): run opencode 2 as well as opencode 1 opencode 2 (@opencode/cli) moved MCP servers to `mcp.servers`, turns Code Mode on by default, replaced `permission` with ordered `permissions` rules, and dropped `run --pure` for `--standalone` (runs otherwise go through a shared background service). Pick the config and flag from `opencode --version`. Its `execute` tool stays allowed: with it denied, opencode 2.0.30 never offers the idx tools to the model in `opencode run`, though the server connects before the first step. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh --- docs/SMOKE.md | 14 ++++++++++---- tests/agents/agent-smoke.sh | 34 +++++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/docs/SMOKE.md b/docs/SMOKE.md index 4035634..5601eb5 100644 --- a/docs/SMOKE.md +++ b/docs/SMOKE.md @@ -106,9 +106,14 @@ each run keeps a copy next to its output as `..mcp-stderr`. output but still deletes every copied agent config. - Tools are auto-approved for the run (Claude `dontAsk` with `mcp__idx__*`, Codex `default_tools_approval_mode="approve"`); Claude and pi - also get no built-in tools, Hermes only the `idx` toolset, and opencode a - permission that denies everything but `idx_*`, so they can only answer - through MCP. + also get no built-in tools, Hermes only the `idx` toolset, and opencode + permissions that deny its shell, file, and web tools, so they can only + answer through MCP. +- opencode 1 (`opencode-ai`) and 2 (`@opencode/cli`) are both supported; the + harness reads `opencode --version` and writes the matching config. Version + 2 runs with `--standalone`, off the shared background service. A wrapper + that pins `XDG_*` paths (such as a Nix `opencode2` wrapper) defeats the + isolated config dirs; put the plain binary first on PATH. - `IDX_AGENT_MODEL_` picks a model (for example `IDX_AGENT_MODEL_CLAUDE=sonnet`). `IDX_AGENT_EXEC_PREFIX` prefixes every agent command, for hosts that inject API keys through a wrapper. @@ -118,4 +123,5 @@ each run keeps a copy next to its output as `..mcp-stderr`. measure pass rates. `--keep` keeps raw agent output and `results.jsonl`. - `nix develop .#agents` adds Claude Code, Codex, and opencode from nixpkgs after your PATH, so installed versions win. pi and Hermes Agent must be - installed on the host. + installed on the host. The nixpkgs opencode (1.2.17) lacks `run --pure`; + use a current opencode 1 or 2. diff --git a/tests/agents/agent-smoke.sh b/tests/agents/agent-smoke.sh index 55472f6..b8f4728 100755 --- a/tests/agents/agent-smoke.sh +++ b/tests/agents/agent-smoke.sh @@ -273,19 +273,39 @@ run_hermes() { } setup_opencode() { + local version mkdir -p "$work/opencode/cwd" - jq -n --argjson s "$server_json" \ - '{"$schema": "https://opencode.ai/config.json", - mcp: {idx: {type: "local", command: ([$s.command] + $s.args), environment: $s.env, enabled: true}}, - permission: {"*": "deny", "idx_*": "allow"}}' \ - > "$work/opencode/cwd/opencode.json" + version="$("${exec_prefix[@]}" opencode --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)" + if [[ "${version%%.*}" -ge 2 ]]; then + # OpenCode 2 (@opencode/cli). A denied call ends the run, and models + # use Code Mode's `execute` while the server connects, so deny only + # the tools that could route around it. --standalone keeps the run + # off the shared background service. + jq -n --argjson s "$server_json" \ + '{mcp: {servers: {idx: {type: "local", command: ([$s.command] + $s.args), + environment: $s.env, codemode: false}}}, + permissions: ((["read", "edit", "glob", "grep", "shell", "subagent", "skill", + "question", "webfetch", "websearch", "external_directory"] + | map({action: ., resource: "*", effect: "deny"})) + + [{action: "idx_*", resource: "*", effect: "allow"}])}' \ + > "$work/opencode/cwd/opencode.json" + echo --standalone > "$work/opencode/run-flag" + else + jq -n --argjson s "$server_json" \ + '{"$schema": "https://opencode.ai/config.json", + mcp: {idx: {type: "local", command: ([$s.command] + $s.args), environment: $s.env, enabled: true}}, + permission: {"*": "deny", "idx_*": "allow"}}' \ + > "$work/opencode/cwd/opencode.json" + echo --pure > "$work/opencode/run-flag" + fi } run_opencode() { - local prompt="$1" out="$2" model=() home="$work/opencode" + local prompt="$1" out="$2" model=() home="$work/opencode" flag [[ -n "${IDX_AGENT_MODEL_OPENCODE:-}" ]] && model=(-m "$IDX_AGENT_MODEL_OPENCODE") + flag="$(< "$home/run-flag")" (cd "$home/cwd" && XDG_CONFIG_HOME="$home/config" XDG_DATA_HOME="$home/data" \ XDG_CACHE_HOME="$home/cache" XDG_STATE_HOME="$home/state" \ - "${exec_prefix[@]}" opencode run --pure --format json "${model[@]}" "$prompt" < /dev/null > "$out.raw") + "${exec_prefix[@]}" opencode run "$flag" --format json "${model[@]}" "$prompt" < /dev/null > "$out.raw") jq -c 'select(.type=="tool_use") | .part | select(.tool | startswith("idx_")) | select(.state.status=="completed") | {name: (.tool | ltrimstr("idx_")), args: .state.input}' "$out.raw" > "$out.calls" jq -r 'select(.type=="text") | .part.text' "$out.raw" > "$out.answer"