feat(grok): capture one-shot Grok usage/cost from the opencode store

A GROK agent runs opencode, not Claude Code: it has no SDK /usage/status
server and writes no Claude transcript, so _resolve_final_token_usage found
nothing and every Grok agent finalized at 0 tokens / $0 — the opencode_usage
reader existed but had no caller.

- Mount a per-agent opencode data dir ($DATA/opencode/<agent_id> →
  /home/agent/.local/share/opencode) so opencode.db is captured, and mount the
  same host dir into the orchestrator (/data/opencode) in all three compose
  files so the finalizer can read it back — the opencode analogue of the
  mounted Claude transcript.
- _resolve_final_token_usage branches on provider_type: GROK reads opencode.db
  via opencode_usage (reasoning folded into output, billed at the output rate)
  and skips the SDK/transcript path. A 0-token read logs a WARNING so a silent
  mount failure isn't mistaken for a real zero-cost run.
- ROBOCO_OPENCODE_DATA_DIR overrides the in-orchestrator path for local runs.
This commit is contained in:
Renn F
2026-06-18 11:54:58 +02:00
parent 7e077fe642
commit e36549f01f
7 changed files with 190 additions and 4 deletions
+4
View File
@@ -76,6 +76,7 @@ class _FakeHost:
if config.mcp_config_path
else None,
"settings": str(agent_settings_path) if agent_settings_path else None,
"opencode": f"/host/opencode/{config.agent_id}",
}
def _build_mount_args(
@@ -200,6 +201,9 @@ async def test_grok_spawn_wires_gateway_and_image_last() -> None:
# Tool restriction lives in the rendered opencode.json (opencode `tools`),
# not a spawn env var — no ROBOCO_AGENT_TOOLS is injected.
assert not any(c.startswith("ROBOCO_AGENT_TOOLS=") for c in cmd)
# The opencode store is mounted so the orchestrator can read usage/cost
# back at finalize.
assert "/host/opencode/be-dev-1:/home/agent/.local/share/opencode" in cmd
# Identity wiring from the shared host helpers is present.
assert "ROBOCO_AGENT_TOKEN=hmac-be-dev-1" in cmd
# The image is the final docker-run argument.