diff --git a/Sources/Cloud/CloudPresenceEntry.swift b/Sources/Cloud/CloudPresenceEntry.swift new file mode 100644 index 000000000000..8d744044f5b7 --- /dev/null +++ b/Sources/Cloud/CloudPresenceEntry.swift @@ -0,0 +1,119 @@ +import Foundation + +/// Where one teammate points inside a cmux-tui surface. Mirrors the daemon's +/// `PresenceAnchor` (`cmux-tui/spec/presence.md`). +enum CloudPresenceAnchor: Equatable, Sendable { + /// A terminal cell. `row` and `col` are inside the publisher's viewport; + /// `scrollOffset` is how many rows that viewport sits above the live + /// bottom, so a viewer at another offset can shift the row. + case cell(row: Int, col: Int, scrollOffset: UInt64) + /// A browser or display point in CSS/document pixels. + case point(x: Double, y: Double) + + var json: [String: Any] { + switch self { + case let .cell(row, col, scrollOffset): + return ["kind": "cell", "row": row, "col": col, "scroll_offset": scrollOffset] + case let .point(x, y): + return ["kind": "point", "x": x, "y": y] + } + } + + init?(json: Any?) { + guard let object = json as? [String: Any], let kind = object["kind"] as? String else { return nil } + switch kind { + case "cell": + guard let row = CloudPresenceEntry.int(object["row"]), + let col = CloudPresenceEntry.int(object["col"]) else { return nil } + let offset = (object["scroll_offset"] as? NSNumber)?.uint64Value ?? 0 + self = .cell(row: row, col: col, scrollOffset: offset) + case "point": + guard let x = (object["x"] as? NSNumber)?.doubleValue, + let y = (object["y"] as? NSNumber)?.doubleValue else { return nil } + self = .point(x: x, y: y) + default: + return nil + } + } + + /// The row a viewer whose viewport sits `viewerScrollOffset` rows above + /// the live bottom must draw this cell on. Nil when the row is off screen. + func viewerRow(viewerScrollOffset: UInt64, rows: Int) -> Int? { + guard case let .cell(row, _, publisherOffset) = self else { return nil } + let shifted = Int64(row) + Int64(viewerScrollOffset) - Int64(publisherOffset) + guard shifted >= 0, shifted < Int64(rows) else { return nil } + return Int(shifted) + } +} + +enum CloudPresenceHighlightMode: String, Sendable { + /// Fades on the viewer after a couple of seconds. + case laser + /// Stays until the publisher clears it or disconnects. + case pin +} + +struct CloudPresenceHighlight: Equatable, Sendable { + let start: CloudPresenceAnchor + let end: CloudPresenceAnchor + let mode: CloudPresenceHighlightMode + + var json: [String: Any] { + ["start": start.json, "end": end.json, "mode": mode.rawValue] + } + + init(start: CloudPresenceAnchor, end: CloudPresenceAnchor, mode: CloudPresenceHighlightMode) { + self.start = start + self.end = end + self.mode = mode + } + + init?(json: Any?) { + guard let object = json as? [String: Any], + let start = CloudPresenceAnchor(json: object["start"]), + let end = CloudPresenceAnchor(json: object["end"]), + let mode = (object["mode"] as? String).flatMap(CloudPresenceHighlightMode.init(rawValue:)) else { + return nil + } + self.init(start: start, end: end, mode: mode) + } +} + +/// One `presence-changed` payload: the latest pointer and highlight of one +/// daemon connection. `surface == nil` means that connection cleared its +/// presence, disconnected, or its surface exited. +struct CloudPresenceEntry: Equatable, Sendable { + let client: UInt64 + let name: String? + let kind: String? + /// Palette slot in `0..<8`, stable for the connection. + let color: Int + let surface: UInt64? + let pointer: CloudPresenceAnchor? + let highlight: CloudPresenceHighlight? + let updatedAtMs: UInt64 + let generation: UInt64 + + var isCleared: Bool { surface == nil } + + init?(json object: [String: Any]) { + guard let client = (object["client"] as? NSNumber)?.uint64Value, + let generation = (object["generation"] as? NSNumber)?.uint64Value else { return nil } + self.client = client + name = object["name"] as? String + kind = object["kind"] as? String + color = Int((object["color"] as? NSNumber)?.intValue ?? 0) & 7 + surface = (object["surface"] as? NSNumber).flatMap { $0.uint64Value > 0 ? $0.uint64Value : nil } + pointer = CloudPresenceAnchor(json: object["pointer"]) + highlight = CloudPresenceHighlight(json: object["highlight"]) + updatedAtMs = (object["updated_at_ms"] as? NSNumber)?.uint64Value ?? 0 + self.generation = generation + } + + static func int(_ value: Any?) -> Int? { + guard let number = value as? NSNumber else { return nil } + let signed = number.int64Value + guard signed >= 0, signed <= Int64(Int32.max) else { return nil } + return Int(signed) + } +} diff --git a/Sources/Cloud/CloudPresenceLink.swift b/Sources/Cloud/CloudPresenceLink.swift new file mode 100644 index 000000000000..753cd908548b --- /dev/null +++ b/Sources/Cloud/CloudPresenceLink.swift @@ -0,0 +1,229 @@ +import Foundation + +/// One presence-only control connection per cloud machine. +/// +/// The link never attaches a surface. It identifies, names itself, subscribes +/// with `presence_only`, and then forwards every `presence-changed` frame to +/// its owner while publishing this Mac's own pointer at a bounded rate. +/// When the socket closes the link reports `.disconnected` and retries with a +/// bounded backoff while its pane registration remains alive. +@MainActor +final class CloudPresenceLink { + enum Phase: Equatable { + case connecting + case ready + case disconnected + } + + /// Updates faster than this are dropped on the sender; the daemon also + /// caps at 240/s and the event bus coalesces per client, so a dropped + /// move is replaced by the next one within a frame. + static let minimumPublishInterval: TimeInterval = 1.0 / 30.0 + + let machineID: String + let socketPath: String + private(set) var phase: Phase = .connecting + private(set) var serverSupportsPresence = false + + private let commandBuilder = CloudTuiManualIOCommand() + private let clientName: String + private var connection: CloudTuiManualIOConnection? + private var connectTask: Task? + private var eventTask: Task? + private var reconnectTask: Task? + private var nextRequestID: UInt64 = 1 + private var identifyRequestID: UInt64 = 0 + private var listClientsRequestID: UInt64 = 0 + private var selfClientID: UInt64? + private var reconnectAttempt = 0 + private var stopping = false + private var lastPublish: TimeInterval = 0 + private var lastPublished: (surface: UInt64, pointer: CloudPresenceAnchor?, highlight: CloudPresenceHighlight?)? + private let onEntry: @MainActor (CloudPresenceEntry) -> Void + private let onPhaseChange: @MainActor (CloudPresenceLink) -> Void + + init( + machineID: String, + socketPath: String, + clientName: String, + onEntry: @escaping @MainActor (CloudPresenceEntry) -> Void, + onPhaseChange: @escaping @MainActor (CloudPresenceLink) -> Void + ) { + self.machineID = machineID + self.socketPath = socketPath + self.clientName = clientName + self.onEntry = onEntry + self.onPhaseChange = onPhaseChange + startConnection() + } + + private func startConnection() { + guard !stopping else { return } + phase = .connecting + selfClientID = nil + connectTask = Task { @MainActor [weak self] in + guard let self else { return } + let connection = CloudTuiManualIOConnection( + socketPath: socketPath, + queue: DispatchQueue(label: "com.cmux.cloud-presence", qos: .userInitiated) + ) + do { + try await connection.start() + } catch { + connection.close() + guard !Task.isCancelled else { return } + self.transition(to: .disconnected) + return + } + guard !Task.isCancelled, !self.stopping, self.phase == .connecting else { + connection.close() + return + } + self.connection = connection + self.startEventTask(connection) + let identify = self.takeRequestID() + self.identifyRequestID = identify + connection.send(self.commandBuilder.identify(requestID: identify)) + connection.send( + self.commandBuilder.setPresenceClientInfo( + name: clientName, + kind: "mac", + requestID: self.takeRequestID() + ) + ) + } + } + + func stop() { + stopping = true + connectTask?.cancel() + eventTask?.cancel() + reconnectTask?.cancel() + reconnectTask = nil + if phase == .ready, let connection { + connection.send(commandBuilder.presenceClear(requestID: takeRequestID())) + } + connection?.close() + connection = nil + transition(to: .disconnected) + } + + /// Publishes a pointer and highlight, or a pointer-less state when the + /// mouse left the pane. Identical repeats are dropped. + func publish(surfaceID: UInt64, pointer: CloudPresenceAnchor?, highlight: CloudPresenceHighlight?) { + guard phase == .ready, serverSupportsPresence, let connection else { return } + if let last = lastPublished, + last.surface == surfaceID, last.pointer == pointer, last.highlight == highlight { + return + } + let now = Date().timeIntervalSinceReferenceDate + // Pointer moves are throttled; a highlight edge or a pointer clear is + // always sent so the last state on the wire is the settled one. + let settled = pointer == nil || highlight != lastPublished?.highlight || surfaceID != lastPublished?.surface + if !settled, now - lastPublish < Self.minimumPublishInterval { return } + lastPublish = now + lastPublished = (surfaceID, pointer, highlight) + connection.send( + commandBuilder.presenceUpdate( + surfaceID: surfaceID, + pointer: pointer, + highlight: highlight, + requestID: takeRequestID() + ) + ) + } + + func clear() { + guard phase == .ready, serverSupportsPresence, let connection else { return } + guard lastPublished != nil else { return } + lastPublished = nil + connection.send(commandBuilder.presenceClear(requestID: takeRequestID())) + } + + private func startEventTask(_ connection: CloudTuiManualIOConnection) { + eventTask?.cancel() + eventTask = Task { @MainActor [weak self, connection] in + for await frame in connection.events { + guard let self, !Task.isCancelled else { return } + self.handle(frame: frame, on: connection) + } + guard let self, self.connection === connection else { return } + self.transition(to: .disconnected) + } + } + + private func handle(frame: CloudTuiManualIOFrame, on connection: CloudTuiManualIOConnection) { + switch frame { + case let .presence(entry): + guard entry.client != selfClientID else { return } + onEntry(entry) + case let .response(requestID, ok, _, capabilities, _, _, _, clientID): + if requestID == identifyRequestID { + identifyRequestID = 0 + guard ok else { + transition(to: .disconnected) + return + } + serverSupportsPresence = capabilities.contains(commandBuilder.presenceCapability) + guard serverSupportsPresence else { + transition(to: .ready) + return + } + let listRequestID = takeRequestID() + listClientsRequestID = listRequestID + connection.send(commandBuilder.listClients(requestID: listRequestID)) + return + } + guard requestID == listClientsRequestID else { return } + listClientsRequestID = 0 + guard ok, let clientID else { + transition(to: .disconnected) + return + } + selfClientID = clientID + connection.send(commandBuilder.subscribePresence(requestID: takeRequestID())) + transition(to: .ready) + case .snapshot, .output, .resized, .colorsChanged, .detached: + return + case .overflow: + transition(to: .disconnected) + } + } + + private func takeRequestID() -> UInt64 { + defer { nextRequestID &+= 1 } + return nextRequestID + } + + private func transition(to phase: Phase) { + guard self.phase != phase else { + if phase == .disconnected { scheduleReconnect() } + return + } + self.phase = phase + if phase == .ready { + reconnectAttempt = 0 + } else if phase == .disconnected { + scheduleReconnect() + } + onPhaseChange(self) + } + + private func scheduleReconnect() { + guard !stopping, reconnectTask == nil else { return } + let delay = min(30, 1 << min(reconnectAttempt, 5)) + reconnectAttempt = min(reconnectAttempt + 1, 5) + reconnectTask = Task { @MainActor [weak self] in + guard let self else { return } + do { + try await Task.sleep(nanoseconds: UInt64(delay) * 1_000_000_000) + } catch { + return + } + guard !Task.isCancelled, !self.stopping else { return } + self.reconnectTask = nil + self.transition(to: .connecting) + self.startConnection() + } + } +} diff --git a/Sources/Cloud/CloudPresenceOverlayView.swift b/Sources/Cloud/CloudPresenceOverlayView.swift new file mode 100644 index 000000000000..f61b6caddd65 --- /dev/null +++ b/Sources/Cloud/CloudPresenceOverlayView.swift @@ -0,0 +1,246 @@ +import AppKit + +/// Draws teammates' pointers and highlights above one cloud terminal pane. +/// +/// Click-through and hit-test transparent, so it never steals input from the +/// Ghostty surface below. Geometry (cell size, grid, scroll offset) is fed by +/// the owning scroll view; the overlay only maps daemon anchors to rectangles. +final class CloudPresenceOverlayView: NSView { + struct Geometry: Equatable { + var cellSize: CGSize + var columns: Int + var rows: Int + /// Rows this viewer's viewport sits above the live bottom. + var scrollOffset: UInt64 + var contentInset: CGPoint + } + + /// A laser highlight older than this is not drawn. + static let laserLifetime: TimeInterval = 2.5 + /// A pointer that has not moved for this long is not drawn. + static let pointerLifetime: TimeInterval = 4.0 + + static let palette: [NSColor] = [ + NSColor(srgbRed: 0.98, green: 0.36, blue: 0.36, alpha: 1), + NSColor(srgbRed: 0.26, green: 0.62, blue: 1.00, alpha: 1), + NSColor(srgbRed: 0.24, green: 0.80, blue: 0.48, alpha: 1), + NSColor(srgbRed: 0.98, green: 0.70, blue: 0.20, alpha: 1), + NSColor(srgbRed: 0.72, green: 0.44, blue: 0.98, alpha: 1), + NSColor(srgbRed: 0.20, green: 0.80, blue: 0.86, alpha: 1), + NSColor(srgbRed: 0.98, green: 0.48, blue: 0.76, alpha: 1), + NSColor(srgbRed: 0.64, green: 0.76, blue: 0.24, alpha: 1), + ] + + var geometry = Geometry(cellSize: .zero, columns: 0, rows: 0, scrollOffset: 0, contentInset: .zero) { + didSet { if geometry != oldValue { needsDisplay = true } } + } + + private(set) var entries: [CloudPresenceEntry] = [] { + didSet { if entries != oldValue { needsDisplay = true } } + } + + private var fadeTimer: DispatchSourceTimer? + private var laserStartTimes: [UInt64: UInt64] = [:] + + override var acceptsFirstResponder: Bool { false } + override var isFlipped: Bool { true } + + override func hitTest(_ point: NSPoint) -> NSView? { nil } + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + wantsLayer = true + layer?.backgroundColor = NSColor.clear.cgColor + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) not implemented") + } + + deinit { + fadeTimer?.cancel() + } + + func apply(entries: [CloudPresenceEntry]) { + let now = Self.now() + let previousEntries = self.entries + var nextLaserStartTimes: [UInt64: UInt64] = [:] + for entry in entries { + guard let highlight = entry.highlight, highlight.mode == .laser else { continue } + if let previous = previousEntries.first(where: { $0.client == entry.client }), + previous.highlight == entry.highlight, + let start = laserStartTimes[entry.client] { + nextLaserStartTimes[entry.client] = start + } else { + nextLaserStartTimes[entry.client] = min(entry.updatedAtMs, now) + } + } + laserStartTimes = nextLaserStartTimes + self.entries = entries + isHidden = entries.isEmpty + scheduleFadeIfNeeded() + } + + /// Laser highlights and idle pointers age out on the viewer's clock, so + /// keep redrawing while anything on screen can still expire. + private func scheduleFadeIfNeeded() { + fadeTimer?.cancel() + fadeTimer = nil + guard !entries.isEmpty else { return } + let timer = DispatchSource.makeTimerSource(queue: .main) + timer.schedule(deadline: .now() + 0.25, repeating: 0.25) + timer.setEventHandler { [weak self] in + guard let self else { return } + self.needsDisplay = true + if !self.entries.contains(where: { self.isVisible($0, now: Self.now()) }) { + self.fadeTimer?.cancel() + self.fadeTimer = nil + } + } + timer.resume() + fadeTimer = timer + } + + private static func now() -> UInt64 { + UInt64(Date().timeIntervalSince1970 * 1000) + } + + private func age(of entry: CloudPresenceEntry, now: UInt64) -> TimeInterval { + guard now > entry.updatedAtMs else { return 0 } + return TimeInterval(now - entry.updatedAtMs) / 1000 + } + + private func isVisible(_ entry: CloudPresenceEntry, now: UInt64) -> Bool { + let age = age(of: entry, now: now) + if entry.pointer != nil, age < Self.pointerLifetime { return true } + if let highlight = entry.highlight { + return highlight.mode == .pin || laserAge(of: entry, now: now) < Self.laserLifetime + } + return false + } + + private func laserAge(of entry: CloudPresenceEntry, now: UInt64) -> TimeInterval { + let start = laserStartTimes[entry.client] ?? entry.updatedAtMs + guard now > start else { return 0 } + return TimeInterval(now - start) / 1000 + } + + // MARK: Drawing + + override func draw(_ dirtyRect: NSRect) { + guard geometry.cellSize.width > 0, geometry.cellSize.height > 0, geometry.rows > 0 else { return } + let now = Self.now() + for entry in entries { + let color = Self.palette[entry.color & 7] + let age = age(of: entry, now: now) + if let highlight = entry.highlight { + let alpha: CGFloat + switch highlight.mode { + case .pin: + alpha = 0.28 + case .laser: + let highlightAge = laserAge(of: entry, now: now) + alpha = highlightAge < Self.laserLifetime + ? 0.34 * CGFloat(max(0, 1 - highlightAge / Self.laserLifetime)) + : 0 + } + if alpha > 0 { + drawHighlight(highlight, color: color.withAlphaComponent(alpha)) + } + } + if let pointer = entry.pointer, age < Self.pointerLifetime, + let rect = cellRect(for: pointer) { + drawPointer(at: rect, color: color, label: entry.name ?? "client \(entry.client)") + } + } + } + + private func cellRect(for anchor: CloudPresenceAnchor) -> CGRect? { + guard case let .cell(_, col, _) = anchor, + let row = anchor.viewerRow(viewerScrollOffset: geometry.scrollOffset, rows: geometry.rows), + col >= 0, col < geometry.columns else { return nil } + return CGRect( + x: geometry.contentInset.x + CGFloat(col) * geometry.cellSize.width, + y: geometry.contentInset.y + CGFloat(row) * geometry.cellSize.height, + width: geometry.cellSize.width, + height: geometry.cellSize.height + ) + } + + /// A cell range is drawn like a text selection: partial first and last + /// rows, full rows between. Rows off screen are skipped. + private func drawHighlight(_ highlight: CloudPresenceHighlight, color: NSColor) { + guard case let .cell(startRow, startCol, startOffset) = highlight.start, + case let .cell(endRow, endCol, endOffset) = highlight.end else { return } + var first = ( + row: Int64(startRow) + Int64(geometry.scrollOffset) - Int64(startOffset), + col: startCol + ) + var last = ( + row: Int64(endRow) + Int64(geometry.scrollOffset) - Int64(endOffset), + col: endCol + ) + if first.row > last.row || (first.row == last.row && first.col > last.col) { + swap(&first, &last) + } + color.setFill() + let rowRange = max(first.row, 0)...min(last.row, Int64(geometry.rows - 1)) + guard rowRange.lowerBound <= rowRange.upperBound else { return } + for row in rowRange { + let fromCol = row == first.row ? max(0, min(first.col, geometry.columns - 1)) : 0 + let toCol = row == last.row ? max(0, min(last.col, geometry.columns - 1)) : geometry.columns - 1 + guard fromCol <= toCol else { continue } + let rect = CGRect( + x: geometry.contentInset.x + CGFloat(fromCol) * geometry.cellSize.width, + y: geometry.contentInset.y + CGFloat(row) * geometry.cellSize.height, + width: CGFloat(toCol - fromCol + 1) * geometry.cellSize.width, + height: geometry.cellSize.height + ) + NSBezierPath(roundedRect: rect.insetBy(dx: -1, dy: -0.5), xRadius: 2, yRadius: 2).fill() + } + } + + private func drawPointer(at cell: CGRect, color: NSColor, label: String) { + // Cell frame. + color.withAlphaComponent(0.9).setStroke() + let frame = NSBezierPath(roundedRect: cell.insetBy(dx: -1, dy: -1), xRadius: 2, yRadius: 2) + frame.lineWidth = 1.5 + frame.stroke() + + // Arrow cursor anchored at the cell's top-left corner. + let tip = CGPoint(x: cell.minX, y: cell.minY) + let arrow = NSBezierPath() + arrow.move(to: tip) + arrow.line(to: CGPoint(x: tip.x, y: tip.y + 13)) + arrow.line(to: CGPoint(x: tip.x + 3.5, y: tip.y + 10)) + arrow.line(to: CGPoint(x: tip.x + 6, y: tip.y + 15)) + arrow.line(to: CGPoint(x: tip.x + 8, y: tip.y + 14)) + arrow.line(to: CGPoint(x: tip.x + 5.5, y: tip.y + 9)) + arrow.line(to: CGPoint(x: tip.x + 10, y: tip.y + 9)) + arrow.close() + color.setFill() + arrow.fill() + NSColor.white.withAlphaComponent(0.85).setStroke() + arrow.lineWidth = 1 + arrow.stroke() + + // Name pill beside the arrow. + let attributes: [NSAttributedString.Key: Any] = [ + .font: NSFont.systemFont(ofSize: 10, weight: .semibold), + .foregroundColor: NSColor.white, + ] + let text = NSAttributedString(string: label, attributes: attributes) + let size = text.size() + var pill = CGRect( + x: tip.x + 12, + y: tip.y + 12, + width: size.width + 10, + height: size.height + 4 + ) + if pill.maxX > bounds.maxX { pill.origin.x = max(0, bounds.maxX - pill.width) } + if pill.maxY > bounds.maxY { pill.origin.y = max(0, tip.y - pill.height - 2) } + color.setFill() + NSBezierPath(roundedRect: pill, xRadius: pill.height / 2, yRadius: pill.height / 2).fill() + text.draw(at: CGPoint(x: pill.minX + 5, y: pill.minY + 2)) + } +} diff --git a/Sources/Cloud/CloudPresenceStore.swift b/Sources/Cloud/CloudPresenceStore.swift new file mode 100644 index 000000000000..124066eb27cc --- /dev/null +++ b/Sources/Cloud/CloudPresenceStore.swift @@ -0,0 +1,157 @@ +import AppKit +import Foundation + +extension Notification.Name { + /// Posted on the main thread when any presence entry for a machine + /// changes. `userInfo[CloudPresenceStore.machineIDKey]` names the machine. + static let cloudPresenceDidChange = Notification.Name("cmux.cloudPresenceDidChange") +} + +/// The Mac-side presence model: which local panes mirror which daemon +/// surfaces, one presence link per machine, and the latest entry per remote +/// client. Overlays read `entries(forPane:)`; the Ghostty view publishes +/// through `publish`. +@MainActor +final class CloudPresenceStore { + static let shared = CloudPresenceStore() + static let machineIDKey = "machineID" + + struct Pane: Equatable { + var machineID: String + var remoteSurfaceID: UInt64 + var socketPath: String + } + + private var panes: [UUID: Pane] = [:] + private var links: [String: CloudPresenceLink] = [:] + /// machineID -> client -> entry. Cleared entries are removed. + private var entries: [String: [UInt64: CloudPresenceEntry]] = [:] + private var lastPublishedPane: UUID? + + /// The label other clients see next to this Mac's pointer. + var clientName: String = { + let full = NSFullUserName() + return full.isEmpty ? NSUserName() : full + }() + + // MARK: Pane registry + + func registerPane(panelID: UUID, machineID: String, remoteSurfaceID: UInt64, socketPath: String) { + panes[panelID] = Pane(machineID: machineID, remoteSurfaceID: remoteSurfaceID, socketPath: socketPath) + ensureLink(machineID: machineID, socketPath: socketPath) + } + + func updateRemoteSurfaceID(panelID: UUID, remoteSurfaceID: UInt64) { + guard var pane = panes[panelID], pane.remoteSurfaceID != remoteSurfaceID else { return } + pane.remoteSurfaceID = remoteSurfaceID + panes[panelID] = pane + } + + func updateSocketPath(panelID: UUID, socketPath: String) { + guard var pane = panes[panelID], pane.socketPath != socketPath else { return } + pane.socketPath = socketPath + panes[panelID] = pane + ensureLink(machineID: pane.machineID, socketPath: socketPath) + } + + func unregisterPane(panelID: UUID) { + guard let pane = panes.removeValue(forKey: panelID) else { return } + if lastPublishedPane == panelID { + lastPublishedPane = nil + links[pane.machineID]?.clear() + } + if !panes.values.contains(where: { $0.machineID == pane.machineID }) { + links.removeValue(forKey: pane.machineID)?.stop() + if entries.removeValue(forKey: pane.machineID) != nil { + post(machineID: pane.machineID) + } + } + } + + func isPresencePane(_ panelID: UUID) -> Bool { + panes[panelID] != nil + } + + // MARK: Reading + + /// Live entries from other clients that point at this pane's surface. + func entries(forPane panelID: UUID) -> [CloudPresenceEntry] { + guard let pane = panes[panelID], let machineEntries = entries[pane.machineID] else { return [] } + return machineEntries.values + .filter { $0.surface == pane.remoteSurfaceID } + .sorted { $0.client < $1.client } + } + + func machineID(forPane panelID: UUID) -> String? { + panes[panelID]?.machineID + } + + // MARK: Publishing + + func publish(panelID: UUID, pointer: CloudPresenceAnchor?, highlight: CloudPresenceHighlight?) { + guard let pane = panes[panelID] else { return } + if let previous = lastPublishedPane, previous != panelID, + let previousPane = panes[previous], previousPane.machineID != pane.machineID { + links[previousPane.machineID]?.clear() + } + lastPublishedPane = panelID + ensureLink(machineID: pane.machineID, socketPath: pane.socketPath) + .publish(surfaceID: pane.remoteSurfaceID, pointer: pointer, highlight: highlight) + } + + func clear(panelID: UUID) { + guard let pane = panes[panelID], lastPublishedPane == panelID else { return } + lastPublishedPane = nil + links[pane.machineID]?.clear() + } + + // MARK: Links + + @discardableResult + private func ensureLink(machineID: String, socketPath: String) -> CloudPresenceLink { + if let link = links[machineID], link.socketPath == socketPath, link.phase != .disconnected { + return link + } + links[machineID]?.stop() + let link = CloudPresenceLink( + machineID: machineID, + socketPath: socketPath, + clientName: clientName, + onEntry: { [weak self] entry in self?.apply(entry, machineID: machineID) }, + onPhaseChange: { [weak self] link in self?.linkPhaseChanged(link) } + ) + links[machineID] = link + return link + } + + private func linkPhaseChanged(_ link: CloudPresenceLink) { + guard link.phase == .disconnected, links[link.machineID] === link else { return } + // Remote pointers are meaningless without the stream; drop them and + // let the next register/publish re-dial. + if entries.removeValue(forKey: link.machineID) != nil { + post(machineID: link.machineID) + } + } + + private func apply(_ entry: CloudPresenceEntry, machineID: String) { + var machineEntries = entries[machineID] ?? [:] + if let existing = machineEntries[entry.client], existing.generation >= entry.generation { + return + } + if entry.isCleared { + guard machineEntries.removeValue(forKey: entry.client) != nil else { return } + } else { + machineEntries[entry.client] = entry + } + entries[machineID] = machineEntries + post(machineID: machineID) + } + + private func post(machineID: String) { + NotificationCenter.default.post( + name: .cloudPresenceDidChange, + object: self, + userInfo: [Self.machineIDKey: machineID] + ) + } +} diff --git a/Sources/Cloud/CloudTuiManualIOCommand.swift b/Sources/Cloud/CloudTuiManualIOCommand.swift index a0819d9d168e..f607195e37c5 100644 --- a/Sources/Cloud/CloudTuiManualIOCommand.swift +++ b/Sources/Cloud/CloudTuiManualIOCommand.swift @@ -55,6 +55,54 @@ struct CloudTuiManualIOCommand: Sendable { ] } + /// The capability under which the daemon fans out collaboration + /// pointers and highlights (`cmux-tui/spec/presence.md`). + let presenceCapability = "presence-v1" + + /// Advertises a presence-only connection: it never attaches a surface. + func setPresenceClientInfo(name: String, kind: String, requestID: UInt64) -> [String: Any] { + [ + "id": requestID, + "cmd": "set-client-info", + "name": name, + "kind": kind, + "capabilities": [presenceCapability], + ] + } + + /// Subscribes to `presence-changed` and nothing else. + func subscribePresence(requestID: UInt64) -> [String: Any] { + ["id": requestID, "cmd": "subscribe", "presence_only": true] + } + + /// Reads the requesting connection's opaque client id after the handshake. + func listClients(requestID: UInt64) -> [String: Any] { + ["id": requestID, "cmd": "list-clients"] + } + + /// Asks for every live pointer so a late joiner can draw them. + func presenceList(requestID: UInt64) -> [String: Any] { + ["id": requestID, "cmd": "presence-list"] + } + + /// Publishes this connection's pointer and highlight on one surface. + func presenceUpdate( + surfaceID: UInt64, + pointer: CloudPresenceAnchor?, + highlight: CloudPresenceHighlight?, + requestID: UInt64 + ) -> [String: Any] { + var command: [String: Any] = ["id": requestID, "cmd": "presence-update", "surface": surfaceID] + if let pointer { command["pointer"] = pointer.json } + if let highlight { command["highlight"] = highlight.json } + return command + } + + /// Withdraws this connection's presence. + func presenceClear(requestID: UInt64) -> [String: Any] { + ["id": requestID, "cmd": "presence-clear"] + } + /// Claims this connection as the terminal's geometry owner. /// /// A `resize-surface` report is sent before this command. The daemon diff --git a/Sources/Cloud/CloudTuiManualIOFrame.swift b/Sources/Cloud/CloudTuiManualIOFrame.swift index efb74fb0233d..0ef36df28709 100644 --- a/Sources/Cloud/CloudTuiManualIOFrame.swift +++ b/Sources/Cloud/CloudTuiManualIOFrame.swift @@ -14,6 +14,8 @@ enum CloudTuiManualIOFrame: Equatable, Sendable { case colorsChanged(surfaceID: UInt64, colors: CloudTuiRemoteColors) case detached(surfaceID: UInt64) case overflow(surfaceID: UInt64?) + /// A `presence-changed` subscribe event (capability `presence-v1`). + case presence(CloudPresenceEntry) case response( requestID: UInt64, ok: Bool, @@ -21,6 +23,7 @@ enum CloudTuiManualIOFrame: Equatable, Sendable { capabilities: [String], outcome: String?, accepted: Bool?, - error: String? + error: String?, + selfClientID: UInt64? ) } diff --git a/Sources/Cloud/CloudTuiManualIOFrameDecoder.swift b/Sources/Cloud/CloudTuiManualIOFrameDecoder.swift index 6370e26845fc..b68cff9d9244 100644 --- a/Sources/Cloud/CloudTuiManualIOFrameDecoder.swift +++ b/Sources/Cloud/CloudTuiManualIOFrameDecoder.swift @@ -27,11 +27,28 @@ struct CloudTuiManualIOFrameDecoder: Sendable { capabilities: (responseData?["capabilities"] as? [String]) ?? [], outcome: responseData?["outcome"] as? String, accepted: responseData?["accepted"] as? Bool, - error: object["error"] as? String + error: object["error"] as? String, + selfClientID: Self.selfClientID(from: object["data"]) ) } + private static func selfClientID(from value: Any?) -> UInt64? { + guard let clients = value as? [Any] else { return nil } + for client in clients { + guard let object = client as? [String: Any], + object["self"] as? Bool == true, + let clientID = uint64(object["client"]) else { continue } + return clientID + } + return nil + } + private func decodeEvent(_ event: String, object: [String: Any]) -> CloudTuiManualIOFrame? { + // Presence clears carry `surface: null`, so decode it before the + // positive-surface guard that every byte-attach event requires. + if event == "presence-changed" { + return CloudPresenceEntry(json: object).map(CloudTuiManualIOFrame.presence) + } guard let surfaceID = Self.positiveUInt64(object["surface"]) else { if event == "overflow" { return .overflow(surfaceID: nil) } return nil diff --git a/Sources/Cloud/CloudTuiManualMirrorSession.swift b/Sources/Cloud/CloudTuiManualMirrorSession.swift index c4b965f8ca22..b8bce7196aae 100644 --- a/Sources/Cloud/CloudTuiManualMirrorSession.swift +++ b/Sources/Cloud/CloudTuiManualMirrorSession.swift @@ -519,7 +519,11 @@ final class CloudTuiManualMirrorSession { case let .overflow(surfaceID): guard surfaceID == nil || surfaceID == remoteSurfaceID else { return } transitionToDisconnected() - case let .response(requestID, ok, lease, capabilities, outcome, accepted, error): + case .presence: + // Presence rides the per-machine CloudPresenceLink, never a pane + // attachment; this connection never subscribes. + return + case let .response(requestID, ok, lease, capabilities, outcome, accepted, error, _): handleResponse( requestID: requestID, ok: ok, diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift index bf8d2488f7f2..c48bc9994e99 100644 --- a/Sources/GhosttyTerminalView.swift +++ b/Sources/GhosttyTerminalView.swift @@ -3853,6 +3853,10 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { nonisolated let selectionAccessibilitySignal = TerminalSelectionAccessibilitySignal() private var selectionAccessibilityNotifier: TerminalSelectionAccessibilityNotifier? var cellSize: CGSize = .zero + /// Cloud presence: the cell where a Cmd+Shift drag started, while it runs. + var cloudPresenceHighlightAnchor: CloudPresenceAnchor? + /// Cloud presence: the last highlight this pane published. + var cloudPresenceHighlight: CloudPresenceHighlight? private var lastKnownMousePointInView: NSPoint? private let commandClickReleaseRouter = TerminalCommandClickReleaseRouter() private var commandClickReleaseRoutingActive = false @@ -7759,6 +7763,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { override func mouseDown(with event: NSEvent) { if routeInputDuringClipboardRead(event) { return } + if beginCloudPresenceHighlightIfRequested(event) { return } reconcileGhosttyMouseButtons( reason: "mouseDown.preflight", forceButtons: Set([.left]) @@ -7796,6 +7801,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { #if DEBUG cmuxDebugLog("terminal.mouseUp surface=\(terminalSurface?.id.uuidString.prefix(5) ?? "nil") mods=[\(debugModifierString(event.modifierFlags))]") #endif + if finishCloudPresenceHighlight(event) { return } completePendingLeftMouseRelease(with: event) } @@ -8933,6 +8939,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { cmdHeld: event.modifierFlags.contains(.command), suppressPathHover: suppressCommandPathHover ) + publishCloudPresencePointer(at: eventPoint) } override func mouseEntered(with event: NSEvent) { @@ -8978,8 +8985,87 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { window.makeFirstResponder(self) } + // MARK: Cloud presence (pointer + Cmd+Shift-drag highlight) + + /// Columns, rows, and cell size in points for anchor mapping. Nil until + /// the runtime has reported a cell size. + func cloudPresenceGrid() -> (columns: Int, rows: Int, cellSize: CGSize)? { + guard let surface, cellSize.width > 0, cellSize.height > 0 else { return nil } + let size = ghostty_surface_size(surface) + guard size.columns > 0, size.rows > 0 else { return nil } + return (Int(size.columns), Int(size.rows), cellSize) + } + + /// The grid cell under a view point, as a daemon anchor. Mirrors the + /// centered-inset math the word-path resolver uses. + func cloudPresenceCell(at point: NSPoint) -> CloudPresenceAnchor? { + guard let grid = cloudPresenceGrid() else { return nil } + let xInset = max(0, (bounds.width - (CGFloat(grid.columns) * grid.cellSize.width)) / 2) + let yInset = max(0, (bounds.height - (CGFloat(grid.rows) * grid.cellSize.height)) / 2) + let yFromTop = bounds.height - point.y + let row = Int((yFromTop - yInset) / grid.cellSize.height) + let col = Int((point.x - xInset) / grid.cellSize.width) + guard row >= 0, row < grid.rows, col >= 0, col < grid.columns else { return nil } + return .cell(row: row, col: col, scrollOffset: scrollbar?.rowsBelowViewport ?? 0) + } + + private func cloudPresencePanelID() -> UUID? { + guard let panelID = terminalSurface?.id, CloudPresenceStore.shared.isPresencePane(panelID) else { + return nil + } + return panelID + } + + private func publishCloudPresencePointer(at point: NSPoint) { + guard let panelID = cloudPresencePanelID(), cloudPresenceHighlightAnchor == nil else { return } + CloudPresenceStore.shared.publish( + panelID: panelID, + pointer: cloudPresenceCell(at: point), + highlight: cloudPresenceHighlight + ) + } + + private func beginCloudPresenceHighlightIfRequested(_ event: NSEvent) -> Bool { + let flags = event.modifierFlags.intersection(.deviceIndependentFlagsMask) + guard flags.contains([.command, .shift]), !flags.contains(.option), !flags.contains(.control), + let panelID = cloudPresencePanelID() else { return false } + let point = convert(event.locationInWindow, from: nil) + guard let cell = cloudPresenceCell(at: point) else { return false } + focusFromPointerDown() + cloudPresenceHighlightAnchor = cell + cloudPresenceHighlight = CloudPresenceHighlight(start: cell, end: cell, mode: .laser) + CloudPresenceStore.shared.publish(panelID: panelID, pointer: cell, highlight: cloudPresenceHighlight) + return true + } + + private func continueCloudPresenceHighlight(_ event: NSEvent) -> Bool { + guard let anchor = cloudPresenceHighlightAnchor, let panelID = cloudPresencePanelID() else { return false } + let point = convert(event.locationInWindow, from: nil) + guard let cell = cloudPresenceCell(at: point) else { return true } + cloudPresenceHighlight = CloudPresenceHighlight(start: anchor, end: cell, mode: .laser) + CloudPresenceStore.shared.publish(panelID: panelID, pointer: cell, highlight: cloudPresenceHighlight) + return true + } + + private func finishCloudPresenceHighlight(_ event: NSEvent) -> Bool { + guard cloudPresenceHighlightAnchor != nil else { return false } + cloudPresenceHighlightAnchor = nil + guard let panelID = cloudPresencePanelID() else { return true } + let point = convert(event.locationInWindow, from: nil) + CloudPresenceStore.shared.publish( + panelID: panelID, + pointer: cloudPresenceCell(at: point), + highlight: cloudPresenceHighlight + ) + return true + } + override func mouseExited(with event: NSEvent) { if routeInputDuringClipboardRead(event) { return } + if let panelID = terminalSurface?.id, CloudPresenceStore.shared.isPresencePane(panelID), + cloudPresenceHighlightAnchor == nil { + CloudPresenceStore.shared.publish(panelID: panelID, pointer: nil, highlight: cloudPresenceHighlight) + } reconcileGhosttyMouseButtons(reason: "mouseExited") if wordPathHoverActive { wordPathHoverActive = false @@ -8994,6 +9080,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations { override func mouseDragged(with event: NSEvent) { if routeInputDuringClipboardRead(event) { return } + if continueCloudPresenceHighlight(event) { return } synchronizeGhosttyMouseSurfaceIdentity() guard let surface = surface else { return } let mouseState = rememberGhosttyMouseState(from: event) @@ -9740,6 +9827,7 @@ final class GhosttySurfaceScrollView: NSView { private let keyboardCopyModeBadgeIconView: NSImageView private let keyboardCopyModeBadgeLabel: NSTextField let linkHoverIndicatorView: TerminalLinkHoverIndicatorView + let cloudPresenceOverlayView: CloudPresenceOverlayView private let imageTransferIndicatorContainerView: NSView private let imageTransferIndicatorView: NSVisualEffectView private let imageTransferIndicatorSpinner: NSProgressIndicator @@ -9985,6 +10073,7 @@ final class GhosttySurfaceScrollView: NSView { keyboardCopyModeBadgeIconView = NSImageView(frame: .zero) keyboardCopyModeBadgeLabel = NSTextField(labelWithString: terminalKeyboardCopyModeIndicatorText) linkHoverIndicatorView = TerminalLinkHoverIndicatorView(frame: .zero) + cloudPresenceOverlayView = CloudPresenceOverlayView(frame: .zero) imageTransferIndicatorContainerView = NSView(frame: .zero) imageTransferIndicatorView = NSVisualEffectView(frame: .zero) imageTransferIndicatorSpinner = NSProgressIndicator(frame: .zero) @@ -10197,6 +10286,16 @@ final class GhosttySurfaceScrollView: NSView { linkHoverIndicatorView.frame = bounds linkHoverIndicatorView.autoresizingMask = [.width, .height] addSubview(linkHoverIndicatorView) + cloudPresenceOverlayView.frame = bounds + cloudPresenceOverlayView.isHidden = true + addSubview(cloudPresenceOverlayView, positioned: .below, relativeTo: linkHoverIndicatorView) + observers.append(NotificationCenter.default.addObserver( + forName: .cloudPresenceDidChange, + object: nil, + queue: .main + ) { [weak self] _ in + self?.synchronizeCloudPresenceOverlay() + }) scrollView.contentView.postsBoundsChangedNotifications = true observers.append(NotificationCenter.default.addObserver( @@ -10516,6 +10615,11 @@ final class GhosttySurfaceScrollView: NSView { _ = setFrameIfNeeded(notificationRingOverlayView, to: bounds) _ = setFrameIfNeeded(flashOverlayView, to: bounds) _ = setFrameIfNeeded(linkHoverIndicatorView, to: contentFrame) + _ = setFrameIfNeeded(cloudPresenceOverlayView, to: contentFrame) + synchronizeCloudPresenceGeometry() + if let cloudTerminalReconnectOverlayView { + _ = setFrameIfNeeded(cloudTerminalReconnectOverlayView, to: contentFrame) + } synchronizeCloudTerminalReconnectOverlay() if let overlay = searchOverlayHostingView { _ = setFrameIfNeeded(overlay, to: contentFrame) @@ -10586,6 +10690,33 @@ final class GhosttySurfaceScrollView: NSView { return workspace.cloudTerminalReconnectOverlayPresentation(forSurfaceId: terminalSurface.id) } + /// Re-reads teammates' pointers for this pane. Cheap: the store keeps the + /// latest entry per client and this pane filters by its remote surface. + private func synchronizeCloudPresenceOverlay() { + guard let panelID = surfaceView.terminalSurface?.id else { return } + let entries = CloudPresenceStore.shared.entries(forPane: panelID) + if !entries.isEmpty { synchronizeCloudPresenceGeometry() } + cloudPresenceOverlayView.apply(entries: entries) + if !entries.isEmpty, cloudPresenceOverlayView.superview === self { + addSubview(cloudPresenceOverlayView, positioned: .below, relativeTo: linkHoverIndicatorView) + } + } + + private func synchronizeCloudPresenceGeometry() { + guard let grid = surfaceView.cloudPresenceGrid() else { return } + let size = cloudPresenceOverlayView.bounds.size + cloudPresenceOverlayView.geometry = CloudPresenceOverlayView.Geometry( + cellSize: grid.cellSize, + columns: grid.columns, + rows: grid.rows, + scrollOffset: surfaceView.scrollbar?.rowsBelowViewport ?? 0, + contentInset: CGPoint( + x: max(0, (size.width - CGFloat(grid.columns) * grid.cellSize.width) / 2), + y: max(0, (size.height - CGFloat(grid.rows) * grid.cellSize.height) / 2) + ) + ) + } + func synchronizeCloudTerminalReconnectOverlay() { let legacyPresentation = cloudTerminalOverlay.session == nil ? currentCloudTerminalReconnectPresentation() : nil guard cloudTerminalOverlay.session != nil || legacyPresentation != nil || cloudTerminalOverlay.overlay != nil else { return } @@ -13201,6 +13332,7 @@ final class GhosttySurfaceScrollView: NSView { scrollbackViewportIntent = syncDecision.intent let wasVisible = scrollView.hasVerticalScroller surfaceView.scrollbar = scrollbar + synchronizeCloudPresenceGeometry() let isVisible = shouldShowTerminalScrollBar() if wasVisible != isVisible { _ = synchronizeGeometryAndContent( diff --git a/Sources/Surfaces/CmuxTuiSurfaceProvider+ManualMirror.swift b/Sources/Surfaces/CmuxTuiSurfaceProvider+ManualMirror.swift index e2690cb279a6..213fa153ce04 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProvider+ManualMirror.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProvider+ManualMirror.swift @@ -66,6 +66,12 @@ extension CmuxTuiSurfaceProvider { session?.claimGeometry() } manualMirrorSessions[created.panelID] = session + CloudPresenceStore.shared.registerPane( + panelID: created.panelID, + machineID: machineID, + remoteSurfaceID: resolved.surfaceID, + socketPath: connected.socketPath + ) session.reconnect(socketPath: connected.socketPath) return CloudManualMirrorMaterialization( workspaceID: created.workspaceID, diff --git a/Sources/Surfaces/CmuxTuiSurfaceProviders.swift b/Sources/Surfaces/CmuxTuiSurfaceProviders.swift index 5f7e2ebf6265..6a146efe1029 100644 --- a/Sources/Surfaces/CmuxTuiSurfaceProviders.swift +++ b/Sources/Surfaces/CmuxTuiSurfaceProviders.swift @@ -176,7 +176,10 @@ final class CmuxTuiSurfaceProvider: SurfaceProvider { stateRecoveryRefreshQueued = false stateRecoveryCount = 0 eventsFeedWarning = nil - for session in manualMirrorSessions.values { session.stop() } + for (panelID, session) in manualMirrorSessions { + session.stop() + CloudPresenceStore.shared.unregisterPane(panelID: panelID) + } manualMirrorSessions.removeAll() manualMirrorSurfaceIDsSocketPath = nil for task in remoteTerminalProjectionTasks.values { task.cancel() } @@ -359,6 +362,9 @@ final class CmuxTuiSurfaceProvider: SurfaceProvider { switch resolutions[session.terminalID] { case let .resolved(surfaceID): session.updateRemoteSurfaceID(surfaceID) + if let panelID = manualMirrorSessions.first(where: { $0.value === session })?.key { + CloudPresenceStore.shared.updateRemoteSurfaceID(panelID: panelID, remoteSurfaceID: surfaceID) + } reconnectableSessionIDs.insert(ObjectIdentifier(session)) case .exited: // The remote shell ended. Stop reconnecting; the pane @@ -377,9 +383,10 @@ final class CmuxTuiSurfaceProvider: SurfaceProvider { } closePanes(forExitedTerminals: exitedTerminalIDs) } - for session in manualMirrorSessions.values + for (panelID, session) in manualMirrorSessions where reconnectableSessionIDs.contains(ObjectIdentifier(session)) { session.reconnect(socketPath: connected.socketPath) + CloudPresenceStore.shared.updateSocketPath(panelID: panelID, socketPath: connected.socketPath) } } catch { guard isCurrentRefresh(lifecycle: lifecycle, refresh: generation) else { return false } @@ -676,6 +683,7 @@ final class CmuxTuiSurfaceProvider: SurfaceProvider { private func closeManualMirrorPane(panelID: UUID, terminalID: String) { materializedPanels.remove(panelID) manualMirrorSessions.removeValue(forKey: panelID)?.stop() + CloudPresenceStore.shared.unregisterPane(panelID: panelID) guard let workspace = AppDelegate.shared?.workspace(containingSurfaceID: panelID) else { return } SurfacePaneFactory.closeExited(panelID: panelID, in: workspace.id) } @@ -1619,6 +1627,7 @@ final class CmuxTuiSurfaceProvider: SurfaceProvider { browserPaneTasks.removeValue(forKey: projection.panelID)?.cancel() materializedPanels.remove(projection.panelID) manualMirrorSessions.removeValue(forKey: projection.panelID)?.stop() + CloudPresenceStore.shared.unregisterPane(panelID: projection.panelID) } @discardableResult @@ -1626,6 +1635,7 @@ final class CmuxTuiSurfaceProvider: SurfaceProvider { browserPaneTasks.removeValue(forKey: projection.panelID)?.cancel() materializedPanels.remove(projection.panelID) manualMirrorSessions.removeValue(forKey: projection.panelID)?.stop() + CloudPresenceStore.shared.unregisterPane(panelID: projection.panelID) SurfacePaneFactory.close(panelID: projection.panelID, in: projection.workspaceID) return false } diff --git a/cmux-tui/bindings/cpp/.cmux-sdk-manifest.json b/cmux-tui/bindings/cpp/.cmux-sdk-manifest.json index b257833e701b..439de52e33b6 100644 --- a/cmux-tui/bindings/cpp/.cmux-sdk-manifest.json +++ b/cmux-tui/bindings/cpp/.cmux-sdk-manifest.json @@ -2,27 +2,27 @@ "files": [ { "path": "include/cmux/raw/generated/commands.hpp", - "sha256": "8d5251382b2c5f65b3de2d1ab1ec5eab77d399de67025434f10b90d478edc958", - "size": 15390 + "sha256": "b1a72f380dd70374defb1ccfcd5af445e5a4299d3e294cf8cf6dcca47ff45af4", + "size": 15767 }, { "path": "include/cmux/raw/generated/events.hpp", - "sha256": "674427059469e0c5a85579e12bdc3a6a83342328cd783af6dc8cef26500ce13d", - "size": 2052 + "sha256": "1a7ed31f459e87ee5e118766b0051ead1f031af89f4bfc34753fe2de3032d898", + "size": 2074 }, { "path": "include/cmux/raw/generated/models.hpp", - "sha256": "34e6b23b26ef7e5744ce77063baeb5c35a10504bc7be73a355ae8c9513927e6c", - "size": 131707 + "sha256": "f0bdf3ed7161ee68ddf023ff9f88e60e1a6545ce35ff9ad02d0de9ab57610bde", + "size": 136447 }, { "path": "src/raw/generated/protocol.cpp", - "sha256": "529344e42d964a03fd6be2771bb3e491d1268fe501e76a333180d0062ff5b352", - "size": 855339 + "sha256": "308b1248c08d055eb522661b5077ec9d6a5fc2623a171d716d178b7bf279e1f0", + "size": 883330 } ], "format": 1, - "ir_sha256": "8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86", + "ir_sha256": "e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663", "language": "cpp", "mux_protocol": 12, "schema_version": 2 diff --git a/cmux-tui/bindings/cpp/include/cmux/raw/generated/commands.hpp b/cmux-tui/bindings/cpp/include/cmux/raw/generated/commands.hpp index 026e45b57682..9d8181157d36 100644 --- a/cmux-tui/bindings/cpp/include/cmux/raw/generated/commands.hpp +++ b/cmux-tui/bindings/cpp/include/cmux/raw/generated/commands.hpp @@ -102,6 +102,9 @@ class Client { [[nodiscard]] Result pairing_response(const PairingResponseRequest& request, RequestOptions options = {}); [[nodiscard]] Result pane_neighbor(const PaneNeighborRequest& request, RequestOptions options = {}); [[nodiscard]] Result ping(const PingRequest& request = {}, RequestOptions options = {}); + [[nodiscard]] Result presence_clear(const PresenceClearRequest& request = {}, RequestOptions options = {}); + [[nodiscard]] Result presence_list(const PresenceListRequest& request = {}, RequestOptions options = {}); + [[nodiscard]] Result presence_update(const PresenceUpdateRequest& request, RequestOptions options = {}); [[nodiscard]] Result process_info(const ProcessInfoRequest& request, RequestOptions options = {}); [[nodiscard]] Result put_frontend_projection(const PutFrontendProjectionRequest& request, RequestOptions options = {}); [[nodiscard]] Result read_screen(const ReadScreenRequest& request, RequestOptions options = {}); diff --git a/cmux-tui/bindings/cpp/include/cmux/raw/generated/events.hpp b/cmux-tui/bindings/cpp/include/cmux/raw/generated/events.hpp index 75c8ccb4d7b4..088ba8b7a141 100644 --- a/cmux-tui/bindings/cpp/include/cmux/raw/generated/events.hpp +++ b/cmux-tui/bindings/cpp/include/cmux/raw/generated/events.hpp @@ -17,7 +17,7 @@ struct UnknownEvent { }; struct Event { - using Variant = std::variant; + using Variant = std::variant; Variant value; Json raw; [[nodiscard]] std::string_view name() const noexcept; diff --git a/cmux-tui/bindings/cpp/include/cmux/raw/generated/models.hpp b/cmux-tui/bindings/cpp/include/cmux/raw/generated/models.hpp index cb5a83eec9fb..c1f7813c9138 100644 --- a/cmux-tui/bindings/cpp/include/cmux/raw/generated/models.hpp +++ b/cmux-tui/bindings/cpp/include/cmux/raw/generated/models.hpp @@ -14,7 +14,7 @@ namespace cmux::raw { inline constexpr std::uint32_t kMuxProtocolVersion = 12U; -inline constexpr std::string_view kProtocolIrSha256 = "8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86"; +inline constexpr std::string_view kProtocolIrSha256 = "e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663"; struct AgentRecord; enum class AgentReportSource; @@ -76,6 +76,11 @@ struct Pane; enum class PaneDirection; struct PaneNeighborResult; struct PingResult; +struct PresenceAnchor; +struct PresenceEntry; +struct PresenceHighlight; +enum class PresenceHighlightMode; +struct PresenceListResult; struct ProcessInfoResult; struct ProviderWorkspaceMutationResult; struct ReadScreenResult; @@ -194,6 +199,9 @@ struct NotifyRequest; struct PairingResponseRequest; struct PaneNeighborRequest; struct PingRequest; +struct PresenceClearRequest; +struct PresenceListRequest; +struct PresenceUpdateRequest; struct ProcessInfoRequest; struct PutFrontendProjectionRequest; struct ReadScreenRequest; @@ -264,6 +272,7 @@ struct PairingRequestedEvent; struct PairingResolvedEvent; struct PaneAddedEvent; struct PaneClosedEvent; +struct PresenceChangedEvent; struct RenderDeltaEvent; struct RenderStateEvent; struct ResizedEvent; @@ -299,6 +308,8 @@ enum class IdMappingKind; struct LayoutLeaf; struct LayoutSplit; struct LayoutStack; +struct PresenceAnchorCell; +struct PresenceAnchorPoint; enum class TabBrowserSource; enum class TabBrowserStatus; enum class TabKind; @@ -1774,6 +1785,83 @@ struct PingResult { friend bool operator==(const PingResult&, const PingResult&) = default; }; +struct PresenceAnchorCell { + std::uint32_t col{}; + std::uint32_t row{}; + std::optional scroll_offset{}; + friend bool operator==(const PresenceAnchorCell&, const PresenceAnchorCell&) = default; +}; + +struct PresenceAnchorPoint { + double x{}; + double y{}; + friend bool operator==(const PresenceAnchorPoint&, const PresenceAnchorPoint&) = default; +}; + +struct PresenceAnchor { + using Variant = std::variant; + Variant value{}; + friend bool operator==(const PresenceAnchor&, const PresenceAnchor&) = default; +}; + +enum class PresenceHighlightMode { + laser, + pin, +}; + +struct PresenceHighlight { + PresenceAnchor end{}; + PresenceHighlightMode mode{}; + PresenceAnchor start{}; + friend bool operator==(const PresenceHighlight&, const PresenceHighlight&) = default; +}; + +struct PresenceChangedEvent { + std::uint64_t client{}; + std::uint64_t color{}; + std::uint64_t generation{}; + std::optional highlight{}; + std::optional kind{}; + std::optional name{}; + std::optional pointer{}; + std::optional surface{}; + std::uint64_t updated_at_ms{}; + friend bool operator==(const PresenceChangedEvent&, const PresenceChangedEvent&) = default; +}; + +struct PresenceClearRequest { + friend bool operator==(const PresenceClearRequest&, const PresenceClearRequest&) = default; +}; + +struct PresenceEntry { + std::uint64_t client{}; + std::uint64_t color{}; + std::uint64_t generation{}; + std::optional highlight{}; + std::optional kind{}; + std::optional name{}; + std::optional pointer{}; + std::optional surface{}; + std::uint64_t updated_at_ms{}; + friend bool operator==(const PresenceEntry&, const PresenceEntry&) = default; +}; + +struct PresenceListRequest { + friend bool operator==(const PresenceListRequest&, const PresenceListRequest&) = default; +}; + +struct PresenceListResult { + std::vector entries{}; + friend bool operator==(const PresenceListResult&, const PresenceListResult&) = default; +}; + +struct PresenceUpdateRequest { + Field highlight{}; + Field pointer{}; + Id surface{}; + friend bool operator==(const PresenceUpdateRequest&, const PresenceUpdateRequest&) = default; +}; + struct ProcessInfoRequest { Id surface{}; friend bool operator==(const ProcessInfoRequest&, const ProcessInfoRequest&) = default; @@ -2398,6 +2486,7 @@ enum class SubscribeRequestTreeEvents { }; struct SubscribeRequest { + Field presence_only{}; Field surface{}; Field tree_events{}; friend bool operator==(const SubscribeRequest&, const SubscribeRequest&) = default; @@ -3045,6 +3134,36 @@ struct Codec { static Result decode(const Json& value); }; +template <> +struct Codec { + static Result encode(const PresenceAnchor& value); + static Result decode(const Json& value); +}; + +template <> +struct Codec { + static Result encode(const PresenceEntry& value); + static Result decode(const Json& value); +}; + +template <> +struct Codec { + static Result encode(const PresenceHighlight& value); + static Result decode(const Json& value); +}; + +template <> +struct Codec { + static Result encode(const PresenceHighlightMode& value); + static Result decode(const Json& value); +}; + +template <> +struct Codec { + static Result encode(const PresenceListResult& value); + static Result decode(const Json& value); +}; + template <> struct Codec { static Result encode(const ProcessInfoResult& value); @@ -3753,6 +3872,24 @@ struct Codec { static Result decode(const Json& value); }; +template <> +struct Codec { + static Result encode(const PresenceClearRequest& value); + static Result decode(const Json& value); +}; + +template <> +struct Codec { + static Result encode(const PresenceListRequest& value); + static Result decode(const Json& value); +}; + +template <> +struct Codec { + static Result encode(const PresenceUpdateRequest& value); + static Result decode(const Json& value); +}; + template <> struct Codec { static Result encode(const ProcessInfoRequest& value); @@ -4173,6 +4310,12 @@ struct Codec { static Result decode(const Json& value); }; +template <> +struct Codec { + static Result encode(const PresenceChangedEvent& value); + static Result decode(const Json& value); +}; + template <> struct Codec { static Result encode(const RenderDeltaEvent& value); @@ -4383,6 +4526,18 @@ struct Codec { static Result decode(const Json& value); }; +template <> +struct Codec { + static Result encode(const PresenceAnchorCell& value); + static Result decode(const Json& value); +}; + +template <> +struct Codec { + static Result encode(const PresenceAnchorPoint& value); + static Result decode(const Json& value); +}; + template <> struct Codec { static Result encode(const TabBrowserSource& value); diff --git a/cmux-tui/bindings/cpp/src/raw/generated/protocol.cpp b/cmux-tui/bindings/cpp/src/raw/generated/protocol.cpp index ce0134738059..886e17404e5f 100644 --- a/cmux-tui/bindings/cpp/src/raw/generated/protocol.cpp +++ b/cmux-tui/bindings/cpp/src/raw/generated/protocol.cpp @@ -2911,6 +2911,275 @@ Result Codec::decode(const Json& value) { return result; } +Result Codec::encode(const PresenceAnchor& value) { + return encode_value(value.value); +} + +Result Codec::decode(const Json& value) { + auto tag = require_string(value, "kind"); + if (!tag) return std::move(tag).error(); + if (tag.value() == "cell") { + auto decoded = decode_value(value); + if (!decoded) return std::move(decoded).error(); + return PresenceAnchor{PresenceAnchor::Variant(std::move(decoded).value())}; + } + if (tag.value() == "point") { + auto decoded = decode_value(value); + if (!decoded) return std::move(decoded).error(); + return PresenceAnchor{PresenceAnchor::Variant(std::move(decoded).value())}; + } + return make_error(ErrorCode::decode, "unknown PresenceAnchor tag"); +} + +Result Codec::encode(const PresenceEntry& value) { + (void)value; + Json::Object object; + auto encoded_client = encode_value(value.client); + if (!encoded_client) return std::move(encoded_client).error(); + object.emplace("client", std::move(encoded_client).value()); + auto encoded_color = encode_value(value.color); + if (!encoded_color) return std::move(encoded_color).error(); + object.emplace("color", std::move(encoded_color).value()); + auto encoded_generation = encode_value(value.generation); + if (!encoded_generation) return std::move(encoded_generation).error(); + object.emplace("generation", std::move(encoded_generation).value()); + if (value.highlight) { + auto encoded = encode_value(*value.highlight); + if (!encoded) return std::move(encoded).error(); + object.emplace("highlight", std::move(encoded).value()); + } else { + object.emplace("highlight", Json(nullptr)); + } + if (value.kind) { + auto encoded = encode_value(*value.kind); + if (!encoded) return std::move(encoded).error(); + object.emplace("kind", std::move(encoded).value()); + } else { + object.emplace("kind", Json(nullptr)); + } + if (value.name) { + auto encoded = encode_value(*value.name); + if (!encoded) return std::move(encoded).error(); + object.emplace("name", std::move(encoded).value()); + } else { + object.emplace("name", Json(nullptr)); + } + if (value.pointer) { + auto encoded = encode_value(*value.pointer); + if (!encoded) return std::move(encoded).error(); + object.emplace("pointer", std::move(encoded).value()); + } else { + object.emplace("pointer", Json(nullptr)); + } + if (value.surface) { + auto encoded = encode_value(*value.surface); + if (!encoded) return std::move(encoded).error(); + object.emplace("surface", std::move(encoded).value()); + } else { + object.emplace("surface", Json(nullptr)); + } + auto encoded_updated_at_ms = encode_value(value.updated_at_ms); + if (!encoded_updated_at_ms) return std::move(encoded_updated_at_ms).error(); + object.emplace("updated_at_ms", std::move(encoded_updated_at_ms).value()); + return Json(std::move(object)); +} + +Result Codec::decode(const Json& value) { + auto source = value.as_object(); + if (!source) return std::move(source).error(); + PresenceEntry result{}; + const Json* field_client = value.find("client"); + if (!field_client) { + return make_error(ErrorCode::decode, "missing required field 'client'"); + } + if (field_client) { + auto decoded = decode_value(*field_client); + if (!decoded) return std::move(decoded).error(); + result.client = std::move(decoded).value(); + } + const Json* field_color = value.find("color"); + if (!field_color) { + return make_error(ErrorCode::decode, "missing required field 'color'"); + } + if (field_color) { + auto decoded = decode_value(*field_color); + if (!decoded) return std::move(decoded).error(); + result.color = std::move(decoded).value(); + } + const Json* field_generation = value.find("generation"); + if (!field_generation) { + return make_error(ErrorCode::decode, "missing required field 'generation'"); + } + if (field_generation) { + auto decoded = decode_value(*field_generation); + if (!decoded) return std::move(decoded).error(); + result.generation = std::move(decoded).value(); + } + const Json* field_highlight = value.find("highlight"); + if (!field_highlight) { + return make_error(ErrorCode::decode, "missing required field 'highlight'"); + } + if (field_highlight) { + if (field_highlight->is_null()) { + result.highlight.reset(); + } else { + auto decoded = decode_value(*field_highlight); + if (!decoded) return std::move(decoded).error(); + result.highlight = std::move(decoded).value(); + } + } + const Json* field_kind = value.find("kind"); + if (!field_kind) { + return make_error(ErrorCode::decode, "missing required field 'kind'"); + } + if (field_kind) { + if (field_kind->is_null()) { + result.kind.reset(); + } else { + auto decoded = decode_value(*field_kind); + if (!decoded) return std::move(decoded).error(); + result.kind = std::move(decoded).value(); + } + } + const Json* field_name = value.find("name"); + if (!field_name) { + return make_error(ErrorCode::decode, "missing required field 'name'"); + } + if (field_name) { + if (field_name->is_null()) { + result.name.reset(); + } else { + auto decoded = decode_value(*field_name); + if (!decoded) return std::move(decoded).error(); + result.name = std::move(decoded).value(); + } + } + const Json* field_pointer = value.find("pointer"); + if (!field_pointer) { + return make_error(ErrorCode::decode, "missing required field 'pointer'"); + } + if (field_pointer) { + if (field_pointer->is_null()) { + result.pointer.reset(); + } else { + auto decoded = decode_value(*field_pointer); + if (!decoded) return std::move(decoded).error(); + result.pointer = std::move(decoded).value(); + } + } + const Json* field_surface = value.find("surface"); + if (!field_surface) { + return make_error(ErrorCode::decode, "missing required field 'surface'"); + } + if (field_surface) { + if (field_surface->is_null()) { + result.surface.reset(); + } else { + auto decoded = decode_value(*field_surface); + if (!decoded) return std::move(decoded).error(); + result.surface = std::move(decoded).value(); + } + } + const Json* field_updated_at_ms = value.find("updated_at_ms"); + if (!field_updated_at_ms) { + return make_error(ErrorCode::decode, "missing required field 'updated_at_ms'"); + } + if (field_updated_at_ms) { + auto decoded = decode_value(*field_updated_at_ms); + if (!decoded) return std::move(decoded).error(); + result.updated_at_ms = std::move(decoded).value(); + } + return result; +} + +Result Codec::encode(const PresenceHighlight& value) { + (void)value; + Json::Object object; + auto encoded_end = encode_value(value.end); + if (!encoded_end) return std::move(encoded_end).error(); + object.emplace("end", std::move(encoded_end).value()); + auto encoded_mode = encode_value(value.mode); + if (!encoded_mode) return std::move(encoded_mode).error(); + object.emplace("mode", std::move(encoded_mode).value()); + auto encoded_start = encode_value(value.start); + if (!encoded_start) return std::move(encoded_start).error(); + object.emplace("start", std::move(encoded_start).value()); + return Json(std::move(object)); +} + +Result Codec::decode(const Json& value) { + auto source = value.as_object(); + if (!source) return std::move(source).error(); + PresenceHighlight result{}; + const Json* field_end = value.find("end"); + if (!field_end) { + return make_error(ErrorCode::decode, "missing required field 'end'"); + } + if (field_end) { + auto decoded = decode_value(*field_end); + if (!decoded) return std::move(decoded).error(); + result.end = std::move(decoded).value(); + } + const Json* field_mode = value.find("mode"); + if (!field_mode) { + return make_error(ErrorCode::decode, "missing required field 'mode'"); + } + if (field_mode) { + auto decoded = decode_value(*field_mode); + if (!decoded) return std::move(decoded).error(); + result.mode = std::move(decoded).value(); + } + const Json* field_start = value.find("start"); + if (!field_start) { + return make_error(ErrorCode::decode, "missing required field 'start'"); + } + if (field_start) { + auto decoded = decode_value(*field_start); + if (!decoded) return std::move(decoded).error(); + result.start = std::move(decoded).value(); + } + return result; +} + +Result Codec::encode(const PresenceHighlightMode& value) { + switch (value) { + case PresenceHighlightMode::laser: return Json(std::string("laser")); + case PresenceHighlightMode::pin: return Json(std::string("pin")); + } + return make_error(ErrorCode::invalid_argument, "invalid enum value"); +} + +Result Codec::decode(const Json& value) { + if (value == Json(std::string("laser"))) return PresenceHighlightMode::laser; + if (value == Json(std::string("pin"))) return PresenceHighlightMode::pin; + return make_error(ErrorCode::decode, "unknown PresenceHighlightMode value"); +} + +Result Codec::encode(const PresenceListResult& value) { + (void)value; + Json::Object object; + auto encoded_entries = encode_value(value.entries); + if (!encoded_entries) return std::move(encoded_entries).error(); + object.emplace("entries", std::move(encoded_entries).value()); + return Json(std::move(object)); +} + +Result Codec::decode(const Json& value) { + auto source = value.as_object(); + if (!source) return std::move(source).error(); + PresenceListResult result{}; + const Json* field_entries = value.find("entries"); + if (!field_entries) { + return make_error(ErrorCode::decode, "missing required field 'entries'"); + } + if (field_entries) { + auto decoded = decode_value>(*field_entries); + if (!decoded) return std::move(decoded).error(); + result.entries = std::move(decoded).value(); + } + return result; +} + Result Codec::encode(const ProcessInfoResult& value) { (void)value; Json::Object object; @@ -10850,6 +11119,87 @@ Result Codec::decode(const Json& value) { return result; } +Result Codec::encode(const PresenceClearRequest& value) { + (void)value; + Json::Object object; + return Json(std::move(object)); +} + +Result Codec::decode(const Json& value) { + auto source = value.as_object(); + if (!source) return std::move(source).error(); + PresenceClearRequest result{}; + return result; +} + +Result Codec::encode(const PresenceListRequest& value) { + (void)value; + Json::Object object; + return Json(std::move(object)); +} + +Result Codec::decode(const Json& value) { + auto source = value.as_object(); + if (!source) return std::move(source).error(); + PresenceListRequest result{}; + return result; +} + +Result Codec::encode(const PresenceUpdateRequest& value) { + (void)value; + Json::Object object; + if (!value.highlight.is_absent()) { + auto encoded = encode_value(value.highlight); + if (!encoded) return std::move(encoded).error(); + object.emplace("highlight", std::move(encoded).value()); + } + if (!value.pointer.is_absent()) { + auto encoded = encode_value(value.pointer); + if (!encoded) return std::move(encoded).error(); + object.emplace("pointer", std::move(encoded).value()); + } + auto encoded_surface = encode_value(value.surface); + if (!encoded_surface) return std::move(encoded_surface).error(); + object.emplace("surface", std::move(encoded_surface).value()); + return Json(std::move(object)); +} + +Result Codec::decode(const Json& value) { + auto source = value.as_object(); + if (!source) return std::move(source).error(); + PresenceUpdateRequest result{}; + const Json* field_highlight = value.find("highlight"); + if (field_highlight) { + if (field_highlight->is_null()) { + result.highlight = Field::null(); + } else { + auto decoded = decode_value(*field_highlight); + if (!decoded) return std::move(decoded).error(); + result.highlight = Field(std::move(decoded).value()); + } + } + const Json* field_pointer = value.find("pointer"); + if (field_pointer) { + if (field_pointer->is_null()) { + result.pointer = Field::null(); + } else { + auto decoded = decode_value(*field_pointer); + if (!decoded) return std::move(decoded).error(); + result.pointer = Field(std::move(decoded).value()); + } + } + const Json* field_surface = value.find("surface"); + if (!field_surface) { + return make_error(ErrorCode::decode, "missing required field 'surface'"); + } + if (field_surface) { + auto decoded = decode_value(*field_surface); + if (!decoded) return std::move(decoded).error(); + result.surface = std::move(decoded).value(); + } + return result; +} + Result Codec::encode(const ProcessInfoRequest& value) { (void)value; Json::Object object; @@ -12930,6 +13280,11 @@ Result Codec::decode(const Json& value) { Result Codec::encode(const SubscribeRequest& value) { (void)value; Json::Object object; + if (!value.presence_only.is_absent()) { + auto encoded = encode_value(value.presence_only); + if (!encoded) return std::move(encoded).error(); + object.emplace("presence_only", std::move(encoded).value()); + } if (!value.surface.is_absent()) { auto encoded = encode_value(value.surface); if (!encoded) return std::move(encoded).error(); @@ -12947,6 +13302,16 @@ Result Codec::decode(const Json& value) { auto source = value.as_object(); if (!source) return std::move(source).error(); SubscribeRequest result{}; + const Json* field_presence_only = value.find("presence_only"); + if (field_presence_only) { + if (field_presence_only->is_null()) { + result.presence_only = Field::null(); + } else { + auto decoded = decode_value(*field_presence_only); + if (!decoded) return std::move(decoded).error(); + result.presence_only = Field(std::move(decoded).value()); + } + } const Json* field_surface = value.find("surface"); if (field_surface) { if (field_surface->is_null()) { @@ -14842,6 +15207,177 @@ Result Codec::decode(const Json& value) { return result; } +Result Codec::encode(const PresenceChangedEvent& value) { + (void)value; + Json::Object object; + object.emplace("event", Json(std::string("presence-changed"))); + auto encoded_client = encode_value(value.client); + if (!encoded_client) return std::move(encoded_client).error(); + object.emplace("client", std::move(encoded_client).value()); + auto encoded_color = encode_value(value.color); + if (!encoded_color) return std::move(encoded_color).error(); + object.emplace("color", std::move(encoded_color).value()); + auto encoded_generation = encode_value(value.generation); + if (!encoded_generation) return std::move(encoded_generation).error(); + object.emplace("generation", std::move(encoded_generation).value()); + if (value.highlight) { + auto encoded = encode_value(*value.highlight); + if (!encoded) return std::move(encoded).error(); + object.emplace("highlight", std::move(encoded).value()); + } else { + object.emplace("highlight", Json(nullptr)); + } + if (value.kind) { + auto encoded = encode_value(*value.kind); + if (!encoded) return std::move(encoded).error(); + object.emplace("kind", std::move(encoded).value()); + } else { + object.emplace("kind", Json(nullptr)); + } + if (value.name) { + auto encoded = encode_value(*value.name); + if (!encoded) return std::move(encoded).error(); + object.emplace("name", std::move(encoded).value()); + } else { + object.emplace("name", Json(nullptr)); + } + if (value.pointer) { + auto encoded = encode_value(*value.pointer); + if (!encoded) return std::move(encoded).error(); + object.emplace("pointer", std::move(encoded).value()); + } else { + object.emplace("pointer", Json(nullptr)); + } + if (value.surface) { + auto encoded = encode_value(*value.surface); + if (!encoded) return std::move(encoded).error(); + object.emplace("surface", std::move(encoded).value()); + } else { + object.emplace("surface", Json(nullptr)); + } + auto encoded_updated_at_ms = encode_value(value.updated_at_ms); + if (!encoded_updated_at_ms) return std::move(encoded_updated_at_ms).error(); + object.emplace("updated_at_ms", std::move(encoded_updated_at_ms).value()); + return Json(std::move(object)); +} + +Result Codec::decode(const Json& value) { + auto source = value.as_object(); + if (!source) return std::move(source).error(); + PresenceChangedEvent result{}; + const Json* field_client = value.find("client"); + if (!field_client) { + return make_error(ErrorCode::decode, "missing required field 'client'"); + } + if (field_client) { + auto decoded = decode_value(*field_client); + if (!decoded) return std::move(decoded).error(); + result.client = std::move(decoded).value(); + } + const Json* field_color = value.find("color"); + if (!field_color) { + return make_error(ErrorCode::decode, "missing required field 'color'"); + } + if (field_color) { + auto decoded = decode_value(*field_color); + if (!decoded) return std::move(decoded).error(); + result.color = std::move(decoded).value(); + } + const Json* field_generation = value.find("generation"); + if (!field_generation) { + return make_error(ErrorCode::decode, "missing required field 'generation'"); + } + if (field_generation) { + auto decoded = decode_value(*field_generation); + if (!decoded) return std::move(decoded).error(); + result.generation = std::move(decoded).value(); + } + const Json* field_highlight = value.find("highlight"); + if (!field_highlight) { + return make_error(ErrorCode::decode, "missing required field 'highlight'"); + } + if (field_highlight) { + if (field_highlight->is_null()) { + result.highlight.reset(); + } else { + auto decoded = decode_value(*field_highlight); + if (!decoded) return std::move(decoded).error(); + result.highlight = std::move(decoded).value(); + } + } + const Json* field_kind = value.find("kind"); + if (!field_kind) { + return make_error(ErrorCode::decode, "missing required field 'kind'"); + } + if (field_kind) { + if (field_kind->is_null()) { + result.kind.reset(); + } else { + auto decoded = decode_value(*field_kind); + if (!decoded) return std::move(decoded).error(); + result.kind = std::move(decoded).value(); + } + } + const Json* field_name = value.find("name"); + if (!field_name) { + return make_error(ErrorCode::decode, "missing required field 'name'"); + } + if (field_name) { + if (field_name->is_null()) { + result.name.reset(); + } else { + auto decoded = decode_value(*field_name); + if (!decoded) return std::move(decoded).error(); + result.name = std::move(decoded).value(); + } + } + const Json* field_pointer = value.find("pointer"); + if (!field_pointer) { + return make_error(ErrorCode::decode, "missing required field 'pointer'"); + } + if (field_pointer) { + if (field_pointer->is_null()) { + result.pointer.reset(); + } else { + auto decoded = decode_value(*field_pointer); + if (!decoded) return std::move(decoded).error(); + result.pointer = std::move(decoded).value(); + } + } + const Json* field_surface = value.find("surface"); + if (!field_surface) { + return make_error(ErrorCode::decode, "missing required field 'surface'"); + } + if (field_surface) { + if (field_surface->is_null()) { + result.surface.reset(); + } else { + auto decoded = decode_value(*field_surface); + if (!decoded) return std::move(decoded).error(); + result.surface = std::move(decoded).value(); + } + } + const Json* field_updated_at_ms = value.find("updated_at_ms"); + if (!field_updated_at_ms) { + return make_error(ErrorCode::decode, "missing required field 'updated_at_ms'"); + } + if (field_updated_at_ms) { + auto decoded = decode_value(*field_updated_at_ms); + if (!decoded) return std::move(decoded).error(); + result.updated_at_ms = std::move(decoded).value(); + } + const Json* field_event = value.find("event"); + if (!field_event) { + return make_error(ErrorCode::decode, "missing required field 'event'"); + } + if (field_event) { + if (*field_event != Json(std::string("presence-changed"))) { + return make_error(ErrorCode::decode, "field 'event' has the wrong literal value"); + } + } + return result; +} + Result Codec::encode(const RenderDeltaEvent& value) { (void)value; Json::Object object; @@ -17516,6 +18052,111 @@ Result Codec::decode(const Json& value) { return result; } +Result Codec::encode(const PresenceAnchorCell& value) { + (void)value; + Json::Object object; + object.emplace("kind", Json(std::string("cell"))); + auto encoded_col = encode_value(value.col); + if (!encoded_col) return std::move(encoded_col).error(); + object.emplace("col", std::move(encoded_col).value()); + auto encoded_row = encode_value(value.row); + if (!encoded_row) return std::move(encoded_row).error(); + object.emplace("row", std::move(encoded_row).value()); + if (value.scroll_offset) { + auto encoded = encode_value(*value.scroll_offset); + if (!encoded) return std::move(encoded).error(); + object.emplace("scroll_offset", std::move(encoded).value()); + } + return Json(std::move(object)); +} + +Result Codec::decode(const Json& value) { + auto source = value.as_object(); + if (!source) return std::move(source).error(); + PresenceAnchorCell result{}; + const Json* field_col = value.find("col"); + if (!field_col) { + return make_error(ErrorCode::decode, "missing required field 'col'"); + } + if (field_col) { + auto decoded = decode_value(*field_col); + if (!decoded) return std::move(decoded).error(); + result.col = std::move(decoded).value(); + } + const Json* field_row = value.find("row"); + if (!field_row) { + return make_error(ErrorCode::decode, "missing required field 'row'"); + } + if (field_row) { + auto decoded = decode_value(*field_row); + if (!decoded) return std::move(decoded).error(); + result.row = std::move(decoded).value(); + } + const Json* field_scroll_offset = value.find("scroll_offset"); + if (field_scroll_offset) { + auto decoded = decode_value(*field_scroll_offset); + if (!decoded) return std::move(decoded).error(); + result.scroll_offset = std::move(decoded).value(); + } + const Json* field_kind = value.find("kind"); + if (!field_kind) { + return make_error(ErrorCode::decode, "missing required field 'kind'"); + } + if (field_kind) { + if (*field_kind != Json(std::string("cell"))) { + return make_error(ErrorCode::decode, "field 'kind' has the wrong literal value"); + } + } + return result; +} + +Result Codec::encode(const PresenceAnchorPoint& value) { + (void)value; + Json::Object object; + object.emplace("kind", Json(std::string("point"))); + auto encoded_x = encode_value(value.x); + if (!encoded_x) return std::move(encoded_x).error(); + object.emplace("x", std::move(encoded_x).value()); + auto encoded_y = encode_value(value.y); + if (!encoded_y) return std::move(encoded_y).error(); + object.emplace("y", std::move(encoded_y).value()); + return Json(std::move(object)); +} + +Result Codec::decode(const Json& value) { + auto source = value.as_object(); + if (!source) return std::move(source).error(); + PresenceAnchorPoint result{}; + const Json* field_x = value.find("x"); + if (!field_x) { + return make_error(ErrorCode::decode, "missing required field 'x'"); + } + if (field_x) { + auto decoded = decode_value(*field_x); + if (!decoded) return std::move(decoded).error(); + result.x = std::move(decoded).value(); + } + const Json* field_y = value.find("y"); + if (!field_y) { + return make_error(ErrorCode::decode, "missing required field 'y'"); + } + if (field_y) { + auto decoded = decode_value(*field_y); + if (!decoded) return std::move(decoded).error(); + result.y = std::move(decoded).value(); + } + const Json* field_kind = value.find("kind"); + if (!field_kind) { + return make_error(ErrorCode::decode, "missing required field 'kind'"); + } + if (field_kind) { + if (*field_kind != Json(std::string("point"))) { + return make_error(ErrorCode::decode, "field 'kind' has the wrong literal value"); + } + } + return result; +} + Result Codec::encode(const TabBrowserSource& value) { switch (value) { case TabBrowserSource::external: return Json(std::string("external")); @@ -17993,6 +18634,11 @@ Result Codec::decode(const Json& value) { if (!decoded) return std::move(decoded).error(); return Event{Event::Variant(std::move(decoded).value()), value}; } + if (name.value() == "presence-changed") { + auto decoded = decode_value(value); + if (!decoded) return std::move(decoded).error(); + return Event{Event::Variant(std::move(decoded).value()), value}; + } if (name.value() == "render-delta") { auto decoded = decode_value(value); if (!decoded) return std::move(decoded).error(); @@ -18146,20 +18792,20 @@ constexpr std::array kCommand50FieldRequirements{{ {"mutation_id", 7U, ""}, {"origin", 7U, ""}, }}; -constexpr std::array kCommand73FieldRequirements{{ +constexpr std::array kCommand76FieldRequirements{{ {"expected_generation", 7U, ""}, {"expected_revision", 7U, ""}, {"key", 7U, "workspace-registry-v1"}, {"mutation_id", 7U, ""}, {"origin", 7U, ""}, }}; -constexpr std::array kCommand79FieldRequirements{{ +constexpr std::array kCommand82FieldRequirements{{ {"key", 9U, ""}, }}; -constexpr std::array kCommand84FieldRequirements{{ +constexpr std::array kCommand87FieldRequirements{{ {"paste", 7U, ""}, }}; -constexpr std::array kCommand90FieldRequirements{{ +constexpr std::array kCommand93FieldRequirements{{ {"complete", 9U, ""}, {"cursor", 9U, ""}, {"cursor_blink", 9U, ""}, @@ -18168,20 +18814,21 @@ constexpr std::array kCommand90FieldRequirements{{ {"selection_bg", 9U, ""}, {"selection_fg", 9U, ""}, }}; -constexpr std::array kCommand92FieldRequirements{{ +constexpr std::array kCommand95FieldRequirements{{ {"transaction", 9U, "layout-undo-v1"}, }}; -constexpr std::array kCommand93FieldRequirements{{ +constexpr std::array kCommand96FieldRequirements{{ {"transaction", 9U, "layout-undo-v1"}, }}; -constexpr std::array kCommand95FieldRequirements{{ +constexpr std::array kCommand98FieldRequirements{{ {"force", 10U, "daemon-handoff-force-v1"}, }}; -constexpr std::array kCommand98FieldRequirements{{ +constexpr std::array kCommand101FieldRequirements{{ + {"presence_only", 12U, "presence-v1"}, {"surface", 9U, "surface-subscribe-filter"}, {"tree_events", 7U, ""}, }}; -constexpr std::array kCommands{{ +constexpr std::array kCommands{{ {"apply-layout", "control", 6U, "", false, "", "", std::span{}}, {"attach-surface", "frontend", 5U, "", true, "attach", "detached", std::span(kCommand1FieldRequirements)}, {"browser-activate", "frontend", 6U, "", false, "", "", std::span{}}, @@ -18243,6 +18890,9 @@ constexpr std::array kCommands{{ {"pairing-response", "local-admin", 7U, "", false, "", "", std::span{}}, {"pane-neighbor", "control", 6U, "", false, "", "", std::span{}}, {"ping", "control", 6U, "", false, "", "", std::span{}}, + {"presence-clear", "control", 12U, "presence-v1", false, "", "", std::span{}}, + {"presence-list", "control", 12U, "presence-v1", false, "", "", std::span{}}, + {"presence-update", "control", 12U, "presence-v1", false, "", "", std::span{}}, {"process-info", "control", 6U, "", false, "", "", std::span{}}, {"put-frontend-projection", "control", 7U, "", false, "", "", std::span{}}, {"read-screen", "control", 5U, "", false, "", "", std::span{}}, @@ -18255,32 +18905,32 @@ constexpr std::array kCommands{{ {"rename-provider-managed-workspace", "provider-authority", 9U, "provider-managed-workspace-authority-v2", false, "", "", std::span{}}, {"rename-screen", "control", 5U, "", false, "", "", std::span{}}, {"rename-surface", "control", 5U, "", false, "", "", std::span{}}, - {"rename-workspace", "control", 5U, "", false, "", "", std::span(kCommand73FieldRequirements)}, + {"rename-workspace", "control", 5U, "", false, "", "", std::span(kCommand76FieldRequirements)}, {"report-agent", "control", 6U, "", false, "", "", std::span{}}, {"report-focus", "control", 12U, "client-focus-v1", false, "", "", std::span{}}, {"resize-attached-view", "frontend", 10U, "view-attachment-lease-v1", false, "", "", std::span{}}, {"resize-surface", "control", 5U, "", false, "", "", std::span{}}, {"resolve-terminal", "control", 9U, "", false, "", "", std::span{}}, - {"run", "control", 6U, "", false, "", "", std::span(kCommand79FieldRequirements)}, + {"run", "control", 6U, "", false, "", "", std::span(kCommand82FieldRequirements)}, {"scroll-surface", "control", 5U, "", false, "", "", std::span{}}, {"select-screen", "control", 5U, "", false, "", "", std::span{}}, {"select-tab", "control", 5U, "", false, "", "", std::span{}}, {"select-workspace", "control", 5U, "", false, "", "", std::span{}}, - {"send", "control", 5U, "", false, "", "", std::span(kCommand84FieldRequirements)}, + {"send", "control", 5U, "", false, "", "", std::span(kCommand87FieldRequirements)}, {"send-key", "control", 6U, "", false, "", "", std::span{}}, {"server-stats", "local-admin", 12U, "server-stats-v1", false, "", "", std::span{}}, {"set-cell-pixels", "frontend", 6U, "", false, "", "", std::span{}}, {"set-client-info", "control", 6U, "", false, "", "", std::span{}}, {"set-client-sizing", "control", 10U, "", false, "", "", std::span{}}, - {"set-default-colors", "control", 5U, "", false, "", "", std::span(kCommand90FieldRequirements)}, + {"set-default-colors", "control", 5U, "", false, "", "", std::span(kCommand93FieldRequirements)}, {"set-ratio", "control", 5U, "", false, "", "", std::span{}}, - {"set-split-ratio", "control", 8U, "", false, "", "", std::span(kCommand92FieldRequirements)}, - {"set-viewport-pane-width", "control", 9U, "viewport-column-resize-v1", false, "", "", std::span(kCommand93FieldRequirements)}, + {"set-split-ratio", "control", 8U, "", false, "", "", std::span(kCommand95FieldRequirements)}, + {"set-viewport-pane-width", "control", 9U, "viewport-column-resize-v1", false, "", "", std::span(kCommand96FieldRequirements)}, {"set-window-title", "control", 6U, "", false, "", "", std::span{}}, - {"shutdown-daemon", "local-admin", 9U, "", false, "", "", std::span(kCommand95FieldRequirements)}, + {"shutdown-daemon", "local-admin", 9U, "", false, "", "", std::span(kCommand98FieldRequirements)}, {"sidebar-plugin", "frontend", 6U, "", false, "", "", std::span{}}, {"split", "control", 5U, "", false, "", "", std::span{}}, - {"subscribe", "frontend", 5U, "", true, "subscribe", "", std::span(kCommand98FieldRequirements)}, + {"subscribe", "frontend", 5U, "", true, "subscribe", "", std::span(kCommand101FieldRequirements)}, {"swap-pane", "control", 6U, "", false, "", "", std::span{}}, {"terminal-events", "control", 9U, "", false, "", "", std::span{}}, {"undo-layout", "control", 9U, "layout-undo-v1", false, "", "", std::span{}}, @@ -18289,7 +18939,7 @@ constexpr std::array kCommands{{ {"wait-for", "control", 6U, "", false, "", "", std::span{}}, {"zoom-pane", "control", 6U, "", false, "", "", std::span{}}, }}; -constexpr std::array kEvents{{ +constexpr std::array kEvents{{ {"agent-changed", 11U, "", "subscribe", "emitted"}, {"bell", 5U, "", "subscribe", "emitted"}, {"browser-state", 6U, "", "attach-browser", "emitted"}, @@ -18314,6 +18964,7 @@ constexpr std::array kEvents{{ {"pairing-resolved", 7U, "", "subscribe", "emitted"}, {"pane-added", 7U, "", "subscribe-deltas", "emitted"}, {"pane-closed", 7U, "", "subscribe-deltas", "emitted"}, + {"presence-changed", 12U, "presence-v1", "subscribe", "emitted"}, {"render-delta", 7U, "", "attach-render", "emitted"}, {"render-state", 7U, "", "attach-render", "emitted"}, {"resized", 6U, "", "attach-byte", "emitted"}, @@ -19029,6 +19680,39 @@ Result Client::ping( return decode_value(response.value()); } +Result Client::presence_clear( + const PresenceClearRequest& request, RequestOptions options) { + auto encoded = encode_value(request); + if (!encoded) return std::move(encoded).error(); + auto parameters = encoded.value().as_object(); + if (!parameters) return std::move(parameters).error(); + auto response = core_.request("presence-clear", *parameters.value(), options.timeout); + if (!response) return std::move(response).error(); + return decode_value(response.value()); +} + +Result Client::presence_list( + const PresenceListRequest& request, RequestOptions options) { + auto encoded = encode_value(request); + if (!encoded) return std::move(encoded).error(); + auto parameters = encoded.value().as_object(); + if (!parameters) return std::move(parameters).error(); + auto response = core_.request("presence-list", *parameters.value(), options.timeout); + if (!response) return std::move(response).error(); + return decode_value(response.value()); +} + +Result Client::presence_update( + const PresenceUpdateRequest& request, RequestOptions options) { + auto encoded = encode_value(request); + if (!encoded) return std::move(encoded).error(); + auto parameters = encoded.value().as_object(); + if (!parameters) return std::move(parameters).error(); + auto response = core_.request("presence-update", *parameters.value(), options.timeout); + if (!response) return std::move(response).error(); + return decode_value(response.value()); +} + Result Client::process_info( const ProcessInfoRequest& request, RequestOptions options) { auto encoded = encode_value(request); diff --git a/cmux-tui/bindings/cpp/tests/test_generated.cpp b/cmux-tui/bindings/cpp/tests/test_generated.cpp index 70f3e4843d6e..401e84d95cea 100644 --- a/cmux-tui/bindings/cpp/tests/test_generated.cpp +++ b/cmux-tui/bindings/cpp/tests/test_generated.cpp @@ -84,7 +84,7 @@ static_assert(std::is_same_v< static_assert(!std::is_copy_constructible_v); static_assert(std::is_move_constructible_v); -constexpr std::size_t kExpectedRawCommandCount = 106U; +constexpr std::size_t kExpectedRawCommandCount = 109U; constexpr std::array kViewportHistoryCommandNames{ "clear-history", "new-pane-right", @@ -102,7 +102,7 @@ TEST("generated command and event metadata is exhaustive and unique") { const auto commands = cmux::raw::command_metadata(); const auto events = cmux::raw::event_metadata(); CHECK_EQ(commands.size(), kExpectedRawCommandCount); - CHECK_EQ(events.size(), 48U); + CHECK_EQ(events.size(), 49U); std::set command_names; bool checked_attach_fields = false; diff --git a/cmux-tui/bindings/go/raw/.cmux-sdk-manifest.json b/cmux-tui/bindings/go/raw/.cmux-sdk-manifest.json index eaa79b149da4..a0a18b345841 100644 --- a/cmux-tui/bindings/go/raw/.cmux-sdk-manifest.json +++ b/cmux-tui/bindings/go/raw/.cmux-sdk-manifest.json @@ -2,32 +2,32 @@ "files": [ { "path": "generated_commands.go", - "sha256": "8d9365843638b40f43ceb632b40a4ad80f636c8c0289a4947586a3ab9f63f86a", - "size": 361825 + "sha256": "6a7661ebef516bf41ac076b9a4c9887b2a27d793a0eda6f10f35796e8579830f", + "size": 368583 }, { "path": "generated_events.go", - "sha256": "d7207a38547f496e0d709cb4013ebd641579988abf77f4afb536dad032c6f204", - "size": 111197 + "sha256": "7c564252383b1dcb17bc12a9cd91310985ad9054e9e82253c0d70f56816ced6e", + "size": 117063 }, { "path": "generated_metadata.go", - "sha256": "e0411cf24e930b3ca625d944ef8222dd46f8d78b0c78bceedbe426d26652e422", - "size": 49445 + "sha256": "da16464a6c97a7f7fe9f9c36cad8b8a72cec034341c6d4eec7ac33d97436efd3", + "size": 50675 }, { "path": "generated_presence_test.go", - "sha256": "86e7fb5abb5d2cf59c08899c10a6aa15ace17793fe39937e282a9cbe476ef328", - "size": 1062558 + "sha256": "1663a58f1008db70d060f914eface7ea64af0dea39466d3f25fbfe10a49e7c66", + "size": 1099428 }, { "path": "generated_types.go", - "sha256": "7bade9d9f30db12a964d821a2eaf77c670a3ac7e8781b3387e76f90a48914c50", - "size": 296267 + "sha256": "45bade636f81572613b27f83cbf9187718b2243b05975867d4ec28ed36769b19", + "size": 311707 } ], "format": 1, - "ir_sha256": "8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86", + "ir_sha256": "e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663", "language": "go", "mux_protocol": 12, "schema_version": 2 diff --git a/cmux-tui/bindings/go/raw/client_test.go b/cmux-tui/bindings/go/raw/client_test.go index c571c0f96a74..11afd5ab87e9 100644 --- a/cmux-tui/bindings/go/raw/client_test.go +++ b/cmux-tui/bindings/go/raw/client_test.go @@ -23,8 +23,8 @@ import ( func TestGeneratedInventoryHasTypedMethodForEveryCommand(t *testing.T) { commands := AllCommandMetadata() - if len(commands) != 106 { - t.Fatalf("generated commands = %d, want 106", len(commands)) + if len(commands) != 109 { + t.Fatalf("generated commands = %d, want 109", len(commands)) } clientType := reflect.TypeOf((*Client)(nil)) commandNames := make(map[string]struct{}, len(commands)) @@ -54,8 +54,8 @@ func TestGeneratedInventoryHasTypedMethodForEveryCommand(t *testing.T) { t.Errorf("generated command inventory is missing %s", name) } } - if events := AllEventMetadata(); len(events) != 48 { - t.Fatalf("generated events = %d, want 48", len(events)) + if events := AllEventMetadata(); len(events) != 49 { + t.Fatalf("generated events = %d, want 49", len(events)) } } diff --git a/cmux-tui/bindings/go/raw/generated_commands.go b/cmux-tui/bindings/go/raw/generated_commands.go index 91a7c85f5ee9..a9e884ee78ef 100644 --- a/cmux-tui/bindings/go/raw/generated_commands.go +++ b/cmux-tui/bindings/go/raw/generated_commands.go @@ -1,5 +1,5 @@ // Code generated by cmux-tui SDK codegen. DO NOT EDIT. -// Mux protocol 12; IR SHA-256 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. +// Mux protocol 12; IR SHA-256 e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. package raw @@ -6089,6 +6089,177 @@ func (value *PingRequest) UnmarshalJSON(data []byte) error { return nil } +// PresenceClearRequest is the exact presence-clear wire payload. +type PresenceClearRequest struct { +} + +func (value *PresenceClearRequest) UnmarshalJSON(data []byte) error { + if !isJSONObject(data) { + return fmt.Errorf("decode PresenceClearRequest: expected object") + } + var fields struct { + } + if err := json.Unmarshal(data, &fields); err != nil { + return fmt.Errorf("decode PresenceClearRequest: %w", err) + } + type wire PresenceClearRequest + var decoded wire + *value = PresenceClearRequest(decoded) + return nil +} + +type PresenceClearResult = EmptyResult + +// PresenceListRequest is the exact presence-list wire payload. +type PresenceListRequest struct { +} + +func (value *PresenceListRequest) UnmarshalJSON(data []byte) error { + if !isJSONObject(data) { + return fmt.Errorf("decode PresenceListRequest: expected object") + } + var fields struct { + } + if err := json.Unmarshal(data, &fields); err != nil { + return fmt.Errorf("decode PresenceListRequest: %w", err) + } + type wire PresenceListRequest + var decoded wire + *value = PresenceListRequest(decoded) + return nil +} + +// PresenceUpdateRequest is the exact presence-update wire payload. +type PresenceUpdateRequest struct { + Highlight Presence[PresenceHighlight] `json:"-"` + Pointer Presence[PresenceAnchor] `json:"-"` + Surface ID `json:"surface"` +} + +func (value PresenceUpdateRequest) MarshalJSON() ([]byte, error) { + type wire PresenceUpdateRequest + encoded, err := json.Marshal(wire(value)) + if err != nil { + return nil, err + } + var object map[string]json.RawMessage + if err := json.Unmarshal(encoded, &object); err != nil { + return nil, err + } + if value.Highlight.IsAbsent() { + delete(object, "highlight") + } else if value.Highlight.IsNull() { + object["highlight"] = json.RawMessage("null") + } else { + fieldValue, _ := value.Highlight.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode PresenceUpdateRequest.Highlight: %w", err) + } + object["highlight"] = encodedField + } + if value.Pointer.IsAbsent() { + delete(object, "pointer") + } else if value.Pointer.IsNull() { + object["pointer"] = json.RawMessage("null") + } else { + fieldValue, _ := value.Pointer.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode PresenceUpdateRequest.Pointer: %w", err) + } + object["pointer"] = encodedField + } + return json.Marshal(object) +} + +func (value *PresenceUpdateRequest) UnmarshalJSON(data []byte) error { + if !isJSONObject(data) { + return fmt.Errorf("decode PresenceUpdateRequest: expected object") + } + var fields struct { + Highlight Presence[PresenceHighlight] `json:"highlight"` + Pointer Presence[PresenceAnchor] `json:"pointer"` + Surface *ID `json:"surface"` + } + if err := json.Unmarshal(data, &fields); err != nil { + return fmt.Errorf("decode PresenceUpdateRequest: %w", err) + } + type wire PresenceUpdateRequest + var decoded wire + decoded.Highlight = fields.Highlight + decoded.Pointer = fields.Pointer + if fields.Surface == nil { + return fmt.Errorf("decode PresenceUpdateRequest: required field surface is missing or null") + } + decoded.Surface = *fields.Surface + *value = PresenceUpdateRequest(decoded) + return nil +} + +type PresenceUpdateResult = EmptyResult + +type PresenceUpdateOptions struct { + Highlight Presence[PresenceHighlight] `json:"-"` + Pointer Presence[PresenceAnchor] `json:"-"` +} + +func (value PresenceUpdateOptions) MarshalJSON() ([]byte, error) { + type wire PresenceUpdateOptions + encoded, err := json.Marshal(wire(value)) + if err != nil { + return nil, err + } + var object map[string]json.RawMessage + if err := json.Unmarshal(encoded, &object); err != nil { + return nil, err + } + if value.Highlight.IsAbsent() { + delete(object, "highlight") + } else if value.Highlight.IsNull() { + object["highlight"] = json.RawMessage("null") + } else { + fieldValue, _ := value.Highlight.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode PresenceUpdateOptions.Highlight: %w", err) + } + object["highlight"] = encodedField + } + if value.Pointer.IsAbsent() { + delete(object, "pointer") + } else if value.Pointer.IsNull() { + object["pointer"] = json.RawMessage("null") + } else { + fieldValue, _ := value.Pointer.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode PresenceUpdateOptions.Pointer: %w", err) + } + object["pointer"] = encodedField + } + return json.Marshal(object) +} + +func (value *PresenceUpdateOptions) UnmarshalJSON(data []byte) error { + if !isJSONObject(data) { + return fmt.Errorf("decode PresenceUpdateOptions: expected object") + } + var fields struct { + Highlight Presence[PresenceHighlight] `json:"highlight"` + Pointer Presence[PresenceAnchor] `json:"pointer"` + } + if err := json.Unmarshal(data, &fields); err != nil { + return fmt.Errorf("decode PresenceUpdateOptions: %w", err) + } + type wire PresenceUpdateOptions + var decoded wire + decoded.Highlight = fields.Highlight + decoded.Pointer = fields.Pointer + *value = PresenceUpdateOptions(decoded) + return nil +} + // ProcessInfoRequest is the exact process-info wire payload. type ProcessInfoRequest struct { Surface ID `json:"surface"` @@ -9524,8 +9695,9 @@ func (value *SubscribeRequestTreeEvents) UnmarshalJSON(data []byte) error { } type SubscribeRequest struct { - Surface Presence[ID] `json:"-"` - TreeEvents Presence[SubscribeRequestTreeEvents] `json:"-"` + PresenceOnly Presence[bool] `json:"-"` + Surface Presence[ID] `json:"-"` + TreeEvents Presence[SubscribeRequestTreeEvents] `json:"-"` } func (value SubscribeRequest) MarshalJSON() ([]byte, error) { @@ -9545,6 +9717,18 @@ func (value SubscribeRequest) MarshalJSON() ([]byte, error) { if err := json.Unmarshal(encoded, &object); err != nil { return nil, err } + if value.PresenceOnly.IsAbsent() { + delete(object, "presence_only") + } else if value.PresenceOnly.IsNull() { + object["presence_only"] = json.RawMessage("null") + } else { + fieldValue, _ := value.PresenceOnly.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode SubscribeRequest.PresenceOnly: %w", err) + } + object["presence_only"] = encodedField + } if value.Surface.IsAbsent() { delete(object, "surface") } else if value.Surface.IsNull() { @@ -9577,14 +9761,16 @@ func (value *SubscribeRequest) UnmarshalJSON(data []byte) error { return fmt.Errorf("decode SubscribeRequest: expected object") } var fields struct { - Surface Presence[ID] `json:"surface"` - TreeEvents Presence[SubscribeRequestTreeEvents] `json:"tree_events"` + PresenceOnly Presence[bool] `json:"presence_only"` + Surface Presence[ID] `json:"surface"` + TreeEvents Presence[SubscribeRequestTreeEvents] `json:"tree_events"` } if err := json.Unmarshal(data, &fields); err != nil { return fmt.Errorf("decode SubscribeRequest: %w", err) } type wire SubscribeRequest var decoded wire + decoded.PresenceOnly = fields.PresenceOnly decoded.Surface = fields.Surface decoded.TreeEvents = fields.TreeEvents *value = SubscribeRequest(decoded) @@ -10736,6 +10922,30 @@ func (c *Client) Ping(ctx context.Context) (PingResult, error) { return result, err } +// PresenceClear sends presence-clear. Protocol v12; authority control. +func (c *Client) PresenceClear(ctx context.Context) error { + err := c.requestGenerated(ctx, commandMetadata["presence-clear"], "presence-clear", nil, nil) + return err +} + +// PresenceList sends presence-list. Protocol v12; authority control. +func (c *Client) PresenceList(ctx context.Context) (PresenceListResult, error) { + var result PresenceListResult + err := c.requestGenerated(ctx, commandMetadata["presence-list"], "presence-list", nil, &result) + return result, err +} + +// PresenceUpdate sends presence-update. Protocol v12; authority control. +func (c *Client) PresenceUpdate(ctx context.Context, surface ID, options PresenceUpdateOptions) error { + params, err := mergeCommandParams(map[string]any{"surface": surface}, options) + if err != nil { + err = fmt.Errorf("%w: encode presence-update parameters: %v", ErrInvalidArgument, err) + return err + } + err = c.requestGenerated(ctx, commandMetadata["presence-update"], "presence-update", params, nil) + return err +} + // ProcessInfo sends process-info. Protocol v6; authority control. func (c *Client) ProcessInfo(ctx context.Context, surface ID) (ProcessInfoResult, error) { var result ProcessInfoResult diff --git a/cmux-tui/bindings/go/raw/generated_events.go b/cmux-tui/bindings/go/raw/generated_events.go index 088a88b17234..e47ac73e69e9 100644 --- a/cmux-tui/bindings/go/raw/generated_events.go +++ b/cmux-tui/bindings/go/raw/generated_events.go @@ -1,5 +1,5 @@ // Code generated by cmux-tui SDK codegen. DO NOT EDIT. -// Mux protocol 12; IR SHA-256 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. +// Mux protocol 12; IR SHA-256 e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. package raw @@ -1589,6 +1589,161 @@ func (PaneClosedEvent) EventName() string { return "pane-closed" } func (PaneClosedEvent) isDeltaEvent() {} func (PaneClosedEvent) isSubscribeEvent() {} +// PresenceChangedEvent is emitted by protocol v12. +type PresenceChangedEvent struct { + Client uint64 `json:"client"` + Color uint64 `json:"color"` + Generation uint64 `json:"generation"` + Highlight RequiredNullable[PresenceHighlight] `json:"-"` + Kind RequiredNullable[string] `json:"-"` + Name RequiredNullable[string] `json:"-"` + Pointer RequiredNullable[PresenceAnchor] `json:"-"` + Surface RequiredNullable[ID] `json:"-"` + UpdatedAtMs uint64 `json:"updated_at_ms"` +} + +func (value PresenceChangedEvent) MarshalJSON() ([]byte, error) { + type wire PresenceChangedEvent + encoded, err := json.Marshal(wire(value)) + if err != nil { + return nil, err + } + var object map[string]json.RawMessage + if err := json.Unmarshal(encoded, &object); err != nil { + return nil, err + } + if !value.Highlight.IsSet() { + return nil, fmt.Errorf("encode PresenceChangedEvent: required nullable field highlight is missing") + } + if value.Highlight.IsNull() { + object["highlight"] = json.RawMessage("null") + } else { + fieldValue, _ := value.Highlight.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode PresenceChangedEvent.Highlight: %w", err) + } + object["highlight"] = encodedField + } + if !value.Kind.IsSet() { + return nil, fmt.Errorf("encode PresenceChangedEvent: required nullable field kind is missing") + } + if value.Kind.IsNull() { + object["kind"] = json.RawMessage("null") + } else { + fieldValue, _ := value.Kind.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode PresenceChangedEvent.Kind: %w", err) + } + object["kind"] = encodedField + } + if !value.Name.IsSet() { + return nil, fmt.Errorf("encode PresenceChangedEvent: required nullable field name is missing") + } + if value.Name.IsNull() { + object["name"] = json.RawMessage("null") + } else { + fieldValue, _ := value.Name.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode PresenceChangedEvent.Name: %w", err) + } + object["name"] = encodedField + } + if !value.Pointer.IsSet() { + return nil, fmt.Errorf("encode PresenceChangedEvent: required nullable field pointer is missing") + } + if value.Pointer.IsNull() { + object["pointer"] = json.RawMessage("null") + } else { + fieldValue, _ := value.Pointer.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode PresenceChangedEvent.Pointer: %w", err) + } + object["pointer"] = encodedField + } + if !value.Surface.IsSet() { + return nil, fmt.Errorf("encode PresenceChangedEvent: required nullable field surface is missing") + } + if value.Surface.IsNull() { + object["surface"] = json.RawMessage("null") + } else { + fieldValue, _ := value.Surface.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode PresenceChangedEvent.Surface: %w", err) + } + object["surface"] = encodedField + } + return json.Marshal(object) +} + +func (value *PresenceChangedEvent) UnmarshalJSON(data []byte) error { + if !isJSONObject(data) { + return fmt.Errorf("decode PresenceChangedEvent: expected object") + } + var fields struct { + Client *uint64 `json:"client"` + Color *uint64 `json:"color"` + Generation *uint64 `json:"generation"` + Highlight RequiredNullable[PresenceHighlight] `json:"highlight"` + Kind RequiredNullable[string] `json:"kind"` + Name RequiredNullable[string] `json:"name"` + Pointer RequiredNullable[PresenceAnchor] `json:"pointer"` + Surface RequiredNullable[ID] `json:"surface"` + UpdatedAtMs *uint64 `json:"updated_at_ms"` + } + if err := json.Unmarshal(data, &fields); err != nil { + return fmt.Errorf("decode PresenceChangedEvent: %w", err) + } + type wire PresenceChangedEvent + var decoded wire + if fields.Client == nil { + return fmt.Errorf("decode PresenceChangedEvent: required field client is missing or null") + } + decoded.Client = *fields.Client + if fields.Color == nil { + return fmt.Errorf("decode PresenceChangedEvent: required field color is missing or null") + } + decoded.Color = *fields.Color + if fields.Generation == nil { + return fmt.Errorf("decode PresenceChangedEvent: required field generation is missing or null") + } + decoded.Generation = *fields.Generation + if !fields.Highlight.IsSet() { + return fmt.Errorf("decode PresenceChangedEvent: required field highlight is missing") + } + decoded.Highlight = fields.Highlight + if !fields.Kind.IsSet() { + return fmt.Errorf("decode PresenceChangedEvent: required field kind is missing") + } + decoded.Kind = fields.Kind + if !fields.Name.IsSet() { + return fmt.Errorf("decode PresenceChangedEvent: required field name is missing") + } + decoded.Name = fields.Name + if !fields.Pointer.IsSet() { + return fmt.Errorf("decode PresenceChangedEvent: required field pointer is missing") + } + decoded.Pointer = fields.Pointer + if !fields.Surface.IsSet() { + return fmt.Errorf("decode PresenceChangedEvent: required field surface is missing") + } + decoded.Surface = fields.Surface + if fields.UpdatedAtMs == nil { + return fmt.Errorf("decode PresenceChangedEvent: required field updated_at_ms is missing or null") + } + decoded.UpdatedAtMs = *fields.UpdatedAtMs + *value = PresenceChangedEvent(decoded) + return nil +} + +func (PresenceChangedEvent) EventName() string { return "presence-changed" } +func (PresenceChangedEvent) isDeltaEvent() {} +func (PresenceChangedEvent) isSubscribeEvent() {} + // RenderDeltaEvent is emitted by protocol v7. type RenderDeltaEvent struct { Cursor RenderCursor `json:"cursor"` @@ -3141,6 +3296,11 @@ func parseEvent(raw map[string]any) Event { if decodeEvent(raw, &event) { return event } + case "presence-changed": + var event PresenceChangedEvent + if decodeEvent(raw, &event) { + return event + } case "render-delta": var event RenderDeltaEvent if decodeEvent(raw, &event) { diff --git a/cmux-tui/bindings/go/raw/generated_metadata.go b/cmux-tui/bindings/go/raw/generated_metadata.go index 7409fb19c56f..2f8cd5dfdfaf 100644 --- a/cmux-tui/bindings/go/raw/generated_metadata.go +++ b/cmux-tui/bindings/go/raw/generated_metadata.go @@ -1,12 +1,12 @@ // Code generated by cmux-tui SDK codegen. DO NOT EDIT. -// Mux protocol 12; IR SHA-256 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. +// Mux protocol 12; IR SHA-256 e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. package raw const ( SDKSchemaVersion = 2 MuxProtocolVersion = 12 - SDKIRSHA256 = "8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86" + SDKIRSHA256 = "e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663" ) type Authority string @@ -123,6 +123,9 @@ var commandMetadata = map[string]CommandMetadata{ "pairing-response": {Name: "pairing-response", GoMethod: "PairingResponse", Authority: AuthorityLocalAdmin, Since: 7, Capability: "", Stream: "", FieldSince: nil, FieldCapabilities: nil}, "pane-neighbor": {Name: "pane-neighbor", GoMethod: "PaneNeighbor", Authority: AuthorityControl, Since: 6, Capability: "", Stream: "", FieldSince: nil, FieldCapabilities: nil}, "ping": {Name: "ping", GoMethod: "Ping", Authority: AuthorityControl, Since: 6, Capability: "", Stream: "", FieldSince: nil, FieldCapabilities: nil}, + "presence-clear": {Name: "presence-clear", GoMethod: "PresenceClear", Authority: AuthorityControl, Since: 12, Capability: "presence-v1", Stream: "", FieldSince: nil, FieldCapabilities: nil}, + "presence-list": {Name: "presence-list", GoMethod: "PresenceList", Authority: AuthorityControl, Since: 12, Capability: "presence-v1", Stream: "", FieldSince: nil, FieldCapabilities: nil}, + "presence-update": {Name: "presence-update", GoMethod: "PresenceUpdate", Authority: AuthorityControl, Since: 12, Capability: "presence-v1", Stream: "", FieldSince: nil, FieldCapabilities: nil}, "process-info": {Name: "process-info", GoMethod: "ProcessInfo", Authority: AuthorityControl, Since: 6, Capability: "", Stream: "", FieldSince: nil, FieldCapabilities: nil}, "put-frontend-projection": {Name: "put-frontend-projection", GoMethod: "PutFrontendProjection", Authority: AuthorityControl, Since: 7, Capability: "", Stream: "", FieldSince: nil, FieldCapabilities: nil}, "read-screen": {Name: "read-screen", GoMethod: "ReadScreen", Authority: AuthorityControl, Since: 5, Capability: "", Stream: "", FieldSince: nil, FieldCapabilities: nil}, @@ -160,7 +163,7 @@ var commandMetadata = map[string]CommandMetadata{ "shutdown-daemon": {Name: "shutdown-daemon", GoMethod: "ShutdownDaemon", Authority: AuthorityLocalAdmin, Since: 9, Capability: "", Stream: "", FieldSince: map[string]uint32{"force": 10}, FieldCapabilities: map[string]string{"force": "daemon-handoff-force-v1"}}, "sidebar-plugin": {Name: "sidebar-plugin", GoMethod: "SidebarPlugin", Authority: AuthorityFrontend, Since: 6, Capability: "", Stream: "", FieldSince: nil, FieldCapabilities: nil}, "split": {Name: "split", GoMethod: "Split", Authority: AuthorityControl, Since: 5, Capability: "", Stream: "", FieldSince: nil, FieldCapabilities: nil}, - "subscribe": {Name: "subscribe", GoMethod: "Subscribe", Authority: AuthorityFrontend, Since: 5, Capability: "", Stream: "subscribe", FieldSince: map[string]uint32{"surface": 9, "tree_events": 7}, FieldCapabilities: map[string]string{"surface": "surface-subscribe-filter"}}, + "subscribe": {Name: "subscribe", GoMethod: "Subscribe", Authority: AuthorityFrontend, Since: 5, Capability: "", Stream: "subscribe", FieldSince: map[string]uint32{"presence_only": 12, "surface": 9, "tree_events": 7}, FieldCapabilities: map[string]string{"presence_only": "presence-v1", "surface": "surface-subscribe-filter"}}, "swap-pane": {Name: "swap-pane", GoMethod: "SwapPane", Authority: AuthorityControl, Since: 6, Capability: "", Stream: "", FieldSince: nil, FieldCapabilities: nil}, "terminal-events": {Name: "terminal-events", GoMethod: "TerminalEvents", Authority: AuthorityControl, Since: 9, Capability: "", Stream: "", FieldSince: nil, FieldCapabilities: nil}, "undo-layout": {Name: "undo-layout", GoMethod: "UndoLayout", Authority: AuthorityControl, Since: 9, Capability: "layout-undo-v1", Stream: "", FieldSince: nil, FieldCapabilities: nil}, @@ -195,6 +198,7 @@ var eventMetadata = map[string]EventMetadata{ "pairing-resolved": {Name: "pairing-resolved", Since: 7, Capability: "", Streams: []string{"subscribe"}, Emission: "emitted"}, "pane-added": {Name: "pane-added", Since: 7, Capability: "", Streams: []string{"subscribe-deltas"}, Emission: "emitted"}, "pane-closed": {Name: "pane-closed", Since: 7, Capability: "", Streams: []string{"subscribe-deltas"}, Emission: "emitted"}, + "presence-changed": {Name: "presence-changed", Since: 12, Capability: "presence-v1", Streams: []string{"subscribe"}, Emission: "emitted"}, "render-delta": {Name: "render-delta", Since: 7, Capability: "", Streams: []string{"attach-render"}, Emission: "emitted"}, "render-state": {Name: "render-state", Since: 7, Capability: "", Streams: []string{"attach-render"}, Emission: "emitted"}, "resized": {Name: "resized", Since: 6, Capability: "", Streams: []string{"attach-byte"}, Emission: "emitted"}, @@ -264,7 +268,7 @@ func ProfileInfo(name Profile) (ProfileMetadata, bool) { } func AllCommandMetadata() []CommandMetadata { - result := make([]CommandMetadata, 0, 106) + result := make([]CommandMetadata, 0, 109) result = append(result, cloneCommandMetadata(commandMetadata["apply-layout"])) result = append(result, cloneCommandMetadata(commandMetadata["attach-surface"])) result = append(result, cloneCommandMetadata(commandMetadata["browser-activate"])) @@ -326,6 +330,9 @@ func AllCommandMetadata() []CommandMetadata { result = append(result, cloneCommandMetadata(commandMetadata["pairing-response"])) result = append(result, cloneCommandMetadata(commandMetadata["pane-neighbor"])) result = append(result, cloneCommandMetadata(commandMetadata["ping"])) + result = append(result, cloneCommandMetadata(commandMetadata["presence-clear"])) + result = append(result, cloneCommandMetadata(commandMetadata["presence-list"])) + result = append(result, cloneCommandMetadata(commandMetadata["presence-update"])) result = append(result, cloneCommandMetadata(commandMetadata["process-info"])) result = append(result, cloneCommandMetadata(commandMetadata["put-frontend-projection"])) result = append(result, cloneCommandMetadata(commandMetadata["read-screen"])) @@ -375,7 +382,7 @@ func AllCommandMetadata() []CommandMetadata { } func AllEventMetadata() []EventMetadata { - result := make([]EventMetadata, 0, 48) + result := make([]EventMetadata, 0, 49) var metadata EventMetadata metadata = eventMetadata["agent-changed"] metadata.Streams = append([]string(nil), metadata.Streams...) @@ -449,6 +456,9 @@ func AllEventMetadata() []EventMetadata { metadata = eventMetadata["pane-closed"] metadata.Streams = append([]string(nil), metadata.Streams...) result = append(result, metadata) + metadata = eventMetadata["presence-changed"] + metadata.Streams = append([]string(nil), metadata.Streams...) + result = append(result, metadata) metadata = eventMetadata["render-delta"] metadata.Streams = append([]string(nil), metadata.Streams...) result = append(result, metadata) diff --git a/cmux-tui/bindings/go/raw/generated_presence_test.go b/cmux-tui/bindings/go/raw/generated_presence_test.go index 7b1b3d3131f6..b36a7c90f00f 100644 --- a/cmux-tui/bindings/go/raw/generated_presence_test.go +++ b/cmux-tui/bindings/go/raw/generated_presence_test.go @@ -1,5 +1,5 @@ // Code generated by cmux-tui SDK codegen. DO NOT EDIT. -// Mux protocol 12; IR SHA-256 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. +// Mux protocol 12; IR SHA-256 e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. package raw @@ -995,6 +995,138 @@ func TestGeneratedSchemaPresenceRoundTrips(t *testing.T) { } assertGeneratedFieldJSON(t, presentValue, "ghostty_commit", true, "\"value\"") }) + t.Run("PresenceAnchorCell.ScrollOffset", func(t *testing.T) { + var missing PresenceAnchorCell + if err := json.Unmarshal([]byte("{\"col\":1,\"kind\":\"cell\",\"row\":1}"), &missing); err != nil { + t.Fatal(err) + } + if missing.ScrollOffset != nil { + t.Fatal("ScrollOffset did not preserve absence") + } + assertGeneratedFieldJSON(t, missing, "scroll_offset", false, "") + var nullValue PresenceAnchorCell + if err := json.Unmarshal([]byte("{\"col\":1,\"kind\":\"cell\",\"row\":1,\"scroll_offset\":null}"), &nullValue); err == nil { + t.Fatal("non-nullable field scroll_offset accepted null") + } + var presentValue PresenceAnchorCell + if err := json.Unmarshal([]byte("{\"col\":1,\"kind\":\"cell\",\"row\":1,\"scroll_offset\":1}"), &presentValue); err != nil { + t.Fatal(err) + } + if presentValue.ScrollOffset == nil { + t.Fatal("ScrollOffset lost its value") + } + assertGeneratedFieldJSON(t, presentValue, "scroll_offset", true, "1") + }) + t.Run("PresenceEntry.Highlight", func(t *testing.T) { + var missing PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &missing); err == nil { + t.Fatal("missing required nullable field highlight decoded successfully") + } + var nullValue PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.Highlight.IsSet() || !nullValue.Highlight.IsNull() { + t.Fatal("Highlight did not preserve required null") + } + assertGeneratedFieldJSON(t, nullValue, "highlight", true, "null") + var presentValue PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":{\"end\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"mode\":\"laser\",\"start\":{\"col\":1,\"kind\":\"cell\",\"row\":1}},\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.Highlight.Get(); !ok { + t.Fatal("Highlight did not preserve a required value") + } + assertGeneratedFieldJSON(t, presentValue, "highlight", true, "{\"end\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"mode\":\"laser\",\"start\":{\"col\":1,\"kind\":\"cell\",\"row\":1}}") + }) + t.Run("PresenceEntry.Kind", func(t *testing.T) { + var missing PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &missing); err == nil { + t.Fatal("missing required nullable field kind decoded successfully") + } + var nullValue PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.Kind.IsSet() || !nullValue.Kind.IsNull() { + t.Fatal("Kind did not preserve required null") + } + assertGeneratedFieldJSON(t, nullValue, "kind", true, "null") + var presentValue PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":\"value\",\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.Kind.Get(); !ok { + t.Fatal("Kind did not preserve a required value") + } + assertGeneratedFieldJSON(t, presentValue, "kind", true, "\"value\"") + }) + t.Run("PresenceEntry.Name", func(t *testing.T) { + var missing PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &missing); err == nil { + t.Fatal("missing required nullable field name decoded successfully") + } + var nullValue PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.Name.IsSet() || !nullValue.Name.IsNull() { + t.Fatal("Name did not preserve required null") + } + assertGeneratedFieldJSON(t, nullValue, "name", true, "null") + var presentValue PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":\"value\",\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.Name.Get(); !ok { + t.Fatal("Name did not preserve a required value") + } + assertGeneratedFieldJSON(t, presentValue, "name", true, "\"value\"") + }) + t.Run("PresenceEntry.Pointer", func(t *testing.T) { + var missing PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"surface\":null,\"updated_at_ms\":1}"), &missing); err == nil { + t.Fatal("missing required nullable field pointer decoded successfully") + } + var nullValue PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.Pointer.IsSet() || !nullValue.Pointer.IsNull() { + t.Fatal("Pointer did not preserve required null") + } + assertGeneratedFieldJSON(t, nullValue, "pointer", true, "null") + var presentValue PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"surface\":null,\"updated_at_ms\":1}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.Pointer.Get(); !ok { + t.Fatal("Pointer did not preserve a required value") + } + assertGeneratedFieldJSON(t, presentValue, "pointer", true, "{\"col\":1,\"kind\":\"cell\",\"row\":1}") + }) + t.Run("PresenceEntry.Surface", func(t *testing.T) { + var missing PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"updated_at_ms\":1}"), &missing); err == nil { + t.Fatal("missing required nullable field surface decoded successfully") + } + var nullValue PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.Surface.IsSet() || !nullValue.Surface.IsNull() { + t.Fatal("Surface did not preserve required null") + } + assertGeneratedFieldJSON(t, nullValue, "surface", true, "null") + var presentValue PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":1,\"updated_at_ms\":1}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.Surface.Get(); !ok { + t.Fatal("Surface did not preserve a required value") + } + assertGeneratedFieldJSON(t, presentValue, "surface", true, "1") + }) t.Run("ProcessInfoResult.Command", func(t *testing.T) { var missing ProcessInfoResult if err := json.Unmarshal([]byte("{\"cwd\":null,\"pid\":null}"), &missing); err == nil { @@ -8187,6 +8319,110 @@ func TestGeneratedSchemaPresenceRoundTrips(t *testing.T) { } assertGeneratedFieldJSON(t, presentValue, "surface", true, "1") }) + t.Run("PresenceUpdateRequest.Highlight", func(t *testing.T) { + var missing PresenceUpdateRequest + if err := json.Unmarshal([]byte("{\"surface\":1}"), &missing); err != nil { + t.Fatal(err) + } + if !missing.Highlight.IsAbsent() { + t.Fatal("Highlight did not preserve absence") + } + assertGeneratedFieldJSON(t, missing, "highlight", false, "") + var nullValue PresenceUpdateRequest + if err := json.Unmarshal([]byte("{\"surface\":1,\"highlight\":null}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.Highlight.IsNull() { + t.Fatal("Highlight did not preserve null") + } + assertGeneratedFieldJSON(t, nullValue, "highlight", true, "null") + var presentValue PresenceUpdateRequest + if err := json.Unmarshal([]byte("{\"surface\":1,\"highlight\":{\"end\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"mode\":\"laser\",\"start\":{\"col\":1,\"kind\":\"cell\",\"row\":1}}}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.Highlight.Get(); !ok { + t.Fatal("Highlight did not preserve a value") + } + assertGeneratedFieldJSON(t, presentValue, "highlight", true, "{\"end\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"mode\":\"laser\",\"start\":{\"col\":1,\"kind\":\"cell\",\"row\":1}}") + }) + t.Run("PresenceUpdateRequest.Pointer", func(t *testing.T) { + var missing PresenceUpdateRequest + if err := json.Unmarshal([]byte("{\"surface\":1}"), &missing); err != nil { + t.Fatal(err) + } + if !missing.Pointer.IsAbsent() { + t.Fatal("Pointer did not preserve absence") + } + assertGeneratedFieldJSON(t, missing, "pointer", false, "") + var nullValue PresenceUpdateRequest + if err := json.Unmarshal([]byte("{\"surface\":1,\"pointer\":null}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.Pointer.IsNull() { + t.Fatal("Pointer did not preserve null") + } + assertGeneratedFieldJSON(t, nullValue, "pointer", true, "null") + var presentValue PresenceUpdateRequest + if err := json.Unmarshal([]byte("{\"surface\":1,\"pointer\":{\"col\":1,\"kind\":\"cell\",\"row\":1}}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.Pointer.Get(); !ok { + t.Fatal("Pointer did not preserve a value") + } + assertGeneratedFieldJSON(t, presentValue, "pointer", true, "{\"col\":1,\"kind\":\"cell\",\"row\":1}") + }) + t.Run("PresenceUpdateOptions.Highlight", func(t *testing.T) { + var missing PresenceUpdateOptions + if err := json.Unmarshal([]byte("{}"), &missing); err != nil { + t.Fatal(err) + } + if !missing.Highlight.IsAbsent() { + t.Fatal("Highlight did not preserve absence") + } + assertGeneratedFieldJSON(t, missing, "highlight", false, "") + var nullValue PresenceUpdateOptions + if err := json.Unmarshal([]byte("{\"highlight\":null}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.Highlight.IsNull() { + t.Fatal("Highlight did not preserve null") + } + assertGeneratedFieldJSON(t, nullValue, "highlight", true, "null") + var presentValue PresenceUpdateOptions + if err := json.Unmarshal([]byte("{\"highlight\":{\"end\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"mode\":\"laser\",\"start\":{\"col\":1,\"kind\":\"cell\",\"row\":1}}}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.Highlight.Get(); !ok { + t.Fatal("Highlight did not preserve a value") + } + assertGeneratedFieldJSON(t, presentValue, "highlight", true, "{\"end\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"mode\":\"laser\",\"start\":{\"col\":1,\"kind\":\"cell\",\"row\":1}}") + }) + t.Run("PresenceUpdateOptions.Pointer", func(t *testing.T) { + var missing PresenceUpdateOptions + if err := json.Unmarshal([]byte("{}"), &missing); err != nil { + t.Fatal(err) + } + if !missing.Pointer.IsAbsent() { + t.Fatal("Pointer did not preserve absence") + } + assertGeneratedFieldJSON(t, missing, "pointer", false, "") + var nullValue PresenceUpdateOptions + if err := json.Unmarshal([]byte("{\"pointer\":null}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.Pointer.IsNull() { + t.Fatal("Pointer did not preserve null") + } + assertGeneratedFieldJSON(t, nullValue, "pointer", true, "null") + var presentValue PresenceUpdateOptions + if err := json.Unmarshal([]byte("{\"pointer\":{\"col\":1,\"kind\":\"cell\",\"row\":1}}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.Pointer.Get(); !ok { + t.Fatal("Pointer did not preserve a value") + } + assertGeneratedFieldJSON(t, presentValue, "pointer", true, "{\"col\":1,\"kind\":\"cell\",\"row\":1}") + }) t.Run("PutFrontendProjectionRequest.ExpectedGeneration", func(t *testing.T) { var missing PutFrontendProjectionRequest if err := json.Unmarshal([]byte("{\"frontend\":\"value\",\"projection\":null,\"schema_version\":1,\"scope\":\"value\",\"subject_key\":\"value\"}"), &missing); err != nil { @@ -10865,6 +11101,32 @@ func TestGeneratedSchemaPresenceRoundTrips(t *testing.T) { } assertGeneratedFieldJSON(t, presentValue, "rows", true, "1") }) + t.Run("SubscribeRequest.PresenceOnly", func(t *testing.T) { + var missing SubscribeRequest + if err := json.Unmarshal([]byte("{}"), &missing); err != nil { + t.Fatal(err) + } + if !missing.PresenceOnly.IsAbsent() { + t.Fatal("PresenceOnly did not preserve absence") + } + assertGeneratedFieldJSON(t, missing, "presence_only", false, "") + var nullValue SubscribeRequest + if err := json.Unmarshal([]byte("{\"presence_only\":null}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.PresenceOnly.IsNull() { + t.Fatal("PresenceOnly did not preserve null") + } + assertGeneratedFieldJSON(t, nullValue, "presence_only", true, "null") + var presentValue SubscribeRequest + if err := json.Unmarshal([]byte("{\"presence_only\":true}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.PresenceOnly.Get(); !ok { + t.Fatal("PresenceOnly did not preserve a value") + } + assertGeneratedFieldJSON(t, presentValue, "presence_only", true, "true") + }) t.Run("SubscribeRequest.Surface", func(t *testing.T) { var missing SubscribeRequest if err := json.Unmarshal([]byte("{}"), &missing); err != nil { @@ -11897,6 +12159,116 @@ func TestGeneratedSchemaPresenceRoundTrips(t *testing.T) { } assertGeneratedFieldJSON(t, presentValue, "surface", true, "1") }) + t.Run("PresenceChangedEvent.Highlight", func(t *testing.T) { + var missing PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &missing); err == nil { + t.Fatal("missing required nullable field highlight decoded successfully") + } + var nullValue PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.Highlight.IsSet() || !nullValue.Highlight.IsNull() { + t.Fatal("Highlight did not preserve required null") + } + assertGeneratedFieldJSON(t, nullValue, "highlight", true, "null") + var presentValue PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":{\"end\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"mode\":\"laser\",\"start\":{\"col\":1,\"kind\":\"cell\",\"row\":1}},\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.Highlight.Get(); !ok { + t.Fatal("Highlight did not preserve a required value") + } + assertGeneratedFieldJSON(t, presentValue, "highlight", true, "{\"end\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"mode\":\"laser\",\"start\":{\"col\":1,\"kind\":\"cell\",\"row\":1}}") + }) + t.Run("PresenceChangedEvent.Kind", func(t *testing.T) { + var missing PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &missing); err == nil { + t.Fatal("missing required nullable field kind decoded successfully") + } + var nullValue PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.Kind.IsSet() || !nullValue.Kind.IsNull() { + t.Fatal("Kind did not preserve required null") + } + assertGeneratedFieldJSON(t, nullValue, "kind", true, "null") + var presentValue PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":\"value\",\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.Kind.Get(); !ok { + t.Fatal("Kind did not preserve a required value") + } + assertGeneratedFieldJSON(t, presentValue, "kind", true, "\"value\"") + }) + t.Run("PresenceChangedEvent.Name", func(t *testing.T) { + var missing PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &missing); err == nil { + t.Fatal("missing required nullable field name decoded successfully") + } + var nullValue PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.Name.IsSet() || !nullValue.Name.IsNull() { + t.Fatal("Name did not preserve required null") + } + assertGeneratedFieldJSON(t, nullValue, "name", true, "null") + var presentValue PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":\"value\",\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.Name.Get(); !ok { + t.Fatal("Name did not preserve a required value") + } + assertGeneratedFieldJSON(t, presentValue, "name", true, "\"value\"") + }) + t.Run("PresenceChangedEvent.Pointer", func(t *testing.T) { + var missing PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"surface\":null,\"updated_at_ms\":1}"), &missing); err == nil { + t.Fatal("missing required nullable field pointer decoded successfully") + } + var nullValue PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.Pointer.IsSet() || !nullValue.Pointer.IsNull() { + t.Fatal("Pointer did not preserve required null") + } + assertGeneratedFieldJSON(t, nullValue, "pointer", true, "null") + var presentValue PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"surface\":null,\"updated_at_ms\":1}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.Pointer.Get(); !ok { + t.Fatal("Pointer did not preserve a required value") + } + assertGeneratedFieldJSON(t, presentValue, "pointer", true, "{\"col\":1,\"kind\":\"cell\",\"row\":1}") + }) + t.Run("PresenceChangedEvent.Surface", func(t *testing.T) { + var missing PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"updated_at_ms\":1}"), &missing); err == nil { + t.Fatal("missing required nullable field surface decoded successfully") + } + var nullValue PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &nullValue); err != nil { + t.Fatal(err) + } + if !nullValue.Surface.IsSet() || !nullValue.Surface.IsNull() { + t.Fatal("Surface did not preserve required null") + } + assertGeneratedFieldJSON(t, nullValue, "surface", true, "null") + var presentValue PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":1,\"updated_at_ms\":1}"), &presentValue); err != nil { + t.Fatal(err) + } + if _, ok := presentValue.Surface.Get(); !ok { + t.Fatal("Surface did not preserve a required value") + } + assertGeneratedFieldJSON(t, presentValue, "surface", true, "1") + }) t.Run("RenderDeltaEvent.DefaultBg", func(t *testing.T) { var missing RenderDeltaEvent if err := json.Unmarshal([]byte("{\"cursor\":{\"blink\":true,\"color\":null,\"style\":\"block\",\"visible\":true,\"x\":1,\"y\":1},\"full\":true,\"rows\":[],\"surface\":1}"), &missing); err != nil { @@ -13610,6 +13982,120 @@ func TestGeneratedRequiredFieldsRejectOmission(t *testing.T) { t.Fatal("missing required field version decoded successfully") } }) + t.Run("PresenceAnchorCell.Col", func(t *testing.T) { + var decoded PresenceAnchorCell + if err := json.Unmarshal([]byte("{\"kind\":\"cell\",\"row\":1}"), &decoded); err == nil { + t.Fatal("missing required field col decoded successfully") + } + }) + t.Run("PresenceAnchorCell.Kind", func(t *testing.T) { + var decoded PresenceAnchorCell + if err := json.Unmarshal([]byte("{\"col\":1,\"row\":1}"), &decoded); err == nil { + t.Fatal("missing required field kind decoded successfully") + } + }) + t.Run("PresenceAnchorCell.Row", func(t *testing.T) { + var decoded PresenceAnchorCell + if err := json.Unmarshal([]byte("{\"col\":1,\"kind\":\"cell\"}"), &decoded); err == nil { + t.Fatal("missing required field row decoded successfully") + } + }) + t.Run("PresenceAnchorPoint.Kind", func(t *testing.T) { + var decoded PresenceAnchorPoint + if err := json.Unmarshal([]byte("{\"x\":1.5,\"y\":1.5}"), &decoded); err == nil { + t.Fatal("missing required field kind decoded successfully") + } + }) + t.Run("PresenceAnchorPoint.X", func(t *testing.T) { + var decoded PresenceAnchorPoint + if err := json.Unmarshal([]byte("{\"kind\":\"point\",\"y\":1.5}"), &decoded); err == nil { + t.Fatal("missing required field x decoded successfully") + } + }) + t.Run("PresenceAnchorPoint.Y", func(t *testing.T) { + var decoded PresenceAnchorPoint + if err := json.Unmarshal([]byte("{\"kind\":\"point\",\"x\":1.5}"), &decoded); err == nil { + t.Fatal("missing required field y decoded successfully") + } + }) + t.Run("PresenceEntry.Client", func(t *testing.T) { + var decoded PresenceEntry + if err := json.Unmarshal([]byte("{\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field client decoded successfully") + } + }) + t.Run("PresenceEntry.Color", func(t *testing.T) { + var decoded PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field color decoded successfully") + } + }) + t.Run("PresenceEntry.Generation", func(t *testing.T) { + var decoded PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field generation decoded successfully") + } + }) + t.Run("PresenceEntry.Highlight", func(t *testing.T) { + var decoded PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field highlight decoded successfully") + } + }) + t.Run("PresenceEntry.Kind", func(t *testing.T) { + var decoded PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field kind decoded successfully") + } + }) + t.Run("PresenceEntry.Name", func(t *testing.T) { + var decoded PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field name decoded successfully") + } + }) + t.Run("PresenceEntry.Pointer", func(t *testing.T) { + var decoded PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field pointer decoded successfully") + } + }) + t.Run("PresenceEntry.Surface", func(t *testing.T) { + var decoded PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field surface decoded successfully") + } + }) + t.Run("PresenceEntry.UpdatedAtMs", func(t *testing.T) { + var decoded PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null}"), &decoded); err == nil { + t.Fatal("missing required field updated_at_ms decoded successfully") + } + }) + t.Run("PresenceHighlight.End", func(t *testing.T) { + var decoded PresenceHighlight + if err := json.Unmarshal([]byte("{\"mode\":\"laser\",\"start\":{\"col\":1,\"kind\":\"cell\",\"row\":1}}"), &decoded); err == nil { + t.Fatal("missing required field end decoded successfully") + } + }) + t.Run("PresenceHighlight.Mode", func(t *testing.T) { + var decoded PresenceHighlight + if err := json.Unmarshal([]byte("{\"end\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"start\":{\"col\":1,\"kind\":\"cell\",\"row\":1}}"), &decoded); err == nil { + t.Fatal("missing required field mode decoded successfully") + } + }) + t.Run("PresenceHighlight.Start", func(t *testing.T) { + var decoded PresenceHighlight + if err := json.Unmarshal([]byte("{\"end\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"mode\":\"laser\"}"), &decoded); err == nil { + t.Fatal("missing required field start decoded successfully") + } + }) + t.Run("PresenceListResult.Entries", func(t *testing.T) { + var decoded PresenceListResult + if err := json.Unmarshal([]byte("{}"), &decoded); err == nil { + t.Fatal("missing required field entries decoded successfully") + } + }) t.Run("ProcessInfoResult.Command", func(t *testing.T) { var decoded ProcessInfoResult if err := json.Unmarshal([]byte("{\"cwd\":null,\"pid\":null}"), &decoded); err == nil { @@ -15458,6 +15944,12 @@ func TestGeneratedRequiredFieldsRejectOmission(t *testing.T) { t.Fatal("missing required field pane decoded successfully") } }) + t.Run("PresenceUpdateRequest.Surface", func(t *testing.T) { + var decoded PresenceUpdateRequest + if err := json.Unmarshal([]byte("{}"), &decoded); err == nil { + t.Fatal("missing required field surface decoded successfully") + } + }) t.Run("ProcessInfoRequest.Surface", func(t *testing.T) { var decoded ProcessInfoRequest if err := json.Unmarshal([]byte("{}"), &decoded); err == nil { @@ -16274,6 +16766,60 @@ func TestGeneratedRequiredFieldsRejectOmission(t *testing.T) { t.Fatal("missing required field workspace decoded successfully") } }) + t.Run("PresenceChangedEvent.Client", func(t *testing.T) { + var decoded PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field client decoded successfully") + } + }) + t.Run("PresenceChangedEvent.Color", func(t *testing.T) { + var decoded PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field color decoded successfully") + } + }) + t.Run("PresenceChangedEvent.Generation", func(t *testing.T) { + var decoded PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field generation decoded successfully") + } + }) + t.Run("PresenceChangedEvent.Highlight", func(t *testing.T) { + var decoded PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field highlight decoded successfully") + } + }) + t.Run("PresenceChangedEvent.Kind", func(t *testing.T) { + var decoded PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field kind decoded successfully") + } + }) + t.Run("PresenceChangedEvent.Name", func(t *testing.T) { + var decoded PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field name decoded successfully") + } + }) + t.Run("PresenceChangedEvent.Pointer", func(t *testing.T) { + var decoded PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field pointer decoded successfully") + } + }) + t.Run("PresenceChangedEvent.Surface", func(t *testing.T) { + var decoded PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("missing required field surface decoded successfully") + } + }) + t.Run("PresenceChangedEvent.UpdatedAtMs", func(t *testing.T) { + var decoded PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null}"), &decoded); err == nil { + t.Fatal("missing required field updated_at_ms decoded successfully") + } + }) t.Run("RenderDeltaEvent.Cursor", func(t *testing.T) { var decoded RenderDeltaEvent if err := json.Unmarshal([]byte("{\"full\":true,\"rows\":[],\"surface\":1}"), &decoded); err == nil { @@ -17837,6 +18383,90 @@ func TestGeneratedRequiredNonnullableFieldsRejectNull(t *testing.T) { t.Fatal("required non-nullable field version accepted null") } }) + t.Run("PresenceAnchorCell.Col", func(t *testing.T) { + var decoded PresenceAnchorCell + if err := json.Unmarshal([]byte("{\"col\":null,\"kind\":\"cell\",\"row\":1}"), &decoded); err == nil { + t.Fatal("required non-nullable field col accepted null") + } + }) + t.Run("PresenceAnchorCell.Kind", func(t *testing.T) { + var decoded PresenceAnchorCell + if err := json.Unmarshal([]byte("{\"col\":1,\"kind\":null,\"row\":1}"), &decoded); err == nil { + t.Fatal("required non-nullable field kind accepted null") + } + }) + t.Run("PresenceAnchorCell.Row", func(t *testing.T) { + var decoded PresenceAnchorCell + if err := json.Unmarshal([]byte("{\"col\":1,\"kind\":\"cell\",\"row\":null}"), &decoded); err == nil { + t.Fatal("required non-nullable field row accepted null") + } + }) + t.Run("PresenceAnchorPoint.Kind", func(t *testing.T) { + var decoded PresenceAnchorPoint + if err := json.Unmarshal([]byte("{\"kind\":null,\"x\":1.5,\"y\":1.5}"), &decoded); err == nil { + t.Fatal("required non-nullable field kind accepted null") + } + }) + t.Run("PresenceAnchorPoint.X", func(t *testing.T) { + var decoded PresenceAnchorPoint + if err := json.Unmarshal([]byte("{\"kind\":\"point\",\"x\":null,\"y\":1.5}"), &decoded); err == nil { + t.Fatal("required non-nullable field x accepted null") + } + }) + t.Run("PresenceAnchorPoint.Y", func(t *testing.T) { + var decoded PresenceAnchorPoint + if err := json.Unmarshal([]byte("{\"kind\":\"point\",\"x\":1.5,\"y\":null}"), &decoded); err == nil { + t.Fatal("required non-nullable field y accepted null") + } + }) + t.Run("PresenceEntry.Client", func(t *testing.T) { + var decoded PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":null,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("required non-nullable field client accepted null") + } + }) + t.Run("PresenceEntry.Color", func(t *testing.T) { + var decoded PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":null,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("required non-nullable field color accepted null") + } + }) + t.Run("PresenceEntry.Generation", func(t *testing.T) { + var decoded PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":null,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("required non-nullable field generation accepted null") + } + }) + t.Run("PresenceEntry.UpdatedAtMs", func(t *testing.T) { + var decoded PresenceEntry + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":null}"), &decoded); err == nil { + t.Fatal("required non-nullable field updated_at_ms accepted null") + } + }) + t.Run("PresenceHighlight.End", func(t *testing.T) { + var decoded PresenceHighlight + if err := json.Unmarshal([]byte("{\"end\":null,\"mode\":\"laser\",\"start\":{\"col\":1,\"kind\":\"cell\",\"row\":1}}"), &decoded); err == nil { + t.Fatal("required non-nullable field end accepted null") + } + }) + t.Run("PresenceHighlight.Mode", func(t *testing.T) { + var decoded PresenceHighlight + if err := json.Unmarshal([]byte("{\"end\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"mode\":null,\"start\":{\"col\":1,\"kind\":\"cell\",\"row\":1}}"), &decoded); err == nil { + t.Fatal("required non-nullable field mode accepted null") + } + }) + t.Run("PresenceHighlight.Start", func(t *testing.T) { + var decoded PresenceHighlight + if err := json.Unmarshal([]byte("{\"end\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"mode\":\"laser\",\"start\":null}"), &decoded); err == nil { + t.Fatal("required non-nullable field start accepted null") + } + }) + t.Run("PresenceListResult.Entries", func(t *testing.T) { + var decoded PresenceListResult + if err := json.Unmarshal([]byte("{\"entries\":null}"), &decoded); err == nil { + t.Fatal("required non-nullable field entries accepted null") + } + }) t.Run("ProviderWorkspaceMutationResult.Key", func(t *testing.T) { var decoded ProviderWorkspaceMutationResult if err := json.Unmarshal([]byte("{\"key\":null,\"workspace\":1,\"workspace_revision\":1}"), &decoded); err == nil { @@ -19421,6 +20051,12 @@ func TestGeneratedRequiredNonnullableFieldsRejectNull(t *testing.T) { t.Fatal("required non-nullable field pane accepted null") } }) + t.Run("PresenceUpdateRequest.Surface", func(t *testing.T) { + var decoded PresenceUpdateRequest + if err := json.Unmarshal([]byte("{\"surface\":null}"), &decoded); err == nil { + t.Fatal("required non-nullable field surface accepted null") + } + }) t.Run("ProcessInfoRequest.Surface", func(t *testing.T) { var decoded ProcessInfoRequest if err := json.Unmarshal([]byte("{\"surface\":null}"), &decoded); err == nil { @@ -20153,6 +20789,30 @@ func TestGeneratedRequiredNonnullableFieldsRejectNull(t *testing.T) { t.Fatal("required non-nullable field workspace accepted null") } }) + t.Run("PresenceChangedEvent.Client", func(t *testing.T) { + var decoded PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":null,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("required non-nullable field client accepted null") + } + }) + t.Run("PresenceChangedEvent.Color", func(t *testing.T) { + var decoded PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":null,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("required non-nullable field color accepted null") + } + }) + t.Run("PresenceChangedEvent.Generation", func(t *testing.T) { + var decoded PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":null,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":1}"), &decoded); err == nil { + t.Fatal("required non-nullable field generation accepted null") + } + }) + t.Run("PresenceChangedEvent.UpdatedAtMs", func(t *testing.T) { + var decoded PresenceChangedEvent + if err := json.Unmarshal([]byte("{\"client\":1,\"color\":1,\"generation\":1,\"highlight\":null,\"kind\":null,\"name\":null,\"pointer\":null,\"surface\":null,\"updated_at_ms\":null}"), &decoded); err == nil { + t.Fatal("required non-nullable field updated_at_ms accepted null") + } + }) t.Run("RenderDeltaEvent.Cursor", func(t *testing.T) { var decoded RenderDeltaEvent if err := json.Unmarshal([]byte("{\"cursor\":null,\"full\":true,\"rows\":[],\"surface\":1}"), &decoded); err == nil { @@ -20876,6 +21536,24 @@ func TestGeneratedConstrainedFieldsRejectUnknownValues(t *testing.T) { t.Fatal("invalid constrained field ok decoded successfully") } }) + t.Run("PresenceAnchorCell.Kind", func(t *testing.T) { + var decoded PresenceAnchorCell + if err := json.Unmarshal([]byte("{\"col\":1,\"kind\":\"__cmux_invalid__\",\"row\":1}"), &decoded); err == nil { + t.Fatal("invalid constrained field kind decoded successfully") + } + }) + t.Run("PresenceAnchorPoint.Kind", func(t *testing.T) { + var decoded PresenceAnchorPoint + if err := json.Unmarshal([]byte("{\"kind\":\"__cmux_invalid__\",\"x\":1.5,\"y\":1.5}"), &decoded); err == nil { + t.Fatal("invalid constrained field kind decoded successfully") + } + }) + t.Run("PresenceHighlight.Mode", func(t *testing.T) { + var decoded PresenceHighlight + if err := json.Unmarshal([]byte("{\"end\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"mode\":\"__cmux_invalid__\",\"start\":{\"col\":1,\"kind\":\"cell\",\"row\":1}}"), &decoded); err == nil { + t.Fatal("invalid constrained field mode decoded successfully") + } + }) t.Run("RenderCursor.Style", func(t *testing.T) { var decoded RenderCursor if err := json.Unmarshal([]byte("{\"blink\":true,\"color\":null,\"style\":\"__cmux_invalid__\",\"visible\":true,\"x\":1,\"y\":1}"), &decoded); err == nil { @@ -21533,6 +22211,36 @@ func TestGeneratedConstrainedFieldsRejectUnknownValuesOnMarshal(t *testing.T) { t.Fatal("invalid constrained field ok encoded successfully") } }) + t.Run("PresenceAnchorCell.Kind", func(t *testing.T) { + var decoded PresenceAnchorCell + if err := json.Unmarshal([]byte("{\"col\":1,\"kind\":\"cell\",\"row\":1}"), &decoded); err != nil { + t.Fatal(err) + } + decoded.Kind = PresenceAnchorCellKind("__cmux_invalid__") + if _, err := json.Marshal(decoded); err == nil { + t.Fatal("invalid constrained field kind encoded successfully") + } + }) + t.Run("PresenceAnchorPoint.Kind", func(t *testing.T) { + var decoded PresenceAnchorPoint + if err := json.Unmarshal([]byte("{\"kind\":\"point\",\"x\":1.5,\"y\":1.5}"), &decoded); err != nil { + t.Fatal(err) + } + decoded.Kind = PresenceAnchorPointKind("__cmux_invalid__") + if _, err := json.Marshal(decoded); err == nil { + t.Fatal("invalid constrained field kind encoded successfully") + } + }) + t.Run("PresenceHighlight.Mode", func(t *testing.T) { + var decoded PresenceHighlight + if err := json.Unmarshal([]byte("{\"end\":{\"col\":1,\"kind\":\"cell\",\"row\":1},\"mode\":\"laser\",\"start\":{\"col\":1,\"kind\":\"cell\",\"row\":1}}"), &decoded); err != nil { + t.Fatal(err) + } + decoded.Mode = PresenceHighlightMode("__cmux_invalid__") + if _, err := json.Marshal(decoded); err == nil { + t.Fatal("invalid constrained field mode encoded successfully") + } + }) t.Run("RenderCursor.Style", func(t *testing.T) { var decoded RenderCursor if err := json.Unmarshal([]byte("{\"blink\":true,\"color\":null,\"style\":\"block\",\"visible\":true,\"x\":1,\"y\":1}"), &decoded); err != nil { @@ -22108,12 +22816,12 @@ func TestGeneratedConstrainedFieldsRejectUnknownValuesOnMarshal(t *testing.T) { } const ( - generatedFieldShapeCount = 505 - generatedOptionalNullableFieldCount = 334 - generatedRequiredNullableFieldCount = 79 - generatedOptionalNonnullableFieldCount = 92 - generatedRequiredFieldCount = 722 - generatedRequiredNonnullableFieldCount = 643 - generatedConstrainedFieldCount = 88 - generatedEncodedConstrainedFieldCount = 88 + generatedFieldShapeCount = 521 + generatedOptionalNullableFieldCount = 339 + generatedRequiredNullableFieldCount = 89 + generatedOptionalNonnullableFieldCount = 93 + generatedRequiredFieldCount = 751 + generatedRequiredNonnullableFieldCount = 662 + generatedConstrainedFieldCount = 91 + generatedEncodedConstrainedFieldCount = 91 ) diff --git a/cmux-tui/bindings/go/raw/generated_types.go b/cmux-tui/bindings/go/raw/generated_types.go index 0bdcb1ce387b..8f3d86f77df5 100644 --- a/cmux-tui/bindings/go/raw/generated_types.go +++ b/cmux-tui/bindings/go/raw/generated_types.go @@ -1,5 +1,5 @@ // Code generated by cmux-tui SDK codegen. DO NOT EDIT. -// Mux protocol 12; IR SHA-256 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. +// Mux protocol 12; IR SHA-256 e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. package raw @@ -4345,6 +4345,518 @@ func (value *PingResult) UnmarshalJSON(data []byte) error { return nil } +type PresenceAnchorCellKind string + +const ( + PresenceAnchorCellKindCell PresenceAnchorCellKind = "cell" +) + +func (value PresenceAnchorCellKind) valid() bool { + switch value { + case PresenceAnchorCellKindCell: + return true + default: + return false + } +} + +func (value PresenceAnchorCellKind) MarshalJSON() ([]byte, error) { + if !value.valid() { + return nil, fmt.Errorf("%s has invalid value %v", "PresenceAnchorCellKind", value) + } + return json.Marshal(string(value)) +} + +func (value *PresenceAnchorCellKind) UnmarshalJSON(data []byte) error { + var decoded string + if err := json.Unmarshal(data, &decoded); err != nil { + return err + } + candidate := PresenceAnchorCellKind(decoded) + if !candidate.valid() { + return fmt.Errorf("%s has invalid value %v", "PresenceAnchorCellKind", decoded) + } + *value = candidate + return nil +} + +type PresenceAnchorCell struct { + Col uint32 `json:"col"` + Kind PresenceAnchorCellKind `json:"kind"` + Row uint32 `json:"row"` + ScrollOffset *uint64 `json:"scroll_offset,omitempty"` +} + +func (value PresenceAnchorCell) MarshalJSON() ([]byte, error) { + switch value.Kind { + case "cell": + default: + return nil, fmt.Errorf("encode PresenceAnchorCell.Kind: invalid value %v", value.Kind) + } + type wire PresenceAnchorCell + return json.Marshal(wire(value)) +} + +func (value *PresenceAnchorCell) UnmarshalJSON(data []byte) error { + if !isJSONObject(data) { + return fmt.Errorf("decode PresenceAnchorCell: expected object") + } + var fields struct { + Col *uint32 `json:"col"` + Kind *PresenceAnchorCellKind `json:"kind"` + Row *uint32 `json:"row"` + ScrollOffset optionalNonNullJSON[uint64] `json:"scroll_offset"` + } + if err := json.Unmarshal(data, &fields); err != nil { + return fmt.Errorf("decode PresenceAnchorCell: %w", err) + } + type wire PresenceAnchorCell + var decoded wire + if fields.Col == nil { + return fmt.Errorf("decode PresenceAnchorCell: required field col is missing or null") + } + decoded.Col = *fields.Col + if fields.Kind == nil { + return fmt.Errorf("decode PresenceAnchorCell: required field kind is missing or null") + } + decoded.Kind = *fields.Kind + switch decoded.Kind { + case "cell": + default: + return fmt.Errorf("decode PresenceAnchorCell.Kind: invalid value %v", decoded.Kind) + } + if fields.Row == nil { + return fmt.Errorf("decode PresenceAnchorCell: required field row is missing or null") + } + decoded.Row = *fields.Row + if fields.ScrollOffset.null { + return fmt.Errorf("decode PresenceAnchorCell: non-nullable field scroll_offset is null") + } + if fields.ScrollOffset.set { + decoded.ScrollOffset = &fields.ScrollOffset.value + } + *value = PresenceAnchorCell(decoded) + return nil +} + +type PresenceAnchorPointKind string + +const ( + PresenceAnchorPointKindPoint PresenceAnchorPointKind = "point" +) + +func (value PresenceAnchorPointKind) valid() bool { + switch value { + case PresenceAnchorPointKindPoint: + return true + default: + return false + } +} + +func (value PresenceAnchorPointKind) MarshalJSON() ([]byte, error) { + if !value.valid() { + return nil, fmt.Errorf("%s has invalid value %v", "PresenceAnchorPointKind", value) + } + return json.Marshal(string(value)) +} + +func (value *PresenceAnchorPointKind) UnmarshalJSON(data []byte) error { + var decoded string + if err := json.Unmarshal(data, &decoded); err != nil { + return err + } + candidate := PresenceAnchorPointKind(decoded) + if !candidate.valid() { + return fmt.Errorf("%s has invalid value %v", "PresenceAnchorPointKind", decoded) + } + *value = candidate + return nil +} + +type PresenceAnchorPoint struct { + Kind PresenceAnchorPointKind `json:"kind"` + X float64 `json:"x"` + Y float64 `json:"y"` +} + +func (value PresenceAnchorPoint) MarshalJSON() ([]byte, error) { + switch value.Kind { + case "point": + default: + return nil, fmt.Errorf("encode PresenceAnchorPoint.Kind: invalid value %v", value.Kind) + } + type wire PresenceAnchorPoint + return json.Marshal(wire(value)) +} + +func (value *PresenceAnchorPoint) UnmarshalJSON(data []byte) error { + if !isJSONObject(data) { + return fmt.Errorf("decode PresenceAnchorPoint: expected object") + } + var fields struct { + Kind *PresenceAnchorPointKind `json:"kind"` + X *float64 `json:"x"` + Y *float64 `json:"y"` + } + if err := json.Unmarshal(data, &fields); err != nil { + return fmt.Errorf("decode PresenceAnchorPoint: %w", err) + } + type wire PresenceAnchorPoint + var decoded wire + if fields.Kind == nil { + return fmt.Errorf("decode PresenceAnchorPoint: required field kind is missing or null") + } + decoded.Kind = *fields.Kind + switch decoded.Kind { + case "point": + default: + return fmt.Errorf("decode PresenceAnchorPoint.Kind: invalid value %v", decoded.Kind) + } + if fields.X == nil { + return fmt.Errorf("decode PresenceAnchorPoint: required field x is missing or null") + } + decoded.X = *fields.X + if fields.Y == nil { + return fmt.Errorf("decode PresenceAnchorPoint: required field y is missing or null") + } + decoded.Y = *fields.Y + *value = PresenceAnchorPoint(decoded) + return nil +} + +type PresenceAnchor struct { + Tag string `json:"kind"` + Value any `json:"-"` + Raw json.RawMessage `json:"-"` +} + +func (value *PresenceAnchor) UnmarshalJSON(data []byte) error { + var fields map[string]json.RawMessage + if err := decodeJSON(data, &fields); err != nil { + return err + } + rawTag, hasTag := fields["kind"] + if !hasTag { + return fmt.Errorf("decode PresenceAnchor: required field kind is missing") + } + if isJSONNull(rawTag) { + return fmt.Errorf("decode PresenceAnchor: non-nullable field kind is null") + } + var decodedTag string + if err := decodeJSON(rawTag, &decodedTag); err != nil { + return fmt.Errorf("decode PresenceAnchor.Kind: %w", err) + } + value.Tag = decodedTag + value.Raw = append(value.Raw[:0], data...) + switch decodedTag { + case "cell": + var decoded PresenceAnchorCell + if err := decodeJSON(data, &decoded); err != nil { + return err + } + value.Value = decoded + case "point": + var decoded PresenceAnchorPoint + if err := decodeJSON(data, &decoded); err != nil { + return err + } + value.Value = decoded + default: + value.Value = nil + } + return nil +} + +func (value PresenceAnchor) MarshalJSON() ([]byte, error) { + if value.Value == nil { + if value.Raw != nil { + return value.Raw, nil + } + return json.Marshal(map[string]any{"kind": value.Tag}) + } + payload, err := json.Marshal(value.Value) + if err != nil { + return nil, err + } + var fields map[string]json.RawMessage + if err := decodeJSON(payload, &fields); err != nil { + return nil, err + } + encodedTag, err := json.Marshal(value.Tag) + if err != nil { + return nil, err + } + fields["kind"] = encodedTag + return json.Marshal(fields) +} + +func NewPresenceAnchorCell(value PresenceAnchorCell) PresenceAnchor { + value.Kind = PresenceAnchorCellKindCell + return PresenceAnchor{Tag: "cell", Value: value} +} + +func (value PresenceAnchor) AsCell() (PresenceAnchorCell, bool) { + decoded, ok := value.Value.(PresenceAnchorCell) + return decoded, ok +} + +func NewPresenceAnchorPoint(value PresenceAnchorPoint) PresenceAnchor { + value.Kind = PresenceAnchorPointKindPoint + return PresenceAnchor{Tag: "point", Value: value} +} + +func (value PresenceAnchor) AsPoint() (PresenceAnchorPoint, bool) { + decoded, ok := value.Value.(PresenceAnchorPoint) + return decoded, ok +} + +type PresenceEntry struct { + Client uint64 `json:"client"` + Color uint64 `json:"color"` + Generation uint64 `json:"generation"` + Highlight RequiredNullable[PresenceHighlight] `json:"-"` + Kind RequiredNullable[string] `json:"-"` + Name RequiredNullable[string] `json:"-"` + Pointer RequiredNullable[PresenceAnchor] `json:"-"` + Surface RequiredNullable[ID] `json:"-"` + UpdatedAtMs uint64 `json:"updated_at_ms"` +} + +func (value PresenceEntry) MarshalJSON() ([]byte, error) { + type wire PresenceEntry + encoded, err := json.Marshal(wire(value)) + if err != nil { + return nil, err + } + var object map[string]json.RawMessage + if err := json.Unmarshal(encoded, &object); err != nil { + return nil, err + } + if !value.Highlight.IsSet() { + return nil, fmt.Errorf("encode PresenceEntry: required nullable field highlight is missing") + } + if value.Highlight.IsNull() { + object["highlight"] = json.RawMessage("null") + } else { + fieldValue, _ := value.Highlight.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode PresenceEntry.Highlight: %w", err) + } + object["highlight"] = encodedField + } + if !value.Kind.IsSet() { + return nil, fmt.Errorf("encode PresenceEntry: required nullable field kind is missing") + } + if value.Kind.IsNull() { + object["kind"] = json.RawMessage("null") + } else { + fieldValue, _ := value.Kind.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode PresenceEntry.Kind: %w", err) + } + object["kind"] = encodedField + } + if !value.Name.IsSet() { + return nil, fmt.Errorf("encode PresenceEntry: required nullable field name is missing") + } + if value.Name.IsNull() { + object["name"] = json.RawMessage("null") + } else { + fieldValue, _ := value.Name.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode PresenceEntry.Name: %w", err) + } + object["name"] = encodedField + } + if !value.Pointer.IsSet() { + return nil, fmt.Errorf("encode PresenceEntry: required nullable field pointer is missing") + } + if value.Pointer.IsNull() { + object["pointer"] = json.RawMessage("null") + } else { + fieldValue, _ := value.Pointer.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode PresenceEntry.Pointer: %w", err) + } + object["pointer"] = encodedField + } + if !value.Surface.IsSet() { + return nil, fmt.Errorf("encode PresenceEntry: required nullable field surface is missing") + } + if value.Surface.IsNull() { + object["surface"] = json.RawMessage("null") + } else { + fieldValue, _ := value.Surface.Get() + encodedField, err := json.Marshal(fieldValue) + if err != nil { + return nil, fmt.Errorf("encode PresenceEntry.Surface: %w", err) + } + object["surface"] = encodedField + } + return json.Marshal(object) +} + +func (value *PresenceEntry) UnmarshalJSON(data []byte) error { + if !isJSONObject(data) { + return fmt.Errorf("decode PresenceEntry: expected object") + } + var fields struct { + Client *uint64 `json:"client"` + Color *uint64 `json:"color"` + Generation *uint64 `json:"generation"` + Highlight RequiredNullable[PresenceHighlight] `json:"highlight"` + Kind RequiredNullable[string] `json:"kind"` + Name RequiredNullable[string] `json:"name"` + Pointer RequiredNullable[PresenceAnchor] `json:"pointer"` + Surface RequiredNullable[ID] `json:"surface"` + UpdatedAtMs *uint64 `json:"updated_at_ms"` + } + if err := json.Unmarshal(data, &fields); err != nil { + return fmt.Errorf("decode PresenceEntry: %w", err) + } + type wire PresenceEntry + var decoded wire + if fields.Client == nil { + return fmt.Errorf("decode PresenceEntry: required field client is missing or null") + } + decoded.Client = *fields.Client + if fields.Color == nil { + return fmt.Errorf("decode PresenceEntry: required field color is missing or null") + } + decoded.Color = *fields.Color + if fields.Generation == nil { + return fmt.Errorf("decode PresenceEntry: required field generation is missing or null") + } + decoded.Generation = *fields.Generation + if !fields.Highlight.IsSet() { + return fmt.Errorf("decode PresenceEntry: required field highlight is missing") + } + decoded.Highlight = fields.Highlight + if !fields.Kind.IsSet() { + return fmt.Errorf("decode PresenceEntry: required field kind is missing") + } + decoded.Kind = fields.Kind + if !fields.Name.IsSet() { + return fmt.Errorf("decode PresenceEntry: required field name is missing") + } + decoded.Name = fields.Name + if !fields.Pointer.IsSet() { + return fmt.Errorf("decode PresenceEntry: required field pointer is missing") + } + decoded.Pointer = fields.Pointer + if !fields.Surface.IsSet() { + return fmt.Errorf("decode PresenceEntry: required field surface is missing") + } + decoded.Surface = fields.Surface + if fields.UpdatedAtMs == nil { + return fmt.Errorf("decode PresenceEntry: required field updated_at_ms is missing or null") + } + decoded.UpdatedAtMs = *fields.UpdatedAtMs + *value = PresenceEntry(decoded) + return nil +} + +type PresenceHighlight struct { + End PresenceAnchor `json:"end"` + Mode PresenceHighlightMode `json:"mode"` + Start PresenceAnchor `json:"start"` +} + +func (value *PresenceHighlight) UnmarshalJSON(data []byte) error { + if !isJSONObject(data) { + return fmt.Errorf("decode PresenceHighlight: expected object") + } + var fields struct { + End *PresenceAnchor `json:"end"` + Mode *PresenceHighlightMode `json:"mode"` + Start *PresenceAnchor `json:"start"` + } + if err := json.Unmarshal(data, &fields); err != nil { + return fmt.Errorf("decode PresenceHighlight: %w", err) + } + type wire PresenceHighlight + var decoded wire + if fields.End == nil { + return fmt.Errorf("decode PresenceHighlight: required field end is missing or null") + } + decoded.End = *fields.End + if fields.Mode == nil { + return fmt.Errorf("decode PresenceHighlight: required field mode is missing or null") + } + decoded.Mode = *fields.Mode + if fields.Start == nil { + return fmt.Errorf("decode PresenceHighlight: required field start is missing or null") + } + decoded.Start = *fields.Start + *value = PresenceHighlight(decoded) + return nil +} + +type PresenceHighlightMode string + +const ( + PresenceHighlightModeLaser PresenceHighlightMode = "laser" + PresenceHighlightModePin PresenceHighlightMode = "pin" +) + +func (value PresenceHighlightMode) valid() bool { + switch value { + case PresenceHighlightModeLaser, PresenceHighlightModePin: + return true + default: + return false + } +} + +func (value PresenceHighlightMode) MarshalJSON() ([]byte, error) { + if !value.valid() { + return nil, fmt.Errorf("%s has invalid value %v", "PresenceHighlightMode", value) + } + return json.Marshal(string(value)) +} + +func (value *PresenceHighlightMode) UnmarshalJSON(data []byte) error { + var decoded string + if err := json.Unmarshal(data, &decoded); err != nil { + return err + } + candidate := PresenceHighlightMode(decoded) + if !candidate.valid() { + return fmt.Errorf("%s has invalid value %v", "PresenceHighlightMode", decoded) + } + *value = candidate + return nil +} + +type PresenceListResult struct { + Entries []PresenceEntry `json:"entries"` +} + +func (value *PresenceListResult) UnmarshalJSON(data []byte) error { + if !isJSONObject(data) { + return fmt.Errorf("decode PresenceListResult: expected object") + } + var fields struct { + Entries *[]PresenceEntry `json:"entries"` + } + if err := json.Unmarshal(data, &fields); err != nil { + return fmt.Errorf("decode PresenceListResult: %w", err) + } + type wire PresenceListResult + var decoded wire + if fields.Entries == nil { + return fmt.Errorf("decode PresenceListResult: required field entries is missing or null") + } + decoded.Entries = *fields.Entries + *value = PresenceListResult(decoded) + return nil +} + type ProcessInfoResult struct { Command RequiredNullable[string] `json:"-"` Cwd RequiredNullable[string] `json:"-"` diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/.cmux-sdk-manifest.json b/cmux-tui/bindings/java/src/com/cmux/raw/.cmux-sdk-manifest.json index 8344d88770ce..ca85f6c5815b 100644 --- a/cmux-tui/bindings/java/src/com/cmux/raw/.cmux-sdk-manifest.json +++ b/cmux-tui/bindings/java/src/com/cmux/raw/.cmux-sdk-manifest.json @@ -317,8 +317,8 @@ }, { "path": "Commands.java", - "sha256": "16f2a3bf258e3fa5f6b0c7362bc4794f33e0393aaba868e99c1f58cbaae676ef", - "size": 25709 + "sha256": "6eb6389edc90af16d78db717b9996cbcaa77e3b670a84d5d0ee00c7f27b5774a", + "size": 26460 }, { "path": "ConfigReloadRequestedEvent.java", @@ -432,8 +432,8 @@ }, { "path": "Events.java", - "sha256": "1712b8e31182f51af62764c98d007c9c032c28ef43c7ea890818090ee3c5ac9d", - "size": 9451 + "sha256": "2ed0ac9b15ba2e5f330aff70299fded66c029b98b1fa0a475d03adc0ef38e073", + "size": 9652 }, { "path": "ExportLayoutRequest.java", @@ -507,8 +507,8 @@ }, { "path": "GeneratedCmuxClient.java", - "sha256": "24d6586816090aaa943d5449e38bea85f104cda74334c47daf3c41bce063ff74", - "size": 24808 + "sha256": "a3a5a5514e3d0a78dc1719b640ae1690b930ea31b9b80b3bed4b2355d64cacf5", + "size": 25423 }, { "path": "GetBrowserProviderRequest.java", @@ -860,6 +860,61 @@ "sha256": "33db8915404de53a8f81edd37b024d9bf360b25f30ebb75d48960ca506819ce0", "size": 4254 }, + { + "path": "PresenceAnchor.java", + "sha256": "1873202176e7e1627aed44369581c90181b763158a1df12cb349b8a9ca9f1306", + "size": 650 + }, + { + "path": "PresenceAnchorCell.java", + "sha256": "10f6760b242d666dbdb2e100715bbfae4c469f80bcf74060a78abc1c1731ed77", + "size": 3366 + }, + { + "path": "PresenceAnchorPoint.java", + "sha256": "f42de603c3456bfcbf145941a9ea45a30b702e2efc5c9961ad423829177d3bec", + "size": 2598 + }, + { + "path": "PresenceChangedEvent.java", + "sha256": "1fbd1baea9607a14d7ce5d8aada5023e3cc6dac86d268b5214fb82cf18f57bf7", + "size": 7991 + }, + { + "path": "PresenceClearRequest.java", + "sha256": "c0219e51d0a8ce2064ec581e5cbd4ce30a8b4c02662702b50b52e7fec94bca22", + "size": 1368 + }, + { + "path": "PresenceEntry.java", + "sha256": "600da6382771a26aefcf6075ca4f148fbff7a9ce1a2d1c556644284cc5f45d37", + "size": 7531 + }, + { + "path": "PresenceHighlight.java", + "sha256": "44f3a1911c70d1f41c03d67d030c52658b2d1093f7c3aa226025c6f34318ad28", + "size": 3229 + }, + { + "path": "PresenceHighlightMode.java", + "sha256": "9475b182b2cb0923cff8fe3b879286f1c4f877345215e526a7ffa8b0e4956ba5", + "size": 921 + }, + { + "path": "PresenceListRequest.java", + "sha256": "280a9363b9b2dd822eecaf831072062e712be164267fe171f8cac20e9712d55b", + "size": 1359 + }, + { + "path": "PresenceListResult.java", + "sha256": "38617f56b284c9ed952a3b628b60d30c1645c2bfa07ecc283ab09845babe5caa", + "size": 2066 + }, + { + "path": "PresenceUpdateRequest.java", + "sha256": "5502d091e9875629e4a0361e849936f4fdbe82baad9b063ac012430087cab569", + "size": 3448 + }, { "path": "ProcessInfoRequest.java", "sha256": "d16de4af06635fb24ddfcddd1248fa0ef1e30b2592cac5a4177835f92abbc659", @@ -872,8 +927,8 @@ }, { "path": "Protocol.java", - "sha256": "0de6592852bf247c49a8b0011b8dd966e9c91d8315ad92d9bdd8a1dc806098e2", - "size": 4199 + "sha256": "fca80901bbcf7d6f1012ea524cd023c4ec1831d1a681f7600c8dc1b7667fdffd", + "size": 4276 }, { "path": "ProtocolEvent.java", @@ -1287,8 +1342,8 @@ }, { "path": "SubscribeRequest.java", - "sha256": "3d2bf4e413e0230cacbd33933da70f55f9d418cccb0b048a46318c580d52fea3", - "size": 2752 + "sha256": "7f6f1834f94620a15ea1c627fe34b05e2a99305ed7a030f792bb23a43ed4327e", + "size": 3505 }, { "path": "SubscribeRequestTreeEvents.java", @@ -1557,7 +1612,7 @@ } ], "format": 1, - "ir_sha256": "8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86", + "ir_sha256": "e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663", "language": "java", "mux_protocol": 12, "schema_version": 2 diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/Commands.java b/cmux-tui/bindings/java/src/com/cmux/raw/Commands.java index b3f15092ac08..b1f706ce4172 100644 --- a/cmux-tui/bindings/java/src/com/cmux/raw/Commands.java +++ b/cmux-tui/bindings/java/src/com/cmux/raw/Commands.java @@ -71,6 +71,9 @@ private Commands() {} public static final CommandMetadata PAIRING_RESPONSE = new CommandMetadata("pairing-response", Authority.LOCAL_ADMIN, 7, null, StreamKind.NONE, Map.of(), Map.of()); public static final CommandMetadata PANE_NEIGHBOR = new CommandMetadata("pane-neighbor", Authority.CONTROL, 6, null, StreamKind.NONE, Map.of(), Map.of()); public static final CommandMetadata PING = new CommandMetadata("ping", Authority.CONTROL, 6, null, StreamKind.NONE, Map.of(), Map.of()); + public static final CommandMetadata PRESENCE_CLEAR = new CommandMetadata("presence-clear", Authority.CONTROL, 12, "presence-v1", StreamKind.NONE, Map.of(), Map.of()); + public static final CommandMetadata PRESENCE_LIST = new CommandMetadata("presence-list", Authority.CONTROL, 12, "presence-v1", StreamKind.NONE, Map.of(), Map.of()); + public static final CommandMetadata PRESENCE_UPDATE = new CommandMetadata("presence-update", Authority.CONTROL, 12, "presence-v1", StreamKind.NONE, Map.of(), Map.of()); public static final CommandMetadata PROCESS_INFO = new CommandMetadata("process-info", Authority.CONTROL, 6, null, StreamKind.NONE, Map.of(), Map.of()); public static final CommandMetadata PUT_FRONTEND_PROJECTION = new CommandMetadata("put-frontend-projection", Authority.CONTROL, 7, null, StreamKind.NONE, Map.of(), Map.of()); public static final CommandMetadata READ_SCREEN = new CommandMetadata("read-screen", Authority.CONTROL, 5, null, StreamKind.NONE, Map.of(), Map.of()); @@ -108,7 +111,7 @@ private Commands() {} public static final CommandMetadata SHUTDOWN_DAEMON = new CommandMetadata("shutdown-daemon", Authority.LOCAL_ADMIN, 9, null, StreamKind.NONE, Map.ofEntries(Map.entry("force", 10L)), Map.ofEntries(Map.entry("force", "daemon-handoff-force-v1"))); public static final CommandMetadata SIDEBAR_PLUGIN = new CommandMetadata("sidebar-plugin", Authority.FRONTEND, 6, null, StreamKind.NONE, Map.of(), Map.of()); public static final CommandMetadata SPLIT = new CommandMetadata("split", Authority.CONTROL, 5, null, StreamKind.NONE, Map.of(), Map.of()); - public static final CommandMetadata SUBSCRIBE = new CommandMetadata("subscribe", Authority.FRONTEND, 5, null, StreamKind.SUBSCRIBE, Map.ofEntries(Map.entry("surface", 9L), Map.entry("tree_events", 7L)), Map.ofEntries(Map.entry("surface", "surface-subscribe-filter"))); + public static final CommandMetadata SUBSCRIBE = new CommandMetadata("subscribe", Authority.FRONTEND, 5, null, StreamKind.SUBSCRIBE, Map.ofEntries(Map.entry("presence_only", 12L), Map.entry("surface", 9L), Map.entry("tree_events", 7L)), Map.ofEntries(Map.entry("presence_only", "presence-v1"), Map.entry("surface", "surface-subscribe-filter"))); public static final CommandMetadata SWAP_PANE = new CommandMetadata("swap-pane", Authority.CONTROL, 6, null, StreamKind.NONE, Map.of(), Map.of()); public static final CommandMetadata TERMINAL_EVENTS = new CommandMetadata("terminal-events", Authority.CONTROL, 9, null, StreamKind.NONE, Map.of(), Map.of()); public static final CommandMetadata UNDO_LAYOUT = new CommandMetadata("undo-layout", Authority.CONTROL, 9, "layout-undo-v1", StreamKind.NONE, Map.of(), Map.of()); @@ -181,6 +184,9 @@ private Commands() {} values.put("pairing-response", PAIRING_RESPONSE); values.put("pane-neighbor", PANE_NEIGHBOR); values.put("ping", PING); + values.put("presence-clear", PRESENCE_CLEAR); + values.put("presence-list", PRESENCE_LIST); + values.put("presence-update", PRESENCE_UPDATE); values.put("process-info", PROCESS_INFO); values.put("put-frontend-projection", PUT_FRONTEND_PROJECTION); values.put("read-screen", READ_SCREEN); diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/Events.java b/cmux-tui/bindings/java/src/com/cmux/raw/Events.java index 5aae5f5f3165..cf4c95a6c1e3 100644 --- a/cmux-tui/bindings/java/src/com/cmux/raw/Events.java +++ b/cmux-tui/bindings/java/src/com/cmux/raw/Events.java @@ -35,6 +35,7 @@ private Events() {} public static final EventMetadata PAIRING_RESOLVED = new EventMetadata("pairing-resolved", 7, null, List.of("subscribe"), true); public static final EventMetadata PANE_ADDED = new EventMetadata("pane-added", 7, null, List.of("subscribe-deltas"), true); public static final EventMetadata PANE_CLOSED = new EventMetadata("pane-closed", 7, null, List.of("subscribe-deltas"), true); + public static final EventMetadata PRESENCE_CHANGED = new EventMetadata("presence-changed", 12, "presence-v1", List.of("subscribe"), true); public static final EventMetadata RENDER_DELTA = new EventMetadata("render-delta", 7, null, List.of("attach-render"), true); public static final EventMetadata RENDER_STATE = new EventMetadata("render-state", 7, null, List.of("attach-render"), true); public static final EventMetadata RESIZED = new EventMetadata("resized", 6, null, List.of("attach-byte"), true); @@ -87,6 +88,7 @@ private Events() {} values.put("pairing-resolved", PAIRING_RESOLVED); values.put("pane-added", PANE_ADDED); values.put("pane-closed", PANE_CLOSED); + values.put("presence-changed", PRESENCE_CHANGED); values.put("render-delta", RENDER_DELTA); values.put("render-state", RENDER_STATE); values.put("resized", RESIZED); diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/GeneratedCmuxClient.java b/cmux-tui/bindings/java/src/com/cmux/raw/GeneratedCmuxClient.java index 327788c0423b..f118fd8cb7a3 100644 --- a/cmux-tui/bindings/java/src/com/cmux/raw/GeneratedCmuxClient.java +++ b/cmux-tui/bindings/java/src/com/cmux/raw/GeneratedCmuxClient.java @@ -318,6 +318,21 @@ public final PingResult ping() throws CmuxException { return PingResult.fromWire(result); } + public final EmptyResult presenceClear() throws CmuxException { + Object result = execute(Commands.PRESENCE_CLEAR, Map.of()); + return EmptyResult.fromWire(result); + } + + public final PresenceListResult presenceList() throws CmuxException { + Object result = execute(Commands.PRESENCE_LIST, Map.of()); + return PresenceListResult.fromWire(result); + } + + public final EmptyResult presenceUpdate(PresenceUpdateRequest request) throws CmuxException { + Object result = execute(Commands.PRESENCE_UPDATE, request.toWire()); + return EmptyResult.fromWire(result); + } + public final ProcessInfoResult processInfo(ProcessInfoRequest request) throws CmuxException { Object result = execute(Commands.PROCESS_INFO, request.toWire()); return ProcessInfoResult.fromWire(result); diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/PresenceAnchor.java b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceAnchor.java new file mode 100644 index 000000000000..d5dfd4350c60 --- /dev/null +++ b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceAnchor.java @@ -0,0 +1,18 @@ +// Generated from cmux-tui/spec/sdk-schema.json. DO NOT EDIT. +package com.cmux.raw; + + +import java.util.Map; + + +public interface PresenceAnchor extends WireValue { + static PresenceAnchor fromWire(Object value) { + Map object = Wire.object(value, "PresenceAnchor"); + String tag = Wire.string(Wire.required(object, "kind"), "PresenceAnchor.kind"); + return switch (tag) { + case "cell" -> PresenceAnchorCell.fromWire(value); + case "point" -> PresenceAnchorPoint.fromWire(value); + default -> throw new CmuxDecodeException("unknown PresenceAnchor tag " + tag, null); + }; + } +} diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/PresenceAnchorCell.java b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceAnchorCell.java new file mode 100644 index 000000000000..a0ea7a73e9bd --- /dev/null +++ b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceAnchorCell.java @@ -0,0 +1,94 @@ +// Generated from cmux-tui/spec/sdk-schema.json. DO NOT EDIT. +package com.cmux.raw; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +public final class PresenceAnchorCell implements WireValue, PresenceAnchor { + private final long col; + private final long row; + private final Field scrollOffset; + + private PresenceAnchorCell(Builder builder) { + if (!builder.colSet) throw new IllegalArgumentException("col is required"); + this.col = builder.col; + if (!builder.rowSet) throw new IllegalArgumentException("row is required"); + this.row = builder.row; + this.scrollOffset = builder.scrollOffset; + } + + public static Builder builder() { return new Builder(); } + + public long col() { return col; } + public String kind() { return "cell"; } + public long row() { return row; } + public Field scrollOffset() { return scrollOffset; } + + public static PresenceAnchorCell fromWire(Object value) { + Map object = Wire.object(value, "PresenceAnchorCell"); + Builder builder = builder(); + Object rawCol = Wire.required(object, "col"); + builder.col(Wire.uint32(rawCol, "PresenceAnchorCell.col")); + Object rawKind = Wire.required(object, "kind"); + ProtocolSupport.literal(rawKind, "cell", "PresenceAnchorCell.kind"); + Object rawRow = Wire.required(object, "row"); + builder.row(Wire.uint32(rawRow, "PresenceAnchorCell.row")); + Object rawScrollOffset = Wire.optional(object, "scroll_offset"); + if (!Wire.isMissing(rawScrollOffset)) { + builder.scrollOffset(Wire.uint64(rawScrollOffset, "PresenceAnchorCell.scroll_offset")); + } + return builder.build(); + } + + @Override + public Map toWire() { + LinkedHashMap object = new LinkedHashMap<>(); + Wire.put(object, "col", col); + Wire.put(object, "kind", "cell"); + Wire.put(object, "row", row); + Wire.put(object, "scroll_offset", scrollOffset); + return Collections.unmodifiableMap(object); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof PresenceAnchorCell that)) return false; + return Objects.equals(col, that.col) && Objects.equals(row, that.row) && Objects.equals(scrollOffset, that.scrollOffset); + } + + @Override + public int hashCode() { return Objects.hash(col, row, scrollOffset); } + + @Override + public String toString() { return "PresenceAnchorCell" + toWire(); } + + public static final class Builder { + private Long col; + private boolean colSet; + private Long row; + private boolean rowSet; + private Field scrollOffset = Field.omitted(); + + public Builder col(long value) { + this.col = value; + this.colSet = true; + return this; + } + public Builder row(long value) { + this.row = value; + this.rowSet = true; + return this; + } + public Builder scrollOffset(UInt64 value) { + this.scrollOffset = Field.of(value); + return this; + } + public PresenceAnchorCell build() { return new PresenceAnchorCell(this); } + } +} diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/PresenceAnchorPoint.java b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceAnchorPoint.java new file mode 100644 index 000000000000..15df0ffd58b0 --- /dev/null +++ b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceAnchorPoint.java @@ -0,0 +1,81 @@ +// Generated from cmux-tui/spec/sdk-schema.json. DO NOT EDIT. +package com.cmux.raw; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +public final class PresenceAnchorPoint implements WireValue, PresenceAnchor { + private final double x; + private final double y; + + private PresenceAnchorPoint(Builder builder) { + if (!builder.xSet) throw new IllegalArgumentException("x is required"); + this.x = builder.x; + if (!builder.ySet) throw new IllegalArgumentException("y is required"); + this.y = builder.y; + } + + public static Builder builder() { return new Builder(); } + + public String kind() { return "point"; } + public double x() { return x; } + public double y() { return y; } + + public static PresenceAnchorPoint fromWire(Object value) { + Map object = Wire.object(value, "PresenceAnchorPoint"); + Builder builder = builder(); + Object rawKind = Wire.required(object, "kind"); + ProtocolSupport.literal(rawKind, "point", "PresenceAnchorPoint.kind"); + Object rawX = Wire.required(object, "x"); + builder.x(Wire.float64(rawX, "PresenceAnchorPoint.x")); + Object rawY = Wire.required(object, "y"); + builder.y(Wire.float64(rawY, "PresenceAnchorPoint.y")); + return builder.build(); + } + + @Override + public Map toWire() { + LinkedHashMap object = new LinkedHashMap<>(); + Wire.put(object, "kind", "point"); + Wire.put(object, "x", x); + Wire.put(object, "y", y); + return Collections.unmodifiableMap(object); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof PresenceAnchorPoint that)) return false; + return Objects.equals(x, that.x) && Objects.equals(y, that.y); + } + + @Override + public int hashCode() { return Objects.hash(x, y); } + + @Override + public String toString() { return "PresenceAnchorPoint" + toWire(); } + + public static final class Builder { + private Double x; + private boolean xSet; + private Double y; + private boolean ySet; + + public Builder x(double value) { + this.x = value; + this.xSet = true; + return this; + } + public Builder y(double value) { + this.y = value; + this.ySet = true; + return this; + } + public PresenceAnchorPoint build() { return new PresenceAnchorPoint(this); } + } +} diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/PresenceChangedEvent.java b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceChangedEvent.java new file mode 100644 index 000000000000..3c18a1f0a01f --- /dev/null +++ b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceChangedEvent.java @@ -0,0 +1,179 @@ +// Generated from cmux-tui/spec/sdk-schema.json. DO NOT EDIT. +package com.cmux.raw; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +/** Immutable presence-changed event. Protocol v12; streams: subscribe. */ +public final class PresenceChangedEvent implements WireValue, DeltaStreamEvent, ProtocolEvent, SubscribeEvent { + private final UInt64 client; + private final UInt64 color; + private final UInt64 generation; + private final PresenceHighlight highlight; + private final String kind; + private final String name; + private final PresenceAnchor pointer; + private final UInt64 surface; + private final UInt64 updatedAtMs; + + private PresenceChangedEvent(Builder builder) { + if (!builder.clientSet) throw new IllegalArgumentException("client is required"); + this.client = Wire.nonNull(builder.client, "client"); + if (!builder.colorSet) throw new IllegalArgumentException("color is required"); + this.color = Wire.nonNull(builder.color, "color"); + if (!builder.generationSet) throw new IllegalArgumentException("generation is required"); + this.generation = Wire.nonNull(builder.generation, "generation"); + if (!builder.highlightSet) throw new IllegalArgumentException("highlight is required"); + this.highlight = builder.highlight; + if (!builder.kindSet) throw new IllegalArgumentException("kind is required"); + this.kind = builder.kind; + if (!builder.nameSet) throw new IllegalArgumentException("name is required"); + this.name = builder.name; + if (!builder.pointerSet) throw new IllegalArgumentException("pointer is required"); + this.pointer = builder.pointer; + if (!builder.surfaceSet) throw new IllegalArgumentException("surface is required"); + this.surface = builder.surface; + if (!builder.updatedAtMsSet) throw new IllegalArgumentException("updated_at_ms is required"); + this.updatedAtMs = Wire.nonNull(builder.updatedAtMs, "updated_at_ms"); + } + + public static Builder builder() { return new Builder(); } + + public UInt64 client() { return client; } + public UInt64 color() { return color; } + public UInt64 generation() { return generation; } + public PresenceHighlight highlight() { return highlight; } + public String kind() { return kind; } + public String name() { return name; } + public PresenceAnchor pointer() { return pointer; } + public UInt64 surface() { return surface; } + public UInt64 updatedAtMs() { return updatedAtMs; } + @Override public String event() { return "presence-changed"; } + + public static PresenceChangedEvent fromWire(Object value) { + Map object = Wire.object(value, "PresenceChangedEvent"); + Builder builder = builder(); + ProtocolSupport.literal(Wire.required(object, "event"), "presence-changed", "PresenceChangedEvent.event"); + Object rawClient = Wire.required(object, "client"); + builder.client(Wire.uint64(rawClient, "PresenceChangedEvent.client")); + Object rawColor = Wire.required(object, "color"); + builder.color(Wire.uint64(rawColor, "PresenceChangedEvent.color")); + Object rawGeneration = Wire.required(object, "generation"); + builder.generation(Wire.uint64(rawGeneration, "PresenceChangedEvent.generation")); + Object rawHighlight = Wire.required(object, "highlight"); + builder.highlight(rawHighlight == null ? null : PresenceHighlight.fromWire(rawHighlight)); + Object rawKind = Wire.required(object, "kind"); + builder.kind(rawKind == null ? null : Wire.string(rawKind, "PresenceChangedEvent.kind")); + Object rawName = Wire.required(object, "name"); + builder.name(rawName == null ? null : Wire.string(rawName, "PresenceChangedEvent.name")); + Object rawPointer = Wire.required(object, "pointer"); + builder.pointer(rawPointer == null ? null : PresenceAnchor.fromWire(rawPointer)); + Object rawSurface = Wire.required(object, "surface"); + builder.surface(rawSurface == null ? null : Wire.uint64(rawSurface, "PresenceChangedEvent.surface")); + Object rawUpdatedAtMs = Wire.required(object, "updated_at_ms"); + builder.updatedAtMs(Wire.uint64(rawUpdatedAtMs, "PresenceChangedEvent.updated_at_ms")); + return builder.build(); + } + + @Override + public Map toWire() { + LinkedHashMap object = new LinkedHashMap<>(); + object.put("event", "presence-changed"); + Wire.put(object, "client", client); + Wire.put(object, "color", color); + Wire.put(object, "generation", generation); + Wire.put(object, "highlight", highlight); + Wire.put(object, "kind", kind); + Wire.put(object, "name", name); + Wire.put(object, "pointer", pointer); + Wire.put(object, "surface", surface); + Wire.put(object, "updated_at_ms", updatedAtMs); + return Collections.unmodifiableMap(object); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof PresenceChangedEvent that)) return false; + return Objects.equals(client, that.client) && Objects.equals(color, that.color) && Objects.equals(generation, that.generation) && Objects.equals(highlight, that.highlight) && Objects.equals(kind, that.kind) && Objects.equals(name, that.name) && Objects.equals(pointer, that.pointer) && Objects.equals(surface, that.surface) && Objects.equals(updatedAtMs, that.updatedAtMs); + } + + @Override + public int hashCode() { return Objects.hash(client, color, generation, highlight, kind, name, pointer, surface, updatedAtMs); } + + @Override + public String toString() { return "PresenceChangedEvent" + toWire(); } + + public static final class Builder { + private UInt64 client; + private boolean clientSet; + private UInt64 color; + private boolean colorSet; + private UInt64 generation; + private boolean generationSet; + private PresenceHighlight highlight; + private boolean highlightSet; + private String kind; + private boolean kindSet; + private String name; + private boolean nameSet; + private PresenceAnchor pointer; + private boolean pointerSet; + private UInt64 surface; + private boolean surfaceSet; + private UInt64 updatedAtMs; + private boolean updatedAtMsSet; + + public Builder client(UInt64 value) { + this.client = value; + this.clientSet = true; + return this; + } + public Builder color(UInt64 value) { + this.color = value; + this.colorSet = true; + return this; + } + public Builder generation(UInt64 value) { + this.generation = value; + this.generationSet = true; + return this; + } + public Builder highlight(PresenceHighlight value) { + this.highlight = value; + this.highlightSet = true; + return this; + } + public Builder kind(String value) { + this.kind = value; + this.kindSet = true; + return this; + } + public Builder name(String value) { + this.name = value; + this.nameSet = true; + return this; + } + public Builder pointer(PresenceAnchor value) { + this.pointer = value; + this.pointerSet = true; + return this; + } + public Builder surface(UInt64 value) { + this.surface = value; + this.surfaceSet = true; + return this; + } + public Builder updatedAtMs(UInt64 value) { + this.updatedAtMs = value; + this.updatedAtMsSet = true; + return this; + } + public PresenceChangedEvent build() { return new PresenceChangedEvent(this); } + } +} diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/PresenceClearRequest.java b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceClearRequest.java new file mode 100644 index 000000000000..5a698c235583 --- /dev/null +++ b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceClearRequest.java @@ -0,0 +1,50 @@ +// Generated from cmux-tui/spec/sdk-schema.json. DO NOT EDIT. +package com.cmux.raw; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +/** Immutable presence-clear request. Protocol v12; authority: control. */ +public final class PresenceClearRequest implements WireValue { + + private PresenceClearRequest(Builder builder) { + } + + public static Builder builder() { return new Builder(); } + + + public static PresenceClearRequest fromWire(Object value) { + Map object = Wire.object(value, "PresenceClearRequest"); + Builder builder = builder(); + return builder.build(); + } + + @Override + public Map toWire() { + LinkedHashMap object = new LinkedHashMap<>(); + return Collections.unmodifiableMap(object); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof PresenceClearRequest that)) return false; + return true; + } + + @Override + public int hashCode() { return Objects.hash(); } + + @Override + public String toString() { return "PresenceClearRequest" + toWire(); } + + public static final class Builder { + + public PresenceClearRequest build() { return new PresenceClearRequest(this); } + } +} diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/PresenceEntry.java b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceEntry.java new file mode 100644 index 000000000000..8e64b5355daf --- /dev/null +++ b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceEntry.java @@ -0,0 +1,175 @@ +// Generated from cmux-tui/spec/sdk-schema.json. DO NOT EDIT. +package com.cmux.raw; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +public final class PresenceEntry implements WireValue { + private final UInt64 client; + private final UInt64 color; + private final UInt64 generation; + private final PresenceHighlight highlight; + private final String kind; + private final String name; + private final PresenceAnchor pointer; + private final UInt64 surface; + private final UInt64 updatedAtMs; + + private PresenceEntry(Builder builder) { + if (!builder.clientSet) throw new IllegalArgumentException("client is required"); + this.client = Wire.nonNull(builder.client, "client"); + if (!builder.colorSet) throw new IllegalArgumentException("color is required"); + this.color = Wire.nonNull(builder.color, "color"); + if (!builder.generationSet) throw new IllegalArgumentException("generation is required"); + this.generation = Wire.nonNull(builder.generation, "generation"); + if (!builder.highlightSet) throw new IllegalArgumentException("highlight is required"); + this.highlight = builder.highlight; + if (!builder.kindSet) throw new IllegalArgumentException("kind is required"); + this.kind = builder.kind; + if (!builder.nameSet) throw new IllegalArgumentException("name is required"); + this.name = builder.name; + if (!builder.pointerSet) throw new IllegalArgumentException("pointer is required"); + this.pointer = builder.pointer; + if (!builder.surfaceSet) throw new IllegalArgumentException("surface is required"); + this.surface = builder.surface; + if (!builder.updatedAtMsSet) throw new IllegalArgumentException("updated_at_ms is required"); + this.updatedAtMs = Wire.nonNull(builder.updatedAtMs, "updated_at_ms"); + } + + public static Builder builder() { return new Builder(); } + + public UInt64 client() { return client; } + public UInt64 color() { return color; } + public UInt64 generation() { return generation; } + public PresenceHighlight highlight() { return highlight; } + public String kind() { return kind; } + public String name() { return name; } + public PresenceAnchor pointer() { return pointer; } + public UInt64 surface() { return surface; } + public UInt64 updatedAtMs() { return updatedAtMs; } + + public static PresenceEntry fromWire(Object value) { + Map object = Wire.object(value, "PresenceEntry"); + Builder builder = builder(); + Object rawClient = Wire.required(object, "client"); + builder.client(Wire.uint64(rawClient, "PresenceEntry.client")); + Object rawColor = Wire.required(object, "color"); + builder.color(Wire.uint64(rawColor, "PresenceEntry.color")); + Object rawGeneration = Wire.required(object, "generation"); + builder.generation(Wire.uint64(rawGeneration, "PresenceEntry.generation")); + Object rawHighlight = Wire.required(object, "highlight"); + builder.highlight(rawHighlight == null ? null : PresenceHighlight.fromWire(rawHighlight)); + Object rawKind = Wire.required(object, "kind"); + builder.kind(rawKind == null ? null : Wire.string(rawKind, "PresenceEntry.kind")); + Object rawName = Wire.required(object, "name"); + builder.name(rawName == null ? null : Wire.string(rawName, "PresenceEntry.name")); + Object rawPointer = Wire.required(object, "pointer"); + builder.pointer(rawPointer == null ? null : PresenceAnchor.fromWire(rawPointer)); + Object rawSurface = Wire.required(object, "surface"); + builder.surface(rawSurface == null ? null : Wire.uint64(rawSurface, "PresenceEntry.surface")); + Object rawUpdatedAtMs = Wire.required(object, "updated_at_ms"); + builder.updatedAtMs(Wire.uint64(rawUpdatedAtMs, "PresenceEntry.updated_at_ms")); + return builder.build(); + } + + @Override + public Map toWire() { + LinkedHashMap object = new LinkedHashMap<>(); + Wire.put(object, "client", client); + Wire.put(object, "color", color); + Wire.put(object, "generation", generation); + Wire.put(object, "highlight", highlight); + Wire.put(object, "kind", kind); + Wire.put(object, "name", name); + Wire.put(object, "pointer", pointer); + Wire.put(object, "surface", surface); + Wire.put(object, "updated_at_ms", updatedAtMs); + return Collections.unmodifiableMap(object); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof PresenceEntry that)) return false; + return Objects.equals(client, that.client) && Objects.equals(color, that.color) && Objects.equals(generation, that.generation) && Objects.equals(highlight, that.highlight) && Objects.equals(kind, that.kind) && Objects.equals(name, that.name) && Objects.equals(pointer, that.pointer) && Objects.equals(surface, that.surface) && Objects.equals(updatedAtMs, that.updatedAtMs); + } + + @Override + public int hashCode() { return Objects.hash(client, color, generation, highlight, kind, name, pointer, surface, updatedAtMs); } + + @Override + public String toString() { return "PresenceEntry" + toWire(); } + + public static final class Builder { + private UInt64 client; + private boolean clientSet; + private UInt64 color; + private boolean colorSet; + private UInt64 generation; + private boolean generationSet; + private PresenceHighlight highlight; + private boolean highlightSet; + private String kind; + private boolean kindSet; + private String name; + private boolean nameSet; + private PresenceAnchor pointer; + private boolean pointerSet; + private UInt64 surface; + private boolean surfaceSet; + private UInt64 updatedAtMs; + private boolean updatedAtMsSet; + + public Builder client(UInt64 value) { + this.client = value; + this.clientSet = true; + return this; + } + public Builder color(UInt64 value) { + this.color = value; + this.colorSet = true; + return this; + } + public Builder generation(UInt64 value) { + this.generation = value; + this.generationSet = true; + return this; + } + public Builder highlight(PresenceHighlight value) { + this.highlight = value; + this.highlightSet = true; + return this; + } + public Builder kind(String value) { + this.kind = value; + this.kindSet = true; + return this; + } + public Builder name(String value) { + this.name = value; + this.nameSet = true; + return this; + } + public Builder pointer(PresenceAnchor value) { + this.pointer = value; + this.pointerSet = true; + return this; + } + public Builder surface(UInt64 value) { + this.surface = value; + this.surfaceSet = true; + return this; + } + public Builder updatedAtMs(UInt64 value) { + this.updatedAtMs = value; + this.updatedAtMsSet = true; + return this; + } + public PresenceEntry build() { return new PresenceEntry(this); } + } +} diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/PresenceHighlight.java b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceHighlight.java new file mode 100644 index 000000000000..287203020753 --- /dev/null +++ b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceHighlight.java @@ -0,0 +1,91 @@ +// Generated from cmux-tui/spec/sdk-schema.json. DO NOT EDIT. +package com.cmux.raw; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +public final class PresenceHighlight implements WireValue { + private final PresenceAnchor end; + private final PresenceHighlightMode mode; + private final PresenceAnchor start; + + private PresenceHighlight(Builder builder) { + if (!builder.endSet) throw new IllegalArgumentException("end is required"); + this.end = Wire.nonNull(builder.end, "end"); + if (!builder.modeSet) throw new IllegalArgumentException("mode is required"); + this.mode = Wire.nonNull(builder.mode, "mode"); + if (!builder.startSet) throw new IllegalArgumentException("start is required"); + this.start = Wire.nonNull(builder.start, "start"); + } + + public static Builder builder() { return new Builder(); } + + public PresenceAnchor end() { return end; } + public PresenceHighlightMode mode() { return mode; } + public PresenceAnchor start() { return start; } + + public static PresenceHighlight fromWire(Object value) { + Map object = Wire.object(value, "PresenceHighlight"); + Builder builder = builder(); + Object rawEnd = Wire.required(object, "end"); + builder.end(PresenceAnchor.fromWire(rawEnd)); + Object rawMode = Wire.required(object, "mode"); + builder.mode(PresenceHighlightMode.fromWire(rawMode)); + Object rawStart = Wire.required(object, "start"); + builder.start(PresenceAnchor.fromWire(rawStart)); + return builder.build(); + } + + @Override + public Map toWire() { + LinkedHashMap object = new LinkedHashMap<>(); + Wire.put(object, "end", end); + Wire.put(object, "mode", mode); + Wire.put(object, "start", start); + return Collections.unmodifiableMap(object); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof PresenceHighlight that)) return false; + return Objects.equals(end, that.end) && Objects.equals(mode, that.mode) && Objects.equals(start, that.start); + } + + @Override + public int hashCode() { return Objects.hash(end, mode, start); } + + @Override + public String toString() { return "PresenceHighlight" + toWire(); } + + public static final class Builder { + private PresenceAnchor end; + private boolean endSet; + private PresenceHighlightMode mode; + private boolean modeSet; + private PresenceAnchor start; + private boolean startSet; + + public Builder end(PresenceAnchor value) { + this.end = value; + this.endSet = true; + return this; + } + public Builder mode(PresenceHighlightMode value) { + this.mode = value; + this.modeSet = true; + return this; + } + public Builder start(PresenceAnchor value) { + this.start = value; + this.startSet = true; + return this; + } + public PresenceHighlight build() { return new PresenceHighlight(this); } + } +} diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/PresenceHighlightMode.java b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceHighlightMode.java new file mode 100644 index 000000000000..bb9ba0d26627 --- /dev/null +++ b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceHighlightMode.java @@ -0,0 +1,34 @@ +// Generated from cmux-tui/spec/sdk-schema.json. DO NOT EDIT. +package com.cmux.raw; + +import java.util.Objects; + +public enum PresenceHighlightMode implements WireEnum { + LASER("laser"), + PIN("pin"); + + private final Object wireValue; + + PresenceHighlightMode(Object wireValue) { + this.wireValue = wireValue; + } + + @Override + public String wireValue() { + return String.valueOf(wireValue); + } + + public Object rawWireValue() { + return wireValue; + } + + public static PresenceHighlightMode fromWire(Object value) { + for (PresenceHighlightMode candidate : values()) { + if (Objects.equals(candidate.wireValue, value) + || Objects.equals(String.valueOf(candidate.wireValue), value)) { + return candidate; + } + } + throw new CmuxDecodeException("unknown PresenceHighlightMode value " + value, null); + } +} diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/PresenceListRequest.java b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceListRequest.java new file mode 100644 index 000000000000..8ad8a951b47b --- /dev/null +++ b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceListRequest.java @@ -0,0 +1,50 @@ +// Generated from cmux-tui/spec/sdk-schema.json. DO NOT EDIT. +package com.cmux.raw; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +/** Immutable presence-list request. Protocol v12; authority: control. */ +public final class PresenceListRequest implements WireValue { + + private PresenceListRequest(Builder builder) { + } + + public static Builder builder() { return new Builder(); } + + + public static PresenceListRequest fromWire(Object value) { + Map object = Wire.object(value, "PresenceListRequest"); + Builder builder = builder(); + return builder.build(); + } + + @Override + public Map toWire() { + LinkedHashMap object = new LinkedHashMap<>(); + return Collections.unmodifiableMap(object); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof PresenceListRequest that)) return false; + return true; + } + + @Override + public int hashCode() { return Objects.hash(); } + + @Override + public String toString() { return "PresenceListRequest" + toWire(); } + + public static final class Builder { + + public PresenceListRequest build() { return new PresenceListRequest(this); } + } +} diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/PresenceListResult.java b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceListResult.java new file mode 100644 index 000000000000..9076d4f9c0d9 --- /dev/null +++ b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceListResult.java @@ -0,0 +1,63 @@ +// Generated from cmux-tui/spec/sdk-schema.json. DO NOT EDIT. +package com.cmux.raw; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +public final class PresenceListResult implements WireValue { + private final List entries; + + private PresenceListResult(Builder builder) { + if (!builder.entriesSet) throw new IllegalArgumentException("entries is required"); + this.entries = List.copyOf(Wire.nonNull(builder.entries, "entries")); + } + + public static Builder builder() { return new Builder(); } + + public List entries() { return entries; } + + public static PresenceListResult fromWire(Object value) { + Map object = Wire.object(value, "PresenceListResult"); + Builder builder = builder(); + Object rawEntries = Wire.required(object, "entries"); + builder.entries(Wire.array(rawEntries, "PresenceListResult.entries", item -> PresenceEntry.fromWire(item))); + return builder.build(); + } + + @Override + public Map toWire() { + LinkedHashMap object = new LinkedHashMap<>(); + Wire.put(object, "entries", entries); + return Collections.unmodifiableMap(object); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof PresenceListResult that)) return false; + return Objects.equals(entries, that.entries); + } + + @Override + public int hashCode() { return Objects.hash(entries); } + + @Override + public String toString() { return "PresenceListResult" + toWire(); } + + public static final class Builder { + private List entries; + private boolean entriesSet; + + public Builder entries(List value) { + this.entries = value; + this.entriesSet = true; + return this; + } + public PresenceListResult build() { return new PresenceListResult(this); } + } +} diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/PresenceUpdateRequest.java b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceUpdateRequest.java new file mode 100644 index 000000000000..c98cbb33e1a9 --- /dev/null +++ b/cmux-tui/bindings/java/src/com/cmux/raw/PresenceUpdateRequest.java @@ -0,0 +1,90 @@ +// Generated from cmux-tui/spec/sdk-schema.json. DO NOT EDIT. +package com.cmux.raw; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +/** Immutable presence-update request. Protocol v12; authority: control. */ +public final class PresenceUpdateRequest implements WireValue { + private final Field highlight; + private final Field pointer; + private final UInt64 surface; + + private PresenceUpdateRequest(Builder builder) { + this.highlight = builder.highlight; + this.pointer = builder.pointer; + if (!builder.surfaceSet) throw new IllegalArgumentException("surface is required"); + this.surface = Wire.nonNull(builder.surface, "surface"); + } + + public static Builder builder() { return new Builder(); } + + public Field highlight() { return highlight; } + public Field pointer() { return pointer; } + public UInt64 surface() { return surface; } + + public static PresenceUpdateRequest fromWire(Object value) { + Map object = Wire.object(value, "PresenceUpdateRequest"); + Builder builder = builder(); + Object rawHighlight = Wire.optional(object, "highlight"); + if (!Wire.isMissing(rawHighlight)) { + builder.highlight(rawHighlight == null ? null : PresenceHighlight.fromWire(rawHighlight)); + } + Object rawPointer = Wire.optional(object, "pointer"); + if (!Wire.isMissing(rawPointer)) { + builder.pointer(rawPointer == null ? null : PresenceAnchor.fromWire(rawPointer)); + } + Object rawSurface = Wire.required(object, "surface"); + builder.surface(Wire.uint64(rawSurface, "PresenceUpdateRequest.surface")); + return builder.build(); + } + + @Override + public Map toWire() { + LinkedHashMap object = new LinkedHashMap<>(); + Wire.put(object, "highlight", highlight); + Wire.put(object, "pointer", pointer); + Wire.put(object, "surface", surface); + return Collections.unmodifiableMap(object); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof PresenceUpdateRequest that)) return false; + return Objects.equals(highlight, that.highlight) && Objects.equals(pointer, that.pointer) && Objects.equals(surface, that.surface); + } + + @Override + public int hashCode() { return Objects.hash(highlight, pointer, surface); } + + @Override + public String toString() { return "PresenceUpdateRequest" + toWire(); } + + public static final class Builder { + private Field highlight = Field.omitted(); + private Field pointer = Field.omitted(); + private UInt64 surface; + private boolean surfaceSet; + + public Builder highlight(PresenceHighlight value) { + this.highlight = Field.ofNullable(value); + return this; + } + public Builder pointer(PresenceAnchor value) { + this.pointer = Field.ofNullable(value); + return this; + } + public Builder surface(UInt64 value) { + this.surface = value; + this.surfaceSet = true; + return this; + } + public PresenceUpdateRequest build() { return new PresenceUpdateRequest(this); } + } +} diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/Protocol.java b/cmux-tui/bindings/java/src/com/cmux/raw/Protocol.java index 5b731279774a..1a09c1ac9f2a 100644 --- a/cmux-tui/bindings/java/src/com/cmux/raw/Protocol.java +++ b/cmux-tui/bindings/java/src/com/cmux/raw/Protocol.java @@ -9,7 +9,7 @@ public final class Protocol { public static final String SDK_VERSION = "1.0.0"; public static final int VERSION = 12; public static final int SCHEMA_VERSION = 2; - public static final String IR_SHA256 = "8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86"; + public static final String IR_SHA256 = "e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663"; private Protocol() {} public static ProtocolEvent decodeEvent(Object value) { @@ -40,6 +40,7 @@ public static ProtocolEvent decodeEvent(Object value) { case "pairing-resolved" -> PairingResolvedEvent.fromWire(value); case "pane-added" -> PaneAddedEvent.fromWire(value); case "pane-closed" -> PaneClosedEvent.fromWire(value); + case "presence-changed" -> PresenceChangedEvent.fromWire(value); case "render-delta" -> RenderDeltaEvent.fromWire(value); case "render-state" -> RenderStateEvent.fromWire(value); case "resized" -> ResizedEvent.fromWire(value); diff --git a/cmux-tui/bindings/java/src/com/cmux/raw/SubscribeRequest.java b/cmux-tui/bindings/java/src/com/cmux/raw/SubscribeRequest.java index f26380ef7421..e84b1c6435d1 100644 --- a/cmux-tui/bindings/java/src/com/cmux/raw/SubscribeRequest.java +++ b/cmux-tui/bindings/java/src/com/cmux/raw/SubscribeRequest.java @@ -12,22 +12,29 @@ /** Immutable subscribe request. Protocol v5; authority: frontend. */ public final class SubscribeRequest implements WireValue { + private final Field presenceOnly; private final Field surface; private final Field treeEvents; private SubscribeRequest(Builder builder) { + this.presenceOnly = builder.presenceOnly; this.surface = builder.surface; this.treeEvents = builder.treeEvents; } public static Builder builder() { return new Builder(); } + public Field presenceOnly() { return presenceOnly; } public Field surface() { return surface; } public Field treeEvents() { return treeEvents; } public static SubscribeRequest fromWire(Object value) { Map object = Wire.object(value, "SubscribeRequest"); Builder builder = builder(); + Object rawPresenceOnly = Wire.optional(object, "presence_only"); + if (!Wire.isMissing(rawPresenceOnly)) { + builder.presenceOnly(rawPresenceOnly == null ? null : Wire.bool(rawPresenceOnly, "SubscribeRequest.presence_only")); + } Object rawSurface = Wire.optional(object, "surface"); if (!Wire.isMissing(rawSurface)) { builder.surface(rawSurface == null ? null : Wire.uint64(rawSurface, "SubscribeRequest.surface")); @@ -42,6 +49,7 @@ public static SubscribeRequest fromWire(Object value) { @Override public Map toWire() { LinkedHashMap object = new LinkedHashMap<>(); + Wire.put(object, "presence_only", presenceOnly); Wire.put(object, "surface", surface); Wire.put(object, "tree_events", treeEvents); return Collections.unmodifiableMap(object); @@ -50,19 +58,24 @@ public Map toWire() { @Override public boolean equals(Object other) { if (!(other instanceof SubscribeRequest that)) return false; - return Objects.equals(surface, that.surface) && Objects.equals(treeEvents, that.treeEvents); + return Objects.equals(presenceOnly, that.presenceOnly) && Objects.equals(surface, that.surface) && Objects.equals(treeEvents, that.treeEvents); } @Override - public int hashCode() { return Objects.hash(surface, treeEvents); } + public int hashCode() { return Objects.hash(presenceOnly, surface, treeEvents); } @Override public String toString() { return "SubscribeRequest" + toWire(); } public static final class Builder { + private Field presenceOnly = Field.omitted(); private Field surface = Field.omitted(); private Field treeEvents = Field.omitted(); + public Builder presenceOnly(Boolean value) { + this.presenceOnly = Field.ofNullable(value); + return this; + } public Builder surface(UInt64 value) { this.surface = Field.ofNullable(value); return this; diff --git a/cmux-tui/bindings/java/tests/com/cmux/raw/GeneratedCoverageTest.java b/cmux-tui/bindings/java/tests/com/cmux/raw/GeneratedCoverageTest.java index 930e51c06a53..9df4bad82742 100644 --- a/cmux-tui/bindings/java/tests/com/cmux/raw/GeneratedCoverageTest.java +++ b/cmux-tui/bindings/java/tests/com/cmux/raw/GeneratedCoverageTest.java @@ -20,8 +20,8 @@ public final class GeneratedCoverageTest { public static void main(String[] args) throws Exception { check(Protocol.VERSION == 12, "protocol version"); check("1.0.0".equals(Protocol.SDK_VERSION), "SDK release version"); - check(Commands.ALL.size() == 106, "all 106 commands generated"); - check(Events.ALL.size() == 48, "all 48 events generated"); + check(Commands.ALL.size() == 109, "all 109 commands generated"); + check(Events.ALL.size() == 49, "all 49 events generated"); Map methods = Arrays.stream(GeneratedCmuxClient.class.getDeclaredMethods()) .filter(method -> Modifier.isPublic(method.getModifiers())) diff --git a/cmux-tui/bindings/python/cmux/raw/_generated/.cmux-sdk-manifest.json b/cmux-tui/bindings/python/cmux/raw/_generated/.cmux-sdk-manifest.json index 74807aae1358..9bf19cf6d383 100644 --- a/cmux-tui/bindings/python/cmux/raw/_generated/.cmux-sdk-manifest.json +++ b/cmux-tui/bindings/python/cmux/raw/_generated/.cmux-sdk-manifest.json @@ -7,32 +7,32 @@ }, { "path": "_schema.py", - "sha256": "d3156a009731734ca32473dd49bb07e65b398527c38c8fd51c8846296c1f3ca2", - "size": 164021 + "sha256": "51342819216072d8ab12459f5d6210c95852e7b7b6f664f75b0b73742715ce17", + "size": 169113 }, { "path": "client.py", - "sha256": "9b39b635a0c05289b345896c0c3da61ccf3e62aba7ab6c61b53e2cfcba51629e", - "size": 38845 + "sha256": "b2dafcad84994b1a8b70dbaf03bc1c754958b97f9ae63359726028a2f5a38e61", + "size": 39752 }, { "path": "codec.py", - "sha256": "0362dabbf7a73c616a68915bca6e9247a57bebd5f6affd1905ea497c2e759c68", - "size": 29462 + "sha256": "ae738fb9529d508863d2bc1d8f91219f89038a784a84acbbc6cc12b804d15fe4", + "size": 30104 }, { "path": "metadata.py", - "sha256": "f28309aad5e301e2e561cc9984a27b752c6852e5f7f1b38e9e400d10bc8f7c1c", - "size": 46234 + "sha256": "f2e4682adf2ab57e0604aca305aace922dd4838905fd7d2197d4b29f394245bd", + "size": 47275 }, { "path": "models.py", - "sha256": "28a7efcb96474805ca36770f2d091d238add2ba0f085b5e8cc8a23b88d6520f7", - "size": 89677 + "sha256": "642d3cb95c45207b1677c1026a92cf87c5163b316ae6a07ef5a3294558f7fc13", + "size": 92475 } ], "format": 1, - "ir_sha256": "8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86", + "ir_sha256": "e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663", "language": "python", "mux_protocol": 12, "schema_version": 2 diff --git a/cmux-tui/bindings/python/cmux/raw/_generated/_schema.py b/cmux-tui/bindings/python/cmux/raw/_generated/_schema.py index 86760451fc5f..7527150f08be 100644 --- a/cmux-tui/bindings/python/cmux/raw/_generated/_schema.py +++ b/cmux-tui/bindings/python/cmux/raw/_generated/_schema.py @@ -5,4 +5,4 @@ import json -SCHEMA = json.loads('{"$schema":"./sdk-schema.schema.json","commands":{"apply-layout":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["layout must contain at least one leaf or stack member.","cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"layout":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"DeclarativeLayout"}},"name":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ApplyLayoutResult"},"since":6,"stream":null},"attach-surface":{"authority":"frontend","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["cols and rows must be supplied together.","Browser surfaces reject mode:render."],"fields":{"cols":{"capability":"attach-initial-size","default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"mode":{"default":"bytes","nullable":true,"presence":"optional","since":7,"type":{"kind":"enum","values":["bytes","render"]}},"rows":{"capability":"attach-initial-size","default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":{"event_names":["browser-state","colors-changed","detached","frame","notification","output","overflow","render-delta","render-state","resized","scroll-changed","vt-state"],"kind":"attach","mode_field":"mode","modes":{"browser":["browser-state","frame","notification","scroll-changed","overflow","detached"],"bytes":["vt-state","output","resized","colors-changed","scroll-changed","notification","overflow","detached"],"render":["render-state","render-delta","scroll-changed","overflow","detached"]},"ordering":"Initial state precedes the command response. Later surface events preserve order. A resized replay replaces the previous byte mirror; overflow ends that surface stream.","terminal_event":"detached"}},"browser-activate":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-back":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only; queue acknowledgement is not page-load success."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-forward":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only; queue acknowledgement is not page-load success."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-frame-presented":{"authority":"frontend","capability":"browser-pointer-frame-guard-v1","constraints":["Acknowledges the exact rendered browser frame for this connection.","Requires browser-pointer-frame-guard-v1."],"request":{"additional_properties":false,"fields":{"frame_seq":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":10,"stream":null},"browser-insert-text":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only.","The bounded disposable input queue drops newest input when full."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"text":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-key":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only.","The bounded disposable input queue drops newest input when full."],"request":{"additional_properties":false,"fields":{"code":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["down","up"]}},"modifiers":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"text":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"windows_virtual_key_code":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-key-press":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only.","One request preserves the atomic press sequence through the bounded input queue."],"request":{"additional_properties":false,"fields":{"code":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"modifiers":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"text":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"windows_virtual_key_code":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":10,"stream":null},"browser-mouse":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only; coordinates are CSS pixels.","The bounded disposable input queue drops newest input when full."],"request":{"additional_properties":false,"fields":{"button":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"click_count":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint32"}},"frame_seq":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["down","up","move"]}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"x_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"y_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-mouse-guarded":{"authority":"frontend","capability":"browser-pointer-frame-guard-v1","constraints":["Browser surfaces only; coordinates are CSS pixels.","The frame sequence must be the exact presented token for this connection.","Requires browser-pointer-frame-guard-v1."],"request":{"additional_properties":false,"fields":{"button":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"click_count":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint32"}},"frame_seq":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["down","up","move"]}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"x_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"y_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":10,"stream":null},"browser-navigate":{"authority":"frontend","capability":null,"constraints":["Queue acknowledgement only; observe browser-state for outcome.","Navigation is latest-wins."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"url":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-reload":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only; queue acknowledgement is not page-load success."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-wheel":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only; values are CSS pixels.","The bounded disposable input queue drops newest input when full."],"request":{"additional_properties":false,"fields":{"delta_y_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"frame_seq":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"x_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"y_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-wheel-guarded":{"authority":"frontend","capability":"browser-pointer-frame-guard-v1","constraints":["Browser surfaces only; values are CSS pixels.","The frame sequence must be the exact presented token for this connection.","Requires browser-pointer-frame-guard-v1."],"request":{"additional_properties":false,"fields":{"delta_y_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"frame_seq":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"x_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"y_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":10,"stream":null},"clear-history":{"authority":"control","capability":"clear-history-v1","constraints":["PTY surfaces only.","Failed responses classify error_delivery as known-not-delivered or ambiguous."],"request":{"additional_properties":false,"fields":{"fallback_key":{"capability":"clear-history-key-v1","default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"ref","name":"TerminalKeyInput"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":9,"stream":null},"clear-window-title":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"client-focus":{"authority":"control","capability":"client-focus-v1","constraints":[],"request":{"additional_properties":false,"fields":{"client_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"additional_properties":false,"fields":{"pane":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"tab":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":12,"stream":null},"close-pane":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"close-provider-managed-workspace":{"authority":"provider-authority","capability":"provider-managed-workspace-authority-v2","constraints":["Call only after the external provider durably accepts the close."],"request":{"additional_properties":false,"constraints":["workspace and key must identify the same live provider-managed workspace."],"fields":{"authority":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ProviderWorkspaceMutationResult"},"since":9,"stream":null},"close-screen":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"close-surface":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"close-terminal":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent."],"fields":{"expected_generation":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"CloseTerminalResult"},"since":9,"stream":null},"close-workspace":{"authority":"control","capability":null,"constraints":["Provider-managed workspaces reject this ordinary mutation."],"request":{"additional_properties":false,"constraints":["At least one of workspace and key must be supplied; both must identify the same workspace when supplied.","origin and mutation_id are either both present or both absent."],"fields":{"expected_generation":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"uint64"}},"key":{"capability":"workspace-registry-v1","default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"WorkspaceMutationResult"},"since":5,"stream":null},"copy":{"authority":"control","capability":null,"constraints":["PTY surfaces only."],"request":{"additional_properties":false,"fields":{"mode":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["screen","selection","scrollback"]}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"CopyResult"},"since":6,"stream":null},"create-surface-with-receipt":{"authority":"control","capability":"creation-receipts-v1","constraints":["Repeating one origin and receipt with identical fields returns the original creation result.","A new idempotency_key is valid only when durable creation resolution instructs retry_new_idempotency_key."],"request":{"additional_properties":false,"constraints":["operation is one of new-tab, run-command, new-browser-tab, new-workspace, new-screen, new-pane, new-pane-right, split-right, or split-down.","Each operation admits only its documented selector and option fields.","idempotency_key names one execution attempt and defaults to receipt.","cols and rows must be supplied together."],"fields":{"argv":{"default":null,"nullable":true,"presence":"optional","type":{"items":{"kind":"scalar","name":"string"},"kind":"array"}},"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"cwd":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"idempotency_key":{"capability":"creation-attempt-keys-v1","default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"operation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"origin":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}},"receipt":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"selector_fallbacks":{"default":[],"nullable":false,"presence":"optional","type":{"items":{"kind":"ref","name":"ResourceSelectors"},"kind":"array","max_items":7}},"selectors":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"ResourceSelectors"}},"url":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"width":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"float32"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"JsonValue"},"since":10,"stream":null},"create-terminal":{"authority":"control","capability":"workspace-registry-v1","constraints":[],"request":{"additional_properties":false,"constraints":["At least one of workspace and key must be supplied; when both are supplied they must identify the same workspace.","argv and command are mutually exclusive and must be nonempty when supplied.","cols and rows must be supplied together.","origin and mutation_id are either both present or both absent.","terminal_id may be supplied only when origin and mutation_id are both present."],"fields":{"argv":{"default":null,"nullable":true,"presence":"optional","type":{"items":{"kind":"scalar","name":"string"},"kind":"array","min_items":1}},"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"command":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"cwd":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"expected_generation":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"key":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"name":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"terminal_id":{"constraints":[{"format":"32-character lowercase UUIDv4 hex without dashes","pattern":"^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$"}],"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"string"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"TerminalPlacement"},"since":7,"stream":null},"create-workspace":{"authority":"control","capability":"workspace-registry-v1","constraints":[],"request":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent.","At most 4096 live workspaces may exist; tombstoned keys cannot be reused."],"fields":{"expected_generation":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"key":{"constraints":[{"format":"lowercase canonical UUID"}],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"name":{"constraints":[{"max_utf8_bytes":1024}],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"WorkspaceMutationResult"},"since":7,"stream":null},"detach-attached-view":{"authority":"frontend","capability":"view-attachment-detach-v1","constraints":["The command closes only the named view stream and releases its size contribution.","A retired lease returns outcome:superseded."],"request":{"additional_properties":false,"fields":{"lease":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"AttachedViewOutcomeResult"},"since":10,"stream":null},"detach-client":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"client":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"export-layout":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"screen":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ExportLayoutResult"},"since":6,"stream":null},"focus-direction":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"dir":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"PaneDirection"}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"FocusDirectionResult"},"since":6,"stream":null},"focus-pane":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"get-browser-provider":{"authority":"local-admin","capability":"browser-provider-v1","constraints":["Provider endpoints and targets are disclosed only over a trusted local transport; bearer credentials are accepted only during registration and are never returned.","Automation must select a target by stable tab id instead of treating CDP discovery as topology authority."],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"BrowserProviderSnapshot"},"since":10,"stream":null},"get-cell-pixels":{"authority":"frontend","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"GetCellPixelsResult"},"since":6,"stream":null},"get-frontend-projection":{"authority":"control","capability":null,"constraints":["Each identifier is nonempty, contains no control character, and is at most 128 bytes."],"request":{"additional_properties":false,"fields":{"frontend":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"scope":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"subject_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"FrontendProjection"},"since":7,"stream":null},"identify":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"IdentifyResult"},"since":5,"stream":null},"ids":{"authority":"control","capability":null,"constraints":["Short ids are snapshot-local labels; command parameters accept numeric ids only."],"request":{"additional_properties":false,"fields":{"kind":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"enum","values":["workspace","screen","pane","surface"]}}},"kind":"object"},"result":{"kind":"ref","name":"IdsResult"},"since":6,"stream":null},"journal-frontend-event":{"authority":"control","capability":"frontend-journal-v1","constraints":["The server derives producer identity from the authenticated control client."],"request":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"FrontendJournalEvent"}}},"kind":"object"},"result":{"additional_properties":false,"fields":{"committed":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}}},"kind":"object"},"since":10,"stream":null},"list-agents":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"state":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"AgentState"}},"surface":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ListAgentsResult"},"since":6,"stream":null},"list-clients":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"items":{"kind":"ref","name":"ClientInfo"},"kind":"array"},"since":6,"stream":null},"list-terminals":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"ListTerminalsResult"},"since":9,"stream":null},"list-workspaces":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"Tree"},"since":5,"stream":null},"machine-listening-tcp":{"authority":"control","capability":"machine-listening-tcp-v1","constraints":["Routine Cloud port inventory uses this command over the authenticated private cmux-tui link."],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"MachineListeningTcpResult"},"since":12,"stream":null},"machine-usage":{"authority":"control","capability":"machine-usage-v1","constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"MachineUsageResult"},"since":12,"stream":null},"mark-workspaces-provider-managed":{"authority":"provider-authority","capability":"provider-managed-workspace-authority-v2","constraints":["Authority must match the value provisioned before this mux generation accepted control clients."],"request":{"additional_properties":false,"fields":{"authority":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":9,"stream":null},"mint-terminal-renderer":{"authority":"frontend","capability":null,"constraints":["Only terminal-host-backed PTYs can mint one-use renderer credentials."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"ttl_ms":{"constraints":[{"maximum":60000,"minimum":1}],"default":30000,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"MintTerminalRendererResult"},"since":9,"stream":null},"mint-terminal-renderer-by-terminal":{"authority":"frontend","capability":null,"constraints":["The terminal resource ID is resolved atomically to the live terminal-host-backed PTY before minting a one-use renderer credential."],"request":{"additional_properties":false,"fields":{"terminal":{"constraints":[{"pattern":"^term_[0-9a-f]{32}$"}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"ttl_ms":{"constraints":[{"maximum":60000,"minimum":1}],"default":30000,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"MintTerminalRendererResult"},"since":11,"stream":null},"move-tab":{"authority":"control","capability":null,"constraints":["An out-of-range index clamps to the destination end."],"request":{"additional_properties":false,"fields":{"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"move-terminal":{"authority":"control","capability":null,"constraints":["A move to the current workspace still commits a terminal revision with changed:false."],"request":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent."],"fields":{"expected_generation":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"workspace_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"MoveTerminalResult"},"since":9,"stream":null},"move-workspace":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["At least one of workspace and key must be supplied; both must identify the same workspace when supplied.","origin and mutation_id are either both present or both absent."],"fields":{"expected_generation":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"uint64"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"key":{"capability":"workspace-registry-v1","default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"WorkspaceMutationResult"},"since":5,"stream":null},"new-browser-tab":{"authority":"control","capability":null,"constraints":["Bootstrap and navigation failures are asynchronous browser-state outcomes."],"request":{"additional_properties":false,"constraints":["cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"url":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"SurfaceResult"},"since":5,"stream":null},"new-pane":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"result":{"kind":"ref","name":"SurfaceResult"},"since":9,"stream":null},"new-pane-right":{"authority":"control","capability":"viewport-splits-v1","constraints":[],"request":{"additional_properties":false,"constraints":["Omitted width defaults to two thirds.","cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"width":{"constraints":[{"maximum":1.0,"minimum":0.1}],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"float32"}}},"kind":"object"},"result":{"kind":"ref","name":"SurfaceResult"},"since":9,"stream":null},"new-screen":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"SurfaceResult"},"since":5,"stream":null},"new-tab":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"cwd":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"result":{"kind":"ref","name":"SurfaceResult"},"since":5,"stream":null},"new-workspace":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"name":{"constraints":[{"max_utf8_bytes":1024}],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"result":{"kind":"ref","name":"SurfaceResult"},"since":5,"stream":null},"notify":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"body":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"level":{"default":"info","nullable":true,"presence":"optional","type":{"kind":"ref","name":"NotificationLevel"}},"surface":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}},"title":{"constraints":[{"min_length":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"NotifyResult"},"since":6,"stream":null},"pairing-response":{"authority":"local-admin","capability":null,"constraints":["The request id must identify a live, unexpired pairing challenge."],"request":{"additional_properties":false,"fields":{"approve":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"request":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":7,"stream":null},"pane-neighbor":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"dir":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"PaneDirection"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"PaneNeighborResult"},"since":6,"stream":null},"ping":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"PingResult"},"since":6,"stream":null},"process-info":{"authority":"control","capability":null,"constraints":["PTY surfaces only."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ProcessInfoResult"},"since":6,"stream":null},"put-frontend-projection":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent.","Serialized projection must be at most 1048576 bytes."],"fields":{"expected_generation":{"default":null,"description":"Accepted by the current decoder but ignored for projection writes.","nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"expected_projection_revision":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"description":"Accepted by the current decoder but ignored for projection writes.","nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"frontend":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"projection":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"JsonValue"}},"schema_version":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"scope":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"subject_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"FrontendProjection"},"since":7,"stream":null},"read-screen":{"authority":"control","capability":null,"constraints":["PTY surfaces only."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ReadScreenResult"},"since":5,"stream":null},"read-scrollback":{"authority":"control","capability":null,"constraints":["PTY surfaces only; row indexes are snapshot-relative and not durable."],"request":{"additional_properties":false,"fields":{"count":{"constraints":[{"maximum":65535,"minimum":0}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"start":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ReadScrollbackResult"},"since":7,"stream":null},"register-browser-provider":{"authority":"local-admin","capability":"browser-provider-v1","constraints":["The lease is scoped to the trusted local control connection and is released on disconnect.","The endpoint must be an explicit loopback ws URL with no credentials or fragment.","Bearer authentication is optional and sends the token only in the CDP WebSocket upgrade Authorization header.","Each registration replaces that connection\'s complete target set; target ids are never journaled."],"request":{"additional_properties":false,"fields":{"authentication":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"BrowserProviderAuthentication"}},"bearer_token":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"endpoint":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"provider_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"targets":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"BrowserProviderTarget"},"kind":"array"}}},"kind":"object"},"result":{"kind":"ref","name":"BrowserProviderSnapshot"},"since":10,"stream":null},"release-attached-view-size":{"authority":"frontend","capability":"view-attachment-lease-v1","constraints":["The attach stream remains live for cached rendering."],"request":{"additional_properties":false,"fields":{"lease":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"AttachedViewOutcomeResult"},"since":10,"stream":null},"release-surface-size":{"authority":"control","capability":null,"constraints":["An absent lease is a successful no-op."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":7,"stream":null},"reload-config":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"additional_properties":false,"fields":{"path":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"reloaded":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}}},"kind":"object"},"since":6,"stream":null},"rename-pane":{"authority":"control","capability":null,"constraints":["An empty name clears the pane name."],"request":{"additional_properties":false,"fields":{"name":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"rename-provider-managed-workspace":{"authority":"provider-authority","capability":"provider-managed-workspace-authority-v2","constraints":["Call only after the external provider durably accepts the rename."],"request":{"additional_properties":false,"constraints":["workspace and key must identify the same live provider-managed workspace."],"fields":{"authority":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"name":{"constraints":[{"max_utf8_bytes":1024}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ProviderWorkspaceMutationResult"},"since":9,"stream":null},"rename-screen":{"authority":"control","capability":null,"constraints":["An empty name clears the screen name."],"request":{"additional_properties":false,"fields":{"name":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"rename-surface":{"authority":"control","capability":null,"constraints":["An empty name clears the surface name."],"request":{"additional_properties":false,"fields":{"name":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"rename-workspace":{"authority":"control","capability":null,"constraints":["Provider-managed workspaces reject this ordinary mutation."],"request":{"additional_properties":false,"constraints":["At least one of workspace and key must be supplied; both must identify the same workspace when supplied.","origin and mutation_id are either both present or both absent."],"fields":{"expected_generation":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"uint64"}},"key":{"capability":"workspace-registry-v1","default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"name":{"constraints":[{"max_utf8_bytes":1024}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"WorkspaceMutationResult"},"since":5,"stream":null},"report-agent":{"authority":"control","capability":null,"constraints":["A stored hook report outranks later socket reports until another hook report or surface close."],"request":{"additional_properties":false,"fields":{"session":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"source":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentReportSource"}},"state":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentState"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ReportAgentResult"},"since":6,"stream":null},"report-focus":{"authority":"control","capability":"client-focus-v1","constraints":[],"request":{"additional_properties":false,"fields":{"client_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"tab":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":12,"stream":null},"resize-attached-view":{"authority":"frontend","capability":"view-attachment-lease-v1","constraints":["The lease must belong to this connection and surface.","A retired lease returns outcome:superseded without changing replacement views."],"request":{"additional_properties":false,"fields":{"cols":{"constraints":[{"clamped_maximum":10000,"clamped_minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"lease":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"rows":{"constraints":[{"clamped_maximum":10000,"clamped_minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"AttachedViewResizeResult"},"since":10,"stream":null},"resize-surface":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"cols":{"constraints":[{"clamped_maximum":10000,"clamped_minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"rows":{"constraints":[{"clamped_maximum":10000,"clamped_minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ResizeSurfaceResult"},"since":5,"stream":null},"resolve-terminal":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"terminal_id":{"constraints":[{"format":"UUIDv4 hex without dashes","pattern":"^[0-9a-f]{32}$"}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"ResolveTerminalResult"},"since":9,"stream":null},"run":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["Exactly one of argv and command must be supplied.","pane and new_workspace:true are mutually exclusive.","key is valid only with new_workspace:true.","cols and rows affect sizing only when both are present."],"fields":{"argv":{"default":null,"nullable":true,"presence":"optional","type":{"items":{"kind":"scalar","name":"string"},"kind":"array","min_items":1}},"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"command":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"cwd":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"key":{"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"string"}},"name":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"new_workspace":{"default":false,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"result":{"kind":"ref","name":"RunResult"},"since":6,"stream":null},"scroll-surface":{"authority":"control","capability":null,"constraints":["PTY surfaces only; negative values scroll up."],"request":{"additional_properties":false,"fields":{"delta":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"int64"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"select-screen":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["When both index and delta are supplied, index wins."],"fields":{"delta":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"int64"}},"index":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"select-tab":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["When both index and delta are supplied, index wins."],"fields":{"delta":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"int64"}},"index":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"select-workspace":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["When both index and delta are supplied, index wins."],"fields":{"delta":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"int64"}},"index":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"send":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["When both are present, UTF-8 text bytes precede decoded bytes."],"fields":{"bytes":{"constraints":[{"encoding":"standard base64"}],"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Base64"}},"paste":{"default":false,"nullable":false,"presence":"optional","since":7,"type":{"kind":"scalar","name":"boolean"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"text":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"send-key":{"authority":"control","capability":null,"constraints":["PTY surfaces only."],"request":{"additional_properties":false,"fields":{"keys":{"constraints":[{"syntax":"lowercase modifier+key chords"}],"nullable":false,"presence":"required","type":{"items":{"kind":"scalar","name":"string"},"kind":"array","min_items":1}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"server-stats":{"authority":"local-admin","capability":"server-stats-v1","constraints":["Owner-only diagnostics; never journaled and safe to poll."],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"ServerStatsResult"},"since":12,"stream":null},"set-cell-pixels":{"authority":"frontend","capability":null,"constraints":["Accepted browser resizes complete asynchronously."],"request":{"additional_properties":false,"fields":{"height_px":{"constraints":[{"clamped_minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"width_px":{"constraints":[{"clamped_minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"result":{"kind":"ref","name":"SetCellPixelsResult"},"since":6,"stream":null},"set-client-info":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"capabilities":{"constraints":["Advertises additive client capabilities for this connection."],"default":null,"nullable":true,"presence":"optional","type":{"items":{"kind":"scalar","name":"string"},"kind":"array"}},"kind":{"constraints":["Control characters become spaces; at most 64 Unicode characters are retained."],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"name":{"constraints":["Control characters become spaces; at most 64 Unicode characters are retained."],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"set-client-sizing":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["exclusive:true requires client and enabled:true.","Omitting client is valid only with enabled:true and restores all clients for the surface."],"fields":{"client":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"enabled":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"exclusive":{"default":false,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":10,"stream":null},"set-default-colors":{"authority":"control","capability":null,"constraints":["Color strings are exactly #rrggbb.","With complete:true, absent optional values reset to built-in defaults."],"request":{"additional_properties":false,"fields":{"bg":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"ColorHex"}},"complete":{"default":false,"nullable":false,"presence":"optional","since":9,"type":{"kind":"scalar","name":"boolean"}},"cursor":{"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"ref","name":"ColorHex"}},"cursor_blink":{"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"boolean"}},"cursor_style":{"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"ref","name":"CursorStyle"}},"fg":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"ColorHex"}},"palette":{"constraints":["Decimal string keys are palette indexes 0 through 255."],"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"map","values":{"kind":"ref","name":"ColorHex"}}},"selection_bg":{"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"ref","name":"ColorHex"}},"selection_fg":{"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"ref","name":"ColorHex"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"set-ratio":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"dir":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"SplitDirection"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"ratio":{"constraints":[{"clamped_maximum":0.95,"clamped_minimum":0.05}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float32"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"set-split-ratio":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"ratio":{"constraints":[{"clamped_maximum":0.95,"clamped_minimum":0.05}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float32"}},"split":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"transaction":{"capability":"layout-undo-v1","default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":8,"stream":null},"set-viewport-pane-width":{"authority":"control","capability":"viewport-column-resize-v1","constraints":["width must be finite."],"request":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"transaction":{"capability":"layout-undo-v1","default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"uint64"}},"width":{"constraints":[{"maximum":1.0,"minimum":0.1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float32"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":9,"stream":null},"set-window-title":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"title":{"constraints":["C0 controls are sanitized before OSC output."],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"shutdown-daemon":{"authority":"local-admin","capability":null,"constraints":["pid and generation must match the latest identify result.","force bypasses native-browser ownership only; the identity fence and trusted-local authority still apply.","Clients must require daemon-handoff-force-v1 before sending force:true.","The daemon exits only after the success response is queued."],"request":{"additional_properties":false,"fields":{"force":{"capability":"daemon-handoff-force-v1","default":false,"nullable":false,"presence":"optional","since":10,"type":{"kind":"scalar","name":"boolean"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"pid":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"result":{"kind":"ref","name":"ShutdownDaemonResult"},"since":9,"stream":null},"sidebar-plugin":{"authority":"frontend","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"relaunch":{"default":false,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"result":{"kind":"ref","name":"SidebarPluginResult"},"since":6,"stream":null},"split":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"dir":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"SplitDirection"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"result":{"kind":"ref","name":"SurfaceResult"},"since":5,"stream":null},"subscribe":{"authority":"frontend","capability":null,"constraints":["subscribe sends no initial tree snapshot.","surface filtering occurs before the bounded mailbox."],"request":{"additional_properties":false,"fields":{"surface":{"capability":"surface-subscribe-filter","default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"ref","name":"Id"}},"tree_events":{"default":"coarse","nullable":true,"presence":"optional","since":7,"type":{"kind":"enum","values":["coarse","deltas"]}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":{"event_names":["agent-changed","bell","client-attached","client-changed","client-detached","client-list-invalidated","config-reload-requested","empty","frontend-projection-changed","layout-changed","notification","overflow","pairing-requested","pairing-resolved","pane-added","pane-closed","screen-added","screen-closed","screen-renamed","scroll-changed","status","surface-exited","surface-output","surface-resize-failed","surface-resized","tab-added","tab-closed","tab-renamed","terminal-registry-changed","title-changed","tree-changed","window-title-requested","workspace-added","workspace-closed","workspace-moved","workspace-renamed"],"kind":"subscribe","mode_field":"tree_events","modes":{"coarse":["tree-changed"],"deltas":["workspace-added","workspace-closed","workspace-renamed","workspace-moved","screen-added","screen-closed","screen-renamed","pane-added","pane-closed","tab-added","tab-closed","tab-renamed","tree-changed"]},"ordering":"Response and event objects may interleave. Events preserve enqueue order per subscription. Delta workspace revisions are serialized in durable commit order; overflow ends the stream and requires resubscribe plus snapshot.","terminal_event":null}},"swap-pane":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["Exactly one of dir and target must be supplied."],"fields":{"dir":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"PaneDirection"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"target":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"terminal-events":{"authority":"control","capability":null,"constraints":["Consumers apply only contiguous revisions for one registry_id and generation."],"request":{"additional_properties":false,"fields":{"after_revision":{"default":0,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"TerminalEventsResult"},"since":9,"stream":null},"undo-layout":{"authority":"control","capability":"layout-undo-v1","constraints":["Clients must reject incomplete or contradictory result variants."],"request":{"additional_properties":false,"constraints":["confirm_close requires the exact preview revision."],"fields":{"confirm_close":{"default":false,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"revision":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"LayoutUndoResult"},"since":9,"stream":null},"unregister-browser-provider":{"authority":"local-admin","capability":"browser-provider-v1","constraints":["Only the calling connection\'s provider lease is removed."],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"BrowserProviderUnregisterResult"},"since":10,"stream":null},"vt-state":{"authority":"control","capability":null,"constraints":["PTY surfaces only."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"VtStateResult"},"since":5,"stream":null},"wait-for":{"authority":"control","capability":null,"constraints":["Blocks subsequent requests on this connection; SDKs should use a dedicated connection."],"request":{"additional_properties":false,"fields":{"pattern":{"constraints":[{"syntax":"Rust regex"}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"timeout_ms":{"description":"Zero performs one immediate check.","nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"WaitForResult"},"since":6,"stream":null},"zoom-pane":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"mode":{"default":"toggle","nullable":true,"presence":"optional","type":{"kind":"enum","values":["toggle","on","off"]}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ZoomPaneResult"},"since":6,"stream":null}},"events":{"agent-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"agent-changed"}},"session":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"source":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentSource"}},"state":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentState"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"updated_at_ms":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":11,"streams":["subscribe"]},"bell":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"bell"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"browser-state":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"error":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"browser-state"}},"frame":{"description":"The initial browser-state includes the latest frame when one exists; later state updates omit it.","nullable":true,"presence":"optional","type":{"kind":"ref","name":"BrowserFrame"}},"frames_stalled":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"status":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["starting","live","failed"]}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"title":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"url":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":6,"streams":["attach-browser"]},"client-attached":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"client":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"client-attached"}},"kind":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"name":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"transport":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["unix","ws"]}}},"kind":"object"},"since":6,"streams":["subscribe"]},"client-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"client":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"client-changed"}},"kind":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"name":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":6,"streams":["subscribe"]},"client-detached":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"client":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"client-detached"}}},"kind":"object"},"since":6,"streams":["subscribe"]},"client-list-invalidated":{"capability":null,"emission":"serialized-never-emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"client-list-invalidated"}}},"kind":"object"},"since":9,"streams":["subscribe"]},"colors-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"bg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"cursor":{"nullable":true,"presence":"optional","type":{"kind":"ref","name":"ColorHex"}},"cursor_blink":{"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"cursor_style":{"nullable":true,"presence":"optional","type":{"kind":"ref","name":"CursorStyle"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"colors-changed"}},"fg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"palette":{"nullable":false,"presence":"optional","since":7,"type":{"kind":"map","values":{"kind":"ref","name":"ColorHex"}}},"selection_bg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"selection_fg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"surface":{"nullable":false,"presence":"optional","since":7,"type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":6,"streams":["attach-byte"]},"config-reload-requested":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"config-reload-requested"}}},"kind":"object"},"since":6,"streams":["subscribe"]},"daemon-shutdown":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"daemon-shutdown"}}},"kind":"object"},"since":12,"streams":["control"]},"detached":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"detached"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":5,"streams":["attach-byte","attach-render","attach-browser"]},"empty":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"empty"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"frame":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"data":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Base64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"frame"}},"height":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"seq":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"width":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"since":6,"streams":["attach-browser"]},"frontend-projection-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"frontend-projection-changed"}},"frontend":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"mutation_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"origin":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"projection_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"scope":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"subject_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":7,"streams":["subscribe"]},"graphics-status":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["kitty-image-budget-worker-start-failed carries error.","kitty-image-budget-update-failed carries retry_exhausted and summary.","cell-pixel-update-retries-exhausted carries attempts, remaining, cell_width, and cell_height."],"fields":{"attempts":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"cell_height":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"cell_width":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"error":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"graphics-status"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["kitty-image-budget-worker-start-failed","kitty-image-budget-update-failed","cell-pixel-update-retries-exhausted"]}},"remaining":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"retry_exhausted":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"summary":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":10,"streams":["subscribe"]},"layout-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"layout-changed"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":6,"streams":["subscribe"]},"machine-usage-changed":{"capability":"machine-usage-v1","emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"machine-usage-changed"}},"usage":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"MachineUsage"}}},"kind":"object"},"since":12,"streams":["subscribe"]},"notification":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"body":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"notification"}},"level":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"NotificationLevel"}},"notification":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"title":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":6,"streams":["subscribe","attach-byte","attach-browser"]},"output":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"colors":{"nullable":false,"presence":"optional","since":7,"type":{"kind":"ref","name":"TerminalColors"}},"data":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Base64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"output"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":5,"streams":["attach-byte"]},"overflow":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["scope and surface are either both present for attach overflow or both absent for subscribe overflow."],"fields":{"error":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"overflow"}},"scope":{"nullable":false,"presence":"optional","type":{"kind":"literal","value":"surface"}},"surface":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe","attach-byte","attach-render","attach-browser"]},"pairing-requested":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"code":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"pairing-requested"}},"expires_in":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"peer":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"request":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":7,"streams":["subscribe"]},"pairing-resolved":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"pairing-resolved"}},"request":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":7,"streams":["subscribe"]},"pane-added":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Pane"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"pane-added"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"pane-closed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Pane"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"pane-closed"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"render-delta":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["size is present if and only if the surface resized; every resize has full:true."],"fields":{"cursor":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"RenderCursor"}},"default_bg":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"ColorHex"}},"default_fg":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"ColorHex"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"render-delta"}},"full":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"graphics":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"RenderGraphicsDelta"}},"history_epoch":{"nullable":false,"presence":"optional","since":10,"type":{"kind":"scalar","name":"uint64"}},"rows":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"RenderRow"},"kind":"array"}},"scrollback_rows":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint32"}},"size":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"Size"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["attach-render"]},"render-state":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"cursor":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"RenderCursor"}},"default_bg":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"default_fg":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"render-state"}},"graphics":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"RenderGraphics"}},"history_epoch":{"nullable":false,"presence":"required","since":10,"type":{"kind":"scalar","name":"uint64"}},"rows":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"RenderRow"},"kind":"array"}},"scrollback_rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"size":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Size"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["attach-render"]},"resized":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["At least one of replay and data is present; replay is canonical from protocol 7."],"fields":{"colors":{"nullable":false,"presence":"optional","since":7,"type":{"kind":"ref","name":"TerminalColors"}},"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"data":{"description":"Protocol 6 compatibility field.","nullable":false,"presence":"optional","type":{"kind":"ref","name":"Base64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"resized"}},"kitty_graphics_state":{"nullable":false,"presence":"optional","since":10,"type":{"kind":"ref","name":"KittyGraphicsState"}},"kitty_image_aliases":{"nullable":false,"presence":"optional","since":9,"type":{"items":{"kind":"ref","name":"KittyImageAlias"},"kind":"array"}},"replay":{"nullable":false,"presence":"optional","since":7,"type":{"kind":"ref","name":"Base64"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":6,"streams":["attach-byte"]},"screen-added":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Screen"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"screen-added"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"screen-closed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Screen"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"screen-closed"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"screen-renamed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Screen"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"screen-renamed"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"scroll-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"at_bottom":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"scroll-changed"}},"offset":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":6,"streams":["subscribe","attach-byte","attach-render","attach-browser"]},"status":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"status"}},"message":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"surface-exited":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"surface-exited"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"surface-output":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"surface-output"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"surface-resize-failed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"error":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"surface-resize-failed"}},"reservation_id":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"retry_after_ms":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe"]},"surface-resized":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"surface-resized"}},"reservation_id":{"nullable":true,"presence":"required","since":7,"type":{"kind":"scalar","name":"uint64"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"tab-added":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Tab"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"tab-added"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"tab-closed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Tab"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"tab-closed"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"tab-renamed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Tab"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"tab-renamed"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"terminal-registry-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"terminal-registry-changed"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"refetch":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"terminal-events-or-list-terminals"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":9,"streams":["subscribe"]},"title-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"title-changed"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"title":{"nullable":false,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"tree-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"tree-changed"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"vt-state":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"colors":{"nullable":false,"presence":"optional","since":6,"type":{"kind":"ref","name":"TerminalColors"}},"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"data":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Base64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"vt-state"}},"kitty_graphics_state":{"nullable":false,"presence":"optional","since":10,"type":{"kind":"ref","name":"KittyGraphicsState"}},"kitty_image_aliases":{"nullable":false,"presence":"optional","since":9,"type":{"items":{"kind":"ref","name":"KittyImageAlias"},"kind":"array"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":5,"streams":["attach-byte"]},"window-title-requested":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"window-title-requested"}},"title":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":6,"streams":["subscribe"]},"workspace-added":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent."],"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Workspace"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"workspace-added"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"mutation_id":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"workspace-closed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent."],"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Workspace"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"workspace-closed"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"mutation_id":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"workspace-moved":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent."],"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Workspace"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"workspace-moved"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"mutation_id":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"workspace-renamed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent."],"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Workspace"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"workspace-renamed"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"mutation_id":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]}},"ir_sha256":"8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86","profiles":{"control":{"description":"Base authenticated session-control commands available to ordinary SDK clients.","inherits":[]},"frontend":{"description":"Rendering, input, presentation, subscribe, and attach commands.","inherits":["control"]},"local-admin":{"description":"Trusted local administration commands.","inherits":["control"],"transport":"Unix-classified transport, including direct Unix and the current stdio relay"},"provider-authority":{"description":"Provider-owned workspace mutation commands.","inherits":["control"],"requires_authority":true}},"protocol":{"id_type":"uint64","javascript_id_policy":"All protocol identifiers are uint64 JSON numbers. JavaScript and TypeScript SDKs must decode them losslessly as bigint (or validated decimal strings at their public boundary), and must not expose IEEE-754 number ids. Pairing request ids, revisions, timestamps, frame sequences, and reservation ids follow the same rule.","name":"cmux-tui-mux","version":12},"schema_version":2,"types":{"AgentRecord":{"additional_properties":false,"fields":{"session":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"source":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentSource"}},"state":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentState"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"updated_at_ms":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"AgentReportSource":{"kind":"enum","values":["socket","hook"]},"AgentSource":{"kind":"enum","values":["detected","socket","hook"]},"AgentState":{"kind":"enum","values":["working","blocked","idle","done","unknown"]},"AppliedPane":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"ApplyLayoutResult":{"additional_properties":false,"fields":{"panes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"AppliedPane"},"kind":"array"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"AttachedViewOutcomeResult":{"additional_properties":false,"fields":{"outcome":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ViewAttachmentOutcome"}}},"kind":"object"},"AttachedViewResizeResult":{"additional_properties":false,"fields":{"accepted":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"outcome":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ViewAttachmentOutcome"}},"reservation_id":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"Base64":{"kind":"alias","target":{"kind":"scalar","name":"string"}},"BrowserFrame":{"additional_properties":false,"fields":{"data":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Base64"}},"height":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"seq":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"width":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"BrowserProviderAuthentication":{"kind":"enum","values":["none","bearer"]},"BrowserProviderSnapshot":{"additional_properties":false,"constraints":["available is true exactly when provider_id, endpoint, authentication, and clients are present.","Provider bearer tokens are accepted only during registration and are never returned."],"fields":{"authentication":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"BrowserProviderAuthentication"}},"available":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"clients":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"endpoint":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"provider_id":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"targets":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"BrowserProviderTarget"},"kind":"array"}}},"kind":"object"},"BrowserProviderTarget":{"additional_properties":false,"fields":{"tab_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"target_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"BrowserProviderUnregisterResult":{"additional_properties":false,"fields":{"removed":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}}},"kind":"object"},"CellPixelFailure":{"additional_properties":false,"fields":{"error":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"CellPixelResize":{"additional_properties":false,"fields":{"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"reservation_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"CellPixelSurface":{"additional_properties":false,"fields":{"height_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"width_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"ClientInfo":{"additional_properties":false,"fields":{"attached":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Id"},"kind":"array"}},"client":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"connected_seconds":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"kind":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"name":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"self":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"sizes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"ClientSize"},"kind":"array"}},"transport":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ClientTransport"}}},"kind":"object"},"ClientSize":{"additional_properties":false,"fields":{"cols":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"rows":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"size_participating":{"nullable":false,"presence":"required","since":10,"type":{"kind":"scalar","name":"boolean"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"ClientTransport":{"kind":"enum","values":["local","unix","ws"]},"CloseTerminalResult":{"additional_properties":false,"fields":{"already_closed":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"closed":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ColorHex":{"kind":"alias","target":{"kind":"scalar","name":"string"}},"CopyResult":{"additional_properties":false,"fields":{"mode":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["screen","selection","scrollback"]}},"text":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"CursorStyle":{"kind":"enum","values":["block","underline","bar"]},"DeadPane":{"additional_properties":false,"fields":{"dead":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}},"id":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"DeclarativeLayout":{"kind":"tagged_union","tag":"type","variants":{"leaf":{"additional_properties":false,"fields":{"command":{"default":null,"nullable":true,"presence":"optional","type":{"items":{"kind":"scalar","name":"string"},"kind":"array","min_items":1}},"cwd":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"type":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"leaf"}}},"kind":"object"},"split":{"additional_properties":false,"fields":{"a":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"DeclarativeLayout"}},"b":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"DeclarativeLayout"}},"dir":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"SplitDirection"}},"ratio":{"constraints":[{"clamped_maximum":0.95,"clamped_minimum":0.05}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float32"}},"type":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"split"}}},"kind":"object"},"stack":{"additional_properties":false,"fields":{"expanded":{"constraints":["Must identify a member of panes."],"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"panes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Id"},"kind":"array","min_items":1}},"type":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"stack"}}},"kind":"object"}}},"EmptyResult":{"additional_properties":false,"fields":{},"kind":"object"},"ExportLayoutResult":{"additional_properties":false,"fields":{"layout":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Layout"}},"panes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"ExportedPane"},"kind":"array"}}},"kind":"object"},"ExportedPane":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"surfaces":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Id"},"kind":"array"}}},"kind":"object"},"FocusDirectionResult":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"FrontendFocusTarget":{"kind":"enum","values":["pane","machine_rail","workspace_rail","tabs_rail","projection_rail"]},"FrontendJournalEvent":{"kind":"tagged_union","tag":"kind","variants":{"focus":{"additional_properties":false,"fields":{"content_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"event_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"frontend_projection_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"focus"}},"pane_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"screen_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"tab_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"target":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"FrontendFocusTarget"}},"workspace_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"resize":{"additional_properties":false,"fields":{"cell_height":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"cell_width":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"event_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"frontend_projection_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"resize"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"viewport":{"additional_properties":false,"fields":{"event_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"frontend_projection_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"viewport"}},"offset":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"screen_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"settled":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"target":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"}}},"FrontendProjection":{"additional_properties":false,"fields":{"frontend":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"projection":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"JsonValue"}},"projection_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"replayed":{"default":false,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"schema_version":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"scope":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"subject_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"GetCellPixelsResult":{"additional_properties":false,"fields":{"height_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surfaces":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"CellPixelSurface"},"kind":"array"}},"width_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"Id":{"kind":"alias","target":{"kind":"scalar","name":"uint64"}},"IdMapping":{"additional_properties":false,"fields":{"id":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["workspace","screen","pane","surface"]}},"short_id":{"constraints":[{"pattern":"^[a-z0-9]{6}$"}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"IdentifyResult":{"additional_properties":false,"fields":{"app":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"cmux-tui"}},"build_commit":{"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"capabilities":{"default":[],"nullable":false,"presence":"optional","type":{"items":{"kind":"scalar","name":"string"},"kind":"array"}},"daemon_handoff":{"nullable":false,"presence":"required","since":9,"type":{"kind":"literal","value":1}},"generation":{"nullable":false,"presence":"required","since":7,"type":{"kind":"scalar","name":"string"}},"ghostty_commit":{"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"lifecycle_ready":{"default":true,"nullable":false,"presence":"optional","since":12,"type":{"kind":"scalar","name":"boolean"}},"pid":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"protocol":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"registry_id":{"nullable":false,"presence":"required","since":7,"type":{"kind":"scalar","name":"string"}},"session":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","since":9,"type":{"kind":"scalar","name":"uint64"}},"version":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace_revision":{"nullable":false,"presence":"required","since":7,"type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"IdsResult":{"additional_properties":false,"fields":{"ids":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"IdMapping"},"kind":"array"}}},"kind":"object"},"JsonValue":{"kind":"opaque_json","reason":"The wire field intentionally carries a frontend-authored or runtime-authored arbitrary JSON document."},"KittyGraphicsState":{"additional_properties":false,"fields":{"alternate_next_image_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"alternate_replay_next_image_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"image_bytes":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"images":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"inflight_bytes":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"placements":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"primary_next_image_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"primary_replay_next_image_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"replay_cursor_offset":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"KittyImageAlias":{"additional_properties":false,"fields":{"image_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"image_number":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"Layout":{"kind":"tagged_union","tag":"type","variants":{"leaf":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"type":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"leaf"}}},"kind":"object"},"split":{"additional_properties":false,"fields":{"a":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Layout"}},"b":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Layout"}},"dir":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"SplitDirection"}},"ratio":{"constraints":[{"maximum":0.95,"minimum":0.05}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float32"}},"split":{"description":"Stable for the lifetime of this split node.","nullable":false,"presence":"optional","since":8,"type":{"kind":"ref","name":"Id"}},"type":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"split"}}},"kind":"object"},"stack":{"additional_properties":false,"fields":{"expanded":{"constraints":["Must identify a member of panes."],"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"panes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Id"},"kind":"array","min_items":1}},"type":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"stack"}}},"kind":"object"}}},"LayoutUndoConfirmationRequired":{"additional_properties":false,"fields":{"closes_panes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Id"},"kind":"array"}},"confirmation_required":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}},"revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"undone":{"nullable":false,"presence":"required","type":{"kind":"literal","value":false}}},"kind":"object"},"LayoutUndoResult":{"kind":"untagged_union","variants":[{"kind":"ref","name":"LayoutUndoUndone"},{"kind":"ref","name":"LayoutUndoConfirmationRequired"}]},"LayoutUndoUndone":{"additional_properties":false,"fields":{"confirmation_required":{"default":false,"nullable":false,"presence":"optional","type":{"kind":"literal","value":false}},"revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"undone":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}}},"kind":"object"},"ListAgentsResult":{"additional_properties":false,"fields":{"agents":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"AgentRecord"},"kind":"array"}}},"kind":"object"},"ListTerminalsResult":{"additional_properties":false,"fields":{"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"terminals":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"TerminalRecord"},"kind":"array"}}},"kind":"object"},"LivePane":{"additional_properties":false,"fields":{"active_tab":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"focused_at":{"default":0,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"id":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"name":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"short_id":{"constraints":[{"pattern":"^[a-z0-9]{6}$"}],"nullable":false,"presence":"optional","since":6,"type":{"kind":"scalar","name":"string"}},"tabs":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Tab"},"kind":"array"}}},"kind":"object"},"MachineListeningTcpResult":{"additional_properties":false,"constraints":["The daemon runs only a fixed socket-listing command; callers cannot supply command text.","The output is limited to 524288 bytes."],"fields":{"stdout":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"MachineUsage":{"additional_properties":false,"constraints":["period_days is the trailing window length in days.","api_equivalent_usd is the list-price equivalent of the machine\'s model traffic in that window."],"fields":{"api_equivalent_usd":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"as_of":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"period_days":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"total_tokens":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"vm_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"MachineUsageResult":{"additional_properties":false,"constraints":["usage is null when the daemon has no readout (not a Cloud VM, endpoint unavailable, or usage not ready)."],"fields":{"usage":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"MachineUsage"}}},"kind":"object"},"MintTerminalRendererResult":{"additional_properties":false,"fields":{"endpoint":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"incarnation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"protocol_version":{"nullable":false,"presence":"required","since":11,"type":{"kind":"scalar","name":"uint16"}},"rights":{"constraints":[{"current_value":7}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"token":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"ttl_ms":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"MoveTerminalResult":{"additional_properties":false,"fields":{"changed":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"lifecycle":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalLifecycle"}},"pane":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"replayed":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"screen":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"workspace":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"NotificationLevel":{"kind":"enum","values":["info","warning","error"]},"NotificationMarker":{"additional_properties":false,"fields":{"level":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"NotificationLevel"}},"notification":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"unread":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}}},"kind":"object"},"NotifyResult":{"additional_properties":false,"fields":{"notification":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"Pane":{"kind":"untagged_union","variants":[{"kind":"ref","name":"LivePane"},{"kind":"ref","name":"DeadPane"}]},"PaneDirection":{"kind":"enum","values":["left","right","up","down"]},"PaneNeighborResult":{"additional_properties":false,"fields":{"pane":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"PingResult":{"additional_properties":false,"fields":{"build_commit":{"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"ghostty_commit":{"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"ok":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}},"protocol":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"version":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"ProcessInfoResult":{"additional_properties":false,"fields":{"command":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"cwd":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"foreground_cwd":{"description":"Working directory of the process group that owns the PTY, read at request time. Null when the lookup fails; absent from daemons that predate the field. Clients treat absence as null.","nullable":true,"presence":"optional","since":12,"type":{"kind":"scalar","name":"string"}},"pid":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"ProviderWorkspaceMutationResult":{"additional_properties":false,"fields":{"key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ReadScreenResult":{"additional_properties":false,"fields":{"text":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"ReadScrollbackResult":{"additional_properties":false,"fields":{"epoch":{"nullable":false,"presence":"required","since":10,"type":{"kind":"scalar","name":"uint64"}},"rows":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"RenderRow"},"kind":"array"}},"start":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"total":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"RenderCursor":{"additional_properties":false,"fields":{"blink":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"color":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"style":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"CursorStyle"}},"visible":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"x":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"y":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"RenderGraphicFormat":{"kind":"enum","values":["rgb","rgba"]},"RenderGraphicImage":{"additional_properties":false,"fields":{"data":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Base64"}},"format":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"RenderGraphicFormat"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"height":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"width":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"RenderGraphicPlacement":{"additional_properties":false,"fields":{"anchor_col":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"anchor_row":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint32"}},"columns":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"grid_cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"grid_rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"image_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"ordinal":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"pixel_height":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"pixel_width":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"placement_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"source_height":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"source_width":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"source_x":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"source_y":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"viewport_col":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"int32"}},"viewport_row":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"int32"}},"viewport_visible":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"x_offset":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"y_offset":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"z":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"int32"}}},"kind":"object"},"RenderGraphics":{"additional_properties":false,"fields":{"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"images":{"nullable":false,"presence":"optional","type":{"items":{"kind":"ref","name":"RenderGraphicImage"},"kind":"array"}},"placements":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"RenderGraphicPlacement"},"kind":"array"}},"removed_image_ids":{"nullable":false,"presence":"optional","type":{"items":{"kind":"scalar","name":"uint32"},"kind":"array"}}},"kind":"object"},"RenderGraphicsDelta":{"additional_properties":false,"fields":{"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"images":{"nullable":false,"presence":"optional","type":{"items":{"kind":"ref","name":"RenderGraphicImage"},"kind":"array"}},"placements":{"nullable":false,"presence":"optional","type":{"items":{"kind":"ref","name":"RenderGraphicPlacement"},"kind":"array"}},"removed_image_ids":{"nullable":false,"presence":"optional","type":{"items":{"kind":"scalar","name":"uint32"},"kind":"array"}}},"kind":"object"},"RenderRow":{"additional_properties":false,"fields":{"row":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"runs":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"RenderRun"},"kind":"array"}}},"kind":"object"},"RenderRun":{"additional_properties":false,"fields":{"attrs":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"bg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"fg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"text":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"underline":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"RenderUnderline"}},"width_hint":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"RenderUnderline":{"kind":"enum","values":["single","double","curly","dotted","dashed"]},"ReportAgentResult":{"additional_properties":false,"fields":{"session":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"source":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentReportSource"}},"state":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentState"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"ResizeSurfaceResult":{"additional_properties":false,"fields":{"accepted":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"reservation_id":{"nullable":true,"presence":"required","since":7,"type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ResolveTerminalResult":{"additional_properties":false,"fields":{"exit":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"TerminalExit"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"launch_spec":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"JsonValue"}},"lifecycle":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalLifecycle"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"workspace_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"ResourceSelectors":{"additional_properties":false,"fields":{"agent":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"browser":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"client":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"frontend_projection":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"machine":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"notification":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"pairing_request":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"screen":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"session":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"sidebar_view":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"split":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"stream":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"tab":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"terminal":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"RunResult":{"additional_properties":false,"fields":{"already_exited":{"nullable":false,"presence":"required","since":11,"type":{"kind":"scalar","name":"boolean"}},"exit":{"nullable":true,"presence":"required","since":11,"type":{"kind":"ref","name":"TerminalExit"}},"lifecycle":{"nullable":false,"presence":"required","since":11,"type":{"kind":"ref","name":"TerminalLifecycle"}},"pane":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"screen":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"terminal_id":{"nullable":false,"presence":"required","since":9,"type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"required","since":9,"type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","since":11,"type":{"kind":"scalar","name":"uint64"}},"workspace":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"Screen":{"additional_properties":false,"fields":{"active":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"active_pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"id":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"layout":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Layout"}},"name":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"panes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Pane"},"kind":"array"}},"short_id":{"constraints":[{"pattern":"^[a-z0-9]{6}$"}],"nullable":false,"presence":"optional","since":6,"type":{"kind":"scalar","name":"string"}},"zoomed_pane":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"ServerStatsConnections":{"additional_properties":false,"constraints":["refused counts sockets dropped at limit; for hook producers each one is a lost event."],"fields":{"accepted":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"active":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"limit":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"peak":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"refused":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ServerStatsHistogram":{"additional_properties":false,"constraints":["Percentiles are log-linear bucket upper bounds and overestimate the true sample by at most 25%.","Latency histograms are in microseconds; batch_size counts events."],"fields":{"count":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"max":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"mean":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"p50":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"p90":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"p99":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ServerStatsJournalWriter":{"additional_properties":false,"constraints":["commit_us excludes lock wait; commit_lock_wait_us is the writer waiting for the registry lock.","terminal_queued and durable_queued are live lane depths."],"fields":{"batch_size":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsHistogram"}},"batches":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"commit_failures":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"commit_lock_wait_us":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsHistogram"}},"commit_us":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsHistogram"}},"deadline_expiries":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"durable_events":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"durable_queued":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"phase":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsWriterPhase"}},"phase_for_us":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"receipt_wait_us":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsHistogram"}},"terminal_events":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"terminal_queued":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ServerStatsLockHolder":{"additional_properties":false,"constraints":["site is the file:line that acquired the registry lock."],"fields":{"held_for_us":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"site":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"ServerStatsLockSite":{"additional_properties":false,"constraints":[],"fields":{"acquisitions":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"hold_max_us":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"hold_total_us":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"site":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"ServerStatsLockStall":{"additional_properties":false,"constraints":["blocker is the site holding the lock when the waiter\'s wait began, or null when it was free."],"fields":{"blocker":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"waited_us":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"waiter":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"ServerStatsRegistryLock":{"additional_properties":false,"constraints":["contended_acquisitions counts waits of at least 1 ms; stalls counts waits of at least 100 ms.","top_sites is ordered by hold_total_us descending and holds at most eight entries."],"fields":{"contended_acquisitions":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"hold_us":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsHistogram"}},"holder":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ServerStatsLockHolder"}},"last_stall":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ServerStatsLockStall"}},"stalls":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"top_sites":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"ServerStatsLockSite"},"kind":"array"}},"wait_us":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsHistogram"}}},"kind":"object"},"ServerStatsResult":{"additional_properties":false,"constraints":["schema is 1.","journal_writer is null for ephemeral sessions without a durable journal.","Counters accumulate since daemon start; reading them never touches SQLite or the journal."],"fields":{"connections":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsConnections"}},"journal_writer":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ServerStatsJournalWriter"}},"registry_lock":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsRegistryLock"}},"schema":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"uptime_ms":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ServerStatsWriterPhase":{"kind":"enum","values":["idle","waiting_lock","committing"]},"SetCellPixelsResult":{"additional_properties":false,"fields":{"failures":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"CellPixelFailure"},"kind":"array"}},"resizes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"CellPixelResize"},"kind":"array"}}},"kind":"object"},"ShutdownDaemonResult":{"additional_properties":false,"fields":{"accepted":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"pid":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"SidebarPluginResult":{"additional_properties":false,"fields":{"error":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"retry_after_ms":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"Size":{"additional_properties":false,"fields":{"cols":{"constraints":[{"maximum":10000,"minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"rows":{"constraints":[{"maximum":10000,"minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"SplitDirection":{"kind":"enum","values":["right","down"]},"SurfaceResult":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"terminal_id":{"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"string"}}},"kind":"object"},"Tab":{"additional_properties":false,"fields":{"browser_error":{"nullable":true,"presence":"optional","since":6,"type":{"kind":"scalar","name":"string"}},"browser_frames_stalled":{"nullable":true,"presence":"optional","since":6,"type":{"kind":"scalar","name":"boolean"}},"browser_source":{"nullable":true,"presence":"required","type":{"kind":"enum","values":["external","launched"]}},"browser_status":{"nullable":true,"presence":"optional","since":6,"type":{"kind":"enum","values":["starting","live","failed"]}},"dead":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["pty","browser"]}},"name":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"notification":{"nullable":true,"presence":"optional","since":6,"type":{"kind":"ref","name":"NotificationMarker"}},"short_id":{"constraints":[{"pattern":"^[a-z0-9]{6}$"}],"nullable":false,"presence":"optional","since":6,"type":{"kind":"scalar","name":"string"}},"size":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Size"}},"supports_clear_history_key_fallback":{"capability":"clear-history-key-v1","nullable":false,"presence":"optional","since":9,"type":{"kind":"scalar","name":"boolean"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"terminal_id":{"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"string"}},"terminal_resource_id":{"constraints":[{"pattern":"^term_[0-9a-f]{32}$"}],"nullable":true,"presence":"optional","since":10,"type":{"kind":"scalar","name":"string"}},"title":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"TerminalColors":{"additional_properties":false,"fields":{"bg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"cursor":{"nullable":true,"presence":"optional","since":6,"type":{"kind":"ref","name":"ColorHex"}},"cursor_blink":{"nullable":true,"presence":"optional","since":6,"type":{"kind":"scalar","name":"boolean"}},"cursor_style":{"nullable":true,"presence":"optional","since":6,"type":{"kind":"ref","name":"CursorStyle"}},"fg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"palette":{"constraints":["Decimal string keys are palette indexes 0 through 255."],"nullable":false,"presence":"optional","since":7,"type":{"kind":"map","values":{"kind":"ref","name":"ColorHex"}}},"selection_bg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"selection_fg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}}},"kind":"object"},"TerminalEventsResult":{"additional_properties":false,"fields":{"events":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"TerminalRegistryEvent"},"kind":"array"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"TerminalExit":{"additional_properties":false,"fields":{"exited_at_ms":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"outcome":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalExitOutcome"}}},"kind":"object"},"TerminalExitOutcome":{"kind":"tagged_union","tag":"kind","variants":{"exit":{"additional_properties":false,"fields":{"code":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"int32"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"exit"}}},"kind":"object"},"signal":{"additional_properties":false,"fields":{"core_dumped":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"signal"}},"signal":{"constraints":[{"minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"int32"}}},"kind":"object"},"unknown":{"additional_properties":false,"fields":{"kind":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"unknown"}},"reason":{"constraints":[{"min_length":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"}}},"TerminalKey":{"kind":"enum","values":["unidentified","backquote","backslash","bracket-left","bracket-right","comma","digit0","digit1","digit2","digit3","digit4","digit5","digit6","digit7","digit8","digit9","equal","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","minus","period","quote","semicolon","slash","backspace","enter","space","tab","delete","end","home","insert","page-down","page-up","arrow-down","arrow-left","arrow-right","arrow-up","numpad0","numpad1","numpad2","numpad3","numpad4","numpad5","numpad6","numpad7","numpad8","numpad9","numpad-add","numpad-backspace","numpad-comma","numpad-decimal","numpad-divide","numpad-enter","numpad-equal","numpad-multiply","numpad-subtract","numpad-up","numpad-down","numpad-right","numpad-left","numpad-begin","numpad-home","numpad-end","numpad-insert","numpad-delete","numpad-page-up","numpad-page-down","escape","f1","f2","f3","f4","f5","f6","f7","f8","f9","f10","f11","f12","f13","f14","f15","f16","f17","f18","f19","f20"]},"TerminalKeyAction":{"kind":"enum","values":["press","release","repeat"]},"TerminalKeyInput":{"additional_properties":false,"constraints":["consumed_mods must be a subset of mods.","unshifted_codepoint, shifted_codepoint, and base_layout_codepoint contain exactly one Unicode scalar when present.","utf8 contains no control characters.","macos_option_as_alt may be false only when Alt is active and consumed."],"fields":{"action":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"TerminalKeyAction"}},"base_layout_codepoint":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"composing":{"default":false,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"consumed_mods":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalModifiers"}},"key":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalKey"}},"macos_option_as_alt":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"mods":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalModifiers"}},"shifted_codepoint":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"unshifted_codepoint":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"utf8":{"constraints":[{"max_length":4096}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"TerminalLifecycle":{"kind":"enum","values":["launching","adopting","running","exited","tombstoned"]},"TerminalModifiers":{"additional_properties":false,"fields":{"alt":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"caps_lock":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"control":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"num_lock":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"shift":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"super":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}}},"kind":"object"},"TerminalPlacement":{"additional_properties":false,"fields":{"already_exited":{"nullable":false,"presence":"required","since":11,"type":{"kind":"scalar","name":"boolean"}},"exit":{"nullable":true,"presence":"required","since":11,"type":{"kind":"ref","name":"TerminalExit"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"lifecycle":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalLifecycle"}},"pane":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"replayed":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"screen":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"workspace":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"TerminalRecord":{"additional_properties":false,"fields":{"exit":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"TerminalExit"}},"launch_spec":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"JsonValue"}},"lifecycle":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalLifecycle"}},"terminal_id":{"constraints":[{"format":"UUIDv4 hex without dashes","pattern":"^[0-9a-f]{32}$"}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"TerminalRegistryEvent":{"additional_properties":false,"fields":{"kind":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"mutation_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"origin":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"result":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"JsonValue"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"workspace_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"Tree":{"additional_properties":false,"fields":{"generation":{"capability":"workspace-registry-v1","nullable":false,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"pane_revision":{"nullable":false,"presence":"optional","since":9,"type":{"kind":"scalar","name":"uint64"}},"registry_id":{"capability":"workspace-registry-v1","nullable":false,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"optional","since":9,"type":{"kind":"scalar","name":"uint64"}},"workspace_revision":{"capability":"workspace-registry-v1","nullable":false,"presence":"optional","since":7,"type":{"kind":"scalar","name":"uint64"}},"workspaces":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Workspace"},"kind":"array"}}},"kind":"object"},"ViewAttachmentOutcome":{"kind":"enum","values":["applied","passive","superseded"]},"VtStateResult":{"additional_properties":false,"fields":{"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"data":{"constraints":[{"encoding":"standard base64"}],"nullable":false,"presence":"required","type":{"kind":"ref","name":"Base64"}},"kitty_graphics_state":{"nullable":false,"presence":"optional","since":10,"type":{"kind":"ref","name":"KittyGraphicsState"}},"kitty_image_aliases":{"nullable":false,"presence":"optional","since":9,"type":{"items":{"kind":"ref","name":"KittyImageAlias"},"kind":"array"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"WaitForResult":{"additional_properties":false,"fields":{"elapsed_ms":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"matched":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}},"text":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"Workspace":{"additional_properties":false,"fields":{"active":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"id":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"key":{"capability":"workspace-registry-v1","constraints":[{"format":"lowercase canonical UUID"}],"nullable":false,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"name":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"screens":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Screen"},"kind":"array"}},"short_id":{"constraints":[{"pattern":"^[a-z0-9]{6}$"}],"nullable":false,"presence":"optional","since":6,"type":{"kind":"scalar","name":"string"}}},"kind":"object"},"WorkspaceMutationResult":{"additional_properties":false,"fields":{"changed":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"replayed":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ZoomPaneResult":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"zoomed":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"zoomed_pane":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"}}}') +SCHEMA = json.loads('{"$schema":"./sdk-schema.schema.json","commands":{"apply-layout":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["layout must contain at least one leaf or stack member.","cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"layout":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"DeclarativeLayout"}},"name":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ApplyLayoutResult"},"since":6,"stream":null},"attach-surface":{"authority":"frontend","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["cols and rows must be supplied together.","Browser surfaces reject mode:render."],"fields":{"cols":{"capability":"attach-initial-size","default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"mode":{"default":"bytes","nullable":true,"presence":"optional","since":7,"type":{"kind":"enum","values":["bytes","render"]}},"rows":{"capability":"attach-initial-size","default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":{"event_names":["browser-state","colors-changed","detached","frame","notification","output","overflow","render-delta","render-state","resized","scroll-changed","vt-state"],"kind":"attach","mode_field":"mode","modes":{"browser":["browser-state","frame","notification","scroll-changed","overflow","detached"],"bytes":["vt-state","output","resized","colors-changed","scroll-changed","notification","overflow","detached"],"render":["render-state","render-delta","scroll-changed","overflow","detached"]},"ordering":"Initial state precedes the command response. Later surface events preserve order. A resized replay replaces the previous byte mirror; overflow ends that surface stream.","terminal_event":"detached"}},"browser-activate":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-back":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only; queue acknowledgement is not page-load success."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-forward":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only; queue acknowledgement is not page-load success."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-frame-presented":{"authority":"frontend","capability":"browser-pointer-frame-guard-v1","constraints":["Acknowledges the exact rendered browser frame for this connection.","Requires browser-pointer-frame-guard-v1."],"request":{"additional_properties":false,"fields":{"frame_seq":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":10,"stream":null},"browser-insert-text":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only.","The bounded disposable input queue drops newest input when full."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"text":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-key":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only.","The bounded disposable input queue drops newest input when full."],"request":{"additional_properties":false,"fields":{"code":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["down","up"]}},"modifiers":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"text":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"windows_virtual_key_code":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-key-press":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only.","One request preserves the atomic press sequence through the bounded input queue."],"request":{"additional_properties":false,"fields":{"code":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"modifiers":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"text":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"windows_virtual_key_code":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":10,"stream":null},"browser-mouse":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only; coordinates are CSS pixels.","The bounded disposable input queue drops newest input when full."],"request":{"additional_properties":false,"fields":{"button":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"click_count":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint32"}},"frame_seq":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["down","up","move"]}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"x_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"y_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-mouse-guarded":{"authority":"frontend","capability":"browser-pointer-frame-guard-v1","constraints":["Browser surfaces only; coordinates are CSS pixels.","The frame sequence must be the exact presented token for this connection.","Requires browser-pointer-frame-guard-v1."],"request":{"additional_properties":false,"fields":{"button":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"click_count":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint32"}},"frame_seq":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["down","up","move"]}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"x_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"y_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":10,"stream":null},"browser-navigate":{"authority":"frontend","capability":null,"constraints":["Queue acknowledgement only; observe browser-state for outcome.","Navigation is latest-wins."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"url":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-reload":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only; queue acknowledgement is not page-load success."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-wheel":{"authority":"frontend","capability":null,"constraints":["Browser surfaces only; values are CSS pixels.","The bounded disposable input queue drops newest input when full."],"request":{"additional_properties":false,"fields":{"delta_y_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"frame_seq":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"x_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"y_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"browser-wheel-guarded":{"authority":"frontend","capability":"browser-pointer-frame-guard-v1","constraints":["Browser surfaces only; values are CSS pixels.","The frame sequence must be the exact presented token for this connection.","Requires browser-pointer-frame-guard-v1."],"request":{"additional_properties":false,"fields":{"delta_y_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"frame_seq":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"x_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"y_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":10,"stream":null},"clear-history":{"authority":"control","capability":"clear-history-v1","constraints":["PTY surfaces only.","Failed responses classify error_delivery as known-not-delivered or ambiguous."],"request":{"additional_properties":false,"fields":{"fallback_key":{"capability":"clear-history-key-v1","default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"ref","name":"TerminalKeyInput"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":9,"stream":null},"clear-window-title":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"client-focus":{"authority":"control","capability":"client-focus-v1","constraints":[],"request":{"additional_properties":false,"fields":{"client_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"additional_properties":false,"fields":{"pane":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"tab":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":12,"stream":null},"close-pane":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"close-provider-managed-workspace":{"authority":"provider-authority","capability":"provider-managed-workspace-authority-v2","constraints":["Call only after the external provider durably accepts the close."],"request":{"additional_properties":false,"constraints":["workspace and key must identify the same live provider-managed workspace."],"fields":{"authority":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ProviderWorkspaceMutationResult"},"since":9,"stream":null},"close-screen":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"close-surface":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"close-terminal":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent."],"fields":{"expected_generation":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"CloseTerminalResult"},"since":9,"stream":null},"close-workspace":{"authority":"control","capability":null,"constraints":["Provider-managed workspaces reject this ordinary mutation."],"request":{"additional_properties":false,"constraints":["At least one of workspace and key must be supplied; both must identify the same workspace when supplied.","origin and mutation_id are either both present or both absent."],"fields":{"expected_generation":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"uint64"}},"key":{"capability":"workspace-registry-v1","default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"WorkspaceMutationResult"},"since":5,"stream":null},"copy":{"authority":"control","capability":null,"constraints":["PTY surfaces only."],"request":{"additional_properties":false,"fields":{"mode":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["screen","selection","scrollback"]}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"CopyResult"},"since":6,"stream":null},"create-surface-with-receipt":{"authority":"control","capability":"creation-receipts-v1","constraints":["Repeating one origin and receipt with identical fields returns the original creation result.","A new idempotency_key is valid only when durable creation resolution instructs retry_new_idempotency_key."],"request":{"additional_properties":false,"constraints":["operation is one of new-tab, run-command, new-browser-tab, new-workspace, new-screen, new-pane, new-pane-right, split-right, or split-down.","Each operation admits only its documented selector and option fields.","idempotency_key names one execution attempt and defaults to receipt.","cols and rows must be supplied together."],"fields":{"argv":{"default":null,"nullable":true,"presence":"optional","type":{"items":{"kind":"scalar","name":"string"},"kind":"array"}},"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"cwd":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"idempotency_key":{"capability":"creation-attempt-keys-v1","default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"operation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"origin":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}},"receipt":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"selector_fallbacks":{"default":[],"nullable":false,"presence":"optional","type":{"items":{"kind":"ref","name":"ResourceSelectors"},"kind":"array","max_items":7}},"selectors":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"ResourceSelectors"}},"url":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"width":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"float32"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"JsonValue"},"since":10,"stream":null},"create-terminal":{"authority":"control","capability":"workspace-registry-v1","constraints":[],"request":{"additional_properties":false,"constraints":["At least one of workspace and key must be supplied; when both are supplied they must identify the same workspace.","argv and command are mutually exclusive and must be nonempty when supplied.","cols and rows must be supplied together.","origin and mutation_id are either both present or both absent.","terminal_id may be supplied only when origin and mutation_id are both present."],"fields":{"argv":{"default":null,"nullable":true,"presence":"optional","type":{"items":{"kind":"scalar","name":"string"},"kind":"array","min_items":1}},"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"command":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"cwd":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"expected_generation":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"key":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"name":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"terminal_id":{"constraints":[{"format":"32-character lowercase UUIDv4 hex without dashes","pattern":"^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$"}],"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"string"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"TerminalPlacement"},"since":7,"stream":null},"create-workspace":{"authority":"control","capability":"workspace-registry-v1","constraints":[],"request":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent.","At most 4096 live workspaces may exist; tombstoned keys cannot be reused."],"fields":{"expected_generation":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"key":{"constraints":[{"format":"lowercase canonical UUID"}],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"name":{"constraints":[{"max_utf8_bytes":1024}],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"WorkspaceMutationResult"},"since":7,"stream":null},"detach-attached-view":{"authority":"frontend","capability":"view-attachment-detach-v1","constraints":["The command closes only the named view stream and releases its size contribution.","A retired lease returns outcome:superseded."],"request":{"additional_properties":false,"fields":{"lease":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"AttachedViewOutcomeResult"},"since":10,"stream":null},"detach-client":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"client":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"export-layout":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"screen":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ExportLayoutResult"},"since":6,"stream":null},"focus-direction":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"dir":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"PaneDirection"}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"FocusDirectionResult"},"since":6,"stream":null},"focus-pane":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"get-browser-provider":{"authority":"local-admin","capability":"browser-provider-v1","constraints":["Provider endpoints and targets are disclosed only over a trusted local transport; bearer credentials are accepted only during registration and are never returned.","Automation must select a target by stable tab id instead of treating CDP discovery as topology authority."],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"BrowserProviderSnapshot"},"since":10,"stream":null},"get-cell-pixels":{"authority":"frontend","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"GetCellPixelsResult"},"since":6,"stream":null},"get-frontend-projection":{"authority":"control","capability":null,"constraints":["Each identifier is nonempty, contains no control character, and is at most 128 bytes."],"request":{"additional_properties":false,"fields":{"frontend":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"scope":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"subject_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"FrontendProjection"},"since":7,"stream":null},"identify":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"IdentifyResult"},"since":5,"stream":null},"ids":{"authority":"control","capability":null,"constraints":["Short ids are snapshot-local labels; command parameters accept numeric ids only."],"request":{"additional_properties":false,"fields":{"kind":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"enum","values":["workspace","screen","pane","surface"]}}},"kind":"object"},"result":{"kind":"ref","name":"IdsResult"},"since":6,"stream":null},"journal-frontend-event":{"authority":"control","capability":"frontend-journal-v1","constraints":["The server derives producer identity from the authenticated control client."],"request":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"FrontendJournalEvent"}}},"kind":"object"},"result":{"additional_properties":false,"fields":{"committed":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}}},"kind":"object"},"since":10,"stream":null},"list-agents":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"state":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"AgentState"}},"surface":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ListAgentsResult"},"since":6,"stream":null},"list-clients":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"items":{"kind":"ref","name":"ClientInfo"},"kind":"array"},"since":6,"stream":null},"list-terminals":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"ListTerminalsResult"},"since":9,"stream":null},"list-workspaces":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"Tree"},"since":5,"stream":null},"machine-listening-tcp":{"authority":"control","capability":"machine-listening-tcp-v1","constraints":["Routine Cloud port inventory uses this command over the authenticated private cmux-tui link."],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"MachineListeningTcpResult"},"since":12,"stream":null},"machine-usage":{"authority":"control","capability":"machine-usage-v1","constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"MachineUsageResult"},"since":12,"stream":null},"mark-workspaces-provider-managed":{"authority":"provider-authority","capability":"provider-managed-workspace-authority-v2","constraints":["Authority must match the value provisioned before this mux generation accepted control clients."],"request":{"additional_properties":false,"fields":{"authority":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":9,"stream":null},"mint-terminal-renderer":{"authority":"frontend","capability":null,"constraints":["Only terminal-host-backed PTYs can mint one-use renderer credentials."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"ttl_ms":{"constraints":[{"maximum":60000,"minimum":1}],"default":30000,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"MintTerminalRendererResult"},"since":9,"stream":null},"mint-terminal-renderer-by-terminal":{"authority":"frontend","capability":null,"constraints":["The terminal resource ID is resolved atomically to the live terminal-host-backed PTY before minting a one-use renderer credential."],"request":{"additional_properties":false,"fields":{"terminal":{"constraints":[{"pattern":"^term_[0-9a-f]{32}$"}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"ttl_ms":{"constraints":[{"maximum":60000,"minimum":1}],"default":30000,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"MintTerminalRendererResult"},"since":11,"stream":null},"move-tab":{"authority":"control","capability":null,"constraints":["An out-of-range index clamps to the destination end."],"request":{"additional_properties":false,"fields":{"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"move-terminal":{"authority":"control","capability":null,"constraints":["A move to the current workspace still commits a terminal revision with changed:false."],"request":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent."],"fields":{"expected_generation":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"workspace_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"MoveTerminalResult"},"since":9,"stream":null},"move-workspace":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["At least one of workspace and key must be supplied; both must identify the same workspace when supplied.","origin and mutation_id are either both present or both absent."],"fields":{"expected_generation":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"uint64"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"key":{"capability":"workspace-registry-v1","default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"WorkspaceMutationResult"},"since":5,"stream":null},"new-browser-tab":{"authority":"control","capability":null,"constraints":["Bootstrap and navigation failures are asynchronous browser-state outcomes."],"request":{"additional_properties":false,"constraints":["cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"url":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"SurfaceResult"},"since":5,"stream":null},"new-pane":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"result":{"kind":"ref","name":"SurfaceResult"},"since":9,"stream":null},"new-pane-right":{"authority":"control","capability":"viewport-splits-v1","constraints":[],"request":{"additional_properties":false,"constraints":["Omitted width defaults to two thirds.","cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"width":{"constraints":[{"maximum":1.0,"minimum":0.1}],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"float32"}}},"kind":"object"},"result":{"kind":"ref","name":"SurfaceResult"},"since":9,"stream":null},"new-screen":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"SurfaceResult"},"since":5,"stream":null},"new-tab":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"cwd":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"result":{"kind":"ref","name":"SurfaceResult"},"since":5,"stream":null},"new-workspace":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"name":{"constraints":[{"max_utf8_bytes":1024}],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"result":{"kind":"ref","name":"SurfaceResult"},"since":5,"stream":null},"notify":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"body":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"level":{"default":"info","nullable":true,"presence":"optional","type":{"kind":"ref","name":"NotificationLevel"}},"surface":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}},"title":{"constraints":[{"min_length":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"NotifyResult"},"since":6,"stream":null},"pairing-response":{"authority":"local-admin","capability":null,"constraints":["The request id must identify a live, unexpired pairing challenge."],"request":{"additional_properties":false,"fields":{"approve":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"request":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":7,"stream":null},"pane-neighbor":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"dir":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"PaneDirection"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"PaneNeighborResult"},"since":6,"stream":null},"ping":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"PingResult"},"since":6,"stream":null},"presence-clear":{"authority":"control","capability":"presence-v1","constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":12,"stream":null},"presence-list":{"authority":"control","capability":"presence-v1","constraints":["Pointers idle for 60 seconds are dropped unless their highlight mode is pin."],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"PresenceListResult"},"since":12,"stream":null},"presence-update":{"authority":"control","capability":"presence-v1","constraints":["Replaces this connection\'s whole presence state; omitted pointer or highlight means none.","At most 240 updates per second per connection; more fail with a bad request error.","Never journaled; a server restart forgets all presence."],"request":{"additional_properties":false,"fields":{"highlight":{"nullable":true,"presence":"optional","type":{"kind":"ref","name":"PresenceHighlight"}},"pointer":{"nullable":true,"presence":"optional","type":{"kind":"ref","name":"PresenceAnchor"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":12,"stream":null},"process-info":{"authority":"control","capability":null,"constraints":["PTY surfaces only."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ProcessInfoResult"},"since":6,"stream":null},"put-frontend-projection":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent.","Serialized projection must be at most 1048576 bytes."],"fields":{"expected_generation":{"default":null,"description":"Accepted by the current decoder but ignored for projection writes.","nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"expected_projection_revision":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"description":"Accepted by the current decoder but ignored for projection writes.","nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"frontend":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"projection":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"JsonValue"}},"schema_version":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"scope":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"subject_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"FrontendProjection"},"since":7,"stream":null},"read-screen":{"authority":"control","capability":null,"constraints":["PTY surfaces only."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ReadScreenResult"},"since":5,"stream":null},"read-scrollback":{"authority":"control","capability":null,"constraints":["PTY surfaces only; row indexes are snapshot-relative and not durable."],"request":{"additional_properties":false,"fields":{"count":{"constraints":[{"maximum":65535,"minimum":0}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"start":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ReadScrollbackResult"},"since":7,"stream":null},"register-browser-provider":{"authority":"local-admin","capability":"browser-provider-v1","constraints":["The lease is scoped to the trusted local control connection and is released on disconnect.","The endpoint must be an explicit loopback ws URL with no credentials or fragment.","Bearer authentication is optional and sends the token only in the CDP WebSocket upgrade Authorization header.","Each registration replaces that connection\'s complete target set; target ids are never journaled."],"request":{"additional_properties":false,"fields":{"authentication":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"BrowserProviderAuthentication"}},"bearer_token":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"endpoint":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"provider_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"targets":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"BrowserProviderTarget"},"kind":"array"}}},"kind":"object"},"result":{"kind":"ref","name":"BrowserProviderSnapshot"},"since":10,"stream":null},"release-attached-view-size":{"authority":"frontend","capability":"view-attachment-lease-v1","constraints":["The attach stream remains live for cached rendering."],"request":{"additional_properties":false,"fields":{"lease":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"AttachedViewOutcomeResult"},"since":10,"stream":null},"release-surface-size":{"authority":"control","capability":null,"constraints":["An absent lease is a successful no-op."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":7,"stream":null},"reload-config":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"additional_properties":false,"fields":{"path":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"reloaded":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}}},"kind":"object"},"since":6,"stream":null},"rename-pane":{"authority":"control","capability":null,"constraints":["An empty name clears the pane name."],"request":{"additional_properties":false,"fields":{"name":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"rename-provider-managed-workspace":{"authority":"provider-authority","capability":"provider-managed-workspace-authority-v2","constraints":["Call only after the external provider durably accepts the rename."],"request":{"additional_properties":false,"constraints":["workspace and key must identify the same live provider-managed workspace."],"fields":{"authority":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"name":{"constraints":[{"max_utf8_bytes":1024}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ProviderWorkspaceMutationResult"},"since":9,"stream":null},"rename-screen":{"authority":"control","capability":null,"constraints":["An empty name clears the screen name."],"request":{"additional_properties":false,"fields":{"name":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"rename-surface":{"authority":"control","capability":null,"constraints":["An empty name clears the surface name."],"request":{"additional_properties":false,"fields":{"name":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"rename-workspace":{"authority":"control","capability":null,"constraints":["Provider-managed workspaces reject this ordinary mutation."],"request":{"additional_properties":false,"constraints":["At least one of workspace and key must be supplied; both must identify the same workspace when supplied.","origin and mutation_id are either both present or both absent."],"fields":{"expected_generation":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"expected_revision":{"aliases":["expected_terminal_revision"],"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"uint64"}},"key":{"capability":"workspace-registry-v1","default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"mutation_id":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"name":{"constraints":[{"max_utf8_bytes":1024}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"origin":{"default":null,"nullable":true,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"WorkspaceMutationResult"},"since":5,"stream":null},"report-agent":{"authority":"control","capability":null,"constraints":["A stored hook report outranks later socket reports until another hook report or surface close."],"request":{"additional_properties":false,"fields":{"session":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"source":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentReportSource"}},"state":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentState"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ReportAgentResult"},"since":6,"stream":null},"report-focus":{"authority":"control","capability":"client-focus-v1","constraints":[],"request":{"additional_properties":false,"fields":{"client_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"tab":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":12,"stream":null},"resize-attached-view":{"authority":"frontend","capability":"view-attachment-lease-v1","constraints":["The lease must belong to this connection and surface.","A retired lease returns outcome:superseded without changing replacement views."],"request":{"additional_properties":false,"fields":{"cols":{"constraints":[{"clamped_maximum":10000,"clamped_minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"lease":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"rows":{"constraints":[{"clamped_maximum":10000,"clamped_minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"AttachedViewResizeResult"},"since":10,"stream":null},"resize-surface":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"cols":{"constraints":[{"clamped_maximum":10000,"clamped_minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"rows":{"constraints":[{"clamped_maximum":10000,"clamped_minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ResizeSurfaceResult"},"since":5,"stream":null},"resolve-terminal":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"terminal_id":{"constraints":[{"format":"UUIDv4 hex without dashes","pattern":"^[0-9a-f]{32}$"}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"ResolveTerminalResult"},"since":9,"stream":null},"run":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["Exactly one of argv and command must be supplied.","pane and new_workspace:true are mutually exclusive.","key is valid only with new_workspace:true.","cols and rows affect sizing only when both are present."],"fields":{"argv":{"default":null,"nullable":true,"presence":"optional","type":{"items":{"kind":"scalar","name":"string"},"kind":"array","min_items":1}},"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"command":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"cwd":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"key":{"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"string"}},"name":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"new_workspace":{"default":false,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"result":{"kind":"ref","name":"RunResult"},"since":6,"stream":null},"scroll-surface":{"authority":"control","capability":null,"constraints":["PTY surfaces only; negative values scroll up."],"request":{"additional_properties":false,"fields":{"delta":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"int64"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"select-screen":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["When both index and delta are supplied, index wins."],"fields":{"delta":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"int64"}},"index":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"select-tab":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["When both index and delta are supplied, index wins."],"fields":{"delta":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"int64"}},"index":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"select-workspace":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["When both index and delta are supplied, index wins."],"fields":{"delta":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"int64"}},"index":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"send":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["When both are present, UTF-8 text bytes precede decoded bytes."],"fields":{"bytes":{"constraints":[{"encoding":"standard base64"}],"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Base64"}},"paste":{"default":false,"nullable":false,"presence":"optional","since":7,"type":{"kind":"scalar","name":"boolean"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"text":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"send-key":{"authority":"control","capability":null,"constraints":["PTY surfaces only."],"request":{"additional_properties":false,"fields":{"keys":{"constraints":[{"syntax":"lowercase modifier+key chords"}],"nullable":false,"presence":"required","type":{"items":{"kind":"scalar","name":"string"},"kind":"array","min_items":1}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"server-stats":{"authority":"local-admin","capability":"server-stats-v1","constraints":["Owner-only diagnostics; never journaled and safe to poll."],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"ServerStatsResult"},"since":12,"stream":null},"set-cell-pixels":{"authority":"frontend","capability":null,"constraints":["Accepted browser resizes complete asynchronously."],"request":{"additional_properties":false,"fields":{"height_px":{"constraints":[{"clamped_minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"width_px":{"constraints":[{"clamped_minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"result":{"kind":"ref","name":"SetCellPixelsResult"},"since":6,"stream":null},"set-client-info":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"capabilities":{"constraints":["Advertises additive client capabilities for this connection."],"default":null,"nullable":true,"presence":"optional","type":{"items":{"kind":"scalar","name":"string"},"kind":"array"}},"kind":{"constraints":["Control characters become spaces; at most 64 Unicode characters are retained."],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"name":{"constraints":["Control characters become spaces; at most 64 Unicode characters are retained."],"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"set-client-sizing":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["exclusive:true requires client and enabled:true.","Omitting client is valid only with enabled:true and restores all clients for the surface."],"fields":{"client":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"enabled":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"exclusive":{"default":false,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":10,"stream":null},"set-default-colors":{"authority":"control","capability":null,"constraints":["Color strings are exactly #rrggbb.","With complete:true, absent optional values reset to built-in defaults."],"request":{"additional_properties":false,"fields":{"bg":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"ColorHex"}},"complete":{"default":false,"nullable":false,"presence":"optional","since":9,"type":{"kind":"scalar","name":"boolean"}},"cursor":{"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"ref","name":"ColorHex"}},"cursor_blink":{"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"boolean"}},"cursor_style":{"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"ref","name":"CursorStyle"}},"fg":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"ColorHex"}},"palette":{"constraints":["Decimal string keys are palette indexes 0 through 255."],"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"map","values":{"kind":"ref","name":"ColorHex"}}},"selection_bg":{"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"ref","name":"ColorHex"}},"selection_fg":{"default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"ref","name":"ColorHex"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"set-ratio":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"dir":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"SplitDirection"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"ratio":{"constraints":[{"clamped_maximum":0.95,"clamped_minimum":0.05}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float32"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":null},"set-split-ratio":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"ratio":{"constraints":[{"clamped_maximum":0.95,"clamped_minimum":0.05}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float32"}},"split":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"transaction":{"capability":"layout-undo-v1","default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":8,"stream":null},"set-viewport-pane-width":{"authority":"control","capability":"viewport-column-resize-v1","constraints":["width must be finite."],"request":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"transaction":{"capability":"layout-undo-v1","default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"uint64"}},"width":{"constraints":[{"maximum":1.0,"minimum":0.1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float32"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":9,"stream":null},"set-window-title":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"title":{"constraints":["C0 controls are sanitized before OSC output."],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"shutdown-daemon":{"authority":"local-admin","capability":null,"constraints":["pid and generation must match the latest identify result.","force bypasses native-browser ownership only; the identity fence and trusted-local authority still apply.","Clients must require daemon-handoff-force-v1 before sending force:true.","The daemon exits only after the success response is queued."],"request":{"additional_properties":false,"fields":{"force":{"capability":"daemon-handoff-force-v1","default":false,"nullable":false,"presence":"optional","since":10,"type":{"kind":"scalar","name":"boolean"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"pid":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"result":{"kind":"ref","name":"ShutdownDaemonResult"},"since":9,"stream":null},"sidebar-plugin":{"authority":"frontend","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"relaunch":{"default":false,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"result":{"kind":"ref","name":"SidebarPluginResult"},"since":6,"stream":null},"split":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["cols and rows affect sizing only when both are present."],"fields":{"cols":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"dir":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"SplitDirection"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"rows":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"result":{"kind":"ref","name":"SurfaceResult"},"since":5,"stream":null},"subscribe":{"authority":"frontend","capability":null,"constraints":["subscribe sends no initial tree snapshot.","surface filtering occurs before the bounded mailbox."],"request":{"additional_properties":false,"fields":{"presence_only":{"capability":"presence-v1","nullable":true,"presence":"optional","since":12,"type":{"kind":"scalar","name":"boolean"}},"surface":{"capability":"surface-subscribe-filter","default":null,"nullable":true,"presence":"optional","since":9,"type":{"kind":"ref","name":"Id"}},"tree_events":{"default":"coarse","nullable":true,"presence":"optional","since":7,"type":{"kind":"enum","values":["coarse","deltas"]}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":5,"stream":{"event_names":["agent-changed","bell","client-attached","client-changed","client-detached","client-list-invalidated","config-reload-requested","empty","frontend-projection-changed","layout-changed","notification","overflow","pairing-requested","pairing-resolved","pane-added","pane-closed","screen-added","screen-closed","screen-renamed","scroll-changed","status","surface-exited","surface-output","surface-resize-failed","surface-resized","tab-added","tab-closed","tab-renamed","terminal-registry-changed","title-changed","tree-changed","window-title-requested","workspace-added","workspace-closed","workspace-moved","workspace-renamed"],"kind":"subscribe","mode_field":"tree_events","modes":{"coarse":["tree-changed"],"deltas":["workspace-added","workspace-closed","workspace-renamed","workspace-moved","screen-added","screen-closed","screen-renamed","pane-added","pane-closed","tab-added","tab-closed","tab-renamed","tree-changed"]},"ordering":"Response and event objects may interleave. Events preserve enqueue order per subscription. Delta workspace revisions are serialized in durable commit order; overflow ends the stream and requires resubscribe plus snapshot.","terminal_event":null}},"swap-pane":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"constraints":["Exactly one of dir and target must be supplied."],"fields":{"dir":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"PaneDirection"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"target":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"EmptyResult"},"since":6,"stream":null},"terminal-events":{"authority":"control","capability":null,"constraints":["Consumers apply only contiguous revisions for one registry_id and generation."],"request":{"additional_properties":false,"fields":{"after_revision":{"default":0,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"TerminalEventsResult"},"since":9,"stream":null},"undo-layout":{"authority":"control","capability":"layout-undo-v1","constraints":["Clients must reject incomplete or contradictory result variants."],"request":{"additional_properties":false,"constraints":["confirm_close requires the exact preview revision."],"fields":{"confirm_close":{"default":false,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"revision":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"LayoutUndoResult"},"since":9,"stream":null},"unregister-browser-provider":{"authority":"local-admin","capability":"browser-provider-v1","constraints":["Only the calling connection\'s provider lease is removed."],"request":{"additional_properties":false,"fields":{},"kind":"object"},"result":{"kind":"ref","name":"BrowserProviderUnregisterResult"},"since":10,"stream":null},"vt-state":{"authority":"control","capability":null,"constraints":["PTY surfaces only."],"request":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"VtStateResult"},"since":5,"stream":null},"wait-for":{"authority":"control","capability":null,"constraints":["Blocks subsequent requests on this connection; SDKs should use a dedicated connection."],"request":{"additional_properties":false,"fields":{"pattern":{"constraints":[{"syntax":"Rust regex"}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"timeout_ms":{"description":"Zero performs one immediate check.","nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"result":{"kind":"ref","name":"WaitForResult"},"since":6,"stream":null},"zoom-pane":{"authority":"control","capability":null,"constraints":[],"request":{"additional_properties":false,"fields":{"mode":{"default":"toggle","nullable":true,"presence":"optional","type":{"kind":"enum","values":["toggle","on","off"]}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"result":{"kind":"ref","name":"ZoomPaneResult"},"since":6,"stream":null}},"events":{"agent-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"agent-changed"}},"session":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"source":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentSource"}},"state":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentState"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"updated_at_ms":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":11,"streams":["subscribe"]},"bell":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"bell"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"browser-state":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"error":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"browser-state"}},"frame":{"description":"The initial browser-state includes the latest frame when one exists; later state updates omit it.","nullable":true,"presence":"optional","type":{"kind":"ref","name":"BrowserFrame"}},"frames_stalled":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"status":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["starting","live","failed"]}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"title":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"url":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":6,"streams":["attach-browser"]},"client-attached":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"client":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"client-attached"}},"kind":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"name":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"transport":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["unix","ws"]}}},"kind":"object"},"since":6,"streams":["subscribe"]},"client-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"client":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"client-changed"}},"kind":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"name":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":6,"streams":["subscribe"]},"client-detached":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"client":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"client-detached"}}},"kind":"object"},"since":6,"streams":["subscribe"]},"client-list-invalidated":{"capability":null,"emission":"serialized-never-emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"client-list-invalidated"}}},"kind":"object"},"since":9,"streams":["subscribe"]},"colors-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"bg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"cursor":{"nullable":true,"presence":"optional","type":{"kind":"ref","name":"ColorHex"}},"cursor_blink":{"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"cursor_style":{"nullable":true,"presence":"optional","type":{"kind":"ref","name":"CursorStyle"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"colors-changed"}},"fg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"palette":{"nullable":false,"presence":"optional","since":7,"type":{"kind":"map","values":{"kind":"ref","name":"ColorHex"}}},"selection_bg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"selection_fg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"surface":{"nullable":false,"presence":"optional","since":7,"type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":6,"streams":["attach-byte"]},"config-reload-requested":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"config-reload-requested"}}},"kind":"object"},"since":6,"streams":["subscribe"]},"daemon-shutdown":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"daemon-shutdown"}}},"kind":"object"},"since":12,"streams":["control"]},"detached":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"detached"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":5,"streams":["attach-byte","attach-render","attach-browser"]},"empty":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"empty"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"frame":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"data":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Base64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"frame"}},"height":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"seq":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"width":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"since":6,"streams":["attach-browser"]},"frontend-projection-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"frontend-projection-changed"}},"frontend":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"mutation_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"origin":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"projection_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"scope":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"subject_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":7,"streams":["subscribe"]},"graphics-status":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["kitty-image-budget-worker-start-failed carries error.","kitty-image-budget-update-failed carries retry_exhausted and summary.","cell-pixel-update-retries-exhausted carries attempts, remaining, cell_width, and cell_height."],"fields":{"attempts":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"cell_height":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"cell_width":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"error":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"graphics-status"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["kitty-image-budget-worker-start-failed","kitty-image-budget-update-failed","cell-pixel-update-retries-exhausted"]}},"remaining":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"retry_exhausted":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"summary":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":10,"streams":["subscribe"]},"layout-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"layout-changed"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":6,"streams":["subscribe"]},"machine-usage-changed":{"capability":"machine-usage-v1","emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"machine-usage-changed"}},"usage":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"MachineUsage"}}},"kind":"object"},"since":12,"streams":["subscribe"]},"notification":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"body":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"notification"}},"level":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"NotificationLevel"}},"notification":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"title":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":6,"streams":["subscribe","attach-byte","attach-browser"]},"output":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"colors":{"nullable":false,"presence":"optional","since":7,"type":{"kind":"ref","name":"TerminalColors"}},"data":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Base64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"output"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":5,"streams":["attach-byte"]},"overflow":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["scope and surface are either both present for attach overflow or both absent for subscribe overflow."],"fields":{"error":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"overflow"}},"scope":{"nullable":false,"presence":"optional","type":{"kind":"literal","value":"surface"}},"surface":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe","attach-byte","attach-render","attach-browser"]},"pairing-requested":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"code":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"pairing-requested"}},"expires_in":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"peer":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"request":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":7,"streams":["subscribe"]},"pairing-resolved":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"pairing-resolved"}},"request":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":7,"streams":["subscribe"]},"pane-added":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Pane"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"pane-added"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"pane-closed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Pane"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"pane-closed"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"presence-changed":{"capability":"presence-v1","emission":"emitted","payload":{"additional_properties":false,"fields":{"client":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"color":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"presence-changed"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"highlight":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"PresenceHighlight"}},"kind":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"name":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"pointer":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"PresenceAnchor"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"updated_at_ms":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":12,"streams":["subscribe"]},"render-delta":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["size is present if and only if the surface resized; every resize has full:true."],"fields":{"cursor":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"RenderCursor"}},"default_bg":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"ColorHex"}},"default_fg":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"ColorHex"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"render-delta"}},"full":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"graphics":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"RenderGraphicsDelta"}},"history_epoch":{"nullable":false,"presence":"optional","since":10,"type":{"kind":"scalar","name":"uint64"}},"rows":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"RenderRow"},"kind":"array"}},"scrollback_rows":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint32"}},"size":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"Size"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["attach-render"]},"render-state":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"cursor":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"RenderCursor"}},"default_bg":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"default_fg":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"render-state"}},"graphics":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"RenderGraphics"}},"history_epoch":{"nullable":false,"presence":"required","since":10,"type":{"kind":"scalar","name":"uint64"}},"rows":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"RenderRow"},"kind":"array"}},"scrollback_rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"size":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Size"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["attach-render"]},"resized":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["At least one of replay and data is present; replay is canonical from protocol 7."],"fields":{"colors":{"nullable":false,"presence":"optional","since":7,"type":{"kind":"ref","name":"TerminalColors"}},"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"data":{"description":"Protocol 6 compatibility field.","nullable":false,"presence":"optional","type":{"kind":"ref","name":"Base64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"resized"}},"kitty_graphics_state":{"nullable":false,"presence":"optional","since":10,"type":{"kind":"ref","name":"KittyGraphicsState"}},"kitty_image_aliases":{"nullable":false,"presence":"optional","since":9,"type":{"items":{"kind":"ref","name":"KittyImageAlias"},"kind":"array"}},"replay":{"nullable":false,"presence":"optional","since":7,"type":{"kind":"ref","name":"Base64"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":6,"streams":["attach-byte"]},"screen-added":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Screen"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"screen-added"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"screen-closed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Screen"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"screen-closed"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"screen-renamed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Screen"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"screen-renamed"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"scroll-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"at_bottom":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"scroll-changed"}},"offset":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":6,"streams":["subscribe","attach-byte","attach-render","attach-browser"]},"status":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"status"}},"message":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"surface-exited":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"surface-exited"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"surface-output":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"surface-output"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"surface-resize-failed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"error":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"surface-resize-failed"}},"reservation_id":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"retry_after_ms":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe"]},"surface-resized":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"surface-resized"}},"reservation_id":{"nullable":true,"presence":"required","since":7,"type":{"kind":"scalar","name":"uint64"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"tab-added":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Tab"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"tab-added"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"tab-closed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Tab"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"tab-closed"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"tab-renamed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Tab"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"tab-renamed"}},"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"terminal-registry-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"terminal-registry-changed"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"refetch":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"terminal-events-or-list-terminals"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":9,"streams":["subscribe"]},"title-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"title-changed"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"title":{"nullable":false,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"tree-changed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"tree-changed"}}},"kind":"object"},"since":5,"streams":["subscribe"]},"vt-state":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"colors":{"nullable":false,"presence":"optional","since":6,"type":{"kind":"ref","name":"TerminalColors"}},"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"data":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Base64"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"vt-state"}},"kitty_graphics_state":{"nullable":false,"presence":"optional","since":10,"type":{"kind":"ref","name":"KittyGraphicsState"}},"kitty_image_aliases":{"nullable":false,"presence":"optional","since":9,"type":{"items":{"kind":"ref","name":"KittyImageAlias"},"kind":"array"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"since":5,"streams":["attach-byte"]},"window-title-requested":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"fields":{"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"window-title-requested"}},"title":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"since":6,"streams":["subscribe"]},"workspace-added":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent."],"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Workspace"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"workspace-added"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"mutation_id":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"workspace-closed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent."],"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Workspace"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"workspace-closed"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"mutation_id":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"workspace-moved":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent."],"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Workspace"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"workspace-moved"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"mutation_id":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]},"workspace-renamed":{"capability":null,"emission":"emitted","payload":{"additional_properties":false,"constraints":["origin and mutation_id are either both present or both absent."],"fields":{"entity":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Workspace"}},"event":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"workspace-renamed"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"mutation_id":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"origin":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"since":7,"streams":["subscribe-deltas"]}},"ir_sha256":"e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663","profiles":{"control":{"description":"Base authenticated session-control commands available to ordinary SDK clients.","inherits":[]},"frontend":{"description":"Rendering, input, presentation, subscribe, and attach commands.","inherits":["control"]},"local-admin":{"description":"Trusted local administration commands.","inherits":["control"],"transport":"Unix-classified transport, including direct Unix and the current stdio relay"},"provider-authority":{"description":"Provider-owned workspace mutation commands.","inherits":["control"],"requires_authority":true}},"protocol":{"id_type":"uint64","javascript_id_policy":"All protocol identifiers are uint64 JSON numbers. JavaScript and TypeScript SDKs must decode them losslessly as bigint (or validated decimal strings at their public boundary), and must not expose IEEE-754 number ids. Pairing request ids, revisions, timestamps, frame sequences, and reservation ids follow the same rule.","name":"cmux-tui-mux","version":12},"schema_version":2,"types":{"AgentRecord":{"additional_properties":false,"fields":{"session":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"source":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentSource"}},"state":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentState"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"updated_at_ms":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"AgentReportSource":{"kind":"enum","values":["socket","hook"]},"AgentSource":{"kind":"enum","values":["detected","socket","hook"]},"AgentState":{"kind":"enum","values":["working","blocked","idle","done","unknown"]},"AppliedPane":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"ApplyLayoutResult":{"additional_properties":false,"fields":{"panes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"AppliedPane"},"kind":"array"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"AttachedViewOutcomeResult":{"additional_properties":false,"fields":{"outcome":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ViewAttachmentOutcome"}}},"kind":"object"},"AttachedViewResizeResult":{"additional_properties":false,"fields":{"accepted":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"outcome":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ViewAttachmentOutcome"}},"reservation_id":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"Base64":{"kind":"alias","target":{"kind":"scalar","name":"string"}},"BrowserFrame":{"additional_properties":false,"fields":{"data":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Base64"}},"height":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"seq":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"width":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"BrowserProviderAuthentication":{"kind":"enum","values":["none","bearer"]},"BrowserProviderSnapshot":{"additional_properties":false,"constraints":["available is true exactly when provider_id, endpoint, authentication, and clients are present.","Provider bearer tokens are accepted only during registration and are never returned."],"fields":{"authentication":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"BrowserProviderAuthentication"}},"available":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"clients":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"endpoint":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"provider_id":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"string"}},"revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"targets":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"BrowserProviderTarget"},"kind":"array"}}},"kind":"object"},"BrowserProviderTarget":{"additional_properties":false,"fields":{"tab_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"target_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"BrowserProviderUnregisterResult":{"additional_properties":false,"fields":{"removed":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}}},"kind":"object"},"CellPixelFailure":{"additional_properties":false,"fields":{"error":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"CellPixelResize":{"additional_properties":false,"fields":{"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"reservation_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"CellPixelSurface":{"additional_properties":false,"fields":{"height_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"width_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"ClientInfo":{"additional_properties":false,"fields":{"attached":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Id"},"kind":"array"}},"client":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"connected_seconds":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"kind":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"name":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"self":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"sizes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"ClientSize"},"kind":"array"}},"transport":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ClientTransport"}}},"kind":"object"},"ClientSize":{"additional_properties":false,"fields":{"cols":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"rows":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"size_participating":{"nullable":false,"presence":"required","since":10,"type":{"kind":"scalar","name":"boolean"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"ClientTransport":{"kind":"enum","values":["local","unix","ws"]},"CloseTerminalResult":{"additional_properties":false,"fields":{"already_closed":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"closed":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ColorHex":{"kind":"alias","target":{"kind":"scalar","name":"string"}},"CopyResult":{"additional_properties":false,"fields":{"mode":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["screen","selection","scrollback"]}},"text":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"CursorStyle":{"kind":"enum","values":["block","underline","bar"]},"DeadPane":{"additional_properties":false,"fields":{"dead":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}},"id":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"DeclarativeLayout":{"kind":"tagged_union","tag":"type","variants":{"leaf":{"additional_properties":false,"fields":{"command":{"default":null,"nullable":true,"presence":"optional","type":{"items":{"kind":"scalar","name":"string"},"kind":"array","min_items":1}},"cwd":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"type":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"leaf"}}},"kind":"object"},"split":{"additional_properties":false,"fields":{"a":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"DeclarativeLayout"}},"b":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"DeclarativeLayout"}},"dir":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"SplitDirection"}},"ratio":{"constraints":[{"clamped_maximum":0.95,"clamped_minimum":0.05}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float32"}},"type":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"split"}}},"kind":"object"},"stack":{"additional_properties":false,"fields":{"expanded":{"constraints":["Must identify a member of panes."],"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"panes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Id"},"kind":"array","min_items":1}},"type":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"stack"}}},"kind":"object"}}},"EmptyResult":{"additional_properties":false,"fields":{},"kind":"object"},"ExportLayoutResult":{"additional_properties":false,"fields":{"layout":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Layout"}},"panes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"ExportedPane"},"kind":"array"}}},"kind":"object"},"ExportedPane":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"surfaces":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Id"},"kind":"array"}}},"kind":"object"},"FocusDirectionResult":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"FrontendFocusTarget":{"kind":"enum","values":["pane","machine_rail","workspace_rail","tabs_rail","projection_rail"]},"FrontendJournalEvent":{"kind":"tagged_union","tag":"kind","variants":{"focus":{"additional_properties":false,"fields":{"content_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"event_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"frontend_projection_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"focus"}},"pane_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"screen_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"tab_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"target":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"FrontendFocusTarget"}},"workspace_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"resize":{"additional_properties":false,"fields":{"cell_height":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"cell_width":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"event_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"frontend_projection_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"resize"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"viewport":{"additional_properties":false,"fields":{"event_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"frontend_projection_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"viewport"}},"offset":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"screen_id":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"settled":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"target":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"}}},"FrontendProjection":{"additional_properties":false,"fields":{"frontend":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"projection":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"JsonValue"}},"projection_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"replayed":{"default":false,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"schema_version":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"scope":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"subject_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"GetCellPixelsResult":{"additional_properties":false,"fields":{"height_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"surfaces":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"CellPixelSurface"},"kind":"array"}},"width_px":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"Id":{"kind":"alias","target":{"kind":"scalar","name":"uint64"}},"IdMapping":{"additional_properties":false,"fields":{"id":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["workspace","screen","pane","surface"]}},"short_id":{"constraints":[{"pattern":"^[a-z0-9]{6}$"}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"IdentifyResult":{"additional_properties":false,"fields":{"app":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"cmux-tui"}},"build_commit":{"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"capabilities":{"default":[],"nullable":false,"presence":"optional","type":{"items":{"kind":"scalar","name":"string"},"kind":"array"}},"daemon_handoff":{"nullable":false,"presence":"required","since":9,"type":{"kind":"literal","value":1}},"generation":{"nullable":false,"presence":"required","since":7,"type":{"kind":"scalar","name":"string"}},"ghostty_commit":{"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"lifecycle_ready":{"default":true,"nullable":false,"presence":"optional","since":12,"type":{"kind":"scalar","name":"boolean"}},"pid":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"protocol":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"registry_id":{"nullable":false,"presence":"required","since":7,"type":{"kind":"scalar","name":"string"}},"session":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","since":9,"type":{"kind":"scalar","name":"uint64"}},"version":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace_revision":{"nullable":false,"presence":"required","since":7,"type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"IdsResult":{"additional_properties":false,"fields":{"ids":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"IdMapping"},"kind":"array"}}},"kind":"object"},"JsonValue":{"kind":"opaque_json","reason":"The wire field intentionally carries a frontend-authored or runtime-authored arbitrary JSON document."},"KittyGraphicsState":{"additional_properties":false,"fields":{"alternate_next_image_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"alternate_replay_next_image_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"image_bytes":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"images":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"inflight_bytes":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"placements":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"primary_next_image_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"primary_replay_next_image_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"replay_cursor_offset":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"KittyImageAlias":{"additional_properties":false,"fields":{"image_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"image_number":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"Layout":{"kind":"tagged_union","tag":"type","variants":{"leaf":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"type":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"leaf"}}},"kind":"object"},"split":{"additional_properties":false,"fields":{"a":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Layout"}},"b":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Layout"}},"dir":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"SplitDirection"}},"ratio":{"constraints":[{"maximum":0.95,"minimum":0.05}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float32"}},"split":{"description":"Stable for the lifetime of this split node.","nullable":false,"presence":"optional","since":8,"type":{"kind":"ref","name":"Id"}},"type":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"split"}}},"kind":"object"},"stack":{"additional_properties":false,"fields":{"expanded":{"constraints":["Must identify a member of panes."],"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"panes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Id"},"kind":"array","min_items":1}},"type":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"stack"}}},"kind":"object"}}},"LayoutUndoConfirmationRequired":{"additional_properties":false,"fields":{"closes_panes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Id"},"kind":"array"}},"confirmation_required":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}},"revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"undone":{"nullable":false,"presence":"required","type":{"kind":"literal","value":false}}},"kind":"object"},"LayoutUndoResult":{"kind":"untagged_union","variants":[{"kind":"ref","name":"LayoutUndoUndone"},{"kind":"ref","name":"LayoutUndoConfirmationRequired"}]},"LayoutUndoUndone":{"additional_properties":false,"fields":{"confirmation_required":{"default":false,"nullable":false,"presence":"optional","type":{"kind":"literal","value":false}},"revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"screen":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"undone":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}}},"kind":"object"},"ListAgentsResult":{"additional_properties":false,"fields":{"agents":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"AgentRecord"},"kind":"array"}}},"kind":"object"},"ListTerminalsResult":{"additional_properties":false,"fields":{"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"terminals":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"TerminalRecord"},"kind":"array"}}},"kind":"object"},"LivePane":{"additional_properties":false,"fields":{"active_tab":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"focused_at":{"default":0,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint64"}},"id":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"name":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"short_id":{"constraints":[{"pattern":"^[a-z0-9]{6}$"}],"nullable":false,"presence":"optional","since":6,"type":{"kind":"scalar","name":"string"}},"tabs":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Tab"},"kind":"array"}}},"kind":"object"},"MachineListeningTcpResult":{"additional_properties":false,"constraints":["The daemon runs only a fixed socket-listing command; callers cannot supply command text.","The output is limited to 524288 bytes."],"fields":{"stdout":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"MachineUsage":{"additional_properties":false,"constraints":["period_days is the trailing window length in days.","api_equivalent_usd is the list-price equivalent of the machine\'s model traffic in that window."],"fields":{"api_equivalent_usd":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"as_of":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"period_days":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"total_tokens":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"vm_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"MachineUsageResult":{"additional_properties":false,"constraints":["usage is null when the daemon has no readout (not a Cloud VM, endpoint unavailable, or usage not ready)."],"fields":{"usage":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"MachineUsage"}}},"kind":"object"},"MintTerminalRendererResult":{"additional_properties":false,"fields":{"endpoint":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"incarnation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"protocol_version":{"nullable":false,"presence":"required","since":11,"type":{"kind":"scalar","name":"uint16"}},"rights":{"constraints":[{"current_value":7}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"token":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"ttl_ms":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"MoveTerminalResult":{"additional_properties":false,"fields":{"changed":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"lifecycle":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalLifecycle"}},"pane":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"replayed":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"screen":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"workspace":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"NotificationLevel":{"kind":"enum","values":["info","warning","error"]},"NotificationMarker":{"additional_properties":false,"fields":{"level":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"NotificationLevel"}},"notification":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"unread":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}}},"kind":"object"},"NotifyResult":{"additional_properties":false,"fields":{"notification":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"Pane":{"kind":"untagged_union","variants":[{"kind":"ref","name":"LivePane"},{"kind":"ref","name":"DeadPane"}]},"PaneDirection":{"kind":"enum","values":["left","right","up","down"]},"PaneNeighborResult":{"additional_properties":false,"fields":{"pane":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"PingResult":{"additional_properties":false,"fields":{"build_commit":{"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"ghostty_commit":{"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"ok":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}},"protocol":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"version":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"PresenceAnchor":{"kind":"tagged_union","tag":"kind","variants":{"cell":{"additional_properties":false,"fields":{"col":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"cell"}},"row":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"scroll_offset":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"point":{"additional_properties":false,"fields":{"kind":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"point"}},"x":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}},"y":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"float64"}}},"kind":"object"}}},"PresenceEntry":{"additional_properties":false,"constraints":["surface is null only in presence-changed after a clear, disconnect, or surface exit; presence-list never returns such entries.","color is a palette slot in 0..8, stable for the connection."],"fields":{"client":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"color":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"highlight":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"PresenceHighlight"}},"kind":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"name":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"pointer":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"PresenceAnchor"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"updated_at_ms":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"PresenceHighlight":{"additional_properties":false,"fields":{"end":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"PresenceAnchor"}},"mode":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"PresenceHighlightMode"}},"start":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"PresenceAnchor"}}},"kind":"object"},"PresenceHighlightMode":{"kind":"enum","values":["laser","pin"]},"PresenceListResult":{"additional_properties":false,"fields":{"entries":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"PresenceEntry"},"kind":"array"}}},"kind":"object"},"ProcessInfoResult":{"additional_properties":false,"fields":{"command":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"cwd":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"foreground_cwd":{"description":"Working directory of the process group that owns the PTY, read at request time. Null when the lookup fails; absent from daemons that predate the field. Clients treat absence as null.","nullable":true,"presence":"optional","since":12,"type":{"kind":"scalar","name":"string"}},"pid":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"ProviderWorkspaceMutationResult":{"additional_properties":false,"fields":{"key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ReadScreenResult":{"additional_properties":false,"fields":{"text":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"ReadScrollbackResult":{"additional_properties":false,"fields":{"epoch":{"nullable":false,"presence":"required","since":10,"type":{"kind":"scalar","name":"uint64"}},"rows":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"RenderRow"},"kind":"array"}},"start":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"total":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"RenderCursor":{"additional_properties":false,"fields":{"blink":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"color":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"style":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"CursorStyle"}},"visible":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"x":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"y":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"RenderGraphicFormat":{"kind":"enum","values":["rgb","rgba"]},"RenderGraphicImage":{"additional_properties":false,"fields":{"data":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Base64"}},"format":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"RenderGraphicFormat"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"height":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"width":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"RenderGraphicPlacement":{"additional_properties":false,"fields":{"anchor_col":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint16"}},"anchor_row":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint32"}},"columns":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"grid_cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"grid_rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"image_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"ordinal":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"pixel_height":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"pixel_width":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"placement_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"source_height":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"source_width":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"source_x":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"source_y":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"viewport_col":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"int32"}},"viewport_row":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"int32"}},"viewport_visible":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"x_offset":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"y_offset":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"z":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"int32"}}},"kind":"object"},"RenderGraphics":{"additional_properties":false,"fields":{"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"images":{"nullable":false,"presence":"optional","type":{"items":{"kind":"ref","name":"RenderGraphicImage"},"kind":"array"}},"placements":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"RenderGraphicPlacement"},"kind":"array"}},"removed_image_ids":{"nullable":false,"presence":"optional","type":{"items":{"kind":"scalar","name":"uint32"},"kind":"array"}}},"kind":"object"},"RenderGraphicsDelta":{"additional_properties":false,"fields":{"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"images":{"nullable":false,"presence":"optional","type":{"items":{"kind":"ref","name":"RenderGraphicImage"},"kind":"array"}},"placements":{"nullable":false,"presence":"optional","type":{"items":{"kind":"ref","name":"RenderGraphicPlacement"},"kind":"array"}},"removed_image_ids":{"nullable":false,"presence":"optional","type":{"items":{"kind":"scalar","name":"uint32"},"kind":"array"}}},"kind":"object"},"RenderRow":{"additional_properties":false,"fields":{"row":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"runs":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"RenderRun"},"kind":"array"}}},"kind":"object"},"RenderRun":{"additional_properties":false,"fields":{"attrs":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"bg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"fg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"text":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"underline":{"nullable":false,"presence":"optional","type":{"kind":"ref","name":"RenderUnderline"}},"width_hint":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"RenderUnderline":{"kind":"enum","values":["single","double","curly","dotted","dashed"]},"ReportAgentResult":{"additional_properties":false,"fields":{"session":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"source":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentReportSource"}},"state":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"AgentState"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"ResizeSurfaceResult":{"additional_properties":false,"fields":{"accepted":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"reservation_id":{"nullable":true,"presence":"required","since":7,"type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ResolveTerminalResult":{"additional_properties":false,"fields":{"exit":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"TerminalExit"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"launch_spec":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"JsonValue"}},"lifecycle":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalLifecycle"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"workspace_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"ResourceSelectors":{"additional_properties":false,"fields":{"agent":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"browser":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"client":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"frontend_projection":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"machine":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"notification":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"pairing_request":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"pane":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"screen":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"session":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"sidebar_view":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"split":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"stream":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"tab":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"terminal":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"workspace":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"RunResult":{"additional_properties":false,"fields":{"already_exited":{"nullable":false,"presence":"required","since":11,"type":{"kind":"scalar","name":"boolean"}},"exit":{"nullable":true,"presence":"required","since":11,"type":{"kind":"ref","name":"TerminalExit"}},"lifecycle":{"nullable":false,"presence":"required","since":11,"type":{"kind":"ref","name":"TerminalLifecycle"}},"pane":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"screen":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"terminal_id":{"nullable":false,"presence":"required","since":9,"type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"required","since":9,"type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","since":11,"type":{"kind":"scalar","name":"uint64"}},"workspace":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"Screen":{"additional_properties":false,"fields":{"active":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"active_pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"id":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"layout":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Layout"}},"name":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"panes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Pane"},"kind":"array"}},"short_id":{"constraints":[{"pattern":"^[a-z0-9]{6}$"}],"nullable":false,"presence":"optional","since":6,"type":{"kind":"scalar","name":"string"}},"zoomed_pane":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"ServerStatsConnections":{"additional_properties":false,"constraints":["refused counts sockets dropped at limit; for hook producers each one is a lost event."],"fields":{"accepted":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"active":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"limit":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"peak":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"refused":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ServerStatsHistogram":{"additional_properties":false,"constraints":["Percentiles are log-linear bucket upper bounds and overestimate the true sample by at most 25%.","Latency histograms are in microseconds; batch_size counts events."],"fields":{"count":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"max":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"mean":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"p50":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"p90":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"p99":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ServerStatsJournalWriter":{"additional_properties":false,"constraints":["commit_us excludes lock wait; commit_lock_wait_us is the writer waiting for the registry lock.","terminal_queued and durable_queued are live lane depths."],"fields":{"batch_size":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsHistogram"}},"batches":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"commit_failures":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"commit_lock_wait_us":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsHistogram"}},"commit_us":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsHistogram"}},"deadline_expiries":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"durable_events":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"durable_queued":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"phase":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsWriterPhase"}},"phase_for_us":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"receipt_wait_us":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsHistogram"}},"terminal_events":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"terminal_queued":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ServerStatsLockHolder":{"additional_properties":false,"constraints":["site is the file:line that acquired the registry lock."],"fields":{"held_for_us":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"site":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"ServerStatsLockSite":{"additional_properties":false,"constraints":[],"fields":{"acquisitions":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"hold_max_us":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"hold_total_us":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"site":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"ServerStatsLockStall":{"additional_properties":false,"constraints":["blocker is the site holding the lock when the waiter\'s wait began, or null when it was free."],"fields":{"blocker":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"waited_us":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"waiter":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"ServerStatsRegistryLock":{"additional_properties":false,"constraints":["contended_acquisitions counts waits of at least 1 ms; stalls counts waits of at least 100 ms.","top_sites is ordered by hold_total_us descending and holds at most eight entries."],"fields":{"contended_acquisitions":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"hold_us":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsHistogram"}},"holder":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ServerStatsLockHolder"}},"last_stall":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ServerStatsLockStall"}},"stalls":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"top_sites":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"ServerStatsLockSite"},"kind":"array"}},"wait_us":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsHistogram"}}},"kind":"object"},"ServerStatsResult":{"additional_properties":false,"constraints":["schema is 1.","journal_writer is null for ephemeral sessions without a durable journal.","Counters accumulate since daemon start; reading them never touches SQLite or the journal."],"fields":{"connections":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsConnections"}},"journal_writer":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ServerStatsJournalWriter"}},"registry_lock":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"ServerStatsRegistryLock"}},"schema":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}},"uptime_ms":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ServerStatsWriterPhase":{"kind":"enum","values":["idle","waiting_lock","committing"]},"SetCellPixelsResult":{"additional_properties":false,"fields":{"failures":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"CellPixelFailure"},"kind":"array"}},"resizes":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"CellPixelResize"},"kind":"array"}}},"kind":"object"},"ShutdownDaemonResult":{"additional_properties":false,"fields":{"accepted":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"pid":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint32"}}},"kind":"object"},"SidebarPluginResult":{"additional_properties":false,"fields":{"error":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"retry_after_ms":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"Size":{"additional_properties":false,"fields":{"cols":{"constraints":[{"maximum":10000,"minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"rows":{"constraints":[{"maximum":10000,"minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"SplitDirection":{"kind":"enum","values":["right","down"]},"SurfaceResult":{"additional_properties":false,"fields":{"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"terminal_id":{"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"string"}}},"kind":"object"},"Tab":{"additional_properties":false,"fields":{"browser_error":{"nullable":true,"presence":"optional","since":6,"type":{"kind":"scalar","name":"string"}},"browser_frames_stalled":{"nullable":true,"presence":"optional","since":6,"type":{"kind":"scalar","name":"boolean"}},"browser_source":{"nullable":true,"presence":"required","type":{"kind":"enum","values":["external","launched"]}},"browser_status":{"nullable":true,"presence":"optional","since":6,"type":{"kind":"enum","values":["starting","live","failed"]}},"dead":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"enum","values":["pty","browser"]}},"name":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"notification":{"nullable":true,"presence":"optional","since":6,"type":{"kind":"ref","name":"NotificationMarker"}},"short_id":{"constraints":[{"pattern":"^[a-z0-9]{6}$"}],"nullable":false,"presence":"optional","since":6,"type":{"kind":"scalar","name":"string"}},"size":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Size"}},"supports_clear_history_key_fallback":{"capability":"clear-history-key-v1","nullable":false,"presence":"optional","since":9,"type":{"kind":"scalar","name":"boolean"}},"surface":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"terminal_id":{"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"optional","since":9,"type":{"kind":"scalar","name":"string"}},"terminal_resource_id":{"constraints":[{"pattern":"^term_[0-9a-f]{32}$"}],"nullable":true,"presence":"optional","since":10,"type":{"kind":"scalar","name":"string"}},"title":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"TerminalColors":{"additional_properties":false,"fields":{"bg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"cursor":{"nullable":true,"presence":"optional","since":6,"type":{"kind":"ref","name":"ColorHex"}},"cursor_blink":{"nullable":true,"presence":"optional","since":6,"type":{"kind":"scalar","name":"boolean"}},"cursor_style":{"nullable":true,"presence":"optional","since":6,"type":{"kind":"ref","name":"CursorStyle"}},"fg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"palette":{"constraints":["Decimal string keys are palette indexes 0 through 255."],"nullable":false,"presence":"optional","since":7,"type":{"kind":"map","values":{"kind":"ref","name":"ColorHex"}}},"selection_bg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}},"selection_fg":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"ColorHex"}}},"kind":"object"},"TerminalEventsResult":{"additional_properties":false,"fields":{"events":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"TerminalRegistryEvent"},"kind":"array"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"TerminalExit":{"additional_properties":false,"fields":{"exited_at_ms":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"outcome":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalExitOutcome"}}},"kind":"object"},"TerminalExitOutcome":{"kind":"tagged_union","tag":"kind","variants":{"exit":{"additional_properties":false,"fields":{"code":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"int32"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"exit"}}},"kind":"object"},"signal":{"additional_properties":false,"fields":{"core_dumped":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"kind":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"signal"}},"signal":{"constraints":[{"minimum":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"int32"}}},"kind":"object"},"unknown":{"additional_properties":false,"fields":{"kind":{"nullable":false,"presence":"required","type":{"kind":"literal","value":"unknown"}},"reason":{"constraints":[{"min_length":1}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"}}},"TerminalKey":{"kind":"enum","values":["unidentified","backquote","backslash","bracket-left","bracket-right","comma","digit0","digit1","digit2","digit3","digit4","digit5","digit6","digit7","digit8","digit9","equal","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","minus","period","quote","semicolon","slash","backspace","enter","space","tab","delete","end","home","insert","page-down","page-up","arrow-down","arrow-left","arrow-right","arrow-up","numpad0","numpad1","numpad2","numpad3","numpad4","numpad5","numpad6","numpad7","numpad8","numpad9","numpad-add","numpad-backspace","numpad-comma","numpad-decimal","numpad-divide","numpad-enter","numpad-equal","numpad-multiply","numpad-subtract","numpad-up","numpad-down","numpad-right","numpad-left","numpad-begin","numpad-home","numpad-end","numpad-insert","numpad-delete","numpad-page-up","numpad-page-down","escape","f1","f2","f3","f4","f5","f6","f7","f8","f9","f10","f11","f12","f13","f14","f15","f16","f17","f18","f19","f20"]},"TerminalKeyAction":{"kind":"enum","values":["press","release","repeat"]},"TerminalKeyInput":{"additional_properties":false,"constraints":["consumed_mods must be a subset of mods.","unshifted_codepoint, shifted_codepoint, and base_layout_codepoint contain exactly one Unicode scalar when present.","utf8 contains no control characters.","macos_option_as_alt may be false only when Alt is active and consumed."],"fields":{"action":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"ref","name":"TerminalKeyAction"}},"base_layout_codepoint":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"composing":{"default":false,"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"consumed_mods":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalModifiers"}},"key":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalKey"}},"macos_option_as_alt":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"mods":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalModifiers"}},"shifted_codepoint":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"unshifted_codepoint":{"default":null,"nullable":true,"presence":"optional","type":{"kind":"scalar","name":"string"}},"utf8":{"constraints":[{"max_length":4096}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"TerminalLifecycle":{"kind":"enum","values":["launching","adopting","running","exited","tombstoned"]},"TerminalModifiers":{"additional_properties":false,"fields":{"alt":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"caps_lock":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"control":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"num_lock":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"shift":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"super":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}}},"kind":"object"},"TerminalPlacement":{"additional_properties":false,"fields":{"already_exited":{"nullable":false,"presence":"required","since":11,"type":{"kind":"scalar","name":"boolean"}},"exit":{"nullable":true,"presence":"required","since":11,"type":{"kind":"ref","name":"TerminalExit"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"lifecycle":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalLifecycle"}},"pane":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"replayed":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"screen":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"surface":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"workspace":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"},"TerminalRecord":{"additional_properties":false,"fields":{"exit":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"TerminalExit"}},"launch_spec":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"JsonValue"}},"lifecycle":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"TerminalLifecycle"}},"terminal_id":{"constraints":[{"format":"UUIDv4 hex without dashes","pattern":"^[0-9a-f]{32}$"}],"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_incarnation":{"nullable":true,"presence":"required","type":{"kind":"scalar","name":"string"}},"workspace_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"TerminalRegistryEvent":{"additional_properties":false,"fields":{"kind":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"mutation_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"origin":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"result":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"JsonValue"}},"terminal_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"workspace_key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"Tree":{"additional_properties":false,"fields":{"generation":{"capability":"workspace-registry-v1","nullable":false,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"pane_revision":{"nullable":false,"presence":"optional","since":9,"type":{"kind":"scalar","name":"uint64"}},"registry_id":{"capability":"workspace-registry-v1","nullable":false,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"terminal_revision":{"nullable":false,"presence":"optional","since":9,"type":{"kind":"scalar","name":"uint64"}},"workspace_revision":{"capability":"workspace-registry-v1","nullable":false,"presence":"optional","since":7,"type":{"kind":"scalar","name":"uint64"}},"workspaces":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Workspace"},"kind":"array"}}},"kind":"object"},"ViewAttachmentOutcome":{"kind":"enum","values":["applied","passive","superseded"]},"VtStateResult":{"additional_properties":false,"fields":{"cols":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}},"data":{"constraints":[{"encoding":"standard base64"}],"nullable":false,"presence":"required","type":{"kind":"ref","name":"Base64"}},"kitty_graphics_state":{"nullable":false,"presence":"optional","since":10,"type":{"kind":"ref","name":"KittyGraphicsState"}},"kitty_image_aliases":{"nullable":false,"presence":"optional","since":9,"type":{"items":{"kind":"ref","name":"KittyImageAlias"},"kind":"array"}},"rows":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint16"}}},"kind":"object"},"WaitForResult":{"additional_properties":false,"fields":{"elapsed_ms":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"matched":{"nullable":false,"presence":"required","type":{"kind":"literal","value":true}},"text":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}}},"kind":"object"},"Workspace":{"additional_properties":false,"fields":{"active":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"id":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"key":{"capability":"workspace-registry-v1","constraints":[{"format":"lowercase canonical UUID"}],"nullable":false,"presence":"optional","since":7,"type":{"kind":"scalar","name":"string"}},"name":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"screens":{"nullable":false,"presence":"required","type":{"items":{"kind":"ref","name":"Screen"},"kind":"array"}},"short_id":{"constraints":[{"pattern":"^[a-z0-9]{6}$"}],"nullable":false,"presence":"optional","since":6,"type":{"kind":"scalar","name":"string"}}},"kind":"object"},"WorkspaceMutationResult":{"additional_properties":false,"fields":{"changed":{"nullable":false,"presence":"optional","type":{"kind":"scalar","name":"boolean"}},"generation":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"index":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}},"key":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"registry_id":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"string"}},"replayed":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"workspace":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"workspace_revision":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"uint64"}}},"kind":"object"},"ZoomPaneResult":{"additional_properties":false,"fields":{"pane":{"nullable":false,"presence":"required","type":{"kind":"ref","name":"Id"}},"zoomed":{"nullable":false,"presence":"required","type":{"kind":"scalar","name":"boolean"}},"zoomed_pane":{"nullable":true,"presence":"required","type":{"kind":"ref","name":"Id"}}},"kind":"object"}}}') diff --git a/cmux-tui/bindings/python/cmux/raw/_generated/client.py b/cmux-tui/bindings/python/cmux/raw/_generated/client.py index dc88e71018a0..518c109f3edc 100644 --- a/cmux-tui/bindings/python/cmux/raw/_generated/client.py +++ b/cmux-tui/bindings/python/cmux/raw/_generated/client.py @@ -195,6 +195,15 @@ def pane_neighbor(self, pane: Id, dir: PaneDirection) -> PaneNeighborResult: def ping(self) -> PingResult: return self._invoke_command('ping', PingRequest()) + def presence_clear(self) -> EmptyResult: + return self._invoke_command('presence-clear', PresenceClearRequest()) + + def presence_list(self) -> PresenceListResult: + return self._invoke_command('presence-list', PresenceListRequest()) + + def presence_update(self, surface: Id, *, highlight: Union[PresenceHighlight, None, MissingType] = MISSING, pointer: Union[PresenceAnchor, None, MissingType] = MISSING) -> EmptyResult: + return self._invoke_command('presence-update', PresenceUpdateRequest(surface=surface, highlight=highlight, pointer=pointer)) + def process_info(self, surface: Id) -> ProcessInfoResult: return self._invoke_command('process-info', ProcessInfoRequest(surface=surface)) @@ -306,8 +315,8 @@ def sidebar_plugin(self, cols: int, rows: int, *, relaunch: Union[bool, MissingT def split(self, pane: Id, dir: SplitDirection, *, cols: Union[int, None, MissingType] = MISSING, rows: Union[int, None, MissingType] = MISSING) -> SurfaceResult: return self._invoke_command('split', SplitRequest(pane=pane, dir=dir, cols=cols, rows=rows)) - def subscribe(self, surface: Union[Id, None, MissingType] = MISSING, *, tree_events: Union[Literal['coarse', 'deltas'], None, MissingType] = MISSING) -> Any: - return self._open_command_stream('subscribe', SubscribeRequest(surface=surface, tree_events=tree_events)) + def subscribe(self, surface: Union[Id, None, MissingType] = MISSING, *, tree_events: Union[Literal['coarse', 'deltas'], None, MissingType] = MISSING, presence_only: Union[bool, None, MissingType] = MISSING) -> Any: + return self._open_command_stream('subscribe', SubscribeRequest(surface=surface, tree_events=tree_events, presence_only=presence_only)) def swap_pane(self, pane: Id, *, dir: Union[PaneDirection, None, MissingType] = MISSING, target: Union[Id, None, MissingType] = MISSING) -> EmptyResult: return self._invoke_command('swap-pane', SwapPaneRequest(pane=pane, dir=dir, target=target)) @@ -392,6 +401,9 @@ def zoom_pane(self, pane: Union[Id, None, MissingType] = MISSING, *, mode: Union GeneratedClientMixin.pairing_response.__cmux_command__ = COMMANDS['pairing-response'] GeneratedClientMixin.pane_neighbor.__cmux_command__ = COMMANDS['pane-neighbor'] GeneratedClientMixin.ping.__cmux_command__ = COMMANDS['ping'] +GeneratedClientMixin.presence_clear.__cmux_command__ = COMMANDS['presence-clear'] +GeneratedClientMixin.presence_list.__cmux_command__ = COMMANDS['presence-list'] +GeneratedClientMixin.presence_update.__cmux_command__ = COMMANDS['presence-update'] GeneratedClientMixin.process_info.__cmux_command__ = COMMANDS['process-info'] GeneratedClientMixin.put_frontend_projection.__cmux_command__ = COMMANDS['put-frontend-projection'] GeneratedClientMixin.read_screen.__cmux_command__ = COMMANDS['read-screen'] diff --git a/cmux-tui/bindings/python/cmux/raw/_generated/codec.py b/cmux-tui/bindings/python/cmux/raw/_generated/codec.py index 14f2945c2f06..d71ac1c7fbed 100644 --- a/cmux-tui/bindings/python/cmux/raw/_generated/codec.py +++ b/cmux-tui/bindings/python/cmux/raw/_generated/codec.py @@ -66,6 +66,11 @@ class ProtocolDecodeError(ValueError): 'types/NotifyResult': models.NotifyResult, 'types/PaneNeighborResult': models.PaneNeighborResult, 'types/PingResult': models.PingResult, + 'types/PresenceAnchor/variants/cell': models.PresenceAnchorCell, + 'types/PresenceAnchor/variants/point': models.PresenceAnchorPoint, + 'types/PresenceEntry': models.PresenceEntry, + 'types/PresenceHighlight': models.PresenceHighlight, + 'types/PresenceListResult': models.PresenceListResult, 'types/ProcessInfoResult': models.ProcessInfoResult, 'types/ProviderWorkspaceMutationResult': models.ProviderWorkspaceMutationResult, 'types/ReadScreenResult': models.ReadScreenResult, @@ -177,6 +182,9 @@ class ProtocolDecodeError(ValueError): 'commands/pairing-response/request': models.PairingResponseRequest, 'commands/pane-neighbor/request': models.PaneNeighborRequest, 'commands/ping/request': models.PingRequest, + 'commands/presence-clear/request': models.PresenceClearRequest, + 'commands/presence-list/request': models.PresenceListRequest, + 'commands/presence-update/request': models.PresenceUpdateRequest, 'commands/process-info/request': models.ProcessInfoRequest, 'commands/put-frontend-projection/request': models.PutFrontendProjectionRequest, 'commands/read-screen/request': models.ReadScreenRequest, @@ -247,6 +255,7 @@ class ProtocolDecodeError(ValueError): 'events/pairing-resolved/payload': models.PairingResolvedEvent, 'events/pane-added/payload': models.PaneAddedEvent, 'events/pane-closed/payload': models.PaneClosedEvent, + 'events/presence-changed/payload': models.PresenceChangedEvent, 'events/render-delta/payload': models.RenderDeltaEvent, 'events/render-state/payload': models.RenderStateEvent, 'events/resized/payload': models.ResizedEvent, @@ -283,6 +292,7 @@ class ProtocolDecodeError(ValueError): 'types/FrontendFocusTarget': models.FrontendFocusTarget, 'types/NotificationLevel': models.NotificationLevel, 'types/PaneDirection': models.PaneDirection, + 'types/PresenceHighlightMode': models.PresenceHighlightMode, 'types/RenderGraphicFormat': models.RenderGraphicFormat, 'types/RenderUnderline': models.RenderUnderline, 'types/ServerStatsWriterPhase': models.ServerStatsWriterPhase, diff --git a/cmux-tui/bindings/python/cmux/raw/_generated/metadata.py b/cmux-tui/bindings/python/cmux/raw/_generated/metadata.py index 39c16d2d59df..9b49e080cbac 100644 --- a/cmux-tui/bindings/python/cmux/raw/_generated/metadata.py +++ b/cmux-tui/bindings/python/cmux/raw/_generated/metadata.py @@ -8,7 +8,7 @@ SCHEMA_VERSION = 2 MUX_PROTOCOL = 12 -IR_SHA256 = '8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86' +IR_SHA256 = 'e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663' @dataclass(frozen=True) @@ -825,6 +825,39 @@ class EventMetadata: { }, ), + 'presence-clear': CommandMetadata( + 'presence-clear', + 'control', + 12, + 'presence-v1', + ('control', 'frontend', 'local-admin', 'provider-authority'), + None, + { + }, + ), + 'presence-list': CommandMetadata( + 'presence-list', + 'control', + 12, + 'presence-v1', + ('control', 'frontend', 'local-admin', 'provider-authority'), + None, + { + }, + ), + 'presence-update': CommandMetadata( + 'presence-update', + 'control', + 12, + 'presence-v1', + ('control', 'frontend', 'local-admin', 'provider-authority'), + None, + { + 'highlight': CommandFieldMetadata(None, None), + 'pointer': CommandFieldMetadata(None, None), + 'surface': CommandFieldMetadata(None, None), + }, + ), 'process-info': CommandMetadata( 'process-info', 'control', @@ -1320,6 +1353,7 @@ class EventMetadata: ('frontend',), 'subscribe', { + 'presence_only': CommandFieldMetadata(12, 'presence-v1'), 'surface': CommandFieldMetadata(9, 'surface-subscribe-filter'), 'tree_events': CommandFieldMetadata(7, None), }, @@ -1434,6 +1468,7 @@ class EventMetadata: 'pairing-resolved': EventMetadata('pairing-resolved', 7, None, ('subscribe',), 'emitted'), 'pane-added': EventMetadata('pane-added', 7, None, ('subscribe-deltas',), 'emitted'), 'pane-closed': EventMetadata('pane-closed', 7, None, ('subscribe-deltas',), 'emitted'), + 'presence-changed': EventMetadata('presence-changed', 12, 'presence-v1', ('subscribe',), 'emitted'), 'render-delta': EventMetadata('render-delta', 7, None, ('attach-render',), 'emitted'), 'render-state': EventMetadata('render-state', 7, None, ('attach-render',), 'emitted'), 'resized': EventMetadata('resized', 6, None, ('attach-byte',), 'emitted'), diff --git a/cmux-tui/bindings/python/cmux/raw/_generated/models.py b/cmux-tui/bindings/python/cmux/raw/_generated/models.py index 60cea8607717..a1289ca32352 100644 --- a/cmux-tui/bindings/python/cmux/raw/_generated/models.py +++ b/cmux-tui/bindings/python/cmux/raw/_generated/models.py @@ -85,6 +85,10 @@ class PaneDirection(str, Enum): UP = 'up' DOWN = 'down' +class PresenceHighlightMode(str, Enum): + LASER = 'laser' + PIN = 'pin' + class RenderGraphicFormat(str, Enum): RGB = 'rgb' RGBA = 'rgba' @@ -703,6 +707,51 @@ class PingResult: ghostty_commit: Union[str, None, MissingType] = field(default=MISSING) +@dataclass(frozen=True) +class PresenceAnchorCell: + __cmux_schema_path__: ClassVar[str] = 'types/PresenceAnchor/variants/cell' + col: int + kind: Literal['cell'] + row: int + scroll_offset: Union[int, MissingType] = field(default=MISSING) + + +@dataclass(frozen=True) +class PresenceAnchorPoint: + __cmux_schema_path__: ClassVar[str] = 'types/PresenceAnchor/variants/point' + kind: Literal['point'] + x: float + y: float + + +@dataclass(frozen=True) +class PresenceEntry: + __cmux_schema_path__: ClassVar[str] = 'types/PresenceEntry' + surface: Union[Id, None] + client: int + color: int + generation: int + highlight: Union[PresenceHighlight, None] + kind: Union[str, None] + name: Union[str, None] + pointer: Union[PresenceAnchor, None] + updated_at_ms: int + + +@dataclass(frozen=True) +class PresenceHighlight: + __cmux_schema_path__: ClassVar[str] = 'types/PresenceHighlight' + end: PresenceAnchor + mode: PresenceHighlightMode + start: PresenceAnchor + + +@dataclass(frozen=True) +class PresenceListResult: + __cmux_schema_path__: ClassVar[str] = 'types/PresenceListResult' + entries: List[PresenceEntry] + + @dataclass(frozen=True) class ProcessInfoResult: __cmux_schema_path__: ClassVar[str] = 'types/ProcessInfoResult' @@ -1728,6 +1777,26 @@ class PingRequest: pass +@dataclass(frozen=True) +class PresenceClearRequest: + __cmux_schema_path__: ClassVar[str] = 'commands/presence-clear/request' + pass + + +@dataclass(frozen=True) +class PresenceListRequest: + __cmux_schema_path__: ClassVar[str] = 'commands/presence-list/request' + pass + + +@dataclass(frozen=True) +class PresenceUpdateRequest: + __cmux_schema_path__: ClassVar[str] = 'commands/presence-update/request' + surface: Id + highlight: Union[PresenceHighlight, None, MissingType] = field(default=MISSING) + pointer: Union[PresenceAnchor, None, MissingType] = field(default=MISSING) + + @dataclass(frozen=True) class ProcessInfoRequest: __cmux_schema_path__: ClassVar[str] = 'commands/process-info/request' @@ -2044,6 +2113,7 @@ class SubscribeRequest: __cmux_schema_path__: ClassVar[str] = 'commands/subscribe/request' surface: Union[Id, None, MissingType] = field(default=MISSING) tree_events: Union[Literal['coarse', 'deltas'], None, MissingType] = field(default=MISSING) + presence_only: Union[bool, None, MissingType] = field(default=MISSING) @dataclass(frozen=True) @@ -2343,6 +2413,22 @@ class PaneClosedEvent(EventBase): raw: Mapping[str, Any] = field(default_factory=dict, repr=False, compare=False, metadata={'cmux_skip': True}) +@dataclass(frozen=True) +class PresenceChangedEvent(EventBase): + __cmux_schema_path__: ClassVar[str] = 'events/presence-changed/payload' + surface: Union[Id, None] + client: int + color: int + event: Literal['presence-changed'] + generation: int + highlight: Union[PresenceHighlight, None] + kind: Union[str, None] + name: Union[str, None] + pointer: Union[PresenceAnchor, None] + updated_at_ms: int + raw: Mapping[str, Any] = field(default_factory=dict, repr=False, compare=False, metadata={'cmux_skip': True}) + + @dataclass(frozen=True) class RenderDeltaEvent(EventBase): __cmux_schema_path__: ClassVar[str] = 'events/render-delta/payload' @@ -2637,9 +2723,10 @@ class WorkspaceRenamedEvent(EventBase): Layout = Union[LayoutLeaf, LayoutSplit, LayoutStack] LayoutUndoResult = Union[LayoutUndoUndone, LayoutUndoConfirmationRequired] Pane = Union[LivePane, DeadPane] +PresenceAnchor = Union[PresenceAnchorCell, PresenceAnchorPoint] TerminalExitOutcome = Union[TerminalExitOutcomeExit, TerminalExitOutcomeSignal, TerminalExitOutcomeUnknown] -KnownEvent = Union[AgentChangedEvent, BellEvent, BrowserStateEvent, ClientAttachedEvent, ClientChangedEvent, ClientDetachedEvent, ClientListInvalidatedEvent, ColorsChangedEvent, ConfigReloadRequestedEvent, DaemonShutdownEvent, DetachedEvent, EmptyEvent, FrameEvent, FrontendProjectionChangedEvent, GraphicsStatusEvent, LayoutChangedEvent, MachineUsageChangedEvent, NotificationEvent, OutputEvent, OverflowEvent, PairingRequestedEvent, PairingResolvedEvent, PaneAddedEvent, PaneClosedEvent, RenderDeltaEvent, RenderStateEvent, ResizedEvent, ScreenAddedEvent, ScreenClosedEvent, ScreenRenamedEvent, ScrollChangedEvent, StatusEvent, SurfaceExitedEvent, SurfaceOutputEvent, SurfaceResizeFailedEvent, SurfaceResizedEvent, TabAddedEvent, TabClosedEvent, TabRenamedEvent, TerminalRegistryChangedEvent, TitleChangedEvent, TreeChangedEvent, VtStateEvent, WindowTitleRequestedEvent, WorkspaceAddedEvent, WorkspaceClosedEvent, WorkspaceMovedEvent, WorkspaceRenamedEvent] +KnownEvent = Union[AgentChangedEvent, BellEvent, BrowserStateEvent, ClientAttachedEvent, ClientChangedEvent, ClientDetachedEvent, ClientListInvalidatedEvent, ColorsChangedEvent, ConfigReloadRequestedEvent, DaemonShutdownEvent, DetachedEvent, EmptyEvent, FrameEvent, FrontendProjectionChangedEvent, GraphicsStatusEvent, LayoutChangedEvent, MachineUsageChangedEvent, NotificationEvent, OutputEvent, OverflowEvent, PairingRequestedEvent, PairingResolvedEvent, PaneAddedEvent, PaneClosedEvent, PresenceChangedEvent, RenderDeltaEvent, RenderStateEvent, ResizedEvent, ScreenAddedEvent, ScreenClosedEvent, ScreenRenamedEvent, ScrollChangedEvent, StatusEvent, SurfaceExitedEvent, SurfaceOutputEvent, SurfaceResizeFailedEvent, SurfaceResizedEvent, TabAddedEvent, TabClosedEvent, TabRenamedEvent, TerminalRegistryChangedEvent, TitleChangedEvent, TreeChangedEvent, VtStateEvent, WindowTitleRequestedEvent, WorkspaceAddedEvent, WorkspaceClosedEvent, WorkspaceMovedEvent, WorkspaceRenamedEvent] AnyEvent = Union[KnownEvent, UnknownEvent] __all__ = [ @@ -2658,6 +2745,7 @@ class WorkspaceRenamedEvent(EventBase): 'FrontendFocusTarget', 'NotificationLevel', 'PaneDirection', + 'PresenceHighlightMode', 'RenderGraphicFormat', 'RenderUnderline', 'ServerStatsWriterPhase', @@ -2717,6 +2805,11 @@ class WorkspaceRenamedEvent(EventBase): 'NotifyResult', 'PaneNeighborResult', 'PingResult', + 'PresenceAnchorCell', + 'PresenceAnchorPoint', + 'PresenceEntry', + 'PresenceHighlight', + 'PresenceListResult', 'ProcessInfoResult', 'ProviderWorkspaceMutationResult', 'ReadScreenResult', @@ -2828,6 +2921,9 @@ class WorkspaceRenamedEvent(EventBase): 'PairingResponseRequest', 'PaneNeighborRequest', 'PingRequest', + 'PresenceClearRequest', + 'PresenceListRequest', + 'PresenceUpdateRequest', 'ProcessInfoRequest', 'PutFrontendProjectionRequest', 'ReadScreenRequest', @@ -2898,6 +2994,7 @@ class WorkspaceRenamedEvent(EventBase): 'PairingResolvedEvent', 'PaneAddedEvent', 'PaneClosedEvent', + 'PresenceChangedEvent', 'RenderDeltaEvent', 'RenderStateEvent', 'ResizedEvent', @@ -2931,5 +3028,6 @@ class WorkspaceRenamedEvent(EventBase): 'Layout', 'LayoutUndoResult', 'Pane', + 'PresenceAnchor', 'TerminalExitOutcome', ] diff --git a/cmux-tui/bindings/python/tests/test_protocol.py b/cmux-tui/bindings/python/tests/test_protocol.py index 1e5cedb3cc67..4f4f7026a885 100644 --- a/cmux-tui/bindings/python/tests/test_protocol.py +++ b/cmux-tui/bindings/python/tests/test_protocol.py @@ -29,7 +29,7 @@ class GeneratedProtocolTests(unittest.TestCase): def test_protocol_inventory_is_exhaustive(self) -> None: self.assertEqual(MUX_PROTOCOL, 12) - self.assertEqual(len(COMMANDS), 106) + self.assertEqual(len(COMMANDS), 109) self.assertEqual(set(COMMANDS), set(SCHEMA["commands"])) self.assertEqual(set(EVENTS), set(SCHEMA["events"])) self.assertEqual(len(IR_SHA256), 64) diff --git a/cmux-tui/bindings/rust/src/convenience.rs b/cmux-tui/bindings/rust/src/convenience.rs index 303210f236ff..e56067095dc8 100644 --- a/cmux-tui/bindings/rust/src/convenience.rs +++ b/cmux-tui/bindings/rust/src/convenience.rs @@ -28,6 +28,7 @@ impl SubscriptionBuilder { pub fn open(self, client: &mut CmuxClient) -> Result { client.subscribe(SubscribeRequest { + presence_only: Optional::Missing, tree_events: Optional::Value(if self.deltas { SubscribeRequestTreeEvents::Deltas } else { diff --git a/cmux-tui/bindings/rust/src/generated/.cmux-sdk-manifest.json b/cmux-tui/bindings/rust/src/generated/.cmux-sdk-manifest.json index f1c90586beab..00a771618f58 100644 --- a/cmux-tui/bindings/rust/src/generated/.cmux-sdk-manifest.json +++ b/cmux-tui/bindings/rust/src/generated/.cmux-sdk-manifest.json @@ -2,32 +2,32 @@ "files": [ { "path": "commands.rs", - "sha256": "410a71a9d5aae2d172a2ebaafa648ee7d1818d5c7d8b8021ae6f0c712ec6e162", - "size": 64135 + "sha256": "473f8c98dfb31c76f5bc5729bfafd4ee4efac03865a2625233a6a33cdfdb2ab3", + "size": 65675 }, { "path": "events.rs", - "sha256": "abb0e24f04b7eae64c44ec70f5fa69fccce582d3367b01a00acdbe2bdb282e01", - "size": 42814 + "sha256": "58484176e85b99bff6db99a006c984a29ca6f829a23a89a2aab60eed3dd50ad6", + "size": 43746 }, { "path": "metadata.rs", - "sha256": "c7e253620e72109b520f903f1c93c983786252dc205b541386cc6dd181bd7e71", - "size": 38808 + "sha256": "4b364349224a6bb3a67bb18b034bb18db230431a4daf25d7fc94c9a3cf3a5436", + "size": 39796 }, { "path": "mod.rs", - "sha256": "5245a9df97c6324e32a9bb14364116dbbeb8c54dec5722e1453bac3645d6b54a", + "sha256": "7775fdb515c3780d29328e0f01482236c4fd8a345322eb2aa6b099a0d78c99f8", "size": 365 }, { "path": "types.rs", - "sha256": "790a60737d4f074cce8f15b38aeaef7f7c2181f92976ed5ad35441f8598cf7dd", - "size": 44354 + "sha256": "cea05b98cc1c4dff9e7267ce272276f1475c0b95456b5befcc97f852e99d5fe7", + "size": 45763 } ], "format": 1, - "ir_sha256": "8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86", + "ir_sha256": "e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663", "language": "rust", "mux_protocol": 12, "schema_version": 2 diff --git a/cmux-tui/bindings/rust/src/generated/commands.rs b/cmux-tui/bindings/rust/src/generated/commands.rs index fbcca6888a1b..3f8fba7c16a6 100644 --- a/cmux-tui/bindings/rust/src/generated/commands.rs +++ b/cmux-tui/bindings/rust/src/generated/commands.rs @@ -1,5 +1,5 @@ // This file is generated. Do not edit by hand. -// cmux-tui mux protocol 12, IR 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. +// cmux-tui mux protocol 12, IR e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. // The emitter owns this layout so generation is independent of the installed rustfmt. use super::metadata::*; @@ -788,6 +788,32 @@ pub struct PaneNeighborRequest { pub struct PingRequest { } +#[rustfmt::skip] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] +pub struct PresenceClearRequest { +} + +#[rustfmt::skip] +pub type PresenceClearResult = T::EmptyResult; + +#[rustfmt::skip] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] +pub struct PresenceListRequest { +} + +#[rustfmt::skip] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PresenceUpdateRequest { + #[serde(default, skip_serializing_if = "Optional::is_missing")] + pub highlight: Optional, + #[serde(default, skip_serializing_if = "Optional::is_missing")] + pub pointer: Optional, + pub surface: T::Id, +} + +#[rustfmt::skip] +pub type PresenceUpdateResult = T::EmptyResult; + #[rustfmt::skip] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ProcessInfoRequest { @@ -1239,6 +1265,8 @@ pub enum SubscribeRequestTreeEvents { #[rustfmt::skip] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] pub struct SubscribeRequest { + #[serde(default, skip_serializing_if = "Optional::is_missing")] + pub presence_only: Optional, #[serde(default, skip_serializing_if = "Optional::is_missing")] pub surface: Optional, #[serde(default, skip_serializing_if = "Optional::is_missing")] @@ -1621,6 +1649,18 @@ impl CmuxClient { self.execute(&PING_METADATA, &request) } + pub fn presence_clear(&mut self, request: PresenceClearRequest) -> Result { + self.execute(&PRESENCE_CLEAR_METADATA, &request) + } + + pub fn presence_list(&mut self, request: PresenceListRequest) -> Result { + self.execute(&PRESENCE_LIST_METADATA, &request) + } + + pub fn presence_update(&mut self, request: PresenceUpdateRequest) -> Result { + self.execute(&PRESENCE_UPDATE_METADATA, &request) + } + pub fn process_info(&mut self, request: ProcessInfoRequest) -> Result { self.execute(&PROCESS_INFO_METADATA, &request) } @@ -1825,6 +1865,10 @@ impl CmuxClient { } pub fn subscribe(&mut self, request: SubscribeRequest) -> Result { + if !request.presence_only.is_missing() { + self.require_protocol_field("subscribe", 12)?; + self.require_capability_field("subscribe", "presence-v1")?; + } if !request.surface.is_missing() { self.require_protocol_field("subscribe", 9)?; self.require_capability_field("subscribe", "surface-subscribe-filter")?; diff --git a/cmux-tui/bindings/rust/src/generated/events.rs b/cmux-tui/bindings/rust/src/generated/events.rs index f8b0e6560534..f175f1c3a630 100644 --- a/cmux-tui/bindings/rust/src/generated/events.rs +++ b/cmux-tui/bindings/rust/src/generated/events.rs @@ -1,5 +1,5 @@ // This file is generated. Do not edit by hand. -// cmux-tui mux protocol 12, IR 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. +// cmux-tui mux protocol 12, IR e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. // The emitter owns this layout so generation is independent of the installed rustfmt. use super::metadata::*; @@ -257,6 +257,20 @@ pub struct PaneClosedEvent { pub workspace: T::Id, } +#[rustfmt::skip] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PresenceChangedEvent { + pub client: u64, + pub color: u64, + pub generation: u64, + pub highlight: Nullable, + pub kind: Nullable, + pub name: Nullable, + pub pointer: Nullable, + pub surface: Nullable, + pub updated_at_ms: u64, +} + #[rustfmt::skip] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RenderDeltaEvent { @@ -554,6 +568,7 @@ pub enum Event { PairingResolved(PairingResolvedEvent), PaneAdded(PaneAddedEvent), PaneClosed(PaneClosedEvent), + PresenceChanged(PresenceChangedEvent), RenderDelta(RenderDeltaEvent), RenderState(RenderStateEvent), Resized(ResizedEvent), @@ -609,6 +624,7 @@ impl Event { Self::PairingResolved(_) => Some("pairing-resolved"), Self::PaneAdded(_) => Some("pane-added"), Self::PaneClosed(_) => Some("pane-closed"), + Self::PresenceChanged(_) => Some("presence-changed"), Self::RenderDelta(_) => Some("render-delta"), Self::RenderState(_) => Some("render-state"), Self::Resized(_) => Some("resized"), @@ -663,6 +679,7 @@ impl Event { Self::PairingResolved(_) => Some(&PAIRING_RESOLVED_EVENT_METADATA), Self::PaneAdded(_) => Some(&PANE_ADDED_EVENT_METADATA), Self::PaneClosed(_) => Some(&PANE_CLOSED_EVENT_METADATA), + Self::PresenceChanged(_) => Some(&PRESENCE_CHANGED_EVENT_METADATA), Self::RenderDelta(_) => Some(&RENDER_DELTA_EVENT_METADATA), Self::RenderState(_) => Some(&RENDER_STATE_EVENT_METADATA), Self::Resized(_) => Some(&RESIZED_EVENT_METADATA), @@ -888,6 +905,14 @@ pub fn decode_event(raw: Value) -> Event { decode_error: Some(error.to_string()), }), }, + Some("presence-changed") => match serde_json::from_value::(raw.clone()) { + Ok(event) => Event::PresenceChanged(event), + Err(error) => Event::Unknown(UnknownEvent { + name, + raw, + decode_error: Some(error.to_string()), + }), + }, Some("render-delta") => match serde_json::from_value::(raw.clone()) { Ok(event) => Event::RenderDelta(event), Err(error) => Event::Unknown(UnknownEvent { diff --git a/cmux-tui/bindings/rust/src/generated/metadata.rs b/cmux-tui/bindings/rust/src/generated/metadata.rs index 3ef4f3e43115..ae993f3ae021 100644 --- a/cmux-tui/bindings/rust/src/generated/metadata.rs +++ b/cmux-tui/bindings/rust/src/generated/metadata.rs @@ -1,12 +1,12 @@ // This file is generated. Do not edit by hand. -// cmux-tui mux protocol 12, IR 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. +// cmux-tui mux protocol 12, IR e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. // The emitter owns this layout so generation is independent of the installed rustfmt. use crate::{CommandMetadata, EventMetadata, ProfileMetadata, StreamMetadata}; pub const SDK_SCHEMA_VERSION: u32 = 2; pub const MUX_PROTOCOL_VERSION: u32 = 12; -pub const SDK_IR_SHA256: &str = "8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86"; +pub const SDK_IR_SHA256: &str = "e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663"; #[rustfmt::skip] pub const CONTROL_PROFILE: ProfileMetadata = ProfileMetadata { @@ -593,6 +593,33 @@ pub const PING_METADATA: CommandMetadata = CommandMetadata { stream: None, }; +#[rustfmt::skip] +pub const PRESENCE_CLEAR_METADATA: CommandMetadata = CommandMetadata { + name: "presence-clear", + since: 12, + capability: Some("presence-v1"), + authority: "control", + stream: None, +}; + +#[rustfmt::skip] +pub const PRESENCE_LIST_METADATA: CommandMetadata = CommandMetadata { + name: "presence-list", + since: 12, + capability: Some("presence-v1"), + authority: "control", + stream: None, +}; + +#[rustfmt::skip] +pub const PRESENCE_UPDATE_METADATA: CommandMetadata = CommandMetadata { + name: "presence-update", + since: 12, + capability: Some("presence-v1"), + authority: "control", + stream: None, +}; + #[rustfmt::skip] pub const PROCESS_INFO_METADATA: CommandMetadata = CommandMetadata { name: "process-info", @@ -1214,6 +1241,15 @@ pub const PANE_CLOSED_EVENT_METADATA: EventMetadata = EventMetadata { emission: "emitted", }; +#[rustfmt::skip] +pub const PRESENCE_CHANGED_EVENT_METADATA: EventMetadata = EventMetadata { + name: "presence-changed", + since: 12, + capability: Some("presence-v1"), + streams: &["subscribe"], + emission: "emitted", +}; + #[rustfmt::skip] pub const RENDER_DELTA_EVENT_METADATA: EventMetadata = EventMetadata { name: "render-delta", @@ -1433,6 +1469,6 @@ pub const WORKSPACE_RENAMED_EVENT_METADATA: EventMetadata = EventMetadata { #[rustfmt::skip] pub static PROFILES: &[ProfileMetadata] = &[CONTROL_PROFILE, FRONTEND_PROFILE, LOCAL_ADMIN_PROFILE, PROVIDER_AUTHORITY_PROFILE]; #[rustfmt::skip] -pub static COMMANDS: &[CommandMetadata] = &[APPLY_LAYOUT_METADATA, ATTACH_SURFACE_METADATA, BROWSER_ACTIVATE_METADATA, BROWSER_BACK_METADATA, BROWSER_FORWARD_METADATA, BROWSER_FRAME_PRESENTED_METADATA, BROWSER_INSERT_TEXT_METADATA, BROWSER_KEY_METADATA, BROWSER_KEY_PRESS_METADATA, BROWSER_MOUSE_METADATA, BROWSER_MOUSE_GUARDED_METADATA, BROWSER_NAVIGATE_METADATA, BROWSER_RELOAD_METADATA, BROWSER_WHEEL_METADATA, BROWSER_WHEEL_GUARDED_METADATA, CLEAR_HISTORY_METADATA, CLEAR_WINDOW_TITLE_METADATA, CLIENT_FOCUS_METADATA, CLOSE_PANE_METADATA, CLOSE_PROVIDER_MANAGED_WORKSPACE_METADATA, CLOSE_SCREEN_METADATA, CLOSE_SURFACE_METADATA, CLOSE_TERMINAL_METADATA, CLOSE_WORKSPACE_METADATA, COPY_METADATA, CREATE_SURFACE_WITH_RECEIPT_METADATA, CREATE_TERMINAL_METADATA, CREATE_WORKSPACE_METADATA, DETACH_ATTACHED_VIEW_METADATA, DETACH_CLIENT_METADATA, EXPORT_LAYOUT_METADATA, FOCUS_DIRECTION_METADATA, FOCUS_PANE_METADATA, GET_BROWSER_PROVIDER_METADATA, GET_CELL_PIXELS_METADATA, GET_FRONTEND_PROJECTION_METADATA, IDENTIFY_METADATA, IDS_METADATA, JOURNAL_FRONTEND_EVENT_METADATA, LIST_AGENTS_METADATA, LIST_CLIENTS_METADATA, LIST_TERMINALS_METADATA, LIST_WORKSPACES_METADATA, MACHINE_LISTENING_TCP_METADATA, MACHINE_USAGE_METADATA, MARK_WORKSPACES_PROVIDER_MANAGED_METADATA, MINT_TERMINAL_RENDERER_METADATA, MINT_TERMINAL_RENDERER_BY_TERMINAL_METADATA, MOVE_TAB_METADATA, MOVE_TERMINAL_METADATA, MOVE_WORKSPACE_METADATA, NEW_BROWSER_TAB_METADATA, NEW_PANE_METADATA, NEW_PANE_RIGHT_METADATA, NEW_SCREEN_METADATA, NEW_TAB_METADATA, NEW_WORKSPACE_METADATA, NOTIFY_METADATA, PAIRING_RESPONSE_METADATA, PANE_NEIGHBOR_METADATA, PING_METADATA, PROCESS_INFO_METADATA, PUT_FRONTEND_PROJECTION_METADATA, READ_SCREEN_METADATA, READ_SCROLLBACK_METADATA, REGISTER_BROWSER_PROVIDER_METADATA, RELEASE_ATTACHED_VIEW_SIZE_METADATA, RELEASE_SURFACE_SIZE_METADATA, RELOAD_CONFIG_METADATA, RENAME_PANE_METADATA, RENAME_PROVIDER_MANAGED_WORKSPACE_METADATA, RENAME_SCREEN_METADATA, RENAME_SURFACE_METADATA, RENAME_WORKSPACE_METADATA, REPORT_AGENT_METADATA, REPORT_FOCUS_METADATA, RESIZE_ATTACHED_VIEW_METADATA, RESIZE_SURFACE_METADATA, RESOLVE_TERMINAL_METADATA, RUN_METADATA, SCROLL_SURFACE_METADATA, SELECT_SCREEN_METADATA, SELECT_TAB_METADATA, SELECT_WORKSPACE_METADATA, SEND_METADATA, SEND_KEY_METADATA, SERVER_STATS_METADATA, SET_CELL_PIXELS_METADATA, SET_CLIENT_INFO_METADATA, SET_CLIENT_SIZING_METADATA, SET_DEFAULT_COLORS_METADATA, SET_RATIO_METADATA, SET_SPLIT_RATIO_METADATA, SET_VIEWPORT_PANE_WIDTH_METADATA, SET_WINDOW_TITLE_METADATA, SHUTDOWN_DAEMON_METADATA, SIDEBAR_PLUGIN_METADATA, SPLIT_METADATA, SUBSCRIBE_METADATA, SWAP_PANE_METADATA, TERMINAL_EVENTS_METADATA, UNDO_LAYOUT_METADATA, UNREGISTER_BROWSER_PROVIDER_METADATA, VT_STATE_METADATA, WAIT_FOR_METADATA, ZOOM_PANE_METADATA]; +pub static COMMANDS: &[CommandMetadata] = &[APPLY_LAYOUT_METADATA, ATTACH_SURFACE_METADATA, BROWSER_ACTIVATE_METADATA, BROWSER_BACK_METADATA, BROWSER_FORWARD_METADATA, BROWSER_FRAME_PRESENTED_METADATA, BROWSER_INSERT_TEXT_METADATA, BROWSER_KEY_METADATA, BROWSER_KEY_PRESS_METADATA, BROWSER_MOUSE_METADATA, BROWSER_MOUSE_GUARDED_METADATA, BROWSER_NAVIGATE_METADATA, BROWSER_RELOAD_METADATA, BROWSER_WHEEL_METADATA, BROWSER_WHEEL_GUARDED_METADATA, CLEAR_HISTORY_METADATA, CLEAR_WINDOW_TITLE_METADATA, CLIENT_FOCUS_METADATA, CLOSE_PANE_METADATA, CLOSE_PROVIDER_MANAGED_WORKSPACE_METADATA, CLOSE_SCREEN_METADATA, CLOSE_SURFACE_METADATA, CLOSE_TERMINAL_METADATA, CLOSE_WORKSPACE_METADATA, COPY_METADATA, CREATE_SURFACE_WITH_RECEIPT_METADATA, CREATE_TERMINAL_METADATA, CREATE_WORKSPACE_METADATA, DETACH_ATTACHED_VIEW_METADATA, DETACH_CLIENT_METADATA, EXPORT_LAYOUT_METADATA, FOCUS_DIRECTION_METADATA, FOCUS_PANE_METADATA, GET_BROWSER_PROVIDER_METADATA, GET_CELL_PIXELS_METADATA, GET_FRONTEND_PROJECTION_METADATA, IDENTIFY_METADATA, IDS_METADATA, JOURNAL_FRONTEND_EVENT_METADATA, LIST_AGENTS_METADATA, LIST_CLIENTS_METADATA, LIST_TERMINALS_METADATA, LIST_WORKSPACES_METADATA, MACHINE_LISTENING_TCP_METADATA, MACHINE_USAGE_METADATA, MARK_WORKSPACES_PROVIDER_MANAGED_METADATA, MINT_TERMINAL_RENDERER_METADATA, MINT_TERMINAL_RENDERER_BY_TERMINAL_METADATA, MOVE_TAB_METADATA, MOVE_TERMINAL_METADATA, MOVE_WORKSPACE_METADATA, NEW_BROWSER_TAB_METADATA, NEW_PANE_METADATA, NEW_PANE_RIGHT_METADATA, NEW_SCREEN_METADATA, NEW_TAB_METADATA, NEW_WORKSPACE_METADATA, NOTIFY_METADATA, PAIRING_RESPONSE_METADATA, PANE_NEIGHBOR_METADATA, PING_METADATA, PRESENCE_CLEAR_METADATA, PRESENCE_LIST_METADATA, PRESENCE_UPDATE_METADATA, PROCESS_INFO_METADATA, PUT_FRONTEND_PROJECTION_METADATA, READ_SCREEN_METADATA, READ_SCROLLBACK_METADATA, REGISTER_BROWSER_PROVIDER_METADATA, RELEASE_ATTACHED_VIEW_SIZE_METADATA, RELEASE_SURFACE_SIZE_METADATA, RELOAD_CONFIG_METADATA, RENAME_PANE_METADATA, RENAME_PROVIDER_MANAGED_WORKSPACE_METADATA, RENAME_SCREEN_METADATA, RENAME_SURFACE_METADATA, RENAME_WORKSPACE_METADATA, REPORT_AGENT_METADATA, REPORT_FOCUS_METADATA, RESIZE_ATTACHED_VIEW_METADATA, RESIZE_SURFACE_METADATA, RESOLVE_TERMINAL_METADATA, RUN_METADATA, SCROLL_SURFACE_METADATA, SELECT_SCREEN_METADATA, SELECT_TAB_METADATA, SELECT_WORKSPACE_METADATA, SEND_METADATA, SEND_KEY_METADATA, SERVER_STATS_METADATA, SET_CELL_PIXELS_METADATA, SET_CLIENT_INFO_METADATA, SET_CLIENT_SIZING_METADATA, SET_DEFAULT_COLORS_METADATA, SET_RATIO_METADATA, SET_SPLIT_RATIO_METADATA, SET_VIEWPORT_PANE_WIDTH_METADATA, SET_WINDOW_TITLE_METADATA, SHUTDOWN_DAEMON_METADATA, SIDEBAR_PLUGIN_METADATA, SPLIT_METADATA, SUBSCRIBE_METADATA, SWAP_PANE_METADATA, TERMINAL_EVENTS_METADATA, UNDO_LAYOUT_METADATA, UNREGISTER_BROWSER_PROVIDER_METADATA, VT_STATE_METADATA, WAIT_FOR_METADATA, ZOOM_PANE_METADATA]; #[rustfmt::skip] -pub static EVENTS: &[EventMetadata] = &[AGENT_CHANGED_EVENT_METADATA, BELL_EVENT_METADATA, BROWSER_STATE_EVENT_METADATA, CLIENT_ATTACHED_EVENT_METADATA, CLIENT_CHANGED_EVENT_METADATA, CLIENT_DETACHED_EVENT_METADATA, CLIENT_LIST_INVALIDATED_EVENT_METADATA, COLORS_CHANGED_EVENT_METADATA, CONFIG_RELOAD_REQUESTED_EVENT_METADATA, DAEMON_SHUTDOWN_EVENT_METADATA, DETACHED_EVENT_METADATA, EMPTY_EVENT_METADATA, FRAME_EVENT_METADATA, FRONTEND_PROJECTION_CHANGED_EVENT_METADATA, GRAPHICS_STATUS_EVENT_METADATA, LAYOUT_CHANGED_EVENT_METADATA, MACHINE_USAGE_CHANGED_EVENT_METADATA, NOTIFICATION_EVENT_METADATA, OUTPUT_EVENT_METADATA, OVERFLOW_EVENT_METADATA, PAIRING_REQUESTED_EVENT_METADATA, PAIRING_RESOLVED_EVENT_METADATA, PANE_ADDED_EVENT_METADATA, PANE_CLOSED_EVENT_METADATA, RENDER_DELTA_EVENT_METADATA, RENDER_STATE_EVENT_METADATA, RESIZED_EVENT_METADATA, SCREEN_ADDED_EVENT_METADATA, SCREEN_CLOSED_EVENT_METADATA, SCREEN_RENAMED_EVENT_METADATA, SCROLL_CHANGED_EVENT_METADATA, STATUS_EVENT_METADATA, SURFACE_EXITED_EVENT_METADATA, SURFACE_OUTPUT_EVENT_METADATA, SURFACE_RESIZE_FAILED_EVENT_METADATA, SURFACE_RESIZED_EVENT_METADATA, TAB_ADDED_EVENT_METADATA, TAB_CLOSED_EVENT_METADATA, TAB_RENAMED_EVENT_METADATA, TERMINAL_REGISTRY_CHANGED_EVENT_METADATA, TITLE_CHANGED_EVENT_METADATA, TREE_CHANGED_EVENT_METADATA, VT_STATE_EVENT_METADATA, WINDOW_TITLE_REQUESTED_EVENT_METADATA, WORKSPACE_ADDED_EVENT_METADATA, WORKSPACE_CLOSED_EVENT_METADATA, WORKSPACE_MOVED_EVENT_METADATA, WORKSPACE_RENAMED_EVENT_METADATA]; +pub static EVENTS: &[EventMetadata] = &[AGENT_CHANGED_EVENT_METADATA, BELL_EVENT_METADATA, BROWSER_STATE_EVENT_METADATA, CLIENT_ATTACHED_EVENT_METADATA, CLIENT_CHANGED_EVENT_METADATA, CLIENT_DETACHED_EVENT_METADATA, CLIENT_LIST_INVALIDATED_EVENT_METADATA, COLORS_CHANGED_EVENT_METADATA, CONFIG_RELOAD_REQUESTED_EVENT_METADATA, DAEMON_SHUTDOWN_EVENT_METADATA, DETACHED_EVENT_METADATA, EMPTY_EVENT_METADATA, FRAME_EVENT_METADATA, FRONTEND_PROJECTION_CHANGED_EVENT_METADATA, GRAPHICS_STATUS_EVENT_METADATA, LAYOUT_CHANGED_EVENT_METADATA, MACHINE_USAGE_CHANGED_EVENT_METADATA, NOTIFICATION_EVENT_METADATA, OUTPUT_EVENT_METADATA, OVERFLOW_EVENT_METADATA, PAIRING_REQUESTED_EVENT_METADATA, PAIRING_RESOLVED_EVENT_METADATA, PANE_ADDED_EVENT_METADATA, PANE_CLOSED_EVENT_METADATA, PRESENCE_CHANGED_EVENT_METADATA, RENDER_DELTA_EVENT_METADATA, RENDER_STATE_EVENT_METADATA, RESIZED_EVENT_METADATA, SCREEN_ADDED_EVENT_METADATA, SCREEN_CLOSED_EVENT_METADATA, SCREEN_RENAMED_EVENT_METADATA, SCROLL_CHANGED_EVENT_METADATA, STATUS_EVENT_METADATA, SURFACE_EXITED_EVENT_METADATA, SURFACE_OUTPUT_EVENT_METADATA, SURFACE_RESIZE_FAILED_EVENT_METADATA, SURFACE_RESIZED_EVENT_METADATA, TAB_ADDED_EVENT_METADATA, TAB_CLOSED_EVENT_METADATA, TAB_RENAMED_EVENT_METADATA, TERMINAL_REGISTRY_CHANGED_EVENT_METADATA, TITLE_CHANGED_EVENT_METADATA, TREE_CHANGED_EVENT_METADATA, VT_STATE_EVENT_METADATA, WINDOW_TITLE_REQUESTED_EVENT_METADATA, WORKSPACE_ADDED_EVENT_METADATA, WORKSPACE_CLOSED_EVENT_METADATA, WORKSPACE_MOVED_EVENT_METADATA, WORKSPACE_RENAMED_EVENT_METADATA]; diff --git a/cmux-tui/bindings/rust/src/generated/mod.rs b/cmux-tui/bindings/rust/src/generated/mod.rs index 97892b160fbd..9265f257d974 100644 --- a/cmux-tui/bindings/rust/src/generated/mod.rs +++ b/cmux-tui/bindings/rust/src/generated/mod.rs @@ -1,5 +1,5 @@ // This file is generated. Do not edit by hand. -// cmux-tui mux protocol 12, IR 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. +// cmux-tui mux protocol 12, IR e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. // The emitter owns this layout so generation is independent of the installed rustfmt. mod commands; diff --git a/cmux-tui/bindings/rust/src/generated/types.rs b/cmux-tui/bindings/rust/src/generated/types.rs index be7077aa1e9d..4284fbda5d48 100644 --- a/cmux-tui/bindings/rust/src/generated/types.rs +++ b/cmux-tui/bindings/rust/src/generated/types.rs @@ -1,5 +1,5 @@ // This file is generated. Do not edit by hand. -// cmux-tui mux protocol 12, IR 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. +// cmux-tui mux protocol 12, IR e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. // The emitter owns this layout so generation is independent of the installed rustfmt. use crate::{Nullable, Optional}; @@ -639,6 +639,61 @@ pub struct PingResult { pub version: String, } +#[rustfmt::skip] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(tag = "kind")] +pub enum PresenceAnchor { + #[serde(rename = "cell")] + Cell { + col: u32, + row: u32, + #[serde(default, deserialize_with = "crate::presence::deserialize_optional_non_null", skip_serializing_if = "Option::is_none")] + scroll_offset: Option, + }, + #[serde(rename = "point")] + Point { + x: f64, + y: f64, + }, +} + +#[rustfmt::skip] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PresenceEntry { + pub client: u64, + pub color: u64, + pub generation: u64, + pub highlight: Nullable, + pub kind: Nullable, + pub name: Nullable, + pub pointer: Nullable, + pub surface: Nullable, + pub updated_at_ms: u64, +} + +#[rustfmt::skip] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PresenceHighlight { + pub end: PresenceAnchor, + pub mode: PresenceHighlightMode, + pub start: PresenceAnchor, +} + +#[rustfmt::skip] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum PresenceHighlightMode { + #[serde(rename = "laser")] + Laser, + #[serde(rename = "pin")] + Pin, +} + +#[rustfmt::skip] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PresenceListResult { + pub entries: Vec, +} + #[rustfmt::skip] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ProcessInfoResult { diff --git a/cmux-tui/bindings/typescript/src/raw/generated/.cmux-sdk-manifest.json b/cmux-tui/bindings/typescript/src/raw/generated/.cmux-sdk-manifest.json index bc5982379fd5..32bda773e38f 100644 --- a/cmux-tui/bindings/typescript/src/raw/generated/.cmux-sdk-manifest.json +++ b/cmux-tui/bindings/typescript/src/raw/generated/.cmux-sdk-manifest.json @@ -2,32 +2,32 @@ "files": [ { "path": "commands.ts", - "sha256": "4ea54b53632f7f7ca8680a51a03233b1bb143e579f20af254f9f7b65a5287cae", - "size": 50804 + "sha256": "c4e5a6ab280c67d470f8fc1df1214eb0bafd0c6f11a3d02d4689064f4f3679d7", + "size": 52078 }, { "path": "events.ts", - "sha256": "628ee75d53bdbba5e3f848a8ce7618fc4f3f1aa23662f8effefb71c800204929", - "size": 15538 + "sha256": "e570d743d0a44c3ba0eec3289570213f1103aa2034b164b7d4e466e07a8324a6", + "size": 15978 }, { "path": "index.ts", - "sha256": "a7a3596e1488aa900bd524098cc34c6545d59bf4d8aad43e737fd258a98216e4", + "sha256": "220ae3a21f200ebd5e20a1bc2561fd4a03752492f50f3846a7a7ee0b6a8049b0", "size": 272 }, { "path": "metadata.ts", - "sha256": "3b4269f07ffdfc091be557b724ec4f20bdcf22a9cb3bc7db6b11c672fe17ef3d", - "size": 294531 + "sha256": "6a7da8200e213c6b0d9250d1ad10fb27f009a2dcca62e935809d91a9f5f0a87b", + "size": 303563 }, { "path": "types.ts", - "sha256": "97256f7c439812c7bb227cae56c2bc23115777095fb29c390d0e85833fdfcfce", - "size": 20007 + "sha256": "74434e16763be155c9e29727be3cd092123ad7c240c87efed96fc05fa9cd1048", + "size": 20760 } ], "format": 1, - "ir_sha256": "8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86", + "ir_sha256": "e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663", "language": "typescript", "mux_protocol": 12, "schema_version": 2 diff --git a/cmux-tui/bindings/typescript/src/raw/generated/commands.ts b/cmux-tui/bindings/typescript/src/raw/generated/commands.ts index 2c5f5a6a549a..b4125be1c0c5 100644 --- a/cmux-tui/bindings/typescript/src/raw/generated/commands.ts +++ b/cmux-tui/bindings/typescript/src/raw/generated/commands.ts @@ -1,5 +1,5 @@ /* This file is generated. Do not edit by hand. */ -/* cmux-tui mux protocol 12, IR 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. */ +/* cmux-tui mux protocol 12, IR e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. */ import type * as T from "./types.js"; @@ -554,6 +554,26 @@ export interface PingRequest extends CmuxRequestBase { cmd: "ping"; } +/** Protocol v12; authority: control. */ +export interface PresenceClearRequest extends CmuxRequestBase { + cmd: "presence-clear"; +} +export type PresenceClearResult = T.EmptyResult; + +/** Protocol v12; authority: control. */ +export interface PresenceListRequest extends CmuxRequestBase { + cmd: "presence-list"; +} + +/** Protocol v12; authority: control. */ +export interface PresenceUpdateRequest extends CmuxRequestBase { + cmd: "presence-update"; + "highlight"?: (T.PresenceHighlight) | null; + "pointer"?: (T.PresenceAnchor) | null; + "surface": T.Id; +} +export type PresenceUpdateResult = T.EmptyResult; + /** Protocol v6; authority: control. */ export interface ProcessInfoRequest extends CmuxRequestBase { cmd: "process-info"; @@ -890,6 +910,7 @@ export type SplitResult = T.SurfaceResult; /** Protocol v5; authority: frontend. */ export interface SubscribeRequest extends CmuxRequestBase { cmd: "subscribe"; + "presence_only"?: (boolean) | null; "surface"?: (T.Id) | null; "tree_events"?: ("coarse" | "deltas") | null; } @@ -1010,6 +1031,9 @@ export type CmuxRequest = | PairingResponseRequest | PaneNeighborRequest | PingRequest + | PresenceClearRequest + | PresenceListRequest + | PresenceUpdateRequest | ProcessInfoRequest | PutFrontendProjectionRequest | ReadScreenRequest @@ -1546,6 +1570,30 @@ export interface CmuxCommandDefinitionMap { capability: null; stream: null; }; + "presence-clear": { + request: PresenceClearRequest; + result: PresenceClearResult; + authority: "control"; + since: 12; + capability: "presence-v1"; + stream: null; + }; + "presence-list": { + request: PresenceListRequest; + result: T.PresenceListResult; + authority: "control"; + since: 12; + capability: "presence-v1"; + stream: null; + }; + "presence-update": { + request: PresenceUpdateRequest; + result: PresenceUpdateResult; + authority: "control"; + since: 12; + capability: "presence-v1"; + stream: null; + }; "process-info": { request: ProcessInfoRequest; result: T.ProcessInfoResult; diff --git a/cmux-tui/bindings/typescript/src/raw/generated/events.ts b/cmux-tui/bindings/typescript/src/raw/generated/events.ts index e63793c09a86..b4ae1f62a141 100644 --- a/cmux-tui/bindings/typescript/src/raw/generated/events.ts +++ b/cmux-tui/bindings/typescript/src/raw/generated/events.ts @@ -1,5 +1,5 @@ /* This file is generated. Do not edit by hand. */ -/* cmux-tui mux protocol 12, IR 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. */ +/* cmux-tui mux protocol 12, IR e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. */ import type * as T from "./types.js"; @@ -181,6 +181,19 @@ export type PaneClosedEvent = { event: "pane-closed" } & { "workspace": T.Id; }; +/** Protocol v12; emission: emitted; streams: subscribe. */ +export type PresenceChangedEvent = { event: "presence-changed" } & { + "client": bigint; + "color": bigint; + "generation": bigint; + "highlight": (T.PresenceHighlight) | null; + "kind": (string) | null; + "name": (string) | null; + "pointer": (T.PresenceAnchor) | null; + "surface": (T.Id) | null; + "updated_at_ms": bigint; +}; + /** Protocol v7; emission: emitted; streams: attach-render. */ export type RenderDeltaEvent = { event: "render-delta" } & { "cursor": T.RenderCursor; @@ -425,6 +438,7 @@ export type KnownCmuxEvent = | PairingResolvedEvent | PaneAddedEvent | PaneClosedEvent + | PresenceChangedEvent | RenderDeltaEvent | RenderStateEvent | ResizedEvent @@ -473,6 +487,7 @@ export type KnownSubscribeEvent = | PairingResolvedEvent | PaneAddedEvent | PaneClosedEvent + | PresenceChangedEvent | ScreenAddedEvent | ScreenClosedEvent | ScreenRenamedEvent diff --git a/cmux-tui/bindings/typescript/src/raw/generated/index.ts b/cmux-tui/bindings/typescript/src/raw/generated/index.ts index cf9e31d28b2b..e00f903a7242 100644 --- a/cmux-tui/bindings/typescript/src/raw/generated/index.ts +++ b/cmux-tui/bindings/typescript/src/raw/generated/index.ts @@ -1,5 +1,5 @@ /* This file is generated. Do not edit by hand. */ -/* cmux-tui mux protocol 12, IR 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. */ +/* cmux-tui mux protocol 12, IR e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. */ export * from "./types.js"; export * from "./commands.js"; diff --git a/cmux-tui/bindings/typescript/src/raw/generated/metadata.ts b/cmux-tui/bindings/typescript/src/raw/generated/metadata.ts index 7d4cf06aceaa..751269583c7f 100644 --- a/cmux-tui/bindings/typescript/src/raw/generated/metadata.ts +++ b/cmux-tui/bindings/typescript/src/raw/generated/metadata.ts @@ -1,10 +1,10 @@ /* This file is generated. Do not edit by hand. */ -/* cmux-tui mux protocol 12, IR 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. */ +/* cmux-tui mux protocol 12, IR e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. */ export const SDK_SCHEMA_VERSION = 2 as const; export const MUX_PROTOCOL_VERSION = 12 as const; -export const SDK_IR_SHA256 = "8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86" as const; +export const SDK_IR_SHA256 = "e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663" as const; export const PROTOCOL = { "id_type": "uint64", "javascript_id_policy": "All protocol identifiers are uint64 JSON numbers. JavaScript and TypeScript SDKs must decode them losslessly as bigint (or validated decimal strings at their public boundary), and must not expose IEEE-754 number ids. Pairing request ids, revisions, timestamps, frame sequences, and reservation ids follow the same rule.", @@ -719,6 +719,36 @@ export const COMMAND_METADATA = { "stream": null, "constraints": [] }, + "presence-clear": { + "authority": "control", + "since": 12, + "capability": "presence-v1", + "fields": {}, + "stream": null, + "constraints": [] + }, + "presence-list": { + "authority": "control", + "since": 12, + "capability": "presence-v1", + "fields": {}, + "stream": null, + "constraints": [ + "Pointers idle for 60 seconds are dropped unless their highlight mode is pin." + ] + }, + "presence-update": { + "authority": "control", + "since": 12, + "capability": "presence-v1", + "fields": {}, + "stream": null, + "constraints": [ + "Replaces this connection's whole presence state; omitted pointer or highlight means none.", + "At most 240 updates per second per connection; more fail with a bad request error.", + "Never journaled; a server restart forgets all presence." + ] + }, "process-info": { "authority": "control", "since": 6, @@ -1143,6 +1173,10 @@ export const COMMAND_METADATA = { "since": 5, "capability": null, "fields": { + "presence_only": { + "since": 12, + "capability": "presence-v1" + }, "surface": { "since": 9, "capability": "surface-subscribe-filter" @@ -1488,6 +1522,14 @@ export const EVENT_METADATA = { ], "emission": "emitted" }, + "presence-changed": { + "since": 12, + "capability": "presence-v1", + "streams": [ + "subscribe" + ], + "emission": "emitted" + }, "render-delta": { "since": 7, "capability": null, @@ -3943,6 +3985,216 @@ export const TYPE_SCHEMAS: Readonly> = { }, "kind": "object" }, + "PresenceAnchor": { + "kind": "tagged_union", + "tag": "kind", + "variants": { + "cell": { + "additional_properties": false, + "fields": { + "col": { + "nullable": false, + "presence": "required", + "type": { + "kind": "scalar", + "name": "uint32" + } + }, + "kind": { + "nullable": false, + "presence": "required", + "type": { + "kind": "literal", + "value": "cell" + } + }, + "row": { + "nullable": false, + "presence": "required", + "type": { + "kind": "scalar", + "name": "uint32" + } + }, + "scroll_offset": { + "nullable": false, + "presence": "optional", + "type": { + "kind": "scalar", + "name": "uint64" + } + } + }, + "kind": "object" + }, + "point": { + "additional_properties": false, + "fields": { + "kind": { + "nullable": false, + "presence": "required", + "type": { + "kind": "literal", + "value": "point" + } + }, + "x": { + "nullable": false, + "presence": "required", + "type": { + "kind": "scalar", + "name": "float64" + } + }, + "y": { + "nullable": false, + "presence": "required", + "type": { + "kind": "scalar", + "name": "float64" + } + } + }, + "kind": "object" + } + } + }, + "PresenceEntry": { + "additional_properties": false, + "constraints": [ + "surface is null only in presence-changed after a clear, disconnect, or surface exit; presence-list never returns such entries.", + "color is a palette slot in 0..8, stable for the connection." + ], + "fields": { + "client": { + "nullable": false, + "presence": "required", + "type": { + "kind": "scalar", + "name": "uint64" + } + }, + "color": { + "nullable": false, + "presence": "required", + "type": { + "kind": "scalar", + "name": "uint64" + } + }, + "generation": { + "nullable": false, + "presence": "required", + "type": { + "kind": "scalar", + "name": "uint64" + } + }, + "highlight": { + "nullable": true, + "presence": "required", + "type": { + "kind": "ref", + "name": "PresenceHighlight" + } + }, + "kind": { + "nullable": true, + "presence": "required", + "type": { + "kind": "scalar", + "name": "string" + } + }, + "name": { + "nullable": true, + "presence": "required", + "type": { + "kind": "scalar", + "name": "string" + } + }, + "pointer": { + "nullable": true, + "presence": "required", + "type": { + "kind": "ref", + "name": "PresenceAnchor" + } + }, + "surface": { + "nullable": true, + "presence": "required", + "type": { + "kind": "ref", + "name": "Id" + } + }, + "updated_at_ms": { + "nullable": false, + "presence": "required", + "type": { + "kind": "scalar", + "name": "uint64" + } + } + }, + "kind": "object" + }, + "PresenceHighlight": { + "additional_properties": false, + "fields": { + "end": { + "nullable": false, + "presence": "required", + "type": { + "kind": "ref", + "name": "PresenceAnchor" + } + }, + "mode": { + "nullable": false, + "presence": "required", + "type": { + "kind": "ref", + "name": "PresenceHighlightMode" + } + }, + "start": { + "nullable": false, + "presence": "required", + "type": { + "kind": "ref", + "name": "PresenceAnchor" + } + } + }, + "kind": "object" + }, + "PresenceHighlightMode": { + "kind": "enum", + "values": [ + "laser", + "pin" + ] + }, + "PresenceListResult": { + "additional_properties": false, + "fields": { + "entries": { + "nullable": false, + "presence": "required", + "type": { + "items": { + "kind": "ref", + "name": "PresenceEntry" + }, + "kind": "array" + } + } + }, + "kind": "object" + }, "ProcessInfoResult": { "additional_properties": false, "fields": { @@ -9323,6 +9575,64 @@ export const COMMAND_SCHEMAS: Readonly> = { "name": "PingResult" } }, + "presence-clear": { + "request": { + "additional_properties": false, + "fields": {}, + "kind": "object" + }, + "result": { + "kind": "ref", + "name": "EmptyResult" + } + }, + "presence-list": { + "request": { + "additional_properties": false, + "fields": {}, + "kind": "object" + }, + "result": { + "kind": "ref", + "name": "PresenceListResult" + } + }, + "presence-update": { + "request": { + "additional_properties": false, + "fields": { + "highlight": { + "nullable": true, + "presence": "optional", + "type": { + "kind": "ref", + "name": "PresenceHighlight" + } + }, + "pointer": { + "nullable": true, + "presence": "optional", + "type": { + "kind": "ref", + "name": "PresenceAnchor" + } + }, + "surface": { + "nullable": false, + "presence": "required", + "type": { + "kind": "ref", + "name": "Id" + } + } + }, + "kind": "object" + }, + "result": { + "kind": "ref", + "name": "EmptyResult" + } + }, "process-info": { "request": { "additional_properties": false, @@ -10957,6 +11267,16 @@ export const COMMAND_SCHEMAS: Readonly> = { "request": { "additional_properties": false, "fields": { + "presence_only": { + "capability": "presence-v1", + "nullable": true, + "presence": "optional", + "since": 12, + "type": { + "kind": "scalar", + "name": "boolean" + } + }, "surface": { "capability": "surface-subscribe-filter", "default": null, @@ -12203,6 +12523,92 @@ export const EVENT_SCHEMAS: Readonly> = { }, "kind": "object" }, + "presence-changed": { + "additional_properties": false, + "fields": { + "client": { + "nullable": false, + "presence": "required", + "type": { + "kind": "scalar", + "name": "uint64" + } + }, + "color": { + "nullable": false, + "presence": "required", + "type": { + "kind": "scalar", + "name": "uint64" + } + }, + "event": { + "nullable": false, + "presence": "required", + "type": { + "kind": "literal", + "value": "presence-changed" + } + }, + "generation": { + "nullable": false, + "presence": "required", + "type": { + "kind": "scalar", + "name": "uint64" + } + }, + "highlight": { + "nullable": true, + "presence": "required", + "type": { + "kind": "ref", + "name": "PresenceHighlight" + } + }, + "kind": { + "nullable": true, + "presence": "required", + "type": { + "kind": "scalar", + "name": "string" + } + }, + "name": { + "nullable": true, + "presence": "required", + "type": { + "kind": "scalar", + "name": "string" + } + }, + "pointer": { + "nullable": true, + "presence": "required", + "type": { + "kind": "ref", + "name": "PresenceAnchor" + } + }, + "surface": { + "nullable": true, + "presence": "required", + "type": { + "kind": "ref", + "name": "Id" + } + }, + "updated_at_ms": { + "nullable": false, + "presence": "required", + "type": { + "kind": "scalar", + "name": "uint64" + } + } + }, + "kind": "object" + }, "render-delta": { "additional_properties": false, "constraints": [ diff --git a/cmux-tui/bindings/typescript/src/raw/generated/types.ts b/cmux-tui/bindings/typescript/src/raw/generated/types.ts index 47987aabb6b8..c8463e5b10a0 100644 --- a/cmux-tui/bindings/typescript/src/raw/generated/types.ts +++ b/cmux-tui/bindings/typescript/src/raw/generated/types.ts @@ -1,5 +1,5 @@ /* This file is generated. Do not edit by hand. */ -/* cmux-tui mux protocol 12, IR 8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86. */ +/* cmux-tui mux protocol 12, IR e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663. */ /** JSON accepted by the wire codec. bigint is serialized as an exact JSON integer. */ @@ -385,6 +385,41 @@ export type PingResult = { "version": string; }; +export type PresenceAnchor = ({ "kind": "cell" } & { + "col": number; + "kind": "cell"; + "row": number; + "scroll_offset"?: bigint; +}) | ({ "kind": "point" } & { + "kind": "point"; + "x": number; + "y": number; +}); + +export type PresenceEntry = { + "client": bigint; + "color": bigint; + "generation": bigint; + "highlight": (PresenceHighlight) | null; + "kind": (string) | null; + "name": (string) | null; + "pointer": (PresenceAnchor) | null; + "surface": (Id) | null; + "updated_at_ms": bigint; +}; + +export type PresenceHighlight = { + "end": PresenceAnchor; + "mode": PresenceHighlightMode; + "start": PresenceAnchor; +}; + +export type PresenceHighlightMode = "laser" | "pin"; + +export type PresenceListResult = { + "entries": Array; +}; + export type ProcessInfoResult = { "command": (string) | null; "cwd": (string) | null; diff --git a/cmux-tui/bindings/typescript/test/generated.test.ts b/cmux-tui/bindings/typescript/test/generated.test.ts index 7624963a3a09..d32f8449e8a0 100644 --- a/cmux-tui/bindings/typescript/test/generated.test.ts +++ b/cmux-tui/bindings/typescript/test/generated.test.ts @@ -12,8 +12,8 @@ import { test("generated protocol coverage matches the canonical v12 IR", () => { assert.equal(MUX_PROTOCOL_VERSION, 12); assert.equal(SDK_SCHEMA_VERSION, 2); - assert.equal(Object.keys(COMMAND_METADATA).length, 106); - assert.equal(Object.keys(EVENT_METADATA).length, 48); + assert.equal(Object.keys(COMMAND_METADATA).length, 109); + assert.equal(Object.keys(EVENT_METADATA).length, 49); assert.equal(SDK_IR_SHA256.length, 64); assert.deepEqual(Object.keys(PROFILES).sort(), [ "control", @@ -27,7 +27,7 @@ test("generated active events exclude serialized-only shapes", () => { const emitted = Object.entries(EVENT_METADATA) .filter(([, metadata]) => metadata.emission === "emitted") .map(([name]) => name); - assert.equal(emitted.length, 47); + assert.equal(emitted.length, 48); assert.equal(EVENT_METADATA["machine-usage-changed"].emission, "emitted"); assert.equal(emitted.includes("machine-usage-changed"), true); assert.equal(EVENT_METADATA["client-list-invalidated"].emission, "serialized-never-emitted"); diff --git a/cmux-tui/bindings/zig/examples/watch.zig b/cmux-tui/bindings/zig/examples/watch.zig index 45f6671ff687..9521ab44249f 100644 --- a/cmux-tui/bindings/zig/examples/watch.zig +++ b/cmux-tui/bindings/zig/examples/watch.zig @@ -126,11 +126,11 @@ test "package consumer imports handwritten root and generated raw module" { wheel.pointer_frame_seq, ); try std.testing.expectEqual( - @as(usize, 106), + @as(usize, 109), cmux.raw.protocol.command_count, ); try std.testing.expectEqual( - @as(usize, 48), + @as(usize, 49), cmux.raw.protocol.event_count, ); try std.testing.expect( diff --git a/cmux-tui/bindings/zig/src/raw.zig b/cmux-tui/bindings/zig/src/raw.zig index 0de8e4bc9f7c..b1d7d5d3ff7f 100644 --- a/cmux-tui/bindings/zig/src/raw.zig +++ b/cmux-tui/bindings/zig/src/raw.zig @@ -44,7 +44,7 @@ test { _ = @import("raw/wire_presence_test.zig"); _ = @import("raw/generated/presence_test.zig"); std.testing.refAllDecls(protocol); - try std.testing.expectEqual(@as(usize, 106), protocol.command_count); + try std.testing.expectEqual(@as(usize, 109), protocol.command_count); for ([_][]const u8{ "browser-frame-presented", "browser-key-press", @@ -64,5 +64,5 @@ test { } try std.testing.expect(found); } - try std.testing.expectEqual(@as(usize, 48), protocol.event_count); + try std.testing.expectEqual(@as(usize, 49), protocol.event_count); } diff --git a/cmux-tui/bindings/zig/src/raw/generated/.cmux-sdk-manifest.json b/cmux-tui/bindings/zig/src/raw/generated/.cmux-sdk-manifest.json index bf7f153f7cc3..7e63af7ec588 100644 --- a/cmux-tui/bindings/zig/src/raw/generated/.cmux-sdk-manifest.json +++ b/cmux-tui/bindings/zig/src/raw/generated/.cmux-sdk-manifest.json @@ -2,17 +2,17 @@ "files": [ { "path": "presence_test.zig", - "sha256": "1525e8bd8cd98b0becc9a3c33e2ef4f7d8b85657f9de015d8584152b399fd5bf", - "size": 6955 + "sha256": "dae12cd03e634129be35dc2471a633c494719a66c90950602e10889be59ec809", + "size": 7037 }, { "path": "protocol.zig", - "sha256": "5aac102b32437555f3fe9a0f4800fc6725775f0e9e8ca81d0118b7323c28a4a6", - "size": 164736 + "sha256": "9d42fc74540bd94dfaceb50d1a8c501a2c36c99e6c99ea8b681ceaeb5ffeea69", + "size": 169659 } ], "format": 1, - "ir_sha256": "8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86", + "ir_sha256": "e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663", "language": "zig", "mux_protocol": 12, "schema_version": 2 diff --git a/cmux-tui/bindings/zig/src/raw/generated/presence_test.zig b/cmux-tui/bindings/zig/src/raw/generated/presence_test.zig index b1aaafabda08..8055687238fe 100644 --- a/cmux-tui/bindings/zig/src/raw/generated/presence_test.zig +++ b/cmux-tui/bindings/zig/src/raw/generated/presence_test.zig @@ -77,6 +77,7 @@ test "every generated optional non-null field rejects explicit null" { try expectExplicitNullRejected(protocol.LayoutUndoUndone, "confirmation_required"); try expectExplicitNullRejected(protocol.LivePane, "focused_at"); try expectExplicitNullRejected(protocol.LivePane, "short_id"); + try expectExplicitNullRejected(protocol.PresenceAnchorCell, "scroll_offset"); try expectExplicitNullRejected(protocol.RenderGraphicPlacement, "anchor_col"); try expectExplicitNullRejected(protocol.RenderGraphicPlacement, "anchor_row"); try expectExplicitNullRejected(protocol.RenderGraphics, "images"); diff --git a/cmux-tui/bindings/zig/src/raw/generated/protocol.zig b/cmux-tui/bindings/zig/src/raw/generated/protocol.zig index 5bcb0c765817..129f7a9a23b9 100644 --- a/cmux-tui/bindings/zig/src/raw/generated/protocol.zig +++ b/cmux-tui/bindings/zig/src/raw/generated/protocol.zig @@ -7,7 +7,7 @@ const client_runtime = @import("../client.zig"); pub const schema_version: u16 = 2; pub const mux_protocol: u16 = 12; -pub const ir_sha256 = "8ff10c20fef75f9aaa1498eaf5e1107f084bdcf3febdcf8806fb4e7fc1c90b86"; +pub const ir_sha256 = "e5f9d207cfb314bdcf3e7ab96b235e8b98188fbe2f662e2738ec490ab0279663"; pub const AgentRecord = struct { session: wire.Nullable([]const u8), @@ -774,6 +774,86 @@ pub const PingResult = struct { version: []const u8, }; +pub const PresenceAnchorCell = struct { + col: u32, + row: u32, + scroll_offset: ?u64 = null, + + pub const cmux_wire_optional_nonnull_fields = [_][]const u8{ + "scroll_offset", + }; +}; + +pub const PresenceAnchorPoint = struct { + x: f64, + y: f64, +}; + +pub const PresenceAnchor = union(enum) { + cell: PresenceAnchorCell, + point: PresenceAnchorPoint, + + pub const cmux_wire_custom_union = true; + + pub fn cmuxEncode(self: @This(), allocator: std.mem.Allocator) !wire.Value { + return switch (self) { + .cell => |payload| try wire.encodeTagged(allocator, "kind", "cell", payload), + .point => |payload| try wire.encodeTagged(allocator, "kind", "point", payload), + }; + } + + pub fn cmuxDecode(allocator: std.mem.Allocator, value: wire.Value) !@This() { + const tag_value = try wire.objectString(value, "kind"); + if (std.mem.eql(u8, tag_value, "cell")) { + return .{ .cell = try wire.decodeLeaky(PresenceAnchorCell, allocator, value) }; + } + if (std.mem.eql(u8, tag_value, "point")) { + return .{ .point = try wire.decodeLeaky(PresenceAnchorPoint, allocator, value) }; + } + return error.UnknownUnionVariant; + } +}; + +pub const PresenceEntry = struct { + client: u64, + color: u64, + generation: u64, + highlight: wire.Nullable(PresenceHighlight), + kind: wire.Nullable([]const u8), + name: wire.Nullable([]const u8), + pointer: wire.Nullable(PresenceAnchor), + surface: wire.Nullable(Id), + updated_at_ms: u64, +}; + +pub const PresenceHighlight = struct { + end: PresenceAnchor, + mode: PresenceHighlightMode, + start: PresenceAnchor, +}; + +pub const PresenceHighlightMode = enum { + laser, + pin, + + pub fn fromWire(value: []const u8) !@This() { + if (std.mem.eql(u8, value, "laser")) return .laser; + if (std.mem.eql(u8, value, "pin")) return .pin; + return error.UnknownEnumValue; + } + + pub fn toWire(self: @This()) []const u8 { + return switch (self) { + .laser => "laser", + .pin => "pin", + }; + } +}; + +pub const PresenceListResult = struct { + entries: []const PresenceEntry, +}; + pub const ProcessInfoResult = struct { command: wire.Nullable([]const u8), cwd: wire.Nullable([]const u8), @@ -3246,6 +3326,59 @@ pub fn ping(client: anytype, request: PingRequest) !wire.Decoded(PingResult) { ); } +pub const PresenceClearRequest = struct {}; + +pub const PresenceClearResult = EmptyResult; + +pub fn presenceClear(client: anytype, request: PresenceClearRequest) !wire.Decoded(PresenceClearResult) { + return client.callTyped( + PresenceClearResult, + .{ + .name = "presence-clear", + .authority = "control", + .since = 12, + .capability = "presence-v1", + }, + request, + ); +} + +pub const PresenceListRequest = struct {}; + +pub fn presenceList(client: anytype, request: PresenceListRequest) !wire.Decoded(PresenceListResult) { + return client.callTyped( + PresenceListResult, + .{ + .name = "presence-list", + .authority = "control", + .since = 12, + .capability = "presence-v1", + }, + request, + ); +} + +pub const PresenceUpdateRequest = struct { + highlight: wire.Field(PresenceHighlight) = .absent, + pointer: wire.Field(PresenceAnchor) = .absent, + surface: Id, +}; + +pub const PresenceUpdateResult = EmptyResult; + +pub fn presenceUpdate(client: anytype, request: PresenceUpdateRequest) !wire.Decoded(PresenceUpdateResult) { + return client.callTyped( + PresenceUpdateResult, + .{ + .name = "presence-update", + .authority = "control", + .since = 12, + .capability = "presence-v1", + }, + request, + ); +} + pub const ProcessInfoRequest = struct { surface: Id, }; @@ -4084,6 +4217,7 @@ pub const SubscribeRequestTreeEvents = enum { }; pub const SubscribeRequest = struct { + presence_only: wire.Field(bool) = .absent, surface: wire.Field(Id) = .absent, tree_events: wire.Field(SubscribeRequestTreeEvents) = .absent, }; @@ -4098,6 +4232,7 @@ pub fn subscribe(client: anytype, request: SubscribeRequest) !client_runtime.Str .since = 5, .capability = null, .fields = &.{ + .{ .name = "presence_only", .since = 12, .capability = "presence-v1" }, .{ .name = "surface", .since = 9, .capability = "surface-subscribe-filter" }, .{ .name = "tree_events", .since = 7, .capability = null }, }, @@ -4528,6 +4663,19 @@ pub const PaneClosedEvent = struct { workspace: Id, }; +pub const PresenceChangedEvent = struct { + client: u64, + color: u64, + event: []const u8, + generation: u64, + highlight: wire.Nullable(PresenceHighlight), + kind: wire.Nullable([]const u8), + name: wire.Nullable([]const u8), + pointer: wire.Nullable(PresenceAnchor), + surface: wire.Nullable(Id), + updated_at_ms: u64, +}; + pub const RenderDeltaEvent = struct { cursor: RenderCursor, default_bg: ?ColorHex = null, @@ -4822,6 +4970,7 @@ pub const Event = union(enum) { pairing_resolved: PairingResolvedEvent, pane_added: PaneAddedEvent, pane_closed: PaneClosedEvent, + presence_changed: PresenceChangedEvent, render_delta: RenderDeltaEvent, render_state: RenderStateEvent, resized: ResizedEvent, @@ -4875,6 +5024,7 @@ pub fn eventWireName(event: Event) []const u8 { .pairing_resolved => "pairing-resolved", .pane_added => "pane-added", .pane_closed => "pane-closed", + .presence_changed => "presence-changed", .render_delta => "render-delta", .render_state => "render-state", .resized => "resized", @@ -5013,6 +5163,10 @@ pub fn decodeEvent(allocator: std.mem.Allocator, value: wire.Value) !DecodedEven const decoded = try wire.decodeLeaky(PaneClosedEvent, arena.allocator(), value); return .{ .arena = arena, .value = .{ .pane_closed = decoded } }; } + if (std.mem.eql(u8, name, "presence-changed")) { + const decoded = try wire.decodeLeaky(PresenceChangedEvent, arena.allocator(), value); + return .{ .arena = arena, .value = .{ .presence_changed = decoded } }; + } if (std.mem.eql(u8, name, "render-delta")) { const decoded = try wire.decodeLeaky(RenderDeltaEvent, arena.allocator(), value); return .{ .arena = arena, .value = .{ .render_delta = decoded } }; @@ -5134,7 +5288,7 @@ pub const CommandDescriptor = struct { stream: ?[]const u8, }; -pub const command_count: usize = 106; +pub const command_count: usize = 109; pub const commands = [_]CommandDescriptor{ .{ .name = "apply-layout", .authority = "control", .since = 6, .capability = null, .stream = null }, .{ .name = "attach-surface", .authority = "frontend", .since = 5, .capability = null, .stream = "attach" }, @@ -5197,6 +5351,9 @@ pub const commands = [_]CommandDescriptor{ .{ .name = "pairing-response", .authority = "local-admin", .since = 7, .capability = null, .stream = null }, .{ .name = "pane-neighbor", .authority = "control", .since = 6, .capability = null, .stream = null }, .{ .name = "ping", .authority = "control", .since = 6, .capability = null, .stream = null }, + .{ .name = "presence-clear", .authority = "control", .since = 12, .capability = "presence-v1", .stream = null }, + .{ .name = "presence-list", .authority = "control", .since = 12, .capability = "presence-v1", .stream = null }, + .{ .name = "presence-update", .authority = "control", .since = 12, .capability = "presence-v1", .stream = null }, .{ .name = "process-info", .authority = "control", .since = 6, .capability = null, .stream = null }, .{ .name = "put-frontend-projection", .authority = "control", .since = 7, .capability = null, .stream = null }, .{ .name = "read-screen", .authority = "control", .since = 5, .capability = null, .stream = null }, @@ -5275,32 +5432,33 @@ const event_streams_20 = [_][]const u8{"subscribe"}; const event_streams_21 = [_][]const u8{"subscribe"}; const event_streams_22 = [_][]const u8{"subscribe-deltas"}; const event_streams_23 = [_][]const u8{"subscribe-deltas"}; -const event_streams_24 = [_][]const u8{"attach-render"}; +const event_streams_24 = [_][]const u8{"subscribe"}; const event_streams_25 = [_][]const u8{"attach-render"}; -const event_streams_26 = [_][]const u8{"attach-byte"}; -const event_streams_27 = [_][]const u8{"subscribe-deltas"}; +const event_streams_26 = [_][]const u8{"attach-render"}; +const event_streams_27 = [_][]const u8{"attach-byte"}; const event_streams_28 = [_][]const u8{"subscribe-deltas"}; const event_streams_29 = [_][]const u8{"subscribe-deltas"}; -const event_streams_30 = [_][]const u8{ "subscribe", "attach-byte", "attach-render", "attach-browser" }; -const event_streams_31 = [_][]const u8{"subscribe"}; +const event_streams_30 = [_][]const u8{"subscribe-deltas"}; +const event_streams_31 = [_][]const u8{ "subscribe", "attach-byte", "attach-render", "attach-browser" }; const event_streams_32 = [_][]const u8{"subscribe"}; const event_streams_33 = [_][]const u8{"subscribe"}; const event_streams_34 = [_][]const u8{"subscribe"}; const event_streams_35 = [_][]const u8{"subscribe"}; -const event_streams_36 = [_][]const u8{"subscribe-deltas"}; +const event_streams_36 = [_][]const u8{"subscribe"}; const event_streams_37 = [_][]const u8{"subscribe-deltas"}; const event_streams_38 = [_][]const u8{"subscribe-deltas"}; -const event_streams_39 = [_][]const u8{"subscribe"}; +const event_streams_39 = [_][]const u8{"subscribe-deltas"}; const event_streams_40 = [_][]const u8{"subscribe"}; const event_streams_41 = [_][]const u8{"subscribe"}; -const event_streams_42 = [_][]const u8{"attach-byte"}; -const event_streams_43 = [_][]const u8{"subscribe"}; -const event_streams_44 = [_][]const u8{"subscribe-deltas"}; +const event_streams_42 = [_][]const u8{"subscribe"}; +const event_streams_43 = [_][]const u8{"attach-byte"}; +const event_streams_44 = [_][]const u8{"subscribe"}; const event_streams_45 = [_][]const u8{"subscribe-deltas"}; const event_streams_46 = [_][]const u8{"subscribe-deltas"}; const event_streams_47 = [_][]const u8{"subscribe-deltas"}; +const event_streams_48 = [_][]const u8{"subscribe-deltas"}; -pub const event_count: usize = 48; +pub const event_count: usize = 49; pub const events = [_]EventDescriptor{ .{ .name = "agent-changed", .since = 11, .capability = null, .streams = &event_streams_0 }, .{ .name = "bell", .since = 5, .capability = null, .streams = &event_streams_1 }, @@ -5326,28 +5484,29 @@ pub const events = [_]EventDescriptor{ .{ .name = "pairing-resolved", .since = 7, .capability = null, .streams = &event_streams_21 }, .{ .name = "pane-added", .since = 7, .capability = null, .streams = &event_streams_22 }, .{ .name = "pane-closed", .since = 7, .capability = null, .streams = &event_streams_23 }, - .{ .name = "render-delta", .since = 7, .capability = null, .streams = &event_streams_24 }, - .{ .name = "render-state", .since = 7, .capability = null, .streams = &event_streams_25 }, - .{ .name = "resized", .since = 6, .capability = null, .streams = &event_streams_26 }, - .{ .name = "screen-added", .since = 7, .capability = null, .streams = &event_streams_27 }, - .{ .name = "screen-closed", .since = 7, .capability = null, .streams = &event_streams_28 }, - .{ .name = "screen-renamed", .since = 7, .capability = null, .streams = &event_streams_29 }, - .{ .name = "scroll-changed", .since = 6, .capability = null, .streams = &event_streams_30 }, - .{ .name = "status", .since = 5, .capability = null, .streams = &event_streams_31 }, - .{ .name = "surface-exited", .since = 5, .capability = null, .streams = &event_streams_32 }, - .{ .name = "surface-output", .since = 5, .capability = null, .streams = &event_streams_33 }, - .{ .name = "surface-resize-failed", .since = 7, .capability = null, .streams = &event_streams_34 }, - .{ .name = "surface-resized", .since = 5, .capability = null, .streams = &event_streams_35 }, - .{ .name = "tab-added", .since = 7, .capability = null, .streams = &event_streams_36 }, - .{ .name = "tab-closed", .since = 7, .capability = null, .streams = &event_streams_37 }, - .{ .name = "tab-renamed", .since = 7, .capability = null, .streams = &event_streams_38 }, - .{ .name = "terminal-registry-changed", .since = 9, .capability = null, .streams = &event_streams_39 }, - .{ .name = "title-changed", .since = 5, .capability = null, .streams = &event_streams_40 }, - .{ .name = "tree-changed", .since = 5, .capability = null, .streams = &event_streams_41 }, - .{ .name = "vt-state", .since = 5, .capability = null, .streams = &event_streams_42 }, - .{ .name = "window-title-requested", .since = 6, .capability = null, .streams = &event_streams_43 }, - .{ .name = "workspace-added", .since = 7, .capability = null, .streams = &event_streams_44 }, - .{ .name = "workspace-closed", .since = 7, .capability = null, .streams = &event_streams_45 }, - .{ .name = "workspace-moved", .since = 7, .capability = null, .streams = &event_streams_46 }, - .{ .name = "workspace-renamed", .since = 7, .capability = null, .streams = &event_streams_47 }, + .{ .name = "presence-changed", .since = 12, .capability = "presence-v1", .streams = &event_streams_24 }, + .{ .name = "render-delta", .since = 7, .capability = null, .streams = &event_streams_25 }, + .{ .name = "render-state", .since = 7, .capability = null, .streams = &event_streams_26 }, + .{ .name = "resized", .since = 6, .capability = null, .streams = &event_streams_27 }, + .{ .name = "screen-added", .since = 7, .capability = null, .streams = &event_streams_28 }, + .{ .name = "screen-closed", .since = 7, .capability = null, .streams = &event_streams_29 }, + .{ .name = "screen-renamed", .since = 7, .capability = null, .streams = &event_streams_30 }, + .{ .name = "scroll-changed", .since = 6, .capability = null, .streams = &event_streams_31 }, + .{ .name = "status", .since = 5, .capability = null, .streams = &event_streams_32 }, + .{ .name = "surface-exited", .since = 5, .capability = null, .streams = &event_streams_33 }, + .{ .name = "surface-output", .since = 5, .capability = null, .streams = &event_streams_34 }, + .{ .name = "surface-resize-failed", .since = 7, .capability = null, .streams = &event_streams_35 }, + .{ .name = "surface-resized", .since = 5, .capability = null, .streams = &event_streams_36 }, + .{ .name = "tab-added", .since = 7, .capability = null, .streams = &event_streams_37 }, + .{ .name = "tab-closed", .since = 7, .capability = null, .streams = &event_streams_38 }, + .{ .name = "tab-renamed", .since = 7, .capability = null, .streams = &event_streams_39 }, + .{ .name = "terminal-registry-changed", .since = 9, .capability = null, .streams = &event_streams_40 }, + .{ .name = "title-changed", .since = 5, .capability = null, .streams = &event_streams_41 }, + .{ .name = "tree-changed", .since = 5, .capability = null, .streams = &event_streams_42 }, + .{ .name = "vt-state", .since = 5, .capability = null, .streams = &event_streams_43 }, + .{ .name = "window-title-requested", .since = 6, .capability = null, .streams = &event_streams_44 }, + .{ .name = "workspace-added", .since = 7, .capability = null, .streams = &event_streams_45 }, + .{ .name = "workspace-closed", .since = 7, .capability = null, .streams = &event_streams_46 }, + .{ .name = "workspace-moved", .since = 7, .capability = null, .streams = &event_streams_47 }, + .{ .name = "workspace-renamed", .since = 7, .capability = null, .streams = &event_streams_48 }, }; diff --git a/cmux-tui/crates/cmux-tui-core/src/event_bus.rs b/cmux-tui/crates/cmux-tui-core/src/event_bus.rs index 9086fb6a407a..6444d1226c47 100644 --- a/cmux-tui/crates/cmux-tui-core/src/event_bus.rs +++ b/cmux-tui/crates/cmux-tui-core/src/event_bus.rs @@ -23,6 +23,8 @@ struct MuxEventSubscriber { enum MuxEventFilter { All, ConfigReload, + /// Only `PresenceChanged`; nothing else reaches the mailbox. + Presence, AttachedSurface(SurfaceId), SurfaceSession(SurfaceSessionScope), } @@ -62,6 +64,7 @@ enum CoalescedEventKey { Title(SurfaceId), SurfaceOutput(SurfaceId), Scroll(SurfaceId), + Presence(u64), } impl MuxEventBroadcaster { @@ -73,6 +76,10 @@ impl MuxEventBroadcaster { self.subscribe_with_filter(MuxEventFilter::ConfigReload) } + pub fn subscribe_presence(&self) -> MuxEventReceiver { + self.subscribe_with_filter(MuxEventFilter::Presence) + } + pub fn subscribe_attached_surface(&self, surface: SurfaceId) -> MuxEventReceiver { self.subscribe_with_filter(MuxEventFilter::AttachedSurface(surface)) } @@ -137,6 +144,7 @@ impl MuxEventFilter { match self { Self::All => true, Self::ConfigReload => matches!(event, MuxEvent::ConfigReloadRequested), + Self::Presence => matches!(event, MuxEvent::PresenceChanged(_)), Self::AttachedSurface(surface) => match event { MuxEvent::Notification(notification) => notification.surface == Some(*surface), MuxEvent::ScrollChanged { surface: event_surface, .. } => { @@ -160,6 +168,7 @@ impl SurfaceSessionScope { | MuxEvent::AgentChanged { surface, .. } | MuxEvent::TitleChanged { surface, .. } | MuxEvent::ScrollChanged { surface, .. } => *surface == self.surface, + MuxEvent::PresenceChanged(entry) => entry.surface.is_none_or(|s| s == self.surface), MuxEvent::Notification(notification) => { notification.surface.is_none_or(|surface| surface == self.surface) } @@ -246,6 +255,10 @@ impl MuxEventMailbox { event @ MuxEvent::ScrollChanged { surface, .. } => { state.push_coalesced(sequence, CoalescedEventKey::Scroll(surface), event) } + MuxEvent::PresenceChanged(entry) => { + let key = CoalescedEventKey::Presence(entry.client); + state.push_coalesced(sequence, key, MuxEvent::PresenceChanged(entry)) + } MuxEvent::ConfigReloadRequested => state.push_coalesced( sequence, CoalescedEventKey::ConfigReload, diff --git a/cmux-tui/crates/cmux-tui-core/src/lib.rs b/cmux-tui/crates/cmux-tui-core/src/lib.rs index 7f3d624e65f5..99ce56fd6813 100644 --- a/cmux-tui/crates/cmux-tui-core/src/lib.rs +++ b/cmux-tui/crates/cmux-tui-core/src/lib.rs @@ -20,6 +20,7 @@ mod journal_kernel; mod model; mod mux; mod pairing; +mod presence; pub mod provider_management; pub mod resource; mod resource_api; @@ -65,6 +66,11 @@ pub use mux::{ WorkspacePlacement, ZoomMode, ZoomState, }; pub use pairing::{PairingChallenge, PairingDecision, PairingError}; +pub use presence::{ + PRESENCE_MAX_CLIENTS, PRESENCE_MAX_UPDATES_PER_SECOND, PRESENCE_PALETTE_SIZE, + PRESENCE_POINTER_TTL, PresenceAnchor, PresenceEntry, PresenceHighlight, PresenceHighlightMode, + PresenceHub, PresenceUpdate, PresenceUpdateError, +}; pub use resource_api::{ResourceMachineRequest, ResourceMachineService}; pub use resource_selector::{ResolvedResourcePath, ResourceSelectors, ResourceTarget}; pub use short_id::assign_short_ids; diff --git a/cmux-tui/crates/cmux-tui-core/src/mux.rs b/cmux-tui/crates/cmux-tui-core/src/mux.rs index dba71b35b101..3067da909060 100644 --- a/cmux-tui/crates/cmux-tui-core/src/mux.rs +++ b/cmux-tui/crates/cmux-tui-core/src/mux.rs @@ -975,6 +975,9 @@ pub enum MuxEvent { /// The daemon's machine-level model spend readout changed. `None` means /// the readout is unavailable and frontends must hide it. MachineUsageChanged(Option), + /// One client's collaboration presence (pointer/highlight) changed. + /// `surface: None` means the client cleared it or went away. + PresenceChanged(crate::PresenceEntry), /// Every workspace is gone. Empty, } @@ -2360,6 +2363,7 @@ pub struct Mux { pub(crate) control_clients: crate::server::ClientRegistry, pub(crate) surface_operation_admission: Arc, pairing: PairingBroker, + pub(crate) presence: crate::PresenceHub, #[cfg(test)] test_surface_runtime: bool, pub session: String, @@ -2758,6 +2762,7 @@ impl Mux { crate::server::ServerSurfaceOperationAdmission::default(), ), pairing: PairingBroker::new(), + presence: crate::PresenceHub::default(), #[cfg(test)] test_surface_runtime, session, @@ -6443,7 +6448,17 @@ impl Mux { Some(self.subscribers.subscribe_surface_session(surface, workspace.id, screen.id, pane)) } + /// Subscribe to presence changes only (`subscribe` with `presence_only`). + pub fn subscribe_presence(&self) -> MuxEventReceiver { + self.subscribers.subscribe_presence() + } + pub fn emit(&self, event: MuxEvent) { + if let MuxEvent::SurfaceExited(surface) = &event { + for entry in self.presence.forget_surface(*surface) { + self.subscribers.emit(MuxEvent::PresenceChanged(entry)); + } + } self.subscribers.emit(event); } diff --git a/cmux-tui/crates/cmux-tui-core/src/presence.rs b/cmux-tui/crates/cmux-tui-core/src/presence.rs new file mode 100644 index 000000000000..a6deb379418d --- /dev/null +++ b/cmux-tui/crates/cmux-tui-core/src/presence.rs @@ -0,0 +1,332 @@ +//! Ephemeral collaboration presence: where each connected client points. +//! +//! The hub is deliberately small. It validates nothing about surfaces (the +//! server does that before calling in), it never touches the journal or the +//! registry lock, and it holds only the latest state per client. A daemon +//! restart forgets everything, which is correct for a "look here" signal. +//! +//! Frontends own the mapping from an anchor to pixels. The daemon only stores +//! and fans out anchors, so a new surface kind adds one `PresenceAnchor` +//! variant and nothing else here. + +use std::collections::BTreeMap; +use std::sync::Mutex; +use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; + +use serde::{Deserialize, Serialize}; + +use crate::SurfaceId; + +/// Distinct pointer colors a frontend can render; the hub assigns one per client. +pub const PRESENCE_PALETTE_SIZE: u64 = 8; +/// Most updates one client may send inside one second before the hub rejects. +pub const PRESENCE_MAX_UPDATES_PER_SECOND: u32 = 240; +/// Clients that may hold presence state at once. +pub const PRESENCE_MAX_CLIENTS: usize = 256; +/// A pointer that has not moved for this long is dropped from snapshots. +pub const PRESENCE_POINTER_TTL: Duration = Duration::from_secs(60); + +/// One point inside a surface, in the surface's own coordinate system. +#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)] +#[serde(tag = "kind", rename_all = "kebab-case")] +pub enum PresenceAnchor { + /// A terminal grid cell. `row` counts from the top of the publisher's + /// viewport; `scroll_offset` is how many rows that viewport sits above + /// the live bottom, so a viewer at another offset can shift the row. + Cell { + row: u32, + col: u32, + #[serde(default)] + scroll_offset: u64, + }, + /// A browser or display point in CSS/document pixels. + Point { x: f64, y: f64 }, +} + +/// How long a highlight should stay on screen. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum PresenceHighlightMode { + /// Fades after a couple of seconds; a frontend decides the exact timing. + Laser, + /// Stays until the client clears it or disconnects. + Pin, +} + +/// A region one client wants everybody to look at. +#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)] +pub struct PresenceHighlight { + pub start: PresenceAnchor, + pub end: PresenceAnchor, + pub mode: PresenceHighlightMode, +} + +/// The latest presence state of one client. +#[derive(Clone, Debug, PartialEq)] +pub struct PresenceEntry { + pub client: u64, + pub name: Option, + pub kind: Option, + /// Palette slot in `0..PRESENCE_PALETTE_SIZE`, stable for the connection. + pub color: u64, + /// `None` after a clear: the client is known but points nowhere. + pub surface: Option, + pub pointer: Option, + pub highlight: Option, + pub updated_at_ms: u64, + /// Increments on every accepted change so late events can be discarded. + pub generation: u64, +} + +impl PresenceEntry { + fn cleared(client: u64, color: u64, generation: u64) -> Self { + Self { + client, + name: None, + kind: None, + color, + surface: None, + pointer: None, + highlight: None, + updated_at_ms: now_ms(), + generation, + } + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum PresenceUpdateError { + RateLimited, + TooManyClients, +} + +impl std::fmt::Display for PresenceUpdateError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::RateLimited => write!( + f, + "rate limited: more than {PRESENCE_MAX_UPDATES_PER_SECOND} presence updates in one second" + ), + Self::TooManyClients => { + write!(f, "too many presence clients (limit {PRESENCE_MAX_CLIENTS})") + } + } + } +} + +impl std::error::Error for PresenceUpdateError {} + +struct PresenceSlot { + entry: PresenceEntry, + last_change: Instant, + window_started: Instant, + window_count: u32, +} + +#[derive(Default)] +pub struct PresenceHub { + slots: Mutex>, +} + +/// What one client wants to publish. +pub struct PresenceUpdate { + pub name: Option, + pub kind: Option, + pub surface: SurfaceId, + pub pointer: Option, + pub highlight: Option, +} + +impl PresenceHub { + /// Replace one client's presence. Returns the entry to broadcast. + pub fn update( + &self, + client: u64, + update: PresenceUpdate, + ) -> Result { + self.update_at(client, update, Instant::now()) + } + + fn update_at( + &self, + client: u64, + update: PresenceUpdate, + now: Instant, + ) -> Result { + let mut slots = self.slots.lock().unwrap(); + if !slots.contains_key(&client) && slots.len() >= PRESENCE_MAX_CLIENTS { + return Err(PresenceUpdateError::TooManyClients); + } + let slot = slots.entry(client).or_insert_with(|| PresenceSlot { + entry: PresenceEntry::cleared(client, client % PRESENCE_PALETTE_SIZE, 0), + last_change: now, + window_started: now, + window_count: 0, + }); + if now.duration_since(slot.window_started) >= Duration::from_secs(1) { + slot.window_started = now; + slot.window_count = 0; + } + if slot.window_count >= PRESENCE_MAX_UPDATES_PER_SECOND { + return Err(PresenceUpdateError::RateLimited); + } + slot.window_count += 1; + slot.last_change = now; + let entry = &mut slot.entry; + entry.name = update.name; + entry.kind = update.kind; + entry.surface = Some(update.surface); + entry.pointer = update.pointer; + entry.highlight = update.highlight; + entry.updated_at_ms = now_ms(); + entry.generation += 1; + Ok(entry.clone()) + } + + /// Forget one client. Returns a cleared entry to broadcast when the + /// client had published anything. + pub fn clear(&self, client: u64) -> Option { + let mut slots = self.slots.lock().unwrap(); + let slot = slots.remove(&client)?; + Some(PresenceEntry::cleared(client, slot.entry.color, slot.entry.generation + 1)) + } + + /// Every client that still points somewhere. Stale pointers are dropped + /// here rather than by a timer so the hub needs no thread. + pub fn snapshot(&self) -> Vec { + self.snapshot_at(Instant::now()) + } + + fn snapshot_at(&self, now: Instant) -> Vec { + let mut slots = self.slots.lock().unwrap(); + slots.retain(|_, slot| { + let pinned = slot.entry.highlight.is_some_and(|h| h.mode == PresenceHighlightMode::Pin); + pinned || now.duration_since(slot.last_change) < PRESENCE_POINTER_TTL + }); + slots + .values() + .filter(|slot| slot.entry.surface.is_some()) + .map(|slot| slot.entry.clone()) + .collect() + } + + /// Drop every entry that points at a surface that no longer exists. + /// Returns the cleared entries to broadcast. + pub fn forget_surface(&self, surface: SurfaceId) -> Vec { + let mut slots = self.slots.lock().unwrap(); + let mut cleared = Vec::new(); + for slot in slots.values_mut() { + if slot.entry.surface == Some(surface) { + slot.entry.surface = None; + slot.entry.pointer = None; + slot.entry.highlight = None; + slot.entry.updated_at_ms = now_ms(); + slot.entry.generation += 1; + cleared.push(slot.entry.clone()); + } + } + cleared + } +} + +fn now_ms() -> u64 { + SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_millis() as u64).unwrap_or(0) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn update(surface: SurfaceId, row: u32, col: u32) -> PresenceUpdate { + PresenceUpdate { + name: Some("ada".into()), + kind: Some("mac".into()), + surface, + pointer: Some(PresenceAnchor::Cell { row, col, scroll_offset: 0 }), + highlight: None, + } + } + + #[test] + fn update_assigns_a_stable_color_and_bumps_generation() { + let hub = PresenceHub::default(); + let first = hub.update(11, update(3, 1, 2)).unwrap(); + let second = hub.update(11, update(3, 4, 5)).unwrap(); + assert_eq!(first.color, 11 % PRESENCE_PALETTE_SIZE); + assert_eq!(second.color, first.color); + assert_eq!(first.generation, 1); + assert_eq!(second.generation, 2); + assert_eq!(second.pointer, Some(PresenceAnchor::Cell { row: 4, col: 5, scroll_offset: 0 })); + assert_eq!(hub.snapshot().len(), 1); + } + + #[test] + fn clear_returns_a_cleared_entry_and_removes_the_slot() { + let hub = PresenceHub::default(); + hub.update(7, update(3, 0, 0)).unwrap(); + let cleared = hub.clear(7).unwrap(); + assert_eq!(cleared.surface, None); + assert_eq!(cleared.generation, 2); + assert!(hub.snapshot().is_empty()); + assert!(hub.clear(7).is_none()); + } + + #[test] + fn rate_limit_resets_after_one_second() { + let hub = PresenceHub::default(); + let start = Instant::now(); + for _ in 0..PRESENCE_MAX_UPDATES_PER_SECOND { + hub.update_at(1, update(3, 0, 0), start).unwrap(); + } + assert_eq!( + hub.update_at(1, update(3, 0, 0), start).unwrap_err(), + PresenceUpdateError::RateLimited + ); + hub.update_at(1, update(3, 0, 0), start + Duration::from_secs(1)).unwrap(); + } + + #[test] + fn snapshot_drops_idle_pointers_but_keeps_pins() { + let hub = PresenceHub::default(); + let start = Instant::now(); + hub.update_at(1, update(3, 0, 0), start).unwrap(); + let mut pinned = update(3, 0, 0); + pinned.highlight = Some(PresenceHighlight { + start: PresenceAnchor::Cell { row: 0, col: 0, scroll_offset: 0 }, + end: PresenceAnchor::Cell { row: 0, col: 9, scroll_offset: 0 }, + mode: PresenceHighlightMode::Pin, + }); + hub.update_at(2, pinned, start).unwrap(); + let later = start + PRESENCE_POINTER_TTL + Duration::from_secs(1); + let live = hub.snapshot_at(later); + assert_eq!(live.iter().map(|e| e.client).collect::>(), vec![2]); + } + + #[test] + fn forget_surface_clears_only_pointers_on_that_surface() { + let hub = PresenceHub::default(); + hub.update(1, update(3, 0, 0)).unwrap(); + hub.update(2, update(4, 0, 0)).unwrap(); + let cleared = hub.forget_surface(3); + assert_eq!(cleared.len(), 1); + assert_eq!(cleared[0].client, 1); + assert_eq!(cleared[0].surface, None); + assert_eq!(hub.snapshot().iter().map(|e| e.client).collect::>(), vec![2]); + } + + #[test] + fn anchors_round_trip_as_tagged_json() { + let cell: PresenceAnchor = + serde_json::from_value(serde_json::json!({"kind": "cell", "row": 2, "col": 7})) + .unwrap(); + assert_eq!(cell, PresenceAnchor::Cell { row: 2, col: 7, scroll_offset: 0 }); + let point: PresenceAnchor = + serde_json::from_value(serde_json::json!({"kind": "point", "x": 1.5, "y": 2.0})) + .unwrap(); + assert_eq!(point, PresenceAnchor::Point { x: 1.5, y: 2.0 }); + assert_eq!( + serde_json::to_value(PresenceHighlightMode::Laser).unwrap(), + serde_json::json!("laser") + ); + } +} diff --git a/cmux-tui/crates/cmux-tui-core/src/server.rs b/cmux-tui/crates/cmux-tui-core/src/server.rs index 52cf2d5480a9..3e070b2caa7b 100644 --- a/cmux-tui/crates/cmux-tui-core/src/server.rs +++ b/cmux-tui/crates/cmux-tui-core/src/server.rs @@ -113,6 +113,9 @@ pub const CLIENT_FOCUS_CAPABILITY: &str = "client-focus-v1"; pub const DAEMON_SHUTDOWN_EVENT: &str = "daemon-shutdown"; /// The daemon answers `machine-usage` and emits `machine-usage-changed`. pub const MACHINE_USAGE_CAPABILITY: &str = "machine-usage-v1"; +/// `presence-update`, `presence-clear`, `presence-list`, and the +/// `presence-changed` subscribe event. +pub const PRESENCE_CAPABILITY: &str = "presence-v1"; /// The daemon reads the host's listening TCP sockets for an authenticated /// client. Cloud clients use this over the private cmux-tui link, so routine /// port inventory never needs a provider or web control-plane call. @@ -140,6 +143,20 @@ fn validate_client_focus_id(client_id: &str) -> anyhow::Result<()> { /// `machine-usage` result and `machine-usage-changed` payload body: `usage` /// is the readout object or null when the daemon has none. +fn presence_entry_json(entry: &crate::PresenceEntry) -> Value { + json!({ + "client": entry.client, + "name": entry.name, + "kind": entry.kind, + "color": entry.color, + "surface": entry.surface, + "pointer": entry.pointer, + "highlight": entry.highlight, + "updated_at_ms": entry.updated_at_ms, + "generation": entry.generation, + }) +} + fn machine_usage_json(usage: Option<&MachineUsage>) -> Value { json!({ "usage": usage.map(|usage| json!({ @@ -213,6 +230,7 @@ fn advertised_capabilities(bounded_clear_history_fallback_writes: bool) -> Vec<& BROWSER_PROVIDER_CAPABILITY, CLIENT_FOCUS_CAPABILITY, MACHINE_USAGE_CAPABILITY, + PRESENCE_CAPABILITY, MACHINE_LISTENING_TCP_CAPABILITY, SERVER_STATS_CAPABILITY, ]; @@ -701,6 +719,19 @@ enum Command { ListClients, /// Read the machine-level model spend readout hosted by this daemon. MachineUsage, + /// Publish this connection's collaboration pointer and highlight on one + /// surface. Ephemeral: never journaled, forgotten on disconnect. + PresenceUpdate { + surface: SurfaceId, + #[serde(default)] + pointer: Option, + #[serde(default)] + highlight: Option, + }, + /// Withdraw this connection's presence. + PresenceClear, + /// Every client that currently points somewhere. + PresenceList, /// Read listening TCP sockets on this host. The fixed command has no /// caller-controlled arguments and returns only the socket listing. MachineListeningTcp, @@ -1338,6 +1369,8 @@ enum Command { tree_events: Option, #[serde(default)] surface: Option, + #[serde(default)] + presence_only: Option, }, /// Stream a surface: vt-state event followed by live output events. AttachSurface { @@ -5553,6 +5586,9 @@ fn disconnect_client_with_notice( } else { record.writer.close(); } + if let Some(entry) = mux.presence.clear(client) { + mux.emit(MuxEvent::PresenceChanged(entry)); + } mux.emit(MuxEvent::ClientDetached(client)); true } @@ -11266,6 +11302,25 @@ fn handle_command_with_cancellation( } Command::ListClients => Ok(mux.control_clients_json(client)), Command::MachineUsage => Ok(machine_usage_json(mux.machine_usage().as_ref())), + Command::PresenceUpdate { surface, pointer, highlight } => { + get_surface(mux, surface)?; + let (name, kind) = mux.control_clients.client_info(client).unwrap_or((None, None)); + let entry = mux + .presence + .update(client, crate::PresenceUpdate { name, kind, surface, pointer, highlight }) + .map_err(|error| anyhow::anyhow!("bad request: {error}"))?; + mux.emit(MuxEvent::PresenceChanged(entry)); + Ok(json!({})) + } + Command::PresenceClear => { + if let Some(entry) = mux.presence.clear(client) { + mux.emit(MuxEvent::PresenceChanged(entry)); + } + Ok(json!({})) + } + Command::PresenceList => Ok(json!({ + "entries": mux.presence.snapshot().iter().map(presence_entry_json).collect::>(), + })), Command::MachineListeningTcp => machine_listening_tcp_json(), Command::RegisterBrowserProvider { provider_id, @@ -12661,17 +12716,21 @@ fn handle_command_with_cancellation( mux.scroll_surface_viewport(&surface, delta)?; Ok(json!({})) } - Command::Subscribe { tree_events, surface } => { + Command::Subscribe { tree_events, surface, presence_only } => { let tree_deltas = match tree_events.as_deref().unwrap_or("coarse") { "coarse" => false, "deltas" => true, other => anyhow::bail!("bad request: unsupported tree_events {other:?}"), }; - let events = match surface { - Some(surface) => mux + let events = match (presence_only.unwrap_or(false), surface) { + (true, Some(_)) => { + anyhow::bail!("bad request: presence_only cannot be combined with surface") + } + (true, None) => mux.subscribe_presence(), + (false, Some(surface)) => mux .subscribe_surface_session(surface) .ok_or_else(|| anyhow::anyhow!("unknown surface {surface}"))?, - None => mux.subscribe(), + (false, None) => mux.subscribe(), }; let event_mux = mux.clone(); let trusted_pairing_client = mux.control_clients.is_unix(client); @@ -13239,6 +13298,11 @@ fn subscribed_event_json(event: &MuxEvent) -> Value { payload["event"] = json!("machine-usage-changed"); payload } + MuxEvent::PresenceChanged(entry) => { + let mut payload = presence_entry_json(entry); + payload["event"] = json!("presence-changed"); + payload + } MuxEvent::ConfigReloadRequested => json!({"event": "config-reload-requested"}), MuxEvent::WindowTitleRequested(title) => { json!({"event": "window-title-requested", "title": title}) @@ -19091,6 +19155,98 @@ mod tests { assert!(mux.surface(surface.id).is_some(), "the session must survive its last viewer"); } + #[test] + fn presence_update_emits_a_coalesced_event_and_clears_on_disconnect() { + let mux = test_mux(); + let surface = sizing_browser(&mux, (80, 24)); + let events = mux.subscribe(); + let writer = test_writer(); + let client = mux.control_clients.register(ClientTransport::Unix, writer.clone()); + assert!(handle_message( + &mux, + client, + &json!({"id": 1, "cmd": "set-client-info", "name": "ada", "kind": "tui"}).to_string(), + &writer, + )); + for col in 0..3u32 { + assert!(handle_message( + &mux, + client, + &json!({ + "id": 2, + "cmd": "presence-update", + "surface": surface.id, + "pointer": {"kind": "cell", "row": 1, "col": col}, + }) + .to_string(), + &writer, + )); + } + // Three updates coalesce to the newest state for one subscriber. + let latest = loop { + match events.recv_timeout(Duration::from_secs(1)).unwrap() { + MuxEvent::PresenceChanged(entry) => break entry, + _ => continue, + } + }; + assert_eq!(latest.client, client); + assert_eq!(latest.name.as_deref(), Some("ada")); + assert_eq!(latest.surface, Some(surface.id)); + assert_eq!( + latest.pointer, + Some(crate::PresenceAnchor::Cell { row: 1, col: 2, scroll_offset: 0 }) + ); + assert_eq!(latest.generation, 3); + assert_eq!( + subscribed_event_json(&MuxEvent::PresenceChanged(latest.clone()))["event"], + "presence-changed" + ); + assert_eq!(mux.presence.snapshot().len(), 1); + + assert!(disconnect_client(&mux, client, false)); + let cleared = loop { + match events.recv_timeout(Duration::from_secs(1)).unwrap() { + MuxEvent::PresenceChanged(entry) if entry.surface.is_none() => break entry, + _ => continue, + } + }; + assert_eq!(cleared.client, client); + assert!(cleared.generation > latest.generation); + assert!(mux.presence.snapshot().is_empty()); + } + + #[test] + fn presence_is_cleared_when_its_surface_exits() { + let mux = test_mux(); + let surface = sizing_browser(&mux, (80, 24)); + let writer = test_writer(); + let client = mux.control_clients.register(ClientTransport::Unix, writer.clone()); + assert!(handle_message( + &mux, + client, + &json!({ + "id": 1, + "cmd": "presence-update", + "surface": surface.id, + "pointer": {"kind": "point", "x": 10.5, "y": 20.0}, + }) + .to_string(), + &writer, + )); + assert_eq!(mux.presence.snapshot().len(), 1); + let events = mux.subscribe(); + mux.emit(MuxEvent::SurfaceExited(surface.id)); + let cleared = loop { + match events.recv_timeout(Duration::from_secs(1)).unwrap() { + MuxEvent::PresenceChanged(entry) => break entry, + _ => continue, + } + }; + assert_eq!(cleared.client, client); + assert_eq!(cleared.surface, None); + assert!(mux.presence.snapshot().is_empty()); + } + #[test] fn peer_detach_is_id_stable_and_does_not_disconnect_the_initiator() { let mux = test_mux(); diff --git a/cmux-tui/crates/cmux-tui-core/tests/websocket_transport.rs b/cmux-tui/crates/cmux-tui-core/tests/websocket_transport.rs index b7b0755d8779..ad6c3577febf 100644 --- a/cmux-tui/crates/cmux-tui-core/tests/websocket_transport.rs +++ b/cmux-tui/crates/cmux-tui-core/tests/websocket_transport.rs @@ -612,3 +612,115 @@ fn websocket_non_loopback_bind_requires_and_accepts_explicit_insecure_opt_in() { mux.shutdown(); } + +#[test] +fn websocket_presence_pointer_fans_out_and_clears_on_disconnect() { + let mux = Mux::new("ws-presence", SurfaceOptions::default()); + let surface = mux + .run_command_surface(vec!["/bin/cat".to_string()], None, true, None, None, Some((80, 24))) + .unwrap() + .surface; + let server = server::serve_websocket( + mux.clone(), + "127.0.0.1:0".parse().unwrap(), + Some(TEST_TOKEN.to_string()), + false, + ) + .unwrap(); + + // Viewer: subscribes and only watches. + let mut viewer = authenticated_connect(server.local_addr()); + send_json(&mut viewer, json!({"id": 1, "cmd": "identify"})); + let identify = read_until(&mut viewer, |value| value["id"] == 1); + assert!( + identify["data"]["capabilities"] + .as_array() + .unwrap() + .iter() + .any(|capability| capability == server::PRESENCE_CAPABILITY), + "server must advertise presence-v1" + ); + send_json(&mut viewer, json!({"id": 2, "cmd": "subscribe", "presence_only": true})); + assert_eq!(read_until(&mut viewer, |value| value["id"] == 2)["ok"], true); + // Presence-only subscribers never see tree traffic. + mux.emit(MuxEvent::TreeChanged); + + // Pointer: names itself, then points at a cell with a laser highlight. + let mut pointer = authenticated_connect(server.local_addr()); + send_json( + &mut pointer, + json!({"id": 3, "cmd": "set-client-info", "name": "ada", "kind": "mac"}), + ); + assert_eq!(read_until(&mut pointer, |value| value["id"] == 3)["ok"], true); + send_json( + &mut pointer, + json!({ + "id": 4, + "cmd": "presence-update", + "surface": surface, + "pointer": {"kind": "cell", "row": 3, "col": 12}, + "highlight": { + "start": {"kind": "cell", "row": 3, "col": 0}, + "end": {"kind": "cell", "row": 3, "col": 40}, + "mode": "laser" + } + }), + ); + assert_eq!(read_until(&mut pointer, |value| value["id"] == 4)["ok"], true); + + let changed = loop { + let value = read_json(&mut viewer); + assert_ne!(value["event"], "tree-changed", "presence_only must filter tree events"); + if value["event"] == "presence-changed" { + break value; + } + }; + assert_eq!(changed["name"], "ada"); + assert_eq!(changed["kind"], "mac"); + assert_eq!(changed["surface"], surface); + assert_eq!( + changed["pointer"], + json!({"kind": "cell", "row": 3, "col": 12, "scroll_offset": 0}) + ); + assert_eq!(changed["highlight"]["mode"], "laser"); + assert_eq!(changed["highlight"]["end"]["col"], 40); + assert!(changed["color"].as_u64().unwrap() < 8); + let pointer_client = changed["client"].as_u64().unwrap(); + let generation = changed["generation"].as_u64().unwrap(); + + // A late joiner sees the same state through presence-list. + let mut late = authenticated_connect(server.local_addr()); + send_json(&mut late, json!({"id": 5, "cmd": "presence-list"})); + let listed = read_until(&mut late, |value| value["id"] == 5); + let entries = listed["data"]["entries"].as_array().unwrap(); + assert_eq!(entries.len(), 1); + assert_eq!(entries[0]["client"], pointer_client); + assert_eq!(entries[0]["pointer"]["row"], 3); + + // Unknown surfaces are rejected before anything is stored. + send_json( + &mut pointer, + json!({"id": 6, "cmd": "presence-update", "surface": 999_999, "pointer": {"kind": "point", "x": 1.0, "y": 2.0}}), + ); + let rejected = read_until(&mut pointer, |value| value["id"] == 6); + assert_eq!(rejected["ok"], false); + assert!(rejected["error"].as_str().unwrap().contains("unknown surface")); + + // Dropping the pointer's socket clears its presence for everyone. + pointer.get_mut().shutdown(Shutdown::Both).unwrap(); + drop(pointer); + let cleared = read_until(&mut viewer, |value| { + value["event"] == "presence-changed" + && value["client"] == pointer_client + && value["surface"].is_null() + }); + assert!(cleared["generation"].as_u64().unwrap() > generation); + assert!(cleared["pointer"].is_null()); + assert!(cleared["highlight"].is_null()); + + send_json(&mut late, json!({"id": 7, "cmd": "presence-list"})); + let listed = read_until(&mut late, |value| value["id"] == 7); + assert!(listed["data"]["entries"].as_array().unwrap().is_empty()); + + mux.shutdown(); +} diff --git a/cmux-tui/scripts/check-sdk-schema.py b/cmux-tui/scripts/check-sdk-schema.py index d3deed0887bc..ec59cf5e5288 100755 --- a/cmux-tui/scripts/check-sdk-schema.py +++ b/cmux-tui/scripts/check-sdk-schema.py @@ -19,6 +19,8 @@ RUNTIME_NAMED_REQUEST_REFS = { "BrowserProviderTargetRequest": "BrowserProviderTarget", "crate::FrontendJournalEvent": "FrontendJournalEvent", + "crate::PresenceAnchor": "PresenceAnchor", + "crate::PresenceHighlight": "PresenceHighlight", "crate::ResourceSelectors": "ResourceSelectors", "ProtocolKeyInput": "TerminalKeyInput", } diff --git a/cmux-tui/spec/README.md b/cmux-tui/spec/README.md index 4d86cb2b9ad0..757f04c55368 100644 --- a/cmux-tui/spec/README.md +++ b/cmux-tui/spec/README.md @@ -57,6 +57,7 @@ documented for cmux frontends and compatibility adapters: | [`frontends.md`](frontends.md) | Private frontend synchronization | | [`programmability.md`](programmability.md) | Implementation inventory and ownership | | [`native-frontend.md`](native-frontend.md) | Native TUI integration boundaries | +| [`presence.md`](presence.md) | Ephemeral collaboration presence: pointers and highlights | | [`session-journal.md`](session-journal.md) | Canonical event storage, hooks, agent ownership, and restoration | The private protocol is not a second public API. High-level SDK packages expose diff --git a/cmux-tui/spec/commands.md b/cmux-tui/spec/commands.md index 6d259fd99e5a..1756067e8448 100644 --- a/cmux-tui/spec/commands.md +++ b/cmux-tui/spec/commands.md @@ -2953,6 +2953,83 @@ Errors: | --- | --- | | `bad request: invalid client_id` | Empty, oversized, or non-graphic id | +### presence-update + +| Field | Value | +| --- | --- | +| name | `presence-update` | +| status | implemented | +| since | protocol 12 additive extension; capability `presence-v1` | + +Publishes this connection's collaboration presence on one surface: an optional pointer and an optional highlight. Replaces the connection's whole presence state. Presence is ephemeral: it is never journaled, a daemon restart forgets it, and a disconnect or surface exit clears it for everyone. Frontends map anchors to pixels; the daemon only validates the surface, rate-limits, and fans out `presence-changed`. See [`presence.md`](presence.md). + +Params: + +| Name | JSON type | Required/default | Constraints | +| --- | --- | --- | --- | +| `surface` | `Id` | required | Must be a live surface | +| `pointer` | `PresenceAnchor` | default null | `{kind:"cell",row,col,scroll_offset?}` or `{kind:"point",x,y}` | +| `highlight` | `PresenceHighlight` | default null | `{start:PresenceAnchor,end:PresenceAnchor,mode:"laser"|"pin"}` | + +Result: + +```text +object{} +``` + +Errors: + +| Error | Condition | +| --- | --- | +| `unknown surface ...` | Surface is not alive | +| `bad request: rate limited ...` | More than 240 updates in one second from this connection | +| `bad request: too many presence clients ...` | 256 connections already hold presence | + +Example: + +```json +{"id":4,"cmd":"presence-update","surface":7,"pointer":{"kind":"cell","row":3,"col":12},"highlight":{"start":{"kind":"cell","row":3,"col":0},"end":{"kind":"cell","row":3,"col":40},"mode":"laser"}} +{"id":4,"ok":true,"data":{}} +``` + +### presence-clear + +| Field | Value | +| --- | --- | +| name | `presence-clear` | +| status | implemented | +| since | protocol 12 additive extension; capability `presence-v1` | + +Withdraws this connection's presence. Emits `presence-changed` with `surface:null` when the connection had published anything. + +Params: none. + +Result: + +```text +object{} +``` + +### presence-list + +| Field | Value | +| --- | --- | +| name | `presence-list` | +| status | implemented | +| since | protocol 12 additive extension; capability `presence-v1` | + +Every connection that currently points somewhere, for late joiners. Pointers idle for 60 seconds are dropped unless their highlight mode is `pin`. + +Params: none. + +Result: + +```text +object{entries:PresenceEntry[]} +``` + +`PresenceEntry` is the `presence-changed` payload without its `event` field. + ### move-tab | Field | Value | @@ -3124,6 +3201,7 @@ Example: | since | protocol 5 | | `tree_events` field | protocol 7 additive extension | | `surface` field | protocol 9 additive extension | +| `presence_only` field | protocol 12 additive extension; capability `presence-v1` | Subscribes the connection to mux events. After this command, response lines and event lines may be interleaved on the same connection. `subscribe` does not send an initial tree snapshot; clients should call `list-workspaces` when they need state. @@ -3139,6 +3217,7 @@ Params: | --- | --- | --- | --- | | `tree_events` | `string` | default `"coarse"` | Protocol 7: `"coarse"` or `"deltas"` | | `surface` | `Id` | optional | Protocol 9: existing surface to scope at the event source | +| `presence_only` | `bool` | default false | Protocol 12, capability `presence-v1`: deliver only `presence-changed`; cannot be combined with `surface` | Result: @@ -3151,7 +3230,7 @@ Errors: | Error | Condition | | --- | --- | | thread spawn error string | Server cannot create the event writer thread | -| `bad request: ...` | Malformed request envelope, wrong field type, or unsupported `tree_events` value | +| `bad request: ...` | Malformed request envelope, wrong field type, unsupported `tree_events` value, or `presence_only` with `surface` | CLI mapping: diff --git a/cmux-tui/spec/events.md b/cmux-tui/spec/events.md index 92a52adcbaac..63ac4a45ef4c 100644 --- a/cmux-tui/spec/events.md +++ b/cmux-tui/spec/events.md @@ -12,7 +12,7 @@ Implemented event lines can appear on subscribe, attach, or control lifecycle st | Stream | How to start | Event names | | --- | --- | --- | -| Subscribe stream | `subscribe` command | `tree-changed`, all workspace/screen/pane/tab deltas, `frontend-projection-changed`, `terminal-registry-changed`, `layout-changed`, `surface-output`, `scroll-changed`, `surface-resized`, `surface-resize-failed`, `surface-exited`, `title-changed`, `bell`, `notification`, `status`, `config-reload-requested`, `window-title-requested`, `machine-usage-changed`, `client-attached`, `client-changed`, `client-detached`, `client-list-invalidated`, `pairing-requested`, `pairing-resolved`, `empty`, `overflow` | +| Subscribe stream | `subscribe` command | `tree-changed`, all workspace/screen/pane/tab deltas, `frontend-projection-changed`, `terminal-registry-changed`, `layout-changed`, `surface-output`, `scroll-changed`, `surface-resized`, `surface-resize-failed`, `surface-exited`, `title-changed`, `bell`, `notification`, `status`, `config-reload-requested`, `window-title-requested`, `machine-usage-changed`, `presence-changed`, `client-attached`, `client-changed`, `client-detached`, `client-list-invalidated`, `pairing-requested`, `pairing-resolved`, `empty`, `overflow` | | Attach stream v5 | `attach-surface` command | `vt-state`, `output`, `detached`, `overflow` | | Attach stream v6 PTY | `attach-surface` command | `vt-state`, `resized`, `output`, `colors-changed`, `notification`, `scroll-changed`, `detached`, `overflow` | | Attach stream v7 render mode | `attach-surface` command | `render-state`, `render-delta`, `scroll-changed`, `detached`, `overflow` | @@ -56,6 +56,7 @@ Control lifecycle notices are sent on the authenticated control queue. They do n | `daemon-shutdown` | control | session | protocol 12; sent after the successful `shutdown-daemon` or `session.shutdown` response | | `window-title-requested` | subscribe | session | protocol 6 | | `machine-usage-changed` | subscribe | session | protocol 12 additive extension; capability `machine-usage-v1` | +| `presence-changed` | subscribe | `client` | protocol 12 additive extension; capability `presence-v1` | | `client-attached` | subscribe | `client` | protocol 6 | | `client-changed` | subscribe | `client` | protocol 6 | | `client-detached` | subscribe | `client` | protocol 6 | @@ -778,6 +779,39 @@ Example: {"event":"machine-usage-changed","usage":{"vm_id":"3f1c...","period_days":30,"total_tokens":184220,"api_equivalent_usd":1.23,"as_of":"2026-09-01T00:00:00Z"}} ``` +### presence-changed + +| Field | Value | +| --- | --- | +| event | `presence-changed` | +| status | implemented | +| since | protocol 12 additive extension; capability `presence-v1` | + +Payload: + +```text +object{ + event:"presence-changed", + client:uint64, + name:string|null, + kind:string|null, + color:uint64, + surface:Id|null, + pointer:PresenceAnchor|null, + highlight:PresenceHighlight|null, + updated_at_ms:uint64, + generation:uint64 +} +``` + +Meaning: one connection's collaboration pointer or highlight changed. `name` and `kind` are that connection's `set-client-info` labels. `color` is a palette slot in `0..8`, stable for the connection. `surface:null` means the connection cleared its presence, disconnected, or its surface exited; frontends remove every overlay for that `client`. `generation` increases on every change, so a frontend discards an event whose generation is not above the last one it applied for that client. Delivery is coalesced per client: a slow subscriber sees the latest state, not every intermediate pointer. + +Example: + +```json +{"event":"presence-changed","client":3,"name":"ada","kind":"mac","color":3,"surface":7,"pointer":{"kind":"cell","row":3,"col":12,"scroll_offset":0},"highlight":null,"updated_at_ms":1757548800000,"generation":9} +``` + ### empty | Field | Value | diff --git a/cmux-tui/spec/inventory.json b/cmux-tui/spec/inventory.json index dc9718052c54..4a43c6438a7a 100644 --- a/cmux-tui/spec/inventory.json +++ b/cmux-tui/spec/inventory.json @@ -178,6 +178,9 @@ "notify", "pane-neighbor", "ping", + "presence-clear", + "presence-list", + "presence-update", "process-info", "put-frontend-projection", "read-screen", @@ -406,6 +409,12 @@ "subscribe-deltas" ] }, + { + "name": "presence-changed", + "streams": [ + "subscribe" + ] + }, { "name": "render-delta", "streams": [ diff --git a/cmux-tui/spec/presence.md b/cmux-tui/spec/presence.md new file mode 100644 index 000000000000..ff1e5a895697 --- /dev/null +++ b/cmux-tui/spec/presence.md @@ -0,0 +1,49 @@ +# Presence + +Presence is the "look here" signal between people attached to one session: a +pointer and a highlight per connection, on one surface at a time. It exists so +a teammate can point at a line in a terminal or a spot in a browser while +talking. It is not focus, not selection, and not input. + +## Invariants + +1. Presence is ephemeral. It is never written to the session journal, a daemon + restart forgets it, and `session.journal.subscribe` never carries it. +2. The daemon stores only the latest state per connection. Delivery on the + subscribe stream is coalesced per client, so a slow subscriber sees the + newest pointer, never a backlog of intermediate ones. +3. A disconnect, `presence-clear`, or the exit of the pointed-at surface emits + one `presence-changed` with `surface:null`. Frontends remove every overlay + for that client on such an event. +4. Anchors are surface coordinates, never pixels. `cell` is a terminal grid + cell plus the publisher's `scroll_offset`: how many rows the publisher's viewport sits above the live bottom (0 when at the bottom). A viewer at offset `V` draws the cell on row `row + V - scroll_offset` and hides it when that falls outside the grid. `point` is a browser or + display point in CSS/document pixels. Frontends own the mapping to their + own viewport and may hide an anchor they cannot place. +5. The daemon validates that the surface is alive and nothing else. It does + not require the publisher to be attached to the surface, because a control + link and a viewer can be separate connections. +6. Identity is the connection: `client`, plus the `set-client-info` labels + `name` and `kind`, plus a daemon-assigned `color` slot. Authenticated actor + identity is a separate, later contract; until then labels are self-asserted. +7. Limits: 240 updates per second per connection, 256 connections holding + presence, and a 60 second idle drop for pointers without a `pin` highlight. + +## Wire + +| Item | Name | +| --- | --- | +| capability | `presence-v1` | +| commands | `presence-update`, `presence-clear`, `presence-list`; `subscribe` with `presence_only:true` | +| event | `presence-changed` on the subscribe stream | + +Command and payload shapes are normative in [`commands.md`](commands.md) and +[`events.md`](events.md); types are in `sdk-schema.json`. + +## Rendering guidance + +- `laser` highlights fade after about two seconds on the viewer's side. +- `pin` highlights stay until the publisher clears or disconnects. +- Hide a pointer whose `updated_at_ms` is older than a few seconds; the daemon + keeps it for late joiners but a resting pointer is noise. +- A `cell` anchor is visible only when the viewer's scrollback offset places + that row on screen; compare `scroll_offset` to the local viewport. diff --git a/cmux-tui/spec/sdk-schema.json b/cmux-tui/spec/sdk-schema.json index 06d4442d636f..ec4b0b79a26f 100644 --- a/cmux-tui/spec/sdk-schema.json +++ b/cmux-tui/spec/sdk-schema.json @@ -5219,6 +5219,216 @@ "journal_writer is null for ephemeral sessions without a durable journal.", "Counters accumulate since daemon start; reading them never touches SQLite or the journal." ] + }, + "PresenceAnchor": { + "kind": "tagged_union", + "tag": "kind", + "variants": { + "cell": { + "kind": "object", + "fields": { + "kind": { + "type": { + "kind": "literal", + "value": "cell" + }, + "presence": "required", + "nullable": false + }, + "row": { + "type": { + "kind": "scalar", + "name": "uint32" + }, + "presence": "required", + "nullable": false + }, + "col": { + "type": { + "kind": "scalar", + "name": "uint32" + }, + "presence": "required", + "nullable": false + }, + "scroll_offset": { + "type": { + "kind": "scalar", + "name": "uint64" + }, + "presence": "optional", + "nullable": false + } + }, + "additional_properties": false + }, + "point": { + "kind": "object", + "fields": { + "kind": { + "type": { + "kind": "literal", + "value": "point" + }, + "presence": "required", + "nullable": false + }, + "x": { + "type": { + "kind": "scalar", + "name": "float64" + }, + "presence": "required", + "nullable": false + }, + "y": { + "type": { + "kind": "scalar", + "name": "float64" + }, + "presence": "required", + "nullable": false + } + }, + "additional_properties": false + } + } + }, + "PresenceHighlightMode": { + "kind": "enum", + "values": [ + "laser", + "pin" + ] + }, + "PresenceHighlight": { + "kind": "object", + "fields": { + "start": { + "type": { + "kind": "ref", + "name": "PresenceAnchor" + }, + "presence": "required", + "nullable": false + }, + "end": { + "type": { + "kind": "ref", + "name": "PresenceAnchor" + }, + "presence": "required", + "nullable": false + }, + "mode": { + "type": { + "kind": "ref", + "name": "PresenceHighlightMode" + }, + "presence": "required", + "nullable": false + } + }, + "additional_properties": false + }, + "PresenceEntry": { + "kind": "object", + "fields": { + "client": { + "type": { + "kind": "scalar", + "name": "uint64" + }, + "presence": "required", + "nullable": false + }, + "name": { + "type": { + "kind": "scalar", + "name": "string" + }, + "presence": "required", + "nullable": true + }, + "kind": { + "type": { + "kind": "scalar", + "name": "string" + }, + "presence": "required", + "nullable": true + }, + "color": { + "type": { + "kind": "scalar", + "name": "uint64" + }, + "presence": "required", + "nullable": false + }, + "surface": { + "type": { + "kind": "ref", + "name": "Id" + }, + "presence": "required", + "nullable": true + }, + "pointer": { + "type": { + "kind": "ref", + "name": "PresenceAnchor" + }, + "presence": "required", + "nullable": true + }, + "highlight": { + "type": { + "kind": "ref", + "name": "PresenceHighlight" + }, + "presence": "required", + "nullable": true + }, + "updated_at_ms": { + "type": { + "kind": "scalar", + "name": "uint64" + }, + "presence": "required", + "nullable": false + }, + "generation": { + "type": { + "kind": "scalar", + "name": "uint64" + }, + "presence": "required", + "nullable": false + } + }, + "additional_properties": false, + "constraints": [ + "surface is null only in presence-changed after a clear, disconnect, or surface exit; presence-list never returns such entries.", + "color is a palette slot in 0..8, stable for the connection." + ] + }, + "PresenceListResult": { + "kind": "object", + "fields": { + "entries": { + "type": { + "kind": "array", + "items": { + "kind": "ref", + "name": "PresenceEntry" + } + }, + "presence": "required", + "nullable": false + } + }, + "additional_properties": false } }, "commands": { @@ -9764,6 +9974,16 @@ "default": null, "since": 9, "capability": "surface-subscribe-filter" + }, + "presence_only": { + "type": { + "kind": "scalar", + "name": "boolean" + }, + "presence": "optional", + "nullable": true, + "since": 12, + "capability": "presence-v1" } }, "additional_properties": false @@ -10319,6 +10539,85 @@ "constraints": [ "Owner-only diagnostics; never journaled and safe to poll." ] + }, + "presence-update": { + "authority": "control", + "since": 12, + "capability": "presence-v1", + "request": { + "kind": "object", + "fields": { + "surface": { + "type": { + "kind": "ref", + "name": "Id" + }, + "presence": "required", + "nullable": false + }, + "pointer": { + "type": { + "kind": "ref", + "name": "PresenceAnchor" + }, + "presence": "optional", + "nullable": true + }, + "highlight": { + "type": { + "kind": "ref", + "name": "PresenceHighlight" + }, + "presence": "optional", + "nullable": true + } + }, + "additional_properties": false + }, + "result": { + "kind": "ref", + "name": "EmptyResult" + }, + "stream": null, + "constraints": [ + "Replaces this connection's whole presence state; omitted pointer or highlight means none.", + "At most 240 updates per second per connection; more fail with a bad request error.", + "Never journaled; a server restart forgets all presence." + ] + }, + "presence-clear": { + "authority": "control", + "since": 12, + "capability": "presence-v1", + "request": { + "kind": "object", + "fields": {}, + "additional_properties": false + }, + "result": { + "kind": "ref", + "name": "EmptyResult" + }, + "stream": null, + "constraints": [] + }, + "presence-list": { + "authority": "control", + "since": 12, + "capability": "presence-v1", + "request": { + "kind": "object", + "fields": {}, + "additional_properties": false + }, + "result": { + "kind": "ref", + "name": "PresenceListResult" + }, + "stream": null, + "constraints": [ + "Pointers idle for 60 seconds are dropped unless their highlight mode is pin." + ] } }, "events": { @@ -13023,6 +13322,100 @@ }, "additional_properties": false } + }, + "presence-changed": { + "since": 12, + "capability": "presence-v1", + "streams": [ + "subscribe" + ], + "emission": "emitted", + "payload": { + "kind": "object", + "fields": { + "event": { + "type": { + "kind": "literal", + "value": "presence-changed" + }, + "presence": "required", + "nullable": false + }, + "client": { + "type": { + "kind": "scalar", + "name": "uint64" + }, + "presence": "required", + "nullable": false + }, + "name": { + "type": { + "kind": "scalar", + "name": "string" + }, + "presence": "required", + "nullable": true + }, + "kind": { + "type": { + "kind": "scalar", + "name": "string" + }, + "presence": "required", + "nullable": true + }, + "color": { + "type": { + "kind": "scalar", + "name": "uint64" + }, + "presence": "required", + "nullable": false + }, + "surface": { + "type": { + "kind": "ref", + "name": "Id" + }, + "presence": "required", + "nullable": true + }, + "pointer": { + "type": { + "kind": "ref", + "name": "PresenceAnchor" + }, + "presence": "required", + "nullable": true + }, + "highlight": { + "type": { + "kind": "ref", + "name": "PresenceHighlight" + }, + "presence": "required", + "nullable": true + }, + "updated_at_ms": { + "type": { + "kind": "scalar", + "name": "uint64" + }, + "presence": "required", + "nullable": false + }, + "generation": { + "type": { + "kind": "scalar", + "name": "uint64" + }, + "presence": "required", + "nullable": false + } + }, + "additional_properties": false + } } } } diff --git a/cmux.xcodeproj/project.pbxproj b/cmux.xcodeproj/project.pbxproj index 2426197c84fd..983184c205f2 100644 --- a/cmux.xcodeproj/project.pbxproj +++ b/cmux.xcodeproj/project.pbxproj @@ -747,6 +747,11 @@ F11347000000000000000003 /* CloudPortOpenRegressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11347000000000000000004 /* CloudPortOpenRegressionTests.swift */; }; 7A0CE100000000000000071C /* CloudPortRoutePlan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0CE100000000000000071B /* CloudPortRoutePlan.swift */; }; 7A0CE100000000000000072C /* CloudPortRoutePlanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0CE100000000000000072B /* CloudPortRoutePlanTests.swift */; }; + C113273010000000000000001 /* CloudPresenceEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = C113273020000000000000001 /* CloudPresenceEntry.swift */; }; + C113273110000000000000001 /* CloudPresenceLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = C113273120000000000000001 /* CloudPresenceLink.swift */; }; + C113273310000000000000001 /* CloudPresenceOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C113273320000000000000001 /* CloudPresenceOverlayView.swift */; }; + C113273210000000000000001 /* CloudPresenceStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C113273220000000000000001 /* CloudPresenceStore.swift */; }; + C113273410000000000000001 /* CloudPresenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C113273420000000000000001 /* CloudPresenceTests.swift */; }; 7A0CE1000000000000000027 /* CloudPrivateNetworkGate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0CE1000000000000000001 /* CloudPrivateNetworkGate.swift */; }; 7A0CE1000000000000000504 /* CloudPrivateNetworkPurpose.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0CE1000000000000000503 /* CloudPrivateNetworkPurpose.swift */; }; 7A0CE1000000000000000304 /* CloudPrivateNetworkUse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0CE1000000000000000303 /* CloudPrivateNetworkUse.swift */; }; @@ -4380,6 +4385,11 @@ F11347000000000000000004 /* CloudPortOpenRegressionTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudPortOpenRegressionTests.swift; sourceTree = ""; }; 7A0CE100000000000000071B /* CloudPortRoutePlan.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudPortRoutePlan.swift; sourceTree = ""; }; 7A0CE100000000000000072B /* CloudPortRoutePlanTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudPortRoutePlanTests.swift; sourceTree = ""; }; + C113273020000000000000001 /* CloudPresenceEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudPresenceEntry.swift; sourceTree = ""; }; + C113273120000000000000001 /* CloudPresenceLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudPresenceLink.swift; sourceTree = ""; }; + C113273320000000000000001 /* CloudPresenceOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudPresenceOverlayView.swift; sourceTree = ""; }; + C113273220000000000000001 /* CloudPresenceStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudPresenceStore.swift; sourceTree = ""; }; + C113273420000000000000001 /* CloudPresenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudPresenceTests.swift; sourceTree = ""; }; 7A0CE1000000000000000001 /* CloudPrivateNetworkGate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudPrivateNetworkGate.swift; sourceTree = ""; }; 7A0CE1000000000000000503 /* CloudPrivateNetworkPurpose.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudPrivateNetworkPurpose.swift; sourceTree = ""; }; 7A0CE1000000000000000303 /* CloudPrivateNetworkUse.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CloudPrivateNetworkUse.swift; sourceTree = ""; }; @@ -7529,6 +7539,10 @@ B8B056D80000000000000002 /* MobileHostIdentityTests.swift */ = {isa = PBXFileRef C11324420000000000000001 /* CloudTuiManualIOFrame.swift */, C11324440000000000000001 /* CloudTuiRemoteColors.swift */, C11324020000000000000001 /* CloudTuiManualIOFrameDecoder.swift */, + C113273020000000000000001 /* CloudPresenceEntry.swift */, + C113273120000000000000001 /* CloudPresenceLink.swift */, + C113273220000000000000001 /* CloudPresenceStore.swift */, + C113273320000000000000001 /* CloudPresenceOverlayView.swift */, C11324220000000000000001 /* CloudTuiManualIOGrid.swift */, C11322C20000000000000001 /* CloudTuiLegacySnapshotParser.swift */, C11322D20000000000000001 /* CloudTuiManualIOConnection.swift */, @@ -9799,6 +9813,7 @@ B8B056D80000000000000002 /* MobileHostIdentityTests.swift */ = {isa = PBXFileRef A54730000000000000000005 /* DeferredAgentResumeIndexFallbackTests.swift */, C11322A20000000000000001 /* CloudManualMirrorTransportTests.swift */, C12376020000000000000001 /* CloudManualMirrorPresentationTests.swift */, + C113273420000000000000001 /* CloudPresenceTests.swift */, 9520B0019520B0019520B001 /* HermesFirstClassSupportTests.swift */, A11CE0020000000000000001 /* AboutLicensesResourceTests.swift */, F2000001A1B2C3D4E5F60718 /* UpdatePillReleaseVisibilityTests.swift */, @@ -11823,6 +11838,10 @@ B8B056D80000000000000002 /* MobileHostIdentityTests.swift */ = {isa = PBXFileRef 7A0CE1000000000000000716 /* CloudPortForwardRelay.swift in Sources */, 7A0CE1000000000000000712 /* CloudPortForwardTarget.swift in Sources */, 7A0CE100000000000000071C /* CloudPortRoutePlan.swift in Sources */, + C113273010000000000000001 /* CloudPresenceEntry.swift in Sources */, + C113273110000000000000001 /* CloudPresenceLink.swift in Sources */, + C113273310000000000000001 /* CloudPresenceOverlayView.swift in Sources */, + C113273210000000000000001 /* CloudPresenceStore.swift in Sources */, 7A0CE1000000000000000027 /* CloudPrivateNetworkGate.swift in Sources */, 7A0CE1000000000000000504 /* CloudPrivateNetworkPurpose.swift in Sources */, 7A0CE1000000000000000304 /* CloudPrivateNetworkUse.swift in Sources */, @@ -14128,6 +14147,7 @@ B8B056D80000000000000002 /* MobileHostIdentityTests.swift */ = {isa = PBXFileRef 602239E07C1B45F48EA589EC /* CloudPortForwardAddressReuseTests.swift in Sources */, F11347000000000000000003 /* CloudPortOpenRegressionTests.swift in Sources */, 7A0CE100000000000000072C /* CloudPortRoutePlanTests.swift in Sources */, + C113273410000000000000001 /* CloudPresenceTests.swift in Sources */, 0C45DC8D9BE54DDEA2397892 /* CloudPrivateRouteSelectionTests.swift in Sources */, AC38407F09E94DA38B94F168 /* CloudProviderRefreshCoordinatorTests.swift in Sources */, 4DB42AF8827945DFADF7C4D4 /* CloudSidebarSurfaceRegressionTests.swift in Sources */, diff --git a/cmuxTests/CloudManualMirrorTransportTests.swift b/cmuxTests/CloudManualMirrorTransportTests.swift index b77dcaf73f05..0bb725094094 100644 --- a/cmuxTests/CloudManualMirrorTransportTests.swift +++ b/cmuxTests/CloudManualMirrorTransportTests.swift @@ -256,7 +256,7 @@ struct CloudManualMirrorTransportTests { ], ]) let frame = try #require(CloudTuiManualIOFrameDecoder().decode(line)) - guard case let .response(requestID, ok, lease, capabilities, outcome, accepted, error) = frame else { + guard case let .response(requestID, ok, lease, capabilities, outcome, accepted, error, _) = frame else { Issue.record("expected a response frame") return } diff --git a/cmuxTests/CloudPresenceTests.swift b/cmuxTests/CloudPresenceTests.swift new file mode 100644 index 000000000000..357059f0bb3c --- /dev/null +++ b/cmuxTests/CloudPresenceTests.swift @@ -0,0 +1,148 @@ +import Foundation +import Testing + +#if canImport(cmux_DEV) +@testable import cmux_DEV +#elseif canImport(cmux) +@testable import cmux +#endif + +/// Behavioral coverage for the Mac side of cmux-tui collaboration presence: +/// wire decoding, the commands a presence link sends, and the row mapping an +/// overlay applies when two viewers sit at different scrollback offsets. +@Suite +struct CloudPresenceTests { + private let decoder = CloudTuiManualIOFrameDecoder() + private let commands = CloudTuiManualIOCommand() + + private static func line(_ object: [String: Any]) throws -> Data { + try JSONSerialization.data(withJSONObject: object) + } + + @Test + func presenceChangedDecodesPointerAndHighlight() throws { + let frame = try #require(decoder.decode(try Self.line([ + "event": "presence-changed", + "client": 3, + "name": "ada", + "kind": "mac", + "color": 11, + "surface": 7, + "pointer": ["kind": "cell", "row": 3, "col": 12, "scroll_offset": 5], + "highlight": [ + "start": ["kind": "cell", "row": 3, "col": 0], + "end": ["kind": "cell", "row": 4, "col": 40], + "mode": "laser", + ], + "updated_at_ms": 1_757_548_800_000, + "generation": 9, + ]))) + guard case let .presence(entry) = frame else { + Issue.record("expected a presence frame, got \(frame)") + return + } + #expect(entry.client == 3) + #expect(entry.name == "ada") + #expect(entry.color == 3, "palette slot wraps to 0..<8") + #expect(entry.surface == 7) + #expect(entry.pointer == .cell(row: 3, col: 12, scrollOffset: 5)) + #expect(entry.highlight?.mode == .laser) + #expect(entry.highlight?.end == .cell(row: 4, col: 40, scrollOffset: 0)) + #expect(entry.generation == 9) + #expect(!entry.isCleared) + } + + @Test + func presenceClearWithNullSurfaceStillDecodes() throws { + // Byte-attach events require a positive surface; a presence clear is + // the one event that legitimately carries `surface: null`. + let frame = try #require(decoder.decode(try Self.line([ + "event": "presence-changed", + "client": 3, + "name": NSNull(), + "kind": NSNull(), + "color": 3, + "surface": NSNull(), + "pointer": NSNull(), + "highlight": NSNull(), + "updated_at_ms": 1, + "generation": 10, + ]))) + guard case let .presence(entry) = frame else { + Issue.record("expected a presence frame, got \(frame)") + return + } + #expect(entry.isCleared) + #expect(entry.pointer == nil) + #expect(entry.highlight == nil) + } + + @Test + func presenceCommandsCarryAnchorsAndCapability() throws { + let info = commands.setPresenceClientInfo(name: "ada", kind: "mac", requestID: 2) + #expect(info["cmd"] as? String == "set-client-info") + #expect(info["capabilities"] as? [String] == ["presence-v1"]) + + let subscribe = commands.subscribePresence(requestID: 3) + #expect(subscribe["cmd"] as? String == "subscribe") + #expect(subscribe["presence_only"] as? Bool == true) + + let update = commands.presenceUpdate( + surfaceID: 7, + pointer: .cell(row: 1, col: 2, scrollOffset: 3), + highlight: CloudPresenceHighlight( + start: .cell(row: 1, col: 0, scrollOffset: 3), + end: .point(x: 4.5, y: 6), + mode: .pin + ), + requestID: 4 + ) + #expect(update["cmd"] as? String == "presence-update") + #expect(update["surface"] as? UInt64 == 7) + let pointer = try #require(update["pointer"] as? [String: Any]) + #expect(pointer["kind"] as? String == "cell") + #expect(pointer["row"] as? Int == 1) + #expect(pointer["scroll_offset"] as? UInt64 == 3) + let highlight = try #require(update["highlight"] as? [String: Any]) + #expect(highlight["mode"] as? String == "pin") + #expect((highlight["end"] as? [String: Any])?["kind"] as? String == "point") + #expect(JSONSerialization.isValidJSONObject(update)) + + let list = commands.listClients(requestID: 6) + #expect(list["cmd"] as? String == "list-clients") + + let listResponse = try #require(decoder.decode(try Self.line([ + "id": 6, + "ok": true, + "data": [["client": 42, "self": true]], + ]))) + guard case let .response(requestID, ok, _, _, _, _, _, selfClientID) = listResponse else { + Issue.record("expected a list-clients response") + return + } + #expect(requestID == 6) + #expect(ok) + #expect(selfClientID == 42) + + let bare = commands.presenceUpdate(surfaceID: 7, pointer: nil, highlight: nil, requestID: 5) + #expect(bare["pointer"] == nil) + #expect(bare["highlight"] == nil) + } + + @Test + func viewerRowShiftsByScrollbackOffsetDifference() { + let anchor = CloudPresenceAnchor.cell(row: 10, col: 0, scrollOffset: 4) + // Same offset: same row. + #expect(anchor.viewerRow(viewerScrollOffset: 4, rows: 24) == 10) + // Viewer scrolled two rows further up: the cell appears two rows lower. + #expect(anchor.viewerRow(viewerScrollOffset: 6, rows: 24) == 12) + // Viewer at the live bottom: the cell is four rows higher. + #expect(anchor.viewerRow(viewerScrollOffset: 0, rows: 24) == 6) + // Off the top or bottom of the viewer's grid: hidden. + #expect(anchor.viewerRow(viewerScrollOffset: 0, rows: 5) == nil) + #expect(CloudPresenceAnchor.cell(row: 0, col: 0, scrollOffset: 30) + .viewerRow(viewerScrollOffset: 0, rows: 24) == nil) + // Points never map to a terminal row. + #expect(CloudPresenceAnchor.point(x: 1, y: 2).viewerRow(viewerScrollOffset: 0, rows: 24) == nil) + } +}