-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Problem
Currently, messages are delivered as single frames over the WebSocket connection. This works for small payloads but breaks down with:
- Large file transfers (documents, images, model weights)
- Embedding vectors
- Batch data payloads
- Any payload approaching the frame size limit (
relayMaxFrameBytes)
Messages that exceed the frame limit are rejected. There's no way to send large data between agents.
Proposal
Implement chunked streaming over the existing WebSocket connection. No new infrastructure needed — the WebSocket is already there, bidirectional, and persistent.
Frame Protocol
Add a streaming frame type alongside existing deliver and heartbeat:
stream_start → { streamId, requestId, totalBytes, totalChunks, metadata }
stream_chunk → { streamId, chunkIndex, data (base64 or binary), checksum }
stream_end → { streamId, finalChecksum }
stream_abort → { streamId, reason }
stream_ack → { streamId, chunkIndex } (flow control)
Flow
Sender's connector → Sender's proxy DO → WebSocket → Recipient's connector
1. Sender sends stream_start (metadata: size, type, sender DID)
2. Proxy DO stores stream metadata, forwards to recipient's WebSocket
3. Sender sends stream_chunk (N chunks, flow-controlled by stream_ack)
4. Recipient's connector reassembles chunks into the inbox
5. Sender sends stream_end with final checksum
6. Recipient verifies checksum → enqueues complete message for replay to OpenClaw
7. Receipt sent back on completion
Flow Control
- Recipient sends
stream_ackper chunk (or per N chunks as a window) - Sender pauses if no ack received within timeout
- Prevents fast sender from overwhelming slow recipient
- DO can enforce per-stream memory limits
Failure Handling
stream_abortfrom either side cancels the stream- If WebSocket drops mid-stream: recipient has partial chunks in temp storage
- On reconnect: sender can resume from last ack'd chunk (if stream hasn't expired)
- Stream TTL: incomplete streams are cleaned up after configurable timeout
- Checksum mismatch on
stream_end: recipient sendsstream_abort, sender can retry
DO Considerations
- DO doesn't need to buffer the entire payload — it forwards chunks as they arrive
- If recipient is offline: chunks queue in DO storage (same pattern as regular messages)
- Per-stream memory limit on DO to prevent abuse
- Concurrent stream limit per agent pair
Why Not HTTP Multipart?
The WebSocket is already established, authenticated, and bidirectional. HTTP would mean:
- New auth flow per upload
- No flow control (stream_ack)
- Can't resume on disconnect
- Separate infrastructure from the message path
Backward Compatibility
- Old connectors that don't understand
stream_*frames ignore them (existing frame parsing skips unknown types) - Protocol version negotiation (#future) would handle this cleanly
- Fallback: reject large messages with clear error suggesting connector upgrade
Implementation Phases
- Phase 1: Basic chunking — split large messages into chunks, reassemble on receipt. No flow control, no resume.
- Phase 2: Flow control —
stream_ackwindow, backpressure - Phase 3: Resume on disconnect — checkpoint from last ack'd chunk
Related
- Frame size limit:
relayMaxFrameBytesin proxy config - DO queue:
RELAY_QUEUE_MAX_MESSAGES_PER_AGENT— streaming chunks should count as one logical message, not N
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels