// Generated by scripts/protocol-gen-swift.ts — do not edit by hand
import Foundation

public let GATEWAY_PROTOCOL_VERSION = 2

public enum ErrorCode: String, Codable {
    case notLinked = "NOT_LINKED"
    case agentTimeout = "AGENT_TIMEOUT"
    case invalidRequest = "INVALID_REQUEST"
    case unavailable = "UNAVAILABLE"
}

public struct ConnectParams: Codable {
    public let minprotocol: Int
    public let maxprotocol: Int
    public let client: [String: AnyCodable]
    public let caps: [String]?
    public let auth: [String: AnyCodable]?
    public let locale: String?
    public let useragent: String?

    public init(
        minprotocol: Int,
        maxprotocol: Int,
        client: [String: AnyCodable],
        caps: [String]?,
        auth: [String: AnyCodable]?,
        locale: String?,
        useragent: String?
    ) {
        self.minprotocol = minprotocol
        self.maxprotocol = maxprotocol
        self.client = client
        self.caps = caps
        self.auth = auth
        self.locale = locale
        self.useragent = useragent
    }
    private enum CodingKeys: String, CodingKey {
        case minprotocol = "minProtocol"
        case maxprotocol = "maxProtocol"
        case client
        case caps
        case auth
        case locale
        case useragent = "userAgent"
    }
}

public struct HelloOk: Codable {
    public let type: String
    public let _protocol: Int
    public let server: [String: AnyCodable]
    public let features: [String: AnyCodable]
    public let snapshot: Snapshot
    public let canvashosturl: String?
    public let policy: [String: AnyCodable]

    public init(
        type: String,
        _protocol: Int,
        server: [String: AnyCodable],
        features: [String: AnyCodable],
        snapshot: Snapshot,
        canvashosturl: String?,
        policy: [String: AnyCodable]
    ) {
        self.type = type
        self._protocol = _protocol
        self.server = server
        self.features = features
        self.snapshot = snapshot
        self.canvashosturl = canvashosturl
        self.policy = policy
    }
    private enum CodingKeys: String, CodingKey {
        case type
        case _protocol = "protocol"
        case server
        case features
        case snapshot
        case canvashosturl = "canvasHostUrl"
        case policy
    }
}

public struct RequestFrame: Codable {
    public let type: String
    public let id: String
    public let method: String
    public let params: AnyCodable?

    public init(
        type: String,
        id: String,
        method: String,
        params: AnyCodable?
    ) {
        self.type = type
        self.id = id
        self.method = method
        self.params = params
    }
    private enum CodingKeys: String, CodingKey {
        case type
        case id
        case method
        case params
    }
}

public struct ResponseFrame: Codable {
    public let type: String
    public let id: String
    public let ok: Bool
    public let payload: AnyCodable?
    public let error: [String: AnyCodable]?

    public init(
        type: String,
        id: String,
        ok: Bool,
        payload: AnyCodable?,
        error: [String: AnyCodable]?
    ) {
        self.type = type
        self.id = id
        self.ok = ok
        self.payload = payload
        self.error = error
    }
    private enum CodingKeys: String, CodingKey {
        case type
        case id
        case ok
        case payload
        case error
    }
}

public struct EventFrame: Codable {
    public let type: String
    public let event: String
    public let payload: AnyCodable?
    public let seq: Int?
    public let stateversion: [String: AnyCodable]?

    public init(
        type: String,
        event: String,
        payload: AnyCodable?,
        seq: Int?,
        stateversion: [String: AnyCodable]?
    ) {
        self.type = type
        self.event = event
        self.payload = payload
        self.seq = seq
        self.stateversion = stateversion
    }
    private enum CodingKeys: String, CodingKey {
        case type
        case event
        case payload
        case seq
        case stateversion = "stateVersion"
    }
}

public struct PresenceEntry: Codable {
    public let host: String?
    public let ip: String?
    public let version: String?
    public let platform: String?
    public let devicefamily: String?
    public let modelidentifier: String?
    public let mode: String?
    public let lastinputseconds: Int?
    public let reason: String?
    public let tags: [String]?
    public let text: String?
    public let ts: Int
    public let instanceid: String?

    public init(
        host: String?,
        ip: String?,
        version: String?,
        platform: String?,
        devicefamily: String?,
        modelidentifier: String?,
        mode: String?,
        lastinputseconds: Int?,
        reason: String?,
        tags: [String]?,
        text: String?,
        ts: Int,
        instanceid: String?
    ) {
        self.host = host
        self.ip = ip
        self.version = version
        self.platform = platform
        self.devicefamily = devicefamily
        self.modelidentifier = modelidentifier
        self.mode = mode
        self.lastinputseconds = lastinputseconds
        self.reason = reason
        self.tags = tags
        self.text = text
        self.ts = ts
        self.instanceid = instanceid
    }
    private enum CodingKeys: String, CodingKey {
        case host
        case ip
        case version
        case platform
        case devicefamily = "deviceFamily"
        case modelidentifier = "modelIdentifier"
        case mode
        case lastinputseconds = "lastInputSeconds"
        case reason
        case tags
        case text
        case ts
        case instanceid = "instanceId"
    }
}

public struct StateVersion: Codable {
    public let presence: Int
    public let health: Int

    public init(
        presence: Int,
        health: Int
    ) {
        self.presence = presence
        self.health = health
    }
    private enum CodingKeys: String, CodingKey {
        case presence
        case health
    }
}

public struct Snapshot: Codable {
    public let presence: [PresenceEntry]
    public let health: AnyCodable
    public let stateversion: StateVersion
    public let uptimems: Int

    public init(
        presence: [PresenceEntry],
        health: AnyCodable,
        stateversion: StateVersion,
        uptimems: Int
    ) {
        self.presence = presence
        self.health = health
        self.stateversion = stateversion
        self.uptimems = uptimems
    }
    private enum CodingKeys: String, CodingKey {
        case presence
        case health
        case stateversion = "stateVersion"
        case uptimems = "uptimeMs"
    }
}

public struct ErrorShape: Codable {
    public let code: String
    public let message: String
    public let details: AnyCodable?
    public let retryable: Bool?
    public let retryafterms: Int?

    public init(
        code: String,
        message: String,
        details: AnyCodable?,
        retryable: Bool?,
        retryafterms: Int?
    ) {
        self.code = code
        self.message = message
        self.details = details
        self.retryable = retryable
        self.retryafterms = retryafterms
    }
    private enum CodingKeys: String, CodingKey {
        case code
        case message
        case details
        case retryable
        case retryafterms = "retryAfterMs"
    }
}

public struct AgentEvent: Codable {
    public let runid: String
    public let seq: Int
    public let stream: String
    public let ts: Int
    public let data: [String: AnyCodable]

    public init(
        runid: String,
        seq: Int,
        stream: String,
        ts: Int,
        data: [String: AnyCodable]
    ) {
        self.runid = runid
        self.seq = seq
        self.stream = stream
        self.ts = ts
        self.data = data
    }
    private enum CodingKeys: String, CodingKey {
        case runid = "runId"
        case seq
        case stream
        case ts
        case data
    }
}

public struct SendParams: Codable {
    public let to: String
    public let message: String
    public let mediaurl: String?
    public let provider: String?
    public let idempotencykey: String

    public init(
        to: String,
        message: String,
        mediaurl: String?,
        provider: String?,
        idempotencykey: String
    ) {
        self.to = to
        self.message = message
        self.mediaurl = mediaurl
        self.provider = provider
        self.idempotencykey = idempotencykey
    }
    private enum CodingKeys: String, CodingKey {
        case to
        case message
        case mediaurl = "mediaUrl"
        case provider
        case idempotencykey = "idempotencyKey"
    }
}

public struct AgentParams: Codable {
    public let message: String
    public let to: String?
    public let sessionid: String?
    public let sessionkey: String?
    public let thinking: String?
    public let deliver: Bool?
    public let channel: String?
    public let timeout: Int?
    public let idempotencykey: String

    public init(
        message: String,
        to: String?,
        sessionid: String?,
        sessionkey: String?,
        thinking: String?,
        deliver: Bool?,
        channel: String?,
        timeout: Int?,
        idempotencykey: String
    ) {
        self.message = message
        self.to = to
        self.sessionid = sessionid
        self.sessionkey = sessionkey
        self.thinking = thinking
        self.deliver = deliver
        self.channel = channel
        self.timeout = timeout
        self.idempotencykey = idempotencykey
    }
    private enum CodingKeys: String, CodingKey {
        case message
        case to
        case sessionid = "sessionId"
        case sessionkey = "sessionKey"
        case thinking
        case deliver
        case channel
        case timeout
        case idempotencykey = "idempotencyKey"
    }
}

public struct WakeParams: Codable {
    public let mode: AnyCodable
    public let text: String

    public init(
        mode: AnyCodable,
        text: String
    ) {
        self.mode = mode
        self.text = text
    }
    private enum CodingKeys: String, CodingKey {
        case mode
        case text
    }
}

public struct NodePairRequestParams: Codable {
    public let nodeid: String
    public let displayname: String?
    public let platform: String?
    public let version: String?
    public let devicefamily: String?
    public let modelidentifier: String?
    public let caps: [String]?
    public let commands: [String]?
    public let remoteip: String?
    public let silent: Bool?

    public init(
        nodeid: String,
        displayname: String?,
        platform: String?,
        version: String?,
        devicefamily: String?,
        modelidentifier: String?,
        caps: [String]?,
        commands: [String]?,
        remoteip: String?,
        silent: Bool?
    ) {
        self.nodeid = nodeid
        self.displayname = displayname
        self.platform = platform
        self.version = version
        self.devicefamily = devicefamily
        self.modelidentifier = modelidentifier
        self.caps = caps
        self.commands = commands
        self.remoteip = remoteip
        self.silent = silent
    }
    private enum CodingKeys: String, CodingKey {
        case nodeid = "nodeId"
        case displayname = "displayName"
        case platform
        case version
        case devicefamily = "deviceFamily"
        case modelidentifier = "modelIdentifier"
        case caps
        case commands
        case remoteip = "remoteIp"
        case silent
    }
}

public struct NodePairListParams: Codable {
}

public struct NodePairApproveParams: Codable {
    public let requestid: String

    public init(
        requestid: String
    ) {
        self.requestid = requestid
    }
    private enum CodingKeys: String, CodingKey {
        case requestid = "requestId"
    }
}

public struct NodePairRejectParams: Codable {
    public let requestid: String

    public init(
        requestid: String
    ) {
        self.requestid = requestid
    }
    private enum CodingKeys: String, CodingKey {
        case requestid = "requestId"
    }
}

public struct NodePairVerifyParams: Codable {
    public let nodeid: String
    public let token: String

    public init(
        nodeid: String,
        token: String
    ) {
        self.nodeid = nodeid
        self.token = token
    }
    private enum CodingKeys: String, CodingKey {
        case nodeid = "nodeId"
        case token
    }
}

public struct NodeListParams: Codable {
}

public struct NodeDescribeParams: Codable {
    public let nodeid: String

    public init(
        nodeid: String
    ) {
        self.nodeid = nodeid
    }
    private enum CodingKeys: String, CodingKey {
        case nodeid = "nodeId"
    }
}

public struct NodeInvokeParams: Codable {
    public let nodeid: String
    public let command: String
    public let params: AnyCodable?
    public let timeoutms: Int?
    public let idempotencykey: String

    public init(
        nodeid: String,
        command: String,
        params: AnyCodable?,
        timeoutms: Int?,
        idempotencykey: String
    ) {
        self.nodeid = nodeid
        self.command = command
        self.params = params
        self.timeoutms = timeoutms
        self.idempotencykey = idempotencykey
    }
    private enum CodingKeys: String, CodingKey {
        case nodeid = "nodeId"
        case command
        case params
        case timeoutms = "timeoutMs"
        case idempotencykey = "idempotencyKey"
    }
}

public struct SessionsListParams: Codable {
    public let limit: Int?
    public let activeminutes: Int?
    public let includeglobal: Bool?
    public let includeunknown: Bool?

    public init(
        limit: Int?,
        activeminutes: Int?,
        includeglobal: Bool?,
        includeunknown: Bool?
    ) {
        self.limit = limit
        self.activeminutes = activeminutes
        self.includeglobal = includeglobal
        self.includeunknown = includeunknown
    }
    private enum CodingKeys: String, CodingKey {
        case limit
        case activeminutes = "activeMinutes"
        case includeglobal = "includeGlobal"
        case includeunknown = "includeUnknown"
    }
}

public struct SessionsPatchParams: Codable {
    public let key: String
    public let thinkinglevel: AnyCodable?
    public let verboselevel: AnyCodable?
    public let groupactivation: AnyCodable?

    public init(
        key: String,
        thinkinglevel: AnyCodable?,
        verboselevel: AnyCodable?,
        groupactivation: AnyCodable?
    ) {
        self.key = key
        self.thinkinglevel = thinkinglevel
        self.verboselevel = verboselevel
        self.groupactivation = groupactivation
    }
    private enum CodingKeys: String, CodingKey {
        case key
        case thinkinglevel = "thinkingLevel"
        case verboselevel = "verboseLevel"
        case groupactivation = "groupActivation"
    }
}

public struct SessionsResetParams: Codable {
    public let key: String

    public init(
        key: String
    ) {
        self.key = key
    }
    private enum CodingKeys: String, CodingKey {
        case key
    }
}

public struct SessionsDeleteParams: Codable {
    public let key: String
    public let deletetranscript: Bool?

    public init(
        key: String,
        deletetranscript: Bool?
    ) {
        self.key = key
        self.deletetranscript = deletetranscript
    }
    private enum CodingKeys: String, CodingKey {
        case key
        case deletetranscript = "deleteTranscript"
    }
}

public struct SessionsCompactParams: Codable {
    public let key: String
    public let maxlines: Int?

    public init(
        key: String,
        maxlines: Int?
    ) {
        self.key = key
        self.maxlines = maxlines
    }
    private enum CodingKeys: String, CodingKey {
        case key
        case maxlines = "maxLines"
    }
}

public struct ConfigGetParams: Codable {
}

public struct ConfigSetParams: Codable {
    public let raw: String

    public init(
        raw: String
    ) {
        self.raw = raw
    }
    private enum CodingKeys: String, CodingKey {
        case raw
    }
}

public struct ProvidersStatusParams: Codable {
    public let probe: Bool?
    public let timeoutms: Int?

    public init(
        probe: Bool?,
        timeoutms: Int?
    ) {
        self.probe = probe
        self.timeoutms = timeoutms
    }
    private enum CodingKeys: String, CodingKey {
        case probe
        case timeoutms = "timeoutMs"
    }
}

public struct WebLoginStartParams: Codable {
    public let force: Bool?
    public let timeoutms: Int?
    public let verbose: Bool?

    public init(
        force: Bool?,
        timeoutms: Int?,
        verbose: Bool?
    ) {
        self.force = force
        self.timeoutms = timeoutms
        self.verbose = verbose
    }
    private enum CodingKeys: String, CodingKey {
        case force
        case timeoutms = "timeoutMs"
        case verbose
    }
}

public struct WebLoginWaitParams: Codable {
    public let timeoutms: Int?

    public init(
        timeoutms: Int?
    ) {
        self.timeoutms = timeoutms
    }
    private enum CodingKeys: String, CodingKey {
        case timeoutms = "timeoutMs"
    }
}

public struct ModelChoice: Codable {
    public let id: String
    public let name: String
    public let provider: String
    public let contextwindow: Int?

    public init(
        id: String,
        name: String,
        provider: String,
        contextwindow: Int?
    ) {
        self.id = id
        self.name = name
        self.provider = provider
        self.contextwindow = contextwindow
    }
    private enum CodingKeys: String, CodingKey {
        case id
        case name
        case provider
        case contextwindow = "contextWindow"
    }
}

public struct ModelsListParams: Codable {
}

public struct ModelsListResult: Codable {
    public let models: [ModelChoice]

    public init(
        models: [ModelChoice]
    ) {
        self.models = models
    }
    private enum CodingKeys: String, CodingKey {
        case models
    }
}

public struct SkillsStatusParams: Codable {
}

public struct SkillsInstallParams: Codable {
    public let name: String
    public let installid: String
    public let timeoutms: Int?

    public init(
        name: String,
        installid: String,
        timeoutms: Int?
    ) {
        self.name = name
        self.installid = installid
        self.timeoutms = timeoutms
    }
    private enum CodingKeys: String, CodingKey {
        case name
        case installid = "installId"
        case timeoutms = "timeoutMs"
    }
}

public struct SkillsUpdateParams: Codable {
    public let skillkey: String
    public let enabled: Bool?
    public let apikey: String?
    public let env: [String: AnyCodable]?

    public init(
        skillkey: String,
        enabled: Bool?,
        apikey: String?,
        env: [String: AnyCodable]?
    ) {
        self.skillkey = skillkey
        self.enabled = enabled
        self.apikey = apikey
        self.env = env
    }
    private enum CodingKeys: String, CodingKey {
        case skillkey = "skillKey"
        case enabled
        case apikey = "apiKey"
        case env
    }
}

public struct CronJob: Codable {
    public let id: String
    public let name: String
    public let description: String?
    public let enabled: Bool
    public let createdatms: Int
    public let updatedatms: Int
    public let schedule: AnyCodable
    public let sessiontarget: AnyCodable
    public let wakemode: AnyCodable
    public let payload: AnyCodable
    public let isolation: [String: AnyCodable]?
    public let state: [String: AnyCodable]

    public init(
        id: String,
        name: String,
        description: String?,
        enabled: Bool,
        createdatms: Int,
        updatedatms: Int,
        schedule: AnyCodable,
        sessiontarget: AnyCodable,
        wakemode: AnyCodable,
        payload: AnyCodable,
        isolation: [String: AnyCodable]?,
        state: [String: AnyCodable]
    ) {
        self.id = id
        self.name = name
        self.description = description
        self.enabled = enabled
        self.createdatms = createdatms
        self.updatedatms = updatedatms
        self.schedule = schedule
        self.sessiontarget = sessiontarget
        self.wakemode = wakemode
        self.payload = payload
        self.isolation = isolation
        self.state = state
    }
    private enum CodingKeys: String, CodingKey {
        case id
        case name
        case description
        case enabled
        case createdatms = "createdAtMs"
        case updatedatms = "updatedAtMs"
        case schedule
        case sessiontarget = "sessionTarget"
        case wakemode = "wakeMode"
        case payload
        case isolation
        case state
    }
}

public struct CronListParams: Codable {
    public let includedisabled: Bool?

    public init(
        includedisabled: Bool?
    ) {
        self.includedisabled = includedisabled
    }
    private enum CodingKeys: String, CodingKey {
        case includedisabled = "includeDisabled"
    }
}

public struct CronStatusParams: Codable {
}

public struct CronAddParams: Codable {
    public let name: String
    public let description: String?
    public let enabled: Bool?
    public let schedule: AnyCodable
    public let sessiontarget: AnyCodable
    public let wakemode: AnyCodable
    public let payload: AnyCodable
    public let isolation: [String: AnyCodable]?

    public init(
        name: String,
        description: String?,
        enabled: Bool?,
        schedule: AnyCodable,
        sessiontarget: AnyCodable,
        wakemode: AnyCodable,
        payload: AnyCodable,
        isolation: [String: AnyCodable]?
    ) {
        self.name = name
        self.description = description
        self.enabled = enabled
        self.schedule = schedule
        self.sessiontarget = sessiontarget
        self.wakemode = wakemode
        self.payload = payload
        self.isolation = isolation
    }
    private enum CodingKeys: String, CodingKey {
        case name
        case description
        case enabled
        case schedule
        case sessiontarget = "sessionTarget"
        case wakemode = "wakeMode"
        case payload
        case isolation
    }
}

public struct CronUpdateParams: Codable {
    public let id: String
    public let patch: [String: AnyCodable]

    public init(
        id: String,
        patch: [String: AnyCodable]
    ) {
        self.id = id
        self.patch = patch
    }
    private enum CodingKeys: String, CodingKey {
        case id
        case patch
    }
}

public struct CronRemoveParams: Codable {
    public let id: String

    public init(
        id: String
    ) {
        self.id = id
    }
    private enum CodingKeys: String, CodingKey {
        case id
    }
}

public struct CronRunParams: Codable {
    public let id: String
    public let mode: AnyCodable?

    public init(
        id: String,
        mode: AnyCodable?
    ) {
        self.id = id
        self.mode = mode
    }
    private enum CodingKeys: String, CodingKey {
        case id
        case mode
    }
}

public struct CronRunsParams: Codable {
    public let id: String
    public let limit: Int?

    public init(
        id: String,
        limit: Int?
    ) {
        self.id = id
        self.limit = limit
    }
    private enum CodingKeys: String, CodingKey {
        case id
        case limit
    }
}

public struct CronRunLogEntry: Codable {
    public let ts: Int
    public let jobid: String
    public let action: String
    public let status: AnyCodable?
    public let error: String?
    public let summary: String?
    public let runatms: Int?
    public let durationms: Int?
    public let nextrunatms: Int?

    public init(
        ts: Int,
        jobid: String,
        action: String,
        status: AnyCodable?,
        error: String?,
        summary: String?,
        runatms: Int?,
        durationms: Int?,
        nextrunatms: Int?
    ) {
        self.ts = ts
        self.jobid = jobid
        self.action = action
        self.status = status
        self.error = error
        self.summary = summary
        self.runatms = runatms
        self.durationms = durationms
        self.nextrunatms = nextrunatms
    }
    private enum CodingKeys: String, CodingKey {
        case ts
        case jobid = "jobId"
        case action
        case status
        case error
        case summary
        case runatms = "runAtMs"
        case durationms = "durationMs"
        case nextrunatms = "nextRunAtMs"
    }
}

public struct ChatHistoryParams: Codable {
    public let sessionkey: String
    public let limit: Int?

    public init(
        sessionkey: String,
        limit: Int?
    ) {
        self.sessionkey = sessionkey
        self.limit = limit
    }
    private enum CodingKeys: String, CodingKey {
        case sessionkey = "sessionKey"
        case limit
    }
}

public struct ChatSendParams: Codable {
    public let sessionkey: String
    public let message: String
    public let thinking: String?
    public let deliver: Bool?
    public let attachments: [AnyCodable]?
    public let timeoutms: Int?
    public let idempotencykey: String

    public init(
        sessionkey: String,
        message: String,
        thinking: String?,
        deliver: Bool?,
        attachments: [AnyCodable]?,
        timeoutms: Int?,
        idempotencykey: String
    ) {
        self.sessionkey = sessionkey
        self.message = message
        self.thinking = thinking
        self.deliver = deliver
        self.attachments = attachments
        self.timeoutms = timeoutms
        self.idempotencykey = idempotencykey
    }
    private enum CodingKeys: String, CodingKey {
        case sessionkey = "sessionKey"
        case message
        case thinking
        case deliver
        case attachments
        case timeoutms = "timeoutMs"
        case idempotencykey = "idempotencyKey"
    }
}

public struct ChatAbortParams: Codable {
    public let sessionkey: String
    public let runid: String

    public init(
        sessionkey: String,
        runid: String
    ) {
        self.sessionkey = sessionkey
        self.runid = runid
    }
    private enum CodingKeys: String, CodingKey {
        case sessionkey = "sessionKey"
        case runid = "runId"
    }
}

public struct ChatEvent: Codable {
    public let runid: String
    public let sessionkey: String
    public let seq: Int
    public let state: AnyCodable
    public let message: AnyCodable?
    public let errormessage: String?
    public let usage: AnyCodable?
    public let stopreason: String?

    public init(
        runid: String,
        sessionkey: String,
        seq: Int,
        state: AnyCodable,
        message: AnyCodable?,
        errormessage: String?,
        usage: AnyCodable?,
        stopreason: String?
    ) {
        self.runid = runid
        self.sessionkey = sessionkey
        self.seq = seq
        self.state = state
        self.message = message
        self.errormessage = errormessage
        self.usage = usage
        self.stopreason = stopreason
    }
    private enum CodingKeys: String, CodingKey {
        case runid = "runId"
        case sessionkey = "sessionKey"
        case seq
        case state
        case message
        case errormessage = "errorMessage"
        case usage
        case stopreason = "stopReason"
    }
}

public struct TickEvent: Codable {
    public let ts: Int

    public init(
        ts: Int
    ) {
        self.ts = ts
    }
    private enum CodingKeys: String, CodingKey {
        case ts
    }
}

public struct ShutdownEvent: Codable {
    public let reason: String
    public let restartexpectedms: Int?

    public init(
        reason: String,
        restartexpectedms: Int?
    ) {
        self.reason = reason
        self.restartexpectedms = restartexpectedms
    }
    private enum CodingKeys: String, CodingKey {
        case reason
        case restartexpectedms = "restartExpectedMs"
    }
}

public enum GatewayFrame: Codable {
    case req(RequestFrame)
    case res(ResponseFrame)
    case event(EventFrame)
    case unknown(type: String, raw: [String: AnyCodable])

    private enum CodingKeys: String, CodingKey {
        case type
    }

    public init(from decoder: Decoder) throws {
        let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
        let type = try typeContainer.decode(String.self, forKey: .type)
        switch type {
        case "req":
            self = .req(try RequestFrame(from: decoder))
        case "res":
            self = .res(try ResponseFrame(from: decoder))
        case "event":
            self = .event(try EventFrame(from: decoder))
        default:
            let container = try decoder.singleValueContainer()
            let raw = try container.decode([String: AnyCodable].self)
            self = .unknown(type: type, raw: raw)
        }
    }

    public func encode(to encoder: Encoder) throws {
        switch self {
        case .req(let v): try v.encode(to: encoder)
        case .res(let v): try v.encode(to: encoder)
        case .event(let v): try v.encode(to: encoder)
        case .unknown(_, let raw):
            var container = encoder.singleValueContainer()
            try container.encode(raw)
        }
    }

}
