From cafd36eae180589ff9ccedbdfc32380844f5befc Mon Sep 17 00:00:00 2001 From: senior Date: Sat, 4 Jul 2026 07:20:25 +0000 Subject: [PATCH] fix(agent): single source of truth for model IDs (route.sh + install-opencode.sh) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the agent registry to a shared agents.json and have both route.sh (agent → model) and install-opencode.sh (ollama-cloud provider models map) derive from it, so the two lists can no longer drift. route.sh now copies agents.json to /tmp/agents.json instead of an inline heredoc. install-opencode.sh builds the ollama-cloud map by collecting every registry model with the prefix and stripping it — non-ollama-cloud models (e.g. @lead's anthropic/claude-opus, the local ornith:35b) are excluded by construction. Resolves #31. --- .gitea/workflows/scripts/agents.json | 7 +++++++ .gitea/workflows/scripts/install-opencode.sh | 10 ++++++++-- .gitea/workflows/scripts/route.sh | 15 ++++++--------- 3 files changed, 21 insertions(+), 11 deletions(-) create mode 100644 .gitea/workflows/scripts/agents.json diff --git a/.gitea/workflows/scripts/agents.json b/.gitea/workflows/scripts/agents.json new file mode 100644 index 0000000..549c15a --- /dev/null +++ b/.gitea/workflows/scripts/agents.json @@ -0,0 +1,7 @@ +{ + "pm": {"model":"ollama-cloud/gemma4:cloud","vision":true, "mode":"comment","skills":["gitea-api"],"desc":"Product manager — research, plan, ask clarifying questions, and decide which dev should do the work. Comments only; never edits files."}, + "junior": {"model":"ollama-cloud/kimi-k2.7-code:cloud","vision":false,"mode":"pr", "skills":[],"desc":"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": {"model":"ollama-cloud/glm-5.2:cloud","vision":false,"mode":"pr", "skills":["gitea-api","node1-ssh"],"desc":"Senior dev — complex, multi-file implementation (GLM-5.2 via Ollama Cloud, text-only)."}, + "lead": {"model":"anthropic/claude-opus-4-8","vision":true, "mode":"pr", "skills":["gitea-api","node1-ssh"],"desc":"Tech lead — the hardest problems, architecture, and final calls."}, + "qa": {"model":"ollama-cloud/minimax-m3:cloud","vision":true, "mode":"comment","skills":["gitea-api"],"desc":"QA — verifies things work. Drives a headless browser (Playwright) to open a URL/web app, click through it, screenshot, and report bugs or confirm behavior. Comments findings; opens no PRs."} +} \ No newline at end of file diff --git a/.gitea/workflows/scripts/install-opencode.sh b/.gitea/workflows/scripts/install-opencode.sh index a026875..c1e91e7 100755 --- a/.gitea/workflows/scripts/install-opencode.sh +++ b/.gitea/workflows/scripts/install-opencode.sh @@ -28,10 +28,16 @@ SKILLS="${SKILLS:-[]}" PERM=$(jq -nc --argjson s "$SKILLS" ' {skill: ( {"*":"deny"} + (reduce $s[] as $k ({}; . + {($k):"allow"})) )}') # Two ollama providers: local self-hosted (ornith) + Ollama Cloud (gemma4/kimi-k2.7-code/glm-5.2/minimax-m3). -jq -n --argjson mcp "$MCP" --argjson perm "$PERM" --arg url "$OLLAMA_URL" --arg ckey "$OLLAMA_CLOUD_API_KEY" '{ +# The ollama-cloud `models:` map is 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. Only the +# `ollama-cloud/` provider prefix models participate — e.g. `anthropic/claude-opus-4-8` (@lead) is a +# built-in provider and `ornith:35b` is local-only, neither belongs here. 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") +jq -n --argjson mcp "$MCP" --argjson perm "$PERM" --argjson cloud "$CLOUD_MODELS" --arg url "$OLLAMA_URL" --arg ckey "$OLLAMA_CLOUD_API_KEY" '{ provider: { ollama: {npm:"@ai-sdk/openai-compatible", options:{baseURL:($url+"/v1")}, models:{"ornith:35b":{}}}, - "ollama-cloud": {npm:"@ai-sdk/openai-compatible", options:{baseURL:"https://ollama.com/v1", apiKey:$ckey}, models:{"glm-5.2:cloud":{},"gemma4:cloud":{},"kimi-k2.7-code:cloud":{},"minimax-m3:cloud":{}}} + "ollama-cloud": {npm:"@ai-sdk/openai-compatible", options:{baseURL:"https://ollama.com/v1", apiKey:$ckey}, models:$cloud} }, permission: $perm, mcp: $mcp diff --git a/.gitea/workflows/scripts/route.sh b/.gitea/workflows/scripts/route.sh index 7237d46..727af61 100755 --- a/.gitea/workflows/scripts/route.sh +++ b/.gitea/workflows/scripts/route.sh @@ -10,21 +10,18 @@ set -eu # --- agent registry: model + capabilities + mode + role + skills --- +# The registry is the SINGLE SOURCE OF TRUTH, kept in agents.json next to this +# script. install-opencode.sh derives its ollama-cloud provider `models:` map +# from the same file, so an agent's model can never be missing from the provider +# config — drift is impossible by construction. See issue #31. # `skills` is the allow-list of opencode Skills each agent may load. It scopes the # `permission.skill` block written into opencode.json (see install-opencode.sh) so an agent only # ever sees (and can load) the skills relevant to its role. Skills NOT listed here are hidden from # that agent entirely — not even the one-line summary appears in its , so the # full API/how-to detail never reaches an agent that shouldn't act on it. A teammate can still learn # *that* another agent has a capability from the roster and ask them to use it. -cat > /tmp/agents.json <<'JSON' -{ - "pm": {"model":"ollama-cloud/gemma4:cloud","vision":true, "mode":"comment","skills":["gitea-api"],"desc":"Product manager — research, plan, ask clarifying questions, and decide which dev should do the work. Comments only; never edits files."}, - "junior": {"model":"ollama-cloud/kimi-k2.7-code:cloud","vision":false,"mode":"pr", "skills":[],"desc":"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": {"model":"ollama-cloud/glm-5.2:cloud","vision":false,"mode":"pr", "skills":["gitea-api","node1-ssh"],"desc":"Senior dev — complex, multi-file implementation (GLM-5.2 via Ollama Cloud, text-only)."}, - "lead": {"model":"anthropic/claude-opus-4-8","vision":true, "mode":"pr", "skills":["gitea-api","node1-ssh"],"desc":"Tech lead — the hardest problems, architecture, and final calls."}, - "qa": {"model":"ollama-cloud/minimax-m3:cloud","vision":true, "mode":"comment","skills":["gitea-api"],"desc":"QA — verifies things work. Drives a headless browser (Playwright) to open a URL/web app, click through it, screenshot, and report bugs or confirm behavior. Comments findings; opens no PRs."} -} -JSON +AGENTS_JSON="${SCRIPTS:-$(dirname -- "$0")}/agents.json" +cp "$AGENTS_JSON" /tmp/agents.json # On a new issue, @pm auto-assesses. On a comment, route by the @mention. # A comment event has a comment id (CID); an issue-opened event does not. (event_name is unreliable # here — see agent.yml: this reusable workflow sees it as 'workflow_call'.)