Files
goclaw/pkg/protocol/frames.go
T
Viet TranandClaude Opus 4.6 f3f4c67b36 Initial commit: GoClaw AI agent gateway
Multi-agent AI gateway with WebSocket RPC, HTTP API, and messaging channel integrations.
Go port of OpenClaw with multi-tenant PostgreSQL, per-user isolation, security hardening,
and production observability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 14:58:07 +07:00

107 lines
3.3 KiB
Go

// Package protocol defines the wire format for the GoClaw Gateway WebSocket protocol.
// This package is importable by Service 2 and other clients.
package protocol
import "encoding/json"
// Protocol version. Clients must negotiate this during connect handshake.
const ProtocolVersion = 3
// Frame types
const (
FrameTypeRequest = "req"
FrameTypeResponse = "res"
FrameTypeEvent = "event"
)
// RawFrame is used for initial parsing to determine frame type.
type RawFrame struct {
Type string `json:"type"`
Raw json.RawMessage `json:"-"` // original bytes for re-parsing
}
// RequestFrame is sent by clients to invoke an RPC method.
type RequestFrame struct {
Type string `json:"type"` // always "req"
ID string `json:"id"` // unique request ID (client-generated)
Method string `json:"method"` // RPC method name
Params json.RawMessage `json:"params,omitempty"`
}
// ResponseFrame is sent by the server in response to a request.
type ResponseFrame struct {
Type string `json:"type"` // always "res"
ID string `json:"id"` // matches request ID
OK bool `json:"ok"` // true if success
Payload interface{} `json:"payload,omitempty"` // response data (when ok=true)
Error *ErrorShape `json:"error,omitempty"` // error info (when ok=false)
}
// ErrorShape describes a protocol error.
type ErrorShape struct {
Code string `json:"code"`
Message string `json:"message"`
Details interface{} `json:"details,omitempty"`
Retryable bool `json:"retryable,omitempty"`
RetryAfterMs int `json:"retryAfterMs,omitempty"`
}
// EventFrame is pushed from server to client without a preceding request.
type EventFrame struct {
Type string `json:"type"` // always "event"
Event string `json:"event"` // event name
Payload interface{} `json:"payload,omitempty"` // event data
Seq int64 `json:"seq,omitempty"` // ordering sequence number
StateVersion *StateVersion `json:"stateVersion,omitempty"` // version counters for state sync
}
// StateVersion tracks version counters for optimistic state sync.
type StateVersion struct {
Presence int64 `json:"presence"`
Health int64 `json:"health"`
}
// NewOKResponse creates a success response frame.
func NewOKResponse(id string, payload interface{}) *ResponseFrame {
return &ResponseFrame{
Type: FrameTypeResponse,
ID: id,
OK: true,
Payload: payload,
}
}
// NewErrorResponse creates an error response frame.
func NewErrorResponse(id string, code, message string) *ResponseFrame {
return &ResponseFrame{
Type: FrameTypeResponse,
ID: id,
OK: false,
Error: &ErrorShape{
Code: code,
Message: message,
},
}
}
// NewEvent creates an event frame.
func NewEvent(event string, payload interface{}) *EventFrame {
return &EventFrame{
Type: FrameTypeEvent,
Event: event,
Payload: payload,
}
}
// ParseFrameType extracts the frame type from raw JSON bytes.
// Returns the type string and remaining bytes for re-parsing.
func ParseFrameType(data []byte) (string, error) {
var raw struct {
Type string `json:"type"`
}
if err := json.Unmarshal(data, &raw); err != nil {
return "", err
}
return raw.Type, nil
}