Skip to content

feat(backends): add hermes, openclaw (ACP) + nanoclaw (socket) backends#56

Open
drewstone wants to merge 2 commits into
mainfrom
feat/acp-backends
Open

feat(backends): add hermes, openclaw (ACP) + nanoclaw (socket) backends#56
drewstone wants to merge 2 commits into
mainfrom
feat/acp-backends

Conversation

@drewstone

Copy link
Copy Markdown
Owner

What

Adds a generic ACP (Agent Client Protocol) backend to cli-bridge, registering hermes and openclaw. These two don't fit the existing "spawn a CLI, parse stdout" pattern — they expose <bin> acp, a JSON-RPC 2.0 ndjson stdio server with a stateful session. cli-bridge had no ACP client; this is it.

Wire flow (verified live against hermes acp, protocol v1)

spawn `<bin> acp`  (cwd = req.cwd → the agent discovers workspace skills/context)
 → initialize {protocolVersion, clientCapabilities}
 → session/new {cwd, mcpServers}              → { sessionId }
 → session/prompt {sessionId, prompt:[{type:text}]}
 ← stream session/update {content:{text}}      → ChatDelta.content
 ← session/request_permission                  → auto-allow (first option, trusted scope)
 ← result {stopReason}                          → finish_reason

Only message chunks become content — agent_thought_chunk / plan / tool chunks are skipped so reasoning never pollutes the OpenAI stream.

Registration

hermes + openclaw backends (bins HERMES_BIN / OPENCLAW_BIN), opt-in via BRIDGE_BACKENDS. Health reports unavailable if the binary is absent (openclaw isn't installed on every host yet), so registering is safe.

Verification

  • Live end-to-end against hermes acp: health → ready (Hermes v0.10.0); chat() → spawned the ACP server, ran the full handshake + session/prompt, streamed real deltas, clean stop. The wire protocol was confirmed by a raw ndjson probe before writing the backend.
  • 4/4 unit tests against a mock ACP agent: handshake + streaming, message-vs-thought filtering, permission auto-allow, spawn-error surfacing. Typechecks clean.

nanoclaw (intentionally not here)

nanoclaw is a multi-channel messaging daemon (it boots a DB + channels; acp just starts the daemon — no one-shot stdio mode), so it doesn't fit the stdio-backend model. It needs a different integration (its own HTTP/queue interface) — tracked separately so this PR stays a clean, verified ACP addition.

🤖 Generated with Claude Code

… Protocol)

cli-bridge had no ACP client; the existing backends spawn a CLI and parse stdout, but
hermes and openclaw expose `<bin> acp` — a JSON-RPC 2.0 ndjson stdio server with a
stateful session. AcpBackend drives any such agent:

  spawn `<bin> acp` (cwd=req.cwd so it discovers workspace skills/context)
  → initialize → session/new {cwd,mcpServers} → session/prompt {prompt:[text]}
  ← stream session/update {content:{text}} → ChatDelta.content (message chunks only;
    thought/plan/tool chunks skipped so reasoning never pollutes the OpenAI stream)
  ← session/request_permission → auto-allow (first option; trusted scope)
  ← result {stopReason} → finish_reason

Registered as hermes + openclaw backends (config bins HERMES_BIN/OPENCLAW_BIN, opt-in
via BRIDGE_BACKENDS; health reports `unavailable` if the binary is absent).

VERIFIED: live end-to-end against `hermes acp` — health ready (v0.10.0), the handshake +
session/prompt streamed real deltas with a clean stop. Wire protocol confirmed by a raw
ndjson probe first. 4/4 unit tests against a mock ACP agent (handshake, message-vs-thought
filtering, permission auto-allow, spawn-error), typechecks clean.

nanoclaw is NOT included here: it is a multi-channel messaging DAEMON (boots a DB +
channels, no `acp`/one-shot stdio mode), so it does not fit the stdio-backend model —
it needs a different integration (its own HTTP/queue interface), tracked separately.
…daemon

NanoClaw isn't a one-shot CLI: it's a long-lived multi-channel agent daemon (Claude Code
under the @onecli-sh Chat SDK) that exposes a CLI channel over a Unix socket
(<nanoclaw>/data/cli.sock). So the proper integration is a socket CLIENT, mirroring
NanoClaw's own scripts/chat.ts:

  connect(socket) → write {"text": prompt}\n
  ← stream {"text": reply}\n lines → ChatDelta.content
  completion is SILENCE-based (NanoClaw sends no done event) — ends after silenceMs of
  quiet following the first reply, or socket close, or the hard cap.

Registered as the `nanoclaw` backend (NANOCLAW_SOCKET → the daemon's data/cli.sock;
opt-in via BRIDGE_BACKENDS; health=unavailable when the daemon isn't running).

LIMITATION (documented): the daemon owns its own workspace, so NanoClaw does NOT honor a
per-request cwd — workspace skill/profile materialization doesn't reach it (configure
skills on the NanoClaw side). This is inherent to its daemon model, not a shortcut.

Verified: 5/5 tests against a REAL Unix-socket server speaking NanoClaw's exact protocol
(stream-then-silence, socket-close finish, health ready/unavailable). Typechecks clean.
@drewstone

Copy link
Copy Markdown
Owner Author

Added: NanoClaw backend (3rd harness)

NanoClaw doesn't fit the ACP/stdio model — it's a long-lived multi-channel daemon (Claude Code under the @onecli-sh Chat SDK) exposing a CLI channel over a Unix socket (<nanoclaw>/data/cli.sock). So NanoclawBackend is a socket client, mirroring NanoClaw's own scripts/chat.ts:

connect → write {"text": prompt}\n → stream {"text": reply}\n → silence = done
  • Registered as nanoclaw (NANOCLAW_SOCKET → the daemon's data/cli.sock; opt-in via BRIDGE_BACKENDS; health unavailable when the daemon isn't running).
  • Documented limitation: the daemon owns its own workspace, so NanoClaw doesn't honor a per-request cwd — workspace skill/profile materialization doesn't reach it (configure skills NanoClaw-side). Inherent to the daemon model, not a shortcut.
  • Verified: 5/5 tests against a real Unix-socket server speaking NanoClaw's exact protocol (stream-then-silence, socket-close finish, health states).

This PR now adds all three harnesses you asked for: hermes + openclaw (ACP) and nanoclaw (socket). 9/9 tests, typecheck clean.

@drewstone drewstone changed the title feat(backends): ACP backend — hermes + openclaw (Agent Client Protocol) feat(backends): add hermes, openclaw (ACP) + nanoclaw (socket) backends Jun 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant