Files
openai--openai-agents-python/examples/sandbox/extensions/runloop/capabilities.py
T
2026-07-13 12:39:17 +08:00

996 lines
34 KiB
Python

from __future__ import annotations
import argparse
import asyncio
import io
import json
import os
import sys
import time
import urllib.error
import urllib.request
import uuid
from pathlib import Path
from typing import Any, Literal, cast
from urllib.parse import urljoin
from openai.types.responses import ResponseTextDeltaEvent
from pydantic import BaseModel
from agents import Agent, ModelSettings, Runner, function_tool
from agents.run import RunConfig
from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig
if __package__ is None or __package__ == "":
sys.path.insert(0, str(Path(__file__).resolve().parents[4]))
from examples.sandbox.misc.example_support import text_manifest, tool_call_name
from examples.sandbox.misc.workspace_shell import WorkspaceShellCapability
try:
from agents.extensions.sandbox import (
DEFAULT_RUNLOOP_ROOT_WORKSPACE_ROOT,
DEFAULT_RUNLOOP_WORKSPACE_ROOT,
RunloopAfterIdle,
RunloopGatewaySpec,
RunloopLaunchParameters,
RunloopMcpSpec,
RunloopSandboxClient,
RunloopSandboxClientOptions,
RunloopSandboxSessionState,
RunloopTunnelConfig,
RunloopUserParameters,
)
except Exception as exc: # pragma: no cover - import path depends on optional extras
raise SystemExit(
"Runloop sandbox examples require the optional repo extra.\n"
"Install it with: uv sync --extra runloop"
) from exc
DEFAULT_MODEL = "gpt-5.6-sol"
DEFAULT_HTTP_PORT = 8123
DEFAULT_AGENT_PROMPT = (
"Inspect this Runloop sandbox workspace, verify the configuration using the shell tool, "
"and summarize which Runloop-specific capabilities were exercised."
)
EXAMPLE_RESOURCE_SLUG = "runloop-capabilities-example"
PERSISTENT_SECRET_NAME = "RUNLOOP_CAPABILITIES_EXAMPLE_TOKEN"
PERSISTENT_SECRET_VALUE = "runloop-capabilities-example-token"
PERSISTENT_NETWORK_POLICY_NAME = "runloop-capabilities-example-policy"
HTTP_LOG_PATH = Path(".runloop-http.log")
RUNTIME_CONTEXT_PATH = Path("runtime_context.json")
AGENT_PROOF_PATH = Path("verification/agent-proof.txt")
class RunloopResourceQueryResult(BaseModel):
resource_type: Literal["secret", "network_policy"]
name: str
found: bool
id: str | None = None
description: str | None = None
class RunloopResourceBootstrapResult(BaseModel):
resource_type: Literal["secret", "network_policy"]
name: str
action: Literal["created", "reused", "override"]
id: str | None = None
found_before_bootstrap: bool
def _phase(title: str) -> None:
print(f"\n=== {title} ===", flush=True)
def _require_env(name: str) -> None:
if os.environ.get(name):
return
raise SystemExit(f"{name} must be set before running this example.")
def _run_id() -> str:
return uuid.uuid4().hex[:8]
def _summarize_resource(item: object, fields: tuple[str, ...]) -> dict[str, object]:
summary: dict[str, object] = {}
for field in fields:
value = getattr(item, field, None)
if value is not None:
summary[field] = value
return summary
async def _collect_async_items(items: Any, *, limit: int) -> list[Any]:
collected: list[Any] = []
async for item in items:
collected.append(item)
if len(collected) >= limit:
break
return collected
def _status_code(exc: BaseException) -> int | None:
status_code = getattr(exc, "status_code", None)
if isinstance(status_code, int):
return status_code
response = getattr(exc, "response", None)
response_status = getattr(response, "status_code", None)
return response_status if isinstance(response_status, int) else None
def _is_not_found(exc: BaseException) -> bool:
return _status_code(exc) == 404
def _error_message(exc: BaseException) -> str | None:
message = getattr(exc, "message", None)
if isinstance(message, str):
return message
body = getattr(exc, "body", None)
if isinstance(body, dict):
body_message = body.get("message")
if isinstance(body_message, str):
return body_message
return None
def _is_conflict(exc: BaseException) -> bool:
status_code = _status_code(exc)
if status_code == 409:
return True
if status_code == 400:
message = _error_message(exc)
return isinstance(message, str) and "already exists" in message.lower()
return False
async def _collect_maybe_async_items(items: Any, *, limit: int) -> list[Any]:
if hasattr(items, "__aiter__"):
return await _collect_async_items(items, limit=limit)
return list(items)[:limit]
async def _read_text(session: Any, path: Path) -> str:
data = await session.read(path)
try:
payload = data.read()
finally:
data.close()
if isinstance(payload, bytes):
return payload.decode("utf-8")
return str(payload)
async def _write_json(session: Any, path: Path, payload: dict[str, object]) -> None:
await session.write(
path, io.BytesIO(json.dumps(payload, indent=2, sort_keys=True).encode("utf-8"))
)
def _build_manifest(*, workspace_root: str, context: dict[str, object]) -> Manifest:
manifest = text_manifest(
{
"README.md": (
"# Runloop Capabilities Example\n\n"
"This workspace is used to validate the Runloop-specific sandbox integration end "
"to end.\n"
),
"checklist.md": (
"# Checklist\n\n"
"1. Inspect the workspace.\n"
"2. Verify the resource discovery results in the context files.\n"
"3. Confirm the managed secret is available without printing its full value.\n"
"4. Confirm the HTTP preview server and verification file.\n"
"5. Summarize what Runloop-native features were exercised and whether persistent "
"resources were reused or created.\n"
),
"platform_context.json": json.dumps(context, indent=2, sort_keys=True) + "\n",
}
)
return Manifest(root=workspace_root, entries=manifest.entries)
def _build_sandbox_agent(
*, model: str, manifest: Manifest, managed_secret_name: str
) -> SandboxAgent:
return SandboxAgent(
name="Runloop Capabilities Guide",
model=model,
instructions=(
"Inspect the Runloop sandbox workspace carefully before answering. Use the shell tool "
"to verify what happened in the environment and keep the final response concise. "
"Follow this sequence:\n"
"1. Run `pwd` and `find . -maxdepth 3 -type f | sort`.\n"
"2. Read `README.md`, `checklist.md`, `platform_context.json`, and `runtime_context.json`.\n"
"3. Report whether the managed secret and network policy existed before bootstrap by "
"reading the query/bootstrap summaries from the context files.\n"
f"4. Confirm whether `${managed_secret_name}` is set, but never print the full value. "
"Only report whether it exists and its character length.\n"
f"5. Read `{HTTP_LOG_PATH.as_posix()}` and confirm the HTTP server started.\n"
f"6. Create `{AGENT_PROOF_PATH.as_posix()}` with these exact lines:\n"
" runloop_capabilities_verified=true\n"
" managed_secret_checked=true\n"
" tunnel_verified=true\n"
"7. Print that verification file from the shell.\n"
"8. Final answer: 2 short sentences naming the specific Runloop features exercised, "
"including whether the persistent secret and policy were reused or created.\n"
"Only mention facts you verified from files, environment inspection, or shell output."
),
default_manifest=manifest,
capabilities=[WorkspaceShellCapability()],
model_settings=ModelSettings(tool_choice="required"),
)
def _build_query_agent(
*,
model: str,
query_secret_tool: Any,
query_policy_tool: Any,
managed_secret_name: str,
network_policy_name: str,
) -> Agent:
return Agent(
name="Runloop Resource Discovery Guide",
model=model,
instructions=(
"Use the provided Runloop query tools to check whether the persistent example "
"resources already exist before any create step. Keep the final answer concise."
),
tools=[query_secret_tool, query_policy_tool],
model_settings=ModelSettings(tool_choice="required"),
).clone(
instructions=(
"Use the provided Runloop query tools to check whether the persistent example "
"resources already exist before any create step. Keep the final answer concise."
),
handoff_description=None,
output_type=None,
)
def _stream_event_banner(event_name: str) -> str | None:
if event_name == "tool_called":
return "[tool call]"
if event_name == "tool_output":
return "[tool output]"
return None
def _runloop_state(session: Any) -> RunloopSandboxSessionState:
return cast(RunloopSandboxSessionState, session.state)
async def _run_plain_agent(
*,
agent: Agent,
prompt: str,
workflow_name: str,
stream: bool,
) -> str:
if not stream:
result = await Runner.run(agent, prompt, run_config=RunConfig(workflow_name=workflow_name))
print(result.final_output)
return str(result.final_output)
stream_result = Runner.run_streamed(
agent,
prompt,
run_config=RunConfig(workflow_name=workflow_name),
)
saw_text_delta = False
saw_any_text = False
async for event in stream_result.stream_events():
if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
if not saw_text_delta:
print("assistant> ", end="", flush=True)
saw_text_delta = True
print(event.data.delta, end="", flush=True)
saw_any_text = True
continue
if event.type != "run_item_stream_event":
continue
banner = _stream_event_banner(event.name)
if banner is None:
continue
if saw_text_delta:
print()
saw_text_delta = False
print(f"{banner}: {tool_call_name(event.item.raw_item) or 'tool'}", flush=True)
if saw_text_delta:
print()
if not saw_any_text:
print(stream_result.final_output)
return str(stream_result.final_output)
async def _run_sandbox_agent(
*,
agent: SandboxAgent,
prompt: str,
session: Any,
workflow_name: str,
stream: bool,
) -> str:
if not stream:
result = await Runner.run(
agent,
prompt,
run_config=RunConfig(
sandbox=SandboxRunConfig(session=session),
workflow_name=workflow_name,
),
)
print(result.final_output)
return str(result.final_output)
stream_result = Runner.run_streamed(
agent,
prompt,
run_config=RunConfig(
sandbox=SandboxRunConfig(session=session),
workflow_name=workflow_name,
),
)
saw_text_delta = False
saw_any_text = False
async for event in stream_result.stream_events():
if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
if not saw_text_delta:
print("assistant> ", end="", flush=True)
saw_text_delta = True
print(event.data.delta, end="", flush=True)
saw_any_text = True
continue
if event.type != "run_item_stream_event":
continue
banner = _stream_event_banner(event.name)
if banner is None:
continue
if saw_text_delta:
print()
saw_text_delta = False
print(f"{banner}: {tool_call_name(event.item.raw_item) or 'tool'}", flush=True)
if saw_text_delta:
print()
if not saw_any_text:
print(stream_result.final_output)
return str(stream_result.final_output)
async def _start_http_server(session: Any, *, port: int, workspace_root: str) -> None:
command = (
"python -m http.server "
f"{port} --bind 0.0.0.0 --directory {workspace_root} "
f"> {HTTP_LOG_PATH.as_posix()} 2>&1 &"
)
result = await session.exec(command, shell=True, timeout=10)
if not result.ok():
raise RuntimeError(result.stderr.decode("utf-8", errors="replace"))
def _build_endpoint_url(endpoint: Any) -> str:
scheme = "https" if endpoint.tls else "http"
port = endpoint.port
host = endpoint.host
if (scheme == "https" and port == 443) or (scheme == "http" and port == 80):
return f"{scheme}://{host}/"
return f"{scheme}://{host}:{port}/"
async def _fetch_text(url: str, *, timeout_s: float) -> str:
def _fetch() -> str:
with urllib.request.urlopen(url, timeout=timeout_s) as response:
payload = response.read()
if isinstance(payload, bytes):
return payload.decode("utf-8", errors="replace")
return str(payload)
return await asyncio.to_thread(_fetch)
async def _poll_http_preview(url: str, *, expected_substring: str, timeout_s: float) -> str:
deadline = time.monotonic() + timeout_s
last_error: Exception | None = None
while time.monotonic() < deadline:
try:
body = await _fetch_text(url, timeout_s=5.0)
if expected_substring in body:
return body
except (urllib.error.URLError, TimeoutError) as exc:
last_error = exc
await asyncio.sleep(2)
if last_error is not None:
raise RuntimeError(f"HTTP preview never became ready: {last_error}") from last_error
raise RuntimeError("HTTP preview never returned the expected content.")
async def _preflight_public_resources(client: RunloopSandboxClient) -> dict[str, object]:
blueprints = await _collect_async_items(
await client.platform.blueprints.list_public(limit=3),
limit=3,
)
benchmarks = await _collect_async_items(
await client.platform.benchmarks.list_public(limit=3),
limit=3,
)
blueprint_summaries = [
_summarize_resource(item, ("id", "name", "status")) for item in blueprints
]
benchmark_summaries = [
_summarize_resource(item, ("id", "name", "description")) for item in benchmarks
]
if blueprint_summaries:
print("public blueprints:")
for summary in blueprint_summaries:
print(f" - {summary}")
else:
print("public blueprints: none returned")
if benchmark_summaries:
print("public benchmarks:")
for summary in benchmark_summaries:
print(f" - {summary}")
else:
print("public benchmarks: none returned")
return {
"public_blueprints": blueprint_summaries,
"public_benchmarks": benchmark_summaries,
}
async def _query_runloop_secret(
client: RunloopSandboxClient,
*,
name: str,
) -> RunloopResourceQueryResult:
try:
secret = cast(Any, await client.platform.secrets.get(name))
except Exception as exc:
if _is_not_found(exc):
return RunloopResourceQueryResult(resource_type="secret", name=name, found=False)
raise
return RunloopResourceQueryResult(
resource_type="secret",
name=name,
found=True,
id=cast(str | None, getattr(secret, "id", None)),
)
async def _query_runloop_network_policy(
client: RunloopSandboxClient,
*,
name: str,
) -> RunloopResourceQueryResult:
policies = await _collect_maybe_async_items(
await client.platform.network_policies.list(name=name, limit=10),
limit=10,
)
for policy in policies:
if getattr(policy, "name", None) != name:
continue
info = cast(
Any, await client.platform.network_policies.get(cast(str, policy.id)).get_info()
)
return RunloopResourceQueryResult(
resource_type="network_policy",
name=name,
found=True,
id=cast(str | None, getattr(policy, "id", None)),
description=cast(str | None, getattr(info, "description", None)),
)
return RunloopResourceQueryResult(resource_type="network_policy", name=name, found=False)
def _build_resource_query_tools(
client: RunloopSandboxClient,
*,
managed_secret_name: str,
network_policy_name: str,
) -> tuple[list[Any], dict[str, RunloopResourceQueryResult]]:
query_results: dict[str, RunloopResourceQueryResult] = {}
@function_tool
async def query_runloop_secret(name: str) -> RunloopResourceQueryResult:
"""Query whether a Runloop secret exists by name and return non-sensitive metadata."""
result = await _query_runloop_secret(client, name=name)
query_results["secret"] = result
return result
@function_tool
async def query_runloop_network_policy(name: str) -> RunloopResourceQueryResult:
"""Query whether a Runloop network policy exists by name and return basic metadata."""
result = await _query_runloop_network_policy(client, name=name)
query_results["network_policy"] = result
return result
tools = [query_runloop_secret, query_runloop_network_policy]
_ = (managed_secret_name, network_policy_name)
return tools, query_results
async def _run_resource_query_phase(
client: RunloopSandboxClient,
*,
model: str,
stream: bool,
managed_secret_name: str,
network_policy_name: str,
) -> tuple[dict[str, RunloopResourceQueryResult], str]:
tools, query_results = _build_resource_query_tools(
client,
managed_secret_name=managed_secret_name,
network_policy_name=network_policy_name,
)
query_agent = Agent(
name="Runloop Resource Discovery Guide",
model=model,
instructions=(
"Use both query tools before answering. You are checking whether the persistent "
"Runloop example resources already exist before any create step.\n\n"
f"1. Call `query_runloop_secret` with `{managed_secret_name}`.\n"
f"2. Call `query_runloop_network_policy` with `{network_policy_name}`.\n"
"3. Final answer in 2 short sentences stating whether each resource already exists."
),
tools=tools,
model_settings=ModelSettings(tool_choice="required"),
)
prompt = (
"Check whether the persistent Runloop secret and network policy for this example already "
"exist before the script attempts any create or reuse step."
)
output = await _run_plain_agent(
agent=query_agent,
prompt=prompt,
workflow_name="Runloop resource query example",
stream=stream,
)
if "secret" not in query_results or "network_policy" not in query_results:
raise RuntimeError("The query agent did not call both Runloop resource query tools.")
return query_results, output
async def _bootstrap_persistent_resources(
client: RunloopSandboxClient,
*,
managed_secret_name: str,
managed_secret_value: str,
network_policy_name: str,
network_policy_id_override: str | None,
query_results: dict[str, RunloopResourceQueryResult],
axon_name: str | None,
) -> dict[str, object]:
secret_query = query_results["secret"]
policy_query = query_results["network_policy"]
bootstrap: dict[str, object] = {
"managed_secret_value": managed_secret_value,
"secret": RunloopResourceBootstrapResult(
resource_type="secret",
name=managed_secret_name,
action="reused" if secret_query.found else "created",
id=secret_query.id,
found_before_bootstrap=secret_query.found,
),
"network_policy": RunloopResourceBootstrapResult(
resource_type="network_policy",
name=network_policy_name,
action="override"
if network_policy_id_override
else ("reused" if policy_query.found else "created"),
id=network_policy_id_override or policy_query.id,
found_before_bootstrap=policy_query.found,
),
"axon_id": None,
"axon_name": axon_name,
}
secret_result = cast(RunloopResourceBootstrapResult, bootstrap["secret"])
if not secret_query.found:
created_secret = cast(
Any,
await client.platform.secrets.create(
name=managed_secret_name, value=managed_secret_value
),
)
secret_result.id = cast(str | None, getattr(created_secret, "id", None))
print(
"persistent secret bootstrap:",
secret_result.model_dump(mode="json"),
)
policy_result = cast(RunloopResourceBootstrapResult, bootstrap["network_policy"])
if network_policy_id_override is None and not policy_query.found:
try:
created_policy = cast(
Any,
await client.platform.network_policies.create(
name=network_policy_name,
allow_all=True,
description="Persistent network policy for the Runloop capabilities example.",
),
)
except Exception as exc:
if not _is_conflict(exc):
raise
policy_result.action = "reused"
policy_result.found_before_bootstrap = True
refreshed_policy = await _query_runloop_network_policy(client, name=network_policy_name)
policy_result.id = refreshed_policy.id
else:
policy_result.id = cast(str | None, getattr(created_policy, "id", None))
print(
"persistent network policy bootstrap:",
policy_result.model_dump(mode="json"),
)
if axon_name is not None:
axon = cast(Any, await client.platform.axons.create(name=axon_name))
await client.platform.axons.query_sql(
cast(str, axon.id),
sql="CREATE TABLE IF NOT EXISTS events (id INTEGER PRIMARY KEY AUTOINCREMENT, kind TEXT NOT NULL)",
)
await client.platform.axons.batch_sql(
cast(str, axon.id),
statements=[
{"sql": "INSERT INTO events (kind) VALUES (?)", "params": ["capabilities"]},
{"sql": "INSERT INTO events (kind) VALUES (?)", "params": ["agent_guided"]},
],
)
query_result = cast(
Any,
await client.platform.axons.query_sql(
cast(str, axon.id),
sql="SELECT COUNT(*) AS total_events FROM events",
),
)
publish_result = cast(
Any,
await client.platform.axons.publish(
cast(str, axon.id),
event_type="capabilities_example",
origin="AGENT_EVENT",
payload=json.dumps({"axon_name": axon_name}),
source="openai-agents-python",
),
)
bootstrap["axon_id"] = cast(str, axon.id)
print(
"axon demo created:",
{
"id": cast(str, axon.id),
"name": axon_name,
"rows": query_result.rows,
"published": getattr(publish_result, "published", None),
},
)
return bootstrap
def _optional_gateways(args: argparse.Namespace) -> dict[str, RunloopGatewaySpec]:
if not (args.gateway_env_var and args.gateway_name and args.gateway_secret_name):
return {}
return {
args.gateway_env_var: RunloopGatewaySpec(
gateway=args.gateway_name,
secret=args.gateway_secret_name,
)
}
def _optional_mcp(args: argparse.Namespace) -> dict[str, RunloopMcpSpec]:
if not (args.mcp_env_var and args.mcp_config and args.mcp_secret_name):
return {}
return {
args.mcp_env_var: RunloopMcpSpec(
mcp_config=args.mcp_config,
secret=args.mcp_secret_name,
)
}
async def main(args: argparse.Namespace) -> None:
_require_env("OPENAI_API_KEY")
_require_env("RUNLOOP_API_KEY")
workspace_root = (
DEFAULT_RUNLOOP_ROOT_WORKSPACE_ROOT if args.root else DEFAULT_RUNLOOP_WORKSPACE_ROOT
)
run_id = _run_id()
metadata = {
"example": "runloop-capabilities",
"run_id": run_id,
}
client = RunloopSandboxClient()
session = None
resumed = None
session_closed = False
resumed_closed = False
try:
_phase("Public Resource Discovery")
public_context = await _preflight_public_resources(client)
_phase("Agent Resource Discovery")
query_results, query_agent_output = await _run_resource_query_phase(
client,
model=args.model,
stream=args.stream,
managed_secret_name=PERSISTENT_SECRET_NAME,
network_policy_name=PERSISTENT_NETWORK_POLICY_NAME,
)
print(
"resource query results:",
{key: value.model_dump(mode="json") for key, value in query_results.items()},
)
_phase("Persistent Resource Bootstrap")
axon_name = f"{EXAMPLE_RESOURCE_SLUG}-axon-{run_id}" if args.with_axon_demo else None
bootstrap = await _bootstrap_persistent_resources(
client,
managed_secret_name=PERSISTENT_SECRET_NAME,
managed_secret_value=PERSISTENT_SECRET_VALUE,
network_policy_name=PERSISTENT_NETWORK_POLICY_NAME,
network_policy_id_override=args.network_policy_id,
query_results=query_results,
axon_name=axon_name,
)
secret_bootstrap = cast(RunloopResourceBootstrapResult, bootstrap["secret"])
network_policy_bootstrap = cast(RunloopResourceBootstrapResult, bootstrap["network_policy"])
network_policy_id = network_policy_bootstrap.id
context = {
"example_slug": EXAMPLE_RESOURCE_SLUG,
"workspace_root": workspace_root,
"requested_blueprint_name": args.blueprint_name,
"public_resources": public_context,
"resource_query_agent_output": query_agent_output,
"resource_queries": {
key: value.model_dump(mode="json") for key, value in query_results.items()
},
"resource_bootstrap": {
"secret": secret_bootstrap.model_dump(mode="json"),
"network_policy": network_policy_bootstrap.model_dump(mode="json"),
"axon_id": bootstrap["axon_id"],
"axon_name": bootstrap["axon_name"],
},
"managed_secret_env_var": PERSISTENT_SECRET_NAME,
"network_policy_id": network_policy_id,
"metadata": metadata,
"gateway_bindings": sorted(_optional_gateways(args)),
"mcp_bindings": sorted(_optional_mcp(args)),
}
manifest = _build_manifest(workspace_root=workspace_root, context=context)
agent = _build_sandbox_agent(
model=args.model,
manifest=manifest,
managed_secret_name=PERSISTENT_SECRET_NAME,
)
options = RunloopSandboxClientOptions(
blueprint_name=args.blueprint_name,
pause_on_exit=True,
exposed_ports=(args.http_port,),
user_parameters=(RunloopUserParameters(username="root", uid=0) if args.root else None),
launch_parameters=RunloopLaunchParameters(
network_policy_id=network_policy_id,
resource_size_request=args.resource_size,
after_idle=RunloopAfterIdle(idle_time_seconds=300, on_idle="suspend"),
launch_commands=["echo runloop-capabilities-example"],
),
tunnel=RunloopTunnelConfig(
auth_mode="open",
http_keep_alive=True,
wake_on_http=True,
),
gateways=_optional_gateways(args),
mcp=_optional_mcp(args),
metadata=metadata,
managed_secrets={PERSISTENT_SECRET_NAME: PERSISTENT_SECRET_VALUE},
)
_phase("Sandbox Create")
session = await client.create(manifest=manifest, options=options)
await session.start()
session_state = _runloop_state(session)
print(
"session started:",
{
"devbox_id": session_state.devbox_id,
"secret_refs": session_state.secret_refs,
"metadata": session_state.metadata,
},
)
_phase("Tunnel Check")
await _write_json(
session,
RUNTIME_CONTEXT_PATH,
{
**context,
"devbox_id": session_state.devbox_id,
"secret_refs": session_state.secret_refs,
"runtime_phase": "before_tunnel_check",
},
)
await _start_http_server(session, port=args.http_port, workspace_root=workspace_root)
endpoint = await session.resolve_exposed_port(args.http_port)
preview_url = urljoin(_build_endpoint_url(endpoint), "README.md")
preview_body = await _poll_http_preview(
preview_url,
expected_substring="Runloop Capabilities Example",
timeout_s=45.0,
)
print("resolved tunnel:", preview_url)
await _write_json(
session,
RUNTIME_CONTEXT_PATH,
{
**context,
"devbox_id": session_state.devbox_id,
"secret_refs": session_state.secret_refs,
"tunnel_url": preview_url,
"http_preview_contains_readme": "Runloop Capabilities Example" in preview_body,
"runtime_phase": "before_agent_run",
},
)
_phase("Agent Verification")
await _run_sandbox_agent(
agent=agent,
prompt=args.prompt,
session=session,
workflow_name="Runloop capabilities example",
stream=args.stream,
)
proof_text = await _read_text(session, AGENT_PROOF_PATH)
print("agent proof:")
print(proof_text.rstrip())
_phase("Suspend")
await session.aclose()
session_closed = True
print("session persisted and suspended")
_phase("Resume Check")
resumed = await client.resume(session.state)
await resumed.start()
resumed_state = _runloop_state(resumed)
resumed_runtime_context = await _read_text(resumed, RUNTIME_CONTEXT_PATH)
resumed_proof_text = await _read_text(resumed, AGENT_PROOF_PATH)
print("resumed runtime context bytes:", len(resumed_runtime_context.encode("utf-8")))
print("resumed proof:")
print(resumed_proof_text.rstrip())
resumed_state.pause_on_exit = False
await resumed.aclose()
resumed_closed = True
print("resumed session cleaned up with delete semantics")
_phase("Persistent Resource Summary")
print(
"persistent resources retained:",
{
"secret": secret_bootstrap.model_dump(mode="json"),
"network_policy": network_policy_bootstrap.model_dump(mode="json"),
},
)
if bootstrap["axon_id"] is not None:
print(
"axon retained for manual cleanup:",
{
"axon_id": bootstrap["axon_id"],
"axon_name": bootstrap["axon_name"],
},
)
finally:
if resumed is not None and not resumed_closed:
try:
_runloop_state(resumed).pause_on_exit = False
await resumed.aclose()
except Exception as exc:
print(f"warning: failed to close resumed session cleanly: {exc}")
elif session is not None and not session_closed:
try:
_runloop_state(session).pause_on_exit = False
await session.aclose()
except Exception as exc:
print(f"warning: failed to close initial session cleanly: {exc}")
elif session is not None and session_closed and resumed is None:
try:
cleanup_session = await client.resume(session.state)
_runloop_state(cleanup_session).pause_on_exit = False
await cleanup_session.aclose()
except Exception as exc:
print(f"warning: failed to resume suspended session for cleanup: {exc}")
await client.close()
def _build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser()
parser.add_argument("--model", default=DEFAULT_MODEL, help="Model name to use.")
parser.add_argument(
"--prompt", default=DEFAULT_AGENT_PROMPT, help="Prompt to send to the agent."
)
parser.add_argument("--blueprint-name", default=None, help="Optional Runloop blueprint name.")
parser.add_argument(
"--resource-size",
default="MEDIUM",
choices=["X_SMALL", "SMALL", "MEDIUM", "LARGE", "X_LARGE", "XX_LARGE", "CUSTOM_SIZE"],
help="Runloop resource size request for the devbox.",
)
parser.add_argument(
"--network-policy-id",
default=None,
help="Optional Runloop network policy id override. Without this flag, the example reuses or creates the persistent example policy by name.",
)
parser.add_argument(
"--http-port",
type=int,
default=DEFAULT_HTTP_PORT,
help="Port used by the preview HTTP server.",
)
parser.add_argument(
"--root",
action="store_true",
default=False,
help="Launch the Runloop devbox as root. The workspace root becomes /root.",
)
parser.add_argument(
"--stream",
action="store_true",
default=False,
help="Stream the agent response and tool activity.",
)
parser.add_argument(
"--with-axon-demo",
action="store_true",
default=False,
help="Also create and use a temporary Axon. This leaves the Axon behind for manual cleanup.",
)
parser.add_argument(
"--gateway-env-var", default=None, help="Env var name for a gateway binding."
)
parser.add_argument(
"--gateway-name", default=None, help="Runloop gateway name for the binding."
)
parser.add_argument(
"--gateway-secret-name",
default=None,
help="Runloop secret name used by the gateway binding.",
)
parser.add_argument("--mcp-env-var", default=None, help="Env var name for an MCP binding.")
parser.add_argument(
"--mcp-config", default=None, help="Runloop MCP config name for the binding."
)
parser.add_argument(
"--mcp-secret-name",
default=None,
help="Runloop secret name used by the MCP binding.",
)
return parser
if __name__ == "__main__":
asyncio.run(main(_build_parser().parse_args()))