一键导入
opencode-acp
Manage OpenCode ACP sessions from Hermes — spawn, delegate, attach, agent management
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Manage OpenCode ACP sessions from Hermes — spawn, delegate, attach, agent management
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Reconcile split-brain truth sources in a multi-organ federation — when arifOS capability_map, Hermes config.yaml providers block, MCP server registry, and runtime env all claim to be the canonical source for "what's configured". Load when Arif reports "Hermes says 1 provider, arifOS says 28", when a tool call fails with unknown provider, or when fixing audit findings about API key / model / organ coverage.
Add, fix, or migrate custom/third-party LLM providers in a Hermes Agent install so they appear in `/model` (CLI) and the Telegram gateway picker. Covers providers block shape, env-var key mapping, security-blocker on direct config writes, three-tier multimodal fallback chains, MiMo UltraSpeed/Pro/v2.5 capability matrix and supports_vision flag for forced auxiliary routing, vault→hermes.env key sync pattern, picker zen with 5-state failure classification (OK/PAUSED/BROKEN/CHAT_INCOMPATIBLE/EXC) and dedupe by route priority, MoA preset cross-dependency on provider models, MiniMax direct provider + mmx-cli for multimodal generation, OpenCode Go vs Zen tier verification, and dead fallback cleanup.
MiniMax multimodal via mmx-cli — TTS, video, music, image, vision, search
Test whether a proposed improvement actually beats the existing baseline BEFORE committing to build it. Spawn parallel forge agents that each compare challenger vs baseline on the same data. Kill ideas that don't improve; forge only what measurably helps.
Map of the arifOS federation — organs, ports, roles, MCP surfaces, health probes. Load when Arif asks 'what organs are alive', 'show me the federation', 'is X running', 'what port is Y', or when you need to route a task to the right organ.
Probe live state BEFORE proposing fixes, additions, or changes. Read configs, test commands, check what exists — then act only on real gaps. Use ALWAYS. This is not optional. Arif corrected multiple times (2026-07-04, 2026-07-08, 2026-07-11) for proposing fixes to things that already worked, for citing stale audits as live state, and for accepting 'first time' / 'novel' / 'pick from N options' prompts without probing disk.
| name | opencode-acp |
| description | Manage OpenCode ACP sessions from Hermes — spawn, delegate, attach, agent management |
| version | 1.1.0 |
| tags | ["opencode","acp","delegation","coding","agent"] |
| metadata | {"hermes":{"requires":["opencode"]}} |
Manage OpenCode coding sessions from Hermes via ACP (Agent Client Protocol).
npm install -g opencode)/root/.config/opencode/opencode.jsonopencode-go/deepseek-v4-flash-free (no API key needed)Spawn OpenCode as an ACP subprocess for isolated coding tasks:
delegate_task(
goal="Build a REST API endpoint for /api/health",
context="Working directory: /root/project. Model: opencode-go/deepseek-v4-flash-free.",
acp_command="opencode",
acp_args=["acp"],
toolsets=["coding", "terminal", "file"]
)
| Command | Purpose |
|---|---|
opencode acp | Full ACP server (stdio mode) |
opencode acp --pure | ACP without external plugins |
opencode acp --port <N> | HTTP ACP server on port N |
OpenCode reads from /root/.config/opencode/opencode.json:
model: opencode-go/deepseek-v4-flash-free (primary — no API key needed)agent.forge.model: MUST match global model — this override wins over global configsmall_model: opencode-go/big-pickleAlways update ALL agent model overrides AND the global model together.
Override per-session via environment:
delegate_task(
goal="...",
acp_command="opencode",
acp_args=["acp"],
# Model is controlled by opencode.json, not by delegate_task
)
For long-running coding sessions:
# Start OpenCode in background
terminal(command="opencode acp --port 9090", background=true, notify_on_complete=false)
# Or via tmux for interactive
terminal(command="tmux new-session -d -s code1 'opencode'", timeout=10)
terminal(command="sleep5 && tmux send-keys -t code1 'Build auth middleware' Enter", timeout=15)
# Query mode
opencode -q "Fix the import error in src/main.py"
# With specific model
opencode -q "Refactor this function" --model tokenplan-mimo/mimo-v2.5-pro
# List agents
opencode agent list
# Create agent
opencode agent create --name "forge-worker" --model opencode-go/deepseek-v4-flash-free
# Agent types: build (primary), compaction, planner, text-to-image
| Mode | Command | Use Case |
|---|---|---|
| ACP (stdio) | opencode acp | delegate_task integration |
| ACP (HTTP) | opencode acp --port N | Remote/multi-client |
| Headless | opencode serve | Background server |
| Web | opencode web | Browser UI |
| Attach | opencode attach <url> | Connect to running server |
OpenCode's MCP config at /root/.config/opencode/opencode.json section mcp:
Hermes and OpenCode share the same MCP servers — no duplication needed.
references/opencode-session-monitoring.md — query OpenCode SQLite DB for live session monitoring (active sessions, messages, tool calls, thinking text)Working model (2026-07-10): opencode-go/deepseek-v4-flash-free
timeout 20 opencode run --model opencode-go/deepseek-v4-flash-free "hi" → successThe model is controlled in 3+ places — ALL must match:
| File | Key path | Value to set |
|---|---|---|
~/.config/opencode/opencode.json | model | opencode-go/deepseek-v4-flash-free |
~/.config/opencode/opencode.json | agent.{forge,auditor,ops,planner}.model | opencode-go/deepseek-v4-flash-free |
~/.config/opencode/opencode.json | small_model | opencode-go/big-pickle |
~/.local/state/opencode/model.json | recent[0] | {"providerID":"opencode-go","modelID":"deepseek-v4-flash-free"} |
~/.local/state/opencode/model.json | favorite | ["opencode-go/deepseek-v4-flash-free"] |
~/.openclaw/openclaw.json | agents.defaults.model.primary | opencode-go/deepseek-v4-flash-free |
Why mimo-v2.5-pro kept returning despite config changes (root cause found 2026-07-10):
The global model field in opencode.json was updated, but agent.forge.model was NOT — it stayed as tokenplan-mimo/mimo-v2.5-pro. Agent-level overrides win over global config. OpenCode CLI output > forge · mimo-v2.5-pro because the forge agent reads its own model field first.
Verification — must show deepseek-v4-flash-free:
timeout 20 opencode run "model name" 2>&1 | head -3
# Correct: > forge · deepseek-v4-flash-free
# Wrong: > forge · mimo-v2.5-pro (agent.forge.model stale override still active)
After any config change — restart serve:
pkill -f "opencode serve"; sleep 2
opencode serve --hostname 127.0.0.1 --port 4096 &
sleep 5 && timeout 20 opencode run "model name" 2>&1 | head -3
What does NOT work (2026-07-10):
deepseek/deepseek-v4-flash → "Unauthorized: Authentication Fails (governor)"deepseek/deepseek-v4-pro → "Unauthorized: Authentication Fails (governor)"opencode-go/kimi-k2.7-code → "Missing API key"opencode-go/deepseek-v4-pro → "Missing API key"OpenCode's deepseek provider config often has an incorrect baseURL that causes auth failures:
# WRONG in opencode.json (causes "Unauthorized: Authentication Fails (governor)"):
baseURL: https://api.deepseek.com/v1
# CORRECT — DeepSeek's OpenAI-compatible API does NOT use /v1 suffix:
baseURL: https://api.deepseek.com
Fix command:
python3 -c "
import json
d = json.load(open('/root/.config/opencode/opencode.json'))
d['provider']['deepseek']['options']['baseURL'] = 'https://api.deepseek.com'
json.dump(d, open('/root/.config/opencode/opencode.json', 'w'), indent=2)
"
Note: Even after fixing baseURL, the deepseek provider may STILL fail auth via OpenCode's @ai-sdk/openai-compatible npm adapter. Direct curl to https://api.deepseek.com/chat/completions with the same key works fine. This is an OpenCode adapter compatibility issue, not a key/balance problem. DeepSeek balance confirmed good ($47.41 as of 2026-07-19).
Fallback: When the deepseek provider fails in OpenCode despite correct config and valid key, do NOT keep debugging. The opencode-go/deepseek-v4-flash-free provider works (though slow on complex tasks). If that also fails or times out, execute the task directly through terminal/MCP tools instead.
model only = incomplete. agent.forge.model override is separate and wins. Check BOTH when debugging model issues.opencode-go/deepseek-v4-flash-free is the only free model that works immediately. Everything else either needs a missing API key or hits the governor.~/.local/state/opencode/ caches recent models. If the model still shows wrong after config fix, update the recent array and restart serve.When ALL OpenCode providers error or time out, stop debugging. Execute the task directly:
# You have full access to:
# 1. Terminal (build, test, git, deploy)
# 2. MCP tools (arifOS, A-FORGE, GEOX, WEALTH, WELL)
# 3. execute_code (Python with hermes_tools)
# 4. Direct API calls (curl to DeepSeek etc.)
# Pattern:
terminal(command="cd /root/arifOS && ...") # build/test
git commit + git push # commit
systemctl restart <service> # deploy
When to fall back:
Do NOT:
OpenCode stores session metadata in JSON, not SQLite. SQLite DB has NO sessions table.
import json
from datetime import datetime
with open("/root/.openclaw/agents/opencode/sessions/sessions.json") as f:
data = json.load(f)
# Keys: 'agent:opencode:main', 'agent:opencode:main:heartbeat', 'agent:opencode:direct:{chat_id}'
# Fields: sessionId, updatedAt, lastInteractionAt, abortedLastRun, sessionFile
for k, v in data.items():
print(f"{k}: {datetime.fromtimestamp(v['updatedAt']/1000)} | aborted={v.get('abortedLastRun')}")
Process check — what's actually running NOW:
ps aux | grep -E 'opencode|openclaw' | grep -v grep | grep -v earlyoom
Expected live: openclaw gateway, opencode serve :4096, bot.py, opencode (pts/N interactive).
KEY: abortedLastRun: True = last run was interrupted/rate-limited (historical). Does NOT mean session is stuck. Check ps aux CPU time on pts/N for actual liveness.
Historical JSONL sessions: /root/.openclaw/agents/opencode/sessions/{sessionId}.jsonl
Bot path: /root/.openclaw/workspace/bots/opencode-bot/bot.py
Hardcoded at line ~79: OPENCODE_MODEL = "minimax/MiniMax-M3" (no key configured = fails silently)
Update this constant + restart whenever switching models.
| Role | Agent | Model | Provider |
|---|---|---|---|
| OpenCode (primary) | build | qwen3.7-max | qwen-token |
| FORGE worker | build | GLM-5.2 | qwen-token |
| AUDITOR | build | DeepSeek V4 Pro | qwen-token |
| OPS | build | MiniMax M2.7 | minimax |
| PLAN | planner | Kimi K2.7 Code | qwen-token |
NEVER flag these as failures when delegating to OpenCode or any external agent. When spawning agents, always state the correct thresholds explicitly — agents default to flat ≥0.80:
| Floor | Value | Meaning | Correct threshold |
|---|---|---|---|
| F7 HUMILITY | 0.04 | Within Ω₀ target range | Target: 0.03–0.05 |
| F9 ANTI-HANTU | 0.0 | Zero hallucination — perfect | Target: <0.30, 0.0 = optimal |
When delegating F1/F12 diagnostic work to OpenCode: "F7=0.04 and F9=0.0 are correct-by-design. Ignore them."
opencode.json), NOT Hermes config.yamlopencode agent list is massive, pipe through jq or grepabortedLastRun: True = historical interrupt, NOT current stuck state — always check process listWhen delegating to subagents (via OpenCode or any delegate_task call), the agent's summary of what it built is NOT trustworthy on its own. The agent may:
Real incident (E1 arif_verify, 2026-07-10):
mode="verify" (ledger chain verification) instead of arif_verify(token, payload_hash) (SEAL token vs command hash verification)The pattern that prevents this:
1. Subagent returns → read the actual files it modified
2. Ask: "Does the code DO what the spec requires?"
- Not: "Does the summary say it worked?"
- Not: "Did the build pass?" (builds can pass with wrong logic)
3. If wrong → fix it locally, don't re-delegate
4. Commit with a note about what was corrected
Verification checklist for delegated code:
Why re-delegation fails: A subagent that built the wrong thing has demonstrated it doesn't understand the requirement correctly. Re-delegating gives it the same context and gets the same wrong answer. Fix it yourself.
skill_manage(action='create') creates the SKILL.md in Hermes' skill directory (/root/HERMES/skills/). It does NOT auto-symlink to:
~/.claude/skills/ (Claude Code)~/.openclaw/skills/ (OpenClaw)After creating a skill, manually symlink to both dirs:
# For skills in subdirectories (e.g. autonomous-ai-agents/opencode-acp)
ln -sf /root/HERMES/skills/autonomous-ai-agents/<name>/SKILL.md ~/.claude/skills/<name>.md
ln -sf /root/HERMES/skills/autonomous-ai-agents/<name>/SKILL.md ~/.openclaw/skills/<name>.md
# For top-level skills (e.g. minimax-cli)
ln -sf /root/HERMES/skills/<name>/SKILL.md ~/.claude/skills/<name>.md
ln -sf /root/HERMES/skills/<name>/SKILL.md ~/.openclaw/skills/<name>.md
Path note: /root/.hermes/skills/ is a symlink to /root/HERMES/skills/ (same inode). Always use the canonical /root/HERMES/skills/ path for symlinks to avoid nested symlink confusion.
Caught by: OpenCode forge agent audited disk after Hermes claimed "4 skills created" — found2/4 missing from OpenClaw. Signal was right (missing symlinks), diagnosis was wrong ("doesn't exist" vs "not symlinked").
skill_manage(action='create') writes to /root/HERMES/skills/ (canonical, uppercase). But other agents read from their own skill directories:
~/.openclaw/skills/~/.claude/skills/~/.hermes/skills/ (symlink to /root/HERMES/skills/)After creating a skill, manually verify and symlink:
# Verify canonical location
ls -la /root/HERMES/skills/<category>/<name>/SKILL.md
# Symlink to OpenClaw
ln -sf /root/HERMES/skills/<category>/<name>/SKILL.md ~/.openclaw/skills/<name>.md
# Symlink to Claude
ln -sf /root/HERMES/skills/<category>/<name>/SKILL.md ~/.claude/skills/<name>.md
# Verify all symlinks resolve
readlink -f ~/.openclaw/skills/<name>.md
readlink -f ~/.claude/skills/<name>.md
Lesson from 2026-07-06: Hermes sealed a receipt claiming4 skills were created. OpenCode's forge agent audited the disk and found2 symlinks missing in OpenClaw. The skills existed on disk (in subdirectories) but weren't symlinked. "Not at path X" ≠ "not on disk" — always check canonical source before declaring absence.
Path confusion: /root/.hermes/skills is a SYMLINK to /root/HERMES/skills (same inode). Always use the canonical uppercase path for symlinks. The lowercase path works for reads but may confuse agents that check readlink.
When OpenCode (or any external agent) proposes a migration plan, verify against REALITY before executing. OpenCode may suggest changes based on its own config context, not yours.
Arif's directive: "dont simply follow opencode. zen it based on reality."
Pattern:
ps aux, curl :port/health, cat config)Example: OpenCode proposed adding minimax-media/minimax-code MCP servers. Reality: systemd services were running but dead (404 on /health). The fix was stopping systemd + killing zombies + replacing with stdio — NOT following OpenCode's proposed config format.
Sessions live in JSON, not SQLite. The old SQLite approach is deprecated.
Sessions tracked in: /root/.openclaw/agents/opencode/sessions/sessions.json
Live process signals:
opencode serve :4096 — API server (background, always on)
openclaw gateway — Telegram bridge (background, always on)
opencode — interactive CLI (pts/N, human present)
opencode-bot bot.py — Telegram bot handler
Critical: ps aux | grep opencode tells you what's live vs stale. CPU time on pts/N = active session. An abortedLastRun: True flag means interrupted/rate-limited — NOT stuck.
See references/opencode-session-monitoring.md for the correct JSON-based session reading pattern.
When OpenCode is already running interactively on pts/N (started by the user, not via terminal(background=true)), you CANNOT use process() tools. Monitor via file system instead:
# 1. Find the process
ps aux | grep opencode | grep -v grep | grep -v earlyoom
# PID 224645 65% CPU pts/2 opencode ← active
# 2. Working directory
ls -la /proc/<PID>/cwd
# 3. What it's building (recent file changes)
find /root/A-FORGE/src -name "*.ts" -newer /root/A-FORGE/package.json -mmin -15
cd /root/A-FORGE && git status --short # ?? = new, M = modified
# 4. Read the artifacts, run tests, triage failures
npx tsx test/new_tool.test.ts
Status report pattern: Component → Status → Quality table. Don't just say "it's working" — show test results, note TODOs, flag architectural concerns.
When OpenCode is running and modifying files at the same time you're patching them:
OpenCode modifies files while you're reading/patching. A patch() call may fail with "Found N matches" or "old_string not found" because the file changed since your last read.
Pattern: Always re-read before retrying:
read_file → patch() → "Found N matches" → re-read_file → patch()
Real incident: Patching forge_visual_qa.test.ts while OpenCode was actively editing it. First patch succeeded; second patch failed because OpenCode had rewritten the same section between reads.
When delegated code has test failures, distinguish:
| Type | Example | Fix |
|---|---|---|
| Test bug | Assertion checks .includes("rejected") but failure_reason says "zero confidence" (different condition triggered first) | Fix the test assertion |
| Implementation bug | evaluateTriWitness caps confidence internally for geometric mean but returns uncapped values in the output struct | Fix the code |
How to tell: Read BOTH the test AND the implementation. Don't just fix the test to pass — ask "is the test revealing a real bug?"
Real incident (2026-07-16, forge_visual_qa):
failure_reason?.includes("rejected"). Cause: W3 had confidence=0, so anyZero triggered before anyRejected — failure_reason was about zero, not rejection. Test bug: wrong assertion for the scenario.w1_vision.confidence <= 0.90. Cause: evaluateTriWitness capped internally but returned original uncapped witness objects. Implementation bug: F7 HUMILITY floor not enforced in output. Fixed by spreading with capped values: { ...w1, confidence: w1c }.When OpenCode has already finished and you need to read what happened — use the JSONL session files on disk. See references/opencode-historical-sessions.md for the full retrieval pattern (session location, JSONL parsing, trajectory status checks, pitfalls).
Quick path:
# Find latest completed session
ls -lt /root/.openclaw/agents/opencode/sessions/*.jsonl | grep -v trajectory | head -3
references/opencode-session-monitoring.md — CORRECTED: JSON-based session monitoring (sessions.json + .jsonl files; SQLite approach deprecated, DB has no sessions table)references/opencode-historical-sessions.md — JSONL file format, session retrieval, trajectory status (COMPLETED sessions)references/a2a-federation-audit.md — A2A conformance audit recipe, warga verification, registry vs public distinctionreferences/active-session-monitoring.md — managing OpenCode sessions NOT spawned through Hermes (ps/proc monitoring, concurrent modification, test triage)| Role | Agent | Model | Provider |
|---|---|---|---|
| OpenCode (primary) | build | deepseek-v4-flash-free | opencode-go (free, no key) |
| FORGE worker | build | deepseek-v4-flash-free | opencode-go |
| AUDITOR | build | deepseek-v4-flash-free | opencode-go |
| OPS | build | deepseek-v4-flash-free | opencode-go |
| PLAN | planner | deepseek-v4-flash-free | opencode-go |
Mimo exhausted 2026-07-10. Primary switched to
opencode-go/deepseek-v4-flash-free. All agent overrides (agent.forge.model, etc.) MUST also be updated to match.
Symptom: telegram.error.Conflict: terminated by other getUpdates request
Root cause: Old bot instance still connected to Telegram polling while a new one starts. Telegram only accepts one polling connection per bot token.
Check:
lsof -i :443 2>/dev/null | grep python3 | grep telegram | wc -l
# 2 connections = conflict. Should be 1 per bot.
Fix — never kill the systemd instance directly:
systemctl stop opencode-bot — stop systemdpkill -9 -f "opencode-bot/bot.py"systemctl start opencode-bot — let systemd restart cleanNever start bot.py manually — systemd must own the single instance. Manual starts create conflicts.
Note: When bot restarts, Telegram holds the old session for ~60s. New instance shows Conflict errors during this window. Self-clears automatically.