diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index f7014c3..a713055 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.11.0"
+ ".": "0.12.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 51b5464..6fad0eb 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-8f9c749573846b07a55a3131b66456f0a592838c6bfc986ab30948df66cd6f11.yml
-openapi_spec_hash: 59f1ac98ad6cf13b12c59196bcecffd7
-config_hash: 60052b2c1c0862014416821aba875574
+configured_endpoints: 15
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/warp-bnavetta%2Fwarp-api-8e7a3c9ef00ac517caaa193265398850b6925ab898aaf84e83e28c9f023c0ba8.yml
+openapi_spec_hash: ebf53d80dd9066d5ac9401f1493544e3
+config_hash: 253e4b5ca01236d448980a78491c17c5
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 543cbc1..a06d159 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,27 @@
# Changelog
+## 0.12.0 (2026-04-14)
+
+Full Changelog: [v0.11.0...v0.12.0](https://github.com/warpdotdev/oz-sdk-python/compare/v0.11.0...v0.12.0)
+
+### Features
+
+* Add parent_run_id filter to List runs endpoint ([33cf04c](https://github.com/warpdotdev/oz-sdk-python/commit/33cf04c0777d53059f8a3c73b925b468276aba2a))
+* Add system prompt to resolve-prompt for harnesses. ([2b7afab](https://github.com/warpdotdev/oz-sdk-python/commit/2b7afabf4a54764ab162ac32f4dbe7fe4274ba72))
+* Add trigger URL to task source. ([9662dbe](https://github.com/warpdotdev/oz-sdk-python/commit/9662dbe10cc5392271b3e56c35cb8ef95ec80eed))
+* **api:** api update ([ebd2c55](https://github.com/warpdotdev/oz-sdk-python/commit/ebd2c5520128722d88285aea9dd0caf3242d9a31))
+* **api:** api update ([1ffc53b](https://github.com/warpdotdev/oz-sdk-python/commit/1ffc53b4d69eeb8060faac1903f1ba25f7a4c443))
+* **api:** api update ([cf28804](https://github.com/warpdotdev/oz-sdk-python/commit/cf288045d369ed927db45ac5e5ebd994ccebb547))
+* **api:** api update ([a6d82f1](https://github.com/warpdotdev/oz-sdk-python/commit/a6d82f1a71ad8e0ff5f10c43cc81938babc1444e))
+* **api:** api update ([b9872f2](https://github.com/warpdotdev/oz-sdk-python/commit/b9872f2642191ef6e1d4d5d3542a08f9e2784b9e))
+* **api:** api update ([0ef9c8f](https://github.com/warpdotdev/oz-sdk-python/commit/0ef9c8f483269d203a3edc7b5805f48f4263eb2f))
+* Inject auth secrets via ambient agent config. ([6032a0c](https://github.com/warpdotdev/oz-sdk-python/commit/6032a0ccfe2ce669ce1f815ad853b912d4ca575e))
+
+
+### Bug Fixes
+
+* ensure file data are only sent as 1 parameter ([6720ea9](https://github.com/warpdotdev/oz-sdk-python/commit/6720ea9331a945cf2d030e20b8851a223697aab3))
+
## 0.11.0 (2026-04-09)
Full Changelog: [v0.10.1...v0.11.0](https://github.com/warpdotdev/oz-sdk-python/compare/v0.10.1...v0.11.0)
diff --git a/api.md b/api.md
index 3ef6227..065cedb 100644
--- a/api.md
+++ b/api.md
@@ -7,6 +7,7 @@ from oz_agent_sdk.types import (
AgentSkill,
AmbientAgentConfig,
AwsProviderConfig,
+ CloudEnvironment,
CloudEnvironmentConfig,
Error,
ErrorCode,
@@ -16,6 +17,7 @@ from oz_agent_sdk.types import (
UserProfile,
AgentListResponse,
AgentGetArtifactResponse,
+ AgentListEnvironmentsResponse,
AgentRunResponse,
)
```
@@ -24,6 +26,7 @@ Methods:
- client.agent.list(\*\*params) -> AgentListResponse
- client.agent.get_artifact(artifact_uid) -> AgentGetArtifactResponse
+- client.agent.list_environments(\*\*params) -> AgentListEnvironmentsResponse
- client.agent.run(\*\*params) -> AgentRunResponse
## Runs
diff --git a/pyproject.toml b/pyproject.toml
index 01da747..6b28584 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "oz-agent-sdk"
-version = "0.11.0"
+version = "0.12.0"
description = "The official Python library for the oz-api API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/oz_agent_sdk/_utils/_utils.py b/src/oz_agent_sdk/_utils/_utils.py
index eec7f4a..63b8cd6 100644
--- a/src/oz_agent_sdk/_utils/_utils.py
+++ b/src/oz_agent_sdk/_utils/_utils.py
@@ -86,8 +86,9 @@ def _extract_items(
index += 1
if is_dict(obj):
try:
- # We are at the last entry in the path so we must remove the field
- if (len(path)) == index:
+ # Remove the field if there are no more dict keys in the path,
+ # only "" traversal markers or end.
+ if all(p == "" for p in path[index:]):
item = obj.pop(key)
else:
item = obj[key]
diff --git a/src/oz_agent_sdk/_version.py b/src/oz_agent_sdk/_version.py
index 113b87a..b96fedf 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.11.0" # x-release-please-version
+__version__ = "0.12.0" # x-release-please-version
diff --git a/src/oz_agent_sdk/resources/agent/agent.py b/src/oz_agent_sdk/resources/agent/agent.py
index b720375..d8d58ef 100644
--- a/src/oz_agent_sdk/resources/agent/agent.py
+++ b/src/oz_agent_sdk/resources/agent/agent.py
@@ -15,7 +15,7 @@
RunsResourceWithStreamingResponse,
AsyncRunsResourceWithStreamingResponse,
)
-from ...types import agent_run_params, agent_list_params
+from ...types import agent_run_params, agent_list_params, agent_list_environments_params
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
from ..._utils import path_template, maybe_transform, async_maybe_transform
from .sessions import (
@@ -47,6 +47,7 @@
from ...types.agent_list_response import AgentListResponse
from ...types.ambient_agent_config_param import AmbientAgentConfigParam
from ...types.agent_get_artifact_response import AgentGetArtifactResponse
+from ...types.agent_list_environments_response import AgentListEnvironmentsResponse
__all__ = ["AgentResource", "AsyncAgentResource"]
@@ -163,8 +164,9 @@ def get_artifact(
) -> AgentGetArtifactResponse:
"""Retrieve an artifact by its UUID.
- For supported downloadable artifacts, returns
- a time-limited signed download URL.
+ For downloadable file-like artifacts, returns
+ a time-limited signed download URL. For plan artifacts, returns the current plan
+ content inline.
Args:
extra_headers: Send extra headers
@@ -190,6 +192,49 @@ def get_artifact(
),
)
+ def list_environments(
+ self,
+ *,
+ sort_by: Literal["name", "last_updated"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AgentListEnvironmentsResponse:
+ """Retrieve cloud environments accessible to the authenticated principal.
+
+ Returns
+ environments the caller owns, has been granted guest access to, or has accessed
+ via link sharing.
+
+ Args:
+ sort_by: Sort order for the returned environments.
+
+ - `name`: alphabetical by environment name
+ - `last_updated`: most recently updated first (default)
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._get(
+ "/agent/environments",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"sort_by": sort_by}, agent_list_environments_params.AgentListEnvironmentsParams),
+ ),
+ cast_to=AgentListEnvironmentsResponse,
+ )
+
def run(
self,
*,
@@ -392,8 +437,9 @@ async def get_artifact(
) -> AgentGetArtifactResponse:
"""Retrieve an artifact by its UUID.
- For supported downloadable artifacts, returns
- a time-limited signed download URL.
+ For downloadable file-like artifacts, returns
+ a time-limited signed download URL. For plan artifacts, returns the current plan
+ content inline.
Args:
extra_headers: Send extra headers
@@ -419,6 +465,51 @@ async def get_artifact(
),
)
+ async def list_environments(
+ self,
+ *,
+ sort_by: Literal["name", "last_updated"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AgentListEnvironmentsResponse:
+ """Retrieve cloud environments accessible to the authenticated principal.
+
+ Returns
+ environments the caller owns, has been granted guest access to, or has accessed
+ via link sharing.
+
+ Args:
+ sort_by: Sort order for the returned environments.
+
+ - `name`: alphabetical by environment name
+ - `last_updated`: most recently updated first (default)
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._get(
+ "/agent/environments",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"sort_by": sort_by}, agent_list_environments_params.AgentListEnvironmentsParams
+ ),
+ ),
+ cast_to=AgentListEnvironmentsResponse,
+ )
+
async def run(
self,
*,
@@ -519,6 +610,9 @@ def __init__(self, agent: AgentResource) -> None:
self.get_artifact = to_raw_response_wrapper(
agent.get_artifact,
)
+ self.list_environments = to_raw_response_wrapper(
+ agent.list_environments,
+ )
self.run = to_raw_response_wrapper(
agent.run,
)
@@ -549,6 +643,9 @@ def __init__(self, agent: AsyncAgentResource) -> None:
self.get_artifact = async_to_raw_response_wrapper(
agent.get_artifact,
)
+ self.list_environments = async_to_raw_response_wrapper(
+ agent.list_environments,
+ )
self.run = async_to_raw_response_wrapper(
agent.run,
)
@@ -579,6 +676,9 @@ def __init__(self, agent: AgentResource) -> None:
self.get_artifact = to_streamed_response_wrapper(
agent.get_artifact,
)
+ self.list_environments = to_streamed_response_wrapper(
+ agent.list_environments,
+ )
self.run = to_streamed_response_wrapper(
agent.run,
)
@@ -609,6 +709,9 @@ def __init__(self, agent: AsyncAgentResource) -> None:
self.get_artifact = async_to_streamed_response_wrapper(
agent.get_artifact,
)
+ self.list_environments = async_to_streamed_response_wrapper(
+ agent.list_environments,
+ )
self.run = async_to_streamed_response_wrapper(
agent.run,
)
diff --git a/src/oz_agent_sdk/resources/agent/runs.py b/src/oz_agent_sdk/resources/agent/runs.py
index e6e55b5..00c3f49 100644
--- a/src/oz_agent_sdk/resources/agent/runs.py
+++ b/src/oz_agent_sdk/resources/agent/runs.py
@@ -87,6 +87,7 @@ def retrieve(
def list(
self,
*,
+ ancestor_run_id: str | Omit = omit,
artifact_type: Literal["PLAN", "PULL_REQUEST", "SCREENSHOT", "FILE"] | Omit = omit,
created_after: Union[str, datetime] | Omit = omit,
created_before: Union[str, datetime] | Omit = omit,
@@ -119,6 +120,9 @@ def list(
to `sort_by=updated_at` and `sort_order=desc`.
Args:
+ ancestor_run_id: Filter runs by ancestor run ID. The referenced run must exist and be accessible
+ to the caller.
+
artifact_type: Filter runs by artifact type
created_after: Filter runs created after this timestamp (RFC3339 format)
@@ -182,6 +186,7 @@ def list(
timeout=timeout,
query=maybe_transform(
{
+ "ancestor_run_id": ancestor_run_id,
"artifact_type": artifact_type,
"created_after": created_after,
"created_before": created_before,
@@ -307,6 +312,7 @@ async def retrieve(
def list(
self,
*,
+ ancestor_run_id: str | Omit = omit,
artifact_type: Literal["PLAN", "PULL_REQUEST", "SCREENSHOT", "FILE"] | Omit = omit,
created_after: Union[str, datetime] | Omit = omit,
created_before: Union[str, datetime] | Omit = omit,
@@ -339,6 +345,9 @@ def list(
to `sort_by=updated_at` and `sort_order=desc`.
Args:
+ ancestor_run_id: Filter runs by ancestor run ID. The referenced run must exist and be accessible
+ to the caller.
+
artifact_type: Filter runs by artifact type
created_after: Filter runs created after this timestamp (RFC3339 format)
@@ -402,6 +411,7 @@ def list(
timeout=timeout,
query=maybe_transform(
{
+ "ancestor_run_id": ancestor_run_id,
"artifact_type": artifact_type,
"created_after": created_after,
"created_before": created_before,
diff --git a/src/oz_agent_sdk/types/__init__.py b/src/oz_agent_sdk/types/__init__.py
index 4c520a7..e27ca02 100644
--- a/src/oz_agent_sdk/types/__init__.py
+++ b/src/oz_agent_sdk/types/__init__.py
@@ -8,6 +8,7 @@
from .user_profile import UserProfile as UserProfile
from .agent_run_params import AgentRunParams as AgentRunParams
from .agent_list_params import AgentListParams as AgentListParams
+from .cloud_environment import CloudEnvironment as CloudEnvironment
from .mcp_server_config import McpServerConfig as McpServerConfig
from .agent_run_response import AgentRunResponse as AgentRunResponse
from .agent_list_response import AgentListResponse as AgentListResponse
@@ -18,3 +19,5 @@
from .cloud_environment_config import CloudEnvironmentConfig as CloudEnvironmentConfig
from .ambient_agent_config_param import AmbientAgentConfigParam as AmbientAgentConfigParam
from .agent_get_artifact_response import AgentGetArtifactResponse as AgentGetArtifactResponse
+from .agent_list_environments_params import AgentListEnvironmentsParams as AgentListEnvironmentsParams
+from .agent_list_environments_response import AgentListEnvironmentsResponse as AgentListEnvironmentsResponse
diff --git a/src/oz_agent_sdk/types/agent/artifact_item.py b/src/oz_agent_sdk/types/agent/artifact_item.py
index 299379b..437b857 100644
--- a/src/oz_agent_sdk/types/agent/artifact_item.py
+++ b/src/oz_agent_sdk/types/agent/artifact_item.py
@@ -24,12 +24,21 @@ class PlanArtifactData(BaseModel):
document_uid: str
"""Unique identifier for the plan document"""
+ artifact_uid: Optional[str] = None
+ """
+ Unique identifier for the plan artifact, usable with the artifact retrieval
+ endpoint
+ """
+
notebook_uid: Optional[str] = None
"""Unique identifier for the associated notebook"""
title: Optional[str] = None
"""Title of the plan"""
+ url: Optional[str] = None
+ """URL to open the plan in Warp Drive"""
+
class PlanArtifact(BaseModel):
artifact_type: Literal["PLAN"]
diff --git a/src/oz_agent_sdk/types/agent/run_item.py b/src/oz_agent_sdk/types/agent/run_item.py
index d246450..61dd012 100644
--- a/src/oz_agent_sdk/types/agent/run_item.py
+++ b/src/oz_agent_sdk/types/agent/run_item.py
@@ -215,3 +215,6 @@ class RunItem(BaseModel):
For terminal error states, includes structured error code and retryability info
from the platform error catalog.
"""
+
+ trigger_url: Optional[str] = None
+ """URL to the run trigger (e.g. Slack thread, Linear issue, schedule)"""
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 0b4e389..b53a299 100644
--- a/src/oz_agent_sdk/types/agent/run_list_params.py
+++ b/src/oz_agent_sdk/types/agent/run_list_params.py
@@ -14,6 +14,12 @@
class RunListParams(TypedDict, total=False):
+ ancestor_run_id: str
+ """Filter runs by ancestor run ID.
+
+ The referenced run must exist and be accessible to the caller.
+ """
+
artifact_type: Literal["PLAN", "PULL_REQUEST", "SCREENSHOT", "FILE"]
"""Filter runs by artifact type"""
diff --git a/src/oz_agent_sdk/types/agent_get_artifact_response.py b/src/oz_agent_sdk/types/agent_get_artifact_response.py
index 30405bc..7e835c8 100644
--- a/src/oz_agent_sdk/types/agent_get_artifact_response.py
+++ b/src/oz_agent_sdk/types/agent_get_artifact_response.py
@@ -9,6 +9,8 @@
__all__ = [
"AgentGetArtifactResponse",
+ "PlanArtifactResponse",
+ "PlanArtifactResponseData",
"ScreenshotArtifactResponse",
"ScreenshotArtifactResponseData",
"FileArtifactResponse",
@@ -16,6 +18,44 @@
]
+class PlanArtifactResponseData(BaseModel):
+ """Response data for a plan artifact, including current markdown content."""
+
+ content: str
+ """Current markdown content of the plan"""
+
+ content_type: str
+ """MIME type of the returned plan content"""
+
+ document_uid: str
+ """Unique identifier for the plan document"""
+
+ notebook_uid: str
+ """Unique identifier for the associated notebook"""
+
+ title: Optional[str] = None
+ """Current title of the plan"""
+
+ url: Optional[str] = None
+ """URL to open the plan in Warp Drive"""
+
+
+class PlanArtifactResponse(BaseModel):
+ """Response for retrieving a plan artifact."""
+
+ artifact_type: Literal["PLAN"]
+ """Type of the artifact"""
+
+ artifact_uid: str
+ """Unique identifier (UUID) for the artifact"""
+
+ created_at: datetime
+ """Timestamp when the artifact was created (RFC3339)"""
+
+ data: PlanArtifactResponseData
+ """Response data for a plan artifact, including current markdown content."""
+
+
class ScreenshotArtifactResponseData(BaseModel):
"""Response data for a screenshot artifact, including a signed download URL."""
@@ -90,5 +130,6 @@ class FileArtifactResponse(BaseModel):
AgentGetArtifactResponse: TypeAlias = Annotated[
- Union[ScreenshotArtifactResponse, FileArtifactResponse], PropertyInfo(discriminator="artifact_type")
+ Union[PlanArtifactResponse, ScreenshotArtifactResponse, FileArtifactResponse],
+ PropertyInfo(discriminator="artifact_type"),
]
diff --git a/src/oz_agent_sdk/types/agent_list_environments_params.py b/src/oz_agent_sdk/types/agent_list_environments_params.py
new file mode 100644
index 0000000..481c31d
--- /dev/null
+++ b/src/oz_agent_sdk/types/agent_list_environments_params.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["AgentListEnvironmentsParams"]
+
+
+class AgentListEnvironmentsParams(TypedDict, total=False):
+ sort_by: Literal["name", "last_updated"]
+ """Sort order for the returned environments.
+
+ - `name`: alphabetical by environment name
+ - `last_updated`: most recently updated first (default)
+ """
diff --git a/src/oz_agent_sdk/types/agent_list_environments_response.py b/src/oz_agent_sdk/types/agent_list_environments_response.py
new file mode 100644
index 0000000..103845a
--- /dev/null
+++ b/src/oz_agent_sdk/types/agent_list_environments_response.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from .._models import BaseModel
+from .cloud_environment import CloudEnvironment
+
+__all__ = ["AgentListEnvironmentsResponse"]
+
+
+class AgentListEnvironmentsResponse(BaseModel):
+ environments: List[CloudEnvironment]
+ """List of accessible cloud environments"""
diff --git a/src/oz_agent_sdk/types/ambient_agent_config.py b/src/oz_agent_sdk/types/ambient_agent_config.py
index abd16c1..69eaf3a 100644
--- a/src/oz_agent_sdk/types/ambient_agent_config.py
+++ b/src/oz_agent_sdk/types/ambient_agent_config.py
@@ -8,7 +8,7 @@
from .._models import BaseModel
from .mcp_server_config import McpServerConfig
-__all__ = ["AmbientAgentConfig", "Harness"]
+__all__ = ["AmbientAgentConfig", "Harness", "HarnessAuthSecrets"]
class Harness(BaseModel):
@@ -17,15 +17,6 @@ class Harness(BaseModel):
Default (nil/empty) uses Warp's built-in harness.
"""
- auth_secret_name: Optional[str] = None
- """Name of a managed secret to use as the authentication credential for the
- harness.
-
- The secret must exist within the caller's personal or team scope. The
- environment variable injected into the agent is determined by the secret type
- (e.g. ANTHROPIC_API_KEY for anthropic_api_key secrets).
- """
-
type: Optional[Literal["oz", "claude"]] = None
"""The harness type identifier.
@@ -34,6 +25,20 @@ class Harness(BaseModel):
"""
+class HarnessAuthSecrets(BaseModel):
+ """
+ Authentication secrets for third-party harnesses.
+ Only the secret for the harness specified gets injected into the environment.
+ """
+
+ claude_auth_secret_name: Optional[str] = None
+ """
+ Name of a managed secret for Claude Code harness authentication. The secret must
+ exist within the caller's personal or team scope. Only applicable when harness
+ type is "claude".
+ """
+
+
class AmbientAgentConfig(BaseModel):
"""Configuration for a cloud agent run"""
@@ -55,6 +60,12 @@ class AmbientAgentConfig(BaseModel):
uses Warp's built-in harness.
"""
+ harness_auth_secrets: Optional[HarnessAuthSecrets] = None
+ """
+ Authentication secrets for third-party harnesses. Only the secret for the
+ harness specified gets injected into the environment.
+ """
+
idle_timeout_minutes: Optional[int] = None
"""
Number of minutes to keep the agent environment alive after task completion. If
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 24da607..8ea7901 100644
--- a/src/oz_agent_sdk/types/ambient_agent_config_param.py
+++ b/src/oz_agent_sdk/types/ambient_agent_config_param.py
@@ -7,7 +7,7 @@
from .mcp_server_config_param import McpServerConfigParam
-__all__ = ["AmbientAgentConfigParam", "Harness"]
+__all__ = ["AmbientAgentConfigParam", "Harness", "HarnessAuthSecrets"]
class Harness(TypedDict, total=False):
@@ -16,15 +16,6 @@ class Harness(TypedDict, total=False):
Default (nil/empty) uses Warp's built-in harness.
"""
- auth_secret_name: str
- """Name of a managed secret to use as the authentication credential for the
- harness.
-
- The secret must exist within the caller's personal or team scope. The
- environment variable injected into the agent is determined by the secret type
- (e.g. ANTHROPIC_API_KEY for anthropic_api_key secrets).
- """
-
type: Literal["oz", "claude"]
"""The harness type identifier.
@@ -33,6 +24,20 @@ class Harness(TypedDict, total=False):
"""
+class HarnessAuthSecrets(TypedDict, total=False):
+ """
+ Authentication secrets for third-party harnesses.
+ Only the secret for the harness specified gets injected into the environment.
+ """
+
+ claude_auth_secret_name: str
+ """
+ Name of a managed secret for Claude Code harness authentication. The secret must
+ exist within the caller's personal or team scope. Only applicable when harness
+ type is "claude".
+ """
+
+
class AmbientAgentConfigParam(TypedDict, total=False):
"""Configuration for a cloud agent run"""
@@ -54,6 +59,12 @@ class AmbientAgentConfigParam(TypedDict, total=False):
uses Warp's built-in harness.
"""
+ harness_auth_secrets: HarnessAuthSecrets
+ """
+ Authentication secrets for third-party harnesses. Only the secret for the
+ harness specified gets injected into the environment.
+ """
+
idle_timeout_minutes: int
"""
Number of minutes to keep the agent environment alive after task completion. If
diff --git a/src/oz_agent_sdk/types/cloud_environment.py b/src/oz_agent_sdk/types/cloud_environment.py
new file mode 100644
index 0000000..4bde98c
--- /dev/null
+++ b/src/oz_agent_sdk/types/cloud_environment.py
@@ -0,0 +1,74 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+
+from .scope import Scope
+from .._models import BaseModel
+from .user_profile import UserProfile
+from .agent.run_state import RunState
+from .cloud_environment_config import CloudEnvironmentConfig
+
+__all__ = ["CloudEnvironment", "LastTaskCreated"]
+
+
+class LastTaskCreated(BaseModel):
+ """Summary of the most recently created task for an environment"""
+
+ id: str
+ """Unique identifier of the task"""
+
+ created_at: datetime
+ """When the task was created (RFC3339)"""
+
+ state: RunState
+ """Current state of the run:
+
+ - QUEUED: Run is waiting to be picked up
+ - PENDING: Run is being prepared
+ - CLAIMED: Run has been claimed by a worker
+ - INPROGRESS: Run is actively being executed
+ - SUCCEEDED: Run completed successfully
+ - FAILED: Run failed
+ - BLOCKED: Run is blocked (e.g., awaiting user input or approval)
+ - ERROR: Run encountered an error
+ - CANCELLED: Run was cancelled by user
+ """
+
+ title: str
+ """Title of the task"""
+
+ updated_at: datetime
+ """When the task was last updated (RFC3339)"""
+
+ started_at: Optional[datetime] = None
+ """When the task started running (RFC3339), null if not yet started"""
+
+
+class CloudEnvironment(BaseModel):
+ """A cloud environment for running agents"""
+
+ config: CloudEnvironmentConfig
+ """Configuration for a cloud environment used by scheduled agents"""
+
+ last_updated: datetime
+ """Timestamp when the environment was last updated (RFC3339)"""
+
+ setup_failed: bool
+ """True when the most recent task failed during setup before it started running"""
+
+ uid: str
+ """Unique identifier for the environment"""
+
+ creator: Optional[UserProfile] = None
+
+ last_editor: Optional[UserProfile] = None
+
+ last_task_created: Optional[LastTaskCreated] = None
+ """Summary of the most recently created task for an environment"""
+
+ last_task_run_timestamp: Optional[datetime] = None
+ """Timestamp of the most recent task run in this environment (RFC3339)"""
+
+ scope: Optional[Scope] = None
+ """Ownership scope for a resource (team or personal)"""
diff --git a/tests/api_resources/agent/test_runs.py b/tests/api_resources/agent/test_runs.py
index bf257f8..cf95a65 100644
--- a/tests/api_resources/agent/test_runs.py
+++ b/tests/api_resources/agent/test_runs.py
@@ -71,6 +71,7 @@ def test_method_list(self, client: OzAPI) -> None:
@parametrize
def test_method_list_with_all_params(self, client: OzAPI) -> None:
run = client.agent.runs.list(
+ ancestor_run_id="ancestor_run_id",
artifact_type="PLAN",
created_after=parse_datetime("2019-12-27T18:11:19.117Z"),
created_before=parse_datetime("2019-12-27T18:11:19.117Z"),
@@ -215,6 +216,7 @@ async def test_method_list(self, async_client: AsyncOzAPI) -> None:
@parametrize
async def test_method_list_with_all_params(self, async_client: AsyncOzAPI) -> None:
run = await async_client.agent.runs.list(
+ ancestor_run_id="ancestor_run_id",
artifact_type="PLAN",
created_after=parse_datetime("2019-12-27T18:11:19.117Z"),
created_before=parse_datetime("2019-12-27T18:11:19.117Z"),
diff --git a/tests/api_resources/agent/test_schedules.py b/tests/api_resources/agent/test_schedules.py
index 9b22b45..51fbae1 100644
--- a/tests/api_resources/agent/test_schedules.py
+++ b/tests/api_resources/agent/test_schedules.py
@@ -40,10 +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": {
- "auth_secret_name": "auth_secret_name",
- "type": "oz",
- },
+ "harness": {"type": "oz"},
+ "harness_auth_secrets": {"claude_auth_secret_name": "claude_auth_secret_name"},
"idle_timeout_minutes": 1,
"mcp_servers": {
"foo": {
@@ -159,10 +157,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": {
- "auth_secret_name": "auth_secret_name",
- "type": "oz",
- },
+ "harness": {"type": "oz"},
+ "harness_auth_secrets": {"claude_auth_secret_name": "claude_auth_secret_name"},
"idle_timeout_minutes": 1,
"mcp_servers": {
"foo": {
@@ -405,10 +401,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": {
- "auth_secret_name": "auth_secret_name",
- "type": "oz",
- },
+ "harness": {"type": "oz"},
+ "harness_auth_secrets": {"claude_auth_secret_name": "claude_auth_secret_name"},
"idle_timeout_minutes": 1,
"mcp_servers": {
"foo": {
@@ -524,10 +518,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": {
- "auth_secret_name": "auth_secret_name",
- "type": "oz",
- },
+ "harness": {"type": "oz"},
+ "harness_auth_secrets": {"claude_auth_secret_name": "claude_auth_secret_name"},
"idle_timeout_minutes": 1,
"mcp_servers": {
"foo": {
diff --git a/tests/api_resources/test_agent.py b/tests/api_resources/test_agent.py
index 70c07fb..7f628ef 100644
--- a/tests/api_resources/test_agent.py
+++ b/tests/api_resources/test_agent.py
@@ -13,6 +13,7 @@
AgentRunResponse,
AgentListResponse,
AgentGetArtifactResponse,
+ AgentListEnvironmentsResponse,
)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -102,6 +103,42 @@ def test_path_params_get_artifact(self, client: OzAPI) -> None:
"",
)
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_environments(self, client: OzAPI) -> None:
+ agent = client.agent.list_environments()
+ assert_matches_type(AgentListEnvironmentsResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_environments_with_all_params(self, client: OzAPI) -> None:
+ agent = client.agent.list_environments(
+ sort_by="name",
+ )
+ assert_matches_type(AgentListEnvironmentsResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list_environments(self, client: OzAPI) -> None:
+ response = client.agent.with_raw_response.list_environments()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = response.parse()
+ assert_matches_type(AgentListEnvironmentsResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list_environments(self, client: OzAPI) -> None:
+ with client.agent.with_streaming_response.list_environments() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = response.parse()
+ assert_matches_type(AgentListEnvironmentsResponse, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
def test_method_run(self, client: OzAPI) -> None:
@@ -124,10 +161,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": {
- "auth_secret_name": "auth_secret_name",
- "type": "oz",
- },
+ "harness": {"type": "oz"},
+ "harness_auth_secrets": {"claude_auth_secret_name": "claude_auth_secret_name"},
"idle_timeout_minutes": 1,
"mcp_servers": {
"foo": {
@@ -263,6 +298,42 @@ async def test_path_params_get_artifact(self, async_client: AsyncOzAPI) -> None:
"",
)
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_environments(self, async_client: AsyncOzAPI) -> None:
+ agent = await async_client.agent.list_environments()
+ assert_matches_type(AgentListEnvironmentsResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_environments_with_all_params(self, async_client: AsyncOzAPI) -> None:
+ agent = await async_client.agent.list_environments(
+ sort_by="name",
+ )
+ assert_matches_type(AgentListEnvironmentsResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list_environments(self, async_client: AsyncOzAPI) -> None:
+ response = await async_client.agent.with_raw_response.list_environments()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ agent = await response.parse()
+ assert_matches_type(AgentListEnvironmentsResponse, agent, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list_environments(self, async_client: AsyncOzAPI) -> None:
+ async with async_client.agent.with_streaming_response.list_environments() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ agent = await response.parse()
+ assert_matches_type(AgentListEnvironmentsResponse, agent, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
@pytest.mark.skip(reason="Mock server tests are disabled")
@parametrize
async def test_method_run(self, async_client: AsyncOzAPI) -> None:
@@ -285,10 +356,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": {
- "auth_secret_name": "auth_secret_name",
- "type": "oz",
- },
+ "harness": {"type": "oz"},
+ "harness_auth_secrets": {"claude_auth_secret_name": "claude_auth_secret_name"},
"idle_timeout_minutes": 1,
"mcp_servers": {
"foo": {
diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py
index 95b71bd..f014004 100644
--- a/tests/test_extract_files.py
+++ b/tests/test_extract_files.py
@@ -35,6 +35,15 @@ def test_multiple_files() -> None:
assert query == {"documents": [{}, {}]}
+def test_top_level_file_array() -> None:
+ query = {"files": [b"file one", b"file two"], "title": "hello"}
+ assert extract_files(query, paths=[["files", ""]]) == [
+ ("files[]", b"file one"),
+ ("files[]", b"file two"),
+ ]
+ assert query == {"title": "hello"}
+
+
@pytest.mark.parametrize(
"query,paths,expected",
[