fix: opencode config

This commit is contained in:
2026-07-21 00:37:11 +02:00
parent 45858bff06
commit 6b857e9adb
9 changed files with 49 additions and 10 deletions
+2
View File
@@ -9,6 +9,8 @@ AGENTCI_PLAN_MODEL=openai/gpt-5.6-sol
AGENTCI_PLAN_VARIANT=
AGENTCI_IMPLEMENT_MODEL=openai/gpt-5.6-sol
AGENTCI_IMPLEMENT_VARIANT=
AGENTCI_EXPLORE_MODEL=openai/gpt-5.6-luna
AGENTCI_EXPLORE_VARIANT=low
AGENTCI_RESEARCH_MODEL=openai/gpt-5.6-luna
AGENTCI_RESEARCH_VARIANT=high
# Optional; Context7 works without a key at lower rate limits.
+2
View File
@@ -65,6 +65,8 @@ RUN chmod 0755 \
&& OPENCODE_CONFIG=/etc/opencode/opencode.json \
OPENCODE_DISABLE_PROJECT_CONFIG=1 \
OPENCODE_PURE=1 \
AGENTCI_EXPLORE_MODEL=openai/gpt-5.6-luna \
AGENTCI_EXPLORE_VARIANT=low \
AGENTCI_RESEARCH_MODEL=openai/gpt-5.6-luna \
AGENTCI_RESEARCH_VARIANT=high \
CONTEXT7_API_KEY= \
+7 -5
View File
@@ -60,7 +60,9 @@ has a connected provider. The worker leaves jobs queued while the runtime is una
Models use OpenCode's `provider/model` format. Planning, implementation, and research can use
different providers. Optional `AGENTCI_PLAN_VARIANT` and `AGENTCI_IMPLEMENT_VARIANT` values are
passed directly to OpenCode for providers that support variants. `AGENTCI_RESEARCH_VARIANT`
configures the research subagent and defaults to `high`.
configures the research subagent and defaults to `high`. `AGENTCI_EXPLORE_MODEL` and
`AGENTCI_EXPLORE_VARIANT` configure OpenCode's explore agent and default to
`openai/gpt-5.6-luna` with `low`.
`AGENTCI_OPENCODE_VERSION` controls the npm version or range installed into the image and defaults
to `^1`. The build verifies that the resolved version is still OpenCode 1.x and prints it. Compose
@@ -75,10 +77,10 @@ are root-owned and read-only, and external skill discovery is disabled, so an ag
instructions for later repositories. CodeGraph, Context7, and `gh_grep` are configured as MCP
servers. Set `AGENTCI_CONTEXT7_API_KEY` to raise Context7 rate limits.
The `research` subagent has the same unrestricted permissions as every other agent. Its prompt
asks it to focus on external evidence, but this is guidance rather than an isolation boundary.
Agent CI initializes or refreshes CodeGraph before every turn and locally excludes `.codegraph/`
from Git.
The `research` subagent can only use Exa web search, Context7, and the `gh_grep` public-code search
MCP. All filesystem, shell, editing, task, and other tools are denied for that agent. Exa is enabled
with `OPENCODE_ENABLE_EXA=1`. Agent CI initializes or refreshes CodeGraph before every parent turn
and locally excludes `.codegraph/` from Git.
### Security boundary
+5
View File
@@ -23,6 +23,8 @@ services:
AGENTCI_PLAN_VARIANT: ${AGENTCI_PLAN_VARIANT:-}
AGENTCI_IMPLEMENT_MODEL: ${AGENTCI_IMPLEMENT_MODEL:-openai/gpt-5.6-sol}
AGENTCI_IMPLEMENT_VARIANT: ${AGENTCI_IMPLEMENT_VARIANT:-}
AGENTCI_EXPLORE_MODEL: ${AGENTCI_EXPLORE_MODEL:-openai/gpt-5.6-luna}
AGENTCI_EXPLORE_VARIANT: ${AGENTCI_EXPLORE_VARIANT:-low}
AGENTCI_RESEARCH_MODEL: ${AGENTCI_RESEARCH_MODEL:-openai/gpt-5.6-luna}
AGENTCI_RESEARCH_VARIANT: ${AGENTCI_RESEARCH_VARIANT:-high}
AGENTCI_PLAN_REVIEW_ROUNDS: ${AGENTCI_PLAN_REVIEW_ROUNDS:-4}
@@ -63,11 +65,14 @@ services:
OPENCODE_DISABLE_CLAUDE_CODE_SKILLS: "1"
OPENCODE_DISABLE_CLAUDE_CODE: "1"
OPENCODE_DISABLE_AUTOUPDATE: "1"
OPENCODE_ENABLE_EXA: "1"
OPENCODE_PURE: "1"
OPENCODE_SERVER_USERNAME: ${OPENCODE_SERVER_USERNAME:-opencode}
OPENCODE_SERVER_PASSWORD_FILE: /run/secrets/opencode_server_password
AGENTCI_GITEA_URL: ${AGENTCI_GITEA_URL:-http://gitea:3000}
AGENTCI_TEA_CONFIG_HOME: /run/agentci
AGENTCI_EXPLORE_MODEL: ${AGENTCI_EXPLORE_MODEL:-openai/gpt-5.6-luna}
AGENTCI_EXPLORE_VARIANT: ${AGENTCI_EXPLORE_VARIANT:-low}
AGENTCI_RESEARCH_MODEL: ${AGENTCI_RESEARCH_MODEL:-openai/gpt-5.6-luna}
AGENTCI_RESEARCH_VARIANT: ${AGENTCI_RESEARCH_VARIANT:-high}
CONTEXT7_API_KEY: ${AGENTCI_CONTEXT7_API_KEY:-}
+8 -1
View File
@@ -16,6 +16,8 @@
"permission": "allow"
},
"explore": {
"model": "{env:AGENTCI_EXPLORE_MODEL}",
"variant": "{env:AGENTCI_EXPLORE_VARIANT}",
"permission": "allow"
},
"research": {
@@ -23,7 +25,12 @@
"mode": "subagent",
"model": "{env:AGENTCI_RESEARCH_MODEL}",
"variant": "{env:AGENTCI_RESEARCH_VARIANT}",
"permission": "allow",
"permission": {
"*": "deny",
"websearch": "allow",
"context7_*": "allow",
"gh_grep_*": "allow"
},
"prompt": "Research external, current, or unfamiliar technical facts for the parent agent. Use Context7 for library documentation, gh_grep for public-code examples, and web search for primary sources or broader verification. Prefer authoritative sources, report links, distinguish facts from inference, and return a concise evidence-focused summary. Do not include secrets or proprietary source in external queries."
}
},
+3 -1
View File
@@ -36,6 +36,8 @@ class Settings(BaseSettings):
plan_variant: str | None = None
implement_model: str = "openai/gpt-5.6-sol"
implement_variant: str | None = None
explore_model: str = "openai/gpt-5.6-luna"
explore_variant: str = "low"
research_model: str = "openai/gpt-5.6-luna"
research_variant: str = "high"
plan_review_rounds: int = Field(default=4, ge=1, le=20)
@@ -53,7 +55,7 @@ class Settings(BaseSettings):
def strip_url(cls, value: str) -> str:
return value.rstrip("/")
@field_validator("plan_model", "implement_model", "research_model")
@field_validator("plan_model", "implement_model", "explore_model", "research_model")
@classmethod
def validate_opencode_model(cls, value: str) -> str:
provider, separator, model = value.partition("/")
+1
View File
@@ -59,6 +59,7 @@ async def build_container(settings: Settings) -> Container:
required_models=(
(settings.plan_model, settings.plan_variant),
(settings.implement_model, settings.implement_variant),
(settings.explore_model, settings.explore_variant),
(settings.research_model, settings.research_variant),
),
timeout_seconds=settings.turn_timeout_seconds,
+9 -1
View File
@@ -32,6 +32,12 @@ def test_defaults_research_variant_to_high() -> None:
assert settings.research_variant == "high"
def test_defaults_explore_agent_to_luna_low() -> None:
settings = Settings(_env_file=None) # type: ignore[call-arg]
assert settings.explore_model == "openai/gpt-5.6-luna"
assert settings.explore_variant == "low"
def test_reads_comma_delimited_install_scripts_from_environment(monkeypatch) -> None:
monkeypatch.setenv("AGENTCI_INSTALL_SCRIPTS", "python,dotnet")
@@ -40,7 +46,9 @@ def test_reads_comma_delimited_install_scripts_from_environment(monkeypatch) ->
assert settings.install_scripts == ["python", "dotnet"]
@pytest.mark.parametrize("field", ["plan_model", "implement_model", "research_model"])
@pytest.mark.parametrize(
"field", ["plan_model", "implement_model", "explore_model", "research_model"]
)
def test_requires_provider_qualified_opencode_models(field: str) -> None:
with pytest.raises(ValidationError, match="provider/model"):
Settings(_env_file=None, **{field: "model-only"}) # type: ignore[call-arg]
+12 -2
View File
@@ -2,17 +2,25 @@ import json
from pathlib import Path
def test_config_grants_all_agents_unrestricted_permissions() -> None:
def test_config_grants_parent_agents_access_and_restricts_research() -> None:
root = Path(__file__).parents[1]
config = json.loads((root / "opencode" / "opencode.json").read_text())
assert config["permission"] == "allow"
assert all(
config["agent"][name]["permission"] == "allow"
for name in ("build", "plan", "general", "explore", "research")
for name in ("build", "plan", "general", "explore")
)
assert config["agent"]["research"]["permission"] == {
"*": "deny",
"websearch": "allow",
"context7_*": "allow",
"gh_grep_*": "allow",
}
assert config["mcp"]["codegraph"]["command"] == ["codegraph", "serve", "--mcp"]
assert config["mcp"]["context7"]["url"] == "https://mcp.context7.com/mcp"
assert config["agent"]["explore"]["model"] == "{env:AGENTCI_EXPLORE_MODEL}"
assert config["agent"]["explore"]["variant"] == "{env:AGENTCI_EXPLORE_VARIANT}"
assert config["agent"]["research"]["variant"] == "{env:AGENTCI_RESEARCH_VARIANT}"
@@ -28,6 +36,8 @@ def test_compose_removes_codex_sandbox_exceptions() -> None:
assert "HOME: /etc/opencode/home" in compose
assert "OPENCODE_DISABLE_EXTERNAL_SKILLS" in compose
assert "OPENCODE_DISABLE_DEFAULT_PLUGINS" not in compose
assert 'OPENCODE_ENABLE_EXA: "1"' in compose
assert "AGENTCI_EXPLORE_VARIANT: ${AGENTCI_EXPLORE_VARIANT:-low}" in compose
assert "AGENTCI_RESEARCH_VARIANT: ${AGENTCI_RESEARCH_VARIANT:-high}" in compose
assert compose.count("/run/agentci:mode=1777") == 2
assert "AGENTCI_OPENCODE_VERSION: ${AGENTCI_OPENCODE_VERSION:-^1}" in compose