diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ddf52d6..ae1d26c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,6 +15,7 @@ jobs: - run: cargo clippy --all-targets --all-features -- -D warnings - run: cargo test --all-targets --all-features - run: cargo test --no-default-features --features sim,mcp + - run: cargo run --features mcp --bin leash-schema -- --check - run: cargo package --locked - run: scripts/smoke-all.sh diff --git a/Cargo.toml b/Cargo.toml index 456bff2..47e8393 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "leash-harness" version = "0.1.1" edition = "2021" +default-run = "leash" license = "MIT" description = "Composable local-LLM and robotics harness with MCP, CLI, and safe robot adapters" repository = "https://github.com/specdog/leash" @@ -17,6 +18,7 @@ include = [ "README.md", "docs/**", "examples/**", + "schemas/**", "scripts/**", "src/**", ] @@ -25,6 +27,11 @@ include = [ name = "leash" path = "src/bin/leash.rs" +[[bin]] +name = "leash-schema" +path = "src/bin/leash-schema.rs" +required-features = ["mcp"] + [features] default = ["sim", "http", "mcp"] sim = [] diff --git a/README.md b/README.md index cf4546f..1b10d3c 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,20 @@ POST /estop/reset { token, approval } Clear estop WS /ws/telemetry Streaming telemetry envelope frames ``` +## External Schemas And Clients + +Leash publishes generated JSON Schema for external tools at +`schemas/leash-messages.schema.json`. Regenerate it from Rust wire types with: + +```bash +cargo run --features mcp --bin leash-schema -- --output schemas/leash-messages.schema.json +``` + +CI checks schema freshness with `--check`, and +[docs/SCHEMAS.md](docs/SCHEMAS.md) documents compatibility rules. Standard-library +Python and Node examples in `examples/clients/` read `/health`, consume +`/telemetry`, and invoke `POST /stop` against sim HTTP. + ## Viewer Frames `WS /ws/telemetry`, `GET /events/telemetry`, and `GET /sse/telemetry` emit diff --git a/docs/SCHEMAS.md b/docs/SCHEMAS.md new file mode 100644 index 0000000..be8028c --- /dev/null +++ b/docs/SCHEMAS.md @@ -0,0 +1,35 @@ +# Message Schemas + +Leash publishes JSON Schema for the wire messages external tools consume: + +- HTTP health, capabilities, telemetry, stream frames, and stop responses +- MCP HTTP status, tool list, module map, and call responses +- capability descriptors, safety classes, module graph messages, and adapter messages +- perception, visualization, planner, patrol, spatial-memory, drone, and manipulator payloads + +The canonical artifact is [schemas/leash-messages.schema.json](../schemas/leash-messages.schema.json). +It is generated from Rust `serde` + `schemars` types: + +```bash +cargo run --features mcp --bin leash-schema -- --output schemas/leash-messages.schema.json +``` + +CI runs the generator in `--check` mode. If a Rust wire type changes without +updating the checked-in schema, CI fails. + +## Compatibility Rules + +`schema_version` is the external message contract version. Change it when a +consumer must update code to keep parsing messages safely, including field +removal, field rename, enum value removal or rename, or a required-field change. + +Backward-compatible changes keep the same `schema_version`: adding optional +fields, adding fields with serde defaults, adding new schemas, widening numeric +ranges, or adding enum values that clients can safely ignore. Consumers should +ignore unknown object fields and switch on known enum values with an explicit +fallback path. + +Versioned payloads such as `visualization.version` and manipulator `version` +stay scoped to that nested payload. A nested payload version bump does not +require a top-level `schema_version` bump unless the cross-message contract also +breaks. diff --git a/examples/clients/README.md b/examples/clients/README.md new file mode 100644 index 0000000..b4f5038 --- /dev/null +++ b/examples/clients/README.md @@ -0,0 +1,16 @@ +# External Client Examples + +These examples use only language standard libraries. Start sim HTTP first: + +```bash +cargo run -- serve http --profile sim --listen 127.0.0.1:8000 +``` + +Then run either client: + +```bash +LEASH_URL=http://127.0.0.1:8000 python3 examples/clients/python/http_client.py +LEASH_URL=http://127.0.0.1:8000 node examples/clients/node/http-client.mjs +``` + +Each client reads `/health`, consumes `/telemetry`, and invokes `POST /stop`. diff --git a/examples/clients/node/http-client.mjs b/examples/clients/node/http-client.mjs new file mode 100644 index 0000000..7923cad --- /dev/null +++ b/examples/clients/node/http-client.mjs @@ -0,0 +1,31 @@ +const baseUrl = (process.env.LEASH_URL || "http://127.0.0.1:8000").replace(/\/$/, ""); + +async function requestJson(method, path) { + const response = await fetch(`${baseUrl}${path}`, { method }); + if (!response.ok) { + throw new Error(`${method} ${path} returned HTTP ${response.status}`); + } + return response.json(); +} + +const health = await requestJson("GET", "/health"); +const telemetry = await requestJson("GET", "/telemetry"); +const stop = await requestJson("POST", "/stop"); + +if (health.ok !== true) { + throw new Error("health did not report ok=true"); +} +if (!telemetry.robot || telemetry.profile !== "sim") { + throw new Error("telemetry did not look like a sim frame"); +} +if (stop.ok !== true) { + throw new Error("stop did not report ok=true"); +} + +console.log(JSON.stringify({ + ok: true, + runtime: "node", + profile: health.profile, + robot: telemetry.robot, + stop, +})); diff --git a/examples/clients/python/http_client.py b/examples/clients/python/http_client.py new file mode 100644 index 0000000..8688df2 --- /dev/null +++ b/examples/clients/python/http_client.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +import json +import os +import urllib.request + + +BASE_URL = os.environ.get("LEASH_URL", "http://127.0.0.1:8000").rstrip("/") + + +def request_json(method, path): + request = urllib.request.Request(f"{BASE_URL}{path}", method=method) + if method == "POST": + request.data = b"" + with urllib.request.urlopen(request, timeout=5) as response: + return json.loads(response.read().decode("utf-8")) + + +def main(): + health = request_json("GET", "/health") + telemetry = request_json("GET", "/telemetry") + stop = request_json("POST", "/stop") + + if health.get("ok") is not True: + raise SystemExit("health did not report ok=true") + if not telemetry.get("robot") or telemetry.get("profile") != "sim": + raise SystemExit("telemetry did not look like a sim frame") + if stop.get("ok") is not True: + raise SystemExit("stop did not report ok=true") + + print(json.dumps({ + "ok": True, + "runtime": "python", + "profile": health["profile"], + "robot": telemetry["robot"], + "stop": stop, + }, sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/schemas/README.md b/schemas/README.md new file mode 100644 index 0000000..0ef8dd4 --- /dev/null +++ b/schemas/README.md @@ -0,0 +1,17 @@ +# Leash Schemas + +`leash-messages.schema.json` is generated from Rust wire types with: + +```bash +cargo run --features mcp --bin leash-schema -- --output schemas/leash-messages.schema.json +``` + +CI checks the file with: + +```bash +cargo run --features mcp --bin leash-schema -- --check +``` + +The top-level `schema_version` changes only when the external message contract +has a breaking change. Additive optional fields and fields with serde defaults +are backward compatible under the same version. diff --git a/schemas/leash-messages.schema.json b/schemas/leash-messages.schema.json new file mode 100644 index 0000000..b0c4fc2 --- /dev/null +++ b/schemas/leash-messages.schema.json @@ -0,0 +1,6097 @@ +{ + "$id": "https://specdog.github.io/leash/schemas/leash-messages.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Canonical JSON Schema output for Leash HTTP, MCP, telemetry, capability, module, and adapter messages generated from Rust types.", + "schema_version": "leash-message-schema-v1", + "schemas": { + "AdapterCategory": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "enum": [ + "simulation", + "compatibility", + "mobile-base", + "drone", + "manipulator", + "perception", + "sensor" + ], + "title": "AdapterCategory", + "type": "string" + }, + "AdapterMaturity": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "enum": [ + "stable", + "beta", + "alpha", + "experimental" + ], + "title": "AdapterMaturity", + "type": "string" + }, + "AdapterProfile": { + "$defs": { + "AdapterCategory": { + "enum": [ + "simulation", + "compatibility", + "mobile-base", + "drone", + "manipulator", + "perception", + "sensor" + ], + "type": "string" + }, + "AdapterMaturity": { + "enum": [ + "stable", + "beta", + "alpha", + "experimental" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "boundary": { + "type": "string" + }, + "capabilities": { + "items": { + "type": "string" + }, + "type": "array" + }, + "category": { + "$ref": "#/$defs/AdapterCategory" + }, + "feature_flags": { + "items": { + "type": "string" + }, + "type": "array" + }, + "maturity": { + "$ref": "#/$defs/AdapterMaturity" + }, + "required_gates": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "category", + "maturity", + "capabilities", + "feature_flags", + "required_gates", + "boundary" + ], + "title": "AdapterProfile", + "type": "object" + }, + "AgentMessage": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "id": { + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "source": { + "type": "string" + }, + "text": { + "type": "string" + }, + "ts_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "id", + "ts_ms", + "source", + "text" + ], + "title": "AgentMessage", + "type": "object" + }, + "AgentMessageAck": { + "$defs": { + "AgentMessage": { + "properties": { + "id": { + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "source": { + "type": "string" + }, + "text": { + "type": "string" + }, + "ts_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "id", + "ts_ms", + "source", + "text" + ], + "type": "object" + }, + "AgentModelResponse": { + "properties": { + "model": { + "type": "string" + }, + "ok": { + "type": "boolean" + }, + "prompt": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "text": { + "type": "string" + } + }, + "required": [ + "ok", + "provider", + "model", + "prompt", + "text" + ], + "type": "object" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "message": { + "$ref": "#/$defs/AgentMessage" + }, + "ok": { + "type": "boolean" + }, + "response": { + "anyOf": [ + { + "$ref": "#/$defs/AgentModelResponse" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "ok", + "message" + ], + "title": "AgentMessageAck", + "type": "object" + }, + "AgentMessageList": { + "$defs": { + "AgentMessage": { + "properties": { + "id": { + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "source": { + "type": "string" + }, + "text": { + "type": "string" + }, + "ts_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "id", + "ts_ms", + "source", + "text" + ], + "type": "object" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "messages": { + "items": { + "$ref": "#/$defs/AgentMessage" + }, + "type": "array" + }, + "ok": { + "type": "boolean" + } + }, + "required": [ + "ok", + "messages" + ], + "title": "AgentMessageList", + "type": "object" + }, + "AgentModelResponse": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "model": { + "type": "string" + }, + "ok": { + "type": "boolean" + }, + "prompt": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "text": { + "type": "string" + } + }, + "required": [ + "ok", + "provider", + "model", + "prompt", + "text" + ], + "title": "AgentModelResponse", + "type": "object" + }, + "AutonomyOverlay": { + "$defs": { + "PatrolStrategy": { + "enum": [ + "coverage", + "frontier", + "random" + ], + "type": "string" + }, + "PlannerGoal": { + "properties": { + "frame_id": { + "type": "string" + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + }, + "tolerance_m": { + "format": "double", + "type": "number" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "tolerance_m", + "speed_mode" + ], + "type": "object" + }, + "SpeedMode": { + "enum": [ + "low", + "medium", + "high" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "active": { + "type": "boolean" + }, + "goal": { + "anyOf": [ + { + "$ref": "#/$defs/PlannerGoal" + }, + { + "type": "null" + } + ] + }, + "mode": { + "type": "string" + }, + "status": { + "type": "string" + }, + "strategy": { + "anyOf": [ + { + "$ref": "#/$defs/PatrolStrategy" + }, + { + "type": "null" + } + ] + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "visited_cells": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "mode", + "active", + "status", + "visited_cells" + ], + "title": "AutonomyOverlay", + "type": "object" + }, + "BatteryStatus": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "status": { + "type": "string" + }, + "voltage_v": { + "format": "double", + "type": [ + "number", + "null" + ] + } + }, + "required": [ + "status" + ], + "title": "BatteryStatus", + "type": "object" + }, + "CameraStatus": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "health": { + "type": "string" + }, + "snapshot_url": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": "string" + }, + "stream_url": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "status", + "health" + ], + "title": "CameraStatus", + "type": "object" + }, + "Capabilities": { + "$defs": { + "AcceleratorBackend": { + "enum": [ + "none", + "cpu", + "cuda" + ], + "type": "string" + }, + "AcceleratorProbe": { + "properties": { + "available": { + "type": "boolean" + }, + "backend": { + "$ref": "#/$defs/AcceleratorBackend" + }, + "compiled": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "selected": { + "type": "boolean" + } + }, + "required": [ + "backend", + "compiled", + "available", + "selected", + "message" + ], + "type": "object" + }, + "AcceleratorStatus": { + "properties": { + "active": { + "$ref": "#/$defs/AcceleratorBackend" + }, + "available": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "probes": { + "items": { + "$ref": "#/$defs/AcceleratorProbe" + }, + "type": "array" + }, + "requested": { + "$ref": "#/$defs/AcceleratorBackend" + }, + "required": { + "type": "boolean" + } + }, + "required": [ + "requested", + "active", + "available", + "required", + "message", + "probes" + ], + "type": "object" + }, + "AdapterCategory": { + "enum": [ + "simulation", + "compatibility", + "mobile-base", + "drone", + "manipulator", + "perception", + "sensor" + ], + "type": "string" + }, + "AdapterMaturity": { + "enum": [ + "stable", + "beta", + "alpha", + "experimental" + ], + "type": "string" + }, + "AdapterProfile": { + "properties": { + "boundary": { + "type": "string" + }, + "capabilities": { + "items": { + "type": "string" + }, + "type": "array" + }, + "category": { + "$ref": "#/$defs/AdapterCategory" + }, + "feature_flags": { + "items": { + "type": "string" + }, + "type": "array" + }, + "maturity": { + "$ref": "#/$defs/AdapterMaturity" + }, + "required_gates": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "category", + "maturity", + "capabilities", + "feature_flags", + "required_gates", + "boundary" + ], + "type": "object" + }, + "CapabilityDescriptor": { + "properties": { + "description": { + "type": "string" + }, + "input_schema": true, + "module": { + "type": "string" + }, + "name": { + "type": "string" + }, + "output_schema": true, + "safety": { + "$ref": "#/$defs/SafetyClass" + } + }, + "required": [ + "name", + "description", + "module", + "safety", + "input_schema", + "output_schema" + ], + "type": "object" + }, + "ModuleHealth": { + "properties": { + "message": { + "type": [ + "string", + "null" + ] + }, + "ok": { + "type": "boolean" + }, + "state": { + "$ref": "#/$defs/ModuleState" + } + }, + "required": [ + "ok", + "state" + ], + "type": "object" + }, + "ModuleInfo": { + "properties": { + "capabilities": { + "items": { + "type": "string" + }, + "type": "array" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "health": { + "$ref": "#/$defs/ModuleHealth" + }, + "id": { + "format": "uint", + "minimum": 0, + "type": "integer" + }, + "inputs": { + "items": { + "$ref": "#/$defs/StreamDescriptor" + }, + "type": "array" + }, + "module_type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "outputs": { + "items": { + "$ref": "#/$defs/StreamDescriptor" + }, + "type": "array" + }, + "physical": { + "type": "boolean" + }, + "required": { + "type": "boolean" + }, + "state": { + "$ref": "#/$defs/ModuleState" + } + }, + "required": [ + "id", + "name", + "module_type", + "state", + "health", + "dependencies", + "inputs", + "outputs", + "capabilities", + "physical", + "required" + ], + "type": "object" + }, + "ModuleState": { + "enum": [ + "planned", + "starting", + "running", + "stopped", + "failed" + ], + "type": "string" + }, + "SafetyClass": { + "enum": [ + "observe-only", + "sim-control", + "physical-stop", + "physical-motion", + "physical-high-risk" + ], + "type": "string" + }, + "SpeedMode": { + "enum": [ + "low", + "medium", + "high" + ], + "type": "string" + }, + "StreamDescriptor": { + "properties": { + "direction": { + "$ref": "#/$defs/StreamDirection" + }, + "message_type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "transport": { + "type": "string" + } + }, + "required": [ + "name", + "direction", + "message_type", + "transport" + ], + "type": "object" + }, + "StreamDirection": { + "enum": [ + "input", + "output" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "accelerator": { + "$ref": "#/$defs/AcceleratorStatus" + }, + "adapter": { + "$ref": "#/$defs/AdapterProfile" + }, + "capabilities": { + "items": { + "$ref": "#/$defs/CapabilityDescriptor" + }, + "type": "array" + }, + "endpoints": { + "items": { + "type": "string" + }, + "type": "array" + }, + "mcp_tools": { + "items": { + "type": "string" + }, + "type": "array" + }, + "mode": { + "type": "string" + }, + "modules": { + "items": { + "$ref": "#/$defs/ModuleInfo" + }, + "type": "array" + }, + "ok": { + "type": "boolean" + }, + "physical": { + "type": "boolean" + }, + "profile": { + "type": "string" + }, + "replay": { + "type": "boolean" + }, + "role": { + "type": "string" + }, + "speed_modes": { + "items": { + "$ref": "#/$defs/SpeedMode" + }, + "type": "array" + }, + "stream_transport": { + "type": "string" + } + }, + "required": [ + "ok", + "mode", + "replay", + "role", + "profile", + "physical", + "adapter", + "stream_transport", + "endpoints", + "mcp_tools", + "speed_modes", + "accelerator", + "modules", + "capabilities" + ], + "title": "Capabilities", + "type": "object" + }, + "CapabilityDescriptor": { + "$defs": { + "SafetyClass": { + "enum": [ + "observe-only", + "sim-control", + "physical-stop", + "physical-motion", + "physical-high-risk" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "description": { + "type": "string" + }, + "input_schema": true, + "module": { + "type": "string" + }, + "name": { + "type": "string" + }, + "output_schema": true, + "safety": { + "$ref": "#/$defs/SafetyClass" + } + }, + "required": [ + "name", + "description", + "module", + "safety", + "input_schema", + "output_schema" + ], + "title": "CapabilityDescriptor", + "type": "object" + }, + "CaptureResult": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "byte_len": { + "format": "uint", + "minimum": 0, + "type": "integer" + }, + "captured_at_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "content_type": { + "type": "string" + }, + "ok": { + "type": "boolean" + }, + "sha256": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "ok", + "source", + "content_type", + "byte_len", + "captured_at_ms", + "sha256" + ], + "title": "CaptureResult", + "type": "object" + }, + "CommandOverlay": { + "$defs": { + "SpeedMode": { + "enum": [ + "low", + "medium", + "high" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "estop": { + "type": "boolean" + }, + "left_cmd": { + "format": "double", + "type": "number" + }, + "max_speed": { + "format": "double", + "type": "number" + }, + "right_cmd": { + "format": "double", + "type": "number" + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "left_cmd", + "right_cmd", + "speed_mode", + "max_speed", + "estop" + ], + "title": "CommandOverlay", + "type": "object" + }, + "CommandStreamState": { + "$defs": { + "SpeedMode": { + "enum": [ + "low", + "medium", + "high" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "left_cmd": { + "format": "double", + "type": "number" + }, + "max_speed": { + "format": "double", + "type": "number" + }, + "right_cmd": { + "format": "double", + "type": "number" + }, + "session_id": { + "type": [ + "string", + "null" + ] + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + } + }, + "required": [ + "left_cmd", + "right_cmd", + "speed_mode", + "max_speed" + ], + "title": "CommandStreamState", + "type": "object" + }, + "CostmapFrame": { + "$defs": { + "MapMetadata": { + "properties": { + "cell_order": { + "type": "string" + }, + "frame_id": { + "type": "string" + }, + "height": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "map_id": { + "type": "string" + }, + "origin": { + "$ref": "#/$defs/Pose2d" + }, + "resolution_m": { + "format": "double", + "type": "number" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width": { + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "map_id", + "frame_id", + "width", + "height", + "resolution_m", + "origin", + "cell_order" + ], + "type": "object" + }, + "Pose2d": { + "properties": { + "frame_id": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + }, + "yaw_rad": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "yaw_rad" + ], + "type": "object" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "costs": { + "items": { + "format": "uint8", + "maximum": 255, + "minimum": 0, + "type": "integer" + }, + "type": "array" + }, + "frame_id": { + "type": "string" + }, + "height": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "metadata": { + "$ref": "#/$defs/MapMetadata", + "default": { + "cell_order": "", + "frame_id": "", + "height": 0, + "map_id": "", + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + } + }, + "origin": { + "$ref": "#/$defs/Pose2d" + }, + "resolution_m": { + "format": "double", + "type": "number" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width": { + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "width", + "height", + "resolution_m", + "origin", + "costs" + ], + "title": "CostmapFrame", + "type": "object" + }, + "DetectionFrame": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "confidence": { + "format": "double", + "type": "number" + }, + "frame_id": { + "type": "string" + }, + "height_m": { + "format": "double", + "type": "number" + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width_m": { + "format": "double", + "type": "number" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "id", + "label", + "confidence", + "x_m", + "y_m", + "width_m", + "height_m" + ], + "title": "DetectionFrame", + "type": "object" + }, + "DriveOutcome": { + "$defs": { + "SpeedMode": { + "enum": [ + "low", + "medium", + "high" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "left": { + "format": "double", + "type": "number" + }, + "max_speed": { + "format": "double", + "type": "number" + }, + "ok": { + "type": "boolean" + }, + "right": { + "format": "double", + "type": "number" + }, + "soft_odometry_limited": { + "type": "boolean" + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + }, + "stopped_by_deadman": { + "type": "boolean" + } + }, + "required": [ + "ok", + "left", + "right", + "speed_mode", + "max_speed", + "stopped_by_deadman", + "soft_odometry_limited" + ], + "title": "DriveOutcome", + "type": "object" + }, + "DroneCommandStatus": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "args": { + "default": null + }, + "command": { + "type": "string" + }, + "mavlink_endpoint": { + "type": [ + "string", + "null" + ] + }, + "message": { + "type": "string" + }, + "ok": { + "type": "boolean" + }, + "profile": { + "type": "string" + }, + "simulated": { + "type": "boolean" + }, + "status": { + "type": "string" + } + }, + "required": [ + "ok", + "command", + "profile", + "simulated", + "status", + "message" + ], + "title": "DroneCommandStatus", + "type": "object" + }, + "Health": { + "$defs": { + "AcceleratorBackend": { + "enum": [ + "none", + "cpu", + "cuda" + ], + "type": "string" + }, + "AcceleratorProbe": { + "properties": { + "available": { + "type": "boolean" + }, + "backend": { + "$ref": "#/$defs/AcceleratorBackend" + }, + "compiled": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "selected": { + "type": "boolean" + } + }, + "required": [ + "backend", + "compiled", + "available", + "selected", + "message" + ], + "type": "object" + }, + "AcceleratorStatus": { + "properties": { + "active": { + "$ref": "#/$defs/AcceleratorBackend" + }, + "available": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "probes": { + "items": { + "$ref": "#/$defs/AcceleratorProbe" + }, + "type": "array" + }, + "requested": { + "$ref": "#/$defs/AcceleratorBackend" + }, + "required": { + "type": "boolean" + } + }, + "required": [ + "requested", + "active", + "available", + "required", + "message", + "probes" + ], + "type": "object" + }, + "ModuleHealth": { + "properties": { + "message": { + "type": [ + "string", + "null" + ] + }, + "ok": { + "type": "boolean" + }, + "state": { + "$ref": "#/$defs/ModuleState" + } + }, + "required": [ + "ok", + "state" + ], + "type": "object" + }, + "ModuleInfo": { + "properties": { + "capabilities": { + "items": { + "type": "string" + }, + "type": "array" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "health": { + "$ref": "#/$defs/ModuleHealth" + }, + "id": { + "format": "uint", + "minimum": 0, + "type": "integer" + }, + "inputs": { + "items": { + "$ref": "#/$defs/StreamDescriptor" + }, + "type": "array" + }, + "module_type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "outputs": { + "items": { + "$ref": "#/$defs/StreamDescriptor" + }, + "type": "array" + }, + "physical": { + "type": "boolean" + }, + "required": { + "type": "boolean" + }, + "state": { + "$ref": "#/$defs/ModuleState" + } + }, + "required": [ + "id", + "name", + "module_type", + "state", + "health", + "dependencies", + "inputs", + "outputs", + "capabilities", + "physical", + "required" + ], + "type": "object" + }, + "ModuleState": { + "enum": [ + "planned", + "starting", + "running", + "stopped", + "failed" + ], + "type": "string" + }, + "StreamDescriptor": { + "properties": { + "direction": { + "$ref": "#/$defs/StreamDirection" + }, + "message_type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "transport": { + "type": "string" + } + }, + "required": [ + "name", + "direction", + "message_type", + "transport" + ], + "type": "object" + }, + "StreamDirection": { + "enum": [ + "input", + "output" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "accelerator": { + "$ref": "#/$defs/AcceleratorStatus" + }, + "deadman_ok": { + "type": "boolean" + }, + "estop": { + "type": "boolean" + }, + "mode": { + "type": "string" + }, + "modules": { + "items": { + "$ref": "#/$defs/ModuleInfo" + }, + "type": "array" + }, + "ok": { + "type": "boolean" + }, + "physical_actuation_enabled": { + "type": "boolean" + }, + "profile": { + "type": "string" + }, + "replay": { + "type": "boolean" + }, + "role": { + "type": "string" + }, + "uptime_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "ok", + "mode", + "replay", + "role", + "profile", + "uptime_ms", + "estop", + "deadman_ok", + "physical_actuation_enabled", + "accelerator", + "modules" + ], + "title": "Health", + "type": "object" + }, + "ImageObservation": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "byte_len": { + "format": "uint", + "minimum": 0, + "type": "integer" + }, + "content_type": { + "type": "string" + }, + "frame_id": { + "type": "string" + }, + "height_px": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "sha256": { + "type": [ + "string", + "null" + ] + }, + "source": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width_px": { + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "source", + "width_px", + "height_px", + "content_type", + "byte_len" + ], + "title": "ImageObservation", + "type": "object" + }, + "InvocationOrigin": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "enum": [ + "runtime", + "cli", + "http", + "mcp", + "agent" + ], + "title": "InvocationOrigin", + "type": "string" + }, + "ManipulatorCommandStatus": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "args": { + "default": null + }, + "command": { + "type": "string" + }, + "message": { + "type": "string" + }, + "ok": { + "type": "boolean" + }, + "profile": { + "type": "string" + }, + "simulated": { + "type": "boolean" + }, + "status": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "required": [ + "version", + "ok", + "command", + "profile", + "simulated", + "status", + "message" + ], + "title": "ManipulatorCommandStatus", + "type": "object" + }, + "ManipulatorJoint": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "effort_nm": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "name": { + "type": "string" + }, + "position_rad": { + "format": "double", + "type": "number" + }, + "velocity_radps": { + "format": "double", + "type": "number" + } + }, + "required": [ + "name", + "position_rad", + "velocity_radps" + ], + "title": "ManipulatorJoint", + "type": "object" + }, + "ManipulatorJointState": { + "$defs": { + "ManipulatorJoint": { + "properties": { + "effort_nm": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "name": { + "type": "string" + }, + "position_rad": { + "format": "double", + "type": "number" + }, + "velocity_radps": { + "format": "double", + "type": "number" + } + }, + "required": [ + "name", + "position_rad", + "velocity_radps" + ], + "type": "object" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "joints": { + "items": { + "$ref": "#/$defs/ManipulatorJoint" + }, + "type": "array" + }, + "ok": { + "type": "boolean" + }, + "profile": { + "type": "string" + }, + "simulated": { + "type": "boolean" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "required": [ + "version", + "ok", + "profile", + "simulated", + "source", + "joints" + ], + "title": "ManipulatorJointState", + "type": "object" + }, + "MapMetadata": { + "$defs": { + "Pose2d": { + "properties": { + "frame_id": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + }, + "yaw_rad": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "yaw_rad" + ], + "type": "object" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "cell_order": { + "type": "string" + }, + "frame_id": { + "type": "string" + }, + "height": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "map_id": { + "type": "string" + }, + "origin": { + "$ref": "#/$defs/Pose2d" + }, + "resolution_m": { + "format": "double", + "type": "number" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width": { + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "map_id", + "frame_id", + "width", + "height", + "resolution_m", + "origin", + "cell_order" + ], + "title": "MapMetadata", + "type": "object" + }, + "McpCallResponse": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "ok": { + "type": "boolean" + }, + "result": true, + "tool": { + "type": "string" + } + }, + "required": [ + "ok", + "tool", + "result" + ], + "title": "McpCallResponse", + "type": "object" + }, + "McpModuleToolMap": { + "$defs": { + "McpModuleTools": { + "properties": { + "module": { + "type": "string" + }, + "module_type": { + "type": "string" + }, + "physical": { + "type": "boolean" + }, + "state": { + "$ref": "#/$defs/ModuleState" + }, + "tools": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "module", + "module_type", + "state", + "physical", + "tools" + ], + "type": "object" + }, + "ModuleState": { + "enum": [ + "planned", + "starting", + "running", + "stopped", + "failed" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "modules": { + "items": { + "$ref": "#/$defs/McpModuleTools" + }, + "type": "array" + }, + "ok": { + "type": "boolean" + } + }, + "required": [ + "ok", + "modules" + ], + "title": "McpModuleToolMap", + "type": "object" + }, + "McpStatus": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "module_count": { + "format": "uint", + "minimum": 0, + "type": "integer" + }, + "modules_healthy": { + "type": "boolean" + }, + "ok": { + "type": "boolean" + }, + "physical": { + "type": "boolean" + }, + "profile": { + "type": "string" + }, + "replay": { + "type": "boolean" + }, + "role": { + "type": "string" + }, + "tool_count": { + "format": "uint", + "minimum": 0, + "type": "integer" + }, + "transport": { + "type": "string" + } + }, + "required": [ + "ok", + "transport", + "role", + "profile", + "replay", + "physical", + "modules_healthy", + "module_count", + "tool_count" + ], + "title": "McpStatus", + "type": "object" + }, + "McpToolDescriptor": { + "$defs": { + "SafetyClass": { + "enum": [ + "observe-only", + "sim-control", + "physical-stop", + "physical-motion", + "physical-high-risk" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "description": { + "type": "string" + }, + "input_schema": true, + "module": { + "type": "string" + }, + "name": { + "type": "string" + }, + "output_schema": true, + "safety": { + "$ref": "#/$defs/SafetyClass" + } + }, + "required": [ + "name", + "description", + "module", + "safety", + "input_schema", + "output_schema" + ], + "title": "McpToolDescriptor", + "type": "object" + }, + "McpToolList": { + "$defs": { + "McpToolDescriptor": { + "properties": { + "description": { + "type": "string" + }, + "input_schema": true, + "module": { + "type": "string" + }, + "name": { + "type": "string" + }, + "output_schema": true, + "safety": { + "$ref": "#/$defs/SafetyClass" + } + }, + "required": [ + "name", + "description", + "module", + "safety", + "input_schema", + "output_schema" + ], + "type": "object" + }, + "SafetyClass": { + "enum": [ + "observe-only", + "sim-control", + "physical-stop", + "physical-motion", + "physical-high-risk" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "ok": { + "type": "boolean" + }, + "tools": { + "items": { + "$ref": "#/$defs/McpToolDescriptor" + }, + "type": "array" + } + }, + "required": [ + "ok", + "tools" + ], + "title": "McpToolList", + "type": "object" + }, + "ModuleGraph": { + "$defs": { + "ModuleHealth": { + "properties": { + "message": { + "type": [ + "string", + "null" + ] + }, + "ok": { + "type": "boolean" + }, + "state": { + "$ref": "#/$defs/ModuleState" + } + }, + "required": [ + "ok", + "state" + ], + "type": "object" + }, + "ModuleInfo": { + "properties": { + "capabilities": { + "items": { + "type": "string" + }, + "type": "array" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "health": { + "$ref": "#/$defs/ModuleHealth" + }, + "id": { + "format": "uint", + "minimum": 0, + "type": "integer" + }, + "inputs": { + "items": { + "$ref": "#/$defs/StreamDescriptor" + }, + "type": "array" + }, + "module_type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "outputs": { + "items": { + "$ref": "#/$defs/StreamDescriptor" + }, + "type": "array" + }, + "physical": { + "type": "boolean" + }, + "required": { + "type": "boolean" + }, + "state": { + "$ref": "#/$defs/ModuleState" + } + }, + "required": [ + "id", + "name", + "module_type", + "state", + "health", + "dependencies", + "inputs", + "outputs", + "capabilities", + "physical", + "required" + ], + "type": "object" + }, + "ModuleState": { + "enum": [ + "planned", + "starting", + "running", + "stopped", + "failed" + ], + "type": "string" + }, + "StreamDescriptor": { + "properties": { + "direction": { + "$ref": "#/$defs/StreamDirection" + }, + "message_type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "transport": { + "type": "string" + } + }, + "required": [ + "name", + "direction", + "message_type", + "transport" + ], + "type": "object" + }, + "StreamDirection": { + "enum": [ + "input", + "output" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "modules": { + "items": { + "$ref": "#/$defs/ModuleInfo" + }, + "type": "array" + } + }, + "required": [ + "modules" + ], + "title": "ModuleGraph", + "type": "object" + }, + "ModuleHealth": { + "$defs": { + "ModuleState": { + "enum": [ + "planned", + "starting", + "running", + "stopped", + "failed" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "message": { + "type": [ + "string", + "null" + ] + }, + "ok": { + "type": "boolean" + }, + "state": { + "$ref": "#/$defs/ModuleState" + } + }, + "required": [ + "ok", + "state" + ], + "title": "ModuleHealth", + "type": "object" + }, + "ModuleInfo": { + "$defs": { + "ModuleHealth": { + "properties": { + "message": { + "type": [ + "string", + "null" + ] + }, + "ok": { + "type": "boolean" + }, + "state": { + "$ref": "#/$defs/ModuleState" + } + }, + "required": [ + "ok", + "state" + ], + "type": "object" + }, + "ModuleState": { + "enum": [ + "planned", + "starting", + "running", + "stopped", + "failed" + ], + "type": "string" + }, + "StreamDescriptor": { + "properties": { + "direction": { + "$ref": "#/$defs/StreamDirection" + }, + "message_type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "transport": { + "type": "string" + } + }, + "required": [ + "name", + "direction", + "message_type", + "transport" + ], + "type": "object" + }, + "StreamDirection": { + "enum": [ + "input", + "output" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "capabilities": { + "items": { + "type": "string" + }, + "type": "array" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "health": { + "$ref": "#/$defs/ModuleHealth" + }, + "id": { + "format": "uint", + "minimum": 0, + "type": "integer" + }, + "inputs": { + "items": { + "$ref": "#/$defs/StreamDescriptor" + }, + "type": "array" + }, + "module_type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "outputs": { + "items": { + "$ref": "#/$defs/StreamDescriptor" + }, + "type": "array" + }, + "physical": { + "type": "boolean" + }, + "required": { + "type": "boolean" + }, + "state": { + "$ref": "#/$defs/ModuleState" + } + }, + "required": [ + "id", + "name", + "module_type", + "state", + "health", + "dependencies", + "inputs", + "outputs", + "capabilities", + "physical", + "required" + ], + "title": "ModuleInfo", + "type": "object" + }, + "ModuleState": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "enum": [ + "planned", + "starting", + "running", + "stopped", + "failed" + ], + "title": "ModuleState", + "type": "string" + }, + "OccupancyGridFrame": { + "$defs": { + "MapMetadata": { + "properties": { + "cell_order": { + "type": "string" + }, + "frame_id": { + "type": "string" + }, + "height": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "map_id": { + "type": "string" + }, + "origin": { + "$ref": "#/$defs/Pose2d" + }, + "resolution_m": { + "format": "double", + "type": "number" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width": { + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "map_id", + "frame_id", + "width", + "height", + "resolution_m", + "origin", + "cell_order" + ], + "type": "object" + }, + "Pose2d": { + "properties": { + "frame_id": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + }, + "yaw_rad": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "yaw_rad" + ], + "type": "object" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "cells": { + "items": { + "format": "int8", + "maximum": 127, + "minimum": -128, + "type": "integer" + }, + "type": "array" + }, + "frame_id": { + "type": "string" + }, + "height": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "metadata": { + "$ref": "#/$defs/MapMetadata", + "default": { + "cell_order": "", + "frame_id": "", + "height": 0, + "map_id": "", + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + } + }, + "origin": { + "$ref": "#/$defs/Pose2d" + }, + "resolution_m": { + "format": "double", + "type": "number" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width": { + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "width", + "height", + "resolution_m", + "origin", + "cells" + ], + "title": "OccupancyGridFrame", + "type": "object" + }, + "OdometryStatus": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "left_m": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "right_m": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "status": { + "type": "string" + } + }, + "required": [ + "status" + ], + "title": "OdometryStatus", + "type": "object" + }, + "PatrolStatus": { + "$defs": { + "PatrolStrategy": { + "enum": [ + "coverage", + "frontier", + "random" + ], + "type": "string" + }, + "PlannerGoal": { + "properties": { + "frame_id": { + "type": "string" + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + }, + "tolerance_m": { + "format": "double", + "type": "number" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "tolerance_m", + "speed_mode" + ], + "type": "object" + }, + "Pose2d": { + "properties": { + "frame_id": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + }, + "yaw_rad": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "yaw_rad" + ], + "type": "object" + }, + "SpeedMode": { + "enum": [ + "low", + "medium", + "high" + ], + "type": "string" + }, + "VisualizationPath": { + "properties": { + "frame_id": { + "type": "string" + }, + "poses": { + "items": { + "$ref": "#/$defs/Pose2d" + }, + "type": "array" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "poses" + ], + "type": "object" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "active": { + "type": "boolean" + }, + "goal": { + "anyOf": [ + { + "$ref": "#/$defs/PlannerGoal" + }, + { + "type": "null" + } + ] + }, + "message": { + "type": "string" + }, + "ok": { + "type": "boolean" + }, + "path": { + "$ref": "#/$defs/VisualizationPath" + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + }, + "status": { + "type": "string" + }, + "strategy": { + "anyOf": [ + { + "$ref": "#/$defs/PatrolStrategy" + }, + { + "type": "null" + } + ] + }, + "visited_cells": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "ok", + "active", + "status", + "message", + "speed_mode", + "path", + "visited_cells" + ], + "title": "PatrolStatus", + "type": "object" + }, + "PatrolStrategy": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "enum": [ + "coverage", + "frontier", + "random" + ], + "title": "PatrolStrategy", + "type": "string" + }, + "PlannerGoal": { + "$defs": { + "SpeedMode": { + "enum": [ + "low", + "medium", + "high" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "frame_id": { + "type": "string" + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + }, + "tolerance_m": { + "format": "double", + "type": "number" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "tolerance_m", + "speed_mode" + ], + "title": "PlannerGoal", + "type": "object" + }, + "PlannerStatus": { + "$defs": { + "DriveOutcome": { + "properties": { + "left": { + "format": "double", + "type": "number" + }, + "max_speed": { + "format": "double", + "type": "number" + }, + "ok": { + "type": "boolean" + }, + "right": { + "format": "double", + "type": "number" + }, + "soft_odometry_limited": { + "type": "boolean" + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + }, + "stopped_by_deadman": { + "type": "boolean" + } + }, + "required": [ + "ok", + "left", + "right", + "speed_mode", + "max_speed", + "stopped_by_deadman", + "soft_odometry_limited" + ], + "type": "object" + }, + "PlannerGoal": { + "properties": { + "frame_id": { + "type": "string" + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + }, + "tolerance_m": { + "format": "double", + "type": "number" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "tolerance_m", + "speed_mode" + ], + "type": "object" + }, + "Pose2d": { + "properties": { + "frame_id": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + }, + "yaw_rad": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "yaw_rad" + ], + "type": "object" + }, + "SpeedMode": { + "enum": [ + "low", + "medium", + "high" + ], + "type": "string" + }, + "VisualizationPath": { + "properties": { + "frame_id": { + "type": "string" + }, + "poses": { + "items": { + "$ref": "#/$defs/Pose2d" + }, + "type": "array" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "poses" + ], + "type": "object" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "active": { + "type": "boolean" + }, + "goal": { + "anyOf": [ + { + "$ref": "#/$defs/PlannerGoal" + }, + { + "type": "null" + } + ] + }, + "last_drive": { + "anyOf": [ + { + "$ref": "#/$defs/DriveOutcome" + }, + { + "type": "null" + } + ] + }, + "message": { + "type": "string" + }, + "ok": { + "type": "boolean" + }, + "path": { + "$ref": "#/$defs/VisualizationPath" + }, + "status": { + "type": "string" + } + }, + "required": [ + "ok", + "active", + "status", + "message", + "path" + ], + "title": "PlannerStatus", + "type": "object" + }, + "PointCloudMetadata": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "frame_id": { + "type": "string" + }, + "point_count": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "source": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "point_count", + "fields", + "source" + ], + "title": "PointCloudMetadata", + "type": "object" + }, + "Pose2d": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "frame_id": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + }, + "yaw_rad": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "yaw_rad" + ], + "title": "Pose2d", + "type": "object" + }, + "RawFrameStatus": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "last_ms": { + "format": "uint128", + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "source": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": [ + "status", + "source" + ], + "title": "RawFrameStatus", + "type": "object" + }, + "ResourceSample": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "cpu_time_ticks": { + "format": "uint64", + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "memory_rss_bytes": { + "format": "uint64", + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "process_id": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "sampled_at_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "sampled_at_ms", + "process_id" + ], + "title": "ResourceSample", + "type": "object" + }, + "RunLogEntry": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "event": { + "type": "string" + }, + "fields": { + "additionalProperties": true, + "type": "object" + }, + "level": { + "type": "string" + }, + "module": { + "type": "string" + }, + "run_id": { + "type": "string" + }, + "timestamp": { + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "timestamp", + "run_id", + "module", + "event", + "level", + "fields" + ], + "title": "RunLogEntry", + "type": "object" + }, + "SafetyClass": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "enum": [ + "observe-only", + "sim-control", + "physical-stop", + "physical-motion", + "physical-high-risk" + ], + "title": "SafetyClass", + "type": "string" + }, + "SafetyStreamState": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "deadman_ok": { + "type": "boolean" + }, + "estop": { + "type": "boolean" + }, + "physical_actuation_enabled": { + "type": "boolean" + }, + "soft_odometry_limit_m": { + "format": "double", + "type": "number" + }, + "soft_odometry_limited": { + "type": "boolean" + }, + "stopped_by_deadman": { + "type": "boolean" + } + }, + "required": [ + "estop", + "deadman_ok", + "stopped_by_deadman", + "soft_odometry_limited", + "soft_odometry_limit_m", + "physical_actuation_enabled" + ], + "title": "SafetyStreamState", + "type": "object" + }, + "SensorSnapshot": { + "$defs": { + "BatteryStatus": { + "properties": { + "status": { + "type": "string" + }, + "voltage_v": { + "format": "double", + "type": [ + "number", + "null" + ] + } + }, + "required": [ + "status" + ], + "type": "object" + }, + "CameraStatus": { + "properties": { + "health": { + "type": "string" + }, + "snapshot_url": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": "string" + }, + "stream_url": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "status", + "health" + ], + "type": "object" + }, + "OdometryStatus": { + "properties": { + "left_m": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "right_m": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "status": { + "type": "string" + } + }, + "required": [ + "status" + ], + "type": "object" + }, + "RawFrameStatus": { + "properties": { + "last_ms": { + "format": "uint128", + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "source": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": [ + "status", + "source" + ], + "type": "object" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "battery": { + "$ref": "#/$defs/BatteryStatus" + }, + "camera": { + "$ref": "#/$defs/CameraStatus" + }, + "odometry": { + "$ref": "#/$defs/OdometryStatus" + }, + "raw_frame": { + "$ref": "#/$defs/RawFrameStatus" + } + }, + "required": [ + "battery", + "odometry", + "camera", + "raw_frame" + ], + "title": "SensorSnapshot", + "type": "object" + }, + "SpatialMemoryEntry": { + "$defs": { + "SpatialMemoryKind": { + "enum": [ + "location", + "object" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "confidence": { + "format": "double", + "type": "number" + }, + "effective_confidence": { + "format": "double", + "type": "number" + }, + "frame_id": { + "type": "string" + }, + "kind": { + "$ref": "#/$defs/SpatialMemoryKind" + }, + "name": { + "type": "string" + }, + "observed_at_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "stale": { + "type": "boolean" + }, + "updated_at_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + } + }, + "required": [ + "name", + "kind", + "frame_id", + "x_m", + "y_m", + "observed_at_ms", + "updated_at_ms", + "confidence", + "effective_confidence", + "stale" + ], + "title": "SpatialMemoryEntry", + "type": "object" + }, + "SpatialMemoryKind": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "enum": [ + "location", + "object" + ], + "title": "SpatialMemoryKind", + "type": "string" + }, + "SpatialMemoryStatus": { + "$defs": { + "SpatialMemoryEntry": { + "properties": { + "confidence": { + "format": "double", + "type": "number" + }, + "effective_confidence": { + "format": "double", + "type": "number" + }, + "frame_id": { + "type": "string" + }, + "kind": { + "$ref": "#/$defs/SpatialMemoryKind" + }, + "name": { + "type": "string" + }, + "observed_at_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "stale": { + "type": "boolean" + }, + "updated_at_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + } + }, + "required": [ + "name", + "kind", + "frame_id", + "x_m", + "y_m", + "observed_at_ms", + "updated_at_ms", + "confidence", + "effective_confidence", + "stale" + ], + "type": "object" + }, + "SpatialMemoryKind": { + "enum": [ + "location", + "object" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "count": { + "format": "uint", + "minimum": 0, + "type": "integer" + }, + "entries": { + "items": { + "$ref": "#/$defs/SpatialMemoryEntry" + }, + "type": "array" + }, + "ok": { + "type": "boolean" + }, + "store_path": { + "type": "string" + } + }, + "required": [ + "ok", + "store_path", + "count", + "entries" + ], + "title": "SpatialMemoryStatus", + "type": "object" + }, + "SpeedMode": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "enum": [ + "low", + "medium", + "high" + ], + "title": "SpeedMode", + "type": "string" + }, + "StreamDescriptor": { + "$defs": { + "StreamDirection": { + "enum": [ + "input", + "output" + ], + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "direction": { + "$ref": "#/$defs/StreamDirection" + }, + "message_type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "transport": { + "type": "string" + } + }, + "required": [ + "name", + "direction", + "message_type", + "transport" + ], + "title": "StreamDescriptor", + "type": "object" + }, + "StreamDirection": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "enum": [ + "input", + "output" + ], + "title": "StreamDirection", + "type": "string" + }, + "TelemetryFrame": { + "$defs": { + "BatteryStatus": { + "properties": { + "status": { + "type": "string" + }, + "voltage_v": { + "format": "double", + "type": [ + "number", + "null" + ] + } + }, + "required": [ + "status" + ], + "type": "object" + }, + "CameraStatus": { + "properties": { + "health": { + "type": "string" + }, + "snapshot_url": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": "string" + }, + "stream_url": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "status", + "health" + ], + "type": "object" + }, + "DetectionFrame": { + "properties": { + "confidence": { + "format": "double", + "type": "number" + }, + "frame_id": { + "type": "string" + }, + "height_m": { + "format": "double", + "type": "number" + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width_m": { + "format": "double", + "type": "number" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "id", + "label", + "confidence", + "x_m", + "y_m", + "width_m", + "height_m" + ], + "type": "object" + }, + "OdometryStatus": { + "properties": { + "left_m": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "right_m": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "status": { + "type": "string" + } + }, + "required": [ + "status" + ], + "type": "object" + }, + "RawFrameStatus": { + "properties": { + "last_ms": { + "format": "uint128", + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "source": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": [ + "status", + "source" + ], + "type": "object" + }, + "ResourceSample": { + "properties": { + "cpu_time_ticks": { + "format": "uint64", + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "memory_rss_bytes": { + "format": "uint64", + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "process_id": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "sampled_at_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "sampled_at_ms", + "process_id" + ], + "type": "object" + }, + "SensorSnapshot": { + "properties": { + "battery": { + "$ref": "#/$defs/BatteryStatus" + }, + "camera": { + "$ref": "#/$defs/CameraStatus" + }, + "odometry": { + "$ref": "#/$defs/OdometryStatus" + }, + "raw_frame": { + "$ref": "#/$defs/RawFrameStatus" + } + }, + "required": [ + "battery", + "odometry", + "camera", + "raw_frame" + ], + "type": "object" + }, + "SpeedMode": { + "enum": [ + "low", + "medium", + "high" + ], + "type": "string" + }, + "VisionResult": { + "properties": { + "detections": { + "items": { + "$ref": "#/$defs/DetectionFrame" + }, + "type": "array" + }, + "duration_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "observed_at_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "ok": { + "type": "boolean" + }, + "source": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": [ + "ok", + "status", + "source", + "observed_at_ms", + "duration_ms", + "detections" + ], + "type": "object" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "battery_v": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "deadman_ok": { + "type": "boolean" + }, + "estop": { + "type": "boolean" + }, + "left_cmd": { + "format": "double", + "type": "number" + }, + "max_speed": { + "format": "double", + "type": "number" + }, + "odometry_left": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "odometry_right": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "profile": { + "type": "string" + }, + "resource": { + "anyOf": [ + { + "$ref": "#/$defs/ResourceSample" + }, + { + "type": "null" + } + ] + }, + "right_cmd": { + "format": "double", + "type": "number" + }, + "robot": { + "type": "string" + }, + "sensors": { + "$ref": "#/$defs/SensorSnapshot" + }, + "session_id": { + "type": [ + "string", + "null" + ] + }, + "soft_odometry_limit_m": { + "format": "double", + "type": "number" + }, + "soft_odometry_limited": { + "type": "boolean" + }, + "source": { + "type": "string" + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + }, + "stopped_by_deadman": { + "type": "boolean" + }, + "ts_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "vision": { + "$ref": "#/$defs/VisionResult", + "default": { + "detections": [], + "duration_ms": 0, + "error": null, + "observed_at_ms": 0, + "ok": false, + "source": "none", + "status": "unavailable" + } + } + }, + "required": [ + "ts_ms", + "robot", + "profile", + "left_cmd", + "right_cmd", + "deadman_ok", + "estop", + "stopped_by_deadman", + "soft_odometry_limited", + "soft_odometry_limit_m", + "speed_mode", + "max_speed", + "sensors", + "source" + ], + "title": "TelemetryFrame", + "type": "object" + }, + "TelemetryStreamFrame": { + "$defs": { + "AcceleratorBackend": { + "enum": [ + "none", + "cpu", + "cuda" + ], + "type": "string" + }, + "AcceleratorProbe": { + "properties": { + "available": { + "type": "boolean" + }, + "backend": { + "$ref": "#/$defs/AcceleratorBackend" + }, + "compiled": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "selected": { + "type": "boolean" + } + }, + "required": [ + "backend", + "compiled", + "available", + "selected", + "message" + ], + "type": "object" + }, + "AcceleratorStatus": { + "properties": { + "active": { + "$ref": "#/$defs/AcceleratorBackend" + }, + "available": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "probes": { + "items": { + "$ref": "#/$defs/AcceleratorProbe" + }, + "type": "array" + }, + "requested": { + "$ref": "#/$defs/AcceleratorBackend" + }, + "required": { + "type": "boolean" + } + }, + "required": [ + "requested", + "active", + "available", + "required", + "message", + "probes" + ], + "type": "object" + }, + "AutonomyOverlay": { + "properties": { + "active": { + "type": "boolean" + }, + "goal": { + "anyOf": [ + { + "$ref": "#/$defs/PlannerGoal" + }, + { + "type": "null" + } + ] + }, + "mode": { + "type": "string" + }, + "status": { + "type": "string" + }, + "strategy": { + "anyOf": [ + { + "$ref": "#/$defs/PatrolStrategy" + }, + { + "type": "null" + } + ] + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "visited_cells": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "mode", + "active", + "status", + "visited_cells" + ], + "type": "object" + }, + "BatteryStatus": { + "properties": { + "status": { + "type": "string" + }, + "voltage_v": { + "format": "double", + "type": [ + "number", + "null" + ] + } + }, + "required": [ + "status" + ], + "type": "object" + }, + "CameraStatus": { + "properties": { + "health": { + "type": "string" + }, + "snapshot_url": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": "string" + }, + "stream_url": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "status", + "health" + ], + "type": "object" + }, + "CommandOverlay": { + "properties": { + "estop": { + "type": "boolean" + }, + "left_cmd": { + "format": "double", + "type": "number" + }, + "max_speed": { + "format": "double", + "type": "number" + }, + "right_cmd": { + "format": "double", + "type": "number" + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "left_cmd", + "right_cmd", + "speed_mode", + "max_speed", + "estop" + ], + "type": "object" + }, + "CommandStreamState": { + "properties": { + "left_cmd": { + "format": "double", + "type": "number" + }, + "max_speed": { + "format": "double", + "type": "number" + }, + "right_cmd": { + "format": "double", + "type": "number" + }, + "session_id": { + "type": [ + "string", + "null" + ] + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + } + }, + "required": [ + "left_cmd", + "right_cmd", + "speed_mode", + "max_speed" + ], + "type": "object" + }, + "CostmapFrame": { + "properties": { + "costs": { + "items": { + "format": "uint8", + "maximum": 255, + "minimum": 0, + "type": "integer" + }, + "type": "array" + }, + "frame_id": { + "type": "string" + }, + "height": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "metadata": { + "$ref": "#/$defs/MapMetadata", + "default": { + "cell_order": "", + "frame_id": "", + "height": 0, + "map_id": "", + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + } + }, + "origin": { + "$ref": "#/$defs/Pose2d" + }, + "resolution_m": { + "format": "double", + "type": "number" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width": { + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "width", + "height", + "resolution_m", + "origin", + "costs" + ], + "type": "object" + }, + "DetectionFrame": { + "properties": { + "confidence": { + "format": "double", + "type": "number" + }, + "frame_id": { + "type": "string" + }, + "height_m": { + "format": "double", + "type": "number" + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width_m": { + "format": "double", + "type": "number" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "id", + "label", + "confidence", + "x_m", + "y_m", + "width_m", + "height_m" + ], + "type": "object" + }, + "Health": { + "properties": { + "accelerator": { + "$ref": "#/$defs/AcceleratorStatus" + }, + "deadman_ok": { + "type": "boolean" + }, + "estop": { + "type": "boolean" + }, + "mode": { + "type": "string" + }, + "modules": { + "items": { + "$ref": "#/$defs/ModuleInfo" + }, + "type": "array" + }, + "ok": { + "type": "boolean" + }, + "physical_actuation_enabled": { + "type": "boolean" + }, + "profile": { + "type": "string" + }, + "replay": { + "type": "boolean" + }, + "role": { + "type": "string" + }, + "uptime_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "ok", + "mode", + "replay", + "role", + "profile", + "uptime_ms", + "estop", + "deadman_ok", + "physical_actuation_enabled", + "accelerator", + "modules" + ], + "type": "object" + }, + "MapMetadata": { + "properties": { + "cell_order": { + "type": "string" + }, + "frame_id": { + "type": "string" + }, + "height": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "map_id": { + "type": "string" + }, + "origin": { + "$ref": "#/$defs/Pose2d" + }, + "resolution_m": { + "format": "double", + "type": "number" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width": { + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "map_id", + "frame_id", + "width", + "height", + "resolution_m", + "origin", + "cell_order" + ], + "type": "object" + }, + "ModuleHealth": { + "properties": { + "message": { + "type": [ + "string", + "null" + ] + }, + "ok": { + "type": "boolean" + }, + "state": { + "$ref": "#/$defs/ModuleState" + } + }, + "required": [ + "ok", + "state" + ], + "type": "object" + }, + "ModuleInfo": { + "properties": { + "capabilities": { + "items": { + "type": "string" + }, + "type": "array" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "health": { + "$ref": "#/$defs/ModuleHealth" + }, + "id": { + "format": "uint", + "minimum": 0, + "type": "integer" + }, + "inputs": { + "items": { + "$ref": "#/$defs/StreamDescriptor" + }, + "type": "array" + }, + "module_type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "outputs": { + "items": { + "$ref": "#/$defs/StreamDescriptor" + }, + "type": "array" + }, + "physical": { + "type": "boolean" + }, + "required": { + "type": "boolean" + }, + "state": { + "$ref": "#/$defs/ModuleState" + } + }, + "required": [ + "id", + "name", + "module_type", + "state", + "health", + "dependencies", + "inputs", + "outputs", + "capabilities", + "physical", + "required" + ], + "type": "object" + }, + "ModuleState": { + "enum": [ + "planned", + "starting", + "running", + "stopped", + "failed" + ], + "type": "string" + }, + "OccupancyGridFrame": { + "properties": { + "cells": { + "items": { + "format": "int8", + "maximum": 127, + "minimum": -128, + "type": "integer" + }, + "type": "array" + }, + "frame_id": { + "type": "string" + }, + "height": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "metadata": { + "$ref": "#/$defs/MapMetadata", + "default": { + "cell_order": "", + "frame_id": "", + "height": 0, + "map_id": "", + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + } + }, + "origin": { + "$ref": "#/$defs/Pose2d" + }, + "resolution_m": { + "format": "double", + "type": "number" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width": { + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "width", + "height", + "resolution_m", + "origin", + "cells" + ], + "type": "object" + }, + "OdometryStatus": { + "properties": { + "left_m": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "right_m": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "status": { + "type": "string" + } + }, + "required": [ + "status" + ], + "type": "object" + }, + "PatrolStrategy": { + "enum": [ + "coverage", + "frontier", + "random" + ], + "type": "string" + }, + "PlannerGoal": { + "properties": { + "frame_id": { + "type": "string" + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + }, + "tolerance_m": { + "format": "double", + "type": "number" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "tolerance_m", + "speed_mode" + ], + "type": "object" + }, + "PointCloudMetadata": { + "properties": { + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "frame_id": { + "type": "string" + }, + "point_count": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "source": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "point_count", + "fields", + "source" + ], + "type": "object" + }, + "Pose2d": { + "properties": { + "frame_id": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + }, + "yaw_rad": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "yaw_rad" + ], + "type": "object" + }, + "RawFrameStatus": { + "properties": { + "last_ms": { + "format": "uint128", + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "source": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": [ + "status", + "source" + ], + "type": "object" + }, + "ResourceSample": { + "properties": { + "cpu_time_ticks": { + "format": "uint64", + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "memory_rss_bytes": { + "format": "uint64", + "minimum": 0, + "type": [ + "integer", + "null" + ] + }, + "process_id": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "sampled_at_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "sampled_at_ms", + "process_id" + ], + "type": "object" + }, + "SafetyStreamState": { + "properties": { + "deadman_ok": { + "type": "boolean" + }, + "estop": { + "type": "boolean" + }, + "physical_actuation_enabled": { + "type": "boolean" + }, + "soft_odometry_limit_m": { + "format": "double", + "type": "number" + }, + "soft_odometry_limited": { + "type": "boolean" + }, + "stopped_by_deadman": { + "type": "boolean" + } + }, + "required": [ + "estop", + "deadman_ok", + "stopped_by_deadman", + "soft_odometry_limited", + "soft_odometry_limit_m", + "physical_actuation_enabled" + ], + "type": "object" + }, + "SensorSnapshot": { + "properties": { + "battery": { + "$ref": "#/$defs/BatteryStatus" + }, + "camera": { + "$ref": "#/$defs/CameraStatus" + }, + "odometry": { + "$ref": "#/$defs/OdometryStatus" + }, + "raw_frame": { + "$ref": "#/$defs/RawFrameStatus" + } + }, + "required": [ + "battery", + "odometry", + "camera", + "raw_frame" + ], + "type": "object" + }, + "SpeedMode": { + "enum": [ + "low", + "medium", + "high" + ], + "type": "string" + }, + "StreamDescriptor": { + "properties": { + "direction": { + "$ref": "#/$defs/StreamDirection" + }, + "message_type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "transport": { + "type": "string" + } + }, + "required": [ + "name", + "direction", + "message_type", + "transport" + ], + "type": "object" + }, + "StreamDirection": { + "enum": [ + "input", + "output" + ], + "type": "string" + }, + "TelemetryFrame": { + "properties": { + "battery_v": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "deadman_ok": { + "type": "boolean" + }, + "estop": { + "type": "boolean" + }, + "left_cmd": { + "format": "double", + "type": "number" + }, + "max_speed": { + "format": "double", + "type": "number" + }, + "odometry_left": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "odometry_right": { + "format": "double", + "type": [ + "number", + "null" + ] + }, + "profile": { + "type": "string" + }, + "resource": { + "anyOf": [ + { + "$ref": "#/$defs/ResourceSample" + }, + { + "type": "null" + } + ] + }, + "right_cmd": { + "format": "double", + "type": "number" + }, + "robot": { + "type": "string" + }, + "sensors": { + "$ref": "#/$defs/SensorSnapshot" + }, + "session_id": { + "type": [ + "string", + "null" + ] + }, + "soft_odometry_limit_m": { + "format": "double", + "type": "number" + }, + "soft_odometry_limited": { + "type": "boolean" + }, + "source": { + "type": "string" + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + }, + "stopped_by_deadman": { + "type": "boolean" + }, + "ts_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "vision": { + "$ref": "#/$defs/VisionResult", + "default": { + "detections": [], + "duration_ms": 0, + "error": null, + "observed_at_ms": 0, + "ok": false, + "source": "none", + "status": "unavailable" + } + } + }, + "required": [ + "ts_ms", + "robot", + "profile", + "left_cmd", + "right_cmd", + "deadman_ok", + "estop", + "stopped_by_deadman", + "soft_odometry_limited", + "soft_odometry_limit_m", + "speed_mode", + "max_speed", + "sensors", + "source" + ], + "type": "object" + }, + "Twist2d": { + "properties": { + "angular_z_radps": { + "format": "double", + "type": "number" + }, + "frame_id": { + "type": "string" + }, + "linear_x_mps": { + "format": "double", + "type": "number" + }, + "linear_y_mps": { + "format": "double", + "type": "number" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "linear_x_mps", + "linear_y_mps", + "angular_z_radps" + ], + "type": "object" + }, + "VisionResult": { + "properties": { + "detections": { + "items": { + "$ref": "#/$defs/DetectionFrame" + }, + "type": "array" + }, + "duration_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "observed_at_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "ok": { + "type": "boolean" + }, + "source": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": [ + "ok", + "status", + "source", + "observed_at_ms", + "duration_ms", + "detections" + ], + "type": "object" + }, + "VisualizationFrame": { + "properties": { + "autonomy": { + "$ref": "#/$defs/AutonomyOverlay", + "default": { + "active": false, + "goal": null, + "mode": "idle", + "status": "idle", + "strategy": null, + "ts_ms": 0, + "visited_cells": [] + } + }, + "command": { + "$ref": "#/$defs/CommandOverlay" + }, + "costmap": { + "$ref": "#/$defs/CostmapFrame", + "default": { + "costs": [], + "frame_id": "", + "height": 0, + "metadata": { + "cell_order": "", + "frame_id": "", + "height": 0, + "map_id": "", + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + }, + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + } + }, + "detections": { + "items": { + "$ref": "#/$defs/DetectionFrame" + }, + "type": "array" + }, + "map": { + "$ref": "#/$defs/MapMetadata", + "default": { + "cell_order": "", + "frame_id": "", + "height": 0, + "map_id": "", + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + } + }, + "occupancy_grid": { + "$ref": "#/$defs/OccupancyGridFrame" + }, + "path": { + "$ref": "#/$defs/VisualizationPath" + }, + "point_cloud": { + "$ref": "#/$defs/PointCloudMetadata" + }, + "pose": { + "$ref": "#/$defs/Pose2d" + }, + "profile": { + "type": "string" + }, + "robot": { + "type": "string" + }, + "ts_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "twist": { + "$ref": "#/$defs/Twist2d", + "default": { + "angular_z_radps": 0.0, + "frame_id": "", + "linear_x_mps": 0.0, + "linear_y_mps": 0.0, + "ts_ms": 0 + } + }, + "version": { + "type": "string" + } + }, + "required": [ + "version", + "ts_ms", + "robot", + "profile", + "pose", + "path", + "occupancy_grid", + "point_cloud", + "detections", + "command" + ], + "type": "object" + }, + "VisualizationPath": { + "properties": { + "frame_id": { + "type": "string" + }, + "poses": { + "items": { + "$ref": "#/$defs/Pose2d" + }, + "type": "array" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "poses" + ], + "type": "object" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "command": { + "$ref": "#/$defs/CommandStreamState" + }, + "health": { + "$ref": "#/$defs/Health" + }, + "kind": { + "type": "string" + }, + "safety": { + "$ref": "#/$defs/SafetyStreamState" + }, + "telemetry": { + "$ref": "#/$defs/TelemetryFrame" + }, + "ts_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "visualization": { + "$ref": "#/$defs/VisualizationFrame", + "default": { + "autonomy": { + "active": false, + "goal": null, + "mode": "idle", + "status": "idle", + "strategy": null, + "ts_ms": 0, + "visited_cells": [] + }, + "command": { + "estop": false, + "left_cmd": 0.0, + "max_speed": 0.0, + "right_cmd": 0.0, + "speed_mode": "medium", + "ts_ms": 0 + }, + "costmap": { + "costs": [], + "frame_id": "", + "height": 0, + "metadata": { + "cell_order": "", + "frame_id": "", + "height": 0, + "map_id": "", + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + }, + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + }, + "detections": [], + "map": { + "cell_order": "", + "frame_id": "", + "height": 0, + "map_id": "", + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + }, + "occupancy_grid": { + "cells": [], + "frame_id": "", + "height": 0, + "metadata": { + "cell_order": "", + "frame_id": "", + "height": 0, + "map_id": "", + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + }, + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + }, + "path": { + "frame_id": "", + "poses": [], + "ts_ms": 0 + }, + "point_cloud": { + "fields": [], + "frame_id": "", + "point_count": 0, + "source": "", + "ts_ms": 0 + }, + "pose": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "profile": "", + "robot": "", + "ts_ms": 0, + "twist": { + "angular_z_radps": 0.0, + "frame_id": "", + "linear_x_mps": 0.0, + "linear_y_mps": 0.0, + "ts_ms": 0 + }, + "version": "leash-visualization-v1" + } + } + }, + "required": [ + "kind", + "ts_ms", + "telemetry", + "health", + "command", + "safety" + ], + "title": "TelemetryStreamFrame", + "type": "object" + }, + "Twist2d": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "angular_z_radps": { + "format": "double", + "type": "number" + }, + "frame_id": { + "type": "string" + }, + "linear_x_mps": { + "format": "double", + "type": "number" + }, + "linear_y_mps": { + "format": "double", + "type": "number" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "linear_x_mps", + "linear_y_mps", + "angular_z_radps" + ], + "title": "Twist2d", + "type": "object" + }, + "VisionResult": { + "$defs": { + "DetectionFrame": { + "properties": { + "confidence": { + "format": "double", + "type": "number" + }, + "frame_id": { + "type": "string" + }, + "height_m": { + "format": "double", + "type": "number" + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width_m": { + "format": "double", + "type": "number" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "id", + "label", + "confidence", + "x_m", + "y_m", + "width_m", + "height_m" + ], + "type": "object" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "detections": { + "items": { + "$ref": "#/$defs/DetectionFrame" + }, + "type": "array" + }, + "duration_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "observed_at_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "ok": { + "type": "boolean" + }, + "source": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": [ + "ok", + "status", + "source", + "observed_at_ms", + "duration_ms", + "detections" + ], + "title": "VisionResult", + "type": "object" + }, + "VisualizationFrame": { + "$defs": { + "AutonomyOverlay": { + "properties": { + "active": { + "type": "boolean" + }, + "goal": { + "anyOf": [ + { + "$ref": "#/$defs/PlannerGoal" + }, + { + "type": "null" + } + ] + }, + "mode": { + "type": "string" + }, + "status": { + "type": "string" + }, + "strategy": { + "anyOf": [ + { + "$ref": "#/$defs/PatrolStrategy" + }, + { + "type": "null" + } + ] + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "visited_cells": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "mode", + "active", + "status", + "visited_cells" + ], + "type": "object" + }, + "CommandOverlay": { + "properties": { + "estop": { + "type": "boolean" + }, + "left_cmd": { + "format": "double", + "type": "number" + }, + "max_speed": { + "format": "double", + "type": "number" + }, + "right_cmd": { + "format": "double", + "type": "number" + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "left_cmd", + "right_cmd", + "speed_mode", + "max_speed", + "estop" + ], + "type": "object" + }, + "CostmapFrame": { + "properties": { + "costs": { + "items": { + "format": "uint8", + "maximum": 255, + "minimum": 0, + "type": "integer" + }, + "type": "array" + }, + "frame_id": { + "type": "string" + }, + "height": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "metadata": { + "$ref": "#/$defs/MapMetadata", + "default": { + "cell_order": "", + "frame_id": "", + "height": 0, + "map_id": "", + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + } + }, + "origin": { + "$ref": "#/$defs/Pose2d" + }, + "resolution_m": { + "format": "double", + "type": "number" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width": { + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "width", + "height", + "resolution_m", + "origin", + "costs" + ], + "type": "object" + }, + "DetectionFrame": { + "properties": { + "confidence": { + "format": "double", + "type": "number" + }, + "frame_id": { + "type": "string" + }, + "height_m": { + "format": "double", + "type": "number" + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width_m": { + "format": "double", + "type": "number" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "id", + "label", + "confidence", + "x_m", + "y_m", + "width_m", + "height_m" + ], + "type": "object" + }, + "MapMetadata": { + "properties": { + "cell_order": { + "type": "string" + }, + "frame_id": { + "type": "string" + }, + "height": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "map_id": { + "type": "string" + }, + "origin": { + "$ref": "#/$defs/Pose2d" + }, + "resolution_m": { + "format": "double", + "type": "number" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width": { + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "map_id", + "frame_id", + "width", + "height", + "resolution_m", + "origin", + "cell_order" + ], + "type": "object" + }, + "OccupancyGridFrame": { + "properties": { + "cells": { + "items": { + "format": "int8", + "maximum": 127, + "minimum": -128, + "type": "integer" + }, + "type": "array" + }, + "frame_id": { + "type": "string" + }, + "height": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "metadata": { + "$ref": "#/$defs/MapMetadata", + "default": { + "cell_order": "", + "frame_id": "", + "height": 0, + "map_id": "", + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + } + }, + "origin": { + "$ref": "#/$defs/Pose2d" + }, + "resolution_m": { + "format": "double", + "type": "number" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "width": { + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "width", + "height", + "resolution_m", + "origin", + "cells" + ], + "type": "object" + }, + "PatrolStrategy": { + "enum": [ + "coverage", + "frontier", + "random" + ], + "type": "string" + }, + "PlannerGoal": { + "properties": { + "frame_id": { + "type": "string" + }, + "speed_mode": { + "$ref": "#/$defs/SpeedMode" + }, + "tolerance_m": { + "format": "double", + "type": "number" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "tolerance_m", + "speed_mode" + ], + "type": "object" + }, + "PointCloudMetadata": { + "properties": { + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "frame_id": { + "type": "string" + }, + "point_count": { + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "source": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "point_count", + "fields", + "source" + ], + "type": "object" + }, + "Pose2d": { + "properties": { + "frame_id": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + }, + "yaw_rad": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "yaw_rad" + ], + "type": "object" + }, + "SpeedMode": { + "enum": [ + "low", + "medium", + "high" + ], + "type": "string" + }, + "Twist2d": { + "properties": { + "angular_z_radps": { + "format": "double", + "type": "number" + }, + "frame_id": { + "type": "string" + }, + "linear_x_mps": { + "format": "double", + "type": "number" + }, + "linear_y_mps": { + "format": "double", + "type": "number" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "linear_x_mps", + "linear_y_mps", + "angular_z_radps" + ], + "type": "object" + }, + "VisualizationPath": { + "properties": { + "frame_id": { + "type": "string" + }, + "poses": { + "items": { + "$ref": "#/$defs/Pose2d" + }, + "type": "array" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "poses" + ], + "type": "object" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "autonomy": { + "$ref": "#/$defs/AutonomyOverlay", + "default": { + "active": false, + "goal": null, + "mode": "idle", + "status": "idle", + "strategy": null, + "ts_ms": 0, + "visited_cells": [] + } + }, + "command": { + "$ref": "#/$defs/CommandOverlay" + }, + "costmap": { + "$ref": "#/$defs/CostmapFrame", + "default": { + "costs": [], + "frame_id": "", + "height": 0, + "metadata": { + "cell_order": "", + "frame_id": "", + "height": 0, + "map_id": "", + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + }, + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + } + }, + "detections": { + "items": { + "$ref": "#/$defs/DetectionFrame" + }, + "type": "array" + }, + "map": { + "$ref": "#/$defs/MapMetadata", + "default": { + "cell_order": "", + "frame_id": "", + "height": 0, + "map_id": "", + "origin": { + "frame_id": "", + "ts_ms": 0, + "x_m": 0.0, + "y_m": 0.0, + "yaw_rad": 0.0 + }, + "resolution_m": 0.0, + "ts_ms": 0, + "width": 0 + } + }, + "occupancy_grid": { + "$ref": "#/$defs/OccupancyGridFrame" + }, + "path": { + "$ref": "#/$defs/VisualizationPath" + }, + "point_cloud": { + "$ref": "#/$defs/PointCloudMetadata" + }, + "pose": { + "$ref": "#/$defs/Pose2d" + }, + "profile": { + "type": "string" + }, + "robot": { + "type": "string" + }, + "ts_ms": { + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "twist": { + "$ref": "#/$defs/Twist2d", + "default": { + "angular_z_radps": 0.0, + "frame_id": "", + "linear_x_mps": 0.0, + "linear_y_mps": 0.0, + "ts_ms": 0 + } + }, + "version": { + "type": "string" + } + }, + "required": [ + "version", + "ts_ms", + "robot", + "profile", + "pose", + "path", + "occupancy_grid", + "point_cloud", + "detections", + "command" + ], + "title": "VisualizationFrame", + "type": "object" + }, + "VisualizationPath": { + "$defs": { + "Pose2d": { + "properties": { + "frame_id": { + "type": "string" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + }, + "x_m": { + "format": "double", + "type": "number" + }, + "y_m": { + "format": "double", + "type": "number" + }, + "yaw_rad": { + "format": "double", + "type": "number" + } + }, + "required": [ + "frame_id", + "x_m", + "y_m", + "yaw_rad" + ], + "type": "object" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "frame_id": { + "type": "string" + }, + "poses": { + "items": { + "$ref": "#/$defs/Pose2d" + }, + "type": "array" + }, + "ts_ms": { + "default": 0, + "format": "uint128", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "frame_id", + "poses" + ], + "title": "VisualizationPath", + "type": "object" + } + }, + "title": "Leash message schemas" +} diff --git a/scripts/README.md b/scripts/README.md index 30f6f42..280e4ec 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -4,7 +4,8 @@ This folder contains shell smoke tests and the bot installer. Scripts are intend ```mermaid flowchart TB - all["smoke-all.sh\naggregate no-hardware release proof"] --> http["smoke-http.sh\nHTTP routes, telemetry streams, policy denial"] + all["smoke-all.sh\naggregate no-hardware release proof"] --> schema["leash-schema --check\nchecked-in JSON Schemas"] + all --> http["smoke-http.sh\nHTTP routes, telemetry streams, policy denial"] all --> mcp["smoke-mcp.sh\nstdio MCP initialize, tools, health"] all --> mcphttp["smoke-mcp-http.sh\nlocalhost MCP HTTP, CLI, planner, and patrol calls"] all --> replayhttp["smoke-replay-http.sh\nHTTP replay observe"] @@ -17,8 +18,8 @@ flowchart TB ## Files - `install-bot.sh`: builds a Leash binary and installs a user `systemd` service plus env file on a bot host. -- `smoke-all.sh`: aggregate no-hardware release proof; CI runs this. -- `smoke-http.sh`: HTTP, WebSocket/SSE, visualization frame, map/costmap contracts, agent input, capture, drive, and policy checks. +- `smoke-all.sh`: aggregate no-hardware release proof; CI runs this and checks generated schemas. +- `smoke-http.sh`: HTTP, WebSocket/SSE, visualization frame, map/costmap contracts, external clients, agent input, capture, drive, and policy checks. - `smoke-mcp.sh`: stdio MCP initialization and tool calls. - `smoke-mcp-http.sh`: localhost MCP HTTP routes, `leash mcp` CLI calls, sim planner set/status calls, and sim patrol start/status/stop calls. - `smoke-replay-http.sh`: replay mode over HTTP. diff --git a/scripts/smoke-all.sh b/scripts/smoke-all.sh index 301fd88..f46a53c 100755 --- a/scripts/smoke-all.sh +++ b/scripts/smoke-all.sh @@ -33,10 +33,15 @@ for (const key of [ } const checks = [ + { + name: "message-schemas-current", + argv: ["cargo", "run", "--quiet", "--features", "mcp", "--bin", "leash-schema", "--", "--check"], + proof: "checked-in JSON Schemas matched Rust wire message types", + }, { name: "http-routes-and-policy", argv: ["bash", "scripts/smoke-http.sh"], - proof: "HTTP routes, WebSocket/SSE telemetry with visualization map/costmap frames, agent input, capture, authorized drive, and drive-denial policy passed", + proof: "HTTP routes, WebSocket/SSE telemetry with visualization map/costmap frames, external clients, agent input, capture, authorized drive, and drive-denial policy passed", }, { name: "mcp-stdio", diff --git a/scripts/smoke-http.sh b/scripts/smoke-http.sh index 878d7e6..623fe40 100755 --- a/scripts/smoke-http.sh +++ b/scripts/smoke-http.sh @@ -264,6 +264,15 @@ assert_sse_telemetry() { NODE } +assert_client_example_summary() { + local runtime="$1" + RUNTIME="$runtime" node -e 'const payload = JSON.parse(require("node:fs").readFileSync(0, "utf8")); +if (payload.ok !== true) throw new Error("client example ok was not true"); +if (payload.runtime !== process.env.RUNTIME) throw new Error(`unexpected client runtime: ${payload.runtime}`); +if (payload.profile !== "sim") throw new Error(`unexpected client profile: ${payload.profile}`); +if (!payload.stop || payload.stop.ok !== true) throw new Error("client stop response was missing");' +} + curl -fsS "$base/health" | assert_health_modules curl -fsS "$base/capabilities" | assert_capabilities_streams curl -fsS "$base/modules" | parse_json @@ -310,5 +319,7 @@ curl -fsS -X POST "$base/drive" \ -H "content-type: application/json" \ --data '{"token":"smoke-token","left":0.2,"right":0.2}' | assert_drive_outcome curl -fsS -X POST "$base/motors/stop" | parse_json +LEASH_URL="$base" python3 examples/clients/python/http_client.py | assert_client_example_summary python +LEASH_URL="$base" node examples/clients/node/http-client.mjs | assert_client_example_summary node echo "http smoke ok: $base" diff --git a/src/bin/leash-schema.rs b/src/bin/leash-schema.rs new file mode 100644 index 0000000..25cfc44 --- /dev/null +++ b/src/bin/leash-schema.rs @@ -0,0 +1,168 @@ +use std::{collections::BTreeMap, env, fs, path::PathBuf, process}; + +use anyhow::{anyhow, Context, Result}; +use schemars::{schema_for, JsonSchema}; +use serde_json::{json, Value}; + +use leash_harness::{ + capability::{CapabilityDescriptor, InvocationOrigin, SafetyClass}, + mcp::{McpCallResponse, McpModuleToolMap, McpStatus, McpToolDescriptor, McpToolList}, + module::{ + ModuleGraph, ModuleHealth, ModuleInfo, ModuleState, StreamDescriptor, StreamDirection, + }, + stack::{AdapterCategory, AdapterMaturity, AdapterProfile}, + types::{ + AgentMessage, AgentMessageAck, AgentMessageList, AgentModelResponse, AutonomyOverlay, + BatteryStatus, CameraStatus, Capabilities, CaptureResult, CommandOverlay, + CommandStreamState, CostmapFrame, DetectionFrame, DriveOutcome, DroneCommandStatus, Health, + ImageObservation, ManipulatorCommandStatus, ManipulatorJoint, ManipulatorJointState, + MapMetadata, OccupancyGridFrame, OdometryStatus, PatrolStatus, PatrolStrategy, PlannerGoal, + PlannerStatus, PointCloudMetadata, Pose2d, RawFrameStatus, ResourceSample, RunLogEntry, + SafetyStreamState, SensorSnapshot, SpatialMemoryEntry, SpatialMemoryKind, + SpatialMemoryStatus, SpeedMode, TelemetryFrame, TelemetryStreamFrame, Twist2d, + VisionResult, VisualizationFrame, VisualizationPath, + }, +}; + +const DEFAULT_OUTPUT: &str = "schemas/leash-messages.schema.json"; +const SCHEMA_VERSION: &str = "leash-message-schema-v1"; +const SCHEMA_ID: &str = "https://specdog.github.io/leash/schemas/leash-messages.schema.json"; + +#[derive(Debug)] +struct Args { + output: PathBuf, + check: bool, +} + +fn main() -> Result<()> { + let args = parse_args()?; + let rendered = format!("{}\n", serde_json::to_string_pretty(&schema_document()?)?); + + if args.check { + let current = fs::read_to_string(&args.output) + .with_context(|| format!("read {}", args.output.display()))?; + if current != rendered { + eprintln!( + "{} is stale; run `cargo run --features mcp --bin leash-schema -- --output {}`", + args.output.display(), + args.output.display() + ); + process::exit(1); + } + println!("schema current: {}", args.output.display()); + return Ok(()); + } + + if let Some(parent) = args.output.parent() { + fs::create_dir_all(parent).with_context(|| format!("create {}", parent.display()))?; + } + fs::write(&args.output, rendered) + .with_context(|| format!("write {}", args.output.display()))?; + println!("wrote {}", args.output.display()); + Ok(()) +} + +fn parse_args() -> Result { + let mut output = PathBuf::from(DEFAULT_OUTPUT); + let mut check = false; + let mut args = env::args().skip(1); + + while let Some(arg) = args.next() { + match arg.as_str() { + "--check" => check = true, + "--output" | "-o" => { + let value = args + .next() + .ok_or_else(|| anyhow!("{arg} requires a path argument"))?; + output = PathBuf::from(value); + } + "--help" | "-h" => { + println!("Usage: leash-schema [--check] [--output PATH]"); + process::exit(0); + } + other => return Err(anyhow!("unknown argument '{other}'")), + } + } + + Ok(Args { output, check }) +} + +fn schema_document() -> Result { + let mut schemas = BTreeMap::new(); + + insert::(&mut schemas, "Health")?; + insert::(&mut schemas, "Capabilities")?; + insert::(&mut schemas, "TelemetryFrame")?; + insert::(&mut schemas, "TelemetryStreamFrame")?; + insert::(&mut schemas, "CommandStreamState")?; + insert::(&mut schemas, "SafetyStreamState")?; + insert::(&mut schemas, "SensorSnapshot")?; + insert::(&mut schemas, "BatteryStatus")?; + insert::(&mut schemas, "OdometryStatus")?; + insert::(&mut schemas, "CameraStatus")?; + insert::(&mut schemas, "RawFrameStatus")?; + insert::(&mut schemas, "SpeedMode")?; + insert::(&mut schemas, "ResourceSample")?; + insert::(&mut schemas, "RunLogEntry")?; + insert::(&mut schemas, "AgentMessage")?; + insert::(&mut schemas, "AgentMessageAck")?; + insert::(&mut schemas, "AgentMessageList")?; + insert::(&mut schemas, "AgentModelResponse")?; + insert::(&mut schemas, "ModuleGraph")?; + insert::(&mut schemas, "ModuleInfo")?; + insert::(&mut schemas, "ModuleHealth")?; + insert::(&mut schemas, "ModuleState")?; + insert::(&mut schemas, "StreamDescriptor")?; + insert::(&mut schemas, "StreamDirection")?; + insert::(&mut schemas, "CapabilityDescriptor")?; + insert::(&mut schemas, "SafetyClass")?; + insert::(&mut schemas, "InvocationOrigin")?; + insert::(&mut schemas, "CaptureResult")?; + insert::(&mut schemas, "DriveOutcome")?; + insert::(&mut schemas, "PlannerGoal")?; + insert::(&mut schemas, "PlannerStatus")?; + insert::(&mut schemas, "PatrolStrategy")?; + insert::(&mut schemas, "PatrolStatus")?; + insert::(&mut schemas, "SpatialMemoryKind")?; + insert::(&mut schemas, "SpatialMemoryEntry")?; + insert::(&mut schemas, "SpatialMemoryStatus")?; + insert::(&mut schemas, "VisualizationFrame")?; + insert::(&mut schemas, "VisualizationPath")?; + insert::(&mut schemas, "MapMetadata")?; + insert::(&mut schemas, "Pose2d")?; + insert::(&mut schemas, "Twist2d")?; + insert::(&mut schemas, "OccupancyGridFrame")?; + insert::(&mut schemas, "CostmapFrame")?; + insert::(&mut schemas, "PointCloudMetadata")?; + insert::(&mut schemas, "DetectionFrame")?; + insert::(&mut schemas, "CommandOverlay")?; + insert::(&mut schemas, "AutonomyOverlay")?; + insert::(&mut schemas, "ImageObservation")?; + insert::(&mut schemas, "VisionResult")?; + insert::(&mut schemas, "AdapterCategory")?; + insert::(&mut schemas, "AdapterMaturity")?; + insert::(&mut schemas, "AdapterProfile")?; + insert::(&mut schemas, "McpToolDescriptor")?; + insert::(&mut schemas, "McpToolList")?; + insert::(&mut schemas, "McpCallResponse")?; + insert::(&mut schemas, "McpStatus")?; + insert::(&mut schemas, "McpModuleToolMap")?; + insert::(&mut schemas, "DroneCommandStatus")?; + insert::(&mut schemas, "ManipulatorJoint")?; + insert::(&mut schemas, "ManipulatorJointState")?; + insert::(&mut schemas, "ManipulatorCommandStatus")?; + + Ok(json!({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": SCHEMA_ID, + "title": "Leash message schemas", + "description": "Canonical JSON Schema output for Leash HTTP, MCP, telemetry, capability, module, and adapter messages generated from Rust types.", + "schema_version": SCHEMA_VERSION, + "schemas": schemas, + })) +} + +fn insert(schemas: &mut BTreeMap, name: &str) -> Result<()> { + schemas.insert(name.to_string(), serde_json::to_value(schema_for!(T))?); + Ok(()) +} diff --git a/src/mcp.rs b/src/mcp.rs index c65cf5d..8207a01 100644 --- a/src/mcp.rs +++ b/src/mcp.rs @@ -15,7 +15,7 @@ use crate::{ types::{PatrolStrategy, SpatialMemoryKind, SpeedMode}, }; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)] pub struct McpToolDescriptor { pub name: String, pub description: String, @@ -25,20 +25,20 @@ pub struct McpToolDescriptor { pub output_schema: Value, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)] pub struct McpToolList { pub ok: bool, pub tools: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)] pub struct McpCallResponse { pub ok: bool, pub tool: String, pub result: Value, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)] pub struct McpStatus { pub ok: bool, pub transport: String, @@ -51,13 +51,13 @@ pub struct McpStatus { pub tool_count: usize, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)] pub struct McpModuleToolMap { pub ok: bool, pub modules: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)] pub struct McpModuleTools { pub module: String, pub module_type: String,