{
  "id": "agenticros",
  "name": "AgenticROS",
  "version": "0.0.1",
  "description": "ROS2 integration for AI agent platforms (OpenClaw adapter)",
  "activation": {
    "onStartup": true
  },
  "contracts": {
    "tools": [
      "ros2_publish",
      "ros2_subscribe_once",
      "ros2_service_call",
      "ros2_action_goal",
      "ros2_param_get",
      "ros2_param_set",
      "ros2_list_topics",
      "ros2_save_place",
      "ros2_list_places",
      "ros2_navigate_to_place",
      "ros2_list_capabilities",
      "ros2_list_robots",
      "ros2_discover_robots",
      "ros2_find_robots_for",
      "run_mission",
      "mission_cancel",
      "mission_pause",
      "mission_resume",
      "ros2_camera_snapshot",
      "ros2_depth_distance",
      "ros2_find_object",
      "memory_remember",
      "memory_recall",
      "memory_forget",
      "memory_status",
      "follow_robot",
      "follow_me_see",
      "ollama_status",
      "find_object",
      "scan_surroundings",
      "jarvis_control",
      "jarvis_speak"
    ]
  },
  "configSchema": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "transport": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "rosbridge",
              "local",
              "webrtc",
              "zenoh"
            ],
            "description": "Transport mode: local (Mode A, default — gateway co-located with the robot), rosbridge (Mode B, off-robot via WebSocket), webrtc (Mode C, cloud), or zenoh (Mode D)",
            "default": "local"
          }
        }
      },
      "rosbridge": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Rosbridge WebSocket URL",
            "default": "ws://localhost:9090"
          },
          "reconnect": {
            "type": "boolean",
            "description": "Auto-reconnect on disconnect",
            "default": true
          },
          "reconnectInterval": {
            "type": "number",
            "description": "Reconnect interval in milliseconds",
            "default": 3000
          }
        },
        "required": [
          "url"
        ]
      },
      "local": {
        "type": "object",
        "properties": {
          "domainId": {
            "type": "number",
            "description": "ROS2 domain ID for local DDS communication",
            "default": 0
          }
        }
      },
      "zenoh": {
        "type": "object",
        "properties": {
          "routerEndpoint": {
            "type": "string",
            "description": "Zenoh router endpoint (e.g. ws://localhost:10000 for zenoh-ts)",
            "default": "tcp/localhost:7447"
          },
          "domainId": {
            "type": "number",
            "description": "ROS2 domain ID (matches rmw_zenoh)",
            "default": 0
          },
          "keyFormat": {
            "type": "string",
            "enum": [
              "ros2dds",
              "rmw_zenoh"
            ],
            "description": "Use ros2dds when using zenoh-bridge-ros2dds; use rmw_zenoh when robot uses Zenoh RMW directly",
            "default": "ros2dds"
          },
          "bridgeNamespace": {
            "type": "string",
            "description": "Must match zenoh-bridge-ros2dds plugins.ros2dds.namespace when not default \"/\" (e.g. \"/bot1\"). Omit for default bridge namespace."
          }
        }
      },
      "webrtc": {
        "type": "object",
        "properties": {
          "signalingUrl": {
            "type": "string",
            "description": "WebSocket URL of the signaling server (e.g., wss://signal-host)"
          },
          "apiUrl": {
            "type": "string",
            "description": "REST API URL of the signaling server (e.g., https://signal-host)"
          },
          "robotId": {
            "type": "string",
            "description": "Target robot's ID on the signaling server"
          },
          "robotKey": {
            "type": "string",
            "description": "Robot key secret (validated by robot, not server)"
          },
          "iceServers": {
            "type": "array",
            "description": "STUN/TURN server configuration",
            "items": {
              "type": "object",
              "properties": {
                "urls": {
                  "description": "STUN/TURN server URL(s)"
                },
                "username": {
                  "type": "string"
                },
                "credential": {
                  "type": "string"
                }
              }
            },
            "default": [
              {
                "urls": "stun:stun.l.google.com:19302"
              }
            ]
          }
        }
      },
      "robot": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Robot display name",
            "default": "Robot"
          },
          "namespace": {
            "type": "string",
            "description": "ROS2 topic namespace so cmd_vel and other root-level topics are published to /<namespace>/<topic> (e.g. robot3946b404c33e4aa39a8d16deb1c5c593 → /robot3946.../cmd_vel). Required if the robot base subscribes to a namespaced cmd_vel.",
            "default": ""
          },
          "cameraTopic": {
            "type": "string",
            "description": "Camera topic for what do you see and default for teleop",
            "default": ""
          }
        }
      },
      "robots": {
        "type": "array",
        "description": "Multi-robot fleet list. When non-empty, used by ros2_list_robots and per-tool robot_id. When empty, the legacy config.robot entry is used. Manage via `agenticros robots` CLI.",
        "default": [],
        "items": {
          "type": "object",
          "required": [
            "id"
          ],
          "properties": {
            "id": {
              "type": "string",
              "description": "Stable identifier referenced by robot_id tool arguments"
            },
            "name": {
              "type": "string",
              "default": "Robot"
            },
            "namespace": {
              "type": "string",
              "default": ""
            },
            "cameraTopic": {
              "type": "string",
              "default": ""
            },
            "default": {
              "type": "boolean",
              "description": "When true, this robot is active even if not first in the list"
            },
            "kind": {
              "type": "string",
              "description": "Robot kind for ros2_find_robots_for (e.g. amr, arm, drone, rover)",
              "default": "amr"
            },
            "sensors": {
              "type": "object",
              "properties": {
                "has_realsense": {
                  "type": "boolean",
                  "default": false
                },
                "has_lidar": {
                  "type": "boolean",
                  "default": false
                },
                "has_arm": {
                  "type": "boolean",
                  "default": false
                }
              }
            },
            "capabilities": {
              "type": "array",
              "description": "Optional per-robot capability allowlist for ros2_find_robots_for",
              "items": {
                "type": "string"
              }
            },
            "transport": {
              "type": "object",
              "description": "Per-robot transport override (inherits global transport when omitted)",
              "properties": {
                "mode": {
                  "type": "string",
                  "enum": [
                    "rosbridge",
                    "local",
                    "webrtc",
                    "zenoh"
                  ]
                },
                "zenoh": {
                  "type": "object",
                  "properties": {
                    "routerEndpoint": {
                      "type": "string"
                    },
                    "domainId": {
                      "type": "number"
                    },
                    "keyFormat": {
                      "type": "string",
                      "enum": [
                        "ros2dds",
                        "rmw_zenoh"
                      ]
                    },
                    "bridgeNamespace": {
                      "type": "string"
                    }
                  }
                },
                "rosbridge": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string"
                    },
                    "reconnect": {
                      "type": "boolean"
                    },
                    "reconnectInterval": {
                      "type": "number"
                    }
                  }
                },
                "local": {
                  "type": "object",
                  "properties": {
                    "domainId": {
                      "type": "number"
                    }
                  }
                },
                "webrtc": {
                  "type": "object",
                  "properties": {
                    "signalingUrl": {
                      "type": "string"
                    },
                    "apiUrl": {
                      "type": "string"
                    },
                    "robotId": {
                      "type": "string"
                    },
                    "robotKey": {
                      "type": "string"
                    },
                    "iceServers": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "teleop": {
        "type": "object",
        "description": "Teleop web app: camera and twist controls",
        "properties": {
          "cameraTopic": {
            "type": "string",
            "default": ""
          },
          "cameraTopics": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "topic": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                }
              }
            },
            "default": []
          },
          "cmdVelTopic": {
            "type": "string",
            "default": ""
          },
          "speedDefault": {
            "type": "number",
            "description": "Default linear speed (m/s) for teleop",
            "default": 0.3,
            "minimum": 0,
            "maximum": 2
          },
          "cameraPollMs": {
            "type": "number",
            "default": 150,
            "minimum": 50,
            "maximum": 2000
          }
        }
      },
      "safety": {
        "type": "object",
        "properties": {
          "maxLinearVelocity": {
            "type": "number",
            "description": "Maximum linear velocity (m/s)",
            "default": 1
          },
          "maxAngularVelocity": {
            "type": "number",
            "description": "Maximum angular velocity (rad/s)",
            "default": 1.5
          },
          "workspaceLimits": {
            "type": "object",
            "description": "Workspace boundary limits (meters)",
            "properties": {
              "xMin": {
                "type": "number",
                "default": -10
              },
              "xMax": {
                "type": "number",
                "default": 10
              },
              "yMin": {
                "type": "number",
                "default": -10
              },
              "yMax": {
                "type": "number",
                "default": 10
              }
            }
          }
        }
      },
      "describer": {
        "type": "object",
        "description": "Optional in-plugin image describer. When enabled, ros2_camera_snapshot calls an OpenAI-compatible vision endpoint (typically local Ollama with a qwen2.5-VL model) and embeds the description text directly in the tool result. Required when the primary chat model is text-only (e.g. qwen2.5:7b for tool calling on Jetson) because OpenClaw filters image content blocks out of tool results for text-only models, leaving the agent unable to describe what the robot sees.",
        "properties": {
          "enabled": {
            "type": "boolean",
            "default": false,
            "description": "Master switch for the describer."
          },
          "url": {
            "type": "string",
            "default": "http://host.openshell.internal:11435/v1/chat/completions",
            "description": "OpenAI-compat chat completions endpoint URL (e.g. Ollama)."
          },
          "apiKey": {
            "type": "string",
            "description": "Optional Bearer token for the endpoint."
          },
          "model": {
            "type": "string",
            "default": "qwen2.5vl:7b",
            "description": "Vision model id (must accept OpenAI image_url content blocks)."
          },
          "prompt": {
            "type": "string",
            "description": "Prompt sent alongside each image. A reasonable default is used when omitted."
          },
          "maxTokens": {
            "type": "number",
            "default": 400,
            "minimum": 50,
            "maximum": 2048,
            "description": "Max tokens to generate for each description."
          },
          "timeoutMs": {
            "type": "number",
            "default": 60000,
            "minimum": 1000,
            "maximum": 180000,
            "description": "HTTP request timeout in milliseconds."
          },
          "maxImageDimension": {
            "type": "number",
            "default": 896,
            "minimum": 0,
            "maximum": 4096,
            "description": "Resize images larger than this max dimension before sending to the VL model. 0 disables resizing."
          }
        }
      },
      "memory": {
        "type": "object",
        "description": "Optional cross-adapter semantic memory. Off by default; see docs/memory.md.",
        "properties": {
          "enabled": {
            "type": "boolean",
            "default": false,
            "description": "Master switch. When false, no memory tools are registered."
          },
          "backend": {
            "type": "string",
            "enum": [
              "local",
              "mem0"
            ],
            "default": "local",
            "description": "local = JSON-on-disk, no deps; mem0 = semantic search via mem0ai (requires `pnpm add mem0ai`)."
          },
          "namespace": {
            "type": "string",
            "description": "Override per-record namespace. Defaults to robot.namespace at call time."
          },
          "local": {
            "type": "object",
            "properties": {
              "storePath": {
                "type": "string",
                "default": "~/.agenticros/memory.json"
              }
            }
          },
          "mem0": {
            "type": "object",
            "properties": {
              "inferOnWrite": {
                "type": "boolean",
                "default": false,
                "description": "When true, mem0 LLM-extracts facts on add. Default false (raw store)."
              },
              "historyDbPath": {
                "type": "string",
                "default": "~/.agenticros/memory-history.db"
              },
              "embedder": {
                "type": "object",
                "description": "Optional. When omitted, factory auto-detects Ollama → OpenAI → error."
              },
              "vectorStore": {
                "type": "object",
                "description": "Optional. Passed to mem0 verbatim."
              },
              "llm": {
                "type": "object",
                "description": "Optional. Required only when inferOnWrite is true."
              }
            }
          }
        }
      },
      "hive": {
        "type": "object",
        "description": "Optional fleet hive. Off by default; see docs/hive.md. Not a memory backend.",
        "properties": {
          "enabled": {
            "type": "boolean",
            "default": false,
            "description": "Share with my fleet. When false, no hive_* tools are registered."
          },
          "url": {
            "type": "string",
            "description": "Hive HTTP base URL. Omit for http://127.0.0.1:6502."
          },
          "identityId": {
            "type": "string",
            "description": "Filled automatically from the ARC robot id."
          },
          "hiveId": {
            "type": "string",
            "description": "Filled automatically after the robot joins the org hive."
          },
          "recipes": {
            "type": "object",
            "properties": {
              "detect": { "type": "boolean", "default": false },
              "describe": { "type": "boolean", "default": false },
              "health": { "type": "boolean", "default": false }
            }
          }
        }
      },
      "skills": {
        "type": "object",
        "description": "Per-skill config. Keys are skill ids (e.g. followme). Each skill validates its own slice.",
        "additionalProperties": true
      },
      "skillPaths": {
        "type": "array",
        "description": "Directories to scan for skill packages (package.json with an `agenticros` block).",
        "items": {
          "type": "string"
        },
        "default": []
      },
      "skillPackages": {
        "type": "array",
        "description": "Npm (or local) package names to load as skills (e.g. agenticros-skill-followme).",
        "items": {
          "type": "string"
        },
        "default": []
      },
      "skillRefs": {
        "type": "array",
        "description": "Marketplace refs (owner/skill) installed via `agenticros skills install`, used to re-resolve skillPaths/skillPackages from the skills cache.",
        "items": {
          "type": "string"
        },
        "default": []
      }
    }
  },
  "uiHints": {
    "transport.mode": {
      "label": "Transport Mode",
      "description": "How the plugin connects to ROS2: rosbridge (WebSocket), local (DDS), webrtc (P2P), or zenoh (Mode D)"
    },
    "zenoh.routerEndpoint": {
      "label": "Zenoh Router Endpoint",
      "placeholder": "ws://localhost:10000"
    },
    "zenoh.domainId": {
      "label": "Zenoh Domain ID",
      "advanced": true
    },
    "zenoh.bridgeNamespace": {
      "label": "Zenoh bridge ROS namespace",
      "description": "Same as ros2dds.namespace on the robot bridge when not \"/\"",
      "advanced": true
    },
    "rosbridge.url": {
      "label": "Rosbridge URL",
      "placeholder": "ws://localhost:9090"
    },
    "robot.name": {
      "label": "Robot Name",
      "placeholder": "Robot"
    },
    "robot.namespace": {
      "label": "ROS2 Robot Namespace",
      "description": "ROS2 topic namespace; e.g. robot-uuid gives topics like /robot-uuid/cmd_vel",
      "placeholder": ""
    },
    "safety.maxLinearVelocity": {
      "label": "Max Linear Velocity (m/s)",
      "advanced": true
    },
    "safety.maxAngularVelocity": {
      "label": "Max Angular Velocity (rad/s)",
      "advanced": true
    },
    "safety.workspaceLimits": {
      "label": "Workspace Boundary Limits",
      "advanced": true
    },
    "skills.followme.depthTopic": {
      "label": "Follow Me — depth image topic",
      "description": "sensor_msgs/Image depth topic for distance (e.g. RealSense depth)"
    },
    "skills.followme.rateHz": {
      "label": "Follow Me — loop rate (Hz)",
      "advanced": true
    },
    "skills.followme.invertLinearX": {
      "label": "Follow Me — invert cmd_vel linear.x",
      "description": "Flip forward/back if the base drives the wrong way",
      "advanced": true
    },
    "skills.followme.logTickTiming": {
      "label": "Follow Me — log tick timing",
      "description": "Emit latency logs for depth / sectors / Ollama per tick",
      "advanced": true
    },
    "skills.followme.criticalStopDistanceM": {
      "label": "Follow Me — hard stop distance (m)"
    },
    "skills.followme.maxVelocityFraction": {
      "label": "Follow Me — max speed fraction of Safety limits",
      "description": "Caps linear/angular vs safety.maxLinearVelocity / maxAngularVelocity (e.g. 0.2 = 20%)"
    }
  }
}
