@helmio/openclaw-helmio-chat
v0.4.0
Published
OpenClaw channel plugin that bridges Helmio's SignalR ChatHub to per-agent turns inside the Company VM. Installed at cloud-init via `openclaw plugins install npm:@helmio/openclaw-helmio-chat@<pinned>`. ADR-0008.
Readme
@helmio/openclaw-helmio-chat
OpenClaw channel plugin that bridges Helmio's SignalR ChatHub to per-agent turns inside the Company VM. Phase 1 scope per ADR-0008 Decision Principle 1.
What it does
- Inbound — connects to
${HELMIO_API_URL}/api/chat-hubwith the per-Company bridge token (HELMIO_CHAT_BRIDGE_TOKEN). On everymessageCreatedevent from a user (participantTypeId === 1), routes a turn to the CEO agent viadispatchInboundReplyWithBase(...). When the agent's primary reply settles, thedelivercallback POSTs the text back to${HELMIO_API_URL}/api/chat/messages. - Outbound —
outbound.attachedResults.sendTextPOSTs to the same endpoint when an agent invokes the sharedmessagetool with an explicit target. Attribution defaults to the CEO in Phase 1 (the only routable agent); Phase 2 will derive the calling agent fromctx.gateway/ sessionKey parsing. - Authentication — the bridge token authenticates the SignalR connection (
.WithAccessToken(...)) and every outbound HTTP POST (Authorization: Bearer ...). Backend trust model is in ADR-0008 Principle 1 #5.
Env vars
Both required, written into the VM .env by CompanyProvisionService.RenderEnv during provisioning:
HELMIO_API_URL— backend base URL, e.g.https://10.0.0.2or a cloudflared tunnel hostname.HELMIO_CHAT_BRIDGE_TOKEN— per-Company 32-byte URL-safe base64 token, hashed and stored inHelmioChatBridgeToken.
Agent metadata contract
The CEO is identified by agents.list[i].params.isCeo === true. Agent ids in the OpenClaw config are the stringified Helmio Agent.Id per CanonicalAgentList.Build; the plugin parses them back to numeric ids for the outbound POST payload.
Why a channel plugin and not a tool plugin
OpenClaw treats messaging channels as a first-class capability rather than as a collection of tools. The channel-plugin shape gives this plugin a lot for free:
- Core's system-prompt builder advertises the shared
messagetool against each channel — agents in a chat-driven turn know how to reply without per-agent coaching. - The channel turn kernel (
dispatchInboundReplyWithBase) wires session-key + dispatcher + record + finalize ordering automatically. - Phase 2 affordances (DMs, named groups,
@mentionfan-out viaAgentRouteBinding[],actions: ["edit", "delete"]on the sharedmessagetool) become amendments to the same shape rather than a second refactor.
Phase 1 scope
- CEO-only inbound routing — every user message routes a turn to the CEO agent regardless of which conversation it came from. Non-CEO agents are not yet wired as inbound recipients.
- No
@mentionparsing, no message edit/delete actions, no media. - Target grammar:
conv:<conversationId>(kind-agnostic). Wire payload is conversation-id keyed — the CEO's reply lands in the same conversation the user posted in, and the backend validates participation viaConversationParticipant. - Known routing gap: if the user posts in a DM with a non-CEO agent, the plugin's CEO-only inbound routing tries to post the CEO into that DM and the backend 403s ("agent is not a participant in the target conversation"). Fix is part of the Phase 2
@mentionfan-out work; for now the user sees no reply.
Install (cloud-init)
docker exec openclaw openclaw plugins install npm:@helmio/openclaw-helmio-chat@<pinned-version>Retry up to 3 times with 10s backoff; second + third attempts pass --force to race past gateway-startup config writes (ConfigMutationConflictError at v2026.5.7 — see project_openclaw_external_plugin_pattern.md).
Out of scope (phase 2)
@mentionparsing + multi-agent fan-out viaAgentRouteBinding[]+runtime.channel.turn.run(...)per ADR-0008 Principle 1 #7. This is also what fixes the DM-to-non-CEO 403 above — inbound routing decides the right agent based on the conversation kind + participants.- Token redaction (Notion / Linear paste →
(secret redacted)). helmio_chat_create_group(name, members[])tool (Principle 1 #8).- Threading (
ParentMessageId). - Per-agent outbound attribution (currently defaults to CEO for
outbound.sendText).
