{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://behavioralstate.io/v1/schemas/agents/registry.json",
  "title": "BSP Service Registry",
  "description": "Schema for service descriptors and the service registry capability (io.bsp.agents.registry).",
  "$defs": {
    "webhook": {
      "type": "object",
      "description": "Callback configuration for push event delivery over HTTP.",
      "required": ["url"],
      "properties": {
        "url": {
          "type": "string",
          "format": "uri",
          "description": "HTTPS endpoint to receive events as CloudEvents (HTTP binding)."
        },
        "secret": {
          "type": "string",
          "writeOnly": true,
          "description": "HMAC secret for payload signing. Write-only — accepted on registration, never returned in read responses."
        }
      },
      "additionalProperties": false
    },
    "serviceDescriptor": {
      "type": "object",
      "description": "The identity card for an BSP-compliant service — what it does, what commands it accepts, what events it produces.",
      "required": ["id", "name", "accepts", "produces", "status"],
      "properties": {
        "id": {
          "type": "string",
          "description": "Globally unique service identifier."
        },
        "name": {
          "type": "string",
          "description": "Human-readable name."
        },
        "description": {
          "type": "string",
          "description": "What this service does."
        },
        "version": {
          "type": "string",
          "description": "Semver version of the service implementation currently running (e.g. '1.2.0'). Allows consumers to know which version is live and supports gradual rollouts."
        },
        "type": {
          "type": "string",
          "description": "Service type classification (e.g. negotiation-service, pricing-engine, sensor)."
        },
        "endpoint": {
          "type": "string",
          "format": "uri",
          "description": "Base URL of this service's BSP surface. Consumers append /.well-known/bsp to discover transport bindings and send commands. Optional — omit if the service is not directly addressable."
        },
        "accepts": {
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[A-Z][a-zA-Z0-9]*$",
            "description": "CloudEvent type string in PascalCase (e.g. 'ProposeCounter', 'SubmitOrder')."
          },
          "description": "CloudEvent type strings (PascalCase) identifying the command types this service ingests."
        },
        "produces": {
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[A-Z][a-zA-Z0-9]*$",
            "description": "CloudEvent type string in PascalCase (e.g. 'CounterProposed', 'OrderSubmitted')."
          },
          "description": "CloudEvent type strings (PascalCase) identifying the event types this service publishes as a result of processing."
        },
        "status": {
          "type": "string",
          "enum": ["running", "paused", "stopped", "error"],
          "description": "Current service status."
        },
        "webhook": {
          "$ref": "#/$defs/webhook"
        },
        "metadata": {
          "type": "object",
          "description": "Opaque, service-defined key-value configuration. Intended for operational settings such as AI model name, system prompt, or provider credentials. The protocol does not interpret or validate the contents. Clients may read this field; the service owns and manages it.",
          "additionalProperties": true
        }
      },
      "additionalProperties": false
    },
    "serviceRegistration": {
      "type": "object",
      "description": "Request body for registering or updating a service (upsert). If a service with the given id already exists its descriptor is fully replaced. Status defaults to stopped on first registration; preserved on update.",
      "required": ["id", "name", "accepts", "produces"],
      "properties": {
        "id": {
          "type": "string",
          "description": "Globally unique service identifier."
        },
        "name": {
          "type": "string",
          "description": "Human-readable name."
        },
        "description": {
          "type": "string",
          "description": "What this service does."
        },
        "version": {
          "type": "string",
          "description": "Semver version of the service implementation (e.g. '1.2.0')."
        },
        "type": {
          "type": "string",
          "description": "Service type classification."
        },
        "endpoint": {
          "type": "string",
          "format": "uri",
          "description": "Base URL of this service's BSP surface. Consumers append /.well-known/bsp to discover transport bindings and send commands. Optional — omit if the service is not directly addressable."
        },
        "accepts": {
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[A-Z][a-zA-Z0-9]*$",
            "description": "CloudEvent type string in PascalCase (e.g. 'ProposeCounter', 'SubmitOrder')."
          },
          "description": "CloudEvent type strings (PascalCase) identifying the command types this service accepts."
        },
        "produces": {
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[A-Z][a-zA-Z0-9]*$",
            "description": "CloudEvent type string in PascalCase (e.g. 'CounterProposed', 'OrderSubmitted')."
          },
          "description": "CloudEvent type strings (PascalCase) identifying the event types this service produces."
        },
        "webhook": {
          "$ref": "#/$defs/webhook"
        },
        "metadata": {
          "type": "object",
          "description": "Opaque, service-defined key-value configuration — same semantics as the descriptor field. Provided on registration; returned on all read responses.",
          "additionalProperties": true
        }
      },
      "additionalProperties": false
    },
    "serviceList": {
      "type": "object",
      "description": "Response body for GET /services.",
      "required": ["services"],
      "properties": {
        "services": {
          "type": "array",
          "items": { "$ref": "#/$defs/serviceDescriptor" }
        }
      },
      "additionalProperties": false
    }
  }
}
