Files
SynthesisAgent/docs/wire-protocol.md
zhi 0fb46e318e chore: initial scaffolding for SynthesisAgent
Bridge between OpenClaw (multi-channel hub) and interactive Claude Code,
keeping autonomous agent usage on the subscription quota after the
2026-06-15 Agent SDK credit split.

Initial scaffolding only — two submodules with skeletons:

- SynthesisAgent.ClaudePlugin: stdio MCP server registered as a --channels
  source. Declares experimental.claude/channel capability (verified 2026-05-14
  against the official Anthropic discord plugin) and emits
  notifications/claude/channel to push OpenClaw inbound messages into the
  Claude turn loop.

- SynthesisAgent.OpenclawPlugin: process manager + session mapping +
  bridge WebSocket. Currently shaped around a custom ws protocol; will be
  rewritten as a model-provider HTTP server (contractor-agent pattern) so
  OpenClaw routes inbound channel messages through it via /v1/chat/completions.

See STATUS.md for the open punch list and docs/wire-protocol.md for the
(soon-to-change) inter-plugin frame schema.
2026-05-14 09:39:02 +00:00

123 lines
3.8 KiB
Markdown

# Wire Protocol — OpenclawPlugin ↔ ClaudePlugin
A WebSocket connection from each spawned ClaudePlugin instance back to OpenclawPlugin's bridge server. JSON messages, one per frame.
## Connection bootstrap
On spawn, OpenclawPlugin sets these env vars on the Claude process so ClaudePlugin can find its way home:
| Env var | Purpose |
|--------------------------------|---------------------------------------------------------------|
| `SYNTHESIS_BRIDGE_URL` | WebSocket URL, e.g. `ws://127.0.0.1:18801/bridge` |
| `SYNTHESIS_BRIDGE_TOKEN` | Shared secret, validated server-side |
| `SYNTHESIS_OPENCLAW_SESSION` | The OpenClaw session ID this Claude process serves |
| `SYNTHESIS_CLAUDE_SESSION` | The Claude session UUID (for traceability) |
ClaudePlugin connects on startup and sends:
```json
{ "type": "hello",
"openclaw_session": "<sid>",
"claude_session": "<uuid>",
"pid": 12345,
"token": "<bridge_token>" }
```
OpenclawPlugin responds with:
```json
{ "type": "hello_ack", "tools": [ /* tool catalog */ ], "session_meta": { ... } }
```
If `hello` is rejected (bad token, unknown session), the server closes the socket.
## Direction: OpenClaw → Claude (inbound events)
```json
{ "type": "inbound_message",
"request_id": "msg_abc123",
"content": "user message text",
"meta": {
"chat_id": "<openclaw_session_id>",
"message_id": "...",
"user": "alice",
"user_id": "...",
"ts": "2026-05-14T...Z",
"source_channel": "discord",
"attachments": [ ... ]
}
}
```
ClaudePlugin translates each `inbound_message` into:
```json
{ "method": "notifications/claude/channel",
"params": { "content": ..., "meta": ... } }
```
…and emits it on its MCP stdio. Claude Code reacts by starting a new turn.
## Direction: OpenClaw → Claude (permission reply)
When the user answers a permission prompt out of band (e.g. "yes abxyz" in Discord), OpenClaw forwards:
```json
{ "type": "permission_reply",
"request_id": "abxyz",
"behavior": "allow" }
```
ClaudePlugin emits an MCP `notifications/claude/channel/permission` for that request_id.
## Direction: Claude → OpenClaw (tool calls)
Each `tools/call` Claude makes on ClaudePlugin (excluding internal ones the plugin owns) is forwarded over the bridge:
```json
{ "type": "tool_call",
"call_id": "tc_001",
"tool": "pcexec",
"args": { ... } }
```
OpenclawPlugin invokes the corresponding OpenClaw tool and responds:
```json
{ "type": "tool_result",
"call_id": "tc_001",
"ok": true,
"result": { ... } }
```
…or with `"ok": false, "error": "..."`.
## Direction: Claude → OpenClaw (permission request)
When Claude Code asks for a permission via `notifications/claude/channel/permission_request`, ClaudePlugin forwards:
```json
{ "type": "permission_request",
"request_id": "<5 lowercase letters minus 'l'>",
"tool": "Bash",
"args": { ... },
"rationale": "..." }
```
OpenclawPlugin routes this to the user via the source channel (e.g. posts a Discord message: "Approve? Reply `yes <code>`").
## Direction: bidirectional health
```json
{ "type": "ping", "ts": ... }
{ "type": "pong", "ts": ... }
```
OpenclawPlugin sends `ping` every 30s. ClaudePlugin missing 2 consecutive pings → server marks the connection dead, allows reconnect.
## Reconnect semantics
- ClaudePlugin reconnects on socket close with exponential backoff (1s, 2s, 4s, max 30s).
- During disconnect, OpenclawPlugin buffers up to N inbound messages per session (configurable). Buffer overflow drops the oldest with a warning.
- On reconnect, the `hello` message re-establishes session binding.