Commits merged: - docs(FN-3717): update openclaw runtime and settings docs - test(FN-3717): fix openclaw engine test typing - test(FN-3717): strengthen openclaw bridge coverage - feat(FN-3717): complete Step 2 — wire engine and bundle delivery - fix(FN-3717): address openclaw bridge review feedback - feat(FN-3717): complete Step 1 — add OpenClaw MCP bridge Files changed: .changeset/fn-3717-openclaw-tool-bridge.md | 5 ++ docs/settings-reference.md | 28 +++++--- packages/cli/src/__tests__/bundle-output.test.ts | 9 +++ packages/cli/tsup.config.ts | 7 ++ .../src/__tests__/openclaw-runtime-e2e.test.ts | 28 ++++++++ .../__tests__/openclaw-runtime-integration.test.ts | 10 +++ plugins/fusion-plugin-openclaw-runtime/README.md | 13 ++++ .../src/__tests__/cli-spawn.test.ts | 82 +++++++++++++++++++--- .../src/__tests__/mcp-config.test.ts | 45 ++++++++++++ .../src/__tests__/runtime-adapter.test.ts | 46 ++++++++++++ .../fusion-plugin-openclaw-runtime/src/index.ts | 5 ++ .../src/mcp-config.ts | 60 ++++++++++++++++ .../src/mcp-schema-server.cjs | 59 ++++++++++++++++ .../src/pi-module.ts | 52 ++++++++++++-- .../src/runtime-adapter.ts | 26 +++++++ .../fusion-plugin-openclaw-runtime/src/types.ts | 4 ++ 16 files changed, 457 insertions(+), 22 deletions(-) Fusion-Task-Id: FN-3717
113 lines
5.0 KiB
Markdown
113 lines
5.0 KiB
Markdown
# OpenClaw Runtime Plugin
|
||
|
||
Drives the local **`openclaw` CLI** ([openclaw/openclaw](https://github.com/openclaw/openclaw)) as a subprocess. By default it runs `openclaw agent --local` (embedded mode, no daemon required); you can opt into the WebSocket gateway with `useGateway: true`.
|
||
|
||
## What it does
|
||
|
||
For each `promptWithFallback(session, prompt)`:
|
||
|
||
1. Spawns `openclaw --no-color agent --local --json --session-id <uuid> --message <prompt>` (plus `--agent`, `--model`, `--thinking`, `--timeout` if configured).
|
||
2. Reads the single JSON document on stdout (matching `OpenClawAgentJson`):
|
||
- Concatenates `payloads[]` where `!isError && !isReasoning` → `session.callbacks.onText(...)`.
|
||
- Joins `payloads[].isReasoning === true` → `session.callbacks.onThinking(...)`.
|
||
- Surfaces tool-level errors (`payloads[].isError === true`) as a logger warning.
|
||
- Stores `meta.agentMeta.usage` on the session for token accounting.
|
||
3. Reuses the same UUID across every prompt for the session so OpenClaw resumes the same agent conversation server-side.
|
||
|
||
The previous HTTP `/v1/chat/completions` integration has been removed — that endpoint required a separate gateway daemon and was an OpenAI-compat shim. The CLI surface is the canonical OpenClaw API.
|
||
|
||
## Fusion tool-control (MCP bridge)
|
||
|
||
When a Fusion OpenClaw session includes custom tools, the runtime plugin now enables tool-control through OpenClaw's supported MCP configuration flow:
|
||
|
||
1. Collect session tools and filter out built-ins: `read`, `write`, `edit`, `bash`, `grep`, `find`.
|
||
2. Convert remaining tools into MCP-compatible schemas.
|
||
3. Write a temporary schema file and MCP server config (`node mcp-schema-server.cjs <schema.json>`).
|
||
4. Configure a profile-scoped MCP server using `openclaw --profile <id> mcp set fusion-custom-tools <json>`.
|
||
5. Spawn the agent turn with `openclaw --profile <id> agent ...` so OpenClaw can see the configured MCP server.
|
||
|
||
No private protocol is used — this is the verified OpenClaw CLI contract (`mcp set` + `--profile`).
|
||
|
||
## Prerequisites
|
||
|
||
```bash
|
||
npm install -g openclaw
|
||
```
|
||
|
||
Verify with `openclaw --version` (expect `OpenClaw 2026.x.y`).
|
||
|
||
If you want gateway mode (`useGateway: true`), also start `openclaw gateway run` separately.
|
||
|
||
> **First-run note:** the very first `openclaw agent` invocation lazy-installs runtime deps and can take 30–60s. Subsequent calls are fast.
|
||
|
||
## Settings
|
||
|
||
| Key | Env var | Default | Notes |
|
||
|---|---|---|---|
|
||
| `binaryPath` | `OPENCLAW_BIN` | `openclaw` | Path to the `openclaw` binary. |
|
||
| `agentId` | `OPENCLAW_AGENT_ID` | `main` | Maps to `--agent <id>`. List with `openclaw agents list`. |
|
||
| `model` | `OPENCLAW_MODEL` | (OpenClaw default) | Maps to `--model <provider/model>`, e.g. `anthropic/claude-haiku-4-5`. |
|
||
| `thinking` | `OPENCLAW_THINKING` | `off` | One of `off | minimal | low | medium | high | xhigh | adaptive | max`. |
|
||
| `cliTimeoutSec` | `OPENCLAW_TIMEOUT_SEC` | `0` | OpenClaw-side timeout (0 = no limit). |
|
||
| `cliTimeoutMs` | `OPENCLAW_CLI_TIMEOUT_MS` | `300000` | Hard kill on the Fusion side. |
|
||
| `useGateway` | `OPENCLAW_USE_GATEWAY` | `false` | When `true`, omit `--local`; the CLI tries the WS gateway and falls back to embedded after ~2 s. |
|
||
|
||
Settings precedence: plugin settings → env var → default.
|
||
|
||
## Limitations
|
||
|
||
- **No per-token streaming.** `--json` emits a single JSON document at process exit. `onText` is called exactly once.
|
||
- **Default ignores the gateway.** With `useGateway: false` (default) we always pass `--local`, skipping the WebSocket connect attempt entirely. Most users want this.
|
||
- **Built-in tools are intentionally excluded from MCP bridge.** `read`, `write`, `edit`, `bash`, `grep`, and `find` stay native to Fusion and are not duplicated through OpenClaw MCP.
|
||
- **AbortSignal sends SIGTERM.** If the CLI ignores it (e.g. during a long model download), the hard-kill timer (`cliTimeoutMs`) eventually fires.
|
||
|
||
## Public API
|
||
|
||
```ts
|
||
import {
|
||
OpenClawRuntimeAdapter,
|
||
resolveCliConfig,
|
||
buildOpenClawArgs,
|
||
createCliSession,
|
||
promptCli,
|
||
describeCliModel,
|
||
extractStderrError,
|
||
probeOpenClawBinary,
|
||
type CliConfig,
|
||
type GatewaySession,
|
||
type OpenClawAgentJson,
|
||
type OpenClawBinaryStatus,
|
||
} from "@fusion-plugin-examples/openclaw-runtime";
|
||
```
|
||
|
||
`probeOpenClawBinary({ binaryPath?, timeoutMs? })` runs `openclaw --version` and returns `{ available, version, binaryPath, reason, probeDurationMs }` — used by the dashboard's "Runtimes → OpenClaw" settings card.
|
||
|
||
## Agent configuration
|
||
|
||
To create a Fusion agent backed by OpenClaw, set `runtimeConfig.runtimeHint`:
|
||
|
||
```json
|
||
{
|
||
"name": "OpenClaw Executor",
|
||
"role": "executor",
|
||
"runtimeConfig": {
|
||
"runtimeHint": "openclaw"
|
||
}
|
||
}
|
||
```
|
||
|
||
Runtime selection happens in the dashboard's **New Agent → Plugin Runtime → OpenClaw**.
|
||
|
||
## Metadata
|
||
|
||
- **Plugin ID:** `fusion-plugin-openclaw-runtime`
|
||
- **Runtime ID:** `openclaw`
|
||
- **Package:** `@fusion-plugin-examples/openclaw-runtime`
|
||
|
||
## Development
|
||
|
||
```bash
|
||
pnpm --filter @fusion-plugin-examples/openclaw-runtime test # 44 tests
|
||
pnpm --filter @fusion-plugin-examples/openclaw-runtime build
|
||
```
|