{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://yarramate.org/schema/visual-session-started/v2",
  "title": "YarraMate visual session started",
  "description": "Public first line printed by `yarramate-visual start`. It never carries the private agent capability.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "format",
    "protocolVersion",
    "sessionId",
    "authority",
    "title",
    "chatEnabled",
    "browserUrl",
    "webSocketUrl",
    "origin",
    "descriptorPath",
    "sessionRoot",
    "capabilities",
    "startedAt"
  ],
  "properties": {
    "format": {
      "const": "yarramate/visual-session-started/v2"
    },
    "protocolVersion": {
      "const": "yarramate/visual-protocol/v5"
    },
    "sessionId": {
      "$ref": "https://yarramate.org/schema/visual-event/v1#/$defs/identifier"
    },
    "authority": {
      "$ref": "https://yarramate.org/schema/visual-model/v1#/$defs/authority"
    },
    "title": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "chatEnabled": {
      "type": "boolean"
    },
    "browserUrl": {
      "type": "string",
      "pattern": "^http://127\\.0\\.0\\.1:[0-9]{1,5}/bootstrap\\?key=[0-9a-f]{16,}$"
    },
    "webSocketUrl": {
      "type": "string",
      "pattern": "^ws://127\\.0\\.0\\.1:[0-9]{1,5}/[A-Za-z0-9._/-]*$"
    },
    "origin": {
      "$ref": "#/$defs/localOrigin"
    },
    "descriptorPath": {
      "$ref": "#/$defs/sessionFileUri"
    },
    "sessionRoot": {
      "$ref": "#/$defs/sessionFileUri"
    },
    "capabilities": {
      "$ref": "#/$defs/capabilities"
    },
    "startedAt": {
      "$ref": "https://yarramate.org/schema/visual-event/v1#/$defs/timestamp"
    }
  },
  "allOf": [
    {
      "if": {
        "type": "object",
        "required": ["chatEnabled"],
        "properties": {
          "chatEnabled": { "const": false }
        }
      },
      "then": {
        "type": "object",
        "properties": {
          "capabilities": {
            "type": "object",
            "properties": {
              "chat": { "const": false }
            }
          }
        }
      }
    }
  ],
  "$defs": {
    "localOrigin": {
      "type": "string",
      "pattern": "^http://127\\.0\\.0\\.1:[0-9]{1,5}$"
    },
    "sessionFileUri": {
      "description": "Canonical local `file:` URI naming one filesystem path, as minted by Node's `pathToFileURL`. This pattern is a coarse structural gate only: every URI a local path encodes to begins `file:///` (empty host, then a POSIX `/` root or a Windows `/C:/` drive root), so a UNC-derived `file://server/share/x` fails it outright. The real check cannot be written as a schema keyword and lives beside these validators in `protocol.ts`: decode with `fileURLToPath`, refuse a non-empty host, and require that re-encoding the decoded path reproduces this string byte-for-byte. `maxLength` is double the 4096-byte native path bound because a fully percent-encoded path costs three characters per byte; it widens the wire envelope, not the path-length invariant.",
      "type": "string",
      "minLength": 8,
      "maxLength": 8192,
      "pattern": "^file:///[^\\u0000]*$"
    },
    "capabilities": {
      "description": "Features the browser session may use. Diagram-only mode disables chat.",
      "type": "object",
      "additionalProperties": false,
      "required": ["chat", "choices", "navigation", "transcript"],
      "properties": {
        "chat": { "type": "boolean" },
        "choices": { "type": "boolean" },
        "navigation": { "type": "boolean" },
        "transcript": { "type": "boolean" }
      }
    }
  }
}
