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

3.8 KiB

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:

{ "type": "hello",
  "openclaw_session": "<sid>",
  "claude_session":   "<uuid>",
  "pid":              12345,
  "token":            "<bridge_token>" }

OpenclawPlugin responds with:

{ "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)

{ "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:

{ "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:

{ "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:

{ "type": "tool_call",
  "call_id":  "tc_001",
  "tool":     "pcexec",
  "args":     { ... } }

OpenclawPlugin invokes the corresponding OpenClaw tool and responds:

{ "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:

{ "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

{ "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.