fix: opencode config
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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= \
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:-}
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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("/")
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user