This document explains how to keep openclaw-go in sync when the OpenClaw gateway protocol changes.
All protocol types are canonically defined in TypeScript using @sinclair/typebox:
| TypeScript Schema | Go Target |
|---|---|
src/gateway/protocol/schema/frames.ts |
protocol/frames.go |
src/gateway/protocol/schema/agent.ts |
protocol/agent.go |
src/gateway/protocol/schema/agents-models-skills.ts |
protocol/agents.go, protocol/skills.go |
src/gateway/protocol/schema/sessions.ts |
protocol/sessions.go |
src/gateway/protocol/schema/config.ts |
protocol/config.go |
src/gateway/protocol/schema/cron.ts |
protocol/cron.go |
src/gateway/protocol/schema/channels.ts |
protocol/channels.go |
src/gateway/protocol/schema/nodes.ts |
protocol/nodes.go |
src/gateway/protocol/schema/devices.ts |
protocol/devices.go |
src/gateway/protocol/schema/exec-approvals.ts |
protocol/exec_approvals.go |
src/gateway/protocol/schema/logs-chat.ts |
protocol/chat.go |
src/gateway/protocol/schema/push.ts |
protocol/push.go |
src/gateway/protocol/schema/snapshot.ts |
protocol/sessions.go (Presence/Snapshot types) |
src/gateway/protocol/schema/wizard.ts |
protocol/config.go (Wizard types) |
src/gateway/protocol/schema/error-codes.ts |
protocol/constants.go |
src/gateway/protocol/schema/protocol-schemas.ts |
client/methods_*.go (method names) |
Each file maps to one domain area of the TypeScript schema:
| File | Domain |
|---|---|
frames.go |
Wire frame types (RequestFrame, ResponseFrame, EventFrame) |
agent.go |
Single-agent request/response types |
agents.go |
Multi-agent listing and management types |
skills.go |
Skill/model listing types |
sessions.go |
Session management + Presence/Snapshot types |
config.go |
Gateway configuration types |
cron.go |
Cron job types |
channels.go |
Channel and Talk types |
nodes.go |
Node management types |
devices.go |
Device types |
exec_approvals.go |
Exec approval types |
chat.go |
Chat history and send types |
push.go |
Push test types |
constants.go |
Protocol version, error codes, frame type constants |
models_test.go |
JSON round-trip tests for all types |
API methods are split by domain to mirror the protocol files:
| File | Domain |
|---|---|
client.go |
GatewayClient, connection, read loop |
request.go |
Request / RequestDecoded / RequestVoid helpers |
subscribe.go |
Event channel subscription |
methods_agent.go |
SendAgent, AbortAgent |
methods_agents.go |
Agent listing and management |
methods_channels.go |
Channel operations |
methods_chat.go |
Chat history, send, abort |
methods_config.go |
Config get/patch |
methods_cron.go |
Cron list/run |
methods_devices.go |
Device operations |
methods_exec.go |
Exec approval operations |
methods_misc.go |
Miscellaneous (e.g. Status) |
methods_nodes.go |
Node operations |
methods_sessions.go |
Session list/patch/delete |
client_test.go |
Integration tests |
- Locate the new
Type.Object(...)definition in the relevantschema/*.tsfile. - Translate each field to a Go struct field:
Type.String()→stringType.Integer()→intType.Boolean()→boolType.Optional(X)→ pointer*X+json:",omitempty"Type.Array(X)→[]XType.Record(...)/Type.Unknown()→json.RawMessageormap[string]interface{}- camelCase JSON key →
json:"camelCaseKey"
- Add the struct to the matching domain file in
protocol/(see table above). - Add a JSON round-trip test in
protocol/models_test.go.
Example: Adding a new FooParams struct:
// TypeScript schema (source)
export const FooParamsSchema = Type.Object({
sessionKey: NonEmptyString,
mode: Type.Optional(NonEmptyString),
limit: Type.Optional(Type.Integer({ minimum: 1 })),
});// Go translation (e.g. protocol/sessions.go)
type FooParams struct {
SessionKey string `json:"sessionKey"`
Mode *string `json:"mode,omitempty"`
Limit *int `json:"limit,omitempty"`
}- Find the new method key in
src/gateway/protocol/schema/protocol-schemas.ts. - Identify its params and response schema.
- Add the typed method to the matching domain file in
client/methods_<domain>.go:
func (c *GatewayClient) Foo(ctx context.Context, params protocol.FooParams) (*protocol.FooResult, error) {
var result protocol.FooResult
if err := c.RequestDecoded(ctx, "foo.method", params, &result); err != nil {
return nil, err
}
return &result, nil
}- Add a matching test in
client/client_test.go.
Use git diff on the schema directory to see what changed since the last SDK update:
# From the repo root — see all schema changes since the last protocol tag
git diff <last-tag>..HEAD -- src/gateway/protocol/schema/Or watch for additions/removals in types.ts (it exports every public type):
git diff <last-tag>..HEAD -- src/gateway/protocol/schema/types.tsFor a higher level of automation, consider the following tools:
quicktype can generate Go structs from JSON Schema. The typebox schemas can be exported to JSON Schema via:
# In src/gateway/protocol — add a script:
npx ts-node export-schemas.ts > schemas.json
quicktype --lang go --src-lang schema schemas.json -o ../openclaw-go/protocol/generated.goWrite a small TypeScript/Node script (scripts/gen-go-protocol.ts) that:
- Imports all
*Schemavalues fromschema/*.ts. - Uses
@sinclair/typebox+JSON.stringifyto emit the JSON Schema. - Maps each field type to a Go type string.
- Writes one file per domain under
protocol/.
This gives the tightest control over the output format, including per-file domain grouping and struct ordering.
Add a GitHub Actions step that:
- Computes a SHA of all
src/gateway/protocol/schema/*.tsfiles. - Compares to a
.protocol-hashfile committed inopenclaw-go/. - Fails the check and prints a diff if hashes diverge.
# .github/workflows/protocol-sync.yml
- name: Check protocol sync
run: |
NEW_HASH=$(sha256sum src/gateway/protocol/schema/*.ts | sha256sum | awk '{print $1}')
STORED=$(cat openclaw-go/.protocol-hash)
if [ "$NEW_HASH" != "$STORED" ]; then
echo "Protocol schema changed — run sync and update openclaw-go/.protocol-hash"
git diff HEAD -- src/gateway/protocol/schema/
exit 1
fi-
git diffthe relevantschema/*.tsfiles - Add/update Go structs in the matching
protocol/<domain>.gofile - Add/update methods in the matching
client/methods_<domain>.gofile - Add/update tests (
protocol/models_test.go,client/client_test.go) - Run
go build ./... && go test ./... - Update the
.protocol-hashfile (if using Option C)