Files
SynthesisAgent.OpenclawPlugin/README.md
zhi 0324a47d13 feat: HTTP /v1/chat/completions + WS bridge + process manager
Real first cut. OpenClaw routes agent turns via /v1/chat/completions
(OpenAI-compatible SSE) into our bridge. Bridge ensures a long-lived
claude process per session-key, pushes the user message as
notifications/claude/channel into the running Claude Code, awaits a
reply via the WS connection, streams the reply back as SSE deltas.

- core/process-manager: spawn / track / reap claude processes, auto-confirm
  the --dangerously-load-development-channels dev-mode prompt by piping
  "1\n" to stdin shortly after spawn
- web/bridge-server: unified HTTP + WS server, per-session FIFO queue,
  SSE heartbeat (empty content delta — SSE comments don't reset OpenClaw's
  idle watchdog), reply buffering for streaming progress chunks
- index.ts: definePluginEntry for OpenClaw runtime + standalone-mode main
  for laptop smoke testing (just `bun index.ts`)

Same-machine simplifications: no bridge token, no permission_request
reverse channel. Session key = agent_id::chat_id (contractor-agent
convention).
2026-05-14 13:28:00 +00:00

89 lines
3.3 KiB
Markdown

# SynthesisAgent.OpenclawPlugin
OpenClaw plugin that routes agent turns through long-lived interactive Claude Code processes. Replaces the contractor-agent `claude -p` spawn pattern.
## Components
```
index.ts plugin entry + standalone-run main
core/config.ts config + defaults
core/session-mapping.ts openclaw_session ↔ claude_session_uuid (JSON file)
core/process-manager.ts spawn / track / reap claude processes
web/bridge-server.ts HTTP server + WS bridge server in one module
```
## Two ways to run
### As OpenClaw plugin (production)
OpenClaw loads `index.ts` via `definePluginEntry`. On `gateway_start`, the bridge server boots on the configured ports.
### Standalone (development / testing)
```bash
bun index.ts
```
Same code path, but no `definePluginEntry` registration — just boots the bridge with default config.
## Config (`openclaw.plugin.json` configSchema)
| Key | Default | Notes |
|---|---|---|
| `bridgePort` | 18900 | HTTP port for `/v1/chat/completions` |
| `channelWsPort` | 18901 | WebSocket port for ClaudePlugin connections |
| `permissionMode` | `bypassPermissions` | Spawned Claude's permission mode |
| `idleKillMs` | 3 600 000 | Idle TTL before SIGTERM |
| `maxProcesses` | 16 | Process pool cap |
| `mappingDbPath` | `~/.openclaw/synthesis/sessions.json` | Persistent session map |
| `channelName` | `synthesis` | Used in `--channels server:<channelName>` |
## Laptop smoke test (no real OpenClaw needed)
```bash
# 0. Globally register the ClaudePlugin as an MCP server (one-time)
claude mcp add --scope user synthesis -- bun run \
/path/to/SynthesisAgent.ClaudePlugin/server.ts
# 1. Start OpenclawPlugin standalone (this terminal)
cd SynthesisAgent.OpenclawPlugin
bun install
bun index.ts
# → HTTP listening on 127.0.0.1:18900
# → WS listening on 127.0.0.1:18901/bridge
# 2. POST a chat completion (another terminal)
curl -N -X POST http://127.0.0.1:18900/v1/chat/completions \
-H 'Content-Type: application/json' \
-H 'X-Openclaw-Agent-Id: dev' \
-H 'X-Openclaw-Chat-Id: test-1' \
-H "X-Openclaw-Workspace: $HOME/some-trusted-dir" \
-d '{
"model":"synthesis-claude-bridge",
"messages":[
{"role":"user","content":"Reply with exactly the word READY"}
]
}'
```
Expected flow:
1. OpenclawPlugin receives request, ensures a claude process for `dev::test-1`
2. Spawns `claude --channels server:synthesis --dangerously-load-development-channels server:synthesis --resume <new-uuid> ...`
3. ClaudePlugin (inside the claude process) dials `ws://127.0.0.1:18901/bridge`, sends `hello`
4. OpenclawPlugin pushes `inbound` frame, ClaudePlugin emits `notifications/claude/channel`
5. Claude reacts, eventually calls `reply(text)` tool
6. ClaudePlugin sends `reply` WS frame, OpenclawPlugin streams it as SSE delta
7. curl sees the reply text
## Known v1 simplifications (documented punch list)
- Session-key extraction reads headers only — production OpenClaw routing will need the contractor-agent style "Conversation info" parser
- No tool-catalog proxy: ClaudePlugin only exposes `reply`; the model uses Claude Code's built-in tools (Read/Edit/Bash/Grep) for everything else
- No permission_request reverse channel (full perms by config)
- No bridge token / auth handshake (same-machine assumption)
- Standalone mode boots with defaults only; no config flags
## License
Apache-2.0