diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 10f3091..6b7b74c 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.2.0"
+ ".": "0.3.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 36782ae..5dba58c 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 7
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-705638ac8966569986bd9ebb7c9761bf0016909e9f2753e77ceabb12c8049511.yml
-openapi_spec_hash: a8fbbcaa38e91c7f97313620b42d8d62
-config_hash: a35b56eb05306a0f02e83c11d57f975f
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-f7d6b6489159f611a2bfdc267ce0a6fc0455bed1ffa0c310044baaa5d8381b9b.yml
+openapi_spec_hash: cd88d8068abfde8382da0bed674e440c
+config_hash: 5c69fb596588b8ace08203858518c149
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 04011d6..3a2ee1b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,22 @@
# Changelog
+## 0.3.0 (2025-12-18)
+
+Full Changelog: [v0.2.0...v0.3.0](https://github.com/browserbase/stagehand-java/compare/v0.2.0...v0.3.0)
+
+### Features
+
+* **api:** manual updates ([b6bdf1a](https://github.com/browserbase/stagehand-java/commit/b6bdf1a26a45556dcfee59b088983cd7e7839f43))
+* **api:** manual updates ([9b6860a](https://github.com/browserbase/stagehand-java/commit/9b6860a4f60d1a740fbb026eef291e40fb8e1845))
+* **api:** manual updates ([28f7978](https://github.com/browserbase/stagehand-java/commit/28f7978e1f040169dbb20aa68efb8e5ceb9a34d2))
+* **api:** manual updates ([07db616](https://github.com/browserbase/stagehand-java/commit/07db616b7209716efa346035d65f5d7bf616c992))
+* **api:** manual updates ([3cf2073](https://github.com/browserbase/stagehand-java/commit/3cf20739785134610aa7bcbdbce3215c3c3a9970))
+* **api:** manual updates ([adace53](https://github.com/browserbase/stagehand-java/commit/adace53fc3e4a0fb4b866172aec98519ec0f29d5))
+* **api:** manual updates ([dd65420](https://github.com/browserbase/stagehand-java/commit/dd65420fecb521fba36cbf2a623ce675973a27a0))
+* **api:** manual updates ([067c9e6](https://github.com/browserbase/stagehand-java/commit/067c9e644940fa3671665a902bc605b8e358473f))
+* **api:** manual updates ([d7aabe3](https://github.com/browserbase/stagehand-java/commit/d7aabe36f42d8344477f2f468bd3ba15837c0516))
+* **api:** manual updates ([13074f1](https://github.com/browserbase/stagehand-java/commit/13074f11bbc12057b60e89f37f850ca8f5bc7a83))
+
## 0.2.0 (2025-12-16)
Full Changelog: [v0.1.0...v0.2.0](https://github.com/browserbase/stagehand-java/compare/v0.1.0...v0.2.0)
diff --git a/README.md b/README.md
index c383ecb..d87bf13 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
-[](https://central.sonatype.com/artifact/com.browserbase.api/stagehand-java/0.2.0)
-[](https://javadoc.io/doc/com.browserbase.api/stagehand-java/0.2.0)
+[](https://central.sonatype.com/artifact/com.browserbase.api/stagehand-java/0.3.0)
+[](https://javadoc.io/doc/com.browserbase.api/stagehand-java/0.3.0)
@@ -13,7 +13,7 @@ It is generated with [Stainless](https://www.stainless.com/).
-The REST API documentation can be found on [docs.stagehand.dev](https://docs.stagehand.dev). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.browserbase.api/stagehand-java/0.2.0).
+The REST API documentation can be found on [docs.stagehand.dev](https://docs.stagehand.dev). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.browserbase.api/stagehand-java/0.3.0).
@@ -24,7 +24,7 @@ The REST API documentation can be found on [docs.stagehand.dev](https://docs.sta
### Gradle
```kotlin
-implementation("com.browserbase.api:stagehand-java:0.2.0")
+implementation("com.browserbase.api:stagehand-java:0.3.0")
```
### Maven
@@ -33,7 +33,7 @@ implementation("com.browserbase.api:stagehand-java:0.2.0")
com.browserbase.api
stagehand-java
- 0.2.0
+ 0.3.0
```
@@ -56,7 +56,7 @@ import com.browserbase.api.models.sessions.SessionActResponse;
StagehandClient client = StagehandOkHttpClient.fromEnv();
SessionActParams params = SessionActParams.builder()
- .sessionId("00000000-your-session-id-000000000000")
+ .id("00000000-your-session-id-000000000000")
.input("click the first link on the page")
.build();
SessionActResponse response = client.sessions().act(params);
@@ -162,7 +162,7 @@ import java.util.concurrent.CompletableFuture;
StagehandClient client = StagehandOkHttpClient.fromEnv();
SessionActParams params = SessionActParams.builder()
- .sessionId("00000000-your-session-id-000000000000")
+ .id("00000000-your-session-id-000000000000")
.input("click the first link on the page")
.build();
CompletableFuture response = client.async().sessions().act(params);
@@ -182,7 +182,7 @@ import java.util.concurrent.CompletableFuture;
StagehandClientAsync client = StagehandOkHttpClientAsync.fromEnv();
SessionActParams params = SessionActParams.builder()
- .sessionId("00000000-your-session-id-000000000000")
+ .id("00000000-your-session-id-000000000000")
.input("click the first link on the page")
.build();
CompletableFuture response = client.sessions().act(params);
@@ -203,8 +203,7 @@ import com.browserbase.api.models.sessions.SessionStartParams;
import com.browserbase.api.models.sessions.SessionStartResponse;
SessionStartParams params = SessionStartParams.builder()
- .browserbaseApiKey("your Browserbase API key")
- .browserbaseProjectId("your Browserbase Project ID")
+ .modelName("openai/gpt-5-nano")
.build();
HttpResponseFor response = client.sessions().withRawResponse().start(params);
@@ -237,6 +236,8 @@ The SDK throws custom unchecked exception types:
| 5xx | [`InternalServerException`](stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/InternalServerException.kt) |
| others | [`UnexpectedStatusCodeException`](stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/UnexpectedStatusCodeException.kt) |
+ [`SseException`](stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/SseException.kt) is thrown for errors encountered during [SSE streaming](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) after a successful initial HTTP response.
+
- [`StagehandIoException`](stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/StagehandIoException.kt): I/O networking errors.
- [`StagehandRetryableException`](stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/StagehandRetryableException.kt): Generic error indicating a failure that could be retried by the client.
@@ -505,8 +506,8 @@ import com.browserbase.api.core.JsonMissing;
import com.browserbase.api.models.sessions.SessionActParams;
SessionActParams params = SessionActParams.builder()
- .input("click the sign in button")
- .sessionId(JsonMissing.of())
+ .input("Click the login button")
+ .id(JsonMissing.of())
.build();
```
diff --git a/build.gradle.kts b/build.gradle.kts
index 63a11b4..d06f0a9 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -9,7 +9,7 @@ repositories {
allprojects {
group = "com.browserbase.api"
- version = "0.2.0" // x-release-please-version
+ version = "0.3.0" // x-release-please-version
}
subprojects {
diff --git a/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClient.kt b/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClient.kt
index 8955635..40010ee 100644
--- a/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClient.kt
+++ b/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClient.kt
@@ -7,6 +7,7 @@ import com.browserbase.api.client.StagehandClientImpl
import com.browserbase.api.core.ClientOptions
import com.browserbase.api.core.Sleeper
import com.browserbase.api.core.Timeout
+import com.browserbase.api.core.http.AsyncStreamResponse
import com.browserbase.api.core.http.Headers
import com.browserbase.api.core.http.HttpClient
import com.browserbase.api.core.http.QueryParams
@@ -16,6 +17,7 @@ import java.net.Proxy
import java.time.Clock
import java.time.Duration
import java.util.Optional
+import java.util.concurrent.Executor
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
@@ -121,6 +123,17 @@ class StagehandOkHttpClient private constructor() {
*/
fun jsonMapper(jsonMapper: JsonMapper) = apply { clientOptions.jsonMapper(jsonMapper) }
+ /**
+ * The executor to use for running [AsyncStreamResponse.Handler] callbacks.
+ *
+ * Defaults to a dedicated cached thread pool.
+ *
+ * This class takes ownership of the executor and shuts it down, if possible, when closed.
+ */
+ fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply {
+ clientOptions.streamHandlerExecutor(streamHandlerExecutor)
+ }
+
/**
* The interface to use for delaying execution, like during retries.
*
diff --git a/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClientAsync.kt b/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClientAsync.kt
index 2cb208d..33ce68f 100644
--- a/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClientAsync.kt
+++ b/stagehand-java-client-okhttp/src/main/kotlin/com/browserbase/api/client/okhttp/StagehandOkHttpClientAsync.kt
@@ -7,6 +7,7 @@ import com.browserbase.api.client.StagehandClientAsyncImpl
import com.browserbase.api.core.ClientOptions
import com.browserbase.api.core.Sleeper
import com.browserbase.api.core.Timeout
+import com.browserbase.api.core.http.AsyncStreamResponse
import com.browserbase.api.core.http.Headers
import com.browserbase.api.core.http.HttpClient
import com.browserbase.api.core.http.QueryParams
@@ -16,6 +17,7 @@ import java.net.Proxy
import java.time.Clock
import java.time.Duration
import java.util.Optional
+import java.util.concurrent.Executor
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
@@ -121,6 +123,17 @@ class StagehandOkHttpClientAsync private constructor() {
*/
fun jsonMapper(jsonMapper: JsonMapper) = apply { clientOptions.jsonMapper(jsonMapper) }
+ /**
+ * The executor to use for running [AsyncStreamResponse.Handler] callbacks.
+ *
+ * Defaults to a dedicated cached thread pool.
+ *
+ * This class takes ownership of the executor and shuts it down, if possible, when closed.
+ */
+ fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply {
+ clientOptions.streamHandlerExecutor(streamHandlerExecutor)
+ }
+
/**
* The interface to use for delaying execution, like during retries.
*
diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/ClientOptions.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/ClientOptions.kt
index 8692cb0..2da3f9a 100644
--- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/ClientOptions.kt
+++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/ClientOptions.kt
@@ -2,6 +2,7 @@
package com.browserbase.api.core
+import com.browserbase.api.core.http.AsyncStreamResponse
import com.browserbase.api.core.http.Headers
import com.browserbase.api.core.http.HttpClient
import com.browserbase.api.core.http.PhantomReachableClosingHttpClient
@@ -11,6 +12,11 @@ import com.fasterxml.jackson.databind.json.JsonMapper
import java.time.Clock
import java.time.Duration
import java.util.Optional
+import java.util.concurrent.Executor
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+import java.util.concurrent.ThreadFactory
+import java.util.concurrent.atomic.AtomicLong
import kotlin.jvm.optionals.getOrNull
/** A class representing the SDK client configuration. */
@@ -40,6 +46,14 @@ private constructor(
* rarely needs to be overridden.
*/
@get:JvmName("jsonMapper") val jsonMapper: JsonMapper,
+ /**
+ * The executor to use for running [AsyncStreamResponse.Handler] callbacks.
+ *
+ * Defaults to a dedicated cached thread pool.
+ *
+ * This class takes ownership of the executor and shuts it down, if possible, when closed.
+ */
+ @get:JvmName("streamHandlerExecutor") val streamHandlerExecutor: Executor,
/**
* The interface to use for delaying execution, like during retries.
*
@@ -147,6 +161,7 @@ private constructor(
private var httpClient: HttpClient? = null
private var checkJacksonVersionCompatibility: Boolean = true
private var jsonMapper: JsonMapper = jsonMapper()
+ private var streamHandlerExecutor: Executor? = null
private var sleeper: Sleeper? = null
private var clock: Clock = Clock.systemUTC()
private var baseUrl: String? = null
@@ -164,6 +179,7 @@ private constructor(
httpClient = clientOptions.originalHttpClient
checkJacksonVersionCompatibility = clientOptions.checkJacksonVersionCompatibility
jsonMapper = clientOptions.jsonMapper
+ streamHandlerExecutor = clientOptions.streamHandlerExecutor
sleeper = clientOptions.sleeper
clock = clientOptions.clock
baseUrl = clientOptions.baseUrl
@@ -207,6 +223,20 @@ private constructor(
*/
fun jsonMapper(jsonMapper: JsonMapper) = apply { this.jsonMapper = jsonMapper }
+ /**
+ * The executor to use for running [AsyncStreamResponse.Handler] callbacks.
+ *
+ * Defaults to a dedicated cached thread pool.
+ *
+ * This class takes ownership of the executor and shuts it down, if possible, when closed.
+ */
+ fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply {
+ this.streamHandlerExecutor =
+ if (streamHandlerExecutor is ExecutorService)
+ PhantomReachableExecutorService(streamHandlerExecutor)
+ else streamHandlerExecutor
+ }
+
/**
* The interface to use for delaying execution, like during retries.
*
@@ -422,6 +452,24 @@ private constructor(
*/
fun build(): ClientOptions {
val httpClient = checkRequired("httpClient", httpClient)
+ val streamHandlerExecutor =
+ streamHandlerExecutor
+ ?: PhantomReachableExecutorService(
+ Executors.newCachedThreadPool(
+ object : ThreadFactory {
+
+ private val threadFactory: ThreadFactory =
+ Executors.defaultThreadFactory()
+ private val count = AtomicLong(0)
+
+ override fun newThread(runnable: Runnable): Thread =
+ threadFactory.newThread(runnable).also {
+ it.name =
+ "stagehand-stream-handler-thread-${count.getAndIncrement()}"
+ }
+ }
+ )
+ )
val sleeper = sleeper ?: PhantomReachableSleeper(DefaultSleeper())
val browserbaseApiKey = checkRequired("browserbaseApiKey", browserbaseApiKey)
val browserbaseProjectId = checkRequired("browserbaseProjectId", browserbaseProjectId)
@@ -464,6 +512,7 @@ private constructor(
.build(),
checkJacksonVersionCompatibility,
jsonMapper,
+ streamHandlerExecutor,
sleeper,
clock,
baseUrl,
@@ -491,6 +540,7 @@ private constructor(
*/
fun close() {
httpClient.close()
+ (streamHandlerExecutor as? ExecutorService)?.shutdown()
sleeper.close()
}
}
diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/handlers/SseHandler.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/handlers/SseHandler.kt
new file mode 100644
index 0000000..039faae
--- /dev/null
+++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/handlers/SseHandler.kt
@@ -0,0 +1,137 @@
+// File generated from our OpenAPI spec by Stainless.
+
+@file:JvmName("SseHandler")
+
+package com.browserbase.api.core.handlers
+
+import com.browserbase.api.core.JsonMissing
+import com.browserbase.api.core.http.HttpResponse
+import com.browserbase.api.core.http.HttpResponse.Handler
+import com.browserbase.api.core.http.SseMessage
+import com.browserbase.api.core.http.StreamResponse
+import com.browserbase.api.core.http.map
+import com.browserbase.api.errors.SseException
+import com.fasterxml.jackson.databind.json.JsonMapper
+import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
+
+@JvmSynthetic
+internal fun sseHandler(jsonMapper: JsonMapper): Handler> =
+ streamHandler { response, lines ->
+ val state = SseState(jsonMapper)
+ var done = false
+ for (line in lines) {
+ // Stop emitting messages, but iterate through the full stream.
+ if (done) {
+ continue
+ }
+
+ val message = state.decode(line) ?: continue
+
+ when {
+ message.data.startsWith("finished") -> {
+ // In this case we don't break because we still want to iterate through the full
+ // stream.
+ done = true
+ continue
+ }
+ message.data.startsWith("error") -> {
+ throw SseException.builder()
+ .statusCode(response.statusCode())
+ .headers(response.headers())
+ .body(
+ try {
+ jsonMapper.readValue(message.data, jacksonTypeRef())
+ } catch (e: Exception) {
+ JsonMissing.of()
+ }
+ )
+ .build()
+ }
+ }
+
+ if (message.event == null) {
+ yield(message)
+ }
+ }
+ }
+
+private class SseState(
+ val jsonMapper: JsonMapper,
+ var event: String? = null,
+ val data: MutableList = mutableListOf(),
+ var lastId: String? = null,
+ var retry: Int? = null,
+) {
+ // https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation
+ fun decode(line: String): SseMessage? {
+ if (line.isEmpty()) {
+ return flush()
+ }
+
+ if (line.startsWith(':')) {
+ return null
+ }
+
+ val fieldName: String
+ var value: String
+
+ val colonIndex = line.indexOf(':')
+ if (colonIndex == -1) {
+ fieldName = line
+ value = ""
+ } else {
+ fieldName = line.substring(0, colonIndex)
+ value = line.substring(colonIndex + 1)
+ }
+
+ if (value.startsWith(' ')) {
+ value = value.substring(1)
+ }
+
+ when (fieldName) {
+ "event" -> event = value
+ "data" -> data.add(value)
+ "id" -> {
+ if (!value.contains('\u0000')) {
+ lastId = value
+ }
+ }
+ "retry" -> value.toIntOrNull()?.let { retry = it }
+ }
+
+ return null
+ }
+
+ private fun flush(): SseMessage? {
+ if (isEmpty()) {
+ return null
+ }
+
+ val message =
+ SseMessage.builder()
+ .jsonMapper(jsonMapper)
+ .event(event)
+ .data(data.joinToString("\n"))
+ .id(lastId)
+ .retry(retry)
+ .build()
+
+ // NOTE: Per the SSE spec, do not reset lastId.
+ event = null
+ data.clear()
+ retry = null
+
+ return message
+ }
+
+ private fun isEmpty(): Boolean =
+ event.isNullOrEmpty() && data.isEmpty() && lastId.isNullOrEmpty() && retry == null
+}
+
+@JvmSynthetic
+internal inline fun Handler>.mapJson():
+ Handler> =
+ object : Handler> {
+ override fun handle(response: HttpResponse): StreamResponse =
+ this@mapJson.handle(response).map { it.json() }
+ }
diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/handlers/StreamHandler.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/handlers/StreamHandler.kt
new file mode 100644
index 0000000..4cd4f25
--- /dev/null
+++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/handlers/StreamHandler.kt
@@ -0,0 +1,102 @@
+@file:JvmName("StreamHandler")
+
+package com.browserbase.api.core.handlers
+
+import com.browserbase.api.core.http.HttpResponse
+import com.browserbase.api.core.http.HttpResponse.Handler
+import com.browserbase.api.core.http.PhantomReachableClosingStreamResponse
+import com.browserbase.api.core.http.StreamResponse
+import com.browserbase.api.errors.StagehandIoException
+import java.io.IOException
+import java.util.stream.Stream
+import kotlin.streams.asStream
+
+@JvmSynthetic
+internal fun streamHandler(
+ block: suspend SequenceScope.(response: HttpResponse, lines: Sequence) -> Unit
+): Handler> =
+ object : Handler> {
+
+ override fun handle(response: HttpResponse): StreamResponse {
+ val reader = response.body().bufferedReader()
+ val sequence =
+ // Wrap in a `CloseableSequence` to avoid performing a read on the `reader`
+ // after it has been closed, which would throw an `IOException`.
+ CloseableSequence(
+ sequence {
+ reader.useLines { lines ->
+ block(
+ response,
+ // We wrap the `lines` instead of the top-level sequence because
+ // we only want to catch `IOException` from the reader; not from
+ // the user's own code.
+ IOExceptionWrappingSequence(lines),
+ )
+ }
+ }
+ .constrainOnce()
+ )
+
+ return PhantomReachableClosingStreamResponse(
+ object : StreamResponse {
+
+ override fun stream(): Stream = sequence.asStream()
+
+ override fun close() {
+ sequence.close()
+ reader.close()
+ response.close()
+ }
+ }
+ )
+ }
+ }
+
+/** A sequence that catches, wraps, and rethrows [IOException] as [StagehandIoException]. */
+private class IOExceptionWrappingSequence(private val sequence: Sequence) : Sequence {
+
+ override fun iterator(): Iterator {
+ val iterator = sequence.iterator()
+ return object : Iterator {
+
+ override fun next(): T =
+ try {
+ iterator.next()
+ } catch (e: IOException) {
+ throw StagehandIoException("Stream failed", e)
+ }
+
+ override fun hasNext(): Boolean =
+ try {
+ iterator.hasNext()
+ } catch (e: IOException) {
+ throw StagehandIoException("Stream failed", e)
+ }
+ }
+ }
+}
+
+/**
+ * A sequence that can be closed.
+ *
+ * Once [close] is called, it will not yield more elements. It will also no longer consult the
+ * underlying [Iterator.hasNext] method.
+ */
+private class CloseableSequence(private val sequence: Sequence) : Sequence {
+
+ private var isClosed: Boolean = false
+
+ override fun iterator(): Iterator {
+ val iterator = sequence.iterator()
+ return object : Iterator {
+
+ override fun next(): T = iterator.next()
+
+ override fun hasNext(): Boolean = !isClosed && iterator.hasNext()
+ }
+ }
+
+ fun close() {
+ isClosed = true
+ }
+}
diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/http/SseMessage.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/http/SseMessage.kt
new file mode 100644
index 0000000..21cc44e
--- /dev/null
+++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/core/http/SseMessage.kt
@@ -0,0 +1,74 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.browserbase.api.core.http
+
+import com.browserbase.api.errors.StagehandInvalidDataException
+import com.fasterxml.jackson.databind.json.JsonMapper
+import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
+import java.util.Objects
+
+internal class SseMessage
+private constructor(
+ val jsonMapper: JsonMapper,
+ val event: String?,
+ val data: String,
+ val id: String?,
+ val retry: Int?,
+) {
+
+ companion object {
+ @JvmStatic fun builder() = Builder()
+ }
+
+ class Builder internal constructor() {
+
+ private var jsonMapper: JsonMapper? = null
+ private var event: String? = null
+ private var data: String = ""
+ private var id: String? = null
+ private var retry: Int? = null
+
+ fun jsonMapper(jsonMapper: JsonMapper) = apply { this.jsonMapper = jsonMapper }
+
+ fun event(event: String?) = apply { this.event = event }
+
+ fun data(data: String) = apply { this.data = data }
+
+ fun id(id: String?) = apply { this.id = id }
+
+ fun retry(retry: Int?) = apply { this.retry = retry }
+
+ fun build(): SseMessage = SseMessage(jsonMapper!!, event, data, id, retry)
+ }
+
+ inline fun json(): T =
+ try {
+ jsonMapper.readerFor(jacksonTypeRef()).readValue(jsonNode)
+ } catch (e: Exception) {
+ throw StagehandInvalidDataException("Error reading response", e)
+ }
+
+ private val jsonNode by lazy {
+ try {
+ jsonMapper.readTree(data)
+ } catch (e: Exception) {
+ throw StagehandInvalidDataException("Error reading response", e)
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is SseMessage &&
+ event == other.event &&
+ data == other.data &&
+ id == other.id &&
+ retry == other.retry
+ }
+
+ override fun hashCode(): Int = Objects.hash(event, data, id, retry)
+
+ override fun toString(): String = "SseMessage{event=$event, data=$data, id=$id, retry=$retry}"
+}
diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/SseException.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/SseException.kt
new file mode 100644
index 0000000..0dfdd2f
--- /dev/null
+++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/errors/SseException.kt
@@ -0,0 +1,91 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.browserbase.api.errors
+
+import com.browserbase.api.core.JsonValue
+import com.browserbase.api.core.checkRequired
+import com.browserbase.api.core.http.Headers
+import java.util.Optional
+import kotlin.jvm.optionals.getOrNull
+
+class SseException
+private constructor(
+ private val statusCode: Int,
+ private val headers: Headers,
+ private val body: JsonValue,
+ cause: Throwable?,
+) : StagehandServiceException("$statusCode: $body", cause) {
+
+ override fun statusCode(): Int = statusCode
+
+ override fun headers(): Headers = headers
+
+ override fun body(): JsonValue = body
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [SseException].
+ *
+ * The following fields are required:
+ * ```java
+ * .statusCode()
+ * .headers()
+ * .body()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [SseException]. */
+ class Builder internal constructor() {
+
+ private var statusCode: Int? = null
+ private var headers: Headers? = null
+ private var body: JsonValue? = null
+ private var cause: Throwable? = null
+
+ @JvmSynthetic
+ internal fun from(sseException: SseException) = apply {
+ statusCode = sseException.statusCode
+ headers = sseException.headers
+ body = sseException.body
+ cause = sseException.cause
+ }
+
+ fun statusCode(statusCode: Int) = apply { this.statusCode = statusCode }
+
+ fun headers(headers: Headers) = apply { this.headers = headers }
+
+ fun body(body: JsonValue) = apply { this.body = body }
+
+ fun cause(cause: Throwable?) = apply { this.cause = cause }
+
+ /** Alias for calling [Builder.cause] with `cause.orElse(null)`. */
+ fun cause(cause: Optional) = cause(cause.getOrNull())
+
+ /**
+ * Returns an immutable instance of [SseException].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .statusCode()
+ * .headers()
+ * .body()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): SseException =
+ SseException(
+ checkRequired("statusCode", statusCode),
+ checkRequired("headers", headers),
+ checkRequired("body", body),
+ cause,
+ )
+ }
+}
diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/Action.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/Action.kt
index d30a40b..e9ae4bc 100644
--- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/Action.kt
+++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/Action.kt
@@ -19,39 +19,28 @@ import java.util.Objects
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
+/** Action object returned by observe and used by act */
class Action
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
- private val arguments: JsonField>,
private val description: JsonField,
- private val method: JsonField,
private val selector: JsonField,
- private val backendNodeId: JsonField,
+ private val arguments: JsonField>,
+ private val method: JsonField,
private val additionalProperties: MutableMap,
) {
@JsonCreator
private constructor(
- @JsonProperty("arguments")
- @ExcludeMissing
- arguments: JsonField> = JsonMissing.of(),
@JsonProperty("description")
@ExcludeMissing
description: JsonField = JsonMissing.of(),
- @JsonProperty("method") @ExcludeMissing method: JsonField = JsonMissing.of(),
@JsonProperty("selector") @ExcludeMissing selector: JsonField = JsonMissing.of(),
- @JsonProperty("backendNodeId")
+ @JsonProperty("arguments")
@ExcludeMissing
- backendNodeId: JsonField = JsonMissing.of(),
- ) : this(arguments, description, method, selector, backendNodeId, mutableMapOf())
-
- /**
- * Arguments for the method
- *
- * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
- * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
- */
- fun arguments(): List = arguments.getRequired("arguments")
+ arguments: JsonField> = JsonMissing.of(),
+ @JsonProperty("method") @ExcludeMissing method: JsonField = JsonMissing.of(),
+ ) : this(description, selector, arguments, method, mutableMapOf())
/**
* Human-readable description of the action
@@ -62,15 +51,7 @@ private constructor(
fun description(): String = description.getRequired("description")
/**
- * Method to execute (e.g., "click", "fill")
- *
- * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
- * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
- */
- fun method(): String = method.getRequired("method")
-
- /**
- * CSS or XPath selector for the element
+ * CSS selector or XPath for the element
*
* @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
* unexpectedly missing or null (e.g. if the server responded with an unexpected value).
@@ -78,19 +59,20 @@ private constructor(
fun selector(): String = selector.getRequired("selector")
/**
- * CDP backend node ID
+ * Arguments to pass to the method
*
* @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
*/
- fun backendNodeId(): Optional = backendNodeId.getOptional("backendNodeId")
+ fun arguments(): Optional> = arguments.getOptional("arguments")
/**
- * Returns the raw JSON value of [arguments].
+ * The method to execute (click, fill, etc.)
*
- * Unlike [arguments], this method doesn't throw if the JSON field has an unexpected type.
+ * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
*/
- @JsonProperty("arguments") @ExcludeMissing fun _arguments(): JsonField> = arguments
+ fun method(): Optional = method.getOptional("method")
/**
* Returns the raw JSON value of [description].
@@ -100,27 +82,25 @@ private constructor(
@JsonProperty("description") @ExcludeMissing fun _description(): JsonField = description
/**
- * Returns the raw JSON value of [method].
+ * Returns the raw JSON value of [selector].
*
- * Unlike [method], this method doesn't throw if the JSON field has an unexpected type.
+ * Unlike [selector], this method doesn't throw if the JSON field has an unexpected type.
*/
- @JsonProperty("method") @ExcludeMissing fun _method(): JsonField = method
+ @JsonProperty("selector") @ExcludeMissing fun _selector(): JsonField = selector
/**
- * Returns the raw JSON value of [selector].
+ * Returns the raw JSON value of [arguments].
*
- * Unlike [selector], this method doesn't throw if the JSON field has an unexpected type.
+ * Unlike [arguments], this method doesn't throw if the JSON field has an unexpected type.
*/
- @JsonProperty("selector") @ExcludeMissing fun _selector(): JsonField = selector
+ @JsonProperty("arguments") @ExcludeMissing fun _arguments(): JsonField> = arguments
/**
- * Returns the raw JSON value of [backendNodeId].
+ * Returns the raw JSON value of [method].
*
- * Unlike [backendNodeId], this method doesn't throw if the JSON field has an unexpected type.
+ * Unlike [method], this method doesn't throw if the JSON field has an unexpected type.
*/
- @JsonProperty("backendNodeId")
- @ExcludeMissing
- fun _backendNodeId(): JsonField = backendNodeId
+ @JsonProperty("method") @ExcludeMissing fun _method(): JsonField = method
@JsonAnySetter
private fun putAdditionalProperty(key: String, value: JsonValue) {
@@ -141,9 +121,7 @@ private constructor(
*
* The following fields are required:
* ```java
- * .arguments()
* .description()
- * .method()
* .selector()
* ```
*/
@@ -153,24 +131,45 @@ private constructor(
/** A builder for [Action]. */
class Builder internal constructor() {
- private var arguments: JsonField>? = null
private var description: JsonField? = null
- private var method: JsonField? = null
private var selector: JsonField? = null
- private var backendNodeId: JsonField = JsonMissing.of()
+ private var arguments: JsonField>? = null
+ private var method: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
internal fun from(action: Action) = apply {
- arguments = action.arguments.map { it.toMutableList() }
description = action.description
- method = action.method
selector = action.selector
- backendNodeId = action.backendNodeId
+ arguments = action.arguments.map { it.toMutableList() }
+ method = action.method
additionalProperties = action.additionalProperties.toMutableMap()
}
- /** Arguments for the method */
+ /** Human-readable description of the action */
+ fun description(description: String) = description(JsonField.of(description))
+
+ /**
+ * Sets [Builder.description] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.description] with a well-typed [String] value instead.
+ * This method is primarily for setting the field to an undocumented or not yet supported
+ * value.
+ */
+ fun description(description: JsonField) = apply { this.description = description }
+
+ /** CSS selector or XPath for the element */
+ fun selector(selector: String) = selector(JsonField.of(selector))
+
+ /**
+ * Sets [Builder.selector] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.selector] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun selector(selector: JsonField) = apply { this.selector = selector }
+
+ /** Arguments to pass to the method */
fun arguments(arguments: List) = arguments(JsonField.of(arguments))
/**
@@ -196,19 +195,7 @@ private constructor(
}
}
- /** Human-readable description of the action */
- fun description(description: String) = description(JsonField.of(description))
-
- /**
- * Sets [Builder.description] to an arbitrary JSON value.
- *
- * You should usually call [Builder.description] with a well-typed [String] value instead.
- * This method is primarily for setting the field to an undocumented or not yet supported
- * value.
- */
- fun description(description: JsonField) = apply { this.description = description }
-
- /** Method to execute (e.g., "click", "fill") */
+ /** The method to execute (click, fill, etc.) */
fun method(method: String) = method(JsonField.of(method))
/**
@@ -219,31 +206,6 @@ private constructor(
*/
fun method(method: JsonField) = apply { this.method = method }
- /** CSS or XPath selector for the element */
- fun selector(selector: String) = selector(JsonField.of(selector))
-
- /**
- * Sets [Builder.selector] to an arbitrary JSON value.
- *
- * You should usually call [Builder.selector] with a well-typed [String] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun selector(selector: JsonField) = apply { this.selector = selector }
-
- /** CDP backend node ID */
- fun backendNodeId(backendNodeId: Long) = backendNodeId(JsonField.of(backendNodeId))
-
- /**
- * Sets [Builder.backendNodeId] to an arbitrary JSON value.
- *
- * You should usually call [Builder.backendNodeId] with a well-typed [Long] value instead.
- * This method is primarily for setting the field to an undocumented or not yet supported
- * value.
- */
- fun backendNodeId(backendNodeId: JsonField) = apply {
- this.backendNodeId = backendNodeId
- }
-
fun additionalProperties(additionalProperties: Map) = apply {
this.additionalProperties.clear()
putAllAdditionalProperties(additionalProperties)
@@ -270,9 +232,7 @@ private constructor(
*
* The following fields are required:
* ```java
- * .arguments()
* .description()
- * .method()
* .selector()
* ```
*
@@ -280,11 +240,10 @@ private constructor(
*/
fun build(): Action =
Action(
- checkRequired("arguments", arguments).map { it.toImmutable() },
checkRequired("description", description),
- checkRequired("method", method),
checkRequired("selector", selector),
- backendNodeId,
+ (arguments ?: JsonMissing.of()).map { it.toImmutable() },
+ method,
additionalProperties.toMutableMap(),
)
}
@@ -296,11 +255,10 @@ private constructor(
return@apply
}
- arguments()
description()
- method()
selector()
- backendNodeId()
+ arguments()
+ method()
validated = true
}
@@ -319,11 +277,10 @@ private constructor(
*/
@JvmSynthetic
internal fun validity(): Int =
- (arguments.asKnown().getOrNull()?.size ?: 0) +
- (if (description.asKnown().isPresent) 1 else 0) +
- (if (method.asKnown().isPresent) 1 else 0) +
+ (if (description.asKnown().isPresent) 1 else 0) +
(if (selector.asKnown().isPresent) 1 else 0) +
- (if (backendNodeId.asKnown().isPresent) 1 else 0)
+ (arguments.asKnown().getOrNull()?.size ?: 0) +
+ (if (method.asKnown().isPresent) 1 else 0)
override fun equals(other: Any?): Boolean {
if (this === other) {
@@ -331,20 +288,19 @@ private constructor(
}
return other is Action &&
- arguments == other.arguments &&
description == other.description &&
- method == other.method &&
selector == other.selector &&
- backendNodeId == other.backendNodeId &&
+ arguments == other.arguments &&
+ method == other.method &&
additionalProperties == other.additionalProperties
}
private val hashCode: Int by lazy {
- Objects.hash(arguments, description, method, selector, backendNodeId, additionalProperties)
+ Objects.hash(description, selector, arguments, method, additionalProperties)
}
override fun hashCode(): Int = hashCode
override fun toString() =
- "Action{arguments=$arguments, description=$description, method=$method, selector=$selector, backendNodeId=$backendNodeId, additionalProperties=$additionalProperties}"
+ "Action{description=$description, selector=$selector, arguments=$arguments, method=$method, additionalProperties=$additionalProperties}"
}
diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/ModelConfig.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/ModelConfig.kt
index e08ebfa..e416a06 100644
--- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/ModelConfig.kt
+++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/ModelConfig.kt
@@ -2,205 +2,72 @@
package com.browserbase.api.models.sessions
-import com.browserbase.api.core.Enum
+import com.browserbase.api.core.BaseDeserializer
+import com.browserbase.api.core.BaseSerializer
import com.browserbase.api.core.ExcludeMissing
import com.browserbase.api.core.JsonField
import com.browserbase.api.core.JsonMissing
import com.browserbase.api.core.JsonValue
+import com.browserbase.api.core.allMaxBy
+import com.browserbase.api.core.checkRequired
+import com.browserbase.api.core.getOrThrow
import com.browserbase.api.errors.StagehandInvalidDataException
import com.fasterxml.jackson.annotation.JsonAnyGetter
import com.fasterxml.jackson.annotation.JsonAnySetter
import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
+import com.fasterxml.jackson.core.JsonGenerator
+import com.fasterxml.jackson.core.ObjectCodec
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.SerializerProvider
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize
+import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
import java.util.Collections
import java.util.Objects
import java.util.Optional
-import kotlin.jvm.optionals.getOrNull
+/**
+ * Model name string with provider prefix (e.g., 'openai/gpt-5-nano', 'anthropic/claude-4.5-opus')
+ */
+@JsonDeserialize(using = ModelConfig.Deserializer::class)
+@JsonSerialize(using = ModelConfig.Serializer::class)
class ModelConfig
-@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
- private val apiKey: JsonField,
- private val baseUrl: JsonField,
- private val model: JsonField,
- private val provider: JsonField,
- private val additionalProperties: MutableMap,
+ private val name: String? = null,
+ private val modelConfigObject: ModelConfigObject? = null,
+ private val _json: JsonValue? = null,
) {
- @JsonCreator
- private constructor(
- @JsonProperty("apiKey") @ExcludeMissing apiKey: JsonField = JsonMissing.of(),
- @JsonProperty("baseURL") @ExcludeMissing baseUrl: JsonField = JsonMissing.of(),
- @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(),
- @JsonProperty("provider") @ExcludeMissing provider: JsonField = JsonMissing.of(),
- ) : this(apiKey, baseUrl, model, provider, mutableMapOf())
-
/**
- * API key for the model provider
- *
- * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the
- * server responded with an unexpected value).
+ * Model name string with provider prefix (e.g., 'openai/gpt-5-nano',
+ * 'anthropic/claude-4.5-opus')
*/
- fun apiKey(): Optional = apiKey.getOptional("apiKey")
+ fun name(): Optional = Optional.ofNullable(name)
- /**
- * Custom base URL for API
- *
- * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the
- * server responded with an unexpected value).
- */
- fun baseUrl(): Optional = baseUrl.getOptional("baseURL")
+ fun modelConfigObject(): Optional = Optional.ofNullable(modelConfigObject)
- /**
- * Model name
- *
- * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the
- * server responded with an unexpected value).
- */
- fun model(): Optional = model.getOptional("model")
+ fun isName(): Boolean = name != null
- /**
- * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the
- * server responded with an unexpected value).
- */
- fun provider(): Optional = provider.getOptional("provider")
+ fun isModelConfigObject(): Boolean = modelConfigObject != null
/**
- * Returns the raw JSON value of [apiKey].
- *
- * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected type.
+ * Model name string with provider prefix (e.g., 'openai/gpt-5-nano',
+ * 'anthropic/claude-4.5-opus')
*/
- @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey
-
- /**
- * Returns the raw JSON value of [baseUrl].
- *
- * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl
-
- /**
- * Returns the raw JSON value of [model].
- *
- * Unlike [model], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model
-
- /**
- * Returns the raw JSON value of [provider].
- *
- * Unlike [provider], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonField = provider
-
- @JsonAnySetter
- private fun putAdditionalProperty(key: String, value: JsonValue) {
- additionalProperties.put(key, value)
- }
-
- @JsonAnyGetter
- @ExcludeMissing
- fun _additionalProperties(): Map =
- Collections.unmodifiableMap(additionalProperties)
-
- fun toBuilder() = Builder().from(this)
-
- companion object {
-
- /** Returns a mutable builder for constructing an instance of [ModelConfig]. */
- @JvmStatic fun builder() = Builder()
- }
-
- /** A builder for [ModelConfig]. */
- class Builder internal constructor() {
-
- private var apiKey: JsonField = JsonMissing.of()
- private var baseUrl: JsonField = JsonMissing.of()
- private var model: JsonField = JsonMissing.of()
- private var provider: JsonField = JsonMissing.of()
- private var additionalProperties: MutableMap = mutableMapOf()
-
- @JvmSynthetic
- internal fun from(modelConfig: ModelConfig) = apply {
- apiKey = modelConfig.apiKey
- baseUrl = modelConfig.baseUrl
- model = modelConfig.model
- provider = modelConfig.provider
- additionalProperties = modelConfig.additionalProperties.toMutableMap()
- }
+ fun asName(): String = name.getOrThrow("name")
- /** API key for the model provider */
- fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey))
+ fun asModelConfigObject(): ModelConfigObject = modelConfigObject.getOrThrow("modelConfigObject")
- /**
- * Sets [Builder.apiKey] to an arbitrary JSON value.
- *
- * You should usually call [Builder.apiKey] with a well-typed [String] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey }
-
- /** Custom base URL for API */
- fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl))
-
- /**
- * Sets [Builder.baseUrl] to an arbitrary JSON value.
- *
- * You should usually call [Builder.baseUrl] with a well-typed [String] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl }
+ fun _json(): Optional = Optional.ofNullable(_json)
- /** Model name */
- fun model(model: String) = model(JsonField.of(model))
-
- /**
- * Sets [Builder.model] to an arbitrary JSON value.
- *
- * You should usually call [Builder.model] with a well-typed [String] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun model(model: JsonField) = apply { this.model = model }
-
- fun provider(provider: Provider) = provider(JsonField.of(provider))
-
- /**
- * Sets [Builder.provider] to an arbitrary JSON value.
- *
- * You should usually call [Builder.provider] with a well-typed [Provider] value instead.
- * This method is primarily for setting the field to an undocumented or not yet supported
- * value.
- */
- fun provider(provider: JsonField) = apply { this.provider = provider }
-
- fun additionalProperties(additionalProperties: Map) = apply {
- this.additionalProperties.clear()
- putAllAdditionalProperties(additionalProperties)
+ fun accept(visitor: Visitor): T =
+ when {
+ name != null -> visitor.visitName(name)
+ modelConfigObject != null -> visitor.visitModelConfigObject(modelConfigObject)
+ else -> visitor.unknown(_json)
}
- fun putAdditionalProperty(key: String, value: JsonValue) = apply {
- additionalProperties.put(key, value)
- }
-
- fun putAllAdditionalProperties(additionalProperties: Map) = apply {
- this.additionalProperties.putAll(additionalProperties)
- }
-
- fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
-
- fun removeAllAdditionalProperties(keys: Set) = apply {
- keys.forEach(::removeAdditionalProperty)
- }
-
- /**
- * Returns an immutable instance of [ModelConfig].
- *
- * Further updates to this [Builder] will not mutate the returned instance.
- */
- fun build(): ModelConfig =
- ModelConfig(apiKey, baseUrl, model, provider, additionalProperties.toMutableMap())
- }
-
private var validated: Boolean = false
fun validate(): ModelConfig = apply {
@@ -208,10 +75,15 @@ private constructor(
return@apply
}
- apiKey()
- baseUrl()
- model()
- provider().ifPresent { it.validate() }
+ accept(
+ object : Visitor {
+ override fun visitName(name: String) {}
+
+ override fun visitModelConfigObject(modelConfigObject: ModelConfigObject) {
+ modelConfigObject.validate()
+ }
+ }
+ )
validated = true
}
@@ -230,112 +102,312 @@ private constructor(
*/
@JvmSynthetic
internal fun validity(): Int =
- (if (apiKey.asKnown().isPresent) 1 else 0) +
- (if (baseUrl.asKnown().isPresent) 1 else 0) +
- (if (model.asKnown().isPresent) 1 else 0) +
- (provider.asKnown().getOrNull()?.validity() ?: 0)
+ accept(
+ object : Visitor {
+ override fun visitName(name: String) = 1
+
+ override fun visitModelConfigObject(modelConfigObject: ModelConfigObject) =
+ modelConfigObject.validity()
+
+ override fun unknown(json: JsonValue?) = 0
+ }
+ )
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is ModelConfig &&
+ name == other.name &&
+ modelConfigObject == other.modelConfigObject
+ }
- class Provider @JsonCreator private constructor(private val value: JsonField) : Enum {
+ override fun hashCode(): Int = Objects.hash(name, modelConfigObject)
+
+ override fun toString(): String =
+ when {
+ name != null -> "ModelConfig{name=$name}"
+ modelConfigObject != null -> "ModelConfig{modelConfigObject=$modelConfigObject}"
+ _json != null -> "ModelConfig{_unknown=$_json}"
+ else -> throw IllegalStateException("Invalid ModelConfig")
+ }
+
+ companion object {
/**
- * Returns this class instance's raw value.
- *
- * This is usually only useful if this instance was deserialized from data that doesn't
- * match any known member, and you want to know that value. For example, if the SDK is on an
- * older version than the API, then the API may respond with new members that the SDK is
- * unaware of.
+ * Model name string with provider prefix (e.g., 'openai/gpt-5-nano',
+ * 'anthropic/claude-4.5-opus')
*/
- @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value
+ @JvmStatic fun ofName(name: String) = ModelConfig(name = name)
- companion object {
+ @JvmStatic
+ fun ofModelConfigObject(modelConfigObject: ModelConfigObject) =
+ ModelConfig(modelConfigObject = modelConfigObject)
+ }
- @JvmField val OPENAI = of("openai")
+ /**
+ * An interface that defines how to map each variant of [ModelConfig] to a value of type [T].
+ */
+ interface Visitor {
- @JvmField val ANTHROPIC = of("anthropic")
+ /**
+ * Model name string with provider prefix (e.g., 'openai/gpt-5-nano',
+ * 'anthropic/claude-4.5-opus')
+ */
+ fun visitName(name: String): T
- @JvmField val GOOGLE = of("google")
+ fun visitModelConfigObject(modelConfigObject: ModelConfigObject): T
- @JvmStatic fun of(value: String) = Provider(JsonField.of(value))
+ /**
+ * Maps an unknown variant of [ModelConfig] to a value of type [T].
+ *
+ * An instance of [ModelConfig] can contain an unknown variant if it was deserialized from
+ * data that doesn't match any known variant. For example, if the SDK is on an older version
+ * than the API, then the API may respond with new variants that the SDK is unaware of.
+ *
+ * @throws StagehandInvalidDataException in the default implementation.
+ */
+ fun unknown(json: JsonValue?): T {
+ throw StagehandInvalidDataException("Unknown ModelConfig: $json")
}
+ }
- /** An enum containing [Provider]'s known values. */
- enum class Known {
- OPENAI,
- ANTHROPIC,
- GOOGLE,
+ internal class Deserializer : BaseDeserializer(ModelConfig::class) {
+
+ override fun ObjectCodec.deserialize(node: JsonNode): ModelConfig {
+ val json = JsonValue.fromJsonNode(node)
+
+ val bestMatches =
+ sequenceOf(
+ tryDeserialize(node, jacksonTypeRef())?.let {
+ ModelConfig(modelConfigObject = it, _json = json)
+ },
+ tryDeserialize(node, jacksonTypeRef())?.let {
+ ModelConfig(name = it, _json = json)
+ },
+ )
+ .filterNotNull()
+ .allMaxBy { it.validity() }
+ .toList()
+ return when (bestMatches.size) {
+ // This can happen if what we're deserializing is completely incompatible with all
+ // the possible variants (e.g. deserializing from array).
+ 0 -> ModelConfig(_json = json)
+ 1 -> bestMatches.single()
+ // If there's more than one match with the highest validity, then use the first
+ // completely valid match, or simply the first match if none are completely valid.
+ else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first()
+ }
+ }
+ }
+
+ internal class Serializer : BaseSerializer(ModelConfig::class) {
+
+ override fun serialize(
+ value: ModelConfig,
+ generator: JsonGenerator,
+ provider: SerializerProvider,
+ ) {
+ when {
+ value.name != null -> generator.writeObject(value.name)
+ value.modelConfigObject != null -> generator.writeObject(value.modelConfigObject)
+ value._json != null -> generator.writeObject(value._json)
+ else -> throw IllegalStateException("Invalid ModelConfig")
+ }
}
+ }
+
+ class ModelConfigObject
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
+ private constructor(
+ private val modelName: JsonField,
+ private val apiKey: JsonField,
+ private val baseUrl: JsonField,
+ private val additionalProperties: MutableMap,
+ ) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("modelName")
+ @ExcludeMissing
+ modelName: JsonField = JsonMissing.of(),
+ @JsonProperty("apiKey") @ExcludeMissing apiKey: JsonField = JsonMissing.of(),
+ @JsonProperty("baseURL") @ExcludeMissing baseUrl: JsonField = JsonMissing.of(),
+ ) : this(modelName, apiKey, baseUrl, mutableMapOf())
/**
- * An enum containing [Provider]'s known values, as well as an [_UNKNOWN] member.
+ * Model name string without prefix (e.g., 'gpt-5-nano', 'claude-4.5-opus')
*
- * An instance of [Provider] can contain an unknown value in a couple of cases:
- * - It was deserialized from data that doesn't match any known member. For example, if the
- * SDK is on an older version than the API, then the API may respond with new members that
- * the SDK is unaware of.
- * - It was constructed with an arbitrary value using the [of] method.
+ * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
*/
- enum class Value {
- OPENAI,
- ANTHROPIC,
- GOOGLE,
- /** An enum member indicating that [Provider] was instantiated with an unknown value. */
- _UNKNOWN,
- }
+ fun modelName(): String = modelName.getRequired("modelName")
/**
- * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN]
- * if the class was instantiated with an unknown value.
+ * API key for the model provider
*
- * Use the [known] method instead if you're certain the value is always known or if you want
- * to throw for the unknown case.
+ * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if
+ * the server responded with an unexpected value).
*/
- fun value(): Value =
- when (this) {
- OPENAI -> Value.OPENAI
- ANTHROPIC -> Value.ANTHROPIC
- GOOGLE -> Value.GOOGLE
- else -> Value._UNKNOWN
- }
+ fun apiKey(): Optional = apiKey.getOptional("apiKey")
/**
- * Returns an enum member corresponding to this class instance's value.
+ * Base URL for the model provider
*
- * Use the [value] method instead if you're uncertain the value is always known and don't
- * want to throw for the unknown case.
+ * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if
+ * the server responded with an unexpected value).
+ */
+ fun baseUrl(): Optional = baseUrl.getOptional("baseURL")
+
+ /**
+ * Returns the raw JSON value of [modelName].
*
- * @throws StagehandInvalidDataException if this class instance's value is a not a known
- * member.
+ * Unlike [modelName], this method doesn't throw if the JSON field has an unexpected type.
*/
- fun known(): Known =
- when (this) {
- OPENAI -> Known.OPENAI
- ANTHROPIC -> Known.ANTHROPIC
- GOOGLE -> Known.GOOGLE
- else -> throw StagehandInvalidDataException("Unknown Provider: $value")
- }
+ @JsonProperty("modelName") @ExcludeMissing fun _modelName(): JsonField = modelName
/**
- * Returns this class instance's primitive wire representation.
+ * Returns the raw JSON value of [apiKey].
*
- * This differs from the [toString] method because that method is primarily for debugging
- * and generally doesn't throw.
+ * Unlike [apiKey], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("apiKey") @ExcludeMissing fun _apiKey(): JsonField = apiKey
+
+ /**
+ * Returns the raw JSON value of [baseUrl].
*
- * @throws StagehandInvalidDataException if this class instance's value does not have the
- * expected primitive type.
+ * Unlike [baseUrl], this method doesn't throw if the JSON field has an unexpected type.
*/
- fun asString(): String =
- _value().asString().orElseThrow {
- StagehandInvalidDataException("Value is not a String")
+ @JsonProperty("baseURL") @ExcludeMissing fun _baseUrl(): JsonField = baseUrl
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [ModelConfigObject].
+ *
+ * The following fields are required:
+ * ```java
+ * .modelName()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [ModelConfigObject]. */
+ class Builder internal constructor() {
+
+ private var modelName: JsonField? = null
+ private var apiKey: JsonField = JsonMissing.of()
+ private var baseUrl: JsonField = JsonMissing.of()
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(modelConfigObject: ModelConfigObject) = apply {
+ modelName = modelConfigObject.modelName
+ apiKey = modelConfigObject.apiKey
+ baseUrl = modelConfigObject.baseUrl
+ additionalProperties = modelConfigObject.additionalProperties.toMutableMap()
+ }
+
+ /** Model name string without prefix (e.g., 'gpt-5-nano', 'claude-4.5-opus') */
+ fun modelName(modelName: String) = modelName(JsonField.of(modelName))
+
+ /**
+ * Sets [Builder.modelName] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.modelName] with a well-typed [String] value instead.
+ * This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun modelName(modelName: JsonField) = apply { this.modelName = modelName }
+
+ /** API key for the model provider */
+ fun apiKey(apiKey: String) = apiKey(JsonField.of(apiKey))
+
+ /**
+ * Sets [Builder.apiKey] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.apiKey] with a well-typed [String] value instead.
+ * This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun apiKey(apiKey: JsonField) = apply { this.apiKey = apiKey }
+
+ /** Base URL for the model provider */
+ fun baseUrl(baseUrl: String) = baseUrl(JsonField.of(baseUrl))
+
+ /**
+ * Sets [Builder.baseUrl] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.baseUrl] with a well-typed [String] value instead.
+ * This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun baseUrl(baseUrl: JsonField) = apply { this.baseUrl = baseUrl }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
}
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [ModelConfigObject].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .modelName()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): ModelConfigObject =
+ ModelConfigObject(
+ checkRequired("modelName", modelName),
+ apiKey,
+ baseUrl,
+ additionalProperties.toMutableMap(),
+ )
+ }
+
private var validated: Boolean = false
- fun validate(): Provider = apply {
+ fun validate(): ModelConfigObject = apply {
if (validated) {
return@apply
}
- known()
+ modelName()
+ apiKey()
+ baseUrl()
validated = true
}
@@ -353,40 +425,31 @@ private constructor(
*
* Used for best match union deserialization.
*/
- @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (if (modelName.asKnown().isPresent) 1 else 0) +
+ (if (apiKey.asKnown().isPresent) 1 else 0) +
+ (if (baseUrl.asKnown().isPresent) 1 else 0)
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
- return other is Provider && value == other.value
+ return other is ModelConfigObject &&
+ modelName == other.modelName &&
+ apiKey == other.apiKey &&
+ baseUrl == other.baseUrl &&
+ additionalProperties == other.additionalProperties
}
- override fun hashCode() = value.hashCode()
-
- override fun toString() = value.toString()
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other) {
- return true
+ private val hashCode: Int by lazy {
+ Objects.hash(modelName, apiKey, baseUrl, additionalProperties)
}
- return other is ModelConfig &&
- apiKey == other.apiKey &&
- baseUrl == other.baseUrl &&
- model == other.model &&
- provider == other.provider &&
- additionalProperties == other.additionalProperties
- }
+ override fun hashCode(): Int = hashCode
- private val hashCode: Int by lazy {
- Objects.hash(apiKey, baseUrl, model, provider, additionalProperties)
+ override fun toString() =
+ "ModelConfigObject{modelName=$modelName, apiKey=$apiKey, baseUrl=$baseUrl, additionalProperties=$additionalProperties}"
}
-
- override fun hashCode(): Int = hashCode
-
- override fun toString() =
- "ModelConfig{apiKey=$apiKey, baseUrl=$baseUrl, model=$model, provider=$provider, additionalProperties=$additionalProperties}"
}
diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActParams.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActParams.kt
index 34cae33..a37780c 100644
--- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActParams.kt
+++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActParams.kt
@@ -28,30 +28,43 @@ import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
+import java.time.OffsetDateTime
+import java.time.format.DateTimeFormatter
import java.util.Collections
import java.util.Objects
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
-/**
- * Performs a browser action based on natural language instruction or a specific action object
- * returned by observe().
- */
+/** Executes a browser action using natural language instructions or a predefined Action object. */
class SessionActParams
private constructor(
- private val sessionId: String?,
+ private val id: String?,
+ private val xLanguage: XLanguage?,
+ private val xSdkVersion: String?,
+ private val xSentAt: OffsetDateTime?,
private val xStreamResponse: XStreamResponse?,
private val body: Body,
private val additionalHeaders: Headers,
private val additionalQueryParams: QueryParams,
) : Params {
- fun sessionId(): Optional = Optional.ofNullable(sessionId)
+ /** Unique session identifier */
+ fun id(): Optional = Optional.ofNullable(id)
+ /** Client SDK language */
+ fun xLanguage(): Optional = Optional.ofNullable(xLanguage)
+
+ /** Version of the Stagehand SDK */
+ fun xSdkVersion(): Optional = Optional.ofNullable(xSdkVersion)
+
+ /** ISO timestamp when request was sent */
+ fun xSentAt(): Optional = Optional.ofNullable(xSentAt)
+
+ /** Whether to stream the response via SSE */
fun xStreamResponse(): Optional = Optional.ofNullable(xStreamResponse)
/**
- * Natural language instruction
+ * Natural language instruction or Action object
*
* @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
* unexpectedly missing or null (e.g. if the server responded with an unexpected value).
@@ -59,7 +72,7 @@ private constructor(
fun input(): Input = body.input()
/**
- * Frame ID to act on (optional)
+ * Target frame ID for the action
*
* @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
@@ -119,7 +132,10 @@ private constructor(
/** A builder for [SessionActParams]. */
class Builder internal constructor() {
- private var sessionId: String? = null
+ private var id: String? = null
+ private var xLanguage: XLanguage? = null
+ private var xSdkVersion: String? = null
+ private var xSentAt: OffsetDateTime? = null
private var xStreamResponse: XStreamResponse? = null
private var body: Body.Builder = Body.builder()
private var additionalHeaders: Headers.Builder = Headers.builder()
@@ -127,18 +143,41 @@ private constructor(
@JvmSynthetic
internal fun from(sessionActParams: SessionActParams) = apply {
- sessionId = sessionActParams.sessionId
+ id = sessionActParams.id
+ xLanguage = sessionActParams.xLanguage
+ xSdkVersion = sessionActParams.xSdkVersion
+ xSentAt = sessionActParams.xSentAt
xStreamResponse = sessionActParams.xStreamResponse
body = sessionActParams.body.toBuilder()
additionalHeaders = sessionActParams.additionalHeaders.toBuilder()
additionalQueryParams = sessionActParams.additionalQueryParams.toBuilder()
}
- fun sessionId(sessionId: String?) = apply { this.sessionId = sessionId }
+ /** Unique session identifier */
+ fun id(id: String?) = apply { this.id = id }
+
+ /** Alias for calling [Builder.id] with `id.orElse(null)`. */
+ fun id(id: Optional) = id(id.getOrNull())
+
+ /** Client SDK language */
+ fun xLanguage(xLanguage: XLanguage?) = apply { this.xLanguage = xLanguage }
+
+ /** Alias for calling [Builder.xLanguage] with `xLanguage.orElse(null)`. */
+ fun xLanguage(xLanguage: Optional) = xLanguage(xLanguage.getOrNull())
+
+ /** Version of the Stagehand SDK */
+ fun xSdkVersion(xSdkVersion: String?) = apply { this.xSdkVersion = xSdkVersion }
+
+ /** Alias for calling [Builder.xSdkVersion] with `xSdkVersion.orElse(null)`. */
+ fun xSdkVersion(xSdkVersion: Optional) = xSdkVersion(xSdkVersion.getOrNull())
- /** Alias for calling [Builder.sessionId] with `sessionId.orElse(null)`. */
- fun sessionId(sessionId: Optional) = sessionId(sessionId.getOrNull())
+ /** ISO timestamp when request was sent */
+ fun xSentAt(xSentAt: OffsetDateTime?) = apply { this.xSentAt = xSentAt }
+ /** Alias for calling [Builder.xSentAt] with `xSentAt.orElse(null)`. */
+ fun xSentAt(xSentAt: Optional) = xSentAt(xSentAt.getOrNull())
+
+ /** Whether to stream the response via SSE */
fun xStreamResponse(xStreamResponse: XStreamResponse?) = apply {
this.xStreamResponse = xStreamResponse
}
@@ -158,7 +197,7 @@ private constructor(
*/
fun body(body: Body) = apply { this.body = body.toBuilder() }
- /** Natural language instruction */
+ /** Natural language instruction or Action object */
fun input(input: Input) = apply { body.input(input) }
/**
@@ -175,7 +214,7 @@ private constructor(
/** Alias for calling [input] with `Input.ofAction(action)`. */
fun input(action: Action) = apply { body.input(action) }
- /** Frame ID to act on (optional) */
+ /** Target frame ID for the action */
fun frameId(frameId: String) = apply { body.frameId(frameId) }
/**
@@ -327,7 +366,10 @@ private constructor(
*/
fun build(): SessionActParams =
SessionActParams(
- sessionId,
+ id,
+ xLanguage,
+ xSdkVersion,
+ xSentAt,
xStreamResponse,
body.build(),
additionalHeaders.build(),
@@ -339,13 +381,16 @@ private constructor(
fun _pathParam(index: Int): String =
when (index) {
- 0 -> sessionId ?: ""
+ 0 -> id ?: ""
else -> ""
}
override fun _headers(): Headers =
Headers.builder()
.apply {
+ xLanguage?.let { put("x-language", it.toString()) }
+ xSdkVersion?.let { put("x-sdk-version", it) }
+ xSentAt?.let { put("x-sent-at", DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(it)) }
xStreamResponse?.let { put("x-stream-response", it.toString()) }
putAll(additionalHeaders)
}
@@ -370,7 +415,7 @@ private constructor(
) : this(input, frameId, options, mutableMapOf())
/**
- * Natural language instruction
+ * Natural language instruction or Action object
*
* @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
* unexpectedly missing or null (e.g. if the server responded with an unexpected value).
@@ -378,7 +423,7 @@ private constructor(
fun input(): Input = input.getRequired("input")
/**
- * Frame ID to act on (optional)
+ * Target frame ID for the action
*
* @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if
* the server responded with an unexpected value).
@@ -453,7 +498,7 @@ private constructor(
additionalProperties = body.additionalProperties.toMutableMap()
}
- /** Natural language instruction */
+ /** Natural language instruction or Action object */
fun input(input: Input) = input(JsonField.of(input))
/**
@@ -471,7 +516,7 @@ private constructor(
/** Alias for calling [input] with `Input.ofAction(action)`. */
fun input(action: Action) = input(Input.ofAction(action))
- /** Frame ID to act on (optional) */
+ /** Target frame ID for the action */
fun frameId(frameId: String) = frameId(JsonField.of(frameId))
/**
@@ -589,7 +634,7 @@ private constructor(
"Body{input=$input, frameId=$frameId, options=$options, additionalProperties=$additionalProperties}"
}
- /** Natural language instruction */
+ /** Natural language instruction or Action object */
@JsonDeserialize(using = Input.Deserializer::class)
@JsonSerialize(using = Input.Serializer::class)
class Input
@@ -599,18 +644,18 @@ private constructor(
private val _json: JsonValue? = null,
) {
- /** Natural language instruction */
fun string(): Optional = Optional.ofNullable(string)
+ /** Action object returned by observe and used by act */
fun action(): Optional = Optional.ofNullable(action)
fun isString(): Boolean = string != null
fun isAction(): Boolean = action != null
- /** Natural language instruction */
fun asString(): String = string.getOrThrow("string")
+ /** Action object returned by observe and used by act */
fun asAction(): Action = action.getOrThrow("action")
fun _json(): Optional = Optional.ofNullable(_json)
@@ -687,18 +732,18 @@ private constructor(
companion object {
- /** Natural language instruction */
@JvmStatic fun ofString(string: String) = Input(string = string)
+ /** Action object returned by observe and used by act */
@JvmStatic fun ofAction(action: Action) = Input(action = action)
}
/** An interface that defines how to map each variant of [Input] to a value of type [T]. */
interface Visitor {
- /** Natural language instruction */
fun visitString(string: String): T
+ /** Action object returned by observe and used by act */
fun visitAction(action: Action): T
/**
@@ -767,7 +812,7 @@ private constructor(
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
private val model: JsonField,
- private val timeout: JsonField,
+ private val timeout: JsonField,
private val variables: JsonField,
private val additionalProperties: MutableMap,
) {
@@ -775,28 +820,31 @@ private constructor(
@JsonCreator
private constructor(
@JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(),
- @JsonProperty("timeout") @ExcludeMissing timeout: JsonField = JsonMissing.of(),
+ @JsonProperty("timeout") @ExcludeMissing timeout: JsonField = JsonMissing.of(),
@JsonProperty("variables")
@ExcludeMissing
variables: JsonField = JsonMissing.of(),
) : this(model, timeout, variables, mutableMapOf())
/**
+ * Model name string with provider prefix (e.g., 'openai/gpt-5-nano',
+ * 'anthropic/claude-4.5-opus')
+ *
* @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if
* the server responded with an unexpected value).
*/
fun model(): Optional = model.getOptional("model")
/**
- * Timeout in milliseconds
+ * Timeout in ms for the action
*
* @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if
* the server responded with an unexpected value).
*/
- fun timeout(): Optional = timeout.getOptional("timeout")
+ fun timeout(): Optional = timeout.getOptional("timeout")
/**
- * Template variables for instruction
+ * Variables to substitute in the action instruction
*
* @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if
* the server responded with an unexpected value).
@@ -815,7 +863,7 @@ private constructor(
*
* Unlike [timeout], this method doesn't throw if the JSON field has an unexpected type.
*/
- @JsonProperty("timeout") @ExcludeMissing fun _timeout(): JsonField = timeout
+ @JsonProperty("timeout") @ExcludeMissing fun _timeout(): JsonField = timeout
/**
* Returns the raw JSON value of [variables].
@@ -848,7 +896,7 @@ private constructor(
class Builder internal constructor() {
private var model: JsonField = JsonMissing.of()
- private var timeout: JsonField = JsonMissing.of()
+ private var timeout: JsonField = JsonMissing.of()
private var variables: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@@ -860,6 +908,10 @@ private constructor(
additionalProperties = options.additionalProperties.toMutableMap()
}
+ /**
+ * Model name string with provider prefix (e.g., 'openai/gpt-5-nano',
+ * 'anthropic/claude-4.5-opus')
+ */
fun model(model: ModelConfig) = model(JsonField.of(model))
/**
@@ -871,19 +923,28 @@ private constructor(
*/
fun model(model: JsonField) = apply { this.model = model }
- /** Timeout in milliseconds */
- fun timeout(timeout: Long) = timeout(JsonField.of(timeout))
+ /** Alias for calling [model] with `ModelConfig.ofName(name)`. */
+ fun model(name: String) = model(ModelConfig.ofName(name))
+
+ /**
+ * Alias for calling [model] with `ModelConfig.ofModelConfigObject(modelConfigObject)`.
+ */
+ fun model(modelConfigObject: ModelConfig.ModelConfigObject) =
+ model(ModelConfig.ofModelConfigObject(modelConfigObject))
+
+ /** Timeout in ms for the action */
+ fun timeout(timeout: Double) = timeout(JsonField.of(timeout))
/**
* Sets [Builder.timeout] to an arbitrary JSON value.
*
- * You should usually call [Builder.timeout] with a well-typed [Long] value instead.
+ * You should usually call [Builder.timeout] with a well-typed [Double] value instead.
* This method is primarily for setting the field to an undocumented or not yet
* supported value.
*/
- fun timeout(timeout: JsonField) = apply { this.timeout = timeout }
+ fun timeout(timeout: JsonField) = apply { this.timeout = timeout }
- /** Template variables for instruction */
+ /** Variables to substitute in the action instruction */
fun variables(variables: Variables) = variables(JsonField.of(variables))
/**
@@ -956,7 +1017,7 @@ private constructor(
(if (timeout.asKnown().isPresent) 1 else 0) +
(variables.asKnown().getOrNull()?.validity() ?: 0)
- /** Template variables for instruction */
+ /** Variables to substitute in the action instruction */
class Variables
@JsonCreator
private constructor(
@@ -1081,6 +1142,143 @@ private constructor(
"Options{model=$model, timeout=$timeout, variables=$variables, additionalProperties=$additionalProperties}"
}
+ /** Client SDK language */
+ class XLanguage @JsonCreator private constructor(private val value: JsonField) : Enum {
+
+ /**
+ * Returns this class instance's raw value.
+ *
+ * This is usually only useful if this instance was deserialized from data that doesn't
+ * match any known member, and you want to know that value. For example, if the SDK is on an
+ * older version than the API, then the API may respond with new members that the SDK is
+ * unaware of.
+ */
+ @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value
+
+ companion object {
+
+ @JvmField val TYPESCRIPT = of("typescript")
+
+ @JvmField val PYTHON = of("python")
+
+ @JvmField val PLAYGROUND = of("playground")
+
+ @JvmStatic fun of(value: String) = XLanguage(JsonField.of(value))
+ }
+
+ /** An enum containing [XLanguage]'s known values. */
+ enum class Known {
+ TYPESCRIPT,
+ PYTHON,
+ PLAYGROUND,
+ }
+
+ /**
+ * An enum containing [XLanguage]'s known values, as well as an [_UNKNOWN] member.
+ *
+ * An instance of [XLanguage] can contain an unknown value in a couple of cases:
+ * - It was deserialized from data that doesn't match any known member. For example, if the
+ * SDK is on an older version than the API, then the API may respond with new members that
+ * the SDK is unaware of.
+ * - It was constructed with an arbitrary value using the [of] method.
+ */
+ enum class Value {
+ TYPESCRIPT,
+ PYTHON,
+ PLAYGROUND,
+ /**
+ * An enum member indicating that [XLanguage] was instantiated with an unknown value.
+ */
+ _UNKNOWN,
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN]
+ * if the class was instantiated with an unknown value.
+ *
+ * Use the [known] method instead if you're certain the value is always known or if you want
+ * to throw for the unknown case.
+ */
+ fun value(): Value =
+ when (this) {
+ TYPESCRIPT -> Value.TYPESCRIPT
+ PYTHON -> Value.PYTHON
+ PLAYGROUND -> Value.PLAYGROUND
+ else -> Value._UNKNOWN
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value.
+ *
+ * Use the [value] method instead if you're uncertain the value is always known and don't
+ * want to throw for the unknown case.
+ *
+ * @throws StagehandInvalidDataException if this class instance's value is a not a known
+ * member.
+ */
+ fun known(): Known =
+ when (this) {
+ TYPESCRIPT -> Known.TYPESCRIPT
+ PYTHON -> Known.PYTHON
+ PLAYGROUND -> Known.PLAYGROUND
+ else -> throw StagehandInvalidDataException("Unknown XLanguage: $value")
+ }
+
+ /**
+ * Returns this class instance's primitive wire representation.
+ *
+ * This differs from the [toString] method because that method is primarily for debugging
+ * and generally doesn't throw.
+ *
+ * @throws StagehandInvalidDataException if this class instance's value does not have the
+ * expected primitive type.
+ */
+ fun asString(): String =
+ _value().asString().orElseThrow {
+ StagehandInvalidDataException("Value is not a String")
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): XLanguage = apply {
+ if (validated) {
+ return@apply
+ }
+
+ known()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: StagehandInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is XLanguage && value == other.value
+ }
+
+ override fun hashCode() = value.hashCode()
+
+ override fun toString() = value.toString()
+ }
+
+ /** Whether to stream the response via SSE */
class XStreamResponse @JsonCreator private constructor(private val value: JsonField) :
Enum {
@@ -1218,7 +1416,10 @@ private constructor(
}
return other is SessionActParams &&
- sessionId == other.sessionId &&
+ id == other.id &&
+ xLanguage == other.xLanguage &&
+ xSdkVersion == other.xSdkVersion &&
+ xSentAt == other.xSentAt &&
xStreamResponse == other.xStreamResponse &&
body == other.body &&
additionalHeaders == other.additionalHeaders &&
@@ -1226,8 +1427,17 @@ private constructor(
}
override fun hashCode(): Int =
- Objects.hash(sessionId, xStreamResponse, body, additionalHeaders, additionalQueryParams)
+ Objects.hash(
+ id,
+ xLanguage,
+ xSdkVersion,
+ xSentAt,
+ xStreamResponse,
+ body,
+ additionalHeaders,
+ additionalQueryParams,
+ )
override fun toString() =
- "SessionActParams{sessionId=$sessionId, xStreamResponse=$xStreamResponse, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}"
+ "SessionActParams{id=$id, xLanguage=$xLanguage, xSdkVersion=$xSdkVersion, xSentAt=$xSentAt, xStreamResponse=$xStreamResponse, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}"
}
diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActResponse.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActResponse.kt
index 69dfcab..db27614 100644
--- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActResponse.kt
+++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionActResponse.kt
@@ -16,44 +16,31 @@ import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
import java.util.Collections
import java.util.Objects
+import java.util.Optional
import kotlin.jvm.optionals.getOrNull
class SessionActResponse
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
- private val actions: JsonField>,
- private val message: JsonField,
+ private val data: JsonField,
private val success: JsonField,
private val additionalProperties: MutableMap,
) {
@JsonCreator
private constructor(
- @JsonProperty("actions")
- @ExcludeMissing
- actions: JsonField> = JsonMissing.of(),
- @JsonProperty("message") @ExcludeMissing message: JsonField = JsonMissing.of(),
+ @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(),
@JsonProperty("success") @ExcludeMissing success: JsonField = JsonMissing.of(),
- ) : this(actions, message, success, mutableMapOf())
-
- /**
- * Actions that were executed
- *
- * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
- * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
- */
- fun actions(): List = actions.getRequired("actions")
+ ) : this(data, success, mutableMapOf())
/**
- * Result message
- *
* @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
* unexpectedly missing or null (e.g. if the server responded with an unexpected value).
*/
- fun message(): String = message.getRequired("message")
+ fun data(): Data = data.getRequired("data")
/**
- * Whether the action succeeded
+ * Indicates whether the request was successful
*
* @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
* unexpectedly missing or null (e.g. if the server responded with an unexpected value).
@@ -61,18 +48,11 @@ private constructor(
fun success(): Boolean = success.getRequired("success")
/**
- * Returns the raw JSON value of [actions].
- *
- * Unlike [actions], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("actions") @ExcludeMissing fun _actions(): JsonField> = actions
-
- /**
- * Returns the raw JSON value of [message].
+ * Returns the raw JSON value of [data].
*
- * Unlike [message], this method doesn't throw if the JSON field has an unexpected type.
+ * Unlike [data], this method doesn't throw if the JSON field has an unexpected type.
*/
- @JsonProperty("message") @ExcludeMissing fun _message(): JsonField = message
+ @JsonProperty("data") @ExcludeMissing fun _data(): JsonField = data
/**
* Returns the raw JSON value of [success].
@@ -100,8 +80,7 @@ private constructor(
*
* The following fields are required:
* ```java
- * .actions()
- * .message()
+ * .data()
* .success()
* ```
*/
@@ -111,57 +90,28 @@ private constructor(
/** A builder for [SessionActResponse]. */
class Builder internal constructor() {
- private var actions: JsonField>? = null
- private var message: JsonField? = null
+ private var data: JsonField? = null
private var success: JsonField? = null
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
internal fun from(sessionActResponse: SessionActResponse) = apply {
- actions = sessionActResponse.actions.map { it.toMutableList() }
- message = sessionActResponse.message
+ data = sessionActResponse.data
success = sessionActResponse.success
additionalProperties = sessionActResponse.additionalProperties.toMutableMap()
}
- /** Actions that were executed */
- fun actions(actions: List) = actions(JsonField.of(actions))
-
- /**
- * Sets [Builder.actions] to an arbitrary JSON value.
- *
- * You should usually call [Builder.actions] with a well-typed `List` value instead.
- * This method is primarily for setting the field to an undocumented or not yet supported
- * value.
- */
- fun actions(actions: JsonField>) = apply {
- this.actions = actions.map { it.toMutableList() }
- }
-
- /**
- * Adds a single [Action] to [actions].
- *
- * @throws IllegalStateException if the field was previously set to a non-list.
- */
- fun addAction(action: Action) = apply {
- actions =
- (actions ?: JsonField.of(mutableListOf())).also {
- checkKnown("actions", it).add(action)
- }
- }
-
- /** Result message */
- fun message(message: String) = message(JsonField.of(message))
+ fun data(data: Data) = data(JsonField.of(data))
/**
- * Sets [Builder.message] to an arbitrary JSON value.
+ * Sets [Builder.data] to an arbitrary JSON value.
*
- * You should usually call [Builder.message] with a well-typed [String] value instead. This
+ * You should usually call [Builder.data] with a well-typed [Data] value instead. This
* method is primarily for setting the field to an undocumented or not yet supported value.
*/
- fun message(message: JsonField) = apply { this.message = message }
+ fun data(data: JsonField) = apply { this.data = data }
- /** Whether the action succeeded */
+ /** Indicates whether the request was successful */
fun success(success: Boolean) = success(JsonField.of(success))
/**
@@ -198,8 +148,7 @@ private constructor(
*
* The following fields are required:
* ```java
- * .actions()
- * .message()
+ * .data()
* .success()
* ```
*
@@ -207,8 +156,7 @@ private constructor(
*/
fun build(): SessionActResponse =
SessionActResponse(
- checkRequired("actions", actions).map { it.toImmutable() },
- checkRequired("message", message),
+ checkRequired("data", data),
checkRequired("success", success),
additionalProperties.toMutableMap(),
)
@@ -221,8 +169,7 @@ private constructor(
return@apply
}
- actions().forEach { it.validate() }
- message()
+ data().validate()
success()
validated = true
}
@@ -242,9 +189,507 @@ private constructor(
*/
@JvmSynthetic
internal fun validity(): Int =
- (actions.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
- (if (message.asKnown().isPresent) 1 else 0) +
- (if (success.asKnown().isPresent) 1 else 0)
+ (data.asKnown().getOrNull()?.validity() ?: 0) + (if (success.asKnown().isPresent) 1 else 0)
+
+ class Data
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
+ private constructor(
+ private val result: JsonField,
+ private val actionId: JsonField,
+ private val additionalProperties: MutableMap,
+ ) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("result") @ExcludeMissing result: JsonField = JsonMissing.of(),
+ @JsonProperty("actionId") @ExcludeMissing actionId: JsonField = JsonMissing.of(),
+ ) : this(result, actionId, mutableMapOf())
+
+ /**
+ * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun result(): Result = result.getRequired("result")
+
+ /**
+ * Action ID for tracking
+ *
+ * @throws StagehandInvalidDataException if the JSON field has an unexpected type (e.g. if
+ * the server responded with an unexpected value).
+ */
+ fun actionId(): Optional = actionId.getOptional("actionId")
+
+ /**
+ * Returns the raw JSON value of [result].
+ *
+ * Unlike [result], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("result") @ExcludeMissing fun _result(): JsonField = result
+
+ /**
+ * Returns the raw JSON value of [actionId].
+ *
+ * Unlike [actionId], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("actionId") @ExcludeMissing fun _actionId(): JsonField = actionId
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [Data].
+ *
+ * The following fields are required:
+ * ```java
+ * .result()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [Data]. */
+ class Builder internal constructor() {
+
+ private var result: JsonField? = null
+ private var actionId: JsonField = JsonMissing.of()
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(data: Data) = apply {
+ result = data.result
+ actionId = data.actionId
+ additionalProperties = data.additionalProperties.toMutableMap()
+ }
+
+ fun result(result: Result) = result(JsonField.of(result))
+
+ /**
+ * Sets [Builder.result] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.result] with a well-typed [Result] value instead.
+ * This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun result(result: JsonField) = apply { this.result = result }
+
+ /** Action ID for tracking */
+ fun actionId(actionId: String) = actionId(JsonField.of(actionId))
+
+ /**
+ * Sets [Builder.actionId] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.actionId] with a well-typed [String] value instead.
+ * This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun actionId(actionId: JsonField) = apply { this.actionId = actionId }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [Data].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .result()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): Data =
+ Data(checkRequired("result", result), actionId, additionalProperties.toMutableMap())
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): Data = apply {
+ if (validated) {
+ return@apply
+ }
+
+ result().validate()
+ actionId()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: StagehandInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (result.asKnown().getOrNull()?.validity() ?: 0) +
+ (if (actionId.asKnown().isPresent) 1 else 0)
+
+ class Result
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
+ private constructor(
+ private val actionDescription: JsonField,
+ private val actions: JsonField>,
+ private val message: JsonField,
+ private val success: JsonField,
+ private val additionalProperties: MutableMap,
+ ) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("actionDescription")
+ @ExcludeMissing
+ actionDescription: JsonField = JsonMissing.of(),
+ @JsonProperty("actions")
+ @ExcludeMissing
+ actions: JsonField> = JsonMissing.of(),
+ @JsonProperty("message")
+ @ExcludeMissing
+ message: JsonField = JsonMissing.of(),
+ @JsonProperty("success")
+ @ExcludeMissing
+ success: JsonField = JsonMissing.of(),
+ ) : this(actionDescription, actions, message, success, mutableMapOf())
+
+ /**
+ * Description of the action that was performed
+ *
+ * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected
+ * value).
+ */
+ fun actionDescription(): String = actionDescription.getRequired("actionDescription")
+
+ /**
+ * List of actions that were executed
+ *
+ * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected
+ * value).
+ */
+ fun actions(): List = actions.getRequired("actions")
+
+ /**
+ * Human-readable result message
+ *
+ * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected
+ * value).
+ */
+ fun message(): String = message.getRequired("message")
+
+ /**
+ * Whether the action completed successfully
+ *
+ * @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected
+ * value).
+ */
+ fun success(): Boolean = success.getRequired("success")
+
+ /**
+ * Returns the raw JSON value of [actionDescription].
+ *
+ * Unlike [actionDescription], this method doesn't throw if the JSON field has an
+ * unexpected type.
+ */
+ @JsonProperty("actionDescription")
+ @ExcludeMissing
+ fun _actionDescription(): JsonField = actionDescription
+
+ /**
+ * Returns the raw JSON value of [actions].
+ *
+ * Unlike [actions], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("actions")
+ @ExcludeMissing
+ fun _actions(): JsonField> = actions
+
+ /**
+ * Returns the raw JSON value of [message].
+ *
+ * Unlike [message], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("message") @ExcludeMissing fun _message(): JsonField = message
+
+ /**
+ * Returns the raw JSON value of [success].
+ *
+ * Unlike [success], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("success") @ExcludeMissing fun _success(): JsonField = success
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [Result].
+ *
+ * The following fields are required:
+ * ```java
+ * .actionDescription()
+ * .actions()
+ * .message()
+ * .success()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [Result]. */
+ class Builder internal constructor() {
+
+ private var actionDescription: JsonField? = null
+ private var actions: JsonField>? = null
+ private var message: JsonField? = null
+ private var success: JsonField? = null
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(result: Result) = apply {
+ actionDescription = result.actionDescription
+ actions = result.actions.map { it.toMutableList() }
+ message = result.message
+ success = result.success
+ additionalProperties = result.additionalProperties.toMutableMap()
+ }
+
+ /** Description of the action that was performed */
+ fun actionDescription(actionDescription: String) =
+ actionDescription(JsonField.of(actionDescription))
+
+ /**
+ * Sets [Builder.actionDescription] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.actionDescription] with a well-typed [String]
+ * value instead. This method is primarily for setting the field to an undocumented
+ * or not yet supported value.
+ */
+ fun actionDescription(actionDescription: JsonField) = apply {
+ this.actionDescription = actionDescription
+ }
+
+ /** List of actions that were executed */
+ fun actions(actions: List) = actions(JsonField.of(actions))
+
+ /**
+ * Sets [Builder.actions] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.actions] with a well-typed `List` value
+ * instead. This method is primarily for setting the field to an undocumented or not
+ * yet supported value.
+ */
+ fun actions(actions: JsonField>) = apply {
+ this.actions = actions.map { it.toMutableList() }
+ }
+
+ /**
+ * Adds a single [Action] to [actions].
+ *
+ * @throws IllegalStateException if the field was previously set to a non-list.
+ */
+ fun addAction(action: Action) = apply {
+ actions =
+ (actions ?: JsonField.of(mutableListOf())).also {
+ checkKnown("actions", it).add(action)
+ }
+ }
+
+ /** Human-readable result message */
+ fun message(message: String) = message(JsonField.of(message))
+
+ /**
+ * Sets [Builder.message] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.message] with a well-typed [String] value
+ * instead. This method is primarily for setting the field to an undocumented or not
+ * yet supported value.
+ */
+ fun message(message: JsonField) = apply { this.message = message }
+
+ /** Whether the action completed successfully */
+ fun success(success: Boolean) = success(JsonField.of(success))
+
+ /**
+ * Sets [Builder.success] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.success] with a well-typed [Boolean] value
+ * instead. This method is primarily for setting the field to an undocumented or not
+ * yet supported value.
+ */
+ fun success(success: JsonField) = apply { this.success = success }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) =
+ apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply {
+ additionalProperties.remove(key)
+ }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [Result].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .actionDescription()
+ * .actions()
+ * .message()
+ * .success()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): Result =
+ Result(
+ checkRequired("actionDescription", actionDescription),
+ checkRequired("actions", actions).map { it.toImmutable() },
+ checkRequired("message", message),
+ checkRequired("success", success),
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): Result = apply {
+ if (validated) {
+ return@apply
+ }
+
+ actionDescription()
+ actions().forEach { it.validate() }
+ message()
+ success()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: StagehandInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (if (actionDescription.asKnown().isPresent) 1 else 0) +
+ (actions.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
+ (if (message.asKnown().isPresent) 1 else 0) +
+ (if (success.asKnown().isPresent) 1 else 0)
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Result &&
+ actionDescription == other.actionDescription &&
+ actions == other.actions &&
+ message == other.message &&
+ success == other.success &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy {
+ Objects.hash(actionDescription, actions, message, success, additionalProperties)
+ }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "Result{actionDescription=$actionDescription, actions=$actions, message=$message, success=$success, additionalProperties=$additionalProperties}"
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Data &&
+ result == other.result &&
+ actionId == other.actionId &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy { Objects.hash(result, actionId, additionalProperties) }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "Data{result=$result, actionId=$actionId, additionalProperties=$additionalProperties}"
+ }
override fun equals(other: Any?): Boolean {
if (this === other) {
@@ -252,18 +697,15 @@ private constructor(
}
return other is SessionActResponse &&
- actions == other.actions &&
- message == other.message &&
+ data == other.data &&
success == other.success &&
additionalProperties == other.additionalProperties
}
- private val hashCode: Int by lazy {
- Objects.hash(actions, message, success, additionalProperties)
- }
+ private val hashCode: Int by lazy { Objects.hash(data, success, additionalProperties) }
override fun hashCode(): Int = hashCode
override fun toString() =
- "SessionActResponse{actions=$actions, message=$message, success=$success, additionalProperties=$additionalProperties}"
+ "SessionActResponse{data=$data, success=$success, additionalProperties=$additionalProperties}"
}
diff --git a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndParams.kt b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndParams.kt
index a3bb3fe..90a656f 100644
--- a/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndParams.kt
+++ b/stagehand-java-core/src/main/kotlin/com/browserbase/api/models/sessions/SessionEndParams.kt
@@ -2,25 +2,48 @@
package com.browserbase.api.models.sessions
+import com.browserbase.api.core.Enum
+import com.browserbase.api.core.JsonField
import com.browserbase.api.core.JsonValue
import com.browserbase.api.core.Params
import com.browserbase.api.core.http.Headers
import com.browserbase.api.core.http.QueryParams
import com.browserbase.api.core.toImmutable
+import com.browserbase.api.errors.StagehandInvalidDataException
+import com.fasterxml.jackson.annotation.JsonCreator
+import java.time.OffsetDateTime
+import java.time.format.DateTimeFormatter
import java.util.Objects
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
-/** Closes the browser and cleans up all resources associated with the session. */
+/** Terminates the browser session and releases all associated resources. */
class SessionEndParams
private constructor(
- private val sessionId: String?,
+ private val id: String?,
+ private val xLanguage: XLanguage?,
+ private val xSdkVersion: String?,
+ private val xSentAt: OffsetDateTime?,
+ private val xStreamResponse: XStreamResponse?,
private val additionalHeaders: Headers,
private val additionalQueryParams: QueryParams,
private val additionalBodyProperties: Map,
) : Params {
- fun sessionId(): Optional = Optional.ofNullable(sessionId)
+ /** Unique session identifier */
+ fun id(): Optional = Optional.ofNullable(id)
+
+ /** Client SDK language */
+ fun xLanguage(): Optional