diff --git a/.release-please-manifest.json b/.release-please-manifest.json index bc845f32a..6a197bef5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.16.0" + ".": "1.17.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 791eb0d2e..16a69feca 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 109 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-b0d4f639559e78ee64d536a35464cff1ef1928e92c2a32a0384dc887da662ef3.yml -openapi_spec_hash: a822f02fec32ae89e2bc6a6f95b8845f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-0a2dfe3cc4b6bc1f5505ca129f672ff3ab2669b4b4194dc1abce7423ee7e3a8b.yml +openapi_spec_hash: 88104e9274c509dbefca3775f3dfa581 config_hash: ecb1ff09d29b565ed1452b5e0362e64d diff --git a/CHANGELOG.md b/CHANGELOG.md index 1da26f72e..612d08b97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## 1.17.0 (2026-04-08) + +Full Changelog: [v1.16.0...v1.17.0](https://github.com/runloopai/api-client-python/compare/v1.16.0...v1.17.0) + +### Features + +* Add protocol, launch etc arguments to attach axon grpc, persist them in the db ([#8564](https://github.com/runloopai/api-client-python/issues/8564)) ([76ad33d](https://github.com/runloopai/api-client-python/commit/76ad33d7da11f4324b576f4b03a5eab437eecd2f)) +* stream kernel messages (kmsg) to devbox logs ([#8588](https://github.com/runloopai/api-client-python/issues/8588)) ([8f0ad67](https://github.com/runloopai/api-client-python/commit/8f0ad674d85e44b02aa0d708fe2037dbce6d5fca)) + + +### Bug Fixes + +* **client:** preserve hardcoded query params when merging with user params ([5ce9b5d](https://github.com/runloopai/api-client-python/commit/5ce9b5d4cb882a93b802275622471086530146f0)) + + +### Chores + +* Cleanup remaining junk left over from browser and computer use ([#8553](https://github.com/runloopai/api-client-python/issues/8553)) ([ddccee2](https://github.com/runloopai/api-client-python/commit/ddccee245aaea07df9d7b6a9b307cb9cd77878cb)) + + +### Documentation + +* fix typo, fix repoc/git-based agent mounts ignoring custom mount paths ([#8537](https://github.com/runloopai/api-client-python/issues/8537)) ([638bc7a](https://github.com/runloopai/api-client-python/commit/638bc7ac64b07b686d5ddbb6756ac9fabd42dc32)) + ## 1.16.0 (2026-04-03) Full Changelog: [v1.15.0...v1.16.0](https://github.com/runloopai/api-client-python/compare/v1.15.0...v1.16.0) diff --git a/pyproject.toml b/pyproject.toml index 7c5db1fa5..79180039f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "runloop_api_client" -version = "1.16.0" +version = "1.17.0" description = "The official Python library for the runloop API" dynamic = ["readme"] license = "MIT" diff --git a/src/runloop_api_client/_base_client.py b/src/runloop_api_client/_base_client.py index 945a6d520..7c742dfbc 100644 --- a/src/runloop_api_client/_base_client.py +++ b/src/runloop_api_client/_base_client.py @@ -540,6 +540,10 @@ def _build_request( files = cast(HttpxRequestFiles, ForceMultipartDict()) prepared_url = self._prepare_url(options.url) + # preserve hard-coded query params from the url + if params and prepared_url.query: + params = {**dict(prepared_url.params.items()), **params} + prepared_url = prepared_url.copy_with(raw_path=prepared_url.raw_path.split(b"?", 1)[0]) if "_" in prepared_url.host: # work around https://github.com/encode/httpx/discussions/2880 kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} diff --git a/src/runloop_api_client/_version.py b/src/runloop_api_client/_version.py index 9b52bccf6..e93a9c96c 100644 --- a/src/runloop_api_client/_version.py +++ b/src/runloop_api_client/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "runloop_api_client" -__version__ = "1.16.0" # x-release-please-version +__version__ = "1.17.0" # x-release-please-version diff --git a/src/runloop_api_client/types/blueprint_view.py b/src/runloop_api_client/types/blueprint_view.py index 851b09426..c7f119144 100644 --- a/src/runloop_api_client/types/blueprint_view.py +++ b/src/runloop_api_client/types/blueprint_view.py @@ -78,9 +78,7 @@ class BlueprintView(BaseModel): Services can be explicitly started when creating a Devbox. """ - devbox_capabilities: Optional[List[Literal["unknown", "computer_usage", "browser_usage", "docker_in_docker"]]] = ( - None - ) + devbox_capabilities: Optional[List[Literal["unknown", "docker_in_docker"]]] = None """Capabilities that will be available on Devbox.""" failure_reason: Optional[Literal["out_of_memory", "out_of_disk", "build_failed"]] = None diff --git a/src/runloop_api_client/types/devbox_view.py b/src/runloop_api_client/types/devbox_view.py index 401f1b073..82a8c2595 100644 --- a/src/runloop_api_client/types/devbox_view.py +++ b/src/runloop_api_client/types/devbox_view.py @@ -56,12 +56,8 @@ class DevboxView(BaseModel): id: str """The ID of the Devbox.""" - capabilities: List[Literal["unknown", "computer_usage", "browser_usage", "docker_in_docker"]] - """A list of capability groups this devbox has access to. - - This allows devboxes to be compatible with certain tools sets like computer - usage APIs. - """ + capabilities: List[Literal["unknown", "docker_in_docker"]] + """A list of capability groups this devbox has access to.""" create_time_ms: int """Creation time of the Devbox (Unix timestamp milliseconds).""" diff --git a/src/runloop_api_client/types/devboxes/devbox_logs_list_view.py b/src/runloop_api_client/types/devboxes/devbox_logs_list_view.py index fdcb72023..fc3a9b6e8 100644 --- a/src/runloop_api_client/types/devboxes/devbox_logs_list_view.py +++ b/src/runloop_api_client/types/devboxes/devbox_logs_list_view.py @@ -12,7 +12,7 @@ class Log(BaseModel): level: str """Log line severity level.""" - source: Literal["setup_commands", "entrypoint", "exec", "files", "stats"] + source: Literal["setup_commands", "entrypoint", "exec", "files", "stats", "kmsg"] """The source of the log.""" timestamp_ms: int diff --git a/src/runloop_api_client/types/shared/broker_mount.py b/src/runloop_api_client/types/shared/broker_mount.py index ff48078e9..867fc362f 100644 --- a/src/runloop_api_client/types/shared/broker_mount.py +++ b/src/runloop_api_client/types/shared/broker_mount.py @@ -15,10 +15,16 @@ class BrokerMount(BaseModel): type: Literal["broker_mount"] agent_binary: Optional[str] = None - """Binary to launch the agent (e.g., 'opencode'). Used by ACP broker.""" + """Binary to launch the agent (e.g., 'opencode'). + + Used by protocols that launch a subprocess (acp, claude_json, codex_app_server). + """ launch_args: Optional[List[str]] = None - """Arguments to pass to the agent command (e.g., ['acp']). Used by ACP broker.""" + """Arguments to pass to the agent command (e.g., ['acp']). + + Used by protocols that launch a subprocess (acp, claude_json, codex_app_server). + """ protocol: Optional[Literal["acp", "claude_json", "codex_app_server"]] = None """The protocol used by the broker to deliver events to the agent.""" diff --git a/src/runloop_api_client/types/shared/code_mount_parameters.py b/src/runloop_api_client/types/shared/code_mount_parameters.py index b430f1513..ae2cb5874 100644 --- a/src/runloop_api_client/types/shared/code_mount_parameters.py +++ b/src/runloop_api_client/types/shared/code_mount_parameters.py @@ -11,7 +11,7 @@ class CodeMountParameters(BaseModel): repo_name: str """The name of the repo to mount. - By default, code will be mounted at /home/user/{repo_name}s. + By default, code will be mounted at /home/user/{repo_name}. """ repo_owner: str diff --git a/src/runloop_api_client/types/shared/mount.py b/src/runloop_api_client/types/shared/mount.py index f283abe6b..5aef35821 100644 --- a/src/runloop_api_client/types/shared/mount.py +++ b/src/runloop_api_client/types/shared/mount.py @@ -16,7 +16,7 @@ class CodeMount(BaseModel): repo_name: str """The name of the repo to mount. - By default, code will be mounted at /home/user/{repo_name}s. + By default, code will be mounted at /home/user/{repo_name}. """ repo_owner: str diff --git a/src/runloop_api_client/types/shared_params/broker_mount.py b/src/runloop_api_client/types/shared_params/broker_mount.py index 4a495b051..bb1dfccc6 100644 --- a/src/runloop_api_client/types/shared_params/broker_mount.py +++ b/src/runloop_api_client/types/shared_params/broker_mount.py @@ -17,10 +17,16 @@ class BrokerMount(TypedDict, total=False): type: Required[Literal["broker_mount"]] agent_binary: Optional[str] - """Binary to launch the agent (e.g., 'opencode'). Used by ACP broker.""" + """Binary to launch the agent (e.g., 'opencode'). + + Used by protocols that launch a subprocess (acp, claude_json, codex_app_server). + """ launch_args: Optional[SequenceNotStr[str]] - """Arguments to pass to the agent command (e.g., ['acp']). Used by ACP broker.""" + """Arguments to pass to the agent command (e.g., ['acp']). + + Used by protocols that launch a subprocess (acp, claude_json, codex_app_server). + """ protocol: Optional[Literal["acp", "claude_json", "codex_app_server"]] """The protocol used by the broker to deliver events to the agent.""" diff --git a/src/runloop_api_client/types/shared_params/code_mount_parameters.py b/src/runloop_api_client/types/shared_params/code_mount_parameters.py index 5afa72523..b09c8b3c2 100644 --- a/src/runloop_api_client/types/shared_params/code_mount_parameters.py +++ b/src/runloop_api_client/types/shared_params/code_mount_parameters.py @@ -12,7 +12,7 @@ class CodeMountParameters(TypedDict, total=False): repo_name: Required[str] """The name of the repo to mount. - By default, code will be mounted at /home/user/{repo_name}s. + By default, code will be mounted at /home/user/{repo_name}. """ repo_owner: Required[str] diff --git a/src/runloop_api_client/types/shared_params/mount.py b/src/runloop_api_client/types/shared_params/mount.py index bdc8a142c..4972fb6f4 100644 --- a/src/runloop_api_client/types/shared_params/mount.py +++ b/src/runloop_api_client/types/shared_params/mount.py @@ -16,7 +16,7 @@ class CodeMount(TypedDict, total=False): repo_name: Required[str] """The name of the repo to mount. - By default, code will be mounted at /home/user/{repo_name}s. + By default, code will be mounted at /home/user/{repo_name}. """ repo_owner: Required[str] diff --git a/tests/test_client.py b/tests/test_client.py index 725862258..408c7cedd 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -438,6 +438,30 @@ def test_default_query_option(self) -> None: client.close() + def test_hardcoded_query_params_in_url(self, client: Runloop) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo?beta=true", + params={"limit": "10", "page": "abc"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/files/a%2Fb?beta=true", + params={"limit": "10"}, + ) + ) + assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" + def test_request_extra_json(self, client: Runloop) -> None: request = client._build_request( FinalRequestOptions( @@ -1384,6 +1408,30 @@ async def test_default_query_option(self) -> None: await client.close() + async def test_hardcoded_query_params_in_url(self, async_client: AsyncRunloop) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true"} + + request = async_client._build_request( + FinalRequestOptions( + method="get", + url="/foo?beta=true", + params={"limit": "10", "page": "abc"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} + + request = async_client._build_request( + FinalRequestOptions( + method="get", + url="/files/a%2Fb?beta=true", + params={"limit": "10"}, + ) + ) + assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" + def test_request_extra_json(self, client: Runloop) -> None: request = client._build_request( FinalRequestOptions(