Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7d54546a3 | ||
|
|
38b36b2304 | ||
|
|
e040247097 | ||
|
|
c8efc17729 | ||
|
|
60f9e95ecc | ||
|
|
dd25c0ac2b | ||
|
|
8ae6abd9d0 | ||
|
|
b30db8a4c9 | ||
|
|
6955bc4f71 |
@@ -25,9 +25,10 @@ jobs:
|
||||
# Trusted author only, and only when a known agent is mentioned. This gate is the main
|
||||
# defense against malicious-issue prompt injection — do not loosen it.
|
||||
if: >
|
||||
(github.event.comment == null && github.event.issue.user.login == 'ffaerber') ||
|
||||
(github.event.comment == null && (github.event.issue.user.login == 'ffaerber' || github.event.issue.user.login == 'hermes')) ||
|
||||
(github.event.comment != null &&
|
||||
(github.event.comment.user.login == 'ffaerber' ||
|
||||
github.event.comment.user.login == 'hermes' ||
|
||||
github.event.comment.user.login == 'pm' ||
|
||||
github.event.comment.user.login == 'junior' ||
|
||||
github.event.comment.user.login == 'senior' ||
|
||||
@@ -149,6 +150,7 @@ jobs:
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
OLLAMA_URL: ${{ secrets.OLLAMA_URL }}
|
||||
OLLAMA_CLOUD_API_KEY: ${{ secrets.OLLAMA_CLOUD_API_KEY }}
|
||||
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
|
||||
NAME: ${{ steps.prep.outputs.name }}
|
||||
SKILLS: ${{ steps.prep.outputs.skills }} # JSON array of skills this agent may load
|
||||
run: bash "$SCRIPTS/install-opencode.sh"
|
||||
@@ -211,7 +213,7 @@ jobs:
|
||||
id: run
|
||||
env:
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
|
||||
# SELF_TOKEN = the RUNNING agent's OWN token (TOKEN_PM for @pm, TOKEN_OPS for @ops, …).
|
||||
# Only this agent's token is placed in its process env, so no agent can act as another.
|
||||
# Powers the gitea-api / gitea-admin skills — each agent calls Gitea as itself. Every
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"desc": "Senior dev — complex, multi-file implementation (GLM-5.2 via Ollama Cloud, text-only)."
|
||||
},
|
||||
"lead": {
|
||||
"model": "anthropic/claude-opus-4-8",
|
||||
"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": "anthropic/claude-opus-4-8",
|
||||
"model": "xai-oc/grok-4.5",
|
||||
"vision": false,
|
||||
"mode": "comment",
|
||||
"skills": [
|
||||
|
||||
@@ -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 (anthropic/…) 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}') ;;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
# Install opencode + provider config (+ Playwright MCP for browser agents).
|
||||
#
|
||||
# Required env (provided by the workflow step): OLLAMA_URL OLLAMA_CLOUD_API_KEY NAME SKILLS
|
||||
# GITHUB_PATH HOME
|
||||
# Required env (provided by the workflow step): OLLAMA_URL OLLAMA_CLOUD_API_KEY XAI_API_KEY
|
||||
# NAME SKILLS GITHUB_PATH HOME
|
||||
set -eu
|
||||
|
||||
# PIN the opencode version: an unpinned `latest` means a breaking release (CLI flags, or the
|
||||
@@ -38,19 +38,30 @@ esac
|
||||
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).
|
||||
# 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. Built-in providers (e.g. `anthropic/claude-opus-4-8` for @lead) are not derived here.
|
||||
# Three OpenAI-compatible providers: local self-hosted ollama (ornith) + Ollama Cloud
|
||||
# (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-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")
|
||||
jq -n --argjson mcp "$MCP" --argjson perm "$PERM" --argjson cloud "$CLOUD_MODELS" --argjson local "$LOCAL_MODELS" --arg url "$OLLAMA_URL" --arg ckey "$OLLAMA_CLOUD_API_KEY" '{
|
||||
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}
|
||||
"ollama-cloud": {npm:"@ai-sdk/openai-compatible", options:{baseURL:"https://ollama.com/v1", apiKey:$ckey}, models:$cloud},
|
||||
"xai-oc": {npm:"@ai-sdk/openai-compatible", options:{baseURL:"https://api.x.ai/v1", apiKey:$xkey}, models:$xai}
|
||||
},
|
||||
permission: $perm,
|
||||
mcp: $mcp
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# plain-text reply (/tmp/agent_out.md) plus the raw event stream (/tmp/events.jsonl).
|
||||
#
|
||||
# Required env (provided by the workflow step):
|
||||
# ANTHROPIC_API_KEY SELF_TOKEN NAME MODEL VISION MODE HAS_IMAGES BRANCH AUTOPILOT NUM TITLE
|
||||
# XAI_API_KEY SELF_TOKEN NAME MODEL VISION MODE HAS_IMAGES BRANCH AUTOPILOT NUM TITLE
|
||||
# IBODY CMT
|
||||
# FILES (the opencode -f image flags, from the imgs step output)
|
||||
# AUTOPILOT is 'true' when the issue carries the `autopilot` label (label-gated autopilot mode).
|
||||
|
||||
@@ -107,7 +107,7 @@ curl -sS -X POST -H "Authorization: token $SELF_TOKEN" "$API/orgs/{org}/labels"
|
||||
3. Commit the standard caller so it gets the agents — `PUT /repos/{owner}/{repo}/contents/.gitea/workflows/ai-agent.yml`
|
||||
with base64 `content`, `message`, `branch:"main"` (copy the exact caller from the `agents` repo README).
|
||||
4. Add the agent bot users as collaborators: `PUT /repos/{owner}/{repo}/collaborators/{username}` (`{"permission":"write"}`).
|
||||
5. Ensure the repo can run agents — the org must hold the runtime secrets (ANTHROPIC_API_KEY, SELF_TOKEN,
|
||||
5. Ensure the repo can run agents — the org must hold the runtime secrets (XAI_API_KEY, SELF_TOKEN,
|
||||
TOKEN_* , OLLAMA_URL, OLLAMA_CLOUD_API_KEY); set any missing via the secrets calls above.
|
||||
|
||||
## Packages / container registry
|
||||
|
||||
@@ -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` | `anthropic/claude-opus-4-8` | 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` | `anthropic/claude-opus-4-8` | 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 |
|
||||
|--------|-----|
|
||||
| `ANTHROPIC_API_KEY` | `@lead` (and `@pm`/`@senior`/`@qa` if on Claude) |
|
||||
| `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`. |
|
||||
|
||||
Reference in New Issue
Block a user