From d7b281cfbc820ca5d627cfebc20f9ce8c7fe2889 Mon Sep 17 00:00:00 2001 From: SarthakWade Date: Sat, 12 Sep 2026 23:12:46 +0530 Subject: [PATCH 1/5] feat(sdk): add canonical protocol contract --- README.md | 6 + apps/headless/LinuxHost/main.swift | 4 + apps/headless/MCP/main.swift | 6 +- apps/headless/Sources/HeadlessCLI/main.swift | 54 +- .../HeadlessProtocol/Authentication.swift | 26 +- .../Sources/HeadlessProtocol/CLI.swift | 17 +- .../HeadlessProtocol/Capabilities.swift | 5 + .../Sources/HeadlessProtocol/HostError.swift | 2 +- .../Sources/HeadlessProtocol/Protocol.swift | 50 +- .../HeadlessProtocol/ProtocolSchema.swift | 709 ++++++++++++++++++ .../HeadlessProtocol/SupervisedHost.swift | 51 ++ .../Tests/HeadlessMCPTests/main.swift | 11 + .../HeadlessProtocolTests/ProtocolTests.swift | 176 ++++- apps/headless/build.sh | 6 +- apps/headless/docs/COMMANDS.md | 11 +- apps/headless/docs/P4.md | 20 + apps/headless/main.swift | 5 + apps/headless/test.sh | 7 + docs/roadmap/architecture-decisions.md | 44 ++ sdk/README.md | 51 ++ sdk/protocol-schema.json | 1 + 21 files changed, 1183 insertions(+), 79 deletions(-) create mode 100644 apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift create mode 100644 apps/headless/Sources/HeadlessProtocol/SupervisedHost.swift create mode 100644 sdk/README.md create mode 100644 sdk/protocol-schema.json diff --git a/README.md b/README.md index ab211f6..be9b6d3 100644 --- a/README.md +++ b/README.md @@ -301,6 +301,12 @@ npx @lockintime/headless help npx -p @lockintime/headless headless-mcp ``` +SDK generators consume the Swift-owned contract printed by `headless schema`. +The checked-in `sdk/protocol-schema.json` is verified against that output in +the protocol suite. Wire compatibility is exact by protocol version and is +independent of the product release version. Clients must negotiate engine +capabilities and fail explicitly rather than emulate missing behavior. + The launcher selects the matching macOS or Linux release, verifies it against the release `SHA256SUMS`, validates its archive and embedded product version, and caches it privately for subsequent commands. Its download origin is fixed diff --git a/apps/headless/LinuxHost/main.swift b/apps/headless/LinuxHost/main.swift index f7a6bcc..1847564 100644 --- a/apps/headless/LinuxHost/main.swift +++ b/apps/headless/LinuxHost/main.swift @@ -190,6 +190,9 @@ do { ) let server = LocalSocketServer() try server.start { request in core.handle(request) } + let ownerMonitor = SupervisedHostOwnerMonitor.startIfRequested { + stopped.signal() + } #if canImport(Glibc) signal(SIGTERM, SIG_IGN) @@ -203,6 +206,7 @@ do { #endif stopped.wait() + ownerMonitor?.stop() server.stop() core.stop() } catch { diff --git a/apps/headless/MCP/main.swift b/apps/headless/MCP/main.swift index aa3004e..a00f5ab 100644 --- a/apps/headless/MCP/main.swift +++ b/apps/headless/MCP/main.swift @@ -52,7 +52,11 @@ while let line = readLine() { write(["jsonrpc": "2.0", "id": id ?? NSNull(), "result": [ "protocolVersion": "2025-06-18", "capabilities": ["tools": ["listChanged": false]], - "serverInfo": ["name": "headless", "version": headlessProductVersion], + "serverInfo": [ + "name": "headless", "version": headlessProductVersion, + "headlessProtocolVersion": headlessProtocolVersion, + "headlessSchemaVersion": headlessProtocolSchemaVersion, + ], ]]) case "notifications/initialized": continue diff --git a/apps/headless/Sources/HeadlessCLI/main.swift b/apps/headless/Sources/HeadlessCLI/main.swift index 78553cb..506e084 100644 --- a/apps/headless/Sources/HeadlessCLI/main.swift +++ b/apps/headless/Sources/HeadlessCLI/main.swift @@ -22,6 +22,11 @@ private func printResponse(_ response: CommandResponse) throws { } private struct HostLauncher { + struct Launch { + let response: CommandResponse + let process: Process? + } + let client = LocalSocketClient() func ping() -> CommandResponse? { @@ -30,14 +35,16 @@ private struct HostLauncher { func start( presentation: AgentStartupPresentation? = nil, - allowlist: NavigationAllowlist = .unrestricted - ) throws -> CommandResponse { + allowlist: NavigationAllowlist = .unrestricted, + supervised: Bool = false + ) throws -> Launch { #if !os(macOS) if presentation != nil { throw SettingsError.unsupportedPlatform("startup-presentation") } #endif if let response = ping(), response.ok { + if supervised { throw HostLaunchError.alreadyRunning } try validateRunningAllowlist(response, requested: allowlist) - return response + return Launch(response: response, process: nil) } #if os(Linux) // Report an unsupported browser directly to the operator instead of @@ -60,13 +67,14 @@ private struct HostLauncher { let effectivePresentation = AgentStartupPresentation.background #endif environment["HEADLESS_START_FOREGROUND"] = effectivePresentation == .foreground ? "1" : "0" + environment["HEADLESS_SUPERVISED"] = supervised ? "1" : "0" if allowlist.isRestricted { environment[headlessNavigationAllowlistEnvironmentKey] = allowlist.environmentValue } else { environment.removeValue(forKey: headlessNavigationAllowlistEnvironmentKey) } process.environment = environment - process.standardInput = FileHandle.nullDevice + process.standardInput = supervised ? FileHandle.standardInput : FileHandle.nullDevice if let hostLog = environment["HEADLESS_HOST_LOG"], hostLog.hasPrefix("/") { let logURL = URL(fileURLWithPath: hostLog) FileManager.default.createFile(atPath: logURL.path, contents: nil) @@ -84,20 +92,38 @@ private struct HostLauncher { if let response = ping(), response.ok { do { try validateRunningAllowlist(response, requested: allowlist) - return response + return Launch(response: response, process: supervised ? process : nil) } catch { - process.terminate() + terminateAndReap(process) throw error } } if !process.isRunning { + process.waitUntilExit() throw HostLaunchError.exited(process.terminationStatus) } Thread.sleep(forTimeInterval: 0.05) } while Date() < deadline + terminateAndReap(process) throw HostLaunchError.timedOut } + private func terminateAndReap(_ process: Process) { + guard process.isRunning else { + process.waitUntilExit() + return + } + process.terminate() + let deadline = Date().addingTimeInterval(3) + while process.isRunning, Date() < deadline { + Thread.sleep(forTimeInterval: 0.05) + } + if process.isRunning { + _ = kill(process.processIdentifier, SIGKILL) + } + process.waitUntilExit() + } + private func validateRunningAllowlist( _ response: CommandResponse, requested allowlist: NavigationAllowlist ) throws { @@ -144,6 +170,7 @@ private struct HostLauncher { private enum HostLaunchError: Error, CustomStringConvertible { case notFound + case alreadyRunning case timedOut case exited(Int32) case allowlistMismatch(running: [String], requested: [String]) @@ -151,6 +178,8 @@ private enum HostLaunchError: Error, CustomStringConvertible { var description: String { switch self { case .notFound: return "Could not find headless-host. Run the Headless build first." + case .alreadyRunning: + return "A shared Headless host is already running. Stop it before starting a supervised host." case .timedOut: return "Headless host did not become ready within 8 seconds." case .exited(let status): return "Headless host exited during startup (status \(status))." case .allowlistMismatch(let running, let requested): @@ -239,6 +268,8 @@ do { print("headless \(headlessProductVersion)") case .capabilities: printJSON(capabilitiesDocument) + case .schema: + printJSON(protocolSchemaDocument) case .runtime: #if os(Linux) printJSON(try ChromiumRuntimeResolver().resolve().diagnostic) @@ -248,8 +279,15 @@ do { "supported": .bool(true), "transport": .string("native-webkit"), ])) #endif - case .start(let presentation, let allowlist): - try printResponse(try HostLauncher().start(presentation: presentation, allowlist: allowlist)) + case .start(let presentation, let allowlist, let supervised): + let launch = try HostLauncher().start( + presentation: presentation, allowlist: allowlist, supervised: supervised + ) + try printResponse(launch.response) + if let process = launch.process { + process.waitUntilExit() + exit(process.terminationStatus) + } case .config(let command): let settings = try SettingsStore.production() switch command { diff --git a/apps/headless/Sources/HeadlessProtocol/Authentication.swift b/apps/headless/Sources/HeadlessProtocol/Authentication.swift index a369f1a..6cf8650 100644 --- a/apps/headless/Sources/HeadlessProtocol/Authentication.swift +++ b/apps/headless/Sources/HeadlessProtocol/Authentication.swift @@ -302,19 +302,19 @@ public enum AuthenticationError: Error, Equatable, CustomStringConvertible { public var code: String { switch self { - case .challengeNotFound: return "AUTH_CHALLENGE_NOT_FOUND" - case .challengeExpired: return "AUTH_CHALLENGE_EXPIRED" - case .challengeConsumed: return "AUTH_CHALLENGE_CONSUMED" - case .originChanged: return "AUTH_ORIGIN_CHANGED" - case .formChanged: return "AUTH_FORM_CHANGED" - case .accountNotFound: return "AUTH_ACCOUNT_NOT_FOUND" - case .credentialAliasExists: return "CREDENTIAL_ALIAS_EXISTS" - case .vaultUnavailable: return "VAULT_UNAVAILABLE" - case .vaultLocked: return "VAULT_LOCKED" - case .userPresenceUnavailable: return "USER_PRESENCE_UNAVAILABLE" - case .userPresenceDenied: return "USER_PRESENCE_DENIED" - case .invalidBrokerResponse: return "VAULT_RESPONSE_INVALID" - case .brokerFailed: return "VAULT_OPERATION_FAILED" + case .challengeNotFound: return AuthenticationProtocolErrorCode.challengeNotFound.rawValue + case .challengeExpired: return AuthenticationProtocolErrorCode.challengeExpired.rawValue + case .challengeConsumed: return AuthenticationProtocolErrorCode.challengeConsumed.rawValue + case .originChanged: return AuthenticationProtocolErrorCode.originChanged.rawValue + case .formChanged: return AuthenticationProtocolErrorCode.formChanged.rawValue + case .accountNotFound: return AuthenticationProtocolErrorCode.accountNotFound.rawValue + case .credentialAliasExists: return AuthenticationProtocolErrorCode.credentialAliasExists.rawValue + case .vaultUnavailable: return AuthenticationProtocolErrorCode.vaultUnavailable.rawValue + case .vaultLocked: return AuthenticationProtocolErrorCode.vaultLocked.rawValue + case .userPresenceUnavailable: return AuthenticationProtocolErrorCode.userPresenceUnavailable.rawValue + case .userPresenceDenied: return AuthenticationProtocolErrorCode.userPresenceDenied.rawValue + case .invalidBrokerResponse: return AuthenticationProtocolErrorCode.invalidBrokerResponse.rawValue + case .brokerFailed: return AuthenticationProtocolErrorCode.brokerFailed.rawValue } } diff --git a/apps/headless/Sources/HeadlessProtocol/CLI.swift b/apps/headless/Sources/HeadlessProtocol/CLI.swift index d971af5..8d7d572 100644 --- a/apps/headless/Sources/HeadlessProtocol/CLI.swift +++ b/apps/headless/Sources/HeadlessProtocol/CLI.swift @@ -17,8 +17,12 @@ public enum LocalCommand: Equatable, Sendable { case help case version case capabilities + case schema case runtime - case start(presentation: AgentStartupPresentation?, allowlist: NavigationAllowlist) + case start( + presentation: AgentStartupPresentation?, allowlist: NavigationAllowlist, + supervised: Bool + ) case config(ConfigCLICommand) case credentials(CredentialCLICommand) } @@ -96,6 +100,9 @@ public struct CLIParser { case "capabilities": try requireEmpty(arguments) return CLIInvocation(local: .capabilities, jsonOutput: true) + case "schema": + try requireEmpty(arguments) + return CLIInvocation(local: .schema, jsonOutput: true) case "runtime": try requireEmpty(arguments) return CLIInvocation(local: .runtime, jsonOutput: true) @@ -528,6 +535,7 @@ public struct CLIParser { private func parseStart(_ arguments: [String], jsonOutput: Bool) throws -> CLIInvocation { var args = arguments + let supervised = removeFlag("--supervised", from: &args) var presentation: AgentStartupPresentation? if removeFlag("--background", from: &args) { presentation = .background @@ -547,7 +555,9 @@ public struct CLIParser { allowlist = try NavigationAllowlist.parse(rawAllows) } return CLIInvocation( - local: .start(presentation: presentation, allowlist: allowlist), + local: .start( + presentation: presentation, allowlist: allowlist, supervised: supervised + ), jsonOutput: jsonOutput ) } @@ -810,7 +820,7 @@ Core workflow: Commands: version | --version - start [--background|--foreground] [--allow PATTERN]... | status | stop | runtime + start [--background|--foreground] [--allow PATTERN]... [--supervised] | status | stop | runtime profile clear config list | config describe KEY | config get KEY config set KEY VALUE | config reset KEY @@ -852,6 +862,7 @@ Commands: flow start | flow stop [--output FLOW.json] | flow run FLOW.json report create [--output REPORT.json] capabilities + schema Global options: --session NAME target a named browser session diff --git a/apps/headless/Sources/HeadlessProtocol/Capabilities.swift b/apps/headless/Sources/HeadlessProtocol/Capabilities.swift index ab0d9df..1d12ae9 100644 --- a/apps/headless/Sources/HeadlessProtocol/Capabilities.swift +++ b/apps/headless/Sources/HeadlessProtocol/Capabilities.swift @@ -185,6 +185,10 @@ public let capabilitiesDocument: JSONValue = { .sorted() return .object([ "protocolVersion": .string(headlessProtocolVersion), + "protocolSchema": .object([ + "command": .string("schema"), + "schemaVersion": .number(Double(headlessProtocolSchemaVersion)), + ]), "transport": stringArray(["local-unix-socket"]), "currentEngine": .string(currentBrowserEngineCapabilities.engine.rawValue), "commands": .array(CommandName.allCases.map { .string($0.rawValue) }), @@ -207,6 +211,7 @@ public let capabilitiesDocument: JSONValue = { "localCommands": stringArray([ "config.describe", "config.get", "config.list", "config.reset", "config.set", "credentials.add", "credentials.list", "credentials.remove", "credentials.rename", + "schema", ]), "settings": .object([ "definitions": .array(SettingsRegistry.shared.definitions.compactMap { definition in diff --git a/apps/headless/Sources/HeadlessProtocol/HostError.swift b/apps/headless/Sources/HeadlessProtocol/HostError.swift index 3a57174..a64d2a5 100644 --- a/apps/headless/Sources/HeadlessProtocol/HostError.swift +++ b/apps/headless/Sources/HeadlessProtocol/HostError.swift @@ -1,6 +1,6 @@ import Foundation -public enum HostErrorCode: String, Sendable { +public enum HostErrorCode: String, CaseIterable, Sendable { case timedOut = "TIMEOUT" case elementNotFound = "ELEMENT_NOT_FOUND" case regionNotFound = "REGION_NOT_FOUND" diff --git a/apps/headless/Sources/HeadlessProtocol/Protocol.swift b/apps/headless/Sources/HeadlessProtocol/Protocol.swift index f61f87a..6e30453 100644 --- a/apps/headless/Sources/HeadlessProtocol/Protocol.swift +++ b/apps/headless/Sources/HeadlessProtocol/Protocol.swift @@ -155,11 +155,7 @@ public struct CommandRequest: Codable, Equatable, Sendable { } private func validateCommandParameters() throws { - func allow(_ keys: Set) throws { - if let unexpected = parameters.keys.first(where: { !keys.contains($0) }) { - throw ProtocolValidationError.invalidParameter("Unexpected parameter for \(command.rawValue): \(unexpected)") - } - } + try protocolCommandDefinition(for: command).validate(parameters) func string(_ key: String, required: Bool = false, maximumBytes: Int = 8_192) throws -> String? { guard let value = parameters[key] else { if required { throw ProtocolValidationError.invalidParameter("Missing string parameter: \(key)") } @@ -194,11 +190,7 @@ public struct CommandRequest: Codable, Equatable, Sendable { } return result } - func target(allowValue: Bool, required: Bool = true, validateAllowedKeys: Bool = true) throws { - let allowed: Set = allowValue ? ["target", "role", "name", "value"] : ["target", "role", "name"] - if validateAllowedKeys { - try allow(allowed) - } + func target(allowValue: Bool, required: Bool = true) throws { let reference = try string("target", maximumBytes: 16) let role = try string("role", maximumBytes: 128) let name = try string("name", maximumBytes: 1_000) @@ -217,9 +209,8 @@ public struct CommandRequest: Codable, Equatable, Sendable { switch command { case .ping, .shutdown, .profileClear, .sessionList, .sessionClose, .back, .reload, .captureInfo, .artifactList, .recordStatus, .qaReport, .qaClear: - try allow([]) + break case .authLogin: - try allow(["challenge", "account", "interactive"]) if let challenge = try string("challenge", maximumBytes: 64), UUID(uuidString: challenge) == nil { throw ProtocolValidationError.invalidParameter("Invalid authentication challenge") @@ -238,16 +229,13 @@ public struct CommandRequest: Codable, Equatable, Sendable { ) } case .sessionCreate: - try allow(["name", "isolated"]) if let name = try string("name", required: true, maximumBytes: 64) { try validateIdentifier(name, field: "session") } try boolean("isolated") case .visit: - try allow(["url"]) if let value = try string("url", required: true) { _ = try normalizedWebURL(value) } case .inspect: - try allow(["interactive", "text", "context", "task", "within", "limit", "budget", "depth"]) try boolean("interactive"); try boolean("text") if let context = try string("context", maximumBytes: 16), !["summary", "outline", "text", "actions", "full"].contains(context) { @@ -273,16 +261,13 @@ public struct CommandRequest: Codable, Equatable, Sendable { case .fill: try target(allowValue: true) case .upload: - try target(allowValue: false, validateAllowedKeys: false) - try allow(["target", "role", "name", "artifact"]) + try target(allowValue: false) if let artifact = try string("artifact", required: true, maximumBytes: 128) { try validateArtifactName(artifact, expectedExtensions: uploadArtifactExtensions) } case .press: - try allow(["key"]) _ = try string("key", required: true, maximumBytes: 32) case .scroll: - try allow(["direction", "amount"]) let direction = try string("direction") ?? "down" guard ["up", "down", "top", "bottom"].contains(direction) else { throw ProtocolValidationError.invalidParameter("Invalid scroll direction") @@ -292,20 +277,17 @@ public struct CommandRequest: Codable, Equatable, Sendable { maximum: ProtocolBounds.scrollAmount.upperBound ) case .wait: - try allow(["settled", "url", "text", "timeoutMs"]) try boolean("settled") _ = try string("url") _ = try string("text", maximumBytes: 30_000) _ = try number("timeoutMs", minimum: 100, maximum: 120_000) case .tour: - try allow(["fullPage", "pace"]) try boolean("fullPage") _ = try number("pace", minimum: 100, maximum: 5_000) case .screenshot: - try allow(["fullPage", "target", "role", "name", "output", "series", "outputPrefix", "format", "clipboard"]) try boolean("fullPage") try boolean("clipboard") - try target(allowValue: false, required: false, validateAllowedKeys: false) + try target(allowValue: false, required: false) let hasTarget = parameters["target"] != nil || parameters["role"] != nil || parameters["name"] != nil let series = try string("series", maximumBytes: 32) if let series, !["viewport", "section"].contains(series) { @@ -347,7 +329,6 @@ public struct CommandRequest: Codable, Equatable, Sendable { try validateArtifactPrefix(outputPrefix) } case .recordStart: - try allow(["output", "fps", "format", "quality"]) let format = try recordingFormat( explicit: string("format", maximumBytes: 16), output: parameters["output"]?.stringValue @@ -360,41 +341,33 @@ public struct CommandRequest: Codable, Equatable, Sendable { _ = try RecordingQuality.parse(quality) } case .recordStop: - try allow(["output"]) if let output = try string("output", maximumBytes: 128) { try validateArtifactName(output, expectedExtensions: RecordingFormat.artifactExtensions) } case .consoleList: - try allow(["level", "limit"]) if let level = try string("level", maximumBytes: 16), !["all", "log", "info", "debug", "warn", "error", "assert"].contains(level) { throw ProtocolValidationError.invalidParameter("Invalid console level") } _ = try number("limit", minimum: 1, maximum: 200) case .networkList: - try allow(["failed", "status", "limit"]) try boolean("failed") _ = try number("status", minimum: 100, maximum: 599) _ = try number("limit", minimum: 1, maximum: 200) case .networkGet: - try allow(["requestId"]) _ = try string("requestId", required: true, maximumBytes: 128) case .stylesGet: - try target(allowValue: false, validateAllowedKeys: false) - try allow(["target", "role", "name", "properties"]) + try target(allowValue: false) _ = try strings("properties", maximumItems: 64, maximumBytes: 128) case .cookiesList: - try allow(["includeValues"]) try boolean("includeValues") case .storageList: - try allow(["scope", "includeValues"]) let scope = try string("scope", maximumBytes: 16) ?? "all" guard ["local", "session", "all"].contains(scope) else { throw ProtocolValidationError.invalidParameter("Invalid storage scope") } try boolean("includeValues") case .visualCompare: - try allow(["before", "after", "output"]) for key in ["before", "after"] { if let name = try string(key, required: true, maximumBytes: 128) { try validateArtifactName(name, expectedExtension: "png") @@ -404,26 +377,22 @@ public struct CommandRequest: Codable, Equatable, Sendable { try validateArtifactName(output, expectedExtension: "png") } case .performanceGet, .animationList: - try allow([]) + break case .reportCreate: - try allow(["output"]) if let output = try string("output", maximumBytes: 128) { try validateArtifactName(output, expectedExtension: "json") } case .flowStart: - try allow([]) + break case .flowStop: - try allow(["output"]) if let output = try string("output", maximumBytes: 128) { try validateArtifactName(output, expectedExtension: "json") } case .flowRun: - try allow(["input"]) if let input = try string("input", required: true, maximumBytes: 128) { try validateArtifactName(input, expectedExtension: "json") } case .networkEmulate: - try allow(["offline", "latencyMs", "downloadKbps", "uploadKbps"]) try boolean("offline") _ = try number( "latencyMs", minimum: ProtocolBounds.networkLatencyMilliseconds.lowerBound, @@ -438,7 +407,6 @@ public struct CommandRequest: Codable, Equatable, Sendable { maximum: ProtocolBounds.networkThroughputKbps.upperBound ) case .networkMockSet: - try allow(["url", "status", "body", "contentType"]) if let url = try string("url", required: true, maximumBytes: 8_192) { _ = try normalizedWebURL(url) } _ = try number("status", minimum: 100, maximum: 599) _ = try string("body", required: true, maximumBytes: 65_536) @@ -447,7 +415,7 @@ public struct CommandRequest: Codable, Equatable, Sendable { throw ProtocolValidationError.invalidParameter("Invalid content type") } case .networkMockClear: - try allow([]) + break } } } diff --git a/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift b/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift new file mode 100644 index 0000000..4667efb --- /dev/null +++ b/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift @@ -0,0 +1,709 @@ +import Foundation + +public let headlessProtocolSchemaVersion = 1 + +public enum ProtocolParameterKind: String, Sendable { + case string + case number + case integer + case boolean + case stringArray = "string-array" +} + +public enum ProtocolResultValueKind: String, Sendable { + case string + case stringOrNull = "string-or-null" + case number + case boolean + case object + case array + case json +} + +public enum AuthenticationProtocolErrorCode: String, CaseIterable, Sendable { + case challengeNotFound = "AUTH_CHALLENGE_NOT_FOUND" + case challengeExpired = "AUTH_CHALLENGE_EXPIRED" + case challengeConsumed = "AUTH_CHALLENGE_CONSUMED" + case originChanged = "AUTH_ORIGIN_CHANGED" + case formChanged = "AUTH_FORM_CHANGED" + case accountNotFound = "AUTH_ACCOUNT_NOT_FOUND" + case credentialAliasExists = "CREDENTIAL_ALIAS_EXISTS" + case vaultUnavailable = "VAULT_UNAVAILABLE" + case vaultLocked = "VAULT_LOCKED" + case userPresenceUnavailable = "USER_PRESENCE_UNAVAILABLE" + case userPresenceDenied = "USER_PRESENCE_DENIED" + case invalidBrokerResponse = "VAULT_RESPONSE_INVALID" + case brokerFailed = "VAULT_OPERATION_FAILED" +} + +public struct ProtocolResultField: Sendable { + public let name: String + public let kind: ProtocolResultValueKind + public let required: Bool + + fileprivate var document: JSONValue { + .object([ + "name": .string(name), + "type": .string(kind.rawValue), + "required": .bool(required), + ]) + } +} + +public struct ProtocolResultDefinition: Sendable { + public let name: String + public let fields: [ProtocolResultField] + + fileprivate var document: JSONValue { + .object([ + "name": .string(name), + "type": .string("object"), + "additionalProperties": .bool(true), + "fields": .array(fields.map(\.document)), + ]) + } +} + +private func resultField( + _ name: String, _ kind: ProtocolResultValueKind, required: Bool = true +) -> ProtocolResultField { + ProtocolResultField(name: name, kind: kind, required: required) +} + +private func result( + _ name: String, _ fields: [ProtocolResultField] = [] +) -> ProtocolResultDefinition { + ProtocolResultDefinition(name: name, fields: fields) +} + +public func protocolResultDefinition(for command: CommandName) -> ProtocolResultDefinition { + switch command { + case .ping: + return result("HostStatus", [ + resultField("ready", .boolean), resultField("pid", .number), + resultField("engine", .string), resultField("platform", .string), + resultField("productVersion", .string), resultField("protocolVersion", .string), + resultField("capabilities", .object), resultField("recordingAvailable", .boolean), + resultField("artifactDirectory", .string), resultField("navigationAllowlist", .array), + ]) + case .shutdown: return result("Shutdown", [resultField("stopping", .boolean)]) + case .profileClear: + return result("ProfileClear", [resultField("cleared", .boolean), resultField("session", .string)]) + case .sessionCreate: + return result("SessionCreate", [resultField("session", .string), resultField("isolated", .boolean)]) + case .sessionList: + return result("SessionList", [resultField("sessions", .array), resultField("details", .array)]) + case .sessionClose: return result("SessionClose", [resultField("closed", .string)]) + case .visit, .back, .reload, .wait: + return result("PageState", [ + resultField("url", .string), resultField("title", .string), + resultField("readyState", .string), resultField("text", .string), + resultField("runningAnimations", .number), resultField("mutationQuietMs", .number), + resultField("scrollY", .number), resultField("contentHeight", .number), + ]) + case .inspect: + return result("Inspection", [ + resultField("url", .string), resultField("title", .string), + resultField("contextMode", .string), resultField("viewport", .object), + resultField("untrustedContent", .boolean), + resultField("elements", .array, required: false), + resultField("regions", .array, required: false), + resultField("snippets", .array, required: false), + resultField("text", .string, required: false), + ]) + case .click: + return result("Click", [ + resultField("clicked", .string), resultField("role", .string), + resultField("name", .string), + ]) + case .fill: + return result("Fill", [resultField("filled", .string), resultField("valueLength", .number)]) + case .upload: + return result("Upload", [ + resultField("uploaded", .string), resultField("role", .string), + resultField("name", .string), resultField("artifact", .string), + ]) + case .press: return result("Press", [resultField("pressed", .string)]) + case .scroll: + return result("Scroll", [resultField("direction", .string), resultField("amount", .number)]) + case .tour: + return result("Tour", [ + resultField("start", .number), resultField("end", .number), + resultField("durationMs", .number), + ]) + case .captureInfo: + return result("CaptureInfo", [ + resultField("engine", .string), resultField("page", .object), + resultField("trace", .array), resultField("recording", .object), + ]) + case .screenshot: + return result("Screenshot", [ + resultField("name", .string, required: false), + resultField("path", .string, required: false), + resultField("kind", .string, required: false), + resultField("bytes", .number, required: false), + resultField("createdAt", .number, required: false), + resultField("artifacts", .array, required: false), + resultField("truncated", .boolean, required: false), + ]) + case .artifactList: + return result("ArtifactList", [ + resultField("directory", .string), resultField("artifacts", .array), + resultField("total", .number), resultField("omitted", .number), + resultField("truncated", .boolean), + ]) + case .recordStart, .recordStatus, .recordStop: + return result("Recording", [ + resultField("active", .boolean), resultField("format", .string, required: false), + resultField("quality", .string, required: false), + resultField("name", .string, required: false), + ]) + case .qaReport: return result("QAReport", [resultField("summary", .object, required: false)]) + case .qaClear: return result("QAClear", [resultField("cleared", .number)]) + case .consoleList: + return result("ConsoleList", [ + resultField("untrustedContent", .boolean), resultField("messages", .array), + resultField("returned", .number), resultField("available", .number), + ]) + case .networkList: + return result("NetworkList", [ + resultField("untrustedContent", .boolean), resultField("requests", .array), + resultField("returned", .number), resultField("available", .number), + ]) + case .networkGet: + return result("NetworkDetail", [ + resultField("found", .boolean), resultField("requestId", .string, required: false), + resultField("untrustedContent", .boolean), + resultField("request", .object, required: false), + ]) + case .stylesGet: + return result("Styles", [ + resultField("ref", .string), resultField("role", .string), + resultField("name", .string), resultField("box", .object), + resultField("styles", .object), + ]) + case .cookiesList: + return result("CookieList", [ + resultField("cookies", .array), resultField("returned", .number), + resultField("available", .number), resultField("truncated", .boolean), + ]) + case .storageList: + return result("StorageList", [resultField("origin", .string), resultField("stores", .array)]) + case .visualCompare: + return result("VisualComparison", [ + resultField("name", .string), resultField("changedPixels", .number, required: false), + resultField("differenceRatio", .number, required: false), + ]) + case .performanceGet: + return result("Performance", [ + resultField("url", .string), resultField("timing", .json), + resultField("webVitals", .object), resultField("resources", .object), + ]) + case .animationList: + return result("AnimationList", [ + resultField("count", .number), resultField("animations", .array), + resultField("truncated", .boolean), + ]) + case .reportCreate, .flowStop: + return result("Artifact", [ + resultField("name", .string), resultField("path", .string), + resultField("kind", .string), resultField("bytes", .number), + resultField("createdAt", .number), + ]) + case .flowStart: + return result("FlowStart", [resultField("recording", .boolean), resultField("note", .string)]) + case .flowRun: + return result("FlowRun", [resultField("completed", .number), resultField("input", .string)]) + case .networkEmulate: + return result("NetworkEmulation", [ + resultField("offline", .boolean), resultField("latencyMs", .number), + resultField("downloadKbps", .number), resultField("uploadKbps", .number), + resultField("engine", .string), + ]) + case .networkMockSet: + return result("NetworkMock", [ + resultField("url", .string), resultField("status", .number), + resultField("activeMocks", .number), + ]) + case .networkMockClear: + return result("NetworkMockClear", [resultField("cleared", .number)]) + case .authLogin: + return result("AuthenticationLogin", [ + resultField("origin", .string), resultField("account", .stringOrNull), + resultField("saved", .boolean), resultField("continuation", .string), + ]) + } +} + +public struct ProtocolParameterDefinition: Sendable { + public let name: String + public let kind: ProtocolParameterKind + public let required: Bool + public let maximumBytes: Int? + public let minimum: Double? + public let maximum: Double? + public let values: [String]? + public let maximumItems: Int? + public let itemMaximumBytes: Int? + public let sensitive: Bool + + public init( + _ name: String, + kind: ProtocolParameterKind, + required: Bool = false, + maximumBytes: Int? = nil, + minimum: Double? = nil, + maximum: Double? = nil, + values: [String]? = nil, + maximumItems: Int? = nil, + itemMaximumBytes: Int? = nil, + sensitive: Bool = false + ) { + self.name = name + self.kind = kind + self.required = required + self.maximumBytes = maximumBytes + self.minimum = minimum + self.maximum = maximum + self.values = values + self.maximumItems = maximumItems + self.itemMaximumBytes = itemMaximumBytes + self.sensitive = sensitive + } + + fileprivate func validate(_ value: JSONValue?) throws { + guard let value else { + if required { + let type = kind == .string ? "string " : "" + throw ProtocolValidationError.invalidParameter("Missing \(type)parameter: \(name)") + } + return + } + + switch (kind, value) { + case (.string, .string(let text)): + guard (!required || !text.isEmpty), + maximumBytes.map({ text.utf8.count <= $0 }) ?? true, + values.map({ $0.contains(text) }) ?? true else { + throw ProtocolValidationError.invalidParameter("Invalid string parameter: \(name)") + } + case (.number, .number(let number)), (.integer, .number(let number)): + guard number.isFinite, + minimum.map({ number >= $0 }) ?? true, + maximum.map({ number <= $0 }) ?? true, + kind != .integer || number.rounded() == number else { + let type = kind == .integer ? "integer" : "numeric" + throw ProtocolValidationError.invalidParameter("Invalid \(type) parameter: \(name)") + } + case (.boolean, .bool): + return + case (.stringArray, .array(let items)): + guard maximumItems.map({ items.count <= $0 }) ?? true else { + throw ProtocolValidationError.invalidParameter("Invalid string array parameter: \(name)") + } + let strings = items.compactMap(\.stringValue) + guard strings.count == items.count, + strings.allSatisfy({ value in + !value.isEmpty && (itemMaximumBytes.map { value.utf8.count <= $0 } ?? true) + }) else { + throw ProtocolValidationError.invalidParameter("Invalid string array parameter: \(name)") + } + default: + let type: String + switch kind { + case .string: type = "string" + case .number: type = "numeric" + case .integer: type = "integer" + case .boolean: type = "boolean" + case .stringArray: type = "string array" + } + throw ProtocolValidationError.invalidParameter("Invalid \(type) parameter: \(name)") + } + } + + fileprivate var document: JSONValue { + var result: [String: JSONValue] = [ + "name": .string(name), + "type": .string(kind.rawValue), + "required": .bool(required), + "sensitive": .bool(sensitive), + ] + if let maximumBytes { result["maximumBytes"] = .number(Double(maximumBytes)) } + if let minimum { result["minimum"] = .number(minimum) } + if let maximum { result["maximum"] = .number(maximum) } + if let values { result["values"] = .array(values.map(JSONValue.string)) } + if let maximumItems { result["maximumItems"] = .number(Double(maximumItems)) } + if let itemMaximumBytes { result["itemMaximumBytes"] = .number(Double(itemMaximumBytes)) } + return .object(result) + } +} + +public struct ProtocolCommandDefinition: Sendable { + public let command: CommandName + public let parameters: [ProtocolParameterDefinition] + public let capabilityNegotiated: Bool + public let resultContainsUntrustedContent: Bool + public let constraints: [String] + + public init( + _ command: CommandName, + parameters: [ProtocolParameterDefinition] = [], + capabilityNegotiated: Bool = false, + resultContainsUntrustedContent: Bool = false, + constraints: [String] = [] + ) { + self.command = command + self.parameters = parameters + self.capabilityNegotiated = capabilityNegotiated + self.resultContainsUntrustedContent = resultContainsUntrustedContent + self.constraints = constraints + } + + public func validate(_ values: [String: JSONValue]) throws { + let allowed = Set(parameters.map(\.name)) + if let unexpected = values.keys.sorted().first(where: { !allowed.contains($0) }) { + throw ProtocolValidationError.invalidParameter( + "Unexpected parameter for \(command.rawValue): \(unexpected)" + ) + } + for parameter in parameters { + try parameter.validate(values[parameter.name]) + } + } + + fileprivate var document: JSONValue { + .object([ + "name": .string(command.rawValue), + "parameters": .array(parameters.map(\.document)), + "capabilityNegotiated": .bool(capabilityNegotiated), + "result": .object([ + "schema": protocolResultDefinition(for: command).document, + "mayContainUntrustedContent": .bool(resultContainsUntrustedContent), + ]), + "constraints": .array(constraints.map(JSONValue.string)), + ]) + } +} + +private func string( + _ name: String, required: Bool = false, maximumBytes: Int = 8_192, + values: [String]? = nil, sensitive: Bool = false +) -> ProtocolParameterDefinition { + ProtocolParameterDefinition( + name, kind: .string, required: required, maximumBytes: maximumBytes, + values: values, sensitive: sensitive + ) +} + +private func number( + _ name: String, minimum: Double, maximum: Double +) -> ProtocolParameterDefinition { + ProtocolParameterDefinition(name, kind: .number, minimum: minimum, maximum: maximum) +} + +private func integer( + _ name: String, minimum: Double, maximum: Double +) -> ProtocolParameterDefinition { + ProtocolParameterDefinition(name, kind: .integer, minimum: minimum, maximum: maximum) +} + +private func boolean(_ name: String) -> ProtocolParameterDefinition { + ProtocolParameterDefinition(name, kind: .boolean) +} + +private func strings( + _ name: String, maximumItems: Int, itemMaximumBytes: Int +) -> ProtocolParameterDefinition { + ProtocolParameterDefinition( + name, kind: .stringArray, maximumItems: maximumItems, + itemMaximumBytes: itemMaximumBytes + ) +} + +private let targetParameters = [ + string("target", maximumBytes: 16), + string("role", maximumBytes: 128), + string("name", maximumBytes: 1_000), +] + +private func command( + _ name: CommandName, + _ parameters: [ProtocolParameterDefinition] = [], + capabilityNegotiated: Bool = false, + untrusted: Bool = false, + constraints: [String] = [] +) -> ProtocolCommandDefinition { + ProtocolCommandDefinition( + name, parameters: parameters, capabilityNegotiated: capabilityNegotiated, + resultContainsUntrustedContent: untrusted, constraints: constraints + ) +} + +public let protocolCommandDefinitions: [CommandName: ProtocolCommandDefinition] = { + let definitions: [ProtocolCommandDefinition] = [ + command(.ping), + command(.shutdown), + command(.profileClear), + command(.sessionCreate, [string("name", required: true, maximumBytes: 64), boolean("isolated")]), + command(.sessionList), + command(.sessionClose), + command(.visit, [string("url", required: true)], untrusted: true), + command(.inspect, [ + boolean("interactive"), boolean("text"), + string("context", maximumBytes: 16, values: ["summary", "outline", "text", "actions", "full"]), + string("task", maximumBytes: 512), string("within", maximumBytes: 16), + integer("limit", minimum: 1, maximum: 250), + integer("budget", minimum: 256, maximum: 16_000), + integer("depth", minimum: 0, maximum: 8), + ], untrusted: true), + command( + .click, targetParameters, untrusted: true, + constraints: ["exactly one target reference or semantic role/name target"] + ), + command( + .fill, + targetParameters + [ + string("value", required: true, maximumBytes: 900_000, sensitive: true), + ], + untrusted: true, + constraints: ["exactly one target reference or semantic role/name target"] + ), + command( + .upload, + targetParameters + [string("artifact", required: true, maximumBytes: 128)], + capabilityNegotiated: true, untrusted: true, + constraints: [ + "artifact must be an existing private-store basename", + "exactly one target reference or semantic role/name target", + ] + ), + command(.press, [string("key", required: true, maximumBytes: 32)], untrusted: true), + command(.scroll, [ + string("direction", values: ["up", "down", "top", "bottom"]), + number( + "amount", minimum: ProtocolBounds.scrollAmount.lowerBound, + maximum: ProtocolBounds.scrollAmount.upperBound + ), + ], untrusted: true), + command(.back, untrusted: true), + command(.reload, untrusted: true), + command(.wait, [ + boolean("settled"), string("url"), string("text", maximumBytes: 30_000), + number("timeoutMs", minimum: 100, maximum: 120_000), + ], untrusted: true), + command(.tour, [boolean("fullPage"), number("pace", minimum: 100, maximum: 5_000)], untrusted: true), + command(.captureInfo, untrusted: true), + command(.screenshot, targetParameters + [ + boolean("fullPage"), string("output", maximumBytes: 128), + string("series", maximumBytes: 32, values: ["viewport", "section"]), + string("outputPrefix", maximumBytes: 80), + string("format", maximumBytes: 16, values: ["png", "jpg", "jpeg", "pdf"]), + boolean("clipboard"), + ], capabilityNegotiated: true, constraints: [ + "target, full-page, and series modes are mutually constrained", + "PDF requires full-page mode and no clipboard", + ]), + command(.artifactList), + command(.recordStart, [ + string("output", maximumBytes: 128), number("fps", minimum: 1, maximum: 30), + string("format", maximumBytes: 16, values: RecordingFormat.allCases.map(\.rawValue)), + string("quality", maximumBytes: 16, values: RecordingQuality.allCases.map(\.rawValue)), + ]), + command(.recordStatus), + command(.recordStop, [string("output", maximumBytes: 128)]), + command(.qaReport, untrusted: true), + command(.qaClear), + command(.consoleList, [ + string("level", maximumBytes: 16, values: ["all", "log", "info", "debug", "warn", "error", "assert"]), + number("limit", minimum: 1, maximum: 200), + ], untrusted: true), + command(.networkList, [ + boolean("failed"), number("status", minimum: 100, maximum: 599), + number("limit", minimum: 1, maximum: 200), + ], untrusted: true), + command(.networkGet, [string("requestId", required: true, maximumBytes: 128)], untrusted: true), + command( + .stylesGet, + targetParameters + [strings("properties", maximumItems: 64, itemMaximumBytes: 128)], + untrusted: true, + constraints: ["exactly one target reference or semantic role/name target"] + ), + command( + .cookiesList, [boolean("includeValues")], untrusted: true, + constraints: ["values require the sensitive diagnostics environment gate"] + ), + command(.storageList, [ + string("scope", maximumBytes: 16, values: ["local", "session", "all"]), + boolean("includeValues"), + ], untrusted: true, constraints: ["values require the sensitive diagnostics environment gate"]), + command(.visualCompare, [ + string("before", required: true, maximumBytes: 128), + string("after", required: true, maximumBytes: 128), + string("output", maximumBytes: 128), + ]), + command(.performanceGet, untrusted: true), + command(.animationList, untrusted: true), + command(.reportCreate, [string("output", maximumBytes: 128)], untrusted: true), + command(.flowStart), + command(.flowStop, [string("output", maximumBytes: 128)]), + command(.flowRun, [string("input", required: true, maximumBytes: 128)], untrusted: true), + command(.networkEmulate, [ + boolean("offline"), + number( + "latencyMs", minimum: ProtocolBounds.networkLatencyMilliseconds.lowerBound, + maximum: ProtocolBounds.networkLatencyMilliseconds.upperBound + ), + number( + "downloadKbps", minimum: ProtocolBounds.networkThroughputKbps.lowerBound, + maximum: ProtocolBounds.networkThroughputKbps.upperBound + ), + number( + "uploadKbps", minimum: ProtocolBounds.networkThroughputKbps.lowerBound, + maximum: ProtocolBounds.networkThroughputKbps.upperBound + ), + ], capabilityNegotiated: true), + command(.networkMockSet, [ + string("url", required: true), number("status", minimum: 100, maximum: 599), + string("body", required: true, maximumBytes: 65_536), + string("contentType", maximumBytes: 256), + ], capabilityNegotiated: true, untrusted: true), + command(.networkMockClear, capabilityNegotiated: true), + command(.authLogin, [ + string("challenge", maximumBytes: 64), string("account", maximumBytes: 64), + boolean("interactive"), + ], capabilityNegotiated: true, untrusted: true, constraints: [ + "choose interactive login or account alias", + "saved login requires a valid single-use challenge", + ]), + ] + precondition(definitions.count == CommandName.allCases.count) + return Dictionary(uniqueKeysWithValues: definitions.map { ($0.command, $0) }) +}() + +public func protocolCommandDefinition(for command: CommandName) -> ProtocolCommandDefinition { + guard let definition = protocolCommandDefinitions[command] else { + preconditionFailure("Missing protocol schema for \(command.rawValue)") + } + return definition +} + +public let protocolErrorCodes: [String] = { + let hostCodes = HostErrorCode.allCases.map(\.rawValue) + let authenticationCodes = AuthenticationProtocolErrorCode.allCases.map(\.rawValue) + let boundaryCodes = [ + "ARTIFACT_ERROR", "AUTH_REQUIRED", "HOST_STOPPING", "HOST_UNAVAILABLE", + "INTERNAL_ERROR", "INVALID_CAPTURE_FORMAT", "INVALID_INPUT", "INVALID_REQUEST", + "INVALID_SESSION", "PEER_DENIED", "RECORDER_UNAVAILABLE", "RECORDING_ACTIVE", + "RECORDING_FAILED", "RECORDING_NOT_ACTIVE", "RESPONSE_TOO_LARGE", "SESSION_EXISTS", + "SESSION_NOT_FOUND", + ] + return Array(Set(hostCodes + authenticationCodes + boundaryCodes)).sorted() +}() + +public let protocolSchemaDocument: JSONValue = { + return .object([ + "format": .string("headless-sdk-contract"), + "jsonSchemaDialect": .string("https://json-schema.org/draft/2020-12/schema"), + "schemaVersion": .number(Double(headlessProtocolSchemaVersion)), + "protocolVersion": .string(headlessProtocolVersion), + "maximumMessageBytes": .number(Double(headlessMaximumMessageBytes)), + "transport": .object([ + "kind": .string("local-unix-socket"), + "framing": .string("one-newline-terminated-json-document-per-connection"), + "socketMode": .string("0600"), + "runtimeDirectoryMode": .string("0700"), + "peerAuthorization": .string("same-os-user"), + "tcp": .bool(false), + ]), + "request": .object([ + "type": .string("object"), + "additionalProperties": .bool(false), + "required": .array(["id", "version", "command", "parameters"].map(JSONValue.string)), + "id": .object(["type": .string("string"), "minimumBytes": .number(1), "maximumBytes": .number(128)]), + "version": .object(["type": .string("string"), "const": .string(headlessProtocolVersion)]), + "session": .object(["type": .array([.string("string"), .string("null")]), "maximumBytes": .number(64)]), + "parameters": .object(["type": .string("object"), "additionalProperties": .bool(false)]), + ]), + "response": .object([ + "type": .string("object"), + "additionalProperties": .bool(true), + "required": .array(["id", "version", "ok"].map(JSONValue.string)), + "success": .object(["ok": .bool(true), "result": .string("json"), "error": .null]), + "failure": .object([ + "ok": .bool(false), "result": .null, + "error": .object([ + "required": .array(["code", "message"].map(JSONValue.string)), + "optional": .array(["suggestion", "details"].map(JSONValue.string)), + "codes": .array(protocolErrorCodes.map(JSONValue.string)), + ]), + ]), + ]), + "errorDetails": .object([ + "AUTH_REQUIRED": .object([ + "mayContainUntrustedContent": .bool(true), + "schema": result("AuthenticationRequired", [ + resultField("challenge", .string), resultField("origin", .string), + resultField("detection", .string), resultField("accounts", .array), + resultField("expiresInSeconds", .number), + resultField("userPresenceRequired", .boolean), + resultField("credentialUseAvailable", .boolean), + resultField("vaultAvailable", .boolean), + resultField("vaultStatus", .string), + resultField("untrustedContent", .boolean), + resultField("originalActionReplayed", .boolean), + ]).document, + ]), + ]), + "commands": .array(CommandName.allCases.map { protocolCommandDefinition(for: $0).document }), + "localCommands": .array([ + "capabilities", "config.describe", "config.get", "config.list", "config.reset", + "config.set", "credentials.add", "credentials.list", "credentials.remove", + "credentials.rename", "help", "runtime", "schema", "start", "version", + ].map(JSONValue.string)), + "hostLifecycle": .object([ + "connect": .string("attach to an existing host and never assume ownership"), + "sharedStart": .string("headless start --background"), + "supervisedStart": .string("headless start --background --supervised"), + "supervisedOwnerChannel": .string("open standard-input pipe"), + "ownerExit": .string("host shuts down when the owner pipe closes"), + "existingHost": .string("supervised start fails rather than attaching"), + "shutdown": .string("only explicit user requests may stop a shared host"), + ]), + "compatibility": .object([ + "policy": .string("exact-wire-version"), + "olderClient": .string("may call only commands and parameters declared by its bundled schema"), + "olderHost": .string("missing commands or capabilities fail explicitly; clients must not emulate them"), + "mismatch": .string("host rejects request or client rejects response before decoding result"), + "productVersionIndependent": .bool(true), + ]), + "cancellation": .object([ + "beforeSend": .string("request is not written"), + "afterSend": .string("close the client connection; command outcome is unknown until state is inspected"), + "ownedHost": .string("terminate, await, and reap only a host process launched by that SDK instance"), + "sharedHost": .string("never stop the host implicitly"), + ]), + "timeouts": .object([ + "connect": .string("fails before a request is sent"), + "requestBeforeWrite": .string("safe to retry with a new request identifier"), + "requestAfterWrite": .string("outcome unknown; never retry automatically"), + "startup": .string("terminate and reap only the supervised launcher and host"), + ]), + "releasePolicy": .object([ + "schemaVersioning": .string("integer; generators reject unknown versions"), + "sdkVersioning": .string("semantic versions independent of product and wire versions"), + "preOneDeprecation": .string("at least one minor SDK release"), + "stableRemoval": .string("major SDK release and migration notes"), + "provenance": .string("publish from reviewed tags with package attestations"), + "securityReporting": .string("SECURITY.md; never include secrets or private artifacts"), + ]), + "security": .object([ + "unknownRequestFields": .string("rejected"), + "unknownResponseFields": .string("accepted for compatible additive evolution"), + "pageData": .string("untrusted"), + "credentialParameters": .string("aliases-and-challenge-identifiers-only"), + "sensitiveDiagnostics": .string("requires-command-flag-and-host-environment-gate"), + "arbitraryJavaScript": .bool(false), + "tcpListener": .bool(false), + ]), + ]) +}() diff --git a/apps/headless/Sources/HeadlessProtocol/SupervisedHost.swift b/apps/headless/Sources/HeadlessProtocol/SupervisedHost.swift new file mode 100644 index 0000000..753e3fd --- /dev/null +++ b/apps/headless/Sources/HeadlessProtocol/SupervisedHost.swift @@ -0,0 +1,51 @@ +import Foundation + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#endif + +public final class SupervisedHostOwnerMonitor: @unchecked Sendable { + private let source: DispatchSourceRead + private let lock = NSLock() + private var active = true + + public static func startIfRequested( + onOwnerExit: @escaping @Sendable () -> Void + ) -> SupervisedHostOwnerMonitor? { + guard ProcessInfo.processInfo.environment["HEADLESS_SUPERVISED"] == "1" else { + return nil + } + return SupervisedHostOwnerMonitor(onOwnerExit: onOwnerExit) + } + + private init(onOwnerExit: @escaping @Sendable () -> Void) { + source = DispatchSource.makeReadSource(fileDescriptor: STDIN_FILENO, queue: .global()) + source.setEventHandler { [weak self] in + guard let self else { return } + var byte: UInt8 = 0 + let count = withUnsafeMutableBytes(of: &byte) { buffer in + read(STDIN_FILENO, buffer.baseAddress, 1) + } + if count >= 0 || errno != EINTR { + self.stop() + onOwnerExit() + } + } + source.resume() + } + + public func stop() { + lock.lock() + guard active else { + lock.unlock() + return + } + active = false + source.cancel() + lock.unlock() + } + + deinit { stop() } +} diff --git a/apps/headless/Tests/HeadlessMCPTests/main.swift b/apps/headless/Tests/HeadlessMCPTests/main.swift index bf0a6b9..4c0ecc7 100644 --- a/apps/headless/Tests/HeadlessMCPTests/main.swift +++ b/apps/headless/Tests/HeadlessMCPTests/main.swift @@ -90,6 +90,17 @@ func run() throws { let serverInfo = try object(initialize["serverInfo"], "initialize server info was absent") try expect(serverInfo["name"] as? String == "headless", "initialize server name changed") try expect(serverInfo["version"] as? String == CommandLine.arguments[2], "MCP product version changed") + try expect( + serverInfo["headlessProtocolVersion"] as? String == headlessProtocolVersion, + "MCP wire version drifted from the SDK contract" + ) + let mcpSchemaVersion = try integer( + serverInfo["headlessSchemaVersion"], "MCP schema version was absent" + ) + try expect( + mcpSchemaVersion == headlessProtocolSchemaVersion, + "MCP schema version drifted from the SDK contract" + ) let list = try object(responses[1]["result"], "tools/list result was absent") guard let tools = list["tools"] as? [[String: Any]], tools.count == 1 else { diff --git a/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift b/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift index ac5ab57..913f32c 100644 --- a/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift +++ b/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift @@ -542,7 +542,8 @@ struct ProtocolTests { try expect( repeated.local == .start( presentation: nil, - allowlist: try NavigationAllowlist.parse(["localhost", "*.staging.example.com"]) + allowlist: try NavigationAllowlist.parse(["localhost", "*.staging.example.com"]), + supervised: false ), "repeated --allow flags should parse in order" ) @@ -551,7 +552,8 @@ struct ProtocolTests { try expect( commaSeparated.local == .start( presentation: nil, - allowlist: try NavigationAllowlist.parse(["localhost", "127.0.0.1"]) + allowlist: try NavigationAllowlist.parse(["localhost", "127.0.0.1"]), + supervised: false ), "comma-separated --allow values should parse" ) @@ -577,7 +579,8 @@ struct ProtocolTests { try expect( withBackground.local == .start( presentation: .background, - allowlist: try NavigationAllowlist.parse(["localhost"]) + allowlist: try NavigationAllowlist.parse(["localhost"]), + supervised: false ), "start --allow should compose with --background" ) @@ -587,7 +590,8 @@ struct ProtocolTests { try expect( withForeground.local == .start( presentation: .foreground, - allowlist: try NavigationAllowlist.parse(["127.0.0.1"]) + allowlist: try NavigationAllowlist.parse(["127.0.0.1"]), + supervised: false ), "start --allow should compose with --foreground" ) @@ -1271,17 +1275,28 @@ struct ProtocolTests { } let localCommands: [([String], LocalCommand)] = [ - (["start"], .start(presentation: nil, allowlist: .unrestricted)), - (["start", "--background"], .start(presentation: .background, allowlist: .unrestricted)), - (["start", "--foreground"], .start(presentation: .foreground, allowlist: .unrestricted)), + (["start"], .start( + presentation: nil, allowlist: .unrestricted, supervised: false + )), + (["start", "--background"], .start( + presentation: .background, allowlist: .unrestricted, supervised: false + )), + (["start", "--foreground"], .start( + presentation: .foreground, allowlist: .unrestricted, supervised: false + )), + (["start", "--supervised"], .start( + presentation: nil, allowlist: .unrestricted, supervised: true + )), (["start", "--allow", "localhost"], .start( - presentation: nil, allowlist: try NavigationAllowlist.parse(["localhost"]) + presentation: nil, allowlist: try NavigationAllowlist.parse(["localhost"]), + supervised: false )), ( ["start", "--allow", "localhost", "--allow", "127.0.0.1", "--background"], .start( presentation: .background, - allowlist: try NavigationAllowlist.parse(["localhost", "127.0.0.1"]) + allowlist: try NavigationAllowlist.parse(["localhost", "127.0.0.1"]), + supervised: false ) ), (["config", "get", "startup-presentation"], .config(.get("startup-presentation"))), @@ -2398,6 +2413,7 @@ struct ProtocolTests { try expect( localCommandNames.isSuperset(of: [ "config.describe", "config.get", "config.list", "config.reset", "config.set", + "schema", ]), "capabilities should advertise every local config command" ) @@ -2520,6 +2536,131 @@ struct ProtocolTests { try expect(security["arbitraryJavaScript"] == .bool(false), "capabilities must not advertise arbitrary JavaScript") } + static func sdkProtocolSchemaContract() throws { + try expect( + protocolCommandDefinitions.count == CommandName.allCases.count, + "the SDK schema must describe every wire command exactly once" + ) + try expect( + Set(protocolCommandDefinitions.keys) == Set(CommandName.allCases), + "the SDK schema command set must match CommandName" + ) + for command in CommandName.allCases { + let definition = protocolCommandDefinition(for: command) + let result = protocolResultDefinition(for: command) + try expect( + definition.command == command, + "the SDK schema must preserve the command identity" + ) + try expect( + Set(definition.parameters.map(\.name)).count == definition.parameters.count, + "the SDK schema must not contain duplicate parameter names" + ) + try expect( + !result.name.isEmpty && Set(result.fields.map(\.name)).count == result.fields.count, + "the SDK schema must define one coherent result shape per command" + ) + } + try expect( + protocolCommandDefinition(for: .visit).resultContainsUntrustedContent + && protocolCommandDefinition(for: .captureInfo).resultContainsUntrustedContent, + "page state and capture metadata must remain marked as untrusted" + ) + + let schemaInvocation = try CLIParser().parse(["schema"]) + try expect(schemaInvocation.local == .schema, "schema must remain a local CLI command") + try expect(schemaInvocation.request == nil, "schema must not enter the browser protocol") + try expectThrows("schema should reject unexpected arguments") { + _ = try CLIParser().parse(["schema", "extra"]) + } + + let fill = protocolCommandDefinition(for: .fill) + let sensitiveParameters = fill.parameters.filter(\.sensitive).map(\.name) + try expect(sensitiveParameters == ["value"], "fill value sensitivity must be machine-readable") + let authentication = protocolCommandDefinition(for: .authLogin) + try expect( + Set(authentication.parameters.map(\.name)) == ["challenge", "account", "interactive"], + "authentication schema must expose aliases and challenge identifiers only" + ) + try expect( + authentication.parameters.allSatisfy { !$0.sensitive }, + "authentication schema must never define a password parameter" + ) + let screenshotFormat = protocolCommandDefinition(for: .screenshot).parameters + .first { $0.name == "format" } + try expect( + screenshotFormat?.values == ["png", "jpg", "jpeg", "pdf"], + "the schema must expose every accepted screenshot spelling" + ) + let recordingQuality = protocolCommandDefinition(for: .recordStart).parameters + .first { $0.name == "quality" } + try expect( + recordingQuality?.values == RecordingQuality.allCases.map(\.rawValue), + "the schema must derive recording quality values from the parser enum" + ) + try expect( + protocolErrorCodes.contains(AuthenticationError.challengeExpired.code) + && protocolErrorCodes.contains("INVALID_INPUT") + && protocolErrorCodes.contains("RESPONSE_TOO_LARGE"), + "the schema must include authentication, validation, and transport failures" + ) + + let schemaData = try ProtocolCodec.encoder.encode(protocolSchemaDocument) + try expect( + schemaData.count < headlessMaximumMessageBytes, + "the protocol schema must fit the protocol frame bound" + ) + let source = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + .appendingPathComponent("sdk/protocol-schema.json") + let golden = try Data(contentsOf: source) + try expect( + try ProtocolCodec.decoder.decode(JSONValue.self, from: golden) == protocolSchemaDocument, + "the checked-in SDK schema must match the Swift-owned contract" + ) + } + + static func supervisedHostOwnerPipe() throws { + let process = Process() + process.executableURL = URL(fileURLWithPath: CommandLine.arguments[0]) + process.arguments = ["--supervised-owner-child"] + var environment = ProcessInfo.processInfo.environment + environment["HEADLESS_SUPERVISED"] = "1" + process.environment = environment + let input = Pipe() + let output = Pipe() + process.standardInput = input + process.standardOutput = output + process.standardError = FileHandle.nullDevice + let finished = DispatchSemaphore(value: 0) + process.terminationHandler = { _ in finished.signal() } + try process.run() + defer { + if process.isRunning { + process.terminate() + process.waitUntilExit() + } + } + Thread.sleep(forTimeInterval: 0.1) + try expect(process.isRunning, "a supervised host must stay alive while its owner pipe is open") + try input.fileHandleForWriting.close() + try expect( + finished.wait(timeout: .now() + 3) == .success, + "a supervised host must stop when its owner pipe closes" + ) + let marker = String( + decoding: output.fileHandleForReading.readDataToEndOfFile(), as: UTF8.self + ) + try expect( + process.terminationStatus == 0 && marker.contains("owner-closed"), + "the supervised owner monitor did not perform an orderly shutdown" + ) + } + static func oversizedSocketRequestIsRejected() throws { try LocalRuntime.preparePrivateDirectory() let socketPath = LocalRuntime.directoryURL @@ -3771,6 +3912,21 @@ struct ProtocolTests { } static func main() { + if CommandLine.arguments.count == 2, + CommandLine.arguments[1] == "--supervised-owner-child" { + let stopped = DispatchSemaphore(value: 0) + guard let monitor = SupervisedHostOwnerMonitor.startIfRequested(onOwnerExit: { + print("owner-closed") + fflush(stdout) + stopped.signal() + }) else { + fputs("supervision was not enabled\n", stderr) + exit(1) + } + stopped.wait() + monitor.stop() + exit(0) + } if CommandLine.arguments.count == 3, CommandLine.arguments[1] == "--peer-denied-client" { do { @@ -3828,6 +3984,8 @@ struct ProtocolTests { ("visual comparison", visualComparisonInvokesBoundedTool), ("recording arguments and bounds", recordingArgumentsAndFailureBounds), ("capabilities match commands", capabilitiesMatchProtocolCommands), + ("SDK protocol schema contract", sdkProtocolSchemaContract), + ("supervised host owner pipe", supervisedHostOwnerPipe), ("screenshot series helpers", screenshotSeriesHelpers), ("diagnostic summary", diagnosticSummary), ("diagnostic bounds and URL redaction", diagnosticsBoundAndRedacted), diff --git a/apps/headless/build.sh b/apps/headless/build.sh index f0e9afc..e1182ea 100755 --- a/apps/headless/build.sh +++ b/apps/headless/build.sh @@ -59,8 +59,10 @@ if [[ -z "${SDKROOT:-}" ]]; then for sdk in /Library/Developer/CommandLineTools/SDKs/MacOSX*.sdk(NOn); do if swiftc -module-cache-path build/module-cache -sdk "$sdk" \ -target "$NATIVE_ARCH-apple-macos13.0" -typecheck \ - Sources/HeadlessProtocol/Protocol.swift \ - Sources/HeadlessProtocol/CredentialCommands.swift \ + Sources/HeadlessProtocol/Protocol.swift \ + Sources/HeadlessProtocol/ProtocolSchema.swift \ + Sources/HeadlessProtocol/SupervisedHost.swift \ + Sources/HeadlessProtocol/CredentialCommands.swift \ Sources/HeadlessProtocol/HostError.swift \ Sources/HeadlessProtocol/CaptureFormats.swift \ Sources/HeadlessProtocol/NavigationAllowlist.swift >/dev/null 2>&1; then diff --git a/apps/headless/docs/COMMANDS.md b/apps/headless/docs/COMMANDS.md index e29c835..86532f7 100644 --- a/apps/headless/docs/COMMANDS.md +++ b/apps/headless/docs/COMMANDS.md @@ -21,12 +21,13 @@ headless -- --value # stop option parsing; literal values ```sh version | --version -start [--background|--foreground] [--allow PATTERN]... | status | stop | runtime +start [--background|--foreground] [--allow PATTERN]... [--supervised] | status | stop | runtime profile clear config list | config describe KEY | config get KEY config set KEY VALUE | config reset KEY session create [NAME] [--isolated] | session list | session close NAME capabilities +schema ``` - `start` launches the host if it is not already running. Repeatable @@ -37,6 +38,14 @@ capabilities `start --allow` with the same hosts in any order is a no-op. `stop` controls the host afterwards. `runtime` reports which engine is active and where it came from. +- `start --supervised` is for SDK-owned lifecycle management. It refuses to + attach to an existing host, keeps the launcher alive, and shuts the host down + when the launcher's standard-input owner pipe closes. Normal starts remain + shared and detached. +- `schema` prints the versioned SDK contract generated from the Swift request + definitions. It is local-only and includes request and response envelopes, + command parameters and bounds, errors, compatibility, cancellation, and + security metadata. The checked-in golden copy is `sdk/protocol-schema.json`. - `config list` discovers agent-visible settings. `config describe KEY` reports its type, default, platform scope, access class, effect timing, current value, and whether the current platform supports it. `config get`, `set`, and diff --git a/apps/headless/docs/P4.md b/apps/headless/docs/P4.md index ac3ae06..3967d58 100644 --- a/apps/headless/docs/P4.md +++ b/apps/headless/docs/P4.md @@ -27,3 +27,23 @@ synthetic DOM implementation. This fidelity boundary is machine-readable: The same verbs and response shapes remain portable. Hover and drag are not implied by this upgrade and remain future command-design work. + +## SDK contract + +`headless schema` emits the versioned, machine-readable SDK contract without +starting or contacting a browser host. Parameter names, primitive types, +required fields, byte and numeric bounds, enum values, and array limits come +from Swift definitions that also run before the command-specific protocol +validators. `sdk/protocol-schema.json` is a checked-in golden artifact; the +protocol suite fails if it drifts from the executable output. + +The contract retains the local Unix socket and exact wire-version policy. It +does not add TCP, remote control, arbitrary JavaScript, raw credential fields, +or a second validator. Capability-gated commands must fail explicitly when an +engine does not support them. Page-derived results remain untrusted. + +Closing a client connection after a request was sent does not undo the browser +operation. SDKs report that outcome as unknown and require a fresh inspection. +`headless start --supervised` provides the owned-host path: it refuses to attach +to an existing host, inherits a live standard-input owner pipe, and shuts down +when that pipe closes. An SDK must never stop a shared host implicitly. diff --git a/apps/headless/main.swift b/apps/headless/main.swift index 54b39f7..6671431 100644 --- a/apps/headless/main.swift +++ b/apps/headless/main.swift @@ -952,6 +952,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { var controllers: [BrowserWindowController] = [] private let agentServer = LocalSocketServer() private var hostCore: HostCore? + private var ownerMonitor: SupervisedHostOwnerMonitor? func applicationDidFinishLaunching(_ notification: Notification) { let artifacts: ArtifactStore @@ -1015,6 +1016,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate { shutdownHandler: { DispatchQueue.main.async { NSApp.terminate(nil) } } ) hostCore = core + ownerMonitor = SupervisedHostOwnerMonitor.startIfRequested { + DispatchQueue.main.async { NSApp.terminate(nil) } + } do { try agentServer.start { [weak self] request in self?.hostCore?.handle(request) ?? CommandResponse.failure( @@ -1075,6 +1079,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { true } func applicationWillTerminate(_ notification: Notification) { + ownerMonitor?.stop() hostCore?.stop() agentServer.stop() } diff --git a/apps/headless/test.sh b/apps/headless/test.sh index 3d58157..c870775 100755 --- a/apps/headless/test.sh +++ b/apps/headless/test.sh @@ -25,6 +25,8 @@ if [[ "$(uname -s)" == "Darwin" ]]; then if swiftc -module-cache-path build/module-cache -sdk "$sdk" \ -target "$(uname -m)-apple-macos13.0" -typecheck \ Sources/HeadlessProtocol/Protocol.swift \ + Sources/HeadlessProtocol/ProtocolSchema.swift \ + Sources/HeadlessProtocol/SupervisedHost.swift \ Sources/HeadlessProtocol/CredentialCommands.swift \ Sources/HeadlessProtocol/HostError.swift \ Sources/HeadlessProtocol/CaptureFormats.swift \ @@ -79,6 +81,11 @@ fi echo "headless tests: CLI product version does not match $EXPECTED_VERSION" >&2 exit 1 } +"$BIN_PATH/headless" schema > "$TEST_SCRATCH/protocol-schema.json" +cmp "$TEST_SCRATCH/protocol-schema.json" ../../sdk/protocol-schema.json || { + echo "headless tests: sdk/protocol-schema.json is stale; regenerate it with headless schema" >&2 + exit 1 +} PATH_INVOCATION_ROOT="$TEST_SCRATCH/path-invocation" mkdir -p "$PATH_INVOCATION_ROOT" ln -s "$BIN_PATH/headless" "$PATH_INVOCATION_ROOT/headless" diff --git a/docs/roadmap/architecture-decisions.md b/docs/roadmap/architecture-decisions.md index 86cd017..3291cce 100644 --- a/docs/roadmap/architecture-decisions.md +++ b/docs/roadmap/architecture-decisions.md @@ -812,6 +812,49 @@ rule that durable saved-credential retrieval needs trusted per-use presence. --- +## 28. SDKs derive from one Swift-owned protocol contract + +**Decision:** SDK generation starts from a deterministic machine-readable +contract emitted by `headless schema`. The Swift descriptors define every wire +command's parameter names, primitive types, required fields, byte and numeric +bounds, enum values, and array limits, and those descriptors execute before +command-specific semantic validation. `sdk/protocol-schema.json` is a golden +artifact checked against the executable in tests, not a second hand-maintained +validator. + +SDKs use only the existing newline-framed JSON protocol over the private +same-user Unix socket. They do not add TCP, remote control, arbitrary +JavaScript, a Chromium debug port, unrestricted profile paths, or direct +credential values. Authentication APIs carry aliases and challenge IDs only. +Page-derived results stay identified as untrusted, sensitive diagnostics keep +both gates, and unsupported engine behavior is capability-negotiated and fails +explicitly. + +Wire compatibility is exact by `headlessProtocolVersion` and independent of +the product and SDK package versions. Compatible additions may retain the wire +version; removals, changed meanings, weaker validation, or incompatible +envelopes require a new wire version. The schema format has a separate integer +version. Clients reject unknown schema versions and mismatched response IDs or +wire versions before decoding command results. + +Cancellation before send writes no request. Cancellation or timeout after send +closes the client connection but cannot claim the browser operation was rolled +back; clients report an unknown outcome and refresh state. SDK-owned hosts use +`headless start --supervised`, which refuses to attach to an existing host and +keeps a standard-input owner pipe open. Closing that pipe stops the owned host +and lets the launcher reap it. SDKs never stop a shared host they discovered. + +**Status:** accepted for [#163](https://github.com/LockInTime/headless/issues/163). + +**Consequences:** TypeScript and Python clients generate or validate their +public command types from the golden schema and pin its digest. Schema, SDK, +CLI-parser, protocol-validator, and MCP parity are tested together. SDK package +versions may advance independently while declaring the supported wire version. +Deprecation, support-window, provenance, and security-reporting rules live with +the schema so client packages cannot silently invent a different policy. + +--- + ## Decision log | # | Decision | Status | Date | @@ -836,5 +879,6 @@ rule that durable saved-credential retrieval needs trusted per-use presence. | 25 | Typed local settings registry; security policy stays fixed | Implemented | 2026-09-12 | | 26 | Isolated sessions own one ephemeral browser context | Implemented | 2026-09-12 | | 27 | Interactive authentication keeps consent in trusted host | Implemented | 2026-09-12 | +| 28 | SDKs derive from one Swift-owned protocol contract | Decided | 2026-09-12 | New decisions append here with the same format. diff --git a/sdk/README.md b/sdk/README.md new file mode 100644 index 0000000..1f7cc20 --- /dev/null +++ b/sdk/README.md @@ -0,0 +1,51 @@ +# Headless SDK contract + +`protocol-schema.json` is the checked-in SDK contract emitted by the Swift +implementation: + +```sh +swift run --package-path apps/headless headless schema > sdk/protocol-schema.json +``` + +The protocol suite compares this file byte-for-byte with `headless schema` and +also decodes it back to the Swift value. Do not edit the JSON by hand. + +## Compatibility + +The wire protocol and product versions are independent. SDKs support the exact +wire version in their bundled schema. An older client sends only commands and +parameters present in that schema. A newer client must reject an older host +before decoding a result for an unsupported wire version, and must use the +host capability document before issuing an engine-specific command. + +Compatible additions retain the current wire version. Removing or changing a +field, command, constraint, error meaning, framing rule, or security guarantee +requires a protocol-version change and migration notes. The schema format has +its own integer version so generators can reject unknown metadata. + +## Transport and cancellation + +SDKs use the existing private, same-user Unix socket. They do not add TCP, +remote control, a Chromium debug port, or arbitrary JavaScript. Closing a +client connection before a request is written cancels it. Closing after send +does not roll back a browser operation, so the SDK must report an unknown +outcome and require a fresh inspection. + +An SDK may terminate and reap only a host process that it launched and owns. +It must not stop an already-running shared host during cancellation, timeout, +or client shutdown. + +Use `headless start --background --supervised` for an owned host. The command +fails if a shared host already exists, prints the normal startup response, and +then remains attached to the host. The SDK keeps the child standard-input pipe +open for the ownership lifetime. Closing the pipe makes the host stop and lets +the launcher be awaited. A plain `headless start` remains detached and shared. + +## Release policy + +Generated SDKs pin a schema digest and wire version. Schema and client changes +are reviewed together. SDK packages are versioned independently from the +Headless product while declaring their supported wire versions. Before 1.0, +deprecations remain for at least one minor SDK release; after 1.0, removals +require a major SDK release. Security reports use the repository process in +`SECURITY.md` and must not include credentials, cookies, or private artifacts. diff --git a/sdk/protocol-schema.json b/sdk/protocol-schema.json new file mode 100644 index 0000000..6cacdb9 --- /dev/null +++ b/sdk/protocol-schema.json @@ -0,0 +1 @@ +{"cancellation":{"afterSend":"close the client connection; command outcome is unknown until state is inspected","beforeSend":"request is not written","ownedHost":"terminate, await, and reap only a host process launched by that SDK instance","sharedHost":"never stop the host implicitly"},"commands":[{"capabilityNegotiated":false,"constraints":[],"name":"ping","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"shutdown","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"stopping","required":true,"type":"boolean"}],"name":"Shutdown","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"profile.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"boolean"},{"name":"session","required":true,"type":"string"}],"name":"ProfileClear","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.create","parameters":[{"maximumBytes":64,"name":"name","required":true,"sensitive":false,"type":"string"},{"name":"isolated","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"session","required":true,"type":"string"},{"name":"isolated","required":true,"type":"boolean"}],"name":"SessionCreate","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"sessions","required":true,"type":"array"},{"name":"details","required":true,"type":"array"}],"name":"SessionList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.close","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"closed","required":true,"type":"string"}],"name":"SessionClose","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"visit","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"inspect","parameters":[{"name":"interactive","required":false,"sensitive":false,"type":"boolean"},{"name":"text","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":16,"name":"context","required":false,"sensitive":false,"type":"string","values":["summary","outline","text","actions","full"]},{"maximumBytes":512,"name":"task","required":false,"sensitive":false,"type":"string"},{"maximumBytes":16,"name":"within","required":false,"sensitive":false,"type":"string"},{"maximum":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximum":16000,"minimum":256,"name":"budget","required":false,"sensitive":false,"type":"integer"},{"maximum":8,"minimum":0,"name":"depth","required":false,"sensitive":false,"type":"integer"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"contextMode","required":true,"type":"string"},{"name":"viewport","required":true,"type":"object"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"elements","required":false,"type":"array"},{"name":"regions","required":false,"type":"array"},{"name":"snippets","required":false,"type":"array"},{"name":"text","required":false,"type":"string"}],"name":"Inspection","type":"object"}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"click","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"clicked","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"}],"name":"Click","type":"object"}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"fill","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":900000,"name":"value","required":true,"sensitive":true,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"filled","required":true,"type":"string"},{"name":"valueLength","required":true,"type":"number"}],"name":"Fill","type":"object"}}},{"capabilityNegotiated":true,"constraints":["artifact must be an existing private-store basename","exactly one target reference or semantic role/name target"],"name":"upload","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"artifact","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"uploaded","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"artifact","required":true,"type":"string"}],"name":"Upload","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"press","parameters":[{"maximumBytes":32,"name":"key","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"pressed","required":true,"type":"string"}],"name":"Press","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"scroll","parameters":[{"maximumBytes":8192,"name":"direction","required":false,"sensitive":false,"type":"string","values":["up","down","top","bottom"]},{"maximum":100000,"minimum":0.1,"name":"amount","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"direction","required":true,"type":"string"},{"name":"amount","required":true,"type":"number"}],"name":"Scroll","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"back","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"reload","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"wait","parameters":[{"name":"settled","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":8192,"name":"url","required":false,"sensitive":false,"type":"string"},{"maximumBytes":30000,"name":"text","required":false,"sensitive":false,"type":"string"},{"maximum":120000,"minimum":100,"name":"timeoutMs","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"tour","parameters":[{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximum":5000,"minimum":100,"name":"pace","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"start","required":true,"type":"number"},{"name":"end","required":true,"type":"number"},{"name":"durationMs","required":true,"type":"number"}],"name":"Tour","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"capture.info","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"engine","required":true,"type":"string"},{"name":"page","required":true,"type":"object"},{"name":"trace","required":true,"type":"array"},{"name":"recording","required":true,"type":"object"}],"name":"CaptureInfo","type":"object"}}},{"capabilityNegotiated":true,"constraints":["target, full-page, and series modes are mutually constrained","PDF requires full-page mode and no clipboard"],"name":"screenshot","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximumBytes":32,"name":"series","required":false,"sensitive":false,"type":"string","values":["viewport","section"]},{"maximumBytes":80,"name":"outputPrefix","required":false,"sensitive":false,"type":"string"},{"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["png","jpg","jpeg","pdf"]},{"name":"clipboard","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":false,"type":"string"},{"name":"path","required":false,"type":"string"},{"name":"kind","required":false,"type":"string"},{"name":"bytes","required":false,"type":"number"},{"name":"createdAt","required":false,"type":"number"},{"name":"artifacts","required":false,"type":"array"},{"name":"truncated","required":false,"type":"boolean"}],"name":"Screenshot","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"artifact.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"directory","required":true,"type":"string"},{"name":"artifacts","required":true,"type":"array"},{"name":"total","required":true,"type":"number"},{"name":"omitted","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"ArtifactList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.start","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximum":30,"minimum":1,"name":"fps","required":false,"sensitive":false,"type":"number"},{"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["mp4","mov","webm","gif"]},{"maximumBytes":16,"name":"quality","required":false,"sensitive":false,"type":"string","values":["fast","balanced","high"]}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.status","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.report","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"summary","required":false,"type":"object"}],"name":"QAReport","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"QAClear","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"console.list","parameters":[{"maximumBytes":16,"name":"level","required":false,"sensitive":false,"type":"string","values":["all","log","info","debug","warn","error","assert"]},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"messages","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"ConsoleList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.list","parameters":[{"name":"failed","required":false,"sensitive":false,"type":"boolean"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"requests","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"NetworkList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.get","parameters":[{"maximumBytes":128,"name":"requestId","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"found","required":true,"type":"boolean"},{"name":"requestId","required":false,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"request","required":false,"type":"object"}],"name":"NetworkDetail","type":"object"}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"styles.get","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"itemMaximumBytes":128,"maximumItems":64,"name":"properties","required":false,"sensitive":false,"type":"string-array"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"ref","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"box","required":true,"type":"object"},{"name":"styles","required":true,"type":"object"}],"name":"Styles","type":"object"}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"cookies.list","parameters":[{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"cookies","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"CookieList","type":"object"}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"storage.list","parameters":[{"maximumBytes":16,"name":"scope","required":false,"sensitive":false,"type":"string","values":["local","session","all"]},{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"stores","required":true,"type":"array"}],"name":"StorageList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"visual.compare","parameters":[{"maximumBytes":128,"name":"before","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"after","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"changedPixels","required":false,"type":"number"},{"name":"differenceRatio","required":false,"type":"number"}],"name":"VisualComparison","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"performance.get","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"timing","required":true,"type":"json"},{"name":"webVitals","required":true,"type":"object"},{"name":"resources","required":true,"type":"object"}],"name":"Performance","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"animation.list","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"count","required":true,"type":"number"},{"name":"animations","required":true,"type":"array"},{"name":"truncated","required":true,"type":"boolean"}],"name":"AnimationList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"report.create","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.start","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"recording","required":true,"type":"boolean"},{"name":"note","required":true,"type":"string"}],"name":"FlowStart","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.run","parameters":[{"maximumBytes":128,"name":"input","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"completed","required":true,"type":"number"},{"name":"input","required":true,"type":"string"}],"name":"FlowRun","type":"object"}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.emulate","parameters":[{"name":"offline","required":false,"sensitive":false,"type":"boolean"},{"maximum":120000,"minimum":0,"name":"latencyMs","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"downloadKbps","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"uploadKbps","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"offline","required":true,"type":"boolean"},{"name":"latencyMs","required":true,"type":"number"},{"name":"downloadKbps","required":true,"type":"number"},{"name":"uploadKbps","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"}],"name":"NetworkEmulation","type":"object"}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.set","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximumBytes":65536,"name":"body","required":true,"sensitive":false,"type":"string"},{"maximumBytes":256,"name":"contentType","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"status","required":true,"type":"number"},{"name":"activeMocks","required":true,"type":"number"}],"name":"NetworkMock","type":"object"}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"NetworkMockClear","type":"object"}}},{"capabilityNegotiated":true,"constraints":["choose interactive login or account alias","saved login requires a valid single-use challenge"],"name":"auth.login","parameters":[{"maximumBytes":64,"name":"challenge","required":false,"sensitive":false,"type":"string"},{"maximumBytes":64,"name":"account","required":false,"sensitive":false,"type":"string"},{"name":"interactive","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"account","required":true,"type":"string-or-null"},{"name":"saved","required":true,"type":"boolean"},{"name":"continuation","required":true,"type":"string"}],"name":"AuthenticationLogin","type":"object"}}}],"compatibility":{"mismatch":"host rejects request or client rejects response before decoding result","olderClient":"may call only commands and parameters declared by its bundled schema","olderHost":"missing commands or capabilities fail explicitly; clients must not emulate them","policy":"exact-wire-version","productVersionIndependent":true},"errorDetails":{"AUTH_REQUIRED":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"challenge","required":true,"type":"string"},{"name":"origin","required":true,"type":"string"},{"name":"detection","required":true,"type":"string"},{"name":"accounts","required":true,"type":"array"},{"name":"expiresInSeconds","required":true,"type":"number"},{"name":"userPresenceRequired","required":true,"type":"boolean"},{"name":"credentialUseAvailable","required":true,"type":"boolean"},{"name":"vaultAvailable","required":true,"type":"boolean"},{"name":"vaultStatus","required":true,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationRequired","type":"object"}}},"format":"headless-sdk-contract","hostLifecycle":{"connect":"attach to an existing host and never assume ownership","existingHost":"supervised start fails rather than attaching","ownerExit":"host shuts down when the owner pipe closes","sharedStart":"headless start --background","shutdown":"only explicit user requests may stop a shared host","supervisedOwnerChannel":"open standard-input pipe","supervisedStart":"headless start --background --supervised"},"jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","localCommands":["capabilities","config.describe","config.get","config.list","config.reset","config.set","credentials.add","credentials.list","credentials.remove","credentials.rename","help","runtime","schema","start","version"],"maximumMessageBytes":1048576,"protocolVersion":"0.5","releasePolicy":{"preOneDeprecation":"at least one minor SDK release","provenance":"publish from reviewed tags with package attestations","schemaVersioning":"integer; generators reject unknown versions","sdkVersioning":"semantic versions independent of product and wire versions","securityReporting":"SECURITY.md; never include secrets or private artifacts","stableRemoval":"major SDK release and migration notes"},"request":{"additionalProperties":false,"id":{"maximumBytes":128,"minimumBytes":1,"type":"string"},"parameters":{"additionalProperties":false,"type":"object"},"required":["id","version","command","parameters"],"session":{"maximumBytes":64,"type":["string","null"]},"type":"object","version":{"const":"0.5","type":"string"}},"response":{"additionalProperties":true,"failure":{"error":{"codes":["ARTIFACT_ERROR","AUTH_ACCOUNT_NOT_FOUND","AUTH_CHALLENGE_CONSUMED","AUTH_CHALLENGE_EXPIRED","AUTH_CHALLENGE_NOT_FOUND","AUTH_FORM_CHANGED","AUTH_ORIGIN_CHANGED","AUTH_REQUIRED","CREDENTIAL_ALIAS_EXISTS","ELEMENT_NOT_FOUND","FLOW_FAILED","HOST_STOPPING","HOST_UNAVAILABLE","INTERNAL_ERROR","INVALID_CAPTURE_FORMAT","INVALID_COMMAND","INVALID_FLOW","INVALID_INPUT","INVALID_REQUEST","INVALID_SESSION","MISSING_PARAMETER","OPERATION_FAILED","PEER_DENIED","RECORDER_UNAVAILABLE","RECORDING_ACTIVE","RECORDING_FAILED","RECORDING_NOT_ACTIVE","REGION_NOT_FOUND","RESPONSE_TOO_LARGE","SENSITIVE_DIAGNOSTICS_DISABLED","SESSION_EXISTS","SESSION_NOT_FOUND","TIMEOUT","UNSAFE_NAVIGATION","UNSAFE_RESOURCE_TYPE","UNSUPPORTED_CAPABILITY","USER_PRESENCE_DENIED","USER_PRESENCE_UNAVAILABLE","VAULT_LOCKED","VAULT_OPERATION_FAILED","VAULT_RESPONSE_INVALID","VAULT_UNAVAILABLE"],"optional":["suggestion","details"],"required":["code","message"]},"ok":false,"result":null},"required":["id","version","ok"],"success":{"error":null,"ok":true,"result":"json"},"type":"object"},"schemaVersion":1,"security":{"arbitraryJavaScript":false,"credentialParameters":"aliases-and-challenge-identifiers-only","pageData":"untrusted","sensitiveDiagnostics":"requires-command-flag-and-host-environment-gate","tcpListener":false,"unknownRequestFields":"rejected","unknownResponseFields":"accepted for compatible additive evolution"},"timeouts":{"connect":"fails before a request is sent","requestAfterWrite":"outcome unknown; never retry automatically","requestBeforeWrite":"safe to retry with a new request identifier","startup":"terminate and reap only the supervised launcher and host"},"transport":{"framing":"one-newline-terminated-json-document-per-connection","kind":"local-unix-socket","peerAuthorization":"same-os-user","runtimeDirectoryMode":"0700","socketMode":"0600","tcp":false}} From d18b673196675145ac42778f42295cf85d27186a Mon Sep 17 00:00:00 2001 From: SarthakWade Date: Sat, 12 Sep 2026 23:36:58 +0530 Subject: [PATCH 2/5] fix(sdk): harden supervised protocol contract --- apps/headless/Sources/HeadlessCLI/main.swift | 76 +++++++++-- .../HeadlessProtocol/ProtocolSchema.swift | 123 +++++++++++++++++- .../Tests/HeadlessMCPTests/main.swift | 29 ++++- .../HeadlessProtocolTests/ProtocolTests.swift | 106 +++++++++++++-- apps/headless/Tests/linux-e2e.sh | 41 ++++++ apps/headless/Tests/macos-e2e.sh | 43 ++++++ apps/headless/docs/COMMANDS.md | 6 +- apps/headless/docs/P4.md | 6 +- apps/headless/test.sh | 5 +- docs/roadmap/architecture-decisions.md | 9 +- sdk/README.md | 22 +++- sdk/protocol-fixtures.json | 103 +++++++++++++++ sdk/protocol-schema.json | 2 +- 13 files changed, 524 insertions(+), 47 deletions(-) create mode 100644 sdk/protocol-fixtures.json diff --git a/apps/headless/Sources/HeadlessCLI/main.swift b/apps/headless/Sources/HeadlessCLI/main.swift index 506e084..fff387b 100644 --- a/apps/headless/Sources/HeadlessCLI/main.swift +++ b/apps/headless/Sources/HeadlessCLI/main.swift @@ -25,6 +25,7 @@ private struct HostLauncher { struct Launch { let response: CommandResponse let process: Process? + let ownerHandle: FileHandle? } let client = LocalSocketClient() @@ -44,7 +45,7 @@ private struct HostLauncher { if let response = ping(), response.ok { if supervised { throw HostLaunchError.alreadyRunning } try validateRunningAllowlist(response, requested: allowlist) - return Launch(response: response, process: nil) + return Launch(response: response, process: nil, ownerHandle: nil) } #if os(Linux) // Report an unsupported browser directly to the operator instead of @@ -74,7 +75,8 @@ private struct HostLauncher { environment.removeValue(forKey: headlessNavigationAllowlistEnvironmentKey) } process.environment = environment - process.standardInput = supervised ? FileHandle.standardInput : FileHandle.nullDevice + let ownerPipe = supervised ? Pipe() : nil + process.standardInput = ownerPipe?.fileHandleForReading ?? FileHandle.nullDevice if let hostLog = environment["HEADLESS_HOST_LOG"], hostLog.hasPrefix("/") { let logURL = URL(fileURLWithPath: hostLog) FileManager.default.createFile(atPath: logURL.path, contents: nil) @@ -85,29 +87,76 @@ private struct HostLauncher { process.standardOutput = FileHandle.nullDevice process.standardError = FileHandle.nullDevice } - try process.run() + do { + try process.run() + try ownerPipe?.fileHandleForReading.close() + } catch { + try? ownerPipe?.fileHandleForReading.close() + try? ownerPipe?.fileHandleForWriting.close() + throw error + } let deadline = Date().addingTimeInterval(8) repeat { if let response = ping(), response.ok { do { try validateRunningAllowlist(response, requested: allowlist) - return Launch(response: response, process: supervised ? process : nil) + if supervised, runningHostProcessIdentifier(response) != process.processIdentifier { + throw HostLaunchError.ownershipMismatch + } + return Launch( + response: response, process: supervised ? process : nil, + ownerHandle: ownerPipe?.fileHandleForWriting + ) } catch { + try? ownerPipe?.fileHandleForWriting.close() terminateAndReap(process) throw error } } if !process.isRunning { + try? ownerPipe?.fileHandleForWriting.close() process.waitUntilExit() throw HostLaunchError.exited(process.terminationStatus) } Thread.sleep(forTimeInterval: 0.05) } while Date() < deadline + try? ownerPipe?.fileHandleForWriting.close() terminateAndReap(process) throw HostLaunchError.timedOut } + func waitForSupervisedHost(_ launch: Launch) -> Int32 { + guard let process = launch.process, let ownerHandle = launch.ownerHandle else { + return 0 + } + while process.isRunning { + var descriptor = pollfd( + fd: STDIN_FILENO, + events: Int16(POLLIN | POLLHUP | POLLERR), + revents: 0 + ) + let status = poll(&descriptor, 1, 100) + if status > 0 { + var byte: UInt8 = 0 + let count = withUnsafeMutableBytes(of: &byte) { buffer in + read(STDIN_FILENO, buffer.baseAddress, 1) + } + if count >= 0 || errno != EINTR { break } + } else if status < 0, errno != EINTR { + break + } + } + try? ownerHandle.close() + let gracefulDeadline = Date().addingTimeInterval(3) + while process.isRunning, Date() < gracefulDeadline { + Thread.sleep(forTimeInterval: 0.05) + } + if process.isRunning { terminateAndReap(process) } + else { process.waitUntilExit() } + return process.terminationStatus + } + private func terminateAndReap(_ process: Process) { guard process.isRunning else { process.waitUntilExit() @@ -142,6 +191,17 @@ private struct HostLauncher { return values.compactMap(\.stringValue) } + private func runningHostProcessIdentifier(_ response: CommandResponse) -> Int32? { + guard case .object(let result) = response.result, + let value = result["pid"]?.numberValue, + value.rounded() == value, + value >= 1, + value <= Double(Int32.max) else { + return nil + } + return Int32(value) + } + private func resolveHostExecutable() throws -> URL { let fileManager = FileManager.default var candidates: [URL] = [] @@ -171,6 +231,7 @@ private struct HostLauncher { private enum HostLaunchError: Error, CustomStringConvertible { case notFound case alreadyRunning + case ownershipMismatch case timedOut case exited(Int32) case allowlistMismatch(running: [String], requested: [String]) @@ -180,6 +241,8 @@ private enum HostLaunchError: Error, CustomStringConvertible { case .notFound: return "Could not find headless-host. Run the Headless build first." case .alreadyRunning: return "A shared Headless host is already running. Stop it before starting a supervised host." + case .ownershipMismatch: + return "A different Headless host answered during supervised startup." case .timedOut: return "Headless host did not become ready within 8 seconds." case .exited(let status): return "Headless host exited during startup (status \(status))." case .allowlistMismatch(let running, let requested): @@ -284,10 +347,7 @@ do { presentation: presentation, allowlist: allowlist, supervised: supervised ) try printResponse(launch.response) - if let process = launch.process { - process.waitUntilExit() - exit(process.terminationStatus) - } + if launch.process != nil { exit(HostLauncher().waitForSupervisedHost(launch)) } case .config(let command): let settings = try SettingsStore.production() switch command { diff --git a/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift b/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift index 4667efb..8cff324 100644 --- a/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift +++ b/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift @@ -62,6 +62,41 @@ public struct ProtocolResultDefinition: Sendable { "fields": .array(fields.map(\.document)), ]) } + + public func validate(_ value: JSONValue) throws { + guard case .object(let object) = value else { + throw ProtocolValidationError.invalidParameter("Invalid \(name) result: expected object") + } + for field in fields { + guard let value = object[field.name] else { + if field.required { + throw ProtocolValidationError.invalidParameter( + "Invalid \(name) result: missing \(field.name)" + ) + } + continue + } + guard field.accepts(value) else { + throw ProtocolValidationError.invalidParameter( + "Invalid \(name) result: \(field.name) must be \(field.kind.rawValue)" + ) + } + } + } +} + +private extension ProtocolResultField { + func accepts(_ value: JSONValue) -> Bool { + switch (kind, value) { + case (.string, .string), (.number, .number), (.boolean, .bool), + (.object, .object), (.array, .array), (.json, _): + return true + case (.stringOrNull, .string), (.stringOrNull, .null): + return true + default: + return false + } + } } private func resultField( @@ -158,7 +193,12 @@ public func protocolResultDefinition(for command: CommandName) -> ProtocolResult resultField("quality", .string, required: false), resultField("name", .string, required: false), ]) - case .qaReport: return result("QAReport", [resultField("summary", .object, required: false)]) + case .qaReport: + return result("QAReport", [ + resultField("untrustedContent", .boolean), resultField("summary", .object), + resultField("issues", .array), resultField("events", .array), + resultField("omitted", .object), resultField("truncated", .boolean), + ]) case .qaClear: return result("QAClear", [resultField("cleared", .number)]) case .consoleList: return result("ConsoleList", [ @@ -231,6 +271,8 @@ public func protocolResultDefinition(for command: CommandName) -> ProtocolResult return result("AuthenticationLogin", [ resultField("origin", .string), resultField("account", .stringOrNull), resultField("saved", .boolean), resultField("continuation", .string), + resultField("passwordExposed", .boolean), + resultField("originalActionReplayed", .boolean), ]) } } @@ -243,6 +285,7 @@ public struct ProtocolParameterDefinition: Sendable { public let minimum: Double? public let maximum: Double? public let values: [String]? + public let caseInsensitiveValues: Bool public let maximumItems: Int? public let itemMaximumBytes: Int? public let sensitive: Bool @@ -255,6 +298,7 @@ public struct ProtocolParameterDefinition: Sendable { minimum: Double? = nil, maximum: Double? = nil, values: [String]? = nil, + caseInsensitiveValues: Bool = false, maximumItems: Int? = nil, itemMaximumBytes: Int? = nil, sensitive: Bool = false @@ -266,6 +310,7 @@ public struct ProtocolParameterDefinition: Sendable { self.minimum = minimum self.maximum = maximum self.values = values + self.caseInsensitiveValues = caseInsensitiveValues self.maximumItems = maximumItems self.itemMaximumBytes = itemMaximumBytes self.sensitive = sensitive @@ -284,7 +329,12 @@ public struct ProtocolParameterDefinition: Sendable { case (.string, .string(let text)): guard (!required || !text.isEmpty), maximumBytes.map({ text.utf8.count <= $0 }) ?? true, - values.map({ $0.contains(text) }) ?? true else { + values.map({ allowed in + if caseInsensitiveValues { + return allowed.contains { $0.caseInsensitiveCompare(text) == .orderedSame } + } + return allowed.contains(text) + }) ?? true else { throw ProtocolValidationError.invalidParameter("Invalid string parameter: \(name)") } case (.number, .number(let number)), (.integer, .number(let number)): @@ -332,6 +382,7 @@ public struct ProtocolParameterDefinition: Sendable { if let minimum { result["minimum"] = .number(minimum) } if let maximum { result["maximum"] = .number(maximum) } if let values { result["values"] = .array(values.map(JSONValue.string)) } + if caseInsensitiveValues { result["caseInsensitiveValues"] = .bool(true) } if let maximumItems { result["maximumItems"] = .number(Double(maximumItems)) } if let itemMaximumBytes { result["itemMaximumBytes"] = .number(Double(itemMaximumBytes)) } return .object(result) @@ -387,11 +438,12 @@ public struct ProtocolCommandDefinition: Sendable { private func string( _ name: String, required: Bool = false, maximumBytes: Int = 8_192, - values: [String]? = nil, sensitive: Bool = false + values: [String]? = nil, caseInsensitiveValues: Bool = false, + sensitive: Bool = false ) -> ProtocolParameterDefinition { ProtocolParameterDefinition( name, kind: .string, required: required, maximumBytes: maximumBytes, - values: values, sensitive: sensitive + values: values, caseInsensitiveValues: caseInsensitiveValues, sensitive: sensitive ) } @@ -497,7 +549,10 @@ public let protocolCommandDefinitions: [CommandName: ProtocolCommandDefinition] boolean("fullPage"), string("output", maximumBytes: 128), string("series", maximumBytes: 32, values: ["viewport", "section"]), string("outputPrefix", maximumBytes: 80), - string("format", maximumBytes: 16, values: ["png", "jpg", "jpeg", "pdf"]), + string( + "format", maximumBytes: 16, values: ["png", "jpg", "jpeg", "pdf"], + caseInsensitiveValues: true + ), boolean("clipboard"), ], capabilityNegotiated: true, constraints: [ "target, full-page, and series modes are mutually constrained", @@ -506,8 +561,14 @@ public let protocolCommandDefinitions: [CommandName: ProtocolCommandDefinition] command(.artifactList), command(.recordStart, [ string("output", maximumBytes: 128), number("fps", minimum: 1, maximum: 30), - string("format", maximumBytes: 16, values: RecordingFormat.allCases.map(\.rawValue)), - string("quality", maximumBytes: 16, values: RecordingQuality.allCases.map(\.rawValue)), + string( + "format", maximumBytes: 16, values: RecordingFormat.allCases.map(\.rawValue), + caseInsensitiveValues: true + ), + string( + "quality", maximumBytes: 16, values: RecordingQuality.allCases.map(\.rawValue), + caseInsensitiveValues: true + ), ]), command(.recordStatus), command(.recordStop, [string("output", maximumBytes: 128)]), @@ -660,6 +721,45 @@ public let protocolSchemaDocument: JSONValue = { "config.set", "credentials.add", "credentials.list", "credentials.remove", "credentials.rename", "help", "runtime", "schema", "start", "version", ].map(JSONValue.string)), + "localLifecycle": .object([ + "connect": .object([ + "transport": .string("local-unix-socket"), + "ownership": .string("shared"), + "errors": .array(["HOST_UNAVAILABLE"].map(JSONValue.string)), + ]), + "launch": .object([ + "command": .string("start"), + "argv": .array(["start", "--background", "--supervised"].map(JSONValue.string)), + "options": .array([ + .object([ + "name": .string("presentation"), "type": .string("string"), + "values": .array(["background", "foreground"].map(JSONValue.string)), + "required": .bool(false), + ]), + .object([ + "name": .string("allow"), "type": .string("string-array"), + "maximumItems": .number(Double(NavigationAllowlist.maximumPatternCount)), + "itemMaximumBytes": .number(300), + "required": .bool(false), + ]), + .object([ + "name": .string("supervised"), "type": .string("boolean"), + "required": .bool(true), "const": .bool(true), + ]), + ]), + "result": protocolResultDefinition(for: .ping).document, + "errors": .array([ + "HOST_START_FAILED", "NAVIGATION_ALLOWLIST_CONFLICT", + "UNSUPPORTED_BROWSER_RUNTIME", "UNSUPPORTED_CAPABILITY", + ].map(JSONValue.string)), + "ownership": .string("owned-only-after-response-pid-matches-launched-child"), + ]), + ]), + "localErrorCodes": .array([ + "CONFIGURATION_FAILED", "HOST_START_FAILED", "HOST_UNAVAILABLE", + "INVALID_CONFIGURATION", "NAVIGATION_ALLOWLIST_CONFLICT", + "UNSUPPORTED_BROWSER_RUNTIME", "UNSUPPORTED_CAPABILITY", "VAULT_UNAVAILABLE", + ].map(JSONValue.string)), "hostLifecycle": .object([ "connect": .string("attach to an existing host and never assume ownership"), "sharedStart": .string("headless start --background"), @@ -696,6 +796,15 @@ public let protocolSchemaDocument: JSONValue = { "provenance": .string("publish from reviewed tags with package attestations"), "securityReporting": .string("SECURITY.md; never include secrets or private artifacts"), ]), + "supportWindow": .object([ + "wireVersions": .array([headlessProtocolVersion].map(JSONValue.string)), + "schemaVersions": .array([.number(Double(headlessProtocolSchemaVersion))]), + "hostPlatforms": .array(["macOS 13 or newer", "Linux amd64", "Linux arm64"].map(JSONValue.string)), + "typescriptRuntimes": .array(["Node.js 22 or newer"].map(JSONValue.string)), + "pythonRuntimes": .array(["CPython 3.11 through 3.14"].map(JSONValue.string)), + "maintenance": .string("latest two minor SDK lines and at least 12 months after supersession, whichever is longer"), + "securityFixes": .string("supported SDK lines receive applicable security fixes"), + ]), "security": .object([ "unknownRequestFields": .string("rejected"), "unknownResponseFields": .string("accepted for compatible additive evolution"), diff --git a/apps/headless/Tests/HeadlessMCPTests/main.swift b/apps/headless/Tests/HeadlessMCPTests/main.swift index 4c0ecc7..d3d8ec6 100644 --- a/apps/headless/Tests/HeadlessMCPTests/main.swift +++ b/apps/headless/Tests/HeadlessMCPTests/main.swift @@ -24,9 +24,28 @@ func integer(_ value: Any?, _ message: String) throws -> Int { } func run() throws { - guard CommandLine.arguments.count == 3 else { - throw TestFailure(description: "usage: headless-mcp-tests /path/to/headless-mcp EXPECTED_VERSION") + guard CommandLine.arguments.count == 4 else { + throw TestFailure( + description: "usage: headless-mcp-tests /path/to/headless-mcp EXPECTED_VERSION /path/to/protocol-fixtures.json" + ) + } + + let fixtureData = try Data(contentsOf: URL(fileURLWithPath: CommandLine.arguments[3])) + let fixtureRoot = try object( + JSONSerialization.jsonObject(with: fixtureData), "SDK fixture envelope was invalid" + ) + guard let fixtureCases = fixtureRoot["cases"] as? [[String: Any]], + let fixture = fixtureCases.first, + let fixtureArguments = fixture["argv"] as? [String], + let fixtureRequest = fixture["request"] as? [String: Any], + let fixtureCommand = fixtureRequest["command"] as? String else { + throw TestFailure(description: "SDK status fixture was absent") } + let fixtureCallData = try JSONSerialization.data(withJSONObject: [ + "jsonrpc": "2.0", "id": 3, "method": "tools/call", + "params": ["name": "headless", "arguments": ["argv": fixtureArguments]], + ]) + let fixtureCall = String(decoding: fixtureCallData, as: UTF8.self) try LocalRuntime.preparePrivateDirectory() let socketPath = LocalRuntime.directoryURL @@ -57,7 +76,7 @@ func run() throws { #"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}"#, #"{"jsonrpc":"2.0","method":"notifications/initialized"}"#, #"{"jsonrpc":"2.0","id":2,"method":"tools/list"}"#, - #"{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"headless","arguments":{"argv":["status"]}}}"#, + fixtureCall, #"{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"headless","arguments":{"argv":["stop"]}}}"#, #"{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"headless","arguments":{"argv":["session","close","disposable"]}}}"#, "not-json", @@ -129,6 +148,10 @@ func run() throws { try expect(browserResponse["ok"] as? Bool == true, "browser protocol response was not successful") let browserResult = try object(browserResponse["result"], "browser protocol result was absent") try expect(browserResult["ready"] as? Bool == true, "browser command did not reach the local host") + try expect( + browserResult["command"] as? String == fixtureCommand, + "MCP request drifted from the shared SDK fixture" + ) for (index, expectedCommand) in [(3, "shutdown"), (4, "session.close")] { let destructiveCall = try object(responses[index]["result"], "destructive tools/call result was absent") diff --git a/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift b/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift index 913f32c..5ce25e1 100644 --- a/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift +++ b/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift @@ -36,6 +36,16 @@ private func expectThrows(_ message: String, _ body: () throws -> Void) throws { } } +private func repositoryFile(_ relativePath: String) -> URL? { + var directory = URL(fileURLWithPath: #filePath).deletingLastPathComponent() + while directory.path != "/" { + let candidate = directory.appendingPathComponent(relativePath) + if FileManager.default.fileExists(atPath: candidate.path) { return candidate } + directory.deleteLastPathComponent() + } + return nil +} + private func expectSettingsError( _ expected: SettingsError, _ message: String, _ body: () throws -> Void ) throws { @@ -2592,12 +2602,20 @@ struct ProtocolTests { screenshotFormat?.values == ["png", "jpg", "jpeg", "pdf"], "the schema must expose every accepted screenshot spelling" ) + try expect( + screenshotFormat?.caseInsensitiveValues == true, + "the schema must preserve case-insensitive screenshot formats" + ) let recordingQuality = protocolCommandDefinition(for: .recordStart).parameters .first { $0.name == "quality" } try expect( recordingQuality?.values == RecordingQuality.allCases.map(\.rawValue), "the schema must derive recording quality values from the parser enum" ) + try expect( + recordingQuality?.caseInsensitiveValues == true, + "the schema must preserve case-insensitive recording quality values" + ) try expect( protocolErrorCodes.contains(AuthenticationError.challengeExpired.code) && protocolErrorCodes.contains("INVALID_INPUT") @@ -2610,18 +2628,81 @@ struct ProtocolTests { schemaData.count < headlessMaximumMessageBytes, "the protocol schema must fit the protocol frame bound" ) - let source = URL(fileURLWithPath: #filePath) - .deletingLastPathComponent() - .deletingLastPathComponent() - .deletingLastPathComponent() - .deletingLastPathComponent() - .deletingLastPathComponent() - .appendingPathComponent("sdk/protocol-schema.json") - let golden = try Data(contentsOf: source) - try expect( - try ProtocolCodec.decoder.decode(JSONValue.self, from: golden) == protocolSchemaDocument, - "the checked-in SDK schema must match the Swift-owned contract" - ) + if let source = repositoryFile("sdk/protocol-schema.json") { + let golden = try Data(contentsOf: source) + try expect( + try ProtocolCodec.decoder.decode(JSONValue.self, from: golden) == protocolSchemaDocument, + "the checked-in SDK schema must match the Swift-owned contract" + ) + } else if ProcessInfo.processInfo.environment["HEADLESS_REQUIRE_SDK_CONTRACT"] == "1" { + throw TestFailure(description: "required sdk/protocol-schema.json was not found") + } + } + + static func sdkProtocolFixtures() throws { + guard let source = repositoryFile("sdk/protocol-fixtures.json") else { + if ProcessInfo.processInfo.environment["HEADLESS_REQUIRE_SDK_CONTRACT"] == "1" { + throw TestFailure(description: "required sdk/protocol-fixtures.json was not found") + } + return + } + let document = try ProtocolCodec.decoder.decode( + JSONValue.self, from: Data(contentsOf: source) + ) + guard case .object(let root) = document, + root["schemaVersion"] == .number(Double(headlessProtocolSchemaVersion)), + root["protocolVersion"] == .string(headlessProtocolVersion), + case .array(let cases)? = root["cases"], + case .array(let directRequests)? = root["directRequests"], + case .array(let invalidRequests)? = root["invalidRequests"] else { + throw TestFailure(description: "SDK fixture envelope is invalid") + } + + for fixture in cases { + guard case .object(let fields) = fixture, + case .array(let rawArguments)? = fields["argv"], + case .object? = fields["request"], + let requestValue = fields["request"], + let responseValue = fields["response"] else { + throw TestFailure(description: "SDK fixture case is invalid") + } + let arguments = rawArguments.compactMap(\.stringValue) + try expect(arguments.count == rawArguments.count, "fixture argv must contain strings") + let request = try ProtocolCodec.decoder.decode( + CommandRequest.self, from: ProtocolCodec.encoder.encode(requestValue) + ) + try request.validate() + let invocation = try CLIParser().parse(arguments) + guard let parsed = invocation.request else { + throw TestFailure(description: "fixture argv did not produce a wire request") + } + try expect(parsed.command == request.command, "fixture CLI command drifted") + try expect(parsed.session == request.session, "fixture CLI session drifted") + try expect(parsed.parameters == request.parameters, "fixture CLI parameters drifted") + + let response = try ProtocolCodec.decoder.decode( + CommandResponse.self, from: ProtocolCodec.encoder.encode(responseValue) + ) + try expect(response.id == request.id, "fixture response id drifted") + try expect(response.version == headlessProtocolVersion, "fixture response version drifted") + guard response.ok, let result = response.result else { + throw TestFailure(description: "fixture success response is invalid") + } + try protocolResultDefinition(for: request.command).validate(result) + } + + for value in directRequests { + let request = try ProtocolCodec.decoder.decode( + CommandRequest.self, from: ProtocolCodec.encoder.encode(value) + ) + try request.validate() + } + for value in invalidRequests { + let request = try ProtocolCodec.decoder.decode( + CommandRequest.self, from: ProtocolCodec.encoder.encode(value) + ) + try expectThrows("invalid SDK fixture request was accepted") { try request.validate() } + } } static func supervisedHostOwnerPipe() throws { @@ -3985,6 +4066,7 @@ struct ProtocolTests { ("recording arguments and bounds", recordingArgumentsAndFailureBounds), ("capabilities match commands", capabilitiesMatchProtocolCommands), ("SDK protocol schema contract", sdkProtocolSchemaContract), + ("SDK protocol fixtures", sdkProtocolFixtures), ("supervised host owner pipe", supervisedHostOwnerPipe), ("screenshot series helpers", screenshotSeriesHelpers), ("diagnostic summary", diagnosticSummary), diff --git a/apps/headless/Tests/linux-e2e.sh b/apps/headless/Tests/linux-e2e.sh index 71f9a70..ea939e3 100755 --- a/apps/headless/Tests/linux-e2e.sh +++ b/apps/headless/Tests/linux-e2e.sh @@ -6,6 +6,9 @@ export XDG_DATA_HOME="/tmp/headless-data-e2e-$$" export XDG_CONFIG_HOME="/tmp/headless-config-e2e-$$" export HEADLESS_HOST_LOG="/tmp/headless-host-e2e-$$.log" STEP="setup" +SUPERVISED_FIFO="" +SUPERVISED_OUTPUT="" +SUPERVISED_LAUNCHER_PID="" FIXTURE_ROOT="$(mktemp -d /tmp/headless-fixture.XXXXXX)" INSTALL_ROOT="$(mktemp -d /tmp/headless-install.XXXXXX)" @@ -35,6 +38,9 @@ cleanup() { fi fi headless stop >/dev/null 2>&1 || true + if [ -n "$SUPERVISED_LAUNCHER_PID" ]; then + kill "$SUPERVISED_LAUNCHER_PID" >/dev/null 2>&1 || true + fi kill "$FIXTURE_PID" >/dev/null 2>&1 || true rm -rf "$FIXTURE_ROOT" rm -rf "$INSTALL_ROOT" @@ -42,6 +48,8 @@ cleanup() { rm -rf "$XDG_DATA_HOME" rm -rf "$XDG_CONFIG_HOME" rm -f "$HEADLESS_HOST_LOG" + [ -z "$SUPERVISED_FIFO" ] || rm -f "$SUPERVISED_FIFO" + [ -z "$SUPERVISED_OUTPUT" ] || rm -f "$SUPERVISED_OUTPUT" exit "$status" } trap cleanup EXIT INT TERM @@ -112,6 +120,39 @@ if PRESENTATION_START="$(headless start --foreground 2>&1)"; then fi echo "$PRESENTATION_START" | grep -q 'UNSUPPORTED_CAPABILITY' +STEP="supervised-host-owner-exit" +SUPERVISED_FIFO="$(mktemp /tmp/headless-supervised-fifo.XXXXXX)" +SUPERVISED_OUTPUT="$(mktemp /tmp/headless-supervised-output.XXXXXX)" +rm -f "$SUPERVISED_FIFO" +mkfifo "$SUPERVISED_FIFO" +headless start --supervised <"$SUPERVISED_FIFO" >"$SUPERVISED_OUTPUT" & +SUPERVISED_LAUNCHER_PID=$! +exec 9>"$SUPERVISED_FIFO" +for _ in $(seq 1 160); do + [ -s "$SUPERVISED_OUTPUT" ] && headless status >/dev/null 2>&1 && break + sleep 0.05 +done +SUPERVISED_RESULT="$(cat "$SUPERVISED_OUTPUT")" +echo "$SUPERVISED_RESULT" | grep -q '"ready":true' +SUPERVISED_HOST_PID="$(echo "$SUPERVISED_RESULT" | sed -n 's/.*"pid":\([0-9][0-9]*\).*/\1/p')" +test -n "$SUPERVISED_HOST_PID" +test "$(headless status | sed -n 's/.*"pid":\([0-9][0-9]*\).*/\1/p')" = "$SUPERVISED_HOST_PID" +kill -9 "$SUPERVISED_LAUNCHER_PID" +wait "$SUPERVISED_LAUNCHER_PID" >/dev/null 2>&1 || true +SUPERVISED_LAUNCHER_PID="" +for _ in $(seq 1 100); do + ! kill -0 "$SUPERVISED_HOST_PID" >/dev/null 2>&1 && break + sleep 0.05 +done +if kill -0 "$SUPERVISED_HOST_PID" >/dev/null 2>&1; then + echo "supervised host survived launcher termination" >&2 + exit 1 +fi +exec 9>&- +rm -f "$SUPERVISED_FIFO" "$SUPERVISED_OUTPUT" +SUPERVISED_FIFO="" +SUPERVISED_OUTPUT="" + STEP="host-start" headless start | grep -q '"ready":true' test "$(stat -c %a "$XDG_DATA_HOME/headless")" = "700" diff --git a/apps/headless/Tests/macos-e2e.sh b/apps/headless/Tests/macos-e2e.sh index 6d01d40..ecdbfbc 100755 --- a/apps/headless/Tests/macos-e2e.sh +++ b/apps/headless/Tests/macos-e2e.sh @@ -31,6 +31,9 @@ export HEADLESS_HOST_LOG="$HOST_LOG" STEP="boot" RESTORE_PID="" HOST_PID="" +SUPERVISED_LAUNCHER_PID="" +SUPERVISED_FIFO="" +SUPERVISED_OUTPUT="" DEFAULTS_DOMAIN="com.headless.app" PRESENTATION_KEY="AgentStartupPresentation" DEFAULTS_HAD_LAST_URL=0 @@ -439,6 +442,9 @@ cleanup() { if [[ -n "$RESTORE_PID" ]]; then kill "$RESTORE_PID" >/dev/null 2>&1 || true fi + if [[ -n "$SUPERVISED_LAUNCHER_PID" ]]; then + kill "$SUPERVISED_LAUNCHER_PID" >/dev/null 2>&1 || true + fi kill "$FIXTURE_PID" >/dev/null 2>&1 || true restore_last_url restore_startup_presentation @@ -448,6 +454,8 @@ cleanup() { rm -rf "$HEADLESS_ARTIFACT_DIR" rm -rf "$MENU_SNAPSHOT_DIR" rm -f "$HEADLESS_SOCKET" "$LOG" "$HOST_LOG" "$RESTORE_LOG" + [[ -z "$SUPERVISED_FIFO" ]] || rm -f "$SUPERVISED_FIFO" + [[ -z "$SUPERVISED_OUTPUT" ]] || rm -f "$SUPERVISED_OUTPUT" if [[ "$CLIPBOARD_SAVED" == 0 ]]; then rm -f "$CLIPBOARD_BACKUP" fi @@ -530,6 +538,41 @@ test "$(defaults read "$DEFAULTS_DOMAIN" "$PRESENTATION_KEY")" = "background" CONFIGURED_PRESENTATION="$("$CLI" config get startup-presentation)" echo "$CONFIGURED_PRESENTATION" | grep -q '"configured":"background"' echo "$CONFIGURED_PRESENTATION" | grep -q '"startupPresentation":"background"' + +STEP="supervised-host-owner-exit" +SUPERVISED_FIFO="$(mktemp "${TMPDIR:-/tmp}/headless-supervised-fifo.XXXXXX")" +SUPERVISED_OUTPUT="$(mktemp "${TMPDIR:-/tmp}/headless-supervised-output.XXXXXX")" +rm -f "$SUPERVISED_FIFO" +mkfifo "$SUPERVISED_FIFO" +"$CLI" start --background --supervised <"$SUPERVISED_FIFO" >"$SUPERVISED_OUTPUT" & +SUPERVISED_LAUNCHER_PID=$! +exec 9>"$SUPERVISED_FIFO" +for _ in {1..160}; do + [[ -s "$SUPERVISED_OUTPUT" ]] && "$CLI" status >/dev/null 2>&1 && break + sleep 0.05 +done +SUPERVISED_RESULT="$(cat "$SUPERVISED_OUTPUT")" +echo "$SUPERVISED_RESULT" | grep -q '"ready":true' +SUPERVISED_HOST_PID="$(echo "$SUPERVISED_RESULT" | sed -n 's/.*"pid":\([0-9][0-9]*\).*/\1/p')" +test -n "$SUPERVISED_HOST_PID" +test "$("$CLI" status | sed -n 's/.*"pid":\([0-9][0-9]*\).*/\1/p')" = "$SUPERVISED_HOST_PID" +kill -9 "$SUPERVISED_LAUNCHER_PID" +wait "$SUPERVISED_LAUNCHER_PID" >/dev/null 2>&1 || true +SUPERVISED_LAUNCHER_PID="" +for _ in {1..100}; do + ! kill -0 "$SUPERVISED_HOST_PID" >/dev/null 2>&1 && break + sleep 0.05 +done +if kill -0 "$SUPERVISED_HOST_PID" >/dev/null 2>&1; then + echo "supervised host survived launcher termination" >&2 + exit 1 +fi +exec 9>&- +rm -f "$SUPERVISED_FIFO" "$SUPERVISED_OUTPUT" +SUPERVISED_FIFO="" +SUPERVISED_OUTPUT="" + +STEP="start-host" START_RESULT="$("$CLI" start)" || { print -r -u2 -- "headless start failed:" print -r -u2 -- "$START_RESULT" diff --git a/apps/headless/docs/COMMANDS.md b/apps/headless/docs/COMMANDS.md index 86532f7..dbd8375 100644 --- a/apps/headless/docs/COMMANDS.md +++ b/apps/headless/docs/COMMANDS.md @@ -39,9 +39,9 @@ schema controls the host afterwards. `runtime` reports which engine is active and where it came from. - `start --supervised` is for SDK-owned lifecycle management. It refuses to - attach to an existing host, keeps the launcher alive, and shuts the host down - when the launcher's standard-input owner pipe closes. Normal starts remain - shared and detached. + attach to an existing host, verifies the launched host PID, and shuts the host + down when the launcher input closes or the launcher exits. Normal starts + remain shared and detached. - `schema` prints the versioned SDK contract generated from the Swift request definitions. It is local-only and includes request and response envelopes, command parameters and bounds, errors, compatibility, cancellation, and diff --git a/apps/headless/docs/P4.md b/apps/headless/docs/P4.md index 3967d58..90607fc 100644 --- a/apps/headless/docs/P4.md +++ b/apps/headless/docs/P4.md @@ -45,5 +45,7 @@ engine does not support them. Page-derived results remain untrusted. Closing a client connection after a request was sent does not undo the browser operation. SDKs report that outcome as unknown and require a fresh inspection. `headless start --supervised` provides the owned-host path: it refuses to attach -to an existing host, inherits a live standard-input owner pipe, and shuts down -when that pipe closes. An SDK must never stop a shared host implicitly. +to an existing host, creates a private ownership pipe, verifies the responding +host PID, and shuts down when the launcher input closes. An SDK must never stop +a shared host implicitly. The generated contract also declares typed lifecycle +errors and the supported runtime window. diff --git a/apps/headless/test.sh b/apps/headless/test.sh index c870775..8a1c11f 100755 --- a/apps/headless/test.sh +++ b/apps/headless/test.sh @@ -66,7 +66,7 @@ swift build "${SDK_ARGS[@]}" --product headless --scratch-path "$TEST_SCRATCH" swift build "${SDK_ARGS[@]}" --product headless-credential-broker --scratch-path "$TEST_SCRATCH" swift build "${SDK_ARGS[@]}" --product headless-mcp --scratch-path "$TEST_SCRATCH" swift build "${SDK_ARGS[@]}" --product headless-mcp-tests --scratch-path "$TEST_SCRATCH" -"$BIN_PATH/headless-protocol-tests" +HEADLESS_REQUIRE_SDK_CONTRACT=1 "$BIN_PATH/headless-protocol-tests" if [[ "$(uname -s)" == "Darwin" ]]; then cc -D_GNU_SOURCE -std=c11 -Wall -Wextra -Werror \ -I SecurePrompt/include SecurePrompt/SecurePrompt.c Tests/secure-prompt.c \ @@ -102,4 +102,5 @@ for invocation in path symlink; do exit 1 fi done -"$BIN_PATH/headless-mcp-tests" "$BIN_PATH/headless-mcp" "$EXPECTED_VERSION" +"$BIN_PATH/headless-mcp-tests" "$BIN_PATH/headless-mcp" "$EXPECTED_VERSION" \ + "../../sdk/protocol-fixtures.json" diff --git a/docs/roadmap/architecture-decisions.md b/docs/roadmap/architecture-decisions.md index 3291cce..6d6c35e 100644 --- a/docs/roadmap/architecture-decisions.md +++ b/docs/roadmap/architecture-decisions.md @@ -841,14 +841,17 @@ Cancellation before send writes no request. Cancellation or timeout after send closes the client connection but cannot claim the browser operation was rolled back; clients report an unknown outcome and refresh state. SDK-owned hosts use `headless start --supervised`, which refuses to attach to an existing host and -keeps a standard-input owner pipe open. Closing that pipe stops the owned host -and lets the launcher reap it. SDKs never stop a shared host they discovered. +creates a private owner pipe after launching the host. Ownership is granted only +when the startup response PID matches that child. Closing launcher input, or the +launcher exiting, closes the private pipe, stops the owned host, and lets the +launcher reap it. SDKs never stop a shared host they discovered. **Status:** accepted for [#163](https://github.com/LockInTime/headless/issues/163). **Consequences:** TypeScript and Python clients generate or validate their public command types from the golden schema and pin its digest. Schema, SDK, -CLI-parser, protocol-validator, and MCP parity are tested together. SDK package +shared fixtures, CLI-parser, protocol-validator, and MCP parity are tested +together. SDK package versions may advance independently while declaring the supported wire version. Deprecation, support-window, provenance, and security-reporting rules live with the schema so client packages cannot silently invent a different policy. diff --git a/sdk/README.md b/sdk/README.md index 1f7cc20..ccbcd84 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -1,14 +1,15 @@ # Headless SDK contract `protocol-schema.json` is the checked-in SDK contract emitted by the Swift -implementation: +implementation. `protocol-fixtures.json` contains representative requests and +responses consumed by Swift, MCP, and every generated SDK: ```sh swift run --package-path apps/headless headless schema > sdk/protocol-schema.json ``` -The protocol suite compares this file byte-for-byte with `headless schema` and -also decodes it back to the Swift value. Do not edit the JSON by hand. +The protocol suite compares the schema byte-for-byte with `headless schema` and +also decodes it back to the Swift value. Do not edit the schema JSON by hand. ## Compatibility @@ -37,9 +38,12 @@ or client shutdown. Use `headless start --background --supervised` for an owned host. The command fails if a shared host already exists, prints the normal startup response, and -then remains attached to the host. The SDK keeps the child standard-input pipe -open for the ownership lifetime. Closing the pipe makes the host stop and lets -the launcher be awaited. A plain `headless start` remains detached and shared. +then remains attached to the host. The launcher owns a private pipe to the host, +so the host still stops if the launcher is killed while another process retains +the launcher's input stream. The startup response PID must match the launched +host before ownership is granted. Closing the launcher input makes the host stop +and lets the launcher be awaited. A plain `headless start` remains detached and +shared. ## Release policy @@ -49,3 +53,9 @@ Headless product while declaring their supported wire versions. Before 1.0, deprecations remain for at least one minor SDK release; after 1.0, removals require a major SDK release. Security reports use the repository process in `SECURITY.md` and must not include credentials, cookies, or private artifacts. + +The initial support window covers wire protocol `0.5`, schema format `1`, +macOS 13 or newer, Linux amd64/arm64, Node.js 22 or newer, and CPython 3.11 +through 3.14. The latest two minor SDK lines remain maintained for at least 12 +months after supersession, whichever period is longer. Applicable security +fixes are backported to supported lines. diff --git a/sdk/protocol-fixtures.json b/sdk/protocol-fixtures.json new file mode 100644 index 0000000..86d4ce7 --- /dev/null +++ b/sdk/protocol-fixtures.json @@ -0,0 +1,103 @@ +{ + "schemaVersion": 1, + "protocolVersion": "0.5", + "cases": [ + { + "name": "host status", + "argv": ["status"], + "request": { + "id": "fixture-status", + "version": "0.5", + "command": "ping", + "parameters": {} + }, + "response": { + "id": "fixture-status", + "version": "0.5", + "ok": true, + "result": { + "ready": true, + "pid": 1234, + "engine": "webkit", + "platform": "macOS", + "productVersion": "1.2.3", + "protocolVersion": "0.5", + "capabilities": {}, + "recordingAvailable": false, + "artifactDirectory": "/private/fixture", + "navigationAllowlist": [] + } + } + }, + { + "name": "visit page", + "argv": ["visit", "https://example.com/path"], + "request": { + "id": "fixture-visit", + "version": "0.5", + "command": "visit", + "parameters": {"url": "https://example.com/path"} + }, + "response": { + "id": "fixture-visit", + "version": "0.5", + "ok": true, + "result": { + "url": "https://example.com/path", + "title": "Fixture", + "readyState": "complete", + "text": "Untrusted fixture page", + "runningAnimations": 0, + "mutationQuietMs": 500, + "scrollY": 0, + "contentHeight": 800 + } + } + }, + { + "name": "credential alias login", + "argv": ["auth", "login", "--challenge", "11111111-1111-4111-8111-111111111111", "--account", "work"], + "request": { + "id": "fixture-auth", + "version": "0.5", + "command": "auth.login", + "parameters": {"challenge": "11111111-1111-4111-8111-111111111111", "account": "work"} + }, + "response": { + "id": "fixture-auth", + "version": "0.5", + "ok": true, + "result": { + "origin": "https://example.com", + "account": "work", + "saved": true, + "continuation": "authenticated", + "passwordExposed": false, + "originalActionReplayed": false + } + } + } + ], + "directRequests": [ + { + "id": "fixture-uppercase-screenshot", + "version": "0.5", + "command": "screenshot", + "parameters": {"format": "PNG", "fullPage": false} + }, + { + "id": "fixture-uppercase-recording", + "version": "0.5", + "command": "record.start", + "parameters": {"format": "MP4", "quality": "HIGH"} + } + ], + "invalidRequests": [ + { + "id": "fixture-unknown-parameter", + "version": "0.5", + "command": "ping", + "parameters": {"unexpected": true} + } + ] +} diff --git a/sdk/protocol-schema.json b/sdk/protocol-schema.json index 6cacdb9..c986c8a 100644 --- a/sdk/protocol-schema.json +++ b/sdk/protocol-schema.json @@ -1 +1 @@ -{"cancellation":{"afterSend":"close the client connection; command outcome is unknown until state is inspected","beforeSend":"request is not written","ownedHost":"terminate, await, and reap only a host process launched by that SDK instance","sharedHost":"never stop the host implicitly"},"commands":[{"capabilityNegotiated":false,"constraints":[],"name":"ping","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"shutdown","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"stopping","required":true,"type":"boolean"}],"name":"Shutdown","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"profile.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"boolean"},{"name":"session","required":true,"type":"string"}],"name":"ProfileClear","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.create","parameters":[{"maximumBytes":64,"name":"name","required":true,"sensitive":false,"type":"string"},{"name":"isolated","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"session","required":true,"type":"string"},{"name":"isolated","required":true,"type":"boolean"}],"name":"SessionCreate","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"sessions","required":true,"type":"array"},{"name":"details","required":true,"type":"array"}],"name":"SessionList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.close","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"closed","required":true,"type":"string"}],"name":"SessionClose","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"visit","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"inspect","parameters":[{"name":"interactive","required":false,"sensitive":false,"type":"boolean"},{"name":"text","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":16,"name":"context","required":false,"sensitive":false,"type":"string","values":["summary","outline","text","actions","full"]},{"maximumBytes":512,"name":"task","required":false,"sensitive":false,"type":"string"},{"maximumBytes":16,"name":"within","required":false,"sensitive":false,"type":"string"},{"maximum":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximum":16000,"minimum":256,"name":"budget","required":false,"sensitive":false,"type":"integer"},{"maximum":8,"minimum":0,"name":"depth","required":false,"sensitive":false,"type":"integer"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"contextMode","required":true,"type":"string"},{"name":"viewport","required":true,"type":"object"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"elements","required":false,"type":"array"},{"name":"regions","required":false,"type":"array"},{"name":"snippets","required":false,"type":"array"},{"name":"text","required":false,"type":"string"}],"name":"Inspection","type":"object"}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"click","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"clicked","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"}],"name":"Click","type":"object"}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"fill","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":900000,"name":"value","required":true,"sensitive":true,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"filled","required":true,"type":"string"},{"name":"valueLength","required":true,"type":"number"}],"name":"Fill","type":"object"}}},{"capabilityNegotiated":true,"constraints":["artifact must be an existing private-store basename","exactly one target reference or semantic role/name target"],"name":"upload","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"artifact","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"uploaded","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"artifact","required":true,"type":"string"}],"name":"Upload","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"press","parameters":[{"maximumBytes":32,"name":"key","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"pressed","required":true,"type":"string"}],"name":"Press","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"scroll","parameters":[{"maximumBytes":8192,"name":"direction","required":false,"sensitive":false,"type":"string","values":["up","down","top","bottom"]},{"maximum":100000,"minimum":0.1,"name":"amount","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"direction","required":true,"type":"string"},{"name":"amount","required":true,"type":"number"}],"name":"Scroll","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"back","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"reload","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"wait","parameters":[{"name":"settled","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":8192,"name":"url","required":false,"sensitive":false,"type":"string"},{"maximumBytes":30000,"name":"text","required":false,"sensitive":false,"type":"string"},{"maximum":120000,"minimum":100,"name":"timeoutMs","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"tour","parameters":[{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximum":5000,"minimum":100,"name":"pace","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"start","required":true,"type":"number"},{"name":"end","required":true,"type":"number"},{"name":"durationMs","required":true,"type":"number"}],"name":"Tour","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"capture.info","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"engine","required":true,"type":"string"},{"name":"page","required":true,"type":"object"},{"name":"trace","required":true,"type":"array"},{"name":"recording","required":true,"type":"object"}],"name":"CaptureInfo","type":"object"}}},{"capabilityNegotiated":true,"constraints":["target, full-page, and series modes are mutually constrained","PDF requires full-page mode and no clipboard"],"name":"screenshot","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximumBytes":32,"name":"series","required":false,"sensitive":false,"type":"string","values":["viewport","section"]},{"maximumBytes":80,"name":"outputPrefix","required":false,"sensitive":false,"type":"string"},{"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["png","jpg","jpeg","pdf"]},{"name":"clipboard","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":false,"type":"string"},{"name":"path","required":false,"type":"string"},{"name":"kind","required":false,"type":"string"},{"name":"bytes","required":false,"type":"number"},{"name":"createdAt","required":false,"type":"number"},{"name":"artifacts","required":false,"type":"array"},{"name":"truncated","required":false,"type":"boolean"}],"name":"Screenshot","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"artifact.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"directory","required":true,"type":"string"},{"name":"artifacts","required":true,"type":"array"},{"name":"total","required":true,"type":"number"},{"name":"omitted","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"ArtifactList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.start","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximum":30,"minimum":1,"name":"fps","required":false,"sensitive":false,"type":"number"},{"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["mp4","mov","webm","gif"]},{"maximumBytes":16,"name":"quality","required":false,"sensitive":false,"type":"string","values":["fast","balanced","high"]}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.status","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.report","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"summary","required":false,"type":"object"}],"name":"QAReport","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"QAClear","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"console.list","parameters":[{"maximumBytes":16,"name":"level","required":false,"sensitive":false,"type":"string","values":["all","log","info","debug","warn","error","assert"]},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"messages","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"ConsoleList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.list","parameters":[{"name":"failed","required":false,"sensitive":false,"type":"boolean"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"requests","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"NetworkList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.get","parameters":[{"maximumBytes":128,"name":"requestId","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"found","required":true,"type":"boolean"},{"name":"requestId","required":false,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"request","required":false,"type":"object"}],"name":"NetworkDetail","type":"object"}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"styles.get","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"itemMaximumBytes":128,"maximumItems":64,"name":"properties","required":false,"sensitive":false,"type":"string-array"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"ref","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"box","required":true,"type":"object"},{"name":"styles","required":true,"type":"object"}],"name":"Styles","type":"object"}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"cookies.list","parameters":[{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"cookies","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"CookieList","type":"object"}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"storage.list","parameters":[{"maximumBytes":16,"name":"scope","required":false,"sensitive":false,"type":"string","values":["local","session","all"]},{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"stores","required":true,"type":"array"}],"name":"StorageList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"visual.compare","parameters":[{"maximumBytes":128,"name":"before","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"after","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"changedPixels","required":false,"type":"number"},{"name":"differenceRatio","required":false,"type":"number"}],"name":"VisualComparison","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"performance.get","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"timing","required":true,"type":"json"},{"name":"webVitals","required":true,"type":"object"},{"name":"resources","required":true,"type":"object"}],"name":"Performance","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"animation.list","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"count","required":true,"type":"number"},{"name":"animations","required":true,"type":"array"},{"name":"truncated","required":true,"type":"boolean"}],"name":"AnimationList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"report.create","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.start","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"recording","required":true,"type":"boolean"},{"name":"note","required":true,"type":"string"}],"name":"FlowStart","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.run","parameters":[{"maximumBytes":128,"name":"input","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"completed","required":true,"type":"number"},{"name":"input","required":true,"type":"string"}],"name":"FlowRun","type":"object"}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.emulate","parameters":[{"name":"offline","required":false,"sensitive":false,"type":"boolean"},{"maximum":120000,"minimum":0,"name":"latencyMs","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"downloadKbps","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"uploadKbps","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"offline","required":true,"type":"boolean"},{"name":"latencyMs","required":true,"type":"number"},{"name":"downloadKbps","required":true,"type":"number"},{"name":"uploadKbps","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"}],"name":"NetworkEmulation","type":"object"}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.set","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximumBytes":65536,"name":"body","required":true,"sensitive":false,"type":"string"},{"maximumBytes":256,"name":"contentType","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"status","required":true,"type":"number"},{"name":"activeMocks","required":true,"type":"number"}],"name":"NetworkMock","type":"object"}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"NetworkMockClear","type":"object"}}},{"capabilityNegotiated":true,"constraints":["choose interactive login or account alias","saved login requires a valid single-use challenge"],"name":"auth.login","parameters":[{"maximumBytes":64,"name":"challenge","required":false,"sensitive":false,"type":"string"},{"maximumBytes":64,"name":"account","required":false,"sensitive":false,"type":"string"},{"name":"interactive","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"account","required":true,"type":"string-or-null"},{"name":"saved","required":true,"type":"boolean"},{"name":"continuation","required":true,"type":"string"}],"name":"AuthenticationLogin","type":"object"}}}],"compatibility":{"mismatch":"host rejects request or client rejects response before decoding result","olderClient":"may call only commands and parameters declared by its bundled schema","olderHost":"missing commands or capabilities fail explicitly; clients must not emulate them","policy":"exact-wire-version","productVersionIndependent":true},"errorDetails":{"AUTH_REQUIRED":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"challenge","required":true,"type":"string"},{"name":"origin","required":true,"type":"string"},{"name":"detection","required":true,"type":"string"},{"name":"accounts","required":true,"type":"array"},{"name":"expiresInSeconds","required":true,"type":"number"},{"name":"userPresenceRequired","required":true,"type":"boolean"},{"name":"credentialUseAvailable","required":true,"type":"boolean"},{"name":"vaultAvailable","required":true,"type":"boolean"},{"name":"vaultStatus","required":true,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationRequired","type":"object"}}},"format":"headless-sdk-contract","hostLifecycle":{"connect":"attach to an existing host and never assume ownership","existingHost":"supervised start fails rather than attaching","ownerExit":"host shuts down when the owner pipe closes","sharedStart":"headless start --background","shutdown":"only explicit user requests may stop a shared host","supervisedOwnerChannel":"open standard-input pipe","supervisedStart":"headless start --background --supervised"},"jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","localCommands":["capabilities","config.describe","config.get","config.list","config.reset","config.set","credentials.add","credentials.list","credentials.remove","credentials.rename","help","runtime","schema","start","version"],"maximumMessageBytes":1048576,"protocolVersion":"0.5","releasePolicy":{"preOneDeprecation":"at least one minor SDK release","provenance":"publish from reviewed tags with package attestations","schemaVersioning":"integer; generators reject unknown versions","sdkVersioning":"semantic versions independent of product and wire versions","securityReporting":"SECURITY.md; never include secrets or private artifacts","stableRemoval":"major SDK release and migration notes"},"request":{"additionalProperties":false,"id":{"maximumBytes":128,"minimumBytes":1,"type":"string"},"parameters":{"additionalProperties":false,"type":"object"},"required":["id","version","command","parameters"],"session":{"maximumBytes":64,"type":["string","null"]},"type":"object","version":{"const":"0.5","type":"string"}},"response":{"additionalProperties":true,"failure":{"error":{"codes":["ARTIFACT_ERROR","AUTH_ACCOUNT_NOT_FOUND","AUTH_CHALLENGE_CONSUMED","AUTH_CHALLENGE_EXPIRED","AUTH_CHALLENGE_NOT_FOUND","AUTH_FORM_CHANGED","AUTH_ORIGIN_CHANGED","AUTH_REQUIRED","CREDENTIAL_ALIAS_EXISTS","ELEMENT_NOT_FOUND","FLOW_FAILED","HOST_STOPPING","HOST_UNAVAILABLE","INTERNAL_ERROR","INVALID_CAPTURE_FORMAT","INVALID_COMMAND","INVALID_FLOW","INVALID_INPUT","INVALID_REQUEST","INVALID_SESSION","MISSING_PARAMETER","OPERATION_FAILED","PEER_DENIED","RECORDER_UNAVAILABLE","RECORDING_ACTIVE","RECORDING_FAILED","RECORDING_NOT_ACTIVE","REGION_NOT_FOUND","RESPONSE_TOO_LARGE","SENSITIVE_DIAGNOSTICS_DISABLED","SESSION_EXISTS","SESSION_NOT_FOUND","TIMEOUT","UNSAFE_NAVIGATION","UNSAFE_RESOURCE_TYPE","UNSUPPORTED_CAPABILITY","USER_PRESENCE_DENIED","USER_PRESENCE_UNAVAILABLE","VAULT_LOCKED","VAULT_OPERATION_FAILED","VAULT_RESPONSE_INVALID","VAULT_UNAVAILABLE"],"optional":["suggestion","details"],"required":["code","message"]},"ok":false,"result":null},"required":["id","version","ok"],"success":{"error":null,"ok":true,"result":"json"},"type":"object"},"schemaVersion":1,"security":{"arbitraryJavaScript":false,"credentialParameters":"aliases-and-challenge-identifiers-only","pageData":"untrusted","sensitiveDiagnostics":"requires-command-flag-and-host-environment-gate","tcpListener":false,"unknownRequestFields":"rejected","unknownResponseFields":"accepted for compatible additive evolution"},"timeouts":{"connect":"fails before a request is sent","requestAfterWrite":"outcome unknown; never retry automatically","requestBeforeWrite":"safe to retry with a new request identifier","startup":"terminate and reap only the supervised launcher and host"},"transport":{"framing":"one-newline-terminated-json-document-per-connection","kind":"local-unix-socket","peerAuthorization":"same-os-user","runtimeDirectoryMode":"0700","socketMode":"0600","tcp":false}} +{"cancellation":{"afterSend":"close the client connection; command outcome is unknown until state is inspected","beforeSend":"request is not written","ownedHost":"terminate, await, and reap only a host process launched by that SDK instance","sharedHost":"never stop the host implicitly"},"commands":[{"capabilityNegotiated":false,"constraints":[],"name":"ping","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"shutdown","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"stopping","required":true,"type":"boolean"}],"name":"Shutdown","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"profile.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"boolean"},{"name":"session","required":true,"type":"string"}],"name":"ProfileClear","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.create","parameters":[{"maximumBytes":64,"name":"name","required":true,"sensitive":false,"type":"string"},{"name":"isolated","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"session","required":true,"type":"string"},{"name":"isolated","required":true,"type":"boolean"}],"name":"SessionCreate","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"sessions","required":true,"type":"array"},{"name":"details","required":true,"type":"array"}],"name":"SessionList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.close","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"closed","required":true,"type":"string"}],"name":"SessionClose","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"visit","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"inspect","parameters":[{"name":"interactive","required":false,"sensitive":false,"type":"boolean"},{"name":"text","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":16,"name":"context","required":false,"sensitive":false,"type":"string","values":["summary","outline","text","actions","full"]},{"maximumBytes":512,"name":"task","required":false,"sensitive":false,"type":"string"},{"maximumBytes":16,"name":"within","required":false,"sensitive":false,"type":"string"},{"maximum":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximum":16000,"minimum":256,"name":"budget","required":false,"sensitive":false,"type":"integer"},{"maximum":8,"minimum":0,"name":"depth","required":false,"sensitive":false,"type":"integer"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"contextMode","required":true,"type":"string"},{"name":"viewport","required":true,"type":"object"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"elements","required":false,"type":"array"},{"name":"regions","required":false,"type":"array"},{"name":"snippets","required":false,"type":"array"},{"name":"text","required":false,"type":"string"}],"name":"Inspection","type":"object"}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"click","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"clicked","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"}],"name":"Click","type":"object"}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"fill","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":900000,"name":"value","required":true,"sensitive":true,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"filled","required":true,"type":"string"},{"name":"valueLength","required":true,"type":"number"}],"name":"Fill","type":"object"}}},{"capabilityNegotiated":true,"constraints":["artifact must be an existing private-store basename","exactly one target reference or semantic role/name target"],"name":"upload","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"artifact","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"uploaded","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"artifact","required":true,"type":"string"}],"name":"Upload","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"press","parameters":[{"maximumBytes":32,"name":"key","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"pressed","required":true,"type":"string"}],"name":"Press","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"scroll","parameters":[{"maximumBytes":8192,"name":"direction","required":false,"sensitive":false,"type":"string","values":["up","down","top","bottom"]},{"maximum":100000,"minimum":0.1,"name":"amount","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"direction","required":true,"type":"string"},{"name":"amount","required":true,"type":"number"}],"name":"Scroll","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"back","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"reload","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"wait","parameters":[{"name":"settled","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":8192,"name":"url","required":false,"sensitive":false,"type":"string"},{"maximumBytes":30000,"name":"text","required":false,"sensitive":false,"type":"string"},{"maximum":120000,"minimum":100,"name":"timeoutMs","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"tour","parameters":[{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximum":5000,"minimum":100,"name":"pace","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"start","required":true,"type":"number"},{"name":"end","required":true,"type":"number"},{"name":"durationMs","required":true,"type":"number"}],"name":"Tour","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"capture.info","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"engine","required":true,"type":"string"},{"name":"page","required":true,"type":"object"},{"name":"trace","required":true,"type":"array"},{"name":"recording","required":true,"type":"object"}],"name":"CaptureInfo","type":"object"}}},{"capabilityNegotiated":true,"constraints":["target, full-page, and series modes are mutually constrained","PDF requires full-page mode and no clipboard"],"name":"screenshot","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximumBytes":32,"name":"series","required":false,"sensitive":false,"type":"string","values":["viewport","section"]},{"maximumBytes":80,"name":"outputPrefix","required":false,"sensitive":false,"type":"string"},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["png","jpg","jpeg","pdf"]},{"name":"clipboard","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":false,"type":"string"},{"name":"path","required":false,"type":"string"},{"name":"kind","required":false,"type":"string"},{"name":"bytes","required":false,"type":"number"},{"name":"createdAt","required":false,"type":"number"},{"name":"artifacts","required":false,"type":"array"},{"name":"truncated","required":false,"type":"boolean"}],"name":"Screenshot","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"artifact.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"directory","required":true,"type":"string"},{"name":"artifacts","required":true,"type":"array"},{"name":"total","required":true,"type":"number"},{"name":"omitted","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"ArtifactList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.start","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximum":30,"minimum":1,"name":"fps","required":false,"sensitive":false,"type":"number"},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["mp4","mov","webm","gif"]},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"quality","required":false,"sensitive":false,"type":"string","values":["fast","balanced","high"]}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.status","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.report","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"summary","required":true,"type":"object"},{"name":"issues","required":true,"type":"array"},{"name":"events","required":true,"type":"array"},{"name":"omitted","required":true,"type":"object"},{"name":"truncated","required":true,"type":"boolean"}],"name":"QAReport","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"QAClear","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"console.list","parameters":[{"maximumBytes":16,"name":"level","required":false,"sensitive":false,"type":"string","values":["all","log","info","debug","warn","error","assert"]},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"messages","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"ConsoleList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.list","parameters":[{"name":"failed","required":false,"sensitive":false,"type":"boolean"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"requests","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"NetworkList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.get","parameters":[{"maximumBytes":128,"name":"requestId","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"found","required":true,"type":"boolean"},{"name":"requestId","required":false,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"request","required":false,"type":"object"}],"name":"NetworkDetail","type":"object"}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"styles.get","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"itemMaximumBytes":128,"maximumItems":64,"name":"properties","required":false,"sensitive":false,"type":"string-array"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"ref","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"box","required":true,"type":"object"},{"name":"styles","required":true,"type":"object"}],"name":"Styles","type":"object"}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"cookies.list","parameters":[{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"cookies","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"CookieList","type":"object"}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"storage.list","parameters":[{"maximumBytes":16,"name":"scope","required":false,"sensitive":false,"type":"string","values":["local","session","all"]},{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"stores","required":true,"type":"array"}],"name":"StorageList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"visual.compare","parameters":[{"maximumBytes":128,"name":"before","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"after","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"changedPixels","required":false,"type":"number"},{"name":"differenceRatio","required":false,"type":"number"}],"name":"VisualComparison","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"performance.get","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"timing","required":true,"type":"json"},{"name":"webVitals","required":true,"type":"object"},{"name":"resources","required":true,"type":"object"}],"name":"Performance","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"animation.list","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"count","required":true,"type":"number"},{"name":"animations","required":true,"type":"array"},{"name":"truncated","required":true,"type":"boolean"}],"name":"AnimationList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"report.create","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.start","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"recording","required":true,"type":"boolean"},{"name":"note","required":true,"type":"string"}],"name":"FlowStart","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.run","parameters":[{"maximumBytes":128,"name":"input","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"completed","required":true,"type":"number"},{"name":"input","required":true,"type":"string"}],"name":"FlowRun","type":"object"}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.emulate","parameters":[{"name":"offline","required":false,"sensitive":false,"type":"boolean"},{"maximum":120000,"minimum":0,"name":"latencyMs","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"downloadKbps","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"uploadKbps","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"offline","required":true,"type":"boolean"},{"name":"latencyMs","required":true,"type":"number"},{"name":"downloadKbps","required":true,"type":"number"},{"name":"uploadKbps","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"}],"name":"NetworkEmulation","type":"object"}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.set","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximumBytes":65536,"name":"body","required":true,"sensitive":false,"type":"string"},{"maximumBytes":256,"name":"contentType","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"status","required":true,"type":"number"},{"name":"activeMocks","required":true,"type":"number"}],"name":"NetworkMock","type":"object"}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"NetworkMockClear","type":"object"}}},{"capabilityNegotiated":true,"constraints":["choose interactive login or account alias","saved login requires a valid single-use challenge"],"name":"auth.login","parameters":[{"maximumBytes":64,"name":"challenge","required":false,"sensitive":false,"type":"string"},{"maximumBytes":64,"name":"account","required":false,"sensitive":false,"type":"string"},{"name":"interactive","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"account","required":true,"type":"string-or-null"},{"name":"saved","required":true,"type":"boolean"},{"name":"continuation","required":true,"type":"string"},{"name":"passwordExposed","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationLogin","type":"object"}}}],"compatibility":{"mismatch":"host rejects request or client rejects response before decoding result","olderClient":"may call only commands and parameters declared by its bundled schema","olderHost":"missing commands or capabilities fail explicitly; clients must not emulate them","policy":"exact-wire-version","productVersionIndependent":true},"errorDetails":{"AUTH_REQUIRED":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"challenge","required":true,"type":"string"},{"name":"origin","required":true,"type":"string"},{"name":"detection","required":true,"type":"string"},{"name":"accounts","required":true,"type":"array"},{"name":"expiresInSeconds","required":true,"type":"number"},{"name":"userPresenceRequired","required":true,"type":"boolean"},{"name":"credentialUseAvailable","required":true,"type":"boolean"},{"name":"vaultAvailable","required":true,"type":"boolean"},{"name":"vaultStatus","required":true,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationRequired","type":"object"}}},"format":"headless-sdk-contract","hostLifecycle":{"connect":"attach to an existing host and never assume ownership","existingHost":"supervised start fails rather than attaching","ownerExit":"host shuts down when the owner pipe closes","sharedStart":"headless start --background","shutdown":"only explicit user requests may stop a shared host","supervisedOwnerChannel":"open standard-input pipe","supervisedStart":"headless start --background --supervised"},"jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","localCommands":["capabilities","config.describe","config.get","config.list","config.reset","config.set","credentials.add","credentials.list","credentials.remove","credentials.rename","help","runtime","schema","start","version"],"localErrorCodes":["CONFIGURATION_FAILED","HOST_START_FAILED","HOST_UNAVAILABLE","INVALID_CONFIGURATION","NAVIGATION_ALLOWLIST_CONFLICT","UNSUPPORTED_BROWSER_RUNTIME","UNSUPPORTED_CAPABILITY","VAULT_UNAVAILABLE"],"localLifecycle":{"connect":{"errors":["HOST_UNAVAILABLE"],"ownership":"shared","transport":"local-unix-socket"},"launch":{"argv":["start","--background","--supervised"],"command":"start","errors":["HOST_START_FAILED","NAVIGATION_ALLOWLIST_CONFLICT","UNSUPPORTED_BROWSER_RUNTIME","UNSUPPORTED_CAPABILITY"],"options":[{"name":"presentation","required":false,"type":"string","values":["background","foreground"]},{"itemMaximumBytes":300,"maximumItems":32,"name":"allow","required":false,"type":"string-array"},{"const":true,"name":"supervised","required":true,"type":"boolean"}],"ownership":"owned-only-after-response-pid-matches-launched-child","result":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}}},"maximumMessageBytes":1048576,"protocolVersion":"0.5","releasePolicy":{"preOneDeprecation":"at least one minor SDK release","provenance":"publish from reviewed tags with package attestations","schemaVersioning":"integer; generators reject unknown versions","sdkVersioning":"semantic versions independent of product and wire versions","securityReporting":"SECURITY.md; never include secrets or private artifacts","stableRemoval":"major SDK release and migration notes"},"request":{"additionalProperties":false,"id":{"maximumBytes":128,"minimumBytes":1,"type":"string"},"parameters":{"additionalProperties":false,"type":"object"},"required":["id","version","command","parameters"],"session":{"maximumBytes":64,"type":["string","null"]},"type":"object","version":{"const":"0.5","type":"string"}},"response":{"additionalProperties":true,"failure":{"error":{"codes":["ARTIFACT_ERROR","AUTH_ACCOUNT_NOT_FOUND","AUTH_CHALLENGE_CONSUMED","AUTH_CHALLENGE_EXPIRED","AUTH_CHALLENGE_NOT_FOUND","AUTH_FORM_CHANGED","AUTH_ORIGIN_CHANGED","AUTH_REQUIRED","CREDENTIAL_ALIAS_EXISTS","ELEMENT_NOT_FOUND","FLOW_FAILED","HOST_STOPPING","HOST_UNAVAILABLE","INTERNAL_ERROR","INVALID_CAPTURE_FORMAT","INVALID_COMMAND","INVALID_FLOW","INVALID_INPUT","INVALID_REQUEST","INVALID_SESSION","MISSING_PARAMETER","OPERATION_FAILED","PEER_DENIED","RECORDER_UNAVAILABLE","RECORDING_ACTIVE","RECORDING_FAILED","RECORDING_NOT_ACTIVE","REGION_NOT_FOUND","RESPONSE_TOO_LARGE","SENSITIVE_DIAGNOSTICS_DISABLED","SESSION_EXISTS","SESSION_NOT_FOUND","TIMEOUT","UNSAFE_NAVIGATION","UNSAFE_RESOURCE_TYPE","UNSUPPORTED_CAPABILITY","USER_PRESENCE_DENIED","USER_PRESENCE_UNAVAILABLE","VAULT_LOCKED","VAULT_OPERATION_FAILED","VAULT_RESPONSE_INVALID","VAULT_UNAVAILABLE"],"optional":["suggestion","details"],"required":["code","message"]},"ok":false,"result":null},"required":["id","version","ok"],"success":{"error":null,"ok":true,"result":"json"},"type":"object"},"schemaVersion":1,"security":{"arbitraryJavaScript":false,"credentialParameters":"aliases-and-challenge-identifiers-only","pageData":"untrusted","sensitiveDiagnostics":"requires-command-flag-and-host-environment-gate","tcpListener":false,"unknownRequestFields":"rejected","unknownResponseFields":"accepted for compatible additive evolution"},"supportWindow":{"hostPlatforms":["macOS 13 or newer","Linux amd64","Linux arm64"],"maintenance":"latest two minor SDK lines and at least 12 months after supersession, whichever is longer","pythonRuntimes":["CPython 3.11 through 3.14"],"schemaVersions":[1],"securityFixes":"supported SDK lines receive applicable security fixes","typescriptRuntimes":["Node.js 22 or newer"],"wireVersions":["0.5"]},"timeouts":{"connect":"fails before a request is sent","requestAfterWrite":"outcome unknown; never retry automatically","requestBeforeWrite":"safe to retry with a new request identifier","startup":"terminate and reap only the supervised launcher and host"},"transport":{"framing":"one-newline-terminated-json-document-per-connection","kind":"local-unix-socket","peerAuthorization":"same-os-user","runtimeDirectoryMode":"0700","socketMode":"0600","tcp":false}} From 6d8d5918babcdb29555c5a097280aa5057e19761 Mon Sep 17 00:00:00 2001 From: SarthakWade Date: Sat, 12 Sep 2026 23:56:23 +0530 Subject: [PATCH 3/5] fix(sdk): generate command policies --- .../Sources/HeadlessProtocol/CLI.swift | 14 +-- .../HeadlessProtocol/ProtocolSchema.swift | 100 ++++++++++++++++++ .../HeadlessProtocolTests/ProtocolTests.swift | 24 +++++ apps/headless/docs/P4.md | 9 +- docs/roadmap/architecture-decisions.md | 3 +- sdk/README.md | 4 + sdk/protocol-schema.json | 2 +- 7 files changed, 141 insertions(+), 15 deletions(-) diff --git a/apps/headless/Sources/HeadlessProtocol/CLI.swift b/apps/headless/Sources/HeadlessProtocol/CLI.swift index 8d7d572..d550e37 100644 --- a/apps/headless/Sources/HeadlessProtocol/CLI.swift +++ b/apps/headless/Sources/HeadlessProtocol/CLI.swift @@ -40,15 +40,11 @@ public struct CLIInvocation: Equatable, Sendable { } public func requestTimeout(for request: CommandRequest) -> TimeInterval { - if let milliseconds = request.parameters["timeoutMs"]?.numberValue { - return min(125, max(10, milliseconds / 1_000 + 5)) - } - if request.command == .tour || request.command == .flowRun { return 125 } - if request.command == .recordStop { return 30 } - if request.command == .screenshot { - return request.parameters["series"]?.stringValue == nil ? 30 : 125 - } - return 15 + TimeInterval( + protocolCommandDefinition(for: request.command).timeout.milliseconds( + for: request.parameters + ) + ) / 1_000 } public enum CLIParseError: Error, Equatable, CustomStringConvertible { diff --git a/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift b/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift index 8cff324..38bde7e 100644 --- a/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift +++ b/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift @@ -20,6 +20,100 @@ public enum ProtocolResultValueKind: String, Sendable { case json } +public enum ProtocolCommandScope: String, Sendable { + case host + case session +} + +public struct ProtocolTimeoutPolicy: Sendable { + public let defaultMilliseconds: Int + public let parameterName: String? + public let parameterGraceMilliseconds: Int? + public let minimumMilliseconds: Int? + public let maximumMilliseconds: Int? + public let parameterPresentOverrides: [String: Int] + + public func milliseconds(for parameters: [String: JSONValue]) -> Int { + if let parameterName, + let value = parameters[parameterName]?.numberValue, + let grace = parameterGraceMilliseconds, + let minimum = minimumMilliseconds, + let maximum = maximumMilliseconds { + return min(maximum, max(minimum, Int(value.rounded(.up)) + grace)) + } + for name in parameterPresentOverrides.keys.sorted() + where parameters[name] != nil { + return parameterPresentOverrides[name] ?? defaultMilliseconds + } + return defaultMilliseconds + } + + fileprivate var document: JSONValue { + var fields: [String: JSONValue] = [ + "defaultMilliseconds": .number(Double(defaultMilliseconds)), + "parameterPresentOverrides": .object( + parameterPresentOverrides.mapValues { .number(Double($0)) } + ), + ] + if let parameterName { fields["parameterName"] = .string(parameterName) } + if let parameterGraceMilliseconds { + fields["parameterGraceMilliseconds"] = .number(Double(parameterGraceMilliseconds)) + } + if let minimumMilliseconds { + fields["minimumMilliseconds"] = .number(Double(minimumMilliseconds)) + } + if let maximumMilliseconds { + fields["maximumMilliseconds"] = .number(Double(maximumMilliseconds)) + } + return .object(fields) + } +} + +private func commandScope(for command: CommandName) -> ProtocolCommandScope { + switch command { + case .ping, .shutdown, .profileClear, .sessionCreate, .sessionList, .artifactList: + return .host + default: + return .session + } +} + +private func timeoutPolicy(for command: CommandName) -> ProtocolTimeoutPolicy { + switch command { + case .wait: + return ProtocolTimeoutPolicy( + defaultMilliseconds: 15_000, + parameterName: "timeoutMs", parameterGraceMilliseconds: 5_000, + minimumMilliseconds: 10_000, maximumMilliseconds: 125_000, + parameterPresentOverrides: [:] + ) + case .tour, .flowRun: + return ProtocolTimeoutPolicy( + defaultMilliseconds: 125_000, parameterName: nil, + parameterGraceMilliseconds: nil, minimumMilliseconds: nil, + maximumMilliseconds: nil, parameterPresentOverrides: [:] + ) + case .recordStop: + return ProtocolTimeoutPolicy( + defaultMilliseconds: 30_000, parameterName: nil, + parameterGraceMilliseconds: nil, minimumMilliseconds: nil, + maximumMilliseconds: nil, parameterPresentOverrides: [:] + ) + case .screenshot: + return ProtocolTimeoutPolicy( + defaultMilliseconds: 30_000, parameterName: nil, + parameterGraceMilliseconds: nil, minimumMilliseconds: nil, + maximumMilliseconds: nil, parameterPresentOverrides: ["series": 125_000] + ) + default: + return ProtocolTimeoutPolicy( + defaultMilliseconds: 15_000, parameterName: nil, + parameterGraceMilliseconds: nil, minimumMilliseconds: nil, + maximumMilliseconds: nil, parameterPresentOverrides: [:] + ) + } +} + public enum AuthenticationProtocolErrorCode: String, CaseIterable, Sendable { case challengeNotFound = "AUTH_CHALLENGE_NOT_FOUND" case challengeExpired = "AUTH_CHALLENGE_EXPIRED" @@ -391,6 +485,8 @@ public struct ProtocolParameterDefinition: Sendable { public struct ProtocolCommandDefinition: Sendable { public let command: CommandName + public let scope: ProtocolCommandScope + public let timeout: ProtocolTimeoutPolicy public let parameters: [ProtocolParameterDefinition] public let capabilityNegotiated: Bool public let resultContainsUntrustedContent: Bool @@ -404,6 +500,8 @@ public struct ProtocolCommandDefinition: Sendable { constraints: [String] = [] ) { self.command = command + self.scope = commandScope(for: command) + self.timeout = timeoutPolicy(for: command) self.parameters = parameters self.capabilityNegotiated = capabilityNegotiated self.resultContainsUntrustedContent = resultContainsUntrustedContent @@ -425,6 +523,8 @@ public struct ProtocolCommandDefinition: Sendable { fileprivate var document: JSONValue { .object([ "name": .string(command.rawValue), + "scope": .string(scope.rawValue), + "timeout": timeout.document, "parameters": .array(parameters.map(\.document)), "capabilityNegotiated": .bool(capabilityNegotiated), "result": .object([ diff --git a/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift b/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift index 5ce25e1..c9e09d0 100644 --- a/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift +++ b/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift @@ -2570,7 +2570,31 @@ struct ProtocolTests { !result.name.isEmpty && Set(result.fields.map(\.name)).count == result.fields.count, "the SDK schema must define one coherent result shape per command" ) + try expect( + definition.timeout.defaultMilliseconds > 0, + "every SDK command must declare a positive transport timeout" + ) } + let hostCommands: Set = [ + .ping, .shutdown, .profileClear, .sessionCreate, .sessionList, .artifactList, + ] + try expect( + Set(protocolCommandDefinitions.values.filter { $0.scope == .host }.map(\.command)) + == hostCommands, + "SDK host and session command scopes must match HostCore dispatch" + ) + try expect( + protocolCommandDefinition(for: .wait).timeout.milliseconds( + for: ["timeoutMs": .number(90_000)] + ) == 95_000, + "SDK timeout metadata must preserve parameter-based wait deadlines" + ) + try expect( + protocolCommandDefinition(for: .screenshot).timeout.milliseconds( + for: ["series": .string("viewport")] + ) == 125_000, + "SDK timeout metadata must preserve screenshot series deadlines" + ) try expect( protocolCommandDefinition(for: .visit).resultContainsUntrustedContent && protocolCommandDefinition(for: .captureInfo).resultContainsUntrustedContent, diff --git a/apps/headless/docs/P4.md b/apps/headless/docs/P4.md index 90607fc..63320d9 100644 --- a/apps/headless/docs/P4.md +++ b/apps/headless/docs/P4.md @@ -32,10 +32,11 @@ implied by this upgrade and remain future command-design work. `headless schema` emits the versioned, machine-readable SDK contract without starting or contacting a browser host. Parameter names, primitive types, -required fields, byte and numeric bounds, enum values, and array limits come -from Swift definitions that also run before the command-specific protocol -validators. `sdk/protocol-schema.json` is a checked-in golden artifact; the -protocol suite fails if it drifts from the executable output. +required fields, byte and numeric bounds, enum values, array limits, command +scope, and timeout policy come from Swift definitions that also run before the +command-specific protocol validators. `sdk/protocol-schema.json` is a checked-in +golden artifact; the protocol suite fails if it drifts from the executable +output. The contract retains the local Unix socket and exact wire-version policy. It does not add TCP, remote control, arbitrary JavaScript, raw credential fields, diff --git a/docs/roadmap/architecture-decisions.md b/docs/roadmap/architecture-decisions.md index 6d6c35e..5aef920 100644 --- a/docs/roadmap/architecture-decisions.md +++ b/docs/roadmap/architecture-decisions.md @@ -817,7 +817,8 @@ rule that durable saved-credential retrieval needs trusted per-use presence. **Decision:** SDK generation starts from a deterministic machine-readable contract emitted by `headless schema`. The Swift descriptors define every wire command's parameter names, primitive types, required fields, byte and numeric -bounds, enum values, and array limits, and those descriptors execute before +bounds, enum values, array limits, API scope, and transport timeout policy. +Those descriptors execute before command-specific semantic validation. `sdk/protocol-schema.json` is a golden artifact checked against the executable in tests, not a second hand-maintained validator. diff --git a/sdk/README.md b/sdk/README.md index ccbcd84..b33887a 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -19,6 +19,10 @@ parameters present in that schema. A newer client must reject an older host before decoding a result for an unsupported wire version, and must use the host capability document before issuing an engine-specific command. +Each command declares whether it is host-scoped or session-scoped and carries +the timeout policy used by the CLI and MCP adapter. Generated clients consume +those fields rather than maintaining their own command lists or deadlines. + Compatible additions retain the current wire version. Removing or changing a field, command, constraint, error meaning, framing rule, or security guarantee requires a protocol-version change and migration notes. The schema format has diff --git a/sdk/protocol-schema.json b/sdk/protocol-schema.json index c986c8a..e0eea30 100644 --- a/sdk/protocol-schema.json +++ b/sdk/protocol-schema.json @@ -1 +1 @@ -{"cancellation":{"afterSend":"close the client connection; command outcome is unknown until state is inspected","beforeSend":"request is not written","ownedHost":"terminate, await, and reap only a host process launched by that SDK instance","sharedHost":"never stop the host implicitly"},"commands":[{"capabilityNegotiated":false,"constraints":[],"name":"ping","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"shutdown","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"stopping","required":true,"type":"boolean"}],"name":"Shutdown","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"profile.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"boolean"},{"name":"session","required":true,"type":"string"}],"name":"ProfileClear","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.create","parameters":[{"maximumBytes":64,"name":"name","required":true,"sensitive":false,"type":"string"},{"name":"isolated","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"session","required":true,"type":"string"},{"name":"isolated","required":true,"type":"boolean"}],"name":"SessionCreate","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"sessions","required":true,"type":"array"},{"name":"details","required":true,"type":"array"}],"name":"SessionList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.close","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"closed","required":true,"type":"string"}],"name":"SessionClose","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"visit","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"inspect","parameters":[{"name":"interactive","required":false,"sensitive":false,"type":"boolean"},{"name":"text","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":16,"name":"context","required":false,"sensitive":false,"type":"string","values":["summary","outline","text","actions","full"]},{"maximumBytes":512,"name":"task","required":false,"sensitive":false,"type":"string"},{"maximumBytes":16,"name":"within","required":false,"sensitive":false,"type":"string"},{"maximum":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximum":16000,"minimum":256,"name":"budget","required":false,"sensitive":false,"type":"integer"},{"maximum":8,"minimum":0,"name":"depth","required":false,"sensitive":false,"type":"integer"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"contextMode","required":true,"type":"string"},{"name":"viewport","required":true,"type":"object"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"elements","required":false,"type":"array"},{"name":"regions","required":false,"type":"array"},{"name":"snippets","required":false,"type":"array"},{"name":"text","required":false,"type":"string"}],"name":"Inspection","type":"object"}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"click","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"clicked","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"}],"name":"Click","type":"object"}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"fill","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":900000,"name":"value","required":true,"sensitive":true,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"filled","required":true,"type":"string"},{"name":"valueLength","required":true,"type":"number"}],"name":"Fill","type":"object"}}},{"capabilityNegotiated":true,"constraints":["artifact must be an existing private-store basename","exactly one target reference or semantic role/name target"],"name":"upload","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"artifact","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"uploaded","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"artifact","required":true,"type":"string"}],"name":"Upload","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"press","parameters":[{"maximumBytes":32,"name":"key","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"pressed","required":true,"type":"string"}],"name":"Press","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"scroll","parameters":[{"maximumBytes":8192,"name":"direction","required":false,"sensitive":false,"type":"string","values":["up","down","top","bottom"]},{"maximum":100000,"minimum":0.1,"name":"amount","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"direction","required":true,"type":"string"},{"name":"amount","required":true,"type":"number"}],"name":"Scroll","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"back","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"reload","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"wait","parameters":[{"name":"settled","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":8192,"name":"url","required":false,"sensitive":false,"type":"string"},{"maximumBytes":30000,"name":"text","required":false,"sensitive":false,"type":"string"},{"maximum":120000,"minimum":100,"name":"timeoutMs","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"tour","parameters":[{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximum":5000,"minimum":100,"name":"pace","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"start","required":true,"type":"number"},{"name":"end","required":true,"type":"number"},{"name":"durationMs","required":true,"type":"number"}],"name":"Tour","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"capture.info","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"engine","required":true,"type":"string"},{"name":"page","required":true,"type":"object"},{"name":"trace","required":true,"type":"array"},{"name":"recording","required":true,"type":"object"}],"name":"CaptureInfo","type":"object"}}},{"capabilityNegotiated":true,"constraints":["target, full-page, and series modes are mutually constrained","PDF requires full-page mode and no clipboard"],"name":"screenshot","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximumBytes":32,"name":"series","required":false,"sensitive":false,"type":"string","values":["viewport","section"]},{"maximumBytes":80,"name":"outputPrefix","required":false,"sensitive":false,"type":"string"},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["png","jpg","jpeg","pdf"]},{"name":"clipboard","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":false,"type":"string"},{"name":"path","required":false,"type":"string"},{"name":"kind","required":false,"type":"string"},{"name":"bytes","required":false,"type":"number"},{"name":"createdAt","required":false,"type":"number"},{"name":"artifacts","required":false,"type":"array"},{"name":"truncated","required":false,"type":"boolean"}],"name":"Screenshot","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"artifact.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"directory","required":true,"type":"string"},{"name":"artifacts","required":true,"type":"array"},{"name":"total","required":true,"type":"number"},{"name":"omitted","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"ArtifactList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.start","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximum":30,"minimum":1,"name":"fps","required":false,"sensitive":false,"type":"number"},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["mp4","mov","webm","gif"]},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"quality","required":false,"sensitive":false,"type":"string","values":["fast","balanced","high"]}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.status","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.report","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"summary","required":true,"type":"object"},{"name":"issues","required":true,"type":"array"},{"name":"events","required":true,"type":"array"},{"name":"omitted","required":true,"type":"object"},{"name":"truncated","required":true,"type":"boolean"}],"name":"QAReport","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"QAClear","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"console.list","parameters":[{"maximumBytes":16,"name":"level","required":false,"sensitive":false,"type":"string","values":["all","log","info","debug","warn","error","assert"]},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"messages","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"ConsoleList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.list","parameters":[{"name":"failed","required":false,"sensitive":false,"type":"boolean"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"requests","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"NetworkList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.get","parameters":[{"maximumBytes":128,"name":"requestId","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"found","required":true,"type":"boolean"},{"name":"requestId","required":false,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"request","required":false,"type":"object"}],"name":"NetworkDetail","type":"object"}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"styles.get","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"itemMaximumBytes":128,"maximumItems":64,"name":"properties","required":false,"sensitive":false,"type":"string-array"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"ref","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"box","required":true,"type":"object"},{"name":"styles","required":true,"type":"object"}],"name":"Styles","type":"object"}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"cookies.list","parameters":[{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"cookies","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"CookieList","type":"object"}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"storage.list","parameters":[{"maximumBytes":16,"name":"scope","required":false,"sensitive":false,"type":"string","values":["local","session","all"]},{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"stores","required":true,"type":"array"}],"name":"StorageList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"visual.compare","parameters":[{"maximumBytes":128,"name":"before","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"after","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"changedPixels","required":false,"type":"number"},{"name":"differenceRatio","required":false,"type":"number"}],"name":"VisualComparison","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"performance.get","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"timing","required":true,"type":"json"},{"name":"webVitals","required":true,"type":"object"},{"name":"resources","required":true,"type":"object"}],"name":"Performance","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"animation.list","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"count","required":true,"type":"number"},{"name":"animations","required":true,"type":"array"},{"name":"truncated","required":true,"type":"boolean"}],"name":"AnimationList","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"report.create","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.start","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"recording","required":true,"type":"boolean"},{"name":"note","required":true,"type":"string"}],"name":"FlowStart","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.run","parameters":[{"maximumBytes":128,"name":"input","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"completed","required":true,"type":"number"},{"name":"input","required":true,"type":"string"}],"name":"FlowRun","type":"object"}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.emulate","parameters":[{"name":"offline","required":false,"sensitive":false,"type":"boolean"},{"maximum":120000,"minimum":0,"name":"latencyMs","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"downloadKbps","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"uploadKbps","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"offline","required":true,"type":"boolean"},{"name":"latencyMs","required":true,"type":"number"},{"name":"downloadKbps","required":true,"type":"number"},{"name":"uploadKbps","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"}],"name":"NetworkEmulation","type":"object"}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.set","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximumBytes":65536,"name":"body","required":true,"sensitive":false,"type":"string"},{"maximumBytes":256,"name":"contentType","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"status","required":true,"type":"number"},{"name":"activeMocks","required":true,"type":"number"}],"name":"NetworkMock","type":"object"}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"NetworkMockClear","type":"object"}}},{"capabilityNegotiated":true,"constraints":["choose interactive login or account alias","saved login requires a valid single-use challenge"],"name":"auth.login","parameters":[{"maximumBytes":64,"name":"challenge","required":false,"sensitive":false,"type":"string"},{"maximumBytes":64,"name":"account","required":false,"sensitive":false,"type":"string"},{"name":"interactive","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"account","required":true,"type":"string-or-null"},{"name":"saved","required":true,"type":"boolean"},{"name":"continuation","required":true,"type":"string"},{"name":"passwordExposed","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationLogin","type":"object"}}}],"compatibility":{"mismatch":"host rejects request or client rejects response before decoding result","olderClient":"may call only commands and parameters declared by its bundled schema","olderHost":"missing commands or capabilities fail explicitly; clients must not emulate them","policy":"exact-wire-version","productVersionIndependent":true},"errorDetails":{"AUTH_REQUIRED":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"challenge","required":true,"type":"string"},{"name":"origin","required":true,"type":"string"},{"name":"detection","required":true,"type":"string"},{"name":"accounts","required":true,"type":"array"},{"name":"expiresInSeconds","required":true,"type":"number"},{"name":"userPresenceRequired","required":true,"type":"boolean"},{"name":"credentialUseAvailable","required":true,"type":"boolean"},{"name":"vaultAvailable","required":true,"type":"boolean"},{"name":"vaultStatus","required":true,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationRequired","type":"object"}}},"format":"headless-sdk-contract","hostLifecycle":{"connect":"attach to an existing host and never assume ownership","existingHost":"supervised start fails rather than attaching","ownerExit":"host shuts down when the owner pipe closes","sharedStart":"headless start --background","shutdown":"only explicit user requests may stop a shared host","supervisedOwnerChannel":"open standard-input pipe","supervisedStart":"headless start --background --supervised"},"jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","localCommands":["capabilities","config.describe","config.get","config.list","config.reset","config.set","credentials.add","credentials.list","credentials.remove","credentials.rename","help","runtime","schema","start","version"],"localErrorCodes":["CONFIGURATION_FAILED","HOST_START_FAILED","HOST_UNAVAILABLE","INVALID_CONFIGURATION","NAVIGATION_ALLOWLIST_CONFLICT","UNSUPPORTED_BROWSER_RUNTIME","UNSUPPORTED_CAPABILITY","VAULT_UNAVAILABLE"],"localLifecycle":{"connect":{"errors":["HOST_UNAVAILABLE"],"ownership":"shared","transport":"local-unix-socket"},"launch":{"argv":["start","--background","--supervised"],"command":"start","errors":["HOST_START_FAILED","NAVIGATION_ALLOWLIST_CONFLICT","UNSUPPORTED_BROWSER_RUNTIME","UNSUPPORTED_CAPABILITY"],"options":[{"name":"presentation","required":false,"type":"string","values":["background","foreground"]},{"itemMaximumBytes":300,"maximumItems":32,"name":"allow","required":false,"type":"string-array"},{"const":true,"name":"supervised","required":true,"type":"boolean"}],"ownership":"owned-only-after-response-pid-matches-launched-child","result":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}}},"maximumMessageBytes":1048576,"protocolVersion":"0.5","releasePolicy":{"preOneDeprecation":"at least one minor SDK release","provenance":"publish from reviewed tags with package attestations","schemaVersioning":"integer; generators reject unknown versions","sdkVersioning":"semantic versions independent of product and wire versions","securityReporting":"SECURITY.md; never include secrets or private artifacts","stableRemoval":"major SDK release and migration notes"},"request":{"additionalProperties":false,"id":{"maximumBytes":128,"minimumBytes":1,"type":"string"},"parameters":{"additionalProperties":false,"type":"object"},"required":["id","version","command","parameters"],"session":{"maximumBytes":64,"type":["string","null"]},"type":"object","version":{"const":"0.5","type":"string"}},"response":{"additionalProperties":true,"failure":{"error":{"codes":["ARTIFACT_ERROR","AUTH_ACCOUNT_NOT_FOUND","AUTH_CHALLENGE_CONSUMED","AUTH_CHALLENGE_EXPIRED","AUTH_CHALLENGE_NOT_FOUND","AUTH_FORM_CHANGED","AUTH_ORIGIN_CHANGED","AUTH_REQUIRED","CREDENTIAL_ALIAS_EXISTS","ELEMENT_NOT_FOUND","FLOW_FAILED","HOST_STOPPING","HOST_UNAVAILABLE","INTERNAL_ERROR","INVALID_CAPTURE_FORMAT","INVALID_COMMAND","INVALID_FLOW","INVALID_INPUT","INVALID_REQUEST","INVALID_SESSION","MISSING_PARAMETER","OPERATION_FAILED","PEER_DENIED","RECORDER_UNAVAILABLE","RECORDING_ACTIVE","RECORDING_FAILED","RECORDING_NOT_ACTIVE","REGION_NOT_FOUND","RESPONSE_TOO_LARGE","SENSITIVE_DIAGNOSTICS_DISABLED","SESSION_EXISTS","SESSION_NOT_FOUND","TIMEOUT","UNSAFE_NAVIGATION","UNSAFE_RESOURCE_TYPE","UNSUPPORTED_CAPABILITY","USER_PRESENCE_DENIED","USER_PRESENCE_UNAVAILABLE","VAULT_LOCKED","VAULT_OPERATION_FAILED","VAULT_RESPONSE_INVALID","VAULT_UNAVAILABLE"],"optional":["suggestion","details"],"required":["code","message"]},"ok":false,"result":null},"required":["id","version","ok"],"success":{"error":null,"ok":true,"result":"json"},"type":"object"},"schemaVersion":1,"security":{"arbitraryJavaScript":false,"credentialParameters":"aliases-and-challenge-identifiers-only","pageData":"untrusted","sensitiveDiagnostics":"requires-command-flag-and-host-environment-gate","tcpListener":false,"unknownRequestFields":"rejected","unknownResponseFields":"accepted for compatible additive evolution"},"supportWindow":{"hostPlatforms":["macOS 13 or newer","Linux amd64","Linux arm64"],"maintenance":"latest two minor SDK lines and at least 12 months after supersession, whichever is longer","pythonRuntimes":["CPython 3.11 through 3.14"],"schemaVersions":[1],"securityFixes":"supported SDK lines receive applicable security fixes","typescriptRuntimes":["Node.js 22 or newer"],"wireVersions":["0.5"]},"timeouts":{"connect":"fails before a request is sent","requestAfterWrite":"outcome unknown; never retry automatically","requestBeforeWrite":"safe to retry with a new request identifier","startup":"terminate and reap only the supervised launcher and host"},"transport":{"framing":"one-newline-terminated-json-document-per-connection","kind":"local-unix-socket","peerAuthorization":"same-os-user","runtimeDirectoryMode":"0700","socketMode":"0600","tcp":false}} +{"cancellation":{"afterSend":"close the client connection; command outcome is unknown until state is inspected","beforeSend":"request is not written","ownedHost":"terminate, await, and reap only a host process launched by that SDK instance","sharedHost":"never stop the host implicitly"},"commands":[{"capabilityNegotiated":false,"constraints":[],"name":"ping","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"shutdown","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"stopping","required":true,"type":"boolean"}],"name":"Shutdown","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"profile.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"boolean"},{"name":"session","required":true,"type":"string"}],"name":"ProfileClear","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.create","parameters":[{"maximumBytes":64,"name":"name","required":true,"sensitive":false,"type":"string"},{"name":"isolated","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"session","required":true,"type":"string"},{"name":"isolated","required":true,"type":"boolean"}],"name":"SessionCreate","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"sessions","required":true,"type":"array"},{"name":"details","required":true,"type":"array"}],"name":"SessionList","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.close","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"closed","required":true,"type":"string"}],"name":"SessionClose","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"visit","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"inspect","parameters":[{"name":"interactive","required":false,"sensitive":false,"type":"boolean"},{"name":"text","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":16,"name":"context","required":false,"sensitive":false,"type":"string","values":["summary","outline","text","actions","full"]},{"maximumBytes":512,"name":"task","required":false,"sensitive":false,"type":"string"},{"maximumBytes":16,"name":"within","required":false,"sensitive":false,"type":"string"},{"maximum":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximum":16000,"minimum":256,"name":"budget","required":false,"sensitive":false,"type":"integer"},{"maximum":8,"minimum":0,"name":"depth","required":false,"sensitive":false,"type":"integer"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"contextMode","required":true,"type":"string"},{"name":"viewport","required":true,"type":"object"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"elements","required":false,"type":"array"},{"name":"regions","required":false,"type":"array"},{"name":"snippets","required":false,"type":"array"},{"name":"text","required":false,"type":"string"}],"name":"Inspection","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"click","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"clicked","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"}],"name":"Click","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"fill","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":900000,"name":"value","required":true,"sensitive":true,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"filled","required":true,"type":"string"},{"name":"valueLength","required":true,"type":"number"}],"name":"Fill","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["artifact must be an existing private-store basename","exactly one target reference or semantic role/name target"],"name":"upload","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"artifact","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"uploaded","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"artifact","required":true,"type":"string"}],"name":"Upload","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"press","parameters":[{"maximumBytes":32,"name":"key","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"pressed","required":true,"type":"string"}],"name":"Press","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"scroll","parameters":[{"maximumBytes":8192,"name":"direction","required":false,"sensitive":false,"type":"string","values":["up","down","top","bottom"]},{"maximum":100000,"minimum":0.1,"name":"amount","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"direction","required":true,"type":"string"},{"name":"amount","required":true,"type":"number"}],"name":"Scroll","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"back","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"reload","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"wait","parameters":[{"name":"settled","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":8192,"name":"url","required":false,"sensitive":false,"type":"string"},{"maximumBytes":30000,"name":"text","required":false,"sensitive":false,"type":"string"},{"maximum":120000,"minimum":100,"name":"timeoutMs","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"maximumMilliseconds":125000,"minimumMilliseconds":10000,"parameterGraceMilliseconds":5000,"parameterName":"timeoutMs","parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"tour","parameters":[{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximum":5000,"minimum":100,"name":"pace","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"start","required":true,"type":"number"},{"name":"end","required":true,"type":"number"},{"name":"durationMs","required":true,"type":"number"}],"name":"Tour","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":125000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"capture.info","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"engine","required":true,"type":"string"},{"name":"page","required":true,"type":"object"},{"name":"trace","required":true,"type":"array"},{"name":"recording","required":true,"type":"object"}],"name":"CaptureInfo","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["target, full-page, and series modes are mutually constrained","PDF requires full-page mode and no clipboard"],"name":"screenshot","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximumBytes":32,"name":"series","required":false,"sensitive":false,"type":"string","values":["viewport","section"]},{"maximumBytes":80,"name":"outputPrefix","required":false,"sensitive":false,"type":"string"},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["png","jpg","jpeg","pdf"]},{"name":"clipboard","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":false,"type":"string"},{"name":"path","required":false,"type":"string"},{"name":"kind","required":false,"type":"string"},{"name":"bytes","required":false,"type":"number"},{"name":"createdAt","required":false,"type":"number"},{"name":"artifacts","required":false,"type":"array"},{"name":"truncated","required":false,"type":"boolean"}],"name":"Screenshot","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":30000,"parameterPresentOverrides":{"series":125000}}},{"capabilityNegotiated":false,"constraints":[],"name":"artifact.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"directory","required":true,"type":"string"},{"name":"artifacts","required":true,"type":"array"},{"name":"total","required":true,"type":"number"},{"name":"omitted","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"ArtifactList","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.start","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximum":30,"minimum":1,"name":"fps","required":false,"sensitive":false,"type":"number"},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["mp4","mov","webm","gif"]},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"quality","required":false,"sensitive":false,"type":"string","values":["fast","balanced","high"]}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.status","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":30000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.report","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"summary","required":true,"type":"object"},{"name":"issues","required":true,"type":"array"},{"name":"events","required":true,"type":"array"},{"name":"omitted","required":true,"type":"object"},{"name":"truncated","required":true,"type":"boolean"}],"name":"QAReport","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"QAClear","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"console.list","parameters":[{"maximumBytes":16,"name":"level","required":false,"sensitive":false,"type":"string","values":["all","log","info","debug","warn","error","assert"]},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"messages","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"ConsoleList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.list","parameters":[{"name":"failed","required":false,"sensitive":false,"type":"boolean"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"requests","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"NetworkList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.get","parameters":[{"maximumBytes":128,"name":"requestId","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"found","required":true,"type":"boolean"},{"name":"requestId","required":false,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"request","required":false,"type":"object"}],"name":"NetworkDetail","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"styles.get","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"itemMaximumBytes":128,"maximumItems":64,"name":"properties","required":false,"sensitive":false,"type":"string-array"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"ref","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"box","required":true,"type":"object"},{"name":"styles","required":true,"type":"object"}],"name":"Styles","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"cookies.list","parameters":[{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"cookies","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"CookieList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"storage.list","parameters":[{"maximumBytes":16,"name":"scope","required":false,"sensitive":false,"type":"string","values":["local","session","all"]},{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"stores","required":true,"type":"array"}],"name":"StorageList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"visual.compare","parameters":[{"maximumBytes":128,"name":"before","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"after","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"changedPixels","required":false,"type":"number"},{"name":"differenceRatio","required":false,"type":"number"}],"name":"VisualComparison","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"performance.get","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"timing","required":true,"type":"json"},{"name":"webVitals","required":true,"type":"object"},{"name":"resources","required":true,"type":"object"}],"name":"Performance","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"animation.list","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"count","required":true,"type":"number"},{"name":"animations","required":true,"type":"array"},{"name":"truncated","required":true,"type":"boolean"}],"name":"AnimationList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"report.create","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.start","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"recording","required":true,"type":"boolean"},{"name":"note","required":true,"type":"string"}],"name":"FlowStart","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.run","parameters":[{"maximumBytes":128,"name":"input","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"completed","required":true,"type":"number"},{"name":"input","required":true,"type":"string"}],"name":"FlowRun","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":125000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.emulate","parameters":[{"name":"offline","required":false,"sensitive":false,"type":"boolean"},{"maximum":120000,"minimum":0,"name":"latencyMs","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"downloadKbps","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"uploadKbps","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"offline","required":true,"type":"boolean"},{"name":"latencyMs","required":true,"type":"number"},{"name":"downloadKbps","required":true,"type":"number"},{"name":"uploadKbps","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"}],"name":"NetworkEmulation","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.set","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximumBytes":65536,"name":"body","required":true,"sensitive":false,"type":"string"},{"maximumBytes":256,"name":"contentType","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"status","required":true,"type":"number"},{"name":"activeMocks","required":true,"type":"number"}],"name":"NetworkMock","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"NetworkMockClear","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["choose interactive login or account alias","saved login requires a valid single-use challenge"],"name":"auth.login","parameters":[{"maximumBytes":64,"name":"challenge","required":false,"sensitive":false,"type":"string"},{"maximumBytes":64,"name":"account","required":false,"sensitive":false,"type":"string"},{"name":"interactive","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"account","required":true,"type":"string-or-null"},{"name":"saved","required":true,"type":"boolean"},{"name":"continuation","required":true,"type":"string"},{"name":"passwordExposed","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationLogin","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}}],"compatibility":{"mismatch":"host rejects request or client rejects response before decoding result","olderClient":"may call only commands and parameters declared by its bundled schema","olderHost":"missing commands or capabilities fail explicitly; clients must not emulate them","policy":"exact-wire-version","productVersionIndependent":true},"errorDetails":{"AUTH_REQUIRED":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"challenge","required":true,"type":"string"},{"name":"origin","required":true,"type":"string"},{"name":"detection","required":true,"type":"string"},{"name":"accounts","required":true,"type":"array"},{"name":"expiresInSeconds","required":true,"type":"number"},{"name":"userPresenceRequired","required":true,"type":"boolean"},{"name":"credentialUseAvailable","required":true,"type":"boolean"},{"name":"vaultAvailable","required":true,"type":"boolean"},{"name":"vaultStatus","required":true,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationRequired","type":"object"}}},"format":"headless-sdk-contract","hostLifecycle":{"connect":"attach to an existing host and never assume ownership","existingHost":"supervised start fails rather than attaching","ownerExit":"host shuts down when the owner pipe closes","sharedStart":"headless start --background","shutdown":"only explicit user requests may stop a shared host","supervisedOwnerChannel":"open standard-input pipe","supervisedStart":"headless start --background --supervised"},"jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","localCommands":["capabilities","config.describe","config.get","config.list","config.reset","config.set","credentials.add","credentials.list","credentials.remove","credentials.rename","help","runtime","schema","start","version"],"localErrorCodes":["CONFIGURATION_FAILED","HOST_START_FAILED","HOST_UNAVAILABLE","INVALID_CONFIGURATION","NAVIGATION_ALLOWLIST_CONFLICT","UNSUPPORTED_BROWSER_RUNTIME","UNSUPPORTED_CAPABILITY","VAULT_UNAVAILABLE"],"localLifecycle":{"connect":{"errors":["HOST_UNAVAILABLE"],"ownership":"shared","transport":"local-unix-socket"},"launch":{"argv":["start","--background","--supervised"],"command":"start","errors":["HOST_START_FAILED","NAVIGATION_ALLOWLIST_CONFLICT","UNSUPPORTED_BROWSER_RUNTIME","UNSUPPORTED_CAPABILITY"],"options":[{"name":"presentation","required":false,"type":"string","values":["background","foreground"]},{"itemMaximumBytes":300,"maximumItems":32,"name":"allow","required":false,"type":"string-array"},{"const":true,"name":"supervised","required":true,"type":"boolean"}],"ownership":"owned-only-after-response-pid-matches-launched-child","result":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}}},"maximumMessageBytes":1048576,"protocolVersion":"0.5","releasePolicy":{"preOneDeprecation":"at least one minor SDK release","provenance":"publish from reviewed tags with package attestations","schemaVersioning":"integer; generators reject unknown versions","sdkVersioning":"semantic versions independent of product and wire versions","securityReporting":"SECURITY.md; never include secrets or private artifacts","stableRemoval":"major SDK release and migration notes"},"request":{"additionalProperties":false,"id":{"maximumBytes":128,"minimumBytes":1,"type":"string"},"parameters":{"additionalProperties":false,"type":"object"},"required":["id","version","command","parameters"],"session":{"maximumBytes":64,"type":["string","null"]},"type":"object","version":{"const":"0.5","type":"string"}},"response":{"additionalProperties":true,"failure":{"error":{"codes":["ARTIFACT_ERROR","AUTH_ACCOUNT_NOT_FOUND","AUTH_CHALLENGE_CONSUMED","AUTH_CHALLENGE_EXPIRED","AUTH_CHALLENGE_NOT_FOUND","AUTH_FORM_CHANGED","AUTH_ORIGIN_CHANGED","AUTH_REQUIRED","CREDENTIAL_ALIAS_EXISTS","ELEMENT_NOT_FOUND","FLOW_FAILED","HOST_STOPPING","HOST_UNAVAILABLE","INTERNAL_ERROR","INVALID_CAPTURE_FORMAT","INVALID_COMMAND","INVALID_FLOW","INVALID_INPUT","INVALID_REQUEST","INVALID_SESSION","MISSING_PARAMETER","OPERATION_FAILED","PEER_DENIED","RECORDER_UNAVAILABLE","RECORDING_ACTIVE","RECORDING_FAILED","RECORDING_NOT_ACTIVE","REGION_NOT_FOUND","RESPONSE_TOO_LARGE","SENSITIVE_DIAGNOSTICS_DISABLED","SESSION_EXISTS","SESSION_NOT_FOUND","TIMEOUT","UNSAFE_NAVIGATION","UNSAFE_RESOURCE_TYPE","UNSUPPORTED_CAPABILITY","USER_PRESENCE_DENIED","USER_PRESENCE_UNAVAILABLE","VAULT_LOCKED","VAULT_OPERATION_FAILED","VAULT_RESPONSE_INVALID","VAULT_UNAVAILABLE"],"optional":["suggestion","details"],"required":["code","message"]},"ok":false,"result":null},"required":["id","version","ok"],"success":{"error":null,"ok":true,"result":"json"},"type":"object"},"schemaVersion":1,"security":{"arbitraryJavaScript":false,"credentialParameters":"aliases-and-challenge-identifiers-only","pageData":"untrusted","sensitiveDiagnostics":"requires-command-flag-and-host-environment-gate","tcpListener":false,"unknownRequestFields":"rejected","unknownResponseFields":"accepted for compatible additive evolution"},"supportWindow":{"hostPlatforms":["macOS 13 or newer","Linux amd64","Linux arm64"],"maintenance":"latest two minor SDK lines and at least 12 months after supersession, whichever is longer","pythonRuntimes":["CPython 3.11 through 3.14"],"schemaVersions":[1],"securityFixes":"supported SDK lines receive applicable security fixes","typescriptRuntimes":["Node.js 22 or newer"],"wireVersions":["0.5"]},"timeouts":{"connect":"fails before a request is sent","requestAfterWrite":"outcome unknown; never retry automatically","requestBeforeWrite":"safe to retry with a new request identifier","startup":"terminate and reap only the supervised launcher and host"},"transport":{"framing":"one-newline-terminated-json-document-per-connection","kind":"local-unix-socket","peerAuthorization":"same-os-user","runtimeDirectoryMode":"0700","socketMode":"0600","tcp":false}} From 993030408d7704c1d252af83d4dbc73d54d62e5e Mon Sep 17 00:00:00 2001 From: SarthakWade Date: Sun, 13 Sep 2026 00:42:33 +0530 Subject: [PATCH 4/5] docs(sdk): align combined package releases --- apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift | 2 +- docs/roadmap/architecture-decisions.md | 6 ++++-- sdk/README.md | 6 ++++-- sdk/protocol-schema.json | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift b/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift index 38bde7e..438c594 100644 --- a/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift +++ b/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift @@ -890,7 +890,7 @@ public let protocolSchemaDocument: JSONValue = { ]), "releasePolicy": .object([ "schemaVersioning": .string("integer; generators reject unknown versions"), - "sdkVersioning": .string("semantic versions independent of product and wire versions"), + "sdkVersioning": .string("semantic versions independent of the wire version; combined product distributors track product tags"), "preOneDeprecation": .string("at least one minor SDK release"), "stableRemoval": .string("major SDK release and migration notes"), "provenance": .string("publish from reviewed tags with package attestations"), diff --git a/docs/roadmap/architecture-decisions.md b/docs/roadmap/architecture-decisions.md index 5aef920..6cd4fb8 100644 --- a/docs/roadmap/architecture-decisions.md +++ b/docs/roadmap/architecture-decisions.md @@ -852,8 +852,10 @@ launcher reap it. SDKs never stop a shared host they discovered. **Consequences:** TypeScript and Python clients generate or validate their public command types from the golden schema and pin its digest. Schema, SDK, shared fixtures, CLI-parser, protocol-validator, and MCP parity are tested -together. SDK package -versions may advance independently while declaring the supported wire version. +together. SDK package versions are independent of the wire version. A package +that also distributes the Headless product tracks product tags so its launcher +can resolve a matching release; standalone SDK packages may version +independently while declaring their supported wire version. Deprecation, support-window, provenance, and security-reporting rules live with the schema so client packages cannot silently invent a different policy. diff --git a/sdk/README.md b/sdk/README.md index b33887a..acd43ed 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -52,8 +52,10 @@ shared. ## Release policy Generated SDKs pin a schema digest and wire version. Schema and client changes -are reviewed together. SDK packages are versioned independently from the -Headless product while declaring their supported wire versions. Before 1.0, +are reviewed together. SDK package versions are independent of the wire +version. A package that also distributes Headless tracks product tags so its +launcher can resolve a matching release; standalone SDK packages may version +independently while declaring their supported wire versions. Before 1.0, deprecations remain for at least one minor SDK release; after 1.0, removals require a major SDK release. Security reports use the repository process in `SECURITY.md` and must not include credentials, cookies, or private artifacts. diff --git a/sdk/protocol-schema.json b/sdk/protocol-schema.json index e0eea30..a4051a6 100644 --- a/sdk/protocol-schema.json +++ b/sdk/protocol-schema.json @@ -1 +1 @@ -{"cancellation":{"afterSend":"close the client connection; command outcome is unknown until state is inspected","beforeSend":"request is not written","ownedHost":"terminate, await, and reap only a host process launched by that SDK instance","sharedHost":"never stop the host implicitly"},"commands":[{"capabilityNegotiated":false,"constraints":[],"name":"ping","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"shutdown","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"stopping","required":true,"type":"boolean"}],"name":"Shutdown","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"profile.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"boolean"},{"name":"session","required":true,"type":"string"}],"name":"ProfileClear","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.create","parameters":[{"maximumBytes":64,"name":"name","required":true,"sensitive":false,"type":"string"},{"name":"isolated","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"session","required":true,"type":"string"},{"name":"isolated","required":true,"type":"boolean"}],"name":"SessionCreate","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"sessions","required":true,"type":"array"},{"name":"details","required":true,"type":"array"}],"name":"SessionList","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.close","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"closed","required":true,"type":"string"}],"name":"SessionClose","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"visit","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"inspect","parameters":[{"name":"interactive","required":false,"sensitive":false,"type":"boolean"},{"name":"text","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":16,"name":"context","required":false,"sensitive":false,"type":"string","values":["summary","outline","text","actions","full"]},{"maximumBytes":512,"name":"task","required":false,"sensitive":false,"type":"string"},{"maximumBytes":16,"name":"within","required":false,"sensitive":false,"type":"string"},{"maximum":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximum":16000,"minimum":256,"name":"budget","required":false,"sensitive":false,"type":"integer"},{"maximum":8,"minimum":0,"name":"depth","required":false,"sensitive":false,"type":"integer"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"contextMode","required":true,"type":"string"},{"name":"viewport","required":true,"type":"object"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"elements","required":false,"type":"array"},{"name":"regions","required":false,"type":"array"},{"name":"snippets","required":false,"type":"array"},{"name":"text","required":false,"type":"string"}],"name":"Inspection","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"click","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"clicked","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"}],"name":"Click","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"fill","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":900000,"name":"value","required":true,"sensitive":true,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"filled","required":true,"type":"string"},{"name":"valueLength","required":true,"type":"number"}],"name":"Fill","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["artifact must be an existing private-store basename","exactly one target reference or semantic role/name target"],"name":"upload","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"artifact","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"uploaded","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"artifact","required":true,"type":"string"}],"name":"Upload","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"press","parameters":[{"maximumBytes":32,"name":"key","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"pressed","required":true,"type":"string"}],"name":"Press","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"scroll","parameters":[{"maximumBytes":8192,"name":"direction","required":false,"sensitive":false,"type":"string","values":["up","down","top","bottom"]},{"maximum":100000,"minimum":0.1,"name":"amount","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"direction","required":true,"type":"string"},{"name":"amount","required":true,"type":"number"}],"name":"Scroll","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"back","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"reload","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"wait","parameters":[{"name":"settled","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":8192,"name":"url","required":false,"sensitive":false,"type":"string"},{"maximumBytes":30000,"name":"text","required":false,"sensitive":false,"type":"string"},{"maximum":120000,"minimum":100,"name":"timeoutMs","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"maximumMilliseconds":125000,"minimumMilliseconds":10000,"parameterGraceMilliseconds":5000,"parameterName":"timeoutMs","parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"tour","parameters":[{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximum":5000,"minimum":100,"name":"pace","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"start","required":true,"type":"number"},{"name":"end","required":true,"type":"number"},{"name":"durationMs","required":true,"type":"number"}],"name":"Tour","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":125000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"capture.info","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"engine","required":true,"type":"string"},{"name":"page","required":true,"type":"object"},{"name":"trace","required":true,"type":"array"},{"name":"recording","required":true,"type":"object"}],"name":"CaptureInfo","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["target, full-page, and series modes are mutually constrained","PDF requires full-page mode and no clipboard"],"name":"screenshot","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximumBytes":32,"name":"series","required":false,"sensitive":false,"type":"string","values":["viewport","section"]},{"maximumBytes":80,"name":"outputPrefix","required":false,"sensitive":false,"type":"string"},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["png","jpg","jpeg","pdf"]},{"name":"clipboard","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":false,"type":"string"},{"name":"path","required":false,"type":"string"},{"name":"kind","required":false,"type":"string"},{"name":"bytes","required":false,"type":"number"},{"name":"createdAt","required":false,"type":"number"},{"name":"artifacts","required":false,"type":"array"},{"name":"truncated","required":false,"type":"boolean"}],"name":"Screenshot","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":30000,"parameterPresentOverrides":{"series":125000}}},{"capabilityNegotiated":false,"constraints":[],"name":"artifact.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"directory","required":true,"type":"string"},{"name":"artifacts","required":true,"type":"array"},{"name":"total","required":true,"type":"number"},{"name":"omitted","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"ArtifactList","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.start","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximum":30,"minimum":1,"name":"fps","required":false,"sensitive":false,"type":"number"},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["mp4","mov","webm","gif"]},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"quality","required":false,"sensitive":false,"type":"string","values":["fast","balanced","high"]}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.status","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":30000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.report","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"summary","required":true,"type":"object"},{"name":"issues","required":true,"type":"array"},{"name":"events","required":true,"type":"array"},{"name":"omitted","required":true,"type":"object"},{"name":"truncated","required":true,"type":"boolean"}],"name":"QAReport","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"QAClear","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"console.list","parameters":[{"maximumBytes":16,"name":"level","required":false,"sensitive":false,"type":"string","values":["all","log","info","debug","warn","error","assert"]},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"messages","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"ConsoleList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.list","parameters":[{"name":"failed","required":false,"sensitive":false,"type":"boolean"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"requests","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"NetworkList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.get","parameters":[{"maximumBytes":128,"name":"requestId","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"found","required":true,"type":"boolean"},{"name":"requestId","required":false,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"request","required":false,"type":"object"}],"name":"NetworkDetail","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"styles.get","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"itemMaximumBytes":128,"maximumItems":64,"name":"properties","required":false,"sensitive":false,"type":"string-array"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"ref","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"box","required":true,"type":"object"},{"name":"styles","required":true,"type":"object"}],"name":"Styles","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"cookies.list","parameters":[{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"cookies","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"CookieList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"storage.list","parameters":[{"maximumBytes":16,"name":"scope","required":false,"sensitive":false,"type":"string","values":["local","session","all"]},{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"stores","required":true,"type":"array"}],"name":"StorageList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"visual.compare","parameters":[{"maximumBytes":128,"name":"before","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"after","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"changedPixels","required":false,"type":"number"},{"name":"differenceRatio","required":false,"type":"number"}],"name":"VisualComparison","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"performance.get","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"timing","required":true,"type":"json"},{"name":"webVitals","required":true,"type":"object"},{"name":"resources","required":true,"type":"object"}],"name":"Performance","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"animation.list","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"count","required":true,"type":"number"},{"name":"animations","required":true,"type":"array"},{"name":"truncated","required":true,"type":"boolean"}],"name":"AnimationList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"report.create","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.start","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"recording","required":true,"type":"boolean"},{"name":"note","required":true,"type":"string"}],"name":"FlowStart","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.run","parameters":[{"maximumBytes":128,"name":"input","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"completed","required":true,"type":"number"},{"name":"input","required":true,"type":"string"}],"name":"FlowRun","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":125000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.emulate","parameters":[{"name":"offline","required":false,"sensitive":false,"type":"boolean"},{"maximum":120000,"minimum":0,"name":"latencyMs","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"downloadKbps","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"uploadKbps","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"offline","required":true,"type":"boolean"},{"name":"latencyMs","required":true,"type":"number"},{"name":"downloadKbps","required":true,"type":"number"},{"name":"uploadKbps","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"}],"name":"NetworkEmulation","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.set","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximumBytes":65536,"name":"body","required":true,"sensitive":false,"type":"string"},{"maximumBytes":256,"name":"contentType","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"status","required":true,"type":"number"},{"name":"activeMocks","required":true,"type":"number"}],"name":"NetworkMock","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"NetworkMockClear","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["choose interactive login or account alias","saved login requires a valid single-use challenge"],"name":"auth.login","parameters":[{"maximumBytes":64,"name":"challenge","required":false,"sensitive":false,"type":"string"},{"maximumBytes":64,"name":"account","required":false,"sensitive":false,"type":"string"},{"name":"interactive","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"account","required":true,"type":"string-or-null"},{"name":"saved","required":true,"type":"boolean"},{"name":"continuation","required":true,"type":"string"},{"name":"passwordExposed","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationLogin","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}}],"compatibility":{"mismatch":"host rejects request or client rejects response before decoding result","olderClient":"may call only commands and parameters declared by its bundled schema","olderHost":"missing commands or capabilities fail explicitly; clients must not emulate them","policy":"exact-wire-version","productVersionIndependent":true},"errorDetails":{"AUTH_REQUIRED":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"challenge","required":true,"type":"string"},{"name":"origin","required":true,"type":"string"},{"name":"detection","required":true,"type":"string"},{"name":"accounts","required":true,"type":"array"},{"name":"expiresInSeconds","required":true,"type":"number"},{"name":"userPresenceRequired","required":true,"type":"boolean"},{"name":"credentialUseAvailable","required":true,"type":"boolean"},{"name":"vaultAvailable","required":true,"type":"boolean"},{"name":"vaultStatus","required":true,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationRequired","type":"object"}}},"format":"headless-sdk-contract","hostLifecycle":{"connect":"attach to an existing host and never assume ownership","existingHost":"supervised start fails rather than attaching","ownerExit":"host shuts down when the owner pipe closes","sharedStart":"headless start --background","shutdown":"only explicit user requests may stop a shared host","supervisedOwnerChannel":"open standard-input pipe","supervisedStart":"headless start --background --supervised"},"jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","localCommands":["capabilities","config.describe","config.get","config.list","config.reset","config.set","credentials.add","credentials.list","credentials.remove","credentials.rename","help","runtime","schema","start","version"],"localErrorCodes":["CONFIGURATION_FAILED","HOST_START_FAILED","HOST_UNAVAILABLE","INVALID_CONFIGURATION","NAVIGATION_ALLOWLIST_CONFLICT","UNSUPPORTED_BROWSER_RUNTIME","UNSUPPORTED_CAPABILITY","VAULT_UNAVAILABLE"],"localLifecycle":{"connect":{"errors":["HOST_UNAVAILABLE"],"ownership":"shared","transport":"local-unix-socket"},"launch":{"argv":["start","--background","--supervised"],"command":"start","errors":["HOST_START_FAILED","NAVIGATION_ALLOWLIST_CONFLICT","UNSUPPORTED_BROWSER_RUNTIME","UNSUPPORTED_CAPABILITY"],"options":[{"name":"presentation","required":false,"type":"string","values":["background","foreground"]},{"itemMaximumBytes":300,"maximumItems":32,"name":"allow","required":false,"type":"string-array"},{"const":true,"name":"supervised","required":true,"type":"boolean"}],"ownership":"owned-only-after-response-pid-matches-launched-child","result":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}}},"maximumMessageBytes":1048576,"protocolVersion":"0.5","releasePolicy":{"preOneDeprecation":"at least one minor SDK release","provenance":"publish from reviewed tags with package attestations","schemaVersioning":"integer; generators reject unknown versions","sdkVersioning":"semantic versions independent of product and wire versions","securityReporting":"SECURITY.md; never include secrets or private artifacts","stableRemoval":"major SDK release and migration notes"},"request":{"additionalProperties":false,"id":{"maximumBytes":128,"minimumBytes":1,"type":"string"},"parameters":{"additionalProperties":false,"type":"object"},"required":["id","version","command","parameters"],"session":{"maximumBytes":64,"type":["string","null"]},"type":"object","version":{"const":"0.5","type":"string"}},"response":{"additionalProperties":true,"failure":{"error":{"codes":["ARTIFACT_ERROR","AUTH_ACCOUNT_NOT_FOUND","AUTH_CHALLENGE_CONSUMED","AUTH_CHALLENGE_EXPIRED","AUTH_CHALLENGE_NOT_FOUND","AUTH_FORM_CHANGED","AUTH_ORIGIN_CHANGED","AUTH_REQUIRED","CREDENTIAL_ALIAS_EXISTS","ELEMENT_NOT_FOUND","FLOW_FAILED","HOST_STOPPING","HOST_UNAVAILABLE","INTERNAL_ERROR","INVALID_CAPTURE_FORMAT","INVALID_COMMAND","INVALID_FLOW","INVALID_INPUT","INVALID_REQUEST","INVALID_SESSION","MISSING_PARAMETER","OPERATION_FAILED","PEER_DENIED","RECORDER_UNAVAILABLE","RECORDING_ACTIVE","RECORDING_FAILED","RECORDING_NOT_ACTIVE","REGION_NOT_FOUND","RESPONSE_TOO_LARGE","SENSITIVE_DIAGNOSTICS_DISABLED","SESSION_EXISTS","SESSION_NOT_FOUND","TIMEOUT","UNSAFE_NAVIGATION","UNSAFE_RESOURCE_TYPE","UNSUPPORTED_CAPABILITY","USER_PRESENCE_DENIED","USER_PRESENCE_UNAVAILABLE","VAULT_LOCKED","VAULT_OPERATION_FAILED","VAULT_RESPONSE_INVALID","VAULT_UNAVAILABLE"],"optional":["suggestion","details"],"required":["code","message"]},"ok":false,"result":null},"required":["id","version","ok"],"success":{"error":null,"ok":true,"result":"json"},"type":"object"},"schemaVersion":1,"security":{"arbitraryJavaScript":false,"credentialParameters":"aliases-and-challenge-identifiers-only","pageData":"untrusted","sensitiveDiagnostics":"requires-command-flag-and-host-environment-gate","tcpListener":false,"unknownRequestFields":"rejected","unknownResponseFields":"accepted for compatible additive evolution"},"supportWindow":{"hostPlatforms":["macOS 13 or newer","Linux amd64","Linux arm64"],"maintenance":"latest two minor SDK lines and at least 12 months after supersession, whichever is longer","pythonRuntimes":["CPython 3.11 through 3.14"],"schemaVersions":[1],"securityFixes":"supported SDK lines receive applicable security fixes","typescriptRuntimes":["Node.js 22 or newer"],"wireVersions":["0.5"]},"timeouts":{"connect":"fails before a request is sent","requestAfterWrite":"outcome unknown; never retry automatically","requestBeforeWrite":"safe to retry with a new request identifier","startup":"terminate and reap only the supervised launcher and host"},"transport":{"framing":"one-newline-terminated-json-document-per-connection","kind":"local-unix-socket","peerAuthorization":"same-os-user","runtimeDirectoryMode":"0700","socketMode":"0600","tcp":false}} +{"cancellation":{"afterSend":"close the client connection; command outcome is unknown until state is inspected","beforeSend":"request is not written","ownedHost":"terminate, await, and reap only a host process launched by that SDK instance","sharedHost":"never stop the host implicitly"},"commands":[{"capabilityNegotiated":false,"constraints":[],"name":"ping","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"shutdown","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"stopping","required":true,"type":"boolean"}],"name":"Shutdown","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"profile.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"boolean"},{"name":"session","required":true,"type":"string"}],"name":"ProfileClear","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.create","parameters":[{"maximumBytes":64,"name":"name","required":true,"sensitive":false,"type":"string"},{"name":"isolated","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"session","required":true,"type":"string"},{"name":"isolated","required":true,"type":"boolean"}],"name":"SessionCreate","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"sessions","required":true,"type":"array"},{"name":"details","required":true,"type":"array"}],"name":"SessionList","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.close","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"closed","required":true,"type":"string"}],"name":"SessionClose","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"visit","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"inspect","parameters":[{"name":"interactive","required":false,"sensitive":false,"type":"boolean"},{"name":"text","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":16,"name":"context","required":false,"sensitive":false,"type":"string","values":["summary","outline","text","actions","full"]},{"maximumBytes":512,"name":"task","required":false,"sensitive":false,"type":"string"},{"maximumBytes":16,"name":"within","required":false,"sensitive":false,"type":"string"},{"maximum":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximum":16000,"minimum":256,"name":"budget","required":false,"sensitive":false,"type":"integer"},{"maximum":8,"minimum":0,"name":"depth","required":false,"sensitive":false,"type":"integer"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"contextMode","required":true,"type":"string"},{"name":"viewport","required":true,"type":"object"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"elements","required":false,"type":"array"},{"name":"regions","required":false,"type":"array"},{"name":"snippets","required":false,"type":"array"},{"name":"text","required":false,"type":"string"}],"name":"Inspection","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"click","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"clicked","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"}],"name":"Click","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"fill","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":900000,"name":"value","required":true,"sensitive":true,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"filled","required":true,"type":"string"},{"name":"valueLength","required":true,"type":"number"}],"name":"Fill","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["artifact must be an existing private-store basename","exactly one target reference or semantic role/name target"],"name":"upload","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"artifact","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"uploaded","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"artifact","required":true,"type":"string"}],"name":"Upload","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"press","parameters":[{"maximumBytes":32,"name":"key","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"pressed","required":true,"type":"string"}],"name":"Press","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"scroll","parameters":[{"maximumBytes":8192,"name":"direction","required":false,"sensitive":false,"type":"string","values":["up","down","top","bottom"]},{"maximum":100000,"minimum":0.1,"name":"amount","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"direction","required":true,"type":"string"},{"name":"amount","required":true,"type":"number"}],"name":"Scroll","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"back","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"reload","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"wait","parameters":[{"name":"settled","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":8192,"name":"url","required":false,"sensitive":false,"type":"string"},{"maximumBytes":30000,"name":"text","required":false,"sensitive":false,"type":"string"},{"maximum":120000,"minimum":100,"name":"timeoutMs","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"maximumMilliseconds":125000,"minimumMilliseconds":10000,"parameterGraceMilliseconds":5000,"parameterName":"timeoutMs","parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"tour","parameters":[{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximum":5000,"minimum":100,"name":"pace","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"start","required":true,"type":"number"},{"name":"end","required":true,"type":"number"},{"name":"durationMs","required":true,"type":"number"}],"name":"Tour","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":125000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"capture.info","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"engine","required":true,"type":"string"},{"name":"page","required":true,"type":"object"},{"name":"trace","required":true,"type":"array"},{"name":"recording","required":true,"type":"object"}],"name":"CaptureInfo","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["target, full-page, and series modes are mutually constrained","PDF requires full-page mode and no clipboard"],"name":"screenshot","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximumBytes":32,"name":"series","required":false,"sensitive":false,"type":"string","values":["viewport","section"]},{"maximumBytes":80,"name":"outputPrefix","required":false,"sensitive":false,"type":"string"},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["png","jpg","jpeg","pdf"]},{"name":"clipboard","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":false,"type":"string"},{"name":"path","required":false,"type":"string"},{"name":"kind","required":false,"type":"string"},{"name":"bytes","required":false,"type":"number"},{"name":"createdAt","required":false,"type":"number"},{"name":"artifacts","required":false,"type":"array"},{"name":"truncated","required":false,"type":"boolean"}],"name":"Screenshot","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":30000,"parameterPresentOverrides":{"series":125000}}},{"capabilityNegotiated":false,"constraints":[],"name":"artifact.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"directory","required":true,"type":"string"},{"name":"artifacts","required":true,"type":"array"},{"name":"total","required":true,"type":"number"},{"name":"omitted","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"ArtifactList","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.start","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximum":30,"minimum":1,"name":"fps","required":false,"sensitive":false,"type":"number"},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["mp4","mov","webm","gif"]},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"quality","required":false,"sensitive":false,"type":"string","values":["fast","balanced","high"]}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.status","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":30000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.report","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"summary","required":true,"type":"object"},{"name":"issues","required":true,"type":"array"},{"name":"events","required":true,"type":"array"},{"name":"omitted","required":true,"type":"object"},{"name":"truncated","required":true,"type":"boolean"}],"name":"QAReport","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"QAClear","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"console.list","parameters":[{"maximumBytes":16,"name":"level","required":false,"sensitive":false,"type":"string","values":["all","log","info","debug","warn","error","assert"]},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"messages","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"ConsoleList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.list","parameters":[{"name":"failed","required":false,"sensitive":false,"type":"boolean"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"requests","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"NetworkList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.get","parameters":[{"maximumBytes":128,"name":"requestId","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"found","required":true,"type":"boolean"},{"name":"requestId","required":false,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"request","required":false,"type":"object"}],"name":"NetworkDetail","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"styles.get","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"itemMaximumBytes":128,"maximumItems":64,"name":"properties","required":false,"sensitive":false,"type":"string-array"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"ref","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"box","required":true,"type":"object"},{"name":"styles","required":true,"type":"object"}],"name":"Styles","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"cookies.list","parameters":[{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"cookies","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"CookieList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"storage.list","parameters":[{"maximumBytes":16,"name":"scope","required":false,"sensitive":false,"type":"string","values":["local","session","all"]},{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"stores","required":true,"type":"array"}],"name":"StorageList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"visual.compare","parameters":[{"maximumBytes":128,"name":"before","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"after","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"changedPixels","required":false,"type":"number"},{"name":"differenceRatio","required":false,"type":"number"}],"name":"VisualComparison","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"performance.get","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"timing","required":true,"type":"json"},{"name":"webVitals","required":true,"type":"object"},{"name":"resources","required":true,"type":"object"}],"name":"Performance","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"animation.list","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"count","required":true,"type":"number"},{"name":"animations","required":true,"type":"array"},{"name":"truncated","required":true,"type":"boolean"}],"name":"AnimationList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"report.create","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.start","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"recording","required":true,"type":"boolean"},{"name":"note","required":true,"type":"string"}],"name":"FlowStart","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.run","parameters":[{"maximumBytes":128,"name":"input","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"completed","required":true,"type":"number"},{"name":"input","required":true,"type":"string"}],"name":"FlowRun","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":125000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.emulate","parameters":[{"name":"offline","required":false,"sensitive":false,"type":"boolean"},{"maximum":120000,"minimum":0,"name":"latencyMs","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"downloadKbps","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"uploadKbps","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"offline","required":true,"type":"boolean"},{"name":"latencyMs","required":true,"type":"number"},{"name":"downloadKbps","required":true,"type":"number"},{"name":"uploadKbps","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"}],"name":"NetworkEmulation","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.set","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximumBytes":65536,"name":"body","required":true,"sensitive":false,"type":"string"},{"maximumBytes":256,"name":"contentType","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"status","required":true,"type":"number"},{"name":"activeMocks","required":true,"type":"number"}],"name":"NetworkMock","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"NetworkMockClear","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["choose interactive login or account alias","saved login requires a valid single-use challenge"],"name":"auth.login","parameters":[{"maximumBytes":64,"name":"challenge","required":false,"sensitive":false,"type":"string"},{"maximumBytes":64,"name":"account","required":false,"sensitive":false,"type":"string"},{"name":"interactive","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"account","required":true,"type":"string-or-null"},{"name":"saved","required":true,"type":"boolean"},{"name":"continuation","required":true,"type":"string"},{"name":"passwordExposed","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationLogin","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}}],"compatibility":{"mismatch":"host rejects request or client rejects response before decoding result","olderClient":"may call only commands and parameters declared by its bundled schema","olderHost":"missing commands or capabilities fail explicitly; clients must not emulate them","policy":"exact-wire-version","productVersionIndependent":true},"errorDetails":{"AUTH_REQUIRED":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"challenge","required":true,"type":"string"},{"name":"origin","required":true,"type":"string"},{"name":"detection","required":true,"type":"string"},{"name":"accounts","required":true,"type":"array"},{"name":"expiresInSeconds","required":true,"type":"number"},{"name":"userPresenceRequired","required":true,"type":"boolean"},{"name":"credentialUseAvailable","required":true,"type":"boolean"},{"name":"vaultAvailable","required":true,"type":"boolean"},{"name":"vaultStatus","required":true,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationRequired","type":"object"}}},"format":"headless-sdk-contract","hostLifecycle":{"connect":"attach to an existing host and never assume ownership","existingHost":"supervised start fails rather than attaching","ownerExit":"host shuts down when the owner pipe closes","sharedStart":"headless start --background","shutdown":"only explicit user requests may stop a shared host","supervisedOwnerChannel":"open standard-input pipe","supervisedStart":"headless start --background --supervised"},"jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","localCommands":["capabilities","config.describe","config.get","config.list","config.reset","config.set","credentials.add","credentials.list","credentials.remove","credentials.rename","help","runtime","schema","start","version"],"localErrorCodes":["CONFIGURATION_FAILED","HOST_START_FAILED","HOST_UNAVAILABLE","INVALID_CONFIGURATION","NAVIGATION_ALLOWLIST_CONFLICT","UNSUPPORTED_BROWSER_RUNTIME","UNSUPPORTED_CAPABILITY","VAULT_UNAVAILABLE"],"localLifecycle":{"connect":{"errors":["HOST_UNAVAILABLE"],"ownership":"shared","transport":"local-unix-socket"},"launch":{"argv":["start","--background","--supervised"],"command":"start","errors":["HOST_START_FAILED","NAVIGATION_ALLOWLIST_CONFLICT","UNSUPPORTED_BROWSER_RUNTIME","UNSUPPORTED_CAPABILITY"],"options":[{"name":"presentation","required":false,"type":"string","values":["background","foreground"]},{"itemMaximumBytes":300,"maximumItems":32,"name":"allow","required":false,"type":"string-array"},{"const":true,"name":"supervised","required":true,"type":"boolean"}],"ownership":"owned-only-after-response-pid-matches-launched-child","result":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}}},"maximumMessageBytes":1048576,"protocolVersion":"0.5","releasePolicy":{"preOneDeprecation":"at least one minor SDK release","provenance":"publish from reviewed tags with package attestations","schemaVersioning":"integer; generators reject unknown versions","sdkVersioning":"semantic versions independent of the wire version; combined product distributors track product tags","securityReporting":"SECURITY.md; never include secrets or private artifacts","stableRemoval":"major SDK release and migration notes"},"request":{"additionalProperties":false,"id":{"maximumBytes":128,"minimumBytes":1,"type":"string"},"parameters":{"additionalProperties":false,"type":"object"},"required":["id","version","command","parameters"],"session":{"maximumBytes":64,"type":["string","null"]},"type":"object","version":{"const":"0.5","type":"string"}},"response":{"additionalProperties":true,"failure":{"error":{"codes":["ARTIFACT_ERROR","AUTH_ACCOUNT_NOT_FOUND","AUTH_CHALLENGE_CONSUMED","AUTH_CHALLENGE_EXPIRED","AUTH_CHALLENGE_NOT_FOUND","AUTH_FORM_CHANGED","AUTH_ORIGIN_CHANGED","AUTH_REQUIRED","CREDENTIAL_ALIAS_EXISTS","ELEMENT_NOT_FOUND","FLOW_FAILED","HOST_STOPPING","HOST_UNAVAILABLE","INTERNAL_ERROR","INVALID_CAPTURE_FORMAT","INVALID_COMMAND","INVALID_FLOW","INVALID_INPUT","INVALID_REQUEST","INVALID_SESSION","MISSING_PARAMETER","OPERATION_FAILED","PEER_DENIED","RECORDER_UNAVAILABLE","RECORDING_ACTIVE","RECORDING_FAILED","RECORDING_NOT_ACTIVE","REGION_NOT_FOUND","RESPONSE_TOO_LARGE","SENSITIVE_DIAGNOSTICS_DISABLED","SESSION_EXISTS","SESSION_NOT_FOUND","TIMEOUT","UNSAFE_NAVIGATION","UNSAFE_RESOURCE_TYPE","UNSUPPORTED_CAPABILITY","USER_PRESENCE_DENIED","USER_PRESENCE_UNAVAILABLE","VAULT_LOCKED","VAULT_OPERATION_FAILED","VAULT_RESPONSE_INVALID","VAULT_UNAVAILABLE"],"optional":["suggestion","details"],"required":["code","message"]},"ok":false,"result":null},"required":["id","version","ok"],"success":{"error":null,"ok":true,"result":"json"},"type":"object"},"schemaVersion":1,"security":{"arbitraryJavaScript":false,"credentialParameters":"aliases-and-challenge-identifiers-only","pageData":"untrusted","sensitiveDiagnostics":"requires-command-flag-and-host-environment-gate","tcpListener":false,"unknownRequestFields":"rejected","unknownResponseFields":"accepted for compatible additive evolution"},"supportWindow":{"hostPlatforms":["macOS 13 or newer","Linux amd64","Linux arm64"],"maintenance":"latest two minor SDK lines and at least 12 months after supersession, whichever is longer","pythonRuntimes":["CPython 3.11 through 3.14"],"schemaVersions":[1],"securityFixes":"supported SDK lines receive applicable security fixes","typescriptRuntimes":["Node.js 22 or newer"],"wireVersions":["0.5"]},"timeouts":{"connect":"fails before a request is sent","requestAfterWrite":"outcome unknown; never retry automatically","requestBeforeWrite":"safe to retry with a new request identifier","startup":"terminate and reap only the supervised launcher and host"},"transport":{"framing":"one-newline-terminated-json-document-per-connection","kind":"local-unix-socket","peerAuthorization":"same-os-user","runtimeDirectoryMode":"0700","socketMode":"0600","tcp":false}} From 8cc63a3f976873f46b1c0a8d38fd86564c0b0ee6 Mon Sep 17 00:00:00 2001 From: SarthakWade Date: Sun, 13 Sep 2026 03:10:10 +0530 Subject: [PATCH 5/5] fix(sdk): keep default launch platform neutral --- .../HeadlessProtocol/ProtocolSchema.swift | 6 +++--- sdk/README.md | 17 +++++++++-------- sdk/protocol-schema.json | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift b/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift index 438c594..4c48715 100644 --- a/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift +++ b/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift @@ -829,7 +829,7 @@ public let protocolSchemaDocument: JSONValue = { ]), "launch": .object([ "command": .string("start"), - "argv": .array(["start", "--background", "--supervised"].map(JSONValue.string)), + "argv": .array(["start", "--supervised"].map(JSONValue.string)), "options": .array([ .object([ "name": .string("presentation"), "type": .string("string"), @@ -862,8 +862,8 @@ public let protocolSchemaDocument: JSONValue = { ].map(JSONValue.string)), "hostLifecycle": .object([ "connect": .string("attach to an existing host and never assume ownership"), - "sharedStart": .string("headless start --background"), - "supervisedStart": .string("headless start --background --supervised"), + "sharedStart": .string("headless start"), + "supervisedStart": .string("headless start --supervised"), "supervisedOwnerChannel": .string("open standard-input pipe"), "ownerExit": .string("host shuts down when the owner pipe closes"), "existingHost": .string("supervised start fails rather than attaching"), diff --git a/sdk/README.md b/sdk/README.md index acd43ed..2be164a 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -40,14 +40,15 @@ An SDK may terminate and reap only a host process that it launched and owns. It must not stop an already-running shared host during cancellation, timeout, or client shutdown. -Use `headless start --background --supervised` for an owned host. The command -fails if a shared host already exists, prints the normal startup response, and -then remains attached to the host. The launcher owns a private pipe to the host, -so the host still stops if the launcher is killed while another process retains -the launcher's input stream. The startup response PID must match the launched -host before ownership is granted. Closing the launcher input makes the host stop -and lets the launcher be awaited. A plain `headless start` remains detached and -shared. +Use `headless start --supervised` for an owned host. Omitting a presentation +option preserves the platform default; macOS callers may explicitly request +`--background` or `--foreground`. The command fails if a shared host already +exists, prints the normal startup response, and then remains attached to the +host. The launcher owns a private pipe to the host, so the host still stops if +the launcher is killed while another process retains the launcher's input +stream. The startup response PID must match the launched host before ownership +is granted. Closing the launcher input makes the host stop and lets the launcher +be awaited. A plain `headless start` remains detached and shared. ## Release policy diff --git a/sdk/protocol-schema.json b/sdk/protocol-schema.json index a4051a6..45731a4 100644 --- a/sdk/protocol-schema.json +++ b/sdk/protocol-schema.json @@ -1 +1 @@ -{"cancellation":{"afterSend":"close the client connection; command outcome is unknown until state is inspected","beforeSend":"request is not written","ownedHost":"terminate, await, and reap only a host process launched by that SDK instance","sharedHost":"never stop the host implicitly"},"commands":[{"capabilityNegotiated":false,"constraints":[],"name":"ping","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"shutdown","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"stopping","required":true,"type":"boolean"}],"name":"Shutdown","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"profile.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"boolean"},{"name":"session","required":true,"type":"string"}],"name":"ProfileClear","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.create","parameters":[{"maximumBytes":64,"name":"name","required":true,"sensitive":false,"type":"string"},{"name":"isolated","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"session","required":true,"type":"string"},{"name":"isolated","required":true,"type":"boolean"}],"name":"SessionCreate","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"sessions","required":true,"type":"array"},{"name":"details","required":true,"type":"array"}],"name":"SessionList","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.close","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"closed","required":true,"type":"string"}],"name":"SessionClose","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"visit","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"inspect","parameters":[{"name":"interactive","required":false,"sensitive":false,"type":"boolean"},{"name":"text","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":16,"name":"context","required":false,"sensitive":false,"type":"string","values":["summary","outline","text","actions","full"]},{"maximumBytes":512,"name":"task","required":false,"sensitive":false,"type":"string"},{"maximumBytes":16,"name":"within","required":false,"sensitive":false,"type":"string"},{"maximum":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximum":16000,"minimum":256,"name":"budget","required":false,"sensitive":false,"type":"integer"},{"maximum":8,"minimum":0,"name":"depth","required":false,"sensitive":false,"type":"integer"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"contextMode","required":true,"type":"string"},{"name":"viewport","required":true,"type":"object"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"elements","required":false,"type":"array"},{"name":"regions","required":false,"type":"array"},{"name":"snippets","required":false,"type":"array"},{"name":"text","required":false,"type":"string"}],"name":"Inspection","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"click","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"clicked","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"}],"name":"Click","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"fill","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":900000,"name":"value","required":true,"sensitive":true,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"filled","required":true,"type":"string"},{"name":"valueLength","required":true,"type":"number"}],"name":"Fill","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["artifact must be an existing private-store basename","exactly one target reference or semantic role/name target"],"name":"upload","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"artifact","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"uploaded","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"artifact","required":true,"type":"string"}],"name":"Upload","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"press","parameters":[{"maximumBytes":32,"name":"key","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"pressed","required":true,"type":"string"}],"name":"Press","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"scroll","parameters":[{"maximumBytes":8192,"name":"direction","required":false,"sensitive":false,"type":"string","values":["up","down","top","bottom"]},{"maximum":100000,"minimum":0.1,"name":"amount","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"direction","required":true,"type":"string"},{"name":"amount","required":true,"type":"number"}],"name":"Scroll","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"back","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"reload","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"wait","parameters":[{"name":"settled","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":8192,"name":"url","required":false,"sensitive":false,"type":"string"},{"maximumBytes":30000,"name":"text","required":false,"sensitive":false,"type":"string"},{"maximum":120000,"minimum":100,"name":"timeoutMs","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"maximumMilliseconds":125000,"minimumMilliseconds":10000,"parameterGraceMilliseconds":5000,"parameterName":"timeoutMs","parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"tour","parameters":[{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximum":5000,"minimum":100,"name":"pace","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"start","required":true,"type":"number"},{"name":"end","required":true,"type":"number"},{"name":"durationMs","required":true,"type":"number"}],"name":"Tour","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":125000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"capture.info","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"engine","required":true,"type":"string"},{"name":"page","required":true,"type":"object"},{"name":"trace","required":true,"type":"array"},{"name":"recording","required":true,"type":"object"}],"name":"CaptureInfo","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["target, full-page, and series modes are mutually constrained","PDF requires full-page mode and no clipboard"],"name":"screenshot","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximumBytes":32,"name":"series","required":false,"sensitive":false,"type":"string","values":["viewport","section"]},{"maximumBytes":80,"name":"outputPrefix","required":false,"sensitive":false,"type":"string"},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["png","jpg","jpeg","pdf"]},{"name":"clipboard","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":false,"type":"string"},{"name":"path","required":false,"type":"string"},{"name":"kind","required":false,"type":"string"},{"name":"bytes","required":false,"type":"number"},{"name":"createdAt","required":false,"type":"number"},{"name":"artifacts","required":false,"type":"array"},{"name":"truncated","required":false,"type":"boolean"}],"name":"Screenshot","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":30000,"parameterPresentOverrides":{"series":125000}}},{"capabilityNegotiated":false,"constraints":[],"name":"artifact.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"directory","required":true,"type":"string"},{"name":"artifacts","required":true,"type":"array"},{"name":"total","required":true,"type":"number"},{"name":"omitted","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"ArtifactList","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.start","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximum":30,"minimum":1,"name":"fps","required":false,"sensitive":false,"type":"number"},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["mp4","mov","webm","gif"]},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"quality","required":false,"sensitive":false,"type":"string","values":["fast","balanced","high"]}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.status","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":30000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.report","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"summary","required":true,"type":"object"},{"name":"issues","required":true,"type":"array"},{"name":"events","required":true,"type":"array"},{"name":"omitted","required":true,"type":"object"},{"name":"truncated","required":true,"type":"boolean"}],"name":"QAReport","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"QAClear","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"console.list","parameters":[{"maximumBytes":16,"name":"level","required":false,"sensitive":false,"type":"string","values":["all","log","info","debug","warn","error","assert"]},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"messages","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"ConsoleList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.list","parameters":[{"name":"failed","required":false,"sensitive":false,"type":"boolean"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"requests","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"NetworkList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.get","parameters":[{"maximumBytes":128,"name":"requestId","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"found","required":true,"type":"boolean"},{"name":"requestId","required":false,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"request","required":false,"type":"object"}],"name":"NetworkDetail","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"styles.get","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"itemMaximumBytes":128,"maximumItems":64,"name":"properties","required":false,"sensitive":false,"type":"string-array"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"ref","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"box","required":true,"type":"object"},{"name":"styles","required":true,"type":"object"}],"name":"Styles","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"cookies.list","parameters":[{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"cookies","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"CookieList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"storage.list","parameters":[{"maximumBytes":16,"name":"scope","required":false,"sensitive":false,"type":"string","values":["local","session","all"]},{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"stores","required":true,"type":"array"}],"name":"StorageList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"visual.compare","parameters":[{"maximumBytes":128,"name":"before","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"after","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"changedPixels","required":false,"type":"number"},{"name":"differenceRatio","required":false,"type":"number"}],"name":"VisualComparison","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"performance.get","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"timing","required":true,"type":"json"},{"name":"webVitals","required":true,"type":"object"},{"name":"resources","required":true,"type":"object"}],"name":"Performance","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"animation.list","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"count","required":true,"type":"number"},{"name":"animations","required":true,"type":"array"},{"name":"truncated","required":true,"type":"boolean"}],"name":"AnimationList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"report.create","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.start","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"recording","required":true,"type":"boolean"},{"name":"note","required":true,"type":"string"}],"name":"FlowStart","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.run","parameters":[{"maximumBytes":128,"name":"input","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"completed","required":true,"type":"number"},{"name":"input","required":true,"type":"string"}],"name":"FlowRun","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":125000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.emulate","parameters":[{"name":"offline","required":false,"sensitive":false,"type":"boolean"},{"maximum":120000,"minimum":0,"name":"latencyMs","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"downloadKbps","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"uploadKbps","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"offline","required":true,"type":"boolean"},{"name":"latencyMs","required":true,"type":"number"},{"name":"downloadKbps","required":true,"type":"number"},{"name":"uploadKbps","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"}],"name":"NetworkEmulation","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.set","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximumBytes":65536,"name":"body","required":true,"sensitive":false,"type":"string"},{"maximumBytes":256,"name":"contentType","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"status","required":true,"type":"number"},{"name":"activeMocks","required":true,"type":"number"}],"name":"NetworkMock","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"NetworkMockClear","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["choose interactive login or account alias","saved login requires a valid single-use challenge"],"name":"auth.login","parameters":[{"maximumBytes":64,"name":"challenge","required":false,"sensitive":false,"type":"string"},{"maximumBytes":64,"name":"account","required":false,"sensitive":false,"type":"string"},{"name":"interactive","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"account","required":true,"type":"string-or-null"},{"name":"saved","required":true,"type":"boolean"},{"name":"continuation","required":true,"type":"string"},{"name":"passwordExposed","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationLogin","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}}],"compatibility":{"mismatch":"host rejects request or client rejects response before decoding result","olderClient":"may call only commands and parameters declared by its bundled schema","olderHost":"missing commands or capabilities fail explicitly; clients must not emulate them","policy":"exact-wire-version","productVersionIndependent":true},"errorDetails":{"AUTH_REQUIRED":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"challenge","required":true,"type":"string"},{"name":"origin","required":true,"type":"string"},{"name":"detection","required":true,"type":"string"},{"name":"accounts","required":true,"type":"array"},{"name":"expiresInSeconds","required":true,"type":"number"},{"name":"userPresenceRequired","required":true,"type":"boolean"},{"name":"credentialUseAvailable","required":true,"type":"boolean"},{"name":"vaultAvailable","required":true,"type":"boolean"},{"name":"vaultStatus","required":true,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationRequired","type":"object"}}},"format":"headless-sdk-contract","hostLifecycle":{"connect":"attach to an existing host and never assume ownership","existingHost":"supervised start fails rather than attaching","ownerExit":"host shuts down when the owner pipe closes","sharedStart":"headless start --background","shutdown":"only explicit user requests may stop a shared host","supervisedOwnerChannel":"open standard-input pipe","supervisedStart":"headless start --background --supervised"},"jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","localCommands":["capabilities","config.describe","config.get","config.list","config.reset","config.set","credentials.add","credentials.list","credentials.remove","credentials.rename","help","runtime","schema","start","version"],"localErrorCodes":["CONFIGURATION_FAILED","HOST_START_FAILED","HOST_UNAVAILABLE","INVALID_CONFIGURATION","NAVIGATION_ALLOWLIST_CONFLICT","UNSUPPORTED_BROWSER_RUNTIME","UNSUPPORTED_CAPABILITY","VAULT_UNAVAILABLE"],"localLifecycle":{"connect":{"errors":["HOST_UNAVAILABLE"],"ownership":"shared","transport":"local-unix-socket"},"launch":{"argv":["start","--background","--supervised"],"command":"start","errors":["HOST_START_FAILED","NAVIGATION_ALLOWLIST_CONFLICT","UNSUPPORTED_BROWSER_RUNTIME","UNSUPPORTED_CAPABILITY"],"options":[{"name":"presentation","required":false,"type":"string","values":["background","foreground"]},{"itemMaximumBytes":300,"maximumItems":32,"name":"allow","required":false,"type":"string-array"},{"const":true,"name":"supervised","required":true,"type":"boolean"}],"ownership":"owned-only-after-response-pid-matches-launched-child","result":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}}},"maximumMessageBytes":1048576,"protocolVersion":"0.5","releasePolicy":{"preOneDeprecation":"at least one minor SDK release","provenance":"publish from reviewed tags with package attestations","schemaVersioning":"integer; generators reject unknown versions","sdkVersioning":"semantic versions independent of the wire version; combined product distributors track product tags","securityReporting":"SECURITY.md; never include secrets or private artifacts","stableRemoval":"major SDK release and migration notes"},"request":{"additionalProperties":false,"id":{"maximumBytes":128,"minimumBytes":1,"type":"string"},"parameters":{"additionalProperties":false,"type":"object"},"required":["id","version","command","parameters"],"session":{"maximumBytes":64,"type":["string","null"]},"type":"object","version":{"const":"0.5","type":"string"}},"response":{"additionalProperties":true,"failure":{"error":{"codes":["ARTIFACT_ERROR","AUTH_ACCOUNT_NOT_FOUND","AUTH_CHALLENGE_CONSUMED","AUTH_CHALLENGE_EXPIRED","AUTH_CHALLENGE_NOT_FOUND","AUTH_FORM_CHANGED","AUTH_ORIGIN_CHANGED","AUTH_REQUIRED","CREDENTIAL_ALIAS_EXISTS","ELEMENT_NOT_FOUND","FLOW_FAILED","HOST_STOPPING","HOST_UNAVAILABLE","INTERNAL_ERROR","INVALID_CAPTURE_FORMAT","INVALID_COMMAND","INVALID_FLOW","INVALID_INPUT","INVALID_REQUEST","INVALID_SESSION","MISSING_PARAMETER","OPERATION_FAILED","PEER_DENIED","RECORDER_UNAVAILABLE","RECORDING_ACTIVE","RECORDING_FAILED","RECORDING_NOT_ACTIVE","REGION_NOT_FOUND","RESPONSE_TOO_LARGE","SENSITIVE_DIAGNOSTICS_DISABLED","SESSION_EXISTS","SESSION_NOT_FOUND","TIMEOUT","UNSAFE_NAVIGATION","UNSAFE_RESOURCE_TYPE","UNSUPPORTED_CAPABILITY","USER_PRESENCE_DENIED","USER_PRESENCE_UNAVAILABLE","VAULT_LOCKED","VAULT_OPERATION_FAILED","VAULT_RESPONSE_INVALID","VAULT_UNAVAILABLE"],"optional":["suggestion","details"],"required":["code","message"]},"ok":false,"result":null},"required":["id","version","ok"],"success":{"error":null,"ok":true,"result":"json"},"type":"object"},"schemaVersion":1,"security":{"arbitraryJavaScript":false,"credentialParameters":"aliases-and-challenge-identifiers-only","pageData":"untrusted","sensitiveDiagnostics":"requires-command-flag-and-host-environment-gate","tcpListener":false,"unknownRequestFields":"rejected","unknownResponseFields":"accepted for compatible additive evolution"},"supportWindow":{"hostPlatforms":["macOS 13 or newer","Linux amd64","Linux arm64"],"maintenance":"latest two minor SDK lines and at least 12 months after supersession, whichever is longer","pythonRuntimes":["CPython 3.11 through 3.14"],"schemaVersions":[1],"securityFixes":"supported SDK lines receive applicable security fixes","typescriptRuntimes":["Node.js 22 or newer"],"wireVersions":["0.5"]},"timeouts":{"connect":"fails before a request is sent","requestAfterWrite":"outcome unknown; never retry automatically","requestBeforeWrite":"safe to retry with a new request identifier","startup":"terminate and reap only the supervised launcher and host"},"transport":{"framing":"one-newline-terminated-json-document-per-connection","kind":"local-unix-socket","peerAuthorization":"same-os-user","runtimeDirectoryMode":"0700","socketMode":"0600","tcp":false}} +{"cancellation":{"afterSend":"close the client connection; command outcome is unknown until state is inspected","beforeSend":"request is not written","ownedHost":"terminate, await, and reap only a host process launched by that SDK instance","sharedHost":"never stop the host implicitly"},"commands":[{"capabilityNegotiated":false,"constraints":[],"name":"ping","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"shutdown","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"stopping","required":true,"type":"boolean"}],"name":"Shutdown","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"profile.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"boolean"},{"name":"session","required":true,"type":"string"}],"name":"ProfileClear","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.create","parameters":[{"maximumBytes":64,"name":"name","required":true,"sensitive":false,"type":"string"},{"name":"isolated","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"session","required":true,"type":"string"},{"name":"isolated","required":true,"type":"boolean"}],"name":"SessionCreate","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"sessions","required":true,"type":"array"},{"name":"details","required":true,"type":"array"}],"name":"SessionList","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"session.close","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"closed","required":true,"type":"string"}],"name":"SessionClose","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"visit","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"inspect","parameters":[{"name":"interactive","required":false,"sensitive":false,"type":"boolean"},{"name":"text","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":16,"name":"context","required":false,"sensitive":false,"type":"string","values":["summary","outline","text","actions","full"]},{"maximumBytes":512,"name":"task","required":false,"sensitive":false,"type":"string"},{"maximumBytes":16,"name":"within","required":false,"sensitive":false,"type":"string"},{"maximum":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximum":16000,"minimum":256,"name":"budget","required":false,"sensitive":false,"type":"integer"},{"maximum":8,"minimum":0,"name":"depth","required":false,"sensitive":false,"type":"integer"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"contextMode","required":true,"type":"string"},{"name":"viewport","required":true,"type":"object"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"elements","required":false,"type":"array"},{"name":"regions","required":false,"type":"array"},{"name":"snippets","required":false,"type":"array"},{"name":"text","required":false,"type":"string"}],"name":"Inspection","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"click","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"clicked","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"}],"name":"Click","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"fill","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":900000,"name":"value","required":true,"sensitive":true,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"filled","required":true,"type":"string"},{"name":"valueLength","required":true,"type":"number"}],"name":"Fill","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["artifact must be an existing private-store basename","exactly one target reference or semantic role/name target"],"name":"upload","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"artifact","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"uploaded","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"artifact","required":true,"type":"string"}],"name":"Upload","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"press","parameters":[{"maximumBytes":32,"name":"key","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"pressed","required":true,"type":"string"}],"name":"Press","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"scroll","parameters":[{"maximumBytes":8192,"name":"direction","required":false,"sensitive":false,"type":"string","values":["up","down","top","bottom"]},{"maximum":100000,"minimum":0.1,"name":"amount","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"direction","required":true,"type":"string"},{"name":"amount","required":true,"type":"number"}],"name":"Scroll","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"back","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"reload","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"wait","parameters":[{"name":"settled","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":8192,"name":"url","required":false,"sensitive":false,"type":"string"},{"maximumBytes":30000,"name":"text","required":false,"sensitive":false,"type":"string"},{"maximum":120000,"minimum":100,"name":"timeoutMs","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"title","required":true,"type":"string"},{"name":"readyState","required":true,"type":"string"},{"name":"text","required":true,"type":"string"},{"name":"runningAnimations","required":true,"type":"number"},{"name":"mutationQuietMs","required":true,"type":"number"},{"name":"scrollY","required":true,"type":"number"},{"name":"contentHeight","required":true,"type":"number"}],"name":"PageState","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"maximumMilliseconds":125000,"minimumMilliseconds":10000,"parameterGraceMilliseconds":5000,"parameterName":"timeoutMs","parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"tour","parameters":[{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximum":5000,"minimum":100,"name":"pace","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"start","required":true,"type":"number"},{"name":"end","required":true,"type":"number"},{"name":"durationMs","required":true,"type":"number"}],"name":"Tour","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":125000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"capture.info","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"engine","required":true,"type":"string"},{"name":"page","required":true,"type":"object"},{"name":"trace","required":true,"type":"array"},{"name":"recording","required":true,"type":"object"}],"name":"CaptureInfo","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["target, full-page, and series modes are mutually constrained","PDF requires full-page mode and no clipboard"],"name":"screenshot","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"name":"fullPage","required":false,"sensitive":false,"type":"boolean"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximumBytes":32,"name":"series","required":false,"sensitive":false,"type":"string","values":["viewport","section"]},{"maximumBytes":80,"name":"outputPrefix","required":false,"sensitive":false,"type":"string"},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["png","jpg","jpeg","pdf"]},{"name":"clipboard","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":false,"type":"string"},{"name":"path","required":false,"type":"string"},{"name":"kind","required":false,"type":"string"},{"name":"bytes","required":false,"type":"number"},{"name":"createdAt","required":false,"type":"number"},{"name":"artifacts","required":false,"type":"array"},{"name":"truncated","required":false,"type":"boolean"}],"name":"Screenshot","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":30000,"parameterPresentOverrides":{"series":125000}}},{"capabilityNegotiated":false,"constraints":[],"name":"artifact.list","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"directory","required":true,"type":"string"},{"name":"artifacts","required":true,"type":"array"},{"name":"total","required":true,"type":"number"},{"name":"omitted","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"ArtifactList","type":"object"}},"scope":"host","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.start","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"},{"maximum":30,"minimum":1,"name":"fps","required":false,"sensitive":false,"type":"number"},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"format","required":false,"sensitive":false,"type":"string","values":["mp4","mov","webm","gif"]},{"caseInsensitiveValues":true,"maximumBytes":16,"name":"quality","required":false,"sensitive":false,"type":"string","values":["fast","balanced","high"]}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.status","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"record.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"active","required":true,"type":"boolean"},{"name":"format","required":false,"type":"string"},{"name":"quality","required":false,"type":"string"},{"name":"name","required":false,"type":"string"}],"name":"Recording","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":30000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.report","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"summary","required":true,"type":"object"},{"name":"issues","required":true,"type":"array"},{"name":"events","required":true,"type":"array"},{"name":"omitted","required":true,"type":"object"},{"name":"truncated","required":true,"type":"boolean"}],"name":"QAReport","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"qa.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"QAClear","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"console.list","parameters":[{"maximumBytes":16,"name":"level","required":false,"sensitive":false,"type":"string","values":["all","log","info","debug","warn","error","assert"]},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"messages","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"ConsoleList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.list","parameters":[{"name":"failed","required":false,"sensitive":false,"type":"boolean"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximum":200,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"requests","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"}],"name":"NetworkList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"network.get","parameters":[{"maximumBytes":128,"name":"requestId","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"found","required":true,"type":"boolean"},{"name":"requestId","required":false,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"request","required":false,"type":"object"}],"name":"NetworkDetail","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["exactly one target reference or semantic role/name target"],"name":"styles.get","parameters":[{"maximumBytes":16,"name":"target","required":false,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"role","required":false,"sensitive":false,"type":"string"},{"maximumBytes":1000,"name":"name","required":false,"sensitive":false,"type":"string"},{"itemMaximumBytes":128,"maximumItems":64,"name":"properties","required":false,"sensitive":false,"type":"string-array"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"ref","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"box","required":true,"type":"object"},{"name":"styles","required":true,"type":"object"}],"name":"Styles","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"cookies.list","parameters":[{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"cookies","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"available","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"}],"name":"CookieList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":["values require the sensitive diagnostics environment gate"],"name":"storage.list","parameters":[{"maximumBytes":16,"name":"scope","required":false,"sensitive":false,"type":"string","values":["local","session","all"]},{"name":"includeValues","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"stores","required":true,"type":"array"}],"name":"StorageList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"visual.compare","parameters":[{"maximumBytes":128,"name":"before","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"after","required":true,"sensitive":false,"type":"string"},{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"changedPixels","required":false,"type":"number"},{"name":"differenceRatio","required":false,"type":"number"}],"name":"VisualComparison","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"performance.get","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"timing","required":true,"type":"json"},{"name":"webVitals","required":true,"type":"object"},{"name":"resources","required":true,"type":"object"}],"name":"Performance","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"animation.list","parameters":[],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"count","required":true,"type":"number"},{"name":"animations","required":true,"type":"array"},{"name":"truncated","required":true,"type":"boolean"}],"name":"AnimationList","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"report.create","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.start","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"recording","required":true,"type":"boolean"},{"name":"note","required":true,"type":"string"}],"name":"FlowStart","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.stop","parameters":[{"maximumBytes":128,"name":"output","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"path","required":true,"type":"string"},{"name":"kind","required":true,"type":"string"},{"name":"bytes","required":true,"type":"number"},{"name":"createdAt","required":true,"type":"number"}],"name":"Artifact","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":false,"constraints":[],"name":"flow.run","parameters":[{"maximumBytes":128,"name":"input","required":true,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"completed","required":true,"type":"number"},{"name":"input","required":true,"type":"string"}],"name":"FlowRun","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":125000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.emulate","parameters":[{"name":"offline","required":false,"sensitive":false,"type":"boolean"},{"maximum":120000,"minimum":0,"name":"latencyMs","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"downloadKbps","required":false,"sensitive":false,"type":"number"},{"maximum":1000000,"minimum":-1,"name":"uploadKbps","required":false,"sensitive":false,"type":"number"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"offline","required":true,"type":"boolean"},{"name":"latencyMs","required":true,"type":"number"},{"name":"downloadKbps","required":true,"type":"number"},{"name":"uploadKbps","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"}],"name":"NetworkEmulation","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.set","parameters":[{"maximumBytes":8192,"name":"url","required":true,"sensitive":false,"type":"string"},{"maximum":599,"minimum":100,"name":"status","required":false,"sensitive":false,"type":"number"},{"maximumBytes":65536,"name":"body","required":true,"sensitive":false,"type":"string"},{"maximumBytes":256,"name":"contentType","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"url","required":true,"type":"string"},{"name":"status","required":true,"type":"number"},{"name":"activeMocks","required":true,"type":"number"}],"name":"NetworkMock","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":[],"name":"network.mock.clear","parameters":[],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"cleared","required":true,"type":"number"}],"name":"NetworkMockClear","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}},{"capabilityNegotiated":true,"constraints":["choose interactive login or account alias","saved login requires a valid single-use challenge"],"name":"auth.login","parameters":[{"maximumBytes":64,"name":"challenge","required":false,"sensitive":false,"type":"string"},{"maximumBytes":64,"name":"account","required":false,"sensitive":false,"type":"string"},{"name":"interactive","required":false,"sensitive":false,"type":"boolean"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"origin","required":true,"type":"string"},{"name":"account","required":true,"type":"string-or-null"},{"name":"saved","required":true,"type":"boolean"},{"name":"continuation","required":true,"type":"string"},{"name":"passwordExposed","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationLogin","type":"object"}},"scope":"session","timeout":{"defaultMilliseconds":15000,"parameterPresentOverrides":{}}}],"compatibility":{"mismatch":"host rejects request or client rejects response before decoding result","olderClient":"may call only commands and parameters declared by its bundled schema","olderHost":"missing commands or capabilities fail explicitly; clients must not emulate them","policy":"exact-wire-version","productVersionIndependent":true},"errorDetails":{"AUTH_REQUIRED":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"challenge","required":true,"type":"string"},{"name":"origin","required":true,"type":"string"},{"name":"detection","required":true,"type":"string"},{"name":"accounts","required":true,"type":"array"},{"name":"expiresInSeconds","required":true,"type":"number"},{"name":"userPresenceRequired","required":true,"type":"boolean"},{"name":"credentialUseAvailable","required":true,"type":"boolean"},{"name":"vaultAvailable","required":true,"type":"boolean"},{"name":"vaultStatus","required":true,"type":"string"},{"name":"untrustedContent","required":true,"type":"boolean"},{"name":"originalActionReplayed","required":true,"type":"boolean"}],"name":"AuthenticationRequired","type":"object"}}},"format":"headless-sdk-contract","hostLifecycle":{"connect":"attach to an existing host and never assume ownership","existingHost":"supervised start fails rather than attaching","ownerExit":"host shuts down when the owner pipe closes","sharedStart":"headless start","shutdown":"only explicit user requests may stop a shared host","supervisedOwnerChannel":"open standard-input pipe","supervisedStart":"headless start --supervised"},"jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","localCommands":["capabilities","config.describe","config.get","config.list","config.reset","config.set","credentials.add","credentials.list","credentials.remove","credentials.rename","help","runtime","schema","start","version"],"localErrorCodes":["CONFIGURATION_FAILED","HOST_START_FAILED","HOST_UNAVAILABLE","INVALID_CONFIGURATION","NAVIGATION_ALLOWLIST_CONFLICT","UNSUPPORTED_BROWSER_RUNTIME","UNSUPPORTED_CAPABILITY","VAULT_UNAVAILABLE"],"localLifecycle":{"connect":{"errors":["HOST_UNAVAILABLE"],"ownership":"shared","transport":"local-unix-socket"},"launch":{"argv":["start","--supervised"],"command":"start","errors":["HOST_START_FAILED","NAVIGATION_ALLOWLIST_CONFLICT","UNSUPPORTED_BROWSER_RUNTIME","UNSUPPORTED_CAPABILITY"],"options":[{"name":"presentation","required":false,"type":"string","values":["background","foreground"]},{"itemMaximumBytes":300,"maximumItems":32,"name":"allow","required":false,"type":"string-array"},{"const":true,"name":"supervised","required":true,"type":"boolean"}],"ownership":"owned-only-after-response-pid-matches-launched-child","result":{"additionalProperties":true,"fields":[{"name":"ready","required":true,"type":"boolean"},{"name":"pid","required":true,"type":"number"},{"name":"engine","required":true,"type":"string"},{"name":"platform","required":true,"type":"string"},{"name":"productVersion","required":true,"type":"string"},{"name":"protocolVersion","required":true,"type":"string"},{"name":"capabilities","required":true,"type":"object"},{"name":"recordingAvailable","required":true,"type":"boolean"},{"name":"artifactDirectory","required":true,"type":"string"},{"name":"navigationAllowlist","required":true,"type":"array"}],"name":"HostStatus","type":"object"}}},"maximumMessageBytes":1048576,"protocolVersion":"0.5","releasePolicy":{"preOneDeprecation":"at least one minor SDK release","provenance":"publish from reviewed tags with package attestations","schemaVersioning":"integer; generators reject unknown versions","sdkVersioning":"semantic versions independent of the wire version; combined product distributors track product tags","securityReporting":"SECURITY.md; never include secrets or private artifacts","stableRemoval":"major SDK release and migration notes"},"request":{"additionalProperties":false,"id":{"maximumBytes":128,"minimumBytes":1,"type":"string"},"parameters":{"additionalProperties":false,"type":"object"},"required":["id","version","command","parameters"],"session":{"maximumBytes":64,"type":["string","null"]},"type":"object","version":{"const":"0.5","type":"string"}},"response":{"additionalProperties":true,"failure":{"error":{"codes":["ARTIFACT_ERROR","AUTH_ACCOUNT_NOT_FOUND","AUTH_CHALLENGE_CONSUMED","AUTH_CHALLENGE_EXPIRED","AUTH_CHALLENGE_NOT_FOUND","AUTH_FORM_CHANGED","AUTH_ORIGIN_CHANGED","AUTH_REQUIRED","CREDENTIAL_ALIAS_EXISTS","ELEMENT_NOT_FOUND","FLOW_FAILED","HOST_STOPPING","HOST_UNAVAILABLE","INTERNAL_ERROR","INVALID_CAPTURE_FORMAT","INVALID_COMMAND","INVALID_FLOW","INVALID_INPUT","INVALID_REQUEST","INVALID_SESSION","MISSING_PARAMETER","OPERATION_FAILED","PEER_DENIED","RECORDER_UNAVAILABLE","RECORDING_ACTIVE","RECORDING_FAILED","RECORDING_NOT_ACTIVE","REGION_NOT_FOUND","RESPONSE_TOO_LARGE","SENSITIVE_DIAGNOSTICS_DISABLED","SESSION_EXISTS","SESSION_NOT_FOUND","TIMEOUT","UNSAFE_NAVIGATION","UNSAFE_RESOURCE_TYPE","UNSUPPORTED_CAPABILITY","USER_PRESENCE_DENIED","USER_PRESENCE_UNAVAILABLE","VAULT_LOCKED","VAULT_OPERATION_FAILED","VAULT_RESPONSE_INVALID","VAULT_UNAVAILABLE"],"optional":["suggestion","details"],"required":["code","message"]},"ok":false,"result":null},"required":["id","version","ok"],"success":{"error":null,"ok":true,"result":"json"},"type":"object"},"schemaVersion":1,"security":{"arbitraryJavaScript":false,"credentialParameters":"aliases-and-challenge-identifiers-only","pageData":"untrusted","sensitiveDiagnostics":"requires-command-flag-and-host-environment-gate","tcpListener":false,"unknownRequestFields":"rejected","unknownResponseFields":"accepted for compatible additive evolution"},"supportWindow":{"hostPlatforms":["macOS 13 or newer","Linux amd64","Linux arm64"],"maintenance":"latest two minor SDK lines and at least 12 months after supersession, whichever is longer","pythonRuntimes":["CPython 3.11 through 3.14"],"schemaVersions":[1],"securityFixes":"supported SDK lines receive applicable security fixes","typescriptRuntimes":["Node.js 22 or newer"],"wireVersions":["0.5"]},"timeouts":{"connect":"fails before a request is sent","requestAfterWrite":"outcome unknown; never retry automatically","requestBeforeWrite":"safe to retry with a new request identifier","startup":"terminate and reap only the supervised launcher and host"},"transport":{"framing":"one-newline-terminated-json-document-per-connection","kind":"local-unix-socket","peerAuthorization":"same-os-user","runtimeDirectoryMode":"0700","socketMode":"0600","tcp":false}}