Update README with capabilities negotiation flow

- Add capabilities negotiation to Features list
- Document connection flow (5 steps)
- Add capabilities_response message format
- Add request_capabilities message format
- Update JavaScript example with capabilities handling
- Update Python example with capabilities handling
This commit is contained in:
root
2026-03-31 21:43:06 +02:00
parent 1421f9d645
commit 0d226d608b

111
README.md
View File

@@ -14,6 +14,7 @@ Custom WebSocket-based messaging channel plugin for OpenClaw. Enables custom mes
- Reactions - Reactions
- Threads - Threads
- Typing indicators - Typing indicators
- 🔄 **Capabilities Negotiation** - Clients report their capabilities on connect
- 🛡️ **DM Security** - Three policies: `allowlist`, `open`, `closed` - 🛡️ **DM Security** - Three policies: `allowlist`, `open`, `closed`
- 🔗 **Threading** - Full support for threaded conversations - 🔗 **Threading** - Full support for threaded conversations
- 🤝 **Pairing Flow** - Text-based pairing with code exchange - 🤝 **Pairing Flow** - Text-based pairing with code exchange
@@ -87,9 +88,41 @@ const ws = new WebSocket('ws://localhost:8765/ws', {
}); });
``` ```
### Connection Flow
1. **Client connects** → Server authenticates via API key
2. **Server sends handshake** → Server capabilities (maximum allowed)
3. **Server requests capabilities** → Asks client what it supports
4. **Client responds** → Client capabilities + metadata
5. **Effective capabilities** → Server uses intersection of both
6. **Messaging begins** → Only supported message types allowed
### Message Format ### Message Format
**Client → Server:** #### Client → Server
**Capabilities Response (required after handshake):**
```json
{
"type": "capabilities_response",
"capabilities": {
"text": true,
"images": true,
"files": false,
"voice": false,
"reactions": false,
"threads": true,
"typing": true
},
"metadata": {
"clientName": "My App",
"version": "1.0.0",
"platform": "custom"
}
}
```
**Text Message:**
```json ```json
{ {
"type": "message", "type": "message",
@@ -101,7 +134,9 @@ const ws = new WebSocket('ws://localhost:8765/ws', {
} }
``` ```
**Server → Client (Handshake):** #### Server → Client
**Handshake:**
```json ```json
{ {
"type": "handshake", "type": "handshake",
@@ -109,19 +144,27 @@ const ws = new WebSocket('ws://localhost:8765/ws', {
"capabilities": { "capabilities": {
"text": true, "text": true,
"images": true, "images": true,
"files": false, "files": true,
"voice": false, "voice": true,
"reactions": false, "reactions": false,
"threads": true, "threads": true,
"typing": true "typing": true
}, },
"metadata": { "metadata": {
"serverName": "My WebSocket Server" "serverName": "My WebSocket Server"
} },
"timestamp": 1703275200000
} }
``` ```
**Server → Client (Message):** **Capabilities Request:**
```json
{
"type": "request_capabilities"
}
```
**Message:**
```json ```json
{ {
"type": "message", "type": "message",
@@ -146,12 +189,39 @@ const ws = new WebSocket('ws://localhost:8765/ws?key=YOUR_API_KEY');
ws.on('message', (data) => { ws.on('message', (data) => {
const msg = JSON.parse(data.toString()); const msg = JSON.parse(data.toString());
if (msg.type === 'message') {
switch (msg.type) {
case 'handshake':
console.log('Server capabilities:', msg.capabilities);
break;
case 'request_capabilities':
// Send client capabilities
ws.send(JSON.stringify({
type: 'capabilities_response',
capabilities: {
text: true,
images: true,
files: false,
voice: false,
reactions: false,
threads: true,
typing: true
},
metadata: {
clientName: 'My App',
version: '1.0.0'
}
}));
break;
case 'message':
console.log('Bot:', msg.content.text); console.log('Bot:', msg.content.text);
break;
} }
}); });
// Send message // Send message (after capabilities_response)
ws.send(JSON.stringify({ ws.send(JSON.stringify({
type: 'message', type: 'message',
from: 'user-123', from: 'user-123',
@@ -167,7 +237,30 @@ import json
def on_message(ws, message): def on_message(ws, message):
data = json.loads(message) data = json.loads(message)
if data['type'] == 'message':
if data['type'] == 'handshake':
print(f"Server capabilities: {data['capabilities']}")
elif data['type'] == 'request_capabilities':
# Send client capabilities
ws.send(json.dumps({
"type": "capabilities_response",
"capabilities": {
"text": True,
"images": True,
"files": False,
"voice": False,
"reactions": False,
"threads": True,
"typing": True
},
"metadata": {
"clientName": "My Python App",
"version": "1.0.0"
}
}))
elif data['type'] == 'message':
print(f"Bot: {data['content']['text']}") print(f"Bot: {data['content']['text']}")
ws = websocket.WebSocketApp( ws = websocket.WebSocketApp(