diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index e1a2442..f7014c3 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.10.1"
+ ".": "0.11.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 917ae03..7345065 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 14
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/warp-bnavetta%2Fwarp-api-a912e2533a6f1fbeee38d4b7739b771ed5711c648a6a7f3d8769b8b2cb4f31fb.yml
-openapi_spec_hash: ef48f8fcc46a51b00893505e9b52c95d
-config_hash: e894152aaebba5a2e65e27efaf2712e2
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/warp-bnavetta%2Fwarp-api-1b6767b852f3064c16752433a1cfd1db8ef38aa442dcb33b136e78fcc094bc51.yml
+openapi_spec_hash: 1fdcb18c88b88e4d257b7748a4896b26
+config_hash: 1888db8b2f33dc16874aea51a90e78f7
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2e6735e..5abdb25 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,20 @@
# Changelog
+## 0.11.0 (2026-04-01)
+
+Full Changelog: [v0.10.1...v0.11.0](https://github.com/warpdotdev/oz-sdk-python/compare/v0.10.1...v0.11.0)
+
+### Features
+
+* Add VMIdleTimeoutMinutes param to API ([31b5a63](https://github.com/warpdotdev/oz-sdk-python/commit/31b5a63204a2baca1a41f0b4124408d2d2b7e6a4))
+* Address Stainless diagnostics ([58d59e3](https://github.com/warpdotdev/oz-sdk-python/commit/58d59e3593d27ad4bb17e340c384d9709f650806))
+* **api:** api update ([9fe02aa](https://github.com/warpdotdev/oz-sdk-python/commit/9fe02aad21c71cbab837f7d335e5234e1974bb90))
+* **api:** api update ([7fc79ef](https://github.com/warpdotdev/oz-sdk-python/commit/7fc79efe0897451670cb44cf29c0f99b06ffe821))
+* Create run for every local conversation and add filter ([aac84c5](https://github.com/warpdotdev/oz-sdk-python/commit/aac84c585eb3b0ed5262d683e544960a82eec2a2))
+* Endpoint to upload third-party harness block snapshots ([2f71989](https://github.com/warpdotdev/oz-sdk-python/commit/2f7198972cec56e72b802c7505fb445d10304bf4))
+* **internal:** implement indices array format for query and form serialization ([c518876](https://github.com/warpdotdev/oz-sdk-python/commit/c518876f3da1129d3c351982ed4a06590fd15f2e))
+* Use correct branch for Stainless PRs ([d7b298d](https://github.com/warpdotdev/oz-sdk-python/commit/d7b298d99c5dcb5e02530329fef222de92f3e9d6))
+
## 0.10.1 (2026-03-24)
Full Changelog: [v0.10.0...v0.10.1](https://github.com/warpdotdev/oz-sdk-python/compare/v0.10.0...v0.10.1)
diff --git a/README.md b/README.md
index 9ea4d38..9aba4a6 100644
--- a/README.md
+++ b/README.md
@@ -170,6 +170,69 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ
Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`.
+## Pagination
+
+List methods in the Oz API API are paginated.
+
+This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually:
+
+```python
+from oz_agent_sdk import OzAPI
+
+client = OzAPI()
+
+all_runs = []
+# Automatically fetches more pages as needed.
+for run in client.agent.runs.list():
+ # Do something with run here
+ all_runs.append(run)
+print(all_runs)
+```
+
+Or, asynchronously:
+
+```python
+import asyncio
+from oz_agent_sdk import AsyncOzAPI
+
+client = AsyncOzAPI()
+
+
+async def main() -> None:
+ all_runs = []
+ # Iterate through items across all pages, issuing requests as needed.
+ async for run in client.agent.runs.list():
+ all_runs.append(run)
+ print(all_runs)
+
+
+asyncio.run(main())
+```
+
+Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages:
+
+```python
+first_page = await client.agent.runs.list()
+if first_page.has_next_page():
+ print(f"will fetch next page using these details: {first_page.next_page_info()}")
+ next_page = await first_page.get_next_page()
+ print(f"number of items we just fetched: {len(next_page.runs)}")
+
+# Remove `await` for non-async usage.
+```
+
+Or just work directly with the returned data:
+
+```python
+first_page = await client.agent.runs.list()
+
+print(f"next page cursor: {first_page.page_info.next_cursor}") # => "next page cursor: ..."
+for run in first_page.runs:
+ print(run.run_id)
+
+# Remove `await` for non-async usage.
+```
+
## Nested params
Nested parameters are dictionaries, typed using `TypedDict`, for example:
diff --git a/api.md b/api.md
index 19e78a3..3ef6227 100644
--- a/api.md
+++ b/api.md
@@ -6,9 +6,11 @@ Types:
from oz_agent_sdk.types import (
AgentSkill,
AmbientAgentConfig,
+ AwsProviderConfig,
CloudEnvironmentConfig,
Error,
ErrorCode,
+ GcpProviderConfig,
McpServerConfig,
Scope,
UserProfile,
@@ -22,7 +24,7 @@ Methods:
- client.agent.list(\*\*params) -> AgentListResponse
- client.agent.get_artifact(artifact_uid) -> AgentGetArtifactResponse
-- client.agent.run(\*\*params) -> AgentRunResponse
+- client.agent.run(\*\*params) -> AgentRunResponse
## Runs
@@ -34,7 +36,6 @@ from oz_agent_sdk.types.agent import (
RunItem,
RunSourceType,
RunState,
- RunListResponse,
RunCancelResponse,
)
```
@@ -42,7 +43,7 @@ from oz_agent_sdk.types.agent import (
Methods:
- client.agent.runs.retrieve(run_id) -> RunItem
-- client.agent.runs.list(\*\*params) -> RunListResponse
+- client.agent.runs.list(\*\*params) -> SyncRunsCursorPage[RunItem]
- client.agent.runs.cancel(run_id) -> str
## Schedules
diff --git a/pyproject.toml b/pyproject.toml
index e8c1daf..01da747 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "oz-agent-sdk"
-version = "0.10.1"
+version = "0.11.0"
description = "The official Python library for the oz-api API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/oz_agent_sdk/_base_client.py b/src/oz_agent_sdk/_base_client.py
index d1510d4..fc8cfaf 100644
--- a/src/oz_agent_sdk/_base_client.py
+++ b/src/oz_agent_sdk/_base_client.py
@@ -63,7 +63,7 @@
)
from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping
from ._compat import PYDANTIC_V1, model_copy, model_dump
-from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type
+from ._models import GenericModel, SecurityOptions, FinalRequestOptions, validate_type, construct_type
from ._response import (
APIResponse,
BaseAPIResponse,
@@ -432,9 +432,27 @@ def _make_status_error(
) -> _exceptions.APIStatusError:
raise NotImplementedError()
+ def _auth_headers(
+ self,
+ security: SecurityOptions, # noqa: ARG002
+ ) -> dict[str, str]:
+ return {}
+
+ def _auth_query(
+ self,
+ security: SecurityOptions, # noqa: ARG002
+ ) -> dict[str, str]:
+ return {}
+
+ def _custom_auth(
+ self,
+ security: SecurityOptions, # noqa: ARG002
+ ) -> httpx.Auth | None:
+ return None
+
def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx.Headers:
custom_headers = options.headers or {}
- headers_dict = _merge_mappings(self.default_headers, custom_headers)
+ headers_dict = _merge_mappings({**self._auth_headers(options.security), **self.default_headers}, custom_headers)
self._validate_headers(headers_dict, custom_headers)
# headers are case-insensitive while dictionaries are not.
@@ -506,7 +524,7 @@ def _build_request(
raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`")
headers = self._build_headers(options, retries_taken=retries_taken)
- params = _merge_mappings(self.default_query, options.params)
+ params = _merge_mappings({**self._auth_query(options.security), **self.default_query}, options.params)
content_type = headers.get("Content-Type")
files = options.files
@@ -671,7 +689,6 @@ def default_headers(self) -> dict[str, str | Omit]:
"Content-Type": "application/json",
"User-Agent": self.user_agent,
**self.platform_headers(),
- **self.auth_headers,
**self._custom_headers,
}
@@ -990,8 +1007,9 @@ def request(
self._prepare_request(request)
kwargs: HttpxSendArgs = {}
- if self.custom_auth is not None:
- kwargs["auth"] = self.custom_auth
+ custom_auth = self._custom_auth(options.security)
+ if custom_auth is not None:
+ kwargs["auth"] = custom_auth
if options.follow_redirects is not None:
kwargs["follow_redirects"] = options.follow_redirects
@@ -1952,6 +1970,7 @@ def make_request_options(
idempotency_key: str | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
post_parser: PostParser | NotGiven = not_given,
+ security: SecurityOptions | None = None,
) -> RequestOptions:
"""Create a dict of type RequestOptions without keys of NotGiven values."""
options: RequestOptions = {}
@@ -1977,6 +1996,9 @@ def make_request_options(
# internal
options["post_parser"] = post_parser # type: ignore
+ if security is not None:
+ options["security"] = security
+
return options
diff --git a/src/oz_agent_sdk/_client.py b/src/oz_agent_sdk/_client.py
index 27973c0..81603d2 100644
--- a/src/oz_agent_sdk/_client.py
+++ b/src/oz_agent_sdk/_client.py
@@ -21,6 +21,7 @@
)
from ._utils import is_given, get_async_library
from ._compat import cached_property
+from ._models import SecurityOptions
from ._version import __version__
from ._streaming import Stream as Stream, AsyncStream as AsyncStream
from ._exceptions import OzAPIError, APIStatusError
@@ -112,9 +113,14 @@ def with_streaming_response(self) -> OzAPIWithStreamedResponse:
def qs(self) -> Querystring:
return Querystring(array_format="repeat")
- @property
@override
- def auth_headers(self) -> dict[str, str]:
+ def _auth_headers(self, security: SecurityOptions) -> dict[str, str]:
+ return {
+ **(self._bearer_auth if security.get("bearer_auth", False) else {}),
+ }
+
+ @property
+ def _bearer_auth(self) -> dict[str, str]:
api_key = self.api_key
return {"Authorization": f"Bearer {api_key}"}
@@ -287,9 +293,14 @@ def with_streaming_response(self) -> AsyncOzAPIWithStreamedResponse:
def qs(self) -> Querystring:
return Querystring(array_format="repeat")
- @property
@override
- def auth_headers(self) -> dict[str, str]:
+ def _auth_headers(self, security: SecurityOptions) -> dict[str, str]:
+ return {
+ **(self._bearer_auth if security.get("bearer_auth", False) else {}),
+ }
+
+ @property
+ def _bearer_auth(self) -> dict[str, str]:
api_key = self.api_key
return {"Authorization": f"Bearer {api_key}"}
diff --git a/src/oz_agent_sdk/_models.py b/src/oz_agent_sdk/_models.py
index 29070e0..e22dd2a 100644
--- a/src/oz_agent_sdk/_models.py
+++ b/src/oz_agent_sdk/_models.py
@@ -791,6 +791,10 @@ def _create_pydantic_model(type_: _T) -> Type[RootModel[_T]]:
return RootModel[type_] # type: ignore
+class SecurityOptions(TypedDict, total=False):
+ bearer_auth: bool
+
+
class FinalRequestOptionsInput(TypedDict, total=False):
method: Required[str]
url: Required[str]
@@ -804,6 +808,7 @@ class FinalRequestOptionsInput(TypedDict, total=False):
json_data: Body
extra_json: AnyMapping
follow_redirects: bool
+ security: SecurityOptions
@final
@@ -818,6 +823,7 @@ class FinalRequestOptions(pydantic.BaseModel):
idempotency_key: Union[str, None] = None
post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven()
follow_redirects: Union[bool, None] = None
+ security: SecurityOptions = {"bearer_auth": True}
content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None
# It should be noted that we cannot use `json` here as that would override
diff --git a/src/oz_agent_sdk/_qs.py b/src/oz_agent_sdk/_qs.py
index ada6fd3..de8c99b 100644
--- a/src/oz_agent_sdk/_qs.py
+++ b/src/oz_agent_sdk/_qs.py
@@ -101,7 +101,10 @@ def _stringify_item(
items.extend(self._stringify_item(key, item, opts))
return items
elif array_format == "indices":
- raise NotImplementedError("The array indices format is not supported yet")
+ items = []
+ for i, item in enumerate(value):
+ items.extend(self._stringify_item(f"{key}[{i}]", item, opts))
+ return items
elif array_format == "brackets":
items = []
key = key + "[]"
diff --git a/src/oz_agent_sdk/_types.py b/src/oz_agent_sdk/_types.py
index eac5f8b..cbb7d7e 100644
--- a/src/oz_agent_sdk/_types.py
+++ b/src/oz_agent_sdk/_types.py
@@ -36,7 +36,7 @@
from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport
if TYPE_CHECKING:
- from ._models import BaseModel
+ from ._models import BaseModel, SecurityOptions
from ._response import APIResponse, AsyncAPIResponse
Transport = BaseTransport
@@ -121,6 +121,7 @@ class RequestOptions(TypedDict, total=False):
extra_json: AnyMapping
idempotency_key: str
follow_redirects: bool
+ security: SecurityOptions
# Sentinel class used until PEP 0661 is accepted
diff --git a/src/oz_agent_sdk/_version.py b/src/oz_agent_sdk/_version.py
index 0f7ea40..113b87a 100644
--- a/src/oz_agent_sdk/_version.py
+++ b/src/oz_agent_sdk/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "oz_agent_sdk"
-__version__ = "0.10.1" # x-release-please-version
+__version__ = "0.11.0" # x-release-please-version
diff --git a/src/oz_agent_sdk/pagination.py b/src/oz_agent_sdk/pagination.py
new file mode 100644
index 0000000..3f925ea
--- /dev/null
+++ b/src/oz_agent_sdk/pagination.py
@@ -0,0 +1,85 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Generic, TypeVar, Optional
+from typing_extensions import override
+
+from ._models import BaseModel
+from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage
+
+__all__ = ["RunsCursorPagePageInfo", "SyncRunsCursorPage", "AsyncRunsCursorPage"]
+
+_T = TypeVar("_T")
+
+
+class RunsCursorPagePageInfo(BaseModel):
+ has_next_page: Optional[bool] = None
+
+ next_cursor: Optional[str] = None
+
+
+class SyncRunsCursorPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]):
+ runs: List[_T]
+ page_info: Optional[RunsCursorPagePageInfo] = None
+
+ @override
+ def _get_page_items(self) -> List[_T]:
+ runs = self.runs
+ if not runs:
+ return []
+ return runs
+
+ @override
+ def has_next_page(self) -> bool:
+ has_next_page = None
+ if self.page_info is not None:
+ if self.page_info.has_next_page is not None:
+ has_next_page = self.page_info.has_next_page
+ if has_next_page is not None and has_next_page is False:
+ return False
+
+ return super().has_next_page()
+
+ @override
+ def next_page_info(self) -> Optional[PageInfo]:
+ next_cursor = None
+ if self.page_info is not None:
+ if self.page_info.next_cursor is not None:
+ next_cursor = self.page_info.next_cursor
+ if not next_cursor:
+ return None
+
+ return PageInfo(params={"cursor": next_cursor})
+
+
+class AsyncRunsCursorPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]):
+ runs: List[_T]
+ page_info: Optional[RunsCursorPagePageInfo] = None
+
+ @override
+ def _get_page_items(self) -> List[_T]:
+ runs = self.runs
+ if not runs:
+ return []
+ return runs
+
+ @override
+ def has_next_page(self) -> bool:
+ has_next_page = None
+ if self.page_info is not None:
+ if self.page_info.has_next_page is not None:
+ has_next_page = self.page_info.has_next_page
+ if has_next_page is not None and has_next_page is False:
+ return False
+
+ return super().has_next_page()
+
+ @override
+ def next_page_info(self) -> Optional[PageInfo]:
+ next_cursor = None
+ if self.page_info is not None:
+ if self.page_info.next_cursor is not None:
+ next_cursor = self.page_info.next_cursor
+ if not next_cursor:
+ return None
+
+ return PageInfo(params={"cursor": next_cursor})
diff --git a/src/oz_agent_sdk/resources/agent/agent.py b/src/oz_agent_sdk/resources/agent/agent.py
index 4adaca9..c70be37 100644
--- a/src/oz_agent_sdk/resources/agent/agent.py
+++ b/src/oz_agent_sdk/resources/agent/agent.py
@@ -203,10 +203,10 @@ def run(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> AgentRunResponse:
- """Spawn a cloud agent with a prompt and optional configuration.
+ """Alias for POST /agent/run.
- The agent will be
- queued for execution and assigned a unique run ID.
+ This is the preferred endpoint for creating new agent
+ runs. Behavior is identical to POST /agent/run.
Args:
attachments: Optional file attachments to include with the prompt (max 5). Attachments are
@@ -244,7 +244,7 @@ def run(
timeout: Override the client-level default timeout for this request, in seconds
"""
return self._post(
- "/agent/run",
+ "/agent/runs",
body=maybe_transform(
{
"attachments": attachments,
@@ -417,10 +417,10 @@ async def run(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> AgentRunResponse:
- """Spawn a cloud agent with a prompt and optional configuration.
+ """Alias for POST /agent/run.
- The agent will be
- queued for execution and assigned a unique run ID.
+ This is the preferred endpoint for creating new agent
+ runs. Behavior is identical to POST /agent/run.
Args:
attachments: Optional file attachments to include with the prompt (max 5). Attachments are
@@ -458,7 +458,7 @@ async def run(
timeout: Override the client-level default timeout for this request, in seconds
"""
return await self._post(
- "/agent/run",
+ "/agent/runs",
body=await async_maybe_transform(
{
"attachments": attachments,
diff --git a/src/oz_agent_sdk/resources/agent/runs.py b/src/oz_agent_sdk/resources/agent/runs.py
index 440bdcf..e13feec 100644
--- a/src/oz_agent_sdk/resources/agent/runs.py
+++ b/src/oz_agent_sdk/resources/agent/runs.py
@@ -9,7 +9,7 @@
import httpx
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from ..._utils import path_template, maybe_transform, async_maybe_transform
+from ..._utils import path_template, maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -18,12 +18,12 @@
async_to_raw_response_wrapper,
async_to_streamed_response_wrapper,
)
+from ...pagination import SyncRunsCursorPage, AsyncRunsCursorPage
from ...types.agent import RunSourceType, run_list_params
-from ..._base_client import make_request_options
+from ..._base_client import AsyncPaginator, make_request_options
from ...types.agent.run_item import RunItem
from ...types.agent.run_state import RunState
from ...types.agent.run_source_type import RunSourceType
-from ...types.agent.run_list_response import RunListResponse
__all__ = ["RunsResource", "AsyncRunsResource"]
@@ -93,6 +93,7 @@ def list(
creator: str | Omit = omit,
cursor: str | Omit = omit,
environment_id: str | Omit = omit,
+ execution_location: Literal["LOCAL", "REMOTE"] | Omit = omit,
limit: int | Omit = omit,
model_id: str | Omit = omit,
name: str | Omit = omit,
@@ -111,14 +112,14 @@ def list(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> RunListResponse:
+ ) -> SyncRunsCursorPage[RunItem]:
"""Retrieve a paginated list of agent runs with optional filtering.
Results default
to `sort_by=updated_at` and `sort_order=desc`.
Args:
- artifact_type: Filter runs by artifact type (PLAN or PULL_REQUEST)
+ artifact_type: Filter runs by artifact type
created_after: Filter runs created after this timestamp (RFC3339 format)
@@ -130,6 +131,8 @@ def list(
environment_id: Filter runs by environment ID
+ execution_location: Filter by where the run executed
+
limit: Maximum number of runs to return
model_id: Filter by model ID
@@ -169,8 +172,9 @@ def list(
timeout: Override the client-level default timeout for this request, in seconds
"""
- return self._get(
+ return self._get_api_list(
"/agent/runs",
+ page=SyncRunsCursorPage[RunItem],
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -184,6 +188,7 @@ def list(
"creator": creator,
"cursor": cursor,
"environment_id": environment_id,
+ "execution_location": execution_location,
"limit": limit,
"model_id": model_id,
"name": name,
@@ -200,7 +205,7 @@ def list(
run_list_params.RunListParams,
),
),
- cast_to=RunListResponse,
+ model=RunItem,
)
def cancel(
@@ -299,7 +304,7 @@ async def retrieve(
cast_to=RunItem,
)
- async def list(
+ def list(
self,
*,
artifact_type: Literal["PLAN", "PULL_REQUEST", "SCREENSHOT"] | Omit = omit,
@@ -308,6 +313,7 @@ async def list(
creator: str | Omit = omit,
cursor: str | Omit = omit,
environment_id: str | Omit = omit,
+ execution_location: Literal["LOCAL", "REMOTE"] | Omit = omit,
limit: int | Omit = omit,
model_id: str | Omit = omit,
name: str | Omit = omit,
@@ -326,14 +332,14 @@ async def list(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> RunListResponse:
+ ) -> AsyncPaginator[RunItem, AsyncRunsCursorPage[RunItem]]:
"""Retrieve a paginated list of agent runs with optional filtering.
Results default
to `sort_by=updated_at` and `sort_order=desc`.
Args:
- artifact_type: Filter runs by artifact type (PLAN or PULL_REQUEST)
+ artifact_type: Filter runs by artifact type
created_after: Filter runs created after this timestamp (RFC3339 format)
@@ -345,6 +351,8 @@ async def list(
environment_id: Filter runs by environment ID
+ execution_location: Filter by where the run executed
+
limit: Maximum number of runs to return
model_id: Filter by model ID
@@ -384,14 +392,15 @@ async def list(
timeout: Override the client-level default timeout for this request, in seconds
"""
- return await self._get(
+ return self._get_api_list(
"/agent/runs",
+ page=AsyncRunsCursorPage[RunItem],
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
- query=await async_maybe_transform(
+ query=maybe_transform(
{
"artifact_type": artifact_type,
"created_after": created_after,
@@ -399,6 +408,7 @@ async def list(
"creator": creator,
"cursor": cursor,
"environment_id": environment_id,
+ "execution_location": execution_location,
"limit": limit,
"model_id": model_id,
"name": name,
@@ -415,7 +425,7 @@ async def list(
run_list_params.RunListParams,
),
),
- cast_to=RunListResponse,
+ model=RunItem,
)
async def cancel(
diff --git a/src/oz_agent_sdk/resources/agent/schedules.py b/src/oz_agent_sdk/resources/agent/schedules.py
index a4b5592..f9fbac0 100644
--- a/src/oz_agent_sdk/resources/agent/schedules.py
+++ b/src/oz_agent_sdk/resources/agent/schedules.py
@@ -275,8 +275,7 @@ def pause(
) -> ScheduledAgentItem:
"""Pause a scheduled agent.
- The agent will not run until resumed. This sets the
- enabled flag to false.
+ The agent will not run until resumed.
Args:
extra_headers: Send extra headers
@@ -311,7 +310,7 @@ def resume(
"""Resume a paused scheduled agent.
The agent will start running according to its
- cron schedule. This sets the enabled flag to true.
+ cron schedule.
Args:
extra_headers: Send extra headers
@@ -584,8 +583,7 @@ async def pause(
) -> ScheduledAgentItem:
"""Pause a scheduled agent.
- The agent will not run until resumed. This sets the
- enabled flag to false.
+ The agent will not run until resumed.
Args:
extra_headers: Send extra headers
@@ -620,7 +618,7 @@ async def resume(
"""Resume a paused scheduled agent.
The agent will start running according to its
- cron schedule. This sets the enabled flag to true.
+ cron schedule.
Args:
extra_headers: Send extra headers
diff --git a/src/oz_agent_sdk/types/__init__.py b/src/oz_agent_sdk/types/__init__.py
index f4d2daa..4c520a7 100644
--- a/src/oz_agent_sdk/types/__init__.py
+++ b/src/oz_agent_sdk/types/__init__.py
@@ -11,6 +11,8 @@
from .mcp_server_config import McpServerConfig as McpServerConfig
from .agent_run_response import AgentRunResponse as AgentRunResponse
from .agent_list_response import AgentListResponse as AgentListResponse
+from .aws_provider_config import AwsProviderConfig as AwsProviderConfig
+from .gcp_provider_config import GcpProviderConfig as GcpProviderConfig
from .ambient_agent_config import AmbientAgentConfig as AmbientAgentConfig
from .mcp_server_config_param import McpServerConfigParam as McpServerConfigParam
from .cloud_environment_config import CloudEnvironmentConfig as CloudEnvironmentConfig
diff --git a/src/oz_agent_sdk/types/agent/__init__.py b/src/oz_agent_sdk/types/agent/__init__.py
index 3484850..1176974 100644
--- a/src/oz_agent_sdk/types/agent/__init__.py
+++ b/src/oz_agent_sdk/types/agent/__init__.py
@@ -7,7 +7,6 @@
from .artifact_item import ArtifactItem as ArtifactItem
from .run_list_params import RunListParams as RunListParams
from .run_source_type import RunSourceType as RunSourceType
-from .run_list_response import RunListResponse as RunListResponse
from .run_cancel_response import RunCancelResponse as RunCancelResponse
from .scheduled_agent_item import ScheduledAgentItem as ScheduledAgentItem
from .schedule_create_params import ScheduleCreateParams as ScheduleCreateParams
diff --git a/src/oz_agent_sdk/types/agent/run_item.py b/src/oz_agent_sdk/types/agent/run_item.py
index 014ab6b..0995dd7 100644
--- a/src/oz_agent_sdk/types/agent/run_item.py
+++ b/src/oz_agent_sdk/types/agent/run_item.py
@@ -2,6 +2,7 @@
from typing import List, Optional
from datetime import datetime
+from typing_extensions import Literal
from ..scope import Scope
from ..._models import BaseModel
@@ -160,6 +161,13 @@ class RunItem(BaseModel):
creator: Optional[UserProfile] = None
+ execution_location: Optional[Literal["LOCAL", "REMOTE"]] = None
+ """Where the run executed:
+
+ - LOCAL: Executed in the user's local Oz environment
+ - REMOTE: Executed by a remote/cloud worker
+ """
+
is_sandbox_running: Optional[bool] = None
"""Whether the sandbox environment is currently running"""
diff --git a/src/oz_agent_sdk/types/agent/run_list_params.py b/src/oz_agent_sdk/types/agent/run_list_params.py
index cb662bc..a1dc2af 100644
--- a/src/oz_agent_sdk/types/agent/run_list_params.py
+++ b/src/oz_agent_sdk/types/agent/run_list_params.py
@@ -15,7 +15,7 @@
class RunListParams(TypedDict, total=False):
artifact_type: Literal["PLAN", "PULL_REQUEST", "SCREENSHOT"]
- """Filter runs by artifact type (PLAN or PULL_REQUEST)"""
+ """Filter runs by artifact type"""
created_after: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")]
"""Filter runs created after this timestamp (RFC3339 format)"""
@@ -32,6 +32,9 @@ class RunListParams(TypedDict, total=False):
environment_id: str
"""Filter runs by environment ID"""
+ execution_location: Literal["LOCAL", "REMOTE"]
+ """Filter by where the run executed"""
+
limit: int
"""Maximum number of runs to return"""
diff --git a/src/oz_agent_sdk/types/agent/run_list_response.py b/src/oz_agent_sdk/types/agent/run_list_response.py
deleted file mode 100644
index db1beb9..0000000
--- a/src/oz_agent_sdk/types/agent/run_list_response.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import List, Optional
-
-from .run_item import RunItem
-from ..._models import BaseModel
-
-__all__ = ["RunListResponse", "PageInfo"]
-
-
-class PageInfo(BaseModel):
- has_next_page: bool
- """Whether there are more results available"""
-
- next_cursor: Optional[str] = None
- """Opaque cursor for fetching the next page"""
-
-
-class RunListResponse(BaseModel):
- page_info: PageInfo
-
- runs: List[RunItem]
diff --git a/src/oz_agent_sdk/types/ambient_agent_config.py b/src/oz_agent_sdk/types/ambient_agent_config.py
index 67667c8..b667e62 100644
--- a/src/oz_agent_sdk/types/ambient_agent_config.py
+++ b/src/oz_agent_sdk/types/ambient_agent_config.py
@@ -25,6 +25,19 @@ class AmbientAgentConfig(BaseModel):
environment_id: Optional[str] = None
"""UID of the environment to run the agent in"""
+ harness: Optional[str] = None
+ """
+ Agent harness to use for the agent run. Default (empty) uses Warp's built-in Oz
+ harness.
+ """
+
+ idle_timeout_minutes: Optional[int] = None
+ """
+ Number of minutes to keep the agent environment alive after task completion. If
+ not set, defaults to 10 minutes. Maximum allowed value is min(60,
+ floor(max_instance_runtime_seconds / 60) for your billing tier).
+ """
+
mcp_servers: Optional[Dict[str, McpServerConfig]] = None
"""Map of MCP server configurations by name"""
diff --git a/src/oz_agent_sdk/types/ambient_agent_config_param.py b/src/oz_agent_sdk/types/ambient_agent_config_param.py
index 1cd17fe..4f04d52 100644
--- a/src/oz_agent_sdk/types/ambient_agent_config_param.py
+++ b/src/oz_agent_sdk/types/ambient_agent_config_param.py
@@ -25,6 +25,19 @@ class AmbientAgentConfigParam(TypedDict, total=False):
environment_id: str
"""UID of the environment to run the agent in"""
+ harness: str
+ """
+ Agent harness to use for the agent run. Default (empty) uses Warp's built-in Oz
+ harness.
+ """
+
+ idle_timeout_minutes: int
+ """
+ Number of minutes to keep the agent environment alive after task completion. If
+ not set, defaults to 10 minutes. Maximum allowed value is min(60,
+ floor(max_instance_runtime_seconds / 60) for your billing tier).
+ """
+
mcp_servers: Dict[str, McpServerConfigParam]
"""Map of MCP server configurations by name"""
diff --git a/src/oz_agent_sdk/types/aws_provider_config.py b/src/oz_agent_sdk/types/aws_provider_config.py
new file mode 100644
index 0000000..61020d8
--- /dev/null
+++ b/src/oz_agent_sdk/types/aws_provider_config.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .._models import BaseModel
+
+__all__ = ["AwsProviderConfig"]
+
+
+class AwsProviderConfig(BaseModel):
+ """AWS IAM role assumption settings"""
+
+ role_arn: str
+ """AWS IAM role ARN to assume"""
diff --git a/src/oz_agent_sdk/types/cloud_environment_config.py b/src/oz_agent_sdk/types/cloud_environment_config.py
index 30ecd79..15ef158 100644
--- a/src/oz_agent_sdk/types/cloud_environment_config.py
+++ b/src/oz_agent_sdk/types/cloud_environment_config.py
@@ -3,8 +3,10 @@
from typing import List, Optional
from .._models import BaseModel
+from .aws_provider_config import AwsProviderConfig
+from .gcp_provider_config import GcpProviderConfig
-__all__ = ["CloudEnvironmentConfig", "GitHubRepo", "Providers", "ProvidersAws", "ProvidersGcp"]
+__all__ = ["CloudEnvironmentConfig", "GitHubRepo", "Providers"]
class GitHubRepo(BaseModel):
@@ -15,33 +17,13 @@ class GitHubRepo(BaseModel):
"""GitHub repository name"""
-class ProvidersAws(BaseModel):
- """AWS IAM role assumption settings"""
-
- role_arn: str
- """AWS IAM role ARN to assume"""
-
-
-class ProvidersGcp(BaseModel):
- """GCP Workload Identity Federation settings"""
-
- project_number: str
- """GCP project number"""
-
- workload_identity_federation_pool_id: str
- """Workload Identity Federation pool ID"""
-
- workload_identity_federation_provider_id: str
- """Workload Identity Federation provider ID"""
-
-
class Providers(BaseModel):
"""Optional cloud provider configurations for automatic auth"""
- aws: Optional[ProvidersAws] = None
+ aws: Optional[AwsProviderConfig] = None
"""AWS IAM role assumption settings"""
- gcp: Optional[ProvidersGcp] = None
+ gcp: Optional[GcpProviderConfig] = None
"""GCP Workload Identity Federation settings"""
diff --git a/src/oz_agent_sdk/types/gcp_provider_config.py b/src/oz_agent_sdk/types/gcp_provider_config.py
new file mode 100644
index 0000000..168ab6a
--- /dev/null
+++ b/src/oz_agent_sdk/types/gcp_provider_config.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .._models import BaseModel
+
+__all__ = ["GcpProviderConfig"]
+
+
+class GcpProviderConfig(BaseModel):
+ """GCP Workload Identity Federation settings"""
+
+ project_number: str
+ """GCP project number"""
+
+ workload_identity_federation_pool_id: str
+ """Workload Identity Federation pool ID"""
+
+ workload_identity_federation_provider_id: str
+ """Workload Identity Federation provider ID"""
diff --git a/tests/api_resources/agent/test_runs.py b/tests/api_resources/agent/test_runs.py
index 480564c..bf257f8 100644
--- a/tests/api_resources/agent/test_runs.py
+++ b/tests/api_resources/agent/test_runs.py
@@ -10,7 +10,8 @@
from tests.utils import assert_matches_type
from oz_agent_sdk import OzAPI, AsyncOzAPI
from oz_agent_sdk._utils import parse_datetime
-from oz_agent_sdk.types.agent import RunItem, RunListResponse
+from oz_agent_sdk.pagination import SyncRunsCursorPage, AsyncRunsCursorPage
+from oz_agent_sdk.types.agent import RunItem
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -64,7 +65,7 @@ def test_path_params_retrieve(self, client: OzAPI) -> None:
@parametrize
def test_method_list(self, client: OzAPI) -> None:
run = client.agent.runs.list()
- assert_matches_type(RunListResponse, run, path=["response"])
+ assert_matches_type(SyncRunsCursorPage[RunItem], run, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -76,6 +77,7 @@ def test_method_list_with_all_params(self, client: OzAPI) -> None:
creator="creator",
cursor="cursor",
environment_id="environment_id",
+ execution_location="LOCAL",
limit=1,
model_id="model_id",
name="name",
@@ -89,7 +91,7 @@ def test_method_list_with_all_params(self, client: OzAPI) -> None:
state=["QUEUED"],
updated_after=parse_datetime("2019-12-27T18:11:19.117Z"),
)
- assert_matches_type(RunListResponse, run, path=["response"])
+ assert_matches_type(SyncRunsCursorPage[RunItem], run, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -99,7 +101,7 @@ def test_raw_response_list(self, client: OzAPI) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
run = response.parse()
- assert_matches_type(RunListResponse, run, path=["response"])
+ assert_matches_type(SyncRunsCursorPage[RunItem], run, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -109,7 +111,7 @@ def test_streaming_response_list(self, client: OzAPI) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
run = response.parse()
- assert_matches_type(RunListResponse, run, path=["response"])
+ assert_matches_type(SyncRunsCursorPage[RunItem], run, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -207,7 +209,7 @@ async def test_path_params_retrieve(self, async_client: AsyncOzAPI) -> None:
@parametrize
async def test_method_list(self, async_client: AsyncOzAPI) -> None:
run = await async_client.agent.runs.list()
- assert_matches_type(RunListResponse, run, path=["response"])
+ assert_matches_type(AsyncRunsCursorPage[RunItem], run, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -219,6 +221,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOzAPI) -> No
creator="creator",
cursor="cursor",
environment_id="environment_id",
+ execution_location="LOCAL",
limit=1,
model_id="model_id",
name="name",
@@ -232,7 +235,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOzAPI) -> No
state=["QUEUED"],
updated_after=parse_datetime("2019-12-27T18:11:19.117Z"),
)
- assert_matches_type(RunListResponse, run, path=["response"])
+ assert_matches_type(AsyncRunsCursorPage[RunItem], run, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -242,7 +245,7 @@ async def test_raw_response_list(self, async_client: AsyncOzAPI) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
run = await response.parse()
- assert_matches_type(RunListResponse, run, path=["response"])
+ assert_matches_type(AsyncRunsCursorPage[RunItem], run, path=["response"])
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
@@ -252,7 +255,7 @@ async def test_streaming_response_list(self, async_client: AsyncOzAPI) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
run = await response.parse()
- assert_matches_type(RunListResponse, run, path=["response"])
+ assert_matches_type(AsyncRunsCursorPage[RunItem], run, path=["response"])
assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/agent/test_schedules.py b/tests/api_resources/agent/test_schedules.py
index e8f8a24..1a1eceb 100644
--- a/tests/api_resources/agent/test_schedules.py
+++ b/tests/api_resources/agent/test_schedules.py
@@ -40,6 +40,8 @@ def test_method_create_with_all_params(self, client: OzAPI) -> None:
"base_prompt": "base_prompt",
"computer_use_enabled": True,
"environment_id": "environment_id",
+ "harness": "harness",
+ "idle_timeout_minutes": 1,
"mcp_servers": {
"foo": {
"args": ["string"],
@@ -154,6 +156,8 @@ def test_method_update_with_all_params(self, client: OzAPI) -> None:
"base_prompt": "base_prompt",
"computer_use_enabled": True,
"environment_id": "environment_id",
+ "harness": "harness",
+ "idle_timeout_minutes": 1,
"mcp_servers": {
"foo": {
"args": ["string"],
@@ -395,6 +399,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncOzAPI) ->
"base_prompt": "base_prompt",
"computer_use_enabled": True,
"environment_id": "environment_id",
+ "harness": "harness",
+ "idle_timeout_minutes": 1,
"mcp_servers": {
"foo": {
"args": ["string"],
@@ -509,6 +515,8 @@ async def test_method_update_with_all_params(self, async_client: AsyncOzAPI) ->
"base_prompt": "base_prompt",
"computer_use_enabled": True,
"environment_id": "environment_id",
+ "harness": "harness",
+ "idle_timeout_minutes": 1,
"mcp_servers": {
"foo": {
"args": ["string"],
diff --git a/tests/api_resources/test_agent.py b/tests/api_resources/test_agent.py
index 00acc5b..82f097a 100644
--- a/tests/api_resources/test_agent.py
+++ b/tests/api_resources/test_agent.py
@@ -123,6 +123,8 @@ def test_method_run_with_all_params(self, client: OzAPI) -> None:
"base_prompt": "base_prompt",
"computer_use_enabled": True,
"environment_id": "environment_id",
+ "harness": "harness",
+ "idle_timeout_minutes": 1,
"mcp_servers": {
"foo": {
"args": ["string"],
@@ -140,7 +142,7 @@ def test_method_run_with_all_params(self, client: OzAPI) -> None:
},
conversation_id="conversation_id",
interactive=True,
- prompt="Fix the bug in auth.go",
+ prompt="prompt",
skill="skill",
team=True,
title="title",
@@ -277,6 +279,8 @@ async def test_method_run_with_all_params(self, async_client: AsyncOzAPI) -> Non
"base_prompt": "base_prompt",
"computer_use_enabled": True,
"environment_id": "environment_id",
+ "harness": "harness",
+ "idle_timeout_minutes": 1,
"mcp_servers": {
"foo": {
"args": ["string"],
@@ -294,7 +298,7 @@ async def test_method_run_with_all_params(self, async_client: AsyncOzAPI) -> Non
},
conversation_id="conversation_id",
interactive=True,
- prompt="Fix the bug in auth.go",
+ prompt="prompt",
skill="skill",
team=True,
title="title",
diff --git a/tests/test_client.py b/tests/test_client.py
index 46b6ed3..9b58870 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -849,7 +849,7 @@ def test_parse_retry_after_header(
@mock.patch("oz_agent_sdk._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: OzAPI) -> None:
- respx_mock.post("/agent/run").mock(side_effect=httpx.TimeoutException("Test timeout error"))
+ respx_mock.post("/agent/runs").mock(side_effect=httpx.TimeoutException("Test timeout error"))
with pytest.raises(APITimeoutError):
client.agent.with_streaming_response.run().__enter__()
@@ -859,7 +859,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien
@mock.patch("oz_agent_sdk._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: OzAPI) -> None:
- respx_mock.post("/agent/run").mock(return_value=httpx.Response(500))
+ respx_mock.post("/agent/runs").mock(return_value=httpx.Response(500))
with pytest.raises(APIStatusError):
client.agent.with_streaming_response.run().__enter__()
@@ -889,7 +889,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)
- respx_mock.post("/agent/run").mock(side_effect=retry_handler)
+ respx_mock.post("/agent/runs").mock(side_effect=retry_handler)
response = client.agent.with_raw_response.run()
@@ -911,7 +911,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)
- respx_mock.post("/agent/run").mock(side_effect=retry_handler)
+ respx_mock.post("/agent/runs").mock(side_effect=retry_handler)
response = client.agent.with_raw_response.run(extra_headers={"x-stainless-retry-count": Omit()})
@@ -934,7 +934,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)
- respx_mock.post("/agent/run").mock(side_effect=retry_handler)
+ respx_mock.post("/agent/runs").mock(side_effect=retry_handler)
response = client.agent.with_raw_response.run(extra_headers={"x-stainless-retry-count": "42"})
@@ -1751,7 +1751,7 @@ async def test_parse_retry_after_header(
@mock.patch("oz_agent_sdk._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncOzAPI) -> None:
- respx_mock.post("/agent/run").mock(side_effect=httpx.TimeoutException("Test timeout error"))
+ respx_mock.post("/agent/runs").mock(side_effect=httpx.TimeoutException("Test timeout error"))
with pytest.raises(APITimeoutError):
await async_client.agent.with_streaming_response.run().__aenter__()
@@ -1761,7 +1761,7 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter,
@mock.patch("oz_agent_sdk._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncOzAPI) -> None:
- respx_mock.post("/agent/run").mock(return_value=httpx.Response(500))
+ respx_mock.post("/agent/runs").mock(return_value=httpx.Response(500))
with pytest.raises(APIStatusError):
await async_client.agent.with_streaming_response.run().__aenter__()
@@ -1791,7 +1791,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)
- respx_mock.post("/agent/run").mock(side_effect=retry_handler)
+ respx_mock.post("/agent/runs").mock(side_effect=retry_handler)
response = await client.agent.with_raw_response.run()
@@ -1815,7 +1815,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)
- respx_mock.post("/agent/run").mock(side_effect=retry_handler)
+ respx_mock.post("/agent/runs").mock(side_effect=retry_handler)
response = await client.agent.with_raw_response.run(extra_headers={"x-stainless-retry-count": Omit()})
@@ -1838,7 +1838,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)
- respx_mock.post("/agent/run").mock(side_effect=retry_handler)
+ respx_mock.post("/agent/runs").mock(side_effect=retry_handler)
response = await client.agent.with_raw_response.run(extra_headers={"x-stainless-retry-count": "42"})