From ddccee245aaea07df9d7b6a9b307cb9cd77878cb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 02:28:29 +0000 Subject: [PATCH 1/9] chore: Cleanup remaining junk left over from browser and computer use (#8553) --- .stats.yml | 4 ++-- src/runloop_api_client/types/blueprint_view.py | 4 +--- src/runloop_api_client/types/devbox_view.py | 8 ++------ 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/.stats.yml b/.stats.yml index 791eb0d2e..febdae404 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-278ae77dcf804cd28eda8546ee0bc6ac95f02cdf571e89f257166913b7f4099a.yml +openapi_spec_hash: a29714be5cdaad5ed4f05bd45a5377a1 config_hash: ecb1ff09d29b565ed1452b5e0362e64d 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).""" From 76ad33d7da11f4324b576f4b03a5eab437eecd2f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2026 00:21:43 +0000 Subject: [PATCH 2/9] feat: Add protocol, launch etc arguments to attach axon grpc, persist them in the db (#8564) --- .stats.yml | 4 ++-- src/runloop_api_client/types/shared/broker_mount.py | 10 ++++++++-- .../types/shared_params/broker_mount.py | 10 ++++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/.stats.yml b/.stats.yml index febdae404..0d18a09ef 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-278ae77dcf804cd28eda8546ee0bc6ac95f02cdf571e89f257166913b7f4099a.yml -openapi_spec_hash: a29714be5cdaad5ed4f05bd45a5377a1 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-2b3cbb2a02e1e261f8d057e6ed0f10d109c8a19ddc95b87438dd2dab0f07f894.yml +openapi_spec_hash: a7730acfaa14d18dc3c0258841efc150 config_hash: ecb1ff09d29b565ed1452b5e0362e64d 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_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.""" From 638bc7ac64b07b686d5ddbb6756ac9fabd42dc32 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2026 01:16:53 +0000 Subject: [PATCH 3/9] docs: fix typo, fix repoc/git-based agent mounts ignoring custom mount paths (#8537) --- .stats.yml | 4 ++-- src/runloop_api_client/types/shared/code_mount_parameters.py | 2 +- src/runloop_api_client/types/shared/mount.py | 2 +- .../types/shared_params/code_mount_parameters.py | 2 +- src/runloop_api_client/types/shared_params/mount.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0d18a09ef..c3b34d559 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-2b3cbb2a02e1e261f8d057e6ed0f10d109c8a19ddc95b87438dd2dab0f07f894.yml -openapi_spec_hash: a7730acfaa14d18dc3c0258841efc150 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-495200aa8fb3721ab54c9369b6e92098b2eaf253089e625c93d701a235a10754.yml +openapi_spec_hash: 8933118cf53fdc6cafb6c43041f07b53 config_hash: ecb1ff09d29b565ed1452b5e0362e64d 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/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] From 8f0ad674d85e44b02aa0d708fe2037dbce6d5fca Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2026 21:29:20 +0000 Subject: [PATCH 4/9] feat: stream kernel messages (kmsg) to devbox logs (#8588) --- .stats.yml | 4 ++-- .../types/devboxes/devbox_logs_list_view.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index c3b34d559..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-495200aa8fb3721ab54c9369b6e92098b2eaf253089e625c93d701a235a10754.yml -openapi_spec_hash: 8933118cf53fdc6cafb6c43041f07b53 +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/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 From 5ce9b5d4cb882a93b802275622471086530146f0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 02:24:32 +0000 Subject: [PATCH 5/9] fix(client): preserve hardcoded query params when merging with user params --- src/runloop_api_client/_base_client.py | 4 +++ tests/test_client.py | 48 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) 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/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( From 118162e3b64d531c5e2b66f66d47724b01fefb1c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 23:35:01 +0000 Subject: [PATCH 6/9] fix(broker): remove codex in favor of codex via acp (#8602) --- .stats.yml | 4 ++-- src/runloop_api_client/types/shared/broker_mount.py | 6 +++--- src/runloop_api_client/types/shared_params/broker_mount.py | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.stats.yml b/.stats.yml index 16a69feca..c3f1be3bd 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-0a2dfe3cc4b6bc1f5505ca129f672ff3ab2669b4b4194dc1abce7423ee7e3a8b.yml -openapi_spec_hash: 88104e9274c509dbefca3775f3dfa581 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-07efaf8d4a6487d9682186239706cf0da903131db3ca8ce52fb09a8103f4a091.yml +openapi_spec_hash: b29e96f8962659b2977a0bb178cfe404 config_hash: ecb1ff09d29b565ed1452b5e0362e64d diff --git a/src/runloop_api_client/types/shared/broker_mount.py b/src/runloop_api_client/types/shared/broker_mount.py index 867fc362f..6614a69ba 100644 --- a/src/runloop_api_client/types/shared/broker_mount.py +++ b/src/runloop_api_client/types/shared/broker_mount.py @@ -17,14 +17,14 @@ class BrokerMount(BaseModel): agent_binary: Optional[str] = None """Binary to launch the agent (e.g., 'opencode'). - Used by protocols that launch a subprocess (acp, claude_json, codex_app_server). + Used by protocols that launch a subprocess (acp, claude_json). """ launch_args: Optional[List[str]] = None """Arguments to pass to the agent command (e.g., ['acp']). - Used by protocols that launch a subprocess (acp, claude_json, codex_app_server). + Used by protocols that launch a subprocess (acp, claude_json). """ - protocol: Optional[Literal["acp", "claude_json", "codex_app_server"]] = None + protocol: Optional[Literal["acp", "claude_json"]] = None """The protocol used by the broker to deliver events to the agent.""" 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 bb1dfccc6..c0225bb7c 100644 --- a/src/runloop_api_client/types/shared_params/broker_mount.py +++ b/src/runloop_api_client/types/shared_params/broker_mount.py @@ -19,14 +19,14 @@ class BrokerMount(TypedDict, total=False): agent_binary: Optional[str] """Binary to launch the agent (e.g., 'opencode'). - Used by protocols that launch a subprocess (acp, claude_json, codex_app_server). + Used by protocols that launch a subprocess (acp, claude_json). """ launch_args: Optional[SequenceNotStr[str]] """Arguments to pass to the agent command (e.g., ['acp']). - Used by protocols that launch a subprocess (acp, claude_json, codex_app_server). + Used by protocols that launch a subprocess (acp, claude_json). """ - protocol: Optional[Literal["acp", "claude_json", "codex_app_server"]] + protocol: Optional[Literal["acp", "claude_json"]] """The protocol used by the broker to deliver events to the agent.""" From 1dd716d820546f3d088ad61a714a0d5c219124f4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 23:55:16 +0000 Subject: [PATCH 7/9] feat(api): add lifecycle configuration to launch parameters (#8606) --- .stats.yml | 4 +- .../resources/devboxes/devboxes.py | 8 +++- .../types/devbox_create_params.py | 4 +- .../types/devbox_enable_tunnel_params.py | 4 +- .../types/shared/launch_parameters.py | 38 ++++++++++++++++- .../types/shared_params/launch_parameters.py | 38 ++++++++++++++++- tests/api_resources/test_benchmarks.py | 14 +++++++ tests/api_resources/test_blueprints.py | 42 +++++++++++++++++++ tests/api_resources/test_devboxes.py | 14 +++++++ tests/api_resources/test_scenarios.py | 42 +++++++++++++++++++ 10 files changed, 198 insertions(+), 10 deletions(-) diff --git a/.stats.yml b/.stats.yml index c3f1be3bd..cf2319281 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-07efaf8d4a6487d9682186239706cf0da903131db3ca8ce52fb09a8103f4a091.yml -openapi_spec_hash: b29e96f8962659b2977a0bb178cfe404 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-72e5e6463144100abe3cc23ed837d5ebba4b95d97f9d58406954a3c48f27c310.yml +openapi_spec_hash: fee1880d8e4bb34c1cf97f5eef3b3248 config_hash: ecb1ff09d29b565ed1452b5e0362e64d diff --git a/src/runloop_api_client/resources/devboxes/devboxes.py b/src/runloop_api_client/resources/devboxes/devboxes.py index 5db3f18f6..a80acc108 100644 --- a/src/runloop_api_client/resources/devboxes/devboxes.py +++ b/src/runloop_api_client/resources/devboxes/devboxes.py @@ -761,7 +761,9 @@ def enable_tunnel( policies, resetting the idle timer. Defaults to true if not specified. wake_on_http: When true, HTTP traffic to a suspended devbox will automatically trigger a - resume. Defaults to false if not specified. + resume. Defaults to false if not specified. Prefer + lifecycle.resume_triggers.http on launch_parameters for new integrations. If + both are set, lifecycle.resume_triggers.http takes precedence. extra_headers: Send extra headers @@ -2394,7 +2396,9 @@ async def enable_tunnel( policies, resetting the idle timer. Defaults to true if not specified. wake_on_http: When true, HTTP traffic to a suspended devbox will automatically trigger a - resume. Defaults to false if not specified. + resume. Defaults to false if not specified. Prefer + lifecycle.resume_triggers.http on launch_parameters for new integrations. If + both are set, lifecycle.resume_triggers.http takes precedence. extra_headers: Send extra headers diff --git a/src/runloop_api_client/types/devbox_create_params.py b/src/runloop_api_client/types/devbox_create_params.py index 1b41796b2..af2e8de6d 100644 --- a/src/runloop_api_client/types/devbox_create_params.py +++ b/src/runloop_api_client/types/devbox_create_params.py @@ -153,5 +153,7 @@ class Tunnel(TypedDict, total=False): wake_on_http: Optional[bool] """ When true, HTTP traffic to a suspended devbox will automatically trigger a - resume. Defaults to false if not specified. + resume. Defaults to false if not specified. Prefer + lifecycle.resume_triggers.http on launch_parameters for new integrations. If + both are set, lifecycle.resume_triggers.http takes precedence. """ diff --git a/src/runloop_api_client/types/devbox_enable_tunnel_params.py b/src/runloop_api_client/types/devbox_enable_tunnel_params.py index cd380ebb4..68c5023d8 100644 --- a/src/runloop_api_client/types/devbox_enable_tunnel_params.py +++ b/src/runloop_api_client/types/devbox_enable_tunnel_params.py @@ -21,5 +21,7 @@ class DevboxEnableTunnelParams(TypedDict, total=False): wake_on_http: Optional[bool] """ When true, HTTP traffic to a suspended devbox will automatically trigger a - resume. Defaults to false if not specified. + resume. Defaults to false if not specified. Prefer + lifecycle.resume_triggers.http on launch_parameters for new integrations. If + both are set, lifecycle.resume_triggers.http takes precedence. """ diff --git a/src/runloop_api_client/types/shared/launch_parameters.py b/src/runloop_api_client/types/shared/launch_parameters.py index f882691e0..a1b52ec03 100644 --- a/src/runloop_api_client/types/shared/launch_parameters.py +++ b/src/runloop_api_client/types/shared/launch_parameters.py @@ -6,7 +6,31 @@ from ..._models import BaseModel from .after_idle import AfterIdle -__all__ = ["LaunchParameters", "UserParameters"] +__all__ = ["LaunchParameters", "Lifecycle", "LifecycleResumeTriggers", "UserParameters"] + + +class LifecycleResumeTriggers(BaseModel): + """Triggers that can resume a suspended Devbox.""" + + http: Optional[bool] = None + """When true, HTTP traffic to a suspended Devbox via tunnel will trigger a resume.""" + + +class Lifecycle(BaseModel): + """Lifecycle configuration for idle and resume behavior. + + Configure idle policy via lifecycle.after_idle (if both this and the top-level after_idle are set, they must match) and resume triggers via lifecycle.resume_triggers. + """ + + after_idle: Optional[AfterIdle] = None + """Configure Devbox lifecycle based on idle activity. + + If both this and the top-level after_idle are set, they must have the same + value. Prefer this field for new integrations. + """ + + resume_triggers: Optional[LifecycleResumeTriggers] = None + """Triggers that can resume a suspended Devbox.""" class UserParameters(BaseModel): @@ -30,7 +54,9 @@ class LaunchParameters(BaseModel): after_idle: Optional[AfterIdle] = None """Configure Devbox lifecycle based on idle activity. - If after_idle is set, Devbox will ignore keep_alive_time_seconds. + If after_idle is set, Devbox will ignore keep_alive_time_seconds. If both + after_idle and lifecycle.after_idle are set, they must have the same value. Use + lifecycle.after_idle instead. """ architecture: Optional[Literal["x86_64", "arm64"]] = None @@ -60,6 +86,14 @@ class LaunchParameters(BaseModel): launch_commands: Optional[List[str]] = None """Set of commands to be run at launch time, before the entrypoint process is run.""" + lifecycle: Optional[Lifecycle] = None + """Lifecycle configuration for idle and resume behavior. + + Configure idle policy via lifecycle.after_idle (if both this and the top-level + after_idle are set, they must match) and resume triggers via + lifecycle.resume_triggers. + """ + network_policy_id: Optional[str] = None """ (Optional) ID of the network policy to apply to Devboxes launched with these diff --git a/src/runloop_api_client/types/shared_params/launch_parameters.py b/src/runloop_api_client/types/shared_params/launch_parameters.py index 65babd117..3906163bf 100644 --- a/src/runloop_api_client/types/shared_params/launch_parameters.py +++ b/src/runloop_api_client/types/shared_params/launch_parameters.py @@ -8,7 +8,31 @@ from ..._types import SequenceNotStr from .after_idle import AfterIdle -__all__ = ["LaunchParameters", "UserParameters"] +__all__ = ["LaunchParameters", "Lifecycle", "LifecycleResumeTriggers", "UserParameters"] + + +class LifecycleResumeTriggers(TypedDict, total=False): + """Triggers that can resume a suspended Devbox.""" + + http: Optional[bool] + """When true, HTTP traffic to a suspended Devbox via tunnel will trigger a resume.""" + + +class Lifecycle(TypedDict, total=False): + """Lifecycle configuration for idle and resume behavior. + + Configure idle policy via lifecycle.after_idle (if both this and the top-level after_idle are set, they must match) and resume triggers via lifecycle.resume_triggers. + """ + + after_idle: Optional[AfterIdle] + """Configure Devbox lifecycle based on idle activity. + + If both this and the top-level after_idle are set, they must have the same + value. Prefer this field for new integrations. + """ + + resume_triggers: Optional[LifecycleResumeTriggers] + """Triggers that can resume a suspended Devbox.""" class UserParameters(TypedDict, total=False): @@ -32,7 +56,9 @@ class LaunchParameters(TypedDict, total=False): after_idle: Optional[AfterIdle] """Configure Devbox lifecycle based on idle activity. - If after_idle is set, Devbox will ignore keep_alive_time_seconds. + If after_idle is set, Devbox will ignore keep_alive_time_seconds. If both + after_idle and lifecycle.after_idle are set, they must have the same value. Use + lifecycle.after_idle instead. """ architecture: Optional[Literal["x86_64", "arm64"]] @@ -62,6 +88,14 @@ class LaunchParameters(TypedDict, total=False): launch_commands: Optional[SequenceNotStr[str]] """Set of commands to be run at launch time, before the entrypoint process is run.""" + lifecycle: Optional[Lifecycle] + """Lifecycle configuration for idle and resume behavior. + + Configure idle policy via lifecycle.after_idle (if both this and the top-level + after_idle are set, they must match) and resume triggers via + lifecycle.resume_triggers. + """ + network_policy_id: Optional[str] """ (Optional) ID of the network policy to apply to Devboxes launched with these diff --git a/tests/api_resources/test_benchmarks.py b/tests/api_resources/test_benchmarks.py index 2e1909775..924deb133 100644 --- a/tests/api_resources/test_benchmarks.py +++ b/tests/api_resources/test_benchmarks.py @@ -299,6 +299,13 @@ def test_method_start_run_with_all_params(self, client: Runloop) -> None: "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", @@ -674,6 +681,13 @@ async def test_method_start_run_with_all_params(self, async_client: AsyncRunloop "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", diff --git a/tests/api_resources/test_blueprints.py b/tests/api_resources/test_blueprints.py index 8b36a5d3b..303a9bf3d 100644 --- a/tests/api_resources/test_blueprints.py +++ b/tests/api_resources/test_blueprints.py @@ -64,6 +64,13 @@ def test_method_create_with_all_params(self, client: Runloop) -> None: "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", @@ -279,6 +286,13 @@ def test_method_create_from_inspection_with_all_params(self, client: Runloop) -> "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", @@ -437,6 +451,13 @@ def test_method_preview_with_all_params(self, client: Runloop) -> None: "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", @@ -538,6 +559,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) - "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", @@ -753,6 +781,13 @@ async def test_method_create_from_inspection_with_all_params(self, async_client: "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", @@ -911,6 +946,13 @@ async def test_method_preview_with_all_params(self, async_client: AsyncRunloop) "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", diff --git a/tests/api_resources/test_devboxes.py b/tests/api_resources/test_devboxes.py index c04de1d06..23b74b0df 100644 --- a/tests/api_resources/test_devboxes.py +++ b/tests/api_resources/test_devboxes.py @@ -84,6 +84,13 @@ def test_method_create_with_all_params(self, client: Runloop) -> None: "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", @@ -1700,6 +1707,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) - "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", diff --git a/tests/api_resources/test_scenarios.py b/tests/api_resources/test_scenarios.py index 8182687c0..748d153c8 100644 --- a/tests/api_resources/test_scenarios.py +++ b/tests/api_resources/test_scenarios.py @@ -78,6 +78,13 @@ def test_method_create_with_all_params(self, client: Runloop) -> None: "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", @@ -213,6 +220,13 @@ def test_method_update_with_all_params(self, client: Runloop) -> None: "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", @@ -421,6 +435,13 @@ def test_method_start_run_with_all_params(self, client: Runloop) -> None: "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", @@ -529,6 +550,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) - "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", @@ -664,6 +692,13 @@ async def test_method_update_with_all_params(self, async_client: AsyncRunloop) - "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", @@ -872,6 +907,13 @@ async def test_method_start_run_with_all_params(self, async_client: AsyncRunloop "custom_gb_memory": 0, "keep_alive_time_seconds": 0, "launch_commands": ["string"], + "lifecycle": { + "after_idle": { + "idle_time_seconds": 0, + "on_idle": "shutdown", + }, + "resume_triggers": {"http": True}, + }, "network_policy_id": "network_policy_id", "required_services": ["string"], "resource_size_request": "X_SMALL", From fc633d64b89ac81aa00cdf5f3528b7b9a2e61627 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 18:57:31 +0000 Subject: [PATCH 8/9] fix: propagate git ref to maverick for git-based agents (#8608) --- .stats.yml | 4 ++-- .../types/shared/code_mount_parameters.py | 6 ++++++ src/runloop_api_client/types/shared/mount.py | 6 ++++++ .../types/shared_params/code_mount_parameters.py | 6 ++++++ src/runloop_api_client/types/shared_params/mount.py | 6 ++++++ tests/api_resources/test_blueprints.py | 4 ++++ tests/api_resources/test_devboxes.py | 2 ++ 7 files changed, 32 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index cf2319281..448e19886 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-72e5e6463144100abe3cc23ed837d5ebba4b95d97f9d58406954a3c48f27c310.yml -openapi_spec_hash: fee1880d8e4bb34c1cf97f5eef3b3248 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-a1c7e69cbbf7a7cf63893358470cee52714633e6d31ce6dff2e7255c7445a1aa.yml +openapi_spec_hash: a0e88c05a9b74c2bc9192bd7d94de3c0 config_hash: ecb1ff09d29b565ed1452b5e0362e64d 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 ae2cb5874..32099b9e4 100644 --- a/src/runloop_api_client/types/shared/code_mount_parameters.py +++ b/src/runloop_api_client/types/shared/code_mount_parameters.py @@ -20,5 +20,11 @@ class CodeMountParameters(BaseModel): token: Optional[str] = None """The authentication token necessary to pull repo.""" + git_ref: Optional[str] = None + """Optional git ref (branch, tag, or commit SHA) to checkout. + + Defaults to the repository default branch. + """ + install_command: Optional[str] = None """Installation command to install and setup repository.""" diff --git a/src/runloop_api_client/types/shared/mount.py b/src/runloop_api_client/types/shared/mount.py index 5aef35821..8c29dbbdb 100644 --- a/src/runloop_api_client/types/shared/mount.py +++ b/src/runloop_api_client/types/shared/mount.py @@ -27,6 +27,12 @@ class CodeMount(BaseModel): token: Optional[str] = None """The authentication token necessary to pull repo.""" + git_ref: Optional[str] = None + """Optional git ref (branch, tag, or commit SHA) to checkout. + + Defaults to the repository default branch. + """ + install_command: Optional[str] = None """Installation command to install and setup repository.""" 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 b09c8b3c2..b7d1468ad 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 @@ -21,5 +21,11 @@ class CodeMountParameters(TypedDict, total=False): token: Optional[str] """The authentication token necessary to pull repo.""" + git_ref: Optional[str] + """Optional git ref (branch, tag, or commit SHA) to checkout. + + Defaults to the repository default branch. + """ + install_command: Optional[str] """Installation command to install and setup repository.""" diff --git a/src/runloop_api_client/types/shared_params/mount.py b/src/runloop_api_client/types/shared_params/mount.py index 4972fb6f4..1badcd396 100644 --- a/src/runloop_api_client/types/shared_params/mount.py +++ b/src/runloop_api_client/types/shared_params/mount.py @@ -27,6 +27,12 @@ class CodeMount(TypedDict, total=False): token: Optional[str] """The authentication token necessary to pull repo.""" + git_ref: Optional[str] + """Optional git ref (branch, tag, or commit SHA) to checkout. + + Defaults to the repository default branch. + """ + install_command: Optional[str] """Installation command to install and setup repository.""" diff --git a/tests/api_resources/test_blueprints.py b/tests/api_resources/test_blueprints.py index 303a9bf3d..89afc9e4b 100644 --- a/tests/api_resources/test_blueprints.py +++ b/tests/api_resources/test_blueprints.py @@ -47,6 +47,7 @@ def test_method_create_with_all_params(self, client: Runloop) -> None: "repo_name": "repo_name", "repo_owner": "repo_owner", "token": "token", + "git_ref": "git_ref", "install_command": "install_command", } ], @@ -434,6 +435,7 @@ def test_method_preview_with_all_params(self, client: Runloop) -> None: "repo_name": "repo_name", "repo_owner": "repo_owner", "token": "token", + "git_ref": "git_ref", "install_command": "install_command", } ], @@ -542,6 +544,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) - "repo_name": "repo_name", "repo_owner": "repo_owner", "token": "token", + "git_ref": "git_ref", "install_command": "install_command", } ], @@ -929,6 +932,7 @@ async def test_method_preview_with_all_params(self, async_client: AsyncRunloop) "repo_name": "repo_name", "repo_owner": "repo_owner", "token": "token", + "git_ref": "git_ref", "install_command": "install_command", } ], diff --git a/tests/api_resources/test_devboxes.py b/tests/api_resources/test_devboxes.py index 23b74b0df..ec948f719 100644 --- a/tests/api_resources/test_devboxes.py +++ b/tests/api_resources/test_devboxes.py @@ -60,6 +60,7 @@ def test_method_create_with_all_params(self, client: Runloop) -> None: "repo_name": "repo_name", "repo_owner": "repo_owner", "token": "token", + "git_ref": "git_ref", "install_command": "install_command", } ], @@ -1683,6 +1684,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) - "repo_name": "repo_name", "repo_owner": "repo_owner", "token": "token", + "git_ref": "git_ref", "install_command": "install_command", } ], From 426e250a00a4587f2c0f72979c3414939986aef8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 18:57:55 +0000 Subject: [PATCH 9/9] release: 1.17.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 27 +++++++++++++++++++++++++++ pyproject.toml | 2 +- src/runloop_api_client/_version.py | 2 +- 4 files changed, 30 insertions(+), 3 deletions(-) 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/CHANGELOG.md b/CHANGELOG.md index 1da26f72e..42b189891 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 1.17.0 (2026-04-09) + +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)) +* **api:** add lifecycle configuration to launch parameters ([#8606](https://github.com/runloopai/api-client-python/issues/8606)) ([1dd716d](https://github.com/runloopai/api-client-python/commit/1dd716d820546f3d088ad61a714a0d5c219124f4)) +* 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 + +* **broker:** remove codex in favor of codex via acp ([#8602](https://github.com/runloopai/api-client-python/issues/8602)) ([118162e](https://github.com/runloopai/api-client-python/commit/118162e3b64d531c5e2b66f66d47724b01fefb1c)) +* **client:** preserve hardcoded query params when merging with user params ([5ce9b5d](https://github.com/runloopai/api-client-python/commit/5ce9b5d4cb882a93b802275622471086530146f0)) +* propagate git ref to maverick for git-based agents ([#8608](https://github.com/runloopai/api-client-python/issues/8608)) ([fc633d6](https://github.com/runloopai/api-client-python/commit/fc633d64b89ac81aa00cdf5f3528b7b9a2e61627)) + + +### 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/_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