docs(add-opencode): rewrite skill for copy-from-providers-branch pattern

v2 no longer ships opencode on trunk. The skill now:
- Fetches origin/providers
- Copies opencode source files to their target paths
- Appends self-registration imports to both provider barrels
- Adds @opencode-ai/sdk@1.4.3 as a pinned agent-runner dep
- Adds OPENCODE_VERSION ARG + opencode-ai pnpm global install to Dockerfile
- Rebuilds host + container

All steps idempotent. Credential/env/Zen docs unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-04-17 14:14:10 +03:00
parent 03dd559786
commit 32a973f1cd
+97 -20
View File
@@ -5,16 +5,94 @@ description: Use OpenCode as an agent provider on NanoClaw v2 (AGENT_PROVIDER=op
# OpenCode agent provider (v2)
NanoClaw **v2** runs agents in a long-lived **poll loop** inside the container. The backend is selected with **`AGENT_PROVIDER`** (`claude` | `opencode` | `mock`), not the v1 `AGENT_RUNNER` env var.
NanoClaw **v2** runs agents in a long-lived **poll loop** inside the container. The backend is selected with **`AGENT_PROVIDER`** (`claude` | `opencode` | `mock`).
## What it does (upstream v2)
v2 trunk ships with only the `claude` provider baked in. This skill copies the OpenCode provider files in from the `providers` branch, wires them into the host and container barrels, installs dependencies, and rebuilds the image.
- **`container/agent-runner/src/providers/opencode.ts`** — `OpenCodeProvider` implementing `AgentProvider` (SSE via OpenCode server, session resume, MCP from merged `ProviderOptions.mcpServers` only — no `settings.json` MCP bridge).
- **`container/agent-runner/src/providers/mcp-to-opencode.ts`** — maps v2 `McpServerConfig` to OpenCode `mcp` entries.
- **`container/agent-runner/src/providers/factory.ts`** — registers `opencode`.
- **`container/agent-runner/package.json`** — dependency `@opencode-ai/sdk`.
- **`container/Dockerfile`** — global **`opencode-ai`** CLI for `opencode serve`.
- **`src/container-runner.ts`** — when effective provider is `opencode`: `XDG_DATA_HOME=/opencode-xdg`, session-scoped host mount, `NO_PROXY`/`no_proxy` merge for `127.0.0.1,localhost`, passes through `OPENCODE_PROVIDER`, `OPENCODE_MODEL`, `OPENCODE_SMALL_MODEL` from the host environment.
## Install
### Pre-flight
If all of the following are already present, skip to **Configuration**:
- `src/providers/opencode.ts`
- `container/agent-runner/src/providers/opencode.ts`
- `import './opencode.js';` line in `src/providers/index.ts`
- `import './opencode.js';` line in `container/agent-runner/src/providers/index.ts`
- `@opencode-ai/sdk` in `container/agent-runner/package.json`
- `opencode-ai@${OPENCODE_VERSION}` in the pnpm global-install block in `container/Dockerfile`
Missing pieces — continue below. All steps are idempotent; re-running is safe.
### 1. Fetch the providers branch
```bash
git fetch origin providers
```
### 2. Copy the OpenCode source files
Wholesale copies (owned entirely by this skill — user edits to these files won't survive a re-run, as designed):
```bash
git show origin/providers:src/providers/opencode.ts > src/providers/opencode.ts
git show origin/providers:container/agent-runner/src/providers/opencode.ts > container/agent-runner/src/providers/opencode.ts
git show origin/providers:container/agent-runner/src/providers/mcp-to-opencode.ts > container/agent-runner/src/providers/mcp-to-opencode.ts
git show origin/providers:container/agent-runner/src/providers/mcp-to-opencode.test.ts > container/agent-runner/src/providers/mcp-to-opencode.test.ts
git show origin/providers:container/agent-runner/src/providers/opencode.factory.test.ts > container/agent-runner/src/providers/opencode.factory.test.ts
```
### 3. Append the self-registration imports
Each barrel gets one line appended at the end — skip if the line is already present.
`src/providers/index.ts`:
```typescript
import './opencode.js';
```
`container/agent-runner/src/providers/index.ts`:
```typescript
import './opencode.js';
```
### 4. Add the agent-runner dependency
Pinned. Bump deliberately, not with `bun update`.
```bash
cd container/agent-runner && bun add @opencode-ai/sdk@1.4.3 && cd -
```
### 5. Add `opencode-ai` to the container Dockerfile
Two edits to `container/Dockerfile`, both idempotent (skip if already present):
**(a)** In the "Pin CLI versions" ARG block (around line 18), add after `ARG VERCEL_VERSION=latest`:
```dockerfile
ARG OPENCODE_VERSION=latest
```
**(b)** In the `pnpm install -g` block (around line 80), append `"opencode-ai@${OPENCODE_VERSION}"` to the list:
```dockerfile
pnpm install -g \
"@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" \
"agent-browser@${AGENT_BROWSER_VERSION}" \
"vercel@${VERCEL_VERSION}" \
"opencode-ai@${OPENCODE_VERSION}"
```
### 6. Build
```bash
pnpm run build # host
pnpm exec tsc -p container/agent-runner/tsconfig.json --noEmit # container typecheck
./container/build.sh # agent image
```
## Configuration
@@ -22,18 +100,16 @@ NanoClaw **v2** runs agents in a long-lived **poll loop** inside the container.
Set model/provider strings in the form OpenCode expects (often `provider/model-id`). **Put comments on their own lines** — a `#` inside a value is kept verbatim and breaks model IDs.
These variables are read **on the host** and passed into the container only when the effective provider is `opencode` (see `src/container-runner.ts`). They do not switch the provider by themselves; the DB still needs `agent_provider` set (below).
These variables are read **on the host** and passed into the container only when the effective provider is `opencode`. They do not switch the provider by themselves; the DB still needs `agent_provider` set (below).
- `OPENCODE_PROVIDER` — OpenCode provider id, e.g. `openrouter`, `anthropic` (if unset, the runner defaults to `anthropic`).
- `OPENCODE_MODEL` — full model id, e.g. `openrouter/anthropic/claude-sonnet-4`.
- `OPENCODE_SMALL_MODEL` — optional second model for small tasks.
- `OPENCODE_SMALL_MODEL` — optional second model for "small" tasks.
Credentials: OneCLI / credential proxy patterns are unchanged. For non-`anthropic` OpenCode providers, the runner registers a placeholder API key and **`ANTHROPIC_BASE_URL`** (the credential proxy) as `baseURL` so the real key never lives in the container.
#### Example: OpenRouter
Use ids that match OpenCodes registry / your custom registrations. Adjust names to what you actually run.
```env
# OpenCode — host passes these into the container when agent_provider is opencode
OPENCODE_PROVIDER=openrouter
@@ -59,9 +135,9 @@ OPENCODE_MODEL=openrouter/google/gemini-2.5-pro-preview
#### OpenCode Zen (`x-api-key`, not Bearer)
Zens HTTP API (e.g. `POST …/zen/v1/messages`) expects the key in the **`x-api-key`** header. If OneCLI injects **`Authorization: Bearer …`** only, Zen often returns **401 / Missing API key** even though the gateway is working.
Zen's HTTP API (e.g. `POST …/zen/v1/messages`) expects the key in the **`x-api-key`** header. If OneCLI injects **`Authorization: Bearer …`** only, Zen often returns **401 / "Missing API key"** even though the gateway is working.
**Naming:** NanoClaw **`AGENT_PROVIDER=opencode`** (v2 DB `agent_provider`) means run the **OpenCode agent provider**. Separately, **`OPENCODE_PROVIDER=opencode`** in `.env` is OpenCodes **Zen provider id** inside the OpenCode config (see [Zen docs](https://opencode.ai/docs/zen/)).
**Naming:** NanoClaw **`AGENT_PROVIDER=opencode`** (v2 DB `agent_provider`) means "run the **OpenCode agent provider**." Separately, **`OPENCODE_PROVIDER=opencode`** in `.env` is OpenCode's **Zen provider id** inside the OpenCode config (see [Zen docs](https://opencode.ai/docs/zen/)).
**Host `.env` (typical Zen shape):**
@@ -72,7 +148,7 @@ OPENCODE_PROVIDER=opencode
OPENCODE_MODEL=opencode/big-pickle
OPENCODE_SMALL_MODEL=opencode/big-pickle
# Point the credential proxy at Zens Anthropic-compatible base URL (host + OneCLI must forward this host).
# Point the credential proxy at Zen's Anthropic-compatible base URL (host + OneCLI must forward this host).
ANTHROPIC_BASE_URL=https://opencode.ai/zen/v1
```
@@ -97,18 +173,19 @@ Extra MCP servers still come from **`NANOCLAW_MCP_SERVERS`** / `container_config
## Operational notes
- OpenCode keeps a local **`opencode serve`** process and SSE subscription; the provider tears down with **`stream.return`** and **SIGKILL** on the server process on **`abort()`** / shared runtime reset to avoid MCP/zombie hangs.
- Session continuation is opaque (`ses_*` ids); stale sessions are cleared using **`isSessionInvalid`** on OpenCode-specific errors (timeouts, connection resets, not-found patterns) in addition to the poll-loops existing recovery.
- Session continuation is opaque (`ses_*` ids); stale sessions are cleared using **`isSessionInvalid`** on OpenCode-specific errors (timeouts, connection resets, not-found patterns) in addition to the poll-loop's existing recovery.
- **`NO_PROXY`** for localhost matters when the OpenCode client talks to `127.0.0.1` inside the container while HTTP(S)_PROXY is set (e.g. OneCLI).
## Verify
```bash
grep -q opencode container/agent-runner/src/providers/factory.ts && echo "OpenCode registered" || echo "Missing"
npm run build --prefix container/agent-runner
grep -q "./opencode.js" container/agent-runner/src/providers/index.ts && echo "container barrel: OK"
grep -q "./opencode.js" src/providers/index.ts && echo "host barrel: OK"
grep -q "@opencode-ai/sdk" container/agent-runner/package.json && echo "agent-runner dep: OK"
grep -q "opencode-ai@" container/Dockerfile && echo "Dockerfile install: OK"
cd container/agent-runner && bun test src/providers/ && cd -
```
Rebuild the agent image after Dockerfile changes: `./container/build.sh` (or your usual image build).
## Migrate from v1 wording
If documentation or habits still say **`AGENT_RUNNER=opencode`**, update to **`AGENT_PROVIDER=opencode`** and store **`agent_provider`** in v2 tables, not v1 runner columns.