fix(grok): make the opencode runtime actually load — proven live on grok-build-0.1

Live verification (opencode 1.17.8 + grok-build-0.1, funded key) showed the Grok
runtime was loading INERT, three ways:

1. The provider override `provider.xai.npm=@ai-sdk/openai` failed model
   resolution (ProviderModelNotFoundError) — opencode can't resolve that package
   from its module path. Worse, ANY custom `provider.xai` block (even just
   options) breaks plugin-tool registration. opencode's BUILT-IN xai provider
   drives grok-build-0.1 with working tool-calls, so emit NO provider block; the
   key + base reach it via XAI_API_KEY / XAI_BASE_URL env (provider.options.apiKey
   alone does NOT authenticate).
2. Plugins referenced by absolute path in the config `plugin:` array never
   registered their hooks/tools. opencode 1.17.8 only registers from the plugin
   AUTO-DISCOVERY dir (~/.config/opencode/plugin/). Bake all plugins there.
3. Plugins must use a NAMED export, not `export default`.

Changes:
- opencode_config: no `provider` block, no `plugin` array; drop the dead
  XaiTarget + timeout machinery; build_opencode_config now takes a model string.
- GrokProvider / orchestrator interactive env: inject XAI_API_KEY + XAI_BASE_URL
  (drop the now-unused OPENAI_*).
- secret-scrub / budget-feed / secretary-tools / intake-tools: named exports;
  baked into /home/agent/.config/opencode/plugin/ (drop the EXTRA_PLUGINS env).
- agent-grok* Dockerfiles: plugin dir + agent ownership; drop the unneeded
  @ai-sdk/openai global install.

Verified live end-to-end: grok-build-0.1 calls read_company_state AND
submit_directive through secretary-tools.js and the backend receives both with
the agent token; a tool.execute.before guard fires; built-in tool-calls work.
Targeted gate green (ruff/mypy/xenon + opencode_config/providers/interactive
tests; node --check the plugins).
This commit is contained in:
Renn F
2026-06-18 21:14:06 +02:00
parent f314723c98
commit e29168653a
13 changed files with 150 additions and 251 deletions
@@ -1,7 +1,7 @@
"""Interactive intake/secretary builders fork a GROK route onto opencode.
A GROK route swaps the Claude SDK-driver image for the opencode-serve image and
the ANTHROPIC_* env for OPENAI_* + the opencode store mount; every other
the ANTHROPIC_* env for XAI_* + the opencode store mount; every other
provider keeps the Claude path's ANTHROPIC_* behaviour.
"""
@@ -48,7 +48,7 @@ def _intake_spec(
)
def test_intake_grok_uses_openai_env_and_opencode_mount() -> None:
def test_intake_grok_uses_xai_env_and_opencode_mount() -> None:
cmd = AgentOrchestrator._build_intake_run_cmd(
_intake_spec(
"grok",
@@ -57,8 +57,8 @@ def test_intake_grok_uses_openai_env_and_opencode_mount() -> None:
grok_variant="minimal",
)
)
assert "OPENAI_BASE_URL=https://api.x.ai/v1" in cmd
assert "OPENAI_API_KEY=xai-key" in cmd
assert "XAI_API_KEY=xai-key" in cmd
assert "XAI_BASE_URL=https://api.x.ai/v1" in cmd
assert "ROBOCO_AGENT_MODEL=grok-build-0.1" in cmd
assert "ROBOCO_SYSTEM_PROMPT=/app/system-prompt.md" in cmd
assert "/h/oc/intake-1:/home/agent/.local/share/opencode" in cmd
@@ -97,7 +97,7 @@ def test_intake_anthropic_keeps_anthropic_env() -> None:
)
assert "ANTHROPIC_BASE_URL=https://api.anthropic.com" in cmd
assert "ANTHROPIC_AUTH_TOKEN=sk-ant" in cmd
assert not any(c.startswith("OPENAI_") for c in cmd)
assert not any(c.startswith("XAI_") for c in cmd)
assert cmd[-1] == "roboco-agent-prompter"
@@ -118,7 +118,7 @@ def test_secretary_grok_uses_openai_env_and_grok_image() -> None:
model="grok-build-0.1",
)
cmd = AgentOrchestrator._build_secretary_run_cmd(spec)
assert "OPENAI_API_KEY=xai-key" in cmd
assert "XAI_API_KEY=xai-key" in cmd
assert "/h/oc/sec-1:/home/agent/.local/share/opencode" in cmd
# The HMAC identity the directive tools authenticate with survives.
assert "ROBOCO_AGENT_TOKEN=hmac-secretary" in cmd