From 20ce3214ca7ec84ea068a169d535c4d3c77407a9 Mon Sep 17 00:00:00 2001 From: SarthakWade Date: Sun, 20 Sep 2026 15:43:37 +0530 Subject: [PATCH] feat(select): add semantic native dropdown selection --- .agents/skills/headless-computer-use/SKILL.md | 2 + .../references/commands.md | 9 +- apps/headless/Host/AgentBridge.swift | 12 ++ apps/headless/LinuxHost/BrowserProcess.swift | 9 ++ apps/headless/LinuxHost/main.swift | 3 + .../Sources/HeadlessProtocol/CLI.swift | 22 ++++ .../HeadlessProtocol/Capabilities.swift | 4 + .../Sources/HeadlessProtocol/HostCore.swift | 2 + .../Sources/HeadlessProtocol/Protocol.swift | 10 ++ .../HeadlessProtocol/ProtocolSchema.swift | 18 +++ .../Resources/AgentRuntime.js | 57 ++++++++- apps/headless/Tests/Fixtures/select.html | 42 +++++++ .../HeadlessProtocolTests/ProtocolTests.swift | 107 ++++++++++++++++- apps/headless/Tests/agent-runtime.test.mjs | 94 +++++++++++++++ apps/headless/Tests/fixture-server.mjs | 1 + apps/headless/Tests/linux-e2e.sh | 31 ++++- apps/headless/Tests/macos-e2e.sh | 24 ++++ apps/headless/docs/COMMANDS.md | 17 ++- apps/headless/docs/P1.md | 4 +- apps/headless/docs/P2.md | 5 + docs/roadmap/architecture-decisions.md | 31 +++++ docs/roadmap/improvements-backlog.md | 4 +- packages/headless-npm/src/generated.ts | 105 ++++++++++++++++- .../src/headless_sdk/generated.py | 110 +++++++++++++++++- sdk/protocol-schema.json | 2 +- 25 files changed, 707 insertions(+), 18 deletions(-) create mode 100644 apps/headless/Tests/Fixtures/select.html diff --git a/.agents/skills/headless-computer-use/SKILL.md b/.agents/skills/headless-computer-use/SKILL.md index 70d2c3d..a4bbf58 100644 --- a/.agents/skills/headless-computer-use/SKILL.md +++ b/.agents/skills/headless-computer-use/SKILL.md @@ -32,6 +32,8 @@ macOS WebKit reports them as unsupported. 4. Start with `inspect --context summary --task "..."`, then use an outline, scoped text, or scoped actions only when the task needs them. 5. Prefer role/name targeting; otherwise use a ref from the latest inspection. + For native dropdowns, use `select` with exactly one exact `--label` or + `--value`; do not treat custom ARIA widgets as native selects. 6. After navigation or a substantial rerender, wait for the expected URL, text, or settled state and inspect again before the next interaction. 7. Capture evidence and diagnostics in proportion to the task. diff --git a/.agents/skills/headless-computer-use/references/commands.md b/.agents/skills/headless-computer-use/references/commands.md index a299ee1..a826629 100644 --- a/.agents/skills/headless-computer-use/references/commands.md +++ b/.agents/skills/headless-computer-use/references/commands.md @@ -30,6 +30,8 @@ headless --session NAME click REF headless --session NAME click --role ROLE --name NAME headless --session NAME fill REF "TEXT" headless --session NAME fill REF -- "--json stays literal" +headless --session NAME select REF --label LABEL +headless --session NAME select --role combobox --name NAME --value VALUE headless --session NAME upload REF --artifact FILE headless --session NAME upload --role textbox --name NAME --artifact FILE headless --session NAME press KEY @@ -46,9 +48,10 @@ large pages, request `outline`, select a returned `@rN` region, then use bound the result; check `omitted` before assuming it describes the whole page. Use `click --role ... --name ...` for unique accessible controls. Use an `@eN` ref from the latest inspection when role/name is ambiguous. Inspect again after -navigation or a large rerender. File inputs advertise `upload` for an existing -private artifact-store basename. Upload never accepts or imports a filesystem -path. Ask before uploading, as in [safety.md](safety.md). +navigation or a large rerender. Native single-select controls advertise +`select`; use exactly one exact `--label` or `--value`. File inputs advertise +`upload` for an existing private artifact-store basename. Upload never accepts +or imports a filesystem path. Ask before uploading, as in [safety.md](safety.md). Pass fill text as one quoted shell argument so whitespace is preserved. Put `--` before a value that contains a literal global flag such as `--json` or diff --git a/apps/headless/Host/AgentBridge.swift b/apps/headless/Host/AgentBridge.swift index 610b4f5..c186529 100644 --- a/apps/headless/Host/AgentBridge.swift +++ b/apps/headless/Host/AgentBridge.swift @@ -66,6 +66,15 @@ extension BrowserWindowController { return try callAgent("return globalThis.__headlessAgent.fill(args);", arguments: ["args": args]) } + func agentSelect(parameters: [String: JSONValue]) throws -> JSONValue { + var args = try browserTargetArguments(parameters) + if let label = parameters["label"]?.stringValue { args["label"] = label } + if let value = parameters["value"]?.stringValue { args["value"] = value } + return try callAgent( + "return globalThis.__headlessAgent.select(args);", arguments: ["args": args] + ) + } + func agentAuthenticationState() throws -> JSONValue { try callAgent("return globalThis.__headlessAgent.authentication();") } @@ -544,6 +553,9 @@ extension BrowserWindowController: BrowserEngineSession { func hostFill(parameters: [String: JSONValue]) throws -> JSONValue { try agentFill(parameters: parameters) } + func hostSelect(parameters: [String: JSONValue]) throws -> JSONValue { + try agentSelect(parameters: parameters) + } func hostPress(parameters: [String: JSONValue]) throws -> JSONValue { try agentPress(parameters: parameters) } diff --git a/apps/headless/LinuxHost/BrowserProcess.swift b/apps/headless/LinuxHost/BrowserProcess.swift index 364084b..e7ac866 100644 --- a/apps/headless/LinuxHost/BrowserProcess.swift +++ b/apps/headless/LinuxHost/BrowserProcess.swift @@ -804,6 +804,15 @@ final class LinuxBrowserSession: @unchecked Sendable { ]) } + func select(parameters: [String: JSONValue]) throws -> JSONValue { + var args = try browserTargetArguments(parameters) + if let label = parameters["label"]?.stringValue { args["label"] = label } + if let value = parameters["value"]?.stringValue { args["value"] = value } + return try evaluate( + "return globalThis.__headlessAgent.select(args);", input: ["args": args] + ) + } + func authenticationState() throws -> JSONValue { try evaluate("return globalThis.__headlessAgent.authentication();") } diff --git a/apps/headless/LinuxHost/main.swift b/apps/headless/LinuxHost/main.swift index 0dc2c2d..f1d0fe3 100644 --- a/apps/headless/LinuxHost/main.swift +++ b/apps/headless/LinuxHost/main.swift @@ -91,6 +91,9 @@ final class ChromiumBrowserEngineSession: BrowserEngineSession { func hostFill(parameters: [String: JSONValue]) throws -> JSONValue { try browserSession.fill(parameters: parameters) } + func hostSelect(parameters: [String: JSONValue]) throws -> JSONValue { + try browserSession.select(parameters: parameters) + } func hostUpload(parameters: [String: JSONValue], artifactURL: URL) throws -> JSONValue { try browserSession.upload(parameters: parameters, artifactURL: artifactURL) } diff --git a/apps/headless/Sources/HeadlessProtocol/CLI.swift b/apps/headless/Sources/HeadlessProtocol/CLI.swift index d253137..6d1e4d7 100644 --- a/apps/headless/Sources/HeadlessProtocol/CLI.swift +++ b/apps/headless/Sources/HeadlessProtocol/CLI.swift @@ -158,6 +158,8 @@ public struct CLIParser { return remote(.fill, session: session, parameters: [ "target": .string(arguments[0]), "value": .string(arguments[1]), ], jsonOutput: jsonOutput) + case "select": + return try parseSelect(arguments, session: session, jsonOutput: jsonOutput) case "press": guard arguments.count == 1 else { throw CLIParseError.missingArgument("KEY") } return remote(.press, session: session, parameters: ["key": .string(arguments[0])], jsonOutput: jsonOutput) @@ -401,6 +403,24 @@ public struct CLIParser { return remote(.upload, session: session, parameters: parameters, jsonOutput: jsonOutput) } + private func parseSelect( + _ arguments: [String], session: String?, jsonOutput: Bool + ) throws -> CLIInvocation { + var args = arguments + let label = try removeOption("--label", from: &args) + let value = try removeOption("--value", from: &args) + guard (label == nil) != (value == nil) else { + throw CLIParseError.missingArgument("exactly one of --label or --value") + } + let invocation = try parseTargeted( + .select, arguments: args, session: session, jsonOutput: jsonOutput + ) + var parameters = invocation.request?.parameters ?? [:] + if let label { parameters["label"] = .string(label) } + if let value { parameters["value"] = .string(value) } + return remote(.select, session: session, parameters: parameters, jsonOutput: jsonOutput) + } + private func parseTargeted( _ command: CommandName, arguments: [String], @@ -852,6 +872,8 @@ Commands: [--within @rN] [--limit N] [--budget TOKENS] [--depth N] [--text] click REF | click --role ROLE [--name NAME] fill REF TEXT | fill REF -- TEXT_WITH_LITERAL_FLAGS | press KEY + select REF --label LABEL | select REF --value VALUE + select --role ROLE [--name NAME] (--label LABEL | --value VALUE) upload REF --artifact FILE | upload --role ROLE [--name NAME] --artifact FILE scroll [up|down|top|bottom] [--amount PX] back | reload diff --git a/apps/headless/Sources/HeadlessProtocol/Capabilities.swift b/apps/headless/Sources/HeadlessProtocol/Capabilities.swift index 1d12ae9..2a3e237 100644 --- a/apps/headless/Sources/HeadlessProtocol/Capabilities.swift +++ b/apps/headless/Sources/HeadlessProtocol/Capabilities.swift @@ -24,6 +24,7 @@ public struct BrowserEngineCapabilities: Sendable { public let qaDiagnosticSynchronization: String public let screenshotClipboard: Bool public let inputDispatch: String + public let selectDispatch: String public let normalProfileStorage: String public let fileUpload: Bool @@ -68,6 +69,7 @@ public struct BrowserEngineCapabilities: Sendable { "screenshotClipboard": .bool(screenshotClipboard), "tourTimeoutMs": .number(65_000), "inputDispatch": .string(inputDispatch), + "selectDispatch": .string(selectDispatch), "fileUpload": .bool(fileUpload), "normalProfile": .object([ "persistent": .bool(true), @@ -113,6 +115,7 @@ public struct BrowserEngineCapabilities: Sendable { qaDiagnosticSynchronization: "best-effort-page-world-observer", screenshotClipboard: true, inputDispatch: "synthetic-dom", + selectDispatch: "synthetic-dom", normalProfileStorage: "persistent-wkwebsite-data-store", fileUpload: false ) @@ -136,6 +139,7 @@ public struct BrowserEngineCapabilities: Sendable { qaDiagnosticSynchronization: "runtime-round-trip-flush", screenshotClipboard: false, inputDispatch: "trusted-cdp", + selectDispatch: "synthetic-dom", normalProfileStorage: "private-xdg-data-directory", fileUpload: true ) diff --git a/apps/headless/Sources/HeadlessProtocol/HostCore.swift b/apps/headless/Sources/HeadlessProtocol/HostCore.swift index 2654348..c4dc887 100644 --- a/apps/headless/Sources/HeadlessProtocol/HostCore.swift +++ b/apps/headless/Sources/HeadlessProtocol/HostCore.swift @@ -37,6 +37,7 @@ public protocol BrowserEngineSession: AnyObject { func hostInspect(parameters: [String: JSONValue]) throws -> JSONValue func hostClick(parameters: [String: JSONValue]) throws -> JSONValue func hostFill(parameters: [String: JSONValue]) throws -> JSONValue + func hostSelect(parameters: [String: JSONValue]) throws -> JSONValue func hostPress(parameters: [String: JSONValue]) throws -> JSONValue func hostScroll(parameters: [String: JSONValue]) throws -> JSONValue func hostWait(parameters: [String: JSONValue]) throws -> JSONValue @@ -554,6 +555,7 @@ public final class HostCore: @unchecked Sendable { case .inspect: return try session.hostInspect(parameters: request.parameters) case .click: return try session.hostClick(parameters: request.parameters) case .fill: return try session.hostFill(parameters: request.parameters) + case .select: return try session.hostSelect(parameters: request.parameters) case .upload: guard let artifact = request.parameters["artifact"]?.stringValue else { throw HostError(code: .missingParameter, message: "Artifact name is required.") diff --git a/apps/headless/Sources/HeadlessProtocol/Protocol.swift b/apps/headless/Sources/HeadlessProtocol/Protocol.swift index 5274520..5409fc2 100644 --- a/apps/headless/Sources/HeadlessProtocol/Protocol.swift +++ b/apps/headless/Sources/HeadlessProtocol/Protocol.swift @@ -64,6 +64,7 @@ public enum CommandName: String, Codable, CaseIterable, Sendable { case inspect case click case fill + case select case upload case press case scroll @@ -265,6 +266,15 @@ public struct CommandRequest: Codable, Equatable, Sendable { try target(allowValue: false) case .fill: try target(allowValue: true) + case .select: + try target(allowValue: false) + let label = try string("label", maximumBytes: 1_000) + let value = try string("value", maximumBytes: 1_000) + guard (label == nil) != (value == nil) else { + throw ProtocolValidationError.invalidParameter( + "Choose exactly one option label or value" + ) + } case .upload: try target(allowValue: false) if let artifact = try string("artifact", required: true, maximumBytes: 128) { diff --git a/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift b/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift index 95554ec..0e10cc1 100644 --- a/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift +++ b/apps/headless/Sources/HeadlessProtocol/ProtocolSchema.swift @@ -320,6 +320,11 @@ public func protocolResultDefinition(for command: CommandName) -> ProtocolResult ]) case .fill: return result("Fill", [resultField("filled", .string), resultField("valueLength", .number)]) + case .select: + return result("Select", [ + resultField("selected", .string), resultField("role", .string), + resultField("name", .string), resultField("optionIndex", .number), + ]) case .upload: return result("Upload", [ resultField("uploaded", .string), resultField("role", .string), @@ -701,6 +706,19 @@ public let protocolCommandDefinitions: [CommandName: ProtocolCommandDefinition] untrusted: true, constraints: ["exactly one target reference or semantic role/name target"] ), + command( + .select, + targetParameters + [ + string("label", maximumBytes: 1_000, sensitive: true), + string("value", maximumBytes: 1_000, sensitive: true), + ], + untrusted: true, + constraints: [ + "exactly one target reference or semantic role/name target", + "exactly one option label or value", + "native single-selection HTML select controls only", + ] + ), command( .upload, targetParameters + [string("artifact", required: true, maximumBytes: 128)], diff --git a/apps/headless/Sources/HeadlessProtocol/Resources/AgentRuntime.js b/apps/headless/Sources/HeadlessProtocol/Resources/AgentRuntime.js index 9d7da88..f2ab9fc 100644 --- a/apps/headless/Sources/HeadlessProtocol/Resources/AgentRuntime.js +++ b/apps/headless/Sources/HeadlessProtocol/Resources/AgentRuntime.js @@ -145,7 +145,7 @@ if (!globalThis.__headlessAgent) { const elementRole = role(element); // Only advertise verbs implemented by the public Headless protocol. // Unsupported controls can still appear for context, but must not route - // an agent toward nonexistent select/slide commands. File inputs advertise + // an agent toward nonexistent verbs. File inputs advertise // upload only when the host injected __headlessFileUpload, never fill or // click as the primary verb. if (element instanceof HTMLInputElement) { @@ -161,6 +161,8 @@ if (!globalThis.__headlessAgent) { if (tag === 'a' && element.hasAttribute('href')) hints.push('click'); if (tag === 'button' || elementRole === 'button') hints.push('click'); if (tag === 'summary' || elementRole === 'tab' || elementRole === 'menuitem') hints.push('click'); + if (element instanceof HTMLSelectElement && !element.multiple && !element.disabled + && element.getAttribute('aria-disabled') !== 'true') hints.push('select'); if (element instanceof HTMLTextAreaElement || element.isContentEditable) hints.push('fill'); if (element.tabIndex >= 0 && hints.length === 0) hints.push('click'); return Array.from(new Set(hints)); @@ -743,6 +745,57 @@ if (!globalThis.__headlessAgent) { element.dispatchEvent(new Event('change', {bubbles: true})); return {filled: refFor(element), valueLength: String(args.value).length}; }; + const select = args => { + const element = target(args); + if (!(element instanceof HTMLSelectElement)) { + fail('INVALID_INPUT', 'INVALID_INPUT: target is not a native select'); + } + if (element.multiple) { + fail('INVALID_INPUT', 'INVALID_INPUT: multi-select controls are not supported'); + } + if (element.disabled || element.getAttribute('aria-disabled') === 'true') { + fail('INVALID_INPUT', 'INVALID_INPUT: select control is disabled'); + } + const hasLabel = typeof args.label === 'string'; + const hasValue = typeof args.value === 'string'; + if (hasLabel === hasValue) { + fail('INVALID_INPUT', 'INVALID_INPUT: choose exactly one option label or value'); + } + const wanted = hasLabel ? normalize(args.label) : args.value; + const matches = Array.from(element.options).filter(option => + hasLabel ? normalize(option.label || option.textContent) === wanted : option.value === wanted + ); + if (matches.length === 0) { + fail('INVALID_INPUT', 'INVALID_INPUT: matching option was not found'); + } + if (matches.length > 1) { + fail('INVALID_INPUT', `INVALID_INPUT: option matcher is ambiguous (${matches.length} matches)`); + } + const option = matches[0]; + const parentDisabled = option.parentElement instanceof HTMLOptGroupElement + && option.parentElement.disabled; + if (option.disabled || parentDisabled) { + fail('INVALID_INPUT', 'INVALID_INPUT: matching option is disabled'); + } + const optionIndex = Array.from(element.options).indexOf(option); + if (optionIndex < 0) fail('INVALID_INPUT', 'INVALID_INPUT: matching option is detached'); + if (element.selectedIndex !== optionIndex) { + const setter = Object.getOwnPropertyDescriptor( + HTMLSelectElement.prototype, 'selectedIndex' + )?.set; + if (!setter) fail('OPERATION_FAILED', 'OPERATION_FAILED: native select setter unavailable'); + element.focus({preventScroll: false}); + setter.call(element, optionIndex); + if (element.selectedIndex !== optionIndex) { + fail('OPERATION_FAILED', 'OPERATION_FAILED: native select did not accept the option'); + } + element.dispatchEvent(new Event('input', {bubbles: true})); + element.dispatchEvent(new Event('change', {bubbles: true})); + } + return { + selected: refFor(element), role: role(element), name: name(element), optionIndex + }; + }; const credentialFill = args => { const initialOrigin = String(location.origin || ''); if (initialOrigin !== args.origin) throw new Error('AUTH_ORIGIN_CHANGED'); @@ -1036,7 +1089,7 @@ if (!globalThis.__headlessAgent) { return {count: document.getAnimations().length, animations: all, truncated: document.getAnimations().length > all.length}; }; return { - snapshot, click, fill, credentialFill, finishCredentialFill, press, inputTarget, fileInput, fileInputMetadata, + snapshot, click, fill, select, credentialFill, finishCredentialFill, press, inputTarget, fileInput, fileInputMetadata, authentication, scroll, state, tour, screenshotPlan, scrollToCapturePoint, rectangle, styles, storage, performance: performanceSummary, animations }; diff --git a/apps/headless/Tests/Fixtures/select.html b/apps/headless/Tests/Fixtures/select.html new file mode 100644 index 0000000..d2c577b --- /dev/null +++ b/apps/headless/Tests/Fixtures/select.html @@ -0,0 +1,42 @@ + + + + + Native select fixture + + +
+

Native select fixture

+ + + + + + + + +
Canada
+ country=none events=none +
+ + + diff --git a/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift b/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift index a755b17..195d656 100644 --- a/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift +++ b/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift @@ -336,6 +336,11 @@ private final class TestBrowserSession: BrowserEngineSession { } func hostClick(parameters: [String: JSONValue]) throws -> JSONValue { .object(["clicked": .bool(true)]) } func hostFill(parameters: [String: JSONValue]) throws -> JSONValue { .object(["filled": .bool(true)]) } + private(set) var lastSelectParameters: [String: JSONValue]? + func hostSelect(parameters: [String: JSONValue]) throws -> JSONValue { + lastSelectParameters = parameters + return .object(["selected": .bool(true), "parameters": .object(parameters)]) + } private(set) var lastUploadPath: String? func hostUpload(parameters: [String: JSONValue], artifactURL: URL) throws -> JSONValue { lastUploadPath = artifactURL.path @@ -960,6 +965,34 @@ struct ProtocolTests { parameters: ["target": .string("@e../1")] ).validate() } + try CommandRequest( + id: "valid-select", command: .select, + parameters: ["target": .string("@e1"), "label": .string("Canada")] + ).validate() + try expectThrows("select should require one option matcher") { + try CommandRequest( + id: "select-without-option", command: .select, + parameters: ["target": .string("@e1")] + ).validate() + } + try expectThrows("select should reject conflicting option matchers") { + try CommandRequest( + id: "select-conflicting-option", command: .select, + parameters: [ + "target": .string("@e1"), "label": .string("Canada"), + "value": .string("CA"), + ] + ).validate() + } + try expectThrows("select option matchers should stay bounded") { + try CommandRequest( + id: "select-unbounded-option", command: .select, + parameters: [ + "target": .string("@e1"), + "label": .string(String(repeating: "x", count: 1_001)), + ] + ).validate() + } try expectThrows("screenshot traversal should be rejected") { try CommandRequest( id: "bad-output", command: .screenshot, @@ -1145,6 +1178,46 @@ struct ProtocolTests { ) } + static func cliSemanticSelect() throws { + let byLabel = try CLIParser().parse([ + "select", "--role", "combobox", "--name", "Country", + "--label", "Canada", + ]) + try expect(byLabel.request?.command == .select, "select command should parse") + try expect( + byLabel.request?.parameters == [ + "role": .string("combobox"), "name": .string("Country"), + "label": .string("Canada"), + ], + "semantic select target and label should parse" + ) + let byValue = try CLIParser().parse(["select", "@e4", "--value", "CA"]) + try expect( + byValue.request?.parameters == [ + "target": .string("@e4"), "value": .string("CA"), + ], + "ref select target and value should parse" + ) + let literal = try CLIParser().parse(["select", "@e4", "--label", "--", "--json"]) + try expect( + literal.request?.parameters["label"] == .string("--json") && !literal.jsonOutput, + "select labels after the sentinel should remain literal" + ) + try expectThrows("select should require an option matcher") { + _ = try CLIParser().parse(["select", "@e4"]) + } + try expectThrows("select should reject two option matchers") { + _ = try CLIParser().parse([ + "select", "@e4", "--label", "Canada", "--value", "CA", + ]) + } + try expectThrows("select should reject conflicting target forms") { + _ = try CLIParser().parse([ + "select", "@e4", "--role", "combobox", "--label", "Canada", + ]) + } + } + static func cliInspectContextAndTask() throws { let invocation = try CLIParser().parse([ "inspect", "--context", "text", "--task", "search open ai", @@ -2456,6 +2529,11 @@ struct ProtocolTests { parameters: ["target": .string("@e1"), "value": .string(secret)] ) try expect(fill == nil, "fill values must never become replayable flow steps") + let select = flowStepIfSafe( + command: .select, + parameters: ["target": .string("@e1"), "value": .string(secret)] + ) + try expect(select == nil, "select option matchers must not become replayable flow steps") let uploadStep = flowStepIfSafe( command: .upload, parameters: ["target": .string("@e1"), "artifact": .string("resume.pdf")] @@ -2476,7 +2554,10 @@ struct ProtocolTests { flowStepIfSafe(command: $0, parameters: safeParameters) } let encoded = try ProtocolCodec.encoder.encode(RecordedFlow(commands: commands)) - try expect(!String(decoding: encoded, as: UTF8.self).contains(secret), "serialized flows must omit fill values") + try expect( + !String(decoding: encoded, as: UTF8.self).contains(secret), + "serialized flows must omit fill values and select matchers" + ) } static func visualComparisonInvokesBoundedTool() throws { @@ -2807,6 +2888,11 @@ struct ProtocolTests { chromiumFeatures["inputDispatch"] == .string("trusted-cdp"), "Chromium should declare trusted CDP input" ) + try expect( + webkitFeatures["selectDispatch"] == .string("synthetic-dom") + && chromiumFeatures["selectDispatch"] == .string("synthetic-dom"), + "both engines should declare fixed isolated-world select dispatch" + ) try expect( webkitFeatures["fileUpload"] == .bool(false), "WebKit should declare file upload unsupported until a native attach path exists" @@ -3256,7 +3342,10 @@ struct ProtocolTests { "partial series reservations should be removed after a later collision" ) try expect(RecordingFormat.webm.videoCodec == "vp9", "recording metadata should report the codec, not encoder") - try expect(!agentRuntimeJavaScript.contains("hints.push('select')"), "inspect must not advertise a missing select command") + try expect( + agentRuntimeJavaScript.contains("hints.push('select')"), + "inspect must advertise the implemented native select command" + ) try expect( agentRuntimeJavaScript.contains("__headlessFileUpload"), "upload hints must be gated on engine file-upload support" @@ -4625,6 +4714,19 @@ struct ProtocolTests { "agent control should be enabled at creation and before command execution" ) + let selectParameters: [String: JSONValue] = [ + "role": .string("combobox"), "name": .string("Country"), + "label": .string("Canada"), + ] + let selected = core.handle(CommandRequest( + command: .select, session: "secondary", parameters: selectParameters + )) + try expect(selected.ok, "shared host select dispatch should succeed") + try expect( + engine.createdSessions[1].lastSelectParameters == selectParameters, + "HostCore should pass validated select parameters to the engine" + ) + let capture = core.handle(CommandRequest(command: .captureInfo, session: "secondary")) guard capture.ok, case .object(let captureResult) = capture.result else { throw TestFailure(description: "shared capture info should succeed") @@ -4827,6 +4929,7 @@ struct ProtocolTests { ("CLI visit", cliVisit), ("CLI fill literal value", cliFillPreservesLiteralValue), ("CLI semantic click", cliSemanticClick), + ("CLI semantic select", cliSemanticSelect), ("CLI inspect context and task", cliInspectContextAndTask), ("CLI conflicting target", cliRejectsConflictingClickTarget), ("CLI settled wait", cliWaitDefaultsToSettled), diff --git a/apps/headless/Tests/agent-runtime.test.mjs b/apps/headless/Tests/agent-runtime.test.mjs index 5f7d2ba..8652d1d 100644 --- a/apps/headless/Tests/agent-runtime.test.mjs +++ b/apps/headless/Tests/agent-runtime.test.mjs @@ -176,21 +176,47 @@ const controls = window.document.createElement('section'); controls.innerHTML = ` + + + + + + + +
Canada
Unsafe
Read only
`; window.document.body.prepend(controls); const button = controls.querySelector('button'); const input = controls.querySelector('input'); +const country = controls.querySelector('#runtime-country'); +const duplicateCountry = controls.querySelector('[aria-label="Duplicate country"]'); +const disabledCountry = controls.querySelector('[aria-label="Disabled country"]'); +const disabledOption = controls.querySelector('[aria-label="Disabled option"]'); +const disabledGroup = controls.querySelector('[aria-label="Disabled group"]'); +const manyCountries = controls.querySelector('[aria-label="Many countries"]'); button.getBoundingClientRect = () => ({x: 20, y: 20, top: 20, left: 20, right: 120, bottom: 60, width: 100, height: 40}); input.getBoundingClientRect = () => ({x: 20, y: 80, top: 80, left: 20, right: 220, bottom: 120, width: 200, height: 40}); let clicks = 0; let inputs = 0; let changes = 0; +const selectionEvents = []; const pressed = []; button.addEventListener('click', () => { clicks += 1; }); input.addEventListener('input', () => { inputs += 1; }); input.addEventListener('change', () => { changes += 1; }); +country.addEventListener('input', event => selectionEvents.push(`input:${event.isTrusted}`)); +country.addEventListener('change', event => selectionEvents.push(`change:${event.isTrusted}`)); input.addEventListener('keydown', event => pressed.push(`down:${event.key}`)); input.addEventListener('keyup', event => pressed.push(`up:${event.key}`)); @@ -217,6 +243,74 @@ assert.equal(filled.value, undefined, 'fill responses must not echo values'); assert.equal(input.value, 'private value'); assert.equal(inputs, 1); assert.equal(changes, 1); +const selectActions = agent.snapshot(false, false, { + context: 'actions', task: 'Country', limit: 20, +}); +const countryAction = selectActions.elements.find(element => element.name === 'Country'); +assert.deepEqual(Array.from(countryAction?.actions || []), ['select']); +assert(!selectActions.elements.find(element => element.name === 'Disabled country')?.actions.includes('select')); +assert(!selectActions.elements.find(element => element.name === 'Many countries')?.actions.includes('select')); + +const selectedByLabel = agent.select({role: 'combobox', name: 'Country', label: 'Canada'}); +assert.equal(selectedByLabel.selected, countryAction.ref); +assert.equal(selectedByLabel.optionIndex, 1); +assert.equal(selectedByLabel.label, undefined, 'select responses must not echo option labels'); +assert.equal(selectedByLabel.value, undefined, 'select responses must not echo option values'); +assert.equal(country.value, 'CA'); +assert.deepEqual(selectionEvents, ['input:false', 'change:false']); + +const selectedByValue = agent.select({target: countryAction.ref, value: 'CA'}); +assert.equal(selectedByValue.optionIndex, 1); +assert.deepEqual( + selectionEvents, + ['input:false', 'change:false'], + 'selecting the active option should be idempotent and emit no duplicate events', +); +assert.throws( + () => agent.select({role: 'combobox', name: 'Duplicate country', label: 'Canada'}), + error => error.headlessCode === 'INVALID_INPUT' && /ambiguous/.test(error.message), +); +assert.equal(duplicateCountry.selectedIndex, 0, 'ambiguous selection must not mutate the control'); +assert.throws( + () => agent.select({role: 'combobox', name: 'Disabled country', label: 'Canada'}), + error => error.headlessCode === 'INVALID_INPUT' && /control is disabled/.test(error.message), +); +assert.equal(disabledCountry.selectedIndex, 0); +assert.throws( + () => agent.select({role: 'combobox', name: 'Disabled option', label: 'Canada'}), + error => error.headlessCode === 'INVALID_INPUT' && /option is disabled/.test(error.message), +); +assert.equal(disabledOption.selectedIndex, 0); +assert.throws( + () => agent.select({role: 'combobox', name: 'Disabled group', label: 'Canada'}), + error => error.headlessCode === 'INVALID_INPUT' && /option is disabled/.test(error.message), +); +assert.equal(disabledGroup.selectedIndex, 0); +const manyCountriesBefore = manyCountries.selectedIndex; +assert.throws( + () => agent.select({role: 'combobox', name: 'Many countries', label: 'Canada'}), + error => error.headlessCode === 'INVALID_INPUT' && /multi-select/.test(error.message), +); +assert.equal(manyCountries.selectedIndex, manyCountriesBefore); +assert.throws( + () => agent.select({role: 'combobox', name: 'Custom country', label: 'Canada'}), + error => error.headlessCode === 'INVALID_INPUT' && /not a native select/.test(error.message), +); +assert.throws( + () => agent.select({role: 'combobox', name: 'Country', label: 'Missing'}), + error => error.headlessCode === 'INVALID_INPUT' && /not found/.test(error.message), +); +assert.equal(country.selectedIndex, 1, 'failed option matching must preserve the prior selection'); + +country.style.display = 'none'; +agent.snapshot(false, false, {context: 'actions', limit: 8}); +country.style.display = ''; +assert.throws( + () => agent.select({target: countryAction.ref, value: 'US'}), + error => error.headlessCode === 'ELEMENT_NOT_FOUND' && /expired/.test(error.message), +); +assert.equal(country.selectedIndex, 1, 'stale refs must fail before selection'); +input.focus(); assert.equal(agent.press('A').pressed, 'A'); assert.deepEqual(pressed, ['down:A', 'up:A']); assert.throws( diff --git a/apps/headless/Tests/fixture-server.mjs b/apps/headless/Tests/fixture-server.mjs index 99bd3e5..152c6d3 100644 --- a/apps/headless/Tests/fixture-server.mjs +++ b/apps/headless/Tests/fixture-server.mjs @@ -9,6 +9,7 @@ const routes = new Map([ ['/next', 'next.html'], ['/hostile', 'hostile.html'], ['/large-document', 'large-document.html'], + ['/select', 'select.html'], ['/auth-state', 'auth-state.html'], ['/auth-login', 'auth-login.html'], ['/file-upload', 'file-upload.html'], diff --git a/apps/headless/Tests/linux-e2e.sh b/apps/headless/Tests/linux-e2e.sh index 1e6b9d6..038c4cb 100755 --- a/apps/headless/Tests/linux-e2e.sh +++ b/apps/headless/Tests/linux-e2e.sh @@ -12,11 +12,12 @@ SUPERVISED_LAUNCHER_PID="" FIXTURE_ROOT="$(mktemp -d /tmp/headless-fixture.XXXXXX)" INSTALL_ROOT="$(mktemp -d /tmp/headless-install.XXXXXX)" -mkdir -p "$FIXTURE_ROOT/designers/dashboard" "$FIXTURE_ROOT/next" "$FIXTURE_ROOT/hostile" "$FIXTURE_ROOT/large-document" "$FIXTURE_ROOT/trusted-input" "$FIXTURE_ROOT/auth-state" "$FIXTURE_ROOT/auth-login" "$FIXTURE_ROOT/file-upload" "$FIXTURE_ROOT/api" "$FIXTURE_ROOT/allowlist-exits" "$FIXTURE_ROOT/allowlist-redirect" +mkdir -p "$FIXTURE_ROOT/designers/dashboard" "$FIXTURE_ROOT/next" "$FIXTURE_ROOT/hostile" "$FIXTURE_ROOT/large-document" "$FIXTURE_ROOT/select" "$FIXTURE_ROOT/trusted-input" "$FIXTURE_ROOT/auth-state" "$FIXTURE_ROOT/auth-login" "$FIXTURE_ROOT/file-upload" "$FIXTURE_ROOT/api" "$FIXTURE_ROOT/allowlist-exits" "$FIXTURE_ROOT/allowlist-redirect" cp /opt/headless/fixtures/dashboard.html "$FIXTURE_ROOT/designers/dashboard/index.html" cp /opt/headless/fixtures/next.html "$FIXTURE_ROOT/next/index.html" cp /opt/headless/fixtures/hostile.html "$FIXTURE_ROOT/hostile/index.html" cp /opt/headless/fixtures/large-document.html "$FIXTURE_ROOT/large-document/index.html" +cp /opt/headless/fixtures/select.html "$FIXTURE_ROOT/select/index.html" cp /opt/headless/fixtures/trusted-input.html "$FIXTURE_ROOT/trusted-input/index.html" cp /opt/headless/fixtures/auth-state.html "$FIXTURE_ROOT/auth-state/index.html" cp /opt/headless/fixtures/auth-login.html "$FIXTURE_ROOT/auth-login/index.html" @@ -372,6 +373,34 @@ TRUSTED_INPUT="$(headless --session qa inspect --text)" echo "$TRUSTED_INPUT" | grep -q 'input:true' echo "$TRUSTED_INPUT" | grep -q 'key:Enter:true' echo "$TRUSTED_INPUT" | grep -q 'click:true' +STEP="native-select" +headless --session qa visit http://127.0.0.1:41739/select/ | grep -q 'Native select fixture' +SELECT_RESULT="$(headless --session qa select --role combobox --name Country --label Canada)" +echo "$SELECT_RESULT" | grep -q '"optionIndex":1' +if echo "$SELECT_RESULT" | grep -Eq 'Canada|"CA"'; then + echo "select response exposed option label or value" >&2 + exit 1 +fi +headless --session qa wait --text 'country=CA events=input,change' --timeout 2000 | grep -q 'country=CA events=input,change' +headless --session qa select --role combobox --name Country --value US | grep -q '"optionIndex":2' +headless --session qa wait --text 'events=input,change,input,change' --timeout 2000 | grep -q 'country=US' +assert_select_rejected() { + SELECT_NAME="$1" + SELECT_MATCHER="$2" + SELECT_OPTION="$3" + if SELECT_ERROR="$(headless --session qa select --role combobox --name "$SELECT_NAME" "$SELECT_MATCHER" "$SELECT_OPTION" 2>&1)"; then + echo "invalid select case unexpectedly succeeded: $SELECT_NAME" >&2 + exit 1 + fi + echo "$SELECT_ERROR" | grep -q 'INVALID_INPUT' +} +assert_select_rejected 'Duplicate country' --label Canada +assert_select_rejected 'Disabled country' --label Canada +assert_select_rejected 'Disabled option' --label Canada +assert_select_rejected 'Disabled group' --label Canada +assert_select_rejected 'Many countries' --label Canada +assert_select_rejected 'Custom country' --label Canada +assert_select_rejected Country --label Missing STEP="file-upload" printf 'resume-fixture\n' > "$HEADLESS_ARTIFACT_DIR/resume.txt" chmod 600 "$HEADLESS_ARTIFACT_DIR/resume.txt" diff --git a/apps/headless/Tests/macos-e2e.sh b/apps/headless/Tests/macos-e2e.sh index 4a4281c..1bb002f 100755 --- a/apps/headless/Tests/macos-e2e.sh +++ b/apps/headless/Tests/macos-e2e.sh @@ -1405,6 +1405,30 @@ STEP="safe-input-download-report" echo "$BLOCKED_DOWNLOAD_REPORT" | grep -q '"kind":"download-blocked"' echo "$BLOCKED_DOWNLOAD_REPORT" | grep -q '/download.txt' "$CLI" --session qa qa clear | grep -q '"cleared"' +STEP="native-select" +"$CLI" --session qa visit "http://127.0.0.1:$PORT/select" | grep -q 'Native select fixture' +SELECT_RESULT="$("$CLI" --session qa select --role combobox --name Country --label Canada)" +echo "$SELECT_RESULT" | grep -q '"optionIndex":1' +if echo "$SELECT_RESULT" | grep -Eq 'Canada|"CA"'; then + echo "select response exposed option label or value" >&2 + fail +fi +"$CLI" --session qa wait --text 'country=CA events=input,change' --timeout 2000 | grep -q 'country=CA events=input,change' +"$CLI" --session qa select --role combobox --name Country --value US | grep -q '"optionIndex":2' +"$CLI" --session qa wait --text 'events=input,change,input,change' --timeout 2000 | grep -q 'country=US' +for SELECT_NAME in 'Duplicate country' 'Disabled country' 'Disabled option' 'Disabled group' 'Many countries' 'Custom country'; do + if SELECT_ERROR="$("$CLI" --session qa select --role combobox --name "$SELECT_NAME" --label Canada 2>&1)"; then + echo "invalid select case unexpectedly succeeded: $SELECT_NAME" >&2 + fail + fi + echo "$SELECT_ERROR" | grep -q 'INVALID_INPUT' +done +if SELECT_ERROR="$("$CLI" --session qa select --role combobox --name Country --label Missing 2>&1)"; then + echo "missing select option unexpectedly succeeded" >&2 + fail +fi +echo "$SELECT_ERROR" | grep -q 'INVALID_INPUT' +"$CLI" --session qa visit "http://127.0.0.1:$PORT/designers/dashboard" | grep -q 'Designers Dashboard' STEP="artifacts-visual" "$CLI" --session qa screenshot --output viewport.png | grep -q '"name":"viewport.png"' "$CLI" --session qa screenshot --full-page --output full-page.png | grep -q '"name":"full-page.png"' diff --git a/apps/headless/docs/COMMANDS.md b/apps/headless/docs/COMMANDS.md index 725eb36..e86926c 100644 --- a/apps/headless/docs/COMMANDS.md +++ b/apps/headless/docs/COMMANDS.md @@ -218,6 +218,8 @@ inspect [--context summary|outline|text|actions|full] [--task TEXT] [--within @rN] [--limit N] [--budget TOKENS] [--depth N] [--text] click REF | click --role ROLE [--name NAME] fill REF TEXT | fill REF -- TEXT_WITH_LITERAL_FLAGS | press KEY +select REF --label LABEL | select REF --value VALUE +select --role ROLE [--name NAME] (--label LABEL | --value VALUE) upload REF --artifact FILE | upload --role ROLE [--name NAME] --artifact FILE scroll [up|down|top|bottom] [--amount PX] back | reload @@ -237,11 +239,20 @@ back | reload the most recent inspection and are reissued on every inspect; region references (`@rN`) stay resolvable so you can outline first and scope later. See "Reference lifetime" in P1.md for the full contract. -- `click`, `fill`, `upload`, and `press` accept either a reference or a semantic - target (`--role`/`--name`). On Linux these dispatch trusted CDP input events; - WebKit uses synthetic input, and capabilities declare the difference. +- `click`, `select`, and `upload` accept either a reference or a semantic target + (`--role`/`--name`). `fill` accepts a reference; `press` acts on the focused + control. On Linux click, fill, and key input dispatch trusted CDP events; + WebKit uses synthetic input. Native select dispatch is a fixed synthetic DOM + operation on both engines, declared separately by capabilities. - `fill REF -- value` keeps leading dashes in the value. Flow recordings never record fill values. +- `select` supports native single-selection HTML controls only. Choose exactly + one exact option `--label` after whitespace normalization or exact `--value`. + Disabled controls and options, disabled optgroups, multi-selects, custom ARIA + widgets, missing options, and ambiguous options fail without mutation. A + successful change dispatches `input` followed by `change`; selecting the + current option is idempotent. Responses do not echo option labels or values, + and flow recordings do not record select commands. - `upload` attaches a file that already lives in the private artifact store. `--artifact` is a validated basename only, never a filesystem path. File bytes never travel on the socket. Linux Chromium attaches through diff --git a/apps/headless/docs/P1.md b/apps/headless/docs/P1.md index fa3ad97..8691afb 100644 --- a/apps/headless/docs/P1.md +++ b/apps/headless/docs/P1.md @@ -141,7 +141,9 @@ Inspection supports progressive disclosure across five contexts: stable region references such as `@r4`. - `text` returns deduplicated semantic snippets instead of a flat body dump. - `actions` returns visible controls and only executable protocol verbs in each - control's `actions` field (`click`, `fill`, or `upload`). + control's `actions` field (`click`, `fill`, `select`, or `upload`). Native + single-selection HTML controls advertise `select`; custom ARIA and + multi-select widgets do not. - `full` preserves the broader page/media snapshot and explicit `--text` escape hatch. diff --git a/apps/headless/docs/P2.md b/apps/headless/docs/P2.md index 3a1abea..ca9a673 100644 --- a/apps/headless/docs/P2.md +++ b/apps/headless/docs/P2.md @@ -79,6 +79,11 @@ Network emulation uses Chromium CDP. Mocks match exact normalized HTTP(S) URLs, have a 64 KiB response-body limit, and add no persistent proxy or listener. They are cleared explicitly or when the browser session closes. +Native single-selection HTML controls use one fixed isolated-world `select` +operation on both engines. The capability matrix reports this separately as +`selectDispatch: synthetic-dom`, so Chromium's `inputDispatch: trusted-cdp` +claim remains limited to click, fill, and key input. + The Linux host accepts only a qualified non-Snap runtime for this CDP pipe. Automatic selection prefers an installed bundled runtime and native browser binaries before PATH wrappers. The Docker P2 flow explicitly selects Debian's diff --git a/docs/roadmap/architecture-decisions.md b/docs/roadmap/architecture-decisions.md index 6810cc7..feaf9a8 100644 --- a/docs/roadmap/architecture-decisions.md +++ b/docs/roadmap/architecture-decisions.md @@ -1060,6 +1060,36 @@ a general product claim. --- +## 35. Select is a fixed native-control operation, not general evaluation + +**Decision:** add one portable `select` command for native single-selection +HTML `; readonly "upload": Untrusted; readonly "press": Untrusted; readonly "scroll": Untrusted; @@ -1404,6 +1422,81 @@ export const COMMAND_METADATA = { "parameterPresentOverrides": {} } }, + "select": { + "capabilityNegotiated": false, + "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": 1000, + "name": "label", + "required": false, + "sensitive": true, + "type": "string" + }, + { + "maximumBytes": 1000, + "name": "value", + "required": false, + "sensitive": true, + "type": "string" + } + ], + "result": { + "mayContainUntrustedContent": true, + "schema": { + "additionalProperties": true, + "fields": [ + { + "name": "selected", + "required": true, + "type": "string" + }, + { + "name": "role", + "required": true, + "type": "string" + }, + { + "name": "name", + "required": true, + "type": "string" + }, + { + "name": "optionIndex", + "required": true, + "type": "number" + } + ], + "name": "Select", + "type": "object" + } + }, + "scope": "session", + "timeout": { + "defaultMilliseconds": 15000, + "parameterPresentOverrides": {} + } + }, "upload": { "capabilityNegotiated": true, "parameters": [ @@ -3293,6 +3386,10 @@ export abstract class GeneratedCommandClient { return this.invoke("fill", parameters, options); } + select(parameters: SelectParameters = {}, options?: CommandOptions): Promise> { + return this.invoke("select", parameters, options); + } + upload(parameters: UploadParameters, options?: CommandOptions): Promise> { return this.invoke("upload", parameters, options); } @@ -3450,6 +3547,10 @@ export abstract class GeneratedSessionCommandClient { return this.invoke("fill", parameters, options); } + select(parameters: SelectParameters = {}, options?: CommandOptions): Promise> { + return this.invoke("select", parameters, options); + } + upload(parameters: UploadParameters, options?: CommandOptions): Promise> { return this.invoke("upload", parameters, options); } diff --git a/packages/headless-python/src/headless_sdk/generated.py b/packages/headless-python/src/headless_sdk/generated.py index 6baf630..a5b3311 100644 --- a/packages/headless-python/src/headless_sdk/generated.py +++ b/packages/headless-python/src/headless_sdk/generated.py @@ -13,7 +13,7 @@ PROTOCOL_SCHEMA_VERSION = 1 MAXIMUM_MESSAGE_BYTES = 1048576 MAXIMUM_COMMAND_TIMEOUT_SECONDS = 125.0 -PROTOCOL_SCHEMA_SHA256 = '0eda66ca7a4ee067e6289ff70f96fd51e55361ca4941f26b03944294cd4dcdf3' +PROTOCOL_SCHEMA_SHA256 = '0ec7caa5ef47c4068411298f459fbd7317e8f805e2e41aab93f1c70d801bb73d' PROTOCOL_FIXTURES_SHA256 = '0b51ffaa2d3e3aaf0c32adcfeb02c180dcbe44face0d49e1c332b69f403ae062' RESPONSE_ADDITIONAL_PROPERTIES = True COMMAND_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', 'PAGINATION_CURSOR_EXPIRED', 'PAGINATION_CURSOR_INVALID', 'PAGINATION_CURSOR_SCOPE_MISMATCH', 'PAGINATION_CURSOR_STALE', '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') @@ -22,7 +22,7 @@ LifecycleErrorCode = Literal['HOST_START_FAILED', 'NAVIGATION_ALLOWLIST_CONFLICT', 'UNSUPPORTED_BROWSER_RUNTIME', 'UNSUPPORTED_CAPABILITY'] LAUNCH_PRESENTATIONS = ('background', 'foreground') LaunchPresentation = Literal['background', 'foreground'] -CommandName = Literal['ping', 'shutdown', 'profile.clear', 'session.create', 'session.list', 'session.close', 'visit', 'inspect', 'click', 'fill', 'upload', 'press', 'scroll', 'back', 'reload', 'wait', 'tour', 'capture.info', 'screenshot', 'artifact.list', 'record.start', 'record.status', 'record.stop', 'qa.report', 'qa.clear', 'console.list', 'network.list', 'network.get', 'styles.get', 'cookies.list', 'storage.list', 'visual.compare', 'performance.get', 'animation.list', 'report.create', 'flow.start', 'flow.stop', 'flow.run', 'network.emulate', 'network.mock.set', 'network.mock.clear', 'auth.login'] +CommandName = Literal['ping', 'shutdown', 'profile.clear', 'session.create', 'session.list', 'session.close', 'visit', 'inspect', 'click', 'fill', 'select', 'upload', 'press', 'scroll', 'back', 'reload', 'wait', 'tour', 'capture.info', 'screenshot', 'artifact.list', 'record.start', 'record.status', 'record.stop', 'qa.report', 'qa.clear', 'console.list', 'network.list', 'network.get', 'styles.get', 'cookies.list', 'storage.list', 'visual.compare', 'performance.get', 'animation.list', 'report.create', 'flow.start', 'flow.stop', 'flow.run', 'network.emulate', 'network.mock.set', 'network.mock.clear', 'auth.login'] class PingParameters(TypedDict): pass @@ -67,6 +67,13 @@ class FillParameters(TypedDict): name: NotRequired[str] value: str +class SelectParameters(TypedDict): + target: NotRequired[str] + role: NotRequired[str] + name: NotRequired[str] + label: NotRequired[str] + value: NotRequired[str] + class UploadParameters(TypedDict): target: NotRequired[str] role: NotRequired[str] @@ -273,6 +280,12 @@ class Fill(TypedDict, total=False): filled: Required[str] valueLength: Required[int | float] +class Select(TypedDict, total=False): + selected: Required[str] + role: Required[str] + name: Required[str] + optionIndex: Required[int | float] + class Upload(TypedDict, total=False): uploaded: Required[str] role: Required[str] @@ -1402,6 +1415,49 @@ class AuthenticationRequired(TypedDict, total=False): 'type': 'object'}}, 'scope': 'session', 'timeout': {'defaultMilliseconds': 15000, 'parameterPresentOverrides': {}}}, + 'select': {'capabilityNegotiated': False, + 'constraints': ['exactly one target reference or semantic role/name target', + 'exactly one option label or value', + 'native single-selection HTML select controls only'], + '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': 1000, + 'name': 'label', + 'required': False, + 'sensitive': True, + 'type': 'string'}, + {'maximumBytes': 1000, + 'name': 'value', + 'required': False, + 'sensitive': True, + 'type': 'string'}], + 'result': {'mayContainUntrustedContent': True, + 'schema': {'additionalProperties': True, + 'fields': [{'name': 'selected', + 'required': True, + 'type': 'string'}, + {'name': 'role', 'required': True, 'type': 'string'}, + {'name': 'name', 'required': True, 'type': 'string'}, + {'name': 'optionIndex', + 'required': True, + 'type': 'number'}], + 'name': 'Select', + 'type': 'object'}}, + 'scope': 'session', + 'timeout': {'defaultMilliseconds': 15000, 'parameterPresentOverrides': {}}}, 'session.close': {'capabilityNegotiated': False, 'constraints': [], 'parameters': [], @@ -1997,6 +2053,31 @@ def fill( parameters['name'] = cast(JsonValue, name) return cast(Untrusted[Fill], self._invoke_sync('fill', parameters, timeout, cancel)) + def select( + self, + *, + target: str | None = None, + role: str | None = None, + name: str | None = None, + label: str | None = None, + value: str | None = None, + timeout: float | None = None, + cancel: SyncCancellation | None = None, + ) -> Untrusted[Select]: + parameters: dict[str, JsonValue] = { + } + if target is not None: + parameters['target'] = cast(JsonValue, target) + if role is not None: + parameters['role'] = cast(JsonValue, role) + if name is not None: + parameters['name'] = cast(JsonValue, name) + if label is not None: + parameters['label'] = cast(JsonValue, label) + if value is not None: + parameters['value'] = cast(JsonValue, value) + return cast(Untrusted[Select], self._invoke_sync('select', parameters, timeout, cancel)) + def upload( self, *, @@ -2642,6 +2723,31 @@ async def fill( parameters['name'] = cast(JsonValue, name) return cast(Untrusted[Fill], await self._invoke_async('fill', parameters, timeout, cancel)) + async def select( + self, + *, + target: str | None = None, + role: str | None = None, + name: str | None = None, + label: str | None = None, + value: str | None = None, + timeout: float | None = None, + cancel: AsyncCancellation | None = None, + ) -> Untrusted[Select]: + parameters: dict[str, JsonValue] = { + } + if target is not None: + parameters['target'] = cast(JsonValue, target) + if role is not None: + parameters['role'] = cast(JsonValue, role) + if name is not None: + parameters['name'] = cast(JsonValue, name) + if label is not None: + parameters['label'] = cast(JsonValue, label) + if value is not None: + parameters['value'] = cast(JsonValue, value) + return cast(Untrusted[Select], await self._invoke_async('select', parameters, timeout, cancel)) + async def upload( self, *, diff --git a/sdk/protocol-schema.json b/sdk/protocol-schema.json index 3b0fa61..32016d9 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":[{"items":{"type":"string"},"name":"sessions","required":true,"type":"array"},{"items":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"isolated","required":true,"type":"boolean"},{"name":"ageMs","required":true,"type":"number"},{"name":"status","required":true,"type":"string","values":["available","navigating","unavailable"]},{"name":"url","required":true,"type":"string-or-null"},{"name":"title","required":true,"type":"string-or-null"},{"name":"urlTruncated","required":true,"type":"boolean"},{"name":"titleTruncated","required":true,"type":"boolean"},{"name":"untrustedContent","required":true,"type":"boolean"}],"name":"SessionDetail","type":"object"},"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":[{"maximum":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximumBytes":64,"name":"cursor","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"directory","required":true,"type":"string"},{"name":"artifacts","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"total","required":true,"type":"number"},{"name":"omitted","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"},{"name":"nextCursor","required":true,"type":"string-or-null"},{"name":"mutation","required":true,"type":"string","values":["none"]}],"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":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximumBytes":64,"name":"cursor","required":false,"sensitive":false,"type":"string"}],"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":"truncated","required":true,"type":"boolean"},{"name":"nextCursor","required":true,"type":"string-or-null"},{"name":"mutation","required":true,"type":"string","values":["none"]}],"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":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximumBytes":64,"name":"cursor","required":false,"sensitive":false,"type":"string"}],"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":"truncated","required":true,"type":"boolean"},{"name":"nextCursor","required":true,"type":"string-or-null"},{"name":"mutation","required":true,"type":"string","values":["none"]}],"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","PAGINATION_CURSOR_EXPIRED","PAGINATION_CURSOR_INVALID","PAGINATION_CURSOR_SCOPE_MISMATCH","PAGINATION_CURSOR_STALE","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":[{"items":{"type":"string"},"name":"sessions","required":true,"type":"array"},{"items":{"additionalProperties":true,"fields":[{"name":"name","required":true,"type":"string"},{"name":"isolated","required":true,"type":"boolean"},{"name":"ageMs","required":true,"type":"number"},{"name":"status","required":true,"type":"string","values":["available","navigating","unavailable"]},{"name":"url","required":true,"type":"string-or-null"},{"name":"title","required":true,"type":"string-or-null"},{"name":"urlTruncated","required":true,"type":"boolean"},{"name":"titleTruncated","required":true,"type":"boolean"},{"name":"untrustedContent","required":true,"type":"boolean"}],"name":"SessionDetail","type":"object"},"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":false,"constraints":["exactly one target reference or semantic role/name target","exactly one option label or value","native single-selection HTML select controls only"],"name":"select","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":1000,"name":"label","required":false,"sensitive":true,"type":"string"},{"maximumBytes":1000,"name":"value","required":false,"sensitive":true,"type":"string"}],"result":{"mayContainUntrustedContent":true,"schema":{"additionalProperties":true,"fields":[{"name":"selected","required":true,"type":"string"},{"name":"role","required":true,"type":"string"},{"name":"name","required":true,"type":"string"},{"name":"optionIndex","required":true,"type":"number"}],"name":"Select","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":[{"maximum":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximumBytes":64,"name":"cursor","required":false,"sensitive":false,"type":"string"}],"result":{"mayContainUntrustedContent":false,"schema":{"additionalProperties":true,"fields":[{"name":"directory","required":true,"type":"string"},{"name":"artifacts","required":true,"type":"array"},{"name":"returned","required":true,"type":"number"},{"name":"total","required":true,"type":"number"},{"name":"omitted","required":true,"type":"number"},{"name":"truncated","required":true,"type":"boolean"},{"name":"nextCursor","required":true,"type":"string-or-null"},{"name":"mutation","required":true,"type":"string","values":["none"]}],"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":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximumBytes":64,"name":"cursor","required":false,"sensitive":false,"type":"string"}],"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":"truncated","required":true,"type":"boolean"},{"name":"nextCursor","required":true,"type":"string-or-null"},{"name":"mutation","required":true,"type":"string","values":["none"]}],"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":250,"minimum":1,"name":"limit","required":false,"sensitive":false,"type":"integer"},{"maximumBytes":64,"name":"cursor","required":false,"sensitive":false,"type":"string"}],"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":"truncated","required":true,"type":"boolean"},{"name":"nextCursor","required":true,"type":"string-or-null"},{"name":"mutation","required":true,"type":"string","values":["none"]}],"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","PAGINATION_CURSOR_EXPIRED","PAGINATION_CURSOR_INVALID","PAGINATION_CURSOR_SCOPE_MISMATCH","PAGINATION_CURSOR_STALE","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}}