From c8efc177290d0c4f8ab5e83ea71f979cb557a890 Mon Sep 17 00:00:00 2001 From: Felix Faerber Date: Thu, 30 Jul 2026 14:08:21 +0300 Subject: [PATCH] fix(agents): rename xai provider key to xai-oc to avoid opencode catalog collision (#118) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit opencode ships a built-in `xai` model-catalog entry (a real @ai-sdk/xai integration with Responses-API support). Naming our custom OpenAI-compatible shim `xai` too made opencode's --auto permission-check path assume the catalog's provider object once a `permission` block is present (always, in this pipeline) and call .responses(), which our shim doesn't implement — crashing every @lead/@ops run with "Z.responses is not a function". Reproduced locally with the production config shape on opencode 1.17.13; renaming the provider key to xai-oc (model id xai-oc/grok-4.5) fixes it reliably across repeated fresh-state runs. --- .gitea/workflows/scripts/agents.json | 4 ++-- .gitea/workflows/scripts/build-activity-log.sh | 2 +- .gitea/workflows/scripts/install-opencode.sh | 16 ++++++++++++---- README.md | 6 +++--- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/.gitea/workflows/scripts/agents.json b/.gitea/workflows/scripts/agents.json index 2c4f747..3d56dba 100644 --- a/.gitea/workflows/scripts/agents.json +++ b/.gitea/workflows/scripts/agents.json @@ -25,7 +25,7 @@ "desc": "Senior dev — complex, multi-file implementation (GLM-5.2 via Ollama Cloud, text-only)." }, "lead": { - "model": "xai/grok-4.5", + "model": "xai-oc/grok-4.5", "vision": true, "mode": "pr", "skills": [ @@ -43,7 +43,7 @@ "desc": "QA / reviewer — reviews PRs: reads the diff, drives a headless browser (Playwright) to verify behavior, posts specific recommendations on the PR and the pass/fail verdict on the issue. Never edits code, never merges." }, "ops": { - "model": "xai/grok-4.5", + "model": "xai-oc/grok-4.5", "vision": false, "mode": "comment", "skills": [ diff --git a/.gitea/workflows/scripts/build-activity-log.sh b/.gitea/workflows/scripts/build-activity-log.sh index 4c92200..4ad0138 100755 --- a/.gitea/workflows/scripts/build-activity-log.sh +++ b/.gitea/workflows/scripts/build-activity-log.sh @@ -38,7 +38,7 @@ COST=${COST:-0}; INP=${INP:-0}; OUT=${OUT:-0}; CR=${CR:-0}; CW=${CW:-0}; RE=${RE IN_TOTAL=$(( INP + CR + CW )) # total input context processed # Cost label: ollama / ollama-cloud models are SUBSCRIPTION-billed (GPU-time against the plan, no # $/token price exists), so a "$0.0000" there would be misleading — label it a subscription instead. -# Metered providers (xai/…) get the real dollar cost opencode computed. +# Metered providers (xai-oc/…) get the real dollar cost opencode computed. case "${MODEL:-}" in ollama*|*"/ollama"*) COSTF="subscription" ;; *) COSTF=$(awk -v c="$COST" 'BEGIN{printf "$%.4f", c+0}') ;; diff --git a/.gitea/workflows/scripts/install-opencode.sh b/.gitea/workflows/scripts/install-opencode.sh index a236034..b6d705f 100755 --- a/.gitea/workflows/scripts/install-opencode.sh +++ b/.gitea/workflows/scripts/install-opencode.sh @@ -42,18 +42,26 @@ PERM=$(jq -nc --argjson s "$SKILLS" ' # (gemma4/kimi-k2.7-code/glm-5.2/minimax-m3) + xAI (grok-4.5). The provider `models:` maps are # DERIVED from agents.json (the single source of truth, shared with route.sh) so every model an # agent is routed to is always declared in the provider config. `ollama-cloud/` prefix models go to -# the cloud provider; `ollama/` prefix models go to the local provider; `xai/` prefix models go to -# the xAI provider (OpenAI-compatible, https://api.x.ai/v1). No other built-in providers remain. +# the cloud provider; `ollama/` prefix models go to the local provider; `xai-oc/` prefix models go +# to our xAI shim (OpenAI-compatible, https://api.x.ai/v1). No other built-in providers remain. +# +# The provider key is `xai-oc`, NOT `xai` — opencode ships a built-in `xai` entry in its own model +# catalog (a real @ai-sdk/xai integration that implements the Responses API). Naming our custom +# openai-compatible shim `xai` collides with that catalog entry: opencode's --auto permission-check +# path then assumes the catalog's provider (which has `.responses()`) and crashes with +# "Z.responses is not a function" on every run once a `permission` block is present (i.e. always in +# this pipeline). Confirmed by reproducing locally with the production config shape on opencode +# 1.17.13 — renaming the key to `xai-oc` avoids the collision entirely. See issue #118. # See issue #31. AGENTS_JSON="${SCRIPTS:-$(dirname -- "$0")}/agents.json" CLOUD_MODELS=$(jq -r '[.[] | .model | select(startswith("ollama-cloud/")) | sub("^ollama-cloud/";"")] | map({(.):{}}) | add // {}' "$AGENTS_JSON") LOCAL_MODELS=$(jq -r '[.[] | .model | select(startswith("ollama/")) | sub("^ollama/";"")] | map({(.):{}}) | add // {"ornith:35b":{}}' "$AGENTS_JSON") -XAI_MODELS=$(jq -r '[.[] | .model | select(startswith("xai/")) | sub("^xai/";"")] | map({(.):{}}) | add // {}' "$AGENTS_JSON") +XAI_MODELS=$(jq -r '[.[] | .model | select(startswith("xai-oc/")) | sub("^xai-oc/";"")] | map({(.):{}}) | add // {}' "$AGENTS_JSON") jq -n --argjson mcp "$MCP" --argjson perm "$PERM" --argjson cloud "$CLOUD_MODELS" --argjson local "$LOCAL_MODELS" --argjson xai "$XAI_MODELS" --arg url "$OLLAMA_URL" --arg ckey "$OLLAMA_CLOUD_API_KEY" --arg xkey "$XAI_API_KEY" '{ provider: { ollama: {npm:"@ai-sdk/openai-compatible", options:{baseURL:($url+"/v1")}, models:$local}, "ollama-cloud": {npm:"@ai-sdk/openai-compatible", options:{baseURL:"https://ollama.com/v1", apiKey:$ckey}, models:$cloud}, - xai: {npm:"@ai-sdk/openai-compatible", options:{baseURL:"https://api.x.ai/v1", apiKey:$xkey}, models:$xai} + "xai-oc": {npm:"@ai-sdk/openai-compatible", options:{baseURL:"https://api.x.ai/v1", apiKey:$xkey}, models:$xai} }, permission: $perm, mcp: $mcp diff --git a/README.md b/README.md index 4d7f9de..7d73bbe 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,9 @@ Shared **AI dev-team** workflow for Gitea Actions, reusable across repos. It giv | `@pm` | `ollama-cloud/minimax-m3:cloud` | yes | comment | `gitea-api` | Product manager & orchestrator — plans, picks the dev, hands finished PRs to `@qa`, reports back to the issue creator (autopilot: merges approved PRs itself). Issue thread only; never edits files, never reads the PR diff. | | `@junior` | `ollama-cloud/kimi-k2.7-code:cloud` | no | pr | — | Junior dev — small, low-risk changes (mostly YAML/compose/config). Text-only, cannot read images. Defers complex or image tasks to `@senior` or `@lead`. | | `@senior` | `ollama-cloud/glm-5.2:cloud` | no | pr | `gitea-api` | Senior dev — complex, multi-file implementation (GLM-5.2 via Ollama Cloud, text-only). | -| `@lead` | `xai/grok-4.5` | yes | pr | `gitea-api` | Tech lead — the hardest problems, architecture, and final calls. | +| `@lead` | `xai-oc/grok-4.5` | yes | pr | `gitea-api` | Tech lead — the hardest problems, architecture, and final calls. | | `@qa` | `ollama-cloud/minimax-m3:cloud` | yes | comment | `gitea-api` | QA / reviewer — reads the PR diff, drives a headless browser (Playwright) to verify behavior; recommendations on the PR, pass/fail verdict on the issue. Never edits code, never merges. | -| `@ops` | `xai/grok-4.5` | no | comment | `gitea-admin` | Gitea operator — administers the instance itself (create orgs/users/repos, labels, secrets, scoped per-user tokens, bootstrap repos). Comments only; never edits code. Confirms before destructive actions. | +| `@ops` | `xai-oc/grok-4.5` | no | comment | `gitea-admin` | Gitea operator — administers the instance itself (create orgs/users/repos, labels, secrets, scoped per-user tokens, bootstrap repos). Comments only; never edits code. Confirms before destructive actions. | | `@intern` | `ollama/ornith:35b` | no | pr | — | Intern — very basic tasks only, routed to the local Ollama model (`ornith:35b`). Text-only, cannot read images. Escalates anything non-trivial to `@junior`, `@senior` or `@lead`. | The registry `.gitea/workflows/scripts/agents.json` is the source of truth for this mapping — if you @@ -111,7 +111,7 @@ points `$SCRIPTS` at it. Keep the workflow and its scripts moving together on `m | Secret | For | |--------|-----| -| `XAI_API_KEY` | `@lead`, `@ops` (and any other agent switched to a `xai/…` model) | +| `XAI_API_KEY` | `@lead`, `@ops` (and any other agent switched to a `xai-oc/…` model) | | `OLLAMA_URL`, `OLLAMA_CLOUD_API_KEY` | local ornith / Ollama Cloud (gemma4, kimi-k2.7-code, glm-5.2, minimax-m3) | | `TOKEN_PM`,`TOKEN_SENIOR`,`TOKEN_JUNIOR`,`TOKEN_LEAD`,`TOKEN_QA` | **primary** — each agent's own Gitea-user PAT. The running agent gets *only its own* token (as `SELF_TOKEN`) so it posts, commits and comments as itself, and its `gitea-api` skill acts with its own scopes. Scopes: devs + `TOKEN_PM` carry `write:repository` (`@pm` is the only agent that merges, autopilot only); `TOKEN_QA` is `read:repository` + `write:issue` (reviews, never merges). | | `TOKEN_OPS` | `@ops` only — the admin PAT behind the `gitea-admin` skill (create orgs/users/repos, manage labels & secrets, mint scoped tokens). Injected into the agent process only when the agent is `@ops`. | -- 2.54.0